]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Reimplement smart equipping (#22815)
authorKara <lunarautomaton6@gmail.com>
Thu, 21 Dec 2023 14:17:09 +0000 (07:17 -0700)
committerGitHub <noreply@github.com>
Thu, 21 Dec 2023 14:17:09 +0000 (01:17 +1100)
* Reimplement smart equipping

* inv prediction fix

* oops

Content.Server/Hands/Systems/HandsSystem.cs
Content.Shared/Containers/ItemSlot/ItemSlotsSystem.cs
Content.Shared/Interaction/SmartEquipSystem.cs [new file with mode: 0644]
Resources/Locale/en-US/hands/hands-system.ftl
Resources/Locale/en-US/interaction/smart-equip-system.ftl [new file with mode: 0644]

index 5c750e7544f1ac957fe006a7c798793e68ad28a8..15190c81c7987dd77ea570cb3ee51c8ec937459e 100644 (file)
@@ -59,8 +59,6 @@ namespace Content.Server.Hands.Systems
 
             CommandBinds.Builder
                 .Bind(ContentKeyFunctions.ThrowItemInHand, new PointerInputCmdHandler(HandleThrowItem))
-                .Bind(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack))
-                .Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt))
                 .Register<HandsSystem>();
         }
 
@@ -217,85 +215,7 @@ namespace Content.Server.Hands.Systems
 
             return true;
         }
-        private void HandleSmartEquipBackpack(ICommonSession? session)
-        {
-            HandleSmartEquip(session, "back");
-        }
-
-        private void HandleSmartEquipBelt(ICommonSession? session)
-        {
-            HandleSmartEquip(session, "belt");
-        }
-
-        // why tf is this even in hands system.
-        // TODO: move to storage or inventory
-        private void HandleSmartEquip(ICommonSession? session, string equipmentSlot)
-        {
-            if (session is not { } playerSession)
-                return;
-
-            if (playerSession.AttachedEntity is not {Valid: true} plyEnt || !Exists(plyEnt))
-                return;
-
-            if (!_actionBlockerSystem.CanInteract(plyEnt, null))
-                return;
 
-            if (!TryComp<HandsComponent>(plyEnt, out var hands) ||  hands.ActiveHand == null)
-                return;
-
-            if (!_inventorySystem.TryGetSlotEntity(plyEnt, equipmentSlot, out var slotEntity) ||
-                !TryComp(slotEntity, out StorageComponent? storageComponent))
-            {
-                if (_inventorySystem.HasSlot(plyEnt, equipmentSlot))
-                {
-                    if (hands.ActiveHand.HeldEntity == null && slotEntity != null)
-                    {
-                        _inventorySystem.TryUnequip(plyEnt, equipmentSlot);
-                        PickupOrDrop(plyEnt, slotEntity.Value);
-                        return;
-                    }
-                    if (hands.ActiveHand.HeldEntity == null)
-                        return;
-                    if (!_inventorySystem.CanEquip(plyEnt, hands.ActiveHand.HeldEntity.Value, equipmentSlot, out var reason))
-                    {
-                        _popupSystem.PopupEntity(Loc.GetString(reason), plyEnt, session);
-                        return;
-                    }
-                    if (slotEntity == null)
-                    {
-                        _inventorySystem.TryEquip(plyEnt, hands.ActiveHand.HeldEntity.Value, equipmentSlot);
-                        return;
-                    }
-                    _inventorySystem.TryUnequip(plyEnt, equipmentSlot);
-                    _inventorySystem.TryEquip(plyEnt, hands.ActiveHand.HeldEntity.Value, equipmentSlot);
-                    PickupOrDrop(plyEnt, slotEntity.Value);
-                    return;
-                }
-                _popupSystem.PopupEntity(Loc.GetString("hands-system-missing-equipment-slot", ("slotName", equipmentSlot)), plyEnt, session);
-                return;
-            }
-
-            if (hands.ActiveHand.HeldEntity != null)
-            {
-                _storageSystem.PlayerInsertHeldEntity(slotEntity.Value, plyEnt, storageComponent);
-            }
-            else
-            {
-                if (!storageComponent.Container.ContainedEntities.Any())
-                {
-                    _popupSystem.PopupEntity(Loc.GetString("hands-system-empty-equipment-slot", ("slotName", equipmentSlot)), plyEnt,  session);
-                }
-                else
-                {
-                    var lastStoredEntity = storageComponent.Container.ContainedEntities[^1];
-
-                    if (storageComponent.Container.Remove(lastStoredEntity))
-                    {
-                        PickupOrDrop(plyEnt, lastStoredEntity, animateUser: true, handsComp: hands);
-                    }
-                }
-            }
-        }
         #endregion
     }
 }
