]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Inventory slot enumerator rejig (#21788)
authorLeon Friedrich <60421075+ElectroJr@users.noreply.github.com>
Thu, 7 Dec 2023 21:20:51 +0000 (16:20 -0500)
committerGitHub <noreply@github.com>
Thu, 7 Dec 2023 21:20:51 +0000 (08:20 +1100)
18 files changed:
Content.Client/Clothing/ClientClothingSystem.cs
Content.Client/Inventory/ClientInventorySystem.cs
Content.Client/Inventory/StrippableBoundUserInterface.cs
Content.Server/Administration/Commands/SetOutfitCommand.cs
Content.Server/Administration/Systems/AdminSystem.cs
Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs
Content.Server/Body/Systems/InternalsSystem.cs
Content.Server/Chemistry/EntitySystems/SolutionInjectOnCollideSystem.cs
Content.Server/Inventory/ServerInventorySystem.cs
Content.Server/Movement/StressTestMovementSystem.cs
Content.Server/Zombies/ZombieSystem.cs
Content.Shared/Inventory/InventoryComponent.cs
Content.Shared/Inventory/InventorySystem.Equip.cs
Content.Shared/Inventory/InventorySystem.Helpers.cs
Content.Shared/Inventory/InventorySystem.Relay.cs
Content.Shared/Inventory/InventorySystem.Slots.cs
Content.Shared/Storage/EntitySystems/MagnetPickupSystem.cs
Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Clothing.cs

index b16e14d65355797af7981afbf797c53304a93e19..979f7430e17d55cef6deb87a8a7520c0ac8e72f6 100644 (file)
@@ -202,17 +202,15 @@ public sealed class ClientClothingSystem : ClothingSystem
         revealedLayers.Clear();
     }
 
-    public void InitClothing(EntityUid uid, InventoryComponent? component = null, SpriteComponent? sprite = null)
+    public void InitClothing(EntityUid uid, InventoryComponent component)
     {
-        if (!Resolve(uid, ref sprite, ref component) || !_inventorySystem.TryGetSlots(uid, out var slots, component))
+        if (!TryComp(uid, out SpriteComponent? sprite))
             return;
 
-        foreach (var slot in slots)
+        var enumerator = _inventorySystem.GetSlotEnumerator((uid, component));
+        while (enumerator.NextItem(out var item, out var slot))
         {
-            if (!_inventorySystem.TryGetSlotContainer(uid, slot.Name, out var containerSlot, out _, component) ||
-                !containerSlot.ContainedEntity.HasValue) continue;
-
-            RenderEquipment(uid, containerSlot.ContainedEntity.Value, slot.Name, component, sprite);
+            RenderEquipment(uid, item, slot.Name, component, sprite);
         }
     }
 
index 6976a8b5bc92c46e77f7178c87b63d5ac94f577e..d4615210f2450abc84000e4c43f719e312499f87 100644 (file)
@@ -1,9 +1,7 @@
 using Content.Client.Clothing;
 using Content.Client.Examine;
-using Content.Client.UserInterface.Controls;
 using Content.Client.Verbs.UI;
 using Content.Shared.Clothing.Components;
-using Content.Shared.Hands.Components;
 using Content.Shared.Interaction;
 using Content.Shared.Interaction.Events;
 using Content.Shared.Inventory;
@@ -15,14 +13,12 @@ using Robust.Client.UserInterface;
 using Robust.Shared.Containers;
 using Robust.Shared.Input.Binding;
 using Robust.Shared.Player;
