#nullable enable using Content.Shared.Stacks; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Prototypes; namespace Content.IntegrationTests.Tests.Interaction; public abstract partial class InteractionTest { /// /// Utility class for working with prototypes ids that may refer to stacks or entities. /// /// /// Intended to make tests easier by removing ambiguity around "SheetSteel1", "SheetSteel", and "Steel". All three /// should be treated identically by interaction tests. /// protected sealed class EntitySpecifier { /// /// Either the stack or entity prototype for this entity. Stack prototypes take priority. /// public string Prototype; /// /// The quantity. If the entity has a stack component, this is the total stack quantity. /// Otherwise this is the number of entities. /// /// /// If used for spawning and this number is larger than the max stack size, only a single stack will be spawned. /// public int Quantity; /// /// If true, a check has been performed to see if the prototype ia an entity prototype with a stack component, /// in which case the specifier was converted into a stack-specifier /// public bool Converted; public EntitySpecifier(string prototype, int quantity, bool converted = false) { Assert.That(quantity, Is.GreaterThan(0)); Prototype = prototype; Quantity = quantity; Converted = converted; } public static implicit operator EntitySpecifier(string prototype) => new(prototype, 1); public static implicit operator EntitySpecifier((string, int) tuple) => new(tuple.Item1, tuple.Item2); /// /// Convert applicable entity prototypes into stack prototypes. /// public void ConvertToStack(IPrototypeManager protoMan, IComponentFactory factory) { if (Converted) return; Converted = true; if (string.IsNullOrWhiteSpace(Prototype)) return; if (protoMan.HasIndex(Prototype)) return; if (!protoMan.TryIndex(Prototype, out var entProto)) { Assert.Fail($"Unknown prototype: {Prototype}"); return; } if (entProto.TryGetComponent(factory.GetComponentName(typeof(StackComponent)), out var stackComp)) { Prototype = stackComp.StackTypeId; } } } protected async Task SpawnEntity(EntitySpecifier spec, EntityCoordinates coords) { EntityUid uid = default!; if (ProtoMan.TryIndex(spec.Prototype, out var stackProto)) { await Server.WaitPost(() => { uid = SEntMan.SpawnEntity(stackProto.Spawn, coords); Stack.SetCount(uid, spec.Quantity); }); return uid; } if (!ProtoMan.TryIndex(spec.Prototype, out var entProto)) { Assert.Fail($"Unkown prototype: {spec.Prototype}"); return default; } if (entProto.TryGetComponent(Factory.GetComponentName(typeof(StackComponent)), out var stackComp)) { return await SpawnEntity((stackComp.StackTypeId, spec.Quantity), coords); } Assert.That(spec.Quantity, Is.EqualTo(1), "SpawnEntity only supports returning a singular entity"); await Server.WaitPost(() => uid = SEntMan.SpawnEntity(spec.Prototype, coords)); return uid; } /// /// Convert an entity-uid to a matching entity specifier. Usefull when doing entity lookups & checking that the /// right quantity of entities/materials werre produced. /// protected EntitySpecifier ToEntitySpecifier(EntityUid uid) { if (SEntMan.TryGetComponent(uid, out StackComponent? stack)) return new EntitySpecifier(stack.StackTypeId, stack.Count) { Converted = true }; var meta = SEntMan.GetComponent(uid); Assert.That(meta.EntityPrototype, Is.Not.Null); return new(meta.EntityPrototype!.ID, 1) { Converted = true }; } }