diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index d738e5aa1f6..93ceb912480 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -462,6 +462,10 @@
#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)
+ /// If returned, allow to revive through false death.
+ #define COMPONENT_DEFIB_FAKEDEATH_ACCEPTED (1<<2)
+ /// If returned, make the fake death look like a unresponsive ghost.
+ #define COMPONENT_DEFIB_FAKEDEATH_DENIED (1<<3)
///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)
diff --git a/code/_onclick/hud/alert.dm b/code/_onclick/hud/alert.dm
index 80abb2b6f26..51647fac277 100644
--- a/code/_onclick/hud/alert.dm
+++ b/code/_onclick/hud/alert.dm
@@ -870,3 +870,58 @@ so as to remain in compliance with the most up-to-date laws."
if(!istype(usr))
return
living_owner.do_succumb(TRUE)
+
+/atom/movable/screen/alert/changeling_defib_revive
+ name = "Your heart is being defibrillated."
+ desc = "Click this status to be revived from fake death."
+ icon_state = "template"
+ timeout = 10 SECONDS
+ var/do_we_revive = FALSE
+ var/image/toggle_overlay
+
+/atom/movable/screen/alert/changeling_defib_revive/Initialize(mapload, parent_unit)
+ . = ..()
+ var/image/defib_appearance = image(parent_unit)
+ defib_appearance.layer = FLOAT_LAYER
+ defib_appearance.plane = FLOAT_PLANE
+ overlays += defib_appearance
+
+/atom/movable/screen/alert/changeling_defib_revive/attach_owner(mob/new_owner)
+ . = ..()
+ RegisterSignal(owner, COMSIG_LIVING_DEFIBBED, PROC_REF(on_defib_revive))
+
+/atom/movable/screen/alert/changeling_defib_revive/Destroy()
+ if(owner)
+ UnregisterSignal(owner, COMSIG_LIVING_DEFIBBED)
+ if(toggle_overlay)
+ overlays -= toggle_overlay
+ qdel(toggle_overlay)
+ return ..()
+
+/atom/movable/screen/alert/changeling_defib_revive/Click()
+ if(!..())
+ return
+ do_we_revive = !do_we_revive
+ if(!toggle_overlay)
+ toggle_overlay = image('icons/mob/screen_gen.dmi', icon_state = "selector")
+ toggle_overlay.layer = FLOAT_LAYER
+ toggle_overlay.plane = FLOAT_PLANE + 2
+ if(do_we_revive)
+ overlays |= toggle_overlay
+ else
+ overlays -= toggle_overlay
+
+/atom/movable/screen/alert/changeling_defib_revive/proc/on_defib_revive()
+ . = COMPONENT_DEFIB_FAKEDEATH_DENIED
+ if(do_we_revive)
+ var/datum/antagonist/changeling/cling = owner.mind.has_antag_datum(/datum/antagonist/changeling)
+ cling.remove_specific_power(/datum/action/changeling/revive)
+ cling.regenerating = FALSE
+ var/heart_name = "heart"
+ if(iscarbon(owner))
+ var/mob/living/carbon/C = owner
+ var/datum/organ/heart/heart = C.get_int_organ_datum(ORGAN_DATUM_HEART)
+ heart_name = heart.linked_organ.name
+ to_chat(owner, "Electricity flows through our [heart_name]! We have been brought back to life and have stopped our regeneration.")
+ . = COMPONENT_DEFIB_FAKEDEATH_ACCEPTED
+ owner.clear_alert("cling_defib")
diff --git a/code/datums/components/defibrillator.dm b/code/datums/components/defibrillator.dm
index ff1020e5eca..fd51de84560 100644
--- a/code/datums/components/defibrillator.dm
+++ b/code/datums/components/defibrillator.dm
@@ -173,6 +173,11 @@
to_chat(ghost, "Your heart is being defibrillated. Return to your body if you want to be revived! (Verbs -> Ghost -> Re-enter corpse)")
window_flash(ghost.client)
SEND_SOUND(ghost, sound('sound/effects/genetics.ogg'))
+ else if(HAS_TRAIT_FROM(target, TRAIT_FAKEDEATH, CHANGELING_TRAIT))
+ to_chat(target, "Your heart is being defibrillated. Click the defibrillator status to be revived!")
+ window_flash(target.client)
+ SEND_SOUND(target, sound('sound/effects/genetics.ogg'))
+ target.throw_alert("cling_defib", /atom/movable/screen/alert/changeling_defib_revive, alert_args = list(parent, target))
if(!do_after(user, 3 SECONDS * speed_multiplier, target = target)) // Beginning to place the paddles on patient's chest to allow some time for people to move away to stop the process
busy = FALSE
@@ -238,37 +243,39 @@
playsound(get_turf(defib_ref), 'sound/machines/defib_zap.ogg', 50, 1, -1)
ghost = target.get_ghost(TRUE) // We have to double check whether the dead guy has entered their body during the above
- var/defib_success = TRUE
-
// Run through some quick failure states after shocking.
var/time_dead = world.time - target.timeofdeath
+ var/failure_message
if(!target.is_revivable())
- user.visible_message("[defib_ref] buzzes: Resuscitation failed - Heart tissue damage beyond point of no return for defibrillation.")
- defib_success = FALSE
+ failure_message = "[defib_ref] buzzes: Resuscitation failed - Heart tissue damage beyond point of no return for defibrillation."
else if(target.getBruteLoss() >= 180 || target.getFireLoss() >= 180)
- user.visible_message("[defib_ref] buzzes: Resuscitation failed - Severe tissue damage detected.")
- defib_success = FALSE
+ failure_message = "[defib_ref] buzzes: Resuscitation failed - Severe tissue damage detected."
else if(HAS_TRAIT(target, TRAIT_HUSK))
- user.visible_message("[defib_ref] buzzes: Resuscitation failed - Subject is husked.")
- defib_success = FALSE
+ failure_message = "[defib_ref] buzzes: Resuscitation failed - Subject is husked."
else if(target.blood_volume < BLOOD_VOLUME_SURVIVE)
- user.visible_message("[defib_ref] buzzes: Resuscitation failed - Patient blood volume critically low.")
- defib_success = FALSE
+ failure_message = "[defib_ref] buzzes: Resuscitation failed - Patient blood volume critically low."
else if(!target.get_organ_slot("brain")) // So things like headless clings don't get outed
- user.visible_message("[defib_ref] buzzes: Resuscitation failed - No brain detected within patient.")
- defib_success = FALSE
+ failure_message = "[defib_ref] buzzes: Resuscitation failed - No brain detected within patient."
else if(ghost)
if(!ghost.can_reenter_corpse || target.suiciding) // DNR or AntagHUD
- user.visible_message("[defib_ref] buzzes: Resuscitation failed - No electrical brain activity detected.")
+ failure_message = "[defib_ref] buzzes: Resuscitation failed - No electrical brain activity detected."
else
- user.visible_message("[defib_ref] buzzes: Resuscitation failed - Patient's brain is unresponsive. Further attempts may succeed.")
- defib_success = FALSE
- else if((signal_result & COMPONENT_BLOCK_DEFIB) || HAS_TRAIT(target, TRAIT_FAKEDEATH) || HAS_TRAIT(target, TRAIT_BADDNA) || target.suiciding) // these are a bit more arbitrary
- user.visible_message("[defib_ref] buzzes: Resuscitation failed.")
- defib_success = FALSE
+ failure_message = "[defib_ref] buzzes: Resuscitation failed - Patient's brain is unresponsive. Further attempts may succeed."
+ else if(HAS_TRAIT(target, TRAIT_FAKEDEATH))
+ if(signal_result & COMPONENT_DEFIB_FAKEDEATH_DENIED)
+ failure_message = "[defib_ref] buzzes: Resuscitation failed - Patient's brain is unresponsive. Further attempts may succeed."
+ else if(signal_result & COMPONENT_DEFIB_FAKEDEATH_ACCEPTED)
+ // as much as I hate that this is here, it has to come after the `Patient is not in a valid state. Operation aborted.` check.
+ REMOVE_TRAIT(target, TRAIT_FAKEDEATH, CHANGELING_TRAIT)
+ else
+ failure_message = "[defib_ref] buzzes: Resuscitation failed." // has a fakedeath like capulettium
- if(!defib_success)
+ else if((signal_result & COMPONENT_BLOCK_DEFIB) || HAS_TRAIT(target, TRAIT_BADDNA) || target.suiciding) // these are a bit more arbitrary
+ failure_message = "[defib_ref] buzzes: Resuscitation failed."
+
+ if(failure_message)
+ user.visible_message(failure_message)
playsound(get_turf(defib_ref), 'sound/machines/defib_failed.ogg', 50, 0)
else
// Heal each basic damage type by as much as we're under -100 health
@@ -313,7 +320,7 @@
target.med_hud_set_status()
add_attack_logs(user, target, "Revived with [defib_ref]")
SSblackbox.record_feedback("tally", "players_revived", 1, "defibrillator")
- SEND_SIGNAL(parent, COMSIG_DEFIB_SHOCK_APPLIED, user, target, should_cause_harm, defib_success)
+ SEND_SIGNAL(parent, COMSIG_DEFIB_SHOCK_APPLIED, user, target, should_cause_harm, isnull(failure_message))
set_cooldown(cooldown)
busy = FALSE
diff --git a/code/modules/antagonists/changeling/changeling_power.dm b/code/modules/antagonists/changeling/changeling_power.dm
index b5d08685e4e..15fba006b05 100644
--- a/code/modules/antagonists/changeling/changeling_power.dm
+++ b/code/modules/antagonists/changeling/changeling_power.dm
@@ -24,7 +24,7 @@
var/req_stat = CONSCIOUS
/// If this power is active or not. Used for toggleable abilities.
var/active = FALSE
- /// If this power can be used while the changeling has the `TRAIT_FAKE_DEATH` trait.
+ /// If this power can be used while the changeling has the `TRAIT_FAKEDEATH` trait.
var/bypass_fake_death = FALSE
/*
diff --git a/code/modules/antagonists/changeling/datum_changeling.dm b/code/modules/antagonists/changeling/datum_changeling.dm
index e6f36e577ae..f8507d96cd8 100644
--- a/code/modules/antagonists/changeling/datum_changeling.dm
+++ b/code/modules/antagonists/changeling/datum_changeling.dm
@@ -277,6 +277,22 @@ RESTRICT_TYPE(/datum/antagonist/changeling)
acquired_powers += power
power.on_purchase(changeling || owner.current, src)
+/**
+ * Removes all `power_type` abilities that the changeling has. Refunds the cost of the power from our genetic points.
+ *
+ * Arugments:
+ * * datum/action/changeling/power - the typepath power to remove from the changeling.
+ * * refund_cost - if we should refund genetic points when giving the power
+ */
+/datum/antagonist/changeling/proc/remove_specific_power(datum/action/changeling/power_type, refund_cost = TRUE)
+ for(var/datum/action/changeling/power in acquired_powers)
+ if(!istype(power, power_type))
+ continue
+ if(refund_cost)
+ genetic_points -= power.dna_cost
+ acquired_powers -= power
+ qdel(power)
+
/**
* Store the languages from the `new_languages` list into the `absorbed_languages` list. Teaches the changeling the new languages.
*
diff --git a/code/modules/antagonists/changeling/powers/fakedeath.dm b/code/modules/antagonists/changeling/powers/fakedeath.dm
index d826b4712e7..2ffddd180ad 100644
--- a/code/modules/antagonists/changeling/powers/fakedeath.dm
+++ b/code/modules/antagonists/changeling/powers/fakedeath.dm
@@ -18,11 +18,14 @@
user.updatehealth("fakedeath sting")
cling.regenerating = TRUE
+ cling.remove_specific_power(/datum/action/changeling/revive)
addtimer(CALLBACK(src, PROC_REF(ready_to_regenerate), user), CHANGELING_FAKEDEATH_TIME)
SSblackbox.record_feedback("nested tally", "changeling_powers", 1, list("[name]"))
return TRUE
/datum/action/changeling/fakedeath/proc/ready_to_regenerate(mob/user)
+ if(!HAS_TRAIT_FROM(user, TRAIT_FAKEDEATH, CHANGELING_TRAIT))
+ return
if(!QDELETED(user) && user.mind && cling?.acquired_powers)
to_chat(user, "We are ready to regenerate.")
cling.give_power(new /datum/action/changeling/revive)
diff --git a/code/modules/antagonists/changeling/powers/revive.dm b/code/modules/antagonists/changeling/powers/revive.dm
index 20e4c793330..2ed3b19acfc 100644
--- a/code/modules/antagonists/changeling/powers/revive.dm
+++ b/code/modules/antagonists/changeling/powers/revive.dm
@@ -11,6 +11,10 @@
if(HAS_TRAIT(user, TRAIT_UNREVIVABLE))
to_chat(user, "Something is preventing us from regenerating, we will need to revive at another point.")
return FALSE
+ if(!HAS_TRAIT_FROM(user, TRAIT_FAKEDEATH, CHANGELING_TRAIT))
+ cling.acquired_powers -= src
+ Remove(user)
+ return
REMOVE_TRAIT(user, TRAIT_FAKEDEATH, CHANGELING_TRAIT)
for(var/obj/item/grab/G in user.grabbed_by)
var/mob/living/carbon/M = G.assailant