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

@@ -202,17 +202,15 @@ public sealed class ClientClothingSystem : ClothingSystem
revealedLayers.Clear(); 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; 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) || RenderEquipment(uid, item, slot.Name, component, sprite);
!containerSlot.ContainedEntity.HasValue) continue;
RenderEquipment(uid, containerSlot.ContainedEntity.Value, slot.Name, component, sprite);
} }
} }

View File

@@ -1,9 +1,7 @@
using Content.Client.Clothing; using Content.Client.Clothing;
using Content.Client.Examine; using Content.Client.Examine;
using Content.Client.UserInterface.Controls;
using Content.Client.Verbs.UI; using Content.Client.Verbs.UI;
using Content.Shared.Clothing.Components; using Content.Shared.Clothing.Components;
using Content.Shared.Hands.Components;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Interaction.Events; using Content.Shared.Interaction.Events;
using Content.Shared.Inventory; using Content.Shared.Inventory;
@@ -15,14 +13,12 @@ using Robust.Client.UserInterface;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Input.Binding; using Robust.Shared.Input.Binding;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes;
namespace Content.Client.Inventory namespace Content.Client.Inventory
{ {
[UsedImplicitly] [UsedImplicitly]
public sealed class ClientInventorySystem : InventorySystem public sealed class ClientInventorySystem : InventorySystem
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!; [Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IUserInterfaceManager _ui = default!; [Dependency] private readonly IUserInterfaceManager _ui = default!;
@@ -89,7 +85,7 @@ namespace Content.Client.Inventory
private void OnDidUnequip(InventorySlotsComponent component, DidUnequipEvent args) private void OnDidUnequip(InventorySlotsComponent component, DidUnequipEvent args)
{ {
UpdateSlot(args.Equipee, component, args.Slot); UpdateSlot(args.Equipee, component, args.Slot);
if (args.Equipee != _playerManager.LocalPlayer?.ControlledEntity) if (args.Equipee != _playerManager.LocalEntity)
return; return;
var update = new SlotSpriteUpdate(null, args.SlotGroup, args.Slot, false); var update = new SlotSpriteUpdate(null, args.SlotGroup, args.Slot, false);
OnSpriteUpdate?.Invoke(update); OnSpriteUpdate?.Invoke(update);
@@ -98,7 +94,7 @@ namespace Content.Client.Inventory
private void OnDidEquip(InventorySlotsComponent component, DidEquipEvent args) private void OnDidEquip(InventorySlotsComponent component, DidEquipEvent args)
{ {
UpdateSlot(args.Equipee, component, args.Slot); UpdateSlot(args.Equipee, component, args.Slot);
if (args.Equipee != _playerManager.LocalPlayer?.ControlledEntity) if (args.Equipee != _playerManager.LocalEntity)
return; return;
var update = new SlotSpriteUpdate(args.Equipment, args.SlotGroup, args.Slot, var update = new SlotSpriteUpdate(args.Equipment, args.SlotGroup, args.Slot,
HasComp<StorageComponent>(args.Equipment)); HasComp<StorageComponent>(args.Equipment));
@@ -107,10 +103,8 @@ namespace Content.Client.Inventory
private void OnShutdown(EntityUid uid, InventoryComponent component, ComponentShutdown args) private void OnShutdown(EntityUid uid, InventoryComponent component, ComponentShutdown args)
{ {
if (uid != _playerManager.LocalPlayer?.ControlledEntity) if (uid == _playerManager.LocalEntity)
return; OnUnlinkInventory?.Invoke();
OnUnlinkInventory?.Invoke();
} }
private void OnPlayerDetached(EntityUid uid, InventorySlotsComponent component, LocalPlayerDetachedEvent args) private void OnPlayerDetached(EntityUid uid, InventorySlotsComponent component, LocalPlayerDetachedEvent args)
@@ -151,13 +145,10 @@ namespace Content.Client.Inventory
base.OnInit(uid, component, args); base.OnInit(uid, component, args);
_clothingVisualsSystem.InitClothing(uid, component); _clothingVisualsSystem.InitClothing(uid, component);
if (!_prototypeManager.TryIndex(component.TemplateId, out InventoryTemplatePrototype? invTemplate) || if (!TryComp(uid, out InventorySlotsComponent? inventorySlots))
!TryComp(uid, out InventorySlotsComponent? inventorySlots))
{
return; return;
}
foreach (var slot in invTemplate.Slots) foreach (var slot in component.Slots)
{ {
TryAddSlotDef(uid, inventorySlots, slot); TryAddSlotDef(uid, inventorySlots, slot);
} }
@@ -165,7 +156,7 @@ namespace Content.Client.Inventory
public void ReloadInventory(InventorySlotsComponent? component = null) public void ReloadInventory(InventorySlotsComponent? component = null)
{ {
var player = _playerManager.LocalPlayer?.ControlledEntity; var player = _playerManager.LocalEntity;
if (player == null || !Resolve(player.Value, ref component, false)) if (player == null || !Resolve(player.Value, ref component, false))
{ {
return; return;
@@ -179,7 +170,7 @@ namespace Content.Client.Inventory
{ {
var oldData = component.SlotData[slotName]; var oldData = component.SlotData[slotName];
var newData = component.SlotData[slotName] = new SlotData(oldData, state); var newData = component.SlotData[slotName] = new SlotData(oldData, state);
if (owner == _playerManager.LocalPlayer?.ControlledEntity) if (owner == _playerManager.LocalEntity)
EntitySlotUpdate?.Invoke(newData); EntitySlotUpdate?.Invoke(newData);
} }
@@ -198,7 +189,7 @@ namespace Content.Client.Inventory
var newData = component.SlotData[slotName] = var newData = component.SlotData[slotName] =
new SlotData(component.SlotData[slotName], newHighlight, newBlocked); new SlotData(component.SlotData[slotName], newHighlight, newBlocked);
if (owner == _playerManager.LocalPlayer?.ControlledEntity) if (owner == _playerManager.LocalEntity)
EntitySlotUpdate?.Invoke(newData); EntitySlotUpdate?.Invoke(newData);
} }
@@ -208,48 +199,11 @@ namespace Content.Client.Inventory
if (!component.SlotData.TryAdd(newSlotDef.Name, newSlotData)) if (!component.SlotData.TryAdd(newSlotDef.Name, newSlotData))
return false; return false;
if (owner == _playerManager.LocalPlayer?.ControlledEntity) if (owner == _playerManager.LocalEntity)
OnSlotAdded?.Invoke(newSlotData); OnSlotAdded?.Invoke(newSlotData);
return true; 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) public void UIInventoryActivate(string slot)
{ {
EntityManager.RaisePredictiveEvent(new UseSlotNetworkMessage(slot)); EntityManager.RaisePredictiveEvent(new UseSlotNetworkMessage(slot));

View File

@@ -90,11 +90,11 @@ namespace Content.Client.Inventory
_strippingMenu.ClearButtons(); _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); _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)) if (!_inv.TryGetSlotContainer(invUid, slotId, out var container, out var slotDef, inv))
return; return;

View File

@@ -92,9 +92,9 @@ namespace Content.Server.Administration.Commands
} }
var invSystem = entityManager.System<InventorySystem>(); 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); invSystem.TryUnequip(target, slot.Name, true, true, false, inventoryComponent);
var gearStr = startingGear.GetGear(slot.Name, profile); var gearStr = startingGear.GetGear(slot.Name, profile);

View File

@@ -368,15 +368,12 @@ namespace Content.Server.Administration.Systems
} }
} }
if (TryComp(entity.Value, out InventoryComponent? inventory) && if (_inventory.TryGetContainerSlotEnumerator(entity.Value, out var enumerator))
_inventory.TryGetSlots(entity.Value, out var slots, inventory))
{ {
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)) if (_inventory.TryUnequip(entity.Value, entity.Value, slot.Name, true, true))
{ _physics.ApplyAngularImpulse(item, ThrowingSystem.ThrowAngularImpulse);
_physics.ApplyAngularImpulse(item.Value, ThrowingSystem.ThrowAngularImpulse);
}
} }
} }

