SharedCryoPodSystem.cs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. using Content.Server.Medical.Components;
  2. using Content.Shared.Administration.Logs;
  3. using Content.Shared.Body.Components;
  4. using Content.Shared.Database;
  5. using Content.Shared.DoAfter;
  6. using Content.Shared.DragDrop;
  7. using Content.Shared.Emag.Systems;
  8. using Content.Shared.Mobs.Components;
  9. using Content.Shared.Mobs.Systems;
  10. using Content.Shared.Popups;
  11. using Content.Shared.Standing;
  12. using Content.Shared.Stunnable;
  13. using Content.Shared.Verbs;
  14. using Robust.Shared.Containers;
  15. using Robust.Shared.Serialization;
  16. namespace Content.Shared.Medical.Cryogenics;
  17. public abstract partial class SharedCryoPodSystem: EntitySystem
  18. {
  19. [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
  20. [Dependency] private readonly StandingStateSystem _standingStateSystem = default!;
  21. [Dependency] private readonly EmagSystem _emag = default!;
  22. [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
  23. [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
  24. [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
  25. [Dependency] private readonly SharedPointLightSystem _light = default!;
  26. [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!;
  27. public override void Initialize()
  28. {
  29. base.Initialize();
  30. SubscribeLocalEvent<CryoPodComponent, CanDropTargetEvent>(OnCryoPodCanDropOn);
  31. InitializeInsideCryoPod();
  32. }
  33. private void OnCryoPodCanDropOn(EntityUid uid, CryoPodComponent component, ref CanDropTargetEvent args)
  34. {
  35. if (args.Handled)
  36. return;
  37. args.CanDrop = HasComp<BodyComponent>(args.Dragged);
  38. args.Handled = true;
  39. }
  40. protected void OnComponentInit(EntityUid uid, CryoPodComponent cryoPodComponent, ComponentInit args)
  41. {
  42. cryoPodComponent.BodyContainer = _containerSystem.EnsureContainer<ContainerSlot>(uid, "scanner-body");
  43. }
  44. protected void UpdateAppearance(EntityUid uid, CryoPodComponent? cryoPod = null, AppearanceComponent? appearance = null)
  45. {
  46. if (!Resolve(uid, ref cryoPod))
  47. return;
  48. var cryoPodEnabled = HasComp<ActiveCryoPodComponent>(uid);
  49. if (_light.TryGetLight(uid, out var light))
  50. {
  51. _light.SetEnabled(uid, cryoPodEnabled && cryoPod.BodyContainer.ContainedEntity != null, light);
  52. }
  53. if (!Resolve(uid, ref appearance))
  54. return;
  55. _appearanceSystem.SetData(uid, CryoPodComponent.CryoPodVisuals.ContainsEntity, cryoPod.BodyContainer.ContainedEntity == null, appearance);
  56. _appearanceSystem.SetData(uid, CryoPodComponent.CryoPodVisuals.IsOn, cryoPodEnabled, appearance);
  57. }
  58. public bool InsertBody(EntityUid uid, EntityUid target, CryoPodComponent cryoPodComponent)
  59. {
  60. if (cryoPodComponent.BodyContainer.ContainedEntity != null)
  61. return false;
  62. if (!HasComp<MobStateComponent>(target))
  63. return false;
  64. var xform = Transform(target);
  65. _containerSystem.Insert((target, xform), cryoPodComponent.BodyContainer);
  66. EnsureComp<InsideCryoPodComponent>(target);
  67. _standingStateSystem.Stand(target, force: true); // Force-stand the mob so that the cryo pod sprite overlays it fully
  68. UpdateAppearance(uid, cryoPodComponent);
  69. return true;
  70. }
  71. public void TryEjectBody(EntityUid uid, EntityUid userId, CryoPodComponent? cryoPodComponent)
  72. {
  73. if (!Resolve(uid, ref cryoPodComponent))
  74. {
  75. return;
  76. }
  77. if (cryoPodComponent.Locked)
  78. {
  79. _popupSystem.PopupEntity(Loc.GetString("cryo-pod-locked"), uid, userId);
  80. return;
  81. }
  82. var ejected = EjectBody(uid, cryoPodComponent);
  83. if (ejected != null)
  84. _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ejected.Value)} ejected from {ToPrettyString(uid)} by {ToPrettyString(userId)}");
  85. }
  86. /// <summary>
  87. /// Ejects the contained body
  88. /// </summary>
  89. /// <param name="uid">The cryopod entity</param>
  90. /// <param name="cryoPodComponent">Cryopod component of <see cref="uid"/></param>
  91. /// <returns>Ejected entity</returns>
  92. public virtual EntityUid? EjectBody(EntityUid uid, CryoPodComponent? cryoPodComponent)
  93. {
  94. if (!Resolve(uid, ref cryoPodComponent))
  95. return null;
  96. if (cryoPodComponent.BodyContainer.ContainedEntity is not {Valid: true} contained)
  97. return null;
  98. _containerSystem.Remove(contained, cryoPodComponent.BodyContainer);
  99. // InsideCryoPodComponent is removed automatically in its EntGotRemovedFromContainerMessage listener
  100. // RemComp<InsideCryoPodComponent>(contained);
  101. // Restore the correct position of the patient. Checking the components manually feels hacky, but I did not find a better way for now.
  102. if (HasComp<KnockedDownComponent>(contained) || _mobStateSystem.IsIncapacitated(contained))
  103. {
  104. _standingStateSystem.Down(contained);
  105. }
  106. else
  107. {
  108. _standingStateSystem.Stand(contained);
  109. }
  110. UpdateAppearance(uid, cryoPodComponent);
  111. return contained;
  112. }
  113. protected void AddAlternativeVerbs(EntityUid uid, CryoPodComponent cryoPodComponent, GetVerbsEvent<AlternativeVerb> args)
  114. {
  115. if (!args.CanAccess || !args.CanInteract)
  116. return;
  117. // Eject verb
  118. if (cryoPodComponent.BodyContainer.ContainedEntity != null)
  119. {
  120. args.Verbs.Add(new AlternativeVerb
  121. {
  122. Text = Loc.GetString("cryo-pod-verb-noun-occupant"),
  123. Category = VerbCategory.Eject,
  124. Priority = 1, // Promote to top to make ejecting the ALT-click action
  125. Act = () => TryEjectBody(uid, args.User, cryoPodComponent)
  126. });
  127. }
  128. }
  129. protected void OnEmagged(EntityUid uid, CryoPodComponent? cryoPodComponent, ref GotEmaggedEvent args)
  130. {
  131. if (!Resolve(uid, ref cryoPodComponent))
  132. return;
  133. if (!_emag.CompareFlag(args.Type, EmagType.Interaction))
  134. return;
  135. if (cryoPodComponent.PermaLocked && cryoPodComponent.Locked)
  136. return;
  137. cryoPodComponent.PermaLocked = true;
  138. cryoPodComponent.Locked = true;
  139. args.Handled = true;
  140. }
  141. protected void OnCryoPodPryFinished(EntityUid uid, CryoPodComponent cryoPodComponent, CryoPodPryFinished args)
  142. {
  143. if (args.Cancelled)
  144. return;
  145. var ejected = EjectBody(uid, cryoPodComponent);
  146. if (ejected != null)
  147. _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ejected.Value)} pried out of {ToPrettyString(uid)} by {ToPrettyString(args.User)}");
  148. }
  149. [Serializable, NetSerializable]
  150. public sealed partial class CryoPodPryFinished : SimpleDoAfterEvent
  151. {
  152. }
  153. [Serializable, NetSerializable]
  154. public sealed partial class CryoPodDragFinished : SimpleDoAfterEvent
  155. {
  156. }
  157. }