From 640cab345ec3e0cc41026580103981932efb98df Mon Sep 17 00:00:00 2001 From: Guti <32563288+TheCaramelion@users.noreply.github.com> Date: Mon, 21 Apr 2025 12:12:15 +0200 Subject: [PATCH] Virology Update #3 (#17227) * Base stuff * Thresholds * Cockroaches, adjustments * Extrapolator + TGUI Update * Adjustments * Updoot * Revert "Updoot" This reverts commit 8c27a2525ecb39e3fdb5c80dd69d88068831be30. * Gwuh * test * heals * Genetic * Holder and vomit * Indents * Compilable :) * Various fixes * Updates Symptoms * Genevirus * Neutered Symptoms * Adjustments * rads * Extra symptoms * Extra extra symptoms * Adjustments, fixes, more symptoms * Powder that makes the linter green * Finishing touches? * Fixup maps in TGM format 5041170ae1b3: maps/expedition_vr/beach/submaps/quarantineshuttle.dmm Automatically commited by: tools\mapmerge2\fixup.py * Last bits * Defines * Oxy heal * I crave the green check * fix * Maps * Macrophages to turf * Fimxes * Fixes :) * Vomit * Preset * Fire desc * SPELLING MISTAKE * Extra stuffs * types Allows infecting children of a type * feet paws? * fix * Fixes * Update * Flags * Update _disease.dm * Infinite blood glitch (100% real) * virus data properly carries over * GODSPEED, KELENIUS YOUR 10 YEAR TODO IS FINALLY DONE * define * U2 * Implicit * Damn it Fleming * oops linter is kill * Update _reagents.dm * Extra check * . * ough * fixes * Small changes --------- Co-authored-by: Cameron Lennox --- code/__defines/_lists.dm | 6 + code/__defines/dcs/signals.dm | 2 + code/__defines/diseases.dm | 53 +- code/__defines/mobs.dm | 7 + code/controllers/subsystems/supply.dm | 4 +- code/datums/diseases/_MobProcs.dm | 140 ++++-- code/datums/diseases/_disease.dm | 95 ++-- code/datums/diseases/advance/advance.dm | 227 ++++----- .../datums/diseases/advance/disease_preset.dm | 5 +- .../diseases/advance/symptoms/blobspores.dm | 57 ++- .../diseases/advance/symptoms/choking.dm | 75 ++- .../diseases/advance/symptoms/cockroach.dm | 61 +++ .../diseases/advance/symptoms/confusion.dm | 49 +- .../datums/diseases/advance/symptoms/cough.dm | 75 ++- .../advance/symptoms/damage_converter.dm | 2 +- .../diseases/advance/symptoms/deafness.dm | 60 ++- .../datums/diseases/advance/symptoms/dizzy.dm | 37 -- .../datums/diseases/advance/symptoms/fever.dm | 60 ++- code/datums/diseases/advance/symptoms/fire.dm | 82 +++- .../diseases/advance/symptoms/flesh_eating.dm | 63 ++- code/datums/diseases/advance/symptoms/flip.dm | 18 +- .../diseases/advance/symptoms/genetics.dm | 134 ++++++ code/datums/diseases/advance/symptoms/hair.dm | 21 +- .../diseases/advance/symptoms/hallucigen.dm | 79 ++- .../diseases/advance/symptoms/headache.dm | 55 ++- code/datums/diseases/advance/symptoms/heal.dm | 309 ++++++++---- .../diseases/advance/symptoms/hematophagy.dm | 43 -- .../diseases/advance/symptoms/itching.dm | 43 +- .../diseases/advance/symptoms/language.dm | 38 +- .../datums/diseases/advance/symptoms/light.dm | 53 ++ .../diseases/advance/symptoms/macrophage.dm | 85 ++-- code/datums/diseases/advance/symptoms/mlem.dm | 45 +- .../advance/symptoms/necrotic_agent.dm | 17 +- .../diseases/advance/symptoms/oxygen.dm | 38 -- .../advance/symptoms/photosensitivity.dm | 2 +- code/datums/diseases/advance/symptoms/pica.dm | 46 -- .../diseases/advance/symptoms/radiation.dm | 108 +++++ .../diseases/advance/symptoms/sensory.dm | 4 +- .../diseases/advance/symptoms/shivering.dm | 56 ++- code/datums/diseases/advance/symptoms/size.dm | 89 +--- .../diseases/advance/symptoms/sneeze.dm | 80 +-- code/datums/diseases/advance/symptoms/spin.dm | 34 +- .../diseases/advance/symptoms/stimulant.dm | 83 +++- .../diseases/advance/symptoms/symptoms.dm | 74 ++- .../advance/symptoms/synthetic_infection.dm | 2 +- .../diseases/advance/symptoms/telepathy.dm | 45 -- .../datums/diseases/advance/symptoms/viral.dm | 157 +++++- .../diseases/advance/symptoms/vision.dm | 84 +++- .../datums/diseases/advance/symptoms/vomit.dm | 40 +- .../diseases/advance/symptoms/weakness.dm | 4 +- .../datums/diseases/advance/symptoms/weigh.dm | 37 -- .../diseases/advance/symptoms/weight.dm | 65 +++ .../datums/diseases/advance/symptoms/youth.dm | 42 ++ code/datums/diseases/anxiety.dm | 7 +- code/datums/diseases/appendicitis.dm | 14 +- code/datums/diseases/beesease.dm | 7 +- code/datums/diseases/brainrot.dm | 7 +- code/datums/diseases/choreomania.dm | 10 +- code/datums/diseases/cold.dm | 11 +- code/datums/diseases/cold9.dm | 51 +- code/datums/diseases/darkness.dm | 2 +- code/datums/diseases/fake_gbs.dm | 4 +- code/datums/diseases/flu.dm | 7 +- code/datums/diseases/food_poisoning.dm | 8 +- code/datums/diseases/gbs.dm | 6 +- code/datums/diseases/lycancoughy.dm | 4 +- code/datums/diseases/magnitis.dm | 2 +- code/datums/diseases/roanoake.dm | 5 +- code/datums/outfits/jobs/medical.dm | 1 + code/datums/supplypacks/medical.dm | 6 +- code/game/atoms.dm | 23 +- code/game/jobs/job/job_goodies.dm | 4 +- code/game/machinery/adv_med.dm | 4 +- code/game/machinery/computer/medical.dm | 2 +- code/game/machinery/pandemic.dm | 455 ++++++++---------- .../effects/decals/Cleanable/humans.dm | 34 +- .../objects/effects/decals/Cleanable/misc.dm | 13 + .../objects/items/devices/extrapolator.dm | 264 ++++++++++ .../objects/items/devices/scanners/guide.dm | 2 +- .../objects/items/devices/scanners/health.dm | 7 +- .../objects/items/weapons/storage/belt.dm | 3 +- code/game/objects/structures/holosign.dm | 6 +- code/game/turfs/simulated.dm | 4 +- code/game/turfs/turf.dm | 29 ++ code/modules/events/disease_outbreak.dm | 13 +- code/modules/mob/holder.dm | 9 + code/modules/mob/living/carbon/carbon.dm | 4 +- .../living/carbon/human/human_attackhand.dm | 2 +- .../mob/living/carbon/human/human_defense.dm | 9 + code/modules/mob/living/carbon/human/life.dm | 8 +- code/modules/mob/living/living.dm | 126 +++-- code/modules/mob/living/living_defense.dm | 7 + .../subtypes/animal/passive/mouse.dm | 27 ++ .../simple_mob/subtypes/vore/macrophage.dm | 67 ++- code/modules/mob/mob_defines.dm | 4 +- code/modules/organs/blood.dm | 6 +- code/modules/reagents/holder/holder.dm | 1 + .../reagents/reactions/instant/virology.dm | 14 +- .../reagent_containers/_reagent_containers.dm | 6 + .../reagents/reagent_containers/syringes.dm | 8 +- .../reagents/reagent_containers/virology.dm | 27 +- code/modules/reagents/reagents/_reagents.dm | 13 +- code/modules/reagents/reagents/core.dm | 13 +- code/modules/reagents/reagents/virology.dm | 7 +- code/modules/surgery/_helpers.dm | 11 + code/modules/surgery/dismemberment.dm | 31 ++ icons/effects/blood.dmi | Bin 54778 -> 58267 bytes icons/obj/device.dmi | Bin 95483 -> 96718 bytes .../beach/submaps/quarantineshuttle.dmm | 11 +- maps/groundbase/gb-z2.dmm | 12 +- maps/stellar_delight/stellar_delight1.dmm | 7 +- maps/tether/tether-01-surface1.dmm | 12 +- tgui/packages/tgui/interfaces/PanDEMIC.tsx | 360 -------------- .../tgui/interfaces/Pandemic/Beaker.tsx | 154 ++++++ .../tgui/interfaces/Pandemic/Specimen.tsx | 84 ++++ .../tgui/interfaces/Pandemic/Symptom.tsx | 101 ++++ .../tgui/interfaces/Pandemic/Virus.tsx | 103 ++++ .../tgui/interfaces/Pandemic/helpers.ts | 16 + .../tgui/interfaces/Pandemic/index.tsx | 29 ++ .../tgui/interfaces/Pandemic/types.ts | 59 +++ vorestation.dme | 19 +- 121 files changed, 3669 insertions(+), 1903 deletions(-) create mode 100644 code/datums/diseases/advance/symptoms/cockroach.dm delete mode 100644 code/datums/diseases/advance/symptoms/dizzy.dm create mode 100644 code/datums/diseases/advance/symptoms/genetics.dm delete mode 100644 code/datums/diseases/advance/symptoms/hematophagy.dm create mode 100644 code/datums/diseases/advance/symptoms/light.dm delete mode 100644 code/datums/diseases/advance/symptoms/oxygen.dm delete mode 100644 code/datums/diseases/advance/symptoms/pica.dm create mode 100644 code/datums/diseases/advance/symptoms/radiation.dm delete mode 100644 code/datums/diseases/advance/symptoms/telepathy.dm delete mode 100644 code/datums/diseases/advance/symptoms/weigh.dm create mode 100644 code/datums/diseases/advance/symptoms/weight.dm create mode 100644 code/datums/diseases/advance/symptoms/youth.dm create mode 100644 code/game/objects/items/devices/extrapolator.dm create mode 100644 code/modules/surgery/_helpers.dm create mode 100644 code/modules/surgery/dismemberment.dm delete mode 100644 tgui/packages/tgui/interfaces/PanDEMIC.tsx create mode 100644 tgui/packages/tgui/interfaces/Pandemic/Beaker.tsx create mode 100644 tgui/packages/tgui/interfaces/Pandemic/Specimen.tsx create mode 100644 tgui/packages/tgui/interfaces/Pandemic/Symptom.tsx create mode 100644 tgui/packages/tgui/interfaces/Pandemic/Virus.tsx create mode 100644 tgui/packages/tgui/interfaces/Pandemic/helpers.ts create mode 100644 tgui/packages/tgui/interfaces/Pandemic/index.tsx create mode 100644 tgui/packages/tgui/interfaces/Pandemic/types.ts diff --git a/code/__defines/_lists.dm b/code/__defines/_lists.dm index ebf923672dd..43c53308a24 100644 --- a/code/__defines/_lists.dm +++ b/code/__defines/_lists.dm @@ -47,3 +47,9 @@ #define reverseList(L) reverseRange(L.Copy()) #define islist(L) istype(L, /list) + +/// Performs an insertion on the given lazy list with the given key and value. If the value already exists, a new one will not be made. +#define LAZYORASSOCLIST(lazy_list, key, value) \ + LAZYINITLIST(lazy_list); \ + LAZYINITLIST(lazy_list[key]); \ + lazy_list[key] |= value; diff --git a/code/__defines/dcs/signals.dm b/code/__defines/dcs/signals.dm index 6bdf7e73e5f..734da1990a4 100644 --- a/code/__defines/dcs/signals.dm +++ b/code/__defines/dcs/signals.dm @@ -185,6 +185,8 @@ #define COMSIG_ATOM_START_PULL "movable_start_pull" ///called on /living when someone starts pulling it (atom/movable/puller, state, force) #define COMSIG_LIVING_START_PULL "living_start_pull" +///from base atom/Exited(): (mob/user, obj/item/extrapolator/extrapolator, dry_run, list/result) +#define COMSIG_ATOM_EXTRAPOLATOR_ACT "atom_extrapolator_act" ///////////////// diff --git a/code/__defines/diseases.dm b/code/__defines/diseases.dm index ac34e469b5e..3dd0d595ec3 100644 --- a/code/__defines/diseases.dm +++ b/code/__defines/diseases.dm @@ -1,3 +1,4 @@ +#define DISEASE_LIMIT 1 #define VIRUS_SYMPTOM_LIMIT 6 //Visibility Flags @@ -11,20 +12,44 @@ #define CAN_NOT_POPULATE (1<<3) //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) +#define DISEASE_SPREAD_SPECIAL (1<<0) +#define DISEASE_SPREAD_NON_CONTAGIOUS (1<<1) +#define DISEASE_SPREAD_BLOOD (1<<2) +#define DISEASE_SPREAD_FLUIDS (1<<3) +#define DISEASE_SPREAD_CONTACT (1<<4) +#define DISEASE_SPREAD_AIRBORNE (1<<5) //Severity Defines -#define NONTHREAT "No threat" -#define MINOR "Minor" -#define MEDIUM "Medium" -#define HARMFUL "Harmful" -#define DANGEROUS "Dangerous!" -#define BIOHAZARD "BIOHAZARD THREAT!" +#define DISEASE_BENEFICIAL "Beneficial" +#define DISEASE_POSITIVE "Positive" +#define DISEASE_NONTHREAT "No threat" +#define DISEASE_MINOR "Minor" +#define DISEASE_MEDIUM "Medium" +#define DISEASE_HARMFUL "Harmful" +#define DISEASE_DANGEROUS "Dangerous" +#define DISEASE_BIOHAZARD "BIOHAZARD" +#define DISEASE_PANDEMIC "PANDEMIC" -#define SYMPTOM_ACTIVATION_PROB 3 +//Various Virus Flags +#define NEEDS_ALL_CURES 0x1 /// If a virus requires EVERY cure in the cure list to become cured. +#define SPREAD_DEAD 0x2 /// If a virus spreads to the dead and proceesses in them. +#define INFECT_SYNTHETICS 0x4 /// If a synthetic can be infected with the virus. +#define HAS_TIMER 0x8 /// If the cure timer is currently active. +#define PROCESSING 0x10 /// If stage_act has been called and we are now processing. +#define CARRIER 0x20 /// If we are a carrier for the virus but we will not be affected by it. +#define BYPASSES_IMMUNITY 0x40 /// If this virus bypasses immunity. +#define DISCOVERED 0x80 /// If applied, this virus will show up on medical HUDs. Automatically set when it reaches mid-stage. + +#define EXTRAPOLATOR_RESULT_DISEASES "extrapolator_result_disease" +#define EXTRAPOLATOR_RESULT_ACT_PRIORITY "extrapolator_result_action_priority" +#define EXTRAPOLATOR_ACT_PRIORITY_SPECIAL "extrapolator_action_priority_special" +#define EXTRAPOLATOR_ACT_PRIORITY_ISOLATE "extrapolator_action_priority_isolate" +#define EXTRAPOLATOR_ACT_ADD_DISEASES(target_list, diseases)\ + do {\ + var/_D = ##diseases;\ + if ((islist(_D) && length(_D)) || istype(_D, /datum/disease)) { \ + LAZYORASSOCLIST(##target_list, EXTRAPOLATOR_RESULT_DISEASES, _D);\ + }\ + } while(0) +#define EXTRAPOLATOR_ACT_CHECK(target_list, wanted_action_priority) (##target_list[EXTRAPOLATOR_RESULT_ACT_PRIORITY] == ##wanted_action_priority) +#define EXTRAPOLATOR_ACT_SET(target_list, wanted_action_priority) (##target_list[EXTRAPOLATOR_RESULT_ACT_PRIORITY] = ##wanted_action_priority) diff --git a/code/__defines/mobs.dm b/code/__defines/mobs.dm index b7c61460170..41b0dcc70e2 100644 --- a/code/__defines/mobs.dm +++ b/code/__defines/mobs.dm @@ -527,3 +527,10 @@ // Default name for accesories #define DEVELOPER_WARNING_NAME "you should not see this..." + +// Green vomit +#define VOMIT_TOXIC 1 +// Purple vomit +#define VOMIT_PURPLE 2 +// Nanite vomit +#define VOMIT_NANITE 3 diff --git a/code/controllers/subsystems/supply.dm b/code/controllers/subsystems/supply.dm index a7610dea59c..69984d25d93 100644 --- a/code/controllers/subsystems/supply.dm +++ b/code/controllers/subsystems/supply.dm @@ -119,8 +119,8 @@ SUBSYSTEM_DEF(supply) EC.contents[EC.contents.len]["quantity"] = cashmoney.worth EC.value += EC.contents[EC.contents.len]["value"] - if(istype(A, /obj/item/reagent_containers/glass/bottle/vaccine)) - var/obj/item/reagent_containers/glass/bottle/vaccine/sale_bottle = A + if(istype(A, /obj/item/reagent_containers/glass/beaker/vial/vaccine)) + var/obj/item/reagent_containers/glass/beaker/vial/vaccine/sale_bottle = A if(!istype(CR, /obj/structure/closet/crate/freezer)) EC.contents = list( "error" = "Error: Product was improperly packaged. Send conents in freezer crate to preserve contents for transport." diff --git a/code/datums/diseases/_MobProcs.dm b/code/datums/diseases/_MobProcs.dm index e40326a8707..0ba6e5dc91b 100644 --- a/code/datums/diseases/_MobProcs.dm +++ b/code/datums/diseases/_MobProcs.dm @@ -5,8 +5,22 @@ return TRUE return FALSE +/mob/proc/RemoveDisease(datum/disease/D) + viruses -= D + return TRUE + +/mob/proc/HasResistance(resistance) + if(resistances.Find(resistance)) + return TRUE + return FALSE + +/mob/proc/IsInfected() + if(isemptylist(GetViruses())) + return FALSE + return TRUE + /mob/proc/CanContractDisease(datum/disease/D) - if(stat == DEAD && !D.allow_dead) + if(stat == DEAD && !global_flag_check(D.virus_modifiers, SPREAD_DEAD)) return FALSE if(D.GetDiseaseID() in GetResistances()) @@ -18,17 +32,22 @@ if(istype(D, /datum/disease/advance) && count_by_type(GetViruses(), /datum/disease/advance) > 0) return FALSE - if(!(type in D.viable_mobtypes)) - return -1 + var/compatible_type = FALSE + for(var/type_to_test in D.viable_mobtypes) + if(ispath(type, type_to_test)) + compatible_type = TRUE + break + if(!compatible_type) + return FALSE if(isSynthetic()) - if(D.infect_synthetics) + if(global_flag_check(D.virus_modifiers, INFECT_SYNTHETICS)) return TRUE return FALSE return TRUE -/mob/proc/ContractDisease(datum/disease/D) +/mob/proc/ContractDisease(datum/disease/D, var/target_zone) if(!CanContractDisease(D)) return 0 AddDisease(D) @@ -41,7 +60,7 @@ DD.affected_mob = src GLOB.active_diseases += DD - var/list/skipped = list("affected_mob", "holder", "carrier", "stage", "type", "parent_type", "vars", "transformed") + var/list/skipped = list("affected_mob", "holder", "carrier", "stage", "type", "parent_type", "vars", "transformed", "_active_timers") if(respect_carrier) skipped -= "carrier" for(var/V in DD.vars) @@ -49,35 +68,24 @@ continue if(istype(DD.vars[V],/list)) var/list/L = D.vars[V] - if(L) + if(islist(L)) DD.vars[V] = L.Copy() else DD.vars[V] = D.vars[V] log_admin("[key_name(src)] has contracted the virus \"[DD]\"") -/mob/living/carbon/ContractDisease(datum/disease/D) +/mob/living/carbon/human/ContractDisease(datum/disease/D, target_zone) if(!CanContractDisease(D)) - return 0 + return FALSE var/obj/item/clothing/Cl = null - var/passed = 1 + var/passed = TRUE - 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 + var/head_chance = 80 + var/body_chance = 100 + var/hands_chance = 35/2 + var/feet_chance = 15/2 if(prob(15/D.permeability_mod)) return @@ -85,27 +93,37 @@ if(nutrition > 300 && prob(nutrition/50)) return - var/target_zone = pick(head_ch;1,body_ch;2,hands_ch;3,feet_ch;4) + if(!target_zone) + target_zone = pick(list( + BP_HEAD = head_chance, + BP_TORSO = body_chance, + BP_R_HAND = hands_chance, + BP_L_HAND = hands_chance, + BP_R_FOOT = feet_chance, + BP_L_FOOT = feet_chance + )) + else + target_zone = check_zone(target_zone) if(ishuman(src)) var/mob/living/carbon/human/H = src switch(target_zone) - if(1) + if(BP_HEAD) 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(BP_TORSO) 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(BP_L_HAND, BP_R_HAND) if(isobj(H.wear_suit) && H.wear_suit.body_parts_covered & HANDS) Cl = H.wear_suit passed = prob((Cl.permeability_coefficient*100) - 1) @@ -113,7 +131,7 @@ if(passed && isobj(H.gloves)) Cl = H.gloves passed = prob((Cl.permeability_coefficient*100) - 1) - if(4) + if(BP_L_FOOT, BP_R_FOOT) if(isobj(H.wear_suit) && H.wear_suit.body_parts_covered & FEET) Cl = H.wear_suit passed = prob((Cl.permeability_coefficient*100) - 1) @@ -121,12 +139,20 @@ 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 + +/mob/living/proc/AirborneContractDisease(datum/disease/D, force_spread) + if(((D.spread_flags & DISEASE_SPREAD_AIRBORNE) || force_spread) && prob(50*D.spreading_modifier) - 1) + ForceContractDisease(D) + +/mob/living/carbon/AirborneContractDisease(datum/disease/D, force_spread) + if(internal) + return + if(mNobreath in mutations) + return + ..() /mob/proc/ForceContractDisease(datum/disease/D, respect_carrier) if(!CanContractDisease(D)) @@ -136,22 +162,25 @@ return TRUE /mob/living/carbon/human/CanContractDisease(datum/disease/D) - if(species.virus_immune && !D.bypasses_immunity) - return FALSE - for(var/organ in D.required_organs) - if(locate(organ) in internal_organs) - continue - if(locate(organ) in organs) - continue - return FALSE + if(!((locate(organ) in organs) || (locate(organ) in internal_organs))) + return FALSE + + if(species.virus_immune && !global_flag_check(D.virus_modifiers, BYPASSES_IMMUNITY)) + D.virus_modifiers |= CARRIER + else + D.virus_modifiers &= ~CARRIER + return ..() /mob/living/carbon/human/monkey/CanContractDisease(datum/disease/D) . = ..() if(. == -1) if(D.viable_mobtypes.Find(/mob/living/carbon/human)) - return 1 + return + +/mob/living/proc/CanSpreadAirborneDisease() + return !is_mouth_covered() /mob/living/proc/handle_diseases() return @@ -160,10 +189,24 @@ LAZYINITLIST(viruses) return viruses +/mob/proc/GetSpreadableViruses() + LAZYINITLIST(viruses) + var/list/viruses_to_return = list() + for(var/datum/disease/D in viruses) + if(D.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS)) + continue + viruses_to_return += D + return viruses_to_return + /mob/proc/GetResistances() LAZYINITLIST(resistances) return resistances +/mob/proc/AddResistances(resistance) + LAZYINITLIST(resistances) + resistances |= resistance + return TRUE + /client/proc/ReleaseVirus() set category = "Fun.Event Kit" set name = "Release Virus" @@ -173,17 +216,24 @@ return FALSE var/disease = tgui_input_list(usr, "Choose virus", "Viruses", subtypesof(/datum/disease), subtypesof(/datum/disease)) + + if(isnull(disease)) + return FALSE + var/mob/living/carbon/human/H = tgui_input_list(usr, "Choose infectee", "Characters", human_mob_list) - var/datum/disease/D = new disease - - if(isnull(D) || isnull(H)) + if(isnull(H)) return FALSE + var/datum/disease/D = new disease + if(!H.HasDisease(D)) H.ForceContractDisease(D) message_admins("[key_name_admin(usr)] has triggered a virus outbreak of [D.name]! Affected mob: [key_name_admin(H)]") log_admin("[key_name_admin(usr)] infected [key_name_admin(H)] with [D.name]") + if(!GLOB.archive_diseases[D.GetDiseaseID()]) + GLOB.archive_diseases[D.GetDiseaseID()] = D + return TRUE diff --git a/code/datums/diseases/_disease.dm b/code/datums/diseases/_disease.dm index 43993056b50..c42ff2665d5 100644 --- a/code/datums/diseases/_disease.dm +++ b/code/datums/diseases/_disease.dm @@ -4,7 +4,8 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) //Flags var/visibility_flags = 0 var/disease_flags = CURABLE|CAN_CARRY|CAN_RESIST - var/spread_flags = AIRBORNE + var/spread_flags = DISEASE_SPREAD_AIRBORNE + var/virus_modifiers = NEEDS_ALL_CURES //Fluff /// Used for identification of viruses in the Medical Records Virus Database @@ -20,34 +21,26 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) var/stage = 1 var/max_stages = 0 var/stage_prob = 4 + /// The fraction of stages the virus must at least be at to show up on medical HUDs. Rounded up. var/discovery_threshold = 0.5 - /// If TRUE, this virus will show up on medical HUDs. Automatically set when it reaches mid-stage. - var/discovered = FALSE // Other var/list/viable_mobtypes = list() var/mob/living/carbon/affected_mob var/list/cures = list() - var/infectivity = 65 + var/infectivity = 10 var/cure_chance = 8 - var/carrier = FALSE - var/bypasses_immunity = FALSE - var/virus_heal_resistant = FALSE + var/spreading_modifier = 1 var/permeability_mod = 1 - var/severity = NONTHREAT + var/danger = DISEASE_MINOR var/list/required_organs = list() - var/needs_all_cures = TRUE var/list/strain_data = list() - var/allow_dead = FALSE - var/infect_synthetics = FALSE - var/processing = FALSE - var/has_timer = FALSE /datum/disease/Destroy() affected_mob = null GLOB.active_diseases.Remove(src) - if(processing) + if(global_flag_check(virus_modifiers, PROCESSING)) End() return ..() @@ -56,11 +49,11 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) return FALSE var/cure = has_cure() - if(carrier && !cure) + if(global_flag_check(virus_modifiers, CARRIER) && !cure) return FALSE - if(!processing) - processing = TRUE + if(!global_flag_check(virus_modifiers, PROCESSING)) + virus_modifiers |= PROCESSING Start() stage = min(stage, max_stages) @@ -72,8 +65,8 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) /datum/disease/proc/handle_stage_advance(has_cure = FALSE) if(!has_cure && prob(stage_prob)) stage = min(stage + 1, max_stages) - if(!discovered && stage >= CEILING(max_stages * discovery_threshold, 1)) - discovered = TRUE + if(!global_flag_check(virus_modifiers, DISCOVERED) && stage >= CEILING(max_stages * discovery_threshold, 1)) + virus_modifiers |= DISCOVERED /datum/disease/proc/handle_cure_testing(has_cure = FALSE) if(has_cure && prob(cure_chance)) @@ -102,29 +95,32 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) if(affected_mob.bloodstr.has_reagent(C_id) || affected_mob.ingested.has_reagent(C_id)) cures_found++ - if(needs_all_cures && cures_found < length(cures)) + if(global_flag_check(virus_modifiers, NEEDS_ALL_CURES) && cures_found < length(cures)) return FALSE return cures_found -/datum/disease/proc/spread(force_spread = 0) +/datum/disease/proc/spread(var/force_spread = 0) if(!affected_mob) return if(affected_mob.is_incorporeal()) return - if((spread_flags & SPECIAL || spread_flags & NON_CONTAGIOUS || spread_flags & BLOOD) && !force_spread) + if(!(spread_flags & DISEASE_SPREAD_AIRBORNE) && !force_spread) return - if(affected_mob.bloodstr.has_reagent(REAGENT_ID_SPACEACILLIN) || (affected_mob.nutrition > 300 && prob(affected_mob.nutrition/50))) + if(affected_mob.stat == DEAD && !global_flag_check(virus_modifiers, SPREAD_DEAD) && !force_spread) return - var/spread_range = 1 + if(affected_mob.reagents.has_reagent(REAGENT_ID_SPACEACILLIN) || (affected_mob.nutrition > 300 && prob(affected_mob.nutrition/50))) + return + + var/spread_range = 2 if(force_spread) spread_range = force_spread - if(spread_flags & AIRBORNE) + if(spread_flags & DISEASE_SPREAD_AIRBORNE) spread_range++ var/turf/target = affected_mob.loc @@ -133,38 +129,41 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) if(C.is_incorporeal()) continue var/turf/current = get_turf(C) - if(current) - while(TRUE) - if(current == target) - C.ContractDisease(src) - break - var/direction = get_dir(current, target) - var/turf/next = get_step(current, direction) - if(!current.CanZASPass(next)) - break - current = next + if(disease_air_spread_walk(target, current)) + C.AirborneContractDisease(src, force_spread) + +/proc/disease_air_spread_walk(turf/start, turf/end) + if(!start || !end) + return FALSE + while(TRUE) + if(end == start) + return TRUE + var/turf/Temp = get_step_towards(end, start) + if(!end.CanZASPass(Temp)) + return FALSE + end = Temp /datum/disease/proc/cure() if(affected_mob) if(disease_flags & CAN_RESIST) if(!(type in affected_mob.GetResistances())) - affected_mob.resistances += type + affected_mob.AddResistances(type) remove_virus() qdel(src) /datum/disease/proc/start_cure_timer() - if(has_timer) + if(global_flag_check(virus_modifiers, HAS_TIMER)) return if(!(disease_flags & CURABLE)) return - has_timer = TRUE + virus_modifiers |= HAS_TIMER addtimer(CALLBACK(src, PROC_REF(check_natural_immunity)), (1 HOUR) + rand( -20 MINUTES, 30 MINUTES), TIMER_DELETE_ME) /datum/disease/proc/check_natural_immunity() if(!(disease_flags & CURABLE)) return if(prob(rand(10, 15))) - has_timer = FALSE + virus_modifiers &= ~HAS_TIMER cure() return addtimer(CALLBACK(src, PROC_REF(check_natural_immunity)), rand(5 MINUTES, 10 MINUTES), TIMER_DELETE_ME) @@ -180,20 +179,30 @@ GLOBAL_LIST_INIT(diseases, subtypesof(/datum/disease)) return D /datum/disease/proc/GetDiseaseID() - return type + return + +/datum/disease/proc/IsSpreadByBlood() + if(spread_flags & DISEASE_SPREAD_BLOOD) + return TRUE + return FALSE + +/datum/disease/proc/IsSpreadByFluids() + if(spread_flags & DISEASE_SPREAD_FLUIDS) + return TRUE + return FALSE /datum/disease/proc/IsSpreadByTouch() - if(spread_flags & CONTACT_FEET || spread_flags & CONTACT_HANDS || spread_flags & CONTACT_GENERAL) + if(spread_flags & DISEASE_SPREAD_CONTACT) return TRUE return FALSE /datum/disease/proc/IsSpreadByAir() - if(spread_flags & AIRBORNE) + if(spread_flags & DISEASE_SPREAD_AIRBORNE) return TRUE return FALSE /datum/disease/proc/remove_virus() - affected_mob.viruses -= src + affected_mob.RemoveDisease(src) // Called when a disease is added onto a mob /datum/disease/proc/Start() diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm index 56c2135a6ee..c8b71354921 100644 --- a/code/datums/diseases/advance/advance.dm +++ b/code/datums/diseases/advance/advance.dm @@ -26,22 +26,25 @@ GLOBAL_LIST_INIT(advance_cures, list( spread_text = "Unknown" viable_mobtypes = list(/mob/living/carbon/human) - var/s_processing = FALSE + var/last_modified_by = "no CKEY" + var/resistance + var/stealth + var/stage_rate + var/transmission + var/severity + var/speed var/list/symptoms = list() + + var/s_processing = FALSE var/id = "" -/datum/disease/advance/New(process = 1, datum/disease/advance/D) - if(!istype(D)) +/datum/disease/advance/New(process = TRUE, datum/disease/advance/D) + if(istype(D)) + for(var/datum/symptom/S in D.symptoms) + symptoms += new S.type + else D = null - if(!symptoms || !length(symptoms)) - - if(!D || !D.symptoms || !length(D.symptoms)) - symptoms = GenerateSymptoms(0, 2) - else - for(var/datum/symptom/S in D.symptoms) - symptoms += new S.type - Refresh() ..(process, D) return @@ -80,7 +83,7 @@ GLOBAL_LIST_INIT(advance_cures, list( return TRUE -/datum/disease/advance/cure(resistance=1) +/datum/disease/advance/cure(resistance = TRUE) if(affected_mob) var/id = "[GetDiseaseID()]" if(resistance && !(id in affected_mob.GetResistances())) @@ -150,8 +153,8 @@ GLOBAL_LIST_INIT(advance_cures, list( return generated /datum/disease/advance/proc/Refresh(new_name = FALSE, archive = FALSE) - var/list/properties = GenerateProperties() - AssignProperties(properties) + GenerateProperties() + AssignProperties() id = null if(!GLOB.archive_diseases[GetDiseaseID()]) @@ -160,82 +163,92 @@ GLOBAL_LIST_INIT(advance_cures, list( GLOB.archive_diseases[GetDiseaseID()] = src // So we don't infinite loop GLOB.archive_diseases[GetDiseaseID()] = new /datum/disease/advance(0, src, 1) - var/datum/disease/advance/A = GLOB.archive_diseases[GetDiseaseID()] - AssignName(A.name) - /datum/disease/advance/proc/GenerateProperties() + resistance = 0 + stealth = 0 + stage_rate = 0 + transmission = 0 + severity = 0 - if(!symptoms || !length(symptoms)) - CRASH("We did not have any symptoms before generating properties.") + var/c1sev + var/c2sev + var/c3sev - var/list/properties = list("resistance" = 1, "stealth" = 0, "stage rate" = 1, "transmittable" = 1, "severity" = 0) + for(var/datum/symptom/S as() in symptoms) + resistance = S.resistance + stealth += S.stealth + stage_rate += S.stage_speed + transmission += S.transmission + for(var/datum/symptom/S as() in symptoms) + S.severityset(src) + switch(S.severity) + if(-INFINITY to 0) + c1sev += S.severity + if(1 to 2) + c2sev = max(c2sev, min(3, (S.severity + c2sev))) + if(3 to 4) + c2sev = max(c2sev, min(4, (S.severity + c2sev))) + if(5 to INFINITY) + if(c3sev >= 5) + c3sev += (S.severity -3) + else + c3sev += S.severity - for(var/datum/symptom/S in symptoms) + severity += (max(c2sev, c3sev) + c1sev) - properties["resistance"] += S.resistance - properties["stealth"] += S.stealth - properties["stage rate"] += S.stage_speed - properties["transmittable"] += S.transmittable - properties["severity"] = max(properties["severity"], S.severity) // severity is based on the highest severity symptom +/datum/disease/advance/proc/AssignProperties() - return properties - -/datum/disease/advance/proc/AssignProperties(list/properties = list()) - - if(properties && length(properties)) - switch(properties["stealth"]) - if(2) - visibility_flags = HIDDEN_SCANNER - if(3 to INFINITY) - visibility_flags = HIDDEN_SCANNER|HIDDEN_PANDEMIC - - // 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) - SetSeverity(properties["severity"]) - GenerateCure(properties) + if(stealth >= 2) + visibility_flags |= HIDDEN_SCANNER else - CRASH("Our properties were empty or null!") + visibility_flags &= ~HIDDEN_SCANNER -/datum/disease/advance/proc/SetSpread(spread_id) - switch(spread_id) - if(NON_CONTAGIOUS, SPECIAL) - spread_text = "Non-contagious" - if(CONTACT_GENERAL, CONTACT_HANDS, CONTACT_FEET) - spread_text = "On contact" - if(AIRBORNE) - spread_text = "Airborne" - if(BLOOD) + SetSpread() + permeability_mod = max(CEILING(0.4 * transmission, 1), 1) + cure_chance = 15 - clamp(resistance, -5, 5) // can be between 10 and 20 + stage_prob = max(stage_rate, 2) + SetSeverity(severity) + GenerateCure() + +/datum/disease/advance/proc/SetSpread() + switch(transmission) + if(-INFINITY to 5) + spread_flags = DISEASE_SPREAD_BLOOD spread_text = "Blood" - - spread_flags = spread_id + if(6 to 10) + spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_FLUIDS + spread_text = "Fluids" + if(11 to INFINITY) + spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_FLUIDS | DISEASE_SPREAD_CONTACT + spread_text = "On Contact" /datum/disease/advance/proc/SetSeverity(level_sev) switch(level_sev) - if(-INFINITY to 0) - severity = NONTHREAT + if(-INFINITY to -2) + severity = DISEASE_BENEFICIAL + if(-1) + severity = DISEASE_POSITIVE + if(0) + severity = DISEASE_NONTHREAT if(1) - severity = MINOR + severity = DISEASE_MINOR if(2) - severity = MEDIUM + severity = DISEASE_MEDIUM if(3) - severity = HARMFUL + severity = DISEASE_HARMFUL if(4) - severity = DANGEROUS + severity = DISEASE_DANGEROUS if(5 to INFINITY) - severity = BIOHAZARD + severity = DISEASE_BIOHAZARD else severity = "Unknown" -/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)) - cures = list(GLOB.advance_cures[res]) - cure_text = cures[1] +/datum/disease/advance/proc/GenerateCure() + var/res = clamp(resistance - (length(symptoms) / 2), 1, length(GLOB.advance_cures)) + cures = list(GLOB.advance_cures[res]) + cure_text = cures[1] return // Randomly generate a symptom, has a chance to lose or gain a symptom. @@ -243,14 +256,15 @@ GLOBAL_LIST_INIT(advance_cures, list( var/s = safepick(GenerateSymptoms(min_level, max_level, 1)) if(s) AddSymptom(s) - Refresh(1) + Refresh(TRUE) return +// Randomly generates a symptom from a given list, has a chance to lose or gain a symptom. /datum/disease/advance/proc/PickyEvolve(var/list/datum/symptom/D) var/s = safepick(D) if(s) AddSymptom(new s) - Refresh(1) + Refresh(TRUE) return // Randomly remove a symptom. @@ -259,12 +273,20 @@ GLOBAL_LIST_INIT(advance_cures, list( var/s = safepick(symptoms) if(s) RemoveSymptom(s) - Refresh(1) + Refresh(TRUE) return +// Randomly neuter a symptom. +/datum/disease/advance/proc/Neuter() + if(symptoms.len) + var/s = safepick(symptoms) + if(s) + NeuterSymptom(s) + Refresh(TRUE) + // Name the disease. -/datum/disease/advance/proc/AssignName(name = "Unknown") - src.name = name +/datum/disease/advance/proc/AssignName(new_name = "Unknown") + name = new_name return // Return a unique ID of the disease. @@ -278,6 +300,10 @@ GLOBAL_LIST_INIT(advance_cures, list( id = result return id +/datum/disease/advance/proc/Finalize() + for(var/datum/symptom/S in symptoms) + S.OnAdd(src) + // Add a symptom, if it is over the limit (with a small chance to be able to go over) // we take a random symptom away and add the new one. /datum/disease/advance/proc/AddSymptom(datum/symptom/S) @@ -297,6 +323,13 @@ GLOBAL_LIST_INIT(advance_cures, list( symptoms -= S return +// Neuters a symptom, allowing it only for stats. +/datum/disease/advance/proc/NeuterSymptom(datum/symptom/S) + if(!S.neutered) + S.neutered = TRUE + S.name += " (neutered)" + S.OnRemove(src) + // Mix a list of advance diseases and return the mixed result. /proc/Advance_Mix(list/D_list) @@ -324,7 +357,7 @@ GLOBAL_LIST_INIT(advance_cures, list( // Should be only 1 entry left, but if not let's only return a single entry var/datum/disease/advance/to_return = pick(diseases) - to_return.Refresh(1) + to_return.Refresh(new_name = TRUE) return to_return /proc/SetViruses(datum/reagent/R, list/data) @@ -342,7 +375,7 @@ GLOBAL_LIST_INIT(advance_cures, list( set name = "Create Advanced Virus" set desc = "Create an advanced virus and release it." - if(!is_admin()) + if(!is_admin(usr)) return FALSE var/i = VIRUS_SYMPTOM_LIMIT @@ -355,8 +388,8 @@ GLOBAL_LIST_INIT(advance_cures, list( symptoms += "Done" symptoms += GLOB.list_symptoms.Copy() do - if(usr) - var/symptom = tgui_input_list(usr, "Choose a symptom to add ([i] remaining)", "Choose a Symptom", symptoms) + if(src) + var/symptom = tgui_input_list(src, "Choose a symptom to add ([i] remaining)", "Choose a Symptom", symptoms) if(isnull(symptom)) return else if(istext(symptom)) @@ -370,16 +403,16 @@ GLOBAL_LIST_INIT(advance_cures, list( if(length(D.symptoms) > 0) - var/new_name = tgui_input_text(usr, "Name your new disease.", "New Name") + var/new_name = tgui_input_text(src, "Name your new disease.", "New Name") if(!new_name) return FALSE - D.AssignName(new_name) - D.Refresh() + D.Refresh(new_name) + D.Finalize() for(var/datum/disease/advance/AD in GLOB.active_diseases) AD.Refresh() - H = tgui_input_list(usr, "Choose infectee", "Infectees", human_mob_list) + H = tgui_input_list(src, "Choose infectee", "Infectees", human_mob_list) if(isnull(H)) return FALSE @@ -390,35 +423,7 @@ GLOBAL_LIST_INIT(advance_cures, list( var/list/name_symptoms = list() for(var/datum/symptom/S in D.symptoms) name_symptoms += S.name - message_admins("[key_name_admin(usr)] has triggered a custom virus outbreak of [D.name]! It has these symptoms: [english_list(name_symptoms)]") - log_admin("[key_name_admin(usr)] infected [key_name_admin(H)] with [D.name]. It has these symptoms: [english_list(name_symptoms)]") + message_admins("[key_name_admin(src)] has triggered a custom virus outbreak of [D.name]! It has these symptoms: [english_list(name_symptoms)]") + log_admin("[key_name_admin(src)] infected [key_name_admin(H)] with [D.name]. It has these symptoms: [english_list(name_symptoms)]") return TRUE - -/datum/disease/advance/proc/totalStageSpeed() - var/total_stage_speed = 0 - 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 - 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 - 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 - for(var/i in symptoms) - var/datum/symptom/S = i - total_transmittable += S.transmittable - return total_transmittable diff --git a/code/datums/diseases/advance/disease_preset.dm b/code/datums/diseases/advance/disease_preset.dm index d23f4ddcbc4..597d5a5dd52 100644 --- a/code/datums/diseases/advance/disease_preset.dm +++ b/code/datums/diseases/advance/disease_preset.dm @@ -1,11 +1,9 @@ /datum/disease/advance/random - name = "Experimental Disease" var/randomname = TRUE var/datum/symptom/setsymptom = null var/max_symptoms_override /datum/disease/advance/random/minor - name = "Minor Experimental Disease" max_symptoms_override = 4 /datum/disease/advance/random/New(max_symptoms, max_level = 6, min_level = 1, list/guaranteed_symptoms = setsymptom) @@ -35,9 +33,10 @@ symptoms += new chosen_symptom for(var/guaranteed_symptom in guaranteed_symptoms) symptoms += new guaranteed_symptom + Finalize() + Refresh() /datum/disease/advance/random/macrophage - name = "Unknown Disease" setsymptom = /datum/symptom/macrophage /datum/disease/advance/random/blob diff --git a/code/datums/diseases/advance/symptoms/blobspores.dm b/code/datums/diseases/advance/symptoms/blobspores.dm index 8c8ef33d620..29887dfc1e4 100644 --- a/code/datums/diseases/advance/symptoms/blobspores.dm +++ b/code/datums/diseases/advance/symptoms/blobspores.dm @@ -15,27 +15,42 @@ BONUS */ /datum/symptom/blobspores name = "Blob Spores" + desc = "This symptom causes the host to produce blob spores, which will leave the host at the later stages, and if the host dies, all of the spores will erupt from the host at the same time, while also producing a blob tile." stealth = 1 - resistance = 5 + resistance = 6 stage_speed = -2 - transmittable = 1 - level = -1 + transmission = 1 + level = 9 severity = 3 + naturally_occuring = FALSE + symptom_delay_min = 20 SECONDS + symptom_delay_max = 40 SECONDS + + threshold_descs = list( + "Resistance 8" = "Spawns a strong blob instead of a normal blob", + "Resistance 11" = "There is a chance to spawn a factory blob, instead of a normal blob.", + "Resistance 14" = "Has a chance to spawn a blob node instead of a normal blob" + ) var/ready_to_pop var/factory_blob var/strong_blob var/node_blob +/datum/symptom/blobspores/severityset(datum/disease/advance/A) + . = ..() + if(A.resistance >= 14) + severity += 1 + /datum/symptom/blobspores/Start(datum/disease/advance/A) if(!..()) return - if(A.totalResistance() >= 8) + if(A.resistance >= 11) factory_blob = TRUE - if(A.totalResistance() >= 5) + if(A.resistance >= 8) strong_blob = TRUE - if(A.totalResistance() >= 10) + if(A.resistance >= 14) node_blob = TRUE /datum/symptom/blobspores/Activate(datum/disease/advance/A) @@ -45,31 +60,35 @@ BONUS switch(A.stage) if(1) - if(prob(2)) - to_chat(M, span_notice("You feel bloated.")) + to_chat(M, span_notice("You feel bloated.")) - if(prob(3) && !M.jitteriness) + if(!M.jitteriness) to_chat(M, span_notice("You feel a bit jittery.")) M.jitteriness = 10 if(2) - if(prob(1) && ishuman(M)) + if(ishuman(M)) var/mob/living/carbon/human/H = M H.vomit(TRUE, FALSE) if(3, 4) - if(prob(10)) - to_chat(M, span_notice("You feel blobby?")) + to_chat(M, span_notice("You feel blobby?")) if(5) - ready_to_pop = TRUE - if(prob(5)) - M.audible_emote("coughs up a small amount of blood!") - if(ishuman(M)) - var/mob/living/carbon/human/H = M - var/bleeding_rng = rand(1, 2) - H.drip(bleeding_rng) + M.audible_emote("coughs up a small amount of blood!") + if(ishuman(M)) + var/mob/living/carbon/human/H = M + var/bleeding_rng = rand(1, 2) + H.drip(bleeding_rng) + +/datum/symptom/blobspores/OnStageChange(datum/disease/advance/A) + if(!..()) + return + if(A.stage == 5) + ready_to_pop = TRUE /datum/symptom/blobspores/OnDeath(datum/disease/advance/A) + if(neutered) + return var/mob/living/M = A.affected_mob M.visible_message(span_danger("[M] starts swelling grotesquely!")) addtimer(CALLBACK(src, PROC_REF(pop), A, M), 10 SECONDS) diff --git a/code/datums/diseases/advance/symptoms/choking.dm b/code/datums/diseases/advance/symptoms/choking.dm index 24be1c1a153..eb3b492e494 100644 --- a/code/datums/diseases/advance/symptoms/choking.dm +++ b/code/datums/diseases/advance/symptoms/choking.dm @@ -16,37 +16,62 @@ Bonus */ /datum/symptom/choking - name = "Choking" - stealth = -3 - resistance = -2 - stage_speed = -2 - transmittable = -4 - level = 3 - severity = 3 + name = "Acute Respiratory Distress Syndrome" + desc = "The virus causes shrinking of the host's lungs, causing severe asphyxiation." + stealth = -2 + resistance = 0 + stage_speed = -1 + transmission = -2 + level = 9 + severity = 5 + naturally_occuring = FALSE + base_message_chance = 15 + symptom_delay_min = 10 SECONDS + symptom_delay_max = 30 SECONDS + + var/paralysis = FALSE + + threshold_descs = list( + "Transmission 8" = "Doubles the damage caused by the symptom." + ) + +/datum/symptom/choking/severityset(datum/disease/advance/A) + . = ..() + if(A.transmission >= 8) + severity += 1 + +/datum/symptom/choking/Start(datum/disease/advance/A) + if(!..()) + return + + if(A.transmission >= 8) + power = 2 /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, span_warning(pick("You're having difficulty breathing.", "Your breathing becomes heavy."))) - if(3, 4) - to_chat(M, span_boldwarning(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, span_userdanger(pick("You're choking!", "You can't breathe!"))) - Choke(M, A) - M.emote("gasp") + if(!..()) + return + + var/mob/living/M = A.affected_mob + if(!M.needs_to_breathe() || M.stat == DEAD) + return + + switch(A.stage) + if(3, 4) + to_chat(M, span_warning(pick("Your windpipe feels thin.", "Your lungs feel small-"))) + Choke_stage_3_4(M, A) + M.emote("gasp") + if(5) + to_chat(M, span_userdanger(pick("Your lungs hurt!", "It hurts to 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()) + var/get_damage = rand(5, 10) * power M.adjustOxyLoss(get_damage) - return 1 + return TRUE /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) + var/get_damage = rand(2, 5) * power M.adjustOxyLoss(get_damage) - return 1 + return TRUE diff --git a/code/datums/diseases/advance/symptoms/cockroach.dm b/code/datums/diseases/advance/symptoms/cockroach.dm new file mode 100644 index 00000000000..da7481a9f0d --- /dev/null +++ b/code/datums/diseases/advance/symptoms/cockroach.dm @@ -0,0 +1,61 @@ +/datum/symptom/cockroach + name = "SBG Syndrome" + desc = "Causes bluespace synchronicity with nearby air channels, making the roaches infesting the station's scrubber crawl from the host's face." + stealth = 1 + resistance = 2 + stage_speed = 3 + transmission = 1 + level = 0 + severity = 0 + symptom_delay_min = 15 SECONDS + symptom_delay_max = 40 SECONDS + + var/death_roaches = FALSE + + threshold_descs = list( + "Stage Speed 8" = "Increases roach speed", + "Transmission 8" = "When the host dies, more roaches spawn." + ) + +/datum/symptom/cockroach/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stage_rate >= 8) + symptom_delay_min = 5 + symptom_delay_max = 15 + if(A.transmission >= 8) + death_roaches = TRUE + +/datum/symptom/cockroach/Activate(datum/disease/advance/A) + if(!..()) + return + var/mob/living/M = A.affected_mob + if(M.stat == DEAD) + return + switch(A.stage) + if(2) + if(prob(base_message_chance)) + to_chat(M, span_notice("You feel a tingle under your skin.")) + if(3) + if(prob(base_message_chance)) + to_chat(M, span_notice("Your pores feel drafty.")) + if(prob(5)) + to_chat(M, span_notice("You feel attuned to the atmosphere.")) + if(4) + if(prob(base_message_chance)) + to_chat(M, span_notice("You feel in tune with the station.")) + if(5) + if(prob(base_message_chance)) + M.visible_message(span_danger("[M] squirms as a cockroach crawl from their pores!"), span_userdanger("A cockroach crawls out of your face!!")) + new /mob/living/simple_mob/animal/passive/cockroach(M.loc) + if(prob(base_message_chance)) + to_chat(M, span_notice("You feel something crawling in your pipes!")) + +/datum/symptom/cockroach/OnDeath(datum/disease/advance/A) + if(!..()) + return + if(death_roaches) + var/mob/living/M = A.affected_mob + to_chat(M, span_warning("Your pores explode into a colony of roaches!")) + for(var/i in 1 to rand(1, 5)) + new /mob/living/simple_mob/animal/passive/cockroach(M.loc) diff --git a/code/datums/diseases/advance/symptoms/confusion.dm b/code/datums/diseases/advance/symptoms/confusion.dm index dc28c109a23..1ba1d0ad74e 100644 --- a/code/datums/diseases/advance/symptoms/confusion.dm +++ b/code/datums/diseases/advance/symptoms/confusion.dm @@ -17,22 +17,45 @@ Bonus /datum/symptom/confusion name = "Confusion" + desc = "The virus interferes with the proper function of the neural system, leading to bouts of confusion and erratic movement." stealth = 1 resistance = -1 stage_speed = -3 - transmittable = 0 - level = 4 + transmission = 0 + level = 3 severity = 2 + base_message_chance = 25 + symptom_delay_min = 15 SECONDS + symptom_delay_max = 30 SECONDS + + threshold_descs = list( + "Transmission 6" = "Increases confusion duration.", + "Stealth 4" = "The symptom remains hidden until active." + ) + +/datum/symptom/confusion/severityset(datum/disease/advance/A) + . = ..() + if(A.resistance >= 6) + severity += 1 + +/datum/symptom/confusion/Start(datum/disease/advance/A) + if(!..()) + return + if(A.transmission >= 6) + power = 1.5 + if(A.stealth >= 4) + supress_warning = TRUE /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, span_warning(pick("Your head hurts.", "Your mind blanks for a moment."))) - else - to_chat(M, span_userdanger("You can't think straight!")) - M.AdjustConfused(rand(16, 200)) - - return + if(!..()) + return + var/mob/living/L = A.affected_mob + if(L.stat == DEAD) + return + switch(A.stage) + if(1, 2, 3, 4) + if(prob(base_message_chance) && !supress_warning) + to_chat(L, span_warning(pick("Your head hurts", "Your mind blanks for a moment."))) + else + to_chat(L, span_userdanger("You can't think straight!")) + L.confused = min(50 * power, L.confused+8) diff --git a/code/datums/diseases/advance/symptoms/cough.dm b/code/datums/diseases/advance/symptoms/cough.dm index cc932ab4cc9..440bbc7340e 100644 --- a/code/datums/diseases/advance/symptoms/cough.dm +++ b/code/datums/diseases/advance/symptoms/cough.dm @@ -17,25 +17,70 @@ BONUS /datum/symptom/cough name = "Cough" + desc = "The virus irritates the throat of the host, causing occasional coughing." stealth = -1 resistance = 3 stage_speed = 1 - transmittable = 2 + transmission = 2 level = 1 - severity = 1 + severity = 0 + base_message_chance = 15 + symptom_delay_min = 15 SECONDS + symptom_delay_max = 45 SECONDS + + var/infective = FALSE + + threshold_descs = list( + "Resistance 3" = "Host will drop small items when coughing.", + "Resistance 10" = "Occasonally causes coughing fits that stun the host.", + "Stage Speed 6" = "Increases cough frequency", + "Stealth 4" = "The symptom remains hidden until active.", + "Transmission 11" = "The hosts coughing will occasonally spread the virus." + ) + +/datum/symptom/cough/severityset(datum/disease/advance/A) + . = ..() + if(A.resistance >= 3) + severity += 1 + if(A.resistance >= 10) + severity += 1 + +/datum/symptom/cough/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stealth >= 4) + supress_warning = TRUE + if(A.resistance >= 3) + power = 1.5 + if(A.resistance >= 10) + power = 2 + if(A.stage_rate >= 6) + symptom_delay_max = 10 + if(A.transmission >= 11) + infective = TRUE /datum/symptom/cough/Activate(var/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, span_warning(pick("You swallow excess mucus", "You lightly cough."))) - else - M.emote("cough") + if(!..()) + return + var/mob/living/M = A.affected_mob + if(M.stat == DEAD) + return + switch(A.stage) + if(1, 2, 3) + if(prob(base_message_chance) && !supress_warning) + to_chat(M, span_warning(pick("Your throat itches.", "You lightly cough."))) + else + M.emote("cough") + if(power >= 1.5) var/obj/item/I = M.get_active_hand() - if(I && I.w_class == ITEMSIZE_SMALL) - M.drop_item() - if(!M.wear_mask) // Small spread if not wearing a mask - A.spread(2) - return + if(I && I.w_class == ITEMSIZE_COST_TINY) + M.drop_item(get_turf(M)) + if(power >= 2 && prob(10)) + to_chat(M, span_userdanger(pick("You have a coughing fit!", "You can't stop coughing!"))) + M.Stun(5) + M.emote("cough") + addtimer(CALLBACK(M, TYPE_PROC_REF(/mob, emote), "cough"), 1 SECONDS) + addtimer(CALLBACK(M, TYPE_PROC_REF(/mob, emote), "cough"), 3 SECONDS) + addtimer(CALLBACK(M, TYPE_PROC_REF(/mob, emote), "cough"), 6 SECONDS) + if(infective && prob(50)) + addtimer(CALLBACK(A, TYPE_PROC_REF(/datum/disease, spread), 2), 20) diff --git a/code/datums/diseases/advance/symptoms/damage_converter.dm b/code/datums/diseases/advance/symptoms/damage_converter.dm index b47aa89a6c3..a57c407b8d7 100644 --- a/code/datums/diseases/advance/symptoms/damage_converter.dm +++ b/code/datums/diseases/advance/symptoms/damage_converter.dm @@ -20,7 +20,7 @@ Bonus stealth = 1 resistance = -4 stage_speed = -4 - transmittable = -2 + transmission = -2 level = 4 severity = 0 diff --git a/code/datums/diseases/advance/symptoms/deafness.dm b/code/datums/diseases/advance/symptoms/deafness.dm index 21a44b2e9f4..d32bef5f04a 100644 --- a/code/datums/diseases/advance/symptoms/deafness.dm +++ b/code/datums/diseases/advance/symptoms/deafness.dm @@ -17,22 +17,50 @@ Bonus /datum/symptom/deafness name = "Deafness" - stealth = 1 - resistance = -2 + desc = "The virus causes inflammation of the eardrums, causing intermittent deafness." + stealth = -1 + resistance = -1 stage_speed = 1 - transmittable = -3 - level = 4 - severity = 3 + transmission = -3 + level = 3 + severity = 2 + base_message_chance = 100 + symptom_delay_min = 25 SECONDS + symptom_delay_max = 80 SECONDS -/datum/symptom/deafness/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob + threshold_descs = list( + "Resistance 9" = "Causes permanent hearing loss.", + "Stealth 4" = "The symptom remains hidden until active." + ) - switch(A.stage) - if(3, 4) - to_chat(M, span_warning("[pick("you hear a ringing in your ear.", "You ears pop.")]")) - if(5) - to_chat(M, span_userdanger("You ear pop and begin ringing loudly!")) - M.ear_deaf += 20 - return +/datum/symptom/deafness/severityset(datum/disease/advance/A) + . = ..() + if(A.resistance >= 9) + severity += 1 + +/datum/symptom/deafness/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stealth >= 4) + supress_warning = TRUE + if(A.resistance >= 9) + power = 2 + +/datum/symptom/deafness/Activate(var/datum/disease/advance/A) + if(!..()) + return + var/mob/living/carbon/M = A.affected_mob + if(M.stat == DEAD) + return + switch(A.stage) + if(3, 4) + if(prob(base_message_chance) && !supress_warning) + to_chat(M, span_warning(pick("You hear a ringing in your ears.", "Your ears pop."))) + if(5) + if(power >= 2) + M.adjustEarDamage(100, 10) + to_chat(M, span_userdanger("Your ears pop painfully and start bleeding!")) + M.emote("scream") + else + to_chat(M, span_userdanger("Your ears pop and begin ringing loudly!")) + M.adjustEarDamage(0, 10) diff --git a/code/datums/diseases/advance/symptoms/dizzy.dm b/code/datums/diseases/advance/symptoms/dizzy.dm deleted file mode 100644 index 90bc8836b05..00000000000 --- a/code/datums/diseases/advance/symptoms/dizzy.dm +++ /dev/null @@ -1,37 +0,0 @@ -/* -////////////////////////////////////// - -Dizziness - - Hidden. - Lowers resistance considerably. - Decreases stage speed. - Reduced transmittability - Intense Level. - -Bonus - Shakes the affected mob's screen for short periods. - -////////////////////////////////////// -*/ - -/datum/symptom/dizzy - name = "Dizziness" - stealth = 2 - resistance = -2 - stage_speed = -3 - transmittable = -1 - level = 4 - severity = 2 - -/datum/symptom/dizzy/Activate(var/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, span_warning(pick("You feel dizzy.", "Your head spins."))) - else - to_chat(M, span_userdanger("A wave of dizziness washes over you!")) - M.make_dizzy(10) - return diff --git a/code/datums/diseases/advance/symptoms/fever.dm b/code/datums/diseases/advance/symptoms/fever.dm index 6e252e56cd9..bd3698eb54b 100644 --- a/code/datums/diseases/advance/symptoms/fever.dm +++ b/code/datums/diseases/advance/symptoms/fever.dm @@ -17,24 +17,54 @@ Bonus /datum/symptom/fever name = "Fever" - stealth = 0 + desc = "The virus causes a febrile response from the host, raising it's body temperature." + stealth = -1 resistance = 3 stage_speed = 3 - transmittable = 2 + transmission = 2 level = 2 - severity = 2 + severity = 0 + base_message_chance = 20 + symptom_delay_min = 25 SECONDS + symptom_delay_max = 50 SECONDS -/datum/symptom/fever/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/carbon/M = A.affected_mob + var/unsafe = FALSE + + threshold_descs = list( + "Resistance 5" = "Increases fever intensity, fever can overheat and harm the host.", + "Resistance 10" = "Further increases fever intensity." + ) + +/datum/symptom/fever/severityset(datum/disease/advance/A) + . = ..() + if(A.resistance >= 5) + severity += 1 + if(A.resistance >= 10) + severity += 1 + +/datum/symptom/fever/Start(datum/disease/advance/A) + if(!..()) + return + if(A.resistance >= 5) + power = 1.5 + unsafe = TRUE + if(A.resistance >= 10) + power = 2.5 + +/datum/symptom/fever/Activate(datum/disease/advance/A) + if(!..()) + return + var/mob/living/carbon/M = A.affected_mob + if(M.stat == DEAD) + return + if(!unsafe || A.stage < 4) to_chat(M, span_warning(pick("You feel hot.", "You feel like you're burning."))) - if(M.bodytemperature < BODYTEMP_HEAT_DAMAGE_LIMIT) - Heat(M, A) + else + to_chat(M, span_userdanger(pick("You feel too hot", "You feel like your blod is boiling."))) + set_body_temp(A.affected_mob, A) - return - -/datum/symptom/fever/proc/Heat(var/mob/living/M, var/datum/disease/advance/A) - var/get_heat = (sqrtor0(21+A.totalTransmittable()*2))+(sqrtor0(20+A.totalStageSpeed()*3)) - M.bodytemperature = min(M.bodytemperature + (get_heat * A.stage), BODYTEMP_HEAT_DAMAGE_LIMIT - 1) - return TRUE +/datum/symptom/fever/proc/set_body_temp(mob/living/M, datum/disease/advance/A) + if(!unsafe) + M.bodytemperature = min(M.bodytemperature + max((3 * power) * A.stage, (BODYTEMP_HEAT_DAMAGE_LIMIT - 1))) + else + M.bodytemperature = min(M.bodytemperature + max((3 * power) * A.stage, (BODYTEMP_HEAT_DAMAGE_LIMIT + 20))) diff --git a/code/datums/diseases/advance/symptoms/fire.dm b/code/datums/diseases/advance/symptoms/fire.dm index 956d778f5d5..abb9a24f25c 100644 --- a/code/datums/diseases/advance/symptoms/fire.dm +++ b/code/datums/diseases/advance/symptoms/fire.dm @@ -17,40 +17,70 @@ Bonus /datum/symptom/fire name = "Spontaneous Combustion" + desc = "The virus turns fat into an extremely flammable compound, and raises the body's temperature, making the host burst into flames spontaneously." stealth = 1 - resistance = -4 - stage_speed = -4 - transmittable = -4 - level = 6 - severity = 5 + resistance = -1 + stage_speed = -2 + transmission = -1 + level = 7 + severity = 4 + + base_message_chance = 20 + symptom_delay_min = 40 SECONDS + symptom_delay_max = 85 SECONDS + + var/infective = FALSE + + threshold_descs = list( + "Stage Speed 4" = "Increases the intensity of the flames.", + "Stage Speed 8" = "Further increases the intensity of the flames.", + "Transmission 8" = "Host will spread the virus through skin flake when bursting into flames.", + "Stealth 4" = "The symptom remains hidden until active." + ) + +/datum/symptom/fire/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stage_rate >= 4) + power = 1.5 + if(A.stage_rate >= 8) + power = 2 + if(A.stealth >= 4) + supress_warning = TRUE + if(A.transmission >= 8) + infective = TRUE /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) + if(!..()) + return + var/mob/living/M = A.affected_mob + switch(A.stage) + if(3) + if(prob(base_message_chance) && !supress_warning && M.stat != DEAD) to_chat(M, span_warning(pick("You feel hot.", "You hear a crackling noise.", "You smell smoke."))) - if(4) - Firestacks_stage_4(M, A) - M.IgniteMob() - to_chat(M, span_userdanger("Your skin bursts into flames!")) - M.emote("scream") - if(5) - Firestacks_stage_5(M, A) - M.IgniteMob() + if(4) + Firestacks_stage_4(M, A) + M.IgniteMob() + to_chat(M, span_userdanger("Your skin bursts into flames!")) + M.emote("scream") + if(5) + Firestacks_stage_5(M, A) + M.IgniteMob() + if(M.stat != DEAD) to_chat(M, span_userdanger("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 + M.adjust_fire_stacks(1 * power) + M.take_overall_damage(burn = 2 * power) + if(infective) + M.visible_message(span_danger("[M] bursts into flames, spreading burning sparks about the area!")) + return TRUE /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 + M.adjust_fire_stacks(3 * power) + M.take_overall_damage(burn = 5 * power) + if(infective) + M.visible_message(span_danger("[M] bursts into flames, spreading burning sparks about the area!")) + return TRUE diff --git a/code/datums/diseases/advance/symptoms/flesh_eating.dm b/code/datums/diseases/advance/symptoms/flesh_eating.dm index 00a5a273e7b..2e181a7bafb 100644 --- a/code/datums/diseases/advance/symptoms/flesh_eating.dm +++ b/code/datums/diseases/advance/symptoms/flesh_eating.dm @@ -17,26 +17,57 @@ Bonus /datum/symptom/flesh_eating name = "Necrotizing Fasciitis" + desc = "The virus aggressively attacks the skin and blood, leading to extreme bleeding." stealth = -3 - resistance = -4 + resistance = -2 stage_speed = 0 - transmittable = -4 - level = 6 - severity = 5 + transmission = -1 + level = 7 + severity = 4 + base_message_chance = 50 + symptom_delay_min = 20 SECONDS + symptom_delay_max = 60 SECONDS + + var/bleed = FALSE + var/damage = FALSE + + threshold_descs = list( + "Resistance 10" = "The host takes brute damage as their flesh is burst open.", + "Transmission 8" = "The host will bleed far more violently, loosing even more blood, and spraying infected blood everywhere." + ) + +/datum/symptom/flesh_eating/Start(datum/disease/advance/A) + if(!..()) + return + if(A.resistance >= 10) + damage = TRUE + if(A.transmission >= 8) + power = 2 + bleed = TRUE /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) + if(!..()) + return + var/mob/living/M = A.affected_mob + switch(A.stage) + if(2, 3) + if(prob(base_message_chance) && M.stat != DEAD) to_chat(M, span_warning(pick("You feel a sudden pain across your body.", "Drops of blood appear suddenly on your skin."))) - if(4,5) - to_chat(M, span_userdanger(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 + if(4, 5) + if(M.stat != DEAD) + to_chat(M, span_userdanger(pick("You cringe as a violent pan takes over your body.", "It feels like your body is eating itself inside out.", "IT HURTS."))) + Flesheat(M, A) /datum/symptom/flesh_eating/proc/Flesheat(mob/living/M, datum/disease/advance/A) - var/get_damage = ((sqrtor0(16-A.totalStealth()))*5) - M.adjustBruteLoss(get_damage) - return 1 + if(damage) + M.take_overall_damage(BRUTE = rand(15, 25)) + if(ishuman(M)) + return + var/mob/living/carbon/human/H = M + var/obj/item/organ/external/O = pick(H.organs) + + if(bleed) + O.createwound(PIERCE, 5 * power) + else + O.createwound(BRUISE, 7.5 * power) + return TRUE diff --git a/code/datums/diseases/advance/symptoms/flip.dm b/code/datums/diseases/advance/symptoms/flip.dm index 2c9164a5365..48c01bc1f47 100644 --- a/code/datums/diseases/advance/symptoms/flip.dm +++ b/code/datums/diseases/advance/symptoms/flip.dm @@ -15,18 +15,20 @@ BONUS ////////////////////////////////////// */ -/datum/symptom/spyndrome +/datum/symptom/flip name = "Flippinov" + desc = "The virus hijacks the host's motor system, making them flip incontrollably." stealth = 2 resistance = 0 stage_speed = 3 - transmittable = 1 + transmission = 1 + symptom_delay_min = 15 SECONDS + symptom_delay_max = 40 SECONDS level = 1 severity = 0 -/datum/symptom/spyndrome/Activate(datum/disease/advance/A) - ..() - - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/L = A.affected_mob - L.emote("flip") +/datum/symptom/flip/Activate(datum/disease/advance/A) + if(!..()) + return + var/mob/living/M = A.affected_mob + M.emote("flip") diff --git a/code/datums/diseases/advance/symptoms/genetics.dm b/code/datums/diseases/advance/symptoms/genetics.dm new file mode 100644 index 00000000000..97d9082bc2a --- /dev/null +++ b/code/datums/diseases/advance/symptoms/genetics.dm @@ -0,0 +1,134 @@ +/datum/symptom/genetic + name = "Basic Genetic Symptom (does nothing)" + stealth = 0 + resistance = 0 + stage_speed = 0 + transmission = 0 + level = -1 + base_message_chance = 20 + symptom_delay_min = 1 + symptom_delay_max = 1 + var/datum/gene/trait/mutation + var/passive_message + var/preset = FALSE + +/datum/symptom/genetic/Start(datum/disease/advance/A) + if(!..()) + return + var/mob/living/carbon/human/H = A.affected_mob + if(mutation) + preset = H.dna.GetSEState(mutation.block) + +/datum/symptom/genetic/OnAdd(datum/disease/advance/A) + . = ..() + if(mutation && !preset) + var/mob/living/carbon/human/H = A.affected_mob + H.dna.SetSEState(mutation.block, TRUE) + domutcheck(H, null, TRUE) + H.UpdateAppearance() + +/datum/symptom/genetic/OnRemove(datum/disease/advance/A) + . = ..() + if(mutation && !preset) + var/mob/living/carbon/human/H = A.affected_mob + H.dna.SetSEState(mutation.block, FALSE) + domutcheck(H, null, TRUE) + H.UpdateAppearance() + +/datum/symptom/genetic/Activate(datum/disease/advance/A) + if(!..()) + return + var/mob/living/carbon/human/H = A.affected_mob + switch(A.stage) + if(4, 5) + if(passive_message && prob(2)) + to_chat(H, passive_message) + +/* +////////////////////////////////////// + +Telepathy + + Hidden. + Decreases resistance. + Decreases stage speed significantly. + Decreases transmittablity tremendously. + Critical Level. + +Bonus + The user gains telepathy. + +////////////////////////////////////// +*/ + +/datum/symptom/genetic/telepathy + name = "Pineal Gland Decalcification" + stealth = 2 + resistance = -2 + stage_speed = -3 + transmission = -4 + level = 5 + severity = 0 + +/datum/symptom/genetic/telepathy/New() + . = ..() + mutation = get_gene_from_trait(/datum/trait/positive/superpower_remotetalk) + +/* +////////////////////////////////////// + +Hematophagy + + Little bit hidden. + Decreases resistance slightly. + Decreases stage speed tremendously. + Slightly increased transmittablity. + Intense level. + +BONUS + The host must feed on BLOOD + +////////////////////////////////////// +*/ + +/datum/symptom/genetic/hematophagy + name = "Hematophagy" + stealth = 1 + resistance = -1 + resistance = -4 + transmission = 1 + severity = 1 + +/datum/symptom/genetic/hematophagy/New() + . = ..() + mutation = get_gene_from_trait(/datum/trait/positive/superpower_remotetalk) + +/* +////////////////////////////////////// + +Pica + + Not noticable or unnoticable. + Decreases resistance. + Increases stage speed. + Little transmittable. + Low Level. + +BONUS + The host gains hunger for any kind of object. + +////////////////////////////////////// +*/ + +/datum/symptom/genetic/pica + name = "Pica" + stealth = 0 + resistance = -2 + stage_speed = 3 + transmission = 1 + level = 1 + severity = 0 + +/datum/symptom/genetic/pica/New() + . = ..() + mutation = get_gene_from_trait(/datum/trait/neutral/trashcan) diff --git a/code/datums/diseases/advance/symptoms/hair.dm b/code/datums/diseases/advance/symptoms/hair.dm index 2b2342ae08a..fc41c3207c8 100644 --- a/code/datums/diseases/advance/symptoms/hair.dm +++ b/code/datums/diseases/advance/symptoms/hair.dm @@ -6,7 +6,7 @@ Alopecia Noticable. Decreases resistance slightly. Reduces stage speed slightly. - Transmittable. + transmission. Intense Level. BONUS @@ -17,16 +17,19 @@ BONUS /datum/symptom/shedding name = "Alopecia" - stealth = -1 - resistance = -1 - stage_speed = -1 - transmittable = 2 - level = 4 - severity = 1 + desc = "The virus attacks hair follicles, making hair thin and brittle." + stealth = 0 + resistance = 3 + stage_speed = 2 + transmission = 2 + level = 5 + severity = 0 + base_message_chance = 50 + symptom_delay_min = 45 + symptom_delay_max = 90 /datum/symptom/shedding/Activate(datum/disease/advance/A) - ..() - if(!prob(SYMPTOM_ACTIVATION_PROB)) + if(!..()) return if(ishuman(A.affected_mob)) return diff --git a/code/datums/diseases/advance/symptoms/hallucigen.dm b/code/datums/diseases/advance/symptoms/hallucigen.dm index 4bd1e44401d..4db05f1878d 100644 --- a/code/datums/diseases/advance/symptoms/hallucigen.dm +++ b/code/datums/diseases/advance/symptoms/hallucigen.dm @@ -17,24 +17,67 @@ Bonus /datum/symptom/hallucigen name = "Hallucigen" - stealth = -2 - resistance = -3 - stage_speed = -3 - transmittable = -1 - level = 5 - severity = 3 + desc = "The virus stimulates the brain, causing occasional hallucinations." + stealth = 1 + resistance = -1 + stage_speed = 1 + transmission = 1 + level = 3 + severity = 1 + base_message_chance = 25 + symptom_delay_min = 10 SECONDS + symptom_delay_max = 70 SECONDS + + var/fake_healthy = FALSE + + threshold_descs = list( + "Stage Speed 7" = "Increases the amount of hallucinations.", + "Stealth 2" = "The virus mimics positive symptoms" + ) + +/datum/symptom/hallucigen/severityset(datum/disease/advance/A) + . = ..() + if(A.stage_rate >= 7) + severity += 1 + +/datum/symptom/hallucigen/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stealth >= 2) + fake_healthy = TRUE + base_message_chance = 50 + if(A.stage_rate >= 7) + power = 2 /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, span_warning(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, span_boldwarning(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, span_userdanger(pick("Oh, your head...", "Your head pounds.", "They're everywhere! Run!", "Something in the shadows..."))) - M.hallucination = rand(5, 10) + if(!..()) + return + var/mob/living/carbon/M = A.affected_mob + if(M.stat == DEAD) + return + var/list/healthy_messages = list( + "Your lungs feel great.", + "You realize you haven't been breathing", + "You don't feel the need to breathe.", + "Your eyes feel great.", + "You are now blinking manually.", + "You don't feel the need to blnk." + ) - return + switch(A.stage) + if(1, 2) + if(prob(base_message_chance)) + if(!fake_healthy) + to_chat(M, span_notice(pick("Something appears in your peripheral vision, then winks out.", "You hear a faint whisper with no source.", "Your head aches."))) + else + to_chat(M, span_notice(pick(healthy_messages))) + if(3, 4) + if(prob(base_message_chance)) + if(!fake_healthy) + to_chat(M, span_danger(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, span_notice(pick(healthy_messages))) + else + if(prob(base_message_chance)) + to_chat(M, span_userdanger(pick("Oh, your head...", "Your head pounds.", "They're everywhere! Run!", "Something is in the shadows..."))) + M.hallucination += (45 * power) diff --git a/code/datums/diseases/advance/symptoms/headache.dm b/code/datums/diseases/advance/symptoms/headache.dm index de84b21fc11..94cc4bccc2d 100644 --- a/code/datums/diseases/advance/symptoms/headache.dm +++ b/code/datums/diseases/advance/symptoms/headache.dm @@ -17,16 +17,55 @@ BONUS /datum/symptom/headache name = "Headache" + desc = "The virus causes inflammation inside the brain, causing constant headaches." stealth = -1 resistance = 4 stage_speed = 2 - transmittable = 0 + transmission = 0 level = 1 - severity = 1 + severity = 0 + base_message_chance = 100 + symptom_delay_min = 20 SECONDS + symptom_delay_max = 40 SECONDS -/datum/symptom/headache/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - to_chat(M, span_warning(pick("Your head hurts.", "Your head starts pounding."))) - return + threshold_descs = list( + "Stage Speed 6" = "Headaches will cause severe pain, that weakens the host.", + "Stage Speed 9" = "Headaches become less frequent but far more intense, preventing any action from the host.", + "Stealth 4" = "Reduces headache frequency until later stages." + ) + +/datum/symptom/headache/severityset(datum/disease/advance/A) + . = ..() + if(A.stage_rate >= 6) + severity += 1 + if(A.stage_rate >= 9) + severity += 1 + +/datum/symptom/headache/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stealth >= 4) + base_message_chance = 50 + if(A.stage_rate >= 6) + power = 2 + if(A.stage_rate >= 9) + symptom_delay_min = 30 SECONDS + symptom_delay_max = 60 SECONDS + power = 3 + +/datum/symptom/headache/Activate(datum/disease/advance/A) + if(!..()) + return + + var/mob/living/M = A.affected_mob + if(M.stat == DEAD) + return + if(power < 2) + if(prob(base_message_chance) || A.stage >= 4) + to_chat(M, span_warning(pick("Your head hurts.", "Your head pounds."))) + if(power >= 2 && A.stage >= 4) + to_chat(M, span_danger(pick("Your head hurts a lot.", "Your head pounds incessantly."))) + M.Weaken(10) + if(power >= 3 && A.stage >= 5) + to_chat(M, span_userdanger(pick("Your head hurts!", "You feel a burning knife inside your brain!", "A wave of pain fills your brain!"))) + M.Stun(15) diff --git a/code/datums/diseases/advance/symptoms/heal.dm b/code/datums/diseases/advance/symptoms/heal.dm index 0d77fc6f072..78f23343f30 100644 --- a/code/datums/diseases/advance/symptoms/heal.dm +++ b/code/datums/diseases/advance/symptoms/heal.dm @@ -1,125 +1,222 @@ -/* -////////////////////////////////////// - -Healing - - Little bit hidden. - Lowers resistance tremendously. - Decreases stage speed tremendously. - Decreases transmittablity temrendously. - Fatal Level. - -Bonus - Heals toxins in the affected mob's blood stream. - -////////////////////////////////////// -*/ - /datum/symptom/heal - name = "Toxic Filter" - stealth = 1 - resistance = -4 - stage_speed = -4 - transmittable = -4 - level = 6 - severity = 0 + name = "Basic Healing (does nothing)" + stealth = 0 + resistance = 0 + stage_speed = 0 + transmission = 0 + level = -1 + base_message_chance = 20 + symptom_delay_min = 1 + symptom_delay_max = 1 + var/passive_message = "" + threshold_descs = list( + "Stealth 4" = "Healing will no longer be visible to onlookers." + ) -/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) - return - -/datum/symptom/heal/proc/Heal(mob/living/M, datum/disease/advance/A) - var/get_damage = max(0, (sqrtor0(20+A.totalStageSpeed())*(1+rand()))) - M.adjustToxLoss(-get_damage) +/datum/symptom/heal/Start(datum/disease/advance/A) + if(!..()) + return FALSE return TRUE -/* -////////////////////////////////////// +/datum/symptom/heal/Activate(datum/disease/advance/A) + if(!..()) + return + var/mob/living/M = A.affected_mob + switch(A.stage) + if(4, 5) + var/effectiveness = CanHeal(A) + if(!effectiveness) + if(passive_message && prob(2) && passive_message_condition(M)) + to_chat(M, passive_message) + return + else + Heal(M, A, effectiveness) + return -Metabolism +/datum/symptom/heal/proc/CanHeal(datum/disease/advance/A) + return power - Little bit hidden. - Lowers resistance. - Decreases stage speed. - Decreases transmittablity temrendously. - High Level. +/datum/symptom/heal/proc/Heal(mob/living/M, /datum/disease/advance/A, actual_power = 1) + return TRUE -Bonus - Cures all diseases (except itself) and creates anti-bodies for them until the symptom dies. +/datum/symptom/heal/proc/passive_message_condition(mob/living/M) + return TRUE -////////////////////////////////////// -*/ +/datum/symptom/heal/chem + name = "Toxolysis" + desc = "The virus rapidly breaks down any foreign chemicals in the bloodstream." + stealth = 0 + resistance = -2 + stage_speed = 2 + transmission = -2 + level = 6 + power = 2 + var/food_conversion = FALSE + + threshold_descs = list( + "Resistance 7" = "Increases chem removal speed.", + "Stage Speed 6" = "Consumed chemicals nourish the host." + ) + +/datum/symptom/heal/chem/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stage >= 6) + food_conversion = TRUE + if(A.resistance >= 7) + power = 4 + +/datum/symptom/heal/chem/Heal(mob/living/carbon/human/H, datum/disease/advance/A, actual_power) + for(var/datum/reagent/R in H.bloodstr.reagent_list) + H.reagents.remove_reagent(R.type, actual_power) + if(food_conversion) + H.adjust_nutrition(2) + if(prob(2) && H.stat != DEAD) + to_chat(H, span_notice("You feel a mild warmth as your blood purifies itself.")) + + +/datum/symptom/growth + name = "Pituitary Disruption" + desc = "Causes uncontrolled growth in the subject." + stealth = -3 + resistance = -2 + stage_speed = 1 + transmission = -2 + level = 8 + severity = 1 + symptom_delay_min = 10 SECONDS + symptom_delay_max = 20 SECONDS + + var/current_size = 1 + var/tetsuo = FALSE + var/bruteheal = FALSE + + var/datum/mind/ownermind + + threshold_descs = list( + "Stage Speed 6" = "The disease heals brute damage at a fast rate, but causes expulsion of benign tumors.", + "Stage Speed 12" = "The disease heals brute damage incredibly fast, but deteriorates cell health and causes tumors to become more advanced. The disease will also regenerate lost limbs." + ) + +/datum/symptom/growth/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stage_rate >= 6) + bruteheal = TRUE + if(A.stage_rate >= 12) + tetsuo = TRUE + power = 3 + + var/mob/living/carbon/human/H = A.affected_mob + ownermind = H.mind + +/datum/symptom/growth/Activate(datum/disease/advance/A) + if(!..()) + return + var/mob/living/carbon/human/H = A.affected_mob + switch(A.stage) + if(4, 5) + if(prob(5) && bruteheal) + if(H.stat != DEAD) + to_chat(H, span_userdanger("You retch, and a splatter of gore escapes your gullet!")) + H.vomit(lost_nutrition = 0, blood = TRUE, stun = FALSE) + // Spitting tumors go here! IF they get up-ported. + if(tetsuo) + var/list/missing = H.get_missing_limbs() + if(prob(35) && H.mind && ishuman(H) && H.stat != DEAD) + new /obj/effect/decal/cleanable/blood/gibs(H.loc) + if(missing.len) + for(var/Z in missing) + if(H.regenerate_limb(Z, TRUE)) + playsound(H, 'sound/effects/blobattack.ogg', 50, 1) + H.visible_message(span_warning("[H]'s missing limbs reform, making a loud, grotesque sound!"), span_userdanger("You limbs regrow, making a loud, crunchy sound and giving you great pain!")) + H.emote("scream") + if(Z == BP_HEAD) + if(isliving(ownermind.current)) + var/mob/living/owner = ownermind.current + if(owner.stat != DEAD) + ownermind = null + break + ownermind.transfer_to(H) + H.grab_ghost() + break + if(bruteheal) + H.heal_overall_damage(2 * power) + if(prob(33) && tetsuo) + H.adjustCloneLoss(1) + else + if(prob(base_message_chance) && H.stat != DEAD) + to_chat(H, span_notice(pick("You feel bloated.", "The station seems small.", "You are the strongest."))) + return /datum/symptom/heal/metabolism - name = "Anti-Bodies Metabolism" + name = "Metabolic Boost" + desc = "The virus causes the host's metabolism to accelerate rapidly, making them process chemicals twice as fast, but also causing increased hunger." stealth = -1 - resistance = -1 - stage_speed = -1 - transmittable = -4 - level = 3 - severity = 0 - var/list/cured_diseases = list() + resistance = -2 + stage_speed = 2 + transmission = 1 + level = 6 + var/triple_metabolism = FALSE + var/reduced_hunger = FALSE -/datum/symptom/heal/metabolism/Heal(mob/living/M, datum/disease/advance/A) - var/cured = 0 - for(var/thing in M.GetViruses()) - var/datum/disease/D = thing - if(D.virus_heal_resistant) - continue - if(D != A) - cured = TRUE - cured_diseases += D.GetDiseaseID() - D.cure() - if(cured) - to_chat(M, span_notice("You feel much better.")) + threshold_descs = list( + "Stealth 3" = "Reduces hunger rate.", + "Stage Speed 10" = "Chemical metabolization is tripled instead of doubled." + ) -/datum/symptom/heal/metabolism/End(datum/disease/advance/A) - var/mob/living/M = A.affected_mob - if(istype(M)) - if(length(cured_diseases)) - for(var/res in M.GetResistances()) - M.resistances -= res - to_chat(M, span_warning("You feel weaker.")) +/datum/symptom/heal/metabolism/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stage_rate >= 10) + triple_metabolism = TRUE + if(A.stealth >= 3) + reduced_hunger = TRUE -/* -////////////////////////////////////// +/datum/symptom/heal/metabolism/Heal(mob/living/carbon/human/H, datum/disease/advance/A, actual_power) + if(!istype(H)) + return + if(H.ingested) + H.ingested.metabolize() + if(triple_metabolism) + H.ingested.metabolize() + var/lost_nutrition = 9 - (reduced_hunger * 5) + H.adjust_nutrition(-lost_nutrition * 0.1) + if(prob(2) && H.stat != DEAD) + to_chat(H, span_notice("You feel an odd gurgle in your stomach, as if it was working much faster than normal.")) + return TRUE -Longevity +/datum/symptom/heal/oxygen + name = "Oxygenation" + desc = "The virus causes the host's body to produce oxygen, allowing self-respiration." + stealth = 1 + resistance = -3 + stage_speed = -3 + transmission = -4 + level = 8 + base_message_chance = 5 + symptom_delay_min = 5 SECONDS + symptom_delay_max = 20 SECONDS + var/regenerate_blood = FALSE - Medium hidden boost. - Large resistance boost. - Large stage speed boost. - Large transmittablity boost. - High Level. + threshold_descs = list( + "Resistance 8" = "Additionally regenerates lost blood." + ) -Bonus - After a certain amount of time the symptom will cure itself. +/datum/symptom/heal/oxygen/Start(datum/disease/advance/A) + if(!..()) + return + if(A.resistance >= 8) + regenerate_blood = TRUE -////////////////////////////////////// -*/ +/datum/symptom/heal/oxygen/Heal(mob/living/carbon/human/H, datum/disease/advance/A, actual_power) + if(!istype(H)) + return + if(H.getOxyLoss() > 0) + H.adjustOxyLoss(3 * power) + if(regenerate_blood && H.vessel.get_reagent_amount(REAGENT_ID_BLOOD) < H.species.blood_volume) + H.add_chemical_effect(CE_BLOODRESTORE, 1) -/datum/symptom/heal/longevity - name = "Longevity" - stealth = 3 - resistance = 4 - stage_speed = 4 - transmittable = 4 - level = 3 - severity = 0 - var/longevity = 30 - -/datum/symptom/heal/longevity/Heal(mob/living/M, datum/disease/advance/A) - longevity -= 1 - if(!longevity) - A.cure() - -/datum/symptom/heal/longevity/Start(datum/disease/advance/A) - longevity = rand(initial(longevity) - 5, initial(longevity) + 5) /* ////////////////////////////////////// @@ -136,19 +233,19 @@ Bonus Heals clone damage, treats radiation. ////////////////////////////////////// -*/ /datum/symptom/heal/dna name = "Deoxyribonucleic Acid Restoration" stealth = -1 resistance = -1 stage_speed = 0 - transmittable = -3 + transmission = -3 level = 5 severity = 0 /datum/symptom/heal/dna/Heal(var/mob/living/carbon/M, var/datum/disease/advance/A) - var/amt_healed = max(0, (sqrtor0(20+A.totalStageSpeed()*(3+rand())))-(sqrtor0(16+A.totalStealth()*rand()))) + var/amt_healed = max(0, (sqrtor0(20+A.stage_rate*(3+rand())))-(sqrtor0(16+A.stealth*rand()))) M.adjustBrainLoss(-amt_healed) M.radiation = max(M.radiation - 3, 0) return TRUE +*/ diff --git a/code/datums/diseases/advance/symptoms/hematophagy.dm b/code/datums/diseases/advance/symptoms/hematophagy.dm deleted file mode 100644 index 086c0400a99..00000000000 --- a/code/datums/diseases/advance/symptoms/hematophagy.dm +++ /dev/null @@ -1,43 +0,0 @@ -/* -////////////////////////////////////// - -Hematophagy - - Little bit hidden. - Decreases resistance slightly. - Decreases stage speed tremendously. - Slightly increased transmittablity. - Intense level. - -BONUS - The host must feed on BLOOD - -////////////////////////////////////// -*/ - -/datum/symptom/hematophagy - name = "Hematophagy" - stealth = 1 - resistance = -1 - resistance = -4 - transmittable = 1 - level = 4 - severity = 1 - -/datum/symptom/hematophagy/Start(datum/disease/advance/A) - if(ishuman(A.affected_mob)) - var/mob/living/carbon/human/H = A.affected_mob - - H.species.organic_food_coeff = 0 - H.species.bloodsucker = TRUE - - add_verb(H, /mob/living/carbon/human/proc/bloodsuck) - -/datum/symptom/hematophagy/End(datum/disease/advance/A) - if(ishuman(A.affected_mob)) - var/mob/living/carbon/human/H = A.affected_mob - - H.species.organic_food_coeff = initial(H.species.organic_food_coeff) - H.species.bloodsucker = initial(H.species.bloodsucker) - - remove_verb(H, /mob/living/carbon/human/proc/bloodsuck) diff --git a/code/datums/diseases/advance/symptoms/itching.dm b/code/datums/diseases/advance/symptoms/itching.dm index f9e67814439..5eeada1d924 100644 --- a/code/datums/diseases/advance/symptoms/itching.dm +++ b/code/datums/diseases/advance/symptoms/itching.dm @@ -17,16 +17,43 @@ BONUS /datum/symptom/itching name = "Itching" + desc = "The virus irritates the skin, causing itching." stealth = 0 resistance = 3 stage_speed = 3 - transmittable = 1 + transmission = 1 level = 1 - severity = 1 + severity = 0 + symptom_delay_min = 15 SECONDS + symptom_delay_max = 35 SECONDS -/datum/symptom/itching/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - to_chat(M, span_warning("Your [pick("back", "arm", "leg", "elbow", "head")] itches.")) - return + var/scratch = FALSE + + threshold_descs = list( + "Transmission 6" = "Increases frequency of itching.", + "Stage Speed 7" = "The host will scratch itself when itchin, causing superficial damage." + ) + +/datum/symptom/itching/Start(datum/disease/advance/A) + if(!..()) + return + if(A.transmission >= 6) + symptom_delay_min = 5 SECONDS + symptom_delay_max = 10 SECONDS + if(A.stage >= 7) + scratch = TRUE + +/datum/symptom/itching/Activate(datum/disease/advance/A) + if(!..()) + return + + var/mob/living/carbon/M = A.affected_mob + if(M.stat >= DEAD) + return + var/picked_bodypart = pick(BP_HEAD, BP_TORSO, BP_R_ARM, BP_L_ARM, BP_R_LEG, BP_L_LEG) + var/obj/item/organ/bodypart = M.get_organ(picked_bodypart) + var/can_scratch = scratch && !M.incapacitated() + if(bodypart && !bodypart.robotic) + M.visible_message("[can_scratch ? span_warning("[M] scratches their [bodypart.name].") : ""]", span_notice("Your [bodypart.name] itches. [can_scratch ? " You scratch it." : ""]")) + if(can_scratch) + bodypart.take_damage(0.5) diff --git a/code/datums/diseases/advance/symptoms/language.dm b/code/datums/diseases/advance/symptoms/language.dm index 1f06e5c030c..7d3f95e83af 100644 --- a/code/datums/diseases/advance/symptoms/language.dm +++ b/code/datums/diseases/advance/symptoms/language.dm @@ -17,16 +17,36 @@ Bonus /datum/symptom/language name = "Lingual Disocation" - stealth = 3 + desc = "The virus attack the broca area of the brain, messing with the host's speech." + stealth = 0 resistance = 2 - stage_speed = -2 - transmittable = -1 - level = 3 + stage_speed = 1 + transmission = 1 + level = 1 severity = 1 + symptom_delay_min = 20 SECONDS + symptom_delay_max = 50 SECONDS + + var/gibberish = FALSE + + threshold_descs = list( + "Resistance 5" = "The host might end up speaking a completely made up language." + ) + +/datum/symptom/language/Start(datum/disease/advance/A) + if(!..()) + return + if(A.resistance) + gibberish = TRUE /datum/symptom/language/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/carbon/human/H = A.affected_mob - H.apply_default_language(pick(H.languages)) - return + if(!..()) + return + var/mob/living/L = A.affected_mob + if(L.stat == DEAD) + return + + if(gibberish && prob(10)) + L.apply_default_language(LANGUAGE_GIBBERISH) + else + L.apply_default_language(pick(L.languages)) diff --git a/code/datums/diseases/advance/symptoms/light.dm b/code/datums/diseases/advance/symptoms/light.dm new file mode 100644 index 00000000000..0d3e5b697d3 --- /dev/null +++ b/code/datums/diseases/advance/symptoms/light.dm @@ -0,0 +1,53 @@ +/datum/symptom/light + name = "Photosensitive muscle condensation" + desc = "The virus will cause muscles to contract when exposed to light, resulting in lowered speed, but increased durability. Muscles will become more malleable in the darkness, resulting in the host moving faster, but being more easily bruised." + stealth = 0 + resistance = 2 + stage_speed = -3 + transmission = 0 + level = 8 + severity = -2 + var/currenthealthmodifier + threshold_descs = list( + "Stealth 3" = "The virus causes a wider disparity between light and dark." + ) + +/datum/symptom/light/severityset(datum/disease/advance/A) + . = ..() + if(A.stealth >= 3) + severity -= 1 + +/datum/symptom/light/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stealth >= 3) + power = 2 + +/datum/symptom/light/Activate(datum/disease/advance/A) + if(!..()) + return + var/mob/living/carbon/human/H = A.affected_mob + if(H.stat >= DEAD) + return + var/realpower = power + var/healthchange = min(1 * realpower, (10 * realpower) - currenthealthmodifier) + if(isturf(H.loc)) + var/turf/T = H.loc + var/light_amount = min(1, T.get_lumcount()) - 0.5 + if(light_amount < 0.5) + realpower = power * -1 + healthchange = max(1 * realpower, (10 * realpower) - currenthealthmodifier) + if(prob(5)) + to_chat(H, span_warning(pick("You feel vulnerable.", "Your limbs feel loose and limber.", "The dark makes you feel realxed."))) + else if(prob(5)) + to_chat(H, span_warning(pick("Your muscles feel tight.", "You feel lethargic.", "Your muscles feel hard and tough."))) + if(A.stage >= 5) + currenthealthmodifier += healthchange + H.maxHealth += healthchange + H.health += healthchange + +/datum/symptom/light/End(datum/disease/advance/A) + . = ..() + var/mob/living/carbon/human/H = A.affected_mob + H.maxHealth -= currenthealthmodifier + H.health -= currenthealthmodifier diff --git a/code/datums/diseases/advance/symptoms/macrophage.dm b/code/datums/diseases/advance/symptoms/macrophage.dm index a04a3fc50fe..77ccb5d2e89 100644 --- a/code/datums/diseases/advance/symptoms/macrophage.dm +++ b/code/datums/diseases/advance/symptoms/macrophage.dm @@ -17,57 +17,84 @@ BONUS /datum/symptom/macrophage name = "Macrophage" + desc = "The virus grows within the host, ceasing to be microscopic and causing severe bodily harm. These Phages will seek out, attack, and infect more viable hosts." stealth = -4 resistance = -1 stage_speed = -2 - transmittable = 2 - level = 6 + transmission = 2 + level = 9 severity = 2 + symptom_delay_min = 40 + symptom_delay_max = 60 var/gigagerms = FALSE var/netspeed = 0 var/phagecounter = 10 + threshold_descs = list( + "Stage Speed" = "The higher the stage speed, the more frequently will burst from the host.", + "Resistance" = "The higher the resistance, the more health phages will have, and the more damage the will do.", + "Transmission 10" = "Phages can be larger, and more aggressive.", + "Transmission 12" = "Phages will carry all diseases within the host, instead of only containing their own." + ) + +/datum/symptom/macrophage/severityset(datum/disease/advance/A) + . = ..() + if(A.transmission >= 10) + severity += 2 + /datum/symptom/macrophage/Start(datum/disease/advance/A) - netspeed = max(1, A.stage) - if(A.severity >= HARMFUL) + if(!..()) + return + netspeed = max(1, A.stage_rate) + if(A.transmission >= 10) gigagerms = TRUE /datum/symptom/macrophage/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2, 3) + if(!..()) + return + var/mob/living/M = A.affected_mob + switch(A.stage) + if(1, 2, 3) + if(prob(base_message_chance) && M.stat != DEAD) to_chat(M, span_notice("Your skin crawls.")) - if(4) + if(4) + if(prob(base_message_chance)) M.visible_message(span_danger("Lumps form on [M]'s skin!"), span_userdanger("You cringe in pain as lumps form and move around on your skin!")) - if(5) - phagecounter -= max(2, A.totalStageSpeed()) - if(gigagerms && phagecounter <= 0) - Burst(A, M, TRUE) - phagecounter += 10 - while(phagecounter <= 0) - phagecounter += 5 - Burst(A, M) + if(5) + phagecounter -= max(2, A.stage_rate) + if(gigagerms && phagecounter <= 0) + Burst(A, M, TRUE) + phagecounter += 10 + while(phagecounter <= 0) + phagecounter += 5 + Burst(A, M) /datum/symptom/macrophage/proc/Burst(datum/disease/advance/A, var/mob/living/M, var/gigagerms = FALSE) var/mob/living/simple_mob/vore/aggressive/macrophage/phage - phage = new(M.loc) - M.apply_damage(rand(1, 7)) - phage.viruses = A.Copy() - phage.health += A.totalResistance() - phage.maxHealth += A.totalResistance() + + if(gigagerms) + phage = new /mob/living/simple_mob/vore/aggressive/macrophage/giant(get_turf((M.loc))) + phage.melee_damage_lower = rand(10, 15) + phage.melee_damage_upper = rand(15, 20) + M.apply_damage(rand(10, 20)) + playsound(M, 'sound/effects/splat.ogg', 50, 1) + M.emote("scream") + else + phage = new(get_turf((M.loc))) + M.apply_damage(rand(1, 7)) + + phage.health += A.resistance + phage.maxHealth += A.resistance phage.infections += A phage.base_disease = A - if(A.spread_flags & CONTACT_GENERAL) + if(A.transmission >= 12) for(var/datum/disease/D in M.GetViruses()) - if((D.spread_flags & SPECIAL) || (D.spread_flags & NON_CONTAGIOUS)) + if((D.spread_flags & DISEASE_SPREAD_SPECIAL) || (D.spread_flags & DISEASE_SPREAD_CONTACT)) continue if(D == A) continue - phage.viruses += D - - M.visible_message(span_danger("A strange crearure bursts out of [M]!"), span_userdanger("A slimy creature bursts forth from your flesh!")) - addtimer(CALLBACK(phage, TYPE_PROC_REF(/mob/living/simple_mob/vore/aggressive/macrophage, deathcheck)), 3000) + phage.infections += D + M.visible_message(span_danger("A strange creature burst out of [M]!"), span_userdanger("A slimy creature bursts forth from your flesh!")) + addtimer(CALLBACK(phage, TYPE_PROC_REF(/mob/living/simple_mob/vore/aggressive/macrophage, deathcheck)), 3 MINUTES) diff --git a/code/datums/diseases/advance/symptoms/mlem.dm b/code/datums/diseases/advance/symptoms/mlem.dm index e48329be9cc..ebd3c79eb4a 100644 --- a/code/datums/diseases/advance/symptoms/mlem.dm +++ b/code/datums/diseases/advance/symptoms/mlem.dm @@ -17,16 +17,49 @@ BONUS /datum/symptom/mlem name = "Mlemington" + desc = "The host uncontrollably licks their nose. Mlem." stealth = 0 resistance = 3 stage_speed = 3 - transmittable = 1 + transmission = 1 level = 1 severity = 1 + var/infective = FALSE + + threshold_descs = list( + "Resistance 5" = "The host may occasionally go on a mlemming spree.", + "Transmission 8" = "The host will spread the virus through saliva when mlemming." + ) + +/datum/symptom/mlem/severityset(datum/disease/advance/A) + . = ..() + if(A.transmission >= 8) + infective = TRUE + severity += 1 + +/datum/symptom/mlem/Start(datum/disease/advance/A) + if(!..()) + return + if(A.resistance >= 5) + power = 1.5 + /datum/symptom/itching/Activate(var/datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - M.emote("mlem") - return + if(!..()) + return + var/mob/living/M = A.affected_mob + if(M.stat == DEAD) + return + switch(A.stage) + if(1, 2, 3) + if(prob(base_message_chance)) + to_chat(M, span_notice("You think about licking your nose...")) + else + M.emote("mlem") + if(power >= 1.5) + M.emote("mlem") + if(power >= 2) + M.emote("mlem") + addtimer(CALLBACK(M, TYPE_PROC_REF(/mob, emote), "mlem"), 2 SECONDS) + addtimer(CALLBACK(M, TYPE_PROC_REF(/mob, emote), "mlem"), 5 SECONDS) + addtimer(CALLBACK(M, TYPE_PROC_REF(/mob, emote), "mlem"), 8 SECONDS) diff --git a/code/datums/diseases/advance/symptoms/necrotic_agent.dm b/code/datums/diseases/advance/symptoms/necrotic_agent.dm index 24a3e9ff065..60dd226ed10 100644 --- a/code/datums/diseases/advance/symptoms/necrotic_agent.dm +++ b/code/datums/diseases/advance/symptoms/necrotic_agent.dm @@ -17,15 +17,16 @@ Bonus /datum/symptom/necrotic_agent name = "Necrotic Agent" - stealth = -2 - resistance = -3 - stage_speed = -3 - transmittable = 0 + desc = "Allows the virus to infect corpses, and work on the dead." + stealth = 2 + resistance = -2 + stage_speed = -1 + transmission = 0 level = 6 severity = 3 -/datum/symptom/necrotic_agent/Start(datum/disease/advance/A) - A.allow_dead = TRUE +/datum/symptom/necrotic_agent/OnAdd(datum/disease/advance/A) + A.virus_modifiers |= SPREAD_DEAD -/datum/symptom/necrotic_agent/End(datum/disease/advance/A) - A.allow_dead = FALSE +/datum/symptom/necrotic_agent/OnRemove(datum/disease/advance/A) + A.virus_modifiers &= ~SPREAD_DEAD diff --git a/code/datums/diseases/advance/symptoms/oxygen.dm b/code/datums/diseases/advance/symptoms/oxygen.dm deleted file mode 100644 index a0e04987003..00000000000 --- a/code/datums/diseases/advance/symptoms/oxygen.dm +++ /dev/null @@ -1,38 +0,0 @@ -/* -////////////////////////////////////// - -Self-Respiration - - Slightly hidden. - Lowers resistance significantly. - Decreases stage speed significantly. - Decreases transmittablity tremendously. - Fatal Level. - -Bonus - The body generates dexalin. - -////////////////////////////////////// -*/ - -/datum/symptom/oxygen - name = "Self-Respiration" - stealth = 1 - resistance = -3 - stage_speed = -3 - transmittable = -4 - level = 6 - severity = 0 - -/datum/symptom/oxygen/Activate(var/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(REAGENT_ID_DEXALIN) < 10) - M.reagents.add_reagent(REAGENT_ID_DEXALIN, 10) - else - if(prob(SYMPTOM_ACTIVATION_PROB * 5)) - to_chat(M, span_notice(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/photosensitivity.dm b/code/datums/diseases/advance/symptoms/photosensitivity.dm index 5908bfb259c..77f08f1a360 100644 --- a/code/datums/diseases/advance/symptoms/photosensitivity.dm +++ b/code/datums/diseases/advance/symptoms/photosensitivity.dm @@ -20,7 +20,7 @@ Bonus stealth = -2 resistance = 2 stage_speed = 1 - transmittable = -4 + transmission = -4 level = 6 severity = 5 diff --git a/code/datums/diseases/advance/symptoms/pica.dm b/code/datums/diseases/advance/symptoms/pica.dm deleted file mode 100644 index 6f8221a74b4..00000000000 --- a/code/datums/diseases/advance/symptoms/pica.dm +++ /dev/null @@ -1,46 +0,0 @@ -/* -////////////////////////////////////// - -Pica - - Not noticable or unnoticable. - Decreases resistance. - Increases stage speed. - Little transmittable. - Low Level. - -BONUS - The host gains hunger for any kind of object. - -////////////////////////////////////// -*/ - -/datum/symptom/pica - name = "Pica" - stealth = 0 - resistance = -2 - stage_speed = 3 - transmittable = 1 - level = 1 - severity = 0 - -/datum/symptom/pica/Start(datum/disease/advance/A) - add_verb(A.affected_mob, /mob/living/proc/eat_trash) - add_verb(A.affected_mob, /mob/living/proc/toggle_trash_catching) - -/datum/symptom/pica/End(datum/disease/advance/A) - remove_verb(A.affected_mob, /mob/living/proc/eat_trash) - remove_verb(A.affected_mob, /mob/living/proc/toggle_trash_catching) - -/datum/symptom/pica/Activate(datum/disease/advance/A) - ..() - - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/list/nearby = oview(5, A.affected_mob) - to_chat(A.affected_mob, span_warning("You could go fo a bite of [pick(nearby)]...")) - else if (prob(SYMPTOM_ACTIVATION_PROB)) - if(ishuman(A.affected_mob)) - var/mob/living/carbon/human/H = A.affected_mob - var/list/item = H.get_equipped_items() - to_chat(H, span_warning("[pick(item)] looks oddly [pick("delicious", "tasty", "scrumptious", "inviting")]...")) - return diff --git a/code/datums/diseases/advance/symptoms/radiation.dm b/code/datums/diseases/advance/symptoms/radiation.dm new file mode 100644 index 00000000000..0c926c1749d --- /dev/null +++ b/code/datums/diseases/advance/symptoms/radiation.dm @@ -0,0 +1,108 @@ +/datum/symptom/radiation + name = "Irradiant Cells" + desc = "Causes the cells in the host's body to give off harmful radiation." + stealth = -1 + resistance = 2 + stage_speed = 1 + transmission = 2 + level = 7 + severity = 3 + symptom_delay_min = 10 SECONDS + symptom_delay_max = 40 SECONDS + threshold_descs = list( + "Speed 8" = "Host takes radiation damage faster." + ) + +/datum/symptom/radiation/severityset(datum/disease/advance/A) + . = ..() + if(A.stage_rate >= 8) + severity += 1 + +/datum/symptom/radiation/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stage_rate >= 8) + power = 2 + +/datum/symptom/radiation/Activate(datum/disease/advance/A) + if(!..()) + return + var/mob/living/carbon/human/H = A.affected_mob + if(H.stat == DEAD) + return + switch(A.stage) + if(1) + if(prob(10)) + to_chat(H, span_notice("You feel off...")) + if(2, 3) + if(prob(50)) + to_chat(H, span_danger("You feel like the atoms inside you are beginning to split...")) + if(4, 5) + radiate(H) + +/datum/symptom/radiation/proc/radiate(mob/living/carbon/human/H) + to_chat(H, span_danger("You feel a wave of pain throughout your body!")) + H.radiation += 50 * power + return TRUE + +/datum/symptom/radconversion + name = "Aptopic Culling" + desc = "The virus causes infected cells to die off when exposed to radiation, causing open wounds to appear on the host's flesh. The end result of this process is the removal of radioactive contamination from the host." + stealth = 1 + resistance = 1 + stage_speed = 1 + transmission = -2 + level = 8 + severity = 0 + symptom_delay_min = 1 + symptom_delay_max = 1 + + var/toxheal = FALSE + var/cellheal = FALSE + + threshold_descs = list( + "Stage Speed 6" = "The disease also kills off contaminated cells, converting Toxin damage to Brute damage, at an efficient rate.", + "Resistance 12" = "The disease also kills off genetically damaged cells, coverting Genetic damage to Burn damage, an inefficient rate." + ) + +/datum/symptom/radconversion/severityset(datum/disease/advance/A) + . = ..() + if(A.stage_rate >= 6) + severity -= 1 + if(A.resistance >= 12) + severity -= 1 + +/datum/symptom/radconversion/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stage_rate >= 6) + toxheal = TRUE + if(A.resistance >= 12) + cellheal = TRUE + +/datum/symptom/radconversion/Activate(datum/disease/advance/A) + if(!..()) + return + var/mob/living/carbon/human/H = A.affected_mob + if(A.stage >= 4) + if(H.radiation) + H.radiation -= max(H.radiation * 0.05, min(10, H.radiation)) + H.take_overall_damage(2) + if(prob(5)) + if(H.stat != DEAD) + to_chat(H, span_userdanger("A tear opens in your flesh!")) + blood_splatter(H.loc, H) + if(H.getToxLoss() && toxheal) + H.adjustToxLoss(-2) + H.take_overall_damage(1) + if(prob(5)) + if(H.stat != DEAD) + to_chat(H, span_userdanger("A tear opens in your flesh!")) + blood_splatter(H.loc, H) + if(H.getCloneLoss() && cellheal) + H.adjustCloneLoss(-1) + H.take_overall_damage(burn = 2) + if(prob(5) && H.stat != DEAD) + to_chat(H, span_userdanger("A nasty rash appears on your skin!")) + else if (prob(2) && ((H.getCloneLoss() && cellheal) || (H.getToxLoss() && toxheal) || H.radiation) && H.stat != DEAD) + to_chat(H, span_notice("You feel a tingling sensation.")) diff --git a/code/datums/diseases/advance/symptoms/sensory.dm b/code/datums/diseases/advance/symptoms/sensory.dm index 8de86908419..fcce95fd09b 100644 --- a/code/datums/diseases/advance/symptoms/sensory.dm +++ b/code/datums/diseases/advance/symptoms/sensory.dm @@ -20,7 +20,7 @@ Bonus stealth = -3 resistance = -4 stage_speed = -4 - transmittable = -3 + transmission = -3 level = 5 severity = 0 @@ -47,7 +47,7 @@ Bonus stealth = -1 resistance = -3 stage_speed = -2 - transmittable = -4 + transmission = -4 level = 4 /datum/symptom/sensory_restoration/Activate(datum/disease/advance/A) diff --git a/code/datums/diseases/advance/symptoms/shivering.dm b/code/datums/diseases/advance/symptoms/shivering.dm index efe4b39f17e..ec556abef70 100644 --- a/code/datums/diseases/advance/symptoms/shivering.dm +++ b/code/datums/diseases/advance/symptoms/shivering.dm @@ -17,23 +17,53 @@ Bonus /datum/symptom/shivering name = "Shivering" + desc = "The virus inhibits the body's thermoregulation, cooling the body down." stealth = 0 resistance = 2 stage_speed = 2 - transmittable = 2 + transmission = 2 level = 2 - severity = 2 + severity = 0 + symptom_delay_min = 20 SECONDS + symptom_delay_max = 40 SECONDS + + var/unsafe = FALSE + + threshold_descs = list( + "Stage Speed 5" = "Increases cooling speed; The host can fall below safe temperature levels.", + "Stage Speed 10" = "Further increases cooling speed." + ) + +/datum/symptom/shivering/severityset(datum/disease/advance/A) + . = ..() + if(A.stage_rate >= 5) + severity += 1 + if(A.stage_rate >= 10) + severity += 1 + +/datum/symptom/shivering/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stage_rate >= 5) + power = 1.5 + unsafe = TRUE + if(A.stage_rate >= 10) + power = 2.5 /datum/symptom/shivering/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/L = A.affected_mob - to_chat(L, span_warning(pick("You feel cold.", "You start shivering."))) - if(L.bodytemperature > BODYTEMP_COLD_DAMAGE_LIMIT) - Chill(L, A) - return + if(!..()) + return + var/mob/living/carbon/M = A.affected_mob + if(M.stat == DEAD) + return + if(!unsafe || A.stage < 4) + to_chat(M, span_warning(pick("You feel cold.", "You shiver."))) + else + to_chat(M, span_userdanger(pick("You fel your blood run cold.", "You feel ice in your veins.", "You feel like you can't heat up.", "You shiver violently."))) + set_body_temp(M, A) -/datum/symptom/shivering/proc/Chill(mob/living/M, datum/disease/advance/A) - var/get_cold = (sqrtor0(16+A.totalStealth()*2))+(sqrtor0(21+A.totalResistance()*2)) - M.bodytemperature = max(M.bodytemperature - (get_cold * A.stage), BODYTEMP_COLD_DAMAGE_LIMIT + 1) - return 1 +/datum/symptom/shivering/proc/set_body_temp(var/mob/living/carbon/H, datum/disease/advance/A) + if(!unsafe) + H.bodytemperature = max(-((3 * power) * A.stage), (BODYTEMP_COLD_DAMAGE_LIMIT +1)) + else + H.bodytemperature = max(-((3 * power) * A.stage), (BODYTEMP_COLD_DAMAGE_LIMIT - 20)) diff --git a/code/datums/diseases/advance/symptoms/size.dm b/code/datums/diseases/advance/symptoms/size.dm index a0a71fe1b71..b01336d9997 100644 --- a/code/datums/diseases/advance/symptoms/size.dm +++ b/code/datums/diseases/advance/symptoms/size.dm @@ -17,82 +17,33 @@ BONUS /datum/symptom/size name = "Mass Revectoring" - stealth = -4 - resistance = 1 + desc = "The virus causes distortion of the host's body, causing it to change size." + stealth = 1 + resistance = 0 stage_speed = 2 - transmittable = -2 + transmission = 2 level = 4 - severity = 1 + severity = 0 + symptom_delay_min = 20 SECONDS + symptom_delay_max = 60 SECONDS + var/min_size = RESIZE_MINIMUM + var/max_size = RESIZE_MAXIMUM /datum/symptom/size/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - - switch(A.stage) - if(4, 5) - M.emote("twitch") - Resize(M, rand(25, 200)) - -/datum/symptom/size/proc/Resize(mob/living/M, var/size) - M.resize(size+M.size_multiplier/100) - -/* -////////////////////////////////////// - -Enlargement Disorder - - Very noticeable. - Increases resistance slightly. - Increases stage speed. - Decreases transmittablity - Intense level. - -BONUS - Grows the host to bigger sizes - -////////////////////////////////////// -*/ + if(!..()) + return + var/mob/living/M = A.affected_mob + switch(A.stage) + if(4, 5) + M.emote("twitch") + M.resize(pick(min_size, max_size)) /datum/symptom/size/grow name = "Enlargement Disorder" - -/datum/symptom/size/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - - switch(A.stage) - if(4, 5) - M.emote("twitch") - Resize(M, rand(100, 200)) - -/* -////////////////////////////////////// - -Dwindling Malady - - Very noticeable. - Increases resistance slightly. - Increases stage speed. - Decreases transmittablity - Intense level. - -BONUS - Shrinks the host to small sizes - -////////////////////////////////////// -*/ + min_size = RESIZE_NORMAL + max_size = RESIZE_MAXIMUM /datum/symptom/size/shrink name = "Dwindling Malady" - -/datum/symptom/size/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - - switch(A.stage) - if(4, 5) - M.emote("twitch") - Resize(M, rand(25, 100)) + min_size = RESIZE_MINIMUM + max_size = RESIZE_NORMAL diff --git a/code/datums/diseases/advance/symptoms/sneeze.dm b/code/datums/diseases/advance/symptoms/sneeze.dm index e1f226049f9..5c9d681206c 100644 --- a/code/datums/diseases/advance/symptoms/sneeze.dm +++ b/code/datums/diseases/advance/symptoms/sneeze.dm @@ -18,30 +18,43 @@ Bonus /datum/symptom/sneeze name = "Sneezing" + desc = "The virus causes irritation of the nasal cavity, making the host sneeze occasionally." stealth = -2 resistance = 3 stage_speed = 0 - transmittable = 4 + transmission = 4 + symptom_delay_min = 15 SECONDS + symptom_delay_max = 40 SECONDS level = 1 - severity = 1 + severity = 0 + + var/infective = FALSE + + threshold_descs = list( + "Stealth 4" = "The symptom remains hidden until active.", + "Trasmission 12" = "The host may spread the disease through sneezing." + ) + +/datum/symptom/sneeze/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stealth >= 4) + supress_warning = TRUE + if(A.transmission >= 12) + infective = TRUE /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) + if(!..()) + return + var/mob/living/M = A.affected_mob + switch(A.stage) + if(1, 2, 3) + if(!supress_warning) M.emote("sniff") - else - M.emote("sneeze") - if(!M.wear_mask) // Spread only if they're not covering their face - A.spread(5) - - if(prob(30) && !M.wear_mask) // Same for mucus - var/obj/effect/decal/cleanable/mucus/icky = new(get_turf(M)) - icky.viruses |= A.Copy() - - return + else + M.emote("sneeze") + if(infective) + addtimer(CALLBACK(A, TYPE_PROC_REF(/datum/disease, spread), 4), 20) /* ////////////////////////////////////// @@ -61,33 +74,30 @@ Bonus ////////////////////////////////////// */ -/datum/symptom/sneeze/bluespace +/datum/symptom/bsneeze name = "Bluespace Sneezing" + desc = "The virus condenses bluespace particles in the host's nasal cavity, teleporting the host on sneeze." stealth = -2 resistance = 3 stage_speed = 0 - transmittable = 1 + transmission = 1 + symptom_delay_min = 20 SECONDS + symptom_delay_max = 30 SECONDS level = 4 severity = 3 -/datum/symptom/sneeze/bluespace/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/M = A.affected_mob - switch(A.stage) - if(1, 2, 3) +/datum/symptom/bsneeze/Activate(datum/disease/advance/A) + if(!..()) + return + var/mob/living/M = A.affected_mob + switch(A.stage) + if(1, 2, 3) + if(!supress_warning) M.emote("sniff") - else - SneezeTeleport(A, M) - if(!M.wear_mask) - A.spread(A.stage) - if(prob(30) && !M.wear_mask) - var/obj/effect/decal/cleanable/mucus/icky = new(get_turf(M)) - icky.viruses |= A.Copy() + else + SneezeTeleport(A, A.affected_mob) - return - -/datum/symptom/sneeze/bluespace/proc/SneezeTeleport(datum/disease/advance/A, var/mob/living/mob) +/datum/symptom/bsneeze/proc/SneezeTeleport(datum/disease/advance/A, var/mob/living/mob) var/list/destination = list() var/place diff --git a/code/datums/diseases/advance/symptoms/spin.dm b/code/datums/diseases/advance/symptoms/spin.dm index 0dc0d7d6bf8..95ef7569b68 100644 --- a/code/datums/diseases/advance/symptoms/spin.dm +++ b/code/datums/diseases/advance/symptoms/spin.dm @@ -20,17 +20,33 @@ BONUS stealth = 2 resistance = 0 stage_speed = 3 - transmittable = 1 + transmission = 1 level = 1 + symptom_delay_min = 15 SECONDS + symptom_delay_max = 35 SECONDS severity = 0 + threshold_descs = list( + "Resistance 6" = "The target will flip more violently." + ) -/datum/symptom/spyndrome/Activate(var/datum/disease/advance/A) - ..() + var/bigspin = FALSE - if(prob(SYMPTOM_ACTIVATION_PROB)) - if(A.affected_mob.buckled()) - to_chat(viewers(A.affected_mob), span_warning("[A.affected_mob.name] struggles violently against their restraints!")) +/datum/symptom/spyndrome/Start(datum/disease/advance/A) + if(!..()) + return + if(A.resistance >= 6) + bigspin = TRUE + +/datum/symptom/spyndrome/Activate(datum/disease/advance/A) + if(!..()) + return + var/mob/living/M = A.affected_mob + switch(A.stage) + if(1, 2, 3) + if(prob(base_message_chance)) + to_chat(M, span_notice("You can't stand still.")) else - to_chat(viewers(A.affected_mob), span_warning("[A.affected_mob.name] spins around violently!")) - A.affected_mob.emote("spin") - return + if(bigspin) + M.emote("floorspin") + else + M.emote("spin") diff --git a/code/datums/diseases/advance/symptoms/stimulant.dm b/code/datums/diseases/advance/symptoms/stimulant.dm index 4861a442409..9378221e191 100644 --- a/code/datums/diseases/advance/symptoms/stimulant.dm +++ b/code/datums/diseases/advance/symptoms/stimulant.dm @@ -16,28 +16,69 @@ Bonus */ /datum/symptom/stimulant - name = "Overactive Adrenal Gland" - stealth = 0 - resistance = -1 + name = "Hyperactivity" + desc = "The virus causes restlessness, nervousness and hyperactivity, increasing the rate at which the host needs to eat, but making them harder to tire out" + stealth = -4 + resistance = 0 stage_speed = 2 - transmittable = -3 - level = 3 + transmission = -3 + level = 8 severity = 1 + symptom_delay_min = 1 + symptom_delay_max = 1 + + var/clearacc = FALSE + + threshold_descs = list( + "Resistance 8" = "This virus causes an even greater rate of nutriment loss, able to cause starvation, but it's energy gain greatly increases.", + "Stage 8" = "The virus causes extreme nervousness and paranoia, resulting in occasional hallucinations, and extreme restlessness, but great overall energy." + ) + +/datum/symptom/stimulant/severityset(datum/disease/advance/A) + . = ..() + if(A.resistance >= 8) + severity -= 1 + if(A.stage_rate >= 8) + severity -= 1 + +/datum/symptom/stimulant/Start(datum/disease/advance/A) + if(!..()) + return + power = initial(power) + if(A.resistance >= 8) + power += 2 + if(A.stage_rate >= 8) + power += 1 + clearacc = TRUE /datum/symptom/stimulant/Activate(datum/disease/advance/A) - ..() - - if(prob(SYMPTOM_ACTIVATION_PROB)) - var/mob/living/L = A.affected_mob - to_chat(L, span_notice("You feel a rush of energy inside you!")) - switch(A.stage) - if(1, 2) - L.jitteriness += 5 - if(3, 4) - L.jitteriness += 10 - else - if(L.reagents.get_reagent_amount(REAGENT_ID_HYPERZINE) < 10) - L.reagents.add_reagent(REAGENT_ID_HYPERZINE, 5) - if(prob(30)) - L.jitteriness += 15 - return + if(!..()) + return + var/mob/living/carbon/human/H = A.affected_mob + if(H.stat == DEAD) + return + switch(A.stage) + if(2 to 3) + if(prob(power) && H.stat) + H.jitteriness += (2 * power) + H.emote("twitch") + to_chat(H, span_notice("[pick("you feel energetic!", "You feel well-rested.", "You feel great!")]")) + if(4 to 5) + H.drowsyness = max(0, H.drowsyness - 10 * power) + H.AdjustSleeping(-10 * power) + H.AdjustStunned(-10 * power) + if(prob(power) && prob(50)) + if(H.stat) + H.emote("twitch") + H.make_jittery(2 * power) + H.reagents.add_reagent(REAGENT_ID_HYPERZINE, 5) + to_chat(H, span_notice("[pick("You feel nevervous...", "You feel anxious.", "You feel like everything is moving in slow motion.")]")) + if(H.nutrition > 150 - (30 * power)) + H.nutrition = max(150 - (30 * power), H.nutrition - (2 * power)) + if(prob(25)) + H.make_jittery(2 * power) + if(clearacc) + if(prob(power) && prob(50)) + if(H.stat) + H.emote("scream") + H.hallucination = min(20, H.hallucination + (5 * power)) diff --git a/code/datums/diseases/advance/symptoms/symptoms.dm b/code/datums/diseases/advance/symptoms/symptoms.dm index df5954f97b7..c7322cbb31b 100644 --- a/code/datums/diseases/advance/symptoms/symptoms.dm +++ b/code/datums/diseases/advance/symptoms/symptoms.dm @@ -5,16 +5,29 @@ GLOBAL_LIST_INIT(list_symptoms, subtypesof(/datum/symptom)) /datum/symptom // Buffs/Debuffs the symptom has to the overall engineered disease. var/name = "" + var/desc = "ERR://355. PanDEMIC was not able to initialize description!" // Someone forgot the description + var/threshold_descs = list() var/stealth = 0 var/resistance = 0 var/stage_speed = 0 - var/transmittable = 0 + var/transmission = 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. 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!!! var/id = "" + var/supress_warning = FALSE + var/next_activaction = 0 + var/symptom_delay_min = 1 SECONDS + var/symptom_delay_max = 1 SECONDS + var/naturally_occuring = TRUE // If this symptom can roll from random diseases + + var/base_message_chance = 10 + var/power = 1 + // If the symptom is neutered or not. If it is, it will only affect stats + var/neutered = FALSE + var/stopped = FALSE // Used for Viral Suspended Animaton, stops a symptom but doesn't neuter it. /datum/symptom/New() var/list/S = GLOB.list_symptoms @@ -24,18 +37,71 @@ GLOBAL_LIST_INIT(list_symptoms, subtypesof(/datum/symptom)) return CRASH("We couldn't assign an ID!") +/datum/symptom/proc/Copy() + var/datum/symptom/new_symp = new type + new_symp.name = name + new_symp.id = id + new_symp.neutered = neutered + return new_symp + // Called when processing of the advance disease, which holds this symptom, starts. /datum/symptom/proc/Start(datum/disease/advance/A) - return + if(neutered) + return FALSE + next_activaction = world.time + rand(symptom_delay_min, symptom_delay_max) + return TRUE + +/datum/symptom/proc/severityset(datum/disease/advance/A) + severity = initial(severity) // Called when the advance disease is going to be deleted or when the advance disease stops processing. /datum/symptom/proc/End(datum/disease/advance/A) - return + if(neutered) + return FALSE + return TRUE // Called when the disease activates. It's what makes your diseases work! /datum/symptom/proc/Activate(datum/disease/advance/A) - return + if(!A) + return FALSE + if(isbelly(A.affected_mob.loc)) // So you can eat people to "isolate" them. Or get eaten by a macrophage. + return FALSE + if(neutered || stopped) + return FALSE + if(world.time < next_activaction) + return FALSE + else + next_activaction = world.time + rand(symptom_delay_min, symptom_delay_max) + return TRUE // Called when the host dies /datum/symptom/proc/OnDeath(datum/disease/advance/A) + return !neutered + +// Called when the stage changes +/datum/symptom/proc/OnStageChange(datum/disease/advance/A) + if(neutered || stopped) + return FALSE + return TRUE + +/datum/symptom/proc/OnAdd(datum/disease/advance/A) return + +/datum/symptom/proc/OnRemove(datum/disease/advance/A) + return + +/datum/symptom/proc/generate_threshold_desc() + return + +/datum/symptom/proc/get_symptom_data() + var/list/data = list() + data["name"] = name + data["desc"] = desc + data["stealth"] = stealth + data["resistance"] = resistance + data["stage_speed"] = stage_speed + data["transmission"] = transmission + data["neutered"] = neutered + data["level"] = level + data["threshold_desc"] = threshold_descs + return data diff --git a/code/datums/diseases/advance/symptoms/synthetic_infection.dm b/code/datums/diseases/advance/symptoms/synthetic_infection.dm index 7bd565ec9e7..987eaf5d7df 100644 --- a/code/datums/diseases/advance/symptoms/synthetic_infection.dm +++ b/code/datums/diseases/advance/symptoms/synthetic_infection.dm @@ -20,7 +20,7 @@ Bonus stealth = 1 resistance = 2 stage_speed = 0 - transmittable = 1 + transmission = 1 level = 5 severity = 3 diff --git a/code/datums/diseases/advance/symptoms/telepathy.dm b/code/datums/diseases/advance/symptoms/telepathy.dm deleted file mode 100644 index c87e3178be7..00000000000 --- a/code/datums/diseases/advance/symptoms/telepathy.dm +++ /dev/null @@ -1,45 +0,0 @@ -/* -////////////////////////////////////// - -Telepathy - - Hidden. - Decreases resistance. - Decreases stage speed significantly. - Decreases transmittablity tremendously. - Critical Level. - -Bonus - The user gains telepathy. - -////////////////////////////////////// -*/ - -/datum/symptom/telepathy - name = "Pineal Gland Decalcification" - stealth = 2 - resistance = -2 - stage_speed = -3 - transmittable = -4 - level = 5 - severity = 0 - -/datum/symptom/telepathy/Start(datum/disease/advance/A) - if(iscarbon(A)) - var/mob/living/carbon/human/H = A.affected_mob - // Traitgenes Locate the gene from trait - var/datum/gene/trait/G = get_gene_from_trait(/datum/trait/positive/superpower_remotetalk) - H.dna.SetSEState(G.block, 1) - domutcheck(H, null, TRUE) - H.UpdateAppearance() - to_chat(H, span_notice("Your mind expands...")) - -/datum/symptom/telepathy/End(datum/disease/advance/A) - if(iscarbon(A)) - var/mob/living/carbon/human/H = A.affected_mob - // Traitgenes Locate the gene from trait - var/datum/gene/trait/G = get_gene_from_trait(/datum/trait/positive/superpower_remotetalk) - H.dna.SetSEState(G.block, 0) - domutcheck(H, null, TRUE) - H.UpdateAppearance() - to_chat(H, span_notice("Everything feels... Normal.")) diff --git a/code/datums/diseases/advance/symptoms/viral.dm b/code/datums/diseases/advance/symptoms/viral.dm index 3c901f371de..43b00aec538 100644 --- a/code/datums/diseases/advance/symptoms/viral.dm +++ b/code/datums/diseases/advance/symptoms/viral.dm @@ -14,29 +14,20 @@ BONUS ////////////////////////////////////// */ /datum/symptom/viraladaptation - name = "Viral self-adaptation" + name = "Viral Self-Adaptation" + desc = "The virus mimics the function of normal body cells, becoming harder to spot and to eradicate, but reducing it's speed." stealth = 3 resistance = 5 stage_speed = -3 - transmittable = 0 - level = 3 + transmission = 0 + level = 4 severity = 0 -/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, span_notice("You feel off, but no different from before.")) - if(5) - to_chat(M, span_notice("You feel better, but nothing interesting happens.")) - /* ////////////////////////////////////// Viral evolution - Moderate stealth reductopn. + Moderate stealth reduction. Major decreases to resistance. increases stage speed. increase to transmission @@ -48,20 +39,134 @@ BONUS ////////////////////////////////////// */ /datum/symptom/viralevolution - name = "Viral evolutionary acceleration" + name = "Viral Evolutionary Acceleration" + desc = "The virus quickly adapts to spread as fast as possible both outside and inside a host. This, however, makes the virus easier to spot, and les able to fight off a cure." stealth = -2 resistance = -3 stage_speed = 5 - transmittable = 3 - level = 3 - severity = 0 + transmission = 3 + level = 4 -/datum/symptom/viralevolution/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) +/datum/symptom/viralpower + name = "Viral Power Multiplier" + desc = "The vrus has more powerful symptoms. May have unpredictable effects." + stealth = 2 + resistance = 2 + stage_speed = 2 + transmission = 2 + level = -1 + var/maxpower + var/powerbudget + var/scramble = FALSE + var/used = FALSE + + threshold_descs = list( + "Transmission 8" = "Constantly scrambles the power of all symptoms.", + "Stage Speed 8" = "Doubles the power boost." + ) + +/datum/symptom/viralpower/Start(datum/disease/advance/A) + if(!..()) + return + if(A.speed >= 8) + power = 2 + if(A.transmission >= 8) + scramble = TRUE + +/datum/symptom/viralpower/Activate(datum/disease/advance/A) + if(!..()) + return + if(!used) + for(var/datum/symptom/S as() in A.symptoms) + if(S == src) + return + S.power += power + maxpower += S.power + if(scramble) + powerbudget += power + maxpower += power + power = 0 + used = TRUE + if(scramble) + var/datum/symptom/S = pick(A.symptoms) + if(S == src) + return + if(powerbudget && (prob(50) || powerbudget == maxpower)) + S.power += 1 + powerbudget -= 1 + else if(S.power >= 2) + S.power -= 1 + powerbudget += 1 + +/datum/symptom/viralreverse + name = "Viral Aggressive Metabolism" + desc = "The virus sacrifices its long term survivability to nearly instantly fully spread inside a host. \ + The virus will start at the last stage, but will eventually decay and die off by itself." + stealth = 1 + resistance = 1 + stage_speed = 3 + transmission = -4 + level = 4 + symptom_delay_min = 1 + symptom_delay_max = 1 + threshold_descs = list( + "Stage Speed" = "Highest between these determines the amount before self-curing.", + "Stealth 4" = "Doubles the time before the virus self-cures." + ) + + var/time_to_cure + +/datum/symptom/viralreverse/Activate(datum/disease/advance/A) + if(!..()) + return + if(time_to_cure > 0) + time_to_cure-- + else var/mob/living/M = A.affected_mob - switch(A.stage) - if(1) - to_chat(M, span_notice("You feel better, but no different from before.")) - if(5) - to_chat(M,span_notice("You feel off, but nothing interesting happens.")) + Heal(M, A) + +/datum/symptom/viralreverse/proc/Heal(mob/living/M, datum/disease/advance/A) + A.stage -= 1 + if(A.stage < 2) + to_chat(M, span_notice("You suddenly feel healthy.")) + A.cure(FALSE) + +/datum/symptom/viralreverse/Start(datum/disease/advance/A) + if(!..()) + return + A.stage = 5 + if(A.stealth >= 4) + power = 2 + time_to_cure = max(A.resistance, A.stage_rate) * 10 * power + +/datum/symptom/viralincubate + name = "Viral Suspended Animation" + desc = "The virus has very little effect until it reaches its final stage" + stealth = 4 + resistance = -2 + stage_speed = -2 + transmission = 1 + level = 4 + symptom_delay_min = 1 + symptom_delay_max = 1 + var/list/captives = list() + var/used = FALSE + +/datum/symptom/viralincubate/Activate(datum/disease/advance/A) + if(!..()) + return + if(A.stage >= 5) + for(var/datum/symptom/S as() in captives) + S.stopped = FALSE + captives -= S + if(!LAZYLEN(captives)) + stopped = TRUE + else if(!used) + for(var/datum/symptom/S as() in A.symptoms) + if(S.neutered) + continue + if(S == src) + continue + S.stopped = TRUE + captives += S + used = TRUE diff --git a/code/datums/diseases/advance/symptoms/vision.dm b/code/datums/diseases/advance/symptoms/vision.dm index 2710124db43..62da7032a88 100644 --- a/code/datums/diseases/advance/symptoms/vision.dm +++ b/code/datums/diseases/advance/symptoms/vision.dm @@ -17,35 +17,69 @@ Bonus /datum/symptom/visionloss name = "Hyphema" + desc = "The virus causes inflammation of the retina, leading to eye damage and eventually blindness." stealth = -1 - resistance = -4 + resistance = -3 stage_speed = -4 - transmittable = -3 - level = 5 - severity = 4 + transmission = -2 + level = 3 + severity = 2 + base_message_chance = 50 + symptom_delay_min = 30 SECONDS + symptom_delay_max = 80 SECONDS + + var/remove_eyes = FALSE + + threshold_descs = list( + "Resistance 12" = "Weakens extraocular muscles, eventually leading to complete detachment of the eyes.", + "Stealth 4" = "The symptom remains hidden until active." + ) + +/datum/symptom/visionloss/severityset(datum/disease/advance/A) + . = ..() + if(A.resistance >= 12) + severity += 1 + +/datum/symptom/visionloss/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stealth >= 4) + supress_warning = TRUE + if(A.resistance >= 12) + remove_eyes = TRUE /datum/symptom/visionloss/Activate(datum/disease/advance/A) - ..() - if(prob(SYMPTOM_ACTIVATION_PROB)) - if(iscarbon(A)) - var/mob/living/carbon/M = A.affected_mob - var/obj/item/organ/internal/eyes/eyes = M.internal_organs_by_name[O_EYES] - if(!eyes) - return - switch(A.stage) - if(1, 2) + if(!..()) + return + if(iscarbon(A.affected_mob)) + var/mob/living/carbon/M = A.affected_mob + var/obj/item/organ/internal/eyes/eyes = M.internal_organs_by_name[O_EYES] + if(!eyes) + return + switch(A.stage) + if(1, 2) + if(prob(base_message_chance) && !supress_warning && M.stat != DEAD) to_chat(M, span_warning("Your eyes itch.")) - if(3, 4) + if(3, 4) + if(M.stat != DEAD) to_chat(M, span_boldwarning("Your eyes burn!")) - M.eye_blurry = 20 - eyes.take_damage(1) - else - to_chat(M, span_userdanger("Your eyes burn horrificly!")) - M.eye_blurry = 40 - eyes.take_damage(5) - if(eyes.damage >= 10) - M.disabilities |= NEARSIGHTED - if(prob(eyes.damage - 10 + 1)) - if(!M.eye_blind) + M.eye_blurry = 20 + eyes.take_damage(1) + else + M.eye_blurry += 20 + eyes.take_damage(5) + if(eyes.damage >= 10) + M.disabilities |= NEARSIGHTED + if(prob(eyes.damage - 10 + 1)) + if(!remove_eyes) + if(!M.is_blind()) + if(M.stat != DEAD) to_chat(M, span_userdanger("You go blind!")) - M.Blind(20) + eyes.take_damage(eyes.max_damage) + else + M.visible_message(span_warning("[M]'s eyes fall out of their sockets!"), span_userdanger("Your eyes out of their sockets!")) + eyes.forceMove(get_turf(M)) + + else + if(M.stat != DEAD) + to_chat(M, span_userdanger("Your eyes burn horrifically!")) diff --git a/code/datums/diseases/advance/symptoms/vomit.dm b/code/datums/diseases/advance/symptoms/vomit.dm index 06c2b0fafbf..99dc0332bd3 100644 --- a/code/datums/diseases/advance/symptoms/vomit.dm +++ b/code/datums/diseases/advance/symptoms/vomit.dm @@ -17,18 +17,48 @@ Bonus /datum/symptom/vomit name = "Vomiting" + desc = "The virus causes nausea and irritates the stomach, causing occasional vomit." stealth = -2 resistance = 0 stage_speed = 1 - transmittable = 2 + transmission = 2 level = 3 severity = 1 + base_message_chance = 100 + symptom_delay_min = 20 SECONDS + symptom_delay_max = 60 SECONDS + + var/vomit_blood = FALSE + var/proj_vomit = 1 + + threshold_descs = list( + "Stage Speed 5" = "Host will vomit blood.", + "Transmission 6" = "Host will projectile vomit, increasing vomit range.", + "Stealth 4" = "The symptom remans hidden until active." + ) + +/datum/symptom/vomit/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stealth >= 4) + supress_warning = FALSE + if(A.stage_rate >= 5) + vomit_blood = TRUE + if(A.transmission >= 6) + proj_vomit = 5 /datum/symptom/vomit/Activate(datum/disease/advance/A) if(!..()) return var/mob/living/M = A.affected_mob - if(prob(2)) - to_chat(M, span_warning(pick("you feel nauseated.", "You feel like you're going to throw up!"))) - if(prob(SYMPTOM_ACTIVATION_PROB)) - M.vomit() + if(M.stat == DEAD) + return + switch(A.stage) + if(1, 2, 3, 4) + if(prob(base_message_chance) && !supress_warning) + to_chat(M, span_warning(pick("You feel nauseated.", "You feel like you're going to throw up!"))) + else + vomit(M) + +/datum/symptom/vomit/proc/vomit(mob/living/carbon/M) + M.vomit(20, vomit_blood, stun = 2, distance = proj_vomit) diff --git a/code/datums/diseases/advance/symptoms/weakness.dm b/code/datums/diseases/advance/symptoms/weakness.dm index 917be5a46ce..219eff1481f 100644 --- a/code/datums/diseases/advance/symptoms/weakness.dm +++ b/code/datums/diseases/advance/symptoms/weakness.dm @@ -20,7 +20,9 @@ Bonus stealth = -1 resistance = -1 stage_speed = -2 - transmittable = -2 + transmission = -2 + symptom_delay_min = 20 SECONDS + symptom_delay_max = 60 SECONDS level = 3 severity = 3 diff --git a/code/datums/diseases/advance/symptoms/weigh.dm b/code/datums/diseases/advance/symptoms/weigh.dm deleted file mode 100644 index ceca8a77f59..00000000000 --- a/code/datums/diseases/advance/symptoms/weigh.dm +++ /dev/null @@ -1,37 +0,0 @@ -/* -////////////////////////////////////// - -Weight Loss - - Very Very Noticable. - Decreases resistance. - Decreases stage speed. - Reduced Transmittable. - High level. - -Bonus - Decreases the weight of the mob, - forcing it to be skinny. - -////////////////////////////////////// -*/ - -/datum/symptom/weight_loss - name = "Weight Loss" - stealth = -3 - resistance = -2 - stage_speed = -2 - transmittable = -2 - level = 3 - severity = 1 - -/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, span_warning(pick("You feel hungry.", "You crave for food."))) - else - to_chat(M, span_warning(pick("So hungry...", "You'd kill someone for a bite of food...", "Hunger cramps seize you..."))) - M.adjust_nutrition(-20) diff --git a/code/datums/diseases/advance/symptoms/weight.dm b/code/datums/diseases/advance/symptoms/weight.dm new file mode 100644 index 00000000000..88eba6323b9 --- /dev/null +++ b/code/datums/diseases/advance/symptoms/weight.dm @@ -0,0 +1,65 @@ +/* +////////////////////////////////////// + +Weight Loss + + Very Very Noticable. + Decreases resistance. + Decreases stage speed. + Reduced Transmittable. + High level. + +Bonus + Decreases the weight of the mob, + forcing it to be skinny. + +////////////////////////////////////// +*/ + +/datum/symptom/weight_loss + name = "Weight Loss" + desc = "The virus mutates the host's metabolism, making it almost unable to gain nutrition from food." + stealth = 0 + resistance = 2 + stage_speed = -2 + transmission = -1 + level = 3 + severity = 2 + base_message_chance = 100 + symptom_delay_min = 15 SECONDS + symptom_delay_max = 45 SECONDS + + var/starving = TRUE + + threshold_descs = list( + "Stealth 2" = "The symptom is less noticeable, and does not cause starvation." + ) + +/datum/symptom/weight_loss/severityset(datum/disease/advance/A) + . = ..() + if(A.stealth >= 2) + severity -= 3 + +/datum/symptom/weight_loss/Start(datum/disease/advance/A) + if(!..()) + return + if(A.stealth >= 2) + base_message_chance = 25 + starving = FALSE + +/datum/symptom/weight_loss/Activate(datum/disease/advance/A) + if(!..()) + return + var/mob/living/M = A.affected_mob + if(M.stat == DEAD) + return + switch(A.stage) + if(1, 2, 3, 4) + if(prob(base_message_chance)) + to_chat(M, span_warning(pick("You feel hungry.", "You crave for food."))) + else + if(prob(base_message_chance)) + to_chat(M, span_warning(pick("So hungry...", "You'd kill someone for a bite of food...", "Hunger cramps seize you..."))) + M.adjust_nutrition(rand(10, 50)) + if(starving) + M.adjust_nutrition(-100) diff --git a/code/datums/diseases/advance/symptoms/youth.dm b/code/datums/diseases/advance/symptoms/youth.dm new file mode 100644 index 00000000000..6597a0ad375 --- /dev/null +++ b/code/datums/diseases/advance/symptoms/youth.dm @@ -0,0 +1,42 @@ +/datum/symptom/youth + name = "Eternal Youth" + desc = "The virus becomes symbiotically connected to the cells in the host's body, preventing and reversing aging. \ + The virus, in turn, becomes more resistant, spreads faster, and is harder to spot, although it doesn't thrive as well without a host." + stealth = 3 + resistance = 4 + stage_speed = 4 + transmission = -4 + level = 5 + base_message_chance = 100 + symptom_delay_min = 25 SECONDS + symptom_delay_max = 50 SECONDS + +/datum/symptom/youth/Activate(datum/disease/advance/A) + if(!..()) + return + var/mob/living/M = A.affected_mob + if(M.stat == DEAD) + return + 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, span_notice("You haven't had this much energy in years!")) + if(2) + if(H.age > 36) + //H.age = 36 + to_chat(H, span_notice("You're suddenly in a good mood.")) + if(3) + if(H.age > 31) + //H.age = 31 + to_chat(H, span_notice("You begin to feel more lithe.")) + if(4) + if(H.age > 26) + //H.age = 26 + to_chat(H, span_notice("You feel reinvigorated.")) + if(5) + if(H.age > 21) + //H.age = 21 + to_chat(H, span_notice("You feel like you can take on the world!")) diff --git a/code/datums/diseases/anxiety.dm b/code/datums/diseases/anxiety.dm index c3ed12fa5cc..19afe869243 100644 --- a/code/datums/diseases/anxiety.dm +++ b/code/datums/diseases/anxiety.dm @@ -3,17 +3,16 @@ form = "Infection" max_stages = 4 spread_text = "On contact" - spread_flags = CONTACT_GENERAL + spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_FLUIDS | DISEASE_SPREAD_CONTACT cure_text = REAGENT_ETHANOL cures = list(REAGENT_ID_ETHANOL) agent = "Excess Lepdopticides" viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/human/monkey) desc = "If left untreated subject will regurgitate butterflies." - severity = MINOR + danger = DISEASE_MINOR /datum/disease/anxiety/stage_act() - if(!..()) - return FALSE + ..() switch(stage) if(2) if(prob(15)) diff --git a/code/datums/diseases/appendicitis.dm b/code/datums/diseases/appendicitis.dm index a414c138e3c..66e467e9614 100644 --- a/code/datums/diseases/appendicitis.dm +++ b/code/datums/diseases/appendicitis.dm @@ -2,22 +2,20 @@ form = "Condition" name = "Appendicitis" max_stages = 3 - spread_text = "Non-contagius" - spread_flags = NON_CONTAGIOUS + spread_text = "Non-contagious" + disease_flags = CAN_CARRY|CAN_RESIST|CAN_NOT_POPULATE + spread_flags = DISEASE_SPREAD_NON_CONTAGIOUS + virus_modifiers = NEEDS_ALL_CURES | BYPASSES_IMMUNITY 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|CAN_NOT_POPULATE + danger = DISEASE_MINOR visibility_flags = HIDDEN_PANDEMIC required_organs = list(/obj/item/organ/internal/appendix) - bypasses_immunity = TRUE - virus_heal_resistant = TRUE /datum/disease/appendicitis/stage_act() - if(!..()) - return + ..() switch(stage) if(1) if(prob(5)) diff --git a/code/datums/diseases/beesease.dm b/code/datums/diseases/beesease.dm index be6923c7106..716bd3eac1d 100644 --- a/code/datums/diseases/beesease.dm +++ b/code/datums/diseases/beesease.dm @@ -3,17 +3,16 @@ form = "Infection" max_stages = 4 spread_text = "On contact" - spread_flags = CONTACT_GENERAL + spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_FLUIDS | DISEASE_SPREAD_CONTACT cure_text = REAGENT_SUGAR cures = list(REAGENT_ID_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 + danger = DISEASE_MEDIUM /datum/disease/beesease/stage_act() - if(!..()) - return FALSE + ..() switch(stage) if(2) if(prob(2)) diff --git a/code/datums/diseases/brainrot.dm b/code/datums/diseases/brainrot.dm index b7fa0ea033c..452b4cb238c 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 = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_FLUIDS | DISEASE_SPREAD_CONTACT cure_text = REAGENT_ALKYSINE cures = list(REAGENT_ID_ALKYSINE) agent = "Cryptococcus Cosmosis" @@ -10,11 +10,10 @@ 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 + danger = DISEASE_HARMFUL /datum/disease/brainrot/stage_act() - if(!..()) - return FALSE + ..() switch(stage) if(2) if(prob(2)) diff --git a/code/datums/diseases/choreomania.dm b/code/datums/diseases/choreomania.dm index 01802a78016..835b2296800 100644 --- a/code/datums/diseases/choreomania.dm +++ b/code/datums/diseases/choreomania.dm @@ -2,6 +2,7 @@ name = "Choreomania" max_stages = 3 spread_text = "Airborne" + spread_flags = DISEASE_SPREAD_AIRBORNE | DISEASE_SPREAD_CONTACT | DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_FLUIDS cure_text = REAGENT_ADRANOL cures = list(REAGENT_ID_ADRANOL) cure_chance = 10 @@ -9,13 +10,12 @@ viable_mobtypes = list(/mob/living/carbon/human) permeability_mod = 0.75 desc = "If left untreated the subject... Won't stop dancing!" - severity = MINOR + danger = DISEASE_MINOR var/list/dance = list(2,4,8,2,4,8,2,4,8,2,4,8,1,4,1,4,1,4,2,4,8,2) /datum/disease/choreomania/stage_act() - if(!..()) - return FALSE + ..() switch(stage) if(2) if(prob(1)) @@ -31,8 +31,6 @@ for(var/D in dance) affected_mob.dir = D animate(affected_mob, pixel_x = 5, time = 5) - sleep(3) animate(affected_mob, pixel_x = -5, time = 5) - animate(pixel_x = affected_mob.default_pixel_x, pixel_y = affected_mob.default_pixel_x, time = 2) - sleep(3) + animate(affected_mob, pixel_x = affected_mob.default_pixel_x, pixel_y = affected_mob.default_pixel_x, time = 2) return diff --git a/code/datums/diseases/cold.dm b/code/datums/diseases/cold.dm index f9576f857b2..f88ba69bc22 100644 --- a/code/datums/diseases/cold.dm +++ b/code/datums/diseases/cold.dm @@ -2,19 +2,18 @@ name = "The Cold" max_stages = 3 spread_text = "Airborne" - spread_flags = AIRBORNE + spread_flags = DISEASE_SPREAD_AIRBORNE cure_text = "Rest & " + REAGENT_SPACEACILLIN cures = list(REAGENT_ID_SPACEACILLIN, REAGENT_ID_CHICKENSOUP) - needs_all_cures = FALSE + virus_modifiers = NONE //Does NOT have needs_all_cures agent = "XY-rhinovirus" 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 + danger = DISEASE_MINOR /datum/disease/cold/stage_act() - if(!..()) - return FALSE + ..() switch(stage) if(2) if(affected_mob.stat == UNCONSCIOUS && prob(40)) @@ -59,7 +58,7 @@ if(prob(1)) to_chat(affected_mob, span_notice("Mucous runs down the back of your throat.")) if(prob(1) && prob(50)) - if(!affected_mob.resistances.Find(/datum/disease/flu)) + if(affected_mob.HasResistance(/datum/disease/flu)) var/datum/disease/Flu = new /datum/disease/flu(0) affected_mob.ContractDisease(Flu) cure() diff --git a/code/datums/diseases/cold9.dm b/code/datums/diseases/cold9.dm index ff82a5cfb57..0fdda21c10d 100644 --- a/code/datums/diseases/cold9.dm +++ b/code/datums/diseases/cold9.dm @@ -3,28 +3,43 @@ medical_name = "ICE9 Cold" max_stages = 3 spread_text = "On contact" - spread_flags = CONTACT_GENERAL + spread_flags = DISEASE_SPREAD_CONTACT | DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_FLUIDS cure_text = REAGENT_SPACEACILLIN cures = list(REAGENT_ID_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 + danger = DISEASE_HARMFUL /datum/disease/cold9/stage_act() - if(!..()) - return FALSE - if(stage < 2) - return - - var/stage_factor = stage - 1 - affected_mob.bodytemperature -= 7.5 * stage_factor - if(prob(2 * stage_factor)) - affected_mob.say("*sneeze") - if(prob(2 * stage_factor)) - affected_mob.say("*cough") - if(prob(3 * stage_factor)) - to_chat(affected_mob, span_danger("Your throat feels sore.")) - if(prob(5 * stage_factor)) - to_chat(affected_mob, span_danger("You feel stiff.")) - affected_mob.adjustFireLoss(1) + ..() + switch(stage) + if(1) + if(prob(1)) + affected_mob.emote("sniff") + if(2) + if(prob(10)) + affected_mob.bodytemperature -= 2 + if(prob(1) && prob(10)) + to_chat(affected_mob, span_notice("You feel better.")) + cure() + return + if(prob(1)) + affected_mob.emote("sneeze") + if(prob(1)) + affected_mob.emote("cough") + if(prob(1)) + to_chat(affected_mob, span_danger("Your throat feels sore.")) + if(prob(5)) + to_chat(affected_mob, span_danger("You feel stiff.")) + if(3) + if(prob(10)) + affected_mob.bodytemperature -= 5 + if(prob(1)) + affected_mob.emote("sneeze") + if(prob(1)) + affected_mob.emote("cough") + if(prob(1)) + to_chat(affected_mob, span_danger("Your throat feels sore.")) + if(prob(10)) + to_chat(affected_mob, span_danger("You feel stiff.")) diff --git a/code/datums/diseases/darkness.dm b/code/datums/diseases/darkness.dm index b340ca5d45d..f8df462591b 100644 --- a/code/datums/diseases/darkness.dm +++ b/code/datums/diseases/darkness.dm @@ -7,4 +7,4 @@ agent = "Bluespace Exposure" viable_mobtypes = list(/mob/living/carbon/human) desc = "If left untreated, subject will lose grip on reality." - severity = HARMFUL + danger = HARMFUL diff --git a/code/datums/diseases/fake_gbs.dm b/code/datums/diseases/fake_gbs.dm index 4ab932e8cde..3f8b49bd772 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 = DISEASE_SPREAD_CONTACT | DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_FLUIDS cure_text = REAGENT_ADRANOL + " & " + REAGENT_SULFUR cures = list(REAGENT_ID_ADRANOL, REAGENT_ID_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 // Mimics real GBS + danger = DISEASE_BIOHAZARD // Mimics real GBS /datum/disease/fake_gbs/stage_act() if(!..()) diff --git a/code/datums/diseases/flu.dm b/code/datums/diseases/flu.dm index 4152623124a..da6b7033380 100644 --- a/code/datums/diseases/flu.dm +++ b/code/datums/diseases/flu.dm @@ -4,17 +4,16 @@ spread_text = "Airborne" cure_text = REAGENT_SPACEACILLIN cures = list(REAGENT_ID_SPACEACILLIN, REAGENT_ID_CHICKENSOUP) - needs_all_cures = FALSE + virus_modifiers = NONE //Does NOT have needs_all_cures cure_chance = 10 agent = "H13N1 flu virion" 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 + danger = DISEASE_MINOR /datum/disease/flu/stage_act() - if(!..()) - return FALSE + ..() switch(stage) if(2) if(affected_mob.lying && prob(20)) diff --git a/code/datums/diseases/food_poisoning.dm b/code/datums/diseases/food_poisoning.dm index bfb9aa445a7..529a5e07dc3 100644 --- a/code/datums/diseases/food_poisoning.dm +++ b/code/datums/diseases/food_poisoning.dm @@ -3,16 +3,14 @@ max_stages = 3 stage_prob = 5 spread_text = "Non-Contagious" - spread_flags = NON_CONTAGIOUS + spread_flags = DISEASE_SPREAD_NON_CONTAGIOUS cure_text = "Sleep" agent = REAGENT_SALMONELLA cures = list(REAGENT_ID_CHICKENSOUP) cure_chance = 10 viable_mobtypes = list(/mob/living/carbon/human) - desc = "Nausea, sickness, and vomitting." - severity = MINOR - disease_flags = CURABLE|CAN_NOT_POPULATE - virus_heal_resistant = TRUE + desc = "Nausea, sickness, and vomiting." + danger = DISEASE_MINOR /datum/disease/food_poisoning/stage_act() if(!..()) diff --git a/code/datums/diseases/gbs.dm b/code/datums/diseases/gbs.dm index 3da7052495c..cd97f6fac8f 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 = DISEASE_SPREAD_CONTACT | DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_FLUIDS cure_text = REAGENT_ADRANOL + " & " + REAGENT_SULFUR cures = list(REAGENT_ID_ADRANOL, REAGENT_ID_SULFUR) cure_chance = 15 agent = "Gravitokinetic Bipotential SADS+" viable_mobtypes = list(/mob/living/carbon/human) - severity = BIOHAZARD + danger = DISEASE_PANDEMIC disease_flags = CAN_NOT_POPULATE /datum/disease/gbs/stage_act() @@ -42,7 +42,7 @@ name = "Non-Contagious GBS" stage_prob = 5 spread_text = "Non-contagious" - spread_flags = NON_CONTAGIOUS + spread_flags = DISEASE_SPREAD_NON_CONTAGIOUS cure_text = REAGENT_CRYOXADONE cures = list(REAGENT_ID_CRYOXADONE) cure_chance = 10 diff --git a/code/datums/diseases/lycancoughy.dm b/code/datums/diseases/lycancoughy.dm index 7aba6ffc6ae..9172db1e119 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 = DISEASE_SPREAD_CONTACT | DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_FLUIDS cure_text = REAGENT_ETHANOL cures = list(REAGENT_ID_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 + danger = DISEASE_HARMFUL var/barklimit var/list/puppy_types = list(/mob/living/simple_mob/animal/passive/dog/corgi/puppy) 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 e791bb6c880..e0a80afe1d5 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 + danger = DISEASE_MINOR /datum/disease/magnitis/stage_act() if(!..()) diff --git a/code/datums/diseases/roanoake.dm b/code/datums/diseases/roanoake.dm index dac36d609f4..4e884004b63 100644 --- a/code/datums/diseases/roanoake.dm +++ b/code/datums/diseases/roanoake.dm @@ -10,10 +10,9 @@ cure_chance = 10 viable_mobtypes = list(/mob/living/carbon/human) desc = "If left untreated, subject will become a xenochimera upon perishing." - severity = BIOHAZARD + danger = DISEASE_BIOHAZARD disease_flags = CURABLE - virus_heal_resistant = TRUE - allow_dead = TRUE + //allow_dead = TRUE //Unused var/list/obj/item/organ/organ_list = list() var/obj/item/organ/O diff --git a/code/datums/outfits/jobs/medical.dm b/code/datums/outfits/jobs/medical.dm index 6a4d120d37d..6e34c37b2bf 100644 --- a/code/datums/outfits/jobs/medical.dm +++ b/code/datums/outfits/jobs/medical.dm @@ -50,6 +50,7 @@ mask = /obj/item/clothing/mask/surgical backpack = /obj/item/storage/backpack/virology satchel_one = /obj/item/storage/backpack/satchel/vir + belt = /obj/item/extrapolator /decl/hierarchy/outfit/job/medical/doctor/nurse name = OUTFIT_JOB_NAME(JOB_ALT_NURSE) diff --git a/code/datums/supplypacks/medical.dm b/code/datums/supplypacks/medical.dm index f31fbc7b45d..7bf3047c089 100644 --- a/code/datums/supplypacks/medical.dm +++ b/code/datums/supplypacks/medical.dm @@ -351,7 +351,7 @@ /datum/supply_pack/med/virus name = "Virus culture crate" desc = "Glass bottles with viral cultures. HANDLE WITH CARE. Requires Chief Medical Officer access." - contains = list(/obj/item/reagent_containers/glass/bottle/culture/cold = 1, /obj/item/reagent_containers/glass/bottle/culture/flu = 1) + contains = list(/obj/item/reagent_containers/glass/beaker/vial/culture/cold = 1, /obj/item/reagent_containers/glass/beaker/vial/culture/flu = 1) cost = 25 containertype = /obj/structure/closet/crate/secure/zenghu containername = "Virus culture crate" @@ -425,7 +425,7 @@ name = "Experimental Disease crate" desc = "An experimental disease. Contains a multitude of symptoms." contains = list( - /obj/item/reagent_containers/glass/bottle/culture/random_virus = 1 + /obj/item/reagent_containers/glass/beaker/vial/culture/random_virus = 1 ) cost = 60 containertype = /obj/structure/closet/crate/freezer @@ -435,7 +435,7 @@ name = "Minor Experimental Disease crate" desc = "An experimental disease. Contains a weakened, untested viral culture." contains = list( - /obj/item/reagent_containers/glass/bottle/culture/random_virus/minor = 1 + /obj/item/reagent_containers/glass/beaker/vial/culture/random_virus/minor = 1 ) cost = 40 containertype = /obj/structure/closet/crate/freezer diff --git a/code/game/atoms.dm b/code/game/atoms.dm index a2704880168..7e3ecc2e4b1 100644 --- a/code/game/atoms.dm +++ b/code/game/atoms.dm @@ -483,17 +483,6 @@ . = 1 return 1 -/atom/proc/add_vomit_floor(mob/living/carbon/M as mob, var/toxvomit = 0) - if( istype(src, /turf/simulated) ) - var/obj/effect/decal/cleanable/vomit/this = new /obj/effect/decal/cleanable/vomit(src) - - for(var/datum/disease/D in M.GetViruses()) - this.viruses |= D.Copy() - - // Make toxins vomit look different - if(toxvomit) - this.icon_state = "vomittox_[pick(1,4)]" - /atom/proc/clean_blood() if(!simulated) return @@ -853,3 +842,15 @@ GLOBAL_LIST_EMPTY(icon_dimensions) var/icon/my_icon = icon(icon_path) GLOB.icon_dimensions[icon_path] = list("width" = my_icon.Width(), "height" = my_icon.Height()) return GLOB.icon_dimensions[icon_path] + +/** +* Respond to our atom being checked by a virus extrapolator. +* +* Default behaviour is to send COMSIG_ATOM_EXTRAPOLATOR_ACT and return an empty list (which may be populated by the signal) +* +* Returns a list of viruses in the atom. +* Include EXTRAPOLATOR_SPECIAL_HANDLED in the list if the extrapolation act has been handled by this proc or a signal, and should not be handled by the extrapolator itself. +*/ +/atom/proc/extrapolator_act(mob/living/user, obj/item/extrapolator/extrapolator, dry_run = FALSE) + . = list(EXTRAPOLATOR_RESULT_DISEASES = list()) + SEND_SIGNAL(src, COMSIG_ATOM_EXTRAPOLATOR_ACT, user, extrapolator, dry_run, .) diff --git a/code/game/jobs/job/job_goodies.dm b/code/game/jobs/job/job_goodies.dm index f4512d39fca..90ec8b2c1f7 100644 --- a/code/game/jobs/job/job_goodies.dm +++ b/code/game/jobs/job/job_goodies.dm @@ -511,8 +511,8 @@ /obj/item/storage/pill_bottle/spaceacillin = 150, /obj/item/clothing/mask/surgical = 150, /obj/item/clothing/gloves/sterile/latex = 150, - /obj/item/reagent_containers/glass/bottle/culture/cold = 150, - /obj/item/reagent_containers/glass/bottle/culture/flu = 150, + /obj/item/reagent_containers/glass/beaker/vial/culture/random_virus/minor = 150, + /obj/item/reagent_containers/glass/beaker/vial/culture/random_virus = 150, /obj/item/reagent_containers/blood/OMinus = 100, ) diff --git a/code/game/machinery/adv_med.dm b/code/game/machinery/adv_med.dm index 425a94528d4..6e5cccb616e 100644 --- a/code/game/machinery/adv_med.dm +++ b/code/game/machinery/adv_med.dm @@ -188,7 +188,7 @@ occupantData["health"] = H.health occupantData["maxHealth"] = H.getMaxHealth() - occupantData["hasVirus"] = LAZYLEN(H.viruses) + occupantData["hasVirus"] = H.IsInfected() occupantData["bruteLoss"] = H.getBruteLoss() occupantData["oxyLoss"] = H.getOxyLoss() @@ -379,7 +379,7 @@ dat += (occupant.health > (occupant.getMaxHealth() / 2) ? span_blue(health_text) : span_red(health_text)) dat += "
" - if(LAZYLEN(occupant.viruses)) + if(occupant.IsInfected()) for(var/datum/disease/D in occupant.GetViruses()) if(D.visibility_flags & HIDDEN_SCANNER) continue diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm index 0b24e9276e2..e6cb5dc9003 100644 --- a/code/game/machinery/computer/medical.dm +++ b/code/game/machinery/computer/medical.dm @@ -173,7 +173,7 @@ if(MED_DATA_V_DATA) data["virus"] = list() for(var/datum/disease/D in GLOB.active_diseases) - if(!D.discovered) + if(!global_flag_check(D.virus_modifiers, DISCOVERED)) continue var/datum/data/record/v = GLOB.active_diseases[D] data["virus"] += list(list("name" = v.fields["name"], "D" = "\ref[v]")) diff --git a/code/game/machinery/pandemic.dm b/code/game/machinery/pandemic.dm index dd245e07967..99ff2859db9 100644 --- a/code/game/machinery/pandemic.dm +++ b/code/game/machinery/pandemic.dm @@ -6,15 +6,16 @@ anchored = TRUE icon = 'icons/obj/pandemic.dmi' icon_state = "pandemic0" + idle_power_usage = 20 + use_power = TRUE + var/temp_html = "" - var/printing = null - var/wait = null - var/selected_strain_index = 1 + var/printing = FALSE + var/wait = FALSE var/obj/item/reagent_containers/beaker = null -// PanDEMIC Bottle -/obj/item/reagent_containers/glass/bottle/vaccine - icon_state = "bottle10" +// PanDEMIC Vial +/obj/item/reagent_containers/glass/beaker/vial/vaccine possible_transfer_amounts = (list(5, 10, 15)) volume = 15 @@ -26,200 +27,59 @@ stat |= BROKEN update_icon() -/obj/machinery/computer/pandemic/proc/GetViruses() - if(beaker && beaker.reagents) - if(length(beaker.reagents.reagent_list)) - var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list - if(BL) - if(BL.data && BL.data["viruses"]) - var/list/viruses = BL.data["viruses"] - return viruses - -/obj/machinery/computer/pandemic/proc/GetVirusByIndex(index) - var/list/viruses = GetViruses() - if(viruses && index > 0 && index <= length(viruses)) - return viruses[index] - -/obj/machinery/computer/pandemic/proc/GetResistances() - if(beaker && beaker.reagents) - if(length(beaker.reagents.reagent_list)) - var/datum/reagent/blood/BL = locate() in beaker.reagents.reagent_list - if(BL) - if(BL.data && BL.data["resistances"]) - var/list/resistances = BL.data["resistances"] - return resistances - -/obj/machinery/computer/pandemic/proc/GetResistancesByIndex(index) - var/list/resistances = GetResistances() - if(resistances && index > 0 && index <= length(resistances)) - return resistances[index] - -/obj/machinery/computer/pandemic/proc/GetVirusTypeByIndex(index) - var/datum/disease/D = GetVirusByIndex(index) - if(D) - return D.GetDiseaseID() - -/obj/machinery/computer/pandemic/proc/replicator_cooldown(waittime) - wait = 1 - update_icon() - spawn(waittime) - wait = null - update_icon() - playsound(loc, 'sound/machines/ping.ogg', 30, 1) - /obj/machinery/computer/pandemic/update_icon() if(stat & BROKEN) icon_state = (beaker ? "pandemic1_b" : "pandemic0_b") return icon_state = "pandemic[(beaker)?"1":"0"][!(stat & NOPOWER) ? "" : "_nopower"]" -/obj/machinery/computer/pandemic/proc/create_culture(name, bottle_type = "culture", cooldown = 50, vaccine = FALSE) - - var/obj/item/reagent_containers/glass/bottle/B - if(vaccine) - B = new /obj/item/reagent_containers/glass/bottle/vaccine(loc) - B.name = "[name] vaccine" - else - B = new(loc) - B.icon_state = "bottle10" - B.name = "[name] [bottle_type] bottle" - - B.pixel_x = rand(-3, 3) - B.pixel_y = rand(-3, 3) - replicator_cooldown(cooldown) - return B - /obj/machinery/computer/pandemic/tgui_act(action, params, datum/tgui/ui, datum/tgui_state/state) - if(..()) + . = ..() + if(.) return if(inoperable()) return - . = TRUE - switch(action) - if("clone_strain") + if("create_culture_bottle") + if(wait) + return FALSE + create_culture_bottle(params["index"]) + return TRUE + if("create_vaccine_bottle") if(wait) atom_say("The replicator is not ready yet.") - return - - var/strain_index = text2num(params["strain_index"]) - if(isnull(strain_index)) - atom_say("Unable to respond to command.") - return - var/datum/disease/virus = GetVirusByIndex(strain_index) - var/datum/disease/D = null - if(!virus) - atom_say("Unable to find requested strain.") - return - var/type = virus.GetDiseaseID() - if(!ispath(type)) - var/datum/disease/advance/A = GLOB.archive_diseases[type] - if(A) - D = new A.type(0, A) - else if(type) - if(type in GLOB.diseases) // Make sure this is a disease - D = new type(0, null) - if(!D) - atom_say("Unable to synthesize requested strain.") - return - var/default_name = "" - if(D.name == "Unknown" || D.name == "") - default_name = replacetext(beaker.name, new/regex(" culture bottle\\Z", "g"), "") - else - default_name = D.name - var/name = tgui_input_text(ui.user, "Name:", "Name the culture", default_name, MAX_NAME_LEN) - if(name == null || wait) - return - var/obj/item/reagent_containers/glass/bottle/B = create_culture(name) - B.desc = "A small bottle. Contains [D.agent] culture in synthblood medium." - B.reagents.add_reagent(REAGENT_ID_BLOOD, 20, list("viruses" = list(D))) - if("clone_vaccine") - if(wait) - atom_say("The replicator is not ready yet.") - return - - var/resistance_index = text2num(params["resistance_index"]) - if(isnull(resistance_index)) - atom_say("Unable to find requested antibody.") - return - var/vaccine_type = GetResistancesByIndex(resistance_index) - var/vaccine_name = "Unknown" - if(!ispath(vaccine_type)) - if(GLOB.archive_diseases[vaccine_type]) - var/datum/disease/D = GLOB.archive_diseases[vaccine_type] - if(D) - vaccine_name = D.name - else if(vaccine_type) - var/datum/disease/D = new vaccine_type(0, null) - if(D) - vaccine_name = D.name - - if(!vaccine_type) - atom_say("Unable to synthesize requested antibody.") - return - - var/obj/item/reagent_containers/glass/bottle/vaccine/B = create_culture(vaccine_name, REAGENT_ID_VACCINE, 200, TRUE) - B.reagents.add_reagent(REAGENT_ID_VACCINE, 15, list(vaccine_type)) - if(beaker && beaker.reagents && length(beaker.reagents.reagent_list)) - beaker.reagents.remove_reagent(REAGENT_ID_BLOOD, 5) - if(!length(beaker.reagents.reagent_list)) - update_tgui_static_data(ui.user) - + return FALSE + create_vaccine_bottle(params["index"]) + return TRUE if("eject_beaker") eject_beaker() update_tgui_static_data(ui.user) + return TRUE if("destroy_eject_beaker") beaker.reagents.clear_reagents() eject_beaker() update_tgui_static_data(ui.user) - if("print_release_forms") - var/strain_index = text2num(params["strain_index"]) + return TRUE + if("empty_beaker") + beaker.reagents.clear_reagents() + return TRUE + if("rename_disease") + rename_disease(params["index"], params["name"]) + return TRUE + if("print_release_form") + var/strain_index = text2num(params["index"]) if(isnull(strain_index)) atom_say("Unable to respond to command.") - return - var/type = GetVirusTypeByIndex(strain_index) + return FALSE + var/type = get_virus_id_by_index(strain_index) if(!type) atom_say("Unable to find requested strain.") - return + return FALSE var/datum/disease/advance/A = GLOB.archive_diseases[type] if(!A) atom_say("Unable to find requested strain.") - return - print_form(A, ui.user) - if("name_strain") - var/strain_index = text2num(params["strain_index"]) - if(isnull(strain_index)) - atom_say("Unable to respond to command.") - return - var/type = GetVirusTypeByIndex(strain_index) - if(!type) - atom_say("Unable to find requested strain.") - return - var/datum/disease/advance/A = GLOB.archive_diseases[type] - if(!A) - atom_say("Unable to find requested strain.") - return - if(A.name != "Unknown") - atom_say("Request rejected. Strain already has a name.") - return - var/new_name = tgui_input_text(ui.user, "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() - update_tgui_static_data(ui.user) - if("switch_strain") - var/strain_index = text2num(params["strain_index"]) - if(isnull(strain_index) || strain_index < 1) - atom_say("Unable to respond to command.") - return - var/list/viruses = GetViruses() - if(strain_index > length(viruses)) - atom_say("Unable to find requested strain.") - return - selected_strain_index = strain_index; + return FALSE else return FALSE @@ -229,86 +89,35 @@ /obj/machinery/computer/pandemic/tgui_interact(mob/user, datum/tgui/ui, datum/tgui/parent_ui = null) ui = SStgui.try_update_ui(user, src, ui) if(!ui) - ui = new(user, src, "PanDEMIC", name) + ui = new(user, src, "Pandemic", name) ui.open() /obj/machinery/computer/pandemic/tgui_data(mob/user) - var/datum/reagent/blood/Blood = null - if(beaker) - var/datum/reagents/R = beaker.reagents - for(var/datum/reagent/blood/B in R.reagent_list) - if(B) - Blood = B - break - - var/list/data = list( - "synthesisCooldown" = wait ? TRUE : FALSE, - "beakerLoaded" = beaker ? TRUE : FALSE, - "beakerContainsBlood" = Blood ? TRUE : FALSE, - "beakerContainsVirus" = length(Blood?.data["viruses"]) != 0, - "selectedStrainIndex" = selected_strain_index, + var/list/data = list() + data["is_ready"] = !wait + if(!beaker) + data["has_beaker"] = FALSE + data["has_blood"] = FALSE + return data + data["has_beaker"] = TRUE + data["beaker"] = list( + "volume" = round(beaker.reagents?.total_volume, 0.01) || 0, + "capacity" = beaker.volume ) + var/datum/reagent/blood/blood = locate() in beaker.reagents.reagent_list + if(!blood) + data["has_blood"] = FALSE + return data + + data["has_blood"] = TRUE + data["blood"] = list() + data["blood"]["dna"] = blood.data["blood_DNA"] || "none" + data["blood"]["type"] = blood.data["blood_type"] || "none" + data["viruses"] = get_viruses_data(blood) + data["resistances"] = get_resistance_data(blood) return data -/obj/machinery/computer/pandemic/tgui_static_data(mob/user) - var/list/data = list() - . = data - - var/datum/reagent/blood/Blood = null - if(beaker) - var/datum/reagents/R = beaker.reagents - for(var/datum/reagent/blood/B in R.reagent_list) - if(B) - Blood = B - break - - var/list/strains = list() - for(var/datum/disease/D in GetViruses()) - if(D.visibility_flags & HIDDEN_PANDEMIC) - continue - - var/list/symptoms = list() - if(istype(D, /datum/disease/advance)) - var/datum/disease/advance/A = D - D = GLOB.archive_diseases[A.GetDiseaseID()] - if(!D) - CRASH("We weren't able to get the advance disease from the archive.") - for(var/datum/symptom/S in A.symptoms) - symptoms += list(list( - "name" = S.name, - "stealth" = S.stealth, - "resistance" = S.resistance, - "stageSpeed" = S.stage_speed, - "transmissibility" = S.transmittable, - "complexity" = S.level, - )) - - strains += list(list( - "commonName" = D.name, - "description" = D.desc, - "bloodDNA" = Blood.data["blood_DNA"], - "bloodType" = Blood.data["blood_type"], - "diseaseAgent" = D.agent, - "possibleTreatments" = D.cure_text, - "transmissionRoute" = D.spread_text, - "symptoms" = symptoms, - "isAdvanced" = istype(D, /datum/disease/advance), - )) - data["strains"] = strains - - var/list/resistances = list() - for(var/resistance in GetResistances()) - if(!ispath(resistance)) - var/datum/disease/D = GLOB.archive_diseases[resistance] - if(D) - resistances += list(D.name) - else if(resistance) - var/datum/disease/D = new resistance(0, null) - if(D) - resistances += list(D.name) - data["resistances"] = resistances - /obj/machinery/computer/pandemic/proc/eject_beaker() set name = "Eject Beaker" set category = "Object" @@ -320,10 +129,13 @@ beaker.forceMove(loc) beaker = null icon_state = "pandemic0" - selected_strain_index = 1 /obj/machinery/computer/pandemic/proc/print_form(datum/disease/advance/D, mob/living/user) D = GLOB.archive_diseases[D.GetDiseaseID()] + if(!istype(D)) + visible_message(span_warning("ERROR: Unable to print form.")) + playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, 1) + return if(!(printing) && D) var/reason = tgui_input_text(user,"Enter a reason for the release", "Write", multiline = TRUE) if(!reason) @@ -333,7 +145,7 @@ for(var/I in D.symptoms) var/datum/symptom/S = I english_symptoms += S.name - var/symtoms = english_list(english_symptoms) + var/symptoms = english_list(english_symptoms) var/signature if(tgui_alert(user, "Would you like to add your signature?", "Signature", list("Yes","No")) == "Yes") @@ -341,7 +153,7 @@ else signature = "" - printing = 1 + printing = TRUE var/obj/item/paper/P = new /obj/item/paper(loc) visible_message(span_notice("[src] rattles and prints out a sheet of paper.")) playsound(loc, 'sound/machines/printer.ogg', 50, 1) @@ -349,7 +161,7 @@ P.info = span_underline(span_huge(span_bold("
Releasing Virus
"))) P.info += "
" P.info += span_underline("Name of the Virus:") + " [D.name]
" - P.info += span_underline("Symptoms:") + " [symtoms]
" + P.info += span_underline("Symptoms:") + " [symptoms]
" P.info += span_underline("Spreads by:") + " [D.spread_text]
" P.info += span_underline("Cured by:") + " [D.cure_text]
" P.info += "
" @@ -360,7 +172,7 @@ P.info += "If approved, stamp below with the Chief Medical Officer's stamp, and/or the Captain's stamp if required:" P.updateinfolinks() P.name = "Releasing Virus - [D.name]" - printing = null + printing = FALSE /obj/machinery/computer/pandemic/attack_ai(mob/user) return attack_hand(user) @@ -394,3 +206,146 @@ icon_state = "pandemic1" else return ..() + +/obj/machinery/computer/pandemic/proc/get_viruses_data(datum/reagent/blood/blood) + . = list() + var/list/viruses = blood.get_diseases() + var/index = 1 + + for(var/datum/disease/disease as anything in viruses) + if(CHECK_BITFIELD(disease.visibility_flags, HIDDEN_PANDEMIC)) + continue + + var/list/traits = list() + traits["name"] = disease.name + if(istype(disease, /datum/disease/advance)) + var/datum/disease/advance/adv_disease = disease + traits["can_rename"] = (adv_disease.name == "Unknown") + traits["name"] = disease.name + traits["is_adv"] = TRUE + traits["symptoms"] = list() + for(var/datum/symptom/symptom as() in adv_disease.symptoms) + traits["symptoms"] += list(symptom.get_symptom_data()) + traits["resistance"] = adv_disease.resistance + traits["stealth"] = adv_disease.stealth + traits["stage_speed"] = adv_disease.stage_rate + traits["transmission"] = adv_disease.transmission + traits["symptom_severity"] = adv_disease.severity + + traits["index"] = index++ + traits["agent"] = disease.agent + traits["description"] = disease.desc || "none" + traits["spread"] = disease.spread_text || "none" + traits["cure"] = disease.cure_text || "none" + traits["danger"] = disease.danger || "none" + + . += list(traits) + +/obj/machinery/computer/pandemic/proc/get_resistance_data(datum/reagent/blood/blood) + var/list/data = list() + if(!islist(blood.data["resistances"])) + return data + var/list/resistances = blood.data["resistances"] + for(var/id in resistances) + var/list/resistance = list() + var/datum/disease/disease = GLOB.archive_diseases[id] + if(disease) + resistance["id"] = id + resistance["name"] = disease.name + data += list(resistance) + return data + +/obj/machinery/computer/pandemic/proc/get_by_index(thing, index) + if(!beaker || !beaker.reagents) + return FALSE + var/datum/reagent/blood/blood = locate() in beaker.reagents.reagent_list + if(blood?.data[thing]) + return blood.data[thing][index] + return FALSE + +/obj/machinery/computer/pandemic/proc/get_virus_id_by_index(index) + var/datum/disease/disease = get_by_index("viruses", index) + if(!disease) + return FALSE + return disease.GetDiseaseID() + +/obj/machinery/computer/pandemic/proc/create_vaccine_bottle(index) + use_power(active_power_usage) + var/id = index + var/datum/disease/disease = GLOB.archive_diseases[id] + var/obj/item/reagent_containers/glass/beaker/vial/bottle = new(drop_location()) + bottle.name = "[disease.name] vaccine" + bottle.reagents.add_reagent(REAGENT_ID_VACCINE, 15, list(get_by_index("resistances", id))) + beaker.reagents.remove_reagent(REAGENT_ID_BLOOD, 5) + wait = TRUE + addtimer(CALLBACK(src, PROC_REF(reset_replicator_cooldown)), 20 SECONDS) + return TRUE + +/obj/machinery/computer/pandemic/proc/create_culture_bottle(index) + var/id = get_virus_id_by_index(text2num(index)) + var/datum/disease/advance/adv_disease = GLOB.archive_diseases[id] + var/old_name = adv_disease.name + + if(!istype(adv_disease)) + to_chat(usr, span_warning("ERROR: Cannot replicate virus strain.")) + return FALSE + + if(!beaker.reagents.has_reagent(REAGENT_ID_BLOOD, 10)) + to_chat(usr, span_warning("ERROR: Not enough blood in the sample.")) + return FALSE + + use_power(active_power_usage) + adv_disease = adv_disease.Copy() + adv_disease.name = old_name + var/list/cures = get_beaker_cures(id) + if(cures.len) + adv_disease.cures = cures[1] + adv_disease.cure_text = cures[2] + var/list/data = list("viruses" = list(adv_disease)) + + var/obj/item/reagent_containers/glass/beaker/vial/bottle = new(drop_location()) + bottle.name = "[adv_disease.name] culture vial" + bottle.desc = "A small vial containing [adv_disease.agent] culture in synthblood." + bottle.reagents.add_reagent(REAGENT_ID_BLOOD, 10, data) + beaker.reagents.remove_reagent(REAGENT_ID_BLOOD, 10) + wait = TRUE + addtimer(CALLBACK(src, PROC_REF(reset_replicator_cooldown)), 5 SECONDS) + return TRUE + +/obj/machinery/computer/pandemic/proc/get_beaker_cures(disease_id) + var/list/cures = list() + if(!beaker) + return cures + + var/datum/reagent/blood/blood = beaker.reagents.get_reagent(REAGENT_ID_BLOOD) + if(!blood) + return cures + + var/list/viruses = blood.get_diseases() + if(!length(viruses)) + return cures + + for(var/datum/disease/advance/disease in viruses) + if(disease.GetDiseaseID() == disease_id) + cures.Add(disease.cures) + cures.Add(disease.cure_text) + break + + return cures + +/obj/machinery/computer/pandemic/proc/rename_disease(index, name) + var/id = get_virus_id_by_index(text2num(index)) + var/datum/disease/advance/adv_disease = GLOB.archive_diseases[id] + + if(adv_disease) + if(!name) + return FALSE + adv_disease.AssignName(name) + return TRUE + return FALSE + +/obj/machinery/computer/pandemic/proc/reset_replicator_cooldown() + wait = FALSE + SStgui.update_uis(src) + playsound(src, 'sound/machines/ping.ogg', 30, TRUE) + return TRUE diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index fff4c1a8008..22bce1ad340 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -125,7 +125,7 @@ var/global/list/image/splatter_cache=list() if(viruses) for(var/datum/disease/D in viruses) if(D.IsSpreadByTouch()) - perp.ContractDisease(D) + perp.ContractDisease(D, BP_R_FOOT) amount-- @@ -156,7 +156,7 @@ var/global/list/image/splatter_cache=list() if(viruses) for(var/datum/disease/D in viruses) if(D.IsSpreadByTouch()) - user.ContractDisease(D) + user.ContractDisease(D, BP_R_HAND) /obj/effect/decal/cleanable/blood/splatter random_icon_states = list("mgibbl1", "mgibbl2", "mgibbl3", "mgibbl4", "mgibbl5") @@ -287,7 +287,9 @@ var/global/list/image/splatter_cache=list() return if(viruses) for(var/datum/disease/D in viruses) - perp.ContractDisease(D) + if(D.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS)) + continue + perp.ContractDisease(D, BP_R_FOOT) /obj/effect/decal/cleanable/mucus/attack_hand(mob/living/carbon/human/perp) if(perp.is_incorporeal()) @@ -296,7 +298,9 @@ var/global/list/image/splatter_cache=list() return if(viruses) for(var/datum/disease/D in viruses) - perp.ContractDisease(D) + if(D.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS)) + continue + perp.ContractDisease(D, BP_R_HAND) /obj/effect/decal/cleanable/vomit/Crossed(mob/living/carbon/human/perp) if(perp.is_incorporeal()) @@ -305,15 +309,31 @@ var/global/list/image/splatter_cache=list() return if(viruses) for(var/datum/disease/D in viruses) - perp.ContractDisease(D) + if(D.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS)) + continue + perp.ContractDisease(D, BP_R_FOOT) -/obj/effect/decal/cleanable/vomit/Crossed(mob/living/carbon/human/perp) +/obj/effect/decal/cleanable/vomit/attack_hand(mob/living/carbon/human/perp) if(perp.is_incorporeal()) return if(!istype(perp)) return if(viruses) for(var/datum/disease/D in viruses) - perp.ContractDisease(D) + if(D.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS)) + continue + perp.ContractDisease(D, BP_R_HAND) + +/obj/effect/decal/cleanable/mucus/extrapolator_act(mob/living/user, obj/item/extrapolator/extrapolator, dry_run) + . = ..() + EXTRAPOLATOR_ACT_ADD_DISEASES(., viruses) + +/obj/effect/decal/cleanable/vomit/extrapolator_act(mob/living/user, obj/item/extrapolator/extrapolator, dry_run) + . = ..() + EXTRAPOLATOR_ACT_ADD_DISEASES(., viruses) + +/obj/effect/decal/cleanable/blood/extrapolator_act(mob/living/user, obj/item/extrapolator/extrapolator, dry_run) + . = ..() + EXTRAPOLATOR_ACT_ADD_DISEASES(., viruses) #undef DRYING_TIME diff --git a/code/game/objects/effects/decals/Cleanable/misc.dm b/code/game/objects/effects/decals/Cleanable/misc.dm index b5fdc8d4a51..3d3b6e9e685 100644 --- a/code/game/objects/effects/decals/Cleanable/misc.dm +++ b/code/game/objects/effects/decals/Cleanable/misc.dm @@ -123,6 +123,19 @@ random_icon_states = list("vomit_1", "vomit_2", "vomit_3", "vomit_4") var/list/datum/disease/viruses = list() +/obj/effect/decal/cleanable/vomit/old + name = "crusty dried vomit" + desc = "You try not to look at the chunks, and fail." + +/obj/effect/decal/cleanable/vomit/old/Initialize(mapload, list/datum/disease/diseases) + . = ..() + icon_state += "-old" + if(length(diseases)) + viruses += diseases + if(prob(95)) + var/datum/disease/advance/new_disease = new /datum/disease/advance/random(rand(2, 5), rand(7, 9), 4) + src.viruses += new_disease + /obj/effect/decal/cleanable/tomato_smudge name = "tomato smudge" desc = "It's red." diff --git a/code/game/objects/items/devices/extrapolator.dm b/code/game/objects/items/devices/extrapolator.dm new file mode 100644 index 00000000000..6308b28c1d3 --- /dev/null +++ b/code/game/objects/items/devices/extrapolator.dm @@ -0,0 +1,264 @@ +/obj/item/extrapolator + name = "virus extrapolator" + icon = 'icons/obj/device.dmi' + icon_state = "extrapolator_scan" + desc = "A bulky scanning device, used to extract genetic material of potential pathogens." + item_flags = NOBLUDGEON + slot_flags = SLOT_BELT + w_class = ITEMSIZE_NORMAL + /// Whether the extrapolator is curently in use + var/using = FALSE + /// Whether the extrapolator is curently in SCAN or EXTRACT mode + var/scan = TRUE + /// The scanning module installed in the extrapolator. Used to determine extraction speed, and the stealthiest virus that's possible to extract. + var/obj/item/stock_parts/scanning_module/scanner + /// A list of advance IDs that this extrapolator has already extracted. + var/list/extracted_ids = list() + /// How long it takes for the extrapolator to extract a virus. + var/extract_time = 10 SECONDS + /// How long it tkaes for the extrapolator to isolate a symptom. + var/isolate_time = 15 SECONDS + /// The extrapolator can extract any virus with a stealth below this value. + var/maximum_stealth = 3 + /// The extrapolator can extract any symptom with a stealth below this value. + var/maximum_level = 5 + /// The typepath of the default scanning module that will generate in the extrapolator, if it starts with none. + var/default_scanning_module = /obj/item/stock_parts/scanning_module + /// Cooldown for when the extrapolator can be used next. + COOLDOWN_DECLARE(usage_cooldown) + +/obj/item/extrapolator/Initialize(mapload, obj/item/stock_parts/scanning_module/starting_scanner) + . = ..() + starting_scanner = starting_scanner || default_scanning_module + if(ispath(starting_scanner, /obj/item/stock_parts/scanning_module)) + scanner = new starting_scanner(src) + else if(istype(starting_scanner)) + starting_scanner.forceMove(src) + scanner = starting_scanner + + refresh_parts() + +/obj/item/extrapolator/attackby(obj/item/item, mob/user, params) + if(istype(item, /obj/item/stock_parts/scanning_module)) + if(!scanner) + user.drop_item() + item.loc = src + scanner = item + to_chat(user, span_notice("You install \the [scanner] in [src].")) + refresh_parts() + else + to_chat(user, span_notice("[src] already has \the [scanner] installed.")) + return + + if(item.has_tool_quality(TOOL_SCREWDRIVER) && scanner) + if(!scanner) // You never know + to_chat(user, span_warning("\The [src] has no scanner to remove!")) + return FALSE + to_chat(user, span_notice("You remove \the [scanner] from \the [src].")) + scanner.forceMove(drop_location()) + scanner = null + playsound(src, item.usesound, 50, 1) + return TRUE + +/obj/item/extrapolator/attack_self(mob/user) + . = ..() + playsound(src, 'sound/machines/click.ogg', 50, TRUE) + if(scan) + icon_state = "extrapolator_sample" + scan = FALSE + to_chat(user, span_notice("You remove the probe from the device and set it to EXTRACT.")) + else + icon_state = "extrapolator_scan" + scan = TRUE + to_chat(user, span_notice("You put the probe back into the device and set it to SCAN.")) + +/obj/item/extrapolator/examine(mob/user) + . = ..() + if(in_range(user, src) || isobserver(user)) + if(!scanner) + . += span_notice("The scanner is missing.") + else + . += span_notice("A class " + span_bold("[scanner.rating]") + " scanning module is installed. It is screwed in place.") + . += span_notice("Can detect diseases below stealth " + span_bold("[maximum_stealth]") + ".") + . += span_notice("Can extract diseases in " + span_bold("[DisplayTimeText(extract_time)]") + ".") + . += span_notice("Can isolate symptoms [maximum_level >= 9 ? "of any level" : "below level [maximum_level]"], in [DisplayTimeText(isolate_time)].") + +/obj/item/extrapolator/proc/refresh_parts() + if(!scanner) + return + var/effective_scanner_rating = scanner.rating +1 + extract_time = (10 SECONDS) / effective_scanner_rating + isolate_time = (15 SECONDS) / effective_scanner_rating + maximum_stealth = scanner.rating + 2 + maximum_level = scanner.rating + 5 + +/obj/item/extrapolator/attack(atom/AM, mob/living/user) + return + +/obj/item/extrapolator/afterattack(atom/target, mob/user, proximity_flag, click_parameters) + . = ..() + if(!proximity_flag && !scan) + return + if(using) + to_chat(user, span_warning("[icon2html(src, user)] The extrapolator is already in use.")) + return + if(!COOLDOWN_FINISHED(src, usage_cooldown)) + to_chat(user, span_warning("[icon2html(src, user)] The extrapolator is still recharging!")) + return + if(scanner) + var/list/result = target?.extrapolator_act(user, src, dry_run = TRUE) + var/list/diseases = result && result[EXTRAPOLATOR_RESULT_DISEASES] + if(!length(diseases)) + var/list/atom/targets = find_valid_targets(user, target) + var/target_amt = length(targets) + if(target_amt) + target = target_amt > 1 ? tgui_input_list(user, "Select object to analyze", "Viral Extrapolation", targets, default = targets[1]) : targets[1] + if(target) + result = target.extrapolator_act(user, src, dry_run = TRUE) + diseases = result && result[EXTRAPOLATOR_RESULT_DISEASES] + if(!target) + return + if(!length(diseases)) + if(scan) + to_chat(user, span_notice("[icon2html(src, user)] \The [src] fails to return any data.")) + else + to_chat(user, span_notice("[icon2html(src, user)] \The [src]'s probe detects no diseases.")) + return + if(EXTRAPOLATOR_ACT_CHECK(result, EXTRAPOLATOR_ACT_PRIORITY_SPECIAL)) + // extrapolator_act did some sort of special behavior, we don't need to do anything further + return + if(scan) + scan(user, target) + else + extrapolate(user, target) + else + to_chat(user, span_warning("The extrapolator has no scanner installed!")) + +/obj/item/extrapolator/proc/find_valid_targets(mob/living/user, atom/target) + . = list() + var/turf/target_turf = get_turf(target) + if(!target_turf) + return + for(var/atom/target_to_try in target_turf.contents - target) + var/list/result = target_to_try.extrapolator_act(user, src, dry_run = TRUE) + if(length(result[EXTRAPOLATOR_RESULT_DISEASES])) + . += target_to_try + +/obj/item/extrapolator/proc/scan(mob/living/user, atom/target) + . = TRUE + var/list/result = target?.extrapolator_act(user, target) + var/list/diseases = result[EXTRAPOLATOR_RESULT_DISEASES] + var/show_message = FALSE + if(!length(diseases)) + return FALSE + if(EXTRAPOLATOR_ACT_CHECK(result, EXTRAPOLATOR_ACT_PRIORITY_SPECIAL)) + return + var/list/message = list() + if(length(diseases)) + message += span_boldnotice("[costly_icon2html(target, user)] [target] scan results") + message += span_boldnotice("[icon2html(src, user)] \The [src] detects the following diseases:") + for(var/datum/disease/disease in diseases) + if(istype(disease, /datum/disease/advance)) + var/datum/disease/advance/advance_disease = disease + if(advance_disease.stealth >= maximum_stealth) + continue + show_message = TRUE + var/list/properties + if(global_flag_check(advance_disease.virus_modifiers, CARRIER)) + LAZYADD(properties, "carrier") + message += "[advance_disease.name][LAZYLEN(properties) ? " ([properties.Join(", ")])" : ""], stage [advance_disease.stage]/5" + if(extracted_ids[advance_disease.GetDiseaseID()]) + message += "This virus has been extracted by \the [src] previously." + message += "[advance_disease.name] has the following symptoms:" + for(var/datum/symptom/symptom in advance_disease.symptoms) + message += "[symptom.name]" + else + message += span_info("[disease.name], stage [disease.stage]/[disease.max_stages].") + if(show_message) + to_chat(user, examine_block(jointext(message, "\n")), avoid_highlighting = TRUE, trailing_newline = FALSE, type = MESSAGE_TYPE_INFO) + else + to_chat(user, span_notice("[icon2html(src, user)] \The [src] fails to return any data.")) + +/obj/item/extrapolator/proc/extrapolate(mob/living/user, atom/target, isolate = FALSE) + . = FALSE + var/list/result = target?.extrapolator_act(user, target) + var/list/diseases = result[EXTRAPOLATOR_RESULT_DISEASES] + if(!length(diseases)) + return + if(EXTRAPOLATOR_ACT_CHECK(result, EXTRAPOLATOR_ACT_PRIORITY_SPECIAL)) // hardcoded "we handled this ourselves" response + return TRUE + if(EXTRAPOLATOR_ACT_CHECK(result, EXTRAPOLATOR_ACT_PRIORITY_ISOLATE)) + isolate = TRUE + var/list/advance_diseases = list() + for(var/datum/disease/advance/candidate in diseases) + if(candidate.stealth >= maximum_stealth) + continue + advance_diseases += candidate + if(!length(advance_diseases)) + to_chat(user, span_warning("[icon2html(src, user)] There are no valid diseases to make a culture from.")) + return + var/datum/disease/advance/target_disease = length(advance_diseases) > 1 ? tgui_input_list(user, "Select disease to extract", "Viral Extraction", advance_diseases, default = advance_diseases[1]) : advance_diseases[1] + if(!target_disease) + return + using = TRUE + // I'll see about this... + // var/choice = tgui_alert(user, "What would you like to isolate?", "Isolate", list("Symptom", "Disease")) + // if(choice == "Symptom") + //. = isolate_symptom(user, target, target_disease) + // else + . = isolate_disease(user, target, target_disease) + using = FALSE + +/obj/item/extrapolator/proc/isolate_symptom(mob/living/user, atom/target, datum/disease/advance/target_disease) + . = FALSE + var/list/symptoms = list() + for(var/datum/symptom/symptom in target_disease.symptoms) + if(symptom.level <= maximum_level) + symptoms += symptom + continue + if(!length(symptoms)) + to_chat(user, span_warning("[icon2html(src, user)] There are no symptoms that could be isolated..")) + return + var/datum/symptom/chosen = length(symptoms) > 1 ? tgui_input_list(user, "Select symptom to isolate", "Symptom Extraction", symptoms, default = symptoms[1]) : symptoms[1] + if(!chosen) + return + user.visible_message(span_notice("[user] slots [target] into [src], which begins to whir and beep!"), span_notice("[icon2html(src, user)] You begin isolating " + span_bold("[chosen.name]") + " from [target]..."),) + var/datum/disease/advance/symptom_holder = new + symptom_holder.name = chosen.name + symptom_holder.symptoms += chosen + symptom_holder.Finalize() + symptom_holder.Refresh() + if(do_after(user, extract_time, target = target)) + create_culture(user, symptom_holder, target) + return TRUE + +/obj/item/extrapolator/proc/isolate_disease(mob/living/user, atom/target, datum/disease/advance/target_disease, timer = 10 SECONDS) + . = FALSE + user.visible_message(span_notice("[user] begins to thoroughly scan [target] with [src]..."), \ + span_notice("[icon2html(src, user)] You begin isolating " + span_bold("[target_disease.name]") + " from [target]...")) + if(do_after(user, isolate_time, target = target)) + create_culture(user, target_disease, target) + return TRUE + +/obj/item/extrapolator/proc/create_culture(mob/living/user, datum/disease/advance/disease) + . = FALSE + var/datum/disease/advance/D = disease.Copy() + var/list/data = list("viruses" = list(disease)) + if(user.get_active_hand() != src) + to_chat(user, span_warning("The extrapolator must be held in your active hand to work!")) + return + var/obj/item/reagent_containers/glass/beaker/vial/culture_bottle = new(user.drop_location()) + culture_bottle.name = "[D.name] culture bottle" + culture_bottle.desc = "A small bottle. Contains [D.agent] culture in synthblood medium." + culture_bottle.reagents.add_reagent(REAGENT_ID_BLOOD, 5, data) + user.put_in_hands(culture_bottle) + playsound(src, 'sound/machines/ping.ogg', vol = 30, vary = TRUE) + COOLDOWN_START(src, usage_cooldown, 1 SECONDS) + extracted_ids[D.GetDiseaseID()] = TRUE + return TRUE + +/obj/item/extrapolator/tier4 + default_scanning_module = /obj/item/stock_parts/scanning_module/hyper + +/obj/item/extrapolator/tier5 + default_scanning_module = /obj/item/stock_parts/scanning_module/omni diff --git a/code/game/objects/items/devices/scanners/guide.dm b/code/game/objects/items/devices/scanners/guide.dm index 75b3ee90b8c..3e731be2e2a 100644 --- a/code/game/objects/items/devices/scanners/guide.dm +++ b/code/game/objects/items/devices/scanners/guide.dm @@ -99,7 +99,7 @@ dat += span_bold("Genetic Damage") + " - Utilize cryogenic pod with appropriate chemicals (i.e. Cryoxadone) and below 70 K, or give Rezadone.
" if(bone) dat += span_bold("Bone fracture") + " - Splint damaged area. Treat with bone repair surgery or Osteodaxon after treating brute damage.
" - if(M.viruses && M.viruses.len) + if(M.IsInfected()) for(var/datum/disease/D in M.GetViruses()) if(D.visibility_flags & HIDDEN_SCANNER) continue diff --git a/code/game/objects/items/devices/scanners/health.dm b/code/game/objects/items/devices/scanners/health.dm index 7114bc02553..a4e05556b37 100644 --- a/code/game/objects/items/devices/scanners/health.dm +++ b/code/game/objects/items/devices/scanners/health.dm @@ -257,14 +257,11 @@ else dat += span_warning("Unknown substance[(unknown > 1)?"s":""] found in subject's dermis.") dat += "
" - if(LAZYLEN(C.viruses)) + if(C.IsInfected()) for (var/datum/disease/virus in C.GetViruses()) if(virus.visibility_flags & HIDDEN_SCANNER || virus.visibility_flags & HIDDEN_PANDEMIC) continue - virus.discovered = TRUE - dat += span_warning("Warning: [virus.name] detected in subject's blood.") - dat += "
" - dat += span_warning("Severity: [virus.severity]") + dat += span_alert(span_bold("Warning: [virus.form] detected in subject's blood.")) dat += "
" if (M.getCloneLoss()) dat += span_warning("Subject appears to have been imperfectly cloned.") diff --git a/code/game/objects/items/weapons/storage/belt.dm b/code/game/objects/items/weapons/storage/belt.dm index 4107e1f25eb..8a59f61e261 100644 --- a/code/game/objects/items/weapons/storage/belt.dm +++ b/code/game/objects/items/weapons/storage/belt.dm @@ -247,7 +247,8 @@ /obj/item/sleevemate, /obj/item/mass_spectrometer, /obj/item/surgical, - /obj/item/clothing/mask/chewable/candy/lolli // VOREstation edit end + /obj/item/clothing/mask/chewable/candy/lolli, + /obj/item/extrapolator ) /obj/item/storage/belt/medical/emt diff --git a/code/game/objects/structures/holosign.dm b/code/game/objects/structures/holosign.dm index 8c006c0c690..de1fa84390b 100644 --- a/code/game/objects/structures/holosign.dm +++ b/code/game/objects/structures/holosign.dm @@ -98,11 +98,11 @@ addtimer(VARSET_CALLBACK(src, icon_state, "holo_medical"), 10 SECONDS, TIMER_DELETE_ME) /obj/structure/holosign/barrier/medical/proc/CheckHuman(mob/living/carbon/human/H) - if(istype(H.get_species(), SPECIES_XENOCHIMERA)) + if(H.get_species() == SPECIES_XENOCHIMERA) return FALSE if(H.GetViruses()) - for(var/datum/disease/D in H.GetViruses()) - if(D.severity == NONTHREAT) + for(var/datum/disease/D in H.GetSpreadableViruses()) + if(D.danger == DISEASE_POSITIVE || D.danger == DISEASE_BENEFICIAL) continue return FALSE return TRUE diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index f03c77770a5..653e2808b69 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -202,8 +202,8 @@ B.blood_DNA = list() if(!B.blood_DNA[M.dna.unique_enzymes]) B.blood_DNA[M.dna.unique_enzymes] = M.dna.b_type - if(M.viruses) - B.viruses = M.viruses.Copy() + if(M.IsInfected()) + B.viruses = M.GetViruses() return 1 //we bloodied the floor blood_splatter(src,M.get_blood(M.vessel),1) return 1 //we bloodied the floor diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm index 7ac75745c16..9b552ee4534 100644 --- a/code/game/turfs/turf.dm +++ b/code/game/turfs/turf.dm @@ -466,3 +466,32 @@ // We were the the B-side in a turf translation /turf/proc/post_translate_B(var/turf/A) return + +/turf/proc/add_vomit_floor(mob/living/M, toxvomit = NONE, purge = TRUE) + + var/obj/effect/decal/cleanable/vomit/V = new /obj/effect/decal/cleanable/vomit(src, M.GetSpreadableViruses()) + + if (QDELETED(V)) + V = locate() in src + if(!V) + return + if(toxvomit == VOMIT_PURPLE) + V.icon_state = "vomitpurp_1" + V.random_icon_states = list("vomitpurp_1", "vomitpurp_2", "vomitpurp_3", "vomitpurp_4") + else if (toxvomit == VOMIT_TOXIC) + V.icon_state = "vomittox_1" + V.random_icon_states = list("vomittox_1", "vomittox_2", "vomittox_3", "vomittox_4") + else if (toxvomit == VOMIT_NANITE) + V.name = "metallic slurry" + V.desc = "A puddle of metallic slury that looks vaguely like very fine sand. It almost seems like it's moving..." + V.icon_state = "vomitnanite_1" + V.random_icon_states = list("vomitnanite_1", "vomitnanite_2", "vomitnanite_3", "vomitnanite_4") + if(purge && ishuman(M)) + var/mob/living/carbon/human/H = M + if(H.ingested) + clear_reagents_to_vomit_pool(H, V) + +/proc/clear_reagents_to_vomit_pool(mob/living/carbon/human/H, obj/effect/decal/cleanable/vomit/V) + H.ingested.trans_to(V, H.ingested.total_volume / 10) + for(var/datum/reagent/R in H.ingested.reagent_list) + H.ingested.remove_reagent(R, min(R.volume, 10)) diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm index ed08c1a0366..d4960ed9465 100644 --- a/code/modules/events/disease_outbreak.dm +++ b/code/modules/events/disease_outbreak.dm @@ -27,7 +27,7 @@ GLOBAL_LIST_EMPTY(current_pending_diseases) else chosen_disease = create_virus(severity * 2) - chosen_disease.carrier = TRUE + chosen_disease.virus_modifiers |= CARRIER /datum/event/disease_outbreak/start() GLOB.current_pending_diseases += chosen_disease @@ -57,6 +57,9 @@ GLOBAL_LIST_EMPTY(current_pending_diseases) candidates -= H chosen_infect-- + if(!GLOB.archive_diseases[chosen_disease.GetDiseaseID()]) + GLOB.archive_diseases[chosen_disease.GetDiseaseID()] = chosen_disease + //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 @@ -82,14 +85,14 @@ GLOBAL_LIST_EMPTY(current_pending_diseases) var/datum/disease/CD = new candidate if(CD.disease_flags & CAN_NOT_POPULATE) continue - switch(CD.severity) - if(NONTHREAT, MINOR) + switch(CD.danger) + if(DISEASE_NONTHREAT, DISEASE_MINOR) diseases_minor += candidate - if(MEDIUM, HARMFUL, DANGEROUS, BIOHAZARD) + if(DISEASE_MEDIUM, DISEASE_HARMFUL, DISEASE_DANGEROUS, DISEASE_BIOHAZARD) diseases_moderate_major += candidate /datum/event/disease_outbreak/proc/populate_symptoms() for(var/candidate in subtypesof(/datum/symptom)) var/datum/symptom/CS = candidate - if(initial(CS.transmittable) > 1) + if(initial(CS.transmission) > 1) transmissable_symptoms += candidate diff --git a/code/modules/mob/holder.dm b/code/modules/mob/holder.dm index bd9f2d52ad3..2e44c0a89fa 100644 --- a/code/modules/mob/holder.dm +++ b/code/modules/mob/holder.dm @@ -155,6 +155,10 @@ var/list/holder_mob_icon_cache = list() if(istype(I)) I.on_holder_escape(src) +/obj/item/holder/extrapolator_act(mob/living/user, obj/item/extrapolator/extrapolator, dry_run) + . = ..() + EXTRAPOLATOR_ACT_ADD_DISEASES(., held_mob.GetViruses()) + //Mob specific holders. /obj/item/holder/diona origin_tech = list(TECH_MAGNET = 3, TECH_BIO = 5) @@ -184,6 +188,11 @@ var/list/holder_mob_icon_cache = list() origin_tech = list(TECH_BIO = 2) w_class = ITEMSIZE_TINY +/obj/item/holder/mouse/extrapolator_act(mob/living/user, obj/item/extrapolator/extrapolator, dry_run) + . = ..() + var/mob/living/simple_mob/animal/passive/mouse/M = held_mob + EXTRAPOLATOR_ACT_ADD_DISEASES(., M.rat_diseases) + /obj/item/holder/mouse/white item_state = "mouse_white" diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm index c96997c24ef..d6a220b2e28 100644 --- a/code/modules/mob/living/carbon/carbon.dm +++ b/code/modules/mob/living/carbon/carbon.dm @@ -371,7 +371,7 @@ if(istype(A, /mob/living/carbon) && prob(10)) var/mob/living/carbon/human/H = A for(var/datum/disease/D in GetViruses()) - if(D.spread_flags & CONTACT_GENERAL) + if(D.spread_flags & DISEASE_SPREAD_CONTACT) H.ContractDisease(D) /mob/living/carbon/cannot_use_vents() @@ -523,5 +523,5 @@ if(prob(D.infectivity)) D.spread() - if(stat != DEAD || D.allow_dead) + if(stat != DEAD || global_flag_check(D.virus_modifiers, SPREAD_DEAD)) D.stage_act() diff --git a/code/modules/mob/living/carbon/human/human_attackhand.dm b/code/modules/mob/living/carbon/human/human_attackhand.dm index b9b0f0d6fdf..f6c4b30f884 100644 --- a/code/modules/mob/living/carbon/human/human_attackhand.dm +++ b/code/modules/mob/living/carbon/human/human_attackhand.dm @@ -76,7 +76,7 @@ if(istype(M,/mob/living/carbon) && has_hands) for(var/datum/disease/D in M.GetViruses()) - if(D.spread_flags & CONTACT_HANDS) + if(D.spread_flags & DISEASE_SPREAD_CONTACT) ContractDisease(D) switch(M.a_intent) diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index d1c0bb78735..43156ca83eb 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -692,3 +692,12 @@ emp_act flick(G.hud.icon_state, G.hud) return 1 + +/mob/living/carbon/human/is_mouth_covered(head_only, mask_only) + if(!check_has_mouth()) + return TRUE + + if((isobj(head) && head.body_parts_covered & FACE) || isobj(wear_mask) || (isobj(wear_suit) && wear_suit.body_parts_covered & FACE)) + return TRUE + + return FALSE diff --git a/code/modules/mob/living/carbon/human/life.dm b/code/modules/mob/living/carbon/human/life.dm index ebf09ec3271..4469c4101e7 100644 --- a/code/modules/mob/living/carbon/human/life.dm +++ b/code/modules/mob/living/carbon/human/life.dm @@ -542,7 +542,7 @@ if(head && (head.item_flags & BLOCK_GAS_SMOKE_EFFECT)) return ..() - +/* /mob/living/carbon/human/handle_post_breath(datum/gas_mixture/breath) ..() //spread some viruses while we are at it @@ -556,7 +556,7 @@ continue for(var/mob/living/carbon/M in view(1,src)) ContractDisease(D) - +*/ /mob/living/carbon/human/get_breath_from_internal(volume_needed=BREATH_VOLUME) if(internal) @@ -2296,9 +2296,9 @@ /mob/living/carbon/human/proc/has_virus() for(var/thing in viruses) var/datum/disease/D = thing - if(!D.discovered) + if(!global_flag_check(D.virus_modifiers, DISCOVERED)) continue - if((!(D.visibility_flags & HIDDEN_SCANNER)) && (D.severity != NONTHREAT)) + if((!(D.visibility_flags & HIDDEN_SCANNER)) && (D.danger != DISEASE_NONTHREAT)) return TRUE return FALSE diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index 08e3b8ac676..c495202ca66 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -911,65 +911,91 @@ if(deaf >= 0) ear_deaf = deaf -/mob/living/proc/vomit(var/skip_wait, var/blood_vomit) +/mob/living/proc/vomit(lost_nutrition = 10, blood = FALSE, stun = 5, distance = 1, message = TRUE, toxic = VOMIT_TOXIC, purge = FALSE) + if(!check_has_mouth()) - return + return TRUE - if(!lastpuke) - lastpuke = 1 - if(isSynthetic()) - to_chat(src, span_danger("A sudden, dizzying wave of internal feedback rushes over you!")) - src.Weaken(5) - else - if (nutrition <= 100) - to_chat(src, span_danger("You gag as you want to throw up, but there's nothing in your stomach!")) - src.Weaken(10) + if(nutrition < 100 && !blood) + if(message) + visible_message(span_warning("[src] dry heaves!"), span_userdanger("You try to throw up, but there's nothing in your stomach!")) + + if(stun) + Stun(stun) + return TRUE + + var/obj/vomit_goal = get_active_hand() + + if(!istype(vomit_goal, /obj/item/reagent_containers/glass/bucket)) + vomit_goal = check_vomit_goal() + + if(iscarbon(src) && is_mouth_covered()) + if(message) + visible_message(span_danger("[src] throws up all over themself!"), span_userdanger("You throw up all over yourself!")) + distance = 0 + else if(vomit_goal) + if(message) + visible_message(span_danger("[src] throws up into the [vomit_goal]!"), span_userdanger("You throw up into the [vomit_goal]!")) + if(istype(vomit_goal, /obj/item/reagent_containers/glass/bucket)) + var/obj/item/organ/internal/stomach/S = organs_by_name[O_STOMACH] + var/obj/item/reagent_containers/glass/bucket/puke_bucket = vomit_goal + if(S && S.acidtype) + puke_bucket.reagents.add_reagent(S.acidtype, rand(3, 6)) + else if(blood) + puke_bucket.reagents.add_reagent(REAGENT_BLOOD, rand(3, 6)) else - to_chat(src, span_warning("You feel nauseous...")) + puke_bucket.reagents.add_reagent(REAGENT_ID_TOXIN, rand(3, 6)) + distance = 0 + else + if(message) + visible_message(span_danger("[src] throws up!"), span_userdanger("You throw up!")) - if(ishuman(src)) - var/mob/living/carbon/human/Hu = src - if(CE_ANTACID in Hu.chem_effects) - if(prob(min(90, Hu.chem_effects[CE_ANTACID] * 15))) - spawn(rand(30 SECONDS, 2 MINUTES)) - lastpuke = FALSE - return + if(stun) + Stun(stun) - spawn() - if(!skip_wait) - sleep(150) //15 seconds until second warning - to_chat(src, span_warning("You feel like you are about to throw up!")) - sleep(100) //and you have 10 more for mad dash to the bucket + playsound(get_turf(src), 'sound/effects/splat.ogg', 50, 1) + var/turf/T = get_turf(src) + var/vomit_type = NONE + var/mob/living/carbon/human/H = src - //Damaged livers cause you to vomit blood. - if(!blood_vomit) - if(ishuman(src)) - var/mob/living/carbon/human/H = src - if(!H.isSynthetic()) - var/obj/item/organ/internal/liver/L = H.internal_organs_by_name[O_LIVER] - if(!L || L.is_broken()) - blood_vomit = 1 + if(isSynthetic()) + vomit_type = VOMIT_NANITE + else if(ishuman(src) && H.ingested.has_reagent(REAGENT_ID_PHORON) && !isSynthetic()) + vomit_type = VOMIT_PURPLE + else if(toxloss && !isSynthetic()) + vomit_type = VOMIT_TOXIC - Stun(5) - src.visible_message(span_warning("[src] throws up!"),span_warning("You throw up!")) - playsound(src, 'sound/effects/splat.ogg', 50, 1) + if(!blood) + adjust_nutrition(-lost_nutrition) + adjustToxLoss(-3) - var/turf/simulated/T = get_turf(src) //TODO: Make add_blood_floor remove blood from human mobs - if(istype(T)) - if(blood_vomit) - T.add_blood_floor(src) - else - T.add_vomit_floor(src, 1) + if(distance) + for(var/i=0 to distance) + if(blood) + if(T) + blood_splatter(T, large = TRUE) + if(stun) + adjustBruteLoss(2) + else if(T) + T.add_vomit_floor(src, vomit_type, purge) + T = get_step(T, dir) + return TRUE - if(blood_vomit) - if(getBruteLoss() < 50) - adjustBruteLoss(3) - else - adjust_nutrition(-40) - adjustToxLoss(-3) - - spawn(350) - lastpuke = 0 +/mob/living/proc/check_vomit_goal() + PRIVATE_PROC(TRUE) + var/obj_list = list(/obj/machinery/disposal,/obj/structure/toilet,/obj/structure/sink,/obj/structure/urinal) + for(var/type in obj_list) + // check standing on + var/turf/T = get_turf(src) + var/obj/O = locate(type) in T + if(O) + return O + // check ahead of us + T = get_turf(get_step(T,dir)) + O = locate(type) in T + if(O && O.Adjacent(src)) + return O + return null /mob/living/update_canmove() if(!resting && cannot_stand() && can_stand_overridden()) diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm index 97e12bb821d..d7124c9371c 100644 --- a/code/modules/mob/living/living_defense.dm +++ b/code/modules/mob/living/living_defense.dm @@ -616,3 +616,10 @@ /mob/living/proc/restore_all_organs() return + +/mob/living/proc/is_mouth_covered(head_only = FALSE, mask_only = FALSE) + return FALSE + +/mob/living/extrapolator_act(mob/living/user, obj/item/extrapolator/extrapolator, dry_run) + . = ..() + EXTRAPOLATOR_ACT_ADD_DISEASES(., viruses) diff --git a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm index 534be53c10c..57db88f53dc 100644 --- a/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm +++ b/code/modules/mob/living/simple_mob/subtypes/animal/passive/mouse.dm @@ -44,6 +44,8 @@ var/body_color //brown, gray, white and black, leave blank for random + var/list/datum/disease/rat_diseases + /mob/living/simple_mob/animal/passive/mouse/Initialize(mapload, keep_parent_data) . = ..() @@ -75,6 +77,14 @@ if (body_color == "black") holder_type = /obj/item/holder/mouse/black + if(prob(25)) + LAZYINITLIST(rat_diseases) + rat_diseases += new /datum/disease/advance/random(rand(1, 5), 9, 1) + +/mob/living/simple_mob/animal/passive/mouse/extrapolator_act(mob/living/user, obj/item/extrapolator/extrapolator, dry_run = FALSE) + . = ..() + EXTRAPOLATOR_ACT_ADD_DISEASES(., rat_diseases) + /mob/living/simple_mob/animal/passive/mouse/Crossed(atom/movable/AM as mob|obj) if(AM.is_incorporeal()) return @@ -190,3 +200,20 @@ speak = list("Squeek!","SQUEEK!","Squeek?") emote_hear = list("squeeks","squeaks","squiks") emote_see = list("runs in a circle", "shakes", "scritches at something") + +/mob/living/simple_mob/animal/passive/mouse/white/virology + name = "Fleming" + desc = "A small white rodent, often found in Virology. This one isn't quite the nuisance!" + +/mob/living/simple_mob/animal/passive/mouse/white/virology/Initialize(mapload) + ..() + name = initial(name) + desc = initial(desc) + rat_diseases += new /datum/disease/advance/random(2, 2, 1) + +/mob/living/simple_mob/animal/passive/mouse/white/virology/Crossed(atom/movable/AM) + . = ..() + + if(isliving(AM) && !isnull(rat_diseases) && prob(20)) + var/mob/living/L = AM + L.ContractDisease(pick(rat_diseases), BP_R_FOOT) diff --git a/code/modules/mob/living/simple_mob/subtypes/vore/macrophage.dm b/code/modules/mob/living/simple_mob/subtypes/vore/macrophage.dm index 21b252b23df..16c6171e312 100644 --- a/code/modules/mob/living/simple_mob/subtypes/vore/macrophage.dm +++ b/code/modules/mob/living/simple_mob/subtypes/vore/macrophage.dm @@ -56,7 +56,7 @@ name = "Giant Germ" desc = "An incredibly huge virus!" - size_multiplier = 1.5 + size_multiplier = 1.75 maxHealth = 40 health = 40 @@ -66,16 +66,27 @@ /mob/living/simple_mob/vore/aggressive/macrophage/Initialize(mapload) . = ..() var/datum/disease/advance/random/macrophage/D = new - health += D.totalResistance() - maxHealth += D.totalResistance() - melee_damage_lower += max(0, D.totalResistance()) - melee_damage_upper += max(0, D.totalResistance()) + health += D.resistance + maxHealth += D.resistance + melee_damage_lower += max(0, D.resistance) + melee_damage_upper += max(0, D.resistance) infections += D base_disease = D +/mob/living/simple_mob/vore/aggressive/macrophage/extrapolator_act(mob/living/user, obj/item/extrapolator/extrapolator, dry_run = FALSE) + . = ..() + EXTRAPOLATOR_ACT_ADD_DISEASES(., base_disease) + // Still no idea why extrapolator == src, but I'll leave this for later if I find out. + // if(!dry_run && !EXTRAPOLATOR_ACT_CHECK(., EXTRAPOLATOR_ACT_PRIORITY_SPECIAL) && extrapolator.create_culture(user, base_disease)) + if(do_after(user, 2 SECONDS)) + user.visible_message(span_danger("[user] stabs [src] with [extrapolator], sucking it up!"), \ + span_danger("You stab [src] with [extrapolator]'s probe, destroying it!")) + death() + EXTRAPOLATOR_ACT_SET(., EXTRAPOLATOR_ACT_PRIORITY_SPECIAL) + /mob/living/simple_mob/vore/aggressive/macrophage/proc/deathcheck() if(locate(/mob/living/carbon/human) in vore_selected) - addtimer(CALLBACK(src, TYPE_PROC_REF(/mob/living/simple_mob/vore/aggressive/macrophage, deathcheck)), 3000) + addtimer(CALLBACK(src, TYPE_PROC_REF(/mob/living/simple_mob/vore/aggressive/macrophage, deathcheck)), 3 MINUTES) else death() @@ -88,13 +99,39 @@ /mob/living/simple_mob/vore/aggressive/macrophage/blue icon_state = "macrophage-4" -/mob/living/simple_mob/vore/aggressive/macrophage/do_attack(atom/A, turf/T) - . = ..() - if(iscarbon(A)) - var/mob/living/carbon/human/victim = A - if((victim.HasDisease(base_disease) || !victim.CanContractDisease(base_disease)) && prob(75)) - ai_holder.lose_target() - victim.ContractDisease(base_disease) +/mob/living/simple_mob/vore/aggressive/macrophage/apply_melee_effects(atom/A) + if(ishuman(A) && prob(25)) + var/mob/living/carbon/human/H = A + H.ContractDisease(base_disease) + +/mob/living/simple_mob/vore/aggressive/macrophage/do_special_attack(var/atom/A) + . = TRUE + set_AI_busy(TRUE) + do_windup_animation(A, 20) + addtimer(CALLBACK(src, PROC_REF(charge), A), 20, TIMER_STOPPABLE) + +/mob/living/simple_mob/vore/aggressive/macrophage/proc/charge(var/atom/A) + if(QDELETED(A) || !isturf(get_turf(A))) + set_AI_busy(FALSE) + return + status_flags |= LEAPING + flying = TRUE + hovering = TRUE + visible_message(span_warning("The [src] lunges at \the [A]!")) + throw_at(A, 7, 2) + if(status_flags & LEAPING) + status_flags &= ~LEAPING + flying = FALSE + hovering = FALSE + + var/mob/living/target = null + if(Adjacent(A)) + target = A + + if(ishuman(target)) + var/mob/living/carbon/human/H = target + H.ContractDisease(base_disease) + set_AI_busy(FALSE) /mob/living/simple_mob/vore/aggressive/macrophage/death() ..() @@ -106,6 +143,8 @@ pred.ForceContractDisease(base_disease) else var/obj/effect/decal/cleanable/mucus/sick = new(loc) + sick.pixel_x = rand(-24, 24) + sick.pixel_y = rand(-24, 24) sick.viruses += base_disease qdel(src) @@ -139,7 +178,7 @@ var/mob/living/simple_mob/vore/aggressive/macrophage/macrophage = holder for(var/list_target in our_targets) var/mob/living/victim = list_target - if(victim.viruses) + if(victim.IsInfected()) if(victim.HasDisease(macrophage.base_disease) && prob(75)) // Less likely to be a target if you're infected our_targets -= list_target continue diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm index 7a03e004fc1..e520bacdfe6 100644 --- a/code/modules/mob/mob_defines.dm +++ b/code/modules/mob/mob_defines.dm @@ -239,8 +239,8 @@ var/list/datum/action/actions - var/list/viruses - var/list/resistances + VAR_PROTECTED/list/viruses + VAR_PROTECTED/list/resistances var/custom_footstep = FOOTSTEP_MOB_SHOE VAR_PRIVATE/is_motion_tracking = FALSE // Prevent multiple unsubs and resubs, also used to check if the vis layer is enabled, use has_motiontracking() to get externally. diff --git a/code/modules/organs/blood.dm b/code/modules/organs/blood.dm index 2d36c22e549..3c366912c00 100644 --- a/code/modules/organs/blood.dm +++ b/code/modules/organs/blood.dm @@ -275,9 +275,7 @@ var/const/CE_STABLE_THRESHOLD = 0.5 if(!B.data["viruses"]) B.data["viruses"] = list() - for(var/datum/disease/D in GetViruses()) - if(D.spread_flags & SPECIAL) - continue + for(var/datum/disease/D in GetSpreadableViruses()) B.data["viruses"] |= D.Copy() if(!B.data["resistances"]) @@ -320,7 +318,7 @@ var/const/CE_STABLE_THRESHOLD = 0.5 var/list/sniffles = injected.data["viruses"] for(var/ID in sniffles) var/datum/disease/D = ID - if((D.spread_flags & SPECIAL) || (D.spread_flags & NON_CONTAGIOUS)) // You can't put non-contagius diseases in blood, but just in case + if(D.spread_flags & (DISEASE_SPREAD_SPECIAL | DISEASE_SPREAD_NON_CONTAGIOUS)) // You can't put non-contagius diseases in blood, but just in case continue ContractDisease(D) if (injected.data["resistances"] && prob(5)) diff --git a/code/modules/reagents/holder/holder.dm b/code/modules/reagents/holder/holder.dm index 47da1bc42b4..b2a2da51fe5 100644 --- a/code/modules/reagents/holder/holder.dm +++ b/code/modules/reagents/holder/holder.dm @@ -139,6 +139,7 @@ R.holder = src R.volume = amount R.initialize_data(data) + SetViruses(R,data) if(was_from_belly) R.from_belly = was_from_belly update_total() diff --git a/code/modules/reagents/reactions/instant/virology.dm b/code/modules/reagents/reactions/instant/virology.dm index 4b20bebcf4a..de517a456ce 100644 --- a/code/modules/reagents/reactions/instant/virology.dm +++ b/code/modules/reagents/reactions/instant/virology.dm @@ -134,7 +134,6 @@ id = "mixvirussize" required_reagents = list(REAGENT_ID_SIZEVIRUSFOOD = 1) symptoms = list( - /datum/symptom/macrophage, /datum/symptom/size, /datum/symptom/size/grow, /datum/symptom/size/shrink @@ -160,3 +159,16 @@ required_reagents = list(REAGENT_ID_VACCINE = 1) catalysts = list(REAGENT_ID_INAPROVALINE = 0.1) result_amount = 0.5 + +/decl/chemical_reaction/instant/neuter_virus + name = "Neuter Virus" + id = "neutervirus" + required_reagents = list(REAGENT_ID_IMMUNOSUPRIZINE = 1) + catalysts = list(REAGENT_ID_BLOOD = 1) + +/decl/chemical_reaction/instant/neuter_virus/on_reaction(var/datum/reagents/holder) + 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(D) + D.Neuter() diff --git a/code/modules/reagents/reagent_containers/_reagent_containers.dm b/code/modules/reagents/reagent_containers/_reagent_containers.dm index 9b19627cd8b..3b6e4948463 100644 --- a/code/modules/reagents/reagent_containers/_reagent_containers.dm +++ b/code/modules/reagents/reagent_containers/_reagent_containers.dm @@ -158,3 +158,9 @@ if(R.from_belly) return TRUE return FALSE + +/obj/item/reagent_containers/extrapolator_act(mob/living/user, obj/item/extrapolator/extrapolator, dry_run = FALSE) + . = ..() + EXTRAPOLATOR_ACT_SET(., EXTRAPOLATOR_ACT_PRIORITY_ISOLATE) + var/datum/reagent/blood/blood = reagents.get_reagent(REAGENT_ID_BLOOD) + EXTRAPOLATOR_ACT_ADD_DISEASES(., blood?.get_diseases()) diff --git a/code/modules/reagents/reagent_containers/syringes.dm b/code/modules/reagents/reagent_containers/syringes.dm index fe939643cc8..3fe91459cda 100644 --- a/code/modules/reagents/reagent_containers/syringes.dm +++ b/code/modules/reagents/reagent_containers/syringes.dm @@ -84,6 +84,10 @@ /obj/item/reagent_containers/syringe/attackby(obj/item/I as obj, mob/user as mob) return +/obj/item/reagent_containers/syringe/extrapolator_act(mob/living/user, obj/item/extrapolator/extrapolator, dry_run) + . = ..() + EXTRAPOLATOR_ACT_ADD_DISEASES(., viruses) + /obj/item/reagent_containers/syringe/afterattack(obj/target, mob/user, proximity) if(!proximity || !target.reagents) return @@ -407,9 +411,9 @@ targets |= hash //Grab any viruses they have - if(iscarbon(target) && LAZYLEN(target.viruses.len)) + if(iscarbon(target) && LAZYLEN(target.IsInfected())) LAZYINITLIST(viruses) - var/datum/disease/virus = pick(target.viruses.len) + var/datum/disease/virus = pick(target.IsInfected()) viruses[hash] = virus.Copy() //Dirtiness should be very low if you're the first injectee. If you're spam-injecting 4 people in a row around you though, diff --git a/code/modules/reagents/reagent_containers/virology.dm b/code/modules/reagents/reagent_containers/virology.dm index 0f8eef9cd98..ac5f00f0afd 100644 --- a/code/modules/reagents/reagent_containers/virology.dm +++ b/code/modules/reagents/reagent_containers/virology.dm @@ -1,65 +1,64 @@ -/obj/item/reagent_containers/glass/bottle/culture +/obj/item/reagent_containers/glass/beaker/vial/culture name = "virus culture" desc = "A bottle with a virus culture" - icon_state = "bottle-1" var/list/data = list("donor" = null, "viruses" = null, "blood_DNA" = null, "blood_type" = null, "resistances" = null, "trace_chems" = null) var/list/diseases = list() -/obj/item/reagent_containers/glass/bottle/culture/cold +/obj/item/reagent_containers/glass/beaker/vial/culture/cold name = "cold virus culture" desc = "A bottle with the common cold culture" -/obj/item/reagent_containers/glass/bottle/culture/cold/Initialize(mapload) +/obj/item/reagent_containers/glass/beaker/vial/culture/cold/Initialize(mapload) . = ..() diseases += new /datum/disease/advance/cold data["viruses"] = diseases reagents.add_reagent(REAGENT_ID_BLOOD, 10, data) -/obj/item/reagent_containers/glass/bottle/culture/flu +/obj/item/reagent_containers/glass/beaker/vial/culture/flu name = "flu virus culture" desc = "A bottle with the flu culture" -/obj/item/reagent_containers/glass/bottle/culture/flu/Initialize(mapload) +/obj/item/reagent_containers/glass/beaker/vial/culture/flu/Initialize(mapload) . = ..() diseases += new /datum/disease/advance/flu data["viruses"] = diseases reagents.add_reagent(REAGENT_ID_BLOOD, 10, data) -/obj/item/reagent_containers/glass/bottle/culture/blobspores +/obj/item/reagent_containers/glass/beaker/vial/culture/blobspores name = "blob spores culture" desc = "A bottle with blob spores" -/obj/item/reagent_containers/glass/bottle/culture/blobspores/Initialize(mapload) +/obj/item/reagent_containers/glass/beaker/vial/culture/blobspores/Initialize(mapload) . = ..() diseases += new /datum/disease/advance/blobspores data["viruses"] = diseases reagents.add_reagent(REAGENT_ID_BLOOD, 10, data) -/obj/item/reagent_containers/glass/bottle/culture/macrophages +/obj/item/reagent_containers/glass/beaker/vial/culture/macrophages name = "macrophages culture" desc = "A bottle with giant viruses" -/obj/item/reagent_containers/glass/bottle/culture/macrophages/Initialize(mapload) +/obj/item/reagent_containers/glass/beaker/vial/culture/macrophages/Initialize(mapload) . = ..() diseases += new /datum/disease/advance/macrophage data["viruses"] = diseases reagents.add_reagent(REAGENT_ID_BLOOD, 10, data) -/obj/item/reagent_containers/glass/bottle/culture/random_virus +/obj/item/reagent_containers/glass/beaker/vial/culture/random_virus name = "experimental disease culture bottle" desc = "A small bottle. Contains an untested viral culture." -/obj/item/reagent_containers/glass/bottle/culture/random_virus/Initialize(mapload) +/obj/item/reagent_containers/glass/beaker/vial/culture/random_virus/Initialize(mapload) . = ..() diseases += new /datum/disease/advance/random data["viruses"] = diseases reagents.add_reagent(REAGENT_ID_BLOOD, 10, data) -/obj/item/reagent_containers/glass/bottle/culture/random_virus/minor +/obj/item/reagent_containers/glass/beaker/vial/culture/random_virus/minor name = "minor experimental disease culture bottle" desc = "A small bottle. Contains a weak version of an untested viral culture." -/obj/item/reagent_containers/glass/bottle/culture/random_virus/minor/Initialize(mapload) +/obj/item/reagent_containers/glass/beaker/vial/culture/random_virus/minor/Initialize(mapload) . = ..() diseases += new /datum/disease/advance/random/minor data["viruses"] = diseases diff --git a/code/modules/reagents/reagents/_reagents.dm b/code/modules/reagents/reagents/_reagents.dm index 1ba640e95af..96fcb5f4608 100644 --- a/code/modules/reagents/reagents/_reagents.dm +++ b/code/modules/reagents/reagents/_reagents.dm @@ -220,19 +220,10 @@ /datum/reagent/Destroy() // This should only be called by the holder, so it's already handled clearing its references holder = null + if(islist(data)) + data.Cut() . = ..() -/* DEPRECATED - TODO: REMOVE EVERYWHERE */ - -/datum/reagent/proc/reaction_turf(var/turf/target) - touch_turf(target) - -/datum/reagent/proc/reaction_obj(var/obj/target) - touch_obj(target) - -/datum/reagent/proc/reaction_mob(var/mob/target) - touch_mob(target) - /// Called by [/datum/reagents/proc/conditional_update] /datum/reagent/proc/on_update(atom/A) return diff --git a/code/modules/reagents/reagents/core.dm b/code/modules/reagents/reagents/core.dm index a6b9c40f535..98001df9934 100644 --- a/code/modules/reagents/reagents/core.dm +++ b/code/modules/reagents/reagents/core.dm @@ -78,7 +78,7 @@ if(!ID) continue var/datum/disease/D = ID - if((D.spread_flags & SPECIAL) || (D.spread_flags & NON_CONTAGIOUS)) + if((D.spread_flags & DISEASE_SPREAD_SPECIAL) || (D.spread_flags & DISEASE_SPREAD_NON_CONTAGIOUS)) continue M.ContractDisease(D) @@ -95,11 +95,11 @@ if(vlist.len) for(var/ID in vlist) var/datum/disease/D = ID - if((D.spread_flags & SPECIAL) || (D.spread_flags & NON_CONTAGIOUS)) + if((D.spread_flags & DISEASE_SPREAD_SPECIAL) || (D.spread_flags & DISEASE_SPREAD_NON_CONTAGIOUS)) continue M.ContractDisease(D) if(data && data["resistances"]) - M.resistances |= data["resistances"] + M.AddResistances(data["resistances"]) /datum/reagent/blood/mix_data(newdata, newamount) if(!data || !newdata) @@ -168,6 +168,13 @@ M.inject_blood(src, volume * volume_mod) remove_self(volume) +/datum/reagent/blood/proc/get_diseases() + . = list() + if(data && data["viruses"]) + for(var/thing in data["viruses"]) + var/datum/disease/D = thing + . += D + /datum/reagent/blood/synthblood name = REAGENT_SYNTHBLOOD description = "Synthetic Blood" diff --git a/code/modules/reagents/reagents/virology.dm b/code/modules/reagents/reagents/virology.dm index dd133bce82e..a4f3471ff86 100644 --- a/code/modules/reagents/reagents/virology.dm +++ b/code/modules/reagents/reagents/virology.dm @@ -11,7 +11,7 @@ var/datum/disease/D = thing if(D.GetDiseaseID() in data) D.cure() - M.resistances |= data + M.AddResistances(data) /datum/reagent/vaccines/mix_data(newdata, newamount) if(islist(newdata)) @@ -30,10 +30,7 @@ color = "#41B0C0" taste_mult = 1.5 -/datum/reagent/medicine - name = REAGENT_DEVELOPER_WARNING // Unit test ignore - -/datum/reagent/medicine/adranol/adranolvirusfood +/datum/reagent/adranol/adranolvirusfood name = REAGENT_ADRANOLVIRUSFOOD id = REAGENT_ID_ADRANOLVIRUSFOOD description = "Mutates viruses when mixed in blood. This one seems rather weak." diff --git a/code/modules/surgery/_helpers.dm b/code/modules/surgery/_helpers.dm new file mode 100644 index 00000000000..746a13dd564 --- /dev/null +++ b/code/modules/surgery/_helpers.dm @@ -0,0 +1,11 @@ +/mob/living/proc/get_missing_limbs() + return list() + +/mob/living/carbon/human/get_missing_limbs() + RETURN_TYPE(/list) + var/list/full = BP_ALL + + for(var/zone in full) + if(get_bodypart_name(zone)) + full -= zone + return full diff --git a/code/modules/surgery/dismemberment.dm b/code/modules/surgery/dismemberment.dm new file mode 100644 index 00000000000..f1601a2e0b0 --- /dev/null +++ b/code/modules/surgery/dismemberment.dm @@ -0,0 +1,31 @@ +/mob/living/proc/regenerate_limbs(noheal = FALSE, list/excluded_zones = list()) + SEND_SIGNAL(src, COMSIG_LIVING_REGENERATE_LIMBS, noheal, excluded_zones) + +/mob/living/carbon/human/regenerate_limbs(noheal = FALSE, list/excluded_zones = list()) + var/list/zone_list = BP_ALL + if(length(excluded_zones)) + zone_list -= excluded_zones + for(var/organ in zone_list) + regenerate_limb(organ) + for(var/obj/item/organ/external/stump/stump in organs) + qdel(stump) + +/mob/living/proc/regenerate_limb(limb_zone, noheal) + return + +/mob/living/carbon/human/regenerate_limb(limb_zone, noheal) + var/obj/item/organ/external/bodypart + if(get_organ(limb_zone)) + bodypart = get_organ(limb_zone) + if(!bodypart.is_stump()) + return FALSE + bodypart.removed() + qdel(bodypart) + bodypart = null + var/list/organ_data = species.has_limbs[limb_zone] + var/limb_path = organ_data["path"] + var/obj/item/organ/O = new limb_path(src) + organ_data["descriptor"] = O.name + + update_icons_body() + return TRUE diff --git a/icons/effects/blood.dmi b/icons/effects/blood.dmi index 95e060cbd577cc3c7b5558b34bb770529cdd1c6e..2e6c98dad6b1dfa95201cfc7c9a769f9a23abe7f 100644 GIT binary patch delta 36626 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 OLAW`2I@a2sPX7nbwslqj delta 33109 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 G_x}T6)+4z9 diff --git a/icons/obj/device.dmi b/icons/obj/device.dmi index 423bfd14b8afa67dd4f314737e324d02f03b7d35..1ad93c12b61a1ca489743c060f6ae12f975db6c2 100644 GIT binary patch delta 10413 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 V%3aez?g$`!)KxSeR4H49{C^Mb;iv!r delta 9168 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 P_}3#f?FVHl=7IkY@%d+s diff --git a/maps/expedition_vr/beach/submaps/quarantineshuttle.dmm b/maps/expedition_vr/beach/submaps/quarantineshuttle.dmm index 8b7ac40dd50..bb118bcaaba 100644 --- a/maps/expedition_vr/beach/submaps/quarantineshuttle.dmm +++ b/maps/expedition_vr/beach/submaps/quarantineshuttle.dmm @@ -141,7 +141,7 @@ /area/submap/cave/qShuttle) "au" = ( /obj/structure/closet/crate/secure/loot, -/obj/item/reagent_containers/glass/bottle/culture/cold, +/obj/item/reagent_containers/glass/beaker/vial/culture/random_virus, /turf/simulated/shuttle/floor{ icon_state = "floor_yellow" }, @@ -332,13 +332,6 @@ icon_state = "floor_yellow" }, /area/submap/cave/qShuttle) -"aV" = ( -/obj/structure/closet/crate/secure/loot, -/obj/item/reagent_containers/glass/bottle/culture/flu, -/turf/simulated/shuttle/floor{ - icon_state = "floor_yellow" - }, -/area/submap/cave/qShuttle) "aW" = ( /obj/item/material/shard, /turf/simulated/shuttle/floor{ @@ -1362,7 +1355,7 @@ aa aa ab ad -aV +au aH aW bk diff --git a/maps/groundbase/gb-z2.dmm b/maps/groundbase/gb-z2.dmm index d43839f493b..91b9451800b 100644 --- a/maps/groundbase/gb-z2.dmm +++ b/maps/groundbase/gb-z2.dmm @@ -312,6 +312,11 @@ /area/groundbase/level2/nw) "aH" = ( /obj/structure/table/glass, +/obj/item/extrapolator, +/obj/item/extrapolator{ + pixel_x = -5; + pixel_y = 5 + }, /turf/simulated/floor/tiled/white, /area/medical/virology) "aI" = ( @@ -7028,12 +7033,7 @@ /obj/machinery/light{ dir = 1 }, -/obj/structure/closet/crate/medical, -/obj/item/reagent_containers/glass/bottle/culture/cold{ - pixel_x = -5; - pixel_y = 5 - }, -/obj/item/reagent_containers/glass/bottle/culture/flu, +/mob/living/simple_mob/animal/passive/mouse/white/virology, /turf/simulated/floor/tiled/white, /area/medical/virology) "tm" = ( diff --git a/maps/stellar_delight/stellar_delight1.dmm b/maps/stellar_delight/stellar_delight1.dmm index f8c97900d3e..4559b32d04b 100644 --- a/maps/stellar_delight/stellar_delight1.dmm +++ b/maps/stellar_delight/stellar_delight1.dmm @@ -8952,12 +8952,13 @@ dir = 8; pixel_x = -24 }, -/obj/structure/closet/crate/medical, -/obj/item/reagent_containers/glass/bottle/culture/cold{ +/mob/living/simple_mob/animal/passive/mouse/white/virology, +/obj/structure/table/glass, +/obj/item/extrapolator{ pixel_x = -5; pixel_y = 5 }, -/obj/item/reagent_containers/glass/bottle/culture/flu, +/obj/item/extrapolator, /turf/simulated/floor/tiled/eris/white/bluecorner, /area/medical/virology) "sg" = ( diff --git a/maps/tether/tether-01-surface1.dmm b/maps/tether/tether-01-surface1.dmm index 43d8170d21a..dd569e2d382 100644 --- a/maps/tether/tether-01-surface1.dmm +++ b/maps/tether/tether-01-surface1.dmm @@ -34903,12 +34903,7 @@ /obj/effect/floor_decal/corner/lime/border{ dir = 1 }, -/obj/structure/closet/crate/freezer, -/obj/item/reagent_containers/glass/bottle/culture/cold{ - pixel_x = -5; - pixel_y = 5 - }, -/obj/item/reagent_containers/glass/bottle/culture/flu, +/mob/living/simple_mob/animal/passive/mouse/white/virology, /turf/simulated/floor/tiled/white, /area/medical/virology) "nMH" = ( @@ -37437,6 +37432,11 @@ dir = 1 }, /obj/structure/table/standard, +/obj/item/extrapolator{ + pixel_x = -5; + pixel_y = 5 + }, +/obj/item/extrapolator, /turf/simulated/floor/tiled/white, /area/medical/virology) "vBy" = ( diff --git a/tgui/packages/tgui/interfaces/PanDEMIC.tsx b/tgui/packages/tgui/interfaces/PanDEMIC.tsx deleted file mode 100644 index 1c4a582bb54..00000000000 --- a/tgui/packages/tgui/interfaces/PanDEMIC.tsx +++ /dev/null @@ -1,360 +0,0 @@ -import React from 'react'; -import { useBackend } from 'tgui/backend'; -import { Window } from 'tgui/layouts'; -import { - Button, - LabeledList, - NoticeBox, - Section, - Stack, - Table, - Tabs, -} from 'tgui-core/components'; - -type Data = { - beakerLoaded: boolean; - beakerContainsBlood: boolean; - beakerContainsVirus: boolean; - resistances: string[]; - selectedStrainIndex: number; - strains: Strain[]; - synthesisCooldown: boolean; -}; - -type Strain = { - commonName: string; - description: string; - diseaseAgent: string; - bloodDNA: string; - bloodType: string; - possibleTreatments: string; - transmissionRoute: string; - isAdvanced: boolean; - symptoms: Symptom[]; -}; - -type Symptom = { - name: string; - stealth: number; - resistance: number; - stageSpeed: number; - transmissibility: number; -}; - -export const PanDEMIC = () => { - const { data } = useBackend(); - const { - beakerLoaded, - beakerContainsBlood, - beakerContainsVirus, - resistances, - } = data; - - let emptyPlaceholder: React.JSX.Element | null = null; - if (!beakerLoaded) { - emptyPlaceholder = <>No container loaded.; - } else if (!beakerContainsBlood) { - emptyPlaceholder = <>No blood sample found in the loaded container.; - } else if (beakerContainsBlood && !beakerContainsVirus) { - emptyPlaceholder = <>No disease detected in provided blood sample.; - } - - return ( - - - - {emptyPlaceholder && !beakerContainsVirus ? ( -

} - > - {emptyPlaceholder} -
- ) : ( - - )} - {resistances.length > 0 && } - - - - ); -}; - -const CommonCultureActions = () => { - const { act, data } = useBackend(); - const { beakerLoaded } = data; - return ( - - - - - - act('destroy_eject_beaker')} - > - Destroy - - - - ); -}; - -const CultureInformationSection = () => { - const { act, data } = useBackend(); - const { selectedStrainIndex, strains, synthesisCooldown } = data; - - if (strains.length === 0) { - return ( -
}> - No disease detected in provided blood sample. -
- ); - } - - return ( - -
- - - - } - > - - - -
-
- ); -}; - -const StrainInformationSection = ({ - strains, - selectedStrainIndex, -}: { - strains: Strain[]; - selectedStrainIndex: number; -}) => { - const { act } = useBackend(); - const selectedStrain = strains[selectedStrainIndex - 1]; - - return ( - - - {strains.map((strain, index) => ( - act('switch_strain', { strain_index: index + 1 })} - > - {strain.commonName} - - ))} - - -
- {selectedStrain ? ( - - ) : ( - No strain information available. - )} -
- - {selectedStrain && selectedStrain.symptoms.length > 0 && ( - <> - - - - )} -
- ); -}; - -const StrainSymptomsSection = ({ strain }: { strain: Strain }) => { - const symptoms = strain.symptoms; - - return ( - -
- - - Name - Stealth - Resistance - Stage Speed - Transmissibility - - {symptoms.map((symptom, index) => ( - - {symptom.name} - {symptom.stealth} - {symptom.resistance} - {symptom.stageSpeed} - - {symptom.transmissibility} - - - ))} -
-
-
- ); -}; - -const TotalStats = ({ strain }: { strain: Strain }) => { - const symptoms = strain.symptoms; - - const TotalStats = symptoms.reduce( - (totals, symptom) => ({ - stealth: totals.stealth + symptom.stealth, - resistance: totals.resistance + symptom.resistance, - stageSpeed: totals.stageSpeed + symptom.stageSpeed, - transmissibility: totals.transmissibility + symptom.transmissibility, - }), - { stealth: 0, resistance: 0, stageSpeed: 0, transmissibility: 0 }, - ); - - return ( -
- - - Stealth - Resistance - Stage Speed - Transmissibility - - - {TotalStats.stealth} - {TotalStats.resistance} - {TotalStats.stageSpeed} - - {TotalStats.transmissibility} - - -
-
- ); -}; - -const StrainInformation = ({ - strain, - strainIndex, -}: { - strain: Strain; - strainIndex: number; -}) => { - const { act } = useBackend(); - const { - commonName, - description, - diseaseAgent, - bloodDNA, - bloodType, - possibleTreatments, - transmissionRoute, - isAdvanced, - } = strain; - - return ( - - - - {commonName ?? 'Unknown'} - {isAdvanced && ( - <> - - - - )} - - - {description && ( - {description} - )} - {diseaseAgent} - - {bloodDNA || 'Undetectable'} - - - {bloodType || 'Undetectable'} - - - {transmissionRoute || 'None'} - - - {possibleTreatments || 'None'} - - - ); -}; - -const ResistancesSection = () => { - const { act, data } = useBackend(); - const { resistances, synthesisCooldown } = data; - const vaccineIcons = ['flask', 'vial', 'eye-dropper']; - - return ( - -
- - {resistances.map((resistance, index) => ( - -
-
- ); -}; diff --git a/tgui/packages/tgui/interfaces/Pandemic/Beaker.tsx b/tgui/packages/tgui/interfaces/Pandemic/Beaker.tsx new file mode 100644 index 00000000000..09eec322fd6 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Pandemic/Beaker.tsx @@ -0,0 +1,154 @@ +import { useBackend } from 'tgui/backend'; +import { + Button, + LabeledList, + NoticeBox, + ProgressBar, + Section, + Stack, +} from 'tgui-core/components'; +import { capitalizeFirst } from 'tgui-core/string'; + +import type { Data } from './types'; + +export const BeakerDisplay = (props) => { + const { act, data } = useBackend(); + const { has_beaker, beaker, has_blood } = data; + const cant_empty = !has_beaker || !beaker?.volume; + let content; + + if (!has_beaker) { + content = No container loaded.; + } else if (!beaker?.volume) { + content = Container is empty.; + } else if (!has_blood) { + content = ( + No blood sample found in the loaded container. + ); + } else { + content = ( + + + + + + + + + ); + } + + return ( +
+ + + + + + + + + + + } + > + {content} +
+ ); +}; + +const Info = (props) => { + const { data } = useBackend(); + const { beaker, blood } = data; + if (!beaker || !blood) { + return No container loaded; + } + + return ( + + + + + {capitalizeFirst(blood.dna)} + + + {capitalizeFirst(blood.type)} + + + + + + + + + + + + ); +}; + +const Antibodies = (props) => { + const { act, data } = useBackend(); + const { is_ready, resistances = [] } = data; + if (!resistances) { + return Nothing detected.; + } + + return ( + + + {!resistances.length + ? 'None' + : resistances.map((resistance) => { + return ( + + ); + })} + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/Pandemic/Specimen.tsx b/tgui/packages/tgui/interfaces/Pandemic/Specimen.tsx new file mode 100644 index 00000000000..5ca3bec407e --- /dev/null +++ b/tgui/packages/tgui/interfaces/Pandemic/Specimen.tsx @@ -0,0 +1,84 @@ +import { useState } from 'react'; +import { useBackend } from 'tgui/backend'; +import { Button, NoticeBox, Section, Stack, Tabs } from 'tgui-core/components'; + +import { SymptomDisplay } from './Symptom'; +import type { Data } from './types'; +import { VirusDisplay } from './Virus'; + +export const SpecimenDisplay = (props) => { + const { act, data } = useBackend(); + const { is_ready, viruses = [] } = data; + + const [tab, setTab] = useState(0); + const virus = viruses[tab]; + + return ( +
+ {viruses.length > 1 && ( + + + {viruses.map((virus, index) => { + return ( + setTab(index)} + key={index} + > + {virus.name} + + ); + })} + + + )} + + + + + + + + } + > + {!virus ? ( + Nothing detected + ) : ( + + + + + + {virus?.symptoms && } + + + )} +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Pandemic/Symptom.tsx b/tgui/packages/tgui/interfaces/Pandemic/Symptom.tsx new file mode 100644 index 00000000000..eb9557c61d1 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Pandemic/Symptom.tsx @@ -0,0 +1,101 @@ +import { + Collapsible, + LabeledList, + NoticeBox, + Section, + Stack, + Tooltip, +} from 'tgui-core/components'; + +import { getColor } from './helpers'; +import type { Threshold } from './types'; + +export const SymptomDisplay = (props) => { + const { symptoms = [] } = props; + if (!symptoms?.length) { + return No symptoms detected.; + } + + return ( +
+ {symptoms.map((symptom) => { + const { name, desc, threshold_desc } = symptom; + return ( + + + + {desc} + + + + + + + + + ); + })} +
+ ); +}; + +const Thresholds = (props) => { + const { thresholds = [] } = props; + const convertedThresholds = Object.entries(thresholds); + + return ( +
+ {!convertedThresholds.length ? ( + None + ) : ( + + {convertedThresholds.map(([label, descr], index) => { + return ( + + {String(descr)} + + ); + })} + + )} +
+ ); +}; + +const Traits = (props) => { + const { + symptom: { level, resistance, stage_speed, stealth, transmission }, + } = props; + + return ( +
+ + + + {level} + + + + + {resistance} + + + + + {stage_speed} + + + + + {stealth} + + + + + {transmission} + + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Pandemic/Virus.tsx b/tgui/packages/tgui/interfaces/Pandemic/Virus.tsx new file mode 100644 index 00000000000..f722d4a0104 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Pandemic/Virus.tsx @@ -0,0 +1,103 @@ +import { useBackend } from 'tgui/backend'; +import { + Box, + Input, + LabeledList, + Section, + Stack, + Tooltip, +} from 'tgui-core/components'; +import { capitalizeFirst, decodeHtmlEntities } from 'tgui-core/string'; + +import { getColor } from './helpers'; +import type { Data } from './types'; + +export const VirusDisplay = (props) => { + const { virus } = props; + + return ( + + + + + {virus.is_adv && ( + <> + + + + + + )} + + ); +}; + +const Info = (props) => { + const { act } = useBackend(); + const { + virus: { agent, can_rename, cure, description, index, name, spread }, + } = props; + + return ( + + + {can_rename ? ( + + act('rename_disease', { + index: index, + name: value, + }) + } + /> + ) : ( + {decodeHtmlEntities(name)} + )} + + {description} + + {capitalizeFirst(agent)} + + {spread} + {cure} + + ); +}; + +const Traits = (props) => { + const { + virus: { resistance, stage_speed, stealth, transmission }, + } = props; + + return ( +
+ + + + {resistance} + + + + + {stage_speed} + + + + + {stealth} + + + + + {transmission} + + + +
+ ); +}; diff --git a/tgui/packages/tgui/interfaces/Pandemic/helpers.ts b/tgui/packages/tgui/interfaces/Pandemic/helpers.ts new file mode 100644 index 00000000000..d454eeee889 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Pandemic/helpers.ts @@ -0,0 +1,16 @@ +export function getColor(severity: number): string { + switch (true) { + case severity <= -10: + return 'blue'; + case severity <= -5: + return 'darkturquoise'; + case severity <= 0: + return 'green'; + case severity <= 7: + return 'yellow'; + case severity <= 13: + return 'orange'; + default: + return 'bad'; + } +} diff --git a/tgui/packages/tgui/interfaces/Pandemic/index.tsx b/tgui/packages/tgui/interfaces/Pandemic/index.tsx new file mode 100644 index 00000000000..70d131f19a4 --- /dev/null +++ b/tgui/packages/tgui/interfaces/Pandemic/index.tsx @@ -0,0 +1,29 @@ +import { useBackend } from 'tgui/backend'; +import { Window } from 'tgui/layouts'; +import { Stack } from 'tgui-core/components'; + +import { BeakerDisplay } from './Beaker'; +import { SpecimenDisplay } from './Specimen'; +import type { Data } from './types'; + +export const Pandemic = (props) => { + const { data } = useBackend(); + const { has_beaker, has_blood } = data; + + return ( + + + + + + + {!!has_beaker && !!has_blood && ( + + + + )} + + + + ); +}; diff --git a/tgui/packages/tgui/interfaces/Pandemic/types.ts b/tgui/packages/tgui/interfaces/Pandemic/types.ts new file mode 100644 index 00000000000..e289a376e2e --- /dev/null +++ b/tgui/packages/tgui/interfaces/Pandemic/types.ts @@ -0,0 +1,59 @@ +import type { BooleanLike } from 'tgui-core/react'; + +export type Data = { + beaker?: Beaker; + blood?: Blood; + has_beaker: BooleanLike; + has_blood: BooleanLike; + is_ready: BooleanLike; + resistances: Resistance[]; + viruses?: Virus[]; +}; + +type Beaker = { + volume: number; + capacity: number; +}; + +type Blood = { + dna: string; + type: string; +}; + +type Resistance = { + id: string; + name: string; +}; + +type Virus = { + name: string; + can_rename: BooleanLike; + is_adv: BooleanLike; + symptoms?: Symptom[]; + resistance: number; + stealth: number; + stage_speed: number; + transmission: number; + index: number; + agent: string; + description: string; + spread: string; + cure: string; +}; + +type Symptom = { + name: string; + desc: string; + stealth: number; + resistance: number; + stage_speed: number; + transmission: number; + level: number; + neutered: BooleanLike; + treshod_desc: Threshold[]; +}; + +export type Threshold = { + label: string; + descr: string; +}; diff --git a/vorestation.dme b/vorestation.dme index 2cc1dfeaa53..c2960c4a7c4 100644 --- a/vorestation.dme +++ b/vorestation.dme @@ -519,11 +519,10 @@ #include "code\datums\diseases\advance\disease_preset.dm" #include "code\datums\diseases\advance\symptoms\blobspores.dm" #include "code\datums\diseases\advance\symptoms\choking.dm" +#include "code\datums\diseases\advance\symptoms\cockroach.dm" #include "code\datums\diseases\advance\symptoms\confusion.dm" #include "code\datums\diseases\advance\symptoms\cough.dm" -#include "code\datums\diseases\advance\symptoms\damage_converter.dm" #include "code\datums\diseases\advance\symptoms\deafness.dm" -#include "code\datums\diseases\advance\symptoms\dizzy.dm" #include "code\datums\diseases\advance\symptoms\fever.dm" #include "code\datums\diseases\advance\symptoms\fire.dm" #include "code\datums\diseases\advance\symptoms\flesh_eating.dm" @@ -532,28 +531,23 @@ #include "code\datums\diseases\advance\symptoms\hallucigen.dm" #include "code\datums\diseases\advance\symptoms\headache.dm" #include "code\datums\diseases\advance\symptoms\heal.dm" -#include "code\datums\diseases\advance\symptoms\hematophagy.dm" #include "code\datums\diseases\advance\symptoms\itching.dm" #include "code\datums\diseases\advance\symptoms\language.dm" +#include "code\datums\diseases\advance\symptoms\light.dm" #include "code\datums\diseases\advance\symptoms\macrophage.dm" #include "code\datums\diseases\advance\symptoms\mlem.dm" #include "code\datums\diseases\advance\symptoms\necrotic_agent.dm" -#include "code\datums\diseases\advance\symptoms\photosensitivity.dm" -#include "code\datums\diseases\advance\symptoms\pica.dm" -#include "code\datums\diseases\advance\symptoms\sensory.dm" +#include "code\datums\diseases\advance\symptoms\radiation.dm" #include "code\datums\diseases\advance\symptoms\shivering.dm" #include "code\datums\diseases\advance\symptoms\size.dm" #include "code\datums\diseases\advance\symptoms\sneeze.dm" -#include "code\datums\diseases\advance\symptoms\spin.dm" #include "code\datums\diseases\advance\symptoms\stimulant.dm" #include "code\datums\diseases\advance\symptoms\symptoms.dm" -#include "code\datums\diseases\advance\symptoms\synthetic_infection.dm" -#include "code\datums\diseases\advance\symptoms\telepathy.dm" #include "code\datums\diseases\advance\symptoms\viral.dm" #include "code\datums\diseases\advance\symptoms\vision.dm" #include "code\datums\diseases\advance\symptoms\vomit.dm" -#include "code\datums\diseases\advance\symptoms\weakness.dm" -#include "code\datums\diseases\advance\symptoms\weigh.dm" +#include "code\datums\diseases\advance\symptoms\weight.dm" +#include "code\datums\diseases\advance\symptoms\youth.dm" #include "code\datums\elements\_element.dm" #include "code\datums\elements\conflict_checking.dm" #include "code\datums\elements\footstep.dm" @@ -1374,6 +1368,7 @@ #include "code\game\objects\items\devices\defib.dm" #include "code\game\objects\items\devices\denecrotizer_vr.dm" #include "code\game\objects\items\devices\e_beacon.dm" +#include "code\game\objects\items\devices\extrapolator.dm" #include "code\game\objects\items\devices\flash.dm" #include "code\game\objects\items\devices\flash_vr.dm" #include "code\game\objects\items\devices\flashlight.dm" @@ -4225,7 +4220,9 @@ #include "code\modules\stockmarket\stockmarket.dm" #include "code\modules\stockmarket\stocks.dm" #include "code\modules\surgery\_defines.dm" +#include "code\modules\surgery\_helpers.dm" #include "code\modules\surgery\bones.dm" +#include "code\modules\surgery\dismemberment.dm" #include "code\modules\surgery\encased.dm" #include "code\modules\surgery\external_repair.dm" #include "code\modules\surgery\face.dm"