using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.List;
-namespace Content.Client.Guidebook;
+namespace Content.Client.Guidebook.Components;
/// <summary>
/// This component stores a reference to a guidebook that contains information relevant to this entity.
/// </summary>
[RegisterComponent]
+[Access(typeof(GuidebookSystem))]
public sealed class GuideHelpComponent : Component
{
/// <summary>
- /// What guides to include show when opening the guidebook. The first entry will be used to select the currently
- /// selected guidebook.
+ /// What guides to include show when opening the guidebook. The first entry will be used to select the currently
+ /// selected guidebook.
/// </summary>
[DataField("guides", customTypeSerializer: typeof(PrototypeIdListSerializer<GuideEntryPrototype>), required: true)]
+ [ViewVariables]
public List<string> Guides = new();
/// <summary>
- /// Whether or not to automatically include the children of the given guides.
+ /// Whether or not to automatically include the children of the given guides.
/// </summary>
[DataField("includeChildren")]
+ [ViewVariables(VVAccess.ReadWrite)]
public bool IncludeChildren = true;
+
+ /// <summary>
+ /// Whether or not to open the UI when interacting with the entity while on hand.
+ /// Mostly intended for books
+ /// </summary>
+ [DataField("openOnActivation")]
+ [ViewVariables(VVAccess.ReadWrite)]
+ public bool OpenOnActivation;
}
-namespace Content.Client.Guidebook;
+namespace Content.Client.Guidebook.Components;
/// <summary>
/// This is used for the guidebook monkey.
using System.Linq;
-using Content.Client.Guidebook.Controls;
+using Content.Client.Guidebook.Components;
using Content.Client.Light;
using Content.Client.Verbs;
-using Content.Shared.Input;
using Content.Shared.Interaction;
using Content.Shared.Light.Component;
using Content.Shared.Speech;
using Content.Shared.Verbs;
using Robust.Client.GameObjects;
using Robust.Client.Player;
-using Robust.Shared.Input;
-using Robust.Shared.Input.Binding;
using Robust.Shared.Player;
-using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.Guidebook;
public sealed class GuidebookSystem : EntitySystem
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
- [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly SharedAudioSystem _audioSystem = default!;
[Dependency] private readonly VerbSystem _verbSystem = default!;
[Dependency] private readonly RgbLightControllerSystem _rgbLightControllerSystem = default!;
+ [Dependency] private readonly SharedPointLightSystem _pointLightSystem = default!;
[Dependency] private readonly TagSystem _tags = default!;
- private GuidebookWindow _guideWindow = default!;
+ public event Action<List<string>, List<string>?, string?, bool, string?>? OnGuidebookOpen;
public const string GuideEmbedTag = "GuideEmbeded";
/// <inheritdoc/>
public override void Initialize()
{
- CommandBinds.Builder
- .Bind(ContentKeyFunctions.OpenGuidebook,
- new PointerInputCmdHandler(HandleOpenGuidebook))
- .Register<GuidebookSystem>();
- _guideWindow = new GuidebookWindow();
-
SubscribeLocalEvent<GuideHelpComponent, GetVerbsEvent<ExamineVerb>>(OnGetVerbs);
+ SubscribeLocalEvent<GuideHelpComponent, ActivateInWorldEvent>(OnInteract);
+
SubscribeLocalEvent<GuidebookControlsTestComponent, InteractHandEvent>(OnGuidebookControlsTestInteractHand);
SubscribeLocalEvent<GuidebookControlsTestComponent, ActivateInWorldEvent>(OnGuidebookControlsTestActivateInWorld);
SubscribeLocalEvent<GuidebookControlsTestComponent, GetVerbsEvent<AlternativeVerb>>(
{
Text = Loc.GetString("guide-help-verb"),
Icon = new SpriteSpecifier.Texture(new ResourcePath("/Textures/Interface/VerbIcons/information.svg.192dpi.png")),
- Act = () => OpenGuidebook(component.Guides, includeChildren: component.IncludeChildren, selected: component.Guides[0]),
+ Act = () => OnGuidebookOpen?.Invoke(component.Guides, null, null, component.IncludeChildren, component.Guides[0]),
ClientExclusive = true,
CloseMenu = true
});
}
+ private void OnInteract(EntityUid uid, GuideHelpComponent component, ActivateInWorldEvent args)
+ {
+ if (!component.OpenOnActivation || component.Guides.Count == 0 || _tags.HasTag(uid, GuideEmbedTag))
+ return;
+
+ OnGuidebookOpen?.Invoke(component.Guides, null, null, component.IncludeChildren, component.Guides[0]);
+ args.Handled = true;
+ }
+
private void OnGuidebookControlsTestGetAlternateVerbs(EntityUid uid, GuidebookControlsTestComponent component, GetVerbsEvent<AlternativeVerb> args)
{
args.Verbs.Add(new AlternativeVerb()
{
Act = () =>
{
- var light = EnsureComp<PointLightComponent>(uid); // RGB demands this.
- light.Enabled = false;
+ EnsureComp<PointLightComponent>(uid); // RGB demands this.
+ _pointLightSystem.SetEnabled(uid, false);
var rgb = EnsureComp<RgbLightControllerComponent>(uid);
var sprite = EnsureComp<SpriteComponent>(uid);
var activateMsg = new InteractHandEvent(user, activated);
RaiseLocalEvent(activated, activateMsg, true);
}
-
- private bool HandleOpenGuidebook(in PointerInputCmdHandler.PointerInputCmdArgs args)
- {
- if (args.State != BoundKeyState.Down)
- return false;
-
- OpenGuidebook();
- return true;
- }
-
- /// <summary>
- /// Opens the guidebook.
- /// </summary>
- /// <param name="guides">What guides should be shown. If not specified, this will instead raise a <see
- /// cref="GetGuidesEvent"/> and automatically include all guide prototypes.</param>
- /// <param name="rootEntries">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.</param>
- /// <param name="forceRoot">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.</param>
- /// <param name="includeChildren">Whether or not to automatically include child entries. If false, this will ONLY
- /// show the specified entries</param>
- /// <param name="selected">The guide whose contents should be displayed when the guidebook is opened</param>
- public bool OpenGuidebook(
- Dictionary<string, GuideEntry>? guides = null,
- List<string>? rootEntries = null,
- string? forceRoot = null,
- bool includeChildren = true,
- string? selected = null)
- {
- _guideWindow.OpenCenteredRight();
-
- if (guides == null)
- {
- var ev = new GetGuidesEvent()
- {
- Guides = _prototypeManager.EnumeratePrototypes<GuideEntryPrototype>().ToDictionary(x => x.ID, x => (GuideEntry) x)
- };
- RaiseLocalEvent(ev);
- guides = ev.Guides;
- }
- else if (includeChildren)
- {
- var oldGuides = guides;
- guides = new(oldGuides);
- foreach (var guide in oldGuides.Values)
- {
- RecursivelyAddChildren(guide, guides);
- }
- }
-
- _guideWindow.UpdateGuides(guides, rootEntries, forceRoot, selected);
-
- return true;
- }
-
- public bool OpenGuidebook(
- List<string> guideList,
- List<string>? rootEntries = null,
- string? forceRoot = null,
- bool includeChildren = true,
- string? selected = null)
- {
- Dictionary<string, GuideEntry> guides = new();
- foreach (var guideId in guideList)
- {
- if (!_prototypeManager.TryIndex<GuideEntryPrototype>(guideId, out var guide))
- {
- Logger.Error($"Encountered unknown guide prototype: {guideId}");
- continue;
- }
- guides.Add(guideId, guide);
- }
-
- return OpenGuidebook(guides, rootEntries, forceRoot, includeChildren, selected);
- }
-
- private void RecursivelyAddChildren(GuideEntry guide, Dictionary<string, GuideEntry> guides)
- {
- foreach (var childId in guide.Children)
- {
- if (guides.ContainsKey(childId))
- continue;
-
- if (!_prototypeManager.TryIndex<GuideEntryPrototype>(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);
- }
- }
-}
-
-public sealed class GetGuidesEvent : EntityEventArgs
-{
- public Dictionary<string, GuideEntry> Guides { get; init; } = new();
}
using Content.Client.Changelog;
using Content.Client.UserInterface.Systems.EscapeMenu;
+using Content.Client.UserInterface.Systems.Guidebook;
using Content.Shared.CCVar;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
AddInfoButton("server-info-wiki-button", CCVars.InfoLinksWiki);
AddInfoButton("server-info-forum-button", CCVars.InfoLinksForum);
+ var guidebookController = UserInterfaceManager.GetUIController<GuidebookUIController>();
+ var guidebookButton = new Button() { Text = Loc.GetString("server-info-guidebook-button") };
+ guidebookButton.OnPressed += _ =>
+ {
+ guidebookController.ToggleGuidebook();
+ };
+ buttons.AddChild(guidebookButton);
+
var changelogButton = new ChangelogButton();
changelogButton.OnPressed += args => UserInterfaceManager.GetUIController<ChangelogUIController>().ToggleWindow();
buttons.AddChild(changelogButton);
using Content.Client.Gameplay;
-using Content.Client.Guidebook;
-using Content.Client.Info;
using Content.Client.UserInterface.Controls;
+using Content.Client.UserInterface.Systems.Guidebook;
using Content.Client.UserInterface.Systems.Info;
using Content.Shared.CCVar;
using JetBrains.Annotations;
using Robust.Client.Console;
-using Robust.Client.Input;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers;
using Robust.Shared.Configuration;
[Dependency] private readonly ChangelogUIController _changelog = default!;
[Dependency] private readonly InfoUIController _info = default!;
[Dependency] private readonly OptionsUIController _options = default!;
- [UISystemDependency] private readonly GuidebookSystem? _guidebook = default!;
+ [Dependency] private readonly GuidebookUIController _guidebook = default!;
private Options.UI.EscapeMenu? _escapeWindow;
_escapeWindow.GuidebookButton.OnPressed += _ =>
{
- _guidebook?.OpenGuidebook();
+ _guidebook.ToggleGuidebook();
};
// Hide wiki button if we don't have a link for it.
--- /dev/null
+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<LobbyState>, IOnStateEntered<GameplayState>, IOnStateExited<LobbyState>, IOnStateExited<GameplayState>, IOnSystemChanged<GuidebookSystem>
+{
+ [UISystemDependency] private readonly GuidebookSystem _guidebookSystem = default!;
+ [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
+
+ private GuidebookWindow? _guideWindow;
+ private MenuButton? GuidebookButton => UIManager.GetActiveUIWidgetOrNull<MenuBar.Widgets.GameTopMenuBar>()?.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<GuidebookWindow>();
+ _guideWindow.OnClose += OnWindowClosed;
+ _guideWindow.OnOpen += OnWindowOpen;
+
+ // setup keybinding
+ CommandBinds.Builder
+ .Bind(ContentKeyFunctions.OpenGuidebook,
+ InputCmdHandler.FromDelegate(_ => ToggleGuidebook()))
+ .Register<GuidebookUIController>();
+ }
+
+ 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<GuidebookUIController>();
+ }
+
+ 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;
+ }
+
+ /// <summary>
+ /// Opens the guidebook.
+ /// </summary>
+ /// <param name="guides">What guides should be shown. If not specified, this will instead list all the entries</param>
+ /// <param name="rootEntries">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.</param>
+ /// <param name="forceRoot">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.</param>
+ /// <param name="includeChildren">Whether or not to automatically include child entries. If false, this will ONLY
+ /// show the specified entries</param>
+ /// <param name="selected">The guide whose contents should be displayed when the guidebook is opened</param>
+ public void ToggleGuidebook(
+ Dictionary<string, GuideEntry>? guides = null,
+ List<string>? 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<GuideEntryPrototype>()
+ .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);
+ _guideWindow.OpenCenteredRight();
+ }
+
+ public void ToggleGuidebook(
+ List<string> guideList,
+ List<string>? rootEntries = null,
+ string? forceRoot = null,
+ bool includeChildren = true,
+ string? selected = null)
+ {
+ Dictionary<string, GuideEntry> guides = new();
+ foreach (var guideId in guideList)
+ {
+ if (!_prototypeManager.TryIndex<GuideEntryPrototype>(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<string, GuideEntry> guides)
+ {
+ foreach (var childId in guide.Children)
+ {
+ if (guides.ContainsKey(childId))
+ continue;
+
+ if (!_prototypeManager.TryIndex<GuideEntryPrototype>(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);
+ }
+ }
+}
-using Content.Client.Gameplay;
using Content.Client.UserInterface.Systems.Actions;
using Content.Client.UserInterface.Systems.Admin;
using Content.Client.UserInterface.Systems.Bwoink;
using Content.Client.UserInterface.Systems.Crafting;
using Content.Client.UserInterface.Systems.EscapeMenu;
using Content.Client.UserInterface.Systems.Gameplay;
+using Content.Client.UserInterface.Systems.Guidebook;
using Content.Client.UserInterface.Systems.Inventory;
using Content.Client.UserInterface.Systems.MenuBar.Widgets;
using Content.Client.UserInterface.Systems.Sandbox;
[Dependency] private readonly AHelpUIController _ahelp = default!;
[Dependency] private readonly ActionUIController _action = default!;
[Dependency] private readonly SandboxUIController _sandbox = default!;
+ [Dependency] private readonly GuidebookUIController _guidebook = default!;
private GameTopMenuBar? GameTopMenuBar => UIManager.GetActiveUIWidgetOrNull<GameTopMenuBar>();
public void UnloadButtons()
{
_escape.UnloadButton();
+ _guidebook.UnloadButton();
_inventory.UnloadButton();
_admin.UnloadButton();
_character.UnloadButton();
public void LoadButtons()
{
_escape.LoadButton();
+ _guidebook.LoadButton();
_inventory.LoadButton();
_admin.LoadButton();
_character.LoadButton();
HorizontalExpand="True"
AppendStyleClass="{x:Static style:StyleBase.ButtonOpenRight}"
/>
+ <ui:MenuButton
+ Name="GuidebookButton"
+ Access="Internal"
+ Icon="{xe:Tex '/Textures/Interface/VerbIcons/information.svg.192dpi.png'}"
+ ToolTip="{Loc 'game-hud-open-guide-menu-button-tooltip'}"
+ BoundKey = "{x:Static is:ContentKeyFunctions.OpenGuidebook}"
+ MinSize="42 64"
+ HorizontalExpand="True"
+ AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}"
+ />
<ui:MenuButton
Name="CharacterButton"
Access="Internal"
game-hud-open-escape-menu-button-tooltip = Open escape menu.
+game-hud-open-guide-menu-button-tooltip = Open guidebook menu.
game-hud-open-character-menu-button-tooltip = Open character menu.
game-hud-open-inventory-menu-button-tooltip = Open inventory menu.
game-hud-open-crafting-menu-button-tooltip = Open crafting menu.
game-hud-open-actions-menu-button-tooltip = Open actions menu.
game-hud-open-admin-menu-button-tooltip = Open admin menu.
-game-hud-open-sandbox-menu-button-tooltip = Open sandbox menu.
\ No newline at end of file
+game-hud-open-sandbox-menu-button-tooltip = Open sandbox menu.
server-info-rules-button = Rules
+server-info-guidebook-button = Guidebook
server-info-discord-button = Discord
server-info-website-button = Website
server-info-wiki-button = Wiki