Add verbose (client predicted!) examine text to Gas Miners (#30480)
* add verbose examine text to gas miners so their behaviour can be understood * no need for these to be properties * use an enum instead of two booleans for the miner state * require the gas miner to be anchored in order to not be disabled * xmldoc * pr feedback * file-scope namespace * it's to late to hide my transgressions in a rebase * turns out the normal examine distance is totally fine for this
This commit is contained in:
10
Content.Client/Atmos/EntitySystems/GasMinerSystem.cs
Normal file
10
Content.Client/Atmos/EntitySystems/GasMinerSystem.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using Content.Shared.Atmos.EntitySystems;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
|
namespace Content.Client.Atmos.EntitySystems;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed class GasMinerSystem : SharedGasMinerSystem
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
90
Content.Server/Atmos/EntitySystems/GasMinerSystem.cs
Normal file
90
Content.Server/Atmos/EntitySystems/GasMinerSystem.cs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,43 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
60
Content.Shared/Atmos/Components/GasMinerComponent.cs
Normal file
60
Content.Shared/Atmos/Components/GasMinerComponent.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
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,
|
||||||
|
}
|
||||||
55
Content.Shared/Atmos/EntitySystems/SharedGasMinerSystem.cs
Normal file
55
Content.Shared/Atmos/EntitySystems/SharedGasMinerSystem.cs
Normal file
@@ -0,0 +1,55 @@
|
|||||||
|
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)),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
Resources/Locale/en-US/atmos/gas-miner-component.ftl
Normal file
11
Resources/Locale/en-US/atmos/gas-miner-component.ftl
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
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.
|
||||||
Reference in New Issue
Block a user