* Fix firelock prediction issues with periodic pulses of closing lights For some reason this function was setting a time for the next state which was triggering the door system to try to close the firelock. This does not happen serverside because the function only fires from an event called clientside apparently. It appears to be an attempt to stop firelocks from closing instantly that did not function properly, and I cannot discern any other purpose. As such I have removed it. * Remove redundant serverside check This became redundant with commit 439a87f2
232 lines
9.5 KiB
C#
232 lines
9.5 KiB
C#
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;
|
|
using Content.Shared.Doors.Components;
|
|
using Content.Shared.Doors.Systems;
|
|
using Robust.Shared.Map.Components;
|
|
|
|
namespace Content.Server.Doors.Systems
|
|
{
|
|
public sealed class FirelockSystem : SharedFirelockSystem
|
|
{
|
|
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
|
|
[Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!;
|
|
[Dependency] private readonly AtmosphereSystem _atmosSystem = default!;
|
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
|
[Dependency] private readonly SharedMapSystem _mapping = 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)
|
|
{
|
|
// TODO this should REALLLLY not be door specific appearance thing.
|
|
_appearance.SetData(uid, DoorVisuals.Powered, args.Powered);
|
|
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 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 (fire, pressure) = CheckPressureAndFire(uid, firelock, xform, airtight, airtightQuery);
|
|
_appearance.SetData(uid, DoorVisuals.ClosedLights, fire || pressure, appearance);
|
|
firelock.Temperature = fire;
|
|
firelock.Pressure = pressure;
|
|
Dirty(uid, firelock);
|
|
}
|
|
}
|
|
}
|
|
|
|
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 || args.AlarmType == AtmosAlarmType.Warning)
|
|
{
|
|
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;
|
|
}
|
|
}
|
|
}
|