Refactors the AtmosphereSystem public-facing API to allow for multiple atmos backends. (#8134)
* Refactors the entirety of the AtmosphereSystem public-facing API to allow for multiple atmos backends. * actually compiles * Remove commented out code * funny bracket * Move archived moles, temperature from GasMixture to TileAtmosphere. * WIP customizable map default mixture still VERY buggy * broken mess aaaaaaaaaaaaa * Fix lattice, etc not being considered space * visualization for "IsSpace" * help * Update Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs Co-authored-by: Moony <moonheart08@users.noreply.github.com> * Holy SHIT it compiles AGAIN * Fix AtmosDeviceSystem crash at shutdown * Fix immutable tiles on map blueprints not being fixed by fixgridatmos/revalidate. * Use space instead of gasmixture immutable for heat capacity calculations * Remove all LINDA-specific code from GasMixture, move it to TileAtmosphere/AtmosphereSystem instead. * Fix roundstart tiles not processing * Update Content.Server/Atmos/Commands/SetTemperatureCommand.cs Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> * Update Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs Changed Files tab is so large I can't commit both suggestions at once mfw Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: Moony <moonheart08@users.noreply.github.com> Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
43216a000f
commit
aa9281d667
@@ -141,16 +141,27 @@ namespace Content.Client.Atmos.Overlays
|
||||
DrawPressureDirection(drawHandle, data.LastPressureDirection, tile, Color.LightGray);
|
||||
}
|
||||
|
||||
// -- Excited Groups --
|
||||
if (data.InExcitedGroup)
|
||||
{
|
||||
var tilePos = new Vector2(tile.X, tile.Y);
|
||||
|
||||
// -- Excited Groups --
|
||||
if (data.InExcitedGroup != 0)
|
||||
{
|
||||
var basisA = tilePos;
|
||||
var basisB = tilePos + new Vector2(1.0f, 1.0f);
|
||||
var basisC = tilePos + new Vector2(0.0f, 1.0f);
|
||||
var basisD = tilePos + new Vector2(1.0f, 0.0f);
|
||||
drawHandle.DrawLine(basisA, basisB, Color.Cyan);
|
||||
drawHandle.DrawLine(basisC, basisD, Color.Cyan);
|
||||
var color = Color.White // Use first three nibbles for an unique color... Good enough?
|
||||
.WithRed( data.InExcitedGroup & 0x000F)
|
||||
.WithGreen((data.InExcitedGroup & 0x00F0) >>4)
|
||||
.WithBlue( (data.InExcitedGroup & 0x0F00) >>8);
|
||||
drawHandle.DrawLine(basisA, basisB, color);
|
||||
drawHandle.DrawLine(basisC, basisD, color);
|
||||
}
|
||||
|
||||
// -- Space Tiles --
|
||||
if (data.IsSpace)
|
||||
{
|
||||
drawHandle.DrawCircle(tilePos + Vector2.One/2, 0.125f, Color.Orange);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
@@ -36,7 +37,9 @@ namespace Content.Server.Atmos.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
if (_entities.HasComponent<IAtmosphereComponent>(euid))
|
||||
var atmos = entMan.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
|
||||
|
||||
if (atmos.HasAtmosphere(euid))
|
||||
{
|
||||
shell.WriteLine("Grid already has an atmosphere.");
|
||||
return;
|
||||
|
||||
@@ -31,9 +31,9 @@ namespace Content.Server.Atmos.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
var atmosphereSystem = entMan.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
|
||||
var indices = new Vector2i(x, y);
|
||||
var tile = atmosphereSystem.GetTileMixture(euid, indices, true);
|
||||
var tile = atmosphereSystem.GetTileMixture(euid, null, indices, true);
|
||||
|
||||
if (tile == null)
|
||||
{
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Shared.Administration;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.Atmos.Commands
|
||||
{
|
||||
[AdminCommand(AdminFlags.Debug)]
|
||||
public sealed class AddUnsimulatedAtmosCommand : IConsoleCommand
|
||||
{
|
||||
public string Command => "addunsimulatedatmos";
|
||||
public string Description => "Adds unimulated atmos support to a grid.";
|
||||
public string Help => $"{Command} <GridId>";
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length < 1)
|
||||
{
|
||||
shell.WriteLine(Help);
|
||||
return;
|
||||
}
|
||||
|
||||
var entMan = IoCManager.Resolve<IEntityManager>();
|
||||
|
||||
if (EntityUid.TryParse(args[0], out var euid))
|
||||
{
|
||||
shell.WriteError($"Failed to parse euid '{args[0]}'.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!entMan.HasComponent<IMapGridComponent>(euid))
|
||||
{
|
||||
shell.WriteError($"Euid '{euid}' does not exist or is not a grid.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (entMan.HasComponent<IAtmosphereComponent>(euid))
|
||||
{
|
||||
shell.WriteLine("Grid already has an atmosphere.");
|
||||
return;
|
||||
}
|
||||
|
||||
entMan.AddComponent<UnsimulatedGridAtmosphereComponent>(euid);
|
||||
|
||||
shell.WriteLine($"Added unsimulated atmosphere to grid {euid}.");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -134,7 +134,7 @@ namespace Content.Server.Atmos.Commands
|
||||
|
||||
if (gas == null)
|
||||
{
|
||||
foreach (var tile in atmosphereSystem.GetAllTileMixtures(gridId.Value, true))
|
||||
foreach (var tile in atmosphereSystem.GetAllMixtures(gridId.Value, true))
|
||||
{
|
||||
if (tile.Immutable) continue;
|
||||
|
||||
@@ -146,7 +146,7 @@ namespace Content.Server.Atmos.Commands
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach (var tile in atmosphereSystem.GetAllTileMixtures(gridId.Value, true))
|
||||
foreach (var tile in atmosphereSystem.GetAllMixtures(gridId.Value, true))
|
||||
{
|
||||
if (tile.Immutable) continue;
|
||||
|
||||
|
||||
@@ -23,7 +23,7 @@ namespace Content.Server.Atmos.Commands
|
||||
|
||||
var mapMan = IoCManager.Resolve<IMapManager>();
|
||||
|
||||
if (!gridId.IsValid() || !mapMan.TryGetGrid(gridId, out _))
|
||||
if (!mapMan.TryGetGrid(gridId, out var grid))
|
||||
{
|
||||
shell.WriteLine("Invalid grid ID.");
|
||||
return;
|
||||
@@ -31,7 +31,7 @@ namespace Content.Server.Atmos.Commands
|
||||
|
||||
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
|
||||
foreach (var tile in atmosphereSystem.GetAllTileMixtures(gridId, true))
|
||||
foreach (var tile in atmosphereSystem.GetAllMixtures(grid.GridEntityId, true))
|
||||
{
|
||||
tile.AdjustMoles(gasId, moles);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Content.Server.Atmos.Commands
|
||||
|
||||
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
var indices = new Vector2i(x, y);
|
||||
var tile = atmosphereSystem.GetTileMixture(id, indices, true);
|
||||
var tile = atmosphereSystem.GetTileMixture(id, null, indices, true);
|
||||
|
||||
if (tile == null)
|
||||
{
|
||||
|
||||
@@ -37,7 +37,7 @@ namespace Content.Server.Atmos.Commands
|
||||
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
|
||||
var tiles = 0;
|
||||
foreach (var tile in atmosphereSystem.GetAllTileMixtures(gridId, true))
|
||||
foreach (var tile in atmosphereSystem.GetAllMixtures(gridComp.GridEntityId, true))
|
||||
{
|
||||
tiles++;
|
||||
tile.Temperature = temperature;
|
||||
|
||||
@@ -2,14 +2,21 @@ using Content.Server.Administration;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using SharpZstd.Interop;
|
||||
|
||||
namespace Content.Server.Atmos.Commands
|
||||
{
|
||||
[AdminCommand(AdminFlags.Debug)]
|
||||
public sealed class SetTemperatureCommand : IConsoleCommand
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entities = default!;
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
public string Command => "settemp";
|
||||
public string Description => "Sets a tile's temperature (in kelvin).";
|
||||
public string Help => "Usage: settemp <X> <Y> <GridId> <Temperature>";
|
||||
@@ -28,9 +35,16 @@ namespace Content.Server.Atmos.Commands
|
||||
return;
|
||||
}
|
||||
|
||||
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
if (!_mapManager.TryGetGrid(gridId, out var grid))
|
||||
{
|
||||
shell.WriteError("Invalid grid.");
|
||||
return;
|
||||
}
|
||||
|
||||
var atmospheres = _entities.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
|
||||
var indices = new Vector2i(x, y);
|
||||
var tile = atmosphereSystem.GetTileMixture(gridId, indices, true);
|
||||
|
||||
var tile = atmospheres.GetTileMixture(grid.GridEntityId, null, indices, true);
|
||||
|
||||
if (tile == null)
|
||||
{
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Server.UserInterface;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Maps;
|
||||
using Content.Shared.Popups;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
@@ -120,7 +121,7 @@ namespace Content.Server.Atmos.Components
|
||||
{
|
||||
// Already get the pressure before Dirty(), because we can't get the EntitySystem in that thread or smth
|
||||
var pressure = 0f;
|
||||
var tile = EntitySystem.Get<AtmosphereSystem>().GetTileMixture(_entities.GetComponent<TransformComponent>(Owner).Coordinates);
|
||||
var tile = EntitySystem.Get<AtmosphereSystem>().GetContainingMixture(Owner, true);
|
||||
if (tile != null)
|
||||
{
|
||||
pressure = tile.Pressure;
|
||||
@@ -178,8 +179,12 @@ namespace Content.Server.Atmos.Components
|
||||
pos = _position.Value;
|
||||
}
|
||||
|
||||
var gridUid = pos.GetGridUid(_entities);
|
||||
var mapUid = pos.GetMapUid(_entities);
|
||||
var position = pos.ToVector2i(_entities, IoCManager.Resolve<IMapManager>());
|
||||
|
||||
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
var tile = atmosphereSystem.GetTileMixture(pos);
|
||||
var tile = atmosphereSystem.GetTileMixture(gridUid, mapUid, position);
|
||||
if (tile == null)
|
||||
{
|
||||
error = "No Atmosphere!";
|
||||
|
||||
@@ -261,7 +261,7 @@ namespace Content.Server.Atmos.Components
|
||||
{
|
||||
if (_integrity <= 0)
|
||||
{
|
||||
var environment = atmosphereSystem.GetTileMixture(_entMan.GetComponent<TransformComponent>(Owner).Coordinates, true);
|
||||
var environment = atmosphereSystem.GetContainingMixture(Owner, false, true);
|
||||
if(environment != null)
|
||||
atmosphereSystem.Merge(environment, Air);
|
||||
|
||||
@@ -279,7 +279,7 @@ namespace Content.Server.Atmos.Components
|
||||
{
|
||||
if (_integrity <= 0)
|
||||
{
|
||||
var environment = atmosphereSystem.GetTileMixture(_entMan.GetComponent<TransformComponent>(Owner).Coordinates, true);
|
||||
var environment = atmosphereSystem.GetContainingMixture(Owner, false, true);
|
||||
if (environment == null)
|
||||
return;
|
||||
|
||||
|
||||
@@ -8,12 +8,12 @@ namespace Content.Server.Atmos.Components
|
||||
/// <summary>
|
||||
/// Internal Atmos class. Use <see cref="AtmosphereSystem"/> to interact with atmos instead.
|
||||
/// </summary>
|
||||
[ComponentReference(typeof(IAtmosphereComponent))]
|
||||
[RegisterComponent, Serializable]
|
||||
[Virtual]
|
||||
public class GridAtmosphereComponent : Component, IAtmosphereComponent, ISerializationHooks
|
||||
[RegisterComponent, Serializable,
|
||||
Access(typeof(AtmosphereSystem), typeof(GasTileOverlaySystem), typeof(AtmosDebugOverlaySystem))]
|
||||
public sealed class GridAtmosphereComponent : Component, ISerializationHooks
|
||||
{
|
||||
public virtual bool Simulated => true;
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Simulated { get; set; } = true;
|
||||
|
||||
[ViewVariables]
|
||||
public bool ProcessingPaused { get; set; } = false;
|
||||
@@ -22,7 +22,7 @@ namespace Content.Server.Atmos.Components
|
||||
public float Timer { get; set; } = 0f;
|
||||
|
||||
[ViewVariables]
|
||||
public int UpdateCounter { get; set; } = 0;
|
||||
public int UpdateCounter { get; set; } = 1; // DO NOT SET TO ZERO BY DEFAULT! It will break roundstart atmos...
|
||||
|
||||
[DataField("uniqueMixes")]
|
||||
public List<GasMixture>? UniqueMixes;
|
||||
@@ -94,7 +94,7 @@ namespace Content.Server.Atmos.Components
|
||||
public long EqualizationQueueCycleControl { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
public AtmosphereProcessingState State { get; set; } = AtmosphereProcessingState.TileEqualize;
|
||||
public AtmosphereProcessingState State { get; set; } = AtmosphereProcessingState.Revalidate;
|
||||
|
||||
void ISerializationHooks.BeforeSerialization()
|
||||
{
|
||||
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace Content.Server.Atmos.Components
|
||||
{
|
||||
public interface IAtmosphereComponent : IComponent
|
||||
{
|
||||
/// <summary>
|
||||
/// Whether this atmosphere is simulated or not.
|
||||
/// </summary>
|
||||
bool Simulated { get; }
|
||||
}
|
||||
}
|
||||
21
Content.Server/Atmos/Components/MapAtmosphereComponent.cs
Normal file
21
Content.Server/Atmos/Components/MapAtmosphereComponent.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
namespace Content.Server.Atmos.Components;
|
||||
|
||||
/// <summary>
|
||||
/// Component that defines the default GasMixture for a map.
|
||||
/// </summary>
|
||||
/// <remarks>Honestly, no need to [Friend] this. It's just two simple data fields... Change them to your heart's content.</remarks>
|
||||
[RegisterComponent]
|
||||
public sealed class MapAtmosphereComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The default GasMixture a map will have. Space mixture by default.
|
||||
/// </summary>
|
||||
[DataField("mixture"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public GasMixture? Mixture = GasMixture.SpaceGas;
|
||||
|
||||
/// <summary>
|
||||
/// Whether empty tiles will be considered space or not.
|
||||
/// </summary>
|
||||
[DataField("space"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Space = true;
|
||||
}
|
||||
@@ -1,9 +0,0 @@
|
||||
namespace Content.Server.Atmos.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IAtmosphereComponent))]
|
||||
public sealed class SpaceAtmosphereComponent : Component, IAtmosphereComponent
|
||||
{
|
||||
public bool Simulated => false;
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
namespace Content.Server.Atmos.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IAtmosphereComponent))]
|
||||
[Serializable]
|
||||
public sealed class UnsimulatedGridAtmosphereComponent : GridAtmosphereComponent
|
||||
{
|
||||
public override bool Simulated => false;
|
||||
}
|
||||
}
|
||||
@@ -111,17 +111,19 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
public void InvalidatePosition(EntityUid gridId, Vector2i pos, bool fixVacuum = false)
|
||||
{
|
||||
if (!gridId.IsValid())
|
||||
if (!_mapManager.TryGetGrid(gridId, out var grid))
|
||||
return;
|
||||
|
||||
var gridUid = grid.GridEntityId;
|
||||
|
||||
var query = EntityManager.GetEntityQuery<AirtightComponent>();
|
||||
_explosionSystem.UpdateAirtightMap(gridId, pos, query);
|
||||
// TODO make atmos system use query
|
||||
_atmosphereSystem.UpdateAdjacent(gridId, pos);
|
||||
_atmosphereSystem.InvalidateTile(gridId, pos);
|
||||
_atmosphereSystem.UpdateAdjacent(gridUid, pos);
|
||||
_atmosphereSystem.InvalidateTile(gridUid, pos);
|
||||
|
||||
if(fixVacuum)
|
||||
_atmosphereSystem.FixVacuum(gridId, pos);
|
||||
_atmosphereSystem.FixTileVacuum(gridUid, pos);
|
||||
}
|
||||
|
||||
private AtmosDirection Rotate(AtmosDirection myDirection, Angle myAngle)
|
||||
|
||||
@@ -92,21 +92,18 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
}
|
||||
}
|
||||
|
||||
private AtmosDebugOverlayData ConvertTileToData(TileAtmosphere? tile)
|
||||
private AtmosDebugOverlayData ConvertTileToData(TileAtmosphere? tile, bool mapIsSpace)
|
||||
{
|
||||
var gases = new float[Atmospherics.TotalNumberOfGases];
|
||||
var gases = new float[Atmospherics.AdjustedNumberOfGases];
|
||||
|
||||
if (tile?.Air == null)
|
||||
{
|
||||
return new AtmosDebugOverlayData(0, gases, AtmosDirection.Invalid, tile?.LastPressureDirection ?? AtmosDirection.Invalid, false, tile?.BlockedAirflow ?? AtmosDirection.Invalid);
|
||||
return new AtmosDebugOverlayData(Atmospherics.TCMB, gases, AtmosDirection.Invalid, tile?.LastPressureDirection ?? AtmosDirection.Invalid, 0, tile?.BlockedAirflow ?? AtmosDirection.Invalid, tile?.Space ?? mapIsSpace);
|
||||
}
|
||||
else
|
||||
{
|
||||
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||
{
|
||||
gases[i] = tile.Air.GetMoles(i);
|
||||
}
|
||||
return new AtmosDebugOverlayData(tile.Air.Temperature, gases, tile.PressureDirection, tile.LastPressureDirection, tile.ExcitedGroup != null, tile.BlockedAirflow);
|
||||
NumericsHelpers.Add(gases, tile.Air.Moles);
|
||||
return new AtmosDebugOverlayData(tile.Air.Temperature, gases, tile.PressureDirection, tile.LastPressureDirection, tile.ExcitedGroup?.GetHashCode() ?? 0, tile.BlockedAirflow, tile.Space);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -132,16 +129,22 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
continue;
|
||||
|
||||
var transform = EntityManager.GetComponent<TransformComponent>(entity);
|
||||
var mapUid = transform.MapUid;
|
||||
|
||||
var mapIsSpace = _atmosphereSystem.IsTileSpace(null, mapUid, Vector2i.Zero);
|
||||
|
||||
var worldBounds = Box2.CenteredAround(transform.WorldPosition,
|
||||
new Vector2(LocalViewRange, LocalViewRange));
|
||||
|
||||
foreach (var grid in _mapManager.FindGridsIntersecting(transform.MapID, worldBounds))
|
||||
{
|
||||
if (!EntityManager.EntityExists(grid.GridEntityId))
|
||||
var uid = grid.GridEntityId;
|
||||
|
||||
if (!Exists(uid))
|
||||
continue;
|
||||
|
||||
if (!EntityManager.TryGetComponent<GridAtmosphereComponent?>(grid.GridEntityId, out var gam)) continue;
|
||||
if (!TryComp(uid, out GridAtmosphereComponent? gridAtmos))
|
||||
continue;
|
||||
|
||||
var entityTile = grid.GetTileRef(transform.Coordinates).GridIndices;
|
||||
var baseTile = new Vector2i(entityTile.X - (LocalViewRange / 2), entityTile.Y - (LocalViewRange / 2));
|
||||
@@ -153,7 +156,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
for (var x = 0; x < LocalViewRange; x++)
|
||||
{
|
||||
var vector = new Vector2i(baseTile.X + x, baseTile.Y + y);
|
||||
debugOverlayContent[index++] = ConvertTileToData(_atmosphereSystem.GetTileAtmosphereOrCreateSpace(grid, gam, vector));
|
||||
debugOverlayContent[index++] = ConvertTileToData(gridAtmos.Tiles.TryGetValue(vector, out var tile) ? tile : null, mapIsSpace);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -12,17 +12,47 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
public readonly EntityCoordinates Coordinates;
|
||||
public readonly GasMixture GasMixture;
|
||||
public readonly TransformComponent Transform;
|
||||
|
||||
public AtmosExposedUpdateEvent(EntityCoordinates coordinates, GasMixture mixture)
|
||||
public AtmosExposedUpdateEvent(EntityCoordinates coordinates, GasMixture mixture, TransformComponent transform)
|
||||
{
|
||||
Coordinates = coordinates;
|
||||
GasMixture = mixture;
|
||||
Transform = transform;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Event that tries to query the mixture a certain entity is exposed to.
|
||||
/// </summary>
|
||||
[ByRefEvent]
|
||||
public struct AtmosExposedGetAirEvent
|
||||
{
|
||||
public GasMixture? Gas;
|
||||
/// <summary>
|
||||
/// The entity we want to query this for.
|
||||
/// </summary>
|
||||
public readonly EntityUid Entity;
|
||||
|
||||
/// <summary>
|
||||
/// The mixture that the entity is exposed to. Output parameter.
|
||||
/// </summary>
|
||||
public GasMixture? Gas = null;
|
||||
|
||||
/// <summary>
|
||||
/// Whether to invalidate the mixture, if possible.
|
||||
/// </summary>
|
||||
public bool Invalidate = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether this event has been handled or not.
|
||||
/// Check this before changing anything.
|
||||
/// </summary>
|
||||
public bool Handled = false;
|
||||
|
||||
public AtmosExposedGetAirEvent(EntityUid entity, bool invalidate = false)
|
||||
{
|
||||
Entity = entity;
|
||||
invalidate = invalidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
301
Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs
Normal file
301
Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs
Normal file
@@ -0,0 +1,301 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Atmos.Piping.Components;
|
||||
using Content.Server.Atmos.Reactions;
|
||||
using Content.Server.NodeContainer.NodeGroups;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems;
|
||||
|
||||
public partial class AtmosphereSystem
|
||||
{
|
||||
public GasMixture? GetContainingMixture(EntityUid uid, bool ignoreExposed = false, bool excite = false, TransformComponent? transform = null)
|
||||
{
|
||||
if (!ignoreExposed)
|
||||
{
|
||||
// Used for things like disposals/cryo to change which air people are exposed to.
|
||||
var ev = new AtmosExposedGetAirEvent(uid, excite);
|
||||
|
||||
// Give the entity itself a chance to handle this.
|
||||
RaiseLocalEvent(uid, ref ev, false);
|
||||
|
||||
if (ev.Handled)
|
||||
return ev.Gas;
|
||||
|
||||
// We need to get the parent now, so we need the transform... If the parent is invalid, we can't do much else.
|
||||
if(!Resolve(uid, ref transform) || !transform.ParentUid.IsValid() || transform.MapUid == null)
|
||||
return GetTileMixture(null, null, Vector2i.Zero, excite);
|
||||
|
||||
// Give the parent entity a chance to handle the event...
|
||||
RaiseLocalEvent(transform.ParentUid, ref ev, false);
|
||||
|
||||
if (ev.Handled)
|
||||
return ev.Gas;
|
||||
}
|
||||
// Oops, we did a little bit of code duplication...
|
||||
else if(!Resolve(uid, ref transform))
|
||||
{
|
||||
return GetTileMixture(null, null, Vector2i.Zero, excite);
|
||||
}
|
||||
|
||||
|
||||
var gridUid = transform.GridUid;
|
||||
var mapUid = transform.MapUid;
|
||||
var position = _transformSystem.GetGridOrMapTilePosition(uid, transform);
|
||||
|
||||
return GetTileMixture(gridUid, mapUid, position, excite);
|
||||
}
|
||||
|
||||
public bool HasAtmosphere(EntityUid gridUid)
|
||||
{
|
||||
var ev = new HasAtmosphereMethodEvent(gridUid);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
|
||||
return ev.Result;
|
||||
}
|
||||
|
||||
public bool SetSimulatedGrid(EntityUid gridUid, bool simulated)
|
||||
{
|
||||
var ev = new SetSimulatedGridMethodEvent(gridUid, simulated);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
|
||||
return ev.Handled;
|
||||
}
|
||||
|
||||
public bool IsSimulatedGrid(EntityUid gridUid)
|
||||
{
|
||||
var ev = new IsSimulatedGridMethodEvent(gridUid);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
|
||||
return ev.Simulated;
|
||||
}
|
||||
|
||||
public IEnumerable<GasMixture> GetAllMixtures(EntityUid gridUid, bool excite = false)
|
||||
{
|
||||
var ev = new GetAllMixturesMethodEvent(gridUid, excite);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
|
||||
if(!ev.Handled)
|
||||
return Enumerable.Empty<GasMixture>();
|
||||
|
||||
DebugTools.AssertNotNull(ev.Mixtures);
|
||||
return ev.Mixtures!;
|
||||
}
|
||||
|
||||
public void InvalidateTile(EntityUid gridUid, Vector2i tile)
|
||||
{
|
||||
var ev = new InvalidateTileMethodEvent(gridUid, tile);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
}
|
||||
|
||||
public GasMixture? GetTileMixture(EntityUid? gridUid, EntityUid? mapUid, Vector2i tile, bool excite = false)
|
||||
{
|
||||
var ev = new GetTileMixtureMethodEvent(gridUid, mapUid, tile, excite);
|
||||
|
||||
// If we've been passed a grid, try to let it handle it.
|
||||
if(gridUid.HasValue)
|
||||
RaiseLocalEvent(gridUid.Value, ref ev, false);
|
||||
|
||||
if (ev.Handled)
|
||||
return ev.Mixture;
|
||||
|
||||
// We either don't have a grid, or the event wasn't handled.
|
||||
// Let the map handle it instead, and also broadcast the event.
|
||||
if(mapUid.HasValue)
|
||||
RaiseLocalEvent(mapUid.Value, ref ev, true);
|
||||
else
|
||||
RaiseLocalEvent(ref ev);
|
||||
|
||||
// Default to a space mixture... This is a space game, after all!
|
||||
return ev.Mixture ?? GasMixture.SpaceGas;
|
||||
}
|
||||
|
||||
public ReactionResult ReactTile(EntityUid gridId, Vector2i tile)
|
||||
{
|
||||
var ev = new ReactTileMethodEvent(gridId, tile);
|
||||
RaiseLocalEvent(gridId, ref ev);
|
||||
|
||||
ev.Handled = true;
|
||||
|
||||
return ev.Result;
|
||||
}
|
||||
|
||||
public bool IsTileAirBlocked(EntityUid gridUid, Vector2i tile, AtmosDirection directions = AtmosDirection.All, IMapGridComponent? mapGridComp = null)
|
||||
{
|
||||
var ev = new IsTileAirBlockedMethodEvent(gridUid, tile, directions, mapGridComp);
|
||||
return ev.Result;
|
||||
}
|
||||
|
||||
public bool IsTileSpace(EntityUid? gridUid, EntityUid? mapUid, Vector2i tile, IMapGridComponent? mapGridComp = null)
|
||||
{
|
||||
var ev = new IsTileSpaceMethodEvent(gridUid, mapUid, tile, mapGridComp);
|
||||
|
||||
// Try to let the grid (if any) handle it...
|
||||
if (gridUid.HasValue)
|
||||
RaiseLocalEvent(gridUid.Value, ref ev, false);
|
||||
|
||||
// If we didn't have a grid or the event wasn't handled
|
||||
// we let the map know, and also broadcast the event while at it!
|
||||
if (mapUid.HasValue && !ev.Handled)
|
||||
RaiseLocalEvent(mapUid.Value, ref ev, true);
|
||||
|
||||
// We didn't have a map, and the event isn't handled, therefore broadcast the event.
|
||||
else if (!mapUid.HasValue && !ev.Handled)
|
||||
RaiseLocalEvent(ref ev);
|
||||
|
||||
// If nothing handled the event, it'll default to true.
|
||||
// Oh well, this is a space game after all, deal with it!
|
||||
return ev.Result;
|
||||
}
|
||||
|
||||
public bool IsTileMixtureProbablySafe(EntityUid? gridUid, EntityUid mapUid, Vector2i tile)
|
||||
{
|
||||
return IsMixtureProbablySafe(GetTileMixture(gridUid, mapUid, tile));
|
||||
}
|
||||
|
||||
public float GetTileHeatCapacity(EntityUid? gridUid, EntityUid mapUid, Vector2i tile)
|
||||
{
|
||||
return GetHeatCapacity(GetTileMixture(gridUid, mapUid, tile) ?? GasMixture.SpaceGas);
|
||||
}
|
||||
|
||||
public IEnumerable<Vector2i> GetAdjacentTiles(EntityUid gridUid, Vector2i tile)
|
||||
{
|
||||
var ev = new GetAdjacentTilesMethodEvent(gridUid, tile);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
|
||||
return ev.Result ?? Enumerable.Empty<Vector2i>();
|
||||
}
|
||||
|
||||
public IEnumerable<GasMixture> GetAdjacentTileMixtures(EntityUid gridUid, Vector2i tile, bool includeBlocked = false, bool excite = false)
|
||||
{
|
||||
var ev = new GetAdjacentTileMixturesMethodEvent(gridUid, tile, includeBlocked, excite);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
|
||||
return ev.Result ?? Enumerable.Empty<GasMixture>();
|
||||
}
|
||||
|
||||
public void UpdateAdjacent(EntityUid gridUid, Vector2i tile, IMapGridComponent? mapGridComp = null)
|
||||
{
|
||||
var ev = new UpdateAdjacentMethodEvent(gridUid, tile, mapGridComp);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
}
|
||||
|
||||
public void HotspotExpose(EntityUid gridUid, Vector2i tile, float exposedTemperature, float exposedVolume, bool soh = false)
|
||||
{
|
||||
var ev = new HotspotExposeMethodEvent(gridUid, tile, exposedTemperature, exposedVolume, soh);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
}
|
||||
|
||||
public void HotspotExtinguish(EntityUid gridUid, Vector2i tile)
|
||||
{
|
||||
var ev = new HotspotExtinguishMethodEvent(gridUid, tile);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
}
|
||||
|
||||
public bool IsHotspotActive(EntityUid gridUid, Vector2i tile)
|
||||
{
|
||||
var ev = new IsHotspotActiveMethodEvent(gridUid, tile);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
|
||||
// If not handled, this will be false. Just like in space!
|
||||
return ev.Result;
|
||||
}
|
||||
|
||||
public void FixTileVacuum(EntityUid gridUid, Vector2i tile)
|
||||
{
|
||||
var ev = new FixTileVacuumMethodEvent(gridUid, tile);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
}
|
||||
|
||||
public void AddPipeNet(EntityUid gridUid, PipeNet pipeNet)
|
||||
{
|
||||
var ev = new AddPipeNetMethodEvent(gridUid, pipeNet);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
}
|
||||
|
||||
public void RemovePipeNet(EntityUid gridUid, PipeNet pipeNet)
|
||||
{
|
||||
var ev = new RemovePipeNetMethodEvent(gridUid, pipeNet);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
}
|
||||
|
||||
public bool AddAtmosDevice(EntityUid gridUid, AtmosDeviceComponent device)
|
||||
{
|
||||
// TODO: check device is on grid
|
||||
|
||||
var ev = new AddAtmosDeviceMethodEvent(gridUid, device);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
return ev.Result;
|
||||
}
|
||||
|
||||
public bool RemoveAtmosDevice(EntityUid gridUid, AtmosDeviceComponent device)
|
||||
{
|
||||
// TODO: check device is on grid
|
||||
|
||||
var ev = new RemoveAtmosDeviceMethodEvent(gridUid, device);
|
||||
RaiseLocalEvent(gridUid, ref ev);
|
||||
return ev.Result;
|
||||
}
|
||||
|
||||
[ByRefEvent] private record struct HasAtmosphereMethodEvent
|
||||
(EntityUid Grid, bool Result = false, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct SetSimulatedGridMethodEvent
|
||||
(EntityUid Grid, bool Simulated, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct IsSimulatedGridMethodEvent
|
||||
(EntityUid Grid, bool Simulated = false, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct GetAllMixturesMethodEvent
|
||||
(EntityUid Grid, bool Excite = false, IEnumerable<GasMixture>? Mixtures = null, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct InvalidateTileMethodEvent
|
||||
(EntityUid Grid, Vector2i Tile, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct GetTileMixtureMethodEvent
|
||||
(EntityUid? GridUid, EntityUid? MapUid, Vector2i Tile, bool Excite = false, GasMixture? Mixture = null, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct ReactTileMethodEvent
|
||||
(EntityUid GridId, Vector2i Tile, ReactionResult Result = default, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct IsTileAirBlockedMethodEvent
|
||||
(EntityUid Grid, Vector2i Tile, AtmosDirection Direction = AtmosDirection.All, IMapGridComponent? MapGridComponent = null, bool Result = false, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct IsTileSpaceMethodEvent
|
||||
(EntityUid? Grid, EntityUid? Map, Vector2i Tile, IMapGridComponent? MapGridComponent = null, bool Result = true, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct GetAdjacentTilesMethodEvent
|
||||
(EntityUid Grid, Vector2i Tile, IEnumerable<Vector2i>? Result = null, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct GetAdjacentTileMixturesMethodEvent
|
||||
(EntityUid Grid, Vector2i Tile, bool IncludeBlocked, bool Excite,
|
||||
IEnumerable<GasMixture>? Result = null, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct UpdateAdjacentMethodEvent
|
||||
(EntityUid Grid, Vector2i Tile, IMapGridComponent? MapGridComponent = null, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct HotspotExposeMethodEvent
|
||||
(EntityUid Grid, Vector2i Tile, float ExposedTemperature, float ExposedVolume, bool soh, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct HotspotExtinguishMethodEvent
|
||||
(EntityUid Grid, Vector2i Tile, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct IsHotspotActiveMethodEvent
|
||||
(EntityUid Grid, Vector2i Tile, bool Result = false, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct FixTileVacuumMethodEvent
|
||||
(EntityUid Grid, Vector2i Tile, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct AddPipeNetMethodEvent
|
||||
(EntityUid Grid, PipeNet PipeNet, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct RemovePipeNetMethodEvent
|
||||
(EntityUid Grid, PipeNet PipeNet, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct AddAtmosDeviceMethodEvent
|
||||
(EntityUid Grid, AtmosDeviceComponent Device, bool Result = false, bool Handled = false);
|
||||
|
||||
[ByRefEvent] private record struct RemoveAtmosDeviceMethodEvent
|
||||
(EntityUid Grid, AtmosDeviceComponent Device, bool Result = false, bool Handled = false);
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using Content.Server.Administration;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Shared.Administration;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
@@ -83,12 +84,20 @@ public sealed partial class AtmosphereSystem
|
||||
continue;
|
||||
}
|
||||
|
||||
var transform = Transform(euid);
|
||||
|
||||
foreach (var (indices, tileMain) in gridAtmosphere.Tiles)
|
||||
{
|
||||
var tile = tileMain.Air;
|
||||
if (tile == null)
|
||||
continue;
|
||||
|
||||
if (tile.Immutable && !IsTileSpace(euid, transform.MapUid, indices, gridComp))
|
||||
{
|
||||
tile = new GasMixture(tile.Volume) { Temperature = tile.Temperature };
|
||||
tileMain.Air = tile;
|
||||
}
|
||||
|
||||
tile.Clear();
|
||||
var mixtureId = 0;
|
||||
foreach (var entUid in gridComp.Grid.GetAnchoredEntities(indices))
|
||||
@@ -102,7 +111,7 @@ public sealed partial class AtmosphereSystem
|
||||
Merge(tile, mixture);
|
||||
tile.Temperature = mixture.Temperature;
|
||||
|
||||
InvalidateTile(gridAtmosphere, indices);
|
||||
gridAtmosphere.InvalidatedCoords.Add(indices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
Merge(combined, tile.Air);
|
||||
|
||||
if (!ExcitedGroupsSpaceIsAllConsuming || !tile.Air.Immutable)
|
||||
if (!ExcitedGroupsSpaceIsAllConsuming || !tile.Space)
|
||||
continue;
|
||||
|
||||
combined.Clear();
|
||||
|
||||
@@ -48,19 +48,11 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
return GetHeatCapacityCalculation(mixture.Moles, mixture.Immutable);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the heat capacity for a gas mixture, using the archived values.
|
||||
/// </summary>
|
||||
public float GetHeatCapacityArchived(GasMixture mixture)
|
||||
{
|
||||
return GetHeatCapacityCalculation(mixture.MolesArchived, mixture.Immutable);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private float GetHeatCapacityCalculation(float[] moles, bool immutable)
|
||||
private float GetHeatCapacityCalculation(float[] moles, bool space)
|
||||
{
|
||||
// Little hack to make space gas mixtures have heat capacity, therefore allowing them to cool down rooms.
|
||||
if (immutable && MathHelper.CloseTo(NumericsHelpers.HorizontalAdd(moles), 0f))
|
||||
if (space && MathHelper.CloseTo(NumericsHelpers.HorizontalAdd(moles), 0f))
|
||||
{
|
||||
return Atmospherics.SpaceHeatCapacity;
|
||||
}
|
||||
@@ -154,140 +146,6 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shares gas between two gas mixtures. Part of LINDA.
|
||||
/// </summary>
|
||||
public float Share(GasMixture receiver, GasMixture sharer, int atmosAdjacentTurfs)
|
||||
{
|
||||
var temperatureDelta = receiver.TemperatureArchived - sharer.TemperatureArchived;
|
||||
var absTemperatureDelta = Math.Abs(temperatureDelta);
|
||||
var oldHeatCapacity = 0f;
|
||||
var oldSharerHeatCapacity = 0f;
|
||||
|
||||
if (absTemperatureDelta > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||
{
|
||||
oldHeatCapacity = GetHeatCapacity(receiver);
|
||||
oldSharerHeatCapacity = GetHeatCapacity(sharer);
|
||||
}
|
||||
|
||||
var heatCapacityToSharer = 0f;
|
||||
var heatCapacitySharerToThis = 0f;
|
||||
var movedMoles = 0f;
|
||||
var absMovedMoles = 0f;
|
||||
|
||||
for(var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||
{
|
||||
var thisValue = receiver.Moles[i];
|
||||
var sharerValue = sharer.Moles[i];
|
||||
var delta = (thisValue - sharerValue) / (atmosAdjacentTurfs + 1);
|
||||
if (!(MathF.Abs(delta) >= Atmospherics.GasMinMoles)) continue;
|
||||
if (absTemperatureDelta > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||
{
|
||||
var gasHeatCapacity = delta * GasSpecificHeats[i];
|
||||
if (delta > 0)
|
||||
{
|
||||
heatCapacityToSharer += gasHeatCapacity;
|
||||
}
|
||||
else
|
||||
{
|
||||
heatCapacitySharerToThis -= gasHeatCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
if (!receiver.Immutable) receiver.Moles[i] -= delta;
|
||||
if (!sharer.Immutable) sharer.Moles[i] += delta;
|
||||
movedMoles += delta;
|
||||
absMovedMoles += MathF.Abs(delta);
|
||||
}
|
||||
|
||||
receiver.LastShare = absMovedMoles;
|
||||
|
||||
if (absTemperatureDelta > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||
{
|
||||
var newHeatCapacity = oldHeatCapacity + heatCapacitySharerToThis - heatCapacityToSharer;
|
||||
var newSharerHeatCapacity = oldSharerHeatCapacity + heatCapacityToSharer - heatCapacitySharerToThis;
|
||||
|
||||
// Transfer of thermal energy (via changed heat capacity) between self and sharer.
|
||||
if (!receiver.Immutable && newHeatCapacity > Atmospherics.MinimumHeatCapacity)
|
||||
{
|
||||
receiver.Temperature = ((oldHeatCapacity * receiver.Temperature) - (heatCapacityToSharer * receiver.TemperatureArchived) + (heatCapacitySharerToThis * sharer.TemperatureArchived)) / newHeatCapacity;
|
||||
}
|
||||
|
||||
if (!sharer.Immutable && newSharerHeatCapacity > Atmospherics.MinimumHeatCapacity)
|
||||
{
|
||||
sharer.Temperature = ((oldSharerHeatCapacity * sharer.Temperature) - (heatCapacitySharerToThis * sharer.TemperatureArchived) + (heatCapacityToSharer*receiver.TemperatureArchived)) / newSharerHeatCapacity;
|
||||
}
|
||||
|
||||
// Thermal energy of the system (self and sharer) is unchanged.
|
||||
|
||||
if (MathF.Abs(oldSharerHeatCapacity) > Atmospherics.MinimumHeatCapacity)
|
||||
{
|
||||
if (MathF.Abs(newSharerHeatCapacity / oldSharerHeatCapacity - 1) < 0.1)
|
||||
{
|
||||
TemperatureShare(receiver, sharer, Atmospherics.OpenHeatTransferCoefficient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(temperatureDelta > Atmospherics.MinimumTemperatureToMove) &&
|
||||
!(MathF.Abs(movedMoles) > Atmospherics.MinimumMolesDeltaToMove)) return 0f;
|
||||
var moles = receiver.TotalMoles;
|
||||
var theirMoles = sharer.TotalMoles;
|
||||
|
||||
return (receiver.TemperatureArchived * (moles + movedMoles)) - (sharer.TemperatureArchived * (theirMoles - movedMoles)) * Atmospherics.R / receiver.Volume;
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shares temperature between two mixtures, taking a conduction coefficient into account.
|
||||
/// </summary>
|
||||
public float TemperatureShare(GasMixture receiver, GasMixture sharer, float conductionCoefficient)
|
||||
{
|
||||
var temperatureDelta = receiver.TemperatureArchived - sharer.TemperatureArchived;
|
||||
if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||
{
|
||||
var heatCapacity = GetHeatCapacityArchived(receiver);
|
||||
var sharerHeatCapacity = GetHeatCapacityArchived(sharer);
|
||||
|
||||
if (sharerHeatCapacity > Atmospherics.MinimumHeatCapacity && heatCapacity > Atmospherics.MinimumHeatCapacity)
|
||||
{
|
||||
var heat = conductionCoefficient * temperatureDelta * (heatCapacity * sharerHeatCapacity / (heatCapacity + sharerHeatCapacity));
|
||||
|
||||
if (!receiver.Immutable)
|
||||
receiver.Temperature = MathF.Abs(MathF.Max(receiver.Temperature - heat / heatCapacity, Atmospherics.TCMB));
|
||||
|
||||
if (!sharer.Immutable)
|
||||
sharer.Temperature = MathF.Abs(MathF.Max(sharer.Temperature + heat / sharerHeatCapacity, Atmospherics.TCMB));
|
||||
}
|
||||
}
|
||||
|
||||
return sharer.Temperature;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shares temperature between a gas mixture and an abstract sharer, taking a conduction coefficient into account.
|
||||
/// </summary>
|
||||
public float TemperatureShare(GasMixture receiver, float conductionCoefficient, float sharerTemperature, float sharerHeatCapacity)
|
||||
{
|
||||
var temperatureDelta = receiver.TemperatureArchived - sharerTemperature;
|
||||
if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||
{
|
||||
var heatCapacity = GetHeatCapacityArchived(receiver);
|
||||
|
||||
if (sharerHeatCapacity > Atmospherics.MinimumHeatCapacity && heatCapacity > Atmospherics.MinimumHeatCapacity)
|
||||
{
|
||||
var heat = conductionCoefficient * temperatureDelta * (heatCapacity * sharerHeatCapacity / (heatCapacity + sharerHeatCapacity));
|
||||
|
||||
if (!receiver.Immutable)
|
||||
receiver.Temperature = MathF.Abs(MathF.Max(receiver.Temperature - heat / heatCapacity, Atmospherics.TCMB));
|
||||
|
||||
sharerTemperature = MathF.Abs(MathF.Max(sharerTemperature + heat / sharerHeatCapacity, Atmospherics.TCMB));
|
||||
}
|
||||
}
|
||||
|
||||
return sharerTemperature;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Releases gas from this mixture to the output mixture.
|
||||
/// If the output mixture is null, then this is being released into space.
|
||||
@@ -362,6 +220,62 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
Merge(destination, buffer);
|
||||
}
|
||||
|
||||
/// <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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares two gas mixtures to see if they are within acceptable ranges for group processing to be enabled.
|
||||
/// </summary>
|
||||
public GasCompareResult CompareExchange(GasMixture sample, GasMixture otherSample)
|
||||
{
|
||||
var moles = 0f;
|
||||
|
||||
for(var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||
{
|
||||
var gasMoles = sample.Moles[i];
|
||||
var delta = MathF.Abs(gasMoles - otherSample.Moles[i]);
|
||||
if (delta > Atmospherics.MinimumMolesDeltaToMove && (delta > gasMoles * Atmospherics.MinimumAirRatioToMove))
|
||||
return (GasCompareResult)i; // We can move gases!
|
||||
moles += gasMoles;
|
||||
}
|
||||
|
||||
if (moles > Atmospherics.MinimumMolesDeltaToMove)
|
||||
{
|
||||
var tempDelta = MathF.Abs(sample.Temperature - otherSample.Temperature);
|
||||
if (tempDelta > Atmospherics.MinimumTemperatureDeltaToSuspend)
|
||||
return GasCompareResult.TemperatureExchange; // There can be temperature exchange.
|
||||
}
|
||||
|
||||
// No exchange at all!
|
||||
return GasCompareResult.NoExchange;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Performs reactions for a given gas mixture on an optional holder.
|
||||
/// </summary>
|
||||
@@ -401,5 +315,11 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
return reaction;
|
||||
}
|
||||
|
||||
public enum GasCompareResult
|
||||
{
|
||||
NoExchange = -2,
|
||||
TemperatureExchange = -1,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,549 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.Reactions;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems;
|
||||
|
||||
public sealed partial class AtmosphereSystem
|
||||
{
|
||||
private void InitializeGridAtmosphere()
|
||||
{
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, ComponentInit>(OnGridAtmosphereInit);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, GridSplitEvent>(OnGridSplit);
|
||||
|
||||
#region Atmos API Subscriptions
|
||||
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, HasAtmosphereMethodEvent>(GridHasAtmosphere);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, IsSimulatedGridMethodEvent>(GridIsSimulated);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, GetAllMixturesMethodEvent>(GridGetAllMixtures);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, InvalidateTileMethodEvent>(GridInvalidateTile);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, GetTileMixtureMethodEvent>(GridGetTileMixture);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, ReactTileMethodEvent>(GridReactTile);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, IsTileAirBlockedMethodEvent>(GridIsTileAirBlocked);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, IsTileSpaceMethodEvent>(GridIsTileSpace);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, GetAdjacentTilesMethodEvent>(GridGetAdjacentTiles);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, GetAdjacentTileMixturesMethodEvent>(GridGetAdjacentTileMixtures);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, UpdateAdjacentMethodEvent>(GridUpdateAdjacent);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, HotspotExposeMethodEvent>(GridHotspotExpose);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, HotspotExtinguishMethodEvent>(GridHotspotExtinguish);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, IsHotspotActiveMethodEvent>(GridIsHotspotActive);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, FixTileVacuumMethodEvent>(GridFixTileVacuum);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, AddPipeNetMethodEvent>(GridAddPipeNet);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, RemovePipeNetMethodEvent>(GridRemovePipeNet);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, AddAtmosDeviceMethodEvent>(GridAddAtmosDevice);
|
||||
SubscribeLocalEvent<GridAtmosphereComponent, RemoveAtmosDeviceMethodEvent>(GridRemoveAtmosDevice);
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
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.Owner, indices,
|
||||
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;
|
||||
}
|
||||
|
||||
gridAtmosphere.InvalidatedCoords.Add(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.MolesArchived = newTileAtmosphere.Air == null ? null : new float[Atmospherics.AdjustedNumberOfGases];
|
||||
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.
|
||||
originalGridAtmos.InvalidatedCoords.Add(indices);
|
||||
newGridAtmos.InvalidatedCoords.Add(indices);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void GridHasAtmosphere(EntityUid uid, GridAtmosphereComponent component, ref HasAtmosphereMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Result = true;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridIsSimulated(EntityUid uid, GridAtmosphereComponent component, ref IsSimulatedGridMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Simulated = component.Simulated;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridGetAllMixtures(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref GetAllMixturesMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
IEnumerable<GasMixture> EnumerateMixtures(EntityUid gridUid, GridAtmosphereComponent grid, bool invalidate)
|
||||
{
|
||||
foreach (var (indices, tile) in grid.Tiles)
|
||||
{
|
||||
if (tile.Air == null)
|
||||
continue;
|
||||
|
||||
if (invalidate)
|
||||
{
|
||||
//var ev = new InvalidateTileMethodEvent(gridUid, indices);
|
||||
//GridInvalidateTile(gridUid, grid, ref ev);
|
||||
AddActiveTile(grid, tile);
|
||||
}
|
||||
|
||||
yield return tile.Air;
|
||||
}
|
||||
}
|
||||
|
||||
// Return the enumeration over all the tiles in the atmosphere.
|
||||
args.Mixtures = EnumerateMixtures(uid, component, args.Excite);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridInvalidateTile(EntityUid uid, GridAtmosphereComponent component, ref InvalidateTileMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
component.InvalidatedCoords.Add(args.Tile);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridGetTileMixture(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref GetTileMixtureMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!component.Tiles.TryGetValue(args.Tile, out var tile))
|
||||
return; // Do NOT handle the event if we don't have that tile, the map will handle it instead.
|
||||
|
||||
if (args.Excite)
|
||||
component.InvalidatedCoords.Add(args.Tile);
|
||||
|
||||
args.Mixture = tile.Air;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridReactTile(EntityUid uid, GridAtmosphereComponent component, ref ReactTileMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!component.Tiles.TryGetValue(args.Tile, out var tile))
|
||||
return;
|
||||
|
||||
args.Result = tile.Air is { } air ? React(air, tile) : ReactionResult.NoReaction;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridIsTileAirBlocked(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref IsTileAirBlockedMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
var mapGridComp = args.MapGridComponent;
|
||||
|
||||
if (!Resolve(uid, ref mapGridComp))
|
||||
return;
|
||||
|
||||
var directions = AtmosDirection.Invalid;
|
||||
|
||||
var enumerator = GetObstructingComponentsEnumerator(mapGridComp.Grid, args.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(args.Direction))
|
||||
{
|
||||
args.Result = true;
|
||||
args.Handled = true;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
args.Result = false;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridIsTileSpace(EntityUid uid, GridAtmosphereComponent component, ref IsTileSpaceMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
// We don't have that tile, so let the map handle it.
|
||||
if (!component.Tiles.TryGetValue(args.Tile, out var tile))
|
||||
return;
|
||||
|
||||
args.Result = tile.Space;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridGetAdjacentTiles(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref GetAdjacentTilesMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!component.Tiles.TryGetValue(args.Tile, out var tile))
|
||||
return;
|
||||
|
||||
IEnumerable<Vector2i> EnumerateAdjacent(GridAtmosphereComponent grid, TileAtmosphere t)
|
||||
{
|
||||
foreach (var adj in t.AdjacentTiles)
|
||||
{
|
||||
if (adj == null)
|
||||
continue;
|
||||
|
||||
yield return adj.GridIndices;
|
||||
}
|
||||
}
|
||||
|
||||
args.Result = EnumerateAdjacent(component, tile);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridGetAdjacentTileMixtures(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref GetAdjacentTileMixturesMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!component.Tiles.TryGetValue(args.Tile, out var tile))
|
||||
return;
|
||||
|
||||
IEnumerable<GasMixture> EnumerateAdjacent(GridAtmosphereComponent grid, TileAtmosphere t)
|
||||
{
|
||||
foreach (var adj in t.AdjacentTiles)
|
||||
{
|
||||
if (adj?.Air == null)
|
||||
continue;
|
||||
|
||||
yield return adj.Air;
|
||||
}
|
||||
}
|
||||
|
||||
args.Result = EnumerateAdjacent(component, tile);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridUpdateAdjacent(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref UpdateAdjacentMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
var mapGridComp = args.MapGridComponent;
|
||||
|
||||
if (!Resolve(uid, ref mapGridComp))
|
||||
return;
|
||||
|
||||
var xform = Transform(uid);
|
||||
EntityUid? mapUid = _mapManager.MapExists(xform.MapID) ? _mapManager.GetMapEntityId(xform.MapID) : null;
|
||||
|
||||
if (!component.Tiles.TryGetValue(args.Tile, out var tile))
|
||||
return;
|
||||
|
||||
tile.AdjacentBits = AtmosDirection.Invalid;
|
||||
tile.BlockedAirflow = GetBlockedDirections(mapGridComp.Grid, tile.GridIndices);
|
||||
|
||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << i);
|
||||
|
||||
var otherIndices = tile.GridIndices.Offset(direction);
|
||||
|
||||
if (!component.Tiles.TryGetValue(otherIndices, out var adjacent))
|
||||
{
|
||||
adjacent = new TileAtmosphere(tile.GridIndex, otherIndices,
|
||||
GetTileMixture(uid, mapUid, args.Tile),
|
||||
space:IsTileSpace(uid, mapUid, otherIndices, mapGridComp));
|
||||
}
|
||||
|
||||
var oppositeDirection = direction.GetOpposite();
|
||||
|
||||
adjacent.BlockedAirflow = GetBlockedDirections(mapGridComp.Grid, adjacent.GridIndices);
|
||||
|
||||
// Pass in IMapGridComponent so we don't have to resolve it for every adjacent direction.
|
||||
var tileBlockedEv = new IsTileAirBlockedMethodEvent(uid, tile.GridIndices, direction, mapGridComp);
|
||||
GridIsTileAirBlocked(uid, component, ref tileBlockedEv);
|
||||
|
||||
var adjacentBlockedEv =
|
||||
new IsTileAirBlockedMethodEvent(uid, adjacent.GridIndices, oppositeDirection, mapGridComp);
|
||||
GridIsTileAirBlocked(uid, component, ref adjacentBlockedEv);
|
||||
|
||||
if (!adjacent.BlockedAirflow.IsFlagSet(oppositeDirection) && !tileBlockedEv.Result)
|
||||
{
|
||||
adjacent.AdjacentBits |= oppositeDirection;
|
||||
adjacent.AdjacentTiles[oppositeDirection.ToIndex()] = tile;
|
||||
}
|
||||
else
|
||||
{
|
||||
adjacent.AdjacentBits &= ~oppositeDirection;
|
||||
adjacent.AdjacentTiles[oppositeDirection.ToIndex()] = null;
|
||||
}
|
||||
|
||||
if (!tile.BlockedAirflow.IsFlagSet(direction) && !adjacentBlockedEv.Result)
|
||||
{
|
||||
tile.AdjacentBits |= direction;
|
||||
tile.AdjacentTiles[direction.ToIndex()] = adjacent;
|
||||
}
|
||||
else
|
||||
{
|
||||
tile.AdjacentBits &= ~direction;
|
||||
tile.AdjacentTiles[direction.ToIndex()] = null;
|
||||
}
|
||||
|
||||
DebugTools.Assert(!(tile.AdjacentBits.IsFlagSet(direction) ^
|
||||
adjacent.AdjacentBits.IsFlagSet(oppositeDirection)));
|
||||
|
||||
if (!adjacent.AdjacentBits.IsFlagSet(adjacent.MonstermosInfo.CurrentTransferDirection))
|
||||
adjacent.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
|
||||
}
|
||||
|
||||
if (!tile.AdjacentBits.IsFlagSet(tile.MonstermosInfo.CurrentTransferDirection))
|
||||
tile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
|
||||
}
|
||||
|
||||
private void GridHotspotExpose(EntityUid uid, GridAtmosphereComponent component, ref HotspotExposeMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!component.Tiles.TryGetValue(args.Tile, out var tile))
|
||||
return;
|
||||
|
||||
HotspotExpose(component, tile, args.ExposedTemperature, args.ExposedVolume, args.soh);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridHotspotExtinguish(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref HotspotExtinguishMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!component.Tiles.TryGetValue(args.Tile, out var tile))
|
||||
return;
|
||||
|
||||
tile.Hotspot = new Hotspot();
|
||||
args.Handled = true;
|
||||
|
||||
//var ev = new InvalidateTileMethodEvent(uid, args.Tile);
|
||||
//GridInvalidateTile(uid, component, ref ev);
|
||||
AddActiveTile(component, tile);
|
||||
}
|
||||
|
||||
private void GridIsHotspotActive(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref IsHotspotActiveMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!component.Tiles.TryGetValue(args.Tile, out var tile))
|
||||
return;
|
||||
|
||||
args.Result = tile.Hotspot.Valid;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void GridFixTileVacuum(EntityUid uid, GridAtmosphereComponent component, ref FixTileVacuumMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
var adjEv = new GetAdjacentTileMixturesMethodEvent(uid, args.Tile, false, true);
|
||||
GridGetAdjacentTileMixtures(uid, component, ref adjEv);
|
||||
|
||||
if (!adjEv.Handled || !component.Tiles.TryGetValue(args.Tile, out var tile))
|
||||
return;
|
||||
|
||||
if (!TryComp<IMapGridComponent>(uid, out var mapGridComp))
|
||||
return;
|
||||
|
||||
var adjacent = adjEv.Result!.ToArray();
|
||||
|
||||
tile.Air = new GasMixture
|
||||
{
|
||||
Volume = GetVolumeForTiles(mapGridComp.Grid, 1),
|
||||
Temperature = Atmospherics.T20C
|
||||
};
|
||||
|
||||
tile.MolesArchived = new float[Atmospherics.AdjustedNumberOfGases];
|
||||
tile.ArchivedCycle = 0;
|
||||
|
||||
// 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(tile.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...
|
||||
tile.Air.Temperature = totalTemperature / adjacent.Length;
|
||||
}
|
||||
|
||||
private void GridAddPipeNet(EntityUid uid, GridAtmosphereComponent component, ref AddPipeNetMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = component.PipeNets.Add(args.PipeNet);
|
||||
}
|
||||
|
||||
private void GridRemovePipeNet(EntityUid uid, GridAtmosphereComponent component, ref RemovePipeNetMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Handled = component.PipeNets.Remove(args.PipeNet);
|
||||
}
|
||||
|
||||
private void GridAddAtmosDevice(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref AddAtmosDeviceMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!component.AtmosDevices.Add(args.Device))
|
||||
return;
|
||||
|
||||
args.Device.JoinedGrid = uid;
|
||||
args.Handled = true;
|
||||
args.Result = true;
|
||||
}
|
||||
|
||||
private void GridRemoveAtmosDevice(EntityUid uid, GridAtmosphereComponent component,
|
||||
ref RemoveAtmosDeviceMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
if (!component.AtmosDevices.Remove(args.Device))
|
||||
return;
|
||||
|
||||
args.Device.JoinedGrid = null;
|
||||
args.Handled = true;
|
||||
args.Result = true;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
private 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.GridUid, tile.GridIndices,
|
||||
new GasMixture(volume) {Temperature = Atmospherics.T20C});
|
||||
|
||||
gridAtmosphere.InvalidatedCoords.Add(tile.GridIndices);
|
||||
}
|
||||
|
||||
var uid = gridAtmosphere.Owner;
|
||||
|
||||
// Gotta do this afterwards so we can properly update adjacent tiles.
|
||||
foreach (var (position, _) in gridAtmosphere.Tiles.ToArray())
|
||||
{
|
||||
var ev = new UpdateAdjacentMethodEvent(uid, position);
|
||||
GridUpdateAdjacent(uid, gridAtmosphere, ref ev);
|
||||
InvalidateVisuals(mapGrid.GridEntityId, position);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -48,7 +48,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
}
|
||||
|
||||
shouldShareAir = true;
|
||||
} else if (tile.Air!.Compare(enemyTile.Air!) != GasMixture.GasCompareResult.NoExchange)
|
||||
} else if (CompareExchange(tile.Air, enemyTile.Air) != GasCompareResult.NoExchange)
|
||||
{
|
||||
if (!enemyTile.Excited)
|
||||
{
|
||||
@@ -78,7 +78,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
if (shouldShareAir)
|
||||
{
|
||||
var difference = Share(tile.Air!, enemyTile.Air!, adjacentTileLength);
|
||||
var difference = Share(tile, enemyTile, adjacentTileLength);
|
||||
|
||||
// Monstermos already handles this, so let's not handle it ourselves.
|
||||
if (!MonstermosEqualization)
|
||||
@@ -114,17 +114,25 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
private void Archive(TileAtmosphere tile, int fireCount)
|
||||
{
|
||||
tile.Air?.Archive();
|
||||
tile.ArchivedCycle = fireCount;
|
||||
if (tile.Air != null)
|
||||
{
|
||||
tile.Air.Moles.AsSpan().CopyTo(tile.MolesArchived.AsSpan());
|
||||
tile.TemperatureArchived = tile.Air.Temperature;
|
||||
}
|
||||
else
|
||||
{
|
||||
tile.TemperatureArchived = tile.Temperature;
|
||||
}
|
||||
|
||||
tile.ArchivedCycle = fireCount;
|
||||
}
|
||||
|
||||
private void LastShareCheck(TileAtmosphere tile)
|
||||
{
|
||||
if (tile.Air == null || tile.ExcitedGroup == null)
|
||||
return;
|
||||
|
||||
switch (tile.Air.LastShare)
|
||||
switch (tile.LastShare)
|
||||
{
|
||||
case > Atmospherics.MinimumAirToSuspend:
|
||||
ExcitedGroupResetCooldowns(tile.ExcitedGroup);
|
||||
@@ -134,5 +142,193 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
private void AddActiveTile(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile)
|
||||
{
|
||||
if (tile.Air == null)
|
||||
return;
|
||||
|
||||
tile.Excited = true;
|
||||
gridAtmosphere.ActiveTiles.Add(tile);
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Calculates the heat capacity for a gas mixture, using the archived values.
|
||||
/// </summary>
|
||||
public float GetHeatCapacityArchived(TileAtmosphere tile)
|
||||
{
|
||||
if (tile.Air == null)
|
||||
return tile.HeatCapacity;
|
||||
|
||||
// Moles archived is not null if air is not null.
|
||||
return GetHeatCapacityCalculation(tile.MolesArchived!, tile.Space);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shares gas between two tiles. Part of LINDA.
|
||||
/// </summary>
|
||||
public float Share(TileAtmosphere tileReceiver, TileAtmosphere tileSharer, int atmosAdjacentTurfs)
|
||||
{
|
||||
if (tileReceiver.Air is not {} receiver || tileSharer.Air is not {} sharer)
|
||||
return 0f;
|
||||
|
||||
var temperatureDelta = tileReceiver.TemperatureArchived - tileSharer.TemperatureArchived;
|
||||
var absTemperatureDelta = Math.Abs(temperatureDelta);
|
||||
var oldHeatCapacity = 0f;
|
||||
var oldSharerHeatCapacity = 0f;
|
||||
|
||||
if (absTemperatureDelta > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||
{
|
||||
oldHeatCapacity = GetHeatCapacity(receiver);
|
||||
oldSharerHeatCapacity = GetHeatCapacity(sharer);
|
||||
}
|
||||
|
||||
var heatCapacityToSharer = 0f;
|
||||
var heatCapacitySharerToThis = 0f;
|
||||
var movedMoles = 0f;
|
||||
var absMovedMoles = 0f;
|
||||
|
||||
for(var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||
{
|
||||
var thisValue = receiver.Moles[i];
|
||||
var sharerValue = sharer.Moles[i];
|
||||
var delta = (thisValue - sharerValue) / (atmosAdjacentTurfs + 1);
|
||||
if (!(MathF.Abs(delta) >= Atmospherics.GasMinMoles)) continue;
|
||||
if (absTemperatureDelta > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||
{
|
||||
var gasHeatCapacity = delta * GasSpecificHeats[i];
|
||||
if (delta > 0)
|
||||
{
|
||||
heatCapacityToSharer += gasHeatCapacity;
|
||||
}
|
||||
else
|
||||
{
|
||||
heatCapacitySharerToThis -= gasHeatCapacity;
|
||||
}
|
||||
}
|
||||
|
||||
if (!receiver.Immutable) receiver.Moles[i] -= delta;
|
||||
if (!sharer.Immutable) sharer.Moles[i] += delta;
|
||||
movedMoles += delta;
|
||||
absMovedMoles += MathF.Abs(delta);
|
||||
}
|
||||
|
||||
tileReceiver.LastShare = absMovedMoles;
|
||||
|
||||
if (absTemperatureDelta > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||
{
|
||||
var newHeatCapacity = oldHeatCapacity + heatCapacitySharerToThis - heatCapacityToSharer;
|
||||
var newSharerHeatCapacity = oldSharerHeatCapacity + heatCapacityToSharer - heatCapacitySharerToThis;
|
||||
|
||||
// Transfer of thermal energy (via changed heat capacity) between self and sharer.
|
||||
if (!receiver.Immutable && newHeatCapacity > Atmospherics.MinimumHeatCapacity)
|
||||
{
|
||||
receiver.Temperature = ((oldHeatCapacity * receiver.Temperature) - (heatCapacityToSharer * tileReceiver.TemperatureArchived) + (heatCapacitySharerToThis * tileSharer.TemperatureArchived)) / newHeatCapacity;
|
||||
}
|
||||
|
||||
if (!sharer.Immutable && newSharerHeatCapacity > Atmospherics.MinimumHeatCapacity)
|
||||
{
|
||||
sharer.Temperature = ((oldSharerHeatCapacity * sharer.Temperature) - (heatCapacitySharerToThis * tileSharer.TemperatureArchived) + (heatCapacityToSharer * tileReceiver.TemperatureArchived)) / newSharerHeatCapacity;
|
||||
}
|
||||
|
||||
// Thermal energy of the system (self and sharer) is unchanged.
|
||||
|
||||
if (MathF.Abs(oldSharerHeatCapacity) > Atmospherics.MinimumHeatCapacity)
|
||||
{
|
||||
if (MathF.Abs(newSharerHeatCapacity / oldSharerHeatCapacity - 1) < 0.1)
|
||||
{
|
||||
TemperatureShare(tileReceiver, tileSharer, Atmospherics.OpenHeatTransferCoefficient);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(temperatureDelta > Atmospherics.MinimumTemperatureToMove) &&
|
||||
!(MathF.Abs(movedMoles) > Atmospherics.MinimumMolesDeltaToMove)) return 0f;
|
||||
var moles = receiver.TotalMoles;
|
||||
var theirMoles = sharer.TotalMoles;
|
||||
|
||||
return (tileReceiver.TemperatureArchived * (moles + movedMoles)) - (tileSharer.TemperatureArchived * (theirMoles - movedMoles)) * Atmospherics.R / receiver.Volume;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shares temperature between two mixtures, taking a conduction coefficient into account.
|
||||
/// </summary>
|
||||
public float TemperatureShare(TileAtmosphere tileReceiver, TileAtmosphere tileSharer, float conductionCoefficient)
|
||||
{
|
||||
if (tileReceiver.Air is not { } receiver || tileSharer.Air is not { } sharer)
|
||||
return 0f;
|
||||
|
||||
var temperatureDelta = tileReceiver.TemperatureArchived - tileSharer.TemperatureArchived;
|
||||
if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||
{
|
||||
var heatCapacity = GetHeatCapacityArchived(tileReceiver);
|
||||
var sharerHeatCapacity = GetHeatCapacityArchived(tileSharer);
|
||||
|
||||
if (sharerHeatCapacity > Atmospherics.MinimumHeatCapacity && heatCapacity > Atmospherics.MinimumHeatCapacity)
|
||||
{
|
||||
var heat = conductionCoefficient * temperatureDelta * (heatCapacity * sharerHeatCapacity / (heatCapacity + sharerHeatCapacity));
|
||||
|
||||
if (!receiver.Immutable)
|
||||
receiver.Temperature = MathF.Abs(MathF.Max(receiver.Temperature - heat / heatCapacity, Atmospherics.TCMB));
|
||||
|
||||
if (!sharer.Immutable)
|
||||
sharer.Temperature = MathF.Abs(MathF.Max(sharer.Temperature + heat / sharerHeatCapacity, Atmospherics.TCMB));
|
||||
}
|
||||
}
|
||||
|
||||
return sharer.Temperature;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shares temperature between a gas mixture and an abstract sharer, taking a conduction coefficient into account.
|
||||
/// </summary>
|
||||
public float TemperatureShare(TileAtmosphere tileReceiver, float conductionCoefficient, float sharerTemperature, float sharerHeatCapacity)
|
||||
{
|
||||
if (tileReceiver.Air is not {} receiver)
|
||||
return 0;
|
||||
|
||||
var temperatureDelta = tileReceiver.TemperatureArchived - sharerTemperature;
|
||||
if (MathF.Abs(temperatureDelta) > Atmospherics.MinimumTemperatureDeltaToConsider)
|
||||
{
|
||||
var heatCapacity = GetHeatCapacityArchived(tileReceiver);
|
||||
|
||||
if (sharerHeatCapacity > Atmospherics.MinimumHeatCapacity && heatCapacity > Atmospherics.MinimumHeatCapacity)
|
||||
{
|
||||
var heat = conductionCoefficient * temperatureDelta * (heatCapacity * sharerHeatCapacity / (heatCapacity + sharerHeatCapacity));
|
||||
|
||||
if (!receiver.Immutable)
|
||||
receiver.Temperature = MathF.Abs(MathF.Max(receiver.Temperature - heat / heatCapacity, Atmospherics.TCMB));
|
||||
|
||||
sharerTemperature = MathF.Abs(MathF.Max(sharerTemperature + heat / sharerHeatCapacity, Atmospherics.TCMB));
|
||||
}
|
||||
}
|
||||
|
||||
return sharerTemperature;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
31
Content.Server/Atmos/EntitySystems/AtmosphereSystem.Map.cs
Normal file
31
Content.Server/Atmos/EntitySystems/AtmosphereSystem.Map.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Content.Server.Atmos.Components;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems;
|
||||
|
||||
public partial class AtmosphereSystem
|
||||
{
|
||||
private void InitializeMap()
|
||||
{
|
||||
SubscribeLocalEvent<MapAtmosphereComponent, IsTileSpaceMethodEvent>(MapIsTileSpace);
|
||||
SubscribeLocalEvent<MapAtmosphereComponent, GetTileMixtureMethodEvent>(MapGetTileMixture);
|
||||
}
|
||||
|
||||
private void MapIsTileSpace(EntityUid uid, MapAtmosphereComponent component, ref IsTileSpaceMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
args.Result = component.Space;
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void MapGetTileMixture(EntityUid uid, MapAtmosphereComponent component, ref GetTileMixtureMethodEvent args)
|
||||
{
|
||||
if (args.Handled)
|
||||
return;
|
||||
|
||||
// Clone the mixture, if possible.
|
||||
args.Mixture = component.Mixture?.Clone();
|
||||
args.Handled = true;
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,6 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
public sealed partial class AtmosphereSystem
|
||||
{
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
|
||||
private readonly TileAtmosphereComparer _monstermosComparer = new();
|
||||
|
||||
private readonly TileAtmosphere?[] _equalizeTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit];
|
||||
@@ -81,7 +79,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if(tileCount < Atmospherics.MonstermosHardTileLimit)
|
||||
_equalizeTiles[tileCount++] = adj;
|
||||
|
||||
if (adj.Air.Immutable && MonstermosDepressurization)
|
||||
if (adj.Space && MonstermosDepressurization)
|
||||
{
|
||||
// Looks like someone opened an airlock to space!
|
||||
|
||||
@@ -146,6 +144,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
var direction = (AtmosDirection) (1 << j);
|
||||
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
||||
var tile2 = otherTile.AdjacentTiles[j]!;
|
||||
DebugTools.Assert(tile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
|
||||
|
||||
// skip anything that isn't part of our current processing block.
|
||||
if (tile2.MonstermosInfo.FastDone || tile2.MonstermosInfo.LastQueueCycle != queueCycle)
|
||||
@@ -213,8 +212,8 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
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;
|
||||
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
|
||||
if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
||||
|
||||
_equalizeQueue[queueLength++] = otherTile2;
|
||||
otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow;
|
||||
otherTile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite();
|
||||
@@ -279,6 +278,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
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;
|
||||
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
|
||||
if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
||||
_equalizeQueue[queueLength++] = otherTile2;
|
||||
otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow;
|
||||
@@ -335,7 +335,8 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
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;
|
||||
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
|
||||
if (otherTile2.Air != null && CompareExchange(otherTile2.Air, tile.Air) == GasCompareResult.NoExchange) continue;
|
||||
AddActiveTile(gridAtmosphere, otherTile2);
|
||||
break;
|
||||
}
|
||||
@@ -372,7 +373,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
otherTile.MonstermosInfo.LastCycle = cycleNum;
|
||||
otherTile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
|
||||
// Tiles in the _depressurizeTiles array cannot have null air.
|
||||
if (!otherTile.Air!.Immutable)
|
||||
if (!otherTile.Space)
|
||||
{
|
||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||
{
|
||||
@@ -380,6 +381,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
||||
var otherTile2 = otherTile.AdjacentTiles[j];
|
||||
if (otherTile2?.Air == null) continue;
|
||||
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
|
||||
if (otherTile2.MonstermosInfo.LastQueueCycle == queueCycle) continue;
|
||||
|
||||
ConsiderFirelocks(gridAtmosphere, otherTile, otherTile2);
|
||||
@@ -421,11 +423,12 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << j);
|
||||
// Tiles in _depressurizeProgressionOrder cannot have null air.
|
||||
if (!otherTile.AdjacentBits.IsFlagSet(direction) && !otherTile.Air!.Immutable) continue;
|
||||
if (!otherTile.AdjacentBits.IsFlagSet(direction) && !otherTile.Space) continue;
|
||||
var tile2 = otherTile.AdjacentTiles[j];
|
||||
if (tile2?.MonstermosInfo.LastQueueCycle != queueCycle) continue;
|
||||
DebugTools.Assert(tile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
|
||||
if (tile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
||||
if(tile2.Air?.Immutable ?? false) continue;
|
||||
if(tile2.Space) continue;
|
||||
tile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite();
|
||||
tile2.MonstermosInfo.CurrentTransferAmount = 0;
|
||||
tile2.PressureSpecificTarget = otherTile.PressureSpecificTarget;
|
||||
@@ -479,7 +482,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
}
|
||||
|
||||
if(tileCount > 10 && (totalMolesRemoved / tileCount) > 20)
|
||||
_adminLogger.Add(LogType.ExplosiveDepressurization, LogImpact.High,
|
||||
_adminLog.Add(LogType.ExplosiveDepressurization, LogImpact.High,
|
||||
$"Explosive depressurization removed {totalMolesRemoved} moles from {tileCount} tiles starting from position {tile.GridIndices:position} on grid ID {tile.GridIndex:grid}");
|
||||
|
||||
Array.Clear(_depressurizeTiles, 0, Atmospherics.MonstermosHardTileLimit);
|
||||
@@ -513,8 +516,10 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if (!reconsiderAdjacent)
|
||||
return;
|
||||
|
||||
UpdateAdjacent(mapGrid, gridAtmosphere, tile);
|
||||
UpdateAdjacent(mapGrid, gridAtmosphere, other);
|
||||
var tileEv = new UpdateAdjacentMethodEvent(mapGrid.GridEntityId, tile.GridIndices);
|
||||
var otherEv = new UpdateAdjacentMethodEvent(mapGrid.GridEntityId, other.GridIndices);
|
||||
GridUpdateAdjacent(mapGrid.GridEntityId, gridAtmosphere, ref tileEv);
|
||||
GridUpdateAdjacent(mapGrid.GridEntityId, gridAtmosphere, ref otherEv);
|
||||
InvalidateVisuals(tile.GridIndex, tile.GridIndices);
|
||||
InvalidateVisuals(other.GridIndex, other.GridIndices);
|
||||
}
|
||||
@@ -541,6 +546,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
var amount = transferDirections[i];
|
||||
var otherTile = tile.AdjacentTiles[i];
|
||||
if (otherTile?.Air == null) continue;
|
||||
DebugTools.Assert(otherTile.AdjacentBits.IsFlagSet(direction.GetOpposite()));
|
||||
if (amount <= 0) continue;
|
||||
|
||||
// Everything that calls this method already ensures that Air will not be null.
|
||||
@@ -569,6 +575,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
private void AdjustEqMovement(TileAtmosphere tile, AtmosDirection direction, float amount)
|
||||
{
|
||||
DebugTools.AssertNotNull(tile);
|
||||
DebugTools.Assert(tile.AdjacentBits.IsFlagSet(direction));
|
||||
DebugTools.Assert(tile.AdjacentTiles[direction.ToIndex()] != null);
|
||||
tile.MonstermosInfo[direction] += amount;
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Server.Atmos.Piping.Components;
|
||||
using Content.Server.NodeContainer.NodeGroups;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems
|
||||
@@ -42,37 +43,45 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
atmosphere.InvalidatedCoords.Clear();
|
||||
}
|
||||
|
||||
if (!TryGetMapGrid(atmosphere, out var mapGrid))
|
||||
var uid = atmosphere.Owner;
|
||||
|
||||
if (!TryComp(uid, out IMapGridComponent? mapGridComp))
|
||||
return true;
|
||||
|
||||
var mapGrid = mapGridComp.Grid;
|
||||
var mapUid = _mapManager.GetMapEntityIdOrThrow(mapGridComp.Grid.ParentMapId);
|
||||
|
||||
var volume = GetVolumeForTiles(mapGrid, 1);
|
||||
|
||||
var number = 0;
|
||||
while (atmosphere.CurrentRunInvalidatedCoordinates.TryDequeue(out var indices))
|
||||
{
|
||||
var tile = GetTileAtmosphere(atmosphere, indices);
|
||||
|
||||
if (tile == null)
|
||||
if (!atmosphere.Tiles.TryGetValue(indices, out var tile))
|
||||
{
|
||||
tile = new TileAtmosphere(mapGrid.GridEntityId, indices, new GasMixture(volume){Temperature = Atmospherics.T20C});
|
||||
atmosphere.Tiles[indices] = tile;
|
||||
}
|
||||
|
||||
var isAirBlocked = IsTileAirBlocked(mapGrid, indices);
|
||||
var airBlockedEv = new IsTileAirBlockedMethodEvent(uid, indices, MapGridComponent:mapGridComp);
|
||||
GridIsTileAirBlocked(uid, atmosphere, ref airBlockedEv);
|
||||
var isAirBlocked = airBlockedEv.Result;
|
||||
|
||||
UpdateAdjacent(mapGrid, atmosphere, tile);
|
||||
var updateAdjacentEv = new UpdateAdjacentMethodEvent(uid, indices, mapGridComp);
|
||||
GridUpdateAdjacent(uid, atmosphere, ref updateAdjacentEv);
|
||||
|
||||
if (IsTileSpace(mapGrid, indices) && !isAirBlocked)
|
||||
// Call this instead of the grid method as the map has a say on whether the tile is space or not.
|
||||
if ((!mapGrid.TryGetTileRef(indices, out var t) || t.IsSpace(_tileDefinitionManager)) && !isAirBlocked)
|
||||
{
|
||||
tile.Air = new GasMixture(volume);
|
||||
tile.Air.MarkImmutable();
|
||||
atmosphere.Tiles[indices] = tile;
|
||||
|
||||
tile.Air = GetTileMixture(null, mapUid, indices);
|
||||
tile.MolesArchived = tile.Air != null ? new float[Atmospherics.AdjustedNumberOfGases] : null;
|
||||
tile.Space = IsTileSpace(null, mapUid, indices, mapGridComp);
|
||||
} else if (isAirBlocked)
|
||||
{
|
||||
var nullAir = false;
|
||||
|
||||
foreach (var airtight in GetObstructingComponents(mapGrid, indices))
|
||||
var enumerator = GetObstructingComponentsEnumerator(mapGrid, indices);
|
||||
|
||||
while (enumerator.MoveNext(out var airtight))
|
||||
{
|
||||
if (!airtight.NoAirWhenFullyAirBlocked)
|
||||
continue;
|
||||
@@ -84,6 +93,9 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if (nullAir)
|
||||
{
|
||||
tile.Air = null;
|
||||
tile.MolesArchived = null;
|
||||
tile.ArchivedCycle = 0;
|
||||
tile.LastShare = 0f;
|
||||
tile.Hotspot = new Hotspot();
|
||||
}
|
||||
}
|
||||
@@ -91,23 +103,31 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
if (tile.Air == null && NeedsVacuumFixing(mapGrid, indices))
|
||||
{
|
||||
FixVacuum(atmosphere, tile.GridIndices);
|
||||
var vacuumEv = new FixTileVacuumMethodEvent(uid, indices);
|
||||
GridFixTileVacuum(uid, atmosphere, ref vacuumEv);
|
||||
}
|
||||
|
||||
// Tile used to be space, but isn't anymore.
|
||||
if (tile.Air?.Immutable ?? false)
|
||||
if (tile.Space || (tile.Air?.Immutable ?? false))
|
||||
{
|
||||
tile.Air = null;
|
||||
tile.MolesArchived = null;
|
||||
tile.ArchivedCycle = 0;
|
||||
tile.LastShare = 0f;
|
||||
tile.Space = false;
|
||||
}
|
||||
|
||||
tile.Air ??= new GasMixture(volume){Temperature = Atmospherics.T20C};
|
||||
tile.MolesArchived ??= new float[Atmospherics.AdjustedNumberOfGases];
|
||||
}
|
||||
|
||||
// We activate the tile.
|
||||
AddActiveTile(atmosphere, tile);
|
||||
|
||||
// TODO ATMOS: Query all the contents of this tile (like walls) and calculate the correct thermal conductivity and heat capacity
|
||||
var tileDef = GetTile(tile)?.Tile.GetContentTileDefinition(_tileDefinitionManager);
|
||||
var tileDef = mapGrid.TryGetTileRef(indices, out var tileRef)
|
||||
? tileRef.GetContentTileDefinition(_tileDefinitionManager) : null;
|
||||
|
||||
tile.ThermalConductivity = tileDef?.ThermalConductivity ?? 0.5f;
|
||||
tile.HeatCapacity = tileDef?.HeatCapacity ?? float.PositiveInfinity;
|
||||
InvalidateVisuals(mapGrid.GridEntityId, indices);
|
||||
@@ -116,8 +136,8 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
var direction = (AtmosDirection) (1 << i);
|
||||
var otherIndices = indices.Offset(direction);
|
||||
var otherTile = GetTileAtmosphere(atmosphere, otherIndices);
|
||||
if (otherTile != null)
|
||||
|
||||
if (atmosphere.Tiles.TryGetValue(otherIndices, out var otherTile))
|
||||
AddActiveTile(atmosphere, otherTile);
|
||||
}
|
||||
|
||||
@@ -138,9 +158,13 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if(!atmosphere.ProcessingPaused)
|
||||
atmosphere.CurrentRunTiles = new Queue<TileAtmosphere>(atmosphere.ActiveTiles);
|
||||
|
||||
if (!TryGetMapGrid(atmosphere, out var mapGrid))
|
||||
var uid = atmosphere.Owner;
|
||||
|
||||
if (!TryComp(uid, out IMapGridComponent? mapGridComp))
|
||||
throw new Exception("Tried to process a grid atmosphere on an entity that isn't a grid!");
|
||||
|
||||
var mapGrid = mapGridComp.Grid;
|
||||
|
||||
var number = 0;
|
||||
while (atmosphere.CurrentRunTiles.TryDequeue(out var tile))
|
||||
{
|
||||
|
||||
@@ -73,7 +73,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
// 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);
|
||||
tile.Temperature = TemperatureShare(tile, tile.ThermalConductivity, tile.Temperature, tile.HeatCapacity);
|
||||
}
|
||||
|
||||
FinishSuperconduction(gridAtmosphere, tile, tile.Air?.Temperature ?? tile.Temperature);
|
||||
@@ -107,7 +107,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
if (other.Air != null)
|
||||
{
|
||||
TemperatureShare(other.Air, tile.Air, Atmospherics.WindowHeatTransferCoefficient);
|
||||
TemperatureShare(other, tile, Atmospherics.WindowHeatTransferCoefficient);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -122,7 +122,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
if (tile.Air == null)
|
||||
return;
|
||||
|
||||
other.Temperature = TemperatureShare(tile.Air, other.ThermalConductivity, other.Temperature, other.HeatCapacity);
|
||||
other.Temperature = TemperatureShare(tile, other.ThermalConductivity, other.Temperature, other.HeatCapacity);
|
||||
}
|
||||
|
||||
private void TemperatureShareMutualSolid(TileAtmosphere tile, TileAtmosphere other, float conductionCoefficient)
|
||||
|
||||
84
Content.Server/Atmos/EntitySystems/AtmosphereSystem.Utils.cs
Normal file
84
Content.Server/Atmos/EntitySystems/AtmosphereSystem.Utils.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Shared.Map;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems;
|
||||
|
||||
public partial class AtmosphereSystem
|
||||
{
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void InvalidateVisuals(EntityUid gridUid, Vector2i tile)
|
||||
{
|
||||
_gasTileOverlaySystem.Invalidate(gridUid, tile);
|
||||
}
|
||||
|
||||
public bool NeedsVacuumFixing(IMapGrid mapGrid, Vector2i indices)
|
||||
{
|
||||
var value = false;
|
||||
|
||||
var enumerator = GetObstructingComponentsEnumerator(mapGrid, indices);
|
||||
|
||||
while (enumerator.MoveNext(out var airtight))
|
||||
{
|
||||
value |= airtight.FixVacuum;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <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>
|
||||
private float GetVolumeForTiles(IMapGrid mapGrid, int tiles = 1)
|
||||
{
|
||||
return Atmospherics.CellVolume * mapGrid.TileSize * tiles;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets all obstructing <see cref="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>The enumerator for the airtight components.</returns>
|
||||
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 airtight))
|
||||
{
|
||||
if(airtight.AirBlocked)
|
||||
value |= airtight.AirBlockedDirection;
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
/// <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);
|
||||
}
|
||||
}
|
||||
@@ -4,11 +4,13 @@ using Content.Server.NodeContainer.EntitySystems;
|
||||
using Content.Shared.Atmos.EntitySystems;
|
||||
using Content.Shared.Maps;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems
|
||||
{
|
||||
/// <summary>
|
||||
/// This is our SSAir equivalent, if you need to interact with or query atmos in any way, go through this.
|
||||
/// </summary>
|
||||
@@ -16,9 +18,13 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
public sealed partial class AtmosphereSystem : SharedAtmosphereSystem
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLog = default!;
|
||||
[Dependency] private readonly SharedContainerSystem _containers = default!;
|
||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
||||
[Dependency] private readonly GasTileOverlaySystem _gasTileOverlaySystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
|
||||
|
||||
private const float ExposedUpdateDelay = 1f;
|
||||
@@ -33,7 +39,8 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
InitializeGases();
|
||||
InitializeCommands();
|
||||
InitializeCVars();
|
||||
InitializeGrid();
|
||||
InitializeGridAtmosphere();
|
||||
InitializeMap();
|
||||
|
||||
|
||||
SubscribeLocalEvent<TileChangedEvent>(OnTileChanged);
|
||||
@@ -49,15 +56,6 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
private void OnTileChanged(TileChangedEvent ev)
|
||||
{
|
||||
// When a tile changes, we want to update it only if it's gone from
|
||||
// space -> not space or vice versa. So if the old tile is the
|
||||
// same as the new tile in terms of space-ness, ignore the change
|
||||
|
||||
if (ev.NewTile.IsSpace(_tileDefinitionManager) == ev.OldTile.IsSpace(_tileDefinitionManager))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
InvalidateTile(ev.NewTile.GridUid, ev.NewTile.GridIndices);
|
||||
}
|
||||
|
||||
@@ -75,19 +73,15 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
foreach (var (exposed, transform) in EntityManager.EntityQuery<AtmosExposedComponent, TransformComponent>())
|
||||
{
|
||||
// Used for things like disposals/cryo to change which air people are exposed to.
|
||||
var airEvent = new AtmosExposedGetAirEvent();
|
||||
RaiseLocalEvent(exposed.Owner, ref airEvent, false);
|
||||
var air = GetContainingMixture(exposed.Owner, transform:transform);
|
||||
|
||||
airEvent.Gas ??= GetTileMixture(transform.Coordinates);
|
||||
if (airEvent.Gas == null)
|
||||
if (air == null)
|
||||
continue;
|
||||
|
||||
var updateEvent = new AtmosExposedUpdateEvent(transform.Coordinates, airEvent.Gas);
|
||||
RaiseLocalEvent(exposed.Owner, ref updateEvent, true);
|
||||
var updateEvent = new AtmosExposedUpdateEvent(transform.Coordinates, air, transform);
|
||||
RaiseLocalEvent(exposed.Owner, ref updateEvent);
|
||||
}
|
||||
|
||||
_exposedTimer -= ExposedUpdateDelay;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,6 +12,7 @@ namespace Content.Server.Atmos.EntitySystems;
|
||||
public sealed class AutomaticAtmosSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -27,7 +28,7 @@ public sealed class AutomaticAtmosSystem : EntitySystem
|
||||
// TODO: a simple array lookup, as tile IDs are likely contiguous, and there's at most 2^16 possibilities anyway.
|
||||
if (!((ev.OldTile.IsSpace(_tileDefinitionManager) && !ev.NewTile.IsSpace(_tileDefinitionManager)) ||
|
||||
(!ev.OldTile.IsSpace(_tileDefinitionManager) && ev.NewTile.IsSpace(_tileDefinitionManager))) ||
|
||||
HasComp<IAtmosphereComponent>(ev.Entity))
|
||||
_atmosphereSystem.HasAtmosphere(ev.Entity))
|
||||
return;
|
||||
|
||||
if (!TryComp<PhysicsComponent>(ev.Entity, out var physics))
|
||||
|
||||
@@ -140,6 +140,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
foreach (var (barotrauma, damageable, transform) in EntityManager.EntityQuery<BarotraumaComponent, DamageableComponent, TransformComponent>())
|
||||
{
|
||||
var uid = barotrauma.Owner;
|
||||
var totalDamage = FixedPoint2.Zero;
|
||||
foreach (var (barotraumaDamageType, _) in barotrauma.Damage.DamageDict)
|
||||
{
|
||||
@@ -152,7 +153,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
var pressure = 1f;
|
||||
|
||||
if (_atmosphereSystem.GetTileMixture(transform.Coordinates) is { } mixture)
|
||||
if (_atmosphereSystem.GetContainingMixture(uid) is {} mixture)
|
||||
{
|
||||
pressure = MathF.Max(mixture.Pressure, 1f);
|
||||
}
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Stunnable;
|
||||
@@ -12,6 +14,10 @@ using Content.Shared.Database;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Popups;
|
||||
using Content.Shared.Temperature;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Physics.Dynamics;
|
||||
|
||||
@@ -25,6 +31,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
[Dependency] private readonly TemperatureSystem _temperatureSystem = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
|
||||
private const float MinimumFireStacks = -10f;
|
||||
@@ -272,7 +279,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
continue;
|
||||
}
|
||||
|
||||
var air = _atmosphereSystem.GetTileMixture(transform.Coordinates);
|
||||
var air = _atmosphereSystem.GetContainingMixture(uid);
|
||||
|
||||
// If we're in an oxygenless environment, put the fire out.
|
||||
if (air == null || air.GetMoles(Gas.Oxygen) < 1f)
|
||||
@@ -281,7 +288,13 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
continue;
|
||||
}
|
||||
|
||||
_atmosphereSystem.HotspotExpose(transform.Coordinates, 700f, 50f, true);
|
||||
if(transform.GridUid != null)
|
||||
{
|
||||
_atmosphereSystem.HotspotExpose(transform.GridUid.Value,
|
||||
_transformSystem.GetGridOrMapTilePosition(uid, transform),
|
||||
700f, 50f, true);
|
||||
|
||||
}
|
||||
|
||||
foreach (var otherUid in flammable.Collided.ToArray())
|
||||
{
|
||||
|
||||
@@ -140,9 +140,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
/// <returns>true if updated</returns>
|
||||
private bool TryRefreshTile(GridAtmosphereComponent gridAtmosphere, GasOverlayData oldTile, Vector2i indices, out GasOverlayData overlayData)
|
||||
{
|
||||
var tile = _atmosphereSystem.GetTileAtmosphere(gridAtmosphere, indices);
|
||||
|
||||
if (tile == null)
|
||||
if (!gridAtmosphere.Tiles.TryGetValue(indices, out var tile))
|
||||
{
|
||||
overlayData = default;
|
||||
return false;
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
using System.Linq;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Atmos.Reactions;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.Serialization;
|
||||
@@ -19,17 +21,12 @@ namespace Content.Server.Atmos
|
||||
[DataField("moles")] [ViewVariables]
|
||||
public float[] Moles = new float[Atmospherics.AdjustedNumberOfGases];
|
||||
|
||||
public float[] MolesArchived = new float[Atmospherics.AdjustedNumberOfGases];
|
||||
|
||||
[DataField("temperature")] [ViewVariables]
|
||||
private float _temperature = Atmospherics.TCMB;
|
||||
|
||||
[DataField("immutable")] [ViewVariables]
|
||||
public bool Immutable { get; private set; }
|
||||
|
||||
[DataField("lastShare")] [ViewVariables]
|
||||
public float LastShare { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
public readonly Dictionary<GasReaction, float> ReactionResults = new()
|
||||
{
|
||||
@@ -65,8 +62,6 @@ namespace Content.Server.Atmos
|
||||
}
|
||||
}
|
||||
|
||||
public float TemperatureArchived { get; private set; }
|
||||
|
||||
[DataField("volume")] [ViewVariables]
|
||||
public float Volume { get; set; }
|
||||
|
||||
@@ -87,13 +82,6 @@ namespace Content.Server.Atmos
|
||||
Immutable = true;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Archive()
|
||||
{
|
||||
Moles.AsSpan().CopyTo(MolesArchived.AsSpan());
|
||||
TemperatureArchived = Temperature;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public float GetMoles(int gasId)
|
||||
{
|
||||
@@ -192,40 +180,6 @@ namespace Content.Server.Atmos
|
||||
Temperature = sample.Temperature;
|
||||
}
|
||||
|
||||
public enum GasCompareResult
|
||||
{
|
||||
NoExchange = -2,
|
||||
TemperatureExchange = -1,
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Compares sample to self to see if within acceptable ranges that group processing may be enabled.
|
||||
/// </summary>
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public GasCompareResult Compare(GasMixture sample)
|
||||
{
|
||||
var moles = 0f;
|
||||
|
||||
for(var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||
{
|
||||
var gasMoles = Moles[i];
|
||||
var delta = MathF.Abs(gasMoles - sample.Moles[i]);
|
||||
if (delta > Atmospherics.MinimumMolesDeltaToMove && (delta > gasMoles * Atmospherics.MinimumAirRatioToMove))
|
||||
return (GasCompareResult)i; // We can move gases!
|
||||
moles += gasMoles;
|
||||
}
|
||||
|
||||
if (moles > Atmospherics.MinimumMolesDeltaToMove)
|
||||
{
|
||||
var tempDelta = MathF.Abs(Temperature - sample.Temperature);
|
||||
if (tempDelta > Atmospherics.MinimumTemperatureDeltaToSuspend)
|
||||
return GasCompareResult.TemperatureExchange; // There can be temperature exchange.
|
||||
}
|
||||
|
||||
// No exchange at all!
|
||||
return GasCompareResult.NoExchange;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Clear()
|
||||
{
|
||||
@@ -244,7 +198,6 @@ namespace Content.Server.Atmos
|
||||
{
|
||||
// The arrays MUST have a specific length.
|
||||
Array.Resize(ref Moles, Atmospherics.AdjustedNumberOfGases);
|
||||
Array.Resize(ref MolesArchived, Atmospherics.AdjustedNumberOfGases);
|
||||
}
|
||||
|
||||
public override bool Equals(object? obj)
|
||||
@@ -259,15 +212,13 @@ namespace Content.Server.Atmos
|
||||
if (ReferenceEquals(null, other)) return false;
|
||||
if (ReferenceEquals(this, other)) return true;
|
||||
return Moles.SequenceEqual(other.Moles)
|
||||
&& MolesArchived.SequenceEqual(other.MolesArchived)
|
||||
&& _temperature.Equals(other._temperature)
|
||||
&& ReactionResults.SequenceEqual(other.ReactionResults)
|
||||
&& Immutable == other.Immutable
|
||||
&& LastShare.Equals(other.LastShare)
|
||||
&& TemperatureArchived.Equals(other.TemperatureArchived)
|
||||
&& Volume.Equals(other.Volume);
|
||||
}
|
||||
|
||||
[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
|
||||
public override int GetHashCode()
|
||||
{
|
||||
var hashCode = new HashCode();
|
||||
@@ -275,15 +226,11 @@ namespace Content.Server.Atmos
|
||||
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||
{
|
||||
var moles = Moles[i];
|
||||
var molesArchived = MolesArchived[i];
|
||||
hashCode.Add(moles);
|
||||
hashCode.Add(molesArchived);
|
||||
}
|
||||
|
||||
hashCode.Add(_temperature);
|
||||
hashCode.Add(TemperatureArchived);
|
||||
hashCode.Add(Immutable);
|
||||
hashCode.Add(LastShare);
|
||||
hashCode.Add(Volume);
|
||||
|
||||
return hashCode.ToHashCode();
|
||||
@@ -294,11 +241,8 @@ namespace Content.Server.Atmos
|
||||
var newMixture = new GasMixture()
|
||||
{
|
||||
Moles = (float[])Moles.Clone(),
|
||||
MolesArchived = (float[])MolesArchived.Clone(),
|
||||
_temperature = _temperature,
|
||||
Immutable = Immutable,
|
||||
LastShare = LastShare,
|
||||
TemperatureArchived = TemperatureArchived,
|
||||
Volume = Volume,
|
||||
};
|
||||
return newMixture;
|
||||
|
||||
@@ -5,6 +5,7 @@ using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Temperature.Systems;
|
||||
using Content.Server.Body.Components;
|
||||
using Content.Shared.Examine;
|
||||
using Robust.Server.GameObjects;
|
||||
using Content.Shared.Tag;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Random;
|
||||
@@ -13,6 +14,7 @@ namespace Content.Server.Atmos.Miasma
|
||||
{
|
||||
public sealed class MiasmaSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||
|
||||
@@ -108,9 +110,11 @@ namespace Content.Server.Atmos.Miasma
|
||||
|
||||
float molRate = perishable.MolsPerSecondPerUnitMass * _rotUpdateRate;
|
||||
|
||||
var tileMix = _atmosphereSystem.GetTileMixture(Transform(perishable.Owner).Coordinates);
|
||||
if (tileMix != null)
|
||||
tileMix.AdjustMoles(Gas.Miasma, molRate * physics.FixturesMass);
|
||||
var transform = Transform(perishable.Owner);
|
||||
var indices = _transformSystem.GetGridOrMapTilePosition(perishable.Owner);
|
||||
|
||||
var tileMix = _atmosphereSystem.GetTileMixture(transform.GridUid, null, indices, true);
|
||||
tileMix?.AdjustMoles(Gas.Miasma, molRate * physics.FixturesMass);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -167,9 +171,10 @@ namespace Content.Server.Atmos.Miasma
|
||||
return;
|
||||
|
||||
var molsToDump = (component.MolsPerSecondPerUnitMass * physics.FixturesMass) * component.DeathAccumulator;
|
||||
var tileMix = _atmosphereSystem.GetTileMixture(Transform(uid).Coordinates);
|
||||
if (tileMix != null)
|
||||
tileMix.AdjustMoles(Gas.Miasma, molsToDump);
|
||||
var transform = Transform(uid);
|
||||
var indices = _transformSystem.GetGridOrMapTilePosition(uid, transform);
|
||||
var tileMix = _atmosphereSystem.GetTileMixture(transform.GridUid, null, indices, true);
|
||||
tileMix?.AdjustMoles(Gas.Miasma, molsToDump);
|
||||
|
||||
// Waste of entities to let these through
|
||||
foreach (var part in args.GibbedParts)
|
||||
|
||||
@@ -9,6 +9,7 @@ using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Monitor;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
@@ -24,6 +25,7 @@ namespace Content.Server.Atmos.Monitor.Systems
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly AtmosDeviceSystem _atmosDeviceSystem = default!;
|
||||
[Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
// Commands
|
||||
@@ -118,27 +120,31 @@ namespace Content.Server.Atmos.Monitor.Systems
|
||||
if (!Resolve(uid, ref component, ref appearance)) return;
|
||||
|
||||
var transform = Transform(component.Owner);
|
||||
|
||||
if (transform.GridUid == null)
|
||||
return;
|
||||
|
||||
// atmos alarms will first attempt to get the air
|
||||
// directly underneath it - if not, then it will
|
||||
// instead place itself directly in front of the tile
|
||||
// it is facing, and then visually shift itself back
|
||||
// via sprite offsets (SS13 style but fuck it)
|
||||
var coords = transform.Coordinates;
|
||||
var pos = _transformSystem.GetGridOrMapTilePosition(uid, transform);
|
||||
|
||||
if (_atmosphereSystem.IsTileAirBlocked(coords))
|
||||
if (_atmosphereSystem.IsTileAirBlocked(transform.GridUid.Value, pos))
|
||||
{
|
||||
|
||||
var rotPos = transform.LocalRotation.RotateVec(new Vector2(0, -1));
|
||||
transform.Anchored = false;
|
||||
coords = coords.Offset(rotPos);
|
||||
transform.Coordinates = coords;
|
||||
|
||||
appearance.SetData("offset", - new Vector2(0, -1));
|
||||
appearance.SetData("offset", - new Vector2i(0, -1));
|
||||
|
||||
transform.Anchored = true;
|
||||
}
|
||||
|
||||
GasMixture? air = _atmosphereSystem.GetTileMixture(coords);
|
||||
GasMixture? air = _atmosphereSystem.GetContainingMixture(uid, true);
|
||||
component.TileGas = air;
|
||||
|
||||
_checkPos.Remove(uid);
|
||||
@@ -214,8 +220,7 @@ namespace Content.Server.Atmos.Monitor.Systems
|
||||
if (atmosDeviceComponent.JoinedGrid == null)
|
||||
{
|
||||
_atmosDeviceSystem.JoinAtmosphere(atmosDeviceComponent);
|
||||
var coords = Transform(component.Owner).Coordinates;
|
||||
var air = _atmosphereSystem.GetTileMixture(coords);
|
||||
var air = _atmosphereSystem.GetContainingMixture(uid, true);
|
||||
component.TileGas = air;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,9 +21,10 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
public sealed class GasVolumePumpSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
|
||||
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
|
||||
[Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
@@ -65,7 +66,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
|| !nodeContainer.TryGetNode(pump.InletName, out PipeNode? inlet)
|
||||
|| !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet))
|
||||
{
|
||||
_ambientSoundSystem.SetAmbience(pump.Owner, false);
|
||||
_ambientSoundSystem.SetAmbience(uid, false);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -88,7 +89,9 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
// Some of the gas from the mixture leaks when overclocked.
|
||||
if (pump.Overclocked)
|
||||
{
|
||||
var tile = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent<TransformComponent>(pump.Owner).Coordinates, true);
|
||||
var transform = Transform(uid);
|
||||
var indices = _transformSystem.GetGridOrMapTilePosition(uid, transform);
|
||||
var tile = _atmosphereSystem.GetTileMixture(transform.GridUid, null, indices, true);
|
||||
|
||||
if (tile != null)
|
||||
{
|
||||
@@ -98,7 +101,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
}
|
||||
|
||||
_atmosphereSystem.Merge(outlet.Air, removed);
|
||||
_ambientSoundSystem.SetAmbience(pump.Owner, removed.TotalMoles > 0f);
|
||||
_ambientSoundSystem.SetAmbience(uid, removed.TotalMoles > 0f);
|
||||
}
|
||||
|
||||
private void OnVolumePumpLeaveAtmosphere(EntityUid uid, GasVolumePumpComponent pump, AtmosDeviceDisabledEvent args)
|
||||
|
||||
@@ -27,20 +27,25 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
SubscribeLocalEvent<AtmosDeviceComponent, AnchorStateChangedEvent>(OnDeviceAnchorChanged);
|
||||
}
|
||||
|
||||
private bool CanJoinAtmosphere(AtmosDeviceComponent component)
|
||||
private bool CanJoinAtmosphere(AtmosDeviceComponent component, TransformComponent transform)
|
||||
{
|
||||
return !component.RequireAnchored || EntityManager.GetComponent<TransformComponent>(component.Owner).Anchored;
|
||||
return (!component.RequireAnchored || transform.Anchored) && transform.GridUid != null;
|
||||
}
|
||||
|
||||
public void JoinAtmosphere(AtmosDeviceComponent component)
|
||||
{
|
||||
if (!CanJoinAtmosphere(component))
|
||||
var transform = Transform(component.Owner);
|
||||
|
||||
if (!CanJoinAtmosphere(component, transform))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// TODO: low-hanging fruit for perf improvements around here
|
||||
|
||||
// GridUid is not null because we can join atmosphere.
|
||||
// We try to add the device to a valid atmosphere, and if we can't, try to add it to the entity system.
|
||||
if (!_atmosphereSystem.AddAtmosDevice(component))
|
||||
if (!_atmosphereSystem.AddAtmosDevice(transform.GridUid!.Value, component))
|
||||
{
|
||||
if (component.JoinSystem)
|
||||
{
|
||||
@@ -62,7 +67,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
public void LeaveAtmosphere(AtmosDeviceComponent component)
|
||||
{
|
||||
// Try to remove the component from an atmosphere, and if not
|
||||
if (component.JoinedGrid != null && !_atmosphereSystem.RemoveAtmosDevice(component))
|
||||
if (component.JoinedGrid != null && !_atmosphereSystem.RemoveAtmosDevice(component.JoinedGrid.Value, component))
|
||||
{
|
||||
// The grid might have been removed but not us... This usually shouldn't happen.
|
||||
component.JoinedGrid = null;
|
||||
|
||||
@@ -27,7 +27,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
if (!component.Enabled || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodes))
|
||||
return;
|
||||
|
||||
if (_atmosphereSystem.GetTileMixture(EntityManager.GetComponent<TransformComponent>(component.Owner).Coordinates) is not {} environment)
|
||||
if (_atmosphereSystem.GetContainingMixture(uid, true) is not {} environment)
|
||||
return;
|
||||
|
||||
foreach (var node in nodes.Nodes.Values)
|
||||
@@ -48,7 +48,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems
|
||||
if (!component.Enabled || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodes))
|
||||
return;
|
||||
|
||||
if (_atmosphereSystem.GetTileMixture(EntityManager.GetComponent<TransformComponent>(component.Owner).Coordinates, true) is not {} environment)
|
||||
if (_atmosphereSystem.GetContainingMixture(uid, true, true) is not {} environment)
|
||||
environment = GasMixture.SpaceGas;
|
||||
|
||||
var lost = 0f;
|
||||
|
||||
@@ -1,9 +1,13 @@
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Atmos.Piping.Components;
|
||||
using Content.Server.Atmos.Piping.Other.Components;
|
||||
using Content.Shared.Atmos;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Other.EntitySystems
|
||||
{
|
||||
@@ -11,6 +15,7 @@ namespace Content.Server.Atmos.Piping.Other.EntitySystems
|
||||
public sealed class GasMinerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -34,10 +39,14 @@ namespace Content.Server.Atmos.Piping.Other.EntitySystems
|
||||
|
||||
private bool CheckMinerOperation(GasMinerComponent miner, [NotNullWhen(true)] out GasMixture? environment)
|
||||
{
|
||||
environment = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent<TransformComponent>(miner.Owner).Coordinates, true);
|
||||
var uid = miner.Owner;
|
||||
environment = _atmosphereSystem.GetContainingMixture(uid, true, true);
|
||||
|
||||
var transform = Transform(uid);
|
||||
var position = _transformSystem.GetGridOrMapTilePosition(uid, transform);
|
||||
|
||||
// Space.
|
||||
if (_atmosphereSystem.IsTileSpace(EntityManager.GetComponent<TransformComponent>(miner.Owner).Coordinates))
|
||||
if (_atmosphereSystem.IsTileSpace(transform.GridUid, transform.MapUid, position))
|
||||
{
|
||||
miner.Broken = true;
|
||||
return false;
|
||||
|
||||
@@ -51,7 +51,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
||||
if (!Resolve(uid, ref canister, ref transform))
|
||||
return;
|
||||
|
||||
var environment = _atmosphereSystem.GetTileMixture(transform.Coordinates, true);
|
||||
var environment = _atmosphereSystem.GetContainingMixture(uid, false, true);
|
||||
|
||||
if (environment is not null)
|
||||
_atmosphereSystem.Merge(environment, canister.Air);
|
||||
@@ -180,7 +180,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
||||
}
|
||||
else
|
||||
{
|
||||
var environment = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent<TransformComponent>(canister.Owner).Coordinates, true);
|
||||
var environment = _atmosphereSystem.GetContainingMixture(uid, false, true);
|
||||
_atmosphereSystem.ReleaseGasTo(canister.Air, environment, canister.ReleasePressure);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,7 +58,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
||||
if (!nodeContainer.TryGetNode(injector.InletName, out PipeNode? inlet))
|
||||
return;
|
||||
|
||||
var environment = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent<TransformComponent>(injector.Owner).Coordinates, true);
|
||||
var environment = _atmosphereSystem.GetContainingMixture(uid, true, true);
|
||||
|
||||
if (environment == null)
|
||||
return;
|
||||
|
||||
@@ -22,7 +22,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
||||
|
||||
private void OnPassiveVentUpdated(EntityUid uid, GasPassiveVentComponent vent, AtmosDeviceUpdateEvent args)
|
||||
{
|
||||
var environment = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent<TransformComponent>(vent.Owner).Coordinates, true);
|
||||
var environment = _atmosphereSystem.GetContainingMixture(uid, true, true);
|
||||
|
||||
if (environment == null)
|
||||
return;
|
||||
|
||||
@@ -67,7 +67,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
||||
return;
|
||||
}
|
||||
|
||||
var environment = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent<TransformComponent>(vent.Owner).Coordinates, true);
|
||||
var environment = _atmosphereSystem.GetContainingMixture(uid, true, true);
|
||||
|
||||
// We're in an air-blocked tile... Do nothing.
|
||||
if (environment == null)
|
||||
|
||||
@@ -15,6 +15,7 @@ using Content.Shared.Atmos.Monitor;
|
||||
using Content.Shared.Atmos.Piping.Unary.Components;
|
||||
using Content.Shared.Audio;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
||||
@@ -26,6 +27,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!;
|
||||
[Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -54,19 +56,24 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
|
||||
if (!scrubber.Enabled
|
||||
|| !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)
|
||||
|| !nodeContainer.TryGetNode(scrubber.OutletName, out PipeNode? outlet))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var xform = Transform(uid);
|
||||
var environment = _atmosphereSystem.GetTileMixture(xform.Coordinates, true);
|
||||
|
||||
if (xform.GridUid == null)
|
||||
return;
|
||||
|
||||
var position = _transformSystem.GetGridOrMapTilePosition(uid, xform);
|
||||
|
||||
var environment = _atmosphereSystem.GetTileMixture(xform.GridUid, xform.MapUid, position, true);
|
||||
|
||||
Scrub(timeDelta, scrubber, environment, outlet);
|
||||
|
||||
if (!scrubber.WideNet) return;
|
||||
if (!scrubber.WideNet)
|
||||
return;
|
||||
|
||||
// Scrub adjacent tiles too.
|
||||
foreach (var adjacent in _atmosphereSystem.GetAdjacentTileMixtures(xform.Coordinates, false, true))
|
||||
foreach (var adjacent in _atmosphereSystem.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true))
|
||||
{
|
||||
Scrub(timeDelta, scrubber, adjacent, outlet);
|
||||
}
|
||||
|
||||
@@ -9,7 +9,7 @@ namespace Content.Server.Atmos
|
||||
/// Internal Atmos class that stores data about the atmosphere in a grid.
|
||||
/// You shouldn't use this directly, use <see cref="AtmosphereSystem"/> instead.
|
||||
/// </summary>
|
||||
[Access(typeof(AtmosphereSystem))]
|
||||
[Access(typeof(AtmosphereSystem), typeof(GasTileOverlaySystem), typeof(AtmosDebugOverlaySystem))]
|
||||
public sealed class TileAtmosphere : IGasMixtureHolder
|
||||
{
|
||||
[ViewVariables]
|
||||
@@ -39,6 +39,12 @@ namespace Content.Server.Atmos
|
||||
[ViewVariables]
|
||||
public bool Excited { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether this tile should be considered space.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool Space { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Adjacent tiles in the same order as <see cref="AtmosDirection"/>. (NSEW)
|
||||
/// </summary>
|
||||
@@ -81,6 +87,13 @@ namespace Content.Server.Atmos
|
||||
[Access(typeof(AtmosphereSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
|
||||
public GasMixture? Air { get; set; }
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("lastShare")]
|
||||
public float LastShare;
|
||||
|
||||
[ViewVariables]
|
||||
public float[]? MolesArchived;
|
||||
|
||||
GasMixture IGasMixtureHolder.Air
|
||||
{
|
||||
get => Air ?? new GasMixture(Atmospherics.CellVolume){ Temperature = Temperature };
|
||||
@@ -93,11 +106,13 @@ namespace Content.Server.Atmos
|
||||
[ViewVariables]
|
||||
public AtmosDirection BlockedAirflow { get; set; } = AtmosDirection.Invalid;
|
||||
|
||||
public TileAtmosphere(EntityUid gridIndex, Vector2i gridIndices, GasMixture? mixture = null, bool immutable = false)
|
||||
public TileAtmosphere(EntityUid gridIndex, Vector2i gridIndices, GasMixture? mixture = null, bool immutable = false, bool space = false)
|
||||
{
|
||||
GridIndex = gridIndex;
|
||||
GridIndices = gridIndices;
|
||||
Air = mixture;
|
||||
Space = space;
|
||||
MolesArchived = Air != null ? new float[Atmospherics.AdjustedNumberOfGases] : null;
|
||||
|
||||
if(immutable)
|
||||
Air?.MarkImmutable();
|
||||
|
||||
@@ -102,7 +102,7 @@ namespace Content.Server.Body.Systems
|
||||
|
||||
if (ev.Gas == null)
|
||||
{
|
||||
ev.Gas = _atmosSys.GetTileMixture(Transform(uid).Coordinates);
|
||||
ev.Gas = _atmosSys.GetContainingMixture(uid, false, true);
|
||||
if (ev.Gas == null) return;
|
||||
}
|
||||
|
||||
@@ -133,7 +133,7 @@ namespace Content.Server.Body.Systems
|
||||
|
||||
if (ev.Gas == null)
|
||||
{
|
||||
ev.Gas = _atmosSys.GetTileMixture(Transform(uid).Coordinates);
|
||||
ev.Gas = _atmosSys.GetContainingMixture(uid, false, true);
|
||||
|
||||
// Walls and grids without atmos comp return null. I guess it makes sense to not be able to exhale in walls,
|
||||
// but this also means you cannot exhale on some grids.
|
||||
|
||||
@@ -7,6 +7,7 @@ using Content.Shared.Botany;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.Reagent;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
@@ -225,7 +226,8 @@ namespace Content.Server.Botany.Components
|
||||
_updateSpriteAfterUpdate = true;
|
||||
}
|
||||
|
||||
var environment = EntitySystem.Get<AtmosphereSystem>().GetTileMixture(_entMan.GetComponent<TransformComponent>(Owner).Coordinates, true) ??
|
||||
var atmosphereSystem = _entMan.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
|
||||
var environment = atmosphereSystem.GetContainingMixture(Owner, true, true) ??
|
||||
GasMixture.SpaceGas;
|
||||
|
||||
if (Seed.ConsumeGasses.Count > 0)
|
||||
|
||||
@@ -21,10 +21,9 @@ public sealed class CreateGas : ReagentEffect
|
||||
|
||||
public override void Effect(ReagentEffectArgs args)
|
||||
{
|
||||
var atmosSys = EntitySystem.Get<AtmosphereSystem>();
|
||||
var atmosSys = args.EntityManager.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
|
||||
|
||||
var xform = args.EntityManager.GetComponent<TransformComponent>(args.SolutionEntity);
|
||||
var tileMix = atmosSys.GetTileMixture(xform.Coordinates);
|
||||
var tileMix = atmosSys.GetContainingMixture(args.SolutionEntity, false, true);
|
||||
|
||||
if (tileMix != null)
|
||||
{
|
||||
|
||||
@@ -21,7 +21,7 @@ namespace Content.Server.Chemistry.TileReactions
|
||||
|
||||
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
|
||||
var environment = atmosphereSystem.GetTileMixture(tile.GridUid, tile.GridIndices, true);
|
||||
var environment = atmosphereSystem.GetTileMixture(tile.GridUid, null, tile.GridIndices, true);
|
||||
|
||||
if (environment == null || !atmosphereSystem.IsHotspotActive(tile.GridUid, tile.GridIndices))
|
||||
return FixedPoint2.Zero;
|
||||
@@ -30,7 +30,7 @@ namespace Content.Server.Chemistry.TileReactions
|
||||
MathF.Max(MathF.Min(environment.Temperature - (_coolingTemperature * 1000f),
|
||||
environment.Temperature / _coolingTemperature), Atmospherics.TCMB);
|
||||
|
||||
atmosphereSystem.React(tile.GridUid, tile.GridIndices);
|
||||
atmosphereSystem.ReactTile(tile.GridUid, tile.GridIndices);
|
||||
atmosphereSystem.HotspotExtinguish(tile.GridUid, tile.GridIndices);
|
||||
|
||||
return FixedPoint2.Zero;
|
||||
|
||||
@@ -20,12 +20,12 @@ namespace Content.Server.Chemistry.TileReactions
|
||||
|
||||
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
|
||||
var environment = atmosphereSystem.GetTileMixture(tile.GridUid, tile.GridIndices, true);
|
||||
var environment = atmosphereSystem.GetTileMixture(tile.GridUid, null, tile.GridIndices, true);
|
||||
if (environment == null || !atmosphereSystem.IsHotspotActive(tile.GridUid, tile.GridIndices))
|
||||
return FixedPoint2.Zero;
|
||||
|
||||
environment.Temperature *= MathF.Max(_temperatureMultiplier * reactVolume.Float(), 1f);
|
||||
atmosphereSystem.React(tile.GridUid, tile.GridIndices);
|
||||
atmosphereSystem.ReactTile(tile.GridUid, tile.GridIndices);
|
||||
|
||||
return reactVolume;
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
|
||||
_disposalUnitSystem.TryEjectContents(duc);
|
||||
}
|
||||
|
||||
if (_atmosphereSystem.GetTileMixture(holderTransform.Coordinates, true) is {} environment)
|
||||
if (_atmosphereSystem.GetContainingMixture(uid, false, true) is {} environment)
|
||||
{
|
||||
_atmosphereSystem.Merge(environment, holder.Air);
|
||||
holder.Air.Clear();
|
||||
|
||||
@@ -42,6 +42,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
|
||||
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
|
||||
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
|
||||
[Dependency] private readonly DumpableSystem _dumpableSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
|
||||
private readonly List<DisposalUnitComponent> _activeDisposals = new();
|
||||
|
||||
@@ -521,8 +522,9 @@ namespace Content.Server.Disposal.Unit.EntitySystems
|
||||
|
||||
var air = component.Air;
|
||||
var entryComponent = EntityManager.GetComponent<DisposalEntryComponent>(entry);
|
||||
var indices = _transformSystem.GetGridOrMapTilePosition(component.Owner, xform);
|
||||
|
||||
if (_atmosSystem.GetTileMixture(xform.Coordinates, true) is {Temperature: > 0} environment)
|
||||
if (_atmosSystem.GetTileMixture(xform.GridUid, xform.MapUid, indices, true) is {Temperature: > 0} environment)
|
||||
{
|
||||
var transferMoles = 0.1f * (0.25f * Atmospherics.OneAtmosphere * 1.01f - air.Pressure) * air.Volume / (environment.Temperature * Atmospherics.R);
|
||||
|
||||
|
||||
@@ -2,6 +2,10 @@ using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Doors.Systems;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
|
||||
namespace Content.Server.Doors.Components
|
||||
{
|
||||
@@ -42,12 +46,20 @@ namespace Content.Server.Doors.Components
|
||||
|
||||
public bool IsHoldingPressure(float threshold = 20)
|
||||
{
|
||||
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
var transform = _entMan.GetComponent<TransformComponent>(Owner);
|
||||
|
||||
if (transform.GridUid is not {} gridUid)
|
||||
return false;
|
||||
|
||||
var atmosphereSystem = _entMan.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
|
||||
var transformSystem = _entMan.EntitySysManager.GetEntitySystem<TransformSystem>();
|
||||
|
||||
var position = transformSystem.GetGridOrMapTilePosition(Owner, transform);
|
||||
|
||||
var minMoles = float.MaxValue;
|
||||
var maxMoles = 0f;
|
||||
|
||||
foreach (var adjacent in atmosphereSystem.GetAdjacentTileMixtures(_entMan.GetComponent<TransformComponent>(Owner).Coordinates))
|
||||
foreach (var adjacent in atmosphereSystem.GetAdjacentTileMixtures(gridUid, position))
|
||||
{
|
||||
var moles = adjacent.TotalMoles;
|
||||
if (moles < minMoles)
|
||||
@@ -61,20 +73,25 @@ namespace Content.Server.Doors.Components
|
||||
|
||||
public bool IsHoldingFire()
|
||||
{
|
||||
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
var atmosphereSystem = _entMan.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
|
||||
var transformSystem = _entMan.EntitySysManager.GetEntitySystem<TransformSystem>();
|
||||
|
||||
if (!atmosphereSystem.TryGetGridAndTile(_entMan.GetComponent<TransformComponent>(Owner).Coordinates, out var tuple))
|
||||
var transform = _entMan.GetComponent<TransformComponent>(Owner);
|
||||
var position = transformSystem.GetGridOrMapTilePosition(Owner, transform);
|
||||
|
||||
// No grid, no fun.
|
||||
if (transform.GridUid is not {} gridUid)
|
||||
return false;
|
||||
|
||||
if (atmosphereSystem.GetTileMixture(tuple.Value.Grid, tuple.Value.Tile) == null)
|
||||
if (atmosphereSystem.GetTileMixture(gridUid, null, position) == null)
|
||||
return false;
|
||||
|
||||
if (atmosphereSystem.IsHotspotActive(tuple.Value.Grid, tuple.Value.Tile))
|
||||
if (atmosphereSystem.IsHotspotActive(gridUid, position))
|
||||
return true;
|
||||
|
||||
foreach (var adjacent in atmosphereSystem.GetAdjacentTiles(_entMan.GetComponent<TransformComponent>(Owner).Coordinates))
|
||||
foreach (var adjacent in atmosphereSystem.GetAdjacentTiles(gridUid, position))
|
||||
{
|
||||
if (atmosphereSystem.IsHotspotActive(tuple.Value.Grid, adjacent))
|
||||
if (atmosphereSystem.IsHotspotActive(gridUid, adjacent))
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@ using Content.Shared.MobState.Components;
|
||||
using Content.Shared.PDA;
|
||||
using Content.Shared.Roles;
|
||||
using Content.Shared.Traitor.Uplink;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Server.Player;
|
||||
using Robust.Shared.Configuration;
|
||||
using Robust.Shared.Map;
|
||||
@@ -36,6 +37,8 @@ public sealed class TraitorDeathMatchRuleSystem : GameRuleSystem
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly MaxTimeRestartRuleSystem _restarter = default!;
|
||||
[Dependency] private readonly InventorySystem _inventory = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
|
||||
public override string Prototype => "TraitorDeathMatch";
|
||||
|
||||
@@ -243,10 +246,17 @@ public sealed class TraitorDeathMatchRuleSystem : GameRuleSystem
|
||||
_robustRandom.Shuffle(ents);
|
||||
var foundATarget = false;
|
||||
bestTarget = EntityCoordinates.Invalid;
|
||||
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
|
||||
|
||||
foreach (var entity in ents)
|
||||
{
|
||||
if (!atmosphereSystem.IsTileMixtureProbablySafe(Transform(entity).Coordinates))
|
||||
var transform = Transform(entity);
|
||||
|
||||
if (transform.GridUid == null || transform.MapUid == null)
|
||||
continue;
|
||||
|
||||
var position = _transformSystem.GetGridOrMapTilePosition(entity, transform);
|
||||
|
||||
if (!_atmosphereSystem.IsTileMixtureProbablySafe(transform.GridUid.Value, transform.MapUid.Value, position))
|
||||
continue;
|
||||
|
||||
var distanceFromNearest = float.PositiveInfinity;
|
||||
|
||||
@@ -14,8 +14,8 @@ namespace Content.Server.Light.EntitySystems
|
||||
public sealed class MatchstickSystem : EntitySystem
|
||||
{
|
||||
private HashSet<MatchstickComponent> _litMatches = new();
|
||||
[Dependency]
|
||||
private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -38,7 +38,14 @@ namespace Content.Server.Light.EntitySystems
|
||||
if (match.CurrentState != SmokableState.Lit || Paused(match.Owner) || match.Deleted)
|
||||
continue;
|
||||
|
||||
_atmosphereSystem.HotspotExpose(EntityManager.GetComponent<TransformComponent>(match.Owner).Coordinates, 400, 50, true);
|
||||
var xform = Transform(match.Owner);
|
||||
|
||||
if (xform.GridUid is not {} gridUid)
|
||||
return;
|
||||
|
||||
var position = _transformSystem.GetGridOrMapTilePosition(match.Owner, xform);
|
||||
|
||||
_atmosphereSystem.HotspotExpose(gridUid, position, 400, 50, true);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -33,10 +33,13 @@ namespace Content.Server.NodeContainer.NodeGroups
|
||||
Grid = entMan.GetComponent<TransformComponent>(sourceNode.Owner).GridUid;
|
||||
|
||||
if (Grid == null)
|
||||
{
|
||||
Logger.Error($"Created a pipe network without an associated grid. Pipe networks currently need to be tied to a grid for amtos to work. Source entity: {entMan.ToPrettyString(sourceNode.Owner)}");
|
||||
return;
|
||||
}
|
||||
|
||||
_atmosphereSystem = entMan.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
|
||||
_atmosphereSystem.AddPipeNet(this);
|
||||
_atmosphereSystem.AddPipeNet(Grid.Value, this);
|
||||
}
|
||||
|
||||
public void Update()
|
||||
@@ -85,7 +88,11 @@ namespace Content.Server.NodeContainer.NodeGroups
|
||||
private void RemoveFromGridAtmos()
|
||||
{
|
||||
DebugTools.AssertNotNull(_atmosphereSystem);
|
||||
_atmosphereSystem?.RemovePipeNet(this);
|
||||
|
||||
if (Grid == null)
|
||||
return;
|
||||
|
||||
_atmosphereSystem?.RemovePipeNet(Grid.Value, this);
|
||||
}
|
||||
|
||||
public override string GetDebugData()
|
||||
|
||||
@@ -11,6 +11,7 @@ using Content.Shared.FixedPoint;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Smoking;
|
||||
using Content.Shared.Temperature;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
|
||||
namespace Content.Server.Nutrition.EntitySystems
|
||||
@@ -21,8 +22,8 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmos = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly InventorySystem _inventorySystem = default!;
|
||||
|
||||
private const float UpdateTimer = 3f;
|
||||
|
||||
private float _timer = 0f;
|
||||
@@ -79,6 +80,7 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
if (_timer < UpdateTimer)
|
||||
return;
|
||||
|
||||
// TODO Use an "active smoke" component instead, EntityQuery over that.
|
||||
foreach (var uid in _active.ToArray())
|
||||
{
|
||||
if (!TryComp(uid, out SmokableComponent? smokable))
|
||||
@@ -96,7 +98,12 @@ namespace Content.Server.Nutrition.EntitySystems
|
||||
if (smokable.ExposeTemperature > 0 && smokable.ExposeVolume > 0)
|
||||
{
|
||||
var transform = Transform(uid);
|
||||
_atmos.HotspotExpose(transform.Coordinates, smokable.ExposeTemperature, smokable.ExposeVolume, true);
|
||||
|
||||
if (transform.GridUid is {} gridUid)
|
||||
{
|
||||
var position = _transformSystem.GetGridOrMapTilePosition(uid, transform);
|
||||
_atmos.HotspotExpose(gridUid, position, smokable.ExposeTemperature, smokable.ExposeVolume, true);
|
||||
}
|
||||
}
|
||||
|
||||
var inhaledSolution = _solutionContainerSystem.SplitSolution(uid, solution, smokable.InhaleAmount * _timer);
|
||||
|
||||
@@ -252,7 +252,7 @@ namespace Content.Server.PneumaticCannon
|
||||
{
|
||||
// we checked for this earlier in HasGas so a GetComp is okay
|
||||
var gas = EntityManager.GetComponent<GasTankComponent>(contained);
|
||||
var environment = _atmos.GetTileMixture(EntityManager.GetComponent<TransformComponent>(comp.Owner).Coordinates, true);
|
||||
var environment = _atmos.GetContainingMixture(comp.Owner, false, true);
|
||||
var removed = gas.RemoveAir(GetMoleUsageFromPower(comp.Power));
|
||||
if (environment != null && removed != null)
|
||||
{
|
||||
|
||||
@@ -6,6 +6,7 @@ using Content.Server.Nutrition.Components;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Actions;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.RatKing
|
||||
@@ -17,6 +18,7 @@ namespace Content.Server.RatKing
|
||||
[Dependency] private readonly DiseaseSystem _disease = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmos = default!;
|
||||
[Dependency] private readonly TransformSystem _xform = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -79,9 +81,10 @@ namespace Content.Server.RatKing
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("rat-king-domain-popup"), uid, Filter.Pvs(uid));
|
||||
|
||||
var tileMix = _atmos.GetTileMixture(Transform(uid).Coordinates);
|
||||
if (tileMix != null)
|
||||
tileMix.AdjustMoles(Gas.Miasma, component.MolesMiasmaPerDomain);
|
||||
var transform = Transform(uid);
|
||||
var indices = _xform.GetGridOrMapTilePosition(uid, transform);
|
||||
var tileMix = _atmos.GetTileMixture(transform.GridUid, transform.MapUid, indices, true);
|
||||
tileMix?.AdjustMoles(Gas.Miasma, component.MolesMiasmaPerDomain);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ namespace Content.Server.StationEvents.Events
|
||||
return;
|
||||
}
|
||||
|
||||
var environment = atmosphereSystem.GetTileMixture(_targetGrid, _targetTile, true);
|
||||
var environment = atmosphereSystem.GetTileMixture(_targetGrid, null, _targetTile, true);
|
||||
|
||||
environment?.AdjustMoles(_leakGas, LeakCooldown * _molesPerSecond);
|
||||
}
|
||||
|
||||
@@ -227,7 +227,8 @@ namespace Content.Server.StationEvents.Events
|
||||
|
||||
targetGrid = robustRandom.Pick(possibleTargets);
|
||||
|
||||
if (!entityManager.TryGetComponent<IMapGridComponent>(targetGrid, out var gridComp))
|
||||
if (!entityManager.TryGetComponent<IMapGridComponent>(targetGrid, out var gridComp)
|
||||
|| !entityManager.TryGetComponent<TransformComponent>(targetGrid, out var transform))
|
||||
return false;
|
||||
var grid = gridComp.Grid;
|
||||
|
||||
@@ -242,7 +243,9 @@ namespace Content.Server.StationEvents.Events
|
||||
var randomY = robustRandom.Next((int) gridBounds.Bottom, (int) gridBounds.Top);
|
||||
|
||||
tile = new Vector2i(randomX - (int) gridPos.X, randomY - (int) gridPos.Y);
|
||||
if (atmosphereSystem.IsTileSpace(grid, tile) || atmosphereSystem.IsTileAirBlocked(grid, tile)) continue;
|
||||
if (atmosphereSystem.IsTileSpace(grid.GridEntityId, transform.MapUid, tile, mapGridComp:gridComp)
|
||||
|| atmosphereSystem.IsTileAirBlocked(grid.GridEntityId, tile, mapGridComp:gridComp))
|
||||
continue;
|
||||
found = true;
|
||||
targetCoords = grid.GridTileToLocal(tile);
|
||||
break;
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.Administration.Logs;
|
||||
using Content.Server.Atmos.Components;
|
||||
@@ -7,11 +9,15 @@ using Content.Shared.Alert;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Database;
|
||||
using Content.Shared.Inventory;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
|
||||
namespace Content.Server.Temperature.Systems
|
||||
{
|
||||
public sealed class TemperatureSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageableSystem = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
|
||||
@@ -94,8 +100,15 @@ namespace Content.Server.Temperature.Systems
|
||||
|
||||
private void OnAtmosExposedUpdate(EntityUid uid, TemperatureComponent temperature, ref AtmosExposedUpdateEvent args)
|
||||
{
|
||||
var transform = args.Transform;
|
||||
|
||||
if (transform.MapUid == null)
|
||||
return;
|
||||
|
||||
var position = _transformSystem.GetGridOrMapTilePosition(uid, transform);
|
||||
|
||||
var temperatureDelta = args.GasMixture.Temperature - temperature.CurrentTemperature;
|
||||
var tileHeatCapacity = _atmosphereSystem.GetTileHeatCapacity(args.Coordinates);
|
||||
var tileHeatCapacity = _atmosphereSystem.GetTileHeatCapacity(transform.GridUid, transform.MapUid.Value, position);
|
||||
var heat = temperatureDelta * (tileHeatCapacity * temperature.HeatCapacity / (tileHeatCapacity + temperature.HeatCapacity));
|
||||
ChangeHeat(uid, heat * temperature.AtmosTemperatureTransferEfficiency, temperature: temperature );
|
||||
}
|
||||
|
||||
@@ -78,9 +78,10 @@ namespace Content.Server.Tools
|
||||
SolutionContainerManagerComponent? solutionContainer = null,
|
||||
SharedItemComponent? item = null,
|
||||
PointLightComponent? light = null,
|
||||
AppearanceComponent? appearance = null)
|
||||
AppearanceComponent? appearance = null,
|
||||
TransformComponent? transform = null)
|
||||
{
|
||||
if (!Resolve(uid, ref welder, ref solutionContainer))
|
||||
if (!Resolve(uid, ref welder, ref solutionContainer, ref transform))
|
||||
return false;
|
||||
|
||||
// Optional components.
|
||||
@@ -113,8 +114,11 @@ namespace Content.Server.Tools
|
||||
|
||||
SoundSystem.Play(welder.WelderOnSounds.GetSound(), Filter.Pvs(uid), uid, AudioHelpers.WithVariation(0.125f).WithVolume(-5f));
|
||||
|
||||
// TODO: Use TransformComponent directly.
|
||||
_atmosphereSystem.HotspotExpose(EntityManager.GetComponent<TransformComponent>(welder.Owner).Coordinates, 700, 50, true);
|
||||
if (transform.GridUid is {} gridUid)
|
||||
{
|
||||
var position = _transformSystem.GetGridOrMapTilePosition(uid, transform);
|
||||
_atmosphereSystem.HotspotExpose(gridUid, position, 700, 50, true);
|
||||
}
|
||||
|
||||
welder.Dirty();
|
||||
|
||||
@@ -300,17 +304,22 @@ namespace Content.Server.Tools
|
||||
if (_welderTimer < WelderUpdateTimer)
|
||||
return;
|
||||
|
||||
// TODO Use an "active welder" component instead, EntityQuery over that.
|
||||
foreach (var tool in _activeWelders.ToArray())
|
||||
{
|
||||
if (!EntityManager.TryGetComponent(tool, out WelderComponent? welder)
|
||||
|| !EntityManager.TryGetComponent(tool, out SolutionContainerManagerComponent? solutionContainer))
|
||||
|| !EntityManager.TryGetComponent(tool, out SolutionContainerManagerComponent? solutionContainer)
|
||||
|| !EntityManager.TryGetComponent(tool, out TransformComponent? transform))
|
||||
continue;
|
||||
|
||||
if (!_solutionContainerSystem.TryGetSolution(tool, welder.FuelSolution, out var solution, solutionContainer))
|
||||
continue;
|
||||
|
||||
// TODO: Use TransformComponent directly.
|
||||
_atmosphereSystem.HotspotExpose(EntityManager.GetComponent<TransformComponent>(welder.Owner).Coordinates, 700, 50, true);
|
||||
if (transform.GridUid is { } gridUid)
|
||||
{
|
||||
var position = _transformSystem.GetGridOrMapTilePosition(tool, transform);
|
||||
_atmosphereSystem.HotspotExpose(gridUid, position, 700, 50, true);
|
||||
}
|
||||
|
||||
solution.RemoveReagent(welder.FuelReagent, welder.FuelConsumption * _welderTimer);
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ using Content.Server.DoAfter;
|
||||
using Content.Server.Popups;
|
||||
using Content.Shared.Audio;
|
||||
using Content.Shared.Tools.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Player;
|
||||
@@ -23,6 +24,7 @@ namespace Content.Server.Tools
|
||||
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
|
||||
@@ -40,7 +40,7 @@ public sealed class GasArtifactSystem : EntitySystem
|
||||
|
||||
var transform = Transform(uid);
|
||||
|
||||
var environment = _atmosphereSystem.GetTileMixture(transform.Coordinates, true);
|
||||
var environment = _atmosphereSystem.GetContainingMixture(uid, false, true);
|
||||
if (environment == null)
|
||||
return;
|
||||
|
||||
|
||||
@@ -2,12 +2,14 @@
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
|
||||
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
|
||||
|
||||
public sealed class TemperatureArtifactSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -19,14 +21,16 @@ public sealed class TemperatureArtifactSystem : EntitySystem
|
||||
{
|
||||
var transform = Transform(uid);
|
||||
|
||||
var center = _atmosphereSystem.GetTileMixture(transform.Coordinates, true);
|
||||
var center = _atmosphereSystem.GetContainingMixture(uid, false, true);
|
||||
if (center == null)
|
||||
return;
|
||||
UpdateTileTemperature(component, center);
|
||||
|
||||
if (component.EffectAdjacentTiles)
|
||||
if (component.EffectAdjacentTiles && transform.GridUid != null)
|
||||
{
|
||||
var adjacent = _atmosphereSystem.GetAdjacentTileMixtures(transform.Coordinates, invalidate: true);
|
||||
var adjacent = _atmosphereSystem.GetAdjacentTileMixtures(transform.GridUid.Value,
|
||||
_transformSystem.GetGridOrMapTilePosition(uid, transform), excite: true);
|
||||
|
||||
foreach (var mixture in adjacent)
|
||||
{
|
||||
UpdateTileTemperature(component, mixture);
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
|
||||
using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems;
|
||||
@@ -10,6 +11,7 @@ public sealed class ArtifactGasTriggerSystem : EntitySystem
|
||||
[Dependency] private readonly IRobustRandom _random = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly ArtifactSystem _artifactSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -32,10 +34,14 @@ public sealed class ArtifactGasTriggerSystem : EntitySystem
|
||||
var query = EntityManager.EntityQuery<ArtifactGasTriggerComponent, TransformComponent>();
|
||||
foreach (var (trigger, transform) in query)
|
||||
{
|
||||
var uid = trigger.Owner;
|
||||
|
||||
if (trigger.ActivationGas == null)
|
||||
continue;
|
||||
|
||||
var environment = _atmosphereSystem.GetTileMixture(transform.Coordinates);
|
||||
var environment = _atmosphereSystem.GetTileMixture(transform.GridUid, transform.MapUid,
|
||||
_transformSystem.GetGridOrMapTilePosition(uid, transform));
|
||||
|
||||
if (environment == null)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Temperature;
|
||||
using Content.Shared.Weapons.Melee;
|
||||
using Robust.Server.GameObjects;
|
||||
|
||||
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems;
|
||||
|
||||
@@ -10,6 +11,7 @@ public sealed class ArtifactHeatTriggerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||
[Dependency] private readonly ArtifactSystem _artifactSystem = default!;
|
||||
[Dependency] private readonly TransformSystem _transformSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
@@ -25,7 +27,9 @@ public sealed class ArtifactHeatTriggerSystem : EntitySystem
|
||||
var query = EntityManager.EntityQuery<ArtifactHeatTriggerComponent, TransformComponent, ArtifactComponent>();
|
||||
foreach (var (trigger, transform, artifact) in query)
|
||||
{
|
||||
var environment = _atmosphereSystem.GetTileMixture(transform.Coordinates);
|
||||
var uid = trigger.Owner;
|
||||
var environment = _atmosphereSystem.GetTileMixture(transform.GridUid, transform.MapUid,
|
||||
_transformSystem.GetGridOrMapTilePosition(uid, transform));
|
||||
if (environment == null)
|
||||
continue;
|
||||
|
||||
|
||||
@@ -16,10 +16,11 @@ namespace Content.Shared.Atmos.EntitySystems
|
||||
public readonly float[] Moles;
|
||||
public readonly AtmosDirection PressureDirection;
|
||||
public readonly AtmosDirection LastPressureDirection;
|
||||
public readonly bool InExcitedGroup;
|
||||
public readonly int InExcitedGroup;
|
||||
public readonly AtmosDirection BlockDirection;
|
||||
public readonly bool IsSpace;
|
||||
|
||||
public AtmosDebugOverlayData(float temperature, float[] moles, AtmosDirection pressureDirection, AtmosDirection lastPressureDirection, bool inExcited, AtmosDirection blockDirection)
|
||||
public AtmosDebugOverlayData(float temperature, float[] moles, AtmosDirection pressureDirection, AtmosDirection lastPressureDirection, int inExcited, AtmosDirection blockDirection, bool isSpace)
|
||||
{
|
||||
Temperature = temperature;
|
||||
Moles = moles;
|
||||
@@ -27,6 +28,7 @@ namespace Content.Shared.Atmos.EntitySystems
|
||||
LastPressureDirection = lastPressureDirection;
|
||||
InExcitedGroup = inExcited;
|
||||
BlockDirection = blockDirection;
|
||||
IsSpace = isSpace;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -524,6 +524,7 @@ public sealed class $CLASS$ : Shared$CLASS$ {
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Protolathe/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pullable/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Reparenting/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=roundstart/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=Ruinable/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=saltern/@EntryIndexedValue">True</s:Boolean>
|
||||
<s:Boolean x:Key="/Default/UserDictionary/Words/=sandboxing/@EntryIndexedValue">True</s:Boolean>
|
||||
|
||||
Reference in New Issue
Block a user