View File

@@ -285,27 +285,7 @@ public sealed partial class AdminVerbSystem
Text = "Refill Internals Oxygen", Text = "Refill Internals Oxygen",
Category = VerbCategory.Tricks, Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/oxygen.rsi"), "icon"), Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/oxygen.rsi"), "icon"),
Act = () => Act = () => RefillEquippedTanks(args.User, Gas.Oxygen),
{
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);
}
},
Impact = LogImpact.Extreme, Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-internals-refill-oxygen-description"), Message = Loc.GetString("admin-trick-internals-refill-oxygen-description"),
Priority = (int) TricksVerbPriorities.RefillOxygen, Priority = (int) TricksVerbPriorities.RefillOxygen,
@@ -317,27 +297,7 @@ public sealed partial class AdminVerbSystem
Text = "Refill Internals Nitrogen", Text = "Refill Internals Nitrogen",
Category = VerbCategory.Tricks, Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/red.rsi"), "icon"), Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/red.rsi"), "icon"),
Act = () => Act = () =>RefillEquippedTanks(args.User, Gas.Nitrogen),
{
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);
}
},
Impact = LogImpact.Extreme, Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-internals-refill-nitrogen-description"), Message = Loc.GetString("admin-trick-internals-refill-nitrogen-description"),
Priority = (int) TricksVerbPriorities.RefillNitrogen, Priority = (int) TricksVerbPriorities.RefillNitrogen,
@@ -349,27 +309,7 @@ public sealed partial class AdminVerbSystem
Text = "Refill Internals Plasma", Text = "Refill Internals Plasma",
Category = VerbCategory.Tricks, Category = VerbCategory.Tricks,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/plasma.rsi"), "icon"), Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Tanks/plasma.rsi"), "icon"),
Act = () => Act = () => RefillEquippedTanks(args.User, Gas.Plasma),
{
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);
}
},
Impact = LogImpact.Extreme, Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-internals-refill-plasma-description"), Message = Loc.GetString("admin-trick-internals-refill-plasma-description"),
Priority = (int) TricksVerbPriorities.RefillPlasma, 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)
{ {
if (!Resolve(tank, ref tankComponent)) 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, false))
return; return;
var mixSize = tankComponent.Air.Volume; var mixSize = tankComponent.Air.Volume;

