diff --git a/code/_onclick/hud/screen_objects.dm b/code/_onclick/hud/screen_objects.dm
index 1eb555fceaf7..ed30ddaaec96 100644
--- a/code/_onclick/hud/screen_objects.dm
+++ b/code/_onclick/hud/screen_objects.dm
@@ -59,9 +59,11 @@
/atom/movable/screen/action_button/attack_ghost(mob/dead/observer/user)
return
-/atom/movable/screen/action_button/clicked(mob/user)
+/atom/movable/screen/action_button/clicked(mob/user, list/mods)
if(!user || !source_action)
return TRUE
+ if(source_action.owner != user)
+ return TRUE
if(source_action.can_use_action())
source_action.action_activate()
@@ -97,7 +99,7 @@
icon_state = "hide"
var/hidden = 0
-/atom/movable/screen/action_button/hide_toggle/clicked(mob/user, mods)
+/atom/movable/screen/action_button/hide_toggle/clicked(mob/user, list/mods)
user.hud_used.action_buttons_hidden = !user.hud_used.action_buttons_hidden
hidden = user.hud_used.action_buttons_hidden
if(hidden)
@@ -107,7 +109,7 @@
name = "Hide Buttons"
icon_state = "hide"
user.update_action_buttons()
- return 1
+ return TRUE
/atom/movable/screen/action_button/ghost/minimap/get_button_screen_loc(button_number)
return "SOUTH:6,CENTER+1:24"
@@ -211,7 +213,7 @@
update_icon(user)
return 1
-/atom/movable/screen/clicked(mob/user)
+/atom/movable/screen/clicked(mob/user, list/mods)
if(!user)
return TRUE
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index ae86518a640f..ba84f1cca76d 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -188,37 +188,38 @@
clean_observe_target()
/// When the observer target gets a screen, our observer gets a screen minus some game screens we don't want the observer to touch
-/mob/dead/observer/proc/observe_target_screen_add(observe_target_mob_client, add_to_screen)
+/mob/dead/observer/proc/observe_target_screen_add(observe_target_mob_client, screen_add)
SIGNAL_HANDLER
- if(!client)
- return
-
- if(istype(add_to_screen, /atom/movable/screen/action_button))
- return
-
- if(istype(add_to_screen, /atom/movable/screen/fullscreen))
- return
+ var/static/list/excluded_types = typecacheof(list(
+ /atom/movable/screen/fullscreen,
+ /atom/movable/screen/click_catcher,
+ /atom/movable/screen/escape_menu,
+ /atom/movable/screen/buildmode,
+ /obj/effect/detector_blip,
+ ))
- if(istype(add_to_screen, /atom/movable/screen/click_catcher))
+ if(!client)
return
- if(istype(add_to_screen, /atom/movable/screen/escape_menu))
- return
+ // `screen_add` can sometimes be a list, so it's safest to just handle everything as one.
+ var/list/stuff_to_add = (islist(screen_add) ? screen_add : list(screen_add))
- if(istype(add_to_screen, /obj/effect/detector_blip))
- return
+ for(var/item in stuff_to_add)
+ // Ignore anything that's in `excluded_types`.
+ if(is_type_in_typecache(item, excluded_types))
+ continue
- client.add_to_screen(add_to_screen)
+ client.add_to_screen(screen_add)
/// When the observer target loses a screen, our observer loses it as well
-/mob/dead/observer/proc/observe_target_screen_remove(observe_target_mob_client, remove_from_screen)
+/mob/dead/observer/proc/observe_target_screen_remove(observe_target_mob_client, screen_remove)
SIGNAL_HANDLER
if(!client)
return
- client.remove_from_screen(remove_from_screen)
+ client.remove_from_screen(screen_remove)
/// When the observe target ghosts our observer disconnect from their screen updates
/mob/dead/observer/proc/observe_target_ghosting(mob/observer_target_mob)
@@ -256,48 +257,25 @@
ManualFollow(target)
reset_perspective()
- if(!ishuman(target) || !client.prefs?.auto_observe)
+ if(!iscarbon(target) || !client.prefs?.auto_observe)
return
- var/mob/living/carbon/human/human_target = target
-
- client.eye = human_target
- observe_target_mob = human_target
- RegisterSignal(observe_target_mob, COMSIG_PARENT_QDELETING, PROC_REF(clean_observe_target))
- RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(observer_move_react))
-
- if(!human_target.hud_used)
+ var/mob/living/carbon/carbon_target = target
+ if(!carbon_target.hud_used)
return
client.clear_screen()
- LAZYINITLIST(human_target.observers)
- human_target.observers |= src
- human_target.hud_used.show_hud(human_target.hud_used.hud_version, src)
-
- var/list/target_contents = human_target.get_contents()
-
- //Handles any currently open storage containers the target is looking in when we observe
- for(var/obj/item/storage/checked_storage in target_contents)
- if(!(human_target in checked_storage.content_watchers))
- continue
-
- client.add_to_screen(checked_storage.closer)
- client.add_to_screen(checked_storage.contents)
-
- if(checked_storage.storage_slots)
- client.add_to_screen(checked_storage.boxes)
- else
- client.add_to_screen(checked_storage.storage_start)
- client.add_to_screen(checked_storage.storage_continue)
- client.add_to_screen(checked_storage.storage_end)
-
- break
+ client.eye = carbon_target
+ observe_target_mob = carbon_target
+ carbon_target.auto_observed(src)
+ RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(observer_move_react))
+ RegisterSignal(observe_target_mob, COMSIG_PARENT_QDELETING, PROC_REF(clean_observe_target))
RegisterSignal(observe_target_mob, COMSIG_MOB_GHOSTIZE, PROC_REF(observe_target_ghosting))
RegisterSignal(observe_target_mob, COMSIG_MOB_NEW_MIND, PROC_REF(observe_target_new_mind))
RegisterSignal(observe_target_mob, COMSIG_MOB_LOGIN, PROC_REF(observe_target_login))
- if(human_target.client)
- observe_target_client = human_target.client
+ if(observe_target_mob.client)
+ observe_target_client = observe_target_mob.client
RegisterSignal(observe_target_client, COMSIG_CLIENT_SCREEN_ADD, PROC_REF(observe_target_screen_add))
RegisterSignal(observe_target_client, COMSIG_CLIENT_SCREEN_REMOVE, PROC_REF(observe_target_screen_remove))
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index a5ef1231a140..c56ccafc85ab 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -416,6 +416,25 @@
"}
show_browser(user, dat, name, "mob[name]")
+/**
+ * Called by [/mob/dead/observer/proc/do_observe] when a carbon mob is observed by a ghost with [/datum/preferences/var/auto_observe] enabled.
+ *
+ * Any HUD changes past this point are handled by [/mob/dead/observer/proc/observe_target_screen_add]
+ * and [/mob/dead/observer/proc/observe_target_screen_remove].
+ *
+ * Override on subtype mobs if they have any extra HUD elements/behaviour.
+ */
+/mob/living/carbon/proc/auto_observed(mob/dead/observer/observer)
+ SHOULD_CALL_PARENT(TRUE)
+
+ LAZYINITLIST(observers)
+ observers |= observer
+ hud_used.show_hud(hud_used.hud_version, observer)
+
+ for(var/datum/action/action as anything in actions)
+ // Add the action's button (not the action itself) to the observer's screen.
+ observer.client.add_to_screen(action.button)
+
//generates realistic-ish pulse output based on preset levels
/mob/living/carbon/proc/get_pulse(method) //method 0 is for hands, 1 is for machines, more accurate
var/temp = 0 //see setup.dm:694
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 4c62361ec52e..3ebd199b08d9 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -311,6 +311,29 @@
"}
show_browser(user, dat, name, "mob[name]")
+/**
+ * Handles any storage containers that the human is looking inside when auto-observed.
+ */
+/mob/living/carbon/human/auto_observed(mob/dead/observer/observer)
+ . = ..()
+
+ // If `src` doesn't have an inventory open.
+ if(!s_active)
+ return
+
+ // Add the storage interface to `observer`'s screen.
+ observer.client.add_to_screen(s_active.closer)
+ observer.client.add_to_screen(s_active.contents)
+
+ // If the storage has a set number of item slots.
+ if(s_active.storage_slots)
+ observer.client.add_to_screen(s_active.boxes)
+ // If the storage instead has a maximum combined item 'weight'.
+ else
+ observer.client.add_to_screen(s_active.storage_start)
+ observer.client.add_to_screen(s_active.storage_continue)
+ observer.client.add_to_screen(s_active.storage_end)
+
// called when something steps onto a human
// this handles mulebots and vehicles
/mob/living/carbon/human/Crossed(atom/movable/AM)
diff --git a/strings/metatips.txt b/strings/metatips.txt
index a28c90239593..00bb827de69f 100644
--- a/strings/metatips.txt
+++ b/strings/metatips.txt
@@ -12,6 +12,7 @@ As a mentor, you can become the imaginary friend of a new player to teach them!
You shouldn't ignore what your allies are up to. Sometimes they can be organizing a flank in hivemind/radio, sometimes they can be walking up behind you with a slug-loaded shotgun. Either way, it pays to be alert to what they're doing, as much to as what the enemies are.
The Wiki (https://cm-ss13.com/wiki) is a very useful repository of information about the game, such as weapons, equipment, xenomorph castes and their strains. It may not be fully up to date much of the time, but the basics are usually accurate.
As an observer, you may see how much remaining hijack time is left in the status panel.
+As an observer, you can quickly follow someone by ctrl-clicking on their sprite.
You can always AdminHelp with the F1 key to question a member of staff regarding rules or game bugs.
As ghost you are given extra tools for spectating the round: you can jump and follow specific players, get notifications about CAS and OB strikes, can see all health bars, and such.
You can press ESC key to bring up the game pause menu. It allows you change settings, AdminHelp and MentorHelp, and even access the Web Maps of game by clicking at top right.