From 3d41d5f2bcb501b1746e58ad0ca6a6cbc83e69ac Mon Sep 17 00:00:00 2001
From: DrSmugleaf <10968691+DrSmugleaf@users.noreply.github.com>
Date: Sat, 18 May 2024 18:35:46 -0700
Subject: [PATCH] Make wielding automatically drop the item on your other hand
(#27975)
* Make wielding automatically drop the item on your other hand
* Fix docs
* Remove redundant parameter
* Fix not deleting virtuals on fail
* Make count freeable hands method
* Add popup when dropping item
---
.../Hands/Components/HandHelpers.cs | 10 ++++
.../Hands/EntitySystems/SharedHandsSystem.cs | 13 ++++-
.../VirtualItem/SharedVirtualItemSystem.cs | 48 ++++++++++++++++---
Content.Shared/Wieldable/WieldableSystem.cs | 16 ++++++-
.../Locale/en-US/virtual/virtual-item.ftl | 1 +
5 files changed, 79 insertions(+), 9 deletions(-)
create mode 100644 Resources/Locale/en-US/virtual/virtual-item.ftl
diff --git a/Content.Shared/Hands/Components/HandHelpers.cs b/Content.Shared/Hands/Components/HandHelpers.cs
index 11fff6d9c8a..aecf3a69369 100644
--- a/Content.Shared/Hands/Components/HandHelpers.cs
+++ b/Content.Shared/Hands/Components/HandHelpers.cs
@@ -1,4 +1,5 @@
using System.Linq;
+using Content.Shared.Hands.EntitySystems;
namespace Content.Shared.Hands.Components;
@@ -20,6 +21,15 @@ public static class HandHelpers
///
public static int CountFreeHands(this HandsComponent component) => component.Hands.Values.Count(hand => hand.IsEmpty);
+ ///
+ /// Get the number of hands that are not currently holding anything. This is a LinQ method, not a property, so
+ /// cache it instead of accessing this multiple times.
+ ///
+ public static int CountFreeableHands(this Entity component, SharedHandsSystem system)
+ {
+ return system.CountFreeableHands(component);
+ }
+
///
/// Get a list of hands that are currently holding nothing. This is a LinQ method, not a property, so cache
/// it instead of accessing this multiple times.
diff --git a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs
index fd732009e9a..e48aafeab52 100644
--- a/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs
+++ b/Content.Shared/Hands/EntitySystems/SharedHandsSystem.cs
@@ -5,7 +5,6 @@
using Content.Shared.Hands.Components;
using Content.Shared.Interaction;
using Content.Shared.Inventory.VirtualItem;
-using Content.Shared.Item;
using Content.Shared.Storage.EntitySystems;
using Robust.Shared.Containers;
using Robust.Shared.Input.Binding;
@@ -299,4 +298,16 @@ public bool TryGetHand(EntityUid handsUid, string handId, [NotNullWhen(true)] ou
return hands.Hands.TryGetValue(handId, out hand);
}
+
+ public int CountFreeableHands(Entity hands)
+ {
+ var freeable = 0;
+ foreach (var hand in hands.Comp.Hands.Values)
+ {
+ if (hand.IsEmpty || CanDropHeld(hands, hand))
+ freeable++;
+ }
+
+ return freeable;
+ }
}
diff --git a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs
index e45530e4582..b31cc755763 100644
--- a/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs
+++ b/Content.Shared/Inventory/VirtualItem/SharedVirtualItemSystem.cs
@@ -4,6 +4,7 @@
using Content.Shared.Interaction;
using Content.Shared.Inventory.Events;
using Content.Shared.Item;
+using Content.Shared.Popups;
using Robust.Shared.Containers;
using Robust.Shared.Network;
using Robust.Shared.Prototypes;
@@ -29,6 +30,7 @@ public abstract class SharedVirtualItemSystem : EntitySystem
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
+ [Dependency] private readonly SharedPopupSystem _popup = default!;
[ValidatePrototypeId]
private const string VirtualItem = "VirtualItem";
@@ -71,23 +73,53 @@ private void OnBeforeRangedInteract(Entity ent, ref Before
}
#region Hands
+
///
/// Spawns a virtual item in a empty hand
///
/// The entity we will make a virtual entity copy of
/// The entity that we want to insert the virtual entity
- public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user)
+ /// Whether or not to try and drop other items to make space
+ public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, bool dropOthers = false)
{
- return TrySpawnVirtualItemInHand(blockingEnt, user, out _);
+ return TrySpawnVirtualItemInHand(blockingEnt, user, out _, dropOthers);
}
- ///
- public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem)
+ ///
+ public bool TrySpawnVirtualItemInHand(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem, bool dropOthers = false)
{
- if (!TrySpawnVirtualItem(blockingEnt, user, out virtualItem) || !_handsSystem.TryGetEmptyHand(user, out var hand))
+ virtualItem = null;
+ if (!_handsSystem.TryGetEmptyHand(user, out var empty))
+ {
+ if (!dropOthers)
+ return false;
+
+ foreach (var hand in _handsSystem.EnumerateHands(user))
+ {
+ if (hand.HeldEntity is not { } held)
+ continue;
+
+ if (held == blockingEnt || HasComp(held))
+ continue;
+
+ if (!_handsSystem.TryDrop(user, hand))
+ continue;
+
+ if (!TerminatingOrDeleted(held))
+ _popup.PopupClient(Loc.GetString("virtual-item-dropped-other", ("dropped", held)), user, user);
+
+ empty = hand;
+ break;
+ }
+ }
+
+ if (empty == null)
+ return false;
+
+ if (!TrySpawnVirtualItem(blockingEnt, user, out virtualItem))
return false;
- _handsSystem.DoPickup(user, hand, virtualItem.Value);
+ _handsSystem.DoPickup(user, empty, virtualItem.Value);
return true;
}
@@ -120,6 +152,7 @@ public void DeleteInHandsMatching(EntityUid user, EntityUid matching)
/// The entity we will make a virtual entity copy of
/// The entity that we want to insert the virtual entity
/// The slot to which we will insert the virtual entity (could be the "shoes" slot, for example)
+ /// Whether or not to force an equip
public bool TrySpawnVirtualItemInInventory(EntityUid blockingEnt, EntityUid user, string slot, bool force = false)
{
return TrySpawnVirtualItemInInventory(blockingEnt, user, slot, force, out _);
@@ -140,6 +173,8 @@ public bool TrySpawnVirtualItemInInventory(EntityUid blockingEnt, EntityUid user
/// that's done check if the found virtual entity is a copy of our matching entity,
/// if it is, delete it
///
+ /// The entity that we want to delete the virtual entity from
+ /// The entity that made the virtual entity
/// Set this param if you have the name of the slot, it avoids unnecessary queries
public void DeleteInSlotMatching(EntityUid user, EntityUid matching, string? slotName = null)
{
@@ -178,6 +213,7 @@ public void DeleteInSlotMatching(EntityUid user, EntityUid matching, string? slo
///
/// The entity we will make a virtual entity copy of
/// The entity that we want to insert the virtual entity
+ /// The virtual item, if spawned
public bool TrySpawnVirtualItem(EntityUid blockingEnt, EntityUid user, [NotNullWhen(true)] out EntityUid? virtualItem)
{
if (_netManager.IsClient)
diff --git a/Content.Shared/Wieldable/WieldableSystem.cs b/Content.Shared/Wieldable/WieldableSystem.cs
index b765566f44a..cee6c65fa11 100644
--- a/Content.Shared/Wieldable/WieldableSystem.cs
+++ b/Content.Shared/Wieldable/WieldableSystem.cs
@@ -161,7 +161,7 @@ public bool CanWield(EntityUid uid, WieldableComponent component, EntityUid user
return false;
}
- if (hands.CountFreeHands() < component.FreeHandsRequired)
+ if (_handsSystem.CountFreeableHands((user, hands)) < component.FreeHandsRequired)
{
if (!quiet)
{
@@ -202,9 +202,21 @@ public bool TryWield(EntityUid used, WieldableComponent component, EntityUid use
if (component.WieldSound != null)
_audioSystem.PlayPredicted(component.WieldSound, used, user);
+ var virtuals = new List();
for (var i = 0; i < component.FreeHandsRequired; i++)
{
- _virtualItemSystem.TrySpawnVirtualItemInHand(used, user);
+ if (_virtualItemSystem.TrySpawnVirtualItemInHand(used, user, out var virtualItem, true))
+ {
+ virtuals.Add(virtualItem.Value);
+ continue;
+ }
+
+ foreach (var existingVirtual in virtuals)
+ {
+ QueueDel(existingVirtual);
+ }
+
+ return false;
}
if (TryComp(used, out UseDelayComponent? useDelay)
diff --git a/Resources/Locale/en-US/virtual/virtual-item.ftl b/Resources/Locale/en-US/virtual/virtual-item.ftl
new file mode 100644
index 00000000000..cb91f24cf7c
--- /dev/null
+++ b/Resources/Locale/en-US/virtual/virtual-item.ftl
@@ -0,0 +1 @@
+virtual-item-dropped-other = You dropped {THE($dropped)}!