Example #1
0
        //[MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void ExplosivelyDepressurize(int cycleNum)
        {
            if (Air == null)
            {
                return;
            }
            var totalGasesRemoved = 0f;
            var queueCycle        = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
            var tiles             = new List <TileAtmosphere>();
            var spaceTiles        = new List <TileAtmosphere>();

            tiles.Add(this);
            _tileAtmosInfo = new TileAtmosInfo
            {
                LastQueueCycle           = queueCycle,
                CurrentTransferDirection = Direction.Invalid
            };
            var tileCount = 1;

            for (var i = 0; i < tileCount; i++)
            {
                var tile = tiles[i];
                tile._tileAtmosInfo.LastCycle = cycleNum;
                tile._tileAtmosInfo.CurrentTransferDirection = Direction.Invalid;
                if (tile.Air.Immutable)
                {
                    spaceTiles.Add(tile);
                    tile.PressureSpecificTarget = tile;
                }
                else
                {
                    if (i > Atmospherics.ZumosHardTileLimit)
                    {
                        continue;
                    }
                    foreach (var direction in Cardinal)
                    {
                        if (!_adjacentTiles.TryGetValue(direction, out var tile2))
                        {
                            continue;
                        }
                        if (tile2?.Air == null)
                        {
                            continue;
                        }
                        if (tile2._tileAtmosInfo.LastQueueCycle == queueCycle)
                        {
                            continue;
                        }
                        tile.ConsiderFirelocks(tile2);
                        if (tile._adjacentTiles[direction]?.Air != null)
                        {
                            tile2._tileAtmosInfo = new TileAtmosInfo {
                                LastQueueCycle = queueCycle
                            };
                            tiles.Add(tile2);
                            tileCount++;
                        }
                    }
                }
            }

            var queueCycleSlow   = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
            var progressionOrder = new List <TileAtmosphere>();

            foreach (var tile in spaceTiles)
            {
                progressionOrder.Add(tile);
                tile._tileAtmosInfo.LastSlowQueueCycle       = queueCycleSlow;
                tile._tileAtmosInfo.CurrentTransferDirection = Direction.Invalid;
            }

            var progressionCount = progressionOrder.Count;

            for (int i = 0; i < progressionCount; i++)
            {
                var tile = progressionOrder[i];
                foreach (var direction in Cardinal)
                {
                    if (!_adjacentTiles.TryGetValue(direction, out var tile2))
                    {
                        continue;
                    }
                    if (tile2?._tileAtmosInfo.LastQueueCycle != queueCycle)
                    {
                        continue;
                    }
                    if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow)
                    {
                        continue;
                    }
                    if (tile2.Air.Immutable)
                    {
                        continue;
                    }
                    tile2._tileAtmosInfo.CurrentTransferDirection = direction.GetOpposite();
                    tile2._tileAtmosInfo.CurrentTransferAmount    = 0;
                    tile2.PressureSpecificTarget            = tile.PressureSpecificTarget;
                    tile2._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
                    progressionOrder.Add(tile2);
                    progressionCount++;
                }
            }

            for (int i = 0; i < progressionCount; i++)
            {
                var tile = progressionOrder[i];
                if (tile._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid)
                {
                    continue;
                }
                var hpdLength = _gridAtmosphereComponent.HighPressureDeltaCount;
                var inHdp     = _gridAtmosphereComponent.HasHighPressureDelta(tile);
                if (!inHdp)
                {
                    _gridAtmosphereComponent.AddHighPressureDelta(tile);
                }
                if (!tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, out var tile2) || tile2.Air == null)
                {
                    continue;
                }
                var sum = tile2.Air.TotalMoles;
                totalGasesRemoved += sum;
                tile._tileAtmosInfo.CurrentTransferAmount  += sum;
                tile2._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
                tile.PressureDifference = tile._tileAtmosInfo.CurrentTransferAmount;
                tile._pressureDirection = tile._tileAtmosInfo.CurrentTransferDirection;
                if (tile2._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid)
                {
                    tile2.PressureDifference = tile2._tileAtmosInfo.CurrentTransferAmount;
                    tile2._pressureDirection = tile._tileAtmosInfo.CurrentTransferDirection;
                }
                tile.Air.Clear();
                tile.UpdateVisuals();
                tile.HandleDecompressionFloorRip(sum);
            }
        }
