Material generators from Afterlight (#18387)

This commit is contained in:
Nemanja
2023-07-31 14:42:38 -04:00
committed by GitHub
parent b9af7d3668
commit 2d08f02d23
34 changed files with 639 additions and 29 deletions

View File

@@ -27,4 +27,10 @@ public sealed class UpgradePowerSupplierComponent : Component
/// </summary>
[DataField("scaling", required: true), ViewVariables(VVAccess.ReadWrite)]
public MachineUpgradeScalingType Scaling;
/// <summary>
/// The current value that the power supply is being scaled by,
/// </summary>
[DataField("actualScalar"), ViewVariables(VVAccess.ReadWrite)]
public float ActualScalar = 1f;
}

View File

@@ -76,8 +76,12 @@ internal sealed class PowerMonitoringConsoleSystem : EntitySystem
}
foreach (PowerSupplierComponent pcc in netQ.Suppliers)
{
sources.Add(LoadOrSource(pcc, pcc.MaxSupply, false));
totalSources += pcc.MaxSupply;
var supply = pcc.Enabled
? pcc.MaxSupply
: 0f;
sources.Add(LoadOrSource(pcc, supply, false));
totalSources += supply;
}
foreach (BatteryDischargerComponent pcc in netQ.Dischargers)
{

View File

@@ -43,7 +43,7 @@ public sealed class UpgradePowerSystem : EntitySystem
load *= MathF.Pow(component.PowerDrawMultiplier, rating - 1);
break;
default:
Logger.Error($"invalid power scaling type for {ToPrettyString(uid)}.");
Log.Error($"invalid power scaling type for {ToPrettyString(uid)}.");
load = 0;
break;
}
@@ -82,19 +82,19 @@ public sealed class UpgradePowerSystem : EntitySystem
supply *= MathF.Pow(component.PowerSupplyMultiplier, rating - 1);
break;
default:
Logger.Error($"invalid power scaling type for {ToPrettyString(uid)}.");
Log.Error($"invalid power scaling type for {ToPrettyString(uid)}.");
supply = component.BaseSupplyRate;
break;
}
component.ActualScalar = supply / component.BaseSupplyRate;
if (TryComp<PowerSupplierComponent>(uid, out var powa))
powa.MaxSupply = supply;
}
private void OnSupplierUpgradeExamine(EntityUid uid, UpgradePowerSupplierComponent component, UpgradeExamineEvent args)
{
// UpgradePowerSupplierComponent.PowerSupplyMultiplier is not the actual multiplier, so we have to do this.
if (TryComp<PowerSupplierComponent>(uid, out var powa))
args.AddPercentageUpgrade("upgrade-power-supply", powa.MaxSupply / component.BaseSupplyRate);
args.AddPercentageUpgrade("upgrade-power-supply", component.ActualScalar);
}
}

View File

@@ -0,0 +1,24 @@
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Whitelist;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Dictionary;
namespace Content.Server.Power.Generator;
/// <summary>
/// This is used for chemical fuel input into generators.
/// </summary>
[RegisterComponent, Access(typeof(GeneratorSystem))]
public sealed class ChemicalFuelGeneratorAdapterComponent : Component
{
/// <summary>
/// The acceptable list of input entities.
/// </summary>
[DataField("whitelist")]
public EntityWhitelist? Whitelist;
/// <summary>
/// The conversion factor for different chems you can put in.
/// </summary>
[DataField("chemConversionFactors", required: true, customTypeSerializer:typeof(PrototypeIdDictionarySerializer<float, ReagentPrototype>))]
public Dictionary<string, float> ChemConversionFactors = default!;
}

View File

@@ -0,0 +1,14 @@
namespace Content.Server.Power.Generator;
/// <summary>
/// This is used for stuff that can directly be shoved into a generator.
/// </summary>
[RegisterComponent, Access(typeof(GeneratorSystem))]
public sealed class ChemicalFuelGeneratorDirectSourceComponent : Component
{
/// <summary>
/// The solution to pull fuel material from.
/// </summary>
[DataField("solution", required: true), ViewVariables(VVAccess.ReadWrite)]
public string Solution = default!;
}

View File