-using Robust.Shared.Prototypes;
 
 namespace Content.Client.Inventory
 {
     [UsedImplicitly]
     public sealed class ClientInventorySystem : InventorySystem
     {
-        [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
         [Dependency] private readonly IPlayerManager _playerManager = default!;
         [Dependency] private readonly IUserInterfaceManager _ui = default!;
 
@@ -89,7 +85,7 @@ namespace Content.Client.Inventory
         private void OnDidUnequip(InventorySlotsComponent component, DidUnequipEvent args)
         {
             UpdateSlot(args.Equipee, component, args.Slot);
-            if (args.Equipee != _playerManager.LocalPlayer?.ControlledEntity)
+            if (args.Equipee != _playerManager.LocalEntity)
                 return;
             var update = new SlotSpriteUpdate(null, args.SlotGroup, args.Slot, false);
             OnSpriteUpdate?.Invoke(update);
@@ -98,7 +94,7 @@ namespace Content.Client.Inventory
         private void OnDidEquip(InventorySlotsComponent component, DidEquipEvent args)
         {
             UpdateSlot(args.Equipee, component, args.Slot);
-            if (args.Equipee != _playerManager.LocalPlayer?.ControlledEntity)
+            if (args.Equipee != _playerManager.LocalEntity)
                 return;
             var update = new SlotSpriteUpdate(args.Equipment, args.SlotGroup, args.Slot,
                 HasComp<StorageComponent>(args.Equipment));
@@ -107,10 +103,8 @@ namespace Content.Client.Inventory
 
         private void OnShutdown(EntityUid uid, InventoryComponent component, ComponentShutdown args)
         {
-            if (uid != _playerManager.LocalPlayer?.ControlledEntity)
-                return;
-
-            OnUnlinkInventory?.Invoke();
+            if (uid == _playerManager.LocalEntity)
+                OnUnlinkInventory?.Invoke();
         }
 
         private void OnPlayerDetached(EntityUid uid, InventorySlotsComponent component, LocalPlayerDetachedEvent args)
@@ -151,13 +145,10 @@ namespace Content.Client.Inventory
             base.OnInit(uid, component, args);
             _clothingVisualsSystem.InitClothing(uid, component);
 
-            if (!_prototypeManager.TryIndex(component.TemplateId, out InventoryTemplatePrototype? invTemplate) ||
-                !TryComp(uid, out InventorySlotsComponent? inventorySlots))
-            {
+            if (!TryComp(uid, out InventorySlotsComponent? inventorySlots))
                 return;
-            }
 
-            foreach (var slot in invTemplate.Slots)
+            foreach (var slot in component.Slots)
             {
                 TryAddSlotDef(uid, inventorySlots, slot);
             }
@@ -165,7 +156,7 @@ namespace Content.Client.Inventory
 
         public void ReloadInventory(InventorySlotsComponent? component = null)
         {
-            var player = _playerManager.LocalPlayer?.ControlledEntity;
+            var player = _playerManager.LocalEntity;
             if (player == null || !Resolve(player.Value, ref component, false))
             {
                 return;
@@ -179,7 +170,7 @@ namespace Content.Client.Inventory
         {
             var oldData = component.SlotData[slotName];
             var newData = component.SlotData[slotName] = new SlotData(oldData, state);
-            if (owner == _playerManager.LocalPlayer?.ControlledEntity)
+            if (owner == _playerManager.LocalEntity)
                 EntitySlotUpdate?.Invoke(newData);
         }
 
@@ -198,7 +189,7 @@ namespace Content.Client.Inventory
 
             var newData = component.SlotData[slotName] =
                 new SlotData(component.SlotData[slotName], newHighlight, newBlocked);
-            if (owner == _playerManager.LocalPlayer?.ControlledEntity)
+            if (owner == _playerManager.LocalEntity)
                 EntitySlotUpdate?.Invoke(newData);
         }
 
@@ -208,48 +199,11 @@ namespace Content.Client.Inventory
             if (!component.SlotData.TryAdd(newSlotDef.Name, newSlotData))
                 return false;
 
-            if (owner == _playerManager.LocalPlayer?.ControlledEntity)
+            if (owner == _playerManager.LocalEntity)
                 OnSlotAdded?.Invoke(newSlotData);
             return true;
         }
 
-        public void RemoveSlotDef(EntityUid owner, InventorySlotsComponent component, SlotData slotData)
-        {
-            if (component.SlotData.Remove(slotData.SlotName))
-            {
-                if (owner == _playerManager.LocalPlayer?.ControlledEntity)
-                    OnSlotRemoved?.Invoke(slotData);
-            }
-        }
-
-        public void RemoveSlotDef(EntityUid owner, InventorySlotsComponent component, string slotName)
-        {
-            if (!component.SlotData.TryGetValue(slotName, out var slotData))
-                return;
-
-            component.SlotData.Remove(slotName);
-
-            if (owner == _playerManager.LocalPlayer?.ControlledEntity)
-                OnSlotRemoved?.Invoke(slotData);
-        }
-
-        // TODO hud refactor This should also live in a UI Controller
-        private void HoverInSlotButton(EntityUid uid, string slot, SlotControl control,
-            InventoryComponent? inventoryComponent = null, HandsComponent? hands = null)
-        {
-            if (!Resolve(uid, ref inventoryComponent))
-                return;
-
-            if (!Resolve(uid, ref hands, false))
-                return;
-
-            if (hands.ActiveHandEntity is not EntityUid heldEntity)
-                return;
-
-            if (!TryGetSlotContainer(uid, slot, out var containerSlot, out var slotDef, inventoryComponent))
-                return;
-        }
-
         public void UIInventoryActivate(string slot)
         {
             EntityManager.RaisePredictiveEvent(new UseSlotNetworkMessage(slot));
index 6dbb8f3c221ca466a0928b38014d0418f6ed6e3c..9fbb64309fbb098eebc278e2e6642ebd3125b0e2 100644 (file)
@@ -90,11 +90,11 @@ namespace Content.Client.Inventory
 
             _strippingMenu.ClearButtons();
 
-            if (EntMan.TryGetComponent<InventoryComponent>(Owner, out var inv) && _protoMan.TryIndex<InventoryTemplatePrototype>(inv.TemplateId, out var template))
+            if (EntMan.TryGetComponent<InventoryComponent>(Owner, out var inv))
             {
-                foreach (var slot in template.Slots)
+                foreach (var slot in inv.Slots)
                 {
-                    AddInventoryButton(Owner, slot.Name, template, inv);
+                    AddInventoryButton(Owner, slot.Name, inv);
                 }
             }
 
@@ -190,7 +190,7 @@ namespace Content.Client.Inventory
                 _ui.GetUIController<VerbMenuUIController>().OpenVerbMenu(slot.Entity.Value);
         }
 
-        private void AddInventoryButton(EntityUid invUid, string slotId, InventoryTemplatePrototype _, InventoryComponent inv)
+        private void AddInventoryButton(EntityUid invUid, string slotId, InventoryComponent inv)
         {
             if (!_inv.TryGetSlotContainer(invUid, slotId, out var container, out var slotDef, inv))
                 return;
index 97c1fa0656c63a37d48620ab361d49fda0a42718..72ff9ff9b6287a7dd21adddbdf14ec687184a4a8 100644 (file)
@@ -92,9 +92,9 @@ namespace Content.Server.Administration.Commands
             }
 
             var invSystem = entityManager.System<InventorySystem>();
-            if (invSystem.TryGetSlots(target, out var slotDefinitions, inventoryComponent))
+            if (invSystem.TryGetSlots(target, out var slots))
             {
-                foreach (var slot in slotDefinitions)
+                foreach (var slot in slots)
                 {
                     invSystem.TryUnequip(target, slot.Name, true, true, false, inventoryComponent);
                     var gearStr = startingGear.GetGear(slot.Name, profile);
index cc31071fe8cb69de28c7845111cd6f8a17d82e92..966bff2f7157c6b3f9aed25ec9d95b22be0d79eb 100644 (file)
@@ -368,15 +368,12 @@ namespace Content.Server.Administration.Systems
                     }
                 }
 
-                if (TryComp(entity.Value, out InventoryComponent? inventory) &&
-                    _inventory.TryGetSlots(entity.Value, out var slots, inventory))
+                if (_inventory.TryGetContainerSlotEnumerator(entity.Value, out var enumerator))
                 {
-                    foreach (var slot in slots)
+                    while (enumerator.NextItem(out var item, out var slot))
                     {
-                        if (_inventory.TryUnequip(entity.Value, entity.Value, slot.Name, out var item, true, true))
-                        {
-                            _physics.ApplyAngularImpulse(item.Value, ThrowingSystem.ThrowAngularImpulse);
-                        }
+                        if (_inventory.TryUnequip(entity.Value, entity.Value, slot.Name, true, true))
+                            _physics.ApplyAngularImpulse(item, ThrowingSystem.ThrowAngularImpulse);
                     }
                 }
 
index d7df3faee2bfd44b4d6a659bee91349887d20cfa..8d754d1b3af899d5ed48b5b7fcd5083c11155935 100644 (file)
@@ -285,27 +285,7 @@ public sealed partial class AdminVerbSystem
                     Text = "Refill Internals Oxygen",
                     Category = VerbCategory.Tricks,
                     Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
-                    Act = () =>
-                    {
-                        foreach (var slot in _inventorySystem.GetSlots(args.Target))
-                        {
-                            if (!_inventorySystem.TryGetSlotEntity(args.Target, slot.Name, out var entity))
-                                continue;
-
-                            if (!TryComp(entity, out tank))
-                                continue;
-
-                            RefillGasTank(entity.Value, Gas.Oxygen, tank);
-                        }
-
-                        foreach (var held in _handsSystem.EnumerateHeld(args.Target))
-                        {
-                            if (!TryComp(held, out tank))
-                                continue;
-
-                            RefillGasTank(held, Gas.Oxygen, tank);
-                        }
-                    },
+                    Act = () => RefillEquippedTanks(args.User, Gas.Oxygen),
                     Impact = LogImpact.Extreme,
                     Message = Loc.GetString("admin-trick-internals-refill-oxygen-description"),
                     Priority = (int) TricksVerbPriorities.RefillOxygen,
@@ -317,27 +297,7 @@ public sealed partial class AdminVerbSystem
                     Text = "Refill Internals Nitrogen",
                     Category = VerbCategory.Tricks,
                     Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/red.rsi"), "icon"),
-                    Act = () =>
-                    {
-                        foreach (var slot in _inventorySystem.GetSlots(args.Target))
-                        {
-                            if (!_inventorySystem.TryGetSlotEntity(args.Target, slot.Name, out var entity))
-                                continue;
-
-                            if (!TryComp(entity, out tank))
-                                continue;
-
-                            RefillGasTank(entity.Value, Gas.Nitrogen, tank);
-                        }
-
-                        foreach (var held in _handsSystem.EnumerateHeld(args.Target))
-                        {
-                            if (!TryComp(held, out tank))
-                                continue;
-
-                            RefillGasTank(held, Gas.Nitrogen, tank);
-                        }
-                    },
+                    Act = () =>RefillEquippedTanks(args.User, Gas.Nitrogen),
                     Impact = LogImpact.Extreme,
                     Message = Loc.GetString("admin-trick-internals-refill-nitrogen-description"),
                     Priority = (int) TricksVerbPriorities.RefillNitrogen,
@@ -349,27 +309,7 @@ public sealed partial class AdminVerbSystem
                     Text = "Refill Internals Plasma",
                     Category = VerbCategory.Tricks,
                     Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
-                    Act = () =>
-                    {
-                        foreach (var slot in _inventorySystem.GetSlots(args.Target))
-                        {
-                            if (!_inventorySystem.TryGetSlotEntity(args.Target, slot.Name, out var entity))
-                                continue;
-
-                            if (!TryComp(entity, out tank))
-                                continue;
-
-                            RefillGasTank(entity.Value, Gas.Plasma, tank);
-                        }
-
-                        foreach (var held in _handsSystem.EnumerateHeld(args.Target))
-                        {
-                            if (!TryComp(held, out tank))
-                                continue;
-
-                            RefillGasTank(held, Gas.Plasma, tank);
-                        }
-                    },
+                    Act = () => RefillEquippedTanks(args.User, Gas.Plasma),
                     Impact = LogImpact.Extreme,
                     Message = Loc.GetString("admin-trick-internals-refill-plasma-description"),
                     Priority = (int) TricksVerbPriorities.RefillPlasma,
@@ -792,9 +732,17 @@ public sealed partial class AdminVerbSystem
         }
     }
 
-    private void RefillGasTank(EntityUid tank, Gas gasType, GasTankComponent? tankComponent)
+    private void RefillEquippedTanks(EntityUid target, Gas plasma)
+    {
+        foreach (var held in _inventorySystem.GetHandOrInventoryEntities(target))
+        {
+            RefillGasTank(held, Gas.Plasma);
+        }
+    }
+
+    private void RefillGasTank(EntityUid tank, Gas gasType, GasTankComponent? tankComponent = null)
     {
-        if (!Resolve(tank, ref tankComponent))
+        if (!Resolve(tank, ref tankComponent, false))
             return;
 
         var mixSize = tankComponent.Air.Volume;
index 17a6544976b98c97035542f73b0868ff01b585cb..b96e108968dd554ff8e7a182e602336d2e56e9c4 100644 (file)
@@ -6,6 +6,7 @@ using Content.Server.Popups;
 using Content.Shared.Alert;
 using Content.Shared.Atmos;
 using Content.Shared.DoAfter;
+using Content.Shared.Hands.Components;
 using Content.Shared.Internals;
 using Content.Shared.Inventory;
 using Content.Shared.Verbs;
@@ -26,6 +27,8 @@ public sealed class InternalsSystem : EntitySystem
     [Dependency] private readonly InventorySystem _inventory = default!;
     [Dependency] private readonly PopupSystem _popupSystem = default!;
 
+    public const SlotFlags InventorySlots = SlotFlags.POCKET | SlotFlags.BELT;
+
     public override void Initialize()
     {
         base.Initialize();
@@ -81,7 +84,7 @@ public sealed class InternalsSystem : EntitySystem
             return;
         }
 
-        var tank = FindBestGasTank(uid, internals);
+        var tank = FindBestGasTank(uid);
 
         if (tank == null)
         {
@@ -224,59 +227,35 @@ public sealed class InternalsSystem : EntitySystem
         return 1;
     }
 
-    public Entity<GasTankComponent>? FindBestGasTank(EntityUid internalsOwner, InternalsComponent component)
+    public Entity<GasTankComponent>? FindBestGasTank(Entity<HandsComponent?, InventoryComponent?, ContainerManagerComponent?> user)
     {
         // Prioritise
         // 1. back equipped tanks
         // 2. exo-slot tanks
         // 3. in-hand tanks
         // 4. pocket/belt tanks
-        InventoryComponent? inventory = null;
-        ContainerManagerComponent? containerManager = null;
 
-        if (_inventory.TryGetSlotEntity(internalsOwner, "back", out var backEntity, inventory, containerManager) &&
+        if (!Resolve(user.Owner, ref user.Comp1, ref user.Comp2, ref user.Comp3))
+            return null;
+
+        if (_inventory.TryGetSlotEntity(user.Owner, "back", out var backEntity, user.Comp2, user.Comp3) &&
             TryComp<GasTankComponent>(backEntity, out var backGasTank) &&
             _gasTank.CanConnectToInternals(backGasTank))
         {
             return (backEntity.Value, backGasTank);
         }
 
-        if (_inventory.TryGetSlotEntity(internalsOwner, "suitstorage", out var entity, inventory, containerManager) &&
+        if (_inventory.TryGetSlotEntity(user.Owner, "suitstorage", out var entity, user.Comp2, user.Comp3) &&
             TryComp<GasTankComponent>(entity, out var gasTank) &&
             _gasTank.CanConnectToInternals(gasTank))
         {
             return (entity.Value, gasTank);
         }
 
-        var tanks = new List<Entity<GasTankComponent>>();
-
-        foreach (var hand in _hands.EnumerateHands(internalsOwner))
-        {
-            if (TryComp(hand.HeldEntity, out gasTank) && _gasTank.CanConnectToInternals(gasTank))
-                tanks.Add((hand.HeldEntity.Value, gasTank));
-        }
-
-        if (tanks.Count > 0)
+        foreach (var item in _inventory.GetHandOrInventoryEntities((user.Owner, user.Comp1, user.Comp2)))
         {
-            tanks.Sort((x, y) => y.Comp.Air.TotalMoles.CompareTo(x.Comp.Air.TotalMoles));
-            return tanks[0];
-        }
-
-        if (Resolve(internalsOwner, ref inventory, false))
-        {
-            var enumerator = new InventorySystem.ContainerSlotEnumerator(internalsOwner, inventory.TemplateId, _protoManager, _inventory, SlotFlags.POCKET | SlotFlags.BELT);
-
-            while (enumerator.MoveNext(out var container))
-            {
-                if (TryComp(container.ContainedEntity, out gasTank) && _gasTank.CanConnectToInternals(gasTank))
-                    tanks.Add((container.ContainedEntity.Value, gasTank));
-            }
-
-            if (tanks.Count > 0)
-            {
-                tanks.Sort((x, y) => y.Comp.Air.TotalMoles.CompareTo(x.Comp.Air.TotalMoles));
-                return tanks[0];
-            }
+            if (TryComp(item, out gasTank) && _gasTank.CanConnectToInternals(gasTank))
+                return (item, gasTank);
         }
 
         return null;
index dcfc57a9b252639847273e629059127808da7543..e053ce972047819c57b13013dde1fb3294f4f6bc 100644 (file)
@@ -35,15 +35,13 @@ namespace Content.Server.Chemistry.EntitySystems
                 return;
             }
 
-            if (component.BlockSlots != 0x0 && TryComp<InventoryComponent>(target, out var inventory))
+            if (component.BlockSlots != 0x0)
             {
-                var containerEnumerator = new InventorySystem.ContainerSlotEnumerator(target, inventory.TemplateId, _protoManager, _inventorySystem, component.BlockSlots);
+                var containerEnumerator = _inventorySystem.GetSlotEnumerator(target, component.BlockSlots);
 
-                while (containerEnumerator.MoveNext(out var container))
-                {
-                    if (!container.ContainedEntity.HasValue) continue;
+                // TODO add a helper method for this?
+                if (containerEnumerator.MoveNext(out _))
                     return;
-                }
             }
 
             var solRemoved = solution.SplitSolution(component.TransferAmount);
index f8d4bd3a1fa98b69f29f6b4763b8d25a35173a3e..7e3d9b3c7d35d467dae3e659e5148b401c764a28 100644 (file)
@@ -25,10 +25,8 @@ namespace Content.Server.Inventory
 
         private void OnExploded(Entity<InventoryComponent> ent, ref BeforeExplodeEvent args)
         {
-            if (!TryGetContainerSlotEnumerator(ent, out var slots, ent.Comp))
-                return;
-
             // explode each item in their inventory too
+            var slots = new InventorySlotEnumerator(ent);
             while (slots.MoveNext(out var slot))
             {
                 if (slot.ContainedEntity != null)
@@ -55,33 +53,16 @@ namespace Content.Server.Inventory
             }
         }
 
-        public void TransferEntityInventories(EntityUid uid, EntityUid target)
+        public void TransferEntityInventories(Entity<InventoryComponent?> source, Entity<InventoryComponent?> target)
         {
-            if (!TryGetContainerSlotEnumerator(uid, out var enumerator))
+            if (!Resolve(source.Owner, ref source.Comp) || !Resolve(target.Owner, ref target.Comp))
                 return;
 
-            Dictionary<string, EntityUid> inventoryEntities = new();
-            var slots = GetSlots(uid);
-            while (enumerator.MoveNext(out var containerSlot))
-            {
-                //records all the entities stored in each of the target's slots
-                foreach (var slot in slots)
-                {
-                    if (TryGetSlotContainer(target, slot.Name, out var conslot, out _) &&
-                        conslot.ID == containerSlot.ID &&
-                        containerSlot.ContainedEntity is { } containedEntity)
-                    {
-                        inventoryEntities.Add(slot.Name, containedEntity);
-                    }
-                }
-                //drops everything in the target's inventory on the ground
-                TryUnequip(uid, containerSlot.ID, true, true);
-            }
-            // This takes the objects we removed and stored earlier
-            // and actually equips all of it to the new entity
-            foreach (var (slot, item) in inventoryEntities)
+            var enumerator = new InventorySlotEnumerator(source.Comp);
+            while (enumerator.NextItem(out var item, out var slot))
             {
-                TryEquip(target, item, slot , true, true);
+                if (TryUnequip(source, slot.Name, true, true, inventory: source.Comp))
+                    TryEquip(target, item, slot.Name , true, true, inventory: target.Comp);
             }
         }
     }
index 632af9cecf8b8fd21e1911d0b8d5d76ae1748391..81ea6840e31fcc5224edeb21c6c338c20806c551 100644 (file)
@@ -26,6 +26,9 @@ public sealed class StressTestMovementSystem : EntitySystem
 
         while (query.MoveNext(out var uid, out var stressTest, out var transform))
         {
+            if (!transform.ParentUid.IsValid())
+                continue;
+
             stressTest.Progress += frameTime;
 
             if (stressTest.Progress > 1)
index 59edad6b4a0de31eb373e92d5a6ac3d2c768c98d..1009e0a294748194d7017bd655ffd38fd92b75ef 100644 (file)
@@ -40,6 +40,16 @@ namespace Content.Server.Zombies
         [Dependency] private readonly MobStateSystem _mobState = default!;
         [Dependency] private readonly SharedPopupSystem _popup = default!;
 
+        public const SlotFlags ProtectiveSlots =
+            SlotFlags.FEET |
+            SlotFlags.HEAD |
+            SlotFlags.EYES |
+            SlotFlags.GLOVES |
+            SlotFlags.MASK |
+            SlotFlags.NECK |
+            SlotFlags.INNERCLOTHING |
+            SlotFlags.OUTERCLOTHING;
+
         public override void Initialize()
         {
             base.Initialize();
@@ -164,33 +174,27 @@ namespace Content.Server.Zombies
 
         private float GetZombieInfectionChance(EntityUid uid, ZombieComponent component)
         {
-            var baseChance = component.MaxZombieInfectionChance;
-
-            if (!TryComp<InventoryComponent>(uid, out var inventoryComponent))
-                return baseChance;
-
-            var enumerator =
-                new InventorySystem.ContainerSlotEnumerator(uid, inventoryComponent.TemplateId, _protoManager, _inv,
-                    SlotFlags.FEET |
-                    SlotFlags.HEAD |
-                    SlotFlags.EYES |
-                    SlotFlags.GLOVES |
-                    SlotFlags.MASK |
-                    SlotFlags.NECK |
-                    SlotFlags.INNERCLOTHING |
-                    SlotFlags.OUTERCLOTHING);
+            var max = component.MaxZombieInfectionChance;
+
+            if (!_inventory.TryGetContainerSlotEnumerator(uid, out var enumerator, ProtectiveSlots))
+                return max;
 
             var items = 0f;
             var total = 0f;
             while (enumerator.MoveNext(out var con))
             {
                 total++;
-
                 if (con.ContainedEntity != null)
                     items++;
             }
 
-            var max = component.MaxZombieInfectionChance;
+            if (total == 0)
+                return max;
+
+            // Everyone knows that when it comes to zombies, socks & sandals provide just as much protection as an
+            // armored vest. Maybe these should be weighted per-item. I.e. some kind of coverage/protection component.
+            // Or at the very least different weights per slot.
+
             var min = component.MinZombieInfectionChance;
             //gets a value between the max and min based on how many items the entity is wearing
             var chance = (max-min) * ((total - items)/total) + min;
index 07a2463d12a6c8ee553ecbb8cda85e70066d19d3..2a8710f0f282ea56fe6f8d348ab05d55c77d079e 100644 (file)
@@ -1,4 +1,5 @@
-using Robust.Shared.GameStates;
+using Robust.Shared.Containers;
+using Robust.Shared.GameStates;
 using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
 
 namespace Content.Shared.Inventory;
@@ -11,4 +12,7 @@ public sealed partial class InventoryComponent : Component
     public string TemplateId { get; private set; } = "human";
 
     [DataField("speciesId")] public string? SpeciesId { get; set; }
+
+    public SlotDefinition[] Slots = Array.Empty<SlotDefinition>();
+    public ContainerSlot[] Containers = Array.Empty<ContainerSlot>();
 }
index 70083fbfebaa3ac880ad049009770cb2f48fa2d4..a6c818ca7e8587407f63b68c36b17533cb72eee3 100644 (file)
@@ -17,6 +17,7 @@ using Robust.Shared.Network;
 using Robust.Shared.Player;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Timing;
+using Robust.Shared.Utility;
 
 namespace Content.Shared.Inventory;
 
@@ -47,12 +48,10 @@ public abstract partial class InventorySystem
 
     protected void QuickEquip(EntityUid uid, ClothingComponent component, UseInHandEvent args)
     {
-        if (!TryComp(args.User, out InventoryComponent? inv)
-            || !TryComp(args.User, out HandsComponent? hands)
-            || !_prototypeManager.TryIndex<InventoryTemplatePrototype>(inv.TemplateId, out var prototype))
+        if (!TryComp(args.User, out InventoryComponent? inv) || !HasComp<HandsComponent>(args.User))
             return;
 
-        foreach (var slotDef in prototype.Slots)
+        foreach (var slotDef in inv.Slots)
         {
             if (!CanEquip(args.User, uid, slotDef.Name, out _, slotDef, inv))
                 continue;
@@ -255,6 +254,7 @@ public abstract partial class InventorySystem
         if (slotDefinition == null && !TryGetSlot(target, slot, out slotDefinition, inventory: inventory))
             return false;
 
+        DebugTools.Assert(slotDefinition.Name == slot);
         if (slotDefinition.DependsOn != null && !TryGetSlotEntity(target, slotDefinition.DependsOn, out _, inventory))
             return false;
 
@@ -347,7 +347,8 @@ public abstract partial class InventorySystem
 
         removedItem = slotContainer.ContainedEntity;
 
-        if (!removedItem.HasValue) return false;
+        if (!removedItem.HasValue)
+            return false;
 
         if (!force && !CanUnequip(actor, target, slot, out var reason, slotContainer, slotDefinition, inventory))
         {
@@ -360,7 +361,7 @@ public abstract partial class InventorySystem
         if (!force && !_containerSystem.CanRemove(removedItem.Value, slotContainer))
             return false;
 
-        foreach (var slotDef in GetSlots(target, inventory))
+        foreach (var slotDef in inventory.Slots)
         {
             if (slotDef != slotDefinition && slotDef.DependsOn == slotDefinition.Name)
             {
index 0e24d2ec24e45d21af8a7044b1ccf4df5dd1ad97..b1212abe674ee1076873cbf61f82ffdf03bc9be0 100644 (file)
@@ -10,11 +10,11 @@ public partial class InventorySystem
     /// <summary>
     /// Yields all entities in hands or inventory slots with the specific flags.
     /// </summary>
-    public IEnumerable<EntityUid> GetHandOrInventoryEntities(EntityUid user, SlotFlags flags = SlotFlags.All)
+    public IEnumerable<EntityUid> GetHandOrInventoryEntities(Entity<HandsComponent?, InventoryComponent?> user, SlotFlags flags = SlotFlags.All)
     {
-        if (TryComp<HandsComponent>(user, out var handsComp))
+        if (Resolve(user.Owner, ref user.Comp1, false))
         {
-            foreach (var hand in handsComp.Hands.Values)
+            foreach (var hand in user.Comp1.Hands.Values)
             {
                 if (hand.HeldEntity == null)
                     continue;
@@ -23,27 +23,22 @@ public partial class InventorySystem
             }
         }
 
-        if (TryComp<InventoryComponent>(user, out var inventoryComp))
-        {
-            var slotEnumerator = new ContainerSlotEnumerator(user, inventoryComp.TemplateId,
-                _prototypeManager, this, flags);
-
-            while (slotEnumerator.MoveNext(out var slot))
-            {
-                if (slot.ContainedEntity == null)
-                    continue;
+        if (!Resolve(user.Owner, ref user.Comp2, false))
+            yield break;
 
-                yield return slot.ContainedEntity.Value;
-            }
+        var slotEnumerator = new InventorySlotEnumerator(user.Comp2, flags);
+        while (slotEnumerator.NextItem(out var item))
+        {
+            yield return item;
         }
     }
 
     /// <summary>
     ///     Returns the definition of the inventory slot that the given entity is currently in..
     /// </summary>
-    public bool TryGetContainingSlot(EntityUid uid, [NotNullWhen(true)] out SlotDefinition? slot)
+    public bool TryGetContainingSlot(Entity<TransformComponent?, MetaDataComponent?> entity, [NotNullWhen(true)] out SlotDefinition? slot)
     {
-        if (!_containerSystem.TryGetContainingContainer(uid, out var container))
+        if (!_containerSystem.TryGetContainingContainer(entity.Owner, out var container, entity.Comp2, entity.Comp1))
         {
             slot = null;
             return false;
@@ -55,9 +50,10 @@ public partial class InventorySystem
     /// <summary>
     ///     Returns true if the given entity is equipped to an inventory slot with the given inventory slot flags.
     /// </summary>
-    public bool InSlotWithFlags(EntityUid uid, SlotFlags flags)
+    public bool InSlotWithFlags(Entity<TransformComponent?, MetaDataComponent?> entity, SlotFlags flags)
     {
-        return TryGetContainingSlot(uid, out var slot) && ((slot.SlotFlags & flags) == flags);
+        return TryGetContainingSlot(entity, out var slot)
+               && (slot.SlotFlags & flags) == flags;
     }
 
     public bool SpawnItemInSlot(EntityUid uid, string slot, string prototype, bool silent = false, bool force = false, InventoryComponent? inventory = null)
index 773128a12b83c28a37d5b7bd7571533c669bc8f8..fb27811073203e4057649c3892b49711758ad450 100644 (file)
@@ -12,7 +12,6 @@ using Content.Shared.Slippery;
 using Content.Shared.Strip.Components;
 using Content.Shared.Temperature;
 using Content.Shared.Verbs;
-using Robust.Shared.Containers;
 
 namespace Content.Shared.Inventory;
 
@@ -59,17 +58,15 @@ public partial class InventorySystem
 
     public void RelayEvent<T>(Entity<InventoryComponent> inventory, ref T args) where T : IInventoryRelayEvent
     {
-        var containerEnumerator = new ContainerSlotEnumerator(inventory, inventory.Comp.TemplateId, _prototypeManager, this, args.TargetSlots);
+        if (args.TargetSlots == SlotFlags.NONE)
+            return;
 
         // this copies the by-ref event if it is a struct
         var ev = new InventoryRelayedEvent<T>(args);
-
-        while (containerEnumerator.MoveNext(out var container))
+        var enumerator = new InventorySlotEnumerator(inventory, args.TargetSlots);
+        while (enumerator.NextItem(out var item))
         {
-            if (!container.ContainedEntity.HasValue)
-                continue;
-
-            RaiseLocalEvent(container.ContainedEntity.Value, ev);
+            RaiseLocalEvent(item, ev);
         }
 
         // and now we copy it back
@@ -81,40 +78,23 @@ public partial class InventorySystem
         if (args.TargetSlots == SlotFlags.NONE)
             return;
 
-        var containerEnumerator = new ContainerSlotEnumerator(inventory, inventory.Comp.TemplateId, _prototypeManager, this, args.TargetSlots);
         var ev = new InventoryRelayedEvent<T>(args);
-        while (containerEnumerator.MoveNext(out var container))
+        var enumerator = new InventorySlotEnumerator(inventory, args.TargetSlots);
+        while (enumerator.NextItem(out var item))
         {
-            if (!container.ContainedEntity.HasValue)
-                continue;
-
-            RaiseLocalEvent(container.ContainedEntity.Value, ev);
+            RaiseLocalEvent(item, ev);
         }
     }
 
     private void OnGetEquipmentVerbs(EntityUid uid, InventoryComponent component, GetVerbsEvent<EquipmentVerb> args)
     {
         // Automatically relay stripping related verbs to all equipped clothing.
-
-        if (!_prototypeManager.TryIndex(component.TemplateId, out InventoryTemplatePrototype? proto))
-            return;
-
-        if (!TryComp(uid, out ContainerManagerComponent? containers))
-            return;
-
         var ev = new InventoryRelayedEvent<GetVerbsEvent<EquipmentVerb>>(args);
-        foreach (var slotDef in proto.Slots)
+        var enumerator = new InventorySlotEnumerator(component);
+        while (enumerator.NextItem(out var item, out var slotDef))
         {
-            if (slotDef.StripHidden && args.User != uid)
-                continue;
-
-            if (!containers.TryGetContainer(slotDef.Name, out var container))
-                continue;
-
-            if (container is not ContainerSlot slot || slot.ContainedEntity is not { } ent)
-                continue;
-
-            RaiseLocalEvent(ent, ev);
+            if (!slotDef.StripHidden || args.User == uid)
+                RaiseLocalEvent(item, ev);
         }
     }
 
index 49915f186b7f0caa813e239b75a30babef1bf76e..68b659c0e95be32973a43fa1cc18e34ca4394ab6 100644 (file)
@@ -1,6 +1,7 @@
 using System.Diagnostics.CodeAnalysis;
 using Robust.Shared.Containers;
 using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
 
 namespace Content.Shared.Inventory;
 
@@ -28,9 +29,14 @@ public partial class InventorySystem : EntitySystem
         if (!_prototypeManager.TryIndex(component.TemplateId, out InventoryTemplatePrototype? invTemplate))
             return;
 
-        foreach (var slot in invTemplate.Slots)
+        component.Slots = invTemplate.Slots;
+        component.Containers = new ContainerSlot[component.Slots.Length];
+        for (var i = 0; i < component.Containers.Length; i++)
         {
-            _containerSystem.EnsureContainer<ContainerSlot>(uid, slot.Name).OccludesLight = false;
+            var slot = component.Slots[i];
+            var container = _containerSystem.EnsureContainer<ContainerSlot>(uid, slot.Name);
+            container.OccludesLight = false;
+            component.Containers[i] = container;
         }
     }
 
@@ -52,7 +58,8 @@ public partial class InventorySystem : EntitySystem
             return false;
         }
 
-        if (container is not ContainerSlot containerSlotChecked) return false;
+        if (container is not ContainerSlot containerSlotChecked)
+            return false;
 
         containerSlot = containerSlotChecked;
         return true;
@@ -67,12 +74,10 @@ public partial class InventorySystem : EntitySystem
         if (!Resolve(uid, ref inventory, false))
             return false;
 
-        if (!_prototypeManager.TryIndex<InventoryTemplatePrototype>(inventory.TemplateId, out var templatePrototype))
-            return false;
-
-        foreach (var slotDef in templatePrototype.Slots)
+        foreach (var slotDef in inventory.Slots)
         {
-            if (!slotDef.Name.Equals(slot)) continue;
+            if (!slotDef.Name.Equals(slot))
+                continue;
             slotDefinition = slotDef;
             return true;
         }
@@ -80,33 +85,36 @@ public partial class InventorySystem : EntitySystem
         return false;
     }
 
-    public bool TryGetContainerSlotEnumerator(EntityUid uid, out ContainerSlotEnumerator containerSlotEnumerator, InventoryComponent? component = null)
+    public bool TryGetContainerSlotEnumerator(Entity<InventoryComponent?> entity, out InventorySlotEnumerator containerSlotEnumerator, SlotFlags flags = SlotFlags.All)
     {
-        containerSlotEnumerator = default;
-        if (!Resolve(uid, ref component, false))
+        if (!Resolve(entity.Owner, ref entity.Comp))
+        {
+            containerSlotEnumerator = default;
             return false;
+        }
 
-        containerSlotEnumerator = new ContainerSlotEnumerator(uid, component.TemplateId, _prototypeManager, this);
+        containerSlotEnumerator = new InventorySlotEnumerator(entity.Comp, flags);
         return true;
     }
 
-    public bool TryGetSlots(EntityUid uid, [NotNullWhen(true)] out SlotDefinition[]? slotDefinitions, InventoryComponent? inventoryComponent = null)
+    public InventorySlotEnumerator GetSlotEnumerator(Entity<InventoryComponent?> entity, SlotFlags flags = SlotFlags.All)
     {
-        slotDefinitions = null;
-        if (!Resolve(uid, ref inventoryComponent, false))
-            return false;
+        if (!Resolve(entity.Owner, ref entity.Comp))
+            return InventorySlotEnumerator.Empty;
 
-        if (!_prototypeManager.TryIndex<InventoryTemplatePrototype>(inventoryComponent.TemplateId, out var templatePrototype))
-            return false;
-
-        slotDefinitions = templatePrototype.Slots;
-        return true;
+        return new InventorySlotEnumerator(entity.Comp, flags);
     }
 
-    public SlotDefinition[] GetSlots(EntityUid uid, InventoryComponent? inventoryComponent = null)
+    public bool TryGetSlots(EntityUid uid, [NotNullWhen(true)] out SlotDefinition[]? slotDefinitions)
     {
-        if (!Resolve(uid, ref inventoryComponent)) throw new InvalidOperationException();
-        return _prototypeManager.Index<InventoryTemplatePrototype>(inventoryComponent.TemplateId).Slots;
+        if (!TryComp(uid, out InventoryComponent? inv))
+        {
+            slotDefinitions = null;
+            return false;
+        }
+
+        slotDefinitions = inv.Slots;
+        return true;
     }
 
     private ViewVariablesPath? HandleViewVariablesSlots(EntityUid uid, InventoryComponent comp, string relativePath)
@@ -118,48 +126,98 @@ public partial class InventorySystem : EntitySystem
 
     private IEnumerable<string> ListViewVariablesSlots(EntityUid uid, InventoryComponent comp)
     {
-        foreach (var slotDef in GetSlots(uid, comp))
+        foreach (var slotDef in comp.Slots)
         {
             yield return slotDef.Name;
         }
     }
 
-    public struct ContainerSlotEnumerator
+    /// <summary>
+    /// Enumerator for iterating over an inventory's slot containers. Also has methods that skip empty containers.
+    /// It should be safe to add or remove items while enumerating.
+    /// </summary>
+    public struct InventorySlotEnumerator
     {
-        private readonly InventorySystem _inventorySystem;
-        private readonly EntityUid _uid;
         private readonly SlotDefinition[] _slots;
+        private readonly ContainerSlot[] _containers;
         private readonly SlotFlags _flags;
         private int _nextIdx = 0;
+        public static InventorySlotEnumerator Empty = new(Array.Empty<SlotDefinition>(), Array.Empty<ContainerSlot>());
 
-        public ContainerSlotEnumerator(EntityUid uid, string prototypeId, IPrototypeManager prototypeManager, InventorySystem inventorySystem, SlotFlags flags = SlotFlags.All)
+        public InventorySlotEnumerator(InventoryComponent inventory,  SlotFlags flags = SlotFlags.All)
+            : this(inventory.Slots, inventory.Containers, flags)
         {
-            _uid = uid;
-            _inventorySystem = inventorySystem;
-            _flags = flags;
+        }
 
-            if (prototypeManager.TryIndex<InventoryTemplatePrototype>(prototypeId, out var prototype))
-                _slots = prototype.Slots;
-            else
-                _slots = Array.Empty<SlotDefinition>();
+        public InventorySlotEnumerator(SlotDefinition[] slots, ContainerSlot[] containers,  SlotFlags flags = SlotFlags.All)
+        {
+            DebugTools.Assert(flags != SlotFlags.NONE);
+            DebugTools.AssertEqual(slots.Length, containers.Length);
+            _flags = flags;
+            _slots = slots;
+            _containers = containers;
         }
 
         public bool MoveNext([NotNullWhen(true)] out ContainerSlot? container)
         {
+            while (_nextIdx < _slots.Length)
+            {
+                var i = _nextIdx++;
+                var slot = _slots[i];
+
+                if ((slot.SlotFlags & _flags) == 0)
+                    continue;
+
+                container = _containers[i];
+                return true;
+            }
+
             container = null;
+            return false;
+        }
+
+        public bool NextItem(out EntityUid item)
+        {
+            while (_nextIdx < _slots.Length)
+            {
+                var i = _nextIdx++;
+                var slot = _slots[i];
 
+                if ((slot.SlotFlags & _flags) == 0)
+                    continue;
+
+                var container = _containers[i];
+                if (container.ContainedEntity is { } uid)
+                {
+                    item = uid;
+                    return true;
+                }
+            }
+
+            item = default;
+            return false;
+        }
+
+        public bool NextItem(out EntityUid item, [NotNullWhen(true)] out SlotDefinition? slot)
+        {
             while (_nextIdx < _slots.Length)
             {
-                var slot = _slots[_nextIdx];
-                _nextIdx++;
+                var i = _nextIdx++;
+                slot = _slots[i];
 
                 if ((slot.SlotFlags & _flags) == 0)
                     continue;
 
-                if (_inventorySystem.TryGetSlotContainer(_uid, slot.Name, out container, out _))
+                var container = _containers[i];
+                if (container.ContainedEntity is { } uid)
+                {
+                    item = uid;
                     return true;
+                }
             }
 
+            item = default;
+            slot = null;
             return false;
         }
     }
index c7505ab3c0c459e576bf9b39ff83fb30a256dca8..1703db25f3d97ffae2cee82eb2a08c29593e8248 100644 (file)
@@ -42,24 +42,24 @@ public sealed class MagnetPickupSystem : EntitySystem
     public override void Update(float frameTime)
     {
         base.Update(frameTime);
-        var query = EntityQueryEnumerator<MagnetPickupComponent, StorageComponent, TransformComponent>();
+        var query = EntityQueryEnumerator<MagnetPickupComponent, StorageComponent, TransformComponent, MetaDataComponent>();
         var currentTime = _timing.CurTime;
 
-        while (query.MoveNext(out var uid, out var comp, out var storage, out var xform))
+        while (query.MoveNext(out var uid, out var comp, out var storage, out var xform, out var meta))
         {
             if (comp.NextScan > currentTime)
                 continue;
 
             comp.NextScan += ScanDelay;
 
-            // No space
-            if (!_storage.HasSpace((uid, storage)))
+            if (!_inventory.TryGetContainingSlot((uid, xform, meta), out var slotDef))
                 continue;
 
-            if (!_inventory.TryGetContainingSlot(uid, out var slotDef))
+            if ((slotDef.SlotFlags & comp.SlotFlags) == 0x0)
                 continue;
 
-            if ((slotDef.SlotFlags & comp.SlotFlags) == 0x0)
+            // No space
+            if (!_storage.HasSpace((uid, storage)))
                 continue;
 
             var parentUid = xform.ParentUid;
index 0f2a6316448375cf8a4e2c84f30952f426fcdbfb..b9b21a47981db87f413ea125e03539a90a1554ca 100644 (file)
@@ -32,24 +32,19 @@ public partial class SharedGunSystem
     private bool TryGetClothingSlotEntity(EntityUid uid, ClothingSlotAmmoProviderComponent component, [NotNullWhen(true)] out EntityUid? slotEntity)
     {
         slotEntity = null;
-        if (!Containers.TryGetContainingContainer(uid, out var container))
-            return false;
-        var user = container.Owner;
 
-        if (!TryComp<InventoryComponent>(user, out var inventory))
+        if (!_inventory.TryGetContainerSlotEnumerator(uid, out var enumerator, component.TargetSlot))
             return false;
-        var slots = _inventory.GetSlots(user, inventory);
-        foreach (var slot in slots)
+
+        while (enumerator.NextItem(out var item))
         {
-            if (slot.SlotFlags != component.TargetSlot)
-                continue;
-            if (!_inventory.TryGetSlotEntity(user, slot.Name, out var e, inventory))
+            if (component.ProviderWhitelist == null || component.ProviderWhitelist.IsValid(item, EntityManager))
                 continue;
-            if (component.ProviderWhitelist != null && !component.ProviderWhitelist.IsValid(e.Value, EntityManager))
-                continue;
-            slotEntity = e;
+
+            slotEntity = item;
+            return true;
         }
 
-        return slotEntity != null;
+        return false;
     }
 }