diff --git a/code/datums/diseases/_MobProcs.dm b/code/datums/diseases/_MobProcs.dm
index ae9b68b1..452b339a 100644
--- a/code/datums/diseases/_MobProcs.dm
+++ b/code/datums/diseases/_MobProcs.dm
@@ -20,7 +20,7 @@
if(!(D.infectable_biotypes & mob_biotypes))
return FALSE
- if(!(type in D.viable_mobtypes))
+ if(!D.viable_mobtypes[type])
return FALSE
return TRUE
@@ -144,4 +144,4 @@
return !is_mouth_covered()
/mob/living/carbon/CanSpreadAirborneDisease()
- return !((head && (head.flags_cover & HEADCOVERSMOUTH) && (head.armor.getRating("bio") >= 25)) || (wear_mask && (wear_mask.flags_cover & MASKCOVERSMOUTH) && (wear_mask.armor.getRating("bio") >= 25)))
\ No newline at end of file
+ return !((head && (head.flags_cover & HEADCOVERSMOUTH) && (head.armor.getRating("bio") >= 25)) || (wear_mask && (wear_mask.flags_cover & MASKCOVERSMOUTH) && (wear_mask.armor.getRating("bio") >= 25)))
diff --git a/code/datums/diseases/_disease.dm b/code/datums/diseases/_disease.dm
index 940b61c9..9145a536 100644
--- a/code/datums/diseases/_disease.dm
+++ b/code/datums/diseases/_disease.dm
@@ -18,7 +18,7 @@
var/stage_prob = 4
//Other
- var/list/viable_mobtypes = list() //typepaths of viable mobs
+ var/list/viable_mobtypes = list() //typecache of viable mobs
var/mob/living/carbon/affected_mob = null
var/list/cures = list() //list of cures if the disease has the CURABLE flag, these are reagent ids
var/infectivity = 65
@@ -34,6 +34,10 @@
var/process_dead = FALSE //if this ticks while the host is dead
var/copy_type = null //if this is null, copies will use the type of the instance being copied
+/datum/disease/New(make_typecache = TRUE)
+ if(make_typecache && length(viable_mobtypes))
+ viable_mobtypes = typecacheof(viable_mobtypes)
+
/datum/disease/Destroy()
. = ..()
if(affected_mob)
diff --git a/code/datums/diseases/advance/advance.dm b/code/datums/diseases/advance/advance.dm
index 0ecb8e74..2cd1c93f 100644
--- a/code/datums/diseases/advance/advance.dm
+++ b/code/datums/diseases/advance/advance.dm
@@ -1,492 +1,493 @@
-/*
-
- Advance Disease is a system for Virologist to Engineer their own disease with symptoms that have effects and properties
- which add onto the overall disease.
-
- If you need help with creating new symptoms or expanding the advance disease, ask for Giacom on #coderbus.
-
-*/
-
-
-
-
-/*
-
- PROPERTIES
-
- */
-
-/datum/disease/advance
- name = "Unknown" // We will always let our Virologist name our disease.
- desc = "An engineered disease which can contain a multitude of symptoms."
- form = "Advance Disease" // Will let med-scanners know that this disease was engineered.
- agent = "advance microbes"
- max_stages = 5
- spread_text = "Unknown"
- viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
-
- // NEW VARS
- var/list/properties = list()
- var/list/symptoms = list() // The symptoms of the disease.
- var/id = ""
- var/processing = FALSE
- var/mutable = TRUE //set to FALSE to prevent most in-game methods of altering the disease via virology
- var/oldres //To prevent setting new cures unless resistance changes.
-
- // The order goes from easy to cure to hard to cure. Keep in mind that sentient diseases pick two cures from tier 6 and up, ensure they wont react away in bodies.
- var/static/list/advance_cures = list(
- list( // level 1
- /datum/reagent/copper, /datum/reagent/silver, /datum/reagent/iodine, /datum/reagent/iron, /datum/reagent/carbon
- ),
- list( // level 2
- /datum/reagent/potassium, /datum/reagent/consumable/ethanol, /datum/reagent/lithium,
- /datum/reagent/silicon, /datum/reagent/bromine
- ),
- list( // level 3
- /datum/reagent/consumable/sodiumchloride, /datum/reagent/consumable/sugar, /datum/reagent/consumable/orangejuice,
- /datum/reagent/consumable/tomatojuice, /datum/reagent/consumable/milk
- ),
- list( //level 4
- /datum/reagent/medicine/spaceacillin, /datum/reagent/medicine/salglu_solution,
- /datum/reagent/medicine/epinephrine, /datum/reagent/medicine/charcoal
- ),
- list( //level 5
- /datum/reagent/oil, /datum/reagent/medicine/synaptizine, /datum/reagent/medicine/mannitol,
- /datum/reagent/drug/space_drugs, /datum/reagent/cryptobiolin
- ),
- list( // level 6
- /datum/reagent/phenol, /datum/reagent/medicine/inacusiate, /datum/reagent/medicine/oculine, /datum/reagent/medicine/antihol
- ),
- list( // level 7
- /datum/reagent/medicine/leporazine, /datum/reagent/toxin/mindbreaker, /datum/reagent/medicine/corazone
- ),
- list( // level 8
- /datum/reagent/pax, /datum/reagent/drug/happiness, /datum/reagent/medicine/ephedrine
- ),
- list( // level 9
- /datum/reagent/toxin/lipolicide, /datum/reagent/medicine/sal_acid
- ),
- list( // level 10
- /datum/reagent/medicine/haloperidol, /datum/reagent/drug/aranesp, /datum/reagent/medicine/diphenhydramine
- ),
- list( //level 11
- /datum/reagent/medicine/modafinil, /datum/reagent/toxin/anacea
- )
- )
-
-/*
-
- OLD PROCS
-
- */
-
-/datum/disease/advance/New()
- Refresh()
-
-/datum/disease/advance/Destroy()
- if(processing)
- for(var/datum/symptom/S in symptoms)
- S.End(src)
- return ..()
-
-/datum/disease/advance/try_infect(var/mob/living/infectee, make_copy = TRUE)
- //see if we are more transmittable than enough diseases to replace them
- //diseases replaced in this way do not confer immunity
- var/list/advance_diseases = list()
- for(var/datum/disease/advance/P in infectee.diseases)
- advance_diseases += P
- var/replace_num = advance_diseases.len + 1 - DISEASE_LIMIT //amount of diseases that need to be removed to fit this one
- if(replace_num > 0)
- sortTim(advance_diseases, /proc/cmp_advdisease_resistance_asc)
- for(var/i in 1 to replace_num)
- var/datum/disease/advance/competition = advance_diseases[i]
- if(totalTransmittable() > competition.totalResistance())
- competition.cure(FALSE)
- else
- return FALSE //we are not strong enough to bully our way in
- infect(infectee, make_copy)
- return TRUE
-
-// Randomly pick a symptom to activate.
-/datum/disease/advance/stage_act()
- ..()
- if(carrier)
- return
-
- if(symptoms && symptoms.len)
- if(!processing)
- processing = TRUE
- for(var/datum/symptom/S in symptoms)
- if(S.Start(src)) //this will return FALSE if the symptom is neutered
- S.next_activation = world.time + rand(S.symptom_delay_min * 10, S.symptom_delay_max * 10)
- S.on_stage_change(src)
-
- for(var/datum/symptom/S in symptoms)
- S.Activate(src)
-
-// Tell symptoms stage changed
-/datum/disease/advance/update_stage(new_stage)
- ..()
- for(var/datum/symptom/S in symptoms)
- S.on_stage_change(src)
-
-// Compares type then ID.
-/datum/disease/advance/IsSame(datum/disease/advance/D)
-
- if(!(istype(D, /datum/disease/advance)))
- return 0
-
- if(GetDiseaseID() != D.GetDiseaseID())
- return 0
- return 1
-
-// Returns the advance disease with a different reference memory.
-/datum/disease/advance/Copy()
- var/datum/disease/advance/A = ..()
- QDEL_LIST(A.symptoms)
- for(var/datum/symptom/S in symptoms)
- A.symptoms += S.Copy()
- A.properties = properties.Copy()
- A.id = id
- A.oldres = oldres
- //this is a new disease starting over at stage 1, so processing is not copied
- return A
-
-//Describe this disease to an admin in detail (for logging)
-/datum/disease/advance/admin_details()
- var/list/name_symptoms = list()
- for(var/datum/symptom/S in symptoms)
- name_symptoms += S.name
- return "[name] sym:[english_list(name_symptoms)] r:[totalResistance()] s:[totalStealth()] ss:[totalStageSpeed()] t:[totalTransmittable()]"
-/*
-
- NEW PROCS
-
- */
-
-// 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)
- for(var/datum/symptom/S in possible_symptoms)
- AddSymptom(S.Copy())
-
-/datum/disease/advance/proc/HasSymptom(datum/symptom/S)
- for(var/datum/symptom/symp in symptoms)
- if(symp.type == S.type)
- return 1
- return 0
-
-// Will generate new unique symptoms, use this if there are none. Returns a list of symptoms that were generated.
-/datum/disease/advance/proc/GenerateSymptoms(level_min, level_max, amount_get = 0)
-
- var/list/generated = list() // Symptoms we generated.
-
- // Generate symptoms. By default, we only choose non-deadly symptoms.
- var/list/possible_symptoms = list()
- for(var/symp in SSdisease.list_symptoms)
- var/datum/symptom/S = new symp
- if(S.naturally_occuring && S.level >= level_min && S.level <= level_max)
- if(!HasSymptom(S))
- possible_symptoms += S
-
- if(!possible_symptoms.len)
- return generated
-
- // Random chance to get more than one symptom
- var/number_of = amount_get
- if(!amount_get)
- number_of = 1
- while(prob(20))
- number_of += 1
-
- for(var/i = 1; number_of >= i && possible_symptoms.len; i++)
- generated += pick_n_take(possible_symptoms)
-
- return generated
-
-/datum/disease/advance/proc/Refresh(new_name = FALSE)
- GenerateProperties()
- AssignProperties()
- if(processing && symptoms && symptoms.len)
- for(var/datum/symptom/S in symptoms)
- S.Start(src)
- S.on_stage_change(src)
- id = null
-
- var/the_id = GetDiseaseID()
- if(!SSdisease.archive_diseases[the_id])
- SSdisease.archive_diseases[the_id] = src // So we don't infinite loop
- SSdisease.archive_diseases[the_id] = Copy()
- if(new_name)
- AssignName()
-
-//Generate disease properties based on the effects. Returns an associated list.
-/datum/disease/advance/proc/GenerateProperties()
- properties = list("resistance" = 0, "stealth" = 0, "stage_rate" = 0, "transmittable" = 0, "severity" = 0)
-
- 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
- if(!S.neutered)
- properties["severity"] = max(properties["severity"], S.severity) // severity is based on the highest severity non-neutered symptom
-
-// Assign the properties that are in the list.
-/datum/disease/advance/proc/AssignProperties()
-
- if(properties && properties.len)
- if(properties["stealth"] >= 2)
- visibility_flags |= HIDDEN_SCANNER
- else
- visibility_flags &= ~HIDDEN_SCANNER
-
- SetSpread(CLAMP(2 ** (properties["transmittable"] - symptoms.len), DISEASE_SPREAD_BLOOD, DISEASE_SPREAD_AIRBORNE))
-
- permeability_mod = max(CEILING(0.4 * properties["transmittable"], 1), 1)
- cure_chance = 15 - CLAMP(properties["resistance"], -5, 5) // can be between 10 and 20
- stage_prob = max(properties["stage_rate"], 2)
- SetSeverity(properties["severity"])
- GenerateCure(properties)
- else
- CRASH("Our properties were empty or null!")
-
-
-// Assign the spread type and give it the correct description.
-/datum/disease/advance/proc/SetSpread(spread_id)
- switch(spread_id)
- if(DISEASE_SPREAD_NON_CONTAGIOUS)
- spread_flags = DISEASE_SPREAD_NON_CONTAGIOUS
- spread_text = "None"
- if(DISEASE_SPREAD_SPECIAL)
- spread_flags = DISEASE_SPREAD_SPECIAL
- spread_text = "None"
- if(DISEASE_SPREAD_BLOOD)
- spread_flags = DISEASE_SPREAD_BLOOD
- spread_text = "Blood"
- if(DISEASE_SPREAD_CONTACT_FLUIDS)
- spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_FLUIDS
- spread_text = "Fluids"
- if(DISEASE_SPREAD_CONTACT_SKIN)
- spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_FLUIDS | DISEASE_SPREAD_CONTACT_SKIN
- spread_text = "On contact"
- if(DISEASE_SPREAD_AIRBORNE)
- spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_FLUIDS | DISEASE_SPREAD_CONTACT_SKIN | DISEASE_SPREAD_AIRBORNE
- spread_text = "Airborne"
-
-/datum/disease/advance/proc/SetSeverity(level_sev)
-
- switch(level_sev)
-
- if(-INFINITY to 0)
- severity = DISEASE_SEVERITY_POSITIVE
- if(1)
- severity = DISEASE_SEVERITY_NONTHREAT
- if(2)
- severity = DISEASE_SEVERITY_MINOR
- if(3)
- severity = DISEASE_SEVERITY_MEDIUM
- if(4)
- severity = DISEASE_SEVERITY_HARMFUL
- if(5)
- severity = DISEASE_SEVERITY_DANGEROUS
- if(6 to INFINITY)
- severity = DISEASE_SEVERITY_BIOHAZARD
- else
- severity = "Unknown"
-
-
-// Will generate a random cure, the less resistance the symptoms have, the harder the cure.
-/datum/disease/advance/proc/GenerateCure()
- if(properties && properties.len)
- var/res = CLAMP(properties["resistance"] - (symptoms.len / 2), 1, advance_cures.len)
- if(res == oldres)
- return
- cures = list(pick(advance_cures[res]))
- oldres = res
-
- // Get the cure name from the cure_id
- var/datum/reagent/D = GLOB.chemical_reagents_list[cures[1]]
- cure_text = D.name
-
-// Randomly generate a symptom, has a chance to lose or gain a symptom.
-/datum/disease/advance/proc/Evolve(min_level, max_level, ignore_mutable = FALSE)
- if(!mutable && !ignore_mutable)
- return
- var/s = safepick(GenerateSymptoms(min_level, max_level, 1))
- if(s)
- AddSymptom(s)
- Refresh(TRUE)
- return
-
-// Randomly remove a symptom.
-/datum/disease/advance/proc/Devolve(ignore_mutable = FALSE)
- if(!mutable && !ignore_mutable)
- return
- if(symptoms.len > 1)
- var/s = safepick(symptoms)
- if(s)
- RemoveSymptom(s)
- Refresh(TRUE)
-
-// Randomly neuter a symptom.
-/datum/disease/advance/proc/Neuter(ignore_mutable = FALSE)
- if(!mutable && !ignore_mutable)
- return
- if(symptoms.len)
- var/s = safepick(symptoms)
- if(s)
- NeuterSymptom(s)
- Refresh(TRUE)
-
-// Name the disease.
-/datum/disease/advance/proc/AssignName(name = "Unknown")
- Refresh()
- var/datum/disease/advance/A = SSdisease.archive_diseases[GetDiseaseID()]
- A.name = name
- for(var/datum/disease/advance/AD in SSdisease.active_diseases)
- AD.Refresh()
-
-// Return a unique ID of the disease.
-/datum/disease/advance/GetDiseaseID()
- if(!id)
- var/list/L = list()
- for(var/datum/symptom/S in symptoms)
- if(S.neutered)
- L += "[S.id]N"
- else
- L += S.id
- L = sortList(L) // Sort the list so it doesn't matter which order the symptoms are in.
- var/result = jointext(L, ":")
- id = result
- return id
-
-
-// Add a symptom, if it is over the limit we take a random symptom away and add the new one.
-/datum/disease/advance/proc/AddSymptom(datum/symptom/S)
-
- if(HasSymptom(S))
- return
-
- if(!(symptoms.len < (VIRUS_SYMPTOM_LIMIT - 1) + rand(-1, 1)))
- RemoveSymptom(pick(symptoms))
- symptoms += S
- S.OnAdd(src)
-
-// Simply removes the symptom.
-/datum/disease/advance/proc/RemoveSymptom(datum/symptom/S)
- symptoms -= S
- S.OnRemove(src)
-
-// Neuter a symptom, so it will only affect stats
-/datum/disease/advance/proc/NeuterSymptom(datum/symptom/S)
- if(!S.neutered)
- S.neutered = TRUE
- S.name += " (neutered)"
- S.OnRemove(src)
-
-/*
-
- Static Procs
-
-*/
-
-// Mix a list of advance diseases and return the mixed result.
-/proc/Advance_Mix(var/list/D_list)
- var/list/diseases = list()
-
- for(var/datum/disease/advance/A in D_list)
- diseases += A.Copy()
-
- if(!diseases.len)
- return null
- if(diseases.len <= 1)
- return pick(diseases) // Just return the only entry.
-
- var/i = 0
- // Mix our diseases until we are left with only one result.
- while(i < 20 && diseases.len > 1)
-
- i++
-
- var/datum/disease/advance/D1 = pick(diseases)
- diseases -= D1
-
- var/datum/disease/advance/D2 = pick(diseases)
- D2.Mix(D1)
-
- // Should be only 1 entry left, but if not let's only return a single entry
- var/datum/disease/advance/to_return = pick(diseases)
- to_return.Refresh(1)
- return to_return
-
-/proc/SetViruses(datum/reagent/R, list/data)
- if(data)
- var/list/preserve = list()
- if(istype(data) && data["viruses"])
- for(var/datum/disease/A in data["viruses"])
- preserve += A.Copy()
- R.data = data.Copy()
- if(preserve.len)
- R.data["viruses"] = preserve
-
-/proc/AdminCreateVirus(client/user)
-
- if(!user)
- return
-
- var/i = VIRUS_SYMPTOM_LIMIT
-
- var/datum/disease/advance/D = new()
- D.symptoms = list()
-
- var/list/symptoms = list()
- symptoms += "Done"
- symptoms += SSdisease.list_symptoms.Copy()
- do
- if(user)
- var/symptom = input(user, "Choose a symptom to add ([i] remaining)", "Choose a Symptom") in 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
- i -= 1
- while(i > 0)
-
- if(D.symptoms.len > 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/mob/living/carbon/human/H in shuffle(GLOB.alive_mob_list))
- if(!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.admin_details()]")
- log_virus("[key_name(user)] has triggered a custom virus outbreak of [D.admin_details()]!")
-
-
-/datum/disease/advance/proc/totalStageSpeed()
- return properties["stage_rate"]
-
-/datum/disease/advance/proc/totalStealth()
- return properties["stealth"]
-
-/datum/disease/advance/proc/totalResistance()
- return properties["resistance"]
-
-/datum/disease/advance/proc/totalTransmittable()
- return properties["transmittable"]
+/*
+
+ Advance Disease is a system for Virologist to Engineer their own disease with symptoms that have effects and properties
+ which add onto the overall disease.
+
+ If you need help with creating new symptoms or expanding the advance disease, ask for Giacom on #coderbus.
+
+*/
+
+
+
+
+/*
+
+ PROPERTIES
+
+ */
+
+/datum/disease/advance
+ name = "Unknown" // We will always let our Virologist name our disease.
+ desc = "An engineered disease which can contain a multitude of symptoms."
+ form = "Advance Disease" // Will let med-scanners know that this disease was engineered.
+ agent = "advance microbes"
+ max_stages = 5
+ spread_text = "Unknown"
+ viable_mobtypes = list(/mob/living/carbon/human, /mob/living/carbon/monkey)
+
+ // NEW VARS
+ var/list/properties = list()
+ var/list/symptoms = list() // The symptoms of the disease.
+ var/id = ""
+ var/processing = FALSE
+ var/mutable = TRUE //set to FALSE to prevent most in-game methods of altering the disease via virology
+ var/oldres //To prevent setting new cures unless resistance changes.
+
+ // The order goes from easy to cure to hard to cure. Keep in mind that sentient diseases pick two cures from tier 6 and up, ensure they wont react away in bodies.
+ var/static/list/advance_cures = list(
+ list( // level 1
+ /datum/reagent/copper, /datum/reagent/silver, /datum/reagent/iodine, /datum/reagent/iron, /datum/reagent/carbon
+ ),
+ list( // level 2
+ /datum/reagent/potassium, /datum/reagent/consumable/ethanol, /datum/reagent/lithium,
+ /datum/reagent/silicon, /datum/reagent/bromine
+ ),
+ list( // level 3
+ /datum/reagent/consumable/sodiumchloride, /datum/reagent/consumable/sugar, /datum/reagent/consumable/orangejuice,
+ /datum/reagent/consumable/tomatojuice, /datum/reagent/consumable/milk
+ ),
+ list( //level 4
+ /datum/reagent/medicine/spaceacillin, /datum/reagent/medicine/salglu_solution,
+ /datum/reagent/medicine/epinephrine, /datum/reagent/medicine/charcoal
+ ),
+ list( //level 5
+ /datum/reagent/oil, /datum/reagent/medicine/synaptizine, /datum/reagent/medicine/mannitol,
+ /datum/reagent/drug/space_drugs, /datum/reagent/cryptobiolin
+ ),
+ list( // level 6
+ /datum/reagent/phenol, /datum/reagent/medicine/inacusiate, /datum/reagent/medicine/oculine, /datum/reagent/medicine/antihol
+ ),
+ list( // level 7
+ /datum/reagent/medicine/leporazine, /datum/reagent/toxin/mindbreaker, /datum/reagent/medicine/corazone
+ ),
+ list( // level 8
+ /datum/reagent/pax, /datum/reagent/drug/happiness, /datum/reagent/medicine/ephedrine
+ ),
+ list( // level 9
+ /datum/reagent/toxin/lipolicide, /datum/reagent/medicine/sal_acid
+ ),
+ list( // level 10
+ /datum/reagent/medicine/haloperidol, /datum/reagent/drug/aranesp, /datum/reagent/medicine/diphenhydramine
+ ),
+ list( //level 11
+ /datum/reagent/medicine/modafinil, /datum/reagent/toxin/anacea
+ )
+ )
+
+/*
+
+ OLD PROCS
+
+ */
+
+/datum/disease/advance/New(make_typecache = TRUE)
+ ..()
+ Refresh()
+
+/datum/disease/advance/Destroy()
+ if(processing)
+ for(var/datum/symptom/S in symptoms)
+ S.End(src)
+ return ..()
+
+/datum/disease/advance/try_infect(var/mob/living/infectee, make_copy = TRUE)
+ //see if we are more transmittable than enough diseases to replace them
+ //diseases replaced in this way do not confer immunity
+ var/list/advance_diseases = list()
+ for(var/datum/disease/advance/P in infectee.diseases)
+ advance_diseases += P
+ var/replace_num = advance_diseases.len + 1 - DISEASE_LIMIT //amount of diseases that need to be removed to fit this one
+ if(replace_num > 0)
+ sortTim(advance_diseases, /proc/cmp_advdisease_resistance_asc)
+ for(var/i in 1 to replace_num)
+ var/datum/disease/advance/competition = advance_diseases[i]
+ if(totalTransmittable() > competition.totalResistance())
+ competition.cure(FALSE)
+ else
+ return FALSE //we are not strong enough to bully our way in
+ infect(infectee, make_copy)
+ return TRUE
+
+// Randomly pick a symptom to activate.
+/datum/disease/advance/stage_act()
+ ..()
+ if(carrier)
+ return
+
+ if(symptoms && symptoms.len)
+ if(!processing)
+ processing = TRUE
+ for(var/datum/symptom/S in symptoms)
+ if(S.Start(src)) //this will return FALSE if the symptom is neutered
+ S.next_activation = world.time + rand(S.symptom_delay_min * 10, S.symptom_delay_max * 10)
+ S.on_stage_change(src)
+
+ for(var/datum/symptom/S in symptoms)
+ S.Activate(src)
+
+// Tell symptoms stage changed
+/datum/disease/advance/update_stage(new_stage)
+ ..()
+ for(var/datum/symptom/S in symptoms)
+ S.on_stage_change(src)
+
+// Compares type then ID.
+/datum/disease/advance/IsSame(datum/disease/advance/D)
+
+ if(!(istype(D, /datum/disease/advance)))
+ return 0
+
+ if(GetDiseaseID() != D.GetDiseaseID())
+ return 0
+ return 1
+
+// Returns the advance disease with a different reference memory.
+/datum/disease/advance/Copy()
+ var/datum/disease/advance/A = ..()
+ QDEL_LIST(A.symptoms)
+ for(var/datum/symptom/S in symptoms)
+ A.symptoms += S.Copy()
+ A.properties = properties.Copy()
+ A.id = id
+ A.oldres = oldres
+ //this is a new disease starting over at stage 1, so processing is not copied
+ return A
+
+//Describe this disease to an admin in detail (for logging)
+/datum/disease/advance/admin_details()
+ var/list/name_symptoms = list()
+ for(var/datum/symptom/S in symptoms)
+ name_symptoms += S.name
+ return "[name] sym:[english_list(name_symptoms)] r:[totalResistance()] s:[totalStealth()] ss:[totalStageSpeed()] t:[totalTransmittable()]"
+/*
+
+ NEW PROCS
+
+ */
+
+// 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)
+ for(var/datum/symptom/S in possible_symptoms)
+ AddSymptom(S.Copy())
+
+/datum/disease/advance/proc/HasSymptom(datum/symptom/S)
+ for(var/datum/symptom/symp in symptoms)
+ if(symp.type == S.type)
+ return 1
+ return 0
+
+// Will generate new unique symptoms, use this if there are none. Returns a list of symptoms that were generated.
+/datum/disease/advance/proc/GenerateSymptoms(level_min, level_max, amount_get = 0)
+
+ var/list/generated = list() // Symptoms we generated.
+
+ // Generate symptoms. By default, we only choose non-deadly symptoms.
+ var/list/possible_symptoms = list()
+ for(var/symp in SSdisease.list_symptoms)
+ var/datum/symptom/S = new symp
+ if(S.naturally_occuring && S.level >= level_min && S.level <= level_max)
+ if(!HasSymptom(S))
+ possible_symptoms += S
+
+ if(!possible_symptoms.len)
+ return generated
+
+ // Random chance to get more than one symptom
+ var/number_of = amount_get
+ if(!amount_get)
+ number_of = 1
+ while(prob(20))
+ number_of += 1
+
+ for(var/i = 1; number_of >= i && possible_symptoms.len; i++)
+ generated += pick_n_take(possible_symptoms)
+
+ return generated
+
+/datum/disease/advance/proc/Refresh(new_name = FALSE)
+ GenerateProperties()
+ AssignProperties()
+ if(processing && symptoms && symptoms.len)
+ for(var/datum/symptom/S in symptoms)
+ S.Start(src)
+ S.on_stage_change(src)
+ id = null
+
+ var/the_id = GetDiseaseID()
+ if(!SSdisease.archive_diseases[the_id])
+ SSdisease.archive_diseases[the_id] = src // So we don't infinite loop
+ SSdisease.archive_diseases[the_id] = Copy()
+ if(new_name)
+ AssignName()
+
+//Generate disease properties based on the effects. Returns an associated list.
+/datum/disease/advance/proc/GenerateProperties()
+ properties = list("resistance" = 0, "stealth" = 0, "stage_rate" = 0, "transmittable" = 0, "severity" = 0)
+
+ 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
+ if(!S.neutered)
+ properties["severity"] = max(properties["severity"], S.severity) // severity is based on the highest severity non-neutered symptom
+
+// Assign the properties that are in the list.
+/datum/disease/advance/proc/AssignProperties()
+
+ if(properties && properties.len)
+ if(properties["stealth"] >= 2)
+ visibility_flags |= HIDDEN_SCANNER
+ else
+ visibility_flags &= ~HIDDEN_SCANNER
+
+ SetSpread(CLAMP(2 ** (properties["transmittable"] - symptoms.len), DISEASE_SPREAD_BLOOD, DISEASE_SPREAD_AIRBORNE))
+
+ permeability_mod = max(CEILING(0.4 * properties["transmittable"], 1), 1)
+ cure_chance = 15 - CLAMP(properties["resistance"], -5, 5) // can be between 10 and 20
+ stage_prob = max(properties["stage_rate"], 2)
+ SetSeverity(properties["severity"])
+ GenerateCure(properties)
+ else
+ CRASH("Our properties were empty or null!")
+
+
+// Assign the spread type and give it the correct description.
+/datum/disease/advance/proc/SetSpread(spread_id)
+ switch(spread_id)
+ if(DISEASE_SPREAD_NON_CONTAGIOUS)
+ spread_flags = DISEASE_SPREAD_NON_CONTAGIOUS
+ spread_text = "None"
+ if(DISEASE_SPREAD_SPECIAL)
+ spread_flags = DISEASE_SPREAD_SPECIAL
+ spread_text = "None"
+ if(DISEASE_SPREAD_BLOOD)
+ spread_flags = DISEASE_SPREAD_BLOOD
+ spread_text = "Blood"
+ if(DISEASE_SPREAD_CONTACT_FLUIDS)
+ spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_FLUIDS
+ spread_text = "Fluids"
+ if(DISEASE_SPREAD_CONTACT_SKIN)
+ spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_FLUIDS | DISEASE_SPREAD_CONTACT_SKIN
+ spread_text = "On contact"
+ if(DISEASE_SPREAD_AIRBORNE)
+ spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_FLUIDS | DISEASE_SPREAD_CONTACT_SKIN | DISEASE_SPREAD_AIRBORNE
+ spread_text = "Airborne"
+
+/datum/disease/advance/proc/SetSeverity(level_sev)
+
+ switch(level_sev)
+
+ if(-INFINITY to 0)
+ severity = DISEASE_SEVERITY_POSITIVE
+ if(1)
+ severity = DISEASE_SEVERITY_NONTHREAT
+ if(2)
+ severity = DISEASE_SEVERITY_MINOR
+ if(3)
+ severity = DISEASE_SEVERITY_MEDIUM
+ if(4)
+ severity = DISEASE_SEVERITY_HARMFUL
+ if(5)
+ severity = DISEASE_SEVERITY_DANGEROUS
+ if(6 to INFINITY)
+ severity = DISEASE_SEVERITY_BIOHAZARD
+ else
+ severity = "Unknown"
+
+
+// Will generate a random cure, the less resistance the symptoms have, the harder the cure.
+/datum/disease/advance/proc/GenerateCure()
+ if(properties && properties.len)
+ var/res = CLAMP(properties["resistance"] - (symptoms.len / 2), 1, advance_cures.len)
+ if(res == oldres)
+ return
+ cures = list(pick(advance_cures[res]))
+ oldres = res
+
+ // Get the cure name from the cure_id
+ var/datum/reagent/D = GLOB.chemical_reagents_list[cures[1]]
+ cure_text = D.name
+
+// Randomly generate a symptom, has a chance to lose or gain a symptom.
+/datum/disease/advance/proc/Evolve(min_level, max_level, ignore_mutable = FALSE)
+ if(!mutable && !ignore_mutable)
+ return
+ var/s = safepick(GenerateSymptoms(min_level, max_level, 1))
+ if(s)
+ AddSymptom(s)
+ Refresh(TRUE)
+ return
+
+// Randomly remove a symptom.
+/datum/disease/advance/proc/Devolve(ignore_mutable = FALSE)
+ if(!mutable && !ignore_mutable)
+ return
+ if(symptoms.len > 1)
+ var/s = safepick(symptoms)
+ if(s)
+ RemoveSymptom(s)
+ Refresh(TRUE)
+
+// Randomly neuter a symptom.
+/datum/disease/advance/proc/Neuter(ignore_mutable = FALSE)
+ if(!mutable && !ignore_mutable)
+ return
+ if(symptoms.len)
+ var/s = safepick(symptoms)
+ if(s)
+ NeuterSymptom(s)
+ Refresh(TRUE)
+
+// Name the disease.
+/datum/disease/advance/proc/AssignName(name = "Unknown")
+ Refresh()
+ var/datum/disease/advance/A = SSdisease.archive_diseases[GetDiseaseID()]
+ A.name = name
+ for(var/datum/disease/advance/AD in SSdisease.active_diseases)
+ AD.Refresh()
+
+// Return a unique ID of the disease.
+/datum/disease/advance/GetDiseaseID()
+ if(!id)
+ var/list/L = list()
+ for(var/datum/symptom/S in symptoms)
+ if(S.neutered)
+ L += "[S.id]N"
+ else
+ L += S.id
+ L = sortList(L) // Sort the list so it doesn't matter which order the symptoms are in.
+ var/result = jointext(L, ":")
+ id = result
+ return id
+
+
+// Add a symptom, if it is over the limit we take a random symptom away and add the new one.
+/datum/disease/advance/proc/AddSymptom(datum/symptom/S)
+
+ if(HasSymptom(S))
+ return
+
+ if(!(symptoms.len < (VIRUS_SYMPTOM_LIMIT - 1) + rand(-1, 1)))
+ RemoveSymptom(pick(symptoms))
+ symptoms += S
+ S.OnAdd(src)
+
+// Simply removes the symptom.
+/datum/disease/advance/proc/RemoveSymptom(datum/symptom/S)
+ symptoms -= S
+ S.OnRemove(src)
+
+// Neuter a symptom, so it will only affect stats
+/datum/disease/advance/proc/NeuterSymptom(datum/symptom/S)
+ if(!S.neutered)
+ S.neutered = TRUE
+ S.name += " (neutered)"
+ S.OnRemove(src)
+
+/*
+
+ Static Procs
+
+*/
+
+// Mix a list of advance diseases and return the mixed result.
+/proc/Advance_Mix(var/list/D_list)
+ var/list/diseases = list()
+
+ for(var/datum/disease/advance/A in D_list)
+ diseases += A.Copy()
+
+ if(!diseases.len)
+ return null
+ if(diseases.len <= 1)
+ return pick(diseases) // Just return the only entry.
+
+ var/i = 0
+ // Mix our diseases until we are left with only one result.
+ while(i < 20 && diseases.len > 1)
+
+ i++
+
+ var/datum/disease/advance/D1 = pick(diseases)
+ diseases -= D1
+
+ var/datum/disease/advance/D2 = pick(diseases)
+ D2.Mix(D1)
+
+ // Should be only 1 entry left, but if not let's only return a single entry
+ var/datum/disease/advance/to_return = pick(diseases)
+ to_return.Refresh(1)
+ return to_return
+
+/proc/SetViruses(datum/reagent/R, list/data)
+ if(data)
+ var/list/preserve = list()
+ if(istype(data) && data["viruses"])
+ for(var/datum/disease/A in data["viruses"])
+ preserve += A.Copy()
+ R.data = data.Copy()
+ if(preserve.len)
+ R.data["viruses"] = preserve
+
+/proc/AdminCreateVirus(client/user)
+
+ if(!user)
+ return
+
+ var/i = VIRUS_SYMPTOM_LIMIT
+
+ var/datum/disease/advance/D = new()
+ D.symptoms = list()
+
+ var/list/symptoms = list()
+ symptoms += "Done"
+ symptoms += SSdisease.list_symptoms.Copy()
+ do
+ if(user)
+ var/symptom = input(user, "Choose a symptom to add ([i] remaining)", "Choose a Symptom") in 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
+ i -= 1
+ while(i > 0)
+
+ if(D.symptoms.len > 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/mob/living/carbon/human/H in shuffle(GLOB.alive_mob_list))
+ if(!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.admin_details()]")
+ log_virus("[key_name(user)] has triggered a custom virus outbreak of [D.admin_details()]!")
+
+
+/datum/disease/advance/proc/totalStageSpeed()
+ return properties["stage_rate"]
+
+/datum/disease/advance/proc/totalStealth()
+ return properties["stealth"]
+
+/datum/disease/advance/proc/totalResistance()
+ return properties["resistance"]
+
+/datum/disease/advance/proc/totalTransmittable()
+ return properties["transmittable"]
diff --git a/code/datums/diseases/advance/presets.dm b/code/datums/diseases/advance/presets.dm
index 1924d924..b12b6571 100644
--- a/code/datums/diseases/advance/presets.dm
+++ b/code/datums/diseases/advance/presets.dm
@@ -2,7 +2,7 @@
/datum/disease/advance/cold
copy_type = /datum/disease/advance
-/datum/disease/advance/cold/New()
+/datum/disease/advance/cold/New(make_typecache = TRUE)
name = "Cold"
symptoms = list(new/datum/symptom/sneeze)
..()
@@ -11,7 +11,7 @@
/datum/disease/advance/flu
copy_type = /datum/disease/advance
-/datum/disease/advance/flu/New()
+/datum/disease/advance/flu/New(make_typecache = TRUE)
name = "Flu"
symptoms = list(new/datum/symptom/cough)
..()
@@ -21,7 +21,7 @@
name = "Experimental Disease"
copy_type = /datum/disease/advance
-/datum/disease/advance/random/New(max_symptoms, max_level = 8)
+/datum/disease/advance/random/New(make_typecache = TRUE, max_symptoms, max_level = 8)
if(!max_symptoms)
max_symptoms = rand(1, VIRUS_SYMPTOM_LIMIT)
var/list/datum/symptom/possible_symptoms = list()
@@ -37,6 +37,6 @@
if(chosen_symptom)
var/datum/symptom/S = new chosen_symptom
symptoms += S
- Refresh()
name = "Sample #[rand(1,10000)]"
+ ..()
\ No newline at end of file
diff --git a/code/datums/diseases/retrovirus.dm b/code/datums/diseases/retrovirus.dm
index 5854249e..0e77fc09 100644
--- a/code/datums/diseases/retrovirus.dm
+++ b/code/datums/diseases/retrovirus.dm
@@ -1,84 +1,84 @@
-/datum/disease/dna_retrovirus
- name = "Retrovirus"
- max_stages = 4
- spread_text = "Contact"
- spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_SKIN | DISEASE_SPREAD_CONTACT_FLUIDS
- 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 = DISEASE_SEVERITY_HARMFUL
- permeability_mod = 0.4
- stage_prob = 2
- var/restcure = 0
-
-/datum/disease/dna_retrovirus/New()
- ..()
- agent = "Virus class [pick("A","B","C","D","E","F")][pick("A","B","C","D","E","F")]-[rand(50,300)]"
- if(prob(40))
- cures = list(/datum/reagent/medicine/mutadone)
- else
- restcure = 1
-
-/datum/disease/dna_retrovirus/Copy()
- var/datum/disease/dna_retrovirus/D = ..()
- D.restcure = restcure
- return D
-
-/datum/disease/dna_retrovirus/stage_act()
- ..()
- switch(stage)
- if(1)
- if(restcure)
- if(affected_mob.lying && prob(30))
- to_chat(affected_mob, "You feel better.")
- cure()
- return
- if (prob(8))
- to_chat(affected_mob, "Your head hurts.")
- if (prob(9))
- to_chat(affected_mob, "You feel a tingling sensation in your chest.")
- if (prob(9))
- to_chat(affected_mob, "You feel angry.")
- if(2)
- if(restcure)
- if(affected_mob.lying && prob(20))
- to_chat(affected_mob, "You feel better.")
- cure()
- return
- if (prob(8))
- to_chat(affected_mob, "Your skin feels loose.")
- if (prob(10))
- to_chat(affected_mob, "You feel very strange.")
- if (prob(4))
- to_chat(affected_mob, "You feel a stabbing pain in your head!")
- affected_mob.Unconscious(40)
- if (prob(4))
- to_chat(affected_mob, "Your stomach churns.")
- if(3)
- if(restcure)
- if(affected_mob.lying && prob(20))
- to_chat(affected_mob, "You feel better.")
- cure()
- return
- if (prob(10))
- to_chat(affected_mob, "Your entire body vibrates.")
-
- if (prob(35))
- if(prob(50))
- scramble_dna(affected_mob, 1, 0, rand(15,45))
- else
- scramble_dna(affected_mob, 0, 1, rand(15,45))
-
- if(4)
- if(restcure)
- if(affected_mob.lying && prob(5))
- to_chat(affected_mob, "You feel better.")
- cure()
- return
- if (prob(60))
- if(prob(50))
- scramble_dna(affected_mob, 1, 0, rand(50,75))
- else
- scramble_dna(affected_mob, 0, 1, rand(50,75))
\ No newline at end of file
+/datum/disease/dna_retrovirus
+ name = "Retrovirus"
+ max_stages = 4
+ spread_text = "Contact"
+ spread_flags = DISEASE_SPREAD_BLOOD | DISEASE_SPREAD_CONTACT_SKIN | DISEASE_SPREAD_CONTACT_FLUIDS
+ 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 = DISEASE_SEVERITY_HARMFUL
+ permeability_mod = 0.4
+ stage_prob = 2
+ var/restcure = 0
+
+/datum/disease/dna_retrovirus/New(make_typecache = TRUE)
+ ..()
+ agent = "Virus class [pick("A","B","C","D","E","F")][pick("A","B","C","D","E","F")]-[rand(50,300)]"
+ if(prob(40))
+ cures = list(/datum/reagent/medicine/mutadone)
+ else
+ restcure = 1
+
+/datum/disease/dna_retrovirus/Copy()
+ var/datum/disease/dna_retrovirus/D = ..()
+ D.restcure = restcure
+ return D
+
+/datum/disease/dna_retrovirus/stage_act()
+ ..()
+ switch(stage)
+ if(1)
+ if(restcure)
+ if(affected_mob.lying && prob(30))
+ to_chat(affected_mob, "You feel better.")
+ cure()
+ return
+ if (prob(8))
+ to_chat(affected_mob, "Your head hurts.")
+ if (prob(9))
+ to_chat(affected_mob, "You feel a tingling sensation in your chest.")
+ if (prob(9))
+ to_chat(affected_mob, "You feel angry.")
+ if(2)
+ if(restcure)
+ if(affected_mob.lying && prob(20))
+ to_chat(affected_mob, "You feel better.")
+ cure()
+ return
+ if (prob(8))
+ to_chat(affected_mob, "Your skin feels loose.")
+ if (prob(10))
+ to_chat(affected_mob, "You feel very strange.")
+ if (prob(4))
+ to_chat(affected_mob, "You feel a stabbing pain in your head!")
+ affected_mob.Unconscious(40)
+ if (prob(4))
+ to_chat(affected_mob, "Your stomach churns.")
+ if(3)
+ if(restcure)
+ if(affected_mob.lying && prob(20))
+ to_chat(affected_mob, "You feel better.")
+ cure()
+ return
+ if (prob(10))
+ to_chat(affected_mob, "Your entire body vibrates.")
+
+ if (prob(35))
+ if(prob(50))
+ scramble_dna(affected_mob, 1, 0, rand(15,45))
+ else
+ scramble_dna(affected_mob, 0, 1, rand(15,45))
+
+ if(4)
+ if(restcure)
+ if(affected_mob.lying && prob(5))
+ to_chat(affected_mob, "You feel better.")
+ cure()
+ return
+ if (prob(60))
+ if(prob(50))
+ scramble_dna(affected_mob, 1, 0, rand(50,75))
+ else
+ scramble_dna(affected_mob, 0, 1, rand(50,75))
diff --git a/code/game/machinery/computer/medical.dm b/code/game/machinery/computer/medical.dm
index a4fcb325..425ee5e2 100644
--- a/code/game/machinery/computer/medical.dm
+++ b/code/game/machinery/computer/medical.dm
@@ -143,7 +143,7 @@
if(5)
dat += "
Virus Database"
for(var/Dt in typesof(/datum/disease/))
- var/datum/disease/Dis = new Dt(0)
+ var/datum/disease/Dis = new Dt(FALSE)
if(istype(Dis, /datum/disease/advance))
continue // TODO (tm): Add advance diseases to the virus database which no one uses.
if(!Dis.desc)
@@ -246,7 +246,7 @@
else if(href_list["vir"])
var/type = href_list["vir"]
- var/datum/disease/Dis = new type(0)
+ var/datum/disease/Dis = new type(FALSE)
var/AfS = ""
for(var/mob/M in Dis.viable_mobtypes)
AfS += " [initial(M.name)];"
diff --git a/code/modules/events/disease_outbreak.dm b/code/modules/events/disease_outbreak.dm
index 2f220b17..737a395f 100644
--- a/code/modules/events/disease_outbreak.dm
+++ b/code/modules/events/disease_outbreak.dm
@@ -63,7 +63,7 @@
else
D = new virus_type()
else
- D = new /datum/disease/advance/random(max_severity, max_severity)
+ D = new /datum/disease/advance/random(TRUE, max_severity, max_severity)
D.carrier = TRUE
H.ForceContractDisease(D, FALSE, TRUE)
diff --git a/code/modules/mob/living/carbon/life.dm b/code/modules/mob/living/carbon/life.dm
index 037f7bad..44f21f10 100644
--- a/code/modules/mob/living/carbon/life.dm
+++ b/code/modules/mob/living/carbon/life.dm
@@ -1,803 +1,803 @@
-/mob/living/carbon/Life()
- set invisibility = 0
-
- //Hyper Change
- if(size_multiplier != previous_size)
- sleep(1)
- resize(size_multiplier)
-
- if(notransform)
- return
-
- if(damageoverlaytemp)
- damageoverlaytemp = 0
- update_damage_hud()
-
-
- if(!IsInStasis())
- //Reagent processing needs to come before breathing, to prevent edge cases.
- handle_organs()
-
- . = ..()
-
- if (QDELETED(src))
- return
-
- if(.) //not dead
- handle_blood()
-
- if(stat != DEAD)
- var/bprv = handle_bodyparts()
- if(bprv & BODYPART_LIFE_UPDATE_HEALTH)
- updatehealth()
- update_stamina()
-
- if(stat != DEAD)
- handle_brain_damage()
-/* Bug cause?
- if(stat != DEAD)
- handle_liver()
-*/
- if(stat == DEAD)
- stop_sound_channel(CHANNEL_HEARTBEAT)
- handle_death()
- rot()
-
- //Updates the number of stored chemicals for powers
- handle_changeling()
-
- if(stat != DEAD)
- return 1
-
-//Procs called while dead
-/mob/living/carbon/proc/handle_death()
- for(var/datum/reagent/R in reagents.reagent_list)
- if(R.chemical_flags & REAGENT_DEAD_PROCESS)
- R.on_mob_dead(src)
-
-///////////////
-// BREATHING //
-///////////////
-
-//Start of a breath chain, calls breathe()
-/mob/living/carbon/handle_breathing(times_fired)
- var/next_breath = 4
- var/obj/item/organ/lungs/L = getorganslot(ORGAN_SLOT_LUNGS)
- var/obj/item/organ/heart/H = getorganslot(ORGAN_SLOT_HEART)
- if(L)
- if(L.damage > L.high_threshold)
- next_breath--
- if(H)
- if(H.damage > H.high_threshold)
- next_breath--
-
- if((times_fired % next_breath) == 0 || failed_last_breath)
- breathe() //Breathe per 4 ticks if healthy, down to 2 if our lungs or heart are damaged, unless suffocating
- if(failed_last_breath)
- var/mob/living/carbon/human/B = src
- if(HAS_TRAIT(B, TRAIT_CHOKE_SLUT))
- B.adjustArousalLoss(7)
- if (B.getArousalLoss() >= 100 && ishuman(B) && B.has_dna())
- B.mob_climax(forced_climax=TRUE)
- else
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "suffocation", /datum/mood_event/suffocation)
- else
- SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "suffocation")
- else
- if(istype(loc, /obj/))
- var/obj/location_as_object = loc
- location_as_object.handle_internal_lifeform(src,0)
-
-//Second link in a breath chain, calls check_breath()
-/mob/living/carbon/proc/breathe()
- var/obj/item/organ/lungs = getorganslot(ORGAN_SLOT_LUNGS)
- if(reagents.has_reagent(/datum/reagent/toxin/lexorin))
- return
- if(istype(loc, /obj/machinery/atmospherics/components/unary/cryo_cell))
- return
- if(istype(loc, /obj/item/dogborg/sleeper))
- return
- if(ismob(loc))
- return
- if(isbelly(loc))
- return
-
- var/datum/gas_mixture/environment
- if(loc)
- environment = loc.return_air()
-
- var/datum/gas_mixture/breath
-
- if(!getorganslot(ORGAN_SLOT_BREATHING_TUBE))
- if(health <= HEALTH_THRESHOLD_FULLCRIT || (pulledby && pulledby.grab_state >= GRAB_KILL) || HAS_TRAIT(src, TRAIT_MAGIC_CHOKE) || (lungs && lungs.organ_flags & ORGAN_FAILING))
- losebreath++ //You can't breath at all when in critical or when being choked, so you're going to miss a breath
-
- else if(health <= crit_threshold)
- losebreath += 0.25 //You're having trouble breathing in soft crit, so you'll miss a breath one in four times
-
- //Suffocate
- if(losebreath >= 1) //You've missed a breath, take oxy damage
- losebreath--
- if(prob(10))
- emote("gasp")
- if(istype(loc, /obj/))
- var/obj/loc_as_obj = loc
- loc_as_obj.handle_internal_lifeform(src,0)
- else
- //Breathe from internal
- breath = get_breath_from_internal(BREATH_VOLUME)
-
- if(isnull(breath)) //in case of 0 pressure internals
-
- if(isobj(loc)) //Breathe from loc as object
- var/obj/loc_as_obj = loc
- breath = loc_as_obj.handle_internal_lifeform(src, BREATH_VOLUME)
-
- else if(isturf(loc)) //Breathe from loc as turf
- var/breath_moles = 0
- if(environment)
- breath_moles = environment.total_moles()*BREATH_PERCENTAGE
-
- breath = loc.remove_air(breath_moles)
- else //Breathe from loc as obj again
- if(istype(loc, /obj/))
- var/obj/loc_as_obj = loc
- loc_as_obj.handle_internal_lifeform(src,0)
-
- check_breath(breath)
-
- if(breath)
- loc.assume_air(breath)
- air_update_turf()
-
-/mob/living/carbon/proc/has_smoke_protection()
- if(HAS_TRAIT(src, TRAIT_NOBREATH))
- return TRUE
- return FALSE
-
-
-//Third link in a breath chain, calls handle_breath_temperature()
-/mob/living/carbon/proc/check_breath(datum/gas_mixture/breath)
- if((status_flags & GODMODE))
- return
-
- var/obj/item/organ/lungs = getorganslot(ORGAN_SLOT_LUNGS)
- if(!lungs)
- adjustOxyLoss(2)
-
- //CRIT
- if(!breath || (breath.total_moles() == 0) || !lungs)
- if(reagents.has_reagent(/datum/reagent/medicine/epinephrine) && lungs)
- return
- adjustOxyLoss(1)
-
- failed_last_breath = 1
- throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy)
- return 0
-
- var/safe_oxy_min = 16
- var/safe_oxy_max = 50
- var/safe_co2_max = 10
- var/safe_tox_max = 0.05
- var/SA_para_min = 1
- var/SA_sleep_min = 5
- var/oxygen_used = 0
- var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME
-
- var/list/breath_gases = breath.gases
- var/O2_partialpressure = (breath_gases[/datum/gas/oxygen]/breath.total_moles())*breath_pressure
- var/Toxins_partialpressure = (breath_gases[/datum/gas/plasma]/breath.total_moles())*breath_pressure
- var/CO2_partialpressure = (breath_gases[/datum/gas/carbon_dioxide]/breath.total_moles())*breath_pressure
-
-
- //OXYGEN
- if(O2_partialpressure > safe_oxy_max) // Too much Oxygen - blatant CO2 effect copy/pasta
- if(!o2overloadtime)
- o2overloadtime = world.time
- else if(world.time - o2overloadtime > 120)
- Dizzy(10) // better than a minute of you're fucked KO, but certainly a wake up call. Honk.
- adjustOxyLoss(3)
- if(world.time - o2overloadtime > 300)
- adjustOxyLoss(8)
- if(prob(20))
- emote("cough")
- throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy)
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "suffocation", /datum/mood_event/suffocation)
-
- if(O2_partialpressure < safe_oxy_min) //Not enough oxygen
- if(prob(20))
- emote("gasp")
- if(O2_partialpressure > 0)
- var/ratio = 1 - O2_partialpressure/safe_oxy_min
- adjustOxyLoss(min(5*ratio, 3))
- failed_last_breath = 1
- oxygen_used = breath_gases[/datum/gas/oxygen]*ratio
- else
- adjustOxyLoss(3)
- failed_last_breath = 1
- throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy)
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "suffocation", /datum/mood_event/suffocation)
-
- else //Enough oxygen
- failed_last_breath = 0
- o2overloadtime = 0 //reset our counter for this too
- if(health >= crit_threshold)
- adjustOxyLoss(-5)
- oxygen_used = breath_gases[/datum/gas/oxygen]
- clear_alert("not_enough_oxy")
- SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "suffocation")
-
- breath_gases[/datum/gas/oxygen] -= oxygen_used
- breath_gases[/datum/gas/carbon_dioxide] += oxygen_used
-
- //CARBON DIOXIDE
- if(CO2_partialpressure > safe_co2_max)
- if(!co2overloadtime)
- co2overloadtime = world.time
- else if(world.time - co2overloadtime > 120)
- Unconscious(60)
- adjustOxyLoss(3)
- if(world.time - co2overloadtime > 300)
- adjustOxyLoss(8)
- if(prob(20))
- emote("cough")
-
- else
- co2overloadtime = 0
-
- //TOXINS/PLASMA
- if(Toxins_partialpressure > safe_tox_max)
- var/ratio = (breath_gases[/datum/gas/plasma]/safe_tox_max) * 10
- adjustToxLoss(CLAMP(ratio, MIN_TOXIC_GAS_DAMAGE, MAX_TOXIC_GAS_DAMAGE))
- throw_alert("too_much_tox", /obj/screen/alert/too_much_tox)
- else
- clear_alert("too_much_tox")
-
- //NITROUS OXIDE
- if(breath_gases[/datum/gas/nitrous_oxide])
- var/SA_partialpressure = (breath_gases[/datum/gas/nitrous_oxide]/breath.total_moles())*breath_pressure
- if(SA_partialpressure > SA_para_min)
- Unconscious(60)
- if(SA_partialpressure > SA_sleep_min)
- Sleeping(max(AmountSleeping() + 40, 200))
- else if(SA_partialpressure > 0.01)
- if(prob(20))
- emote(pick("giggle","laugh"))
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "chemical_euphoria", /datum/mood_event/chemical_euphoria)
- else
- SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "chemical_euphoria")
-
- //BZ (Facepunch port of their Agent B)
- if(breath_gases[/datum/gas/bz])
- var/bz_partialpressure = (breath_gases[/datum/gas/bz]/breath.total_moles())*breath_pressure
- if(bz_partialpressure > 1)
- hallucination += 10
- else if(bz_partialpressure > 0.01)
- hallucination += 5
-
- //TRITIUM
- if(breath_gases[/datum/gas/tritium])
- var/tritium_partialpressure = (breath_gases[/datum/gas/tritium]/breath.total_moles())*breath_pressure
- radiation += tritium_partialpressure/10
-
- //NITRYL
- if(breath_gases[/datum/gas/nitryl])
- var/nitryl_partialpressure = (breath_gases[/datum/gas/nitryl]/breath.total_moles())*breath_pressure
- adjustFireLoss(nitryl_partialpressure/4)
-
- //PHEROMONE
- if(breath_gases[/datum/gas/pheromone])
- var/pheromone_partialpressure = (breath_gases[/datum/gas/pheromone]/breath.total_moles())*breath_pressure
- if(pheromone_partialpressure > MINIMUM_MOLES_DELTA_TO_MOVE)
-
- //pheromone side effects
- switch(pheromone_partialpressure)
- if(1 to 5)
- // At lower pp, give out a little warning
- SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "smell")
- if(prob(5))
- to_chat(src, "There is an entracing smell in the air.")
- if(5 to 20)
- //At somewhat higher pp, warning becomes more obvious
- if(prob(15))
- to_chat(src, "You smell something enticing inside this room.")
- if(15 to INFINITY)
- //Small chance to vomit. By now, people have internals on anyway
- if(prob(5))
- to_chat(src, "The enticing smell is unbearable!")
-
- //MIASMA
- if(breath_gases[/datum/gas/miasma])
- var/miasma_partialpressure = (breath_gases[/datum/gas/miasma]/breath.total_moles())*breath_pressure
- if(miasma_partialpressure > MINIMUM_MOLES_DELTA_TO_MOVE)
-
- if(prob(0.05 * miasma_partialpressure))
- var/datum/disease/advance/miasma_disease = new /datum/disease/advance/random(2,3)
- miasma_disease.name = "Unknown"
- ForceContractDisease(miasma_disease, TRUE, TRUE)
-
- //Miasma side effects
- switch(miasma_partialpressure)
- if(1 to 5)
- // At lower pp, give out a little warning
- SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "smell")
- if(prob(5))
- to_chat(src, "There is an unpleasant smell in the air.")
- if(5 to 20)
- //At somewhat higher pp, warning becomes more obvious
- if(prob(15))
- to_chat(src, "You smell something horribly decayed inside this room.")
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/bad_smell)
- if(15 to 30)
- //Small chance to vomit. By now, people have internals on anyway
- if(prob(5))
- to_chat(src, "The stench of rotting carcasses is unbearable!")
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/nauseating_stench)
- vomit()
- if(30 to INFINITY)
- //Higher chance to vomit. Let the horror start
- if(prob(25))
- to_chat(src, "The stench of rotting carcasses is unbearable!")
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/nauseating_stench)
- vomit()
- else
- SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "smell")
-
-
- //Clear all moods if no miasma at all
- else
- SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "smell")
-
-
-
-
- GAS_GARBAGE_COLLECT(breath.gases)
-
- //BREATH TEMPERATURE
- handle_breath_temperature(breath)
-
- return 1
-
-//Fourth and final link in a breath chain
-/mob/living/carbon/proc/handle_breath_temperature(datum/gas_mixture/breath)
- return
-
-/mob/living/carbon/proc/get_breath_from_internal(volume_needed)
- var/obj/item/clothing/check
- var/internals = FALSE
-
- for(check in GET_INTERNAL_SLOTS(src))
- if(CHECK_BITFIELD(check.clothing_flags, ALLOWINTERNALS))
- internals = TRUE
- if(internal)
- if(internal.loc != src)
- internal = null
- update_internals_hud_icon(0)
- else if (!internals && !getorganslot(ORGAN_SLOT_BREATHING_TUBE))
- internal = null
- update_internals_hud_icon(0)
- else
- update_internals_hud_icon(1)
- . = internal.remove_air_volume(volume_needed)
- if(!.)
- return FALSE //to differentiate between no internals and active, but empty internals
-
-// Make corpses rot, emitting miasma
-/mob/living/carbon/proc/rot()
- // Properly stored corpses shouldn't create miasma
- if(istype(loc, /obj/structure/closet/crate/coffin)|| istype(loc, /obj/structure/closet/body_bag) || istype(loc, /obj/structure/bodycontainer))
- return
-
- // No decay if formaldehyde in corpse or when the corpse is charred
- if(reagents.has_reagent(/datum/reagent/toxin/formaldehyde, 15) || HAS_TRAIT(src, TRAIT_HUSK))
- return
-
- // Also no decay if corpse chilled or not organic/undead
- if(bodytemperature <= T0C-10 || (!(MOB_ORGANIC & mob_biotypes) && !(MOB_UNDEAD & mob_biotypes)))
- return
-
- // Wait a bit before decaying
- if(world.time - timeofdeath < 1200)
- return
-
- var/deceasedturf = get_turf(src)
-
- // Closed turfs don't have any air in them, so no gas building up
- if(!istype(deceasedturf,/turf/open))
- return
-
- var/turf/open/miasma_turf = deceasedturf
-
- var/list/cached_gases = miasma_turf.air.gases
-
- cached_gases[/datum/gas/miasma] += 0.1
-
-/mob/living/carbon/proc/handle_blood()
- return
-
-/mob/living/carbon/proc/handle_bodyparts()
- for(var/I in bodyparts)
- var/obj/item/bodypart/BP = I
- if(BP.needs_processing)
- . |= BP.on_life()
-
-/mob/living/carbon/proc/handle_organs()
- if(stat != DEAD)
- for(var/V in internal_organs)
- var/obj/item/organ/O = V
- if(O)
- O.on_life()
- else
- if(reagents.has_reagent(/datum/reagent/toxin/formaldehyde, 1)) // No organ decay if the body contains formaldehyde.
- return
- for(var/V in internal_organs)
- var/obj/item/organ/O = V
- if(O)
- O.on_death() //Needed so organs decay while inside the body.
-
-
-
-/mob/living/carbon/handle_diseases()
- for(var/thing in diseases)
- var/datum/disease/D = thing
- if(prob(D.infectivity))
- D.spread()
-
- if(stat != DEAD || D.process_dead)
- D.stage_act()
-
-//todo generalize this and move hud out
-/mob/living/carbon/proc/handle_changeling()
- if(mind && hud_used && hud_used.lingchemdisplay)
- var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
- if(changeling)
- changeling.regenerate()
- hud_used.lingchemdisplay.invisibility = 0
- hud_used.lingchemdisplay.maptext = "[round(changeling.chem_charges)]
"
- else
- hud_used.lingchemdisplay.invisibility = INVISIBILITY_ABSTRACT
-
-
-/mob/living/carbon/handle_mutations_and_radiation()
- if(dna && dna.temporary_mutations.len)
- for(var/mut in dna.temporary_mutations)
- if(dna.temporary_mutations[mut] < world.time)
- if(mut == UI_CHANGED)
- if(dna.previous["UI"])
- dna.uni_identity = merge_text(dna.uni_identity,dna.previous["UI"])
- updateappearance(mutations_overlay_update=1)
- dna.previous.Remove("UI")
- dna.temporary_mutations.Remove(mut)
- continue
- if(mut == UE_CHANGED)
- if(dna.previous["name"])
- real_name = dna.previous["name"]
- name = real_name
- dna.previous.Remove("name")
- if(dna.previous["UE"])
- dna.unique_enzymes = dna.previous["UE"]
- dna.previous.Remove("UE")
- if(dna.previous["blood_type"])
- dna.blood_type = dna.previous["blood_type"]
- dna.previous.Remove("blood_type")
- dna.temporary_mutations.Remove(mut)
- continue
- for(var/datum/mutation/human/HM in dna.mutations)
- if(HM && HM.timed)
- dna.remove_mutation(HM.type)
-
- radiation -= min(radiation, RAD_LOSS_PER_TICK)
- if(radiation > RAD_MOB_SAFE)
- adjustToxLoss(log(radiation-RAD_MOB_SAFE)*RAD_TOX_COEFFICIENT)
-
-/mob/living/carbon/handle_stomach()
- set waitfor = 0
- for(var/mob/living/M in stomach_contents)
- if(M.loc != src)
- stomach_contents.Remove(M)
- continue
- if(iscarbon(M) && stat != DEAD)
- if(M.stat == DEAD)
- M.death(1)
- stomach_contents.Remove(M)
- qdel(M)
- continue
- if(SSmobs.times_fired%3==1)
- if(!(M.status_flags & GODMODE))
- M.adjustBruteLoss(5)
- nutrition += 10
-
-
-/*
-Alcohol Poisoning Chart
-Note that all higher effects of alcohol poisoning will inherit effects for smaller amounts (i.e. light poisoning inherts from slight poisoning)
-In addition, severe effects won't always trigger unless the drink is poisonously strong
-All effects don't start immediately, but rather get worse over time; the rate is affected by the imbiber's alcohol tolerance
-
-0: Non-alcoholic
-1-10: Barely classifiable as alcohol - occassional slurring
-11-20: Slight alcohol content - slurring
-21-30: Below average - imbiber begins to look slightly drunk
-31-40: Just below average - no unique effects
-41-50: Average - mild disorientation, imbiber begins to look drunk
-51-60: Just above average - disorientation, vomiting, imbiber begins to look heavily drunk
-61-70: Above average - small chance of blurry vision, imbiber begins to look smashed
-71-80: High alcohol content - blurry vision, imbiber completely shitfaced
-81-90: Extremely high alcohol content - light brain damage, passing out
-91-100: Dangerously toxic - swift death
-*/
-#define BALLMER_POINTS 5
-GLOBAL_LIST_INIT(ballmer_good_msg, list("Hey guys, what if we rolled out a bluespace wiring system so mice can't destroy the powergrid anymore?",
- "Hear me out here. What if, and this is just a theory, we made R&D controllable from our PDAs?",
- "I'm thinking we should roll out a git repository for our research under the AGPLv3 license so that we can share it among the other stations freely.",
- "I dunno about you guys, but IDs and PDAs being separate is clunky as fuck. Maybe we should merge them into a chip in our arms? That way they can't be stolen easily.",
- "Why the fuck aren't we just making every pair of shoes into galoshes? We have the technology."))
-GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put a webserver that's automatically turned on with default admin passwords into every PDA?",
- "So like, you know how we separate our codebase from the master copy that runs on our consumer boxes? What if we merged the two and undid the separation between codebase and server?",
- "Dude, radical idea: H.O.N.K mechs but with no bananium required.",
- "Best idea ever: Disposal pipes instead of hallways."))
-
-//this updates all special effects: stun, sleeping, knockdown, druggy, stuttering, etc..
-/mob/living/carbon/handle_status_effects()
- ..()
- if(getStaminaLoss() && !combatmode)//CIT CHANGE - prevents stamina regen while combat mode is active
- adjustStaminaLoss(resting ? (recoveringstam ? -7.5 : -3) : -1.5)
-
- if(!recoveringstam && incomingstammult != 1)
- incomingstammult = max(0.01, incomingstammult)
- incomingstammult = min(1, incomingstammult*2)
-
- //CIT CHANGES START HERE. STAMINA BUFFER STUFF
- if(bufferedstam && world.time > stambufferregentime)
- var/drainrate = max((bufferedstam*(bufferedstam/(5)))*0.1,1)
- bufferedstam = max(bufferedstam - drainrate, 0)
- adjustStaminaLoss(drainrate*0.5)
- //END OF CIT CHANGES
-
- var/restingpwr = 1 + 4 * resting
-
- if(ckey)
- if(!client && !(stat == DEAD))
- add_status_indicator("ssd")
- SSD = TRUE
- else
- if(SSD)
- remove_status_indicator("ssd")
- SSD = FALSE
-
- //Dizziness
- if(dizziness)
- var/client/C = client
- var/pixel_x_diff = 0
- var/pixel_y_diff = 0
- var/temp
- var/saved_dizz = dizziness
- if(C)
- var/oldsrc = src
- var/amplitude = dizziness*(sin(dizziness * world.time) + 1) // This shit is annoying at high strength
- src = null
- spawn(0)
- if(C)
- temp = amplitude * sin(saved_dizz * world.time)
- pixel_x_diff += temp
- C.pixel_x += temp
- temp = amplitude * cos(saved_dizz * world.time)
- pixel_y_diff += temp
- C.pixel_y += temp
- sleep(3)
- if(C)
- temp = amplitude * sin(saved_dizz * world.time)
- pixel_x_diff += temp
- C.pixel_x += temp
- temp = amplitude * cos(saved_dizz * world.time)
- pixel_y_diff += temp
- C.pixel_y += temp
- sleep(3)
- if(C)
- C.pixel_x -= pixel_x_diff
- C.pixel_y -= pixel_y_diff
- src = oldsrc
- dizziness = max(dizziness - restingpwr, 0)
-
- if(drowsyness)
- drowsyness = max(drowsyness - restingpwr, 0)
- blur_eyes(2)
- if(prob(5))
- AdjustSleeping(20)
- Unconscious(100)
-
- //Jitteriness
- if(jitteriness)
- do_jitter_animation(jitteriness)
- jitteriness = max(jitteriness - restingpwr, 0)
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "jittery", /datum/mood_event/jittery)
- else
- SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "jittery")
-
- if(stuttering)
- stuttering = max(stuttering-1, 0)
-
- if(slurring || drunkenness)
- slurring = max(slurring-1,0,drunkenness)
-
- if(cultslurring)
- cultslurring = max(cultslurring-1, 0)
-
- if(silent)
- silent = max(silent-1, 0)
-
- if(druggy)
- adjust_drugginess(-1)
-
- if(hallucination)
- handle_hallucinations()
-
- if(drunkenness)
- drunkenness = max(drunkenness - (drunkenness * 0.04), 0)
- if(drunkenness >= 6)
- SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "drunk", /datum/mood_event/drunk)
- jitteriness = max(jitteriness - 3, 0)
- if(HAS_TRAIT(src, TRAIT_DRUNK_HEALING))
- adjustBruteLoss(-0.12, FALSE)
- adjustFireLoss(-0.06, FALSE)
-
- if(mind && (mind.assigned_role == "Scientist" || mind.assigned_role == "Research Director"))
- if(SSresearch.science_tech)
- if(drunkenness >= 12.9 && drunkenness <= 13.8)
- drunkenness = round(drunkenness, 0.01)
- var/ballmer_percent = 0
- if(drunkenness == 13.35) // why run math if I dont have to
- ballmer_percent = 1
- else
- ballmer_percent = (-abs(drunkenness - 13.35) / 0.9) + 1
- if(prob(5))
- say(pick(GLOB.ballmer_good_msg), forced = "ballmer")
- SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = BALLMER_POINTS * ballmer_percent))
- if(drunkenness > 26) // by this point you're into windows ME territory
- if(prob(5))
- SSresearch.science_tech.remove_point_list(list(TECHWEB_POINT_TYPE_GENERIC = BALLMER_POINTS))
- say(pick(GLOB.ballmer_windows_me_msg), forced = "ballmer")
-
- if(drunkenness >= 41)
- if(prob(25))
- confused += 2
- Dizzy(10)
- if(HAS_TRAIT(src, TRAIT_DRUNK_HEALING)) // effects stack with lower tiers
- adjustBruteLoss(-0.3, FALSE)
- adjustFireLoss(-0.15, FALSE)
-
- if(drunkenness >= 51)
- if(prob(5))
- confused += 10
- vomit()
- Dizzy(25)
-
- if(drunkenness >= 61)
- if(prob(50))
- blur_eyes(5)
- if(HAS_TRAIT(src, TRAIT_DRUNK_HEALING))
- adjustBruteLoss(-0.4, FALSE)
- adjustFireLoss(-0.2, FALSE)
-
- if(drunkenness >= 71)
- blur_eyes(5)
-
- if(drunkenness >= 81)
- adjustToxLoss(0.2)
- if(prob(5) && !stat)
- to_chat(src, "Maybe you should lie down for a bit...")
-
- if(drunkenness >= 91)
- adjustOrganLoss(ORGAN_SLOT_BRAIN, 0.4, 60)
- if(prob(20) && !stat)
- if(SSshuttle.emergency.mode == SHUTTLE_DOCKED && is_station_level(z)) //QoL mainly
- to_chat(src, "You're so tired... but you can't miss that shuttle...")
- else
- to_chat(src, "Just a quick nap...")
- Sleeping(900)
-
- if(drunkenness >= 101)
- adjustToxLoss(4) //Let's be honest you shouldn't be alive by now
- else
- SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "drunk")
-
-//used in human and monkey handle_environment()
-/mob/living/carbon/proc/natural_bodytemperature_stabilization()
- if (HAS_TRAIT(src, TRAIT_COLDBLOODED))
- return 0 //Return 0 as your natural temperature. Species proc handle_environment() will adjust your temperature based on this.
-
- var/body_temperature_difference = BODYTEMP_NORMAL - bodytemperature
- switch(bodytemperature)
- if(-INFINITY to BODYTEMP_COLD_DAMAGE_LIMIT) //Cold damage limit is 50 below the default, the temperature where you start to feel effects.
- return max((body_temperature_difference * metabolism_efficiency / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM)
- if(BODYTEMP_COLD_DAMAGE_LIMIT to BODYTEMP_NORMAL)
- return max(body_temperature_difference * metabolism_efficiency / BODYTEMP_AUTORECOVERY_DIVISOR, min(body_temperature_difference, BODYTEMP_AUTORECOVERY_MINIMUM/4))
- if(BODYTEMP_NORMAL to BODYTEMP_HEAT_DAMAGE_LIMIT) // Heat damage limit is 50 above the default, the temperature where you start to feel effects.
- return min(body_temperature_difference * metabolism_efficiency / BODYTEMP_AUTORECOVERY_DIVISOR, max(body_temperature_difference, -BODYTEMP_AUTORECOVERY_MINIMUM/4))
- if(BODYTEMP_HEAT_DAMAGE_LIMIT to INFINITY)
- return min((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), -BODYTEMP_AUTORECOVERY_MINIMUM) //We're dealing with negative numbers
-/////////
-//LIVER//
-/////////
-
-/mob/living/carbon/proc/handle_liver()
- var/obj/item/organ/liver/liver = getorganslot(ORGAN_SLOT_LIVER)
- if((!dna && !liver) || (NOLIVER in dna.species.species_traits))
- return
- if(liver)
- if(liver.damage < liver.maxHealth)
- liver.organ_flags |= ORGAN_FAILING
- liver_failure()
- else
- liver_failure()
-
-/mob/living/carbon/proc/undergoing_liver_failure()
- var/obj/item/organ/liver/liver = getorganslot(ORGAN_SLOT_LIVER)
- if(liver && liver.failing)
- return TRUE
-
-/mob/living/carbon/proc/return_liver_damage()
- var/obj/item/organ/liver/liver = getorganslot(ORGAN_SLOT_LIVER)
- if(liver)
- return liver.damage
-
-/mob/living/carbon/proc/applyLiverDamage(var/d)
- var/obj/item/organ/liver/L = getorganslot(ORGAN_SLOT_LIVER)
- if(L)
- L.damage += d
-
-/mob/living/carbon/proc/liver_failure()
- reagents.end_metabolization(src, keep_liverless = TRUE) //Stops trait-based effects on reagents, to prevent permanent buffs
- reagents.metabolize(src, can_overdose=FALSE, liverless = TRUE)
- if(HAS_TRAIT(src, TRAIT_STABLELIVER))
- return
- adjustToxLoss(4, TRUE, TRUE)
- if(prob(30))
- to_chat(src, "You feel a stabbing pain in your abdomen!")
-
-
-////////////////
-//BRAIN DAMAGE//
-////////////////
-
-/mob/living/carbon/proc/handle_brain_damage()
- for(var/T in get_traumas())
- var/datum/brain_trauma/BT = T
- BT.on_life()
-
-/////////////////////////////////////
-//MONKEYS WITH TOO MUCH CHOLOESTROL//
-/////////////////////////////////////
-
-/mob/living/carbon/proc/can_heartattack()
- if(!needs_heart())
- return FALSE
- var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART)
- if(!heart || (heart.organ_flags & ORGAN_SYNTHETIC))
- return FALSE
- return TRUE
-
-/mob/living/carbon/proc/needs_heart()
- if(HAS_TRAIT(src, TRAIT_STABLEHEART))
- return FALSE
- if(dna && dna.species && (NOBLOOD in dna.species.species_traits)) //not all carbons have species!
- return FALSE
- return TRUE
-
-/mob/living/carbon/proc/undergoing_cardiac_arrest()
- var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART)
- if(istype(heart) && heart.beating)
- return FALSE
- else if(!needs_heart())
- return FALSE
- return TRUE
-
-/mob/living/carbon/proc/set_heartattack(status)
- if(!can_heartattack())
- return FALSE
-
- var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART)
- if(!istype(heart))
- return
-
- heart.beating = !status
+/mob/living/carbon/Life()
+ set invisibility = 0
+
+ //Hyper Change
+ if(size_multiplier != previous_size)
+ sleep(1)
+ resize(size_multiplier)
+
+ if(notransform)
+ return
+
+ if(damageoverlaytemp)
+ damageoverlaytemp = 0
+ update_damage_hud()
+
+
+ if(!IsInStasis())
+ //Reagent processing needs to come before breathing, to prevent edge cases.
+ handle_organs()
+
+ . = ..()
+
+ if (QDELETED(src))
+ return
+
+ if(.) //not dead
+ handle_blood()
+
+ if(stat != DEAD)
+ var/bprv = handle_bodyparts()
+ if(bprv & BODYPART_LIFE_UPDATE_HEALTH)
+ updatehealth()
+ update_stamina()
+
+ if(stat != DEAD)
+ handle_brain_damage()
+/* Bug cause?
+ if(stat != DEAD)
+ handle_liver()
+*/
+ if(stat == DEAD)
+ stop_sound_channel(CHANNEL_HEARTBEAT)
+ handle_death()
+ rot()
+
+ //Updates the number of stored chemicals for powers
+ handle_changeling()
+
+ if(stat != DEAD)
+ return 1
+
+//Procs called while dead
+/mob/living/carbon/proc/handle_death()
+ for(var/datum/reagent/R in reagents.reagent_list)
+ if(R.chemical_flags & REAGENT_DEAD_PROCESS)
+ R.on_mob_dead(src)
+
+///////////////
+// BREATHING //
+///////////////
+
+//Start of a breath chain, calls breathe()
+/mob/living/carbon/handle_breathing(times_fired)
+ var/next_breath = 4
+ var/obj/item/organ/lungs/L = getorganslot(ORGAN_SLOT_LUNGS)
+ var/obj/item/organ/heart/H = getorganslot(ORGAN_SLOT_HEART)
+ if(L)
+ if(L.damage > L.high_threshold)
+ next_breath--
+ if(H)
+ if(H.damage > H.high_threshold)
+ next_breath--
+
+ if((times_fired % next_breath) == 0 || failed_last_breath)
+ breathe() //Breathe per 4 ticks if healthy, down to 2 if our lungs or heart are damaged, unless suffocating
+ if(failed_last_breath)
+ var/mob/living/carbon/human/B = src
+ if(HAS_TRAIT(B, TRAIT_CHOKE_SLUT))
+ B.adjustArousalLoss(7)
+ if (B.getArousalLoss() >= 100 && ishuman(B) && B.has_dna())
+ B.mob_climax(forced_climax=TRUE)
+ else
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "suffocation", /datum/mood_event/suffocation)
+ else
+ SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "suffocation")
+ else
+ if(istype(loc, /obj/))
+ var/obj/location_as_object = loc
+ location_as_object.handle_internal_lifeform(src,0)
+
+//Second link in a breath chain, calls check_breath()
+/mob/living/carbon/proc/breathe()
+ var/obj/item/organ/lungs = getorganslot(ORGAN_SLOT_LUNGS)
+ if(reagents.has_reagent(/datum/reagent/toxin/lexorin))
+ return
+ if(istype(loc, /obj/machinery/atmospherics/components/unary/cryo_cell))
+ return
+ if(istype(loc, /obj/item/dogborg/sleeper))
+ return
+ if(ismob(loc))
+ return
+ if(isbelly(loc))
+ return
+
+ var/datum/gas_mixture/environment
+ if(loc)
+ environment = loc.return_air()
+
+ var/datum/gas_mixture/breath
+
+ if(!getorganslot(ORGAN_SLOT_BREATHING_TUBE))
+ if(health <= HEALTH_THRESHOLD_FULLCRIT || (pulledby && pulledby.grab_state >= GRAB_KILL) || HAS_TRAIT(src, TRAIT_MAGIC_CHOKE) || (lungs && lungs.organ_flags & ORGAN_FAILING))
+ losebreath++ //You can't breath at all when in critical or when being choked, so you're going to miss a breath
+
+ else if(health <= crit_threshold)
+ losebreath += 0.25 //You're having trouble breathing in soft crit, so you'll miss a breath one in four times
+
+ //Suffocate
+ if(losebreath >= 1) //You've missed a breath, take oxy damage
+ losebreath--
+ if(prob(10))
+ emote("gasp")
+ if(istype(loc, /obj/))
+ var/obj/loc_as_obj = loc
+ loc_as_obj.handle_internal_lifeform(src,0)
+ else
+ //Breathe from internal
+ breath = get_breath_from_internal(BREATH_VOLUME)
+
+ if(isnull(breath)) //in case of 0 pressure internals
+
+ if(isobj(loc)) //Breathe from loc as object
+ var/obj/loc_as_obj = loc
+ breath = loc_as_obj.handle_internal_lifeform(src, BREATH_VOLUME)
+
+ else if(isturf(loc)) //Breathe from loc as turf
+ var/breath_moles = 0
+ if(environment)
+ breath_moles = environment.total_moles()*BREATH_PERCENTAGE
+
+ breath = loc.remove_air(breath_moles)
+ else //Breathe from loc as obj again
+ if(istype(loc, /obj/))
+ var/obj/loc_as_obj = loc
+ loc_as_obj.handle_internal_lifeform(src,0)
+
+ check_breath(breath)
+
+ if(breath)
+ loc.assume_air(breath)
+ air_update_turf()
+
+/mob/living/carbon/proc/has_smoke_protection()
+ if(HAS_TRAIT(src, TRAIT_NOBREATH))
+ return TRUE
+ return FALSE
+
+
+//Third link in a breath chain, calls handle_breath_temperature()
+/mob/living/carbon/proc/check_breath(datum/gas_mixture/breath)
+ if((status_flags & GODMODE))
+ return
+
+ var/obj/item/organ/lungs = getorganslot(ORGAN_SLOT_LUNGS)
+ if(!lungs)
+ adjustOxyLoss(2)
+
+ //CRIT
+ if(!breath || (breath.total_moles() == 0) || !lungs)
+ if(reagents.has_reagent(/datum/reagent/medicine/epinephrine) && lungs)
+ return
+ adjustOxyLoss(1)
+
+ failed_last_breath = 1
+ throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy)
+ return 0
+
+ var/safe_oxy_min = 16
+ var/safe_oxy_max = 50
+ var/safe_co2_max = 10
+ var/safe_tox_max = 0.05
+ var/SA_para_min = 1
+ var/SA_sleep_min = 5
+ var/oxygen_used = 0
+ var/breath_pressure = (breath.total_moles()*R_IDEAL_GAS_EQUATION*breath.temperature)/BREATH_VOLUME
+
+ var/list/breath_gases = breath.gases
+ var/O2_partialpressure = (breath_gases[/datum/gas/oxygen]/breath.total_moles())*breath_pressure
+ var/Toxins_partialpressure = (breath_gases[/datum/gas/plasma]/breath.total_moles())*breath_pressure
+ var/CO2_partialpressure = (breath_gases[/datum/gas/carbon_dioxide]/breath.total_moles())*breath_pressure
+
+
+ //OXYGEN
+ if(O2_partialpressure > safe_oxy_max) // Too much Oxygen - blatant CO2 effect copy/pasta
+ if(!o2overloadtime)
+ o2overloadtime = world.time
+ else if(world.time - o2overloadtime > 120)
+ Dizzy(10) // better than a minute of you're fucked KO, but certainly a wake up call. Honk.
+ adjustOxyLoss(3)
+ if(world.time - o2overloadtime > 300)
+ adjustOxyLoss(8)
+ if(prob(20))
+ emote("cough")
+ throw_alert("too_much_oxy", /obj/screen/alert/too_much_oxy)
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "suffocation", /datum/mood_event/suffocation)
+
+ if(O2_partialpressure < safe_oxy_min) //Not enough oxygen
+ if(prob(20))
+ emote("gasp")
+ if(O2_partialpressure > 0)
+ var/ratio = 1 - O2_partialpressure/safe_oxy_min
+ adjustOxyLoss(min(5*ratio, 3))
+ failed_last_breath = 1
+ oxygen_used = breath_gases[/datum/gas/oxygen]*ratio
+ else
+ adjustOxyLoss(3)
+ failed_last_breath = 1
+ throw_alert("not_enough_oxy", /obj/screen/alert/not_enough_oxy)
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "suffocation", /datum/mood_event/suffocation)
+
+ else //Enough oxygen
+ failed_last_breath = 0
+ o2overloadtime = 0 //reset our counter for this too
+ if(health >= crit_threshold)
+ adjustOxyLoss(-5)
+ oxygen_used = breath_gases[/datum/gas/oxygen]
+ clear_alert("not_enough_oxy")
+ SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "suffocation")
+
+ breath_gases[/datum/gas/oxygen] -= oxygen_used
+ breath_gases[/datum/gas/carbon_dioxide] += oxygen_used
+
+ //CARBON DIOXIDE
+ if(CO2_partialpressure > safe_co2_max)
+ if(!co2overloadtime)
+ co2overloadtime = world.time
+ else if(world.time - co2overloadtime > 120)
+ Unconscious(60)
+ adjustOxyLoss(3)
+ if(world.time - co2overloadtime > 300)
+ adjustOxyLoss(8)
+ if(prob(20))
+ emote("cough")
+
+ else
+ co2overloadtime = 0
+
+ //TOXINS/PLASMA
+ if(Toxins_partialpressure > safe_tox_max)
+ var/ratio = (breath_gases[/datum/gas/plasma]/safe_tox_max) * 10
+ adjustToxLoss(CLAMP(ratio, MIN_TOXIC_GAS_DAMAGE, MAX_TOXIC_GAS_DAMAGE))
+ throw_alert("too_much_tox", /obj/screen/alert/too_much_tox)
+ else
+ clear_alert("too_much_tox")
+
+ //NITROUS OXIDE
+ if(breath_gases[/datum/gas/nitrous_oxide])
+ var/SA_partialpressure = (breath_gases[/datum/gas/nitrous_oxide]/breath.total_moles())*breath_pressure
+ if(SA_partialpressure > SA_para_min)
+ Unconscious(60)
+ if(SA_partialpressure > SA_sleep_min)
+ Sleeping(max(AmountSleeping() + 40, 200))
+ else if(SA_partialpressure > 0.01)
+ if(prob(20))
+ emote(pick("giggle","laugh"))
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "chemical_euphoria", /datum/mood_event/chemical_euphoria)
+ else
+ SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "chemical_euphoria")
+
+ //BZ (Facepunch port of their Agent B)
+ if(breath_gases[/datum/gas/bz])
+ var/bz_partialpressure = (breath_gases[/datum/gas/bz]/breath.total_moles())*breath_pressure
+ if(bz_partialpressure > 1)
+ hallucination += 10
+ else if(bz_partialpressure > 0.01)
+ hallucination += 5
+
+ //TRITIUM
+ if(breath_gases[/datum/gas/tritium])
+ var/tritium_partialpressure = (breath_gases[/datum/gas/tritium]/breath.total_moles())*breath_pressure
+ radiation += tritium_partialpressure/10
+
+ //NITRYL
+ if(breath_gases[/datum/gas/nitryl])
+ var/nitryl_partialpressure = (breath_gases[/datum/gas/nitryl]/breath.total_moles())*breath_pressure
+ adjustFireLoss(nitryl_partialpressure/4)
+
+ //PHEROMONE
+ if(breath_gases[/datum/gas/pheromone])
+ var/pheromone_partialpressure = (breath_gases[/datum/gas/pheromone]/breath.total_moles())*breath_pressure
+ if(pheromone_partialpressure > MINIMUM_MOLES_DELTA_TO_MOVE)
+
+ //pheromone side effects
+ switch(pheromone_partialpressure)
+ if(1 to 5)
+ // At lower pp, give out a little warning
+ SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "smell")
+ if(prob(5))
+ to_chat(src, "There is an entracing smell in the air.")
+ if(5 to 20)
+ //At somewhat higher pp, warning becomes more obvious
+ if(prob(15))
+ to_chat(src, "You smell something enticing inside this room.")
+ if(15 to INFINITY)
+ //Small chance to vomit. By now, people have internals on anyway
+ if(prob(5))
+ to_chat(src, "The enticing smell is unbearable!")
+
+ //MIASMA
+ if(breath_gases[/datum/gas/miasma])
+ var/miasma_partialpressure = (breath_gases[/datum/gas/miasma]/breath.total_moles())*breath_pressure
+ if(miasma_partialpressure > MINIMUM_MOLES_DELTA_TO_MOVE)
+
+ if(prob(0.05 * miasma_partialpressure))
+ var/datum/disease/advance/miasma_disease = new /datum/disease/advance/random(TRUE, 2,3)
+ miasma_disease.name = "Unknown"
+ ForceContractDisease(miasma_disease, TRUE, TRUE)
+
+ //Miasma side effects
+ switch(miasma_partialpressure)
+ if(1 to 5)
+ // At lower pp, give out a little warning
+ SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "smell")
+ if(prob(5))
+ to_chat(src, "There is an unpleasant smell in the air.")
+ if(5 to 20)
+ //At somewhat higher pp, warning becomes more obvious
+ if(prob(15))
+ to_chat(src, "You smell something horribly decayed inside this room.")
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/bad_smell)
+ if(15 to 30)
+ //Small chance to vomit. By now, people have internals on anyway
+ if(prob(5))
+ to_chat(src, "The stench of rotting carcasses is unbearable!")
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/nauseating_stench)
+ vomit()
+ if(30 to INFINITY)
+ //Higher chance to vomit. Let the horror start
+ if(prob(25))
+ to_chat(src, "The stench of rotting carcasses is unbearable!")
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "smell", /datum/mood_event/disgust/nauseating_stench)
+ vomit()
+ else
+ SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "smell")
+
+
+ //Clear all moods if no miasma at all
+ else
+ SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "smell")
+
+
+
+
+ GAS_GARBAGE_COLLECT(breath.gases)
+
+ //BREATH TEMPERATURE
+ handle_breath_temperature(breath)
+
+ return 1
+
+//Fourth and final link in a breath chain
+/mob/living/carbon/proc/handle_breath_temperature(datum/gas_mixture/breath)
+ return
+
+/mob/living/carbon/proc/get_breath_from_internal(volume_needed)
+ var/obj/item/clothing/check
+ var/internals = FALSE
+
+ for(check in GET_INTERNAL_SLOTS(src))
+ if(CHECK_BITFIELD(check.clothing_flags, ALLOWINTERNALS))
+ internals = TRUE
+ if(internal)
+ if(internal.loc != src)
+ internal = null
+ update_internals_hud_icon(0)
+ else if (!internals && !getorganslot(ORGAN_SLOT_BREATHING_TUBE))
+ internal = null
+ update_internals_hud_icon(0)
+ else
+ update_internals_hud_icon(1)
+ . = internal.remove_air_volume(volume_needed)
+ if(!.)
+ return FALSE //to differentiate between no internals and active, but empty internals
+
+// Make corpses rot, emitting miasma
+/mob/living/carbon/proc/rot()
+ // Properly stored corpses shouldn't create miasma
+ if(istype(loc, /obj/structure/closet/crate/coffin)|| istype(loc, /obj/structure/closet/body_bag) || istype(loc, /obj/structure/bodycontainer))
+ return
+
+ // No decay if formaldehyde in corpse or when the corpse is charred
+ if(reagents.has_reagent(/datum/reagent/toxin/formaldehyde, 15) || HAS_TRAIT(src, TRAIT_HUSK))
+ return
+
+ // Also no decay if corpse chilled or not organic/undead
+ if(bodytemperature <= T0C-10 || (!(MOB_ORGANIC & mob_biotypes) && !(MOB_UNDEAD & mob_biotypes)))
+ return
+
+ // Wait a bit before decaying
+ if(world.time - timeofdeath < 1200)
+ return
+
+ var/deceasedturf = get_turf(src)
+
+ // Closed turfs don't have any air in them, so no gas building up
+ if(!istype(deceasedturf,/turf/open))
+ return
+
+ var/turf/open/miasma_turf = deceasedturf
+
+ var/list/cached_gases = miasma_turf.air.gases
+
+ cached_gases[/datum/gas/miasma] += 0.1
+
+/mob/living/carbon/proc/handle_blood()
+ return
+
+/mob/living/carbon/proc/handle_bodyparts()
+ for(var/I in bodyparts)
+ var/obj/item/bodypart/BP = I
+ if(BP.needs_processing)
+ . |= BP.on_life()
+
+/mob/living/carbon/proc/handle_organs()
+ if(stat != DEAD)
+ for(var/V in internal_organs)
+ var/obj/item/organ/O = V
+ if(O)
+ O.on_life()
+ else
+ if(reagents.has_reagent(/datum/reagent/toxin/formaldehyde, 1)) // No organ decay if the body contains formaldehyde.
+ return
+ for(var/V in internal_organs)
+ var/obj/item/organ/O = V
+ if(O)
+ O.on_death() //Needed so organs decay while inside the body.
+
+
+
+/mob/living/carbon/handle_diseases()
+ for(var/thing in diseases)
+ var/datum/disease/D = thing
+ if(prob(D.infectivity))
+ D.spread()
+
+ if(stat != DEAD || D.process_dead)
+ D.stage_act()
+
+//todo generalize this and move hud out
+/mob/living/carbon/proc/handle_changeling()
+ if(mind && hud_used && hud_used.lingchemdisplay)
+ var/datum/antagonist/changeling/changeling = mind.has_antag_datum(/datum/antagonist/changeling)
+ if(changeling)
+ changeling.regenerate()
+ hud_used.lingchemdisplay.invisibility = 0
+ hud_used.lingchemdisplay.maptext = "[round(changeling.chem_charges)]
"
+ else
+ hud_used.lingchemdisplay.invisibility = INVISIBILITY_ABSTRACT
+
+
+/mob/living/carbon/handle_mutations_and_radiation()
+ if(dna && dna.temporary_mutations.len)
+ for(var/mut in dna.temporary_mutations)
+ if(dna.temporary_mutations[mut] < world.time)
+ if(mut == UI_CHANGED)
+ if(dna.previous["UI"])
+ dna.uni_identity = merge_text(dna.uni_identity,dna.previous["UI"])
+ updateappearance(mutations_overlay_update=1)
+ dna.previous.Remove("UI")
+ dna.temporary_mutations.Remove(mut)
+ continue
+ if(mut == UE_CHANGED)
+ if(dna.previous["name"])
+ real_name = dna.previous["name"]
+ name = real_name
+ dna.previous.Remove("name")
+ if(dna.previous["UE"])
+ dna.unique_enzymes = dna.previous["UE"]
+ dna.previous.Remove("UE")
+ if(dna.previous["blood_type"])
+ dna.blood_type = dna.previous["blood_type"]
+ dna.previous.Remove("blood_type")
+ dna.temporary_mutations.Remove(mut)
+ continue
+ for(var/datum/mutation/human/HM in dna.mutations)
+ if(HM && HM.timed)
+ dna.remove_mutation(HM.type)
+
+ radiation -= min(radiation, RAD_LOSS_PER_TICK)
+ if(radiation > RAD_MOB_SAFE)
+ adjustToxLoss(log(radiation-RAD_MOB_SAFE)*RAD_TOX_COEFFICIENT)
+
+/mob/living/carbon/handle_stomach()
+ set waitfor = 0
+ for(var/mob/living/M in stomach_contents)
+ if(M.loc != src)
+ stomach_contents.Remove(M)
+ continue
+ if(iscarbon(M) && stat != DEAD)
+ if(M.stat == DEAD)
+ M.death(1)
+ stomach_contents.Remove(M)
+ qdel(M)
+ continue
+ if(SSmobs.times_fired%3==1)
+ if(!(M.status_flags & GODMODE))
+ M.adjustBruteLoss(5)
+ nutrition += 10
+
+
+/*
+Alcohol Poisoning Chart
+Note that all higher effects of alcohol poisoning will inherit effects for smaller amounts (i.e. light poisoning inherts from slight poisoning)
+In addition, severe effects won't always trigger unless the drink is poisonously strong
+All effects don't start immediately, but rather get worse over time; the rate is affected by the imbiber's alcohol tolerance
+
+0: Non-alcoholic
+1-10: Barely classifiable as alcohol - occassional slurring
+11-20: Slight alcohol content - slurring
+21-30: Below average - imbiber begins to look slightly drunk
+31-40: Just below average - no unique effects
+41-50: Average - mild disorientation, imbiber begins to look drunk
+51-60: Just above average - disorientation, vomiting, imbiber begins to look heavily drunk
+61-70: Above average - small chance of blurry vision, imbiber begins to look smashed
+71-80: High alcohol content - blurry vision, imbiber completely shitfaced
+81-90: Extremely high alcohol content - light brain damage, passing out
+91-100: Dangerously toxic - swift death
+*/
+#define BALLMER_POINTS 5
+GLOBAL_LIST_INIT(ballmer_good_msg, list("Hey guys, what if we rolled out a bluespace wiring system so mice can't destroy the powergrid anymore?",
+ "Hear me out here. What if, and this is just a theory, we made R&D controllable from our PDAs?",
+ "I'm thinking we should roll out a git repository for our research under the AGPLv3 license so that we can share it among the other stations freely.",
+ "I dunno about you guys, but IDs and PDAs being separate is clunky as fuck. Maybe we should merge them into a chip in our arms? That way they can't be stolen easily.",
+ "Why the fuck aren't we just making every pair of shoes into galoshes? We have the technology."))
+GLOBAL_LIST_INIT(ballmer_windows_me_msg, list("Yo man, what if, we like, uh, put a webserver that's automatically turned on with default admin passwords into every PDA?",
+ "So like, you know how we separate our codebase from the master copy that runs on our consumer boxes? What if we merged the two and undid the separation between codebase and server?",
+ "Dude, radical idea: H.O.N.K mechs but with no bananium required.",
+ "Best idea ever: Disposal pipes instead of hallways."))
+
+//this updates all special effects: stun, sleeping, knockdown, druggy, stuttering, etc..
+/mob/living/carbon/handle_status_effects()
+ ..()
+ if(getStaminaLoss() && !combatmode)//CIT CHANGE - prevents stamina regen while combat mode is active
+ adjustStaminaLoss(resting ? (recoveringstam ? -7.5 : -3) : -1.5)
+
+ if(!recoveringstam && incomingstammult != 1)
+ incomingstammult = max(0.01, incomingstammult)
+ incomingstammult = min(1, incomingstammult*2)
+
+ //CIT CHANGES START HERE. STAMINA BUFFER STUFF
+ if(bufferedstam && world.time > stambufferregentime)
+ var/drainrate = max((bufferedstam*(bufferedstam/(5)))*0.1,1)
+ bufferedstam = max(bufferedstam - drainrate, 0)
+ adjustStaminaLoss(drainrate*0.5)
+ //END OF CIT CHANGES
+
+ var/restingpwr = 1 + 4 * resting
+
+ if(ckey)
+ if(!client && !(stat == DEAD))
+ add_status_indicator("ssd")
+ SSD = TRUE
+ else
+ if(SSD)
+ remove_status_indicator("ssd")
+ SSD = FALSE
+
+ //Dizziness
+ if(dizziness)
+ var/client/C = client
+ var/pixel_x_diff = 0
+ var/pixel_y_diff = 0
+ var/temp
+ var/saved_dizz = dizziness
+ if(C)
+ var/oldsrc = src
+ var/amplitude = dizziness*(sin(dizziness * world.time) + 1) // This shit is annoying at high strength
+ src = null
+ spawn(0)
+ if(C)
+ temp = amplitude * sin(saved_dizz * world.time)
+ pixel_x_diff += temp
+ C.pixel_x += temp
+ temp = amplitude * cos(saved_dizz * world.time)
+ pixel_y_diff += temp
+ C.pixel_y += temp
+ sleep(3)
+ if(C)
+ temp = amplitude * sin(saved_dizz * world.time)
+ pixel_x_diff += temp
+ C.pixel_x += temp
+ temp = amplitude * cos(saved_dizz * world.time)
+ pixel_y_diff += temp
+ C.pixel_y += temp
+ sleep(3)
+ if(C)
+ C.pixel_x -= pixel_x_diff
+ C.pixel_y -= pixel_y_diff
+ src = oldsrc
+ dizziness = max(dizziness - restingpwr, 0)
+
+ if(drowsyness)
+ drowsyness = max(drowsyness - restingpwr, 0)
+ blur_eyes(2)
+ if(prob(5))
+ AdjustSleeping(20)
+ Unconscious(100)
+
+ //Jitteriness
+ if(jitteriness)
+ do_jitter_animation(jitteriness)
+ jitteriness = max(jitteriness - restingpwr, 0)
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "jittery", /datum/mood_event/jittery)
+ else
+ SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "jittery")
+
+ if(stuttering)
+ stuttering = max(stuttering-1, 0)
+
+ if(slurring || drunkenness)
+ slurring = max(slurring-1,0,drunkenness)
+
+ if(cultslurring)
+ cultslurring = max(cultslurring-1, 0)
+
+ if(silent)
+ silent = max(silent-1, 0)
+
+ if(druggy)
+ adjust_drugginess(-1)
+
+ if(hallucination)
+ handle_hallucinations()
+
+ if(drunkenness)
+ drunkenness = max(drunkenness - (drunkenness * 0.04), 0)
+ if(drunkenness >= 6)
+ SEND_SIGNAL(src, COMSIG_ADD_MOOD_EVENT, "drunk", /datum/mood_event/drunk)
+ jitteriness = max(jitteriness - 3, 0)
+ if(HAS_TRAIT(src, TRAIT_DRUNK_HEALING))
+ adjustBruteLoss(-0.12, FALSE)
+ adjustFireLoss(-0.06, FALSE)
+
+ if(mind && (mind.assigned_role == "Scientist" || mind.assigned_role == "Research Director"))
+ if(SSresearch.science_tech)
+ if(drunkenness >= 12.9 && drunkenness <= 13.8)
+ drunkenness = round(drunkenness, 0.01)
+ var/ballmer_percent = 0
+ if(drunkenness == 13.35) // why run math if I dont have to
+ ballmer_percent = 1
+ else
+ ballmer_percent = (-abs(drunkenness - 13.35) / 0.9) + 1
+ if(prob(5))
+ say(pick(GLOB.ballmer_good_msg), forced = "ballmer")
+ SSresearch.science_tech.add_point_list(list(TECHWEB_POINT_TYPE_GENERIC = BALLMER_POINTS * ballmer_percent))
+ if(drunkenness > 26) // by this point you're into windows ME territory
+ if(prob(5))
+ SSresearch.science_tech.remove_point_list(list(TECHWEB_POINT_TYPE_GENERIC = BALLMER_POINTS))
+ say(pick(GLOB.ballmer_windows_me_msg), forced = "ballmer")
+
+ if(drunkenness >= 41)
+ if(prob(25))
+ confused += 2
+ Dizzy(10)
+ if(HAS_TRAIT(src, TRAIT_DRUNK_HEALING)) // effects stack with lower tiers
+ adjustBruteLoss(-0.3, FALSE)
+ adjustFireLoss(-0.15, FALSE)
+
+ if(drunkenness >= 51)
+ if(prob(5))
+ confused += 10
+ vomit()
+ Dizzy(25)
+
+ if(drunkenness >= 61)
+ if(prob(50))
+ blur_eyes(5)
+ if(HAS_TRAIT(src, TRAIT_DRUNK_HEALING))
+ adjustBruteLoss(-0.4, FALSE)
+ adjustFireLoss(-0.2, FALSE)
+
+ if(drunkenness >= 71)
+ blur_eyes(5)
+
+ if(drunkenness >= 81)
+ adjustToxLoss(0.2)
+ if(prob(5) && !stat)
+ to_chat(src, "Maybe you should lie down for a bit...")
+
+ if(drunkenness >= 91)
+ adjustOrganLoss(ORGAN_SLOT_BRAIN, 0.4, 60)
+ if(prob(20) && !stat)
+ if(SSshuttle.emergency.mode == SHUTTLE_DOCKED && is_station_level(z)) //QoL mainly
+ to_chat(src, "You're so tired... but you can't miss that shuttle...")
+ else
+ to_chat(src, "Just a quick nap...")
+ Sleeping(900)
+
+ if(drunkenness >= 101)
+ adjustToxLoss(4) //Let's be honest you shouldn't be alive by now
+ else
+ SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "drunk")
+
+//used in human and monkey handle_environment()
+/mob/living/carbon/proc/natural_bodytemperature_stabilization()
+ if (HAS_TRAIT(src, TRAIT_COLDBLOODED))
+ return 0 //Return 0 as your natural temperature. Species proc handle_environment() will adjust your temperature based on this.
+
+ var/body_temperature_difference = BODYTEMP_NORMAL - bodytemperature
+ switch(bodytemperature)
+ if(-INFINITY to BODYTEMP_COLD_DAMAGE_LIMIT) //Cold damage limit is 50 below the default, the temperature where you start to feel effects.
+ return max((body_temperature_difference * metabolism_efficiency / BODYTEMP_AUTORECOVERY_DIVISOR), BODYTEMP_AUTORECOVERY_MINIMUM)
+ if(BODYTEMP_COLD_DAMAGE_LIMIT to BODYTEMP_NORMAL)
+ return max(body_temperature_difference * metabolism_efficiency / BODYTEMP_AUTORECOVERY_DIVISOR, min(body_temperature_difference, BODYTEMP_AUTORECOVERY_MINIMUM/4))
+ if(BODYTEMP_NORMAL to BODYTEMP_HEAT_DAMAGE_LIMIT) // Heat damage limit is 50 above the default, the temperature where you start to feel effects.
+ return min(body_temperature_difference * metabolism_efficiency / BODYTEMP_AUTORECOVERY_DIVISOR, max(body_temperature_difference, -BODYTEMP_AUTORECOVERY_MINIMUM/4))
+ if(BODYTEMP_HEAT_DAMAGE_LIMIT to INFINITY)
+ return min((body_temperature_difference / BODYTEMP_AUTORECOVERY_DIVISOR), -BODYTEMP_AUTORECOVERY_MINIMUM) //We're dealing with negative numbers
+/////////
+//LIVER//
+/////////
+
+/mob/living/carbon/proc/handle_liver()
+ var/obj/item/organ/liver/liver = getorganslot(ORGAN_SLOT_LIVER)
+ if((!dna && !liver) || (NOLIVER in dna.species.species_traits))
+ return
+ if(liver)
+ if(liver.damage < liver.maxHealth)
+ liver.organ_flags |= ORGAN_FAILING
+ liver_failure()
+ else
+ liver_failure()
+
+/mob/living/carbon/proc/undergoing_liver_failure()
+ var/obj/item/organ/liver/liver = getorganslot(ORGAN_SLOT_LIVER)
+ if(liver && liver.failing)
+ return TRUE
+
+/mob/living/carbon/proc/return_liver_damage()
+ var/obj/item/organ/liver/liver = getorganslot(ORGAN_SLOT_LIVER)
+ if(liver)
+ return liver.damage
+
+/mob/living/carbon/proc/applyLiverDamage(var/d)
+ var/obj/item/organ/liver/L = getorganslot(ORGAN_SLOT_LIVER)
+ if(L)
+ L.damage += d
+
+/mob/living/carbon/proc/liver_failure()
+ reagents.end_metabolization(src, keep_liverless = TRUE) //Stops trait-based effects on reagents, to prevent permanent buffs
+ reagents.metabolize(src, can_overdose=FALSE, liverless = TRUE)
+ if(HAS_TRAIT(src, TRAIT_STABLELIVER))
+ return
+ adjustToxLoss(4, TRUE, TRUE)
+ if(prob(30))
+ to_chat(src, "You feel a stabbing pain in your abdomen!")
+
+
+////////////////
+//BRAIN DAMAGE//
+////////////////
+
+/mob/living/carbon/proc/handle_brain_damage()
+ for(var/T in get_traumas())
+ var/datum/brain_trauma/BT = T
+ BT.on_life()
+
+/////////////////////////////////////
+//MONKEYS WITH TOO MUCH CHOLOESTROL//
+/////////////////////////////////////
+
+/mob/living/carbon/proc/can_heartattack()
+ if(!needs_heart())
+ return FALSE
+ var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART)
+ if(!heart || (heart.organ_flags & ORGAN_SYNTHETIC))
+ return FALSE
+ return TRUE
+
+/mob/living/carbon/proc/needs_heart()
+ if(HAS_TRAIT(src, TRAIT_STABLEHEART))
+ return FALSE
+ if(dna && dna.species && (NOBLOOD in dna.species.species_traits)) //not all carbons have species!
+ return FALSE
+ return TRUE
+
+/mob/living/carbon/proc/undergoing_cardiac_arrest()
+ var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART)
+ if(istype(heart) && heart.beating)
+ return FALSE
+ else if(!needs_heart())
+ return FALSE
+ return TRUE
+
+/mob/living/carbon/proc/set_heartattack(status)
+ if(!can_heartattack())
+ return FALSE
+
+ var/obj/item/organ/heart/heart = getorganslot(ORGAN_SLOT_HEART)
+ if(!istype(heart))
+ return
+
+ heart.beating = !status
diff --git a/code/modules/surgery/organs/lungs.dm b/code/modules/surgery/organs/lungs.dm
index dfcd09b1..dd9db207 100644
--- a/code/modules/surgery/organs/lungs.dm
+++ b/code/modules/surgery/organs/lungs.dm
@@ -370,7 +370,7 @@
//Miasma sickness
if(prob(0.05 * miasma_pp))
- var/datum/disease/advance/miasma_disease = new /datum/disease/advance/random(2,3)
+ var/datum/disease/advance/miasma_disease = new /datum/disease/advance/random(TRUE, 2,3)
miasma_disease.name = "Unknown"
miasma_disease.try_infect(owner)