@@ -0,0 +1,47 @@
using Content.Shared.Atmos;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Server.Power.Generator;
/// <summary>
/// This is used for providing gas power to machinery.
/// </summary>
[RegisterComponent, Access(typeof(GasPowerReceiverSystem))]
public sealed class GasPowerReceiverComponent : Component
{
/// <summary>
/// Past this temperature we assume we're in reaction mass mode and not magic mode.
/// </summary>
[DataField("maxTemperature"), ViewVariables(VVAccess.ReadWrite)]
public float MaxTemperature = 1000.0f;
/// <summary>
/// The gas that fuels this generator
/// </summary>
[DataField("targetGas", required: true), ViewVariables(VVAccess.ReadWrite)]
public Gas TargetGas;
/// <summary>
/// The amount of gas consumed for operation in magic mode.
/// </summary>
[DataField("molesConsumedSec"), ViewVariables(VVAccess.ReadWrite)]
public float MolesConsumedSec = 1.55975875833f / 4;
/// <summary>
/// The amount of kPA "consumed" for operation in pressure mode.
/// </summary>
[DataField("pressureConsumedSec"), ViewVariables(VVAccess.ReadWrite)]
public float PressureConsumedSec = 100f;
/// <summary>
/// Whether the consumed gas should then be ejected directly into the atmosphere.
/// </summary>
[DataField("offVentGas"), ViewVariables(VVAccess.ReadWrite)]
public bool OffVentGas;
[DataField("lastProcess", customTypeSerializer: typeof(TimeOffsetSerializer)), ViewVariables(VVAccess.ReadWrite)]
public TimeSpan LastProcess = TimeSpan.Zero;
[DataField("powered"), ViewVariables(VVAccess.ReadWrite)]
public bool Powered = true;
}

View File

@@ -0,0 +1,92 @@
using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Piping.Components;
using Content.Server.NodeContainer;
using Content.Server.NodeContainer.EntitySystems;
using Content.Server.NodeContainer.Nodes;
using Content.Server.Power.Components;
using Content.Shared.Atmos;
using Robust.Shared.Timing;
namespace Content.Server.Power.Generator;
/// <summary>
/// This handles gas power receivers, allowing devices to accept power in the form of a gas.
/// </summary>
public sealed class GasPowerReceiverSystem : EntitySystem
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
[Dependency] private readonly NodeContainerSystem _nodeContainer = default!;
/// <inheritdoc/>
public override void Initialize()
{
SubscribeLocalEvent<GasPowerReceiverComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<GasPowerReceiverComponent, AtmosDeviceUpdateEvent>(OnDeviceUpdated);
}
private void OnMapInit(EntityUid uid, GasPowerReceiverComponent component, MapInitEvent args)
{
component.LastProcess = _gameTiming.CurTime;
}
private void OnDeviceUpdated(EntityUid uid, GasPowerReceiverComponent component, AtmosDeviceUpdateEvent args)
{
var timeDelta = (float)(_gameTiming.CurTime - component.LastProcess).TotalSeconds;
component.LastProcess = _gameTiming.CurTime;
if (!HasComp<AtmosDeviceComponent>(uid)
|| !TryComp<NodeContainerComponent>(uid, out var nodeContainer)
|| !_nodeContainer.TryGetNode<PipeNode>(nodeContainer, "pipe", out var pipe))
{
return;
}
// if we're below the max temperature, then we are simply consuming our target gas
if (pipe.Air.Temperature <= component.MaxTemperature)
{
// we have enough gas, so we consume it and are powered
if (pipe.Air.Moles[(int) component.TargetGas] > component.MolesConsumedSec * timeDelta)
{
pipe.Air.AdjustMoles(component.TargetGas, -component.MolesConsumedSec * timeDelta);
SetPowered(uid, component, true);
}
else // we do not have enough gas, so we power off
{
SetPowered(uid, component, false);
}
}
else // we are exceeding the max temp and are now operating in pressure mode
{
var pres = component.PressureConsumedSec * timeDelta;
if (pipe.Air.Pressure >= pres)
{
// remove gas from the pipe
var res = pipe.Air.Remove(pres * 100.0f / (Atmospherics.R * pipe.Air.Temperature));
if (component.OffVentGas)
{
// eject the gas into the atmosphere
var mix = _atmosphereSystem.GetContainingMixture(uid, false, true);
if (mix is not null)
_atmosphereSystem.Merge(res, mix);
}
SetPowered(uid, component, true);
}
else // if we do not have high enough pressure to operate, power off
{
SetPowered(uid, component, false);
}
}
}
private void SetPowered(EntityUid uid, GasPowerReceiverComponent comp, bool state)
{
if (state != comp.Powered)
{
comp.Powered = state;
var ev = new PowerChangedEvent(state, 0);
RaiseLocalEvent(uid, ref ev);
}
}
}

View File

