ECS airlocks (#13500)

This commit is contained in:
metalgearsloth
2023-01-18 05:44:32 +11:00
committed by GitHub
parent 8550baa218
commit 2873a830bd
19 changed files with 270 additions and 291 deletions

View File

@@ -1,186 +0,0 @@
using System.Threading;
using Content.Server.Power.Components;
// using Content.Server.WireHacking;
using Content.Shared.Doors.Components;
using Robust.Shared.Audio;
using Robust.Shared.Player;
// using static Content.Shared.Wires.SharedWiresComponent;
// using static Content.Shared.Wires.SharedWiresComponent.WiresAction;
namespace Content.Server.Doors.Components
{
/// <summary>
/// Companion component to DoorComponent that handles airlock-specific behavior -- wires, requiring power to operate, bolts, and allowing automatic closing.
/// </summary>
[RegisterComponent]
[ComponentReference(typeof(SharedAirlockComponent))]
public sealed class AirlockComponent : SharedAirlockComponent
{
[Dependency] private readonly IEntityManager _entityManager = default!;
/// <summary>
/// Sound to play when the bolts on the airlock go up.
/// </summary>
[DataField("boltUpSound")]
public SoundSpecifier BoltUpSound = new SoundPathSpecifier("/Audio/Machines/boltsup.ogg");
/// <summary>
/// Sound to play when the bolts on the airlock go down.
/// </summary>
[DataField("boltDownSound")]
public SoundSpecifier BoltDownSound = new SoundPathSpecifier("/Audio/Machines/boltsdown.ogg");
/// <summary>
/// Duration for which power will be disabled after pulsing either power wire.
/// </summary>
[DataField("powerWiresTimeout")]
public float PowerWiresTimeout = 5.0f;
/// <summary>
/// Pry modifier for a powered airlock.
/// Most anything that can pry powered has a pry speed bonus,
/// so this default is closer to 6 effectively on e.g. jaws (9 seconds when applied to other default.)
/// </summary>
[DataField("poweredPryModifier")]
public readonly float PoweredPryModifier = 9f;
/// <summary>
/// Whether the maintenance panel should be visible even if the airlock is opened.
/// </summary>
[DataField("openPanelVisible")]
public bool OpenPanelVisible = false;
/// <summary>
/// Whether the airlock should stay open if the airlock was clicked.
/// If the airlock was bumped into it will still auto close.
/// </summary>
[DataField("keepOpenIfClicked")]
public bool KeepOpenIfClicked = false;
private CancellationTokenSource _powerWiresPulsedTimerCancel = new();
private bool _powerWiresPulsed;
/// <summary>
/// True if either power wire was pulsed in the last <see cref="PowerWiresTimeout"/>.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
private bool PowerWiresPulsed
{
get => _powerWiresPulsed;
set
{
_powerWiresPulsed = value;
// UpdateWiresStatus();
// UpdatePowerCutStatus();
}
}
private bool _boltsDown;
[ViewVariables(VVAccess.ReadWrite)]
public bool BoltsDown
{
get => _boltsDown;
set
{
_boltsDown = value;
UpdateBoltLightStatus();
}
}
private bool _boltLightsEnabled = true;
public bool BoltLightsEnabled
{
get => _boltLightsEnabled;
set
{
_boltLightsEnabled = value;
UpdateBoltLightStatus();
}
}
[ViewVariables(VVAccess.ReadWrite)]
public bool BoltLightsVisible
{
get => _boltLightsEnabled && BoltsDown && IsPowered()
&& _entityManager.TryGetComponent<DoorComponent>(Owner, out var doorComponent) && doorComponent.State == DoorState.Closed;
set
{
_boltLightsEnabled = value;
UpdateBoltLightStatus();
}
}
/// <summary>
/// True if the bolt wire is cut, which will force the airlock to always be bolted as long as it has power.
/// </summary>
[ViewVariables]
public bool BoltWireCut;
/// <summary>
/// Whether the airlock should auto close. This value is reset every time the airlock closes.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public bool AutoClose = true;
/// <summary>
/// Delay until an open door automatically closes.
/// </summary>
[DataField("autoCloseDelay")]
public TimeSpan AutoCloseDelay = TimeSpan.FromSeconds(5f);
/// <summary>
/// Multiplicative modifier for the auto-close delay. Can be modified by hacking the airlock wires. Setting to
/// zero will disable auto-closing.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public float AutoCloseDelayModifier = 1.0f;
protected override void Initialize()
{
base.Initialize();
if (_entityManager.TryGetComponent<ApcPowerReceiverComponent>(Owner, out var receiverComponent) &&
_entityManager.TryGetComponent<AppearanceComponent>(Owner, out var appearanceComponent))
{
appearanceComponent.SetData(DoorVisuals.Powered, receiverComponent.Powered);
}
}
public bool CanChangeState()
{
return IsPowered() && !IsBolted();
}
public bool IsBolted()
{
return _boltsDown;
}
public bool IsPowered()
{
return !_entityManager.TryGetComponent<ApcPowerReceiverComponent>(Owner, out var receiverComponent) || receiverComponent.Powered;
}
public void UpdateBoltLightStatus()
{
if (_entityManager.TryGetComponent<AppearanceComponent>(Owner, out var appearanceComponent))
{
appearanceComponent.SetData(DoorVisuals.BoltLights, BoltLightsVisible);
}
}
public void SetBoltsWithAudio(bool newBolts)
{
if (newBolts == BoltsDown)
{
return;
}
BoltsDown = newBolts;
SoundSystem.Play(newBolts ? BoltDownSound.GetSound() : BoltUpSound.GetSound(), Filter.Pvs(Owner), Owner);
}
}
}

View File

@@ -1,4 +1,3 @@
using Content.Server.Doors.Components;
using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Shared.Tools.Components;
@@ -7,7 +6,6 @@ using Content.Shared.Doors;
using Content.Shared.Doors.Components;
using Content.Shared.Doors.Systems;
using Content.Shared.Interaction;
using Content.Shared.Popups;
using Robust.Server.GameObjects;
namespace Content.Server.Doors.Systems
@@ -21,6 +19,7 @@ namespace Content.Server.Doors.Systems
{
base.Initialize();
SubscribeLocalEvent<AirlockComponent, ComponentInit>(OnAirlockInit);
SubscribeLocalEvent<AirlockComponent, PowerChangedEvent>(OnPowerChanged);
SubscribeLocalEvent<AirlockComponent, DoorStateChangedEvent>(OnStateChanged);
SubscribeLocalEvent<AirlockComponent, BeforeDoorOpenedEvent>(OnBeforeDoorOpened);
@@ -30,11 +29,19 @@ namespace Content.Server.Doors.Systems
SubscribeLocalEvent<AirlockComponent, BeforeDoorPryEvent>(OnDoorPry);
}
private void OnAirlockInit(EntityUid uid, AirlockComponent component, ComponentInit args)
{
if (TryComp<ApcPowerReceiverComponent>(uid, out var receiverComponent))
{
Appearance.SetData(uid, DoorVisuals.Powered, receiverComponent.Powered);
}
}
private void OnPowerChanged(EntityUid uid, AirlockComponent component, ref PowerChangedEvent args)
{
if (TryComp<AppearanceComponent>(uid, out var appearanceComponent))
{
appearanceComponent.SetData(DoorVisuals.Powered, args.Powered);
Appearance.SetData(uid, DoorVisuals.Powered, args.Powered, appearanceComponent);
}
if (!TryComp(uid, out DoorComponent? door))
@@ -49,12 +56,13 @@ namespace Content.Server.Doors.Systems
else
{
if (component.BoltWireCut)
component.SetBoltsWithAudio(true);
SetBoltsWithAudio(uid, component, true);
UpdateAutoClose(uid, door: door);
}
// BoltLights also got out
component.UpdateBoltLightStatus();
UpdateBoltLightStatus(uid, component);
}
private void OnStateChanged(EntityUid uid, AirlockComponent component, DoorStateChangedEvent args)
@@ -70,8 +78,7 @@ namespace Content.Server.Doors.Systems
|| args.State != DoorState.Open;
}
// If the door is closed, we should look if the bolt was locked while closing
component.UpdateBoltLightStatus();
UpdateBoltLightStatus(uid, component);
UpdateAutoClose(uid, component);
// Make sure the airlock auto closes again next time it is opened
@@ -93,7 +100,7 @@ namespace Content.Server.Doors.Systems
if (!airlock.AutoClose)
return;
if (!airlock.CanChangeState())
if (!CanChangeState(uid, airlock))
return;
var autoev = new BeforeDoorAutoCloseEvent();
@@ -106,11 +113,11 @@ namespace Content.Server.Doors.Systems
private void OnBeforeDoorOpened(EntityUid uid, AirlockComponent component, BeforeDoorOpenedEvent args)
{
if (!component.CanChangeState())
if (!CanChangeState(uid, component))
args.Cancel();
}
protected override void OnBeforeDoorClosed(EntityUid uid, SharedAirlockComponent component, BeforeDoorClosedEvent args)
protected override void OnBeforeDoorClosed(EntityUid uid, AirlockComponent component, BeforeDoorClosedEvent args)
{
base.OnBeforeDoorClosed(uid, component, args);
@@ -123,7 +130,7 @@ namespace Content.Server.Doors.Systems
if (TryComp(uid, out DoorComponent? door)
&& !door.Partial
&& !Comp<AirlockComponent>(uid).CanChangeState())
&& !CanChangeState(uid, component))
{
args.Cancel();
}
@@ -131,7 +138,7 @@ namespace Content.Server.Doors.Systems
private void OnBeforeDoorDenied(EntityUid uid, AirlockComponent component, BeforeDoorDeniedEvent args)
{
if (!component.CanChangeState())
if (!CanChangeState(uid, component))
args.Cancel();
}
@@ -160,18 +167,68 @@ namespace Content.Server.Doors.Systems
private void OnDoorPry(EntityUid uid, AirlockComponent component, BeforeDoorPryEvent args)
{
if (component.IsBolted())
if (component.BoltsDown)
{
component.Owner.PopupMessage(args.User, Loc.GetString("airlock-component-cannot-pry-is-bolted-message"));
Popup.PopupEntity(Loc.GetString("airlock-component-cannot-pry-is-bolted-message"), uid, args.User);
args.Cancel();
}
if (component.IsPowered())
if (this.IsPowered(uid, EntityManager))
{
if (HasComp<ToolForcePoweredComponent>(args.Tool))
return;
component.Owner.PopupMessage(args.User, Loc.GetString("airlock-component-cannot-pry-is-powered-message"));
Popup.PopupEntity(Loc.GetString("airlock-component-cannot-pry-is-powered-message"), uid, args.User);
args.Cancel();
}
}
public bool CanChangeState(EntityUid uid, AirlockComponent component)
{
return this.IsPowered(uid, EntityManager) && !component.BoltsDown;
}
public void UpdateBoltLightStatus(EntityUid uid, AirlockComponent component)
{
if (!TryComp<AppearanceComponent>(uid, out var appearance))
return;
Appearance.SetData(uid, DoorVisuals.BoltLights, GetBoltLightsVisible(uid, component), appearance);
}
public void SetBoltsWithAudio(EntityUid uid, AirlockComponent component, bool newBolts)
{
if (newBolts == component.BoltsDown)
return;
component.BoltsDown = newBolts;
Audio.PlayPvs(newBolts ? component.BoltDownSound : component.BoltUpSound, uid);
}
public bool GetBoltLightsVisible(EntityUid uid, AirlockComponent component)
{
return component.BoltLightsEnabled &&
component.BoltsDown &&
this.IsPowered(uid, EntityManager) &&
TryComp<DoorComponent>(uid, out var doorComponent) &&
doorComponent.State == DoorState.Closed;
}
public void SetBoltLightsEnabled(EntityUid uid, AirlockComponent component, bool value)
{
if (component.BoltLightsEnabled == value)
return;
component.BoltLightsEnabled = value;
UpdateBoltLightStatus(uid, component);
}
public void SetBoltsDown(EntityUid uid, AirlockComponent component, bool value)
{
if (component.BoltsDown == value)
return;
component.BoltsDown = value;
UpdateBoltLightStatus(uid, component);
}
}
}

