diff --git a/code/__DEFINES/traits.dm b/code/__DEFINES/traits.dm
index f037ba138d2..a8eec56bd30 100644
--- a/code/__DEFINES/traits.dm
+++ b/code/__DEFINES/traits.dm
@@ -721,6 +721,10 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Echolocation has a higher range.
#define TRAIT_ECHOLOCATION_EXTRA_RANGE "echolocation_extra_range"
+/// Trait given to a living mob and any observer mobs that stem from them if they suicide.
+/// For clarity, this trait should always be associated/tied to a reference to the mob that suicided- not anything else.
+#define TRAIT_SUICIDED "committed_suicide"
+
// common trait sources
#define TRAIT_GENERIC "generic"
#define UNCONSCIOUS_TRAIT "unconscious"
diff --git a/code/controllers/subsystem/blackbox.dm b/code/controllers/subsystem/blackbox.dm
index 0126930ae29..a9115e93b20 100644
--- a/code/controllers/subsystem/blackbox.dm
+++ b/code/controllers/subsystem/blackbox.dm
@@ -320,7 +320,10 @@ Versioning
return
if(!L || !L.key || !L.mind)
return
- if(!L.suiciding && !first_death.len)
+
+ var/did_they_suicide = HAS_TRAIT_FROM(L, TRAIT_SUICIDED, REF(L)) // simple boolean, did they suicide (true) or not (false)
+
+ if(!did_they_suicide && !first_death.len)
first_death["name"] = "[(L.real_name == L.name) ? L.real_name : "[L.real_name] as [L.name]"]"
first_death["role"] = null
first_death["role"] = L.mind.assigned_role.title
@@ -353,7 +356,7 @@ Versioning
"y_coord" = L.y,
"z_coord" = L.z,
"last_words" = L.last_words,
- "suicide" = L.suiciding,
+ "suicide" = did_they_suicide,
"map" = SSmapping.config.map_name,
"server_name" = CONFIG_GET(string/serversqlname), // SKYRAT EDIT ADDITION - MULTISERVER
"internet_address" = world.internet_address || "0",
diff --git a/code/datums/components/multiple_lives.dm b/code/datums/components/multiple_lives.dm
index dc95a126152..3cac7e827d3 100644
--- a/code/datums/components/multiple_lives.dm
+++ b/code/datums/components/multiple_lives.dm
@@ -24,12 +24,12 @@
/// Stops a dying station pet from overriding persistence data before we respawn it and thus causing issues.
/datum/component/multiple_lives/proc/on_write_memory(mob/living/source, dead, gibbed)
- if(dead && !source.suiciding)
+ if(dead && !HAS_TRAIT(source, TRAIT_SUICIDED))
return COMPONENT_DONT_WRITE_MEMORY
/datum/component/multiple_lives/proc/respawn(mob/living/source, gibbed)
SIGNAL_HANDLER
- if(source.suiciding) //Freed from this mortail coil.
+ if(HAS_TRAIT(source, TRAIT_SUICIDED)) //Freed from this mortail coil.
qdel(src)
return
//Gives the old mob this trait in case it gets revived, so we won't end up eventually overriding data
diff --git a/code/game/gamemodes/game_mode.dm b/code/game/gamemodes/game_mode.dm
index 015f626b3a7..6470b2e9c7e 100644
--- a/code/game/gamemodes/game_mode.dm
+++ b/code/game/gamemodes/game_mode.dm
@@ -164,7 +164,7 @@
msg += "[L.name] ([L.key]), the [L.job] (Connected, Inactive)\n"
failed = TRUE //AFK client
if(!failed && L.stat)
- if(L.suiciding) //Suicider
+ if(HAS_TRAIT(L, TRAIT_SUICIDED)) //Suicider
msg += "[L.name] ([L.key]), the [L.job] ([span_boldannounce("Suicide")])\n"
failed = TRUE //Disconnected client
if(!failed && (L.stat == UNCONSCIOUS || L.stat == HARD_CRIT))
@@ -178,7 +178,7 @@
for(var/mob/dead/observer/D in GLOB.dead_mob_list)
if(D.mind && D.mind.current == L)
if(L.stat == DEAD)
- if(L.suiciding) //Suicider
+ if(HAS_TRAIT(L, TRAIT_SUICIDED)) //Suicider
msg += "[L.name] ([ckey(D.mind.key)]), the [L.job] ([span_boldannounce("Suicide")])\n"
continue //Disconnected client
else
diff --git a/code/game/gamemodes/objective.dm b/code/game/gamemodes/objective.dm
index c5b9434cf6b..6235e61e1ed 100644
--- a/code/game/gamemodes/objective.dm
+++ b/code/game/gamemodes/objective.dm
@@ -319,7 +319,7 @@ GLOBAL_LIST_EMPTY(objectives) //SKYRAT EDIT ADDITION
if(human_check)
brain_target = target.current?.getorganslot(ORGAN_SLOT_BRAIN)
//Protect will always suceed when someone suicides
- return !target || target.current?.suiciding || considered_alive(target, enforce_human = human_check) || brain_target?.suicided
+ return !target || (target.current && HAS_TRAIT(target.current, TRAIT_SUICIDED)) || considered_alive(target, enforce_human = human_check) || (brain_target && HAS_TRAIT(brain_target, TRAIT_SUICIDED))
/datum/objective/protect/update_explanation_text()
..()
@@ -562,7 +562,7 @@ GLOBAL_LIST_EMPTY(objectives) //SKYRAT EDIT ADDITION
for(var/datum/mind/M in owners)
if(considered_alive(M))
return FALSE
- if(M.current?.suiciding) //killing yourself ISN'T glorious.
+ if(M.current && HAS_TRAIT(M.current, TRAIT_SUICIDED)) //killing yourself ISN'T glorious.
return FALSE
return TRUE
diff --git a/code/game/objects/structures/ai_core.dm b/code/game/objects/structures/ai_core.dm
index 6128982549b..d0d1e5dc9db 100644
--- a/code/game/objects/structures/ai_core.dm
+++ b/code/game/objects/structures/ai_core.dm
@@ -42,9 +42,9 @@
accept_laws = FALSE
. += span_notice("There is a slot for a reinforced glass panel, the [AI_CORE_BRAIN(core_mmi)] could be pried out.[accept_laws ? " A law module can be swiped across." : ""]")
if(GLASS_CORE)
- . += span_notice("The monitor [core_mmi?.brainmob?.mind && !core_mmi?.brainmob?.suiciding ? "and neural interface " : ""]can be screwed in, the panel can be pried out.")
+ . += span_notice("The monitor [core_mmi?.brainmob?.mind && !suicide_check() ? "and neural interface " : ""]can be screwed in, the panel can be pried out.")
if(AI_READY_CORE)
- . += span_notice("The monitor's connection can be cut[core_mmi?.brainmob?.mind && !core_mmi?.brainmob?.suiciding ? " the neural interface can be screwed in." : "."]")
+ . += span_notice("The monitor's connection can be cut[core_mmi?.brainmob?.mind && !suicide_check() ? " the neural interface can be screwed in." : "."]")
/obj/structure/ai_core/handle_atom_del(atom/A)
if(A == circuit)
@@ -144,7 +144,7 @@
if(!core_mmi)
balloon_alert(user, "no brain installed!")
return TOOL_ACT_TOOLTYPE_SUCCESS
- else if(!core_mmi.brainmob?.mind || core_mmi.brainmob?.suiciding)
+ else if(!core_mmi.brainmob?.mind || suicide_check())
balloon_alert(user, "brain is inactive!")
return TOOL_ACT_TOOLTYPE_SUCCESS
else
@@ -255,7 +255,7 @@
if(!core_mmi)
balloon_alert(user, "no brain installed!")
return
- if(!core_mmi.brainmob || !core_mmi.brainmob?.mind || core_mmi.brainmob?.suiciding)
+ if(!core_mmi.brainmob || !core_mmi.brainmob?.mind || suicide_check())
balloon_alert(user, "[AI_CORE_BRAIN(core_mmi)] is inactive!")
return
if(core_mmi.laws.id != DEFAULT_AI_LAWID)
@@ -271,7 +271,7 @@
var/install = tgui_alert(user, "This [AI_CORE_BRAIN(M)] is inactive, would you like to make an inactive AI?", "Installing AI [AI_CORE_BRAIN(M)]", list("Yes", "No"))
if(install != "Yes")
return
- if(M.brainmob?.suiciding)
+ if(M.brainmob && HAS_TRAIT(M.brainmob, TRAIT_SUICIDED))
to_chat(user, span_warning("[M.name] is completely useless!"))
return
if(!user.transferItemToLoc(M, src))
@@ -312,7 +312,7 @@
return
if(P.tool_behaviour == TOOL_SCREWDRIVER)
- if(core_mmi?.brainmob?.suiciding)
+ if(suicide_check())
to_chat(user, span_warning("The brain installed is completely useless."))
return
P.play_tool_sound(src)
@@ -338,7 +338,7 @@
/obj/structure/ai_core/proc/ai_structure_to_mob()
var/mob/living/brain/the_brainmob = core_mmi.brainmob
- if(!the_brainmob.mind || the_brainmob.suiciding)
+ if(!the_brainmob.mind || suicide_check())
return FALSE
the_brainmob.mind.remove_antags_for_borging()
if(!the_brainmob.mind.has_ever_been_ai)
@@ -394,6 +394,12 @@
new /obj/item/stack/sheet/plasteel(loc, 4)
qdel(src)
+/// Quick proc to call to see if the brainmob inside of us has suicided. Returns TRUE if we have, FALSE in any other scenario.
+/obj/structure/ai_core/proc/suicide_check()
+ if(isnull(core_mmi) || isnull(core_mmi.brainmob))
+ return FALSE
+ return HAS_TRAIT(core_mmi.brainmob, TRAIT_SUICIDED)
+
/*
This is a good place for AI-related object verbs so I'm sticking it here.
If adding stuff to this, don't forget that an AI need to cancel_camera() whenever it physically moves to a different location.
@@ -416,7 +422,7 @@ That prevents a few funky behaviors.
if(core_mmi.brainmob.mind)
to_chat(user, span_warning("[src] already contains an active mind!"))
return
- else if(core_mmi.brainmob.suiciding)
+ else if(suicide_check())
to_chat(user, span_warning("[AI_CORE_BRAIN(core_mmi)] installed in [src] is completely useless!"))
return
//Transferring a carded AI to a core.
diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm
index f201166f5c1..84921dcf8ba 100644
--- a/code/game/objects/structures/morgue.dm
+++ b/code/game/objects/structures/morgue.dm
@@ -199,7 +199,7 @@ GLOBAL_LIST_EMPTY(bodycontainers) //Let them act as spawnpoints for revenants an
for(var/mob/living/M in compiled)
var/mob/living/mob_occupant = get_mob_or_brainmob(M)
- if(mob_occupant.client && !mob_occupant.suiciding && !(HAS_TRAIT(mob_occupant, TRAIT_BADDNA)))
+ if(mob_occupant.client && !(HAS_TRAIT(mob_occupant, TRAIT_SUICIDED))&& !(HAS_TRAIT(mob_occupant, TRAIT_BADDNA)))
icon_state = "morgue4" // Revivable
if(mob_occupant.stat == DEAD && beeper && COOLDOWN_FINISHED(src, next_beep))
playsound(src, 'sound/weapons/gun/general/empty_alarm.ogg', 50, FALSE) //Revive them you blind fucks
diff --git a/code/modules/antagonists/cult/runes.dm b/code/modules/antagonists/cult/runes.dm
index c3b60fc65b0..f8856ebdc08 100644
--- a/code/modules/antagonists/cult/runes.dm
+++ b/code/modules/antagonists/cult/runes.dm
@@ -345,7 +345,7 @@ structure_check() searches for nearby cultist structures required for the invoca
qdel(sacrificial)
return TRUE
var/obj/item/soulstone/stone = new /obj/item/soulstone(get_turf(src))
- if(sacrificial.mind && !sacrificial.suiciding)
+ if(sacrificial.mind && !HAS_TRAIT(sacrificial, TRAIT_SUICIDED))
stone.capture_soul(sacrificial, first_invoker, TRUE)
if(sacrificial)
diff --git a/code/modules/client/verbs/suicide.dm b/code/modules/client/verbs/suicide.dm
index 6ddcd5a2c75..06c39c920d3 100644
--- a/code/modules/client/verbs/suicide.dm
+++ b/code/modules/client/verbs/suicide.dm
@@ -1,246 +1,68 @@
-/mob/proc/set_suicide(suicide_state)
- suiciding = suicide_state
+/// Verb to simply kill yourself (in a very visual way to all players) in game! How family-friendly. Can be governed by a series of multiple checks (i.e. confirmation, is it allowed in this area, etc.) which are
+/// handled and called by the proc this verb invokes. It's okay to block this, because we typically always give mobs in-game the ability to Ghost out of their current mob irregardless of context. This, in contrast,
+/// can have as many different checks as you desire to prevent people from doing the deed to themselves.
+/mob/living/verb/suicide()
+ set hidden = TRUE
+ handle_suicide()
+
+/// Actually handles the bare basics of the suicide process. Message type is the message we want to dispatch in the world regarding the suicide, using the defines in this file.
+/// Override this ENTIRELY if you want to add any special behavior to your suicide handling, if you fuck up the order of operations then shit will break.
+/mob/living/proc/handle_suicide()
+ SHOULD_CALL_PARENT(FALSE)
+ if(!suicide_alert())
+ return
+
+ // SKYRAT EDIT ADDITION
+ if(CONFIG_GET(flag/disable_suicide))
+ to_chat(usr, span_warning("Suicide is disabled on this server."))
+ return
+ // SKYRAT EDIT END
+
+ set_suicide(TRUE)
+ send_applicable_messages()
+ final_checkout()
+
+/// Proc that handles adding the TRAIT_SUICIDED on the mob in question, as well as additional operations to ensure that everything goes smoothly when we're certain that this person is going to kill themself.
+/// suicide_state is a boolean, and we handle adding/removing the trait in question. Have the trait function reference this mob as the source if we want to do in-depth tracking of where a suicided trait comes from.
+/// For example, the /mob/dead/observer that will inevitably come from the suicidee will inherit the suicided trait upon creation, and keep this reference. Handy for doing checking should we need it.
+/mob/living/proc/set_suicide(suicide_state)
if(suicide_state)
+ ADD_TRAIT(src, TRAIT_SUICIDED, REF(src))
add_to_mob_suicide_list()
else
+ REMOVE_TRAIT(src, TRAIT_SUICIDED, REF(src))
remove_from_mob_suicide_list()
-/mob/living/carbon/set_suicide(suicide_state) //you thought that box trick was pretty clever, didn't you? well now hardmode is on, boyo.
- . = ..()
- var/obj/item/organ/internal/brain/B = getorganslot(ORGAN_SLOT_BRAIN)
- if(B)
- B.suicided = suicide_state
-
-/mob/living/silicon/robot/set_suicide(suicide_state)
- . = ..()
- if(mmi)
- if(mmi.brain)
- mmi.brain.suicided = suicide_state
- if(mmi.brainmob)
- mmi.brainmob.suiciding = suicide_state
-
-//SKYRAT EDIT REMOVAL BEGIN - SUICIDE_VERB
-/*
-/mob/living/carbon/human/verb/suicide()
- set hidden = TRUE
- if(!canSuicide())
- return
+/// Sends a TGUI Alert to the person attempting to commit suicide. Returns TRUE if they confirm they want to die, FALSE otherwise. Check can_suicide here as well.
+/mob/living/proc/suicide_alert()
+ // Save this for later to ensure that if we change ckeys somehow, we exit out of the suicide.
var/oldkey = ckey
- var/confirm = tgui_alert(usr,"Are you sure you want to commit suicide?", "Confirm Suicide", list("Yes", "No"))
- if(ckey != oldkey)
- return
- if(!canSuicide())
- return
+ if(!can_suicide())
+ return FALSE
+
+ var/confirm = tgui_alert(src, "Are you sure you want to commit suicide?", "Confirm Suicide", list("Yes", "No"))
+
+ // ensure our situation didn't change while we were sleeping waiting for the tgui_alert.
+ if(!can_suicide() || (ckey != oldkey))
+ return FALSE
+
if(confirm == "Yes")
- if(suiciding)
- to_chat(src, span_warning("You're already trying to commit suicide!"))
- return
- set_suicide(TRUE) //need to be called before calling suicide_act as fuck knows what suicide_act will do with your suicider
- var/obj/item/held_item = get_active_held_item()
+ return TRUE
- var/damagetype = SEND_SIGNAL(src, COMSIG_HUMAN_SUICIDE_ACT) || held_item?.suicide_act(src)
- if(damagetype)
- if(damagetype & SHAME)
- adjustStaminaLoss(200)
- set_suicide(FALSE)
- add_mood_event("shameful_suicide", /datum/mood_event/shameful_suicide)
- return
+ balloon_alert(src, "suicide attempt aborted!")
+ return FALSE
- if(damagetype & MANUAL_SUICIDE_NONLETHAL) //Make sure to call the necessary procs if it does kill later
- set_suicide(FALSE)
- return
+/// Checks if we are in a valid state to suicide (not already suiciding, capable of actually killing ourselves, area checks, etc.) Returns TRUE if we can suicide, FALSE if we can not.
+/mob/living/proc/can_suicide()
+ if(HAS_TRAIT_FROM_ONLY(src, TRAIT_SUICIDED, REF(src)))
+ to_chat(src, span_warning("You are already commiting suicide!"))
+ return FALSE
- suicide_log()
-
- var/damage_mod = 0
- for(var/T in list(BRUTELOSS, FIRELOSS, TOXLOSS, OXYLOSS))
- damage_mod += (T & damagetype) ? 1 : 0
- damage_mod = max(1, damage_mod)
-
- //Do 200 damage divided by the number of damage types applied.
- if(damagetype & BRUTELOSS)
- adjustBruteLoss(200/damage_mod)
-
- if(damagetype & FIRELOSS)
- adjustFireLoss(200/damage_mod)
-
- if(damagetype & TOXLOSS)
- adjustToxLoss(200/damage_mod)
-
- if(damagetype & OXYLOSS)
- adjustOxyLoss(200/damage_mod)
-
- if(damagetype & MANUAL_SUICIDE) //Assume the object will handle the death.
- investigate_log("has died from committing suicide[held_item ? " with [held_item]" : ""].", INVESTIGATE_DEATHS)
- return
-
- //If something went wrong, just do normal oxyloss
- if(!(damagetype & (BRUTELOSS | FIRELOSS | TOXLOSS | OXYLOSS) ))
- adjustOxyLoss(max(200 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0))
-
- investigate_log("has died from committing suicide[held_item ? " with [held_item]" : ""].", INVESTIGATE_DEATHS)
- death(FALSE)
- ghostize(FALSE) // Disallows reentering body and disassociates mind
-
- return
-
- var/suicide_message
-
- if(!combat_mode)
- var/obj/item/organ/internal/brain/userbrain = getorgan(/obj/item/organ/internal/brain)
- if(userbrain?.damage >= 75)
- suicide_message = "[src] pulls both arms outwards in front of [p_their()] chest and pumps them behind [p_their()] back, repeats this motion in a smaller range of motion \
- down to [p_their()] hips two times once more all while sliding [p_their()] legs in a faux walking motion, claps [p_their()] hands together \
- in front of [p_them()] while both [p_their()] knees knock together, pumps [p_their()] arms downward, pronating [p_their()] wrists and abducting \
- [p_their()] fingers outward while crossing [p_their()] legs back and forth, repeats this motion again two times while keeping [p_their()] shoulders low\
- and hunching over, does finger guns with right hand and left hand bent on [p_their()] hip while looking directly forward and putting [p_their()] left leg forward then\
- crossing [p_their()] arms and leaning back a little while bending [p_their()] knees at an angle! It looks like [p_theyre()] trying to commit suicide."
- else
- suicide_message = pick("[src] is hugging [p_them()]self to death! It looks like [p_theyre()] trying to commit suicide.", \
- "[src] is high-fiving [p_them()]self to death! It looks like [p_theyre()] trying to commit suicide.", \
- "[src] is getting too high on life! It looks like [p_theyre()] trying to commit suicide.")
- else
- suicide_message = pick("[src] is attempting to bite [p_their()] tongue off! It looks like [p_theyre()] trying to commit suicide.", \
- "[src] is jamming [p_their()] thumbs into [p_their()] eye sockets! It looks like [p_theyre()] trying to commit suicide.", \
- "[src] is twisting [p_their()] own neck! It looks like [p_theyre()] trying to commit suicide.", \
- "[src] is holding [p_their()] breath! It looks like [p_theyre()] trying to commit suicide.")
-
- visible_message(span_danger("[suicide_message]"), span_userdanger("[suicide_message]"))
-
- suicide_log()
-
- adjustOxyLoss(max(200 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0))
- investigate_log("has died from committing suicide[held_item ? " with [held_item]" : ""].", INVESTIGATE_DEATHS)
- death(FALSE)
- ghostize(FALSE) // Disallows reentering body and disassociates mind
-
-/mob/living/brain/verb/suicide()
- set hidden = TRUE
- if(!canSuicide())
- return
- var/confirm = tgui_alert(usr,"Are you sure you want to commit suicide?", "Confirm Suicide", list("Yes", "No"))
- if(!canSuicide())
- return
- if(confirm == "Yes")
- set_suicide(TRUE)
- visible_message(span_danger("[src]'s brain is growing dull and lifeless. [p_they(TRUE)] look[p_s()] like [p_theyve()] lost the will to live."), \
- span_userdanger("[src]'s brain is growing dull and lifeless. [p_they(TRUE)] look[p_s()] like [p_theyve()] lost the will to live."))
-
- suicide_log()
-
- death(FALSE)
- ghostize(FALSE) // Disallows reentering body and disassociates mind
-
-/mob/living/silicon/ai/verb/suicide()
- set hidden = TRUE
- if(!canSuicide())
- return
- var/confirm = tgui_alert(usr,"Are you sure you want to commit suicide?", "Confirm Suicide", list("Yes", "No"))
- if(!canSuicide())
- return
- if(confirm == "Yes")
- set_suicide(TRUE)
- visible_message(span_danger("[src] is powering down. It looks like [p_theyre()] trying to commit suicide."), \
- span_userdanger("[src] is powering down. It looks like [p_theyre()] trying to commit suicide."))
-
- suicide_log()
-
- //put em at -175
- adjustOxyLoss(max(maxHealth * 2 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0))
- death(FALSE)
- ghostize(FALSE) // Disallows reentering body and disassociates mind
-
-/mob/living/silicon/robot/verb/suicide()
- set hidden = TRUE
- if(!canSuicide())
- return
- var/confirm = tgui_alert(usr,"Are you sure you want to commit suicide?", "Confirm Suicide", list("Yes", "No"))
- if(!canSuicide())
- return
- if(confirm == "Yes")
- set_suicide(TRUE)
- visible_message(span_danger("[src] is powering down. It looks like [p_theyre()] trying to commit suicide."), \
- span_userdanger("[src] is powering down. It looks like [p_theyre()] trying to commit suicide."))
-
- suicide_log()
-
- //put em at -175
- adjustOxyLoss(max(maxHealth * 2 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0))
- death(FALSE)
- ghostize(FALSE) // Disallows reentering body and disassociates mind
-*/
-//SKYRAT EDIT REMOVAL END
-
-//SKYRAT EDIT PAI START - Returns ability to leave your PAI
-/mob/living/silicon/pai/verb/suicide()
- set hidden = TRUE
- var/confirm = tgui_alert(usr,"Are you sure you want to commit suicide?", "Confirm Suicide", list("Yes", "No"))
- if(confirm == "Yes")
- var/turf/T = get_turf(src.loc)
- T.visible_message(span_notice("[src] flashes a message across its screen, \"Wiping core files. Please acquire a new personality to continue using pAI device functions.\""), null, \
- span_notice("[src] bleeps electronically."))
-
- suicide_log()
-
- death(FALSE)
- ghostize(FALSE) // Disallows reentering body and disassociates mind
- else
- to_chat(src, "Aborting suicide attempt.")
-//SKYRAT EDIT PAI END
-
-//SKYRAT EDIT REMOVAL START
-/*
-/mob/living/carbon/alien/adult/verb/suicide()
- set hidden = TRUE
- if(!canSuicide())
- return
- var/confirm = tgui_alert(usr,"Are you sure you want to commit suicide?", "Confirm Suicide", list("Yes", "No"))
- if(!canSuicide())
- return
- if(confirm == "Yes")
- set_suicide(TRUE)
- visible_message(span_danger("[src] is thrashing wildly! It looks like [p_theyre()] trying to commit suicide."), \
- span_userdanger("[src] is thrashing wildly! It looks like [p_theyre()] trying to commit suicide."), \
- span_hear("You hear thrashing."))
-
- suicide_log()
-
- //put em at -175
- adjustOxyLoss(max(200 - getFireLoss() - getBruteLoss() - getOxyLoss(), 0))
- death(FALSE)
- ghostize(FALSE) // Disallows reentering body and disassociates mind
-
-/mob/living/simple_animal/verb/suicide()
- set hidden = TRUE
- if(!canSuicide())
- return
- var/confirm = tgui_alert(usr,"Are you sure you want to commit suicide?", "Confirm Suicide", list("Yes", "No"))
- if(!canSuicide())
- return
- if(confirm == "Yes")
- set_suicide(TRUE)
- visible_message(span_danger("[src] begins to fall down. It looks like [p_theyve()] lost the will to live."), \
- span_userdanger("[src] begins to fall down. It looks like [p_theyve()] lost the will to live."))
-
- suicide_log()
-
- death(FALSE)
- ghostize(FALSE) // Disallows reentering body and disassociates mind
-*/
-//SKYRAT EDIT REMOVAL END
-
-/mob/living/proc/suicide_log()
- investigate_log("has died from committing suicide.", INVESTIGATE_DEATHS)
- log_message("committed suicide as [src.type]", LOG_ATTACK)
-
-/mob/living/carbon/human/suicide_log()
- log_message("(job: [src.job ? "[src.job]" : "None"]) committed suicide", LOG_ATTACK)
-
-/mob/living/proc/canSuicide()
- var/area/A = get_area(src)
- if(A.area_flags & BLOCK_SUICIDE)
+ var/area/checkable = get_area(src)
+ if(checkable.area_flags & BLOCK_SUICIDE)
to_chat(src, span_warning("You can't commit suicide here! You can ghost if you'd like."))
- return
+ return FALSE
+
switch(stat)
if(CONSCIOUS)
return TRUE
@@ -250,12 +72,70 @@
to_chat(src, span_warning("You need to be conscious to commit suicide!"))
if(DEAD)
to_chat(src, span_warning("You're already dead!"))
- return
+ return FALSE
-/mob/living/carbon/canSuicide()
- if(!..())
- return
- if(!(mobility_flags & MOBILITY_USE)) //just while I finish up the new 'fun' suiciding verb. This is to prevent metagaming via suicide
- to_chat(src, span_warning("You can't commit suicide whilst immobile! ((You can type Ghost instead however.))"))
- return
+/// Inserts in logging and death + mind dissociation when we're fully done with ending the life of our mob, as well as adjust the health. We will disallow re-entering the body when this is called.
+/// The suicide_tool variable is currently only used for humans in order to allow suicide log to properly put stuff in investigate log.
+/// Set apply_damage to FALSE in order to not do damage (in case it's handled elsewhere in the verb or another proc that the suicide tree calls). Will dissociate client from mind and ghost the player regardless.
+/mob/living/proc/final_checkout(obj/item/suicide_tool, apply_damage = TRUE)
+ if(apply_damage) // enough to really drive home the point that they are DEAD.
+ apply_suicide_damage()
+
+ suicide_log(suicide_tool)
+ death(FALSE)
+ ghostize(FALSE)
+
+/// Send all suicide-related messages out to the world. message_type can be used to change out the dispatched suicide message depending on the suicide context.
+/mob/living/proc/send_applicable_messages(message_type)
+ visible_message(span_danger(get_visible_suicide_message()), span_userdanger(get_visible_suicide_message()), span_hear(get_blind_suicide_message()))
+
+/// Returns a subtype-specific flavorful string pertaining to this exact living mob's ending their own life to those who can see it (visible message).
+/// If you don't want a message, prefer to override send_applicable_messages() on your subtype instead.
+/mob/living/proc/get_visible_suicide_message()
+ return "[src] begins to fall down. It looks like [p_theyve()] lost the will to live."
+
+/// Returns an appropriate string for what people who lack visibility hear when this mob kills itself.
+/// If you don't want a message, prefer to override send_applicable_messages() on your subtype instead.
+/mob/living/proc/get_blind_suicide_message()
+ return "You hear something hitting the floor."
+
+/// Inserts logging in both the mob's logs and the investigate log pertaining to their death. Suicide tool is the object we used to commit suicide, if one was held and used (presently only humans use this arg).
+/mob/living/proc/suicide_log(obj/item/suicide_tool)
+ investigate_log("has died from committing suicide.", INVESTIGATE_DEATHS)
+ log_message("committed suicide as [src.type]", LOG_ATTACK)
+
+/// The actual proc that will apply the damage to the suiciding mob. damage_type is the actual type of damage we want to deal, if that matters.
+/// Return TRUE if we actually apply any real damage, FALSE otherwise.
+/mob/living/proc/apply_suicide_damage(obj/item/suicide_tool, damage_type = NONE)
+ adjustOxyLoss(max(maxHealth * 2 - getToxLoss() - getFireLoss() - getBruteLoss() - getOxyLoss(), 0))
return TRUE
+
+/// If we want to apply multiple types of damage to a carbon mob based on the way they suicide, this is the proc that handles that.
+/// Currently only compatible with Brute, Burn, Toxin, and Suffocation Damage. damage_type is the bitflag that carries the information.
+/mob/living/proc/handle_suicide_damage_spread(damage_type)
+ // We split up double the total health the mob has, then spread it out.
+ var/damage_to_apply = (maxHealth * 2) // For humans, this value comes out to 200.
+ // The multiplier that we divide damage_to_apply by.
+ var/damage_mod = 0
+ // We don't want to damage_type again and again, this will hold the results.
+ var/list/filtered_damage_types = list()
+
+ for(var/type in list(BRUTELOSS, FIRELOSS, OXYLOSS, TOXLOSS))
+ if(!(type & damage_type))
+ continue
+ damage_mod++
+ filtered_damage_types += type
+
+ damage_mod = max(1, damage_mod) // division by zero is silly
+ damage_to_apply = (damage_to_apply / damage_mod)
+
+ for(var/filtered_type in filtered_damage_types)
+ switch(filtered_type)
+ if(BRUTELOSS)
+ adjustBruteLoss(damage_to_apply)
+ if(FIRELOSS)
+ adjustFireLoss(damage_to_apply)
+ if(OXYLOSS)
+ adjustOxyLoss(damage_to_apply)
+ if(TOXLOSS)
+ adjustToxLoss(damage_to_apply)
diff --git a/code/modules/hydroponics/grown/replicapod.dm b/code/modules/hydroponics/grown/replicapod.dm
index cb4f62c87f1..ef7f18c2c89 100644
--- a/code/modules/hydroponics/grown/replicapod.dm
+++ b/code/modules/hydroponics/grown/replicapod.dm
@@ -136,12 +136,12 @@
make_podman = TRUE
break
else
- if(M.ckey == ckey && M.stat == DEAD && !M.suiciding)
+ if(M.ckey == ckey && M.stat == DEAD && !HAS_TRAIT(M, TRAIT_SUICIDED))
make_podman = TRUE
break
else //If the player has ghosted from his corpse before blood was drawn, his ckey is no longer attached to the mob, so we need to match up the cloned player through the mind key
for(var/mob/M in GLOB.player_list)
- if(mind && M.mind && ckey(M.mind.key) == ckey(mind.key) && M.ckey && M.client && M.stat == DEAD && !M.suiciding)
+ if(mind && M.mind && ckey(M.mind.key) == ckey(mind.key) && M.ckey && M.client && M.stat == DEAD && !HAS_TRAIT(M, TRAIT_SUICIDED))
if(isobserver(M))
var/mob/dead/observer/O = M
if(!O.can_reenter_corpse)
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 57d3f0ebf1a..543760d68e7 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -101,7 +101,8 @@ GLOBAL_VAR_INIT(observer_default_invisibility, INVISIBILITY_OBSERVER)
mind = body.mind //we don't transfer the mind but we keep a reference to it.
- set_suicide(body.suiciding) // Transfer whether they committed suicide.
+ if(HAS_TRAIT_FROM_ONLY(body, TRAIT_SUICIDED, REF(body))) // transfer if the body was killed due to suicide
+ ADD_TRAIT(src, TRAIT_SUICIDED, REF(body))
if(ishuman(body))
var/mob/living/carbon/human/body_human = body
diff --git a/code/modules/mob/living/blood.dm b/code/modules/mob/living/blood.dm
index e1d79567b29..2d55f2dfb24 100644
--- a/code/modules/mob/living/blood.dm
+++ b/code/modules/mob/living/blood.dm
@@ -255,7 +255,7 @@
else if(last_mind)
blood_data["ckey"] = ckey(last_mind.key)
- if(!suiciding)
+ if(!HAS_TRAIT_FROM(src, TRAIT_SUICIDED, REF(src)))
blood_data["cloneable"] = 1
blood_data["blood_type"] = dna.blood_type
blood_data["gender"] = gender
diff --git a/code/modules/mob/living/brain/MMI.dm b/code/modules/mob/living/brain/MMI.dm
index b2d12766a4c..a1956aa9f64 100644
--- a/code/modules/mob/living/brain/MMI.dm
+++ b/code/modules/mob/living/brain/MMI.dm
@@ -84,7 +84,7 @@
newbrain.brainmob = null
brainmob.forceMove(src)
brainmob.container = src
- var/fubar_brain = newbrain.suicided || brainmob.suiciding //brain is from a suicider
+ var/fubar_brain = newbrain.suicided || HAS_TRAIT(brainmob, TRAIT_SUICIDED) //brain is from a suicider
if(!fubar_brain && !(newbrain.organ_flags & ORGAN_FAILING)) // the brain organ hasn't been beaten to death, nor was from a suicider.
brainmob.set_stat(CONSCIOUS) //we manually revive the brain mob
else if(!fubar_brain && newbrain.organ_flags & ORGAN_FAILING) // the brain is damaged, but not from a suicider
@@ -275,7 +275,7 @@
if(user)
to_chat(user, span_warning("\The [src] indicates that their mind is currently inactive."))
return FALSE
- if(B.suiciding || brain?.suicided)
+ if(HAS_TRAIT(B, TRAIT_SUICIDED) || brain?.suicided)
if(user)
to_chat(user, span_warning("\The [src] indicates that their mind has no will to live!"))
return FALSE
diff --git a/code/modules/mob/living/brain/brain_item.dm b/code/modules/mob/living/brain/brain_item.dm
index 5afb4e5804a..3e5780cff42 100644
--- a/code/modules/mob/living/brain/brain_item.dm
+++ b/code/modules/mob/living/brain/brain_item.dm
@@ -60,7 +60,7 @@
else
C.key = brainmob.key
- C.set_suicide(brainmob.suiciding)
+ C.set_suicide(HAS_TRAIT(brainmob, TRAIT_SUICIDED))
QDEL_NULL(brainmob)
@@ -117,7 +117,10 @@
brainmob.name = L.real_name
brainmob.real_name = L.real_name
brainmob.timeofhostdeath = L.timeofdeath
- brainmob.suiciding = suicided
+
+ if(suicided)
+ ADD_TRAIT(brainmob, TRAIT_SUICIDED, REF(src))
+
if(L.has_dna())
var/mob/living/carbon/C = L
if(!brainmob.stored_dna)
diff --git a/code/modules/mob/living/brain/posibrain.dm b/code/modules/mob/living/brain/posibrain.dm
index aed1fcf7ece..26cfbe5ecd1 100644
--- a/code/modules/mob/living/brain/posibrain.dm
+++ b/code/modules/mob/living/brain/posibrain.dm
@@ -117,13 +117,13 @@ GLOBAL_VAR(posibrain_notify_cooldown)
return
if(is_occupied() || is_banned_from(user.ckey, ROLE_POSIBRAIN) || QDELETED(brainmob) || QDELETED(src) || QDELETED(user))
return
- if(user.suiciding) //if they suicided, they're out forever.
+ if(HAS_TRAIT(src, TRAIT_SUICIDED)) //if they suicided, they're out forever.
to_chat(user, span_warning("[src] fizzles slightly. Sadly it doesn't take those who suicided!"))
return
var/posi_ask = tgui_alert(user, "Become a [name]? (Warning, You can no longer be revived, and all past lives will be forgotten!)", "Confirm", list("Yes","No"))
if(posi_ask != "Yes" || QDELETED(src))
return
- if(brainmob.suiciding) //clear suicide status if the old occupant suicided.
+ if(HAS_TRAIT(brainmob, TRAIT_SUICIDED)) //clear suicide status if the old occupant suicided.
brainmob.set_suicide(FALSE)
transfer_personality(user)
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index bd56cbbc2ea..8b84fbc7ee5 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -937,8 +937,7 @@
if(HAS_TRAIT(src, TRAIT_DNR)) //This is also added when a ghost DNR's!
return DEFIB_FAIL_DNR
//SKYRAT EDIT ADDITION END - DNR TRAIT
-
- if (suiciding)
+ if (HAS_TRAIT(src, TRAIT_SUICIDED))
return DEFIB_FAIL_SUICIDE
if (HAS_TRAIT(src, TRAIT_HUSK))
@@ -968,7 +967,7 @@
if (current_brain.organ_flags & ORGAN_FAILING)
return DEFIB_FAIL_FAILING_BRAIN
- if (current_brain.suicided || current_brain.brainmob?.suiciding)
+ if (current_brain.suicided || (current_brain.brainmob && HAS_TRAIT(current_brain.brainmob, TRAIT_SUICIDED)))
return DEFIB_FAIL_NO_INTELLIGENCE
if(key && key[1] == "@") // Adminghosts
diff --git a/code/modules/mob/living/carbon/human/death.dm b/code/modules/mob/living/carbon/human/death.dm
index 0c277743875..c4abf6692a9 100644
--- a/code/modules/mob/living/carbon/human/death.dm
+++ b/code/modules/mob/living/carbon/human/death.dm
@@ -27,7 +27,7 @@ GLOBAL_LIST_EMPTY(dead_players_during_shift)
. = ..()
- if(client && !suiciding && !(client in GLOB.dead_players_during_shift))
+ if(client && !HAS_TRAIT(src, TRAIT_SUICIDED) && !(client in GLOB.dead_players_during_shift))
GLOB.dead_players_during_shift += client
if(!QDELETED(dna)) //The gibbed param is bit redundant here since dna won't exist at this point if they got deleted.
diff --git a/code/modules/mob/living/carbon/human/examine.dm b/code/modules/mob/living/carbon/human/examine.dm
index e4cac7b5d2b..5725eac1c03 100644
--- a/code/modules/mob/living/carbon/human/examine.dm
+++ b/code/modules/mob/living/carbon/human/examine.dm
@@ -160,7 +160,7 @@
just_sleeping = TRUE
if(!just_sleeping)
- if(suiciding)
+ if(HAS_TRAIT(src, TRAIT_SUICIDED))
. += span_warning("[t_He] appear[p_s()] to have committed suicide... there is no hope of recovery.")
. += generate_death_examine_text()
diff --git a/code/modules/mob/living/carbon/human/species_types/golems.dm b/code/modules/mob/living/carbon/human/species_types/golems.dm
index e0dd9694180..fd66833d34e 100644
--- a/code/modules/mob/living/carbon/human/species_types/golems.dm
+++ b/code/modules/mob/living/carbon/human/species_types/golems.dm
@@ -910,7 +910,7 @@
/obj/structure/cloth_pile/proc/revive()
if(QDELETED(src) || QDELETED(cloth_golem)) //QDELETED also checks for null, so if no cloth golem is set this won't runtime
return
- if(cloth_golem.suiciding)
+ if(HAS_TRAIT_FROM_ONLY(cloth_golem, TRAIT_SUICIDED, REF(cloth_golem)))
QDEL_NULL(cloth_golem)
return
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 85ffdb0184c..d4c5d31646e 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -908,7 +908,7 @@
reagents.clear_reagents()
if(heal_flags & HEAL_ADMIN)
- suiciding = FALSE
+ REMOVE_TRAIT(src, TRAIT_SUICIDED, REF(src))
updatehealth()
stop_sound_channel(CHANNEL_HEARTBEAT)
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 5284087aeb2..0e32adb1aae 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -70,7 +70,6 @@
///A sound sent when the mob dies, with the *deathgasp emote
var/death_sound
-
/// Helper vars for quick access to firestacks, these should be updated every time firestacks are adjusted
var/on_fire = FALSE
var/fire_stacks = 0
diff --git a/code/modules/mob/living/silicon/ai/ai.dm b/code/modules/mob/living/silicon/ai/ai.dm
index a353c967f21..7d24cb2de8d 100644
--- a/code/modules/mob/living/silicon/ai/ai.dm
+++ b/code/modules/mob/living/silicon/ai/ai.dm
@@ -414,15 +414,17 @@
the_mmi.brainmob.name = src.real_name
the_mmi.brainmob.real_name = src.real_name
the_mmi.brainmob.container = the_mmi
- the_mmi.brainmob.set_suicide(suiciding)
- the_mmi.brain.suicided = suiciding
+
+ var/has_suicided_trait = HAS_TRAIT(src, TRAIT_SUICIDED)
+ the_mmi.brainmob.set_suicide(has_suicided_trait)
+ the_mmi.brain.suicided = has_suicided_trait
if(the_core)
var/obj/structure/ai_core/core = the_core
core.core_mmi = the_mmi
the_mmi.forceMove(the_core)
else
the_mmi.forceMove(get_turf(src))
- if(the_mmi.brainmob.stat == DEAD && !suiciding)
+ if(the_mmi.brainmob.stat == DEAD && !has_suicided_trait)
the_mmi.brainmob.set_stat(CONSCIOUS)
if(mind)
mind.transfer_to(the_mmi.brainmob)
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index b8ddcd81e3f..0ea9af192f1 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -101,6 +101,13 @@
modularInterface.saved_job = "Cyborg"
return ..()
+/mob/living/silicon/robot/set_suicide(suicide_state)
+ . = ..()
+ if(mmi)
+ if(mmi.brain)
+ mmi.brain.suicided = suicide_state
+ if(suicide_state && mmi.brainmob)
+ ADD_TRAIT(mmi.brainmob, TRAIT_SUICIDED, REF(src))
/**
* Sets the tablet theme and icon
diff --git a/code/modules/mob/living/simple_animal/bot/medbot.dm b/code/modules/mob/living/simple_animal/bot/medbot.dm
index 8b80a96685f..8be8b70cd2c 100644
--- a/code/modules/mob/living/simple_animal/bot/medbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/medbot.dm
@@ -443,7 +443,7 @@
if(!(loc == C.loc) && !(isturf(C.loc) && isturf(loc)))
return FALSE
- if(C.suiciding)
+ if(HAS_TRAIT_FROM_ONLY(C, TRAIT_SUICIDED, REF(C)))
return FALSE //Kevorkian school of robotic medical assistants.
if(bot_cover_flags & BOT_COVER_EMAGGED) //Everyone needs our medicine. (Our medicine is toxins)
diff --git a/code/modules/mob/living/simple_animal/guardian/guardian.dm b/code/modules/mob/living/simple_animal/guardian/guardian.dm
index abec85c4c7c..f23082407a4 100644
--- a/code/modules/mob/living/simple_animal/guardian/guardian.dm
+++ b/code/modules/mob/living/simple_animal/guardian/guardian.dm
@@ -353,7 +353,7 @@ GLOBAL_LIST_EMPTY(parasites) //all currently existing/living guardians
forceMove(summoner.loc)
new /obj/effect/temp_visual/guardian/phase(loc)
-/mob/living/simple_animal/hostile/guardian/canSuicide()
+/mob/living/simple_animal/hostile/guardian/can_suicide()
return FALSE
/mob/living/simple_animal/hostile/guardian/proc/is_deployed()
diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm
index 1159a722461..a786aa82742 100644
--- a/code/modules/mob/living/simple_animal/shade.dm
+++ b/code/modules/mob/living/simple_animal/shade.dm
@@ -46,7 +46,7 @@
death_message = "lets out a contented sigh as [p_their()] form unwinds."
..()
-/mob/living/simple_animal/shade/canSuicide()
+/mob/living/simple_animal/shade/can_suicide()
if(istype(loc, /obj/item/soulstone)) //do not suicide inside the soulstone
return FALSE
return ..()
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 035571fb0b5..ea34cf4b701 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -65,9 +65,6 @@
///Cursor icon used when holding shift over things
var/examine_cursor_icon = 'icons/effects/mouse_pointers/examine_pointer.dmi'
- ///Whether this mob has or is in the middle of committing suicide.
- var/suiciding = FALSE
-
/// Whether a mob is alive or dead. TODO: Move this to living - Nodrak (2019, still here)
var/stat = CONSCIOUS
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index dac8f35e0b5..84ca1e9210c 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -250,7 +250,7 @@
if(ignore_mapload && SSatoms.initialized != INITIALIZATION_INNEW_REGULAR) //don't notify for objects created during a map load
return
for(var/mob/dead/observer/ghost in GLOB.player_list)
- if(!notify_suiciders && (ghost in GLOB.suicided_mob_list))
+ if(!notify_suiciders && HAS_TRAIT(ghost, TRAIT_SUICIDED))
continue
if(ignore_key && (ghost.ckey in GLOB.poll_ignore[ignore_key]))
continue
diff --git a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
index 1202d95f2fe..0725c59f655 100644
--- a/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
+++ b/code/modules/reagents/chemistry/reagents/medicine_reagents.dm
@@ -928,7 +928,7 @@
if(exposed_mob.stat != DEAD || !(exposed_mob.mob_biotypes & MOB_ORGANIC))
return ..()
- if(exposed_mob.suiciding) //they are never coming back
+ if(HAS_TRAIT(exposed_mob, TRAIT_SUICIDED)) //they are never coming back
exposed_mob.visible_message(span_warning("[exposed_mob]'s body does not react..."))
return
diff --git a/code/modules/spells/spell_types/pointed/mind_transfer.dm b/code/modules/spells/spell_types/pointed/mind_transfer.dm
index 59d44a1933a..b8719187c4c 100644
--- a/code/modules/spells/spell_types/pointed/mind_transfer.dm
+++ b/code/modules/spells/spell_types/pointed/mind_transfer.dm
@@ -41,7 +41,7 @@
return FALSE
if(!isliving(owner))
return FALSE
- if(owner.suiciding)
+ if(HAS_TRAIT(owner, TRAIT_SUICIDED))
if(feedback)
to_chat(owner, span_warning("You're killing yourself! You can't concentrate enough to do this!"))
return FALSE
diff --git a/code/modules/surgery/bodyparts/head.dm b/code/modules/surgery/bodyparts/head.dm
index 04d5982ca32..a1a0db82b2c 100644
--- a/code/modules/surgery/bodyparts/head.dm
+++ b/code/modules/surgery/bodyparts/head.dm
@@ -109,7 +109,7 @@
if(IS_ORGANIC_LIMB(src) && show_organs_on_examine)
if(!brain)
. += span_info("The brain has been removed from [src].")
- else if(brain.suicided || brainmob?.suiciding)
+ else if(brain.suicided || (brainmob && HAS_TRAIT(brainmob, TRAIT_SUICIDED)))
. += span_info("There's a miserable expression on [real_name]'s face; they must have really hated life. There's no hope of recovery.")
else if(brainmob?.health <= HEALTH_THRESHOLD_DEAD)
. += span_info("It's leaking some kind of... clear fluid? The brain inside must be in pretty bad shape.")
diff --git a/code/modules/surgery/organs/augments_chest.dm b/code/modules/surgery/organs/augments_chest.dm
index 010df97e729..9c88b515839 100644
--- a/code/modules/surgery/organs/augments_chest.dm
+++ b/code/modules/surgery/organs/augments_chest.dm
@@ -66,7 +66,7 @@
to_chat(owner, span_notice("Your reviver implant shuts down and starts recharging. It will be ready again in [DisplayTimeText(revive_cost)]."))
return
- if(!COOLDOWN_FINISHED(src, reviver_cooldown) || owner.suiciding)
+ if(!COOLDOWN_FINISHED(src, reviver_cooldown) || HAS_TRAIT(owner, TRAIT_SUICIDED))
return
switch(owner.stat)
diff --git a/code/modules/surgery/organs/heart.dm b/code/modules/surgery/organs/heart.dm
index 6ae5ba9998a..5a17610bfbd 100644
--- a/code/modules/surgery/organs/heart.dm
+++ b/code/modules/surgery/organs/heart.dm
@@ -348,7 +348,7 @@
return
- if(QDELETED(victim) || victim.suiciding)
+ if(QDELETED(victim) || HAS_TRAIT(victim, TRAIT_SUICIDED))
return //lol rip
if(!COOLDOWN_FINISHED(src, crystalize_cooldown))
diff --git a/code/modules/surgery/revival.dm b/code/modules/surgery/revival.dm
index 4c2f0fc9057..63f88435beb 100644
--- a/code/modules/surgery/revival.dm
+++ b/code/modules/surgery/revival.dm
@@ -19,9 +19,7 @@
return FALSE
if(target.stat != DEAD)
return FALSE
- if(target.suiciding || HAS_TRAIT(target, TRAIT_HUSK))
- return FALSE
- if(HAS_TRAIT(target, TRAIT_DEFIB_BLACKLISTED))
+ if(HAS_TRAIT(target, TRAIT_SUICIDED) || HAS_TRAIT(target, TRAIT_HUSK) || HAS_TRAIT(target, TRAIT_DEFIB_BLACKLISTED))
return FALSE
var/obj/item/organ/internal/brain/target_brain = target.getorganslot(ORGAN_SLOT_BRAIN)
if(!target_brain)
diff --git a/code/modules/zombie/organs.dm b/code/modules/zombie/organs.dm
index 4969e07734f..a31edeeb183 100644
--- a/code/modules/zombie/organs.dm
+++ b/code/modules/zombie/organs.dm
@@ -51,14 +51,10 @@
owner.adjustToxLoss(0.5 * delta_time)
if (DT_PROB(5, delta_time))
to_chat(owner, span_danger("You feel sick..."))
- if(timer_id)
- return
- if(owner.suiciding)
+ if(timer_id || HAS_TRAIT(owner, TRAIT_SUICIDED) || !owner.getorgan(/obj/item/organ/internal/brain))
return
if(owner.stat != DEAD && !converts_living)
return
- if(!owner.getorgan(/obj/item/organ/internal/brain))
- return
if(!iszombie(owner))
to_chat(owner, "You can feel your heart stopping, but something isn't right... \
life has not abandoned your broken form. You can only feel a deep and immutable hunger that \
diff --git a/config/skyrat/skyrat_config.txt b/config/skyrat/skyrat_config.txt
index 7ed3a632992..0b3f64e57b1 100644
--- a/config/skyrat/skyrat_config.txt
+++ b/config/skyrat/skyrat_config.txt
@@ -132,3 +132,6 @@ TICKET_PING_FREQUENCY 0
## Delay between newscasters making lore announcements, default is 18000 (30 minutes)
LORECASTER_DELAY 18000
+
+## If uncommented, suicide will be disabled.
+# DISABLE_SUICIDE
diff --git a/modular_skyrat/master_files/code/controllers/configuration/entries/skyrat_config_entries.dm b/modular_skyrat/master_files/code/controllers/configuration/entries/skyrat_config_entries.dm
index 803187c6496..e00a1f5379d 100644
--- a/modular_skyrat/master_files/code/controllers/configuration/entries/skyrat_config_entries.dm
+++ b/modular_skyrat/master_files/code/controllers/configuration/entries/skyrat_config_entries.dm
@@ -36,3 +36,6 @@
// Turn on/off guncargo permit-locked firing pins
/datum/config_entry/flag/permit_pins
+
+// Disables the ability to commit suicide
+/datum/config_entry/flag/disable_suicide
diff --git a/modular_skyrat/modules/synths/code/surgery/robot_chassis_restoration.dm b/modular_skyrat/modules/synths/code/surgery/robot_chassis_restoration.dm
index bb281a637dc..fca47d7186d 100644
--- a/modular_skyrat/modules/synths/code/surgery/robot_chassis_restoration.dm
+++ b/modular_skyrat/modules/synths/code/surgery/robot_chassis_restoration.dm
@@ -18,7 +18,7 @@
desc = "A surgical procedure that reboots a positronic brain."
/datum/surgery/robot_chassis_restoration/can_start(mob/user, mob/living/carbon/target)
- if(!..() || target.stat != DEAD || target.suiciding || !target.getorganslot(ORGAN_SLOT_BRAIN))
+ if(!..() || target.stat != DEAD || !target.getorganslot(ORGAN_SLOT_BRAIN))
return FALSE
return TRUE