using System.Numerics; using Content.Shared.Atmos; using Content.Shared.Physics; using Content.Shared.Whitelist; using Robust.Shared.Audio; using Robust.Shared.Containers; using Robust.Shared.GameStates; using Robust.Shared.Serialization; namespace Content.Shared.Storage.Components; [RegisterComponent, NetworkedComponent] public sealed partial class EntityStorageComponent : Component, IGasMixtureHolder { public readonly float MaxSize = 1.0f; // maximum width or height of an entity allowed inside the storage. public static readonly TimeSpan InternalOpenAttemptDelay = TimeSpan.FromSeconds(0.5); public TimeSpan NextInternalOpenAttempt; /// /// Collision masks that get removed when the storage gets opened. /// public readonly int MasksToRemove = (int)( CollisionGroup.MidImpassable | CollisionGroup.HighImpassable | CollisionGroup.LowImpassable); /// /// Collision masks that were removed from ANY layer when the storage was opened; /// [DataField] public int RemovedMasks; /// /// The total amount of items that can fit in one entitystorage /// [DataField] public int Capacity = 30; /// /// Whether or not the entity still has collision when open /// [DataField] public bool IsCollidableWhenOpen; /// /// If true, it opens the storage when the entity inside of it moves /// If false, it prevents the storage from opening when the entity inside of it moves. /// This is for objects that you want the player to move while inside, like large cardboard boxes, without opening the storage. /// [DataField] public bool OpenOnMove = true; //The offset for where items are emptied/vacuumed for the EntityStorage. [DataField] public Vector2 EnteringOffset = new(0, 0); //The collision groups checked, so that items are depositied or grabbed from inside walls. [DataField] public CollisionGroup EnteringOffsetCollisionFlags = CollisionGroup.Impassable | CollisionGroup.MidImpassable; /// /// How close you have to be to the "entering" spot to be able to enter /// [DataField] public float EnteringRange = 0.18f; /// /// Whether or not to show the contents when the storage is closed /// [DataField] public bool ShowContents; /// /// Whether or not light is occluded by the storage /// [DataField] public bool OccludesLight = true; /// /// Whether or not all the contents stored should be deleted with the entitystorage /// [DataField] public bool DeleteContentsOnDestruction; /// /// Whether or not the container is sealed and traps air inside of it /// [DataField] public bool Airtight = true; /// /// Whether or not the entitystorage is open or closed /// [DataField] public bool Open; /// /// The sound made when closed /// [DataField] public SoundSpecifier CloseSound = new SoundPathSpecifier("/Audio/Effects/closetclose.ogg"); /// /// The sound made when open /// [DataField] public SoundSpecifier OpenSound = new SoundPathSpecifier("/Audio/Effects/closetopen.ogg"); /// /// Whitelist for what entities are allowed to be inserted into this container. If this is not null, the /// standard requirement that the entity must be an item or mob is waived. /// [DataField] public EntityWhitelist? Whitelist; /// /// The contents of the storage /// [ViewVariables] public Container Contents = default!; /// /// Gas currently contained in this entity storage. /// None while open. Grabs gas from the atmosphere when closed, and exposes any entities inside to it. /// [DataField] public GasMixture Air { get; set; } = new(200); } [Serializable, NetSerializable] public sealed class EntityStorageComponentState : ComponentState { public bool Open; public int Capacity; public bool IsCollidableWhenOpen; public bool OpenOnMove; public float EnteringRange; public TimeSpan NextInternalOpenAttempt; public EntityStorageComponentState(bool open, int capacity, bool isCollidableWhenOpen, bool openOnMove, float enteringRange, TimeSpan nextInternalOpenAttempt) { Open = open; Capacity = capacity; IsCollidableWhenOpen = isCollidableWhenOpen; OpenOnMove = openOnMove; EnteringRange = enteringRange; NextInternalOpenAttempt = nextInternalOpenAttempt; } } /// /// Raised on the entity being inserted whenever checking if an entity can be inserted into an entity storage. /// [ByRefEvent] public record struct InsertIntoEntityStorageAttemptEvent(EntityUid ItemToInsert, bool Cancelled = false); /// /// Raised on the entity storage whenever checking if an entity can be inserted into it. /// [ByRefEvent] public record struct EntityStorageInsertedIntoAttemptEvent(EntityUid ItemToInsert, bool Cancelled = false); /// /// Raised on the Container's owner whenever an entity storage tries to dump its /// contents while within a container. /// [ByRefEvent] public record struct EntityStorageIntoContainerAttemptEvent(BaseContainer Container, bool Cancelled = false); [ByRefEvent] public record struct StorageOpenAttemptEvent(EntityUid User, bool Silent, bool Cancelled = false); [ByRefEvent] public readonly record struct StorageBeforeOpenEvent; [ByRefEvent] public readonly record struct StorageAfterOpenEvent; [ByRefEvent] public record struct StorageCloseAttemptEvent(EntityUid? User, bool Cancelled = false); [ByRefEvent] public readonly record struct StorageBeforeCloseEvent(HashSet Contents, HashSet BypassChecks); [ByRefEvent] public readonly record struct StorageAfterCloseEvent;