using System.Linq; using System.Numerics; using System.Text; using Content.Shared.Shuttles.BUIStates; using Content.Shared.Shuttles.Components; using Content.Shared.Shuttles.Systems; using Robust.Client.AutoGenerated; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.XAML; using Robust.Shared.Log; using Robust.Shared.Utility; namespace Content.Client.Shuttles.UI; [GenerateTypedNameReferences] public sealed partial class DockingScreen : BoxContainer { [Dependency] private readonly IEntityManager _entManager = default!; private readonly SharedShuttleSystem _shuttles; /// /// Stored by GridID then by docks /// public Dictionary> Docks = new(); /// /// Store the dock buttons for the side buttons. /// private readonly Dictionary _ourDockButtons = new(); public event Action? DockRequest; public event Action? UndockRequest; public event Action>? UndockAllRequest; public DockingScreen() { RobustXamlLoader.Load(this); IoCManager.InjectDependencies(this); _shuttles = _entManager.System(); DockingControl.OnViewDock += OnView; DockingControl.DockRequest += (entity, netEntity) => { DockRequest?.Invoke(entity, netEntity); }; DockingControl.UndockRequest += entity => { UndockRequest?.Invoke(entity); }; UndockAllButton.OnPressed += _ => OnUndockAllPressed(); } private void OnUndockAllPressed() { if (UndockAllRequest == null) return; // Find all docks that belong to the current shuttle and are docked var netEntity = _entManager.GetNetEntity(DockingControl.GridEntity!.Value); if (!Docks.TryGetValue(netEntity, out var shuttleDocks)) return; var dockedPorts = new List(); foreach (var dock in shuttleDocks) { if (dock.Connected) { dockedPorts.Add(dock.Entity); } } if (dockedPorts.Count > 0) { UndockAllRequest.Invoke(dockedPorts); } } private void OnView(NetEntity obj) { if (_ourDockButtons.TryGetValue(obj, out var viewed)) { viewed.Pressed = true; } } public void UpdateState(EntityUid? shuttle, DockingInterfaceState state) { Docks = state.Docks; DockingControl.DockState = state; DockingControl.GridEntity = shuttle; BuildDocks(shuttle); // Enable the undock all button only if there are docked ports var hasDockedPorts = false; if (shuttle != null) { var netEntity = _entManager.GetNetEntity(shuttle.Value); if (Docks.TryGetValue(netEntity, out var shuttleDocks)) { hasDockedPorts = shuttleDocks.Any(d => d.Connected); } } UndockAllButton.Disabled = !hasDockedPorts; // Update lock status indicators on all dock buttons UpdateDockLockIndicators(shuttle); } private void UpdateDockLockIndicators(EntityUid? shuttle) { if (shuttle == null) return; var netEntity = _entManager.GetNetEntity(shuttle.Value); if (!Docks.TryGetValue(netEntity, out var shuttleDocks)) return; foreach (var dock in shuttleDocks) { if (!_ourDockButtons.TryGetValue(dock.Entity, out var button)) continue; // Find the lock indicator label in the button's children if (button.ChildCount == 0) continue; var buttonContainer = button.GetChild(0) as BoxContainer; if (buttonContainer == null) continue; // Only update if connected to another grid if (!dock.Connected || dock.GridDockedWith == null) { // If there's a lock indicator but no connection anymore, remove it if (buttonContainer.ChildCount > 1) { var existingIndicator = buttonContainer.GetChild(1); if (existingIndicator != null) { buttonContainer.RemoveChild(existingIndicator); } } continue; } var dockedEntity = _entManager.GetEntity(dock.GridDockedWith.Value); // Get or create lock indicator Label? lockIndicator = null; if (buttonContainer.ChildCount > 1) { lockIndicator = buttonContainer.GetChild(1) as Label; } if (lockIndicator == null) { // Create new lock indicator if it doesn't exist lockIndicator = new Label { HorizontalAlignment = Control.HAlignment.Right, VerticalAlignment = Control.VAlignment.Center, Margin = new Thickness(2f, 0f), MinWidth = 70 }; buttonContainer.AddChild(lockIndicator); } } } private void BuildDocks(EntityUid? shuttle) { DockingControl.BuildDocks(shuttle); var currentDock = DockingControl.ViewedDock; // DockedWith.DisposeAllChildren(); DockPorts.DisposeAllChildren(); _ourDockButtons.Clear(); if (shuttle == null) { DockingControl.SetViewedDock(null); return; } var shuttleNent = _entManager.GetNetEntity(shuttle.Value); if (!Docks.TryGetValue(shuttleNent, out var shuttleDocks) || shuttleDocks.Count <= 0) return; var dockText = new StringBuilder(); var buttonGroup = new ButtonGroup(); var idx = 0; var selected = false; DockingPortState? firstState = null; // Frontier // Build the dock buttons for our docks. foreach (var dock in shuttleDocks.OrderBy(x => x.LabelName ?? x.Name)) // Frontier: order by name { if (idx == 0) // Frontier: get first element firstState = dock; // Frontier: get first element idx++; dockText.Clear(); dockText.Append(dock.LabelName ?? dock.Name); // Create a BoxContainer to hold button text and lock indicator var buttonContainer = new BoxContainer { Orientation = BoxContainer.LayoutOrientation.Horizontal, HorizontalExpand = true, VerticalAlignment = Control.VAlignment.Center, Margin = new Thickness(3f), }; // The text label var textLabel = new Label { Text = dockText.ToString(), HorizontalExpand = true, Margin = new Thickness(4f, 0f), }; buttonContainer.AddChild(textLabel); var button = new Button() { Text = dockText.ToString(), ToggleMode = true, Group = buttonGroup, Margin = new Thickness(0f, 3f), HorizontalExpand = true }; // Add the container with text and lock indicator to the button button.AddChild(buttonContainer); button.OnMouseEntered += args => { DockingControl.HighlightedDock = dock.Entity; }; button.OnMouseExited += args => { DockingControl.HighlightedDock = null; }; if (currentDock == dock.Entity) { selected = true; button.Pressed = true; } button.OnPressed += args => { OnDockPress(dock); }; _ourDockButtons[dock.Entity] = button; DockPorts.AddChild(button); } // Button group needs one selected so just show the first one. if (!selected) { // Frontier: press first button in alphabetical order // var buttonOne = shuttleDocks[0]; // OnDockPress(buttonOne); OnDockPress(firstState!); // End Frontier } var shuttleContainers = new Dictionary(); foreach (var dock in shuttleDocks.OrderBy(x => x.GridDockedWith)) { if (dock.GridDockedWith == null) continue; DockObject? dockContainer; if (!shuttleContainers.TryGetValue(dock.GridDockedWith.Value, out dockContainer)) { dockContainer = new DockObject(); shuttleContainers[dock.GridDockedWith.Value] = dockContainer; var dockGrid = _entManager.GetEntity(dock.GridDockedWith); string? iffLabel = null; if (_entManager.EntityExists(dockGrid)) { iffLabel = _shuttles.GetIFFLabel(dockGrid.Value); } iffLabel ??= Loc.GetString("shuttle-console-unknown"); dockContainer.SetName(iffLabel); // DockedWith.AddChild(dockContainer); } dockContainer.AddDock(dock, DockingControl); } } private void OnDockPress(DockingPortState state) { DockingControl.SetViewedDock(state); } }