diff --git a/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs b/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs index 3f7ccfb903..87b252e9d5 100644 --- a/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs +++ b/Content.Client/Power/PowerMonitoringWindow.xaml.Widgets.cs @@ -6,6 +6,7 @@ using Robust.Shared.Utility; using System.Diagnostics.CodeAnalysis; using System.Linq; using System.Numerics; +using Vector4 = Robust.Shared.Maths.Vector4; namespace Content.Client.Power; @@ -104,6 +105,26 @@ public sealed partial class PowerMonitoringWindow // Update power value // Don't use SI prefixes, just give the number in W, so that it is readily apparent which consumer is using a lot of power. button.PowerValue.Text = Loc.GetString("power-monitoring-window-button-value", ("value", Math.Round(entry.PowerValue).ToString("N0"))); + + // Update battery level if applicable + if (entry.BatteryLevel != null) + { + button.BatteryLevel.Value = entry.BatteryLevel.Value; + button.BatteryLevel.Visible = true; + + button.BatteryPercentage.Text = entry.BatteryLevel.Value.ToString("P0"); + button.BatteryPercentage.Visible = true; + + // Set progress bar color based on percentage + var color = Color.FromHsv(new Vector4(entry.BatteryLevel.Value * 0.33f, 1, 1, 1)); + + button.BatteryLevel.ForegroundStyleBoxOverride = new StyleBoxFlat { BackgroundColor = color }; + } + else + { + button.BatteryLevel.Visible = false; + button.BatteryPercentage.Visible = false; + } } private void UpdateEntrySourcesOrLoads(BoxContainer masterContainer, BoxContainer currentContainer, PowerMonitoringConsoleEntry[]? entries, SpriteSpecifier.Texture icon) @@ -443,6 +464,11 @@ public sealed class PowerMonitoringButton : Button public BoxContainer MainContainer; public TextureRect TextureRect; public Label NameLocalized; + + public ProgressBar BatteryLevel; + public PanelContainer BackgroundPanel; + public Label BatteryPercentage; + public Label PowerValue; public PowerMonitoringButton() @@ -478,6 +504,49 @@ public sealed class PowerMonitoringButton : Button MainContainer.AddChild(NameLocalized); + BatteryLevel = new ProgressBar() + { + SetWidth = 47f, + SetHeight = 20f, + Margin = new Thickness(15, 0, 0, 0), + MaxValue = 1, + Visible = false, + BackgroundStyleBoxOverride = new StyleBoxFlat { BackgroundColor = Color.Black }, + }; + + MainContainer.AddChild(BatteryLevel); + + BackgroundPanel = new PanelContainer + { + // Draw a half-transparent box over the battery level to make the text more readable. + PanelOverride = new StyleBoxFlat + { + BackgroundColor = new Color(0, 0, 0, 0.9f) + }, + HorizontalAlignment = HAlignment.Center, + VerticalAlignment = VAlignment.Center, + HorizontalExpand = true, + VerticalExpand = true, + // Box is undersized perfectly compared to the progress bar, so a little bit of the unaffected progress bar is visible. + SetSize = new Vector2(43f, 16f) + }; + + BatteryLevel.AddChild(BackgroundPanel); + + BatteryPercentage = new Label() + { + VerticalAlignment = VAlignment.Center, + HorizontalAlignment = HAlignment.Center, + Align = Label.AlignMode.Center, + SetWidth = 45f, + MinWidth = 20f, + Margin = new Thickness(10, -4, 10, 0), + ClipText = true, + Visible = false, + }; + + BackgroundPanel.AddChild(BatteryPercentage); + PowerValue = new Label() { HorizontalAlignment = HAlignment.Right, diff --git a/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs b/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs index a07d590461..0fc641ed28 100644 --- a/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs +++ b/Content.Server/Power/EntitySystems/PowerMonitoringConsoleSystem.cs @@ -350,19 +350,20 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori continue; // Get the device power stats - var powerValue = GetPrimaryPowerValues(ent, device, out var powerSupplied, out var powerUsage, out var batteryUsage); + var powerStats = GetPowerStats(ent, device); + //, out var powerSupplied, out var powerUsage, out var batteryUsage); // Update all running totals - totalSources += powerSupplied; - totalLoads += powerUsage; - totalBatteryUsage += batteryUsage; + totalSources += powerStats.PowerSupplied; + totalLoads += powerStats.PowerUsage; + totalBatteryUsage += powerStats.BatteryUsage; // Continue on if the device is not in the current focus group if (device.Group != component.FocusGroup) continue; // Generate a new console entry with which to populate the UI - var entry = new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), device.Group, powerValue); + var entry = new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), device.Group, powerStats.PowerValue, powerStats.BatteryLevel); allEntries.Add(entry); } @@ -426,28 +427,28 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori loadsForFocus.ToArray())); } - private double GetPrimaryPowerValues(EntityUid uid, PowerMonitoringDeviceComponent device, out double powerSupplied, out double powerUsage, out double batteryUsage) + private PowerStats GetPowerStats(EntityUid uid, PowerMonitoringDeviceComponent device) { - var powerValue = 0d; - powerSupplied = 0d; - powerUsage = 0d; - batteryUsage = 0d; + var stats = new PowerStats(); if (device.Group == PowerMonitoringConsoleGroup.Generator) { // This covers most power sources if (TryComp(uid, out var supplier)) { - powerValue = supplier.CurrentSupply; - powerSupplied += powerValue; + stats.PowerValue = supplier.CurrentSupply; + stats.PowerSupplied += stats.PowerValue; } // Edge case: radiation collectors else if (TryComp(uid, out var _) && TryComp(uid, out var battery)) { - powerValue = battery.NetworkBattery.CurrentSupply; - powerSupplied += powerValue; + stats.PowerValue = battery.NetworkBattery.CurrentSupply; + stats.PowerSupplied += stats.PowerValue; + + + stats.BatteryLevel = GetBatteryLevel(uid); } } @@ -458,18 +459,20 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori if (TryComp(uid, out var battery)) { - powerValue = battery.CurrentSupply; + stats.BatteryLevel = GetBatteryLevel(uid); + + stats.PowerValue = battery.CurrentSupply; // Load due to network battery recharging - powerUsage += Math.Max(battery.CurrentReceiving - battery.CurrentSupply, 0d); + stats.PowerUsage += Math.Max(battery.CurrentReceiving - battery.CurrentSupply, 0d); // Track battery usage - batteryUsage += Math.Max(battery.CurrentSupply - battery.CurrentReceiving, 0d); + stats.BatteryUsage += Math.Max(battery.CurrentSupply - battery.CurrentReceiving, 0d); // Records loads attached to APCs if (device.Group == PowerMonitoringConsoleGroup.APC && battery.Enabled) { - powerUsage += battery.NetworkBattery.LoadingNetworkDemand; + stats.PowerUsage += battery.NetworkBattery.LoadingNetworkDemand; } } } @@ -486,16 +489,28 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori if (childDevice.IsCollectionMaster && childDevice.ChildDevices.ContainsKey(uid)) continue; - var childPowerValue = GetPrimaryPowerValues(child, childDevice, out var childPowerSupplied, out var childPowerUsage, out var childBatteryUsage); + var childResult = GetPowerStats(child, childDevice); - powerValue += childPowerValue; - powerSupplied += childPowerSupplied; - powerUsage += childPowerUsage; - batteryUsage += childBatteryUsage; + stats.PowerValue += childResult.PowerValue; + stats.PowerSupplied += childResult.PowerSupplied; + stats.PowerUsage += childResult.PowerUsage; + stats.BatteryUsage += childResult.BatteryUsage; } } - return powerValue; + return stats; + } + + private float? GetBatteryLevel(EntityUid uid) + { + if (!TryComp(uid, out var battery)) + return null; + + var effectiveMax = battery.MaxCharge; + if (effectiveMax == 0) + effectiveMax = 1; + + return battery.CurrentCharge / effectiveMax; } private void GetSourcesForNode(EntityUid uid, Node node, out List sources) @@ -532,7 +547,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori continue; } - indexedSources.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, powerSupplier.CurrentSupply)); + indexedSources.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, powerSupplier.CurrentSupply, GetBatteryLevel(ent))); } } @@ -562,7 +577,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori continue; } - indexedSources.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, entBattery.CurrentSupply)); + indexedSources.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, entBattery.CurrentSupply, GetBatteryLevel(ent))); } } @@ -609,7 +624,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori for (int i = 0; i < sources.Count; i++) { var entry = sources[i]; - sources[i] = new PowerMonitoringConsoleEntry(entry.NetEntity, entry.Group, entry.PowerValue * powerFraction); + sources[i] = new PowerMonitoringConsoleEntry(entry.NetEntity, entry.Group, entry.PowerValue * powerFraction, entry.BatteryLevel); } } @@ -646,7 +661,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori continue; } - indexedLoads.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, powerConsumer.ReceivedPower)); + indexedLoads.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, powerConsumer.ReceivedPower, GetBatteryLevel(ent))); } } @@ -676,7 +691,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori continue; } - indexedLoads.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, battery.CurrentReceiving)); + indexedLoads.Add(ent, new PowerMonitoringConsoleEntry(EntityManager.GetNetEntity(ent), entDevice.Group, battery.CurrentReceiving, GetBatteryLevel(ent))); } } @@ -713,7 +728,7 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori for (int i = 0; i < indexedLoads.Values.Count; i++) { var entry = loads[i]; - loads[i] = new PowerMonitoringConsoleEntry(entry.NetEntity, entry.Group, entry.PowerValue * powerFraction); + loads[i] = new PowerMonitoringConsoleEntry(entry.NetEntity, entry.Group, entry.PowerValue * powerFraction, entry.BatteryLevel); } } @@ -990,4 +1005,13 @@ internal sealed partial class PowerMonitoringConsoleSystem : SharedPowerMonitori Dirty(uid, component); } + + private struct PowerStats + { + public double PowerValue { get; set; } + public double PowerSupplied { get; set; } + public double PowerUsage { get; set; } + public double BatteryUsage { get; set; } + public float? BatteryLevel { get; set; } + } } diff --git a/Content.Shared/Power/SharedPowerMonitoringConsoleComponent.cs b/Content.Shared/Power/SharedPowerMonitoringConsoleComponent.cs index 4d404f209c..d7e060cccd 100644 --- a/Content.Shared/Power/SharedPowerMonitoringConsoleComponent.cs +++ b/Content.Shared/Power/SharedPowerMonitoringConsoleComponent.cs @@ -102,14 +102,16 @@ public struct PowerMonitoringConsoleEntry public NetEntity NetEntity; public PowerMonitoringConsoleGroup Group; public double PowerValue; + public float? BatteryLevel; [NonSerialized] public PowerMonitoringDeviceMetaData? MetaData = null; - public PowerMonitoringConsoleEntry(NetEntity netEntity, PowerMonitoringConsoleGroup group, double powerValue = 0d) + public PowerMonitoringConsoleEntry(NetEntity netEntity, PowerMonitoringConsoleGroup group, double powerValue = 0d, float? batteryLevel = null) { NetEntity = netEntity; Group = group; PowerValue = powerValue; + BatteryLevel = batteryLevel; } } diff --git a/Resources/Locale/en-US/components/power-monitoring-component.ftl b/Resources/Locale/en-US/components/power-monitoring-component.ftl index 9433c9ea9e..3b54b400bc 100644 --- a/Resources/Locale/en-US/components/power-monitoring-component.ftl +++ b/Resources/Locale/en-US/components/power-monitoring-component.ftl @@ -22,7 +22,7 @@ power-monitoring-window-show-hv-cable = High voltage power-monitoring-window-show-mv-cable = Medium voltage power-monitoring-window-show-lv-cable = Low voltage -power-monitoring-window-flavor-left = [user@nanotrasen] $run power_net_query +power-monitoring-window-flavor-left = [user@nanotrasen] $run power_net_query power-monitoring-window-flavor-right = v1.3 power-monitoring-window-rogue-power-consumer = [color=white][font size=14][bold]! WARNING - ROGUE POWER CONSUMING DEVICE DETECTED ![/bold][/font][/color] power-monitoring-window-power-net-abnormalities = [color=white][font size=14][bold]CAUTION - ABNORMAL ACTIVITY IN POWER NET[/bold][/font][/color]