Shuttle console + FTL rework (#24430)
* Add shuttle interior drawing back Just do it per-tile she'll be right, at least it's done with 1 draw call. * Revamp shuttle console * Bunch of cleanup work * Lables sortito * dok * Pixel alignment and colours * Fix a bunch of drawing bugs * Shuttle map drawing * Drawing fixes * Map parallax working finally * weh * Commit all my stuff * mic * deez * Update everything * Xamlify everything * uh * Rudimentary blocker range * My enemies have succeeded * Bunch of changes to FTL * Heaps of cleanup * Fix FTL bugs * FTL * weewoo * FTL fallback * wew * weh * Basic FTL working * FTL working * FTL destination fixes * a * Exclusion zones * Fix drawing / FTL * Beacons working * Coordinates drawing * Fix unknown map names * Dorks beginning * State + docking cleanup start * Basic dock drawing * Bunch of drawing fixes * Batching / color fixes * Cleanup and beacons support * weh * weh * Begin pings * First draft at map objects * Map fixup * Faster drawing * Fix perf + FTL * Cached drawing * Fix drawing * Best I got * strips * Back to lists but with caching * Final optimisation * Fix dock bounds * Docking work * stinker * kobolds * Btns * Docking vis working * Fix docking pre-vis * canasses * Helldivers 2 * a * Array life * Fix * Fix TODOs * liltenhead feature club * dorking * Merge artifacts * Last-minute touchup
This commit is contained in:
@@ -1,340 +1,148 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.Computer;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Content.Shared.Shuttles.BUIStates;
|
||||
using Content.Shared.Shuttles.Components;
|
||||
using Content.Shared.Shuttles.Systems;
|
||||
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.Map;
|
||||
using Robust.Shared.Physics.Components;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.Client.Shuttles.UI;
|
||||
|
||||
[GenerateTypedNameReferences]
|
||||
public sealed partial class ShuttleConsoleWindow : FancyWindow,
|
||||
IComputerWindow<ShuttleConsoleBoundInterfaceState>
|
||||
IComputerWindow<ShuttleBoundUserInterfaceState>
|
||||
{
|
||||
private readonly IEntityManager _entManager;
|
||||
private readonly IGameTiming _timing;
|
||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||
|
||||
private EntityUid? _shuttleEntity;
|
||||
private ShuttleConsoleMode _mode = ShuttleConsoleMode.Nav;
|
||||
|
||||
/// <summary>
|
||||
/// Currently selected dock button for camera.
|
||||
/// </summary>
|
||||
private BaseButton? _selectedDock;
|
||||
public event Action<MapCoordinates, Angle>? RequestFTL;
|
||||
public event Action<NetEntity, Angle>? RequestBeaconFTL;
|
||||
|
||||
/// <summary>
|
||||
/// Stored by grid entityid then by states
|
||||
/// </summary>
|
||||
private readonly Dictionary<NetEntity, List<DockingInterfaceState>> _docks = new();
|
||||
|
||||
private readonly Dictionary<BaseButton, NetEntity> _destinations = new();
|
||||
|
||||
/// <summary>
|
||||
/// Next FTL state change.
|
||||
/// </summary>
|
||||
public TimeSpan FTLTime;
|
||||
|
||||
public Action<NetEntity>? UndockPressed;
|
||||
public Action<NetEntity>? StartAutodockPressed;
|
||||
public Action<NetEntity>? StopAutodockPressed;
|
||||
public Action<NetEntity>? DestinationPressed;
|
||||
public event Action<NetEntity, NetEntity>? DockRequest;
|
||||
public event Action<NetEntity>? UndockRequest;
|
||||
|
||||
public ShuttleConsoleWindow()
|
||||
{
|
||||
RobustXamlLoader.Load(this);
|
||||
_entManager = IoCManager.Resolve<IEntityManager>();
|
||||
_timing = IoCManager.Resolve<IGameTiming>();
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
WorldRangeChange(RadarScreen.WorldRange);
|
||||
RadarScreen.WorldRangeChanged += WorldRangeChange;
|
||||
// Mode switching
|
||||
NavModeButton.OnPressed += NavPressed;
|
||||
MapModeButton.OnPressed += MapPressed;
|
||||
DockModeButton.OnPressed += DockPressed;
|
||||
|
||||
IFFToggle.OnToggled += OnIFFTogglePressed;
|
||||
IFFToggle.Pressed = RadarScreen.ShowIFF;
|
||||
// Modes are exclusive
|
||||
var group = new ButtonGroup();
|
||||
|
||||
DockToggle.OnToggled += OnDockTogglePressed;
|
||||
DockToggle.Pressed = RadarScreen.ShowDocks;
|
||||
NavModeButton.Group = group;
|
||||
MapModeButton.Group = group;
|
||||
DockModeButton.Group = group;
|
||||
|
||||
UndockButton.OnPressed += OnUndockPressed;
|
||||
}
|
||||
NavModeButton.Pressed = true;
|
||||
SetupMode(_mode);
|
||||
|
||||
private void WorldRangeChange(float value)
|
||||
{
|
||||
RadarRange.Text = $"{value:0}";
|
||||
}
|
||||
|
||||
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 SetMatrix(EntityCoordinates? coordinates, Angle? angle)
|
||||
{
|
||||
_shuttleEntity = coordinates?.EntityId;
|
||||
RadarScreen.SetMatrix(coordinates, angle);
|
||||
}
|
||||
|
||||
public void UpdateState(ShuttleConsoleBoundInterfaceState scc)
|
||||
{
|
||||
UpdateDocks(scc.Docks);
|
||||
UpdateFTL(scc.Destinations, scc.FTLState, scc.FTLTime);
|
||||
RadarScreen.UpdateState(scc);
|
||||
MaxRadarRange.Text = $"{scc.MaxRange:0}";
|
||||
}
|
||||
|
||||
private void UpdateFTL(List<(NetEntity Entity, string Destination, bool Enabled)> destinations, FTLState state, TimeSpan time)
|
||||
{
|
||||
HyperspaceDestinations.DisposeAllChildren();
|
||||
_destinations.Clear();
|
||||
|
||||
if (destinations.Count == 0)
|
||||
MapContainer.RequestFTL += (coords, angle) =>
|
||||
{
|
||||
HyperspaceDestinations.AddChild(new Label()
|
||||
{
|
||||
Text = Loc.GetString("shuttle-console-hyperspace-none"),
|
||||
HorizontalAlignment = HAlignment.Center,
|
||||
});
|
||||
}
|
||||
else
|
||||
RequestFTL?.Invoke(coords, angle);
|
||||
};
|
||||
|
||||
MapContainer.RequestBeaconFTL += (ent, angle) =>
|
||||
{
|
||||
destinations.Sort((x, y) => string.Compare(x.Destination, y.Destination, StringComparison.Ordinal));
|
||||
RequestBeaconFTL?.Invoke(ent, angle);
|
||||
};
|
||||
|
||||
foreach (var destination in destinations)
|
||||
{
|
||||
var button = new Button()
|
||||
{
|
||||
Disabled = !destination.Enabled,
|
||||
Text = destination.Destination,
|
||||
};
|
||||
DockContainer.DockRequest += (entity, netEntity) =>
|
||||
{
|
||||
DockRequest?.Invoke(entity, netEntity);
|
||||
};
|
||||
|
||||
_destinations[button] = destination.Entity;
|
||||
button.OnPressed += OnHyperspacePressed;
|
||||
HyperspaceDestinations.AddChild(button);
|
||||
}
|
||||
DockContainer.UndockRequest += entity =>
|
||||
{
|
||||
UndockRequest?.Invoke(entity);
|
||||
};
|
||||
}
|
||||
|
||||
private void ClearModes(ShuttleConsoleMode mode)
|
||||
{
|
||||
if (mode != ShuttleConsoleMode.Nav)
|
||||
{
|
||||
NavContainer.Visible = false;
|
||||
}
|
||||
|
||||
string stateText;
|
||||
|
||||
switch (state)
|
||||
if (mode != ShuttleConsoleMode.Map)
|
||||
{
|
||||
case Shared.Shuttles.Systems.FTLState.Available:
|
||||
stateText = Loc.GetString("shuttle-console-ftl-available");
|
||||
MapContainer.Visible = false;
|
||||
MapContainer.SetMap(MapId.Nullspace, Vector2.Zero);
|
||||
}
|
||||
|
||||
if (mode != ShuttleConsoleMode.Dock)
|
||||
{
|
||||
DockContainer.Visible = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void NavPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
SwitchMode(ShuttleConsoleMode.Nav);
|
||||
}
|
||||
|
||||
private void MapPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
SwitchMode(ShuttleConsoleMode.Map);
|
||||
}
|
||||
|
||||
private void DockPressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
SwitchMode(ShuttleConsoleMode.Dock);
|
||||
}
|
||||
|
||||
private void SetupMode(ShuttleConsoleMode mode)
|
||||
{
|
||||
switch (mode)
|
||||
{
|
||||
case ShuttleConsoleMode.Nav:
|
||||
NavContainer.Visible = true;
|
||||
break;
|
||||
case Shared.Shuttles.Systems.FTLState.Starting:
|
||||
stateText = Loc.GetString("shuttle-console-ftl-starting");
|
||||
case ShuttleConsoleMode.Map:
|
||||
MapContainer.Visible = true;
|
||||
MapContainer.Startup();
|
||||
break;
|
||||
case Shared.Shuttles.Systems.FTLState.Travelling:
|
||||
stateText = Loc.GetString("shuttle-console-ftl-travelling");
|
||||
break;
|
||||
case Shared.Shuttles.Systems.FTLState.Cooldown:
|
||||
stateText = Loc.GetString("shuttle-console-ftl-cooldown");
|
||||
break;
|
||||
case Shared.Shuttles.Systems.FTLState.Arriving:
|
||||
stateText = Loc.GetString("shuttle-console-ftl-arriving");
|
||||
case ShuttleConsoleMode.Dock:
|
||||
DockContainer.Visible = true;
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException(nameof(state), state, null);
|
||||
}
|
||||
|
||||
FTLState.Text = stateText;
|
||||
// Add a buffer due to lag or whatever
|
||||
time += TimeSpan.FromSeconds(0.3);
|
||||
FTLTime = time;
|
||||
FTLTimer.Text = GetFTLText();
|
||||
}
|
||||
|
||||
private string GetFTLText()
|
||||
{
|
||||
return $"{Math.Max(0, (FTLTime - _timing.CurTime).TotalSeconds):0.0}";
|
||||
}
|
||||
|
||||
private void OnHyperspacePressed(BaseButton.ButtonEventArgs obj)
|
||||
{
|
||||
var ent = _destinations[obj.Button];
|
||||
DestinationPressed?.Invoke(ent);
|
||||
}
|
||||
|
||||
#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.NetEntity);
|
||||
grid.Add(dock);
|
||||
}
|
||||
|
||||
DockPorts.DisposeAllChildren();
|
||||
DockingScreen.Docks = _docks;
|
||||
var shuttleNetEntity = _entManager.GetNetEntity(_shuttleEntity);
|
||||
|
||||
if (shuttleNetEntity != null && _docks.TryGetValue(shuttleNetEntity.Value, out var gridDocks))
|
||||
{
|
||||
var index = 1;
|
||||
|
||||
foreach (var state in gridDocks)
|
||||
{
|
||||
var pressed = state.Entity == 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,
|
||||
Margin = new Thickness(0f, 1f),
|
||||
};
|
||||
|
||||
if (pressed)
|
||||
{
|
||||
_selectedDock = button;
|
||||
}
|
||||
|
||||
button.OnMouseEntered += args => OnDockMouseEntered(args, state);
|
||||
button.OnMouseExited += args => OnDockMouseExited(args, state);
|
||||
button.OnToggled += args => OnDockToggled(args, state);
|
||||
DockPorts.AddChild(button);
|
||||
index++;
|
||||
}
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnDockMouseEntered(GUIMouseHoverEventArgs obj, DockingInterfaceState state)
|
||||
public void SwitchMode(ShuttleConsoleMode mode)
|
||||
{
|
||||
RadarScreen.HighlightedDock = state.Entity;
|
||||
}
|
||||
|
||||
private void OnDockMouseExited(GUIMouseHoverEventArgs obj, DockingInterfaceState state)
|
||||
{
|
||||
RadarScreen.HighlightedDock = null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Shows a docking camera instead of radar screen.
|
||||
/// </summary>
|
||||
private void OnDockToggled(BaseButton.ButtonEventArgs obj, DockingInterfaceState state)
|
||||
{
|
||||
if (_selectedDock != null)
|
||||
{
|
||||
// If it got untoggled via other means then we'll stop viewing the old dock.
|
||||
if (DockingScreen.ViewedDock != null && DockingScreen.ViewedDock != state.Entity)
|
||||
{
|
||||
StopAutodockPressed?.Invoke(DockingScreen.ViewedDock.Value);
|
||||
}
|
||||
|
||||
_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
|
||||
{
|
||||
if (_shuttleEntity != null)
|
||||
{
|
||||
DockingScreen.Coordinates = _entManager.GetCoordinates(state.Coordinates);
|
||||
DockingScreen.Angle = state.Angle;
|
||||
}
|
||||
else
|
||||
{
|
||||
DockingScreen.Coordinates = null;
|
||||
DockingScreen.Angle = null;
|
||||
}
|
||||
|
||||
UndockButton.Disabled = false;
|
||||
RadarScreen.Visible = false;
|
||||
DockingScreen.Visible = true;
|
||||
DockingScreen.ViewedDock = state.Entity;
|
||||
StartAutodockPressed?.Invoke(state.Entity);
|
||||
DockingScreen.GridEntity = _shuttleEntity;
|
||||
_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<PhysicsComponent>(_shuttleEntity, out var gridBody) ||
|
||||
!_entManager.TryGetComponent<TransformComponent>(_shuttleEntity, out var gridXform))
|
||||
{
|
||||
if (_mode == mode)
|
||||
return;
|
||||
}
|
||||
|
||||
if (_entManager.TryGetComponent<MetaDataComponent>(_shuttleEntity, out var metadata) && metadata.EntityPaused)
|
||||
{
|
||||
FTLTime += _timing.FrameTime;
|
||||
}
|
||||
_mode = mode;
|
||||
ClearModes(mode);
|
||||
SetupMode(_mode);
|
||||
}
|
||||
|
||||
FTLTimer.Text = GetFTLText();
|
||||
public enum ShuttleConsoleMode : byte
|
||||
{
|
||||
Nav,
|
||||
Map,
|
||||
Dock,
|
||||
}
|
||||
|
||||
var (_, worldRot, worldMatrix) = gridXform.GetWorldPositionRotationMatrix();
|
||||
var worldPos = worldMatrix.Transform(gridBody.LocalCenter);
|
||||
public void UpdateState(EntityUid owner, ShuttleBoundUserInterfaceState cState)
|
||||
{
|
||||
var coordinates = _entManager.GetCoordinates(cState.NavState.Coordinates);
|
||||
NavContainer.SetShuttle(coordinates?.EntityId);
|
||||
MapContainer.SetShuttle(coordinates?.EntityId);
|
||||
MapContainer.SetConsole(owner);
|
||||
|
||||
// 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}";
|
||||
NavContainer.UpdateState(cState.NavState);
|
||||
MapContainer.UpdateState(cState.MapState);
|
||||
DockContainer.UpdateState(coordinates?.EntityId, cState.DockState);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user