diff --git a/Content.Client/Power/Generator/GeneratorWindow.xaml b/Content.Client/Power/Generator/GeneratorWindow.xaml new file mode 100644 index 0000000000..86da3835e2 --- /dev/null +++ b/Content.Client/Power/Generator/GeneratorWindow.xaml @@ -0,0 +1,26 @@ + + + + + + + + + + + + + diff --git a/Content.Client/Power/Generator/GeneratorWindow.xaml.cs b/Content.Client/Power/Generator/GeneratorWindow.xaml.cs new file mode 100644 index 0000000000..029ccbd6ab --- /dev/null +++ b/Content.Client/Power/Generator/GeneratorWindow.xaml.cs @@ -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(); + + } +} diff --git a/Content.Client/Power/Generator/SolidFuelGeneratorBoundUserInterface.cs b/Content.Client/Power/Generator/SolidFuelGeneratorBoundUserInterface.cs new file mode 100644 index 0000000000..f43072f222 --- /dev/null +++ b/Content.Client/Power/Generator/SolidFuelGeneratorBoundUserInterface.cs @@ -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)); + } +} diff --git a/Content.Server/Power/Components/UpgradePowerSupplierComponent.cs b/Content.Server/Power/Components/UpgradePowerSupplierComponent.cs index 25cf38155f..e9262f481e 100644 --- a/Content.Server/Power/Components/UpgradePowerSupplierComponent.cs +++ b/Content.Server/Power/Components/UpgradePowerSupplierComponent.cs @@ -27,4 +27,10 @@ public sealed class UpgradePowerSupplierComponent : Component /// [DataField("scaling", required: true), ViewVariables(VVAccess.ReadWrite)] public MachineUpgradeScalingType Scaling; + + /// + /// The current value that the power supply is being scaled by, + /// + [DataField("actualScalar"), ViewVariables(VVAccess.ReadWrite)] + public float ActualScalar = 1f; } diff --git a/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs b/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs index 3a4da764ce..07e1a62eed 100644 --- a/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs @@ -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) { diff --git a/Content.Server/Power/EntitySystems/UpgradePowerSystem.cs b/Content.Server/Power/EntitySystems/UpgradePowerSystem.cs index 0dd696b251..0d43f60a2b 100644 --- a/Content.Server/Power/EntitySystems/UpgradePowerSystem.cs +++ b/Content.Server/Power/EntitySystems/UpgradePowerSystem.cs @@ -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(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(uid, out var powa)) - args.AddPercentageUpgrade("upgrade-power-supply", powa.MaxSupply / component.BaseSupplyRate); + args.AddPercentageUpgrade("upgrade-power-supply", component.ActualScalar); } } diff --git a/Content.Server/Power/Generator/ChemicalFuelGeneratorAdapterComponent.cs b/Content.Server/Power/Generator/ChemicalFuelGeneratorAdapterComponent.cs new file mode 100644 index 0000000000..a16bdbbca1 --- /dev/null +++ b/Content.Server/Power/Generator/ChemicalFuelGeneratorAdapterComponent.cs @@ -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; + +/// +/// This is used for chemical fuel input into generators. +/// +[RegisterComponent, Access(typeof(GeneratorSystem))] +public sealed class ChemicalFuelGeneratorAdapterComponent : Component +{ + /// + /// The acceptable list of input entities. + /// + [DataField("whitelist")] + public EntityWhitelist? Whitelist; + + /// + /// The conversion factor for different chems you can put in. + /// + [DataField("chemConversionFactors", required: true, customTypeSerializer:typeof(PrototypeIdDictionarySerializer))] + public Dictionary ChemConversionFactors = default!; +} diff --git a/Content.Server/Power/Generator/ChemicalFuelGeneratorDirectSourceComponent.cs b/Content.Server/Power/Generator/ChemicalFuelGeneratorDirectSourceComponent.cs new file mode 100644 index 0000000000..3904a3bdf7 --- /dev/null +++ b/Content.Server/Power/Generator/ChemicalFuelGeneratorDirectSourceComponent.cs @@ -0,0 +1,14 @@ +namespace Content.Server.Power.Generator; + +/// +/// This is used for stuff that can directly be shoved into a generator. +/// +[RegisterComponent, Access(typeof(GeneratorSystem))] +public sealed class ChemicalFuelGeneratorDirectSourceComponent : Component +{ + /// + /// The solution to pull fuel material from. + /// + [DataField("solution", required: true), ViewVariables(VVAccess.ReadWrite)] + public string Solution = default!; +} diff --git a/Content.Server/Power/Generator/GasPowerReceiverComponent.cs b/Content.Server/Power/Generator/GasPowerReceiverComponent.cs new file mode 100644 index 0000000000..2eede9f243 --- /dev/null +++ b/Content.Server/Power/Generator/GasPowerReceiverComponent.cs @@ -0,0 +1,47 @@ +using Content.Shared.Atmos; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom; + +namespace Content.Server.Power.Generator; + +/// +/// This is used for providing gas power to machinery. +/// +[RegisterComponent, Access(typeof(GasPowerReceiverSystem))] +public sealed class GasPowerReceiverComponent : Component +{ + /// + /// Past this temperature we assume we're in reaction mass mode and not magic mode. + /// + [DataField("maxTemperature"), ViewVariables(VVAccess.ReadWrite)] + public float MaxTemperature = 1000.0f; + + /// + /// The gas that fuels this generator + /// + [DataField("targetGas", required: true), ViewVariables(VVAccess.ReadWrite)] + public Gas TargetGas; + + /// + /// The amount of gas consumed for operation in magic mode. + /// + [DataField("molesConsumedSec"), ViewVariables(VVAccess.ReadWrite)] + public float MolesConsumedSec = 1.55975875833f / 4; + + /// + /// The amount of kPA "consumed" for operation in pressure mode. + /// + [DataField("pressureConsumedSec"), ViewVariables(VVAccess.ReadWrite)] + public float PressureConsumedSec = 100f; + + /// + /// Whether the consumed gas should then be ejected directly into the atmosphere. + /// + [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; +} diff --git a/Content.Server/Power/Generator/GasPowerReceiverSystem.cs b/Content.Server/Power/Generator/GasPowerReceiverSystem.cs new file mode 100644 index 0000000000..a8c19fe71b --- /dev/null +++ b/Content.Server/Power/Generator/GasPowerReceiverSystem.cs @@ -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; + +/// +/// This handles gas power receivers, allowing devices to accept power in the form of a gas. +/// +public sealed class GasPowerReceiverSystem : EntitySystem +{ + [Dependency] private readonly IGameTiming _gameTiming = default!; + [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!; + [Dependency] private readonly NodeContainerSystem _nodeContainer = default!; + + /// + public override void Initialize() + { + SubscribeLocalEvent(OnMapInit); + SubscribeLocalEvent(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(uid) + || !TryComp(uid, out var nodeContainer) + || !_nodeContainer.TryGetNode(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); + } + } +} diff --git a/Content.Server/Power/Generator/GeneratorSystem.cs b/Content.Server/Power/Generator/GeneratorSystem.cs new file mode 100644 index 0000000000..c34922e5f4 --- /dev/null +++ b/Content.Server/Power/Generator/GeneratorSystem.cs @@ -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; + +/// +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 _upgradeQuery; + + public override void Initialize() + { + _upgradeQuery = GetEntityQuery(); + + SubscribeLocalEvent(OnSolidFuelAdapterInteractUsing); + SubscribeLocalEvent(OnChemicalFuelAdapterInteractUsing); + SubscribeLocalEvent(OnTargetPowerSet); + } + + private void OnChemicalFuelAdapterInteractUsing(EntityUid uid, ChemicalFuelGeneratorAdapterComponent component, InteractUsingEvent args) + { + if (args.Handled) + return; + + if (!TryComp(args.Used, out var solutions) || + !TryComp(uid, out var generator)) + return; + + if (!(component.Whitelist?.IsValid(args.Used) ?? true)) + return; + + if (TryComp(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(args.Used, out var mat) || + !HasComp(args.Used) || + !TryComp(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(); + + 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)); + } +} diff --git a/Content.Server/Power/Generator/SolidFuelGeneratorAdapterComponent.cs b/Content.Server/Power/Generator/SolidFuelGeneratorAdapterComponent.cs new file mode 100644 index 0000000000..70e3d16410 --- /dev/null +++ b/Content.Server/Power/Generator/SolidFuelGeneratorAdapterComponent.cs @@ -0,0 +1,23 @@ +using Content.Shared.Materials; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; + +namespace Content.Server.Power.Generator; + +/// +/// This is used for allowing you to insert fuel into gens. +/// +[RegisterComponent, Access(typeof(GeneratorSystem))] +public sealed class SolidFuelGeneratorAdapterComponent : Component +{ + /// + /// The material to accept as fuel. + /// + [DataField("fuelMaterial", customTypeSerializer: typeof(PrototypeIdSerializer)), ViewVariables(VVAccess.ReadWrite)] + public string FuelMaterial = "Plasma"; + + /// + /// How much fuel that material should count for. + /// + [DataField("multiplier"), ViewVariables(VVAccess.ReadWrite)] + public float Multiplier = 1.0f; +} diff --git a/Content.Shared/Power/Generator/FuelGeneratorComponent.cs b/Content.Shared/Power/Generator/FuelGeneratorComponent.cs new file mode 100644 index 0000000000..0f98d16abf --- /dev/null +++ b/Content.Shared/Power/Generator/FuelGeneratorComponent.cs @@ -0,0 +1,87 @@ +using Robust.Shared.GameStates; +using Robust.Shared.Serialization; + +namespace Content.Shared.Power.Generator; + +/// +/// This is used for generators that run off some kind of fuel. +/// +[RegisterComponent, NetworkedComponent, Access(typeof(SharedGeneratorSystem))] +public sealed class FuelGeneratorComponent : Component +{ + /// + /// The amount of fuel left in the generator. + /// + [DataField("remainingFuel"), ViewVariables(VVAccess.ReadWrite)] + public float RemainingFuel; + + /// + /// The generator's target power. + /// + [DataField("targetPower"), ViewVariables(VVAccess.ReadWrite)] + public float TargetPower = 15_000.0f; + + /// + /// The maximum target power. + /// + [DataField("maxTargetPower"), ViewVariables(VVAccess.ReadWrite)] + public float MaxTargetPower = 30_000.0f; + + /// + /// The "optimal" power at which the generator is considered to be at 100% efficiency. + /// + [DataField("optimalPower"), ViewVariables(VVAccess.ReadWrite)] + public float OptimalPower = 15_000.0f; + + /// + /// The rate at which one unit of fuel should be consumed. + /// + [DataField("optimalBurnRate"), ViewVariables(VVAccess.ReadWrite)] + public float OptimalBurnRate = 1 / 60.0f; // Once every 60 seconds. + + /// + /// A constant used to calculate fuel efficiency in relation to target power output and optimal power output + /// + [DataField("fuelEfficiencyConstant")] + public float FuelEfficiencyConstant = 1.3f; +} + +/// +/// Sent to the server to adjust the targeted power level. +/// +[Serializable, NetSerializable] +public sealed class SetTargetPowerMessage : BoundUserInterfaceMessage +{ + public int TargetPower; + + public SetTargetPowerMessage(int targetPower) + { + TargetPower = targetPower; + } +} + +/// +/// Contains network state for FuelGeneratorComponent. +/// +[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 +} diff --git a/Content.Shared/Power/Generator/SharedGeneratorSystem.cs b/Content.Shared/Power/Generator/SharedGeneratorSystem.cs new file mode 100644 index 0000000000..aafef6272b --- /dev/null +++ b/Content.Shared/Power/Generator/SharedGeneratorSystem.cs @@ -0,0 +1,19 @@ +namespace Content.Shared.Power.Generator; + +/// +/// This handles small, portable generators that run off a material fuel. +/// +public abstract class SharedGeneratorSystem : EntitySystem +{ + /// + /// Calculates the expected fuel efficiency based on the optimal and target power levels. + /// + /// Target power level + /// Optimal power level + /// + /// Expected fuel efficiency as a percentage + public static float CalcFuelEfficiency(float targetPower, float optimalPower, FuelGeneratorComponent component) + { + return MathF.Pow(optimalPower / targetPower, component.FuelEfficiencyConstant); + } +} diff --git a/Content.Shared/Stacks/SharedStackSystem.cs b/Content.Shared/Stacks/SharedStackSystem.cs index b01c486213..31d8033171 100644 --- a/Content.Shared/Stacks/SharedStackSystem.cs +++ b/Content.Shared/Stacks/SharedStackSystem.cs @@ -3,7 +3,6 @@ using Content.Shared.Examine; using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; -using Content.Shared.Item; using Content.Shared.Popups; using JetBrains.Annotations; using Robust.Shared.GameStates; @@ -228,6 +227,17 @@ namespace Content.Shared.Stacks return merged; } + /// + /// Gets the amount of items in a stack. If it cannot be stacked, returns 1. + /// + /// + /// + /// + public int GetCount(EntityUid uid, StackComponent? component = null) + { + return Resolve(uid, ref component, false) ? component.Count : 1; + } + /// /// Gets the max count for a given entity prototype /// diff --git a/Resources/Locale/en-US/power/components/generator.ftl b/Resources/Locale/en-US/power/components/generator.ftl new file mode 100644 index 0000000000..f5831caa90 --- /dev/null +++ b/Resources/Locale/en-US/power/components/generator.ftl @@ -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)}... diff --git a/Resources/Maps/Misc/terminal.yml b/Resources/Maps/Misc/terminal.yml index f046210aa3..e5a34b68cc 100644 --- a/Resources/Maps/Misc/terminal.yml +++ b/Resources/Maps/Misc/terminal.yml @@ -3495,7 +3495,7 @@ entities: - pos: -12.5,2.5 parent: 818 type: Transform -- proto: GeneratorUranium +- proto: GeneratorBasic15kW entities: - uid: 124 components: diff --git a/Resources/Maps/Shuttles/arrivals.yml b/Resources/Maps/Shuttles/arrivals.yml index fc1f95771f..428300c345 100644 --- a/Resources/Maps/Shuttles/arrivals.yml +++ b/Resources/Maps/Shuttles/arrivals.yml @@ -1243,7 +1243,7 @@ entities: type: Transform - enabled: False type: AmbientSound -- proto: GeneratorUranium +- proto: GeneratorBasic15kW entities: - uid: 114 components: diff --git a/Resources/Maps/Shuttles/cargo.yml b/Resources/Maps/Shuttles/cargo.yml index 38565e20a0..6555e5ccb0 100644 --- a/Resources/Maps/Shuttles/cargo.yml +++ b/Resources/Maps/Shuttles/cargo.yml @@ -972,7 +972,7 @@ entities: type: Transform - enabled: False type: AmbientSound -- proto: GeneratorUranium +- proto: GeneratorBasic15kW entities: - uid: 83 components: diff --git a/Resources/Maps/Shuttles/dart.yml b/Resources/Maps/Shuttles/dart.yml index 67b28cbbb1..57218ff88a 100644 --- a/Resources/Maps/Shuttles/dart.yml +++ b/Resources/Maps/Shuttles/dart.yml @@ -4489,7 +4489,7 @@ entities: - pos: 4.5,-21.5 parent: 1 type: Transform -- proto: GeneratorUranium +- proto: GeneratorBasic15kW entities: - uid: 72 components: diff --git a/Resources/Maps/Shuttles/emergency.yml b/Resources/Maps/Shuttles/emergency.yml index bfcb5a27b7..d82cbee682 100644 --- a/Resources/Maps/Shuttles/emergency.yml +++ b/Resources/Maps/Shuttles/emergency.yml @@ -1908,7 +1908,7 @@ entities: type: Transform - enabled: False type: AmbientSound -- proto: GeneratorUranium +- proto: GeneratorBasic15kW entities: - uid: 239 components: diff --git a/Resources/Maps/Shuttles/emergency_box.yml b/Resources/Maps/Shuttles/emergency_box.yml index 0cf29e8e5b..215821df97 100644 --- a/Resources/Maps/Shuttles/emergency_box.yml +++ b/Resources/Maps/Shuttles/emergency_box.yml @@ -2874,7 +2874,7 @@ entities: type: Transform - enabled: False type: AmbientSound -- proto: GeneratorUranium +- proto: GeneratorBasic15kW entities: - uid: 214 components: diff --git a/Resources/Maps/Shuttles/emergency_courser.yml b/Resources/Maps/Shuttles/emergency_courser.yml index c47c873834..637634ff5c 100644 --- a/Resources/Maps/Shuttles/emergency_courser.yml +++ b/Resources/Maps/Shuttles/emergency_courser.yml @@ -2619,7 +2619,7 @@ entities: type: Transform - enabled: False type: AmbientSound -- proto: GeneratorUranium +- proto: GeneratorBasic15kW entities: - uid: 66 components: diff --git a/Resources/Maps/Shuttles/emergency_lox.yml b/Resources/Maps/Shuttles/emergency_lox.yml index 2f61e00f56..00b3663c16 100644 --- a/Resources/Maps/Shuttles/emergency_lox.yml +++ b/Resources/Maps/Shuttles/emergency_lox.yml @@ -3153,7 +3153,7 @@ entities: type: AmbientSound - color: '#0335FCFF' type: AtmosPipeColor -- proto: GeneratorUranium +- proto: GeneratorBasic15kW entities: - uid: 402 components: diff --git a/Resources/Maps/Shuttles/emergency_raven.yml b/Resources/Maps/Shuttles/emergency_raven.yml index ad2b7ed7de..d71b0bf7eb 100644 --- a/Resources/Maps/Shuttles/emergency_raven.yml +++ b/Resources/Maps/Shuttles/emergency_raven.yml @@ -11414,7 +11414,7 @@ entities: type: AmbientSound - color: '#0055CCFF' type: AtmosPipeColor -- proto: GeneratorUranium +- proto: GeneratorBasic15kW entities: - uid: 609 components: diff --git a/Resources/Maps/Shuttles/emergency_rod.yml b/Resources/Maps/Shuttles/emergency_rod.yml index 9b3e8e4d36..f877c28e37 100644 --- a/Resources/Maps/Shuttles/emergency_rod.yml +++ b/Resources/Maps/Shuttles/emergency_rod.yml @@ -2548,7 +2548,7 @@ entities: type: Transform - enabled: False type: AmbientSound -- proto: GeneratorUranium +- proto: GeneratorBasic15kW entities: - uid: 344 components: diff --git a/Resources/Maps/Shuttles/emergency_transit.yml b/Resources/Maps/Shuttles/emergency_transit.yml index 32508e7d5a..0137633ed2 100644 --- a/Resources/Maps/Shuttles/emergency_transit.yml +++ b/Resources/Maps/Shuttles/emergency_transit.yml @@ -2434,7 +2434,7 @@ entities: type: Transform - enabled: False type: AmbientSound -- proto: GeneratorUranium +- proto: GeneratorBasic15kW entities: - uid: 138 components: diff --git a/Resources/Maps/Shuttles/mining.yml b/Resources/Maps/Shuttles/mining.yml index 6e952eeb90..9d90444c75 100644 --- a/Resources/Maps/Shuttles/mining.yml +++ b/Resources/Maps/Shuttles/mining.yml @@ -982,7 +982,7 @@ entities: - pos: -1.5,-5.5 parent: 181 type: Transform -- proto: GeneratorPlasma +- proto: GeneratorBasic15kW entities: - uid: 67 components: diff --git a/Resources/Maps/Shuttles/pirate.yml b/Resources/Maps/Shuttles/pirate.yml index faf091665f..3f174fed53 100644 --- a/Resources/Maps/Shuttles/pirate.yml +++ b/Resources/Maps/Shuttles/pirate.yml @@ -2883,7 +2883,7 @@ entities: type: Transform - enabled: False type: AmbientSound -- proto: GeneratorUranium +- proto: GeneratorBasic15kW entities: - uid: 55 components: diff --git a/Resources/Maps/Shuttles/striker.yml b/Resources/Maps/Shuttles/striker.yml index 4d341f61e7..3f194ee8d2 100644 --- a/Resources/Maps/Shuttles/striker.yml +++ b/Resources/Maps/Shuttles/striker.yml @@ -1241,7 +1241,7 @@ entities: type: DeviceNetwork - enabled: False type: AmbientSound -- proto: GeneratorUranium +- proto: GeneratorBasic15kW entities: - uid: 41 components: diff --git a/Resources/Maps/Shuttles/wizard.yml b/Resources/Maps/Shuttles/wizard.yml index 65d560d538..eaf048501a 100644 --- a/Resources/Maps/Shuttles/wizard.yml +++ b/Resources/Maps/Shuttles/wizard.yml @@ -2871,7 +2871,7 @@ entities: type: Transform - enabled: False type: AmbientSound -- proto: GeneratorUranium +- proto: GeneratorBasic15kW entities: - uid: 358 components: diff --git a/Resources/Maps/centcomm.yml b/Resources/Maps/centcomm.yml index 6de3b3d350..09ed37a1e1 100644 --- a/Resources/Maps/centcomm.yml +++ b/Resources/Maps/centcomm.yml @@ -25140,7 +25140,7 @@ entities: - pos: 32.5,-21.5 parent: 1668 type: Transform -- proto: GeneratorUranium +- proto: GeneratorBasic15kW entities: - uid: 5176 components: diff --git a/Resources/Maps/nukieplanet.yml b/Resources/Maps/nukieplanet.yml index 8d6ebb8065..4676d7687c 100644 --- a/Resources/Maps/nukieplanet.yml +++ b/Resources/Maps/nukieplanet.yml @@ -6031,7 +6031,7 @@ entities: - pos: -5.7255287,20.539352 parent: 104 type: Transform -- proto: GeneratorPlasma +- proto: GeneratorBasic15kW entities: - uid: 2326 components: diff --git a/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml b/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml index d9434d0e93..ce912ae2c2 100644 --- a/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml +++ b/Resources/Prototypes/Entities/Structures/Power/Generation/generators.yml @@ -167,13 +167,20 @@ - type: PowerSupplier supplyRate: 3000 +- type: entity + parent: BaseGenerator + id: GeneratorBasic15kW + suffix: Basic, 15kW + components: + - type: PowerSupplier + supplyRate: 15000 + - type: entity parent: [ BaseGenerator, ConstructibleMachine ] id: GeneratorPlasma - suffix: Plasma, 5kW + suffix: Plasma, 20kW components: - type: PowerSupplier - supplyRate: 5000 # 260 sec / sheet - type: Sprite sprite: Structures/Power/Generation/portable_generator.rsi state: portgen0_1 @@ -186,14 +193,24 @@ - type: UpgradePowerSupplier powerSupplyMultiplier: 1.25 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 parent: [ BaseGenerator, ConstructibleMachine ] id: GeneratorUranium - suffix: Uranium, 15kW + suffix: Uranium, 30kW components: - type: PowerSupplier - supplyRate: 15000 # 85 sec / sheet - type: Sprite sprite: Structures/Power/Generation/portable_generator.rsi state: portgen1_1 @@ -206,6 +223,18 @@ - type: UpgradePowerSupplier powerSupplyMultiplier: 1.25 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 parent: BaseGeneratorWallmount