diff --git a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml index 401f9768629..a096caa4cfd 100644 --- a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml +++ b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml @@ -1,48 +1,64 @@  + MaxHeight="525" + MinWidth="300"> diff --git a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs index 0cb3ad144d7..b3b3539c09f 100644 --- a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs +++ b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs @@ -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 @@ public sealed partial class HealthAnalyzerWindow : FancyWindow 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 @@ public HealthAnalyzerWindow() public void Populate(HealthAnalyzerScannedUserMessage msg) { - GroupsContainer.RemoveAllChildren(); - var target = _entityManager.GetEntity(msg.TargetEntity); if (target == null @@ -57,82 +60,96 @@ public void Populate(HealthAnalyzerScannedUserMessage msg) NoPatientDataText.Visible = false; - string entityName = Loc.GetString("health-analyzer-window-entity-unknown-text"); - if (_entityManager.HasComponent(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(target.Value) + ? Identity.Name(target.Value, _entityManager) + : Loc.GetString("health-analyzer-window-entity-unknown-text")); + NameLabel.SetMessage(name); + + SpeciesLabel.Text = + _entityManager.TryGetComponent(target.Value, + out var humanoidAppearanceComponent) + ? Loc.GetString(_prototypes.Index(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(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 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 groups, IReadOnlyDictionary damageDict) + Dictionary groups, + IReadOnlyDictionary damageDict) { - HashSet 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 @@ private void DrawDiagnosticGroups( var groupContainer = new BoxContainer { - Margin = new Thickness(0, 0, 0, 15), Align = BoxContainer.AlignMode.Begin, Orientation = BoxContainer.LayoutOrientation.Vertical, }; @@ -159,23 +175,16 @@ private void DrawDiagnosticGroups( 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", Loc.GetString("health-analyzer-window-damage-type-" + type)), - ("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", Loc.GetString("health-analyzer-window-damage-type-" + type)), + ("amount", typeAmount) + ); + + groupContainer.AddChild(CreateDiagnosticItemLabel(damageString.Insert(0, " · "))); } } } @@ -198,7 +207,6 @@ private static Label CreateDiagnosticItemLabel(string text) { return new Label { - Margin = new Thickness(2, 2), Text = text, }; } @@ -207,13 +215,13 @@ private BoxContainer CreateDiagnosticGroupTitle(string text, string id) { 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()) }); diff --git a/Resources/Locale/en-US/medical/components/health-analyzer-component.ftl b/Resources/Locale/en-US/medical/components/health-analyzer-component.ftl index 648db3f4ebd..7a2f8b8c6b7 100644 --- a/Resources/Locale/en-US/medical/components/health-analyzer-component.ftl +++ b/Resources/Locale/en-US/medical/components/health-analyzer-component.ftl @@ -1,18 +1,21 @@ 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-damage-group-Brute = Brute health-analyzer-window-damage-type-Blunt = Blunt health-analyzer-window-damage-type-Slash = Slash @@ -36,3 +39,11 @@ health-analyzer-window-damage-group-Genetic = Genetic health-analyzer-window-damage-type-Cellular = Cellular health-analyzer-window-malnutrition = Severely malnourished + +health-analyzer-window-entity-bleeding-text = Patient is bleeding! + +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! \ No newline at end of file