diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index 6b9fc0d6ba..f371ccccfb 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -260,6 +260,7 @@
#define COMPONENT_EXAMINATE_BLIND 3 //outputs the "something is there but you can't see it" message.
#define COMSIG_MOB_DEATH "mob_death" //from base of mob/death(): (gibbed)
#define COMPONENT_BLOCK_DEATH_BROADCAST 1 //stops the death from being broadcasted in deadchat.
+#define COMSIG_MOB_STATCHANGE "mob_statchange" //!from base of mob/set_stat(): (new_stat, old_stat)
#define COMSIG_MOB_CLICKON "mob_clickon" //from base of mob/clickon(): (atom/A, params)
#define COMSIG_MOB_CANCEL_CLICKON 1
#define COMSIG_MOB_GHOSTIZE "mob_ghostize" //from base of mob/Ghostize(): (can_reenter_corpse, special, penalize)
diff --git a/code/game/machinery/limbgrower.dm b/code/game/machinery/limbgrower.dm
index 3b1a8d804d..aaf3b37864 100644
--- a/code/game/machinery/limbgrower.dm
+++ b/code/game/machinery/limbgrower.dm
@@ -369,7 +369,7 @@
C.set_species(selected)
C.set_resting(TRUE, TRUE)
// Don't want to cause it to deathgasp..
- C.stat = DEAD
+ C.set_stat(DEAD)
C.adjustOxyLoss(200)
// Limb replacement causes toxloss, which can cause too much suffering for the doctor that I don't want
C.adjustCloneLoss(45)
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index a713ddfbf4..bc5b37655b 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -10,6 +10,7 @@
var/icon_door = null
var/icon_door_override = FALSE //override to have open overlay use icon different to its base's
+ var/has_door_icon = TRUE // Set to false to skip trying to draw a door icon.
var/secure = FALSE //secure locker or not, also used if overriding a non-secure locker with a secure door overlay to add fancy lights
var/opened = FALSE
var/welded = FALSE
@@ -76,7 +77,7 @@
. += "[icon_door_override ? icon_door : icon_state]_open"
return
- if(icon_door)
+ if(has_door_icon)
. += "[icon_door || icon_state]_door"
if(welded)
. += icon_welded
diff --git a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm
index fc0b7c96c8..f6e1e6fe9e 100644
--- a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm
+++ b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm
@@ -3,7 +3,7 @@
name = "large cardboard box"
desc = "Just a box..."
icon_state = "cardboard"
- icon_door = null
+ has_door_icon = FALSE
mob_storage_capacity = 1
resistance_flags = FLAMMABLE
max_integrity = 70
diff --git a/code/modules/antagonists/bloodsucker/bloodsucker_life.dm b/code/modules/antagonists/bloodsucker/bloodsucker_life.dm
index a3b7fd4c0f..023bbbe1e3 100644
--- a/code/modules/antagonists/bloodsucker/bloodsucker_life.dm
+++ b/code/modules/antagonists/bloodsucker/bloodsucker_life.dm
@@ -241,7 +241,7 @@
owner.current.Unconscious(20, 1)
/datum/antagonist/bloodsucker/proc/Torpor_Begin(amInCoffin = FALSE)
- owner.current.stat = UNCONSCIOUS
+ owner.current.set_stat(UNCONSCIOUS)
owner.current.apply_status_effect(STATUS_EFFECT_UNCONSCIOUS)
ADD_TRAIT(owner.current, TRAIT_FAKEDEATH, "bloodsucker") // Come after UNCONSCIOUS or else it fails
ADD_TRAIT(owner.current, TRAIT_NODEATH, "bloodsucker") // Without this, you'll just keep dying while you recover.
@@ -260,7 +260,7 @@
to_chat(owner.current, "Your body keeps you going, even as you try to end yourself.")
/datum/antagonist/bloodsucker/proc/Torpor_End()
- owner.current.stat = SOFT_CRIT
+ owner.current.set_stat(SOFT_CRIT)
owner.current.remove_status_effect(STATUS_EFFECT_UNCONSCIOUS)
REMOVE_TRAIT(owner.current, TRAIT_FAKEDEATH, "bloodsucker")
REMOVE_TRAIT(owner.current, TRAIT_NODEATH, "bloodsucker")
diff --git a/code/modules/antagonists/devil/true_devil/_true_devil.dm b/code/modules/antagonists/devil/true_devil/_true_devil.dm
index f013e56737..14c1ad9421 100644
--- a/code/modules/antagonists/devil/true_devil/_true_devil.dm
+++ b/code/modules/antagonists/devil/true_devil/_true_devil.dm
@@ -56,7 +56,7 @@
mind.announce_objectives()
/mob/living/carbon/true_devil/death(gibbed)
- stat = DEAD
+ set_stat(DEAD)
..(gibbed)
drop_all_held_items()
INVOKE_ASYNC(mind.has_antag_datum(/datum/antagonist/devil), TYPE_PROC_REF(/datum/antagonist/devil, beginResurrectionCheck), src)
diff --git a/code/modules/mob/living/brain/MMI.dm b/code/modules/mob/living/brain/MMI.dm
index 847d3370c1..f1e586f090 100644
--- a/code/modules/mob/living/brain/MMI.dm
+++ b/code/modules/mob/living/brain/MMI.dm
@@ -75,7 +75,7 @@
brainmob.forceMove(src)
brainmob.container = src
if(!(newbrain.organ_flags & ORGAN_FAILING)) // the brain organ hasn't been beaten to death.
- brainmob.stat = CONSCIOUS //we manually revive the brain mob
+ brainmob.set_stat(CONSCIOUS) //we manually revive the brain mob
brainmob.remove_from_dead_mob_list()
brainmob.add_to_alive_mob_list()
@@ -112,7 +112,7 @@
if(brain.brainmob)
brainmob.container = null //Reset brainmob mmi var.
brainmob.forceMove(brain) //Throw mob into brain.
- brainmob.stat = DEAD
+ brainmob.set_stat(DEAD)
brainmob.emp_damage = 0
brainmob.reset_perspective() //so the brainmob follows the brain organ instead of the mmi. And to update our vision
brain.brainmob = brainmob //Set the brain to use the brainmob
diff --git a/code/modules/mob/living/brain/death.dm b/code/modules/mob/living/brain/death.dm
index 4ee8d6756b..8a0e71de5f 100644
--- a/code/modules/mob/living/brain/death.dm
+++ b/code/modules/mob/living/brain/death.dm
@@ -1,7 +1,7 @@
/mob/living/brain/death(gibbed)
if(stat == DEAD)
return
- stat = DEAD
+ set_stat(DEAD)
if(!gibbed && container)//If not gibbed but in a container.
var/obj/item/mmi = container
diff --git a/code/modules/mob/living/brain/posibrain.dm b/code/modules/mob/living/brain/posibrain.dm
index 0695f32cea..b9db274b65 100644
--- a/code/modules/mob/living/brain/posibrain.dm
+++ b/code/modules/mob/living/brain/posibrain.dm
@@ -135,7 +135,7 @@ GLOBAL_VAR(posibrain_notify_cooldown)
brainmob.stored_dna = new /datum/dna/stored(brainmob)
C.dna.copy_dna(brainmob.stored_dna)
brainmob.timeofhostdeath = C.timeofdeath
- brainmob.stat = CONSCIOUS
+ brainmob.set_stat(CONSCIOUS)
if(brainmob.mind)
brainmob.mind.assigned_role = new_role
if(C.mind)
@@ -158,7 +158,7 @@ GLOBAL_VAR(posibrain_notify_cooldown)
name = "[initial(name)] ([brainmob.name])"
to_chat(brainmob, welcome_message)
brainmob.mind.assigned_role = new_role
- brainmob.stat = CONSCIOUS
+ brainmob.set_stat(CONSCIOUS)
brainmob.remove_from_dead_mob_list()
brainmob.add_to_alive_mob_list()
diff --git a/code/modules/mob/living/carbon/alien/larva/life.dm b/code/modules/mob/living/carbon/alien/larva/life.dm
index b22ff68531..48713e0728 100644
--- a/code/modules/mob/living/carbon/alien/larva/life.dm
+++ b/code/modules/mob/living/carbon/alien/larva/life.dm
@@ -16,12 +16,12 @@
return
if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (HAS_TRAIT(src, TRAIT_DEATHCOMA)) || health <= crit_threshold)
if(stat == CONSCIOUS)
- stat = UNCONSCIOUS
+ set_stat(UNCONSCIOUS)
if(!eye_blind)
blind_eyes(1)
update_mobility()
else if(stat == UNCONSCIOUS)
- stat = CONSCIOUS
+ set_stat(CONSCIOUS)
if(!(combat_flags & COMBAT_FLAG_HARD_STAMCRIT))
set_resting(FALSE, TRUE)
if(eye_blind <= 1)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index efa178de33..86dc221654 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -807,16 +807,16 @@
death()
return
if(IsUnconscious() || IsSleeping() || getOxyLoss() > 50 || (HAS_TRAIT(src, TRAIT_DEATHCOMA)) || (health <= HEALTH_THRESHOLD_FULLCRIT && !HAS_TRAIT(src, TRAIT_NOHARDCRIT)))
- stat = UNCONSCIOUS
+ set_stat(UNCONSCIOUS)
SEND_SIGNAL(src, COMSIG_DISABLE_COMBAT_MODE)
if(!eye_blind)
blind_eyes(1)
else
if(health <= crit_threshold && !HAS_TRAIT(src, TRAIT_NOSOFTCRIT))
- stat = SOFT_CRIT
+ set_stat(SOFT_CRIT)
SEND_SIGNAL(src, COMSIG_DISABLE_COMBAT_MODE)
else
- stat = CONSCIOUS
+ set_stat(CONSCIOUS)
if(eye_blind <= 1)
adjust_blindness(-1)
update_mobility()
diff --git a/code/modules/mob/living/death.dm b/code/modules/mob/living/death.dm
index 134e7d9cdc..aaf9bf0a7a 100644
--- a/code/modules/mob/living/death.dm
+++ b/code/modules/mob/living/death.dm
@@ -57,7 +57,7 @@
/mob/living/death(gibbed)
SEND_SIGNAL(src, COMSIG_LIVING_PREDEATH, gibbed)
- stat = DEAD
+ set_stat(DEAD)
unset_machine()
timeofdeath = world.time
tod = STATION_TIME_TIMESTAMP("hh:mm:ss", world.time)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 1615b80193..cd69b5ee52 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -626,7 +626,7 @@
remove_from_dead_mob_list()
add_to_alive_mob_list()
suiciding = 0
- stat = UNCONSCIOUS //the mob starts unconscious,
+ set_stat(UNCONSCIOUS) //the mob starts unconscious,
if(!eye_blind)
blind_eyes(1)
updatehealth() //then we check if the mob should wake up.
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index 6f0306350a..efe94a3261 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -413,7 +413,7 @@
else
the_mmi.forceMove(get_turf(src))
if(the_mmi.brainmob.stat == DEAD && !suiciding)
- the_mmi.brainmob.stat = CONSCIOUS
+ the_mmi.brainmob.set_stat(CONSCIOUS)
if(mind)
mind.transfer_to(the_mmi.brainmob)
the_mmi.update_appearance()
diff --git a/code/modules/mob/living/silicon/ai/life.dm b/code/modules/mob/living/silicon/ai/life.dm
index 1506442158..c87c1da0b8 100644
--- a/code/modules/mob/living/silicon/ai/life.dm
+++ b/code/modules/mob/living/silicon/ai/life.dm
@@ -79,7 +79,7 @@
death()
return
else if(stat == UNCONSCIOUS)
- stat = CONSCIOUS
+ set_stat(CONSCIOUS)
adjust_blindness(-1)
diag_hud_set_status()
diff --git a/code/modules/mob/living/silicon/pai/death.dm b/code/modules/mob/living/silicon/pai/death.dm
index 07cb671706..16f86e8827 100644
--- a/code/modules/mob/living/silicon/pai/death.dm
+++ b/code/modules/mob/living/silicon/pai/death.dm
@@ -1,7 +1,7 @@
/mob/living/silicon/pai/death(gibbed)
if(stat == DEAD)
return
- stat = DEAD
+ set_stat(DEAD)
update_mobility()
update_sight()
wipe_fullscreens()
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 2def55c3ee..44e8a41474 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -106,7 +106,7 @@
mmi.forceMove(T)
if(mmi.brainmob)
if(mmi.brainmob.stat == DEAD)
- mmi.brainmob.stat = CONSCIOUS
+ mmi.brainmob.set_stat(CONSCIOUS)
mmi.brainmob.remove_from_dead_mob_list()
mmi.brainmob.add_to_alive_mob_list()
mind.transfer_to(mmi.brainmob)
@@ -922,9 +922,9 @@
toggle_headlamp(1)
return
if(IsUnconscious() || IsStun() || IsKnockdown() || IsParalyzed() || getOxyLoss() > maxHealth * 0.5)
- stat = UNCONSCIOUS
+ set_stat(UNCONSCIOUS)
else
- stat = CONSCIOUS
+ set_stat(CONSCIOUS)
update_mobility()
diag_hud_set_status()
diag_hud_set_health()
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
index bb3c8f1d32..8111dbb366 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/hierophant.dm
@@ -112,7 +112,7 @@ Difficulty: Normal
if(health > 0 || stat == DEAD)
return
else
- stat = DEAD
+ set_stat(DEAD)
blinking = TRUE //we do a fancy animation, release a huge burst(), and leave our staff.
burst_range = 10
visible_message("\"Mrmxmexmrk wipj-hiwxvygx wiuyirgi...\"")
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 3eb2f8bb0f..c7e753744c 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -197,7 +197,7 @@
if(health <= 0)
death()
else
- stat = CONSCIOUS
+ set_stat(CONSCIOUS)
med_hud_set_status()
/mob/living/simple_animal/proc/handle_automated_action()
diff --git a/code/modules/mob/living/simple_animal/slime/death.dm b/code/modules/mob/living/simple_animal/slime/death.dm
index 5cac0c630c..9553fe52b1 100644
--- a/code/modules/mob/living/simple_animal/slime/death.dm
+++ b/code/modules/mob/living/simple_animal/slime/death.dm
@@ -21,7 +21,7 @@
if(buckled)
Feedstop(silent = TRUE) //releases ourselves from the mob we fed on.
- stat = DEAD
+ set_stat(DEAD)
cut_overlays()
update_mobility()
diff --git a/code/modules/mob/living/simple_animal/slime/life.dm b/code/modules/mob/living/simple_animal/slime/life.dm
index 98767a3189..8e66ae14e3 100644
--- a/code/modules/mob/living/simple_animal/slime/life.dm
+++ b/code/modules/mob/living/simple_animal/slime/life.dm
@@ -134,14 +134,14 @@
if(stat == CONSCIOUS && stasis)
to_chat(src, "Nerve gas in the air has put you in stasis!")
- stat = UNCONSCIOUS
+ set_stat(UNCONSCIOUS)
powerlevel = 0
rabid = 0
update_mobility()
regenerate_icons()
else if(stat == UNCONSCIOUS && !stasis)
to_chat(src, "You wake up from the stasis.")
- stat = CONSCIOUS
+ set_stat(CONSCIOUS)
update_mobility()
regenerate_icons()
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index 72d2f327eb..e9bb92516c 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -1009,3 +1009,15 @@ GLOBAL_VAR_INIT(exploit_warn_spam_prevention, 0)
*/
/mob/proc/on_item_dropped(obj/item/I)
return
+
+/**
+ * Used to wrap stat setting to trigger on-stat-change functionality.
+ * Must be used instead of directly setting a mob's stat var,
+ * so that the signal is sent properly.
+ */
+/mob/proc/set_stat(new_stat)
+ if(new_stat == stat)
+ return
+ . = stat
+ stat = new_stat
+ SEND_SIGNAL(src, COMSIG_MOB_STATCHANGE, new_stat, .)
diff --git a/code/modules/research/xenobiology/crossbreeding/charged.dm b/code/modules/research/xenobiology/crossbreeding/charged.dm
index a6298f502c..052e3e354e 100644
--- a/code/modules/research/xenobiology/crossbreeding/charged.dm
+++ b/code/modules/research/xenobiology/crossbreeding/charged.dm
@@ -271,7 +271,7 @@ Charged extracts:
if(M.maxHealth <= 0)
to_chat(user, "The slime is too unstable to return!")
M.revive(full_heal = 1)
- M.stat = CONSCIOUS
+ M.set_stat(CONSCIOUS)
M.visible_message("[M] is filled with renewed vigor and blinks awake!")
M.maxHealth -= 10 //Revival isn't healthy.
M.health -= 10
diff --git a/code/modules/surgery/bodyparts/dismemberment.dm b/code/modules/surgery/bodyparts/dismemberment.dm
index 96b9290239..bec8641d82 100644
--- a/code/modules/surgery/bodyparts/dismemberment.dm
+++ b/code/modules/surgery/bodyparts/dismemberment.dm
@@ -217,7 +217,7 @@
LB.brainmob = brainmob
brainmob = null
LB.brainmob.forceMove(LB)
- LB.brainmob.stat = DEAD
+ LB.brainmob.set_stat(DEAD)
/obj/item/organ/eyes/transfer_to_limb(obj/item/bodypart/head/LB, mob/living/carbon/human/C)
LB.eyes = src
diff --git a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
index bc598e81e3..eb98bdb97f 100644
--- a/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
+++ b/modular_citadel/code/modules/reagents/chemistry/reagents/fermi_reagents.dm
@@ -451,7 +451,7 @@
if(H.InCritical())
perma = TRUE
volume = 5
- H.stat = DEAD
+ H.set_stat(DEAD)
catto.origin = H
/datum/reagent/fermi/secretcatchem/on_mob_life(mob/living/carbon/H)
@@ -488,7 +488,7 @@
var/mob/living/simple_animal/pet/cat/custom_cat/catto = L
if(catto.origin)
var/mob/living/carbon/human/H = catto.origin
- H.stat = CONSCIOUS
+ H.set_stat(CONSCIOUS)
log_reagent("FERMICHEM: [catto] ckey: [catto.key] has returned to normal.")
to_chat(catto, "Your body shifts back to normal!")
H.forceMove(catto.loc)