1
0

SingularityGeneratorSystem.cs 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193
  1. using Content.Server.ParticleAccelerator.Components;
  2. using Content.Shared.Popups;
  3. using Content.Shared.Singularity.Components;
  4. using Content.Shared.Singularity.EntitySystems;
  5. using Robust.Server.GameObjects;
  6. using Robust.Shared.Physics;
  7. using Robust.Shared.Physics.Components;
  8. using Robust.Shared.Physics.Events;
  9. using Robust.Shared.Timing;
  10. namespace Content.Server.Singularity.EntitySystems;
  11. public sealed class SingularityGeneratorSystem : SharedSingularityGeneratorSystem
  12. {
  13. #region Dependencies
  14. [Dependency] private readonly IViewVariablesManager _vvm = default!;
  15. [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
  16. [Dependency] private readonly PhysicsSystem _physics = default!;
  17. [Dependency] private readonly IGameTiming _timing = default!;
  18. [Dependency] private readonly MetaDataSystem _metadata = default!;
  19. #endregion Dependencies
  20. public override void Initialize()
  21. {
  22. base.Initialize();
  23. SubscribeLocalEvent<ParticleProjectileComponent, StartCollideEvent>(HandleParticleCollide);
  24. var vvHandle = _vvm.GetTypeHandler<SingularityGeneratorComponent>();
  25. vvHandle.AddPath(nameof(SingularityGeneratorComponent.Power), (_, comp) => comp.Power, SetPower);
  26. vvHandle.AddPath(nameof(SingularityGeneratorComponent.Threshold), (_, comp) => comp.Threshold, SetThreshold);
  27. }
  28. public override void Shutdown()
  29. {
  30. var vvHandle = _vvm.GetTypeHandler<SingularityGeneratorComponent>();
  31. vvHandle.RemovePath(nameof(SingularityGeneratorComponent.Power));
  32. vvHandle.RemovePath(nameof(SingularityGeneratorComponent.Threshold));
  33. base.Shutdown();
  34. }
  35. /// <summary>
  36. /// Handles what happens when a singularity generator passes its power threshold.
  37. /// Default behavior is to reset the singularities power level and spawn a singularity.
  38. /// </summary>
  39. /// <param name="uid">The uid of the singularity generator.</param>
  40. /// <param name="comp">The state of the singularity generator.</param>
  41. private void OnPassThreshold(EntityUid uid, SingularityGeneratorComponent? comp)
  42. {
  43. if (!Resolve(uid, ref comp))
  44. return;
  45. SetPower(uid, 0, comp);
  46. EntityManager.SpawnEntity(comp.SpawnPrototype, Transform(uid).Coordinates);
  47. }
  48. #region Getters/Setters
  49. /// <summary>
  50. /// Setter for <see cref="SingularityGeneratorComponent.Power"/>
  51. /// If the singularity generator passes its threshold it also spawns a singularity.
  52. /// </summary>
  53. /// <param name="comp">The singularity generator component.</param>
  54. /// <param name="value">The new power level for the generator component to have.</param>
  55. public void SetPower(EntityUid uid, float value, SingularityGeneratorComponent? comp = null)
  56. {
  57. if (!Resolve(uid, ref comp))
  58. return;
  59. var oldValue = comp.Power;
  60. if (value == oldValue)
  61. return;
  62. comp.Power = value;
  63. if (comp.Power >= comp.Threshold && oldValue < comp.Threshold)
  64. OnPassThreshold(uid, comp);
  65. }
  66. /// <summary>
  67. /// Setter for <see cref="SingularityGeneratorComponent.Threshold"/>
  68. /// If the singularity generator has passed its new threshold it also spawns a singularity.
  69. /// </summary>
  70. /// <param name="comp">The singularity generator component.</param>
  71. /// <param name="value">The new threshold power level for the generator component to have.</param>
  72. public void SetThreshold(EntityUid uid, float value, SingularityGeneratorComponent? comp = null)
  73. {
  74. if (!Resolve(uid, ref comp))
  75. return;
  76. var oldValue = comp.Threshold;
  77. if (value == comp.Threshold)
  78. return;
  79. comp.Power = value;
  80. if (comp.Power >= comp.Threshold && comp.Power < oldValue)
  81. OnPassThreshold(uid, comp);
  82. }
  83. #endregion Getters/Setters
  84. #region Event Handlers
  85. /// <summary>
  86. /// Handles PA Particles colliding with a singularity generator.
  87. /// Adds the power from the particles to the generator.
  88. /// TODO: Desnowflake this.
  89. /// </summary>
  90. /// <param name="uid">The uid of the PA particles have collided with.</param>
  91. /// <param name="component">The state of the PA particles.</param>
  92. /// <param name="args">The state of the beginning of the collision.</param>
  93. private void HandleParticleCollide(EntityUid uid, ParticleProjectileComponent component, ref StartCollideEvent args)
  94. {
  95. if (!EntityManager.TryGetComponent<SingularityGeneratorComponent>(args.OtherEntity, out var generatorComp))
  96. return;
  97. if (_timing.CurTime < _metadata.GetPauseTime(uid) + generatorComp.NextFailsafe && !generatorComp.FailsafeDisabled)
  98. {
  99. EntityManager.QueueDeleteEntity(uid);
  100. return;
  101. }
  102. var contained = true;
  103. if (!generatorComp.FailsafeDisabled)
  104. {
  105. var transform = Transform(args.OtherEntity);
  106. var directions = Enum.GetValues<Direction>().Length;
  107. for (var i = 0; i < directions - 1; i += 2) // Skip every other direction, checking only cardinals
  108. {
  109. if (!CheckContainmentField((Direction)i, new Entity<SingularityGeneratorComponent>(args.OtherEntity, generatorComp), transform))
  110. contained = false;
  111. }
  112. }
  113. if (!contained && !generatorComp.FailsafeDisabled)
  114. {
  115. generatorComp.NextFailsafe = _timing.CurTime + generatorComp.FailsafeCooldown;
  116. PopupSystem.PopupEntity(Loc.GetString("comp-generator-failsafe", ("target", args.OtherEntity)), args.OtherEntity, PopupType.LargeCaution);
  117. }
  118. else
  119. {
  120. SetPower(
  121. args.OtherEntity,
  122. generatorComp.Power + component.State switch
  123. {
  124. ParticleAcceleratorPowerState.Standby => 0,
  125. ParticleAcceleratorPowerState.Level0 => 1,
  126. ParticleAcceleratorPowerState.Level1 => 2,
  127. ParticleAcceleratorPowerState.Level2 => 4,
  128. ParticleAcceleratorPowerState.Level3 => 8,
  129. _ => 0
  130. },
  131. generatorComp
  132. );
  133. }
  134. EntityManager.QueueDeleteEntity(uid);
  135. }
  136. #endregion Event Handlers
  137. /// <summary>
  138. /// Checks whether there's a containment field in a given direction away from the generator
  139. /// </summary>
  140. /// <param name="transform">The transform component of the singularity generator.</param>
  141. /// <remarks>Mostly copied from <see cref="ContainmentFieldGeneratorSystem"/> </remarks>
  142. private bool CheckContainmentField(Direction dir, Entity<SingularityGeneratorComponent> generator, TransformComponent transform)
  143. {
  144. var component = generator.Comp;
  145. var (worldPosition, worldRotation) = _transformSystem.GetWorldPositionRotation(transform);
  146. var dirRad = dir.ToAngle() + worldRotation;
  147. var ray = new CollisionRay(worldPosition, dirRad.ToVec(), component.CollisionMask);
  148. var rayCastResults = _physics.IntersectRay(transform.MapID, ray, component.FailsafeDistance, generator, false);
  149. var genQuery = GetEntityQuery<ContainmentFieldComponent>();
  150. RayCastResults? closestResult = null;
  151. foreach (var result in rayCastResults)
  152. {
  153. if (genQuery.HasComponent(result.HitEntity))
  154. closestResult = result;
  155. break;
  156. }
  157. if (closestResult == null)
  158. return false;
  159. var ent = closestResult.Value.HitEntity;
  160. // Check that the field can't be moved. The fields' transform parenting is weird, so skip that
  161. return TryComp<PhysicsComponent>(ent, out var collidableComponent) && collidableComponent.BodyType == BodyType.Static;
  162. }
  163. }