232 lines
8.9 KiB
C#
232 lines
8.9 KiB
C#
using System;
|
|
using Content.Client.UserInterface;
|
|
using Content.Client.UserInterface.Stylesheets;
|
|
using Content.Shared.GameObjects.Components.Power;
|
|
using Robust.Client.GameObjects.Components.UserInterface;
|
|
using Robust.Client.Graphics.Drawing;
|
|
using Robust.Client.UserInterface;
|
|
using Robust.Client.UserInterface.Controls;
|
|
using Robust.Client.UserInterface.CustomControls;
|
|
using Robust.Shared.GameObjects.Components.UserInterface;
|
|
using Robust.Shared.Interfaces.Timing;
|
|
using Robust.Shared.IoC;
|
|
using Robust.Shared.Maths;
|
|
|
|
namespace Content.Client.GameObjects.Components.Power
|
|
{
|
|
public class SolarControlConsoleBoundUserInterface : BoundUserInterface
|
|
{
|
|
[Dependency]
|
|
private IGameTiming _gameTiming = default;
|
|
|
|
private SolarControlWindow _window;
|
|
private SolarControlConsoleBoundInterfaceState _lastState = new SolarControlConsoleBoundInterfaceState(0, 0, 0, 0);
|
|
|
|
protected override void Open()
|
|
{
|
|
base.Open();
|
|
|
|
_window = new SolarControlWindow(_gameTiming);
|
|
_window.OnClose += Close;
|
|
_window.PanelRotation.OnTextEntered += (text) => {
|
|
double value;
|
|
if (double.TryParse(text.Text, out value))
|
|
{
|
|
SolarControlConsoleAdjustMessage msg = new SolarControlConsoleAdjustMessage();
|
|
msg.Rotation = Angle.FromDegrees(value);
|
|
msg.AngularVelocity = _lastState.AngularVelocity;
|
|
SendMessage(msg);
|
|
}
|
|
};
|
|
_window.PanelVelocity.OnTextEntered += (text) => {
|
|
double value;
|
|
if (double.TryParse(text.Text, out value))
|
|
{
|
|
SolarControlConsoleAdjustMessage msg = new SolarControlConsoleAdjustMessage();
|
|
msg.Rotation = _lastState.Rotation;
|
|
msg.AngularVelocity = Angle.FromDegrees(value / 60);
|
|
SendMessage(msg);
|
|
}
|
|
};
|
|
_window.OpenCenteredMinSize();
|
|
}
|
|
|
|
public SolarControlConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey)
|
|
{
|
|
}
|
|
|
|
private string FormatAngle(Angle d)
|
|
{
|
|
return d.Degrees.ToString("F1");
|
|
}
|
|
|
|
// The idea behind this is to prevent every update from the server
|
|
// breaking the textfield.
|
|
private void UpdateField(LineEdit field, string newValue)
|
|
{
|
|
if (!field.HasKeyboardFocus())
|
|
{
|
|
field.Text = newValue;
|
|
}
|
|
}
|
|
|
|
protected override void UpdateState(BoundUserInterfaceState state)
|
|
{
|
|
base.UpdateState(state);
|
|
|
|
SolarControlConsoleBoundInterfaceState scc = (SolarControlConsoleBoundInterfaceState) state;
|
|
_lastState = scc;
|
|
_window.NotARadar.UpdateState(scc);
|
|
_window.OutputPower.Text = ((int) Math.Floor(scc.OutputPower)).ToString();
|
|
_window.SunAngle.Text = FormatAngle(scc.TowardsSun);
|
|
UpdateField(_window.PanelRotation, FormatAngle(scc.Rotation));
|
|
UpdateField(_window.PanelVelocity, FormatAngle(scc.AngularVelocity * 60));
|
|
}
|
|
|
|
protected override void Dispose(bool disposing)
|
|
{
|
|
base.Dispose(disposing);
|
|
|
|
if (disposing)
|
|
{
|
|
_window.Dispose();
|
|
}
|
|
}
|
|
|
|
private sealed class SolarControlWindow : SS14Window
|
|
{
|
|
public Label OutputPower;
|
|
public Label SunAngle;
|
|
|
|
public SolarControlNotARadar NotARadar;
|
|
|
|
public LineEdit PanelRotation;
|
|
public LineEdit PanelVelocity;
|
|
|
|
public SolarControlWindow(IGameTiming igt)
|
|
{
|
|
Title = "Solar Control Window";
|
|
|
|
var rows = new GridContainer();
|
|
rows.Columns = 2;
|
|
|
|
// little secret: the reason I put the values
|
|
// in the first column is because otherwise the UI
|
|
// layouter autoresizes the window to be too small
|
|
rows.AddChild(new Label {Text = "Output Power:"});
|
|
rows.AddChild(new Label {Text = ""});
|
|
|
|
rows.AddChild(OutputPower = new Label {Text = "?"});
|
|
rows.AddChild(new Label {Text = "W"});
|
|
|
|
rows.AddChild(new Label {Text = "Sun Angle:"});
|
|
rows.AddChild(new Label {Text = ""});
|
|
|
|
rows.AddChild(SunAngle = new Label {Text = "?"});
|
|
rows.AddChild(new Label {Text = "°"});
|
|
|
|
rows.AddChild(new Label {Text = "Panel Angle:"});
|
|
rows.AddChild(new Label {Text = ""});
|
|
|
|
rows.AddChild(PanelRotation = new LineEdit());
|
|
rows.AddChild(new Label {Text = "°"});
|
|
|
|
rows.AddChild(new Label {Text = "Panel Angular Velocity:"});
|
|
rows.AddChild(new Label {Text = ""});
|
|
|
|
rows.AddChild(PanelVelocity = new LineEdit());
|
|
rows.AddChild(new Label {Text = "°/min."});
|
|
|
|
rows.AddChild(new Label {Text = "Press Enter to confirm."});
|
|
rows.AddChild(new Label {Text = ""});
|
|
|
|
PanelRotation.SizeFlagsHorizontal = SizeFlags.FillExpand;
|
|
PanelVelocity.SizeFlagsHorizontal = SizeFlags.FillExpand;
|
|
rows.SizeFlagsHorizontal = SizeFlags.Fill;
|
|
rows.SizeFlagsVertical = SizeFlags.Fill;
|
|
|
|
NotARadar = new SolarControlNotARadar(igt);
|
|
|
|
var outerColumns = new HBoxContainer();
|
|
outerColumns.AddChild(rows);
|
|
outerColumns.AddChild(NotARadar);
|
|
outerColumns.SizeFlagsHorizontal = SizeFlags.Fill;
|
|
outerColumns.SizeFlagsVertical = SizeFlags.Fill;
|
|
Contents.AddChild(outerColumns);
|
|
Resizable = false;
|
|
}
|
|
}
|
|
|
|
private sealed class SolarControlNotARadar : Control
|
|
{
|
|
// IoC doesn't apply here, so it's propagated from the parent class.
|
|
// This is used for client-side prediction of the panel rotation.
|
|
// This makes the display feel a lot smoother.
|
|
private IGameTiming _gameTiming;
|
|
|
|
private SolarControlConsoleBoundInterfaceState _lastState = new SolarControlConsoleBoundInterfaceState(0, 0, 0, 0);
|
|
|
|
private TimeSpan _lastStateTime = TimeSpan.Zero;
|
|
|
|
public const int SizeFull = 290;
|
|
public const int RadiusCircle = 140;
|
|
|
|
public SolarControlNotARadar(IGameTiming igt)
|
|
{
|
|
_gameTiming = igt;
|
|
}
|
|
|
|
public void UpdateState(SolarControlConsoleBoundInterfaceState ls)
|
|
{
|
|
_lastState = ls;
|
|
_lastStateTime = _gameTiming.CurTime;
|
|
}
|
|
|
|
protected override Vector2 CalculateMinimumSize()
|
|
{
|
|
return (SizeFull, SizeFull);
|
|
}
|
|
|
|
protected override void Draw(DrawingHandleScreen handle)
|
|
{
|
|
int point = SizeFull / 2;
|
|
Color fakeAA = new Color(0.08f, 0.08f, 0.08f);
|
|
Color gridLines = new Color(0.08f, 0.08f, 0.08f);
|
|
int panelExtentCutback = 4;
|
|
int gridLinesRadial = 8;
|
|
int gridLinesEquatorial = 8;
|
|
|
|
// Draw base
|
|
handle.DrawCircle((point, point), RadiusCircle + 1, fakeAA);
|
|
handle.DrawCircle((point, point), RadiusCircle, Color.Black);
|
|
|
|
// Draw grid lines
|
|
for (int i = 0; i < gridLinesEquatorial; i++)
|
|
{
|
|
handle.DrawCircle((point, point), (RadiusCircle / gridLinesEquatorial) * i, gridLines, false);
|
|
}
|
|
|
|
for (int i = 0; i < gridLinesRadial; i++)
|
|
{
|
|
Angle angle = (Math.PI / gridLinesRadial) * i;
|
|
Vector2 aExtent = angle.ToVec() * RadiusCircle;
|
|
handle.DrawLine((point, point) - aExtent, (point, point) + aExtent, gridLines);
|
|
}
|
|
|
|
// The rotations need to be adjusted because Y is inverted in Robust (like BYOND)
|
|
Vector2 rotMul = (1, -1);
|
|
|
|
Angle predictedPanelRotation = _lastState.Rotation + (_lastState.AngularVelocity * ((_gameTiming.CurTime - _lastStateTime).TotalSeconds));
|
|
|
|
Vector2 extent = predictedPanelRotation.ToVec() * rotMul * RadiusCircle;
|
|
Vector2 extentOrtho = (extent.Y, -extent.X);
|
|
handle.DrawLine((point, point) - extentOrtho, (point, point) + extentOrtho, Color.White);
|
|
handle.DrawLine((point, point) + (extent / panelExtentCutback), (point, point) + extent - (extent / panelExtentCutback), Color.DarkGray);
|
|
|
|
Vector2 sunExtent = _lastState.TowardsSun.ToVec() * rotMul * RadiusCircle;
|
|
handle.DrawLine((point, point) + sunExtent, (point, point), Color.Yellow);
|
|
}
|
|
}
|
|
}
|
|
}
|