Beispiel #1
0
        private void Scrub(float timeDelta, GasVentScrubberComponent scrubber, GasMixture?tile, PipeNode outlet)
        {
            // Cannot scrub if tile is null or air-blocked.
            if (tile == null ||
                outlet.Air.Pressure >= 50 * Atmospherics.OneAtmosphere)    // Cannot scrub if pressure too high.
            {
                return;
            }

            // Take a gas sample.
            var ratio   = MathF.Min(1f, timeDelta * scrubber.TransferRate / tile.Volume);
            var removed = tile.RemoveRatio(ratio);

            // Nothing left to remove from the tile.
            if (MathHelper.CloseToPercent(removed.TotalMoles, 0f))
            {
                return;
            }

            if (scrubber.PumpDirection == ScrubberPumpDirection.Scrubbing)
            {
                _atmosphereSystem.ScrubInto(removed, outlet.Air, scrubber.FilterGases);

                // Remix the gases.
                _atmosphereSystem.Merge(tile, removed);
            }
            else if (scrubber.PumpDirection == ScrubberPumpDirection.Siphoning)
            {
                _atmosphereSystem.Merge(outlet.Air, removed);
            }
        }
Beispiel #2
0
    public void PumpToxins(EntityUid uid, GasMixture to, BloodstreamComponent?blood = null, RespiratorComponent?respiration = null)
    {
        if (!Resolve(uid, ref blood))
        {
            return;
        }

        if (!Resolve(uid, ref respiration, false))
        {
            _atmosSystem.Merge(to, blood.Air);
            blood.Air.Clear();
            return;
        }

        var toxins = _respiratorSystem.Clean(uid, respiration, blood);
        var toOld  = new float[to.Moles.Length];

        Array.Copy(to.Moles, toOld, toOld.Length);

        _atmosSystem.Merge(to, toxins);

        for (var i = 0; i < toOld.Length; i++)
        {
            var newAmount = to.GetMoles(i);
            var oldAmount = toOld[i];
            var delta     = newAmount - oldAmount;

            toxins.AdjustMoles(i, -delta);
        }

        _atmosSystem.Merge(blood.Air, toxins);
    }
        /// <summary>
        /// True if we were able to scrub, false if we were not.
        /// </summary>
        public bool Scrub(float timeDelta, float transferRate, ScrubberPumpDirection mode, HashSet <Gas> filterGases, GasMixture?tile, GasMixture destination)
        {
            // Cannot scrub if tile is null or air-blocked.
            if (tile == null ||
                destination.Pressure >= 50 * Atmospherics.OneAtmosphere)    // Cannot scrub if pressure too high.
            {
                return(false);
            }

            // Take a gas sample.
            var ratio   = MathF.Min(1f, timeDelta * transferRate / tile.Volume);
            var removed = tile.RemoveRatio(ratio);

            // Nothing left to remove from the tile.
            if (MathHelper.CloseToPercent(removed.TotalMoles, 0f))
            {
                return(false);
            }

            if (mode == ScrubberPumpDirection.Scrubbing)
            {
                _atmosphereSystem.ScrubInto(removed, destination, filterGases);

                // Remix the gases.
                _atmosphereSystem.Merge(tile, removed);
            }
            else if (mode == ScrubberPumpDirection.Siphoning)
            {
                _atmosphereSystem.Merge(destination, removed);
            }
            return(true);
        }
Beispiel #4
0
    /// <summary>
    ///     Inhales directly from a given mixture.
    /// </summary>
    public void TakeGasFrom(EntityUid uid, float frameTime, GasMixture from,
                            LungComponent?lung      = null,
                            MechanismComponent?mech = null)
    {
        if (!Resolve(uid, ref lung, ref mech))
        {
            return;
        }

        var ratio = (Atmospherics.BreathVolume / from.Volume) * frameTime;

        _atmosSys.Merge(lung.Air, from.RemoveRatio(ratio));

        // Push to bloodstream
        if (mech.Body == null)
        {
            return;
        }

        if (!EntityManager.TryGetComponent((mech.Body).Owner, out BloodstreamComponent? bloodstream))
        {
            return;
        }

        var to = bloodstream.Air;

        _atmosSys.Merge(to, lung.Air);
        lung.Air.Clear();
    }
