/mob/living/carbon/BiologicalLife(seconds, times_fired) //Updates the number of stored chemicals for powers handle_changeling() //Handles the unique mentabolism of bloodsuckers, look at /datum/antagonist/bloodsucker/proc/LifeTick() handle_bloodsucker() //Reagent processing needs to come before breathing, to prevent edge cases. handle_organs() . = ..() // if . is false, we are dead. if(stat == DEAD) stop_sound_channel(CHANNEL_HEARTBEAT) handle_death() rot() . = FALSE if(!.) return handle_blood() // handle_blood *could* kill us. // we should probably have a better system for if we need to check for death or something in the future hmw if(stat != DEAD) var/bprv = handle_bodyparts() if(bprv & BODYPART_LIFE_UPDATE_HEALTH) updatehealth() update_stamina() doSprintBufferRegen() if(stat != DEAD) handle_brain_damage() if(stat != DEAD) handle_liver() /mob/living/carbon/PhysicalLife(seconds, times_fired) if(!(. = ..())) return if(damageoverlaytemp) damageoverlaytemp = 0 update_damage_hud() //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) 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.return_temperature())/BREATH_VOLUME var/O2_partialpressure = (breath.get_moles(/datum/gas/oxygen)/breath.total_moles())*breath_pressure var/Toxins_partialpressure = (breath.get_moles(/datum/gas/plasma)/breath.total_moles())*breath_pressure var/CO2_partialpressure = (breath.get_moles(/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.get_moles(/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.get_moles(/datum/gas/oxygen) clear_alert("not_enough_oxy") SEND_SIGNAL(src, COMSIG_CLEAR_MOOD_EVENT, "suffocation") breath.adjust_moles(/datum/gas/oxygen, -oxygen_used) breath.adjust_moles(/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.get_moles(/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.get_moles(/datum/gas/nitrous_oxide)) var/SA_partialpressure = (breath.get_moles(/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.get_moles(/datum/gas/bz)) var/bz_partialpressure = (breath.get_moles(/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.get_moles(/datum/gas/tritium)) var/tritium_partialpressure = (breath.get_moles(/datum/gas/tritium)/breath.total_moles())*breath_pressure radiation += tritium_partialpressure/10 //NITRYL if(breath.get_moles(/datum/gas/nitryl)) var/nitryl_partialpressure = (breath.get_moles(/datum/gas/nitryl)/breath.total_moles())*breath_pressure adjustFireLoss(nitryl_partialpressure/4) //MIASMA if(breath.get_moles(/datum/gas/miasma)) var/miasma_partialpressure = (breath.get_moles(/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") //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 if(!HAS_TRAIT(src, TRAIT_NO_INTERNALS)) 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/preservahyde in corpse or when the corpse is charred if(reagents.has_reagent(/datum/reagent/toxin/formaldehyde, 1) || HAS_TRAIT(src, TRAIT_HUSK) || reagents.has_reagent(/datum/reagent/preservahyde, 1)) return // Also no decay if corpse chilled or not organic/undead if((bodytemperature <= T0C-10) || !(mob_biotypes & (MOB_ORGANIC|MOB_UNDEAD))) 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/datum/gas_mixture/stank = new stank.set_moles(/datum/gas/miasma,0.1) stank.set_temperature(BODYTEMP_NORMAL) miasma_turf.assume_air(stank) miasma_turf.air_update_turf() /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) || reagents.has_reagent(/datum/reagent/preservahyde, 1)) // No organ decay if the body contains formaldehyde. Or preservahyde. 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() /mob/living/carbon/handle_wounds() for(var/thing in all_wounds) var/datum/wound/W = thing if(W.processes) // meh W.handle_process() //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 = "