diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs index 763391cdcc..9402d76cde 100644 --- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs +++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Hotspot.cs @@ -153,7 +153,7 @@ namespace Content.Server.Atmos.EntitySystems } else { - var affected = tile.Air.RemoveRatio(tile.Hotspot.Volume / tile.Air.Volume); + var affected = tile.Air.RemoveVolume(tile.Hotspot.Volume); affected.Temperature = tile.Hotspot.Temperature; React(affected, tile); tile.Hotspot.Temperature = affected.Temperature; diff --git a/Content.Server/Atmos/GasMixture.cs b/Content.Server/Atmos/GasMixture.cs index 4278520fa5..2b84daa697 100644 --- a/Content.Server/Atmos/GasMixture.cs +++ b/Content.Server/Atmos/GasMixture.cs @@ -176,6 +176,11 @@ namespace Content.Server.Atmos return removed; } + public GasMixture RemoveVolume(float vol) + { + return RemoveRatio(vol / Volume); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public void CopyFromMutable(GasMixture sample) { diff --git a/Content.Server/Atmos/Piping/Binary/Components/GasRecyclerComponent.cs b/Content.Server/Atmos/Piping/Binary/Components/GasRecyclerComponent.cs new file mode 100644 index 0000000000..03d4d95ed8 --- /dev/null +++ b/Content.Server/Atmos/Piping/Binary/Components/GasRecyclerComponent.cs @@ -0,0 +1,20 @@ +using Content.Shared.Atmos; + +namespace Content.Server.Atmos.Piping.Binary.Components +{ + [RegisterComponent] + public sealed class GasRecyclerComponent : Component + { + [ViewVariables(VVAccess.ReadOnly)] + [DataField("reacting")] + public Boolean Reacting { 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"; + } +} diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs new file mode 100644 index 0000000000..13aeb5ece5 --- /dev/null +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasRecyclerSystem.cs @@ -0,0 +1,120 @@ +using Content.Server.Atmos.EntitySystems; +using Content.Shared.Atmos.Piping; +using Content.Server.Atmos.Piping.Binary.Components; +using Content.Server.Atmos.Piping.Components; +using Content.Server.NodeContainer; +using Content.Server.NodeContainer.Nodes; +using Content.Shared.Atmos; +using Content.Shared.Audio; +using Content.Shared.Examine; +using JetBrains.Annotations; + +namespace Content.Server.Atmos.Piping.Binary.EntitySystems +{ + [UsedImplicitly] + public sealed class GasReyclerSystem : EntitySystem + { + [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly SharedAmbientSoundSystem _ambientSoundSystem = default!; + + private const float MinTemp = 300 + Atmospherics.T0C; // 300 C + private const float MinPressure = 30 * Atmospherics.OneAtmosphere; // 3 MPa + + public override void Initialize() + { + base.Initialize(); + SubscribeLocalEvent(OnEnabled); + SubscribeLocalEvent(OnUpdate); + SubscribeLocalEvent(OnDisabled); + SubscribeLocalEvent(OnExamined); + } + + private void OnEnabled(EntityUid uid, GasRecyclerComponent comp, AtmosDeviceEnabledEvent args) + { + UpdateAppearance(uid, comp); + } + + private void OnExamined(EntityUid uid, GasRecyclerComponent comp, ExaminedEvent args) + { + if (!EntityManager.GetComponent(comp.Owner).Anchored || !args.IsInDetailsRange) // Not anchored? Out of range? No status. + return; + + if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) + || !nodeContainer.TryGetNode(comp.InletName, out PipeNode? inlet) + || !nodeContainer.TryGetNode(comp.OutletName, out PipeNode? outlet)) + { + return; + } + + if (comp.Reacting) + { + args.PushMarkup(Loc.GetString("gas-recycler-reacting")); + } + else + { + if (inlet.Air.Pressure < MinPressure) + { + args.PushMarkup(Loc.GetString("gas-recycler-low-pressure")); + } + + if (inlet.Air.Temperature < MinTemp) + { + args.PushMarkup(Loc.GetString("gas-recycler-low-temperature")); + } + } + } + + private void OnUpdate(EntityUid uid, GasRecyclerComponent comp, AtmosDeviceUpdateEvent args) + { + if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) + || !nodeContainer.TryGetNode(comp.InletName, out PipeNode? inlet) + || !nodeContainer.TryGetNode(comp.OutletName, out PipeNode? outlet)) + { + _ambientSoundSystem.SetAmbience(comp.Owner, false); + return; + } + + // The gas recycler is a passive device, so it permits gas flow even if nothing is being reacted. + comp.Reacting = inlet.Air.Temperature >= MinTemp && inlet.Air.Pressure >= MinPressure; + var removed = inlet.Air.RemoveVolume(PassiveTransferVol(inlet.Air, outlet.Air)); + if (comp.Reacting) + { + var nCO2 = removed.GetMoles(Gas.CarbonDioxide); + removed.AdjustMoles(Gas.CarbonDioxide, -nCO2); + removed.AdjustMoles(Gas.Oxygen, nCO2); + var nN2O = removed.GetMoles(Gas.NitrousOxide); + removed.AdjustMoles(Gas.NitrousOxide, -nN2O); + removed.AdjustMoles(Gas.Nitrogen, nN2O); + } + + _atmosphereSystem.Merge(outlet.Air, removed); + UpdateAppearance(uid, comp); + _ambientSoundSystem.SetAmbience(comp.Owner, true); + } + + public float PassiveTransferVol(GasMixture inlet, GasMixture outlet) + { + if (inlet.Pressure < outlet.Pressure) + { + return 0; + } + float overPressConst = 300; // pressure difference (in atm) to get 200 L/sec transfer rate + float alpha = Atmospherics.MaxTransferRate / (float)Math.Sqrt(overPressConst*Atmospherics.OneAtmosphere); + return alpha * (float)Math.Sqrt(inlet.Pressure - outlet.Pressure); + } + + private void OnDisabled(EntityUid uid, GasRecyclerComponent comp, AtmosDeviceDisabledEvent args) + { + comp.Reacting = false; + UpdateAppearance(uid, comp); + } + + private void UpdateAppearance(EntityUid uid, GasRecyclerComponent? comp = null, AppearanceComponent? appearance = null) + { + if (!Resolve(uid, ref comp, ref appearance, false)) + return; + + appearance.SetData(PumpVisuals.Enabled, comp.Reacting); + } + } +} diff --git a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs index cf41fc1187..e1a547512d 100644 --- a/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs +++ b/Content.Server/Atmos/Piping/Binary/EntitySystems/GasVolumePumpSystem.cs @@ -82,9 +82,7 @@ namespace Content.Server.Atmos.Piping.Binary.EntitySystems 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); + var removed = inlet.Air.RemoveVolume((float)(pump.TransferRate * (_gameTiming.CurTime - device.LastProcess).TotalSeconds)); // Some of the gas from the mixture leaks when overclocked. if (pump.Overclocked) diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs index 97887e36d9..f15e0e7290 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/GasFilterSystem.cs @@ -62,15 +62,15 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems } // We multiply the transfer rate in L/s by the seconds passed since the last process to get the liters. - var transferRatio = (float)(filter.TransferRate * (_gameTiming.CurTime - device.LastProcess).TotalSeconds) / inletNode.Air.Volume; + var transferVol = (float)(filter.TransferRate * (_gameTiming.CurTime - device.LastProcess).TotalSeconds); - if (transferRatio <= 0) + if (transferVol <= 0) { _ambientSoundSystem.SetAmbience(filter.Owner, false); return; } - var removed = inletNode.Air.RemoveRatio(transferRatio); + var removed = inletNode.Air.RemoveVolume(transferVol); if (filter.FilteredGas.HasValue) { diff --git a/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs b/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs index d9403ba3eb..c435dc5af0 100644 --- a/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs +++ b/Content.Server/Atmos/Piping/Trinary/EntitySystems/PressureControlledValveSystem.cs @@ -66,15 +66,15 @@ namespace Content.Server.Atmos.Piping.Trinary.EntitySystems 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) + var transferVolume = (float)(transferRate * (_gameTiming.CurTime - device.LastProcess).TotalSeconds); + if (transferVolume <= 0) { _ambientSoundSystem.SetAmbience(comp.Owner, false); return; } _ambientSoundSystem.SetAmbience(comp.Owner, true); - var removed = inletNode.Air.RemoveRatio(transferRatio); + var removed = inletNode.Air.RemoveVolume(transferVolume); _atmosphereSystem.Merge(outletNode.Air, removed); } diff --git a/Content.Server/Body/Systems/RespiratorSystem.cs b/Content.Server/Body/Systems/RespiratorSystem.cs index c647abf743..2b9c1c70fb 100644 --- a/Content.Server/Body/Systems/RespiratorSystem.cs +++ b/Content.Server/Body/Systems/RespiratorSystem.cs @@ -109,8 +109,7 @@ namespace Content.Server.Body.Systems return; } - var ratio = (Atmospherics.BreathVolume / ev.Gas.Volume); - var actualGas = ev.Gas.RemoveRatio(ratio); + var actualGas = ev.Gas.RemoveVolume(Atmospherics.BreathVolume); var lungRatio = 1.0f / organs.Count; var gas = organs.Count == 1 ? actualGas : actualGas.RemoveRatio(lungRatio); diff --git a/Resources/Locale/en-US/atmos/gas-recycler-system.ftl b/Resources/Locale/en-US/atmos/gas-recycler-system.ftl new file mode 100644 index 0000000000..cc527adf5c --- /dev/null +++ b/Resources/Locale/en-US/atmos/gas-recycler-system.ftl @@ -0,0 +1,3 @@ +gas-recycler-reacting = It is [color=green]converting[/color] waste gases. +gas-recycler-low-pressure = The input pressure is [color=darkred]too low[/color]. +gas-recycler-low-temperature = The input temperature is [color=darkred]too low[/color]. diff --git a/Resources/Prototypes/Catalog/Research/technologies.yml b/Resources/Prototypes/Catalog/Research/technologies.yml index c0541eb546..135768adb6 100644 --- a/Resources/Prototypes/Catalog/Research/technologies.yml +++ b/Resources/Prototypes/Catalog/Research/technologies.yml @@ -270,6 +270,7 @@ unlockedRecipes: - ThermomachineFreezerMachineCircuitBoard - PortableScrubberMachineCircuitBoard + - GasRecyclerMachineCircuitboard # Avionics Circuitry Technology Tree diff --git a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml index 9436adc6f9..a07d1ad655 100644 --- a/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml +++ b/Resources/Prototypes/Entities/Objects/Devices/Circuitboards/Machine/production.yml @@ -541,3 +541,15 @@ materialRequirements: Glass: 2 Cable: 2 + +- type: entity + id: GasRecyclerMachineCircuitboard + parent: BaseMachineCircuitboard + name: gas recycler board + description: A printed circuit board for a gas recycler + components: + - type: MachineBoard + prototype: GasRecycler + materialRequirements: + Steel: 10 + Plasma: 10 diff --git a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml index c8d253a2aa..f1246a135f 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/lathe.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/lathe.yml @@ -278,6 +278,7 @@ - WallmountGeneratorAPUElectronics - WallmountSubstationElectronics - EmitterCircuitboard + - GasRecyclerMachineCircuitboard - type: Machine board: CircuitImprinterMachineCircuitboard - type: Lathe diff --git a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml index cb6fa50c2f..32319b6b8d 100644 --- a/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml +++ b/Resources/Prototypes/Entities/Structures/Piping/Atmospherics/binary.yml @@ -257,3 +257,49 @@ !type:PipeNode nodeGroupID: Pipe pipeDirection: South + +- type: entity + parent: [ GasBinaryBase, BaseMachine, ConstructibleMachine ] + id: GasRecycler + name: gas recycler + description: Recycles carbon dioxide and nitrous oxide. Heater and compressor not included. + placement: + mode: SnapgridCenter + components: + - type: Sprite + sprite: Structures/Machines/gasrecycler.rsi + netsync: false + layers: + - sprite: Structures/Piping/Atmospherics/pipe.rsi + state: pipeStraight + map: [ "enum.PipeVisualLayers.Pipe" ] + - state: running + - state: unlit + shader: unshaded + - type: GenericVisualizer + visuals: + enum.PumpVisuals.Enabled: + enabled: + True: { state: running } + False: { state: unlit } + - type: Appearance + - type: PipeColorVisuals + - type: GasRecycler + - type: AmbientSound + enabled: false + volume: -9 + range: 5 + sound: + path: /Audio/Ambience/Objects/gas_pump.ogg + - type: Destructible + thresholds: + - trigger: + !type:DamageTrigger + damage: 100 + behaviors: + - !type:ChangeConstructionNodeBehavior + node: machineFrame + - !type:DoActsBehavior + acts: ["Destruction"] + - type: Machine + board: GasRecyclerMachineCircuitboard diff --git a/Resources/Prototypes/Recipes/Lathes/electronics.yml b/Resources/Prototypes/Recipes/Lathes/electronics.yml index ca09f5662c..eff89e2f1e 100644 --- a/Resources/Prototypes/Recipes/Lathes/electronics.yml +++ b/Resources/Prototypes/Recipes/Lathes/electronics.yml @@ -404,3 +404,12 @@ materials: Steel: 100 Glass: 900 + +- type: latheRecipe + id: GasRecyclerMachineCircuitboard + icon: Objects/Misc/module.rsi/id_mod.png + result: GasRecyclerMachineCircuitboard + completetime: 4 + materials: + Steel: 100 + Glass: 900 \ No newline at end of file diff --git a/Resources/Textures/Structures/Machines/gasrecycler.rsi/icon.png b/Resources/Textures/Structures/Machines/gasrecycler.rsi/icon.png new file mode 100644 index 0000000000..3c040e2545 Binary files /dev/null and b/Resources/Textures/Structures/Machines/gasrecycler.rsi/icon.png differ diff --git a/Resources/Textures/Structures/Machines/gasrecycler.rsi/meta.json b/Resources/Textures/Structures/Machines/gasrecycler.rsi/meta.json new file mode 100644 index 0000000000..40d9c0cb83 --- /dev/null +++ b/Resources/Textures/Structures/Machines/gasrecycler.rsi/meta.json @@ -0,0 +1,30 @@ +{ + "version": 1, + "license": "CC-BY-SA-3.0", + "copyright": "Created by Peptide90 for SS14", + "size": { + "x": 32, + "y": 32 + }, + "states": [ + { + "name": "icon" + }, + { + "name": "unlit" + }, + { + "name": "running", + "delays": [ + [ + 0.3, + 0.3, + 0.3, + 0.3, + 0.3, + 0.3 + ] + ] + } + ] +} diff --git a/Resources/Textures/Structures/Machines/gasrecycler.rsi/running.png b/Resources/Textures/Structures/Machines/gasrecycler.rsi/running.png new file mode 100644 index 0000000000..e1f0759da6 Binary files /dev/null and b/Resources/Textures/Structures/Machines/gasrecycler.rsi/running.png differ diff --git a/Resources/Textures/Structures/Machines/gasrecycler.rsi/unlit.png b/Resources/Textures/Structures/Machines/gasrecycler.rsi/unlit.png new file mode 100644 index 0000000000..b1823f2770 Binary files /dev/null and b/Resources/Textures/Structures/Machines/gasrecycler.rsi/unlit.png differ