Files
Bubberstation/code/modules/events/heart_attack.dm
Rhials 3db49f75cb Heart Attacks are More Violent, and You Can Defib People Repeatedly to Fix Them Like in a Medical Daytime TV Show Drama (#89775)
## About The Pull Request

This overhauls nearly all aspects related to heart attacks, making them
more urgent and threatening. It also enables the use of defibrillators
for quicker, less invasive treatment of heart attacks.

They are now a status effect instead of a virus, with a fixed countdown
of about two and a half minutes before full stoppage. As this timer
counts down, symptoms steadily escalate from audio cues to suffocation
and dizziness. Through treatment, the countdown can be wound back and
eventually cured (or you can just swap the heart like usual. Boring!!).

### Treatment

Replacing the heart is no longer the *only* option for treatment. Now,
heart attacks can be cured with the use of a defib. Each shock will
rewind the countdown (and symptoms) heavily, curing it after 2-4 zaps in
quick succession. In lieu of a defib, most other shock sources will
work, albeit much less effectively.

So, if you're in security and *really* don't feel like going to medical,
you can just have your fellow officers taze you back to health!

![doctor
approved](https://github.com/user-attachments/assets/f633cb08-b7f7-4291-8c18-6fdea4e97c7a)

The emergency penthrite given after the attack has a new icon/message,
and is slightly less forgiving. You can still use it to shock yourself
back to life, still being a very risky move.

The heart attack doesn't manifest on medical HUDs immediately. It only
appears after the victim has reached stage 3 for the first time. At
stage 3, it's pretty apparent to both the victim and anyone around that
they're having a heart attack anyways, but until then it will be on the
victim to identify symptoms. Heart attacks will always be reported on
health analyzer scans, shown as the organ failing due to a myocardial
infarction and a tooltip suggesting defibs/other shocks to treat it.

This also adds two tips, for secoffs and medics, on how to treat a heart
attack.

Defibs also now use up charge for restarting a stopped heart, instead of
only when reviving a corpse.
## Why It's Good For The Game

Making heart attacks more of a threat gives way more weight to something
people should be afraid of, but aren't. Heart attacks fail to emulate
anything resembling a heart attack, to the point that it is lame and
dissapointing. They're an annoyance and a trip to the surgery room for
anyone familiar enough with the game. This iteration may be more
dangerous, but it's also more *exciting* (and can be treated faster).

Broadening the ways to treat a heart attack, or just stave off symptoms,
gives an amount of player agency over this threat. It may be more
urgent, but it doesn't mean it has to be a trip to medbay every time.
You can look for other ways to get zapped (security will help if asked,
I promise) or sandbox up your own solution.

Also, treating heart attacks with defib shocks just makes sense. Why do
we have to trash the whole heart? Usually after a heart attack it still
has like, a year or so of juice left before it quits for good.

More threatening heart attacks also further incentivize synthetic hearts
and working out, or any other clever ways of shielding oneself from an
attack (in case you're paranoid like that).

And lastly, the whole reason I started with this PR in the first place
-- I think it is a bit odd having heart attacks be handled as *diseases*
in the code. Status effects make more sense.
## Changelog
🆑 Rhials
balance: Heart attacks are now more violent and urgent. If you can hear
your own heartbeat and are short of breath, you should probably see a
doctor.
balance: Heart attacks can now be treated and eventually cured with
repeated defibrillation shocks.
balance: Heart attacks can now be treated and eventually cured with
repeated shocks from other sources.
/🆑
2025-04-07 00:15:38 +01:00

104 lines
5.2 KiB
Plaintext

/datum/round_event_control/heart_attack
name = "Random Heart Attack"
typepath = /datum/round_event/heart_attack
weight = 20
max_occurrences = 2
min_players = 40 // To avoid shafting lowpop
category = EVENT_CATEGORY_HEALTH
description = "A random crewmember's heart gives out."
min_wizard_trigger_potency = 6
max_wizard_trigger_potency = 7
admin_setup = list(/datum/event_admin_setup/minimum_candidate_requirement/heart_attack, /datum/event_admin_setup/input_number/heart_attack)
///Candidates for receiving a healthy dose of heart disease
var/list/heart_attack_candidates = list()
/datum/round_event_control/heart_attack/can_spawn_event(players_amt, allow_magic = FALSE)
. = ..()
if(!.)
return .
generate_candidates() //generating candidates and checking in can_spawn_event prevents extreme edge case of there being the 40 minimum players, with all being ineligible for a heart attack, wasting the event
if(length(heart_attack_candidates))
return TRUE
/**
* Performs initial analysis of which living players are eligible to be selected for a heart attack.
*
* Traverses player_list and checks entries against a series of reviews to see if they should even be considered for a heart attack,
* and at what weight should they be eligible to receive it. The check for whether or not a heart attack should be "blocked" by something is done
* later, at the round_event level, so this proc mostly just checks users for whether or not a heart attack should be possible.
*/
/datum/round_event_control/heart_attack/proc/generate_candidates()
heart_attack_candidates.Cut()
for(var/mob/living/carbon/human/candidate in shuffle(GLOB.player_list))
if(candidate.stat == DEAD || HAS_TRAIT(candidate, TRAIT_CRITICAL_CONDITION) || !candidate.can_heartattack() || (candidate.has_status_effect(/datum/status_effect/heart_attack)) || candidate.undergoing_cardiac_arrest())
continue
if(!(candidate.mind.assigned_role.job_flags & JOB_CREW_MEMBER))//only crewmembers can get one, a bit unfair for some ghost roles and it wastes the event
continue
if(candidate.satiety <= -60 && !candidate.has_status_effect(/datum/status_effect/exercised)) //Multiple junk food items recently //No foodmaxxing for the achievement
heart_attack_candidates[candidate] = 3
else
heart_attack_candidates[candidate] = 1
/datum/round_event/heart_attack
///A list of prime candidates for heart attacking
var/list/victims = list()
///Number of heart attacks to distribute
var/quantity = 1
/datum/round_event/heart_attack/start()
var/datum/round_event_control/heart_attack/heart_control = control
victims += heart_control.heart_attack_candidates
heart_control.heart_attack_candidates.Cut()
while(quantity > 0 && length(victims))
if(attack_heart())
quantity--
/**
* Picks a victim from a list and attempts to give them a heart attack
*
* Performs a pick_weight on a list of potential victims. Once selected, the "winner"
* will receive heart disease. Returns TRUE if a heart attack is successfully given, and
* FALSE if something blocks it.
*/
/datum/round_event/heart_attack/proc/attack_heart()
var/mob/living/carbon/human/winner = pick_weight(victims)
if(winner.has_status_effect(/datum/status_effect/exercised)) //Stuff that should "block" a heart attack rather than just deny eligibility for one goes here.
winner.visible_message(span_warning("[winner] grunts and clutches their chest for a moment, catching [winner.p_their()] breath."), span_medal("Your chest lurches in pain for a brief moment, which quickly fades. \
You feel like you've just avoided a serious health disaster."), span_hear("You hear someone's breathing sharpen for a moment, followed by a sigh of relief."), 4)
winner.playsound_local(get_turf(winner), 'sound/effects/health/slowbeat.ogg', 40, 0, channel = CHANNEL_HEARTBEAT, use_reverb = FALSE)
winner.Stun(3 SECONDS)
if(winner.client)
winner.client.give_award(/datum/award/achievement/misc/healthy, winner)
message_admins("[winner] has just survived a random heart attack!") //time to spawn them a trophy :)
victims -= winner
else
winner.apply_status_effect(/datum/status_effect/heart_attack)
announce_to_ghosts(winner)
victims -= winner
return TRUE
return FALSE
/datum/event_admin_setup/minimum_candidate_requirement/heart_attack
output_text = "There are no candidates eligible to receive a heart attack!"
/datum/event_admin_setup/minimum_candidate_requirement/heart_attack/count_candidates()
var/datum/round_event_control/heart_attack/heart_control = event_control
heart_control.generate_candidates() //can_spawn_event() is bypassed by admin_setup, so this makes sure that the candidates are still generated
return length(heart_control.heart_attack_candidates)
/datum/event_admin_setup/input_number/heart_attack
input_text = "Please select how many people's days you wish to ruin."
default_value = 0
max_value = 90 //Will be overridden
min_value = 0
/datum/event_admin_setup/input_number/heart_attack/prompt_admins()
var/datum/round_event_control/heart_attack/heart_control = event_control
max_value = length(heart_control.heart_attack_candidates)
return ..()
/datum/event_admin_setup/input_number/heart_attack/apply_to_event(datum/round_event/heart_attack/event)
event.quantity = chosen_value