diff --git a/Content.Server/GameObjects/Components/Doors/AirlockComponent.cs b/Content.Server/GameObjects/Components/Doors/AirlockComponent.cs index 65f0892c85..855f127dc7 100644 --- a/Content.Server/GameObjects/Components/Doors/AirlockComponent.cs +++ b/Content.Server/GameObjects/Components/Doors/AirlockComponent.cs @@ -62,7 +62,6 @@ namespace Content.Server.GameObjects.Components.Doors set { _boltsDown = value; - UpdateWiresStatus(); UpdateBoltLightStatus(); } } @@ -75,11 +74,18 @@ namespace Content.Server.GameObjects.Components.Doors set { _boltLightsWirePulsed = value; - UpdateWiresStatus(); UpdateBoltLightStatus(); } } + private const float AutoCloseDelayFast = 1; + // True => AutoCloseDelay; False => AutoCloseDelayFast + private bool NormalCloseSpeed + { + get => CloseSpeed == AutoCloseDelay; + set => CloseSpeed = value ? AutoCloseDelay : AutoCloseDelayFast; + } + private void UpdateWiresStatus() { var powerLight = new StatusLightData(Color.Yellow, StatusLightState.On, "POWR"); @@ -98,12 +104,21 @@ namespace Content.Server.GameObjects.Components.Doors var boltLightsStatus = new StatusLightData(Color.Lime, _boltLightsWirePulsed ? StatusLightState.On : StatusLightState.Off, "BLTL"); + var timingStatus = + new StatusLightData(Color.Orange, !AutoClose ? StatusLightState.Off : + !NormalCloseSpeed ? StatusLightState.BlinkingSlow : + StatusLightState.On, + "TIME"); + + var safetyStatus = + new StatusLightData(Color.Red, Safety ? StatusLightState.On : StatusLightState.Off, "SAFE"); + _wires.SetStatus(AirlockWireStatus.PowerIndicator, powerLight); _wires.SetStatus(AirlockWireStatus.BoltIndicator, boltStatus); _wires.SetStatus(AirlockWireStatus.BoltLightIndicator, boltLightsStatus); - _wires.SetStatus(3, new StatusLightData(Color.Purple, StatusLightState.BlinkingSlow, "AICT")); - _wires.SetStatus(4, new StatusLightData(Color.Orange, StatusLightState.Off, "TIME")); - _wires.SetStatus(5, new StatusLightData(Color.Red, StatusLightState.Off, "SAFE")); + _wires.SetStatus(AirlockWireStatus.AIControlIndicator, new StatusLightData(Color.Purple, StatusLightState.BlinkingSlow, "AICT")); + _wires.SetStatus(AirlockWireStatus.TimingIndicator, timingStatus); + _wires.SetStatus(AirlockWireStatus.SafetyIndicator, safetyStatus); /* _wires.SetStatus(6, powerLight); _wires.SetStatus(7, powerLight); @@ -210,6 +225,23 @@ namespace Content.Server.GameObjects.Components.Doors /// Mending causes them to go on again /// BoltLight, + + // Placeholder for when AI is implemented + AIControl, + + /// + /// Pulsing causes door to close faster + /// Cutting disables door timer, causing door to stop closing automatically + /// Mending restores door timer + /// + Timing, + + /// + /// Pulsing toggles safety + /// Cutting disables safety + /// Mending enables safety + /// + Safety, } public void RegisterWires(WiresComponent.WiresBuilder builder) @@ -218,8 +250,8 @@ namespace Content.Server.GameObjects.Components.Doors builder.CreateWire(Wires.BackupPower); builder.CreateWire(Wires.Bolts); builder.CreateWire(Wires.BoltLight); - builder.CreateWire(4); - builder.CreateWire(5); + builder.CreateWire(Wires.Timing); + builder.CreateWire(Wires.Safety); /* builder.CreateWire(6); builder.CreateWire(7); @@ -263,6 +295,12 @@ namespace Content.Server.GameObjects.Components.Doors // we need to change the property here to set the appearance again BoltLightsVisible = !_boltLightsWirePulsed; break; + case Wires.Timing: + NormalCloseSpeed = !NormalCloseSpeed; + break; + case Wires.Safety: + Safety = !Safety; + break; } } @@ -279,6 +317,12 @@ namespace Content.Server.GameObjects.Components.Doors case Wires.BoltLight: BoltLightsVisible = true; break; + case Wires.Timing: + AutoClose = true; + break; + case Wires.Safety: + Safety = true; + break; } } @@ -292,6 +336,12 @@ namespace Content.Server.GameObjects.Components.Doors case Wires.BoltLight: BoltLightsVisible = false; break; + case Wires.Timing: + AutoClose = false; + break; + case Wires.Safety: + Safety = false; + break; } } diff --git a/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs b/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs index e922096914..23a2a80ee5 100644 --- a/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs +++ b/Content.Server/GameObjects/Components/Doors/ServerDoorComponent.cs @@ -1,5 +1,7 @@ using System; +using System.Linq; using Content.Server.GameObjects.Components.Access; +using Content.Server.GameObjects.Components.Mobs; using Content.Server.GameObjects.EntitySystems; using Content.Server.Interfaces.GameObjects.Components.Interaction; using Content.Server.Utility; @@ -31,17 +33,25 @@ namespace Content.Server.GameObjects set => _state = value; } - private float OpenTimeCounter; - + protected float OpenTimeCounter; + protected bool AutoClose = true; + protected const float AutoCloseDelay = 5; + protected float CloseSpeed = AutoCloseDelay; + private CollidableComponent collidableComponent; private AppearanceComponent _appearance; private CancellationTokenSource _cancellationTokenSource; - private static readonly TimeSpan CloseTime = TimeSpan.FromSeconds(1.2f); + private static readonly TimeSpan CloseTimeOne = TimeSpan.FromSeconds(0.3f); + private static readonly TimeSpan CloseTimeTwo = TimeSpan.FromSeconds(0.9f); private static readonly TimeSpan OpenTimeOne = TimeSpan.FromSeconds(0.3f); private static readonly TimeSpan OpenTimeTwo = TimeSpan.FromSeconds(0.9f); private static readonly TimeSpan DenyTime = TimeSpan.FromSeconds(0.45f); + private const int DoorCrushDamage = 15; + private const float DoorStunTime = 5f; + protected bool Safety = true; + [ViewVariables] private bool _occludes; @@ -192,27 +202,72 @@ namespace Content.Server.GameObjects Close(); } + private void CheckCrush() + { + // Check if collides with something + var collidesWith = collidableComponent.GetCollidingEntities(Vector2.Zero, false); + if (collidesWith.Count() != 0) + { + // Crush + bool hitSomeone = false; + foreach (var e in collidesWith) + { + if (!e.TryGetComponent(out StunnableComponent stun) + || !e.TryGetComponent(out DamageableComponent damage) + || !e.TryGetComponent(out ICollidableComponent otherBody) + || !Owner.TryGetComponent(out ICollidableComponent body)) + continue; + + var percentage = otherBody.WorldAABB.IntersectPercentage(body.WorldAABB); + + if (percentage < 0.1f) + continue; + + damage.TakeDamage(Shared.GameObjects.DamageType.Brute, DoorCrushDamage); + stun.Paralyze(DoorStunTime); + hitSomeone = true; + } + // If we hit someone, open up after stun (opens right when stun ends) + if (hitSomeone) + { + Timer.Spawn(TimeSpan.FromSeconds(DoorStunTime) - OpenTimeOne - OpenTimeTwo, () => Open()); + } + } + } + public bool Close() { + bool shouldCheckCrush = false; if (collidableComponent.IsColliding(Vector2.Zero, false)) { - // Do nothing, somebody's in the door. - return false; + if (Safety) + return false; + + // check if we crush someone while closing + shouldCheckCrush = true; } State = DoorState.Closing; - collidableComponent.Hard = true; OpenTimeCounter = 0; SetAppearance(DoorVisualState.Closing); - - Timer.Spawn(CloseTime, () => + if (_occludes && Owner.TryGetComponent(out OccluderComponent occluder)) { + occluder.Enabled = true; + } + + Timer.Spawn(CloseTimeOne, async () => + { + if (shouldCheckCrush) + { + CheckCrush(); + } + + collidableComponent.Hard = true; + + await Timer.Delay(CloseTimeTwo, _cancellationTokenSource.Token); + State = DoorState.Closed; SetAppearance(DoorVisualState.Closed); - if (_occludes && Owner.TryGetComponent(out OccluderComponent occluder)) - { - occluder.Enabled = true; - } }, _cancellationTokenSource.Token); Owner.EntityManager.EventBus.RaiseEvent(EventSource.Local, new AccessReaderChangeMessage(Owner.Uid, true)); return true; @@ -232,7 +287,6 @@ namespace Content.Server.GameObjects }, _cancellationTokenSource.Token); } - private const float AUTO_CLOSE_DELAY = 5; public virtual void OnUpdate(float frameTime) { if (State != DoorState.Open) @@ -240,8 +294,12 @@ namespace Content.Server.GameObjects return; } - OpenTimeCounter += frameTime; - if (OpenTimeCounter > AUTO_CLOSE_DELAY) + if (AutoClose) + { + OpenTimeCounter += frameTime; + } + + if (OpenTimeCounter > CloseSpeed) { if (!CanClose() || !Close()) { diff --git a/Content.Shared/GameObjects/Components/Doors/AirlockWireStatus.cs b/Content.Shared/GameObjects/Components/Doors/AirlockWireStatus.cs index b2833d6abf..bafe35c625 100644 --- a/Content.Shared/GameObjects/Components/Doors/AirlockWireStatus.cs +++ b/Content.Shared/GameObjects/Components/Doors/AirlockWireStatus.cs @@ -9,5 +9,8 @@ namespace Content.Shared.GameObjects.Components.Doors PowerIndicator, BoltIndicator, BoltLightIndicator, + AIControlIndicator, + TimingIndicator, + SafetyIndicator, } }