Beispiel #5
0
        private void OnFilterUpdated(EntityUid uid, GasFilterComponent filter, AtmosDeviceUpdateEvent args)
        {
            var appearance = EntityManager.GetComponentOrNull <AppearanceComponent>(filter.Owner);

            if (!filter.Enabled ||
                !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) ||
                !EntityManager.TryGetComponent(uid, out AtmosDeviceComponent? device) ||
                !nodeContainer.TryGetNode(filter.InletName, out PipeNode? inletNode) ||
                !nodeContainer.TryGetNode(filter.FilterName, out PipeNode? filterNode) ||
                !nodeContainer.TryGetNode(filter.OutletName, out PipeNode? outletNode) ||
                outletNode.Air.Pressure >= Atmospherics.MaxOutputPressure) // No need to transfer if target is full.
            {
                appearance?.SetData(FilterVisuals.Enabled, false);
                _ambientSoundSystem.SetAmbience(filter.Owner, false);
                return;
            }

            // 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;

            if (transferRatio <= 0)
            {
                appearance?.SetData(FilterVisuals.Enabled, false);
                _ambientSoundSystem.SetAmbience(filter.Owner, false);
                return;
            }

            var removed = inletNode.Air.RemoveRatio(transferRatio);

            if (filter.FilteredGas.HasValue)
            {
                appearance?.SetData(FilterVisuals.Enabled, true);

                var filteredOut = new GasMixture()
                {
                    Temperature = removed.Temperature
                };

                filteredOut.SetMoles(filter.FilteredGas.Value, removed.GetMoles(filter.FilteredGas.Value));
                removed.SetMoles(filter.FilteredGas.Value, 0f);

                var target = filterNode.Air.Pressure < Atmospherics.MaxOutputPressure ? filterNode : inletNode;
                _atmosphereSystem.Merge(target.Air, filteredOut);
                if (filteredOut.Pressure != 0f)
                {
                    _ambientSoundSystem.SetAmbience(filter.Owner, true);
                }
                else
                {
                    _ambientSoundSystem.SetAmbience(filter.Owner, false);
                }
            }

            _atmosphereSystem.Merge(outletNode.Air, removed);
        }
        private void OnVolumePumpUpdated(EntityUid uid, GasVolumePumpComponent pump, AtmosDeviceUpdateEvent args)
        {
            TryComp(uid, out AppearanceComponent? appearance);

            if (!pump.Enabled ||
                !TryComp(uid, out NodeContainerComponent? nodeContainer) ||
                !TryComp(uid, out AtmosDeviceComponent? device) ||
                !nodeContainer.TryGetNode(pump.InletName, out PipeNode? inlet) ||
                !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet))
            {
                appearance?.SetData(PumpVisuals.Enabled, false);
                _ambientSoundSystem.SetAmbience(pump.Owner, false);
                _ambientSoundSystem.SetAmbience(pump.Owner, false);
                return;
            }

            var inputStartingPressure  = inlet.Air.Pressure;
            var outputStartingPressure = outlet.Air.Pressure;

            // Pump mechanism won't do anything if the pressure is too high/too low unless you overclock it.
            if ((inputStartingPressure < pump.LowerThreshold) || (outputStartingPressure > pump.HigherThreshold) && !pump.Overclocked)
            {
                return;
            }

            // Overclocked pumps can only force gas a certain amount.
            if ((outputStartingPressure - inputStartingPressure > pump.OverclockThreshold) && pump.Overclocked)
            {
                return;
            }

            appearance?.SetData(PumpVisuals.Enabled, true);
            _ambientSoundSystem.SetAmbience(pump.Owner, true);

            // 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);

            // Some of the gas from the mixture leaks when overclocked.
            if (pump.Overclocked)
            {
                var tile = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent <TransformComponent>(pump.Owner).Coordinates, true);

                if (tile != null)
                {
                    var leaked = removed.RemoveRatio(pump.LeakRatio);
                    _atmosphereSystem.Merge(tile, leaked);
                }
            }

            _atmosphereSystem.Merge(outlet.Air, removed);
        }
        private void OnPumpUpdated(EntityUid uid, GasPressurePumpComponent pump, AtmosDeviceUpdateEvent args)
        {
            if (!pump.Enabled ||
                !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) ||
                !nodeContainer.TryGetNode(pump.InletName, out PipeNode? inlet) ||
                !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet))
            {
                _ambientSoundSystem.SetAmbience(pump.Owner, false);
                return;
            }

            var outputStartingPressure = outlet.Air.Pressure;

            if (outputStartingPressure >= pump.TargetPressure)
            {
                _ambientSoundSystem.SetAmbience(pump.Owner, false);
                return; // No need to pump gas if target has been reached.
            }

            if (inlet.Air.TotalMoles > 0 && inlet.Air.Temperature > 0)
            {
                // We calculate the necessary moles to transfer using our good ol' friend PV=nRT.
                var pressureDelta = pump.TargetPressure - outputStartingPressure;
                var transferMoles = (pressureDelta * outlet.Air.Volume) / (inlet.Air.Temperature * Atmospherics.R);

                var removed = inlet.Air.Remove(transferMoles);
                _atmosphereSystem.Merge(outlet.Air, removed);
                _ambientSoundSystem.SetAmbience(pump.Owner, removed.TotalMoles > 0f);
            }
        }
        private void OnPumpUpdated(EntityUid uid, GasPressurePumpComponent pump, AtmosDeviceUpdateEvent args)
        {
            var appearance = EntityManager.GetComponentOrNull <AppearanceComponent>(pump.Owner);

            if (!pump.Enabled ||
                !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) ||
                !nodeContainer.TryGetNode(pump.InletName, out PipeNode? inlet) ||
                !nodeContainer.TryGetNode(pump.OutletName, out PipeNode? outlet))
            {
                appearance?.SetData(PumpVisuals.Enabled, false);
                return;
            }

            var outputStartingPressure = outlet.Air.Pressure;

            if (MathHelper.CloseToPercent(pump.TargetPressure, outputStartingPressure))
            {
                appearance?.SetData(PumpVisuals.Enabled, false);
                return; // No need to pump gas if target has been reached.
            }

            if (inlet.Air.TotalMoles > 0 && inlet.Air.Temperature > 0)
            {
                appearance?.SetData(PumpVisuals.Enabled, true);

                // We calculate the necessary moles to transfer using our good ol' friend PV=nRT.
                var pressureDelta = pump.TargetPressure - outputStartingPressure;
                var transferMoles = pressureDelta * outlet.Air.Volume / inlet.Air.Temperature * Atmospherics.R;

                var removed = inlet.Air.Remove(transferMoles);
                _atmosphereSystem.Merge(outlet.Air, removed);
            }
        }
        private void OnOutletInjectorUpdated(EntityUid uid, GasOutletInjectorComponent injector, AtmosDeviceUpdateEvent args)
        {
            injector.Injecting = false;

            if (!injector.Enabled)
            {
                return;
            }

            if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
            {
                return;
            }

            if (!nodeContainer.TryGetNode(injector.InletName, out PipeNode? inlet))
            {
                return;
            }

            var environment = _atmosphereSystem.GetTileMixture(injector.Owner.Transform.Coordinates, true);

            if (environment == null)
            {
                return;
            }

            if (inlet.Air.Temperature > 0)
            {
                var transferMoles = inlet.Air.Pressure * injector.VolumeRate / (inlet.Air.Temperature * Atmospherics.R);

                var removed = inlet.Air.Remove(transferMoles);

                _atmosphereSystem.Merge(environment, removed);
            }
        }
