]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Add option for character name colors in chat & move coloration to clientside (#24625)
authorSlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com>
Sun, 11 Feb 2024 06:38:55 +0000 (07:38 +0100)
committerGitHub <noreply@github.com>
Sun, 11 Feb 2024 06:38:55 +0000 (22:38 -0800)
* Adds option to disable character names in chat/speechbubbles

* Moved the coloring of names to clientside

* Move string functions to SharedChatSystem to avoid duplicate code in SpeechBubble.cs

* Changed to be put under Accessibility section

* Cache CVar

Content.Client/Chat/UI/SpeechBubble.cs
Content.Client/Options/UI/Tabs/MiscTab.xaml
Content.Client/Options/UI/Tabs/MiscTab.xaml.cs
Content.Client/UserInterface/Systems/Chat/ChatUIController.cs
Content.Server/Chat/Systems/ChatSystem.cs
Content.Shared/CCVar/CCVars.cs
Content.Shared/Chat/SharedChatSystem.cs
Resources/Locale/en-US/chat/managers/chat-manager.ftl
Resources/Locale/en-US/escape-menu/ui/options-menu.ftl

index 91e8e5a90f0c82fd077abe8e8444ad6787338bba..82eccbcec84b4f49b39c2f8ca5c22c081fcd5794 100644 (file)
@@ -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);
         }
 
     }
index db176db456c794b02acbd91b348d78251f2bec33..fc94ab7b0a6730190c910270ccb66b08d6d8e046 100644 (file)
@@ -22,6 +22,7 @@
                    FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
                    StyleClasses="LabelKeyText"/>
             <CheckBox Name="ReducedMotionCheckBox" Text="{Loc 'ui-options-reduced-motion'}" />
+            <CheckBox Name="EnableColorNameCheckBox" Text="{Loc 'ui-options-enable-color-name'}" />
             <BoxContainer Orientation="Horizontal">
                 <Label Text="{Loc 'ui-options-screen-shake-intensity'}" Margin="8 0" />
                 <Slider Name="ScreenShakeIntensitySlider"
index b5dcf6477b73de4cac59867b78111b9711ef8ce3..d7a600dd79d8c1097286d615d9f7925950efaa59 100644 (file)
@@ -63,6 +63,7 @@ namespace Content.Client.Options.UI.Tabs
             OpaqueStorageWindowCheckBox.OnToggled += OnCheckBoxToggled;
             FancySpeechBubblesCheckBox.OnToggled += OnCheckBoxToggled;
             FancyNameBackgroundsCheckBox.OnToggled += OnCheckBoxToggled;
+            EnableColorNameCheckBox.OnToggled += OnCheckBoxToggled;
             ReducedMotionCheckBox.OnToggled += OnCheckBoxToggled;
             ScreenShakeIntensitySlider.OnValueChanged += OnScreenShakeIntensitySliderChanged;
             // ToggleWalk.OnToggled += OnCheckBoxToggled;
@@ -76,6 +77,7 @@ namespace Content.Client.Options.UI.Tabs
             OpaqueStorageWindowCheckBox.Pressed = _cfg.GetCVar(CCVars.OpaqueStorageWindow);
             FancySpeechBubblesCheckBox.Pressed = _cfg.GetCVar(CCVars.ChatEnableFancyBubbles);
             FancyNameBackgroundsCheckBox.Pressed = _cfg.GetCVar(CCVars.ChatFancyNameBackground);
+            EnableColorNameCheckBox.Pressed = _cfg.GetCVar(CCVars.ChatEnableColorName);
             ReducedMotionCheckBox.Pressed = _cfg.GetCVar(CCVars.ReducedMotion);
             ScreenShakeIntensitySlider.Value = _cfg.GetCVar(CCVars.ScreenShakeIntensity) * 100f;
             // ToggleWalk.Pressed = _cfg.GetCVar(CCVars.ToggleWalk);
