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