index ad27101cc145141d2af8ef3a5c76ccbea86b07ee..b272cc182ef283eda57496b0f9fea83ab85cd37b 100644 (file)
@@ -277,7 +277,7 @@ namespace Content.Shared.Containers.ItemSlots
         ///     Tries to insert item into a specific slot.
         /// </summary>
         /// <returns>False if failed to insert item</returns>
-        public bool TryInsert(EntityUid uid, string id, EntityUid item, EntityUid? user, ItemSlotsComponent? itemSlots = null)
+        public bool TryInsert(EntityUid uid, string id, EntityUid item, EntityUid? user, ItemSlotsComponent? itemSlots = null, bool excludeUserAudio = false)
         {
             if (!Resolve(uid, ref itemSlots))
                 return false;
@@ -285,19 +285,19 @@ namespace Content.Shared.Containers.ItemSlots
             if (!itemSlots.Slots.TryGetValue(id, out var slot))
                 return false;
 
-            return TryInsert(uid, slot, item, user);
+            return TryInsert(uid, slot, item, user, excludeUserAudio: excludeUserAudio);
         }
 
         /// <summary>
         ///     Tries to insert item into a specific slot.
         /// </summary>
         /// <returns>False if failed to insert item</returns>
-        public bool TryInsert(EntityUid uid, ItemSlot slot, EntityUid item, EntityUid? user)
+        public bool TryInsert(EntityUid uid, ItemSlot slot, EntityUid item, EntityUid? user, bool excludeUserAudio = false)
         {
             if (!CanInsert(uid, item, user, slot))
                 return false;
 
-            Insert(uid, slot, item, user);
+            Insert(uid, slot, item, user, excludeUserAudio: excludeUserAudio);
             return true;
         }
 
@@ -306,12 +306,12 @@ namespace Content.Shared.Containers.ItemSlots
         ///     Does not check action blockers.
         /// </summary>
         /// <returns>False if failed to insert item</returns>
-        public bool TryInsertFromHand(EntityUid uid, ItemSlot slot, EntityUid user, HandsComponent? hands = null)
+        public bool TryInsertFromHand(EntityUid uid, ItemSlot slot, EntityUid user, HandsComponent? hands = null, bool excludeUserAudio = false)
         {
             if (!Resolve(user, ref hands, false))
                 return false;
 
-            if (hands.ActiveHand?.HeldEntity is not EntityUid held)
+            if (hands.ActiveHand?.HeldEntity is not { } held)
                 return false;
 
             if (!CanInsert(uid, held, user, slot))
@@ -321,7 +321,7 @@ namespace Content.Shared.Containers.ItemSlots
             if (!_handsSystem.TryDrop(user, hands.ActiveHand))
                 return false;
 
-            Insert(uid, slot, held, user);
+            Insert(uid, slot, held, user, excludeUserAudio: excludeUserAudio);
             return true;
         }
         #endregion
