]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Add a toggle for colorblind friendly progress bar colors (#25318)
authorNemanja <98561806+EmoGarbage404@users.noreply.github.com>
Sat, 9 Mar 2024 11:43:19 +0000 (06:43 -0500)
committerGitHub <noreply@github.com>
Sat, 9 Mar 2024 11:43:19 +0000 (12:43 +0100)
* Add a toggle for progress bar colors

* yeah this thing

* PJB review

* optimization

Content.Client/DoAfter/DoAfterOverlay.cs
Content.Client/Options/UI/Tabs/MiscTab.xaml
Content.Client/Options/UI/Tabs/MiscTab.xaml.cs
Content.Client/Overlays/EntityHealthBarOverlay.cs
Content.Client/UserInterface/Controls/ProgressTextureRect.cs
Content.Client/UserInterface/Systems/ProgressColorSystem.cs [new file with mode: 0644]
Content.Shared/CCVar/CCVars.cs
Resources/Locale/en-US/escape-menu/ui/options-menu.ftl

index 2957dafdb7a83f0b434761ebbd9d33ac18154efe..2e23dd44cacaeca59d1bd90fecdc46c4a2ede7ca 100644 (file)
@@ -1,9 +1,9 @@
 using System.Numerics;
 using Content.Shared.DoAfter;
+using Content.Client.UserInterface.Systems;
 using Robust.Client.GameObjects;
 using Robust.Client.Graphics;
 using Robust.Shared.Enums;
-using Robust.Shared.Graphics;
 using Robust.Client.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Timing;
@@ -18,6 +18,7 @@ public sealed class DoAfterOverlay : Overlay
     private readonly IPlayerManager _player;
     private readonly SharedTransformSystem _transform;
     private readonly MetaDataSystem _meta;
+    private readonly ProgressColorSystem _progressColor;
 
     private readonly Texture _barTexture;
     private readonly ShaderInstance _shader;
@@ -40,6 +41,7 @@ public sealed class DoAfterOverlay : Overlay
         _player = player;
         _transform = _entManager.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
         _meta = _entManager.EntitySysManager.GetEntitySystem<MetaDataSystem>();
+        _progressColor = _entManager.System<ProgressColorSystem>();
         var sprite = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/progress_bar.rsi"), "icon");
         _barTexture = _entManager.EntitySysManager.GetEntitySystem<SpriteSystem>().Frame0(sprite);
 
@@ -125,7 +127,7 @@ public sealed class DoAfterOverlay : Overlay
                     elapsedRatio = (float) Math.Min(1, elapsed.TotalSeconds / doAfter.Args.Delay.TotalSeconds);
                     var cancelElapsed = (time - doAfter.CancelledTime.Value).TotalSeconds;
                     var flash = Math.Floor(cancelElapsed / FlashTime) % 2 == 0;
-                    color = new Color(1f, 0f, 0f, flash ? alpha : 0f);
+                    color = GetProgressColor(0, flash ? alpha : 0);
                 }
                 else
                 {
@@ -146,14 +148,8 @@ public sealed class DoAfterOverlay : Overlay
         handle.SetTransform(Matrix3.Identity);
     }
 
-    public static Color GetProgressColor(float progress, float alpha = 1f)
+    public Color GetProgressColor(float progress, float alpha = 1f)
     {
-        if (progress >= 1.0f)
-        {
-            return new Color(0f, 1f, 0f, alpha);
-        }
-        // lerp
-        var hue = (5f / 18f) * progress;
-        return Color.FromHsv((hue, 1f, 0.75f, alpha));
+        return _progressColor.GetProgressColor(progress).WithAlpha(alpha);
     }
 }
index fc94ab7b0a6730190c910270ccb66b08d6d8e046..2ee59910f70c08e0022b08f52da8c9830dabdccd 100644 (file)
@@ -4,57 +4,59 @@
                   xmlns:xNamespace="http://schemas.microsoft.com/winfx/2006/xaml"
                   xmlns:s="clr-namespace:Content.Client.Stylesheets">
     <BoxContainer Orientation="Vertical">
