]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Moves guidebook UI logic to a UI Controller, some tweaks (#14601)
authorAJCM-git <60196617+AJCM-git@users.noreply.github.com>
Thu, 23 Mar 2023 03:41:43 +0000 (23:41 -0400)
committerGitHub <noreply@github.com>
Thu, 23 Mar 2023 03:41:43 +0000 (23:41 -0400)
Content.Client/Guidebook/Components/GuideHelpComponent.cs
Content.Client/Guidebook/Components/GuidebookControlsTestComponent.cs
Content.Client/Guidebook/GuidebookSystem.cs
Content.Client/Info/LinkBanner.cs
Content.Client/UserInterface/Systems/EscapeMenu/EscapeUIController.cs
Content.Client/UserInterface/Systems/Guidebook/GuidebookUIController.cs [new file with mode: 0644]
Content.Client/UserInterface/Systems/MenuBar/GameTopMenuBarUIController.cs
Content.Client/UserInterface/Systems/MenuBar/Widgets/GameTopMenuBar.xaml
Resources/Locale/en-US/HUD/game-hud.ftl
Resources/Locale/en-US/info/server-info.ftl

index a0124c5a7be84c9f9ccd61ace3add770285c0e46..f333c873d6d4ebd565c77ae73c96fc30222168ea 100644 (file)
@@ -1,23 +1,34 @@
 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;
 }
index a2935011cdbd62290cdb213f2ed766dc07dd26b1..21c6c0365e94eb3a2bf36120885b243cccb83d80 100644 (file)
@@ -1,4 +1,4 @@
-namespace Content.Client.Guidebook;
+namespace Content.Client.Guidebook.Components;
 
 /// <summary>
 /// This is used for the guidebook monkey.
index 74991547cb0b1bd847703d0773ad1252303231f6..e6272f440c3d346f018efb87178acbf43a0c6a44 100644 (file)
@@ -1,8 +1,7 @@
 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;
@@ -10,10 +9,7 @@ using Content.Shared.Tag;
 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;
@@ -24,25 +20,21 @@ 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>>(
@@ -58,12 +50,21 @@ public sealed class GuidebookSystem : EntitySystem
         {
             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()
@@ -81,8 +82,8 @@ public sealed class GuidebookSystem : EntitySystem
         {
             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);
@@ -143,103 +144,4 @@ public sealed class GuidebookSystem : EntitySystem
         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();
 }
index 4d8059df89271db4fef5204b9116e36741b6fcd3..a30aa413761a8a8376eb5f6c27c00e6b2951fb91 100644 (file)
@@ -1,5 +1,6 @@
 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;
@@ -34,6 +35,14 @@ namespace Content.Client.Info
             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);
index 1e96cdc0452c1b04d3eea45c6be74b639d6329f8..56792c2992f1eb1012357cc11054a0eec929014c 100644 (file)
@@ -1,12 +1,10 @@
 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;
@@ -26,7 +24,7 @@ public sealed class EscapeUIController : UIController, IOnStateEntered<GameplayS
     [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;
 
@@ -102,7 +100,7 @@ public sealed class EscapeUIController : UIController, IOnStateEntered<GameplayS
 
         _escapeWindow.GuidebookButton.OnPressed += _ =>
         {
-            _guidebook?.OpenGuidebook();
+            _guidebook.ToggleGuidebook();
         };
 
         // Hide wiki button if we don't have a link for it.
diff --git a/Content.Client/UserInterface/Systems/Guidebook/GuidebookUIController.cs b/Content.Client/UserInterface/Systems/Guidebook/GuidebookUIController.cs
new file mode 100644 (file)
index 0000000..66376df
--- /dev/null
@@ -0,0 +1,206 @@
+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);
+        }
+    }
+}
index 9779dbc582fb974ad2638950fbb28d3bee7b4ba3..b399e83fc61db88e13a5e0603ae1d51cc9a52902 100644 (file)
@@ -1,4 +1,3 @@
-using Content.Client.Gameplay;
 using Content.Client.UserInterface.Systems.Actions;
 using Content.Client.UserInterface.Systems.Admin;
 using Content.Client.UserInterface.Systems.Bwoink;
@@ -6,6 +5,7 @@ using Content.Client.UserInterface.Systems.Character;
 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;
@@ -23,6 +23,7 @@ public sealed class GameTopMenuBarUIController : UIController
     [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>();
 
@@ -38,6 +39,7 @@ public sealed class GameTopMenuBarUIController : UIController
     public void UnloadButtons()
     {
         _escape.UnloadButton();
+        _guidebook.UnloadButton();
         _inventory.UnloadButton();
         _admin.UnloadButton();
         _character.UnloadButton();
@@ -50,6 +52,7 @@ public sealed class GameTopMenuBarUIController : UIController
     public void LoadButtons()
     {
         _escape.LoadButton();
+        _guidebook.LoadButton();
         _inventory.LoadButton();
         _admin.LoadButton();
         _character.LoadButton();
index 9ba11fec4500b3792070c4ac52f70ba090ad63bf..83b8d010d3cbed97ef73dc8c58aad52cce89a1a9 100644 (file)
         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"
index 1e40c951aebe5a38721e010495fe6434dda00eb1..7f6573d2adf9b6c948f5c9cae70a5dfb9ee30453 100644 (file)
@@ -1,7 +1,8 @@
 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.
index 0686b78c3b391109dbeb256f52e99ac6a68a999a..3039f4cb17a76d66974721cd3797e248ed80fb4b 100644 (file)
@@ -1,4 +1,5 @@
 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