diff --git a/Content.Server/Mech/Components/MechAirFilterComponent.cs b/Content.Server/Mech/Components/MechAirFilterComponent.cs
new file mode 100644
index 0000000000..55dad29deb
--- /dev/null
+++ b/Content.Server/Mech/Components/MechAirFilterComponent.cs
@@ -0,0 +1,22 @@
+using Content.Shared.Atmos;
+
+namespace Content.Server.Mech.Components;
+
+///
+/// This is basically a reverse scrubber for MechAir
+///
+[RegisterComponent]
+public sealed class MechAirFilterComponent : Component
+{
+ ///
+ /// Gases that will be filtered out of internal air
+ ///
+ [DataField("gases", required: true)]
+ public HashSet Gases = new();
+
+ ///
+ /// Target volume to transfer every second.
+ ///
+ [DataField("transferRate")]
+ public float TransferRate = MechAirComponent.GasMixVolume * 0.1f;
+}
diff --git a/Content.Server/Mech/Components/MechAirIntakeComponent.cs b/Content.Server/Mech/Components/MechAirIntakeComponent.cs
new file mode 100644
index 0000000000..5b35a5197f
--- /dev/null
+++ b/Content.Server/Mech/Components/MechAirIntakeComponent.cs
@@ -0,0 +1,28 @@
+using Content.Shared.Atmos;
+
+namespace Content.Server.Mech.Components;
+
+///
+/// This is basically a siphon vent for mech but not using pump vent component because MechAir bad
+///
+[RegisterComponent]
+public sealed class MechAirIntakeComponent : Component
+{
+ ///
+ /// Target pressure change for a single atmos tick
+ ///
+ [DataField("targetPressureChange")]
+ public float TargetPressureChange = 5f;
+
+ ///
+ /// How strong the intake pump is, it will be able to replenish air from lower pressure areas.
+ ///
+ [DataField("pumpPower")]
+ public float PumpPower = 2f;
+
+ ///
+ /// Pressure to intake gases up to, maintains MechAir pressure.
+ ///
+ [DataField("pressure")]
+ public float Pressure = Atmospherics.OneAtmosphere;
+}
diff --git a/Content.Server/Mech/Systems/MechSystem.Filtering.cs b/Content.Server/Mech/Systems/MechSystem.Filtering.cs
new file mode 100644
index 0000000000..930a2afbd1
--- /dev/null
+++ b/Content.Server/Mech/Systems/MechSystem.Filtering.cs
@@ -0,0 +1,81 @@
+using Content.Server.Atmos;
+using Content.Server.Atmos.Piping.Components;
+using Content.Server.Mech.Components;
+using Content.Shared.Atmos;
+using Content.Shared.Mech.Components;
+using Robust.Shared.GameObjects;
+
+namespace Content.Server.Mech.Systems;
+
+// TODO: this could be reused for gasmask or something if MechAir wasnt a thing
+public sealed partial class MechSystem
+{
+ [Dependency] private readonly SharedTransformSystem _transform = default!;
+
+ private void InitializeFiltering()
+ {
+ SubscribeLocalEvent(OnIntakeUpdate);
+ SubscribeLocalEvent(OnFilterUpdate);
+ }
+
+ private void OnIntakeUpdate(EntityUid uid, MechAirIntakeComponent intake, AtmosDeviceUpdateEvent args)
+ {
+ if (!TryComp(uid, out var mech) || !mech.Airtight || !TryComp(uid, out var mechAir))
+ return;
+
+ // if the mech is filled there is nothing to do
+ if (mechAir.Air.Pressure >= intake.Pressure)
+ return;
+
+ var environment = _atmosphere.GetContainingMixture(uid, true, true);
+ // nothing to intake from
+ if (environment == null)
+ return;
+
+ // absolute maximum pressure change
+ var pressureDelta = args.dt * intake.TargetPressureChange;
+ pressureDelta = MathF.Min(pressureDelta, intake.Pressure - mechAir.Air.Pressure);
+ if (pressureDelta <= 0)
+ return;
+
+ // how many moles to transfer to change internal pressure by pressureDelta
+ // ignores temperature difference because lazy
+ var transferMoles = pressureDelta * mechAir.Air.Volume / (environment.Temperature * Atmospherics.R);
+ _atmosphere.Merge(mechAir.Air, environment.Remove(transferMoles));
+ }
+
+ private void OnFilterUpdate(EntityUid uid, MechAirFilterComponent filter, AtmosDeviceUpdateEvent args)
+ {
+ if (!TryComp(uid, out var mech) || !mech.Airtight || !TryComp(uid, out var mechAir))
+ return;
+
+ var ratio = MathF.Min(1f, args.dt * filter.TransferRate / mechAir.Air.Volume);
+ var removed = mechAir.Air.RemoveRatio(ratio);
+ // nothing left to remove from the mech
+ if (MathHelper.CloseToPercent(removed.TotalMoles, 0f))
+ return;
+
+
+ var coordinates = Transform(uid).MapPosition;
+ GasMixture? destination = null;
+ if (_map.TryFindGridAt(coordinates, out _, out var grid))
+ {
+ var tile = grid.GetTileRef(coordinates);
+ destination = _atmosphere.GetTileMixture(tile.GridUid, null, tile.GridIndices, true);
+ }
+
+ if (destination != null)
+ {
+ _atmosphere.ScrubInto(removed, destination, filter.Gases);
+ }
+ else
+ {
+ // filtering into space/planet so just discard them
+ foreach (var gas in filter.Gases)
+ {
+ removed.SetMoles(gas, 0f);
+ }
+ }
+ _atmosphere.Merge(mechAir.Air, removed);
+ }
+}
diff --git a/Content.Server/Mech/Systems/MechSystem.cs b/Content.Server/Mech/Systems/MechSystem.cs
index 37c9db2b0a..453b471df6 100644
--- a/Content.Server/Mech/Systems/MechSystem.cs
+++ b/Content.Server/Mech/Systems/MechSystem.cs
@@ -24,17 +24,17 @@ using Robust.Shared.Map;
namespace Content.Server.Mech.Systems;
///
-public sealed class MechSystem : SharedMechSystem
+public sealed partial class MechSystem : SharedMechSystem
{
+ [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
+ [Dependency] private readonly BatterySystem _battery = default!;
[Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
- [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly IMapManager _map = default!;
- [Dependency] private readonly UserInterfaceSystem _ui = default!;
- [Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
+ [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
- [Dependency] private readonly BatterySystem _batterySystem = default!;
+ [Dependency] private readonly UserInterfaceSystem _ui = default!;
private ISawmill _sawmill = default!;
@@ -45,6 +45,8 @@ public sealed class MechSystem : SharedMechSystem
_sawmill = Logger.GetSawmill("mech");
+ InitializeFiltering();
+
SubscribeLocalEvent(OnInteractUsing);
SubscribeLocalEvent(OnInsertBattery);
SubscribeLocalEvent(OnMapInit);
@@ -127,11 +129,14 @@ public sealed class MechSystem : SharedMechSystem
private void OnMapInit(EntityUid uid, MechComponent component, MapInitEvent args)
{
var xform = Transform(uid);
- foreach (var ent in component.StartingEquipment.Select(equipment => Spawn(equipment, xform.Coordinates)))
+ // TODO: this should use containerfill?
+ foreach (var equipment in component.StartingEquipment)
{
+ var ent = Spawn(equipment, xform.Coordinates);
InsertEquipment(uid, ent, component);
}
+ // TODO: this should just be damage and battery
component.Integrity = component.MaxIntegrity;
component.Energy = component.MaxEnergy;
@@ -305,56 +310,6 @@ public sealed class MechSystem : SharedMechSystem
UserInterfaceSystem.SetUiState(ui, state);
}
- public override bool TryInsert(EntityUid uid, EntityUid? toInsert, MechComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return false;
-
- if (!base.TryInsert(uid, toInsert, component))
- return false;
-
- if (component.Airtight && TryComp(uid, out MechAirComponent? mechAir))
- {
- var coordinates = Transform(uid).MapPosition;
- if (_map.TryFindGridAt(coordinates, out _, out var grid))
- {
- var tile = grid.GetTileRef(coordinates);
-
- if (_atmosphere.GetTileMixture(tile.GridUid, null, tile.GridIndices, true) is { } environment)
- {
- _atmosphere.Merge(mechAir.Air, environment.RemoveVolume(MechAirComponent.GasMixVolume));
- }
- }
- }
- return true;
- }
-
- public override bool TryEject(EntityUid uid, MechComponent? component = null)
- {
- if (!Resolve(uid, ref component))
- return false;
-
- if (!base.TryEject(uid, component))
- return false;
-
- if (component.Airtight && TryComp(uid, out MechAirComponent? mechAir))
- {
- var coordinates = Transform(uid).MapPosition;
- if (_map.TryFindGridAt(coordinates, out _, out var grid))
- {
- var tile = grid.GetTileRef(coordinates);
-
- if (_atmosphere.GetTileMixture(tile.GridUid, null, tile.GridIndices, true) is { } environment)
- {
- _atmosphere.Merge(environment, mechAir.Air);
- mechAir.Air.Clear();
- }
- }
- }
-
- return true;
- }
-
public override void BreakMech(EntityUid uid, MechComponent? component = null)
{
base.BreakMech(uid, component);
@@ -378,7 +333,7 @@ public sealed class MechSystem : SharedMechSystem
if (!TryComp(battery, out var batteryComp))
return false;
- _batterySystem.SetCharge(battery!.Value, batteryComp.CurrentCharge + delta.Float(), batteryComp);
+ _battery.SetCharge(battery!.Value, batteryComp.CurrentCharge + delta.Float(), batteryComp);
if (batteryComp.CurrentCharge != component.Energy) //if there's a discrepency, we have to resync them
{
_sawmill.Debug($"Battery charge was not equal to mech charge. Battery {batteryComp.CurrentCharge}. Mech {component.Energy}");
diff --git a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs
index 86059711bf..4c5326c523 100644
--- a/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs
+++ b/Content.Shared/Mech/EntitySystems/SharedMechSystem.cs
@@ -406,7 +406,7 @@ public abstract class SharedMechSystem : EntitySystem
///
///
/// Whether or not the entity was inserted
- public virtual bool TryInsert(EntityUid uid, EntityUid? toInsert, MechComponent? component = null)
+ public bool TryInsert(EntityUid uid, EntityUid? toInsert, MechComponent? component = null)
{
if (!Resolve(uid, ref component))
return false;
@@ -429,7 +429,7 @@ public abstract class SharedMechSystem : EntitySystem
///
///
/// Whether or not the pilot was ejected.
- public virtual bool TryEject(EntityUid uid, MechComponent? component = null)
+ public bool TryEject(EntityUid uid, MechComponent? component = null)
{
if (!Resolve(uid, ref component))
return false;
diff --git a/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml b/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml
index 06572f3496..725dee61d3 100644
--- a/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml
+++ b/Resources/Prototypes/Entities/Objects/Specific/Mech/mechs.yml
@@ -6,6 +6,22 @@
- type: MobMover
- type: Mech
- type: MechAir
+ - type: MechAirFilter
+ # everything except oxygen and nitrogen
+ gases:
+ - 2
+ - 3
+ - 4
+ - 5
+ - 6
+ - 7
+ - 8
+ #- 9 TODO: fusion
+ - type: MechAirIntake
+ # for intake and filter to work
+ - type: AtmosDevice
+ requireAnchored: false
+ joinSystem: true
- type: DoAfter
- type: Repairable
fuelCost: 25