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.