Files
Bubberstation/code/datums/diseases/asthma_attack.dm
nikothedude fadbf16e1b Asthma quirk, inhalers - Electric Boogaloo (#92747)
## About The Pull Request

Revival of https://github.com/tgstation/tgstation/pull/79691

A while back, I made this PR, but lost motivation after diving too deep
into the code soup of can_breathe and related procs. Now, I have removed
those parts, and have simplified that part of the code to the point I
think it's ready for review.

Many reviews from the previous PR have been addressed in this PR.

<hr>

<details>
  <summary>Details</summary>
  
Asthma is a 4 point negative quirk that emulates real life asthma. It
works by slowly decreasing the amount of pressure each breath you take
receives, until your lungs completely seal, ensuring death if you dont
get oxyloss meds/windpipe surgery.

Inflammation (the tracker for intensity) increases whenever you breathe
smoke, use a cigarette, metabolize histimine, or suffer an asthma
attack.

Asthma attacks have a low chance of happening every second, starting 10
minutes after you spawn and with a 20-30 grace period between attacks.
They are "diseases" that cant be outright cured by albuterol, only put
into remission. They increase inflammation at varying rates depending on
their severity, with extreme asthma attacks being a immediate threat to
your life while mild ones might not even cause inflammation. The
response to these is always the same - use your inhaler before you start
choking.

Asthmatics start with a rescue inhaler, a low-capacity inhaler loaded
with albuterol, which I will get to later.

Albuterol is a new medicine thats a little tricky to make but still
doable. It can be efficiently created with inverse convermol, or
transmutated from salbutamol and convermol. The opposite is true, with
albuterol able to be turned into salbutamol. Two canisters are available
in chemvends.

Upon use, it increases the virtual pressure of all breaths taken by 40%.
This allows for you to breathe in lower pressure environments, as well
as enhancing the effects of things like healium.

It's OD causes your diaphram to spasm, causing sporadic losebreath and
forced breathing.

Inhalers are a fancy new reagent application apparatus that uses the
INHALE reagent bitflag.

Inhalers themselves are rather unremarkable, they are merely the method
of using inhaler canisters (they also have a rotary display
approximating the uses left in a canister - just like real life
inhalers).

Inhaler canisters are the reagent containers, and are generally low
capacity. They can only be used in a inhaler, and contain aerosolized
chemicals.

Inhaler canisters and inhalers are unlocked from chemical synthesis, and
are printable for cheap from a medlathe.

In order to use a inhaler, one must uncover the mouth of a carbon and
wait a few seconds (its faster if its a self-application) before a small
amount of the reagents are delivered via the INHALE bitflag. This only
works on things currently breathing - if theyre dead, have no lungs, or
just, arent breathing - it will fail. This includes asthmatics with 100%
inflammation.
  
</details>

<img width="181" height="74"
alt="282863233-77a7cd6b-44d2-458e-9966-06d485df1521"
src="https://github.com/user-attachments/assets/293b6659-0834-4e9a-b033-cc3b0cfde18e"
/>

<img width="1465" height="202"
alt="282863346-2a247736-0c3a-43b0-a60b-7cff10ce4963"
src="https://github.com/user-attachments/assets/5d9a13dc-b7b2-4de2-adda-8fbc8276e667"
/>


Sprites are not mine; they are from swanni and I can NOT sprite for the
life of me
## Why It's Good For The Game

1. Asthma is just cool. One of my favorite features on bay was the fact
lung damage required you to turn up the pressure on your O2 tank to
survive, and this does precisely that.
2. Its always fun to add new ways to interact with atmos as a player
that arent grossly broken, and I fail to see how a 40% increase of gas
intake will really affect balance too badly.
3. Inhalers are badass.
## Changelog
🆑
add: Asthma quirk, based on IRL asthma
add: Inhalers, a new reagent administering method that uses INHALE
add: Albuterol, a new reagent that increases the amount of gas you
inhale by 40%
balance: Inverse convermol now forms once the reaction is done, not on
metabolize
/🆑

---------

Co-authored-by: SyncIt21 <110812394+SyncIt21@users.noreply.github.com>
Co-authored-by: MrMelbert <51863163+MrMelbert@users.noreply.github.com>
2025-09-08 13:31:21 +12:00

263 lines
8.1 KiB
Plaintext

/datum/disease/asthma_attack
form = "Bronchitis"
name = "Asthma attack"
desc = "Subject is undergoing a autoimmune response which threatens to close the esophagus and halt all respiration, leading to death. \
Minor asthma attacks may disappear on their own, but all are dangerous."
cure_text = "Albuterol/Surgical intervention"
cures = list(/datum/reagent/medicine/albuterol)
agent = "Inflammatory"
viable_mobtypes = list(/mob/living/carbon/human)
disease_flags = CURABLE
spread_flags = DISEASE_SPREAD_NON_CONTAGIOUS
spread_text = "Inflammatory"
visibility_flags = HIDDEN_PANDEMIC
bypasses_immunity = TRUE
disease_flags = CURABLE|INCREMENTAL_CURE
required_organ = ORGAN_SLOT_LUNGS
infectable_biotypes = MOB_ROBOTIC|MOB_ORGANIC|MOB_MINERAL|MOB_UNDEAD
/// The world.time after which we will begin remission.
var/time_to_start_remission
/// The max time, after initial infection, it will take for us to begin remission
var/max_time_til_remission
/// The min time, after initial infection, it will take for us to begin remission
var/min_time_til_remission
/// Are we in remission, where we stop progressing and instead slowly degrade in intensity until we remove ourselves?
var/in_remission = FALSE
/// The current progress to stage demotion. Resets to 0 and reduces our stage by 1 when it exceeds [progress_needed_to_demote]. Only increases when in remission.
var/progress_to_stage_demotion = 0
/// The amount of demotion progress we receive per second while in remission.
var/progress_to_demotion_per_second = 1
/// Once [progress_to_stage_demotion] exceeds or meets this, we reduce our stage.
var/progress_needed_to_demote = 10
/// Do we alert ghosts when we are applied?
var/alert_ghosts = FALSE
/// A assoc list of (severity -> string), where string will be suffixed to our name in (suffix) format.
var/static/list/severity_to_suffix = list(
DISEASE_SEVERITY_MEDIUM = "Minor",
DISEASE_SEVERITY_HARMFUL = "Moderate",
DISEASE_SEVERITY_DANGEROUS = "Severe",
DISEASE_SEVERITY_BIOHAZARD = "EXTREME",
)
/// A assoc list of (stringified number -> number), where the key is the stage and the number is how much inflammation we will cause the asthmatic per second.
var/list/stage_to_inflammation_per_second
/datum/disease/asthma_attack/New()
. = ..()
suffix_name()
time_to_start_remission = world.time + rand(min_time_til_remission, max_time_til_remission)
/datum/disease/asthma_attack/try_infect(mob/living/infectee, make_copy)
if (!get_asthma_quirk())
return FALSE
if (HAS_TRAIT(infectee, TRAIT_NOBREATH))
return FALSE
return ..()
/// Adds our suffix via [severity_to_suffix] in the format of (suffix) to our name.
/datum/disease/asthma_attack/proc/suffix_name()
name += " ([severity_to_suffix[severity]])"
/// Returns the asthma quirk of our victim. As we can only be applied to asthmatics, this should never return null.
/datum/disease/asthma_attack/proc/get_asthma_quirk(mob/living/target = affected_mob)
RETURN_TYPE(/datum/quirk/item_quirk/asthma)
return (locate(/datum/quirk/item_quirk/asthma) in target.quirks)
/datum/disease/asthma_attack/stage_act(seconds_per_tick, times_fired)
. = ..()
if (!.)
return
if (HAS_TRAIT(affected_mob, TRAIT_NOBREATH))
cure()
return FALSE
var/datum/quirk/item_quirk/asthma/asthma_quirk = get_asthma_quirk()
var/inflammation = stage_to_inflammation_per_second["[stage]"]
if (inflammation)
asthma_quirk.adjust_inflammation(inflammation * seconds_per_tick)
if (!(world.time >= time_to_start_remission))
return
if (!in_remission)
in_remission = TRUE
stage_prob = 0
name += " (Remission)"
desc += " <i>The attack has entered remission. It will slowly decrease in intensity before vanishing.</i>"
progress_to_stage_demotion += (progress_to_demotion_per_second * seconds_per_tick)
if (progress_to_stage_demotion >= progress_needed_to_demote)
progress_to_stage_demotion = 0
update_stage(stage - 1)
// TYPES OF ASTHMA ATTACK
/datum/disease/asthma_attack/minor
severity = DISEASE_SEVERITY_MEDIUM
stage_prob = 4
max_time_til_remission = 120 SECONDS
min_time_til_remission = 80 SECONDS
max_stages = 3
cure_chance = 20
stage_to_inflammation_per_second = list(
"2" = 0.3,
"3" = 0.6,
)
/datum/disease/asthma_attack/minor/stage_act(seconds_per_tick, times_fired)
. = ..()
if (!.)
return FALSE
if (SPT_PROB(5, seconds_per_tick))
to_chat(affected_mob, span_warning(pick("Mucous runs down the back of your throat.", "You swallow excess mucus.")))
/datum/disease/asthma_attack/moderate
severity = DISEASE_SEVERITY_HARMFUL
stage_prob = 5
max_time_til_remission = 120 SECONDS
min_time_til_remission = 80 SECONDS
max_stages = 4
cure_chance = 20
stage_to_inflammation_per_second = list(
"2" = 1,
"3" = 2,
"4" = 4,
)
/datum/disease/asthma_attack/moderate/stage_act(seconds_per_tick, times_fired)
. = ..()
if (!.)
return FALSE
if (SPT_PROB(15, seconds_per_tick))
to_chat(affected_mob, span_warning(pick("Mucous runs down the back of your throat.", "You swallow excess mucus.")))
if (stage < 4 || !SPT_PROB(10, seconds_per_tick))
return
to_chat(affected_mob, span_warning("You briefly choke on the mucus piling in your throat!"))
affected_mob.losebreath++
/datum/disease/asthma_attack/severe
severity = DISEASE_SEVERITY_DANGEROUS
stage_prob = 6
max_time_til_remission = 80 SECONDS
min_time_til_remission = 60 SECONDS
max_stages = 5
cure_chance = 20
stage_to_inflammation_per_second = list(
"2" = 1,
"3" = 3,
"4" = 6,
"5" = 8,
)
visibility_flags = HIDDEN_SCANNER
alert_ghosts = TRUE
/datum/disease/asthma_attack/severe/stage_act(seconds_per_tick, times_fired)
. = ..()
if (!.)
return FALSE
if (stage > 1)
visibility_flags &= ~HIDDEN_SCANNER // revealed
if (SPT_PROB(15, seconds_per_tick))
to_chat(affected_mob, span_warning(pick("Mucous runs down the back of your throat.", "You swallow excess mucus.")))
else if (SPT_PROB(20, seconds_per_tick))
affected_mob.emote("cough")
if (stage < 4 || !SPT_PROB(15, seconds_per_tick))
return
to_chat(affected_mob, span_warning("You briefly choke on the mucus piling in your throat!"))
affected_mob.losebreath++
/datum/disease/asthma_attack/critical
severity = DISEASE_SEVERITY_BIOHAZARD
stage_prob = 85
max_time_til_remission = 60 SECONDS // this kills you extremely quickly, so its fair
min_time_til_remission = 40 SECONDS
max_stages = 6
cure_chance = 30
stage_to_inflammation_per_second = list(
"1" = 5,
"2" = 6,
"3" = 7,
"4" = 10,
"5" = 20,
"6" = 500, // youre fucked frankly
)
/// Have we warned our user of the fact they are at stage 5? If no, and are at or above stage five, we send a warning and set this to true.
var/warned_user = FALSE
/// Have we ever reached our max stage? If no, and we are at our max stage, we send a ominous message warning them of their imminent demise.
var/max_stage_reached = FALSE
/datum/disease/asthma_attack/critical/stage_act(seconds_per_tick, times_fired)
. = ..()
if (!.)
return FALSE
if (stage < 5)
if (SPT_PROB(75, seconds_per_tick))
to_chat(affected_mob, span_warning(pick("Mucous runs down the back of your throat.", "You swallow excess mucus.")))
var/wheeze_chance
if (!warned_user && stage >= 5)
to_chat(affected_mob, span_userdanger("You feel like your lungs are filling with fluid! It's getting incredibly hard to breathe!"))
warned_user = TRUE
switch (stage)
if (1)
wheeze_chance = 0
if (2)
wheeze_chance = 20
if (3)
wheeze_chance = 40
if (4)
wheeze_chance = 60
if (5)
wheeze_chance = 80
if (!in_remission)
stage_prob = 10 // slow it down significantly
if (6)
if (!max_stage_reached)
max_stage_reached = TRUE
to_chat(affected_mob, span_userdanger("You feel your windpipe squeeze shut!"))
wheeze_chance = 0
if (SPT_PROB(10, seconds_per_tick))
affected_mob.emote("gag")
var/datum/quirk/item_quirk/asthma/asthma_quirk = get_asthma_quirk()
asthma_quirk.adjust_inflammation(INFINITY)
if (SPT_PROB(wheeze_chance, seconds_per_tick))
affected_mob.emote("wheeze")
if (stage < 4 || !SPT_PROB(15, seconds_per_tick))
return
to_chat(affected_mob, span_warning("You briefly choke on the mucus piling in your throat!"))
affected_mob.losebreath++