2022-07-15 08:46:30 -04:00
using Content.Server.Atmos.Piping.Unary.EntitySystems ;
using Content.Shared.Atmos.Piping.Unary.Components ;
using Content.Shared.Atmos.Visuals ;
using Content.Shared.Examine ;
using Content.Shared.Destructible ;
using Content.Server.Atmos.Piping.Components ;
using Content.Server.Atmos.EntitySystems ;
using Content.Server.Power.Components ;
using Content.Server.NodeContainer ;
using Robust.Server.GameObjects ;
using Content.Server.NodeContainer.Nodes ;
using Content.Server.NodeContainer.NodeGroups ;
using Content.Server.Audio ;
using Content.Server.Administration.Logs ;
2023-06-28 14:28:38 +03:00
using Content.Server.NodeContainer.EntitySystems ;
2022-07-15 08:46:30 -04:00
using Content.Shared.Database ;
namespace Content.Server.Atmos.Portable
{
public sealed class PortableScrubberSystem : EntitySystem
{
[Dependency] private readonly GasVentScrubberSystem _scrubberSystem = default ! ;
[Dependency] private readonly GasCanisterSystem _canisterSystem = default ! ;
[Dependency] private readonly GasPortableSystem _gasPortableSystem = default ! ;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default ! ;
[Dependency] private readonly TransformSystem _transformSystem = default ! ;
[Dependency] private readonly IAdminLogManager _adminLogger = default ! ;
[Dependency] private readonly AmbientSoundSystem _ambientSound = default ! ;
2022-10-22 18:49:30 -04:00
[Dependency] private readonly SharedAppearanceSystem _appearance = default ! ;
2023-06-28 14:28:38 +03:00
[Dependency] private readonly NodeContainerSystem _nodeContainer = default ! ;
2022-07-15 08:46:30 -04:00
public override void Initialize ( )
{
base . Initialize ( ) ;
SubscribeLocalEvent < PortableScrubberComponent , AtmosDeviceUpdateEvent > ( OnDeviceUpdated ) ;
SubscribeLocalEvent < PortableScrubberComponent , AnchorStateChangedEvent > ( OnAnchorChanged ) ;
SubscribeLocalEvent < PortableScrubberComponent , PowerChangedEvent > ( OnPowerChanged ) ;
SubscribeLocalEvent < PortableScrubberComponent , ExaminedEvent > ( OnExamined ) ;
SubscribeLocalEvent < PortableScrubberComponent , DestructionEventArgs > ( OnDestroyed ) ;
2022-09-08 14:22:14 +00:00
SubscribeLocalEvent < PortableScrubberComponent , GasAnalyzerScanEvent > ( OnScrubberAnalyzed ) ;
2022-10-22 18:49:30 -04:00
}
private bool IsFull ( PortableScrubberComponent component )
{
return component . Air . Pressure > = component . MaxPressure ;
2022-07-15 08:46:30 -04:00
}
2023-12-21 18:48:18 -07:00
private void OnDeviceUpdated ( EntityUid uid , PortableScrubberComponent component , ref AtmosDeviceUpdateEvent args )
2022-07-15 08:46:30 -04:00
{
if ( ! TryComp ( uid , out AtmosDeviceComponent ? device ) )
return ;
2023-08-12 23:42:12 -07:00
var timeDelta = args . dt ;
2022-07-15 08:46:30 -04:00
if ( ! component . Enabled )
return ;
2022-10-22 18:49:30 -04:00
// If we are on top of a connector port, empty into it.
2022-07-15 08:46:30 -04:00
if ( TryComp < NodeContainerComponent > ( uid , out var nodeContainer )
2023-06-28 14:28:38 +03:00
& & _nodeContainer . TryGetNode ( nodeContainer , component . PortName , out PortablePipeNode ? portableNode )
2022-07-15 08:46:30 -04:00
& & portableNode . ConnectionsEnabled )
{
_atmosphereSystem . React ( component . Air , portableNode ) ;
if ( portableNode . NodeGroup is PipeNet { NodeCount : > 1 } net )
_canisterSystem . MixContainerWithPipeNet ( component . Air , net . Air ) ;
}
2022-10-22 18:49:30 -04:00
if ( IsFull ( component ) )
2022-07-15 08:46:30 -04:00
{
UpdateAppearance ( uid , true , false ) ;
return ;
}
var xform = Transform ( uid ) ;
if ( xform . GridUid = = null )
return ;
2023-11-28 18:03:44 -05:00
var position = _transformSystem . GetGridTilePositionOrDefault ( ( uid , xform ) ) ;
2022-07-15 08:46:30 -04:00
var environment = _atmosphereSystem . GetTileMixture ( xform . GridUid , xform . MapUid , position , true ) ;
var running = Scrub ( timeDelta , component , environment ) ;
UpdateAppearance ( uid , false , running ) ;
2022-10-22 18:49:30 -04:00
// We scrub once to see if we can and set the animation
2022-07-15 08:46:30 -04:00
if ( ! running )
return ;
2022-10-22 18:49:30 -04:00
// widenet
2022-07-15 08:46:30 -04:00
foreach ( var adjacent in _atmosphereSystem . GetAdjacentTileMixtures ( xform . GridUid . Value , position , false , true ) )
{
2022-10-22 18:49:30 -04:00
Scrub ( timeDelta , component , adjacent ) ;
2022-07-15 08:46:30 -04:00
}
}
/// <summary>
/// If there is a port under us, let us connect with adjacent atmos pipes.
/// </summary>
private void OnAnchorChanged ( EntityUid uid , PortableScrubberComponent component , ref AnchorStateChangedEvent args )
{
if ( ! TryComp ( uid , out NodeContainerComponent ? nodeContainer ) )
return ;
2023-06-28 14:28:38 +03:00
if ( ! _nodeContainer . TryGetNode ( nodeContainer , component . PortName , out PipeNode ? portableNode ) )
2022-07-15 08:46:30 -04:00
return ;
portableNode . ConnectionsEnabled = ( args . Anchored & & _gasPortableSystem . FindGasPortIn ( Transform ( uid ) . GridUid , Transform ( uid ) . Coordinates , out _ ) ) ;
2022-10-22 18:49:30 -04:00
_appearance . SetData ( uid , PortableScrubberVisuals . IsDraining , portableNode . ConnectionsEnabled ) ;
2022-07-15 08:46:30 -04:00
}
2022-10-22 18:49:30 -04:00
2022-10-15 15:08:15 +11:00
private void OnPowerChanged ( EntityUid uid , PortableScrubberComponent component , ref PowerChangedEvent args )
2022-07-15 08:46:30 -04:00
{
2022-10-22 18:49:30 -04:00
UpdateAppearance ( uid , IsFull ( component ) , args . Powered ) ;
2022-07-15 08:46:30 -04:00
component . Enabled = args . Powered ;
}
/// <summary>
/// Examining tells you how full it is as a %.
/// </summary>
private void OnExamined ( EntityUid uid , PortableScrubberComponent component , ExaminedEvent args )
{
if ( args . IsInDetailsRange )
{
var percentage = Math . Round ( ( ( component . Air . Pressure ) / component . MaxPressure ) * 100 ) ;
args . PushMarkup ( Loc . GetString ( "portable-scrubber-fill-level" , ( "percent" , percentage ) ) ) ;
}
}
/// <summary>
/// When this is destroyed, we dump out all the gas inside.
/// </summary>
private void OnDestroyed ( EntityUid uid , PortableScrubberComponent component , DestructionEventArgs args )
{
var environment = _atmosphereSystem . GetContainingMixture ( uid , false , true ) ;
if ( environment ! = null )
_atmosphereSystem . Merge ( environment , component . Air ) ;
2022-10-22 18:49:30 -04:00
_adminLogger . Add ( LogType . CanisterPurged , LogImpact . Medium , $"Portable scrubber {ToPrettyString(uid):canister} purged its contents of {component.Air} into the environment." ) ;
2022-07-15 08:46:30 -04:00
component . Air . Clear ( ) ;
}
private bool Scrub ( float timeDelta , PortableScrubberComponent scrubber , GasMixture ? tile )
{
2023-12-11 15:59:47 -08:00
return _scrubberSystem . Scrub ( timeDelta , scrubber . TransferRate * _atmosphereSystem . PumpSpeedup ( ) , ScrubberPumpDirection . Scrubbing , scrubber . FilterGases , tile , scrubber . Air ) ;
2022-07-15 08:46:30 -04:00
}
private void UpdateAppearance ( EntityUid uid , bool isFull , bool isRunning )
{
_ambientSound . SetAmbience ( uid , isRunning ) ;
2022-10-22 18:49:30 -04:00
_appearance . SetData ( uid , PortableScrubberVisuals . IsFull , isFull ) ;
_appearance . SetData ( uid , PortableScrubberVisuals . IsRunning , isRunning ) ;
2022-07-15 08:46:30 -04:00
}
2022-09-08 14:22:14 +00:00
/// <summary>
/// Returns the gas mixture for the gas analyzer
/// </summary>
private void OnScrubberAnalyzed ( EntityUid uid , PortableScrubberComponent component , GasAnalyzerScanEvent args )
{
2022-09-24 19:46:27 +02:00
var gasMixDict = new Dictionary < string , GasMixture ? > { { Name ( uid ) , component . Air } } ;
2022-09-08 14:22:14 +00:00
// If it's connected to a port, include the port side
2022-09-24 19:46:27 +02:00
if ( TryComp ( uid , out NodeContainerComponent ? nodeContainer ) )
2022-09-08 14:22:14 +00:00
{
2023-06-28 14:28:38 +03:00
if ( _nodeContainer . TryGetNode ( nodeContainer , component . PortName , out PipeNode ? port ) )
2022-09-08 14:22:14 +00:00
gasMixDict . Add ( component . PortName , port . Air ) ;
}
args . GasMixtures = gasMixDict ;
}
2022-07-15 08:46:30 -04:00
}
}