CryoPodSystem.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309
  1. using Content.Server.Administration.Logs;
  2. using Content.Server.Atmos.EntitySystems;
  3. using Content.Server.Atmos.Piping.Components;
  4. using Content.Server.Atmos.Piping.Unary.EntitySystems;
  5. using Content.Server.Body.Components;
  6. using Content.Server.Body.Systems;
  7. using Content.Server.Medical.Components;
  8. using Content.Server.NodeContainer.EntitySystems;
  9. using Content.Server.NodeContainer.NodeGroups;
  10. using Content.Server.NodeContainer.Nodes;
  11. using Content.Server.Temperature.Components;
  12. using Content.Shared.Chemistry.EntitySystems;
  13. using Content.Shared.Atmos;
  14. using Content.Shared.UserInterface;
  15. using Content.Shared.Chemistry;
  16. using Content.Shared.Chemistry.Components;
  17. using Content.Shared.Chemistry.Components.SolutionManager;
  18. using Content.Shared.Climbing.Systems;
  19. using Content.Shared.Containers.ItemSlots;
  20. using Content.Shared.Database;
  21. using Content.Shared.DoAfter;
  22. using Content.Shared.DragDrop;
  23. using Content.Shared.Emag.Systems;
  24. using Content.Shared.Examine;
  25. using Content.Shared.Interaction;
  26. using Content.Shared.Medical.Cryogenics;
  27. using Content.Shared.MedicalScanner;
  28. using Content.Shared.Power;
  29. using Content.Shared.Verbs;
  30. using Robust.Server.GameObjects;
  31. using Robust.Shared.Containers;
  32. using Robust.Shared.Timing;
  33. using SharedToolSystem = Content.Shared.Tools.Systems.SharedToolSystem;
  34. using Content.Shared.Actions; // Shitmed Change
  35. using Content.Shared.Bed.Sleep; // Shitmed Change
  36. namespace Content.Server.Medical;
  37. public sealed partial class CryoPodSystem : SharedCryoPodSystem
  38. {
  39. [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
  40. [Dependency] private readonly GasCanisterSystem _gasCanisterSystem = default!;
  41. [Dependency] private readonly ClimbSystem _climbSystem = default!;
  42. [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
  43. [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
  44. [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
  45. [Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default!;
  46. [Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
  47. [Dependency] private readonly SharedToolSystem _toolSystem = default!;
  48. [Dependency] private readonly IGameTiming _gameTiming = default!;
  49. [Dependency] private readonly MetaDataSystem _metaDataSystem = default!;
  50. [Dependency] private readonly ReactiveSystem _reactiveSystem = default!;
  51. [Dependency] private readonly IAdminLogManager _adminLogger = default!;
  52. [Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
  53. [Dependency] private readonly SleepingSystem _sleepingSystem = default!;
  54. public override void Initialize()
  55. {
  56. base.Initialize();
  57. SubscribeLocalEvent<CryoPodComponent, ComponentInit>(OnComponentInit);
  58. SubscribeLocalEvent<CryoPodComponent, GetVerbsEvent<AlternativeVerb>>(AddAlternativeVerbs);
  59. SubscribeLocalEvent<CryoPodComponent, GotEmaggedEvent>(OnEmagged);
  60. SubscribeLocalEvent<CryoPodComponent, CryoPodDragFinished>(OnDragFinished);
  61. SubscribeLocalEvent<CryoPodComponent, CryoPodPryFinished>(OnCryoPodPryFinished);
  62. SubscribeLocalEvent<CryoPodComponent, AtmosDeviceUpdateEvent>(OnCryoPodUpdateAtmosphere);
  63. SubscribeLocalEvent<CryoPodComponent, DragDropTargetEvent>(HandleDragDropOn);
  64. SubscribeLocalEvent<CryoPodComponent, InteractUsingEvent>(OnInteractUsing);
  65. SubscribeLocalEvent<CryoPodComponent, ExaminedEvent>(OnExamined);
  66. SubscribeLocalEvent<CryoPodComponent, PowerChangedEvent>(OnPowerChanged);
  67. SubscribeLocalEvent<CryoPodComponent, GasAnalyzerScanEvent>(OnGasAnalyzed);
  68. SubscribeLocalEvent<CryoPodComponent, ActivatableUIOpenAttemptEvent>(OnActivateUIAttempt);
  69. SubscribeLocalEvent<CryoPodComponent, AfterActivatableUIOpenEvent>(OnActivateUI);
  70. SubscribeLocalEvent<CryoPodComponent, EntRemovedFromContainerMessage>(OnEjected);
  71. }
  72. public override void Update(float frameTime)
  73. {
  74. base.Update(frameTime);
  75. var curTime = _gameTiming.CurTime;
  76. var bloodStreamQuery = GetEntityQuery<BloodstreamComponent>();
  77. var metaDataQuery = GetEntityQuery<MetaDataComponent>();
  78. var itemSlotsQuery = GetEntityQuery<ItemSlotsComponent>();
  79. var fitsInDispenserQuery = GetEntityQuery<FitsInDispenserComponent>();
  80. var solutionContainerManagerQuery = GetEntityQuery<SolutionContainerManagerComponent>();
  81. var query = EntityQueryEnumerator<ActiveCryoPodComponent, CryoPodComponent>();
  82. while (query.MoveNext(out var uid, out _, out var cryoPod))
  83. {
  84. metaDataQuery.TryGetComponent(uid, out var metaDataComponent);
  85. if (curTime < cryoPod.NextInjectionTime + _metaDataSystem.GetPauseTime(uid, metaDataComponent))
  86. continue;
  87. cryoPod.NextInjectionTime = curTime + TimeSpan.FromSeconds(cryoPod.BeakerTransferTime);
  88. if (!itemSlotsQuery.TryGetComponent(uid, out var itemSlotsComponent))
  89. {
  90. continue;
  91. }
  92. var container = _itemSlotsSystem.GetItemOrNull(uid, cryoPod.SolutionContainerName, itemSlotsComponent);
  93. var patient = cryoPod.BodyContainer.ContainedEntity;
  94. if (container != null
  95. && container.Value.Valid
  96. && patient != null
  97. && fitsInDispenserQuery.TryGetComponent(container, out var fitsInDispenserComponent)
  98. && solutionContainerManagerQuery.TryGetComponent(container,
  99. out var solutionContainerManagerComponent)
  100. && _solutionContainerSystem.TryGetFitsInDispenser((container.Value, fitsInDispenserComponent, solutionContainerManagerComponent),
  101. out var containerSolution, out _))
  102. {
  103. if (!bloodStreamQuery.TryGetComponent(patient, out var bloodstream))
  104. {
  105. continue;
  106. }
  107. var solutionToInject = _solutionContainerSystem.SplitSolution(containerSolution.Value, cryoPod.BeakerTransferAmount);
  108. _bloodstreamSystem.TryAddToChemicals(patient.Value, solutionToInject, bloodstream);
  109. _reactiveSystem.DoEntityReaction(patient.Value, solutionToInject, ReactionMethod.Injection);
  110. }
  111. }
  112. }
  113. public override EntityUid? EjectBody(EntityUid uid, CryoPodComponent? cryoPodComponent)
  114. {
  115. if (!Resolve(uid, ref cryoPodComponent))
  116. return null;
  117. if (cryoPodComponent.BodyContainer.ContainedEntity is not { Valid: true } contained)
  118. return null;
  119. base.EjectBody(uid, cryoPodComponent);
  120. _climbSystem.ForciblySetClimbing(contained, uid);
  121. return contained;
  122. }
  123. #region Interaction
  124. private void HandleDragDropOn(Entity<CryoPodComponent> entity, ref DragDropTargetEvent args)
  125. {
  126. if (entity.Comp.BodyContainer.ContainedEntity != null)
  127. return;
  128. var doAfterArgs = new DoAfterArgs(EntityManager, args.User, entity.Comp.EntryDelay, new CryoPodDragFinished(), entity, target: args.Dragged, used: entity)
  129. {
  130. BreakOnDamage = true,
  131. BreakOnMove = true,
  132. NeedHand = false,
  133. };
  134. _doAfterSystem.TryStartDoAfter(doAfterArgs);
  135. args.Handled = true;
  136. }
  137. private void OnDragFinished(Entity<CryoPodComponent> entity, ref CryoPodDragFinished args)
  138. {
  139. if (args.Cancelled || args.Handled || args.Args.Target == null)
  140. return;
  141. if (InsertBody(entity.Owner, args.Args.Target.Value, entity.Comp))
  142. {
  143. if (!TryComp(entity.Owner, out CryoPodAirComponent? cryoPodAir))
  144. _adminLogger.Add(LogType.Action, LogImpact.Medium,
  145. $"{ToPrettyString(args.User)} inserted {ToPrettyString(args.Args.Target.Value)} into {ToPrettyString(entity.Owner)}");
  146. _adminLogger.Add(LogType.Action, LogImpact.Medium,
  147. $"{ToPrettyString(args.User)} inserted {ToPrettyString(args.Args.Target.Value)} into {ToPrettyString(entity.Owner)} which contains gas: {cryoPodAir!.Air.ToPrettyString():gasMix}");
  148. }
  149. args.Handled = true;
  150. }
  151. private void OnActivateUIAttempt(Entity<CryoPodComponent> entity, ref ActivatableUIOpenAttemptEvent args)
  152. {
  153. if (args.Cancelled)
  154. {
  155. return;
  156. }
  157. var containedEntity = entity.Comp.BodyContainer.ContainedEntity;
  158. if (containedEntity == null || containedEntity == args.User || !HasComp<ActiveCryoPodComponent>(entity))
  159. {
  160. args.Cancel();
  161. }
  162. }
  163. private void OnActivateUI(Entity<CryoPodComponent> entity, ref AfterActivatableUIOpenEvent args)
  164. {
  165. if (!entity.Comp.BodyContainer.ContainedEntity.HasValue)
  166. return;
  167. TryComp<TemperatureComponent>(entity.Comp.BodyContainer.ContainedEntity, out var temp);
  168. TryComp<BloodstreamComponent>(entity.Comp.BodyContainer.ContainedEntity, out var bloodstream);
  169. if (TryComp<HealthAnalyzerComponent>(entity, out var healthAnalyzer))
  170. {
  171. healthAnalyzer.ScannedEntity = entity.Comp.BodyContainer.ContainedEntity;
  172. }
  173. // TODO: This should be a state my dude
  174. _uiSystem.ServerSendUiMessage(
  175. entity.Owner,
  176. HealthAnalyzerUiKey.Key,
  177. new HealthAnalyzerScannedUserMessage(GetNetEntity(entity.Comp.BodyContainer.ContainedEntity),
  178. temp?.CurrentTemperature ?? 0,
  179. (bloodstream != null && _solutionContainerSystem.ResolveSolution(entity.Comp.BodyContainer.ContainedEntity.Value,
  180. bloodstream.BloodSolutionName, ref bloodstream.BloodSolution, out var bloodSolution))
  181. ? bloodSolution.FillFraction
  182. : 0,
  183. null,
  184. null,
  185. null,
  186. null // Shitmed Change
  187. ));
  188. }
  189. private void OnInteractUsing(Entity<CryoPodComponent> entity, ref InteractUsingEvent args)
  190. {
  191. if (args.Handled || !entity.Comp.Locked || entity.Comp.BodyContainer.ContainedEntity == null)
  192. return;
  193. args.Handled = _toolSystem.UseTool(args.Used, args.User, entity.Owner, entity.Comp.PryDelay, "Prying", new CryoPodPryFinished());
  194. }
  195. private void OnExamined(Entity<CryoPodComponent> entity, ref ExaminedEvent args)
  196. {
  197. var container = _itemSlotsSystem.GetItemOrNull(entity.Owner, entity.Comp.SolutionContainerName);
  198. if (args.IsInDetailsRange && container != null && _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out _, out var containerSolution))
  199. {
  200. using (args.PushGroup(nameof(CryoPodComponent)))
  201. {
  202. args.PushMarkup(Loc.GetString("cryo-pod-examine", ("beaker", Name(container.Value))));
  203. if (containerSolution.Volume == 0)
  204. {
  205. args.PushMarkup(Loc.GetString("cryo-pod-empty-beaker"));
  206. }
  207. }
  208. }
  209. }
  210. private void OnPowerChanged(Entity<CryoPodComponent> entity, ref PowerChangedEvent args)
  211. {
  212. // Needed to avoid adding/removing components on a deleted entity
  213. if (Terminating(entity))
  214. {
  215. return;
  216. }
  217. var insidePod = entity.Comp.BodyContainer.ContainedEntity; // Shitmed Change
  218. if (args.Powered)
  219. {
  220. EnsureComp<ActiveCryoPodComponent>(entity);
  221. }
  222. else
  223. {
  224. RemComp<ActiveCryoPodComponent>(entity);
  225. if (insidePod is { } patient) // Shitmed Change
  226. _sleepingSystem.TryWaking(patient);
  227. _uiSystem.CloseUi(entity.Owner, HealthAnalyzerUiKey.Key);
  228. }
  229. UpdateAppearance(entity.Owner, entity.Comp);
  230. }
  231. #endregion
  232. #region Atmos handler
  233. private void OnCryoPodUpdateAtmosphere(Entity<CryoPodComponent> entity, ref AtmosDeviceUpdateEvent args)
  234. {
  235. if (!_nodeContainer.TryGetNode(entity.Owner, entity.Comp.PortName, out PortablePipeNode? portNode))
  236. return;
  237. if (!TryComp(entity, out CryoPodAirComponent? cryoPodAir))
  238. return;
  239. _atmosphereSystem.React(cryoPodAir.Air, portNode);
  240. if (portNode.NodeGroup is PipeNet { NodeCount: > 1 } net)
  241. {
  242. _gasCanisterSystem.MixContainerWithPipeNet(cryoPodAir.Air, net.Air);
  243. }
  244. }
  245. private void OnGasAnalyzed(Entity<CryoPodComponent> entity, ref GasAnalyzerScanEvent args)
  246. {
  247. if (!TryComp(entity, out CryoPodAirComponent? cryoPodAir))
  248. return;
  249. args.GasMixtures ??= new List<(string, GasMixture?)>();
  250. args.GasMixtures.Add((Name(entity.Owner), cryoPodAir.Air));
  251. // If it's connected to a port, include the port side
  252. // multiply by volume fraction to make sure to send only the gas inside the analyzed pipe element, not the whole pipe system
  253. if (_nodeContainer.TryGetNode(entity.Owner, entity.Comp.PortName, out PipeNode? port) && port.Air.Volume != 0f)
  254. {
  255. var portAirLocal = port.Air.Clone();
  256. portAirLocal.Multiply(port.Volume / port.Air.Volume);
  257. portAirLocal.Volume = port.Volume;
  258. args.GasMixtures.Add((entity.Comp.PortName, portAirLocal));
  259. }
  260. }
  261. private void OnEjected(Entity<CryoPodComponent> cryoPod, ref EntRemovedFromContainerMessage args)
  262. {
  263. if (TryComp<HealthAnalyzerComponent>(cryoPod.Owner, out var healthAnalyzer))
  264. {
  265. healthAnalyzer.ScannedEntity = null;
  266. }
  267. // if body is ejected - no need to display health-analyzer
  268. _uiSystem.CloseUi(cryoPod.Owner, HealthAnalyzerUiKey.Key);
  269. }
  270. #endregion
  271. }