Beispiel #10
0
    private void OnActivate(EntityUid uid, GasArtifactComponent component, ArtifactActivatedEvent args)
    {
        if (component.SpawnGas == null || component.SpawnTemperature == null)
        {
            return;
        }

        var transform = Transform(uid);

        var environment = _atmosphereSystem.GetContainingMixture(uid, false, true);

        if (environment == null)
        {
            return;
        }

        if (environment.Pressure >= component.MaxExternalPressure)
        {
            return;
        }

        var merger = new GasMixture(1)
        {
            Temperature = component.SpawnTemperature.Value
        };

        merger.SetMoles(component.SpawnGas.Value, component.SpawnAmount);

        _atmosphereSystem.Merge(environment, merger);
    }
        /// <summary>
        /// Completely dumps the content of the canister into the world.
        /// </summary>
        public void PurgeContents(EntityUid uid, GasCanisterComponent?canister = null, TransformComponent?transform = null)
        {
            if (!Resolve(uid, ref canister, ref transform))
            {
                return;
            }

            var environment = _atmosphereSystem.GetTileMixture(transform.Coordinates, true);

            if (environment is not null)
            {
                _atmosphereSystem.Merge(environment, canister.Air);
            }

            _adminLogSystem.Add(LogType.CanisterPurged, LogImpact.Medium, $"Canister {uid} purged its contents of {canister.Air} into the environment.");
            canister.Air.Clear();
        }
        /// <summary>
        /// Completely dumps the content of the canister into the world.
        /// </summary>
        public void PurgeContents(EntityUid uid, GasCanisterComponent?canister = null, TransformComponent?transform = null)
        {
            if (!Resolve(uid, ref canister, ref transform))
            {
                return;
            }

            var environment = _atmosphereSystem.GetContainingMixture(uid, false, true);

            if (environment is not null)
            {
                _atmosphereSystem.Merge(environment, canister.Air);
            }

            _adminLogger.Add(LogType.CanisterPurged, LogImpact.Medium, $"Canister {ToPrettyString(uid):canister} purged its contents of {canister.Air:gas} into the environment.");
            canister.Air.Clear();
        }
        private void OnBeforeUnanchored(EntityUid uid, AtmosUnsafeUnanchorComponent component, BeforeUnanchoredEvent args)
        {
            if (!component.Enabled || !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodes))
            {
                return;
            }

            if (_atmosphereSystem.GetTileMixture(EntityManager.GetComponent <TransformComponent>(component.Owner).Coordinates, true) is not {
            } environment)
            {
                environment = GasMixture.SpaceGas;
            }

            var lost      = 0f;
            var timesLost = 0;

            foreach (var node in nodes.Nodes.Values)
            {
                if (node is not PipeNode pipe)
                {
                    continue;
                }

                var difference = pipe.Air.Pressure - environment.Pressure;
                lost += difference * environment.Volume / (environment.Temperature * Atmospherics.R);
                timesLost++;
            }

            var sharedLoss = lost / timesLost;
            var buffer     = new GasMixture();

            foreach (var node in nodes.Nodes.Values)
            {
                if (node is not PipeNode pipe)
                {
                    continue;
                }

                _atmosphereSystem.Merge(buffer, pipe.Air.Remove(sharedLoss));
            }

            _atmosphereSystem.Merge(environment, buffer);
        }
