]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
TriggerOnUserInteractHand and TriggerOnUserInteractUsing (#41843)
authorScarKy0 <106310278+ScarKy0@users.noreply.github.com>
Sun, 14 Dec 2025 02:11:58 +0000 (03:11 +0100)
committerGitHub <noreply@github.com>
Sun, 14 Dec 2025 02:11:58 +0000 (02:11 +0000)
* init

* handle check

* oops

* cleanup

* fix resolve

---------

Co-authored-by: slarticodefast <161409025+slarticodefast@users.noreply.github.com>
Content.Server/Trigger/Systems/FireStackOnTriggerSystem.cs
Content.Shared/Interaction/InteractHand.cs
Content.Shared/Interaction/InteractUsing.cs
Content.Shared/Interaction/SharedInteractionSystem.cs
Content.Shared/Trigger/Components/Triggers/TriggerOnUserInteractHandComponent.cs [new file with mode: 0644]
Content.Shared/Trigger/Components/Triggers/TriggerOnUserInteractUsingComponent.cs [new file with mode: 0644]
Content.Shared/Trigger/Systems/TriggerSystem.Interaction.cs

index af3298b865e43e4e6ba70e7126567a9e6a122ce2..b1d89bda051c80b81f805a7b4d0e704147689257 100644 (file)
@@ -1,4 +1,5 @@
 using Content.Server.Atmos.EntitySystems;
+using Content.Shared.Atmos.Components;
 using Content.Shared.Trigger;
 using Content.Shared.Trigger.Components.Effects;
 
@@ -31,7 +32,10 @@ public sealed class FireStackOnTriggerSystem : EntitySystem
         if (target == null)
             return;
 
-        _flame.AdjustFireStacks(target.Value, ent.Comp.FireStacks, ignite: ent.Comp.DoIgnite);
+        if (!TryComp<FlammableComponent>(target.Value, out var flammable))
+            return;
+
+        _flame.AdjustFireStacks(target.Value, ent.Comp.FireStacks, ignite: ent.Comp.DoIgnite, flammable: flammable);
 
         args.Handled = true;
     }
@@ -46,7 +50,10 @@ public sealed class FireStackOnTriggerSystem : EntitySystem
         if (target == null)
             return;
 
-        _flame.Extinguish(target.Value);
+        if (!TryComp<FlammableComponent>(target.Value, out var flammable))
+            return;
+
+        _flame.Extinguish(target.Value, flammable: flammable);
 
         args.Handled = true;
     }
index 1d2df4c28b299c2ea4275a55ef5b62ea1136cf85..f6bf7dd231713b95b5e460bbffb184b214c453bf 100644 (file)
@@ -1,54 +1,75 @@
 using JetBrains.Annotations;
-using Robust.Shared.Map;
 
-namespace Content.Shared.Interaction
+namespace Content.Shared.Interaction;
+
+public sealed class InteractHandEventArgs : EventArgs, ITargetedInteractEventArgs
 {
-    public sealed class InteractHandEventArgs : EventArgs, ITargetedInteractEventArgs
+    public InteractHandEventArgs(EntityUid user, EntityUid target)
     {
-        public InteractHandEventArgs(EntityUid user, EntityUid target)
-        {
-            User = user;
-            Target = target;
-        }
-
-        public EntityUid User { get; }
-        public EntityUid Target { get; }
+        User = user;
+        Target = target;
     }
 
+    public EntityUid User { get; }
+    public EntityUid Target { get; }
+}
+
+/// <summary>
+///     Raised directed on a target entity when it is interacted with by a user with an empty hand.
+/// </summary>
+[PublicAPI]
+public sealed class InteractHandEvent : HandledEntityEventArgs, ITargetedInteractEventArgs
+{
+    /// <summary>
+    ///     Entity that triggered the interaction.
+    /// </summary>
+    public EntityUid User { get; }
+
     /// <summary>
-    ///     Raised directed on a target entity when it is interacted with by a user with an empty hand.
+    ///     Entity that was interacted on.
     /// </summary>
-    [PublicAPI]
-    public sealed class InteractHandEvent : HandledEntityEventArgs, ITargetedInteractEventArgs
+    public EntityUid Target { get; }
+
+    public InteractHandEvent(EntityUid user, EntityUid target)
     {
-        /// <summary>
-        ///     Entity that triggered the interaction.
-        /// </summary>
-        public EntityUid User { get; }
-
-        /// <summary>
-        ///     Entity that was interacted on.
-        /// </summary>
-        public EntityUid Target { get; }
-
-        public InteractHandEvent(EntityUid user, EntityUid target)
-        {
-            User = user;
-            Target = target;
-        }
+        User = user;
+        Target = target;
     }
+}
 
