diff --git a/Content.Server/Content.Server.csproj b/Content.Server/Content.Server.csproj
index e276965d43..856614d596 100644
--- a/Content.Server/Content.Server.csproj
+++ b/Content.Server/Content.Server.csproj
@@ -27,8 +27,5 @@
-
-
-
diff --git a/Content.Server/_EE/Atmos/AtmosphereSystem.EE.API.cs b/Content.Server/_EE/Atmos/AtmosphereSystem.EE.API.cs
new file mode 100644
index 0000000000..5f2fe4e926
--- /dev/null
+++ b/Content.Server/_EE/Atmos/AtmosphereSystem.EE.API.cs
@@ -0,0 +1,43 @@
+using System.Diagnostics.CodeAnalysis;
+using Content.Server.Atmos.Components;
+using Content.Shared.Atmos;
+
+
+namespace Content.Server.Atmos.EntitySystems;
+
+///
+/// This handles...
+///
+public partial class AtmosphereSystem
+{
+ public TileAtmosphere? GetTileAtmosphere(Entity grid, Vector2i tile)
+ {
+ if (!_atmosQuery.Resolve(grid, ref grid.Comp, false))
+ return null;
+
+ return grid.Comp.Tiles.TryGetValue(tile, out var atmosTile) ? atmosTile : null;
+ }
+
+ public bool TryGetTileAtmosphere(Entity grid, Vector2i tile, [NotNullWhen(true)] out TileAtmosphere? tileAtmosphere)
+ {
+ if (!_atmosQuery.Resolve(grid, ref grid.Comp, false))
+ {
+ tileAtmosphere = null;
+ return false;
+ }
+
+ var success = grid.Comp.Tiles.TryGetValue(tile, out var atmosTile);
+ tileAtmosphere = success ? atmosTile : null;
+ return success;
+ }
+
+ public TileEnumerator GetAdjacentTileAtmospheres(Entity grid, Vector2i tile, bool includeBlocked = false, bool excite = false)
+ {
+ if (!_atmosQuery.Resolve(grid, ref grid.Comp, false))
+ return TileEnumerator.Empty;
+
+ return !grid.Comp.Tiles.TryGetValue(tile, out var atmosTile)
+ ? TileEnumerator.Empty
+ : new(atmosTile.AdjacentTiles);
+ }
+}
diff --git a/Content.Server/_EE/Atmos/TileEnumerator.cs b/Content.Server/_EE/Atmos/TileEnumerator.cs
new file mode 100644
index 0000000000..34f0cf1482
--- /dev/null
+++ b/Content.Server/_EE/Atmos/TileEnumerator.cs
@@ -0,0 +1,31 @@
+using System.Diagnostics.CodeAnalysis;
+using Content.Shared.Atmos;
+
+namespace Content.Server.Atmos;
+
+public struct TileEnumerator
+{
+ public readonly TileAtmosphere?[] Tiles;
+ public int Index = 0;
+
+ public static readonly TileEnumerator Empty = new([]);
+
+ internal TileEnumerator(TileAtmosphere?[] tiles)
+ {
+ Tiles = tiles;
+ }
+
+ public bool MoveNext([NotNullWhen(true)] out TileAtmosphere? tileAtmosphere)
+ {
+ while (Index < Tiles.Length)
+ {
+ tileAtmosphere = Tiles[Index++];
+
+ if (tileAtmosphere != null)
+ return true;
+ }
+
+ tileAtmosphere = null;
+ return false;
+ }
+}
diff --git a/Content.Server/_EE/PressureDestructible/Components/PressureDestructibleComponent.cs b/Content.Server/_EE/PressureDestructible/Components/PressureDestructibleComponent.cs
new file mode 100644
index 0000000000..944ea3e7ff
--- /dev/null
+++ b/Content.Server/_EE/PressureDestructible/Components/PressureDestructibleComponent.cs
@@ -0,0 +1,30 @@
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+
+namespace Content.Server._EE.PressureDestructible.Components;
+
+
+///
+/// This is used in making pressure destroy structures.
+///
+[RegisterComponent]
+public sealed partial class PressureDestructibleComponent : Component
+{
+ ///
+ /// How much pressure could this entity reasonably withstand?
+ ///
+ [DataField]
+ public float MaxPressureDifferential { get; set; }
+
+ ///
+ /// How much damage, as a percentage, will the entity take?
+ ///
+ [DataField]
+ public int Damage { get; set; } = 20;
+
+ [DataField("nextUpdate", customTypeSerializer: typeof(TimeOffsetSerializer))]
+ public TimeSpan NextUpdate { get; set; }
+
+ [DataField]
+ public TimeSpan CheckInterval { get; set; } = TimeSpan.FromSeconds(5);
+}
diff --git a/Content.Server/_EE/PressureDestructible/Components/PressureDestructibleImmuneComponent.cs b/Content.Server/_EE/PressureDestructible/Components/PressureDestructibleImmuneComponent.cs
new file mode 100644
index 0000000000..7889dd3096
--- /dev/null
+++ b/Content.Server/_EE/PressureDestructible/Components/PressureDestructibleImmuneComponent.cs
@@ -0,0 +1,8 @@
+namespace Content.Server._EE.PressureDestructible.Components;
+
+
+///
+/// This is used for setting an entity as immune to pressure destructible.
+///
+[RegisterComponent]
+public sealed partial class PressureDestructibleImmuneComponent : Component;
diff --git a/Content.Server/_EE/PressureDestructible/EntitySystems/PressureDestructibleSystem.cs b/Content.Server/_EE/PressureDestructible/EntitySystems/PressureDestructibleSystem.cs
new file mode 100644
index 0000000000..2502eac9d5
--- /dev/null
+++ b/Content.Server/_EE/PressureDestructible/EntitySystems/PressureDestructibleSystem.cs
@@ -0,0 +1,96 @@
+using Content.Server._EE.PressureDestructible.Components;
+using Content.Server.Atmos;
+using Content.Server.Atmos.EntitySystems;
+using Content.Shared.Atmos;
+using Content.Shared.Damage;
+using Content.Shared.FixedPoint;
+using Robust.Server.GameObjects;
+using Robust.Shared.Timing;
+
+namespace Content.Server._EE.PressureDestructible.EntitySystems;
+
+
+///
+/// This handles destroying entities based on pressure if it has a PressureDestructible component
+///
+public sealed class PressureDestructibleSystem : EntitySystem
+{
+ [Dependency] private TransformSystem _transform = default!;
+ [Dependency] private AtmosphereSystem _atmosphere = default!;
+ [Dependency] private DamageableSystem _damageable = default!;
+ [Dependency] private IGameTiming _gameTiming = default!;
+
+ public override void Update(float frameTime)
+ {
+ base.Update(frameTime);
+
+ var query = EntityManager.EntityQueryEnumerator();
+ while (query.MoveNext(out var uid, out var pressureDestructible, out var damageable))
+ {
+ if (_gameTiming.CurTime < pressureDestructible.NextUpdate || pressureDestructible.MaxPressureDifferential == 0)
+ continue;
+
+ pressureDestructible.NextUpdate = _gameTiming.CurTime + pressureDestructible.CheckInterval;
+
+ if (uid == EntityUid.Invalid || !Exists(uid))
+ continue;
+
+ var grid = _transform.GetGrid(uid);
+ var currentTile = _transform.GetGridOrMapTilePosition(uid);
+
+ if (grid == null || grid == EntityUid.Invalid || !Exists(grid))
+ continue;
+
+ var hasAtmos = _atmosphere.TryGetTileAtmosphere((grid.Value, null), currentTile, out _);
+
+ if (!hasAtmos)
+ continue;
+
+ var adjacentTiles = new HashSet();
+ var directionsToCheck = new[] { AtmosDirection.North, AtmosDirection.East, AtmosDirection.South, AtmosDirection.West };
+
+ foreach (var direction in directionsToCheck)
+ {
+ var adjacentTile = currentTile.Offset(direction);
+
+ if (!_atmosphere.TryGetTileAtmosphere(grid.Value, adjacentTile, out var adjacentAtmos))
+ continue;
+
+ adjacentTiles.Add(adjacentAtmos);
+ }
+
+ var greatestDifference = 0f;
+ TileAtmosphere? largestPressureTile = null;
+
+ foreach (var tileAtmos in adjacentTiles)
+ {
+ var largestPressure = largestPressureTile?.Air?.Pressure ?? 0;
+ var tileMix = _atmosphere.GetTileMixture(grid.Value, null, tileAtmos.GridIndices, true);
+ var tilePressure = tileMix?.Pressure!;
+
+ if (tilePressure == null)
+ return;
+
+ var difference = MathF.Abs(largestPressure - (float) tilePressure);
+
+ if (tilePressure == 0)
+ continue;
+
+ if (difference > greatestDifference)
+ greatestDifference = difference;
+
+ if (tilePressure > largestPressure)
+ largestPressureTile = tileAtmos;
+ }
+
+ if (greatestDifference < pressureDestructible.MaxPressureDifferential || HasComp(uid))
+ continue;
+
+ var damageSpecifier = damageable.Damage;
+ var currentDamage = damageSpecifier["Blunt"];
+
+ damageSpecifier.DamageDict["Blunt"] = currentDamage + pressureDestructible.Damage;
+ _damageable.SetDamage(uid, damageable, damageSpecifier);
+ }
+ }
+}
diff --git a/Resources/Prototypes/Entities/Objects/Misc/inflatable_wall.yml b/Resources/Prototypes/Entities/Objects/Misc/inflatable_wall.yml
index d11aa714cb..9b77cc689d 100644
--- a/Resources/Prototypes/Entities/Objects/Misc/inflatable_wall.yml
+++ b/Resources/Prototypes/Entities/Objects/Misc/inflatable_wall.yml
@@ -5,6 +5,8 @@
components:
- type: Clickable
- type: InteractionOutline
+ - type: PressureDestructible
+ maxPressureDifferential: 12500.0
- type: Sprite
sprite: Objects/Misc/inflatable_wall.rsi
state: inflatable_wall
@@ -47,6 +49,8 @@
components:
- type: Clickable
- type: InteractionOutline
+ - type: PressureDestructible
+ maxPressureDifferential: 12500.0
- type: Sprite
sprite: Objects/Misc/inflatable_door.rsi
state: closed
diff --git a/Resources/Prototypes/Entities/Structures/Windows/plasma.yml b/Resources/Prototypes/Entities/Structures/Windows/plasma.yml
index 1bd0356e95..8afe869344 100644
--- a/Resources/Prototypes/Entities/Structures/Windows/plasma.yml
+++ b/Resources/Prototypes/Entities/Structures/Windows/plasma.yml
@@ -52,6 +52,8 @@
price: 60
- type: RadiationBlocker
resistance: 4
+ - type: PressureDestructible
+ maxPressureDifferential: 3000
- type: entity
id: PlasmaWindowDirectional
diff --git a/Resources/Prototypes/Entities/Structures/Windows/reinforced.yml b/Resources/Prototypes/Entities/Structures/Windows/reinforced.yml
index 9914ad8399..0201a9cca3 100644
--- a/Resources/Prototypes/Entities/Structures/Windows/reinforced.yml
+++ b/Resources/Prototypes/Entities/Structures/Windows/reinforced.yml
@@ -55,6 +55,8 @@
trackAllDamage: true
damageOverlay:
sprite: Structures/Windows/cracks.rsi
+ - type: PressureDestructible
+ maxPressureDifferential: 100000.0
- type: entity
parent: ReinforcedWindow
@@ -134,6 +136,8 @@
acts: [ "Destruction" ]
- type: StaticPrice
price: 22
+ - type: PressureDestructible
+ maxPressureDifferential: 5000.0
- type: entity
parent: ReinforcedWindow
diff --git a/Resources/Prototypes/Entities/Structures/Windows/rplasma.yml b/Resources/Prototypes/Entities/Structures/Windows/rplasma.yml
index 3c35d03a43..35c103c7f0 100644
--- a/Resources/Prototypes/Entities/Structures/Windows/rplasma.yml
+++ b/Resources/Prototypes/Entities/Structures/Windows/rplasma.yml
@@ -55,6 +55,8 @@
sprite: Structures/Windows/cracks.rsi
- type: StaticPrice
price: 132
+ - type: PressureDestructible
+ maxPressureDifferential: 0.0
- type: entity
id: PlasmaReinforcedWindowDirectional
diff --git a/Resources/Prototypes/Entities/Structures/Windows/ruranium.yml b/Resources/Prototypes/Entities/Structures/Windows/ruranium.yml
index 2796c89c1a..f06120d45f 100644
--- a/Resources/Prototypes/Entities/Structures/Windows/ruranium.yml
+++ b/Resources/Prototypes/Entities/Structures/Windows/ruranium.yml
@@ -52,6 +52,8 @@
price: 140
- type: RadiationBlocker
resistance: 10
+ - type: PressureDestructible
+ maxPressureDifferential: 0.0
- type: entity
id: UraniumReinforcedWindowDirectional
@@ -100,6 +102,8 @@
acts: [ "Destruction" ]
- type: StaticPrice
price: 70
+ - type: PressureDestructible
+ maxPressureDifferential: 7500.0
- type: entity
parent: ReinforcedUraniumWindow
diff --git a/Resources/Prototypes/Entities/Structures/Windows/window.yml b/Resources/Prototypes/Entities/Structures/Windows/window.yml
index 224e14e4b2..87e1bb24a4 100644
--- a/Resources/Prototypes/Entities/Structures/Windows/window.yml
+++ b/Resources/Prototypes/Entities/Structures/Windows/window.yml
@@ -91,6 +91,8 @@
allowedVerbs:
- KnockOn
- LickObject
+ - type: PressureDestructible
+ maxPressureDifferential: 2000.0
- type: entity
id: WindowRCDResistant
@@ -194,6 +196,8 @@
allowedVerbs:
- KnockOn
- LickObject
+ - type: PressureDestructible
+ maxPressureDifferential: 3000.0
- type: entity
id: WindowDirectionalRCDResistant