From aa9281d6678eaa4148472edfc91ec65c4f967c4b Mon Sep 17 00:00:00 2001 From: Vera Aguilera Puerto <6766154+Zumorica@users.noreply.github.com> Date: Mon, 4 Jul 2022 16:51:34 +0200 Subject: [PATCH] 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 * 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 Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> --- .../Atmos/Overlays/AtmosDebugOverlay.cs | 19 +- .../Atmos/Commands/AddAtmosCommand.cs | 5 +- .../Atmos/Commands/AddGasCommand.cs | 4 +- .../Commands/AddUnsimulatedAtmosCommand.cs | 49 - .../Atmos/Commands/DeleteGasCommand.cs | 4 +- .../Atmos/Commands/FillGasCommand.cs | 4 +- .../Atmos/Commands/RemoveGasCommand.cs | 2 +- .../Commands/SetAtmosTemperatureCommand.cs | 2 +- .../Atmos/Commands/SetTemperatureCommand.cs | 18 +- .../Atmos/Components/GasAnalyzerComponent.cs | 9 +- .../Atmos/Components/GasTankComponent.cs | 4 +- .../Components/GridAtmosphereComponent.cs | 14 +- .../Atmos/Components/IAtmosphereComponent.cs | 10 - .../Components/MapAtmosphereComponent.cs | 21 + .../Components/SpaceAtmosphereComponent.cs | 9 - .../UnsimulatedGridAtmosphereComponent.cs | 10 - .../Atmos/EntitySystems/AirtightSystem.cs | 10 +- .../EntitySystems/AtmosDebugOverlaySystem.cs | 25 +- .../Atmos/EntitySystems/AtmosExposedSystem.cs | 34 +- .../EntitySystems/AtmosphereSystem.API.cs | 301 +++ .../AtmosphereSystem.Commands.cs | 11 +- .../AtmosphereSystem.ExcitedGroup.cs | 2 +- .../EntitySystems/AtmosphereSystem.Gases.cs | 210 +-- .../EntitySystems/AtmosphereSystem.Grid.cs | 1607 ----------------- .../AtmosphereSystem.GridAtmosphere.cs | 549 ++++++ .../EntitySystems/AtmosphereSystem.LINDA.cs | 206 ++- .../EntitySystems/AtmosphereSystem.Map.cs | 31 + .../AtmosphereSystem.Monstermos.cs | 29 +- .../AtmosphereSystem.Processing.cs | 60 +- .../AtmosphereSystem.Superconductivity.cs | 6 +- .../EntitySystems/AtmosphereSystem.Utils.cs | 84 + .../Atmos/EntitySystems/AtmosphereSystem.cs | 138 +- .../EntitySystems/AutomaticAtmosSystem.cs | 3 +- .../Atmos/EntitySystems/BarotraumaSystem.cs | 3 +- .../Atmos/EntitySystems/FlammableSystem.cs | 17 +- .../EntitySystems/GasTileOverlaySystem.cs | 4 +- Content.Server/Atmos/GasMixture.cs | 64 +- Content.Server/Atmos/Miasma/MiasmaSystem.cs | 17 +- .../Monitor/Systems/AtmosMonitoringSystem.cs | 17 +- .../EntitySystems/GasVolumePumpSystem.cs | 11 +- .../Piping/EntitySystems/AtmosDeviceSystem.cs | 15 +- .../AtmosUnsafeUnanchorSystem.cs | 4 +- .../Other/EntitySystems/GasMinerSystem.cs | 13 +- .../Unary/EntitySystems/GasCanisterSystem.cs | 4 +- .../EntitySystems/GasOutletInjectorSystem.cs | 2 +- .../EntitySystems/GasPassiveVentSystem.cs | 2 +- .../Unary/EntitySystems/GasVentPumpSystem.cs | 2 +- .../EntitySystems/GasVentScrubberSystem.cs | 17 +- Content.Server/Atmos/TileAtmosphere.cs | 19 +- .../Body/Systems/RespiratorSystem.cs | 4 +- .../Botany/Components/PlantHolderComponent.cs | 4 +- .../Chemistry/ReagentEffects/CreateGas.cs | 5 +- .../TileReactions/ExtinguishTileReaction.cs | 4 +- .../TileReactions/FlammableTileReaction.cs | 4 +- .../Unit/EntitySystems/DisposableSystem.cs | 2 +- .../Unit/EntitySystems/DisposalUnitSystem.cs | 4 +- .../Doors/Components/FirelockComponent.cs | 33 +- .../Rules/TraitorDeathMatchRuleSystem.cs | 14 +- .../Light/EntitySystems/MatchstickSystem.cs | 13 +- .../NodeContainer/NodeGroups/PipeNet.cs | 11 +- .../Nutrition/EntitySystems/SmokingSystem.cs | 11 +- .../PneumaticCannon/PneumaticCannonSystem.cs | 2 +- Content.Server/RatKing/RatKingSystem.cs | 9 +- .../StationEvents/Events/GasLeak.cs | 4 +- .../StationEvents/Events/StationEvent.cs | 7 +- .../Temperature/Systems/TemperatureSystem.cs | 15 +- Content.Server/Tools/ToolSystem.Welder.cs | 23 +- Content.Server/Tools/ToolSystem.cs | 2 + .../Effects/Systems/GasArtifactSystem.cs | 2 +- .../Systems/TemperatureArtifactSystem.cs | 10 +- .../Systems/ArtifactGasTriggerSystem.cs | 8 +- .../Systems/ArtifactHeatTriggerSystem.cs | 6 +- .../SharedAtmosDebugOverlaySystem.cs | 6 +- SpaceStation14.sln.DotSettings | 1 + 74 files changed, 1764 insertions(+), 2141 deletions(-) delete mode 100644 Content.Server/Atmos/Commands/AddUnsimulatedAtmosCommand.cs delete mode 100644 Content.Server/Atmos/Components/IAtmosphereComponent.cs create mode 100644 Content.Server/Atmos/Components/MapAtmosphereComponent.cs delete mode 100644 Content.Server/Atmos/Components/SpaceAtmosphereComponent.cs delete mode 100644 Content.Server/Atmos/Components/UnsimulatedGridAtmosphereComponent.cs create mode 100644 Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs delete mode 100644 Content.Server/Atmos/EntitySystems/AtmosphereSystem.Grid.cs create mode 100644 Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs create mode 100644 Content.Server/Atmos/EntitySystems/AtmosphereSystem.Map.cs create mode 100644 Content.Server/Atmos/EntitySystems/AtmosphereSystem.Utils.cs diff --git a/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs b/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs index ee8781debb..10e7828381 100644 --- a/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs +++ b/Content.Client/Atmos/Overlays/AtmosDebugOverlay.cs @@ -141,16 +141,27 @@ namespace Content.Client.Atmos.Overlays DrawPressureDirection(drawHandle, data.LastPressureDirection, tile, Color.LightGray); } + var tilePos = new Vector2(tile.X, tile.Y); + // -- Excited Groups -- - if (data.InExcitedGroup) + if (data.InExcitedGroup != 0) { - var tilePos = new Vector2(tile.X, tile.Y); var basisA = tilePos; var basisB = tilePos + new Vector2(1.0f, 1.0f); var basisC = tilePos + new Vector2(0.0f, 1.0f); var basisD = tilePos + new Vector2(1.0f, 0.0f); - drawHandle.DrawLine(basisA, basisB, Color.Cyan); - drawHandle.DrawLine(basisC, basisD, Color.Cyan); + var color = Color.White // Use first three nibbles for an unique color... Good enough? + .WithRed( data.InExcitedGroup & 0x000F) + .WithGreen((data.InExcitedGroup & 0x00F0) >>4) + .WithBlue( (data.InExcitedGroup & 0x0F00) >>8); + drawHandle.DrawLine(basisA, basisB, color); + drawHandle.DrawLine(basisC, basisD, color); + } + + // -- Space Tiles -- + if (data.IsSpace) + { + drawHandle.DrawCircle(tilePos + Vector2.One/2, 0.125f, Color.Orange); } } } diff --git a/Content.Server/Atmos/Commands/AddAtmosCommand.cs b/Content.Server/Atmos/Commands/AddAtmosCommand.cs index 3a86e58d13..6ba58bc0d8 100644 --- a/Content.Server/Atmos/Commands/AddAtmosCommand.cs +++ b/Content.Server/Atmos/Commands/AddAtmosCommand.cs @@ -1,5 +1,6 @@ using Content.Server.Administration; using Content.Server.Atmos.Components; +using Content.Server.Atmos.EntitySystems; using Content.Shared.Administration; using Robust.Shared.Console; @@ -36,7 +37,9 @@ namespace Content.Server.Atmos.Commands return; } - if (_entities.HasComponent(euid)) + var atmos = entMan.EntitySysManager.GetEntitySystem(); + + if (atmos.HasAtmosphere(euid)) { shell.WriteLine("Grid already has an atmosphere."); return; diff --git a/Content.Server/Atmos/Commands/AddGasCommand.cs b/Content.Server/Atmos/Commands/AddGasCommand.cs index ed1c467ff0..e614210740 100644 --- a/Content.Server/Atmos/Commands/AddGasCommand.cs +++ b/Content.Server/Atmos/Commands/AddGasCommand.cs @@ -31,9 +31,9 @@ namespace Content.Server.Atmos.Commands return; } - var atmosphereSystem = EntitySystem.Get(); + var atmosphereSystem = entMan.EntitySysManager.GetEntitySystem(); var indices = new Vector2i(x, y); - var tile = atmosphereSystem.GetTileMixture(euid, indices, true); + var tile = atmosphereSystem.GetTileMixture(euid, null, indices, true); if (tile == null) { diff --git a/Content.Server/Atmos/Commands/AddUnsimulatedAtmosCommand.cs b/Content.Server/Atmos/Commands/AddUnsimulatedAtmosCommand.cs deleted file mode 100644 index bae412a261..0000000000 --- a/Content.Server/Atmos/Commands/AddUnsimulatedAtmosCommand.cs +++ /dev/null @@ -1,49 +0,0 @@ -using Content.Server.Administration; -using Content.Server.Atmos.Components; -using Content.Shared.Administration; -using Robust.Shared.Console; - -namespace Content.Server.Atmos.Commands -{ - [AdminCommand(AdminFlags.Debug)] - public sealed class AddUnsimulatedAtmosCommand : IConsoleCommand - { - public string Command => "addunsimulatedatmos"; - public string Description => "Adds unimulated atmos support to a grid."; - public string Help => $"{Command} "; - - public void Execute(IConsoleShell shell, string argStr, string[] args) - { - if (args.Length < 1) - { - shell.WriteLine(Help); - return; - } - - var entMan = IoCManager.Resolve(); - - if (EntityUid.TryParse(args[0], out var euid)) - { - shell.WriteError($"Failed to parse euid '{args[0]}'."); - return; - } - - if (!entMan.HasComponent(euid)) - { - shell.WriteError($"Euid '{euid}' does not exist or is not a grid."); - return; - } - - if (entMan.HasComponent(euid)) - { - shell.WriteLine("Grid already has an atmosphere."); - return; - } - - entMan.AddComponent(euid); - - shell.WriteLine($"Added unsimulated atmosphere to grid {euid}."); - } - } - -} diff --git a/Content.Server/Atmos/Commands/DeleteGasCommand.cs b/Content.Server/Atmos/Commands/DeleteGasCommand.cs index 01c6a81639..b38b15cec3 100644 --- a/Content.Server/Atmos/Commands/DeleteGasCommand.cs +++ b/Content.Server/Atmos/Commands/DeleteGasCommand.cs @@ -134,7 +134,7 @@ namespace Content.Server.Atmos.Commands if (gas == null) { - foreach (var tile in atmosphereSystem.GetAllTileMixtures(gridId.Value, true)) + foreach (var tile in atmosphereSystem.GetAllMixtures(gridId.Value, true)) { if (tile.Immutable) continue; @@ -146,7 +146,7 @@ namespace Content.Server.Atmos.Commands } else { - foreach (var tile in atmosphereSystem.GetAllTileMixtures(gridId.Value, true)) + foreach (var tile in atmosphereSystem.GetAllMixtures(gridId.Value, true)) { if (tile.Immutable) continue; diff --git a/Content.Server/Atmos/Commands/FillGasCommand.cs b/Content.Server/Atmos/Commands/FillGasCommand.cs index a1b98660f8..aa2690e40d 100644 --- a/Content.Server/Atmos/Commands/FillGasCommand.cs +++ b/Content.Server/Atmos/Commands/FillGasCommand.cs @@ -23,7 +23,7 @@ namespace Content.Server.Atmos.Commands var mapMan = IoCManager.Resolve(); - if (!gridId.IsValid() || !mapMan.TryGetGrid(gridId, out _)) + if (!mapMan.TryGetGrid(gridId, out var grid)) { shell.WriteLine("Invalid grid ID."); return; @@ -31,7 +31,7 @@ namespace Content.Server.Atmos.Commands var atmosphereSystem = EntitySystem.Get(); - foreach (var tile in atmosphereSystem.GetAllTileMixtures(gridId, true)) + foreach (var tile in atmosphereSystem.GetAllMixtures(grid.GridEntityId, true)) { tile.AdjustMoles(gasId, moles); } diff --git a/Content.Server/Atmos/Commands/RemoveGasCommand.cs b/Content.Server/Atmos/Commands/RemoveGasCommand.cs index 09c9af53d6..29b9a1cc76 100644 --- a/Content.Server/Atmos/Commands/RemoveGasCommand.cs +++ b/Content.Server/Atmos/Commands/RemoveGasCommand.cs @@ -24,7 +24,7 @@ namespace Content.Server.Atmos.Commands var atmosphereSystem = EntitySystem.Get(); var indices = new Vector2i(x, y); - var tile = atmosphereSystem.GetTileMixture(id, indices, true); + var tile = atmosphereSystem.GetTileMixture(id, null, indices, true); if (tile == null) { diff --git a/Content.Server/Atmos/Commands/SetAtmosTemperatureCommand.cs b/Content.Server/Atmos/Commands/SetAtmosTemperatureCommand.cs index 5e87f577f4..1fd57dacf9 100644 --- a/Content.Server/Atmos/Commands/SetAtmosTemperatureCommand.cs +++ b/Content.Server/Atmos/Commands/SetAtmosTemperatureCommand.cs @@ -37,7 +37,7 @@ namespace Content.Server.Atmos.Commands var atmosphereSystem = EntitySystem.Get(); var tiles = 0; - foreach (var tile in atmosphereSystem.GetAllTileMixtures(gridId, true)) + foreach (var tile in atmosphereSystem.GetAllMixtures(gridComp.GridEntityId, true)) { tiles++; tile.Temperature = temperature; diff --git a/Content.Server/Atmos/Commands/SetTemperatureCommand.cs b/Content.Server/Atmos/Commands/SetTemperatureCommand.cs index 3367c80294..adfb32b199 100644 --- a/Content.Server/Atmos/Commands/SetTemperatureCommand.cs +++ b/Content.Server/Atmos/Commands/SetTemperatureCommand.cs @@ -2,14 +2,21 @@ using Content.Server.Administration; using Content.Server.Atmos.EntitySystems; using Content.Shared.Administration; using Content.Shared.Atmos; +using Robust.Server.GameObjects; using Robust.Shared.Console; +using Robust.Shared.GameObjects; using Robust.Shared.Map; +using Robust.Shared.Maths; +using SharpZstd.Interop; namespace Content.Server.Atmos.Commands { [AdminCommand(AdminFlags.Debug)] public sealed class SetTemperatureCommand : IConsoleCommand { + [Dependency] private readonly IEntityManager _entities = default!; + [Dependency] private readonly IMapManager _mapManager = default!; + public string Command => "settemp"; public string Description => "Sets a tile's temperature (in kelvin)."; public string Help => "Usage: settemp "; @@ -28,9 +35,16 @@ namespace Content.Server.Atmos.Commands return; } - var atmosphereSystem = EntitySystem.Get(); + if (!_mapManager.TryGetGrid(gridId, out var grid)) + { + shell.WriteError("Invalid grid."); + return; + } + + var atmospheres = _entities.EntitySysManager.GetEntitySystem(); 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) { diff --git a/Content.Server/Atmos/Components/GasAnalyzerComponent.cs b/Content.Server/Atmos/Components/GasAnalyzerComponent.cs index 0c14f51cbf..8d1e1bcdb9 100644 --- a/Content.Server/Atmos/Components/GasAnalyzerComponent.cs +++ b/Content.Server/Atmos/Components/GasAnalyzerComponent.cs @@ -5,6 +5,7 @@ using Content.Server.UserInterface; using Content.Shared.Atmos; using Content.Shared.Atmos.Components; using Content.Shared.Interaction; +using Content.Shared.Maps; using Content.Shared.Popups; using Robust.Server.GameObjects; using Robust.Server.Player; @@ -120,7 +121,7 @@ namespace Content.Server.Atmos.Components { // Already get the pressure before Dirty(), because we can't get the EntitySystem in that thread or smth var pressure = 0f; - var tile = EntitySystem.Get().GetTileMixture(_entities.GetComponent(Owner).Coordinates); + var tile = EntitySystem.Get().GetContainingMixture(Owner, true); if (tile != null) { pressure = tile.Pressure; @@ -178,8 +179,12 @@ namespace Content.Server.Atmos.Components pos = _position.Value; } + var gridUid = pos.GetGridUid(_entities); + var mapUid = pos.GetMapUid(_entities); + var position = pos.ToVector2i(_entities, IoCManager.Resolve()); + var atmosphereSystem = EntitySystem.Get(); - var tile = atmosphereSystem.GetTileMixture(pos); + var tile = atmosphereSystem.GetTileMixture(gridUid, mapUid, position); if (tile == null) { error = "No Atmosphere!"; diff --git a/Content.Server/Atmos/Components/GasTankComponent.cs b/Content.Server/Atmos/Components/GasTankComponent.cs index 53d45f9499..ddacdfd0b4 100644 --- a/Content.Server/Atmos/Components/GasTankComponent.cs +++ b/Content.Server/Atmos/Components/GasTankComponent.cs @@ -261,7 +261,7 @@ namespace Content.Server.Atmos.Components { if (_integrity <= 0) { - var environment = atmosphereSystem.GetTileMixture(_entMan.GetComponent(Owner).Coordinates, true); + var environment = atmosphereSystem.GetContainingMixture(Owner, false, true); if(environment != null) atmosphereSystem.Merge(environment, Air); @@ -279,7 +279,7 @@ namespace Content.Server.Atmos.Components { if (_integrity <= 0) { - var environment = atmosphereSystem.GetTileMixture(_entMan.GetComponent(Owner).Coordinates, true); + var environment = atmosphereSystem.GetContainingMixture(Owner, false, true); if (environment == null) return; diff --git a/Content.Server/Atmos/Components/GridAtmosphereComponent.cs b/Content.Server/Atmos/Components/GridAtmosphereComponent.cs index a7a1728425..45a1c0fcb6 100644 --- a/Content.Server/Atmos/Components/GridAtmosphereComponent.cs +++ b/Content.Server/Atmos/Components/GridAtmosphereComponent.cs @@ -8,12 +8,12 @@ namespace Content.Server.Atmos.Components /// /// Internal Atmos class. Use to interact with atmos instead. /// - [ComponentReference(typeof(IAtmosphereComponent))] - [RegisterComponent, Serializable] - [Virtual] - public class GridAtmosphereComponent : Component, IAtmosphereComponent, ISerializationHooks + [RegisterComponent, Serializable, + Access(typeof(AtmosphereSystem), typeof(GasTileOverlaySystem), typeof(AtmosDebugOverlaySystem))] + public sealed class GridAtmosphereComponent : Component, ISerializationHooks { - public virtual bool Simulated => true; + [ViewVariables(VVAccess.ReadWrite)] + public bool Simulated { get; set; } = true; [ViewVariables] public bool ProcessingPaused { get; set; } = false; @@ -22,7 +22,7 @@ namespace Content.Server.Atmos.Components public float Timer { get; set; } = 0f; [ViewVariables] - public int UpdateCounter { get; set; } = 0; + public int UpdateCounter { get; set; } = 1; // DO NOT SET TO ZERO BY DEFAULT! It will break roundstart atmos... [DataField("uniqueMixes")] public List? UniqueMixes; @@ -94,7 +94,7 @@ namespace Content.Server.Atmos.Components public long EqualizationQueueCycleControl { get; set; } [ViewVariables] - public AtmosphereProcessingState State { get; set; } = AtmosphereProcessingState.TileEqualize; + public AtmosphereProcessingState State { get; set; } = AtmosphereProcessingState.Revalidate; void ISerializationHooks.BeforeSerialization() { diff --git a/Content.Server/Atmos/Components/IAtmosphereComponent.cs b/Content.Server/Atmos/Components/IAtmosphereComponent.cs deleted file mode 100644 index 6fe95e635d..0000000000 --- a/Content.Server/Atmos/Components/IAtmosphereComponent.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Content.Server.Atmos.Components -{ - public interface IAtmosphereComponent : IComponent - { - /// - /// Whether this atmosphere is simulated or not. - /// - bool Simulated { get; } - } -} diff --git a/Content.Server/Atmos/Components/MapAtmosphereComponent.cs b/Content.Server/Atmos/Components/MapAtmosphereComponent.cs new file mode 100644 index 0000000000..09a2c34833 --- /dev/null +++ b/Content.Server/Atmos/Components/MapAtmosphereComponent.cs @@ -0,0 +1,21 @@ +namespace Content.Server.Atmos.Components; + +/// +/// Component that defines the default GasMixture for a map. +/// +/// Honestly, no need to [Friend] this. It's just two simple data fields... Change them to your heart's content. +[RegisterComponent] +public sealed class MapAtmosphereComponent : Component +{ + /// + /// The default GasMixture a map will have. Space mixture by default. + /// + [DataField("mixture"), ViewVariables(VVAccess.ReadWrite)] + public GasMixture? Mixture = GasMixture.SpaceGas; + + /// + /// Whether empty tiles will be considered space or not. + /// + [DataField("space"), ViewVariables(VVAccess.ReadWrite)] + public bool Space = true; +} diff --git a/Content.Server/Atmos/Components/SpaceAtmosphereComponent.cs b/Content.Server/Atmos/Components/SpaceAtmosphereComponent.cs deleted file mode 100644 index d5c3ad07d7..0000000000 --- a/Content.Server/Atmos/Components/SpaceAtmosphereComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -namespace Content.Server.Atmos.Components -{ - [RegisterComponent] - [ComponentReference(typeof(IAtmosphereComponent))] - public sealed class SpaceAtmosphereComponent : Component, IAtmosphereComponent - { - public bool Simulated => false; - } -} diff --git a/Content.Server/Atmos/Components/UnsimulatedGridAtmosphereComponent.cs b/Content.Server/Atmos/Components/UnsimulatedGridAtmosphereComponent.cs deleted file mode 100644 index a5fb02171f..0000000000 --- a/Content.Server/Atmos/Components/UnsimulatedGridAtmosphereComponent.cs +++ /dev/null @@ -1,10 +0,0 @@ -namespace Content.Server.Atmos.Components -{ - [RegisterComponent] - [ComponentReference(typeof(IAtmosphereComponent))] - [Serializable] - public sealed class UnsimulatedGridAtmosphereComponent : GridAtmosphereComponent - { - public override bool Simulated => false; - } -} diff --git a/Content.Server/Atmos/EntitySystems/AirtightSystem.cs b/Content.Server/Atmos/EntitySystems/AirtightSystem.cs index 8ae2049cc0..9133343b82 100644 --- a/Content.Server/Atmos/EntitySystems/AirtightSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AirtightSystem.cs @@ -111,17 +111,19 @@ namespace Content.Server.Atmos.EntitySystems public void InvalidatePosition(EntityUid gridId, Vector2i pos, bool fixVacuum = false) { - if (!gridId.IsValid()) + if (!_mapManager.TryGetGrid(gridId, out var grid)) return; + var gridUid = grid.GridEntityId; + var query = EntityManager.GetEntityQuery(); _explosionSystem.UpdateAirtightMap(gridId, pos, query); // TODO make atmos system use query - _atmosphereSystem.UpdateAdjacent(gridId, pos); - _atmosphereSystem.InvalidateTile(gridId, pos); + _atmosphereSystem.UpdateAdjacent(gridUid, pos); + _atmosphereSystem.InvalidateTile(gridUid, pos); if(fixVacuum) - _atmosphereSystem.FixVacuum(gridId, pos); + _atmosphereSystem.FixTileVacuum(gridUid, pos); } private AtmosDirection Rotate(AtmosDirection myDirection, Angle myAngle) diff --git a/Content.Server/Atmos/EntitySystems/AtmosDebugOverlaySystem.cs b/Content.Server/Atmos/EntitySystems/AtmosDebugOverlaySystem.cs index db302b80b1..174b54f05b 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosDebugOverlaySystem.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosDebugOverlaySystem.cs @@ -92,21 +92,18 @@ namespace Content.Server.Atmos.EntitySystems } } - private AtmosDebugOverlayData ConvertTileToData(TileAtmosphere? tile) + private AtmosDebugOverlayData ConvertTileToData(TileAtmosphere? tile, bool mapIsSpace) { - var gases = new float[Atmospherics.TotalNumberOfGases]; + var gases = new float[Atmospherics.AdjustedNumberOfGases]; if (tile?.Air == null) { - return new AtmosDebugOverlayData(0, gases, AtmosDirection.Invalid, tile?.LastPressureDirection ?? AtmosDirection.Invalid, false, tile?.BlockedAirflow ?? AtmosDirection.Invalid); + return new AtmosDebugOverlayData(Atmospherics.TCMB, gases, AtmosDirection.Invalid, tile?.LastPressureDirection ?? AtmosDirection.Invalid, 0, tile?.BlockedAirflow ?? AtmosDirection.Invalid, tile?.Space ?? mapIsSpace); } else { - for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) - { - gases[i] = tile.Air.GetMoles(i); - } - return new AtmosDebugOverlayData(tile.Air.Temperature, gases, tile.PressureDirection, tile.LastPressureDirection, tile.ExcitedGroup != null, tile.BlockedAirflow); + NumericsHelpers.Add(gases, tile.Air.Moles); + return new AtmosDebugOverlayData(tile.Air.Temperature, gases, tile.PressureDirection, tile.LastPressureDirection, tile.ExcitedGroup?.GetHashCode() ?? 0, tile.BlockedAirflow, tile.Space); } } @@ -132,16 +129,22 @@ namespace Content.Server.Atmos.EntitySystems continue; var transform = EntityManager.GetComponent(entity); + var mapUid = transform.MapUid; + + var mapIsSpace = _atmosphereSystem.IsTileSpace(null, mapUid, Vector2i.Zero); var worldBounds = Box2.CenteredAround(transform.WorldPosition, new Vector2(LocalViewRange, LocalViewRange)); foreach (var grid in _mapManager.FindGridsIntersecting(transform.MapID, worldBounds)) { - if (!EntityManager.EntityExists(grid.GridEntityId)) + var uid = grid.GridEntityId; + + if (!Exists(uid)) continue; - if (!EntityManager.TryGetComponent(grid.GridEntityId, out var gam)) continue; + if (!TryComp(uid, out GridAtmosphereComponent? gridAtmos)) + continue; var entityTile = grid.GetTileRef(transform.Coordinates).GridIndices; var baseTile = new Vector2i(entityTile.X - (LocalViewRange / 2), entityTile.Y - (LocalViewRange / 2)); @@ -153,7 +156,7 @@ namespace Content.Server.Atmos.EntitySystems for (var x = 0; x < LocalViewRange; x++) { var vector = new Vector2i(baseTile.X + x, baseTile.Y + y); - debugOverlayContent[index++] = ConvertTileToData(_atmosphereSystem.GetTileAtmosphereOrCreateSpace(grid, gam, vector)); + debugOverlayContent[index++] = ConvertTileToData(gridAtmos.Tiles.TryGetValue(vector, out var tile) ? tile : null, mapIsSpace); } } diff --git a/Content.Server/Atmos/EntitySystems/AtmosExposedSystem.cs b/Content.Server/Atmos/EntitySystems/AtmosExposedSystem.cs index b827b6d41d..66aa88d407 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosExposedSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosExposedSystem.cs @@ -12,17 +12,47 @@ namespace Content.Server.Atmos.EntitySystems { public readonly EntityCoordinates Coordinates; public readonly GasMixture GasMixture; + public readonly TransformComponent Transform; - public AtmosExposedUpdateEvent(EntityCoordinates coordinates, GasMixture mixture) + public AtmosExposedUpdateEvent(EntityCoordinates coordinates, GasMixture mixture, TransformComponent transform) { Coordinates = coordinates; GasMixture = mixture; + Transform = transform; } } + /// + /// Event that tries to query the mixture a certain entity is exposed to. + /// [ByRefEvent] public struct AtmosExposedGetAirEvent { - public GasMixture? Gas; + /// + /// The entity we want to query this for. + /// + public readonly EntityUid Entity; + + /// + /// The mixture that the entity is exposed to. Output parameter. + /// + public GasMixture? Gas = null; + + /// + /// Whether to invalidate the mixture, if possible. + /// + public bool Invalidate = false; + + /// + /// Whether this event has been handled or not. + /// Check this before changing anything. + /// + public bool Handled = false; + + public AtmosExposedGetAirEvent(EntityUid entity, bool invalidate = false) + { + Entity = entity; + invalidate = invalidate; + } } } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs new file mode 100644 index 0000000000..ee9e57ffda --- /dev/null +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.API.cs @@ -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 GetAllMixtures(EntityUid gridUid, bool excite = false) + { + var ev = new GetAllMixturesMethodEvent(gridUid, excite); + RaiseLocalEvent(gridUid, ref ev); + + if(!ev.Handled) + return Enumerable.Empty(); + + 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 GetAdjacentTiles(EntityUid gridUid, Vector2i tile) + { + var ev = new GetAdjacentTilesMethodEvent(gridUid, tile); + RaiseLocalEvent(gridUid, ref ev); + + return ev.Result ?? Enumerable.Empty(); + } + + public IEnumerable 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(); + } + + 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? 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? Result = null, bool Handled = false); + + [ByRefEvent] private record struct GetAdjacentTileMixturesMethodEvent + (EntityUid Grid, Vector2i Tile, bool IncludeBlocked, bool Excite, + IEnumerable? 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); +} diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs index 002919aff9..9c6a38600b 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Commands.cs @@ -3,6 +3,7 @@ using Content.Server.Administration; using Content.Server.Atmos.Components; using Content.Shared.Administration; using Content.Shared.Atmos; +using Content.Shared.Maps; using Robust.Shared.Console; using Robust.Shared.Map; @@ -83,12 +84,20 @@ public sealed partial class AtmosphereSystem continue; } + var transform = Transform(euid); + foreach (var (indices, tileMain) in gridAtmosphere.Tiles) { var tile = tileMain.Air; if (tile == null) continue; + if (tile.Immutable && !IsTileSpace(euid, transform.MapUid, indices, gridComp)) + { + tile = new GasMixture(tile.Volume) { Temperature = tile.Temperature }; + tileMain.Air = tile; + } + tile.Clear(); var mixtureId = 0; foreach (var entUid in gridComp.Grid.GetAnchoredEntities(indices)) @@ -102,7 +111,7 @@ public sealed partial class AtmosphereSystem Merge(tile, mixture); tile.Temperature = mixture.Temperature; - InvalidateTile(gridAtmosphere, indices); + gridAtmosphere.InvalidatedCoords.Add(indices); } } } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs index 29ba3e9f73..1d809dcd03 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.ExcitedGroup.cs @@ -87,7 +87,7 @@ namespace Content.Server.Atmos.EntitySystems Merge(combined, tile.Air); - if (!ExcitedGroupsSpaceIsAllConsuming || !tile.Air.Immutable) + if (!ExcitedGroupsSpaceIsAllConsuming || !tile.Space) continue; combined.Clear(); diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs index 677ace3e12..be651251f4 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs @@ -48,19 +48,11 @@ namespace Content.Server.Atmos.EntitySystems return GetHeatCapacityCalculation(mixture.Moles, mixture.Immutable); } - /// - /// Calculates the heat capacity for a gas mixture, using the archived values. - /// - public float GetHeatCapacityArchived(GasMixture mixture) - { - return GetHeatCapacityCalculation(mixture.MolesArchived, mixture.Immutable); - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private float GetHeatCapacityCalculation(float[] moles, bool immutable) + private float GetHeatCapacityCalculation(float[] moles, bool space) { // Little hack to make space gas mixtures have heat capacity, therefore allowing them to cool down rooms. - if (immutable && MathHelper.CloseTo(NumericsHelpers.HorizontalAdd(moles), 0f)) + if (space && MathHelper.CloseTo(NumericsHelpers.HorizontalAdd(moles), 0f)) { return Atmospherics.SpaceHeatCapacity; } @@ -136,7 +128,7 @@ namespace Content.Server.Atmos.EntitySystems if (MathF.Abs(receiver.Temperature - source.Temperature) > Atmospherics.MinimumTemperatureDeltaToConsider) { // 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; else { @@ -154,140 +146,6 @@ namespace Content.Server.Atmos.EntitySystems } } - /// - /// Shares gas between two gas mixtures. Part of LINDA. - /// - 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; - - } - - /// - /// Shares temperature between two mixtures, taking a conduction coefficient into account. - /// - 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; - } - - /// - /// Shares temperature between a gas mixture and an abstract sharer, taking a conduction coefficient into account. - /// - 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; - } - /// /// Releases gas from this mixture to the output mixture. /// If the output mixture is null, then this is being released into space. @@ -362,6 +220,62 @@ namespace Content.Server.Atmos.EntitySystems Merge(destination, buffer); } + /// + /// Checks whether a gas mixture is probably safe. + /// This only checks temperature and pressure, not gas composition. + /// + /// Mixture to be checked. + /// Whether the mixture is probably safe. + 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; + } + + /// + /// Compares two gas mixtures to see if they are within acceptable ranges for group processing to be enabled. + /// + 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; + } + /// /// Performs reactions for a given gas mixture on an optional holder. /// @@ -401,5 +315,11 @@ namespace Content.Server.Atmos.EntitySystems return reaction; } + + public enum GasCompareResult + { + NoExchange = -2, + TemperatureExchange = -1, + } } } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Grid.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Grid.cs deleted file mode 100644 index f2aef8d9bc..0000000000 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Grid.cs +++ /dev/null @@ -1,1607 +0,0 @@ -using System.Diagnostics.CodeAnalysis; -using System.Linq; -using System.Runtime.CompilerServices; -using Content.Server.Atmos.Components; -using Content.Server.Atmos.Piping.Components; -using Content.Server.Atmos.Reactions; -using Content.Server.NodeContainer.NodeGroups; -using Content.Shared.Atmos; -using Content.Shared.Maps; -// ReSharper disable once RedundantUsingDirective -using Robust.Shared.Map; -using Robust.Shared.Utility; -using Dependency = Robust.Shared.IoC.DependencyAttribute; - -namespace Content.Server.Atmos.EntitySystems -{ - public sealed partial class AtmosphereSystem - { - [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; - [Dependency] private readonly GasTileOverlaySystem _gasTileOverlaySystem = default!; - - private void InitializeGrid() - { - SubscribeLocalEvent(OnGridAtmosphereInit); - SubscribeLocalEvent(OnGridSplit); - } - - private void OnGridAtmosphereInit(EntityUid uid, GridAtmosphereComponent gridAtmosphere, ComponentInit args) - { - base.Initialize(); - - gridAtmosphere.Tiles.Clear(); - - if (!TryComp(uid, out IMapGridComponent? mapGrid)) - return; - - if (gridAtmosphere.TilesUniqueMixes != null) - { - foreach (var (indices, mix) in gridAtmosphere.TilesUniqueMixes) - { - try - { - gridAtmosphere.Tiles.Add(indices, new TileAtmosphere(mapGrid.Owner, indices, (GasMixture) gridAtmosphere.UniqueMixes![mix].Clone())); - } - catch (ArgumentOutOfRangeException) - { - Logger.Error($"Error during atmos serialization! Tile at {indices} points to an unique mix ({mix}) out of range!"); - throw; - } - - InvalidateTile(gridAtmosphere, indices); - } - } - - GridRepopulateTiles(mapGrid.Grid, gridAtmosphere); - } - - private void OnGridSplit(EntityUid uid, GridAtmosphereComponent originalGridAtmos, ref GridSplitEvent args) - { - foreach (var newGrid in args.NewGrids) - { - // Make extra sure this is a valid grid. - if (!_mapManager.TryGetGrid(newGrid, out var mapGrid)) - continue; - - var entity = mapGrid.GridEntityId; - - // If the new split grid has an atmosphere already somehow, use that. Otherwise, add a new one. - if (!TryComp(entity, out GridAtmosphereComponent? newGridAtmos)) - newGridAtmos = AddComp(entity); - - // We assume the tiles on the new grid have the same coordinates as they did on the old grid... - var enumerator = mapGrid.GetAllTilesEnumerator(); - - while (enumerator.MoveNext(out var tile)) - { - var indices = tile.Value.GridIndices; - - // This split event happens *before* the spaced tiles have been invalidated, therefore we can still - // access their gas data. On the next atmos update tick, these tiles will be spaced. Poof! - if (!originalGridAtmos.Tiles.TryGetValue(indices, out var tileAtmosphere)) - continue; - - // The new grid atmosphere has been initialized, meaning it has all the needed TileAtmospheres... - if (!newGridAtmos.Tiles.TryGetValue(indices, out var newTileAtmosphere)) - // Let's be honest, this is really not gonna happen, but just in case...! - continue; - - // Copy a bunch of data over... Not great, maybe put this in TileAtmosphere? - newTileAtmosphere.Air = tileAtmosphere.Air?.Clone() ?? null; - newTileAtmosphere.Hotspot = tileAtmosphere.Hotspot; - newTileAtmosphere.HeatCapacity = tileAtmosphere.HeatCapacity; - newTileAtmosphere.Temperature = tileAtmosphere.Temperature; - newTileAtmosphere.PressureDifference = tileAtmosphere.PressureDifference; - newTileAtmosphere.PressureDirection = tileAtmosphere.PressureDirection; - - // TODO ATMOS: Somehow force GasTileOverlaySystem to perform an update *right now, right here.* - // The reason why is that right now, gas will flicker until the next GasTileOverlay update. - // That looks bad, of course. We want to avoid that! Anyway that's a bit more complicated so out of scope. - - // Invalidate the tile, it's redundant but redundancy is good! Also HashSet so really, no duplicates. - InvalidateTile(originalGridAtmos, indices); - InvalidateTile(newGridAtmos, indices); - } - } - } - - #region Grid Is Simulated - - /// - /// Returns whether a grid has a simulated atmosphere. - /// - /// Coordinates to be checked. - /// Whether the grid has a simulated atmosphere. - public bool IsSimulatedGrid(EntityCoordinates coordinates) - { - if (TryGetGridAndTile(coordinates, out var tuple)) - return IsSimulatedGrid(tuple.Value.Grid); - - return false; - } - - /// - /// Returns whether a grid has a simulated atmosphere. - /// - /// Grid to be checked. - /// Whether the grid has a simulated atmosphere. - public bool IsSimulatedGrid(EntityUid? grid) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return false; - - if (HasComp(mapGrid.GridEntityId)) - return true; - - return false; - } - - #endregion - - #region Grid Get All Mixtures - - /// - /// Gets all tile mixtures within a grid atmosphere, optionally invalidating them all. - /// - /// Coordinates where to get the grid to get all tile mixtures from. - /// Whether to invalidate all tiles. - /// All tile mixtures in a grid. - public IEnumerable GetAllTileMixtures(EntityCoordinates coordinates, bool invalidate = false) - { - if (TryGetGridAndTile(coordinates, out var tuple)) - return GetAllTileMixtures(tuple.Value.Grid, invalidate); - - return Enumerable.Empty(); - } - - /// - /// Gets all tile mixtures within a grid atmosphere, optionally invalidating them all. - /// - /// Grid where to get all tile mixtures from. - /// Whether to invalidate all tiles. - /// All tile mixtures in a grid. - public IEnumerable GetAllTileMixtures(EntityUid grid, bool invalidate = false) - { - // Return an array with a single space gas mixture for invalid grids. - if (!grid.IsValid()) - return new []{ GasMixture.SpaceGas }; - - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return Enumerable.Empty(); - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - return GetAllTileMixtures(gridAtmosphere, invalidate); - } - - return Enumerable.Empty(); - } - - /// - /// Gets all tile mixtures within a grid atmosphere, optionally invalidating them all. - /// - /// Grid Atmosphere to get all mixtures from. - /// Whether to invalidate all mixtures. - /// All the tile mixtures in a grid. - public IEnumerable GetAllTileMixtures(GridAtmosphereComponent gridAtmosphere, bool invalidate = false) - { - foreach (var (indices, tile) in gridAtmosphere.Tiles) - { - if (tile.Air == null) - continue; - - if (invalidate) - InvalidateTile(gridAtmosphere, indices); - - yield return tile.Air; - } - } - - #endregion - - #region Grid Cell Volume - - /// - /// Gets the volume in liters for a number of tiles, on a specific grid. - /// - /// The grid in question. - /// The amount of tiles. - /// The volume in liters that the tiles occupy. - public float GetVolumeForTiles(EntityUid grid, int tiles = 1) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return Atmospherics.CellVolume * tiles; - - return GetVolumeForTiles(mapGrid, tiles); - - } - - /// - /// Gets the volume in liters for a number of tiles, on a specific grid. - /// - /// The grid in question. - /// The amount of tiles. - /// The volume in liters that the tiles occupy. - public float GetVolumeForTiles(IMapGrid mapGrid, int tiles = 1) - { - return Atmospherics.CellVolume * mapGrid.TileSize * tiles; - - } - - #endregion - - #region Grid Get Obstructing - - /// - /// Gets all obstructing AirtightComponent instances in a specific tile. - /// - /// The grid where to get the tile. - /// The indices of the tile. - /// - public IEnumerable GetObstructingComponents(IMapGrid mapGrid, Vector2i tile) - { - var airQuery = GetEntityQuery(); - var enumerator = mapGrid.GetAnchoredEntitiesEnumerator(tile); - - while (enumerator.MoveNext(out var uid)) - { - if (!airQuery.TryGetComponent(uid.Value, out var airtight)) continue; - yield return airtight; - } - } - - public AtmosObstructionEnumerator GetObstructingComponentsEnumerator(IMapGrid mapGrid, Vector2i tile) - { - var ancEnumerator = mapGrid.GetAnchoredEntitiesEnumerator(tile); - var airQuery = GetEntityQuery(); - - var enumerator = new AtmosObstructionEnumerator(ancEnumerator, airQuery); - return enumerator; - } - - private AtmosDirection GetBlockedDirections(IMapGrid mapGrid, Vector2i indices) - { - var value = AtmosDirection.Invalid; - var enumerator = GetObstructingComponentsEnumerator(mapGrid, indices); - - while (enumerator.MoveNext(out var airtightComponent)) - { - if (airtightComponent.AirBlocked) - value |= airtightComponent.AirBlockedDirection; - } - - return value; - } - - #endregion - - #region Grid Repopulate - - /// - /// Repopulates all tiles on a grid atmosphere. - /// - /// The grid where to get all valid tiles from. - /// The grid atmosphere where the tiles will be repopulated. - public void GridRepopulateTiles(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere) - { - var volume = GetVolumeForTiles(mapGrid, 1); - - foreach (var tile in mapGrid.GetAllTiles()) - { - if(!gridAtmosphere.Tiles.ContainsKey(tile.GridIndices)) - gridAtmosphere.Tiles[tile.GridIndices] = new TileAtmosphere(tile.GridUid, tile.GridIndices, new GasMixture(volume){Temperature = Atmospherics.T20C}); - - InvalidateTile(gridAtmosphere, tile.GridIndices); - } - - foreach (var (position, tile) in gridAtmosphere.Tiles.ToArray()) - { - UpdateAdjacent(mapGrid, gridAtmosphere, tile); - InvalidateVisuals(mapGrid.GridEntityId, position); - } - } - - #endregion - - #region Tile Pry - - /// - /// Pries a tile in a grid. - /// - /// The grid in question. - /// The indices of the tile. - private void PryTile(IMapGrid mapGrid, Vector2i tile) - { - if (!mapGrid.TryGetTileRef(tile, out var tileRef)) - return; - - tileRef.PryTile(_mapManager, _tileDefinitionManager, EntityManager, _robustRandom); - } - - #endregion - - #region Tile Invalidate - - /// - /// Invalidates a tile at a certain position. - /// - /// Coordinates of the tile. - public void InvalidateTile(EntityCoordinates coordinates) - { - if(TryGetGridAndTile(coordinates, out var tuple)) - InvalidateTile(tuple.Value.Grid, tuple.Value.Tile); - } - - /// - /// Invalidates a tile at a certain position. - /// - /// Grid where to invalidate the tile. - /// The indices of the tile. - public void InvalidateTile(EntityUid grid, Vector2i tile) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - InvalidateTile(gridAtmosphere, tile); - return; - } - } - - /// - /// Invalidates a tile at a certain position. - /// - /// Grid Atmosphere where to invalidate the tile. - /// The tile's indices. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void InvalidateTile(GridAtmosphereComponent gridAtmosphere, Vector2i tile) - { - gridAtmosphere.InvalidatedCoords.Add(tile); - } - - #endregion - - #region Tile Invalidate Visuals - - public void InvalidateVisuals(EntityCoordinates coordinates) - { - if(TryGetGridAndTile(coordinates, out var tuple)) - InvalidateVisuals(tuple.Value.Grid, tuple.Value.Tile); - } - - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void InvalidateVisuals(EntityUid grid, Vector2i tile) - { - _gasTileOverlaySystem.Invalidate(grid, tile); - } - - #endregion - - #region Tile Atmosphere Get - - /// - /// Gets the tile atmosphere in a position, or null. - /// - /// Coordinates where to get the tile. - /// Do NOT use this outside of atmos internals. - /// The Tile Atmosphere in the position, or null if not on a grid. - public TileAtmosphere? GetTileAtmosphere(EntityCoordinates coordinates) - { - if (TryGetGridAndTile(coordinates, out var tuple)) - return GetTileAtmosphere(tuple.Value.Grid, tuple.Value.Tile); - - return null; - } - - /// - /// Gets the tile atmosphere in a position, or null. - /// - /// Grid where to get the tile. - /// Indices of the tile. - /// Do NOT use this outside of atmos internals. - /// The Tile Atmosphere in the position, or null. - public TileAtmosphere? GetTileAtmosphere(EntityUid grid, Vector2i tile) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return null; - - if(TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - return GetTileAtmosphere(gridAtmosphere, tile); - } - - return null; - } - - /// - /// Gets the tile atmosphere in a position, or null. - /// - /// Grid atmosphere where to get the tile. - /// Indices of the tile. - /// Do NOT use this outside of atmos internals. - /// The Tile Atmosphere in the position, or null. - public TileAtmosphere? GetTileAtmosphere(GridAtmosphereComponent gridAtmosphere, Vector2i tile) - { - if (gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere)) - return tileAtmosphere; - - return null; - } - - /// - /// Gets the tile atmosphere in a position and if not possible returns a space tile or null. - /// - /// Coordinates of the tile. - /// Do NOT use this outside of atmos internals. - /// The tile atmosphere of a specific position in a grid, a space tile atmosphere if the tile is space or null if not on a grid. - public TileAtmosphere? GetTileAtmosphereOrCreateSpace(EntityCoordinates coordinates) - { - if (TryGetGridAndTile(coordinates, out var tuple)) - return GetTileAtmosphereOrCreateSpace(tuple.Value.Grid, tuple.Value.Tile); - - return null; - } - - /// - /// Gets the tile atmosphere in a position and if not possible returns a space tile or null. - /// - /// Grid where to get the tile. - /// Indices of the tile. - /// Do NOT use this outside of atmos internals. - /// The tile atmosphere of a specific position in a grid, a space tile atmosphere if the tile is space or null if the grid doesn't exist. - public TileAtmosphere? GetTileAtmosphereOrCreateSpace(EntityUid grid, Vector2i tile) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return null; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - return GetTileAtmosphereOrCreateSpace(mapGrid, gridAtmosphere, tile); - } - - return null; - } - - /// - /// Gets the tile atmosphere in a position and if not possible returns a space tile or null. - /// - /// Grid where to get the tile. - /// Grid Atmosphere where to get the tile. - /// Indices of the tile. - /// Do NOT use this outside of atmos internals. - /// The tile atmosphere of a specific position in a grid or a space tile atmosphere if the tile is space. - public TileAtmosphere GetTileAtmosphereOrCreateSpace(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, Vector2i tile) - { - var tileAtmosphere = GetTileAtmosphere(gridAtmosphere, tile); - - // Please note, you might run into a race condition when using this or GetTileAtmosphere. - // The race condition occurs when a tile goes from being space to not-space, and then something - // attempts to get the tile atmosphere for it before it has been revalidated by atmos. - // The tile atmosphere will get revalidated on the next atmos tick, however. - - return tileAtmosphere ?? new TileAtmosphere(mapGrid.GridEntityId, tile, new GasMixture(Atmospherics.CellVolume) {Temperature = Atmospherics.TCMB}, true); - } - - #endregion - - #region Tile Active Add - - /// - /// Makes a tile become active and start processing. - /// - /// Coordinates where to get the tile. - public void AddActiveTile(EntityCoordinates coordinates) - { - if(TryGetGridAndTile(coordinates, out var tuple)) - AddActiveTile(tuple.Value.Grid, tuple.Value.Tile); - } - - /// - /// Makes a tile become active and start processing. - /// - /// Grid where to get the tile. - /// Indices of the tile to be activated. - public void AddActiveTile(EntityUid grid, Vector2i tile) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - AddActiveTile(gridAtmosphere, tile); - return; - } - } - - /// - /// Makes a tile become active and start processing. - /// - /// Grid Atmosphere where to get the tile. - /// Indices of the tile to be activated. - public void AddActiveTile(GridAtmosphereComponent gridAtmosphere, Vector2i tile) - { - if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere)) - return; - - AddActiveTile(gridAtmosphere, tileAtmosphere); - } - - /// - /// Makes a tile become active and start processing. Does NOT check if the tile belongs to the grid atmos. - /// - /// Grid Atmosphere where to get the tile. - /// Tile Atmosphere to be activated. - [MethodImpl(MethodImplOptions.AggressiveInlining)] - private void AddActiveTile(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) - { - if (tile.Air == null) - return; - - tile.Excited = true; - gridAtmosphere.ActiveTiles.Add(tile); - } - - #endregion - - #region Tile Active Remove - - /// - /// Makes a tile become inactive and stop processing. - /// - /// Coordinates where to get the tile. - /// Whether to dispose of the tile's - public void RemoveActiveTile(EntityCoordinates coordinates, bool disposeExcitedGroup = true) - { - if(TryGetGridAndTile(coordinates, out var tuple)) - RemoveActiveTile(tuple.Value.Grid, tuple.Value.Tile, disposeExcitedGroup); - } - - /// - /// Makes a tile become inactive and stop processing. - /// - /// Grid where to get the tile. - /// Indices of the tile to be deactivated. - /// Whether to dispose of the tile's - public void RemoveActiveTile(EntityUid grid, Vector2i tile, bool disposeExcitedGroup = true) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - RemoveActiveTile(gridAtmosphere, tile); - return; - } - } - - /// - /// Makes a tile become inactive and stop processing. - /// - /// Grid Atmosphere where to get the tile. - /// Indices of the tile to be deactivated. - /// Whether to dispose of the tile's - public void RemoveActiveTile(GridAtmosphereComponent gridAtmosphere, Vector2i tile, bool disposeExcitedGroup = true) - { - if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere)) - return; - - RemoveActiveTile(gridAtmosphere, tileAtmosphere, disposeExcitedGroup); - } - - /// - /// Makes a tile become inactive and stop processing. - /// - /// Grid Atmosphere where to get the tile. - /// Tile Atmosphere to be deactivated. - /// Whether to dispose of the tile's - private void RemoveActiveTile(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, bool disposeExcitedGroup = true) - { - tile.Excited = false; - gridAtmosphere.ActiveTiles.Remove(tile); - - if (tile.ExcitedGroup == null) - return; - - if (disposeExcitedGroup) - ExcitedGroupDispose(gridAtmosphere, tile.ExcitedGroup); - else - ExcitedGroupRemoveTile(tile.ExcitedGroup, tile); - } - - #endregion - - #region Tile Mixture - - /// - /// Returns a reference to the gas mixture on a tile, or null. - /// - /// Coordinates where to get the tile. - /// Whether to invalidate the tile. - /// The tile mixture, or null - public GasMixture? GetTileMixture(EntityCoordinates coordinates, bool invalidate = false) - { - return TryGetGridAndTile(coordinates, out var tuple) - ? GetTileMixture(tuple.Value.Grid, tuple.Value.Tile, invalidate) : GasMixture.SpaceGas; - } - - /// - /// Returns a reference to the gas mixture on a tile, or null. - /// - /// Grid where to get the tile air. - /// Indices of the tile. - /// Whether to invalidate the tile. - /// The tile mixture, or null - public GasMixture? GetTileMixture(EntityUid grid, Vector2i tile, bool invalidate = false) - { - // Always return space gas mixtures for invalid grids (grid 0) - if (!grid.IsValid()) - return GasMixture.SpaceGas; - - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return null; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - return GetTileMixture(gridAtmosphere, tile, invalidate); - } - - if (TryComp(mapGrid.GridEntityId, out SpaceAtmosphereComponent? _)) - { - // Always return a new space gas mixture in this case. - return GasMixture.SpaceGas; - } - - return null; - } - - /// - /// Returns a reference to the gas mixture on a tile, or null. - /// - /// Grid Atmosphere where to get the tile air. - /// Indices of the tile. - /// Whether to invalidate the tile. - /// The tile mixture, or null - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GasMixture? GetTileMixture(GridAtmosphereComponent gridAtmosphere, Vector2i tile, bool invalidate = false) - { - if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere)) - return null; - - // Invalidate the tile if needed. - if (invalidate) - InvalidateTile(gridAtmosphere, tile); - - // Return actual tile air or null. - return tileAtmosphere.Air; - } - - #endregion - - #region Tile React - - /// - /// Causes a gas mixture reaction on a specific tile. - /// - /// Coordinates where to get the tile. - /// Reaction results. - public ReactionResult React(EntityCoordinates coordinates) - { - if (TryGetGridAndTile(coordinates, out var tuple)) - return React(tuple.Value.Grid, tuple.Value.Tile); - - return ReactionResult.NoReaction; - } - - /// - /// Causes a gas mixture reaction on a specific tile. - /// - /// Grid where to get the tile. - /// Indices of the tile. - /// Reaction results. - public ReactionResult React(EntityUid grid, Vector2i tile) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return ReactionResult.NoReaction; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - return React(gridAtmosphere, tile); - } - - return ReactionResult.NoReaction; - } - - /// - /// Causes a gas mixture reaction on a specific tile. - /// - /// Grid Atmosphere where to get the tile. - /// Indices of the tile. - /// Reaction results. - public ReactionResult React(GridAtmosphereComponent gridAtmosphere, Vector2i tile) - { - if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere) || tileAtmosphere.Air == null) - return ReactionResult.NoReaction; - - InvalidateTile(gridAtmosphere, tile); - - return React(tileAtmosphere.Air, tileAtmosphere); - } - - #endregion - - #region Tile Air-blocked - - /// - /// Returns if the tile in question is "air-blocked" in a certain direction or not. - /// This could be due to a number of reasons, such as walls, doors, etc. - /// - /// Coordinates where to get the tile. - /// Directions to check. - /// Whether the tile is blocked in the directions specified. - public bool IsTileAirBlocked(EntityCoordinates coordinates, AtmosDirection direction = AtmosDirection.All) - { - if (TryGetGridAndTile(coordinates, out var tuple)) - return IsTileAirBlocked(tuple.Value.Grid, tuple.Value.Tile, direction); - - return false; - } - - /// - /// Returns if the tile in question is "air-blocked" in a certain direction or not. - /// This could be due to a number of reasons, such as walls, doors, etc. - /// - /// Grid where to get the tile. - /// Indices of the tile. - /// Directions to check. - /// Whether the tile is blocked in the directions specified. - public bool IsTileAirBlocked(EntityUid grid, Vector2i tile, AtmosDirection direction = AtmosDirection.All) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return false; - - return IsTileAirBlocked(mapGrid, tile, direction); - } - - /// - /// Returns if the tile in question is "air-blocked" in a certain direction or not. - /// This could be due to a number of reasons, such as walls, doors, etc. - /// - /// Grid where to get the tile. - /// Indices of the tile. - /// Directions to check. - /// Whether the tile is blocked in the directions specified. - public bool IsTileAirBlocked(IMapGrid mapGrid, Vector2i tile, AtmosDirection direction = AtmosDirection.All) - { - var directions = AtmosDirection.Invalid; - - var enumerator = GetObstructingComponentsEnumerator(mapGrid, tile); - - while (enumerator.MoveNext(out var obstructingComponent)) - { - if (!obstructingComponent.AirBlocked) - continue; - - // We set the directions that are air-blocked so far, - // as you could have a full obstruction with only 4 directional air blockers. - directions |= obstructingComponent.AirBlockedDirection; - - if (directions.IsFlagSet(direction)) - return true; - } - - return false; - } - - #endregion - - #region Tile Space - - /// - /// Returns whether the specified tile is a space tile or not. - /// - /// Coordinates where to check the tile. - /// Whether the tile is space or not. - public bool IsTileSpace(EntityCoordinates coordinates) - { - if (TryGetGridAndTile(coordinates, out var tuple)) - return IsTileSpace(tuple.Value.Grid, tuple.Value.Tile); - - return true; - } - - /// - /// Returns whether the specified tile is a space tile or not. - /// - /// Grid where to check the tile. - /// Indices of the tile. - /// Whether the tile is space or not. - public bool IsTileSpace(EntityUid grid, Vector2i tile) - { - return !_mapManager.TryGetGrid(grid, out var mapGrid) || IsTileSpace(mapGrid, tile); - } - - public bool IsTileSpace(IMapGrid mapGrid, Vector2i tile) - { - if (!mapGrid.TryGetTileRef(tile, out var tileRef)) - return true; - - return ((ContentTileDefinition) _tileDefinitionManager[tileRef.Tile.TypeId]).IsSpace; - } - - #endregion - - #region Tile Get Heat Capacity - - /// - /// Get a tile's heat capacity, based on the tile type, tile contents and tile gas mixture. - /// - public float GetTileHeatCapacity(EntityCoordinates coordinates) - { - if (TryGetGridAndTile(coordinates, out var tuple)) - return GetTileHeatCapacity(tuple.Value.Grid, tuple.Value.Tile); - - return Atmospherics.MinimumHeatCapacity; - } - - /// - /// Get a tile's heat capacity, based on the tile type, tile contents and tile gas mixture. - /// - public float GetTileHeatCapacity(EntityUid grid, Vector2i tile) - { - // Always return space gas mixtures for invalid grids (grid 0) - if (!grid.IsValid()) - return Atmospherics.MinimumHeatCapacity; - - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return Atmospherics.MinimumHeatCapacity; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - return GetTileHeatCapacity(gridAtmosphere, tile); - } - - if (TryComp(mapGrid.GridEntityId, out SpaceAtmosphereComponent? _)) - { - return Atmospherics.SpaceHeatCapacity; - } - - return Atmospherics.MinimumHeatCapacity; - } - - /// - /// Get a tile's heat capacity, based on the tile type, tile contents and tile gas mixture. - /// - public float GetTileHeatCapacity(GridAtmosphereComponent gridAtmosphere, Vector2i tile) - { - if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere)) - return Atmospherics.MinimumHeatCapacity; - - return GetTileHeatCapacity(tileAtmosphere); - } - - /// - /// Get a tile's heat capacity, based on the tile type, tile contents and tile gas mixture. - /// - public float GetTileHeatCapacity(TileAtmosphere tile) - { - return tile.HeatCapacity + (tile.Air == null ? 0 : GetHeatCapacity(tile.Air)); - } - - #endregion - - #region Adjacent Get Positions - - /// - /// Gets all the positions adjacent to a tile. Can include air-blocked directions. - /// - /// Coordinates where to get the tile. - /// Whether to include tiles in directions the tile is air-blocked in. - /// The positions adjacent to the tile. - public IEnumerable GetAdjacentTiles(EntityCoordinates coordinates, bool includeBlocked = false) - { - if (TryGetGridAndTile(coordinates, out var tuple)) - return GetAdjacentTiles(tuple.Value.Grid, tuple.Value.Tile, includeBlocked); - - return Enumerable.Empty(); - } - - /// - /// Gets all the positions adjacent to a tile. Can include air-blocked directions. - /// - /// Grid where to get the tiles. - /// Indices of the tile. - /// Whether to include tiles in directions the tile is air-blocked in. - /// The positions adjacent to the tile. - public IEnumerable GetAdjacentTiles(EntityUid grid, Vector2i tile, bool includeBlocked = false) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return Enumerable.Empty(); - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - return GetAdjacentTiles(gridAtmosphere, tile, includeBlocked); - } - - return Enumerable.Empty(); - } - - /// - /// Gets all the positions adjacent to a tile. Can include air-blocked directions. - /// - /// Grid Atmosphere where to get the tiles. - /// Indices of the tile. - /// Whether to include tiles in directions the tile is air-blocked in. - /// The positions adjacent to the tile. - public IEnumerable GetAdjacentTiles(GridAtmosphereComponent gridAtmosphere, Vector2i tile, bool includeBlocked = false) - { - if(!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere)) - yield break; - - for (var i = 0; i < tileAtmosphere.AdjacentTiles.Length; i++) - { - var adjacentTile = tileAtmosphere.AdjacentTiles[i]; - // TileAtmosphere has nullable disabled, so just in case... - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (adjacentTile?.Air == null) - continue; - - if (!includeBlocked) - { - var direction = (AtmosDirection) (1 << i); - if (tileAtmosphere.BlockedAirflow.IsFlagSet(direction)) - continue; - } - - yield return adjacentTile.GridIndices; - } - } - - #endregion - - #region Adjacent Get Mixture - - /// - /// Gets all tile gas mixtures adjacent to a specific tile, and optionally invalidates them. - /// Does not return the tile in question, only the adjacent ones. - /// - /// Coordinates where to get the tile. - /// Whether to include tiles in directions the tile is air-blocked in. - /// Whether to invalidate all adjacent tiles. - /// All adjacent tile gas mixtures to the tile in question - public IEnumerable GetAdjacentTileMixtures(EntityCoordinates coordinates, bool includeBlocked = false, bool invalidate = false) - { - if (TryGetGridAndTile(coordinates, out var tuple)) - return GetAdjacentTileMixtures(tuple.Value.Grid, tuple.Value.Tile, includeBlocked, invalidate); - - return Enumerable.Empty(); - } - - /// - /// Gets all tile gas mixtures adjacent to a specific tile, and optionally invalidates them. - /// Does not return the tile in question, only the adjacent ones. - /// - /// Grid where to get the tile. - /// Indices of the tile. - /// Whether to include tiles in directions the tile is air-blocked in. - /// Whether to invalidate all adjacent tiles. - /// All adjacent tile gas mixtures to the tile in question - public IEnumerable GetAdjacentTileMixtures(EntityUid grid, Vector2i tile, bool includeBlocked = false, bool invalidate = false) - { - // For invalid grids, return an array with a single space gas mixture in it. - if (!grid.IsValid()) - return new []{ GasMixture.SpaceGas }; - - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return Enumerable.Empty(); - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - return GetAdjacentTileMixtures(gridAtmosphere, tile, includeBlocked, invalidate); - } - - return Enumerable.Empty(); - } - - /// - /// Gets all tile gas mixtures adjacent to a specific tile, and optionally invalidates them. - /// Does not return the tile in question, only the adjacent ones. - /// - /// Grid Atmosphere where to get the tile. - /// Indices of the tile. - /// Whether to include tiles in directions the tile is air-blocked in. - /// Whether to invalidate all adjacent tiles. - /// All adjacent tile gas mixtures to the tile in question - public IEnumerable GetAdjacentTileMixtures(GridAtmosphereComponent gridAtmosphere, Vector2i tile, bool includeBlocked = false, bool invalidate = false) - { - if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere)) - return Enumerable.Empty(); - - return GetAdjacentTileMixtures(gridAtmosphere, tileAtmosphere, includeBlocked, invalidate); - } - - /// - /// Gets all tile gas mixtures adjacent to a specific tile, and optionally invalidates them. - /// Does not return the tile in question, only the adjacent ones. - /// - /// Grid Atmosphere where the tile is. - /// Tile Atmosphere in question. - /// Whether to include tiles in directions the tile is air-blocked in. - /// Whether to invalidate all adjacent tiles. - /// All adjacent tile gas mixtures to the tile in question - private IEnumerable GetAdjacentTileMixtures(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, bool includeBlocked = false, bool invalidate = false) - { - for (var i = 0; i < tile.AdjacentTiles.Length; i++) - { - var adjacentTile = tile.AdjacentTiles[i]; - - // TileAtmosphere has nullable disabled, so just in case... - // ReSharper disable once ConditionIsAlwaysTrueOrFalse - if (adjacentTile?.Air == null) - continue; - - if (!includeBlocked) - { - var direction = (AtmosDirection) (1 << i); - if (tile.BlockedAirflow.IsFlagSet(direction)) - continue; - } - - if (invalidate) - InvalidateTile(gridAtmosphere, adjacentTile.GridIndices); - - yield return adjacentTile.Air; - } - } - - #endregion - - #region Adjacent Update - - /// - /// Immediately updates a tile's blocked air directions. - /// - /// Coordinates where to get the tile. - public void UpdateAdjacent(EntityCoordinates coordinates) - { - if(TryGetGridAndTile(coordinates, out var tuple)) - UpdateAdjacent(tuple.Value.Grid, tuple.Value.Tile); - } - - /// - /// Immediately updates a tile's blocked air directions. - /// - /// Grid where to get the tile. - /// Indices of the tile. - public void UpdateAdjacent(EntityUid grid, Vector2i tile) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - UpdateAdjacent(mapGrid, gridAtmosphere, tile); - return; - } - } - - /// - /// Immediately updates a tile's blocked air directions. - /// - /// Grid where to get the tile. - /// Grid Atmosphere where to get the tile. - /// Indices of the tile. - public void UpdateAdjacent(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, Vector2i tile) - { - if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere)) - return; - - UpdateAdjacent(mapGrid, gridAtmosphere, tileAtmosphere); - } - - /// - /// Immediately updates a tile's blocked air directions. - /// - /// Grid where to get the tile. - /// Grid Atmosphere of the tile. - /// Tile Atmosphere to be updated. - private void UpdateAdjacent(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tileAtmosphere) - { - tileAtmosphere.AdjacentBits = AtmosDirection.Invalid; - tileAtmosphere.BlockedAirflow = GetBlockedDirections(mapGrid, tileAtmosphere.GridIndices); - - for (var i = 0; i < Atmospherics.Directions; i++) - { - var direction = (AtmosDirection) (1 << i); - - var otherIndices = tileAtmosphere.GridIndices.Offset(direction); - - var adjacent = GetTileAtmosphereOrCreateSpace(mapGrid, gridAtmosphere, otherIndices); - tileAtmosphere.AdjacentTiles[direction.ToIndex()] = adjacent; - - UpdateAdjacent(mapGrid, gridAtmosphere, adjacent, direction.GetOpposite()); - - if (!tileAtmosphere.BlockedAirflow.IsFlagSet(direction) - && !IsTileAirBlocked(mapGrid, adjacent.GridIndices, direction.GetOpposite())) - { - tileAtmosphere.AdjacentBits |= direction; - } - } - - if (!tileAtmosphere.AdjacentBits.IsFlagSet(tileAtmosphere.MonstermosInfo.CurrentTransferDirection)) - tileAtmosphere.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid; - } - - /// - /// Immediately updates a tile's single blocked air direction. - /// - /// Coordinates where to get the tile. - /// Direction to be updated. - public void UpdateAdjacent(EntityCoordinates coordinates, AtmosDirection direction) - { - if(TryGetGridAndTile(coordinates, out var tuple)) - UpdateAdjacent(tuple.Value.Grid, tuple.Value.Tile, direction); - } - - /// - /// Immediately updates a tile's single blocked air direction. - /// - /// Grid where to get the tile. - /// Indices of the tile. - /// Direction to be updated. - public void UpdateAdjacent(EntityUid grid, Vector2i tile, AtmosDirection direction) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - UpdateAdjacent(mapGrid, gridAtmosphere, tile, direction); - return; - } - } - - /// - /// Immediately updates a tile's single blocked air direction. - /// - /// Grid where to get the tile. - /// Grid Atmosphere where to get the tile. - /// Indices of the tile. - /// Direction to be updated. - public void UpdateAdjacent(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, Vector2i tile, AtmosDirection direction) - { - if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere)) - return; - - UpdateAdjacent(mapGrid, gridAtmosphere, tileAtmosphere, direction); - } - - /// - /// Immediately updates a tile's single blocked air direction. - /// - /// Grid where to get the tile. - /// Grid where to get the tile. - /// Tile Atmosphere to be updated. - /// Direction to be updated. - private void UpdateAdjacent(IMapGrid mapGrid, GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, AtmosDirection direction) - { - tile.AdjacentTiles[direction.ToIndex()] = GetTileAtmosphereOrCreateSpace(mapGrid, gridAtmosphere, tile.GridIndices.Offset(direction)); - - if (!tile.BlockedAirflow.IsFlagSet(direction) && !IsTileAirBlocked(mapGrid, tile.GridIndices.Offset(direction), direction.GetOpposite())) - { - tile.AdjacentBits |= direction; - } - else - { - tile.AdjacentBits &= ~direction; - } - - if (!tile.AdjacentBits.IsFlagSet(tile.MonstermosInfo.CurrentTransferDirection)) - tile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid; - } - - #endregion - - #region Hotspot Expose - - /// - /// Exposes temperature to a tile, creating a hotspot (fire) if the conditions are ideal. - /// Can also be used to make an existing hotspot hotter/bigger. Also invalidates the tile. - /// - /// Coordinates where to get the tile. - /// Temperature to expose to the tile. - /// Volume of the exposed temperature. - /// If true, the existing hotspot values will be set to the exposed values, but only if they're smaller. - public void HotspotExpose(EntityCoordinates coordinates, float exposedTemperature, float exposedVolume, bool soh = false) - { - if(TryGetGridAndTile(coordinates, out var tuple)) - HotspotExpose(tuple.Value.Grid, tuple.Value.Tile, exposedTemperature, exposedVolume, soh); - } - - /// - /// Exposes temperature to a tile, creating a hotspot (fire) if the conditions are ideal. - /// Can also be used to make an existing hotspot hotter/bigger. Also invalidates the tile. - /// - /// Grid where to get the tile. - /// Indices of the tile. - /// Temperature to expose to the tile. - /// Volume of the exposed temperature. - /// If true, the existing hotspot values will be set to the exposed values, but only if they're smaller. - public void HotspotExpose(EntityUid grid, Vector2i tile, float exposedTemperature, float exposedVolume, bool soh = false) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - var tileAtmosphere = GetTileAtmosphere(gridAtmosphere, tile); - - if (tileAtmosphere == null) - return; - - HotspotExpose(gridAtmosphere, tileAtmosphere, exposedTemperature, exposedVolume, soh); - InvalidateTile(gridAtmosphere, tile); - return; - } - } - - #endregion - - #region Hotspot Extinguish - - /// - /// Extinguishes a hotspot (fire) on a certain tile, if any. Also invalidates the tile. - /// - /// Coordinates where to get the tile. - public void HotspotExtinguish(EntityCoordinates coordinates) - { - if(TryGetGridAndTile(coordinates, out var tuple)) - HotspotExtinguish(tuple.Value.Grid, tuple.Value.Tile); - } - - /// - /// Extinguishes a hotspot (fire) on a certain tile, if any. Also invalidates the tile. - /// - /// Grid where to get the tile. - /// Indices of the tile. - public void HotspotExtinguish(EntityUid grid, Vector2i tile) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - HotspotExtinguish(gridAtmosphere, tile); - return; - } - } - - /// - /// Extinguishes a hotspot (fire) on a certain tile, if any. Also invalidates the tile. - /// - /// Grid Atmosphere where to get the tile. - /// Indices of the tile. - public void HotspotExtinguish(GridAtmosphereComponent gridAtmosphere, Vector2i tile) - { - if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere)) - return; - - tileAtmosphere.Hotspot = new Hotspot(); - InvalidateTile(gridAtmosphere, tile); - } - - #endregion - - #region Hotspot Active - - /// - /// Returns whether there's an active hotspot (fire) on a certain tile. - /// - /// Position where to get the tile. - /// Whether the hotspot is active or not. - public bool IsHotspotActive(EntityCoordinates coordinates) - { - if (TryGetGridAndTile(coordinates, out var tuple)) - return IsHotspotActive(tuple.Value.Grid, tuple.Value.Tile); - - return false; - } - - /// - /// Returns whether there's an active hotspot (fire) on a certain tile. - /// - /// Grid where to get the tile - /// Indices for the tile - /// Whether the hotspot is active or not. - public bool IsHotspotActive(EntityUid grid, Vector2i tile) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return false; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - return IsHotspotActive(gridAtmosphere, tile); - } - - return false; - } - - /// - /// Returns whether there's an active hotspot (fire) on a certain tile. - /// - /// Grid Atmosphere where to get the tile - /// Indices for the tile - /// Whether the hotspot is active or not. - public bool IsHotspotActive(GridAtmosphereComponent gridAtmosphere, Vector2i tile) - { - if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere)) - return false; - - return tileAtmosphere.Hotspot.Valid; - } - - #endregion - - #region PipeNet Add - - public void AddPipeNet(PipeNet pipeNet) - { - if (!_mapManager.TryGetGrid(pipeNet.Grid, out var mapGrid)) - return; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - gridAtmosphere.PipeNets.Add(pipeNet); - } - } - - #endregion - - #region PipeNet Remove - - public void RemovePipeNet(PipeNet pipeNet) - { - if (!_mapManager.TryGetGrid(pipeNet.Grid, out var mapGrid)) - return; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - gridAtmosphere.PipeNets.Remove(pipeNet); - } - } - - #endregion - - #region AtmosDevice Add - - public bool AddAtmosDevice(AtmosDeviceComponent atmosDevice) - { - var grid = Comp(atmosDevice.Owner).GridUid; - - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return false; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - atmosDevice.JoinedGrid = grid; - gridAtmosphere.AtmosDevices.Add(atmosDevice); - return true; - } - - return false; - } - - #endregion - - #region AtmosDevice Remove - - public bool RemoveAtmosDevice(AtmosDeviceComponent atmosDevice) - { - if (atmosDevice.JoinedGrid == null) - return false; - - var grid = atmosDevice.JoinedGrid.Value; - - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return false; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere) - && gridAtmosphere.AtmosDevices.Contains(atmosDevice)) - { - atmosDevice.JoinedGrid = null; - gridAtmosphere.AtmosDevices.Remove(atmosDevice); - return true; - } - - return false; - } - - #endregion - - #region Mixture Safety - - /// - /// Checks whether a tile's gas mixture is probably safe. - /// This only checks temperature and pressure, not gas composition. - /// - /// Coordinates where to get the tile. - /// Whether the tile's gas mixture is probably safe. - public bool IsTileMixtureProbablySafe(EntityCoordinates coordinates) - { - return IsMixtureProbablySafe(GetTileMixture(coordinates)); - } - - /// - /// Checks whether a tile's gas mixture is probably safe. - /// This only checks temperature and pressure, not gas composition. - /// - /// Grid where to get the tile. - /// Indices of the tile. - /// Whether the tile's gas mixture is probably safe. - public bool IsTileMixtureProbablySafe(EntityUid grid, Vector2i tile) - { - return IsMixtureProbablySafe(GetTileMixture(grid, tile)); - } - - /// - /// Checks whether a gas mixture is probably safe. - /// This only checks temperature and pressure, not gas composition. - /// - /// Mixture to be checked. - /// Whether the mixture is probably safe. - public bool IsMixtureProbablySafe(GasMixture? air) - { - // Note that oxygen mix isn't checked, but survival boxes make that not necessary. - if (air == null) - return false; - - switch (air.Pressure) - { - case <= Atmospherics.WarningLowPressure: - case >= Atmospherics.WarningHighPressure: - return false; - } - - switch (air.Temperature) - { - case <= 260: - case >= 360: - return false; - } - - return true; - } - - #endregion - - #region Fix Vacuum - - /// - /// Attempts to fix a sudden vacuum by creating gas based on adjacent tiles. - /// - /// Coordinates where to get the tile. - public void FixVacuum(EntityCoordinates coordinates) - { - if(TryGetGridAndTile(coordinates, out var tuple)) - FixVacuum(tuple.Value.Grid, tuple.Value.Tile); - } - - /// - /// Attempts to fix a sudden vacuum by creating gas based on adjacent tiles. - /// - /// Grid where to get the tile. - /// Indices of the tile. - public void FixVacuum(EntityUid grid, Vector2i tile) - { - if (!_mapManager.TryGetGrid(grid, out var mapGrid)) - return; - - if (TryComp(mapGrid.GridEntityId, out GridAtmosphereComponent? gridAtmosphere)) - { - FixVacuum(gridAtmosphere, tile); - return; - } - } - - public void FixVacuum(GridAtmosphereComponent gridAtmosphere, Vector2i tile) - { - if (!gridAtmosphere.Tiles.TryGetValue(tile, out var tileAtmosphere)) - return; - - var adjacent = GetAdjacentTileMixtures(gridAtmosphere, tileAtmosphere, false, true).ToArray(); - tileAtmosphere.Air = new GasMixture(GetVolumeForTiles(tileAtmosphere.GridIndex, 1)) - {Temperature = Atmospherics.T20C}; - - // Return early, let's not cause any funny NaNs. - if (adjacent.Length == 0) - return; - - var ratio = 1f / adjacent.Length; - var totalTemperature = 0f; - - foreach (var adj in adjacent) - { - totalTemperature += adj.Temperature; - - // Remove a bit of gas from the adjacent ratio... - var mix = adj.RemoveRatio(ratio); - - // And merge it to the new tile air. - Merge(tileAtmosphere.Air, mix); - - // Return removed gas to its original mixture. - Merge(adj, mix); - } - - // New temperature is the arithmetic mean of the sum of the adjacent temperatures... - tileAtmosphere.Air.Temperature = totalTemperature / adjacent.Length; - } - - public bool NeedsVacuumFixing(IMapGrid mapGrid, Vector2i indices) - { - var value = false; - - foreach (var airtightComponent in GetObstructingComponents(mapGrid, indices)) - { - value |= airtightComponent.FixVacuum; - } - - return value; - } - - #endregion - - #region Position Helpers - - private TileRef? GetTile(TileAtmosphere tile) - { - return tile.GridIndices.GetTileRef(tile.GridIndex, _mapManager); - } - - public bool TryGetGridAndTile(MapCoordinates coordinates, [NotNullWhen(true)] out (EntityUid Grid, Vector2i Tile)? tuple) - { - if (!_mapManager.TryFindGridAt(coordinates, out var grid)) - { - tuple = null; - return false; - } - - tuple = (grid.GridEntityId, grid.TileIndicesFor(coordinates)); - return true; - } - - public bool TryGetGridAndTile(EntityCoordinates coordinates, [NotNullWhen(true)] out (EntityUid Grid, Vector2i Tile)? tuple) - { - if (!coordinates.IsValid(EntityManager)) - { - tuple = null; - return false; - } - - var gridId = coordinates.GetGridUid(EntityManager); - - if (!_mapManager.TryGetGrid(gridId, out var grid)) - { - tuple = null; - return false; - } - - tuple = (gridId.Value, grid.TileIndicesFor(coordinates)); - return true; - } - - public bool TryGetMapGrid(GridAtmosphereComponent gridAtmosphere, [NotNullWhen(true)] out IMapGrid? mapGrid) - { - if (TryComp(gridAtmosphere.Owner, out IMapGridComponent? mapGridComponent)) - { - mapGrid = mapGridComponent.Grid; - return true; - } - - mapGrid = null; - return false; - } - - #endregion - } -} diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs new file mode 100644 index 0000000000..69d42186e5 --- /dev/null +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.GridAtmosphere.cs @@ -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(OnGridAtmosphereInit); + SubscribeLocalEvent(OnGridSplit); + + #region Atmos API Subscriptions + + SubscribeLocalEvent(GridHasAtmosphere); + SubscribeLocalEvent(GridIsSimulated); + SubscribeLocalEvent(GridGetAllMixtures); + SubscribeLocalEvent(GridInvalidateTile); + SubscribeLocalEvent(GridGetTileMixture); + SubscribeLocalEvent(GridReactTile); + SubscribeLocalEvent(GridIsTileAirBlocked); + SubscribeLocalEvent(GridIsTileSpace); + SubscribeLocalEvent(GridGetAdjacentTiles); + SubscribeLocalEvent(GridGetAdjacentTileMixtures); + SubscribeLocalEvent(GridUpdateAdjacent); + SubscribeLocalEvent(GridHotspotExpose); + SubscribeLocalEvent(GridHotspotExtinguish); + SubscribeLocalEvent(GridIsHotspotActive); + SubscribeLocalEvent(GridFixTileVacuum); + SubscribeLocalEvent(GridAddPipeNet); + SubscribeLocalEvent(GridRemovePipeNet); + SubscribeLocalEvent(GridAddAtmosDevice); + SubscribeLocalEvent(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(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 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 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 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(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; + } + + /// + /// Repopulates all tiles on a grid atmosphere. + /// + /// The grid where to get all valid tiles from. + /// The grid atmosphere where the tiles will be repopulated. + 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); + } + } +} diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs index e6b76bb9a1..165fb06c2d 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.LINDA.cs @@ -48,7 +48,7 @@ namespace Content.Server.Atmos.EntitySystems } shouldShareAir = true; - } else if (tile.Air!.Compare(enemyTile.Air!) != GasMixture.GasCompareResult.NoExchange) + } else if (CompareExchange(tile.Air, enemyTile.Air) != GasCompareResult.NoExchange) { if (!enemyTile.Excited) { @@ -78,7 +78,7 @@ namespace Content.Server.Atmos.EntitySystems if (shouldShareAir) { - var difference = Share(tile.Air!, enemyTile.Air!, adjacentTileLength); + var difference = Share(tile, enemyTile, adjacentTileLength); // Monstermos already handles this, so let's not handle it ourselves. if (!MonstermosEqualization) @@ -114,9 +114,17 @@ namespace Content.Server.Atmos.EntitySystems 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.TemperatureArchived = tile.Temperature; } private void LastShareCheck(TileAtmosphere tile) @@ -124,7 +132,7 @@ namespace Content.Server.Atmos.EntitySystems if (tile.Air == null || tile.ExcitedGroup == null) return; - switch (tile.Air.LastShare) + switch (tile.LastShare) { case > Atmospherics.MinimumAirToSuspend: ExcitedGroupResetCooldowns(tile.ExcitedGroup); @@ -134,5 +142,193 @@ namespace Content.Server.Atmos.EntitySystems break; } } + + /// + /// Makes a tile become active and start processing. Does NOT check if the tile belongs to the grid atmos. + /// + /// Grid Atmosphere where to get the tile. + /// Tile Atmosphere to be activated. + private void AddActiveTile(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile) + { + if (tile.Air == null) + return; + + tile.Excited = true; + gridAtmosphere.ActiveTiles.Add(tile); + } + + /// + /// Makes a tile become inactive and stop processing. + /// + /// Grid Atmosphere where to get the tile. + /// Tile Atmosphere to be deactivated. + /// Whether to dispose of the tile's + 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); + } + + /// + /// Calculates the heat capacity for a gas mixture, using the archived values. + /// + 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); + } + + /// + /// Shares gas between two tiles. Part of LINDA. + /// + 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; + } + + /// + /// Shares temperature between two mixtures, taking a conduction coefficient into account. + /// + 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; + } + + /// + /// Shares temperature between a gas mixture and an abstract sharer, taking a conduction coefficient into account. + /// + 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; + } } } diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Map.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Map.cs new file mode 100644 index 0000000000..e4dd4b5154 --- /dev/null +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Map.cs @@ -0,0 +1,31 @@ +using Content.Server.Atmos.Components; + +namespace Content.Server.Atmos.EntitySystems; + +public partial class AtmosphereSystem +{ + private void InitializeMap() + { + SubscribeLocalEvent(MapIsTileSpace); + SubscribeLocalEvent(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; + } +} diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs index fd00628ab5..b31485654b 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Monstermos.cs @@ -10,8 +10,6 @@ namespace Content.Server.Atmos.EntitySystems { public sealed partial class AtmosphereSystem { - [Dependency] private readonly IRobustRandom _robustRandom = default!; - private readonly TileAtmosphereComparer _monstermosComparer = new(); private readonly TileAtmosphere?[] _equalizeTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit]; @@ -81,7 +79,7 @@ namespace Content.Server.Atmos.EntitySystems if(tileCount < Atmospherics.MonstermosHardTileLimit) _equalizeTiles[tileCount++] = adj; - if (adj.Air.Immutable && MonstermosDepressurization) + if (adj.Space && MonstermosDepressurization) { // Looks like someone opened an airlock to space! @@ -146,6 +144,7 @@ namespace Content.Server.Atmos.EntitySystems var direction = (AtmosDirection) (1 << j); if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue; var tile2 = otherTile.AdjacentTiles[j]!; + DebugTools.Assert(tile2.AdjacentBits.IsFlagSet(direction.GetOpposite())); // skip anything that isn't part of our current processing block. if (tile2.MonstermosInfo.FastDone || tile2.MonstermosInfo.LastQueueCycle != queueCycle) @@ -213,8 +212,8 @@ namespace Content.Server.Atmos.EntitySystems var otherTile2 = otherTile.AdjacentTiles[k]; if (giver.MonstermosInfo.MoleDelta <= 0) break; // We're done here now. Let's not do more work than needed. if (otherTile2 == null || otherTile2.MonstermosInfo.LastQueueCycle != queueCycle) continue; + DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite())); if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue; - _equalizeQueue[queueLength++] = otherTile2; otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; otherTile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite(); @@ -279,6 +278,7 @@ namespace Content.Server.Atmos.EntitySystems if (taker.MonstermosInfo.MoleDelta >= 0) break; // We're done here now. Let's not do more work than needed. if (otherTile2 == null || otherTile2.MonstermosInfo.LastQueueCycle != queueCycle) continue; + DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite())); if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue; _equalizeQueue[queueLength++] = otherTile2; otherTile2.MonstermosInfo.LastSlowQueueCycle = queueCycleSlow; @@ -335,7 +335,8 @@ namespace Content.Server.Atmos.EntitySystems var direction = (AtmosDirection) (1 << j); if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue; var otherTile2 = otherTile.AdjacentTiles[j]!; - if (otherTile2.Air?.Compare(tile.Air) == GasMixture.GasCompareResult.NoExchange) continue; + DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite())); + if (otherTile2.Air != null && CompareExchange(otherTile2.Air, tile.Air) == GasCompareResult.NoExchange) continue; AddActiveTile(gridAtmosphere, otherTile2); break; } @@ -372,7 +373,7 @@ namespace Content.Server.Atmos.EntitySystems otherTile.MonstermosInfo.LastCycle = cycleNum; otherTile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid; // Tiles in the _depressurizeTiles array cannot have null air. - if (!otherTile.Air!.Immutable) + if (!otherTile.Space) { for (var j = 0; j < Atmospherics.Directions; j++) { @@ -380,6 +381,7 @@ namespace Content.Server.Atmos.EntitySystems if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue; var otherTile2 = otherTile.AdjacentTiles[j]; if (otherTile2?.Air == null) continue; + DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite())); if (otherTile2.MonstermosInfo.LastQueueCycle == queueCycle) continue; ConsiderFirelocks(gridAtmosphere, otherTile, otherTile2); @@ -421,11 +423,12 @@ namespace Content.Server.Atmos.EntitySystems { var direction = (AtmosDirection) (1 << j); // Tiles in _depressurizeProgressionOrder cannot have null air. - if (!otherTile.AdjacentBits.IsFlagSet(direction) && !otherTile.Air!.Immutable) continue; + if (!otherTile.AdjacentBits.IsFlagSet(direction) && !otherTile.Space) continue; var tile2 = otherTile.AdjacentTiles[j]; if (tile2?.MonstermosInfo.LastQueueCycle != queueCycle) continue; + DebugTools.Assert(tile2.AdjacentBits.IsFlagSet(direction.GetOpposite())); if (tile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue; - if(tile2.Air?.Immutable ?? false) continue; + if(tile2.Space) continue; tile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite(); tile2.MonstermosInfo.CurrentTransferAmount = 0; tile2.PressureSpecificTarget = otherTile.PressureSpecificTarget; @@ -479,7 +482,7 @@ namespace Content.Server.Atmos.EntitySystems } if(tileCount > 10 && (totalMolesRemoved / tileCount) > 20) - _adminLogger.Add(LogType.ExplosiveDepressurization, LogImpact.High, + _adminLog.Add(LogType.ExplosiveDepressurization, LogImpact.High, $"Explosive depressurization removed {totalMolesRemoved} moles from {tileCount} tiles starting from position {tile.GridIndices:position} on grid ID {tile.GridIndex:grid}"); Array.Clear(_depressurizeTiles, 0, Atmospherics.MonstermosHardTileLimit); @@ -513,8 +516,10 @@ namespace Content.Server.Atmos.EntitySystems if (!reconsiderAdjacent) return; - UpdateAdjacent(mapGrid, gridAtmosphere, tile); - UpdateAdjacent(mapGrid, gridAtmosphere, other); + var tileEv = new UpdateAdjacentMethodEvent(mapGrid.GridEntityId, tile.GridIndices); + var otherEv = new UpdateAdjacentMethodEvent(mapGrid.GridEntityId, other.GridIndices); + GridUpdateAdjacent(mapGrid.GridEntityId, gridAtmosphere, ref tileEv); + GridUpdateAdjacent(mapGrid.GridEntityId, gridAtmosphere, ref otherEv); InvalidateVisuals(tile.GridIndex, tile.GridIndices); InvalidateVisuals(other.GridIndex, other.GridIndices); } @@ -541,6 +546,7 @@ namespace Content.Server.Atmos.EntitySystems var amount = transferDirections[i]; var otherTile = tile.AdjacentTiles[i]; if (otherTile?.Air == null) continue; + DebugTools.Assert(otherTile.AdjacentBits.IsFlagSet(direction.GetOpposite())); if (amount <= 0) continue; // Everything that calls this method already ensures that Air will not be null. @@ -569,6 +575,7 @@ namespace Content.Server.Atmos.EntitySystems private void AdjustEqMovement(TileAtmosphere tile, AtmosDirection direction, float amount) { + DebugTools.AssertNotNull(tile); DebugTools.Assert(tile.AdjacentBits.IsFlagSet(direction)); DebugTools.Assert(tile.AdjacentTiles[direction.ToIndex()] != null); tile.MonstermosInfo[direction] += amount; diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs index b2dba53c6f..b479241a69 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Processing.cs @@ -3,6 +3,7 @@ using Content.Server.Atmos.Piping.Components; using Content.Server.NodeContainer.NodeGroups; using Content.Shared.Atmos; using Content.Shared.Maps; +using Robust.Shared.Map; using Robust.Shared.Timing; namespace Content.Server.Atmos.EntitySystems @@ -42,37 +43,45 @@ namespace Content.Server.Atmos.EntitySystems atmosphere.InvalidatedCoords.Clear(); } - if (!TryGetMapGrid(atmosphere, out var mapGrid)) + var uid = atmosphere.Owner; + + if (!TryComp(uid, out IMapGridComponent? mapGridComp)) return true; + var mapGrid = mapGridComp.Grid; + var mapUid = _mapManager.GetMapEntityIdOrThrow(mapGridComp.Grid.ParentMapId); + var volume = GetVolumeForTiles(mapGrid, 1); var number = 0; while (atmosphere.CurrentRunInvalidatedCoordinates.TryDequeue(out var indices)) { - var tile = GetTileAtmosphere(atmosphere, indices); - - if (tile == null) + if (!atmosphere.Tiles.TryGetValue(indices, out var tile)) { tile = new TileAtmosphere(mapGrid.GridEntityId, indices, new GasMixture(volume){Temperature = Atmospherics.T20C}); atmosphere.Tiles[indices] = tile; } - var isAirBlocked = IsTileAirBlocked(mapGrid, indices); + var airBlockedEv = new IsTileAirBlockedMethodEvent(uid, indices, MapGridComponent:mapGridComp); + GridIsTileAirBlocked(uid, atmosphere, ref airBlockedEv); + var isAirBlocked = airBlockedEv.Result; - UpdateAdjacent(mapGrid, atmosphere, tile); + var updateAdjacentEv = new UpdateAdjacentMethodEvent(uid, indices, mapGridComp); + GridUpdateAdjacent(uid, atmosphere, ref updateAdjacentEv); - if (IsTileSpace(mapGrid, indices) && !isAirBlocked) + // Call this instead of the grid method as the map has a say on whether the tile is space or not. + if ((!mapGrid.TryGetTileRef(indices, out var t) || t.IsSpace(_tileDefinitionManager)) && !isAirBlocked) { - tile.Air = new GasMixture(volume); - tile.Air.MarkImmutable(); - atmosphere.Tiles[indices] = tile; - + tile.Air = GetTileMixture(null, mapUid, indices); + tile.MolesArchived = tile.Air != null ? new float[Atmospherics.AdjustedNumberOfGases] : null; + tile.Space = IsTileSpace(null, mapUid, indices, mapGridComp); } else if (isAirBlocked) { var nullAir = false; - foreach (var airtight in GetObstructingComponents(mapGrid, indices)) + var enumerator = GetObstructingComponentsEnumerator(mapGrid, indices); + + while (enumerator.MoveNext(out var airtight)) { if (!airtight.NoAirWhenFullyAirBlocked) continue; @@ -84,6 +93,9 @@ namespace Content.Server.Atmos.EntitySystems if (nullAir) { tile.Air = null; + tile.MolesArchived = null; + tile.ArchivedCycle = 0; + tile.LastShare = 0f; tile.Hotspot = new Hotspot(); } } @@ -91,23 +103,31 @@ namespace Content.Server.Atmos.EntitySystems { if (tile.Air == null && NeedsVacuumFixing(mapGrid, indices)) { - FixVacuum(atmosphere, tile.GridIndices); + var vacuumEv = new FixTileVacuumMethodEvent(uid, indices); + GridFixTileVacuum(uid, atmosphere, ref vacuumEv); } // Tile used to be space, but isn't anymore. - if (tile.Air?.Immutable ?? false) + if (tile.Space || (tile.Air?.Immutable ?? false)) { tile.Air = null; + tile.MolesArchived = null; + tile.ArchivedCycle = 0; + tile.LastShare = 0f; + tile.Space = false; } tile.Air ??= new GasMixture(volume){Temperature = Atmospherics.T20C}; + tile.MolesArchived ??= new float[Atmospherics.AdjustedNumberOfGases]; } // We activate the tile. AddActiveTile(atmosphere, tile); // TODO ATMOS: Query all the contents of this tile (like walls) and calculate the correct thermal conductivity and heat capacity - var tileDef = GetTile(tile)?.Tile.GetContentTileDefinition(_tileDefinitionManager); + var tileDef = mapGrid.TryGetTileRef(indices, out var tileRef) + ? tileRef.GetContentTileDefinition(_tileDefinitionManager) : null; + tile.ThermalConductivity = tileDef?.ThermalConductivity ?? 0.5f; tile.HeatCapacity = tileDef?.HeatCapacity ?? float.PositiveInfinity; InvalidateVisuals(mapGrid.GridEntityId, indices); @@ -116,8 +136,8 @@ namespace Content.Server.Atmos.EntitySystems { var direction = (AtmosDirection) (1 << i); var otherIndices = indices.Offset(direction); - var otherTile = GetTileAtmosphere(atmosphere, otherIndices); - if (otherTile != null) + + if (atmosphere.Tiles.TryGetValue(otherIndices, out var otherTile)) AddActiveTile(atmosphere, otherTile); } @@ -138,9 +158,13 @@ namespace Content.Server.Atmos.EntitySystems if(!atmosphere.ProcessingPaused) atmosphere.CurrentRunTiles = new Queue(atmosphere.ActiveTiles); - if (!TryGetMapGrid(atmosphere, out var mapGrid)) + var uid = atmosphere.Owner; + + if (!TryComp(uid, out IMapGridComponent? mapGridComp)) throw new Exception("Tried to process a grid atmosphere on an entity that isn't a grid!"); + var mapGrid = mapGridComp.Grid; + var number = 0; while (atmosphere.CurrentRunTiles.TryDequeue(out var tile)) { diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs index 4e3be35eba..33fa16a6c6 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Superconductivity.cs @@ -73,7 +73,7 @@ namespace Content.Server.Atmos.EntitySystems // Conduct with air on my tile if I have it if (tile.Air != null) { - tile.Temperature = TemperatureShare(tile.Air, tile.ThermalConductivity, tile.Temperature, tile.HeatCapacity); + tile.Temperature = TemperatureShare(tile, tile.ThermalConductivity, tile.Temperature, tile.HeatCapacity); } FinishSuperconduction(gridAtmosphere, tile, tile.Air?.Temperature ?? tile.Temperature); @@ -107,7 +107,7 @@ namespace Content.Server.Atmos.EntitySystems if (other.Air != null) { - TemperatureShare(other.Air, tile.Air, Atmospherics.WindowHeatTransferCoefficient); + TemperatureShare(other, tile, Atmospherics.WindowHeatTransferCoefficient); } else { @@ -122,7 +122,7 @@ namespace Content.Server.Atmos.EntitySystems if (tile.Air == null) return; - other.Temperature = TemperatureShare(tile.Air, other.ThermalConductivity, other.Temperature, other.HeatCapacity); + other.Temperature = TemperatureShare(tile, other.ThermalConductivity, other.Temperature, other.HeatCapacity); } private void TemperatureShareMutualSolid(TileAtmosphere tile, TileAtmosphere other, float conductionCoefficient) diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Utils.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Utils.cs new file mode 100644 index 0000000000..36ac6e90de --- /dev/null +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Utils.cs @@ -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; + } + + /// + /// Gets the volume in liters for a number of tiles, on a specific grid. + /// + /// The grid in question. + /// The amount of tiles. + /// The volume in liters that the tiles occupy. + private float GetVolumeForTiles(IMapGrid mapGrid, int tiles = 1) + { + return Atmospherics.CellVolume * mapGrid.TileSize * tiles; + } + + /// + /// Gets all obstructing instances in a specific tile. + /// + /// The grid where to get the tile. + /// The indices of the tile. + /// The enumerator for the airtight components. + public AtmosObstructionEnumerator GetObstructingComponentsEnumerator(IMapGrid mapGrid, Vector2i tile) + { + var ancEnumerator = mapGrid.GetAnchoredEntitiesEnumerator(tile); + var airQuery = GetEntityQuery(); + + 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; + } + + /// + /// Pries a tile in a grid. + /// + /// The grid in question. + /// The indices of the tile. + private void PryTile(IMapGrid mapGrid, Vector2i tile) + { + if (!mapGrid.TryGetTileRef(tile, out var tileRef)) + return; + + tileRef.PryTile(_mapManager, _tileDefinitionManager, EntityManager, _robustRandom); + } +} diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs index 6c8ee5aee2..5089090e12 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.cs @@ -4,90 +4,84 @@ using Content.Server.NodeContainer.EntitySystems; using Content.Shared.Atmos.EntitySystems; using Content.Shared.Maps; using JetBrains.Annotations; +using Robust.Server.GameObjects; using Robust.Shared.Containers; using Robust.Shared.Map; +using Robust.Shared.Random; -namespace Content.Server.Atmos.EntitySystems +namespace Content.Server.Atmos.EntitySystems; + +/// +/// This is our SSAir equivalent, if you need to interact with or query atmos in any way, go through this. +/// +[UsedImplicitly] +public sealed partial class AtmosphereSystem : SharedAtmosphereSystem { - /// - /// This is our SSAir equivalent, if you need to interact with or query atmos in any way, go through this. - /// - [UsedImplicitly] - public sealed partial class AtmosphereSystem : SharedAtmosphereSystem + [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; + [Dependency] private readonly IRobustRandom _robustRandom = default!; + [Dependency] private readonly IAdminLogManager _adminLog = default!; + [Dependency] private readonly SharedContainerSystem _containers = default!; + [Dependency] private readonly SharedPhysicsSystem _physics = default!; + [Dependency] private readonly GasTileOverlaySystem _gasTileOverlaySystem = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; + + + private const float ExposedUpdateDelay = 1f; + private float _exposedTimer = 0f; + + public override void Initialize() { - [Dependency] private readonly IMapManager _mapManager = default!; - [Dependency] private readonly IAdminLogManager _adminLogger = default!; - [Dependency] private readonly SharedContainerSystem _containers = default!; - [Dependency] private readonly SharedPhysicsSystem _physics = default!; + base.Initialize(); + + UpdatesAfter.Add(typeof(NodeGroupSystem)); + + InitializeGases(); + InitializeCommands(); + InitializeCVars(); + InitializeGridAtmosphere(); + InitializeMap(); - private const float ExposedUpdateDelay = 1f; - private float _exposedTimer = 0f; + SubscribeLocalEvent(OnTileChanged); - 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()) { - base.Initialize(); + var air = GetContainingMixture(exposed.Owner, transform:transform); - UpdatesAfter.Add(typeof(NodeGroupSystem)); - - InitializeGases(); - InitializeCommands(); - InitializeCVars(); - InitializeGrid(); - - - SubscribeLocalEvent(OnTileChanged); + if (air == null) + continue; + var updateEvent = new AtmosExposedUpdateEvent(transform.Coordinates, air, transform); + RaiseLocalEvent(exposed.Owner, ref updateEvent); } - public override void Shutdown() - { - 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()) - { - // 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; - } + _exposedTimer -= ExposedUpdateDelay; } } diff --git a/Content.Server/Atmos/EntitySystems/AutomaticAtmosSystem.cs b/Content.Server/Atmos/EntitySystems/AutomaticAtmosSystem.cs index 4b9cb8dba9..bdd7c943cd 100644 --- a/Content.Server/Atmos/EntitySystems/AutomaticAtmosSystem.cs +++ b/Content.Server/Atmos/EntitySystems/AutomaticAtmosSystem.cs @@ -12,6 +12,7 @@ namespace Content.Server.Atmos.EntitySystems; public sealed class AutomaticAtmosSystem : EntitySystem { [Dependency] private readonly ITileDefinitionManager _tileDefinitionManager = default!; + [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; public override void Initialize() { @@ -27,7 +28,7 @@ public sealed class AutomaticAtmosSystem : EntitySystem // TODO: a simple array lookup, as tile IDs are likely contiguous, and there's at most 2^16 possibilities anyway. if (!((ev.OldTile.IsSpace(_tileDefinitionManager) && !ev.NewTile.IsSpace(_tileDefinitionManager)) || (!ev.OldTile.IsSpace(_tileDefinitionManager) && ev.NewTile.IsSpace(_tileDefinitionManager))) || - HasComp(ev.Entity)) + _atmosphereSystem.HasAtmosphere(ev.Entity)) return; if (!TryComp(ev.Entity, out var physics)) diff --git a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs index 556615fb0a..8a202e5fa9 100644 --- a/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs +++ b/Content.Server/Atmos/EntitySystems/BarotraumaSystem.cs @@ -140,6 +140,7 @@ namespace Content.Server.Atmos.EntitySystems foreach (var (barotrauma, damageable, transform) in EntityManager.EntityQuery()) { + var uid = barotrauma.Owner; var totalDamage = FixedPoint2.Zero; foreach (var (barotraumaDamageType, _) in barotrauma.Damage.DamageDict) { @@ -152,7 +153,7 @@ namespace Content.Server.Atmos.EntitySystems var pressure = 1f; - if (_atmosphereSystem.GetTileMixture(transform.Coordinates) is { } mixture) + if (_atmosphereSystem.GetContainingMixture(uid) is {} mixture) { pressure = MathF.Max(mixture.Pressure, 1f); } diff --git a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs index f1e0a6cd14..c92d88ad86 100644 --- a/Content.Server/Atmos/EntitySystems/FlammableSystem.cs +++ b/Content.Server/Atmos/EntitySystems/FlammableSystem.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using Content.Server.Administration.Logs; using Content.Server.Atmos.Components; using Content.Server.Stunnable; @@ -12,6 +14,10 @@ using Content.Shared.Database; using Content.Shared.Interaction; using Content.Shared.Popups; using Content.Shared.Temperature; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Localization; using Robust.Shared.Physics; using Robust.Shared.Physics.Dynamics; @@ -25,6 +31,7 @@ namespace Content.Server.Atmos.EntitySystems [Dependency] private readonly TemperatureSystem _temperatureSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly AlertsSystem _alertsSystem = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly IAdminLogManager _adminLogger = default!; private const float MinimumFireStacks = -10f; @@ -272,7 +279,7 @@ namespace Content.Server.Atmos.EntitySystems continue; } - var air = _atmosphereSystem.GetTileMixture(transform.Coordinates); + var air = _atmosphereSystem.GetContainingMixture(uid); // If we're in an oxygenless environment, put the fire out. if (air == null || air.GetMoles(Gas.Oxygen) < 1f) @@ -281,7 +288,13 @@ namespace Content.Server.Atmos.EntitySystems continue; } - _atmosphereSystem.HotspotExpose(transform.Coordinates, 700f, 50f, true); + if(transform.GridUid != null) + { + _atmosphereSystem.HotspotExpose(transform.GridUid.Value, + _transformSystem.GetGridOrMapTilePosition(uid, transform), + 700f, 50f, true); + + } foreach (var otherUid in flammable.Collided.ToArray()) { diff --git a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs index ea647ef698..118ba3822d 100644 --- a/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs +++ b/Content.Server/Atmos/EntitySystems/GasTileOverlaySystem.cs @@ -140,9 +140,7 @@ namespace Content.Server.Atmos.EntitySystems /// true if updated private bool TryRefreshTile(GridAtmosphereComponent gridAtmosphere, GasOverlayData oldTile, Vector2i indices, out GasOverlayData overlayData) { - var tile = _atmosphereSystem.GetTileAtmosphere(gridAtmosphere, indices); - - if (tile == null) + if (!gridAtmosphere.Tiles.TryGetValue(indices, out var tile)) { overlayData = default; return false; diff --git a/Content.Server/Atmos/GasMixture.cs b/Content.Server/Atmos/GasMixture.cs index 1f7db393a3..84f896fe39 100644 --- a/Content.Server/Atmos/GasMixture.cs +++ b/Content.Server/Atmos/GasMixture.cs @@ -1,5 +1,7 @@ -using System.Linq; +using System.Diagnostics.CodeAnalysis; +using System.Linq; using System.Runtime.CompilerServices; +using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.Reactions; using Content.Shared.Atmos; using Robust.Shared.Serialization; @@ -19,17 +21,12 @@ namespace Content.Server.Atmos [DataField("moles")] [ViewVariables] public float[] Moles = new float[Atmospherics.AdjustedNumberOfGases]; - public float[] MolesArchived = new float[Atmospherics.AdjustedNumberOfGases]; - [DataField("temperature")] [ViewVariables] private float _temperature = Atmospherics.TCMB; [DataField("immutable")] [ViewVariables] public bool Immutable { get; private set; } - [DataField("lastShare")] [ViewVariables] - public float LastShare { get; set; } - [ViewVariables] public readonly Dictionary ReactionResults = new() { @@ -65,8 +62,6 @@ namespace Content.Server.Atmos } } - public float TemperatureArchived { get; private set; } - [DataField("volume")] [ViewVariables] public float Volume { get; set; } @@ -87,13 +82,6 @@ namespace Content.Server.Atmos Immutable = true; } - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public void Archive() - { - Moles.AsSpan().CopyTo(MolesArchived.AsSpan()); - TemperatureArchived = Temperature; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public float GetMoles(int gasId) { @@ -192,40 +180,6 @@ namespace Content.Server.Atmos Temperature = sample.Temperature; } - public enum GasCompareResult - { - NoExchange = -2, - TemperatureExchange = -1, - } - - /// - /// Compares sample to self to see if within acceptable ranges that group processing may be enabled. - /// - [MethodImpl(MethodImplOptions.AggressiveInlining)] - public GasCompareResult Compare(GasMixture sample) - { - var moles = 0f; - - for(var i = 0; i < Atmospherics.TotalNumberOfGases; i++) - { - var gasMoles = Moles[i]; - var delta = MathF.Abs(gasMoles - sample.Moles[i]); - if (delta > Atmospherics.MinimumMolesDeltaToMove && (delta > gasMoles * Atmospherics.MinimumAirRatioToMove)) - return (GasCompareResult)i; // We can move gases! - moles += gasMoles; - } - - if (moles > Atmospherics.MinimumMolesDeltaToMove) - { - var tempDelta = MathF.Abs(Temperature - sample.Temperature); - if (tempDelta > Atmospherics.MinimumTemperatureDeltaToSuspend) - return GasCompareResult.TemperatureExchange; // There can be temperature exchange. - } - - // No exchange at all! - return GasCompareResult.NoExchange; - } - [MethodImpl(MethodImplOptions.AggressiveInlining)] public void Clear() { @@ -244,7 +198,6 @@ namespace Content.Server.Atmos { // The arrays MUST have a specific length. Array.Resize(ref Moles, Atmospherics.AdjustedNumberOfGases); - Array.Resize(ref MolesArchived, Atmospherics.AdjustedNumberOfGases); } public override bool Equals(object? obj) @@ -259,15 +212,13 @@ namespace Content.Server.Atmos if (ReferenceEquals(null, other)) return false; if (ReferenceEquals(this, other)) return true; return Moles.SequenceEqual(other.Moles) - && MolesArchived.SequenceEqual(other.MolesArchived) && _temperature.Equals(other._temperature) && ReactionResults.SequenceEqual(other.ReactionResults) && Immutable == other.Immutable - && LastShare.Equals(other.LastShare) - && TemperatureArchived.Equals(other.TemperatureArchived) && Volume.Equals(other.Volume); } + [SuppressMessage("ReSharper", "NonReadonlyMemberInGetHashCode")] public override int GetHashCode() { var hashCode = new HashCode(); @@ -275,15 +226,11 @@ namespace Content.Server.Atmos for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++) { var moles = Moles[i]; - var molesArchived = MolesArchived[i]; hashCode.Add(moles); - hashCode.Add(molesArchived); } hashCode.Add(_temperature); - hashCode.Add(TemperatureArchived); hashCode.Add(Immutable); - hashCode.Add(LastShare); hashCode.Add(Volume); return hashCode.ToHashCode(); @@ -294,11 +241,8 @@ namespace Content.Server.Atmos var newMixture = new GasMixture() { Moles = (float[])Moles.Clone(), - MolesArchived = (float[])MolesArchived.Clone(), _temperature = _temperature, Immutable = Immutable, - LastShare = LastShare, - TemperatureArchived = TemperatureArchived, Volume = Volume, }; return newMixture; diff --git a/Content.Server/Atmos/Miasma/MiasmaSystem.cs b/Content.Server/Atmos/Miasma/MiasmaSystem.cs index 76f1a54df9..9bdb726ae7 100644 --- a/Content.Server/Atmos/Miasma/MiasmaSystem.cs +++ b/Content.Server/Atmos/Miasma/MiasmaSystem.cs @@ -5,6 +5,7 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Temperature.Systems; using Content.Server.Body.Components; using Content.Shared.Examine; +using Robust.Server.GameObjects; using Content.Shared.Tag; using Robust.Shared.Containers; using Robust.Shared.Random; @@ -13,6 +14,7 @@ namespace Content.Server.Atmos.Miasma { public sealed class MiasmaSystem : EntitySystem { + [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; @@ -108,9 +110,11 @@ namespace Content.Server.Atmos.Miasma float molRate = perishable.MolsPerSecondPerUnitMass * _rotUpdateRate; - var tileMix = _atmosphereSystem.GetTileMixture(Transform(perishable.Owner).Coordinates); - if (tileMix != null) - tileMix.AdjustMoles(Gas.Miasma, molRate * physics.FixturesMass); + var transform = Transform(perishable.Owner); + var indices = _transformSystem.GetGridOrMapTilePosition(perishable.Owner); + + var tileMix = _atmosphereSystem.GetTileMixture(transform.GridUid, null, indices, true); + tileMix?.AdjustMoles(Gas.Miasma, molRate * physics.FixturesMass); } } @@ -167,9 +171,10 @@ namespace Content.Server.Atmos.Miasma return; var molsToDump = (component.MolsPerSecondPerUnitMass * physics.FixturesMass) * component.DeathAccumulator; - var tileMix = _atmosphereSystem.GetTileMixture(Transform(uid).Coordinates); - if (tileMix != null) - tileMix.AdjustMoles(Gas.Miasma, molsToDump); + var transform = Transform(uid); + var indices = _transformSystem.GetGridOrMapTilePosition(uid, transform); + var tileMix = _atmosphereSystem.GetTileMixture(transform.GridUid, null, indices, true); + tileMix?.AdjustMoles(Gas.Miasma, molsToDump); // Waste of entities to let these through foreach (var part in args.GibbedParts) diff --git a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs index f0249d704b..8a443c412d 100644 --- a/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs +++ b/Content.Server/Atmos/Monitor/Systems/AtmosMonitoringSystem.cs @@ -9,6 +9,7 @@ using Content.Server.Power.Components; using Content.Server.Power.EntitySystems; using Content.Shared.Atmos; using Content.Shared.Atmos.Monitor; +using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Player; using Robust.Shared.Prototypes; @@ -24,6 +25,7 @@ namespace Content.Server.Atmos.Monitor.Systems [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly AtmosDeviceSystem _atmosDeviceSystem = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; // Commands @@ -118,27 +120,31 @@ namespace Content.Server.Atmos.Monitor.Systems if (!Resolve(uid, ref component, ref appearance)) return; var transform = Transform(component.Owner); + + if (transform.GridUid == null) + return; + // atmos alarms will first attempt to get the air // directly underneath it - if not, then it will // instead place itself directly in front of the tile // it is facing, and then visually shift itself back // via sprite offsets (SS13 style but fuck it) var coords = transform.Coordinates; + var pos = _transformSystem.GetGridOrMapTilePosition(uid, transform); - if (_atmosphereSystem.IsTileAirBlocked(coords)) + if (_atmosphereSystem.IsTileAirBlocked(transform.GridUid.Value, pos)) { - var rotPos = transform.LocalRotation.RotateVec(new Vector2(0, -1)); transform.Anchored = false; coords = coords.Offset(rotPos); transform.Coordinates = coords; - appearance.SetData("offset", - new Vector2(0, -1)); + appearance.SetData("offset", - new Vector2i(0, -1)); transform.Anchored = true; } - GasMixture? air = _atmosphereSystem.GetTileMixture(coords); + GasMixture? air = _atmosphereSystem.GetContainingMixture(uid, true); component.TileGas = air; _checkPos.Remove(uid); @@ -214,8 +220,7 @@ namespace Content.Server.Atmos.Monitor.Systems if (atmosDeviceComponent.JoinedGrid == null) { _atmosDeviceSystem.JoinAtmosphere(atmosDeviceComponent); - var coords = Transform(component.Owner).Coordinates; - var air = _atmosphereSystem.GetTileMixture(coords); + var air = _atmosphereSystem.GetContainingMixture(uid, true); component.TileGas = air; } } diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs index 626cb4098b..cf41fc1187 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs @@ -21,9 +21,10 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems public sealed class GasVolumePumpSystem : EntitySystem { [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; - [Dependency] private readonly IAdminLogManager _adminLogger = default!; [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; public override void Initialize() @@ -65,7 +66,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems || !nodeContainer.TryGetNode(pump.InletName, out PipeNode? inlet) || !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet)) { - _ambientSoundSystem.SetAmbience(pump.Owner, false); + _ambientSoundSystem.SetAmbience(uid, false); return; } @@ -88,7 +89,9 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems // Some of the gas from the mixture leaks when overclocked. if (pump.Overclocked) { - var tile = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent(pump.Owner).Coordinates, true); + var transform = Transform(uid); + var indices = _transformSystem.GetGridOrMapTilePosition(uid, transform); + var tile = _atmosphereSystem.GetTileMixture(transform.GridUid, null, indices, true); if (tile != null) { @@ -98,7 +101,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems } _atmosphereSystem.Merge(outlet.Air, removed); - _ambientSoundSystem.SetAmbience(pump.Owner, removed.TotalMoles > 0f); + _ambientSoundSystem.SetAmbience(uid, removed.TotalMoles > 0f); } private void OnVolumePumpLeaveAtmosphere(EntityUid uid, GasVolumePumpComponent pump, AtmosDeviceDisabledEvent args) diff --git a/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs b/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs index c9396e0594..130370599f 100644 --- a/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs +++ b/Content.Server/Atmos/Piping/EntitySystems/AtmosDeviceSystem.cs @@ -27,20 +27,25 @@ namespace Content.Server.Atmos.Piping.EntitySystems SubscribeLocalEvent(OnDeviceAnchorChanged); } - private bool CanJoinAtmosphere(AtmosDeviceComponent component) + private bool CanJoinAtmosphere(AtmosDeviceComponent component, TransformComponent transform) { - return !component.RequireAnchored || EntityManager.GetComponent(component.Owner).Anchored; + return (!component.RequireAnchored || transform.Anchored) && transform.GridUid != null; } public void JoinAtmosphere(AtmosDeviceComponent component) { - if (!CanJoinAtmosphere(component)) + var transform = Transform(component.Owner); + + if (!CanJoinAtmosphere(component, transform)) { return; } + // TODO: low-hanging fruit for perf improvements around here + + // GridUid is not null because we can join atmosphere. // We try to add the device to a valid atmosphere, and if we can't, try to add it to the entity system. - if (!_atmosphereSystem.AddAtmosDevice(component)) + if (!_atmosphereSystem.AddAtmosDevice(transform.GridUid!.Value, component)) { if (component.JoinSystem) { @@ -62,7 +67,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems public void LeaveAtmosphere(AtmosDeviceComponent component) { // Try to remove the component from an atmosphere, and if not - if (component.JoinedGrid != null && !_atmosphereSystem.RemoveAtmosDevice(component)) + if (component.JoinedGrid != null && !_atmosphereSystem.RemoveAtmosDevice(component.JoinedGrid.Value, component)) { // The grid might have been removed but not us... This usually shouldn't happen. component.JoinedGrid = null; diff --git a/Content.Server/Atmos/Piping/EntitySystems/AtmosUnsafeUnanchorSystem.cs b/Content.Server/Atmos/Piping/EntitySystems/AtmosUnsafeUnanchorSystem.cs index 52abad4104..9968b7191e 100644 --- a/Content.Server/Atmos/Piping/EntitySystems/AtmosUnsafeUnanchorSystem.cs +++ b/Content.Server/Atmos/Piping/EntitySystems/AtmosUnsafeUnanchorSystem.cs @@ -27,7 +27,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems if (!component.Enabled || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodes)) return; - if (_atmosphereSystem.GetTileMixture(EntityManager.GetComponent(component.Owner).Coordinates) is not {} environment) + if (_atmosphereSystem.GetContainingMixture(uid, true) is not {} environment) return; foreach (var node in nodes.Nodes.Values) @@ -48,7 +48,7 @@ namespace Content.Server.Atmos.Piping.EntitySystems if (!component.Enabled || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodes)) return; - if (_atmosphereSystem.GetTileMixture(EntityManager.GetComponent(component.Owner).Coordinates, true) is not {} environment) + if (_atmosphereSystem.GetContainingMixture(uid, true, true) is not {} environment) environment = GasMixture.SpaceGas; var lost = 0f; diff --git a/Content.Server/Atmos/Piping/Other/EntitySystems/GasMinerSystem.cs b/Content.Server/Atmos/Piping/Other/EntitySystems/GasMinerSystem.cs index fa310d2b5f..9cdee72b68 100644 --- a/Content.Server/Atmos/Piping/Other/EntitySystems/GasMinerSystem.cs +++ b/Content.Server/Atmos/Piping/Other/EntitySystems/GasMinerSystem.cs @@ -1,9 +1,13 @@ using System.Diagnostics.CodeAnalysis; +using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.Piping.Components; using Content.Server.Atmos.Piping.Other.Components; using Content.Shared.Atmos; using JetBrains.Annotations; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; namespace Content.Server.Atmos.Piping.Other.EntitySystems { @@ -11,6 +15,7 @@ namespace Content.Server.Atmos.Piping.Other.EntitySystems public sealed class GasMinerSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; public override void Initialize() { @@ -34,10 +39,14 @@ namespace Content.Server.Atmos.Piping.Other.EntitySystems private bool CheckMinerOperation(GasMinerComponent miner, [NotNullWhen(true)] out GasMixture? environment) { - environment = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent(miner.Owner).Coordinates, true); + var uid = miner.Owner; + environment = _atmosphereSystem.GetContainingMixture(uid, true, true); + + var transform = Transform(uid); + var position = _transformSystem.GetGridOrMapTilePosition(uid, transform); // Space. - if (_atmosphereSystem.IsTileSpace(EntityManager.GetComponent(miner.Owner).Coordinates)) + if (_atmosphereSystem.IsTileSpace(transform.GridUid, transform.MapUid, position)) { miner.Broken = true; return false; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs index 8acabe3996..405e62f894 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasCanisterSystem.cs @@ -51,7 +51,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems if (!Resolve(uid, ref canister, ref transform)) return; - var environment = _atmosphereSystem.GetTileMixture(transform.Coordinates, true); + var environment = _atmosphereSystem.GetContainingMixture(uid, false, true); if (environment is not null) _atmosphereSystem.Merge(environment, canister.Air); @@ -180,7 +180,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems } else { - var environment = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent(canister.Owner).Coordinates, true); + var environment = _atmosphereSystem.GetContainingMixture(uid, false, true); _atmosphereSystem.ReleaseGasTo(canister.Air, environment, canister.ReleasePressure); } } diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs index d7514d79fc..3ef81d0f24 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasOutletInjectorSystem.cs @@ -58,7 +58,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems if (!nodeContainer.TryGetNode(injector.InletName, out PipeNode? inlet)) return; - var environment = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent(injector.Owner).Coordinates, true); + var environment = _atmosphereSystem.GetContainingMixture(uid, true, true); if (environment == null) return; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPassiveVentSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPassiveVentSystem.cs index 90aa96c9eb..6bd5fd7dad 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPassiveVentSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasPassiveVentSystem.cs @@ -22,7 +22,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems private void OnPassiveVentUpdated(EntityUid uid, GasPassiveVentComponent vent, AtmosDeviceUpdateEvent args) { - var environment = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent(vent.Owner).Coordinates, true); + var environment = _atmosphereSystem.GetContainingMixture(uid, true, true); if (environment == null) return; diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs index 5351e6e2a9..8b6b527b41 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentPumpSystem.cs @@ -67,7 +67,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems return; } - var environment = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent(vent.Owner).Coordinates, true); + var environment = _atmosphereSystem.GetContainingMixture(uid, true, true); // We're in an air-blocked tile... Do nothing. if (environment == null) diff --git a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs index c0008afcc4..fb0f24e7a9 100644 --- a/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs +++ b/Content.Server/Atmos/Piping/Unary/EntitySystems/GasVentScrubberSystem.cs @@ -15,6 +15,7 @@ using Content.Shared.Atmos.Monitor; using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Audio; using JetBrains.Annotations; +using Robust.Server.GameObjects; using Robust.Shared.Timing; namespace Content.Server.Atmos.Piping.Unary.EntitySystems @@ -26,6 +27,7 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly DeviceNetworkSystem _deviceNetSystem = default!; [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; public override void Initialize() { @@ -54,19 +56,24 @@ namespace Content.Server.Atmos.Piping.Unary.EntitySystems if (!scrubber.Enabled || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) || !nodeContainer.TryGetNode(scrubber.OutletName, out PipeNode? outlet)) - { return; - } var xform = Transform(uid); - var environment = _atmosphereSystem.GetTileMixture(xform.Coordinates, true); + + if (xform.GridUid == null) + return; + + var position = _transformSystem.GetGridOrMapTilePosition(uid, xform); + + var environment = _atmosphereSystem.GetTileMixture(xform.GridUid, xform.MapUid, position, true); Scrub(timeDelta, scrubber, environment, outlet); - if (!scrubber.WideNet) return; + if (!scrubber.WideNet) + return; // Scrub adjacent tiles too. - foreach (var adjacent in _atmosphereSystem.GetAdjacentTileMixtures(xform.Coordinates, false, true)) + foreach (var adjacent in _atmosphereSystem.GetAdjacentTileMixtures(xform.GridUid.Value, position, false, true)) { Scrub(timeDelta, scrubber, adjacent, outlet); } diff --git a/Content.Server/Atmos/TileAtmosphere.cs b/Content.Server/Atmos/TileAtmosphere.cs index 21cec7f262..76948e1636 100644 --- a/Content.Server/Atmos/TileAtmosphere.cs +++ b/Content.Server/Atmos/TileAtmosphere.cs @@ -9,7 +9,7 @@ namespace Content.Server.Atmos /// Internal Atmos class that stores data about the atmosphere in a grid. /// You shouldn't use this directly, use instead. /// - [Access(typeof(AtmosphereSystem))] + [Access(typeof(AtmosphereSystem), typeof(GasTileOverlaySystem), typeof(AtmosDebugOverlaySystem))] public sealed class TileAtmosphere : IGasMixtureHolder { [ViewVariables] @@ -39,6 +39,12 @@ namespace Content.Server.Atmos [ViewVariables] public bool Excited { get; set; } + /// + /// Whether this tile should be considered space. + /// + [ViewVariables] + public bool Space { get; set; } + /// /// Adjacent tiles in the same order as . (NSEW) /// @@ -81,6 +87,13 @@ namespace Content.Server.Atmos [Access(typeof(AtmosphereSystem), Other = AccessPermissions.ReadExecute)] // FIXME Friends public GasMixture? Air { get; set; } + [ViewVariables] + [DataField("lastShare")] + public float LastShare; + + [ViewVariables] + public float[]? MolesArchived; + GasMixture IGasMixtureHolder.Air { get => Air ?? new GasMixture(Atmospherics.CellVolume){ Temperature = Temperature }; @@ -93,11 +106,13 @@ namespace Content.Server.Atmos [ViewVariables] public AtmosDirection BlockedAirflow { get; set; } = AtmosDirection.Invalid; - public TileAtmosphere(EntityUid gridIndex, Vector2i gridIndices, GasMixture? mixture = null, bool immutable = false) + public TileAtmosphere(EntityUid gridIndex, Vector2i gridIndices, GasMixture? mixture = null, bool immutable = false, bool space = false) { GridIndex = gridIndex; GridIndices = gridIndices; Air = mixture; + Space = space; + MolesArchived = Air != null ? new float[Atmospherics.AdjustedNumberOfGases] : null; if(immutable) Air?.MarkImmutable(); diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs index 1e6ddd88be..448341659e 100644 --- a/Content.Server/Body/Systems/RespiratorSystem.cs +++ b/Content.Server/Body/Systems/RespiratorSystem.cs @@ -102,7 +102,7 @@ namespace Content.Server.Body.Systems if (ev.Gas == null) { - ev.Gas = _atmosSys.GetTileMixture(Transform(uid).Coordinates); + ev.Gas = _atmosSys.GetContainingMixture(uid, false, true); if (ev.Gas == null) return; } @@ -133,7 +133,7 @@ namespace Content.Server.Body.Systems if (ev.Gas == null) { - ev.Gas = _atmosSys.GetTileMixture(Transform(uid).Coordinates); + ev.Gas = _atmosSys.GetContainingMixture(uid, false, true); // Walls and grids without atmos comp return null. I guess it makes sense to not be able to exhale in walls, // but this also means you cannot exhale on some grids. diff --git a/Content.Server/Botany/Components/PlantHolderComponent.cs b/Content.Server/Botany/Components/PlantHolderComponent.cs index 9e099872d6..c2f651c1e1 100644 --- a/Content.Server/Botany/Components/PlantHolderComponent.cs +++ b/Content.Server/Botany/Components/PlantHolderComponent.cs @@ -7,6 +7,7 @@ using Content.Shared.Botany; using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Reagent; using Content.Shared.FixedPoint; +using Robust.Server.GameObjects; using Robust.Shared.Prototypes; using Robust.Shared.Random; using Robust.Shared.Timing; @@ -225,7 +226,8 @@ namespace Content.Server.Botany.Components _updateSpriteAfterUpdate = true; } - var environment = EntitySystem.Get().GetTileMixture(_entMan.GetComponent(Owner).Coordinates, true) ?? + var atmosphereSystem = _entMan.EntitySysManager.GetEntitySystem(); + var environment = atmosphereSystem.GetContainingMixture(Owner, true, true) ?? GasMixture.SpaceGas; if (Seed.ConsumeGasses.Count > 0) diff --git a/Content.Server/Chemistry/ReagentEffects/CreateGas.cs b/Content.Server/Chemistry/ReagentEffects/CreateGas.cs index 0f726dca1a..a2a0c61124 100644 --- a/Content.Server/Chemistry/ReagentEffects/CreateGas.cs +++ b/Content.Server/Chemistry/ReagentEffects/CreateGas.cs @@ -21,10 +21,9 @@ public sealed class CreateGas : ReagentEffect public override void Effect(ReagentEffectArgs args) { - var atmosSys = EntitySystem.Get(); + var atmosSys = args.EntityManager.EntitySysManager.GetEntitySystem(); - var xform = args.EntityManager.GetComponent(args.SolutionEntity); - var tileMix = atmosSys.GetTileMixture(xform.Coordinates); + var tileMix = atmosSys.GetContainingMixture(args.SolutionEntity, false, true); if (tileMix != null) { diff --git a/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs b/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs index e3274a098e..36438d8dee 100644 --- a/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/ExtinguishTileReaction.cs @@ -21,7 +21,7 @@ namespace Content.Server.Chemistry.TileReactions var atmosphereSystem = EntitySystem.Get(); - var environment = atmosphereSystem.GetTileMixture(tile.GridUid, tile.GridIndices, true); + var environment = atmosphereSystem.GetTileMixture(tile.GridUid, null, tile.GridIndices, true); if (environment == null || !atmosphereSystem.IsHotspotActive(tile.GridUid, tile.GridIndices)) return FixedPoint2.Zero; @@ -30,7 +30,7 @@ namespace Content.Server.Chemistry.TileReactions MathF.Max(MathF.Min(environment.Temperature - (_coolingTemperature * 1000f), environment.Temperature / _coolingTemperature), Atmospherics.TCMB); - atmosphereSystem.React(tile.GridUid, tile.GridIndices); + atmosphereSystem.ReactTile(tile.GridUid, tile.GridIndices); atmosphereSystem.HotspotExtinguish(tile.GridUid, tile.GridIndices); return FixedPoint2.Zero; diff --git a/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs b/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs index 753321aa30..47f5c91006 100644 --- a/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs +++ b/Content.Server/Chemistry/TileReactions/FlammableTileReaction.cs @@ -20,12 +20,12 @@ namespace Content.Server.Chemistry.TileReactions var atmosphereSystem = EntitySystem.Get(); - var environment = atmosphereSystem.GetTileMixture(tile.GridUid, tile.GridIndices, true); + var environment = atmosphereSystem.GetTileMixture(tile.GridUid, null, tile.GridIndices, true); if (environment == null || !atmosphereSystem.IsHotspotActive(tile.GridUid, tile.GridIndices)) return FixedPoint2.Zero; environment.Temperature *= MathF.Max(_temperatureMultiplier * reactVolume.Float(), 1f); - atmosphereSystem.React(tile.GridUid, tile.GridIndices); + atmosphereSystem.ReactTile(tile.GridUid, tile.GridIndices); return reactVolume; } diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs index 0f95229679..98991973b8 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposableSystem.cs @@ -70,7 +70,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems _disposalUnitSystem.TryEjectContents(duc); } - if (_atmosphereSystem.GetTileMixture(holderTransform.Coordinates, true) is {} environment) + if (_atmosphereSystem.GetContainingMixture(uid, false, true) is {} environment) { _atmosphereSystem.Merge(environment, holder.Air); holder.Air.Clear(); diff --git a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs index 3b0c064360..6cea4cc0d7 100644 --- a/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs +++ b/Content.Server/Disposal/Unit/EntitySystems/DisposalUnitSystem.cs @@ -42,6 +42,7 @@ namespace Content.Server.Disposal.Unit.EntitySystems [Dependency] private readonly DoAfterSystem _doAfterSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; [Dependency] private readonly DumpableSystem _dumpableSystem = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; private readonly List _activeDisposals = new(); @@ -521,8 +522,9 @@ namespace Content.Server.Disposal.Unit.EntitySystems var air = component.Air; var entryComponent = EntityManager.GetComponent(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); diff --git a/Content.Server/Doors/Components/FirelockComponent.cs b/Content.Server/Doors/Components/FirelockComponent.cs index 564c748a8c..665d46f39e 100644 --- a/Content.Server/Doors/Components/FirelockComponent.cs +++ b/Content.Server/Doors/Components/FirelockComponent.cs @@ -2,6 +2,10 @@ using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Doors.Systems; using Content.Shared.Doors.Components; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Serialization.Manager.Attributes; namespace Content.Server.Doors.Components { @@ -42,12 +46,20 @@ namespace Content.Server.Doors.Components public bool IsHoldingPressure(float threshold = 20) { - var atmosphereSystem = EntitySystem.Get(); + var transform = _entMan.GetComponent(Owner); + + if (transform.GridUid is not {} gridUid) + return false; + + var atmosphereSystem = _entMan.EntitySysManager.GetEntitySystem(); + var transformSystem = _entMan.EntitySysManager.GetEntitySystem(); + + var position = transformSystem.GetGridOrMapTilePosition(Owner, transform); var minMoles = float.MaxValue; var maxMoles = 0f; - foreach (var adjacent in atmosphereSystem.GetAdjacentTileMixtures(_entMan.GetComponent(Owner).Coordinates)) + foreach (var adjacent in atmosphereSystem.GetAdjacentTileMixtures(gridUid, position)) { var moles = adjacent.TotalMoles; if (moles < minMoles) @@ -61,20 +73,25 @@ namespace Content.Server.Doors.Components public bool IsHoldingFire() { - var atmosphereSystem = EntitySystem.Get(); + var atmosphereSystem = _entMan.EntitySysManager.GetEntitySystem(); + var transformSystem = _entMan.EntitySysManager.GetEntitySystem(); - if (!atmosphereSystem.TryGetGridAndTile(_entMan.GetComponent(Owner).Coordinates, out var tuple)) + var transform = _entMan.GetComponent(Owner); + var position = transformSystem.GetGridOrMapTilePosition(Owner, transform); + + // No grid, no fun. + if (transform.GridUid is not {} gridUid) return false; - if (atmosphereSystem.GetTileMixture(tuple.Value.Grid, tuple.Value.Tile) == null) + if (atmosphereSystem.GetTileMixture(gridUid, null, position) == null) return false; - if (atmosphereSystem.IsHotspotActive(tuple.Value.Grid, tuple.Value.Tile)) + if (atmosphereSystem.IsHotspotActive(gridUid, position)) return true; - foreach (var adjacent in atmosphereSystem.GetAdjacentTiles(_entMan.GetComponent(Owner).Coordinates)) + foreach (var adjacent in atmosphereSystem.GetAdjacentTiles(gridUid, position)) { - if (atmosphereSystem.IsHotspotActive(tuple.Value.Grid, adjacent)) + if (atmosphereSystem.IsHotspotActive(gridUid, adjacent)) return true; } diff --git a/Content.Server/GameTicking/Rules/TraitorDeathMatchRuleSystem.cs b/Content.Server/GameTicking/Rules/TraitorDeathMatchRuleSystem.cs index c1934b5e6d..038e6c6293 100644 --- a/Content.Server/GameTicking/Rules/TraitorDeathMatchRuleSystem.cs +++ b/Content.Server/GameTicking/Rules/TraitorDeathMatchRuleSystem.cs @@ -19,6 +19,7 @@ using Content.Shared.MobState.Components; using Content.Shared.PDA; using Content.Shared.Roles; using Content.Shared.Traitor.Uplink; +using Robust.Server.GameObjects; using Robust.Server.Player; using Robust.Shared.Configuration; using Robust.Shared.Map; @@ -36,6 +37,8 @@ public sealed class TraitorDeathMatchRuleSystem : GameRuleSystem [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly MaxTimeRestartRuleSystem _restarter = default!; [Dependency] private readonly InventorySystem _inventory = default!; + [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; public override string Prototype => "TraitorDeathMatch"; @@ -243,10 +246,17 @@ public sealed class TraitorDeathMatchRuleSystem : GameRuleSystem _robustRandom.Shuffle(ents); var foundATarget = false; bestTarget = EntityCoordinates.Invalid; - var atmosphereSystem = EntitySystem.Get(); + foreach (var entity in ents) { - if (!atmosphereSystem.IsTileMixtureProbablySafe(Transform(entity).Coordinates)) + var transform = Transform(entity); + + if (transform.GridUid == null || transform.MapUid == null) + continue; + + var position = _transformSystem.GetGridOrMapTilePosition(entity, transform); + + if (!_atmosphereSystem.IsTileMixtureProbablySafe(transform.GridUid.Value, transform.MapUid.Value, position)) continue; var distanceFromNearest = float.PositiveInfinity; diff --git a/Content.Server/Light/EntitySystems/MatchstickSystem.cs b/Content.Server/Light/EntitySystems/MatchstickSystem.cs index 9b0fa1652a..171b07f44f 100644 --- a/Content.Server/Light/EntitySystems/MatchstickSystem.cs +++ b/Content.Server/Light/EntitySystems/MatchstickSystem.cs @@ -14,8 +14,8 @@ namespace Content.Server.Light.EntitySystems public sealed class MatchstickSystem : EntitySystem { private HashSet _litMatches = new(); - [Dependency] - private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; public override void Initialize() { @@ -38,7 +38,14 @@ namespace Content.Server.Light.EntitySystems if (match.CurrentState != SmokableState.Lit || Paused(match.Owner) || match.Deleted) continue; - _atmosphereSystem.HotspotExpose(EntityManager.GetComponent(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); } } diff --git a/Content.Server/NodeContainer/NodeGroups/PipeNet.cs b/Content.Server/NodeContainer/NodeGroups/PipeNet.cs index df362354cf..e5dd31cbc1 100644 --- a/Content.Server/NodeContainer/NodeGroups/PipeNet.cs +++ b/Content.Server/NodeContainer/NodeGroups/PipeNet.cs @@ -33,10 +33,13 @@ namespace Content.Server.NodeContainer.NodeGroups Grid = entMan.GetComponent(sourceNode.Owner).GridUid; if (Grid == null) + { Logger.Error($"Created a pipe network without an associated grid. Pipe networks currently need to be tied to a grid for amtos to work. Source entity: {entMan.ToPrettyString(sourceNode.Owner)}"); + return; + } _atmosphereSystem = entMan.EntitySysManager.GetEntitySystem(); - _atmosphereSystem.AddPipeNet(this); + _atmosphereSystem.AddPipeNet(Grid.Value, this); } public void Update() @@ -85,7 +88,11 @@ namespace Content.Server.NodeContainer.NodeGroups private void RemoveFromGridAtmos() { DebugTools.AssertNotNull(_atmosphereSystem); - _atmosphereSystem?.RemovePipeNet(this); + + if (Grid == null) + return; + + _atmosphereSystem?.RemovePipeNet(Grid.Value, this); } public override string GetDebugData() diff --git a/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs b/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs index 23552f99bf..60547510b5 100644 --- a/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs +++ b/Content.Server/Nutrition/EntitySystems/SmokingSystem.cs @@ -11,6 +11,7 @@ using Content.Shared.FixedPoint; using Content.Shared.Inventory; using Content.Shared.Smoking; using Content.Shared.Temperature; +using Robust.Server.GameObjects; using Robust.Shared.Containers; namespace Content.Server.Nutrition.EntitySystems @@ -21,8 +22,8 @@ namespace Content.Server.Nutrition.EntitySystems [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!; [Dependency] private readonly AtmosphereSystem _atmos = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly InventorySystem _inventorySystem = default!; - private const float UpdateTimer = 3f; private float _timer = 0f; @@ -79,6 +80,7 @@ namespace Content.Server.Nutrition.EntitySystems if (_timer < UpdateTimer) return; + // TODO Use an "active smoke" component instead, EntityQuery over that. foreach (var uid in _active.ToArray()) { if (!TryComp(uid, out SmokableComponent? smokable)) @@ -96,7 +98,12 @@ namespace Content.Server.Nutrition.EntitySystems if (smokable.ExposeTemperature > 0 && smokable.ExposeVolume > 0) { var transform = Transform(uid); - _atmos.HotspotExpose(transform.Coordinates, smokable.ExposeTemperature, smokable.ExposeVolume, true); + + if (transform.GridUid is {} gridUid) + { + var position = _transformSystem.GetGridOrMapTilePosition(uid, transform); + _atmos.HotspotExpose(gridUid, position, smokable.ExposeTemperature, smokable.ExposeVolume, true); + } } var inhaledSolution = _solutionContainerSystem.SplitSolution(uid, solution, smokable.InhaleAmount * _timer); diff --git a/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs b/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs index 8bc7452622..88364fa810 100644 --- a/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs +++ b/Content.Server/PneumaticCannon/PneumaticCannonSystem.cs @@ -252,7 +252,7 @@ namespace Content.Server.PneumaticCannon { // we checked for this earlier in HasGas so a GetComp is okay var gas = EntityManager.GetComponent(contained); - var environment = _atmos.GetTileMixture(EntityManager.GetComponent(comp.Owner).Coordinates, true); + var environment = _atmos.GetContainingMixture(comp.Owner, false, true); var removed = gas.RemoveAir(GetMoleUsageFromPower(comp.Power)); if (environment != null && removed != null) { diff --git a/Content.Server/RatKing/RatKingSystem.cs b/Content.Server/RatKing/RatKingSystem.cs index a4548b3932..530012cd7e 100644 --- a/Content.Server/RatKing/RatKingSystem.cs +++ b/Content.Server/RatKing/RatKingSystem.cs @@ -6,6 +6,7 @@ using Content.Server.Nutrition.Components; using Content.Server.Popups; using Content.Shared.Actions; using Content.Shared.Atmos; +using Robust.Server.GameObjects; using Robust.Shared.Player; namespace Content.Server.RatKing @@ -17,6 +18,7 @@ namespace Content.Server.RatKing [Dependency] private readonly DiseaseSystem _disease = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly AtmosphereSystem _atmos = default!; + [Dependency] private readonly TransformSystem _xform = default!; public override void Initialize() { @@ -79,9 +81,10 @@ namespace Content.Server.RatKing _popup.PopupEntity(Loc.GetString("rat-king-domain-popup"), uid, Filter.Pvs(uid)); - var tileMix = _atmos.GetTileMixture(Transform(uid).Coordinates); - if (tileMix != null) - tileMix.AdjustMoles(Gas.Miasma, component.MolesMiasmaPerDomain); + var transform = Transform(uid); + var indices = _xform.GetGridOrMapTilePosition(uid, transform); + var tileMix = _atmos.GetTileMixture(transform.GridUid, transform.MapUid, indices, true); + tileMix?.AdjustMoles(Gas.Miasma, component.MolesMiasmaPerDomain); } } diff --git a/Content.Server/StationEvents/Events/GasLeak.cs b/Content.Server/StationEvents/Events/GasLeak.cs index ceef8af090..a6b70cfdbe 100644 --- a/Content.Server/StationEvents/Events/GasLeak.cs +++ b/Content.Server/StationEvents/Events/GasLeak.cs @@ -115,7 +115,7 @@ namespace Content.Server.StationEvents.Events _timeUntilLeak += LeakCooldown; var atmosphereSystem = _entityManager.EntitySysManager.GetEntitySystem(); - + if (!_foundTile || _targetGrid == default || _entityManager.Deleted(_targetGrid) || @@ -125,7 +125,7 @@ namespace Content.Server.StationEvents.Events return; } - var environment = atmosphereSystem.GetTileMixture(_targetGrid, _targetTile, true); + var environment = atmosphereSystem.GetTileMixture(_targetGrid, null, _targetTile, true); environment?.AdjustMoles(_leakGas, LeakCooldown * _molesPerSecond); } diff --git a/Content.Server/StationEvents/Events/StationEvent.cs b/Content.Server/StationEvents/Events/StationEvent.cs index fbdec9ed98..65d8a919a3 100644 --- a/Content.Server/StationEvents/Events/StationEvent.cs +++ b/Content.Server/StationEvents/Events/StationEvent.cs @@ -227,7 +227,8 @@ namespace Content.Server.StationEvents.Events targetGrid = robustRandom.Pick(possibleTargets); - if (!entityManager.TryGetComponent(targetGrid, out var gridComp)) + if (!entityManager.TryGetComponent(targetGrid, out var gridComp) + || !entityManager.TryGetComponent(targetGrid, out var transform)) return false; var grid = gridComp.Grid; @@ -242,7 +243,9 @@ namespace Content.Server.StationEvents.Events var randomY = robustRandom.Next((int) gridBounds.Bottom, (int) gridBounds.Top); tile = new Vector2i(randomX - (int) gridPos.X, randomY - (int) gridPos.Y); - if (atmosphereSystem.IsTileSpace(grid, tile) || atmosphereSystem.IsTileAirBlocked(grid, tile)) continue; + if (atmosphereSystem.IsTileSpace(grid.GridEntityId, transform.MapUid, tile, mapGridComp:gridComp) + || atmosphereSystem.IsTileAirBlocked(grid.GridEntityId, tile, mapGridComp:gridComp)) + continue; found = true; targetCoords = grid.GridTileToLocal(tile); break; diff --git a/Content.Server/Temperature/Systems/TemperatureSystem.cs b/Content.Server/Temperature/Systems/TemperatureSystem.cs index 954f1bfdba..7c079f5439 100644 --- a/Content.Server/Temperature/Systems/TemperatureSystem.cs +++ b/Content.Server/Temperature/Systems/TemperatureSystem.cs @@ -1,3 +1,5 @@ +using System; +using System.Collections.Generic; using System.Linq; using Content.Server.Administration.Logs; using Content.Server.Atmos.Components; @@ -7,11 +9,15 @@ using Content.Shared.Alert; using Content.Shared.Damage; using Content.Shared.Database; using Content.Shared.Inventory; +using Robust.Server.GameObjects; +using Robust.Shared.GameObjects; +using Robust.Shared.IoC; namespace Content.Server.Temperature.Systems { public sealed class TemperatureSystem : EntitySystem { + [Dependency] private readonly TransformSystem _transformSystem = default!; [Dependency] private readonly DamageableSystem _damageableSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly AlertsSystem _alertsSystem = default!; @@ -94,8 +100,15 @@ namespace Content.Server.Temperature.Systems private void OnAtmosExposedUpdate(EntityUid uid, TemperatureComponent temperature, ref AtmosExposedUpdateEvent args) { + var transform = args.Transform; + + if (transform.MapUid == null) + return; + + var position = _transformSystem.GetGridOrMapTilePosition(uid, transform); + var temperatureDelta = args.GasMixture.Temperature - temperature.CurrentTemperature; - var tileHeatCapacity = _atmosphereSystem.GetTileHeatCapacity(args.Coordinates); + var tileHeatCapacity = _atmosphereSystem.GetTileHeatCapacity(transform.GridUid, transform.MapUid.Value, position); var heat = temperatureDelta * (tileHeatCapacity * temperature.HeatCapacity / (tileHeatCapacity + temperature.HeatCapacity)); ChangeHeat(uid, heat * temperature.AtmosTemperatureTransferEfficiency, temperature: temperature ); } diff --git a/Content.Server/Tools/ToolSystem.Welder.cs b/Content.Server/Tools/ToolSystem.Welder.cs index 69c387c017..108d6d11ed 100644 --- a/Content.Server/Tools/ToolSystem.Welder.cs +++ b/Content.Server/Tools/ToolSystem.Welder.cs @@ -78,9 +78,10 @@ namespace Content.Server.Tools SolutionContainerManagerComponent? solutionContainer = null, SharedItemComponent? item = null, PointLightComponent? light = null, - AppearanceComponent? appearance = null) + AppearanceComponent? appearance = null, + TransformComponent? transform = null) { - if (!Resolve(uid, ref welder, ref solutionContainer)) + if (!Resolve(uid, ref welder, ref solutionContainer, ref transform)) return false; // Optional components. @@ -113,8 +114,11 @@ namespace Content.Server.Tools SoundSystem.Play(welder.WelderOnSounds.GetSound(), Filter.Pvs(uid), uid, AudioHelpers.WithVariation(0.125f).WithVolume(-5f)); - // TODO: Use TransformComponent directly. - _atmosphereSystem.HotspotExpose(EntityManager.GetComponent(welder.Owner).Coordinates, 700, 50, true); + if (transform.GridUid is {} gridUid) + { + var position = _transformSystem.GetGridOrMapTilePosition(uid, transform); + _atmosphereSystem.HotspotExpose(gridUid, position, 700, 50, true); + } welder.Dirty(); @@ -300,17 +304,22 @@ namespace Content.Server.Tools if (_welderTimer < WelderUpdateTimer) return; + // TODO Use an "active welder" component instead, EntityQuery over that. foreach (var tool in _activeWelders.ToArray()) { if (!EntityManager.TryGetComponent(tool, out WelderComponent? welder) - || !EntityManager.TryGetComponent(tool, out SolutionContainerManagerComponent? solutionContainer)) + || !EntityManager.TryGetComponent(tool, out SolutionContainerManagerComponent? solutionContainer) + || !EntityManager.TryGetComponent(tool, out TransformComponent? transform)) continue; if (!_solutionContainerSystem.TryGetSolution(tool, welder.FuelSolution, out var solution, solutionContainer)) continue; - // TODO: Use TransformComponent directly. - _atmosphereSystem.HotspotExpose(EntityManager.GetComponent(welder.Owner).Coordinates, 700, 50, true); + if (transform.GridUid is { } gridUid) + { + var position = _transformSystem.GetGridOrMapTilePosition(tool, transform); + _atmosphereSystem.HotspotExpose(gridUid, position, 700, 50, true); + } solution.RemoveReagent(welder.FuelReagent, welder.FuelConsumption * _welderTimer); diff --git a/Content.Server/Tools/ToolSystem.cs b/Content.Server/Tools/ToolSystem.cs index 7ea23d166a..6302698050 100644 --- a/Content.Server/Tools/ToolSystem.cs +++ b/Content.Server/Tools/ToolSystem.cs @@ -6,6 +6,7 @@ using Content.Server.DoAfter; using Content.Server.Popups; using Content.Shared.Audio; using Content.Shared.Tools.Components; +using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Map; using Robust.Shared.Player; @@ -23,6 +24,7 @@ namespace Content.Server.Tools [Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; public override void Initialize() diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/GasArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/GasArtifactSystem.cs index 551911056d..5ac8c76026 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/GasArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/GasArtifactSystem.cs @@ -40,7 +40,7 @@ public sealed class GasArtifactSystem : EntitySystem var transform = Transform(uid); - var environment = _atmosphereSystem.GetTileMixture(transform.Coordinates, true); + var environment = _atmosphereSystem.GetContainingMixture(uid, false, true); if (environment == null) return; diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/TemperatureArtifactSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/TemperatureArtifactSystem.cs index 5a2a71124a..f124ad1caa 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/TemperatureArtifactSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Effects/Systems/TemperatureArtifactSystem.cs @@ -2,12 +2,14 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Components; using Content.Server.Xenoarchaeology.XenoArtifacts.Events; +using Robust.Server.GameObjects; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Effects.Systems; public sealed class TemperatureArtifactSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; public override void Initialize() { @@ -19,14 +21,16 @@ public sealed class TemperatureArtifactSystem : EntitySystem { var transform = Transform(uid); - var center = _atmosphereSystem.GetTileMixture(transform.Coordinates, true); + var center = _atmosphereSystem.GetContainingMixture(uid, false, true); if (center == null) return; UpdateTileTemperature(component, center); - if (component.EffectAdjacentTiles) + if (component.EffectAdjacentTiles && transform.GridUid != null) { - var adjacent = _atmosphereSystem.GetAdjacentTileMixtures(transform.Coordinates, invalidate: true); + var adjacent = _atmosphereSystem.GetAdjacentTileMixtures(transform.GridUid.Value, + _transformSystem.GetGridOrMapTilePosition(uid, transform), excite: true); + foreach (var mixture in adjacent) { UpdateTileTemperature(component, mixture); diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactGasTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactGasTriggerSystem.cs index 3cd16bb503..b773c71bea 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactGasTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactGasTriggerSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Atmos.EntitySystems; using Content.Server.Xenoarchaeology.XenoArtifacts.Events; using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; +using Robust.Server.GameObjects; using Robust.Shared.Random; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; @@ -10,6 +11,7 @@ public sealed class ArtifactGasTriggerSystem : EntitySystem [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly ArtifactSystem _artifactSystem = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; public override void Initialize() { @@ -32,10 +34,14 @@ public sealed class ArtifactGasTriggerSystem : EntitySystem var query = EntityManager.EntityQuery(); foreach (var (trigger, transform) in query) { + var uid = trigger.Owner; + if (trigger.ActivationGas == null) continue; - var environment = _atmosphereSystem.GetTileMixture(transform.Coordinates); + var environment = _atmosphereSystem.GetTileMixture(transform.GridUid, transform.MapUid, + _transformSystem.GetGridOrMapTilePosition(uid, transform)); + if (environment == null) continue; diff --git a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactHeatTriggerSystem.cs b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactHeatTriggerSystem.cs index 0e3d3d65ce..ff486deade 100644 --- a/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactHeatTriggerSystem.cs +++ b/Content.Server/Xenoarchaeology/XenoArtifacts/Triggers/Systems/ArtifactHeatTriggerSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Components; using Content.Shared.Interaction; using Content.Shared.Temperature; using Content.Shared.Weapons.Melee; +using Robust.Server.GameObjects; namespace Content.Server.Xenoarchaeology.XenoArtifacts.Triggers.Systems; @@ -10,6 +11,7 @@ public sealed class ArtifactHeatTriggerSystem : EntitySystem { [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly ArtifactSystem _artifactSystem = default!; + [Dependency] private readonly TransformSystem _transformSystem = default!; public override void Initialize() { @@ -25,7 +27,9 @@ public sealed class ArtifactHeatTriggerSystem : EntitySystem var query = EntityManager.EntityQuery(); foreach (var (trigger, transform, artifact) in query) { - var environment = _atmosphereSystem.GetTileMixture(transform.Coordinates); + var uid = trigger.Owner; + var environment = _atmosphereSystem.GetTileMixture(transform.GridUid, transform.MapUid, + _transformSystem.GetGridOrMapTilePosition(uid, transform)); if (environment == null) continue; diff --git a/Content.Shared/Atmos/EntitySystems/SharedAtmosDebugOverlaySystem.cs b/Content.Shared/Atmos/EntitySystems/SharedAtmosDebugOverlaySystem.cs index 21394f7ab5..4abdc2131f 100644 --- a/Content.Shared/Atmos/EntitySystems/SharedAtmosDebugOverlaySystem.cs +++ b/Content.Shared/Atmos/EntitySystems/SharedAtmosDebugOverlaySystem.cs @@ -16,10 +16,11 @@ namespace Content.Shared.Atmos.EntitySystems public readonly float[] Moles; public readonly AtmosDirection PressureDirection; public readonly AtmosDirection LastPressureDirection; - public readonly bool InExcitedGroup; + public readonly int InExcitedGroup; public readonly AtmosDirection BlockDirection; + public readonly bool IsSpace; - public AtmosDebugOverlayData(float temperature, float[] moles, AtmosDirection pressureDirection, AtmosDirection lastPressureDirection, bool inExcited, AtmosDirection blockDirection) + public AtmosDebugOverlayData(float temperature, float[] moles, AtmosDirection pressureDirection, AtmosDirection lastPressureDirection, int inExcited, AtmosDirection blockDirection, bool isSpace) { Temperature = temperature; Moles = moles; @@ -27,6 +28,7 @@ namespace Content.Shared.Atmos.EntitySystems LastPressureDirection = lastPressureDirection; InExcitedGroup = inExcited; BlockDirection = blockDirection; + IsSpace = isSpace; } } diff --git a/SpaceStation14.sln.DotSettings b/SpaceStation14.sln.DotSettings index 8e00544248..21399c2074 100644 --- a/SpaceStation14.sln.DotSettings +++ b/SpaceStation14.sln.DotSettings @@ -524,6 +524,7 @@ public sealed class $CLASS$ : Shared$CLASS$ { True True True + True True True True