using System.Linq; using Content.Client.Gameplay; using Content.Client.Guidebook; using Content.Client.Guidebook.Controls; using Content.Client.Lobby; using Content.Client.UserInterface.Controls; using Content.Shared.Input; using Robust.Client.UserInterface; using Robust.Client.UserInterface.Controllers; using static Robust.Client.UserInterface.Controls.BaseButton; using Robust.Shared.Input.Binding; using Robust.Shared.Prototypes; using Robust.Shared.Utility; namespace Content.Client.UserInterface.Systems.Guidebook; public sealed class GuidebookUIController : UIController, IOnStateEntered, IOnStateEntered, IOnStateExited, IOnStateExited, IOnSystemChanged { [UISystemDependency] private readonly GuidebookSystem _guidebookSystem = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!; private GuidebookWindow? _guideWindow; private MenuButton? GuidebookButton => UIManager.GetActiveUIWidgetOrNull()?.GuidebookButton; public void OnStateEntered(LobbyState state) { HandleStateEntered(); } public void OnStateEntered(GameplayState state) { HandleStateEntered(); } private void HandleStateEntered() { DebugTools.Assert(_guideWindow == null); // setup window _guideWindow = UIManager.CreateWindow(); _guideWindow.OnClose += OnWindowClosed; _guideWindow.OnOpen += OnWindowOpen; // setup keybinding CommandBinds.Builder .Bind(ContentKeyFunctions.OpenGuidebook, InputCmdHandler.FromDelegate(_ => ToggleGuidebook())) .Register(); } public void OnStateExited(LobbyState state) { HandleStateExited(); } public void OnStateExited(GameplayState state) { HandleStateExited(); } private void HandleStateExited() { if (_guideWindow == null) return; _guideWindow.OnClose -= OnWindowClosed; _guideWindow.OnOpen -= OnWindowOpen; // shutdown _guideWindow.Dispose(); _guideWindow = null; CommandBinds.Unregister(); } public void OnSystemLoaded(GuidebookSystem system) { _guidebookSystem.OnGuidebookOpen += ToggleGuidebook; } public void OnSystemUnloaded(GuidebookSystem system) { _guidebookSystem.OnGuidebookOpen -= ToggleGuidebook; } internal void UnloadButton() { if (GuidebookButton == null) return; GuidebookButton.OnPressed -= GuidebookButtonOnPressed; } internal void LoadButton() { if (GuidebookButton == null) return; GuidebookButton.OnPressed += GuidebookButtonOnPressed; } private void GuidebookButtonOnPressed(ButtonEventArgs obj) { ToggleGuidebook(); } private void OnWindowClosed() { if (GuidebookButton != null) GuidebookButton.Pressed = false; } private void OnWindowOpen() { if (GuidebookButton != null) GuidebookButton.Pressed = true; } /// /// Opens or closes the guidebook. /// /// What guides should be shown. If not specified, this will instead list all the entries /// A list of guides that should form the base of the table of contents. If not specified, /// this will automatically simply be a list of all guides that have no parent. /// This forces a singular guide to contain all other guides. This guide will /// contain its own children, in addition to what would normally be the root guides if this were not /// specified. /// Whether or not to automatically include child entries. If false, this will ONLY /// show the specified entries /// The guide whose contents should be displayed when the guidebook is opened public void ToggleGuidebook( Dictionary? guides = null, List? rootEntries = null, string? forceRoot = null, bool includeChildren = true, string? selected = null) { if (_guideWindow == null) return; if (_guideWindow.IsOpen) { _guideWindow.Close(); return; } if (GuidebookButton != null) GuidebookButton.Pressed = !_guideWindow.IsOpen; if (guides == null) { guides = _prototypeManager.EnumeratePrototypes() .ToDictionary(x => x.ID, x => (GuideEntry) x); } else if (includeChildren) { var oldGuides = guides; guides = new(oldGuides); foreach (var guide in oldGuides.Values) { RecursivelyAddChildren(guide, guides); } } _guideWindow.UpdateGuides(guides, rootEntries, forceRoot, selected); // Expand up to depth-2. _guideWindow.Tree.SetAllExpanded(false); _guideWindow.Tree.SetAllExpanded(true, 1); _guideWindow.OpenCenteredRight(); } public void ToggleGuidebook( List guideList, List? rootEntries = null, string? forceRoot = null, bool includeChildren = true, string? selected = null) { Dictionary guides = new(); foreach (var guideId in guideList) { if (!_prototypeManager.TryIndex(guideId, out var guide)) { Logger.Error($"Encountered unknown guide prototype: {guideId}"); continue; } guides.Add(guideId, guide); } ToggleGuidebook(guides, rootEntries, forceRoot, includeChildren, selected); } private void RecursivelyAddChildren(GuideEntry guide, Dictionary guides) { foreach (var childId in guide.Children) { if (guides.ContainsKey(childId)) continue; if (!_prototypeManager.TryIndex(childId, out var child)) { Logger.Error($"Encountered unknown guide prototype: {childId} as a child of {guide.Id}. If the child is not a prototype, it must be directly provided."); continue; } guides.Add(childId, child); RecursivelyAddChildren(child, guides); } } }