using System.Linq; using System.Numerics; using Content.Shared.Atmos; using Content.Client.UserInterface.Controls; using Content.Shared._Offbrand.Wounds; // Offbrand using Content.Shared.Damage.Components; 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.MedicalScanner; using Content.Shared.Mobs; using Content.Shared.Mobs.Components; using Robust.Client.AutoGenerated; 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 : FancyWindow { private readonly IEntityManager _entityManager; private readonly SpriteSystem _spriteSystem; private readonly IPrototypeManager _prototypes; private readonly IResourceCache _cache; // Begin Offbrand private readonly Tooltips.StatusTooltip _statusTooltip = new(); private readonly Tooltips.BrainHealthTooltip _brainHealthTooltip = new(); private readonly Tooltips.BloodPressureTooltip _bloodPressureTooltip = new(); private readonly Tooltips.HeartRateTooltip _heartRateTooltip = new(); private readonly Tooltips.HeartHealthTooltip _heartHealthTooltip = new(); private readonly Tooltips.LungHealthTooltip _lungHealthTooltip = new(); private readonly Tooltips.BloodTooltip _bloodTooltip = new(); private readonly Tooltips.TemperatureTooltip _temperatureTooltip = new(); private readonly Tooltips.DamageTooltip _damageTooltip = new(); private readonly Tooltips.SpO2Tooltip _spo2Tooltip = new(); private readonly Tooltips.EtCO2Tooltip _etco2Tooltip = new(); private readonly Tooltips.RespiratoryRateTooltip _respiratoryRateTooltip = new(); // End Offbrand public HealthAnalyzerWindow() { RobustXamlLoader.Load(this); var dependencies = IoCManager.Instance!; _entityManager = dependencies.Resolve(); _spriteSystem = _entityManager.System(); _prototypes = dependencies.Resolve(); _cache = dependencies.Resolve(); // Begin Offbrand StatusButton.TooltipSupplier = _ => _statusTooltip; BrainHealthButton.TooltipSupplier = _ => _brainHealthTooltip; BloodPressureButton.TooltipSupplier = _ => _bloodPressureTooltip; HeartRateButton.TooltipSupplier = _ => _heartRateTooltip; HeartHealthButton.TooltipSupplier = _ => _heartHealthTooltip; TemperatureButton.TooltipSupplier = _ => _temperatureTooltip; DamageButton.TooltipSupplier = _ => _damageTooltip; BloodButton.TooltipSupplier = _ => _bloodTooltip; LungHealthButton.TooltipSupplier = _ => _lungHealthTooltip; SpO2Button.TooltipSupplier = _ => _spo2Tooltip; EtCO2Button.TooltipSupplier = _ => _etco2Tooltip; RespiratoryRateButton.TooltipSupplier = _ => _respiratoryRateTooltip; // End Offbrand } public void Populate(HealthAnalyzerScannedUserMessage msg) { var target = _entityManager.GetEntity(msg.TargetEntity); if (target == null || !_entityManager.TryGetComponent(target, out var damageable)) { NoPatientDataText.Visible = true; return; } NoPatientDataText.Visible = false; // Begin Offbrand Tooltips _brainHealthTooltip.Update(msg); _heartRateTooltip.Update(msg); _heartHealthTooltip.Update(msg); _temperatureTooltip.Update(msg, (target.Value, _entityManager.GetComponentOrNull(target))); _spo2Tooltip.Update(msg); _etco2Tooltip.Update(msg); _respiratoryRateTooltip.Update(msg); // End Offbrand Tooltips // Scan Mode 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); SpriteView.Visible = msg.ScanMode.HasValue && msg.ScanMode.Value; NoDataTex.Visible = !SpriteView.Visible; 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"); StatusLabel.Text = _entityManager.TryGetComponent(target.Value, out var mobStateComponent) ? GetStatus(mobStateComponent.CurrentState) : Loc.GetString("health-analyzer-window-entity-unknown-text"); // Total Damage DamageLabel.Text = damageable.TotalDamage.ToString(); // Alerts var showAlerts = msg.Unrevivable == true || msg.Bleeding == true || msg.WoundableData?.NonMedicalReagents == true || msg.WoundableData?.Wounds != null; // Offbrand AlertsDivider.Visible = showAlerts; AlertsContainer.Visible = showAlerts; if (showAlerts) AlertsContainer.RemoveAllChildren(); if (msg.Unrevivable == true) AlertsContainer.AddChild(new RichTextLabel { Text = Loc.GetString("health-analyzer-window-entity-unrevivable-text"), Margin = new Thickness(0, 4), MaxWidth = 300 }); if (msg.Bleeding == true) AlertsContainer.AddChild(new RichTextLabel { Text = Loc.GetString("health-analyzer-window-entity-bleeding-text"), Margin = new Thickness(0, 4), MaxWidth = 300 }); // Begin Offbrand var showReagents = msg.WoundableData?.Reagents?.Count is { } count && count > 0; ReagentsDivider.Visible = showReagents; ReagentsContainer.Visible = showReagents; if (msg.WoundableData is { } woundable) { if (woundable.Wounds is not null) { foreach (var wound in woundable.Wounds) { AlertsContainer.AddChild(new RichTextLabel { Text = Loc.GetString(wound), Margin = new Thickness(0, 4), MaxWidth = 300 }); } } if (woundable.NonMedicalReagents) { AlertsContainer.AddChild(new RichTextLabel { Text = Loc.GetString("health-analyzer-window-entity-non-medical-reagents"), Margin = new Thickness(0, 4), MaxWidth = 300 }); } if (woundable.Reagents is { } reagents) { ReagentsContainer.DisposeAllChildren(); foreach (var (reagent, amounts) in reagents.OrderBy(kvp => _prototypes.Index(kvp.Key).LocalizedName)) { var (quantity, metabolites) = amounts; var proto = _prototypes.Index(reagent); ReagentsContainer.AddChild(new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Horizontal, HorizontalExpand = true, Children = { new PanelContainer { VerticalExpand = true, MinWidth = 4, PanelOverride = new StyleBoxFlat { BackgroundColor = proto.SubstanceColor }, Margin = new Thickness(4, 1), }, new Label { Text = proto.LocalizedName, HorizontalExpand = true, SizeFlagsStretchRatio = 3 }, new Label { Text = $"{metabolites}u", StyleClasses = { Content.Client.Stylesheets.StyleNano.StyleClassLabelSecondaryColor }, HorizontalExpand = true, SizeFlagsStretchRatio = 1 }, new Label { Text = $"{quantity}u", HorizontalExpand = true, SizeFlagsStretchRatio = 1 }, } }); } } BrainHealthText.Visible = true; BrainHealthLabel.Visible = true; BrainHealthLabel.Text = Loc.GetString("health-analyzer-window-entity-brain-health-value", ("value", $"{woundable.BrainHealth * 100:F1}")); BrainHealthButton.Visible = true; HeartHealthText.Visible = true; HeartHealthLabel.Visible = true; HeartHealthLabel.Text = Loc.GetString("health-analyzer-window-entity-heart-health-value", ("value", $"{woundable.HeartHealth * 100:F1}")); HeartHealthButton.Visible = true; HeartRateText.Visible = true; HeartRateLabel.Visible = true; HeartRateLabel.Text = Loc.GetString("health-analyzer-window-entity-heart-rate-value", ("value", woundable.HeartRate)); HeartRateButton.Visible = true; var (systolic, diastolic) = woundable.BloodPressure; BloodPressureText.Visible = true; BloodPressureLabel.Visible = true; BloodPressureLabel.Text = Loc.GetString("health-analyzer-window-entity-blood-pressure-value", ("systolic", systolic), ("diastolic", diastolic)); BloodPressureButton.Visible = true; LungHealthText.Visible = true; LungHealthLabel.Visible = true; LungHealthLabel.Text = Loc.GetString("health-analyzer-window-entity-lung-health-value", ("value", $"{woundable.LungHealth * 100:F1}")); LungHealthButton.Visible = true; SpO2Text.Visible = true; SpO2Text.Text = Loc.GetString("health-analyzer-window-entity-spo2-text", ("spo2", woundable.Spo2Name)); SpO2Label.Visible = true; SpO2Label.Text = Loc.GetString("health-analyzer-window-entity-spo2-value", ("value", $"{woundable.Spo2 * 100:F1}")); SpO2Button.Visible = true; EtCO2Text.Visible = true; EtCO2Text.Text = Loc.GetString("health-analyzer-window-entity-etco2-text", ("etco2", woundable.Etco2Name)); EtCO2Label.Visible = true; EtCO2Label.Text = Loc.GetString("health-analyzer-window-entity-etco2-value", ("value", $"{woundable.Etco2}")); EtCO2Button.Visible = true; RespiratoryRateText.Visible = true; RespiratoryRateLabel.Visible = true; RespiratoryRateLabel.Text = Loc.GetString("health-analyzer-window-entity-respiratory-rate-value", ("value", $"{woundable.RespiratoryRate}")); RespiratoryRateButton.Visible = true; BloodLabel.Visible = false; BloodText.Visible = false; BloodButton.Visible = false; } else { BrainHealthLabel.Visible = false; BloodPressureLabel.Visible = false; HeartRateLabel.Visible = false; HeartHealthLabel.Visible = false; LungHealthLabel.Visible = false; BrainHealthText.Visible = false; BloodPressureText.Visible = false; HeartRateText.Visible = false; HeartHealthText.Visible = false; LungHealthText.Visible = false; BrainHealthButton.Visible = false; BloodPressureButton.Visible = false; HeartRateButton.Visible = false; HeartHealthButton.Visible = false; LungHealthButton.Visible = false; SpO2Text.Visible = false; SpO2Label.Visible = false; SpO2Button.Visible = false; EtCO2Text.Visible = false; EtCO2Label.Visible = false; EtCO2Button.Visible = false; RespiratoryRateText.Visible = false; RespiratoryRateLabel.Visible = false; RespiratoryRateButton.Visible = false; BloodLabel.Visible = true; BloodText.Visible = true; BloodButton.Visible = true; } // End Offbrand // Damage Groups var damageSortedGroups = damageable.DamagePerGroup.OrderByDescending(damage => damage.Value) .ToDictionary(x => x.Key, x => x.Value); IReadOnlyDictionary damagePerType = damageable.Damage.DamageDict; DrawDiagnosticGroups(damageSortedGroups, damagePerType); } private static string GetStatus(MobState mobState) { return mobState switch { 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) { GroupsContainer.RemoveAllChildren(); foreach (var (damageGroupId, damageAmount) in groups) { if (damageAmount == 0) continue; var groupTitleText = $"{Loc.GetString( "health-analyzer-window-damage-group-text", ("damageGroup", _prototypes.Index(damageGroupId).LocalizedName), ("amount", damageAmount) )}"; var groupContainer = new BoxContainer { Align = BoxContainer.AlignMode.Begin, Orientation = BoxContainer.LayoutOrientation.Vertical, }; groupContainer.AddChild(CreateDiagnosticGroupTitle(groupTitleText, damageGroupId)); GroupsContainer.AddChild(groupContainer); // Show the damage for each type in that group. var group = _prototypes.Index(damageGroupId); foreach (var type in group.DamageTypes) { if (!damageDict.TryGetValue(type, out var typeAmount) || typeAmount <= 0) continue; var damageString = Loc.GetString( "health-analyzer-window-damage-type-text", ("damageType", _prototypes.Index(type).LocalizedName), ("amount", typeAmount) ); groupContainer.AddChild(CreateDiagnosticItemLabel(damageString.Insert(0, " · "))); } } // Begin Offbrand NoDamagesText.Visible = GroupsContainer.ChildCount == 0; // End Offbrand } 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(rsiSprite.RsiPath).RSI; if (!rsi.TryGetState(rsiSprite.RsiState, out var state)) { rsiSprite = new SpriteSpecifier.Rsi(rsiPath, "unknown"); } return _spriteSystem.Frame0(rsiSprite); } private static Label CreateDiagnosticItemLabel(string text) { return new Label { Text = text, }; } 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, }; rootContainer.AddChild(new TextureRect { SetSize = new Vector2(30, 30), Texture = GetTexture(id.ToLower()) }); rootContainer.AddChild(CreateDiagnosticItemLabel(text)); return rootContainer; } } }