AirFilterSystem.cs 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109
  1. using Content.Server.Atmos.Components;
  2. using Content.Server.Atmos.Piping.Components;
  3. using Content.Shared.Atmos;
  4. using Robust.Shared.Map;
  5. using System.Diagnostics.CodeAnalysis;
  6. namespace Content.Server.Atmos.EntitySystems;
  7. /// <summary>
  8. /// Handles gas filtering and intake for <see cref="AirIntakeComponent"/> and <see cref="AirFilterComponent"/>.
  9. /// </summary>
  10. public sealed class AirFilterSystem : EntitySystem
  11. {
  12. [Dependency] private readonly AtmosphereSystem _atmosphere = default!;
  13. [Dependency] private readonly SharedTransformSystem _transform = default!;
  14. public override void Initialize()
  15. {
  16. base.Initialize();
  17. SubscribeLocalEvent<AirIntakeComponent, AtmosDeviceUpdateEvent>(OnIntakeUpdate);
  18. SubscribeLocalEvent<AirFilterComponent, AtmosDeviceUpdateEvent>(OnFilterUpdate);
  19. }
  20. private void OnIntakeUpdate(EntityUid uid, AirIntakeComponent intake, ref AtmosDeviceUpdateEvent args)
  21. {
  22. if (!GetAir(uid, out var air))
  23. return;
  24. // if the volume is filled there is nothing to do
  25. if (air.Pressure >= intake.Pressure)
  26. return;
  27. var environment = _atmosphere.GetContainingMixture(uid, args.Grid, args.Map, true, true);
  28. // nothing to intake from
  29. if (environment == null)
  30. return;
  31. // absolute maximum pressure change
  32. var pressureDelta = args.dt * intake.TargetPressureChange;
  33. pressureDelta = MathF.Min(pressureDelta, intake.Pressure - air.Pressure);
  34. if (pressureDelta <= 0)
  35. return;
  36. // how many moles to transfer to change internal pressure by pressureDelta
  37. // ignores temperature difference because lazy
  38. var transferMoles = pressureDelta * air.Volume / (environment.Temperature * Atmospherics.R);
  39. _atmosphere.Merge(air, environment.Remove(transferMoles));
  40. }
  41. private void OnFilterUpdate(EntityUid uid, AirFilterComponent filter, ref AtmosDeviceUpdateEvent args)
  42. {
  43. if (!GetAir(uid, out var air))
  44. return;
  45. var ratio = MathF.Min(1f, args.dt * filter.TransferRate * _atmosphere.PumpSpeedup());
  46. var removed = air.RemoveRatio(ratio);
  47. // nothing left to remove from the volume
  48. if (MathHelper.CloseToPercent(removed.TotalMoles, 0f))
  49. return;
  50. // when oxygen gets too low start removing overflow gases (nitrogen) to maintain oxygen ratio
  51. var oxygen = air.GetMoles(filter.Oxygen) / air.TotalMoles;
  52. var gases = oxygen >= filter.TargetOxygen ? filter.Gases : filter.OverflowGases;
  53. GasMixture? destination = null;
  54. if (args.Grid is {} grid)
  55. {
  56. var position = _transform.GetGridTilePositionOrDefault(uid);
  57. destination = _atmosphere.GetTileMixture(grid, args.Map, position, true);
  58. }
  59. if (destination != null)
  60. {
  61. _atmosphere.ScrubInto(removed, destination, gases);
  62. }
  63. else
  64. {
  65. // filtering into space/planet so just discard them
  66. foreach (var gas in gases)
  67. {
  68. removed.SetMoles(gas, 0f);
  69. }
  70. }
  71. _atmosphere.Merge(air, removed);
  72. }
  73. /// <summary>
  74. /// Uses <see cref="GetFilterAirEvent"/> to get an internal volume of air on an entity.
  75. /// Used for both filter and intake.
  76. /// </summary>
  77. public bool GetAir(EntityUid uid, [NotNullWhen(true)] out GasMixture? air)
  78. {
  79. air = null;
  80. var ev = new GetFilterAirEvent();
  81. RaiseLocalEvent(uid, ref ev);
  82. air = ev.Air;
  83. return air != null;
  84. }
  85. }
  86. /// <summary>
  87. /// Get a reference to an entity's air volume to filter.
  88. /// Do not create a new mixture as this will be modified when filtering and intaking air.
  89. /// </summary>
  90. [ByRefEvent]
  91. public record struct GetFilterAirEvent(GasMixture? Air = null);