diff --git a/Content.Shared/Interaction/SmartEquipSystem.cs b/Content.Shared/Interaction/SmartEquipSystem.cs
new file mode 100644 (file)
index 0000000..17c8f2e
--- /dev/null
@@ -0,0 +1,215 @@
+using Content.Shared.ActionBlocker;
+using Content.Shared.Containers.ItemSlots;
+using Content.Shared.Hands.Components;
+using Content.Shared.Hands.EntitySystems;
+using Content.Shared.Input;
+using Content.Shared.Inventory;
+using Content.Shared.Popups;
+using Content.Shared.Storage;
+using Content.Shared.Storage.EntitySystems;
+using Robust.Shared.Containers;
+using Robust.Shared.Input.Binding;
+using Robust.Shared.Player;
+
+namespace Content.Shared.Interaction;
+
+/// <summary>
+/// This handles smart equipping or inserting/ejecting from slots through keybinds--generally shift+E and shift+B
+/// </summary>
+public sealed class SmartEquipSystem : EntitySystem
+{
+    [Dependency] private readonly SharedHandsSystem _hands = default!;
+    [Dependency] private readonly SharedStorageSystem _storage = default!;
+    [Dependency] private readonly InventorySystem _inventory = default!;
+    [Dependency] private readonly ItemSlotsSystem _slots = default!;
+    [Dependency] private readonly SharedContainerSystem _container = default!;
+    [Dependency] private readonly SharedPopupSystem _popup = default!;
+    [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
+
+    /// <inheritdoc/>
+    public override void Initialize()
+    {
+        CommandBinds.Builder
+            .Bind(ContentKeyFunctions.SmartEquipBackpack, InputCmdHandler.FromDelegate(HandleSmartEquipBackpack, handle: false, outsidePrediction: false))
+            .Bind(ContentKeyFunctions.SmartEquipBelt, InputCmdHandler.FromDelegate(HandleSmartEquipBelt, handle: false, outsidePrediction: false))
+            .Register<SmartEquipSystem>();
+    }
+
+    public override void Shutdown()
+    {
+        base.Shutdown();
+
+        CommandBinds.Unregister<SmartEquipSystem>();
+    }
+
+    private void HandleSmartEquipBackpack(ICommonSession? session)
+    {
+        HandleSmartEquip(session, "back");
+    }
+
+    private void HandleSmartEquipBelt(ICommonSession? session)
+    {
+        HandleSmartEquip(session, "belt");
+    }
+
+    private void HandleSmartEquip(ICommonSession? session, string equipmentSlot)
+    {
+        if (session is not { } playerSession)
+            return;
+
+        if (playerSession.AttachedEntity is not { Valid: true } uid || !Exists(uid))
+            return;
+
+        if (!_actionBlocker.CanInteract(uid, null))
+            return;
+
+        // early out if we don't have any hands or a valid inventory slot
+        if (!TryComp<HandsComponent>(uid, out var hands) || hands.ActiveHand == null)
+            return;
+
+        if (!TryComp<InventoryComponent>(uid, out var inventory) || !_inventory.HasSlot(uid, equipmentSlot, inventory))
+        {
+            _popup.PopupClient(Loc.GetString("smart-equip-missing-equipment-slot", ("slotName", equipmentSlot)), uid, uid);
+            return;
+        }
+
+        var handItem = hands.ActiveHand.HeldEntity;
+
+        // early out if we have an item and cant drop it at all
+        if (handItem != null && !_hands.CanDropHeld(uid, hands.ActiveHand))
+        {
+            _popup.PopupClient(Loc.GetString("smart-equip-cant-drop"), uid, uid);
+            return;
+        }
+
+        // There are eight main cases we want to handle here,
+        // so let's write them out
+
+        // if the slot we're trying to smart equip from:
+        // 1) doesn't have an item
+        //    - with hand item: try to put it in the slot
+        //    - without hand item: fail
+        // 2) has an item, and that item is a storage item
+        //    - with hand item: try to put it in storage
+        //    - without hand item: try to take the last stored item and put it in our hands
+        // 3) has an item, and that item is an item slots holder
+        //    - with hand item: get the highest priority item slot with a valid whitelist and try to insert it
+        //    - without hand item: get the highest priority item slot with an item and try to eject it
+        // 4) has an item, with no special storage components
+        //    - with hand item: fail
+        //    - without hand item: try to put the item into your hand
+
+        _inventory.TryGetSlotEntity(uid, equipmentSlot, out var slotEntity);
+        var emptyEquipmentSlotString = Loc.GetString("smart-equip-empty-equipment-slot", ("slotName", equipmentSlot));
+
+        // case 1 (no slot item):
+        if (slotEntity is not { } slotItem)
+        {
+            if (handItem == null)
+            {
+                _popup.PopupClient(emptyEquipmentSlotString, uid, uid);
+                return;
+            }
+
+            if (!_inventory.CanEquip(uid, handItem.Value, equipmentSlot, out var reason))
+            {
+                _popup.PopupClient(Loc.GetString(reason), uid, uid);
+                return;
+            }
+
+            _hands.TryDrop(uid, hands.ActiveHand, handsComp: hands);
+            _inventory.TryEquip(uid, handItem.Value, equipmentSlot, predicted: true);
+            return;
+        }
+
+        // case 2 (storage item):
+        if (TryComp<StorageComponent>(slotItem, out var storage))
+        {
+            switch (handItem)
+            {
+                case null when storage.Container.ContainedEntities.Count == 0:
+                    _popup.PopupClient(emptyEquipmentSlotString, uid, uid);
+                    return;
+                case null:
+                    var removing = storage.Container.ContainedEntities[^1];
+                    _container.RemoveEntity(slotItem, removing);
+                    _hands.TryPickup(uid, removing, handsComp: hands);
+                    return;
+            }
+
+            if (!_storage.CanInsert(slotItem, handItem.Value, out var reason))
+            {
+                if (reason != null)
+                    _popup.PopupClient(Loc.GetString(reason), uid, uid);
+
+                return;
+            }
+
+            _hands.TryDrop(uid, hands.ActiveHand, handsComp: hands);
+            _storage.Insert(slotItem, handItem.Value, out var stacked, out _);
+
+            if (stacked != null)
+                _hands.TryPickup(uid, stacked.Value, handsComp: hands);
+
+            return;
+        }
+
+        // case 3 (itemslot item):
+        if (TryComp<ItemSlotsComponent>(slotItem, out var slots))
+        {
+            if (handItem == null)
+            {
+                ItemSlot? toEjectFrom = null;
+
+                foreach (var slot in slots.Slots.Values)
+                {
+                    if (slot.HasItem && slot.Priority > (toEjectFrom?.Priority ?? int.MinValue))
+                        toEjectFrom = slot;
+                }
+
+                if (toEjectFrom == null)
+                {
+                    _popup.PopupClient(emptyEquipmentSlotString, uid, uid);
+                    return;
+                }
+
+                _slots.TryEjectToHands(slotItem, toEjectFrom, uid, excludeUserAudio: true);
+                return;
+            }
+
+            ItemSlot? toInsertTo = null;
+
+            foreach (var slot in slots.Slots.Values)
+            {
+                if (!slot.HasItem
+                    && (slot.Whitelist?.IsValid(handItem.Value, EntityManager) ?? true)
+                    && slot.Priority > (toInsertTo?.Priority ?? int.MinValue))
+                {
+                    toInsertTo = slot;
+                }
+            }
+
+            if (toInsertTo == null)
+            {
+                _popup.PopupClient(Loc.GetString("smart-equip-no-valid-item-slot-insert", ("item", handItem.Value)), uid, uid);
+                return;
+            }
+
+            _slots.TryInsertFromHand(slotItem, toInsertTo, uid, hands, excludeUserAudio: true);
+            return;
+        }
+
+        // case 4 (just an item):
+        if (handItem != null)
+            return;
+
+        if (!_inventory.CanUnequip(uid, equipmentSlot, out var inventoryReason))
+        {
+            _popup.PopupClient(Loc.GetString(inventoryReason), uid, uid);
+            return;
+        }
+
+        _inventory.TryUnequip(uid, equipmentSlot, inventory: inventory, predicted: true);
+        _hands.TryPickup(uid, slotItem, handsComp: hands);
+    }
+}
index 7761b0c0ce43a4395c4b9b111ca7465a0ef466ae..6b7859fe1747b7e996b945fcd261f6d8dfb6a78c 100644 (file)
@@ -1,8 +1,3 @@
-## HandsSystem
-hands-system-missing-equipment-slot = You have no {$slotName} to take something out of!
-hands-system-empty-equipment-slot = There's nothing in your {$slotName} to take out!
-
-
 # Examine text after when they're holding something (in-hand)
 comp-hands-examine = { CAPITALIZE(SUBJECT($user)) } { CONJUGATE-BE($user) } holding { $items }.
 comp-hands-examine-empty = { CAPITALIZE(SUBJECT($user)) } { CONJUGATE-BE($user) } not holding anything.
diff --git a/Resources/Locale/en-US/interaction/smart-equip-system.ftl b/Resources/Locale/en-US/interaction/smart-equip-system.ftl
new file mode 100644 (file)
index 0000000..8c61116
--- /dev/null
@@ -0,0 +1,4 @@
+smart-equip-missing-equipment-slot = You have no {$slotName} slot to interact with!
+smart-equip-empty-equipment-slot = There's nothing in your {$slotName} slot to take out!
+smart-equip-no-valid-item-slot-insert = There's no valid item slot for {THE($item)} to go into!
+smart-equip-cant-drop = You can't drop that!