-        <BoxContainer Orientation="Vertical" Margin="8 8 8 8" VerticalExpand="True">
-            <Label Text="{Loc 'ui-options-general-ui-style'}"
-                   FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
-                   StyleClasses="LabelKeyText"/>
-            <BoxContainer Orientation="Horizontal">
-                <Label Text="{Loc 'ui-options-hud-theme'}" />
-                <Control MinSize="4 0" />
-                <OptionButton Name="HudThemeOption" />
+        <ScrollContainer VerticalExpand="True" HorizontalExpand="True">
+            <BoxContainer Orientation="Vertical" Margin="8 8 8 8" VerticalExpand="True">
+                <Label Text="{Loc 'ui-options-general-ui-style'}"
+                       FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
+                       StyleClasses="LabelKeyText"/>
+                <BoxContainer Orientation="Horizontal">
+                    <Label Text="{Loc 'ui-options-hud-theme'}" />
+                    <Control MinSize="4 0" />
+                    <OptionButton Name="HudThemeOption" />
+                </BoxContainer>
+                <BoxContainer Orientation="Horizontal">
+                    <Label Text="{Loc 'ui-options-hud-layout'}" />
+                    <Control MinSize="4 0" />
+                    <OptionButton Name="HudLayoutOption" />
+                </BoxContainer>
+                <Label Text="{Loc 'ui-options-general-accessibility'}"
+                       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'}" />
+                <CheckBox Name="ColorblindFriendlyCheckBox" Text="{Loc 'ui-options-colorblind-friendly'}" />
+                <BoxContainer Orientation="Horizontal">
+                    <Label Text="{Loc 'ui-options-screen-shake-intensity'}" Margin="8 0" />
+                    <Slider Name="ScreenShakeIntensitySlider"
+                            MinValue="0"
+                            MaxValue="100"
+                            Rounded="True"
+                            MinWidth="200" />
+                    <Label Name="ScreenShakeIntensityLabel" Margin="8 0" />
+                </BoxContainer>
+                <Label Text="{Loc 'ui-options-general-discord'}"
+                       FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
+                       StyleClasses="LabelKeyText"/>
+                <CheckBox Name="DiscordRich" Text="{Loc 'ui-options-discordrich'}" />
+                <Label Text="{Loc 'ui-options-general-speech'}"
+                       FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
+                       StyleClasses="LabelKeyText"/>
+                <CheckBox Name="ShowLoocAboveHeadCheckBox" Text="{Loc 'ui-options-show-looc-on-head'}" />
+                <CheckBox Name="FancySpeechBubblesCheckBox" Text="{Loc 'ui-options-fancy-speech'}" />
+                <CheckBox Name="FancyNameBackgroundsCheckBox" Text="{Loc 'ui-options-fancy-name-background'}" />
+                <Label Text="{Loc 'ui-options-general-cursor'}"
+                       FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
+                       StyleClasses="LabelKeyText"/>
+                <CheckBox Name="ShowHeldItemCheckBox" Text="{Loc 'ui-options-show-held-item'}" />
+                <CheckBox Name="ShowCombatModeIndicatorsCheckBox" Text="{Loc 'ui-options-show-combat-mode-indicators'}" />
+                <Label Text="{Loc 'ui-options-general-storage'}"
+                       FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
+                       StyleClasses="LabelKeyText"/>
+                <CheckBox Name="OpaqueStorageWindowCheckBox" Text="{Loc 'ui-options-opaque-storage-window'}" />
+                <CheckBox Name="StaticStorageUI" Text="{Loc 'ui-options-static-storage-ui'}" />
+                <!-- <CheckBox Name="ToggleWalk" Text="{Loc 'ui-options-hotkey-toggle-walk'}" /> -->
             </BoxContainer>
