using Content.Server.Atmos.Components; using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.Piping.Components; using Content.Shared.Atmos.Piping.Components; using JetBrains.Annotations; using Robust.Shared.Timing; using Robust.Shared.Utility; namespace Content.Server.Atmos.Piping.EntitySystems { [UsedImplicitly] public sealed class AtmosDeviceSystem : EntitySystem { [Dependency] private readonly IGameTiming _gameTiming = default!; [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; private float _timer; // Set of atmos devices that are off-grid but have JoinSystem set. private readonly HashSet> _joinedDevices = new(); private static AtmosDeviceDisabledEvent _disabledEv = new(); private static AtmosDeviceEnabledEvent _enabledEv = new(); public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnDeviceInitialize); SubscribeLocalEvent(OnDeviceShutdown); // Re-anchoring should be handled by the parent change. SubscribeLocalEvent(OnDeviceParentChanged); SubscribeLocalEvent(OnDeviceAnchorChanged); } public void JoinAtmosphere(Entity ent) { if (ent.Comp.JoinedGrid != null) { DebugTools.Assert(HasComp(ent.Comp.JoinedGrid)); DebugTools.Assert(Transform(ent).GridUid == ent.Comp.JoinedGrid); DebugTools.Assert(ent.Comp.RequireAnchored == Transform(ent).Anchored); return; } var component = ent.Comp; var transform = Transform(ent); if (component.RequireAnchored && !transform.Anchored) return; // Attempt to add device to a grid atmosphere. bool onGrid = (transform.GridUid != null) && _atmosphereSystem.AddAtmosDevice(transform.GridUid!.Value, ent); if (!onGrid && component.JoinSystem) { _joinedDevices.Add(ent); component.JoinedSystem = true; } component.LastProcess = _gameTiming.CurTime; RaiseLocalEvent(ent, ref _enabledEv); } public void LeaveAtmosphere(Entity ent) { var component = ent.Comp; // Try to remove the component from an atmosphere, and if not if (component.JoinedGrid != null && !_atmosphereSystem.RemoveAtmosDevice(component.JoinedGrid.Value, ent)) { // The grid might have been removed but not us... This usually shouldn't happen. component.JoinedGrid = null; return; } if (component.JoinedSystem) { _joinedDevices.Remove(ent); component.JoinedSystem = false; } component.LastProcess = TimeSpan.Zero; RaiseLocalEvent(ent, ref _disabledEv); } public void RejoinAtmosphere(Entity component) { LeaveAtmosphere(component); JoinAtmosphere(component); } private void OnDeviceInitialize(Entity ent, ref ComponentInit args) { JoinAtmosphere(ent); } private void OnDeviceShutdown(Entity ent, ref ComponentShutdown args) { LeaveAtmosphere(ent); } private void OnDeviceAnchorChanged(Entity ent, ref AnchorStateChangedEvent args) { // Do nothing if the component doesn't require being anchored to function. if (!ent.Comp.RequireAnchored) return; if (args.Anchored) JoinAtmosphere(ent); else LeaveAtmosphere(ent); } private void OnDeviceParentChanged(Entity ent, ref EntParentChangedMessage args) { RejoinAtmosphere(ent); } /// /// Update atmos devices that are off-grid but have JoinSystem set. For devices updates when /// a device is on a grid, see AtmosphereSystem:UpdateProcessing(). /// public override void Update(float frameTime) { _timer += frameTime; if (_timer < _atmosphereSystem.AtmosTime) return; _timer -= _atmosphereSystem.AtmosTime; var time = _gameTiming.CurTime; var ev = new AtmosDeviceUpdateEvent(_atmosphereSystem.AtmosTime, null, null); foreach (var device in _joinedDevices) { var deviceGrid = Transform(device).GridUid; if (HasComp(deviceGrid)) { RejoinAtmosphere(device); } RaiseLocalEvent(device, ref ev); device.Comp.LastProcess = time; } } public bool IsJoinedOffGrid(Entity device) { return _joinedDevices.Contains(device); } } }