Beispiel #14
0
        private void OnPassiveVentUpdated(EntityUid uid, GasPassiveVentComponent vent, AtmosDeviceUpdateEvent args)
        {
            var environment = _atmosphereSystem.GetContainingMixture(uid, true, true);

            if (environment == null)
            {
                return;
            }

            if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
            {
                return;
            }

            if (!nodeContainer.TryGetNode(vent.InletName, out PipeNode? inlet))
            {
                return;
            }

            var environmentPressure = environment.Pressure;
            var pressureDelta       = MathF.Abs(environmentPressure - inlet.Air.Pressure);

            if ((environment.Temperature > 0 || inlet.Air.Temperature > 0) && pressureDelta > 0.5f)
            {
                if (environmentPressure < inlet.Air.Pressure)
                {
                    var airTemperature = environment.Temperature > 0 ? environment.Temperature : inlet.Air.Temperature;
                    var transferMoles  = pressureDelta * environment.Volume / (airTemperature * Atmospherics.R);
                    var removed        = inlet.Air.Remove(transferMoles);
                    _atmosphereSystem.Merge(environment, removed);
                }
                else
                {
                    var airTemperature = inlet.Air.Temperature > 0 ? inlet.Air.Temperature : environment.Temperature;
                    var outputVolume   = inlet.Air.Volume;
                    var transferMoles  = (pressureDelta * outputVolume) / (airTemperature * Atmospherics.R);
                    transferMoles = MathF.Min(transferMoles, environment.TotalMoles * inlet.Air.Volume / environment.Volume);
                    var removed = environment.Remove(transferMoles);
                    _atmosphereSystem.Merge(inlet.Air, removed);
                }
            }
        }
        /// <summary>
        /// When this is destroyed, we dump out all the gas inside.
        /// </summary>
        private void OnDestroyed(EntityUid uid, PortableScrubberComponent component, DestructionEventArgs args)
        {
            var environment = _atmosphereSystem.GetContainingMixture(uid, false, true);

            if (environment != null)
            {
                _atmosphereSystem.Merge(environment, component.Air);
            }

            _adminLogger.Add(LogType.CanisterPurged, LogImpact.Medium, $"Portable scrubber {ToPrettyString(uid):canister} purged its contents of {component.Air:gas} into the environment.");
            component.Air.Clear();
        }
Beispiel #16
0
        public void Inhale(EntityUid uid, SharedBodyComponent?body = null)
        {
            if (!Resolve(uid, ref body, false))
            {
                return;
            }

            var organs = _bodySystem.GetComponentsOnMechanisms <LungComponent>(uid, body);

            // Inhale gas
            var ev = new InhaleLocationEvent();

            RaiseLocalEvent(uid, ev, false);

            if (ev.Gas == null)
            {
                ev.Gas = _atmosSys.GetContainingMixture(uid, false, true);
                if (ev.Gas == null)
                {
                    return;
                }
            }

            var ratio     = (Atmospherics.BreathVolume / ev.Gas.Volume);
            var actualGas = ev.Gas.RemoveRatio(ratio);

            var lungRatio = 1.0f / organs.Count;
            var gas       = organs.Count == 1 ? actualGas : actualGas.RemoveRatio(lungRatio);

            foreach (var(lung, _) in organs)
            {
                // Merge doesn't remove gas from the giver.
                _atmosSys.Merge(lung.Air, gas);
                _lungSystem.GasToReagent(lung.Owner, lung);
            }
        }
