Atmos pipe rework (#3833)
* Initial * Cleanup a bunch of things * some changes dunno * RequireAnchored * a * stuff * more work * Lots of progress * delete pipe visualizer * a * b * pipenet and pipenode cleanup * Fixes * Adds GasValve * Adds GasMiner * Fix stuff, maybe? * More fixes * Ignored components on the client * Adds thermomachine behavior, change a bunch of stuff * Remove Anchored * some work, but it's shitcode * significantly more ECS * ECS AtmosDevices * Cleanup * fix appearance * when the pipe direction is sus * Gas tanks and canisters * pipe anchoring and stuff * coding is my passion * Unsafe pipes take longer to unanchor * turns out we're no longer using eris canisters * Gas canister inserted tank appearance, improvements * Work on a bunch of appearances * Scrubber appearance * Reorganize AtmosphereSystem.Piping into a bunch of different systems * Appearance for vent/scrubber/pump turns off when leaving atmosphere * ThermoMachine appearance * Cleanup gas tanks * Remove passive gate unused imports * remove old canister UI functionality * PipeNode environment air, make everything use AssumeAir instead of merging manually * a * Reorganize atmos to follow new structure * ????? * Canister UI, restructure client * Restructure shared * Fix build tho * listen, at least the canister UI works entirely... * fix build : ) * Atmos device prototypes have names and descriptions * gas canister ui slider doesn't jitter * trinary prototypes * sprite for miners * ignore components * fix YAML * Fix port system doing useless thing * Fix build * fix thinking moment * fix build again because * canister direction * pipenode is a word * GasTank Air will throw on invalid states * fix build.... * Unhardcode volume pump thresholds * Volume pump and filter take time into account * Rename Join/Leave atmosphere events to AtmosDeviceEnabled/Disabled Event * Gas tank node volume is set by initial mixtuer * I love node container
This commit is contained in:
committed by
GitHub
parent
cfc3f2e7fc
commit
a2b737d945
@@ -0,0 +1,52 @@
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Binary.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class GasCanisterComponent : Component
|
||||
{
|
||||
public override string Name => "GasCanister";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("port")]
|
||||
public string PortName { get; set; } = "port";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("tank")]
|
||||
public string TankName { get; set; } = "tank";
|
||||
|
||||
/// <summary>
|
||||
/// Container name for the gas tank holder.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("container")]
|
||||
public string ContainerName { get; set; } = "GasCanisterTankHolder";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("gasMixture")]
|
||||
public GasMixture InitialMixture { get; } = new();
|
||||
|
||||
/// <summary>
|
||||
/// Stores the last pressure the tank had, for appearance-updating purposes.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public float LastPressure { get; set; } = 0f;
|
||||
|
||||
/// <summary>
|
||||
/// Minimum release pressure possible for the release valve.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("minReleasePressure")]
|
||||
public float MinReleasePressure { get; set; } = Atmospherics.OneAtmosphere / 10;
|
||||
|
||||
/// <summary>
|
||||
/// Maximum release pressure possible for the release valve.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
[DataField("maxReleasePressure")]
|
||||
public float MaxReleasePressure { get; set; } = Atmospherics.OneAtmosphere * 10;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Binary.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class GasPassiveGateComponent : Component
|
||||
{
|
||||
public override string Name => "GasPassiveGate";
|
||||
|
||||
[DataField("enabled")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
/// <summary>
|
||||
/// This is the minimum difference needed to overcome the friction in the mechanism.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("frictionDifference")]
|
||||
public float FrictionPressureDifference { get; set; } = 10f;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("inlet")]
|
||||
public string InletName { get; set; } = "inlet";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("outlet")]
|
||||
public string OutletName { get; set; } = "outlet";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float TargetPressure { get; set; } = Atmospherics.OneAtmosphere;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Binary.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class GasPortComponent : Component
|
||||
{
|
||||
public override string Name => "GasPort";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("pipe")]
|
||||
public string PipeName { get; set; } = "connected";
|
||||
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public GasMixture Buffer { get; } = new();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Binary.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class GasPressurePumpComponent : Component
|
||||
{
|
||||
public override string Name => "GasPressurePump";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("inlet")]
|
||||
public string InletName { get; set; } = "inlet";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("outlet")]
|
||||
public string OutletName { get; set; } = "outlet";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float TargetPressure { get; set; } = Atmospherics.OneAtmosphere;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
|
||||
using Content.Server.NodeContainer;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Helpers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Binary.Components
|
||||
{
|
||||
// TODO ATMOS: Make ECS.
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
[RegisterComponent]
|
||||
public class GasValveComponent : Component, IActivate
|
||||
{
|
||||
public override string Name => "GasValve";
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("open")]
|
||||
private bool _open = true;
|
||||
|
||||
[DataField("pipe")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
private string _pipeName = "pipe";
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
|
||||
Set();
|
||||
}
|
||||
|
||||
private void Set()
|
||||
{
|
||||
if (Owner.TryGetComponent(out NodeContainerComponent? nodeContainer)
|
||||
&& nodeContainer.TryGetNode(_pipeName, out PipeNode? pipe))
|
||||
{
|
||||
pipe.ConnectionsEnabled = _open;
|
||||
}
|
||||
}
|
||||
|
||||
private void Toggle()
|
||||
{
|
||||
_open = !_open;
|
||||
Set();
|
||||
}
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if(eventArgs.InRangeUnobstructed() && EntitySystem.Get<ActionBlockerSystem>().CanInteract(eventArgs.User))
|
||||
Toggle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Binary.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class GasVolumePumpComponent : Component
|
||||
{
|
||||
public override string Name => "GasVolumePump";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Enabled { get; set; } = true;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Overclocked { get; set; } = false;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("inlet")]
|
||||
public string InletName { get; set; } = "inlet";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("outlet")]
|
||||
public string OutletName { get; set; } = "outlet";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float TransferRate { get; set; } = Atmospherics.MaxTransferRate;
|
||||
|
||||
[DataField("leakRatio")]
|
||||
public float LeakRatio { get; set; } = 0.1f;
|
||||
|
||||
[DataField("lowerThreshold")]
|
||||
public float LowerThreshold { get; set; } = 0.01f;
|
||||
|
||||
[DataField("higherThreshold")]
|
||||
public float HigherThreshold { get; set; } = 9000f;
|
||||
|
||||
[DataField("overclockThreshold")]
|
||||
public float OverclockThreshold { get; set; } = 1000f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,247 @@
|
||||
using System;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.Piping.Binary.Components;
|
||||
using Content.Server.Atmos.Piping.Components;
|
||||
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.NodeContainer;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Piping.Binary.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Helpers;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class GasCanisterSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<GasCanisterComponent, ComponentStartup>(OnCanisterStartup);
|
||||
SubscribeLocalEvent<GasCanisterComponent, AtmosDeviceUpdateEvent>(OnCanisterUpdated);
|
||||
SubscribeLocalEvent<GasCanisterComponent, ActivateInWorldEvent>(OnCanisterActivate);
|
||||
SubscribeLocalEvent<GasCanisterComponent, InteractHandEvent>(OnCanisterInteractHand);
|
||||
SubscribeLocalEvent<GasCanisterComponent, InteractUsingEvent>(OnCanisterInteractUsing);
|
||||
SubscribeLocalEvent<GasCanisterComponent, EntInsertedIntoContainerMessage>(OnCanisterContainerInserted);
|
||||
SubscribeLocalEvent<GasCanisterComponent, EntRemovedFromContainerMessage>(OnCanisterContainerRemoved);
|
||||
}
|
||||
|
||||
private void OnCanisterStartup(EntityUid uid, GasCanisterComponent canister, ComponentStartup args)
|
||||
{
|
||||
// TODO ATMOS: Don't use Owner to get the UI.
|
||||
if(canister.Owner.GetUIOrNull(GasCanisterUiKey.Key) is {} ui)
|
||||
ui.OnReceiveMessage += msg => OnCanisterUIMessage(uid, canister, msg);
|
||||
|
||||
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
|
||||
return;
|
||||
|
||||
if (!nodeContainer.TryGetNode(canister.PortName, out PipeNode? portNode))
|
||||
return;
|
||||
|
||||
// Create a pipenet if we don't have one already.
|
||||
portNode.TryAssignGroupIfNeeded();
|
||||
portNode.Air.Merge(canister.InitialMixture);
|
||||
portNode.Air.Temperature = canister.InitialMixture.Temperature;
|
||||
portNode.Volume = canister.InitialMixture.Volume;
|
||||
}
|
||||
|
||||
private void DirtyUI(EntityUid uid)
|
||||
{
|
||||
if (!ComponentManager.TryGetComponent(uid, out IMetaDataComponent? metadata)
|
||||
|| !ComponentManager.TryGetComponent(uid, out GasCanisterComponent? canister)
|
||||
|| !ComponentManager.TryGetComponent(uid, out GasPassiveGateComponent? passiveGate)
|
||||
|| !ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)
|
||||
|| !nodeContainer.TryGetNode(canister.PortName, out PipeNode? portNode)
|
||||
|| !nodeContainer.TryGetNode(canister.TankName, out PipeNode? tankNode)
|
||||
|| !ComponentManager.TryGetComponent(uid, out ServerUserInterfaceComponent? userInterfaceComponent)
|
||||
|| !userInterfaceComponent.TryGetBoundUserInterface(GasCanisterUiKey.Key, out var ui))
|
||||
return;
|
||||
|
||||
string? tankLabel = null;
|
||||
var tankPressure = 0f;
|
||||
|
||||
if (ComponentManager.TryGetComponent(uid, out ContainerManagerComponent? containerManager) && containerManager.TryGetContainer(canister.ContainerName, out var tankContainer) && tankContainer.ContainedEntities.Count > 0)
|
||||
{
|
||||
var tank = tankContainer.ContainedEntities[0];
|
||||
tankLabel = tank.Name;
|
||||
tankPressure = tankNode.Air.Pressure;
|
||||
}
|
||||
|
||||
ui.SetState(new GasCanisterBoundUserInterfaceState(metadata.EntityName, portNode.Air.Pressure,
|
||||
portNode.NodeGroup.Nodes.Count > 1, tankLabel, tankPressure,
|
||||
passiveGate.TargetPressure, passiveGate.Enabled,
|
||||
canister.MinReleasePressure, canister.MaxReleasePressure));
|
||||
}
|
||||
|
||||
private void OnCanisterUIMessage(EntityUid uid, GasCanisterComponent canister, ServerBoundUserInterfaceMessage msg)
|
||||
{
|
||||
if (msg.Session.AttachedEntity is not {} entity
|
||||
|| !Get<ActionBlockerSystem>().CanInteract(entity)
|
||||
|| !Get<ActionBlockerSystem>().CanUse(entity))
|
||||
return;
|
||||
|
||||
|
||||
if (!ComponentManager.TryGetComponent(uid, out GasPassiveGateComponent? passiveGate)
|
||||
|| !ComponentManager.TryGetComponent(uid, out ContainerManagerComponent? containerManager)
|
||||
|| !containerManager.TryGetContainer(canister.ContainerName, out var container))
|
||||
return;
|
||||
|
||||
switch (msg.Message)
|
||||
{
|
||||
case GasCanisterHoldingTankEjectMessage:
|
||||
if (container.ContainedEntities.Count == 0)
|
||||
break;
|
||||
|
||||
container.Remove(container.ContainedEntities[0]);
|
||||
break;
|
||||
|
||||
case GasCanisterChangeReleasePressureMessage changeReleasePressure:
|
||||
var pressure = Math.Clamp(changeReleasePressure.Pressure, canister.MinReleasePressure, canister.MaxReleasePressure);
|
||||
|
||||
passiveGate.TargetPressure = pressure;
|
||||
DirtyUI(uid);
|
||||
break;
|
||||
|
||||
case GasCanisterChangeReleaseValveMessage changeReleaseValve:
|
||||
passiveGate.Enabled = changeReleaseValve.Valve;
|
||||
DirtyUI(uid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCanisterUpdated(EntityUid uid, GasCanisterComponent canister, AtmosDeviceUpdateEvent args)
|
||||
{
|
||||
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)
|
||||
|| !ComponentManager.TryGetComponent(uid, out AppearanceComponent? appearance))
|
||||
return;
|
||||
|
||||
if (!nodeContainer.TryGetNode(canister.PortName, out PipeNode? portNode))
|
||||
return;
|
||||
|
||||
DirtyUI(uid);
|
||||
|
||||
// Nothing to do here.
|
||||
if (MathHelper.CloseTo(portNode.Air.Pressure, canister.LastPressure))
|
||||
return;
|
||||
|
||||
canister.LastPressure = portNode.Air.Pressure;
|
||||
|
||||
if (portNode.Air.Pressure < 10)
|
||||
{
|
||||
appearance.SetData(GasCanisterVisuals.PressureState, 0);
|
||||
}
|
||||
else if (portNode.Air.Pressure < Atmospherics.OneAtmosphere)
|
||||
{
|
||||
appearance.SetData(GasCanisterVisuals.PressureState, 1);
|
||||
}
|
||||
else if (portNode.Air.Pressure < (15 * Atmospherics.OneAtmosphere))
|
||||
{
|
||||
appearance.SetData(GasCanisterVisuals.PressureState, 2);
|
||||
}
|
||||
else
|
||||
{
|
||||
appearance.SetData(GasCanisterVisuals.PressureState, 3);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCanisterActivate(EntityUid uid, GasCanisterComponent component, ActivateInWorldEvent args)
|
||||
{
|
||||
if (!args.User.TryGetComponent(out ActorComponent? actor))
|
||||
return;
|
||||
|
||||
component.Owner.GetUIOrNull(GasCanisterUiKey.Key)?.Open(actor.PlayerSession);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
|
||||
private void OnCanisterInteractHand(EntityUid uid, GasCanisterComponent component, InteractHandEvent args)
|
||||
{
|
||||
if (!args.User.TryGetComponent(out ActorComponent? actor))
|
||||
return;
|
||||
|
||||
component.Owner.GetUIOrNull(GasCanisterUiKey.Key)?.Open(actor.PlayerSession);
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnCanisterInteractUsing(EntityUid uid, GasCanisterComponent component, InteractUsingEvent args)
|
||||
{
|
||||
var canister = EntityManager.GetEntity(uid);
|
||||
var container = canister.EnsureContainer<ContainerSlot>(component.ContainerName);
|
||||
|
||||
// Container full.
|
||||
if (container.ContainedEntity != null)
|
||||
return;
|
||||
|
||||
// Check the used item is valid...
|
||||
if (!args.Used.TryGetComponent(out GasTankComponent? _)
|
||||
|| !args.Used.TryGetComponent(out NodeContainerComponent? _))
|
||||
return;
|
||||
|
||||
// Check the user has hands.
|
||||
if (!args.User.TryGetComponent(out HandsComponent? hands))
|
||||
return;
|
||||
|
||||
if (!args.User.InRangeUnobstructed(canister, SharedInteractionSystem.InteractionRange, popup: true))
|
||||
return;
|
||||
|
||||
if (!hands.Drop(args.Used, canister.Transform.Coordinates))
|
||||
return;
|
||||
|
||||
if (!container.Insert(args.Used))
|
||||
return;
|
||||
|
||||
args.Handled = true;
|
||||
}
|
||||
|
||||
private void OnCanisterContainerInserted(EntityUid uid, GasCanisterComponent component, EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (args.Container.ID != component.ContainerName)
|
||||
return;
|
||||
|
||||
DirtyUI(uid);
|
||||
|
||||
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)
|
||||
|| !nodeContainer.TryGetNode(component.TankName, out PipeNode? tankNode))
|
||||
return;
|
||||
|
||||
tankNode.EnvironmentalAir = false;
|
||||
tankNode.ConnectToContainedEntities = true;
|
||||
tankNode.NodeGroup.RemakeGroup();
|
||||
|
||||
if (!ComponentManager.TryGetComponent(uid, out AppearanceComponent? appearance))
|
||||
return;
|
||||
|
||||
appearance.SetData(GasCanisterVisuals.TankInserted, true);
|
||||
}
|
||||
|
||||
private void OnCanisterContainerRemoved(EntityUid uid, GasCanisterComponent component, EntRemovedFromContainerMessage args)
|
||||
{
|
||||
if (args.Container.ID != component.ContainerName)
|
||||
return;
|
||||
|
||||
DirtyUI(uid);
|
||||
|
||||
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer)
|
||||
|| !nodeContainer.TryGetNode(component.TankName, out PipeNode? tankNode))
|
||||
return;
|
||||
|
||||
tankNode.NodeGroup.RemakeGroup();
|
||||
tankNode.ConnectToContainedEntities = false;
|
||||
tankNode.EnvironmentalAir = true;
|
||||
|
||||
if (!ComponentManager.TryGetComponent(uid, out AppearanceComponent? appearance))
|
||||
return;
|
||||
|
||||
appearance.SetData(GasCanisterVisuals.TankInserted, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
using System;
|
||||
using Content.Server.Atmos.Piping.Binary.Components;
|
||||
using Content.Server.Atmos.Piping.Components;
|
||||
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
|
||||
using Content.Server.NodeContainer;
|
||||
using Content.Shared.Atmos;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class GasPassiveGateSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<GasPassiveGateComponent, AtmosDeviceUpdateEvent>(OnPassiveGateUpdated);
|
||||
}
|
||||
|
||||
private void OnPassiveGateUpdated(EntityUid uid, GasPassiveGateComponent gate, AtmosDeviceUpdateEvent args)
|
||||
{
|
||||
if (!gate.Enabled)
|
||||
return;
|
||||
|
||||
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
|
||||
return;
|
||||
|
||||
if (!nodeContainer.TryGetNode(gate.InletName, out PipeNode? inlet)
|
||||
|| !nodeContainer.TryGetNode(gate.OutletName, out PipeNode? outlet))
|
||||
return;
|
||||
|
||||
var outputStartingPressure = outlet.Air.Pressure;
|
||||
var inputStartingPressure = inlet.Air.Pressure;
|
||||
|
||||
if (outputStartingPressure >= MathF.Min(gate.TargetPressure, inputStartingPressure - gate.FrictionPressureDifference))
|
||||
return; // No need to pump gas, target reached or input pressure too low.
|
||||
|
||||
if (inlet.Air.TotalMoles > 0 && inlet.Air.Temperature > 0)
|
||||
{
|
||||
// We calculate the necessary moles to transfer using our good ol' friend PV=nRT.
|
||||
var pressureDelta = MathF.Min(gate.TargetPressure - outputStartingPressure, (inputStartingPressure - outputStartingPressure)/2);
|
||||
// We can't have a pressure delta that would cause outlet pressure > inlet pressure.
|
||||
|
||||
var transferMoles = pressureDelta * outlet.Air.Volume / (inlet.Air.Temperature * Atmospherics.R);
|
||||
|
||||
// Actually transfer the gas.
|
||||
outlet.AssumeAir(inlet.Air.Remove(transferMoles));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
using Content.Server.Atmos.Piping.Binary.Components;
|
||||
using Content.Server.Atmos.Piping.Components;
|
||||
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
|
||||
using Content.Server.NodeContainer;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Piping;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class GasPressurePumpSystem : EntitySystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<GasPressurePumpComponent, AtmosDeviceUpdateEvent>(OnPumpUpdated);
|
||||
SubscribeLocalEvent<GasPressurePumpComponent, AtmosDeviceDisabledEvent>(OnPumpLeaveAtmosphere);
|
||||
}
|
||||
|
||||
private void OnPumpUpdated(EntityUid uid, GasPressurePumpComponent pump, AtmosDeviceUpdateEvent args)
|
||||
{
|
||||
var appearance = pump.Owner.GetComponentOrNull<AppearanceComponent>();
|
||||
appearance?.SetData(PressurePumpVisuals.Enabled, false);
|
||||
|
||||
if (!pump.Enabled)
|
||||
return;
|
||||
|
||||
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
|
||||
return;
|
||||
|
||||
if (!nodeContainer.TryGetNode(pump.InletName, out PipeNode? inlet)
|
||||
|| !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet))
|
||||
return;
|
||||
|
||||
var outputStartingPressure = outlet.Air.Pressure;
|
||||
|
||||
if (MathHelper.CloseTo(pump.TargetPressure, outputStartingPressure))
|
||||
return; // No need to pump gas if target has been reached.
|
||||
|
||||
if (inlet.Air.TotalMoles > 0 && inlet.Air.Temperature > 0)
|
||||
{
|
||||
appearance?.SetData(PressurePumpVisuals.Enabled, true);
|
||||
|
||||
// We calculate the necessary moles to transfer using our good ol' friend PV=nRT.
|
||||
var pressureDelta = pump.TargetPressure - outputStartingPressure;
|
||||
var transferMoles = pressureDelta * outlet.Air.Volume / inlet.Air.Temperature * Atmospherics.R;
|
||||
|
||||
var removed = inlet.Air.Remove(transferMoles);
|
||||
outlet.AssumeAir(removed);
|
||||
}
|
||||
}
|
||||
|
||||
private void OnPumpLeaveAtmosphere(EntityUid uid, GasPressurePumpComponent component, AtmosDeviceDisabledEvent args)
|
||||
{
|
||||
if (ComponentManager.TryGetComponent(uid, out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(PressurePumpVisuals.Enabled, false);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
using Content.Server.Atmos.Piping.Binary.Components;
|
||||
using Content.Server.Atmos.Piping.Components;
|
||||
using Content.Server.GameObjects.Components.NodeContainer.Nodes;
|
||||
using Content.Server.NodeContainer;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.Atmos.Piping.Binary.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public class GasVolumePumpSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private IGameTiming _gameTiming = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<GasVolumePumpComponent, AtmosDeviceUpdateEvent>(OnVolumePumpUpdated);
|
||||
}
|
||||
|
||||
private void OnVolumePumpUpdated(EntityUid uid, GasVolumePumpComponent pump, AtmosDeviceUpdateEvent args)
|
||||
{
|
||||
if (!pump.Enabled)
|
||||
return;
|
||||
|
||||
if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
|
||||
return;
|
||||
|
||||
if (!ComponentManager.TryGetComponent(uid, out AtmosDeviceComponent? device))
|
||||
return;
|
||||
|
||||
if (!nodeContainer.TryGetNode(pump.InletName, out PipeNode? inlet)
|
||||
|| !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet))
|
||||
return;
|
||||
|
||||
var inputStartingPressure = inlet.Air.Pressure;
|
||||
var outputStartingPressure = outlet.Air.Pressure;
|
||||
|
||||
// Pump mechanism won't do anything if the pressure is too high/too low unless you overclock it.
|
||||
if ((inputStartingPressure < pump.LowerThreshold) || (outputStartingPressure > pump.HigherThreshold) && !pump.Overclocked)
|
||||
return;
|
||||
|
||||
// Overclocked pumps can only force gas a certain amount.
|
||||
if ((outputStartingPressure - inputStartingPressure > pump.OverclockThreshold) && pump.Overclocked)
|
||||
return;
|
||||
|
||||
// We multiply the transfer rate in L/s by the seconds passed since the last process to get the liters.
|
||||
var transferRatio = (float)(pump.TransferRate * (_gameTiming.CurTime - device.LastProcess).TotalSeconds) / inlet.Air.Volume;
|
||||
|
||||
var removed = inlet.Air.RemoveRatio(transferRatio);
|
||||
|
||||
// Some of the gas from the mixture leaks when overclocked.
|
||||
if (pump.Overclocked)
|
||||
{
|
||||
var tile = args.Atmosphere.GetTile(pump.Owner.Transform.Coordinates);
|
||||
|
||||
if (tile != null)
|
||||
{
|
||||
var leaked = removed.RemoveRatio(pump.LeakRatio);
|
||||
tile.AssumeAir(leaked);
|
||||
}
|
||||
}
|
||||
|
||||
outlet.AssumeAir(removed);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user