Refactor: Carp Infusion & Lungs (#71940)

## About The Pull Request
Recently before merging PR #71867 I failed to account for the carp lungs
and spacewalk trait in my testing (I did not try to fly across space)
and so I did not notice other bugs. This PR fixes the bugs/oversights
which I missed in my prior PR. Fixes #72678

I have completed a refactor of
`/obj/item/organ/internal/lungs/proc/check_breath` and
`/mob/living/carbon/proc/check_breath` because I noticed that bugs
within the gas alert system (stuck alerts) were caused by their improper
execution, and the code was written in a hard-to-understand style.

This PR also includes a small adjustment to status indicators which
allows you to add an already existing timed indicator without
overwriting it; it instead resets the existing timeout.

Here is a granular list of bugs:

- Bug A: Causes the spacewalk trait to not be applied to the mob, and is
caused by a minor developer oversight in
`/datum/status_effect/organ_set_bonus/carp/enable_bonus()` wherein it
attempts to add `TRAIT_SPACEWALK` to the status effect rather than to
the Mob.
- Bug B: Causes the carp lungs to suffocate you in space, because that
behavior is unimplemented.
- Bug C: Causes the gas alert indicators ("Too much O2!" etc) to stay
stuck to the screen if `check_breath` or other procs fail to remove
them. This is caused by the way `check_breath` early-returns without
executing the necessary logic to manage its gas alerts.
- Bug D: The part of `lungs/proc/check_breath()` that removes consumed
miasma from the air is only doing so when helium is also present and
consumed.

This PR contains fixes for these bugs:

- Fix A: Added the spacewalk trait to the mob, allowing the trait to
enable space flight as expected.
- Fix B: Added `TRAIT_SPACEBREATHING` as a lungs trait, which is used in
`check_breath`, which allows the mob to "breathe" in space or vacuum,
but suffocate when breathing unsuitable gas/O2.
- Fix C: Changed the proc `/mob/proc/throw_alert` such that, when
throwing an alert which already exists, it resets the timeout of the
existing alert rather than replacing it.
- ~Fix C2: Added a 10 second timer to gas alerts. If something fails to
clear them from the screen, they will now clear themselves after 10
seconds.~
- Fix D: Refactored `/mob/living/carbon/proc/check_breath` and
`obj/item/organ/internal/lungs/proc/check_breath` to run the necessary
logic for resetting gas side effects and gas alerts.
- Removed an early-return which was causing the `check_breath` functions
to skip most of their critical sections.
- Fixed the part of `lungs/proc/check_breath()` that removes consumed
miasma from the air, allowing it to remove the correct amount when
expected to.
- Added conditionals to check for `TRAIT_SPACEBREATHING` on lungs to
bypass suffocation.
- Added a fall-back to a default "flyweight" breath
`datum/gas_mixture/empty_breath` to Carbons and lungs, used in in the
event that either gets a null `breath`.
- Renamed `obj/item/organ/internal/lungs/proc/handle_too_little_breath`
to `handle_suffocation` and refactored it to handle more aspects of
suffocation that was originally being handled inline.
- Added `/obj/item/organ/internal/lungs/proc/breathe_gas_volume` to
`check_breath` to replace lots of inlined logic which handles
consuming/exchanging gases (O2 for CO2, etc).

## Why It's Good For The Game

Shortly after PR #71867 was merged, we noticed the carp lungs and
spacewalking trait were not working as expected when doing the Space
Carp DNA infusion. This PR fixes the bug and allows you to spacewalk
like a Space Carp again.

This PR also adjusts the gas alert indicators so they don't get stuck to
the screen as often. This PR also fixes a bug that was causing timed
alerts to overwrite each other when it was unnecessary to do so, and
helps prevent their on-throw animation from being replayed when not
appropriate.

I have included an intermediate refactor of
`/mob/living/carbon/proc/check_breath` and
`obj/item/organ/internal/lungs/proc/check_breath` in this PR, and I have
tried to improve readability/maintainability while correcting critical
oversights in the ways breathing was working. After my changes are
merged, I will likely work on another refactor to delete duplicated
sections of code between Carbons and lungs, as much of it is 99%
identical.

## Changelog

🆑 A.C.M.O.
refactor: Refactored breathing, mostly check_breath, for Carbons and
lungs organ.
add: Added the space-breathing trait for lungs.
fix: Fixed the Space Carp DNA infusion to apply the Spacewalk trait,
allowing you to fly through space.
fix: Fixed the Space Carp lungs, allowing you to breathe in space with
them.
fix: Fixed gas status indicators re-playing their on-throw animation
when they are refreshed.
fix: Fixed gas status indicators getting stuck to the screen in some
situations.
fix: Fixed gas side effects, such as euphoria and hallucinations, to
reset when expected.
fix: For Humans, fixed Miasma only being consumed from the air when
Helium is present.
/🆑

---------

Co-authored-by: Jeremiah <42397676+jlsnow301@users.noreply.github.com>
Co-authored-by: LemonInTheDark <58055496+LemonInTheDark@users.noreply.github.com>
This commit is contained in:
Dani Glore
2023-01-28 20:43:24 +00:00
committed by GitHub
co-authored by Jeremiah LemonInTheDark
parent 532c0ab616
commit 8e4d86ec8f
7 changed files with 671 additions and 409 deletions
+4
View File
@@ -467,6 +467,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
#define TRAIT_PRETENDER_ROYAL_METABOLISM "pretender_royal_metabolism"
#define TRAIT_BALLMER_SCIENTIST "ballmer_scientist"
//LUNG TRAITS
/// Lungs always breathe normally when in vacuum/space.
#define TRAIT_SPACEBREATHING "spacebreathing"
/// This mob can strip other mobs.
#define TRAIT_CAN_STRIP "can_strip"
/// Can use the nuclear device's UI, regardless of a lack of hands
+3
View File
@@ -182,6 +182,9 @@ GLOBAL_LIST_INIT(traits_by_type, list(
/obj/item/bodypart = list(
"TRAIT_PARALYSIS" = TRAIT_PARALYSIS,
),
/obj/item/organ/internal/lungs = list(
"TRAIT_SPACEBREATHING" = TRAIT_SPACEBREATHING,
),
/obj/item/organ/internal/liver = list(
"TRAIT_LAW_ENFORCEMENT_METABOLISM" = TRAIT_LAW_ENFORCEMENT_METABOLISM,
"TRAIT_CULINARY_METABOLISM" = TRAIT_CULINARY_METABOLISM,
+14 -4
View File
@@ -31,11 +31,14 @@
clear_alert(category)
return .()
else if(!severity || severity == thealert.severity)
if(thealert.timeout)
clear_alert(category)
return .()
else //no need to update
if(!thealert.timeout)
// No need to update existing alert
return thealert
// Reset timeout of existing alert
var/timeout = initial(thealert.timeout)
addtimer(CALLBACK(src, PROC_REF(alert_timeout), thealert, category), timeout)
thealert.timeout = world.time + timeout - world.tick_lag
return thealert
else
thealert = new type()
thealert.override_alerts = override
@@ -117,6 +120,13 @@
//Gas alerts
// Gas alerts are continuously thrown/cleared by:
// * /obj/item/organ/internal/lungs/proc/check_breath()
// * /mob/living/carbon/check_breath()
// * /mob/living/carbon/human/check_breath()
// * /datum/element/atmos_requirements/proc/on_non_stasis_life()
// * /mob/living/simple_animal/handle_environment()
/atom/movable/screen/alert/not_enough_oxy
name = "Choking (No O2)"
desc = "You're not getting enough oxygen. Find some good air before you pass out! The box in your backpack has an oxygen tank and breath mask in it."
@@ -13,17 +13,20 @@
/datum/status_effect/organ_set_bonus/carp/enable_bonus()
. = ..()
ADD_TRAIT(src, TRAIT_SPACEWALK, REF(src))
ADD_TRAIT(owner, TRAIT_SPACEWALK, REF(src))
/datum/status_effect/organ_set_bonus/carp/disable_bonus()
. = ..()
REMOVE_TRAIT(src, TRAIT_SPACEWALK, REF(src))
REMOVE_TRAIT(owner, TRAIT_SPACEWALK, REF(src))
///Carp lungs! You can breathe in space! Oh... you can't breathe on the station, you need low oxygen environments.
/// Inverts behavior of lungs. Bypasses suffocation due to space / lack of gas, but also allows Oxygen to suffocate.
/obj/item/organ/internal/lungs/carp
name = "mutated carp-lungs"
desc = "Carp DNA infused into what was once some normal lungs."
safe_oxygen_min = 0 //we don't breathe this!
// Oxygen causes suffocation.
safe_oxygen_min = 0
safe_oxygen_max = 15
icon = 'icons/obj/medical/organs/infuser_organs.dmi'
icon_state = "lungs"
@@ -34,6 +37,7 @@
. = ..()
AddElement(/datum/element/noticable_organ, "neck has odd gills.", BODY_ZONE_HEAD)
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/carp)
ADD_TRAIT(src, TRAIT_SPACEBREATHING, REF(src))
///occasionally sheds carp teeth, stronger melee (bite) attacks, but you can't cover your mouth anymore.
/obj/item/organ/internal/tongue/carp
@@ -49,6 +53,7 @@
/obj/item/organ/internal/tongue/carp/Initialize(mapload)
. = ..()
AddElement(/datum/element/noticable_organ, "has big sharp teeth.", BODY_ZONE_PRECISE_MOUTH)
AddElement(/datum/element/organ_set_bonus, /datum/status_effect/organ_set_bonus/carp)
/obj/item/organ/internal/tongue/carp/Insert(mob/living/carbon/tongue_owner, special, drop_if_replaced)
@@ -54,8 +54,9 @@
///This is used to determine if the mob failed a breath. If they did fail a brath, they will attempt to breathe each tick, otherwise just once per 4 ticks.
var/failed_last_breath = FALSE
/// Used in [carbon/proc/check_breath] and [lungs/proc/check_breath]]
var/co2overloadtime = null
var/obj/item/food/meat/slab/type_of_meat = /obj/item/food/meat/slab
var/gib_type = /obj/effect/decal/cleanable/blood/gibs
+230 -112
View File
@@ -47,7 +47,7 @@
// BREATHING //
///////////////
//Start of a breath chain, calls breathe()
// Start of a breath chain, calls [carbon/proc/breathe()]
/mob/living/carbon/handle_breathing(delta_time, times_fired)
var/next_breath = 4
var/obj/item/organ/internal/lungs/L = getorganslot(ORGAN_SLOT_LUNGS)
@@ -70,7 +70,7 @@
var/obj/location_as_object = loc
location_as_object.handle_internal_lifeform(src,0)
//Second link in a breath chain, calls check_breath()
// Second link in a breath chain, calls [carbon/proc/check_breath()]
/mob/living/carbon/proc/breathe(delta_time, times_fired)
var/obj/item/organ/internal/lungs = getorganslot(ORGAN_SLOT_LUNGS)
if(SEND_SIGNAL(src, COMSIG_CARBON_ATTEMPT_BREATHE) & COMSIG_CARBON_BLOCK_BREATH)
@@ -130,145 +130,202 @@
return TRUE
return FALSE
//Third link in a breath chain, calls handle_breath_temperature()
/**
* This proc tests if the lungs can breathe, if the mob can breathe a given gas mixture, and throws/clears gas alerts.
* If there are moles of gas in the given gas mixture, side-effects may be applied/removed on the mob.
* This proc expects a lungs organ in order to breathe successfully, but does not defer any work to it.
*
* Returns TRUE if the breath was successful, or FALSE if otherwise.
*
* Arguments:
* * breath: A gas mixture to test, or null.
*/
/mob/living/carbon/proc/check_breath(datum/gas_mixture/breath)
. = TRUE
if(status_flags & GODMODE)
failed_last_breath = FALSE
clear_alert(ALERT_NOT_ENOUGH_OXYGEN)
return FALSE
return
if(HAS_TRAIT(src, TRAIT_NOBREATH))
return FALSE
return
// Breath may be null, so use a fallback "empty breath" for convenience.
if(!breath)
/// Fallback "empty breath" for convenience.
var/static/datum/gas_mixture/immutable/empty_breath = new(BREATH_VOLUME)
breath = empty_breath
// Ensure gas volumes are present.
breath.assert_gases(/datum/gas/bz, /datum/gas/carbon_dioxide, /datum/gas/freon, /datum/gas/plasma, /datum/gas/pluoxium, /datum/gas/miasma, /datum/gas/nitrous_oxide, /datum/gas/nitrium, /datum/gas/oxygen)
/// The list of gases in the breath.
var/list/breath_gases = breath.gases
/// Indicates if there are moles of gas in the breath.
var/has_moles = breath.total_moles() != 0
var/obj/item/organ/internal/lungs = getorganslot(ORGAN_SLOT_LUNGS)
if(!lungs)
// Indicates if lungs can breathe without gas.
var/can_breathe_vacuum = FALSE
if(lungs)
// Breathing with lungs.
// Check for vacuum-adapted lungs.
can_breathe_vacuum = HAS_TRAIT(lungs, TRAIT_SPACEBREATHING)
else
// Lungs are missing! Can't breathe.
// Simulates breathing zero moles of gas.
has_moles = FALSE
// Extra damage, let God sort em out!
adjustOxyLoss(2)
//CRIT
if(!breath || (breath.total_moles() == 0) || !lungs)
if(reagents.has_reagent(/datum/reagent/medicine/epinephrine, needs_metabolizing = TRUE) && lungs)
return FALSE
adjustOxyLoss(1)
failed_last_breath = TRUE
throw_alert(ALERT_NOT_ENOUGH_OXYGEN, /atom/movable/screen/alert/not_enough_oxy)
return FALSE
var/safe_oxy_min = 16
/// Minimum O2 before suffocation.
var/safe_oxygen_min = 16
/// Maximum CO2 before side-effects.
var/safe_co2_max = 10
/// Maximum Plasma before side-effects.
var/safe_plas_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
/// Maximum Pluoxum before side-effects.
var/gas_stimulation_min = 0.002 // For Pluoxium
// Vars for N2O induced euphoria, stun, and sleep.
var/n2o_euphoria = EUPHORIA_LAST_FLAG
var/n2o_para_min = 1
var/n2o_sleep_min = 5
var/list/breath_gases = breath.gases
breath.assert_gases(/datum/gas/oxygen, /datum/gas/plasma, /datum/gas/carbon_dioxide, /datum/gas/nitrous_oxide, /datum/gas/bz)
var/O2_partialpressure = (breath_gases[/datum/gas/oxygen][MOLES]/breath.total_moles())*breath_pressure
var/Plasma_partialpressure = (breath_gases[/datum/gas/plasma][MOLES]/breath.total_moles())*breath_pressure
var/CO2_partialpressure = (breath_gases[/datum/gas/carbon_dioxide][MOLES]/breath.total_moles())*breath_pressure
// Partial pressures in our breath
// Main gases.
var/pluoxium_pp = 0
var/o2_pp = 0
var/plasma_pp = 0
var/co2_pp = 0
// Trace gases ordered alphabetically.
var/bz_pp = 0
var/freon_pp = 0
var/n2o_pp = 0
var/nitrium_pp = 0
var/miasma_pp = 0
// Check for moles of gas and handle partial pressures / special conditions.
if(has_moles)
// Breath has more than 0 moles of gas.
// Partial pressures of "main gases".
pluoxium_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/pluoxium][MOLES])
o2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/oxygen][MOLES] + (8 * pluoxium_pp))
plasma_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/plasma][MOLES])
co2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/carbon_dioxide][MOLES])
// Partial pressures of "trace" gases.
bz_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/bz][MOLES])
freon_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/freon][MOLES])
miasma_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/miasma][MOLES])
n2o_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrous_oxide][MOLES])
nitrium_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrium][MOLES])
//OXYGEN
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 = TRUE
oxygen_used = breath_gases[/datum/gas/oxygen][MOLES]*ratio
else
adjustOxyLoss(3)
failed_last_breath = TRUE
throw_alert(ALERT_NOT_ENOUGH_OXYGEN, /atom/movable/screen/alert/not_enough_oxy)
else //Enough oxygen
// Breath has 0 moles of gas.
else if(can_breathe_vacuum)
// The mob can breathe anyways. What are you? Some bottom-feeding, scum-sucking algae eater?
failed_last_breath = FALSE
// Vacuum-adapted lungs regenerate oxyloss even when breathing nothing.
if(health >= crit_threshold)
adjustOxyLoss(-5)
oxygen_used = breath_gases[/datum/gas/oxygen][MOLES]
else
// Can't breathe! Lungs are missing, and/or breath is empty.
. = FALSE
failed_last_breath = TRUE
//-- PLUOXIUM --//
// Behaves like Oxygen with 8X efficacy, but metabolizes into a reagent.
if(pluoxium_pp)
// Inhale Pluoxium. Exhale nothing.
breath_gases[/datum/gas/pluoxium][MOLES] = 0
// Metabolize to reagent.
if(pluoxium_pp > gas_stimulation_min)
var/existing = reagents.get_reagent_amount(/datum/reagent/pluoxium)
reagents.add_reagent(/datum/reagent/pluoxium, max(0, 1 - existing))
//-- OXYGEN --//
// Carbons need only Oxygen to breathe properly.
var/oxygen_used = 0
// Minimum Oxygen effects. "Too little oxygen!"
if(!can_breathe_vacuum && (o2_pp < safe_oxygen_min))
// Breathe insufficient amount of O2.
oxygen_used = handle_suffocation(o2_pp, safe_oxygen_min, breath_gases[/datum/gas/oxygen][MOLES])
throw_alert(ALERT_NOT_ENOUGH_OXYGEN, /atom/movable/screen/alert/not_enough_oxy)
else
// Enough oxygen to breathe.
failed_last_breath = FALSE
clear_alert(ALERT_NOT_ENOUGH_OXYGEN)
if(o2_pp)
// Inhale O2.
oxygen_used = breath_gases[/datum/gas/oxygen][MOLES]
// Heal mob if not in crit.
if(health >= crit_threshold)
adjustOxyLoss(-5)
// Exhale equivalent amount of CO2.
if(o2_pp)
breath_gases[/datum/gas/oxygen][MOLES] -= oxygen_used
breath_gases[/datum/gas/carbon_dioxide][MOLES] += oxygen_used
breath_gases[/datum/gas/oxygen][MOLES] -= oxygen_used
breath_gases[/datum/gas/carbon_dioxide][MOLES] += 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)
//-- CARBON DIOXIDE --//
// Maximum CO2 effects. "Too much CO2!"
if(co2_pp > safe_co2_max)
// CO2 side-effects.
// Give the mob a chance to notice.
if(prob(20))
emote("cough")
// 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(!co2overloadtime)
co2overloadtime = world.time
else if((world.time - co2overloadtime) > 12 SECONDS)
throw_alert(ALERT_TOO_MUCH_CO2, /atom/movable/screen/alert/too_much_co2)
Unconscious(6 SECONDS)
// Lets hurt em a little, let them know we mean business.
adjustOxyLoss(3)
// They've been in here 30s now, start to kill them for their own good!
if((world.time - co2overloadtime) > 30 SECONDS)
adjustOxyLoss(8)
else
// Reset side-effects.
co2overloadtime = 0
clear_alert(ALERT_TOO_MUCH_CO2)
//PLASMA
if(Plasma_partialpressure > safe_plas_max)
var/ratio = (breath_gases[/datum/gas/plasma][MOLES]/safe_plas_max) * 10
//-- PLASMA --//
// Maximum Plasma effects. "Too much Plasma!"
if(plasma_pp > safe_plas_max)
// Plasma side-effects.
var/ratio = (breath_gases[/datum/gas/plasma][MOLES] / safe_plas_max) * 10
adjustToxLoss(clamp(ratio, MIN_TOXIC_GAS_DAMAGE, MAX_TOXIC_GAS_DAMAGE))
throw_alert(ALERT_TOO_MUCH_PLASMA, /atom/movable/screen/alert/too_much_plas)
else
// Reset side-effects.
clear_alert(ALERT_TOO_MUCH_PLASMA)
//NITROUS OXIDE
if(breath_gases[/datum/gas/nitrous_oxide])
var/SA_partialpressure = (breath_gases[/datum/gas/nitrous_oxide][MOLES]/breath.total_moles())*breath_pressure
if(SA_partialpressure > SA_para_min)
throw_alert(ALERT_TOO_MUCH_N2O, /atom/movable/screen/alert/too_much_n2o)
clear_mood_event("chemical_euphoria")
Unconscious(60)
if(SA_partialpressure > SA_sleep_min)
Sleeping(max(AmountSleeping() + 40, 200))
else if(SA_partialpressure > 0.01)
clear_alert(ALERT_TOO_MUCH_N2O)
if(prob(20))
emote(pick("giggle","laugh"))
add_mood_event("chemical_euphoria", /datum/mood_event/chemical_euphoria)
else
clear_mood_event("chemical_euphoria")
clear_alert(ALERT_TOO_MUCH_N2O)
else
clear_mood_event("chemical_euphoria")
clear_alert(ALERT_TOO_MUCH_N2O)
//-- TRACES --//
// If there's some other funk in the air lets deal with it here.
//BZ (Facepunch port of their Agent B)
if(breath_gases[/datum/gas/bz])
var/bz_partialpressure = (breath_gases[/datum/gas/bz][MOLES]/breath.total_moles())*breath_pressure
if(bz_partialpressure > 1)
//-- BZ --//
// (Facepunch port of their Agent B)
if(bz_pp)
if(bz_pp > 1)
adjust_hallucinations(20 SECONDS)
else if(bz_partialpressure > 0.01)
else if(bz_pp > 0.01)
adjust_hallucinations(10 SECONDS)
//NITRIUM
if(breath_gases[/datum/gas/nitrium])
var/nitrium_partialpressure = (breath_gases[/datum/gas/nitrium][MOLES]/breath.total_moles())*breath_pressure
if(nitrium_partialpressure > 0.5)
adjustFireLoss(nitrium_partialpressure * 0.15)
if(nitrium_partialpressure > 5)
adjustToxLoss(nitrium_partialpressure * 0.05)
//-- FREON --//
if(freon_pp)
adjustFireLoss(freon_pp * 0.25)
//FREON
if(breath_gases[/datum/gas/freon])
var/freon_partialpressure = (breath_gases[/datum/gas/freon][MOLES]/breath.total_moles())*breath_pressure
adjustFireLoss(freon_partialpressure * 0.25)
//MIASMA
if(breath_gases[/datum/gas/miasma])
var/miasma_partialpressure = (breath_gases[/datum/gas/miasma][MOLES]/breath.total_moles())*breath_pressure
if(prob(1 * miasma_partialpressure))
var/datum/disease/advance/miasma_disease = new /datum/disease/advance/random(2,3)
//-- MIASMA --//
if(!miasma_pp)
// Clear moodlet if no miasma at all.
clear_mood_event("smell")
else
// Miasma sickness
if(prob(1 * miasma_pp))
var/datum/disease/advance/miasma_disease = new /datum/disease/advance/random(max_symptoms = 2, max_level = 3)
miasma_disease.name = "Unknown"
ForceContractDisease(miasma_disease, TRUE, TRUE)
//Miasma side effects
switch(miasma_partialpressure)
ForceContractDisease(miasma_disease, make_copy = TRUE, del_on_fail = TRUE)
// Miasma side-effects.
switch(miasma_pp)
if(0.25 to 5)
// At lower pp, give out a little warning
clear_mood_event("smell")
@@ -294,16 +351,77 @@
else
clear_mood_event("smell")
//Clear all moods if no miasma at all
//-- NITROUS OXIDE --//
if(n2o_pp > n2o_para_min)
// More N2O, more severe side-effects. Causes stun/sleep.
n2o_euphoria = EUPHORIA_ACTIVE
throw_alert(ALERT_TOO_MUCH_N2O, /atom/movable/screen/alert/too_much_n2o)
// 60 gives them one second to wake up and run away a bit!
Unconscious(6 SECONDS)
// Enough to make the mob sleep.
if(n2o_pp > n2o_sleep_min)
Sleeping(max(AmountSleeping() + 40, 200))
else if(n2o_pp > 0.01)
// No alert for small amounts, but the mob randomly feels euphoric.
if(prob(20))
n2o_euphoria = EUPHORIA_ACTIVE
emote(pick("giggle","laugh"))
else
n2o_euphoria = EUPHORIA_INACTIVE
else
clear_mood_event("smell")
// Reset side-effects, for zero or extremely small amounts of N2O.
n2o_euphoria = EUPHORIA_INACTIVE
clear_alert(ALERT_TOO_MUCH_N2O)
//-- NITRIUM --//
if(nitrium_pp)
if(nitrium_pp > 0.5)
adjustFireLoss(nitrium_pp * 0.15)
if(nitrium_pp > 5)
adjustToxLoss(nitrium_pp * 0.05)
// Handle chemical euphoria mood event, caused by N2O.
if (n2o_euphoria == EUPHORIA_ACTIVE)
add_mood_event("chemical_euphoria", /datum/mood_event/chemical_euphoria)
else if (n2o_euphoria == EUPHORIA_INACTIVE)
clear_mood_event("chemical_euphoria")
// Activate mood on first flag, remove on second, do nothing on third.
if(has_moles)
handle_breath_temperature(breath)
breath.garbage_collect()
//BREATH TEMPERATURE
handle_breath_temperature(breath)
return TRUE
/// 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.
/mob/living/carbon/proc/handle_suffocation(breath_pp = 0, safe_breath_min = 0, true_pp = 0)
. = 0
// Can't suffocate without minimum breath pressure.
if(!safe_breath_min)
return
// Mob is suffocating.
failed_last_breath = TRUE
// Give them a chance to notice something is wrong.
if(prob(20))
emote("gasp")
// Mob is at critical health, check if they can be damaged further.
if(health < crit_threshold)
// Mob is immune to damage at critical health.
if(HAS_TRAIT(src, TRAIT_NOCRITDAMAGE))
return
// Reagents like Epinephrine stop suffocation at critical health.
if(reagents.has_reagent(/datum/reagent/medicine/epinephrine, needs_metabolizing = TRUE))
return
// Low pressure.
if(breath_pp)
var/ratio = safe_breath_min / breath_pp
adjustOxyLoss(min(5 * ratio, 3))
return true_pp * ratio / 6
// Zero pressure.
if(health >= crit_threshold)
adjustOxyLoss(3)
else
adjustOxyLoss(1)
/// Fourth and final link in a breath chain
/mob/living/carbon/proc/handle_breath_temperature(datum/gas_mixture/breath)
+410 -289
View File
@@ -24,7 +24,6 @@
//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
@@ -38,7 +37,7 @@
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 //nitrium and Freon
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
@@ -97,271 +96,317 @@
receiver.clear_alert(ALERT_NOT_ENOUGH_N2O)
return ..()
/**
* This proc tests if the lungs can breathe, if they can breathe a given gas mixture, and throws/clears gas alerts.
* If there are moles of gas in the given gas mixture, side-effects may be applied/removed on the mob.
* If a required gas (such as Oxygen) is missing from the breath, then it calls [proc/handle_suffocation].
*
* 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/internal/lungs/proc/check_breath(datum/gas_mixture/breath, mob/living/carbon/human/breather)
. = TRUE
if(breather.status_flags & GODMODE)
breather.failed_last_breath = FALSE //clear oxy issues
breather.failed_last_breath = FALSE
breather.clear_alert(ALERT_NOT_ENOUGH_OXYGEN)
return
if(HAS_TRAIT(breather, TRAIT_NOBREATH))
return
if(!breath || (breath.total_moles() == 0))
if(breather.reagents.has_reagent(crit_stabilizing_reagent, needs_metabolizing = TRUE))
return
if(breather.health >= breather.crit_threshold)
breather.adjustOxyLoss(HUMAN_MAX_OXYLOSS)
else if(!HAS_TRAIT(breather, TRAIT_NOCRITDAMAGE))
breather.adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS)
breather.failed_last_breath = TRUE
if(safe_oxygen_min)
breather.throw_alert(ALERT_NOT_ENOUGH_OXYGEN, /atom/movable/screen/alert/not_enough_oxy)
else if(safe_plasma_min)
breather.throw_alert(ALERT_NOT_ENOUGH_PLASMA, /atom/movable/screen/alert/not_enough_plas)
else if(safe_co2_min)
breather.throw_alert(ALERT_NOT_ENOUGH_CO2, /atom/movable/screen/alert/not_enough_co2)
else if(safe_nitro_min)
breather.throw_alert(ALERT_NOT_ENOUGH_NITRO, /atom/movable/screen/alert/not_enough_nitro)
return FALSE
// Breath may be null, so use a fallback "empty breath" for convenience.
if(!breath)
/// Fallback "empty breath" for convenience.
var/static/datum/gas_mixture/immutable/empty_breath = new(BREATH_VOLUME)
breath = empty_breath
// Ensure gas volumes are present.
for(var/gas_id in GLOB.meta_gas_info)
breath.assert_gas(gas_id)
if(istype(breather.wear_mask) && (breather.wear_mask.clothing_flags & GAS_FILTERING) && breather.wear_mask.has_filter)
breath = breather.wear_mask.consume_filter(breath)
var/gas_breathed = 0
// Indicates if there are moles of gas in the breath.
var/has_moles = breath.total_moles() != 0
// The list of gases in the breath.
var/list/breath_gases = breath.gases
//Partial pressures in our breath
var/O2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/oxygen][MOLES])+(8*breath.get_breath_partial_pressure(breath_gases[/datum/gas/pluoxium][MOLES]))
var/N2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrogen][MOLES])
var/Plasma_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/plasma][MOLES])
var/CO2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/carbon_dioxide][MOLES])
//Vars for n2o and healium induced euphorias.
// Indicates if lungs can breathe without gas.
var/can_breathe_vacuum = HAS_TRAIT(src, TRAIT_SPACEBREATHING)
// Re-usable var used to remove a limited volume of each gas from the given gas mixture.
var/gas_breathed = 0
// Vars for N2O/healium induced euphoria, stun, and sleep.
var/n2o_euphoria = EUPHORIA_LAST_FLAG
var/healium_euphoria = EUPHORIA_LAST_FLAG
//Handle subtypes' breath processing
handle_gas_override(breather,breath_gases, gas_breathed)
// Partial pressures in the breath.
// Main Gases
var/pluoxium_pp = 0
var/o2_pp = 0
var/n2_pp = 0
var/co2_pp = 0
var/plasma_pp = 0
// Trace Gases, ordered alphabetically.
var/bz_pp = 0
var/freon_pp = 0
var/healium_pp = 0
var/helium_pp = 0
var/halon_pp = 0
var/hypernob_pp = 0
var/miasma_pp = 0
var/n2o_pp = 0
var/nitrium_pp = 0
var/trit_pp = 0
var/zauker_pp = 0
//-- OXY --//
// 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_gases = breath.gases
// Partial pressures of "main" gases.
pluoxium_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/pluoxium][MOLES])
o2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/oxygen][MOLES]) + (8 * pluoxium_pp)
n2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrogen][MOLES])
co2_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/carbon_dioxide][MOLES])
plasma_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/plasma][MOLES])
// Partial pressures of "trace" gases.
bz_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/bz][MOLES])
freon_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/freon][MOLES])
halon_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/halon][MOLES])
healium_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/healium][MOLES])
helium_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/helium][MOLES])
hypernob_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/hypernoblium][MOLES])
miasma_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/miasma][MOLES])
n2o_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrous_oxide][MOLES])
nitrium_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrium][MOLES])
trit_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/tritium][MOLES])
zauker_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/zauker][MOLES])
//Too much oxygen! //Yes, some species may not like it.
// Breath has 0 moles of gas.
else if(can_breathe_vacuum)
// 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.adjustOxyLoss(-5)
else
// Can't breathe!
. = FALSE
breather.failed_last_breath = TRUE
// Handle subtypes' breath processing
handle_gas_override(breather, breath_gases, 0)
//-- MAIN GASES --//
//-- PLUOXIUM --//
// Behaves like Oxygen with 8X efficacy, but metabolizes into a reagent.
if(pluoxium_pp)
// Inhale Pluoxium. Exhale nothing.
breathe_gas_volume(breath_gases, /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))
//-- OXYGEN --//
// Maximum Oxygen effects. "Too much O2!"
// If too much Oxygen is poisonous.
if(safe_oxygen_max)
if(O2_pp > safe_oxygen_max)
var/ratio = (breath_gases[/datum/gas/oxygen][MOLES]/safe_oxygen_max) * 10
if(o2_pp && (o2_pp > safe_oxygen_max))
// O2 side-effects.
var/ratio = (breath_gases[/datum/gas/oxygen][MOLES] / safe_oxygen_max) * 10
breather.apply_damage_type(clamp(ratio, oxy_breath_dam_min, oxy_breath_dam_max), oxy_damage_type)
breather.throw_alert(ALERT_TOO_MUCH_OXYGEN, /atom/movable/screen/alert/too_much_oxy)
else
// Reset side-effects.
breather.clear_alert(ALERT_TOO_MUCH_OXYGEN)
//Too little oxygen!
// Minimum Oxygen effects.
// If the lungs need Oxygen to breathe properly, O2 is exchanged with CO2.
if(safe_oxygen_min)
if(O2_pp < safe_oxygen_min)
gas_breathed = handle_too_little_breath(breather, O2_pp, safe_oxygen_min, breath_gases[/datum/gas/oxygen][MOLES])
// Suffocation side-effects.
if(!can_breathe_vacuum && (o2_pp < safe_oxygen_min))
breather.throw_alert(ALERT_NOT_ENOUGH_OXYGEN, /atom/movable/screen/alert/not_enough_oxy)
// Inhale insufficient amount of O2, exhale CO2.
if(o2_pp)
gas_breathed = handle_suffocation(breather, o2_pp, safe_oxygen_min, breath_gases[/datum/gas/oxygen][MOLES])
breathe_gas_volume(breath_gases, /datum/gas/oxygen, /datum/gas/carbon_dioxide, volume = gas_breathed)
else
// Enough oxygen to breathe.
breather.failed_last_breath = FALSE
if(breather.health >= breather.crit_threshold)
breather.adjustOxyLoss(-5)
gas_breathed = breath_gases[/datum/gas/oxygen][MOLES]
breather.clear_alert(ALERT_NOT_ENOUGH_OXYGEN)
// Inhale Oxygen, exhale equivalent amount of CO2.
if(o2_pp)
breathe_gas_volume(breath_gases, /datum/gas/oxygen, /datum/gas/carbon_dioxide)
// Heal mob if not in crit.
if(breather.health >= breather.crit_threshold)
breather.adjustOxyLoss(-5)
//Exhale
breath_gases[/datum/gas/oxygen][MOLES] -= gas_breathed
breath_gases[/datum/gas/carbon_dioxide][MOLES] += gas_breathed
gas_breathed = 0
//-- Nitrogen --//
//Too much nitrogen!
//-- NITROGEN --//
// Maximum Nitrogen effects. "Too much N2!"
if(safe_nitro_max)
if(N2_pp > safe_nitro_max)
if(n2_pp && (n2_pp > safe_nitro_max))
// N2 side-effects.
var/ratio = (breath_gases[/datum/gas/nitrogen][MOLES]/safe_nitro_max) * 10
breather.apply_damage_type(clamp(ratio, nitro_breath_dam_min, nitro_breath_dam_max), nitro_damage_type)
breather.throw_alert(ALERT_TOO_MUCH_NITRO, /atom/movable/screen/alert/too_much_nitro)
else
// Reset side-effects.
breather.clear_alert(ALERT_TOO_MUCH_NITRO)
//Too little nitrogen!
// Minimum Nitrogen effects.
// If the lungs need Nitrogen to breathe properly, N2 is exchanged with CO2.
if(safe_nitro_min)
if(N2_pp < safe_nitro_min)
gas_breathed = handle_too_little_breath(breather, N2_pp, safe_nitro_min, breath_gases[/datum/gas/nitrogen][MOLES])
// Suffocation side-effects.
if(!can_breathe_vacuum && (n2_pp < safe_nitro_min))
breather.throw_alert(ALERT_NOT_ENOUGH_NITRO, /atom/movable/screen/alert/not_enough_nitro)
// Inhale insufficient amount of N2, exhale CO2.
if(n2_pp)
gas_breathed = handle_suffocation(breather, n2_pp, safe_nitro_min, breath_gases[/datum/gas/nitrogen][MOLES])
breathe_gas_volume(breath_gases, /datum/gas/nitrogen, /datum/gas/carbon_dioxide, volume = gas_breathed)
else
// Enough nitrogen to breathe.
breather.failed_last_breath = FALSE
if(breather.health >= breather.crit_threshold)
breather.adjustOxyLoss(-5)
gas_breathed = breath_gases[/datum/gas/nitrogen][MOLES]
breather.clear_alert(ALERT_NOT_ENOUGH_NITRO)
// Inhale N2, exhale equivalent amount of CO2. Look ma, sideways breathing!
if(n2_pp)
breathe_gas_volume(breath_gases, /datum/gas/nitrogen, /datum/gas/carbon_dioxide)
// Heal mob if not in crit.
if(breather.health >= breather.crit_threshold)
breather.adjustOxyLoss(-5)
//Exhale
breath_gases[/datum/gas/nitrogen][MOLES] -= gas_breathed
breath_gases[/datum/gas/carbon_dioxide][MOLES] += gas_breathed
gas_breathed = 0
//-- CO2 --//
//CO2 does not affect failed_last_breath. So if there was enough oxygen in the air but too much co2, this will hurt you, but only once per 4 ticks, instead of once per tick.
//-- CARBON DIOXIDE --//
// Maximum CO2 effects. "Too much CO2!"
if(safe_co2_max)
if(CO2_pp > safe_co2_max)
if(!breather.co2overloadtime) // 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.
breather.co2overloadtime = world.time
else if(world.time - breather.co2overloadtime > 120)
breather.Unconscious(60)
breather.apply_damage_type(3, co2_damage_type) // Lets hurt em a little, let them know we mean business
if(world.time - breather.co2overloadtime > 300) // They've been in here 30s now, lets start to kill them for their own good!
breather.apply_damage_type(8, co2_damage_type)
breather.throw_alert(ALERT_TOO_MUCH_CO2, /atom/movable/screen/alert/too_much_co2)
if(prob(20)) // Lets give them some chance to know somethings not right though I guess.
if(co2_pp && (co2_pp > safe_co2_max))
// CO2 side-effects.
// Give the mob a chance to notice.
if(prob(20))
breather.emote("cough")
// 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(!breather.co2overloadtime)
breather.co2overloadtime = world.time
else if((world.time - breather.co2overloadtime) > 12 SECONDS)
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_type(3, co2_damage_type)
// 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_type(8, co2_damage_type)
else
// Reset side-effects.
breather.co2overloadtime = 0
breather.clear_alert(ALERT_TOO_MUCH_CO2)
//Too little CO2!
// Minimum CO2 effects.
// If the lungs need CO2 to breathe properly, CO2 is exchanged with O2.
if(safe_co2_min)
if(CO2_pp < safe_co2_min)
gas_breathed = handle_too_little_breath(breather, CO2_pp, safe_co2_min, breath_gases[/datum/gas/carbon_dioxide][MOLES])
// Suffocation side-effects.
if(!can_breathe_vacuum && (co2_pp < safe_co2_min))
breather.throw_alert(ALERT_NOT_ENOUGH_CO2, /atom/movable/screen/alert/not_enough_co2)
// Inhale insufficient amount of CO2, exhale O2.
if(co2_pp)
gas_breathed = handle_suffocation(breather, co2_pp, safe_co2_min, breath_gases[/datum/gas/carbon_dioxide][MOLES])
breathe_gas_volume(breath_gases, /datum/gas/carbon_dioxide, /datum/gas/oxygen, volume = gas_breathed)
else
// Enough CO2 to breathe.
breather.failed_last_breath = FALSE
if(breather.health >= breather.crit_threshold)
breather.adjustOxyLoss(-5)
gas_breathed = breath_gases[/datum/gas/carbon_dioxide][MOLES]
breather.clear_alert(ALERT_NOT_ENOUGH_CO2)
//Exhale
breath_gases[/datum/gas/carbon_dioxide][MOLES] -= gas_breathed
breath_gases[/datum/gas/oxygen][MOLES] += gas_breathed
gas_breathed = 0
// Inhale CO2, exhale equivalent amount of O2. Look ma, reverse breathing!
if(co2_pp)
breathe_gas_volume(breath_gases, /datum/gas/carbon_dioxide, /datum/gas/oxygen)
// Heal mob if not in crit.
if(breather.health >= breather.crit_threshold)
breather.adjustOxyLoss(-5)
//-- PLASMA --//
//Too much plasma!
// Maximum Plasma effects. "Too much Plasma!"
if(safe_plasma_max)
if(Plasma_pp > safe_plasma_max)
var/ratio = (breath_gases[/datum/gas/plasma][MOLES]/safe_plasma_max) * 10
if(plasma_pp && (plasma_pp > safe_plasma_max))
// Plasma side-effects.
var/ratio = (breath_gases[/datum/gas/plasma][MOLES] / safe_plasma_max) * 10
breather.apply_damage_type(clamp(ratio, plas_breath_dam_min, plas_breath_dam_max), plas_damage_type)
breather.throw_alert(ALERT_TOO_MUCH_PLASMA, /atom/movable/screen/alert/too_much_plas)
else
// Reset side-effects.
breather.clear_alert(ALERT_TOO_MUCH_PLASMA)
//Too little plasma!
// Minimum Plasma effects.
// If the lungs need Plasma to breathe properly, Plasma is exchanged with CO2.
if(safe_plasma_min)
if(Plasma_pp < safe_plasma_min)
gas_breathed = handle_too_little_breath(breather, Plasma_pp, safe_plasma_min, breath_gases[/datum/gas/plasma][MOLES])
// Suffocation side-effects.
if(!can_breathe_vacuum && (plasma_pp < safe_plasma_min))
breather.throw_alert(ALERT_NOT_ENOUGH_PLASMA, /atom/movable/screen/alert/not_enough_plas)
// Breathe insufficient amount of Plasma, exhale CO2.
if(plasma_pp)
gas_breathed = handle_suffocation(breather, plasma_pp, safe_plasma_min, breath_gases[/datum/gas/plasma][MOLES])
breathe_gas_volume(breath_gases, /datum/gas/plasma, /datum/gas/carbon_dioxide, volume = gas_breathed)
else
// Enough Plasma to breathe.
breather.failed_last_breath = FALSE
if(breather.health >= breather.crit_threshold)
breather.adjustOxyLoss(-5)
gas_breathed = breath_gases[/datum/gas/plasma][MOLES]
breather.clear_alert(ALERT_NOT_ENOUGH_PLASMA)
//Exhale
breath_gases[/datum/gas/plasma][MOLES] -= gas_breathed
breath_gases[/datum/gas/carbon_dioxide][MOLES] += gas_breathed
gas_breathed = 0
// Inhale Plasma, exhale equivalent amount of CO2.
if(plasma_pp)
breathe_gas_volume(breath_gases, /datum/gas/plasma, /datum/gas/carbon_dioxide)
// Heal mob if not in crit.
if(breather.health >= breather.crit_threshold)
breather.adjustOxyLoss(-5)
//-- TRACES --//
// If there's some other shit in the air lets deal with it here.
if(breath) // If there's some other shit in the air lets deal with it here.
// Pluoxium
var/pluoxium_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/pluoxium][MOLES])
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))
gas_breathed = breath_gases[/datum/gas/pluoxium][MOLES]
breath_gases[/datum/gas/pluoxium][MOLES] -= gas_breathed
// N2O
var/n2o_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrous_oxide][MOLES])
if(n2o_pp > n2o_para_min) // Enough to make us stunned for a bit
breather.throw_alert(ALERT_TOO_MUCH_N2O, /atom/movable/screen/alert/too_much_n2o)
breather.Unconscious(60) // 60 gives them one second to wake up and run away a bit!
if(n2o_pp > n2o_sleep_min) // Enough to make us sleep as well
breather.Sleeping(min(breather.AmountSleeping() + 100, 200))
else if(n2o_pp > 0.01) // There is sleeping gas in their lungs, but only a little, so give them a bit of a warning
breather.clear_alert(ALERT_TOO_MUCH_N2O)
if(prob(20))
n2o_euphoria = EUPHORIA_ACTIVE
breather.emote(pick("giggle", "laugh"))
else
n2o_euphoria = EUPHORIA_INACTIVE
breather.clear_alert(ALERT_TOO_MUCH_N2O)
// BZ
var/bz_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/bz][MOLES])
//-- BZ --//
if(bz_pp)
if(bz_pp > BZ_trip_balls_min)
breather.adjust_hallucinations(20 SECONDS)
breather.reagents.add_reagent(/datum/reagent/bz_metabolites,5)
breather.reagents.add_reagent(/datum/reagent/bz_metabolites, 5)
if(bz_pp > BZ_brain_damage_min && prob(33))
breather.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3, 150)
breather.adjustOrganLoss(ORGAN_SLOT_BRAIN, 3, 150, ORGAN_ORGANIC)
// Tritium
var/trit_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/tritium][MOLES])
// 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)
gas_breathed = breath_gases[/datum/gas/tritium][MOLES]
if (trit_pp > 0)
var/ratio = gas_breathed * 15
breather.adjustToxLoss(clamp(ratio, MIN_TOXIC_GAS_DAMAGE, MAX_TOXIC_GAS_DAMAGE))
breath_gases[/datum/gas/tritium][MOLES] -= gas_breathed
// Nitrium
var/nitrium_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/nitrium][MOLES])
if (prob(nitrium_pp) && nitrium_pp > 15)
breather.adjustOrganLoss(ORGAN_SLOT_LUNGS, nitrium_pp * 0.1)
to_chat(breather, "<span class='notice'>You feel a burning sensation in your chest</span>")
gas_breathed = breath_gases[/datum/gas/nitrium][MOLES]
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, 1 - existing))
breath_gases[/datum/gas/nitrium][MOLES] -= gas_breathed
// Freon
var/freon_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/freon][MOLES])
//-- FREON --//
if(freon_pp)
// Inhale Freon. Exhale nothing.
breathe_gas_volume(breath_gases, /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)
if (freon_pp > 40)
breather.emote("gasp")
breather.adjustFireLoss(15)
if (prob(freon_pp/2))
if (prob(freon_pp / 2))
to_chat(breather, span_alert("Your throat closes up!"))
breather.set_silence_if_lower(6 SECONDS)
else
breather.adjustFireLoss(freon_pp/4)
gas_breathed = breath_gases[/datum/gas/freon][MOLES]
if (gas_breathed > gas_stimulation_min)
breather.reagents.add_reagent(/datum/reagent/freon,1)
breather.adjustFireLoss(freon_pp / 4)
breath_gases[/datum/gas/freon][MOLES]-=gas_breathed
//-- HALON --//
if(halon_pp)
// Inhale Halon. Exhale nothing.
breathe_gas_volume(breath_gases, /datum/gas/halon)
// Metabolize to reagent.
if(halon_pp > gas_stimulation_min)
breather.adjustOxyLoss(5)
breather.reagents.add_reagent(/datum/reagent/halon, max(0, 1 - breather.reagents.get_reagent_amount(/datum/reagent/halon)))
// Healium
var/healium_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/healium][MOLES])
//-- HEALIUM --//
// Sleeping gas with healing properties.
if(!healium_pp)
// Reset side-effects.
healium_euphoria = EUPHORIA_INACTIVE
else
// Inhale Healium. Exhale nothing.
breathe_gas_volume(breath_gases, /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!"))
@@ -369,131 +414,207 @@
breather.emote("gasp")
else
healium_euphoria = EUPHORIA_INACTIVE
// Stun/Sleep side-effects.
if(healium_pp > healium_para_min)
breather.Unconscious(rand(30, 50))//not in seconds to have a much higher variation
if(healium_pp > healium_sleep_min)
var/existing = breather.reagents.get_reagent_amount(/datum/reagent/healium)
breather.reagents.add_reagent(/datum/reagent/healium,max(0, 1 - existing))
gas_breathed = breath_gases[/datum/gas/healium][MOLES]
breath_gases[/datum/gas/healium][MOLES]-=gas_breathed
// Random chance to stun mob. Timing not in seconds to have a much higher variation
breather.Unconscious(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)))
// Proto Nitrate
// Inert
// Zauker
var/zauker_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/zauker][MOLES])
//-- HELIUM --//
// Activates helium speech when partial pressure gets high enough
if(!helium_pp)
helium_speech = FALSE
UnregisterSignal(owner, COMSIG_MOB_SAY)
else
// Inhale Helium. Exhale nothing.
breathe_gas_volume(breath_gases, /datum/gas/helium)
// Helium side-effects.
if(helium_speech && (helium_pp <= helium_speech_min))
helium_speech = FALSE
UnregisterSignal(owner, COMSIG_MOB_SAY)
else if(!helium_speech && (helium_pp > helium_speech_min))
helium_speech = TRUE
RegisterSignal(owner, COMSIG_MOB_SAY, PROC_REF(handle_helium_speech))
//-- HYPER-NOBILUM --//
if(hypernob_pp)
// Inhale Hyber-Nobilum. Exhale nothing.
breathe_gas_volume(breath_gases, /datum/gas/hypernoblium)
// Metabolize to reagent.
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))
//-- MIASMA --//
if(!miasma_pp || !suffers_miasma)
// Clear out moods when immune to miasma, or if there's no miasma at all.
owner.clear_mood_event("smell")
else
// Inhale Miasma. Exhale nothing.
breathe_gas_volume(breath_gases, /datum/gas/miasma)
// Miasma sickness
if(prob(0.5 * miasma_pp))
var/datum/disease/advance/miasma_disease = new /datum/disease/advance/random(max_symptoms = min(round(max(miasma_pp / 2, 1), 1), 6), max_level = min(round(max(miasma_pp, 1), 1), 8))
// tl;dr the first argument chooses the smaller of miasma_pp/2 or 6(typical max virus symptoms), the second chooses the smaller of miasma_pp or 8(max virus symptom level)
// Each argument has a minimum of 1 and rounds to the nearest value. Feel free to change the pp scaling I couldn't decide on good numbers for it.
miasma_disease.name = "Unknown"
miasma_disease.try_infect(owner)
// Miasma side effects
switch(miasma_pp)
if(0.25 to 5)
// At lower pp, give out a little warning
owner.clear_mood_event("smell")
if(prob(5))
to_chat(owner, 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(owner, span_warning("You smell something horribly decayed inside this room."))
owner.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(owner, span_warning("The stench of rotting carcasses is unbearable!"))
owner.add_mood_event("smell", /datum/mood_event/disgust/nauseating_stench)
owner.vomit()
if(30 to INFINITY)
//Higher chance to vomit. Let the horror start
if(prob(15))
to_chat(owner, span_warning("The stench of rotting carcasses is unbearable!"))
owner.add_mood_event("smell", /datum/mood_event/disgust/nauseating_stench)
owner.vomit()
else
owner.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
owner.adjust_disgust(0.1 * miasma_pp)
//-- N2O --//
// N2O side-effects. "Too much N2O!"
// Small amount of N2O, small side-effects. Causes random euphoria and giggling.
if (n2o_pp > n2o_para_min)
// More N2O, more severe side-effects. Causes stun/sleep.
n2o_euphoria = EUPHORIA_ACTIVE
breather.throw_alert(ALERT_TOO_MUCH_N2O, /atom/movable/screen/alert/too_much_n2o)
// 60 gives them one second to wake up and run away a bit!
breather.Unconscious(6 SECONDS)
// Enough to make the mob sleep.
if(n2o_pp > n2o_sleep_min)
breather.Sleeping(min(breather.AmountSleeping() + 100, 200))
else if(n2o_pp > 0.01)
// No alert for small amounts, but the mob randomly feels euphoric.
breather.clear_alert(ALERT_TOO_MUCH_N2O)
if(prob(20))
n2o_euphoria = EUPHORIA_ACTIVE
breather.emote(pick("giggle", "laugh"))
else
n2o_euphoria = EUPHORIA_INACTIVE
else
// Reset side-effects, for zero or extremely small amounts of N2O.
n2o_euphoria = EUPHORIA_INACTIVE
breather.clear_alert(ALERT_TOO_MUCH_N2O)
//-- NITRIUM --//
if (nitrium_pp)
// Inhale Nitrium. Exhale nothing.
breathe_gas_volume(breath_gases, /datum/gas/nitrium)
// Random chance to inflict side effects increases with pressure.
if((prob(nitrium_pp) && (nitrium_pp > 15)))
// Nitrium side-effect.
breather.adjustOrganLoss(ORGAN_SLOT_LUNGS, nitrium_pp * 0.1)
to_chat(breather, "<span class='notice'>You feel a burning sensation in your chest</span>")
// 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, 1 - existing))
//-- PROTO-NITRATE --//
// Inert
//-- TRITIUM --//
if (trit_pp)
// Inhale Tritium. Exhale nothing.
gas_breathed = breathe_gas_volume(breath_gases, /datum/gas/tritium)
// Tritium side-effects.
var/ratio = gas_breathed * 15
breather.adjustToxLoss(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)
//-- ZAUKER --//
if(zauker_pp)
// Inhale Zauker. Exhale nothing.
breathe_gas_volume(breath_gases, /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))
gas_breathed = breath_gases[/datum/gas/zauker][MOLES]
breath_gases[/datum/gas/zauker][MOLES]-=gas_breathed
// Halon
var/halon_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/halon][MOLES])
if(halon_pp > gas_stimulation_min)
breather.adjustOxyLoss(5)
var/existing = breather.reagents.get_reagent_amount(/datum/reagent/halon)
breather.reagents.add_reagent(/datum/reagent/halon,max(0, 1 - existing))
gas_breathed = breath_gases[/datum/gas/halon][MOLES]
breath_gases[/datum/gas/halon][MOLES]-=gas_breathed
// Hyper-Nob
gas_breathed = breath_gases[/datum/gas/hypernoblium][MOLES]
if (gas_breathed > gas_stimulation_min)
var/existing = breather.reagents.get_reagent_amount(/datum/reagent/hypernoblium)
breather.reagents.add_reagent(/datum/reagent/hypernoblium,max(0, 1 - existing))
breath_gases[/datum/gas/hypernoblium][MOLES]-=gas_breathed
// Helium
//Activates helium speech when partial pressure gets high enough
var/helium_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/helium][MOLES])
if(helium_pp > helium_speech_min && !helium_speech)
helium_speech = TRUE
RegisterSignal(owner, COMSIG_MOB_SAY, PROC_REF(handle_helium_speech))
else if(helium_pp <= helium_speech_min && helium_speech)
helium_speech = FALSE
UnregisterSignal(owner, COMSIG_MOB_SAY)
gas_breathed = breath_gases[/datum/gas/helium][MOLES]
breath_gases[/datum/gas/helium][MOLES] -= gas_breathed
// Miasma
if (breath_gases[/datum/gas/miasma] && suffers_miasma)
var/miasma_pp = breath.get_breath_partial_pressure(breath_gases[/datum/gas/miasma][MOLES])
//Miasma sickness
if(prob(0.5 * miasma_pp))
var/datum/disease/advance/miasma_disease = new /datum/disease/advance/random(min(round(max(miasma_pp/2, 1), 1), 6), min(round(max(miasma_pp, 1), 1), 8))
//tl;dr the first argument chooses the smaller of miasma_pp/2 or 6(typical max virus symptoms), the second chooses the smaller of miasma_pp or 8(max virus symptom level) //
miasma_disease.name = "Unknown"//^each argument has a minimum of 1 and rounds to the nearest value. Feel free to change the pp scaling I couldn't decide on good numbers for it.
miasma_disease.try_infect(owner)
// Miasma side effects
switch(miasma_pp)
if(0.25 to 5)
// At lower pp, give out a little warning
owner.clear_mood_event("smell")
if(prob(5))
to_chat(owner, 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(owner, span_warning("You smell something horribly decayed inside this room."))
owner.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(owner, span_warning("The stench of rotting carcasses is unbearable!"))
owner.add_mood_event("smell", /datum/mood_event/disgust/nauseating_stench)
owner.vomit()
if(30 to INFINITY)
//Higher chance to vomit. Let the horror start
if(prob(15))
to_chat(owner, span_warning("The stench of rotting carcasses is unbearable!"))
owner.add_mood_event("smell", /datum/mood_event/disgust/nauseating_stench)
owner.vomit()
else
owner.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
owner.adjust_disgust(0.1 * miasma_pp)
breath_gases[/datum/gas/miasma][MOLES]-=gas_breathed
// Clear out moods when no miasma at all
else
owner.clear_mood_event("smell")
if (n2o_euphoria == EUPHORIA_ACTIVE || healium_euphoria == EUPHORIA_ACTIVE)
owner.add_mood_event("chemical_euphoria", /datum/mood_event/chemical_euphoria)
else if (n2o_euphoria == EUPHORIA_INACTIVE && healium_euphoria == EUPHORIA_INACTIVE)
owner.clear_mood_event("chemical_euphoria")
// Activate mood on first flag, remove on second, do nothing on third.
// Handle chemical euphoria mood event, caused by gases such as N2O or healium.
if (n2o_euphoria == EUPHORIA_ACTIVE || healium_euphoria == EUPHORIA_ACTIVE)
owner.add_mood_event("chemical_euphoria", /datum/mood_event/chemical_euphoria)
else if (n2o_euphoria == EUPHORIA_INACTIVE && healium_euphoria == EUPHORIA_INACTIVE)
owner.clear_mood_event("chemical_euphoria")
// Activate mood on first flag, remove on second, do nothing on third.
if(has_moles)
handle_breath_temperature(breath, breather)
breath.garbage_collect()
return TRUE
breath.garbage_collect()
///override this for breath handling unique to lung subtypes, breath_gas is the list of gas in the breath while gas breathed is just what is being added or removed from that list, just as they are when this is called in check_breath()
/obj/item/organ/internal/lungs/proc/handle_gas_override(mob/living/carbon/human/breather, list/breath_gas, gas_breathed)
return
/obj/item/organ/internal/lungs/proc/handle_too_little_breath(mob/living/carbon/human/suffocator = null, breath_pp = 0, safe_breath_min = 0, true_pp = 0)
. = 0
if(!suffocator || !safe_breath_min) //the other args are either: Ok being 0 or Specifically handled.
return FALSE
/// Remove a volume of gas from the breath. Used to simulate absorbtion and interchange of gas in the lungs.
/// Removes all of the given gas type unless given a volume argument.
/// Returns the amount of gas theoretically removed.
/obj/item/organ/internal/lungs/proc/breathe_gas_volume(list/breath_gases, datum/gas/remove_gas, datum/gas/exchange_gas = null, volume = INFINITY)
volume = min(volume, breath_gases[remove_gas][MOLES])
breath_gases[remove_gas][MOLES] -= volume
if(exchange_gas)
breath_gases[exchange_gas][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/internal/lungs/proc/handle_suffocation(mob/living/carbon/human/suffocator = null, breath_pp = 0, safe_breath_min = 0, true_pp = 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(breath_pp > 0)
var/ratio = safe_breath_min/breath_pp
suffocator.adjustOxyLoss(min(5*ratio, HUMAN_MAX_OXYLOSS)) // Don't fuck them up too fast (space only does HUMAN_MAX_OXYLOSS after all!
suffocator.failed_last_breath = TRUE
. = true_pp*ratio/6
else
// 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.adjustOxyLoss(min(5 * ratio, HUMAN_MAX_OXYLOSS))
return true_pp * ratio / 6
// Zero pressure.
if(suffocator.health >= suffocator.crit_threshold)
suffocator.adjustOxyLoss(HUMAN_MAX_OXYLOSS)
suffocator.failed_last_breath = TRUE
else
suffocator.adjustOxyLoss(HUMAN_CRIT_MAX_OXYLOSS)
/obj/item/organ/internal/lungs/proc/handle_breath_temperature(datum/gas_mixture/breath, mob/living/carbon/human/breather) // called by human/life, handles temperatures