diff --git a/Content.Shared/Interaction/SharedInteractionSystem.cs b/Content.Shared/Interaction/SharedInteractionSystem.cs index 972fa389e4..e8cc831e49 100644 --- a/Content.Shared/Interaction/SharedInteractionSystem.cs +++ b/Content.Shared/Interaction/SharedInteractionSystem.cs @@ -918,11 +918,21 @@ namespace Content.Shared.Interaction } /// - /// Uses a item/object on an entity + /// Uses an item/object on an entity /// Finds components with the InteractUsing interface and calls their function /// NOTE: Does not have an InRangeUnobstructed check /// - public void InteractUsing( + /// User doing the interaction. + /// Item being used on the . + /// Entity getting interacted with by the using the + /// entity. + /// The location that the clicked. + /// Whether to check that the can interact with the + /// . + /// Whether to check that the can use the + /// entity. + /// True if the interaction was handled. Otherwise, false. + public bool InteractUsing( EntityUid user, EntityUid used, EntityUid target, @@ -931,10 +941,10 @@ namespace Content.Shared.Interaction bool checkCanUse = true) { if (checkCanInteract && !_actionBlockerSystem.CanInteract(user, target)) - return; + return false; if (checkCanUse && !_actionBlockerSystem.CanUseHeldEntity(user, used)) - return; + return false; _adminLogger.Add( LogType.InteractUsing, @@ -942,7 +952,7 @@ namespace Content.Shared.Interaction $"{ToPrettyString(user):user} interacted with {ToPrettyString(target):target} using {ToPrettyString(used):used}"); if (RangedInteractDoBefore(user, used, target, clickLocation, true)) - return; + return true; // all interactions should only happen when in range / unobstructed, so no range check is needed var interactUsingEvent = new InteractUsingEvent(user, used, target, clickLocation); @@ -951,15 +961,24 @@ namespace Content.Shared.Interaction DoContactInteraction(user, target, interactUsingEvent); // Contact interactions are currently only used for forensics, so we don't raise used -> target if (interactUsingEvent.Handled) - return; + return true; - InteractDoAfter(user, used, target, clickLocation, canReach: true); + if (InteractDoAfter(user, used, target, clickLocation, canReach: true)) + return true; + return false; } /// /// Used when clicking on an entity resulted in no other interaction. Used for low-priority interactions. /// - public void InteractDoAfter(EntityUid user, EntityUid used, EntityUid? target, EntityCoordinates clickLocation, bool canReach) + /// + /// + /// + /// + /// Whether the is in range of the . + /// + /// True if the interaction was handled. Otherwise, false. + public bool InteractDoAfter(EntityUid user, EntityUid used, EntityUid? target, EntityCoordinates clickLocation, bool canReach) { if (target is { Valid: false }) target = null; @@ -974,10 +993,10 @@ namespace Content.Shared.Interaction } if (afterInteractEvent.Handled) - return; + return true; if (target == null) - return; + return false; var afterInteractUsingEvent = new AfterInteractUsingEvent(user, used, target, clickLocation, canReach); RaiseLocalEvent(target.Value, afterInteractUsingEvent); @@ -988,6 +1007,10 @@ namespace Content.Shared.Interaction DoContactInteraction(user, target, afterInteractUsingEvent); // Contact interactions are currently only used for forensics, so we don't raise used -> target } + + if (afterInteractUsingEvent.Handled) + return true; + return false; } #region ActivateItemInWorld diff --git a/Content.Shared/Storage/Components/StoreAfterFailedInteractComponent.cs b/Content.Shared/Storage/Components/StoreAfterFailedInteractComponent.cs new file mode 100644 index 0000000000..578da52dfc --- /dev/null +++ b/Content.Shared/Storage/Components/StoreAfterFailedInteractComponent.cs @@ -0,0 +1,8 @@ +using Robust.Shared.GameStates; + +namespace Content.Shared.Storage.Components; + +[RegisterComponent, NetworkedComponent] +public sealed partial class StoreAfterFailedInteractComponent : Component +{ +} diff --git a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs index 8ee08f7e48..2689d7750d 100644 --- a/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs +++ b/Content.Shared/Storage/EntitySystems/SharedStorageSystem.cs @@ -22,6 +22,7 @@ using Content.Shared.Popups; using Content.Shared.Stacks; using Content.Shared.Storage.Components; using Content.Shared.Timing; +using Content.Shared.Storage.Events; using Content.Shared.Verbs; using Content.Shared.Whitelist; using Robust.Shared.Audio; @@ -366,7 +367,7 @@ public abstract class SharedStorageSystem : EntitySystem if (HasComp(uid)) return; - PlayerInsertHeldEntity(uid, args.User, storageComp); + PlayerInsertHeldEntity((uid, storageComp), args.User); // Always handle it, even if insertion fails. // We don't want to trigger any AfterInteract logic here. // Example issue would be placing wires if item doesn't fit in backpack. @@ -606,7 +607,15 @@ public abstract class SharedStorageSystem : EntitySystem $"{ToPrettyString(player):player} is interacting with {ToPrettyString(item):item} while it is stored in {ToPrettyString(storage):storage} using {ToPrettyString(player.Comp.ActiveHandEntity):used}"); // Else, interact using the held item - _interactionSystem.InteractUsing(player, player.Comp.ActiveHandEntity.Value, item, Transform(item).Coordinates, checkCanInteract: false); + if (_interactionSystem.InteractUsing(player, + player.Comp.ActiveHandEntity.Value, + item, + Transform(item).Coordinates, + checkCanInteract: false)) + return; + + var failedEv = new StorageInsertFailedEvent((storage, storage.Comp), (player, player.Comp)); + RaiseLocalEvent(storage, ref failedEv); } private void OnSetItemLocation(StorageSetItemLocationEvent msg, EntitySessionEventArgs args) @@ -988,35 +997,36 @@ public abstract class SharedStorageSystem : EntitySystem /// /// Inserts an entity into storage from the player's active hand /// - /// - /// The player to insert an entity from - /// - /// true if inserted, false otherwise - public bool PlayerInsertHeldEntity(EntityUid uid, EntityUid player, StorageComponent? storageComp = null) + /// The storage entity and component to insert into. + /// The player and hands component to insert the held entity from. + /// True if inserted, otherwise false. + public bool PlayerInsertHeldEntity(Entity ent, Entity player) { - if (!Resolve(uid, ref storageComp) || !TryComp(player, out HandsComponent? hands) || hands.ActiveHandEntity == null) + if (!Resolve(ent.Owner, ref ent.Comp) + || !Resolve(player.Owner, ref player.Comp) + || player.Comp.ActiveHandEntity == null) return false; - var toInsert = hands.ActiveHandEntity; + var toInsert = player.Comp.ActiveHandEntity; - if (!CanInsert(uid, toInsert.Value, out var reason, storageComp)) + if (!CanInsert(ent, toInsert.Value, out var reason, ent.Comp)) { - _popupSystem.PopupClient(Loc.GetString(reason ?? "comp-storage-cant-insert"), uid, player); + _popupSystem.PopupClient(Loc.GetString(reason ?? "comp-storage-cant-insert"), ent, player); return false; } - if (!_sharedHandsSystem.CanDrop(player, toInsert.Value, hands)) + if (!_sharedHandsSystem.CanDrop(player, toInsert.Value, player.Comp)) { - _popupSystem.PopupClient(Loc.GetString("comp-storage-cant-drop", ("entity", toInsert.Value)), uid, player); + _popupSystem.PopupClient(Loc.GetString("comp-storage-cant-drop", ("entity", toInsert.Value)), ent, player); return false; } - return PlayerInsertEntityInWorld((uid, storageComp), player, toInsert.Value); + return PlayerInsertEntityInWorld((ent, ent.Comp), player, toInsert.Value); } /// /// Inserts an Entity () in the world into storage, informing if it fails. - /// is *NOT* held, see . + /// is *NOT* held, see . /// /// /// The player to insert an entity with diff --git a/Content.Shared/Storage/EntitySystems/StoreAfterFailedInteractSystem.cs b/Content.Shared/Storage/EntitySystems/StoreAfterFailedInteractSystem.cs new file mode 100644 index 0000000000..77f72806f4 --- /dev/null +++ b/Content.Shared/Storage/EntitySystems/StoreAfterFailedInteractSystem.cs @@ -0,0 +1,21 @@ +using Content.Shared.Storage.Components; +using Content.Shared.Storage.Events; + +namespace Content.Shared.Storage.EntitySystems; + +public sealed class StoreAfterFailedInteractSystem : EntitySystem +{ + [Dependency] private readonly SharedStorageSystem _storage = default!; + + public override void Initialize() + { + base.Initialize(); + + SubscribeLocalEvent(OnStorageInsertFailed); + } + + private void OnStorageInsertFailed(Entity ent, ref StorageInsertFailedEvent args) + { + _storage.PlayerInsertHeldEntity(args.Storage, args.Player); + } +} diff --git a/Content.Shared/Storage/Events/StorageInsertFailedEvent.cs b/Content.Shared/Storage/Events/StorageInsertFailedEvent.cs new file mode 100644 index 0000000000..50f759e43f --- /dev/null +++ b/Content.Shared/Storage/Events/StorageInsertFailedEvent.cs @@ -0,0 +1,6 @@ +using Content.Shared.Hands.Components; + +namespace Content.Shared.Storage.Events; + +[ByRefEvent] +public record struct StorageInsertFailedEvent(Entity Storage, Entity Player);