1
0

PowerMonitoringWindow.xaml.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312
  1. using Content.Client.Pinpointer.UI;
  2. using Content.Client.UserInterface.Controls;
  3. using Content.Shared.Power;
  4. using Robust.Client.AutoGenerated;
  5. using Robust.Client.GameObjects;
  6. using Robust.Client.UserInterface.Controls;
  7. using Robust.Client.UserInterface.XAML;
  8. using Robust.Shared.Map;
  9. using Robust.Shared.Timing;
  10. using Robust.Shared.Utility;
  11. using System.Linq;
  12. namespace Content.Client.Power;
  13. [GenerateTypedNameReferences]
  14. public sealed partial class PowerMonitoringWindow : FancyWindow
  15. {
  16. [Dependency] private IEntityManager _entManager = default!;
  17. private readonly SpriteSystem _spriteSystem;
  18. [Dependency] private IGameTiming _gameTiming = default!;
  19. private const float BlinkFrequency = 1f;
  20. private NetEntity? _focusEntity;
  21. public event Action<NetEntity?, PowerMonitoringConsoleGroup>? SendPowerMonitoringConsoleMessageAction;
  22. private Dictionary<PowerMonitoringConsoleGroup, (SpriteSpecifier.Texture, Color)> _groupBlips = new()
  23. {
  24. { PowerMonitoringConsoleGroup.Generator, (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")), Color.Purple) },
  25. { PowerMonitoringConsoleGroup.SMES, (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_hexagon.png")), Color.OrangeRed) },
  26. { PowerMonitoringConsoleGroup.Substation, (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_square.png")), Color.Yellow) },
  27. { PowerMonitoringConsoleGroup.APC, (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_triangle.png")), Color.LimeGreen) },
  28. };
  29. public EntityUid Entity;
  30. public PowerMonitoringWindow()
  31. {
  32. RobustXamlLoader.Load(this);
  33. IoCManager.InjectDependencies(this);
  34. _spriteSystem = _entManager.System<SpriteSystem>();
  35. // Set trackable entity selected action
  36. NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
  37. // Update nav map
  38. NavMap.ForceNavMapUpdate();
  39. // Set UI tab titles
  40. MasterTabContainer.SetTabTitle(0, Loc.GetString("power-monitoring-window-label-sources"));
  41. MasterTabContainer.SetTabTitle(1, Loc.GetString("power-monitoring-window-label-smes"));
  42. MasterTabContainer.SetTabTitle(2, Loc.GetString("power-monitoring-window-label-substation"));
  43. MasterTabContainer.SetTabTitle(3, Loc.GetString("power-monitoring-window-label-apc"));
  44. // Track when the MasterTabContainer changes its tab
  45. MasterTabContainer.OnTabChanged += OnTabChanged;
  46. // Set UI toggles
  47. ShowHVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.HighVoltage);
  48. ShowMVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.MediumVoltage);
  49. ShowLVCable.OnToggled += _ => OnShowCableToggled(PowerMonitoringConsoleLineGroup.Apc);
  50. }
  51. public void SetEntity(EntityUid uid)
  52. {
  53. Entity = uid;
  54. // Pass owner to nav map
  55. NavMap.Owner = uid;
  56. // Set nav map grid uid
  57. var stationName = Loc.GetString("power-monitoring-window-unknown-location");
  58. if (_entManager.TryGetComponent<TransformComponent>(uid, out var xform))
  59. {
  60. NavMap.MapUid = xform.GridUid;
  61. // Assign station name
  62. if (_entManager.TryGetComponent<MetaDataComponent>(xform.GridUid, out var stationMetaData))
  63. stationName = stationMetaData.EntityName;
  64. var msg = new FormattedMessage();
  65. msg.AddMarkupOrThrow(Loc.GetString("power-monitoring-window-station-name", ("stationName", stationName)));
  66. StationName.SetMessage(msg);
  67. }
  68. else
  69. {
  70. StationName.SetMessage(stationName);
  71. NavMap.Visible = false;
  72. }
  73. }
  74. private void OnTabChanged(int tab)
  75. {
  76. SendPowerMonitoringConsoleMessageAction?.Invoke(_focusEntity, (PowerMonitoringConsoleGroup) tab);
  77. }
  78. private void OnShowCableToggled(PowerMonitoringConsoleLineGroup lineGroup)
  79. {
  80. if (!NavMap.HiddenLineGroups.Remove(lineGroup))
  81. NavMap.HiddenLineGroups.Add(lineGroup);
  82. }
  83. public void ShowEntites
  84. (double totalSources,
  85. double totalBatteryUsage,
  86. double totalLoads,
  87. PowerMonitoringConsoleEntry[] allEntries,
  88. PowerMonitoringConsoleEntry[] focusSources,
  89. PowerMonitoringConsoleEntry[] focusLoads,
  90. EntityCoordinates? monitorCoords)
  91. {
  92. if (!_entManager.TryGetComponent<PowerMonitoringConsoleComponent>(Entity, out var console))
  93. return;
  94. // Update power status text
  95. TotalSources.Text = Loc.GetString("power-monitoring-window-value", ("value", totalSources));
  96. TotalBatteryUsage.Text = Loc.GetString("power-monitoring-window-value", ("value", totalBatteryUsage));
  97. TotalLoads.Text = Loc.GetString("power-monitoring-window-value", ("value", totalLoads));
  98. // 10+% of station power is being drawn from batteries
  99. TotalBatteryUsage.FontColorOverride = (totalSources * 0.1111f) < totalBatteryUsage ? new Color(180, 0, 0) : Color.White;
  100. // Station generator and battery output is less than the current demand
  101. TotalLoads.FontColorOverride = (totalSources + totalBatteryUsage) < totalLoads &&
  102. !MathHelper.CloseToPercent(totalSources + totalBatteryUsage, totalLoads, 0.1f) ? new Color(180, 0, 0) : Color.White;
  103. // Update system warnings
  104. UpdateWarningLabel(console.Flags);
  105. // Reset nav map values
  106. NavMap.TrackedCoordinates.Clear();
  107. NavMap.TrackedEntities.Clear();
  108. // Draw entities on the nav map
  109. var entitiesOfInterest = new List<NetEntity>();
  110. if (_focusEntity != null)
  111. {
  112. entitiesOfInterest.Add(_focusEntity.Value);
  113. foreach (var entry in focusSources)
  114. entitiesOfInterest.Add(entry.NetEntity);
  115. foreach (var entry in focusLoads)
  116. entitiesOfInterest.Add(entry.NetEntity);
  117. }
  118. focusSources.Concat(focusLoads);
  119. foreach ((var netEntity, var metaData) in console.PowerMonitoringDeviceMetaData)
  120. {
  121. if (NavMap.Visible)
  122. AddTrackedEntityToNavMap(netEntity, metaData, entitiesOfInterest);
  123. }
  124. // Show monitor location
  125. var mon = _entManager.GetNetEntity(Entity);
  126. if (monitorCoords != null && mon.IsValid())
  127. {
  128. var texture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")));
  129. var blip = new NavMapBlip(monitorCoords.Value, texture, Color.Cyan, true, false);
  130. NavMap.TrackedEntities[mon] = blip;
  131. }
  132. // If the entry group doesn't match the current tab, the data is out dated, do not use it
  133. if (allEntries.Length > 0 && allEntries[0].Group != GetCurrentPowerMonitoringConsoleGroup())
  134. return;
  135. // Assign meta data to the console entries and sort them
  136. allEntries = GetUpdatedPowerMonitoringConsoleEntries(allEntries, console);
  137. focusSources = GetUpdatedPowerMonitoringConsoleEntries(focusSources, console);
  138. focusLoads = GetUpdatedPowerMonitoringConsoleEntries(focusLoads, console);
  139. // Get current console entry container
  140. BoxContainer currentContainer = SourcesList;
  141. switch (GetCurrentPowerMonitoringConsoleGroup())
  142. {
  143. case PowerMonitoringConsoleGroup.SMES:
  144. currentContainer = SMESList; break;
  145. case PowerMonitoringConsoleGroup.Substation:
  146. currentContainer = SubstationList; break;
  147. case PowerMonitoringConsoleGroup.APC:
  148. currentContainer = ApcList; break;
  149. }
  150. // Clear excess children from the container
  151. while (currentContainer.ChildCount > allEntries.Length)
  152. currentContainer.RemoveChild(currentContainer.GetChild(currentContainer.ChildCount - 1));
  153. // Update the remaining children
  154. for (var index = 0; index < allEntries.Length; index++)
  155. {
  156. var entry = allEntries[index];
  157. if (entry.NetEntity == _focusEntity)
  158. UpdateWindowConsoleEntry(currentContainer, index, entry, focusSources, focusLoads);
  159. else
  160. UpdateWindowConsoleEntry(currentContainer, index, entry);
  161. }
  162. // Auto-scroll renable
  163. if (_autoScrollAwaitsUpdate)
  164. {
  165. _autoScrollActive = true;
  166. _autoScrollAwaitsUpdate = false;
  167. }
  168. }
  169. private void AddTrackedEntityToNavMap(NetEntity netEntity, PowerMonitoringDeviceMetaData metaData, List<NetEntity> entitiesOfInterest)
  170. {
  171. if (!_groupBlips.TryGetValue(metaData.Group, out var data))
  172. return;
  173. var usedEntity = (metaData.CollectionMaster != null) ? metaData.CollectionMaster : netEntity;
  174. var coords = _entManager.GetCoordinates(metaData.Coordinates);
  175. var texture = data.Item1;
  176. var color = data.Item2;
  177. var blink = usedEntity == _focusEntity;
  178. var modulator = Color.White;
  179. if (_focusEntity != null && usedEntity != _focusEntity && !entitiesOfInterest.Contains(usedEntity.Value))
  180. modulator = Color.DimGray;
  181. var blip = new NavMapBlip(coords, _spriteSystem.Frame0(texture), color * modulator, blink);
  182. NavMap.TrackedEntities[netEntity] = blip;
  183. }
  184. private void SetTrackedEntityFromNavMap(NetEntity? netEntity)
  185. {
  186. if (netEntity == null)
  187. return;
  188. if (!_entManager.TryGetComponent<PowerMonitoringConsoleComponent>(Entity, out var console))
  189. return;
  190. if (!console.PowerMonitoringDeviceMetaData.TryGetValue(netEntity.Value, out var metaData))
  191. return;
  192. // Switch entity for master, if applicable
  193. // The master will always be in the same group as the entity
  194. if (metaData.CollectionMaster != null)
  195. netEntity = metaData.CollectionMaster;
  196. _focusEntity = netEntity;
  197. // Switch tabs
  198. SwitchTabsBasedOnPowerMonitoringConsoleGroup(metaData.Group);
  199. // Get the scroll position of the selected entity on the selected button the UI
  200. ActivateAutoScrollToFocus();
  201. // Send message to console that the focus has changed
  202. SendPowerMonitoringConsoleMessageAction?.Invoke(_focusEntity, metaData.Group);
  203. }
  204. protected override void FrameUpdate(FrameEventArgs args)
  205. {
  206. AutoScrollToFocus();
  207. // Warning sign pulse
  208. var lit = _gameTiming.RealTime.TotalSeconds % BlinkFrequency > BlinkFrequency / 2f;
  209. SystemWarningPanel.Modulate = lit ? Color.White : new Color(178, 178, 178);
  210. }
  211. private PowerMonitoringConsoleEntry[] GetUpdatedPowerMonitoringConsoleEntries(PowerMonitoringConsoleEntry[] entries, PowerMonitoringConsoleComponent console)
  212. {
  213. for (int i = 0; i < entries.Length; i++)
  214. {
  215. var entry = entries[i];
  216. if (!console.PowerMonitoringDeviceMetaData.TryGetValue(entry.NetEntity, out var metaData))
  217. continue;
  218. entries[i].MetaData = metaData;
  219. }
  220. // Sort all devices alphabetically by their entity name (not by power usage; otherwise their position on the UI will shift)
  221. Array.Sort(entries, AlphabeticalSort);
  222. return entries;
  223. }
  224. private int AlphabeticalSort(PowerMonitoringConsoleEntry x, PowerMonitoringConsoleEntry y)
  225. {
  226. if (x.MetaData?.EntityName == null)
  227. return -1;
  228. if (y.MetaData?.EntityName == null)
  229. return 1;
  230. return x.MetaData.Value.EntityName.CompareTo(y.MetaData.Value.EntityName);
  231. }
  232. }
  233. public struct PowerMonitoringConsoleTrackable
  234. {
  235. public EntityUid EntityUid;
  236. public PowerMonitoringConsoleGroup Group;
  237. public PowerMonitoringConsoleTrackable(EntityUid uid, PowerMonitoringConsoleGroup group)
  238. {
  239. EntityUid = uid;
  240. Group = group;
  241. }
  242. }