@@ -0,0 +1,123 @@
using Content.Server.Chemistry.Components.SolutionManager;
using Content.Server.Popups;
using Content.Server.Power.Components;
using Content.Shared.Chemistry.Components;
using Content.Shared.Interaction;
using Content.Shared.Materials;
using Content.Shared.Power.Generator;
using Content.Shared.Stacks;
using Robust.Server.GameObjects;
namespace Content.Server.Power.Generator;
/// <inheritdoc/>
public sealed class GeneratorSystem : SharedGeneratorSystem
{
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly SharedStackSystem _stack = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
private EntityQuery<UpgradePowerSupplierComponent> _upgradeQuery;
public override void Initialize()
{
_upgradeQuery = GetEntityQuery<UpgradePowerSupplierComponent>();
SubscribeLocalEvent<SolidFuelGeneratorAdapterComponent, InteractUsingEvent>(OnSolidFuelAdapterInteractUsing);
SubscribeLocalEvent<ChemicalFuelGeneratorAdapterComponent, InteractUsingEvent>(OnChemicalFuelAdapterInteractUsing);
SubscribeLocalEvent<FuelGeneratorComponent, SetTargetPowerMessage>(OnTargetPowerSet);
}
private void OnChemicalFuelAdapterInteractUsing(EntityUid uid, ChemicalFuelGeneratorAdapterComponent component, InteractUsingEvent args)
{
if (args.Handled)
return;
if (!TryComp<SolutionContainerManagerComponent>(args.Used, out var solutions) ||
!TryComp<FuelGeneratorComponent>(uid, out var generator))
return;
if (!(component.Whitelist?.IsValid(args.Used) ?? true))
return;
if (TryComp<ChemicalFuelGeneratorDirectSourceComponent>(args.Used, out var source))
{
if (!solutions.Solutions.ContainsKey(source.Solution))
{
Log.Error($"Couldn't get solution {source.Solution} on {ToPrettyString(args.Used)}");
return;
}
var solution = solutions.Solutions[source.Solution];
generator.RemainingFuel += ReagentsToFuel(component, solution);
solution.RemoveAllSolution();
QueueDel(args.Used);
}
}
private float ReagentsToFuel(ChemicalFuelGeneratorAdapterComponent component, Solution solution)
{
var total = 0.0f;
foreach (var reagent in solution.Contents)
{
if (!component.ChemConversionFactors.ContainsKey(reagent.ReagentId))
continue;
total += reagent.Quantity.Float() * component.ChemConversionFactors[reagent.ReagentId];
}
return total;
}
private void OnTargetPowerSet(EntityUid uid, FuelGeneratorComponent component, SetTargetPowerMessage args)
{
component.TargetPower = Math.Clamp(args.TargetPower, 0, component.MaxTargetPower / 1000) * 1000;
}
private void OnSolidFuelAdapterInteractUsing(EntityUid uid, SolidFuelGeneratorAdapterComponent component, InteractUsingEvent args)
{
if (args.Handled)
return;
if (!TryComp<PhysicalCompositionComponent>(args.Used, out var mat) ||
!HasComp<MaterialComponent>(args.Used) ||
!TryComp<FuelGeneratorComponent>(uid, out var generator))
return;
if (!mat.MaterialComposition.ContainsKey(component.FuelMaterial))
return;
_popup.PopupEntity(Loc.GetString("generator-insert-material", ("item", args.Used), ("generator", uid)), uid);
generator.RemainingFuel += _stack.GetCount(args.Used) * component.Multiplier;
QueueDel(args.Used);
args.Handled = true;
}
public override void Update(float frameTime)
{
var query = EntityQueryEnumerator<FuelGeneratorComponent, PowerSupplierComponent, TransformComponent>();
while (query.MoveNext(out var uid, out var gen, out var supplier, out var xform))
{
supplier.Enabled = gen.RemainingFuel > 0.0f && xform.Anchored;
var upgradeMultiplier = _upgradeQuery.CompOrNull(uid)?.ActualScalar ?? 1f;
supplier.MaxSupply = gen.TargetPower * upgradeMultiplier;
var eff = 1 / CalcFuelEfficiency(gen.TargetPower, gen.OptimalPower, gen);
gen.RemainingFuel = MathF.Max(gen.RemainingFuel - (gen.OptimalBurnRate * frameTime * eff), 0.0f);
UpdateUi(uid, gen);
}
}
private void UpdateUi(EntityUid uid, FuelGeneratorComponent comp)
{
if (!_uiSystem.IsUiOpen(uid, GeneratorComponentUiKey.Key))
return;
_uiSystem.TrySetUiState(uid, GeneratorComponentUiKey.Key, new SolidFuelGeneratorComponentBuiState(comp));
}
}

View File

@@ -0,0 +1,23 @@
using Content.Shared.Materials;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
namespace Content.Server.Power.Generator;
/// <summary>
/// This is used for allowing you to insert fuel into gens.
/// </summary>
[RegisterComponent, Access(typeof(GeneratorSystem))]
public sealed class SolidFuelGeneratorAdapterComponent : Component
{
/// <summary>
/// The material to accept as fuel.
/// </summary>
[DataField("fuelMaterial", customTypeSerializer: typeof(PrototypeIdSerializer<MaterialPrototype>)), ViewVariables(VVAccess.ReadWrite)]
public string FuelMaterial = "Plasma";
/// <summary>
/// How much fuel that material should count for.
/// </summary>
[DataField("multiplier"), ViewVariables(VVAccess.ReadWrite)]
public float Multiplier = 1.0f;
}