diff --git a/Content.IntegrationTests/Tests/Hands/HandTests.cs b/Content.IntegrationTests/Tests/Hands/HandTests.cs index 9ecabbeebf..5e96015feb 100644 --- a/Content.IntegrationTests/Tests/Hands/HandTests.cs +++ b/Content.IntegrationTests/Tests/Hands/HandTests.cs @@ -1,8 +1,10 @@ using System.Linq; +using Content.Server.Storage.EntitySystems; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Robust.Server.GameObjects; using Robust.Server.Player; +using Robust.Shared.Containers; using Robust.Shared.GameObjects; using Robust.Shared.Map; @@ -11,6 +13,19 @@ namespace Content.IntegrationTests.Tests.Hands; [TestFixture] public sealed class HandTests { + [TestPrototypes] + private const string Prototypes = @" +- type: entity + id: TestPickUpThenDropInContainerTestBox + name: box + components: + - type: EntityStorage + - type: ContainerContainer + containers: + entity_storage: !type:Container +"; + + [Test] public async Task TestPickupDrop() { @@ -57,4 +72,69 @@ public sealed class HandTests await server.WaitPost(() => mapMan.DeleteMap(data.MapId)); await pair.CleanReturnAsync(); } + + [Test] + public async Task TestPickUpThenDropInContainer() + { + await using var pair = await PoolManager.GetServerClient(new PoolSettings + { + Connected = true, + DummyTicker = false + }); + var server = pair.Server; + var map = await pair.CreateTestMap(); + await pair.RunTicksSync(5); + + var entMan = server.ResolveDependency(); + var playerMan = server.ResolveDependency(); + var mapMan = server.ResolveDependency(); + var sys = entMan.System(); + var tSys = entMan.System(); + var containerSystem = server.System(); + + EntityUid item = default; + EntityUid box = default; + EntityUid player = default; + HandsComponent hands = default!; + + // spawn the elusive box and crowbar at the coordinates + await server.WaitPost(() => box = server.EntMan.SpawnEntity("TestPickUpThenDropInContainerTestBox", map.GridCoords)); + await server.WaitPost(() => item = server.EntMan.SpawnEntity("Crowbar", map.GridCoords)); + // place the player at the exact same coordinates and have them grab the crowbar + await server.WaitPost(() => + { + player = playerMan.Sessions.First().AttachedEntity!.Value; + tSys.PlaceNextTo(player, item); + hands = entMan.GetComponent(player); + sys.TryPickup(player, item, hands.ActiveHand!); + }); + await pair.RunTicksSync(5); + Assert.That(hands.ActiveHandEntity, Is.EqualTo(item)); + + // Open then close the box to place the player, who is holding the crowbar, inside of it + var storage = server.System(); + await server.WaitPost(() => + { + storage.OpenStorage(box); + storage.CloseStorage(box); + }); + await pair.RunTicksSync(5); + Assert.That(containerSystem.IsEntityInContainer(player), Is.True); + + // Dropping the item while the player is inside the box should cause the item + // to also be inside the same container the player is in now, + // with the item not being in the player's hands + await server.WaitPost(() => + { + sys.TryDrop(player, item, null!); + }); + await pair.RunTicksSync(5); + var xform = entMan.GetComponent(player); + var itemXform = entMan.GetComponent(item); + Assert.That(hands.ActiveHandEntity, Is.Not.EqualTo(item)); + Assert.That(containerSystem.IsInSameOrNoContainer((player, xform), (item, itemXform))); + + await server.WaitPost(() => mapMan.DeleteMap(map.MapId)); + await pair.CleanReturnAsync(); + } } diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs index 4d21e40a98..2e3c6f6203 100644 --- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs +++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.Drop.cs @@ -110,7 +110,10 @@ public abstract partial class SharedHandsSystem return false; var entity = hand.HeldEntity!.Value; - DoDrop(uid, hand, doDropInteraction: doDropInteraction, handsComp); + + // if item is a fake item (like with pulling), just delete it rather than bothering with trying to drop it into the world + if (TryComp(entity, out VirtualItemComponent? @virtual)) + _virtualSystem.DeleteVirtualItem((entity, @virtual), uid); if (TerminatingOrDeleted(entity)) return true; @@ -122,16 +125,18 @@ public abstract partial class SharedHandsSystem var userXform = Transform(uid); var isInContainer = ContainerSystem.IsEntityOrParentInContainer(uid, xform: userXform); + // drop the item inside the container if the user is in a container if (targetDropLocation == null || isInContainer) { - // If user is in a container, drop item into that container. Otherwise, attach to grid or map. TransformSystem.DropNextTo((entity, itemXform), (uid, userXform)); return true; } + // otherwise, remove the item from their hands and place it at the calculated interaction range position + DoDrop(uid, hand, doDropInteraction: doDropInteraction, handsComp); var (itemPos, itemRot) = TransformSystem.GetWorldPositionRotation(entity); var origin = new MapCoordinates(itemPos, itemXform.MapID); - var target = targetDropLocation.Value.ToMap(EntityManager, TransformSystem); + var target = TransformSystem.ToMapCoordinates(targetDropLocation.Value); TransformSystem.SetWorldPositionRotation(entity, GetFinalDropCoordinates(uid, origin, target), itemRot); return true; }