Files
tbd-station-14/Content.Server/Doors/Systems/FirelockSystem.cs
2022-10-16 20:14:18 +11:00

228 lines
8.6 KiB
C#

using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Monitor.Systems;
using Content.Server.Doors.Components;
using Content.Server.Popups;
using Content.Server.Power.EntitySystems;
using Content.Server.Power.Components;
using Content.Shared.Atmos.Monitor;
using Content.Shared.Doors;
using Content.Shared.Doors.Components;
using Content.Shared.Doors.Systems;
using Robust.Server.GameObjects;
using Robust.Shared.Player;
namespace Content.Server.Doors.Systems
{
public sealed class FirelockSystem : EntitySystem
{
[Dependency] private readonly PopupSystem _popupSystem = default!;
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
[Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!;
[Dependency] private readonly AtmosphereSystem _atmosSystem = default!;
[Dependency] private readonly TransformSystem _xformSys = default!;
[Dependency] private readonly AppearanceSystem _appearance = default!;
private static float _visualUpdateInterval = 0.5f;
private float _accumulatedFrameTime;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<FirelockComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
SubscribeLocalEvent<FirelockComponent, DoorGetPryTimeModifierEvent>(OnDoorGetPryTimeModifier);
SubscribeLocalEvent<FirelockComponent, DoorStateChangedEvent>(OnUpdateState);
SubscribeLocalEvent<FirelockComponent, BeforeDoorAutoCloseEvent>(OnBeforeDoorAutoclose);
SubscribeLocalEvent<FirelockComponent, AtmosAlarmEvent>(OnAtmosAlarm);
// Visuals
SubscribeLocalEvent<FirelockComponent, MapInitEvent>(UpdateVisuals);
}
#region Visuals
private void UpdateVisuals(EntityUid uid, FirelockComponent component, EntityEventArgs args) => UpdateVisuals(uid);
public override void Update(float frameTime)
{
_accumulatedFrameTime += frameTime;
if (_accumulatedFrameTime < _visualUpdateInterval)
return;
_accumulatedFrameTime -= _visualUpdateInterval;
var powerQuery = GetEntityQuery<ApcPowerReceiverComponent>();
foreach (var (_, door, appearance, xform) in EntityQuery<FirelockComponent, DoorComponent, AppearanceComponent, TransformComponent>())
{
UpdateVisuals(door.Owner, door, appearance, xform, powerQuery);
}
}
private void UpdateVisuals(EntityUid uid,
DoorComponent? door = null,
AppearanceComponent? appearance = null,
TransformComponent? xform = null,
EntityQuery<ApcPowerReceiverComponent>? powerQuery = null)
{
if (!Resolve(uid, ref door, ref appearance, ref xform, false))
return;
// 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)
{
_appearance.SetData(uid, DoorVisuals.ClosedLights, false, appearance);
return;
}
powerQuery ??= EntityManager.GetEntityQuery<ApcPowerReceiverComponent>();
if (powerQuery.Value.TryGetComponent(uid, out var receiver) && !receiver.Powered)
{
_appearance.SetData(uid, DoorVisuals.ClosedLights, false, appearance);
return;
}
_appearance.SetData(uid, DoorVisuals.ClosedLights, IsHoldingPressureOrFire(uid, xform), appearance);
}
#endregion
public bool EmergencyPressureStop(EntityUid uid, FirelockComponent? firelock = null, DoorComponent? door = null)
{
if (!Resolve(uid, ref firelock, ref door))
return false;
if (door.State == DoorState.Open)
{
if (_doorSystem.TryClose(door.Owner, door))
{
return _doorSystem.OnPartialClose(door.Owner, door);
}
}
return false;
}
private void OnBeforeDoorOpened(EntityUid uid, FirelockComponent component, BeforeDoorOpenedEvent args)
{
if (!this.IsPowered(uid, EntityManager) || IsHoldingPressureOrFire(uid))
args.Cancel();
}
private void OnDoorGetPryTimeModifier(EntityUid uid, FirelockComponent component, DoorGetPryTimeModifierEvent args)
{
var state = CheckPressureAndFire(uid);
if (state.Fire)
{
_popupSystem.PopupEntity(Loc.GetString("firelock-component-is-holding-fire-message"),
uid, Filter.Pvs(uid, entityManager: EntityManager));
}
else if (state.Pressure)
{
_popupSystem.PopupEntity(Loc.GetString("firelock-component-is-holding-pressure-message"),
uid, Filter.Pvs(uid, entityManager: EntityManager));
}
if (state.Fire || state.Pressure)
args.PryTimeModifier *= component.LockedPryTimeModifier;
}
private void OnUpdateState(EntityUid uid, FirelockComponent component, DoorStateChangedEvent args)
{
var ev = new BeforeDoorAutoCloseEvent();
RaiseLocalEvent(uid, ev);
if (ev.Cancelled)
{
return;
}
_doorSystem.SetNextStateChange(uid, component.AutocloseDelay);
UpdateVisuals(uid, component, args);
}
private void OnBeforeDoorAutoclose(EntityUid uid, FirelockComponent component, BeforeDoorAutoCloseEvent args)
{
if (!this.IsPowered(uid, EntityManager))
args.Cancel();
// Make firelocks autoclose, but only if the last alarm type it
// remembers was a danger. This is to prevent people from
// flooding hallways with endless bad air/fire.
if (component.AlarmAutoClose &&
(_atmosAlarmable.TryGetHighestAlert(uid, out var alarm) && alarm != AtmosAlarmType.Danger || alarm == null))
args.Cancel();
}
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 IsHoldingPressureOrFire(EntityUid uid, TransformComponent? xform = null)
{
var result = CheckPressureAndFire(uid, xform);
return result.Pressure || result.Fire;
}
public (bool Pressure, bool Fire) CheckPressureAndFire(EntityUid owner, TransformComponent? xform = null)
{
if (!Resolve(owner, ref xform))
return (false, false);
float threshold = 20;
var position = _xformSys.GetGridOrMapTilePosition(owner, xform);
if (xform.GridUid is not {} gridUid)
return (false, false);
var minMoles = float.MaxValue;
var maxMoles = 0f;
return (IsHoldingPressure(), IsHoldingFire());
bool IsHoldingPressure()
{
foreach (var adjacent in _atmosSystem.GetAdjacentTileMixtures(gridUid, position))
{
var moles = adjacent.TotalMoles;
if (moles < minMoles)
minMoles = moles;
if (moles > maxMoles)
maxMoles = moles;
}
return (maxMoles - minMoles) > threshold;
}
bool IsHoldingFire()
{
if (_atmosSystem.GetTileMixture(gridUid, null, position) == null)
return false;
if (_atmosSystem.IsHotspotActive(gridUid, position))
return true;
foreach (var adjacent in _atmosSystem.GetAdjacentTiles(gridUid, position))
{
if (_atmosSystem.IsHotspotActive(gridUid, adjacent))
return true;
}
return false;
}
}
}
}