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:
Vera Aguilera Puerto
2022-07-04 16:51:34 +02:00
committed by GitHub
parent 43216a000f
commit aa9281d667
74 changed files with 1764 additions and 2141 deletions

View File

@@ -141,16 +141,27 @@ namespace Content.Client.Atmos.Overlays
DrawPressureDirection(drawHandle, data.LastPressureDirection, tile, Color.LightGray); DrawPressureDirection(drawHandle, data.LastPressureDirection, tile, Color.LightGray);
} }
var tilePos = new Vector2(tile.X, tile.Y);
// -- Excited Groups -- // -- Excited Groups --
if (data.InExcitedGroup) if (data.InExcitedGroup != 0)
{ {
var tilePos = new Vector2(tile.X, tile.Y);
var basisA = tilePos; var basisA = tilePos;
var basisB = tilePos + new Vector2(1.0f, 1.0f); var basisB = tilePos + new Vector2(1.0f, 1.0f);
var basisC = tilePos + new Vector2(0.0f, 1.0f); var basisC = tilePos + new Vector2(0.0f, 1.0f);
var basisD = tilePos + new Vector2(1.0f, 0.0f); var basisD = tilePos + new Vector2(1.0f, 0.0f);
drawHandle.DrawLine(basisA, basisB, Color.Cyan); var color = Color.White // Use first three nibbles for an unique color... Good enough?
drawHandle.DrawLine(basisC, basisD, Color.Cyan); .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);
} }
} }
} }

View File

@@ -1,5 +1,6 @@
using Content.Server.Administration; using Content.Server.Administration;
using Content.Server.Atmos.Components; using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Shared.Console; using Robust.Shared.Console;
@@ -36,7 +37,9 @@ namespace Content.Server.Atmos.Commands
return; return;
} }
if (_entities.HasComponent<IAtmosphereComponent>(euid)) var atmos = entMan.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
if (atmos.HasAtmosphere(euid))
{ {
shell.WriteLine("Grid already has an atmosphere."); shell.WriteLine("Grid already has an atmosphere.");
return; return;

View File

@@ -31,9 +31,9 @@ namespace Content.Server.Atmos.Commands
return; return;
} }
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>(); var atmosphereSystem = entMan.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
var indices = new Vector2i(x, y); var indices = new Vector2i(x, y);
var tile = atmosphereSystem.GetTileMixture(euid, indices, true); var tile = atmosphereSystem.GetTileMixture(euid, null, indices, true);
if (tile == null) if (tile == null)
{ {

View File

@@ -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}.");
}
}
}

View File

@@ -134,7 +134,7 @@ namespace Content.Server.Atmos.Commands
if (gas == null) 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; if (tile.Immutable) continue;
@@ -146,7 +146,7 @@ namespace Content.Server.Atmos.Commands
} }
else else
{ {
foreach (var tile in atmosphereSystem.GetAllTileMixtures(gridId.Value, true)) foreach (var tile in atmosphereSystem.GetAllMixtures(gridId.Value, true))
{ {
if (tile.Immutable) continue; if (tile.Immutable) continue;

View File

@@ -23,7 +23,7 @@ namespace Content.Server.Atmos.Commands
var mapMan = IoCManager.Resolve<IMapManager>(); var mapMan = IoCManager.Resolve<IMapManager>();
if (!gridId.IsValid() || !mapMan.TryGetGrid(gridId, out _)) if (!mapMan.TryGetGrid(gridId, out var grid))
{ {
shell.WriteLine("Invalid grid ID."); shell.WriteLine("Invalid grid ID.");
return; return;
@@ -31,7 +31,7 @@ namespace Content.Server.Atmos.Commands
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>(); 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); tile.AdjustMoles(gasId, moles);
} }

View File

@@ -24,7 +24,7 @@ namespace Content.Server.Atmos.Commands
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>(); var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
var indices = new Vector2i(x, y); var indices = new Vector2i(x, y);
var tile = atmosphereSystem.GetTileMixture(id, indices, true); var tile = atmosphereSystem.GetTileMixture(id, null, indices, true);
if (tile == null) if (tile == null)
{ {

View File

@@ -37,7 +37,7 @@ namespace Content.Server.Atmos.Commands
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>(); var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
var tiles = 0; var tiles = 0;
foreach (var tile in atmosphereSystem.GetAllTileMixtures(gridId, true)) foreach (var tile in atmosphereSystem.GetAllMixtures(gridComp.GridEntityId, true))
{ {
tiles++; tiles++;
tile.Temperature = temperature; tile.Temperature = temperature;

View File

@@ -2,14 +2,21 @@ using Content.Server.Administration;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Robust.Server.GameObjects;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.GameObjects;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Maths;
using SharpZstd.Interop;
namespace Content.Server.Atmos.Commands namespace Content.Server.Atmos.Commands
{ {
[AdminCommand(AdminFlags.Debug)] [AdminCommand(AdminFlags.Debug)]
public sealed class SetTemperatureCommand : IConsoleCommand public sealed class SetTemperatureCommand : IConsoleCommand
{ {
[Dependency] private readonly IEntityManager _entities = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
public string Command => "settemp"; public string Command => "settemp";
public string Description => "Sets a tile's temperature (in kelvin)."; public string Description => "Sets a tile's temperature (in kelvin).";
public string Help => "Usage: settemp <X> <Y> <GridId> <Temperature>"; public string Help => "Usage: settemp <X> <Y> <GridId> <Temperature>";
@@ -28,9 +35,16 @@ namespace Content.Server.Atmos.Commands
return; 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 indices = new Vector2i(x, y);
var tile = atmosphereSystem.GetTileMixture(gridId, indices, true);
var tile = atmospheres.GetTileMixture(grid.GridEntityId, null, indices, true);
if (tile == null) if (tile == null)
{ {

View File

@@ -5,6 +5,7 @@ using Content.Server.UserInterface;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Components; using Content.Shared.Atmos.Components;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Maps;
using Content.Shared.Popups; using Content.Shared.Popups;
using Robust.Server.GameObjects; using Robust.Server.GameObjects;
using Robust.Server.Player; 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 // Already get the pressure before Dirty(), because we can't get the EntitySystem in that thread or smth
var pressure = 0f; 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) if (tile != null)
{ {
pressure = tile.Pressure; pressure = tile.Pressure;
@@ -178,8 +179,12 @@ namespace Content.Server.Atmos.Components
pos = _position.Value; 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 atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
var tile = atmosphereSystem.GetTileMixture(pos); var tile = atmosphereSystem.GetTileMixture(gridUid, mapUid, position);
if (tile == null) if (tile == null)
{ {
error = "No Atmosphere!"; error = "No Atmosphere!";

View File

@@ -261,7 +261,7 @@ namespace Content.Server.Atmos.Components
{ {
if (_integrity <= 0) if (_integrity <= 0)
{ {
var environment = atmosphereSystem.GetTileMixture(_entMan.GetComponent<TransformComponent>(Owner).Coordinates, true); var environment = atmosphereSystem.GetContainingMixture(Owner, false, true);
if(environment != null) if(environment != null)
atmosphereSystem.Merge(environment, Air); atmosphereSystem.Merge(environment, Air);
@@ -279,7 +279,7 @@ namespace Content.Server.Atmos.Components
{ {
if (_integrity <= 0) if (_integrity <= 0)
{ {
var environment = atmosphereSystem.GetTileMixture(_entMan.GetComponent<TransformComponent>(Owner).Coordinates, true); var environment = atmosphereSystem.GetContainingMixture(Owner, false, true);
if (environment == null) if (environment == null)
return; return;

View File

@@ -8,12 +8,12 @@ namespace Content.Server.Atmos.Components
/// <summary> /// <summary>
/// Internal Atmos class. Use <see cref="AtmosphereSystem"/> to interact with atmos instead. /// Internal Atmos class. Use <see cref="AtmosphereSystem"/> to interact with atmos instead.
/// </summary> /// </summary>
[ComponentReference(typeof(IAtmosphereComponent))] [RegisterComponent, Serializable,
[RegisterComponent, Serializable] Access(typeof(AtmosphereSystem), typeof(GasTileOverlaySystem), typeof(AtmosDebugOverlaySystem))]
[Virtual] public sealed class GridAtmosphereComponent : Component, ISerializationHooks
public class GridAtmosphereComponent : Component, IAtmosphereComponent, ISerializationHooks
{ {
public virtual bool Simulated => true; [ViewVariables(VVAccess.ReadWrite)]
public bool Simulated { get; set; } = true;
[ViewVariables] [ViewVariables]
public bool ProcessingPaused { get; set; } = false; public bool ProcessingPaused { get; set; } = false;
@@ -22,7 +22,7 @@ namespace Content.Server.Atmos.Components
public float Timer { get; set; } = 0f; public float Timer { get; set; } = 0f;
[ViewVariables] [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")] [DataField("uniqueMixes")]
public List<GasMixture>? UniqueMixes; public List<GasMixture>? UniqueMixes;
@@ -94,7 +94,7 @@ namespace Content.Server.Atmos.Components
public long EqualizationQueueCycleControl { get; set; } public long EqualizationQueueCycleControl { get; set; }
[ViewVariables] [ViewVariables]
public AtmosphereProcessingState State { get; set; } = AtmosphereProcessingState.TileEqualize; public AtmosphereProcessingState State { get; set; } = AtmosphereProcessingState.Revalidate;
void ISerializationHooks.BeforeSerialization() void ISerializationHooks.BeforeSerialization()
{ {

View File

@@ -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; }
}
}

View 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;
}

View File

@@ -1,9 +0,0 @@
namespace Content.Server.Atmos.Components
{
[RegisterComponent]
[ComponentReference(typeof(IAtmosphereComponent))]
public sealed class SpaceAtmosphereComponent : Component, IAtmosphereComponent
{
public bool Simulated => false;
}
}

View File

@@ -1,10 +0,0 @@
namespace Content.Server.Atmos.Components
{
[RegisterComponent]
[ComponentReference(typeof(IAtmosphereComponent))]
[Serializable]
public sealed class UnsimulatedGridAtmosphereComponent : GridAtmosphereComponent
{
public override bool Simulated => false;
}
}

View File

@@ -111,17 +111,19 @@ namespace Content.Server.Atmos.EntitySystems
public void InvalidatePosition(EntityUid gridId, Vector2i pos, bool fixVacuum = false) public void InvalidatePosition(EntityUid gridId, Vector2i pos, bool fixVacuum = false)
{ {
if (!gridId.IsValid()) if (!_mapManager.TryGetGrid(gridId, out var grid))
return; return;
var gridUid = grid.GridEntityId;
var query = EntityManager.GetEntityQuery<AirtightComponent>(); var query = EntityManager.GetEntityQuery<AirtightComponent>();
_explosionSystem.UpdateAirtightMap(gridId, pos, query); _explosionSystem.UpdateAirtightMap(gridId, pos, query);
// TODO make atmos system use query // TODO make atmos system use query
_atmosphereSystem.UpdateAdjacent(gridId, pos); _atmosphereSystem.UpdateAdjacent(gridUid, pos);
_atmosphereSystem.InvalidateTile(gridId, pos); _atmosphereSystem.InvalidateTile(gridUid, pos);
if(fixVacuum) if(fixVacuum)
_atmosphereSystem.FixVacuum(gridId, pos); _atmosphereSystem.FixTileVacuum(gridUid, pos);
} }
private AtmosDirection Rotate(AtmosDirection myDirection, Angle myAngle) private AtmosDirection Rotate(AtmosDirection myDirection, Angle myAngle)

View File

@@ -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) 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 else
{ {
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) 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);
gases[i] = tile.Air.GetMoles(i);
}
return new AtmosDebugOverlayData(tile.Air.Temperature, gases, tile.PressureDirection, tile.LastPressureDirection, tile.ExcitedGroup != null, tile.BlockedAirflow);
} }
} }
@@ -132,16 +129,22 @@ namespace Content.Server.Atmos.EntitySystems
continue; continue;
var transform = EntityManager.GetComponent<TransformComponent>(entity); var transform = EntityManager.GetComponent<TransformComponent>(entity);
var mapUid = transform.MapUid;
var mapIsSpace = _atmosphereSystem.IsTileSpace(null, mapUid, Vector2i.Zero);
var worldBounds = Box2.CenteredAround(transform.WorldPosition, var worldBounds = Box2.CenteredAround(transform.WorldPosition,
new Vector2(LocalViewRange, LocalViewRange)); new Vector2(LocalViewRange, LocalViewRange));
foreach (var grid in _mapManager.FindGridsIntersecting(transform.MapID, worldBounds)) foreach (var grid in _mapManager.FindGridsIntersecting(transform.MapID, worldBounds))
{ {
if (!EntityManager.EntityExists(grid.GridEntityId)) var uid = grid.GridEntityId;
if (!Exists(uid))
continue; 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 entityTile = grid.GetTileRef(transform.Coordinates).GridIndices;
var baseTile = new Vector2i(entityTile.X - (LocalViewRange / 2), entityTile.Y - (LocalViewRange / 2)); 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++) for (var x = 0; x < LocalViewRange; x++)
{ {
var vector = new Vector2i(baseTile.X + x, baseTile.Y + y); 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);
} }
} }

View File

@@ -12,17 +12,47 @@ namespace Content.Server.Atmos.EntitySystems
{ {
public readonly EntityCoordinates Coordinates; public readonly EntityCoordinates Coordinates;
public readonly GasMixture GasMixture; public readonly GasMixture GasMixture;
public readonly TransformComponent Transform;
public AtmosExposedUpdateEvent(EntityCoordinates coordinates, GasMixture mixture) public AtmosExposedUpdateEvent(EntityCoordinates coordinates, GasMixture mixture, TransformComponent transform)
{ {
Coordinates = coordinates; Coordinates = coordinates;
GasMixture = mixture; GasMixture = mixture;
Transform = transform;
} }
} }
/// <summary>
/// Event that tries to query the mixture a certain entity is exposed to.
/// </summary>
[ByRefEvent] [ByRefEvent]
public struct AtmosExposedGetAirEvent 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;
}
} }
} }

View 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);
}

View File

@@ -3,6 +3,7 @@ using Content.Server.Administration;
using Content.Server.Atmos.Components; using Content.Server.Atmos.Components;
using Content.Shared.Administration; using Content.Shared.Administration;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Maps;
using Robust.Shared.Console; using Robust.Shared.Console;
using Robust.Shared.Map; using Robust.Shared.Map;
@@ -83,12 +84,20 @@ public sealed partial class AtmosphereSystem
continue; continue;
} }
var transform = Transform(euid);
foreach (var (indices, tileMain) in gridAtmosphere.Tiles) foreach (var (indices, tileMain) in gridAtmosphere.Tiles)
{ {
var tile = tileMain.Air; var tile = tileMain.Air;
if (tile == null) if (tile == null)
continue; continue;
if (tile.Immutable && !IsTileSpace(euid, transform.MapUid, indices, gridComp))
{
tile = new GasMixture(tile.Volume) { Temperature = tile.Temperature };
tileMain.Air = tile;
}
tile.Clear(); tile.Clear();
var mixtureId = 0; var mixtureId = 0;
foreach (var entUid in gridComp.Grid.GetAnchoredEntities(indices)) foreach (var entUid in gridComp.Grid.GetAnchoredEntities(indices))
@@ -102,7 +111,7 @@ public sealed partial class AtmosphereSystem
Merge(tile, mixture); Merge(tile, mixture);
tile.Temperature = mixture.Temperature; tile.Temperature = mixture.Temperature;
InvalidateTile(gridAtmosphere, indices); gridAtmosphere.InvalidatedCoords.Add(indices);
} }
} }
} }

View File

@@ -87,7 +87,7 @@ namespace Content.Server.Atmos.EntitySystems
Merge(combined, tile.Air); Merge(combined, tile.Air);
if (!ExcitedGroupsSpaceIsAllConsuming || !tile.Air.Immutable) if (!ExcitedGroupsSpaceIsAllConsuming || !tile.Space)
continue; continue;
combined.Clear(); combined.Clear();

View File

@@ -48,19 +48,11 @@ namespace Content.Server.Atmos.EntitySystems
return GetHeatCapacityCalculation(mixture.Moles, mixture.Immutable); 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)] [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. // 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; return Atmospherics.SpaceHeatCapacity;
} }
@@ -136,7 +128,7 @@ namespace Content.Server.Atmos.EntitySystems
if (MathF.Abs(receiver.Temperature - source.Temperature) > Atmospherics.MinimumTemperatureDeltaToConsider) if (MathF.Abs(receiver.Temperature - source.Temperature) > Atmospherics.MinimumTemperatureDeltaToConsider)
{ {
// Often this divides a pipe net into new and completely empty pipe nets // Often this divides a pipe net into new and completely empty pipe nets
if (receiver.TotalMoles == 0) if (receiver.TotalMoles == 0)
receiver.Temperature = source.Temperature; receiver.Temperature = source.Temperature;
else else
{ {
@@ -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> /// <summary>
/// Releases gas from this mixture to the output mixture. /// Releases gas from this mixture to the output mixture.
/// If the output mixture is null, then this is being released into space. /// 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); 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> /// <summary>
/// Performs reactions for a given gas mixture on an optional holder. /// Performs reactions for a given gas mixture on an optional holder.
/// </summary> /// </summary>
@@ -401,5 +315,11 @@ namespace Content.Server.Atmos.EntitySystems
return reaction; return reaction;
} }
public enum GasCompareResult
{
NoExchange = -2,
TemperatureExchange = -1,
}
} }
} }

