Reworks CPR, allowing it to extend defibrillation time (#21754)

* Update traditional CPR to loop automatically

* Implement most of the base stuff

* Bring over most defib changes so far

* Checks and fixes

* cleans up some leftovers, getting it ready for review

* Little bit of IPC flavor

* excludes non oxy-breathers, sorry!

* Better rescue breath handling

* Refactor revivability into a status effect

* Adds a little bit of feedback

* Removes the original variables

* Allows for CPR to activate cavity bombs

* Update code/modules/mob/living/carbon/human/human_mob.dm

Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com>

* snake case time

* breathid instead

* remove unused proc

---------

Co-authored-by: Henri215 <77684085+Henri215@users.noreply.github.com>
This commit is contained in:
Luc
2023-09-09 21:59:47 -05:00
committed by GitHub
co-authored by Henri215
parent e3eed1d23f
commit d392209cb5
14 changed files with 193 additions and 33 deletions
@@ -405,7 +405,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp
create_log(MISC_LOG, "DNR Enabled")
can_reenter_corpse = FALSE
if(!QDELETED(mind.current)) // Could change while they're choosing
mind.current.med_hud_set_status()
mind.current.remove_status_effect(STATUS_EFFECT_REVIVABLE)
/mob/dead/observer/proc/dead_tele()
set category = "Ghost"
-5
View File
@@ -213,11 +213,6 @@
swap_hand()
/mob/living/carbon/proc/help_shake_act(mob/living/carbon/M)
if(stat == DEAD)
if(M != src)
M.visible_message("<span class='notice'>[M] desperately shakes [src] trying to wake [p_them()] up, but sadly there is no reaction!</span>", \
"<span class='notice'>You shake [src] trying to wake [p_them()], sadly they appear to be too far gone!</span>")
return
if(health < HEALTH_THRESHOLD_CRIT)
return
if(src == M && ishuman(src))
@@ -4,6 +4,9 @@
if(!.)
return FALSE
if(!gibbed)
apply_status_effect(STATUS_EFFECT_REVIVABLE)
if(reagents)
reagents.death_metabolize(src)
+122 -19
View File
@@ -1681,49 +1681,152 @@ Eyes need to have significantly high darksight to shine unless the mob has the X
return
..()
#define CPR_CHEST_COMPRESSION_ONLY 0.75
#define CPR_RESCUE_BREATHS 1
#define CPR_CHEST_COMPRESSION_RESTORATION (2 SECONDS)
#define CPR_BREATHS_RESTORATION (4 SECONDS)
/mob/living/carbon/human/proc/do_cpr(mob/living/carbon/human/H)
var/static/list/effective_cpr_messages = list(
"You feel like you're able to stave off the inevitable for a little longer.",
"You can still see the color in their cheeks."
)
var/static/list/ineffective_cpr_messages = list(
"You're starting to feel them stiffen under you, but you keep going.",
"Without rescue breaths, they seem to be turning a little blue, but you press on.",
)
if(H == src)
to_chat(src, "<span class='warning'>You cannot perform CPR on yourself!</span>")
return
if(H.stat == DEAD || HAS_TRAIT(H, TRAIT_FAKEDEATH))
to_chat(src, "<span class='warning'>[H.name] is dead!</span>")
return
if(H.receiving_cpr) // To prevent spam stacking
to_chat(src, "<span class='warning'>They are already receiving CPR!</span>")
return
if(!can_use_hands() || !has_both_hands())
to_chat(src, "<span class='warning'>You need two hands available to do CPR!</span>")
return
if(l_hand || r_hand)
to_chat(src, "<span class='warning'>You can't perform effective CPR with your hands full!</span>")
return
H.receiving_cpr = TRUE
var/cpr_modifier = get_cpr_mod(H)
visible_message("<span class='danger'>[src] is trying to perform CPR on [H.name]!</span>", "<span class='danger'>You try to perform CPR on [H.name]!</span>")
if(do_mob(src, H, 4 SECONDS))
if(H.health <= HEALTH_THRESHOLD_CRIT)
H.adjustOxyLoss(-15 * cpr_modifier)
H.SetLoseBreath(0)
H.AdjustParalysis(-2 SECONDS)
H.updatehealth("cpr")
visible_message("<span class='danger'>[src] performs CPR on [H.name]!</span>", "<span class='notice'>You perform CPR on [H.name].</span>")
if(cpr_modifier == CPR_RESCUE_BREATHS)
to_chat(H, "<span class='notice'>You feel a breath of fresh air enter your lungs. It feels good.</span>")
if(H.stat == DEAD || HAS_TRAIT(H, TRAIT_FAKEDEATH))
if(ismachineperson(H) && do_mob(src, H, 4 SECONDS)) // hehe
visible_message(
"<span class='warning'>[src] bangs [p_their()] head on [H]'s chassis by accident!</span>",
"<span class='danger'>You go in for a rescue breath, and bang your head on [H]'s <i>machine</i> chassis. CPR's not going to work.</span>"
)
playsound(H, 'sound/weapons/ringslam.ogg', 50, TRUE)
adjustBruteLossByPart(2, "head")
H.receiving_cpr = FALSE
add_attack_logs(src, H, "CPRed", ATKLOG_ALL)
return TRUE
else
return
if(HAS_TRAIT(H, TRAIT_FAKEDEATH) || !H.is_revivable())
to_chat(src, "<span class='warning'>[H] is already too far gone for CPR...</span>")
H.receiving_cpr = FALSE
return
visible_message("<span class='danger'>[src] is trying to perform CPR on [H]'s lifeless body!</span>", "<span class='danger'>You start trying to perform CPR on [H]'s lifeless body!</span>")
while(do_mob(src, H, 4 SECONDS) && (H.stat == DEAD) && H.is_revivable())
var/timer_restored
if(cpr_modifier == CPR_CHEST_COMPRESSION_ONLY)
visible_message("<span class='notice'>[src] gives [H] chest compressions.</span>", "<span class='notice'>You can't make rescue breaths work, so you do your best to give chest compressions.</span>")
timer_restored = CPR_CHEST_COMPRESSION_RESTORATION // without rescue breaths, it won't stave off the death timer forever
else
visible_message("<span class='notice'>[src] gives [H] chest compressions and rescue breaths.</span>", "<span class='notice'>You give [H] chest compressions and rescue breaths.</span>")
timer_restored = CPR_BREATHS_RESTORATION // this, however, should keep it indefinitely postponed assuming CPR continues
if(HAS_TRAIT(H, TRAIT_FAKEDEATH))
if(prob(25))
to_chat(H, "<span class='userdanger'>Your chest burns as you receive unnecessary CPR!</span>")
continue
// this is the amount of time that should get added onto the timer.
var/new_time = (timer_restored + H.get_cpr_timer_adjustment(cpr_modifier))
SEND_SIGNAL(H, COMSIG_HUMAN_RECEIVE_CPR, (new_time SECONDS))
if(prob(5))
if(timer_restored > 4 SECONDS)
to_chat(pick(effective_cpr_messages))
else
to_chat(pick(ineffective_cpr_messages))
cpr_try_activate_bomb(H)
if(!H.is_revivable())
to_chat(src, "<span class='notice'>You feel [H]'s body is already starting to stiffen beneath you...it's too late for CPR now.</span>")
else
visible_message("<span class='notice'>[src] stops giving [H] CPR.</span>", "<span class='notice'>You stop giving [H] CPR.</span>")
H.receiving_cpr = FALSE
to_chat(src, "<span class='danger'>You need to stay still while performing CPR!</span>")
return
visible_message("<span class='danger'>[src] is trying to perform CPR on [H.name]!</span>", "<span class='danger'>You try to perform CPR on [H.name]!</span>")
if(cpr_modifier == CPR_CHEST_COMPRESSION_ONLY)
to_chat(src, "<span class='warning'>You can't get to [H]'s mouth, so your CPR will be less effective!</span>")
while(do_mob(src, H, 4 SECONDS) && H.health <= HEALTH_THRESHOLD_CRIT)
H.adjustOxyLoss(-15 * cpr_modifier)
H.SetLoseBreath(0)
H.AdjustParalysis(-2 SECONDS)
H.updatehealth("cpr")
visible_message("<span class='danger'>[src] performs CPR on [H.name]!</span>", "<span class='notice'>You perform CPR on [H.name].</span>")
cpr_try_activate_bomb(H)
if(cpr_modifier == CPR_RESCUE_BREATHS)
to_chat(H, "<span class='notice'>You feel a breath of fresh air enter your lungs. It feels good.</span>")
add_attack_logs(src, H, "CPRed", ATKLOG_ALL)
H.receiving_cpr = FALSE
visible_message("<span class='notice'>[src] stops performing CPR on [H].</span>", "<span class='notice'>You stop performing CPR on [H].</span>")
to_chat(src, "<span class='danger'>You need to stay still while performing CPR!</span>")
/mob/living/carbon/human/proc/get_cpr_mod(mob/living/carbon/human/H)
if(is_mouth_covered() || H.is_mouth_covered())
return CPR_CHEST_COMPRESSION_ONLY
if(!H.check_has_mouth() || !check_has_mouth())
return CPR_CHEST_COMPRESSION_ONLY
if(HAS_TRAIT(src, TRAIT_NOBREATH) || HAS_TRAIT(H, TRAIT_NOBREATH))
return CPR_CHEST_COMPRESSION_ONLY
if(dna?.species.breathid != H.dna?.species.breathid) // sorry non oxy-breathers
to_chat(src, "<span class='warning'>You don't think you'd be able to give [H] very effective rescue breaths...</span>")
return CPR_CHEST_COMPRESSION_ONLY
return CPR_RESCUE_BREATHS
/// Get the amount of time that this round of CPR should add to the death timer
/mob/living/carbon/human/proc/get_cpr_timer_adjustment(cpr_mod)
var/time_since_original_death = round(world.time - timeofdeath) / 10
// this looks good on desmos I guess
// goal here is that you'll get some surplus time added regardless of method if you catch them early
var/adjustment_time = max(round(-60 * log((12 + time_since_original_death) / 240)), 0)
if(cpr_mod == CPR_CHEST_COMPRESSION_ONLY)
// you just won't get as much if you're late
adjustment_time /= 2
return adjustment_time
/mob/living/carbon/human/proc/cpr_try_activate_bomb(mob/living/carbon/human/target)
var/obj/item/organ/external/chest/org = target.get_organ("chest")
if(istype(org) && istype(org.hidden, /obj/item/grenade))
var/obj/item/grenade/G = org.hidden
if(!G.active && prob(25))
to_chat(src, "<span class='notice'>You feel something <i>click</i> under your hands.</span>")
add_attack_logs(src, target, "activated an implanted grenade [G] in [target] with CPR.", ATKLOG_MOST)
playsound(target.loc, 'sound/weapons/armbomb.ogg', 60, TRUE)
G.active = TRUE
addtimer(CALLBACK(G, TYPE_PROC_REF(/obj/item/grenade, prime)), G.det_time)
#undef CPR_CHEST_COMPRESSION_ONLY
#undef CPR_RESCUE_BREATHS
#undef CPR_CHEST_COMPRESSION_RESTORATION
#undef CPR_BREATHS_RESTORATION
/mob/living/carbon/human/canBeHandcuffed()
if(get_num_arms() >= 2)
@@ -476,7 +476,7 @@
return TRUE
if(target.on_fire)
user.pat_out(target)
else if(target.health >= HEALTH_THRESHOLD_CRIT && !HAS_TRAIT(target, TRAIT_FAKEDEATH))
else if(target.health >= HEALTH_THRESHOLD_CRIT && !HAS_TRAIT(target, TRAIT_FAKEDEATH) && target.stat != DEAD)
target.help_shake_act(user)
return TRUE
else
-2
View File
@@ -73,8 +73,6 @@
update_health_hud()
med_hud_set_health()
med_hud_set_status()
if(!gibbed && !QDELETED(src))
addtimer(CALLBACK(src, PROC_REF(med_hud_set_status)), DEFIB_TIME_LIMIT + 1)
GLOB.alive_mob_list -= src
GLOB.dead_mob_list += src
@@ -845,6 +845,12 @@ STATUS EFFECTS
singlemutcheck(src, block, MUTCHK_FORCED)
dna.UpdateSE()
// Revivability
/mob/living/proc/is_revivable()
return has_status_effect(/datum/status_effect/limited_bonus/revivable)
///////////////////////////////// FROZEN /////////////////////////////////////
/mob/living/proc/IsFrozen()