mirror of
https://github.com/Bubberstation/Bubberstation.git
synced 2026-08-24 05:30:05 +01:00
1173 lines
52 KiB
Plaintext
1173 lines
52 KiB
Plaintext
/obj/item/organ/lungs
|
|
name = "lungs"
|
|
icon_state = "lungs"
|
|
|
|
zone = BODY_ZONE_CHEST
|
|
slot = ORGAN_SLOT_LUNGS
|
|
gender = PLURAL
|
|
w_class = WEIGHT_CLASS_SMALL
|
|
|
|
var/respiration_type = NONE // The type(s) of gas this lung needs for respiration
|
|
|
|
healing_factor = STANDARD_ORGAN_HEALING
|
|
decay_factor = STANDARD_ORGAN_DECAY * 0.9 // fails around 16.5 minutes, lungs are one of the last organs to die (of the ones we have)
|
|
|
|
low_threshold_passed = span_warning("You feel short of breath.")
|
|
high_threshold_passed = span_warning("You feel some sort of constriction around your chest as your breathing becomes shallow and rapid.")
|
|
now_fixed = span_warning("Your lungs seem to once again be able to hold air.")
|
|
low_threshold_cleared = span_info("You can breathe normally again.")
|
|
high_threshold_cleared = span_info("The constriction around your chest loosens as your breathing calms down.")
|
|
|
|
cell_line = CELL_LINE_ORGAN_LUNGS
|
|
cells_minimum = 1
|
|
cells_maximum = 2
|
|
|
|
visual = FALSE
|
|
|
|
var/failed = FALSE
|
|
var/operated = FALSE //whether we can still have our damages fixed through surgery
|
|
|
|
food_reagents = list(/datum/reagent/consumable/nutriment/organ_tissue = 5, /datum/reagent/medicine/salbutamol = 5)
|
|
|
|
/// Our previous breath's partial pressures, in the form gas id -> partial pressure
|
|
var/list/last_partial_pressures = list()
|
|
/// List of gas to treat as other gas, in the form list(inital_gas, treat_as, multiplier)
|
|
var/list/treat_as = list()
|
|
/// Assoc list of procs to run while a gas is present, in the form gas id -> proc_path
|
|
var/list/breath_present = list()
|
|
/// Assoc list of procs to run when a gas is immediately removed from the breath, in the form gas id -> proc_path
|
|
var/list/breath_lost = list()
|
|
/// Assoc list of procs to always run, in the form gas_id -> proc_path
|
|
var/list/breathe_always = list()
|
|
|
|
/// Gas mixture to breath out when we're done processing a breath
|
|
/// Will get emptied out when it's all done
|
|
var/datum/gas_mixture/immutable/breath_out
|
|
|
|
//Breath damage
|
|
//These thresholds are checked against what amounts to total_mix_pressure * (gas_type_mols/total_mols)
|
|
var/safe_oxygen_min = 16 // Minimum safe partial pressure of O2, in kPa
|
|
var/safe_oxygen_max = 0
|
|
var/safe_nitro_min = 0
|
|
var/safe_co2_max = 10 // Yes it's an arbitrary value who cares?
|
|
var/safe_plasma_min = 0
|
|
///How much breath partial pressure is a safe amount of plasma. 0 means that we are immune to plasma.
|
|
var/safe_plasma_max = 0.05
|
|
var/n2o_detect_min = 0.08 //Minimum n2o for effects
|
|
var/n2o_para_min = 1 //Sleeping agent
|
|
var/n2o_sleep_min = 5 //Sleeping agent
|
|
var/BZ_trip_balls_min = 1 //BZ gas
|
|
var/BZ_brain_damage_min = 10 //Give people some room to play around without killing the station
|
|
var/gas_stimulation_min = 0.002 // For, Pluoxium, Nitrium and Freon
|
|
///Minimum amount of healium to make you unconscious for 4 seconds
|
|
var/healium_para_min = 3
|
|
///Minimum amount of healium to knock you down for good
|
|
var/healium_sleep_min = 6
|
|
///Minimum amount of helium to affect speech
|
|
var/helium_speech_min = 5
|
|
///Whether these lungs react negatively to miasma
|
|
var/suffers_miasma = TRUE
|
|
// Vars for N2O/healium induced euphoria, stun, and sleep.
|
|
var/n2o_euphoria = EUPHORIA_LAST_FLAG
|
|
var/healium_euphoria = EUPHORIA_LAST_FLAG
|
|
|
|
/// All incoming breaths will have their pressure multiplied against this. Higher values allow more air to be breathed at once,
|
|
/// while lower values can cause suffocation in low pressure environments.
|
|
var/received_pressure_mult = 1
|
|
|
|
var/oxy_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
|
|
var/oxy_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
|
|
var/oxy_damage_type = OXY
|
|
var/nitro_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
|
|
var/nitro_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
|
|
var/nitro_damage_type = OXY
|
|
var/co2_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
|
|
var/co2_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
|
|
var/co2_damage_type = OXY
|
|
var/plas_breath_dam_min = MIN_TOXIC_GAS_DAMAGE
|
|
var/plas_breath_dam_max = MAX_TOXIC_GAS_DAMAGE
|
|
var/plas_damage_type = TOX
|
|
|
|
var/tritium_irradiation_moles_min = 1
|
|
var/tritium_irradiation_moles_max = 15
|
|
var/tritium_irradiation_probability_min = 10
|
|
var/tritium_irradiation_probability_max = 60
|
|
|
|
var/cold_message = "your face freezing and an icicle forming"
|
|
var/cold_level_1_threshold = 260
|
|
var/cold_level_2_threshold = 200
|
|
var/cold_level_3_threshold = 120
|
|
var/cold_level_1_damage = COLD_GAS_DAMAGE_LEVEL_1 //Keep in mind with gas damage levels, you can set these to be negative, if you want someone to heal, instead.
|
|
var/cold_level_2_damage = COLD_GAS_DAMAGE_LEVEL_2
|
|
var/cold_level_3_damage = COLD_GAS_DAMAGE_LEVEL_3
|
|
var/cold_damage_type = BURN
|
|
|
|
var/hot_message = "your face burning and a searing heat"
|
|
var/heat_level_1_threshold = 360
|
|
var/heat_level_2_threshold = 400
|
|
var/heat_level_3_threshold = 1000
|
|
var/heat_level_1_damage = HEAT_GAS_DAMAGE_LEVEL_1
|
|
var/heat_level_2_damage = HEAT_GAS_DAMAGE_LEVEL_2
|
|
var/heat_level_3_damage = HEAT_GAS_DAMAGE_LEVEL_3
|
|
var/heat_damage_type = BURN
|
|
|
|
var/crit_stabilizing_reagent = /datum/reagent/medicine/epinephrine
|
|
|
|
var/breath_noise = "steady in- and exhalation"
|
|
|
|
// assign the respiration_type
|
|
/obj/item/organ/lungs/Initialize(mapload)
|
|
. = ..()
|
|
breath_out = new(BREATH_VOLUME)
|
|
|
|
if(safe_nitro_min)
|
|
respiration_type |= RESPIRATION_N2
|
|
if(safe_oxygen_min)
|
|
respiration_type |= RESPIRATION_OXYGEN
|
|
if(safe_plasma_min)
|
|
respiration_type |= RESPIRATION_PLASMA
|
|
|
|
// Sets up what gases we want to react to, and in what way
|
|
// always is always processed, while_present is called when the gas is in the breath, and on_loss is called right after a gas is lost
|
|
// The naming convention goes like this
|
|
// always : breath_{gas_type}
|
|
// safe/neutral while_present : consume_{gas_type}
|
|
// safe/neutral on_loss : lose_{gas_type}
|
|
// dangerous while_present : too_much_{gas_type}
|
|
// dangerous on_loss : safe_{gas_type}
|
|
// These are reccomendations, if something seems better feel free to ignore them. S a bit vibes based
|
|
if(safe_oxygen_min)
|
|
add_gas_reaction(/datum/gas/oxygen, always = PROC_REF(breathe_oxygen))
|
|
if(safe_oxygen_max)
|
|
add_gas_reaction(/datum/gas/oxygen, while_present = PROC_REF(too_much_oxygen), on_loss = PROC_REF(safe_oxygen))
|
|
add_gas_reaction(/datum/gas/pluoxium, while_present = PROC_REF(consume_pluoxium))
|
|
// We treat a mole of ploux as 8 moles of oxygen
|
|
add_gas_relationship(/datum/gas/pluoxium, /datum/gas/oxygen, 8)
|
|
if(safe_nitro_min)
|
|
add_gas_reaction(/datum/gas/nitrogen, always = PROC_REF(breathe_nitro))
|
|
if(safe_co2_max)
|
|
add_gas_reaction(/datum/gas/carbon_dioxide, while_present = PROC_REF(too_much_co2), on_loss = PROC_REF(safe_co2))
|
|
if(safe_plasma_min)
|
|
add_gas_reaction(/datum/gas/plasma, always = PROC_REF(breathe_plasma))
|
|
if(safe_plasma_max)
|
|
add_gas_reaction(/datum/gas/plasma, while_present = PROC_REF(too_much_plasma), on_loss = PROC_REF(safe_plasma))
|
|
add_gas_reaction(/datum/gas/bz, while_present = PROC_REF(too_much_bz))
|
|
add_gas_reaction(/datum/gas/freon, while_present = PROC_REF(too_much_freon))
|
|
add_gas_reaction(/datum/gas/halon, while_present = PROC_REF(too_much_halon))
|
|
add_gas_reaction(/datum/gas/healium, while_present = PROC_REF(consume_healium), on_loss = PROC_REF(lose_healium))
|
|
add_gas_reaction(/datum/gas/helium, while_present = PROC_REF(consume_helium), on_loss = PROC_REF(lose_helium))
|
|
add_gas_reaction(/datum/gas/hypernoblium, while_present = PROC_REF(consume_hypernoblium))
|
|
if(suffers_miasma)
|
|
add_gas_reaction(/datum/gas/miasma, while_present = PROC_REF(too_much_miasma), on_loss = PROC_REF(safe_miasma))
|
|
add_gas_reaction(/datum/gas/nitrous_oxide, while_present = PROC_REF(too_much_n2o), on_loss = PROC_REF(safe_n2o))
|
|
add_gas_reaction(/datum/gas/nitrium, while_present = PROC_REF(too_much_nitrium))
|
|
add_gas_reaction(/datum/gas/tritium, while_present = PROC_REF(too_much_tritium))
|
|
add_gas_reaction(/datum/gas/zauker, while_present = PROC_REF(too_much_zauker))
|
|
|
|
///Simply exists so that you don't keep any alerts from your previous lack of lungs.
|
|
/obj/item/organ/lungs/on_mob_insert(mob/living/carbon/receiver, special = FALSE, movement_flags)
|
|
. = ..()
|
|
|
|
receiver.clear_alert(ALERT_NOT_ENOUGH_OXYGEN)
|
|
receiver.clear_alert(ALERT_NOT_ENOUGH_CO2)
|
|
receiver.clear_alert(ALERT_NOT_ENOUGH_NITRO)
|
|
receiver.clear_alert(ALERT_NOT_ENOUGH_PLASMA)
|
|
receiver.clear_alert(ALERT_NOT_ENOUGH_N2O)
|
|
update_bronchodilation_alerts()
|
|
|
|
/obj/item/organ/lungs/on_mob_remove(mob/living/carbon/organ_owner, special, movement_flags)
|
|
. = ..()
|
|
// This is very "manual" I realize, but it's useful to ensure cleanup for gases we're removing happens
|
|
// Avoids stuck alerts and such
|
|
var/static/datum/gas_mixture/immutable/dummy = new(BREATH_VOLUME)
|
|
for(var/gas_id in last_partial_pressures)
|
|
var/on_loss = breath_lost[gas_id]
|
|
if(!on_loss)
|
|
continue
|
|
|
|
call(src, on_loss)(organ_owner, dummy, last_partial_pressures[gas_id])
|
|
dummy.garbage_collect()
|
|
|
|
/**
|
|
* Tells the lungs to pay attention to the passed in gas type
|
|
* We'll check for it when breathing, in a few possible ways
|
|
* Accepts 3 optional arguments:
|
|
*
|
|
* proc/while_present * Called while the gas is present in our breath. Return BREATH_LOST to call on_loss afterwards
|
|
* proc/on_loss * Called after we have lost a gas from our breath.
|
|
* proc/always * Always called. Best suited for breathing procs, like oxygen
|
|
*
|
|
* while_present and always get the same arguments (mob/living/carbon/breather, datum/gas_mixture/breath, pp, old_pp)
|
|
* on_loss is almost exactly the same, except it doesn't pass in a current partial pressure, since one isn't avalible
|
|
*/
|
|
/obj/item/organ/lungs/proc/add_gas_reaction(gas_type, while_present = null, on_loss = null, always = null)
|
|
if(always)
|
|
breathe_always[gas_type] = always
|
|
|
|
if(isnull(while_present) && isnull(on_loss))
|
|
return
|
|
|
|
if(while_present)
|
|
breath_present[gas_type] = while_present
|
|
if(on_loss)
|
|
breath_lost[gas_type] = on_loss
|
|
|
|
#define BREATH_RELATIONSHIP_INITIAL_GAS 1
|
|
#define BREATH_RELATIONSHIP_CONVERT 2
|
|
#define BREATH_RELATIONSHIP_MULTIPLIER 3
|
|
/**
|
|
* Tells the lungs to treat the passed in gas type as another passed in gas type
|
|
* Takes the gas to check for as an argument, alongside the gas to convert and the multiplier to use
|
|
* These act in the order of insertion, use that how you will
|
|
*/
|
|
/obj/item/organ/lungs/proc/add_gas_relationship(gas_type, convert_to, multiplier)
|
|
if(isnull(gas_type) || isnull(convert_to) || multiplier == 0)
|
|
return
|
|
|
|
var/list/add = new /list(BREATH_RELATIONSHIP_MULTIPLIER)
|
|
add[BREATH_RELATIONSHIP_INITIAL_GAS] = gas_type
|
|
add[BREATH_RELATIONSHIP_CONVERT] = convert_to
|
|
add[BREATH_RELATIONSHIP_MULTIPLIER] = multiplier
|
|
treat_as += list(add)
|
|
|
|
/// Clears away a gas relationship. Takes the same args as the initial addition
|
|
/obj/item/organ/lungs/proc/remove_gas_relationship(gas_type, convert_to, multiplier)
|
|
if(isnull(gas_type) || isnull(convert_to) || multiplier == 0)
|
|
return
|
|
|
|
for(var/packet in treat_as)
|
|
if(packet[BREATH_RELATIONSHIP_INITIAL_GAS] != gas_type)
|
|
continue
|
|
if(packet[BREATH_RELATIONSHIP_CONVERT] != convert_to)
|
|
continue
|
|
if(packet[BREATH_RELATIONSHIP_MULTIPLIER] != multiplier)
|
|
continue
|
|
treat_as -= packet
|
|
return
|
|
|
|
/// Handles oxygen breathing. Always called by things that need o2, no matter what
|
|
/obj/item/organ/lungs/proc/breathe_oxygen(mob/living/carbon/breather, datum/gas_mixture/breath, o2_pp, old_o2_pp)
|
|
if(o2_pp < safe_oxygen_min && !HAS_TRAIT(breather, TRAIT_NO_BREATHLESS_DAMAGE))
|
|
// Not safe to check the old pp because of can_breath_vacuum
|
|
breather.throw_alert(ALERT_NOT_ENOUGH_OXYGEN, /atom/movable/screen/alert/not_enough_oxy)
|
|
|
|
var/gas_breathed = handle_suffocation(breather, o2_pp, safe_oxygen_min, breath.gases[/datum/gas/oxygen][MOLES])
|
|
if(o2_pp)
|
|
breathe_gas_volume(breath, /datum/gas/oxygen, /datum/gas/carbon_dioxide, volume = gas_breathed)
|
|
return
|
|
|
|
// If we used to not have enough, clear the alert
|
|
// Note this can be redundant, because of the vacuum check. It is fail safe tho, so it's ok
|
|
if(old_o2_pp < safe_oxygen_min)
|
|
breather.failed_last_breath = FALSE
|
|
breather.clear_alert(ALERT_NOT_ENOUGH_OXYGEN)
|
|
|
|
breathe_gas_volume(breath, /datum/gas/oxygen, /datum/gas/carbon_dioxide)
|
|
// Heal mob if not in crit.
|
|
if(breather.health >= breather.crit_threshold && breather.oxyloss)
|
|
breather.adjust_oxy_loss(-5)
|
|
|
|
/// Maximum Oxygen effects. "Too much O2!"
|
|
/obj/item/organ/lungs/proc/too_much_oxygen(mob/living/carbon/breather, datum/gas_mixture/breath, o2_pp, old_o2_pp)
|
|
// If too much Oxygen is poisonous.
|
|
if(o2_pp <= safe_oxygen_max)
|
|
if(old_o2_pp > safe_oxygen_max)
|
|
return BREATH_LOST
|
|
return
|
|
|
|
var/ratio = (breath.gases[/datum/gas/oxygen][MOLES] / safe_oxygen_max) * 10
|
|
breather.apply_damage(clamp(ratio, oxy_breath_dam_min, oxy_breath_dam_max), oxy_damage_type, spread_damage = TRUE)
|
|
if(!HAS_TRAIT(breather, TRAIT_ANOSMIA))
|
|
breather.throw_alert(ALERT_TOO_MUCH_OXYGEN, /atom/movable/screen/alert/too_much_oxy)
|
|
|
|
/// Handles NOT having too much o2. only relevant if safe_oxygen_max has a value
|
|
/obj/item/organ/lungs/proc/safe_oxygen(mob/living/carbon/breather, datum/gas_mixture/breath, old_o2_pp)
|
|
breather.clear_alert(ALERT_TOO_MUCH_OXYGEN)
|
|
|
|
/// Behaves like Oxygen with 8X efficacy, but metabolizes into a reagent.
|
|
/obj/item/organ/lungs/proc/consume_pluoxium(mob/living/carbon/breather, datum/gas_mixture/breath, pluoxium_pp, old_pluoxium_pp)
|
|
breathe_gas_volume(breath, /datum/gas/pluoxium)
|
|
// Metabolize to reagent.
|
|
if(pluoxium_pp > gas_stimulation_min)
|
|
var/existing = breather.reagents.get_reagent_amount(/datum/reagent/pluoxium)
|
|
breather.reagents.add_reagent(/datum/reagent/pluoxium, max(0, 1 - existing))
|
|
|
|
/// If the lungs need Nitrogen to breathe properly, N2 is exchanged with CO2.
|
|
/obj/item/organ/lungs/proc/breathe_nitro(mob/living/carbon/breather, datum/gas_mixture/breath, nitro_pp, old_nitro_pp)
|
|
if(nitro_pp < safe_nitro_min && !HAS_TRAIT(breather, TRAIT_NO_BREATHLESS_DAMAGE))
|
|
// Suffocation side-effects.
|
|
// Not safe to check the old pp because of can_breath_vacuum
|
|
if(!HAS_TRAIT(breather, TRAIT_ANOSMIA))
|
|
breather.throw_alert(ALERT_NOT_ENOUGH_NITRO, /atom/movable/screen/alert/not_enough_nitro)
|
|
var/gas_breathed = handle_suffocation(breather, nitro_pp, safe_nitro_min, breath.gases[/datum/gas/nitrogen][MOLES])
|
|
if(nitro_pp)
|
|
breathe_gas_volume(breath, /datum/gas/nitrogen, /datum/gas/carbon_dioxide, volume = gas_breathed)
|
|
return
|
|
|
|
if(old_nitro_pp < safe_nitro_min)
|
|
breather.failed_last_breath = FALSE
|
|
breather.clear_alert(ALERT_NOT_ENOUGH_NITRO)
|
|
|
|
// Inhale N2, exhale equivalent amount of CO2. Look ma, sideways breathing!
|
|
breathe_gas_volume(breath, /datum/gas/nitrogen, /datum/gas/carbon_dioxide)
|
|
// Heal mob if not in crit.
|
|
if(breather.health >= breather.crit_threshold && breather.oxyloss)
|
|
breather.adjust_oxy_loss(-5)
|
|
|
|
/// Maximum CO2 effects. "Too much CO2!"
|
|
/obj/item/organ/lungs/proc/too_much_co2(mob/living/carbon/breather, datum/gas_mixture/breath, co2_pp, old_co2_pp)
|
|
if(co2_pp <= safe_co2_max)
|
|
if(old_co2_pp > safe_co2_max)
|
|
return BREATH_LOST
|
|
return
|
|
|
|
// If it's the first breath with too much CO2 in it, lets start a counter, then have them pass out after 12s or so.
|
|
if(old_co2_pp < safe_co2_max)
|
|
breather.co2overloadtime = world.time
|
|
|
|
// CO2 side-effects.
|
|
// Give the mob a chance to notice.
|
|
if(prob(20))
|
|
breather.emote("cough")
|
|
|
|
if((world.time - breather.co2overloadtime) > 12 SECONDS)
|
|
if(!HAS_TRAIT(breather, TRAIT_ANOSMIA))
|
|
breather.throw_alert(ALERT_TOO_MUCH_CO2, /atom/movable/screen/alert/too_much_co2)
|
|
breather.Unconscious(6 SECONDS)
|
|
// Lets hurt em a little, let them know we mean business.
|
|
breather.apply_damage(3, co2_damage_type, spread_damage = TRUE)
|
|
// They've been in here 30s now, start to kill them for their own good!
|
|
if((world.time - breather.co2overloadtime) > 30 SECONDS)
|
|
breather.apply_damage(8, co2_damage_type, spread_damage = TRUE)
|
|
|
|
/// Handles NOT having too much co2. only relevant if safe_co2_max has a value
|
|
/obj/item/organ/lungs/proc/safe_co2(mob/living/carbon/breather, datum/gas_mixture/breath, old_co2_pp)
|
|
// Reset side-effects.
|
|
breather.co2overloadtime = 0
|
|
breather.clear_alert(ALERT_TOO_MUCH_CO2)
|
|
|
|
/// If the lungs need Plasma to breathe properly, Plasma is exchanged with CO2.
|
|
/obj/item/organ/lungs/proc/breathe_plasma(mob/living/carbon/breather, datum/gas_mixture/breath, plasma_pp, old_plasma_pp)
|
|
// Suffocation side-effects.
|
|
if(plasma_pp < safe_plasma_min && !HAS_TRAIT(breather, TRAIT_NO_BREATHLESS_DAMAGE))
|
|
// Could check old_plasma_pp but vacuum breathing hates me
|
|
if(!HAS_TRAIT(breather, TRAIT_ANOSMIA))
|
|
breather.throw_alert(ALERT_NOT_ENOUGH_PLASMA, /atom/movable/screen/alert/not_enough_plas)
|
|
// Breathe insufficient amount of Plasma, exhale CO2.
|
|
var/gas_breathed = handle_suffocation(breather, plasma_pp, safe_plasma_min, breath.gases[/datum/gas/plasma][MOLES])
|
|
if(plasma_pp)
|
|
breathe_gas_volume(breath, /datum/gas/plasma, /datum/gas/carbon_dioxide, volume = gas_breathed)
|
|
return
|
|
|
|
if(old_plasma_pp < safe_plasma_min)
|
|
breather.failed_last_breath = FALSE
|
|
breather.clear_alert(ALERT_NOT_ENOUGH_PLASMA)
|
|
// Inhale Plasma, exhale equivalent amount of CO2.
|
|
breathe_gas_volume(breath, /datum/gas/plasma, /datum/gas/carbon_dioxide)
|
|
// Heal mob if not in crit.
|
|
if(breather.health >= breather.crit_threshold && breather.oxyloss)
|
|
breather.adjust_oxy_loss(-5)
|
|
|
|
/// Maximum Plasma effects. "Too much Plasma!"
|
|
/obj/item/organ/lungs/proc/too_much_plasma(mob/living/carbon/breather, datum/gas_mixture/breath, plasma_pp, old_plasma_pp)
|
|
if(plasma_pp <= safe_plasma_max)
|
|
if(old_plasma_pp > safe_plasma_max)
|
|
return BREATH_LOST
|
|
return
|
|
|
|
// If it's the first breath with too much CO2 in it, lets start a counter, then have them pass out after 12s or so.
|
|
if(old_plasma_pp < safe_plasma_max)
|
|
if(!HAS_TRAIT(breather, TRAIT_ANOSMIA))
|
|
breather.throw_alert(ALERT_TOO_MUCH_PLASMA, /atom/movable/screen/alert/too_much_plas)
|
|
|
|
var/ratio = (breath.gases[/datum/gas/plasma][MOLES] / safe_plasma_max) * 10
|
|
breather.apply_damage(clamp(ratio, plas_breath_dam_min, plas_breath_dam_max), plas_damage_type, spread_damage = TRUE)
|
|
|
|
/// Resets plasma side effects
|
|
/obj/item/organ/lungs/proc/safe_plasma(mob/living/carbon/breather, datum/gas_mixture/breath, old_plasma_pp)
|
|
breather.clear_alert(ALERT_TOO_MUCH_PLASMA)
|
|
|
|
/// Too much funny gas, time to get brain damage
|
|
/obj/item/organ/lungs/proc/too_much_bz(mob/living/carbon/breather, datum/gas_mixture/breath, bz_pp, old_bz_pp)
|
|
if(bz_pp > BZ_trip_balls_min)
|
|
breather.reagents.add_reagent(/datum/reagent/bz_metabolites, clamp(bz_pp, 1, 5))
|
|
if(bz_pp > BZ_brain_damage_min && prob(33))
|
|
breather.adjust_organ_loss(ORGAN_SLOT_BRAIN, 3, 150, ORGAN_ORGANIC)
|
|
|
|
/// Breathing in refridgerator coolent, shit's caustic
|
|
/obj/item/organ/lungs/proc/too_much_freon(mob/living/carbon/breather, datum/gas_mixture/breath, freon_pp, old_freon_pp)
|
|
// Inhale Freon. Exhale nothing.
|
|
breathe_gas_volume(breath, /datum/gas/freon)
|
|
if (freon_pp > gas_stimulation_min)
|
|
breather.reagents.add_reagent(/datum/reagent/freon, 1)
|
|
if (prob(freon_pp))
|
|
to_chat(breather, span_alert("Your mouth feels like it's burning!"))
|
|
if (freon_pp > 40)
|
|
breather.emote("gasp")
|
|
breather.adjust_fire_loss(15)
|
|
if (prob(freon_pp / 2))
|
|
to_chat(breather, span_alert("Your throat closes up!"))
|
|
breather.set_silence_if_lower(6 SECONDS)
|
|
else
|
|
breather.adjust_fire_loss(freon_pp / 4)
|
|
|
|
/// Breathing in halon, convert it to a reagent
|
|
/obj/item/organ/lungs/proc/too_much_halon(mob/living/carbon/breather, datum/gas_mixture/breath, halon_pp, old_halon_pp)
|
|
// Inhale Halon. Exhale nothing.
|
|
breathe_gas_volume(breath, /datum/gas/halon)
|
|
// Metabolize to reagent.
|
|
if(halon_pp > gas_stimulation_min)
|
|
breather.adjust_oxy_loss(5)
|
|
breather.reagents.add_reagent(/datum/reagent/halon, max(0, 1 - breather.reagents.get_reagent_amount(/datum/reagent/halon)))
|
|
|
|
/// Sleeping gas with healing properties.
|
|
/obj/item/organ/lungs/proc/consume_healium(mob/living/carbon/breather, datum/gas_mixture/breath, healium_pp, old_healium_pp)
|
|
breathe_gas_volume(breath, /datum/gas/healium)
|
|
// Euphoria side-effect.
|
|
if(healium_pp > gas_stimulation_min)
|
|
if(prob(15))
|
|
to_chat(breather, span_alert("Your head starts spinning and your lungs burn!"))
|
|
healium_euphoria = EUPHORIA_ACTIVE
|
|
breather.emote("gasp")
|
|
else
|
|
healium_euphoria = EUPHORIA_INACTIVE
|
|
// Stun/Sleep side-effects.
|
|
if(healium_pp > healium_para_min && !breather.IsSleeping() && prob(30))
|
|
breather.Sleeping(rand(3 SECONDS, 5 SECONDS))
|
|
// Metabolize to reagent when concentration is high enough.
|
|
if(healium_pp > healium_sleep_min)
|
|
breather.reagents.add_reagent(/datum/reagent/healium, max(0, 1 - breather.reagents.get_reagent_amount(/datum/reagent/healium)))
|
|
|
|
/// Lose healium side effects
|
|
/obj/item/organ/lungs/proc/lose_healium(mob/living/carbon/breather, datum/gas_mixture/breath, old_healium_pp)
|
|
healium_euphoria = EUPHORIA_INACTIVE
|
|
|
|
/// Activates helium speech when partial pressure gets high enough
|
|
/obj/item/organ/lungs/proc/consume_helium(mob/living/carbon/breather, datum/gas_mixture/breath, helium_pp, old_helium_pp)
|
|
breathe_gas_volume(breath, /datum/gas/helium)
|
|
if(helium_pp > helium_speech_min)
|
|
if(old_helium_pp <= helium_speech_min)
|
|
RegisterSignal(breather, COMSIG_MOB_SAY, PROC_REF(handle_helium_speech))
|
|
else
|
|
if(old_helium_pp > helium_speech_min)
|
|
UnregisterSignal(breather, COMSIG_MOB_SAY)
|
|
|
|
/// Lose helium high pitched voice
|
|
/obj/item/organ/lungs/proc/lose_helium(mob/living/carbon/breather, datum/gas_mixture/breath, old_helium_pp)
|
|
UnregisterSignal(breather, COMSIG_MOB_SAY)
|
|
|
|
/// React to speach while hopped up on the high pitched voice juice
|
|
/obj/item/organ/lungs/proc/handle_helium_speech(mob/living/carbon/breather, list/speech_args)
|
|
SIGNAL_HANDLER
|
|
speech_args[SPEECH_SPANS] |= SPAN_SMALL_VOICE
|
|
|
|
/// Gain hypernob effects if we have enough of the stuff
|
|
/obj/item/organ/lungs/proc/consume_hypernoblium(mob/living/carbon/breather, datum/gas_mixture/breath, hypernob_pp, old_hypernob_pp)
|
|
breathe_gas_volume(breath, /datum/gas/hypernoblium)
|
|
if (hypernob_pp > gas_stimulation_min)
|
|
var/existing = breather.reagents.get_reagent_amount(/datum/reagent/hypernoblium)
|
|
breather.reagents.add_reagent(/datum/reagent/hypernoblium,max(0, 1 - existing))
|
|
|
|
/// Breathing in the stink gas
|
|
/obj/item/organ/lungs/proc/too_much_miasma(mob/living/carbon/breather, datum/gas_mixture/breath, miasma_pp, old_miasma_pp)
|
|
// Inhale Miasma. Exhale nothing.
|
|
breathe_gas_volume(breath, /datum/gas/miasma)
|
|
// Miasma side effects
|
|
if (HAS_TRAIT(breather, TRAIT_ANOSMIA)) //Anosmia quirk holder cannot smell miasma, but can get diseases from it.
|
|
return
|
|
switch(miasma_pp)
|
|
if(1 to 5) // BUBBER EDIT CHANGE: Original: 0.25 to 5
|
|
// At lower pp, give out a little warning
|
|
breather.clear_mood_event("smell")
|
|
if(prob(5))
|
|
to_chat(breather, span_notice("There is an unpleasant smell in the air."))
|
|
if(5 to 15)
|
|
//At somewhat higher pp, warning becomes more obvious
|
|
if(prob(15))
|
|
to_chat(breather, span_warning("You smell something horribly decayed inside this room."))
|
|
breather.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(breather, span_warning("The stench of rotting carcasses is unbearable!"))
|
|
breather.add_mood_event("smell", /datum/mood_event/disgust/nauseating_stench)
|
|
breather.vomit(VOMIT_CATEGORY_DEFAULT)
|
|
if(30 to INFINITY)
|
|
//Higher chance to vomit. Let the horror start
|
|
if(prob(15))
|
|
to_chat(breather, span_warning("The stench of rotting carcasses is unbearable!"))
|
|
breather.add_mood_event("smell", /datum/mood_event/disgust/nauseating_stench)
|
|
breather.vomit(VOMIT_CATEGORY_DEFAULT)
|
|
else
|
|
breather.clear_mood_event("smell")
|
|
// In a full miasma atmosphere with 101.34 pKa, about 10 disgust per breath, is pretty low compared to threshholds
|
|
// Then again, this is a purely hypothetical scenario and hardly reachable
|
|
breather.adjust_disgust(0.1 * miasma_pp)
|
|
|
|
/// We're free from the stick, clear out its impacts
|
|
/obj/item/organ/lungs/proc/safe_miasma(mob/living/carbon/breather, datum/gas_mixture/breath, old_miasma_pp)
|
|
// Clear out moods when immune to miasma, or if there's no miasma at all.
|
|
breather.clear_mood_event("smell")
|
|
|
|
/// Causes random euphoria and giggling. Large amounts knock you down
|
|
/obj/item/organ/lungs/proc/too_much_n2o(mob/living/carbon/breather, datum/gas_mixture/breath, n2o_pp, old_n2o_pp)
|
|
if(n2o_pp < n2o_para_min)
|
|
// Small amount of N2O, small side-effects.
|
|
if(n2o_pp <= n2o_detect_min)
|
|
if(old_n2o_pp > n2o_detect_min)
|
|
return BREATH_LOST
|
|
return
|
|
// No alert for small amounts, but the mob randomly feels euphoric.
|
|
if(old_n2o_pp >= n2o_para_min || old_n2o_pp <= n2o_detect_min)
|
|
breather.clear_alert(ALERT_TOO_MUCH_N2O)
|
|
|
|
if(prob(20))
|
|
n2o_euphoria = EUPHORIA_ACTIVE
|
|
breather.emote(pick("giggle", "laugh"))
|
|
breather.set_drugginess(30 SECONDS)
|
|
else
|
|
n2o_euphoria = EUPHORIA_INACTIVE
|
|
return
|
|
|
|
// More N2O, more severe side-effects. Causes stun/sleep.
|
|
if(old_n2o_pp < n2o_para_min)
|
|
if(!HAS_TRAIT(breather, TRAIT_ANOSMIA))
|
|
breather.throw_alert(ALERT_TOO_MUCH_N2O, /atom/movable/screen/alert/too_much_n2o)
|
|
n2o_euphoria = EUPHORIA_ACTIVE
|
|
ADD_TRAIT(breather, TRAIT_ANALGESIA, OXYLOSS_TRAIT) // BUBBER EDIT ADDITION - N2O numbs pain
|
|
|
|
// give them one second of grace to wake up and run away a bit!
|
|
if(!HAS_TRAIT(breather, TRAIT_SLEEPIMMUNE))
|
|
breather.Unconscious(6 SECONDS)
|
|
// Enough to make the mob sleep.
|
|
if(n2o_pp > n2o_sleep_min)
|
|
breather.Sleeping(min(breather.AmountSleeping() + 100, 200))
|
|
|
|
/// N2O side-effects. "Too much N2O!"
|
|
/obj/item/organ/lungs/proc/safe_n2o(mob/living/carbon/breather, datum/gas_mixture/breath, old_n2o_pp)
|
|
n2o_euphoria = EUPHORIA_INACTIVE
|
|
breather.clear_alert(ALERT_TOO_MUCH_N2O)
|
|
REMOVE_TRAIT(breather, TRAIT_ANALGESIA, OXYLOSS_TRAIT) // BUBBER EDIT ADDITION - N2O numbs pain
|
|
|
|
// Breathe in nitrium. It's helpful, but has nasty side effects
|
|
/obj/item/organ/lungs/proc/too_much_nitrium(mob/living/carbon/breather, datum/gas_mixture/breath, nitrium_pp, old_nitrium_pp)
|
|
breathe_gas_volume(breath, /datum/gas/nitrium)
|
|
|
|
if(prob(20))
|
|
breather.emote("burp")
|
|
|
|
// Random chance to inflict side effects increases with pressure.
|
|
if((prob(nitrium_pp) && (nitrium_pp > 15)))
|
|
// Nitrium side-effect.
|
|
breather.adjust_organ_loss(ORGAN_SLOT_LUNGS, nitrium_pp * 0.1)
|
|
to_chat(breather, span_notice("You feel a burning sensation in your chest"))
|
|
// Metabolize to reagents.
|
|
if (nitrium_pp > 5)
|
|
var/existing = breather.reagents.get_reagent_amount(/datum/reagent/nitrium_low_metabolization)
|
|
breather.reagents.add_reagent(/datum/reagent/nitrium_low_metabolization, max(0, 2 - existing))
|
|
if (nitrium_pp > 10)
|
|
var/existing = breather.reagents.get_reagent_amount(/datum/reagent/nitrium_high_metabolization)
|
|
breather.reagents.add_reagent(/datum/reagent/nitrium_high_metabolization, max(0, 2 - existing))
|
|
|
|
/// Radioactive, green gas. Toxin damage, and a radiation chance
|
|
/obj/item/organ/lungs/proc/too_much_tritium(mob/living/carbon/breather, datum/gas_mixture/breath, trit_pp, old_trit_pp)
|
|
var/gas_breathed = breathe_gas_volume(breath, /datum/gas/tritium)
|
|
var/moles_visible = GLOB.meta_gas_info[/datum/gas/tritium][META_GAS_MOLES_VISIBLE] * BREATH_PERCENTAGE
|
|
// Tritium side-effects.
|
|
if(gas_breathed > moles_visible)
|
|
var/ratio = gas_breathed * 15
|
|
breather.adjust_tox_loss(clamp(ratio, MIN_TOXIC_GAS_DAMAGE, MAX_TOXIC_GAS_DAMAGE))
|
|
// If you're breathing in half an atmosphere of radioactive gas, you fucked up.
|
|
if((trit_pp > tritium_irradiation_moles_min) && SSradiation.can_irradiate_basic(breather))
|
|
var/lerp_scale = min(tritium_irradiation_moles_max, trit_pp - tritium_irradiation_moles_min) / (tritium_irradiation_moles_max - tritium_irradiation_moles_min)
|
|
var/chance = LERP(tritium_irradiation_probability_min, tritium_irradiation_probability_max, lerp_scale)
|
|
if (prob(chance))
|
|
breather.AddComponent(/datum/component/irradiated)
|
|
|
|
/// Really toxic stuff, very much trying to kill you
|
|
/obj/item/organ/lungs/proc/too_much_zauker(mob/living/carbon/breather, datum/gas_mixture/breath, zauker_pp, old_zauker_pp)
|
|
breathe_gas_volume(breath, /datum/gas/zauker)
|
|
// Metabolize to reagent.
|
|
if(zauker_pp > gas_stimulation_min)
|
|
var/existing = breather.reagents.get_reagent_amount(/datum/reagent/zauker)
|
|
breather.reagents.add_reagent(/datum/reagent/zauker, max(0, 1 - existing))
|
|
|
|
/**
|
|
* This proc tests if the lungs can breathe, if they can breathe a given gas mixture, and throws/clears gas alerts.
|
|
* It does this by calling subprocs "registered" to pay attention to different gas types
|
|
* There's also support for gases that should always be checked for, and procs that should run when a gas is finished
|
|
*
|
|
*
|
|
* Returns TRUE if the breath was successful, or FALSE if otherwise.
|
|
*
|
|
* Arguments:
|
|
* * breath: A gas mixture to test, or null.
|
|
* * breather: A carbon mob that is using the lungs to breathe.
|
|
*/
|
|
/obj/item/organ/lungs/proc/check_breath(datum/gas_mixture/breath, mob/living/carbon/human/breather)
|
|
if(HAS_TRAIT(breather, TRAIT_GODMODE))
|
|
breather.failed_last_breath = FALSE
|
|
breather.clear_alert(ALERT_NOT_ENOUGH_OXYGEN)
|
|
return FALSE
|
|
|
|
if(HAS_TRAIT(breather, TRAIT_NOBREATH))
|
|
return FALSE
|
|
|
|
// If the breath is falsy or "null", we can use the backup empty_breath.
|
|
if(!breath)
|
|
var/static/datum/gas_mixture/immutable/empty_breath = new(BREATH_VOLUME)
|
|
breath = empty_breath
|
|
|
|
// Indicates if there are moles of gas in the breath.
|
|
var/has_moles = breath.total_moles() != 0
|
|
|
|
// Check for moles of gas and handle partial pressures / special conditions.
|
|
if(has_moles)
|
|
// Breath has more than 0 moles of gas.
|
|
// Route gases through mask filter if breather is wearing one.
|
|
if(istype(breather.wear_mask) && (breather.wear_mask.clothing_flags & GAS_FILTERING) && breather.wear_mask.has_filter)
|
|
breath = breather.wear_mask.consume_filter(breath)
|
|
// Breath has 0 moles of gas, and we can breathe space
|
|
else if(HAS_TRAIT(breather, TRAIT_NO_BREATHLESS_DAMAGE))
|
|
// The lungs can breathe anyways. What are you? Some bottom-feeding, scum-sucking algae eater?
|
|
breather.failed_last_breath = FALSE
|
|
// Vacuum-adapted lungs regenerate oxyloss even when breathing nothing.
|
|
if(breather.health >= breather.crit_threshold && breather.oxyloss)
|
|
breather.adjust_oxy_loss(-5)
|
|
else
|
|
// Can't breathe!
|
|
breather.failed_last_breath = TRUE
|
|
|
|
// The list of gases in the breath.
|
|
var/list/breath_gases = breath.gases
|
|
// Copy the breath's temperature into breath_out to avoid cooling the output breath down unfairly
|
|
breath_out.temperature = breath.temperature
|
|
|
|
var/old_euphoria = (n2o_euphoria == EUPHORIA_ACTIVE || healium_euphoria == EUPHORIA_ACTIVE)
|
|
|
|
// Cache for sonic speed
|
|
var/list/last_partial_pressures = src.last_partial_pressures
|
|
var/list/breathe_always = src.breathe_always
|
|
var/list/breath_present = src.breath_present
|
|
var/list/breath_lost = src.breath_lost
|
|
|
|
// Build out our partial pressures, for use as we go
|
|
var/list/partial_pressures = list()
|
|
for(var/gas_id in breath_gases)
|
|
partial_pressures[gas_id] = breath.get_breath_partial_pressure(breath_gases[gas_id][MOLES] * received_pressure_mult)
|
|
|
|
// Treat gas as other types of gas
|
|
for(var/list/conversion_packet in treat_as)
|
|
var/read_from = conversion_packet[BREATH_RELATIONSHIP_INITIAL_GAS]
|
|
if(!partial_pressures[read_from])
|
|
continue
|
|
var/convert_into = conversion_packet[BREATH_RELATIONSHIP_CONVERT]
|
|
partial_pressures[convert_into] += partial_pressures[read_from] * conversion_packet[BREATH_RELATIONSHIP_MULTIPLIER]
|
|
if(partial_pressures[convert_into] <= 0)
|
|
partial_pressures -= convert_into // No negative values jeremy
|
|
|
|
// First, we breathe the stuff that always wants to be processed
|
|
// This is typically things like o2, stuff the mob needs to live
|
|
for(var/breath_id in breathe_always)
|
|
var/partial_pressure = partial_pressures[breath_id] || 0
|
|
var/old_partial_pressure = last_partial_pressures[breath_id] || 0
|
|
// Ensures the gas will always be instanciated, so people can interact with it safely
|
|
ASSERT_GAS(breath_id, breath)
|
|
var/inhale = breathe_always[breath_id]
|
|
call(src, inhale)(breather, breath, partial_pressure, old_partial_pressure)
|
|
|
|
// Now we'll handle the callbacks that want to be run conditionally off our current breath
|
|
for(var/breath_id in breath_gases)
|
|
var/when_present = breath_present[breath_id]
|
|
if(!when_present)
|
|
continue
|
|
|
|
var/reaction = call(src, when_present)(breather, breath, partial_pressures[breath_id], last_partial_pressures[breath_id])
|
|
if(reaction == BREATH_LOST)
|
|
var/on_lose = breath_lost[breath_id]
|
|
if(on_lose)
|
|
call(src, on_lose)(breather, breath, partial_pressures[breath_id], last_partial_pressures[breath_id])
|
|
|
|
// Finally, we'll run the callbacks that aren't in breath_gases, but WERE in our last breath
|
|
for(var/gas_lost in last_partial_pressures)
|
|
// If we still have it, go away
|
|
if(breath_gases[gas_lost])
|
|
continue
|
|
var/on_loss = breath_lost[gas_lost]
|
|
if(!on_loss)
|
|
continue
|
|
|
|
call(src, on_loss)(breather, breath, last_partial_pressures[gas_lost])
|
|
|
|
src.last_partial_pressures = partial_pressures
|
|
|
|
// Handle chemical euphoria mood event, caused by gases such as N2O or healium.
|
|
var/new_euphoria = (n2o_euphoria == EUPHORIA_ACTIVE || healium_euphoria == EUPHORIA_ACTIVE)
|
|
if (!old_euphoria && new_euphoria)
|
|
breather.add_mood_event("chemical_euphoria", /datum/mood_event/chemical_euphoria)
|
|
else if (old_euphoria && !new_euphoria)
|
|
breather.clear_mood_event("chemical_euphoria")
|
|
|
|
if(has_moles)
|
|
handle_breath_temperature(breath, breather)
|
|
// Merge breath_out into breath. They're kept seprerate before now to ensure stupid like, order of operations shit doesn't happen
|
|
// But that time has passed
|
|
breath.merge(breath_out)
|
|
// Resets immutable gas_mixture to empty.
|
|
breath_out.garbage_collect()
|
|
|
|
breath.garbage_collect()
|
|
// Returning FALSE indicates the breath failed.
|
|
if(!breather.failed_last_breath)
|
|
return TRUE
|
|
|
|
/// Remove gas from breath. If output_gas is given, transfers the removed gas to the lung's gas_mixture.
|
|
/// Removes 100% of the given gas type unless given a volume argument.
|
|
/// Returns the amount of gas theoretically removed.
|
|
/obj/item/organ/lungs/proc/breathe_gas_volume(datum/gas_mixture/breath, remove_id, exchange_id = null, volume = INFINITY)
|
|
var/list/breath_gases = breath.gases
|
|
volume = min(volume, breath_gases[remove_id][MOLES])
|
|
breath_gases[remove_id][MOLES] -= volume
|
|
if(exchange_id)
|
|
ASSERT_GAS(exchange_id, breath_out)
|
|
breath_out.gases[exchange_id][MOLES] += volume
|
|
return volume
|
|
|
|
/// Applies suffocation side-effects to a given Human, scaling based on ratio of required pressure VS "true" pressure.
|
|
/// If pressure is greater than 0, the return value will represent the amount of gas successfully breathed.
|
|
/obj/item/organ/lungs/proc/handle_suffocation(mob/living/carbon/human/suffocator = null, breath_pp = 0, safe_breath_min = 0, mole_count = 0)
|
|
. = 0
|
|
// Can't suffocate without a Human, or without minimum breath pressure.
|
|
if(!suffocator || !safe_breath_min)
|
|
return
|
|
// Mob is suffocating.
|
|
suffocator.failed_last_breath = TRUE
|
|
// Give them a chance to notice something is wrong.
|
|
if(prob(20))
|
|
suffocator.emote("gasp")
|
|
// If mob is at critical health, check if they can be damaged further.
|
|
if(suffocator.health < suffocator.crit_threshold)
|
|
// Mob is immune to damage at critical health.
|
|
if(HAS_TRAIT(suffocator, TRAIT_NOCRITDAMAGE))
|
|
return
|
|
// Reagents like Epinephrine stop suffocation at critical health.
|
|
if(suffocator.reagents.has_reagent(crit_stabilizing_reagent, needs_metabolizing = TRUE))
|
|
return
|
|
// Low pressure.
|
|
if(breath_pp)
|
|
var/ratio = safe_breath_min / breath_pp
|
|
suffocator.apply_damage(min(5 * ratio, HUMAN_MAX_OXYLOSS), OXY)
|
|
return mole_count * ratio / 6
|
|
// Zero pressure.
|
|
if(suffocator.health >= suffocator.crit_threshold)
|
|
suffocator.apply_damage(HUMAN_MAX_OXYLOSS, OXY)
|
|
else
|
|
suffocator.apply_damage(HUMAN_CRIT_MAX_OXYLOSS, OXY)
|
|
|
|
|
|
/obj/item/organ/lungs/proc/handle_breath_temperature(datum/gas_mixture/breath, mob/living/carbon/human/breather) // called by human/life, handles temperatures
|
|
var/breath_temperature = breath.temperature
|
|
|
|
if(!HAS_TRAIT(breather, TRAIT_RESISTCOLD)) // COLD DAMAGE
|
|
var/cold_modifier = breather.dna.species.coldmod
|
|
var/breath_effect_prob = 0
|
|
if(breath_temperature < cold_level_3_threshold)
|
|
breather.apply_damage(cold_level_3_damage * cold_modifier, cold_damage_type, spread_damage = TRUE)
|
|
breath_effect_prob = 100
|
|
if(breath_temperature > cold_level_3_threshold && breath_temperature < cold_level_2_threshold)
|
|
breather.apply_damage(cold_level_2_damage * cold_modifier, cold_damage_type, spread_damage = TRUE)
|
|
breath_effect_prob = 50
|
|
if(breath_temperature > cold_level_2_threshold && breath_temperature < cold_level_1_threshold)
|
|
breather.apply_damage(cold_level_1_damage * cold_modifier, cold_damage_type, spread_damage = TRUE)
|
|
breath_effect_prob = 25
|
|
if(breath_temperature < cold_level_1_threshold)
|
|
if(prob(sqrt(breath_effect_prob) * 4))
|
|
to_chat(breather, span_warning("You feel [cold_message] in your [name]!"))
|
|
if(prob(50))
|
|
breather.emote("shiver")
|
|
if(prob(breath_effect_prob))
|
|
// Breathing into your mask, no particle. We can add fogged up glasses later
|
|
if(breather.is_mouth_covered())
|
|
return
|
|
// Even though breathing via internals TECHNICALLY exhales into the environment, we'll still block it
|
|
if(breather.internal || breather.external)
|
|
return
|
|
emit_breath_particle(breather, /particles/fog/breath)
|
|
|
|
if(!HAS_TRAIT(breather, TRAIT_RESISTHEAT)) // HEAT DAMAGE
|
|
var/heat_modifier = breather.dna.species.heatmod
|
|
var/heat_message_prob = 0
|
|
if(breath_temperature > heat_level_1_threshold && breath_temperature < heat_level_2_threshold)
|
|
breather.apply_damage(heat_level_1_damage * heat_modifier, heat_damage_type, spread_damage = TRUE)
|
|
heat_message_prob = 100
|
|
if(breath_temperature > heat_level_2_threshold && breath_temperature < heat_level_3_threshold)
|
|
breather.apply_damage(heat_level_2_damage * heat_modifier, heat_damage_type, spread_damage = TRUE)
|
|
heat_message_prob = 50
|
|
if(breath_temperature > heat_level_3_threshold)
|
|
breather.apply_damage(heat_level_3_damage * heat_modifier, heat_damage_type, spread_damage = TRUE)
|
|
heat_message_prob = 25
|
|
if(breath_temperature > heat_level_1_threshold)
|
|
if(prob(sqrt(heat_message_prob) * 4))
|
|
to_chat(breather, span_warning("You feel [hot_message] in your [name]!"))
|
|
|
|
// The air you breathe out should match your body temperature
|
|
breath.temperature = breather.bodytemperature
|
|
|
|
/// Creates a particle effect off the mouth of the passed mob.
|
|
/obj/item/organ/lungs/proc/emit_breath_particle(mob/living/carbon/human/breather, particle_type)
|
|
ASSERT(ispath(particle_type, /particles))
|
|
|
|
var/obj/effect/abstract/particle_holder/holder = new(breather, particle_type)
|
|
var/particles/breath_particle = holder.particles
|
|
var/breath_dir = breather.dir
|
|
|
|
var/list/particle_grav = list(0, 0.1, 0)
|
|
var/list/particle_pos = list(0, breather.mob_height + 2, 0)
|
|
if(breath_dir & NORTH)
|
|
particle_grav[2] = 0.2
|
|
breath_particle.rotation = pick(-45, 45)
|
|
// Layer it behind the mob since we're facing away from the camera
|
|
holder.pixel_w -= 4
|
|
holder.pixel_y += 4
|
|
if(breath_dir & WEST)
|
|
particle_grav[1] = -0.2
|
|
particle_pos[1] = -5
|
|
breath_particle.rotation = -45
|
|
if(breath_dir & EAST)
|
|
particle_grav[1] = 0.2
|
|
particle_pos[1] = 5
|
|
breath_particle.rotation = 45
|
|
if(breath_dir & SOUTH)
|
|
particle_grav[2] = 0.2
|
|
breath_particle.rotation = pick(-45, 45)
|
|
// Shouldn't be necessary but just for parity
|
|
holder.pixel_w += 4
|
|
holder.pixel_y -= 4
|
|
|
|
breath_particle.gravity = particle_grav
|
|
breath_particle.position = particle_pos
|
|
|
|
QDEL_IN(holder, breath_particle.lifespan)
|
|
|
|
/obj/item/organ/lungs/on_life(seconds_per_tick)
|
|
. = ..()
|
|
if(failed && !(organ_flags & ORGAN_FAILING))
|
|
failed = FALSE
|
|
return
|
|
if(damage >= low_threshold)
|
|
var/do_i_cough = SPT_PROB((damage < high_threshold) ? 2.5 : 5, seconds_per_tick) // between : past high
|
|
if(do_i_cough)
|
|
owner.emote("cough")
|
|
if(organ_flags & ORGAN_FAILING && owner.stat == CONSCIOUS)
|
|
owner.visible_message(span_danger("[owner] grabs [owner.p_their()] throat, struggling for breath!"), span_userdanger("You suddenly feel like you can't breathe!"))
|
|
failed = TRUE
|
|
|
|
/obj/item/organ/lungs/get_availability(datum/species/owner_species, mob/living/owner_mob)
|
|
return owner_species.mutantlungs
|
|
|
|
/obj/item/organ/lungs/feel_for_damage(self_aware)
|
|
if(organ_flags & ORGAN_FAILING)
|
|
if(self_aware)
|
|
return span_boldwarning("Your lungs hurt madly[HAS_TRAIT(owner, TRAIT_NOBREATH) ? "" : ", and you can't breathe"]!")
|
|
return span_boldwarning("It hurts madly[HAS_TRAIT(owner, TRAIT_NOBREATH) ? "" : ", and you can't breathe"]!")
|
|
if(damage < low_threshold)
|
|
return ""
|
|
if(damage < high_threshold)
|
|
if(self_aware)
|
|
return span_warning("Your lungs feel tight[HAS_TRAIT(owner, TRAIT_NOBREATH) ? "" : ", and breathing is harder"].")
|
|
return span_warning("It feels tight[HAS_TRAIT(owner, TRAIT_NOBREATH) ? "" : ", and breathing is harder"].")
|
|
if(self_aware)
|
|
return span_boldwarning("Your lungs feel extremely tight[HAS_TRAIT(owner, TRAIT_NOBREATH) ? "" : ", and every breath is a struggle"].")
|
|
return span_boldwarning("It feels extremely tight[HAS_TRAIT(owner, TRAIT_NOBREATH) ? "" : ", and every breath is a struggle"].")
|
|
|
|
/obj/item/organ/lungs/get_status_appendix(advanced, add_tooltips)
|
|
var/initial_pressure_mult = initial(received_pressure_mult)
|
|
if (received_pressure_mult == initial_pressure_mult)
|
|
return
|
|
|
|
var/tooltip
|
|
var/dilation_text
|
|
var/beginning_text = "Lung Dilation: "
|
|
if (received_pressure_mult > initial_pressure_mult) // higher than usual
|
|
beginning_text = span_blue("<b>[beginning_text]</b>")
|
|
dilation_text = span_blue("[(received_pressure_mult * 100) - 100]%")
|
|
tooltip = "Subject's lungs are dilated and breathing more air than usual. \
|
|
Increases the effectiveness of healium and other gases."
|
|
|
|
else
|
|
beginning_text = span_alert("<b>[beginning_text]</b>")
|
|
if (received_pressure_mult <= 0) // lethal
|
|
dilation_text = span_alert("<b>[received_pressure_mult * 100]%</b>")
|
|
tooltip = "Subject's lungs are completely shut. Subject is unable to breathe and requires emergency surgery. \
|
|
If asthmatic, perform asthmatic bypass surgery and adminster albuterol inhalant. \
|
|
Otherwise, replace lungs."
|
|
else
|
|
dilation_text = span_alert("[received_pressure_mult * 100]%")
|
|
tooltip = "Subject's lungs are partially shut. \
|
|
If unable to breathe, administer a high-pressure internals tank or replace lungs. \
|
|
If asthmatic, inhaled albuterol or bypass surgery will likely help."
|
|
|
|
return beginning_text + conditional_tooltip(dilation_text, tooltip, add_tooltips)
|
|
|
|
/// by default, returns the lungs' breath_noise var as a notice. called when stethoscope is used on chest, uses the return as a message for stethoscope user.
|
|
/obj/item/organ/lungs/proc/hear_breath_noise(mob/living/hearer)
|
|
return span_notice("[owner.p_Their()] lungs emit [breath_noise].")
|
|
|
|
#define SMOKER_ORGAN_HEALTH (STANDARD_ORGAN_THRESHOLD * 0.75)
|
|
#define SMOKER_LUNG_HEALING (STANDARD_ORGAN_HEALING * 0.75)
|
|
|
|
/obj/item/organ/lungs/plasmaman
|
|
name = "plasma filter"
|
|
desc = "A spongy rib-shaped mass for filtering plasma from the air."
|
|
icon_state = "lungs-plasma"
|
|
organ_traits = list(TRAIT_NOHUNGER) // A fresh breakfast of plasma is a great start to any morning.
|
|
breath_noise = "a crackle, like crushed foam"
|
|
safe_oxygen_min = 0 //We don't breathe this
|
|
safe_plasma_min = 4 //We breathe THIS!
|
|
safe_plasma_max = 0
|
|
|
|
/obj/item/organ/lungs/plasmaman/plasmaman_smoker
|
|
name = "smoker plasma filter"
|
|
desc = "A plasma filter that look discolored, a result from smoking a lot."
|
|
icon_state = "lungs_plasma_smoker"
|
|
breath_noise = "a wheezing crackle, like crushed foam"
|
|
maxHealth = SMOKER_ORGAN_HEALTH
|
|
healing_factor = SMOKER_LUNG_HEALING
|
|
|
|
/obj/item/organ/lungs/slime
|
|
name = "slime vacuole"
|
|
desc = "A large organelle designed to store oxygen and other important gasses."
|
|
breath_noise = "a low burbling"
|
|
safe_plasma_max = 0 //We breathe this to gain POWER.
|
|
|
|
/obj/item/organ/lungs/slime/check_breath(datum/gas_mixture/breath, mob/living/carbon/human/breather_slime)
|
|
. = ..()
|
|
if (breath?.gases[/datum/gas/plasma])
|
|
var/plasma_pp = breath.get_breath_partial_pressure(breath.gases[/datum/gas/plasma][MOLES])
|
|
breather_slime.adjust_blood_volume(0.2 * plasma_pp) // 10/s when breathing literally nothing but plasma, which will suffocate you.
|
|
|
|
/obj/item/organ/lungs/smoker_lungs
|
|
name = "smoker lungs"
|
|
desc = "A pair of lungs that look sickly, a result from smoking a lot."
|
|
icon_state = "lungs_smoker"
|
|
breath_noise = "an unsteady, wheezing rhythm"
|
|
maxHealth = SMOKER_ORGAN_HEALTH
|
|
healing_factor = SMOKER_LUNG_HEALING
|
|
|
|
/obj/item/organ/lungs/cybernetic
|
|
name = "basic cybernetic lungs"
|
|
desc = "A basic cybernetic version of the lungs found in traditional humanoid entities."
|
|
failing_desc = "seems to be broken."
|
|
icon_state = "lungs-c"
|
|
breath_noise = "a steady whirr"
|
|
organ_flags = ORGAN_ROBOTIC
|
|
maxHealth = STANDARD_ORGAN_THRESHOLD * 0.5
|
|
var/emp_vulnerability = 80 //Chance of permanent effects if emp-ed.
|
|
|
|
/obj/item/organ/lungs/cybernetic/emp_act(severity)
|
|
. = ..()
|
|
if(. & EMP_PROTECT_SELF)
|
|
return
|
|
if(!COOLDOWN_FINISHED(src, severe_cooldown)) //So we cant just spam emp to kill people.
|
|
owner.losebreath += 20
|
|
COOLDOWN_START(src, severe_cooldown, 30 SECONDS)
|
|
if(prob(emp_vulnerability/severity)) //Chance of permanent effects
|
|
organ_flags |= ORGAN_EMP //Starts organ faliure - gonna need replacing soon.
|
|
|
|
/obj/item/organ/lungs/cybernetic/tier2
|
|
name = "cybernetic lungs"
|
|
desc = "A cybernetic version of the lungs found in traditional humanoid entities. Allows for greater intakes of oxygen than organic lungs, requiring slightly less pressure."
|
|
icon_state = "lungs-c-u"
|
|
maxHealth = 1.5 * STANDARD_ORGAN_THRESHOLD
|
|
safe_oxygen_min = 13
|
|
emp_vulnerability = 40
|
|
|
|
/obj/item/organ/lungs/cybernetic/tier3
|
|
name = "upgraded cybernetic lungs"
|
|
desc = "A more advanced version of the stock cybernetic lungs. Features the ability to filter out lower levels of plasma and carbon dioxide."
|
|
icon_state = "lungs-c-u2"
|
|
safe_plasma_max = 20
|
|
safe_co2_max = 20
|
|
maxHealth = 2 * STANDARD_ORGAN_THRESHOLD
|
|
safe_oxygen_min = 13
|
|
emp_vulnerability = 20
|
|
|
|
cold_level_1_threshold = 200
|
|
cold_level_2_threshold = 140
|
|
cold_level_3_threshold = 100
|
|
|
|
/obj/item/organ/lungs/cybernetic/surplus
|
|
name = "surplus prosthetic lungs"
|
|
desc = "Two fragile, inflatable sacks of air that only barely mimic the function of human lungs. \
|
|
Offer no protection against EMPs."
|
|
breath_noise = "a concerningly unstable scratchy whirr. You <b>shouldn't touch this</b> while it's running"
|
|
icon_state = "lungs-c-s"
|
|
maxHealth = 0.35 * STANDARD_ORGAN_THRESHOLD
|
|
emp_vulnerability = 100
|
|
|
|
//surplus organs are so awful that they explode when removed, unless failing
|
|
/obj/item/organ/lungs/cybernetic/surplus/Initialize(mapload)
|
|
. = ..()
|
|
AddElement(/datum/element/dangerous_organ_removal, /*surgical = */ TRUE)
|
|
|
|
/obj/item/organ/lungs/cybernetic/surplus/hear_breath_noise(mob/living/hearer)
|
|
return span_danger("[owner.p_Their()] lungs emit [breath_noise].")
|
|
|
|
/obj/item/organ/lungs/ghost
|
|
name = "ghost lungs"
|
|
desc = "No one knows what this is even supposed to breathe."
|
|
icon_state = "lungs-ghost"
|
|
movement_type = PHASING
|
|
organ_flags = parent_type::organ_flags | ORGAN_GHOST
|
|
|
|
/obj/item/organ/lungs/lavaland
|
|
name = "blackened frilled lungs" // blackened from necropolis exposure
|
|
desc = "Exposure to the necropolis has mutated these lungs to breathe the air of Indecipheres, the lava-covered moon."
|
|
icon_state = "lungs-ashwalker"
|
|
breath_noise = "a throbbing smoke-like hiss"
|
|
|
|
// Normal oxygen is 21 kPa partial pressure, but SS13 humans can tolerate down
|
|
// to 16 kPa. So it follows that ashwalkers, as humanoids, follow the same rules.
|
|
#define GAS_TOLERANCE 5
|
|
|
|
/obj/item/organ/lungs/lavaland/Initialize(mapload)
|
|
var/datum/gas_mixture/volumed_mix = new(BREATH_VOLUME * 1000) // to be safe
|
|
var/datum/gas_mixture/immutable/planetary/mix = SSair.parse_gas_string(LAVALAND_DEFAULT_ATMOS, /datum/gas_mixture/immutable/planetary)
|
|
|
|
volumed_mix.copy_from(mix)
|
|
|
|
// Take a "breath" of the air
|
|
var/datum/gas_mixture/breath = volumed_mix.remove(volumed_mix.total_moles() * BREATH_PERCENTAGE)
|
|
|
|
var/list/breath_gases = breath.gases
|
|
|
|
breath.assert_gases(
|
|
/datum/gas/oxygen,
|
|
/datum/gas/plasma,
|
|
/datum/gas/carbon_dioxide,
|
|
/datum/gas/nitrogen,
|
|
/datum/gas/bz,
|
|
/datum/gas/miasma,
|
|
)
|
|
|
|
var/oxygen_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/oxygen][MOLES])
|
|
// var/nitrogen_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrogen][MOLES]) BUBBERSTATION CHANGE: ALLOWS ASHWALKERS TO BREATHE ON STATION
|
|
var/plasma_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/plasma][MOLES])
|
|
var/carbon_dioxide_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/carbon_dioxide][MOLES])
|
|
var/bz_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/bz][MOLES])
|
|
var/miasma_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/miasma][MOLES])
|
|
|
|
safe_oxygen_min = max(0, oxygen_pp - GAS_TOLERANCE)
|
|
/* BUBBERSTATION CHANGE START: ALLOWS ASHWALKERS TO BREATHE ON STATION
|
|
safe_nitro_min = max(0, nitrogen_pp - GAS_TOLERANCE)
|
|
safe_plasma_min = max(0, plasma_pp - GAS_TOLERANCE)
|
|
*/
|
|
safe_nitro_min = 0
|
|
safe_plasma_min = 0
|
|
//BUBBERSTATION CHANGE END
|
|
|
|
// Increase plasma tolerance based on amount in base air
|
|
safe_plasma_max += plasma_pp
|
|
|
|
// CO2 is always a waste gas, so none is required, but ashwalkers
|
|
// tolerate the base amount plus tolerance*2 (humans tolerate only 10 pp)
|
|
|
|
safe_co2_max = carbon_dioxide_pp + GAS_TOLERANCE * 2
|
|
|
|
// The lung tolerance against BZ is also increased the amount of BZ in the base air
|
|
BZ_trip_balls_min += bz_pp
|
|
BZ_brain_damage_min += bz_pp
|
|
|
|
// Lungs adapted to a high miasma atmosphere do not process it, and breathe it back out
|
|
if(miasma_pp)
|
|
suffers_miasma = FALSE
|
|
return ..()
|
|
|
|
|
|
#undef GAS_TOLERANCE
|
|
|
|
/// Adjusting proc for [received_pressure_mult]. Updates bronchodilation alerts.
|
|
/obj/item/organ/lungs/proc/adjust_received_pressure_mult(adjustment)
|
|
received_pressure_mult = max(received_pressure_mult + adjustment, 0)
|
|
update_bronchodilation_alerts()
|
|
|
|
/// Setter proc for [received_pressure_mult]. Updates bronchodilation alerts.
|
|
/obj/item/organ/lungs/proc/set_received_pressure_mult(new_value)
|
|
received_pressure_mult = max(new_value, 0)
|
|
update_bronchodilation_alerts()
|
|
|
|
#define LUNG_CAPACITY_ALERT_BUFFER 0.003
|
|
/// Depending on [received_pressure_mult], gives either a bronchocontraction or bronchoconstriction alert to our owner (if we have one), or clears the alert
|
|
/// if [received_pressure_mult] is near 1.
|
|
/obj/item/organ/lungs/proc/update_bronchodilation_alerts()
|
|
if (!owner)
|
|
return
|
|
|
|
var/initial_value = initial(received_pressure_mult)
|
|
|
|
// you wont really notice if youre only breathing a bit more or a bit less
|
|
var/dilated = (received_pressure_mult > (initial_value + LUNG_CAPACITY_ALERT_BUFFER))
|
|
var/constricted = (received_pressure_mult < (initial_value - LUNG_CAPACITY_ALERT_BUFFER))
|
|
|
|
if (dilated)
|
|
owner.throw_alert(ALERT_BRONCHODILATION, /atom/movable/screen/alert/bronchodilated)
|
|
else if (constricted)
|
|
owner.throw_alert(ALERT_BRONCHODILATION, /atom/movable/screen/alert/bronchoconstricted)
|
|
else
|
|
owner.clear_alert(ALERT_BRONCHODILATION)
|
|
|
|
#undef LUNG_CAPACITY_ALERT_BUFFER
|
|
|
|
/obj/item/organ/lungs/ethereal
|
|
name = "aeration reticulum"
|
|
desc = "These exotic lungs seem crunchier than most."
|
|
icon_state = "lungs_ethereal"
|
|
breath_noise = "a low fluorescent hum"
|
|
heat_level_1_threshold = FIRE_MINIMUM_TEMPERATURE_TO_SPREAD // 150C or 433k, in line with ethereal max safe body temperature
|
|
heat_level_2_threshold = 473
|
|
heat_level_3_threshold = 1073
|
|
|
|
/obj/item/organ/lungs/ethereal/ethereal_smoker
|
|
name = "smoker aeration reticulum"
|
|
desc = "A pair of exotic lungs that look pale and sickly, a result from smoking a lot."
|
|
icon_state = "lungs_ethereal_smoker"
|
|
breath_noise = "a spotty hum, like a broken lightbulb"
|
|
maxHealth = SMOKER_ORGAN_HEALTH
|
|
healing_factor = SMOKER_LUNG_HEALING
|
|
|
|
/obj/item/organ/lungs/ethereal/Initialize(mapload)
|
|
. = ..()
|
|
add_gas_reaction(/datum/gas/water_vapor, while_present = PROC_REF(consume_water))
|
|
|
|
/// H2O electrolysis
|
|
/obj/item/organ/lungs/ethereal/proc/consume_water(mob/living/carbon/breather, datum/gas_mixture/breath, h2o_pp, old_h2o_pp)
|
|
var/gas_breathed = breath.gases[/datum/gas/water_vapor][MOLES]
|
|
breath.adjust_gas(/datum/gas/water_vapor, -gas_breathed)
|
|
var/list/new_gases = list(/datum/gas/oxygen = gas_breathed, /datum/gas/hydrogen = gas_breathed * 2)
|
|
breath_out.adjust_multiple_gases(new_gases)
|
|
|
|
|
|
/obj/item/organ/lungs/pod
|
|
name = "pod vacuole"
|
|
desc = "A large organelle designed to store oxygen and other important gasses."
|
|
breath_noise = "a humid hiss"
|
|
foodtype_flags = PODPERSON_ORGAN_FOODTYPES
|
|
color = COLOR_LIME
|
|
|
|
/obj/item/organ/lungs/evolved
|
|
name = "evolved lungs"
|
|
desc = "A pair of lungs, with an organic filtering system and a stronger musculature."
|
|
|
|
icon_state = "lungs-evolved"
|
|
|
|
safe_plasma_max = 8
|
|
safe_co2_max = 20
|
|
maxHealth = 1.2 * STANDARD_ORGAN_THRESHOLD
|
|
safe_oxygen_min = 8
|
|
|
|
#undef BREATH_RELATIONSHIP_INITIAL_GAS
|
|
#undef BREATH_RELATIONSHIP_CONVERT
|
|
#undef BREATH_RELATIONSHIP_MULTIPLIER
|
|
#undef SMOKER_ORGAN_HEALTH
|
|
#undef SMOKER_LUNG_HEALING
|