Files
tbd-station-14/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Grid.cs
metalgearsloth 72da2db62d Content changes for grid splitting (#7645) (#7746)
Co-authored-by: Vera Aguilera Puerto <gradientvera@outlook.com>
2022-04-24 13:54:25 +10:00

1614 lines
65 KiB
C#

using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.Piping.Components;
using Content.Server.Atmos.Reactions;
using Content.Server.NodeContainer.NodeGroups;
using Content.Shared.Atmos;
using Content.Shared.Maps;
using Robust.Shared.GameObjects;
// ReSharper disable once RedundantUsingDirective
using Robust.Shared.IoC;
using Robust.Shared.Log;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
using Dependency = Robust.Shared.IoC.DependencyAttribute;
namespace Content.Server.Atmos.EntitySystems
{
public sealed partial class AtmosphereSystem
{
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
[Dependency] private readonly GasTileOverlaySystem _gasTileOverlaySystem = default!;
private void InitializeGrid()
{
SubscribeLocalEvent<GridAtmosphereComponent, ComponentInit>(OnGridAtmosphereInit);
SubscribeLocalEvent<GridAtmosphereComponent, GridSplitEvent>(OnGridSplit);
}
private void OnGridAtmosphereInit(EntityUid uid, GridAtmosphereComponent gridAtmosphere, ComponentInit args)
{
base.Initialize();
gridAtmosphere.Tiles.Clear();
if (!TryComp(uid, out IMapGridComponent? mapGrid))
return;
if (gridAtmosphere.TilesUniqueMixes != null)
{
foreach (var (indices, mix) in gridAtmosphere.TilesUniqueMixes)
{
try
{
gridAtmosphere.Tiles.Add(indices, new TileAtmosphere(mapGrid.GridIndex, indices, (GasMixture) gridAtmosphere.UniqueMixes![mix].Clone()));
}
catch (ArgumentOutOfRangeException)
{
Logger.Error($"Error during atmos serialization! Tile at {indices} points to an unique mix ({mix}) out of range!");
throw;
}
InvalidateTile(gridAtmosphere, indices);
}
}
GridRepopulateTiles(mapGrid.Grid, gridAtmosphere);
}
private void OnGridSplit(EntityUid uid, GridAtmosphereComponent originalGridAtmos, ref GridSplitEvent args)
{
foreach (var newGrid in args.NewGrids)
{
// Make extra sure this is a valid grid.
if (!_mapManager.TryGetGrid(newGrid, out var mapGrid))
continue;
var entity = mapGrid.GridEntityId;
// If the new split grid has an atmosphere already somehow, use that. Otherwise, add a new one.
if (!TryComp(entity, out GridAtmosphereComponent? newGridAtmos))
newGridAtmos = AddComp<GridAtmosphereComponent>(entity);
// We assume the tiles on the new grid have the same coordinates as they did on the old grid...
var enumerator = mapGrid.GetAllTilesEnumerator();
while (enumerator.MoveNext(out var tile))
{
var indices = tile.Value.GridIndices;
// This split event happens *before* the spaced tiles have been invalidated, therefore we can still
// access their gas data. On the next atmos update tick, these tiles will be spaced. Poof!
if (!originalGridAtmos.Tiles.TryGetValue(indices, out var tileAtmosphere))
continue;
// The new grid atmosphere has been initialized, meaning it has all the needed TileAtmospheres...
if (!newGridAtmos.Tiles.TryGetValue(indices, out var newTileAtmosphere))
// Let's be honest, this is really not gonna happen, but just in case...!
continue;
// Copy a bunch of data over... Not great, maybe put this in TileAtmosphere?
newTileAtmosphere.Air = tileAtmosphere.Air?.Clone() ?? null;
newTileAtmosphere.Hotspot = tileAtmosphere.Hotspot;
newTileAtmosphere.HeatCapacity = tileAtmosphere.HeatCapacity;
newTileAtmosphere.Temperature = tileAtmosphere.Temperature;
newTileAtmosphere.PressureDifference = tileAtmosphere.PressureDifference;
newTileAtmosphere.PressureDirection = tileAtmosphere.PressureDirection;
// TODO ATMOS: Somehow force GasTileOverlaySystem to perform an update *right now, right here.*
// The reason why is that right now, gas will flicker until the next GasTileOverlay update.
// That looks bad, of course. We want to avoid that! Anyway that's a bit more complicated so out of scope.
// Invalidate the tile, it's redundant but redundancy is good! Also HashSet so really, no duplicates.
InvalidateTile(originalGridAtmos, indices);
InvalidateTile(newGridAtmos, indices);
}
}
}
#region Grid Is Simulated
/// <summary>
/// Returns whether a grid has a simulated atmosphere.
/// </summary>
/// <param name="coordinates">Coordinates to be checked.</param>
/// <returns>Whether the grid has a simulated atmosphere.</returns>
public bool IsSimulatedGrid(EntityCoordinates coordinates)
{
if (TryGetGridAndTile(coordinates, out var tuple))
return IsSimulatedGrid(tuple.Value.Grid);
return false;
}
/// <summary>
/// Returns whether a grid has a simulated atmosphere.
/// </summary>
/// <param name="grid">Grid to be checked.</param>
/// <returns>Whether the grid has a simulated atmosphere.</returns>
public bool IsSimulatedGrid(GridId grid)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return false;
if (HasComp<GridAtmosphereComponent>(mapGrid.GridEntityId))
return true;
return false;
}
#endregion
#region Grid Get All Mixtures
/// <summary>
/// Gets all tile mixtures within a grid atmosphere, optionally invalidating them all.
/// </summary>
/// <param name="coordinates">Coordinates where to get the grid to get all tile mixtures from.</param>
/// <param name="invalidate">Whether to invalidate all tiles.</param>
/// <returns>All tile mixtures in a grid.</returns>
public IEnumerable<GasMixture> GetAllTileMixtures(EntityCoordinates coordinates, bool invalidate = false)
{
if (TryGetGridAndTile(coordinates, out var tuple))
return GetAllTileMixtures(tuple.Value.Grid, invalidate);
return Enumerable.Empty<GasMixture>();
}
/// <summary>
/// Gets all tile mixtures within a grid atmosphere, optionally invalidating them all.
/// </summary>
/// <param name="grid">Grid where to get all tile mixtures from.</param>
/// <param name="invalidate">Whether to invalidate all tiles.</param>
/// <returns>All tile mixtures in a grid.</returns>
public IEnumerable<GasMixture> GetAllTileMixtures(GridId grid, bool invalidate = false)
{
// Return an array with a single space gas mixture for invalid grids.
if (!grid.IsValid())
return new []{ GasMixture.SpaceGas };
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return Enumerable.Empty<GasMixture>();
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
return GetAllTileMixtures(gridAtmosphere, invalidate);
}
return Enumerable.Empty<GasMixture>();
}
/// <summary>
/// Gets all tile mixtures within a grid atmosphere, optionally invalidating them all.
/// </summary>
/// <param name="gridAtmosphere">Grid Atmosphere to get all mixtures from.</param>
/// <param name="invalidate">Whether to invalidate all mixtures.</param>
/// <returns>All the tile mixtures in a grid.</returns>
public IEnumerable<GasMixture> GetAllTileMixtures(GridAtmosphereComponent gridAtmosphere, bool invalidate = false)
{
foreach (var (indices, tile) in gridAtmosphere.Tiles)
{
if (tile.Air == null)
continue;
if (invalidate)
InvalidateTile(gridAtmosphere, indices);
yield return tile.Air;
}
}
#endregion
#region Grid Cell Volume
/// <summary>
/// Gets the volume in liters for a number of tiles, on a specific grid.
/// </summary>
/// <param name="grid">The grid in question.</param>
/// <param name="tiles">The amount of tiles.</param>
/// <returns>The volume in liters that the tiles occupy.</returns>
public float GetVolumeForTiles(GridId grid, int tiles = 1)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return Atmospherics.CellVolume * tiles;
return GetVolumeForTiles(mapGrid, tiles);
}
/// <summary>
/// Gets the volume in liters for a number of tiles, on a specific grid.
/// </summary>
/// <param name="mapGrid">The grid in question.</param>
/// <param name="tiles">The amount of tiles.</param>
/// <returns>The volume in liters that the tiles occupy.</returns>
public float GetVolumeForTiles(IMapGrid mapGrid, int tiles = 1)
{
return Atmospherics.CellVolume * mapGrid.TileSize * tiles;
}
#endregion
#region Grid Get Obstructing
/// <summary>
/// Gets all obstructing AirtightComponent instances in a specific tile.
/// </summary>
/// <param name="mapGrid">The grid where to get the tile.</param>
/// <param name="tile">The indices of the tile.</param>
/// <returns></returns>
public IEnumerable<AirtightComponent> GetObstructingComponents(IMapGrid mapGrid, Vector2i tile)
{
var airQuery = GetEntityQuery<AirtightComponent>();
var enumerator = mapGrid.GetAnchoredEntitiesEnumerator(tile);
while (enumerator.MoveNext(out var uid))
{
if (!airQuery.TryGetComponent(uid.Value, out var airtight)) continue;
yield return airtight;
}
}
public AtmosObstructionEnumerator GetObstructingComponentsEnumerator(IMapGrid mapGrid, Vector2i tile)
{
var ancEnumerator = mapGrid.GetAnchoredEntitiesEnumerator(tile);
var airQuery = GetEntityQuery<AirtightComponent>();
var enumerator = new AtmosObstructionEnumerator(ancEnumerator, airQuery);
return enumerator;
}
private AtmosDirection GetBlockedDirections(IMapGrid mapGrid, Vector2i indices)
{
var value = AtmosDirection.Invalid;
var enumerator = GetObstructingComponentsEnumerator(mapGrid, indices);
while (enumerator.MoveNext(out var airtightComponent))
{
if (airtightComponent.AirBlocked)
value |= airtightComponent.AirBlockedDirection;
}
return value;
}
#endregion
#region Grid Repopulate
/// <summary>
/// Repopulates all tiles on a grid atmosphere.
/// </summary>
/// <param name="mapGrid">The grid where to get all valid tiles from.</param>
/// <param name="gridAtmosphere">The grid atmosphere where the tiles will be repopulated.</param>
public void GridRepopulateTiles(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere)
{
var volume = GetVolumeForTiles(mapGrid, 1);
foreach (var tile in mapGrid.GetAllTiles())
{
if(!gridAtmosphere.Tiles.ContainsKey(tile.GridIndices))
gridAtmosphere.Tiles[tile.GridIndices] = new TileAtmosphere(tile.GridIndex, tile.GridIndices, new GasMixture(volume){Temperature = Atmospherics.T20C});
InvalidateTile(gridAtmosphere, tile.GridIndices);
}
foreach (var (position, tile) in gridAtmosphere.Tiles.ToArray())
{
UpdateAdjacent(mapGrid, gridAtmosphere, tile);
InvalidateVisuals(mapGrid.Index, position);
}
}
#endregion
#region Tile Pry
/// <summary>
/// Pries a tile in a grid.
/// </summary>
/// <param name="mapGrid">The grid in question.</param>
/// <param name="tile">The indices of the tile.</param>
private void PryTile(IMapGrid mapGrid, Vector2i tile)
{
if (!mapGrid.TryGetTileRef(tile, out var tileRef))
return;
tileRef.PryTile(_mapManager, _tileDefinitionManager, EntityManager, _robustRandom);
}
#endregion
#region Tile Invalidate
/// <summary>
/// Invalidates a tile at a certain position.
/// </summary>
/// <param name="coordinates">Coordinates of the tile.</param>
public void InvalidateTile(EntityCoordinates coordinates)
{
if(TryGetGridAndTile(coordinates, out var tuple))
InvalidateTile(tuple.Value.Grid, tuple.Value.Tile);
}
/// <summary>
/// Invalidates a tile at a certain position.
/// </summary>
/// <param name="grid">Grid where to invalidate the tile.</param>
/// <param name="tile">The indices of the tile.</param>
public void InvalidateTile(GridId grid, Vector2i tile)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
InvalidateTile(gridAtmosphere, tile);
return;
}
}
/// <summary>
/// Invalidates a tile at a certain position.
/// </summary>
/// <param name="gridAtmosphere">Grid Atmosphere where to invalidate the tile.</param>
/// <param name="tile">The tile's indices.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InvalidateTile(GridAtmosphereComponent gridAtmosphere, Vector2i tile)
{
gridAtmosphere.InvalidatedCoords.Add(tile);
}
#endregion
#region Tile Invalidate Visuals
public void InvalidateVisuals(EntityCoordinates coordinates)
{
if(TryGetGridAndTile(coordinates, out var tuple))
InvalidateVisuals(tuple.Value.Grid, tuple.Value.Tile);
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void InvalidateVisuals(GridId grid, Vector2i tile)
{
_gasTileOverlaySystem.Invalidate(grid, tile);
}
#endregion
#region Tile Atmosphere Get
/// <summary>
/// Gets the tile atmosphere in a position, or null.
/// </summary>
/// <param name="coordinates">Coordinates where to get the tile.</param>
/// <remarks>Do NOT use this outside of atmos internals.</remarks>
/// <returns>The Tile Atmosphere in the position, or null if not on a grid.</returns>
public TileAtmosphere? GetTileAtmosphere(EntityCoordinates coordinates)
{
if (TryGetGridAndTile(coordinates, out var tuple))
return GetTileAtmosphere(tuple.Value.Grid, tuple.Value.Tile);
return null;
}
/// <summary>
/// Gets the tile atmosphere in a position, or null.
/// </summary>
/// <param name="grid">Grid where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
/// <remarks>Do NOT use this outside of atmos internals.</remarks>
/// <returns>The Tile Atmosphere in the position, or null.</returns>
public TileAtmosphere? GetTileAtmosphere(GridId grid, Vector2i tile)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return null;
if(TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
return GetTileAtmosphere(gridAtmosphere, tile);
}
return null;
}
/// <summary>
/// Gets the tile atmosphere in a position, or null.
/// </summary>
/// <param name="gridAtmosphere">Grid atmosphere where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
/// <remarks>Do NOT use this outside of atmos internals.</remarks>
/// <returns>The Tile Atmosphere in the position, or null.</returns>
public TileAtmosphere? GetTileAtmosphere(GridAtmosphereComponent gridAtmosphere, Vector2i tile)
{
if (gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere))
return tileAtmosphere;
return null;
}
/// <summary>
/// Gets the tile atmosphere in a position and if not possible returns a space tile or null.
/// </summary>
/// <param name="coordinates">Coordinates of the tile.</param>
/// <remarks>Do NOT use this outside of atmos internals.</remarks>
/// <returns>The tile atmosphere of a specific position in a grid, a space tile atmosphere if the tile is space or null if not on a grid.</returns>
public TileAtmosphere? GetTileAtmosphereOrCreateSpace(EntityCoordinates coordinates)
{
if (TryGetGridAndTile(coordinates, out var tuple))
return GetTileAtmosphereOrCreateSpace(tuple.Value.Grid, tuple.Value.Tile);
return null;
}
/// <summary>
/// Gets the tile atmosphere in a position and if not possible returns a space tile or null.
/// </summary>
/// <param name="grid">Grid where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
/// <remarks>Do NOT use this outside of atmos internals.</remarks>
/// <returns>The tile atmosphere of a specific position in a grid, a space tile atmosphere if the tile is space or null if the grid doesn't exist.</returns>
public TileAtmosphere? GetTileAtmosphereOrCreateSpace(GridId grid, Vector2i tile)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return null;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
return GetTileAtmosphereOrCreateSpace(mapGrid, gridAtmosphere, tile);
}
return null;
}
/// <summary>
/// Gets the tile atmosphere in a position and if not possible returns a space tile or null.
/// </summary>
/// <param name="mapGrid">Grid where to get the tile.</param>
/// <param name="gridAtmosphere">Grid Atmosphere where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
/// <remarks>Do NOT use this outside of atmos internals.</remarks>
/// <returns>The tile atmosphere of a specific position in a grid or a space tile atmosphere if the tile is space.</returns>
public TileAtmosphere GetTileAtmosphereOrCreateSpace(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, Vector2i tile)
{
var tileAtmosphere = GetTileAtmosphere(gridAtmosphere, tile);
// Please note, you might run into a race condition when using this or GetTileAtmosphere.
// The race condition occurs when a tile goes from being space to not-space, and then something
// attempts to get the tile atmosphere for it before it has been revalidated by atmos.
// The tile atmosphere will get revalidated on the next atmos tick, however.
return tileAtmosphere ?? new TileAtmosphere(mapGrid.Index, tile, new GasMixture(Atmospherics.CellVolume) {Temperature = Atmospherics.TCMB}, true);
}
#endregion
#region Tile Active Add
/// <summary>
/// Makes a tile become active and start processing.
/// </summary>
/// <param name="coordinates">Coordinates where to get the tile.</param>
public void AddActiveTile(EntityCoordinates coordinates)
{
if(TryGetGridAndTile(coordinates, out var tuple))
AddActiveTile(tuple.Value.Grid, tuple.Value.Tile);
}
/// <summary>
/// Makes a tile become active and start processing.
/// </summary>
/// <param name="grid">Grid where to get the tile.</param>
/// <param name="tile">Indices of the tile to be activated.</param>
public void AddActiveTile(GridId grid, Vector2i tile)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
AddActiveTile(gridAtmosphere, tile);
return;
}
}
/// <summary>
/// Makes a tile become active and start processing.
/// </summary>
/// <param name="gridAtmosphere">Grid Atmosphere where to get the tile.</param>
/// <param name="tile">Indices of the tile to be activated.</param>
public void AddActiveTile(GridAtmosphereComponent gridAtmosphere, Vector2i tile)
{
if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere))
return;
AddActiveTile(gridAtmosphere, tileAtmosphere);
}
/// <summary>
/// Makes a tile become active and start processing. Does NOT check if the tile belongs to the grid atmos.
/// </summary>
/// <param name="gridAtmosphere">Grid Atmosphere where to get the tile.</param>
/// <param name="tile">Tile Atmosphere to be activated.</param>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void AddActiveTile(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile)
{
if (tile.Air == null)
return;
tile.Excited = true;
gridAtmosphere.ActiveTiles.Add(tile);
}
#endregion
#region Tile Active Remove
/// <summary>
/// Makes a tile become inactive and stop processing.
/// </summary>
/// <param name="coordinates">Coordinates where to get the tile.</param>
/// <param name="disposeExcitedGroup">Whether to dispose of the tile's <see cref="ExcitedGroup"/></param>
public void RemoveActiveTile(EntityCoordinates coordinates, bool disposeExcitedGroup = true)
{
if(TryGetGridAndTile(coordinates, out var tuple))
RemoveActiveTile(tuple.Value.Grid, tuple.Value.Tile, disposeExcitedGroup);
}
/// <summary>
/// Makes a tile become inactive and stop processing.
/// </summary>
/// <param name="grid">Grid where to get the tile.</param>
/// <param name="tile">Indices of the tile to be deactivated.</param>
/// <param name="disposeExcitedGroup">Whether to dispose of the tile's <see cref="ExcitedGroup"/></param>
public void RemoveActiveTile(GridId grid, Vector2i tile, bool disposeExcitedGroup = true)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
RemoveActiveTile(gridAtmosphere, tile);
return;
}
}
/// <summary>
/// Makes a tile become inactive and stop processing.
/// </summary>
/// <param name="gridAtmosphere">Grid Atmosphere where to get the tile.</param>
/// <param name="tile">Indices of the tile to be deactivated.</param>
/// <param name="disposeExcitedGroup">Whether to dispose of the tile's <see cref="ExcitedGroup"/></param>
public void RemoveActiveTile(GridAtmosphereComponent gridAtmosphere, Vector2i tile, bool disposeExcitedGroup = true)
{
if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere))
return;
RemoveActiveTile(gridAtmosphere, tileAtmosphere, disposeExcitedGroup);
}
/// <summary>
/// Makes a tile become inactive and stop processing.
/// </summary>
/// <param name="gridAtmosphere">Grid Atmosphere where to get the tile.</param>
/// <param name="tile">Tile Atmosphere to be deactivated.</param>
/// <param name="disposeExcitedGroup">Whether to dispose of the tile's <see cref="ExcitedGroup"/></param>
private void RemoveActiveTile(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, bool disposeExcitedGroup = true)
{
tile.Excited = false;
gridAtmosphere.ActiveTiles.Remove(tile);
if (tile.ExcitedGroup == null)
return;
if (disposeExcitedGroup)
ExcitedGroupDispose(gridAtmosphere, tile.ExcitedGroup);
else
ExcitedGroupRemoveTile(tile.ExcitedGroup, tile);
}
#endregion
#region Tile Mixture
/// <summary>
/// Returns a reference to the gas mixture on a tile, or null.
/// </summary>
/// <param name="coordinates">Coordinates where to get the tile.</param>
/// <param name="invalidate">Whether to invalidate the tile.</param>
/// <returns>The tile mixture, or null</returns>
public GasMixture? GetTileMixture(EntityCoordinates coordinates, bool invalidate = false)
{
return TryGetGridAndTile(coordinates, out var tuple)
? GetTileMixture(tuple.Value.Grid, tuple.Value.Tile, invalidate) : GasMixture.SpaceGas;
}
/// <summary>
/// Returns a reference to the gas mixture on a tile, or null.
/// </summary>
/// <param name="grid">Grid where to get the tile air.</param>
/// <param name="tile">Indices of the tile.</param>
/// <param name="invalidate">Whether to invalidate the tile.</param>
/// <returns>The tile mixture, or null</returns>
public GasMixture? GetTileMixture(GridId grid, Vector2i tile, bool invalidate = false)
{
// Always return space gas mixtures for invalid grids (grid 0)
if (!grid.IsValid())
return GasMixture.SpaceGas;
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return null;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
return GetTileMixture(gridAtmosphere, tile, invalidate);
}
if (TryComp(mapGrid.GridEntityId, out SpaceAtmosphereComponent? _))
{
// Always return a new space gas mixture in this case.
return GasMixture.SpaceGas;
}
return null;
}
/// <summary>
/// Returns a reference to the gas mixture on a tile, or null.
/// </summary>
/// <param name="gridAtmosphere">Grid Atmosphere where to get the tile air.</param>
/// <param name="tile">Indices of the tile.</param>
/// <param name="invalidate">Whether to invalidate the tile.</param>
/// <returns>The tile mixture, or null</returns>
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public GasMixture? GetTileMixture(GridAtmosphereComponent gridAtmosphere, Vector2i tile, bool invalidate = false)
{
if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere))
return null;
// Invalidate the tile if needed.
if (invalidate)
InvalidateTile(gridAtmosphere, tile);
// Return actual tile air or null.
return tileAtmosphere.Air;
}
#endregion
#region Tile React
/// <summary>
/// Causes a gas mixture reaction on a specific tile.
/// </summary>
/// <param name="coordinates">Coordinates where to get the tile.</param>
/// <returns>Reaction results.</returns>
public ReactionResult React(EntityCoordinates coordinates)
{
if (TryGetGridAndTile(coordinates, out var tuple))
return React(tuple.Value.Grid, tuple.Value.Tile);
return ReactionResult.NoReaction;
}
/// <summary>
/// Causes a gas mixture reaction on a specific tile.
/// </summary>
/// <param name="grid">Grid where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
/// <returns>Reaction results.</returns>
public ReactionResult React(GridId grid, Vector2i tile)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return ReactionResult.NoReaction;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
return React(gridAtmosphere, tile);
}
return ReactionResult.NoReaction;
}
/// <summary>
/// Causes a gas mixture reaction on a specific tile.
/// </summary>
/// <param name="gridAtmosphere">Grid Atmosphere where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
/// <returns>Reaction results.</returns>
public ReactionResult React(GridAtmosphereComponent gridAtmosphere, Vector2i tile)
{
if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere) || tileAtmosphere.Air == null)
return ReactionResult.NoReaction;
InvalidateTile(gridAtmosphere, tile);
return React(tileAtmosphere.Air, tileAtmosphere);
}
#endregion
#region Tile Air-blocked
/// <summary>
/// Returns if the tile in question is "air-blocked" in a certain direction or not.
/// This could be due to a number of reasons, such as walls, doors, etc.
/// </summary>
/// <param name="coordinates">Coordinates where to get the tile.</param>
/// <param name="direction">Directions to check.</param>
/// <returns>Whether the tile is blocked in the directions specified.</returns>
public bool IsTileAirBlocked(EntityCoordinates coordinates, AtmosDirection direction = AtmosDirection.All)
{
if (TryGetGridAndTile(coordinates, out var tuple))
return IsTileAirBlocked(tuple.Value.Grid, tuple.Value.Tile, direction);
return false;
}
/// <summary>
/// Returns if the tile in question is "air-blocked" in a certain direction or not.
/// This could be due to a number of reasons, such as walls, doors, etc.
/// </summary>
/// <param name="grid">Grid where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
/// <param name="direction">Directions to check.</param>
/// <returns>Whether the tile is blocked in the directions specified.</returns>
public bool IsTileAirBlocked(GridId grid, Vector2i tile, AtmosDirection direction = AtmosDirection.All)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return false;
return IsTileAirBlocked(mapGrid, tile, direction);
}
/// <summary>
/// Returns if the tile in question is "air-blocked" in a certain direction or not.
/// This could be due to a number of reasons, such as walls, doors, etc.
/// </summary>
/// <param name="mapGrid">Grid where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
/// <param name="direction">Directions to check.</param>
/// <returns>Whether the tile is blocked in the directions specified.</returns>
public bool IsTileAirBlocked(IMapGrid mapGrid, Vector2i tile, AtmosDirection direction = AtmosDirection.All)
{
var directions = AtmosDirection.Invalid;
var enumerator = GetObstructingComponentsEnumerator(mapGrid, tile);
while (enumerator.MoveNext(out var obstructingComponent))
{
if (!obstructingComponent.AirBlocked)
continue;
// We set the directions that are air-blocked so far,
// as you could have a full obstruction with only 4 directional air blockers.
directions |= obstructingComponent.AirBlockedDirection;
if (directions.IsFlagSet(direction))
return true;
}
return false;
}
#endregion
#region Tile Space
/// <summary>
/// Returns whether the specified tile is a space tile or not.
/// </summary>
/// <param name="coordinates">Coordinates where to check the tile.</param>
/// <returns>Whether the tile is space or not.</returns>
public bool IsTileSpace(EntityCoordinates coordinates)
{
if (TryGetGridAndTile(coordinates, out var tuple))
return IsTileSpace(tuple.Value.Grid, tuple.Value.Tile);
return true;
}
/// <summary>
/// Returns whether the specified tile is a space tile or not.
/// </summary>
/// <param name="grid">Grid where to check the tile.</param>
/// <param name="tile">Indices of the tile.</param>
/// <returns>Whether the tile is space or not.</returns>
public bool IsTileSpace(GridId grid, Vector2i tile)
{
return !_mapManager.TryGetGrid(grid, out var mapGrid) || IsTileSpace(mapGrid, tile);
}
public bool IsTileSpace(IMapGrid mapGrid, Vector2i tile)
{
if (!mapGrid.TryGetTileRef(tile, out var tileRef))
return true;
return ((ContentTileDefinition) _tileDefinitionManager[tileRef.Tile.TypeId]).IsSpace;
}
#endregion
#region Tile Get Heat Capacity
/// <summary>
/// Get a tile's heat capacity, based on the tile type, tile contents and tile gas mixture.
/// </summary>
public float GetTileHeatCapacity(EntityCoordinates coordinates)
{
if (TryGetGridAndTile(coordinates, out var tuple))
return GetTileHeatCapacity(tuple.Value.Grid, tuple.Value.Tile);
return Atmospherics.MinimumHeatCapacity;
}
/// <summary>
/// Get a tile's heat capacity, based on the tile type, tile contents and tile gas mixture.
/// </summary>
public float GetTileHeatCapacity(GridId grid, Vector2i tile)
{
// Always return space gas mixtures for invalid grids (grid 0)
if (!grid.IsValid())
return Atmospherics.MinimumHeatCapacity;
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return Atmospherics.MinimumHeatCapacity;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
return GetTileHeatCapacity(gridAtmosphere, tile);
}
if (TryComp(mapGrid.GridEntityId, out SpaceAtmosphereComponent? _))
{
return Atmospherics.SpaceHeatCapacity;
}
return Atmospherics.MinimumHeatCapacity;
}
/// <summary>
/// Get a tile's heat capacity, based on the tile type, tile contents and tile gas mixture.
/// </summary>
public float GetTileHeatCapacity(GridAtmosphereComponent gridAtmosphere, Vector2i tile)
{
if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere))
return Atmospherics.MinimumHeatCapacity;
return GetTileHeatCapacity(tileAtmosphere);
}
/// <summary>
/// Get a tile's heat capacity, based on the tile type, tile contents and tile gas mixture.
/// </summary>
public float GetTileHeatCapacity(TileAtmosphere tile)
{
return tile.HeatCapacity + (tile.Air == null ? 0 : GetHeatCapacity(tile.Air));
}
#endregion
#region Adjacent Get Positions
/// <summary>
/// Gets all the positions adjacent to a tile. Can include air-blocked directions.
/// </summary>
/// <param name="coordinates">Coordinates where to get the tile.</param>
/// <param name="includeBlocked">Whether to include tiles in directions the tile is air-blocked in.</param>
/// <returns>The positions adjacent to the tile.</returns>
public IEnumerable<Vector2i> GetAdjacentTiles(EntityCoordinates coordinates, bool includeBlocked = false)
{
if (TryGetGridAndTile(coordinates, out var tuple))
return GetAdjacentTiles(tuple.Value.Grid, tuple.Value.Tile, includeBlocked);
return Enumerable.Empty<Vector2i>();
}
/// <summary>
/// Gets all the positions adjacent to a tile. Can include air-blocked directions.
/// </summary>
/// <param name="grid">Grid where to get the tiles.</param>
/// <param name="tile">Indices of the tile.</param>
/// <param name="includeBlocked">Whether to include tiles in directions the tile is air-blocked in.</param>
/// <returns>The positions adjacent to the tile.</returns>
public IEnumerable<Vector2i> GetAdjacentTiles(GridId grid, Vector2i tile, bool includeBlocked = false)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return Enumerable.Empty<Vector2i>();
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
return GetAdjacentTiles(gridAtmosphere, tile, includeBlocked);
}
return Enumerable.Empty<Vector2i>();
}
/// <summary>
/// Gets all the positions adjacent to a tile. Can include air-blocked directions.
/// </summary>
/// <param name="gridAtmosphere">Grid Atmosphere where to get the tiles.</param>
/// <param name="tile">Indices of the tile.</param>
/// <param name="includeBlocked">Whether to include tiles in directions the tile is air-blocked in.</param>
/// <returns>The positions adjacent to the tile.</returns>
public IEnumerable<Vector2i> GetAdjacentTiles(GridAtmosphereComponent gridAtmosphere, Vector2i tile, bool includeBlocked = false)
{
if(!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere))
yield break;
for (var i = 0; i < tileAtmosphere.AdjacentTiles.Length; i++)
{
var adjacentTile = tileAtmosphere.AdjacentTiles[i];
// TileAtmosphere has nullable disabled, so just in case...
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (adjacentTile?.Air == null)
continue;
if (!includeBlocked)
{
var direction = (AtmosDirection) (1 << i);
if (tileAtmosphere.BlockedAirflow.IsFlagSet(direction))
continue;
}
yield return adjacentTile.GridIndices;
}
}
#endregion
#region Adjacent Get Mixture
/// <summary>
/// Gets all tile gas mixtures adjacent to a specific tile, and optionally invalidates them.
/// Does not return the tile in question, only the adjacent ones.
/// </summary>
/// <param name="coordinates">Coordinates where to get the tile.</param>
/// <param name="includeBlocked">Whether to include tiles in directions the tile is air-blocked in.</param>
/// <param name="invalidate">Whether to invalidate all adjacent tiles.</param>
/// <returns>All adjacent tile gas mixtures to the tile in question</returns>
public IEnumerable<GasMixture> GetAdjacentTileMixtures(EntityCoordinates coordinates, bool includeBlocked = false, bool invalidate = false)
{
if (TryGetGridAndTile(coordinates, out var tuple))
return GetAdjacentTileMixtures(tuple.Value.Grid, tuple.Value.Tile, includeBlocked, invalidate);
return Enumerable.Empty<GasMixture>();
}
/// <summary>
/// Gets all tile gas mixtures adjacent to a specific tile, and optionally invalidates them.
/// Does not return the tile in question, only the adjacent ones.
/// </summary>
/// <param name="grid">Grid where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
/// <param name="includeBlocked">Whether to include tiles in directions the tile is air-blocked in.</param>
/// <param name="invalidate">Whether to invalidate all adjacent tiles.</param>
/// <returns>All adjacent tile gas mixtures to the tile in question</returns>
public IEnumerable<GasMixture> GetAdjacentTileMixtures(GridId grid, Vector2i tile, bool includeBlocked = false, bool invalidate = false)
{
// For invalid grids, return an array with a single space gas mixture in it.
if (!grid.IsValid())
return new []{ GasMixture.SpaceGas };
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return Enumerable.Empty<GasMixture>();
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
return GetAdjacentTileMixtures(gridAtmosphere, tile, includeBlocked, invalidate);
}
return Enumerable.Empty<GasMixture>();
}
/// <summary>
/// Gets all tile gas mixtures adjacent to a specific tile, and optionally invalidates them.
/// Does not return the tile in question, only the adjacent ones.
/// </summary>
/// <param name="gridAtmosphere">Grid Atmosphere where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
/// <param name="includeBlocked">Whether to include tiles in directions the tile is air-blocked in.</param>
/// <param name="invalidate">Whether to invalidate all adjacent tiles.</param>
/// <returns>All adjacent tile gas mixtures to the tile in question</returns>
public IEnumerable<GasMixture> GetAdjacentTileMixtures(GridAtmosphereComponent gridAtmosphere, Vector2i tile, bool includeBlocked = false, bool invalidate = false)
{
if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere))
return Enumerable.Empty<GasMixture>();
return GetAdjacentTileMixtures(gridAtmosphere, tileAtmosphere, includeBlocked, invalidate);
}
/// <summary>
/// Gets all tile gas mixtures adjacent to a specific tile, and optionally invalidates them.
/// Does not return the tile in question, only the adjacent ones.
/// </summary>
/// <param name="gridAtmosphere">Grid Atmosphere where the tile is.</param>
/// <param name="tile">Tile Atmosphere in question.</param>
/// <param name="includeBlocked">Whether to include tiles in directions the tile is air-blocked in.</param>
/// <param name="invalidate">Whether to invalidate all adjacent tiles.</param>
/// <returns>All adjacent tile gas mixtures to the tile in question</returns>
private IEnumerable<GasMixture> GetAdjacentTileMixtures(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, bool includeBlocked = false, bool invalidate = false)
{
for (var i = 0; i < tile.AdjacentTiles.Length; i++)
{
var adjacentTile = tile.AdjacentTiles[i];
// TileAtmosphere has nullable disabled, so just in case...
// ReSharper disable once ConditionIsAlwaysTrueOrFalse
if (adjacentTile?.Air == null)
continue;
if (!includeBlocked)
{
var direction = (AtmosDirection) (1 << i);
if (tile.BlockedAirflow.IsFlagSet(direction))
continue;
}
if (invalidate)
InvalidateTile(gridAtmosphere, adjacentTile.GridIndices);
yield return adjacentTile.Air;
}
}
#endregion
#region Adjacent Update
/// <summary>
/// Immediately updates a tile's blocked air directions.
/// </summary>
/// <param name="coordinates">Coordinates where to get the tile.</param>
public void UpdateAdjacent(EntityCoordinates coordinates)
{
if(TryGetGridAndTile(coordinates, out var tuple))
UpdateAdjacent(tuple.Value.Grid, tuple.Value.Tile);
}
/// <summary>
/// Immediately updates a tile's blocked air directions.
/// </summary>
/// <param name="grid">Grid where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
public void UpdateAdjacent(GridId grid, Vector2i tile)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
UpdateAdjacent(mapGrid, gridAtmosphere, tile);
return;
}
}
/// <summary>
/// Immediately updates a tile's blocked air directions.
/// </summary>
/// <param name="mapGrid">Grid where to get the tile.</param>
/// <param name="gridAtmosphere">Grid Atmosphere where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
public void UpdateAdjacent(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, Vector2i tile)
{
if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere))
return;
UpdateAdjacent(mapGrid, gridAtmosphere, tileAtmosphere);
}
/// <summary>
/// Immediately updates a tile's blocked air directions.
/// </summary>
/// <param name="mapGrid">Grid where to get the tile.</param>
/// <param name="gridAtmosphere">Grid Atmosphere of the tile.</param>
/// <param name="tileAtmosphere">Tile Atmosphere to be updated.</param>
private void UpdateAdjacent(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tileAtmosphere)
{
tileAtmosphere.AdjacentBits = AtmosDirection.Invalid;
tileAtmosphere.BlockedAirflow = GetBlockedDirections(mapGrid, tileAtmosphere.GridIndices);
for (var i = 0; i < Atmospherics.Directions; i++)
{
var direction = (AtmosDirection) (1 << i);
var otherIndices = tileAtmosphere.GridIndices.Offset(direction);
var adjacent = GetTileAtmosphereOrCreateSpace(mapGrid, gridAtmosphere, otherIndices);
tileAtmosphere.AdjacentTiles[direction.ToIndex()] = adjacent;
UpdateAdjacent(mapGrid, gridAtmosphere, adjacent, direction.GetOpposite());
if (!tileAtmosphere.BlockedAirflow.IsFlagSet(direction)
&& !IsTileAirBlocked(mapGrid, adjacent.GridIndices, direction.GetOpposite()))
{
tileAtmosphere.AdjacentBits |= direction;
}
}
if (!tileAtmosphere.AdjacentBits.IsFlagSet(tileAtmosphere.MonstermosInfo.CurrentTransferDirection))
tileAtmosphere.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
}
/// <summary>
/// Immediately updates a tile's single blocked air direction.
/// </summary>
/// <param name="coordinates">Coordinates where to get the tile.</param>
/// <param name="direction">Direction to be updated.</param>
public void UpdateAdjacent(EntityCoordinates coordinates, AtmosDirection direction)
{
if(TryGetGridAndTile(coordinates, out var tuple))
UpdateAdjacent(tuple.Value.Grid, tuple.Value.Tile, direction);
}
/// <summary>
/// Immediately updates a tile's single blocked air direction.
/// </summary>
/// <param name="grid">Grid where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
/// <param name="direction">Direction to be updated.</param>
public void UpdateAdjacent(GridId grid, Vector2i tile, AtmosDirection direction)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
UpdateAdjacent(mapGrid, gridAtmosphere, tile, direction);
return;
}
}
/// <summary>
/// Immediately updates a tile's single blocked air direction.
/// </summary>
/// <param name="mapGrid">Grid where to get the tile.</param>
/// <param name="gridAtmosphere">Grid Atmosphere where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
/// <param name="direction">Direction to be updated.</param>
public void UpdateAdjacent(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, Vector2i tile, AtmosDirection direction)
{
if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere))
return;
UpdateAdjacent(mapGrid, gridAtmosphere, tileAtmosphere, direction);
}
/// <summary>
/// Immediately updates a tile's single blocked air direction.
/// </summary>
/// <param name="mapGrid">Grid where to get the tile.</param>
/// <param name="gridAtmosphere">Grid where to get the tile.</param>
/// <param name="tile">Tile Atmosphere to be updated.</param>
/// <param name="direction">Direction to be updated.</param>
private void UpdateAdjacent(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, AtmosDirection direction)
{
tile.AdjacentTiles[direction.ToIndex()] = GetTileAtmosphereOrCreateSpace(mapGrid, gridAtmosphere, tile.GridIndices.Offset(direction));
if (!tile.BlockedAirflow.IsFlagSet(direction) && !IsTileAirBlocked(mapGrid, tile.GridIndices.Offset(direction), direction.GetOpposite()))
{
tile.AdjacentBits |= direction;
}
else
{
tile.AdjacentBits &= ~direction;
}
if (!tile.AdjacentBits.IsFlagSet(tile.MonstermosInfo.CurrentTransferDirection))
tile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
}
#endregion
#region Hotspot Expose
/// <summary>
/// Exposes temperature to a tile, creating a hotspot (fire) if the conditions are ideal.
/// Can also be used to make an existing hotspot hotter/bigger. Also invalidates the tile.
/// </summary>
/// <param name="coordinates">Coordinates where to get the tile.</param>
/// <param name="exposedTemperature">Temperature to expose to the tile.</param>
/// <param name="exposedVolume">Volume of the exposed temperature.</param>
/// <param name="soh">If true, the existing hotspot values will be set to the exposed values, but only if they're smaller.</param>
public void HotspotExpose(EntityCoordinates coordinates, float exposedTemperature, float exposedVolume, bool soh = false)
{
if(TryGetGridAndTile(coordinates, out var tuple))
HotspotExpose(tuple.Value.Grid, tuple.Value.Tile, exposedTemperature, exposedVolume, soh);
}
/// <summary>
/// Exposes temperature to a tile, creating a hotspot (fire) if the conditions are ideal.
/// Can also be used to make an existing hotspot hotter/bigger. Also invalidates the tile.
/// </summary>
/// <param name="grid">Grid where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
/// <param name="exposedTemperature">Temperature to expose to the tile.</param>
/// <param name="exposedVolume">Volume of the exposed temperature.</param>
/// <param name="soh">If true, the existing hotspot values will be set to the exposed values, but only if they're smaller.</param>
public void HotspotExpose(GridId grid, Vector2i tile, float exposedTemperature, float exposedVolume, bool soh = false)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
var tileAtmosphere = GetTileAtmosphere(gridAtmosphere, tile);
if (tileAtmosphere == null)
return;
HotspotExpose(gridAtmosphere, tileAtmosphere, exposedTemperature, exposedVolume, soh);
InvalidateTile(gridAtmosphere, tile);
return;
}
}
#endregion
#region Hotspot Extinguish
/// <summary>
/// Extinguishes a hotspot (fire) on a certain tile, if any. Also invalidates the tile.
/// </summary>
/// <param name="coordinates">Coordinates where to get the tile.</param>
public void HotspotExtinguish(EntityCoordinates coordinates)
{
if(TryGetGridAndTile(coordinates, out var tuple))
HotspotExtinguish(tuple.Value.Grid, tuple.Value.Tile);
}
/// <summary>
/// Extinguishes a hotspot (fire) on a certain tile, if any. Also invalidates the tile.
/// </summary>
/// <param name="grid">Grid where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
public void HotspotExtinguish(GridId grid, Vector2i tile)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
HotspotExtinguish(gridAtmosphere, tile);
return;
}
}
/// <summary>
/// Extinguishes a hotspot (fire) on a certain tile, if any. Also invalidates the tile.
/// </summary>
/// <param name="gridAtmosphere">Grid Atmosphere where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
public void HotspotExtinguish(GridAtmosphereComponent gridAtmosphere, Vector2i tile)
{
if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere))
return;
tileAtmosphere.Hotspot = new Hotspot();
InvalidateTile(gridAtmosphere, tile);
}
#endregion
#region Hotspot Active
/// <summary>
/// Returns whether there's an active hotspot (fire) on a certain tile.
/// </summary>
/// <param name="coordinates">Position where to get the tile.</param>
/// <returns>Whether the hotspot is active or not.</returns>
public bool IsHotspotActive(EntityCoordinates coordinates)
{
if (TryGetGridAndTile(coordinates, out var tuple))
return IsHotspotActive(tuple.Value.Grid, tuple.Value.Tile);
return false;
}
/// <summary>
/// Returns whether there's an active hotspot (fire) on a certain tile.
/// </summary>
/// <param name="grid">Grid where to get the tile</param>
/// <param name="tile">Indices for the tile</param>
/// <returns>Whether the hotspot is active or not.</returns>
public bool IsHotspotActive(GridId grid, Vector2i tile)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return false;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
return IsHotspotActive(gridAtmosphere, tile);
}
return false;
}
/// <summary>
/// Returns whether there's an active hotspot (fire) on a certain tile.
/// </summary>
/// <param name="gridAtmosphere">Grid Atmosphere where to get the tile</param>
/// <param name="tile">Indices for the tile</param>
/// <returns>Whether the hotspot is active or not.</returns>
public bool IsHotspotActive(GridAtmosphereComponent gridAtmosphere, Vector2i tile)
{
if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere))
return false;
return tileAtmosphere.Hotspot.Valid;
}
#endregion
#region PipeNet Add
public void AddPipeNet(PipeNet pipeNet)
{
if (!_mapManager.TryGetGrid(pipeNet.Grid, out var mapGrid))
return;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
gridAtmosphere.PipeNets.Add(pipeNet);
}
}
#endregion
#region PipeNet Remove
public void RemovePipeNet(PipeNet pipeNet)
{
if (!_mapManager.TryGetGrid(pipeNet.Grid, out var mapGrid))
return;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
gridAtmosphere.PipeNets.Remove(pipeNet);
}
}
#endregion
#region AtmosDevice Add
public bool AddAtmosDevice(AtmosDeviceComponent atmosDevice)
{
var grid = Comp<TransformComponent>(atmosDevice.Owner).GridID;
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return false;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
atmosDevice.JoinedGrid = grid;
gridAtmosphere.AtmosDevices.Add(atmosDevice);
return true;
}
return false;
}
#endregion
#region AtmosDevice Remove
public bool RemoveAtmosDevice(AtmosDeviceComponent atmosDevice)
{
if (atmosDevice.JoinedGrid == null)
return false;
var grid = atmosDevice.JoinedGrid.Value;
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return false;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)
&& gridAtmosphere.AtmosDevices.Contains(atmosDevice))
{
atmosDevice.JoinedGrid = null;
gridAtmosphere.AtmosDevices.Remove(atmosDevice);
return true;
}
return false;
}
#endregion
#region Mixture Safety
/// <summary>
/// Checks whether a tile's gas mixture is probably safe.
/// This only checks temperature and pressure, not gas composition.
/// </summary>
/// <param name="coordinates">Coordinates where to get the tile.</param>
/// <returns>Whether the tile's gas mixture is probably safe.</returns>
public bool IsTileMixtureProbablySafe(EntityCoordinates coordinates)
{
return IsMixtureProbablySafe(GetTileMixture(coordinates));
}
/// <summary>
/// Checks whether a tile's gas mixture is probably safe.
/// This only checks temperature and pressure, not gas composition.
/// </summary>
/// <param name="grid">Grid where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
/// <returns>Whether the tile's gas mixture is probably safe.</returns>
public bool IsTileMixtureProbablySafe(GridId grid, Vector2i tile)
{
return IsMixtureProbablySafe(GetTileMixture(grid, tile));
}
/// <summary>
/// Checks whether a gas mixture is probably safe.
/// This only checks temperature and pressure, not gas composition.
/// </summary>
/// <param name="air">Mixture to be checked.</param>
/// <returns>Whether the mixture is probably safe.</returns>
public bool IsMixtureProbablySafe(GasMixture? air)
{
// Note that oxygen mix isn't checked, but survival boxes make that not necessary.
if (air == null)
return false;
switch (air.Pressure)
{
case <= Atmospherics.WarningLowPressure:
case >= Atmospherics.WarningHighPressure:
return false;
}
switch (air.Temperature)
{
case <= 260:
case >= 360:
return false;
}
return true;
}
#endregion
#region Fix Vacuum
/// <summary>
/// Attempts to fix a sudden vacuum by creating gas based on adjacent tiles.
/// </summary>
/// <param name="coordinates">Coordinates where to get the tile.</param>
public void FixVacuum(EntityCoordinates coordinates)
{
if(TryGetGridAndTile(coordinates, out var tuple))
FixVacuum(tuple.Value.Grid, tuple.Value.Tile);
}
/// <summary>
/// Attempts to fix a sudden vacuum by creating gas based on adjacent tiles.
/// </summary>
/// <param name="grid">Grid where to get the tile.</param>
/// <param name="tile">Indices of the tile.</param>
public void FixVacuum(GridId grid, Vector2i tile)
{
if (!_mapManager.TryGetGrid(grid, out var mapGrid))
return;
if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere))
{
FixVacuum(gridAtmosphere, tile);
return;
}
}
public void FixVacuum(GridAtmosphereComponent gridAtmosphere, Vector2i tile)
{
if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere))
return;
var adjacent = GetAdjacentTileMixtures(gridAtmosphere, tileAtmosphere, false, true).ToArray();
tileAtmosphere.Air = new GasMixture(GetVolumeForTiles(tileAtmosphere.GridIndex, 1))
{Temperature = Atmospherics.T20C};
// Return early, let's not cause any funny NaNs.
if (adjacent.Length == 0)
return;
var ratio = 1f / adjacent.Length;
var totalTemperature = 0f;
foreach (var adj in adjacent)
{
totalTemperature += adj.Temperature;
// Remove a bit of gas from the adjacent ratio...
var mix = adj.RemoveRatio(ratio);
// And merge it to the new tile air.
Merge(tileAtmosphere.Air, mix);
// Return removed gas to its original mixture.
Merge(adj, mix);
}
// New temperature is the arithmetic mean of the sum of the adjacent temperatures...
tileAtmosphere.Air.Temperature = totalTemperature / adjacent.Length;
}
public bool NeedsVacuumFixing(IMapGrid mapGrid, Vector2i indices)
{
var value = false;
foreach (var airtightComponent in GetObstructingComponents(mapGrid, indices))
{
value |= airtightComponent.FixVacuum;
}
return value;
}
#endregion
#region Position Helpers
private TileRef? GetTile(TileAtmosphere tile)
{
return tile.GridIndices.GetTileRef(tile.GridIndex, _mapManager);
}
public bool TryGetGridAndTile(MapCoordinates coordinates, [NotNullWhen(true)] out (GridId Grid, Vector2i Tile)? tuple)
{
if (!_mapManager.TryFindGridAt(coordinates, out var grid))
{
tuple = null;
return false;
}
tuple = (grid.Index, grid.TileIndicesFor(coordinates));
return true;
}
public bool TryGetGridAndTile(EntityCoordinates coordinates, [NotNullWhen(true)] out (GridId Grid, Vector2i Tile)? tuple)
{
if (!coordinates.IsValid(EntityManager))
{
tuple = null;
return false;
}
var gridId = coordinates.GetGridId(EntityManager);
if (!_mapManager.TryGetGrid(gridId, out var grid))
{
tuple = null;
return false;
}
tuple = (gridId, grid.TileIndicesFor(coordinates));
return true;
}
public bool TryGetMapGrid(GridAtmosphereComponent gridAtmosphere, [NotNullWhen(true)] out IMapGrid? mapGrid)
{
if (TryComp(gridAtmosphere.Owner, out IMapGridComponent? mapGridComponent))
{
mapGrid = mapGridComponent.Grid;
return true;
}
mapGrid = null;
return false;
}
#endregion
}
}