Add docking window to shuttle consoles (#8756)
* lewd * A * Realtime * Sleepy dork * Draw radar position * Accurate infiltrator * experiments * Better drawing * Labels * I need aan adult * Cleanup * Show toggles * display I guess * A * fix * fix * cleanupsies * Bit more polish * Make sure mass scanners actually work * Remove dummy state * fren * opposite * aghost crash * comment * What's in a name * woops * Show docking ports * Dock highlighting * Drawing dock * Shitty docks * Lots of docking drawing * Autodock working * dork * More graceful shutdown * zoomies * Lines and distance change * revert * Good enough * cleanup * Fix default range * Dock UI and loc update * Update on undock * Loc fixes
This commit is contained in:
@@ -1,8 +0,0 @@
|
|||||||
<DefaultWindow xmlns="https://spacestation14.io"
|
|
||||||
xmlns:radar="clr-namespace:Content.Client.Radar"
|
|
||||||
Title="{Loc 'radar-window-title'}"
|
|
||||||
Resizable="False">
|
|
||||||
<BoxContainer Orientation="Horizontal">
|
|
||||||
<radar:RadarControl Name="Radar"/>
|
|
||||||
</BoxContainer>
|
|
||||||
</DefaultWindow>
|
|
||||||
@@ -1,116 +0,0 @@
|
|||||||
using System;
|
|
||||||
using Content.Client.Computer;
|
|
||||||
using Content.Shared.Radar;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Robust.Client.AutoGenerated;
|
|
||||||
using Robust.Client.GameObjects;
|
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
using Robust.Client.UserInterface.CustomControls;
|
|
||||||
using Robust.Client.UserInterface.XAML;
|
|
||||||
using Robust.Shared.Maths;
|
|
||||||
|
|
||||||
namespace Content.Client.Radar;
|
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
|
||||||
public sealed partial class RadarConsoleWindow : DefaultWindow, IComputerWindow<RadarConsoleBoundInterfaceState>
|
|
||||||
{
|
|
||||||
public RadarConsoleWindow()
|
|
||||||
{
|
|
||||||
RobustXamlLoader.Load(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetupComputerWindow(ComputerBoundUserInterfaceBase cb)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateState(RadarConsoleBoundInterfaceState scc)
|
|
||||||
{
|
|
||||||
Radar.UpdateState(scc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public sealed class RadarControl : Control
|
|
||||||
{
|
|
||||||
private const int MinimapRadius = 256;
|
|
||||||
private const int MinimapMargin = 4;
|
|
||||||
private const float GridLinesDistance = 32f;
|
|
||||||
|
|
||||||
private float _radarRange = 256f;
|
|
||||||
private RadarConsoleBoundInterfaceState _lastState = new(256f, Array.Empty<RadarObjectData>());
|
|
||||||
|
|
||||||
private int SizeFull => (int) ((MinimapRadius + MinimapMargin) * 2 * UIScale);
|
|
||||||
private int ScaledMinimapRadius => (int) (MinimapRadius * UIScale);
|
|
||||||
private float MinimapScale => _radarRange != 0 ? ScaledMinimapRadius / _radarRange : 0f;
|
|
||||||
|
|
||||||
public RadarControl()
|
|
||||||
{
|
|
||||||
MinSize = (SizeFull, SizeFull);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateState(RadarConsoleBoundInterfaceState ls)
|
|
||||||
{
|
|
||||||
if (!_radarRange.Equals(ls.Range))
|
|
||||||
{
|
|
||||||
_radarRange = ls.Range;
|
|
||||||
}
|
|
||||||
|
|
||||||
_lastState = ls;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleScreen handle)
|
|
||||||
{
|
|
||||||
var point = SizeFull / 2;
|
|
||||||
var fakeAA = new Color(0.08f, 0.08f, 0.08f);
|
|
||||||
var gridLines = new Color(0.08f, 0.08f, 0.08f);
|
|
||||||
var gridLinesRadial = 8;
|
|
||||||
var gridLinesEquatorial = (int) Math.Floor(_radarRange / GridLinesDistance);
|
|
||||||
|
|
||||||
handle.DrawCircle((point, point), ScaledMinimapRadius + 1, fakeAA);
|
|
||||||
handle.DrawCircle((point, point), ScaledMinimapRadius, Color.Black);
|
|
||||||
|
|
||||||
for (var i = 1; i < gridLinesEquatorial + 1; i++)
|
|
||||||
{
|
|
||||||
handle.DrawCircle((point, point), GridLinesDistance * MinimapScale * i, gridLines, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < gridLinesRadial; i++)
|
|
||||||
{
|
|
||||||
Angle angle = (Math.PI / gridLinesRadial) * i;
|
|
||||||
var aExtent = angle.ToVec() * ScaledMinimapRadius;
|
|
||||||
handle.DrawLine((point, point) - aExtent, (point, point) + aExtent, gridLines);
|
|
||||||
}
|
|
||||||
|
|
||||||
handle.DrawLine((point, point) + new Vector2(8, 8), (point, point) - new Vector2(0, 8), Color.Yellow);
|
|
||||||
handle.DrawLine((point, point) + new Vector2(-8, 8), (point, point) - new Vector2(0, 8), Color.Yellow);
|
|
||||||
|
|
||||||
foreach (var obj in _lastState.Objects)
|
|
||||||
{
|
|
||||||
var minimapPos = obj.Position * MinimapScale;
|
|
||||||
var radius = obj.Radius * MinimapScale;
|
|
||||||
|
|
||||||
if (minimapPos.Length + radius > ScaledMinimapRadius)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
switch (obj.Shape)
|
|
||||||
{
|
|
||||||
case RadarObjectShape.CircleFilled:
|
|
||||||
case RadarObjectShape.Circle:
|
|
||||||
{
|
|
||||||
handle.DrawCircle(minimapPos + point, radius, obj.Color, obj.Shape == RadarObjectShape.CircleFilled);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[UsedImplicitly]
|
|
||||||
public sealed class RadarConsoleBoundUserInterface : ComputerBoundUserInterface<RadarConsoleWindow, RadarConsoleBoundInterfaceState>
|
|
||||||
{
|
|
||||||
public RadarConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) {}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,29 @@
|
|||||||
|
using Content.Client.Shuttles.UI;
|
||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.BUI;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed class RadarConsoleBoundUserInterface : BoundUserInterface
|
||||||
|
{
|
||||||
|
private RadarConsoleWindow? _window;
|
||||||
|
|
||||||
|
public RadarConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) {}
|
||||||
|
|
||||||
|
protected override void Open()
|
||||||
|
{
|
||||||
|
base.Open();
|
||||||
|
_window = new RadarConsoleWindow();
|
||||||
|
_window?.OpenCentered();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
|
{
|
||||||
|
base.UpdateState(state);
|
||||||
|
if (state is not RadarConsoleBoundInterfaceState cState) return;
|
||||||
|
|
||||||
|
_window?.UpdateState(cState);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,70 @@
|
|||||||
|
using Content.Client.Shuttles.UI;
|
||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Content.Shared.Shuttles.Events;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.BUI;
|
||||||
|
|
||||||
|
[UsedImplicitly]
|
||||||
|
public sealed class ShuttleConsoleBoundUserInterface : BoundUserInterface
|
||||||
|
{
|
||||||
|
private ShuttleConsoleWindow? _window;
|
||||||
|
|
||||||
|
public ShuttleConsoleBoundUserInterface(ClientUserInterfaceComponent owner, object uiKey) : base(owner, uiKey) {}
|
||||||
|
|
||||||
|
protected override void Open()
|
||||||
|
{
|
||||||
|
base.Open();
|
||||||
|
_window = new ShuttleConsoleWindow();
|
||||||
|
_window.ShuttleModePressed += OnShuttleModePressed;
|
||||||
|
_window.UndockPressed += OnUndockPressed;
|
||||||
|
_window.StartAutodockPressed += OnAutodockPressed;
|
||||||
|
_window.StopAutodockPressed += OnStopAutodockPressed;
|
||||||
|
_window.OpenCentered();
|
||||||
|
_window.OnClose += OnClose;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnClose()
|
||||||
|
{
|
||||||
|
Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
|
||||||
|
if (disposing)
|
||||||
|
{
|
||||||
|
_window?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnStopAutodockPressed(EntityUid obj)
|
||||||
|
{
|
||||||
|
SendMessage(new StopAutodockRequestMessage() {Entity = obj});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnAutodockPressed(EntityUid obj)
|
||||||
|
{
|
||||||
|
SendMessage(new AutodockRequestMessage() {Entity = obj});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUndockPressed(EntityUid obj)
|
||||||
|
{
|
||||||
|
SendMessage(new UndockRequestMessage() {Entity = obj});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnShuttleModePressed(ShuttleMode obj)
|
||||||
|
{
|
||||||
|
SendMessage(new ShuttleModeRequestMessage() {Mode = obj});
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
|
{
|
||||||
|
base.UpdateState(state);
|
||||||
|
if (state is not ShuttleConsoleBoundInterfaceState cState) return;
|
||||||
|
_window?.UpdateState(cState);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,13 +1,7 @@
|
|||||||
using Content.Shared.Shuttles;
|
|
||||||
using Content.Shared.Shuttles.Components;
|
using Content.Shared.Shuttles.Components;
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
|
|
||||||
namespace Content.Client.Shuttles
|
namespace Content.Client.Shuttles
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(SharedShuttleConsoleComponent))]
|
public sealed class ShuttleConsoleComponent : SharedShuttleConsoleComponent {}
|
||||||
internal sealed class ShuttleConsoleComponent : SharedShuttleConsoleComponent
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
using Content.Shared.Shuttles;
|
|
||||||
|
|
||||||
namespace Content.Client.Shuttles
|
|
||||||
{
|
|
||||||
internal sealed class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
21
Content.Client/Shuttles/Systems/DockingSystem.cs
Normal file
21
Content.Client/Shuttles/Systems/DockingSystem.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using Content.Shared.Shuttles.Events;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.Systems;
|
||||||
|
|
||||||
|
public sealed class DockingSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public void StartAutodock(EntityUid uid)
|
||||||
|
{
|
||||||
|
RaiseNetworkEvent(new AutodockRequestMessage {Entity = uid});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void StopAutodock(EntityUid uid)
|
||||||
|
{
|
||||||
|
RaiseNetworkEvent(new StopAutodockRequestMessage() {Entity = uid});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Undock(EntityUid uid)
|
||||||
|
{
|
||||||
|
RaiseNetworkEvent(new UndockRequestMessage() {Entity = uid});
|
||||||
|
}
|
||||||
|
}
|
||||||
8
Content.Client/Shuttles/Systems/RadarConsoleSystem.cs
Normal file
8
Content.Client/Shuttles/Systems/RadarConsoleSystem.cs
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
using Content.Shared.Shuttles.Systems;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.Systems;
|
||||||
|
|
||||||
|
public sealed class RadarConsoleSystem : SharedRadarConsoleSystem
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
37
Content.Client/Shuttles/Systems/ShuttleConsoleSystem.cs
Normal file
37
Content.Client/Shuttles/Systems/ShuttleConsoleSystem.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Content.Shared.Shuttles.Events;
|
||||||
|
using Content.Shared.Shuttles.Systems;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.Systems
|
||||||
|
{
|
||||||
|
public sealed class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<PilotComponent, ComponentHandleState>(OnHandleState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHandleState(EntityUid uid, PilotComponent component, ref ComponentHandleState args)
|
||||||
|
{
|
||||||
|
if (args.Current is not PilotComponentState state) return;
|
||||||
|
|
||||||
|
var console = state.Console.GetValueOrDefault();
|
||||||
|
if (!console.IsValid())
|
||||||
|
{
|
||||||
|
component.Console = null;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryComp<ShuttleConsoleComponent>(console, out var shuttleConsoleComponent))
|
||||||
|
{
|
||||||
|
Logger.Warning($"Unable to set Helmsman console to {console}");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
component.Console = shuttleConsoleComponent;
|
||||||
|
ActionBlockerSystem.UpdateCanMove(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
251
Content.Client/Shuttles/UI/DockingControl.cs
Normal file
251
Content.Client/Shuttles/UI/DockingControl.cs
Normal file
@@ -0,0 +1,251 @@
|
|||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Physics;
|
||||||
|
using Robust.Shared.Physics.Collision.Shapes;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.UI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Displays the docking view from a specific docking port
|
||||||
|
/// </summary>
|
||||||
|
[Virtual]
|
||||||
|
public class DockingControl : Control
|
||||||
|
{
|
||||||
|
private readonly IEntityManager _entManager;
|
||||||
|
private readonly IMapManager _mapManager;
|
||||||
|
|
||||||
|
private const int MinimapRadius = 384;
|
||||||
|
private const int MinimapMargin = 4;
|
||||||
|
|
||||||
|
private float _range = 8f;
|
||||||
|
private float _rangeSquared = 0f;
|
||||||
|
private const float GridLinesDistance = 32f;
|
||||||
|
|
||||||
|
private int MidPoint => SizeFull / 2;
|
||||||
|
private int SizeFull => (int) ((MinimapRadius + MinimapMargin) * 2 * UIScale);
|
||||||
|
private int ScaledMinimapRadius => (int) (MinimapRadius * UIScale);
|
||||||
|
private float MinimapScale => _range != 0 ? ScaledMinimapRadius / _range : 0f;
|
||||||
|
|
||||||
|
public EntityUid? ViewedDock;
|
||||||
|
public EntityUid? GridEntity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stored by GridID then by docks
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<EntityUid, List<DockingInterfaceState>> Docks = new();
|
||||||
|
|
||||||
|
public DockingControl()
|
||||||
|
{
|
||||||
|
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
_mapManager = IoCManager.Resolve<IMapManager>();
|
||||||
|
_rangeSquared = _range * _range;
|
||||||
|
MinSize = (SizeFull, SizeFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
base.Draw(handle);
|
||||||
|
|
||||||
|
var fakeAA = new Color(0.08f, 0.08f, 0.08f);
|
||||||
|
|
||||||
|
handle.DrawCircle((MidPoint, MidPoint), ScaledMinimapRadius + 1, fakeAA);
|
||||||
|
handle.DrawCircle((MidPoint, MidPoint), ScaledMinimapRadius, Color.Black);
|
||||||
|
|
||||||
|
var gridLines = new Color(0.08f, 0.08f, 0.08f);
|
||||||
|
var gridLinesRadial = 8;
|
||||||
|
var gridLinesEquatorial = (int) Math.Floor(_range / GridLinesDistance);
|
||||||
|
|
||||||
|
for (var i = 1; i < gridLinesEquatorial + 1; i++)
|
||||||
|
{
|
||||||
|
handle.DrawCircle((MidPoint, MidPoint), GridLinesDistance * MinimapScale * i, gridLines, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < gridLinesRadial; i++)
|
||||||
|
{
|
||||||
|
Angle angle = (Math.PI / gridLinesRadial) * i;
|
||||||
|
var aExtent = angle.ToVec() * ScaledMinimapRadius;
|
||||||
|
handle.DrawLine((MidPoint, MidPoint) - aExtent, (MidPoint, MidPoint) + aExtent, gridLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_entManager.TryGetComponent<TransformComponent>(ViewedDock, out var xform) ||
|
||||||
|
!_entManager.TryGetComponent<TransformComponent>(GridEntity, out var gridXform)) return;
|
||||||
|
|
||||||
|
var rotation = Matrix3.CreateRotation(xform.LocalRotation);
|
||||||
|
var matrix = Matrix3.CreateTranslation(-xform.LocalPosition);
|
||||||
|
|
||||||
|
// Draw the fixtures around the dock before drawing it
|
||||||
|
if (_entManager.TryGetComponent<FixturesComponent>(GridEntity, out var fixtures))
|
||||||
|
{
|
||||||
|
foreach (var (_, fixture) in fixtures.Fixtures)
|
||||||
|
{
|
||||||
|
var poly = (PolygonShape) fixture.Shape;
|
||||||
|
|
||||||
|
for (var i = 0; i < poly.VertexCount; i++)
|
||||||
|
{
|
||||||
|
var start = matrix.Transform(poly.Vertices[i]);
|
||||||
|
var end = matrix.Transform(poly.Vertices[(i + 1) % poly.VertexCount]);
|
||||||
|
|
||||||
|
var startOut = start.LengthSquared > _rangeSquared;
|
||||||
|
var endOut = end.LengthSquared > _rangeSquared;
|
||||||
|
|
||||||
|
// We need to draw to the radar border so we'll cap the range,
|
||||||
|
// but if none of the verts are in range then just leave it.
|
||||||
|
if (startOut && endOut)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
start.Y = -start.Y;
|
||||||
|
end.Y = -end.Y;
|
||||||
|
|
||||||
|
// If start is outside we draw capped from end to start
|
||||||
|
if (startOut)
|
||||||
|
{
|
||||||
|
// It's called Jobseeker now.
|
||||||
|
if (!MathHelper.TryGetIntersecting(start, end, _range, out var newStart)) continue;
|
||||||
|
start = newStart.Value;
|
||||||
|
}
|
||||||
|
// otherwise vice versa
|
||||||
|
else if (endOut)
|
||||||
|
{
|
||||||
|
if (!MathHelper.TryGetIntersecting(end, start, _range, out var newEnd)) continue;
|
||||||
|
end = newEnd.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.DrawLine(ScalePosition(start), ScalePosition(end), Color.Goldenrod);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the dock's collision
|
||||||
|
handle.DrawRect(new UIBox2(
|
||||||
|
ScalePosition(rotation.Transform(new Vector2(-0.2f, -0.7f))),
|
||||||
|
ScalePosition(rotation.Transform(new Vector2(0.2f, -0.5f)))), Color.Aquamarine);
|
||||||
|
|
||||||
|
// Draw the dock itself
|
||||||
|
handle.DrawRect(new UIBox2(
|
||||||
|
ScalePosition(rotation.Transform(new Vector2(-0.5f, 0.5f))),
|
||||||
|
ScalePosition(rotation.Transform(new Vector2(0.5f, -0.5f)))), Color.Green);
|
||||||
|
|
||||||
|
// Draw nearby grids
|
||||||
|
var worldPos = gridXform.WorldMatrix.Transform(xform.LocalPosition);
|
||||||
|
var gridInvMatrix = gridXform.InvWorldMatrix;
|
||||||
|
Matrix3.Multiply(in gridInvMatrix, in matrix, out var invMatrix);
|
||||||
|
|
||||||
|
// TODO: Getting some overdraw so need to fix that.
|
||||||
|
|
||||||
|
foreach (var grid in _mapManager.FindGridsIntersecting(gridXform.MapID,
|
||||||
|
new Box2(worldPos - _range, worldPos + _range)))
|
||||||
|
{
|
||||||
|
if (grid.GridEntityId == GridEntity) continue;
|
||||||
|
|
||||||
|
// Draw the fixtures before drawing any docks in range.
|
||||||
|
if (!_entManager.TryGetComponent<FixturesComponent>(grid.GridEntityId, out var gridFixtures)) continue;
|
||||||
|
|
||||||
|
var gridMatrix = grid.WorldMatrix;
|
||||||
|
|
||||||
|
Matrix3.Multiply(in gridMatrix, in invMatrix, out var matty);
|
||||||
|
|
||||||
|
foreach (var (_, fixture) in gridFixtures.Fixtures)
|
||||||
|
{
|
||||||
|
var poly = (PolygonShape) fixture.Shape;
|
||||||
|
|
||||||
|
for (var i = 0; i < poly.VertexCount; i++)
|
||||||
|
{
|
||||||
|
// This is because the same line might be on different fixtures so we don't want to draw it twice.
|
||||||
|
var startPos = poly.Vertices[i];
|
||||||
|
var endPos = poly.Vertices[(i + 1) % poly.VertexCount];
|
||||||
|
|
||||||
|
var start = matty.Transform(startPos);
|
||||||
|
var end = matty.Transform(endPos);
|
||||||
|
|
||||||
|
var startOut = start.LengthSquared > _rangeSquared;
|
||||||
|
var endOut = end.LengthSquared > _rangeSquared;
|
||||||
|
|
||||||
|
// We need to draw to the radar border so we'll cap the range,
|
||||||
|
// but if none of the verts are in range then just leave it.
|
||||||
|
if (startOut && endOut)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
start.Y = -start.Y;
|
||||||
|
end.Y = -end.Y;
|
||||||
|
|
||||||
|
// If start is outside we draw capped from end to start
|
||||||
|
if (startOut)
|
||||||
|
{
|
||||||
|
// It's called Jobseeker now.
|
||||||
|
if (!MathHelper.TryGetIntersecting(start, end, _range, out var newStart)) continue;
|
||||||
|
start = newStart.Value;
|
||||||
|
}
|
||||||
|
// otherwise vice versa
|
||||||
|
else if (endOut)
|
||||||
|
{
|
||||||
|
if (!MathHelper.TryGetIntersecting(end, start, _range, out var newEnd)) continue;
|
||||||
|
end = newEnd.Value;
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.DrawLine(ScalePosition(start), ScalePosition(end), Color.Aquamarine);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw any docks on that grid
|
||||||
|
if (Docks.TryGetValue(grid.GridEntityId, out var gridDocks))
|
||||||
|
{
|
||||||
|
foreach (var dock in gridDocks)
|
||||||
|
{
|
||||||
|
var position = matty.Transform(dock.Coordinates.Position);
|
||||||
|
|
||||||
|
if (position.Length > _range - 0.8f) continue;
|
||||||
|
|
||||||
|
var otherDockRotation = Matrix3.CreateRotation(dock.Angle);
|
||||||
|
|
||||||
|
// Draw the dock's collision
|
||||||
|
var verts = new[]
|
||||||
|
{
|
||||||
|
matty.Transform(dock.Coordinates.Position +
|
||||||
|
otherDockRotation.Transform(new Vector2(-0.2f, -0.7f))),
|
||||||
|
matty.Transform(dock.Coordinates.Position +
|
||||||
|
otherDockRotation.Transform(new Vector2(0.2f, -0.7f))),
|
||||||
|
matty.Transform(dock.Coordinates.Position +
|
||||||
|
otherDockRotation.Transform(new Vector2(0.2f, -0.5f))),
|
||||||
|
matty.Transform(dock.Coordinates.Position +
|
||||||
|
otherDockRotation.Transform(new Vector2(-0.2f, -0.5f))),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < verts.Length; i++)
|
||||||
|
{
|
||||||
|
var vert = verts[i];
|
||||||
|
vert.Y = -vert.Y;
|
||||||
|
verts[i] = ScalePosition(vert);
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts, Color.Turquoise);
|
||||||
|
|
||||||
|
// Draw the dock itself
|
||||||
|
verts = new[]
|
||||||
|
{
|
||||||
|
matty.Transform(dock.Coordinates.Position + new Vector2(-0.5f, -0.5f)),
|
||||||
|
matty.Transform(dock.Coordinates.Position + new Vector2(0.5f, -0.5f)),
|
||||||
|
matty.Transform(dock.Coordinates.Position + new Vector2(0.5f, 0.5f)),
|
||||||
|
matty.Transform(dock.Coordinates.Position + new Vector2(-0.5f, 0.5f)),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < verts.Length; i++)
|
||||||
|
{
|
||||||
|
var vert = verts[i];
|
||||||
|
vert.Y = -vert.Y;
|
||||||
|
verts[i] = ScalePosition(vert);
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts, Color.Green);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2 ScalePosition(Vector2 value)
|
||||||
|
{
|
||||||
|
return value * MinimapScale + MidPoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
6
Content.Client/Shuttles/UI/RadarConsoleWindow.xaml
Normal file
6
Content.Client/Shuttles/UI/RadarConsoleWindow.xaml
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
<userInterface:FancyWindow xmlns="https://spacestation14.io"
|
||||||
|
xmlns:userInterface="clr-namespace:Content.Client.UserInterface"
|
||||||
|
xmlns:ui="clr-namespace:Content.Client.Shuttles.UI"
|
||||||
|
Title="{Loc 'radar-console-window-title'}">
|
||||||
|
<ui:RadarControl Name="RadarScreen"/>
|
||||||
|
</userInterface:FancyWindow>
|
||||||
22
Content.Client/Shuttles/UI/RadarConsoleWindow.xaml.cs
Normal file
22
Content.Client/Shuttles/UI/RadarConsoleWindow.xaml.cs
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
using Content.Client.Computer;
|
||||||
|
using Content.Client.UserInterface;
|
||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.UI;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class RadarConsoleWindow : FancyWindow,
|
||||||
|
IComputerWindow<RadarConsoleBoundInterfaceState>
|
||||||
|
{
|
||||||
|
public RadarConsoleWindow()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateState(RadarConsoleBoundInterfaceState scc)
|
||||||
|
{
|
||||||
|
RadarScreen.UpdateState(scc);
|
||||||
|
}
|
||||||
|
}
|
||||||
348
Content.Client/Shuttles/UI/RadarControl.cs
Normal file
348
Content.Client/Shuttles/UI/RadarControl.cs
Normal file
@@ -0,0 +1,348 @@
|
|||||||
|
using Content.Client.Stylesheets;
|
||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Physics;
|
||||||
|
using Robust.Shared.Physics.Collision.Shapes;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.UI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Displays nearby grids inside of a control.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class RadarControl : Control
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
|
||||||
|
private const float ScrollSensitivity = 8f;
|
||||||
|
|
||||||
|
private const int MinimapRadius = 384;
|
||||||
|
private const int MinimapMargin = 4;
|
||||||
|
private const float GridLinesDistance = 32f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Entity used to transform all of the radar objects.
|
||||||
|
/// </summary>
|
||||||
|
private EntityUid? _entity;
|
||||||
|
|
||||||
|
private float _radarMinRange = 64f;
|
||||||
|
private float _radarMaxRange = 256f;
|
||||||
|
public float RadarRange { get; private set; } = 256f;
|
||||||
|
|
||||||
|
private int MidPoint => SizeFull / 2;
|
||||||
|
private int SizeFull => (int) ((MinimapRadius + MinimapMargin) * 2 * UIScale);
|
||||||
|
private int ScaledMinimapRadius => (int) (MinimapRadius * UIScale);
|
||||||
|
private float MinimapScale => RadarRange != 0 ? ScaledMinimapRadius / RadarRange : 0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows a label on each radar object.
|
||||||
|
/// </summary>
|
||||||
|
private Dictionary<EntityUid, Control> _iffControls = new();
|
||||||
|
|
||||||
|
private Dictionary<EntityUid, Dictionary<EntityUid, DockingInterfaceState>> _docks = new();
|
||||||
|
|
||||||
|
public bool ShowIFF { get; set; } = true;
|
||||||
|
public bool ShowDocks { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Currently hovered docked to show on the map.
|
||||||
|
/// </summary>
|
||||||
|
public EntityUid? HighlightedDock;
|
||||||
|
|
||||||
|
public Action<float>? OnRadarRangeChanged;
|
||||||
|
|
||||||
|
public RadarControl()
|
||||||
|
{
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
MinSize = (SizeFull, SizeFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateState(RadarConsoleBoundInterfaceState ls)
|
||||||
|
{
|
||||||
|
_radarMaxRange = ls.MaxRange;
|
||||||
|
|
||||||
|
if (_radarMaxRange < RadarRange)
|
||||||
|
{
|
||||||
|
RadarRange = _radarMaxRange;
|
||||||
|
OnRadarRangeChanged?.Invoke(RadarRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_radarMaxRange < _radarMinRange)
|
||||||
|
_radarMinRange = _radarMaxRange;
|
||||||
|
|
||||||
|
_entity = ls.Entity;
|
||||||
|
_docks.Clear();
|
||||||
|
|
||||||
|
foreach (var state in ls.Docks)
|
||||||
|
{
|
||||||
|
var coordinates = state.Coordinates;
|
||||||
|
var grid = _docks.GetOrNew(coordinates.EntityId);
|
||||||
|
grid.Add(state.Entity, state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void MouseWheel(GUIMouseWheelEventArgs args)
|
||||||
|
{
|
||||||
|
base.MouseWheel(args);
|
||||||
|
AddRadarRange(-args.Delta.Y * ScrollSensitivity);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddRadarRange(float value)
|
||||||
|
{
|
||||||
|
var oldValue = RadarRange;
|
||||||
|
RadarRange = MathF.Max(0f, MathF.Max(_radarMinRange, MathF.Min(RadarRange + value, _radarMaxRange)));
|
||||||
|
|
||||||
|
if (oldValue.Equals(RadarRange)) return;
|
||||||
|
|
||||||
|
OnRadarRangeChanged?.Invoke(RadarRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
var fakeAA = new Color(0.08f, 0.08f, 0.08f);
|
||||||
|
|
||||||
|
handle.DrawCircle((MidPoint, MidPoint), ScaledMinimapRadius + 1, fakeAA);
|
||||||
|
handle.DrawCircle((MidPoint, MidPoint), ScaledMinimapRadius, Color.Black);
|
||||||
|
|
||||||
|
// No data
|
||||||
|
if (_entity == null)
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gridLines = new Color(0.08f, 0.08f, 0.08f);
|
||||||
|
var gridLinesRadial = 8;
|
||||||
|
var gridLinesEquatorial = (int) Math.Floor(RadarRange / GridLinesDistance);
|
||||||
|
|
||||||
|
for (var i = 1; i < gridLinesEquatorial + 1; i++)
|
||||||
|
{
|
||||||
|
handle.DrawCircle((MidPoint, MidPoint), GridLinesDistance * MinimapScale * i, gridLines, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var i = 0; i < gridLinesRadial; i++)
|
||||||
|
{
|
||||||
|
Angle angle = (Math.PI / gridLinesRadial) * i;
|
||||||
|
var aExtent = angle.ToVec() * ScaledMinimapRadius;
|
||||||
|
handle.DrawLine((MidPoint, MidPoint) - aExtent, (MidPoint, MidPoint) + aExtent, gridLines);
|
||||||
|
}
|
||||||
|
|
||||||
|
var metaQuery = _entManager.GetEntityQuery<MetaDataComponent>();
|
||||||
|
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
||||||
|
var fixturesQuery = _entManager.GetEntityQuery<FixturesComponent>();
|
||||||
|
var bodyQuery = _entManager.GetEntityQuery<PhysicsComponent>();
|
||||||
|
var xform = xformQuery.GetComponent(_entity.Value);
|
||||||
|
var mapPosition = xform.MapPosition;
|
||||||
|
|
||||||
|
if (mapPosition.MapId == MapId.Nullspace)
|
||||||
|
{
|
||||||
|
Clear();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Can also use ourGridBody.LocalCenter
|
||||||
|
var offset = xform.Coordinates.Position;
|
||||||
|
var offsetMatrix = Matrix3.CreateTranslation(-offset);
|
||||||
|
Matrix3 matrix;
|
||||||
|
|
||||||
|
// Draw our grid in detail
|
||||||
|
var ourGridId = xform.GridID;
|
||||||
|
if (ourGridId != GridId.Invalid)
|
||||||
|
{
|
||||||
|
matrix = xform.InvWorldMatrix;
|
||||||
|
var ourGridFixtures = fixturesQuery.GetComponent(ourGridId);
|
||||||
|
// Draw our grid; use non-filled boxes so it doesn't look awful.
|
||||||
|
DrawGrid(handle, offsetMatrix, ourGridFixtures, Color.Yellow);
|
||||||
|
|
||||||
|
DrawDocks(handle, xform.GridEntityId, offsetMatrix);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
matrix = Matrix3.CreateTranslation(-offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
var invertedPosition = xform.Coordinates.Position - offset;
|
||||||
|
invertedPosition.Y = -invertedPosition.Y;
|
||||||
|
// Don't need to transform the InvWorldMatrix again as it's already offset to its position.
|
||||||
|
|
||||||
|
// Draw radar position on the station
|
||||||
|
handle.DrawCircle(ScalePosition(invertedPosition), 5f, Color.Lime);
|
||||||
|
|
||||||
|
var shown = new HashSet<EntityUid>();
|
||||||
|
|
||||||
|
// Draw other grids... differently
|
||||||
|
foreach (var grid in _mapManager.FindGridsIntersecting(mapPosition.MapId,
|
||||||
|
new Box2(mapPosition.Position - RadarRange, mapPosition.Position + RadarRange)))
|
||||||
|
{
|
||||||
|
if (grid.Index == ourGridId) continue;
|
||||||
|
|
||||||
|
var gridBody = bodyQuery.GetComponent(grid.GridEntityId);
|
||||||
|
if (gridBody.Mass < 10f)
|
||||||
|
{
|
||||||
|
ClearLabel(grid.GridEntityId);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
shown.Add(grid.GridEntityId);
|
||||||
|
var name = metaQuery.GetComponent(grid.GridEntityId).EntityName;
|
||||||
|
|
||||||
|
if (name == string.Empty)
|
||||||
|
name = Loc.GetString("shuttle-console-unknown");
|
||||||
|
|
||||||
|
var gridXform = xformQuery.GetComponent(grid.GridEntityId);
|
||||||
|
var gridFixtures = fixturesQuery.GetComponent(grid.GridEntityId);
|
||||||
|
var gridMatrix = gridXform.WorldMatrix;
|
||||||
|
Matrix3.Multiply(in gridMatrix, in matrix, out var matty);
|
||||||
|
|
||||||
|
if (ShowIFF)
|
||||||
|
{
|
||||||
|
if (!_iffControls.TryGetValue(grid.GridEntityId, out var control))
|
||||||
|
{
|
||||||
|
var label = new Label()
|
||||||
|
{
|
||||||
|
HorizontalAlignment = HAlignment.Left,
|
||||||
|
};
|
||||||
|
|
||||||
|
control = new PanelContainer()
|
||||||
|
{
|
||||||
|
HorizontalAlignment = HAlignment.Left,
|
||||||
|
VerticalAlignment = VAlignment.Top,
|
||||||
|
Children = { label },
|
||||||
|
StyleClasses = { StyleNano.StyleClassTooltipPanel },
|
||||||
|
};
|
||||||
|
|
||||||
|
_iffControls[grid.GridEntityId] = control;
|
||||||
|
AddChild(control);
|
||||||
|
}
|
||||||
|
|
||||||
|
var gridCentre = matty.Transform(gridBody.LocalCenter);
|
||||||
|
gridCentre.Y = -gridCentre.Y;
|
||||||
|
|
||||||
|
// TODO: When we get IFF or whatever we can show controls at a further distance; for now
|
||||||
|
// we don't do that because it would immediately reveal nukies.
|
||||||
|
if (gridCentre.Length < RadarRange)
|
||||||
|
{
|
||||||
|
control.Visible = true;
|
||||||
|
var label = (Label) control.GetChild(0);
|
||||||
|
label.Text = Loc.GetString("shuttle-console-iff-label", ("name", name), ("distance", $"{gridCentre.Length:0.0}"));
|
||||||
|
LayoutContainer.SetPosition(control, ScalePosition(gridCentre) / UIScale);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
control.Visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ClearLabel(grid.GridEntityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detailed view
|
||||||
|
DrawGrid(handle, matty, gridFixtures, Color.Aquamarine);
|
||||||
|
|
||||||
|
DrawDocks(handle, grid.GridEntityId, matty);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (ent, _) in _iffControls)
|
||||||
|
{
|
||||||
|
if (shown.Contains(ent)) continue;
|
||||||
|
ClearLabel(ent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Clear()
|
||||||
|
{
|
||||||
|
foreach (var (_, label) in _iffControls)
|
||||||
|
{
|
||||||
|
label.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_iffControls.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearLabel(EntityUid uid)
|
||||||
|
{
|
||||||
|
if (!_iffControls.TryGetValue(uid, out var label)) return;
|
||||||
|
label.Dispose();
|
||||||
|
_iffControls.Remove(uid);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3 matrix)
|
||||||
|
{
|
||||||
|
if (!ShowDocks) return;
|
||||||
|
|
||||||
|
const float DockScale = 1.2f;
|
||||||
|
|
||||||
|
if (_docks.TryGetValue(uid, out var docks))
|
||||||
|
{
|
||||||
|
foreach (var (ent, state) in docks)
|
||||||
|
{
|
||||||
|
var position = state.Coordinates.Position;
|
||||||
|
var uiPosition = matrix.Transform(position);
|
||||||
|
|
||||||
|
if (uiPosition.Length > RadarRange - DockScale) continue;
|
||||||
|
|
||||||
|
var color = HighlightedDock == ent ? Color.Magenta : Color.DarkViolet;
|
||||||
|
|
||||||
|
uiPosition.Y = -uiPosition.Y;
|
||||||
|
|
||||||
|
var verts = new[]
|
||||||
|
{
|
||||||
|
matrix.Transform(position + new Vector2(-DockScale, -DockScale)),
|
||||||
|
matrix.Transform(position + new Vector2(DockScale, -DockScale)),
|
||||||
|
matrix.Transform(position + new Vector2(DockScale, DockScale)),
|
||||||
|
matrix.Transform(position + new Vector2(-DockScale, DockScale)),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < verts.Length; i++)
|
||||||
|
{
|
||||||
|
var vert = verts[i];
|
||||||
|
vert.Y = -vert.Y;
|
||||||
|
verts[i] = ScalePosition(vert);
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawGrid(DrawingHandleScreen handle, Matrix3 matrix, FixturesComponent component, Color color)
|
||||||
|
{
|
||||||
|
foreach (var (_, fixture) in component.Fixtures)
|
||||||
|
{
|
||||||
|
// If the fixture has any points out of range we won't draw any of it.
|
||||||
|
var invalid = false;
|
||||||
|
var poly = (PolygonShape) fixture.Shape;
|
||||||
|
var verts = new Vector2[poly.VertexCount + 1];
|
||||||
|
|
||||||
|
for (var i = 0; i < poly.VertexCount; i++)
|
||||||
|
{
|
||||||
|
var vert = matrix.Transform(poly.Vertices[i]);
|
||||||
|
|
||||||
|
if (vert.Length > RadarRange)
|
||||||
|
{
|
||||||
|
invalid = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
vert.Y = -vert.Y;
|
||||||
|
verts[i] = ScalePosition(vert);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (invalid) continue;
|
||||||
|
|
||||||
|
// Closed list
|
||||||
|
verts[poly.VertexCount] = verts[0];
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.LineStrip, verts, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2 ScalePosition(Vector2 value)
|
||||||
|
{
|
||||||
|
return value * MinimapScale + MidPoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
81
Content.Client/Shuttles/UI/ShuttleConsoleWindow.xaml
Normal file
81
Content.Client/Shuttles/UI/ShuttleConsoleWindow.xaml
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
<userInterface:FancyWindow xmlns="https://spacestation14.io"
|
||||||
|
xmlns:userInterface="clr-namespace:Content.Client.UserInterface"
|
||||||
|
xmlns:ui="clr-namespace:Content.Client.Shuttles.UI"
|
||||||
|
Title="{Loc 'shuttle-console-window-title'}">
|
||||||
|
<GridContainer Columns="3"
|
||||||
|
HorizontalAlignment="Stretch">
|
||||||
|
<BoxContainer Name="LeftDisplay"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
MinWidth="256"
|
||||||
|
Align="Center"
|
||||||
|
Orientation="Vertical">
|
||||||
|
<PanelContainer StyleClasses="BorderedWindowPanel">
|
||||||
|
<BoxContainer Name="DockPorts"
|
||||||
|
Orientation="Vertical">
|
||||||
|
</BoxContainer>
|
||||||
|
</PanelContainer>
|
||||||
|
</BoxContainer>
|
||||||
|
<PanelContainer>
|
||||||
|
<ui:RadarControl Name="RadarScreen"
|
||||||
|
MouseFilter="Stop"/>
|
||||||
|
<ui:DockingControl Name="DockingScreen"
|
||||||
|
Visible="False"
|
||||||
|
MouseFilter="Stop"/>
|
||||||
|
</PanelContainer>
|
||||||
|
<BoxContainer Name="RightDisplay"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
HorizontalAlignment="Left"
|
||||||
|
MinWidth="256"
|
||||||
|
Align="Center"
|
||||||
|
Orientation="Vertical">
|
||||||
|
<PanelContainer Name="ReadonlyDisplay"
|
||||||
|
StyleClasses="BorderedWindowPanel">
|
||||||
|
<GridContainer Columns="2"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Top">
|
||||||
|
<Label Text="{Loc 'shuttle-console-max-radar'}"/>
|
||||||
|
<Label Name="MaxRadarRange"
|
||||||
|
Text="0.0"
|
||||||
|
HorizontalAlignment="Right"/>
|
||||||
|
<Label Text="{Loc 'shuttle-console-radar'}"/>
|
||||||
|
<Label Name="RadarRange"
|
||||||
|
Text="0.0"
|
||||||
|
HorizontalAlignment="Right"/>
|
||||||
|
<Label Text="{Loc 'shuttle-console-position'}"/>
|
||||||
|
<Label Name="GridPosition"
|
||||||
|
Text="0.0, 0.0"
|
||||||
|
Align="Right"/>
|
||||||
|
<Label Text="{Loc 'shuttle-console-orientation'}"/>
|
||||||
|
<Label Name="GridOrientation"
|
||||||
|
Text="0.0"
|
||||||
|
Align="Right"/>
|
||||||
|
<Label Text="{Loc 'shuttle-console-linear-velocity'}"/>
|
||||||
|
<Label Name="GridLinearVelocity"
|
||||||
|
Text="0.0, 0.0"
|
||||||
|
Align="Right"/>
|
||||||
|
<Label Text="{Loc 'shuttle-console-angular-velocity'}"/>
|
||||||
|
<Label Name="GridAngularVelocity"
|
||||||
|
Text="0.0"
|
||||||
|
Align="Right"/>
|
||||||
|
</GridContainer>
|
||||||
|
</PanelContainer>
|
||||||
|
<Button Name="ShuttleModeDisplay"
|
||||||
|
Text="{Loc 'shuttle-console-strafing'}"
|
||||||
|
TextAlign="Center"
|
||||||
|
ToggleMode="True"/>
|
||||||
|
<Button Name="IFFToggle"
|
||||||
|
Text="{Loc 'shuttle-console-iff-toggle'}"
|
||||||
|
TextAlign="Center"
|
||||||
|
ToggleMode="True"/>
|
||||||
|
<Button Name="DockToggle"
|
||||||
|
Text="{Loc 'shuttle-console-dock-toggle'}"
|
||||||
|
TextAlign="Center"
|
||||||
|
ToggleMode="True"/>
|
||||||
|
<Button Name="UndockButton"
|
||||||
|
Text="{Loc 'shuttle-console-undock'}"
|
||||||
|
TextAlign="Center"
|
||||||
|
Disabled="True"/>
|
||||||
|
</BoxContainer>
|
||||||
|
</GridContainer>
|
||||||
|
</userInterface:FancyWindow>
|
||||||
239
Content.Client/Shuttles/UI/ShuttleConsoleWindow.xaml.cs
Normal file
239
Content.Client/Shuttles/UI/ShuttleConsoleWindow.xaml.cs
Normal file
@@ -0,0 +1,239 @@
|
|||||||
|
using Content.Client.Computer;
|
||||||
|
using Content.Client.UserInterface;
|
||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.UI;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class ShuttleConsoleWindow : FancyWindow,
|
||||||
|
IComputerWindow<ShuttleConsoleBoundInterfaceState>
|
||||||
|
{
|
||||||
|
private readonly IEntityManager _entManager;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// EntityUid of the open console.
|
||||||
|
/// </summary>
|
||||||
|
private EntityUid? _entity;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Currently selected dock button for camera.
|
||||||
|
/// </summary>
|
||||||
|
private BaseButton? _selectedDock;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stored by grid entityid then by states
|
||||||
|
/// </summary>
|
||||||
|
private Dictionary<EntityUid, List<DockingInterfaceState>> _docks = new();
|
||||||
|
|
||||||
|
public Action<ShuttleMode>? ShuttleModePressed;
|
||||||
|
public Action<EntityUid>? UndockPressed;
|
||||||
|
public Action<EntityUid>? StartAutodockPressed;
|
||||||
|
public Action<EntityUid>? StopAutodockPressed;
|
||||||
|
|
||||||
|
public ShuttleConsoleWindow()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
|
||||||
|
OnRadarRangeChange(RadarScreen.RadarRange);
|
||||||
|
RadarScreen.OnRadarRangeChanged += OnRadarRangeChange;
|
||||||
|
|
||||||
|
IFFToggle.OnToggled += OnIFFTogglePressed;
|
||||||
|
IFFToggle.Pressed = RadarScreen.ShowIFF;
|
||||||
|
|
||||||
|
DockToggle.OnToggled += OnDockTogglePressed;
|
||||||
|
DockToggle.Pressed = RadarScreen.ShowDocks;
|
||||||
|
|
||||||
|
ShuttleModeDisplay.OnToggled += OnShuttleModePressed;
|
||||||
|
|
||||||
|
UndockButton.OnPressed += OnUndockPressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRadarRangeChange(float value)
|
||||||
|
{
|
||||||
|
RadarRange.Text = $"{value:0}";
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnShuttleModePressed(BaseButton.ButtonEventArgs obj)
|
||||||
|
{
|
||||||
|
ShuttleModePressed?.Invoke(obj.Button.Pressed ? ShuttleMode.Strafing : ShuttleMode.Cruise);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnIFFTogglePressed(BaseButton.ButtonEventArgs args)
|
||||||
|
{
|
||||||
|
RadarScreen.ShowIFF ^= true;
|
||||||
|
args.Button.Pressed = RadarScreen.ShowIFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDockTogglePressed(BaseButton.ButtonEventArgs args)
|
||||||
|
{
|
||||||
|
RadarScreen.ShowDocks ^= true;
|
||||||
|
args.Button.Pressed = RadarScreen.ShowDocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUndockPressed(BaseButton.ButtonEventArgs args)
|
||||||
|
{
|
||||||
|
if (DockingScreen.ViewedDock == null) return;
|
||||||
|
UndockPressed?.Invoke(DockingScreen.ViewedDock.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateState(ShuttleConsoleBoundInterfaceState scc)
|
||||||
|
{
|
||||||
|
_entity = scc.Entity;
|
||||||
|
UpdateDocks(scc.Docks);
|
||||||
|
RadarScreen.UpdateState(scc);
|
||||||
|
MaxRadarRange.Text = $"{scc.MaxRange:0}";
|
||||||
|
ShuttleModeDisplay.Pressed = scc.Mode == ShuttleMode.Strafing;
|
||||||
|
}
|
||||||
|
|
||||||
|
#region Docking
|
||||||
|
|
||||||
|
private void UpdateDocks(List<DockingInterfaceState> docks)
|
||||||
|
{
|
||||||
|
// TODO: We should check for changes so any existing highlighted doesn't delete.
|
||||||
|
// We also need to make up some pseudonumber as well for these.
|
||||||
|
_docks.Clear();
|
||||||
|
|
||||||
|
foreach (var dock in docks)
|
||||||
|
{
|
||||||
|
var grid = _docks.GetOrNew(dock.Coordinates.EntityId);
|
||||||
|
grid.Add(dock);
|
||||||
|
}
|
||||||
|
|
||||||
|
DockPorts.DisposeAllChildren();
|
||||||
|
DockingScreen.Docks = _docks;
|
||||||
|
|
||||||
|
if (!_entManager.TryGetComponent<TransformComponent>(_entity, out var xform))
|
||||||
|
{
|
||||||
|
// TODO: Show Placeholder
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_docks.TryGetValue(xform.GridEntityId, out var gridDocks))
|
||||||
|
{
|
||||||
|
var index = 1;
|
||||||
|
|
||||||
|
foreach (var state in gridDocks)
|
||||||
|
{
|
||||||
|
var ent = state.Entity;
|
||||||
|
var pressed = ent == DockingScreen.ViewedDock;
|
||||||
|
string suffix;
|
||||||
|
|
||||||
|
if (state.Connected)
|
||||||
|
{
|
||||||
|
suffix = Loc.GetString("shuttle-console-docked", ("index", index));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
suffix = $"{index}";
|
||||||
|
}
|
||||||
|
|
||||||
|
var button = new Button()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("shuttle-console-dock-button", ("suffix", suffix)),
|
||||||
|
ToggleMode = true,
|
||||||
|
Pressed = pressed,
|
||||||
|
};
|
||||||
|
|
||||||
|
button.OnMouseEntered += args => OnDockMouseEntered(args, ent);
|
||||||
|
button.OnMouseExited += args => OnDockMouseExited(args, ent);
|
||||||
|
button.OnToggled += args => OnDockToggled(args, ent);
|
||||||
|
DockPorts.AddChild(button);
|
||||||
|
index++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDockMouseEntered(GUIMouseHoverEventArgs obj, EntityUid uid)
|
||||||
|
{
|
||||||
|
RadarScreen.HighlightedDock = uid;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDockMouseExited(GUIMouseHoverEventArgs obj, EntityUid uid)
|
||||||
|
{
|
||||||
|
RadarScreen.HighlightedDock = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows a docking camera instead of radar screen.
|
||||||
|
/// </summary>
|
||||||
|
private void OnDockToggled(BaseButton.ButtonEventArgs obj, EntityUid ent)
|
||||||
|
{
|
||||||
|
if (_selectedDock != null)
|
||||||
|
{
|
||||||
|
_selectedDock.Pressed = false;
|
||||||
|
_selectedDock = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!obj.Button.Pressed)
|
||||||
|
{
|
||||||
|
if (DockingScreen.ViewedDock != null)
|
||||||
|
{
|
||||||
|
StopAutodockPressed?.Invoke(DockingScreen.ViewedDock.Value);
|
||||||
|
DockingScreen.ViewedDock = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
UndockButton.Disabled = true;
|
||||||
|
DockingScreen.Visible = false;
|
||||||
|
RadarScreen.Visible = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// DebugTools.Assert(DockingScreen.ViewedDock == null);
|
||||||
|
_entManager.TryGetComponent<TransformComponent>(_entity, out var xform);
|
||||||
|
|
||||||
|
UndockButton.Disabled = false;
|
||||||
|
RadarScreen.Visible = false;
|
||||||
|
DockingScreen.Visible = true;
|
||||||
|
DockingScreen.ViewedDock = ent;
|
||||||
|
StartAutodockPressed?.Invoke(ent);
|
||||||
|
DockingScreen.GridEntity = xform?.GridEntityId;
|
||||||
|
_selectedDock = obj.Button;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Close()
|
||||||
|
{
|
||||||
|
base.Close();
|
||||||
|
if (DockingScreen.ViewedDock != null)
|
||||||
|
{
|
||||||
|
StopAutodockPressed?.Invoke(DockingScreen.ViewedDock.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#endregion
|
||||||
|
|
||||||
|
protected override void Draw(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
base.Draw(handle);
|
||||||
|
|
||||||
|
if (!_entManager.TryGetComponent<TransformComponent>(_entity, out var entXform) ||
|
||||||
|
!_entManager.TryGetComponent<PhysicsComponent>(entXform.GridEntityId, out var gridBody) ||
|
||||||
|
!_entManager.TryGetComponent<TransformComponent>(entXform.GridEntityId, out var gridXform))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (_, worldRot, worldMatrix) = gridXform.GetWorldPositionRotationMatrix();
|
||||||
|
var worldPos = worldMatrix.Transform(gridBody.LocalCenter);
|
||||||
|
|
||||||
|
// Get the positive reduced angle.
|
||||||
|
var displayRot = -worldRot.Reduced();
|
||||||
|
|
||||||
|
GridPosition.Text = $"{worldPos.X:0.0}, {worldPos.Y:0.0}";
|
||||||
|
GridOrientation.Text = $"{displayRot.Degrees:0.0}";
|
||||||
|
|
||||||
|
var gridVelocity = gridBody.LinearVelocity;
|
||||||
|
gridVelocity = displayRot.RotateVec(gridVelocity);
|
||||||
|
// Get linear velocity relative to the console entity
|
||||||
|
GridLinearVelocity.Text = $"{gridVelocity.X:0.0}, {gridVelocity.Y:0.0}";
|
||||||
|
GridAngularVelocity.Text = $"{-gridBody.AngularVelocity:0.0}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Server.Shuttles.EntitySystems;
|
using Content.Server.Shuttles.Systems;
|
||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
using Content.Shared.Shuttles.Components;
|
using Content.Shared.Shuttles.Components;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using Content.Server.Atmos.Components;
|
using Content.Server.Atmos.Components;
|
||||||
using Content.Server.Shuttles.EntitySystems;
|
using Content.Server.Shuttles.Systems;
|
||||||
using Content.Shared.Maps;
|
using Content.Shared.Maps;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
using Content.Server.Shuttles.Components;
|
using Content.Server.Shuttles.Components;
|
||||||
using Content.Server.Shuttles.EntitySystems;
|
using Content.Server.Shuttles.Systems;
|
||||||
using Content.Shared.Vehicle.Components;
|
using Content.Shared.Vehicle.Components;
|
||||||
using Content.Shared.Movement;
|
using Content.Shared.Movement;
|
||||||
using Content.Shared.Movement.Components;
|
using Content.Shared.Movement.Components;
|
||||||
@@ -123,7 +123,7 @@ namespace Content.Server.Physics.Controllers
|
|||||||
angularInput += sprint.X;
|
angularInput += sprint.X;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case ShuttleMode.Docking:
|
case ShuttleMode.Strafing:
|
||||||
// No angular input possible
|
// No angular input possible
|
||||||
foreach (var (pilot, mover) in pilots)
|
foreach (var (pilot, mover) in pilots)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,9 +0,0 @@
|
|||||||
namespace Content.Server.Radar;
|
|
||||||
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed class RadarConsoleComponent : Component
|
|
||||||
{
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
[DataField("range")]
|
|
||||||
public float Range = 512f;
|
|
||||||
}
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
using Content.Server.UserInterface;
|
|
||||||
using Content.Shared.Radar;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
|
|
||||||
namespace Content.Server.Radar;
|
|
||||||
|
|
||||||
public sealed class RadarConsoleSystem : EntitySystem
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
|
||||||
|
|
||||||
private static float Frequency = 1.5f;
|
|
||||||
|
|
||||||
private float _accumulator;
|
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
|
||||||
{
|
|
||||||
_accumulator += frameTime;
|
|
||||||
|
|
||||||
if (_accumulator < Frequency)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_accumulator -= Frequency;
|
|
||||||
|
|
||||||
foreach (var (component, xform) in EntityManager.EntityQuery<RadarConsoleComponent, TransformComponent>())
|
|
||||||
{
|
|
||||||
var s = component.Owner.GetUIOrNull(RadarConsoleUiKey.Key);
|
|
||||||
|
|
||||||
if (s is null || s.SubscribedSessions.Count == 0)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var (radarPos, _, radarInvMatrix) = xform.GetWorldPositionRotationInvMatrix();
|
|
||||||
|
|
||||||
var mapId = xform.MapID;
|
|
||||||
var objects = new List<RadarObjectData>();
|
|
||||||
|
|
||||||
_mapManager.FindGridsIntersectingEnumerator(mapId, new Box2(radarPos - component.Range, radarPos + component.Range), out var enumerator, true);
|
|
||||||
|
|
||||||
while (enumerator.MoveNext(out var grid))
|
|
||||||
{
|
|
||||||
var phy = Comp<PhysicsComponent>(grid.GridEntityId);
|
|
||||||
var transform = Transform(grid.GridEntityId);
|
|
||||||
|
|
||||||
if (phy.Mass < 50)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var rad = Math.Log2(phy.Mass);
|
|
||||||
var gridCenter = transform.WorldMatrix.Transform(phy.LocalCenter);
|
|
||||||
|
|
||||||
var pos = radarInvMatrix.Transform(gridCenter);
|
|
||||||
pos.Y = -pos.Y; // Robust has an inverted Y, like BYOND. This undoes that.
|
|
||||||
|
|
||||||
if (pos.Length > component.Range)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
objects.Add(new RadarObjectData {Color = Color.Aqua, Position = pos, Radius = (float)rad});
|
|
||||||
}
|
|
||||||
|
|
||||||
s.SetState(new RadarConsoleBoundInterfaceState(component.Range, objects.ToArray()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
14
Content.Server/Shuttles/Components/AutoDockComponent.cs
Normal file
14
Content.Server/Shuttles/Components/AutoDockComponent.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
namespace Content.Server.Shuttles.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Added to entities when they are actively trying to dock with something else.
|
||||||
|
/// We track it because checking every dock constantly would be expensive.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed class AutoDockComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Track who has requested autodocking so we can know when to be removed.
|
||||||
|
/// </summary>
|
||||||
|
public HashSet<EntityUid> Requesters = new();
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
namespace Content.Server.Shuttles.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Added to <see cref="DockingComponent"/> that have recently undocked.
|
||||||
|
/// This checks for whether they've left the specified radius before allowing them to automatically dock again.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed class RecentlyDockedComponent : Component
|
||||||
|
{
|
||||||
|
[ViewVariables, DataField("lastDocked")]
|
||||||
|
public EntityUid LastDocked;
|
||||||
|
|
||||||
|
[ViewVariables(VVAccess.ReadWrite), DataField("radius")]
|
||||||
|
public float Radius = 1.5f;
|
||||||
|
}
|
||||||
@@ -3,8 +3,14 @@ using Content.Shared.Shuttles.Components;
|
|||||||
namespace Content.Server.Shuttles.Components
|
namespace Content.Server.Shuttles.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed class ShuttleComponent : SharedShuttleComponent
|
public sealed class ShuttleComponent : Component
|
||||||
{
|
{
|
||||||
|
[ViewVariables]
|
||||||
|
public bool Enabled = true;
|
||||||
|
|
||||||
|
[ViewVariables]
|
||||||
|
public ShuttleMode Mode = ShuttleMode.Cruise;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The cached thrust available for each cardinal direction
|
/// The cached thrust available for each cardinal direction
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -3,17 +3,10 @@ using Content.Shared.Shuttles.Components;
|
|||||||
namespace Content.Server.Shuttles.Components
|
namespace Content.Server.Shuttles.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(SharedShuttleConsoleComponent))]
|
public sealed class ShuttleConsoleComponent : SharedShuttleConsoleComponent
|
||||||
internal sealed class ShuttleConsoleComponent : SharedShuttleConsoleComponent
|
|
||||||
{
|
{
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public List<PilotComponent> SubscribedPilots = new();
|
public readonly List<PilotComponent> SubscribedPilots = new();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Whether the console can be used to pilot. Toggled whenever it gets powered / unpowered.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public bool Enabled { get; set; } = false;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// How much should the pilot's eye be zoomed by when piloting using this console?
|
/// How much should the pilot's eye be zoomed by when piloting using this console?
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
using Content.Server.Shuttles.EntitySystems;
|
using Content.Server.Shuttles.Systems;
|
||||||
using Content.Shared.Damage;
|
using Content.Shared.Damage;
|
||||||
|
|
||||||
namespace Content.Server.Shuttles.Components
|
namespace Content.Server.Shuttles.Components
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Content.Server.Administration;
|
using Content.Server.Administration;
|
||||||
using Content.Server.Shuttles.Components;
|
using Content.Server.Shuttles.Components;
|
||||||
using Content.Server.Shuttles.EntitySystems;
|
using Content.Server.Shuttles.Systems;
|
||||||
using Content.Shared.Administration;
|
using Content.Shared.Administration;
|
||||||
using Robust.Shared.Console;
|
using Robust.Shared.Console;
|
||||||
|
|
||||||
|
|||||||
@@ -1,250 +0,0 @@
|
|||||||
using Content.Server.Popups;
|
|
||||||
using Content.Server.Power.Components;
|
|
||||||
using Content.Server.Power.EntitySystems;
|
|
||||||
using Content.Server.Shuttles.Components;
|
|
||||||
using Content.Shared.ActionBlocker;
|
|
||||||
using Content.Shared.Alert;
|
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Shared.Popups;
|
|
||||||
using Content.Shared.Shuttles;
|
|
||||||
using Content.Shared.Shuttles.Components;
|
|
||||||
using Content.Shared.Tag;
|
|
||||||
using Content.Shared.Verbs;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Utility;
|
|
||||||
|
|
||||||
namespace Content.Server.Shuttles.EntitySystems
|
|
||||||
{
|
|
||||||
internal sealed class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
|
||||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
|
||||||
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
|
|
||||||
[Dependency] private readonly PopupSystem _popup = default!;
|
|
||||||
[Dependency] private readonly TagSystem _tags = default!;
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<ShuttleConsoleComponent, ComponentShutdown>(HandleConsoleShutdown);
|
|
||||||
SubscribeLocalEvent<ShuttleConsoleComponent, ActivateInWorldEvent>(HandleConsoleInteract);
|
|
||||||
SubscribeLocalEvent<ShuttleConsoleComponent, PowerChangedEvent>(HandlePowerChange);
|
|
||||||
SubscribeLocalEvent<ShuttleConsoleComponent, GetVerbsEvent<InteractionVerb>>(OnConsoleInteract);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<PilotComponent, MoveEvent>(HandlePilotMove);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnConsoleInteract(EntityUid uid, ShuttleConsoleComponent component, GetVerbsEvent<InteractionVerb> args)
|
|
||||||
{
|
|
||||||
if (!args.CanAccess ||
|
|
||||||
!args.CanInteract)
|
|
||||||
return;
|
|
||||||
|
|
||||||
var xform = EntityManager.GetComponent<TransformComponent>(uid);
|
|
||||||
|
|
||||||
// Maybe move mode onto the console instead?
|
|
||||||
if (!_mapManager.TryGetGrid(xform.GridEntityId, out var grid) ||
|
|
||||||
!EntityManager.TryGetComponent(grid.GridEntityId, out ShuttleComponent? shuttle)) return;
|
|
||||||
|
|
||||||
InteractionVerb verb = new()
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("shuttle-mode-toggle"),
|
|
||||||
Act = () => ToggleShuttleMode(args.User, component, shuttle),
|
|
||||||
Disabled = !xform.Anchored || !this.IsPowered(uid, EntityManager),
|
|
||||||
};
|
|
||||||
|
|
||||||
args.Verbs.Add(verb);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ToggleShuttleMode(EntityUid user, ShuttleConsoleComponent consoleComponent, ShuttleComponent shuttleComponent, TransformComponent? consoleXform = null)
|
|
||||||
{
|
|
||||||
// Re-validate
|
|
||||||
if (!this.IsPowered(consoleComponent.Owner, EntityManager)) return;
|
|
||||||
|
|
||||||
if (!Resolve(consoleComponent.Owner, ref consoleXform)) return;
|
|
||||||
|
|
||||||
if (!consoleXform.Anchored || consoleXform.GridEntityId != EntityManager.GetComponent<TransformComponent>(shuttleComponent.Owner).GridEntityId) return;
|
|
||||||
|
|
||||||
switch (shuttleComponent.Mode)
|
|
||||||
{
|
|
||||||
case ShuttleMode.Cruise:
|
|
||||||
shuttleComponent.Mode = ShuttleMode.Docking;
|
|
||||||
_popup.PopupEntity(Loc.GetString("shuttle-mode-docking"), consoleComponent.Owner, Filter.Entities(user));
|
|
||||||
break;
|
|
||||||
case ShuttleMode.Docking:
|
|
||||||
shuttleComponent.Mode = ShuttleMode.Cruise;
|
|
||||||
_popup.PopupEntity(Loc.GetString("shuttle-mode-cruise"), consoleComponent.Owner, Filter.Entities(user));
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new ArgumentOutOfRangeException();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
|
||||||
{
|
|
||||||
base.Update(frameTime);
|
|
||||||
|
|
||||||
var toRemove = new RemQueue<PilotComponent>();
|
|
||||||
|
|
||||||
foreach (var comp in EntityManager.EntityQuery<PilotComponent>())
|
|
||||||
{
|
|
||||||
if (comp.Console == null) continue;
|
|
||||||
|
|
||||||
if (!_blocker.CanInteract(comp.Owner, comp.Console.Owner))
|
|
||||||
{
|
|
||||||
toRemove.Add(comp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var comp in toRemove)
|
|
||||||
{
|
|
||||||
RemovePilot(comp);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Console requires power to operate.
|
|
||||||
/// </summary>
|
|
||||||
private void HandlePowerChange(EntityUid uid, ShuttleConsoleComponent component, PowerChangedEvent args)
|
|
||||||
{
|
|
||||||
if (!args.Powered)
|
|
||||||
{
|
|
||||||
component.Enabled = false;
|
|
||||||
|
|
||||||
ClearPilots(component);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
component.Enabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If pilot is moved then we'll stop them from piloting.
|
|
||||||
/// </summary>
|
|
||||||
private void HandlePilotMove(EntityUid uid, PilotComponent component, ref MoveEvent args)
|
|
||||||
{
|
|
||||||
if (component.Console == null || component.Position == null)
|
|
||||||
{
|
|
||||||
DebugTools.Assert(component.Position == null && component.Console == null);
|
|
||||||
EntityManager.RemoveComponent<PilotComponent>(uid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.NewPosition.TryDistance(EntityManager, component.Position.Value, out var distance) &&
|
|
||||||
distance < PilotComponent.BreakDistance) return;
|
|
||||||
|
|
||||||
RemovePilot(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// For now pilots just interact with the console and can start piloting with wasd.
|
|
||||||
/// </summary>
|
|
||||||
private void HandleConsoleInteract(EntityUid uid, ShuttleConsoleComponent component, ActivateInWorldEvent args)
|
|
||||||
{
|
|
||||||
if (!_tags.HasTag(args.User, "CanPilot"))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pilotComponent = EntityManager.EnsureComponent<PilotComponent>(args.User);
|
|
||||||
|
|
||||||
if (!component.Enabled)
|
|
||||||
{
|
|
||||||
args.User.PopupMessage($"Console is not powered.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.Handled = true;
|
|
||||||
var console = pilotComponent.Console;
|
|
||||||
|
|
||||||
if (console != null)
|
|
||||||
{
|
|
||||||
RemovePilot(pilotComponent);
|
|
||||||
|
|
||||||
if (console == component)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
AddPilot(args.User, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void HandlePilotShutdown(EntityUid uid, PilotComponent component, ComponentShutdown args)
|
|
||||||
{
|
|
||||||
base.HandlePilotShutdown(uid, component, args);
|
|
||||||
RemovePilot(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HandleConsoleShutdown(EntityUid uid, ShuttleConsoleComponent component, ComponentShutdown args)
|
|
||||||
{
|
|
||||||
ClearPilots(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddPilot(EntityUid entity, ShuttleConsoleComponent component)
|
|
||||||
{
|
|
||||||
if (!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent) ||
|
|
||||||
component.SubscribedPilots.Contains(pilotComponent))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TryComp<SharedEyeComponent>(entity, out var eye))
|
|
||||||
{
|
|
||||||
eye.Zoom = component.Zoom;
|
|
||||||
}
|
|
||||||
|
|
||||||
component.SubscribedPilots.Add(pilotComponent);
|
|
||||||
|
|
||||||
_alertsSystem.ShowAlert(entity, AlertType.PilotingShuttle);
|
|
||||||
|
|
||||||
entity.PopupMessage(Loc.GetString("shuttle-pilot-start"));
|
|
||||||
pilotComponent.Console = component;
|
|
||||||
ActionBlockerSystem.UpdateCanMove(entity);
|
|
||||||
pilotComponent.Position = EntityManager.GetComponent<TransformComponent>(entity).Coordinates;
|
|
||||||
pilotComponent.Dirty();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemovePilot(PilotComponent pilotComponent)
|
|
||||||
{
|
|
||||||
var console = pilotComponent.Console;
|
|
||||||
|
|
||||||
if (console is not ShuttleConsoleComponent helmsman) return;
|
|
||||||
|
|
||||||
pilotComponent.Console = null;
|
|
||||||
pilotComponent.Position = null;
|
|
||||||
|
|
||||||
if (TryComp<SharedEyeComponent>(pilotComponent.Owner, out var eye))
|
|
||||||
{
|
|
||||||
eye.Zoom = new(1.0f, 1.0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!helmsman.SubscribedPilots.Remove(pilotComponent)) return;
|
|
||||||
|
|
||||||
_alertsSystem.ClearAlert(pilotComponent.Owner, AlertType.PilotingShuttle);
|
|
||||||
|
|
||||||
pilotComponent.Owner.PopupMessage(Loc.GetString("shuttle-pilot-end"));
|
|
||||||
|
|
||||||
if (pilotComponent.LifeStage < ComponentLifeStage.Stopping)
|
|
||||||
EntityManager.RemoveComponent<PilotComponent>(pilotComponent.Owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RemovePilot(EntityUid entity)
|
|
||||||
{
|
|
||||||
if (!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent)) return;
|
|
||||||
|
|
||||||
RemovePilot(pilotComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ClearPilots(ShuttleConsoleComponent component)
|
|
||||||
{
|
|
||||||
while (component.SubscribedPilots.TryGetValue(0, out var pilot))
|
|
||||||
{
|
|
||||||
RemovePilot(pilot);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
15
Content.Server/Shuttles/Events/DockEvent.cs
Normal file
15
Content.Server/Shuttles/Events/DockEvent.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using Content.Server.Shuttles.Components;
|
||||||
|
|
||||||
|
namespace Content.Server.Shuttles.Events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised whenever 2 airlocks dock.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class DockEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public DockingComponent DockA = default!;
|
||||||
|
public DockingComponent DockB = default!;
|
||||||
|
|
||||||
|
public EntityUid GridAUid = default!;
|
||||||
|
public EntityUid GridBUid = default!;
|
||||||
|
}
|
||||||
15
Content.Server/Shuttles/Events/UndockEvent.cs
Normal file
15
Content.Server/Shuttles/Events/UndockEvent.cs
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
using Content.Server.Shuttles.Components;
|
||||||
|
|
||||||
|
namespace Content.Server.Shuttles.Events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised whenever 2 grids undock.
|
||||||
|
/// </summary>
|
||||||
|
public sealed class UndockEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public DockingComponent DockA = default!;
|
||||||
|
public DockingComponent DockB = default!;
|
||||||
|
|
||||||
|
public EntityUid GridAUid = default!;
|
||||||
|
public EntityUid GridBUid = default!;
|
||||||
|
}
|
||||||
104
Content.Server/Shuttles/Systems/DockingSystem.AutoDock.cs
Normal file
104
Content.Server/Shuttles/Systems/DockingSystem.AutoDock.cs
Normal file
@@ -0,0 +1,104 @@
|
|||||||
|
using Content.Server.Shuttles.Components;
|
||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Content.Shared.Shuttles.Events;
|
||||||
|
using Robust.Shared.Players;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Server.Shuttles.Systems;
|
||||||
|
|
||||||
|
public sealed partial class DockingSystem
|
||||||
|
{
|
||||||
|
private void UpdateAutodock()
|
||||||
|
{
|
||||||
|
// Work out what we can autodock with, what we shouldn't, and when we should stop tracking.
|
||||||
|
// Autodocking only stops when the client closes that dock viewport OR they lose pilotcomponent.
|
||||||
|
var dockingQuery = GetEntityQuery<DockingComponent>();
|
||||||
|
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||||
|
var recentQuery = GetEntityQuery<RecentlyDockedComponent>();
|
||||||
|
|
||||||
|
foreach (var (comp, body) in EntityQuery<AutoDockComponent, PhysicsComponent>())
|
||||||
|
{
|
||||||
|
if (comp.Requesters.Count == 0 || !dockingQuery.TryGetComponent(comp.Owner, out var dock))
|
||||||
|
{
|
||||||
|
RemComp<AutoDockComponent>(comp.Owner);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Don't re-dock if we're already docked or recently were.
|
||||||
|
if (dock.Docked || recentQuery.HasComponent(comp.Owner)) continue;
|
||||||
|
|
||||||
|
var dockable = GetDockable(body, xformQuery.GetComponent(comp.Owner));
|
||||||
|
|
||||||
|
if (dockable == null) continue;
|
||||||
|
|
||||||
|
TryDock(dock, dockable);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Work out recent docks that have gone past their designated threshold.
|
||||||
|
var checkedRecent = new HashSet<EntityUid>();
|
||||||
|
|
||||||
|
foreach (var (comp, xform) in EntityQuery<RecentlyDockedComponent, TransformComponent>())
|
||||||
|
{
|
||||||
|
if (!checkedRecent.Add(comp.Owner)) continue;
|
||||||
|
|
||||||
|
if (!dockingQuery.TryGetComponent(comp.Owner, out var dock))
|
||||||
|
{
|
||||||
|
RemComp<RecentlyDockedComponent>(comp.Owner);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!xformQuery.TryGetComponent(comp.LastDocked, out var otherXform))
|
||||||
|
{
|
||||||
|
RemComp<RecentlyDockedComponent>(comp.Owner);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var worldPos = _transformSystem.GetWorldPosition(xform, xformQuery);
|
||||||
|
var otherWorldPos = _transformSystem.GetWorldPosition(otherXform, xformQuery);
|
||||||
|
|
||||||
|
if ((worldPos - otherWorldPos).Length < comp.Radius) continue;
|
||||||
|
|
||||||
|
_sawmill.Debug($"Removed RecentlyDocked from {ToPrettyString(comp.Owner)} and {ToPrettyString(comp.LastDocked)}");
|
||||||
|
RemComp<RecentlyDockedComponent>(comp.Owner);
|
||||||
|
RemComp<RecentlyDockedComponent>(comp.LastDocked);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRequestUndock(EntityUid uid, ShuttleConsoleComponent component, UndockRequestMessage args)
|
||||||
|
{
|
||||||
|
_sawmill.Debug($"Received undock request for {ToPrettyString(args.Entity)}");
|
||||||
|
|
||||||
|
// TODO: Validation
|
||||||
|
if (!TryComp<DockingComponent>(args.Entity, out var dock) ||
|
||||||
|
!dock.Docked) return;
|
||||||
|
|
||||||
|
Undock(dock);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRequestAutodock(EntityUid uid, ShuttleConsoleComponent component, AutodockRequestMessage args)
|
||||||
|
{
|
||||||
|
_sawmill.Debug($"Received autodock request for {ToPrettyString(args.Entity)}");
|
||||||
|
var player = args.Session.AttachedEntity;
|
||||||
|
|
||||||
|
if (player == null || !HasComp<DockingComponent>(args.Entity)) return;
|
||||||
|
|
||||||
|
// TODO: Validation
|
||||||
|
var comp = EnsureComp<AutoDockComponent>(args.Entity);
|
||||||
|
comp.Requesters.Add(player.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRequestStopAutodock(EntityUid uid, ShuttleConsoleComponent component, StopAutodockRequestMessage args)
|
||||||
|
{
|
||||||
|
_sawmill.Debug($"Received stop autodock request for {ToPrettyString(args.Entity)}");
|
||||||
|
|
||||||
|
var player = args.Session.AttachedEntity;
|
||||||
|
|
||||||
|
// TODO: Validation
|
||||||
|
if (player == null || !TryComp<AutoDockComponent>(args.Entity, out var comp)) return;
|
||||||
|
|
||||||
|
comp.Requesters.Remove(player.Value);
|
||||||
|
|
||||||
|
if (comp.Requesters.Count == 0)
|
||||||
|
RemComp<AutoDockComponent>(args.Entity);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,24 +1,30 @@
|
|||||||
using Content.Server.Doors.Systems;
|
using Content.Server.Doors.Systems;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Shuttles.Components;
|
using Content.Server.Shuttles.Components;
|
||||||
|
using Content.Server.Shuttles.Events;
|
||||||
using Content.Shared.Doors;
|
using Content.Shared.Doors;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
|
using Content.Shared.Shuttles.Events;
|
||||||
using Content.Shared.Verbs;
|
using Content.Shared.Verbs;
|
||||||
|
using Robust.Server.Player;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Physics.Collision.Shapes;
|
using Robust.Shared.Physics.Collision.Shapes;
|
||||||
using Robust.Shared.Physics.Dynamics;
|
using Robust.Shared.Physics.Dynamics;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Shuttles.EntitySystems
|
namespace Content.Server.Shuttles.Systems
|
||||||
{
|
{
|
||||||
public sealed class DockingSystem : EntitySystem
|
public sealed partial class DockingSystem : EntitySystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly FixtureSystem _fixtureSystem = default!;
|
[Dependency] private readonly FixtureSystem _fixtureSystem = default!;
|
||||||
[Dependency] private readonly SharedJointSystem _jointSystem = default!;
|
[Dependency] private readonly SharedJointSystem _jointSystem = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _transformSystem = default!;
|
||||||
|
[Dependency] private readonly ShuttleConsoleSystem _console = default!;
|
||||||
[Dependency] private readonly DoorSystem _doorSystem = default!;
|
[Dependency] private readonly DoorSystem _doorSystem = default!;
|
||||||
|
|
||||||
|
private ISawmill _sawmill = default!;
|
||||||
private const string DockingFixture = "docking";
|
private const string DockingFixture = "docking";
|
||||||
private const string DockingJoint = "docking";
|
private const string DockingJoint = "docking";
|
||||||
private const float DockingRadius = 0.20f;
|
private const float DockingRadius = 0.20f;
|
||||||
@@ -26,14 +32,26 @@ namespace Content.Server.Shuttles.EntitySystems
|
|||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
_sawmill = Logger.GetSawmill("docking");
|
||||||
SubscribeLocalEvent<DockingComponent, ComponentStartup>(OnStartup);
|
SubscribeLocalEvent<DockingComponent, ComponentStartup>(OnStartup);
|
||||||
SubscribeLocalEvent<DockingComponent, ComponentShutdown>(OnShutdown);
|
SubscribeLocalEvent<DockingComponent, ComponentShutdown>(OnShutdown);
|
||||||
SubscribeLocalEvent<DockingComponent, PowerChangedEvent>(OnPowerChange);
|
SubscribeLocalEvent<DockingComponent, PowerChangedEvent>(OnPowerChange);
|
||||||
SubscribeLocalEvent<DockingComponent, AnchorStateChangedEvent>(OnAnchorChange);
|
SubscribeLocalEvent<DockingComponent, AnchorStateChangedEvent>(OnAnchorChange);
|
||||||
SubscribeLocalEvent<DockingComponent, ReAnchorEvent>(OnDockingReAnchor);
|
SubscribeLocalEvent<DockingComponent, ReAnchorEvent>(OnDockingReAnchor);
|
||||||
|
|
||||||
SubscribeLocalEvent<DockingComponent, GetVerbsEvent<InteractionVerb>>(OnVerb);
|
|
||||||
SubscribeLocalEvent<DockingComponent, BeforeDoorAutoCloseEvent>(OnAutoClose);
|
SubscribeLocalEvent<DockingComponent, BeforeDoorAutoCloseEvent>(OnAutoClose);
|
||||||
|
|
||||||
|
// Yes this isn't in shuttle console; it may be used by other systems technically.
|
||||||
|
// in which case I would also add their subs here.
|
||||||
|
SubscribeLocalEvent<ShuttleConsoleComponent, AutodockRequestMessage>(OnRequestAutodock);
|
||||||
|
SubscribeLocalEvent<ShuttleConsoleComponent, StopAutodockRequestMessage>(OnRequestStopAutodock);
|
||||||
|
SubscribeLocalEvent<ShuttleConsoleComponent, UndockRequestMessage>(OnRequestUndock);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
UpdateAutodock();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAutoClose(EntityUid uid, DockingComponent component, BeforeDoorAutoCloseEvent args)
|
private void OnAutoClose(EntityUid uid, DockingComponent component, BeforeDoorAutoCloseEvent args)
|
||||||
@@ -43,56 +61,6 @@ namespace Content.Server.Shuttles.EntitySystems
|
|||||||
args.Cancel();
|
args.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnVerb(EntityUid uid, DockingComponent component, GetVerbsEvent<InteractionVerb> args)
|
|
||||||
{
|
|
||||||
if (!args.CanInteract ||
|
|
||||||
!args.CanAccess) return;
|
|
||||||
|
|
||||||
InteractionVerb? verb;
|
|
||||||
|
|
||||||
// TODO: Have it open the UI and have the UI do this.
|
|
||||||
if (!component.Docked &&
|
|
||||||
TryComp(uid, out PhysicsComponent? body) &&
|
|
||||||
TryComp(uid, out TransformComponent? xform))
|
|
||||||
{
|
|
||||||
DockingComponent? otherDock = null;
|
|
||||||
|
|
||||||
if (component.Enabled)
|
|
||||||
otherDock = GetDockable(body, xform);
|
|
||||||
|
|
||||||
verb = new InteractionVerb
|
|
||||||
{
|
|
||||||
Disabled = otherDock == null,
|
|
||||||
Text = Loc.GetString("docking-component-dock"),
|
|
||||||
Act = () =>
|
|
||||||
{
|
|
||||||
if (otherDock == null) return;
|
|
||||||
TryDock(component, otherDock);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (component.Docked)
|
|
||||||
{
|
|
||||||
verb = new InteractionVerb
|
|
||||||
{
|
|
||||||
Disabled = !component.Docked,
|
|
||||||
Text = Loc.GetString("docking-component-undock"),
|
|
||||||
Act = () =>
|
|
||||||
{
|
|
||||||
if (component.DockedWith == null || !component.Enabled) return;
|
|
||||||
|
|
||||||
Undock(component);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
args.Verbs.Add(verb);
|
|
||||||
}
|
|
||||||
|
|
||||||
private DockingComponent? GetDockable(PhysicsComponent body, TransformComponent dockingXform)
|
private DockingComponent? GetDockable(PhysicsComponent body, TransformComponent dockingXform)
|
||||||
{
|
{
|
||||||
// Did you know Saltern is the most dockable station?
|
// Did you know Saltern is the most dockable station?
|
||||||
@@ -105,12 +73,9 @@ namespace Content.Server.Shuttles.EntitySystems
|
|||||||
var transform = body.GetTransform();
|
var transform = body.GetTransform();
|
||||||
var dockingFixture = _fixtureSystem.GetFixtureOrNull(body, DockingFixture);
|
var dockingFixture = _fixtureSystem.GetFixtureOrNull(body, DockingFixture);
|
||||||
|
|
||||||
|
// Happens if no power or whatever
|
||||||
if (dockingFixture == null)
|
if (dockingFixture == null)
|
||||||
{
|
|
||||||
DebugTools.Assert(false);
|
|
||||||
Logger.ErrorS("docking", $"Found null fixture on {(body).Owner}");
|
|
||||||
return null;
|
return null;
|
||||||
}
|
|
||||||
|
|
||||||
Box2? aabb = null;
|
Box2? aabb = null;
|
||||||
|
|
||||||
@@ -142,7 +107,7 @@ namespace Content.Server.Shuttles.EntitySystems
|
|||||||
if (otherDockingFixture == null)
|
if (otherDockingFixture == null)
|
||||||
{
|
{
|
||||||
DebugTools.Assert(false);
|
DebugTools.Assert(false);
|
||||||
Logger.ErrorS("docking", $"Found null docking fixture on {ent}");
|
_sawmill.Error($"Found null docking fixture on {ent}");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -181,7 +146,7 @@ namespace Content.Server.Shuttles.EntitySystems
|
|||||||
!TryComp(dockBUid, out DockingComponent? dockB))
|
!TryComp(dockBUid, out DockingComponent? dockB))
|
||||||
{
|
{
|
||||||
DebugTools.Assert(false);
|
DebugTools.Assert(false);
|
||||||
Logger.Error("docking", $"Tried to cleanup {dockA.Owner} but not docked?");
|
_sawmill.Error($"Tried to cleanup {dockA.Owner} but not docked?");
|
||||||
|
|
||||||
dockA.DockedWith = null;
|
dockA.DockedWith = null;
|
||||||
if (dockA.DockJoint != null)
|
if (dockA.DockJoint != null)
|
||||||
@@ -247,6 +212,8 @@ namespace Content.Server.Shuttles.EntitySystems
|
|||||||
{
|
{
|
||||||
DisableDocking(uid, component);
|
DisableDocking(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_console.RefreshShuttleConsoles();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDockingReAnchor(EntityUid uid, DockingComponent component, ref ReAnchorEvent args)
|
private void OnDockingReAnchor(EntityUid uid, DockingComponent component, ref ReAnchorEvent args)
|
||||||
@@ -257,6 +224,7 @@ namespace Content.Server.Shuttles.EntitySystems
|
|||||||
|
|
||||||
Undock(component);
|
Undock(component);
|
||||||
Dock(component, other);
|
Dock(component, other);
|
||||||
|
_console.RefreshShuttleConsoles();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnPowerChange(EntityUid uid, DockingComponent component, PowerChangedEvent args)
|
private void OnPowerChange(EntityUid uid, DockingComponent component, PowerChangedEvent args)
|
||||||
@@ -329,12 +297,11 @@ namespace Content.Server.Shuttles.EntitySystems
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void Dock(DockingComponent dockA, DockingComponent dockB)
|
public void Dock(DockingComponent dockA, DockingComponent dockB)
|
||||||
{
|
{
|
||||||
Logger.DebugS("docking", $"Docking between {dockA.Owner} and {dockB.Owner}");
|
_sawmill.Debug($"Docking between {dockA.Owner} and {dockB.Owner}");
|
||||||
|
|
||||||
// https://gamedev.stackexchange.com/questions/98772/b2distancejoint-with-frequency-equal-to-0-vs-b2weldjoint
|
// https://gamedev.stackexchange.com/questions/98772/b2distancejoint-with-frequency-equal-to-0-vs-b2weldjoint
|
||||||
|
|
||||||
// We could also potentially use a prismatic joint? Depending if we want clamps that can extend or whatever
|
// We could also potentially use a prismatic joint? Depending if we want clamps that can extend or whatever
|
||||||
|
|
||||||
var dockAXform = EntityManager.GetComponent<TransformComponent>(dockA.Owner);
|
var dockAXform = EntityManager.GetComponent<TransformComponent>(dockA.Owner);
|
||||||
var dockBXform = EntityManager.GetComponent<TransformComponent>(dockB.Owner);
|
var dockBXform = EntityManager.GetComponent<TransformComponent>(dockB.Owner);
|
||||||
|
|
||||||
@@ -397,10 +364,7 @@ namespace Content.Server.Shuttles.EntitySystems
|
|||||||
EntityManager.EventBus.RaiseEvent(EventSource.Local, msg);
|
EntityManager.EventBus.RaiseEvent(EventSource.Local, msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
private bool CanDock(DockingComponent dockA, DockingComponent dockB)
|
||||||
/// Attempts to dock 2 ports together and will return early if it's not possible.
|
|
||||||
/// </summary>
|
|
||||||
private void TryDock(DockingComponent dockA, DockingComponent dockB)
|
|
||||||
{
|
{
|
||||||
if (!TryComp(dockA.Owner, out PhysicsComponent? bodyA) ||
|
if (!TryComp(dockA.Owner, out PhysicsComponent? bodyA) ||
|
||||||
!TryComp(dockB.Owner, out PhysicsComponent? bodyB) ||
|
!TryComp(dockB.Owner, out PhysicsComponent? bodyB) ||
|
||||||
@@ -409,7 +373,7 @@ namespace Content.Server.Shuttles.EntitySystems
|
|||||||
dockA.DockedWith != null ||
|
dockA.DockedWith != null ||
|
||||||
dockB.DockedWith != null)
|
dockB.DockedWith != null)
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var fixtureA = _fixtureSystem.GetFixtureOrNull(bodyA, DockingFixture);
|
var fixtureA = _fixtureSystem.GetFixtureOrNull(bodyA, DockingFixture);
|
||||||
@@ -417,7 +381,7 @@ namespace Content.Server.Shuttles.EntitySystems
|
|||||||
|
|
||||||
if (fixtureA == null || fixtureB == null)
|
if (fixtureA == null || fixtureB == null)
|
||||||
{
|
{
|
||||||
return;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
var transformA = bodyA.GetTransform();
|
var transformA = bodyA.GetTransform();
|
||||||
@@ -441,7 +405,15 @@ namespace Content.Server.Shuttles.EntitySystems
|
|||||||
if (intersect) break;
|
if (intersect) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!intersect) return;
|
return intersect;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Attempts to dock 2 ports together and will return early if it's not possible.
|
||||||
|
/// </summary>
|
||||||
|
private void TryDock(DockingComponent dockA, DockingComponent dockB)
|
||||||
|
{
|
||||||
|
if (!CanDock(dockA, dockB)) return;
|
||||||
|
|
||||||
Dock(dockA, dockB);
|
Dock(dockA, dockB);
|
||||||
}
|
}
|
||||||
@@ -451,7 +423,7 @@ namespace Content.Server.Shuttles.EntitySystems
|
|||||||
if (dock.DockedWith == null)
|
if (dock.DockedWith == null)
|
||||||
{
|
{
|
||||||
DebugTools.Assert(false);
|
DebugTools.Assert(false);
|
||||||
Logger.ErrorS("docking", $"Tried to undock {(dock).Owner} but not docked with anything?");
|
_sawmill.Error($"Tried to undock {(dock).Owner} but not docked with anything?");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -467,33 +439,12 @@ namespace Content.Server.Shuttles.EntitySystems
|
|||||||
_doorSystem.TryClose(doorB.Owner, doorB);
|
_doorSystem.TryClose(doorB.Owner, doorB);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Could maybe give the shuttle a light push away, or at least if there's no other docks left?
|
var recentlyDocked = EnsureComp<RecentlyDockedComponent>(dock.Owner);
|
||||||
|
recentlyDocked.LastDocked = dock.DockedWith.Value;
|
||||||
|
recentlyDocked = EnsureComp<RecentlyDockedComponent>(dock.DockedWith.Value);
|
||||||
|
recentlyDocked.LastDocked = dock.DockedWith.Value;
|
||||||
|
|
||||||
Cleanup(dock);
|
Cleanup(dock);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised whenever 2 airlocks dock.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class DockEvent : EntityEventArgs
|
|
||||||
{
|
|
||||||
public DockingComponent DockA = default!;
|
|
||||||
public DockingComponent DockB = default!;
|
|
||||||
|
|
||||||
public EntityUid GridAUid = default!;
|
|
||||||
public EntityUid GridBUid = default!;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised whenever 2 grids undock.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class UndockEvent : EntityEventArgs
|
|
||||||
{
|
|
||||||
public DockingComponent DockA = default!;
|
|
||||||
public DockingComponent DockB = default!;
|
|
||||||
|
|
||||||
public EntityUid GridAUid = default!;
|
|
||||||
public EntityUid GridBUid = default!;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
29
Content.Server/Shuttles/Systems/RadarConsoleSystem.cs
Normal file
29
Content.Server/Shuttles/Systems/RadarConsoleSystem.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Content.Shared.Shuttles.Systems;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
|
namespace Content.Server.Shuttles.Systems;
|
||||||
|
|
||||||
|
public sealed class RadarConsoleSystem : SharedRadarConsoleSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<RadarConsoleComponent, ComponentStartup>(OnRadarStartup);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRadarStartup(EntityUid uid, RadarConsoleComponent component, ComponentStartup args)
|
||||||
|
{
|
||||||
|
UpdateState(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateState(RadarConsoleComponent component)
|
||||||
|
{
|
||||||
|
var radarState = new RadarConsoleBoundInterfaceState(component.MaxRange, component.Owner, new List<DockingInterfaceState>());
|
||||||
|
_uiSystem.GetUiOrNull(component.Owner, RadarConsoleUiKey.Key)?.SetState(radarState);
|
||||||
|
}
|
||||||
|
}
|
||||||
337
Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs
Normal file
337
Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs
Normal file
@@ -0,0 +1,337 @@
|
|||||||
|
using Content.Server.Power.Components;
|
||||||
|
using Content.Server.Power.EntitySystems;
|
||||||
|
using Content.Server.Shuttles.Components;
|
||||||
|
using Content.Server.Shuttles.Events;
|
||||||
|
using Content.Server.UserInterface;
|
||||||
|
using Content.Shared.ActionBlocker;
|
||||||
|
using Content.Shared.Alert;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Content.Shared.Shuttles.Events;
|
||||||
|
using Content.Shared.Shuttles.Systems;
|
||||||
|
using Content.Shared.Tag;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Server.Shuttles.Systems
|
||||||
|
{
|
||||||
|
public sealed class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||||
|
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
|
||||||
|
[Dependency] private readonly TagSystem _tags = default!;
|
||||||
|
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ShuttleConsoleComponent, ComponentShutdown>(OnConsoleShutdown);
|
||||||
|
SubscribeLocalEvent<ShuttleConsoleComponent, PowerChangedEvent>(OnConsolePowerChange);
|
||||||
|
SubscribeLocalEvent<ShuttleConsoleComponent, AnchorStateChangedEvent>(OnConsoleAnchorChange);
|
||||||
|
SubscribeLocalEvent<ShuttleConsoleComponent, ActivatableUIOpenAttemptEvent>(OnConsoleUIOpenAttempt);
|
||||||
|
SubscribeLocalEvent<ShuttleConsoleComponent, ShuttleModeRequestMessage>(OnModeRequest);
|
||||||
|
SubscribeLocalEvent<ShuttleConsoleComponent, BoundUIClosedEvent>(OnConsoleUIClose);
|
||||||
|
SubscribeLocalEvent<DockEvent>(OnDock);
|
||||||
|
SubscribeLocalEvent<UndockEvent>(OnUndock);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<PilotComponent, MoveEvent>(HandlePilotMove);
|
||||||
|
SubscribeLocalEvent<PilotComponent, ComponentGetState>(OnGetState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDock(DockEvent ev)
|
||||||
|
{
|
||||||
|
RefreshShuttleConsoles();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnUndock(UndockEvent ev)
|
||||||
|
{
|
||||||
|
RefreshShuttleConsoles();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Refreshes all of the data for shuttle consoles.
|
||||||
|
/// </summary>
|
||||||
|
public void RefreshShuttleConsoles()
|
||||||
|
{
|
||||||
|
// TODO: Should really call this per shuttle in some instances.
|
||||||
|
var docks = GetAllDocks();
|
||||||
|
|
||||||
|
foreach (var comp in EntityQuery<ShuttleConsoleComponent>(true))
|
||||||
|
{
|
||||||
|
UpdateState(comp, docks);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stop piloting if the window is closed.
|
||||||
|
/// </summary>
|
||||||
|
private void OnConsoleUIClose(EntityUid uid, ShuttleConsoleComponent component, BoundUIClosedEvent args)
|
||||||
|
{
|
||||||
|
if ((ShuttleConsoleUiKey) args.UiKey != ShuttleConsoleUiKey.Key ||
|
||||||
|
args.Session.AttachedEntity is not {} user) return;
|
||||||
|
|
||||||
|
// In case they D/C should still clean them up.
|
||||||
|
foreach (var comp in EntityQuery<AutoDockComponent>(true))
|
||||||
|
{
|
||||||
|
comp.Requesters.Remove(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
RemovePilot(user);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnConsoleUIOpenAttempt(EntityUid uid, ShuttleConsoleComponent component, ActivatableUIOpenAttemptEvent args)
|
||||||
|
{
|
||||||
|
if (!TryPilot(args.User, uid))
|
||||||
|
args.Cancel();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnConsoleAnchorChange(EntityUid uid, ShuttleConsoleComponent component, ref AnchorStateChangedEvent args)
|
||||||
|
{
|
||||||
|
UpdateState(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnConsolePowerChange(EntityUid uid, ShuttleConsoleComponent component, PowerChangedEvent args)
|
||||||
|
{
|
||||||
|
UpdateState(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryPilot(EntityUid user, EntityUid uid)
|
||||||
|
{
|
||||||
|
if (!_tags.HasTag(user, "CanPilot") ||
|
||||||
|
!TryComp<ShuttleConsoleComponent>(uid, out var component) ||
|
||||||
|
!this.IsPowered(uid, EntityManager) ||
|
||||||
|
!Transform(uid).Anchored ||
|
||||||
|
!_blocker.CanInteract(user, uid))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pilotComponent = EntityManager.EnsureComponent<PilotComponent>(user);
|
||||||
|
var console = pilotComponent.Console;
|
||||||
|
|
||||||
|
if (console != null)
|
||||||
|
{
|
||||||
|
RemovePilot(pilotComponent);
|
||||||
|
|
||||||
|
if (console == component)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AddPilot(user, component);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetState(EntityUid uid, PilotComponent component, ref ComponentGetState args)
|
||||||
|
{
|
||||||
|
args.State = new PilotComponentState(component.Console?.Owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Client is requesting a change in the shuttle's driving mode.
|
||||||
|
/// </summary>
|
||||||
|
private void OnModeRequest(EntityUid uid, ShuttleConsoleComponent component, ShuttleModeRequestMessage args)
|
||||||
|
{
|
||||||
|
if (args.Session.AttachedEntity is not { } player ||
|
||||||
|
!TryComp<PilotComponent>(player, out var pilot) ||
|
||||||
|
!TryComp<TransformComponent>(player, out var xform) ||
|
||||||
|
pilot.Console is not ShuttleConsoleComponent console) return;
|
||||||
|
|
||||||
|
if (!console.SubscribedPilots.Contains(pilot) ||
|
||||||
|
!TryComp<ShuttleComponent>(xform.GridEntityId, out var shuttle)) return;
|
||||||
|
|
||||||
|
SetShuttleMode(args.Mode, console, shuttle);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the shuttle's movement mode. Does minimal revalidation.
|
||||||
|
/// </summary>
|
||||||
|
private void SetShuttleMode(ShuttleMode mode, ShuttleConsoleComponent consoleComponent,
|
||||||
|
ShuttleComponent shuttleComponent, TransformComponent? consoleXform = null)
|
||||||
|
{
|
||||||
|
// Re-validate
|
||||||
|
if (!this.IsPowered(consoleComponent.Owner, EntityManager) ||
|
||||||
|
!Resolve(consoleComponent.Owner, ref consoleXform) ||
|
||||||
|
!consoleXform.Anchored ||
|
||||||
|
consoleXform.GridID != Transform(shuttleComponent.Owner).GridID)
|
||||||
|
{
|
||||||
|
UpdateState(consoleComponent);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
shuttleComponent.Mode = mode;
|
||||||
|
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case ShuttleMode.Strafing:
|
||||||
|
break;
|
||||||
|
case ShuttleMode.Cruise:
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateState(consoleComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the position and angle of all dockingcomponents.
|
||||||
|
/// </summary>
|
||||||
|
private List<DockingInterfaceState> GetAllDocks()
|
||||||
|
{
|
||||||
|
// TODO: NEED TO MAKE SURE THIS UPDATES ON ANCHORING CHANGES!
|
||||||
|
var result = new List<DockingInterfaceState>();
|
||||||
|
|
||||||
|
foreach (var (comp, xform) in EntityQuery<DockingComponent, TransformComponent>(true))
|
||||||
|
{
|
||||||
|
if (xform.ParentUid != xform.GridUid) continue;
|
||||||
|
|
||||||
|
var state = new DockingInterfaceState()
|
||||||
|
{
|
||||||
|
Coordinates = xform.Coordinates,
|
||||||
|
Angle = xform.LocalRotation,
|
||||||
|
Entity = comp.Owner,
|
||||||
|
Connected = comp.Docked,
|
||||||
|
};
|
||||||
|
result.Add(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateState(ShuttleConsoleComponent component, List<DockingInterfaceState>? docks = null)
|
||||||
|
{
|
||||||
|
TryComp<RadarConsoleComponent>(component.Owner, out var radar);
|
||||||
|
var range = radar?.MaxRange ?? 0f;
|
||||||
|
|
||||||
|
TryComp<ShuttleComponent>(Transform(component.Owner).GridUid, out var shuttle);
|
||||||
|
var mode = shuttle?.Mode ?? ShuttleMode.Cruise;
|
||||||
|
|
||||||
|
docks ??= GetAllDocks();
|
||||||
|
|
||||||
|
_ui.GetUiOrNull(component.Owner, ShuttleConsoleUiKey.Key)
|
||||||
|
?.SetState(new ShuttleConsoleBoundInterfaceState(
|
||||||
|
mode,
|
||||||
|
range,
|
||||||
|
component.Owner,
|
||||||
|
docks));
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(float frameTime)
|
||||||
|
{
|
||||||
|
base.Update(frameTime);
|
||||||
|
|
||||||
|
var toRemove = new RemQueue<PilotComponent>();
|
||||||
|
|
||||||
|
foreach (var comp in EntityManager.EntityQuery<PilotComponent>())
|
||||||
|
{
|
||||||
|
if (comp.Console == null) continue;
|
||||||
|
|
||||||
|
if (!_blocker.CanInteract(comp.Owner, comp.Console.Owner))
|
||||||
|
{
|
||||||
|
toRemove.Add(comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var comp in toRemove)
|
||||||
|
{
|
||||||
|
RemovePilot(comp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If pilot is moved then we'll stop them from piloting.
|
||||||
|
/// </summary>
|
||||||
|
private void HandlePilotMove(EntityUid uid, PilotComponent component, ref MoveEvent args)
|
||||||
|
{
|
||||||
|
if (component.Console == null || component.Position == null)
|
||||||
|
{
|
||||||
|
DebugTools.Assert(component.Position == null && component.Console == null);
|
||||||
|
EntityManager.RemoveComponent<PilotComponent>(uid);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (args.NewPosition.TryDistance(EntityManager, component.Position.Value, out var distance) &&
|
||||||
|
distance < PilotComponent.BreakDistance) return;
|
||||||
|
|
||||||
|
RemovePilot(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void HandlePilotShutdown(EntityUid uid, PilotComponent component, ComponentShutdown args)
|
||||||
|
{
|
||||||
|
base.HandlePilotShutdown(uid, component, args);
|
||||||
|
RemovePilot(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnConsoleShutdown(EntityUid uid, ShuttleConsoleComponent component, ComponentShutdown args)
|
||||||
|
{
|
||||||
|
ClearPilots(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddPilot(EntityUid entity, ShuttleConsoleComponent component)
|
||||||
|
{
|
||||||
|
if (!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent) ||
|
||||||
|
component.SubscribedPilots.Contains(pilotComponent))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryComp<SharedEyeComponent>(entity, out var eye))
|
||||||
|
{
|
||||||
|
eye.Zoom = component.Zoom;
|
||||||
|
}
|
||||||
|
|
||||||
|
component.SubscribedPilots.Add(pilotComponent);
|
||||||
|
|
||||||
|
_alertsSystem.ShowAlert(entity, AlertType.PilotingShuttle);
|
||||||
|
|
||||||
|
pilotComponent.Console = component;
|
||||||
|
ActionBlockerSystem.UpdateCanMove(entity);
|
||||||
|
pilotComponent.Position = EntityManager.GetComponent<TransformComponent>(entity).Coordinates;
|
||||||
|
Dirty(pilotComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemovePilot(PilotComponent pilotComponent)
|
||||||
|
{
|
||||||
|
var console = pilotComponent.Console;
|
||||||
|
|
||||||
|
if (console is not ShuttleConsoleComponent helmsman) return;
|
||||||
|
|
||||||
|
pilotComponent.Console = null;
|
||||||
|
pilotComponent.Position = null;
|
||||||
|
|
||||||
|
if (TryComp<SharedEyeComponent>(pilotComponent.Owner, out var eye))
|
||||||
|
{
|
||||||
|
eye.Zoom = new(1.0f, 1.0f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!helmsman.SubscribedPilots.Remove(pilotComponent)) return;
|
||||||
|
|
||||||
|
_alertsSystem.ClearAlert(pilotComponent.Owner, AlertType.PilotingShuttle);
|
||||||
|
|
||||||
|
pilotComponent.Owner.PopupMessage(Loc.GetString("shuttle-pilot-end"));
|
||||||
|
|
||||||
|
if (pilotComponent.LifeStage < ComponentLifeStage.Stopping)
|
||||||
|
EntityManager.RemoveComponent<PilotComponent>(pilotComponent.Owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemovePilot(EntityUid entity)
|
||||||
|
{
|
||||||
|
if (!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent)) return;
|
||||||
|
|
||||||
|
RemovePilot(pilotComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ClearPilots(ShuttleConsoleComponent component)
|
||||||
|
{
|
||||||
|
while (component.SubscribedPilots.TryGetValue(0, out var pilot))
|
||||||
|
{
|
||||||
|
RemovePilot(pilot);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@ using JetBrains.Annotations;
|
|||||||
using Robust.Shared.Configuration;
|
using Robust.Shared.Configuration;
|
||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
|
|
||||||
namespace Content.Server.Shuttles.EntitySystems
|
namespace Content.Server.Shuttles.Systems
|
||||||
{
|
{
|
||||||
[UsedImplicitly]
|
[UsedImplicitly]
|
||||||
public sealed class ShuttleSystem : EntitySystem
|
public sealed class ShuttleSystem : EntitySystem
|
||||||
@@ -2,7 +2,7 @@ using Content.Server.Shuttles.Components;
|
|||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Physics.Dynamics;
|
using Robust.Shared.Physics.Dynamics;
|
||||||
|
|
||||||
namespace Content.Server.Shuttles.EntitySystems;
|
namespace Content.Server.Shuttles.Systems;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Deletes anything with <see cref="SpaceGarbageComponent"/> that has a cross-grid collision with a static body.
|
/// Deletes anything with <see cref="SpaceGarbageComponent"/> that has a cross-grid collision with a static body.
|
||||||
@@ -9,8 +9,8 @@ using Content.Shared.Examine;
|
|||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Maps;
|
using Content.Shared.Maps;
|
||||||
using Content.Shared.Physics;
|
using Content.Shared.Physics;
|
||||||
using Content.Shared.Temperature;
|
|
||||||
using Content.Shared.Shuttles.Components;
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Content.Shared.Temperature;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
@@ -18,7 +18,7 @@ using Robust.Shared.Physics.Collision.Shapes;
|
|||||||
using Robust.Shared.Physics.Dynamics;
|
using Robust.Shared.Physics.Dynamics;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
namespace Content.Server.Shuttles.EntitySystems
|
namespace Content.Server.Shuttles.Systems
|
||||||
{
|
{
|
||||||
public sealed class ThrusterSystem : EntitySystem
|
public sealed class ThrusterSystem : EntitySystem
|
||||||
{
|
{
|
||||||
@@ -182,24 +182,21 @@ public sealed class StationSystem : EntitySystem
|
|||||||
|
|
||||||
if (stationConfig is not null && name is null)
|
if (stationConfig is not null && name is null)
|
||||||
{
|
{
|
||||||
metaData.EntityName = GenerateStationName(stationConfig);
|
name = GenerateStationName(stationConfig);
|
||||||
}
|
}
|
||||||
else if (name is not null)
|
else if (name is null)
|
||||||
{
|
|
||||||
metaData.EntityName = name;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
_sawmill.Error($"When setting up station {station}, was unable to find a valid name in the config and no name was provided.");
|
_sawmill.Error($"When setting up station {station}, was unable to find a valid name in the config and no name was provided.");
|
||||||
metaData.EntityName = "unnamed station";
|
name = "unnamed station";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
metaData.EntityName = name;
|
||||||
RaiseLocalEvent(new StationInitializedEvent(station));
|
RaiseLocalEvent(new StationInitializedEvent(station));
|
||||||
_sawmill.Info($"Set up station {metaData.EntityName} ({station}).");
|
_sawmill.Info($"Set up station {metaData.EntityName} ({station}).");
|
||||||
|
|
||||||
foreach (var grid in gridIds ?? Array.Empty<EntityUid>())
|
foreach (var grid in gridIds ?? Array.Empty<EntityUid>())
|
||||||
{
|
{
|
||||||
AddGridToStation(station, grid, null, data);
|
AddGridToStation(station, grid, null, data, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
return station;
|
return station;
|
||||||
@@ -213,13 +210,16 @@ public sealed class StationSystem : EntitySystem
|
|||||||
/// <param name="gridComponent">Resolve pattern, grid component of mapGrid.</param>
|
/// <param name="gridComponent">Resolve pattern, grid component of mapGrid.</param>
|
||||||
/// <param name="stationData">Resolve pattern, station data component of station.</param>
|
/// <param name="stationData">Resolve pattern, station data component of station.</param>
|
||||||
/// <exception cref="ArgumentException">Thrown when mapGrid or station are not a grid or station, respectively.</exception>
|
/// <exception cref="ArgumentException">Thrown when mapGrid or station are not a grid or station, respectively.</exception>
|
||||||
public void AddGridToStation(EntityUid station, EntityUid mapGrid, IMapGridComponent? gridComponent = null, StationDataComponent? stationData = null)
|
public void AddGridToStation(EntityUid station, EntityUid mapGrid, IMapGridComponent? gridComponent = null, StationDataComponent? stationData = null, string? name = null)
|
||||||
{
|
{
|
||||||
if (!Resolve(mapGrid, ref gridComponent))
|
if (!Resolve(mapGrid, ref gridComponent))
|
||||||
throw new ArgumentException("Tried to initialize a station on a non-grid entity!", nameof(mapGrid));
|
throw new ArgumentException("Tried to initialize a station on a non-grid entity!", nameof(mapGrid));
|
||||||
if (!Resolve(station, ref stationData))
|
if (!Resolve(station, ref stationData))
|
||||||
throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station));
|
throw new ArgumentException("Tried to use a non-station entity as a station!", nameof(station));
|
||||||
|
|
||||||
|
if (!string.IsNullOrEmpty(name))
|
||||||
|
MetaData(mapGrid).EntityName = name;
|
||||||
|
|
||||||
var stationMember = AddComp<StationMemberComponent>(mapGrid);
|
var stationMember = AddComp<StationMemberComponent>(mapGrid);
|
||||||
stationMember.Station = station;
|
stationMember.Station = station;
|
||||||
stationData.Grids.Add(gridComponent.Owner);
|
stationData.Grids.Add(gridComponent.Owner);
|
||||||
|
|||||||
@@ -1,37 +0,0 @@
|
|||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Radar;
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class RadarConsoleBoundInterfaceState : BoundUserInterfaceState
|
|
||||||
{
|
|
||||||
public float Range;
|
|
||||||
public RadarObjectData[] Objects;
|
|
||||||
|
|
||||||
public RadarConsoleBoundInterfaceState(float range, RadarObjectData[] objects)
|
|
||||||
{
|
|
||||||
Range = range;
|
|
||||||
Objects = objects;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public struct RadarObjectData
|
|
||||||
{
|
|
||||||
public Color Color;
|
|
||||||
public RadarObjectShape Shape;
|
|
||||||
public Vector2 Position;
|
|
||||||
public float Radius;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum RadarObjectShape : byte
|
|
||||||
{
|
|
||||||
Circle,
|
|
||||||
CircleFilled,
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public enum RadarConsoleUiKey : byte
|
|
||||||
{
|
|
||||||
Key
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,41 @@
|
|||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.BUIStates;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
[Virtual]
|
||||||
|
public class RadarConsoleBoundInterfaceState : BoundUserInterfaceState
|
||||||
|
{
|
||||||
|
public readonly float MaxRange;
|
||||||
|
public readonly EntityUid? Entity;
|
||||||
|
public readonly List<DockingInterfaceState> Docks;
|
||||||
|
|
||||||
|
public RadarConsoleBoundInterfaceState(
|
||||||
|
float maxRange,
|
||||||
|
EntityUid? entity,
|
||||||
|
List<DockingInterfaceState> docks)
|
||||||
|
{
|
||||||
|
MaxRange = maxRange;
|
||||||
|
Entity = entity;
|
||||||
|
Docks = docks;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// State of each individual docking port for interface purposes
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class DockingInterfaceState
|
||||||
|
{
|
||||||
|
public EntityCoordinates Coordinates;
|
||||||
|
public Angle Angle;
|
||||||
|
public EntityUid Entity;
|
||||||
|
public bool Connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum RadarConsoleUiKey : byte
|
||||||
|
{
|
||||||
|
Key
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.BUIStates;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class ShuttleConsoleBoundInterfaceState : RadarConsoleBoundInterfaceState
|
||||||
|
{
|
||||||
|
public readonly ShuttleMode Mode;
|
||||||
|
|
||||||
|
public ShuttleConsoleBoundInterfaceState(
|
||||||
|
ShuttleMode mode,
|
||||||
|
float maxRange,
|
||||||
|
EntityUid? entity,
|
||||||
|
List<DockingInterfaceState> docks) : base(maxRange, entity, docks)
|
||||||
|
{
|
||||||
|
Mode = mode;
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Content.Shared/Shuttles/Components/RadarConsoleComponent.cs
Normal file
21
Content.Shared/Shuttles/Components/RadarConsoleComponent.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using Content.Shared.Shuttles.Systems;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.Components;
|
||||||
|
|
||||||
|
[RegisterComponent, NetworkedComponent, Access(typeof(SharedRadarConsoleSystem))]
|
||||||
|
public sealed class RadarConsoleComponent : Component
|
||||||
|
{
|
||||||
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
public float RangeVV
|
||||||
|
{
|
||||||
|
get => MaxRange;
|
||||||
|
set => IoCManager
|
||||||
|
.Resolve<IEntitySystemManager>()
|
||||||
|
.GetEntitySystem<SharedRadarConsoleSystem>()
|
||||||
|
.SetRange(this, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
[ViewVariables, DataField("maxRange")]
|
||||||
|
public float MaxRange = 120f;
|
||||||
|
}
|
||||||
@@ -1,17 +1,8 @@
|
|||||||
namespace Content.Shared.Shuttles.Components
|
namespace Content.Shared.Shuttles.Components
|
||||||
{
|
{
|
||||||
public abstract class SharedShuttleComponent : Component
|
|
||||||
{
|
|
||||||
[ViewVariables]
|
|
||||||
public virtual bool Enabled { get; set; } = true;
|
|
||||||
|
|
||||||
[ViewVariables]
|
|
||||||
public ShuttleMode Mode { get; set; } = ShuttleMode.Cruise;
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum ShuttleMode : byte
|
public enum ShuttleMode : byte
|
||||||
{
|
{
|
||||||
Docking,
|
Strafing,
|
||||||
Cruise,
|
Cruise,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,12 +1,20 @@
|
|||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.Shuttles.Components
|
namespace Content.Shared.Shuttles.Components
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Interact with to start piloting a shuttle.
|
/// Interact with to start piloting a shuttle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[NetworkedComponent()]
|
[NetworkedComponent]
|
||||||
public abstract class SharedShuttleConsoleComponent : Component
|
public abstract class SharedShuttleConsoleComponent : Component
|
||||||
{
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum ShuttleConsoleUiKey : byte
|
||||||
|
{
|
||||||
|
Key,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
12
Content.Shared/Shuttles/Events/AutodockRequestMessage.cs
Normal file
12
Content.Shared/Shuttles/Events/AutodockRequestMessage.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.Events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on the client when it's viewing a particular docking port to try and dock it automatically.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class AutodockRequestMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public EntityUid Entity;
|
||||||
|
}
|
||||||
13
Content.Shared/Shuttles/Events/ShuttleModeRequestMessage.cs
Normal file
13
Content.Shared/Shuttles/Events/ShuttleModeRequestMessage.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.Events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised by the client to request the server change a particular shuttle's mode.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class ShuttleModeRequestMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public ShuttleMode Mode;
|
||||||
|
}
|
||||||
12
Content.Shared/Shuttles/Events/StopAutodockRequestMessage.cs
Normal file
12
Content.Shared/Shuttles/Events/StopAutodockRequestMessage.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.Events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on a client when it is no longer viewing a dock.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class StopAutodockRequestMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public EntityUid Entity;
|
||||||
|
}
|
||||||
12
Content.Shared/Shuttles/Events/UndockRequestMessage.cs
Normal file
12
Content.Shared/Shuttles/Events/UndockRequestMessage.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.Events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on the client when it wishes to not have 2 docking ports docked.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class UndockRequestMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public EntityUid Entity;
|
||||||
|
}
|
||||||
45
Content.Shared/Shuttles/Systems/SharedRadarConsoleSystem.cs
Normal file
45
Content.Shared/Shuttles/Systems/SharedRadarConsoleSystem.cs
Normal file
@@ -0,0 +1,45 @@
|
|||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.Systems;
|
||||||
|
|
||||||
|
public abstract class SharedRadarConsoleSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
SubscribeLocalEvent<RadarConsoleComponent, ComponentGetState>(OnGetState);
|
||||||
|
SubscribeLocalEvent<RadarConsoleComponent, ComponentHandleState>(OnHandleState);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnHandleState(EntityUid uid, RadarConsoleComponent component, ref ComponentHandleState args)
|
||||||
|
{
|
||||||
|
if (args.Current is not RadarConsoleComponentState state) return;
|
||||||
|
component.MaxRange = state.Range;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnGetState(EntityUid uid, RadarConsoleComponent component, ref ComponentGetState args)
|
||||||
|
{
|
||||||
|
args.State = new RadarConsoleComponentState()
|
||||||
|
{
|
||||||
|
Range = component.MaxRange
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
protected virtual void UpdateState(RadarConsoleComponent component) {}
|
||||||
|
|
||||||
|
public void SetRange(RadarConsoleComponent component, float value)
|
||||||
|
{
|
||||||
|
if (component.MaxRange.Equals(value)) return;
|
||||||
|
component.MaxRange = value;
|
||||||
|
Dirty(component);
|
||||||
|
UpdateState(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
protected sealed class RadarConsoleComponentState : ComponentState
|
||||||
|
{
|
||||||
|
public float Range;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,10 +1,9 @@
|
|||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
using Content.Shared.Movement;
|
using Content.Shared.Movement;
|
||||||
using Content.Shared.Shuttles.Components;
|
using Content.Shared.Shuttles.Components;
|
||||||
using Robust.Shared.GameStates;
|
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.Shuttles
|
namespace Content.Shared.Shuttles.Systems
|
||||||
{
|
{
|
||||||
public abstract class SharedShuttleConsoleSystem : EntitySystem
|
public abstract class SharedShuttleConsoleSystem : EntitySystem
|
||||||
{
|
{
|
||||||
@@ -16,38 +15,10 @@ namespace Content.Shared.Shuttles
|
|||||||
SubscribeLocalEvent<PilotComponent, UpdateCanMoveEvent>(HandleMovementBlock);
|
SubscribeLocalEvent<PilotComponent, UpdateCanMoveEvent>(HandleMovementBlock);
|
||||||
SubscribeLocalEvent<PilotComponent, ComponentStartup>(OnStartup);
|
SubscribeLocalEvent<PilotComponent, ComponentStartup>(OnStartup);
|
||||||
SubscribeLocalEvent<PilotComponent, ComponentShutdown>(HandlePilotShutdown);
|
SubscribeLocalEvent<PilotComponent, ComponentShutdown>(HandlePilotShutdown);
|
||||||
SubscribeLocalEvent<PilotComponent, ComponentGetState>(OnGetState);
|
|
||||||
SubscribeLocalEvent<PilotComponent, ComponentHandleState>(OnHandleState);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGetState(EntityUid uid, PilotComponent component, ref ComponentGetState args)
|
|
||||||
{
|
|
||||||
args.State = new PilotComponentState(component.Console?.Owner);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnHandleState(EntityUid uid, PilotComponent component, ref ComponentHandleState args)
|
|
||||||
{
|
|
||||||
if (args.Current is not PilotComponentState state) return;
|
|
||||||
|
|
||||||
var console = state.Console.GetValueOrDefault();
|
|
||||||
if (!console.IsValid())
|
|
||||||
{
|
|
||||||
component.Console = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!TryComp<SharedShuttleConsoleComponent>(console, out var shuttleConsoleComponent))
|
|
||||||
{
|
|
||||||
Logger.Warning($"Unable to set Helmsman console to {console}");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
component.Console = shuttleConsoleComponent;
|
|
||||||
ActionBlockerSystem.UpdateCanMove(uid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
private sealed class PilotComponentState : ComponentState
|
protected sealed class PilotComponentState : ComponentState
|
||||||
{
|
{
|
||||||
public EntityUid? Console { get; }
|
public EntityUid? Console { get; }
|
||||||
|
|
||||||
@@ -1 +1,2 @@
|
|||||||
radar-window-title = Mass Scanner Console
|
radar-console-window-title = Mass Scanner Console
|
||||||
|
shuttle-console-window-title = Shuttle Console
|
||||||
|
|||||||
@@ -1,5 +1,22 @@
|
|||||||
shuttle-pilot-start = Piloting ship
|
shuttle-pilot-start = Piloting ship
|
||||||
shuttle-pilot-end = Stopped piloting
|
shuttle-pilot-end = Stopped piloting
|
||||||
shuttle-mode-cruise = Cruise mode
|
|
||||||
shuttle-mode-docking = Docking mode
|
# Display
|
||||||
shuttle-mode-toggle = Toggle mode
|
shuttle-console-max-radar = Max radar range:
|
||||||
|
shuttle-console-radar = Radar range:
|
||||||
|
shuttle-console-position = Position:
|
||||||
|
shuttle-console-orientation = Orientation:
|
||||||
|
shuttle-console-linear-velocity = Linear velocity:
|
||||||
|
shuttle-console-angular-velocity = Angular velocity:
|
||||||
|
|
||||||
|
shuttle-console-docked = {$index} (Docked)
|
||||||
|
shuttle-console-dock-button = Dock {$suffix}
|
||||||
|
|
||||||
|
shuttle-console-unknown = Unknown
|
||||||
|
shuttle-console-iff-label = "{$name} ({$distance}m)
|
||||||
|
|
||||||
|
# Buttons
|
||||||
|
shuttle-console-strafing = Strafing mode
|
||||||
|
shuttle-console-iff-toggle = Show IFF
|
||||||
|
shuttle-console-dock-toggle = Show docks
|
||||||
|
shuttle-console-undock = Undock
|
||||||
|
|||||||
@@ -20,6 +20,14 @@
|
|||||||
abstract: true
|
abstract: true
|
||||||
components:
|
components:
|
||||||
- type: ShuttleConsole
|
- type: ShuttleConsole
|
||||||
|
- type: ActivatableUI
|
||||||
|
key: enum.ShuttleConsoleUiKey.Key
|
||||||
|
- type: ActivatableUIRequiresPower
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.ShuttleConsoleUiKey.Key
|
||||||
|
type: ShuttleConsoleBoundUserInterface
|
||||||
|
- type: RadarConsole
|
||||||
- type: ExtensionCableReceiver
|
- type: ExtensionCableReceiver
|
||||||
- type: PointLight
|
- type: PointLight
|
||||||
radius: 1.5
|
radius: 1.5
|
||||||
@@ -53,6 +61,8 @@
|
|||||||
- type: ComputerVisualizer
|
- type: ComputerVisualizer
|
||||||
key: syndie_key
|
key: syndie_key
|
||||||
screen: syndishuttle
|
screen: syndishuttle
|
||||||
|
- type: RadarConsole
|
||||||
|
maxRange: 512
|
||||||
- type: PointLight
|
- type: PointLight
|
||||||
radius: 1.5
|
radius: 1.5
|
||||||
energy: 1.6
|
energy: 1.6
|
||||||
|
|||||||
Reference in New Issue
Block a user