+/// <summary>
+/// Raised directed on the user when they interact with an entity with an empty hand.
+/// </summary>
+[PublicAPI]
+public sealed class UserInteractHandEvent : HandledEntityEventArgs, ITargetedInteractEventArgs
+{
     /// <summary>
-    /// Raised on the user before interacting on an entity with bare hand.
-    /// Interaction is cancelled if this event is handled, so set it to true if you do custom interaction logic.
+    ///     Entity that triggered the interaction.
     /// </summary>
-    public sealed class BeforeInteractHandEvent : HandledEntityEventArgs
+    public EntityUid User { get; }
+
+    /// <summary>
+    ///     Entity that was interacted on.
+    /// </summary>
+    public EntityUid Target { get; }
+
+    public UserInteractHandEvent(EntityUid user, EntityUid target)
     {
-        public EntityUid Target { get; }
+        User = user;
+        Target = target;
+    }
+}
+
+/// <summary>
+/// Raised on the user before interacting on an entity with bare hand.
+/// Interaction is cancelled if this event is handled, so set it to true if you do custom interaction logic.
+/// </summary>
+public sealed class BeforeInteractHandEvent : HandledEntityEventArgs
+{
+    public EntityUid Target { get; }
 
-        public BeforeInteractHandEvent(EntityUid target)
-        {
-            Target = target;
-        }
+    public BeforeInteractHandEvent(EntityUid target)
+    {
+        Target = target;
     }
 }
index 3ebf40a517a82017027d8907bcb7367e62fa5b73..7d3d4e978ed84291632f001d19a517f7bd237696 100644 (file)
@@ -2,45 +2,85 @@ using JetBrains.Annotations;
 using Robust.Shared.Map;
 using Robust.Shared.Utility;
 
-namespace Content.Shared.Interaction
+namespace Content.Shared.Interaction;
+
+/// <summary>
+///     Raised when a target entity is interacted with by a user while holding an object in their hand.
+/// </summary>
+[PublicAPI]
+public sealed class InteractUsingEvent : HandledEntityEventArgs
 {
     /// <summary>
-    ///     Raised when a target entity is interacted with by a user while holding an object in their hand.
+    ///     Entity that triggered the interaction.
+    /// </summary>
+    public EntityUid User { get; }
+
+    /// <summary>
+    ///     Entity that the user used to interact.
+    /// </summary>
+    public EntityUid Used { get; }
+
+    /// <summary>
+    ///     Entity that was interacted on.
+    /// </summary>
+    public EntityUid Target { get; }
+
+    /// <summary>
+    ///     The original location that was clicked by the user.
     /// </summary>
-    [PublicAPI]
-    public sealed class InteractUsingEvent : HandledEntityEventArgs
+    public EntityCoordinates ClickLocation { get; }
+
+    public InteractUsingEvent(EntityUid user, EntityUid used, EntityUid target, EntityCoordinates clickLocation)
     {
-        /// <summary>
-        ///     Entity that triggered the interaction.
-        /// </summary>
-        public EntityUid User { get; }
-
-        /// <summary>
-        ///     Entity that the user used to interact.
-        /// </summary>
-        public EntityUid Used { get; }
-
-        /// <summary>
-        ///     Entity that was interacted on.
-        /// </summary>
-        public EntityUid Target { get; }
-
-        /// <summary>
-        ///     The original location that was clicked by the user.
-        /// </summary>
-        public EntityCoordinates ClickLocation { get; }
-
-        public InteractUsingEvent(EntityUid user, EntityUid used, EntityUid target, EntityCoordinates clickLocation)
-        {
-            // Interact using should not have the same used and target.
-            // That should be a use-in-hand event instead.
-            // If this is not the case, can lead to bugs (e.g., attempting to merge a item stack into itself).
-            DebugTools.Assert(used != target);
-
-            User = user;
-            Used = used;
-            Target = target;
-            ClickLocation = clickLocation;
-        }
+        // Interact using should not have the same used and target.
+        // That should be a use-in-hand event instead.
+        // If this is not the case, can lead to bugs (e.g., attempting to merge a item stack into itself).
+        DebugTools.Assert(used != target);
+
+        User = user;
+        Used = used;
+        Target = target;
+        ClickLocation = clickLocation;
     }
 }
