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