]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Temporarily make singularity a bit harder to loose as non-antag
authorSaphire <lattice@saphi.re>
Sat, 16 Nov 2024 21:58:15 +0000 (03:58 +0600)
committerSaphire <lattice@saphi.re>
Sat, 16 Nov 2024 22:01:38 +0000 (04:01 +0600)
Content.Server/Singularity/Components/SingularityGeneratorComponent.cs
Content.Server/Singularity/EntitySystems/SingularityGeneratorSystem.cs
Resources/Locale/en-US/singularity/components/generator-component.ftl [new file with mode: 0644]
Resources/Prototypes/Entities/Structures/Power/Generation/Singularity/generator.yml
Resources/Prototypes/Entities/Structures/Power/Generation/Tesla/generator.yml

index ea2628e5cb82868836b25fcf553f45e1f357259a..180b84995846d2f86264b174702fa0f369b3a04a 100644 (file)
@@ -2,32 +2,69 @@
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
 
 using Content.Server.Singularity.EntitySystems;
+using Content.Shared.Physics;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
 
 namespace Content.Server.Singularity.Components;
 
-[RegisterComponent]
+[RegisterComponent, AutoGenerateComponentPause]
+[Access(typeof(SingularityGeneratorSystem))]
 public sealed partial class SingularityGeneratorComponent : Component
 {
     /// <summary>
     /// The amount of power this generator has accumulated.
     /// If you want to set this use <see  cref="SingularityGeneratorSystem.SetPower"/>
     /// </summary>
-    [DataField("power")]
-    [Access(friends:typeof(SingularityGeneratorSystem))]
+    [DataField]
     public float Power = 0;
 
     /// <summary>
     /// The power threshold at which this generator will spawn a singularity.
     /// If you want to set this use <see  cref="SingularityGeneratorSystem.SetThreshold"/>
     /// </summary>
-    [DataField("threshold")]
-    [Access(friends:typeof(SingularityGeneratorSystem))]
+    [DataField]
     public float Threshold = 16;
 
+    /// <summary>
+    /// Allows the generator to ignore all the failsafe stuff, e.g. when emagged
+    /// </summary>
+    [DataField]
+    public bool FailsafeDisabled = false;
+
+    /// <summary>
+    /// Maximum distance at which the generator will check for a field at
+    /// </summary>
+    [DataField]
+    public float FailsafeDistance = 16;
+
     /// <summary>
     ///     The prototype ID used to spawn a singularity.
     /// </summary>
     [DataField("spawnId", customTypeSerializer: typeof(PrototypeIdSerializer<EntityPrototype>))]
-    [ViewVariables(VVAccess.ReadWrite)]
     public string? SpawnPrototype = "Singularity";
+
+    /// <summary>
+    /// The masks the raycast should not go through
+    /// </summary>
+    [DataField]
+    public int CollisionMask = (int)CollisionGroup.FullTileMask;
+
+    /// <summary>
+    /// Message to use when there's no containment field on cardinal directions
+    /// </summary>
+    [DataField]
+    public LocId ContainmentFailsafeMessage;
+
+    /// <summary>
+    /// For how long the failsafe will cause the generator to stop working and not issue a failsafe warning
+    /// </summary>
+    [DataField]
+    public TimeSpan FailsafeCooldown = TimeSpan.FromSeconds(30);
+
+    /// <summary>
+    /// How long until the generator can issue a failsafe warning again
+    /// </summary>
+    [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+    [AutoPausedField]
+    public TimeSpan NextFailsafe;
 }
index a0c0262794824b1abd26f55822657cb955dd8fba..be0c5e49b5f612d5b5041031b478f47cf4b5e2d6 100644 (file)
@@ -1,7 +1,15 @@
+using System.Diagnostics;
 using Content.Server.ParticleAccelerator.Components;
+using Content.Server.Popups;
 using Content.Server.Singularity.Components;
+using Content.Shared.Emag.Systems;
+using Content.Shared.Popups;
 using Content.Shared.Singularity.Components;
+using Robust.Server.GameObjects;
+using Robust.Shared.Physics;
+using Robust.Shared.Physics.Components;
 using Robust.Shared.Physics.Events;
+using Robust.Shared.Timing;
 
 namespace Content.Server.Singularity.EntitySystems;
 
@@ -9,6 +17,11 @@ public sealed class SingularityGeneratorSystem : EntitySystem
 {
     #region Dependencies
     [Dependency] private readonly IViewVariablesManager _vvm = default!;
+    [Dependency] private readonly SharedTransformSystem _transformSystem = default!;
+    [Dependency] private readonly PhysicsSystem _physics = default!;
+    [Dependency] private readonly IGameTiming _timing = default!;
+    [Dependency] private readonly MetaDataSystem _metadata = default!;
+    [Dependency] private readonly PopupSystem _popupSystem = default!;
     #endregion Dependencies
 
     public override void Initialize()
@@ -16,6 +29,7 @@ public sealed class SingularityGeneratorSystem : EntitySystem
         base.Initialize();
 
         SubscribeLocalEvent<ParticleProjectileComponent, StartCollideEvent>(HandleParticleCollide);
+        SubscribeLocalEvent<SingularityGeneratorComponent, GotEmaggedEvent>(OnEmagged);
 
         var vvHandle = _vvm.GetTypeHandler<SingularityGeneratorComponent>();
         vvHandle.AddPath(nameof(SingularityGeneratorComponent.Power), (_, comp) => comp.Power, SetPower);
@@ -100,11 +114,33 @@ public sealed class SingularityGeneratorSystem : EntitySystem
     /// <param name="args">The state of the beginning of the collision.</param>
     private void HandleParticleCollide(EntityUid uid, ParticleProjectileComponent component, ref StartCollideEvent args)
     {
-        if (EntityManager.TryGetComponent<SingularityGeneratorComponent>(args.OtherEntity, out var singularityGeneratorComponent))
+        if (!EntityManager.TryGetComponent<SingularityGeneratorComponent>(args.OtherEntity, out var generatorComp))
+            return;
+
+        if (_timing.CurTime < _metadata.GetPauseTime(uid) + generatorComp.NextFailsafe)
+        {
+            EntityManager.QueueDeleteEntity(uid);
+            return;
+        }
+
+        var contained = true;
+        var transform = Transform(args.OtherEntity);
+        var directions = Enum.GetValues<Direction>().Length;
+        for (var i = 0; i < directions - 1; i += 2) // Skip every other direction, checking only cardinals
+        {
+            if (!CheckContainmentField((Direction)i, new Entity<SingularityGeneratorComponent>(args.OtherEntity, generatorComp), transform))
+                contained = false;
+        }
+
+        if (!contained)
         {
+            generatorComp.NextFailsafe = _timing.CurTime + generatorComp.FailsafeCooldown;
+            _popupSystem.PopupEntity(Loc.GetString("comp-generator-failsafe", ("target", args.OtherEntity)), args.OtherEntity, PopupType.LargeCaution);
+        }
+        else
             SetPower(
                 args.OtherEntity,
-                singularityGeneratorComponent.Power + component.State switch
+                generatorComp.Power + component.State switch
                 {
                     ParticleAcceleratorPowerState.Standby => 0,
                     ParticleAcceleratorPowerState.Level0 => 1,
@@ -113,10 +149,51 @@ public sealed class SingularityGeneratorSystem : EntitySystem
                     ParticleAcceleratorPowerState.Level3 => 8,
                     _ => 0
                 },
-                singularityGeneratorComponent
+                generatorComp
             );
-            EntityManager.QueueDeleteEntity(uid);
-        }
+        EntityManager.QueueDeleteEntity(uid);
+    }
+
+    private void OnEmagged(EntityUid uid, SingularityGeneratorComponent component, ref GotEmaggedEvent args)
+    {
+        _popupSystem.PopupEntity(Loc.GetString("comp-generator-failsafe-disabled", ("target", uid)), uid);
+        component.FailsafeDisabled = true;
+        args.Handled = true;
     }
     #endregion Event Handlers
+
+    /// <summary>
+    /// Checks whether there's a containment field in a given direction away from the generator
+    /// </summary>
+    /// <param name="transform">The transform component of the singularity generator.</param>
+    /// <remarks>Mostly copied from <see cref="ContainmentFieldGeneratorSystem"/> </remarks>
+    private bool CheckContainmentField(Direction dir, Entity<SingularityGeneratorComponent> generator, TransformComponent transform)
+    {
+        var component = generator.Comp;
+
+        var (worldPosition, worldRotation) = _transformSystem.GetWorldPositionRotation(transform);
+        var dirRad = dir.ToAngle() + worldRotation;
+
+        var ray = new CollisionRay(worldPosition, dirRad.ToVec(), component.CollisionMask);
+        var rayCastResults = _physics.IntersectRay(transform.MapID, ray, component.FailsafeDistance, generator, false);
+        var genQuery = GetEntityQuery<ContainmentFieldComponent>();
+
+        RayCastResults? closestResult = null;
+
+        foreach (var result in rayCastResults)
+        {
+            if (genQuery.HasComponent(result.HitEntity))
+                closestResult = result;
+
+            break;
+        }
+
+        if (closestResult == null)
+            return false;
+
+        var ent = closestResult.Value.HitEntity;
+
+        // Check that the field can't be moved. The fields' transform parenting is weird, so skip that
+        return TryComp<PhysicsComponent>(ent, out var collidableComponent) && collidableComponent.BodyType == BodyType.Static;
+    }
 }
