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:
metalgearsloth
2024-03-03 18:39:19 +11:00
committed by GitHub
parent 2ef38f8a62
commit c5486873db
99 changed files with 4896 additions and 2371 deletions

View File

@@ -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);
}
}