Partial atmos refactor (#22521)
* Reduce atmos component queries * Remove method events * Cache airtight data * Make MolesArchived nullable * Fix airtight cache * only get tile def once * Immutable mixtures * firelock queries * misc * misc cleanup * Trim disconnected tiles * Fix merge issues and bugs * Why does the PR keep increasing in scope * debug overlay * Fix bugs * Fix test, remove unused events * Add setmapatmos command * Fix overlays * Add map check * A * Resolve conflicts with #26102 * Remove some obsolete methods
This commit is contained in:
@@ -8,7 +8,6 @@ using Robust.Client.GameObjects;
|
|||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.ResourceManagement;
|
using Robust.Client.ResourceManagement;
|
||||||
using Robust.Shared.Enums;
|
using Robust.Shared.Enums;
|
||||||
using Robust.Shared.Graphics;
|
|
||||||
using Robust.Shared.Graphics.RSI;
|
using Robust.Shared.Graphics.RSI;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
@@ -23,7 +22,7 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
private readonly IEntityManager _entManager;
|
private readonly IEntityManager _entManager;
|
||||||
private readonly IMapManager _mapManager;
|
private readonly IMapManager _mapManager;
|
||||||
|
|
||||||
public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities;
|
public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities | OverlaySpace.WorldSpaceBelowWorld;
|
||||||
private readonly ShaderInstance _shader;
|
private readonly ShaderInstance _shader;
|
||||||
|
|
||||||
// Gas overlays
|
// Gas overlays
|
||||||
@@ -79,7 +78,8 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
var rsi = resourceCache.GetResource<RSIResource>(animated.RsiPath).RSI;
|
var rsi = resourceCache.GetResource<RSIResource>(animated.RsiPath).RSI;
|
||||||
var stateId = animated.RsiState;
|
var stateId = animated.RsiState;
|
||||||
|
|
||||||
if (!rsi.TryGetState(stateId, out var state)) continue;
|
if (!rsi.TryGetState(stateId, out var state))
|
||||||
|
continue;
|
||||||
|
|
||||||
_frames[i] = state.GetFrames(RsiDirection.South);
|
_frames[i] = state.GetFrames(RsiDirection.South);
|
||||||
_frameDelays[i] = state.GetDelays();
|
_frameDelays[i] = state.GetDelays();
|
||||||
@@ -111,7 +111,8 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
for (var i = 0; i < _gasCount; i++)
|
for (var i = 0; i < _gasCount; i++)
|
||||||
{
|
{
|
||||||
var delays = _frameDelays[i];
|
var delays = _frameDelays[i];
|
||||||
if (delays.Length == 0) continue;
|
if (delays.Length == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
var frameCount = _frameCounter[i];
|
var frameCount = _frameCounter[i];
|
||||||
_timer[i] += args.DeltaSeconds;
|
_timer[i] += args.DeltaSeconds;
|
||||||
@@ -127,7 +128,8 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
for (var i = 0; i < FireStates; i++)
|
for (var i = 0; i < FireStates; i++)
|
||||||
{
|
{
|
||||||
var delays = _fireFrameDelays[i];
|
var delays = _fireFrameDelays[i];
|
||||||
if (delays.Length == 0) continue;
|
if (delays.Length == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
var frameCount = _fireFrameCounter[i];
|
var frameCount = _fireFrameCounter[i];
|
||||||
_fireTimer[i] += args.DeltaSeconds;
|
_fireTimer[i] += args.DeltaSeconds;
|
||||||
@@ -161,26 +163,10 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
var mapUid = _mapManager.GetMapEntityId(args.MapId);
|
var mapUid = _mapManager.GetMapEntityId(args.MapId);
|
||||||
|
|
||||||
if (_entManager.TryGetComponent<MapAtmosphereComponent>(mapUid, out var atmos))
|
if (_entManager.TryGetComponent<MapAtmosphereComponent>(mapUid, out var atmos))
|
||||||
{
|
DrawMapOverlay(drawHandle, args, mapUid, atmos);
|
||||||
var bottomLeft = args.WorldAABB.BottomLeft.Floored();
|
|
||||||
var topRight = args.WorldAABB.TopRight.Ceiled();
|
|
||||||
|
|
||||||
for (var x = bottomLeft.X; x <= topRight.X; x++)
|
if (args.Space != OverlaySpace.WorldSpaceEntities)
|
||||||
{
|
return;
|
||||||
for (var y = bottomLeft.Y; y <= topRight.Y; y++)
|
|
||||||
{
|
|
||||||
var tilePosition = new Vector2(x, y);
|
|
||||||
|
|
||||||
for (var i = 0; i < atmos.OverlayData.Opacity.Length; i++)
|
|
||||||
{
|
|
||||||
var opacity = atmos.OverlayData.Opacity[i];
|
|
||||||
|
|
||||||
if (opacity > 0)
|
|
||||||
args.WorldHandle.DrawTexture(_frames[i][_frameCounter[i]], tilePosition, Color.White.WithAlpha(opacity));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: WorldBounds callback.
|
// TODO: WorldBounds callback.
|
||||||
_mapManager.FindGridsIntersecting(args.MapId, args.WorldAABB, ref gridState,
|
_mapManager.FindGridsIntersecting(args.MapId, args.WorldAABB, ref gridState,
|
||||||
@@ -265,5 +251,41 @@ namespace Content.Client.Atmos.Overlays
|
|||||||
drawHandle.UseShader(null);
|
drawHandle.UseShader(null);
|
||||||
drawHandle.SetTransform(Matrix3.Identity);
|
drawHandle.SetTransform(Matrix3.Identity);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void DrawMapOverlay(
|
||||||
|
DrawingHandleWorld handle,
|
||||||
|
OverlayDrawArgs args,
|
||||||
|
EntityUid map,
|
||||||
|
MapAtmosphereComponent atmos)
|
||||||
|
{
|
||||||
|
var mapGrid = _entManager.HasComponent<MapGridComponent>(map);
|
||||||
|
|
||||||
|
// map-grid atmospheres get drawn above grids
|
||||||
|
if (mapGrid && args.Space != OverlaySpace.WorldSpaceEntities)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Normal map atmospheres get drawn below grids
|
||||||
|
if (!mapGrid && args.Space != OverlaySpace.WorldSpaceBelowWorld)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var bottomLeft = args.WorldAABB.BottomLeft.Floored();
|
||||||
|
var topRight = args.WorldAABB.TopRight.Ceiled();
|
||||||
|
|
||||||
|
for (var x = bottomLeft.X; x <= topRight.X; x++)
|
||||||
|
{
|
||||||
|
for (var y = bottomLeft.Y; y <= topRight.Y; y++)
|
||||||
|
{
|
||||||
|
var tilePosition = new Vector2(x, y);
|
||||||
|
|
||||||
|
for (var i = 0; i < atmos.OverlayData.Opacity.Length; i++)
|
||||||
|
{
|
||||||
|
var opacity = atmos.OverlayData.Opacity[i];
|
||||||
|
|
||||||
|
if (opacity > 0)
|
||||||
|
handle.DrawTexture(_frames[i][_frameCounter[i]], tilePosition, Color.White.WithAlpha(opacity));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public sealed partial class MappingSystem : EntitySystem
|
|||||||
if (tileDef is not ContentTileDefinition contentTileDef)
|
if (tileDef is not ContentTileDefinition contentTileDef)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var tileIcon = contentTileDef.IsSpace
|
var tileIcon = contentTileDef.MapAtmosphere
|
||||||
? _spaceIcon
|
? _spaceIcon
|
||||||
: new Texture(contentTileDef.Sprite!.Value);
|
: new Texture(contentTileDef.Sprite!.Value);
|
||||||
|
|
||||||
|
|||||||
@@ -128,7 +128,7 @@ namespace Content.IntegrationTests.Tests.Body
|
|||||||
metaSys.Update(1.0f);
|
metaSys.Update(1.0f);
|
||||||
metaSys.Update(1.0f);
|
metaSys.Update(1.0f);
|
||||||
respSys.Update(2.0f);
|
respSys.Update(2.0f);
|
||||||
Assert.That(GetMapMoles(), Is.EqualTo(startingMoles).Within(0.0001));
|
Assert.That(GetMapMoles(), Is.EqualTo(startingMoles).Within(0.0002));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1006,15 +1006,10 @@ public abstract partial class InteractionTest
|
|||||||
await Server.WaitPost(() =>
|
await Server.WaitPost(() =>
|
||||||
{
|
{
|
||||||
var atmosSystem = SEntMan.System<AtmosphereSystem>();
|
var atmosSystem = SEntMan.System<AtmosphereSystem>();
|
||||||
var atmos = SEntMan.EnsureComponent<MapAtmosphereComponent>(target);
|
|
||||||
var moles = new float[Atmospherics.AdjustedNumberOfGases];
|
var moles = new float[Atmospherics.AdjustedNumberOfGases];
|
||||||
moles[(int) Gas.Oxygen] = 21.824779f;
|
moles[(int) Gas.Oxygen] = 21.824779f;
|
||||||
moles[(int) Gas.Nitrogen] = 82.10312f;
|
moles[(int) Gas.Nitrogen] = 82.10312f;
|
||||||
atmosSystem.SetMapAtmosphere(target, false, new GasMixture(2500)
|
atmosSystem.SetMapAtmosphere(target, false, new GasMixture(moles, Atmospherics.T20C));
|
||||||
{
|
|
||||||
Temperature = 293.15f,
|
|
||||||
Moles = moles,
|
|
||||||
}, atmos);
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
94
Content.Server/Atmos/Commands/SetMapAtmosCommand.cs
Normal file
94
Content.Server/Atmos/Commands/SetMapAtmosCommand.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
using Content.Server.Administration;
|
||||||
|
using Content.Server.Atmos.Components;
|
||||||
|
using Content.Server.Atmos.EntitySystems;
|
||||||
|
using Content.Shared.Administration;
|
||||||
|
using Content.Shared.Atmos;
|
||||||
|
using Robust.Shared.Console;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
|
namespace Content.Server.Atmos.Commands;
|
||||||
|
|
||||||
|
[AdminCommand(AdminFlags.Admin)]
|
||||||
|
public sealed class AddMapAtmosCommand : LocalizedCommands
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entities = default!;
|
||||||
|
[Dependency] private readonly IMapManager _map = default!;
|
||||||
|
|
||||||
|
private const string _cmd = "cmd-set-map-atmos";
|
||||||
|
public override string Command => "setmapatmos";
|
||||||
|
public override string Description => Loc.GetString($"{_cmd}-desc");
|
||||||
|
public override string Help => Loc.GetString($"{_cmd}-help");
|
||||||
|
|
||||||
|
public override void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length < 2)
|
||||||
|
{
|
||||||
|
shell.WriteLine(Help);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
int.TryParse(args[0], out var id);
|
||||||
|
var map = _map.GetMapEntityId(new MapId(id));
|
||||||
|
if (!map.IsValid())
|
||||||
|
{
|
||||||
|
shell.WriteError(Loc.GetString("cmd-parse-failure-mapid", ("arg", args[0])));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!bool.TryParse(args[1], out var space))
|
||||||
|
{
|
||||||
|
shell.WriteError(Loc.GetString("cmd-parse-failure-bool", ("arg", args[1])));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (space || args.Length < 4)
|
||||||
|
{
|
||||||
|
_entities.RemoveComponent<MapAtmosphereComponent>(map);
|
||||||
|
shell.WriteLine(Loc.GetString($"{_cmd}-removed", ("map", id)));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!float.TryParse(args[2], out var temp))
|
||||||
|
{
|
||||||
|
shell.WriteError(Loc.GetString("cmd-parse-failure-float", ("arg", args[2])));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var mix = new GasMixture(Atmospherics.CellVolume) {Temperature = Math.Min(temp, Atmospherics.TCMB)};
|
||||||
|
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||||
|
{
|
||||||
|
if (args.Length == 3 + i)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!float.TryParse(args[3+i], out var moles))
|
||||||
|
{
|
||||||
|
shell.WriteError(Loc.GetString("cmd-parse-failure-float", ("arg", args[3+i])));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mix.AdjustMoles(i, moles);
|
||||||
|
}
|
||||||
|
|
||||||
|
var atmos = _entities.EntitySysManager.GetEntitySystem<AtmosphereSystem>();
|
||||||
|
atmos.SetMapAtmosphere(map, space, mix);
|
||||||
|
shell.WriteLine(Loc.GetString($"{_cmd}-updated", ("map", id)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
|
||||||
|
{
|
||||||
|
if (args.Length == 1)
|
||||||
|
return CompletionResult.FromHintOptions(CompletionHelper.MapIds(_entities), Loc.GetString($"{_cmd}-hint-map"));
|
||||||
|
|
||||||
|
if (args.Length == 2)
|
||||||
|
return CompletionResult.FromHintOptions(new[]{ "false", "true"}, Loc.GetString($"{_cmd}-hint-space"));
|
||||||
|
|
||||||
|
if (!bool.TryParse(args[1], out var space) || space)
|
||||||
|
return CompletionResult.Empty;
|
||||||
|
|
||||||
|
if (args.Length == 3)
|
||||||
|
return CompletionResult.FromHint(Loc.GetString($"{_cmd}-hint-temp"));
|
||||||
|
|
||||||
|
var gas = (Gas) args.Length - 4;
|
||||||
|
return CompletionResult.FromHint(Loc.GetString($"{_cmd}-hint-gas" , ("gas", gas.ToString())));
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,9 +1,10 @@
|
|||||||
|
using Content.Server.Atmos.EntitySystems;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.Components
|
namespace Content.Server.Atmos.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent, Access(typeof(AirtightSystem))]
|
||||||
public sealed partial class AirtightComponent : Component
|
public sealed partial class AirtightComponent : Component
|
||||||
{
|
{
|
||||||
public (EntityUid Grid, Vector2i Tile) LastPosition { get; set; }
|
public (EntityUid Grid, Vector2i Tile) LastPosition { get; set; }
|
||||||
@@ -29,6 +30,7 @@ namespace Content.Server.Atmos.Components
|
|||||||
[DataField("noAirWhenFullyAirBlocked")]
|
[DataField("noAirWhenFullyAirBlocked")]
|
||||||
public bool NoAirWhenFullyAirBlocked { get; set; } = true;
|
public bool NoAirWhenFullyAirBlocked { get; set; } = true;
|
||||||
|
|
||||||
|
[Access(Other = AccessPermissions.ReadWriteExecute)]
|
||||||
public AtmosDirection AirBlockedDirection => (AtmosDirection)CurrentAirBlockedDirection;
|
public AtmosDirection AirBlockedDirection => (AtmosDirection)CurrentAirBlockedDirection;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,9 @@ namespace Content.Server.Atmos.Components
|
|||||||
[IncludeDataField(customTypeSerializer:typeof(TileAtmosCollectionSerializer))]
|
[IncludeDataField(customTypeSerializer:typeof(TileAtmosCollectionSerializer))]
|
||||||
public Dictionary<Vector2i, TileAtmosphere> Tiles = new(1000);
|
public Dictionary<Vector2i, TileAtmosphere> Tiles = new(1000);
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public HashSet<TileAtmosphere> MapTiles = new(1000);
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public readonly HashSet<TileAtmosphere> ActiveTiles = new(1000);
|
public readonly HashSet<TileAtmosphere> ActiveTiles = new(1000);
|
||||||
|
|
||||||
@@ -80,7 +83,10 @@ namespace Content.Server.Atmos.Components
|
|||||||
public readonly HashSet<Vector2i> InvalidatedCoords = new(1000);
|
public readonly HashSet<Vector2i> InvalidatedCoords = new(1000);
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public readonly Queue<Vector2i> CurrentRunInvalidatedCoordinates = new();
|
public readonly Queue<TileAtmosphere> CurrentRunInvalidatedTiles = new();
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public readonly List<TileAtmosphere> PossiblyDisconnectedTiles = new(100);
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public int InvalidatedCoordsCount => InvalidatedCoords.Count;
|
public int InvalidatedCoordsCount => InvalidatedCoords.Count;
|
||||||
|
|||||||
@@ -12,12 +12,14 @@ public sealed partial class MapAtmosphereComponent : SharedMapAtmosphereComponen
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// The default GasMixture a map will have. Space mixture by default.
|
/// The default GasMixture a map will have. Space mixture by default.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("mixture"), ViewVariables(VVAccess.ReadWrite)]
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
public GasMixture? Mixture = GasMixture.SpaceGas;
|
public GasMixture Mixture = GasMixture.SpaceGas;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Whether empty tiles will be considered space or not.
|
/// Whether empty tiles will be considered space or not.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("space"), ViewVariables(VVAccess.ReadWrite)]
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
public bool Space = true;
|
public bool Space = true;
|
||||||
|
|
||||||
|
public SharedGasTileOverlaySystem.GasOverlayData Overlay;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@ using Content.Server.Atmos.Components;
|
|||||||
using Content.Server.Explosion.EntitySystems;
|
using Content.Server.Explosion.EntitySystems;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.EntitySystems
|
namespace Content.Server.Atmos.EntitySystems
|
||||||
@@ -10,7 +9,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public sealed class AirtightSystem : EntitySystem
|
public sealed class AirtightSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||||
[Dependency] private readonly ExplosionSystem _explosionSystem = default!;
|
[Dependency] private readonly ExplosionSystem _explosionSystem = default!;
|
||||||
|
|
||||||
@@ -121,19 +120,16 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
if (!xform.Anchored || !TryComp(xform.GridUid, out MapGridComponent? grid))
|
if (!xform.Anchored || !TryComp(xform.GridUid, out MapGridComponent? grid))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
airtight.LastPosition = (xform.GridUid.Value, grid.TileIndicesFor(xform.Coordinates));
|
var indices = _transform.GetGridTilePositionOrDefault((ent, xform), grid);
|
||||||
InvalidatePosition(airtight.LastPosition.Item1, airtight.LastPosition.Item2, airtight.FixVacuum && !airtight.AirBlocked);
|
airtight.LastPosition = (xform.GridUid.Value, indices);
|
||||||
|
InvalidatePosition((xform.GridUid.Value, grid), indices);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InvalidatePosition(EntityUid gridId, Vector2i pos, bool fixVacuum = false)
|
public void InvalidatePosition(Entity<MapGridComponent?> grid, Vector2i pos)
|
||||||
{
|
{
|
||||||
if (!TryComp(gridId, out MapGridComponent? grid))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var query = EntityManager.GetEntityQuery<AirtightComponent>();
|
var query = EntityManager.GetEntityQuery<AirtightComponent>();
|
||||||
_explosionSystem.UpdateAirtightMap(gridId, pos, grid, query);
|
_explosionSystem.UpdateAirtightMap(grid, pos, grid, query);
|
||||||
// TODO make atmos system use query
|
_atmosphereSystem.InvalidateTile(grid.Owner, pos);
|
||||||
_atmosphereSystem.InvalidateTile(gridId, pos);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private AtmosDirection Rotate(AtmosDirection myDirection, Angle myAngle)
|
private AtmosDirection Rotate(AtmosDirection myDirection, Angle myAngle)
|
||||||
|
|||||||
@@ -97,22 +97,19 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private AtmosDebugOverlayData ConvertTileToData(TileAtmosphere? tile)
|
private AtmosDebugOverlayData? ConvertTileToData(TileAtmosphere tile)
|
||||||
{
|
{
|
||||||
if (tile == null)
|
|
||||||
return default;
|
|
||||||
|
|
||||||
return new AtmosDebugOverlayData(
|
return new AtmosDebugOverlayData(
|
||||||
tile.GridIndices,
|
tile.GridIndices,
|
||||||
tile.Air?.Temperature ?? default,
|
tile.Air?.Temperature ?? default,
|
||||||
tile.Air?.Moles,
|
tile.Air?.Moles,
|
||||||
tile.PressureDirection,
|
tile.PressureDirection,
|
||||||
tile.LastPressureDirection,
|
tile.LastPressureDirection,
|
||||||
tile.BlockedAirflow,
|
tile.AirtightData.BlockedDirections,
|
||||||
tile.ExcitedGroup?.GetHashCode(),
|
tile.ExcitedGroup?.GetHashCode(),
|
||||||
tile.Space,
|
tile.Space,
|
||||||
false,
|
tile.MapAtmosphere,
|
||||||
false);
|
tile.NoGridTile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using Content.Server.Atmos.Components;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Map.Enumerators;
|
|
||||||
|
|
||||||
namespace Content.Server.Atmos.EntitySystems;
|
|
||||||
|
|
||||||
public struct AtmosObstructionEnumerator
|
|
||||||
{
|
|
||||||
private AnchoredEntitiesEnumerator _enumerator;
|
|
||||||
private EntityQuery<AirtightComponent> _query;
|
|
||||||
|
|
||||||
public AtmosObstructionEnumerator(AnchoredEntitiesEnumerator enumerator, EntityQuery<AirtightComponent> query)
|
|
||||||
{
|
|
||||||
_enumerator = enumerator;
|
|
||||||
_query = query;
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool MoveNext([NotNullWhen(true)] out AirtightComponent? airtight)
|
|
||||||
{
|
|
||||||
if (!_enumerator.MoveNext(out var uid))
|
|
||||||
{
|
|
||||||
airtight = null;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// No rider, it makes it uglier.
|
|
||||||
// ReSharper disable once ConvertIfStatementToReturnStatement
|
|
||||||
if (!_query.TryGetComponent(uid.Value, out airtight))
|
|
||||||
{
|
|
||||||
// ReSharper disable once TailRecursiveCall
|
|
||||||
return MoveNext(out airtight);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -85,10 +85,10 @@ public partial class AtmosphereSystem
|
|||||||
return ev.Mixtures!;
|
return ev.Mixtures!;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void InvalidateTile(EntityUid gridUid, Vector2i tile)
|
public void InvalidateTile(Entity<GridAtmosphereComponent?> entity, Vector2i tile)
|
||||||
{
|
{
|
||||||
var ev = new InvalidateTileMethodEvent(gridUid, tile);
|
if (_atmosQuery.Resolve(entity.Owner, ref entity.Comp, false))
|
||||||
RaiseLocalEvent(gridUid, ref ev);
|
entity.Comp.InvalidatedCoords.Add(tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GasMixture?[]? GetTileMixtures(EntityUid? gridUid, EntityUid? mapUid, List<Vector2i> tiles, bool excite = false)
|
public GasMixture?[]? GetTileMixtures(EntityUid? gridUid, EntityUid? mapUid, List<Vector2i> tiles, bool excite = false)
|
||||||
@@ -176,11 +176,11 @@ public partial class AtmosphereSystem
|
|||||||
|
|
||||||
public bool IsTileAirBlocked(EntityUid gridUid, Vector2i tile, AtmosDirection directions = AtmosDirection.All, MapGridComponent? mapGridComp = null)
|
public bool IsTileAirBlocked(EntityUid gridUid, Vector2i tile, AtmosDirection directions = AtmosDirection.All, MapGridComponent? mapGridComp = null)
|
||||||
{
|
{
|
||||||
var ev = new IsTileAirBlockedMethodEvent(gridUid, tile, directions, mapGridComp);
|
if (!Resolve(gridUid, ref mapGridComp))
|
||||||
RaiseLocalEvent(gridUid, ref ev);
|
return false;
|
||||||
|
|
||||||
// If nothing handled the event, it'll default to true.
|
var data = GetAirtightData(gridUid, mapGridComp, tile);
|
||||||
return ev.Result;
|
return data.BlockedDirections.IsFlagSet(directions);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsTileSpace(EntityUid? gridUid, EntityUid? mapUid, Vector2i tile, MapGridComponent? mapGridComp = null)
|
public bool IsTileSpace(EntityUid? gridUid, EntityUid? mapUid, Vector2i tile, MapGridComponent? mapGridComp = null)
|
||||||
@@ -231,12 +231,6 @@ public partial class AtmosphereSystem
|
|||||||
return ev.Result ?? Enumerable.Empty<GasMixture>();
|
return ev.Result ?? Enumerable.Empty<GasMixture>();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateAdjacent(EntityUid gridUid, Vector2i tile, MapGridComponent? mapGridComp = null)
|
|
||||||
{
|
|
||||||
var ev = new UpdateAdjacentMethodEvent(gridUid, tile, mapGridComp);
|
|
||||||
RaiseLocalEvent(gridUid, ref ev);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void HotspotExpose(EntityUid gridUid, Vector2i tile, float exposedTemperature, float exposedVolume,
|
public void HotspotExpose(EntityUid gridUid, Vector2i tile, float exposedTemperature, float exposedVolume,
|
||||||
EntityUid? sparkSourceUid = null, bool soh = false)
|
EntityUid? sparkSourceUid = null, bool soh = false)
|
||||||
{
|
{
|
||||||
@@ -259,12 +253,6 @@ public partial class AtmosphereSystem
|
|||||||
return ev.Result;
|
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)
|
public void AddPipeNet(EntityUid gridUid, PipeNet pipeNet)
|
||||||
{
|
{
|
||||||
var ev = new AddPipeNetMethodEvent(gridUid, pipeNet);
|
var ev = new AddPipeNetMethodEvent(gridUid, pipeNet);
|
||||||
@@ -307,9 +295,6 @@ public partial class AtmosphereSystem
|
|||||||
[ByRefEvent] private record struct GetAllMixturesMethodEvent
|
[ByRefEvent] private record struct GetAllMixturesMethodEvent
|
||||||
(EntityUid Grid, bool Excite = false, IEnumerable<GasMixture>? Mixtures = null, bool Handled = false);
|
(EntityUid Grid, bool Excite = false, IEnumerable<GasMixture>? Mixtures = null, bool Handled = false);
|
||||||
|
|
||||||
[ByRefEvent] private record struct InvalidateTileMethodEvent
|
|
||||||
(EntityUid Grid, Vector2i Tile, bool Handled = false);
|
|
||||||
|
|
||||||
[ByRefEvent] private record struct GetTileMixturesMethodEvent
|
[ByRefEvent] private record struct GetTileMixturesMethodEvent
|
||||||
(EntityUid? GridUid, EntityUid? MapUid, List<Vector2i> Tiles, bool Excite = false, GasMixture?[]? Mixtures = null, bool Handled = false);
|
(EntityUid? GridUid, EntityUid? MapUid, List<Vector2i> Tiles, bool Excite = false, GasMixture?[]? Mixtures = null, bool Handled = false);
|
||||||
|
|
||||||
@@ -319,16 +304,6 @@ public partial class AtmosphereSystem
|
|||||||
[ByRefEvent] private record struct ReactTileMethodEvent
|
[ByRefEvent] private record struct ReactTileMethodEvent
|
||||||
(EntityUid GridId, Vector2i Tile, ReactionResult Result = default, bool Handled = false);
|
(EntityUid GridId, Vector2i Tile, ReactionResult Result = default, bool Handled = false);
|
||||||
|
|
||||||
[ByRefEvent] private record struct IsTileAirBlockedMethodEvent
|
|
||||||
(EntityUid Grid, Vector2i Tile, AtmosDirection Direction = AtmosDirection.All, MapGridComponent? MapGridComponent = null, bool Result = false, bool Handled = false)
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// True if one of the enabled blockers has <see cref="AirtightComponent.NoAirWhenFullyAirBlocked"/>. Note
|
|
||||||
/// that this does not actually check if all directions are blocked.
|
|
||||||
/// </summary>
|
|
||||||
public bool NoAir = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
[ByRefEvent] private record struct IsTileSpaceMethodEvent
|
[ByRefEvent] private record struct IsTileSpaceMethodEvent
|
||||||
(EntityUid? Grid, EntityUid? Map, Vector2i Tile, MapGridComponent? MapGridComponent = null, bool Result = true, bool Handled = false);
|
(EntityUid? Grid, EntityUid? Map, Vector2i Tile, MapGridComponent? MapGridComponent = null, bool Result = true, bool Handled = false);
|
||||||
|
|
||||||
@@ -339,9 +314,6 @@ public partial class AtmosphereSystem
|
|||||||
(EntityUid Grid, Vector2i Tile, bool IncludeBlocked, bool Excite,
|
(EntityUid Grid, Vector2i Tile, bool IncludeBlocked, bool Excite,
|
||||||
IEnumerable<GasMixture>? Result = null, bool Handled = false);
|
IEnumerable<GasMixture>? Result = null, bool Handled = false);
|
||||||
|
|
||||||
[ByRefEvent] private record struct UpdateAdjacentMethodEvent
|
|
||||||
(EntityUid Grid, Vector2i Tile, MapGridComponent? MapGridComponent = null, bool Handled = false);
|
|
||||||
|
|
||||||
[ByRefEvent] private record struct HotspotExposeMethodEvent
|
[ByRefEvent] private record struct HotspotExposeMethodEvent
|
||||||
(EntityUid Grid, EntityUid? SparkSourceUid, Vector2i Tile, float ExposedTemperature, float ExposedVolume, bool soh, bool Handled = false);
|
(EntityUid Grid, EntityUid? SparkSourceUid, Vector2i Tile, float ExposedTemperature, float ExposedVolume, bool soh, bool Handled = false);
|
||||||
|
|
||||||
@@ -351,9 +323,6 @@ public partial class AtmosphereSystem
|
|||||||
[ByRefEvent] private record struct IsHotspotActiveMethodEvent
|
[ByRefEvent] private record struct IsHotspotActiveMethodEvent
|
||||||
(EntityUid Grid, Vector2i Tile, bool Result = false, bool Handled = false);
|
(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
|
[ByRefEvent] private record struct AddPipeNetMethodEvent
|
||||||
(EntityUid Grid, PipeNet PipeNet, bool Handled = false);
|
(EntityUid Grid, PipeNet PipeNet, bool Handled = false);
|
||||||
|
|
||||||
|
|||||||
@@ -72,7 +72,8 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
|
|
||||||
var tileSize = excitedGroup.Tiles.Count;
|
var tileSize = excitedGroup.Tiles.Count;
|
||||||
|
|
||||||
if (excitedGroup.Disposed) return;
|
if (excitedGroup.Disposed)
|
||||||
|
return;
|
||||||
|
|
||||||
if (tileSize == 0)
|
if (tileSize == 0)
|
||||||
{
|
{
|
||||||
@@ -98,7 +99,9 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
|
|
||||||
foreach (var tile in excitedGroup.Tiles)
|
foreach (var tile in excitedGroup.Tiles)
|
||||||
{
|
{
|
||||||
if (tile?.Air == null) continue;
|
if (tile?.Air == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
tile.Air.CopyFromMutable(combined);
|
tile.Air.CopyFromMutable(combined);
|
||||||
InvalidateVisuals(tile.GridIndex, tile.GridIndices);
|
InvalidateVisuals(tile.GridIndex, tile.GridIndices);
|
||||||
}
|
}
|
||||||
@@ -106,21 +109,23 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
excitedGroup.BreakdownCooldown = 0;
|
excitedGroup.BreakdownCooldown = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExcitedGroupDismantle(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup, bool unexcite = true)
|
/// <summary>
|
||||||
|
/// This de-activates and removes all tiles in an excited group.
|
||||||
|
/// </summary>
|
||||||
|
private void DeactivateGroupTiles(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup)
|
||||||
{
|
{
|
||||||
foreach (var tile in excitedGroup.Tiles)
|
foreach (var tile in excitedGroup.Tiles)
|
||||||
{
|
{
|
||||||
tile.ExcitedGroup = null;
|
tile.ExcitedGroup = null;
|
||||||
|
|
||||||
if (!unexcite)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
RemoveActiveTile(gridAtmosphere, tile);
|
RemoveActiveTile(gridAtmosphere, tile);
|
||||||
}
|
}
|
||||||
|
|
||||||
excitedGroup.Tiles.Clear();
|
excitedGroup.Tiles.Clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This removes an excited group without de-activating its tiles.
|
||||||
|
/// </summary>
|
||||||
private void ExcitedGroupDispose(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup)
|
private void ExcitedGroupDispose(GridAtmosphereComponent gridAtmosphere, ExcitedGroup excitedGroup)
|
||||||
{
|
{
|
||||||
if (excitedGroup.Disposed)
|
if (excitedGroup.Disposed)
|
||||||
@@ -129,9 +134,14 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(excitedGroup), "Grid Atmosphere does not contain Excited Group!");
|
DebugTools.Assert(gridAtmosphere.ExcitedGroups.Contains(excitedGroup), "Grid Atmosphere does not contain Excited Group!");
|
||||||
|
|
||||||
excitedGroup.Disposed = true;
|
excitedGroup.Disposed = true;
|
||||||
|
|
||||||
gridAtmosphere.ExcitedGroups.Remove(excitedGroup);
|
gridAtmosphere.ExcitedGroups.Remove(excitedGroup);
|
||||||
ExcitedGroupDismantle(gridAtmosphere, excitedGroup, false);
|
|
||||||
|
foreach (var tile in excitedGroup.Tiles)
|
||||||
|
{
|
||||||
|
tile.ExcitedGroup = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
excitedGroup.Tiles.Clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ public sealed partial class AtmosphereSystem
|
|||||||
private void InitializeGridAtmosphere()
|
private void InitializeGridAtmosphere()
|
||||||
{
|
{
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, ComponentInit>(OnGridAtmosphereInit);
|
SubscribeLocalEvent<GridAtmosphereComponent, ComponentInit>(OnGridAtmosphereInit);
|
||||||
|
SubscribeLocalEvent<GridAtmosphereComponent, ComponentStartup>(OnGridAtmosphereStartup);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, ComponentRemove>(OnAtmosphereRemove);
|
SubscribeLocalEvent<GridAtmosphereComponent, ComponentRemove>(OnAtmosphereRemove);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, GridSplitEvent>(OnGridSplit);
|
SubscribeLocalEvent<GridAtmosphereComponent, GridSplitEvent>(OnGridSplit);
|
||||||
|
|
||||||
@@ -22,19 +23,15 @@ public sealed partial class AtmosphereSystem
|
|||||||
SubscribeLocalEvent<GridAtmosphereComponent, HasAtmosphereMethodEvent>(GridHasAtmosphere);
|
SubscribeLocalEvent<GridAtmosphereComponent, HasAtmosphereMethodEvent>(GridHasAtmosphere);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, IsSimulatedGridMethodEvent>(GridIsSimulated);
|
SubscribeLocalEvent<GridAtmosphereComponent, IsSimulatedGridMethodEvent>(GridIsSimulated);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, GetAllMixturesMethodEvent>(GridGetAllMixtures);
|
SubscribeLocalEvent<GridAtmosphereComponent, GetAllMixturesMethodEvent>(GridGetAllMixtures);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, InvalidateTileMethodEvent>(GridInvalidateTile);
|
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, GetTileMixtureMethodEvent>(GridGetTileMixture);
|
SubscribeLocalEvent<GridAtmosphereComponent, GetTileMixtureMethodEvent>(GridGetTileMixture);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, GetTileMixturesMethodEvent>(GridGetTileMixtures);
|
SubscribeLocalEvent<GridAtmosphereComponent, GetTileMixturesMethodEvent>(GridGetTileMixtures);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, ReactTileMethodEvent>(GridReactTile);
|
SubscribeLocalEvent<GridAtmosphereComponent, ReactTileMethodEvent>(GridReactTile);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, IsTileAirBlockedMethodEvent>(GridIsTileAirBlocked);
|
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, IsTileSpaceMethodEvent>(GridIsTileSpace);
|
SubscribeLocalEvent<GridAtmosphereComponent, IsTileSpaceMethodEvent>(GridIsTileSpace);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, GetAdjacentTilesMethodEvent>(GridGetAdjacentTiles);
|
SubscribeLocalEvent<GridAtmosphereComponent, GetAdjacentTilesMethodEvent>(GridGetAdjacentTiles);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, GetAdjacentTileMixturesMethodEvent>(GridGetAdjacentTileMixtures);
|
SubscribeLocalEvent<GridAtmosphereComponent, GetAdjacentTileMixturesMethodEvent>(GridGetAdjacentTileMixtures);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, UpdateAdjacentMethodEvent>(GridUpdateAdjacent);
|
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, HotspotExposeMethodEvent>(GridHotspotExpose);
|
SubscribeLocalEvent<GridAtmosphereComponent, HotspotExposeMethodEvent>(GridHotspotExpose);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, HotspotExtinguishMethodEvent>(GridHotspotExtinguish);
|
SubscribeLocalEvent<GridAtmosphereComponent, HotspotExtinguishMethodEvent>(GridHotspotExtinguish);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, IsHotspotActiveMethodEvent>(GridIsHotspotActive);
|
SubscribeLocalEvent<GridAtmosphereComponent, IsHotspotActiveMethodEvent>(GridIsHotspotActive);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, FixTileVacuumMethodEvent>(GridFixTileVacuum);
|
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, AddPipeNetMethodEvent>(GridAddPipeNet);
|
SubscribeLocalEvent<GridAtmosphereComponent, AddPipeNetMethodEvent>(GridAddPipeNet);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, RemovePipeNetMethodEvent>(GridRemovePipeNet);
|
SubscribeLocalEvent<GridAtmosphereComponent, RemovePipeNetMethodEvent>(GridRemovePipeNet);
|
||||||
SubscribeLocalEvent<GridAtmosphereComponent, AddAtmosDeviceMethodEvent>(GridAddAtmosDevice);
|
SubscribeLocalEvent<GridAtmosphereComponent, AddAtmosDeviceMethodEvent>(GridAddAtmosDevice);
|
||||||
@@ -56,22 +53,23 @@ public sealed partial class AtmosphereSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGridAtmosphereInit(EntityUid uid, GridAtmosphereComponent gridAtmosphere, ComponentInit args)
|
private void OnGridAtmosphereInit(EntityUid uid, GridAtmosphereComponent component, ComponentInit args)
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
|
EnsureComp<GasTileOverlayComponent>(uid);
|
||||||
|
foreach (var tile in component.Tiles.Values)
|
||||||
|
{
|
||||||
|
tile.GridIndex = uid;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGridAtmosphereStartup(EntityUid uid, GridAtmosphereComponent component, ComponentStartup args)
|
||||||
|
{
|
||||||
if (!TryComp(uid, out MapGridComponent? mapGrid))
|
if (!TryComp(uid, out MapGridComponent? mapGrid))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
EnsureComp<GasTileOverlayComponent>(uid);
|
InvalidateAllTiles((uid, mapGrid, component));
|
||||||
|
|
||||||
foreach (var (indices, tile) in gridAtmosphere.Tiles)
|
|
||||||
{
|
|
||||||
gridAtmosphere.InvalidatedCoords.Add(indices);
|
|
||||||
tile.GridIndex = uid;
|
|
||||||
}
|
|
||||||
|
|
||||||
GridRepopulateTiles((uid, mapGrid, gridAtmosphere));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGridSplit(EntityUid uid, GridAtmosphereComponent originalGridAtmos, ref GridSplitEvent args)
|
private void OnGridSplit(EntityUid uid, GridAtmosphereComponent originalGridAtmos, ref GridSplitEvent args)
|
||||||
@@ -104,8 +102,7 @@ public sealed partial class AtmosphereSystem
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
// Copy a bunch of data over... Not great, maybe put this in TileAtmosphere?
|
// Copy a bunch of data over... Not great, maybe put this in TileAtmosphere?
|
||||||
newTileAtmosphere.Air = tileAtmosphere.Air?.Clone() ?? null;
|
newTileAtmosphere.Air = tileAtmosphere.Air?.Clone();
|
||||||
newTileAtmosphere.MolesArchived = newTileAtmosphere.Air == null ? null : new float[Atmospherics.AdjustedNumberOfGases];
|
|
||||||
newTileAtmosphere.Hotspot = tileAtmosphere.Hotspot;
|
newTileAtmosphere.Hotspot = tileAtmosphere.Hotspot;
|
||||||
newTileAtmosphere.HeatCapacity = tileAtmosphere.HeatCapacity;
|
newTileAtmosphere.HeatCapacity = tileAtmosphere.HeatCapacity;
|
||||||
newTileAtmosphere.Temperature = tileAtmosphere.Temperature;
|
newTileAtmosphere.Temperature = tileAtmosphere.Temperature;
|
||||||
@@ -170,15 +167,6 @@ public sealed partial class AtmosphereSystem
|
|||||||
args.Handled = true;
|
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,
|
private void GridGetTileMixture(EntityUid uid, GridAtmosphereComponent component,
|
||||||
ref GetTileMixtureMethodEvent args)
|
ref GetTileMixtureMethodEvent args)
|
||||||
{
|
{
|
||||||
@@ -233,43 +221,6 @@ public sealed partial class AtmosphereSystem
|
|||||||
args.Handled = true;
|
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, 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;
|
|
||||||
args.NoAir |= obstructingComponent.NoAirWhenFullyAirBlocked;
|
|
||||||
|
|
||||||
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)
|
private void GridIsTileSpace(EntityUid uid, GridAtmosphereComponent component, ref IsTileSpaceMethodEvent args)
|
||||||
{
|
{
|
||||||
if (args.Handled)
|
if (args.Handled)
|
||||||
@@ -331,71 +282,58 @@ public sealed partial class AtmosphereSystem
|
|||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GridUpdateAdjacent(EntityUid uid, GridAtmosphereComponent component,
|
/// <summary>
|
||||||
ref UpdateAdjacentMethodEvent args)
|
/// Update array of adjacent tiles and the adjacency flags. Optionally activates all tiles with modified adjacencies.
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateAdjacentTiles(
|
||||||
|
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent,
|
||||||
|
TileAtmosphere tile,
|
||||||
|
bool activate = false)
|
||||||
{
|
{
|
||||||
if (args.Handled)
|
var uid = ent.Owner;
|
||||||
return;
|
var atmos = ent.Comp1;
|
||||||
|
var blockedDirs = tile.AirtightData.BlockedDirections;
|
||||||
var mapGridComp = args.MapGridComponent;
|
if (activate)
|
||||||
|
AddActiveTile(atmos, tile);
|
||||||
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.AdjacentBits = AtmosDirection.Invalid;
|
||||||
tile.BlockedAirflow = GetBlockedDirections(mapGridComp, tile.GridIndices);
|
|
||||||
|
|
||||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||||
{
|
{
|
||||||
var direction = (AtmosDirection) (1 << i);
|
var direction = (AtmosDirection) (1 << i);
|
||||||
|
var adjacentIndices = tile.GridIndices.Offset(direction);
|
||||||
|
|
||||||
var otherIndices = tile.GridIndices.Offset(direction);
|
TileAtmosphere? adjacent;
|
||||||
|
if (!tile.NoGridTile)
|
||||||
if (!component.Tiles.TryGetValue(otherIndices, out var adjacent))
|
|
||||||
{
|
{
|
||||||
adjacent = new TileAtmosphere(tile.GridIndex, otherIndices,
|
adjacent = GetOrNewTile(uid, atmos, adjacentIndices);
|
||||||
GetTileMixture(null, mapUid, otherIndices),
|
|
||||||
space: IsTileSpace(null, mapUid, otherIndices, mapGridComp));
|
|
||||||
}
|
}
|
||||||
|
else if (!atmos.Tiles.TryGetValue(adjacentIndices, out adjacent))
|
||||||
var oppositeDirection = direction.GetOpposite();
|
|
||||||
|
|
||||||
adjacent.BlockedAirflow = GetBlockedDirections(mapGridComp, adjacent.GridIndices);
|
|
||||||
|
|
||||||
// Pass in MapGridComponent 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.AdjacentBits &= ~direction;
|
||||||
tile.AdjacentTiles[direction.ToIndex()] = null;
|
tile.AdjacentTiles[i] = null;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var adjBlockDirs = adjacent.AirtightData.BlockedDirections;
|
||||||
|
if (activate)
|
||||||
|
AddActiveTile(atmos, adjacent);
|
||||||
|
|
||||||
|
var oppositeDirection = direction.GetOpposite();
|
||||||
|
if (adjBlockDirs.IsFlagSet(oppositeDirection) || blockedDirs.IsFlagSet(direction))
|
||||||
|
{
|
||||||
|
// Adjacency is blocked by some airtight entity.
|
||||||
|
tile.AdjacentBits &= ~direction;
|
||||||
|
adjacent.AdjacentBits &= ~oppositeDirection;
|
||||||
|
tile.AdjacentTiles[i] = null;
|
||||||
|
adjacent.AdjacentTiles[oppositeDirection.ToIndex()] = null;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// No airtight entity in the way.
|
||||||
|
tile.AdjacentBits |= direction;
|
||||||
|
adjacent.AdjacentBits |= oppositeDirection;
|
||||||
|
tile.AdjacentTiles[i] = adjacent;
|
||||||
|
adjacent.AdjacentTiles[oppositeDirection.ToIndex()] = tile;
|
||||||
}
|
}
|
||||||
|
|
||||||
DebugTools.Assert(!(tile.AdjacentBits.IsFlagSet(direction) ^
|
DebugTools.Assert(!(tile.AdjacentBits.IsFlagSet(direction) ^
|
||||||
@@ -409,6 +347,16 @@ public sealed partial class AtmosphereSystem
|
|||||||
tile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
|
tile.MonstermosInfo.CurrentTransferDirection = AtmosDirection.Invalid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private (GasMixture Air, bool IsSpace) GetDefaultMapAtmosphere(MapAtmosphereComponent? map)
|
||||||
|
{
|
||||||
|
if (map == null)
|
||||||
|
return (GasMixture.SpaceGas, true);
|
||||||
|
|
||||||
|
var air = map.Mixture;
|
||||||
|
DebugTools.Assert(air.Immutable);
|
||||||
|
return (air, map.Space);
|
||||||
|
}
|
||||||
|
|
||||||
private void GridHotspotExpose(EntityUid uid, GridAtmosphereComponent component, ref HotspotExposeMethodEvent args)
|
private void GridHotspotExpose(EntityUid uid, GridAtmosphereComponent component, ref HotspotExposeMethodEvent args)
|
||||||
{
|
{
|
||||||
if (args.Handled)
|
if (args.Handled)
|
||||||
@@ -451,54 +399,50 @@ public sealed partial class AtmosphereSystem
|
|||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GridFixTileVacuum(EntityUid uid, GridAtmosphereComponent component, ref FixTileVacuumMethodEvent args)
|
private void GridFixTileVacuum(
|
||||||
|
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent,
|
||||||
|
TileAtmosphere tile,
|
||||||
|
float volume)
|
||||||
{
|
{
|
||||||
if (args.Handled)
|
DebugTools.AssertNotNull(tile.Air);
|
||||||
return;
|
DebugTools.Assert(tile.Air?.Immutable == false );
|
||||||
|
Array.Clear(tile.MolesArchived);
|
||||||
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<MapGridComponent>(uid, out var mapGridComp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var adjacent = adjEv.Result!.ToArray();
|
|
||||||
|
|
||||||
// Return early, let's not cause any funny NaNs or needless vacuums.
|
|
||||||
if (adjacent.Length == 0)
|
|
||||||
return;
|
|
||||||
|
|
||||||
tile.Air = new GasMixture
|
|
||||||
{
|
|
||||||
Volume = GetVolumeForTiles(mapGridComp, 1),
|
|
||||||
Temperature = Atmospherics.T20C
|
|
||||||
};
|
|
||||||
|
|
||||||
tile.MolesArchived = new float[Atmospherics.AdjustedNumberOfGases];
|
|
||||||
tile.ArchivedCycle = 0;
|
tile.ArchivedCycle = 0;
|
||||||
|
|
||||||
var ratio = 1f / adjacent.Length;
|
var count = 0;
|
||||||
|
foreach (var adj in tile.AdjacentTiles)
|
||||||
|
{
|
||||||
|
if (adj?.Air != null)
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ratio = 1f / count;
|
||||||
var totalTemperature = 0f;
|
var totalTemperature = 0f;
|
||||||
|
|
||||||
foreach (var adj in adjacent)
|
foreach (var adj in tile.AdjacentTiles)
|
||||||
{
|
{
|
||||||
|
if (adj?.Air == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
totalTemperature += adj.Temperature;
|
totalTemperature += adj.Temperature;
|
||||||
|
|
||||||
|
// TODO ATMOS. Why is this removing and then re-adding air to the neighbouring tiles?
|
||||||
|
// Is it some rounding issue to do with Atmospherics.GasMinMoles? because otherwise this is just unnecessary.
|
||||||
|
// if we get rid of this, then this could also just add moles and then multiply by ratio at the end, rather
|
||||||
|
// than having to iterate over adjacent tiles twice.
|
||||||
|
|
||||||
// Remove a bit of gas from the adjacent ratio...
|
// Remove a bit of gas from the adjacent ratio...
|
||||||
var mix = adj.RemoveRatio(ratio);
|
var mix = adj.Air.RemoveRatio(ratio);
|
||||||
|
|
||||||
// And merge it to the new tile air.
|
// And merge it to the new tile air.
|
||||||
Merge(tile.Air, mix);
|
Merge(tile.Air, mix);
|
||||||
|
|
||||||
// Return removed gas to its original mixture.
|
// Return removed gas to its original mixture.
|
||||||
Merge(adj, mix);
|
Merge(adj.Air, mix);
|
||||||
}
|
}
|
||||||
|
|
||||||
// New temperature is the arithmetic mean of the sum of the adjacent temperatures...
|
// New temperature is the arithmetic mean of the sum of the adjacent temperatures...
|
||||||
tile.Air.Temperature = totalTemperature / adjacent.Length;
|
tile.Air.Temperature = totalTemperature / count;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void GridAddPipeNet(EntityUid uid, GridAtmosphereComponent component, ref AddPipeNetMethodEvent args)
|
private void GridAddPipeNet(EntityUid uid, GridAtmosphereComponent component, ref AddPipeNetMethodEvent args)
|
||||||
@@ -547,30 +491,21 @@ public sealed partial class AtmosphereSystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Repopulates all tiles on a grid atmosphere.
|
/// Repopulates all tiles on a grid atmosphere.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="mapGrid">The grid where to get all valid tiles from.</param>
|
public void InvalidateAllTiles(Entity<MapGridComponent?, GridAtmosphereComponent?> entity)
|
||||||
/// <param name="gridAtmosphere">The grid atmosphere where the tiles will be repopulated.</param>
|
|
||||||
private void GridRepopulateTiles(Entity<MapGridComponent, GridAtmosphereComponent> grid)
|
|
||||||
{
|
{
|
||||||
var (uid, mapGrid, gridAtmosphere) = grid;
|
var (uid, grid, atmos) = entity;
|
||||||
var volume = GetVolumeForTiles(mapGrid, 1);
|
if (!Resolve(uid, ref grid, ref atmos))
|
||||||
|
return;
|
||||||
|
|
||||||
foreach (var tile in mapGrid.GetAllTiles())
|
foreach (var indices in atmos.Tiles.Keys)
|
||||||
{
|
{
|
||||||
if (!gridAtmosphere.Tiles.ContainsKey(tile.GridIndices))
|
atmos.InvalidatedCoords.Add(indices);
|
||||||
gridAtmosphere.Tiles[tile.GridIndices] = new TileAtmosphere(tile.GridUid, tile.GridIndices,
|
|
||||||
new GasMixture(volume) { Temperature = Atmospherics.T20C });
|
|
||||||
|
|
||||||
gridAtmosphere.InvalidatedCoords.Add(tile.GridIndices);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TryComp(uid, out GasTileOverlayComponent? overlay);
|
var enumerator = _map.GetAllTilesEnumerator(uid, grid);
|
||||||
|
while (enumerator.MoveNext(out var tile))
|
||||||
// Gotta do this afterwards so we can properly update adjacent tiles.
|
|
||||||
foreach (var (position, _) in gridAtmosphere.Tiles.ToArray())
|
|
||||||
{
|
{
|
||||||
var ev = new UpdateAdjacentMethodEvent(uid, position);
|
atmos.InvalidatedCoords.Add(tile.Value.GridIndices);
|
||||||
GridUpdateAdjacent(uid, gridAtmosphere, ref ev);
|
|
||||||
InvalidateVisuals(uid, position, overlay);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,11 @@
|
|||||||
using Content.Server.Atmos.Components;
|
using Content.Server.Atmos.Components;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Audio;
|
|
||||||
using Content.Shared.Mobs.Components;
|
using Content.Shared.Mobs.Components;
|
||||||
using Content.Shared.Physics;
|
using Content.Shared.Physics;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
|||||||
@@ -1,12 +1,13 @@
|
|||||||
using Content.Server.Atmos.Components;
|
using Content.Server.Atmos.Components;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Atmos.Components;
|
using Content.Shared.Atmos.Components;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.EntitySystems
|
namespace Content.Server.Atmos.EntitySystems
|
||||||
{
|
{
|
||||||
public sealed partial class AtmosphereSystem
|
public sealed partial class AtmosphereSystem
|
||||||
{
|
{
|
||||||
private void ProcessCell(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int fireCount, GasTileOverlayComponent? visuals)
|
private void ProcessCell(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, int fireCount, GasTileOverlayComponent visuals)
|
||||||
{
|
{
|
||||||
// Can't process a tile without air
|
// Can't process a tile without air
|
||||||
if (tile.Air == null)
|
if (tile.Air == null)
|
||||||
@@ -116,15 +117,9 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
private void Archive(TileAtmosphere tile, int fireCount)
|
private void Archive(TileAtmosphere tile, int fireCount)
|
||||||
{
|
{
|
||||||
if (tile.Air != null)
|
if (tile.Air != null)
|
||||||
{
|
|
||||||
tile.Air.Moles.AsSpan().CopyTo(tile.MolesArchived.AsSpan());
|
tile.Air.Moles.AsSpan().CopyTo(tile.MolesArchived.AsSpan());
|
||||||
tile.TemperatureArchived = tile.Air.Temperature;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
tile.TemperatureArchived = tile.Temperature;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
tile.TemperatureArchived = tile.Temperature;
|
||||||
tile.ArchivedCycle = fireCount;
|
tile.ArchivedCycle = fireCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,6 +161,12 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
/// <param name="disposeExcitedGroup">Whether to dispose of the tile's <see cref="ExcitedGroup"/></param>
|
/// <param name="disposeExcitedGroup">Whether to dispose of the tile's <see cref="ExcitedGroup"/></param>
|
||||||
private void RemoveActiveTile(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, bool disposeExcitedGroup = true)
|
private void RemoveActiveTile(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, bool disposeExcitedGroup = true)
|
||||||
{
|
{
|
||||||
|
DebugTools.Assert(tile.Excited == gridAtmosphere.ActiveTiles.Contains(tile));
|
||||||
|
DebugTools.Assert(tile.Excited || tile.ExcitedGroup == null);
|
||||||
|
|
||||||
|
if (!tile.Excited)
|
||||||
|
return;
|
||||||
|
|
||||||
tile.Excited = false;
|
tile.Excited = false;
|
||||||
gridAtmosphere.ActiveTiles.Remove(tile);
|
gridAtmosphere.ActiveTiles.Remove(tile);
|
||||||
|
|
||||||
@@ -186,7 +187,6 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
if (tile.Air == null)
|
if (tile.Air == null)
|
||||||
return tile.HeatCapacity;
|
return tile.HeatCapacity;
|
||||||
|
|
||||||
// Moles archived is not null if air is not null.
|
|
||||||
return GetHeatCapacityCalculation(tile.MolesArchived!, tile.Space);
|
return GetHeatCapacityCalculation(tile.MolesArchived!, tile.Space);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
using Content.Server.Atmos.Components;
|
using Content.Server.Atmos.Components;
|
||||||
using Content.Shared.Atmos.Components;
|
using Content.Shared.Atmos.Components;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.EntitySystems;
|
namespace Content.Server.Atmos.EntitySystems;
|
||||||
|
|
||||||
@@ -8,10 +10,25 @@ public partial class AtmosphereSystem
|
|||||||
{
|
{
|
||||||
private void InitializeMap()
|
private void InitializeMap()
|
||||||
{
|
{
|
||||||
|
SubscribeLocalEvent<MapAtmosphereComponent, ComponentInit>(OnMapStartup);
|
||||||
|
SubscribeLocalEvent<MapAtmosphereComponent, ComponentRemove>(OnMapRemove);
|
||||||
SubscribeLocalEvent<MapAtmosphereComponent, IsTileSpaceMethodEvent>(MapIsTileSpace);
|
SubscribeLocalEvent<MapAtmosphereComponent, IsTileSpaceMethodEvent>(MapIsTileSpace);
|
||||||
SubscribeLocalEvent<MapAtmosphereComponent, GetTileMixtureMethodEvent>(MapGetTileMixture);
|
SubscribeLocalEvent<MapAtmosphereComponent, GetTileMixtureMethodEvent>(MapGetTileMixture);
|
||||||
SubscribeLocalEvent<MapAtmosphereComponent, GetTileMixturesMethodEvent>(MapGetTileMixtures);
|
SubscribeLocalEvent<MapAtmosphereComponent, GetTileMixturesMethodEvent>(MapGetTileMixtures);
|
||||||
SubscribeLocalEvent<MapAtmosphereComponent, ComponentGetState>(OnMapGetState);
|
SubscribeLocalEvent<MapAtmosphereComponent, ComponentGetState>(OnMapGetState);
|
||||||
|
SubscribeLocalEvent<GridAtmosphereComponent, EntParentChangedMessage>(OnGridParentChanged);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMapStartup(EntityUid uid, MapAtmosphereComponent component, ComponentInit args)
|
||||||
|
{
|
||||||
|
component.Mixture.MarkImmutable();
|
||||||
|
component.Overlay = _gasTileOverlaySystem.GetOverlayData(component.Mixture);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMapRemove(EntityUid uid, MapAtmosphereComponent component, ComponentRemove args)
|
||||||
|
{
|
||||||
|
if (!TerminatingOrDeleted(uid))
|
||||||
|
RefreshAllGridMapAtmospheres(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MapIsTileSpace(EntityUid uid, MapAtmosphereComponent component, ref IsTileSpaceMethodEvent args)
|
private void MapIsTileSpace(EntityUid uid, MapAtmosphereComponent component, ref IsTileSpaceMethodEvent args)
|
||||||
@@ -28,54 +45,115 @@ public partial class AtmosphereSystem
|
|||||||
if (args.Handled)
|
if (args.Handled)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Clone the mixture, if possible.
|
args.Mixture = component.Mixture;
|
||||||
args.Mixture = component.Mixture?.Clone();
|
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void MapGetTileMixtures(EntityUid uid, MapAtmosphereComponent component, ref GetTileMixturesMethodEvent args)
|
private void MapGetTileMixtures(EntityUid uid, MapAtmosphereComponent component, ref GetTileMixturesMethodEvent args)
|
||||||
{
|
{
|
||||||
if (args.Handled || component.Mixture == null)
|
if (args.Handled)
|
||||||
return;
|
return;
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
args.Mixtures ??= new GasMixture?[args.Tiles.Count];
|
args.Mixtures ??= new GasMixture?[args.Tiles.Count];
|
||||||
|
|
||||||
for (var i = 0; i < args.Tiles.Count; i++)
|
for (var i = 0; i < args.Tiles.Count; i++)
|
||||||
{
|
{
|
||||||
args.Mixtures[i] ??= component.Mixture.Clone();
|
args.Mixtures[i] ??= component.Mixture;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnMapGetState(EntityUid uid, MapAtmosphereComponent component, ref ComponentGetState args)
|
private void OnMapGetState(EntityUid uid, MapAtmosphereComponent component, ref ComponentGetState args)
|
||||||
{
|
{
|
||||||
args.State = new MapAtmosphereComponentState(_gasTileOverlaySystem.GetOverlayData(component.Mixture));
|
args.State = new MapAtmosphereComponentState(component.Overlay);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetMapAtmosphere(EntityUid uid, bool space, GasMixture mixture, MapAtmosphereComponent? component = null)
|
public void SetMapAtmosphere(EntityUid uid, bool space, GasMixture mixture)
|
||||||
|
{
|
||||||
|
DebugTools.Assert(HasComp<MapComponent>(uid));
|
||||||
|
var component = EnsureComp<MapAtmosphereComponent>(uid);
|
||||||
|
SetMapGasMixture(uid, mixture, component, false);
|
||||||
|
SetMapSpace(uid, space, component, false);
|
||||||
|
RefreshAllGridMapAtmospheres(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMapGasMixture(EntityUid uid, GasMixture mixture, MapAtmosphereComponent? component = null, bool updateTiles = true)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref component))
|
if (!Resolve(uid, ref component))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
if (!mixture.Immutable)
|
||||||
|
{
|
||||||
|
mixture = mixture.Clone();
|
||||||
|
mixture.MarkImmutable();
|
||||||
|
}
|
||||||
|
|
||||||
|
component.Mixture = mixture;
|
||||||
|
component.Overlay = _gasTileOverlaySystem.GetOverlayData(component.Mixture);
|
||||||
|
Dirty(uid, component);
|
||||||
|
if (updateTiles)
|
||||||
|
RefreshAllGridMapAtmospheres(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMapSpace(EntityUid uid, bool space, MapAtmosphereComponent? component = null, bool updateTiles = true)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref component))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (component.Space == space)
|
||||||
|
return;
|
||||||
|
|
||||||
component.Space = space;
|
component.Space = space;
|
||||||
component.Mixture = mixture;
|
|
||||||
Dirty(uid, component);
|
if (updateTiles)
|
||||||
|
RefreshAllGridMapAtmospheres(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetMapGasMixture(EntityUid uid, GasMixture? mixture, MapAtmosphereComponent? component = null)
|
/// <summary>
|
||||||
|
/// Forces a refresh of all MapAtmosphere tiles on every grid on a map.
|
||||||
|
/// </summary>
|
||||||
|
public void RefreshAllGridMapAtmospheres(EntityUid map)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref component))
|
DebugTools.Assert(HasComp<MapComponent>(map));
|
||||||
return;
|
var enumerator = AllEntityQuery<GridAtmosphereComponent, TransformComponent>();
|
||||||
|
while (enumerator.MoveNext(out var grid, out var atmos, out var xform))
|
||||||
component.Mixture = mixture;
|
{
|
||||||
Dirty(uid, component);
|
if (xform.MapUid == map)
|
||||||
|
RefreshMapAtmosphereTiles((grid, atmos));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetMapSpace(EntityUid uid, bool space, MapAtmosphereComponent? component = null)
|
/// <summary>
|
||||||
|
/// Forces a refresh of all MapAtmosphere tiles on a given grid.
|
||||||
|
/// </summary>
|
||||||
|
private void RefreshMapAtmosphereTiles(Entity<GridAtmosphereComponent?> grid)
|
||||||
{
|
{
|
||||||
if (!Resolve(uid, ref component))
|
if (!Resolve(grid.Owner, ref grid.Comp))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
component.Space = space;
|
var atmos = grid.Comp;
|
||||||
Dirty(uid, component);
|
foreach (var tile in atmos.MapTiles)
|
||||||
|
{
|
||||||
|
RemoveMapAtmos(atmos, tile);
|
||||||
|
atmos.InvalidatedCoords.Add(tile.GridIndices);
|
||||||
|
}
|
||||||
|
atmos.MapTiles.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles updating map-atmospheres when grids move across maps.
|
||||||
|
/// </summary>
|
||||||
|
private void OnGridParentChanged(Entity<GridAtmosphereComponent> grid, ref EntParentChangedMessage args)
|
||||||
|
{
|
||||||
|
// Do nothing if detaching to nullspace
|
||||||
|
if (!args.Transform.ParentUid.IsValid())
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Avoid doing work if moving from a space-map to another space-map.
|
||||||
|
if (args.OldParent == null
|
||||||
|
|| HasComp<MapAtmosphereComponent>(args.OldParent)
|
||||||
|
|| HasComp<MapAtmosphereComponent>(args.Transform.ParentUid))
|
||||||
|
{
|
||||||
|
RefreshMapAtmosphereTiles((grid, grid));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using Content.Server.Doors.Systems;
|
|||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Atmos.Components;
|
using Content.Shared.Atmos.Components;
|
||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Doors.Components;
|
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Random;
|
using Robust.Shared.Random;
|
||||||
@@ -27,7 +26,10 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
private readonly TileAtmosphere[] _depressurizeSpaceTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit];
|
private readonly TileAtmosphere[] _depressurizeSpaceTiles = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit];
|
||||||
private readonly TileAtmosphere[] _depressurizeProgressionOrder = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit * 2];
|
private readonly TileAtmosphere[] _depressurizeProgressionOrder = new TileAtmosphere[Atmospherics.MonstermosHardTileLimit * 2];
|
||||||
|
|
||||||
private void EqualizePressureInZone(Entity<MapGridComponent, GridAtmosphereComponent> ent, TileAtmosphere tile, int cycleNum, GasTileOverlayComponent? visuals)
|
private void EqualizePressureInZone(
|
||||||
|
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent,
|
||||||
|
TileAtmosphere tile,
|
||||||
|
int cycleNum)
|
||||||
{
|
{
|
||||||
if (tile.Air == null || (tile.MonstermosInfo.LastCycle >= cycleNum))
|
if (tile.Air == null || (tile.MonstermosInfo.LastCycle >= cycleNum))
|
||||||
return; // Already done.
|
return; // Already done.
|
||||||
@@ -56,7 +58,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var (_, mapGrid, gridAtmosphere) = ent;
|
var gridAtmosphere = ent.Comp1;
|
||||||
var queueCycle = ++gridAtmosphere.EqualizationQueueCycleControl;
|
var queueCycle = ++gridAtmosphere.EqualizationQueueCycleControl;
|
||||||
var totalMoles = 0f;
|
var totalMoles = 0f;
|
||||||
_equalizeTiles[0] = tile;
|
_equalizeTiles[0] = tile;
|
||||||
@@ -91,7 +93,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
{
|
{
|
||||||
// Looks like someone opened an airlock to space!
|
// Looks like someone opened an airlock to space!
|
||||||
|
|
||||||
ExplosivelyDepressurize(ent, tile, cycleNum, visuals);
|
ExplosivelyDepressurize(ent, tile, cycleNum);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -216,9 +218,13 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
for (var k = 0; k < Atmospherics.Directions; k++)
|
for (var k = 0; k < Atmospherics.Directions; k++)
|
||||||
{
|
{
|
||||||
var direction = (AtmosDirection) (1 << k);
|
var direction = (AtmosDirection) (1 << k);
|
||||||
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
if (!otherTile.AdjacentBits.IsFlagSet(direction))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (giver.MonstermosInfo.MoleDelta <= 0)
|
||||||
|
break; // We're done here now. Let's not do more work than needed.
|
||||||
|
|
||||||
var otherTile2 = otherTile.AdjacentTiles[k];
|
var otherTile2 = otherTile.AdjacentTiles[k];
|
||||||
if (giver.MonstermosInfo.MoleDelta <= 0) break; // We're done here now. Let's not do more work than needed.
|
|
||||||
if (otherTile2 == null || otherTile2.MonstermosInfo.LastQueueCycle != queueCycle) continue;
|
if (otherTile2 == null || otherTile2.MonstermosInfo.LastQueueCycle != queueCycle) continue;
|
||||||
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
|
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
|
||||||
if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
if (otherTile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
||||||
@@ -332,7 +338,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
for (var i = 0; i < tileCount; i++)
|
for (var i = 0; i < tileCount; i++)
|
||||||
{
|
{
|
||||||
var otherTile = _equalizeTiles[i]!;
|
var otherTile = _equalizeTiles[i]!;
|
||||||
FinalizeEq(gridAtmosphere, otherTile, visuals);
|
FinalizeEq(gridAtmosphere, otherTile, ent);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var i = 0; i < tileCount; i++)
|
for (var i = 0; i < tileCount; i++)
|
||||||
@@ -341,12 +347,17 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||||
{
|
{
|
||||||
var direction = (AtmosDirection) (1 << j);
|
var direction = (AtmosDirection) (1 << j);
|
||||||
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
if (!otherTile.AdjacentBits.IsFlagSet(direction))
|
||||||
|
continue;
|
||||||
|
|
||||||
var otherTile2 = otherTile.AdjacentTiles[j]!;
|
var otherTile2 = otherTile.AdjacentTiles[j]!;
|
||||||
if (otherTile2.AdjacentBits == 0)
|
if (otherTile2.AdjacentBits == 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
|
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
|
||||||
if (otherTile2.Air != null && CompareExchange(otherTile2.Air, tile.Air) == GasCompareResult.NoExchange) continue;
|
if (otherTile2.Air != null && CompareExchange(otherTile2.Air, tile.Air) == GasCompareResult.NoExchange)
|
||||||
|
continue;
|
||||||
|
|
||||||
AddActiveTile(gridAtmosphere, otherTile2);
|
AddActiveTile(gridAtmosphere, otherTile2);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -359,7 +370,10 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
Array.Clear(_equalizeQueue, 0, Atmospherics.MonstermosTileLimit);
|
Array.Clear(_equalizeQueue, 0, Atmospherics.MonstermosTileLimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ExplosivelyDepressurize(Entity<MapGridComponent, GridAtmosphereComponent> ent, TileAtmosphere tile, int cycleNum, GasTileOverlayComponent? visuals)
|
private void ExplosivelyDepressurize(
|
||||||
|
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent,
|
||||||
|
TileAtmosphere tile,
|
||||||
|
int cycleNum)
|
||||||
{
|
{
|
||||||
// Check if explosive depressurization is enabled and if the tile is valid.
|
// Check if explosive depressurization is enabled and if the tile is valid.
|
||||||
if (!MonstermosDepressurization || tile.Air == null)
|
if (!MonstermosDepressurization || tile.Air == null)
|
||||||
@@ -368,7 +382,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
const int limit = Atmospherics.MonstermosHardTileLimit;
|
const int limit = Atmospherics.MonstermosHardTileLimit;
|
||||||
|
|
||||||
var totalMolesRemoved = 0f;
|
var totalMolesRemoved = 0f;
|
||||||
var (owner, mapGrid, gridAtmosphere) = ent;
|
var (owner, gridAtmosphere, visuals, mapGrid, _) = ent;
|
||||||
var queueCycle = ++gridAtmosphere.EqualizationQueueCycleControl;
|
var queueCycle = ++gridAtmosphere.EqualizationQueueCycleControl;
|
||||||
|
|
||||||
var tileCount = 0;
|
var tileCount = 0;
|
||||||
@@ -388,20 +402,27 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
{
|
{
|
||||||
for (var j = 0; j < Atmospherics.Directions; j++)
|
for (var j = 0; j < Atmospherics.Directions; j++)
|
||||||
{
|
{
|
||||||
var direction = (AtmosDirection) (1 << j);
|
|
||||||
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
|
||||||
var otherTile2 = otherTile.AdjacentTiles[j];
|
var otherTile2 = otherTile.AdjacentTiles[j];
|
||||||
if (otherTile2?.Air == null) continue;
|
if (otherTile2?.Air == null)
|
||||||
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
|
continue;
|
||||||
if (otherTile2.MonstermosInfo.LastQueueCycle == queueCycle) continue;
|
|
||||||
|
|
||||||
ConsiderFirelocks((owner, gridAtmosphere), otherTile, otherTile2, visuals, mapGrid);
|
if (otherTile2.MonstermosInfo.LastQueueCycle == queueCycle)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var direction = (AtmosDirection) (1 << j);
|
||||||
|
DebugTools.Assert(otherTile.AdjacentBits.IsFlagSet(direction));
|
||||||
|
DebugTools.Assert(otherTile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
|
||||||
|
|
||||||
|
ConsiderFirelocks(ent, otherTile, otherTile2);
|
||||||
|
|
||||||
// The firelocks might have closed on us.
|
// The firelocks might have closed on us.
|
||||||
if (!otherTile.AdjacentBits.IsFlagSet(direction)) continue;
|
if (!otherTile.AdjacentBits.IsFlagSet(direction))
|
||||||
|
continue;
|
||||||
|
|
||||||
otherTile2.MonstermosInfo = new MonstermosInfo { LastQueueCycle = queueCycle };
|
otherTile2.MonstermosInfo = new MonstermosInfo { LastQueueCycle = queueCycle };
|
||||||
_depressurizeTiles[tileCount++] = otherTile2;
|
_depressurizeTiles[tileCount++] = otherTile2;
|
||||||
if (tileCount >= limit) break;
|
if (tileCount >= limit)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -437,13 +458,21 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
// Flood fill into this new direction
|
// Flood fill into this new direction
|
||||||
var direction = (AtmosDirection) (1 << j);
|
var direction = (AtmosDirection) (1 << j);
|
||||||
// Tiles in _depressurizeProgressionOrder cannot have null air.
|
// Tiles in _depressurizeProgressionOrder cannot have null air.
|
||||||
if (!otherTile.AdjacentBits.IsFlagSet(direction) && !otherTile.Space) continue;
|
if (!otherTile.AdjacentBits.IsFlagSet(direction) && !otherTile.Space)
|
||||||
|
continue;
|
||||||
|
|
||||||
var tile2 = otherTile.AdjacentTiles[j];
|
var tile2 = otherTile.AdjacentTiles[j];
|
||||||
if (tile2?.MonstermosInfo.LastQueueCycle != queueCycle) continue;
|
if (tile2?.MonstermosInfo.LastQueueCycle != queueCycle)
|
||||||
|
continue;
|
||||||
|
|
||||||
DebugTools.Assert(tile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
|
DebugTools.Assert(tile2.AdjacentBits.IsFlagSet(direction.GetOpposite()));
|
||||||
// If flood fill has already reached this tile, continue.
|
// If flood fill has already reached this tile, continue.
|
||||||
if (tile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow) continue;
|
if (tile2.MonstermosInfo.LastSlowQueueCycle == queueCycleSlow)
|
||||||
if(tile2.Space) continue;
|
continue;
|
||||||
|
|
||||||
|
if(tile2.Space)
|
||||||
|
continue;
|
||||||
|
|
||||||
tile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite();
|
tile2.MonstermosInfo.CurrentTransferDirection = direction.GetOpposite();
|
||||||
tile2.MonstermosInfo.CurrentTransferAmount = 0.0f;
|
tile2.MonstermosInfo.CurrentTransferAmount = 0.0f;
|
||||||
tile2.PressureSpecificTarget = otherTile.PressureSpecificTarget;
|
tile2.PressureSpecificTarget = otherTile.PressureSpecificTarget;
|
||||||
@@ -535,7 +564,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
_physics.ApplyAngularImpulse(owner, Vector2Helpers.Cross(tile.GridIndices - gridPhysics.LocalCenter, direction) * totalMolesRemoved, body: gridPhysics);
|
_physics.ApplyAngularImpulse(owner, Vector2Helpers.Cross(tile.GridIndices - gridPhysics.LocalCenter, direction) * totalMolesRemoved, body: gridPhysics);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(tileCount > 10 && (totalMolesRemoved / tileCount) > 10)
|
if (tileCount > 10 && (totalMolesRemoved / tileCount) > 10)
|
||||||
_adminLog.Add(LogType.ExplosiveDepressurization, LogImpact.High,
|
_adminLog.Add(LogType.ExplosiveDepressurization, LogImpact.High,
|
||||||
$"Explosive depressurization removed {totalMolesRemoved} moles from {tileCount} tiles starting from position {tile.GridIndices:position} on grid ID {tile.GridIndex:grid}");
|
$"Explosive depressurization removed {totalMolesRemoved} moles from {tileCount} tiles starting from position {tile.GridIndices:position} on grid ID {tile.GridIndex:grid}");
|
||||||
|
|
||||||
@@ -544,36 +573,33 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
Array.Clear(_depressurizeProgressionOrder, 0, Atmospherics.MonstermosHardTileLimit * 2);
|
Array.Clear(_depressurizeProgressionOrder, 0, Atmospherics.MonstermosHardTileLimit * 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ConsiderFirelocks(Entity<GridAtmosphereComponent> ent, TileAtmosphere tile, TileAtmosphere other, GasTileOverlayComponent? visuals, MapGridComponent mapGrid)
|
private void ConsiderFirelocks(
|
||||||
|
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent,
|
||||||
|
TileAtmosphere tile,
|
||||||
|
TileAtmosphere other)
|
||||||
{
|
{
|
||||||
var reconsiderAdjacent = false;
|
var reconsiderAdjacent = false;
|
||||||
|
|
||||||
foreach (var entity in mapGrid.GetAnchoredEntities(tile.GridIndices))
|
var mapGrid = ent.Comp3;
|
||||||
|
foreach (var entity in _map.GetAnchoredEntities(ent.Owner, mapGrid, tile.GridIndices))
|
||||||
{
|
{
|
||||||
if (!TryComp(entity, out FirelockComponent? firelock))
|
if (_firelockQuery.TryGetComponent(entity, out var firelock))
|
||||||
continue;
|
reconsiderAdjacent |= _firelockSystem.EmergencyPressureStop(entity, firelock);
|
||||||
|
|
||||||
reconsiderAdjacent |= _firelockSystem.EmergencyPressureStop(entity, firelock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
foreach (var entity in mapGrid.GetAnchoredEntities(other.GridIndices))
|
foreach (var entity in _map.GetAnchoredEntities(ent.Owner, mapGrid, other.GridIndices))
|
||||||
{
|
{
|
||||||
if (!TryComp(entity, out FirelockComponent? firelock))
|
if (_firelockQuery.TryGetComponent(entity, out var firelock))
|
||||||
continue;
|
reconsiderAdjacent |= _firelockSystem.EmergencyPressureStop(entity, firelock);
|
||||||
|
|
||||||
reconsiderAdjacent |= _firelockSystem.EmergencyPressureStop(entity, firelock);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!reconsiderAdjacent)
|
if (!reconsiderAdjacent)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var (owner, gridAtmosphere) = ent;
|
UpdateAdjacentTiles(ent, tile);
|
||||||
var tileEv = new UpdateAdjacentMethodEvent(owner, tile.GridIndices);
|
UpdateAdjacentTiles(ent, other);
|
||||||
var otherEv = new UpdateAdjacentMethodEvent(owner, other.GridIndices);
|
InvalidateVisuals(tile.GridIndex, tile.GridIndices, ent);
|
||||||
GridUpdateAdjacent(owner, gridAtmosphere, ref tileEv);
|
InvalidateVisuals(other.GridIndex, other.GridIndices, ent);
|
||||||
GridUpdateAdjacent(owner, gridAtmosphere, ref otherEv);
|
|
||||||
InvalidateVisuals(tile.GridIndex, tile.GridIndices, visuals);
|
|
||||||
InvalidateVisuals(other.GridIndex, other.GridIndices, visuals);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void FinalizeEq(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, GasTileOverlayComponent? visuals)
|
private void FinalizeEq(GridAtmosphereComponent gridAtmosphere, TileAtmosphere tile, GasTileOverlayComponent? visuals)
|
||||||
@@ -642,7 +668,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
if (adj == null)
|
if (adj == null)
|
||||||
{
|
{
|
||||||
var nonNull = tile.AdjacentTiles.Where(x => x != null).Count();
|
var nonNull = tile.AdjacentTiles.Where(x => x != null).Count();
|
||||||
Log.Error($"Encountered null adjacent tile in {nameof(AdjustEqMovement)}. Dir: {direction}, Tile: {tile.Tile}, non-null adj count: {nonNull}, Trace: {Environment.StackTrace}");
|
Log.Error($"Encountered null adjacent tile in {nameof(AdjustEqMovement)}. Dir: {direction}, Tile: ({tile.GridIndex}, {tile.GridIndices}), non-null adj count: {nonNull}, Trace: {Environment.StackTrace}");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,5 @@
|
|||||||
using Content.Server.Atmos.Components;
|
using Content.Server.Atmos.Components;
|
||||||
using Content.Server.Atmos.Piping.Components;
|
using Content.Server.Atmos.Piping.Components;
|
||||||
using Content.Server.NodeContainer.NodeGroups;
|
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Atmos.Components;
|
using Content.Shared.Atmos.Components;
|
||||||
using Content.Shared.Maps;
|
using Content.Shared.Maps;
|
||||||
@@ -8,6 +7,7 @@ using Robust.Shared.Map;
|
|||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Timing;
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.EntitySystems
|
namespace Content.Server.Atmos.EntitySystems
|
||||||
{
|
{
|
||||||
@@ -30,118 +30,63 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
private int _currentRunAtmosphereIndex;
|
private int _currentRunAtmosphereIndex;
|
||||||
private bool _simulationPaused;
|
private bool _simulationPaused;
|
||||||
|
|
||||||
private readonly List<Entity<GridAtmosphereComponent>> _currentRunAtmosphere = new();
|
private TileAtmosphere GetOrNewTile(EntityUid owner, GridAtmosphereComponent atmosphere, Vector2i index)
|
||||||
|
{
|
||||||
|
var tile = atmosphere.Tiles.GetOrNew(index, out var existing);
|
||||||
|
if (existing)
|
||||||
|
return tile;
|
||||||
|
|
||||||
|
atmosphere.InvalidatedCoords.Add(index);
|
||||||
|
tile.GridIndex = owner;
|
||||||
|
tile.GridIndices = index;
|
||||||
|
return tile;
|
||||||
|
}
|
||||||
|
|
||||||
|
private readonly List<Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent>> _currentRunAtmosphere = new();
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Revalidates all invalid coordinates in a grid atmosphere.
|
/// Revalidates all invalid coordinates in a grid atmosphere.
|
||||||
|
/// I.e., process any tiles that have had their airtight blockers modified.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="ent">The grid atmosphere in question.</param>
|
/// <param name="ent">The grid atmosphere in question.</param>
|
||||||
/// <returns>Whether the process succeeded or got paused due to time constrains.</returns>
|
/// <returns>Whether the process succeeded or got paused due to time constrains.</returns>
|
||||||
private bool ProcessRevalidate(Entity<GridAtmosphereComponent> ent, GasTileOverlayComponent? visuals)
|
private bool ProcessRevalidate(Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent)
|
||||||
{
|
{
|
||||||
var (owner, atmosphere) = ent;
|
if (ent.Comp4.MapUid == null)
|
||||||
if (!atmosphere.ProcessingPaused)
|
|
||||||
{
|
{
|
||||||
atmosphere.CurrentRunInvalidatedCoordinates.Clear();
|
Log.Error($"Attempted to process atmosphere on a map-less grid? Grid: {ToPrettyString(ent)}");
|
||||||
atmosphere.CurrentRunInvalidatedCoordinates.EnsureCapacity(atmosphere.InvalidatedCoords.Count);
|
return true;
|
||||||
foreach (var tile in atmosphere.InvalidatedCoords)
|
|
||||||
{
|
|
||||||
atmosphere.CurrentRunInvalidatedCoordinates.Enqueue(tile);
|
|
||||||
}
|
|
||||||
atmosphere.InvalidatedCoords.Clear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryComp(owner, out MapGridComponent? mapGridComp))
|
var (uid, atmosphere, visuals, grid, xform) = ent;
|
||||||
return true;
|
var volume = GetVolumeForTiles(grid);
|
||||||
|
TryComp(xform.MapUid, out MapAtmosphereComponent? mapAtmos);
|
||||||
|
|
||||||
var mapUid = _mapManager.GetMapEntityIdOrThrow(Transform(owner).MapID);
|
if (!atmosphere.ProcessingPaused)
|
||||||
|
{
|
||||||
|
atmosphere.CurrentRunInvalidatedTiles.Clear();
|
||||||
|
atmosphere.CurrentRunInvalidatedTiles.EnsureCapacity(atmosphere.InvalidatedCoords.Count);
|
||||||
|
foreach (var indices in atmosphere.InvalidatedCoords)
|
||||||
|
{
|
||||||
|
var tile = GetOrNewTile(uid, atmosphere, indices);
|
||||||
|
atmosphere.CurrentRunInvalidatedTiles.Enqueue(tile);
|
||||||
|
|
||||||
var volume = GetVolumeForTiles(mapGridComp);
|
// Update tile.IsSpace and tile.MapAtmosphere, and tile.AirtightData.
|
||||||
|
UpdateTileData(ent, mapAtmos, tile);
|
||||||
|
}
|
||||||
|
atmosphere.InvalidatedCoords.Clear();
|
||||||
|
|
||||||
|
if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var number = 0;
|
var number = 0;
|
||||||
while (atmosphere.CurrentRunInvalidatedCoordinates.TryDequeue(out var indices))
|
while (atmosphere.CurrentRunInvalidatedTiles.TryDequeue(out var tile))
|
||||||
{
|
{
|
||||||
if (!atmosphere.Tiles.TryGetValue(indices, out var tile))
|
DebugTools.Assert(atmosphere.Tiles.GetValueOrDefault(tile.GridIndices) == tile);
|
||||||
{
|
UpdateAdjacentTiles(ent, tile, activate: true);
|
||||||
tile = new TileAtmosphere(owner, indices,
|
UpdateTileAir(ent, tile, volume);
|
||||||
new GasMixture(volume) { Temperature = Atmospherics.T20C });
|
InvalidateVisuals(uid, tile.GridIndices, visuals);
|
||||||
atmosphere.Tiles[indices] = tile;
|
|
||||||
}
|
|
||||||
|
|
||||||
var airBlockedEv = new IsTileAirBlockedMethodEvent(owner, indices, MapGridComponent:mapGridComp);
|
|
||||||
GridIsTileAirBlocked(owner, atmosphere, ref airBlockedEv);
|
|
||||||
var isAirBlocked = airBlockedEv.Result;
|
|
||||||
|
|
||||||
var oldBlocked = tile.BlockedAirflow;
|
|
||||||
var updateAdjacentEv = new UpdateAdjacentMethodEvent(owner, indices, mapGridComp);
|
|
||||||
GridUpdateAdjacent(owner, atmosphere, ref updateAdjacentEv);
|
|
||||||
|
|
||||||
// Blocked airflow changed, rebuild excited groups!
|
|
||||||
if (tile.Excited && tile.BlockedAirflow != oldBlocked)
|
|
||||||
{
|
|
||||||
RemoveActiveTile(atmosphere, tile);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Call this instead of the grid method as the map has a say on whether the tile is space or not.
|
|
||||||
if ((!mapGridComp.TryGetTileRef(indices, out var t) || t.IsSpace(_tileDefinitionManager)) && !isAirBlocked)
|
|
||||||
{
|
|
||||||
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)
|
|
||||||
{
|
|
||||||
if (airBlockedEv.NoAir)
|
|
||||||
{
|
|
||||||
tile.Air = null;
|
|
||||||
tile.MolesArchived = null;
|
|
||||||
tile.ArchivedCycle = 0;
|
|
||||||
tile.LastShare = 0f;
|
|
||||||
tile.Hotspot = new Hotspot();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (tile.Air == null && NeedsVacuumFixing(mapGridComp, indices))
|
|
||||||
{
|
|
||||||
var vacuumEv = new FixTileVacuumMethodEvent(owner, indices);
|
|
||||||
GridFixTileVacuum(owner, atmosphere, ref vacuumEv);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Tile used to be space, but isn't anymore.
|
|
||||||
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 = mapGridComp.TryGetTileRef(indices, out var tileRef)
|
|
||||||
? tileRef.GetContentTileDefinition(_tileDefinitionManager)
|
|
||||||
: null;
|
|
||||||
|
|
||||||
tile.ThermalConductivity = tileDef?.ThermalConductivity ?? 0.5f;
|
|
||||||
tile.HeatCapacity = tileDef?.HeatCapacity ?? float.PositiveInfinity;
|
|
||||||
InvalidateVisuals(owner, indices, visuals);
|
|
||||||
|
|
||||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
|
||||||
{
|
|
||||||
var direction = (AtmosDirection) (1 << i);
|
|
||||||
var otherIndices = indices.Offset(direction);
|
|
||||||
|
|
||||||
if (atmosphere.Tiles.TryGetValue(otherIndices, out var otherTile))
|
|
||||||
AddActiveTile(atmosphere, otherTile);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (number++ < InvalidCoordinatesLagCheckIterations)
|
if (number++ < InvalidCoordinatesLagCheckIterations)
|
||||||
continue;
|
continue;
|
||||||
@@ -149,12 +94,185 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
number = 0;
|
number = 0;
|
||||||
// Process the rest next time.
|
// Process the rest next time.
|
||||||
if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
|
if (_simulationStopwatch.Elapsed.TotalMilliseconds >= AtmosMaxProcessTime)
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
TrimDisconnectedMapTiles(ent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This method queued a tile and all of its neighbours up for processing by <see cref="TrimDisconnectedMapTiles"/>.
|
||||||
|
/// </summary>
|
||||||
|
public void QueueTileTrim(GridAtmosphereComponent atmos, TileAtmosphere tile)
|
||||||
|
{
|
||||||
|
if (!tile.TrimQueued)
|
||||||
|
{
|
||||||
|
tile.TrimQueued = true;
|
||||||
|
atmos.PossiblyDisconnectedTiles.Add(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||||
|
{
|
||||||
|
var direction = (AtmosDirection) (1 << i);
|
||||||
|
var indices = tile.GridIndices.Offset(direction);
|
||||||
|
if (atmos.Tiles.TryGetValue(indices, out var adj)
|
||||||
|
&& adj.NoGridTile
|
||||||
|
&& !adj.TrimQueued)
|
||||||
|
{
|
||||||
|
adj.TrimQueued = true;
|
||||||
|
atmos.PossiblyDisconnectedTiles.Add(adj);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tiles in a <see cref="GridAtmosphereComponent"/> are either grid-tiles, or they they should be are tiles
|
||||||
|
/// adjacent to grid-tiles that represent the map's atmosphere. This method trims any map-tiles that are no longer
|
||||||
|
/// adjacent to any grid-tiles.
|
||||||
|
/// </summary>
|
||||||
|
private void TrimDisconnectedMapTiles(
|
||||||
|
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent)
|
||||||
|
{
|
||||||
|
var atmos = ent.Comp1;
|
||||||
|
|
||||||
|
foreach (var tile in atmos.PossiblyDisconnectedTiles)
|
||||||
|
{
|
||||||
|
tile.TrimQueued = false;
|
||||||
|
if (!tile.NoGridTile)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var connected = false;
|
||||||
|
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||||
|
{
|
||||||
|
var indices = tile.GridIndices.Offset((AtmosDirection) (1 << i));
|
||||||
|
if (_map.TryGetTile(ent.Comp3, indices, out var gridTile) && !gridTile.IsEmpty)
|
||||||
|
{
|
||||||
|
connected = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!connected)
|
||||||
|
{
|
||||||
|
RemoveActiveTile(atmos, tile);
|
||||||
|
atmos.Tiles.Remove(tile.GridIndices);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
atmos.PossiblyDisconnectedTiles.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Checks whether a tile has a corresponding grid-tile, or whether it is a "map" tile. Also checks whether the
|
||||||
|
/// tile should be considered "space"
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateTileData(
|
||||||
|
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent,
|
||||||
|
MapAtmosphereComponent? mapAtmos,
|
||||||
|
TileAtmosphere tile)
|
||||||
|
{
|
||||||
|
var idx = tile.GridIndices;
|
||||||
|
bool mapAtmosphere;
|
||||||
|
if (_map.TryGetTile(ent.Comp3, idx, out var gTile) && !gTile.IsEmpty)
|
||||||
|
{
|
||||||
|
var contentDef = (ContentTileDefinition) _tileDefinitionManager[gTile.TypeId];
|
||||||
|
mapAtmosphere = contentDef.MapAtmosphere;
|
||||||
|
tile.ThermalConductivity = contentDef.ThermalConductivity;
|
||||||
|
tile.HeatCapacity = contentDef.HeatCapacity;
|
||||||
|
tile.NoGridTile = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mapAtmosphere = true;
|
||||||
|
tile.ThermalConductivity = 0.5f;
|
||||||
|
tile.HeatCapacity = float.PositiveInfinity;
|
||||||
|
|
||||||
|
if (!tile.NoGridTile)
|
||||||
|
{
|
||||||
|
tile.NoGridTile = true;
|
||||||
|
|
||||||
|
// This tile just became a non-grid atmos tile.
|
||||||
|
// It, or one of its neighbours, might now be completely disconnected from the grid.
|
||||||
|
QueueTileTrim(ent.Comp1, tile);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateAirtightData(ent.Owner, ent.Comp1, ent.Comp3, tile);
|
||||||
|
|
||||||
|
if (mapAtmosphere)
|
||||||
|
{
|
||||||
|
if (!tile.MapAtmosphere)
|
||||||
|
{
|
||||||
|
(tile.Air, tile.Space) = GetDefaultMapAtmosphere(mapAtmos);
|
||||||
|
tile.MapAtmosphere = true;
|
||||||
|
ent.Comp1.MapTiles.Add(tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
DebugTools.AssertNotNull(tile.Air);
|
||||||
|
DebugTools.Assert(tile.Air?.Immutable ?? false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!tile.MapAtmosphere)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Tile used to be exposed to the map's atmosphere, but isn't anymore.
|
||||||
|
RemoveMapAtmos(ent.Comp1, tile);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void RemoveMapAtmos(GridAtmosphereComponent atmos, TileAtmosphere tile)
|
||||||
|
{
|
||||||
|
DebugTools.Assert(tile.MapAtmosphere);
|
||||||
|
DebugTools.AssertNotNull(tile.Air);
|
||||||
|
DebugTools.Assert(tile.Air?.Immutable ?? false);
|
||||||
|
tile.MapAtmosphere = false;
|
||||||
|
atmos.MapTiles.Remove(tile);
|
||||||
|
tile.Air = null;
|
||||||
|
Array.Clear(tile.MolesArchived);
|
||||||
|
tile.ArchivedCycle = 0;
|
||||||
|
tile.LastShare = 0f;
|
||||||
|
tile.Space = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Check whether a grid-tile should have an air mixture, and give it one if it doesn't already have one.
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateTileAir(
|
||||||
|
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent,
|
||||||
|
TileAtmosphere tile,
|
||||||
|
float volume)
|
||||||
|
{
|
||||||
|
if (tile.MapAtmosphere)
|
||||||
|
{
|
||||||
|
DebugTools.AssertNotNull(tile.Air);
|
||||||
|
DebugTools.Assert(tile.Air?.Immutable ?? false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var data = tile.AirtightData;
|
||||||
|
var fullyBlocked = data.BlockedDirections == AtmosDirection.All;
|
||||||
|
|
||||||
|
if (fullyBlocked && data.NoAirWhenBlocked)
|
||||||
|
{
|
||||||
|
if (tile.Air == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
tile.Air = null;
|
||||||
|
Array.Clear(tile.MolesArchived);
|
||||||
|
tile.ArchivedCycle = 0;
|
||||||
|
tile.LastShare = 0f;
|
||||||
|
tile.Hotspot = new Hotspot();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tile.Air != null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
tile.Air = new GasMixture(volume){Temperature = Atmospherics.T20C};
|
||||||
|
|
||||||
|
if (data.FixVacuum)
|
||||||
|
GridFixTileVacuum(ent, tile, volume);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void QueueRunTiles(
|
private void QueueRunTiles(
|
||||||
@@ -170,19 +288,16 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ProcessTileEqualize(Entity<GridAtmosphereComponent> ent, GasTileOverlayComponent? visuals)
|
private bool ProcessTileEqualize(Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> ent)
|
||||||
{
|
{
|
||||||
var (uid, atmosphere) = ent;
|
var atmosphere = ent.Comp1;
|
||||||
if (!atmosphere.ProcessingPaused)
|
if (!atmosphere.ProcessingPaused)
|
||||||
QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.ActiveTiles);
|
QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.ActiveTiles);
|
||||||
|
|
||||||
if (!TryComp(uid, out MapGridComponent? mapGridComp))
|
|
||||||
throw new Exception("Tried to process a grid atmosphere on an entity that isn't a grid!");
|
|
||||||
|
|
||||||
var number = 0;
|
var number = 0;
|
||||||
while (atmosphere.CurrentRunTiles.TryDequeue(out var tile))
|
while (atmosphere.CurrentRunTiles.TryDequeue(out var tile))
|
||||||
{
|
{
|
||||||
EqualizePressureInZone((uid, mapGridComp, atmosphere), tile, atmosphere.UpdateCounter, visuals);
|
EqualizePressureInZone(ent, tile, atmosphere.UpdateCounter);
|
||||||
|
|
||||||
if (number++ < LagCheckIterations)
|
if (number++ < LagCheckIterations)
|
||||||
continue;
|
continue;
|
||||||
@@ -198,7 +313,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool ProcessActiveTiles(GridAtmosphereComponent atmosphere, GasTileOverlayComponent? visuals)
|
private bool ProcessActiveTiles(GridAtmosphereComponent atmosphere, GasTileOverlayComponent visuals)
|
||||||
{
|
{
|
||||||
if(!atmosphere.ProcessingPaused)
|
if(!atmosphere.ProcessingPaused)
|
||||||
QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.ActiveTiles);
|
QueueRunTiles(atmosphere.CurrentRunTiles, atmosphere.ActiveTiles);
|
||||||
@@ -240,11 +355,11 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
excitedGroup.BreakdownCooldown++;
|
excitedGroup.BreakdownCooldown++;
|
||||||
excitedGroup.DismantleCooldown++;
|
excitedGroup.DismantleCooldown++;
|
||||||
|
|
||||||
if(excitedGroup.BreakdownCooldown > Atmospherics.ExcitedGroupBreakdownCycles)
|
if (excitedGroup.BreakdownCooldown > Atmospherics.ExcitedGroupBreakdownCycles)
|
||||||
ExcitedGroupSelfBreakdown(gridAtmosphere, excitedGroup);
|
ExcitedGroupSelfBreakdown(gridAtmosphere, excitedGroup);
|
||||||
|
else if (excitedGroup.DismantleCooldown > Atmospherics.ExcitedGroupsDismantleCycles)
|
||||||
else if(excitedGroup.DismantleCooldown > Atmospherics.ExcitedGroupsDismantleCycles)
|
DeactivateGroupTiles(gridAtmosphere, excitedGroup);
|
||||||
ExcitedGroupDismantle(gridAtmosphere, excitedGroup);
|
// TODO ATMOS. What is the point of this? why is this only de-exciting the group? Shouldn't it also dismantle it?
|
||||||
|
|
||||||
if (number++ < LagCheckIterations)
|
if (number++ < LagCheckIterations)
|
||||||
continue;
|
continue;
|
||||||
@@ -435,10 +550,10 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
_currentRunAtmosphereIndex = 0;
|
_currentRunAtmosphereIndex = 0;
|
||||||
_currentRunAtmosphere.Clear();
|
_currentRunAtmosphere.Clear();
|
||||||
|
|
||||||
var query = EntityQueryEnumerator<GridAtmosphereComponent>();
|
var query = EntityQueryEnumerator<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent>();
|
||||||
while (query.MoveNext(out var uid, out var grid))
|
while (query.MoveNext(out var uid, out var atmos, out var overlay, out var grid, out var xform ))
|
||||||
{
|
{
|
||||||
_currentRunAtmosphere.Add((uid, grid));
|
_currentRunAtmosphere.Add((uid, atmos, overlay, grid, xform));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -448,8 +563,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
for (; _currentRunAtmosphereIndex < _currentRunAtmosphere.Count; _currentRunAtmosphereIndex++)
|
for (; _currentRunAtmosphereIndex < _currentRunAtmosphere.Count; _currentRunAtmosphereIndex++)
|
||||||
{
|
{
|
||||||
var ent = _currentRunAtmosphere[_currentRunAtmosphereIndex];
|
var ent = _currentRunAtmosphere[_currentRunAtmosphereIndex];
|
||||||
var (owner, atmosphere) = ent;
|
var (owner, atmosphere, visuals, grid, xform) = ent;
|
||||||
TryComp(owner, out GasTileOverlayComponent? visuals);
|
|
||||||
|
|
||||||
if (!TryComp(owner, out TransformComponent? x)
|
if (!TryComp(owner, out TransformComponent? x)
|
||||||
|| x.MapUid == null
|
|| x.MapUid == null
|
||||||
@@ -474,13 +588,14 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
switch (atmosphere.State)
|
switch (atmosphere.State)
|
||||||
{
|
{
|
||||||
case AtmosphereProcessingState.Revalidate:
|
case AtmosphereProcessingState.Revalidate:
|
||||||
if (!ProcessRevalidate(ent, visuals))
|
if (!ProcessRevalidate(ent))
|
||||||
{
|
{
|
||||||
atmosphere.ProcessingPaused = true;
|
atmosphere.ProcessingPaused = true;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
atmosphere.ProcessingPaused = false;
|
atmosphere.ProcessingPaused = false;
|
||||||
|
|
||||||
// Next state depends on whether monstermos equalization is enabled or not.
|
// Next state depends on whether monstermos equalization is enabled or not.
|
||||||
// Note: We do this here instead of on the tile equalization step to prevent ending it early.
|
// Note: We do this here instead of on the tile equalization step to prevent ending it early.
|
||||||
// Therefore, a change to this CVar might only be applied after that step is over.
|
// Therefore, a change to this CVar might only be applied after that step is over.
|
||||||
@@ -489,7 +604,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
: AtmosphereProcessingState.ActiveTiles;
|
: AtmosphereProcessingState.ActiveTiles;
|
||||||
continue;
|
continue;
|
||||||
case AtmosphereProcessingState.TileEqualize:
|
case AtmosphereProcessingState.TileEqualize:
|
||||||
if (!ProcessTileEqualize(ent, visuals))
|
if (!ProcessTileEqualize(ent))
|
||||||
{
|
{
|
||||||
atmosphere.ProcessingPaused = true;
|
atmosphere.ProcessingPaused = true;
|
||||||
return;
|
return;
|
||||||
@@ -499,7 +614,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
atmosphere.State = AtmosphereProcessingState.ActiveTiles;
|
atmosphere.State = AtmosphereProcessingState.ActiveTiles;
|
||||||
continue;
|
continue;
|
||||||
case AtmosphereProcessingState.ActiveTiles:
|
case AtmosphereProcessingState.ActiveTiles:
|
||||||
if (!ProcessActiveTiles(atmosphere, visuals))
|
if (!ProcessActiveTiles(ent, ent))
|
||||||
{
|
{
|
||||||
atmosphere.ProcessingPaused = true;
|
atmosphere.ProcessingPaused = true;
|
||||||
return;
|
return;
|
||||||
@@ -520,7 +635,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
atmosphere.State = AtmosphereProcessingState.HighPressureDelta;
|
atmosphere.State = AtmosphereProcessingState.HighPressureDelta;
|
||||||
continue;
|
continue;
|
||||||
case AtmosphereProcessingState.HighPressureDelta:
|
case AtmosphereProcessingState.HighPressureDelta:
|
||||||
if (!ProcessHighPressureDelta(ent))
|
if (!ProcessHighPressureDelta((ent, ent)))
|
||||||
{
|
{
|
||||||
atmosphere.ProcessingPaused = true;
|
atmosphere.ProcessingPaused = true;
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Content.Server.Atmos.Components;
|
using Content.Server.Atmos.Components;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
|
||||||
namespace Content.Server.Atmos.EntitySystems
|
namespace Content.Server.Atmos.EntitySystems
|
||||||
{
|
{
|
||||||
@@ -12,7 +13,8 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
for(var i = 0; i < Atmospherics.Directions; i++)
|
for(var i = 0; i < Atmospherics.Directions; i++)
|
||||||
{
|
{
|
||||||
var direction = (AtmosDirection) (1 << i);
|
var direction = (AtmosDirection) (1 << i);
|
||||||
if (!directions.IsFlagSet(direction)) continue;
|
if (!directions.IsFlagSet(direction))
|
||||||
|
continue;
|
||||||
|
|
||||||
var adjacent = tile.AdjacentTiles[direction.ToIndex()];
|
var adjacent = tile.AdjacentTiles[direction.ToIndex()];
|
||||||
|
|
||||||
@@ -92,7 +94,9 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
{
|
{
|
||||||
if (tile.Air == null)
|
if (tile.Air == null)
|
||||||
{
|
{
|
||||||
if (other.Tile != null)
|
// TODO ATMOS: why does this need to check if a tile exists if it doesn't use the tile?
|
||||||
|
if (TryComp<MapGridComponent>(other.GridIndex, out var grid)
|
||||||
|
&& _mapSystem.TryGetTileRef(other.GridIndex, grid, other.GridIndices, out var _))
|
||||||
{
|
{
|
||||||
TemperatureShareOpenToSolid(other, tile);
|
TemperatureShareOpenToSolid(other, tile);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -41,20 +41,6 @@ public partial class AtmosphereSystem
|
|||||||
_gasTileOverlaySystem.Invalidate(gridUid, tile, comp);
|
_gasTileOverlaySystem.Invalidate(gridUid, tile, comp);
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool NeedsVacuumFixing(MapGridComponent mapGrid, Vector2i indices)
|
|
||||||
{
|
|
||||||
var value = false;
|
|
||||||
|
|
||||||
var enumerator = GetObstructingComponentsEnumerator(mapGrid, indices);
|
|
||||||
|
|
||||||
while (enumerator.MoveNext(out var airtight))
|
|
||||||
{
|
|
||||||
value |= airtight.FixVacuum;
|
|
||||||
}
|
|
||||||
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the volume in liters for a number of tiles, on a specific grid.
|
/// Gets the volume in liters for a number of tiles, on a specific grid.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -66,34 +52,44 @@ public partial class AtmosphereSystem
|
|||||||
return Atmospherics.CellVolume * mapGrid.TileSize * tiles;
|
return Atmospherics.CellVolume * mapGrid.TileSize * tiles;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
public readonly record struct AirtightData(AtmosDirection BlockedDirections, bool NoAirWhenBlocked,
|
||||||
/// Gets all obstructing <see cref="AirtightComponent"/> instances in a specific tile.
|
bool FixVacuum);
|
||||||
/// </summary>
|
|
||||||
/// <param name="mapGrid">The grid where to get the tile.</param>
|
|
||||||
/// <param name="tile">The indices of the tile.</param>
|
|
||||||
/// <returns>The enumerator for the airtight components.</returns>
|
|
||||||
public AtmosObstructionEnumerator GetObstructingComponentsEnumerator(MapGridComponent mapGrid, Vector2i tile)
|
|
||||||
{
|
|
||||||
var ancEnumerator = mapGrid.GetAnchoredEntitiesEnumerator(tile);
|
|
||||||
var airQuery = GetEntityQuery<AirtightComponent>();
|
|
||||||
|
|
||||||
var enumerator = new AtmosObstructionEnumerator(ancEnumerator, airQuery);
|
private void UpdateAirtightData(EntityUid uid, GridAtmosphereComponent atmos, MapGridComponent grid, TileAtmosphere tile)
|
||||||
return enumerator;
|
{
|
||||||
|
var oldBlocked = tile.AirtightData.BlockedDirections;
|
||||||
|
|
||||||
|
tile.AirtightData = tile.NoGridTile
|
||||||
|
? default
|
||||||
|
: GetAirtightData(uid, grid, tile.GridIndices);
|
||||||
|
|
||||||
|
if (tile.AirtightData.BlockedDirections != oldBlocked && tile.ExcitedGroup != null)
|
||||||
|
ExcitedGroupDispose(atmos, tile.ExcitedGroup);
|
||||||
}
|
}
|
||||||
|
|
||||||
private AtmosDirection GetBlockedDirections(MapGridComponent mapGrid, Vector2i indices)
|
private AirtightData GetAirtightData(EntityUid uid, MapGridComponent grid, Vector2i tile)
|
||||||
{
|
{
|
||||||
var value = AtmosDirection.Invalid;
|
var blockedDirs = AtmosDirection.Invalid;
|
||||||
|
var noAirWhenBlocked = false;
|
||||||
|
var fixVacuum = false;
|
||||||
|
|
||||||
var enumerator = GetObstructingComponentsEnumerator(mapGrid, indices);
|
foreach (var ent in _map.GetAnchoredEntities(uid, grid, tile))
|
||||||
|
|
||||||
while (enumerator.MoveNext(out var airtight))
|
|
||||||
{
|
{
|
||||||
if(airtight.AirBlocked)
|
if (!_airtightQuery.TryGetComponent(ent, out var airtight))
|
||||||
value |= airtight.AirBlockedDirection;
|
continue;
|
||||||
|
|
||||||
|
if(!airtight.AirBlocked)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
blockedDirs |= airtight.AirBlockedDirection;
|
||||||
|
noAirWhenBlocked |= airtight.NoAirWhenFullyAirBlocked;
|
||||||
|
fixVacuum |= airtight.FixVacuum;
|
||||||
|
|
||||||
|
if (blockedDirs == AtmosDirection.All && noAirWhenBlocked && fixVacuum)
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
return value;
|
return new AirtightData(blockedDirs, noAirWhenBlocked, fixVacuum);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using Content.Server.Body.Systems;
|
|||||||
using Content.Server.Fluids.EntitySystems;
|
using Content.Server.Fluids.EntitySystems;
|
||||||
using Content.Server.NodeContainer.EntitySystems;
|
using Content.Server.NodeContainer.EntitySystems;
|
||||||
using Content.Shared.Atmos.EntitySystems;
|
using Content.Shared.Atmos.EntitySystems;
|
||||||
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.Maps;
|
using Content.Shared.Maps;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
@@ -40,6 +41,9 @@ public sealed partial class AtmosphereSystem : SharedAtmosphereSystem
|
|||||||
private const float ExposedUpdateDelay = 1f;
|
private const float ExposedUpdateDelay = 1f;
|
||||||
private float _exposedTimer = 0f;
|
private float _exposedTimer = 0f;
|
||||||
|
|
||||||
|
private EntityQuery<GridAtmosphereComponent> _atmosQuery;
|
||||||
|
private EntityQuery<AirtightComponent> _airtightQuery;
|
||||||
|
private EntityQuery<FirelockComponent> _firelockQuery;
|
||||||
private HashSet<EntityUid> _entSet = new();
|
private HashSet<EntityUid> _entSet = new();
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -55,6 +59,9 @@ public sealed partial class AtmosphereSystem : SharedAtmosphereSystem
|
|||||||
InitializeGridAtmosphere();
|
InitializeGridAtmosphere();
|
||||||
InitializeMap();
|
InitializeMap();
|
||||||
|
|
||||||
|
_atmosQuery = GetEntityQuery<GridAtmosphereComponent>();
|
||||||
|
_airtightQuery = GetEntityQuery<AirtightComponent>();
|
||||||
|
_firelockQuery = GetEntityQuery<FirelockComponent>();
|
||||||
|
|
||||||
SubscribeLocalEvent<TileChangedEvent>(OnTileChanged);
|
SubscribeLocalEvent<TileChangedEvent>(OnTileChanged);
|
||||||
|
|
||||||
|
|||||||
@@ -27,8 +27,12 @@ public sealed class AutomaticAtmosSystem : EntitySystem
|
|||||||
// Also, these calls are surprisingly slow.
|
// Also, these calls are surprisingly slow.
|
||||||
// TODO: Make tiledefmanager cache the IsSpace property, and turn this lookup-through-two-interfaces into
|
// TODO: Make tiledefmanager cache the IsSpace property, and turn this lookup-through-two-interfaces into
|
||||||
// TODO: a simple array lookup, as tile IDs are likely contiguous, and there's at most 2^16 possibilities anyway.
|
// TODO: a simple array lookup, as tile IDs are likely contiguous, and there's at most 2^16 possibilities anyway.
|
||||||
if (!((ev.OldTile.IsSpace(_tileDefinitionManager) && !ev.NewTile.IsSpace(_tileDefinitionManager)) ||
|
|
||||||
(!ev.OldTile.IsSpace(_tileDefinitionManager) && ev.NewTile.IsSpace(_tileDefinitionManager))) ||
|
var oldSpace = ev.OldTile.IsSpace(_tileDefinitionManager);
|
||||||
|
var newSpace = ev.NewTile.IsSpace(_tileDefinitionManager);
|
||||||
|
|
||||||
|
if (!(oldSpace && !newSpace ||
|
||||||
|
!oldSpace && newSpace) ||
|
||||||
_atmosphereSystem.HasAtmosphere(ev.Entity))
|
_atmosphereSystem.HasAtmosphere(ev.Entity))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -260,13 +260,13 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
{
|
{
|
||||||
var gas = _atmo.GetGas(i);
|
var gas = _atmo.GetGas(i);
|
||||||
|
|
||||||
if (mixture?.Moles[i] <= UIMinMoles)
|
if (mixture?[i] <= UIMinMoles)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
if (mixture != null)
|
if (mixture != null)
|
||||||
{
|
{
|
||||||
var gasName = Loc.GetString(gas.Name);
|
var gasName = Loc.GetString(gas.Name);
|
||||||
gases.Add(new GasEntry(gasName, mixture.Moles[i], gas.Color));
|
gases.Add(new GasEntry(gasName, mixture[i], gas.Color));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
{
|
{
|
||||||
var id = VisibleGasId[i];
|
var id = VisibleGasId[i];
|
||||||
var gas = _atmosphereSystem.GetGas(id);
|
var gas = _atmosphereSystem.GetGas(id);
|
||||||
var moles = mixture?.Moles[id] ?? 0f;
|
var moles = mixture?[id] ?? 0f;
|
||||||
ref var opacity = ref data.Opacity[i];
|
ref var opacity = ref data.Opacity[i];
|
||||||
|
|
||||||
if (moles < gas.GasMolesVisible)
|
if (moles < gas.GasMolesVisible)
|
||||||
@@ -217,13 +217,13 @@ namespace Content.Server.Atmos.EntitySystems
|
|||||||
oldData = new GasOverlayData(tile.Hotspot.State, oldData.Opacity);
|
oldData = new GasOverlayData(tile.Hotspot.State, oldData.Opacity);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tile.Air != null)
|
if (tile is {Air: not null, NoGridTile: false})
|
||||||
{
|
{
|
||||||
for (var i = 0; i < VisibleGasId.Length; i++)
|
for (var i = 0; i < VisibleGasId.Length; i++)
|
||||||
{
|
{
|
||||||
var id = VisibleGasId[i];
|
var id = VisibleGasId[i];
|
||||||
var gas = _atmosphereSystem.GetGas(id);
|
var gas = _atmosphereSystem.GetGas(id);
|
||||||
var moles = tile.Air.Moles[id];
|
var moles = tile.Air[id];
|
||||||
ref var oldOpacity = ref oldData.Opacity[i];
|
ref var oldOpacity = ref oldData.Opacity[i];
|
||||||
|
|
||||||
if (moles < gas.GasMolesVisible)
|
if (moles < gas.GasMolesVisible)
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Linq;
|
|||||||
using System.Runtime.CompilerServices;
|
using System.Runtime.CompilerServices;
|
||||||
using Content.Server.Atmos.Reactions;
|
using Content.Server.Atmos.Reactions;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
|
using Content.Shared.Atmos.EntitySystems;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
@@ -17,11 +18,13 @@ namespace Content.Server.Atmos
|
|||||||
{
|
{
|
||||||
public static GasMixture SpaceGas => new() {Volume = Atmospherics.CellVolume, Temperature = Atmospherics.TCMB, Immutable = true};
|
public static GasMixture SpaceGas => new() {Volume = Atmospherics.CellVolume, Temperature = Atmospherics.TCMB, Immutable = true};
|
||||||
|
|
||||||
// This must always have a length that is a multiple of 4 for SIMD acceleration.
|
// No access, to ensure immutable mixtures are never accidentally mutated.
|
||||||
[DataField("moles")]
|
[Access(typeof(SharedAtmosphereSystem), typeof(SharedAtmosDebugOverlaySystem), Other = AccessPermissions.None)]
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[DataField]
|
||||||
public float[] Moles = new float[Atmospherics.AdjustedNumberOfGases];
|
public float[] Moles = new float[Atmospherics.AdjustedNumberOfGases];
|
||||||
|
|
||||||
|
public float this[int gas] => Moles[gas];
|
||||||
|
|
||||||
[DataField("temperature")]
|
[DataField("temperature")]
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
private float _temperature = Atmospherics.TCMB;
|
private float _temperature = Atmospherics.TCMB;
|
||||||
@@ -80,6 +83,19 @@ namespace Content.Server.Atmos
|
|||||||
Volume = volume;
|
Volume = volume;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public GasMixture(float[] moles, float temp, float volume = Atmospherics.CellVolume)
|
||||||
|
{
|
||||||
|
if (moles.Length != Atmospherics.AdjustedNumberOfGases)
|
||||||
|
throw new InvalidOperationException($"Invalid mole array length");
|
||||||
|
|
||||||
|
if (volume < 0)
|
||||||
|
volume = 0;
|
||||||
|
|
||||||
|
_temperature = temp;
|
||||||
|
Moles = moles;
|
||||||
|
Volume = volume;
|
||||||
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void MarkImmutable()
|
public void MarkImmutable()
|
||||||
{
|
{
|
||||||
@@ -117,15 +133,16 @@ namespace Content.Server.Atmos
|
|||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
public void AdjustMoles(int gasId, float quantity)
|
public void AdjustMoles(int gasId, float quantity)
|
||||||
{
|
{
|
||||||
if (!Immutable)
|
if (Immutable)
|
||||||
{
|
return;
|
||||||
if (!float.IsFinite(quantity))
|
|
||||||
throw new ArgumentException($"Invalid quantity \"{quantity}\" specified!", nameof(quantity));
|
|
||||||
|
|
||||||
// Clamping is needed because x - x can be negative with floating point numbers. If we don't
|
if (!float.IsFinite(quantity))
|
||||||
// clamp here, the caller always has to call GetMoles(), clamp, then SetMoles().
|
throw new ArgumentException($"Invalid quantity \"{quantity}\" specified!", nameof(quantity));
|
||||||
Moles[gasId] = MathF.Max(Moles[gasId] + quantity, 0);
|
|
||||||
}
|
// Clamping is needed because x - x can be negative with floating point numbers. If we don't
|
||||||
|
// clamp here, the caller always has to call GetMoles(), clamp, then SetMoles().
|
||||||
|
ref var moles = ref Moles[gasId];
|
||||||
|
moles = MathF.Max(moles + quantity, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||||
@@ -163,7 +180,8 @@ namespace Content.Server.Atmos
|
|||||||
{
|
{
|
||||||
var moles = Moles[i];
|
var moles = Moles[i];
|
||||||
var otherMoles = removed.Moles[i];
|
var otherMoles = removed.Moles[i];
|
||||||
if (moles < Atmospherics.GasMinMoles || float.IsNaN(moles))
|
|
||||||
|
if ((moles < Atmospherics.GasMinMoles || float.IsNaN(moles)) && !Immutable)
|
||||||
Moles[i] = 0;
|
Moles[i] = 0;
|
||||||
|
|
||||||
if (otherMoles < Atmospherics.GasMinMoles || float.IsNaN(otherMoles))
|
if (otherMoles < Atmospherics.GasMinMoles || float.IsNaN(otherMoles))
|
||||||
@@ -202,6 +220,9 @@ namespace Content.Server.Atmos
|
|||||||
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
void ISerializationHooks.AfterDeserialization()
|
||||||
{
|
{
|
||||||
|
// ISerializationHooks is obsolete.
|
||||||
|
// TODO add fixed-length-array serializer
|
||||||
|
|
||||||
// The arrays MUST have a specific length.
|
// The arrays MUST have a specific length.
|
||||||
Array.Resize(ref Moles, Atmospherics.AdjustedNumberOfGases);
|
Array.Resize(ref Moles, Atmospherics.AdjustedNumberOfGases);
|
||||||
}
|
}
|
||||||
@@ -229,8 +250,12 @@ namespace Content.Server.Atmos
|
|||||||
|
|
||||||
public bool Equals(GasMixture? other)
|
public bool Equals(GasMixture? other)
|
||||||
{
|
{
|
||||||
if (ReferenceEquals(null, other)) return false;
|
if (ReferenceEquals(this, other))
|
||||||
if (ReferenceEquals(this, other)) return true;
|
return true;
|
||||||
|
|
||||||
|
if (ReferenceEquals(null, other))
|
||||||
|
return false;
|
||||||
|
|
||||||
return Moles.SequenceEqual(other.Moles)
|
return Moles.SequenceEqual(other.Moles)
|
||||||
&& _temperature.Equals(other._temperature)
|
&& _temperature.Equals(other._temperature)
|
||||||
&& ReactionResults.SequenceEqual(other.ReactionResults)
|
&& ReactionResults.SequenceEqual(other.ReactionResults)
|
||||||
@@ -258,11 +283,13 @@ namespace Content.Server.Atmos
|
|||||||
|
|
||||||
public GasMixture Clone()
|
public GasMixture Clone()
|
||||||
{
|
{
|
||||||
|
if (Immutable)
|
||||||
|
return this;
|
||||||
|
|
||||||
var newMixture = new GasMixture()
|
var newMixture = new GasMixture()
|
||||||
{
|
{
|
||||||
Moles = (float[])Moles.Clone(),
|
Moles = (float[])Moles.Clone(),
|
||||||
_temperature = _temperature,
|
_temperature = _temperature,
|
||||||
Immutable = Immutable,
|
|
||||||
Volume = Volume,
|
Volume = Volume,
|
||||||
};
|
};
|
||||||
return newMixture;
|
return newMixture;
|
||||||
|
|||||||
@@ -136,7 +136,7 @@ public sealed class GasCanisterSystem : EntitySystem
|
|||||||
|
|
||||||
for (int i = 0; i < containedGasArray.Length; i++)
|
for (int i = 0; i < containedGasArray.Length; i++)
|
||||||
{
|
{
|
||||||
containedGasDict.Add((Gas)i, canister.Air.Moles[i]);
|
containedGasDict.Add((Gas)i, canister.Air[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
_adminLogger.Add(LogType.CanisterValve, impact, $"{ToPrettyString(args.Session.AttachedEntity.GetValueOrDefault()):player} set the valve on {ToPrettyString(uid):canister} to {args.Valve:valveState} while it contained [{string.Join(", ", containedGasDict)}]");
|
_adminLogger.Add(LogType.CanisterValve, impact, $"{ToPrettyString(args.Session.AttachedEntity.GetValueOrDefault()):player} set the valve on {ToPrettyString(uid):canister} to {args.Valve:valveState} while it contained [{string.Join(", ", containedGasDict)}]");
|
||||||
|
|||||||
@@ -45,7 +45,7 @@ public sealed class GasCondenserSystem : EntitySystem
|
|||||||
var removed = inlet.Air.Remove(molesToConvert);
|
var removed = inlet.Air.Remove(molesToConvert);
|
||||||
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||||
{
|
{
|
||||||
var moles = removed.Moles[i];
|
var moles = removed[i];
|
||||||
if (moles <= 0)
|
if (moles <= 0)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
|
|||||||
@@ -183,13 +183,7 @@ public sealed partial class TileAtmosCollectionSerializer : ITypeSerializer<Dict
|
|||||||
target.Clear();
|
target.Clear();
|
||||||
foreach (var (key, val) in source)
|
foreach (var (key, val) in source)
|
||||||
{
|
{
|
||||||
target.Add(key,
|
target.Add(key, new TileAtmosphere(val));
|
||||||
new TileAtmosphere(
|
|
||||||
val.GridIndex,
|
|
||||||
val.GridIndices,
|
|
||||||
val.Air?.Clone(),
|
|
||||||
val.Air?.Immutable ?? false,
|
|
||||||
val.Space));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Content.Server.Atmos.Components;
|
||||||
using Content.Server.Atmos.EntitySystems;
|
using Content.Server.Atmos.EntitySystems;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Maps;
|
using Content.Shared.Maps;
|
||||||
@@ -51,6 +52,10 @@ namespace Content.Server.Atmos
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public readonly TileAtmosphere?[] AdjacentTiles = new TileAtmosphere[Atmospherics.Directions];
|
public readonly TileAtmosphere?[] AdjacentTiles = new TileAtmosphere[Atmospherics.Directions];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Neighbouring tiles to which air can flow. This is a combination of this tile's unblocked direction, and the
|
||||||
|
/// unblocked directions on adjacent tiles.
|
||||||
|
/// </summary>
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public AtmosDirection AdjacentBits = AtmosDirection.Invalid;
|
public AtmosDirection AdjacentBits = AtmosDirection.Invalid;
|
||||||
|
|
||||||
@@ -72,10 +77,7 @@ namespace Content.Server.Atmos
|
|||||||
public EntityUid GridIndex { get; set; }
|
public EntityUid GridIndex { get; set; }
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public TileRef? Tile => GridIndices.GetTileRef(GridIndex);
|
public Vector2i GridIndices;
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public Vector2i GridIndices { get; }
|
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public ExcitedGroup? ExcitedGroup { get; set; }
|
public ExcitedGroup? ExcitedGroup { get; set; }
|
||||||
@@ -92,7 +94,7 @@ namespace Content.Server.Atmos
|
|||||||
public float LastShare;
|
public float LastShare;
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public float[]? MolesArchived;
|
public readonly float[] MolesArchived = new float[Atmospherics.AdjustedNumberOfGases];
|
||||||
|
|
||||||
GasMixture IGasMixtureHolder.Air
|
GasMixture IGasMixtureHolder.Air
|
||||||
{
|
{
|
||||||
@@ -103,8 +105,31 @@ namespace Content.Server.Atmos
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public float MaxFireTemperatureSustained { get; set; }
|
public float MaxFireTemperatureSustained { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, then this tile is directly exposed to the map's atmosphere, either because the grid has no tile at
|
||||||
|
/// this position, or because the tile type is not airtight.
|
||||||
|
/// </summary>
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public AtmosDirection BlockedAirflow { get; set; } = AtmosDirection.Invalid;
|
public bool MapAtmosphere;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, this tile does not actually exist on the grid, it only exists to represent the map's atmosphere for
|
||||||
|
/// adjacent grid tiles.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public bool NoGridTile;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If true, this tile is queued for processing in <see cref="GridAtmosphereComponent.PossiblyDisconnectedTiles"/>
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public bool TrimQueued;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Cached information about airtight entities on this tile. This gets updated anytime a tile gets invalidated
|
||||||
|
/// (i.e., gets added to <see cref="GridAtmosphereComponent.InvalidatedCoords"/>).
|
||||||
|
/// </summary>
|
||||||
|
public AtmosphereSystem.AirtightData AirtightData;
|
||||||
|
|
||||||
public TileAtmosphere(EntityUid gridIndex, Vector2i gridIndices, GasMixture? mixture = null, bool immutable = false, bool space = false)
|
public TileAtmosphere(EntityUid gridIndex, Vector2i gridIndices, GasMixture? mixture = null, bool immutable = false, bool space = false)
|
||||||
{
|
{
|
||||||
@@ -112,10 +137,24 @@ namespace Content.Server.Atmos
|
|||||||
GridIndices = gridIndices;
|
GridIndices = gridIndices;
|
||||||
Air = mixture;
|
Air = mixture;
|
||||||
Space = space;
|
Space = space;
|
||||||
MolesArchived = Air != null ? new float[Atmospherics.AdjustedNumberOfGases] : null;
|
|
||||||
|
|
||||||
if(immutable)
|
if(immutable)
|
||||||
Air?.MarkImmutable();
|
Air?.MarkImmutable();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public TileAtmosphere(TileAtmosphere other)
|
||||||
|
{
|
||||||
|
GridIndex = other.GridIndex;
|
||||||
|
GridIndices = other.GridIndices;
|
||||||
|
Space = other.Space;
|
||||||
|
NoGridTile = other.NoGridTile;
|
||||||
|
MapAtmosphere = other.MapAtmosphere;
|
||||||
|
Air = other.Air?.Clone();
|
||||||
|
Array.Copy(other.MolesArchived, MolesArchived, MolesArchived.Length);
|
||||||
|
}
|
||||||
|
|
||||||
|
public TileAtmosphere()
|
||||||
|
{
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ public sealed class LungSystem : EntitySystem
|
|||||||
foreach (var gas in Enum.GetValues<Gas>())
|
foreach (var gas in Enum.GetValues<Gas>())
|
||||||
{
|
{
|
||||||
var i = (int) gas;
|
var i = (int) gas;
|
||||||
var moles = lung.Air.Moles[i];
|
var moles = lung.Air[i];
|
||||||
if (moles <= 0)
|
if (moles <= 0)
|
||||||
continue;
|
continue;
|
||||||
var reagent = _atmosphereSystem.GasReagents[i];
|
var reagent = _atmosphereSystem.GasReagents[i];
|
||||||
|
|||||||
@@ -16,12 +16,15 @@ public sealed partial class ModifyLungGas : ReagentEffect
|
|||||||
|
|
||||||
public override void Effect(ReagentEffectArgs args)
|
public override void Effect(ReagentEffectArgs args)
|
||||||
{
|
{
|
||||||
if (args.EntityManager.TryGetComponent<LungComponent>(args.OrganEntity, out var lung))
|
if (!args.EntityManager.TryGetComponent<LungComponent>(args.OrganEntity, out var lung))
|
||||||
|
return;
|
||||||
|
|
||||||
|
foreach (var (gas, ratio) in _ratios)
|
||||||
{
|
{
|
||||||
foreach (var (gas, ratio) in _ratios)
|
var quantity = ratio * args.Quantity.Float() / Atmospherics.BreathMolesToReagentMultiplier;
|
||||||
{
|
if (quantity < 0)
|
||||||
lung.Air.Moles[(int) gas] += (ratio * args.Quantity.Float()) / Atmospherics.BreathMolesToReagentMultiplier;
|
quantity = Math.Max(quantity, -lung.Air[(int)gas]);
|
||||||
}
|
lung.Air.AdjustMoles(gas, quantity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using Content.Shared.Examine;
|
|||||||
using Content.Shared.Maps;
|
using Content.Shared.Maps;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Construction.Conditions
|
namespace Content.Server.Construction.Conditions
|
||||||
@@ -49,7 +50,15 @@ namespace Content.Server.Construction.Conditions
|
|||||||
var transformSys = entityManager.System<SharedTransformSystem>();
|
var transformSys = entityManager.System<SharedTransformSystem>();
|
||||||
var indices = transform.Coordinates.ToVector2i(entityManager, IoCManager.Resolve<IMapManager>(), transformSys);
|
var indices = transform.Coordinates.ToVector2i(entityManager, IoCManager.Resolve<IMapManager>(), transformSys);
|
||||||
var lookup = entityManager.EntitySysManager.GetEntitySystem<EntityLookupSystem>();
|
var lookup = entityManager.EntitySysManager.GetEntitySystem<EntityLookupSystem>();
|
||||||
var entities = indices.GetEntitiesInTile(transform.GridUid.Value, LookupFlags.Approximate | LookupFlags.Static, lookup);
|
|
||||||
|
|
||||||
|
if (!entityManager.TryGetComponent<MapGridComponent>(transform.GridUid.Value, out var grid))
|
||||||
|
return !HasEntity;
|
||||||
|
|
||||||
|
if (!entityManager.System<SharedMapSystem>().TryGetTileRef(transform.GridUid.Value, grid, indices, out var tile))
|
||||||
|
return !HasEntity;
|
||||||
|
|
||||||
|
var entities = tile.GetEntitiesInTile(LookupFlags.Approximate | LookupFlags.Static, lookup);
|
||||||
|
|
||||||
foreach (var ent in entities)
|
foreach (var ent in entities)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -493,7 +493,7 @@ public sealed partial class ExplosionSystem
|
|||||||
if (_tileDefinitionManager[tileRef.Tile.TypeId] is not ContentTileDefinition tileDef)
|
if (_tileDefinitionManager[tileRef.Tile.TypeId] is not ContentTileDefinition tileDef)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
if (tileDef.IsSpace)
|
if (tileDef.MapAtmosphere)
|
||||||
canCreateVacuum = true; // is already a vacuum.
|
canCreateVacuum = true; // is already a vacuum.
|
||||||
|
|
||||||
int tileBreakages = 0;
|
int tileBreakages = 0;
|
||||||
@@ -509,7 +509,7 @@ public sealed partial class ExplosionSystem
|
|||||||
if (_tileDefinitionManager[tileDef.BaseTurf] is not ContentTileDefinition newDef)
|
if (_tileDefinitionManager[tileDef.BaseTurf] is not ContentTileDefinition newDef)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
if (newDef.IsSpace && !canCreateVacuum)
|
if (newDef.MapAtmosphere && !canCreateVacuum)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
tileDef = newDef;
|
tileDef = newDef;
|
||||||
|
|||||||
@@ -1001,20 +1001,13 @@ public sealed partial class BiomeSystem : SharedBiomeSystem
|
|||||||
light.AmbientLightColor = Color.FromHex("#D8B059");
|
light.AmbientLightColor = Color.FromHex("#D8B059");
|
||||||
Dirty(mapUid, light, metadata);
|
Dirty(mapUid, light, metadata);
|
||||||
|
|
||||||
// Atmos
|
|
||||||
var atmos = EnsureComp<MapAtmosphereComponent>(mapUid);
|
|
||||||
|
|
||||||
var moles = new float[Atmospherics.AdjustedNumberOfGases];
|
var moles = new float[Atmospherics.AdjustedNumberOfGases];
|
||||||
moles[(int) Gas.Oxygen] = 21.824779f;
|
moles[(int) Gas.Oxygen] = 21.824779f;
|
||||||
moles[(int) Gas.Nitrogen] = 82.10312f;
|
moles[(int) Gas.Nitrogen] = 82.10312f;
|
||||||
|
|
||||||
var mixture = new GasMixture(2500)
|
var mixture = new GasMixture(moles, Atmospherics.T20C);
|
||||||
{
|
|
||||||
Temperature = 293.15f,
|
|
||||||
Moles = moles,
|
|
||||||
};
|
|
||||||
|
|
||||||
_atmos.SetMapAtmosphere(mapUid, false, mixture, atmos);
|
_atmos.SetMapAtmosphere(mapUid, false, mixture);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ public sealed class GasPowerReceiverSystem : EntitySystem
|
|||||||
if (pipe.Air.Temperature <= component.MaxTemperature)
|
if (pipe.Air.Temperature <= component.MaxTemperature)
|
||||||
{
|
{
|
||||||
// we have enough gas, so we consume it and are powered
|
// we have enough gas, so we consume it and are powered
|
||||||
if (pipe.Air.Moles[(int) component.TargetGas] > component.MolesConsumedSec * timeDelta)
|
if (pipe.Air[(int) component.TargetGas] > component.MolesConsumedSec * timeDelta)
|
||||||
{
|
{
|
||||||
pipe.Air.AdjustMoles(component.TargetGas, -component.MolesConsumedSec * timeDelta);
|
pipe.Air.AdjustMoles(component.TargetGas, -component.MolesConsumedSec * timeDelta);
|
||||||
SetPowered(uid, component, true);
|
SetPowered(uid, component, true);
|
||||||
|
|||||||
@@ -125,11 +125,7 @@ public sealed class SpawnSalvageMissionJob : Job<bool>
|
|||||||
air.Gases.CopyTo(moles, 0);
|
air.Gases.CopyTo(moles, 0);
|
||||||
var atmos = _entManager.EnsureComponent<MapAtmosphereComponent>(mapUid);
|
var atmos = _entManager.EnsureComponent<MapAtmosphereComponent>(mapUid);
|
||||||
_entManager.System<AtmosphereSystem>().SetMapSpace(mapUid, air.Space, atmos);
|
_entManager.System<AtmosphereSystem>().SetMapSpace(mapUid, air.Space, atmos);
|
||||||
_entManager.System<AtmosphereSystem>().SetMapGasMixture(mapUid, new GasMixture(2500)
|
_entManager.System<AtmosphereSystem>().SetMapGasMixture(mapUid, new GasMixture(moles, mission.Temperature), atmos);
|
||||||
{
|
|
||||||
Temperature = mission.Temperature,
|
|
||||||
Moles = moles,
|
|
||||||
}, atmos);
|
|
||||||
|
|
||||||
if (mission.Color != null)
|
if (mission.Color != null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ namespace Content.Server.Spreader;
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public sealed class SpreaderSystem : EntitySystem
|
public sealed class SpreaderSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
[Dependency] private readonly IPrototypeManager _prototype = default!;
|
||||||
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
[Dependency] private readonly IRobustRandom _robustRandom = default!;
|
||||||
[Dependency] private readonly SharedMapSystem _map = default!;
|
[Dependency] private readonly SharedMapSystem _map = default!;
|
||||||
@@ -33,6 +34,8 @@ public sealed class SpreaderSystem : EntitySystem
|
|||||||
// TODO PERFORMANCE Assign each prototype to an index and convert dictionary to array
|
// TODO PERFORMANCE Assign each prototype to an index and convert dictionary to array
|
||||||
private readonly Dictionary<EntityUid, Dictionary<string, int>> _gridUpdates = [];
|
private readonly Dictionary<EntityUid, Dictionary<string, int>> _gridUpdates = [];
|
||||||
|
|
||||||
|
private EntityQuery<EdgeSpreaderComponent> _query;
|
||||||
|
|
||||||
public const float SpreadCooldownSeconds = 1;
|
public const float SpreadCooldownSeconds = 1;
|
||||||
|
|
||||||
[ValidatePrototypeId<TagPrototype>]
|
[ValidatePrototypeId<TagPrototype>]
|
||||||
@@ -47,6 +50,8 @@ public sealed class SpreaderSystem : EntitySystem
|
|||||||
|
|
||||||
SubscribeLocalEvent<EdgeSpreaderComponent, EntityTerminatingEvent>(OnTerminating);
|
SubscribeLocalEvent<EdgeSpreaderComponent, EntityTerminatingEvent>(OnTerminating);
|
||||||
SetupPrototypes();
|
SetupPrototypes();
|
||||||
|
|
||||||
|
_query = GetEntityQuery<EdgeSpreaderComponent>();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPrototypeReload(PrototypesReloadedEventArgs obj)
|
private void OnPrototypeReload(PrototypesReloadedEventArgs obj)
|
||||||
@@ -66,13 +71,7 @@ public sealed class SpreaderSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnAirtightChanged(ref AirtightChanged ev)
|
private void OnAirtightChanged(ref AirtightChanged ev)
|
||||||
{
|
{
|
||||||
var neighbors = GetSpreadableNeighbors(ev.Entity, ev.Airtight, ev.Position);
|
ActivateSpreadableNeighbors(ev.Entity, ev.Position);
|
||||||
|
|
||||||
foreach (var neighbor in neighbors)
|
|
||||||
{
|
|
||||||
if (!TerminatingOrDeleted(neighbor))
|
|
||||||
EnsureComp<ActiveEdgeSpreaderComponent>(neighbor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnGridInit(GridInitializeEvent ev)
|
private void OnGridInit(GridInitializeEvent ev)
|
||||||
@@ -82,13 +81,7 @@ public sealed class SpreaderSystem : EntitySystem
|
|||||||
|
|
||||||
private void OnTerminating(Entity<EdgeSpreaderComponent> entity, ref EntityTerminatingEvent args)
|
private void OnTerminating(Entity<EdgeSpreaderComponent> entity, ref EntityTerminatingEvent args)
|
||||||
{
|
{
|
||||||
var neighbors = GetSpreadableNeighbors(entity);
|
ActivateSpreadableNeighbors(entity);
|
||||||
|
|
||||||
foreach (var neighbor in neighbors)
|
|
||||||
{
|
|
||||||
if (!TerminatingOrDeleted(neighbor))
|
|
||||||
EnsureComp<ActiveEdgeSpreaderComponent>(neighbor);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <inheritdoc/>
|
/// <inheritdoc/>
|
||||||
@@ -254,8 +247,7 @@ public sealed class SpreaderSystem : EntitySystem
|
|||||||
if (!_map.TryGetTileRef(neighborEnt, neighborGrid, neighborPos, out var tileRef) || tileRef.Tile.IsEmpty)
|
if (!_map.TryGetTileRef(neighborEnt, neighborGrid, neighborPos, out var tileRef) || tileRef.Tile.IsEmpty)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var directionEnumerator =
|
var directionEnumerator = _map.GetAnchoredEntitiesEnumerator(neighborEnt, neighborGrid, neighborPos);
|
||||||
_map.GetAnchoredEntitiesEnumerator(neighborEnt, neighborGrid, neighborPos);
|
|
||||||
var occupied = false;
|
var occupied = false;
|
||||||
|
|
||||||
while (directionEnumerator.MoveNext(out var ent))
|
while (directionEnumerator.MoveNext(out var ent))
|
||||||
@@ -277,8 +269,7 @@ public sealed class SpreaderSystem : EntitySystem
|
|||||||
continue;
|
continue;
|
||||||
|
|
||||||
var oldCount = occupiedTiles.Count;
|
var oldCount = occupiedTiles.Count;
|
||||||
directionEnumerator =
|
directionEnumerator = _map.GetAnchoredEntitiesEnumerator(neighborEnt, neighborGrid, neighborPos);
|
||||||
_map.GetAnchoredEntitiesEnumerator(neighborEnt, neighborGrid, neighborPos);
|
|
||||||
|
|
||||||
while (directionEnumerator.MoveNext(out var ent))
|
while (directionEnumerator.MoveNext(out var ent))
|
||||||
{
|
{
|
||||||
@@ -299,14 +290,11 @@ public sealed class SpreaderSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Given an entity, this returns a list of all adjacent entities with a <see cref="EdgeSpreaderComponent"/>.
|
/// This function activates all spreaders that are adjacent to a given entity. This also activates other spreaders
|
||||||
|
/// on the same tile as the current entity (for thin airtight entities like windoors).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public List<EntityUid> GetSpreadableNeighbors(EntityUid uid, AirtightComponent? comp = null,
|
public void ActivateSpreadableNeighbors(EntityUid uid, (EntityUid Grid, Vector2i Tile)? position = null)
|
||||||
(EntityUid Grid, Vector2i Tile)? position = null)
|
|
||||||
{
|
{
|
||||||
Resolve(uid, ref comp, false);
|
|
||||||
var neighbors = new List<EntityUid>();
|
|
||||||
|
|
||||||
Vector2i tile;
|
Vector2i tile;
|
||||||
EntityUid ent;
|
EntityUid ent;
|
||||||
MapGridComponent? grid;
|
MapGridComponent? grid;
|
||||||
@@ -315,37 +303,40 @@ public sealed class SpreaderSystem : EntitySystem
|
|||||||
{
|
{
|
||||||
var transform = Transform(uid);
|
var transform = Transform(uid);
|
||||||
if (!TryComp(transform.GridUid, out grid) || TerminatingOrDeleted(transform.GridUid.Value))
|
if (!TryComp(transform.GridUid, out grid) || TerminatingOrDeleted(transform.GridUid.Value))
|
||||||
return neighbors;
|
return;
|
||||||
|
|
||||||
tile = _map.TileIndicesFor(transform.GridUid.Value, grid, transform.Coordinates);
|
tile = _map.TileIndicesFor(transform.GridUid.Value, grid, transform.Coordinates);
|
||||||
ent = transform.GridUid.Value;
|
ent = transform.GridUid.Value;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!TryComp(position.Value.Grid, out grid))
|
if (!TryComp(position.Value.Grid, out grid))
|
||||||
return neighbors;
|
return;
|
||||||
tile = position.Value.Tile;
|
(ent, tile) = position.Value;
|
||||||
ent = position.Value.Grid;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var spreaderQuery = GetEntityQuery<EdgeSpreaderComponent>();
|
var anchored = _map.GetAnchoredEntitiesEnumerator(ent, grid, tile);
|
||||||
|
while (anchored.MoveNext(out var entity))
|
||||||
|
{
|
||||||
|
if (entity == ent)
|
||||||
|
continue;
|
||||||
|
DebugTools.Assert(Transform(entity.Value).Anchored);
|
||||||
|
if (_query.HasComponent(ent) && !TerminatingOrDeleted(entity.Value))
|
||||||
|
EnsureComp<ActiveEdgeSpreaderComponent>(entity.Value);
|
||||||
|
}
|
||||||
|
|
||||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||||
{
|
{
|
||||||
var direction = (AtmosDirection) (1 << i);
|
var direction = (AtmosDirection) (1 << i);
|
||||||
if (comp != null && !comp.AirBlockedDirection.IsFlagSet(direction))
|
var adjacentTile = SharedMapSystem.GetDirection(tile, direction.ToDirection());
|
||||||
continue;
|
anchored = _map.GetAnchoredEntitiesEnumerator(ent, grid, adjacentTile);
|
||||||
|
|
||||||
var directionEnumerator =
|
while (anchored.MoveNext(out var entity))
|
||||||
_map.GetAnchoredEntitiesEnumerator(ent, grid, SharedMapSystem.GetDirection(tile, direction.ToDirection()));
|
|
||||||
|
|
||||||
while (directionEnumerator.MoveNext(out var entity))
|
|
||||||
{
|
{
|
||||||
DebugTools.Assert(Transform(entity.Value).Anchored);
|
DebugTools.Assert(Transform(entity.Value).Anchored);
|
||||||
if (spreaderQuery.HasComponent(entity) && !TerminatingOrDeleted(entity.Value))
|
if (_query.HasComponent(ent) && !TerminatingOrDeleted(entity.Value))
|
||||||
neighbors.Add(entity.Value);
|
EnsureComp<ActiveEdgeSpreaderComponent>(entity.Value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return neighbors;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,11 +8,6 @@ namespace Content.Shared.Atmos
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static class Atmospherics
|
public static class Atmospherics
|
||||||
{
|
{
|
||||||
static Atmospherics()
|
|
||||||
{
|
|
||||||
AdjustedNumberOfGases = MathHelper.NextMultipleOf(TotalNumberOfGases, 4);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region ATMOS
|
#region ATMOS
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The universal gas constant, in kPa*L/(K*mol)
|
/// The universal gas constant, in kPa*L/(K*mol)
|
||||||
@@ -183,7 +178,7 @@ namespace Content.Shared.Atmos
|
|||||||
/// This is the actual length of the gases arrays in mixtures.
|
/// This is the actual length of the gases arrays in mixtures.
|
||||||
/// Set to the closest multiple of 4 relative to <see cref="TotalNumberOfGases"/> for SIMD reasons.
|
/// Set to the closest multiple of 4 relative to <see cref="TotalNumberOfGases"/> for SIMD reasons.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly int AdjustedNumberOfGases;
|
public const int AdjustedNumberOfGases = ((TotalNumberOfGases + 3) / 4) * 4;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Amount of heat released per mole of burnt hydrogen or tritium (hydrogen isotope)
|
/// Amount of heat released per mole of burnt hydrogen or tritium (hydrogen isotope)
|
||||||
|
|||||||
@@ -66,7 +66,10 @@ namespace Content.Shared.Atmos.EntitySystems
|
|||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public readonly struct GasOverlayData : IEquatable<GasOverlayData>
|
public readonly struct GasOverlayData : IEquatable<GasOverlayData>
|
||||||
{
|
{
|
||||||
|
[ViewVariables]
|
||||||
public readonly byte FireState;
|
public readonly byte FireState;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
public readonly byte[] Opacity;
|
public readonly byte[] Opacity;
|
||||||
|
|
||||||
// TODO change fire color based on temps
|
// TODO change fire color based on temps
|
||||||
|
|||||||
@@ -77,7 +77,11 @@ namespace Content.Shared.Maps
|
|||||||
[DataField("itemDrop", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
|
[DataField("itemDrop", customTypeSerializer:typeof(PrototypeIdSerializer<EntityPrototype>))]
|
||||||
public string ItemDropPrototypeName { get; private set; } = "FloorTileItemSteel";
|
public string ItemDropPrototypeName { get; private set; } = "FloorTileItemSteel";
|
||||||
|
|
||||||
[DataField("isSpace")] public bool IsSpace { get; private set; }
|
// TODO rename data-field in yaml
|
||||||
|
/// <summary>
|
||||||
|
/// Whether or not the tile is exposed to the map's atmosphere.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("isSpace")] public bool MapAtmosphere { get; private set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Friction override for mob mover in <see cref="SharedMoverController"/>
|
/// Friction override for mob mover in <see cref="SharedMoverController"/>
|
||||||
|
|||||||
@@ -12,22 +12,6 @@ namespace Content.Shared.Maps
|
|||||||
// That, or make the interface arguments non-optional so people stop failing to pass them in.
|
// That, or make the interface arguments non-optional so people stop failing to pass them in.
|
||||||
public static class TurfHelpers
|
public static class TurfHelpers
|
||||||
{
|
{
|
||||||
/// <summary>
|
|
||||||
/// Attempts to get the turf at map indices with grid id or null if no such turf is found.
|
|
||||||
/// </summary>
|
|
||||||
public static TileRef GetTileRef(this Vector2i vector2i, EntityUid gridId, IEntityManager? entityManager = null)
|
|
||||||
{
|
|
||||||
entityManager ??= IoCManager.Resolve<IEntityManager>();
|
|
||||||
|
|
||||||
if (!entityManager.TryGetComponent<MapGridComponent>(gridId, out var grid))
|
|
||||||
return default;
|
|
||||||
|
|
||||||
if (!grid.TryGetTileRef(vector2i, out var tile))
|
|
||||||
return default;
|
|
||||||
|
|
||||||
return tile;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to get the turf at a certain coordinates or null if no such turf is found.
|
/// Attempts to get the turf at a certain coordinates or null if no such turf is found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -68,7 +52,7 @@ namespace Content.Shared.Maps
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public static bool IsSpace(this Tile tile, ITileDefinitionManager? tileDefinitionManager = null)
|
public static bool IsSpace(this Tile tile, ITileDefinitionManager? tileDefinitionManager = null)
|
||||||
{
|
{
|
||||||
return tile.GetContentTileDefinition(tileDefinitionManager).IsSpace;
|
return tile.GetContentTileDefinition(tileDefinitionManager).MapAtmosphere;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -116,15 +100,6 @@ namespace Content.Shared.Maps
|
|||||||
return GetEntitiesInTile(turf.Value, flags, lookupSystem);
|
return GetEntitiesInTile(turf.Value, flags, lookupSystem);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Helper that returns all entities in a turf.
|
|
||||||
/// </summary>
|
|
||||||
[Obsolete("Use the lookup system")]
|
|
||||||
public static IEnumerable<EntityUid> GetEntitiesInTile(this Vector2i indices, EntityUid gridId, LookupFlags flags = LookupFlags.Static, EntityLookupSystem? lookupSystem = null)
|
|
||||||
{
|
|
||||||
return GetEntitiesInTile(indices.GetTileRef(gridId), flags, lookupSystem);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if a turf has something dense on it.
|
/// Checks if a turf has something dense on it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
8
Resources/Locale/en-US/atmos/commands.ftl
Normal file
8
Resources/Locale/en-US/atmos/commands.ftl
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
cmd-set-map-atmos-desc = Sets a map's atmosphere
|
||||||
|
cmd-set-map-atmos-help = setmapatmos <mapid> <space> [<temperature> [moles...]]
|
||||||
|
cmd-set-map-atmos-removed = Atmosphere removed from map {$map}
|
||||||
|
cmd-set-map-atmos-updated = Atmosphere set for map {$map}
|
||||||
|
cmd-set-map-atmos-hint-map = <mapid>
|
||||||
|
cmd-set-map-atmos-hint-space = <space>
|
||||||
|
cmd-set-map-atmos-hint-temp = <temperature> (float)
|
||||||
|
cmd-set-map-atmos-hint-gas = <{$gas} moles> (float)
|
||||||
Reference in New Issue
Block a user