]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Health analyzer UI improve (#17280)
authorArtjom <artjombebenin@gmail.com>
Sat, 28 Oct 2023 05:54:18 +0000 (09:54 +0400)
committerGitHub <noreply@github.com>
Sat, 28 Oct 2023 05:54:18 +0000 (16:54 +1100)
Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml
Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs
Resources/Locale/en-US/medical/components/health-analyzer-component.ftl
Resources/Textures/Objects/Devices/health_analyzer.rsi/airloss.png [new file with mode: 0644]
Resources/Textures/Objects/Devices/health_analyzer.rsi/brute.png [new file with mode: 0644]
Resources/Textures/Objects/Devices/health_analyzer.rsi/burn.png [new file with mode: 0644]
Resources/Textures/Objects/Devices/health_analyzer.rsi/genetic.png [new file with mode: 0644]
Resources/Textures/Objects/Devices/health_analyzer.rsi/meta.json [new file with mode: 0644]
Resources/Textures/Objects/Devices/health_analyzer.rsi/toxin.png [new file with mode: 0644]
Resources/Textures/Objects/Devices/health_analyzer.rsi/unknown.png [new file with mode: 0644]

index 1f64feec2b4b58e655994f9c6acfaa3b7e04ace2..b78c3c6a567a8e82565816beb09acdc09aa17f32 100644 (file)
@@ -1,9 +1,33 @@
 <DefaultWindow xmlns="https://spacestation14.io"
-            MinSize="250 100"
-            SetSize="250 100">
-    <BoxContainer Orientation="Vertical">
-        <Label
-            Name="Diagnostics"
-            Text="{Loc health-analyzer-window-no-patient-data-text}"/>
-    </BoxContainer>
-</DefaultWindow>
+    SetSize="250 100">
+    <ScrollContainer
+        VerticalExpand="True">
+        <BoxContainer
+            Name="RootContainer"
+            Orientation="Vertical">
+            <Label
+                Name="NoPatientDataText"
+                Text="{Loc health-analyzer-window-no-patient-data-text}" />
+            <BoxContainer
+                Name="PatientDataContainer"
+                Orientation="Vertical"
+                Margin="0 0 5 10">
+                <Label
+                    Name="PatientName"/>
+                <Label
+                    Name="Temperature"
+                    Margin="0 5 0 0"/>
+                <Label
+                    Name="BloodLevel"
+                    Margin="0 5 0 0"/>
+                <Label
+                    Name="patientDamageAmount"
+                    Margin="0 15 0 0"/>
+            </BoxContainer>
+            <BoxContainer
+                Name="GroupsContainer"
+                Orientation="Vertical">
+            </BoxContainer>
+        </BoxContainer>
+    </ScrollContainer>
+</DefaultWindow>
\ No newline at end of file
index 1c5a3373e152f8d3ed27fdf30ce1d0a737b1bfd8..36f7a08b9612c4ddbabfb15e7adb341c42f23b27 100644 (file)
@@ -1,5 +1,5 @@
+using System.Linq;
 using System.Numerics;
-using System.Text;
 using Content.Shared.Damage;
 using Content.Shared.Damage.Prototypes;
 using Content.Shared.FixedPoint;
@@ -8,85 +8,182 @@ using Content.Shared.MedicalScanner;
 using Robust.Client.AutoGenerated;
 using Robust.Client.UserInterface.CustomControls;
 using Robust.Client.UserInterface.XAML;
+using Robust.Client.GameObjects;
+using Robust.Client.Graphics;
+using Robust.Client.UserInterface.Controls;
+using Robust.Client.ResourceManagement;
 using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
 
 namespace Content.Client.HealthAnalyzer.UI
 {
     [GenerateTypedNameReferences]
     public sealed partial class HealthAnalyzerWindow : DefaultWindow
     {
+        private readonly IEntityManager _entityManager;
+        private readonly SpriteSystem _spriteSystem;
+        private readonly IPrototypeManager _prototypes;
+        private readonly IResourceCache _cache;
+
+        private const int AnalyzerHeight = 430;
+        private const int AnalyzerWidth = 300;
+
         public HealthAnalyzerWindow()
         {
             RobustXamlLoader.Load(this);
+
+            var dependencies = IoCManager.Instance!;
+            _entityManager = dependencies.Resolve<IEntityManager>();
+            _spriteSystem = _entityManager.System<SpriteSystem>();
+            _prototypes = dependencies.Resolve<IPrototypeManager>();
+            _cache = dependencies.Resolve<IResourceCache>();
         }
 
         public void Populate(HealthAnalyzerScannedUserMessage msg)
         {
-            var text = new StringBuilder();
-            var entities = IoCManager.Resolve<IEntityManager>();
-            var target = entities.GetEntity(msg.TargetEntity);
+            GroupsContainer.RemoveAllChildren();
 
-            if (msg.TargetEntity != null && entities.TryGetComponent<DamageableComponent>(target, out var damageable))
+            var target = _entityManager.GetEntity(msg.TargetEntity);
+
+            if (target == null
+                || !_entityManager.TryGetComponent<DamageableComponent>(target, out var damageable))
             {
-                string entityName = "Unknown";
-                if (msg.TargetEntity != null &&
-                    entities.HasComponent<MetaDataComponent>(target.Value))
-                {
-                    entityName = Identity.Name(target.Value, entities);
-                }
+                NoPatientDataText.Visible = true;
+                return;
+            }
+
+            NoPatientDataText.Visible = false;
+
+            string entityName = Loc.GetString("health-analyzer-window-entity-unknown-text");
+            if (_entityManager.HasComponent<MetaDataComponent>(target.Value))
+            {
+                entityName = Identity.Name(target.Value, _entityManager);
+            }
 
-                IReadOnlyDictionary<string, FixedPoint2> damagePerGroup = damageable.DamagePerGroup;
-                IReadOnlyDictionary<string, FixedPoint2> damagePerType = damageable.Damage.DamageDict;
+            PatientName.Text = Loc.GetString(
+                "health-analyzer-window-entity-health-text",
+                ("entityName", entityName)
+            );
 
-                text.Append($"{Loc.GetString("health-analyzer-window-entity-health-text", ("entityName", entityName))}\n\n");
+            Temperature.Text = Loc.GetString("health-analyzer-window-entity-temperature-text",
+                ("temperature", float.IsNaN(msg.Temperature) ? "N/A" : $"{msg.Temperature - 273f:F1} °C")
+            );
 
+            BloodLevel.Text = Loc.GetString("health-analyzer-window-entity-blood-level-text",
+                ("bloodLevel", float.IsNaN(msg.BloodLevel) ? "N/A" : $"{msg.BloodLevel * 100:F1} %")
+            );
 
-                text.Append($"{Loc.GetString("health-analyzer-window-entity-temperature-text", ("temperature", float.IsNaN(msg.Temperature) ? "N/A" : $"{msg.Temperature - 273f:F1} °C"))}\n");
+            patientDamageAmount.Text = Loc.GetString(
+                "health-analyzer-window-entity-damage-total-text",
+                ("amount", damageable.TotalDamage)
+            );
 
+            var damageSortedGroups =
+                damageable.DamagePerGroup.OrderBy(damage => damage.Value)
+                    .ToDictionary(x => x.Key, x => x.Value);
+            IReadOnlyDictionary<string, FixedPoint2> damagePerType = damageable.Damage.DamageDict;
 
-                text.Append($"{Loc.GetString("health-analyzer-window-entity-blood-level-text", ("bloodLevel", float.IsNaN(msg.BloodLevel) ? "N/A" : $"{msg.BloodLevel * 100:F1} %"))}\n\n");
+            DrawDiagnosticGroups(damageSortedGroups, damagePerType);
 
+            SetHeight = AnalyzerHeight;
+            SetWidth = AnalyzerWidth;
+        }
 
-                // Damage
-                text.Append($"{Loc.GetString("health-analyzer-window-entity-damage-total-text", ("amount", damageable.TotalDamage))}\n");
+        private void DrawDiagnosticGroups(
+            Dictionary<string, FixedPoint2> groups, IReadOnlyDictionary<string, FixedPoint2> damageDict)
+        {
+            HashSet<string> shownTypes = new();
 
-                HashSet<string> shownTypes = new();
+            // Show the total damage and type breakdown for each damage group.
+            foreach (var (damageGroupId, damageAmount) in groups.Reverse())
+            {
+                if (damageAmount == 0)
+                    continue;
 
-                var protos = IoCManager.Resolve<IPrototypeManager>();
+                var groupTitleText = $"{Loc.GetString(
+                    "health-analyzer-window-damage-group-text",
+                    ("damageGroup", Loc.GetString("health-analyzer-window-damage-group-" + damageGroupId)),
+                    ("amount", damageAmount)
+                )}";
 
-                // Show the total damage and type breakdown for each damage group.
-                foreach (var (damageGroupId, damageAmount) in damagePerGroup)
+                var groupContainer = new BoxContainer
                 {
-                    if (damageAmount == 0)
-                    {
-                        continue;
-                    }
-                    text.Append($"\n{Loc.GetString("health-analyzer-window-damage-group-text", ("damageGroup", Loc.GetString("health-analyzer-window-damage-group-" + damageGroupId)), ("amount", damageAmount))}");
+                    Margin = new Thickness(0, 0, 0, 15),
+                    Align = BoxContainer.AlignMode.Begin,
+                    Orientation = BoxContainer.LayoutOrientation.Vertical,
+                };
+
+                groupContainer.AddChild(CreateDiagnosticGroupTitle(groupTitleText, damageGroupId, damageAmount.Int()));
+
+                GroupsContainer.AddChild(groupContainer);
 
-                    // Show the damage for each type in that group.
-                    var group = protos.Index<DamageGroupPrototype>(damageGroupId);
-                    foreach (var type in group.DamageTypes)
+                // Show the damage for each type in that group.
+                var group = _prototypes.Index<DamageGroupPrototype>(damageGroupId);
+
+                foreach (var type in group.DamageTypes)
+                {
+                    if (damageDict.TryGetValue(type, out var typeAmount) && typeAmount > 0)
                     {
-                        if (damagePerType.TryGetValue(type, out var typeAmount) )
-                        {
-                            // If damage types are allowed to belong to more than one damage group, they may appear twice here. Mark them as duplicate.
-                            if (!shownTypes.Contains(type) && typeAmount > 0)
-                            {
-                                shownTypes.Add(type);
-                                text.Append($"\n- {Loc.GetString("health-analyzer-window-damage-type-text", ("damageType", Loc.GetString("health-analyzer-window-damage-type-" + type)), ("amount", typeAmount))}");
-                            }
-                        }
+                        // If damage types are allowed to belong to more than one damage group,
+                        // they may appear twice here. Mark them as duplicate.
+                        if (shownTypes.Contains(type))
+                            continue;
+
+                        shownTypes.Add(type);
+
+                        var damageString = Loc.GetString(
+                            "health-analyzer-window-damage-type-text",
+                            ("damageType", Loc.GetString("health-analyzer-window-damage-type-" + type)),
+                            ("amount", typeAmount)
+                        );
+
+                        groupContainer.AddChild(CreateDiagnosticItemLabel(damageString.Insert(0, "- ")));
                     }
-                    text.AppendLine();
                 }
-                Diagnostics.Text = text.ToString();
-                SetSize = new Vector2(250, 600);
             }
-            else
+        }
+
+        private Texture GetTexture(string texture)
+        {
+            var rsiPath = new ResPath("/Textures/Objects/Devices/health_analyzer.rsi");
+            var rsiSprite = new SpriteSpecifier.Rsi(rsiPath, texture);
+
+            var rsi = _cache.GetResource<RSIResource>(rsiSprite.RsiPath).RSI;
+            if (!rsi.TryGetState(rsiSprite.RsiState, out var state))
             {
-                Diagnostics.Text = Loc.GetString("health-analyzer-window-no-patient-data-text");
-                SetSize = new Vector2(250, 100);
+                rsiSprite = new SpriteSpecifier.Rsi(rsiPath, "unknown");
             }
+
+            return _spriteSystem.Frame0(rsiSprite);
+        }
+
+        private static Label CreateDiagnosticItemLabel(string text)
+        {
+            return new Label
+            {
+                Margin = new Thickness(2, 2),
+                Text = text,
+            };
+        }
+
+        private BoxContainer CreateDiagnosticGroupTitle(string text, string id, int damageAmount)
+        {
+            var rootContainer = new BoxContainer
+            {
+                VerticalAlignment = VAlignment.Bottom,
+                Orientation = BoxContainer.LayoutOrientation.Horizontal
+            };
+
+            rootContainer.AddChild(new TextureRect
+            {
+                Margin = new Thickness(0, 3),
+                SetSize = new Vector2(30, 30),
+                Texture = GetTexture(id.ToLower())
+            });
+
+            rootContainer.AddChild(CreateDiagnosticItemLabel(text));
+
+            return rootContainer;
         }
     }
 }
