]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
TriggerOnPlayerSpawnComplete and ExplosionOnTrigger (#39820)
authorāda <ss.adasts@gmail.com>
Tue, 23 Sep 2025 20:24:45 +0000 (15:24 -0500)
committerGitHub <noreply@github.com>
Tue, 23 Sep 2025 20:24:45 +0000 (22:24 +0200)
Content.Server/Explosion/EntitySystems/ExplosionSystem.cs
Content.Shared/Explosion/EntitySystems/SharedExplosionSystem.cs
Content.Shared/Trigger/Components/Effects/ExplodeOnTriggerComponent.cs
Content.Shared/Trigger/Components/Effects/ExplosionOnTriggerComponent.cs [new file with mode: 0644]
Content.Shared/Trigger/Components/Triggers/TriggerOnPlayerSpawnCompleteComponent.cs [new file with mode: 0644]
Content.Shared/Trigger/Systems/ExplodeOnTriggerSystem.cs
Content.Shared/Trigger/Systems/TriggerSystem.Spawn.cs

index fc31a7704124f444b9e2d88d61ff5e75fa069556..b459f5c70f59c0413f7b1d102d57fe7cf29c93e0 100644 (file)
@@ -63,16 +63,6 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
 
     public const int MaxExplosionAudioRange = 30;
 
-    /// <summary>
-    ///     The "default" explosion prototype.
-    /// </summary>
-    /// <remarks>
-    ///     Generally components should specify an explosion prototype via a yaml datafield, so that the yaml-linter can
-    ///     find errors. However some components, like rogue arrows, or some commands like the admin-smite need to have
-    ///     a "default" option specified outside of yaml data-fields. Hence this const string.
-    /// </remarks>
-    public static readonly ProtoId<ExplosionPrototype> DefaultExplosionPrototypeId = "Default";
-
     public override void Initialize()
     {
         base.Initialize();
@@ -222,10 +212,8 @@ public sealed partial class ExplosionSystem : SharedExplosionSystem
         return r0 * (MathF.Sqrt(12 * totalIntensity / v0 - 3) / 6 + 0.5f);
     }
 
-    /// <summary>
-    ///     Queue an explosions, centered on some entity.
-    /// </summary>
-    public void QueueExplosion(EntityUid uid,
+    /// <inheritdoc />
+    public override void QueueExplosion(EntityUid uid,
         string typeId,
         float totalIntensity,
         float slope,
index f29825580784b6d9ef25975442d73574fa6d5cf0..d6053c9c3c72a270e7c4c1c2242b9eca50c364c6 100644 (file)
@@ -1,14 +1,26 @@
 using Content.Shared.Armor;
 using Content.Shared.Explosion.Components;
+using Robust.Shared.Prototypes;
 
 namespace Content.Shared.Explosion.EntitySystems;
 
+// TODO some sort of struct like DamageSpecifier but for explosions.
 /// <summary>
 /// Lets code in shared trigger explosions and handles explosion resistance examining.
 /// All processing is still done clientside.
 /// </summary>
 public abstract class SharedExplosionSystem : EntitySystem
 {
+    /// <summary>
+    ///     The "default" explosion prototype.
+    /// </summary>
+    /// <remarks>
+    ///     Generally components should specify an explosion prototype via a yaml datafield, so that the yaml-linter can
+    ///     find errors. However some components, like rogue arrows, or some commands like the admin-smite need to have
+    ///     a "default" option specified outside of yaml data-fields. Hence this const string.
+    /// </remarks>
+    public static readonly ProtoId<ExplosionPrototype> DefaultExplosionPrototypeId = "Default";
+
     public override void Initialize()
     {
         base.Initialize();
@@ -37,4 +49,24 @@ public abstract class SharedExplosionSystem : EntitySystem
     public virtual void TriggerExplosive(EntityUid uid, ExplosiveComponent? explosive = null, bool delete = true, float? totalIntensity = null, float? radius = null, EntityUid? user = null)
     {
     }
+
+    /// <summary>
+    /// Queue an explosion centered on some entity. Bypasses needing <see cref="ExplosiveComponent"/>.
+    /// </summary>
+    /// <param name="uid">Where the explosion happens.</param>
+    /// <param name="typeId">A ProtoId of type <see cref="ExplosionPrototype"/>.</param>
+    /// <param name="user">The entity which caused the explosion.</param>
+    /// <param name="addLog">Whether to add an admin log about this explosion. Includes user.</param>
+    public virtual void QueueExplosion(EntityUid uid,
+                                        string typeId,
+                                        float totalIntensity,
+                                        float slope,
+                                        float maxTileIntensity,
+                                        float tileBreakScale = 1f,
+                                        int maxTileBreak = int.MaxValue,
+                                        bool canCreateVacuum = true,
+                                        EntityUid? user = null,
+                                        bool addLog = true)
+    {
+    }
 }
index 2a1af40a2cd572e006deb4eee2424b0f60545fcc..9bb7ce9fa0ef6e7e107c2525617210d6cb0d3494 100644 (file)
@@ -1,3 +1,4 @@
+using Content.Shared.Explosion.Components;
 using Robust.Shared.GameStates;
 
 namespace Content.Shared.Trigger.Components.Effects;
@@ -7,8 +8,6 @@ namespace Content.Shared.Trigger.Components.Effects;
 /// TargetUser will only work of the user has ExplosiveComponent as well.
 /// The User will be logged in the admin logs.
 /// </summary>
-/// <summary>
-/// TODO: Allow this to work without an ExplosiveComponent on the user via QueueExplosion.
-/// </summary>
+/// <seealso cref="ExplosionOnTriggerComponent"/>
 [RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
 public sealed partial class ExplodeOnTriggerComponent : BaseXOnTriggerComponent;
diff --git a/Content.Shared/Trigger/Components/Effects/ExplosionOnTriggerComponent.cs b/Content.Shared/Trigger/Components/Effects/ExplosionOnTriggerComponent.cs
new file mode 100644 (file)
index 0000000..40b59e7
--- /dev/null
@@ -0,0 +1,46 @@
+using Content.Shared.Explosion;
+using Content.Shared.Explosion.Components;
+using Content.Shared.Explosion.EntitySystems;
+using Robust.Shared.GameStates;
+using Robust.Shared.Prototypes;
+
+namespace Content.Shared.Trigger.Components.Effects;
+
+// TODO some sort of struct like DamageSpecifier but for explosions.
+/// <summary>
+/// Will explode the entity using this component's explosion specifications.
+/// If TargetUser is true, they'll explode instead.
+/// The User will be logged in the admin logs.
+/// </summary>
+/// <seealso cref="ExplodeOnTriggerComponent"/>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class ExplosionOnTriggerComponent : BaseXOnTriggerComponent
+{
+    /// <inheritdoc cref="ExplosiveComponent.ExplosionType"/>
+    [DataField, AutoNetworkedField]
+    public ProtoId<ExplosionPrototype> ExplosionType = SharedExplosionSystem.DefaultExplosionPrototypeId;
+
+    /// <inheritdoc cref="ExplosiveComponent.MaxIntensity"/>
+    [DataField, AutoNetworkedField]
+    public float MaxTileIntensity = 4;
+
+    /// <inheritdoc cref="ExplosiveComponent.IntensitySlope"/>
+    [DataField, AutoNetworkedField]
+    public float IntensitySlope = 1;
+
+    /// <inheritdoc cref="ExplosiveComponent.TotalIntensity"/>
+    [DataField, AutoNetworkedField]
+    public float TotalIntensity = 10;
+
+    /// <inheritdoc cref="ExplosiveComponent.TileBreakScale"/>
+    [DataField, AutoNetworkedField]
+    public float TileBreakScale = 1f;
+
+    /// <inheritdoc cref="ExplosiveComponent.MaxTileBreak"/>
+    [DataField, AutoNetworkedField]
+    public int MaxTileBreak = int.MaxValue;
+
+    /// <inheritdoc cref="ExplosiveComponent.CanCreateVacuum"/>
+    [DataField, AutoNetworkedField]
+    public bool CanCreateVacuum = true;
+}
diff --git a/Content.Shared/Trigger/Components/Triggers/TriggerOnPlayerSpawnCompleteComponent.cs b/Content.Shared/Trigger/Components/Triggers/TriggerOnPlayerSpawnCompleteComponent.cs
new file mode 100644 (file)
index 0000000..2151b7e
--- /dev/null
@@ -0,0 +1,11 @@
+using Content.Shared.GameTicking;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Trigger.Components.Triggers;
+
+/// <summary>
+/// A trigger which occurs on <see cref="PlayerSpawnCompleteEvent"/>.
+/// </summary>
+/// <remarks>This does not work with <see cref="TraitSystem"/>, as it would add this component while the event is getting raised.</remarks>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class TriggerOnPlayerSpawnCompleteComponent : BaseTriggerOnXComponent;
index 1c773b79a62394874e30e0c88c16de7e93865f28..120aa23a9d8c275d1e18133a1ce712dac347ee1c 100644 (file)
@@ -11,10 +11,11 @@ public sealed class ExplodeOnTriggerSystem : EntitySystem
     {
         base.Initialize();
 
-        SubscribeLocalEvent<ExplodeOnTriggerComponent, TriggerEvent>(OnTrigger);
+        SubscribeLocalEvent<ExplodeOnTriggerComponent, TriggerEvent>(OnExplodeTrigger);
+        SubscribeLocalEvent<ExplosionOnTriggerComponent, TriggerEvent>(OnQueueExplosionTrigger);
     }
 
-    private void OnTrigger(Entity<ExplodeOnTriggerComponent> ent, ref TriggerEvent args)
+    private void OnExplodeTrigger(Entity<ExplodeOnTriggerComponent> ent, ref TriggerEvent args)
     {
         if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key))
             return;
@@ -27,4 +28,27 @@ public sealed class ExplodeOnTriggerSystem : EntitySystem
         _explosion.TriggerExplosive(target.Value, user: args.User);
         args.Handled = true;
     }
+
+    private void OnQueueExplosionTrigger(Entity<ExplosionOnTriggerComponent> ent, ref TriggerEvent args)
+    {
+        var (uid, comp) = ent;
+        if (args.Key != null && !comp.KeysIn.Contains(args.Key))
+            return;
+
+        var target = comp.TargetUser ? args.User : uid;
+
+        if (target == null)
+            return;
+
+        _explosion.QueueExplosion(target.Value,
+                                    comp.ExplosionType,
+                                    comp.TotalIntensity,
+                                    comp.IntensitySlope,
+                                    comp.MaxTileIntensity,
+                                    comp.TileBreakScale,
+                                    comp.MaxTileBreak,
+                                    comp.CanCreateVacuum,
+                                    args.User);
+        args.Handled = true;
+    }
 }
index 83457361fd5469e655cce0ce5aab81f3b92f02b2..87501107447e0cf18cd6eb9a86e8eecf68d7df73 100644 (file)
@@ -1,4 +1,5 @@
-using Content.Shared.Trigger.Components.Effects;
+using Content.Shared.GameTicking;
+using Content.Shared.Trigger.Components.Effects;
 using Content.Shared.Trigger.Components.Triggers;
 using Robust.Shared.Prototypes;
 
@@ -9,6 +10,7 @@ public sealed partial class TriggerSystem
     private void InitializeSpawn()
     {
         SubscribeLocalEvent<TriggerOnSpawnComponent, MapInitEvent>(OnSpawnInit);
+        SubscribeLocalEvent<TriggerOnPlayerSpawnCompleteComponent, PlayerSpawnCompleteEvent>(OnPlayerSpawn);
 
         SubscribeLocalEvent<SpawnOnTriggerComponent, TriggerEvent>(HandleSpawnOnTrigger);
         SubscribeLocalEvent<SpawnEntityTableOnTriggerComponent, TriggerEvent>(HandleSpawnTableOnTrigger);
@@ -20,6 +22,11 @@ public sealed partial class TriggerSystem
         Trigger(ent.Owner, null, ent.Comp.KeyOut);
     }
 
+    private void OnPlayerSpawn(Entity<TriggerOnPlayerSpawnCompleteComponent> ent, ref PlayerSpawnCompleteEvent args)
+    {
+        Trigger(ent.Owner, null, ent.Comp.KeyOut);
+    }
+
     private void HandleSpawnOnTrigger(Entity<SpawnOnTriggerComponent> ent, ref TriggerEvent args)
     {
         if (args.Key != null && !ent.Comp.KeysIn.Contains(args.Key))