Make PACMANs a little better (#24604)

* 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.
This commit is contained in:
Pieter-Jan Briers
2024-01-27 03:53:43 +01:00
committed by GitHub
parent 41c0efeaf1
commit f855cb2b00
9 changed files with 72 additions and 17 deletions

View File

@@ -1,8 +1,8 @@
<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">
@@ -32,6 +32,11 @@
</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>

View File

@@ -115,6 +115,22 @@ public sealed partial class GeneratorWindow : FancyWindow
}
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)

View File

@@ -1,5 +1,8 @@
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;
@@ -24,6 +27,7 @@ public sealed class PortableGeneratorSystem : SharedPortableGeneratorSystem
[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()
{
@@ -31,6 +35,7 @@ public sealed class PortableGeneratorSystem : SharedPortableGeneratorSystem
// Update UI after main system runs.
UpdatesAfter.Add(typeof(GeneratorSystem));
UpdatesAfter.Add(typeof(PowerNetSystem));
SubscribeLocalEvent<PortableGeneratorComponent, GetVerbsEvent<AlternativeVerb>>(GetAlternativeVerb);
SubscribeLocalEvent<PortableGeneratorComponent, GeneratorStartedEvent>(GeneratorTugged);
@@ -175,15 +180,19 @@ public sealed class PortableGeneratorSystem : SharedPortableGeneratorSystem
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;
@@ -191,9 +200,13 @@ public sealed class PortableGeneratorSystem : SharedPortableGeneratorSystem
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));
}
}

View File

@@ -22,6 +22,8 @@ public abstract class BasePowerNet<TNetType> : BaseNetConnectorNodeGroup<TNetTyp
PowerNetSystem = entMan.EntitySysManager.GetEntitySystem<PowerNetSystem>();
}
public bool IsConnectedNetwork => NodeCount > 1;
public void AddConsumer(PowerConsumerComponent consumer)
{
DebugTools.Assert(consumer.NetworkLoad.LinkedNetwork == default);

View File

@@ -5,6 +5,16 @@ namespace Content.Server.Power.NodeGroups
{
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);

View File

@@ -98,12 +98,17 @@ public sealed class PortableGeneratorComponentBuiState : BoundUserInterfaceState
{
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;
@@ -111,6 +116,7 @@ public sealed class PortableGeneratorComponentBuiState : BoundUserInterfaceState
MaximumPower = component.MaxTargetPower;
OptimalPower = component.OptimalPower;
On = component.On;
NetworkStats = networkStats;
}
}

View File

@@ -1,7 +1,7 @@
### 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
@@ -11,7 +11,7 @@ zzzz-fmt-pressure = { TOSTRING($divided, "G4") } { $places ->
}
# 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
@@ -23,7 +23,7 @@ zzzz-fmt-power-watts = { TOSTRING($divided, "G4") } { $places ->
# 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

View File

@@ -23,6 +23,9 @@ portable-generator-ui-eject = Eject
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}!

View File

@@ -232,11 +232,11 @@
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
@@ -276,10 +276,10 @@
- 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.