1
0

SharedEventHorizonSystem.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243
  1. using Robust.Shared.Map.Components;
  2. using Robust.Shared.Physics.Collision.Shapes;
  3. using Robust.Shared.Physics.Components;
  4. using Robust.Shared.Physics.Events;
  5. using Robust.Shared.Physics.Systems;
  6. using Content.Shared.Ghost;
  7. using Content.Shared.Singularity.Components;
  8. using Robust.Shared.Physics;
  9. namespace Content.Shared.Singularity.EntitySystems;
  10. /// <summary>
  11. /// The entity system primarily responsible for managing <see cref="EventHorizonComponent"/>s.
  12. /// </summary>
  13. public abstract class SharedEventHorizonSystem : EntitySystem
  14. {
  15. [Dependency] private readonly FixtureSystem _fixtures = default!;
  16. [Dependency] private readonly SharedPhysicsSystem _physics = default!;
  17. [Dependency] protected readonly IViewVariablesManager Vvm = default!;
  18. public override void Initialize()
  19. {
  20. base.Initialize();
  21. // Allows for predicted collisions with singularities.
  22. SubscribeLocalEvent<EventHorizonComponent, ComponentStartup>(OnEventHorizonStartup);
  23. SubscribeLocalEvent<EventHorizonComponent, PreventCollideEvent>(OnPreventCollide);
  24. var vvHandle = Vvm.GetTypeHandler<EventHorizonComponent>();
  25. vvHandle.AddPath(nameof(EventHorizonComponent.Radius), (_, comp) => comp.Radius, (uid, value, comp) => SetRadius(uid, value, eventHorizon: comp));
  26. vvHandle.AddPath(nameof(EventHorizonComponent.CanBreachContainment), (_, comp) => comp.CanBreachContainment, (uid, value, comp) => SetCanBreachContainment(uid, value, eventHorizon: comp));
  27. vvHandle.AddPath(nameof(EventHorizonComponent.ColliderFixtureId), (_, comp) => comp.ColliderFixtureId, (uid, value, comp) => SetColliderFixtureId(uid, value, eventHorizon: comp));
  28. vvHandle.AddPath(nameof(EventHorizonComponent.ConsumerFixtureId), (_, comp) => comp.ConsumerFixtureId, (uid, value, comp) => SetConsumerFixtureId(uid, value, eventHorizon: comp));
  29. }
  30. public override void Shutdown()
  31. {
  32. var vvHandle = Vvm.GetTypeHandler<EventHorizonComponent>();
  33. vvHandle.RemovePath(nameof(EventHorizonComponent.Radius));
  34. vvHandle.RemovePath(nameof(EventHorizonComponent.CanBreachContainment));
  35. vvHandle.RemovePath(nameof(EventHorizonComponent.ColliderFixtureId));
  36. vvHandle.RemovePath(nameof(EventHorizonComponent.ConsumerFixtureId));
  37. base.Shutdown();
  38. }
  39. #region Getters/Setters
  40. /// <summary>
  41. /// Setter for <see cref="EventHorizonComponent.Radius"/>
  42. /// May also update the fixture associated with the event horizon.
  43. /// </summary>
  44. /// <param name="uid">The uid of the event horizon to change the radius of.</param>
  45. /// <param name="value">The new radius of the event horizon.</param>
  46. /// <param name="updateFixture">Whether to update the associated fixture upon changing the radius of the event horizon.</param>
  47. /// <param name="eventHorizon">The state of the event horizon to change the radius of.</param>
  48. public void SetRadius(EntityUid uid, float value, bool updateFixture = true, EventHorizonComponent? eventHorizon = null)
  49. {
  50. if (!Resolve(uid, ref eventHorizon))
  51. return;
  52. var oldValue = eventHorizon.Radius;
  53. if (value == oldValue)
  54. return;
  55. eventHorizon.Radius = value;
  56. Dirty(uid, eventHorizon);
  57. if (updateFixture)
  58. UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon);
  59. }
  60. /// <summary>
  61. /// Setter for <see cref="EventHorizonComponent.CanBreachContainment"/>
  62. /// May also update the fixture associated with the event horizon.
  63. /// </summary>
  64. /// <param name="uid">The uid of the event horizon to make (in)capable of breaching containment.</param>
  65. /// <param name="value">Whether the event horizon should be able to breach containment.</param>
  66. /// <param name="updateFixture">Whether to update the associated fixture upon changing whether the event horizon can breach containment.</param>
  67. /// <param name="eventHorizon">The state of the event horizon to make (in)capable of breaching containment.</param>
  68. public void SetCanBreachContainment(EntityUid uid, bool value, bool updateFixture = true, EventHorizonComponent? eventHorizon = null)
  69. {
  70. if (!Resolve(uid, ref eventHorizon))
  71. return;
  72. var oldValue = eventHorizon.CanBreachContainment;
  73. if (value == oldValue)
  74. return;
  75. eventHorizon.CanBreachContainment = value;
  76. Dirty(uid, eventHorizon);
  77. if (updateFixture)
  78. UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon);
  79. }
  80. /// <summary>
  81. /// Setter for <see cref="EventHorizonComponent.HorizonFixtureId"/>
  82. /// May also update the fixture associated with the event horizon.
  83. /// </summary>
  84. /// <param name="uid">The uid of the event horizon with the fixture ID to change.</param>
  85. /// <param name="value">The new fixture ID to associate the event horizon with.</param>
  86. /// <param name="updateFixture">Whether to update the associated fixture upon changing whether the event horizon can breach containment.</param>
  87. /// <param name="eventHorizon">The state of the event horizon with the fixture ID to change.</param>
  88. public void SetColliderFixtureId(EntityUid uid, string? value, bool updateFixture = true, EventHorizonComponent? eventHorizon = null)
  89. {
  90. if (!Resolve(uid, ref eventHorizon))
  91. return;
  92. var oldValue = eventHorizon.ColliderFixtureId;
  93. if (value == oldValue)
  94. return;
  95. eventHorizon.ColliderFixtureId = value;
  96. Dirty(uid, eventHorizon);
  97. if (updateFixture)
  98. UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon);
  99. }
  100. /// <summary>
  101. /// Setter for <see cref="EventHorizonComponent.HorizonFixtureId"/>
  102. /// May also update the fixture associated with the event horizon.
  103. /// </summary>
  104. /// <param name="uid">The uid of the event horizon with the fixture ID to change.</param>
  105. /// <param name="value">The new fixture ID to associate the event horizon with.</param>
  106. /// <param name="updateFixture">Whether to update the associated fixture upon changing whether the event horizon can breach containment.</param>
  107. /// <param name="eventHorizon">The state of the event horizon with the fixture ID to change.</param>
  108. public void SetConsumerFixtureId(EntityUid uid, string? value, bool updateFixture = true, EventHorizonComponent? eventHorizon = null)
  109. {
  110. if (!Resolve(uid, ref eventHorizon))
  111. return;
  112. var oldValue = eventHorizon.ConsumerFixtureId;
  113. if (value == oldValue)
  114. return;
  115. eventHorizon.ConsumerFixtureId = value;
  116. Dirty(uid, eventHorizon);
  117. if (updateFixture)
  118. UpdateEventHorizonFixture(uid, eventHorizon: eventHorizon);
  119. }
  120. /// <summary>
  121. /// Updates the state of the fixture associated with the event horizon.
  122. /// </summary>
  123. /// <param name="uid">The uid of the event horizon associated with the fixture to update.</param>
  124. /// <param name="fixtures">The fixture manager component containing the fixture to update.</param>
  125. /// <param name="eventHorizon">The state of the event horizon associated with the fixture to update.</param>
  126. public void UpdateEventHorizonFixture(EntityUid uid, FixturesComponent? fixtures = null, EventHorizonComponent? eventHorizon = null)
  127. {
  128. if (!Resolve(uid, ref eventHorizon))
  129. return;
  130. var consumerId = eventHorizon.ConsumerFixtureId;
  131. var colliderId = eventHorizon.ColliderFixtureId;
  132. if (consumerId == null || colliderId == null
  133. || !Resolve(uid, ref fixtures, logMissing: false))
  134. return;
  135. // Update both fixtures the event horizon is associated with:
  136. var consumer = _fixtures.GetFixtureOrNull(uid, consumerId, fixtures);
  137. if (consumer != null)
  138. {
  139. _physics.SetRadius(uid, consumerId, consumer, consumer.Shape, eventHorizon.Radius, fixtures);
  140. _physics.SetHard(uid, consumer, false, fixtures);
  141. }
  142. var collider = _fixtures.GetFixtureOrNull(uid, colliderId, fixtures);
  143. if (collider != null)
  144. {
  145. _physics.SetRadius(uid, colliderId, collider, collider.Shape, eventHorizon.Radius, fixtures);
  146. _physics.SetHard(uid, collider, true, fixtures);
  147. }
  148. EntityManager.Dirty(uid, fixtures);
  149. }
  150. #endregion Getters/Setters
  151. #region EventHandlers
  152. /// <summary>
  153. /// Syncs the state of the fixture associated with the event horizon upon startup.
  154. /// </summary>
  155. /// <param name="uid">The entity that has just gained an event horizon component.</param>
  156. /// <param name="comp">The event horizon component that is starting up.</param>
  157. /// <param name="args">The event arguments.</param>
  158. private void OnEventHorizonStartup(EntityUid uid, EventHorizonComponent comp, ComponentStartup args)
  159. {
  160. UpdateEventHorizonFixture(uid, eventHorizon: comp);
  161. }
  162. /// <summary>
  163. /// Prevents the event horizon from colliding with anything it cannot consume.
  164. /// Most notably map grids and ghosts.
  165. /// Also makes event horizons phase through containment if it can breach.
  166. /// </summary>
  167. /// <param name="uid">The entity that is trying to collide with another entity.</param>
  168. /// <param name="comp">The event horizon of the former.</param>
  169. /// <param name="args">The event arguments.</param>
  170. private void OnPreventCollide(EntityUid uid, EventHorizonComponent comp, ref PreventCollideEvent args)
  171. {
  172. if (!args.Cancelled)
  173. PreventCollide(uid, comp, ref args);
  174. }
  175. /// <summary>
  176. /// The actual, functional part of SharedEventHorizonSystem.OnPreventCollide.
  177. /// The return value allows for overrides to early return if the base successfully handles collision prevention.
  178. /// </summary>
  179. /// <param name="uid">The entity that is trying to collide with another entity.</param>
  180. /// <param name="comp">The event horizon of the former.</param>
  181. /// <param name="args">The event arguments.</param>
  182. /// <returns>A bool indicating whether the collision prevention has been handled.</returns>
  183. protected virtual bool PreventCollide(EntityUid uid, EventHorizonComponent comp, ref PreventCollideEvent args)
  184. {
  185. var otherUid = args.OtherEntity;
  186. // For prediction reasons always want the client to ignore these.
  187. if (HasComp<MapGridComponent>(otherUid) ||
  188. HasComp<GhostComponent>(otherUid))
  189. {
  190. args.Cancelled = true;
  191. return true;
  192. }
  193. // If we can, breach containment
  194. // otherwise, check if it's containment and just keep the collision
  195. if (HasComp<ContainmentFieldComponent>(otherUid) ||
  196. HasComp<ContainmentFieldGeneratorComponent>(otherUid))
  197. {
  198. if (comp.CanBreachContainment)
  199. args.Cancelled = true;
  200. return true;
  201. }
  202. return false;
  203. }
  204. #endregion EventHandlers
  205. }