1
0

MedBookSystem.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269
  1. using Content.Server.Body.Components;
  2. using Content.Server.Medical.Components;
  3. using Content.Server.PowerCell;
  4. using Content.Server.Temperature.Components;
  5. using Content.Shared.Traits.Assorted;
  6. using Content.Shared.Chemistry.EntitySystems;
  7. using Content.Shared.Damage;
  8. using Content.Shared.DoAfter;
  9. using Content.Shared.IdentityManagement;
  10. using Content.Shared.Interaction;
  11. using Content.Shared.Interaction.Events;
  12. using Content.Shared.Item.ItemToggle;
  13. using Content.Shared.Item.ItemToggle.Components;
  14. using Content.Shared.MedicalScanner;
  15. using Content.Shared.Mobs.Components;
  16. using Content.Shared.Popups;
  17. using Robust.Server.GameObjects;
  18. using Robust.Shared.Containers;
  19. using Robust.Shared.Timing;
  20. // Shitmed Change
  21. using Content.Shared.Body.Part;
  22. using Content.Shared.Body.Systems;
  23. using Content.Shared._Shitmed.Targeting;
  24. using System.Linq;
  25. namespace Content.Server.Medical;
  26. public sealed class MedBookSystem : EntitySystem
  27. {
  28. [Dependency] private readonly IGameTiming _timing = default!;
  29. [Dependency] private readonly PowerCellSystem _cell = default!;
  30. [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
  31. [Dependency] private readonly SharedBodySystem _bodySystem = default!; // Shitmed Change
  32. [Dependency] private readonly ItemToggleSystem _toggle = default!;
  33. [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
  34. [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
  35. [Dependency] private readonly TransformSystem _transformSystem = default!;
  36. [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
  37. public override void Initialize()
  38. {
  39. SubscribeLocalEvent<MedBookComponent, AfterInteractEvent>(OnAfterInteract);
  40. SubscribeLocalEvent<MedBookComponent, MedBookDoAfterEvent>(OnDoAfter);
  41. SubscribeLocalEvent<MedBookComponent, EntGotInsertedIntoContainerMessage>(OnInsertedIntoContainer);
  42. SubscribeLocalEvent<MedBookComponent, ItemToggledEvent>(OnToggled);
  43. SubscribeLocalEvent<MedBookComponent, DroppedEvent>(OnDropped);
  44. // Shitmed Change Start
  45. Subs.BuiEvents<MedBookComponent>(MedBookUiKey.Key, subs =>
  46. {
  47. subs.Event<MedBookPartMessage>(OnMedBookPartSelected);
  48. });
  49. // Shitmed Change End
  50. }
  51. public override void Update(float frameTime)
  52. {
  53. var analyzerQuery = EntityQueryEnumerator<MedBookComponent, TransformComponent>();
  54. while (analyzerQuery.MoveNext(out var uid, out var component, out var transform))
  55. {
  56. //Update rate limited to 1 second
  57. if (component.NextUpdate > _timing.CurTime)
  58. continue;
  59. if (component.ScannedEntity is not { } patient)
  60. continue;
  61. if (Deleted(patient))
  62. {
  63. StopAnalyzingEntity((uid, component), patient);
  64. continue;
  65. }
  66. // Shitmed Change Start
  67. if (component.CurrentBodyPart != null
  68. && (Deleted(component.CurrentBodyPart)
  69. || TryComp(component.CurrentBodyPart, out BodyPartComponent? bodyPartComponent)
  70. && bodyPartComponent.Body is null))
  71. {
  72. BeginAnalyzingEntity((uid, component), patient, null);
  73. continue;
  74. }
  75. // Shitmed Change End
  76. component.NextUpdate = _timing.CurTime + component.UpdateInterval;
  77. //Get distance between health analyzer and the scanned entity
  78. var patientCoordinates = Transform(patient).Coordinates;
  79. if (!_transformSystem.InRange(patientCoordinates, transform.Coordinates, component.MaxScanRange))
  80. {
  81. //Range too far, disable updates
  82. StopAnalyzingEntity((uid, component), patient);
  83. continue;
  84. }
  85. UpdateScannedUser(uid, patient, true, component.CurrentBodyPart); // Shitmed Change
  86. }
  87. }
  88. /// <summary>
  89. /// Trigger the doafter for scanning
  90. /// </summary>
  91. private void OnAfterInteract(Entity<MedBookComponent> uid, ref AfterInteractEvent args)
  92. {
  93. if (args.Target == null || !args.CanReach || !HasComp<MobStateComponent>(args.Target) || !_cell.HasDrawCharge(uid, user: args.User))
  94. return;
  95. var doAfterCancelled = !_doAfterSystem.TryStartDoAfter(new DoAfterArgs(EntityManager, args.User, uid.Comp.ScanDelay, new MedBookDoAfterEvent(), uid, target: args.Target, used: uid)
  96. {
  97. NeedHand = true,
  98. BreakOnMove = true,
  99. });
  100. if (args.Target == args.User || doAfterCancelled || uid.Comp.Silent)
  101. return;
  102. var msg = Loc.GetString("medbook-popup-scan-target", ("user", Identity.Entity(args.User, EntityManager)));
  103. _popupSystem.PopupEntity(msg, args.Target.Value, args.Target.Value, PopupType.Medium);
  104. }
  105. private void OnDoAfter(Entity<MedBookComponent> uid, ref MedBookDoAfterEvent args)
  106. {
  107. if (args.Handled || args.Cancelled || args.Target == null || !_cell.HasDrawCharge(uid, user: args.User))
  108. return;
  109. OpenUserInterface(args.User, uid);
  110. BeginAnalyzingEntity(uid, args.Target.Value);
  111. args.Handled = true;
  112. }
  113. /// <summary>
  114. /// Turn off when placed into a storage item or moved between slots/hands
  115. /// </summary>
  116. private void OnInsertedIntoContainer(Entity<MedBookComponent> uid, ref EntGotInsertedIntoContainerMessage args)
  117. {
  118. if (uid.Comp.ScannedEntity is { } patient)
  119. _toggle.TryDeactivate(uid.Owner);
  120. }
  121. /// <summary>
  122. /// Disable continuous updates once turned off
  123. /// </summary>
  124. private void OnToggled(Entity<MedBookComponent> ent, ref ItemToggledEvent args)
  125. {
  126. if (!args.Activated && ent.Comp.ScannedEntity is { } patient)
  127. StopAnalyzingEntity(ent, patient);
  128. }
  129. /// <summary>
  130. /// Turn off the analyser when dropped
  131. /// </summary>
  132. private void OnDropped(Entity<MedBookComponent> uid, ref DroppedEvent args)
  133. {
  134. if (uid.Comp.ScannedEntity is { } patient)
  135. _toggle.TryDeactivate(uid.Owner);
  136. }
  137. private void OpenUserInterface(EntityUid user, EntityUid analyzer)
  138. {
  139. if (!_uiSystem.HasUi(analyzer, MedBookUiKey.Key))
  140. return;
  141. _uiSystem.OpenUi(analyzer, MedBookUiKey.Key, user);
  142. }
  143. /// <summary>
  144. /// Mark the entity as having its health analyzed, and link the analyzer to it
  145. /// </summary>
  146. /// <param name="medBook">The health analyzer that should receive the updates</param>
  147. /// <param name="target">The entity to start analyzing</param>
  148. /// <param name="part">Shitmed Change: The body part to analyze, if any</param>
  149. private void BeginAnalyzingEntity(Entity<MedBookComponent> medBook, EntityUid target, EntityUid? part = null)
  150. {
  151. //Link the health analyzer to the scanned entity
  152. medBook.Comp.ScannedEntity = target;
  153. medBook.Comp.CurrentBodyPart = part; // Shitmed Change
  154. _toggle.TryActivate(medBook.Owner);
  155. UpdateScannedUser(medBook, target, true, part); // Shitmed Change
  156. }
  157. /// <summary>
  158. /// Remove the analyzer from the active list, and remove the component if it has no active analyzers
  159. /// </summary>
  160. /// <param name="medBook">The health analyzer that's receiving the updates</param>
  161. /// <param name="target">The entity to analyze</param>
  162. private void StopAnalyzingEntity(Entity<MedBookComponent> medBook, EntityUid target)
  163. {
  164. //Unlink the analyzer
  165. medBook.Comp.ScannedEntity = null;
  166. medBook.Comp.CurrentBodyPart = null; // Shitmed Change
  167. _toggle.TryDeactivate(medBook.Owner);
  168. UpdateScannedUser(medBook, target, false);
  169. }
  170. // Shitmed Change Start
  171. /// <summary>
  172. /// Shitmed Change: Handle the selection of a body part on the health analyzer
  173. /// </summary>
  174. /// <param name="medBook">The health analyzer that's receiving the updates</param>
  175. /// <param name="args">The message containing the selected part</param>
  176. private void OnMedBookPartSelected(Entity<MedBookComponent> medBook, ref MedBookPartMessage args)
  177. {
  178. if (!TryGetEntity(args.Owner, out var owner))
  179. return;
  180. if (args.BodyPart == null)
  181. {
  182. BeginAnalyzingEntity(medBook, owner.Value, null);
  183. }
  184. else
  185. {
  186. var (targetType, targetSymmetry) = _bodySystem.ConvertTargetBodyPart(args.BodyPart.Value);
  187. if (_bodySystem.GetBodyChildrenOfType(owner.Value, targetType, symmetry: targetSymmetry) is { } part)
  188. BeginAnalyzingEntity(medBook, owner.Value, part.FirstOrDefault().Id);
  189. }
  190. }
  191. // Shitmed Change End
  192. /// <summary>
  193. /// Send an update for the target to the medBook
  194. /// </summary>
  195. /// <param name="medBook">The health analyzer</param>
  196. /// <param name="target">The entity being scanned</param>
  197. /// <param name="scanMode">True makes the UI show ACTIVE, False makes the UI show INACTIVE</param>
  198. /// <param name="part">Shitmed Change: The body part being scanned, if any</param>
  199. public void UpdateScannedUser(EntityUid medBook, EntityUid target, bool scanMode, EntityUid? part = null)
  200. {
  201. if (!_uiSystem.HasUi(medBook, MedBookUiKey.Key))
  202. return;
  203. if (!HasComp<DamageableComponent>(target))
  204. return;
  205. var bodyTemperature = float.NaN;
  206. if (TryComp<TemperatureComponent>(target, out var temp))
  207. bodyTemperature = temp.CurrentTemperature;
  208. var bloodAmount = float.NaN;
  209. var bleeding = false;
  210. var unrevivable = false;
  211. if (TryComp<BloodstreamComponent>(target, out var bloodstream) &&
  212. _solutionContainerSystem.ResolveSolution(target, bloodstream.BloodSolutionName,
  213. ref bloodstream.BloodSolution, out var bloodSolution))
  214. {
  215. bloodAmount = bloodSolution.FillFraction;
  216. bleeding = bloodstream.BleedAmount > 0;
  217. }
  218. // Shitmed Change Start
  219. Dictionary<TargetBodyPart, TargetIntegrity>? body = null;
  220. if (HasComp<TargetingComponent>(target))
  221. body = _bodySystem.GetBodyPartStatus(target);
  222. // Shitmed Change End
  223. if (TryComp<UnrevivableComponent>(target, out var unrevivableComp) && unrevivableComp.Analyzable)
  224. unrevivable = true;
  225. _uiSystem.ServerSendUiMessage(medBook, MedBookUiKey.Key, new MedBookScannedUserMessage(
  226. GetNetEntity(target),
  227. bodyTemperature,
  228. bloodAmount,
  229. scanMode,
  230. bleeding,
  231. unrevivable,
  232. // Shitmed Change
  233. body,
  234. part != null ? GetNetEntity(part) : null
  235. ));
  236. }
  237. }