diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index 952a53a0b6b9a..0d59c04afc61f 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -432,6 +432,13 @@ GLOBAL_LIST_INIT(available_random_trauma_list, list(
#define DOING_INTERACTION_WITH_TARGET(user, target) (LAZYACCESS(user.do_afters, target))
#define DOING_INTERACTION_WITH_TARGET_LIMIT(user, target, max_interaction_count) ((LAZYACCESS(user.do_afters, target) || 0) >= max_interaction_count)
+// recent examine defines
+/// How long it takes for an examined atom to be removed from recent_examines. Should be the max of the below time windows
+#define RECENT_EXAMINE_MAX_WINDOW (2 SECONDS)
+/// If you examine the same atom twice in this timeframe, we call examine_more() instead of examine()
+#define EXAMINE_MORE_WINDOW (1 SECONDS)
+
+
#define SILENCE_RANGED_MESSAGE (1<<0)
// Mob Playability Set By Admin Or Ghosting
diff --git a/code/datums/mind.dm b/code/datums/mind.dm
index d05a0a0b82427..436625a26f16e 100644
--- a/code/datums/mind.dm
+++ b/code/datums/mind.dm
@@ -169,6 +169,8 @@
RegisterSignal(new_character, COMSIG_MOB_DEATH, PROC_REF(set_death_time))
if(active || force_key_move)
new_character.key = key //now transfer the key to link the client to our new body
+ if(new_character.client)
+ LAZYCLEARLIST(new_character.client.recent_examines)
SEND_SIGNAL(src, COMSIG_MIND_TRANSFER_TO, old_current, new_character)
// Update SSD indicators
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index 20dfbd188c3ea..bb360a1823e21 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -113,6 +113,9 @@
var/datum/view_data/view_size
+ ///A lazy list of atoms we've examined in the last RECENT_EXAMINE_MAX_WINDOW (default 2) seconds, so that we will call [/atom/proc/examine_more] instead of [/atom/proc/examine] on them when examining
+ var/list/recent_examines
+
// List of all asset filenames sent to this client by the asset cache, along with their assoicated md5s
var/list/sent_assets = list()
/// List of all completed blocking send jobs awaiting acknowledgement by send_asset
diff --git a/code/modules/clothing/masks/_masks.dm b/code/modules/clothing/masks/_masks.dm
index 50dc44d8e44f8..5fb00c8bfbd4c 100644
--- a/code/modules/clothing/masks/_masks.dm
+++ b/code/modules/clothing/masks/_masks.dm
@@ -13,9 +13,9 @@
var/unique_death /// The unique sound effect of dying while wearing this
/obj/item/clothing/mask/attack_self(mob/user)
- if(CHECK_BITFIELD(clothing_flags, VOICEBOX_TOGGLABLE))
- TOGGLE_BITFIELD(clothing_flags, VOICEBOX_DISABLED)
- var/status = !CHECK_BITFIELD(clothing_flags, VOICEBOX_DISABLED)
+ if((clothing_flags & VOICEBOX_TOGGLABLE))
+ clothing_flags ^= (VOICEBOX_DISABLED)
+ var/status = !(clothing_flags & VOICEBOX_DISABLED)
to_chat(user, "You turn the voice box in [src] [status ? "on" : "off"].")
/obj/item/clothing/mask/equipped(mob/M, slot)
diff --git a/code/modules/clothing/masks/breath.dm b/code/modules/clothing/masks/breath.dm
index af9f9c2486aa0..6d6df31836677 100644
--- a/code/modules/clothing/masks/breath.dm
+++ b/code/modules/clothing/masks/breath.dm
@@ -14,7 +14,6 @@
visor_flags_cover = MASKCOVERSMOUTH
resistance_flags = NONE
-
/datum/armor/mask_breath
bio = 50
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index ffcb0154282b7..da21b1b7eff07 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -1696,8 +1696,8 @@
/// Proc to append behavior to the condition of being floored. Called when the condition starts.
/mob/living/proc/on_floored_start()
if(body_position == STANDING_UP) //force them on the ground
- set_lying_angle(pick(90, 270))
set_body_position(LYING_DOWN)
+ set_lying_angle(pick(90, 270))
on_fall()
diff --git a/code/modules/mob/mob.dm b/code/modules/mob/mob.dm
index e6e8fd5ce3c38..528335a21c3c3 100644
--- a/code/modules/mob/mob.dm
+++ b/code/modules/mob/mob.dm
@@ -530,22 +530,40 @@
* [this byond forum post](https://secure.byond.com/forum/?post=1326139&page=2#comment8198716)
* for why this isn't atom/verb/examine()
*/
-/mob/verb/examinate(atom/A as mob|obj|turf in view()) //It used to be oview(12), but I can't really say why
+/mob/verb/examinate(atom/examinify as mob|obj|turf in view()) //It used to be oview(12), but I can't really say why
set name = "Examine"
set category = "IC"
- if(isturf(A) && !(sight & SEE_TURFS) && !(A in view(client ? client.view : world.view, src)))
+ if(isturf(examinify) && !(sight & SEE_TURFS) && !(examinify in view(client ? client.view : world.view, src)))
// shift-click catcher may issue examinate() calls for out-of-sight turfs
return
- if(is_blind(src) && !blind_examine_check(A))
+ if(is_blind(src) && !blind_examine_check(examinify))
return
- face_atom(A)
- var/list/result = A.examine(src)
+ face_atom(examinify)
+ var/list/result
+ if(client)
+ LAZYINITLIST(client.recent_examines)
+ var/ref_to_atom = ref(examinify)
+ var/examine_time = client.recent_examines[ref_to_atom]
+ if(examine_time && (world.time - examine_time < EXAMINE_MORE_WINDOW))
+ result = examinify.examine_more(src)
+ if(!length(result))
+ result += "You examine [examinify] closer, but find nothing of interest..."
+ else
+ result = examinify.examine(src)
+ client.recent_examines[ref_to_atom] = world.time // set to when we last normal examine'd them
+ addtimer(CALLBACK(src, .proc/clear_from_recent_examines, ref_to_atom), RECENT_EXAMINE_MAX_WINDOW)
+ else
+ result = examinify.examine(src) // if a tree is examined but no client is there to see it, did the tree ever really exist?
- to_chat(src, EXAMINE_BLOCK(jointext(result, "\n")))
- SEND_SIGNAL(src, COMSIG_MOB_EXAMINATE, A)
+ if(result.len)
+ for(var/i in 1 to (length(result) - 1))
+ result[i] += "\n"
+
+ to_chat(src, EXAMINE_BLOCK("[result.Join()]"))
+ SEND_SIGNAL(src, COMSIG_MOB_EXAMINATE, examinify)
/mob/proc/blind_examine_check(atom/examined_thing)
return TRUE
@@ -595,6 +613,12 @@
return TRUE
+/mob/proc/clear_from_recent_examines(ref_to_clear)
+ SIGNAL_HANDLER
+ if(!client)
+ return
+ LAZYREMOVE(client.recent_examines, ref_to_clear)
+
/**
* Called by using Activate Held Object with an empty hand/limb
*