diff --git a/code/datums/status_effects/debuffs.dm b/code/datums/status_effects/debuffs.dm
index f4732eea7e8..801076af58f 100644
--- a/code/datums/status_effects/debuffs.dm
+++ b/code/datums/status_effects/debuffs.dm
@@ -402,6 +402,13 @@
id = "sleeping"
tick_interval = 2 SECONDS
needs_update_stat = TRUE
+ /// Whether we decided to take a nap on our own.
+ /// As opposed to being hard knocked out with N2O or similar.
+ var/voluntary = FALSE
+
+/datum/status_effect/incapacitating/sleeping/on_creation(mob/living/new_owner, set_duration, voluntary = FALSE)
+ ..()
+ src.voluntary = voluntary
/datum/status_effect/incapacitating/sleeping/tick()
if(!iscarbon(owner))
diff --git a/code/datums/status_effects/status_effect.dm b/code/datums/status_effects/status_effect.dm
index 17c3be0fcaa..590888b2c43 100644
--- a/code/datums/status_effects/status_effect.dm
+++ b/code/datums/status_effects/status_effect.dm
@@ -105,8 +105,9 @@
// HELPER PROCS //
//////////////////
-/mob/living/proc/apply_status_effect(effect, ...) //applies a given status effect to this mob, returning the effect if it was successful
- . = FALSE
+/// Applies a given status effect to this mob, returning the effect if it was successful or null otherwise
+/mob/living/proc/apply_status_effect(effect, ...)
+ . = null
var/datum/status_effect/S1 = effect
LAZYINITLIST(status_effects)
for(var/datum/status_effect/S in status_effects)
@@ -123,7 +124,8 @@
S1 = new effect(arguments)
. = S1
-/mob/living/proc/remove_status_effect(effect, ...) //removes all of a given status effect from this mob, returning TRUE if at least one was removed
+/// Removes all of a given status effect from this mob, returning TRUE if at least one was removed
+/mob/living/proc/remove_status_effect(effect, ...)
. = FALSE
var/list/arguments = args.Copy(2)
if(status_effects)
@@ -133,15 +135,17 @@
qdel(S)
. = TRUE
-/mob/living/proc/has_status_effect(effect) //returns the effect if the mob calling the proc owns the given status effect
- . = FALSE
+/// Returns the effect if the mob calling the proc owns the given status effect, or null otherwise
+/mob/living/proc/has_status_effect(effect)
+ . = null
if(status_effects)
var/datum/status_effect/S1 = effect
for(var/datum/status_effect/S in status_effects)
if(initial(S1.id) == S.id)
return S
-/mob/living/proc/has_status_effect_list(effect) //returns a list of effects with matching IDs that the mod owns; use for effects there can be multiple of
+/// Returns a list of effects with matching IDs that the mod owns; use for effects there can be multiple of
+/mob/living/proc/has_status_effect_list(effect)
. = list()
if(status_effects)
var/datum/status_effect/S1 = effect
diff --git a/code/modules/mob/living/status_procs.dm b/code/modules/mob/living/status_procs.dm
index 92ed8060f4b..eb1c71b1e3d 100644
--- a/code/modules/mob/living/status_procs.dm
+++ b/code/modules/mob/living/status_procs.dm
@@ -382,7 +382,7 @@
S = apply_status_effect(STATUS_EFFECT_SLEEPING, amount)
return S
-/mob/living/proc/SetSleeping(amount, ignore_canstun = FALSE)
+/mob/living/proc/SetSleeping(amount, ignore_canstun = FALSE, voluntary = FALSE)
if(frozen) // If the mob has been admin frozen, sleeping should not be changeable
return
if(status_flags & GODMODE)
@@ -393,13 +393,19 @@
if(S)
S.duration = amount + world.time
else if(amount > 0)
- S = apply_status_effect(STATUS_EFFECT_SLEEPING, amount)
+ S = apply_status_effect(STATUS_EFFECT_SLEEPING, amount, voluntary)
+ if(!voluntary && S)
+ // Only set it one way (true => false)
+ // Otherwise if we are hard knocked out, and then try to nap, we'd be
+ // treated as "just lightly napping" and woken up by trivial stuff.
+ S.voluntary = FALSE
return S
/mob/living/proc/PermaSleeping() /// used for admin freezing.
var/datum/status_effect/incapacitating/sleeping/S = IsSleeping()
if(S)
S.duration = -1
+ S.voluntary = FALSE
else
S = apply_status_effect(STATUS_EFFECT_SLEEPING, -1)
return S
diff --git a/code/modules/mob/mob_helpers.dm b/code/modules/mob/mob_helpers.dm
index 51586346048..edf0d40520b 100644
--- a/code/modules/mob/mob_helpers.dm
+++ b/code/modules/mob/mob_helpers.dm
@@ -460,9 +460,8 @@ GLOBAL_LIST_INIT(intents, list(INTENT_HELP,INTENT_DISARM,INTENT_GRAB,INTENT_HARM
if(IsSleeping())
to_chat(src, "You are already sleeping.")
return
- else
- if(alert(src, "You sure you want to sleep for a while?", "Sleep", "Yes", "No") == "Yes")
- SetSleeping(40 SECONDS) //Short nap
+ if(alert(src, "You sure you want to sleep for a while?", "Sleep", "Yes", "No") == "Yes")
+ SetSleeping(40 SECONDS, voluntary = TRUE) //Short nap
/mob/living/verb/lay_down()
set name = "Rest"
diff --git a/code/modules/surgery/helpers.dm b/code/modules/surgery/helpers.dm
index d3feb46ebc1..1e030409d61 100644
--- a/code/modules/surgery/helpers.dm
+++ b/code/modules/surgery/helpers.dm
@@ -118,7 +118,13 @@
/proc/get_pain_modifier(mob/living/carbon/human/M) //returns modfier to make surgery harder if patient is conscious and feels pain
- if(M.stat) //stat=0 if CONSCIOUS, 1=UNCONSCIOUS and 2=DEAD. Operating on dead people is easy, too. Just sleeping won't work, though.
+ if(M.stat == DEAD) // Operating on dead people is easy
+ return 1
+ var/datum/status_effect/incapacitating/sleeping/S = M.IsSleeping()
+ if(M.stat == UNCONSCIOUS && !(S?.voluntary))
+ // Either unconscious due to something other than sleep,
+ // or "sleeping" due to being hard knocked out (N2O or similar), rather than just napping.
+ // Either way, not easily woken up.
return 1
if(HAS_TRAIT(M, TRAIT_NOPAIN))//if you don't feel pain, you can hold still
return 1
diff --git a/code/modules/surgery/surgery.dm b/code/modules/surgery/surgery.dm
index 1acef049678..57c0cb64285 100644
--- a/code/modules/surgery/surgery.dm
+++ b/code/modules/surgery/surgery.dm
@@ -114,7 +114,16 @@
if(!ispath(surgery.steps[surgery.status], /datum/surgery_step/robotics))//Repairing robotic limbs doesn't hurt, and neither does cutting someone out of a rig
if(ishuman(target))
var/mob/living/carbon/human/H = target //typecast to human
- prob_chance *= get_pain_modifier(H)//operating on conscious people is hard.
+ var/pain_mod = get_pain_modifier(H)
+ var/datum/status_effect/incapacitating/sleeping/S = H.IsSleeping()
+ if(S?.voluntary)
+ H.SetSleeping(0) // wake up people who are napping through the surgery
+ if(pain_mod < 0.95)
+ to_chat(H, "The surgery on your [target_zone] is agonizingly painful, and wrecks you out of your shallow slumber!")
+ else
+ // Still wake people up, but they shouldn't be as alarmed.
+ to_chat(H, "The surgery being performed on your [target_zone] wakes you up.")
+ prob_chance *= pain_mod //operating on conscious people is hard.
if(prob(prob_chance) || isrobot(user))
if(end_step(user, target, target_zone, tool, surgery))