File diff suppressed because it is too large Load Diff

View File

@@ -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);
}
}
}

View File

@@ -48,7 +48,7 @@ namespace Content.Server.Atmos.EntitySystems
} }
shouldShareAir = true; shouldShareAir = true;
} else if (tile.Air!.Compare(enemyTile.Air!) != GasMixture.GasCompareResult.NoExchange) } else if (CompareExchange(tile.Air, enemyTile.Air) != GasCompareResult.NoExchange)
{ {
if (!enemyTile.Excited) if (!enemyTile.Excited)
{ {
@@ -78,7 +78,7 @@ namespace Content.Server.Atmos.EntitySystems
if (shouldShareAir) 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. // Monstermos already handles this, so let's not handle it ourselves.
if (!MonstermosEqualization) if (!MonstermosEqualization)
@@ -114,9 +114,17 @@ namespace Content.Server.Atmos.EntitySystems
private void Archive(TileAtmosphere tile, int fireCount) private void Archive(TileAtmosphere tile, int fireCount)
{ {
tile.Air?.Archive(); 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; tile.ArchivedCycle = fireCount;
tile.TemperatureArchived = tile.Temperature;
} }
private void LastShareCheck(TileAtmosphere tile) private void LastShareCheck(TileAtmosphere tile)
@@ -124,7 +132,7 @@ namespace Content.Server.Atmos.EntitySystems
if (tile.Air == null || tile.ExcitedGroup == null) if (tile.Air == null || tile.ExcitedGroup == null)
return; return;
switch (tile.Air.LastShare) switch (tile.LastShare)
{ {
case > Atmospherics.MinimumAirToSuspend: case > Atmospherics.MinimumAirToSuspend:
ExcitedGroupResetCooldowns(tile.ExcitedGroup); ExcitedGroupResetCooldowns(tile.ExcitedGroup);
@@ -134,5 +142,193 @@ namespace Content.Server.Atmos.EntitySystems
break; 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;
}
} }
} }

View 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;
}
}

View File

