1
0

HealthAnalyzerWindow.xaml.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354
  1. using System.Linq;
  2. using System.Numerics;
  3. using Content.Client.Message;
  4. using Content.Shared.Atmos;
  5. using Content.Client.UserInterface.Controls;
  6. using Content.Shared.Alert;
  7. using Content.Shared._Shitmed.Targeting; // Shitmed
  8. using Content.Shared.Damage;
  9. using Content.Shared.Damage.Prototypes;
  10. using Content.Shared.FixedPoint;
  11. using Content.Shared.Humanoid;
  12. using Content.Shared.Humanoid.Prototypes;
  13. using Content.Shared.IdentityManagement;
  14. using Content.Shared.Inventory;
  15. using Content.Shared.MedicalScanner;
  16. using Content.Shared.Mobs;
  17. using Content.Shared.Mobs.Components;
  18. using Content.Shared.Mobs.Systems;
  19. using Content.Shared.Nutrition.Components;
  20. using Robust.Client.AutoGenerated;
  21. using Robust.Client.UserInterface.XAML;
  22. using Robust.Client.GameObjects;
  23. using Robust.Client.Graphics;
  24. using Robust.Client.UserInterface.Controls;
  25. using Robust.Client.ResourceManagement;
  26. using Robust.Client.UserInterface;
  27. using Robust.Shared.Prototypes;
  28. using Robust.Shared.Utility;
  29. namespace Content.Client.HealthAnalyzer.UI
  30. {
  31. [GenerateTypedNameReferences]
  32. public sealed partial class HealthAnalyzerWindow : FancyWindow
  33. {
  34. private readonly IEntityManager _entityManager;
  35. private readonly SpriteSystem _spriteSystem;
  36. private readonly IPrototypeManager _prototypes;
  37. private readonly IResourceCache _cache;
  38. // Shitmed Change Start
  39. public event Action<TargetBodyPart?, EntityUid>? OnBodyPartSelected;
  40. private EntityUid _spriteViewEntity;
  41. [ValidatePrototypeId<EntityPrototype>]
  42. private readonly EntProtoId _bodyView = "AlertSpriteView";
  43. private readonly Dictionary<TargetBodyPart, TextureButton> _bodyPartControls;
  44. private EntityUid? _target;
  45. // Shitmed Change End
  46. public HealthAnalyzerWindow()
  47. {
  48. RobustXamlLoader.Load(this);
  49. var dependencies = IoCManager.Instance!;
  50. _entityManager = dependencies.Resolve<IEntityManager>();
  51. _spriteSystem = _entityManager.System<SpriteSystem>();
  52. _prototypes = dependencies.Resolve<IPrototypeManager>();
  53. _cache = dependencies.Resolve<IResourceCache>();
  54. // Shitmed Change Start
  55. _bodyPartControls = new Dictionary<TargetBodyPart, TextureButton>
  56. {
  57. { TargetBodyPart.Head, HeadButton },
  58. { TargetBodyPart.Torso, ChestButton },
  59. { TargetBodyPart.Groin, GroinButton },
  60. { TargetBodyPart.LeftArm, LeftArmButton },
  61. { TargetBodyPart.LeftHand, LeftHandButton },
  62. { TargetBodyPart.RightArm, RightArmButton },
  63. { TargetBodyPart.RightHand, RightHandButton },
  64. { TargetBodyPart.LeftLeg, LeftLegButton },
  65. { TargetBodyPart.LeftFoot, LeftFootButton },
  66. { TargetBodyPart.RightLeg, RightLegButton },
  67. { TargetBodyPart.RightFoot, RightFootButton },
  68. };
  69. foreach (var bodyPartButton in _bodyPartControls)
  70. {
  71. bodyPartButton.Value.MouseFilter = MouseFilterMode.Stop;
  72. bodyPartButton.Value.OnPressed += _ => SetActiveBodyPart(bodyPartButton.Key, bodyPartButton.Value);
  73. }
  74. ReturnButton.OnPressed += _ => ResetBodyPart();
  75. // Shitmed Change End
  76. }
  77. // Shitmed Change Start
  78. public void SetActiveBodyPart(TargetBodyPart part, TextureButton button)
  79. {
  80. if (_target == null)
  81. return;
  82. // Bit of the ole shitcode until we have Groins in the prototypes.
  83. OnBodyPartSelected?.Invoke(part == TargetBodyPart.Groin ? TargetBodyPart.Torso : part, _target.Value);
  84. }
  85. public void ResetBodyPart()
  86. {
  87. if (_target == null)
  88. return;
  89. OnBodyPartSelected?.Invoke(null, _target.Value);
  90. }
  91. public void SetActiveButtons(bool isHumanoid)
  92. {
  93. foreach (var button in _bodyPartControls)
  94. button.Value.Visible = isHumanoid;
  95. }
  96. // Not all of this function got messed with, but it was spread enough to warrant being covered entirely by a Shitmed Change
  97. public void Populate(HealthAnalyzerScannedUserMessage msg)
  98. {
  99. // Start-Shitmed
  100. _target = _entityManager.GetEntity(msg.TargetEntity);
  101. EntityUid? part = msg.Part != null ? _entityManager.GetEntity(msg.Part.Value) : null;
  102. var isPart = part != null;
  103. if (_target == null
  104. || !_entityManager.TryGetComponent<DamageableComponent>(isPart ? part : _target, out var damageable))
  105. {
  106. NoPatientDataText.Visible = true;
  107. return;
  108. }
  109. SetActiveButtons(_entityManager.HasComponent<TargetingComponent>(_target.Value));
  110. ReturnButton.Visible = isPart;
  111. PartNameLabel.Visible = isPart;
  112. if (part != null)
  113. PartNameLabel.Text = _entityManager.HasComponent<MetaDataComponent>(part)
  114. ? Identity.Name(part.Value, _entityManager)
  115. : Loc.GetString("health-analyzer-window-entity-unknown-value-text");
  116. NoPatientDataText.Visible = false;
  117. // Scan Mode
  118. ScanModeLabel.Text = msg.ScanMode.HasValue
  119. ? msg.ScanMode.Value
  120. ? Loc.GetString("health-analyzer-window-scan-mode-active")
  121. : Loc.GetString("health-analyzer-window-scan-mode-inactive")
  122. : Loc.GetString("health-analyzer-window-entity-unknown-text");
  123. ScanModeLabel.FontColorOverride = msg.ScanMode.HasValue && msg.ScanMode.Value ? Color.Green : Color.Red;
  124. // Patient Information
  125. SpriteView.SetEntity(SetupIcon(msg.Body) ?? _target.Value);
  126. SpriteView.Visible = msg.ScanMode.HasValue && msg.ScanMode.Value;
  127. PartView.Visible = SpriteView.Visible;
  128. NoDataTex.Visible = !SpriteView.Visible;
  129. var name = new FormattedMessage();
  130. name.PushColor(Color.White);
  131. name.AddText(_entityManager.HasComponent<MetaDataComponent>(_target.Value)
  132. ? Identity.Name(_target.Value, _entityManager)
  133. : Loc.GetString("health-analyzer-window-entity-unknown-text"));
  134. NameLabel.SetMessage(name);
  135. SpeciesLabel.Text =
  136. _entityManager.TryGetComponent<HumanoidAppearanceComponent>(_target.Value,
  137. out var humanoidAppearanceComponent)
  138. ? Loc.GetString(_prototypes.Index<SpeciesPrototype>(humanoidAppearanceComponent.Species).Name)
  139. : Loc.GetString("health-analyzer-window-entity-unknown-species-text");
  140. // Basic Diagnostic
  141. TemperatureLabel.Text = !float.IsNaN(msg.Temperature)
  142. ? $"{msg.Temperature - Atmospherics.T0C:F1} °C ({msg.Temperature:F1} K)"
  143. : Loc.GetString("health-analyzer-window-entity-unknown-value-text");
  144. BloodLabel.Text = !float.IsNaN(msg.BloodLevel)
  145. ? $"{msg.BloodLevel * 100:F1} %"
  146. : Loc.GetString("health-analyzer-window-entity-unknown-value-text");
  147. StatusLabel.Text =
  148. _entityManager.TryGetComponent<MobStateComponent>(_target.Value, out var mobStateComponent)
  149. ? GetStatus(mobStateComponent.CurrentState)
  150. : Loc.GetString("health-analyzer-window-entity-unknown-text");
  151. // Total Damage
  152. DamageLabel.Text = damageable.TotalDamage.ToString();
  153. // Alerts
  154. var showAlerts = msg.Unrevivable == true || msg.Bleeding == true;
  155. AlertsDivider.Visible = showAlerts;
  156. AlertsContainer.Visible = showAlerts;
  157. if (showAlerts)
  158. AlertsContainer.DisposeAllChildren();
  159. if (msg.Unrevivable == true)
  160. AlertsContainer.AddChild(new RichTextLabel
  161. {
  162. Text = Loc.GetString("health-analyzer-window-entity-unrevivable-text"),
  163. Margin = new Thickness(0, 4),
  164. MaxWidth = 300
  165. });
  166. if (msg.Bleeding == true)
  167. AlertsContainer.AddChild(new RichTextLabel
  168. {
  169. Text = Loc.GetString("health-analyzer-window-entity-bleeding-text"),
  170. Margin = new Thickness(0, 4),
  171. MaxWidth = 300
  172. });
  173. // Damage Groups
  174. var damageSortedGroups =
  175. damageable.DamagePerGroup.OrderByDescending(damage => damage.Value)
  176. .ToDictionary(x => x.Key, x => x.Value);
  177. IReadOnlyDictionary<string, FixedPoint2> damagePerType = damageable.Damage.DamageDict;
  178. DrawDiagnosticGroups(damageSortedGroups, damagePerType);
  179. }
  180. // Shitmed Change End
  181. private static string GetStatus(MobState mobState)
  182. {
  183. return mobState switch
  184. {
  185. MobState.Alive => Loc.GetString("health-analyzer-window-entity-alive-text"),
  186. MobState.Critical => Loc.GetString("health-analyzer-window-entity-critical-text"),
  187. MobState.Dead => Loc.GetString("health-analyzer-window-entity-dead-text"),
  188. _ => Loc.GetString("health-analyzer-window-entity-unknown-text"),
  189. };
  190. }
  191. private void DrawDiagnosticGroups(
  192. Dictionary<string, FixedPoint2> groups,
  193. IReadOnlyDictionary<string, FixedPoint2> damageDict)
  194. {
  195. GroupsContainer.RemoveAllChildren();
  196. foreach (var (damageGroupId, damageAmount) in groups)
  197. {
  198. if (damageAmount == 0)
  199. continue;
  200. var groupTitleText = $"{Loc.GetString(
  201. "health-analyzer-window-damage-group-text",
  202. ("damageGroup", _prototypes.Index<DamageGroupPrototype>(damageGroupId).LocalizedName),
  203. ("amount", damageAmount)
  204. )}";
  205. var groupContainer = new BoxContainer
  206. {
  207. Align = BoxContainer.AlignMode.Begin,
  208. Orientation = BoxContainer.LayoutOrientation.Vertical,
  209. };
  210. groupContainer.AddChild(CreateDiagnosticGroupTitle(groupTitleText, damageGroupId));
  211. GroupsContainer.AddChild(groupContainer);
  212. // Show the damage for each type in that group.
  213. var group = _prototypes.Index<DamageGroupPrototype>(damageGroupId);
  214. foreach (var type in group.DamageTypes)
  215. {
  216. if (!damageDict.TryGetValue(type, out var typeAmount) || typeAmount <= 0)
  217. continue;
  218. var damageString = Loc.GetString(
  219. "health-analyzer-window-damage-type-text",
  220. ("damageType", _prototypes.Index<DamageTypePrototype>(type).LocalizedName),
  221. ("amount", typeAmount)
  222. );
  223. groupContainer.AddChild(CreateDiagnosticItemLabel(damageString.Insert(0, " · ")));
  224. }
  225. }
  226. }
  227. private Texture GetTexture(string texture)
  228. {
  229. var rsiPath = new ResPath("/Textures/Objects/Devices/health_analyzer.rsi");
  230. var rsiSprite = new SpriteSpecifier.Rsi(rsiPath, texture);
  231. var rsi = _cache.GetResource<RSIResource>(rsiSprite.RsiPath).RSI;
  232. if (!rsi.TryGetState(rsiSprite.RsiState, out var state))
  233. {
  234. rsiSprite = new SpriteSpecifier.Rsi(rsiPath, "unknown");
  235. }
  236. return _spriteSystem.Frame0(rsiSprite);
  237. }
  238. private static Label CreateDiagnosticItemLabel(string text)
  239. {
  240. return new Label
  241. {
  242. Text = text,
  243. };
  244. }
  245. private BoxContainer CreateDiagnosticGroupTitle(string text, string id)
  246. {
  247. var rootContainer = new BoxContainer
  248. {
  249. Margin = new Thickness(0, 6, 0, 0),
  250. VerticalAlignment = VAlignment.Bottom,
  251. Orientation = BoxContainer.LayoutOrientation.Horizontal,
  252. };
  253. rootContainer.AddChild(new TextureRect
  254. {
  255. SetSize = new Vector2(30, 30),
  256. Texture = GetTexture(id.ToLower())
  257. });
  258. rootContainer.AddChild(CreateDiagnosticItemLabel(text));
  259. return rootContainer;
  260. }
  261. // Shitmed Change Start
  262. /// <summary>
  263. /// Sets up the Body Doll using Alert Entity to use in Health Analyzer.
  264. /// </summary>
  265. private EntityUid? SetupIcon(Dictionary<TargetBodyPart, TargetIntegrity>? body)
  266. {
  267. if (body is null)
  268. return null;
  269. if (!_entityManager.Deleted(_spriteViewEntity))
  270. _entityManager.QueueDeleteEntity(_spriteViewEntity);
  271. _spriteViewEntity = _entityManager.Spawn(_bodyView);
  272. if (!_entityManager.TryGetComponent<SpriteComponent>(_spriteViewEntity, out var sprite))
  273. return null;
  274. int layer = 0;
  275. foreach (var (bodyPart, integrity) in body)
  276. {
  277. // TODO: PartStatusUIController and make it use layers instead of TextureRects when EE refactors alerts.
  278. string enumName = Enum.GetName(typeof(TargetBodyPart), bodyPart) ?? "Unknown";
  279. int enumValue = (int)integrity;
  280. var rsi = new SpriteSpecifier.Rsi(new ResPath($"/Textures/_Shitmed/Interface/Targeting/Status/{enumName.ToLowerInvariant()}.rsi"), $"{enumName.ToLowerInvariant()}_{enumValue}");
  281. // Shitcode with love from Russia :)
  282. if (!sprite.TryGetLayer(layer, out _))
  283. sprite.AddLayer(_spriteSystem.Frame0(rsi));
  284. else
  285. sprite.LayerSetTexture(layer, _spriteSystem.Frame0(rsi));
  286. sprite.LayerSetScale(layer, new Vector2(3f, 3f));
  287. layer++;
  288. }
  289. return _spriteViewEntity;
  290. }
  291. // Shitmed Change End
  292. }
  293. }