* PACMAN generators show network load/supply.
This gives more feedback to players that their PACMAN is properly connected and what the network status is (i.e. you don't have enough generators).
* Buff JRPACMAN to 8 kW.
Shifted all power values up by +3 kW.
They're frequently too weak to power even single rooms so they deserve a buff.
* Change unit format helpers number format.
Always displays one digit of precision. This avoids jumping around when a value is changing live.
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
- MinSize="450 235"
- SetSize="450 235"
+ MinSize="450 250"
+ SetSize="450 250"
Resizable="False"
Title="{Loc 'portable-generator-ui-title'}">
<BoxContainer Margin="4 0" Orientation="Horizontal">
</BoxContainer>
<Label Name="OutputSwitchLabel" Text="{Loc 'portable-generator-ui-switch'}" Visible="False" />
<Button Name="OutputSwitchButton" Visible="False" />
+ <!-- Network stats menu -->
+ <Label Text="{Loc 'portable-generator-ui-network-stats'}"/>
+ <Control>
+ <Label Name="NetworkStats" />
+ </Control>
</GridContainer>
<Label Margin="2 0 0 0" Name="CloggedLabel" FontColorOverride="Red" Text="{Loc 'portable-generator-ui-clogged'}" />
</BoxContainer>
}
CloggedLabel.Visible = state.Clogged;
+
+ if (state.NetworkStats is { } netStats)
+ {
+ NetworkStats.Text = Loc.GetString(
+ "portable-generator-ui-network-stats-value",
+ ("load", netStats.Load),
+ ("supply", netStats.Supply));
+
+ var good = netStats.Load <= netStats.Supply;
+ NetworkStats.SetOnlyStyleClass(good ? "Good" : "Caution");
+ }
+ else
+ {
+ NetworkStats.Text = Loc.GetString("portable-generator-ui-network-stats-not-connected");
+ NetworkStats.StyleClasses.Clear();
+ }
}
private bool TryGetStartProgress(out float progress)
using Content.Server.DoAfter;
+using Content.Server.NodeContainer.NodeGroups;
using Content.Server.Popups;
+using Content.Server.Power.Components;
+using Content.Server.Power.EntitySystems;
using Content.Shared.DoAfter;
using Content.Shared.Power.Generator;
using Content.Shared.Verbs;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly GeneratorSystem _generator = default!;
[Dependency] private readonly PowerSwitchableSystem _switchable = default!;
+ [Dependency] private readonly PowerNetSystem _powerNet = default!;
public override void Initialize()
{
// Update UI after main system runs.
UpdatesAfter.Add(typeof(GeneratorSystem));
+ UpdatesAfter.Add(typeof(PowerNetSystem));
SubscribeLocalEvent<PortableGeneratorComponent, GetVerbsEvent<AlternativeVerb>>(GetAlternativeVerb);
SubscribeLocalEvent<PortableGeneratorComponent, GeneratorStartedEvent>(GeneratorTugged);
public override void Update(float frameTime)
{
- var query = EntityQueryEnumerator<PortableGeneratorComponent, FuelGeneratorComponent, AppearanceComponent>();
+ var query = EntityQueryEnumerator<PortableGeneratorComponent, FuelGeneratorComponent, PowerSupplierComponent>();
- while (query.MoveNext(out var uid, out var portGen, out var fuelGen, out var xform))
+ while (query.MoveNext(out var uid, out var portGen, out var fuelGen, out var powerSupplier))
{
- UpdateUI(uid, portGen, fuelGen);
+ UpdateUI(uid, portGen, fuelGen, powerSupplier);
}
}
- private void UpdateUI(EntityUid uid, PortableGeneratorComponent comp, FuelGeneratorComponent fuelComp)
+ private void UpdateUI(
+ EntityUid uid,
+ PortableGeneratorComponent comp,
+ FuelGeneratorComponent fuelComp,
+ PowerSupplierComponent powerSupplier)
{
if (!_uiSystem.IsUiOpen(uid, GeneratorComponentUiKey.Key))
return;
var fuel = _generator.GetFuel(uid);
var clogged = _generator.GetIsClogged(uid);
+ (float, float)? networkStats = null;
+ if (powerSupplier.Net is { IsConnectedNetwork: true } net)
+ networkStats = (net.NetworkNode.LastCombinedLoad, net.NetworkNode.LastCombinedSupply);
+
_uiSystem.TrySetUiState(
uid,
GeneratorComponentUiKey.Key,
- new PortableGeneratorComponentBuiState(fuelComp, fuel, clogged));
+ new PortableGeneratorComponentBuiState(fuelComp, fuel, clogged, networkStats));
}
}
PowerNetSystem = entMan.EntitySysManager.GetEntitySystem<PowerNetSystem>();
}
+ public bool IsConnectedNetwork => NodeCount > 1;
+
public void AddConsumer(PowerConsumerComponent consumer)
{
DebugTools.Assert(consumer.NetworkLoad.LinkedNetwork == default);
{
public interface IBasePowerNet
{
+ /// <summary>
+ /// Indicates whether this network forms some form of connection (more than one node).
+ /// </summary>
+ /// <remarks>
+ /// Even "unconnected" power devices form a single-node power network all by themselves.
+ /// To players, this doesn't look like they're connected to anything.
+ /// This property accounts for this and forms a more intuitive check.
+ /// </remarks>
+ bool IsConnectedNetwork { get; }
+
void AddConsumer(PowerConsumerComponent consumer);
void RemoveConsumer(PowerConsumerComponent consumer);
{
public float RemainingFuel;
public bool Clogged;
+ public (float Load, float Supply)? NetworkStats;
public float TargetPower;
public float MaximumPower;
public float OptimalPower;
public bool On;
- public PortableGeneratorComponentBuiState(FuelGeneratorComponent component, float remainingFuel, bool clogged)
+ public PortableGeneratorComponentBuiState(
+ FuelGeneratorComponent component,
+ float remainingFuel,
+ bool clogged,
+ (float Demand, float Supply)? networkStats)
{
RemainingFuel = remainingFuel;
Clogged = clogged;
MaximumPower = component.MaxTargetPower;
OptimalPower = component.OptimalPower;
On = component.On;
+ NetworkStats = networkStats;
}
}
### Special messages used by internal localizer stuff.
# Used internally by the PRESSURE() function.
-zzzz-fmt-pressure = { TOSTRING($divided, "G4") } { $places ->
+zzzz-fmt-pressure = { TOSTRING($divided, "F1") } { $places ->
[0] kPa
[1] MPa
[2] GPa
}
# Used internally by the POWERWATTS() function.
-zzzz-fmt-power-watts = { TOSTRING($divided, "G4") } { $places ->
+zzzz-fmt-power-watts = { TOSTRING($divided, "F1") } { $places ->
[0] W
[1] kW
[2] MW
# Used internally by the POWERJOULES() function.
# Reminder: 1 joule = 1 watt for 1 second (multiply watts by seconds to get joules).
# Therefore 1 kilowatt-hour is equal to 3,600,000 joules (3.6MJ)
-zzzz-fmt-power-joules = { TOSTRING($divided, "G4") } { $places ->
+zzzz-fmt-power-joules = { TOSTRING($divided, "F1") } { $places ->
[0] J
[1] kJ
[2] MJ
portable-generator-ui-eta = (~{ $minutes } min)
portable-generator-ui-unanchored = Unanchored
portable-generator-ui-current-output = Current output: {$voltage}
+portable-generator-ui-network-stats = Network:
+portable-generator-ui-network-stats-value = { POWERWATTS($supply) } / { POWERWATTS($load) }
+portable-generator-ui-network-stats-not-connected = Not connected
power-switchable-generator-examine = The power output is set to {$voltage}.
power-switchable-generator-switched = Switched output to {$voltage}!
name: J.R.P.A.C.M.A.N.-type portable generator
description: |-
A small generator capable of powering individual rooms, in case of emergencies.
- Runs off welding fuel and is rated for up to 5 kW.
+ Runs off welding fuel and is rated for up to 8 kW.
Rated ages 3 and up.
parent: PortableGeneratorBase
id: PortableGeneratorJrPacman
- suffix: Welding Fuel, 5 kW
+ suffix: Welding Fuel, 8 kW
components:
- type: AmbientSound
range: 4
- type: Machine
board: PortableGeneratorJrPacmanMachineCircuitboard
- type: FuelGenerator
- targetPower: 2000
- minTargetPower: 1000
- optimalPower: 5000
- maxTargetPower: 5000
+ targetPower: 5000
+ minTargetPower: 4000
+ optimalPower: 8000
+ maxTargetPower: 8000
# 7.5 minutes at full tank.
optimalBurnRate: 0.11111111
# Shallow curve that allows you to just barely eek out 12 minutes at lowest.