]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
New Health Analyzer UI (#30834)
authorThomas <87614336+Aeshus@users.noreply.github.com>
Tue, 27 Aug 2024 14:57:36 +0000 (09:57 -0500)
committerGitHub <noreply@github.com>
Tue, 27 Aug 2024 14:57:36 +0000 (16:57 +0200)
* WIP: first prototype

* Change text slightly

* Allow names to wrap

* Add label for the scan mode

* Remove ugly text

* Readd bleeding message

* Update code

* Allow for the Health Analyzer UI to grow vertically

Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs
Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml
Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs
Resources/Locale/en-US/medical/components/health-analyzer-component.ftl

index 38760f4aa3cd14eb18fddc3b4e3bd8c6eaa3c370..baea03c89231d1dcde8202745e472a0af38caccd 100644 (file)
@@ -17,6 +17,7 @@ namespace Content.Client.HealthAnalyzer.UI
         protected override void Open()
         {
             base.Open();
+
             _window = this.CreateWindow<HealthAnalyzerWindow>();
 
             _window.Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName;
index 401f9768629390fc4782b7bd5e3cf78afb3354fa..97968c4b990b3dbb8f53c6f7556ae6ededa43783 100644 (file)
@@ -1,48 +1,64 @@
-<controls:FancyWindow
+<controls:FancyWindow
     xmlns="https://spacestation14.io"
     xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
-    SetSize="250 100">
+    MaxHeight="525"
+    MinWidth="300">
     <ScrollContainer
+        Margin="5 5 5 5"
+        ReturnMeasure="True"
         VerticalExpand="True">
         <BoxContainer
             Name="RootContainer"
+            VerticalExpand="True"
             Orientation="Vertical">
             <Label
                 Name="NoPatientDataText"
                 Text="{Loc health-analyzer-window-no-patient-data-text}" />
+
             <BoxContainer
                 Name="PatientDataContainer"
-                Orientation="Vertical"
-                Margin="0 0 5 10">
-                <BoxContainer Name="ScanModePanel" HorizontalExpand="True" Visible="False" Margin="0 5 0 0">
-                    <Label
-                    Name="ScanMode"
-                    Align="Left"
-                    Text="{Loc health-analyzer-window-scan-mode-text}"/>
-                    <Label
-                    Name="ScanModeText"
-                    Align="Right"
-                    HorizontalExpand="True"/>
+                Margin="0 0 0 5"
+                Orientation="Vertical">
+                <BoxContainer Orientation="Horizontal" Margin="0 0 0 5">
+                    <SpriteView OverrideDirection="South" Scale="2 2" Name="SpriteView" Access="Public" SetSize="64 64" />
+                    <BoxContainer Margin="5 0 0 0" Orientation="Vertical" VerticalAlignment="Top">
+                        <RichTextLabel Name="NameLabel" SetWidth="150" />
+                        <Label Name="SpeciesLabel" VerticalAlignment="Top" StyleClasses="LabelSubText" />
+                    </BoxContainer>
+                    <Label Margin="0 0 5 0" HorizontalExpand="True" HorizontalAlignment="Right" VerticalExpand="True"
+                           VerticalAlignment="Top" Name="ScanModeLabel"
+                           Text="{Loc 'health-analyzer-window-entity-unknown-text'}" />
                 </BoxContainer>
-                <Label
-                    Name="PatientName"/>
-                <Label
-                    Name="Temperature"
-                    Margin="0 5 0 0"/>
-                <Label
-                    Name="BloodLevel"
-                    Margin="0 5 0 0"/>
-                <Label
-                    Name="Bleeding"
-                    Margin="0 5 0 0"/>
-                <Label
-                    Name="patientDamageAmount"
-                    Margin="0 15 0 0"/>
+
+                <PanelContainer StyleClasses="LowDivider" />
+
+                <GridContainer Margin="0 5 0 0" Columns="2">
+                    <Label Text="{Loc 'health-analyzer-window-entity-status-text'}" />
+                    <Label Name="StatusLabel" />
+                    <Label Text="{Loc 'health-analyzer-window-entity-temperature-text'}" />
+                    <Label Name="TemperatureLabel" />
+                    <Label Text="{Loc 'health-analyzer-window-entity-blood-level-text'}" />
+                    <Label Name="BloodLabel" />
+                    <Label Text="{Loc 'health-analyzer-window-entity-damage-total-text'}" />
+                    <Label Name="DamageLabel" />
+                </GridContainer>
+            </BoxContainer>
+
+            <PanelContainer Name="AlertsDivider" Visible="False" StyleClasses="LowDivider" />
+
+            <BoxContainer Name="AlertsContainer" Visible="False" Margin="0 5" Orientation="Horizontal"
+                          HorizontalExpand="True" HorizontalAlignment="Center">
+
             </BoxContainer>
+
+            <PanelContainer StyleClasses="LowDivider" />
+
             <BoxContainer
                 Name="GroupsContainer"
+                Margin="0 5 0 5"
                 Orientation="Vertical">
             </BoxContainer>
+
         </BoxContainer>
     </ScrollContainer>
 </controls:FancyWindow>
index fcf6d4551fdd64ea8a74f9d6ce2ee247cad5b107..9b96f5d3fe9284284dce9153efebd3693e00259a 100644 (file)
@@ -1,12 +1,20 @@
 using System.Linq;
 using System.Numerics;
+using Content.Client.Message;
 using Content.Shared.Atmos;
 using Content.Client.UserInterface.Controls;
+using Content.Shared.Alert;
 using Content.Shared.Damage;
 using Content.Shared.Damage.Prototypes;
 using Content.Shared.FixedPoint;
+using Content.Shared.Humanoid;
+using Content.Shared.Humanoid.Prototypes;
 using Content.Shared.IdentityManagement;
+using Content.Shared.Inventory;
 using Content.Shared.MedicalScanner;
+using Content.Shared.Mobs;
+using Content.Shared.Mobs.Components;
+using Content.Shared.Mobs.Systems;
 using Content.Shared.Nutrition.Components;
 using Robust.Client.AutoGenerated;
 using Robust.Client.UserInterface.XAML;
@@ -28,9 +36,6 @@ namespace Content.Client.HealthAnalyzer.UI
         private readonly IPrototypeManager _prototypes;
         private readonly IResourceCache _cache;
 
-        private const int AnalyzerHeight = 430;
-        private const int AnalyzerWidth = 300;
-
         public HealthAnalyzerWindow()
         {
             RobustXamlLoader.Load(this);
@@ -44,8 +49,6 @@ namespace Content.Client.HealthAnalyzer.UI
 
         public void Populate(HealthAnalyzerScannedUserMessage msg)
         {
-            GroupsContainer.RemoveAllChildren();
-
             var target = _entityManager.GetEntity(msg.TargetEntity);
 
             if (target == null
@@ -57,82 +60,96 @@ namespace Content.Client.HealthAnalyzer.UI
 
             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);
-            }
+            // Scan Mode
 
-            if (msg.ScanMode.HasValue)
-            {
-                ScanModePanel.Visible = true;
-                ScanModeText.Text = Loc.GetString(msg.ScanMode.Value ? "health-analyzer-window-scan-mode-active" : "health-analyzer-window-scan-mode-inactive");
-                ScanModeText.FontColorOverride = msg.ScanMode.Value ? Color.Green : Color.Red;
-            }
-            else
-            {
-                ScanModePanel.Visible = false;
-            }
+            ScanModeLabel.Text = msg.ScanMode.HasValue
+                ? msg.ScanMode.Value
+                    ? Loc.GetString("health-analyzer-window-scan-mode-active")
+                    : Loc.GetString("health-analyzer-window-scan-mode-inactive")
+                : Loc.GetString("health-analyzer-window-entity-unknown-text");
+
+            ScanModeLabel.FontColorOverride = msg.ScanMode.HasValue && msg.ScanMode.Value ? Color.Green : Color.Red;
+
+            // Patient Information
+
+            SpriteView.SetEntity(target.Value);
+
+            var name = new FormattedMessage();
+            name.PushColor(Color.White);
+            name.AddText(_entityManager.HasComponent<MetaDataComponent>(target.Value)
+                ? Identity.Name(target.Value, _entityManager)
+                : Loc.GetString("health-analyzer-window-entity-unknown-text"));
+            NameLabel.SetMessage(name);
+
+            SpeciesLabel.Text =
+                _entityManager.TryGetComponent<HumanoidAppearanceComponent>(target.Value,
+                    out var humanoidAppearanceComponent)
+                    ? Loc.GetString(_prototypes.Index<SpeciesPrototype>(humanoidAppearanceComponent.Species).Name)
+                    : Loc.GetString("health-analyzer-window-entity-unknown-species-text");
+
+            // Basic Diagnostic
+
+            TemperatureLabel.Text = !float.IsNaN(msg.Temperature)
+                ? $"{msg.Temperature - Atmospherics.T0C:F1} °C ({msg.Temperature:F1} K)"
+                : Loc.GetString("health-analyzer-window-entity-unknown-value-text");
+
+            BloodLabel.Text = !float.IsNaN(msg.BloodLevel)
+                ? $"{msg.BloodLevel * 100:F1} %"
+                : Loc.GetString("health-analyzer-window-entity-unknown-value-text");
 
-            PatientName.Text = Loc.GetString(
-                "health-analyzer-window-entity-health-text",
-                ("entityName", entityName)
-            );
+            StatusLabel.Text =
+                _entityManager.TryGetComponent<MobStateComponent>(target.Value, out var mobStateComponent)
+                    ? GetStatus(mobStateComponent.CurrentState)
+                    : Loc.GetString("health-analyzer-window-entity-unknown-text");
 
-            Temperature.Text = Loc.GetString("health-analyzer-window-entity-temperature-text",
-                ("temperature", float.IsNaN(msg.Temperature) ? "N/A" : $"{msg.Temperature - Atmospherics.T0C:F1} °C ({msg.Temperature:F1} K)")
-            );
+            // Total Damage
 
-            BloodLevel.Text = Loc.GetString("health-analyzer-window-entity-blood-level-text",
-                ("bloodLevel", float.IsNaN(msg.BloodLevel) ? "N/A" : $"{msg.BloodLevel * 100:F1} %")
-            );
+            DamageLabel.Text = damageable.TotalDamage.ToString();
+
+            // Alerts
+
+            AlertsDivider.Visible = msg.Bleeding == true;
+            AlertsContainer.Visible = msg.Bleeding == true;
 
             if (msg.Bleeding == true)
             {
-                Bleeding.Text = Loc.GetString("health-analyzer-window-entity-bleeding-text");
-                Bleeding.FontColorOverride = Color.Red;
-            }
-            else
-            {
-                Bleeding.Text = string.Empty;  // Clear the text
+                AlertsContainer.DisposeAllChildren();
+                AlertsContainer.AddChild(new Label
+                {
+                    Text = Loc.GetString("health-analyzer-window-entity-bleeding-text"),
+                    FontColorOverride = Color.Red,
+                });
             }
 
-            patientDamageAmount.Text = Loc.GetString(
-                "health-analyzer-window-entity-damage-total-text",
-                ("amount", damageable.TotalDamage)
-            );
+            // Damage Groups
 
             var damageSortedGroups =
-                damageable.DamagePerGroup.OrderBy(damage => damage.Value)
+                damageable.DamagePerGroup.OrderByDescending(damage => damage.Value)
                     .ToDictionary(x => x.Key, x => x.Value);
+
             IReadOnlyDictionary<string, FixedPoint2> damagePerType = damageable.Damage.DamageDict;
 
             DrawDiagnosticGroups(damageSortedGroups, damagePerType);
+        }
 
-            if (_entityManager.TryGetComponent(target, out HungerComponent? hunger)
-                && hunger.StarvationDamage != null
-                && hunger.CurrentThreshold <= HungerThreshold.Starving)
+        private static string GetStatus(MobState mobState)
+        {
+            return mobState switch
             {
-                var box = new Control { Margin = new Thickness(0, 0, 0, 15) };
-
-                box.AddChild(CreateDiagnosticGroupTitle(
-                    Loc.GetString("health-analyzer-window-malnutrition"),
-                    "malnutrition"));
-
-                GroupsContainer.AddChild(box);
-            }
-
-            SetHeight = AnalyzerHeight;
-            SetWidth = AnalyzerWidth;
+                MobState.Alive => Loc.GetString("health-analyzer-window-entity-alive-text"),
+                MobState.Critical => Loc.GetString("health-analyzer-window-entity-critical-text"),
+                MobState.Dead => Loc.GetString("health-analyzer-window-entity-dead-text"),
+                _ => Loc.GetString("health-analyzer-window-entity-unknown-text"),
+            };
         }
 
         private void DrawDiagnosticGroups(
-            Dictionary<string, FixedPoint2> groups, IReadOnlyDictionary<string, FixedPoint2> damageDict)
+            Dictionary<string, FixedPoint2> groups,
+            IReadOnlyDictionary<string, FixedPoint2> damageDict)
         {
-            HashSet<string> shownTypes = new();
+            GroupsContainer.RemoveAllChildren();
 
-            // Show the total damage and type breakdown for each damage group.
-            foreach (var (damageGroupId, damageAmount) in groups.Reverse())
+            foreach (var (damageGroupId, damageAmount) in groups)
             {
                 if (damageAmount == 0)
                     continue;
@@ -145,7 +162,6 @@ namespace Content.Client.HealthAnalyzer.UI
 
                 var groupContainer = new BoxContainer
                 {
-                    Margin = new Thickness(0, 0, 0, 15),
                     Align = BoxContainer.AlignMode.Begin,
                     Orientation = BoxContainer.LayoutOrientation.Vertical,
                 };
@@ -159,23 +175,16 @@ namespace Content.Client.HealthAnalyzer.UI
 
                 foreach (var type in group.DamageTypes)
                 {
-                    if (damageDict.TryGetValue(type, out var typeAmount) && typeAmount > 0)
-                    {
-                        // 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", _prototypes.Index<DamageTypePrototype>(type).LocalizedName),
-                            ("amount", typeAmount)
-                        );
-
-                        groupContainer.AddChild(CreateDiagnosticItemLabel(damageString.Insert(0, "- ")));
-                    }
+                    if (!damageDict.TryGetValue(type, out var typeAmount) || typeAmount <= 0)
+                        continue;
+
+                    var damageString = Loc.GetString(
+                        "health-analyzer-window-damage-type-text",
+                        ("damageType", _prototypes.Index<DamageTypePrototype>(type).LocalizedName),
+                        ("amount", typeAmount)
+                    );
+
+                    groupContainer.AddChild(CreateDiagnosticItemLabel(damageString.Insert(0, " · ")));
                 }
             }
         }
