]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Make PACMANs a little better (#24604)
authorPieter-Jan Briers <pieterjan.briers+git@gmail.com>
Sat, 27 Jan 2024 02:53:43 +0000 (03:53 +0100)
committerGitHub <noreply@github.com>
Sat, 27 Jan 2024 02:53:43 +0000 (21:53 -0500)
* 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.

Content.Client/Power/Generator/GeneratorWindow.xaml
Content.Client/Power/Generator/GeneratorWindow.xaml.cs
Content.Server/Power/Generator/PortableGeneratorSystem.cs
Content.Server/Power/NodeGroups/BasePowerNet.cs
Content.Server/Power/NodeGroups/IBasePowerNet.cs
Content.Shared/Power/Generator/SharedPortableGeneratorComponent.cs
Resources/Locale/en-US/_lib.ftl
Resources/Locale/en-US/power/components/generator.ftl
Resources/Prototypes/Entities/Structures/Power/Generation/portable_generator.yml

index bae63a09edccc362f507e073bceb08ac5269867c..85127a96448a2f74c870bfd209f948b1bb17a688 100644 (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">
                 </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>
index 0b8f94ceaeb988491102d2e566ae11faa7e6be13..d1c5f47b07bedaa23ac168f819e453bfb2fa826b 100644 (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)
index 031e19b65dc8a2da24a36ff9ac89591b77cbe947..96016da5b09610606c77cbd0d873f73403166c95 100644 (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));
     }
 }
index 99fc64ad486150ea3757ea24983fee43fbd2e952..16287022253193227ebd9a8de8efe472b7b169bc 100644 (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);
index 5e9076f98ec179f302dcf736a1a03166143b187b..98f19a693e771d4444232dfb6de66a8055624681 100644 (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);
index 5b83ff1a9064fab9f832261ab1d094da51462437..a771802670cde8269fd59527da8c983b8735b79c 100644 (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;
     }
 }
 
index 9061392616c911a036bb701b433bb4e5ba821539..c901d0f461e6bc5df039916f9da4fcbbec8f77a9 100644 (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
index c66db1086b27268f46782ca53642350b128bc38f..afab640793c98963933051872b0b1039096192e6 100644 (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}!
index ce343892cc6a366d26d6828ee50b51ec40a82138..2fc6c1f5120760b2a8e132bc15f7bf455ac9d912 100644 (file)
   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.