using System; using System.Diagnostics.CodeAnalysis; using System.Linq; using JetBrains.Annotations; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Map; using Robust.Shared.Utility; namespace Robust.Shared.Containers { /// /// Helper functions for the container system. /// [PublicAPI] public static class ContainerHelpers { /// /// Am I inside a container? Only checks the direct parent. To see if the entity, or any parent entity, is /// inside a container, use /// /// Entity that might be inside a container. /// If the entity is inside of a container. [Obsolete("Use ContainerSystem.IsEntityInContainer() instead")] public static bool IsInContainer(this EntityUid entity, IEntityManager? entMan = null) { IoCManager.Resolve(ref entMan); return (entMan.GetComponent(entity).Flags & MetaDataFlags.InContainer) == MetaDataFlags.InContainer; } /// /// Tries to find the container manager that this entity is inside (if any). /// /// Entity that might be inside a container. /// The container manager that this entity is inside of. /// If a container manager was found. public static bool TryGetContainerMan(this EntityUid entity, [NotNullWhen(true)] out IContainerManager? manager, IEntityManager? entMan = null) { IoCManager.Resolve(ref entMan); DebugTools.Assert(entMan.EntityExists(entity)); var parentTransform = entMan.GetComponent(entity).Parent; if (parentTransform != null && TryGetManagerComp(parentTransform.Owner, out manager, entMan) && manager.ContainsEntity(entity)) return true; manager = default; return false; } /// /// Tries to find the container that this entity is inside (if any). /// /// Entity that might be inside a container. /// The container that this entity is inside of. /// If a container was found. [Obsolete("Use ContainerSystem.TryGetContainingContainer() instead")] public static bool TryGetContainer(this EntityUid entity, [NotNullWhen(true)] out IContainer? container, IEntityManager? entMan = null) { IoCManager.Resolve(ref entMan); DebugTools.Assert(entMan.EntityExists(entity)); if (TryGetContainerMan(entity, out var manager, entMan)) return manager.TryGetContainer(entity, out container); container = default; return false; } /// /// Attempts to remove an entity from its container, if any. /// /// Entity that might be inside a container. /// Whether to forcibly remove the entity from the container. /// Whether the entity was actually inside a container or not. /// If the entity could be removed. Also returns false if it wasn't inside a container. public static bool TryRemoveFromContainer(this EntityUid entity, bool force, out bool wasInContainer, IEntityManager? entMan = null) { IoCManager.Resolve(ref entMan); DebugTools.Assert(entMan.EntityExists(entity)); if (TryGetContainer(entity, out var container, entMan)) { wasInContainer = true; if (!force) return container.Remove(entity, entMan); container.ForceRemove(entity, entMan); return true; } wasInContainer = false; return false; } /// /// Attempts to remove an entity from its container, if any. /// /// Entity that might be inside a container. /// Whether to forcibly remove the entity from the container. /// If the entity could be removed. Also returns false if it wasn't inside a container. public static bool TryRemoveFromContainer(this EntityUid entity, bool force = false, IEntityManager? entMan = null) { return TryRemoveFromContainer(entity, force, out _, entMan); } /// /// Attempts to remove all entities in a container. /// public static void EmptyContainer(this IContainer container, bool force = false, EntityCoordinates? moveTo = null, bool attachToGridOrMap = false, IEntityManager? entMan = null) { IoCManager.Resolve(ref entMan); foreach (var entity in container.ContainedEntities.ToArray()) { if (entMan.Deleted(entity)) continue; if (force) container.ForceRemove(entity, entMan); else container.Remove(entity, entMan); if (moveTo.HasValue) entMan.GetComponent(entity).Coordinates = moveTo.Value; if(attachToGridOrMap) entMan.GetComponent(entity).AttachToGridOrMap(); } } /// /// Attempts to remove and delete all entities in a container. /// public static void CleanContainer(this IContainer container, IEntityManager? entMan = null) { IoCManager.Resolve(ref entMan); foreach (var ent in container.ContainedEntities.ToArray()) { if (entMan.Deleted(ent)) continue; container.ForceRemove(ent, entMan); entMan.DeleteEntity(ent); } } public static void AttachParentToContainerOrGrid(this TransformComponent transform, IEntityManager? entMan = null) { IoCManager.Resolve(ref entMan); if (transform.Parent == null || !TryGetContainer(transform.Parent.Owner, out var container, entMan) || !TryInsertIntoContainer(transform, container, entMan)) transform.AttachToGridOrMap(); } private static bool TryInsertIntoContainer(this TransformComponent transform, IContainer container, IEntityManager? entMan = null) { IoCManager.Resolve(ref entMan); if (container.Insert(transform.Owner, entMan)) return true; if (entMan.GetComponent(container.Owner).Parent != null && TryGetContainer(container.Owner, out var newContainer, entMan)) return TryInsertIntoContainer(transform, newContainer, entMan); return false; } private static bool TryGetManagerComp(this EntityUid entity, [NotNullWhen(true)] out IContainerManager? manager, IEntityManager? entMan = null) { IoCManager.Resolve(ref entMan); DebugTools.Assert(entMan.EntityExists(entity)); if (entMan.TryGetComponent(entity, out manager)) return true; // RECURSION ALERT if (entMan.GetComponent(entity).Parent != null) return TryGetManagerComp(entMan.GetComponent(entity).ParentUid, out manager, entMan); return false; } /// /// Shortcut method to make creation of containers easier. /// Creates a new container on the entity and gives it back to you. /// /// The entity to create the container for. /// /// The new container. /// Thrown if there already is a container with the specified ID. /// [Obsolete("Use ContainerSystem.MakeContainer() instead")] public static T CreateContainer(this EntityUid entity, string containerId, IEntityManager? entMan = null) where T : IContainer { IoCManager.Resolve(ref entMan); var containermanager = entMan.EnsureComponent(entity); return containermanager.MakeContainer(containerId); } [Obsolete("Use ContainerSystem.EnsureContainer() instead")] public static T EnsureContainer(this EntityUid entity, string containerId, IEntityManager? entMan = null) where T : IContainer { IoCManager.Resolve(ref entMan); return EnsureContainer(entity, containerId, out _, entMan); } [Obsolete("Use ContainerSystem.EnsureContainer() instead")] public static T EnsureContainer(this EntityUid entity, string containerId, out bool alreadyExisted, IEntityManager? entMan = null) where T : IContainer { IoCManager.Resolve(ref entMan); var containerManager = entMan.EnsureComponent(entity); if (!containerManager.TryGetContainer(containerId, out var existing)) { alreadyExisted = false; return containerManager.MakeContainer(containerId); } if (!(existing is T container)) { throw new InvalidOperationException( $"The container exists but is of a different type: {existing.GetType()}"); } alreadyExisted = true; return container; } } }