From 1245487c9a972ffa14ab3f9fc9701c5b8f663cd8 Mon Sep 17 00:00:00 2001 From: UpAndLeaves <92269094+Alpha-Two@users.noreply.github.com> Date: Thu, 11 Sep 2025 23:07:37 +0100 Subject: [PATCH] storage and inventory toolshed commands (#39046) * First commit * CommandImplementation on singletons is die, a la moony * Fix duplicated thingy because yes * Prototypes, bugfixes, refactoring oh my * Remember to actually stage your ftl changes next time, leaf --- Content.Server/Inventory/InventoryCommand.cs | 198 ++++++++++++++++++ Content.Server/Storage/StorageCommand.cs | 58 +++++ .../en-US/commands/toolshed-commands.ftl | 24 +++ 3 files changed, 280 insertions(+) create mode 100644 Content.Server/Inventory/InventoryCommand.cs create mode 100644 Content.Server/Storage/StorageCommand.cs diff --git a/Content.Server/Inventory/InventoryCommand.cs b/Content.Server/Inventory/InventoryCommand.cs new file mode 100644 index 0000000000..8197889259 --- /dev/null +++ b/Content.Server/Inventory/InventoryCommand.cs @@ -0,0 +1,198 @@ +using System.Linq; +using Content.Server.Administration; +using Content.Shared.Administration; +using Content.Shared.Inventory; +using Robust.Shared.Prototypes; +using Robust.Shared.Toolshed; + +namespace Content.Server.Inventory; + +[ToolshedCommand, AdminCommand(AdminFlags.Debug)] +public sealed class InventoryCommand : ToolshedCommand +{ + private InventorySystem? _inventorySystem; + + [CommandImplementation("getflags")] + public IEnumerable InventoryGetFlags([PipedArgument] IEnumerable ents, SlotFlags slotFlag) + { + var items = Enumerable.Empty(); + foreach (var ent in ents) + { + items = items.Concat(InventoryGetFlags(ent, slotFlag)); + } + + return items; + } + + public IEnumerable InventoryGetFlags(EntityUid ent, SlotFlags slotFlag) + { + _inventorySystem ??= GetSys(); + + if (!EntityManager.TryGetComponent(ent, out var inventory)) + return []; + + List items = new(); + + foreach (var slot in inventory.Slots) + { + if ((slot.SlotFlags & slotFlag) == 0) // Does this seem somewhat illegal? yes. Does C# provide an alternative function for checking if an enum has ANY of a set of bit flags? no. + continue; + if (_inventorySystem.TryGetSlotEntity(ent, slot.Name, out var item, inventory)) + items.Add(item.Value); + } + + return items; + } + + + [CommandImplementation("getnamed")] + public IEnumerable InventoryGetNamed([PipedArgument] IEnumerable ents, string slotName) + { + var items = Enumerable.Empty(); + foreach (var ent in ents) + { + items = items.Concat(InventoryGetNamed(ent, slotName)); + } + + return items; + } + + public IEnumerable InventoryGetNamed(EntityUid ent, string slotName) + { + _inventorySystem ??= GetSys(); + + if (!EntityManager.TryGetComponent(ent, out var inventory)) + return []; + + List items = new(); + + foreach (var slot in inventory.Slots) + { + if (slot.Name != slotName) + continue; + if (_inventorySystem.TryGetSlotEntity(ent, slot.Name, out var item, inventory)) + items.Add(item.Value); + } + + return items; + } + + [CommandImplementation("forceput")] + public EntityUid? InventoryForcePut([PipedArgument] IEnumerable ents, + EntityUid itemEnt, + SlotFlags slotFlag) => InventoryPutEnumerableBase(ents, itemEnt, slotFlag, PutType.ForcePut); + [CommandImplementation("forcespawn")] + public EntityUid? InventoryForceSpawn([PipedArgument] IEnumerable ents, + EntProtoId itemEnt, + SlotFlags slotFlag) => InventorySpawnEnumerableBase(ents, itemEnt, slotFlag, PutType.ForcePut); + + [CommandImplementation("put")] + public EntityUid? InventoryPut([PipedArgument] IEnumerable ents, + EntityUid itemEnt, + SlotFlags slotFlag) => InventoryPutEnumerableBase(ents, itemEnt, slotFlag, PutType.Put); + [CommandImplementation("spawn")] + public EntityUid? InventorySpawn([PipedArgument] IEnumerable ents, + EntProtoId itemEnt, + SlotFlags slotFlag) => InventorySpawnEnumerableBase(ents, itemEnt, slotFlag, PutType.Put); + + [CommandImplementation("tryput")] + public EntityUid? InventoryTryPut([PipedArgument] IEnumerable ents, + EntityUid itemEnt, + SlotFlags slotFlag) => InventoryPutEnumerableBase(ents, itemEnt, slotFlag, PutType.Put); + [CommandImplementation("tryspawn")] + public EntityUid? InventoryTrySpawn([PipedArgument] IEnumerable ents, + EntProtoId itemEnt, + SlotFlags slotFlag) => InventorySpawnEnumerableBase(ents, itemEnt, slotFlag, PutType.Put); + + [CommandImplementation("ensure")] + public EntityUid? InventoryEnsure([PipedArgument] IEnumerable ents, + EntityUid itemEnt, + SlotFlags slotFlag) => InventoryPutEnumerableBase(ents, itemEnt, slotFlag, PutType.Ensure); + [CommandImplementation("ensurespawn")] + public EntityUid? InventoryEnsureSpawn([PipedArgument] IEnumerable ents, + EntProtoId itemEnt, + SlotFlags slotFlag) => InventorySpawnEnumerableBase(ents, itemEnt, slotFlag, PutType.Ensure); + + + private EntityUid? InventorySpawnEnumerableBase(IEnumerable targetEnts, + EntProtoId itemToInsert, + SlotFlags slotFlags, + PutType putType) + { + var entityUids = targetEnts as EntityUid[] ?? targetEnts.ToArray(); + if (!entityUids.Any()) + return null; + + var spawnedItem = Spawn(itemToInsert, Transform(entityUids.First()).Coordinates); + + foreach (var entity in entityUids) + { + var result = InventoryPutBase(entity, spawnedItem, slotFlags, putType); + if (result == null) + continue; + if (!result.Value.Equals(spawnedItem)) Del(spawnedItem); + return result; + } + Del(spawnedItem); + return null; + } + private EntityUid? InventoryPutEnumerableBase(IEnumerable targetEnts, + EntityUid itemToInsert, + SlotFlags slotFlags, + PutType putType) + { + foreach (var entity in targetEnts) + { + var result = InventoryPutBase(entity, itemToInsert, slotFlags, putType); + if (result != null) + return result; + } + + return null; + } + + private EntityUid? InventoryPutBase(EntityUid targetEnt, + EntityUid itemToInsert, + SlotFlags slotFlag, + PutType putType) + { + _inventorySystem ??= GetSys(); + + if (!EntityManager.TryGetComponent(targetEnt, out var inventory)) + return null; + + + foreach (var slot in inventory.Slots) + { + if ((slot.SlotFlags & slotFlag) == 0) + continue; + + + if (_inventorySystem.TryGetSlotEntity(targetEnt, slot.Name, out var originalItem, inventory)) + { + if (putType == PutType.ForcePut) + EntityManager.DeleteEntity(originalItem); + if (putType == PutType.Put) + { + if (!_inventorySystem.TryUnequip(targetEnt, slot.Name, force: true, inventory: inventory)) + return null; + } + } + + if (_inventorySystem.TryEquip(targetEnt, itemToInsert, slot.Name, force: true, inventory: inventory)) + return itemToInsert; + else + return putType == PutType.Ensure ? originalItem : null; + } + + return null; + } + + private enum PutType + { + ForcePut, // Put item in slot, delete old item + Put, // Put item in slot, put old item on floor + TryPut, // Put item in slot, fail if there is already an item + Ensure // Try put item in slot. If there is one, return the item already there + } +} diff --git a/Content.Server/Storage/StorageCommand.cs b/Content.Server/Storage/StorageCommand.cs new file mode 100644 index 0000000000..b7aee26aa9 --- /dev/null +++ b/Content.Server/Storage/StorageCommand.cs @@ -0,0 +1,58 @@ +using System.Linq; +using Content.Server.Administration; +using Content.Shared.Administration; +using Content.Shared.Item; +using Content.Shared.Storage; +using Content.Shared.Storage.EntitySystems; +using Robust.Shared.Containers; +using Robust.Shared.Toolshed; + +namespace Content.Server.Storage; + +[ToolshedCommand, AdminCommand(AdminFlags.Debug)] +public sealed class StorageCommand : ToolshedCommand +{ + private SharedStorageSystem? _storage; + private SharedContainerSystem? _container; + + + [CommandImplementation("insert")] + public IEnumerable StorageInsert([PipedArgument] IEnumerable entsToInsert, + EntityUid targetEnt) => entsToInsert.Where(x => StorageInsert(x, targetEnt) != null); + + public EntityUid? StorageInsert(EntityUid entToInsert, EntityUid targetEnt) + { + _storage ??= GetSys(); + + if (!EntityManager.TryGetComponent(targetEnt, out var storage)) + return null; + + return _storage.Insert(targetEnt, entToInsert, out var stackedEntity, null, storage, false) + ? entToInsert + : null; + } + + + [CommandImplementation("fasttake")] + public IEnumerable StorageFastTake([PipedArgument] IEnumerable storageEnts) => + storageEnts.Select(StorageFastTake).OfType(); + + public EntityUid? StorageFastTake(EntityUid storageEnt) + { + _storage ??= GetSys(); + _container ??= GetSys(); + + + if (!EntityManager.TryGetComponent(storageEnt, out var storage)) + return null; + + var removing = storage.Container.ContainedEntities[^1]; + if (_container.RemoveEntity(storageEnt, removing)) + return removing; + + return null; + } + + + +} diff --git a/Resources/Locale/en-US/commands/toolshed-commands.ftl b/Resources/Locale/en-US/commands/toolshed-commands.ftl index 33bf53f9e3..2e3c395198 100644 --- a/Resources/Locale/en-US/commands/toolshed-commands.ftl +++ b/Resources/Locale/en-US/commands/toolshed-commands.ftl @@ -106,6 +106,30 @@ command-description-scale-multiplyvector = Multiply an entity's sprite size with a certain 2d vector (without changing its fixture). command-description-scale-multiplywithfixture = Multiply an entity's sprite size with a certain factor (including its fixture). +command-description-storage-fasttake = + Takes the most recently placed item from the piped storage entity. +command-description-storage-insert = + Inserts the piped entity into the given storage entity. +command-description-inventory-getflags = + Gets all entities in slots on the piped inventory entity matching a certain slot flag. +command-description-inventory-getnamed = + Gets all entities in slots on the piped inventory entity matching a certain slot name. +command-description-inventory-forceput = + Puts a given entity on the first piped entity that has a slot matching the given flag, deleting any item previously in that slot. +command-description-inventory-forcespawn = + Spawns a given prototype on the first piped entity that has a slot matching the given flag, deleting any item previously in that slot. +command-description-inventory-put = + Puts a given entity on the first piped entity that has a slot matching the given flag, unequiping any item previously in that slot. +command-description-inventory-spawn = + Spawns a given prototype on the first piped entity that has a slot matching the given flag, unequiping any item previously in that slot. +command-description-inventory-tryput = + Tries to put a given entity on the first piped entity that has a slot matching the given flag, failing if any item is in currently in that slot. +command-description-inventory-tryspawn = + Tries to spawn a given prototype on the first piped entity that has a slot matching the given flag, failing if any item is in currently in that slot. +command-description-inventory-ensure = + Puts a given entity on the first piped entity that has a slot matching the given flag if none exists, passing through the UID of whatever is in the slot by the end. +command-description-inventory-ensurespawn = + Spawns a given prototype on the first piped entity that has a slot matching the given flag if none exists, passing through the UID of whatever is in the slot by the end. command-description-dynamicrule-list = Lists all currently active dynamic rules, usually this is just one. command-description-dynamicrule-get =