Example #2
0
 private void ResetTileAtmosInfo()
 {
     _tileAtmosInfo = new TileAtmosInfo {
         CurrentTransferDirection = Direction.Invalid
     };
 }
Example #3
0
        //[MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void EqualizePressureInZone(int cycleNum)
        {
            if (Air == null || (_tileAtmosInfo.LastCycle >= cycleNum))
            {
                return;                                                        // Already done.
            }
            _tileAtmosInfo = new TileAtmosInfo();

            var startingMoles = Air.TotalMoles;
            var runAtmos      = false;

            // We need to figure if this is necessary
            foreach (var(direction, other) in _adjacentTiles)
            {
                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.
            {
                _tileAtmosInfo.LastCycle = cycleNum;
                return;
            }

            var queueCycle = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
            var totalMoles = 0f;
            var tiles      = new TileAtmosphere[Atmospherics.ZumosHardTileLimit];

            tiles[0] = this;
            _tileAtmosInfo.LastQueueCycle = queueCycle;
            var tileCount = 1;

            for (var i = 0; i < tileCount; i++)
            {
                if (i > Atmospherics.ZumosHardTileLimit)
                {
                    break;
                }
                var exploring = tiles[i];

                if (i < Atmospherics.ZumosTileLimit)
                {
                    var tileMoles = exploring.Air.TotalMoles;
                    exploring._tileAtmosInfo.MoleDelta = tileMoles;
                    totalMoles += tileMoles;
                }

                foreach (var(_, adj) in exploring._adjacentTiles)
                {
                    if (adj?.Air == null)
                    {
                        continue;
                    }
                    if (adj._tileAtmosInfo.LastQueueCycle == queueCycle)
                    {
                        continue;
                    }
                    adj._tileAtmosInfo = new TileAtmosInfo();

                    adj._tileAtmosInfo.LastQueueCycle = queueCycle;
                    if (tileCount < Atmospherics.ZumosHardTileLimit)
                    {
                        tiles[tileCount++] = adj;
                    }
                    if (adj.Air.Immutable)
                    {
                        // Looks like someone opened an airlock to space!
                        ExplosivelyDepressurize(cycleNum);
                        return;
                    }
                }
            }

            if (tileCount > Atmospherics.ZumosTileLimit)
            {
                for (var i = Atmospherics.ZumosTileLimit; i < tileCount; i++)
                {
                    //We unmark them. We shouldn't be pushing/pulling gases to/from them.
                    var tile = tiles[i];
                    if (tile == null)
                    {
                        continue;
                    }
                    tiles[i]._tileAtmosInfo.LastQueueCycle = 0;
                }

                tileCount = Atmospherics.ZumosTileLimit;
            }

            //tiles = tiles.AsSpan().Slice(0, tileCount).ToArray(); // According to my benchmarks, this is much slower.
            Array.Resize(ref tiles, tileCount);

            var averageMoles = totalMoles / (tiles.Length);
            var giverTiles   = new List <TileAtmosphere>();
            var takerTiles   = new List <TileAtmosphere>();

            for (var i = 0; i < tileCount; i++)
            {
                var tile = tiles[i];
                tile._tileAtmosInfo.LastCycle  = cycleNum;
                tile._tileAtmosInfo.MoleDelta -= averageMoles;
                if (tile._tileAtmosInfo.MoleDelta > 0)
                {
                    giverTiles.Add(tile);
                }
                else
                {
                    takerTiles.Add(tile);
                }
            }

            var logN = MathF.Log2(tiles.Length);

            // Optimization - try to spread gases using an O(nlogn) algorithm that has a chance of not working first to avoid O(n^2)
            if (giverTiles.Count > logN && takerTiles.Count > logN)
            {
                // Even if it fails, it will speed up the next part.
                Array.Sort(tiles, (a, b)
                           => a._tileAtmosInfo.MoleDelta.CompareTo(b._tileAtmosInfo.MoleDelta));

                foreach (var tile in tiles)
                {
                    tile._tileAtmosInfo.FastDone = true;
                    if (!(tile._tileAtmosInfo.MoleDelta > 0))
                    {
                        continue;
                    }
                    Direction eligibleAdjBits = 0;
                    var       amtEligibleAdj  = 0;
                    foreach (var direction in Cardinal)
                    {
                        if (!tile._adjacentTiles.TryGetValue(direction, out var tile2))
                        {
                            continue;
                        }

                        // skip anything that isn't part of our current processing block. Original one didn't do this unfortunately, which probably cause some massive lag.
                        if (tile2._tileAtmosInfo.FastDone || tile2._tileAtmosInfo.LastQueueCycle != queueCycle)
                        {
                            continue;
                        }

                        eligibleAdjBits |= direction;
                        amtEligibleAdj++;
                    }

                    if (amtEligibleAdj <= 0)
                    {
                        continue;                      // Oof we've painted ourselves into a corner. Bad luck. Next part will handle this.
                    }
                    var molesToMove = tile._tileAtmosInfo.MoleDelta / amtEligibleAdj;
                    foreach (var direction in Cardinal)
                    {
                        if ((eligibleAdjBits & direction) == 0 || !tile._adjacentTiles.TryGetValue(direction, out var tile2))
                        {
                            continue;
                        }
                        tile.AdjustEqMovement(direction, molesToMove);
                        tile._tileAtmosInfo.MoleDelta  -= molesToMove;
                        tile2._tileAtmosInfo.MoleDelta += molesToMove;
                    }
                }

                giverTiles.Clear();
                takerTiles.Clear();

                foreach (var tile in tiles)
                {
                    if (tile._tileAtmosInfo.MoleDelta > 0)
                    {
                        giverTiles.Add(tile);
                    }
                    else
                    {
                        takerTiles.Add(tile);
                    }
                }

                // This is the part that can become O(n^2).
                if (giverTiles.Count < takerTiles.Count)
                {
                    // 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.
                    var queue = new List <TileAtmosphere>(takerTiles.Count);
                    foreach (var giver in giverTiles)
                    {
                        giver._tileAtmosInfo.CurrentTransferDirection = (Direction)(-1);
                        giver._tileAtmosInfo.CurrentTransferAmount    = 0;
                        var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
                        queue.Clear();
                        queue.Add(giver);
                        giver._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
                        var queueCount = queue.Count;
                        for (var i = 0; i < queueCount; i++)
                        {
                            if (giver._tileAtmosInfo.MoleDelta <= 0)
                            {
                                break; // We're done here now. Let's not do more work than needed.
                            }
                            var tile = queue[i];
                            foreach (var direction in Cardinal)
                            {
                                if (!tile._adjacentTiles.TryGetValue(direction, out var tile2))
                                {
                                    continue;
                                }
                                if (giver._tileAtmosInfo.MoleDelta <= 0)
                                {
                                    break; // We're done here now. Let's not do more work than needed.
                                }
                                if (tile2?._tileAtmosInfo == null || tile2._tileAtmosInfo.LastQueueCycle != queueCycle)
                                {
                                    continue;
                                }

                                if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow)
                                {
                                    continue;
                                }
                                queue.Add(tile2);
                                queueCount++;
                                tile2._tileAtmosInfo.LastSlowQueueCycle       = queueCycleSlow;
                                tile2._tileAtmosInfo.CurrentTransferDirection = direction.GetOpposite();
                                tile2._tileAtmosInfo.CurrentTransferAmount    = 0;
                                if (tile2._tileAtmosInfo.MoleDelta < 0)
                                {
                                    // This tile needs gas. Let's give it to 'em.
                                    if (-tile2._tileAtmosInfo.MoleDelta > giver._tileAtmosInfo.MoleDelta)
                                    {
                                        // We don't have enough gas!
                                        tile2._tileAtmosInfo.CurrentTransferAmount -= giver._tileAtmosInfo.MoleDelta;
                                        tile2._tileAtmosInfo.MoleDelta             += giver._tileAtmosInfo.MoleDelta;
                                        giver._tileAtmosInfo.MoleDelta              = 0;
                                    }
                                    else
                                    {
                                        // We have enough gas.
                                        tile2._tileAtmosInfo.CurrentTransferAmount += tile2._tileAtmosInfo.MoleDelta;
                                        giver._tileAtmosInfo.MoleDelta             += tile2._tileAtmosInfo.MoleDelta;
                                        tile2._tileAtmosInfo.MoleDelta              = 0;
                                    }
                                }
                            }
                        }

                        // Putting this loop here helps make it O(n^2) over O(n^3)
                        for (var i = queue.Count - 1; i >= 0; i--)
                        {
                            var tile = queue[i];
                            if (tile._tileAtmosInfo.CurrentTransferAmount != 0 &&
                                tile._tileAtmosInfo.CurrentTransferDirection != (Direction)(-1))
                            {
                                tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount);
                                if (tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, out var adjacent))
                                {
                                    adjacent._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
                                }
                                tile._tileAtmosInfo.CurrentTransferAmount = 0;
                            }
                        }
                    }
                }
                else
                {
                    var queue = new List <TileAtmosphere>(giverTiles.Count);
                    foreach (var taker in takerTiles)
                    {
                        taker._tileAtmosInfo.CurrentTransferDirection = Direction.Invalid;
                        taker._tileAtmosInfo.CurrentTransferAmount    = 0;
                        var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
                        queue.Clear();
                        queue.Add(taker);
                        taker._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
                        var queueCount = queue.Count;
                        for (int i = 0; i < queueCount; i++)
                        {
                            if (taker._tileAtmosInfo.MoleDelta >= 0)
                            {
                                break; // We're done here now. Let's not do more work than needed.
                            }
                            var tile = queue[i];
                            foreach (var direction in Cardinal)
                            {
                                if (!tile._adjacentTiles.ContainsKey(direction))
                                {
                                    continue;
                                }
                                var tile2 = tile._adjacentTiles[direction];

                                if (taker._tileAtmosInfo.MoleDelta >= 0)
                                {
                                    break; // We're done here now. Let's not do more work than needed.
                                }
                                if (tile2?._tileAtmosInfo == null || tile2._tileAtmosInfo.LastQueueCycle != queueCycle)
                                {
                                    continue;
                                }
                                if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow)
                                {
                                    continue;
                                }
                                queue.Add(tile2);
                                queueCount++;
                                tile2._tileAtmosInfo.LastSlowQueueCycle       = queueCycleSlow;
                                tile2._tileAtmosInfo.CurrentTransferDirection = direction.GetOpposite();
                                tile2._tileAtmosInfo.CurrentTransferAmount    = 0;

                                if (tile2._tileAtmosInfo.MoleDelta > 0)
                                {
                                    // This tile has gas we can suck, so let's
                                    if (tile2._tileAtmosInfo.MoleDelta > -taker._tileAtmosInfo.MoleDelta)
                                    {
                                        // They have enough gas
                                        tile2._tileAtmosInfo.CurrentTransferAmount -= taker._tileAtmosInfo.MoleDelta;
                                        tile2._tileAtmosInfo.MoleDelta             += taker._tileAtmosInfo.MoleDelta;
                                        taker._tileAtmosInfo.MoleDelta              = 0;
                                    }
                                    else
                                    {
                                        // They don't have enough gas!
                                        tile2._tileAtmosInfo.CurrentTransferAmount += tile2._tileAtmosInfo.MoleDelta;
                                        taker._tileAtmosInfo.MoleDelta             += tile2._tileAtmosInfo.MoleDelta;
                                        tile2._tileAtmosInfo.MoleDelta              = 0;
                                    }
                                }
                            }
                        }

                        for (var i = queue.Count - 1; i >= 0; i--)
                        {
                            var tile = queue[i];
                            if (tile._tileAtmosInfo.CurrentTransferAmount == 0 ||
                                tile._tileAtmosInfo.CurrentTransferDirection == Direction.Invalid)
                            {
                                continue;
                            }
                            tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount);

                            if (tile._adjacentTiles.TryGetValue(tile._tileAtmosInfo.CurrentTransferDirection, out var adjacent))
                            {
                                adjacent._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
                            }
                            tile._tileAtmosInfo.CurrentTransferAmount = 0;
                        }
                    }
                }

                foreach (var tile in tiles)
                {
                    tile.FinalizeEq();
                }

                foreach (var tile in tiles)
                {
                    foreach (var direction in Cardinal)
                    {
                        if (!tile._adjacentTiles.TryGetValue(direction, out var tile2))
                        {
                            continue;
                        }
                        if (tile2?.Air?.Compare(Air) == GasMixture.GasCompareResult.NoExchange)
                        {
                            continue;
                        }
                        _gridAtmosphereComponent.AddActiveTile(tile2);
                        break;
                    }
                }
            }
        }
