diff --git a/Content.Client/Chat/UI/SpeechBubble.cs b/Content.Client/Chat/UI/SpeechBubble.cs
index 91e8e5a90f..82eccbcec8 100644
--- a/Content.Client/Chat/UI/SpeechBubble.cs
+++ b/Content.Client/Chat/UI/SpeechBubble.cs
@@ -182,20 +182,9 @@ namespace Content.Client.Chat.UI
return msg;
}
- protected string ExtractSpeechSubstring(ChatMessage message, string tag)
- {
- var rawmsg = message.WrappedMessage;
- var tagStart = rawmsg.IndexOf($"[{tag}]");
- var tagEnd = rawmsg.IndexOf($"[/{tag}]");
- if (tagStart < 0 || tagEnd < 0) //the above return -1 if the tag's not found, which in turn will cause the below to throw an exception. a blank speech bubble is far more noticeably broken than the bubble not appearing at all -bhijn
- return "";
- tagStart += tag.Length + 2;
- return rawmsg.Substring(tagStart, tagEnd - tagStart);
- }
-
protected FormattedMessage ExtractAndFormatSpeechSubstring(ChatMessage message, string tag, Color? fontColor = null)
{
- return FormatSpeech(ExtractSpeechSubstring(message, tag), fontColor);
+ return FormatSpeech(SharedChatSystem.GetStringInsideTag(message, tag), fontColor);
}
}
diff --git a/Content.Client/Options/UI/Tabs/MiscTab.xaml b/Content.Client/Options/UI/Tabs/MiscTab.xaml
index db176db456..fc94ab7b0a 100644
--- a/Content.Client/Options/UI/Tabs/MiscTab.xaml
+++ b/Content.Client/Options/UI/Tabs/MiscTab.xaml
@@ -22,6 +22,7 @@
FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
StyleClasses="LabelKeyText"/>
+
]
+ private const string ChatNamePalette = "ChatNames";
+ private string[] _chatNameColors = default!;
+ private bool _chatNameColorsEnabled;
+
private ISawmill _sawmill = default!;
public static readonly Dictionary PrefixToChannel = new()
@@ -168,6 +179,8 @@ public sealed class ChatUIController : UIController
_net.RegisterNetMessage(OnChatMessage);
_net.RegisterNetMessage(OnDeleteChatMessagesBy);
SubscribeNetworkEvent(OnDamageForceSay);
+ _cfg.OnValueChanged(CCVars.ChatEnableColorName, (value) => { _chatNameColorsEnabled = value; });
+ _chatNameColorsEnabled = _cfg.GetCVar(CCVars.ChatEnableColorName);
_speechBubbleRoot = new LayoutContainer();
@@ -212,6 +225,13 @@ public sealed class ChatUIController : UIController
var gameplayStateLoad = UIManager.GetUIController();
gameplayStateLoad.OnScreenLoad += OnScreenLoad;
gameplayStateLoad.OnScreenUnload += OnScreenUnload;
+
+ var nameColors = _prototypeManager.Index(ChatNamePalette).Colors.Values.ToArray();
+ _chatNameColors = new string[nameColors.Length];
+ for (var i = 0; i < nameColors.Length; i++)
+ {
+ _chatNameColors[i] = nameColors[i].ToHex();
+ }
}
public void OnScreenLoad()
@@ -757,6 +777,14 @@ public sealed class ChatUIController : UIController
public void ProcessChatMessage(ChatMessage msg, bool speechBubble = true)
{
+ // color the name unless it's something like "the old man"
+ if ((msg.Channel == ChatChannel.Local || msg.Channel == ChatChannel.Whisper) && _chatNameColorsEnabled)
+ {
+ var grammar = _ent.GetComponentOrNull(_ent.GetEntity(msg.SenderEntity));
+ if (grammar != null && grammar.ProperNoun == true)
+ msg.WrappedMessage = SharedChatSystem.InjectTagInsideTag(msg, "Name", "color", GetNameColor(SharedChatSystem.GetStringInsideTag(msg, "Name")));
+ }
+
// Log all incoming chat to repopulate when filter is un-toggled
if (!msg.HideChat)
{
@@ -845,6 +873,17 @@ public sealed class ChatUIController : UIController
}
}
+ ///
+ /// Returns the chat name color for a mob
+ ///
+ /// Name of the mob
+ /// Hex value of the color
+ public string GetNameColor(string name)
+ {
+ var colorIdx = Math.Abs(name.GetHashCode() % _chatNameColors.Length);
+ return _chatNameColors[colorIdx];
+ }
+
private readonly record struct SpeechBubbleData(ChatMessage Message, SpeechBubble.SpeechType Type);
private sealed class SpeechBubbleQueueData
diff --git a/Content.Server/Chat/Systems/ChatSystem.cs b/Content.Server/Chat/Systems/ChatSystem.cs
index 649607772a..13cc6df674 100644
--- a/Content.Server/Chat/Systems/ChatSystem.cs
+++ b/Content.Server/Chat/Systems/ChatSystem.cs
@@ -13,7 +13,6 @@ using Content.Shared.ActionBlocker;
using Content.Shared.CCVar;
using Content.Shared.Chat;
using Content.Shared.Database;
-using Content.Shared.Decals;
using Content.Shared.Ghost;
using Content.Shared.Humanoid;
using Content.Shared.IdentityManagement;
@@ -27,7 +26,6 @@ using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
-using Robust.Shared.GameObjects.Components.Localization;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
@@ -70,10 +68,6 @@ public sealed partial class ChatSystem : SharedChatSystem
private bool _critLoocEnabled;
private readonly bool _adminLoocEnabled = true;
- [ValidatePrototypeId]
- private const string ChatNamePalette = "ChatNames";
- private string[] _chatNameColors = default!;
-
public override void Initialize()
{
base.Initialize();
@@ -83,13 +77,6 @@ public sealed partial class ChatSystem : SharedChatSystem
_configurationManager.OnValueChanged(CCVars.CritLoocEnabled, OnCritLoocEnabledChanged, true);
SubscribeLocalEvent(OnGameChange);
-
- var nameColors = _prototypeManager.Index(ChatNamePalette).Colors.Values.ToArray();
- _chatNameColors = new string[nameColors.Length];
- for (var i = 0; i < nameColors.Length; i++)
- {
- _chatNameColors[i] = nameColors[i].ToHex();
- }
}
public override void Shutdown()
@@ -424,13 +411,8 @@ public sealed partial class ChatSystem : SharedChatSystem
name = FormattedMessage.EscapeText(name);
- // color the name unless it's something like "the old man"
- string coloredName = name;
- if (!TryComp(source, out var grammar) || grammar.ProperNoun == true)
- coloredName = $"[color={GetNameColor(name)}]{name}[/color]";
-
var wrappedMessage = Loc.GetString(speech.Bold ? "chat-manager-entity-say-bold-wrap-message" : "chat-manager-entity-say-wrap-message",
- ("entityName", coloredName),
+ ("entityName", name),
("verb", Loc.GetString(_random.Pick(speech.SpeechVerbStrings))),
("fontType", speech.FontId),
("fontSize", speech.FontSize),
@@ -499,10 +481,6 @@ public sealed partial class ChatSystem : SharedChatSystem
}
name = FormattedMessage.EscapeText(name);
- // color the name unless it's something like "the old man"
- if (!TryComp(source, out var grammar) || grammar.ProperNoun == true)
- name = $"[color={GetNameColor(name)}]{name}[/color]";
-
var wrappedMessage = Loc.GetString("chat-manager-entity-whisper-wrap-message",
("entityName", name), ("message", FormattedMessage.EscapeText(message)));
@@ -643,17 +621,6 @@ public sealed partial class ChatSystem : SharedChatSystem
#region Utility
- ///
- /// Returns the chat name color for a mob
- ///
- /// Name of the mob
- /// Hex value of the color
- public string GetNameColor(string name)
- {
- var colorIdx = Math.Abs(name.GetHashCode() % _chatNameColors.Length);
- return _chatNameColors[colorIdx];
- }
-
private enum MessageRangeCheckResult
{
Disallowed,
diff --git a/Content.Shared/CCVar/CCVars.cs b/Content.Shared/CCVar/CCVars.cs
index a6cb084c9e..aac3a17b6f 100644
--- a/Content.Shared/CCVar/CCVars.cs
+++ b/Content.Shared/CCVar/CCVars.cs
@@ -1564,6 +1564,9 @@ namespace Content.Shared.CCVar
public static readonly CVarDef ReducedMotion =
CVarDef.Create("accessibility.reduced_motion", false, CVar.CLIENTONLY | CVar.ARCHIVE);
+ public static readonly CVarDef ChatEnableColorName =
+ CVarDef.Create("accessibility.enable_color_name", true, CVar.CLIENTONLY | CVar.ARCHIVE, "Toggles displaying names with individual colors.");
+
///
/// Screen shake intensity slider, controlling the intensity of the CameraRecoilSystem.
/// Goes from 0 (no recoil at all) to 1 (regular amounts of recoil)
diff --git a/Content.Shared/Chat/SharedChatSystem.cs b/Content.Shared/Chat/SharedChatSystem.cs
index 931bfb37fc..e61d93efae 100644
--- a/Content.Shared/Chat/SharedChatSystem.cs
+++ b/Content.Shared/Chat/SharedChatSystem.cs
@@ -214,4 +214,31 @@ public abstract class SharedChatSystem : EntitySystem
return trimmed;
}
+
+ public static string InjectTagInsideTag(ChatMessage message, string outerTag, string innerTag, string? tagParameter)
+ {
+ var rawmsg = message.WrappedMessage;
+ var tagStart = rawmsg.IndexOf($"[{outerTag}]");
+ var tagEnd = rawmsg.IndexOf($"[/{outerTag}]");
+ if (tagStart < 0 || tagEnd < 0) //If the outer tag is not found, the injection is not performed
+ return rawmsg;
+ tagStart += outerTag.Length + 2;
+
+ string innerTagProcessed = tagParameter != null ? $"[{innerTag}={tagParameter}]" : $"[{innerTag}]";
+
+ rawmsg = rawmsg.Insert(tagEnd, $"[/{innerTag}]");
+ rawmsg = rawmsg.Insert(tagStart, innerTagProcessed);
+
+ return rawmsg;
+ }
+ public static string GetStringInsideTag(ChatMessage message, string tag)
+ {
+ var rawmsg = message.WrappedMessage;
+ var tagStart = rawmsg.IndexOf($"[{tag}]");
+ var tagEnd = rawmsg.IndexOf($"[/{tag}]");
+ if (tagStart < 0 || tagEnd < 0)
+ return "";
+ tagStart += tag.Length + 2;
+ return rawmsg.Substring(tagStart, tagEnd - tagStart);
+ }
}
diff --git a/Resources/Locale/en-US/chat/managers/chat-manager.ftl b/Resources/Locale/en-US/chat/managers/chat-manager.ftl
index 6bbb3cd449..a165a02f9d 100644
--- a/Resources/Locale/en-US/chat/managers/chat-manager.ftl
+++ b/Resources/Locale/en-US/chat/managers/chat-manager.ftl
@@ -21,10 +21,10 @@ chat-manager-whisper-headset-on-message = You can't whisper on the radio!
chat-manager-server-wrap-message = [bold]{$message}[/bold]
chat-manager-sender-announcement-wrap-message = [font size=14][bold]{$sender} Announcement:[/font][font size=12]
{$message}[/bold][/font]
-chat-manager-entity-say-wrap-message = [BubbleHeader][bold]{$entityName}[/bold][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]"[BubbleContent]{$message}[/BubbleContent]"[/font]
-chat-manager-entity-say-bold-wrap-message = [BubbleHeader][bold]{$entityName}[/bold][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]"[BubbleContent][bold]{$message}[/bold][/BubbleContent]"[/font]
+chat-manager-entity-say-wrap-message = [BubbleHeader][bold][Name]{$entityName}[/Name][/bold][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]"[BubbleContent]{$message}[/BubbleContent]"[/font]
+chat-manager-entity-say-bold-wrap-message = [BubbleHeader][bold][Name]{$entityName}[/Name][/bold][/BubbleHeader] {$verb}, [font={$fontType} size={$fontSize}]"[BubbleContent][bold]{$message}[/bold][/BubbleContent]"[/font]
-chat-manager-entity-whisper-wrap-message = [font size=11][italic][BubbleHeader]{$entityName}[/BubbleHeader] whispers,"[BubbleContent]{$message}[/BubbleContent]"[/italic][/font]
+chat-manager-entity-whisper-wrap-message = [font size=11][italic][BubbleHeader][Name]{$entityName}[/Name][/BubbleHeader] whispers,"[BubbleContent]{$message}[/BubbleContent]"[/italic][/font]
chat-manager-entity-whisper-unknown-wrap-message = [font size=11][italic][BubbleHeader]Someone[/BubbleHeader] whispers, "[BubbleContent]{$message}[/BubbleContent]"[/italic][/font]
# THE() is not used here because the entity and its name can technically be disconnected if a nameOverride is passed...
diff --git a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl
index 4dd0ea9eb1..b8253c6929 100644
--- a/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl
+++ b/Resources/Locale/en-US/escape-menu/ui/options-menu.ftl
@@ -45,6 +45,7 @@ ui-options-opaque-storage-window = Opaque storage window
ui-options-show-looc-on-head = Show LOOC chat above characters head
ui-options-fancy-speech = Show names in speech bubbles
ui-options-fancy-name-background = Add background to speech bubble names
+ui-options-enable-color-name = Add colors to character names
ui-options-reduced-motion = Reduce motion of visual effects
ui-options-screen-shake-intensity = Screen shake intensity
ui-options-screen-shake-percent = { TOSTRING($intensity, "P0") }