using Content.Client.UserInterface.Controls; using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; using Content.Shared._White.Medical.BodyScanner; using Robust.Client.AutoGenerated; using Robust.Client.Graphics; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Prototypes; namespace Content.Client._White.Medical.BodyScanner { [GenerateTypedNameReferences] public sealed partial class BodyScannerConsoleWindow : FancyWindow { [Dependency] private readonly IPrototypeManager _prototypeManager = default!; public event Action? OnScanButtonPressed; public event Action? OnPrintButtonPressed; public BodyScannerConsoleWindow() { IoCManager.InjectDependencies(this); RobustXamlLoader.Load(this); ScanButton.OnPressed += _ => OnScanButtonPressed?.Invoke(); NoDataScanButton.OnPressed += _ => OnScanButtonPressed?.Invoke(); PrintButton.OnPressed += _ => OnPrintButtonPressed?.Invoke(); } public void UpdateUserInterface(BodyScannerConsoleBoundUserInterfaceState state) { MainContainer.Visible = state.TargetEntityUid != null && !state.Scanning; NoDataContainer.Visible = !MainContainer.Visible; NoDataStatusLabel.Text = state.Scanning ? Loc.GetString("body-scanner-console-window-status-scanning") : " "; ScanButton.Disabled = !state.CanScan; PrintButton.Disabled = !state.CanPrint; NoDataScanButton.Disabled = !state.CanScan; NoDataScanProgressBar.Value = 1 - (float) (state.ScanTimeRemaining / state.ScanTotalTime); if (state.Scanning) return; EntityNameLabel.Text = state.EntityName; // First column TemperatureLabel.Text = $"{state.CurrentTemperature - 273f:F1} \u00B0C"; BloodLevelLabel.Text = $"{state.BloodSolution.FillFraction * 100:F1} %"; TotalDamageLabel.Text = state.TotalDamage.ToString(); AliveStatusLabel.Text = state.CurrentState switch { Shared.Mobs.MobState.Alive => Loc.GetString("body-scanner-console-window-current-alive-status-alive-text"), Shared.Mobs.MobState.Critical => Loc.GetString("body-scanner-console-window-current-alive-status-critical-text"), Shared.Mobs.MobState.Dead => Loc.GetString("body-scanner-console-window-current-alive-status-dead-text"), _ => Loc.GetString("body-scanner-console-window-no-data") }; HashSet shownTypes = new(); var protos = IoCManager.Resolve(); IReadOnlyDictionary damagePerGroup = state.DamagePerGroup; IReadOnlyDictionary damagePerType = state.DamageDict; DamageGroupsContainer.RemoveAllChildren(); // Show the total damage and type breakdown for each damage group. foreach (var (damageGroupId, damageAmount) in damagePerGroup) { var damageGroupTitle = Loc.GetString("health-analyzer-window-damage-group-" + damageGroupId, ("amount", damageAmount)); DamageGroupsContainer.AddChild(new GroupDamageCardComponent(damageGroupTitle, damageGroupId, damagePerType)); } // Second column. CurrentTemperature.Text = Loc.GetString("body-scanner-console-window-temperature-current-temperature-text", ("amount", $"{state.CurrentTemperature - 273:f1}")); HeatDamageThreshold.Text = Loc.GetString("body-scanner-console-window-temperature-heat-damage-threshold-temperature-text", ("amount", $"{state.HeatDamageThreshold - 273:f1}")); ColdDamageThreshold.Text = Loc.GetString("body-scanner-console-window-temperature-cold-damage-threshold-temperature-text", ("amount", $"{state.ColdDamageThreshold - 273:f1}")); CurrentSaturation.Text = Loc.GetString("body-scanner-console-window-saturation-current-saturation-text", ("amount", $"{state.Saturation:f1}")); MinimumSaturation.Text = Loc.GetString("body-scanner-console-window-saturation-maximum-saturation-text", ("amount", $"{state.MinSaturation:f1}")); MaximumSaturation.Text = Loc.GetString("body-scanner-console-window-saturation-minimum-saturation-text", ("amount", $"{state.MaxSaturation:f1}")); CurrentThirst.Text = Loc.GetString("body-scanner-console-window-thirst-current-thirst-text", ("amount", $"{state.CurrentThirst:f1}")); CurrentThirstStatus.Text = Loc.GetString("body-scanner-console-window-thirst-current-thirst-status-text", ("status", Loc.GetString("body-scanner-console-window-thirst-current-thirst-status-" + state.CurrentThirstThreshold))); CurrentHunger.Text = Loc.GetString("body-scanner-console-window-hunger-current-hunger-text", ("amount", $"{state.CurrentHunger:f1}")); CurrentHungerStatus.Text = Loc.GetString("body-scanner-console-window-hunger-current-hunger-status-text", ("status", Loc.GetString("body-scanner-console-window-hunger-current-hunger-status-" + state.CurrentHungerThreshold))); BloodSolutionVolume.Text = Loc.GetString("body-scanner-console-window-blood-solutions-volume-group-text", ("amount", $"{state.BloodSolution.Volume.Float():f1}"), ("maxAmount", $"{state.BloodSolution.MaxVolume.Float():f1}"), ("temperature", $"{state.BloodSolution.Temperature - 271:f1}")); BloodSolutionElements.RemoveAllChildren(); state.BloodSolution.Contents.ForEach(x => { _prototypeManager.TryIndex(x.Reagent.Prototype, out ReagentPrototype? proto); var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text"); BloodSolutionElements.AddChild(new Label() { Text = $"{name}: {x.Quantity}", FontColorOverride = Color.LightGray }); }); ChemicalSolutionVolume.Text = Loc.GetString("body-scanner-console-window-chemical-solutions-volume-group-text", ("amount", $"{state.ChemicalSolution.Volume.Float():f1}"), ("maxAmount", $"{state.ChemicalSolution.MaxVolume.Float():f1}"), ("temperature", $"{state.ChemicalSolution.Temperature - 271:f1}")); ChemicalSolutionElements.RemoveAllChildren(); state.ChemicalSolution.Contents.ForEach(x => { _prototypeManager.TryIndex(x.Reagent.Prototype, out ReagentPrototype? proto); var name = proto?.LocalizedName ?? Loc.GetString("chem-master-window-unknown-reagent-text"); ChemicalSolutionElements.AddChild(new Label() { Text = $"{name}: {x.Quantity}", FontColorOverride = Color.LightGray }); }); // Third column. if (state.DeadThreshold == FixedPoint2.Zero) state.DeadThreshold = 0.01; HealthBar.Value = 1 - (state.TotalDamage / state.DeadThreshold).Float(); HealthBar.ForegroundStyleBoxOverride = new StyleBoxFlat() { BackgroundColor = HealthBar.Value >= 0.5f ? GetColorLerp(Color.Yellow, Color.Green, (HealthBar.Value - 0.5f) * 2) : GetColorLerp(Color.Red, Color.Yellow, HealthBar.Value * 2) }; if (state.TargetEntityUid != null) EntityView.SetEntity(state.TargetEntityUid.Value); // Bottom row. Mind.Text = Loc.GetString("body-scanner-console-window-mind-text", ("value", state.HasMind ? Loc.GetString("body-scanner-console-window-mind-present-text") : Loc.GetString("body-scanner-console-window-mind-absent-text"))); DNA.Text = Loc.GetString("body-scanner-console-window-dna-text", ("value", $"{state.DNA}")); Fingerprint.Text = Loc.GetString("body-scanner-console-window-fingerprint-text", ("value", $"{state.Fingerprint}")); } /// /// Smooth transition from one color to another depending on the percentage. /// /// /// /// /// private Color GetColorLerp(Color startColor, Color endColor, float percentage) { var r = MathHelper.Lerp(startColor.R, endColor.R, percentage); var g = MathHelper.Lerp(startColor.G, endColor.G, percentage); var b = MathHelper.Lerp(startColor.B, endColor.B, percentage); var a = MathHelper.Lerp(startColor.A, endColor.A, percentage); return new Color(r, g, b, a); } } }