2023-05-28 03:59:27 -05:00
using Content.Server.Administration.Logs ;
2022-12-25 12:35:51 +01:00
using Content.Server.Atmos ;
using Content.Server.Atmos.EntitySystems ;
using Content.Server.Atmos.Piping.Components ;
using Content.Server.Atmos.Piping.Unary.EntitySystems ;
using Content.Server.Body.Components ;
using Content.Server.Body.Systems ;
2023-12-29 04:47:43 -08:00
using Content.Server.Chemistry.Containers.EntitySystems ;
2022-12-25 12:35:51 +01:00
using Content.Server.Medical.Components ;
using Content.Server.NodeContainer ;
2023-06-28 14:28:38 +03:00
using Content.Server.NodeContainer.EntitySystems ;
2022-12-25 12:35:51 +01:00
using Content.Server.NodeContainer.NodeGroups ;
using Content.Server.NodeContainer.Nodes ;
using Content.Server.Power.Components ;
2023-10-19 12:34:31 -07:00
using Content.Server.Temperature.Components ;
2024-02-01 11:45:24 +03:00
using Content.Shared.UserInterface ;
2022-12-25 12:35:51 +01:00
using Content.Shared.Chemistry ;
using Content.Shared.Chemistry.Components ;
2023-10-14 09:45:28 -07:00
using Content.Shared.Chemistry.Components.SolutionManager ;
2022-12-25 12:35:51 +01:00
using Content.Shared.Chemistry.Reagent ;
2023-10-19 12:34:31 -07:00
using Content.Shared.Climbing.Systems ;
2022-12-25 12:35:51 +01:00
using Content.Shared.Containers.ItemSlots ;
2023-05-28 03:59:27 -05:00
using Content.Shared.Database ;
2023-02-24 19:01:25 -05:00
using Content.Shared.DoAfter ;
2022-12-25 12:35:51 +01:00
using Content.Shared.DragDrop ;
using Content.Shared.Emag.Systems ;
using Content.Shared.Examine ;
using Content.Shared.Interaction ;
using Content.Shared.Medical.Cryogenics ;
using Content.Shared.MedicalScanner ;
using Content.Shared.Verbs ;
using Robust.Server.GameObjects ;
2024-02-21 17:47:23 +03:00
using Robust.Shared.Containers ;
2022-12-25 12:35:51 +01:00
using Robust.Shared.Timing ;
2023-10-24 00:20:33 +11:00
using SharedToolSystem = Content . Shared . Tools . Systems . SharedToolSystem ;
2022-12-25 12:35:51 +01:00
namespace Content.Server.Medical ;
2023-12-29 04:47:43 -08:00
public sealed partial class CryoPodSystem : SharedCryoPodSystem
2022-12-25 12:35:51 +01:00
{
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default ! ;
[Dependency] private readonly GasCanisterSystem _gasCanisterSystem = default ! ;
[Dependency] private readonly ClimbSystem _climbSystem = default ! ;
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default ! ;
[Dependency] private readonly SolutionContainerSystem _solutionContainerSystem = default ! ;
[Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default ! ;
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default ! ;
2023-04-03 13:13:48 +12:00
[Dependency] private readonly SharedDoAfterSystem _doAfterSystem = default ! ;
2022-12-25 12:35:51 +01:00
[Dependency] private readonly UserInterfaceSystem _uiSystem = default ! ;
2023-02-24 19:01:25 -05:00
[Dependency] private readonly SharedToolSystem _toolSystem = default ! ;
2022-12-25 12:35:51 +01:00
[Dependency] private readonly IGameTiming _gameTiming = default ! ;
[Dependency] private readonly MetaDataSystem _metaDataSystem = default ! ;
[Dependency] private readonly ReactiveSystem _reactiveSystem = default ! ;
2023-05-28 03:59:27 -05:00
[Dependency] private readonly IAdminLogManager _adminLogger = default ! ;
2023-06-28 14:28:38 +03:00
[Dependency] private readonly NodeContainerSystem _nodeContainer = default ! ;
2022-12-25 12:35:51 +01:00
public override void Initialize ( )
{
base . Initialize ( ) ;
SubscribeLocalEvent < CryoPodComponent , ComponentInit > ( OnComponentInit ) ;
SubscribeLocalEvent < CryoPodComponent , GetVerbsEvent < AlternativeVerb > > ( AddAlternativeVerbs ) ;
SubscribeLocalEvent < CryoPodComponent , GotEmaggedEvent > ( OnEmagged ) ;
2023-04-03 13:13:48 +12:00
SubscribeLocalEvent < CryoPodComponent , CryoPodDragFinished > ( OnDragFinished ) ;
2022-12-25 12:35:51 +01:00
SubscribeLocalEvent < CryoPodComponent , CryoPodPryFinished > ( OnCryoPodPryFinished ) ;
SubscribeLocalEvent < CryoPodComponent , AtmosDeviceUpdateEvent > ( OnCryoPodUpdateAtmosphere ) ;
2023-02-14 00:29:34 +11:00
SubscribeLocalEvent < CryoPodComponent , DragDropTargetEvent > ( HandleDragDropOn ) ;
2022-12-25 12:35:51 +01:00
SubscribeLocalEvent < CryoPodComponent , InteractUsingEvent > ( OnInteractUsing ) ;
SubscribeLocalEvent < CryoPodComponent , ExaminedEvent > ( OnExamined ) ;
SubscribeLocalEvent < CryoPodComponent , PowerChangedEvent > ( OnPowerChanged ) ;
SubscribeLocalEvent < CryoPodComponent , GasAnalyzerScanEvent > ( OnGasAnalyzed ) ;
SubscribeLocalEvent < CryoPodComponent , ActivatableUIOpenAttemptEvent > ( OnActivateUIAttempt ) ;
SubscribeLocalEvent < CryoPodComponent , AfterActivatableUIOpenEvent > ( OnActivateUI ) ;
2024-02-21 17:47:23 +03:00
SubscribeLocalEvent < CryoPodComponent , EntRemovedFromContainerMessage > ( OnEjected ) ;
2022-12-25 12:35:51 +01:00
}
public override void Update ( float frameTime )
{
base . Update ( frameTime ) ;
var curTime = _gameTiming . CurTime ;
var bloodStreamQuery = GetEntityQuery < BloodstreamComponent > ( ) ;
var metaDataQuery = GetEntityQuery < MetaDataComponent > ( ) ;
var itemSlotsQuery = GetEntityQuery < ItemSlotsComponent > ( ) ;
var fitsInDispenserQuery = GetEntityQuery < FitsInDispenserComponent > ( ) ;
var solutionContainerManagerQuery = GetEntityQuery < SolutionContainerManagerComponent > ( ) ;
2023-10-19 12:34:31 -07:00
var query = EntityQueryEnumerator < ActiveCryoPodComponent , CryoPodComponent > ( ) ;
while ( query . MoveNext ( out var uid , out _ , out var cryoPod ) )
2022-12-25 12:35:51 +01:00
{
2023-10-19 12:34:31 -07:00
metaDataQuery . TryGetComponent ( uid , out var metaDataComponent ) ;
if ( curTime < cryoPod . NextInjectionTime + _metaDataSystem . GetPauseTime ( uid , metaDataComponent ) )
2022-12-25 12:35:51 +01:00
continue ;
cryoPod . NextInjectionTime = curTime + TimeSpan . FromSeconds ( cryoPod . BeakerTransferTime ) ;
2023-10-19 12:34:31 -07:00
if ( ! itemSlotsQuery . TryGetComponent ( uid , out var itemSlotsComponent ) )
2022-12-25 12:35:51 +01:00
{
continue ;
}
2023-10-19 12:34:31 -07:00
var container = _itemSlotsSystem . GetItemOrNull ( uid , cryoPod . SolutionContainerName , itemSlotsComponent ) ;
2022-12-25 12:35:51 +01:00
var patient = cryoPod . BodyContainer . ContainedEntity ;
if ( container ! = null
& & container . Value . Valid
& & patient ! = null
2022-12-30 15:34:59 +01:00
& & fitsInDispenserQuery . TryGetComponent ( container , out var fitsInDispenserComponent )
& & solutionContainerManagerQuery . TryGetComponent ( container ,
out var solutionContainerManagerComponent )
2023-12-29 04:47:43 -08:00
& & _solutionContainerSystem . TryGetFitsInDispenser ( ( container . Value , fitsInDispenserComponent , solutionContainerManagerComponent ) ,
out var containerSolution , out _ ) )
2022-12-25 12:35:51 +01:00
{
if ( ! bloodStreamQuery . TryGetComponent ( patient , out var bloodstream ) )
{
continue ;
}
2023-12-29 04:47:43 -08:00
var solutionToInject = _solutionContainerSystem . SplitSolution ( containerSolution . Value , cryoPod . BeakerTransferAmount ) ;
2022-12-25 12:35:51 +01:00
_bloodstreamSystem . TryAddToChemicals ( patient . Value , solutionToInject , bloodstream ) ;
_reactiveSystem . DoEntityReaction ( patient . Value , solutionToInject , ReactionMethod . Injection ) ;
}
}
}
2023-05-28 03:59:27 -05:00
public override EntityUid ? EjectBody ( EntityUid uid , CryoPodComponent ? cryoPodComponent )
2022-12-25 12:35:51 +01:00
{
if ( ! Resolve ( uid , ref cryoPodComponent ) )
2023-05-28 03:59:27 -05:00
return null ;
2023-12-29 04:47:43 -08:00
if ( cryoPodComponent . BodyContainer . ContainedEntity is not { Valid : true } contained )
2023-05-28 03:59:27 -05:00
return null ;
2022-12-25 12:35:51 +01:00
base . EjectBody ( uid , cryoPodComponent ) ;
_climbSystem . ForciblySetClimbing ( contained , uid ) ;
2023-05-28 03:59:27 -05:00
return contained ;
2022-12-25 12:35:51 +01:00
}
#region Interaction
2023-12-29 04:47:43 -08:00
private void HandleDragDropOn ( Entity < CryoPodComponent > entity , ref DragDropTargetEvent args )
2022-12-25 12:35:51 +01:00
{
2023-12-29 04:47:43 -08:00
if ( entity . Comp . BodyContainer . ContainedEntity ! = null )
2022-12-25 12:35:51 +01:00
return ;
2023-12-29 04:47:43 -08:00
var doAfterArgs = new DoAfterArgs ( EntityManager , args . User , entity . Comp . EntryDelay , new CryoPodDragFinished ( ) , entity , target : args . Dragged , used : entity )
2022-12-25 12:35:51 +01:00
{
BreakOnDamage = true ,
BreakOnTargetMove = true ,
BreakOnUserMove = true ,
NeedHand = false ,
} ;
2023-04-03 13:13:48 +12:00
_doAfterSystem . TryStartDoAfter ( doAfterArgs ) ;
2022-12-25 12:35:51 +01:00
args . Handled = true ;
}
2023-12-29 04:47:43 -08:00
private void OnDragFinished ( Entity < CryoPodComponent > entity , ref CryoPodDragFinished args )
2023-02-24 19:01:25 -05:00
{
if ( args . Cancelled | | args . Handled | | args . Args . Target = = null )
return ;
2023-12-29 04:47:43 -08:00
if ( InsertBody ( entity . Owner , args . Args . Target . Value , entity . Comp ) )
2023-05-28 03:59:27 -05:00
{
2023-12-29 04:47:43 -08:00
if ( ! TryComp ( entity . Owner , out CryoPodAirComponent ? cryoPodAir ) )
2023-05-28 03:59:27 -05:00
_adminLogger . Add ( LogType . Action , LogImpact . Medium ,
2023-12-29 04:47:43 -08:00
$"{ToPrettyString(args.User)} inserted {ToPrettyString(args.Args.Target.Value)} into {ToPrettyString(entity.Owner)}" ) ;
2023-05-28 03:59:27 -05:00
_adminLogger . Add ( LogType . Action , LogImpact . Medium ,
2023-12-29 04:47:43 -08:00
$"{ToPrettyString(args.User)} inserted {ToPrettyString(args.Args.Target.Value)} into {ToPrettyString(entity.Owner)} which contains gas: {cryoPodAir!.Air.ToPrettyString():gasMix}" ) ;
2023-05-28 03:59:27 -05:00
}
2023-02-24 19:01:25 -05:00
args . Handled = true ;
}
2023-12-29 04:47:43 -08:00
private void OnActivateUIAttempt ( Entity < CryoPodComponent > entity , ref ActivatableUIOpenAttemptEvent args )
2022-12-25 12:35:51 +01:00
{
if ( args . Cancelled )
{
return ;
}
2023-12-29 04:47:43 -08:00
var containedEntity = entity . Comp . BodyContainer . ContainedEntity ;
if ( containedEntity = = null | | containedEntity = = args . User | | ! HasComp < ActiveCryoPodComponent > ( entity ) )
2022-12-25 12:35:51 +01:00
{
args . Cancel ( ) ;
}
}
2023-12-29 04:47:43 -08:00
private void OnActivateUI ( Entity < CryoPodComponent > entity , ref AfterActivatableUIOpenEvent args )
2022-12-25 12:35:51 +01:00
{
2024-02-01 17:33:53 -05:00
if ( ! entity . Comp . BodyContainer . ContainedEntity . HasValue )
return ;
2023-12-29 04:47:43 -08:00
TryComp < TemperatureComponent > ( entity . Comp . BodyContainer . ContainedEntity , out var temp ) ;
TryComp < BloodstreamComponent > ( entity . Comp . BodyContainer . ContainedEntity , out var bloodstream ) ;
2023-07-18 17:13:26 -04:00
2024-02-21 17:47:23 +03:00
if ( TryComp < HealthAnalyzerComponent > ( entity , out var healthAnalyzer ) )
{
healthAnalyzer . ScannedEntity = entity . Comp . BodyContainer . ContainedEntity ;
}
2022-12-25 12:35:51 +01:00
_userInterfaceSystem . TrySendUiMessage (
2023-12-29 04:47:43 -08:00
entity . Owner ,
2023-04-11 17:11:02 -07:00
HealthAnalyzerUiKey . Key ,
2023-12-29 04:47:43 -08:00
new HealthAnalyzerScannedUserMessage ( GetNetEntity ( entity . Comp . BodyContainer . ContainedEntity ) ,
temp ? . CurrentTemperature ? ? 0 ,
2024-02-01 17:33:53 -05:00
( bloodstream ! = null & & _solutionContainerSystem . ResolveSolution ( entity . Comp . BodyContainer . ContainedEntity . Value ,
bloodstream . BloodSolutionName , ref bloodstream . BloodSolution , out var bloodSolution ) )
2023-12-29 04:47:43 -08:00
? bloodSolution . FillFraction
2024-02-06 13:20:09 +00:00
: 0 ,
2024-02-26 18:26:46 -05:00
null ,
2024-02-06 13:20:09 +00:00
null
2023-12-29 04:47:43 -08:00
) ) ;
2022-12-25 12:35:51 +01:00
}
2023-12-29 04:47:43 -08:00
private void OnInteractUsing ( Entity < CryoPodComponent > entity , ref InteractUsingEvent args )
2022-12-25 12:35:51 +01:00
{
2023-12-29 04:47:43 -08:00
if ( args . Handled | | ! entity . Comp . Locked | | entity . Comp . BodyContainer . ContainedEntity = = null )
2022-12-25 12:35:51 +01:00
return ;
2023-12-29 04:47:43 -08:00
args . Handled = _toolSystem . UseTool ( args . Used , args . User , entity . Owner , entity . Comp . PryDelay , "Prying" , new CryoPodPryFinished ( ) ) ;
2022-12-25 12:35:51 +01:00
}
2023-12-29 04:47:43 -08:00
private void OnExamined ( Entity < CryoPodComponent > entity , ref ExaminedEvent args )
2022-12-25 12:35:51 +01:00
{
2023-12-29 04:47:43 -08:00
var container = _itemSlotsSystem . GetItemOrNull ( entity . Owner , entity . Comp . SolutionContainerName ) ;
if ( args . IsInDetailsRange & & container ! = null & & _solutionContainerSystem . TryGetFitsInDispenser ( container . Value , out _ , out var containerSolution ) )
2022-12-25 12:35:51 +01:00
{
2024-01-05 23:53:13 -07:00
using ( args . PushGroup ( nameof ( CryoPodComponent ) ) )
2022-12-25 12:35:51 +01:00
{
2024-01-05 23:53:13 -07:00
args . PushMarkup ( Loc . GetString ( "cryo-pod-examine" , ( "beaker" , Name ( container . Value ) ) ) ) ;
if ( containerSolution . Volume = = 0 )
{
args . PushMarkup ( Loc . GetString ( "cryo-pod-empty-beaker" ) ) ;
}
2022-12-25 12:35:51 +01:00
}
}
}
2023-12-29 04:47:43 -08:00
private void OnPowerChanged ( Entity < CryoPodComponent > entity , ref PowerChangedEvent args )
2022-12-25 12:35:51 +01:00
{
// Needed to avoid adding/removing components on a deleted entity
2023-12-29 04:47:43 -08:00
if ( Terminating ( entity ) )
2022-12-25 12:35:51 +01:00
{
return ;
}
if ( args . Powered )
{
2023-12-29 04:47:43 -08:00
EnsureComp < ActiveCryoPodComponent > ( entity ) ;
2022-12-25 12:35:51 +01:00
}
else
{
2023-12-29 04:47:43 -08:00
RemComp < ActiveCryoPodComponent > ( entity ) ;
_uiSystem . TryCloseAll ( entity . Owner , HealthAnalyzerUiKey . Key ) ;
2022-12-25 12:35:51 +01:00
}
2023-12-29 04:47:43 -08:00
UpdateAppearance ( entity . Owner , entity . Comp ) ;
2022-12-25 12:35:51 +01:00
}
#endregion
#region Atmos handler
2023-12-29 04:47:43 -08:00
private void OnCryoPodUpdateAtmosphere ( Entity < CryoPodComponent > entity , ref AtmosDeviceUpdateEvent args )
2022-12-25 12:35:51 +01:00
{
2023-12-29 04:47:43 -08:00
if ( ! TryComp ( entity , out NodeContainerComponent ? nodeContainer ) )
2022-12-25 12:35:51 +01:00
return ;
2023-12-29 04:47:43 -08:00
if ( ! _nodeContainer . TryGetNode ( nodeContainer , entity . Comp . PortName , out PortablePipeNode ? portNode ) )
2022-12-25 12:35:51 +01:00
return ;
2023-04-09 15:28:19 -07:00
2023-12-29 04:47:43 -08:00
if ( ! TryComp ( entity , out CryoPodAirComponent ? cryoPodAir ) )
2023-04-09 15:28:19 -07:00
return ;
_atmosphereSystem . React ( cryoPodAir . Air , portNode ) ;
2022-12-25 12:35:51 +01:00
2023-12-29 04:47:43 -08:00
if ( portNode . NodeGroup is PipeNet { NodeCount : > 1 } net )
2022-12-25 12:35:51 +01:00
{
2023-04-09 15:28:19 -07:00
_gasCanisterSystem . MixContainerWithPipeNet ( cryoPodAir . Air , net . Air ) ;
2022-12-25 12:35:51 +01:00
}
}
2023-12-29 04:47:43 -08:00
private void OnGasAnalyzed ( Entity < CryoPodComponent > entity , ref GasAnalyzerScanEvent args )
2022-12-25 12:35:51 +01:00
{
2023-12-29 04:47:43 -08:00
if ( ! TryComp ( entity , out CryoPodAirComponent ? cryoPodAir ) )
2023-04-09 15:28:19 -07:00
return ;
2023-12-29 04:47:43 -08:00
var gasMixDict = new Dictionary < string , GasMixture ? > { { Name ( entity . Owner ) , cryoPodAir . Air } } ;
2022-12-25 12:35:51 +01:00
// If it's connected to a port, include the port side
2023-12-29 04:47:43 -08:00
if ( TryComp ( entity , out NodeContainerComponent ? nodeContainer ) )
2022-12-25 12:35:51 +01:00
{
2023-12-29 04:47:43 -08:00
if ( _nodeContainer . TryGetNode ( nodeContainer , entity . Comp . PortName , out PipeNode ? port ) )
gasMixDict . Add ( entity . Comp . PortName , port . Air ) ;
2022-12-25 12:35:51 +01:00
}
args . GasMixtures = gasMixDict ;
}
2024-02-21 17:47:23 +03:00
private void OnEjected ( Entity < CryoPodComponent > cryoPod , ref EntRemovedFromContainerMessage args )
{
if ( TryComp < HealthAnalyzerComponent > ( cryoPod . Owner , out var healthAnalyzer ) )
{
healthAnalyzer . ScannedEntity = null ;
}
// if body is ejected - no need to display health-analyzer
_uiSystem . TryCloseAll ( cryoPod . Owner , HealthAnalyzerUiKey . Key ) ;
}
2022-12-25 12:35:51 +01:00
#endregion
}