Beispiel #17
0
        private void OnOutletInjectorUpdated(EntityUid uid, GasOutletInjectorComponent injector, AtmosDeviceUpdateEvent args)
        {
            if (!injector.Enabled)
            {
                return;
            }

            if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
            {
                return;
            }

            if (!TryComp(uid, out AtmosDeviceComponent? device))
            {
                return;
            }

            if (!nodeContainer.TryGetNode(injector.InletName, out PipeNode? inlet))
            {
                return;
            }

            var environment = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent <TransformComponent>(injector.Owner).Coordinates, true);

            if (environment == null)
            {
                return;
            }

            if (inlet.Air.Temperature < 0)
            {
                return;
            }

            if (environment.Pressure > injector.MaxPressure)
            {
                return;
            }

            var timeDelta = (float)(_gameTiming.CurTime - device.LastProcess).TotalSeconds;

            // TODO adjust ratio so that environment does not go above MaxPressure?
            var ratio   = MathF.Min(1f, timeDelta * injector.TransferRate / inlet.Air.Volume);
            var removed = inlet.Air.RemoveRatio(ratio);

            _atmosphereSystem.Merge(environment, removed);
        }
        private void OnMinerUpdated(EntityUid uid, GasMinerComponent miner, AtmosDeviceUpdateEvent args)
        {
            if (!CheckMinerOperation(miner, out var environment) || !miner.Enabled || !miner.SpawnGas.HasValue || miner.SpawnAmount <= 0f)
            {
                return;
            }

            // Time to mine some gas.

            var merger = new GasMixture(1)
            {
                Temperature = miner.SpawnTemperature
            };

            merger.SetMoles(miner.SpawnGas.Value, miner.SpawnAmount);

            _atmosphereSystem.Merge(environment, merger);
        }
        private void Scrub(AtmosphereSystem atmosphereSystem, GasVentScrubberComponent scrubber, AppearanceComponent?appearance, GasMixture?tile, PipeNode outlet)
        {
            // Cannot scrub if tile is null or air-blocked.
            if (tile == null)
            {
                return;
            }

            // Cannot scrub if pressure too high.
            if (outlet.Air.Pressure >= 50 * Atmospherics.OneAtmosphere)
            {
                return;
            }

            if (scrubber.PumpDirection == ScrubberPumpDirection.Scrubbing)
            {
                appearance?.SetData(ScrubberVisuals.State, scrubber.WideNet ? ScrubberState.WideScrub : ScrubberState.Scrub);
                var transferMoles = MathF.Min(1f, (scrubber.VolumeRate / tile.Volume) * tile.TotalMoles);

                // Take a gas sample.
                var removed = tile.Remove(transferMoles);

                // Nothing left to remove from the tile.
                if (MathHelper.CloseTo(removed.TotalMoles, 0f))
                {
                    return;
                }

                atmosphereSystem.ScrubInto(removed, outlet.Air, scrubber.FilterGases);

                // Remix the gases.
                atmosphereSystem.Merge(tile, removed);
            }
            else if (scrubber.PumpDirection == ScrubberPumpDirection.Siphoning)
            {
                appearance?.SetData(ScrubberVisuals.State, ScrubberState.Siphon);
                var transferMoles = tile.TotalMoles * (scrubber.VolumeRate / tile.Volume);

                var removed = tile.Remove(transferMoles);

                outlet.AssumeAir(removed);
            }
        }