-            <BoxContainer Orientation="Horizontal">
-                <Label Text="{Loc 'ui-options-hud-layout'}" />
-                <Control MinSize="4 0" />
-                <OptionButton Name="HudLayoutOption" />
-            </BoxContainer>
-            <Label Text="{Loc 'ui-options-general-accessibility'}"
-                   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"
-                        MinValue="0"
-                        MaxValue="100"
-                        Rounded="True"
-                        MinWidth="200" />
-                <Label Name="ScreenShakeIntensityLabel" Margin="8 0" />
-            </BoxContainer>
-            <Label Text="{Loc 'ui-options-general-discord'}"
-                   FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
-                   StyleClasses="LabelKeyText"/>
-            <CheckBox Name="DiscordRich" Text="{Loc 'ui-options-discordrich'}" />
-            <Label Text="{Loc 'ui-options-general-speech'}"
-                   FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
-                   StyleClasses="LabelKeyText"/>
-            <CheckBox Name="ShowLoocAboveHeadCheckBox" Text="{Loc 'ui-options-show-looc-on-head'}" />
-            <CheckBox Name="FancySpeechBubblesCheckBox" Text="{Loc 'ui-options-fancy-speech'}" />
-            <CheckBox Name="FancyNameBackgroundsCheckBox" Text="{Loc 'ui-options-fancy-name-background'}" />
-            <Label Text="{Loc 'ui-options-general-cursor'}"
-                   FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
-                   StyleClasses="LabelKeyText"/>
-            <CheckBox Name="ShowHeldItemCheckBox" Text="{Loc 'ui-options-show-held-item'}" />
-            <CheckBox Name="ShowCombatModeIndicatorsCheckBox" Text="{Loc 'ui-options-show-combat-mode-indicators'}" />
-            <Label Text="{Loc 'ui-options-general-storage'}"
-                   FontColorOverride="{xNamespace:Static s:StyleNano.NanoGold}"
-                   StyleClasses="LabelKeyText"/>
-            <CheckBox Name="OpaqueStorageWindowCheckBox" Text="{Loc 'ui-options-opaque-storage-window'}" />
-            <CheckBox Name="StaticStorageUI" Text="{Loc 'ui-options-static-storage-ui'}" />
-            <!-- <CheckBox Name="ToggleWalk" Text="{Loc 'ui-options-hotkey-toggle-walk'}" /> -->
-
-        </BoxContainer>
+        </ScrollContainer>
         <controls:StripeBack HasBottomEdge="False" HasMargins="False">
             <Button Name="ApplyButton"
                     Text="{Loc 'ui-options-apply'}"
index d7a600dd79d8c1097286d615d9f7925950efaa59..3b9c41efdfb4f2ef5e8636d22be338efc12ec2ea 100644 (file)
@@ -64,6 +64,7 @@ namespace Content.Client.Options.UI.Tabs
             FancySpeechBubblesCheckBox.OnToggled += OnCheckBoxToggled;
             FancyNameBackgroundsCheckBox.OnToggled += OnCheckBoxToggled;
             EnableColorNameCheckBox.OnToggled += OnCheckBoxToggled;
+            ColorblindFriendlyCheckBox.OnToggled += OnCheckBoxToggled;
             ReducedMotionCheckBox.OnToggled += OnCheckBoxToggled;
             ScreenShakeIntensitySlider.OnValueChanged += OnScreenShakeIntensitySliderChanged;
             // ToggleWalk.OnToggled += OnCheckBoxToggled;
@@ -78,6 +79,7 @@ namespace Content.Client.Options.UI.Tabs
             FancySpeechBubblesCheckBox.Pressed = _cfg.GetCVar(CCVars.ChatEnableFancyBubbles);
             FancyNameBackgroundsCheckBox.Pressed = _cfg.GetCVar(CCVars.ChatFancyNameBackground);
             EnableColorNameCheckBox.Pressed = _cfg.GetCVar(CCVars.ChatEnableColorName);
+            ColorblindFriendlyCheckBox.Pressed = _cfg.GetCVar(CCVars.AccessibilityColorblindFriendly);
             ReducedMotionCheckBox.Pressed = _cfg.GetCVar(CCVars.ReducedMotion);
             ScreenShakeIntensitySlider.Value = _cfg.GetCVar(CCVars.ScreenShakeIntensity) * 100f;
             // ToggleWalk.Pressed = _cfg.GetCVar(CCVars.ToggleWalk);
@@ -123,6 +125,7 @@ namespace Content.Client.Options.UI.Tabs
             _cfg.SetCVar(CCVars.ChatEnableFancyBubbles, FancySpeechBubblesCheckBox.Pressed);
             _cfg.SetCVar(CCVars.ChatFancyNameBackground, FancyNameBackgroundsCheckBox.Pressed);
             _cfg.SetCVar(CCVars.ChatEnableColorName, EnableColorNameCheckBox.Pressed);
+            _cfg.SetCVar(CCVars.AccessibilityColorblindFriendly, ColorblindFriendlyCheckBox.Pressed);
             _cfg.SetCVar(CCVars.ReducedMotion, ReducedMotionCheckBox.Pressed);
             _cfg.SetCVar(CCVars.ScreenShakeIntensity, ScreenShakeIntensitySlider.Value / 100f);
             // _cfg.SetCVar(CCVars.ToggleWalk, ToggleWalk.Pressed);
@@ -149,6 +152,7 @@ namespace Content.Client.Options.UI.Tabs
             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 isColorblindFriendly = ColorblindFriendlyCheckBox.Pressed == _cfg.GetCVar(CCVars.AccessibilityColorblindFriendly);
             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);
