From 4e5def0d035625286ad140c20689488e37796f0c Mon Sep 17 00:00:00 2001 From: Kara Date: Fri, 16 Sep 2022 11:46:09 -0700 Subject: [PATCH] Entity storage now holds air (#11355) --- .../Components/EntityStorageComponent.cs | 13 ++- .../InsideEntityStorageComponent.cs | 10 ++ .../EntitySystems/EntityStorageSystem.cs | 96 ++++++++++++++++++- 3 files changed, 112 insertions(+), 7 deletions(-) create mode 100644 Content.Server/Storage/Components/InsideEntityStorageComponent.cs diff --git a/Content.Server/Storage/Components/EntityStorageComponent.cs b/Content.Server/Storage/Components/EntityStorageComponent.cs index 776abb4602e512..05ef1f89b97ff7 100644 --- a/Content.Server/Storage/Components/EntityStorageComponent.cs +++ b/Content.Server/Storage/Components/EntityStorageComponent.cs @@ -1,3 +1,4 @@ +using Content.Server.Atmos; using Content.Shared.Physics; using Content.Shared.Whitelist; using Robust.Shared.Audio; @@ -6,9 +7,10 @@ namespace Content.Server.Storage.Components; [RegisterComponent] -public sealed class EntityStorageComponent : Component +public sealed class EntityStorageComponent : Component, IGasMixtureHolder { public readonly float MaxSize = 1.0f; // maximum width or height of an entity allowed inside the storage. + public const float GasMixVolume = 70f; public static readonly TimeSpan InternalOpenAttemptDelay = TimeSpan.FromSeconds(0.5); public TimeSpan LastInternalOpenAttempt; @@ -35,7 +37,7 @@ public sealed class EntityStorageComponent : Component [DataField("isCollidableWhenOpen")] public bool IsCollidableWhenOpen; - //The offset for where items are emptied/vacuumed for the EntityStorage. + //The offset for where items are emptied/vacuumed for the EntityStorage. [DataField("enteringOffset")] public Vector2 EnteringOffset = new(0, 0); @@ -77,6 +79,13 @@ public sealed class EntityStorageComponent : Component [ViewVariables(VVAccess.ReadWrite)] public bool IsWeldedShut; + + /// + /// Gas currently contained in this entity storage. + /// None while open. Grabs gas from the atmosphere when closed, and exposes any entities inside to it. + /// + [ViewVariables(VVAccess.ReadWrite)] + public GasMixture Air { get; set; } = new (GasMixVolume); } public sealed class InsertIntoEntityStorageAttemptEvent : CancellableEntityEventArgs { } diff --git a/Content.Server/Storage/Components/InsideEntityStorageComponent.cs b/Content.Server/Storage/Components/InsideEntityStorageComponent.cs new file mode 100644 index 00000000000000..4a4ef0f01654d8 --- /dev/null +++ b/Content.Server/Storage/Components/InsideEntityStorageComponent.cs @@ -0,0 +1,10 @@ +namespace Content.Server.Storage.Components; + +/// +/// Added to entities contained within entity storage, for directed event purposes. +/// +[RegisterComponent] +public sealed class InsideEntityStorageComponent : Component +{ + public EntityUid Storage; +} diff --git a/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs b/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs index 221d359f2c00a5..2c86d6e6e4a052 100644 --- a/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs +++ b/Content.Server/Storage/EntitySystems/EntityStorageSystem.cs @@ -1,4 +1,5 @@ using System.Linq; +using Content.Server.Atmos.EntitySystems; using Content.Server.Construction; using Content.Server.Construction.Components; using Content.Server.Popups; @@ -29,6 +30,8 @@ public sealed class EntityStorageSystem : EntitySystem [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; [Dependency] private readonly PlaceableSurfaceSystem _placeableSurface = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly AtmosphereSystem _atmos = default!; + [Dependency] private readonly IMapManager _map = default!; public const string ContainerName = "entity_storage"; @@ -41,6 +44,10 @@ public override void Initialize() SubscribeLocalEvent(OnWeldableAttempt); SubscribeLocalEvent(OnWelded); SubscribeLocalEvent(OnDestroy); + + SubscribeLocalEvent(OnInsideInhale); + SubscribeLocalEvent(OnInsideExhale); + SubscribeLocalEvent(OnInsideExposed); } private void OnInit(EntityUid uid, EntityStorageComponent component, ComponentInit args) @@ -54,6 +61,13 @@ private void OnInit(EntityUid uid, EntityStorageComponent component, ComponentIn if (TryComp(uid, out var placeable)) _placeableSurface.SetPlaceable(uid, component.Open, placeable); + + if (!component.Open) + { + // If we're closed on spawn, we need to pull some air into our environment from where we spawned, + // so that we have -something-. For example, if you bought an animal crate or something. + TakeGas(uid, component); + } } private void OnInteract(EntityUid uid, EntityStorageComponent component, ActivateInWorldEvent args) @@ -117,11 +131,12 @@ public void EmptyContents(EntityUid uid, EntityStorageComponent? component = nul var containedArr = component.Contents.ContainedEntities.ToArray(); foreach (var contained in containedArr) { - if (component.Contents.Remove(contained)) - { - Transform(contained).WorldPosition = - uidXform.WorldPosition + uidXform.WorldRotation.RotateVec(component.EnteringOffset); - } + if (!component.Contents.Remove(contained)) + continue; + + RemComp(contained); + Transform(contained).WorldPosition = + uidXform.WorldPosition + uidXform.WorldRotation.RotateVec(component.EnteringOffset); } } @@ -134,6 +149,7 @@ public void OpenStorage(EntityUid uid, EntityStorageComponent? component = null) EmptyContents(uid, component); ModifyComponents(uid, component); SoundSystem.Play(component.OpenSound.GetSound(), Filter.Pvs(component.Owner), component.Owner); + ReleaseGas(uid, component); RaiseLocalEvent(uid, new StorageAfterOpenEvent()); } @@ -161,11 +177,15 @@ public void CloseStorage(EntityUid uid, EntityStorageComponent? component = null if (!AddToContents(entity, uid, component)) continue; + var inside = EnsureComp(entity); + inside.Storage = uid; + count++; if (count >= component.Capacity) break; } + TakeGas(uid, component); ModifyComponents(uid, component); SoundSystem.Play(component.CloseSound.GetSound(), Filter.Pvs(uid), uid); component.LastInternalOpenAttempt = default; @@ -365,4 +385,70 @@ public void ModifyComponents(EntityUid uid, EntityStorageComponent? component = appearance.SetData(StorageVisuals.HasContents, component.Contents.ContainedEntities.Any()); } } + + private void TakeGas(EntityUid uid, EntityStorageComponent component) + { + var tile = GetOffsetTileRef(uid, component); + + if (tile != null && _atmos.GetTileMixture(tile.Value.GridUid, null, tile.Value.GridIndices, true) is {} environment) + { + _atmos.Merge(component.Air, environment.RemoveVolume(EntityStorageComponent.GasMixVolume)); + } + } + + private void ReleaseGas(EntityUid uid, EntityStorageComponent component) + { + var tile = GetOffsetTileRef(uid, component); + + if (tile != null && _atmos.GetTileMixture(tile.Value.GridUid, null, tile.Value.GridIndices, true) is {} environment) + { + _atmos.Merge(environment, component.Air); + component.Air.Clear(); + } + } + + private TileRef? GetOffsetTileRef(EntityUid uid, EntityStorageComponent component) + { + var targetCoordinates = new EntityCoordinates(uid, component.EnteringOffset).ToMap(EntityManager); + + if (_map.TryFindGridAt(targetCoordinates, out var grid)) + { + return grid.GetTileRef(targetCoordinates); + } + + return null; + } + + #region Gas mix event handlers + + private void OnInsideInhale(EntityUid uid, InsideEntityStorageComponent component, InhaleLocationEvent args) + { + if (TryComp(component.Storage, out var storage)) + { + args.Gas = storage.Air; + } + } + + private void OnInsideExhale(EntityUid uid, InsideEntityStorageComponent component, ExhaleLocationEvent args) + { + if (TryComp(component.Storage, out var storage)) + { + args.Gas = storage.Air; + } + } + + private void OnInsideExposed(EntityUid uid, InsideEntityStorageComponent component, ref AtmosExposedGetAirEvent args) + { + if (args.Handled) + return; + + if (TryComp(component.Storage, out var storage)) + { + args.Gas = storage.Air; + } + + args.Handled = true; + } + + #endregion }