+
+/// <summary>
+/// Raised when a user entity interacts with a target while holding an object in their hand.
+/// </summary>
+[PublicAPI]
+public sealed class UserInteractUsingEvent : HandledEntityEventArgs
+{
+    /// <summary>
+    ///     Entity that triggered the interaction.
+    /// </summary>
+    public EntityUid User { get; }
+
+    /// <summary>
+    ///     Entity that the user used to interact.
+    /// </summary>
+    public EntityUid Used { get; }
+
+    /// <summary>
+    ///     Entity that was interacted on.
+    /// </summary>
+    public EntityUid Target { get; }
+
+    /// <summary>
+    ///     The original location that was clicked by the user.
+    /// </summary>
+    public EntityCoordinates ClickLocation { get; }
+
+    public UserInteractUsingEvent(EntityUid user, EntityUid used, EntityUid target, EntityCoordinates clickLocation)
+    {
+        // Interact using should not have the same used and target.
+        // That should be a use-in-hand event instead.
+        // If this is not the case, can lead to bugs (e.g., attempting to merge a item stack into itself).
+        DebugTools.Assert(used != target);
+
+        User = user;
+        Used = used;
+        Target = target;
+        ClickLocation = clickLocation;
+    }
+}
+
index d34e86ad5cc29b4de4376b3f8faa00c6fbd4f4aa..88d42b8a2869949219ebbd3ac514c71420516657 100644 (file)
@@ -523,12 +523,16 @@ namespace Content.Shared.Interaction
             }
 
             DebugTools.Assert(!IsDeleted(user) && !IsDeleted(target));
+
             // all interactions should only happen when in range / unobstructed, so no range check is needed
             var message = new InteractHandEvent(user, target);
             RaiseLocalEvent(target, message, true);
+            var userMessage = new UserInteractHandEvent(user, target);
+            RaiseLocalEvent(user, userMessage, true);
+
             _adminLogger.Add(LogType.InteractHand, LogImpact.Low, $"{user} interacted with {target}");
             DoContactInteraction(user, target, message);
-            if (message.Handled)
+            if (message.Handled || userMessage.Handled)
                 return;
 
             DebugTools.Assert(!IsDeleted(user) && !IsDeleted(target));
@@ -1061,10 +1065,14 @@ namespace Content.Shared.Interaction
             // all interactions should only happen when in range / unobstructed, so no range check is needed
             var interactUsingEvent = new InteractUsingEvent(user, used, target, clickLocation);
             RaiseLocalEvent(target, interactUsingEvent, true);
+
+            var userInteractUsingEvent = new UserInteractUsingEvent(user, used, target, clickLocation);
+            RaiseLocalEvent(user, userInteractUsingEvent, true);
+
             DoContactInteraction(user, used, interactUsingEvent);
             DoContactInteraction(user, target, interactUsingEvent);
             // Contact interactions are currently only used for forensics, so we don't raise used -> target
-            if (interactUsingEvent.Handled)
+            if (interactUsingEvent.Handled || userInteractUsingEvent.Handled)
                 return true;
 
             if (InteractDoAfter(user, used, target, clickLocation, canReach: true, checkDeletion: false))
