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);
}
}
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;
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!;
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);
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));
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)
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);
}
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;
{
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);
}
var newData = component.SlotData[slotName] =
new SlotData(component.SlotData[slotName], newHighlight, newBlocked);
- if (owner == _playerManager.LocalPlayer?.ControlledEntity)
+ if (owner == _playerManager.LocalEntity)
EntitySlotUpdate?.Invoke(newData);
}
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));
_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);
}
}
_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;
}
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);
}
}
- 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);
}
}
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,
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,
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,
}
}
- 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;
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;
[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();
return;
}
- var tank = FindBestGasTank(uid, internals);
+ var tank = FindBestGasTank(uid);
if (tank == null)
{
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;
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);
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)
}
}
- 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);
}
}
}
while (query.MoveNext(out var uid, out var stressTest, out var transform))
{
+ if (!transform.ParentUid.IsValid())
+ continue;
+
stressTest.Progress += frameTime;
if (stressTest.Progress > 1)
[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();
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;
-using Robust.Shared.GameStates;
+using Robust.Shared.Containers;
+using Robust.Shared.GameStates;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Inventory;
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>();
}
using Robust.Shared.Player;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
+using Robust.Shared.Utility;
namespace Content.Shared.Inventory;
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;
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;
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))
{
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)
{
/// <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;
}
}
- 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;
/// <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)
using Content.Shared.Strip.Components;
using Content.Shared.Temperature;
using Content.Shared.Verbs;
-using Robust.Shared.Containers;
namespace Content.Shared.Inventory;
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
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);
}
}
using System.Diagnostics.CodeAnalysis;
using Robust.Shared.Containers;
using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
namespace Content.Shared.Inventory;
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;
}
}
return false;
}
- if (container is not ContainerSlot containerSlotChecked) return false;
+ if (container is not ContainerSlot containerSlotChecked)
+ return false;
containerSlot = containerSlotChecked;
return true;
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;
}
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)
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;
}
}
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;
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;
}
}