UI/Notices/Hints.cs
namespace Sandbox.UI;

public class Hints : GameObjectSystem<Hints>
{
	[Title( "Show UI Hints" )]
	[ConVar( "cl_showhints", ConVarFlags.Saved | ConVarFlags.GameSetting, Help = "Whether to display popup hints." )]
	public static bool cl_showhints { get; set; } = true;

	record class Hint( string Name, string Icon, RealTimeUntil Delay )
	{
		public bool Ready => Delay < 0;
	}

	List<Hint> _queue = new();

	public Hints( Scene scene ) : base( scene )
	{
		Queue( "openspawnmenu", "ℹ️", 10 );
		Queue( "openinspectmenu", "ℹ️", 40 );
		Queue( "openpausemenu", "ℹ️", 70 );

		Listen( Stage.StartUpdate, 0, Tick, "UpdateHints" );
	}

	public void Queue( string hintName, string hintIcon, float delay )
	{
		var hint = new Hint( hintName, hintIcon, delay );
		_queue.Add( hint );
	}

	RealTimeSince timeSinceLast = 0;

	void Tick()
	{
		if ( timeSinceLast < 3 )
			return;

		if ( !cl_showhints )
			return;

		var next = _queue.Where( x => x.Ready ).FirstOrDefault();
		if ( next is null ) return;

		_queue.Remove( next );
		timeSinceLast = 0;

		var phrase = Game.Language.GetPhrase( $"hint.{next.Name}" );
		phrase = ReplaceSpecialTokens( phrase );

		Notices.AddNotice( next.Icon, Color.White, phrase, 5 );
	}

	public void Cancel( string hintName )
	{
		_queue.RemoveAll( x => x.Name.Equals( hintName, StringComparison.OrdinalIgnoreCase ) );
	}

	string ReplaceSpecialTokens( string input )
	{
		if ( !input.Contains( '{' ) ) return input;
		if ( !input.Contains( '}' ) ) return input;

		// replace {input:<inputname>} with the key bound to that input
		{
			input = System.Text.RegularExpressions.Regex.Replace( input,
				@"{input:([^}]+)}",
				match =>
				{
					string key = match.Groups[1].Value.Trim();
					return $"<span class=\"key\"> {Input.GetButtonOrigin( key )} </span>";
				} );
		}

		return input;
	}
}