diff --git a/Content.Server/Storage/Components/EntityStorageComponent.cs b/Content.Server/Storage/Components/EntityStorageComponent.cs
index 776abb4602..05ef1f89b9 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 @@ using Robust.Shared.Containers;
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 0000000000..4a4ef0f016
--- /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 221d359f2c..2c86d6e6e4 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 sealed class EntityStorageSystem : EntitySystem
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 @@ public sealed class EntityStorageSystem : EntitySystem
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 sealed class EntityStorageSystem : EntitySystem
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 sealed class EntityStorageSystem : EntitySystem
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 sealed class EntityStorageSystem : EntitySystem
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 sealed class EntityStorageSystem : EntitySystem
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
}