diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 518005d9b31..d73198d466d 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -392,6 +392,14 @@
#define COMSIG_LIVING_SHOCK_PREVENTED "living_shock_prevented"
///sent by stuff like stunbatons and tasers: ()
#define COMSIG_LIVING_MINOR_SHOCK "living_minor_shock"
+///Sent from defibrillators when everything seems good and the user will be shocked: (defibber, defib_item, ghost)
+#define COMSIG_LIVING_PRE_DEFIB "living_pre_defib"
+ /// If returned from LIVING_BEFORE_DEFIB or LIVING_DEFIBBED, the defibrillation will fail
+ #define COMPONENT_BLOCK_DEFIB (1<<0)
+ /// If returned, don't even show the "failed" message, defer to the signal handler to do that.
+ #define COMPONENT_DEFIB_OVERRIDE (1<<1)
+///send from defibs on ressurection: (defibber, defib_item, ghost)
+#define COMSIG_LIVING_DEFIBBED "living_defibbed"
///from base of mob/living/revive() (full_heal, admin_revive)
#define COMSIG_LIVING_REVIVE "living_revive"
///from base of /mob/living/regenerate_limbs(): (noheal, excluded_limbs)
diff --git a/code/game/gamemodes/wizard/artefact.dm b/code/game/gamemodes/wizard/artefact.dm
index c8e3fd4de38..c829d044fa7 100644
--- a/code/game/gamemodes/wizard/artefact.dm
+++ b/code/game/gamemodes/wizard/artefact.dm
@@ -814,6 +814,7 @@ GLOBAL_LIST_EMPTY(multiverse)
unlimited = 1
/obj/item/organ/internal/heart/cursed/wizard
+ max_shocks_allowed = 3
pump_delay = 60
heal_brute = 25
heal_burn = 25
diff --git a/code/game/objects/items/weapons/defib.dm b/code/game/objects/items/weapons/defib.dm
index f048e5ead22..a649ef540d3 100644
--- a/code/game/objects/items/weapons/defib.dm
+++ b/code/game/objects/items/weapons/defib.dm
@@ -404,7 +404,10 @@
var/total_burn = 0
var/total_brute = 0
+ var/signal_result = SEND_SIGNAL(M, COMSIG_LIVING_PRE_DEFIB, user, defib, ghost)
+
if(do_after(user, 20 * toolspeed, target = M)) //placed on chest and short delay to shock for dramatic effect, revive time is 5sec total
+ signal_result |= SEND_SIGNAL(M, COMSIG_LIVING_DEFIBBED, user, defib, ghost)
for(var/obj/item/carried_item in H.contents)
if(istype(carried_item, /obj/item/clothing/suit/space))
if(!defib.combat)
@@ -413,6 +416,9 @@
busy = FALSE
update_icon(UPDATE_ICON_STATE)
return
+ if(signal_result & COMPONENT_DEFIB_OVERRIDE)
+ // let our signal handle it
+ return
if(H.undergoing_cardiac_arrest())
if(!H.get_int_organ(/obj/item/organ/internal/heart) && !H.get_int_organ(/obj/item/organ/internal/brain/slime)) //prevents defibing someone still alive suffering from a heart attack attack if they lack a heart
user.visible_message("[defib] buzzes: Resuscitation failed - Failed to pick up any heart electrical activity.")
@@ -450,7 +456,7 @@
total_brute += O.brute_dam
total_burn += O.burn_dam
ghost = H.get_ghost(TRUE) // We have to double check whether the dead guy has entered their body during the above
- if(total_burn <= 180 && total_brute <= 180 && !H.suiciding && !ghost && tplus < tlimit && !HAS_TRAIT(H, TRAIT_HUSK) && !HAS_TRAIT(H, TRAIT_BADDNA) && H.blood_volume > BLOOD_VOLUME_SURVIVE && (H.get_int_organ(/obj/item/organ/internal/heart) || H.get_int_organ(/obj/item/organ/internal/brain/slime)))
+ if(total_burn <= 180 && total_brute <= 180 && !H.suiciding && !ghost && tplus < tlimit && !HAS_TRAIT(H, TRAIT_HUSK) && !HAS_TRAIT(H, TRAIT_BADDNA) && H.blood_volume > BLOOD_VOLUME_SURVIVE && (H.get_int_organ(/obj/item/organ/internal/heart) || H.get_int_organ(/obj/item/organ/internal/brain/slime)) && !(signal_result & COMPONENT_BLOCK_DEFIB))
tobehealed = min(health + threshold, 0) // It's HILARIOUS without this min statement, let me tell you
tobehealed -= 5 //They get 5 of each type of damage healed so excessive combined damage will not immediately kill them after they get revived
H.adjustOxyLoss(tobehealed)
@@ -493,7 +499,7 @@
user.visible_message("[defib] buzzes: Resuscitation failed: Patient blood volume critically low.")
else if(ghost)
if(!ghost.can_reenter_corpse) // DNR or AntagHUD
- user.visible_message("[defib] buzzes: Resucitation failed: No electrical brain activity detected.")
+ user.visible_message("[defib] buzzes: Resuscitation failed: No electrical brain activity detected.")
else
user.visible_message("[defib] buzzes: Resuscitation failed: Patient's brain is unresponsive. Further attempts may succeed.")
else
@@ -593,7 +599,10 @@
var/tloss = 600 //brain damage starts setting in on the patient after some time left rotting
var/total_burn = 0
var/total_brute = 0
+
+ var/signal_result = SEND_SIGNAL(M, COMSIG_LIVING_PRE_DEFIB, user, src, ghost)
if(do_after(user, 20 * toolspeed, target = M)) //placed on chest and short delay to shock for dramatic effect, revive time is 5sec total
+ signal_result |= SEND_SIGNAL(M, COMSIG_LIVING_DEFIBBED, user, src, ghost)
if(H.stat == DEAD)
var/health = H.health
M.visible_message("[M]'s body convulses a bit.")
@@ -602,7 +611,7 @@
for(var/obj/item/organ/external/O in H.bodyparts)
total_brute += O.brute_dam
total_burn += O.burn_dam
- if(total_burn <= 180 && total_brute <= 180 && !H.suiciding && !ghost && tplus < tlimit && !HAS_TRAIT(H, TRAIT_HUSK))
+ if(total_burn <= 180 && total_brute <= 180 && !H.suiciding && !ghost && tplus < tlimit && !HAS_TRAIT(H, TRAIT_HUSK) && !(signal_result & COMPONENT_BLOCK_DEFIB))
tobehealed = min(health + threshold, 0) // It's HILARIOUS without this min statement, let me tell you
tobehealed -= 5 //They get 5 of each type of damage healed so excessive combined damage will not immediately kill them after they get revived
H.adjustOxyLoss(tobehealed)
@@ -618,6 +627,7 @@
if(tplus > tloss)
H.setBrainLoss( max(0, min(99, ((tlimit - tplus) / tlimit * 100))))
SEND_SIGNAL(H, COMSIG_LIVING_MINOR_SHOCK, 100)
+ SEND_SIGNAL(H, COMSIG_LIVING_DEFIBBED, user, src)
if(isrobot(user))
var/mob/living/silicon/robot/R = user
R.cell.use(revivecost)
diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm
index 157d1d2afd8..d8cb9ec38ec 100644
--- a/code/modules/surgery/organs/heart.dm
+++ b/code/modules/surgery/organs/heart.dm
@@ -79,6 +79,16 @@
var/pump_delay = 30 //you can pump 1 second early, for lag, but no more (otherwise you could spam heal)
var/blood_loss = 100 //600 blood is human default, so 5 failures (below 122 blood is where humans die because reasons?)
+ // Give the user a chance to be shocked to life with the heart in, since defibs put them to sleep.
+ /// How long the shock pumps their heart for them.
+ var/revival_grace_period = 10 SECONDS
+ /// If true, the user doesn't need to pump their heart.
+ var/in_grace_period = FALSE
+ /// Times that it's been shocked.
+ var/times_shocked = 0
+ /// Max times that the shock will work before it'll just refuse.
+ var/max_shocks_allowed = 5
+
//How much to heal per pump, negative numbers would HURT the player
var/heal_brute = 0
var/heal_burn = 0
@@ -96,7 +106,7 @@
return ..()
/obj/item/organ/internal/heart/cursed/on_life()
- if(world.time > (last_pump + pump_delay))
+ if(world.time > (last_pump + pump_delay) && !in_grace_period)
if(ishuman(owner) && owner.client) //While this entire item exists to make people suffer, they can't control disconnects.
var/mob/living/carbon/human/H = owner
if(!(NO_BLOOD in H.dna.species.species_traits))
@@ -111,9 +121,61 @@
..()
if(owner)
to_chat(owner, "Your heart has been replaced with a cursed one, you have to pump this one manually otherwise you'll die!")
+ RegisterSignal(owner, COMSIG_LIVING_PRE_DEFIB, .proc/just_before_revive)
+ RegisterSignal(owner, COMSIG_LIVING_DEFIBBED, .proc/on_defib_revive)
+
+/obj/item/organ/internal/heart/cursed/proc/on_defib_revive(mob/living/carbon/shocked, mob/living/carbon/shocker, obj/item/defib, mob/dead/observer/ghost = null)
+ SIGNAL_HANDLER // COMSIG_LIVING_DEFIBBED
+
+ if(!owner || !istype(owner) || owner.stat != DEAD)
+ return
+
+ if(times_shocked >= max_shocks_allowed)
+ shocker.visible_message(
+ "Tendrils of ghastly electricity surge from [shocked] as [shocked.p_their()] heart seems to outright refuse defibrillation!",
+ blind_message = "You hear a loud shock."
+ )
+ tesla_zap(owner, 4, 8000, ZAP_MOB_STUN | ZAP_MOB_DAMAGE)
+ // NO, YOU!
+ playsound(get_turf(owner), 'sound/magic/lightningshock.ogg', 50, 1)
+
+ defib.audible_message("[defib] buzzes: Resuscitation failed - Anomalous heart activity detected while administering shock.")
+ playsound(defib, "defib_saftyon.ogg", 50, FALSE)
+
+ return COMPONENT_DEFIB_OVERRIDE
+
+ in_grace_period = TRUE
+ times_shocked++
+ // wake up
+ addtimer(CALLBACK(src, .proc/after_revive), 2 SECONDS) // grab a brush and put a little make-up
+ addtimer(CALLBACK(src, .proc/on_end_grace_period), revival_grace_period) // hide the scars to fade away the shake-up
+
+/obj/item/organ/internal/heart/cursed/proc/after_revive()
+ owner.SetSleeping(0)
+ owner.SetParalysis(0)
+
+/// Run this just before the shock is applied so we end up with enough blood to revive.
+/obj/item/organ/internal/heart/cursed/proc/just_before_revive(mob/living/carbon/shocked, mob/living/carbon/shocker, mob/dead/observer/ghost = null)
+ SIGNAL_HANDLER // COMSIG_LIVING_PRE_DEFIB
+
+ if(owner.stat == DEAD)
+ owner.blood_volume = BLOOD_VOLUME_OKAY
+
+/obj/item/organ/internal/heart/cursed/proc/on_end_grace_period()
+ in_grace_period = FALSE
+ if(!owner)
+ return
+ to_chat(owner, "The effects of the shock seem to wear off, and you feel a familiar tightness in your chest! Get pumping!")
+
+ if(times_shocked < max_shocks_allowed - 1)
+ to_chat(owner, "It doesn't feel like your [name] enjoyed that.")
+ else if(times_shocked == max_shocks_allowed - 1)
+ to_chat(owner, "Your [name] starts to feel heavy in your chest. It doesn't seem like it'll be able to take many more shocks!")
+ else
+ to_chat(owner, "Your [name] feels like lead. Something tells you that if you die with it again, you won't get another chance!")
/datum/action/item_action/organ_action/cursed_heart
- name = "pump your blood"
+ name = "Pump your heart"
//You are now brea- pumping blood manually
/datum/action/item_action/organ_action/cursed_heart/Trigger()
@@ -127,7 +189,7 @@
cursed_heart.last_pump = world.time
playsound(owner,'sound/effects/singlebeat.ogg',40,1)
- to_chat(owner, "Your heart beats.")
+ to_chat(owner, "Your heart beats.")
var/mob/living/carbon/human/H = owner
if(istype(H))