1
0

AtmosMonitoringConsoleWindow.xaml.cs 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435
  1. using Content.Client.Pinpointer.UI;
  2. using Content.Client.UserInterface.Controls;
  3. using Content.Shared.Atmos.Components;
  4. using Content.Shared.Prototypes;
  5. using Robust.Client.AutoGenerated;
  6. using Robust.Client.GameObjects;
  7. using Robust.Client.UserInterface;
  8. using Robust.Client.UserInterface.Controls;
  9. using Robust.Client.UserInterface.XAML;
  10. using Robust.Shared.Map;
  11. using Robust.Shared.Prototypes;
  12. using Robust.Shared.Timing;
  13. using Robust.Shared.Utility;
  14. using System.Diagnostics.CodeAnalysis;
  15. using System.Linq;
  16. namespace Content.Client.Atmos.Consoles;
  17. [GenerateTypedNameReferences]
  18. public sealed partial class AtmosMonitoringConsoleWindow : FancyWindow
  19. {
  20. private readonly IEntityManager _entManager;
  21. private readonly IPrototypeManager _protoManager;
  22. private readonly SpriteSystem _spriteSystem;
  23. private EntityUid? _owner;
  24. private NetEntity? _focusEntity;
  25. private int? _focusNetId;
  26. private bool _autoScrollActive = false;
  27. private readonly Color _unfocusedDeviceColor = Color.DimGray;
  28. private ProtoId<NavMapBlipPrototype> _navMapConsoleProtoId = "NavMapConsole";
  29. private ProtoId<NavMapBlipPrototype> _gasPipeSensorProtoId = "GasPipeSensor";
  30. public AtmosMonitoringConsoleWindow(AtmosMonitoringConsoleBoundUserInterface userInterface, EntityUid? owner)
  31. {
  32. RobustXamlLoader.Load(this);
  33. _entManager = IoCManager.Resolve<IEntityManager>();
  34. _protoManager = IoCManager.Resolve<IPrototypeManager>();
  35. _spriteSystem = _entManager.System<SpriteSystem>();
  36. // Pass the owner to nav map
  37. _owner = owner;
  38. NavMap.Owner = _owner;
  39. // Set nav map grid uid
  40. var stationName = Loc.GetString("atmos-monitoring-window-unknown-location");
  41. EntityCoordinates? consoleCoords = null;
  42. if (_entManager.TryGetComponent<TransformComponent>(owner, out var xform))
  43. {
  44. consoleCoords = xform.Coordinates;
  45. NavMap.MapUid = xform.GridUid;
  46. // Assign station name
  47. if (_entManager.TryGetComponent<MetaDataComponent>(xform.GridUid, out var stationMetaData))
  48. stationName = stationMetaData.EntityName;
  49. var msg = new FormattedMessage();
  50. msg.TryAddMarkup(Loc.GetString("atmos-monitoring-window-station-name", ("stationName", stationName)), out _);
  51. StationName.SetMessage(msg);
  52. }
  53. else
  54. {
  55. StationName.SetMessage(stationName);
  56. NavMap.Visible = false;
  57. }
  58. // Set trackable entity selected action
  59. NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
  60. // Update nav map
  61. NavMap.ForceNavMapUpdate();
  62. // Set tab container headers
  63. MasterTabContainer.SetTabTitle(0, Loc.GetString("atmos-monitoring-window-tab-networks"));
  64. // Set UI toggles
  65. ShowPipeNetwork.OnToggled += _ => OnShowPipeNetworkToggled();
  66. ShowGasPipeSensors.OnToggled += _ => OnShowGasPipeSensors();
  67. // Set nav map colors
  68. if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner, out var console))
  69. return;
  70. NavMap.TileColor = console.NavMapTileColor;
  71. NavMap.WallColor = console.NavMapWallColor;
  72. // Initalize
  73. UpdateUI(consoleCoords, Array.Empty<AtmosMonitoringConsoleEntry>());
  74. }
  75. #region Toggle handling
  76. private void OnShowPipeNetworkToggled()
  77. {
  78. if (_owner == null)
  79. return;
  80. if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
  81. return;
  82. NavMap.ShowPipeNetwork = ShowPipeNetwork.Pressed;
  83. foreach (var (netEnt, device) in console.AtmosDevices)
  84. {
  85. if (device.NavMapBlip == _gasPipeSensorProtoId)
  86. continue;
  87. if (ShowPipeNetwork.Pressed)
  88. AddTrackedEntityToNavMap(device);
  89. else
  90. NavMap.TrackedEntities.Remove(netEnt);
  91. }
  92. }
  93. private void OnShowGasPipeSensors()
  94. {
  95. if (_owner == null)
  96. return;
  97. if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
  98. return;
  99. foreach (var (netEnt, device) in console.AtmosDevices)
  100. {
  101. if (device.NavMapBlip != _gasPipeSensorProtoId)
  102. continue;
  103. if (ShowGasPipeSensors.Pressed)
  104. AddTrackedEntityToNavMap(device, true);
  105. else
  106. NavMap.TrackedEntities.Remove(netEnt);
  107. }
  108. }
  109. #endregion
  110. public void UpdateUI
  111. (EntityCoordinates? consoleCoords,
  112. AtmosMonitoringConsoleEntry[] atmosNetworks)
  113. {
  114. if (_owner == null)
  115. return;
  116. if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
  117. return;
  118. // Reset nav map values
  119. NavMap.TrackedCoordinates.Clear();
  120. NavMap.TrackedEntities.Clear();
  121. if (_focusEntity != null && !console.AtmosDevices.Any(x => x.Key == _focusEntity))
  122. ClearFocus();
  123. // Add tracked entities to the nav map
  124. UpdateNavMapBlips();
  125. // Show the monitor location
  126. var consoleNetEnt = _entManager.GetNetEntity(_owner);
  127. if (consoleCoords != null && consoleNetEnt != null)
  128. {
  129. var proto = _protoManager.Index(_navMapConsoleProtoId);
  130. if (proto.TexturePaths != null && proto.TexturePaths.Length != 0)
  131. {
  132. var texture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(proto.TexturePaths[0]));
  133. var blip = new NavMapBlip(consoleCoords.Value, texture, proto.Color, proto.Blinks, proto.Selectable);
  134. NavMap.TrackedEntities[consoleNetEnt.Value] = blip;
  135. }
  136. }
  137. // Update the nav map
  138. NavMap.ForceNavMapUpdate();
  139. // Clear excess children from the tables
  140. while (AtmosNetworksTable.ChildCount > atmosNetworks.Length)
  141. AtmosNetworksTable.RemoveChild(AtmosNetworksTable.GetChild(AtmosNetworksTable.ChildCount - 1));
  142. // Update all entries in each table
  143. for (int index = 0; index < atmosNetworks.Length; index++)
  144. {
  145. var entry = atmosNetworks.ElementAt(index);
  146. UpdateUIEntry(entry, index, AtmosNetworksTable, console);
  147. }
  148. }
  149. private void UpdateNavMapBlips()
  150. {
  151. if (_owner == null || !_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner.Value, out var console))
  152. return;
  153. if (NavMap.Visible)
  154. {
  155. foreach (var (netEnt, device) in console.AtmosDevices)
  156. {
  157. // Update the focus network ID, incase it has changed
  158. if (_focusEntity == netEnt)
  159. {
  160. _focusNetId = device.NetId;
  161. NavMap.FocusNetId = _focusNetId;
  162. }
  163. var isSensor = device.NavMapBlip == _gasPipeSensorProtoId;
  164. // Skip network devices if the toggled is off
  165. if (!ShowPipeNetwork.Pressed && !isSensor)
  166. continue;
  167. // Skip gas pipe sensors if the toggle is off
  168. if (!ShowGasPipeSensors.Pressed && isSensor)
  169. continue;
  170. AddTrackedEntityToNavMap(device, isSensor);
  171. }
  172. }
  173. }
  174. private void AddTrackedEntityToNavMap(AtmosDeviceNavMapData metaData, bool isSensor = false)
  175. {
  176. var proto = _protoManager.Index(metaData.NavMapBlip);
  177. if (proto.TexturePaths == null || proto.TexturePaths.Length == 0)
  178. return;
  179. var idx = Math.Clamp((int)metaData.Direction / 2, 0, proto.TexturePaths.Length - 1);
  180. var texture = proto.TexturePaths.Length > 0 ? proto.TexturePaths[idx] : proto.TexturePaths[0];
  181. var color = isSensor ? proto.Color : proto.Color * metaData.PipeColor;
  182. if (_focusNetId != null && metaData.NetId != _focusNetId)
  183. color *= _unfocusedDeviceColor;
  184. var blinks = proto.Blinks || _focusEntity == metaData.NetEntity;
  185. var coords = _entManager.GetCoordinates(metaData.NetCoordinates);
  186. var blip = new NavMapBlip(coords, _spriteSystem.Frame0(new SpriteSpecifier.Texture(texture)), color, blinks, proto.Selectable, proto.Scale);
  187. NavMap.TrackedEntities[metaData.NetEntity] = blip;
  188. }
  189. private void UpdateUIEntry(AtmosMonitoringConsoleEntry data, int index, Control table, AtmosMonitoringConsoleComponent console)
  190. {
  191. // Make new UI entry if required
  192. if (index >= table.ChildCount)
  193. {
  194. var newEntryContainer = new AtmosMonitoringEntryContainer(data);
  195. // On click
  196. newEntryContainer.FocusButton.OnButtonUp += args =>
  197. {
  198. if (_focusEntity == newEntryContainer.Data.NetEntity)
  199. {
  200. ClearFocus();
  201. }
  202. else
  203. {
  204. SetFocus(newEntryContainer.Data.NetEntity, newEntryContainer.Data.NetId);
  205. var coords = _entManager.GetCoordinates(newEntryContainer.Data.Coordinates);
  206. NavMap.CenterToCoordinates(coords);
  207. }
  208. // Update affected UI elements across all tables
  209. UpdateConsoleTable(console, AtmosNetworksTable, _focusEntity);
  210. };
  211. // Add the entry to the current table
  212. table.AddChild(newEntryContainer);
  213. }
  214. // Update values and UI elements
  215. var tableChild = table.GetChild(index);
  216. if (tableChild is not AtmosMonitoringEntryContainer)
  217. {
  218. table.RemoveChild(tableChild);
  219. UpdateUIEntry(data, index, table, console);
  220. return;
  221. }
  222. var entryContainer = (AtmosMonitoringEntryContainer)tableChild;
  223. entryContainer.UpdateEntry(data, data.NetEntity == _focusEntity);
  224. }
  225. private void UpdateConsoleTable(AtmosMonitoringConsoleComponent console, Control table, NetEntity? currTrackedEntity)
  226. {
  227. foreach (var tableChild in table.Children)
  228. {
  229. if (tableChild is not AtmosAlarmEntryContainer)
  230. continue;
  231. var entryContainer = (AtmosAlarmEntryContainer)tableChild;
  232. if (entryContainer.NetEntity != currTrackedEntity)
  233. entryContainer.RemoveAsFocus();
  234. else if (entryContainer.NetEntity == currTrackedEntity)
  235. entryContainer.SetAsFocus();
  236. }
  237. }
  238. private void SetTrackedEntityFromNavMap(NetEntity? focusEntity)
  239. {
  240. if (focusEntity == null)
  241. return;
  242. if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner, out var console))
  243. return;
  244. foreach (var (netEnt, device) in console.AtmosDevices)
  245. {
  246. if (netEnt != focusEntity)
  247. continue;
  248. if (device.NavMapBlip != _gasPipeSensorProtoId)
  249. return;
  250. // Set new focus
  251. SetFocus(focusEntity.Value, device.NetId);
  252. // Get the scroll position of the selected entity on the selected button the UI
  253. ActivateAutoScrollToFocus();
  254. break;
  255. }
  256. }
  257. protected override void FrameUpdate(FrameEventArgs args)
  258. {
  259. AutoScrollToFocus();
  260. }
  261. private void ActivateAutoScrollToFocus()
  262. {
  263. _autoScrollActive = true;
  264. }
  265. private void AutoScrollToFocus()
  266. {
  267. if (!_autoScrollActive)
  268. return;
  269. var scroll = AtmosNetworksTable.Parent as ScrollContainer;
  270. if (scroll == null)
  271. return;
  272. if (!TryGetNextScrollPosition(out float? nextScrollPosition))
  273. return;
  274. scroll.VScrollTarget = nextScrollPosition.Value;
  275. if (MathHelper.CloseToPercent(scroll.VScroll, scroll.VScrollTarget))
  276. _autoScrollActive = false;
  277. }
  278. private bool TryGetNextScrollPosition([NotNullWhen(true)] out float? nextScrollPosition)
  279. {
  280. nextScrollPosition = null;
  281. var scroll = AtmosNetworksTable.Parent as ScrollContainer;
  282. if (scroll == null)
  283. return false;
  284. var container = scroll.Children.ElementAt(0) as BoxContainer;
  285. if (container == null || container.Children.Count() == 0)
  286. return false;
  287. // Exit if the heights of the children haven't been initialized yet
  288. if (!container.Children.Any(x => x.Height > 0))
  289. return false;
  290. nextScrollPosition = 0;
  291. foreach (var control in container.Children)
  292. {
  293. if (control is not AtmosMonitoringEntryContainer)
  294. continue;
  295. var entry = (AtmosMonitoringEntryContainer)control;
  296. if (entry.Data.NetEntity == _focusEntity)
  297. return true;
  298. nextScrollPosition += control.Height;
  299. }
  300. // Failed to find control
  301. nextScrollPosition = null;
  302. return false;
  303. }
  304. private void SetFocus(NetEntity focusEntity, int focusNetId)
  305. {
  306. _focusEntity = focusEntity;
  307. _focusNetId = focusNetId;
  308. NavMap.FocusNetId = focusNetId;
  309. OnFocusChanged();
  310. }
  311. private void ClearFocus()
  312. {
  313. _focusEntity = null;
  314. _focusNetId = null;
  315. NavMap.FocusNetId = null;
  316. OnFocusChanged();
  317. }
  318. private void OnFocusChanged()
  319. {
  320. UpdateNavMapBlips();
  321. NavMap.ForceNavMapUpdate();
  322. if (!_entManager.TryGetComponent<AtmosMonitoringConsoleComponent>(_owner, out var console))
  323. return;
  324. for (int index = 0; index < AtmosNetworksTable.ChildCount; index++)
  325. {
  326. var entry = (AtmosMonitoringEntryContainer)AtmosNetworksTable.GetChild(index);
  327. if (entry == null)
  328. continue;
  329. UpdateUIEntry(entry.Data, index, AtmosNetworksTable, console);
  330. }
  331. }
  332. }