diff --git a/Resources/Locale/en-US/singularity/components/generator-component.ftl b/Resources/Locale/en-US/singularity/components/generator-component.ftl
new file mode 100644 (file)
index 0000000..f3a2254
--- /dev/null
@@ -0,0 +1,2 @@
+comp-generator-failsafe = The {$target} shakes as the containment failsafe triggers!
+comp-generator-failsafe = Something fizzles out inside of {$target}...
\ No newline at end of file
index 647eae27724774dd8aa6268d41c8f2a7c0228445..45a40bf0faf3150c76ed6b60c7b8e9a8f7106f4b 100644 (file)
@@ -1,7 +1,7 @@
 - type: entity
   id: SingularityGenerator
   name: gravitational singularity generator
-  description: An Odd Device which produces a Gravitational Singularity when set up.
+  description: An Odd Device which produces a Gravitational Singularity when set up. Comes with a temporary shutdown containment failsafe.
   placement:
     mode: SnapgridCenter
   components:
index d45e6c58ea753a8ceab0d91262894871f86f7e63..bdd90f2f16afba852a5ae91053857ece9f7da4ec 100644 (file)
@@ -2,12 +2,12 @@
   id: TeslaGenerator
   name: tesla generator
   parent: BaseStructureDynamic
-  description: An Odd Device which produces a powerful Tesla ball when set up.
+  description: An Odd Device which produces a powerful Tesla ball when set up. Comes with a temporary shutdown containment failsafe.
   components:
   - type: Sprite
     noRot: true
     sprite: Structures/Power/Generation/Tesla/generator.rsi
-    state: icon 
+    state: icon
   - type: SingularityGenerator # TODO: rename the generator
     spawnId: TeslaEnergyBall
   - type: InteractionOutline