2021-12-10 12:23:18 -06:00
using Content.Server.Administration.Logs ;
2022-02-05 23:57:26 +13:00
using Content.Server.Atmos.EntitySystems ;
2021-06-19 13:25:05 +02:00
using Content.Server.Atmos.Piping.Components ;
using Content.Server.Atmos.Piping.Trinary.Components ;
using Content.Server.NodeContainer ;
2023-06-28 14:28:38 +03:00
using Content.Server.NodeContainer.EntitySystems ;
2021-07-04 18:11:52 +02:00
using Content.Server.NodeContainer.Nodes ;
2021-06-19 13:25:05 +02:00
using Content.Shared.Atmos ;
2022-02-08 09:42:07 +13:00
using Content.Shared.Atmos.Piping ;
2021-11-11 16:10:21 -06:00
using Content.Shared.Atmos.Piping.Trinary.Components ;
2022-03-12 16:20:31 -06:00
using Content.Shared.Audio ;
2021-12-10 12:23:18 -06:00
using Content.Shared.Database ;
2021-11-11 16:10:21 -06:00
using Content.Shared.Interaction ;
using Content.Shared.Popups ;
2021-06-19 13:25:05 +02:00
using JetBrains.Annotations ;
2021-11-11 16:10:21 -06:00
using Robust.Server.GameObjects ;
2023-10-29 04:21:02 +11:00
using Robust.Shared.Player ;
2021-06-19 13:25:05 +02:00
namespace Content.Server.Atmos.Piping.Trinary.EntitySystems
{
[UsedImplicitly]
2022-02-08 09:42:07 +13:00
public sealed class GasMixerSystem : EntitySystem
2021-06-19 13:25:05 +02:00
{
2021-11-11 16:10:21 -06:00
[Dependency] private UserInterfaceSystem _userInterfaceSystem = default ! ;
2022-05-28 23:41:17 -07:00
[Dependency] private IAdminLogManager _adminLogger = default ! ;
2022-02-05 23:57:26 +13:00
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default ! ;
2022-03-12 16:20:31 -06:00
[Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default ! ;
2023-02-02 17:34:53 +01:00
[Dependency] private readonly SharedAppearanceSystem _appearance = default ! ;
2023-06-28 14:28:38 +03:00
[Dependency] private readonly NodeContainerSystem _nodeContainer = default ! ;
2023-10-15 22:56:09 -07:00
[Dependency] private readonly SharedPopupSystem _popup = default ! ;
2021-12-10 12:23:18 -06:00
2021-06-19 13:25:05 +02:00
public override void Initialize ( )
{
base . Initialize ( ) ;
2022-02-08 09:42:07 +13:00
SubscribeLocalEvent < GasMixerComponent , ComponentInit > ( OnInit ) ;
2021-06-19 13:25:05 +02:00
SubscribeLocalEvent < GasMixerComponent , AtmosDeviceUpdateEvent > ( OnMixerUpdated ) ;
2024-01-03 01:31:58 -05:00
SubscribeLocalEvent < GasMixerComponent , ActivateInWorldEvent > ( OnMixerActivate ) ;
2022-09-08 14:22:14 +00:00
SubscribeLocalEvent < GasMixerComponent , GasAnalyzerScanEvent > ( OnMixerAnalyzed ) ;
2021-11-11 16:10:21 -06:00
// Bound UI subscriptions
SubscribeLocalEvent < GasMixerComponent , GasMixerChangeOutputPressureMessage > ( OnOutputPressureChangeMessage ) ;
SubscribeLocalEvent < GasMixerComponent , GasMixerChangeNodePercentageMessage > ( OnChangeNodePercentageMessage ) ;
SubscribeLocalEvent < GasMixerComponent , GasMixerToggleStatusMessage > ( OnToggleStatusMessage ) ;
2022-02-10 16:28:11 +13:00
2022-06-23 14:18:11 +02:00
SubscribeLocalEvent < GasMixerComponent , AtmosDeviceDisabledEvent > ( OnMixerLeaveAtmosphere ) ;
2022-02-10 16:28:11 +13:00
}
2022-06-23 14:18:11 +02:00
private void OnInit ( EntityUid uid , GasMixerComponent mixer , ComponentInit args )
2022-02-10 16:28:11 +13:00
{
2022-06-23 14:18:11 +02:00
UpdateAppearance ( uid , mixer ) ;
2022-02-08 09:42:07 +13:00
}
2023-12-21 18:48:18 -07:00
private void OnMixerUpdated ( EntityUid uid , GasMixerComponent mixer , ref AtmosDeviceUpdateEvent args )
2021-06-19 13:25:05 +02:00
{
// TODO ATMOS: Cache total moles since it's expensive.
2024-03-30 17:17:53 +13:00
if ( ! mixer . Enabled
| | ! _nodeContainer . TryGetNodes ( uid , mixer . InletOneName , mixer . InletTwoName , mixer . OutletName , out PipeNode ? inletOne , out PipeNode ? inletTwo , out PipeNode ? outlet ) )
2022-03-12 16:20:31 -06:00
{
2023-10-15 22:56:09 -07:00
_ambientSoundSystem . SetAmbience ( uid , false ) ;
2021-06-19 13:25:05 +02:00
return ;
2022-03-12 16:20:31 -06:00
}
2021-06-19 13:25:05 +02:00
var outputStartingPressure = outlet . Air . Pressure ;
if ( outputStartingPressure > = mixer . TargetPressure )
return ; // Target reached, no need to mix.
var generalTransfer = ( mixer . TargetPressure - outputStartingPressure ) * outlet . Air . Volume / Atmospherics . R ;
var transferMolesOne = inletOne . Air . Temperature > 0 ? mixer . InletOneConcentration * generalTransfer / inletOne . Air . Temperature : 0f ;
var transferMolesTwo = inletTwo . Air . Temperature > 0 ? mixer . InletTwoConcentration * generalTransfer / inletTwo . Air . Temperature : 0f ;
if ( mixer . InletTwoConcentration < = 0f )
{
if ( inletOne . Air . Temperature < = 0f )
return ;
transferMolesOne = MathF . Min ( transferMolesOne , inletOne . Air . TotalMoles ) ;
transferMolesTwo = 0f ;
}
else if ( mixer . InletOneConcentration < = 0 )
{
if ( inletTwo . Air . Temperature < = 0f )
return ;
transferMolesOne = 0f ;
transferMolesTwo = MathF . Min ( transferMolesTwo , inletTwo . Air . TotalMoles ) ;
}
else
{
if ( inletOne . Air . Temperature < = 0f | | inletTwo . Air . Temperature < = 0f )
return ;
if ( transferMolesOne < = 0 | | transferMolesTwo < = 0 )
2022-03-12 16:20:31 -06:00
{
2023-10-15 22:56:09 -07:00
_ambientSoundSystem . SetAmbience ( uid , false ) ;
2021-06-19 13:25:05 +02:00
return ;
2022-03-12 16:20:31 -06:00
}
2021-06-19 13:25:05 +02:00
if ( inletOne . Air . TotalMoles < transferMolesOne | | inletTwo . Air . TotalMoles < transferMolesTwo )
{
var ratio = MathF . Min ( inletOne . Air . TotalMoles / transferMolesOne , inletTwo . Air . TotalMoles / transferMolesTwo ) ;
transferMolesOne * = ratio ;
transferMolesTwo * = ratio ;
}
}
// Actually transfer the gas now.
2022-06-23 14:18:11 +02:00
var transferred = false ;
2021-06-19 13:25:05 +02:00
if ( transferMolesOne > 0f )
{
2022-06-23 14:18:11 +02:00
transferred = true ;
2021-06-19 13:25:05 +02:00
var removed = inletOne . Air . Remove ( transferMolesOne ) ;
2022-02-05 23:57:26 +13:00
_atmosphereSystem . Merge ( outlet . Air , removed ) ;
2021-06-19 13:25:05 +02:00
}
if ( transferMolesTwo > 0f )
{
2022-06-23 14:18:11 +02:00
transferred = true ;
2021-06-19 13:25:05 +02:00
var removed = inletTwo . Air . Remove ( transferMolesTwo ) ;
2022-02-05 23:57:26 +13:00
_atmosphereSystem . Merge ( outlet . Air , removed ) ;
2021-06-19 13:25:05 +02:00
}
2022-06-23 14:18:11 +02:00
if ( transferred )
2023-10-15 22:56:09 -07:00
_ambientSoundSystem . SetAmbience ( uid , true ) ;
2022-06-23 14:18:11 +02:00
}
2023-12-21 18:48:18 -07:00
private void OnMixerLeaveAtmosphere ( EntityUid uid , GasMixerComponent mixer , ref AtmosDeviceDisabledEvent args )
2022-06-23 14:18:11 +02:00
{
mixer . Enabled = false ;
DirtyUI ( uid , mixer ) ;
UpdateAppearance ( uid , mixer ) ;
2024-04-26 18:16:24 +10:00
_userInterfaceSystem . CloseUi ( uid , GasFilterUiKey . Key ) ;
2021-06-19 13:25:05 +02:00
}
2021-11-11 16:10:21 -06:00
2024-01-03 01:31:58 -05:00
private void OnMixerActivate ( EntityUid uid , GasMixerComponent mixer , ActivateInWorldEvent args )
2021-11-11 16:10:21 -06:00
{
2024-05-31 16:26:19 -04:00
if ( args . Handled | | ! args . Complex )
return ;
2021-12-08 13:00:43 +01:00
if ( ! EntityManager . TryGetComponent ( args . User , out ActorComponent ? actor ) )
2021-11-11 16:10:21 -06:00
return ;
2023-10-15 22:56:09 -07:00
if ( Transform ( uid ) . Anchored )
2021-11-11 16:10:21 -06:00
{
2024-04-26 18:16:24 +10:00
_userInterfaceSystem . OpenUi ( uid , GasMixerUiKey . Key , actor . PlayerSession ) ;
2022-06-23 14:18:11 +02:00
DirtyUI ( uid , mixer ) ;
2021-11-11 16:10:21 -06:00
}
else
{
2023-10-15 22:56:09 -07:00
_popup . PopupCursor ( Loc . GetString ( "comp-gas-mixer-ui-needs-anchor" ) , args . User ) ;
2021-11-11 16:10:21 -06:00
}
args . Handled = true ;
}
private void DirtyUI ( EntityUid uid , GasMixerComponent ? mixer )
{
if ( ! Resolve ( uid , ref mixer ) )
return ;
2024-04-26 18:16:24 +10:00
_userInterfaceSystem . SetUiState ( uid , GasMixerUiKey . Key ,
2023-10-15 22:56:09 -07:00
new GasMixerBoundUserInterfaceState ( EntityManager . GetComponent < MetaDataComponent > ( uid ) . EntityName , mixer . TargetPressure , mixer . Enabled , mixer . InletOneConcentration ) ) ;
2021-11-11 16:10:21 -06:00
}
2022-02-08 09:42:07 +13:00
private void UpdateAppearance ( EntityUid uid , GasMixerComponent ? mixer = null , AppearanceComponent ? appearance = null )
{
if ( ! Resolve ( uid , ref mixer , ref appearance , false ) )
return ;
2023-02-02 17:34:53 +01:00
_appearance . SetData ( uid , FilterVisuals . Enabled , mixer . Enabled , appearance ) ;
2022-02-08 09:42:07 +13:00
}
2021-11-11 16:10:21 -06:00
private void OnToggleStatusMessage ( EntityUid uid , GasMixerComponent mixer , GasMixerToggleStatusMessage args )
{
mixer . Enabled = args . Enabled ;
2022-05-28 23:41:17 -07:00
_adminLogger . Add ( LogType . AtmosPowerChanged , LogImpact . Medium ,
2024-04-26 18:16:24 +10:00
$"{ToPrettyString(args.Actor):player} set the power on {ToPrettyString(uid):device} to {args.Enabled}" ) ;
2021-11-11 16:10:21 -06:00
DirtyUI ( uid , mixer ) ;
2022-02-08 09:42:07 +13:00
UpdateAppearance ( uid , mixer ) ;
2021-11-11 16:10:21 -06:00
}
private void OnOutputPressureChangeMessage ( EntityUid uid , GasMixerComponent mixer , GasMixerChangeOutputPressureMessage args )
{
2022-03-01 03:39:30 +13:00
mixer . TargetPressure = Math . Clamp ( args . Pressure , 0f , mixer . MaxTargetPressure ) ;
2022-05-28 23:41:17 -07:00
_adminLogger . Add ( LogType . AtmosPressureChanged , LogImpact . Medium ,
2024-04-26 18:16:24 +10:00
$"{ToPrettyString(args.Actor):player} set the pressure on {ToPrettyString(uid):device} to {args.Pressure}kPa" ) ;
2021-11-11 16:10:21 -06:00
DirtyUI ( uid , mixer ) ;
}
private void OnChangeNodePercentageMessage ( EntityUid uid , GasMixerComponent mixer ,
GasMixerChangeNodePercentageMessage args )
{
float nodeOne = Math . Clamp ( args . NodeOne , 0f , 100.0f ) / 100.0f ;
mixer . InletOneConcentration = nodeOne ;
mixer . InletTwoConcentration = 1.0f - mixer . InletOneConcentration ;
2022-05-28 23:41:17 -07:00
_adminLogger . Add ( LogType . AtmosRatioChanged , LogImpact . Medium ,
2024-04-26 18:16:24 +10:00
$"{EntityManager.ToPrettyString(args.Actor):player} set the ratio on {EntityManager.ToPrettyString(uid):device} to {mixer.InletOneConcentration}:{mixer.InletTwoConcentration}" ) ;
2021-11-11 16:10:21 -06:00
DirtyUI ( uid , mixer ) ;
}
2022-09-08 14:22:14 +00:00
/// <summary>
/// Returns the gas mixture for the gas analyzer
/// </summary>
private void OnMixerAnalyzed ( EntityUid uid , GasMixerComponent component , GasAnalyzerScanEvent args )
{
2024-04-17 19:42:24 +02:00
args . GasMixtures ? ? = new List < ( string , GasMixture ? ) > ( ) ;
2022-09-08 14:22:14 +00:00
2024-04-17 19:42:24 +02:00
// multiply by volume fraction to make sure to send only the gas inside the analyzed pipe element, not the whole pipe system
if ( _nodeContainer . TryGetNode ( uid , component . InletOneName , out PipeNode ? inletOne ) & & inletOne . Air . Volume ! = 0f )
{
var inletOneAirLocal = inletOne . Air . Clone ( ) ;
inletOneAirLocal . Multiply ( inletOne . Volume / inletOne . Air . Volume ) ;
inletOneAirLocal . Volume = inletOne . Volume ;
args . GasMixtures . Add ( ( $"{inletOne.CurrentPipeDirection} {Loc.GetString(" gas - analyzer - window - text - inlet ")}" , inletOneAirLocal ) ) ;
}
if ( _nodeContainer . TryGetNode ( uid , component . InletTwoName , out PipeNode ? inletTwo ) & & inletTwo . Air . Volume ! = 0f )
{
var inletTwoAirLocal = inletTwo . Air . Clone ( ) ;
inletTwoAirLocal . Multiply ( inletTwo . Volume / inletTwo . Air . Volume ) ;
inletTwoAirLocal . Volume = inletTwo . Volume ;
args . GasMixtures . Add ( ( $"{inletTwo.CurrentPipeDirection} {Loc.GetString(" gas - analyzer - window - text - inlet ")}" , inletTwoAirLocal ) ) ;
}
if ( _nodeContainer . TryGetNode ( uid , component . OutletName , out PipeNode ? outlet ) & & outlet . Air . Volume ! = 0f )
{
var outletAirLocal = outlet . Air . Clone ( ) ;
outletAirLocal . Multiply ( outlet . Volume / outlet . Air . Volume ) ;
outletAirLocal . Volume = outlet . Volume ;
args . GasMixtures . Add ( ( Loc . GetString ( "gas-analyzer-window-text-outlet" ) , outletAirLocal ) ) ;
}
2022-09-08 14:22:14 +00:00
args . DeviceFlipped = inletOne ! = null & & inletTwo ! = null & & inletOne . CurrentPipeDirection . ToDirection ( ) = = inletTwo . CurrentPipeDirection . ToDirection ( ) . GetClockwise90Degrees ( ) ;
}
2021-06-19 13:25:05 +02:00
}
}