SharedAirlockSystem.cs 6.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181
  1. using Content.Shared.Doors.Components;
  2. using Robust.Shared.Audio.Systems;
  3. using Content.Shared.Popups;
  4. using Content.Shared.Prying.Components;
  5. using Content.Shared.Wires;
  6. using Robust.Shared.Timing;
  7. namespace Content.Shared.Doors.Systems;
  8. public abstract class SharedAirlockSystem : EntitySystem
  9. {
  10. [Dependency] private readonly IGameTiming _timing = default!;
  11. [Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
  12. [Dependency] protected readonly SharedAudioSystem Audio = default!;
  13. [Dependency] protected readonly SharedDoorSystem DoorSystem = default!;
  14. [Dependency] protected readonly SharedPopupSystem Popup = default!;
  15. [Dependency] private readonly SharedWiresSystem _wiresSystem = default!;
  16. public override void Initialize()
  17. {
  18. base.Initialize();
  19. SubscribeLocalEvent<AirlockComponent, BeforeDoorClosedEvent>(OnBeforeDoorClosed);
  20. SubscribeLocalEvent<AirlockComponent, DoorStateChangedEvent>(OnStateChanged);
  21. SubscribeLocalEvent<AirlockComponent, DoorBoltsChangedEvent>(OnBoltsChanged);
  22. SubscribeLocalEvent<AirlockComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
  23. SubscribeLocalEvent<AirlockComponent, BeforeDoorDeniedEvent>(OnBeforeDoorDenied);
  24. SubscribeLocalEvent<AirlockComponent, GetPryTimeModifierEvent>(OnGetPryMod);
  25. SubscribeLocalEvent<AirlockComponent, BeforePryEvent>(OnBeforePry);
  26. }
  27. private void OnBeforeDoorClosed(EntityUid uid, AirlockComponent airlock, BeforeDoorClosedEvent args)
  28. {
  29. if (args.Cancelled)
  30. return;
  31. if (!airlock.Safety)
  32. args.PerformCollisionCheck = false;
  33. // only block based on bolts / power status when initially closing the door, not when its already
  34. // mid-transition. Particularly relevant for when the door was pried-closed with a crowbar, which bypasses
  35. // the initial power-check.
  36. if (TryComp(uid, out DoorComponent? door)
  37. && !args.Partial
  38. && !CanChangeState(uid, airlock))
  39. {
  40. args.Cancel();
  41. }
  42. }
  43. private void OnStateChanged(EntityUid uid, AirlockComponent component, DoorStateChangedEvent args)
  44. {
  45. // This is here so we don't accidentally bulldoze state values and mispredict.
  46. if (_timing.ApplyingState)
  47. return;
  48. // Only show the maintenance panel if the airlock is closed
  49. if (TryComp<WiresPanelComponent>(uid, out var wiresPanel))
  50. {
  51. _wiresSystem.ChangePanelVisibility(uid, wiresPanel, component.OpenPanelVisible || args.State != DoorState.Open);
  52. }
  53. // If the door is closed, we should look if the bolt was locked while closing
  54. UpdateAutoClose(uid, component);
  55. // Make sure the airlock auto closes again next time it is opened
  56. if (args.State == DoorState.Closed)
  57. {
  58. component.AutoClose = true;
  59. Dirty(uid, component);
  60. }
  61. }
  62. private void OnBoltsChanged(EntityUid uid, AirlockComponent component, DoorBoltsChangedEvent args)
  63. {
  64. // If unbolted, reset the auto close timer
  65. if (!args.BoltsDown)
  66. UpdateAutoClose(uid, component);
  67. }
  68. private void OnBeforeDoorOpened(EntityUid uid, AirlockComponent component, BeforeDoorOpenedEvent args)
  69. {
  70. if (!CanChangeState(uid, component))
  71. args.Cancel();
  72. }
  73. private void OnBeforeDoorDenied(EntityUid uid, AirlockComponent component, BeforeDoorDeniedEvent args)
  74. {
  75. if (!CanChangeState(uid, component))
  76. args.Cancel();
  77. }
  78. private void OnGetPryMod(EntityUid uid, AirlockComponent component, ref GetPryTimeModifierEvent args)
  79. {
  80. if (component.Powered)
  81. args.PryTimeModifier *= component.PoweredPryModifier;
  82. if (DoorSystem.IsBolted(uid))
  83. args.PryTimeModifier *= component.BoltedPryModifier;
  84. }
  85. /// <summary>
  86. /// Updates the auto close timer.
  87. /// </summary>
  88. public void UpdateAutoClose(EntityUid uid, AirlockComponent? airlock = null, DoorComponent? door = null)
  89. {
  90. if (!Resolve(uid, ref airlock, ref door))
  91. return;
  92. if (door.State != DoorState.Open)
  93. return;
  94. if (!airlock.AutoClose)
  95. return;
  96. if (!CanChangeState(uid, airlock))
  97. return;
  98. var autoev = new BeforeDoorAutoCloseEvent();
  99. RaiseLocalEvent(uid, autoev);
  100. if (autoev.Cancelled)
  101. return;
  102. DoorSystem.SetNextStateChange(uid, airlock.AutoCloseDelay * airlock.AutoCloseDelayModifier);
  103. }
  104. private void OnBeforePry(EntityUid uid, AirlockComponent component, ref BeforePryEvent args)
  105. {
  106. if (args.Cancelled)
  107. return;
  108. if (!component.Powered || args.PryPowered)
  109. return;
  110. args.Message = "airlock-component-cannot-pry-is-powered-message";
  111. args.Cancelled = true;
  112. }
  113. public void UpdateEmergencyLightStatus(EntityUid uid, AirlockComponent component)
  114. {
  115. Appearance.SetData(uid, DoorVisuals.EmergencyLights, component.EmergencyAccess);
  116. }
  117. public void SetEmergencyAccess(Entity<AirlockComponent> ent, bool value, EntityUid? user = null, bool predicted = false)
  118. {
  119. if(!ent.Comp.Powered)
  120. return;
  121. if (ent.Comp.EmergencyAccess == value)
  122. return;
  123. ent.Comp.EmergencyAccess = value;
  124. Dirty(ent, ent.Comp); // This only runs on the server apparently so we need this.
  125. UpdateEmergencyLightStatus(ent, ent.Comp);
  126. var sound = ent.Comp.EmergencyAccess ? ent.Comp.EmergencyOnSound : ent.Comp.EmergencyOffSound;
  127. if (predicted)
  128. Audio.PlayPredicted(sound, ent, user: user);
  129. else
  130. Audio.PlayPvs(sound, ent);
  131. }
  132. public void SetAutoCloseDelayModifier(AirlockComponent component, float value)
  133. {
  134. if (component.AutoCloseDelayModifier.Equals(value))
  135. return;
  136. component.AutoCloseDelayModifier = value;
  137. }
  138. public void SetSafety(AirlockComponent component, bool value)
  139. {
  140. component.Safety = value;
  141. }
  142. public bool CanChangeState(EntityUid uid, AirlockComponent component)
  143. {
  144. return component.Powered && !DoorSystem.IsBolted(uid);
  145. }
  146. }