View File

@@ -6,6 +6,7 @@ using Content.Server.Popups;
using Content.Shared.Alert; using Content.Shared.Alert;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.DoAfter; using Content.Shared.DoAfter;
using Content.Shared.Hands.Components;
using Content.Shared.Internals; using Content.Shared.Internals;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Verbs; using Content.Shared.Verbs;
@@ -26,6 +27,8 @@ public sealed class InternalsSystem : EntitySystem
[Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
public const SlotFlags InventorySlots = SlotFlags.POCKET | SlotFlags.BELT;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -81,7 +84,7 @@ public sealed class InternalsSystem : EntitySystem
return; return;
} }
var tank = FindBestGasTank(uid, internals); var tank = FindBestGasTank(uid);
if (tank == null) if (tank == null)
{ {
@@ -224,59 +227,35 @@ public sealed class InternalsSystem : EntitySystem
return 1; return 1;
} }
public Entity<GasTankComponent>? FindBestGasTank(EntityUid internalsOwner, InternalsComponent component) public Entity<GasTankComponent>? FindBestGasTank(Entity<HandsComponent?, InventoryComponent?, ContainerManagerComponent?> user)
{ {
// Prioritise // Prioritise
// 1. back equipped tanks // 1. back equipped tanks
// 2. exo-slot tanks // 2. exo-slot tanks
// 3. in-hand tanks // 3. in-hand tanks
// 4. pocket/belt 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) && TryComp<GasTankComponent>(backEntity, out var backGasTank) &&
_gasTank.CanConnectToInternals(backGasTank)) _gasTank.CanConnectToInternals(backGasTank))
{ {
return (backEntity.Value, 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) && TryComp<GasTankComponent>(entity, out var gasTank) &&
_gasTank.CanConnectToInternals(gasTank)) _gasTank.CanConnectToInternals(gasTank))
{ {
return (entity.Value, gasTank); return (entity.Value, gasTank);
} }
var tanks = new List<Entity<GasTankComponent>>(); foreach (var item in _inventory.GetHandOrInventoryEntities((user.Owner, user.Comp1, user.Comp2)))
foreach (var hand in _hands.EnumerateHands(internalsOwner))
{ {
if (TryComp(hand.HeldEntity, out gasTank) && _gasTank.CanConnectToInternals(gasTank)) if (TryComp(item, out gasTank) && _gasTank.CanConnectToInternals(gasTank))
tanks.Add((hand.HeldEntity.Value, gasTank)); return (item, gasTank);
}
if (tanks.Count > 0)
{
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];
}
} }
return null; return null;

