2023-03-22 23:41:43 -04:00
using System.Linq ;
using Content.Client.Gameplay ;
using Content.Client.Guidebook ;
using Content.Client.Guidebook.Controls ;
using Content.Client.Lobby ;
2024-06-09 12:26:48 -04:00
using Content.Client.Players.PlayTimeTracking ;
2023-03-22 23:41:43 -04:00
using Content.Client.UserInterface.Controls ;
2024-06-09 12:26:48 -04:00
using Content.Shared.CCVar ;
2024-06-07 00:05:58 +12:00
using Content.Shared.Guidebook ;
2023-03-22 23:41:43 -04:00
using Content.Shared.Input ;
2024-06-09 12:26:48 -04:00
using Robust.Client.State ;
2023-03-22 23:41:43 -04:00
using Robust.Client.UserInterface ;
using Robust.Client.UserInterface.Controllers ;
2024-06-09 12:26:48 -04:00
using Robust.Shared.Configuration ;
2023-03-22 23:41:43 -04:00
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 ! ;
2024-06-09 12:26:48 -04:00
[Dependency] private readonly IConfigurationManager _configuration = default ! ;
[Dependency] private readonly JobRequirementsManager _jobRequirements = default ! ;
private const int PlaytimeOpenGuidebook = 60 ;
2023-03-22 23:41:43 -04:00
private GuidebookWindow ? _guideWindow ;
private MenuButton ? GuidebookButton = > UIManager . GetActiveUIWidgetOrNull < MenuBar . Widgets . GameTopMenuBar > ( ) ? . GuidebookButton ;
2024-08-26 16:06:54 -07:00
private ProtoId < GuideEntryPrototype > ? _lastEntry ;
2023-03-22 23:41:43 -04:00
public void OnStateEntered ( LobbyState state )
{
2024-06-09 12:26:48 -04:00
HandleStateEntered ( state ) ;
2023-03-22 23:41:43 -04:00
}
public void OnStateEntered ( GameplayState state )
{
2024-06-09 12:26:48 -04:00
HandleStateEntered ( state ) ;
2023-03-22 23:41:43 -04:00
}
2024-06-09 12:26:48 -04:00
private void HandleStateEntered ( State state )
2023-03-22 23:41:43 -04:00
{
DebugTools . Assert ( _guideWindow = = null ) ;
// setup window
_guideWindow = UIManager . CreateWindow < GuidebookWindow > ( ) ;
_guideWindow . OnClose + = OnWindowClosed ;
_guideWindow . OnOpen + = OnWindowOpen ;
2024-06-09 12:26:48 -04:00
if ( state is LobbyState & &
_jobRequirements . FetchOverallPlaytime ( ) < TimeSpan . FromMinutes ( PlaytimeOpenGuidebook ) )
{
OpenGuidebook ( ) ;
_guideWindow . RecenterWindow ( new ( 0.5f , 0.5f ) ) ;
_guideWindow . SetPositionFirst ( ) ;
}
2023-03-22 23:41:43 -04:00
// 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 )
{
2024-06-07 00:05:58 +12:00
_guidebookSystem . OnGuidebookOpen + = OpenGuidebook ;
2023-03-22 23:41:43 -04:00
}
public void OnSystemUnloaded ( GuidebookSystem system )
{
2024-06-07 00:05:58 +12:00
_guidebookSystem . OnGuidebookOpen - = OpenGuidebook ;
2023-03-22 23:41:43 -04:00
}
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 ( ) ;
}
2024-06-07 00:05:58 +12:00
public void ToggleGuidebook ( )
{
if ( _guideWindow = = null )
return ;
if ( _guideWindow . IsOpen )
{
UIManager . ClickSound ( ) ;
_guideWindow . Close ( ) ;
}
else
{
OpenGuidebook ( ) ;
}
}
2023-03-22 23:41:43 -04:00
private void OnWindowClosed ( )
{
if ( GuidebookButton ! = null )
GuidebookButton . Pressed = false ;
2024-06-07 07:28:55 -04:00
if ( _guideWindow ! = null )
2024-08-26 16:06:54 -07:00
{
2024-06-07 07:28:55 -04:00
_guideWindow . ReturnContainer . Visible = false ;
2024-08-26 16:06:54 -07:00
_lastEntry = _guideWindow . LastEntry ;
}
2023-03-22 23:41:43 -04:00
}
private void OnWindowOpen ( )
{
if ( GuidebookButton ! = null )
GuidebookButton . Pressed = true ;
}
/// <summary>
2023-05-07 15:12:29 +12:00
/// Opens or closes the guidebook.
2023-03-22 23:41:43 -04:00
/// </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>
2024-06-07 00:05:58 +12:00
public void OpenGuidebook (
Dictionary < ProtoId < GuideEntryPrototype > , GuideEntry > ? guides = null ,
List < ProtoId < GuideEntryPrototype > > ? rootEntries = null ,
ProtoId < GuideEntryPrototype > ? forceRoot = null ,
2023-03-22 23:41:43 -04:00
bool includeChildren = true ,
2024-06-07 00:05:58 +12:00
ProtoId < GuideEntryPrototype > ? selected = null )
2023-03-22 23:41:43 -04:00
{
if ( _guideWindow = = null )
2023-09-26 07:49:27 -07:00
return ;
2023-03-22 23:41:43 -04:00
if ( GuidebookButton ! = null )
2023-12-29 15:43:36 +11:00
GuidebookButton . SetClickPressed ( ! _guideWindow . IsOpen ) ;
2023-03-22 23:41:43 -04:00
if ( guides = = null )
{
guides = _prototypeManager . EnumeratePrototypes < GuideEntryPrototype > ( )
2024-06-07 00:05:58 +12:00
. ToDictionary ( x = > new ProtoId < GuideEntryPrototype > ( x . ID ) , x = > ( GuideEntry ) x ) ;
2023-03-22 23:41:43 -04:00
}
else if ( includeChildren )
{
var oldGuides = guides ;
guides = new ( oldGuides ) ;
foreach ( var guide in oldGuides . Values )
{
RecursivelyAddChildren ( guide , guides ) ;
}
}
2024-08-26 16:06:54 -07:00
if ( selected = = null )
{
if ( _lastEntry is { } lastEntry & & guides . ContainsKey ( lastEntry ) )
{
selected = _lastEntry ;
}
else
{
selected = _configuration . GetCVar ( CCVars . DefaultGuide ) ;
}
}
2023-09-26 07:49:27 -07:00
_guideWindow . UpdateGuides ( guides , rootEntries , forceRoot , selected ) ;
2023-05-07 15:12:29 +12:00
// Expand up to depth-2.
2023-09-26 07:49:27 -07:00
_guideWindow . Tree . SetAllExpanded ( false ) ;
_guideWindow . Tree . SetAllExpanded ( true , 1 ) ;
2023-05-07 15:12:29 +12:00
2023-03-22 23:41:43 -04:00
_guideWindow . OpenCenteredRight ( ) ;
}
2024-06-07 00:05:58 +12:00
public void OpenGuidebook (
List < ProtoId < GuideEntryPrototype > > guideList ,
List < ProtoId < GuideEntryPrototype > > ? rootEntries = null ,
ProtoId < GuideEntryPrototype > ? forceRoot = null ,
2023-03-22 23:41:43 -04:00
bool includeChildren = true ,
2024-06-07 00:05:58 +12:00
ProtoId < GuideEntryPrototype > ? selected = null )
2023-03-22 23:41:43 -04:00
{
2024-06-07 00:05:58 +12:00
Dictionary < ProtoId < GuideEntryPrototype > , GuideEntry > guides = new ( ) ;
2023-03-22 23:41:43 -04:00
foreach ( var guideId in guideList )
{
2024-06-07 00:05:58 +12:00
if ( ! _prototypeManager . TryIndex ( guideId , out var guide ) )
2023-03-22 23:41:43 -04:00
{
Logger . Error ( $"Encountered unknown guide prototype: {guideId}" ) ;
continue ;
}
guides . Add ( guideId , guide ) ;
}
2024-06-07 00:05:58 +12:00
OpenGuidebook ( guides , rootEntries , forceRoot , includeChildren , selected ) ;
}
public void CloseGuidebook ( )
{
if ( _guideWindow = = null )
return ;
if ( _guideWindow . IsOpen )
{
UIManager . ClickSound ( ) ;
_guideWindow . Close ( ) ;
}
2023-03-22 23:41:43 -04:00
}
2024-06-07 00:05:58 +12:00
private void RecursivelyAddChildren ( GuideEntry guide , Dictionary < ProtoId < GuideEntryPrototype > , GuideEntry > guides )
2023-03-22 23:41:43 -04:00
{
foreach ( var childId in guide . Children )
{
if ( guides . ContainsKey ( childId ) )
continue ;
2024-06-07 00:05:58 +12:00
if ( ! _prototypeManager . TryIndex ( childId , out var child ) )
2023-03-22 23:41:43 -04:00
{
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 ) ;
}
}
}