diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs
index 1f4bfe2715..7b874e54da 100644
--- a/Content.Server/Chat/Systems/ChatSystem.cs
+++ b/Content.Server/Chat/Systems/ChatSystem.cs
@@ -13,6 +13,8 @@ using Content.Shared.CCVar;
using Content.Shared.Chat;
using Content.Shared.Database;
using Content.Shared.IdentityManagement;
+using Content.Shared.Interaction;
+using Content.Shared.Inventory;
using Content.Shared.Mobs.Systems;
using Content.Shared.Radio;
using Robust.Server.GameObjects;
@@ -49,9 +51,11 @@ public sealed partial class ChatSystem : SharedChatSystem
[Dependency] private readonly StationSystem _stationSystem = default!;
[Dependency] private readonly MobStateSystem _mobStateSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
+ [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
public const int VoiceRange = 10; // how far voice goes in world units
- public const int WhisperRange = 2; // how far whisper goes in world units
+ public const int WhisperClearRange = 2; // how far whisper goes while still being understandable, in world units
+ public const int WhisperMuffledRange = 5; // how far whisper goes at all, in world units
public const string DefaultAnnouncementSound = "/Audio/Announcements/announce.ogg";
private bool _loocEnabled = true;
@@ -373,7 +377,9 @@ public sealed partial class ChatSystem : SharedChatSystem
var obfuscatedMessage = ObfuscateMessageReadability(message, 0.2f);
- // get the entity's apparent name (if no override provided).
+ // get the entity's name by visual identity (if no override provided).
+ string nameIdentity = FormattedMessage.EscapeText(nameOverride ?? Identity.Name(source, EntityManager));
+ // get the entity's name by voice (if no override provided).
string name;
if (nameOverride != null)
{
@@ -391,23 +397,33 @@ public sealed partial class ChatSystem : SharedChatSystem
var wrappedMessage = Loc.GetString("chat-manager-entity-whisper-wrap-message",
("entityName", name), ("message", FormattedMessage.EscapeText(message)));
-
var wrappedobfuscatedMessage = Loc.GetString("chat-manager-entity-whisper-wrap-message",
- ("entityName", name), ("message", FormattedMessage.EscapeText(obfuscatedMessage)));
+ ("entityName", nameIdentity), ("message", FormattedMessage.EscapeText(obfuscatedMessage)));
+
+ var wrappedUnknownMessage = Loc.GetString("chat-manager-entity-whisper-unknown-wrap-message",
+ ("message", FormattedMessage.EscapeText(obfuscatedMessage)));
- foreach (var (session, data) in GetRecipients(source, VoiceRange))
+ foreach (var (session, data) in GetRecipients(source, WhisperMuffledRange))
{
+ EntityUid listener;
+
if (session.AttachedEntity is not { Valid: true } playerEntity)
continue;
+ listener = session.AttachedEntity.Value;
if (MessageRangeCheck(session, data, range) != MessageRangeCheckResult.Full)
continue; // Won't get logged to chat, and ghosts are too far away to see the pop-up, so we just won't send it to them.
- if (data.Range <= WhisperRange)
+ if (data.Range <= WhisperClearRange)
_chatManager.ChatMessageToOne(ChatChannel.Whisper, message, wrappedMessage, source, false, session.ConnectedClient);
- else
+ //If listener is too far, they only hear fragments of the message
+ //Collisiongroup.Opaque is not ideal for this use. Preferably, there should be a check specifically with "Can Ent1 see Ent2" in mind
+ else if (_interactionSystem.InRangeUnobstructed(source, listener, WhisperMuffledRange, Shared.Physics.CollisionGroup.Opaque)) //Shared.Physics.CollisionGroup.Opaque
_chatManager.ChatMessageToOne(ChatChannel.Whisper, obfuscatedMessage, wrappedobfuscatedMessage, source, false, session.ConnectedClient);
+ //If listener is too far and has no line of sight, they can't identify the whisperer's identity
+ else
+ _chatManager.ChatMessageToOne(ChatChannel.Whisper, obfuscatedMessage, wrappedUnknownMessage, source, false, session.ConnectedClient);
}
_replay.RecordServerMessage(new ChatMessage(ChatChannel.Whisper, message, wrappedMessage, source, MessageRangeHideChatForReplay(range)));
@@ -647,7 +663,7 @@ public sealed partial class ChatSystem : SharedChatSystem
///
/// Returns list of players and ranges for all players withing some range. Also returns observers with a range of -1.
///
- private Dictionary GetRecipients(EntityUid source, float voiceRange)
+ private Dictionary GetRecipients(EntityUid source, float voiceGetRange)
{
// TODO proper speech occlusion
@@ -672,7 +688,7 @@ public sealed partial class ChatSystem : SharedChatSystem
var observer = ghosts.HasComponent(playerEntity);
// even if they are an observer, in some situations we still need the range
- if (sourceCoords.TryDistance(EntityManager, transformEntity.Coordinates, out var distance) && distance < voiceRange)
+ if (sourceCoords.TryDistance(EntityManager, transformEntity.Coordinates, out var distance) && distance < voiceGetRange)
{
recipients.Add(player, new ICChatRecipientData(distance, observer));
continue;
@@ -682,7 +698,7 @@ public sealed partial class ChatSystem : SharedChatSystem
recipients.Add(player, new ICChatRecipientData(-1, true));
}
- RaiseLocalEvent(new ExpandICChatRecipientstEvent(source, voiceRange, recipients));
+ RaiseLocalEvent(new ExpandICChatRecipientstEvent(source, voiceGetRange, recipients));
return recipients;
}
diff --git a/Content.Server/Speech/EntitySystems/ListeningSystem.cs b/Content.Server/Speech/EntitySystems/ListeningSystem.cs
index d0a7b41790..ea3569e055 100644
--- a/Content.Server/Speech/EntitySystems/ListeningSystem.cs
+++ b/Content.Server/Speech/EntitySystems/ListeningSystem.cs
@@ -53,7 +53,7 @@ public sealed class ListeningSystem : EntitySystem
continue;
}
- if (obfuscatedEv != null && distance > ChatSystem.WhisperRange)
+ if (obfuscatedEv != null && distance > ChatSystem.WhisperClearRange)
RaiseLocalEvent(listenerUid, obfuscatedEv);
else
RaiseLocalEvent(listenerUid, ev);
diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl
index 29efc61ebb..e3d6e7db3b 100644
--- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl
+++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl
@@ -21,6 +21,7 @@ chat-manager-sender-announcement-wrap-message = {$sender} Announcement:
{$message}
chat-manager-entity-say-wrap-message = {$entityName} says, "{$message}"
chat-manager-entity-whisper-wrap-message = {$entityName} whispers, "{$message}"
+chat-manager-entity-whisper-unknown-wrap-message = Someone whispers, "{$message}"
chat-manager-entity-me-wrap-message = {$entityName} {$message}
chat-manager-entity-looc-wrap-message = LOOC: {$entityName}: {$message}
chat-manager-send-ooc-wrap-message = OOC: {$playerName}: {$message}