diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs
index 9877e7add9..c588b13d0c 100644
--- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs
+++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs
@@ -284,7 +284,7 @@ namespace Content.Server.Atmos.EntitySystems
var otherTile2 = otherTile.AdjacentTiles[k];
if (taker.MonstermosInfo.MoleDelta >= 0) break; // We're done here now. Let's not do more work than needed.
- if (otherTile2 == null || otherTile2.MonstermosInfo.LastQueueCycle != queueCycle) continue;
+ if (otherTile2 == null || otherTile2.AdjacentBits == 0 || otherTile2.MonstermosInfo.LastQueueCycle != queueCycle) continue;
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
_equalizeQueue[queueLength++] = otherTile2;
@@ -342,6 +342,8 @@ namespace Content.Server.Atmos.EntitySystems
var direction = (AtmosDirection) (1 << j);
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
var otherTile2 = otherTile.AdjacentTiles[j]!;
+ if (otherTile2.AdjacentBits == 0)
+ continue;
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
if (otherTile2.Air != null && CompareExchange(otherTile2.Air, tile.Air) == GasCompareResult.NoExchange) continue;
AddActiveTile(gridAtmosphere, otherTile2);
@@ -423,36 +425,61 @@ namespace Content.Server.Atmos.EntitySystems
otherTile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
}
+ // Moving into the room from the breach or airlock
for (var i = 0; i < progressionCount; i++)
{
+ // From a tile exposed to space
var otherTile = _depressurizeProgressionOrder[i];
for (var j = 0; j < Atmospherics.Directions; j++)
{
+ // Flood fill into this new direction
var direction = (AtmosDirection) (1 << j);
// Tiles in _depressurizeProgressionOrder cannot have null air.
if (!otherTile.AdjacentBits.IsFlagSet(direction) && !otherTile.Space) continue;
var tile2 = otherTile.AdjacentTiles[j];
if (tile2?.MonstermosInfo.LastQueueCycle != queueCycle) continue;
DebugTools.Assert(tile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
+ // If flood fill has already reached this tile, continue.
if (tile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
if(tile2.Space) continue;
tile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite();
- tile2.MonstermosInfo.CurrentTransferAmount = 0;
+ tile2.MonstermosInfo.CurrentTransferAmount = 0.0f;
tile2.PressureSpecificTarget = otherTile.PressureSpecificTarget;
tile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow;
_depressurizeProgressionOrder[progressionCount++] = tile2;
}
}
+ // Moving towards the breach from the edges of the flood filled region
for (var i = progressionCount - 1; i >= 0; i--)
{
var otherTile = _depressurizeProgressionOrder[i];
+ if (otherTile?.Air == null) { continue;}
if (otherTile.MonstermosInfo.CurrentTransferDirection == AtmosDirection.Invalid) continue;
gridAtmosphere.HighPressureDelta.Add(otherTile);
AddActiveTile(gridAtmosphere, otherTile);
var otherTile2 = otherTile.AdjacentTiles[otherTile.MonstermosInfo.CurrentTransferDirection.ToIndex()];
- if (otherTile2?.Air == null) continue;
- var sum = otherTile2.Air.TotalMoles;
+ if (otherTile2?.Air == null)
+ {
+ // The tile connecting us to space is spaced already. So just space this tile now.
+ otherTile.Air!.Clear();
+ otherTile.Air.Temperature = Atmospherics.TCMB;
+ continue;
+ }
+
+ // Pressure as a multiple of normal air pressure (takes temperature into account)
+ float pressureMultiple = (otherTile.Air.Pressure / 110.0f);
+ var sum = otherTile.Air.TotalMoles * Atmospherics.SpacingEscapeRatio * pressureMultiple;
+ if (sum < Atmospherics.SpacingMinGas)
+ {
+ // Boost the last bit of air draining from the tile.
+ sum = Math.Min(Atmospherics.SpacingMinGas, otherTile.Air.TotalMoles);
+ }
+ if (sum + otherTile.MonstermosInfo.CurrentTransferAmount > Atmospherics.SpacingMaxWind * pressureMultiple)
+ {
+ // Limit the flow of air out of tiles which have air flowing into them from elsewhere.
+ sum = Math.Max(Atmospherics.SpacingMinGas, Atmospherics.SpacingMaxWind * pressureMultiple - otherTile.MonstermosInfo.CurrentTransferAmount);
+ }
totalMolesRemoved += sum;
otherTile.MonstermosInfo.CurrentTransferAmount += sum;
otherTile2.MonstermosInfo.CurrentTransferAmount += otherTile.MonstermosInfo.CurrentTransferAmount;
@@ -465,16 +492,33 @@ namespace Content.Server.Atmos.EntitySystems
otherTile2.PressureDirection = otherTile.MonstermosInfo.CurrentTransferDirection;
}
+ if (otherTile.Air != null && otherTile.Air.Pressure - sum > Atmospherics.SpacingMinGas * 0.1f)
+ {
+ // Transfer the air into the other tile (space wind :)
+ ReleaseGasTo(otherTile.Air!, otherTile2.Air!, sum);
+ // And then some magically into space
+ ReleaseGasTo(otherTile2.Air!, null, sum * 0.3f);
- // This gas mixture cannot be null, no tile in _depressurizeProgressionOrder can have a null gas mixture
- otherTile.Air!.Clear();
+ if (otherTile.Air.Temperature > 280.0f)
+ {
+ // Temperature reduces as air drains. But nerf the real temperature reduction a bit
+ // Also, limit the temperature loss to remain > 10 Deg.C for convenience
+ float realtemploss = (otherTile.Air.TotalMoles - sum) / otherTile.Air.TotalMoles;
+ otherTile.Air.Temperature *= 0.9f + 0.1f * realtemploss;
+ }
+ }
+ else
+ {
+ // This gas mixture cannot be null, no tile in _depressurizeProgressionOrder can have a null gas mixture
+ otherTile.Air!.Clear();
- // This is a little hacky, but hear me out. It makes sense. We have just vacuumed all of the tile's air
- // therefore there is no more gas in the tile, therefore the tile should be as cold as space!
- otherTile.Air.Temperature = Atmospherics.TCMB;
+ // This is a little hacky, but hear me out. It makes sense. We have just vacuumed all of the tile's air
+ // therefore there is no more gas in the tile, therefore the tile should be as cold as space!
+ otherTile.Air.Temperature = Atmospherics.TCMB;
+ }
InvalidateVisuals(otherTile.GridIndex, otherTile.GridIndices, visuals);
- HandleDecompressionFloorRip(mapGrid, otherTile, sum);
+ HandleDecompressionFloorRip(mapGrid, otherTile, otherTile.MonstermosInfo.CurrentTransferAmount);
}
if (GridImpulse && tileCount > 0)
@@ -488,7 +532,7 @@ namespace Content.Server.Atmos.EntitySystems
_physics.ApplyAngularImpulse(mapGrid.Owner, Vector2.Cross(tile.GridIndices - gridPhysics.LocalCenter, direction) * totalMolesRemoved, body: gridPhysics);
}
- if(tileCount > 10 && (totalMolesRemoved / tileCount) > 20)
+ if(tileCount > 10 && (totalMolesRemoved / tileCount) > 10)
_adminLog.Add(LogType.ExplosiveDepressurization, LogImpact.High,
$"Explosive depressurization removed {totalMolesRemoved} moles from {tileCount} tiles starting from position {tile.GridIndices:position} on grid ID {tile.GridIndex:grid}");
@@ -607,7 +651,7 @@ namespace Content.Server.Atmos.EntitySystems
if (!MonstermosRipTiles)
return;
- var chance = MathHelper.Clamp(sum / 500, 0.005f, 0.5f);
+ var chance = MathHelper.Clamp(0.01f + (sum / Atmospherics.SpacingMaxWind) * 0.3f, 0.003f, 0.3f);
if (sum > 20 && _robustRandom.Prob(chance))
PryTile(mapGrid, tile.GridIndices);
diff --git a/Content.Shared/Atmos/Atmospherics.cs b/Content.Shared/Atmos/Atmospherics.cs
index 293866ed77..478fc01004 100644
--- a/Content.Shared/Atmos/Atmospherics.cs
+++ b/Content.Shared/Atmos/Atmospherics.cs
@@ -309,6 +309,25 @@ namespace Content.Shared.Atmos
///
public const float MaxTransferRate = 200;
+ ///
+ /// What fraction of air from a spaced tile escapes every tick.
+ /// 1.0 for instant spacing, 0.2 means 20% of remaining air lost each time
+ ///
+ public const float SpacingEscapeRatio = 0.05f;
+
+ ///
+ /// Minimum amount of air allowed on a spaced tile before it is reset to 0 immediately in kPa
+ /// Since the decay due to SpacingEscapeRatio follows a curve, it would never reach 0.0 exactly
+ /// unless we truncate it somewhere.
+ ///
+ public const float SpacingMinGas = 2.0f;
+
+ ///
+ /// How much wind can go through a single tile before that tile doesn't depressurize itself
+ /// (I.e spacing is limited in large rooms heading into smaller spaces)
+ ///
+ public const float SpacingMaxWind = 500.0f;
+
#endregion
}