Inventory slot enumerator rejig (#21788)
This commit is contained in:
@@ -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>();
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user