@@ -164,6 +168,7 @@ namespace Content.Client.Options.UI.Tabs
                                    isFancyChatSame &&
                                    isFancyBackgroundSame &&
                                    isEnableColorNameSame &&
+                                   isColorblindFriendly &&
                                    isReducedMotionSame &&
                                    isScreenShakeIntensitySame &&
                                    // isToggleWalkSame &&
index 11bec135397fa601b679ce8e3679fe28fbbbbd1f..9e562b5dd37cb776c4f128be4aaef42106f42a24 100644 (file)
@@ -8,6 +8,8 @@ using Robust.Client.Graphics;
 using Robust.Shared.Enums;
 using System.Numerics;
 using Content.Shared.StatusIcon.Components;
+using Content.Client.UserInterface.Systems;
+using Robust.Shared.Prototypes;
 using static Robust.Shared.Maths.Color;
 
 namespace Content.Client.Overlays;
@@ -17,19 +19,25 @@ namespace Content.Client.Overlays;
 /// </summary>
 public sealed class EntityHealthBarOverlay : Overlay
 {
+    [Dependency] private readonly IPrototypeManager _prototype = default!;
     private readonly IEntityManager _entManager;
     private readonly SharedTransformSystem _transform;
     private readonly MobStateSystem _mobStateSystem;
     private readonly MobThresholdSystem _mobThresholdSystem;
+    private readonly ProgressColorSystem _progressColor;
     public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
     public HashSet<string> DamageContainers = new();
+    private readonly ShaderInstance _shader;
 
     public EntityHealthBarOverlay(IEntityManager entManager)
     {
+        IoCManager.InjectDependencies(this);
         _entManager = entManager;
-        _transform = _entManager.EntitySysManager.GetEntitySystem<SharedTransformSystem>();
-        _mobStateSystem = _entManager.EntitySysManager.GetEntitySystem<MobStateSystem>();
-        _mobThresholdSystem = _entManager.EntitySysManager.GetEntitySystem<MobThresholdSystem>();
+        _transform = _entManager.System<SharedTransformSystem>();
+        _mobStateSystem = _entManager.System<MobStateSystem>();
+        _mobThresholdSystem = _entManager.System<MobThresholdSystem>();
+        _progressColor = _entManager.System<ProgressColorSystem>();
+        _shader = _prototype.Index<ShaderPrototype>("unshaded").Instance();
     }
 
     protected override void Draw(in OverlayDrawArgs args)
@@ -42,6 +50,8 @@ public sealed class EntityHealthBarOverlay : Overlay
         var scaleMatrix = Matrix3.CreateScale(new Vector2(scale, scale));
         var rotationMatrix = Matrix3.CreateRotation(-rotation);
 
+        handle.UseShader(_shader);
+
         var query = _entManager.AllEntityQueryEnumerator<MobThresholdsComponent, MobStateComponent, DamageableComponent, SpriteComponent>();
         while (query.MoveNext(out var uid,
             out var mobThresholdsComponent,
@@ -147,26 +157,11 @@ public sealed class EntityHealthBarOverlay : Overlay
         return (0, true);
     }
 
-    public static Color GetProgressColor(float progress, bool crit)
+    public Color GetProgressColor(float progress, bool crit)
     {
-        if (progress >= 1.0f)
-        {
-            return SeaBlue;
-        }
-
-        if (!crit)
-        {
-            switch (progress)
-            {
-                case > 0.90F:
-                    return SeaBlue;
-                case > 0.50F:
-                    return Violet;
-                case > 0.15F:
-                    return Ruber;
-            }
-        }
+        if (crit)
+            progress = 0;
 
-        return VividGamboge;
+        return _progressColor.GetProgressColor(progress);
     }
 }
index 3f422e371f72f05a9c19c7283aa72a74b368fb4e..2b8d93a5d4773610e1aea8596fc19ac9f04ae24e 100644 (file)
@@ -1,5 +1,5 @@
 using System.Numerics;
-using Content.Client.DoAfter;
+using Content.Client.UserInterface.Systems;
 using Robust.Client.Graphics;
 using Robust.Client.UserInterface.Controls;
 
@@ -9,11 +9,13 @@ namespace Content.Client.UserInterface.Controls
     {
         public float Progress;
 
+        private readonly ProgressColorSystem _progressColor = IoCManager.Resolve<IEntityManager>().System<ProgressColorSystem>();
+
         protected override void Draw(DrawingHandleScreen handle)
         {
             var dims = Texture != null ? GetDrawDimensions(Texture) : UIBox2.FromDimensions(Vector2.Zero, PixelSize);
             dims.Top = Math.Max(dims.Bottom - dims.Bottom * Progress,0);
-            handle.DrawRect(dims, DoAfterOverlay.GetProgressColor(Progress));
+            handle.DrawRect(dims, _progressColor.GetProgressColor(Progress));
 
             base.Draw(handle);
         }
diff --git a/Content.Client/UserInterface/Systems/ProgressColorSystem.cs b/Content.Client/UserInterface/Systems/ProgressColorSystem.cs
new file mode 100644 (file)
index 0000000..e557980
--- /dev/null
@@ -0,0 +1,77 @@
+using Content.Shared.CCVar;
+using Robust.Shared.Configuration;
+
+namespace Content.Client.UserInterface.Systems;
+
+/// <summary>
+/// This system handles getting an interpolated color based on the value of a cvar.
+/// </summary>
+public sealed class ProgressColorSystem : EntitySystem
+{
+    [Dependency] private readonly IConfigurationManager _configuration = default!;
+
+    private bool _colorBlindFriendly;
+
+    private static readonly Color[] Plasma =
+    {
+        new(240, 249, 33),
+        new(248, 149, 64),
+        new(204, 71, 120),
+        new(126, 3, 168),
+        new(13, 8, 135)
+    };
+
+    /// <inheritdoc/>
+    public override void Initialize()
+    {
+        Subs.CVar(_configuration, CCVars.AccessibilityColorblindFriendly, OnColorBlindFriendlyChanged, true);
+    }
+
+    private void OnColorBlindFriendlyChanged(bool value, in CVarChangeInfo info)
+    {
+        _colorBlindFriendly = value;
+    }
+
+    public Color GetProgressColor(float progress)
+    {
+        if (!_colorBlindFriendly)
+        {
+            if (progress >= 1.0f)
+            {
+                return new Color(0f, 1f, 0f);
+            }
+
+            // lerp
+            var hue = 5f / 18f * progress;
+            return Color.FromHsv((hue, 1f, 0.75f, 1f));
+        }
+
+        return InterpolateColorGaussian(Plasma, progress);
+    }
+
+    /// <summary>
+    /// Interpolates between multiple colors based on a gaussian distribution.
+    /// Taken from https://stackoverflow.com/a/26103117
+    /// </summary>
+    public static Color InterpolateColorGaussian(Color[] colors, double x)
+    {
+        double r = 0.0, g = 0.0, b = 0.0;
+        var total = 0f;
+        var step = 1.0 / (colors.Length - 1);
+        var mu = 0.0;
+        const double sigma2 = 0.035;
+
+        foreach(var color in colors)
+        {
+            var percent = Math.Exp(-(x - mu) * (x - mu) / (2.0 * sigma2)) / Math.Sqrt(2.0 * Math.PI * sigma2);
+            total += (float) percent;
+            mu += step;
+
+            r += color.R * percent;
+            g += color.G * percent;
+            b += color.B * percent;
+        }
+
+        return new Color((float) r / total, (float) g / total, (float) b / total);
+    }
+}
index bfb7081bcb76239de02a66996f03183947e2e8d0..c48a203c5912b289b001f0c46c3b398be89754a3 100644 (file)
@@ -1578,6 +1578,13 @@ namespace Content.Shared.CCVar
         public static readonly CVarDef<float> ScreenShakeIntensity =
             CVarDef.Create("accessibility.screen_shake_intensity", 1f, CVar.CLIENTONLY | CVar.ARCHIVE);
 
+        /// <summary>
+        /// A generic toggle for various visual effects that are color sensitive.
+        /// As of 2/16/24, only applies to progress bar colors.
+        /// </summary>
+        public static readonly CVarDef<bool> AccessibilityColorblindFriendly =
+            CVarDef.Create("accessibility.colorblind_friendly", false, CVar.CLIENTONLY | CVar.ARCHIVE);
+
         /*
          * CHAT
          */
index 7d2f098b8c3cc6b6265b08cc89aacb5ad3dc60b7..46f5df48ad668c9796baf3aca264dccd40dd2df6 100644 (file)
@@ -46,6 +46,7 @@ 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-colorblind-friendly = Colorblind friendly mode
 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") }