Material generators from Afterlight (#18387)
This commit is contained in:
parent
b9af7d3668
commit
2d08f02d23
|
|
@ -0,0 +1,26 @@
|
||||||
|
<controls:FancyWindow xmlns="https://spacestation14.io"
|
||||||
|
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
|
||||||
|
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
|
MinSize="350 130"
|
||||||
|
SetSize="360 180"
|
||||||
|
Title="{Loc 'generator-ui-title'}">
|
||||||
|
<BoxContainer Margin="4 0" Orientation="Horizontal">
|
||||||
|
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="2" VerticalAlignment="Center" Margin="5">
|
||||||
|
<GridContainer Margin="2 0 0 0" Columns="2" HorizontalExpand="True">
|
||||||
|
<!-- Power -->
|
||||||
|
<Label Text="{Loc 'generator-ui-target-power-label'}"/>
|
||||||
|
<SpinBox Name="TargetPower" HorizontalExpand="True"/>
|
||||||
|
<Label Text="{Loc 'generator-ui-efficiency-label'}"/>
|
||||||
|
<Label Name="Efficiency" Text="???%" HorizontalExpand="True"/>
|
||||||
|
<Label Text="{Loc 'generator-ui-fuel-use-label'}"/>
|
||||||
|
<ProgressBar Name="FuelFraction" MinValue="0" MaxValue="1" HorizontalExpand="True"/>
|
||||||
|
<Label Text="{Loc 'generator-ui-fuel-left-label'}"/>
|
||||||
|
<Label Name="FuelLeft" Text="0" HorizontalExpand="True"/>
|
||||||
|
</GridContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
<cc:VSeparator StyleClasses="LowDivider"/>
|
||||||
|
<PanelContainer Margin="12 0 0 0" StyleClasses="Inset" VerticalAlignment="Center">
|
||||||
|
<SpriteView Name="EntityView" SetSize="64 64" Scale="2 2" OverrideDirection="South" Margin="15"/>
|
||||||
|
</PanelContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
</controls:FancyWindow>
|
||||||
|
|
@ -0,0 +1,57 @@
|
||||||
|
using Content.Client.UserInterface.Controls;
|
||||||
|
using Content.Shared.Power.Generator;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
|
namespace Content.Client.Power.Generator;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class GeneratorWindow : FancyWindow
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||||
|
|
||||||
|
private readonly FuelGeneratorComponent? _component;
|
||||||
|
private SolidFuelGeneratorComponentBuiState? _lastState;
|
||||||
|
|
||||||
|
public GeneratorWindow(SolidFuelGeneratorBoundUserInterface bui, EntityUid vis)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
_entityManager.TryGetComponent(vis, out _component);
|
||||||
|
|
||||||
|
EntityView.SetEntity(vis);
|
||||||
|
TargetPower.IsValid += IsValid;
|
||||||
|
TargetPower.ValueChanged += (args) =>
|
||||||
|
{
|
||||||
|
bui.SetTargetPower(args.Value);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsValid(int arg)
|
||||||
|
{
|
||||||
|
if (arg < 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (arg > (_lastState?.MaximumPower / 1000.0f ?? 0))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Update(SolidFuelGeneratorComponentBuiState state)
|
||||||
|
{
|
||||||
|
if (_component == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var oldState = _lastState;
|
||||||
|
_lastState = state;
|
||||||
|
// ReSharper disable once CompareOfFloatsByEqualityOperator
|
||||||
|
if (oldState?.TargetPower != state.TargetPower)
|
||||||
|
TargetPower.OverrideValue((int)(state.TargetPower / 1000.0f));
|
||||||
|
Efficiency.Text = SharedGeneratorSystem.CalcFuelEfficiency(state.TargetPower, state.OptimalPower, _component).ToString("P1");
|
||||||
|
FuelFraction.Value = state.RemainingFuel - (int) state.RemainingFuel;
|
||||||
|
FuelLeft.Text = ((int) MathF.Floor(state.RemainingFuel)).ToString();
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
using Content.Shared.Power.Generator;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Client.Power.Generator;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed class SolidFuelGeneratorBoundUserInterface : BoundUserInterface
|
||||||
|
{
|
||||||
|
private GeneratorWindow? _window;
|
||||||
|
|
||||||
|
public SolidFuelGeneratorBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Open()
|
||||||
|
{
|
||||||
|
base.Open();
|
||||||
|
_window = new GeneratorWindow(this, Owner);
|
||||||
|
|
||||||
|
_window.OpenCenteredLeft();
|
||||||
|
_window.OnClose += Close;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
|
{
|
||||||
|
if (state is not SolidFuelGeneratorComponentBuiState msg)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_window?.Update(msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
_window?.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetTargetPower(int target)
|
||||||
|
{
|
||||||
|
SendMessage(new SetTargetPowerMessage(target));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -27,4 +27,10 @@ public sealed class UpgradePowerSupplierComponent : Component
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[DataField("scaling", required: true), ViewVariables(VVAccess.ReadWrite)]
|
[DataField("scaling", required: true), ViewVariables(VVAccess.ReadWrite)]
|
||||||
public MachineUpgradeScalingType Scaling;
|
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;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -76,8 +76,12 @@ internal sealed class PowerMonitoringConsoleSystem : EntitySystem
|
||||||
}
|
}
|
||||||
foreach (PowerSupplierComponent pcc in netQ.Suppliers)
|
foreach (PowerSupplierComponent pcc in netQ.Suppliers)
|
||||||
{
|
{
|
||||||
sources.Add(LoadOrSource(pcc, pcc.MaxSupply, false));
|
var supply = pcc.Enabled
|
||||||
totalSources += pcc.MaxSupply;
|
? pcc.MaxSupply
|
||||||
|
: 0f;
|
||||||
|
|
||||||
|
sources.Add(LoadOrSource(pcc, supply, false));
|
||||||
|
totalSources += supply;
|
||||||
}
|
}
|
||||||
foreach (BatteryDischargerComponent pcc in netQ.Dischargers)
|
foreach (BatteryDischargerComponent pcc in netQ.Dischargers)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -43,7 +43,7 @@ public sealed class UpgradePowerSystem : EntitySystem
|
||||||
load *= MathF.Pow(component.PowerDrawMultiplier, rating - 1);
|
load *= MathF.Pow(component.PowerDrawMultiplier, rating - 1);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Logger.Error($"invalid power scaling type for {ToPrettyString(uid)}.");
|
Log.Error($"invalid power scaling type for {ToPrettyString(uid)}.");
|
||||||
load = 0;
|
load = 0;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -82,19 +82,19 @@ public sealed class UpgradePowerSystem : EntitySystem
|
||||||
supply *= MathF.Pow(component.PowerSupplyMultiplier, rating - 1);
|
supply *= MathF.Pow(component.PowerSupplyMultiplier, rating - 1);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Logger.Error($"invalid power scaling type for {ToPrettyString(uid)}.");
|
Log.Error($"invalid power scaling type for {ToPrettyString(uid)}.");
|
||||||
supply = component.BaseSupplyRate;
|
supply = component.BaseSupplyRate;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
component.ActualScalar = supply / component.BaseSupplyRate;
|
||||||
|
|
||||||
if (TryComp<PowerSupplierComponent>(uid, out var powa))
|
if (TryComp<PowerSupplierComponent>(uid, out var powa))
|
||||||
powa.MaxSupply = supply;
|
powa.MaxSupply = supply;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnSupplierUpgradeExamine(EntityUid uid, UpgradePowerSupplierComponent component, UpgradeExamineEvent args)
|
private void OnSupplierUpgradeExamine(EntityUid uid, UpgradePowerSupplierComponent component, UpgradeExamineEvent args)
|
||||||
{
|
{
|
||||||
// UpgradePowerSupplierComponent.PowerSupplyMultiplier is not the actual multiplier, so we have to do this.
|
args.AddPercentageUpgrade("upgrade-power-supply", component.ActualScalar);
|
||||||
if (TryComp<PowerSupplierComponent>(uid, out var powa))
|
|
||||||
args.AddPercentageUpgrade("upgrade-power-supply", powa.MaxSupply / component.BaseSupplyRate);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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!;
|
||||||
|
}
|
||||||
|
|
@ -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!;
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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;
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,87 @@
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Power.Generator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This is used for generators that run off some kind of fuel.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, Access(typeof(SharedGeneratorSystem))]
|
||||||
|
public sealed class FuelGeneratorComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The amount of fuel left in the generator.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("remainingFuel"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float RemainingFuel;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The generator's target power.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("targetPower"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float TargetPower = 15_000.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The maximum target power.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("maxTargetPower"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float MaxTargetPower = 30_000.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The "optimal" power at which the generator is considered to be at 100% efficiency.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("optimalPower"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float OptimalPower = 15_000.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The rate at which one unit of fuel should be consumed.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("optimalBurnRate"), ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float OptimalBurnRate = 1 / 60.0f; // Once every 60 seconds.
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// A constant used to calculate fuel efficiency in relation to target power output and optimal power output
|
||||||
|
/// </summary>
|
||||||
|
[DataField("fuelEfficiencyConstant")]
|
||||||
|
public float FuelEfficiencyConstant = 1.3f;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sent to the server to adjust the targeted power level.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class SetTargetPowerMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public int TargetPower;
|
||||||
|
|
||||||
|
public SetTargetPowerMessage(int targetPower)
|
||||||
|
{
|
||||||
|
TargetPower = targetPower;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Contains network state for FuelGeneratorComponent.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class SolidFuelGeneratorComponentBuiState : BoundUserInterfaceState
|
||||||
|
{
|
||||||
|
public float RemainingFuel;
|
||||||
|
public float TargetPower;
|
||||||
|
public float MaximumPower;
|
||||||
|
public float OptimalPower;
|
||||||
|
|
||||||
|
public SolidFuelGeneratorComponentBuiState(FuelGeneratorComponent component)
|
||||||
|
{
|
||||||
|
RemainingFuel = component.RemainingFuel;
|
||||||
|
TargetPower = component.TargetPower;
|
||||||
|
MaximumPower = component.MaxTargetPower;
|
||||||
|
OptimalPower = component.OptimalPower;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum GeneratorComponentUiKey
|
||||||
|
{
|
||||||
|
Key
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
namespace Content.Shared.Power.Generator;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// This handles small, portable generators that run off a material fuel.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SharedGeneratorSystem : EntitySystem
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Calculates the expected fuel efficiency based on the optimal and target power levels.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="targetPower">Target power level</param>
|
||||||
|
/// <param name="optimalPower">Optimal power level</param>
|
||||||
|
/// <param name="component"></param>
|
||||||
|
/// <returns>Expected fuel efficiency as a percentage</returns>
|
||||||
|
public static float CalcFuelEfficiency(float targetPower, float optimalPower, FuelGeneratorComponent component)
|
||||||
|
{
|
||||||
|
return MathF.Pow(optimalPower / targetPower, component.FuelEfficiencyConstant);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,7 +3,6 @@ using Content.Shared.Examine;
|
||||||
using Content.Shared.Hands.Components;
|
using Content.Shared.Hands.Components;
|
||||||
using Content.Shared.Hands.EntitySystems;
|
using Content.Shared.Hands.EntitySystems;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Item;
|
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
|
|
@ -228,6 +227,17 @@ namespace Content.Shared.Stacks
|
||||||
return merged;
|
return merged;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the amount of items in a stack. If it cannot be stacked, returns 1.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="uid"></param>
|
||||||
|
/// <param name="component"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public int GetCount(EntityUid uid, StackComponent? component = null)
|
||||||
|
{
|
||||||
|
return Resolve(uid, ref component, false) ? component.Count : 1;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Gets the max count for a given entity prototype
|
/// Gets the max count for a given entity prototype
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
generator-ui-title = Generator
|
||||||
|
generator-ui-target-power-label = Target Power (KW):
|
||||||
|
generator-ui-efficiency-label = Efficiency:
|
||||||
|
generator-ui-fuel-use-label = Fuel use:
|
||||||
|
generator-ui-fuel-left-label = Fuel left:
|
||||||
|
|
||||||
|
generator-insert-material = Inserted {THE($item)} into {THE($generator)}...
|
||||||
|
|
@ -3495,7 +3495,7 @@ entities:
|
||||||
- pos: -12.5,2.5
|
- pos: -12.5,2.5
|
||||||
parent: 818
|
parent: 818
|
||||||
type: Transform
|
type: Transform
|
||||||
- proto: GeneratorUranium
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 124
|
- uid: 124
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -1243,7 +1243,7 @@ entities:
|
||||||
type: Transform
|
type: Transform
|
||||||
- enabled: False
|
- enabled: False
|
||||||
type: AmbientSound
|
type: AmbientSound
|
||||||
- proto: GeneratorUranium
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 114
|
- uid: 114
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -972,7 +972,7 @@ entities:
|
||||||
type: Transform
|
type: Transform
|
||||||
- enabled: False
|
- enabled: False
|
||||||
type: AmbientSound
|
type: AmbientSound
|
||||||
- proto: GeneratorUranium
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 83
|
- uid: 83
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -4489,7 +4489,7 @@ entities:
|
||||||
- pos: 4.5,-21.5
|
- pos: 4.5,-21.5
|
||||||
parent: 1
|
parent: 1
|
||||||
type: Transform
|
type: Transform
|
||||||
- proto: GeneratorUranium
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 72
|
- uid: 72
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -1908,7 +1908,7 @@ entities:
|
||||||
type: Transform
|
type: Transform
|
||||||
- enabled: False
|
- enabled: False
|
||||||
type: AmbientSound
|
type: AmbientSound
|
||||||
- proto: GeneratorUranium
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 239
|
- uid: 239
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -2874,7 +2874,7 @@ entities:
|
||||||
type: Transform
|
type: Transform
|
||||||
- enabled: False
|
- enabled: False
|
||||||
type: AmbientSound
|
type: AmbientSound
|
||||||
- proto: GeneratorUranium
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 214
|
- uid: 214
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -2619,7 +2619,7 @@ entities:
|
||||||
type: Transform
|
type: Transform
|
||||||
- enabled: False
|
- enabled: False
|
||||||
type: AmbientSound
|
type: AmbientSound
|
||||||
- proto: GeneratorUranium
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 66
|
- uid: 66
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -3153,7 +3153,7 @@ entities:
|
||||||
type: AmbientSound
|
type: AmbientSound
|
||||||
- color: '#0335FCFF'
|
- color: '#0335FCFF'
|
||||||
type: AtmosPipeColor
|
type: AtmosPipeColor
|
||||||
- proto: GeneratorUranium
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 402
|
- uid: 402
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -11414,7 +11414,7 @@ entities:
|
||||||
type: AmbientSound
|
type: AmbientSound
|
||||||
- color: '#0055CCFF'
|
- color: '#0055CCFF'
|
||||||
type: AtmosPipeColor
|
type: AtmosPipeColor
|
||||||
- proto: GeneratorUranium
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 609
|
- uid: 609
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -2548,7 +2548,7 @@ entities:
|
||||||
type: Transform
|
type: Transform
|
||||||
- enabled: False
|
- enabled: False
|
||||||
type: AmbientSound
|
type: AmbientSound
|
||||||
- proto: GeneratorUranium
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 344
|
- uid: 344
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -2434,7 +2434,7 @@ entities:
|
||||||
type: Transform
|
type: Transform
|
||||||
- enabled: False
|
- enabled: False
|
||||||
type: AmbientSound
|
type: AmbientSound
|
||||||
- proto: GeneratorUranium
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 138
|
- uid: 138
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -982,7 +982,7 @@ entities:
|
||||||
- pos: -1.5,-5.5
|
- pos: -1.5,-5.5
|
||||||
parent: 181
|
parent: 181
|
||||||
type: Transform
|
type: Transform
|
||||||
- proto: GeneratorPlasma
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 67
|
- uid: 67
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -2883,7 +2883,7 @@ entities:
|
||||||
type: Transform
|
type: Transform
|
||||||
- enabled: False
|
- enabled: False
|
||||||
type: AmbientSound
|
type: AmbientSound
|
||||||
- proto: GeneratorUranium
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 55
|
- uid: 55
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -1241,7 +1241,7 @@ entities:
|
||||||
type: DeviceNetwork
|
type: DeviceNetwork
|
||||||
- enabled: False
|
- enabled: False
|
||||||
type: AmbientSound
|
type: AmbientSound
|
||||||
- proto: GeneratorUranium
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 41
|
- uid: 41
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -2871,7 +2871,7 @@ entities:
|
||||||
type: Transform
|
type: Transform
|
||||||
- enabled: False
|
- enabled: False
|
||||||
type: AmbientSound
|
type: AmbientSound
|
||||||
- proto: GeneratorUranium
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 358
|
- uid: 358
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -25140,7 +25140,7 @@ entities:
|
||||||
- pos: 32.5,-21.5
|
- pos: 32.5,-21.5
|
||||||
parent: 1668
|
parent: 1668
|
||||||
type: Transform
|
type: Transform
|
||||||
- proto: GeneratorUranium
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 5176
|
- uid: 5176
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -6031,7 +6031,7 @@ entities:
|
||||||
- pos: -5.7255287,20.539352
|
- pos: -5.7255287,20.539352
|
||||||
parent: 104
|
parent: 104
|
||||||
type: Transform
|
type: Transform
|
||||||
- proto: GeneratorPlasma
|
- proto: GeneratorBasic15kW
|
||||||
entities:
|
entities:
|
||||||
- uid: 2326
|
- uid: 2326
|
||||||
components:
|
components:
|
||||||
|
|
|
||||||
|
|
@ -167,13 +167,20 @@
|
||||||
- type: PowerSupplier
|
- type: PowerSupplier
|
||||||
supplyRate: 3000
|
supplyRate: 3000
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
parent: BaseGenerator
|
||||||
|
id: GeneratorBasic15kW
|
||||||
|
suffix: Basic, 15kW
|
||||||
|
components:
|
||||||
|
- type: PowerSupplier
|
||||||
|
supplyRate: 15000
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: [ BaseGenerator, ConstructibleMachine ]
|
parent: [ BaseGenerator, ConstructibleMachine ]
|
||||||
id: GeneratorPlasma
|
id: GeneratorPlasma
|
||||||
suffix: Plasma, 5kW
|
suffix: Plasma, 20kW
|
||||||
components:
|
components:
|
||||||
- type: PowerSupplier
|
- type: PowerSupplier
|
||||||
supplyRate: 5000 # 260 sec / sheet
|
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Power/Generation/portable_generator.rsi
|
sprite: Structures/Power/Generation/portable_generator.rsi
|
||||||
state: portgen0_1
|
state: portgen0_1
|
||||||
|
|
@ -186,14 +193,24 @@
|
||||||
- type: UpgradePowerSupplier
|
- type: UpgradePowerSupplier
|
||||||
powerSupplyMultiplier: 1.25
|
powerSupplyMultiplier: 1.25
|
||||||
scaling: Exponential
|
scaling: Exponential
|
||||||
|
- type: FuelGenerator
|
||||||
|
targetPower: 20000
|
||||||
|
optimalPower: 20000
|
||||||
|
- type: SolidFuelGeneratorAdapter
|
||||||
|
fuelMaterial: Plasma
|
||||||
|
- type: ActivatableUI
|
||||||
|
key: enum.GeneratorComponentUiKey.Key
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.GeneratorComponentUiKey.Key
|
||||||
|
type: SolidFuelGeneratorBoundUserInterface
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: [ BaseGenerator, ConstructibleMachine ]
|
parent: [ BaseGenerator, ConstructibleMachine ]
|
||||||
id: GeneratorUranium
|
id: GeneratorUranium
|
||||||
suffix: Uranium, 15kW
|
suffix: Uranium, 30kW
|
||||||
components:
|
components:
|
||||||
- type: PowerSupplier
|
- type: PowerSupplier
|
||||||
supplyRate: 15000 # 85 sec / sheet
|
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
sprite: Structures/Power/Generation/portable_generator.rsi
|
sprite: Structures/Power/Generation/portable_generator.rsi
|
||||||
state: portgen1_1
|
state: portgen1_1
|
||||||
|
|
@ -206,6 +223,18 @@
|
||||||
- type: UpgradePowerSupplier
|
- type: UpgradePowerSupplier
|
||||||
powerSupplyMultiplier: 1.25
|
powerSupplyMultiplier: 1.25
|
||||||
scaling: Exponential
|
scaling: Exponential
|
||||||
|
- type: FuelGenerator
|
||||||
|
targetPower: 30000
|
||||||
|
optimalPower: 30000
|
||||||
|
optimalBurnRate: 0.00416666666
|
||||||
|
- type: SolidFuelGeneratorAdapter
|
||||||
|
fuelMaterial: Uranium
|
||||||
|
- type: ActivatableUI
|
||||||
|
key: enum.GeneratorComponentUiKey.Key
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.GeneratorComponentUiKey.Key
|
||||||
|
type: SolidFuelGeneratorBoundUserInterface
|
||||||
|
|
||||||
- type: entity
|
- type: entity
|
||||||
parent: BaseGeneratorWallmount
|
parent: BaseGeneratorWallmount
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue