Gas Condensers (#22436)
Co-authored-by: Kevin Zheng <kevinz5000@gmail.com>
@@ -0,0 +1,33 @@
|
|||||||
|
using Content.Server.Atmos.Piping.Unary.EntitySystems;
|
||||||
|
|
||||||
|
namespace Content.Server.Atmos.Piping.Unary.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for an entity that converts moles of gas into units of reagent.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
[Access(typeof(GasCondenserSystem))]
|
||||||
|
public sealed partial class GasCondenserComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The ID for the pipe node.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public string Inlet = "pipe";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The ID for the solution.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public string SolutionId = "tank";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// For a condenser, how many U of reagents are given per each mole of gas.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// Derived from a standard of 500u per canister:
|
||||||
|
/// 400u / 1871.71051 moles per canister
|
||||||
|
/// </remarks>
|
||||||
|
[DataField, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float MolesToReagentMultiplier = 0.2137f;
|
||||||
|
}
|
||||||
@@ -0,0 +1,73 @@
|
|||||||
|
using Content.Server.Atmos.EntitySystems;
|
||||||
|
using Content.Server.Atmos.Piping.Components;
|
||||||
|
using Content.Server.Atmos.Piping.Unary.Components;
|
||||||
|
using Content.Server.NodeContainer;
|
||||||
|
using Content.Server.NodeContainer.EntitySystems;
|
||||||
|
using Content.Server.NodeContainer.Nodes;
|
||||||
|
using Content.Server.Power.Components;
|
||||||
|
using Content.Shared.Atmos;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Content.Server.Power.EntitySystems;
|
||||||
|
using Content.Shared.Chemistry.EntitySystems;
|
||||||
|
|
||||||
|
namespace Content.Server.Atmos.Piping.Unary.EntitySystems;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed class GasCondenserSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
|
||||||
|
[Dependency] private readonly PowerReceiverSystem _power = default!;
|
||||||
|
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
|
||||||
|
[Dependency] private readonly SolutionContainerSystem _solution = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<GasCondenserComponent, AtmosDeviceUpdateEvent>(OnCondenserUpdated);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnCondenserUpdated(EntityUid uid, GasCondenserComponent component, AtmosDeviceUpdateEvent args)
|
||||||
|
{
|
||||||
|
if (!(_power.IsPowered(uid) && TryComp<ApcPowerReceiverComponent>(uid, out var receiver))
|
||||||
|
|| !TryComp<NodeContainerComponent>(uid, out var nodeContainer)
|
||||||
|
|| !_nodeContainer.TryGetNode(nodeContainer, component.Inlet, out PipeNode? inlet)
|
||||||
|
|| !_solution.TryGetSolution(uid, component.SolutionId, out var solution))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (solution.AvailableVolume == 0 || inlet.Air.TotalMoles == 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var molesToConvert = NumberOfMolesToConvert(receiver, inlet.Air, args.dt);
|
||||||
|
var removed = inlet.Air.Remove(molesToConvert);
|
||||||
|
for (var i = 0; i < Atmospherics.TotalNumberOfGases; i++)
|
||||||
|
{
|
||||||
|
var moles = removed.Moles[i];
|
||||||
|
if (moles <= 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (_atmosphereSystem.GetGas(i).Reagent is not {} gasReagent)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var moleToReagentMultiplier = component.MolesToReagentMultiplier;
|
||||||
|
var amount = moles * moleToReagentMultiplier;
|
||||||
|
|
||||||
|
if (_solution.TryAddReagent(uid, solution, gasReagent, amount, out var remaining))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// if we have leftover reagent, then convert it back to moles and put it back in the mixture.
|
||||||
|
inlet.Air.AdjustMoles(i, remaining.Float() / moleToReagentMultiplier);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public float NumberOfMolesToConvert(ApcPowerReceiverComponent comp, GasMixture mix, float dt)
|
||||||
|
{
|
||||||
|
var hc = _atmosphereSystem.GetHeatCapacity(mix);
|
||||||
|
var alpha = 0.8f; // tuned to give us 1-ish u/second of reagent conversion
|
||||||
|
// ignores the energy needed to cool down the solution to the condensation point, but that probably adds too much difficulty and so let's not simulate that
|
||||||
|
var energy = comp.Load * dt;
|
||||||
|
return energy / (alpha * hc);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -374,6 +374,22 @@ public sealed partial class SolutionContainerSystem : EntitySystem
|
|||||||
return acceptedQuantity == reagentQuantity.Quantity;
|
return acceptedQuantity == reagentQuantity.Quantity;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds reagent of an Id to the container.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="targetUid"></param>
|
||||||
|
/// <param name="targetSolution">Container to which we are adding reagent</param>
|
||||||
|
/// <param name="prototype">The Id of the reagent to add.</param>
|
||||||
|
/// <param name="quantity">The amount of reagent to add.</param>
|
||||||
|
/// <returns>If all the reagent could be added.</returns>
|
||||||
|
[PublicAPI]
|
||||||
|
public bool TryAddReagent(EntityUid targetUid, Solution targetSolution, string prototype, FixedPoint2 quantity,
|
||||||
|
float? temperature = null, ReagentData? data = null)
|
||||||
|
{
|
||||||
|
var reagent = new ReagentQuantity(prototype, quantity, data);
|
||||||
|
return TryAddReagent(targetUid, targetSolution, reagent, out _, temperature);
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds reagent of an Id to the container.
|
/// Adds reagent of an Id to the container.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ wires-board-name-default = Wires
|
|||||||
wires-board-name-booze = BoozeDispenser
|
wires-board-name-booze = BoozeDispenser
|
||||||
wires-board-name-soda = SodaDispenser
|
wires-board-name-soda = SodaDispenser
|
||||||
wires-board-name-thermomachine = Thermomachine
|
wires-board-name-thermomachine = Thermomachine
|
||||||
|
wires-board-name-condenser = Condenser
|
||||||
wires-board-name-pa = Mk2 Particle Accelerator
|
wires-board-name-pa = Mk2 Particle Accelerator
|
||||||
wires-board-name-highsec = HighSec Control
|
wires-board-name-highsec = HighSec Control
|
||||||
wires-board-name-vessel = Vessel
|
wires-board-name-vessel = Vessel
|
||||||
|
|||||||
@@ -338,6 +338,21 @@
|
|||||||
deconstructionTarget: null
|
deconstructionTarget: null
|
||||||
node: heater
|
node: heater
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: CondenserMachineCircuitBoard
|
||||||
|
parent: BaseMachineCircuitboard
|
||||||
|
name: condenser machine board
|
||||||
|
description: A machine printed circuit board for a condenser.
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
state: engineering
|
||||||
|
- type: MachineBoard
|
||||||
|
prototype: BaseGasCondenser
|
||||||
|
requirements:
|
||||||
|
MatterBin: 1
|
||||||
|
materialRequirements:
|
||||||
|
Glass: 1
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
id: PortableScrubberMachineCircuitBoard
|
id: PortableScrubberMachineCircuitBoard
|
||||||
parent: BaseMachineCircuitboard
|
parent: BaseMachineCircuitboard
|
||||||
|
|||||||
@@ -306,6 +306,8 @@
|
|||||||
producingSound: /Audio/Machines/circuitprinter.ogg
|
producingSound: /Audio/Machines/circuitprinter.ogg
|
||||||
idleState: icon
|
idleState: icon
|
||||||
runningState: building
|
runningState: building
|
||||||
|
staticRecipes:
|
||||||
|
- CondenserMachineCircuitBoard
|
||||||
dynamicRecipes:
|
dynamicRecipes:
|
||||||
- ThermomachineFreezerMachineCircuitBoard
|
- ThermomachineFreezerMachineCircuitBoard
|
||||||
- PortableScrubberMachineCircuitBoard
|
- PortableScrubberMachineCircuitBoard
|
||||||
|
|||||||
@@ -351,3 +351,89 @@
|
|||||||
enabled: true
|
enabled: true
|
||||||
- type: ApcPowerReceiver
|
- type: ApcPowerReceiver
|
||||||
powerDisabled: false
|
powerDisabled: false
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: [ BaseMachinePowered, ConstructibleMachine ]
|
||||||
|
id: BaseGasCondenser
|
||||||
|
name: condenser
|
||||||
|
description: Condenses gases into liquids. Now we just need some plumbing.
|
||||||
|
placement:
|
||||||
|
mode: SnapgridCenter
|
||||||
|
components:
|
||||||
|
- type: Sprite
|
||||||
|
sprite: Structures/Piping/Atmospherics/condenser.rsi
|
||||||
|
snapCardinals: true
|
||||||
|
granularLayersRendering: true
|
||||||
|
layers:
|
||||||
|
- state: off
|
||||||
|
map: [ "enum.PowerDeviceVisualLayers.Powered" ]
|
||||||
|
- state: panel
|
||||||
|
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||||
|
- state: pipe
|
||||||
|
map: [ "enum.PipeVisualLayers.Pipe" ]
|
||||||
|
renderingStrategy: Default
|
||||||
|
- state: fill-1
|
||||||
|
map: ["enum.SolutionContainerLayers.Fill"]
|
||||||
|
visible: false
|
||||||
|
- state: trans
|
||||||
|
- type: GenericVisualizer
|
||||||
|
visuals:
|
||||||
|
enum.PowerDeviceVisuals.Powered:
|
||||||
|
enum.PowerDeviceVisualLayers.Powered:
|
||||||
|
True: { state: on }
|
||||||
|
False: { state: off }
|
||||||
|
- type: SolutionContainerVisuals
|
||||||
|
maxFillLevels: 7
|
||||||
|
fillBaseName: fill-
|
||||||
|
- type: Appearance
|
||||||
|
- type: PipeColorVisuals
|
||||||
|
- type: Rotatable
|
||||||
|
- type: GasCondenser
|
||||||
|
- type: AtmosPipeColor
|
||||||
|
- type: AtmosDevice
|
||||||
|
- type: ApcPowerReceiver
|
||||||
|
powerLoad: 10000
|
||||||
|
- type: Machine
|
||||||
|
board: CondenserMachineCircuitBoard
|
||||||
|
- type: WiresPanel
|
||||||
|
- type: Wires
|
||||||
|
boardName: wires-board-name-condenser
|
||||||
|
layoutId: Condenser
|
||||||
|
- type: WiresVisuals
|
||||||
|
- type: Destructible
|
||||||
|
thresholds:
|
||||||
|
- trigger:
|
||||||
|
!type:DamageTrigger
|
||||||
|
damage: 200
|
||||||
|
behaviors:
|
||||||
|
- !type:DoActsBehavior
|
||||||
|
acts: [ "Destruction" ]
|
||||||
|
- trigger:
|
||||||
|
!type:DamageTrigger
|
||||||
|
damage: 100
|
||||||
|
behaviors:
|
||||||
|
- !type:DoActsBehavior
|
||||||
|
acts: [ "Destruction" ]
|
||||||
|
- !type:SpillBehavior
|
||||||
|
solution: tank
|
||||||
|
- !type:PlaySoundBehavior
|
||||||
|
sound:
|
||||||
|
path: /Audio/Effects/metalbreak.ogg
|
||||||
|
- type: NodeContainer
|
||||||
|
nodes:
|
||||||
|
pipe:
|
||||||
|
!type:PipeNode
|
||||||
|
nodeGroupID: Pipe
|
||||||
|
pipeDirection: South
|
||||||
|
- type: Transform
|
||||||
|
noRot: false
|
||||||
|
- type: SolutionContainerManager
|
||||||
|
solutions:
|
||||||
|
tank:
|
||||||
|
maxVol: 400
|
||||||
|
canMix: true
|
||||||
|
- type: DrainableSolution
|
||||||
|
solution: tank
|
||||||
|
- type: ExaminableSolution
|
||||||
|
solution: tank
|
||||||
|
- type: PowerSwitch
|
||||||
|
|||||||
@@ -104,6 +104,14 @@
|
|||||||
Glass: 900
|
Glass: 900
|
||||||
Gold: 50
|
Gold: 50
|
||||||
|
|
||||||
|
- type: latheRecipe
|
||||||
|
id: CondenserMachineCircuitBoard
|
||||||
|
result: CondenserMachineCircuitBoard
|
||||||
|
completetime: 4
|
||||||
|
materials:
|
||||||
|
Steel: 100
|
||||||
|
Glass: 900
|
||||||
|
|
||||||
- type: latheRecipe
|
- type: latheRecipe
|
||||||
id: PortableScrubberMachineCircuitBoard
|
id: PortableScrubberMachineCircuitBoard
|
||||||
result: PortableScrubberMachineCircuitBoard
|
result: PortableScrubberMachineCircuitBoard
|
||||||
|
|||||||
|
After Width: | Height: | Size: 149 B |
|
After Width: | Height: | Size: 168 B |
|
After Width: | Height: | Size: 164 B |
|
After Width: | Height: | Size: 180 B |
|
After Width: | Height: | Size: 184 B |
|
After Width: | Height: | Size: 185 B |
|
After Width: | Height: | Size: 187 B |
@@ -0,0 +1,60 @@
|
|||||||
|
{
|
||||||
|
"version":1,
|
||||||
|
"size":
|
||||||
|
{
|
||||||
|
"x":32,
|
||||||
|
"y":32
|
||||||
|
},
|
||||||
|
"copyright":"Created by EmoGarbage404 (github) for Space Station 14.",
|
||||||
|
"license":"CC0-1.0",
|
||||||
|
"states":
|
||||||
|
[
|
||||||
|
{
|
||||||
|
"name":"off"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"on",
|
||||||
|
"delays":
|
||||||
|
[
|
||||||
|
[
|
||||||
|
0.05,
|
||||||
|
0.05,
|
||||||
|
0.05,
|
||||||
|
0.05,
|
||||||
|
0.05
|
||||||
|
]
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"panel"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"trans"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"pipe",
|
||||||
|
"directions":4
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"fill-1"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"fill-2"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"fill-3"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"fill-4"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"fill-5"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"fill-6"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name":"fill-7"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
After Width: | Height: | Size: 687 B |
|
After Width: | Height: | Size: 1.1 KiB |
|
After Width: | Height: | Size: 272 B |
|
After Width: | Height: | Size: 344 B |
|
After Width: | Height: | Size: 346 B |