ECS Atmos Part 4: Moves all atmos logic from TileAtmosphere to AtmosphereSystem. (#4295)
* Moves all atmos logic from TileAtmosphere to AtmosphereSystem. * Atmos uses grid anchoring to check for firelocks instead of grid tile lookups. * CVar for space wind sound * CVar for explosive depressurization
This commit is contained in:
committed by
GitHub
parent
e1fdd902bb
commit
fcafa8f439
@@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Alert;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Stunnable.Components;
|
||||
using Content.Server.Temperature.Components;
|
||||
using Content.Shared.ActionBlocker;
|
||||
@@ -122,7 +123,7 @@ namespace Content.Server.Atmos.Components
|
||||
return;
|
||||
}
|
||||
|
||||
tile.HotspotExpose(700, 50, true);
|
||||
EntitySystem.Get<AtmosphereSystem>().HotspotExpose(tile.GridIndex, tile.GridIndices, 700f, 50f, true);
|
||||
|
||||
var physics = Owner.GetComponent<IPhysBody>();
|
||||
|
||||
|
||||
@@ -36,7 +36,6 @@ namespace Content.Server.Atmos.Components
|
||||
[Dependency] private IServerEntityManager _serverEntityManager = default!;
|
||||
[Dependency] private IGameTiming _gameTiming = default!;
|
||||
|
||||
public GridTileLookupSystem GridTileLookupSystem { get; private set; } = default!;
|
||||
internal GasTileOverlaySystem GasTileOverlaySystem { get; private set; } = default!;
|
||||
public AtmosphereSystem AtmosphereSystem { get; private set; } = default!;
|
||||
|
||||
@@ -182,7 +181,6 @@ namespace Content.Server.Atmos.Components
|
||||
}
|
||||
}
|
||||
|
||||
GridTileLookupSystem = EntitySystem.Get<GridTileLookupSystem>();
|
||||
GasTileOverlaySystem = EntitySystem.Get<GasTileOverlaySystem>();
|
||||
AtmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
|
||||
|
||||
@@ -109,7 +109,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
gases[i] = tile.Air.GetMoles(i);
|
||||
}
|
||||
return new AtmosDebugOverlayData(tile.Air.Temperature, gases, tile.PressureDirectionForDebugOverlay, tile.ExcitedGroup != null, tile.BlockedAirflow);
|
||||
return new AtmosDebugOverlayData(tile.Air.Temperature, gases, tile.PressureDirection, tile.ExcitedGroup != null, tile.BlockedAirflow);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -5,7 +5,9 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
public partial class AtmosphereSystem
|
||||
{
|
||||
public bool SpaceWind { get; private set; }
|
||||
public string? SpaceWindSound { get; private set; }
|
||||
public bool MonstermosEqualization { get; private set; }
|
||||
public bool MonstermosDepressurization { get; private set; }
|
||||
public bool Superconduction { get; private set; }
|
||||
public bool ExcitedGroupsSpaceIsAllConsuming { get; private set; }
|
||||
public float AtmosMaxProcessTime { get; private set; }
|
||||
@@ -15,7 +17,9 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
private void InitializeCVars()
|
||||
{
|
||||
_cfg.OnValueChanged(CCVars.SpaceWind, value => SpaceWind = value, true);
|
||||
_cfg.OnValueChanged(CCVars.SpaceWindSound, value => SpaceWindSound = value, true);
|
||||
_cfg.OnValueChanged(CCVars.MonstermosEqualization, value => MonstermosEqualization = value, true);
|
||||
_cfg.OnValueChanged(CCVars.MonstermosDepressurization, value => MonstermosDepressurization = value, true);
|
||||
_cfg.OnValueChanged(CCVars.Superconduction, value => Superconduction = value, true);
|
||||
_cfg.OnValueChanged(CCVars.AtmosMaxProcessTime, value => AtmosMaxProcessTime = value, true);
|
||||
_cfg.OnValueChanged(CCVars.AtmosTickRate, value => AtmosTickRate = value, true);
|
||||
|
||||
@@ -163,15 +163,15 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
if (ComponentManager.TryGetComponent(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
|
||||
{
|
||||
foreach (var tileAtmos in gridAtmosphere)
|
||||
foreach (var tile in gridAtmosphere)
|
||||
{
|
||||
if (tileAtmos?.Air == null)
|
||||
if (tile?.Air == null)
|
||||
continue;
|
||||
|
||||
if(invalidate)
|
||||
tileAtmos.Invalidate();
|
||||
gridAtmosphere.Invalidate(tile.GridIndices);
|
||||
|
||||
yield return tileAtmos.Air;
|
||||
yield return tile.Air;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -560,7 +560,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
var tileAtmos = gridAtmosphere.GetTile(tile)!;
|
||||
|
||||
for (var i = 0; i < tileAtmos.AdjacentTiles.Count; i++)
|
||||
for (var i = 0; i < tileAtmos.AdjacentTiles.Length; i++)
|
||||
{
|
||||
var adjacentTile = tileAtmos.AdjacentTiles[i];
|
||||
// TileAtmosphere has nullable disabled, so just in case...
|
||||
@@ -634,7 +634,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
var tileAtmos = gridAtmosphere.GetTile(tile)!;
|
||||
|
||||
for (var i = 0; i < tileAtmos.AdjacentTiles.Count; i++)
|
||||
for (var i = 0; i < tileAtmos.AdjacentTiles.Length; i++)
|
||||
{
|
||||
var adjacentTile = tileAtmos.AdjacentTiles[i];
|
||||
|
||||
@@ -651,7 +651,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
}
|
||||
|
||||
if (invalidate)
|
||||
adjacentTile.Invalidate();
|
||||
gridAtmosphere.Invalidate(adjacentTile.GridIndices);
|
||||
|
||||
yield return adjacentTile.Air;
|
||||
}
|
||||
@@ -747,7 +747,12 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
if (ComponentManager.TryGetComponent(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
|
||||
{
|
||||
gridAtmosphere.GetTile(tile, false)?.HotspotExpose(exposedTemperature, exposedVolume, soh);
|
||||
var tileAtmosphere = gridAtmosphere.GetTile(tile, false);
|
||||
|
||||
if (tileAtmosphere == null)
|
||||
return;
|
||||
|
||||
HotspotExpose(gridAtmosphere, tileAtmosphere, exposedTemperature, exposedVolume, soh);
|
||||
gridAtmosphere.Invalidate(tile);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Audio;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
public partial class AtmosphereSystem
|
||||
{
|
||||
private int _spaceWindSoundCooldown = 0;
|
||||
|
||||
private void HighPressureMovements(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile)
|
||||
{
|
||||
// TODO ATMOS finish this
|
||||
|
||||
if(tile.PressureDifference > 15)
|
||||
{
|
||||
if(_spaceWindSoundCooldown == 0)
|
||||
{
|
||||
var coordinates = tile.GridIndices.ToEntityCoordinates(tile.GridIndex, _mapManager);
|
||||
if(!string.IsNullOrEmpty(SpaceWindSound))
|
||||
SoundSystem.Play(Filter.Pvs(coordinates), SpaceWindSound, coordinates,
|
||||
AudioHelpers.WithVariation(0.125f).WithVolume(MathHelper.Clamp(tile.PressureDifference / 10, 10, 100)));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (var entity in Get<GridTileLookupSystem>().GetEntitiesIntersecting(tile.GridIndex, tile.GridIndices))
|
||||
{
|
||||
if (!entity.TryGetComponent(out IPhysBody? physics)
|
||||
|| !entity.IsMovedByPressure(out var pressure)
|
||||
|| entity.IsInContainer())
|
||||
continue;
|
||||
|
||||
var pressureMovements = physics.Owner.EnsureComponent<MovedByPressureComponent>();
|
||||
if (pressure.LastHighPressureMovementAirCycle < gridAtmosphere.UpdateCounter)
|
||||
{
|
||||
pressureMovements.ExperiencePressureDifference(gridAtmosphere.UpdateCounter, tile.PressureDifference, tile.PressureDirection, 0, tile.PressureSpecificTarget?.GridIndices.ToEntityCoordinates(tile.GridIndex, _mapManager) ?? EntityCoordinates.Invalid);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (tile.PressureDifference > 100)
|
||||
{
|
||||
// TODO ATMOS Do space wind graphics here!
|
||||
}
|
||||
|
||||
_spaceWindSoundCooldown++;
|
||||
if (_spaceWindSoundCooldown > 75)
|
||||
_spaceWindSoundCooldown = 0;
|
||||
}
|
||||
|
||||
private void ConsiderPressureDifference(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, TileAtmosphere other, float difference)
|
||||
{
|
||||
gridAtmosphere.AddHighPressureDelta(tile);
|
||||
if (difference > tile.PressureDifference)
|
||||
{
|
||||
tile.PressureDifference = difference;
|
||||
tile.PressureDirection = ((Vector2i)(tile.GridIndices - other.GridIndices)).GetDir().ToAtmosDirection();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
150
Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs
Normal file
150
Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs
Normal file
@@ -0,0 +1,150 @@
|
||||
#nullable disable warnings
|
||||
#nullable enable annotations
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.Reactions;
|
||||
using Content.Server.Coordinates.Helpers;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.GameTicking;
|
||||
using Content.Shared.Maps;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
public partial class AtmosphereSystem
|
||||
{
|
||||
private void ProcessHotspot(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile)
|
||||
{
|
||||
if (!tile.Hotspot.Valid)
|
||||
{
|
||||
gridAtmosphere.RemoveHotspotTile(tile);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!tile.Excited)
|
||||
{
|
||||
gridAtmosphere.AddActiveTile(tile);
|
||||
}
|
||||
|
||||
if (!tile.Hotspot.SkippedFirstProcess)
|
||||
{
|
||||
tile.Hotspot.SkippedFirstProcess = true;
|
||||
return;
|
||||
}
|
||||
|
||||
tile.ExcitedGroup?.ResetCooldowns();
|
||||
|
||||
if ((tile.Hotspot.Temperature < Atmospherics.FireMinimumTemperatureToExist) || (tile.Hotspot.Volume <= 1f)
|
||||
|| tile.Air == null || tile.Air.GetMoles(Gas.Oxygen) < 0.5f || (tile.Air.GetMoles(Gas.Plasma) < 0.5f && tile.Air.GetMoles(Gas.Tritium) < 0.5f))
|
||||
{
|
||||
tile.Hotspot = new Hotspot();
|
||||
tile.UpdateVisuals();
|
||||
return;
|
||||
}
|
||||
|
||||
PerformHotspotExposure(gridAtmosphere, tile);
|
||||
|
||||
if (tile.Hotspot.Bypassing)
|
||||
{
|
||||
tile.Hotspot.State = 3;
|
||||
gridAtmosphere.BurnTile(tile.GridIndices);
|
||||
|
||||
if (tile.Air.Temperature > Atmospherics.FireMinimumTemperatureToSpread)
|
||||
{
|
||||
var radiatedTemperature = tile.Air.Temperature * Atmospherics.FireSpreadRadiosityScale;
|
||||
foreach (var otherTile in tile.AdjacentTiles)
|
||||
{
|
||||
if(!otherTile.Hotspot.Valid)
|
||||
HotspotExpose(gridAtmosphere, otherTile, radiatedTemperature, Atmospherics.CellVolume/4);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
tile.Hotspot.State = (byte) (tile.Hotspot.Volume > Atmospherics.CellVolume * 0.4f ? 2 : 1);
|
||||
}
|
||||
|
||||
if (tile.Hotspot.Temperature > tile.MaxFireTemperatureSustained)
|
||||
tile.MaxFireTemperatureSustained = tile.Hotspot.Temperature;
|
||||
|
||||
// TODO ATMOS Maybe destroy location here?
|
||||
}
|
||||
|
||||
private void HotspotExpose(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, float exposedTemperature, float exposedVolume, bool soh = false)
|
||||
{
|
||||
if (tile.Air == null)
|
||||
return;
|
||||
|
||||
var oxygen = tile.Air.GetMoles(Gas.Oxygen);
|
||||
|
||||
if (oxygen < 0.5f)
|
||||
return;
|
||||
|
||||
var plasma = tile.Air.GetMoles(Gas.Plasma);
|
||||
var tritium = tile.Air.GetMoles(Gas.Tritium);
|
||||
|
||||
if (tile.Hotspot.Valid)
|
||||
{
|
||||
if (soh)
|
||||
{
|
||||
if (plasma > 0.5f || tritium > 0.5f)
|
||||
{
|
||||
if (tile.Hotspot.Temperature < exposedTemperature)
|
||||
tile.Hotspot.Temperature = exposedTemperature;
|
||||
if (tile.Hotspot.Volume < exposedVolume)
|
||||
tile.Hotspot.Volume = exposedVolume;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ((exposedTemperature > Atmospherics.PlasmaMinimumBurnTemperature) && (plasma > 0.5f || tritium > 0.5f))
|
||||
{
|
||||
tile.Hotspot = new Hotspot
|
||||
{
|
||||
Volume = exposedVolume * 25f,
|
||||
Temperature = exposedTemperature,
|
||||
SkippedFirstProcess = tile.CurrentCycle > gridAtmosphere.UpdateCounter
|
||||
};
|
||||
|
||||
tile.Hotspot.Start();
|
||||
|
||||
gridAtmosphere.AddActiveTile(tile);
|
||||
gridAtmosphere.AddHotspotTile(tile);
|
||||
}
|
||||
}
|
||||
|
||||
private void PerformHotspotExposure(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile)
|
||||
{
|
||||
if (tile.Air == null || !tile.Hotspot.Valid) return;
|
||||
|
||||
tile.Hotspot.Bypassing = tile.Hotspot.SkippedFirstProcess && tile.Hotspot.Volume > tile.Air.Volume*0.95f;
|
||||
|
||||
if (tile.Hotspot.Bypassing)
|
||||
{
|
||||
tile.Hotspot.Volume = tile.Air.ReactionResults[GasReaction.Fire] * Atmospherics.FireGrowthRate;
|
||||
tile.Hotspot.Temperature = tile.Air.Temperature;
|
||||
}
|
||||
else
|
||||
{
|
||||
var affected = tile.Air.RemoveRatio(tile.Hotspot.Volume / tile.Air.Volume);
|
||||
affected.Temperature = tile.Hotspot.Temperature;
|
||||
gridAtmosphere.AtmosphereSystem.React(affected, tile);
|
||||
tile.Hotspot.Temperature = affected.Temperature;
|
||||
tile.Hotspot.Volume = affected.ReactionResults[GasReaction.Fire] * Atmospherics.FireGrowthRate;
|
||||
Merge(tile.Air, affected);
|
||||
gridAtmosphere.Invalidate(tile.GridIndices);
|
||||
}
|
||||
|
||||
var tileRef = tile.GridIndices.GetTileRef(tile.GridIndex);
|
||||
|
||||
foreach (var entity in tileRef.GetEntitiesInTileFast())
|
||||
{
|
||||
foreach (var fireAct in entity.GetAllComponents<IFireAct>())
|
||||
{
|
||||
|
||||
fireAct.FireAct(tile.Hotspot.Temperature, tile.Hotspot.Volume);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
135
Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs
Normal file
135
Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs
Normal file
@@ -0,0 +1,135 @@
|
||||
#nullable disable warnings
|
||||
#nullable enable annotations
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Shared.Atmos;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
public partial class AtmosphereSystem
|
||||
{
|
||||
private void ProcessCell(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int fireCount, bool spaceWind = true)
|
||||
{
|
||||
// Can't process a tile without air
|
||||
if (tile.Air == null)
|
||||
{
|
||||
gridAtmosphere.RemoveActiveTile(tile);
|
||||
return;
|
||||
}
|
||||
|
||||
if (tile.ArchivedCycle < fireCount)
|
||||
Archive(tile, fireCount);
|
||||
|
||||
tile.CurrentCycle = fireCount;
|
||||
var adjacentTileLength = 0;
|
||||
|
||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << i);
|
||||
if(tile.AdjacentBits.IsFlagSet(direction))
|
||||
adjacentTileLength++;
|
||||
}
|
||||
|
||||
for(var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << i);
|
||||
if (!tile.AdjacentBits.IsFlagSet(direction)) continue;
|
||||
var enemyTile = tile.AdjacentTiles[i];
|
||||
|
||||
// If the tile is null or has no air, we don't do anything for it.
|
||||
if(enemyTile?.Air == null) continue;
|
||||
if (fireCount <= enemyTile.CurrentCycle) continue;
|
||||
Archive(enemyTile, fireCount);
|
||||
|
||||
var shouldShareAir = false;
|
||||
|
||||
if (tile.ExcitedGroup != null && enemyTile.ExcitedGroup != null)
|
||||
{
|
||||
if (tile.ExcitedGroup != enemyTile.ExcitedGroup)
|
||||
{
|
||||
tile.ExcitedGroup.MergeGroups(enemyTile.ExcitedGroup);
|
||||
}
|
||||
|
||||
shouldShareAir = true;
|
||||
} else if (tile.Air!.Compare(enemyTile.Air!) != GasMixture.GasCompareResult.NoExchange)
|
||||
{
|
||||
if (!enemyTile.Excited)
|
||||
{
|
||||
gridAtmosphere.AddActiveTile(enemyTile);
|
||||
}
|
||||
|
||||
var excitedGroup = tile.ExcitedGroup;
|
||||
excitedGroup ??= enemyTile.ExcitedGroup;
|
||||
|
||||
if (excitedGroup == null)
|
||||
{
|
||||
excitedGroup = new ExcitedGroup();
|
||||
excitedGroup.Initialize(gridAtmosphere);
|
||||
}
|
||||
|
||||
if (tile.ExcitedGroup == null)
|
||||
excitedGroup.AddTile(tile);
|
||||
|
||||
if(enemyTile.ExcitedGroup == null)
|
||||
excitedGroup.AddTile(enemyTile);
|
||||
|
||||
shouldShareAir = true;
|
||||
}
|
||||
|
||||
if (shouldShareAir)
|
||||
{
|
||||
var difference = Share(tile.Air!, enemyTile.Air!, adjacentTileLength);
|
||||
|
||||
if (spaceWind)
|
||||
{
|
||||
if (difference > 0)
|
||||
{
|
||||
ConsiderPressureDifference(gridAtmosphere, tile, enemyTile, difference);
|
||||
}
|
||||
else
|
||||
{
|
||||
ConsiderPressureDifference(gridAtmosphere, enemyTile, tile, -difference);
|
||||
}
|
||||
}
|
||||
|
||||
LastShareCheck(tile);
|
||||
}
|
||||
}
|
||||
|
||||
if(tile.Air != null)
|
||||
React(tile.Air, tile);
|
||||
tile.UpdateVisuals();
|
||||
|
||||
var remove = true;
|
||||
|
||||
if(tile.Air!.Temperature > Atmospherics.MinimumTemperatureStartSuperConduction)
|
||||
if (ConsiderSuperconductivity(gridAtmosphere, tile, true))
|
||||
remove = false;
|
||||
|
||||
if(tile.ExcitedGroup == null && remove)
|
||||
gridAtmosphere.RemoveActiveTile(tile);
|
||||
}
|
||||
|
||||
private void Archive(TileAtmosphere tile, int fireCount)
|
||||
{
|
||||
tile.Air?.Archive();
|
||||
tile.ArchivedCycle = fireCount;
|
||||
tile.TemperatureArchived = tile.Temperature;
|
||||
}
|
||||
|
||||
private void LastShareCheck(TileAtmosphere tile)
|
||||
{
|
||||
if (tile.Air == null || tile.ExcitedGroup == null)
|
||||
return;
|
||||
|
||||
switch (tile.Air.LastShare)
|
||||
{
|
||||
case > Atmospherics.MinimumAirToSuspend:
|
||||
tile.ExcitedGroup.ResetCooldowns();
|
||||
break;
|
||||
case > Atmospherics.MinimumMolesDeltaToMove:
|
||||
tile.ExcitedGroup.DismantleCooldown = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,576 @@
|
||||
#nullable disable warnings
|
||||
#nullable enable annotations
|
||||
using System;
|
||||
using System.Buffers;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Coordinates.Helpers;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
public partial class AtmosphereSystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
|
||||
private readonly TileAtmosphereComparer _monstermosComparer = new();
|
||||
|
||||
public void EqualizePressureInZone(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum)
|
||||
{
|
||||
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;
|
||||
var tiles = ArrayPool<TileAtmosphere>.Shared.Rent(Atmospherics.MonstermosHardTileLimit);
|
||||
tiles[0] = tile;
|
||||
tile.MonstermosInfo.LastQueueCycle = queueCycle;
|
||||
var tileCount = 1;
|
||||
for (var i = 0; i < tileCount; i++)
|
||||
{
|
||||
if (i > Atmospherics.MonstermosHardTileLimit) break;
|
||||
var exploring = tiles[i];
|
||||
|
||||
if (i < Atmospherics.MonstermosTileLimit)
|
||||
{
|
||||
var tileMoles = exploring.Air.TotalMoles;
|
||||
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)
|
||||
tiles[tileCount++] = adj;
|
||||
|
||||
if (adj.Air.Immutable)
|
||||
{
|
||||
// Looks like someone opened an airlock to space!
|
||||
ExplosivelyDepressurize(gridAtmosphere, tile, cycleNum);
|
||||
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.
|
||||
var otherTile = tiles[i];
|
||||
|
||||
if (otherTile == null)
|
||||
continue;
|
||||
|
||||
tiles[i].MonstermosInfo.LastQueueCycle = 0;
|
||||
}
|
||||
|
||||
tileCount = Atmospherics.MonstermosTileLimit;
|
||||
}
|
||||
|
||||
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 otherTile = tiles[i];
|
||||
otherTile.MonstermosInfo.LastCycle = cycleNum;
|
||||
otherTile.MonstermosInfo.MoleDelta -= averageMoles;
|
||||
if (otherTile.MonstermosInfo.MoleDelta > 0)
|
||||
{
|
||||
giverTiles[giverTilesLength++] = otherTile;
|
||||
}
|
||||
else
|
||||
{
|
||||
takerTiles[takerTilesLength++] = otherTile;
|
||||
}
|
||||
}
|
||||
|
||||
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, _monstermosComparer);
|
||||
|
||||
for (var i = 0; i < tileCount; i++)
|
||||
{
|
||||
var otherTile = tiles[i];
|
||||
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;
|
||||
var tile2 = otherTile.AdjacentTiles[j];
|
||||
|
||||
// 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;
|
||||
otherTile.AdjacentTiles[j].MonstermosInfo.MoleDelta += molesToMove;
|
||||
}
|
||||
}
|
||||
|
||||
giverTilesLength = 0;
|
||||
takerTilesLength = 0;
|
||||
|
||||
for (var i = 0; i < tileCount; i++)
|
||||
{
|
||||
var otherTile = tiles[i];
|
||||
if (otherTile.MonstermosInfo.MoleDelta > 0)
|
||||
{
|
||||
giverTiles[giverTilesLength++] = otherTile;
|
||||
}
|
||||
else
|
||||
{
|
||||
takerTiles[takerTilesLength++] = otherTile;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
|
||||
giver.MonstermosInfo.CurrentTransferAmount = 0;
|
||||
var queueCycleSlow = ++gridAtmosphere.EqualizationQueueCycleControl;
|
||||
var queueLength = 0;
|
||||
queue[queueLength++] = giver;
|
||||
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.
|
||||
|
||||
var otherTile = queue[i];
|
||||
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;
|
||||
if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
||||
|
||||
queue[queueLength++] = otherTile2;
|
||||
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--)
|
||||
{
|
||||
var otherTile = queue[i];
|
||||
if (otherTile.MonstermosInfo.CurrentTransferAmount != 0 && otherTile.MonstermosInfo.CurrentTransferDirection != AtmosDirection.Invalid)
|
||||
{
|
||||
AdjustEqMovement(otherTile, otherTile.MonstermosInfo.CurrentTransferDirection, otherTile.MonstermosInfo.CurrentTransferAmount);
|
||||
otherTile.AdjacentTiles[otherTile.MonstermosInfo.CurrentTransferDirection.ToIndex()]
|
||||
.MonstermosInfo.CurrentTransferAmount += otherTile.MonstermosInfo.CurrentTransferAmount;
|
||||
otherTile.MonstermosInfo.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.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
|
||||
taker.MonstermosInfo.CurrentTransferAmount = 0;
|
||||
var queueCycleSlow = ++gridAtmosphere.EqualizationQueueCycleControl;
|
||||
var queueLength = 0;
|
||||
queue[queueLength++] = taker;
|
||||
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.
|
||||
|
||||
var otherTile = queue[i];
|
||||
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.
|
||||
if (otherTile2 == null || otherTile2.MonstermosInfo.LastQueueCycle != queueCycle) continue;
|
||||
if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
||||
queue[queueLength++] = otherTile2;
|
||||
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--)
|
||||
{
|
||||
var otherTile = queue[i];
|
||||
if (otherTile.MonstermosInfo.CurrentTransferAmount == 0 || otherTile.MonstermosInfo.CurrentTransferDirection == AtmosDirection.Invalid)
|
||||
continue;
|
||||
|
||||
AdjustEqMovement(otherTile, otherTile.MonstermosInfo.CurrentTransferDirection, otherTile.MonstermosInfo.CurrentTransferAmount);
|
||||
|
||||
otherTile.AdjacentTiles[otherTile.MonstermosInfo.CurrentTransferDirection.ToIndex()]
|
||||
.MonstermosInfo.CurrentTransferAmount += otherTile.MonstermosInfo.CurrentTransferAmount;
|
||||
otherTile.MonstermosInfo.CurrentTransferAmount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayPool<TileAtmosphere>.Shared.Return(queue);
|
||||
}
|
||||
|
||||
for (var i = 0; i < tileCount; i++)
|
||||
{
|
||||
var otherTile = tiles[i];
|
||||
FinalizeEq(gridAtmosphere, otherTile);
|
||||
}
|
||||
|
||||
for (var i = 0; i < tileCount; i++)
|
||||
{
|
||||
var otherTile = tiles[i];
|
||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << j);
|
||||
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
||||
var otherTile2 = otherTile.AdjacentTiles[j];
|
||||
if (otherTile2?.Air?.Compare(tile.Air) == GasMixture.GasCompareResult.NoExchange) continue;
|
||||
gridAtmosphere.AddActiveTile(otherTile2);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ArrayPool<TileAtmosphere>.Shared.Return(tiles);
|
||||
ArrayPool<TileAtmosphere>.Shared.Return(giverTiles);
|
||||
ArrayPool<TileAtmosphere>.Shared.Return(takerTiles);
|
||||
}
|
||||
|
||||
public void ExplosivelyDepressurize(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int cycleNum)
|
||||
{
|
||||
// Check if explosive depressurization is enabled and if the tile is valid.
|
||||
if (!MonstermosDepressurization || tile.Air == null)
|
||||
return;
|
||||
|
||||
const int limit = Atmospherics.MonstermosHardTileLimit;
|
||||
|
||||
var totalGasesRemoved = 0f;
|
||||
var queueCycle = ++gridAtmosphere.EqualizationQueueCycleControl;
|
||||
var tiles = ArrayPool<TileAtmosphere>.Shared.Rent(limit);
|
||||
var spaceTiles = ArrayPool<TileAtmosphere>.Shared.Rent(limit);
|
||||
|
||||
var tileCount = 0;
|
||||
var spaceTileCount = 0;
|
||||
|
||||
tiles[tileCount++] = tile;
|
||||
|
||||
tile.MonstermosInfo = new MonstermosInfo {LastQueueCycle = queueCycle};
|
||||
|
||||
for (var i = 0; i < tileCount; i++)
|
||||
{
|
||||
var otherTile = tiles[i];
|
||||
otherTile.MonstermosInfo.LastCycle = cycleNum;
|
||||
otherTile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
|
||||
if (otherTile.Air.Immutable)
|
||||
{
|
||||
spaceTiles[spaceTileCount++] = otherTile;
|
||||
otherTile.PressureSpecificTarget = otherTile;
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << j);
|
||||
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
||||
var otherTile2 = otherTile.AdjacentTiles[j];
|
||||
if (otherTile2.Air == null) continue;
|
||||
if (otherTile2.MonstermosInfo.LastQueueCycle == queueCycle) continue;
|
||||
|
||||
ConsiderFirelocks(otherTile, otherTile2);
|
||||
|
||||
// The firelocks might have closed on us.
|
||||
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
||||
otherTile2.MonstermosInfo = new MonstermosInfo {LastQueueCycle = queueCycle};
|
||||
tiles[tileCount++] = otherTile2;
|
||||
}
|
||||
}
|
||||
|
||||
if (tileCount >= limit || spaceTileCount >= limit)
|
||||
break;
|
||||
}
|
||||
|
||||
var queueCycleSlow = ++gridAtmosphere.EqualizationQueueCycleControl;
|
||||
var progressionOrder = ArrayPool<TileAtmosphere>.Shared.Rent(limit * 2);
|
||||
var progressionCount = 0;
|
||||
|
||||
for (var i = 0; i < spaceTileCount; i++)
|
||||
{
|
||||
var otherTile = spaceTiles[i];
|
||||
progressionOrder[progressionCount++] = otherTile;
|
||||
otherTile.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow;
|
||||
otherTile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
|
||||
}
|
||||
|
||||
for (var i = 0; i < progressionCount; i++)
|
||||
{
|
||||
var otherTile = progressionOrder[i];
|
||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << j);
|
||||
// TODO ATMOS This is a terrible hack that accounts for the mess that are space TileAtmospheres.
|
||||
if (!otherTile.AdjacentBits.IsFlagSet(direction) && !otherTile.Air.Immutable) continue;
|
||||
var tile2 = otherTile.AdjacentTiles[j];
|
||||
if (tile2?.MonstermosInfo.LastQueueCycle != queueCycle) continue;
|
||||
if (tile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
||||
if(tile2.Air?.Immutable ?? false) continue;
|
||||
tile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite();
|
||||
tile2.MonstermosInfo.CurrentTransferAmount = 0;
|
||||
tile2.PressureSpecificTarget = otherTile.PressureSpecificTarget;
|
||||
tile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow;
|
||||
progressionOrder[progressionCount++] = tile2;
|
||||
}
|
||||
}
|
||||
|
||||
for (var i = progressionCount - 1; i >= 0; i--)
|
||||
{
|
||||
var otherTile = progressionOrder[i];
|
||||
if (otherTile.MonstermosInfo.CurrentTransferDirection == AtmosDirection.Invalid) continue;
|
||||
gridAtmosphere.AddHighPressureDelta(otherTile);
|
||||
gridAtmosphere.AddActiveTile(otherTile);
|
||||
var otherTile2 = otherTile.AdjacentTiles[otherTile.MonstermosInfo.CurrentTransferDirection.ToIndex()];
|
||||
if (otherTile2?.Air == null) continue;
|
||||
var sum = otherTile2.Air.TotalMoles;
|
||||
totalGasesRemoved += sum;
|
||||
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;
|
||||
}
|
||||
|
||||
otherTile.Air.Clear();
|
||||
otherTile.UpdateVisuals();
|
||||
HandleDecompressionFloorRip(gridAtmosphere, otherTile, sum);
|
||||
}
|
||||
|
||||
ArrayPool<TileAtmosphere>.Shared.Return(tiles);
|
||||
ArrayPool<TileAtmosphere>.Shared.Return(spaceTiles);
|
||||
ArrayPool<TileAtmosphere>.Shared.Return(progressionOrder);
|
||||
}
|
||||
|
||||
private void ConsiderFirelocks(TileAtmosphere tile, TileAtmosphere other)
|
||||
{
|
||||
if (!_mapManager.TryGetGrid(tile.GridIndex, out var mapGrid))
|
||||
return;
|
||||
|
||||
var reconsiderAdjacent = false;
|
||||
|
||||
foreach (var entity in mapGrid.GetAnchoredEntities(tile.GridIndices))
|
||||
{
|
||||
if (!ComponentManager.TryGetComponent(entity, out FirelockComponent firelock))
|
||||
continue;
|
||||
|
||||
reconsiderAdjacent |= firelock.EmergencyPressureStop();
|
||||
}
|
||||
|
||||
foreach (var entity in mapGrid.GetAnchoredEntities(other.GridIndices))
|
||||
{
|
||||
if (!ComponentManager.TryGetComponent(entity, out FirelockComponent firelock))
|
||||
continue;
|
||||
|
||||
reconsiderAdjacent |= firelock.EmergencyPressureStop();
|
||||
}
|
||||
|
||||
if (!reconsiderAdjacent)
|
||||
return;
|
||||
|
||||
tile.UpdateAdjacent();
|
||||
other.UpdateAdjacent();
|
||||
}
|
||||
|
||||
public void FinalizeEq(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile)
|
||||
{
|
||||
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;
|
||||
if (amount > 0)
|
||||
{
|
||||
if (tile.Air.TotalMoles < amount)
|
||||
FinalizeEqNeighbors(gridAtmosphere, tile, transferDirections);
|
||||
|
||||
otherTile.MonstermosInfo[direction.GetOpposite()] = 0;
|
||||
Merge(otherTile.Air, tile.Air.Remove(amount));
|
||||
tile.UpdateVisuals();
|
||||
otherTile.UpdateVisuals();
|
||||
ConsiderPressureDifference(gridAtmosphere, tile, otherTile, amount);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void FinalizeEqNeighbors(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, ReadOnlySpan<float> transferDirs)
|
||||
{
|
||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << i);
|
||||
var amount = transferDirs[i];
|
||||
if(amount < 0 && tile.AdjacentBits.IsFlagSet(direction))
|
||||
FinalizeEq(gridAtmosphere, tile.AdjacentTiles[i]); // A bit of recursion if needed.
|
||||
}
|
||||
}
|
||||
|
||||
private void AdjustEqMovement(TileAtmosphere tile, AtmosDirection direction, float amount)
|
||||
{
|
||||
tile.MonstermosInfo[direction] += amount;
|
||||
tile.AdjacentTiles[direction.ToIndex()].MonstermosInfo[direction.GetOpposite()] -= amount;
|
||||
}
|
||||
|
||||
private void HandleDecompressionFloorRip(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, float sum)
|
||||
{
|
||||
var chance = MathHelper.Clamp(sum / 500, 0.005f, 0.5f);
|
||||
|
||||
if (sum > 20 && _robustRandom.Prob(chance))
|
||||
gridAtmosphere.PryTile(tile.GridIndices);
|
||||
}
|
||||
|
||||
private class TileAtmosphereComparer : IComparer<TileAtmosphere>
|
||||
{
|
||||
public int Compare(TileAtmosphere a, TileAtmosphere b)
|
||||
{
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
var number = 0;
|
||||
while (atmosphere.CurrentRunTiles.TryDequeue(out var tile))
|
||||
{
|
||||
tile.EqualizePressureInZone(this, atmosphere.UpdateCounter);
|
||||
EqualizePressureInZone(atmosphere, tile, atmosphere.UpdateCounter);
|
||||
|
||||
if (number++ < LagCheckIterations) continue;
|
||||
number = 0;
|
||||
@@ -55,7 +55,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
var number = 0;
|
||||
while (atmosphere.CurrentRunTiles.TryDequeue(out var tile))
|
||||
{
|
||||
tile.ProcessCell(this, atmosphere.UpdateCounter, SpaceWind);
|
||||
ProcessCell(atmosphere, tile, atmosphere.UpdateCounter, SpaceWind);
|
||||
|
||||
if (number++ < LagCheckIterations) continue;
|
||||
number = 0;
|
||||
@@ -106,7 +106,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
var number = 0;
|
||||
while (atmosphere.CurrentRunTiles.TryDequeue(out var tile))
|
||||
{
|
||||
tile.HighPressureMovements();
|
||||
HighPressureMovements(atmosphere, tile);
|
||||
tile.PressureDifference = 0f;
|
||||
tile.PressureSpecificTarget = null;
|
||||
atmosphere.HighPressureDelta.Remove(tile);
|
||||
@@ -131,7 +131,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
var number = 0;
|
||||
while (atmosphere.CurrentRunTiles.TryDequeue(out var hotspot))
|
||||
{
|
||||
hotspot.ProcessHotspot();
|
||||
ProcessHotspot(atmosphere, hotspot);
|
||||
|
||||
if (number++ < LagCheckIterations) continue;
|
||||
number = 0;
|
||||
@@ -153,7 +153,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
var number = 0;
|
||||
while (atmosphere.CurrentRunTiles.TryDequeue(out var superconductivity))
|
||||
{
|
||||
superconductivity.Superconduct(this);
|
||||
Superconduct(atmosphere, superconductivity);
|
||||
|
||||
if (number++ < LagCheckIterations) continue;
|
||||
number = 0;
|
||||
|
||||
@@ -0,0 +1,157 @@
|
||||
using System;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Shared.Atmos;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
public partial class AtmosphereSystem
|
||||
{
|
||||
private void Superconduct(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile)
|
||||
{
|
||||
var directions = ConductivityDirections(gridAtmosphere, tile);
|
||||
|
||||
for(var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << i);
|
||||
if (!directions.IsFlagSet(direction)) continue;
|
||||
|
||||
var adjacent = tile.AdjacentTiles[direction.ToIndex()];
|
||||
|
||||
// TODO ATMOS handle adjacent being null.
|
||||
if (adjacent == null || adjacent.ThermalConductivity == 0f)
|
||||
continue;
|
||||
|
||||
if(adjacent.ArchivedCycle < gridAtmosphere.UpdateCounter)
|
||||
Archive(adjacent, gridAtmosphere.UpdateCounter);
|
||||
|
||||
NeighborConductWithSource(gridAtmosphere, adjacent, tile);
|
||||
|
||||
ConsiderSuperconductivity(gridAtmosphere, adjacent);
|
||||
}
|
||||
|
||||
RadiateToSpace(tile);
|
||||
FinishSuperconduction(gridAtmosphere, tile);
|
||||
}
|
||||
|
||||
private AtmosDirection ConductivityDirections(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile)
|
||||
{
|
||||
if(tile.Air == null)
|
||||
{
|
||||
if(tile.ArchivedCycle < gridAtmosphere.UpdateCounter)
|
||||
Archive(tile, gridAtmosphere.UpdateCounter);
|
||||
return AtmosDirection.All;
|
||||
}
|
||||
|
||||
// TODO ATMOS check if this is correct
|
||||
return AtmosDirection.All;
|
||||
}
|
||||
|
||||
public bool ConsiderSuperconductivity(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile)
|
||||
{
|
||||
if (tile.ThermalConductivity == 0f)
|
||||
return false;
|
||||
|
||||
gridAtmosphere.AddSuperconductivityTile(tile);
|
||||
return true;
|
||||
}
|
||||
|
||||
public bool ConsiderSuperconductivity(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, bool starting)
|
||||
{
|
||||
if (tile.Air == null || tile.Air.Temperature < (starting
|
||||
? Atmospherics.MinimumTemperatureStartSuperConduction
|
||||
: Atmospherics.MinimumTemperatureForSuperconduction))
|
||||
return false;
|
||||
|
||||
return !(gridAtmosphere.AtmosphereSystem.GetHeatCapacity(tile.Air) < Atmospherics.MCellWithRatio)
|
||||
&& ConsiderSuperconductivity(gridAtmosphere, tile);
|
||||
}
|
||||
|
||||
public void FinishSuperconduction(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile)
|
||||
{
|
||||
// Conduct with air on my tile if I have it
|
||||
if (tile.Air != null)
|
||||
{
|
||||
tile.Temperature = TemperatureShare(tile.Air, tile.ThermalConductivity, tile.Temperature, tile.HeatCapacity);
|
||||
}
|
||||
|
||||
FinishSuperconduction(gridAtmosphere, tile, tile.Air?.Temperature ?? tile.Temperature);
|
||||
}
|
||||
|
||||
public void FinishSuperconduction(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, float temperature)
|
||||
{
|
||||
// Make sure it's still hot enough to continue conducting.
|
||||
if (temperature < Atmospherics.MinimumTemperatureForSuperconduction)
|
||||
{
|
||||
gridAtmosphere.RemoveSuperconductivityTile(tile);
|
||||
}
|
||||
}
|
||||
|
||||
public void NeighborConductWithSource(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, TileAtmosphere other)
|
||||
{
|
||||
if (tile.Air == null)
|
||||
{
|
||||
if (other.Tile != null)
|
||||
{
|
||||
TemperatureShareOpenToSolid(other, tile);
|
||||
}
|
||||
else
|
||||
{
|
||||
TemperatureShareMutualSolid(other, tile, tile.ThermalConductivity);
|
||||
}
|
||||
|
||||
// TODO ATMOS: tile.TemperatureExpose(null, tile.Temperature, gridAtmosphere.GetVolumeForCells(1));
|
||||
return;
|
||||
}
|
||||
|
||||
if (other.Air != null)
|
||||
{
|
||||
TemperatureShare(other.Air, tile.Air, Atmospherics.WindowHeatTransferCoefficient);
|
||||
}
|
||||
else
|
||||
{
|
||||
TemperatureShareOpenToSolid(tile, other);
|
||||
}
|
||||
|
||||
gridAtmosphere.AddActiveTile(tile);
|
||||
}
|
||||
|
||||
private void TemperatureShareOpenToSolid(TileAtmosphere tile, TileAtmosphere other)
|
||||
{
|
||||
if (tile.Air == null)
|
||||
return;
|
||||
|
||||
other.Temperature = TemperatureShare(tile.Air, other.ThermalConductivity, other.Temperature, other.HeatCapacity);
|
||||
}
|
||||
|
||||
private void TemperatureShareMutualSolid(TileAtmosphere tile, TileAtmosphere other, float conductionCoefficient)
|
||||
{
|
||||
var deltaTemperature = (tile.TemperatureArchived - other.TemperatureArchived);
|
||||
if (MathF.Abs(deltaTemperature) > Atmospherics.MinimumTemperatureDeltaToConsider
|
||||
&& tile.HeatCapacity != 0f && other.HeatCapacity != 0f)
|
||||
{
|
||||
var heat = conductionCoefficient * deltaTemperature *
|
||||
(tile.HeatCapacity * other.HeatCapacity / (tile.HeatCapacity + other.HeatCapacity));
|
||||
|
||||
tile.Temperature -= heat / tile.HeatCapacity;
|
||||
other.Temperature += heat / other.HeatCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
public void RadiateToSpace(TileAtmosphere tile)
|
||||
{
|
||||
// Considering 0ºC as the break even point for radiation in and out.
|
||||
if (tile.Temperature > Atmospherics.T0C)
|
||||
{
|
||||
// Hardcoded space temperature.
|
||||
var deltaTemperature = (tile.TemperatureArchived - Atmospherics.TCMB);
|
||||
if ((tile.HeatCapacity > 0) && (MathF.Abs(deltaTemperature) > Atmospherics.MinimumTemperatureDeltaToConsider))
|
||||
{
|
||||
var heat = tile.ThermalConductivity * deltaTemperature * (tile.HeatCapacity *
|
||||
Atmospherics.HeatCapacityVacuum / (tile.HeatCapacity + Atmospherics.HeatCapacityVacuum));
|
||||
|
||||
tile.Temperature -= heat;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,7 +4,7 @@ using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Atmos
|
||||
{
|
||||
public struct TileAtmosInfo
|
||||
public struct MonstermosInfo
|
||||
{
|
||||
[ViewVariables]
|
||||
public int LastCycle;
|
||||
@@ -30,6 +30,15 @@ namespace Content.Server.Atmos
|
||||
[ViewVariables]
|
||||
public float TransferDirectionSouth;
|
||||
|
||||
[ViewVariables]
|
||||
public float CurrentTransferAmount;
|
||||
|
||||
[ViewVariables]
|
||||
public AtmosDirection CurrentTransferDirection;
|
||||
|
||||
[ViewVariables]
|
||||
public bool FastDone;
|
||||
|
||||
public float this[AtmosDirection direction]
|
||||
{
|
||||
get =>
|
||||
@@ -69,13 +78,5 @@ namespace Content.Server.Atmos
|
||||
get => this[(AtmosDirection) (1 << index)];
|
||||
set => this[(AtmosDirection) (1 << index)] = value;
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public float CurrentTransferAmount;
|
||||
|
||||
public AtmosDirection CurrentTransferDirection;
|
||||
|
||||
[ViewVariables]
|
||||
public bool FastDone;
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ namespace Content.Server.Atmos.Reactions
|
||||
temperature = mixture.Temperature;
|
||||
if (temperature > Atmospherics.FireMinimumTemperatureToExist)
|
||||
{
|
||||
location.HotspotExpose(temperature, mixture.Volume);
|
||||
atmosphereSystem.HotspotExpose(location.GridIndex, location.GridIndices, temperature, mixture.Volume);
|
||||
|
||||
foreach (var entity in location.GridIndices.GetEntitiesInTileFast(location.GridIndex))
|
||||
{
|
||||
@@ -83,7 +83,7 @@ namespace Content.Server.Atmos.Reactions
|
||||
}
|
||||
}
|
||||
|
||||
location.TemperatureExpose(mixture, temperature, mixture.Volume);
|
||||
// TODO ATMOS: location.TemperatureExpose(mixture, temperature, mixture.Volume);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -63,7 +63,7 @@ namespace Content.Server.Atmos.Reactions
|
||||
temperature = mixture.Temperature;
|
||||
if (temperature > Atmospherics.FireMinimumTemperatureToExist)
|
||||
{
|
||||
location.HotspotExpose(temperature, mixture.Volume);
|
||||
atmosphereSystem.HotspotExpose(location.GridIndex, location.GridIndices, temperature, mixture.Volume);
|
||||
|
||||
foreach (var entity in location.GridIndices.GetEntitiesInTileFast(location.GridIndex))
|
||||
{
|
||||
@@ -73,7 +73,7 @@ namespace Content.Server.Atmos.Reactions
|
||||
}
|
||||
}
|
||||
|
||||
location.TemperatureExpose(mixture, temperature, mixture.Volume);
|
||||
// TODO ATMOS: location.TemperatureExpose(mixture, temperature, mixture.Volume);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -141,14 +141,14 @@ namespace Content.Shared.Atmos
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// Hard limit for tile equalization.
|
||||
/// Hard limit for zone-based tile equalization.
|
||||
/// </summary>
|
||||
public const int ZumosHardTileLimit = 2000;
|
||||
public const int MonstermosHardTileLimit = 2000;
|
||||
|
||||
/// <summary>
|
||||
/// Limit for zone-based tile equalization.
|
||||
/// </summary>
|
||||
public const int ZumosTileLimit = 200;
|
||||
public const int MonstermosTileLimit = 200;
|
||||
|
||||
/// <summary>
|
||||
/// Total number of gases. Increase this if you want to add more!
|
||||
|
||||
@@ -243,12 +243,25 @@ namespace Content.Shared.CCVar
|
||||
public static readonly CVarDef<bool> SpaceWind =
|
||||
CVarDef.Create("atmos.space_wind", true, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// The sound that plays when space wind occurs.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<string> SpaceWindSound =
|
||||
CVarDef.Create("atmos.space_wind_sound", "/Audio/Effects/space_wind.ogg", CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Whether monstermos tile equalization is enabled.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> MonstermosEqualization =
|
||||
CVarDef.Create("atmos.monstermos_equalization", true, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Whether monstermos explosive depressurization is enabled.
|
||||
/// Needs <see cref="MonstermosEqualization"/> to be enabled to work.
|
||||
/// </summary>
|
||||
public static readonly CVarDef<bool> MonstermosDepressurization =
|
||||
CVarDef.Create<bool>("atmos.monstermos_depressurization", true, CVar.SERVERONLY);
|
||||
|
||||
/// <summary>
|
||||
/// Whether atmos superconduction is enabled.
|
||||
/// </summary>
|
||||
|
||||
Reference in New Issue
Block a user