SolarControlWindow.xaml.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171
  1. using System;
  2. using System.Numerics;
  3. using Content.Client.Computer;
  4. using Content.Shared.Solar;
  5. using JetBrains.Annotations;
  6. using Robust.Client.AutoGenerated;
  7. using Robust.Client.Graphics;
  8. using Robust.Client.UserInterface;
  9. using Robust.Client.UserInterface.Controls;
  10. using Robust.Client.UserInterface.CustomControls;
  11. using Robust.Client.UserInterface.XAML;
  12. using Robust.Shared.Timing;
  13. namespace Content.Client.Power
  14. {
  15. [GenerateTypedNameReferences]
  16. public sealed partial class SolarControlWindow : DefaultWindow, IComputerWindow<SolarControlConsoleBoundInterfaceState>
  17. {
  18. [ViewVariables]
  19. private SolarControlConsoleBoundInterfaceState _lastState = new(0, 0, 0, 0);
  20. public SolarControlWindow()
  21. {
  22. RobustXamlLoader.Load(this);
  23. }
  24. public void SetupComputerWindow(ComputerBoundUserInterfaceBase cb)
  25. {
  26. PanelRotation.OnTextEntered += text =>
  27. {
  28. if (!double.TryParse((string?) text.Text, out var value))
  29. return;
  30. SolarControlConsoleAdjustMessage msg = new()
  31. {
  32. Rotation = Angle.FromDegrees(value), AngularVelocity = _lastState.AngularVelocity,
  33. };
  34. cb.SendMessage(msg);
  35. // Predict this...
  36. _lastState.Rotation = msg.Rotation;
  37. NotARadar.UpdateState(_lastState);
  38. };
  39. PanelVelocity.OnTextEntered += text =>
  40. {
  41. if (!double.TryParse((string?) text.Text, out var value))
  42. return;
  43. SolarControlConsoleAdjustMessage msg = new()
  44. {
  45. Rotation = NotARadar.PredictedPanelRotation, AngularVelocity = Angle.FromDegrees(value / 60),
  46. };
  47. cb.SendMessage(msg);
  48. // Predict this...
  49. _lastState.Rotation = NotARadar.PredictedPanelRotation;
  50. _lastState.AngularVelocity = msg.AngularVelocity;
  51. NotARadar.UpdateState(_lastState);
  52. };
  53. }
  54. private static string FormatAngle(Angle d)
  55. {
  56. return d.Degrees.ToString("F1");
  57. }
  58. // The idea behind this is to prevent every update from the server
  59. // breaking the textfield.
  60. private static void UpdateField(LineEdit field, string newValue)
  61. {
  62. if (!field.HasKeyboardFocus())
  63. {
  64. field.Text = newValue;
  65. }
  66. }
  67. public void UpdateState(SolarControlConsoleBoundInterfaceState scc)
  68. {
  69. _lastState = scc;
  70. NotARadar.UpdateState(scc);
  71. OutputPower.Text = ((int) MathF.Floor(scc.OutputPower)).ToString();
  72. SunAngle.Text = FormatAngle(scc.TowardsSun);
  73. UpdateField(PanelRotation, FormatAngle(scc.Rotation));
  74. UpdateField(PanelVelocity, FormatAngle(scc.AngularVelocity * 60));
  75. }
  76. }
  77. public sealed class SolarControlNotARadar : Control
  78. {
  79. // This is used for client-side prediction of the panel rotation.
  80. // This makes the display feel a lot smoother.
  81. [Dependency] private readonly IGameTiming _gameTiming = default!;
  82. private SolarControlConsoleBoundInterfaceState _lastState = new(0, 0, 0, 0);
  83. private TimeSpan _lastStateTime = TimeSpan.Zero;
  84. public const int StandardSizeFull = 290;
  85. public const int StandardRadiusCircle = 140;
  86. public int SizeFull => (int) (StandardSizeFull * UIScale);
  87. public int RadiusCircle => (int) (StandardRadiusCircle * UIScale);
  88. public SolarControlNotARadar()
  89. {
  90. IoCManager.InjectDependencies(this);
  91. MinSize = new Vector2(SizeFull, SizeFull);
  92. }
  93. public void UpdateState(SolarControlConsoleBoundInterfaceState ls)
  94. {
  95. _lastState = ls;
  96. _lastStateTime = _gameTiming.CurTime;
  97. }
  98. public Angle PredictedPanelRotation => _lastState.Rotation + _lastState.AngularVelocity * (_gameTiming.CurTime - _lastStateTime).TotalSeconds;
  99. protected override void Draw(DrawingHandleScreen handle)
  100. {
  101. var point = SizeFull / 2;
  102. var fakeAA = new Color(0.08f, 0.08f, 0.08f);
  103. var gridLines = new Color(0.08f, 0.08f, 0.08f);
  104. var panelExtentCutback = 4;
  105. var gridLinesRadial = 8;
  106. var gridLinesEquatorial = 8;
  107. // Draw base
  108. handle.DrawCircle(new Vector2(point, point), RadiusCircle + 1, fakeAA);
  109. handle.DrawCircle(new Vector2(point, point), RadiusCircle, Color.Black);
  110. // Draw grid lines
  111. for (var i = 0; i < gridLinesEquatorial; i++)
  112. {
  113. handle.DrawCircle(new Vector2(point, point), (RadiusCircle / gridLinesEquatorial) * i, gridLines, false);
  114. }
  115. for (var i = 0; i < gridLinesRadial; i++)
  116. {
  117. Angle angle = Math.PI / gridLinesRadial * i;
  118. var aExtent = angle.ToVec() * RadiusCircle;
  119. handle.DrawLine(new Vector2(point, point) - aExtent, new Vector2(point, point) + aExtent, gridLines);
  120. }
  121. // The rotations need to be adjusted because Y is inverted in Robust (like BYOND)
  122. var rotMul = new Vector2(1, -1);
  123. // Hotfix corrections I don't understand
  124. var rotOfs = new Angle(Math.PI * -0.5);
  125. var predictedPanelRotation = PredictedPanelRotation;
  126. var extent = (predictedPanelRotation + rotOfs).ToVec() * rotMul * RadiusCircle;
  127. var extentOrtho = new Vector2(extent.Y, -extent.X);
  128. handle.DrawLine(new Vector2(point, point) - extentOrtho, new Vector2(point, point) + extentOrtho, Color.White);
  129. handle.DrawLine(new Vector2(point, point) + (extent / panelExtentCutback), new Vector2(point, point) + extent - (extent / panelExtentCutback), Color.DarkGray);
  130. var sunExtent = (_lastState.TowardsSun + rotOfs).ToVec() * rotMul * RadiusCircle;
  131. handle.DrawLine(new Vector2(point, point) + sunExtent, new Vector2(point, point), Color.Yellow);
  132. }
  133. }
  134. [UsedImplicitly]
  135. public sealed class SolarControlConsoleBoundUserInterface : ComputerBoundUserInterface<SolarControlWindow, SolarControlConsoleBoundInterfaceState>
  136. {
  137. public SolarControlConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
  138. {
  139. }
  140. }
  141. }