View File

@@ -19,6 +19,7 @@ using Robust.Shared.Audio;
using Robust.Shared.Containers;
using Robust.Shared.Player;
using System.Linq;
using Content.Server.Power.EntitySystems;
using Robust.Shared.Physics.Components;
using Robust.Shared.Physics.Events;
@@ -27,6 +28,7 @@ namespace Content.Server.Doors.Systems;
public sealed class DoorSystem : SharedDoorSystem
{
[Dependency] private readonly AccessReaderSystem _accessReaderSystem = default!;
[Dependency] private readonly AirlockSystem _airlock = default!;
[Dependency] private readonly AirtightSystem _airtightSystem = default!;
[Dependency] private readonly ConstructionSystem _constructionSystem = default!;
[Dependency] private readonly ToolSystem _toolSystem = default!;
@@ -230,7 +232,7 @@ public sealed class DoorSystem : SharedDoorSystem
return true;
// If the door is on emergency access we skip the checks.
if (TryComp<SharedAirlockComponent>(uid, out var airlock) && airlock.EmergencyAccess)
if (TryComp<AirlockComponent>(uid, out var airlock) && airlock.EmergencyAccess)
return true;
if (!Resolve(uid, ref access, false))
@@ -270,7 +272,7 @@ public sealed class DoorSystem : SharedDoorSystem
{
if(TryComp<AirlockComponent>(uid, out var airlockComponent))
{
if (airlockComponent.BoltsDown || !airlockComponent.IsPowered())
if (airlockComponent.BoltsDown || !this.IsPowered(uid, EntityManager))
return;
if (door.State == DoorState.Closed)
@@ -294,8 +296,8 @@ public sealed class DoorSystem : SharedDoorSystem
if (door.OpenSound != null)
PlaySound(uid, door.OpenSound, AudioParams.Default.WithVolume(-5), user, predicted);
if(lastState == DoorState.Emagging && TryComp<AirlockComponent>(door.Owner, out var airlockComponent))
airlockComponent?.SetBoltsWithAudio(!airlockComponent.IsBolted());
if(lastState == DoorState.Emagging && TryComp<AirlockComponent>(uid, out var airlockComponent))
_airlock.SetBoltsWithAudio(uid, airlockComponent, !airlockComponent.BoltsDown);
}
protected override void CheckDoorBump(DoorComponent component, PhysicsComponent body)

View File

@@ -1,6 +1,8 @@
using Content.Server.Doors.Components;
using Content.Server.Doors.Systems;
using Content.Server.Wires;
using Content.Shared.Doors;
using Content.Shared.Doors.Components;
using Content.Shared.Wires;
namespace Content.Server.Doors;
@@ -21,7 +23,7 @@ public sealed class DoorBoltLightWireAction : BaseWireAction
public override StatusLightData? GetStatusLightData(Wire wire)
{
StatusLightState lightState = StatusLightState.Off;
var lightState = StatusLightState.Off;
if (IsPowered(wire.Owner) && EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
{
lightState = door.BoltLightsEnabled
@@ -42,7 +44,7 @@ public sealed class DoorBoltLightWireAction : BaseWireAction
base.Cut(user, wire);
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
{
door.BoltLightsVisible = false;
EntityManager.System<AirlockSystem>().SetBoltLightsEnabled(wire.Owner, door, false);
}
return true;
@@ -53,7 +55,7 @@ public sealed class DoorBoltLightWireAction : BaseWireAction
base.Mend(user, wire);
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
{
door.BoltLightsVisible = true;
EntityManager.System<AirlockSystem>().SetBoltLightsEnabled(wire.Owner, door, true);
}
return true;
@@ -64,7 +66,7 @@ public sealed class DoorBoltLightWireAction : BaseWireAction
base.Pulse(user, wire);
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
{
door.BoltLightsVisible = !door.BoltLightsEnabled;
EntityManager.System<AirlockSystem>().SetBoltLightsEnabled(wire.Owner, door, !door.BoltLightsEnabled);
}
return true;

View File

@@ -1,6 +1,9 @@
using Content.Server.Doors.Components;
using Content.Server.Doors.Systems;
using Content.Server.Wires;
using Content.Shared.Doors;
using Content.Shared.Doors.Components;
using Content.Shared.Doors.Systems;
using Content.Shared.Wires;
namespace Content.Server.Doors;
@@ -42,11 +45,11 @@ public sealed class DoorBoltWireAction : BaseWireAction
public override bool Cut(EntityUid user, Wire wire)
{
base.Cut(user, wire);
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var airlock))
{
door.BoltWireCut = true;
if (!door.BoltsDown && IsPowered(wire.Owner))
door.SetBoltsWithAudio(true);
EntityManager.System<SharedAirlockSystem>().SetBoltWireCut(airlock, true);
if (!airlock.BoltsDown && IsPowered(wire.Owner))
EntityManager.System<AirlockSystem>().SetBoltsWithAudio(wire.Owner, airlock, true);
}
return true;
@@ -56,7 +59,7 @@ public sealed class DoorBoltWireAction : BaseWireAction
{
base.Mend(user, wire);
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
door.BoltWireCut = false;
EntityManager.System<SharedAirlockSystem>().SetBoltWireCut(door, true);
return true;
}
@@ -68,11 +71,11 @@ public sealed class DoorBoltWireAction : BaseWireAction
{
if (IsPowered(wire.Owner))
{
door.SetBoltsWithAudio(!door.BoltsDown);
EntityManager.System<AirlockSystem>().SetBoltsWithAudio(wire.Owner, door, !door.BoltsDown);
}
else if (!door.BoltsDown)
{
door.SetBoltsWithAudio(true);
EntityManager.System<AirlockSystem>().SetBoltsWithAudio(wire.Owner, door, true);
}
}

View File

@@ -1,6 +1,8 @@
using Content.Server.Doors.Components;
using Content.Server.Wires;
using Content.Shared.Doors;
using Content.Shared.Doors.Components;
using Content.Shared.Doors.Systems;
using Content.Shared.Wires;
namespace Content.Server.Doors;
@@ -47,7 +49,7 @@ public sealed class DoorSafetyWireAction : BaseWireAction
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
{
WiresSystem.TryCancelWireAction(wire.Owner, PulseTimeoutKey.Key);
door.Safety = false;
EntityManager.System<SharedAirlockSystem>().SetSafety(door, false);
}
return true;
@@ -58,7 +60,7 @@ public sealed class DoorSafetyWireAction : BaseWireAction
base.Mend(user, wire);
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
{
door.Safety = true;
EntityManager.System<SharedAirlockSystem>().SetSafety(door, true);
}
return true;
@@ -69,7 +71,7 @@ public sealed class DoorSafetyWireAction : BaseWireAction
base.Pulse(user, wire);
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
{
door.Safety = false;
EntityManager.System<SharedAirlockSystem>().SetSafety(door, false);
WiresSystem.StartWireAction(wire.Owner, _timeout, PulseTimeoutKey.Key, new TimedWireEvent(AwaitSafetyTimerFinish, wire));
}
@@ -90,7 +92,7 @@ public sealed class DoorSafetyWireAction : BaseWireAction
{
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
{
door.Safety = true;
EntityManager.System<SharedAirlockSystem>().SetSafety(door, true);
}
}
}

View File

@@ -1,6 +1,8 @@
using Content.Server.Doors.Components;
using Content.Server.Wires;
using Content.Shared.Doors;
using Content.Shared.Doors.Components;
using Content.Shared.Doors.Systems;
using Content.Shared.Wires;
namespace Content.Server.Doors;
@@ -56,7 +58,7 @@ public sealed class DoorTimingWireAction : BaseWireAction
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
{
WiresSystem.TryCancelWireAction(wire.Owner, PulseTimeoutKey.Key);
door.AutoCloseDelayModifier = 0.01f;
EntityManager.System<SharedAirlockSystem>().SetAutoCloseDelayModifier(door, 0.01f);
}
return true;
@@ -67,7 +69,7 @@ public sealed class DoorTimingWireAction : BaseWireAction
base.Mend(user, wire);
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
{
door.AutoCloseDelayModifier = 1f;
EntityManager.System<SharedAirlockSystem>().SetAutoCloseDelayModifier(door, 1f);
}
return true;
@@ -78,7 +80,7 @@ public sealed class DoorTimingWireAction : BaseWireAction
base.Pulse(user, wire);
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
{
door.AutoCloseDelayModifier = 0.5f;
EntityManager.System<SharedAirlockSystem>().SetAutoCloseDelayModifier(door, 0.5f);
WiresSystem.StartWireAction(wire.Owner, _timeout, PulseTimeoutKey.Key, new TimedWireEvent(AwaitTimingTimerFinish, wire));
}
@@ -101,7 +103,7 @@ public sealed class DoorTimingWireAction : BaseWireAction
{
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
{
door.AutoCloseDelayModifier = 1f;
EntityManager.System<SharedAirlockSystem>().SetAutoCloseDelayModifier(door, 1f);
}
}
}