@@ -120,6 +122,7 @@ namespace Content.Client.Options.UI.Tabs
             _cfg.SetCVar(CCVars.LoocAboveHeadShow, ShowLoocAboveHeadCheckBox.Pressed);
             _cfg.SetCVar(CCVars.ChatEnableFancyBubbles, FancySpeechBubblesCheckBox.Pressed);
             _cfg.SetCVar(CCVars.ChatFancyNameBackground, FancyNameBackgroundsCheckBox.Pressed);
+            _cfg.SetCVar(CCVars.ChatEnableColorName, EnableColorNameCheckBox.Pressed);
             _cfg.SetCVar(CCVars.ReducedMotion, ReducedMotionCheckBox.Pressed);
             _cfg.SetCVar(CCVars.ScreenShakeIntensity, ScreenShakeIntensitySlider.Value / 100f);
             // _cfg.SetCVar(CCVars.ToggleWalk, ToggleWalk.Pressed);
@@ -145,6 +148,7 @@ namespace Content.Client.Options.UI.Tabs
             var isLoocShowSame = ShowLoocAboveHeadCheckBox.Pressed == _cfg.GetCVar(CCVars.LoocAboveHeadShow);
             var isFancyChatSame = FancySpeechBubblesCheckBox.Pressed == _cfg.GetCVar(CCVars.ChatEnableFancyBubbles);
             var isFancyBackgroundSame = FancyNameBackgroundsCheckBox.Pressed == _cfg.GetCVar(CCVars.ChatFancyNameBackground);
+            var isEnableColorNameSame = EnableColorNameCheckBox.Pressed == _cfg.GetCVar(CCVars.ChatEnableColorName);
             var isReducedMotionSame = ReducedMotionCheckBox.Pressed == _cfg.GetCVar(CCVars.ReducedMotion);
             var isScreenShakeIntensitySame = Math.Abs(ScreenShakeIntensitySlider.Value / 100f - _cfg.GetCVar(CCVars.ScreenShakeIntensity)) < 0.01f;
             // var isToggleWalkSame = ToggleWalk.Pressed == _cfg.GetCVar(CCVars.ToggleWalk);
@@ -159,6 +163,7 @@ namespace Content.Client.Options.UI.Tabs
                                    isLoocShowSame &&
                                    isFancyChatSame &&
                                    isFancyBackgroundSame &&
+                                   isEnableColorNameSame &&
                                    isReducedMotionSame &&
                                    isScreenShakeIntensitySame &&
                                    // isToggleWalkSame &&
index 1aa414d50548594addd3988386d1497460232b2d..2d15f92f77866d18dd87b078d272865a50c38fb3 100644 (file)
@@ -15,10 +15,12 @@ using Content.Client.UserInterface.Systems.Gameplay;
 using Content.Shared.Administration;
 using Content.Shared.CCVar;
 using Content.Shared.Chat;
+using Content.Shared.Decals;
 using Content.Shared.Damage.ForceSay;
 using Content.Shared.Examine;
 using Content.Shared.Input;
 using Content.Shared.Radio;
+using Robust.Client.GameObjects;
 using Robust.Client.Graphics;
 using Robust.Client.Input;
 using Robust.Client.Player;
@@ -27,9 +29,11 @@ using Robust.Client.UserInterface;
 using Robust.Client.UserInterface.Controllers;
 using Robust.Client.UserInterface.Controls;
 using Robust.Shared.Configuration;
+using Robust.Shared.GameObjects.Components.Localization;
 using Robust.Shared.Input.Binding;
 using Robust.Shared.Map;
 using Robust.Shared.Network;
+using Robust.Shared.Prototypes;
 using Robust.Shared.Replays;
 using Robust.Shared.Timing;
 using Robust.Shared.Utility;
@@ -42,9 +46,11 @@ public sealed class ChatUIController : UIController
     [Dependency] private readonly IChatManager _manager = default!;
     [Dependency] private readonly IConfigurationManager _config = default!;
     [Dependency] private readonly IEyeManager _eye = default!;
+    [Dependency] private readonly IEntityManager _ent = default!;
     [Dependency] private readonly IInputManager _input = default!;
     [Dependency] private readonly IClientNetManager _net = default!;
     [Dependency] private readonly IPlayerManager _player = default!;
