From 7f43f38cc71466983cbecc4fa14bc3580a0b6d35 Mon Sep 17 00:00:00 2001 From: Fishfish458 <47410468+Fishfish458@users.noreply.github.com> Date: Mon, 7 Mar 2022 21:45:52 -0600 Subject: [PATCH] Add health analyzer and medical scanner ECS (#6907) Co-authored-by: fishfish458 Co-authored-by: metalgearsloth --- Content.Client/Entry/IgnoredComponents.cs | 1 + .../UI/HealthAnalyzerBoundUserInterface.cs | 51 +++ .../UI/HealthAnalyzerWindow.xaml | 9 + .../UI/HealthAnalyzerWindow.xaml.cs | 74 +++++ .../MedicalScannerVisualizer.cs | 8 +- .../UI/MedicalScannerBoundUserInterface.cs | 13 +- .../UI/MedicalScannerWindow.xaml | 9 +- .../UI/MedicalScannerWindow.xaml.cs | 63 +--- .../Components/HealthAnalyzerComponent.cs | 27 ++ .../Components/MedicalScannerComponent.cs | 235 +------------- .../Medical/HealthAnalyzerSystem.cs | 120 ++++++++ .../Medical/MedicalScannerSystem.cs | 290 ++++++++++++++++-- .../SharedHealthAnalyzerComponent.cs | 27 ++ .../SharedMedicalScannerComponent.cs | 40 +-- .../components/health-analyzer-component.ftl | 6 + .../components/medical-scanner-component.ftl | 7 +- .../Catalog/Fills/Lockers/medical.yml | 15 +- .../VendingMachines/Inventories/medical.yml | 1 + .../Specific/Medical/healthanalyzer.yml | 17 + .../Entities/Structures/Machines/lathe.yml | 1 + .../Prototypes/Recipes/Lathes/medical.yml | 9 + .../Medical/healthanalyzer.rsi/analyzer.png | Bin 0 -> 1059 bytes .../Medical/healthanalyzer.rsi/icon.png | Bin 0 -> 5806 bytes .../Medical/healthanalyzer.rsi/meta.json | 33 ++ 24 files changed, 668 insertions(+), 388 deletions(-) create mode 100644 Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs create mode 100644 Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml create mode 100644 Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs create mode 100644 Content.Server/Medical/Components/HealthAnalyzerComponent.cs create mode 100644 Content.Server/Medical/HealthAnalyzerSystem.cs create mode 100644 Content.Shared/MedicalScanner/SharedHealthAnalyzerComponent.cs create mode 100644 Resources/Locale/en-US/medical/components/health-analyzer-component.ftl create mode 100644 Resources/Prototypes/Entities/Objects/Specific/Medical/healthanalyzer.yml create mode 100644 Resources/Textures/Objects/Specific/Medical/healthanalyzer.rsi/analyzer.png create mode 100644 Resources/Textures/Objects/Specific/Medical/healthanalyzer.rsi/icon.png create mode 100644 Resources/Textures/Objects/Specific/Medical/healthanalyzer.rsi/meta.json diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index fa31384c4a..3667507aa0 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -317,6 +317,7 @@ namespace Content.Client.Entry "EnergySword", "DoorRemote", "InteractionPopup", + "HealthAnalyzer" }; } } diff --git a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs new file mode 100644 index 0000000000..c7bfffe41d --- /dev/null +++ b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerBoundUserInterface.cs @@ -0,0 +1,51 @@ +using JetBrains.Annotations; +using Robust.Client.GameObjects; + +using static Content.Shared.MedicalScanner.SharedHealthAnalyzerComponent; + +namespace Content.Client.HealthAnalyzer.UI +{ + [UsedImplicitly] + public sealed class HealthAnalyzerBoundUserInterface : BoundUserInterface + { + private HealthAnalyzerWindow? _window; + + public HealthAnalyzerBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) + { + } + + protected override void Open() + { + base.Open(); + _window = new HealthAnalyzerWindow + { + Title = IoCManager.Resolve().GetComponent(Owner.Owner).EntityName, + }; + _window.OnClose += Close; + _window.OpenCentered(); + } + + protected override void ReceiveMessage(BoundUserInterfaceMessage message) + { + if (_window == null) + return; + + if (message is not HealthAnalyzerScannedUserMessage cast) + return; + + _window.Populate(cast); + } + + protected override void Dispose(bool disposing) + { + base.Dispose(disposing); + if (!disposing) + return; + + if (_window != null) + _window.OnClose -= Close; + + _window?.Dispose(); + } + } +} diff --git a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml new file mode 100644 index 0000000000..1f64feec2b --- /dev/null +++ b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml @@ -0,0 +1,9 @@ + + + + diff --git a/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs new file mode 100644 index 0000000000..7d7bbc1232 --- /dev/null +++ b/Content.Client/HealthAnalyzer/UI/HealthAnalyzerWindow.xaml.cs @@ -0,0 +1,74 @@ +using System.Text; +using Robust.Client.AutoGenerated; +using Robust.Client.UserInterface.CustomControls; +using Robust.Client.UserInterface.XAML; +using Content.Shared.Damage.Prototypes; +using Content.Shared.FixedPoint; +using Robust.Shared.Prototypes; +using Content.Shared.Damage; + +using static Content.Shared.MedicalScanner.SharedHealthAnalyzerComponent; + +namespace Content.Client.HealthAnalyzer.UI +{ + [GenerateTypedNameReferences] + public sealed partial class HealthAnalyzerWindow : DefaultWindow + { + public HealthAnalyzerWindow() + { + RobustXamlLoader.Load(this); + } + + public void Populate(HealthAnalyzerScannedUserMessage msg) + { + var text = new StringBuilder(); + var entities = IoCManager.Resolve(); + + if (msg.TargetEntity != null && entities.TryGetComponent(msg.TargetEntity, out var damageable)) + { + string entityName = "Unknown"; + if (msg.TargetEntity != null && entities.TryGetComponent(msg.TargetEntity.Value, out var metaData)) + entityName = metaData.EntityName; + + IReadOnlyDictionary DamagePerGroup = damageable.DamagePerGroup; + IReadOnlyDictionary DamagePerType = damageable.Damage.DamageDict; + + text.Append($"{Loc.GetString("health-analyzer-window-entity-health-text", ("entityName", entityName))}\n"); + + text.Append($"{Loc.GetString("health-analyzer-window-entity-damage-total-text", ("amount", damageable.TotalDamage))}\n"); + + HashSet shownTypes = new(); + + var protos = IoCManager.Resolve(); + + // Show the total damage and type breakdown for each damage group. + foreach (var (damageGroupId, damageAmount) in DamagePerGroup) + { + text.Append($"\n{Loc.GetString("health-analyzer-window-damage-group-text", ("damageGroup", damageGroupId), ("amount", damageAmount))}"); + // Show the damage for each type in that group. + var group = protos.Index(damageGroupId); + foreach (var type in group.DamageTypes) + { + 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)) + { + shownTypes.Add(type); + text.Append($"\n- {Loc.GetString("health-analyzer-window-damage-type-text", ("damageType", type), ("amount", typeAmount))}"); + } + } + } + text.AppendLine(); + } + Diagnostics.Text = text.ToString(); + SetSize = (250, 600); + } + else + { + Diagnostics.Text = Loc.GetString("health-analyzer-window-no-patient-data-text"); + SetSize = (250, 100); + } + } + } +} diff --git a/Content.Client/MedicalScanner/MedicalScannerVisualizer.cs b/Content.Client/MedicalScanner/MedicalScannerVisualizer.cs index bb0f9e34f3..0e3d2bcf2f 100644 --- a/Content.Client/MedicalScanner/MedicalScannerVisualizer.cs +++ b/Content.Client/MedicalScanner/MedicalScannerVisualizer.cs @@ -27,10 +27,10 @@ namespace Content.Client.MedicalScanner { case Off: return "closed"; case Open: return "open"; - case Red: return "closed"; - case Death: return "closed"; + case Red: return "occupied"; + case Death: return "occupied"; case Green: return "occupied"; - case Yellow: return "closed"; + case Yellow: return "occupied"; default: throw new ArgumentOutOfRangeException(nameof(status), status, "unknown MedicalScannerStatus"); } @@ -43,7 +43,7 @@ namespace Content.Client.MedicalScanner case Off: return "off_unlit"; case Open: return "idle_unlit"; case Red: return "red_unlit"; - case Death: return "red_unlit"; + case Death: return "off_unlit"; case Green: return "idle_unlit"; case Yellow: return "maint_unlit"; default: diff --git a/Content.Client/MedicalScanner/UI/MedicalScannerBoundUserInterface.cs b/Content.Client/MedicalScanner/UI/MedicalScannerBoundUserInterface.cs index 3807a9b9ac..175ece10d5 100644 --- a/Content.Client/MedicalScanner/UI/MedicalScannerBoundUserInterface.cs +++ b/Content.Client/MedicalScanner/UI/MedicalScannerBoundUserInterface.cs @@ -1,7 +1,6 @@ using JetBrains.Annotations; using Robust.Client.GameObjects; -using Robust.Shared.GameObjects; -using Robust.Shared.IoC; + using static Content.Shared.MedicalScanner.SharedMedicalScannerComponent; namespace Content.Client.MedicalScanner.UI @@ -23,7 +22,7 @@ namespace Content.Client.MedicalScanner.UI Title = IoCManager.Resolve().GetComponent(Owner.Owner).EntityName, }; _window.OnClose += Close; - _window.ScanButton.OnPressed += _ => SendMessage(new UiButtonPressedMessage(UiButton.ScanDNA)); + _window.ScanButton.OnPressed += _ => SendMessage(new ScanButtonPressedMessage()); _window.OpenCentered(); } @@ -31,7 +30,10 @@ namespace Content.Client.MedicalScanner.UI { base.UpdateState(state); - _window?.Populate((MedicalScannerBoundUserInterfaceState) state); + if (state is not MedicalScannerBoundUserInterfaceState cast) + return; + + _window?.Populate(cast); } protected override void Dispose(bool disposing) @@ -40,6 +42,9 @@ namespace Content.Client.MedicalScanner.UI if (!disposing) return; + if (_window != null) + _window.OnClose -= Close; + _window?.Dispose(); } } diff --git a/Content.Client/MedicalScanner/UI/MedicalScannerWindow.xaml b/Content.Client/MedicalScanner/UI/MedicalScannerWindow.xaml index 54f33aa0f7..90bc8f8595 100644 --- a/Content.Client/MedicalScanner/UI/MedicalScannerWindow.xaml +++ b/Content.Client/MedicalScanner/UI/MedicalScannerWindow.xaml @@ -1,10 +1,11 @@  + MinSize="200 100" + SetSize="200 100"> +