--- /dev/null
+using Content.Shared.Atmos.EntitySystems;
+using JetBrains.Annotations;
+
+namespace Content.Client.Atmos.EntitySystems;
+
+[UsedImplicitly]
+public sealed class GasMinerSystem : SharedGasMinerSystem
+{
+
+}
--- /dev/null
+using System.Diagnostics.CodeAnalysis;
+using Content.Server.Atmos.Piping.Components;
+using Content.Shared.Atmos;
+using Content.Shared.Atmos.Components;
+using Content.Shared.Atmos.EntitySystems;
+using JetBrains.Annotations;
+using Robust.Server.GameObjects;
+
+namespace Content.Server.Atmos.EntitySystems;
+
+[UsedImplicitly]
+public sealed class GasMinerSystem : SharedGasMinerSystem
+{
+ [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
+ [Dependency] private readonly TransformSystem _transformSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+
+ SubscribeLocalEvent<GasMinerComponent, AtmosDeviceUpdateEvent>(OnMinerUpdated);
+ }
+
+ private void OnMinerUpdated(Entity<GasMinerComponent> ent, ref AtmosDeviceUpdateEvent args)
+ {
+ var miner = ent.Comp;
+ var oldState = miner.MinerState;
+ float toSpawn;
+
+ if (!GetValidEnvironment(ent, out var environment) || !Transform(ent).Anchored)
+ {
+ miner.MinerState = GasMinerState.Disabled;
+ }
+ // SpawnAmount is declared in mol/s so to get the amount of gas we hope to mine, we have to multiply this by
+ // how long we have been waiting to spawn it and further cap the number according to the miner's state.
+ else if ((toSpawn = CapSpawnAmount(ent, miner.SpawnAmount * args.dt, environment)) == 0)
+ {
+ miner.MinerState = GasMinerState.Idle;
+ }
+ else
+ {
+ miner.MinerState = GasMinerState.Working;
+
+ // Time to mine some gas.
+ var merger = new GasMixture(1) { Temperature = miner.SpawnTemperature };
+ merger.SetMoles(miner.SpawnGas, toSpawn);
+ _atmosphereSystem.Merge(environment, merger);
+ }
+
+ if (miner.MinerState != oldState)
+ {
+ Dirty(ent);
+ }
+ }
+
+ private bool GetValidEnvironment(Entity<GasMinerComponent> ent, [NotNullWhen(true)] out GasMixture? environment)
+ {
+ var (uid, miner) = ent;
+ var transform = Transform(uid);
+ var position = _transformSystem.GetGridOrMapTilePosition(uid, transform);
+
+ // Treat space as an invalid environment
+ if (_atmosphereSystem.IsTileSpace(transform.GridUid, transform.MapUid, position))
+ {
+ environment = null;
+ return false;
+ }
+
+ environment = _atmosphereSystem.GetContainingMixture((uid, transform), true, true);
+ return environment != null;
+ }
+
+ private float CapSpawnAmount(Entity<GasMinerComponent> ent, float toSpawnTarget, GasMixture environment)
+ {
+ var (uid, miner) = ent;
+
+ // How many moles could we theoretically spawn. Cap by pressure and amount.
+ var allowableMoles = Math.Min(
+ (miner.MaxExternalPressure - environment.Pressure) * environment.Volume / (miner.SpawnTemperature * Atmospherics.R),
+ miner.MaxExternalAmount - environment.TotalMoles);
+
+ var toSpawnReal = Math.Clamp(allowableMoles, 0f, toSpawnTarget);
+
+ if (toSpawnReal < Atmospherics.GasMinMoles) {
+ return 0f;
+ }
+
+ return toSpawnReal;
+ }
+}
+++ /dev/null
-using Content.Shared.Atmos;
-
-namespace Content.Server.Atmos.Piping.Other.Components
-{
- [RegisterComponent]
- public sealed partial class GasMinerComponent : Component
- {
- [ViewVariables(VVAccess.ReadWrite)]
- public bool Enabled { get; set; } = true;
-
- [ViewVariables(VVAccess.ReadOnly)]
- public bool Idle { get; set; } = false;
-
- /// <summary>
- /// If the number of moles in the external environment exceeds this number, no gas will be mined.
- /// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("maxExternalAmount")]
- public float MaxExternalAmount { get; set; } = float.PositiveInfinity;
-
- /// <summary>
- /// If the pressure (in kPA) of the external environment exceeds this number, no gas will be mined.
- /// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("maxExternalPressure")]
- public float MaxExternalPressure { get; set; } = Atmospherics.GasMinerDefaultMaxExternalPressure;
-
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("spawnGas")]
- public Gas? SpawnGas { get; set; } = null;
-
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("spawnTemperature")]
- public float SpawnTemperature { get; set; } = Atmospherics.T20C;
-
- /// <summary>
- /// Number of moles created per second when the miner is working.
- /// </summary>
- [ViewVariables(VVAccess.ReadWrite)]
- [DataField("spawnAmount")]
- public float SpawnAmount { get; set; } = Atmospherics.MolesCellStandard * 20f;
- }
-}
+++ /dev/null
-using System.Diagnostics.CodeAnalysis;
-using Content.Server.Atmos.EntitySystems;
-using Content.Server.Atmos.Piping.Components;
-using Content.Server.Atmos.Piping.Other.Components;
-using Content.Shared.Atmos;
-using JetBrains.Annotations;
-using Robust.Server.GameObjects;
-
-namespace Content.Server.Atmos.Piping.Other.EntitySystems
-{
- [UsedImplicitly]
- public sealed class GasMinerSystem : EntitySystem
- {
- [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
- [Dependency] private readonly TransformSystem _transformSystem = default!;
-
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent<GasMinerComponent, AtmosDeviceUpdateEvent>(OnMinerUpdated);
- }
-
- private void OnMinerUpdated(Entity<GasMinerComponent> ent, ref AtmosDeviceUpdateEvent args)
- {
- var miner = ent.Comp;
-
- if (!GetValidEnvironment(ent, out var environment))
- {
- miner.Idle = true;
- return;
- }
-
- // SpawnAmount is declared in mol/s so to get the amount of gas we hope to mine, we have to multiply this by
- // how long we have been waiting to spawn it and further cap the number according to the miner's state.
- var toSpawn = CapSpawnAmount(ent, miner.SpawnAmount * args.dt, environment);
- miner.Idle = toSpawn == 0;
- if (miner.Idle || !miner.Enabled || !miner.SpawnGas.HasValue)
- return;
-
- // Time to mine some gas.
-
- var merger = new GasMixture(1) { Temperature = miner.SpawnTemperature };
- merger.SetMoles(miner.SpawnGas.Value, toSpawn);
-
- _atmosphereSystem.Merge(environment, merger);
- }
-
- private bool GetValidEnvironment(Entity<GasMinerComponent> ent, [NotNullWhen(true)] out GasMixture? environment)
- {
- var (uid, miner) = ent;
- var transform = Transform(uid);
- var position = _transformSystem.GetGridOrMapTilePosition(uid, transform);
-
- // Treat space as an invalid environment
- if (_atmosphereSystem.IsTileSpace(transform.GridUid, transform.MapUid, position))
- {
- environment = null;
- return false;
- }
-
- environment = _atmosphereSystem.GetContainingMixture((uid, transform), true, true);
- return environment != null;
- }
-
- private float CapSpawnAmount(Entity<GasMinerComponent> ent, float toSpawnTarget, GasMixture environment)
- {
- var (uid, miner) = ent;
-
- // How many moles could we theoretically spawn. Cap by pressure and amount.
- var allowableMoles = Math.Min(
- (miner.MaxExternalPressure - environment.Pressure) * environment.Volume / (miner.SpawnTemperature * Atmospherics.R),
- miner.MaxExternalAmount - environment.TotalMoles);
-
- var toSpawnReal = Math.Clamp(allowableMoles, 0f, toSpawnTarget);
-
- if (toSpawnReal < Atmospherics.GasMinMoles) {
- return 0f;
- }
-
- return toSpawnReal;
- }
- }
-}
--- /dev/null
+using Robust.Shared.Serialization;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Atmos.Components;
+
+[NetworkedComponent]
+[AutoGenerateComponentState]
+[RegisterComponent]
+public sealed partial class GasMinerComponent : Component
+{
+ /// <summary>
+ /// Operational state of the miner.
+ /// </summary>
+ [AutoNetworkedField]
+ [ViewVariables(VVAccess.ReadOnly)]
+ public GasMinerState MinerState = GasMinerState.Disabled;
+
+ /// <summary>
+ /// If the number of moles in the external environment exceeds this number, no gas will be mined.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
+ public float MaxExternalAmount = float.PositiveInfinity;
+
+ /// <summary>
+ /// If the pressure (in kPA) of the external environment exceeds this number, no gas will be mined.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
+ public float MaxExternalPressure = Atmospherics.GasMinerDefaultMaxExternalPressure;
+
+ /// <summary>
+ /// Gas to spawn.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField(required: true)]
+ public Gas SpawnGas;
+
+ /// <summary>
+ /// Temperature in Kelvin.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
+ public float SpawnTemperature = Atmospherics.T20C;
+
+ /// <summary>
+ /// Number of moles created per second when the miner is working.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ [DataField]
+ public float SpawnAmount = Atmospherics.MolesCellStandard * 20f;
+}
+
+[Serializable, NetSerializable]
+public enum GasMinerState : byte
+{
+ Disabled,
+ Idle,
+ Working,
+}
--- /dev/null
+using Content.Shared.Atmos.Components;
+using Content.Shared.Examine;
+using Content.Shared.Temperature;
+
+namespace Content.Shared.Atmos.EntitySystems;
+
+public abstract class SharedGasMinerSystem : EntitySystem
+{
+ [Dependency] private readonly SharedAtmosphereSystem _sharedAtmosphereSystem = default!;
+
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent<GasMinerComponent, ExaminedEvent>(OnExamine);
+ }
+
+ private void OnExamine(Entity<GasMinerComponent> ent, ref ExaminedEvent args)
+ {
+ var component = ent.Comp;
+
+ using (args.PushGroup(nameof(GasMinerComponent)))
+ {
+ args.PushMarkup(Loc.GetString("gas-miner-mines-text",
+ ("gas", Loc.GetString(_sharedAtmosphereSystem.GetGas(component.SpawnGas).Name))));
+
+ args.PushText(Loc.GetString("gas-miner-amount-text",
+ ("moles", $"{component.SpawnAmount:0.#}")));
+
+ args.PushText(Loc.GetString("gas-miner-temperature-text",
+ ("tempK", $"{component.SpawnTemperature:0.#}"),
+ ("tempC", $"{TemperatureHelpers.KelvinToCelsius(component.SpawnTemperature):0.#}")));
+
+ if (component.MaxExternalAmount < float.PositiveInfinity)
+ {
+ args.PushText(Loc.GetString("gas-miner-moles-cutoff-text",
+ ("moles", $"{component.MaxExternalAmount:0.#}")));
+ }
+
+ if (component.MaxExternalPressure < float.PositiveInfinity)
+ {
+ args.PushText(Loc.GetString("gas-miner-pressure-cutoff-text",
+ ("pressure", $"{component.MaxExternalPressure:0.#}")));
+ }
+
+ args.AddMarkup(component.MinerState switch
+ {
+ GasMinerState.Disabled => Loc.GetString("gas-miner-state-disabled-text"),
+ GasMinerState.Idle => Loc.GetString("gas-miner-state-idle-text"),
+ GasMinerState.Working => Loc.GetString("gas-miner-state-working-text"),
+ // C# pattern matching is not exhaustive for enums
+ _ => throw new IndexOutOfRangeException(nameof(component.MinerState)),
+ });
+ }
+ }
+}
--- /dev/null
+gas-miner-mines-text = It mines [color=lightgray]{$gas}[/color] when active.
+
+gas-miner-amount-text = It mines {$moles} moles of gas a second when active.
+gas-miner-temperature-text = Mined gas temp: {$tempK}K ({$tempC}°C).
+
+gas-miner-moles-cutoff-text = Surrounding moles cutoff: {$moles} moles.
+gas-miner-pressure-cutoff-text = Surrounding pressure cutoff: {$pressure} kPA.
+
+gas-miner-state-working-text = The miner is [color=green]active[/color] and mining gas.
+gas-miner-state-idle-text = The miner is [color=yellow]idle[/color] and not mining gas.
+gas-miner-state-disabled-text = The miner is [color=red]disabled[/color] and not mining gas.
\ No newline at end of file