using Content.Server.Atmos.Components; using Content.Server.Explosion.EntitySystems; using Content.Shared.Atmos; using JetBrains.Annotations; using Robust.Shared.Map; using Robust.Shared.Map.Components; namespace Content.Server.Atmos.EntitySystems { [UsedImplicitly] public sealed class AirtightSystem : EntitySystem { [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; [Dependency] private readonly ExplosionSystem _explosionSystem = default!; public override void Initialize() { SubscribeLocalEvent(OnAirtightInit); SubscribeLocalEvent(OnAirtightShutdown); SubscribeLocalEvent(OnAirtightPositionChanged); SubscribeLocalEvent(OnAirtightReAnchor); SubscribeLocalEvent(OnAirtightMoved); } private void OnAirtightInit(Entity airtight, ref ComponentInit args) { var xform = EntityManager.GetComponent(airtight); if (airtight.Comp.FixAirBlockedDirectionInitialize) { var moveEvent = new MoveEvent(airtight, default, default, Angle.Zero, xform.LocalRotation, xform, false); if (AirtightMove(airtight, ref moveEvent)) return; } UpdatePosition(airtight); } private void OnAirtightShutdown(Entity airtight, ref ComponentShutdown args) { var xform = Transform(airtight); // If the grid is deleting no point updating atmos. if (HasComp(xform.GridUid) && MetaData(xform.GridUid.Value).EntityLifeStage > EntityLifeStage.MapInitialized) { return; } SetAirblocked(airtight, false, xform); } private void OnAirtightPositionChanged(EntityUid uid, AirtightComponent airtight, ref AnchorStateChangedEvent args) { var xform = args.Transform; if (!TryComp(xform.GridUid, out MapGridComponent? grid)) return; var gridId = xform.GridUid; var coords = xform.Coordinates; var tilePos = grid.TileIndicesFor(coords); // Update and invalidate new position. airtight.LastPosition = (gridId.Value, tilePos); InvalidatePosition(gridId.Value, tilePos); } private void OnAirtightReAnchor(EntityUid uid, AirtightComponent airtight, ref ReAnchorEvent args) { foreach (var gridId in new[] { args.OldGrid, args.Grid }) { // Update and invalidate new position. airtight.LastPosition = (gridId, args.TilePos); InvalidatePosition(gridId, args.TilePos); } } private void OnAirtightMoved(Entity airtight, ref MoveEvent ev) { AirtightMove(airtight, ref ev); } private bool AirtightMove(Entity ent, ref MoveEvent ev) { var (owner, airtight) = ent; if (!airtight.RotateAirBlocked || airtight.InitialAirBlockedDirection == (int)AtmosDirection.Invalid) return false; airtight.CurrentAirBlockedDirection = (int) Rotate((AtmosDirection)airtight.InitialAirBlockedDirection, ev.NewRotation); var pos = airtight.LastPosition; UpdatePosition(ent, ev.Component); var airtightEv = new AirtightChanged(owner, airtight, pos); RaiseLocalEvent(owner, ref airtightEv, true); return true; } public void SetAirblocked(Entity airtight, bool airblocked, TransformComponent? xform = null) { if (airtight.Comp.AirBlocked == airblocked) return; if (!Resolve(airtight, ref xform)) return; var pos = airtight.Comp.LastPosition; airtight.Comp.AirBlocked = airblocked; UpdatePosition(airtight, xform); var airtightEv = new AirtightChanged(airtight, airtight, pos); RaiseLocalEvent(airtight, ref airtightEv, true); } public void UpdatePosition(Entity ent, TransformComponent? xform = null) { var (owner, airtight) = ent; if (!Resolve(owner, ref xform)) return; if (!xform.Anchored || !TryComp(xform.GridUid, out MapGridComponent? grid)) return; airtight.LastPosition = (xform.GridUid.Value, grid.TileIndicesFor(xform.Coordinates)); InvalidatePosition(airtight.LastPosition.Item1, airtight.LastPosition.Item2, airtight.FixVacuum && !airtight.AirBlocked); } public void InvalidatePosition(EntityUid gridId, Vector2i pos, bool fixVacuum = false) { if (!TryComp(gridId, out MapGridComponent? grid)) return; var query = EntityManager.GetEntityQuery(); _explosionSystem.UpdateAirtightMap(gridId, pos, grid, query); // TODO make atmos system use query _atmosphereSystem.InvalidateTile(gridId, pos); } private AtmosDirection Rotate(AtmosDirection myDirection, Angle myAngle) { var newAirBlockedDirs = AtmosDirection.Invalid; if (myAngle == Angle.Zero) return myDirection; // TODO ATMOS MULTIZ: When we make multiZ atmos, special case this. for (var i = 0; i < Atmospherics.Directions; i++) { var direction = (AtmosDirection) (1 << i); if (!myDirection.IsFlagSet(direction)) continue; var angle = direction.ToAngle(); angle += myAngle; newAirBlockedDirs |= angle.ToAtmosDirectionCardinal(); } return newAirBlockedDirs; } } [ByRefEvent] public readonly record struct AirtightChanged(EntityUid Entity, AirtightComponent Airtight, (EntityUid Grid, Vector2i Tile) Position); }