| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236 |
- using Content.Server.Atmos.Components;
- using Content.Server.Atmos.EntitySystems;
- using Content.Server.Atmos.Monitor.Systems;
- using Content.Server.Power.Components;
- using Content.Server.Power.EntitySystems;
- using Content.Server.Shuttles.Components;
- using Content.Shared.Atmos;
- using Content.Shared.Atmos.Monitor;
- using Content.Shared.Doors.Components;
- using Content.Shared.Doors.Systems;
- using Content.Shared.Power;
- using Robust.Server.GameObjects;
- using Robust.Shared.Map.Components;
- namespace Content.Server.Doors.Systems
- {
- public sealed class FirelockSystem : SharedFirelockSystem
- {
- [Dependency] private readonly SharedDoorSystem _doorSystem = default!;
- [Dependency] private readonly AtmosphereSystem _atmosSystem = default!;
- [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
- [Dependency] private readonly SharedMapSystem _mapping = default!;
- [Dependency] private readonly PointLightSystem _pointLight = default!;
- private const int UpdateInterval = 30;
- private int _accumulatedTicks;
- public override void Initialize()
- {
- base.Initialize();
- SubscribeLocalEvent<FirelockComponent, AtmosAlarmEvent>(OnAtmosAlarm);
- SubscribeLocalEvent<FirelockComponent, PowerChangedEvent>(PowerChanged);
- }
- private void PowerChanged(EntityUid uid, FirelockComponent component, ref PowerChangedEvent args)
- {
- component.Powered = args.Powered;
- Dirty(uid, component);
- }
- public override void Update(float frameTime)
- {
- _accumulatedTicks += 1;
- if (_accumulatedTicks < UpdateInterval)
- return;
- _accumulatedTicks = 0;
- var airtightQuery = GetEntityQuery<AirtightComponent>();
- var appearanceQuery = GetEntityQuery<AppearanceComponent>();
- var xformQuery = GetEntityQuery<TransformComponent>();
- var pointLightQuery = GetEntityQuery<PointLightComponent>();
- var query = EntityQueryEnumerator<FirelockComponent, DoorComponent>();
- while (query.MoveNext(out var uid, out var firelock, out var door))
- {
- // only bother to check pressure on doors that are some variation of closed.
- if (door.State != DoorState.Closed
- && door.State != DoorState.Welded
- && door.State != DoorState.Denying)
- {
- continue;
- }
- if (airtightQuery.TryGetComponent(uid, out var airtight)
- && xformQuery.TryGetComponent(uid, out var xform)
- && appearanceQuery.TryGetComponent(uid, out var appearance))
- {
- var (pressure, fire) = CheckPressureAndFire(uid, firelock, xform, airtight, airtightQuery);
- _appearance.SetData(uid, DoorVisuals.ClosedLights, fire || pressure, appearance);
- firelock.Temperature = fire;
- firelock.Pressure = pressure;
- Dirty(uid, firelock);
- if (pointLightQuery.TryComp(uid, out var pointLight))
- {
- _pointLight.SetEnabled(uid, fire | pressure, pointLight);
- }
- }
- }
- }
- private void OnAtmosAlarm(EntityUid uid, FirelockComponent component, AtmosAlarmEvent args)
- {
- if (!this.IsPowered(uid, EntityManager))
- return;
- if (!TryComp<DoorComponent>(uid, out var doorComponent))
- return;
- if (args.AlarmType == AtmosAlarmType.Normal)
- {
- if (doorComponent.State == DoorState.Closed)
- _doorSystem.TryOpen(uid);
- }
- else if (args.AlarmType == AtmosAlarmType.Danger)
- {
- EmergencyPressureStop(uid, component, doorComponent);
- }
- }
- public (bool Pressure, bool Fire) CheckPressureAndFire(EntityUid uid, FirelockComponent firelock)
- {
- var query = GetEntityQuery<AirtightComponent>();
- if (query.TryGetComponent(uid, out AirtightComponent? airtight))
- return CheckPressureAndFire(uid, firelock, Transform(uid), airtight, query);
- return (false, false);
- }
- public (bool Pressure, bool Fire) CheckPressureAndFire(
- EntityUid uid,
- FirelockComponent firelock,
- TransformComponent xform,
- AirtightComponent airtight,
- EntityQuery<AirtightComponent> airtightQuery)
- {
- if (!airtight.AirBlocked)
- return (false, false);
- if (TryComp(uid, out DockingComponent? dock) && dock.Docked)
- {
- // Currently docking automatically opens the doors. But maybe in future, check the pressure difference before opening doors?
- return (false, false);
- }
- if (!HasComp<GridAtmosphereComponent>(xform.ParentUid))
- return (false, false);
- var grid = Comp<MapGridComponent>(xform.ParentUid);
- var pos = _mapping.CoordinatesToTile(xform.ParentUid, grid, xform.Coordinates);
- var minPressure = float.MaxValue;
- var maxPressure = float.MinValue;
- var minTemperature = float.MaxValue;
- var maxTemperature = float.MinValue;
- var holdingFire = false;
- var holdingPressure = false;
- // We cannot simply use `_atmosSystem.GetAdjacentTileMixtures` because of how the `includeBlocked` option
- // works, we want to ignore the firelock's blocking, while including blockers on other tiles.
- // GetAdjacentTileMixtures also ignores empty/non-existent tiles, which we don't want. Additionally, for
- // edge-fire locks, we only want to enumerate over a single directions. So AFAIK there is no nice way of
- // achieving all this using existing atmos functions, and the functionality is too specialized to bother
- // adding new public atmos system functions.
- List<Vector2i> tiles = new(4);
- List<AtmosDirection> directions = new(4);
- for (var i = 0; i < Atmospherics.Directions; i++)
- {
- var dir = (AtmosDirection)(1 << i);
- if (airtight.AirBlockedDirection.HasFlag(dir))
- {
- directions.Add(dir);
- tiles.Add(pos.Offset(dir));
- }
- }
- // May also have to consider pressure on the same tile as the firelock.
- var count = tiles.Count;
- if (airtight.AirBlockedDirection != AtmosDirection.All)
- tiles.Add(pos);
- var gasses = _atmosSystem.GetTileMixtures(xform.ParentUid, xform.MapUid, tiles);
- if (gasses == null)
- return (false, false);
- for (var i = 0; i < count; i++)
- {
- var gas = gasses[i];
- var dir = directions[i];
- var adjacentPos = tiles[i];
- if (gas != null)
- {
- // Is there some airtight entity blocking this direction? If yes, don't include this direction in the
- // pressure differential
- if (HasAirtightBlocker(_mapping.GetAnchoredEntities(xform.ParentUid, grid, adjacentPos), dir.GetOpposite(), airtightQuery))
- continue;
- var p = gas.Pressure;
- minPressure = Math.Min(minPressure, p);
- maxPressure = Math.Max(maxPressure, p);
- minTemperature = Math.Min(minTemperature, gas.Temperature);
- maxTemperature = Math.Max(maxTemperature, gas.Temperature);
- }
- holdingPressure |= maxPressure - minPressure > firelock.PressureThreshold;
- holdingFire |= maxTemperature - minTemperature > firelock.TemperatureThreshold;
- if (holdingPressure && holdingFire)
- return (holdingPressure, holdingFire);
- }
- if (airtight.AirBlockedDirection == AtmosDirection.All)
- return (holdingPressure, holdingFire);
- var local = gasses[count];
- if (local != null)
- {
- var p = local.Pressure;
- minPressure = Math.Min(minPressure, p);
- maxPressure = Math.Max(maxPressure, p);
- minTemperature = Math.Min(minTemperature, local.Temperature);
- maxTemperature = Math.Max(maxTemperature, local.Temperature);
- }
- else
- {
- minPressure = Math.Min(minPressure, 0);
- maxPressure = Math.Max(maxPressure, 0);
- minTemperature = Math.Min(minTemperature, 0);
- maxTemperature = Math.Max(maxTemperature, 0);
- }
- holdingPressure |= maxPressure - minPressure > firelock.PressureThreshold;
- holdingFire |= maxTemperature - minTemperature > firelock.TemperatureThreshold;
- return (holdingPressure, holdingFire);
- }
- private bool HasAirtightBlocker(IEnumerable<EntityUid> enumerable, AtmosDirection dir, EntityQuery<AirtightComponent> airtightQuery)
- {
- foreach (var ent in enumerable)
- {
- if (!airtightQuery.TryGetComponent(ent, out var airtight) || !airtight.AirBlocked)
- continue;
- if ((airtight.AirBlockedDirection & dir) == dir)
- return true;
- }
- return false;
- }
- }
- }
|