GatewayWindow.xaml.cs 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258
  1. using System.Numerics;
  2. using Content.Client.Computer;
  3. using Content.Client.Stylesheets;
  4. using Content.Client.UserInterface.Controls;
  5. using Content.Shared.Gateway;
  6. using Content.Shared.Shuttles.BUIStates;
  7. using Robust.Client.AutoGenerated;
  8. using Robust.Client.Graphics;
  9. using Robust.Client.UserInterface;
  10. using Robust.Client.UserInterface.Controls;
  11. using Robust.Client.UserInterface.XAML;
  12. using Robust.Shared.Timing;
  13. namespace Content.Client.Gateway.UI;
  14. [GenerateTypedNameReferences]
  15. public sealed partial class GatewayWindow : FancyWindow,
  16. IComputerWindow<EmergencyConsoleBoundUserInterfaceState>
  17. {
  18. private readonly IGameTiming _timing;
  19. public event Action<NetEntity>? OpenPortal;
  20. private List<GatewayDestinationData> _destinations = new();
  21. public NetEntity Owner;
  22. private NetEntity? _current;
  23. private TimeSpan _nextReady;
  24. private TimeSpan _cooldown;
  25. private TimeSpan _unlockTime;
  26. private TimeSpan _nextUnlock;
  27. /// <summary>
  28. /// Re-apply the state if the timer has elapsed.
  29. /// </summary>
  30. private GatewayBoundUserInterfaceState? _lastState;
  31. /// <summary>
  32. /// Are we currently waiting on an unlock timer.
  33. /// </summary>
  34. private bool _isUnlockPending = true;
  35. /// <summary>
  36. /// Are we currently waiting on a cooldown timer.
  37. /// </summary>
  38. private bool _isCooldownPending = true;
  39. public GatewayWindow()
  40. {
  41. RobustXamlLoader.Load(this);
  42. var dependencies = IoCManager.Instance!;
  43. _timing = dependencies.Resolve<IGameTiming>();
  44. NextUnlockBar.ForegroundStyleBoxOverride = new StyleBoxFlat(Color.FromHex("#C74EBD"));
  45. }
  46. public void SetEntity(NetEntity entity)
  47. {
  48. }
  49. public void UpdateState(GatewayBoundUserInterfaceState state)
  50. {
  51. _destinations = state.Destinations;
  52. _current = state.Current;
  53. _nextReady = state.NextReady;
  54. _cooldown = state.Cooldown;
  55. _unlockTime = state.UnlockTime;
  56. _nextUnlock = state.NextUnlock;
  57. _isUnlockPending = _nextUnlock >= _timing.CurTime;
  58. _isCooldownPending = _nextReady >= _timing.CurTime;
  59. Container.DisposeAllChildren();
  60. if (_destinations.Count == 0)
  61. {
  62. Container.AddChild(new BoxContainer()
  63. {
  64. HorizontalExpand = true,
  65. VerticalExpand = true,
  66. Children =
  67. {
  68. new Label()
  69. {
  70. Text = Loc.GetString("gateway-window-no-destinations"),
  71. HorizontalAlignment = HAlignment.Center
  72. }
  73. }
  74. });
  75. return;
  76. }
  77. var now = _timing.CurTime;
  78. foreach (var dest in _destinations)
  79. {
  80. var ent = dest.Entity;
  81. var name = dest.Name;
  82. var locked = dest.Locked && _nextUnlock > _timing.CurTime;
  83. var box = new BoxContainer()
  84. {
  85. Orientation = BoxContainer.LayoutOrientation.Horizontal,
  86. Margin = new Thickness(5f, 5f),
  87. };
  88. // HOW DO I ALIGN THESE GOODER
  89. var nameLabel = new RichTextLabel()
  90. {
  91. VerticalAlignment = VAlignment.Center,
  92. SetWidth = 156f,
  93. };
  94. nameLabel.SetMessage(name);
  95. box.AddChild(nameLabel);
  96. // Buffer
  97. box.AddChild(new Control()
  98. {
  99. HorizontalExpand = true,
  100. });
  101. bool Pressable() => ent == _current || ent == Owner;
  102. var buttonStripe = new StripeBack()
  103. {
  104. Visible = locked,
  105. HorizontalExpand = true,
  106. VerticalExpand = true,
  107. Margin = new Thickness(10f, 0f, 0f, 0f),
  108. Children =
  109. {
  110. new Label()
  111. {
  112. Text = Loc.GetString("gateway-window-locked"),
  113. HorizontalAlignment = HAlignment.Center,
  114. VerticalAlignment = VAlignment.Center,
  115. }
  116. }
  117. };
  118. var openButton = new Button()
  119. {
  120. Text = Loc.GetString("gateway-window-open-portal"),
  121. Pressed = Pressable(),
  122. ToggleMode = true,
  123. Disabled = now < _nextReady || Pressable(),
  124. HorizontalAlignment = HAlignment.Right,
  125. Margin = new Thickness(10f, 0f, 0f, 0f),
  126. Visible = !locked,
  127. SetHeight = 32f,
  128. };
  129. openButton.OnPressed += args =>
  130. {
  131. OpenPortal?.Invoke(ent);
  132. };
  133. if (Pressable())
  134. {
  135. openButton.AddStyleClass(StyleBase.ButtonCaution);
  136. }
  137. var buttonContainer = new BoxContainer()
  138. {
  139. Children =
  140. {
  141. buttonStripe,
  142. openButton,
  143. },
  144. SetSize = new Vector2(128f, 40f),
  145. };
  146. box.AddChild(buttonContainer);
  147. Container.AddChild(new PanelContainer()
  148. {
  149. PanelOverride = new StyleBoxFlat(new Color(30, 30, 34)),
  150. Margin = new Thickness(10f, 5f),
  151. Children =
  152. {
  153. box
  154. }
  155. });
  156. }
  157. _lastState = state;
  158. }
  159. protected override void FrameUpdate(FrameEventArgs args)
  160. {
  161. base.FrameUpdate(args);
  162. var now = _timing.CurTime;
  163. var dirtyState = false;
  164. // if its not going to close then show it as empty
  165. if (_nextUnlock == TimeSpan.Zero)
  166. {
  167. NextUnlockBar.Value = 1f;
  168. NextUnlockText.Text = "00:00";
  169. }
  170. else
  171. {
  172. var remaining = _nextUnlock - now;
  173. if (remaining < TimeSpan.Zero)
  174. {
  175. if (_isUnlockPending)
  176. {
  177. dirtyState = true;
  178. _isUnlockPending = false;
  179. }
  180. NextUnlockBar.Value = 1f;
  181. NextUnlockText.Text = "00:00";
  182. }
  183. else
  184. {
  185. NextUnlockBar.Value = 1f - (float) (remaining.TotalSeconds / _unlockTime.TotalSeconds);
  186. NextUnlockText.Text = $"{remaining.Minutes:00}:{remaining.Seconds:00}";
  187. }
  188. }
  189. // if its not going to close then show it as empty
  190. if (_current == null || _cooldown == TimeSpan.Zero)
  191. {
  192. NextReadyBar.Value = 1f;
  193. NextCloseText.Text = "00:00";
  194. }
  195. else
  196. {
  197. var remaining = _nextReady - now;
  198. if (remaining < TimeSpan.Zero)
  199. {
  200. if (_isCooldownPending)
  201. {
  202. dirtyState = true;
  203. _isCooldownPending = false;
  204. }
  205. NextReadyBar.Value = 1f;
  206. NextCloseText.Text = "00:00";
  207. }
  208. else
  209. {
  210. NextReadyBar.Value = 1f - (float) (remaining.TotalSeconds / _cooldown.TotalSeconds);
  211. NextCloseText.Text = $"{remaining.Minutes:00}:{remaining.Seconds:00}";
  212. }
  213. }
  214. if (dirtyState && _lastState != null)
  215. {
  216. // Refresh UI buttons.
  217. UpdateState(_lastState);
  218. }
  219. }
  220. }