@@ -10,8 +10,6 @@ namespace Content.Server.Atmos.EntitySystems
{ {
public sealed partial class AtmosphereSystem public sealed partial class AtmosphereSystem
{ {
[Dependency] private readonly IRobustRandom _robustRandom = default!;
private readonly TileAtmosphereComparer _monstermosComparer = new(); private readonly TileAtmosphereComparer _monstermosComparer = new();
private readonly TileAtmosphere?[] _equalizeTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit]; private readonly TileAtmosphere?[] _equalizeTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit];
@@ -81,7 +79,7 @@ namespace Content.Server.Atmos.EntitySystems
if(tileCount < Atmospherics.MonstermosHardTileLimit) if(tileCount < Atmospherics.MonstermosHardTileLimit)
_equalizeTiles[tileCount++] = adj; _equalizeTiles[tileCount++] = adj;
if (adj.Air.Immutable && MonstermosDepressurization) if (adj.Space && MonstermosDepressurization)
{ {
// Looks like someone opened an airlock to space! // Looks like someone opened an airlock to space!
@@ -146,6 +144,7 @@ namespace Content.Server.Atmos.EntitySystems
var direction = (AtmosDirection) (1 << j); var direction = (AtmosDirection) (1 << j);
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue; if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
var tile2 = otherTile.AdjacentTiles[j]!; var tile2 = otherTile.AdjacentTiles[j]!;
DebugTools.Assert(tile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
// skip anything that isn't part of our current processing block. // skip anything that isn't part of our current processing block.
if (tile2.MonstermosInfo.FastDone || tile2.MonstermosInfo.LastQueueCycle != queueCycle) if (tile2.MonstermosInfo.FastDone || tile2.MonstermosInfo.LastQueueCycle != queueCycle)
@@ -213,8 +212,8 @@ namespace Content.Server.Atmos.EntitySystems
var otherTile2 = otherTile.AdjacentTiles[k]; 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 (giver.MonstermosInfo.MoleDelta <= 0) break; // We're done here now. Let's not do more work than needed.
if (otherTile2 == null || otherTile2.MonstermosInfo.LastQueueCycle != queueCycle) continue; if (otherTile2 == null || otherTile2.MonstermosInfo.LastQueueCycle != queueCycle) continue;
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue; if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
_equalizeQueue[queueLength++] = otherTile2; _equalizeQueue[queueLength++] = otherTile2;
otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow;
otherTile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite(); 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 (taker.MonstermosInfo.MoleDelta >= 0) break; // We're done here now. Let's not do more work than needed.
if (otherTile2 == null || otherTile2.MonstermosInfo.LastQueueCycle != queueCycle) continue; if (otherTile2 == null || otherTile2.MonstermosInfo.LastQueueCycle != queueCycle) continue;
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue; if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
_equalizeQueue[queueLength++] = otherTile2; _equalizeQueue[queueLength++] = otherTile2;
otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow;
@@ -335,7 +335,8 @@ namespace Content.Server.Atmos.EntitySystems
var direction = (AtmosDirection) (1 << j); var direction = (AtmosDirection) (1 << j);
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue; if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
var otherTile2 = otherTile.AdjacentTiles[j]!; 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); AddActiveTile(gridAtmosphere, otherTile2);
break; break;
} }
@@ -372,7 +373,7 @@ namespace Content.Server.Atmos.EntitySystems
otherTile.MonstermosInfo.LastCycle = cycleNum; otherTile.MonstermosInfo.LastCycle = cycleNum;
otherTile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid; otherTile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
// Tiles in the _depressurizeTiles array cannot have null air. // Tiles in the _depressurizeTiles array cannot have null air.
if (!otherTile.Air!.Immutable) if (!otherTile.Space)
{ {
for (var j = 0; j < Atmospherics.Directions; j++) for (var j = 0; j < Atmospherics.Directions; j++)
{ {
@@ -380,6 +381,7 @@ namespace Content.Server.Atmos.EntitySystems
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue; if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
var otherTile2 = otherTile.AdjacentTiles[j]; var otherTile2 = otherTile.AdjacentTiles[j];
if (otherTile2?.Air == null) continue; if (otherTile2?.Air == null) continue;
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
if (otherTile2.MonstermosInfo.LastQueueCycle == queueCycle) continue; if (otherTile2.MonstermosInfo.LastQueueCycle == queueCycle) continue;
ConsiderFirelocks(gridAtmosphere, otherTile, otherTile2); ConsiderFirelocks(gridAtmosphere, otherTile, otherTile2);
@@ -421,11 +423,12 @@ namespace Content.Server.Atmos.EntitySystems
{ {
var direction = (AtmosDirection) (1 << j); var direction = (AtmosDirection) (1 << j);
// Tiles in _depressurizeProgressionOrder cannot have null air. // 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]; var tile2 = otherTile.AdjacentTiles[j];
if (tile2?.MonstermosInfo.LastQueueCycle != queueCycle) continue; if (tile2?.MonstermosInfo.LastQueueCycle != queueCycle) continue;
DebugTools.Assert(tile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
if (tile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue; if (tile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
if(tile2.Air?.Immutable ?? false) continue; if(tile2.Space) continue;
tile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite(); tile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite();
tile2.MonstermosInfo.CurrentTransferAmount = 0; tile2.MonstermosInfo.CurrentTransferAmount = 0;
tile2.PressureSpecificTarget = otherTile.PressureSpecificTarget; tile2.PressureSpecificTarget = otherTile.PressureSpecificTarget;
@@ -479,7 +482,7 @@ namespace Content.Server.Atmos.EntitySystems
} }
if(tileCount > 10 && (totalMolesRemoved / tileCount) > 20) 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}"); $"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); Array.Clear(_depressurizeTiles, 0, Atmospherics.MonstermosHardTileLimit);
@@ -513,8 +516,10 @@ namespace Content.Server.Atmos.EntitySystems
if (!reconsiderAdjacent) if (!reconsiderAdjacent)
return; return;
UpdateAdjacent(mapGrid, gridAtmosphere, tile); var tileEv = new UpdateAdjacentMethodEvent(mapGrid.GridEntityId, tile.GridIndices);
UpdateAdjacent(mapGrid, gridAtmosphere, other); 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(tile.GridIndex, tile.GridIndices);
InvalidateVisuals(other.GridIndex, other.GridIndices); InvalidateVisuals(other.GridIndex, other.GridIndices);
} }
@@ -541,6 +546,7 @@ namespace Content.Server.Atmos.EntitySystems
var amount = transferDirections[i]; var amount = transferDirections[i];
var otherTile = tile.AdjacentTiles[i]; var otherTile = tile.AdjacentTiles[i];
if (otherTile?.Air == null) continue; if (otherTile?.Air == null) continue;
DebugTools.Assert(otherTile.AdjacentBits.IsFlagSet(direction.GetOpposite()));
if (amount <= 0) continue; if (amount <= 0) continue;
// Everything that calls this method already ensures that Air will not be null. // 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) private void AdjustEqMovement(TileAtmosphere tile, AtmosDirection direction, float amount)
{ {
DebugTools.AssertNotNull(tile);
DebugTools.Assert(tile.AdjacentBits.IsFlagSet(direction)); DebugTools.Assert(tile.AdjacentBits.IsFlagSet(direction));
DebugTools.Assert(tile.AdjacentTiles[direction.ToIndex()] != null); DebugTools.Assert(tile.AdjacentTiles[direction.ToIndex()] != null);
tile.MonstermosInfo[direction] += amount; tile.MonstermosInfo[direction] += amount;

View File

@@ -3,6 +3,7 @@ using Content.Server.Atmos.Piping.Components;
using Content.Server.NodeContainer.NodeGroups; using Content.Server.NodeContainer.NodeGroups;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Maps; using Content.Shared.Maps;
using Robust.Shared.Map;
using Robust.Shared.Timing; using Robust.Shared.Timing;
namespace Content.Server.Atmos.EntitySystems namespace Content.Server.Atmos.EntitySystems
@@ -42,37 +43,45 @@ namespace Content.Server.Atmos.EntitySystems
atmosphere.InvalidatedCoords.Clear(); atmosphere.InvalidatedCoords.Clear();
} }
if (!TryGetMapGrid(atmosphere, out var mapGrid)) var uid = atmosphere.Owner;
if (!TryComp(uid, out IMapGridComponent? mapGridComp))
return true; return true;
var mapGrid = mapGridComp.Grid;
var mapUid = _mapManager.GetMapEntityIdOrThrow(mapGridComp.Grid.ParentMapId);
var volume = GetVolumeForTiles(mapGrid, 1); var volume = GetVolumeForTiles(mapGrid, 1);
var number = 0; var number = 0;
while (atmosphere.CurrentRunInvalidatedCoordinates.TryDequeue(out var indices)) while (atmosphere.CurrentRunInvalidatedCoordinates.TryDequeue(out var indices))
{ {
var tile = GetTileAtmosphere(atmosphere, indices); if (!atmosphere.Tiles.TryGetValue(indices, out var tile))
if (tile == null)
{ {
tile = new TileAtmosphere(mapGrid.GridEntityId, indices, new GasMixture(volume){Temperature = Atmospherics.T20C}); tile = new TileAtmosphere(mapGrid.GridEntityId, indices, new GasMixture(volume){Temperature = Atmospherics.T20C});
atmosphere.Tiles[indices] = tile; 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 = GetTileMixture(null, mapUid, indices);
tile.Air.MarkImmutable(); tile.MolesArchived = tile.Air != null ? new float[Atmospherics.AdjustedNumberOfGases] : null;
atmosphere.Tiles[indices] = tile; tile.Space = IsTileSpace(null, mapUid, indices, mapGridComp);
} else if (isAirBlocked) } else if (isAirBlocked)
{ {
var nullAir = false; var nullAir = false;
foreach (var airtight in GetObstructingComponents(mapGrid, indices)) var enumerator = GetObstructingComponentsEnumerator(mapGrid, indices);
while (enumerator.MoveNext(out var airtight))
{ {
if (!airtight.NoAirWhenFullyAirBlocked) if (!airtight.NoAirWhenFullyAirBlocked)
continue; continue;
@@ -84,6 +93,9 @@ namespace Content.Server.Atmos.EntitySystems
if (nullAir) if (nullAir)
{ {
tile.Air = null; tile.Air = null;
tile.MolesArchived = null;
tile.ArchivedCycle = 0;
tile.LastShare = 0f;
tile.Hotspot = new Hotspot(); tile.Hotspot = new Hotspot();
} }
} }
@@ -91,23 +103,31 @@ namespace Content.Server.Atmos.EntitySystems
{ {
if (tile.Air == null && NeedsVacuumFixing(mapGrid, indices)) 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. // 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.Air = null;
tile.MolesArchived = null;
tile.ArchivedCycle = 0;
tile.LastShare = 0f;
tile.Space = false;
} }
tile.Air ??= new GasMixture(volume){Temperature = Atmospherics.T20C}; tile.Air ??= new GasMixture(volume){Temperature = Atmospherics.T20C};
tile.MolesArchived ??= new float[Atmospherics.AdjustedNumberOfGases];
} }
// We activate the tile. // We activate the tile.
AddActiveTile(atmosphere, tile); AddActiveTile(atmosphere, tile);
// TODO ATMOS: Query all the contents of this tile (like walls) and calculate the correct thermal conductivity and heat capacity // 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.ThermalConductivity = tileDef?.ThermalConductivity ?? 0.5f;
tile.HeatCapacity = tileDef?.HeatCapacity ?? float.PositiveInfinity; tile.HeatCapacity = tileDef?.HeatCapacity ?? float.PositiveInfinity;
InvalidateVisuals(mapGrid.GridEntityId, indices); InvalidateVisuals(mapGrid.GridEntityId, indices);
@@ -116,8 +136,8 @@ namespace Content.Server.Atmos.EntitySystems
{ {
var direction = (AtmosDirection) (1 << i); var direction = (AtmosDirection) (1 << i);
var otherIndices = indices.Offset(direction); var otherIndices = indices.Offset(direction);
var otherTile = GetTileAtmosphere(atmosphere, otherIndices);
if (otherTile != null) if (atmosphere.Tiles.TryGetValue(otherIndices, out var otherTile))
AddActiveTile(atmosphere, otherTile); AddActiveTile(atmosphere, otherTile);
} }
@@ -138,9 +158,13 @@ namespace Content.Server.Atmos.EntitySystems
if(!atmosphere.ProcessingPaused) if(!atmosphere.ProcessingPaused)
atmosphere.CurrentRunTiles = new Queue<TileAtmosphere>(atmosphere.ActiveTiles); 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!"); throw new Exception("Tried to process a grid atmosphere on an entity that isn't a grid!");
var mapGrid = mapGridComp.Grid;
var number = 0; var number = 0;
while (atmosphere.CurrentRunTiles.TryDequeue(out var tile)) while (atmosphere.CurrentRunTiles.TryDequeue(out var tile))
{ {

View File

@@ -73,7 +73,7 @@ namespace Content.Server.Atmos.EntitySystems
// Conduct with air on my tile if I have it // Conduct with air on my tile if I have it
if (tile.Air != null) 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); FinishSuperconduction(gridAtmosphere, tile, tile.Air?.Temperature ?? tile.Temperature);
@@ -107,7 +107,7 @@ namespace Content.Server.Atmos.EntitySystems
if (other.Air != null) if (other.Air != null)
{ {
TemperatureShare(other.Air, tile.Air, Atmospherics.WindowHeatTransferCoefficient); TemperatureShare(other, tile, Atmospherics.WindowHeatTransferCoefficient);
} }
else else
{ {
@@ -122,7 +122,7 @@ namespace Content.Server.Atmos.EntitySystems
if (tile.Air == null) if (tile.Air == null)
return; 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) private void TemperatureShareMutualSolid(TileAtmosphere tile, TileAtmosphere other, float conductionCoefficient)

View 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);
}
}

View File

@@ -4,90 +4,84 @@ using Content.Server.NodeContainer.EntitySystems;
using Content.Shared.Atmos.EntitySystems; using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Maps; using Content.Shared.Maps;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Map; 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>
[UsedImplicitly]
public sealed partial class AtmosphereSystem : SharedAtmosphereSystem
{ {
/// <summary> [Dependency] private readonly IMapManager _mapManager = default!;
/// This is our SSAir equivalent, if you need to interact with or query atmos in any way, go through this. [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
/// </summary> [Dependency] private readonly IRobustRandom _robustRandom = default!;
[UsedImplicitly] [Dependency] private readonly IAdminLogManager _adminLog = default!;
public sealed partial class AtmosphereSystem : SharedAtmosphereSystem [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;
private float _exposedTimer = 0f;
public override void Initialize()
{ {
[Dependency] private readonly IMapManager _mapManager = default!; base.Initialize();
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly SharedContainerSystem _containers = default!; UpdatesAfter.Add(typeof(NodeGroupSystem));
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
InitializeGases();
InitializeCommands();
InitializeCVars();
InitializeGridAtmosphere();
InitializeMap();
private const float ExposedUpdateDelay = 1f; SubscribeLocalEvent<TileChangedEvent>(OnTileChanged);
private float _exposedTimer = 0f;
public override void Initialize() }
public override void Shutdown()
{
base.Shutdown();
ShutdownCommands();
}
private void OnTileChanged(TileChangedEvent ev)
{
InvalidateTile(ev.NewTile.GridUid, ev.NewTile.GridIndices);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
UpdateProcessing(frameTime);
UpdateHighPressure(frameTime);
_exposedTimer += frameTime;
if (_exposedTimer < ExposedUpdateDelay)
return;
foreach (var (exposed, transform) in EntityManager.EntityQuery<AtmosExposedComponent, TransformComponent>())
{ {
base.Initialize(); var air = GetContainingMixture(exposed.Owner, transform:transform);
UpdatesAfter.Add(typeof(NodeGroupSystem)); if (air == null)
continue;
InitializeGases();
InitializeCommands();
InitializeCVars();
InitializeGrid();
SubscribeLocalEvent<TileChangedEvent>(OnTileChanged);
var updateEvent = new AtmosExposedUpdateEvent(transform.Coordinates, air, transform);
RaiseLocalEvent(exposed.Owner, ref updateEvent);
} }
public override void Shutdown() _exposedTimer -= ExposedUpdateDelay;
{
base.Shutdown();
ShutdownCommands();
}
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);
}
public override void Update(float frameTime)
{
base.Update(frameTime);
UpdateProcessing(frameTime);
UpdateHighPressure(frameTime);
_exposedTimer += frameTime;
if (_exposedTimer < ExposedUpdateDelay)
return;
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);
airEvent.Gas ??= GetTileMixture(transform.Coordinates);
if (airEvent.Gas == null)
continue;
var updateEvent = new AtmosExposedUpdateEvent(transform.Coordinates, airEvent.Gas);
RaiseLocalEvent(exposed.Owner, ref updateEvent, true);
}
_exposedTimer -= ExposedUpdateDelay;
}
} }
} }

View File

@@ -12,6 +12,7 @@ namespace Content.Server.Atmos.EntitySystems;
public sealed class AutomaticAtmosSystem : EntitySystem public sealed class AutomaticAtmosSystem : EntitySystem
{ {
[Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
public override void Initialize() 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. // 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)) || if (!((ev.OldTile.IsSpace(_tileDefinitionManager) && !ev.NewTile.IsSpace(_tileDefinitionManager)) ||
(!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; return;
if (!TryComp<PhysicsComponent>(ev.Entity, out var physics)) if (!TryComp<PhysicsComponent>(ev.Entity, out var physics))

View File

@@ -140,6 +140,7 @@ namespace Content.Server.Atmos.EntitySystems
foreach (var (barotrauma, damageable, transform) in EntityManager.EntityQuery<BarotraumaComponent, DamageableComponent, TransformComponent>()) foreach (var (barotrauma, damageable, transform) in EntityManager.EntityQuery<BarotraumaComponent, DamageableComponent, TransformComponent>())
{ {
var uid = barotrauma.Owner;
var totalDamage = FixedPoint2.Zero; var totalDamage = FixedPoint2.Zero;
foreach (var (barotraumaDamageType, _) in barotrauma.Damage.DamageDict) foreach (var (barotraumaDamageType, _) in barotrauma.Damage.DamageDict)
{ {
@@ -152,7 +153,7 @@ namespace Content.Server.Atmos.EntitySystems
var pressure = 1f; var pressure = 1f;
if (_atmosphereSystem.GetTileMixture(transform.Coordinates) is { } mixture) if (_atmosphereSystem.GetContainingMixture(uid) is {} mixture)
{ {
pressure = MathF.Max(mixture.Pressure, 1f); pressure = MathF.Max(mixture.Pressure, 1f);
} }

View File

@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Atmos.Components; using Content.Server.Atmos.Components;
using Content.Server.Stunnable; using Content.Server.Stunnable;
@@ -12,6 +14,10 @@ using Content.Shared.Database;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Popups; using Content.Shared.Popups;
using Content.Shared.Temperature; 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;
using Robust.Shared.Physics.Dynamics; using Robust.Shared.Physics.Dynamics;
@@ -25,6 +31,7 @@ namespace Content.Server.Atmos.EntitySystems
[Dependency] private readonly TemperatureSystem _temperatureSystem = default!; [Dependency] private readonly TemperatureSystem _temperatureSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly AlertsSystem _alertsSystem = default!; [Dependency] private readonly AlertsSystem _alertsSystem = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!;
private const float MinimumFireStacks = -10f; private const float MinimumFireStacks = -10f;
@@ -272,7 +279,7 @@ namespace Content.Server.Atmos.EntitySystems
continue; continue;
} }
var air = _atmosphereSystem.GetTileMixture(transform.Coordinates); var air = _atmosphereSystem.GetContainingMixture(uid);
// If we're in an oxygenless environment, put the fire out. // If we're in an oxygenless environment, put the fire out.
if (air == null || air.GetMoles(Gas.Oxygen) < 1f) if (air == null || air.GetMoles(Gas.Oxygen) < 1f)
@@ -281,7 +288,13 @@ namespace Content.Server.Atmos.EntitySystems
continue; 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()) foreach (var otherUid in flammable.Collided.ToArray())
{ {

View File

@@ -140,9 +140,7 @@ namespace Content.Server.Atmos.EntitySystems
/// <returns>true if updated</returns> /// <returns>true if updated</returns>
private bool TryRefreshTile(GridAtmosphereComponent gridAtmosphere, GasOverlayData oldTile, Vector2i indices, out GasOverlayData overlayData) private bool TryRefreshTile(GridAtmosphereComponent gridAtmosphere, GasOverlayData oldTile, Vector2i indices, out GasOverlayData overlayData)
{ {
var tile = _atmosphereSystem.GetTileAtmosphere(gridAtmosphere, indices); if (!gridAtmosphere.Tiles.TryGetValue(indices, out var tile))
if (tile == null)
{ {
overlayData = default; overlayData = default;
return false; return false;

View File

@@ -1,5 +1,7 @@
using System.Linq; using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Reactions; using Content.Server.Atmos.Reactions;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Robust.Shared.Serialization; using Robust.Shared.Serialization;
@@ -19,17 +21,12 @@ namespace Content.Server.Atmos
[DataField("moles")] [ViewVariables] [DataField("moles")] [ViewVariables]
public float[] Moles = new float[Atmospherics.AdjustedNumberOfGases]; public float[] Moles = new float[Atmospherics.AdjustedNumberOfGases];
public float[] MolesArchived = new float[Atmospherics.AdjustedNumberOfGases];
[DataField("temperature")] [ViewVariables] [DataField("temperature")] [ViewVariables]
private float _temperature = Atmospherics.TCMB; private float _temperature = Atmospherics.TCMB;
[DataField("immutable")] [ViewVariables] [DataField("immutable")] [ViewVariables]
public bool Immutable { get; private set; } public bool Immutable { get; private set; }
[DataField("lastShare")] [ViewVariables]
public float LastShare { get; set; }
[ViewVariables] [ViewVariables]
public readonly Dictionary<GasReaction, float> ReactionResults = new() public readonly Dictionary<GasReaction, float> ReactionResults = new()
{ {
@@ -65,8 +62,6 @@ namespace Content.Server.Atmos
} }
} }
public float TemperatureArchived { get; private set; }
[DataField("volume")] [ViewVariables] [DataField("volume")] [ViewVariables]
public float Volume { get; set; } public float Volume { get; set; }
@@ -87,13 +82,6 @@ namespace Content.Server.Atmos
Immutable = true; Immutable = true;
} }
[MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Archive()
{
Moles.AsSpan().CopyTo(MolesArchived.AsSpan());
TemperatureArchived = Temperature;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public float GetMoles(int gasId) public float GetMoles(int gasId)
{ {
@@ -192,40 +180,6 @@ namespace Content.Server.Atmos
Temperature = sample.Temperature; 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)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
public void Clear() public void Clear()
{ {
@@ -244,7 +198,6 @@ namespace Content.Server.Atmos
{ {
// The arrays MUST have a specific length. // The arrays MUST have a specific length.
Array.Resize(ref Moles, Atmospherics.AdjustedNumberOfGases); Array.Resize(ref Moles, Atmospherics.AdjustedNumberOfGases);
Array.Resize(ref MolesArchived, Atmospherics.AdjustedNumberOfGases);
} }
public override bool Equals(object? obj) public override bool Equals(object? obj)
@@ -259,15 +212,13 @@ namespace Content.Server.Atmos
if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(null, other)) return false;
if (ReferenceEquals(this, other)) return true; if (ReferenceEquals(this, other)) return true;
return Moles.SequenceEqual(other.Moles) return Moles.SequenceEqual(other.Moles)
&& MolesArchived.SequenceEqual(other.MolesArchived)
&& _temperature.Equals(other._temperature) && _temperature.Equals(other._temperature)
&& ReactionResults.SequenceEqual(other.ReactionResults) && ReactionResults.SequenceEqual(other.ReactionResults)
&& Immutable == other.Immutable && Immutable == other.Immutable
&& LastShare.Equals(other.LastShare)
&& TemperatureArchived.Equals(other.TemperatureArchived)
&& Volume.Equals(other.Volume); && Volume.Equals(other.Volume);
} }
[SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")]
public override int GetHashCode() public override int GetHashCode()
{ {
var hashCode = new HashCode(); var hashCode = new HashCode();
@@ -275,15 +226,11 @@ namespace Content.Server.Atmos
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
{ {
var moles = Moles[i]; var moles = Moles[i];
var molesArchived = MolesArchived[i];
hashCode.Add(moles); hashCode.Add(moles);
hashCode.Add(molesArchived);
} }
hashCode.Add(_temperature); hashCode.Add(_temperature);
hashCode.Add(TemperatureArchived);
hashCode.Add(Immutable); hashCode.Add(Immutable);
hashCode.Add(LastShare);
hashCode.Add(Volume); hashCode.Add(Volume);
return hashCode.ToHashCode(); return hashCode.ToHashCode();
@@ -294,11 +241,8 @@ namespace Content.Server.Atmos
var newMixture = new GasMixture() var newMixture = new GasMixture()
{ {
Moles = (float[])Moles.Clone(), Moles = (float[])Moles.Clone(),
MolesArchived = (float[])MolesArchived.Clone(),
_temperature = _temperature, _temperature = _temperature,
Immutable = Immutable, Immutable = Immutable,
LastShare = LastShare,
TemperatureArchived = TemperatureArchived,
Volume = Volume, Volume = Volume,
}; };
return newMixture; return newMixture;

View File

@@ -5,6 +5,7 @@ using Content.Server.Atmos.EntitySystems;
using Content.Server.Temperature.Systems; using Content.Server.Temperature.Systems;
using Content.Server.Body.Components; using Content.Server.Body.Components;
using Content.Shared.Examine; using Content.Shared.Examine;
using Robust.Server.GameObjects;
using Content.Shared.Tag; using Content.Shared.Tag;
using Robust.Shared.Containers; using Robust.Shared.Containers;
using Robust.Shared.Random; using Robust.Shared.Random;
@@ -13,6 +14,7 @@ namespace Content.Server.Atmos.Miasma
{ {
public sealed class MiasmaSystem : EntitySystem public sealed class MiasmaSystem : EntitySystem
{ {
[Dependency] private readonly TransformSystem _transformSystem = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!;
@@ -108,9 +110,11 @@ namespace Content.Server.Atmos.Miasma
float molRate = perishable.MolsPerSecondPerUnitMass * _rotUpdateRate; float molRate = perishable.MolsPerSecondPerUnitMass * _rotUpdateRate;
var tileMix = _atmosphereSystem.GetTileMixture(Transform(perishable.Owner).Coordinates); var transform = Transform(perishable.Owner);
if (tileMix != null) var indices = _transformSystem.GetGridOrMapTilePosition(perishable.Owner);
tileMix.AdjustMoles(Gas.Miasma, molRate * physics.FixturesMass);
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; return;
var molsToDump = (component.MolsPerSecondPerUnitMass * physics.FixturesMass) * component.DeathAccumulator; var molsToDump = (component.MolsPerSecondPerUnitMass * physics.FixturesMass) * component.DeathAccumulator;
var tileMix = _atmosphereSystem.GetTileMixture(Transform(uid).Coordinates); var transform = Transform(uid);
if (tileMix != null) var indices = _transformSystem.GetGridOrMapTilePosition(uid, transform);
tileMix.AdjustMoles(Gas.Miasma, molsToDump); var tileMix = _atmosphereSystem.GetTileMixture(transform.GridUid, null, indices, true);
tileMix?.AdjustMoles(Gas.Miasma, molsToDump);
// Waste of entities to let these through // Waste of entities to let these through
foreach (var part in args.GibbedParts) foreach (var part in args.GibbedParts)

View File

@@ -9,6 +9,7 @@ using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems; using Content.Server.Power.EntitySystems;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Monitor; using Content.Shared.Atmos.Monitor;
using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Player; using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -24,6 +25,7 @@ namespace Content.Server.Atmos.Monitor.Systems
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly AtmosDeviceSystem _atmosDeviceSystem = default!; [Dependency] private readonly AtmosDeviceSystem _atmosDeviceSystem = default!;
[Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
// Commands // Commands
@@ -118,27 +120,31 @@ namespace Content.Server.Atmos.Monitor.Systems
if (!Resolve(uid, ref component, ref appearance)) return; if (!Resolve(uid, ref component, ref appearance)) return;
var transform = Transform(component.Owner); var transform = Transform(component.Owner);
if (transform.GridUid == null)
return;
// atmos alarms will first attempt to get the air // atmos alarms will first attempt to get the air
// directly underneath it - if not, then it will // directly underneath it - if not, then it will
// instead place itself directly in front of the tile // instead place itself directly in front of the tile
// it is facing, and then visually shift itself back // it is facing, and then visually shift itself back
// via sprite offsets (SS13 style but fuck it) // via sprite offsets (SS13 style but fuck it)
var coords = transform.Coordinates; 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)); var rotPos = transform.LocalRotation.RotateVec(new Vector2(0, -1));
transform.Anchored = false; transform.Anchored = false;
coords = coords.Offset(rotPos); coords = coords.Offset(rotPos);
transform.Coordinates = coords; transform.Coordinates = coords;
appearance.SetData("offset", - new Vector2(0, -1)); appearance.SetData("offset", - new Vector2i(0, -1));
transform.Anchored = true; transform.Anchored = true;
} }
GasMixture? air = _atmosphereSystem.GetTileMixture(coords); GasMixture? air = _atmosphereSystem.GetContainingMixture(uid, true);
component.TileGas = air; component.TileGas = air;
_checkPos.Remove(uid); _checkPos.Remove(uid);
@@ -214,8 +220,7 @@ namespace Content.Server.Atmos.Monitor.Systems
if (atmosDeviceComponent.JoinedGrid == null) if (atmosDeviceComponent.JoinedGrid == null)
{ {
_atmosDeviceSystem.JoinAtmosphere(atmosDeviceComponent); _atmosDeviceSystem.JoinAtmosphere(atmosDeviceComponent);
var coords = Transform(component.Owner).Coordinates; var air = _atmosphereSystem.GetContainingMixture(uid, true);
var air = _atmosphereSystem.GetTileMixture(coords);
component.TileGas = air; component.TileGas = air;
} }
} }

View File

@@ -21,9 +21,10 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
public sealed class GasVolumePumpSystem : EntitySystem public sealed class GasVolumePumpSystem : EntitySystem
{ {
[Dependency] private readonly IGameTiming _gameTiming = default!; [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 AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
[Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!;
public override void Initialize() 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.InletName, out PipeNode? inlet)
|| !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet)) || !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet))
{ {
_ambientSoundSystem.SetAmbience(pump.Owner, false); _ambientSoundSystem.SetAmbience(uid, false);
return; return;
} }
@@ -88,7 +89,9 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
// Some of the gas from the mixture leaks when overclocked. // Some of the gas from the mixture leaks when overclocked.
if (pump.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) if (tile != null)
{ {
@@ -98,7 +101,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems
} }
_atmosphereSystem.Merge(outlet.Air, removed); _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) private void OnVolumePumpLeaveAtmosphere(EntityUid uid, GasVolumePumpComponent pump, AtmosDeviceDisabledEvent args)

View File

@@ -27,20 +27,25 @@ namespace Content.Server.Atmos.Piping.EntitySystems
SubscribeLocalEvent<AtmosDeviceComponent, AnchorStateChangedEvent>(OnDeviceAnchorChanged); 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) public void JoinAtmosphere(AtmosDeviceComponent component)
{ {
if (!CanJoinAtmosphere(component)) var transform = Transform(component.Owner);
if (!CanJoinAtmosphere(component, transform))
{ {
return; 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. // 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) if (component.JoinSystem)
{ {
@@ -62,7 +67,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems
public void LeaveAtmosphere(AtmosDeviceComponent component) public void LeaveAtmosphere(AtmosDeviceComponent component)
{ {
// Try to remove the component from an atmosphere, and if not // 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. // The grid might have been removed but not us... This usually shouldn't happen.
component.JoinedGrid = null; component.JoinedGrid = null;

View File

@@ -27,7 +27,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems
if (!component.Enabled || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodes)) if (!component.Enabled || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodes))
return; return;
if (_atmosphereSystem.GetTileMixture(EntityManager.GetComponent<TransformComponent>(component.Owner).Coordinates) is not {} environment) if (_atmosphereSystem.GetContainingMixture(uid, true) is not {} environment)
return; return;
foreach (var node in nodes.Nodes.Values) 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)) if (!component.Enabled || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodes))
return; 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; environment = GasMixture.SpaceGas;
var lost = 0f; var lost = 0f;

View File

@@ -1,9 +1,13 @@
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Piping.Components; using Content.Server.Atmos.Piping.Components;
using Content.Server.Atmos.Piping.Other.Components; using Content.Server.Atmos.Piping.Other.Components;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.Atmos.Piping.Other.EntitySystems namespace Content.Server.Atmos.Piping.Other.EntitySystems
{ {
@@ -11,6 +15,7 @@ namespace Content.Server.Atmos.Piping.Other.EntitySystems
public sealed class GasMinerSystem : EntitySystem public sealed class GasMinerSystem : EntitySystem
{ {
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
public override void Initialize() 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) 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. // Space.
if (_atmosphereSystem.IsTileSpace(EntityManager.GetComponent<TransformComponent>(miner.Owner).Coordinates)) if (_atmosphereSystem.IsTileSpace(transform.GridUid, transform.MapUid, position))
{ {
miner.Broken = true; miner.Broken = true;
return false; return false;

View File

@@ -51,7 +51,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
if (!Resolve(uid, ref canister, ref transform)) if (!Resolve(uid, ref canister, ref transform))
return; return;
var environment = _atmosphereSystem.GetTileMixture(transform.Coordinates, true); var environment = _atmosphereSystem.GetContainingMixture(uid, false, true);
if (environment is not null) if (environment is not null)
_atmosphereSystem.Merge(environment, canister.Air); _atmosphereSystem.Merge(environment, canister.Air);
@@ -180,7 +180,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
} }
else 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); _atmosphereSystem.ReleaseGasTo(canister.Air, environment, canister.ReleasePressure);
} }
} }

View File

@@ -58,7 +58,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
if (!nodeContainer.TryGetNode(injector.InletName, out PipeNode? inlet)) if (!nodeContainer.TryGetNode(injector.InletName, out PipeNode? inlet))
return; return;
var environment = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent<TransformComponent>(injector.Owner).Coordinates, true); var environment = _atmosphereSystem.GetContainingMixture(uid, true, true);
if (environment == null) if (environment == null)
return; return;

View File

@@ -22,7 +22,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
private void OnPassiveVentUpdated(EntityUid uid, GasPassiveVentComponent vent, AtmosDeviceUpdateEvent args) 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) if (environment == null)
return; return;

View File

@@ -67,7 +67,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
return; 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. // We're in an air-blocked tile... Do nothing.
if (environment == null) if (environment == null)

View File

@@ -15,6 +15,7 @@ using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Audio; using Content.Shared.Audio;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Timing; using Robust.Shared.Timing;
namespace Content.Server.Atmos.Piping.Unary.EntitySystems 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 AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!;
[Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -54,19 +56,24 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems
if (!scrubber.Enabled if (!scrubber.Enabled
|| !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)
|| !nodeContainer.TryGetNode(scrubber.OutletName, out PipeNode? outlet)) || !nodeContainer.TryGetNode(scrubber.OutletName, out PipeNode? outlet))
{
return; return;
}
var xform = Transform(uid); 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); Scrub(timeDelta, scrubber, environment, outlet);
if (!scrubber.WideNet) return; if (!scrubber.WideNet)
return;
// Scrub adjacent tiles too. // 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); Scrub(timeDelta, scrubber, adjacent, outlet);
} }

View File

@@ -9,7 +9,7 @@ namespace Content.Server.Atmos
/// Internal Atmos class that stores data about the atmosphere in a grid. /// Internal Atmos class that stores data about the atmosphere in a grid.
/// You shouldn't use this directly, use <see cref="AtmosphereSystem"/> instead. /// You shouldn't use this directly, use <see cref="AtmosphereSystem"/> instead.
/// </summary> /// </summary>
[Access(typeof(AtmosphereSystem))] [Access(typeof(AtmosphereSystem), typeof(GasTileOverlaySystem), typeof(AtmosDebugOverlaySystem))]
public sealed class TileAtmosphere : IGasMixtureHolder public sealed class TileAtmosphere : IGasMixtureHolder
{ {
[ViewVariables] [ViewVariables]
@@ -39,6 +39,12 @@ namespace Content.Server.Atmos
[ViewVariables] [ViewVariables]
public bool Excited { get; set; } public bool Excited { get; set; }
/// <summary>
/// Whether this tile should be considered space.
/// </summary>
[ViewVariables]
public bool Space { get; set; }
/// <summary> /// <summary>
/// Adjacent tiles in the same order as <see cref="AtmosDirection"/>. (NSEW) /// Adjacent tiles in the same order as <see cref="AtmosDirection"/>. (NSEW)
/// </summary> /// </summary>
@@ -81,6 +87,13 @@ namespace Content.Server.Atmos
[Access(typeof(AtmosphereSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends [Access(typeof(AtmosphereSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends
public GasMixture? Air { get; set; } public GasMixture? Air { get; set; }
[ViewVariables]
[DataField("lastShare")]
public float LastShare;
[ViewVariables]
public float[]? MolesArchived;
GasMixture IGasMixtureHolder.Air GasMixture IGasMixtureHolder.Air
{ {
get => Air ?? new GasMixture(Atmospherics.CellVolume){ Temperature = Temperature }; get => Air ?? new GasMixture(Atmospherics.CellVolume){ Temperature = Temperature };
@@ -93,11 +106,13 @@ namespace Content.Server.Atmos
[ViewVariables] [ViewVariables]
public AtmosDirection BlockedAirflow { get; set; } = AtmosDirection.Invalid; 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; GridIndex = gridIndex;
GridIndices = gridIndices; GridIndices = gridIndices;
Air = mixture; Air = mixture;
Space = space;
MolesArchived = Air != null ? new float[Atmospherics.AdjustedNumberOfGases] : null;
if(immutable) if(immutable)
Air?.MarkImmutable(); Air?.MarkImmutable();

View File

@@ -102,7 +102,7 @@ namespace Content.Server.Body.Systems
if (ev.Gas == null) if (ev.Gas == null)
{ {
ev.Gas = _atmosSys.GetTileMixture(Transform(uid).Coordinates); ev.Gas = _atmosSys.GetContainingMixture(uid, false, true);
if (ev.Gas == null) return; if (ev.Gas == null) return;
} }
@@ -133,7 +133,7 @@ namespace Content.Server.Body.Systems
if (ev.Gas == null) 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, // 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. // but this also means you cannot exhale on some grids.

View File

@@ -7,6 +7,7 @@ using Content.Shared.Botany;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Reagent; using Content.Shared.Chemistry.Reagent;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Server.GameObjects;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Random; using Robust.Shared.Random;
using Robust.Shared.Timing; using Robust.Shared.Timing;
@@ -225,7 +226,8 @@ namespace Content.Server.Botany.Components
_updateSpriteAfterUpdate = true; _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; GasMixture.SpaceGas;
if (Seed.ConsumeGasses.Count > 0) if (Seed.ConsumeGasses.Count > 0)

View File

@@ -21,10 +21,9 @@ public sealed class CreateGas : ReagentEffect
public override void Effect(ReagentEffectArgs args) 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.GetContainingMixture(args.SolutionEntity, false, true);
var tileMix = atmosSys.GetTileMixture(xform.Coordinates);
if (tileMix != null) if (tileMix != null)
{ {

View File

@@ -21,7 +21,7 @@ namespace Content.Server.Chemistry.TileReactions
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>(); 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)) if (environment == null || !atmosphereSystem.IsHotspotActive(tile.GridUid, tile.GridIndices))
return FixedPoint2.Zero; return FixedPoint2.Zero;
@@ -30,7 +30,7 @@ namespace Content.Server.Chemistry.TileReactions
MathF.Max(MathF.Min(environment.Temperature - (_coolingTemperature * 1000f), MathF.Max(MathF.Min(environment.Temperature - (_coolingTemperature * 1000f),
environment.Temperature / _coolingTemperature), Atmospherics.TCMB); environment.Temperature / _coolingTemperature), Atmospherics.TCMB);
atmosphereSystem.React(tile.GridUid, tile.GridIndices); atmosphereSystem.ReactTile(tile.GridUid, tile.GridIndices);
atmosphereSystem.HotspotExtinguish(tile.GridUid, tile.GridIndices); atmosphereSystem.HotspotExtinguish(tile.GridUid, tile.GridIndices);
return FixedPoint2.Zero; return FixedPoint2.Zero;

View File

@@ -20,12 +20,12 @@ namespace Content.Server.Chemistry.TileReactions
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>(); 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)) if (environment == null || !atmosphereSystem.IsHotspotActive(tile.GridUid, tile.GridIndices))
return FixedPoint2.Zero; return FixedPoint2.Zero;
environment.Temperature *= MathF.Max(_temperatureMultiplier * reactVolume.Float(), 1f); environment.Temperature *= MathF.Max(_temperatureMultiplier * reactVolume.Float(), 1f);
atmosphereSystem.React(tile.GridUid, tile.GridIndices); atmosphereSystem.ReactTile(tile.GridUid, tile.GridIndices);
return reactVolume; return reactVolume;
} }

View File

@@ -70,7 +70,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
_disposalUnitSystem.TryEjectContents(duc); _disposalUnitSystem.TryEjectContents(duc);
} }
if (_atmosphereSystem.GetTileMixture(holderTransform.Coordinates, true) is {} environment) if (_atmosphereSystem.GetContainingMixture(uid, false, true) is {} environment)
{ {
_atmosphereSystem.Merge(environment, holder.Air); _atmosphereSystem.Merge(environment, holder.Air);
holder.Air.Clear(); holder.Air.Clear();

View File

@@ -42,6 +42,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems
[Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly DoAfterSystem _doAfterSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly DumpableSystem _dumpableSystem = default!; [Dependency] private readonly DumpableSystem _dumpableSystem = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
private readonly List<DisposalUnitComponent> _activeDisposals = new(); private readonly List<DisposalUnitComponent> _activeDisposals = new();
@@ -521,8 +522,9 @@ namespace Content.Server.Disposal.Unit.EntitySystems
var air = component.Air; var air = component.Air;
var entryComponent = EntityManager.GetComponent<DisposalEntryComponent>(entry); 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); var transferMoles = 0.1f * (0.25f * Atmospherics.OneAtmosphere * 1.01f - air.Pressure) * air.Volume / (environment.Temperature * Atmospherics.R);

View File

@@ -2,6 +2,10 @@ using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Doors.Systems; using Content.Server.Doors.Systems;
using Content.Shared.Doors.Components; 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 namespace Content.Server.Doors.Components
{ {
@@ -42,12 +46,20 @@ namespace Content.Server.Doors.Components
public bool IsHoldingPressure(float threshold = 20) 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 minMoles = float.MaxValue;
var maxMoles = 0f; 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; var moles = adjacent.TotalMoles;
if (moles < minMoles) if (moles < minMoles)
@@ -61,20 +73,25 @@ namespace Content.Server.Doors.Components
public bool IsHoldingFire() 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; return false;
if (atmosphereSystem.GetTileMixture(tuple.Value.Grid, tuple.Value.Tile) == null) if (atmosphereSystem.GetTileMixture(gridUid, null, position) == null)
return false; return false;
if (atmosphereSystem.IsHotspotActive(tuple.Value.Grid, tuple.Value.Tile)) if (atmosphereSystem.IsHotspotActive(gridUid, position))
return true; 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; return true;
} }

View File

@@ -19,6 +19,7 @@ using Content.Shared.MobState.Components;
using Content.Shared.PDA; using Content.Shared.PDA;
using Content.Shared.Roles; using Content.Shared.Roles;
using Content.Shared.Traitor.Uplink; using Content.Shared.Traitor.Uplink;
using Robust.Server.GameObjects;
using Robust.Server.Player; using Robust.Server.Player;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Map; using Robust.Shared.Map;
@@ -36,6 +37,8 @@ public sealed class TraitorDeathMatchRuleSystem : GameRuleSystem
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly MaxTimeRestartRuleSystem _restarter = default!; [Dependency] private readonly MaxTimeRestartRuleSystem _restarter = default!;
[Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
public override string Prototype => "TraitorDeathMatch"; public override string Prototype => "TraitorDeathMatch";
@@ -243,10 +246,17 @@ public sealed class TraitorDeathMatchRuleSystem : GameRuleSystem
_robustRandom.Shuffle(ents); _robustRandom.Shuffle(ents);
var foundATarget = false; var foundATarget = false;
bestTarget = EntityCoordinates.Invalid; bestTarget = EntityCoordinates.Invalid;
var atmosphereSystem = EntitySystem.Get<AtmosphereSystem>();
foreach (var entity in ents) 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; continue;
var distanceFromNearest = float.PositiveInfinity; var distanceFromNearest = float.PositiveInfinity;

View File

@@ -14,8 +14,8 @@ namespace Content.Server.Light.EntitySystems
public sealed class MatchstickSystem : EntitySystem public sealed class MatchstickSystem : EntitySystem
{ {
private HashSet<MatchstickComponent> _litMatches = new(); private HashSet<MatchstickComponent> _litMatches = new();
[Dependency] [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly TransformSystem _transformSystem = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -38,7 +38,14 @@ namespace Content.Server.Light.EntitySystems
if (match.CurrentState != SmokableState.Lit || Paused(match.Owner) || match.Deleted) if (match.CurrentState != SmokableState.Lit || Paused(match.Owner) || match.Deleted)
continue; 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);
} }
} }

View File

@@ -33,10 +33,13 @@ namespace Content.Server.NodeContainer.NodeGroups
Grid = entMan.GetComponent<TransformComponent>(sourceNode.Owner).GridUid; Grid = entMan.GetComponent<TransformComponent>(sourceNode.Owner).GridUid;
if (Grid == null) 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)}"); 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 = entMan.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
_atmosphereSystem.AddPipeNet(this); _atmosphereSystem.AddPipeNet(Grid.Value, this);
} }
public void Update() public void Update()
@@ -85,7 +88,11 @@ namespace Content.Server.NodeContainer.NodeGroups
private void RemoveFromGridAtmos() private void RemoveFromGridAtmos()
{ {
DebugTools.AssertNotNull(_atmosphereSystem); DebugTools.AssertNotNull(_atmosphereSystem);
_atmosphereSystem?.RemovePipeNet(this);
if (Grid == null)
return;
_atmosphereSystem?.RemovePipeNet(Grid.Value, this);
} }
public override string GetDebugData() public override string GetDebugData()

View File

@@ -11,6 +11,7 @@ using Content.Shared.FixedPoint;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Content.Shared.Smoking; using Content.Shared.Smoking;
using Content.Shared.Temperature; using Content.Shared.Temperature;
using Robust.Server.GameObjects;
using Robust.Shared.Containers; using Robust.Shared.Containers;
namespace Content.Server.Nutrition.EntitySystems namespace Content.Server.Nutrition.EntitySystems
@@ -21,8 +22,8 @@ namespace Content.Server.Nutrition.EntitySystems
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
[Dependency] private readonly AtmosphereSystem _atmos = default!; [Dependency] private readonly AtmosphereSystem _atmos = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
[Dependency] private readonly InventorySystem _inventorySystem = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!;
private const float UpdateTimer = 3f; private const float UpdateTimer = 3f;
private float _timer = 0f; private float _timer = 0f;
@@ -79,6 +80,7 @@ namespace Content.Server.Nutrition.EntitySystems
if (_timer < UpdateTimer) if (_timer < UpdateTimer)
return; return;
// TODO Use an "active smoke" component instead, EntityQuery over that.
foreach (var uid in _active.ToArray()) foreach (var uid in _active.ToArray())
{ {
if (!TryComp(uid, out SmokableComponent? smokable)) if (!TryComp(uid, out SmokableComponent? smokable))
@@ -96,7 +98,12 @@ namespace Content.Server.Nutrition.EntitySystems
if (smokable.ExposeTemperature > 0 && smokable.ExposeVolume > 0) if (smokable.ExposeTemperature > 0 && smokable.ExposeVolume > 0)
{ {
var transform = Transform(uid); 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); var inhaledSolution = _solutionContainerSystem.SplitSolution(uid, solution, smokable.InhaleAmount * _timer);

View File

@@ -252,7 +252,7 @@ namespace Content.Server.PneumaticCannon
{ {
// we checked for this earlier in HasGas so a GetComp is okay // we checked for this earlier in HasGas so a GetComp is okay
var gas = EntityManager.GetComponent<GasTankComponent>(contained); 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)); var removed = gas.RemoveAir(GetMoleUsageFromPower(comp.Power));
if (environment != null && removed != null) if (environment != null && removed != null)
{ {

View File

@@ -6,6 +6,7 @@ using Content.Server.Nutrition.Components;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Actions; using Content.Shared.Actions;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Robust.Server.GameObjects;
using Robust.Shared.Player; using Robust.Shared.Player;
namespace Content.Server.RatKing namespace Content.Server.RatKing
@@ -17,6 +18,7 @@ namespace Content.Server.RatKing
[Dependency] private readonly DiseaseSystem _disease = default!; [Dependency] private readonly DiseaseSystem _disease = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!;
[Dependency] private readonly AtmosphereSystem _atmos = default!; [Dependency] private readonly AtmosphereSystem _atmos = default!;
[Dependency] private readonly TransformSystem _xform = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -79,9 +81,10 @@ namespace Content.Server.RatKing
_popup.PopupEntity(Loc.GetString("rat-king-domain-popup"), uid, Filter.Pvs(uid)); _popup.PopupEntity(Loc.GetString("rat-king-domain-popup"), uid, Filter.Pvs(uid));
var tileMix = _atmos.GetTileMixture(Transform(uid).Coordinates); var transform = Transform(uid);
if (tileMix != null) var indices = _xform.GetGridOrMapTilePosition(uid, transform);
tileMix.AdjustMoles(Gas.Miasma, component.MolesMiasmaPerDomain); var tileMix = _atmos.GetTileMixture(transform.GridUid, transform.MapUid, indices, true);
tileMix?.AdjustMoles(Gas.Miasma, component.MolesMiasmaPerDomain);
} }
} }

View File

@@ -115,7 +115,7 @@ namespace Content.Server.StationEvents.Events
_timeUntilLeak += LeakCooldown; _timeUntilLeak += LeakCooldown;
var atmosphereSystem = _entityManager.EntitySysManager.GetEntitySystem<AtmosphereSystem>(); var atmosphereSystem = _entityManager.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
if (!_foundTile || if (!_foundTile ||
_targetGrid == default || _targetGrid == default ||
_entityManager.Deleted(_targetGrid) || _entityManager.Deleted(_targetGrid) ||
@@ -125,7 +125,7 @@ namespace Content.Server.StationEvents.Events
return; return;
} }
var environment = atmosphereSystem.GetTileMixture(_targetGrid, _targetTile, true); var environment = atmosphereSystem.GetTileMixture(_targetGrid, null, _targetTile, true);
environment?.AdjustMoles(_leakGas, LeakCooldown * _molesPerSecond); environment?.AdjustMoles(_leakGas, LeakCooldown * _molesPerSecond);
} }

View File

@@ -227,7 +227,8 @@ namespace Content.Server.StationEvents.Events
targetGrid = robustRandom.Pick(possibleTargets); 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; return false;
var grid = gridComp.Grid; var grid = gridComp.Grid;
@@ -242,7 +243,9 @@ namespace Content.Server.StationEvents.Events
var randomY = robustRandom.Next((int) gridBounds.Bottom, (int) gridBounds.Top); var randomY = robustRandom.Next((int) gridBounds.Bottom, (int) gridBounds.Top);
tile = new Vector2i(randomX - (int) gridPos.X, randomY - (int) gridPos.Y); 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; found = true;
targetCoords = grid.GridTileToLocal(tile); targetCoords = grid.GridTileToLocal(tile);
break; break;

View File

@@ -1,3 +1,5 @@
using System;
using System.Collections.Generic;
using System.Linq; using System.Linq;
using Content.Server.Administration.Logs; using Content.Server.Administration.Logs;
using Content.Server.Atmos.Components; using Content.Server.Atmos.Components;
@@ -7,11 +9,15 @@ using Content.Shared.Alert;
using Content.Shared.Damage; using Content.Shared.Damage;
using Content.Shared.Database; using Content.Shared.Database;
using Content.Shared.Inventory; using Content.Shared.Inventory;
using Robust.Server.GameObjects;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.Temperature.Systems namespace Content.Server.Temperature.Systems
{ {
public sealed class TemperatureSystem : EntitySystem public sealed class TemperatureSystem : EntitySystem
{ {
[Dependency] private readonly TransformSystem _transformSystem = default!;
[Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly AlertsSystem _alertsSystem = 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) 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 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)); var heat = temperatureDelta * (tileHeatCapacity * temperature.HeatCapacity / (tileHeatCapacity + temperature.HeatCapacity));
ChangeHeat(uid, heat * temperature.AtmosTemperatureTransferEfficiency, temperature: temperature ); ChangeHeat(uid, heat * temperature.AtmosTemperatureTransferEfficiency, temperature: temperature );
} }

View File

@@ -78,9 +78,10 @@ namespace Content.Server.Tools
SolutionContainerManagerComponent? solutionContainer = null, SolutionContainerManagerComponent? solutionContainer = null,
SharedItemComponent? item = null, SharedItemComponent? item = null,
PointLightComponent? light = 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; return false;
// Optional components. // 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)); SoundSystem.Play(welder.WelderOnSounds.GetSound(), Filter.Pvs(uid), uid, AudioHelpers.WithVariation(0.125f).WithVolume(-5f));
// TODO: Use TransformComponent directly. if (transform.GridUid is {} gridUid)
_atmosphereSystem.HotspotExpose(EntityManager.GetComponent<TransformComponent>(welder.Owner).Coordinates, 700, 50, true); {
var position = _transformSystem.GetGridOrMapTilePosition(uid, transform);
_atmosphereSystem.HotspotExpose(gridUid, position, 700, 50, true);
}
welder.Dirty(); welder.Dirty();
@@ -300,17 +304,22 @@ namespace Content.Server.Tools
if (_welderTimer < WelderUpdateTimer) if (_welderTimer < WelderUpdateTimer)
return; return;
// TODO Use an "active welder" component instead, EntityQuery over that.
foreach (var tool in _activeWelders.ToArray()) foreach (var tool in _activeWelders.ToArray())
{ {
if (!EntityManager.TryGetComponent(tool, out WelderComponent? welder) 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; continue;
if (!_solutionContainerSystem.TryGetSolution(tool, welder.FuelSolution, out var solution, solutionContainer)) if (!_solutionContainerSystem.TryGetSolution(tool, welder.FuelSolution, out var solution, solutionContainer))
continue; continue;
// TODO: Use TransformComponent directly. if (transform.GridUid is { } gridUid)
_atmosphereSystem.HotspotExpose(EntityManager.GetComponent<TransformComponent>(welder.Owner).Coordinates, 700, 50, true); {
var position = _transformSystem.GetGridOrMapTilePosition(tool, transform);
_atmosphereSystem.HotspotExpose(gridUid, position, 700, 50, true);
}
solution.RemoveReagent(welder.FuelReagent, welder.FuelConsumption * _welderTimer); solution.RemoveReagent(welder.FuelReagent, welder.FuelConsumption * _welderTimer);

View File

@@ -6,6 +6,7 @@ using Content.Server.DoAfter;
using Content.Server.Popups; using Content.Server.Popups;
using Content.Shared.Audio; using Content.Shared.Audio;
using Content.Shared.Tools.Components; using Content.Shared.Tools.Components;
using Robust.Server.GameObjects;
using Robust.Shared.Audio; using Robust.Shared.Audio;
using Robust.Shared.Map; using Robust.Shared.Map;
using Robust.Shared.Player; using Robust.Shared.Player;
@@ -23,6 +24,7 @@ namespace Content.Server.Tools
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
public override void Initialize() public override void Initialize()

View File

@@ -40,7 +40,7 @@ public sealed class GasArtifactSystem : EntitySystem
var transform = Transform(uid); var transform = Transform(uid);
var environment = _atmosphereSystem.GetTileMixture(transform.Coordinates, true); var environment = _atmosphereSystem.GetContainingMixture(uid, false, true);
if (environment == null) if (environment == null)
return; return;

View File

@@ -2,12 +2,14 @@
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components; using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events; using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Robust.Server.GameObjects;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems;
public sealed class TemperatureArtifactSystem : EntitySystem public sealed class TemperatureArtifactSystem : EntitySystem
{ {
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -19,14 +21,16 @@ public sealed class TemperatureArtifactSystem : EntitySystem
{ {
var transform = Transform(uid); var transform = Transform(uid);
var center = _atmosphereSystem.GetTileMixture(transform.Coordinates, true); var center = _atmosphereSystem.GetContainingMixture(uid, false, true);
if (center == null) if (center == null)
return; return;
UpdateTileTemperature(component, center); 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) foreach (var mixture in adjacent)
{ {
UpdateTileTemperature(component, mixture); UpdateTileTemperature(component, mixture);

View File

@@ -1,6 +1,7 @@
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Server.Xenoarchaeology.XenoArtifacts.Events; using Content.Server.Xenoarchaeology.XenoArtifacts.Events;
using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
using Robust.Server.GameObjects;
using Robust.Shared.Random; using Robust.Shared.Random;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; 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 IRobustRandom _random = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly ArtifactSystem _artifactSystem = default!; [Dependency] private readonly ArtifactSystem _artifactSystem = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -32,10 +34,14 @@ public sealed class ArtifactGasTriggerSystem : EntitySystem
var query = EntityManager.EntityQuery<ArtifactGasTriggerComponent, TransformComponent>(); var query = EntityManager.EntityQuery<ArtifactGasTriggerComponent, TransformComponent>();
foreach (var (trigger, transform) in query) foreach (var (trigger, transform) in query)
{ {
var uid = trigger.Owner;
if (trigger.ActivationGas == null) if (trigger.ActivationGas == null)
continue; continue;
var environment = _atmosphereSystem.GetTileMixture(transform.Coordinates); var environment = _atmosphereSystem.GetTileMixture(transform.GridUid, transform.MapUid,
_transformSystem.GetGridOrMapTilePosition(uid, transform));
if (environment == null) if (environment == null)
continue; continue;

View File

@@ -3,6 +3,7 @@ using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components;
using Content.Shared.Interaction; using Content.Shared.Interaction;
using Content.Shared.Temperature; using Content.Shared.Temperature;
using Content.Shared.Weapons.Melee; using Content.Shared.Weapons.Melee;
using Robust.Server.GameObjects;
namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; 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 AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly ArtifactSystem _artifactSystem = default!; [Dependency] private readonly ArtifactSystem _artifactSystem = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -25,7 +27,9 @@ public sealed class ArtifactHeatTriggerSystem : EntitySystem
var query = EntityManager.EntityQuery<ArtifactHeatTriggerComponent, TransformComponent, ArtifactComponent>(); var query = EntityManager.EntityQuery<ArtifactHeatTriggerComponent, TransformComponent, ArtifactComponent>();
foreach (var (trigger, transform, artifact) in query) 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) if (environment == null)
continue; continue;

View File

@@ -16,10 +16,11 @@ namespace Content.Shared.Atmos.EntitySystems
public readonly float[] Moles; public readonly float[] Moles;
public readonly AtmosDirection PressureDirection; public readonly AtmosDirection PressureDirection;
public readonly AtmosDirection LastPressureDirection; public readonly AtmosDirection LastPressureDirection;
public readonly bool InExcitedGroup; public readonly int InExcitedGroup;
public readonly AtmosDirection BlockDirection; 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; Temperature = temperature;
Moles = moles; Moles = moles;
@@ -27,6 +28,7 @@ namespace Content.Shared.Atmos.EntitySystems
LastPressureDirection = lastPressureDirection; LastPressureDirection = lastPressureDirection;
InExcitedGroup = inExcited; InExcitedGroup = inExcited;
BlockDirection = blockDirection; BlockDirection = blockDirection;
IsSpace = isSpace;
} }
} }

View File

@@ -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/=Protolathe/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=Pullable/@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/=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/=Ruinable/@EntryIndexedValue">True</s:Boolean>
<s:Boolean x:Key="/Default/UserDictionary/Words/=saltern/@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> <s:Boolean x:Key="/Default/UserDictionary/Words/=sandboxing/@EntryIndexedValue">True</s:Boolean>