+    [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
     [Dependency] private readonly IStateManager _state = default!;
     [Dependency] private readonly IGameTiming _timing = default!;
     [Dependency] private readonly IReplayRecordingManager _replayRecording = default!;
@@ -55,6 +61,11 @@ public sealed class ChatUIController : UIController
     [UISystemDependency] private readonly TypingIndicatorSystem? _typingIndicator = default;
     [UISystemDependency] private readonly ChatSystem? _chatSys = default;
 
+    [ValidatePrototypeId<ColorPalettePrototype>]
+    private const string ChatNamePalette = "ChatNames";
+    private string[] _chatNameColors = default!;
+    private bool _chatNameColorsEnabled;
+
     private ISawmill _sawmill = default!;
 
     public static readonly Dictionary<char, ChatSelectChannel> PrefixToChannel = new()
@@ -168,6 +179,8 @@ public sealed class ChatUIController : UIController
         _net.RegisterNetMessage<MsgChatMessage>(OnChatMessage);
         _net.RegisterNetMessage<MsgDeleteChatMessagesBy>(OnDeleteChatMessagesBy);
         SubscribeNetworkEvent<DamageForceSayEvent>(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<GameplayStateLoadController>();
         gameplayStateLoad.OnScreenLoad += OnScreenLoad;
         gameplayStateLoad.OnScreenUnload += OnScreenUnload;
+
+        var nameColors = _prototypeManager.Index<ColorPalettePrototype>(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<GrammarComponent>(_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
         }
     }
 
+    /// <summary>
+    /// Returns the chat name color for a mob
+    /// </summary>
+    /// <param name="name">Name of the mob</param>
+    /// <returns>Hex value of the color</returns>
+    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
index 649607772a9f2c77bc059e6c596483ad10fe8c57..13cc6df674128889a0f795a52907b3e4e0c49ca6 100644 (file)
@@ -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<ColorPalettePrototype>]
-    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<GameRunLevelChangedEvent>(OnGameChange);
-
-        var nameColors = _prototypeManager.Index<ColorPalettePrototype>(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<GrammarComponent>(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<GrammarComponent>(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
 
-    /// <summary>
-    /// Returns the chat name color for a mob
-    /// </summary>
-    /// <param name="name">Name of the mob</param>
-    /// <returns>Hex value of the color</returns>
-    public string GetNameColor(string name)
-    {
-        var colorIdx = Math.Abs(name.GetHashCode() % _chatNameColors.Length);
-        return _chatNameColors[colorIdx];
-    }
-
     private enum MessageRangeCheckResult
     {
         Disallowed,
index a6cb084c9eda46e325bc2f8f65e6c381099c5b08..aac3a17b6f8b7e220106a9042d9364715983f5a1 100644 (file)
@@ -1564,6 +1564,9 @@ namespace Content.Shared.CCVar
         public static readonly CVarDef<bool> ReducedMotion =
             CVarDef.Create("accessibility.reduced_motion", false, CVar.CLIENTONLY | CVar.ARCHIVE);
 
+        public static readonly CVarDef<bool> ChatEnableColorName =
+            CVarDef.Create("accessibility.enable_color_name", true, CVar.CLIENTONLY | CVar.ARCHIVE, "Toggles displaying names with individual colors.");
+
         /// <summary>
         /// Screen shake intensity slider, controlling the intensity of the CameraRecoilSystem.
         /// Goes from 0 (no recoil at all) to 1 (regular amounts of recoil)
index 931bfb37fcbc4a06c4552fcf6ebd17b1134484c5..e61d93efaeb1d49434d6fa7bfe5383913c608f41 100644 (file)
@@ -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);
+    }
 }
index 6bbb3cd449b9dbd82afba7797fd7e8e180042a5c..a165a02f9d8d8da327aea840e1905fab459f10da 100644 (file)
@@ -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...
index 4dd0ea9eb195eb26a8ee6f01c15f02cbe4013f5b..b8253c69293aa3c86e1af5861fd495cc5a675f04 100644 (file)
@@ -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") }