diff --git a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs
index 5ef784b1e9..677ace3e12 100644
--- a/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs
+++ b/Content.Server/Atmos/EntitySystems/AtmosphereSystem.Gases.cs
@@ -108,6 +108,52 @@ namespace Content.Server.Atmos.EntitySystems
NumericsHelpers.Add(receiver.Moles, giver.Moles);
}
+ ///
+ /// Divides a source gas mixture into several recipient mixtures, scaled by their relative volumes. Does not
+ /// modify the source gas mixture. Used for pipe network splitting. Note that the total destination volume
+ /// may be larger or smaller than the source mixture.
+ ///
+ public void DivideInto(GasMixture source, List receivers)
+ {
+ var totalVolume = 0f;
+ foreach (var receiver in receivers)
+ {
+ if (!receiver.Immutable)
+ totalVolume += receiver.Volume;
+ }
+
+ float? sourceHeatCapacity = null;
+ var buffer = new float[Atmospherics.AdjustedNumberOfGases];
+
+ foreach (var receiver in receivers)
+ {
+ if (receiver.Immutable)
+ continue;
+
+ var fraction = receiver.Volume / totalVolume;
+
+ // Set temperature, if necessary.
+ if (MathF.Abs(receiver.Temperature - source.Temperature) > Atmospherics.MinimumTemperatureDeltaToConsider)
+ {
+ // Often this divides a pipe net into new and completely empty pipe nets
+ if (receiver.TotalMoles == 0)
+ receiver.Temperature = source.Temperature;
+ else
+ {
+ sourceHeatCapacity ??= GetHeatCapacity(source);
+ var receiverHeatCapacity = GetHeatCapacity(receiver);
+ var combinedHeatCapacity = receiverHeatCapacity + sourceHeatCapacity.Value * fraction;
+ if (combinedHeatCapacity > 0f)
+ receiver.Temperature = (GetThermalEnergy(source, sourceHeatCapacity.Value * fraction) + GetThermalEnergy(receiver, receiverHeatCapacity)) / combinedHeatCapacity;
+ }
+ }
+
+ // transfer moles
+ NumericsHelpers.Multiply(source.Moles, fraction, buffer);
+ NumericsHelpers.Add(receiver.Moles, buffer);
+ }
+ }
+
///
/// Shares gas between two gas mixtures. Part of LINDA.
///
diff --git a/Content.Server/NodeContainer/NodeGroups/PipeNet.cs b/Content.Server/NodeContainer/NodeGroups/PipeNet.cs
index ce0c9b5463..04b0e97754 100644
--- a/Content.Server/NodeContainer/NodeGroups/PipeNet.cs
+++ b/Content.Server/NodeContainer/NodeGroups/PipeNet.cs
@@ -21,8 +21,6 @@ namespace Content.Server.NodeContainer.NodeGroups
{
[ViewVariables] public GasMixture Air { get; set; } = new() {Temperature = Atmospherics.T20C};
- [ViewVariables] private readonly List _pipes = new();
-
[ViewVariables] private AtmosphereSystem? _atmosphereSystem;
public GridId Grid => GridId;
@@ -47,7 +45,6 @@ namespace Content.Server.NodeContainer.NodeGroups
foreach (var node in groupNodes)
{
var pipeNode = (PipeNode) node;
- _pipes.Add(pipeNode);
Air.Volume += pipeNode.Volume;
}
}
@@ -56,32 +53,27 @@ namespace Content.Server.NodeContainer.NodeGroups
{
base.RemoveNode(node);
- var pipeNode = (PipeNode) node;
- Air.Volume -= pipeNode.Volume;
- // TODO: Bad O(n^2)
- _pipes.Remove(pipeNode);
+ // if the node is simply being removed into a separate group, we do nothing, as gas redistribution will be
+ // handled by AfterRemake(). But if it is being deleted, we actually want to remove the gas stored in this node.
+ if (!node.Deleting || node is not PipeNode pipe)
+ return;
+
+ Air.Multiply(1f - pipe.Volume / Air.Volume);
+ Air.Volume -= pipe.Volume;
}
public override void AfterRemake(IEnumerable> newGroups)
{
RemoveFromGridAtmos();
- var buffer = new GasMixture(Air.Volume) {Temperature = Air.Temperature};
- var atmosphereSystem = EntitySystem.Get();
-
+ var newAir = new List(newGroups.Count());
foreach (var newGroup in newGroups)
{
- if (newGroup.Key is not IPipeNet newPipeNet)
- continue;
-
- var newAir = newPipeNet.Air;
- var newVolume = newGroup.Cast().Sum(n => n.Volume);
-
- buffer.Clear();
- atmosphereSystem.Merge(buffer, Air);
- buffer.Multiply(MathF.Min(newVolume / Air.Volume, 1f));
- atmosphereSystem.Merge(newAir, buffer);
+ if (newGroup.Key is IPipeNet newPipeNet)
+ newAir.Add(newPipeNet.Air);
}
+
+ _atmosphereSystem!.DivideInto(Air, newAir);
}
private void RemoveFromGridAtmos()
diff --git a/Content.Server/Rotatable/RotatableSystem.cs b/Content.Server/Rotatable/RotatableSystem.cs
index 3b723842e5..2db571a27b 100644
--- a/Content.Server/Rotatable/RotatableSystem.cs
+++ b/Content.Server/Rotatable/RotatableSystem.cs
@@ -30,7 +30,9 @@ namespace Content.Server.Rotatable
private void AddRotateVerbs(EntityUid uid, RotatableComponent component, GetVerbsEvent args)
{
- if (!args.CanAccess || !args.CanInteract)
+ if (!args.CanAccess
+ || !args.CanInteract
+ || Transform(uid).NoLocalRotation) // Good ol prototype inheritance, eh?
return;
// Check if the object is anchored, and whether we are still allowed to rotate it.