index 99513df591dc96b7228c09f311ee5e5bab4a3627..453bbdbb526b7a3b48b41a05b4b70be9bab3c491 100644 (file)
@@ -1,4 +1,5 @@
 health-analyzer-window-no-patient-data-text = No patient data.
+health-analyzer-window-entity-unknown-text = unknown
 health-analyzer-window-entity-health-text = {$entityName}'s health:
 health-analyzer-window-entity-temperature-text = Temperature: {$temperature}
 health-analyzer-window-entity-blood-level-text = Blood Level: {$bloodLevel}
diff --git a/Resources/Textures/Objects/Devices/health_analyzer.rsi/airloss.png b/Resources/Textures/Objects/Devices/health_analyzer.rsi/airloss.png
new file mode 100644 (file)
index 0000000..82fa0e2
Binary files /dev/null and b/Resources/Textures/Objects/Devices/health_analyzer.rsi/airloss.png differ
diff --git a/Resources/Textures/Objects/Devices/health_analyzer.rsi/brute.png b/Resources/Textures/Objects/Devices/health_analyzer.rsi/brute.png
new file mode 100644 (file)
index 0000000..bd5d80d
Binary files /dev/null and b/Resources/Textures/Objects/Devices/health_analyzer.rsi/brute.png differ
diff --git a/Resources/Textures/Objects/Devices/health_analyzer.rsi/burn.png b/Resources/Textures/Objects/Devices/health_analyzer.rsi/burn.png
new file mode 100644 (file)
index 0000000..cf65084
Binary files /dev/null and b/Resources/Textures/Objects/Devices/health_analyzer.rsi/burn.png differ
diff --git a/Resources/Textures/Objects/Devices/health_analyzer.rsi/genetic.png b/Resources/Textures/Objects/Devices/health_analyzer.rsi/genetic.png
new file mode 100644 (file)
index 0000000..b660aed
Binary files /dev/null and b/Resources/Textures/Objects/Devices/health_analyzer.rsi/genetic.png differ
diff --git a/Resources/Textures/Objects/Devices/health_analyzer.rsi/meta.json b/Resources/Textures/Objects/Devices/health_analyzer.rsi/meta.json
new file mode 100644 (file)
index 0000000..3fccf5c
--- /dev/null
@@ -0,0 +1,29 @@
+{
+  "version": 1,
+  "size": {
+    "x": 32,
+    "y": 32
+  },
+  "license": "CC-BY-SA-3.0",
+  "copyright": "airloss, brute, toxin and burn edited from /tg/station https://github.com/tgstation/tgstation/tree/master genetic edited from https://iconscout.com/free-icon/dna-2130814 with license CC BY 4.0",
+  "states": [
+    {
+      "name": "airloss"
+    },
+    {
+      "name": "genetic"
+    },
+    {
+      "name": "brute"
+    },
+    {
+      "name": "toxin"
+    },
+    {
+      "name": "burn"
+    },
+    {
+      "name": "unknown"
+    }
+  ]
+}
\ No newline at end of file
diff --git a/Resources/Textures/Objects/Devices/health_analyzer.rsi/toxin.png b/Resources/Textures/Objects/Devices/health_analyzer.rsi/toxin.png
new file mode 100644 (file)
index 0000000..b0aafdd
Binary files /dev/null and b/Resources/Textures/Objects/Devices/health_analyzer.rsi/toxin.png differ
diff --git a/Resources/Textures/Objects/Devices/health_analyzer.rsi/unknown.png b/Resources/Textures/Objects/Devices/health_analyzer.rsi/unknown.png
new file mode 100644 (file)
index 0000000..f68e2c9
Binary files /dev/null and b/Resources/Textures/Objects/Devices/health_analyzer.rsi/unknown.png differ