]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
5 new triggers: EmptyContainers, Knockdown, Stun, TriggerOnThrowDoHit (#41472)
authorāda <ss.adasts@gmail.com>
Wed, 19 Nov 2025 02:05:13 +0000 (20:05 -0600)
committerGitHub <noreply@github.com>
Wed, 19 Nov 2025 02:05:13 +0000 (02:05 +0000)
* ideas

* finish components

* systems

* one more

* requested

---------

Co-authored-by: iaada <iaada@users.noreply.github.com>
Content.Shared/Trigger/Components/Effects/CleanContainersOnTriggerComponent.cs [new file with mode: 0644]
Content.Shared/Trigger/Components/Effects/EmptyContainersOnTriggerComponent.cs [new file with mode: 0644]
Content.Shared/Trigger/Components/Effects/KnockdownOnTriggerComponent.cs [new file with mode: 0644]
Content.Shared/Trigger/Components/Effects/StunOnTriggerComponent.cs [new file with mode: 0644]
Content.Shared/Trigger/Components/Triggers/TriggerOnThrowDoHitComponent.cs [new file with mode: 0644]
Content.Shared/Trigger/Systems/EmptyContainersOnTriggerSystem.cs [new file with mode: 0644]
Content.Shared/Trigger/Systems/KnockdownOnTriggerSystem.cs [new file with mode: 0644]
Content.Shared/Trigger/Systems/StunOnTriggerSystem.cs [new file with mode: 0644]
Content.Shared/Trigger/Systems/TriggerOnThrowDoHitSystem.cs [new file with mode: 0644]

diff --git a/Content.Shared/Trigger/Components/Effects/CleanContainersOnTriggerComponent.cs b/Content.Shared/Trigger/Components/Effects/CleanContainersOnTriggerComponent.cs
new file mode 100644 (file)
index 0000000..bf4e184
--- /dev/null
@@ -0,0 +1,22 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Trigger.Components.Effects;
+
+/// <summary>
+/// Trigger effect for removing and *deleting* all items in container(s) on the target.
+/// </summary>
+/// <remarks>
+/// Be very careful when setting <see cref="BaseXOnTriggerComponent.TargetUser"/> to true or all your organs might fall out.
+/// In fact, never set it to true.
+/// </remarks>
+/// <seealso cref="EmptyContainersOnTriggerComponent"/>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class CleanContainersOnTriggerComponent : BaseXOnTriggerComponent
+{
+    /// <summary>
+    /// Names of containers to empty.
+    /// If null, all containers will be emptied.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public List<string>? Container;
+}
diff --git a/Content.Shared/Trigger/Components/Effects/EmptyContainersOnTriggerComponent.cs b/Content.Shared/Trigger/Components/Effects/EmptyContainersOnTriggerComponent.cs
new file mode 100644 (file)
index 0000000..de55e1f
--- /dev/null
@@ -0,0 +1,22 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Trigger.Components.Effects;
+
+/// <summary>
+/// Trigger effect for removing all items in container(s) on the target.
+/// </summary>
+/// <remarks>
+/// Be very careful when setting <see cref="BaseXOnTriggerComponent.TargetUser"/> to true or all your organs might fall out.
+/// In fact, never set it to true.
+/// </remarks>
+/// <seealso cref="CleanContainersOnTriggerComponent"/>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class EmptyContainersOnTriggerComponent : BaseXOnTriggerComponent
+{
+    /// <summary>
+    /// Names of containers to empty.
+    /// If null, all containers will be emptied.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public List<string>? Container;
+}
diff --git a/Content.Shared/Trigger/Components/Effects/KnockdownOnTriggerComponent.cs b/Content.Shared/Trigger/Components/Effects/KnockdownOnTriggerComponent.cs
new file mode 100644 (file)
index 0000000..3380913
--- /dev/null
@@ -0,0 +1,36 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Trigger.Components.Effects;
+
+/// <summary>
+/// Trigger effect for sending the target sidewise (crawling).
+/// Knockdowns the user if <see cref="BaseXOnTriggerComponent.TargetUser"/> is true.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class KnockdownOnTriggerComponent : BaseXOnTriggerComponent
+{
+    /// <summary>
+    /// How long the target is forced to be on the ground.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public TimeSpan KnockdownAmount =  TimeSpan.FromSeconds(1);
+
+    /// <summary>
+    /// If true, refresh the duration.
+    /// If false, time is added on-top of any existing forced knockdown.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool Refresh = true;
+
+    /// <summary>
+    /// Should the entity try and stand automatically?
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool AutoStand = true;
+
+    /// <summary>
+    /// Should the entity drop their items upon first being knocked down?
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool Drop = true;
+}
diff --git a/Content.Shared/Trigger/Components/Effects/StunOnTriggerComponent.cs b/Content.Shared/Trigger/Components/Effects/StunOnTriggerComponent.cs
new file mode 100644 (file)
index 0000000..ebe55dc
--- /dev/null
@@ -0,0 +1,24 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Trigger.Components.Effects;
+
+/// <summary>
+/// Trigger effect for stunning an entity.
+/// Stuns the user if <see cref="BaseXOnTriggerComponent.TargetUser"/> is true.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class StunOnTriggerComponent : BaseXOnTriggerComponent
+{
+    /// <summary>
+    /// How long to stun the target.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public TimeSpan StunAmount = TimeSpan.FromSeconds(1);
+
+    /// <summary>
+    /// If true, refresh the stun duration.
+    /// If false, stun is added on-top of any existing stun.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool Refresh = true;
+}
diff --git a/Content.Shared/Trigger/Components/Triggers/TriggerOnThrowDoHitComponent.cs b/Content.Shared/Trigger/Components/Triggers/TriggerOnThrowDoHitComponent.cs
new file mode 100644 (file)
index 0000000..4e83299
--- /dev/null
@@ -0,0 +1,10 @@
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Trigger.Components.Triggers;
+
+/// <summary>
+/// Trigger for when this entity is thrown and then hits a second entity.
+/// User is the entity hit.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class TriggerOnThrowDoHitComponent : BaseTriggerOnXComponent;
diff --git a/Content.Shared/Trigger/Systems/EmptyContainersOnTriggerSystem.cs b/Content.Shared/Trigger/Systems/EmptyContainersOnTriggerSystem.cs
new file mode 100644 (file)
index 0000000..dbe1601
--- /dev/null
@@ -0,0 +1,81 @@
+using Content.Shared.Trigger.Components.Effects;
+using Robust.Shared.Containers;
+
+namespace Content.Shared.Trigger.Systems;
+
+/// <summary>
+/// Empty containers trigger system.
+/// </summary>
+public sealed class EmptyContainersOnTriggerSystem : XOnTriggerSystem<EmptyContainersOnTriggerComponent>
+{
+    [Dependency] private readonly SharedContainerSystem _container = default!;
+
+    protected override void OnTrigger(Entity<EmptyContainersOnTriggerComponent> ent, EntityUid target, ref TriggerEvent args)
+    {
+        if (!TryComp<ContainerManagerComponent>(target, out var containerComp))
+            return;
+
+        // Empty everything. Make sure a player isn't the target because they will get removed from their body along with their organs
+        if (ent.Comp.Container is null)
+        {
+            foreach (var container in _container.GetAllContainers(target, containerComp))
+            {
+                _container.EmptyContainer(container);
+            }
+
+            args.Handled = true;
+        }
+
+        // Empty containers in a sane way
+        else
+        {
+            foreach (var containerId in ent.Comp.Container)
+            {
+                if (!_container.TryGetContainer(target, containerId, out var container, containerComp))
+                    continue;
+
+                _container.EmptyContainer(container);
+                args.Handled = true;
+            }
+        }
+    }
+}
+
+/// <summary>
+/// Empty containers and delete items trigger system.
+/// </summary>
+public sealed class CleanContainersOnTriggerSystem : XOnTriggerSystem<CleanContainersOnTriggerComponent>
+{
+    [Dependency] private readonly SharedContainerSystem _container = default!;
+
+    protected override void OnTrigger(Entity<CleanContainersOnTriggerComponent> ent, EntityUid target, ref TriggerEvent args)
+    {
+        if (!TryComp<ContainerManagerComponent>(target, out var containerComp))
+            return;
+
+        // Empty everything. Make sure a player isn't the target because they will get DELETED
+        if (ent.Comp.Container is null)
+        {
+            foreach (var container in _container.GetAllContainers(target, containerComp))
+            {
+                _container.CleanContainer(container);
+            }
+
+            args.Handled = true;
+        }
+
+        // Empty containers in a sane way
+        else
+        {
+            foreach (var containerId in ent.Comp.Container)
+            {
+                if (!_container.TryGetContainer(target, containerId, out var container, containerComp))
+                    continue;
+
+                _container.CleanContainer(container);
+                args.Handled = true;
+            }
+        }
+    }
+}
+
diff --git a/Content.Shared/Trigger/Systems/KnockdownOnTriggerSystem.cs b/Content.Shared/Trigger/Systems/KnockdownOnTriggerSystem.cs
new file mode 100644 (file)
index 0000000..815e00a
--- /dev/null
@@ -0,0 +1,21 @@
+using Content.Shared.Stunnable;
+using Content.Shared.Trigger.Components.Effects;
+
+namespace Content.Shared.Trigger.Systems;
+
+public sealed class KnockdownOnTriggerSystem : XOnTriggerSystem<KnockdownOnTriggerComponent>
+{
+    [Dependency] private readonly SharedStunSystem _stun = default!;
+
+    protected override void OnTrigger(Entity<KnockdownOnTriggerComponent> ent, EntityUid target, ref TriggerEvent args)
+    {
+        args.Handled |= _stun.TryKnockdown(
+                        target,
+                        ent.Comp.KnockdownAmount,
+                        ent.Comp.Refresh,
+                        ent.Comp.AutoStand,
+                        ent.Comp.Drop,
+                        true
+                        );
+    }
+}
diff --git a/Content.Shared/Trigger/Systems/StunOnTriggerSystem.cs b/Content.Shared/Trigger/Systems/StunOnTriggerSystem.cs
new file mode 100644 (file)
index 0000000..ce86f97
--- /dev/null
@@ -0,0 +1,17 @@
+using Content.Shared.Stunnable;
+using Content.Shared.Trigger.Components.Effects;
+
+namespace Content.Shared.Trigger.Systems;
+
+public sealed class StunOnTriggerSystem : XOnTriggerSystem<StunOnTriggerComponent>
+{
+    [Dependency] private readonly SharedStunSystem _stun = default!;
+
+    protected override void OnTrigger(Entity<StunOnTriggerComponent> ent, EntityUid target, ref TriggerEvent args)
+    {
+        if (ent.Comp.Refresh)
+            args.Handled |= _stun.TryUpdateStunDuration(target, ent.Comp.StunAmount);
+        else
+            args.Handled |= _stun.TryAddStunDuration(target, ent.Comp.StunAmount);
+    }
+}
diff --git a/Content.Shared/Trigger/Systems/TriggerOnThrowDoHitSystem.cs b/Content.Shared/Trigger/Systems/TriggerOnThrowDoHitSystem.cs
new file mode 100644 (file)
index 0000000..ca57b51
--- /dev/null
@@ -0,0 +1,19 @@
+using Content.Shared.Throwing;
+using Content.Shared.Trigger.Components.Triggers;
+
+namespace Content.Shared.Trigger.Systems;
+
+public sealed partial class TriggerOnThrowDoHitSystem : TriggerOnXSystem
+{
+    public override void Initialize()
+    {
+        base.Initialize();
+
+        SubscribeLocalEvent<TriggerOnThrowDoHitComponent, ThrowDoHitEvent>(OnHit);
+    }
+
+    private void OnHit(Entity<TriggerOnThrowDoHitComponent> ent, ref ThrowDoHitEvent args)
+    {
+        Trigger.Trigger(ent.Owner, args.Target, ent.Comp.KeyOut);
+    }
+}