diff --git a/Content.Server/Atmos/Piping/Trinary/Components/PressureControlledValveComponent.cs b/Content.Server/Atmos/Piping/Trinary/Components/PressureControlledValveComponent.cs new file mode 100644 index 0000000000..5df77ed9fd --- /dev/null +++ b/Content.Server/Atmos/Piping/Trinary/Components/PressureControlledValveComponent.cs @@ -0,0 +1,35 @@ +using Content.Shared.Atmos; + +namespace Content.Server.Atmos.Piping.Trinary.Components +{ + [RegisterComponent] + public sealed class PressureControlledValveComponent : Component + { + [ViewVariables(VVAccess.ReadWrite)] + [DataField("inlet")] + public string InletName { get; set; } = "inlet"; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("control")] + public string ControlName { get; set; } = "control"; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("outlet")] + public string OutletName { get; set; } = "outlet"; + + [ViewVariables(VVAccess.ReadOnly)] + [DataField("enabled")] + public bool Enabled { get; set; } = false; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("gain")] + public float Gain { get; set; } = 10; + + [ViewVariables(VVAccess.ReadWrite)] + [DataField("threshold")] + public float Threshold { get; set; } = Atmospherics.OneAtmosphere; + + [DataField("maxTransferRate")] + public float MaxTransferRate { get; set; } = Atmospherics.MaxTransferRate; + } +} diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs new file mode 100644 index 0000000000..d9403ba3eb --- /dev/null +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs @@ -0,0 +1,96 @@ +using Content.Server.Atmos.EntitySystems; +using Content.Server.Atmos.Piping.Components; +using Content.Server.Atmos.Piping.Trinary.Components; +using Content.Server.NodeContainer; +using Content.Server.NodeContainer.Nodes; +using Content.Shared.Atmos.Piping; +using Content.Shared.Audio; +using JetBrains.Annotations; +using Robust.Shared.Timing; + +namespace Content.Server.Atmos.Piping.Trinary.EntitySystems +{ + [UsedImplicitly] + public sealed class PressureControlledValveSystem : EntitySystem + { + [Dependency] private IGameTiming _gameTiming = default!; + [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnInit); + SubscribeLocalEvent(OnUpdate); + SubscribeLocalEvent(OnFilterLeaveAtmosphere); + } + + private void OnInit(EntityUid uid, PressureControlledValveComponent comp, ComponentInit args) + { + UpdateAppearance(uid, comp); + } + + private void OnUpdate(EntityUid uid, PressureControlledValveComponent comp, AtmosDeviceUpdateEvent args) + { + if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) + || !EntityManager.TryGetComponent(uid, out AtmosDeviceComponent? device) + || !nodeContainer.TryGetNode(comp.InletName, out PipeNode? inletNode) + || !nodeContainer.TryGetNode(comp.ControlName, out PipeNode? controlNode) + || !nodeContainer.TryGetNode(comp.OutletName, out PipeNode? outletNode)) + { + _ambientSoundSystem.SetAmbience(comp.Owner, false); + comp.Enabled = false; + return; + } + + // If output is higher than input, flip input/output to enable bidirectional flow. + if (outletNode.Air.Pressure > inletNode.Air.Pressure) + { + PipeNode temp = outletNode; + outletNode = inletNode; + inletNode = temp; + } + + float control = (controlNode.Air.Pressure - outletNode.Air.Pressure) - comp.Threshold; + float transferRate; + if (control < 0) + { + comp.Enabled = false; + transferRate = 0; + } + else + { + comp.Enabled = true; + transferRate = Math.Min(control * comp.Gain, comp.MaxTransferRate); + } + UpdateAppearance(uid, comp); + + // We multiply the transfer rate in L/s by the seconds passed since the last process to get the liters. + var transferRatio = (float)(transferRate * (_gameTiming.CurTime - device.LastProcess).TotalSeconds) / inletNode.Air.Volume; + if (transferRatio <= 0) + { + _ambientSoundSystem.SetAmbience(comp.Owner, false); + return; + } + + _ambientSoundSystem.SetAmbience(comp.Owner, true); + var removed = inletNode.Air.RemoveRatio(transferRatio); + _atmosphereSystem.Merge(outletNode.Air, removed); + } + + private void OnFilterLeaveAtmosphere(EntityUid uid, PressureControlledValveComponent comp, AtmosDeviceDisabledEvent args) + { + comp.Enabled = false; + UpdateAppearance(uid, comp); + _ambientSoundSystem.SetAmbience(comp.Owner, false); + } + + private void UpdateAppearance(EntityUid uid, PressureControlledValveComponent? comp = null, AppearanceComponent? appearance = null) + { + if (!Resolve(uid, ref comp, ref appearance, false)) + return; + + appearance.SetData(FilterVisuals.Enabled, comp.Enabled); + } + } +} diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml index 9baee077fb..c02a6f4998 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/trinary.yml @@ -198,3 +198,58 @@ !type:PipeNode nodeGroupID: Pipe pipeDirection: North + +- type: entity + parent: GasPipeBase + id: PressureControlledValve + name: pneumatic valve + description: Valve controlled by pressure. + placement: + mode: SnapgridCenter + components: + - type: AtmosDevice + - type: SubFloorHide + blockInteractions: false + blockAmbience: false + - type: NodeContainer + nodes: + inlet: + !type:PipeNode + nodeGroupID: Pipe + pipeDirection: North + control: + !type:PipeNode + nodeGroupID: Pipe + pipeDirection: West + outlet: + !type:PipeNode + nodeGroupID: Pipe + pipeDirection: South + - type: Sprite + netsync: false + sprite: Structures/Piping/Atmospherics/pneumaticvalve.rsi + layers: + - sprite: Structures/Piping/Atmospherics/pipe.rsi + state: pipeTJunction + rotation: -90 + map: [ "enum.PipeVisualLayers.Pipe" ] + - state: off + map: [ "enum.SubfloorLayers.FirstLayer", "enabled" ] + - type: Appearance + - type: GenericVisualizer + visuals: + enum.FilterVisuals.Enabled: + enabled: + True: { state: on } + False: { state: off } + - type: PipeColorVisuals + - type: PressureControlledValve + - type: AmbientSound + enabled: false + volume: -9 + range: 5 + sound: + path: /Audio/Ambience/Objects/gas_hiss.ogg + - type: Construction + graph: GasTrinary + node: pneumaticvalve diff --git a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/atmos_trinary.yml b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/atmos_trinary.yml index 97c03b0006..5a77ea405b 100644 --- a/Resources/Prototypes/Recipes/Construction/Graphs/utilities/atmos_trinary.yml +++ b/Resources/Prototypes/Recipes/Construction/Graphs/utilities/atmos_trinary.yml @@ -16,6 +16,12 @@ amount: 2 doAfter: 1 + - to: pneumaticvalve + steps: + - material: Steel + amount: 2 + doAfter: 1 + - node: filter entity: GasFilter edges: @@ -47,3 +53,19 @@ steps: - tool: Welding doAfter: 1 + + - node: pneumaticvalve + entity: PressureControlledValve + edges: + - to: start + conditions: + - !type:EntityAnchored + anchored: false + completed: + - !type:SpawnPrototype + prototype: SheetSteel1 + amount: 2 + - !type:DeleteEntity + steps: + - tool: Welding + doAfter: 1 diff --git a/Resources/Prototypes/Recipes/Construction/utilities.yml b/Resources/Prototypes/Recipes/Construction/utilities.yml index 3642fe0dea..fd93294e46 100644 --- a/Resources/Prototypes/Recipes/Construction/utilities.yml +++ b/Resources/Prototypes/Recipes/Construction/utilities.yml @@ -12,7 +12,7 @@ state: camera objectType: Structure placementMode: SnapgridCenter - + - type: construction name: telescreen id: WallmountTelescreen @@ -511,3 +511,17 @@ conditions: - !type:TileNotBlocked {} +- type: construction + id: PressureControlledValve + name: pneumatic valve + graph: GasTrinary + startNode: start + targetNode: pneumaticvalve + category: Utilities + placementMode: SnapgridCenter + canBuildInImpassable: false + icon: + sprite: Structures/Piping/Atmospherics/pneumaticvalve.rsi + state: off + conditions: + - !type:TileNotBlocked {} diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pneumaticvalve.rsi/meta.json b/Resources/Textures/Structures/Piping/Atmospherics/pneumaticvalve.rsi/meta.json new file mode 100644 index 0000000000..85649ee55a --- /dev/null +++ b/Resources/Textures/Structures/Piping/Atmospherics/pneumaticvalve.rsi/meta.json @@ -0,0 +1,19 @@ +{ + "version": 1, + "license": "CC-BY-SA-4.0", + "copyright": "Kevin Zheng 2022", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "off", + "directions": 4 + }, + { + "name": "on", + "directions": 4 + } + ] +} diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pneumaticvalve.rsi/off.png b/Resources/Textures/Structures/Piping/Atmospherics/pneumaticvalve.rsi/off.png new file mode 100644 index 0000000000..a3b4059ba4 Binary files /dev/null and b/Resources/Textures/Structures/Piping/Atmospherics/pneumaticvalve.rsi/off.png differ diff --git a/Resources/Textures/Structures/Piping/Atmospherics/pneumaticvalve.rsi/on.png b/Resources/Textures/Structures/Piping/Atmospherics/pneumaticvalve.rsi/on.png new file mode 100644 index 0000000000..b641222e22 Binary files /dev/null and b/Resources/Textures/Structures/Piping/Atmospherics/pneumaticvalve.rsi/on.png differ