Inventory slot enumerator rejig (#21788)

This commit is contained in:
Leon Friedrich
2023-12-07 16:20:51 -05:00
committed by GitHub
parent 445c474c2c
commit 287d22cc49
18 changed files with 238 additions and 342 deletions

View 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>();
}

View 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)
{

View 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))
if (!Resolve(user.Owner, ref user.Comp2, false))
yield break;
var slotEnumerator = new InventorySlotEnumerator(user.Comp2, flags);
while (slotEnumerator.NextItem(out var item))
{
var slotEnumerator = new ContainerSlotEnumerator(user, inventoryComp.TemplateId,
_prototypeManager, this, flags);
while (slotEnumerator.MoveNext(out var slot))
{
if (slot.ContainedEntity == null)
continue;
yield return slot.ContainedEntity.Value;
}
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)

View 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);
}
}

View 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)
{
container = null;
while (_nextIdx < _slots.Length)
{
var slot = _slots[_nextIdx];
_nextIdx++;
var i = _nextIdx++;
var slot = _slots[i];
if ((slot.SlotFlags & _flags) == 0)
continue;
if (_inventorySystem.TryGetSlotContainer(_uid, slot.Name, out container, out _))
return true;
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 i = _nextIdx++;
slot = _slots[i];
if ((slot.SlotFlags & _flags) == 0)
continue;
var container = _containers[i];
if (container.ContainedEntity is { } uid)
{
item = uid;
return true;
}
}
item = default;
slot = null;
return false;
}
}