2020-08-22 22:29:20 +02:00
using System ;
2019-09-06 10:05:02 +02:00
using System.Threading ;
2022-01-30 13:49:56 +13:00
using Content.Server.Doors.Systems ;
2021-06-09 22:19:39 +02:00
using Content.Server.Power.Components ;
using Content.Server.VendingMachines ;
2021-07-04 18:11:52 +02:00
using Content.Server.WireHacking ;
2021-06-09 22:19:39 +02:00
using Content.Shared.Doors ;
2022-01-30 13:49:56 +13:00
using Content.Shared.Doors.Components ;
2021-07-10 17:35:33 +02:00
using Content.Shared.Sound ;
2021-03-21 09:12:03 -07:00
using Robust.Shared.Audio ;
2019-09-06 10:05:02 +02:00
using Robust.Shared.GameObjects ;
2021-12-03 11:11:52 +01:00
using Robust.Shared.IoC ;
2020-05-27 15:09:22 +02:00
using Robust.Shared.Maths ;
2021-03-21 09:12:03 -07:00
using Robust.Shared.Player ;
2021-07-10 17:35:33 +02:00
using Robust.Shared.Serialization.Manager.Attributes ;
2020-09-08 13:30:22 +02:00
using Robust.Shared.ViewVariables ;
2021-06-09 22:19:39 +02:00
using static Content . Shared . Wires . SharedWiresComponent ;
using static Content . Shared . Wires . SharedWiresComponent . WiresAction ;
2019-09-06 10:05:02 +02:00
2021-06-09 22:19:39 +02:00
namespace Content.Server.Doors.Components
2019-09-06 10:05:02 +02:00
{
2021-02-12 07:02:14 -08:00
/// <summary>
2022-01-30 13:49:56 +13:00
/// Companion component to DoorComponent that handles airlock-specific behavior -- wires, requiring power to operate, bolts, and allowing automatic closing.
2021-02-12 07:02:14 -08:00
/// </summary>
2019-09-06 10:05:02 +02:00
[RegisterComponent]
2022-01-30 13:49:56 +13:00
[ComponentReference(typeof(SharedAirlockComponent))]
public sealed class AirlockComponent : SharedAirlockComponent , IWires
2019-09-06 10:05:02 +02:00
{
2022-01-15 03:26:37 +01:00
[Dependency] private readonly IEntityManager _entityManager = default ! ;
2021-02-12 07:02:14 -08:00
2021-08-02 04:57:06 -07:00
/// <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" ) ;
2021-02-12 07:02:14 -08:00
2019-09-06 10:05:02 +02:00
/// <summary>
/// Duration for which power will be disabled after pulsing either power wire.
/// </summary>
2021-08-02 04:57:06 -07:00
[DataField("powerWiresTimeout")]
public float PowerWiresTimeout = 5.0f ;
2019-09-06 10:05:02 +02:00
2021-08-05 19:43:58 -07:00
/// <summary>
/// Whether the maintenance panel should be visible even if the airlock is opened.
/// </summary>
[DataField("openPanelVisible")]
public bool OpenPanelVisible = false ;
2019-09-06 10:05:02 +02:00
2020-11-27 11:00:49 +01:00
private CancellationTokenSource _powerWiresPulsedTimerCancel = new ( ) ;
2019-09-06 10:05:02 +02:00
private bool _powerWiresPulsed ;
2020-05-27 15:09:22 +02:00
2019-09-06 10:05:02 +02:00
/// <summary>
/// True if either power wire was pulsed in the last <see cref="PowerWiresTimeout"/>.
/// </summary>
2020-09-08 13:30:22 +02:00
[ViewVariables(VVAccess.ReadWrite)]
2019-09-06 10:05:02 +02:00
private bool PowerWiresPulsed
{
get = > _powerWiresPulsed ;
set
{
_powerWiresPulsed = value ;
UpdateWiresStatus ( ) ;
2019-10-13 18:29:57 +02:00
UpdatePowerCutStatus ( ) ;
2019-09-06 10:05:02 +02:00
}
}
2020-07-02 15:31:06 +02:00
private bool _boltsDown ;
2020-09-08 13:30:22 +02:00
[ViewVariables(VVAccess.ReadWrite)]
2020-10-08 11:59:20 -04:00
public bool BoltsDown
2020-07-02 15:31:06 +02:00
{
get = > _boltsDown ;
set
{
_boltsDown = value ;
UpdateBoltLightStatus ( ) ;
}
}
private bool _boltLightsWirePulsed = true ;
2020-09-08 13:30:22 +02:00
[ViewVariables(VVAccess.ReadWrite)]
2020-07-02 15:31:06 +02:00
private bool BoltLightsVisible
{
2021-02-12 07:02:14 -08:00
get = > _boltLightsWirePulsed & & BoltsDown & & IsPowered ( )
2022-01-30 13:49:56 +13:00
& & _entityManager . TryGetComponent < DoorComponent > ( Owner , out var doorComponent ) & & doorComponent . State = = DoorState . Closed ;
2020-07-02 15:31:06 +02:00
set
{
_boltLightsWirePulsed = value ;
UpdateBoltLightStatus ( ) ;
}
}
2022-01-30 13:49:56 +13:00
/// <summary>
/// Delay until an open door automatically closes.
/// </summary>
[DataField("autoCloseDelay")]
public TimeSpan AutoCloseDelay = TimeSpan . FromSeconds ( 5f ) ;
2021-02-12 07:02:14 -08:00
2022-01-30 13:49:56 +13:00
/// <summary>
/// Multiplicative modifier for the auto-close delay. Can be modified by hacking the airlock wires. Setting to
/// zero will disable auto-closing.
/// </summary>
2021-02-12 07:02:14 -08:00
[ViewVariables(VVAccess.ReadWrite)]
2021-08-02 04:57:06 -07:00
public float AutoCloseDelayModifier = 1.0f ;
2021-02-12 07:02:14 -08:00
2021-06-19 19:41:26 -07:00
protected override void Initialize ( )
2020-07-17 10:51:43 +02:00
{
2021-02-12 07:02:14 -08:00
base . Initialize ( ) ;
2022-01-15 03:26:37 +01:00
if ( _entityManager . TryGetComponent < ApcPowerReceiverComponent > ( Owner , out var receiverComponent ) & &
_entityManager . TryGetComponent < AppearanceComponent > ( Owner , out var appearanceComponent ) )
2021-02-12 07:02:14 -08:00
{
2022-01-15 03:26:37 +01:00
appearanceComponent . SetData ( DoorVisuals . Powered , receiverComponent . Powered ) ;
2021-02-12 07:02:14 -08:00
}
}
2021-08-02 04:57:06 -07:00
public bool CanChangeState ( )
2021-02-12 07:02:14 -08:00
{
return IsPowered ( ) & & ! IsBolted ( ) ;
}
2021-08-02 04:57:06 -07:00
public bool IsBolted ( )
2021-02-12 07:02:14 -08:00
{
return _boltsDown ;
}
2021-08-02 04:57:06 -07:00
public bool IsPowered ( )
2021-02-12 07:02:14 -08:00
{
2022-01-15 20:06:48 +13:00
return ! _entityManager . TryGetComponent < ApcPowerReceiverComponent > ( Owner , out var receiverComponent ) | | receiverComponent . Powered ;
2021-02-12 07:02:14 -08:00
}
2021-08-02 04:57:06 -07:00
public void UpdateBoltLightStatus ( )
2021-02-12 07:02:14 -08:00
{
2022-01-15 03:26:37 +01:00
if ( _entityManager . TryGetComponent < AppearanceComponent > ( Owner , out var appearanceComponent ) )
2021-02-12 07:02:14 -08:00
{
2022-01-15 03:26:37 +01:00
appearanceComponent . SetData ( DoorVisuals . BoltLights , BoltLightsVisible ) ;
2021-02-12 07:02:14 -08:00
}
2020-07-17 10:51:43 +02:00
}
2021-08-02 04:57:06 -07:00
public void UpdateWiresStatus ( )
2019-09-06 10:05:02 +02:00
{
2022-01-15 17:10:06 +13:00
if ( ! _entityManager . TryGetComponent < WiresComponent > ( Owner , out var wiresComponent ) ) return ;
2021-12-22 20:02:26 -08:00
2022-01-15 03:26:37 +01:00
var mainPowerCut = wiresComponent . IsWireCut ( Wires . MainPower ) ;
var backupPowerCut = wiresComponent . IsWireCut ( Wires . BackupPower ) ;
2021-12-22 20:02:26 -08:00
var statusLightState = PowerWiresPulsed ? StatusLightState . BlinkingFast : StatusLightState . On ;
StatusLightData powerLight ;
if ( mainPowerCut & & backupPowerCut )
2021-02-12 07:02:14 -08:00
{
2021-12-22 20:02:26 -08:00
powerLight = new StatusLightData ( Color . DarkGoldenrod , StatusLightState . Off , "POWER" ) ;
2021-02-12 07:02:14 -08:00
}
2021-12-22 20:02:26 -08:00
else if ( mainPowerCut ! = backupPowerCut )
2019-09-06 10:05:02 +02:00
{
2021-12-22 20:02:26 -08:00
powerLight = new StatusLightData ( Color . Gold , statusLightState , "POWER" ) ;
2020-05-27 15:09:22 +02:00
}
2021-12-22 20:02:26 -08:00
else
2019-09-06 10:05:02 +02:00
{
2021-12-22 20:02:26 -08:00
powerLight = new StatusLightData ( Color . Yellow , statusLightState , "POWER" ) ;
2019-09-06 10:05:02 +02:00
}
2020-05-27 15:09:22 +02:00
2020-07-02 15:31:06 +02:00
var boltStatus =
new StatusLightData ( Color . Red , BoltsDown ? StatusLightState . On : StatusLightState . Off , "BOLT" ) ;
var boltLightsStatus = new StatusLightData ( Color . Lime ,
2021-12-22 20:02:26 -08:00
_boltLightsWirePulsed ? StatusLightState . On : StatusLightState . Off , "BOLT LED" ) ;
2020-07-02 15:31:06 +02:00
2020-07-17 10:51:43 +02:00
var timingStatus =
2022-01-30 13:49:56 +13:00
new StatusLightData ( Color . Orange , ( AutoCloseDelayModifier < = 0 ) ? StatusLightState . Off :
! MathHelper . CloseToPercent ( AutoCloseDelayModifier , 1.0f ) ? StatusLightState . BlinkingSlow :
2020-07-17 10:51:43 +02:00
StatusLightState . On ,
"TIME" ) ;
var safetyStatus =
2021-12-22 20:02:26 -08:00
new StatusLightData ( Color . Red , Safety ? StatusLightState . On : StatusLightState . Off , "SAFETY" ) ;
2020-07-17 10:51:43 +02:00
2022-01-15 03:26:37 +01:00
wiresComponent . SetStatus ( AirlockWireStatus . PowerIndicator , powerLight ) ;
wiresComponent . SetStatus ( AirlockWireStatus . BoltIndicator , boltStatus ) ;
wiresComponent . SetStatus ( AirlockWireStatus . BoltLightIndicator , boltLightsStatus ) ;
wiresComponent . SetStatus ( AirlockWireStatus . AIControlIndicator , new StatusLightData ( Color . Purple , StatusLightState . BlinkingSlow , "AI CTRL" ) ) ;
wiresComponent . SetStatus ( AirlockWireStatus . TimingIndicator , timingStatus ) ;
wiresComponent . SetStatus ( AirlockWireStatus . SafetyIndicator , safetyStatus ) ;
2019-09-06 10:05:02 +02:00
}
2019-10-13 18:29:57 +02:00
private void UpdatePowerCutStatus ( )
{
2022-01-15 03:26:37 +01:00
if ( ! _entityManager . TryGetComponent < ApcPowerReceiverComponent > ( Owner , out var receiverComponent ) )
2020-08-22 22:29:20 +02:00
{
return ;
}
if ( PowerWiresPulsed )
{
2022-01-15 03:26:37 +01:00
receiverComponent . PowerDisabled = true ;
2020-08-22 22:29:20 +02:00
return ;
}
2022-01-15 03:26:37 +01:00
if ( ! _entityManager . TryGetComponent < WiresComponent > ( Owner , out var wiresComponent ) )
2020-08-22 22:29:20 +02:00
{
return ;
}
2022-01-15 03:26:37 +01:00
receiverComponent . PowerDisabled =
wiresComponent . IsWireCut ( Wires . MainPower ) & &
wiresComponent . IsWireCut ( Wires . BackupPower ) ;
2019-10-13 16:39:21 +02:00
}
2019-09-06 10:05:02 +02:00
private enum Wires
{
/// <summary>
/// Pulsing turns off power for <see cref="AirlockComponent.PowerWiresTimeout"/>.
/// Cutting turns off power permanently if <see cref="BackupPower"/> is also cut.
/// Mending restores power.
/// </summary>
MainPower ,
2020-05-27 15:09:22 +02:00
2019-09-06 10:05:02 +02:00
/// <see cref="MainPower"/>
BackupPower ,
2020-07-02 15:31:06 +02:00
/// <summary>
/// Pulsing causes for bolts to toggle (but only raise if power is on)
/// Cutting causes Bolts to drop
/// Mending does nothing
/// </summary>
Bolts ,
/// <summary>
/// Pulsing causes light to toggle
/// Cutting causes light to go out
/// Mending causes them to go on again
/// </summary>
BoltLight ,
2020-07-17 10:51:43 +02:00
// Placeholder for when AI is implemented
2022-01-30 13:49:56 +13:00
// aaaaany day now.
2020-07-17 10:51:43 +02:00
AIControl ,
/// <summary>
/// Pulsing causes door to close faster
/// Cutting disables door timer, causing door to stop closing automatically
/// Mending restores door timer
/// </summary>
Timing ,
/// <summary>
/// Pulsing toggles safety
/// Cutting disables safety
/// Mending enables safety
/// </summary>
Safety ,
2019-09-06 10:05:02 +02:00
}
public void RegisterWires ( WiresComponent . WiresBuilder builder )
{
builder . CreateWire ( Wires . MainPower ) ;
builder . CreateWire ( Wires . BackupPower ) ;
2020-07-02 15:31:06 +02:00
builder . CreateWire ( Wires . Bolts ) ;
builder . CreateWire ( Wires . BoltLight ) ;
2020-07-17 10:51:43 +02:00
builder . CreateWire ( Wires . Timing ) ;
builder . CreateWire ( Wires . Safety ) ;
2021-08-02 04:57:06 -07:00
2019-09-06 10:05:02 +02:00
UpdateWiresStatus ( ) ;
}
public void WiresUpdate ( WiresUpdateEventArgs args )
{
2022-01-30 13:49:56 +13:00
if ( ! _entityManager . TryGetComponent < DoorComponent > ( Owner , out var doorComponent ) )
2021-02-12 07:02:14 -08:00
{
return ;
}
2019-09-06 10:05:02 +02:00
if ( args . Action = = Pulse )
{
switch ( args . Identifier )
{
case Wires . MainPower :
case Wires . BackupPower :
PowerWiresPulsed = true ;
2020-08-22 22:29:20 +02:00
_powerWiresPulsedTimerCancel . Cancel ( ) ;
2020-09-03 21:54:10 +02:00
_powerWiresPulsedTimerCancel = new CancellationTokenSource ( ) ;
2021-08-02 04:57:06 -07:00
Owner . SpawnTimer ( TimeSpan . FromSeconds ( PowerWiresTimeout ) ,
2019-09-06 10:05:02 +02:00
( ) = > PowerWiresPulsed = false ,
_powerWiresPulsedTimerCancel . Token ) ;
break ;
2020-07-02 15:31:06 +02:00
case Wires . Bolts :
if ( ! BoltsDown )
{
SetBoltsWithAudio ( true ) ;
}
else
{
if ( IsPowered ( ) ) // only raise again if powered
{
SetBoltsWithAudio ( false ) ;
}
}
break ;
case Wires . BoltLight :
// we need to change the property here to set the appearance again
BoltLightsVisible = ! _boltLightsWirePulsed ;
break ;
2020-07-17 10:51:43 +02:00
case Wires . Timing :
2022-01-30 13:49:56 +13:00
// This is permanent, until the wire gets cut & mended.
2022-02-01 19:35:40 -08:00
AutoCloseDelayModifier = 0.5f ;
2022-01-30 13:49:56 +13:00
EntitySystem . Get < AirlockSystem > ( ) . UpdateAutoClose ( Owner , this ) ;
2020-07-17 10:51:43 +02:00
break ;
case Wires . Safety :
2021-08-02 04:57:06 -07:00
Safety = ! Safety ;
2022-01-30 13:49:56 +13:00
Dirty ( ) ;
2020-07-17 10:51:43 +02:00
break ;
2019-09-06 10:05:02 +02:00
}
}
2021-02-14 06:38:41 -08:00
else if ( args . Action = = Mend )
2019-09-06 10:05:02 +02:00
{
switch ( args . Identifier )
{
case Wires . MainPower :
case Wires . BackupPower :
// mending power wires instantly restores power
_powerWiresPulsedTimerCancel ? . Cancel ( ) ;
PowerWiresPulsed = false ;
break ;
2020-07-02 15:31:06 +02:00
case Wires . BoltLight :
BoltLightsVisible = true ;
break ;
2020-07-17 10:51:43 +02:00
case Wires . Timing :
2022-01-30 13:49:56 +13:00
AutoCloseDelayModifier = 1 ;
EntitySystem . Get < AirlockSystem > ( ) . UpdateAutoClose ( Owner , this ) ;
2020-07-17 10:51:43 +02:00
break ;
case Wires . Safety :
2021-08-02 04:57:06 -07:00
Safety = true ;
2022-01-30 13:49:56 +13:00
Dirty ( ) ;
2020-07-17 10:51:43 +02:00
break ;
2020-07-02 15:31:06 +02:00
}
}
2021-02-14 06:38:41 -08:00
else if ( args . Action = = Cut )
2020-07-02 15:31:06 +02:00
{
switch ( args . Identifier )
{
case Wires . Bolts :
SetBoltsWithAudio ( true ) ;
break ;
case Wires . BoltLight :
BoltLightsVisible = false ;
break ;
2020-07-17 10:51:43 +02:00
case Wires . Timing :
2022-01-30 13:49:56 +13:00
AutoCloseDelayModifier = 0 ; // disable auto close
EntitySystem . Get < AirlockSystem > ( ) . UpdateAutoClose ( Owner , this ) ;
2020-07-17 10:51:43 +02:00
break ;
case Wires . Safety :
2021-08-02 04:57:06 -07:00
Safety = false ;
2022-01-30 13:49:56 +13:00
Dirty ( ) ;
2020-07-17 10:51:43 +02:00
break ;
2019-09-06 10:05:02 +02:00
}
}
UpdateWiresStatus ( ) ;
2019-10-13 18:29:57 +02:00
UpdatePowerCutStatus ( ) ;
2019-09-06 10:05:02 +02:00
}
2020-07-02 15:31:06 +02:00
public void SetBoltsWithAudio ( bool newBolts )
{
if ( newBolts = = BoltsDown )
{
return ;
}
BoltsDown = newBolts ;
2021-08-02 04:57:06 -07:00
SoundSystem . Play ( Filter . Broadcast ( ) , newBolts ? BoltDownSound . GetSound ( ) : BoltUpSound . GetSound ( ) , Owner ) ;
2020-07-02 15:31:06 +02:00
}
2019-09-06 10:05:02 +02:00
}
}