1
0

MedBookWindow.xaml.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362
  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.Damage;
  8. using Content.Shared.Damage.Prototypes;
  9. using Content.Shared.FixedPoint;
  10. using Content.Shared.Humanoid;
  11. using Content.Shared.Humanoid.Prototypes;
  12. using Content.Shared.IdentityManagement;
  13. using Content.Shared.Inventory;
  14. using Content.Shared.MedicalScanner;
  15. using Content.Shared.Mobs;
  16. using Content.Shared.Mobs.Components;
  17. using Content.Shared.Mobs.Systems;
  18. using Content.Shared.Nutrition.Components;
  19. using Robust.Client.AutoGenerated;
  20. using Robust.Client.UserInterface.XAML;
  21. using Robust.Client.GameObjects;
  22. using Robust.Client.Graphics;
  23. using Robust.Client.UserInterface.Controls;
  24. using Robust.Client.ResourceManagement;
  25. using Robust.Client.UserInterface;
  26. using Robust.Shared.Prototypes;
  27. using Robust.Shared.Utility;
  28. using Content.Shared._Shitmed.Targeting; // Shitmed
  29. namespace Content.Client.MedBook.UI
  30. {
  31. [GenerateTypedNameReferences]
  32. public sealed partial class MedBookWindow : 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 MedBookWindow()
  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. public void Populate(MedBookScannedUserMessage msg)
  97. {
  98. _target = _entityManager.GetEntity(msg.TargetEntity);
  99. EntityUid? part = msg.Part != null ? _entityManager.GetEntity(msg.Part.Value) : null;
  100. var isPart = part != null;
  101. if (_target == null
  102. || !_entityManager.TryGetComponent<DamageableComponent>(isPart ? part : _target, out var damageable))
  103. {
  104. NoPatientDataText.Visible = true;
  105. return;
  106. }
  107. SetActiveButtons(_entityManager.HasComponent<TargetingComponent>(_target.Value));
  108. ReturnButton.Visible = isPart;
  109. PartNameLabel.Visible = isPart;
  110. if (part != null)
  111. PartNameLabel.Text = _entityManager.HasComponent<MetaDataComponent>(part)
  112. ? Identity.Name(part.Value, _entityManager)
  113. : Loc.GetString("health-analyzer-window-entity-unknown-value-text");
  114. NoPatientDataText.Visible = false;
  115. // Patient Information
  116. SpriteView.SetEntity(SetupIcon(msg.Body) ?? _target.Value);
  117. SpriteView.Visible = msg.ScanMode.HasValue && msg.ScanMode.Value;
  118. PartView.Visible = SpriteView.Visible;
  119. NoDataTex.Visible = !SpriteView.Visible;
  120. var name = new FormattedMessage();
  121. name.PushColor(Color.White);
  122. name.AddText(_entityManager.HasComponent<MetaDataComponent>(_target.Value)
  123. ? Identity.Name(_target.Value, _entityManager)
  124. : Loc.GetString("health-analyzer-window-entity-unknown-text"));
  125. NameLabel.SetMessage(name);
  126. SpeciesLabel.Text =
  127. _entityManager.TryGetComponent<HumanoidAppearanceComponent>(_target.Value,
  128. out var humanoidAppearanceComponent)
  129. ? Loc.GetString(_prototypes.Index<SpeciesPrototype>(humanoidAppearanceComponent.Species).Name)
  130. : Loc.GetString("health-analyzer-window-entity-unknown-species-text");
  131. // Basic Diagnostic
  132. var parsedBlood = Loc.GetString("medbook-status-normal");
  133. BloodLabel.FontColorOverride = Color.Green;
  134. if (msg.BloodLevel <= 0.6)
  135. {
  136. parsedBlood = Loc.GetString("medbook-status-critical");
  137. BloodLabel.FontColorOverride = Color.Red;
  138. }
  139. else if (msg.BloodLevel <= 0.8)
  140. {
  141. parsedBlood = Loc.GetString("medbook-status-low");
  142. BloodLabel.FontColorOverride = Color.Yellow;
  143. }
  144. BloodLabel.Text = !float.IsNaN(msg.BloodLevel)
  145. ? parsedBlood
  146. : Loc.GetString("health-analyzer-window-entity-unknown-value-text");
  147. StatusLabel.Text = Loc.GetString("health-analyzer-window-entity-alive-text");
  148. StatusLabel.FontColorOverride = Color.Green;
  149. if (_entityManager.TryGetComponent<MobStateComponent>(_target.Value, out var mobStateComponent))
  150. {
  151. if (mobStateComponent.CurrentState == MobState.Critical)
  152. {
  153. StatusLabel.FontColorOverride = Color.Yellow;
  154. StatusLabel.Text = Loc.GetString("health-analyzer-window-entity-critical-text");
  155. }
  156. else if (mobStateComponent.CurrentState == MobState.Dead)
  157. {
  158. StatusLabel.FontColorOverride = Color.Red;
  159. StatusLabel.Text = Loc.GetString("health-analyzer-window-entity-dead-text");
  160. }
  161. }
  162. // Alerts
  163. var showAlerts = msg.Unrevivable == true || msg.Bleeding == true;
  164. AlertsDivider.Visible = showAlerts;
  165. AlertsContainer.Visible = showAlerts;
  166. if (showAlerts)
  167. AlertsContainer.DisposeAllChildren();
  168. if (msg.Unrevivable == true)
  169. AlertsContainer.AddChild(new RichTextLabel
  170. {
  171. Text = Loc.GetString("health-analyzer-window-entity-unrevivable-text"),
  172. Margin = new Thickness(0, 4),
  173. MaxWidth = 300
  174. });
  175. if (msg.Bleeding == true)
  176. AlertsContainer.AddChild(new RichTextLabel
  177. {
  178. Text = Loc.GetString("health-analyzer-window-entity-bleeding-text"),
  179. Margin = new Thickness(0, 4),
  180. MaxWidth = 300
  181. });
  182. // Damage Groups
  183. var damageSortedGroups =
  184. damageable.DamagePerGroup.OrderByDescending(damage => damage.Value)
  185. .ToDictionary(x => x.Key, x => x.Value);
  186. IReadOnlyDictionary<string, FixedPoint2> damagePerType = damageable.Damage.DamageDict;
  187. DrawDiagnosticGroups(damageSortedGroups, damagePerType);
  188. }
  189. // Not all of this function got messed with, but it was spread enough to warrant being covered entirely by a Shitmed Change
  190. private static string GetStatus(MobState mobState)
  191. {
  192. return mobState switch
  193. {
  194. MobState.Alive => Loc.GetString("health-analyzer-window-entity-alive-text"),
  195. MobState.Critical => Loc.GetString("health-analyzer-window-entity-critical-text"),
  196. MobState.Dead => Loc.GetString("health-analyzer-window-entity-dead-text"),
  197. _ => Loc.GetString("health-analyzer-window-entity-unknown-text"),
  198. };
  199. }
  200. private void DrawDiagnosticGroups(
  201. Dictionary<string, FixedPoint2> groups,
  202. IReadOnlyDictionary<string, FixedPoint2> damageDict)
  203. {
  204. GroupsContainer.RemoveAllChildren();
  205. foreach (var (damageGroupId, damageAmount) in groups)
  206. {
  207. if (damageAmount == 0)
  208. continue;
  209. var parsedDamage = "None";
  210. var color = Color.Green;
  211. if (damageAmount > 0 && damageAmount <= 20)
  212. {
  213. parsedDamage = "Minimal";
  214. color = Color.Yellow;
  215. }
  216. else if (damageAmount <= 60)
  217. {
  218. parsedDamage = "Medium";
  219. color = Color.Orange;
  220. }
  221. else if (damageAmount <= 100)
  222. {
  223. parsedDamage = "High";
  224. color = Color.Red;
  225. }
  226. else
  227. {
  228. parsedDamage = "Extreme";
  229. color = Color.DarkRed;
  230. }
  231. var groupTitleText = $"{Loc.GetString(
  232. "health-analyzer-window-damage-group-text",
  233. ("damageGroup", _prototypes.Index<DamageGroupPrototype>(damageGroupId).LocalizedName),
  234. ("amount", parsedDamage)
  235. )}";
  236. var groupContainer = new BoxContainer
  237. {
  238. Align = BoxContainer.AlignMode.Begin,
  239. Orientation = BoxContainer.LayoutOrientation.Vertical,
  240. };
  241. groupContainer.AddChild(CreateDiagnosticGroupTitle(groupTitleText, damageGroupId, color));
  242. GroupsContainer.AddChild(groupContainer);
  243. }
  244. }
  245. private Texture GetTexture(string texture)
  246. {
  247. var rsiPath = new ResPath("/Textures/Objects/Devices/health_analyzer.rsi");
  248. var rsiSprite = new SpriteSpecifier.Rsi(rsiPath, texture);
  249. var rsi = _cache.GetResource<RSIResource>(rsiSprite.RsiPath).RSI;
  250. if (!rsi.TryGetState(rsiSprite.RsiState, out var state))
  251. {
  252. rsiSprite = new SpriteSpecifier.Rsi(rsiPath, "unknown");
  253. }
  254. return _spriteSystem.Frame0(rsiSprite);
  255. }
  256. private static Label CreateDiagnosticItemLabel(string text, Color color)
  257. {
  258. return new Label
  259. {
  260. Text = text,
  261. FontColorOverride = color
  262. };
  263. }
  264. private BoxContainer CreateDiagnosticGroupTitle(string text, string id, Color color)
  265. {
  266. var rootContainer = new BoxContainer
  267. {
  268. Margin = new Thickness(0, 6, 0, 0),
  269. VerticalAlignment = VAlignment.Bottom,
  270. Orientation = BoxContainer.LayoutOrientation.Horizontal,
  271. };
  272. rootContainer.AddChild(new TextureRect
  273. {
  274. SetSize = new Vector2(30, 30),
  275. Texture = GetTexture(id.ToLower())
  276. });
  277. rootContainer.AddChild(CreateDiagnosticItemLabel(text, color));
  278. return rootContainer;
  279. }
  280. // Shitmed Change Start
  281. /// <summary>
  282. /// Sets up the Body Doll using Alert Entity to use in Health Analyzer.
  283. /// </summary>
  284. private EntityUid? SetupIcon(Dictionary<TargetBodyPart, TargetIntegrity>? body)
  285. {
  286. if (body is null)
  287. return null;
  288. if (!_entityManager.Deleted(_spriteViewEntity))
  289. _entityManager.QueueDeleteEntity(_spriteViewEntity);
  290. _spriteViewEntity = _entityManager.Spawn(_bodyView);
  291. if (!_entityManager.TryGetComponent<SpriteComponent>(_spriteViewEntity, out var sprite))
  292. return null;
  293. int layer = 0;
  294. foreach (var (bodyPart, integrity) in body)
  295. {
  296. // TODO: PartStatusUIController and make it use layers instead of TextureRects when EE refactors alerts.
  297. string enumName = Enum.GetName(typeof(TargetBodyPart), bodyPart) ?? "Unknown";
  298. int enumValue = (int)integrity;
  299. var rsi = new SpriteSpecifier.Rsi(new ResPath($"/Textures/_Shitmed/Interface/Targeting/Status/{enumName.ToLowerInvariant()}.rsi"), $"{enumName.ToLowerInvariant()}_{enumValue}");
  300. // Shitcode with love from Russia :)
  301. if (!sprite.TryGetLayer(layer, out _))
  302. sprite.AddLayer(_spriteSystem.Frame0(rsi));
  303. else
  304. sprite.LayerSetTexture(layer, _spriteSystem.Frame0(rsi));
  305. sprite.LayerSetScale(layer, new Vector2(3f, 3f));
  306. layer++;
  307. }
  308. return _spriteViewEntity;
  309. }
  310. // Shitmed Change End
  311. }
  312. }