View File

@@ -35,15 +35,13 @@ namespace Content.Server.Chemistry.EntitySystems
return; 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)) // TODO add a helper method for this?
{ if (containerEnumerator.MoveNext(out _))
if (!container.ContainedEntity.HasValue) continue;
return; return;
}
} }
var solRemoved = solution.SplitSolution(component.TransferAmount); var solRemoved = solution.SplitSolution(component.TransferAmount);

View File

@@ -25,10 +25,8 @@ namespace Content.Server.Inventory
private void OnExploded(Entity<InventoryComponent> ent, ref BeforeExplodeEvent args) 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 // explode each item in their inventory too
var slots = new InventorySlotEnumerator(ent);
while (slots.MoveNext(out var slot)) while (slots.MoveNext(out var slot))
{ {
if (slot.ContainedEntity != null) 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; return;
Dictionary<string, EntityUid> inventoryEntities = new(); var enumerator = new InventorySlotEnumerator(source.Comp);
var slots = GetSlots(uid); while (enumerator.NextItem(out var item, out var slot))
while (enumerator.MoveNext(out var containerSlot))
{ {
//records all the entities stored in each of the target's slots if (TryUnequip(source, slot.Name, true, true, inventory: source.Comp))
foreach (var slot in slots) TryEquip(target, item, slot.Name , true, true, inventory: target.Comp);
{
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)
{
TryEquip(target, item, slot , true, true);
} }
} }
} }

View File

