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
This commit is contained in:
UpAndLeaves
2025-09-11 23:07:37 +01:00
committed by GitHub
parent bb970970c9
commit 1245487c9a
3 changed files with 280 additions and 0 deletions

View File

@@ -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<EntityUid> InventoryGetFlags([PipedArgument] IEnumerable<EntityUid> ents, SlotFlags slotFlag)
{
var items = Enumerable.Empty<EntityUid>();
foreach (var ent in ents)
{
items = items.Concat(InventoryGetFlags(ent, slotFlag));
}
return items;
}
public IEnumerable<EntityUid> InventoryGetFlags(EntityUid ent, SlotFlags slotFlag)
{
_inventorySystem ??= GetSys<InventorySystem>();
if (!EntityManager.TryGetComponent<InventoryComponent>(ent, out var inventory))
return [];
List<EntityUid> 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<EntityUid> InventoryGetNamed([PipedArgument] IEnumerable<EntityUid> ents, string slotName)
{
var items = Enumerable.Empty<EntityUid>();
foreach (var ent in ents)
{
items = items.Concat(InventoryGetNamed(ent, slotName));
}
return items;
}
public IEnumerable<EntityUid> InventoryGetNamed(EntityUid ent, string slotName)
{
_inventorySystem ??= GetSys<InventorySystem>();
if (!EntityManager.TryGetComponent<InventoryComponent>(ent, out var inventory))
return [];
List<EntityUid> 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<EntityUid> ents,
EntityUid itemEnt,
SlotFlags slotFlag) => InventoryPutEnumerableBase(ents, itemEnt, slotFlag, PutType.ForcePut);
[CommandImplementation("forcespawn")]
public EntityUid? InventoryForceSpawn([PipedArgument] IEnumerable<EntityUid> ents,
EntProtoId itemEnt,
SlotFlags slotFlag) => InventorySpawnEnumerableBase(ents, itemEnt, slotFlag, PutType.ForcePut);
[CommandImplementation("put")]
public EntityUid? InventoryPut([PipedArgument] IEnumerable<EntityUid> ents,
EntityUid itemEnt,
SlotFlags slotFlag) => InventoryPutEnumerableBase(ents, itemEnt, slotFlag, PutType.Put);
[CommandImplementation("spawn")]
public EntityUid? InventorySpawn([PipedArgument] IEnumerable<EntityUid> ents,
EntProtoId itemEnt,
SlotFlags slotFlag) => InventorySpawnEnumerableBase(ents, itemEnt, slotFlag, PutType.Put);
[CommandImplementation("tryput")]
public EntityUid? InventoryTryPut([PipedArgument] IEnumerable<EntityUid> ents,
EntityUid itemEnt,
SlotFlags slotFlag) => InventoryPutEnumerableBase(ents, itemEnt, slotFlag, PutType.Put);
[CommandImplementation("tryspawn")]
public EntityUid? InventoryTrySpawn([PipedArgument] IEnumerable<EntityUid> ents,
EntProtoId itemEnt,
SlotFlags slotFlag) => InventorySpawnEnumerableBase(ents, itemEnt, slotFlag, PutType.Put);
[CommandImplementation("ensure")]
public EntityUid? InventoryEnsure([PipedArgument] IEnumerable<EntityUid> ents,
EntityUid itemEnt,
SlotFlags slotFlag) => InventoryPutEnumerableBase(ents, itemEnt, slotFlag, PutType.Ensure);
[CommandImplementation("ensurespawn")]
public EntityUid? InventoryEnsureSpawn([PipedArgument] IEnumerable<EntityUid> ents,
EntProtoId itemEnt,
SlotFlags slotFlag) => InventorySpawnEnumerableBase(ents, itemEnt, slotFlag, PutType.Ensure);
private EntityUid? InventorySpawnEnumerableBase(IEnumerable<EntityUid> 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<EntityUid> 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<InventorySystem>();
if (!EntityManager.TryGetComponent<InventoryComponent>(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
}
}

View File

@@ -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<EntityUid> StorageInsert([PipedArgument] IEnumerable<EntityUid> entsToInsert,
EntityUid targetEnt) => entsToInsert.Where(x => StorageInsert(x, targetEnt) != null);
public EntityUid? StorageInsert(EntityUid entToInsert, EntityUid targetEnt)
{
_storage ??= GetSys<SharedStorageSystem>();
if (!EntityManager.TryGetComponent<StorageComponent>(targetEnt, out var storage))
return null;
return _storage.Insert(targetEnt, entToInsert, out var stackedEntity, null, storage, false)
? entToInsert
: null;
}
[CommandImplementation("fasttake")]
public IEnumerable<EntityUid> StorageFastTake([PipedArgument] IEnumerable<EntityUid> storageEnts) =>
storageEnts.Select(StorageFastTake).OfType<EntityUid>();
public EntityUid? StorageFastTake(EntityUid storageEnt)
{
_storage ??= GetSys<SharedStorageSystem>();
_container ??= GetSys<SharedContainerSystem>();
if (!EntityManager.TryGetComponent<StorageComponent>(storageEnt, out var storage))
return null;
var removing = storage.Container.ContainedEntities[^1];
if (_container.RemoveEntity(storageEnt, removing))
return removing;
return null;
}
}

View File

@@ -106,6 +106,30 @@ command-description-scale-multiplyvector =
Multiply an entity's sprite size with a certain 2d vector (without changing its fixture). Multiply an entity's sprite size with a certain 2d vector (without changing its fixture).
command-description-scale-multiplywithfixture = command-description-scale-multiplywithfixture =
Multiply an entity's sprite size with a certain factor (including its fixture). 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 = command-description-dynamicrule-list =
Lists all currently active dynamic rules, usually this is just one. Lists all currently active dynamic rules, usually this is just one.
command-description-dynamicrule-get = command-description-dynamicrule-get =