@@ -198,7 +207,6 @@ namespace Content.Client.HealthAnalyzer.UI
         {
             return new Label
             {
-                Margin = new Thickness(2, 2),
                 Text = text,
             };
         }
@@ -207,13 +215,13 @@ namespace Content.Client.HealthAnalyzer.UI
         {
             var rootContainer = new BoxContainer
             {
+                Margin = new Thickness(0, 6, 0, 0),
                 VerticalAlignment = VAlignment.Bottom,
-                Orientation = BoxContainer.LayoutOrientation.Horizontal
+                Orientation = BoxContainer.LayoutOrientation.Horizontal,
             };
 
             rootContainer.AddChild(new TextureRect
             {
-                Margin = new Thickness(0, 3),
                 SetSize = new Vector2(30, 30),
                 Texture = GetTexture(id.ToLower())
             });
index 121e50b923e6c3e67ccfd2de11060bcd90e0f159..fe1f92e91406d11609184046f25bf19b49f24748 100644 (file)
@@ -1,18 +1,24 @@
 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}
-health-analyzer-window-entity-bleeding-text = Patient is bleeding!
-health-analyzer-window-entity-damage-total-text = Total Damage: {$amount}
+health-analyzer-window-entity-unknown-text = Unknown
+health-analyzer-window-entity-unknown-species-text = Non-Humanoid
+health-analyzer-window-entity-unknown-value-text = N/A
+
+health-analyzer-window-entity-alive-text = Alive
+health-analyzer-window-entity-dead-text = Dead
+health-analyzer-window-entity-critical-text = Critical
+
+health-analyzer-window-entity-temperature-text = Temperature:
+health-analyzer-window-entity-blood-level-text = Blood Level:
+health-analyzer-window-entity-status-text = Status:
+health-analyzer-window-entity-damage-total-text = Total Damage:
+
 health-analyzer-window-damage-group-text = {$damageGroup}: {$amount}
 health-analyzer-window-damage-type-text = {$damageType}: {$amount}
-health-analyzer-window-damage-type-duplicate-text = {$damageType}: {$amount} (duplicate)
 
-health-analyzer-window-scan-mode-text = Scan Mode:
-health-analyzer-window-scan-mode-active = ACTIVE
-health-analyzer-window-scan-mode-inactive = INACTIVE
+health-analyzer-window-entity-bleeding-text = Patient is bleeding!
 
-health-analyzer-window-malnutrition = Severely malnourished
+health-analyzer-window-scan-mode-text = Scan Mode:
+health-analyzer-window-scan-mode-active = Active
+health-analyzer-window-scan-mode-inactive = Inactive
 
 health-analyzer-popup-scan-target = {CAPITALIZE(THE($user))} is trying to scan you!