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);