diff --git a/code/modules/mob/language/language.dm b/code/modules/mob/language/language.dm
index bf5b51a1b1..a69e08df58 100644
--- a/code/modules/mob/language/language.dm
+++ b/code/modules/mob/language/language.dm
@@ -17,7 +17,7 @@
var/flags = 0 // Various language flags.
var/native // If set, non-native speakers will have trouble speaking.
var/list/syllables // Used when scrambling text for a non-speaker.
- var/list/space_chance = 55 // Likelihood of getting a space in the random scramble string.
+ var/list/space_chance = 55 // Likelihood of getting a space in the random scramble string
/datum/language/proc/get_random_name(var/gender, name_count=2, syllable_count=4)
if(!syllables || !syllables.len)
@@ -101,11 +101,22 @@
log_say("[key_name(speaker)] : ([name]) [message]")
if(!speaker_mask) speaker_mask = speaker.name
- var/msg = "[name], [speaker_mask] [format_message(message, get_spoken_verb(message))]"
-
for(var/mob/player in player_list)
- if(istype(player,/mob/dead) || ((src in player.languages) && check_special_condition(player)))
- player << msg
+ player.hear_broadcast(src, speaker, speaker_mask, format_message(message, get_spoken_verb(message)))
+
+/mob/proc/hear_broadcast(var/datum/language/language, var/speaker, var/message)
+ if((language in languages) && language.check_special_condition(src))
+ var/msg = "[language.name], [speaker] [message]"
+ src << msg
+
+/mob/new_player/hear_broadcast()
+ return
+
+/mob/dead/observer/hear_broadcast(var/datum/language/language, var/mob/speaker, var/speaker_name, var/message)
+ if(speaker.name == speaker_name || antagHUD)
+ src << "[language.name], [speaker_name] ([ghost_follow_link(speaker, src)]) [message]"
+ else
+ src << "[language.name], [speaker_name] [message]"
/datum/language/proc/check_special_condition(var/mob/other)
return 1
diff --git a/code/modules/mob/language/synthetic.dm b/code/modules/mob/language/synthetic.dm
index 3355a1e5c8..501a8a61f0 100644
--- a/code/modules/mob/language/synthetic.dm
+++ b/code/modules/mob/language/synthetic.dm
@@ -22,7 +22,7 @@
for (var/mob/M in dead_mob_list)
if(!istype(M,/mob/new_player) && !istype(M,/mob/living/carbon/brain)) //No meta-evesdropping
- M.show_message("[message_start] [message_body]", 2)
+ M.show_message("[message_start] ([ghost_follow_link(speaker, M)]) [message_body]", 2)
for (var/mob/living/S in living_mob_list)
diff --git a/html/changelogs/PsiOmegaDelta-HiveTracking.yml b/html/changelogs/PsiOmegaDelta-HiveTracking.yml
new file mode 100644
index 0000000000..c42ffec9c3
--- /dev/null
+++ b/html/changelogs/PsiOmegaDelta-HiveTracking.yml
@@ -0,0 +1,6 @@
+author: PsiOmegaDelta
+delete-after: True
+changes:
+ - rscadd: "Observers can now follow both the AI and its eye upon speech."
+ - rscadd: "Observers can now follow both observers and their body, if they ever had one, upon speech."
+ - rscadd: "Observers can now follow hivemind speakers if the speaker is not using an alias or antagHUD is enabled."