Beispiel #20
0
        private void OnPassiveGateUpdated(EntityUid uid, GasPassiveGateComponent gate, AtmosDeviceUpdateEvent args)
        {
            if (!gate.Enabled)
            {
                return;
            }

            if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
            {
                return;
            }

            if (!nodeContainer.TryGetNode(gate.InletName, out PipeNode? inlet) ||
                !nodeContainer.TryGetNode(gate.OutletName, out PipeNode? outlet))
            {
                return;
            }

            var outputStartingPressure = outlet.Air.Pressure;
            var inputStartingPressure  = inlet.Air.Pressure;

            if (outputStartingPressure >= MathF.Min(gate.TargetPressure, inputStartingPressure - gate.FrictionPressureDifference))
            {
                return; // No need to pump gas, target reached or input pressure too low.
            }
            if (inlet.Air.TotalMoles > 0 && inlet.Air.Temperature > 0)
            {
                // We calculate the necessary moles to transfer using our good ol' friend PV=nRT.
                var pressureDelta = MathF.Min(gate.TargetPressure - outputStartingPressure, (inputStartingPressure - outputStartingPressure) / 2);
                // We can't have a pressure delta that would cause outlet pressure > inlet pressure.

                var transferMoles = pressureDelta * outlet.Air.Volume / (inlet.Air.Temperature * Atmospherics.R);

                // Actually transfer the gas.
                _atmosphereSystem.Merge(outlet.Air, inlet.Air.Remove(transferMoles));
            }
        }
        private void OnGasDualPortVentPumpUpdated(EntityUid uid, GasDualPortVentPumpComponent vent, AtmosDeviceUpdateEvent args)
        {
            var appearance = vent.Owner.GetComponentOrNull <AppearanceComponent>();

            if (vent.Welded)
            {
                appearance?.SetData(VentPumpVisuals.State, VentPumpState.Welded);
                return;
            }

            if (!vent.Enabled ||
                !EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) ||
                !nodeContainer.TryGetNode(vent.InletName, out PipeNode? inlet) ||
                !nodeContainer.TryGetNode(vent.OutletName, out PipeNode? outlet))
            {
                appearance?.SetData(VentPumpVisuals.State, VentPumpState.Off);
                return;
            }

            var environment = _atmosphereSystem.GetTileMixture(vent.Owner.Transform.Coordinates, true);

            // We're in an air-blocked tile... Do nothing.
            if (environment == null)
            {
                appearance?.SetData(VentPumpVisuals.State, VentPumpState.Off);
                return;
            }

            if (vent.PumpDirection == VentPumpDirection.Releasing)
            {
                appearance?.SetData(VentPumpVisuals.State, VentPumpState.Out);
                var pressureDelta = 10000f;

                if ((vent.PressureChecks & DualPortVentPressureBound.ExternalBound) != 0)
                {
                    pressureDelta = MathF.Min(pressureDelta, (vent.ExternalPressureBound - environment.Pressure));
                }

                if ((vent.PressureChecks & DualPortVentPressureBound.InputMinimum) != 0)
                {
                    pressureDelta = MathF.Min(pressureDelta, (inlet.Air.Pressure - vent.InputPressureMin));
                }

                if (pressureDelta > 0 && inlet.Air.Temperature > 0)
                {
                    var transferMoles = pressureDelta * environment.Volume / inlet.Air.Temperature * Atmospherics.R;
                    var removed       = inlet.Air.Remove(transferMoles);
                    _atmosphereSystem.Merge(environment, removed);
                }
            }
            else if (vent.PumpDirection == VentPumpDirection.Siphoning && environment.Pressure > 0f)
            {
                appearance?.SetData(VentPumpVisuals.State, VentPumpState.In);
                var ourMultiplier = outlet.Air.Volume / environment.Temperature * Atmospherics.R;
                var molesDelta    = 10000 * ourMultiplier;

                if ((vent.PressureChecks & DualPortVentPressureBound.ExternalBound) != 0)
                {
                    molesDelta =
                        MathF.Min(molesDelta,
                                  (environment.Pressure - vent.OutputPressureMax) * environment.Volume / (environment.Temperature * Atmospherics.R));
                }

                if ((vent.PressureChecks & DualPortVentPressureBound.InputMinimum) != 0)
                {
                    molesDelta = MathF.Min(molesDelta, (vent.InputPressureMin - outlet.Air.Pressure) * ourMultiplier);
                }

                if (molesDelta > 0)
                {
                    var removed = environment.Remove(molesDelta);

                    _atmosphereSystem.Merge(outlet.Air, removed);
                }
            }
        }
        private void OnMixerUpdated(EntityUid uid, GasMixerComponent mixer, AtmosDeviceUpdateEvent args)
        {
            // TODO ATMOS: Cache total moles since it's expensive.

            if (!mixer.Enabled)
            {
                return;
            }

            if (!EntityManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer))
            {
                return;
            }

            if (!nodeContainer.TryGetNode(mixer.InletOneName, out PipeNode? inletOne) ||
                !nodeContainer.TryGetNode(mixer.InletTwoName, out PipeNode? inletTwo) ||
                !nodeContainer.TryGetNode(mixer.OutletName, out PipeNode? outlet))
            {
                return;
            }

            var outputStartingPressure = outlet.Air.Pressure;

            if (outputStartingPressure >= mixer.TargetPressure)
            {
                return; // Target reached, no need to mix.
            }
            var generalTransfer = (mixer.TargetPressure - outputStartingPressure) * outlet.Air.Volume / Atmospherics.R;

            var transferMolesOne = inletOne.Air.Temperature > 0 ? mixer.InletOneConcentration * generalTransfer / inletOne.Air.Temperature : 0f;
            var transferMolesTwo = inletTwo.Air.Temperature > 0 ? mixer.InletTwoConcentration * generalTransfer / inletTwo.Air.Temperature : 0f;

            if (mixer.InletTwoConcentration <= 0f)
            {
                if (inletOne.Air.Temperature <= 0f)
                {
                    return;
                }

                transferMolesOne = MathF.Min(transferMolesOne, inletOne.Air.TotalMoles);
                transferMolesTwo = 0f;
            }

            else if (mixer.InletOneConcentration <= 0)
            {
                if (inletTwo.Air.Temperature <= 0f)
                {
                    return;
                }

                transferMolesOne = 0f;
                transferMolesTwo = MathF.Min(transferMolesTwo, inletTwo.Air.TotalMoles);
            }
            else
            {
                if (inletOne.Air.Temperature <= 0f || inletTwo.Air.Temperature <= 0f)
                {
                    return;
                }

                if (transferMolesOne <= 0 || transferMolesTwo <= 0)
                {
                    return;
                }

                if (inletOne.Air.TotalMoles < transferMolesOne || inletTwo.Air.TotalMoles < transferMolesTwo)
                {
                    var ratio = MathF.Min(inletOne.Air.TotalMoles / transferMolesOne, inletTwo.Air.TotalMoles / transferMolesTwo);
                    transferMolesOne *= ratio;
                    transferMolesTwo *= ratio;
                }
            }

            // Actually transfer the gas now.

            if (transferMolesOne > 0f)
            {
                var removed = inletOne.Air.Remove(transferMolesOne);
                _atmosphereSystem.Merge(outlet.Air, removed);
            }

            if (transferMolesTwo > 0f)
            {
                var removed = inletTwo.Air.Remove(transferMolesTwo);
                _atmosphereSystem.Merge(outlet.Air, removed);
            }
        }