diff --git a/Content.Shared/Trigger/Components/Triggers/TriggerOnUserInteractHandComponent.cs b/Content.Shared/Trigger/Components/Triggers/TriggerOnUserInteractHandComponent.cs
new file mode 100644 (file)
index 0000000..58deef9
--- /dev/null
@@ -0,0 +1,18 @@
+using Content.Shared.Interaction;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Trigger.Components.Triggers;
+
+/// <summary>
+/// Trigger on <see cref="UserInteractHandEvent"/>, aka when owner clicks on an entity with an empty hand.
+/// The trigger user is the entity that got interacted with.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class TriggerOnUserInteractHandComponent : BaseTriggerOnXComponent
+{
+    /// <summary>
+    /// Whether the interaction should be marked as handled after it happens.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool Handle = true;
+}
diff --git a/Content.Shared/Trigger/Components/Triggers/TriggerOnUserInteractUsingComponent.cs b/Content.Shared/Trigger/Components/Triggers/TriggerOnUserInteractUsingComponent.cs
new file mode 100644 (file)
index 0000000..0550b3b
--- /dev/null
@@ -0,0 +1,40 @@
+using Content.Shared.Interaction;
+using Content.Shared.Whitelist;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Trigger.Components.Triggers;
+
+/// <summary>
+/// Triggers when the owner uses another entity to interact with another entity (<see cref="UserInteractUsingEvent"/>).
+/// The trigger user is the interacted entity or the item used, depending on the TargetUsed datafield.
+/// </summary>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
+public sealed partial class TriggerOnUserInteractUsingComponent : BaseTriggerOnXComponent
+{
+    /// <summary>
+    /// Whitelist of entities that can be used to trigger this component.
+    /// </summary>
+    /// <remarks>No whitelist check when null.</remarks>
+    [DataField, AutoNetworkedField]
+    public EntityWhitelist? Whitelist;
+
+    /// <summary>
+    /// Blacklist of entities that cannot be used to trigger this component.
+    /// </summary>
+    /// <remarks>No blacklist check when null.</remarks>
+    [DataField, AutoNetworkedField]
+    public EntityWhitelist? Blacklist;
+
+    /// <summary>
+    /// If false, the trigger user will be the entity that got interacted with.
+    /// If true, the trigger user will the entity that was used to interact.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool TargetUsed = false;
+
+    /// <summary>
+    /// Whether the interaction should be marked as handled after it happens.
+    /// </summary>
+    [DataField, AutoNetworkedField]
+    public bool Handle = true;
+}
index 944239ccf8e1d28b8fb35bdd73a0fdb9b62d04ad..922762973ceba72c801fdc36cfe48fb29fc5dafd 100644 (file)
@@ -16,7 +16,9 @@ public sealed partial class TriggerSystem
         SubscribeLocalEvent<TriggerOnActivateComponent, ActivateInWorldEvent>(OnActivate);
         SubscribeLocalEvent<TriggerOnUseComponent, UseInHandEvent>(OnUse);
         SubscribeLocalEvent<TriggerOnInteractHandComponent, InteractHandEvent>(OnInteractHand);
+        SubscribeLocalEvent<TriggerOnUserInteractHandComponent, UserInteractHandEvent>(OnUserInteractHand);
         SubscribeLocalEvent<TriggerOnInteractUsingComponent, InteractUsingEvent>(OnInteractUsing);
+        SubscribeLocalEvent<TriggerOnUserInteractUsingComponent, UserInteractUsingEvent>(OnUserInteractUsing);
 
         SubscribeLocalEvent<TriggerOnThrowComponent, ThrowEvent>(OnThrow);
         SubscribeLocalEvent<TriggerOnThrownComponent, ThrownEvent>(OnThrown);
@@ -64,6 +66,17 @@ public sealed partial class TriggerSystem
         args.Handled = true;
     }
 
+    private void OnUserInteractHand(Entity<TriggerOnUserInteractHandComponent> ent, ref UserInteractHandEvent args)
+    {
+        if (args.Handled)
+            return;
+
+        Trigger(ent.Owner, args.Target, ent.Comp.KeyOut);
+
+        if (ent.Comp.Handle)
+            args.Handled = true;
+    }
+
     private void OnInteractUsing(Entity<TriggerOnInteractUsingComponent> ent, ref InteractUsingEvent args)
     {
         if (args.Handled)
@@ -76,6 +89,20 @@ public sealed partial class TriggerSystem
         args.Handled = true;
     }
 
+    private void OnUserInteractUsing(Entity<TriggerOnUserInteractUsingComponent> ent, ref UserInteractUsingEvent args)
+    {
+        if (args.Handled)
+            return;
+
+        if (!_whitelist.CheckBoth(args.Used, ent.Comp.Blacklist, ent.Comp.Whitelist))
+            return;
+
+        Trigger(ent.Owner, ent.Comp.TargetUsed ? args.Used : args.Target, ent.Comp.KeyOut);
+
+        if (ent.Comp.Handle)
+            args.Handled = true;
+    }
+
     private void OnThrow(Entity<TriggerOnThrowComponent> ent, ref ThrowEvent args)
     {
         Trigger(ent.Owner, args.Thrown, ent.Comp.KeyOut);