Example #4
0
        public void EqualizePressureInZone(int cycleNum)
        {
            if (Air == null || (_tileAtmosInfo.LastCycle >= cycleNum)) return; // Already done.

            _tileAtmosInfo = new TileAtmosInfo();

            var startingMoles = 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 (!_adjacentBits.HasFlag(direction)) continue;
                var other = _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.
            {
                _tileAtmosInfo.LastCycle = cycleNum;
                return;
            }

            var queueCycle = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
            var totalMoles = 0f;
            var tiles = ArrayPool<TileAtmosphere>.Shared.Rent(Atmospherics.ZumosHardTileLimit);
            tiles[0] = this;
            _tileAtmosInfo.LastQueueCycle = queueCycle;
            var tileCount = 1;
            for (var i = 0; i < tileCount; i++)
            {
                if (i > Atmospherics.ZumosHardTileLimit) break;
                var exploring = tiles[i];

                if (i < Atmospherics.ZumosTileLimit)
                {
                    var tileMoles = exploring.Air.TotalMoles;
                    exploring._tileAtmosInfo.MoleDelta = tileMoles;
                    totalMoles += tileMoles;
                }

                for (var j = 0; j < Atmospherics.Directions; j++)
                {
                    var direction = (AtmosDirection) (1 << j);
                    if (!exploring._adjacentBits.HasFlag(direction)) continue;
                    var adj = exploring._adjacentTiles[j];
                    if (adj?.Air == null) continue;
                    if(adj._tileAtmosInfo.LastQueueCycle == queueCycle) continue;
                    adj._tileAtmosInfo = new TileAtmosInfo {LastQueueCycle = queueCycle};

                    if(tileCount < Atmospherics.ZumosHardTileLimit)
                        tiles[tileCount++] = adj;
                    if (adj.Air.Immutable)
                    {
                        // Looks like someone opened an airlock to space!
                        ExplosivelyDepressurize(cycleNum);
                        return;
                    }
                }
            }

            if (tileCount > Atmospherics.ZumosTileLimit)
            {
                for (var i = Atmospherics.ZumosTileLimit; i < tileCount; i++)
                {
                    //We unmark them. We shouldn't be pushing/pulling gases to/from them.
                    var tile = tiles[i];
                    if (tile == null) continue;
                    tiles[i]._tileAtmosInfo.LastQueueCycle = 0;
                }

                tileCount = Atmospherics.ZumosTileLimit;
            }

            //tiles = tiles.AsSpan().Slice(0, tileCount).ToArray(); // According to my benchmarks, this is much slower.
            //Array.Resize(ref tiles, tileCount);

            var averageMoles = totalMoles / (tileCount);
            var giverTiles = ArrayPool<TileAtmosphere>.Shared.Rent(tileCount);
            var takerTiles = ArrayPool<TileAtmosphere>.Shared.Rent(tileCount);
            var giverTilesLength = 0;
            var takerTilesLength = 0;

            for (var i = 0; i < tileCount; i++)
            {
                var tile = tiles[i];
                tile._tileAtmosInfo.LastCycle = cycleNum;
                tile._tileAtmosInfo.MoleDelta -= averageMoles;
                if (tile._tileAtmosInfo.MoleDelta > 0)
                {
                    giverTiles[giverTilesLength++] = tile;
                }
                else
                {
                    takerTiles[takerTilesLength++] = tile;
                }
            }

            var logN = MathF.Log2(tileCount);

            // Optimization - try to spread gases using an O(nlogn) algorithm that has a chance of not working first to avoid O(n^2)
            if (giverTilesLength > logN && takerTilesLength > logN)
            {
                // Even if it fails, it will speed up the next part.
                Array.Sort(tiles, 0, tileCount, Comparer);

                for (var i = 0; i < tileCount; i++)
                {
                    var tile = tiles[i];
                    tile._tileAtmosInfo.FastDone = true;
                    if (!(tile._tileAtmosInfo.MoleDelta > 0)) continue;
                    var eligibleDirections = AtmosDirection.Invalid;
                    var eligibleDirectionCount = 0;
                    for (var j = 0; j < Atmospherics.Directions; j++)
                    {
                        var direction = (AtmosDirection) (1 << j);
                        if (!tile._adjacentBits.HasFlag(direction)) continue;
                        var tile2 = tile._adjacentTiles[j];

                        // skip anything that isn't part of our current processing block.
                        if (tile2._tileAtmosInfo.FastDone || tile2._tileAtmosInfo.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 = tile._tileAtmosInfo.MoleDelta / eligibleDirectionCount;
                    for (var j = 0; j < Atmospherics.Directions; j++)
                    {
                        var direction = (AtmosDirection) (1 << j);
                        if (!eligibleDirections.HasFlag(direction)) continue;

                        tile.AdjustEqMovement(direction, molesToMove);
                        tile._tileAtmosInfo.MoleDelta -= molesToMove;
                        tile._adjacentTiles[j]._tileAtmosInfo.MoleDelta += molesToMove;
                    }
                }

                giverTilesLength = 0;
                takerTilesLength = 0;

                for (var i = 0; i < tileCount; i++)
                {
                    var tile = tiles[i];
                    if (tile._tileAtmosInfo.MoleDelta > 0)
                    {
                        giverTiles[giverTilesLength++] = tile;
                    }
                    else
                    {
                        takerTiles[takerTilesLength++] = tile;
                    }
                }

                // 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.
                    var queue = ArrayPool<TileAtmosphere>.Shared.Rent(tileCount);
                    for (var j = 0; j < giverTilesLength; j++)
                    {
                        var giver = giverTiles[j];
                        giver._tileAtmosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
                        giver._tileAtmosInfo.CurrentTransferAmount = 0;
                        var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
                        var queueLength = 0;
                        queue[queueLength++] = giver;
                        giver._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
                        for (var i = 0; i < queueLength; i++)
                        {
                            if (giver._tileAtmosInfo.MoleDelta <= 0)
                                break; // We're done here now. Let's not do more work than needed.

                            var tile = queue[i];
                            for (var k = 0; k < Atmospherics.Directions; k++)
                            {
                                var direction = (AtmosDirection) (1 << k);
                                if (!tile._adjacentBits.HasFlag(direction)) continue;
                                var tile2 = tile._adjacentTiles[k];
                                if (giver._tileAtmosInfo.MoleDelta <= 0) break; // We're done here now. Let's not do more work than needed.
                                if (tile2._tileAtmosInfo.LastQueueCycle != queueCycle) continue;
                                if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue;

                                queue[queueLength++] = tile2;
                                tile2._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
                                tile2._tileAtmosInfo.CurrentTransferDirection = direction.GetOpposite();
                                tile2._tileAtmosInfo.CurrentTransferAmount = 0;
                                if (tile2._tileAtmosInfo.MoleDelta < 0)
                                {
                                    // This tile needs gas. Let's give it to 'em.
                                    if (-tile2._tileAtmosInfo.MoleDelta > giver._tileAtmosInfo.MoleDelta)
                                    {
                                        // We don't have enough gas!
                                        tile2._tileAtmosInfo.CurrentTransferAmount -= giver._tileAtmosInfo.MoleDelta;
                                        tile2._tileAtmosInfo.MoleDelta += giver._tileAtmosInfo.MoleDelta;
                                        giver._tileAtmosInfo.MoleDelta = 0;
                                    }
                                    else
                                    {
                                        // We have enough gas.
                                        tile2._tileAtmosInfo.CurrentTransferAmount += tile2._tileAtmosInfo.MoleDelta;
                                        giver._tileAtmosInfo.MoleDelta += tile2._tileAtmosInfo.MoleDelta;
                                        tile2._tileAtmosInfo.MoleDelta = 0;
                                    }
                                }
                            }
                        }

                        // Putting this loop here helps make it O(n^2) over O(n^3)
                        for (var i = queueLength - 1; i >= 0; i--)
                        {
                            var tile = queue[i];
                            if (tile._tileAtmosInfo.CurrentTransferAmount != 0 && tile._tileAtmosInfo.CurrentTransferDirection != AtmosDirection.Invalid)
                            {
                                tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount);
                                tile._adjacentTiles[tile._tileAtmosInfo.CurrentTransferDirection.ToIndex()]
                                    ._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
                                tile._tileAtmosInfo.CurrentTransferAmount = 0;
                            }
                        }
                    }

                    ArrayPool<TileAtmosphere>.Shared.Return(queue);
                }
                else
                {
                    var queue = ArrayPool<TileAtmosphere>.Shared.Rent(tileCount);
                    for (var j = 0; j < takerTilesLength; j++)
                    {
                        var taker = takerTiles[j];
                        taker._tileAtmosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
                        taker._tileAtmosInfo.CurrentTransferAmount = 0;
                        var queueCycleSlow = ++_gridAtmosphereComponent.EqualizationQueueCycleControl;
                        var queueLength = 0;
                        queue[queueLength++] = taker;
                        taker._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
                        for (var i = 0; i < queueLength; i++)
                        {
                            if (taker._tileAtmosInfo.MoleDelta >= 0)
                                break; // We're done here now. Let's not do more work than needed.

                            var tile = queue[i];
                            for (var k = 0; k < Atmospherics.Directions; k++)
                            {
                                var direction = (AtmosDirection) (1 << k);
                                if (!tile._adjacentBits.HasFlag(direction)) continue;
                                var tile2 = tile._adjacentTiles[k];

                                if (taker._tileAtmosInfo.MoleDelta >= 0) break; // We're done here now. Let's not do more work than needed.
                                if (tile2._tileAtmosInfo.LastQueueCycle != queueCycle) continue;
                                if (tile2._tileAtmosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
                                queue[queueLength++] = tile2;
                                tile2._tileAtmosInfo.LastSlowQueueCycle = queueCycleSlow;
                                tile2._tileAtmosInfo.CurrentTransferDirection = direction.GetOpposite();
                                tile2._tileAtmosInfo.CurrentTransferAmount = 0;

                                if (tile2._tileAtmosInfo.MoleDelta > 0)
                                {
                                    // This tile has gas we can suck, so let's
                                    if (tile2._tileAtmosInfo.MoleDelta > -taker._tileAtmosInfo.MoleDelta)
                                    {
                                        // They have enough gas
                                        tile2._tileAtmosInfo.CurrentTransferAmount -= taker._tileAtmosInfo.MoleDelta;
                                        tile2._tileAtmosInfo.MoleDelta += taker._tileAtmosInfo.MoleDelta;
                                        taker._tileAtmosInfo.MoleDelta = 0;
                                    }
                                    else
                                    {
                                        // They don't have enough gas!
                                        tile2._tileAtmosInfo.CurrentTransferAmount += tile2._tileAtmosInfo.MoleDelta;
                                        taker._tileAtmosInfo.MoleDelta += tile2._tileAtmosInfo.MoleDelta;
                                        tile2._tileAtmosInfo.MoleDelta = 0;
                                    }
                                }
                            }
                        }

                        for (var i = queueLength - 1; i >= 0; i--)
                        {
                            var tile = queue[i];
                            if (tile._tileAtmosInfo.CurrentTransferAmount == 0 || tile._tileAtmosInfo.CurrentTransferDirection == AtmosDirection.Invalid)
                                continue;

                            tile.AdjustEqMovement(tile._tileAtmosInfo.CurrentTransferDirection, tile._tileAtmosInfo.CurrentTransferAmount);

                            tile._adjacentTiles[tile._tileAtmosInfo.CurrentTransferDirection.ToIndex()]
                                ._tileAtmosInfo.CurrentTransferAmount += tile._tileAtmosInfo.CurrentTransferAmount;
                            tile._tileAtmosInfo.CurrentTransferAmount = 0;
                        }
                    }

                    ArrayPool<TileAtmosphere>.Shared.Return(queue);
                }

                for (var i = 0; i < tileCount; i++)
                {
                    var tile = tiles[i];
                    tile.FinalizeEq();
                }

                for (var i = 0; i < tileCount; i++)
                {
                    var tile = tiles[i];
                    for (var j = 0; j < Atmospherics.Directions; j++)
                    {
                        var direction = (AtmosDirection) (1 << j);
                        if (!tile._adjacentBits.HasFlag(direction)) continue;
                        var tile2 = tile._adjacentTiles[j];
                        if (tile2?.Air?.Compare(Air) == GasMixture.GasCompareResult.NoExchange) continue;
                        _gridAtmosphereComponent.AddActiveTile(tile2);
                        break;
                    }
                }

                ArrayPool<TileAtmosphere>.Shared.Return(tiles);
                ArrayPool<TileAtmosphere>.Shared.Return(giverTiles);
                ArrayPool<TileAtmosphere>.Shared.Return(takerTiles);
            }
        }