2021-07-20 18:03:49 +02:00
using Content.Server.Atmos.Components ;
2022-09-06 17:55:33 +12:00
using Content.Server.Doors.Systems ;
2023-05-06 17:54:36 +12:00
using Content.Shared.Doors.Components ;
2021-07-20 18:03:49 +02:00
using Content.Shared.Atmos ;
2022-12-19 08:25:27 +13:00
using Content.Shared.Atmos.Components ;
2021-11-28 14:56:53 +01:00
using Content.Shared.Database ;
2021-07-23 11:09:01 +02:00
using Robust.Shared.Map ;
2022-12-19 08:25:27 +13:00
using Robust.Shared.Map.Components ;
2022-09-14 17:26:26 +10:00
using Robust.Shared.Physics.Components ;
2021-07-20 18:03:49 +02:00
using Robust.Shared.Random ;
2021-08-02 17:03:13 +02:00
using Robust.Shared.Utility ;
2022-10-17 04:50:11 +13:00
using System.Linq ;
2023-07-08 14:08:32 +10:00
using System.Numerics ;
2021-07-20 18:03:49 +02:00
namespace Content.Server.Atmos.EntitySystems
{
2022-02-16 00:23:23 -07:00
public sealed partial class AtmosphereSystem
2021-07-20 18:03:49 +02:00
{
2022-09-06 17:55:33 +12:00
[Dependency] private readonly FirelockSystem _firelockSystem = default ! ;
2021-07-20 18:03:49 +02:00
private readonly TileAtmosphereComparer _monstermosComparer = new ( ) ;
2021-08-02 17:03:13 +02:00
private readonly TileAtmosphere ? [ ] _equalizeTiles = new TileAtmosphere [ Atmospherics . MonstermosHardTileLimit ] ;
2021-07-24 16:33:04 +02:00
private readonly TileAtmosphere [ ] _equalizeGiverTiles = new TileAtmosphere [ Atmospherics . MonstermosTileLimit ] ;
private readonly TileAtmosphere [ ] _equalizeTakerTiles = new TileAtmosphere [ Atmospherics . MonstermosTileLimit ] ;
private readonly TileAtmosphere [ ] _equalizeQueue = new TileAtmosphere [ Atmospherics . MonstermosTileLimit ] ;
private readonly TileAtmosphere [ ] _depressurizeTiles = new TileAtmosphere [ Atmospherics . MonstermosHardTileLimit ] ;
private readonly TileAtmosphere [ ] _depressurizeSpaceTiles = new TileAtmosphere [ Atmospherics . MonstermosHardTileLimit ] ;
private readonly TileAtmosphere [ ] _depressurizeProgressionOrder = new TileAtmosphere [ Atmospherics . MonstermosHardTileLimit * 2 ] ;
2022-12-19 08:25:27 +13:00
private void EqualizePressureInZone ( MapGridComponent mapGrid , GridAtmosphereComponent gridAtmosphere , TileAtmosphere tile , int cycleNum , GasTileOverlayComponent ? visuals )
2021-07-20 18:03:49 +02:00
{
if ( tile . Air = = null | | ( tile . MonstermosInfo . LastCycle > = cycleNum ) )
return ; // Already done.
tile . MonstermosInfo = new MonstermosInfo ( ) ;
var startingMoles = tile . Air . TotalMoles ;
var runAtmos = false ;
// We need to figure if this is necessary
for ( var i = 0 ; i < Atmospherics . Directions ; i + + )
{
var direction = ( AtmosDirection ) ( 1 < < i ) ;
if ( ! tile . AdjacentBits . IsFlagSet ( direction ) ) continue ;
var other = tile . AdjacentTiles [ i ] ;
if ( other ? . Air = = null ) continue ;
var comparisonMoles = other . Air . TotalMoles ;
if ( ! ( MathF . Abs ( comparisonMoles - startingMoles ) > Atmospherics . MinimumMolesDeltaToMove ) ) continue ;
runAtmos = true ;
break ;
}
if ( ! runAtmos ) // There's no need so we don't bother.
{
tile . MonstermosInfo . LastCycle = cycleNum ;
return ;
}
var queueCycle = + + gridAtmosphere . EqualizationQueueCycleControl ;
var totalMoles = 0f ;
2021-07-24 16:33:04 +02:00
_equalizeTiles [ 0 ] = tile ;
2021-07-20 18:03:49 +02:00
tile . MonstermosInfo . LastQueueCycle = queueCycle ;
var tileCount = 1 ;
for ( var i = 0 ; i < tileCount ; i + + )
{
if ( i > Atmospherics . MonstermosHardTileLimit ) break ;
2021-08-02 17:03:13 +02:00
var exploring = _equalizeTiles [ i ] ! ;
2021-07-20 18:03:49 +02:00
if ( i < Atmospherics . MonstermosTileLimit )
{
2021-08-02 17:03:13 +02:00
// Tiles in the _equalizeTiles array cannot have null air.
var tileMoles = exploring . Air ! . TotalMoles ;
2021-07-20 18:03:49 +02:00
exploring . MonstermosInfo . MoleDelta = tileMoles ;
totalMoles + = tileMoles ;
}
for ( var j = 0 ; j < Atmospherics . Directions ; j + + )
{
var direction = ( AtmosDirection ) ( 1 < < j ) ;
if ( ! exploring . AdjacentBits . IsFlagSet ( direction ) ) continue ;
var adj = exploring . AdjacentTiles [ j ] ;
if ( adj ? . Air = = null ) continue ;
if ( adj . MonstermosInfo . LastQueueCycle = = queueCycle ) continue ;
adj . MonstermosInfo = new MonstermosInfo { LastQueueCycle = queueCycle } ;
if ( tileCount < Atmospherics . MonstermosHardTileLimit )
2021-07-24 16:33:04 +02:00
_equalizeTiles [ tileCount + + ] = adj ;
2021-07-20 18:03:49 +02:00
2022-07-04 16:51:34 +02:00
if ( adj . Space & & MonstermosDepressurization )
2021-07-20 18:03:49 +02:00
{
// Looks like someone opened an airlock to space!
2021-07-23 11:09:01 +02:00
2022-12-19 08:25:27 +13:00
ExplosivelyDepressurize ( mapGrid , gridAtmosphere , tile , cycleNum , visuals ) ;
2021-07-20 18:03:49 +02:00
return ;
}
}
}
if ( tileCount > Atmospherics . MonstermosTileLimit )
{
for ( var i = Atmospherics . MonstermosTileLimit ; i < tileCount ; i + + )
{
//We unmark them. We shouldn't be pushing/pulling gases to/from them.
2021-07-24 16:33:04 +02:00
var otherTile = _equalizeTiles [ i ] ;
2021-07-20 18:03:49 +02:00
if ( otherTile = = null )
continue ;
2021-08-02 17:03:13 +02:00
otherTile . MonstermosInfo . LastQueueCycle = 0 ;
2021-07-20 18:03:49 +02:00
}
tileCount = Atmospherics . MonstermosTileLimit ;
}
var averageMoles = totalMoles / ( tileCount ) ;
var giverTilesLength = 0 ;
var takerTilesLength = 0 ;
for ( var i = 0 ; i < tileCount ; i + + )
{
2021-08-02 17:03:13 +02:00
var otherTile = _equalizeTiles [ i ] ! ;
2021-07-20 18:03:49 +02:00
otherTile . MonstermosInfo . LastCycle = cycleNum ;
otherTile . MonstermosInfo . MoleDelta - = averageMoles ;
if ( otherTile . MonstermosInfo . MoleDelta > 0 )
{
2021-07-24 16:33:04 +02:00
_equalizeGiverTiles [ giverTilesLength + + ] = otherTile ;
2021-07-20 18:03:49 +02:00
}
else
{
2021-07-24 16:33:04 +02:00
_equalizeTakerTiles [ takerTilesLength + + ] = otherTile ;
2021-07-20 18:03:49 +02:00
}
}
var logN = MathF . Log2 ( tileCount ) ;
2021-08-02 17:03:13 +02:00
// Optimization - try to spread gases using an O(n log n) algorithm that has a chance of not working first to avoid O(n^2)
2021-07-20 18:03:49 +02:00
if ( giverTilesLength > logN & & takerTilesLength > logN )
{
// Even if it fails, it will speed up the next part.
2021-07-24 16:33:04 +02:00
Array . Sort ( _equalizeTiles , 0 , tileCount , _monstermosComparer ) ;
2021-07-20 18:03:49 +02:00
for ( var i = 0 ; i < tileCount ; i + + )
{
2021-08-02 17:03:13 +02:00
var otherTile = _equalizeTiles [ i ] ! ;
2021-07-20 18:03:49 +02:00
otherTile . MonstermosInfo . FastDone = true ;
if ( ! ( otherTile . MonstermosInfo . MoleDelta > 0 ) ) continue ;
var eligibleDirections = AtmosDirection . Invalid ;
var eligibleDirectionCount = 0 ;
for ( var j = 0 ; j < Atmospherics . Directions ; j + + )
{
var direction = ( AtmosDirection ) ( 1 < < j ) ;
if ( ! otherTile . AdjacentBits . IsFlagSet ( direction ) ) continue ;
2021-08-02 17:03:13 +02:00
var tile2 = otherTile . AdjacentTiles [ j ] ! ;
2022-07-04 16:51:34 +02:00
DebugTools . Assert ( tile2 . AdjacentBits . IsFlagSet ( direction . GetOpposite ( ) ) ) ;
2021-07-20 18:03:49 +02:00
// skip anything that isn't part of our current processing block.
if ( tile2 . MonstermosInfo . FastDone | | tile2 . MonstermosInfo . LastQueueCycle ! = queueCycle )
continue ;
eligibleDirections | = direction ;
eligibleDirectionCount + + ;
}
if ( eligibleDirectionCount < = 0 )
continue ; // Oof we've painted ourselves into a corner. Bad luck. Next part will handle this.
var molesToMove = otherTile . MonstermosInfo . MoleDelta / eligibleDirectionCount ;
for ( var j = 0 ; j < Atmospherics . Directions ; j + + )
{
var direction = ( AtmosDirection ) ( 1 < < j ) ;
if ( ! eligibleDirections . IsFlagSet ( direction ) ) continue ;
AdjustEqMovement ( otherTile , direction , molesToMove ) ;
otherTile . MonstermosInfo . MoleDelta - = molesToMove ;
2021-08-02 17:03:13 +02:00
otherTile . AdjacentTiles [ j ] ! . MonstermosInfo . MoleDelta + = molesToMove ;
2021-07-20 18:03:49 +02:00
}
}
giverTilesLength = 0 ;
takerTilesLength = 0 ;
for ( var i = 0 ; i < tileCount ; i + + )
{
2021-08-02 17:03:13 +02:00
var otherTile = _equalizeTiles [ i ] ! ;
2021-07-20 18:03:49 +02:00
if ( otherTile . MonstermosInfo . MoleDelta > 0 )
{
2021-07-24 16:33:04 +02:00
_equalizeGiverTiles [ giverTilesLength + + ] = otherTile ;
2021-07-20 18:03:49 +02:00
}
else
{
2021-07-24 16:33:04 +02:00
_equalizeTakerTiles [ takerTilesLength + + ] = otherTile ;
2021-07-20 18:03:49 +02:00
}
}
}
// This is the part that can become O(n^2).
if ( giverTilesLength < takerTilesLength )
{
// as an optimization, we choose one of two methods based on which list is smaller. We really want to avoid O(n^2) if we can.
for ( var j = 0 ; j < giverTilesLength ; j + + )
{
2021-07-24 16:33:04 +02:00
var giver = _equalizeGiverTiles [ j ] ;
2021-07-20 18:03:49 +02:00
giver . MonstermosInfo . CurrentTransferDirection = AtmosDirection . Invalid ;
giver . MonstermosInfo . CurrentTransferAmount = 0 ;
var queueCycleSlow = + + gridAtmosphere . EqualizationQueueCycleControl ;
var queueLength = 0 ;
2021-07-24 16:33:04 +02:00
_equalizeQueue [ queueLength + + ] = giver ;
2021-07-20 18:03:49 +02:00
giver . MonstermosInfo . LastSlowQueueCycle = queueCycleSlow ;
for ( var i = 0 ; i < queueLength ; i + + )
{
if ( giver . MonstermosInfo . MoleDelta < = 0 )
break ; // We're done here now. Let's not do more work than needed.
2021-07-24 16:33:04 +02:00
var otherTile = _equalizeQueue [ i ] ;
2021-07-20 18:03:49 +02:00
for ( var k = 0 ; k < Atmospherics . Directions ; k + + )
{
var direction = ( AtmosDirection ) ( 1 < < k ) ;
if ( ! otherTile . AdjacentBits . IsFlagSet ( direction ) ) continue ;
var otherTile2 = otherTile . AdjacentTiles [ k ] ;
if ( giver . 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 ;
2022-07-04 16:51:34 +02:00
DebugTools . Assert ( otherTile2 . AdjacentBits . IsFlagSet ( direction . GetOpposite ( ) ) ) ;
2021-07-20 18:03:49 +02:00
if ( otherTile2 . MonstermosInfo . LastSlowQueueCycle = = queueCycleSlow ) continue ;
2021-07-24 16:33:04 +02:00
_equalizeQueue [ queueLength + + ] = otherTile2 ;
2021-07-20 18:03:49 +02:00
otherTile2 . MonstermosInfo . LastSlowQueueCycle = queueCycleSlow ;
otherTile2 . MonstermosInfo . CurrentTransferDirection = direction . GetOpposite ( ) ;
otherTile2 . MonstermosInfo . CurrentTransferAmount = 0 ;
if ( otherTile2 . MonstermosInfo . MoleDelta < 0 )
{
// This tile needs gas. Let's give it to 'em.
if ( - otherTile2 . MonstermosInfo . MoleDelta > giver . MonstermosInfo . MoleDelta )
{
// We don't have enough gas!
otherTile2 . MonstermosInfo . CurrentTransferAmount - = giver . MonstermosInfo . MoleDelta ;
otherTile2 . MonstermosInfo . MoleDelta + = giver . MonstermosInfo . MoleDelta ;
giver . MonstermosInfo . MoleDelta = 0 ;
}
else
{
// We have enough gas.
otherTile2 . MonstermosInfo . CurrentTransferAmount + = otherTile2 . MonstermosInfo . MoleDelta ;
giver . MonstermosInfo . MoleDelta + = otherTile2 . MonstermosInfo . MoleDelta ;
otherTile2 . MonstermosInfo . MoleDelta = 0 ;
}
}
}
}
// Putting this loop here helps make it O(n^2) over O(n^3)
for ( var i = queueLength - 1 ; i > = 0 ; i - - )
{
2021-07-24 16:33:04 +02:00
var otherTile = _equalizeQueue [ i ] ;
2021-07-20 18:03:49 +02:00
if ( otherTile . MonstermosInfo . CurrentTransferAmount ! = 0 & & otherTile . MonstermosInfo . CurrentTransferDirection ! = AtmosDirection . Invalid )
{
AdjustEqMovement ( otherTile , otherTile . MonstermosInfo . CurrentTransferDirection , otherTile . MonstermosInfo . CurrentTransferAmount ) ;
2021-08-02 17:03:13 +02:00
otherTile . AdjacentTiles [ otherTile . MonstermosInfo . CurrentTransferDirection . ToIndex ( ) ] !
2021-07-20 18:03:49 +02:00
. MonstermosInfo . CurrentTransferAmount + = otherTile . MonstermosInfo . CurrentTransferAmount ;
otherTile . MonstermosInfo . CurrentTransferAmount = 0 ;
}
}
}
}
else
{
for ( var j = 0 ; j < takerTilesLength ; j + + )
{
2021-07-24 16:33:04 +02:00
var taker = _equalizeTakerTiles [ j ] ;
2021-07-20 18:03:49 +02:00
taker . MonstermosInfo . CurrentTransferDirection = AtmosDirection . Invalid ;
taker . MonstermosInfo . CurrentTransferAmount = 0 ;
var queueCycleSlow = + + gridAtmosphere . EqualizationQueueCycleControl ;
var queueLength = 0 ;
2021-07-24 16:33:04 +02:00
_equalizeQueue [ queueLength + + ] = taker ;
2021-07-20 18:03:49 +02:00
taker . MonstermosInfo . LastSlowQueueCycle = queueCycleSlow ;
for ( var i = 0 ; i < queueLength ; i + + )
{
if ( taker . MonstermosInfo . MoleDelta > = 0 )
break ; // We're done here now. Let's not do more work than needed.
2021-07-24 16:33:04 +02:00
var otherTile = _equalizeQueue [ i ] ;
2021-07-20 18:03:49 +02:00
for ( var k = 0 ; k < Atmospherics . Directions ; k + + )
{
var direction = ( AtmosDirection ) ( 1 < < k ) ;
if ( ! otherTile . AdjacentBits . IsFlagSet ( direction ) ) continue ;
var otherTile2 = otherTile . AdjacentTiles [ k ] ;
if ( taker . MonstermosInfo . MoleDelta > = 0 ) break ; // We're done here now. Let's not do more work than needed.
2023-05-21 07:53:04 +12:00
if ( otherTile2 = = null | | otherTile2 . AdjacentBits = = 0 | | otherTile2 . MonstermosInfo . LastQueueCycle ! = queueCycle ) continue ;
2022-07-04 16:51:34 +02:00
DebugTools . Assert ( otherTile2 . AdjacentBits . IsFlagSet ( direction . GetOpposite ( ) ) ) ;
2021-07-20 18:03:49 +02:00
if ( otherTile2 . MonstermosInfo . LastSlowQueueCycle = = queueCycleSlow ) continue ;
2021-07-24 16:33:04 +02:00
_equalizeQueue [ queueLength + + ] = otherTile2 ;
2021-07-20 18:03:49 +02:00
otherTile2 . MonstermosInfo . LastSlowQueueCycle = queueCycleSlow ;
otherTile2 . MonstermosInfo . CurrentTransferDirection = direction . GetOpposite ( ) ;
otherTile2 . MonstermosInfo . CurrentTransferAmount = 0 ;
if ( otherTile2 . MonstermosInfo . MoleDelta > 0 )
{
// This tile has gas we can suck, so let's
if ( otherTile2 . MonstermosInfo . MoleDelta > - taker . MonstermosInfo . MoleDelta )
{
// They have enough gas
otherTile2 . MonstermosInfo . CurrentTransferAmount - = taker . MonstermosInfo . MoleDelta ;
otherTile2 . MonstermosInfo . MoleDelta + = taker . MonstermosInfo . MoleDelta ;
taker . MonstermosInfo . MoleDelta = 0 ;
}
else
{
// They don't have enough gas!
otherTile2 . MonstermosInfo . CurrentTransferAmount + = otherTile2 . MonstermosInfo . MoleDelta ;
taker . MonstermosInfo . MoleDelta + = otherTile2 . MonstermosInfo . MoleDelta ;
otherTile2 . MonstermosInfo . MoleDelta = 0 ;
}
}
}
}
for ( var i = queueLength - 1 ; i > = 0 ; i - - )
{
2021-07-24 16:33:04 +02:00
var otherTile = _equalizeQueue [ i ] ;
2021-07-20 18:03:49 +02:00
if ( otherTile . MonstermosInfo . CurrentTransferAmount = = 0 | | otherTile . MonstermosInfo . CurrentTransferDirection = = AtmosDirection . Invalid )
continue ;
AdjustEqMovement ( otherTile , otherTile . MonstermosInfo . CurrentTransferDirection , otherTile . MonstermosInfo . CurrentTransferAmount ) ;
2021-08-02 17:03:13 +02:00
otherTile . AdjacentTiles [ otherTile . MonstermosInfo . CurrentTransferDirection . ToIndex ( ) ] !
2021-07-20 18:03:49 +02:00
. MonstermosInfo . CurrentTransferAmount + = otherTile . MonstermosInfo . CurrentTransferAmount ;
otherTile . MonstermosInfo . CurrentTransferAmount = 0 ;
}
}
}
for ( var i = 0 ; i < tileCount ; i + + )
{
2021-08-02 17:03:13 +02:00
var otherTile = _equalizeTiles [ i ] ! ;
2022-12-19 08:25:27 +13:00
FinalizeEq ( gridAtmosphere , otherTile , visuals ) ;
2021-07-20 18:03:49 +02:00
}
for ( var i = 0 ; i < tileCount ; i + + )
{
2021-08-02 17:03:13 +02:00
var otherTile = _equalizeTiles [ i ] ! ;
2021-07-20 18:03:49 +02:00
for ( var j = 0 ; j < Atmospherics . Directions ; j + + )
{
var direction = ( AtmosDirection ) ( 1 < < j ) ;
if ( ! otherTile . AdjacentBits . IsFlagSet ( direction ) ) continue ;
2021-08-02 17:03:13 +02:00
var otherTile2 = otherTile . AdjacentTiles [ j ] ! ;
2023-05-21 07:53:04 +12:00
if ( otherTile2 . AdjacentBits = = 0 )
continue ;
2022-07-04 16:51:34 +02:00
DebugTools . Assert ( otherTile2 . AdjacentBits . IsFlagSet ( direction . GetOpposite ( ) ) ) ;
if ( otherTile2 . Air ! = null & & CompareExchange ( otherTile2 . Air , tile . Air ) = = GasCompareResult . NoExchange ) continue ;
2021-07-23 11:09:01 +02:00
AddActiveTile ( gridAtmosphere , otherTile2 ) ;
2021-07-20 18:03:49 +02:00
break ;
}
}
2021-07-24 16:33:04 +02:00
// We do cleanup.
Array . Clear ( _equalizeTiles , 0 , Atmospherics . MonstermosHardTileLimit ) ;
Array . Clear ( _equalizeGiverTiles , 0 , Atmospherics . MonstermosTileLimit ) ;
Array . Clear ( _equalizeTakerTiles , 0 , Atmospherics . MonstermosTileLimit ) ;
Array . Clear ( _equalizeQueue , 0 , Atmospherics . MonstermosTileLimit ) ;
2021-07-20 18:03:49 +02:00
}
2022-12-19 08:25:27 +13:00
private void ExplosivelyDepressurize ( MapGridComponent mapGrid , GridAtmosphereComponent gridAtmosphere , TileAtmosphere tile , int cycleNum , GasTileOverlayComponent ? visuals )
2021-07-20 18:03:49 +02:00
{
// Check if explosive depressurization is enabled and if the tile is valid.
if ( ! MonstermosDepressurization | | tile . Air = = null )
return ;
const int limit = Atmospherics . MonstermosHardTileLimit ;
2021-11-23 12:22:18 +01:00
var totalMolesRemoved = 0f ;
2021-07-20 18:03:49 +02:00
var queueCycle = + + gridAtmosphere . EqualizationQueueCycleControl ;
var tileCount = 0 ;
var spaceTileCount = 0 ;
2021-07-24 16:33:04 +02:00
_depressurizeTiles [ tileCount + + ] = tile ;
2021-07-20 18:03:49 +02:00
tile . MonstermosInfo = new MonstermosInfo { LastQueueCycle = queueCycle } ;
for ( var i = 0 ; i < tileCount ; i + + )
{
2021-07-24 16:33:04 +02:00
var otherTile = _depressurizeTiles [ i ] ;
2021-07-20 18:03:49 +02:00
otherTile . MonstermosInfo . LastCycle = cycleNum ;
otherTile . MonstermosInfo . CurrentTransferDirection = AtmosDirection . Invalid ;
2021-08-02 17:03:13 +02:00
// Tiles in the _depressurizeTiles array cannot have null air.
2022-07-04 16:51:34 +02:00
if ( ! otherTile . Space )
2021-07-20 18:03:49 +02:00
{
for ( var j = 0 ; j < Atmospherics . Directions ; j + + )
{
var direction = ( AtmosDirection ) ( 1 < < j ) ;
if ( ! otherTile . AdjacentBits . IsFlagSet ( direction ) ) continue ;
var otherTile2 = otherTile . AdjacentTiles [ j ] ;
2021-08-02 17:03:13 +02:00
if ( otherTile2 ? . Air = = null ) continue ;
2022-07-04 16:51:34 +02:00
DebugTools . Assert ( otherTile2 . AdjacentBits . IsFlagSet ( direction . GetOpposite ( ) ) ) ;
2021-07-20 18:03:49 +02:00
if ( otherTile2 . MonstermosInfo . LastQueueCycle = = queueCycle ) continue ;
2022-12-19 08:25:27 +13:00
ConsiderFirelocks ( gridAtmosphere , otherTile , otherTile2 , visuals , mapGrid ) ;
2021-07-20 18:03:49 +02:00
// The firelocks might have closed on us.
if ( ! otherTile . AdjacentBits . IsFlagSet ( direction ) ) continue ;
2021-09-06 17:30:38 +02:00
otherTile2 . MonstermosInfo = new MonstermosInfo { LastQueueCycle = queueCycle } ;
2021-07-24 16:33:04 +02:00
_depressurizeTiles [ tileCount + + ] = otherTile2 ;
2021-09-06 17:11:11 +02:00
if ( tileCount > = limit ) break ;
2021-07-20 18:03:49 +02:00
}
}
2021-09-06 17:30:38 +02:00
else
{
_depressurizeSpaceTiles [ spaceTileCount + + ] = otherTile ;
otherTile . PressureSpecificTarget = otherTile ;
}
2021-07-20 18:03:49 +02:00
2021-09-06 17:30:38 +02:00
if ( tileCount < limit & & spaceTileCount < limit )
continue ;
break ;
2021-07-20 18:03:49 +02:00
}
var queueCycleSlow = + + gridAtmosphere . EqualizationQueueCycleControl ;
var progressionCount = 0 ;
for ( var i = 0 ; i < spaceTileCount ; i + + )
{
2021-07-24 16:33:04 +02:00
var otherTile = _depressurizeSpaceTiles [ i ] ;
_depressurizeProgressionOrder [ progressionCount + + ] = otherTile ;
2021-07-20 18:03:49 +02:00
otherTile . MonstermosInfo . LastSlowQueueCycle = queueCycleSlow ;
otherTile . MonstermosInfo . CurrentTransferDirection = AtmosDirection . Invalid ;
}
2023-05-21 07:53:04 +12:00
// Moving into the room from the breach or airlock
2021-07-20 18:03:49 +02:00
for ( var i = 0 ; i < progressionCount ; i + + )
{
2023-05-21 07:53:04 +12:00
// From a tile exposed to space
2021-07-24 16:33:04 +02:00
var otherTile = _depressurizeProgressionOrder [ i ] ;
2021-07-20 18:03:49 +02:00
for ( var j = 0 ; j < Atmospherics . Directions ; j + + )
{
2023-05-21 07:53:04 +12:00
// Flood fill into this new direction
2021-07-20 18:03:49 +02:00
var direction = ( AtmosDirection ) ( 1 < < j ) ;
2021-08-02 17:03:13 +02:00
// Tiles in _depressurizeProgressionOrder cannot have null air.
2022-07-04 16:51:34 +02:00
if ( ! otherTile . AdjacentBits . IsFlagSet ( direction ) & & ! otherTile . Space ) continue ;
2021-07-20 18:03:49 +02:00
var tile2 = otherTile . AdjacentTiles [ j ] ;
if ( tile2 ? . MonstermosInfo . LastQueueCycle ! = queueCycle ) continue ;
2022-07-04 16:51:34 +02:00
DebugTools . Assert ( tile2 . AdjacentBits . IsFlagSet ( direction . GetOpposite ( ) ) ) ;
2023-05-21 07:53:04 +12:00
// If flood fill has already reached this tile, continue.
2021-07-20 18:03:49 +02:00
if ( tile2 . MonstermosInfo . LastSlowQueueCycle = = queueCycleSlow ) continue ;
2022-07-04 16:51:34 +02:00
if ( tile2 . Space ) continue ;
2021-07-20 18:03:49 +02:00
tile2 . MonstermosInfo . CurrentTransferDirection = direction . GetOpposite ( ) ;
2023-05-21 07:53:04 +12:00
tile2 . MonstermosInfo . CurrentTransferAmount = 0.0f ;
2021-07-20 18:03:49 +02:00
tile2 . PressureSpecificTarget = otherTile . PressureSpecificTarget ;
tile2 . MonstermosInfo . LastSlowQueueCycle = queueCycleSlow ;
2021-07-24 16:33:04 +02:00
_depressurizeProgressionOrder [ progressionCount + + ] = tile2 ;
2021-07-20 18:03:49 +02:00
}
}
2023-05-21 07:53:04 +12:00
// Moving towards the breach from the edges of the flood filled region
2021-07-20 18:03:49 +02:00
for ( var i = progressionCount - 1 ; i > = 0 ; i - - )
{
2021-07-24 16:33:04 +02:00
var otherTile = _depressurizeProgressionOrder [ i ] ;
2023-05-21 07:53:04 +12:00
if ( otherTile ? . Air = = null ) { continue ; }
2021-07-20 18:03:49 +02:00
if ( otherTile . MonstermosInfo . CurrentTransferDirection = = AtmosDirection . Invalid ) continue ;
2021-07-23 11:09:01 +02:00
gridAtmosphere . HighPressureDelta . Add ( otherTile ) ;
AddActiveTile ( gridAtmosphere , otherTile ) ;
2021-07-20 18:03:49 +02:00
var otherTile2 = otherTile . AdjacentTiles [ otherTile . MonstermosInfo . CurrentTransferDirection . ToIndex ( ) ] ;
2023-05-21 07:53:04 +12:00
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 ) ;
}
2021-11-23 12:22:18 +01:00
totalMolesRemoved + = sum ;
2021-07-20 18:03:49 +02:00
otherTile . MonstermosInfo . CurrentTransferAmount + = sum ;
otherTile2 . MonstermosInfo . CurrentTransferAmount + = otherTile . MonstermosInfo . CurrentTransferAmount ;
otherTile . PressureDifference = otherTile . MonstermosInfo . CurrentTransferAmount ;
otherTile . PressureDirection = otherTile . MonstermosInfo . CurrentTransferDirection ;
if ( otherTile2 . MonstermosInfo . CurrentTransferDirection = = AtmosDirection . Invalid )
{
otherTile2 . PressureDifference = otherTile2 . MonstermosInfo . CurrentTransferAmount ;
otherTile2 . PressureDirection = otherTile . MonstermosInfo . CurrentTransferDirection ;
}
2023-05-21 07:53:04 +12:00
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 ) ;
2021-11-30 11:42:48 +01:00
2023-05-21 07:53:04 +12:00
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 ( ) ;
2021-11-30 11:42:48 +01:00
2023-05-21 07:53:04 +12:00
// 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 ;
}
2021-11-30 11:42:48 +01:00
2022-12-19 08:25:27 +13:00
InvalidateVisuals ( otherTile . GridIndex , otherTile . GridIndices , visuals ) ;
2023-05-21 07:53:04 +12:00
HandleDecompressionFloorRip ( mapGrid , otherTile , otherTile . MonstermosInfo . CurrentTransferAmount ) ;
2021-07-20 18:03:49 +02:00
}
2021-07-23 17:27:16 +02:00
if ( GridImpulse & & tileCount > 0 )
{
2023-07-08 14:08:32 +10:00
var direction = ( ( Vector2 ) _depressurizeTiles [ tileCount - 1 ] . GridIndices - tile . GridIndices ) . Normalized ( ) ;
2021-07-23 17:27:16 +02:00
2022-12-12 14:59:02 +11:00
var gridPhysics = Comp < PhysicsComponent > ( mapGrid . Owner ) ;
2021-07-23 17:27:16 +02:00
// TODO ATMOS: Come up with better values for these.
2023-01-15 15:38:59 +11:00
_physics . ApplyLinearImpulse ( mapGrid . Owner , direction * totalMolesRemoved * gridPhysics . Mass , body : gridPhysics ) ;
2023-07-08 14:08:32 +10:00
_physics . ApplyAngularImpulse ( mapGrid . Owner , Vector2Helpers . Cross ( tile . GridIndices - gridPhysics . LocalCenter , direction ) * totalMolesRemoved , body : gridPhysics ) ;
2021-07-23 17:27:16 +02:00
}
2023-05-21 07:53:04 +12:00
if ( tileCount > 10 & & ( totalMolesRemoved / tileCount ) > 10 )
2022-07-04 16:51:34 +02:00
_adminLog . Add ( LogType . ExplosiveDepressurization , LogImpact . High ,
2021-11-23 12:22:18 +01:00
$"Explosive depressurization removed {totalMolesRemoved} moles from {tileCount} tiles starting from position {tile.GridIndices:position} on grid ID {tile.GridIndex:grid}" ) ;
2021-07-24 16:33:04 +02:00
Array . Clear ( _depressurizeTiles , 0 , Atmospherics . MonstermosHardTileLimit ) ;
Array . Clear ( _depressurizeSpaceTiles , 0 , Atmospherics . MonstermosHardTileLimit ) ;
Array . Clear ( _depressurizeProgressionOrder , 0 , Atmospherics . MonstermosHardTileLimit * 2 ) ;
2021-07-20 18:03:49 +02:00
}
2022-12-19 08:25:27 +13:00
private void ConsiderFirelocks ( GridAtmosphereComponent gridAtmosphere , TileAtmosphere tile , TileAtmosphere other , GasTileOverlayComponent ? visuals , MapGridComponent mapGrid )
2021-07-20 18:03:49 +02:00
{
var reconsiderAdjacent = false ;
foreach ( var entity in mapGrid . GetAnchoredEntities ( tile . GridIndices ) )
{
2021-12-16 12:10:51 +01:00
if ( ! TryComp ( entity , out FirelockComponent ? firelock ) )
2021-07-20 18:03:49 +02:00
continue ;
2022-09-06 17:55:33 +12:00
reconsiderAdjacent | = _firelockSystem . EmergencyPressureStop ( entity , firelock ) ;
2021-07-20 18:03:49 +02:00
}
foreach ( var entity in mapGrid . GetAnchoredEntities ( other . GridIndices ) )
{
2021-12-16 12:10:51 +01:00
if ( ! TryComp ( entity , out FirelockComponent ? firelock ) )
2021-07-20 18:03:49 +02:00
continue ;
2022-09-06 17:55:33 +12:00
reconsiderAdjacent | = _firelockSystem . EmergencyPressureStop ( entity , firelock ) ;
2021-07-20 18:03:49 +02:00
}
if ( ! reconsiderAdjacent )
return ;
2022-12-12 14:59:02 +11:00
var tileEv = new UpdateAdjacentMethodEvent ( mapGrid . Owner , tile . GridIndices ) ;
var otherEv = new UpdateAdjacentMethodEvent ( mapGrid . Owner , other . GridIndices ) ;
GridUpdateAdjacent ( mapGrid . Owner , gridAtmosphere , ref tileEv ) ;
GridUpdateAdjacent ( mapGrid . Owner , gridAtmosphere , ref otherEv ) ;
2022-12-19 08:25:27 +13:00
InvalidateVisuals ( tile . GridIndex , tile . GridIndices , visuals ) ;
InvalidateVisuals ( other . GridIndex , other . GridIndices , visuals ) ;
2021-07-20 18:03:49 +02:00
}
2022-12-19 08:25:27 +13:00
private void FinalizeEq ( GridAtmosphereComponent gridAtmosphere , TileAtmosphere tile , GasTileOverlayComponent ? visuals )
2021-07-20 18:03:49 +02:00
{
Span < float > transferDirections = stackalloc float [ Atmospherics . Directions ] ;
var hasTransferDirs = false ;
for ( var i = 0 ; i < Atmospherics . Directions ; i + + )
{
var amount = tile . MonstermosInfo [ i ] ;
if ( amount = = 0 ) continue ;
transferDirections [ i ] = amount ;
tile . MonstermosInfo [ i ] = 0 ; // Set them to 0 to prevent infinite recursion.
hasTransferDirs = true ;
}
if ( ! hasTransferDirs ) return ;
for ( var i = 0 ; i < Atmospherics . Directions ; i + + )
{
var direction = ( AtmosDirection ) ( 1 < < i ) ;
if ( ! tile . AdjacentBits . IsFlagSet ( direction ) ) continue ;
var amount = transferDirections [ i ] ;
var otherTile = tile . AdjacentTiles [ i ] ;
if ( otherTile ? . Air = = null ) continue ;
2022-07-04 16:51:34 +02:00
DebugTools . Assert ( otherTile . AdjacentBits . IsFlagSet ( direction . GetOpposite ( ) ) ) ;
2022-05-10 12:56:17 +02:00
if ( amount < = 0 ) continue ;
// Everything that calls this method already ensures that Air will not be null.
if ( tile . Air ! . TotalMoles < amount )
2022-12-19 08:25:27 +13:00
FinalizeEqNeighbors ( gridAtmosphere , tile , transferDirections , visuals ) ;
2022-05-10 12:56:17 +02:00
otherTile . MonstermosInfo [ direction . GetOpposite ( ) ] = 0 ;
Merge ( otherTile . Air , tile . Air . Remove ( amount ) ) ;
2022-12-19 08:25:27 +13:00
InvalidateVisuals ( tile . GridIndex , tile . GridIndices , visuals ) ;
InvalidateVisuals ( otherTile . GridIndex , otherTile . GridIndices , visuals ) ;
2022-05-10 12:56:17 +02:00
ConsiderPressureDifference ( gridAtmosphere , tile , direction , amount ) ;
2021-07-20 18:03:49 +02:00
}
}
2022-12-19 08:25:27 +13:00
private void FinalizeEqNeighbors ( GridAtmosphereComponent gridAtmosphere , TileAtmosphere tile , ReadOnlySpan < float > transferDirs , GasTileOverlayComponent ? visuals )
2021-07-20 18:03:49 +02:00
{
for ( var i = 0 ; i < Atmospherics . Directions ; i + + )
{
var direction = ( AtmosDirection ) ( 1 < < i ) ;
var amount = transferDirs [ i ] ;
2021-08-02 17:03:13 +02:00
// Since AdjacentBits is set, AdjacentTiles[i] wouldn't be null, and neither would its air.
2021-07-20 18:03:49 +02:00
if ( amount < 0 & & tile . AdjacentBits . IsFlagSet ( direction ) )
2022-12-19 08:25:27 +13:00
FinalizeEq ( gridAtmosphere , tile . AdjacentTiles [ i ] ! , visuals ) ; // A bit of recursion if needed.
2021-07-20 18:03:49 +02:00
}
}
private void AdjustEqMovement ( TileAtmosphere tile , AtmosDirection direction , float amount )
{
2022-07-04 16:51:34 +02:00
DebugTools . AssertNotNull ( tile ) ;
2021-09-06 16:55:05 +02:00
DebugTools . Assert ( tile . AdjacentBits . IsFlagSet ( direction ) ) ;
2021-08-02 17:03:13 +02:00
DebugTools . Assert ( tile . AdjacentTiles [ direction . ToIndex ( ) ] ! = null ) ;
// Every call to this method already ensures that the adjacent tile won't be null.
2022-10-17 04:50:11 +13:00
// Turns out: no they don't. Temporary debug checks to figure out which caller is causing problems:
if ( tile = = null )
{
Logger . Error ( $"Encountered null-tile in {nameof(AdjustEqMovement)}. Trace: {Environment.StackTrace}" ) ;
return ;
}
var adj = tile . AdjacentTiles [ direction . ToIndex ( ) ] ;
if ( adj = = null )
{
var nonNull = tile . AdjacentTiles . Where ( x = > x ! = null ) . Count ( ) ;
Logger . Error ( $"Encountered null adjacent tile in {nameof(AdjustEqMovement)}. Dir: {direction}, Tile: {tile.Tile}, non-null adj count: {nonNull}, Trace: {Environment.StackTrace}" ) ;
return ;
}
tile . MonstermosInfo [ direction ] + = amount ;
adj . MonstermosInfo [ direction . GetOpposite ( ) ] - = amount ;
2021-07-20 18:03:49 +02:00
}
2022-11-22 13:12:04 +11:00
private void HandleDecompressionFloorRip ( MapGridComponent mapGrid , TileAtmosphere tile , float sum )
2021-07-20 18:03:49 +02:00
{
2021-07-23 13:07:48 +02:00
if ( ! MonstermosRipTiles )
return ;
2023-05-21 07:53:04 +12:00
var chance = MathHelper . Clamp ( 0.01f + ( sum / Atmospherics . SpacingMaxWind ) * 0.3f , 0.003f , 0.3f ) ;
2021-07-20 18:03:49 +02:00
if ( sum > 20 & & _robustRandom . Prob ( chance ) )
2021-07-23 11:09:01 +02:00
PryTile ( mapGrid , tile . GridIndices ) ;
2021-07-20 18:03:49 +02:00
}
2022-02-16 00:23:23 -07:00
private sealed class TileAtmosphereComparer : IComparer < TileAtmosphere ? >
2021-07-20 18:03:49 +02:00
{
2021-08-02 17:03:13 +02:00
public int Compare ( TileAtmosphere ? a , TileAtmosphere ? b )
2021-07-20 18:03:49 +02:00
{
if ( a = = null & & b = = null )
return 0 ;
if ( a = = null )
return - 1 ;
if ( b = = null )
return 1 ;
return a . MonstermosInfo . MoleDelta . CompareTo ( b . MonstermosInfo . MoleDelta ) ;
}
}
}
}