Firelock improvements part 1 (#26582)
* Change prying system and pryunpoweredcomp to allow for custom time modifiers This will be useful if I go the route of making firelocks pryable when unpowered instead of just being able to open and close instantly when unpowered. * Make firelocks properly predicted Shared system made. Since atmos checks can only be done on the server we just have it set relevant bools on the component and then dirty it. Ditched atmos checks on trying to open, they now only happen whenever firelocks are updated. * Make firelocks pryable without a crowbar While this usually would only allow you to do this when a door is unpowered, firelocks do not have the airlock component which actually does that check. As such firelocks will always allow you to pry them open/closed by hand. * Clean up System. Change update interval to be based on ticks. Move as much as possible to shared * Make firelocks unable to emergency close for 2 seconds after being pried open * Clean up * More cleanup * Reorganize SharedFirelockSystem methods to match Initialize order --------- Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com>
This commit is contained in:
@@ -1,9 +1,10 @@
|
|||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
|
using Content.Shared.Doors.Systems;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
|
|
||||||
namespace Content.Client.Doors;
|
namespace Content.Client.Doors;
|
||||||
|
|
||||||
public sealed class FirelockSystem : EntitySystem
|
public sealed class FirelockSystem : SharedFirelockSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
|
||||||
|
|
||||||
|
|||||||
@@ -1,67 +1,56 @@
|
|||||||
using Content.Server.Atmos.Components;
|
using Content.Server.Atmos.Components;
|
||||||
using Content.Server.Atmos.EntitySystems;
|
using Content.Server.Atmos.EntitySystems;
|
||||||
using Content.Server.Atmos.Monitor.Systems;
|
using Content.Server.Atmos.Monitor.Systems;
|
||||||
using Content.Server.Popups;
|
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Server.Shuttles.Components;
|
using Content.Server.Shuttles.Components;
|
||||||
using Content.Shared.Access.Systems;
|
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
using Content.Shared.Atmos.Monitor;
|
using Content.Shared.Atmos.Monitor;
|
||||||
using Content.Shared.Doors;
|
using Content.Shared.Doors;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.Doors.Systems;
|
using Content.Shared.Doors.Systems;
|
||||||
using Content.Shared.Popups;
|
|
||||||
using Content.Shared.Prying.Components;
|
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
|
|
||||||
namespace Content.Server.Doors.Systems
|
namespace Content.Server.Doors.Systems
|
||||||
{
|
{
|
||||||
public sealed class FirelockSystem : EntitySystem
|
public sealed class FirelockSystem : SharedFirelockSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly PopupSystem _popupSystem = default!;
|
|
||||||
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
|
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
|
||||||
[Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!;
|
[Dependency] private readonly AtmosAlarmableSystem _atmosAlarmable = default!;
|
||||||
[Dependency] private readonly AtmosphereSystem _atmosSystem = default!;
|
[Dependency] private readonly AtmosphereSystem _atmosSystem = default!;
|
||||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
|
[Dependency] private readonly SharedMapSystem _mapping = default!;
|
||||||
|
|
||||||
private static float _visualUpdateInterval = 0.5f;
|
private const int UpdateInterval = 30;
|
||||||
private float _accumulatedFrameTime;
|
private int _accumulatedTicks;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
SubscribeLocalEvent<FirelockComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
|
|
||||||
SubscribeLocalEvent<FirelockComponent, GetPryTimeModifierEvent>(OnDoorGetPryTimeModifier);
|
|
||||||
SubscribeLocalEvent<FirelockComponent, DoorStateChangedEvent>(OnUpdateState);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<FirelockComponent, BeforeDoorAutoCloseEvent>(OnBeforeDoorAutoclose);
|
SubscribeLocalEvent<FirelockComponent, BeforeDoorAutoCloseEvent>(OnBeforeDoorAutoclose);
|
||||||
SubscribeLocalEvent<FirelockComponent, AtmosAlarmEvent>(OnAtmosAlarm);
|
SubscribeLocalEvent<FirelockComponent, AtmosAlarmEvent>(OnAtmosAlarm);
|
||||||
|
|
||||||
// Visuals
|
|
||||||
SubscribeLocalEvent<FirelockComponent, MapInitEvent>(UpdateVisuals);
|
|
||||||
SubscribeLocalEvent<FirelockComponent, ComponentStartup>(UpdateVisuals);
|
|
||||||
SubscribeLocalEvent<FirelockComponent, PowerChangedEvent>(PowerChanged);
|
SubscribeLocalEvent<FirelockComponent, PowerChangedEvent>(PowerChanged);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void PowerChanged(EntityUid uid, FirelockComponent component, ref PowerChangedEvent args)
|
private void PowerChanged(EntityUid uid, FirelockComponent component, ref PowerChangedEvent args)
|
||||||
{
|
{
|
||||||
// TODO this should REALLLLY not be door specific appearance thing.
|
// TODO this should REALLLLY not be door specific appearance thing.
|
||||||
_appearance.SetData(uid, DoorVisuals.Powered, args.Powered);
|
_appearance.SetData(uid, DoorVisuals.Powered, args.Powered);
|
||||||
|
component.Powered = args.Powered;
|
||||||
|
Dirty(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
#region Visuals
|
|
||||||
private void UpdateVisuals(EntityUid uid, FirelockComponent component, EntityEventArgs args) => UpdateVisuals(uid, component);
|
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
public override void Update(float frameTime)
|
||||||
{
|
{
|
||||||
_accumulatedFrameTime += frameTime;
|
_accumulatedTicks += 1;
|
||||||
if (_accumulatedFrameTime < _visualUpdateInterval)
|
if (_accumulatedTicks < UpdateInterval)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
_accumulatedFrameTime -= _visualUpdateInterval;
|
_accumulatedTicks = 0;
|
||||||
|
|
||||||
var airtightQuery = GetEntityQuery<AirtightComponent>();
|
var airtightQuery = GetEntityQuery<AirtightComponent>();
|
||||||
var appearanceQuery = GetEntityQuery<AppearanceComponent>();
|
var appearanceQuery = GetEntityQuery<AppearanceComponent>();
|
||||||
@@ -84,94 +73,13 @@ namespace Content.Server.Doors.Systems
|
|||||||
{
|
{
|
||||||
var (fire, pressure) = CheckPressureAndFire(uid, firelock, xform, airtight, airtightQuery);
|
var (fire, pressure) = CheckPressureAndFire(uid, firelock, xform, airtight, airtightQuery);
|
||||||
_appearance.SetData(uid, DoorVisuals.ClosedLights, fire || pressure, appearance);
|
_appearance.SetData(uid, DoorVisuals.ClosedLights, fire || pressure, appearance);
|
||||||
|
firelock.Temperature = fire;
|
||||||
|
firelock.Pressure = pressure;
|
||||||
|
Dirty(uid, firelock);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateVisuals(EntityUid uid,
|
|
||||||
FirelockComponent? firelock = null,
|
|
||||||
DoorComponent? door = null,
|
|
||||||
AirtightComponent? airtight = null,
|
|
||||||
AppearanceComponent? appearance = null,
|
|
||||||
TransformComponent? xform = null)
|
|
||||||
{
|
|
||||||
if (!Resolve(uid, ref door, ref appearance, 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;
|
|
||||||
}
|
|
||||||
|
|
||||||
var query = GetEntityQuery<AirtightComponent>();
|
|
||||||
if (!Resolve(uid, ref firelock, ref airtight, ref appearance, ref xform, false) || !query.Resolve(uid, ref airtight, false))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var (fire, pressure) = CheckPressureAndFire(uid, firelock, xform, airtight, query);
|
|
||||||
_appearance.SetData(uid, DoorVisuals.ClosedLights, fire || pressure, 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(uid, door))
|
|
||||||
{
|
|
||||||
return _doorSystem.OnPartialClose(uid, door);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBeforeDoorOpened(EntityUid uid, FirelockComponent component, BeforeDoorOpenedEvent args)
|
|
||||||
{
|
|
||||||
// Give the Door remote the ability to force a firelock open even if it is holding back dangerous gas
|
|
||||||
var overrideAccess = (args.User != null) && _accessReaderSystem.IsAllowed(args.User.Value, uid);
|
|
||||||
|
|
||||||
if (!this.IsPowered(uid, EntityManager) || (!overrideAccess && IsHoldingPressureOrFire(uid, component)))
|
|
||||||
args.Cancel();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDoorGetPryTimeModifier(EntityUid uid, FirelockComponent component, ref GetPryTimeModifierEvent args)
|
|
||||||
{
|
|
||||||
var state = CheckPressureAndFire(uid, component);
|
|
||||||
|
|
||||||
if (state.Fire)
|
|
||||||
{
|
|
||||||
_popupSystem.PopupEntity(Loc.GetString("firelock-component-is-holding-fire-message"),
|
|
||||||
uid, args.User, PopupType.MediumCaution);
|
|
||||||
}
|
|
||||||
else if (state.Pressure)
|
|
||||||
{
|
|
||||||
_popupSystem.PopupEntity(Loc.GetString("firelock-component-is-holding-pressure-message"),
|
|
||||||
uid, args.User, PopupType.MediumCaution);
|
|
||||||
}
|
|
||||||
|
|
||||||
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);
|
|
||||||
UpdateVisuals(uid, component, args);
|
|
||||||
if (ev.Cancelled)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_doorSystem.SetNextStateChange(uid, component.AutocloseDelay);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnBeforeDoorAutoclose(EntityUid uid, FirelockComponent component, BeforeDoorAutoCloseEvent args)
|
private void OnBeforeDoorAutoclose(EntityUid uid, FirelockComponent component, BeforeDoorAutoCloseEvent args)
|
||||||
{
|
{
|
||||||
if (!this.IsPowered(uid, EntityManager))
|
if (!this.IsPowered(uid, EntityManager))
|
||||||
@@ -204,12 +112,6 @@ namespace Content.Server.Doors.Systems
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public bool IsHoldingPressureOrFire(EntityUid uid, FirelockComponent firelock)
|
|
||||||
{
|
|
||||||
var result = CheckPressureAndFire(uid, firelock);
|
|
||||||
return result.Pressure || result.Fire;
|
|
||||||
}
|
|
||||||
|
|
||||||
public (bool Pressure, bool Fire) CheckPressureAndFire(EntityUid uid, FirelockComponent firelock)
|
public (bool Pressure, bool Fire) CheckPressureAndFire(EntityUid uid, FirelockComponent firelock)
|
||||||
{
|
{
|
||||||
var query = GetEntityQuery<AirtightComponent>();
|
var query = GetEntityQuery<AirtightComponent>();
|
||||||
@@ -234,17 +136,17 @@ namespace Content.Server.Doors.Systems
|
|||||||
return (false, false);
|
return (false, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!TryComp(xform.ParentUid, out GridAtmosphereComponent? gridAtmosphere))
|
if (!HasComp<GridAtmosphereComponent>(xform.ParentUid))
|
||||||
return (false, false);
|
return (false, false);
|
||||||
|
|
||||||
var grid = Comp<MapGridComponent>(xform.ParentUid);
|
var grid = Comp<MapGridComponent>(xform.ParentUid);
|
||||||
var pos = grid.CoordinatesToTile(xform.Coordinates);
|
var pos = _mapping.CoordinatesToTile(xform.ParentUid, grid, xform.Coordinates);
|
||||||
var minPressure = float.MaxValue;
|
var minPressure = float.MaxValue;
|
||||||
var maxPressure = float.MinValue;
|
var maxPressure = float.MinValue;
|
||||||
var minTemperature = float.MaxValue;
|
var minTemperature = float.MaxValue;
|
||||||
var maxTemperature = float.MinValue;
|
var maxTemperature = float.MinValue;
|
||||||
bool holdingFire = false;
|
var holdingFire = false;
|
||||||
bool holdingPressure = false;
|
var holdingPressure = false;
|
||||||
|
|
||||||
// We cannot simply use `_atmosSystem.GetAdjacentTileMixtures` because of how the `includeBlocked` option
|
// 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.
|
// works, we want to ignore the firelock's blocking, while including blockers on other tiles.
|
||||||
@@ -284,7 +186,7 @@ namespace Content.Server.Doors.Systems
|
|||||||
{
|
{
|
||||||
// Is there some airtight entity blocking this direction? If yes, don't include this direction in the
|
// Is there some airtight entity blocking this direction? If yes, don't include this direction in the
|
||||||
// pressure differential
|
// pressure differential
|
||||||
if (HasAirtightBlocker(grid.GetAnchoredEntities(adjacentPos), dir.GetOpposite(), airtightQuery))
|
if (HasAirtightBlocker(_mapping.GetAnchoredEntities(xform.ParentUid, grid, adjacentPos), dir.GetOpposite(), airtightQuery))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var p = gas.Pressure;
|
var p = gas.Pressure;
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Shared.Doors.Components;
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
namespace Content.Shared.Doors.Components
|
namespace Content.Shared.Doors.Components
|
||||||
{
|
{
|
||||||
@@ -7,9 +7,11 @@ namespace Content.Shared.Doors.Components
|
|||||||
/// auto-closing on depressurization, air/fire alarm interactions, and preventing normal door functions when
|
/// auto-closing on depressurization, air/fire alarm interactions, and preventing normal door functions when
|
||||||
/// retaining pressure..
|
/// retaining pressure..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent]
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
public sealed partial class FirelockComponent : Component
|
public sealed partial class FirelockComponent : Component
|
||||||
{
|
{
|
||||||
|
#region Settings
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Pry time modifier to be used when the firelock is currently closed due to fire or pressure.
|
/// Pry time modifier to be used when the firelock is currently closed due to fire or pressure.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -39,5 +41,47 @@ namespace Content.Shared.Doors.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("alarmAutoClose"), ViewVariables(VVAccess.ReadWrite)]
|
[DataField("alarmAutoClose"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
public bool AlarmAutoClose = true;
|
public bool AlarmAutoClose = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The cooldown duration before a firelock can automatically close due to a hazardous environment after it has
|
||||||
|
/// been pried open. Measured in seconds.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public TimeSpan EmergencyCloseCooldownDuration = TimeSpan.FromSeconds(2);
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Set by system
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When the firelock will be allowed to automatically close again due to a hazardous environment.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public TimeSpan? EmergencyCloseCooldown;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the firelock can open, or is locked due to its environment.
|
||||||
|
/// </summary>
|
||||||
|
public bool IsLocked => Pressure || Temperature;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the firelock is holding back a hazardous pressure.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool Pressure;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the firelock is holding back extreme temperatures.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool Temperature;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether the airlock is powered.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool Powered;
|
||||||
|
|
||||||
|
#endregion
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ using Content.Shared.Damage;
|
|||||||
using Content.Shared.Database;
|
using Content.Shared.Database;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.Emag.Systems;
|
using Content.Shared.Emag.Systems;
|
||||||
using Content.Shared.Hands.Components;
|
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Physics;
|
using Content.Shared.Physics;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
@@ -466,7 +465,7 @@ public abstract partial class SharedDoorSystem : EntitySystem
|
|||||||
|
|
||||||
door.Partial = true;
|
door.Partial = true;
|
||||||
|
|
||||||
// Make sure no entity waled into the airlock when it started closing.
|
// Make sure no entity walked into the airlock when it started closing.
|
||||||
if (!CanClose(uid, door))
|
if (!CanClose(uid, door))
|
||||||
{
|
{
|
||||||
door.NextStateChange = GameTiming.CurTime + door.OpenTimeTwo;
|
door.NextStateChange = GameTiming.CurTime + door.OpenTimeTwo;
|
||||||
|
|||||||
125
Content.Shared/Doors/Systems/SharedFirelockSystem.cs
Normal file
125
Content.Shared/Doors/Systems/SharedFirelockSystem.cs
Normal file
@@ -0,0 +1,125 @@
|
|||||||
|
using Content.Shared.Access.Systems;
|
||||||
|
using Content.Shared.Doors.Components;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Prying.Components;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Shared.Doors.Systems;
|
||||||
|
|
||||||
|
public abstract class SharedFirelockSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||||
|
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||||
|
[Dependency] private readonly SharedDoorSystem _doorSystem = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<FirelockComponent, DoorStateChangedEvent>(OnUpdateState);
|
||||||
|
|
||||||
|
// Access/Prying
|
||||||
|
SubscribeLocalEvent<FirelockComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
|
||||||
|
SubscribeLocalEvent<FirelockComponent, GetPryTimeModifierEvent>(OnDoorGetPryTimeModifier);
|
||||||
|
SubscribeLocalEvent<FirelockComponent, PriedEvent>(OnAfterPried);
|
||||||
|
|
||||||
|
// Visuals
|
||||||
|
SubscribeLocalEvent<FirelockComponent, MapInitEvent>(UpdateVisuals);
|
||||||
|
SubscribeLocalEvent<FirelockComponent, ComponentStartup>(UpdateVisuals);
|
||||||
|
}
|
||||||
|
|
||||||
|
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
|
||||||
|
|| firelock.EmergencyCloseCooldown != null
|
||||||
|
&& _gameTiming.CurTime < firelock.EmergencyCloseCooldown)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (!_doorSystem.TryClose(uid, door))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return _doorSystem.OnPartialClose(uid, door);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUpdateState(EntityUid uid, FirelockComponent component, DoorStateChangedEvent args)
|
||||||
|
{
|
||||||
|
var ev = new BeforeDoorAutoCloseEvent();
|
||||||
|
RaiseLocalEvent(uid, ev);
|
||||||
|
UpdateVisuals(uid, component, args);
|
||||||
|
if (ev.Cancelled)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_doorSystem.SetNextStateChange(uid, component.AutocloseDelay);
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Access/Prying
|
||||||
|
|
||||||
|
private void OnBeforeDoorOpened(EntityUid uid, FirelockComponent component, BeforeDoorOpenedEvent args)
|
||||||
|
{
|
||||||
|
// Give the Door remote the ability to force a firelock open even if it is holding back dangerous gas
|
||||||
|
var overrideAccess = (args.User != null) && _accessReaderSystem.IsAllowed(args.User.Value, uid);
|
||||||
|
|
||||||
|
if (!component.Powered || (!overrideAccess && component.IsLocked))
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDoorGetPryTimeModifier(EntityUid uid, FirelockComponent component, ref GetPryTimeModifierEvent args)
|
||||||
|
{
|
||||||
|
if (component.Temperature)
|
||||||
|
{
|
||||||
|
_popupSystem.PopupClient(Loc.GetString("firelock-component-is-holding-fire-message"),
|
||||||
|
uid, args.User, PopupType.MediumCaution);
|
||||||
|
}
|
||||||
|
else if (component.Pressure)
|
||||||
|
{
|
||||||
|
_popupSystem.PopupClient(Loc.GetString("firelock-component-is-holding-pressure-message"),
|
||||||
|
uid, args.User, PopupType.MediumCaution);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.IsLocked)
|
||||||
|
args.PryTimeModifier *= component.LockedPryTimeModifier;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAfterPried(EntityUid uid, FirelockComponent component, ref PriedEvent args)
|
||||||
|
{
|
||||||
|
component.EmergencyCloseCooldown = _gameTiming.CurTime + component.EmergencyCloseCooldownDuration;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
#region Visuals
|
||||||
|
|
||||||
|
private void UpdateVisuals(EntityUid uid, FirelockComponent component, EntityEventArgs args) => UpdateVisuals(uid, component);
|
||||||
|
|
||||||
|
private void UpdateVisuals(EntityUid uid,
|
||||||
|
FirelockComponent? firelock = null,
|
||||||
|
DoorComponent? door = null,
|
||||||
|
AppearanceComponent? appearance = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref door, ref appearance, 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;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Resolve(uid, ref firelock, ref appearance, false))
|
||||||
|
return;
|
||||||
|
|
||||||
|
_appearance.SetData(uid, DoorVisuals.ClosedLights, firelock.IsLocked, appearance);
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
}
|
||||||
@@ -8,4 +8,6 @@ namespace Content.Shared.Prying.Components;
|
|||||||
[RegisterComponent, NetworkedComponent]
|
[RegisterComponent, NetworkedComponent]
|
||||||
public sealed partial class PryUnpoweredComponent : Component
|
public sealed partial class PryUnpoweredComponent : Component
|
||||||
{
|
{
|
||||||
|
[DataField]
|
||||||
|
public float PryModifier = 0.1f;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,17 +93,17 @@ public sealed class PryingSystem : EntitySystem
|
|||||||
id = null;
|
id = null;
|
||||||
|
|
||||||
// We don't care about displaying a message if no tool was used.
|
// We don't care about displaying a message if no tool was used.
|
||||||
if (!CanPry(target, user, out _))
|
if (!TryComp<PryUnpoweredComponent>(target, out var unpoweredComp) || !CanPry(target, user, out _, unpoweredComp: unpoweredComp))
|
||||||
// If we have reached this point we want the event that caused this
|
// If we have reached this point we want the event that caused this
|
||||||
// to be marked as handled.
|
// to be marked as handled.
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
// hand-prying is much slower
|
// hand-prying is much slower
|
||||||
var modifier = CompOrNull<PryingComponent>(user)?.SpeedModifier ?? 0.1f;
|
var modifier = CompOrNull<PryingComponent>(user)?.SpeedModifier ?? unpoweredComp.PryModifier;
|
||||||
return StartPry(target, user, null, modifier, out id);
|
return StartPry(target, user, null, modifier, out id);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CanPry(EntityUid target, EntityUid user, out string? message, PryingComponent? comp = null)
|
private bool CanPry(EntityUid target, EntityUid user, out string? message, PryingComponent? comp = null, PryUnpoweredComponent? unpoweredComp = null)
|
||||||
{
|
{
|
||||||
BeforePryEvent canev;
|
BeforePryEvent canev;
|
||||||
|
|
||||||
@@ -113,7 +113,7 @@ public sealed class PryingSystem : EntitySystem
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (!TryComp<PryUnpoweredComponent>(target, out _))
|
if (!Resolve(target, ref unpoweredComp))
|
||||||
{
|
{
|
||||||
message = null;
|
message = null;
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
@@ -108,6 +108,8 @@
|
|||||||
- type: DoorBolt
|
- type: DoorBolt
|
||||||
- type: AccessReader
|
- type: AccessReader
|
||||||
access: [ [ "Engineering" ] ]
|
access: [ [ "Engineering" ] ]
|
||||||
|
- type: PryUnpowered
|
||||||
|
pryModifier: 0.5
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: Firelock
|
id: Firelock
|
||||||
|
|||||||
Reference in New Issue
Block a user