diff --git a/Content.Client/Doors/AirlockComponent.cs b/Content.Client/Doors/AirlockComponent.cs deleted file mode 100644 index d22994ab6e..0000000000 --- a/Content.Client/Doors/AirlockComponent.cs +++ /dev/null @@ -1,7 +0,0 @@ -using Content.Shared.Doors.Components; - -namespace Content.Client.Doors; - -[RegisterComponent] -[ComponentReference(typeof(SharedAirlockComponent))] -public sealed class AirlockComponent : SharedAirlockComponent { } diff --git a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs index 68455ce9ad..dd74105ca3 100644 --- a/Content.IntegrationTests/Tests/Doors/AirlockTest.cs +++ b/Content.IntegrationTests/Tests/Doors/AirlockTest.cs @@ -38,6 +38,8 @@ namespace Content.IntegrationTests.Tests.Doors components: - type: Door - type: Airlock + - type: ApcPowerReceiver + needsPower: false - type: Physics bodyType: Static - type: Fixtures @@ -54,7 +56,6 @@ namespace Content.IntegrationTests.Tests.Doors await using var pairTracker = await PoolManager.GetServerClient(new PoolSettings{NoClient = true, ExtraPrototypes = Prototypes}); var server = pairTracker.Pair.Server; - var mapManager = server.ResolveDependency(); var entityManager = server.ResolveDependency(); var doors = entityManager.EntitySysManager.GetEntitySystem(); diff --git a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs index 0a70180e1a..8c616bccb0 100644 --- a/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs +++ b/Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs @@ -23,6 +23,7 @@ using Content.Shared.Construction.Components; using Content.Shared.Containers.ItemSlots; using Content.Shared.Damage; using Content.Shared.Database; +using Content.Shared.Doors.Components; using Content.Shared.Inventory; using Content.Shared.PDA; using Content.Shared.Stacks; @@ -72,7 +73,7 @@ public sealed partial class AdminVerbSystem : "/Textures/Interface/AdminActions/bolt.png", Act = () => { - airlock.SetBoltsWithAudio(!airlock.BoltsDown); + _airlockSystem.SetBoltsWithAudio(args.Target, airlock, !airlock.BoltsDown); }, Impact = LogImpact.Medium, Message = Loc.GetString(airlock.BoltsDown @@ -90,7 +91,7 @@ public sealed partial class AdminVerbSystem IconTexture = "/Textures/Interface/AdminActions/emergency_access.png", Act = () => { - _airlockSystem.ToggleEmergencyAccess(airlock); + _airlockSystem.ToggleEmergencyAccess(args.Target, airlock); }, Impact = LogImpact.Medium, Message = Loc.GetString(airlock.EmergencyAccess diff --git a/Content.Server/Construction/Conditions/AirlockBolted.cs b/Content.Server/Construction/Conditions/AirlockBolted.cs index 4d130da573..8ba908fef3 100644 --- a/Content.Server/Construction/Conditions/AirlockBolted.cs +++ b/Content.Server/Construction/Conditions/AirlockBolted.cs @@ -1,6 +1,7 @@ using Content.Shared.Construction; using JetBrains.Annotations; using Content.Server.Doors.Components; +using Content.Shared.Doors.Components; using Content.Shared.Examine; namespace Content.Server.Construction.Conditions diff --git a/Content.Server/Doors/Components/AirlockComponent.cs b/Content.Server/Doors/Components/AirlockComponent.cs deleted file mode 100644 index d1cc54a33e..0000000000 --- a/Content.Server/Doors/Components/AirlockComponent.cs +++ /dev/null @@ -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 -{ - /// - /// Companion component to DoorComponent that handles airlock-specific behavior -- wires, requiring power to operate, bolts, and allowing automatic closing. - /// - [RegisterComponent] - [ComponentReference(typeof(SharedAirlockComponent))] - public sealed class AirlockComponent : SharedAirlockComponent - { - [Dependency] private readonly IEntityManager _entityManager = default!; - - /// - /// Sound to play when the bolts on the airlock go up. - /// - [DataField("boltUpSound")] - public SoundSpecifier BoltUpSound = new SoundPathSpecifier("/Audio/Machines/boltsup.ogg"); - - /// - /// Sound to play when the bolts on the airlock go down. - /// - [DataField("boltDownSound")] - public SoundSpecifier BoltDownSound = new SoundPathSpecifier("/Audio/Machines/boltsdown.ogg"); - - /// - /// Duration for which power will be disabled after pulsing either power wire. - /// - [DataField("powerWiresTimeout")] - public float PowerWiresTimeout = 5.0f; - - /// - /// 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.) - /// - [DataField("poweredPryModifier")] - public readonly float PoweredPryModifier = 9f; - - /// - /// Whether the maintenance panel should be visible even if the airlock is opened. - /// - [DataField("openPanelVisible")] - public bool OpenPanelVisible = false; - - /// - /// Whether the airlock should stay open if the airlock was clicked. - /// If the airlock was bumped into it will still auto close. - /// - [DataField("keepOpenIfClicked")] - public bool KeepOpenIfClicked = false; - - private CancellationTokenSource _powerWiresPulsedTimerCancel = new(); - private bool _powerWiresPulsed; - - /// - /// True if either power wire was pulsed in the last . - /// - [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(Owner, out var doorComponent) && doorComponent.State == DoorState.Closed; - set - { - _boltLightsEnabled = value; - UpdateBoltLightStatus(); - } - } - - /// - /// True if the bolt wire is cut, which will force the airlock to always be bolted as long as it has power. - /// - [ViewVariables] - public bool BoltWireCut; - - /// - /// Whether the airlock should auto close. This value is reset every time the airlock closes. - /// - [ViewVariables(VVAccess.ReadWrite)] - public bool AutoClose = true; - - /// - /// Delay until an open door automatically closes. - /// - [DataField("autoCloseDelay")] - public TimeSpan AutoCloseDelay = TimeSpan.FromSeconds(5f); - - /// - /// Multiplicative modifier for the auto-close delay. Can be modified by hacking the airlock wires. Setting to - /// zero will disable auto-closing. - /// - [ViewVariables(VVAccess.ReadWrite)] - public float AutoCloseDelayModifier = 1.0f; - - protected override void Initialize() - { - base.Initialize(); - - if (_entityManager.TryGetComponent(Owner, out var receiverComponent) && - _entityManager.TryGetComponent(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(Owner, out var receiverComponent) || receiverComponent.Powered; - } - - public void UpdateBoltLightStatus() - { - if (_entityManager.TryGetComponent(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); - } - } -} diff --git a/Content.Server/Doors/Systems/AirlockSystem.cs b/Content.Server/Doors/Systems/AirlockSystem.cs index 46c4c9dc04..e6f4d0f0ce 100644 --- a/Content.Server/Doors/Systems/AirlockSystem.cs +++ b/Content.Server/Doors/Systems/AirlockSystem.cs @@ -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(OnAirlockInit); SubscribeLocalEvent(OnPowerChanged); SubscribeLocalEvent(OnStateChanged); SubscribeLocalEvent(OnBeforeDoorOpened); @@ -30,11 +29,19 @@ namespace Content.Server.Doors.Systems SubscribeLocalEvent(OnDoorPry); } + private void OnAirlockInit(EntityUid uid, AirlockComponent component, ComponentInit args) + { + if (TryComp(uid, out var receiverComponent)) + { + Appearance.SetData(uid, DoorVisuals.Powered, receiverComponent.Powered); + } + } + private void OnPowerChanged(EntityUid uid, AirlockComponent component, ref PowerChangedEvent args) { if (TryComp(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(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(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(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(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); + } } } diff --git a/Content.Server/Doors/Systems/DoorSystem.cs b/Content.Server/Doors/Systems/DoorSystem.cs index 3b2fbf30f1..ee4897ee8d 100644 --- a/Content.Server/Doors/Systems/DoorSystem.cs +++ b/Content.Server/Doors/Systems/DoorSystem.cs @@ -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(uid, out var airlock) && airlock.EmergencyAccess) + if (TryComp(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(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(door.Owner, out var airlockComponent)) - airlockComponent?.SetBoltsWithAudio(!airlockComponent.IsBolted()); + if(lastState == DoorState.Emagging && TryComp(uid, out var airlockComponent)) + _airlock.SetBoltsWithAudio(uid, airlockComponent, !airlockComponent.BoltsDown); } protected override void CheckDoorBump(DoorComponent component, PhysicsComponent body) diff --git a/Content.Server/Doors/WireActions/DoorBoltLightWireAction.cs b/Content.Server/Doors/WireActions/DoorBoltLightWireAction.cs index 7538c384f2..97f026a2bd 100644 --- a/Content.Server/Doors/WireActions/DoorBoltLightWireAction.cs +++ b/Content.Server/Doors/WireActions/DoorBoltLightWireAction.cs @@ -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(wire.Owner, out var door)) { lightState = door.BoltLightsEnabled @@ -42,7 +44,7 @@ public sealed class DoorBoltLightWireAction : BaseWireAction base.Cut(user, wire); if (EntityManager.TryGetComponent(wire.Owner, out var door)) { - door.BoltLightsVisible = false; + EntityManager.System().SetBoltLightsEnabled(wire.Owner, door, false); } return true; @@ -53,7 +55,7 @@ public sealed class DoorBoltLightWireAction : BaseWireAction base.Mend(user, wire); if (EntityManager.TryGetComponent(wire.Owner, out var door)) { - door.BoltLightsVisible = true; + EntityManager.System().SetBoltLightsEnabled(wire.Owner, door, true); } return true; @@ -64,7 +66,7 @@ public sealed class DoorBoltLightWireAction : BaseWireAction base.Pulse(user, wire); if (EntityManager.TryGetComponent(wire.Owner, out var door)) { - door.BoltLightsVisible = !door.BoltLightsEnabled; + EntityManager.System().SetBoltLightsEnabled(wire.Owner, door, !door.BoltLightsEnabled); } return true; diff --git a/Content.Server/Doors/WireActions/DoorBoltWireAction.cs b/Content.Server/Doors/WireActions/DoorBoltWireAction.cs index 32d4c31533..e59f30a6a1 100644 --- a/Content.Server/Doors/WireActions/DoorBoltWireAction.cs +++ b/Content.Server/Doors/WireActions/DoorBoltWireAction.cs @@ -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(wire.Owner, out var door)) + if (EntityManager.TryGetComponent(wire.Owner, out var airlock)) { - door.BoltWireCut = true; - if (!door.BoltsDown && IsPowered(wire.Owner)) - door.SetBoltsWithAudio(true); + EntityManager.System().SetBoltWireCut(airlock, true); + if (!airlock.BoltsDown && IsPowered(wire.Owner)) + EntityManager.System().SetBoltsWithAudio(wire.Owner, airlock, true); } return true; @@ -56,7 +59,7 @@ public sealed class DoorBoltWireAction : BaseWireAction { base.Mend(user, wire); if (EntityManager.TryGetComponent(wire.Owner, out var door)) - door.BoltWireCut = false; + EntityManager.System().SetBoltWireCut(door, true); return true; } @@ -68,11 +71,11 @@ public sealed class DoorBoltWireAction : BaseWireAction { if (IsPowered(wire.Owner)) { - door.SetBoltsWithAudio(!door.BoltsDown); + EntityManager.System().SetBoltsWithAudio(wire.Owner, door, !door.BoltsDown); } else if (!door.BoltsDown) { - door.SetBoltsWithAudio(true); + EntityManager.System().SetBoltsWithAudio(wire.Owner, door, true); } } diff --git a/Content.Server/Doors/WireActions/DoorSafetyWireAction.cs b/Content.Server/Doors/WireActions/DoorSafetyWireAction.cs index 241c628ddf..2de1e2b2e9 100644 --- a/Content.Server/Doors/WireActions/DoorSafetyWireAction.cs +++ b/Content.Server/Doors/WireActions/DoorSafetyWireAction.cs @@ -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(wire.Owner, out var door)) { WiresSystem.TryCancelWireAction(wire.Owner, PulseTimeoutKey.Key); - door.Safety = false; + EntityManager.System().SetSafety(door, false); } return true; @@ -58,7 +60,7 @@ public sealed class DoorSafetyWireAction : BaseWireAction base.Mend(user, wire); if (EntityManager.TryGetComponent(wire.Owner, out var door)) { - door.Safety = true; + EntityManager.System().SetSafety(door, true); } return true; @@ -69,7 +71,7 @@ public sealed class DoorSafetyWireAction : BaseWireAction base.Pulse(user, wire); if (EntityManager.TryGetComponent(wire.Owner, out var door)) { - door.Safety = false; + EntityManager.System().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(wire.Owner, out var door)) { - door.Safety = true; + EntityManager.System().SetSafety(door, true); } } } diff --git a/Content.Server/Doors/WireActions/DoorTimingWireAction.cs b/Content.Server/Doors/WireActions/DoorTimingWireAction.cs index 8cd3e5e399..cbeaa3fba3 100644 --- a/Content.Server/Doors/WireActions/DoorTimingWireAction.cs +++ b/Content.Server/Doors/WireActions/DoorTimingWireAction.cs @@ -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(wire.Owner, out var door)) { WiresSystem.TryCancelWireAction(wire.Owner, PulseTimeoutKey.Key); - door.AutoCloseDelayModifier = 0.01f; + EntityManager.System().SetAutoCloseDelayModifier(door, 0.01f); } return true; @@ -67,7 +69,7 @@ public sealed class DoorTimingWireAction : BaseWireAction base.Mend(user, wire); if (EntityManager.TryGetComponent(wire.Owner, out var door)) { - door.AutoCloseDelayModifier = 1f; + EntityManager.System().SetAutoCloseDelayModifier(door, 1f); } return true; @@ -78,7 +80,7 @@ public sealed class DoorTimingWireAction : BaseWireAction base.Pulse(user, wire); if (EntityManager.TryGetComponent(wire.Owner, out var door)) { - door.AutoCloseDelayModifier = 0.5f; + EntityManager.System().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(wire.Owner, out var door)) { - door.AutoCloseDelayModifier = 1f; + EntityManager.System().SetAutoCloseDelayModifier(door, 1f); } } } diff --git a/Content.Server/Magic/MagicSystem.cs b/Content.Server/Magic/MagicSystem.cs index 52e8453220..79fbfaf1d5 100644 --- a/Content.Server/Magic/MagicSystem.cs +++ b/Content.Server/Magic/MagicSystem.cs @@ -3,7 +3,7 @@ using Content.Server.Body.Components; using Content.Server.Body.Systems; using Content.Server.Coordinates.Helpers; using Content.Server.DoAfter; -using Content.Server.Doors.Components; +using Content.Server.Doors.Systems; using Content.Server.Magic.Events; using Content.Server.Weapons.Ranged.Systems; using Content.Shared.Actions; @@ -18,9 +18,7 @@ using Content.Shared.Spawners.Components; using Content.Shared.Storage; using Robust.Server.GameObjects; using Robust.Shared.Audio; -using Robust.Shared.GameObjects; using Robust.Shared.Map; -using Robust.Shared.Physics.Components; using Robust.Shared.Player; using Robust.Shared.Prototypes; using Robust.Shared.Random; @@ -38,6 +36,7 @@ public sealed class MagicSystem : EntitySystem [Dependency] private readonly IMapManager _mapManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IRobustRandom _random = default!; + [Dependency] private readonly AirlockSystem _airlock = default!; [Dependency] private readonly BodySystem _bodySystem = default!; [Dependency] private readonly EntityLookupSystem _lookup = default!; [Dependency] private readonly SharedDoorSystem _doorSystem = default!; @@ -46,6 +45,7 @@ public sealed class MagicSystem : EntitySystem [Dependency] private readonly GunSystem _gunSystem = default!; [Dependency] private readonly PhysicsSystem _physics = default!; [Dependency] private readonly SharedTransformSystem _transformSystem = default!; + [Dependency] private readonly SharedAudioSystem _audio = default!; public override void Initialize() { @@ -272,7 +272,7 @@ public sealed class MagicSystem : EntitySystem _transformSystem.SetCoordinates(args.Performer, args.Target); transform.AttachToGridOrMap(); - SoundSystem.Play(args.BlinkSound.GetSound(), Filter.Pvs(args.Target), args.Performer, AudioParams.Default.WithVolume(args.BlinkVolume)); + _audio.PlayPvs(args.BlinkSound, args.Performer, AudioParams.Default.WithVolume(args.BlinkVolume)); args.Handled = true; } @@ -289,13 +289,13 @@ public sealed class MagicSystem : EntitySystem var transform = Transform(args.Performer); var coords = transform.Coordinates; - SoundSystem.Play(args.KnockSound.GetSound(), Filter.Pvs(coords), args.Performer, AudioParams.Default.WithVolume(args.KnockVolume)); + _audio.PlayPvs(args.KnockSound, args.Performer, AudioParams.Default.WithVolume(args.KnockVolume)); //Look for doors and don't open them if they're already open. foreach (var entity in _lookup.GetEntitiesInRange(coords, args.Range)) { if (TryComp(entity, out var airlock)) - airlock.BoltsDown = false; + _airlock.SetBoltsDown(entity, airlock, false); if (TryComp(entity, out var doorComp) && doorComp.State is not DoorState.Open) _doorSystem.StartOpening(doorComp.Owner); diff --git a/Content.Server/Power/EntitySystems/StaticPowerSystem.cs b/Content.Server/Power/EntitySystems/StaticPowerSystem.cs index 74b94113d1..9e11d9311a 100644 --- a/Content.Server/Power/EntitySystems/StaticPowerSystem.cs +++ b/Content.Server/Power/EntitySystems/StaticPowerSystem.cs @@ -8,6 +8,9 @@ public static class StaticPowerSystem // ReSharper disable once UnusedParameter.Global public static bool IsPowered(this EntitySystem system, EntityUid uid, IEntityManager entManager, ApcPowerReceiverComponent? receiver = null) { - return entManager.TryGetComponent(uid, out receiver) && receiver.Powered; + if (receiver == null && !entManager.TryGetComponent(uid, out receiver)) + return false; + + return receiver.Powered; } } diff --git a/Content.Server/Remotes/DoorRemoteSystem.cs b/Content.Server/Remotes/DoorRemoteSystem.cs index 1885bc98db..fc42bb4b51 100644 --- a/Content.Server/Remotes/DoorRemoteSystem.cs +++ b/Content.Server/Remotes/DoorRemoteSystem.cs @@ -18,10 +18,11 @@ namespace Content.Server.Remotes public sealed class DoorRemoteSystem : EntitySystem { [Dependency] private readonly IAdminLogManager _adminLogger = default!; + [Dependency] private readonly AirlockSystem _airlock = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly DoorSystem _doorSystem = default!; [Dependency] private readonly SharedInteractionSystem _interactionSystem = default!; - [Dependency] private readonly SharedAirlockSystem _sharedAirlockSystem = default!; + // I'm so sorry [Dependency] private readonly SharedAirlockSystem _sharedAirlockSystem = default!; public override void Initialize() { @@ -90,12 +91,12 @@ namespace Content.Server.Remotes case OperatingMode.ToggleBolts: if (!airlockComp.BoltWireCut) { - airlockComp.SetBoltsWithAudio(!airlockComp.IsBolted()); - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} used {ToPrettyString(args.Used)} on {ToPrettyString(args.Target.Value)} to {(airlockComp.IsBolted() ? "" : "un")}bolt it"); + _airlock.SetBoltsWithAudio(uid, airlockComp, !airlockComp.BoltsDown); + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} used {ToPrettyString(args.Used)} on {ToPrettyString(args.Target.Value)} to {(airlockComp.BoltsDown ? "" : "un")}bolt it"); } break; case OperatingMode.ToggleEmergencyAccess: - _sharedAirlockSystem.ToggleEmergencyAccess(airlockComp); + _airlock.ToggleEmergencyAccess(uid, airlockComp); _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(args.User):player} used {ToPrettyString(args.Used)} on {ToPrettyString(args.Target.Value)} to set emergency access {(airlockComp.EmergencyAccess ? "on" : "off")}"); break; default: diff --git a/Content.Server/Shuttles/Systems/DockingSystem.cs b/Content.Server/Shuttles/Systems/DockingSystem.cs index ecf3fd0ec5..571246a514 100644 --- a/Content.Server/Shuttles/Systems/DockingSystem.cs +++ b/Content.Server/Shuttles/Systems/DockingSystem.cs @@ -1,4 +1,3 @@ -using Content.Server.Doors.Components; using Content.Server.Doors.Systems; using Content.Server.NPC.Pathfinding; using Content.Server.Shuttles.Components; @@ -10,7 +9,6 @@ using Robust.Shared.Map; using Robust.Shared.Physics; using Robust.Shared.Physics.Collision.Shapes; using Robust.Shared.Physics.Components; -using Robust.Shared.Physics.Dynamics; using Robust.Shared.Physics.Dynamics.Joints; using Robust.Shared.Physics.Systems; using Robust.Shared.Utility; @@ -20,6 +18,7 @@ namespace Content.Server.Shuttles.Systems public sealed partial class DockingSystem : EntitySystem { [Dependency] private readonly IMapManager _mapManager = default!; + [Dependency] private readonly AirlockSystem _airlocks = default!; [Dependency] private readonly DoorSystem _doorSystem = default!; [Dependency] private readonly FixtureSystem _fixtureSystem = default!; [Dependency] private readonly PathfindingSystem _pathfinding = default!; @@ -273,7 +272,7 @@ namespace Content.Server.Shuttles.Systems // Listen it makes intersection tests easier; you can probably dump this but it requires a bunch more boilerplate // TODO: I want this to ideally be 2 fixtures to force them to have some level of alignment buuuttt // I also need collisionmanager for that yet again so they get dis. - _fixtureSystem.TryCreateFixture(uid, shape, DockingFixture, hard: false); + _fixtureSystem.TryCreateFixture(uid, shape, DockingFixture, hard: false, body: physicsComponent); } /// @@ -351,7 +350,7 @@ namespace Content.Server.Shuttles.Systems doorA.ChangeAirtight = false; if (TryComp(dockA.Owner, out var airlockA)) { - airlockA.SetBoltsWithAudio(true); + _airlocks.SetBoltsWithAudio(dockA.Owner, airlockA, true); } } } @@ -363,7 +362,7 @@ namespace Content.Server.Shuttles.Systems doorB.ChangeAirtight = false; if (TryComp(dockB.Owner, out var airlockB)) { - airlockB.SetBoltsWithAudio(true); + _airlocks.SetBoltsWithAudio(dockB.Owner, airlockB, true); } } } @@ -448,12 +447,12 @@ namespace Content.Server.Shuttles.Systems if (TryComp(dock.Owner, out var airlockA)) { - airlockA.SetBoltsWithAudio(false); + _airlocks.SetBoltsWithAudio(dock.Owner, airlockA, false); } if (TryComp(dock.DockedWith, out var airlockB)) { - airlockB.SetBoltsWithAudio(false); + _airlocks.SetBoltsWithAudio(dock.DockedWith.Value, airlockB, false); } if (TryComp(dock.Owner, out DoorComponent? doorA)) diff --git a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs index 5be20d2238..ef55fc3d26 100644 --- a/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs +++ b/Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs @@ -14,6 +14,7 @@ using Robust.Shared.Utility; using System.Diagnostics.CodeAnalysis; using Content.Server.Shuttles.Events; using Content.Shared.Buckle.Components; +using Content.Shared.Doors.Components; using Robust.Shared.Map.Components; using Robust.Shared.Physics.Components; @@ -25,6 +26,7 @@ public sealed partial class ShuttleSystem * This is a way to move a shuttle from one location to another, via an intermediate map for fanciness. */ + [Dependency] private readonly AirlockSystem _airlock = default!; [Dependency] private readonly DoorSystem _doors = default!; [Dependency] private readonly ShuttleConsoleSystem _console = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; @@ -344,7 +346,7 @@ public sealed partial class ShuttleSystem if (xform.ParentUid != uid) continue; _doors.TryClose(door.Owner); - door.SetBoltsWithAudio(enabled); + _airlock.SetBoltsWithAudio(door.Owner, door, enabled); } } diff --git a/Content.Shared/Doors/Components/AirlockComponent.cs b/Content.Shared/Doors/Components/AirlockComponent.cs new file mode 100644 index 0000000000..d122acce11 --- /dev/null +++ b/Content.Shared/Doors/Components/AirlockComponent.cs @@ -0,0 +1,96 @@ +using System.Threading; +using Content.Shared.Doors.Systems; +using Robust.Shared.Audio; +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.Doors.Components; + +/// +/// Companion component to DoorComponent that handles airlock-specific behavior -- wires, requiring power to operate, bolts, and allowing automatic closing. +/// +[RegisterComponent, NetworkedComponent] +[Access(typeof(SharedAirlockSystem), Friend = AccessPermissions.ReadWriteExecute, Other = AccessPermissions.Read)] +public sealed class AirlockComponent : Component +{ + [ViewVariables(VVAccess.ReadWrite)] + [DataField("safety")] + public bool Safety = true; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("emergencyAccess")] + public bool EmergencyAccess = false; + + /// + /// Sound to play when the bolts on the airlock go up. + /// + [DataField("boltUpSound")] + public SoundSpecifier BoltUpSound = new SoundPathSpecifier("/Audio/Machines/boltsup.ogg"); + + /// + /// Sound to play when the bolts on the airlock go down. + /// + [DataField("boltDownSound")] + public SoundSpecifier BoltDownSound = new SoundPathSpecifier("/Audio/Machines/boltsdown.ogg"); + + /// + /// 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.) + /// + [DataField("poweredPryModifier")] + public readonly float PoweredPryModifier = 9f; + + /// + /// Whether the maintenance panel should be visible even if the airlock is opened. + /// + [DataField("openPanelVisible")] + public bool OpenPanelVisible = false; + + /// + /// Whether the airlock should stay open if the airlock was clicked. + /// If the airlock was bumped into it will still auto close. + /// + [DataField("keepOpenIfClicked")] + public bool KeepOpenIfClicked = false; + + public bool BoltsDown; + + public bool BoltLightsEnabled = true; + + /// + /// True if the bolt wire is cut, which will force the airlock to always be bolted as long as it has power. + /// + [ViewVariables] + public bool BoltWireCut; + + /// + /// Whether the airlock should auto close. This value is reset every time the airlock closes. + /// + [ViewVariables(VVAccess.ReadWrite)] + public bool AutoClose = true; + + /// + /// Delay until an open door automatically closes. + /// + [DataField("autoCloseDelay")] + public TimeSpan AutoCloseDelay = TimeSpan.FromSeconds(5f); + + /// + /// Multiplicative modifier for the auto-close delay. Can be modified by hacking the airlock wires. Setting to + /// zero will disable auto-closing. + /// + [ViewVariables(VVAccess.ReadWrite)] + public float AutoCloseDelayModifier = 1.0f; +} + +[Serializable, NetSerializable] +public sealed class AirlockComponentState : ComponentState +{ + public readonly bool Safety; + + public AirlockComponentState(bool safety) + { + Safety = safety; + } +} diff --git a/Content.Shared/Doors/Components/SharedAirlockComponent.cs b/Content.Shared/Doors/Components/SharedAirlockComponent.cs deleted file mode 100644 index 00a98ba546..0000000000 --- a/Content.Shared/Doors/Components/SharedAirlockComponent.cs +++ /dev/null @@ -1,27 +0,0 @@ -using Robust.Shared.GameStates; -using Robust.Shared.Serialization; - -namespace Content.Shared.Doors.Components; - -[NetworkedComponent] -public abstract class SharedAirlockComponent : Component -{ - [ViewVariables(VVAccess.ReadWrite)] - [DataField("safety")] - public bool Safety = true; - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("emergencyAccess")] - public bool EmergencyAccess = false; -} - -[Serializable, NetSerializable] -public sealed class AirlockComponentState : ComponentState -{ - public readonly bool Safety; - - public AirlockComponentState(bool safety) - { - Safety = safety; - } -} diff --git a/Content.Shared/Doors/Systems/SharedAirlockSystem.cs b/Content.Shared/Doors/Systems/SharedAirlockSystem.cs index 3142c58fca..9a9de55d46 100644 --- a/Content.Shared/Doors/Systems/SharedAirlockSystem.cs +++ b/Content.Shared/Doors/Systems/SharedAirlockSystem.cs @@ -1,4 +1,5 @@ using Content.Shared.Doors.Components; +using Content.Shared.Popups; using Robust.Shared.GameStates; namespace Content.Shared.Doors.Systems; @@ -6,24 +7,26 @@ namespace Content.Shared.Doors.Systems; public abstract class SharedAirlockSystem : EntitySystem { [Dependency] protected readonly SharedAppearanceSystem Appearance = default!; + [Dependency] protected readonly SharedAudioSystem Audio = default!; [Dependency] protected readonly SharedDoorSystem DoorSystem = default!; + [Dependency] protected readonly SharedPopupSystem Popup = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnGetState); - SubscribeLocalEvent(OnHandleState); - SubscribeLocalEvent(OnBeforeDoorClosed); + SubscribeLocalEvent(OnGetState); + SubscribeLocalEvent(OnHandleState); + SubscribeLocalEvent(OnBeforeDoorClosed); } - private void OnGetState(EntityUid uid, SharedAirlockComponent airlock, ref ComponentGetState args) + private void OnGetState(EntityUid uid, AirlockComponent airlock, ref ComponentGetState args) { // Need to network airlock safety state to avoid mis-predicts when a door auto-closes as the client walks through the door. args.State = new AirlockComponentState(airlock.Safety); } - private void OnHandleState(EntityUid uid, SharedAirlockComponent airlock, ref ComponentHandleState args) + private void OnHandleState(EntityUid uid, AirlockComponent airlock, ref ComponentHandleState args) { if (args.Current is not AirlockComponentState state) return; @@ -31,21 +34,45 @@ public abstract class SharedAirlockSystem : EntitySystem airlock.Safety = state.Safety; } - protected virtual void OnBeforeDoorClosed(EntityUid uid, SharedAirlockComponent airlock, BeforeDoorClosedEvent args) + protected virtual void OnBeforeDoorClosed(EntityUid uid, AirlockComponent airlock, BeforeDoorClosedEvent args) { if (!airlock.Safety) args.PerformCollisionCheck = false; } - public void UpdateEmergencyLightStatus(SharedAirlockComponent component) + public void UpdateEmergencyLightStatus(EntityUid uid, AirlockComponent component) { - Appearance.SetData(component.Owner, DoorVisuals.EmergencyLights, component.EmergencyAccess); + Appearance.SetData(uid, DoorVisuals.EmergencyLights, component.EmergencyAccess); } - public void ToggleEmergencyAccess(SharedAirlockComponent component) + public void ToggleEmergencyAccess(EntityUid uid, AirlockComponent component) { component.EmergencyAccess = !component.EmergencyAccess; - UpdateEmergencyLightStatus(component); + UpdateEmergencyLightStatus(uid, component); + } + + public void SetAutoCloseDelayModifier(AirlockComponent component, float value) + { + if (component.AutoCloseDelayModifier.Equals(value)) + return; + + component.AutoCloseDelayModifier = value; + } + + public void SetSafety(AirlockComponent component, bool value) + { + if (component.Safety == value) + return; + + component.Safety = value; + } + + public void SetBoltWireCut(AirlockComponent component, bool value) + { + if (component.BoltWireCut == value) + return; + + component.BoltWireCut = value; } }