@@ -26,6 +26,9 @@ public sealed class StressTestMovementSystem : EntitySystem
while (query.MoveNext(out var uid, out var stressTest, out var transform)) while (query.MoveNext(out var uid, out var stressTest, out var transform))
{ {
if (!transform.ParentUid.IsValid())
continue;
stressTest.Progress += frameTime; stressTest.Progress += frameTime;
if (stressTest.Progress > 1) if (stressTest.Progress > 1)

View File

@@ -40,6 +40,16 @@ namespace Content.Server.Zombies
[Dependency] private readonly MobStateSystem _mobState = default!; [Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SharedPopupSystem _popup = 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() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -164,33 +174,27 @@ namespace Content.Server.Zombies
private float GetZombieInfectionChance(EntityUid uid, ZombieComponent component) private float GetZombieInfectionChance(EntityUid uid, ZombieComponent component)
{ {
var baseChance = component.MaxZombieInfectionChance; var max = component.MaxZombieInfectionChance;
if (!TryComp<InventoryComponent>(uid, out var inventoryComponent)) if (!_inventory.TryGetContainerSlotEnumerator(uid, out var enumerator, ProtectiveSlots))
return baseChance; return max;
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 items = 0f; var items = 0f;
var total = 0f; var total = 0f;
while (enumerator.MoveNext(out var con)) while (enumerator.MoveNext(out var con))
{ {
total++; total++;
if (con.ContainedEntity != null) if (con.ContainedEntity != null)
items++; 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; var min = component.MinZombieInfectionChance;
//gets a value between the max and min based on how many items the entity is wearing //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; var chance = (max-min) * ((total - items)/total) + min;

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; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Shared.Inventory; namespace Content.Shared.Inventory;
@@ -11,4 +12,7 @@ public sealed partial class InventoryComponent : Component
public string TemplateId { get; private set; } = "human"; public string TemplateId { get; private set; } = "human";
[DataField("speciesId")] public string? SpeciesId { get; set; } [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.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Shared.Inventory; namespace Content.Shared.Inventory;
@@ -47,12 +48,10 @@ public abstract partial class InventorySystem
protected void QuickEquip(EntityUid uid, ClothingComponent component, UseInHandEvent args) protected void QuickEquip(EntityUid uid, ClothingComponent component, UseInHandEvent args)
{ {
if (!TryComp(args.User, out InventoryComponent? inv) if (!TryComp(args.User, out InventoryComponent? inv) || !HasComp<HandsComponent>(args.User))
|| !TryComp(args.User, out HandsComponent? hands)
|| !_prototypeManager.TryIndex<InventoryTemplatePrototype>(inv.TemplateId, out var prototype))
return; return;
foreach (var slotDef in prototype.Slots) foreach (var slotDef in inv.Slots)
{ {
if (!CanEquip(args.User, uid, slotDef.Name, out _, slotDef, inv)) if (!CanEquip(args.User, uid, slotDef.Name, out _, slotDef, inv))
continue; continue;
@@ -255,6 +254,7 @@ public abstract partial class InventorySystem
if (slotDefinition == null && !TryGetSlot(target, slot, out slotDefinition, inventory: inventory)) if (slotDefinition == null && !TryGetSlot(target, slot, out slotDefinition, inventory: inventory))
return false; return false;
DebugTools.Assert(slotDefinition.Name == slot);
if (slotDefinition.DependsOn != null && !TryGetSlotEntity(target, slotDefinition.DependsOn, out _, inventory)) if (slotDefinition.DependsOn != null && !TryGetSlotEntity(target, slotDefinition.DependsOn, out _, inventory))
return false; return false;
@@ -347,7 +347,8 @@ public abstract partial class InventorySystem
removedItem = slotContainer.ContainedEntity; 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 && !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)) if (!force && !_containerSystem.CanRemove(removedItem.Value, slotContainer))
return false; return false;
foreach (var slotDef in GetSlots(target, inventory)) foreach (var slotDef in inventory.Slots)
{ {
if (slotDef != slotDefinition && slotDef.DependsOn == slotDefinition.Name) if (slotDef != slotDefinition && slotDef.DependsOn == slotDefinition.Name)
{ {

View File

@@ -10,11 +10,11 @@ public partial class InventorySystem
/// <summary> /// <summary>
/// Yields all entities in hands or inventory slots with the specific flags. /// Yields all entities in hands or inventory slots with the specific flags.
/// </summary> /// </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) if (hand.HeldEntity == null)
continue; 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, yield return item;
_prototypeManager, this, flags);
while (slotEnumerator.MoveNext(out var slot))
{
if (slot.ContainedEntity == null)
continue;
yield return slot.ContainedEntity.Value;
}
} }
} }
/// <summary> /// <summary>
/// Returns the definition of the inventory slot that the given entity is currently in.. /// Returns the definition of the inventory slot that the given entity is currently in..
/// </summary> /// </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; slot = null;
return false; return false;
@@ -55,9 +50,10 @@ public partial class InventorySystem
/// <summary> /// <summary>
/// Returns true if the given entity is equipped to an inventory slot with the given inventory slot flags. /// Returns true if the given entity is equipped to an inventory slot with the given inventory slot flags.
/// </summary> /// </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) 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.Strip.Components;
using Content.Shared.Temperature; using Content.Shared.Temperature;
using Content.Shared.Verbs; using Content.Shared.Verbs;
using Robust.Shared.Containers;
namespace Content.Shared.Inventory; 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 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 // this copies the by-ref event if it is a struct
var ev = new InventoryRelayedEvent<T>(args); var ev = new InventoryRelayedEvent<T>(args);
var enumerator = new InventorySlotEnumerator(inventory, args.TargetSlots);
while (containerEnumerator.MoveNext(out var container)) while (enumerator.NextItem(out var item))
{ {
if (!container.ContainedEntity.HasValue) RaiseLocalEvent(item, ev);
continue;
RaiseLocalEvent(container.ContainedEntity.Value, ev);
} }
// and now we copy it back // and now we copy it back
@@ -81,40 +78,23 @@ public partial class InventorySystem
if (args.TargetSlots == SlotFlags.NONE) if (args.TargetSlots == SlotFlags.NONE)
return; return;
var containerEnumerator = new ContainerSlotEnumerator(inventory, inventory.Comp.TemplateId, _prototypeManager, this, args.TargetSlots);
var ev = new InventoryRelayedEvent<T>(args); 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) RaiseLocalEvent(item, ev);
continue;
RaiseLocalEvent(container.ContainedEntity.Value, ev);
} }
} }
private void OnGetEquipmentVerbs(EntityUid uid, InventoryComponent component, GetVerbsEvent<EquipmentVerb> args) private void OnGetEquipmentVerbs(EntityUid uid, InventoryComponent component, GetVerbsEvent<EquipmentVerb> args)
{ {
// Automatically relay stripping related verbs to all equipped clothing. // 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); 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) if (!slotDef.StripHidden || args.User == uid)
continue; RaiseLocalEvent(item, ev);
if (!containers.TryGetContainer(slotDef.Name, out var container))
continue;
if (container is not ContainerSlot slot || slot.ContainedEntity is not { } ent)
continue;
RaiseLocalEvent(ent, ev);
} }
} }

View File

@@ -1,6 +1,7 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Shared.Inventory; namespace Content.Shared.Inventory;
@@ -28,9 +29,14 @@ public partial class InventorySystem : EntitySystem
if (!_prototypeManager.TryIndex(component.TemplateId, out InventoryTemplatePrototype? invTemplate)) if (!_prototypeManager.TryIndex(component.TemplateId, out InventoryTemplatePrototype? invTemplate))
return; 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; return false;
} }
if (container is not ContainerSlot containerSlotChecked) return false; if (container is not ContainerSlot containerSlotChecked)
return false;
containerSlot = containerSlotChecked; containerSlot = containerSlotChecked;
return true; return true;
@@ -67,12 +74,10 @@ public partial class InventorySystem : EntitySystem
if (!Resolve(uid, ref inventory, false)) if (!Resolve(uid, ref inventory, false))
return false; return false;
if (!_prototypeManager.TryIndex<InventoryTemplatePrototype>(inventory.TemplateId, out var templatePrototype)) foreach (var slotDef in inventory.Slots)
return false;
foreach (var slotDef in templatePrototype.Slots)
{ {
if (!slotDef.Name.Equals(slot)) continue; if (!slotDef.Name.Equals(slot))
continue;
slotDefinition = slotDef; slotDefinition = slotDef;
return true; return true;
} }
@@ -80,33 +85,36 @@ public partial class InventorySystem : EntitySystem
return false; 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(entity.Owner, ref entity.Comp))
if (!Resolve(uid, ref component, false)) {
containerSlotEnumerator = default;
return false; return false;
}
containerSlotEnumerator = new ContainerSlotEnumerator(uid, component.TemplateId, _prototypeManager, this); containerSlotEnumerator = new InventorySlotEnumerator(entity.Comp, flags);
return true; 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(entity.Owner, ref entity.Comp))
if (!Resolve(uid, ref inventoryComponent, false)) return InventorySlotEnumerator.Empty;
return false;
if (!_prototypeManager.TryIndex<InventoryTemplatePrototype>(inventoryComponent.TemplateId, out var templatePrototype)) return new InventorySlotEnumerator(entity.Comp, flags);
return false;
slotDefinitions = templatePrototype.Slots;
return true;
} }
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(); if (!TryComp(uid, out InventoryComponent? inv))
return _prototypeManager.Index<InventoryTemplatePrototype>(inventoryComponent.TemplateId).Slots; {
slotDefinitions = null;
return false;
}
slotDefinitions = inv.Slots;
return true;
} }
private ViewVariablesPath? HandleViewVariablesSlots(EntityUid uid, InventoryComponent comp, string relativePath) 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) 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; 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 SlotDefinition[] _slots;
private readonly ContainerSlot[] _containers;
private readonly SlotFlags _flags; private readonly SlotFlags _flags;
private int _nextIdx = 0; 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)) public InventorySlotEnumerator(SlotDefinition[] slots, ContainerSlot[] containers, SlotFlags flags = SlotFlags.All)
_slots = prototype.Slots; {
else DebugTools.Assert(flags != SlotFlags.NONE);
_slots = Array.Empty<SlotDefinition>(); DebugTools.AssertEqual(slots.Length, containers.Length);
_flags = flags;
_slots = slots;
_containers = containers;
} }
public bool MoveNext([NotNullWhen(true)] out ContainerSlot? container) public bool MoveNext([NotNullWhen(true)] out ContainerSlot? container)
{ {
container = null;
while (_nextIdx < _slots.Length) while (_nextIdx < _slots.Length)
{ {
var slot = _slots[_nextIdx]; var i = _nextIdx++;
_nextIdx++; var slot = _slots[i];
if ((slot.SlotFlags & _flags) == 0) if ((slot.SlotFlags & _flags) == 0)
continue; continue;
if (_inventorySystem.TryGetSlotContainer(_uid, slot.Name, out container, out _)) container = _containers[i];
return true; 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; return false;
} }
} }

