diff --git a/Content.Client/Medical/Cryogenics/CryoPodSystem.cs b/Content.Client/Medical/Cryogenics/CryoPodSystem.cs index 13db5a8421..c1cbfc573e 100644 --- a/Content.Client/Medical/Cryogenics/CryoPodSystem.cs +++ b/Content.Client/Medical/Cryogenics/CryoPodSystem.cs @@ -1,7 +1,5 @@ using System.Numerics; -using Content.Shared.Emag.Systems; using Content.Shared.Medical.Cryogenics; -using Content.Shared.Verbs; using Robust.Client.GameObjects; namespace Content.Client.Medical.Cryogenics; @@ -15,11 +13,6 @@ public sealed class CryoPodSystem : SharedCryoPodSystem { base.Initialize(); - SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent>(AddAlternativeVerbs); - SubscribeLocalEvent(OnEmagged); - SubscribeLocalEvent(OnCryoPodPryFinished); - SubscribeLocalEvent(OnAppearanceChange); SubscribeLocalEvent(OnCryoPodInsertion); SubscribeLocalEvent(OnCryoPodRemoval); @@ -53,8 +46,8 @@ public sealed class CryoPodSystem : SharedCryoPodSystem return; } - if (!_appearance.TryGetData(uid, CryoPodComponent.CryoPodVisuals.ContainsEntity, out var isOpen, args.Component) - || !_appearance.TryGetData(uid, CryoPodComponent.CryoPodVisuals.IsOn, out var isOn, args.Component)) + if (!_appearance.TryGetData(uid, CryoPodVisuals.ContainsEntity, out var isOpen, args.Component) + || !_appearance.TryGetData(uid, CryoPodVisuals.IsOn, out var isOn, args.Component)) { return; } diff --git a/Content.Server/Medical/CryoPodEjectLockWireAction.cs b/Content.Server/Medical/CryoPodEjectLockWireAction.cs index 6fbf9eb250..d9bd2b3250 100644 --- a/Content.Server/Medical/CryoPodEjectLockWireAction.cs +++ b/Content.Server/Medical/CryoPodEjectLockWireAction.cs @@ -1,4 +1,3 @@ -using Content.Server.Medical.Components; using Content.Server.Wires; using Content.Shared.Medical.Cryogenics; using Content.Shared.Wires; @@ -8,7 +7,7 @@ namespace Content.Server.Medical; /// /// Causes a failure in the cryo pod ejection system when cut. A crowbar will be needed to pry open the pod. /// -public sealed partial class CryoPodEjectLockWireAction: ComponentWireAction +public sealed partial class CryoPodEjectLockWireAction : ComponentWireAction { public override Color Color { get; set; } = Color.Red; public override string Name { get; set; } = "wire-name-lock"; @@ -18,7 +17,10 @@ public sealed partial class CryoPodEjectLockWireAction: ComponentWireAction PryingQuality = "Prying"; + [Dependency] private readonly SharedUserInterfaceSystem _uiSystem = default!; + [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnComponentInit); - SubscribeLocalEvent>(AddAlternativeVerbs); - SubscribeLocalEvent(OnEmagged); - SubscribeLocalEvent(OnDragFinished); - SubscribeLocalEvent(OnCryoPodPryFinished); - - SubscribeLocalEvent(OnCryoPodUpdateAtmosphere); - SubscribeLocalEvent(HandleDragDropOn); - SubscribeLocalEvent(OnInteractUsing); - SubscribeLocalEvent(OnPowerChanged); - SubscribeLocalEvent(OnGasAnalyzed); - SubscribeLocalEvent(OnActivateUIAttempt); SubscribeLocalEvent(OnActivateUI); + SubscribeLocalEvent(OnCryoPodUpdateAtmosphere); + SubscribeLocalEvent(OnGasAnalyzed); SubscribeLocalEvent(OnEjected); } - public override void Update(float frameTime) - { - base.Update(frameTime); - - var curTime = _gameTiming.CurTime; - var bloodStreamQuery = GetEntityQuery(); - var metaDataQuery = GetEntityQuery(); - var itemSlotsQuery = GetEntityQuery(); - var fitsInDispenserQuery = GetEntityQuery(); - var solutionContainerManagerQuery = GetEntityQuery(); - var query = EntityQueryEnumerator(); - - while (query.MoveNext(out var uid, out _, out var cryoPod)) - { - metaDataQuery.TryGetComponent(uid, out var metaDataComponent); - if (curTime < cryoPod.NextInjectionTime + _metaDataSystem.GetPauseTime(uid, metaDataComponent)) - continue; - cryoPod.NextInjectionTime = curTime + TimeSpan.FromSeconds(cryoPod.BeakerTransferTime); - - if (!itemSlotsQuery.TryGetComponent(uid, out var itemSlotsComponent)) - { - continue; - } - var container = _itemSlotsSystem.GetItemOrNull(uid, cryoPod.SolutionContainerName, itemSlotsComponent); - var patient = cryoPod.BodyContainer.ContainedEntity; - if (container != null - && container.Value.Valid - && patient != null - && fitsInDispenserQuery.TryGetComponent(container, out var fitsInDispenserComponent) - && solutionContainerManagerQuery.TryGetComponent(container, - out var solutionContainerManagerComponent) - && _solutionContainerSystem.TryGetFitsInDispenser((container.Value, fitsInDispenserComponent, solutionContainerManagerComponent), - out var containerSolution, out _)) - { - if (!bloodStreamQuery.TryGetComponent(patient, out var bloodstream)) - { - continue; - } - - var solutionToInject = _solutionContainerSystem.SplitSolution(containerSolution.Value, cryoPod.BeakerTransferAmount); - _bloodstreamSystem.TryAddToChemicals((patient.Value, bloodstream), solutionToInject); - _reactiveSystem.DoEntityReaction(patient.Value, solutionToInject, ReactionMethod.Injection); - } - } - } - - public override EntityUid? EjectBody(EntityUid uid, CryoPodComponent? cryoPodComponent) - { - if (!Resolve(uid, ref cryoPodComponent)) - return null; - if (cryoPodComponent.BodyContainer.ContainedEntity is not { Valid: true } contained) - return null; - base.EjectBody(uid, cryoPodComponent); - _climbSystem.ForciblySetClimbing(contained, uid); - return contained; - } - - #region Interaction - - private void HandleDragDropOn(Entity entity, ref DragDropTargetEvent args) - { - if (entity.Comp.BodyContainer.ContainedEntity != null) - return; - - var doAfterArgs = new DoAfterArgs(EntityManager, args.User, entity.Comp.EntryDelay, new CryoPodDragFinished(), entity, target: args.Dragged, used: entity) - { - BreakOnDamage = true, - BreakOnMove = true, - NeedHand = false, - }; - _doAfterSystem.TryStartDoAfter(doAfterArgs); - args.Handled = true; - } - - private void OnDragFinished(Entity entity, ref CryoPodDragFinished args) - { - if (args.Cancelled || args.Handled || args.Args.Target == null) - return; - - if (InsertBody(entity.Owner, args.Args.Target.Value, entity.Comp)) - { - if (!TryComp(entity.Owner, out CryoPodAirComponent? cryoPodAir)) - _adminLogger.Add(LogType.Action, LogImpact.Medium, - $"{ToPrettyString(args.User)} inserted {ToPrettyString(args.Args.Target.Value)} into {ToPrettyString(entity.Owner)}"); - - _adminLogger.Add(LogType.Action, LogImpact.Medium, - $"{ToPrettyString(args.User)} inserted {ToPrettyString(args.Args.Target.Value)} into {ToPrettyString(entity.Owner)} which contains gas: {cryoPodAir!.Air.ToPrettyString():gasMix}"); - } - args.Handled = true; - } - - private void OnActivateUIAttempt(Entity entity, ref ActivatableUIOpenAttemptEvent args) - { - if (args.Cancelled) - { - return; - } - - var containedEntity = entity.Comp.BodyContainer.ContainedEntity; - if (containedEntity == null || containedEntity == args.User || !HasComp(entity)) - { - args.Cancel(); - } - } - private void OnActivateUI(Entity entity, ref AfterActivatableUIOpenEvent args) { if (!entity.Comp.BodyContainer.ContainedEntity.HasValue) @@ -209,38 +63,6 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem )); } - private void OnInteractUsing(Entity entity, ref InteractUsingEvent args) - { - if (args.Handled || !entity.Comp.Locked || entity.Comp.BodyContainer.ContainedEntity == null) - return; - - args.Handled = _toolSystem.UseTool(args.Used, args.User, entity.Owner, entity.Comp.PryDelay, PryingQuality, new CryoPodPryFinished()); - } - - private void OnPowerChanged(Entity entity, ref PowerChangedEvent args) - { - // Needed to avoid adding/removing components on a deleted entity - if (Terminating(entity)) - { - return; - } - - if (args.Powered) - { - EnsureComp(entity); - } - else - { - RemComp(entity); - _uiSystem.CloseUi(entity.Owner, HealthAnalyzerUiKey.Key); - } - UpdateAppearance(entity.Owner, entity.Comp); - } - - #endregion - - #region Atmos handler - private void OnCryoPodUpdateAtmosphere(Entity entity, ref AtmosDeviceUpdateEvent args) { if (!_nodeContainer.TryGetNode(entity.Owner, entity.Comp.PortName, out PortablePipeNode? portNode)) @@ -285,6 +107,4 @@ public sealed partial class CryoPodSystem : SharedCryoPodSystem // if body is ejected - no need to display health-analyzer _uiSystem.CloseUi(cryoPod.Owner, HealthAnalyzerUiKey.Key); } - - #endregion } diff --git a/Content.Shared/Medical/Cryogenics/ActiveCryoPodComponent.cs b/Content.Shared/Medical/Cryogenics/ActiveCryoPodComponent.cs index e242fd7502..d017ef73aa 100644 --- a/Content.Shared/Medical/Cryogenics/ActiveCryoPodComponent.cs +++ b/Content.Shared/Medical/Cryogenics/ActiveCryoPodComponent.cs @@ -1,9 +1,9 @@ -namespace Content.Shared.Medical.Cryogenics; +using Robust.Shared.GameStates; + +namespace Content.Shared.Medical.Cryogenics; /// /// Tracking component for an enabled cryo pod (which periodically tries to inject chemicals in the occupant, if one exists) /// -[RegisterComponent] -public sealed partial class ActiveCryoPodComponent : Component -{ -} +[RegisterComponent, NetworkedComponent] +public sealed partial class ActiveCryoPodComponent : Component; diff --git a/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs b/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs index 43244a5f0d..ed9e9cb904 100644 --- a/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs +++ b/Content.Shared/Medical/Cryogenics/CryoPodComponent.cs @@ -1,57 +1,68 @@ +using Content.Shared.FixedPoint; +using Content.Shared.Tools; using Robust.Shared.Containers; using Robust.Shared.GameStates; +using Robust.Shared.Prototypes; using Robust.Shared.Serialization; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; namespace Content.Shared.Medical.Cryogenics; +/// +/// Component for medical cryo pods. +/// Handles transferring reagents from a beaker slot into an inserted mob, as well as exposing them to connected atmos pipes. +/// [RegisterComponent, NetworkedComponent] +[AutoGenerateComponentState, AutoGenerateComponentPause] public sealed partial class CryoPodComponent : Component { + /// + /// The name of the container the patient is stored in. + /// + public const string BodyContainerName = "scanner-body"; + /// /// Specifies the name of the atmospherics port to draw gas from. /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("port")] - public string PortName { get; set; } = "port"; + [DataField] + public string PortName = "port"; /// - /// Specifies the name of the slot that holds beaker with medicine. + /// Specifies the name of the slot that holds the beaker with medicine. /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("solutionContainerName")] - public string SolutionContainerName { get; set; } = "beakerSlot"; + [DataField] + public string SolutionContainerName = "beakerSlot"; /// - /// How often (seconds) are chemicals transferred from the beaker to the body? + /// How often are chemicals transferred from the beaker to the body? + /// (injection interval) /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("beakerTransferTime")] - public float BeakerTransferTime = 1f; - - [ViewVariables(VVAccess.ReadWrite)] - [DataField("nextInjectionTime", customTypeSerializer:typeof(TimeOffsetSerializer))] - public TimeSpan? NextInjectionTime; + [DataField] + public TimeSpan BeakerTransferTime = TimeSpan.FromSeconds(1); /// - /// How many units to transfer per tick from the beaker to the mob? + /// The timestamp for the next injection. /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("beakerTransferAmount")] - public float BeakerTransferAmount = 1f; + [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))] + [AutoNetworkedField, AutoPausedField] + public TimeSpan NextInjectionTime = TimeSpan.Zero; /// - /// Delay applied when inserting a mob in the pod. + /// How many units to transfer per injection from the beaker to the mob? /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("entryDelay")] + [DataField] + public FixedPoint2 BeakerTransferAmount = 1; + + /// + /// Delay applied when inserting a mob in the pod (in seconds). + /// + [DataField] public float EntryDelay = 2f; /// - /// Delay applied when trying to pry open a locked pod. + /// Delay applied when trying to pry open a locked pod (in seconds). /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("pryDelay")] + [DataField] public float PryDelay = 5f; /// @@ -63,21 +74,25 @@ public sealed partial class CryoPodComponent : Component /// /// If true, the eject verb will not work on the pod and the user must use a crowbar to pry the pod open. /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("locked")] - public bool Locked { get; set; } + [DataField, AutoNetworkedField] + public bool Locked; /// /// Causes the pod to be locked without being fixable by messing with wires. /// - [ViewVariables(VVAccess.ReadWrite)] - [DataField("permaLocked")] - public bool PermaLocked { get; set; } + [DataField, AutoNetworkedField] + public bool PermaLocked; - [Serializable, NetSerializable] - public enum CryoPodVisuals : byte - { - ContainsEntity, - IsOn - } + /// + /// The tool quality needed to eject a body when the pod is locked. + /// + [DataField, AutoNetworkedField] + public ProtoId UnlockToolQuality = "Prying"; +} + +[Serializable, NetSerializable] +public enum CryoPodVisuals : byte +{ + ContainsEntity, + IsOn } diff --git a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs index f45ecc0934..3fcc3da848 100644 --- a/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs +++ b/Content.Shared/Medical/Cryogenics/SharedCryoPodSystem.cs @@ -1,35 +1,58 @@ using Content.Shared.Administration.Logs; using Content.Shared.Body.Components; +using Content.Shared.Body.Systems; +using Content.Shared.Chemistry; +using Content.Shared.Chemistry.Components; +using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.EntitySystems; +using Content.Shared.Climbing.Systems; using Content.Shared.Containers.ItemSlots; using Content.Shared.Database; using Content.Shared.DoAfter; using Content.Shared.DragDrop; using Content.Shared.Emag.Systems; using Content.Shared.Examine; +using Content.Shared.Interaction; +using Content.Shared.MedicalScanner; using Content.Shared.Mobs.Components; using Content.Shared.Mobs.Systems; using Content.Shared.Popups; +using Content.Shared.Power; using Content.Shared.Standing; using Content.Shared.Stunnable; +using Content.Shared.Tools.Systems; +using Content.Shared.UserInterface; using Content.Shared.Verbs; using Robust.Shared.Containers; using Robust.Shared.Serialization; +using Robust.Shared.Timing; namespace Content.Shared.Medical.Cryogenics; -public abstract partial class SharedCryoPodSystem: EntitySystem +public abstract partial class SharedCryoPodSystem : EntitySystem { - [Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!; - [Dependency] private readonly StandingStateSystem _standingStateSystem = default!; + [Dependency] private readonly SharedAppearanceSystem _appearance = default!; + [Dependency] private readonly StandingStateSystem _standingState = default!; [Dependency] private readonly EmagSystem _emag = default!; - [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!; - [Dependency] private readonly MobStateSystem _mobStateSystem = default!; - [Dependency] private readonly SharedPopupSystem _popupSystem = default!; - [Dependency] private readonly SharedContainerSystem _containerSystem = default!; + [Dependency] private readonly ItemSlotsSystem _itemSlots = default!; + [Dependency] private readonly MobStateSystem _mobState = default!; + [Dependency] private readonly SharedPopupSystem _popup = default!; + [Dependency] private readonly SharedContainerSystem _container = default!; [Dependency] private readonly SharedPointLightSystem _light = default!; - [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!; + [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!; [Dependency] private readonly ISharedAdminLogManager _adminLogger = default!; + [Dependency] private readonly ClimbSystem _climb = default!; + [Dependency] private readonly SharedBloodstreamSystem _bloodstream = default!; + [Dependency] private readonly SharedDoAfterSystem _doAfter = default!; + [Dependency] private readonly SharedUserInterfaceSystem _ui = default!; + [Dependency] private readonly SharedToolSystem _tool = default!; + [Dependency] private readonly IGameTiming _timing = default!; + [Dependency] private readonly ReactiveSystem _reactive = default!; + + private EntityQuery _bloodstreamQuery; + private EntityQuery _itemSlotsQuery; + private EntityQuery _dispenserQuery; + private EntityQuery _solutionContainerQuery; public override void Initialize() { @@ -37,13 +60,141 @@ public abstract partial class SharedCryoPodSystem: EntitySystem SubscribeLocalEvent(OnCryoPodCanDropOn); SubscribeLocalEvent(OnExamined); + SubscribeLocalEvent(OnComponentInit); + SubscribeLocalEvent>(AddAlternativeVerbs); + SubscribeLocalEvent(OnEmagged); + SubscribeLocalEvent(OnDragFinished); + SubscribeLocalEvent(OnCryoPodPryFinished); + SubscribeLocalEvent(HandleDragDropOn); + SubscribeLocalEvent(OnInteractUsing); + SubscribeLocalEvent(OnPowerChanged); + SubscribeLocalEvent(OnActivateUIAttempt); + + _bloodstreamQuery = GetEntityQuery(); + _itemSlotsQuery = GetEntityQuery(); + _dispenserQuery = GetEntityQuery(); + _solutionContainerQuery = GetEntityQuery(); + InitializeInsideCryoPod(); } + public override void Update(float frameTime) + { + base.Update(frameTime); + + var curTime = _timing.CurTime; + var query = EntityQueryEnumerator(); + + while (query.MoveNext(out var uid, out _, out var cryoPod)) + { + if (curTime < cryoPod.NextInjectionTime) + continue; + + cryoPod.NextInjectionTime += cryoPod.BeakerTransferTime; + Dirty(uid, cryoPod); + + if (!_itemSlotsQuery.TryComp(uid, out var itemSlotsComponent)) + continue; + + var container = _itemSlots.GetItemOrNull(uid, cryoPod.SolutionContainerName, itemSlotsComponent); + var patient = cryoPod.BodyContainer.ContainedEntity; + if (container != null + && container.Value.Valid + && patient != null + && _dispenserQuery.TryComp(container, out var fitsInDispenserComponent) + && _solutionContainerQuery.TryComp(container, out var solutionContainerManagerComponent) + && _solutionContainer.TryGetFitsInDispenser((container.Value, fitsInDispenserComponent, solutionContainerManagerComponent), + out var containerSolution, out _) + && _bloodstreamQuery.TryComp(patient, out var bloodstream)) + { + var solutionToInject = _solutionContainer.SplitSolution(containerSolution.Value, cryoPod.BeakerTransferAmount); + _bloodstream.TryAddToChemicals((patient.Value, bloodstream), solutionToInject); + _reactive.DoEntityReaction(patient.Value, solutionToInject, ReactionMethod.Injection); + } + } + } + + private void HandleDragDropOn(Entity ent, ref DragDropTargetEvent args) + { + if (ent.Comp.BodyContainer.ContainedEntity != null) + return; + + var doAfterArgs = new DoAfterArgs(EntityManager, args.User, ent.Comp.EntryDelay, new CryoPodDragFinished(), ent, target: args.Dragged, used: ent) + { + BreakOnDamage = true, + BreakOnMove = true, + NeedHand = false, + }; + _doAfter.TryStartDoAfter(doAfterArgs); + args.Handled = true; + } + + private void OnDragFinished(Entity ent, ref CryoPodDragFinished args) + { + if (args.Cancelled || args.Handled || args.Args.Target == null) + return; + + if (InsertBody(ent.Owner, args.Args.Target.Value, ent.Comp)) + { + _adminLogger.Add(LogType.Action, LogImpact.Medium, + $"{ToPrettyString(args.User)} inserted {ToPrettyString(args.Args.Target.Value)} into {ToPrettyString(ent.Owner)}"); + } + args.Handled = true; + } + + private void OnActivateUIAttempt(Entity ent, ref ActivatableUIOpenAttemptEvent args) + { + if (args.Cancelled) + return; + + var containedEntity = ent.Comp.BodyContainer.ContainedEntity; + if (containedEntity == null || containedEntity == args.User || !HasComp(ent)) + args.Cancel(); + } + + private void OnInteractUsing(Entity ent, ref InteractUsingEvent args) + { + if (args.Handled || !ent.Comp.Locked || ent.Comp.BodyContainer.ContainedEntity == null) + return; + + args.Handled = _tool.UseTool(args.Used, args.User, ent.Owner, ent.Comp.PryDelay, ent.Comp.UnlockToolQuality, new CryoPodPryFinished()); + } + + private void OnCryoPodPryFinished(EntityUid uid, CryoPodComponent cryoPodComponent, CryoPodPryFinished args) + { + if (args.Cancelled) + return; + + var ejected = EjectBody(uid, cryoPodComponent); + if (ejected != null) + _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ejected.Value)} pried out of {ToPrettyString(uid)} by {ToPrettyString(args.User)}"); + } + + private void OnPowerChanged(Entity ent, ref PowerChangedEvent args) + { + // Needed to avoid adding/removing components on a deleted entity + if (Terminating(ent)) + return; + + if (args.Powered) + { + EnsureComp(ent); + ent.Comp.NextInjectionTime = _timing.CurTime + ent.Comp.BeakerTransferTime; + Dirty(ent); + } + else + { + RemComp(ent); + _ui.CloseUi(ent.Owner, HealthAnalyzerUiKey.Key); + } + + UpdateAppearance(ent.Owner, ent.Comp); + } + private void OnExamined(Entity entity, ref ExaminedEvent args) { - var container = _itemSlotsSystem.GetItemOrNull(entity.Owner, entity.Comp.SolutionContainerName); - if (args.IsInDetailsRange && container != null && _solutionContainerSystem.TryGetFitsInDispenser(container.Value, out _, out var containerSolution)) + var container = _itemSlots.GetItemOrNull(entity.Owner, entity.Comp.SolutionContainerName); + if (args.IsInDetailsRange && container != null && _solutionContainer.TryGetFitsInDispenser(container.Value, out _, out var containerSolution)) { using (args.PushGroup(nameof(CryoPodComponent))) { @@ -65,12 +216,12 @@ public abstract partial class SharedCryoPodSystem: EntitySystem args.Handled = true; } - protected void OnComponentInit(EntityUid uid, CryoPodComponent cryoPodComponent, ComponentInit args) + private void OnComponentInit(EntityUid uid, CryoPodComponent cryoPodComponent, ComponentInit args) { - cryoPodComponent.BodyContainer = _containerSystem.EnsureContainer(uid, "scanner-body"); + cryoPodComponent.BodyContainer = _container.EnsureContainer(uid, CryoPodComponent.BodyContainerName); } - protected void UpdateAppearance(EntityUid uid, CryoPodComponent? cryoPod = null, AppearanceComponent? appearance = null) + private void UpdateAppearance(EntityUid uid, CryoPodComponent? cryoPod = null, AppearanceComponent? appearance = null) { if (!Resolve(uid, ref cryoPod)) return; @@ -79,14 +230,14 @@ public abstract partial class SharedCryoPodSystem: EntitySystem if (_light.TryGetLight(uid, out var light)) { - _light.SetEnabled(uid, cryoPodEnabled && cryoPod.BodyContainer.ContainedEntity != null, light); + _light.SetEnabled(uid, cryoPodEnabled && cryoPod.BodyContainer?.ContainedEntity != null, light); } if (!Resolve(uid, ref appearance)) return; - _appearanceSystem.SetData(uid, CryoPodComponent.CryoPodVisuals.ContainsEntity, cryoPod.BodyContainer.ContainedEntity == null, appearance); - _appearanceSystem.SetData(uid, CryoPodComponent.CryoPodVisuals.IsOn, cryoPodEnabled, appearance); + _appearance.SetData(uid, CryoPodVisuals.ContainsEntity, cryoPod.BodyContainer?.ContainedEntity == null, appearance); + _appearance.SetData(uid, CryoPodVisuals.IsOn, cryoPodEnabled, appearance); } public bool InsertBody(EntityUid uid, EntityUid target, CryoPodComponent cryoPodComponent) @@ -98,10 +249,10 @@ public abstract partial class SharedCryoPodSystem: EntitySystem return false; var xform = Transform(target); - _containerSystem.Insert((target, xform), cryoPodComponent.BodyContainer); + _container.Insert((target, xform), cryoPodComponent.BodyContainer); EnsureComp(target); - _standingStateSystem.Stand(target, force: true); // Force-stand the mob so that the cryo pod sprite overlays it fully + _standingState.Stand(target, force: true); // Force-stand the mob so that the cryo pod sprite overlays it fully UpdateAppearance(uid, cryoPodComponent); return true; @@ -116,7 +267,7 @@ public abstract partial class SharedCryoPodSystem: EntitySystem if (cryoPodComponent.Locked) { - _popupSystem.PopupEntity(Loc.GetString("cryo-pod-locked"), uid, userId); + _popup.PopupClient(Loc.GetString("cryo-pod-locked"), uid, userId); return; } @@ -131,28 +282,25 @@ public abstract partial class SharedCryoPodSystem: EntitySystem /// The cryopod entity /// Cryopod component of /// Ejected entity - public virtual EntityUid? EjectBody(EntityUid uid, CryoPodComponent? cryoPodComponent) + public EntityUid? EjectBody(EntityUid uid, CryoPodComponent? cryoPodComponent) { if (!Resolve(uid, ref cryoPodComponent)) return null; - if (cryoPodComponent.BodyContainer.ContainedEntity is not {Valid: true} contained) + if (cryoPodComponent.BodyContainer.ContainedEntity is not { Valid: true } contained) return null; - _containerSystem.Remove(contained, cryoPodComponent.BodyContainer); + _container.Remove(contained, cryoPodComponent.BodyContainer); // InsideCryoPodComponent is removed automatically in its EntGotRemovedFromContainerMessage listener // RemComp(contained); // Restore the correct position of the patient. Checking the components manually feels hacky, but I did not find a better way for now. - if (HasComp(contained) || _mobStateSystem.IsIncapacitated(contained)) - { - _standingStateSystem.Down(contained); - } + if (HasComp(contained) || _mobState.IsIncapacitated(contained)) + _standingState.Down(contained); else - { - _standingStateSystem.Stand(contained); - } + _standingState.Stand(contained); + _climb.ForciblySetClimbing(contained, uid); UpdateAppearance(uid, cryoPodComponent); return contained; } @@ -188,26 +336,13 @@ public abstract partial class SharedCryoPodSystem: EntitySystem cryoPodComponent.PermaLocked = true; cryoPodComponent.Locked = true; + Dirty(uid, cryoPodComponent); args.Handled = true; } - protected void OnCryoPodPryFinished(EntityUid uid, CryoPodComponent cryoPodComponent, CryoPodPryFinished args) - { - if (args.Cancelled) - return; - - var ejected = EjectBody(uid, cryoPodComponent); - if (ejected != null) - _adminLogger.Add(LogType.Action, LogImpact.Medium, $"{ToPrettyString(ejected.Value)} pried out of {ToPrettyString(uid)} by {ToPrettyString(args.User)}"); - } + [Serializable, NetSerializable] + public sealed partial class CryoPodPryFinished : SimpleDoAfterEvent; [Serializable, NetSerializable] - public sealed partial class CryoPodPryFinished : SimpleDoAfterEvent - { - } - - [Serializable, NetSerializable] - public sealed partial class CryoPodDragFinished : SimpleDoAfterEvent - { - } + public sealed partial class CryoPodDragFinished : SimpleDoAfterEvent; } diff --git a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml index a7fec0fa69..d147417f9e 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/Medical/cryo_pod.yml @@ -109,6 +109,8 @@ - scanner-body - type: CryoPod - type: CryoPodAir + - type: Climbable # so that ejected bodies don't get stuck + vaultable: false - type: ContainerTemperatureDamageThresholds coldDamageThreshold: 10 - type: GuideHelp