SharedSubFloorHideSystem.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  1. using Content.Shared.Audio;
  2. using Content.Shared.Explosion;
  3. using Content.Shared.Eye;
  4. using Content.Shared.Interaction.Events;
  5. using Content.Shared.Maps;
  6. using JetBrains.Annotations;
  7. using Robust.Shared.Map;
  8. using Robust.Shared.Map.Components;
  9. using Robust.Shared.Serialization;
  10. namespace Content.Shared.SubFloor
  11. {
  12. /// <summary>
  13. /// Entity system backing <see cref="SubFloorHideComponent"/>.
  14. /// </summary>
  15. [UsedImplicitly]
  16. public abstract class SharedSubFloorHideSystem : EntitySystem
  17. {
  18. [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
  19. [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!;
  20. [Dependency] protected readonly SharedMapSystem Map = default!;
  21. [Dependency] protected readonly SharedAppearanceSystem Appearance = default!;
  22. [Dependency] private readonly SharedVisibilitySystem _visibility = default!;
  23. private EntityQuery<SubFloorHideComponent> _hideQuery;
  24. public override void Initialize()
  25. {
  26. base.Initialize();
  27. _hideQuery = GetEntityQuery<SubFloorHideComponent>();
  28. SubscribeLocalEvent<TileChangedEvent>(OnTileChanged);
  29. SubscribeLocalEvent<SubFloorHideComponent, ComponentStartup>(OnSubFloorStarted);
  30. SubscribeLocalEvent<SubFloorHideComponent, ComponentShutdown>(OnSubFloorTerminating);
  31. // Like 80% sure this doesn't need to handle re-anchoring.
  32. SubscribeLocalEvent<SubFloorHideComponent, AnchorStateChangedEvent>(HandleAnchorChanged);
  33. SubscribeLocalEvent<SubFloorHideComponent, GettingInteractedWithAttemptEvent>(OnInteractionAttempt);
  34. SubscribeLocalEvent<SubFloorHideComponent, GettingAttackedAttemptEvent>(OnAttackAttempt);
  35. SubscribeLocalEvent<SubFloorHideComponent, GetExplosionResistanceEvent>(OnGetExplosionResistance);
  36. }
  37. private void OnGetExplosionResistance(EntityUid uid, SubFloorHideComponent component, ref GetExplosionResistanceEvent args)
  38. {
  39. if (component.BlockInteractions && component.IsUnderCover)
  40. args.DamageCoefficient = 0;
  41. }
  42. private void OnAttackAttempt(EntityUid uid, SubFloorHideComponent component, ref GettingAttackedAttemptEvent args)
  43. {
  44. if (component.BlockInteractions && component.IsUnderCover)
  45. args.Cancelled = true;
  46. }
  47. private void OnInteractionAttempt(EntityUid uid, SubFloorHideComponent component, ref GettingInteractedWithAttemptEvent args)
  48. {
  49. // No interactions with entities hidden under floor tiles.
  50. if (component.BlockInteractions && component.IsUnderCover)
  51. args.Cancelled = true;
  52. }
  53. private void OnSubFloorStarted(EntityUid uid, SubFloorHideComponent component, ComponentStartup _)
  54. {
  55. UpdateFloorCover(uid, component);
  56. UpdateAppearance(uid, component);
  57. EntityManager.EnsureComponent<CollideOnAnchorComponent>(uid);
  58. }
  59. private void OnSubFloorTerminating(EntityUid uid, SubFloorHideComponent component, ComponentShutdown _)
  60. {
  61. // If component is being deleted don't need to worry about updating any component stuff because it won't matter very shortly.
  62. if (EntityManager.GetComponent<MetaDataComponent>(uid).EntityLifeStage >= EntityLifeStage.Terminating)
  63. return;
  64. // Regardless of whether we're on a subfloor or not, unhide.
  65. SetUnderCover((uid, component), false);
  66. UpdateAppearance(uid, component);
  67. }
  68. private void HandleAnchorChanged(EntityUid uid, SubFloorHideComponent component, ref AnchorStateChangedEvent args)
  69. {
  70. if (args.Anchored)
  71. {
  72. var xform = Transform(uid);
  73. UpdateFloorCover(uid, component, xform);
  74. }
  75. else if (component.IsUnderCover)
  76. {
  77. SetUnderCover((uid, component), false);
  78. UpdateAppearance(uid, component);
  79. }
  80. }
  81. private void OnTileChanged(ref TileChangedEvent args)
  82. {
  83. if (args.OldTile.IsEmpty)
  84. return; // Nothing is anchored here anyways.
  85. if (args.NewTile.Tile.IsEmpty)
  86. return; // Anything that was here will be unanchored anyways.
  87. UpdateTile(args.NewTile.GridUid, args.Entity.Comp, args.NewTile.GridIndices);
  88. }
  89. /// <summary>
  90. /// Update whether a given entity is currently covered by a floor tile.
  91. /// </summary>
  92. private void UpdateFloorCover(EntityUid uid, SubFloorHideComponent? component = null, TransformComponent? xform = null)
  93. {
  94. if (!Resolve(uid, ref component, ref xform))
  95. return;
  96. if (xform.Anchored && TryComp<MapGridComponent>(xform.GridUid, out var grid))
  97. SetUnderCover((uid, component), HasFloorCover(xform.GridUid.Value, grid, Map.TileIndicesFor(xform.GridUid.Value, grid, xform.Coordinates)));
  98. else
  99. SetUnderCover((uid, component), false);
  100. UpdateAppearance(uid, component);
  101. }
  102. private void SetUnderCover(Entity<SubFloorHideComponent> entity, bool value)
  103. {
  104. // If it's not undercover or it always has visible layers then normal visibility.
  105. _visibility.SetLayer(entity.Owner, value && entity.Comp.VisibleLayers.Count == 0 ? (ushort) VisibilityFlags.Subfloor : (ushort) VisibilityFlags.Normal);
  106. if (entity.Comp.IsUnderCover == value)
  107. return;
  108. entity.Comp.IsUnderCover = value;
  109. }
  110. public bool HasFloorCover(EntityUid gridUid, MapGridComponent grid, Vector2i position)
  111. {
  112. // TODO Redo this function. Currently wires on an asteroid are always "below the floor"
  113. var tileDef = (ContentTileDefinition) _tileDefinitionManager[Map.GetTileRef(gridUid, grid, position).Tile.TypeId];
  114. return !tileDef.IsSubFloor;
  115. }
  116. private void UpdateTile(EntityUid gridUid, MapGridComponent grid, Vector2i position)
  117. {
  118. var covered = HasFloorCover(gridUid, grid, position);
  119. foreach (var uid in Map.GetAnchoredEntities(gridUid, grid, position))
  120. {
  121. if (!_hideQuery.TryComp(uid, out var hideComp))
  122. continue;
  123. if (hideComp.IsUnderCover == covered)
  124. continue;
  125. SetUnderCover((uid, hideComp), covered);
  126. UpdateAppearance(uid, hideComp);
  127. }
  128. }
  129. public void UpdateAppearance(
  130. EntityUid uid,
  131. SubFloorHideComponent? hideComp = null,
  132. AppearanceComponent? appearance = null)
  133. {
  134. if (!Resolve(uid, ref hideComp, false))
  135. return;
  136. if (hideComp.BlockAmbience && hideComp.IsUnderCover)
  137. _ambientSoundSystem.SetAmbience(uid, false);
  138. else if (hideComp.BlockAmbience && !hideComp.IsUnderCover)
  139. _ambientSoundSystem.SetAmbience(uid, true);
  140. if (Resolve(uid, ref appearance, false))
  141. {
  142. Appearance.SetData(uid, SubFloorVisuals.Covered, hideComp.IsUnderCover, appearance);
  143. }
  144. }
  145. [Serializable, NetSerializable]
  146. protected sealed class ShowSubfloorRequestEvent : EntityEventArgs
  147. {
  148. public bool Value;
  149. }
  150. }
  151. [Serializable, NetSerializable]
  152. public enum SubFloorVisuals : byte
  153. {
  154. /// <summary>
  155. /// Is there a floor tile over this entity
  156. /// </summary>
  157. Covered,
  158. /// <summary>
  159. /// Is this entity revealed by a scanner or some other entity?
  160. /// </summary>
  161. ScannerRevealed,
  162. }
  163. public enum SubfloorLayers : byte
  164. {
  165. FirstLayer
  166. }
  167. }