From: ArtisticRoomba <145879011+ArtisticRoomba@users.noreply.github.com>
Date: Tue, 13 Jan 2026 23:06:59 +0000 (-0800)
Subject: AirtightSystem Tests (#42190)
X-Git-Url: https://git.smokeofanarchy.ru/gitweb.cgi?a=commitdiff_plain;h=60e172e12883fc10135a4cf09d42bdfcd0ba0026;p=space-station-14.git
AirtightSystem Tests (#42190)
---
diff --git a/Content.IntegrationTests/Tests/Atmos/AirtightTest.cs b/Content.IntegrationTests/Tests/Atmos/AirtightTest.cs
new file mode 100644
index 0000000000..63e4d9f2d2
--- /dev/null
+++ b/Content.IntegrationTests/Tests/Atmos/AirtightTest.cs
@@ -0,0 +1,585 @@
+using System.Numerics;
+using Content.Server.Atmos.Components;
+using Content.Server.Atmos.EntitySystems;
+using Content.Shared.Atmos;
+using Robust.Shared.GameObjects;
+using Robust.Shared.Map;
+using Robust.Shared.Maths;
+using Robust.Shared.Prototypes;
+using Robust.Shared.Utility;
+
+namespace Content.IntegrationTests.Tests.Atmos;
+
+///
+/// Mega-testclass for testing and .
+///
+[TestOf(typeof(AirtightSystem))]
+[TestOf(typeof(AtmosphereSystem))]
+public sealed class AirtightTest : AtmosTest
+{
+ // Load the same DeltaPressure test because it's quite a useful testmap for testing airtightness.
+ protected override ResPath? TestMapPath => new("Maps/Test/Atmospherics/DeltaPressure/deltapressuretest.yml");
+
+ private readonly EntProtoId _wallProto = new("WallSolid");
+
+ private EntityUid _targetWall = EntityUid.Invalid;
+ private EntityUid _targetRotationEnt = EntityUid.Invalid;
+
+ #region Prototypes
+
+ [TestPrototypes]
+ private const string Prototypes = @"
+- type: entity
+ id: AirtightDirectionalRotationTest
+ parent: WindowDirectional
+ components:
+ - type: Airtight
+ airBlockedDirection: North
+ fixAirBlockedDirectionInitialize: true
+ noAirWhenFullyAirBlocked: false
+";
+
+ #endregion
+
+ #region Component and Helper Assertions
+
+ /*
+ Tests for asserting that proper ComponentInit and other events properly work.
+ */
+
+ [Test]
+ public async Task Component_InitDataCorrect()
+ {
+ // Ensure grid/atmos is initialized.
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ await Server.WaitPost(delegate
+ {
+ var coords = new EntityCoordinates(RelevantAtmos.Owner, Vector2.Zero);
+ _targetWall = SEntMan.SpawnAtPosition(_wallProto, coords);
+ });
+
+ SEntMan.TryGetComponent(_targetWall, out var airtightComp);
+ Assert.That(airtightComp, Is.Not.Null, "Expected spawned wall entity to have AirtightComponent.");
+
+ // The data on the component itself should reflect full blockage.
+ // It should also hold the proper last position.
+ using (Assert.EnterMultipleScope())
+ {
+ Assert.That(airtightComp.AirBlockedDirection, Is.EqualTo(AtmosDirection.All));
+ Assert.That(airtightComp.LastPosition, Is.EqualTo((RelevantAtmos.Owner, Vector2i.Zero)));
+ }
+ }
+
+ [Test]
+ [TestCase(AtmosDirection.North)]
+ [TestCase(AtmosDirection.South)]
+ [TestCase(AtmosDirection.East)]
+ [TestCase(AtmosDirection.West)]
+ public async Task MultiTile_Component_InitDataCorrect(AtmosDirection direction)
+ {
+ // Ensure grid/atmos is initialized.
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ var offsetVec = Vector2i.Zero.Offset(direction);
+ await Server.WaitPost(delegate
+ {
+ var coords = new EntityCoordinates(RelevantAtmos.Owner, offsetVec);
+ _targetWall = SEntMan.SpawnAtPosition(_wallProto, coords);
+ });
+
+ SEntMan.TryGetComponent(_targetWall, out var airtightComp);
+ Assert.That(airtightComp, Is.Not.Null, "Expected spawned wall entity to have AirtightComponent.");
+
+ // The data on the component itself should reflect full blockage.
+ // It should also hold the proper last position.
+ using (Assert.EnterMultipleScope())
+ {
+ Assert.That(airtightComp.AirBlockedDirection, Is.EqualTo(AtmosDirection.All));
+ Assert.That(airtightComp.LastPosition, Is.EqualTo((RelevantAtmos.Owner, offsetVec)));
+ }
+ }
+
+ #endregion
+
+ #region Single Tile Assertion
+
+ /*
+ Tests for asserting single tile airtightness state on both reconstructed and cached data.
+ These tests just spawn a wall in the center and make sure that both reconstructed and cached
+ airtight data reflect the expected states both immediately after the action and after an atmos tick.
+ */
+
+ ///
+ /// Tests that the reconstructed airtight map reflects properly when an airtight entity is spawned.
+ ///
+ [Test]
+ public async Task Spawn_ReconstructedUpdatesImmediately()
+ {
+ // Ensure grid/atmos is initialized.
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ // Before an entity is spawned, the tile in question should be completely unblocked.
+ // This should be reflected in a reconstruction.
+ using (Assert.EnterMultipleScope())
+ {
+ Assert.That(
+ SAtmos.IsTileAirBlocked(ProcessEnt.Owner, Vector2i.Zero, mapGridComp: ProcessEnt.Comp3),
+ Is.False,
+ "Expected no airtightness for reconstructed AirtightData before spawning an airtight entity.");
+ }
+
+ // We cannot use the Spawn InteractionTest helper because it runs ticks,
+ // which invalidate testing for cached data (ticks would update the cache).
+ await Server.WaitPost(delegate
+ {
+ var coords = new EntityCoordinates(RelevantAtmos.Owner, Vector2.Zero);
+ _targetWall = SEntMan.SpawnAtPosition(_wallProto, coords);
+ });
+
+ // Now, immediately after spawn, the reconstructed data should reflect airtightness.
+ Assert.That(
+ SAtmos.IsTileAirBlocked(ProcessEnt.Owner, Vector2i.Zero, mapGridComp: ProcessEnt.Comp3),
+ Is.True,
+ "Expected airtightness for reconstructed AirtightData immediately after spawn.");
+ }
+
+ ///
+ /// Tests that the AirtightData cache updates properly when an airtight entity is spawned.
+ ///
+ [Test]
+ public async Task Spawn_CacheUpdatesOnAtmosTick()
+ {
+ // Ensure grid/atmos is initialized.
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ // Space should be blank before spawn.
+ using (Assert.EnterMultipleScope())
+ {
+ Assert.That(
+ SAtmos.IsTileAirBlockedCached(RelevantAtmos, Vector2i.Zero),
+ Is.False,
+ "Expected cached AirtightData to be unblocked before spawning an airtight entity.");
+
+ var tile = RelevantAtmos.Comp.Tiles[Vector2i.Zero];
+ Assert.That(tile.AdjacentBits,
+ Is.EqualTo(AtmosDirection.All),
+ "Expected tile to be completely unblocked before spawning an airtight entity.");
+
+ Assert.That(tile.AirtightData.BlockedDirections,
+ Is.EqualTo(AtmosDirection.Invalid),
+ "Expected AirtightData to reflect non-airtight state before spawning an airtight entity.");
+
+ for (var i = 0; i < Atmospherics.Directions; i++)
+ {
+ var direction = (AtmosDirection)(1 << i);
+ var curTile = tile.AdjacentTiles[i];
+ Assert.That(curTile, Is.Not.Null, $"Center tile does not hold expected reference to adjacent tile in direction {direction}.");
+ }
+ }
+
+ await Server.WaitPost(delegate
+ {
+ var coords = new EntityCoordinates(RelevantAtmos.Owner, Vector2.Zero);
+ _targetWall = SEntMan.SpawnAtPosition(_wallProto, coords);
+ });
+
+ // Now, immediately after spawn, the reconstructed data should reflect airtightness,
+ // but the cached data should still be stale.
+ // This goes the same for the references, which haven't been updated, as well as the AirtightData.
+ using (Assert.EnterMultipleScope())
+ {
+ Assert.That(
+ SAtmos.IsTileAirBlockedCached(RelevantAtmos, Vector2i.Zero),
+ Is.False,
+ "Expected cached AirtightData to remain stale immediately after spawn before atmos tick.");
+
+ var tile = RelevantAtmos.Comp.Tiles[Vector2i.Zero];
+ Assert.That(tile.AdjacentBits,
+ Is.EqualTo(AtmosDirection.All),
+ "Expected tile to still show non-airtight state before an atmos tick.");
+
+ Assert.That(tile.AirtightData.BlockedDirections,
+ Is.EqualTo(AtmosDirection.Invalid),
+ "Expected AirtightData to reflect non-airtight state after spawn before an atmos tick.");
+
+ for (var i = 0; i < Atmospherics.Directions; i++)
+ {
+ var direction = (AtmosDirection)(1 << i);
+ var curTile = tile.AdjacentTiles[i];
+ Assert.That(curTile, Is.Not.Null, $"Center tile does not hold expected reference to adjacent tile in direction {direction}.");
+ }
+ }
+
+ // Tick to update cache.
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ using (Assert.EnterMultipleScope())
+ {
+ Assert.That(
+ SAtmos.IsTileAirBlocked(ProcessEnt.Owner, Vector2i.Zero, mapGridComp: ProcessEnt.Comp3),
+ Is.True,
+ "Expected airtightness for reconstructed AirtightData after atmos tick.");
+
+ Assert.That(
+ SAtmos.IsTileAirBlockedCached(RelevantAtmos, Vector2i.Zero),
+ Is.True,
+ "Expected cached AirtightData to reflect airtightness after atmos tick.");
+
+ var tile = RelevantAtmos.Comp.Tiles[Vector2i.Zero];
+ Assert.That(tile.AdjacentBits,
+ Is.EqualTo(AtmosDirection.Invalid),
+ "Expected tile to reflect airtight state after atmos tick.");
+
+ Assert.That(tile.AirtightData.BlockedDirections,
+ Is.EqualTo(AtmosDirection.All),
+ "Expected AirtightData to reflect airtight state after spawn before an atmos tick.");
+
+ for (var i = 0; i < Atmospherics.Directions; i++)
+ {
+ var direction = (AtmosDirection)(1 << i);
+ var curTile = tile.AdjacentTiles[i];
+ Assert.That(curTile, Is.Null, $"Center tile holds unexpected reference to adjacent tile in direction {direction}.");
+ }
+ }
+ }
+
+ ///
+ /// Tests that an airtight reconstruction reflects properly after an entity is deleted.
+ ///
+ [Test]
+ public async Task Delete_ReconstructedUpdatesImmediately()
+ {
+ // Ensure grid/atmos is initialized.
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ await Server.WaitPost(delegate
+ {
+ var coords = new EntityCoordinates(RelevantAtmos.Owner, Vector2.Zero);
+ _targetWall = SEntMan.SpawnAtPosition(_wallProto, coords);
+ });
+
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ Assert.That(
+ SAtmos.IsTileAirBlocked(ProcessEnt.Owner, Vector2i.Zero, mapGridComp: ProcessEnt.Comp3),
+ Is.True,
+ "Expected airtightness for reconstructed AirtightData before deletion.");
+
+ await Server.WaitPost(delegate
+ {
+ SEntMan.DeleteEntity(_targetWall);
+ });
+
+ Assert.That(
+ SAtmos.IsTileAirBlocked(ProcessEnt.Owner, Vector2i.Zero, mapGridComp: ProcessEnt.Comp3),
+ Is.False,
+ "Expected no airtightness for reconstructed AirtightData immediately after deletion.");
+
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ Assert.That(
+ SAtmos.IsTileAirBlocked(ProcessEnt.Owner, Vector2i.Zero, mapGridComp: ProcessEnt.Comp3),
+ Is.False,
+ "Expected no airtightness for reconstructed AirtightData after atmos tick.");
+ }
+
+ ///
+ /// Tests that the cached airtight map reflects properly when an entity is deleted
+ ///
+ [Test]
+ public async Task Delete_CacheUpdatesOnAtmosTick()
+ {
+ // Ensure grid/atmos is initialized.
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ await Server.WaitPost(delegate
+ {
+ var coords = new EntityCoordinates(RelevantAtmos.Owner, Vector2.Zero);
+ _targetWall = SEntMan.SpawnAtPosition(_wallProto, coords);
+ });
+
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ await Server.WaitPost(delegate
+ {
+ SEntMan.DeleteEntity(_targetWall);
+ });
+
+ using (Assert.EnterMultipleScope())
+ {
+ Assert.That(
+ SAtmos.IsTileAirBlockedCached(RelevantAtmos, Vector2i.Zero),
+ Is.True,
+ "Expected cached AirtightData to remain stale immediately after deletion before atmos tick.");
+
+ var tile = RelevantAtmos.Comp.Tiles[Vector2i.Zero];
+ Assert.That(tile.AdjacentBits,
+ Is.EqualTo(AtmosDirection.Invalid),
+ "Expected tile to still show airtight state before atmos tick after deletion.");
+
+ Assert.That(tile.AirtightData.BlockedDirections,
+ Is.EqualTo(AtmosDirection.All),
+ "Expected AirtightData to reflect non-airtight state before after deletion before an atmos tick.");
+
+ for (var i = 0; i < Atmospherics.Directions; i++)
+ {
+ var direction = (AtmosDirection)(1 << i);
+ var curTile = tile.AdjacentTiles[i];
+ Assert.That(curTile, Is.Null, $"Center tile holds unexpected reference to adjacent tile in direction {direction}.");
+ }
+ }
+
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ using (Assert.EnterMultipleScope())
+ {
+ Assert.That(
+ SAtmos.IsTileAirBlockedCached(RelevantAtmos, Vector2i.Zero),
+ Is.False,
+ "Expected cached AirtightData to reflect deletion after atmos tick.");
+
+ var tile = RelevantAtmos.Comp.Tiles[Vector2i.Zero];
+ Assert.That(tile.AdjacentBits,
+ Is.EqualTo(AtmosDirection.All),
+ "Expected tile to reflect non-airtight state after atmos tick.");
+
+ Assert.That(tile.AirtightData.BlockedDirections,
+ Is.EqualTo(AtmosDirection.Invalid),
+ "Expected AirtightData to reflect non-airtight state after atmos tick.");
+
+ for (var i = 0; i < Atmospherics.Directions; i++)
+ {
+ var direction = (AtmosDirection)(1 << i);
+ var curTile = tile.AdjacentTiles[i];
+ Assert.That(curTile, Is.Not.Null, $"Center tile does not hold expected reference to adjacent tile in direction {direction}.");
+ }
+ }
+ }
+
+ #endregion
+
+ #region Multi-Tile Assertion
+
+ /*
+ Tests for asserting multi-tile airtightness state on cached data.
+ These tests spawn multiple entities and check that the center unblocked entity
+ properly reflects partial airtightness states.
+
+ Note that reconstruction won't save you in the case where you're surrounded by airtight entities,
+ as those don't show up in the reconstruction. Thus, only cached data tests are done here.
+ */
+
+ ///
+ /// Tests that the cached airtight map reflects properly when airtight entities are spawned
+ /// along the cardinal directions.
+ ///
+ /// The direction to spawn the airtight entity in.
+ [Test]
+ [TestCase(AtmosDirection.North)]
+ [TestCase(AtmosDirection.South)]
+ [TestCase(AtmosDirection.East)]
+ [TestCase(AtmosDirection.West)]
+ public async Task MultiTile_Spawn_CacheUpdatesOnAtmosTick(AtmosDirection atmosDirection)
+ {
+ // Ensure grid/atmos is initialized.
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ // Tile should be completely unblocked.
+ using (Assert.EnterMultipleScope())
+ {
+ var tile = RelevantAtmos.Comp.Tiles[Vector2i.Zero];
+ Assert.That(tile.AdjacentBits,
+ Is.EqualTo(AtmosDirection.All),
+ "Expected tile to be completely unblocked before spawning an airtight entity.");
+
+ for (var i = 0; i < Atmospherics.Directions; i++)
+ {
+ var direction = (AtmosDirection)(1 << i);
+ var curTile = tile.AdjacentTiles[i];
+ Assert.That(curTile, Is.Not.Null, $"Center tile does not hold expected reference to adjacent tile in direction {direction}.");
+ }
+ }
+
+ await Server.WaitPost(delegate
+ {
+ var offsetVec = Vector2i.Zero.Offset(atmosDirection);
+ var coords = new EntityCoordinates(RelevantAtmos.Owner, offsetVec);
+ _targetWall = SEntMan.SpawnAtPosition(_wallProto, coords);
+ });
+
+ using (Assert.EnterMultipleScope())
+ {
+ var tile = RelevantAtmos.Comp.Tiles[Vector2i.Zero];
+ Assert.That(tile.AdjacentBits,
+ Is.EqualTo(AtmosDirection.All),
+ "Expected tile to still show non-airtight state before an atmos tick.");
+
+ for (var i = 0; i < Atmospherics.Directions; i++)
+ {
+ var direction = (AtmosDirection)(1 << i);
+ var curTile = tile.AdjacentTiles[i];
+ Assert.That(curTile, Is.Not.Null, $"Center tile does not hold expected reference to adjacent tile in direction {direction}.");
+ }
+ }
+
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ using (Assert.EnterMultipleScope())
+ {
+ var tile = RelevantAtmos.Comp.Tiles[Vector2i.Zero];
+ Assert.That(tile.AdjacentBits,
+ Is.EqualTo(AtmosDirection.All & ~atmosDirection),
+ "Expected tile to reflect airtight state after atmos tick.");
+
+ for (var i = 0; i < Atmospherics.Directions; i++)
+ {
+ var direction = (AtmosDirection)(1 << i);
+ var curTile = tile.AdjacentTiles[i];
+ if (direction == atmosDirection)
+ {
+ Assert.That(curTile, Is.Null, $"Center tile holds unexpected reference to adjacent tile in direction {direction}.");
+ }
+ else
+ {
+ Assert.That(curTile, Is.Not.Null, $"Center tile does not hold expected reference to adjacent tile in direction {direction}.");
+ }
+ }
+ }
+ }
+
+ ///
+ /// Tests that the cached airtight map reflects properly when an airtight entity is deleted
+ /// along a cardinal direction.
+ ///
+ /// The direction the airtight entity is spawned and then deleted in.
+ [Test]
+ [TestCase(AtmosDirection.North)]
+ [TestCase(AtmosDirection.South)]
+ [TestCase(AtmosDirection.East)]
+ [TestCase(AtmosDirection.West)]
+ public async Task MultiTile_Delete_CacheUpdatesOnAtmosTick(AtmosDirection atmosDirection)
+ {
+ // Ensure grid/atmos is initialized.
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ await Server.WaitPost(delegate
+ {
+ var offsetVec = Vector2i.Zero.Offset(atmosDirection);
+ var coords = new EntityCoordinates(RelevantAtmos.Owner, offsetVec);
+ _targetWall = SEntMan.SpawnAtPosition(_wallProto, coords);
+ });
+
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ await Server.WaitPost(delegate
+ {
+ SEntMan.DeleteEntity(_targetWall);
+ });
+
+ using (Assert.EnterMultipleScope())
+ {
+ var tile = RelevantAtmos.Comp.Tiles[Vector2i.Zero];
+ Assert.That(tile.AdjacentBits,
+ Is.EqualTo(AtmosDirection.All & ~atmosDirection),
+ "Expected tile to remain stale immediately after deletion before an atmos tick.");
+
+ for (var i = 0; i < Atmospherics.Directions; i++)
+ {
+ var direction = (AtmosDirection)(1 << i);
+ var curTile = tile.AdjacentTiles[i];
+ if (direction == atmosDirection)
+ {
+ Assert.That(curTile, Is.Null, $"Center tile holds unexpected reference to adjacent tile in direction {direction}.");
+ }
+ else
+ {
+ Assert.That(curTile, Is.Not.Null, $"Center tile does not hold expected reference to adjacent tile in direction {direction}.");
+ }
+ }
+ }
+
+ // Tick to update cache after deletion.
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ using (Assert.EnterMultipleScope())
+ {
+ var tile = RelevantAtmos.Comp.Tiles[Vector2i.Zero];
+ Assert.That(tile.AdjacentBits,
+ Is.EqualTo(AtmosDirection.All),
+ "Expected tile to reflect non-airtight state after deletion after atmos tick.");
+
+ for (var i = 0; i < Atmospherics.Directions; i++)
+ {
+ var direction = (AtmosDirection)(1 << i);
+ var curTile = tile.AdjacentTiles[i];
+ Assert.That(curTile, Is.Not.Null, $"Center tile does not hold expected reference to adjacent tile in direction {direction}.");
+ }
+ }
+ }
+
+ #endregion
+
+ #region Rotation Assertion
+
+ ///
+ /// Asserts that an airtight entity with a directional air blocked direction
+ /// properly reflects rotation on spawn.
+ ///
+ /// The degrees to rotate the entity on spawn.
+ /// The expected blocked direction after rotation.
+ /// Yeah, so here I learned that RT handles rotation directions
+ /// as positive == counterclockwise.
+ [Test]
+ [TestCase(0f, AtmosDirection.North)]
+ [TestCase(90f, AtmosDirection.West)]
+ [TestCase(180f, AtmosDirection.South)]
+ [TestCase(270f, AtmosDirection.East)]
+ [TestCase(-90f, AtmosDirection.East)]
+ [TestCase(-180f, AtmosDirection.South)]
+ [TestCase(-270f, AtmosDirection.West)]
+ public async Task Rotation_AirBlockedDirectionsOnSpawn(float degrees, AtmosDirection expected)
+ {
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ var rotation = Angle.FromDegrees(degrees);
+
+ await Server.WaitPost(delegate
+ {
+ var coords = new EntityCoordinates(RelevantAtmos.Owner, Vector2.Zero);
+ _targetRotationEnt = SEntMan.SpawnAtPosition("AirtightDirectionalRotationTest", coords);
+
+ Transform.SetLocalRotation(_targetRotationEnt, rotation);
+ });
+
+ SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
+
+ await Server.WaitAssertion(delegate
+ {
+ using (Assert.EnterMultipleScope())
+ {
+ SEntMan.TryGetComponent(_targetRotationEnt, out var airtight);
+ Assert.That(airtight, Is.Not.Null);
+
+ var initial = (AtmosDirection)airtight.InitialAirBlockedDirection;
+ Assert.That(initial,
+ Is.EqualTo(AtmosDirection.North),
+ "Directional airtight entity should block North on spawn.");
+
+ Assert.That(airtight.AirBlockedDirection,
+ Is.EqualTo(expected),
+ $"Expected AirBlockedDirection to be {expected} after rotating by {degrees} degrees on spawn.");
+
+ // i dont trust you airtightsystem
+ if (degrees is 90f or 270f)
+ {
+ Assert.That(expected,
+ Is.Not.EqualTo(initial),
+ "Rotated directions should differ for 90/270 degrees.");
+ }
+ }
+ });
+ }
+
+ #endregion
+}
diff --git a/Content.Server/Atmos/Components/AirtightComponent.cs b/Content.Server/Atmos/Components/AirtightComponent.cs
index 69488a7130..29c1f18af3 100644
--- a/Content.Server/Atmos/Components/AirtightComponent.cs
+++ b/Content.Server/Atmos/Components/AirtightComponent.cs
@@ -40,14 +40,23 @@ namespace Content.Server.Atmos.Components
// depressurizing a room. However it can also effectively be used as a means of generating gasses for free
// TODO ATMOS Mass conservation. Make it actually push/pull air from adjacent tiles instead of destroying & creating,
-
- // TODO ATMOS Do we need these two fields?
+ // TODO ATMOS slate for removal. Stuff doesn't use this.
[DataField("rotateAirBlocked")]
public bool RotateAirBlocked { get; set; } = true;
- // TODO ATMOS remove this? What is this even for??
- [DataField("fixAirBlockedDirectionInitialize")]
- public bool FixAirBlockedDirectionInitialize { get; set; } = true;
+ ///
+ /// Whether to fix the on initialization
+ /// to the entity's current rotation.
+ ///
+ /// This is an optimization routine for initializing airtight components.
+ /// If this entity doesn't have unique airtight directions
+ /// (ex. not all directions are blocked but some are), we can skip
+ /// a lot of event/transform business during initialization.
+ /// This field marks whether this is skipped or not.
+ /// If your entity only blocks air in one direction,
+ /// and that can depend on rotation, this needs to be set to true.
+ [DataField]
+ public bool FixAirBlockedDirectionInitialize = true;
///
/// If true, then the tile that this entity is on will have no air at all if all directions are blocked.
diff --git a/Content.Server/Atmos/EntitySystems/AirtightSystem.cs b/Content.Server/Atmos/EntitySystems/AirtightSystem.cs
index 431707c835..78f61c80a4 100644
--- a/Content.Server/Atmos/EntitySystems/AirtightSystem.cs
+++ b/Content.Server/Atmos/EntitySystems/AirtightSystem.cs
@@ -25,7 +25,10 @@ namespace Content.Server.Atmos.EntitySystems
private void OnAirtightInit(Entity airtight, ref ComponentInit args)
{
- // TODO AIRTIGHT what FixAirBlockedDirectionInitialize even for?
+ // If this entity has unique airtight directions that are affected by rotation,
+ // we need to fix up the current airtight directions based on its rotation.
+ // Otherwise, we can skip all of that logic (stuff adds up when you're initing
+ // a morbillion walls).
if (!airtight.Comp.FixAirBlockedDirectionInitialize)
{
UpdatePosition(airtight);
diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs
index 5cce6075b7..e2e39abdda 100644
--- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs
+++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs
@@ -300,6 +300,8 @@ public partial class AtmosphereSystem
///
/// Checks if a tile on a grid is air-blocked in the specified directions.
+ /// This only checks for if the current tile, and only the current tile, is blocking
+ /// air.
///
/// The grid to check.
/// The tile on the grid to check.
@@ -323,6 +325,8 @@ public partial class AtmosphereSystem
///
/// Checks if a tile on a grid is air-blocked in the specified directions, using cached data.
+ /// This only checks for if the current tile, and only the current tile, is blocking
+ /// air.
///
/// The grid to check.
/// The tile on the grid to check.