View File

@@ -42,26 +42,26 @@ public sealed class MagnetPickupSystem : EntitySystem
public override void Update(float frameTime) public override void Update(float frameTime)
{ {
base.Update(frameTime); base.Update(frameTime);
var query = EntityQueryEnumerator<MagnetPickupComponent, StorageComponent, TransformComponent>(); var query = EntityQueryEnumerator<MagnetPickupComponent, StorageComponent, TransformComponent, MetaDataComponent>();
var currentTime = _timing.CurTime; 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) if (comp.NextScan > currentTime)
continue; continue;
comp.NextScan += ScanDelay; comp.NextScan += ScanDelay;
// No space if (!_inventory.TryGetContainingSlot((uid, xform, meta), out var slotDef))
if (!_storage.HasSpace((uid, storage)))
continue;
if (!_inventory.TryGetContainingSlot(uid, out var slotDef))
continue; continue;
if ((slotDef.SlotFlags & comp.SlotFlags) == 0x0) if ((slotDef.SlotFlags & comp.SlotFlags) == 0x0)
continue; continue;
// No space
if (!_storage.HasSpace((uid, storage)))
continue;
var parentUid = xform.ParentUid; var parentUid = xform.ParentUid;
var playedSound = false; var playedSound = false;
var finalCoords = xform.Coordinates; var finalCoords = xform.Coordinates;

View File

@@ -32,24 +32,19 @@ public partial class SharedGunSystem
private bool TryGetClothingSlotEntity(EntityUid uid, ClothingSlotAmmoProviderComponent component, [NotNullWhen(true)] out EntityUid? slotEntity) private bool TryGetClothingSlotEntity(EntityUid uid, ClothingSlotAmmoProviderComponent component, [NotNullWhen(true)] out EntityUid? slotEntity)
{ {
slotEntity = null; 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; return false;
var slots = _inventory.GetSlots(user, inventory);
foreach (var slot in slots) while (enumerator.NextItem(out var item))
{ {
if (slot.SlotFlags != component.TargetSlot) if (component.ProviderWhitelist == null || component.ProviderWhitelist.IsValid(item, EntityManager))
continue; continue;
if (!_inventory.TryGetSlotEntity(user, slot.Name, out var e, inventory))
continue; slotEntity = item;
if (component.ProviderWhitelist != null && !component.ProviderWhitelist.IsValid(e.Value, EntityManager)) return true;
continue;
slotEntity = e;
} }
return slotEntity != null; return false;
} }
} }