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}