Beispiel #23
0
        private void OnGasVentPumpUpdated(EntityUid uid, GasVentPumpComponent vent, AtmosDeviceUpdateEvent args)
        {
            //Bingo waz here
            if (vent.Welded)
            {
                return;
            }

            var nodeName = vent.PumpDirection switch
            {
                VentPumpDirection.Releasing => vent.Inlet,
                VentPumpDirection.Siphoning => vent.Outlet,
                _ => throw new ArgumentOutOfRangeException()
            };

            if (!vent.Enabled ||
                !TryComp(uid, out AtmosDeviceComponent? device) ||
                !TryComp(uid, out NodeContainerComponent? nodeContainer) ||
                !nodeContainer.TryGetNode(nodeName, out PipeNode? pipe))
            {
                return;
            }

            var environment = _atmosphereSystem.GetTileMixture(EntityManager.GetComponent <TransformComponent>(vent.Owner).Coordinates, true);

            // We're in an air-blocked tile... Do nothing.
            if (environment == null)
            {
                return;
            }

            var timeDelta     = (_gameTiming.CurTime - device.LastProcess).TotalSeconds;
            var pressureDelta = (float)timeDelta * vent.TargetPressureChange;

            if (vent.PumpDirection == VentPumpDirection.Releasing && pipe.Air.Pressure > 0)
            {
                if (environment.Pressure > vent.MaxPressure)
                {
                    return;
                }

                if ((vent.PressureChecks & VentPressureBound.ExternalBound) != 0)
                {
                    pressureDelta = MathF.Min(pressureDelta, vent.ExternalPressureBound - environment.Pressure);
                }

                if (pressureDelta <= 0)
                {
                    return;
                }

                // how many moles to transfer to change external pressure by pressureDelta
                // (ignoring temperature differences because I am lazy)
                var transferMoles = pressureDelta * environment.Volume / (pipe.Air.Temperature * Atmospherics.R);

                // limit transferMoles so the source doesn't go below its bound.
                if ((vent.PressureChecks & VentPressureBound.InternalBound) != 0)
                {
                    var internalDelta = pipe.Air.Pressure - vent.InternalPressureBound;

                    if (internalDelta <= 0)
                    {
                        return;
                    }

                    var maxTransfer = internalDelta * pipe.Air.Volume / (pipe.Air.Temperature * Atmospherics.R);
                    transferMoles = MathF.Min(transferMoles, maxTransfer);
                }

                _atmosphereSystem.Merge(environment, pipe.Air.Remove(transferMoles));
            }
            else if (vent.PumpDirection == VentPumpDirection.Siphoning && environment.Pressure > 0)
            {
                if (pipe.Air.Pressure > vent.MaxPressure)
                {
                    return;
                }

                if ((vent.PressureChecks & VentPressureBound.InternalBound) != 0)
                {
                    pressureDelta = MathF.Min(pressureDelta, vent.InternalPressureBound - pipe.Air.Pressure);
                }

                if (pressureDelta <= 0)
                {
                    return;
                }

                // how many moles to transfer to change internal pressure by pressureDelta
                // (ignoring temperature differences because I am lazy)
                var transferMoles = pressureDelta * pipe.Air.Volume / (environment.Temperature * Atmospherics.R);

                // limit transferMoles so the source doesn't go below its bound.
                if ((vent.PressureChecks & VentPressureBound.ExternalBound) != 0)
                {
                    var externalDelta = environment.Pressure - vent.ExternalPressureBound;

                    if (externalDelta <= 0)
                    {
                        return;
                    }

                    var maxTransfer = externalDelta * environment.Volume / (environment.Temperature * Atmospherics.R);

                    transferMoles = MathF.Min(transferMoles, maxTransfer);
                }

                _atmosphereSystem.Merge(pipe.Air, environment.Remove(transferMoles));
            }
        }
        private void OnCanisterUpdated(EntityUid uid, GasCanisterComponent canister, AtmosDeviceUpdateEvent args)
        {
            if (!ComponentManager.TryGetComponent(uid, out NodeContainerComponent? nodeContainer) ||
                !ComponentManager.TryGetComponent(uid, out AppearanceComponent? appearance))
            {
                return;
            }

            if (!nodeContainer.TryGetNode(canister.PortName, out PortablePipeNode? portNode))
            {
                return;
            }

            _atmosphereSystem.React(canister.Air, portNode);

            if (portNode.NodeGroup is PipeNet {
                NodeCount: > 1
            } net)
            {
                var buffer = new GasMixture(net.Air.Volume + canister.Air.Volume);

                _atmosphereSystem.Merge(buffer, net.Air);
                _atmosphereSystem.Merge(buffer, canister.Air);

                net.Air.Clear();
                _atmosphereSystem.Merge(net.Air, buffer);
                net.Air.Multiply(net.Air.Volume / buffer.Volume);

                canister.Air.Clear();
                _atmosphereSystem.Merge(canister.Air, buffer);
                canister.Air.Multiply(canister.Air.Volume / buffer.Volume);
            }

            // Release valve is open, release gas.
            if (canister.ReleaseValve)
            {
                if (!ComponentManager.TryGetComponent(uid, out ContainerManagerComponent? containerManager) ||
                    !containerManager.TryGetContainer(canister.ContainerName, out var container))
                {
                    return;
                }

                if (container.ContainedEntities.Count > 0)
                {
                    var gasTank = container.ContainedEntities[0].GetComponent <GasTankComponent>();
                    _atmosphereSystem.ReleaseGasTo(canister.Air, gasTank.Air, canister.ReleasePressure);
                }
                else
                {
                    var environment = _atmosphereSystem.GetTileMixture(canister.Owner.Transform.Coordinates, true);
                    _atmosphereSystem.ReleaseGasTo(canister.Air, environment, canister.ReleasePressure);
                }
            }

            DirtyUI(uid);

            // If last pressure is very close to the current pressure, do nothing.
            if (MathHelper.CloseTo(canister.Air.Pressure, canister.LastPressure))
            {
                return;
            }

            canister.LastPressure = canister.Air.Pressure;

            if (canister.Air.Pressure < 10)
            {
                appearance.SetData(GasCanisterVisuals.PressureState, 0);
            }
            else if (canister.Air.Pressure < Atmospherics.OneAtmosphere)
            {
                appearance.SetData(GasCanisterVisuals.PressureState, 1);
            }
            else if (canister.Air.Pressure < (15 * Atmospherics.OneAtmosphere))
            {
                appearance.SetData(GasCanisterVisuals.PressureState, 2);
            }
            else
            {
                appearance.SetData(GasCanisterVisuals.PressureState, 3);
            }
        }
        public void ExitDisposals(EntityUid uid, DisposalHolderComponent?holder = null, TransformComponent?holderTransform = null)
        {
            if (!Resolve(uid, ref holder, ref holderTransform))
            {
                return;
            }
            if (holder.IsExitingDisposals)
            {
                Logger.ErrorS("c.s.disposal.holder", "Tried exiting disposals twice. This should never happen.");
                return;
            }
            holder.IsExitingDisposals = true;

            // Check for a disposal unit to throw them into and then eject them from it.
            // *This ejection also makes the target not collide with the unit.*
            // *This is on purpose.*

            DisposalUnitComponent?duc = null;

            if (_mapManager.TryGetGrid(holderTransform.GridUid, out var grid))
            {
                foreach (var contentUid in grid.GetLocal(holderTransform.Coordinates))
                {
                    if (EntityManager.TryGetComponent(contentUid, out duc))
                    {
                        break;
                    }
                }
            }

            foreach (var entity in holder.Container.ContainedEntities.ToArray())
            {
                RemComp <BeingDisposedComponent>(entity);

                if (EntityManager.TryGetComponent(entity, out IPhysBody? physics))
                {
                    physics.CanCollide = true;
                }

                var meta = MetaData(entity);
                holder.Container.ForceRemove(entity, EntityManager, meta);

                var xform = Transform(entity);
                if (xform.ParentUid != uid)
                {
                    continue;
                }

                if (duc != null)
                {
                    duc.Container.Insert(entity, EntityManager, xform, meta: meta);
                }
                else
                {
                    xform.AttachParentToContainerOrGrid(EntityManager);
                }
            }

            if (duc != null)
            {
                _disposalUnitSystem.TryEjectContents(duc);
            }

            if (_atmosphereSystem.GetTileMixture(holderTransform.Coordinates, true) is {} environment)
            {
                _atmosphereSystem.Merge(environment, holder.Air);
                holder.Air.Clear();
            }

            EntityManager.DeleteEntity(uid);
        }