ui/EscMenu.razor

A Razor UI panel for the escape menu (pause/menu) in the game. It renders buttons for resume, options, restart, and return/quit, shows confirmation dialogs, and handles input (keyboard/controller) and RPC-triggered fades/restarts/scene loads via Manager and Networking calls.

NetworkingFile Access
🌐 scenes/game.scene
@namespace Sandbox
@using Sandbox;
@using Sandbox.UI;
@using System;
@using System.Linq;
@inherits Panel
@attribute [StyleSheet("EscMenu.razor.scss")]

<root>
	@* <label class="title">@(Manager.Instance.IsPaused ? "Paused" : "")</label> *@
	<div class="blank" />
	<div class="content">
		@* <div class="equipment">
			@{
				var _gunId = ProgressManager.GetEffectiveSelectedGunId();
				var _gunDef = ProgressManager.ShopItems.FirstOrDefault( x => x.Id == _gunId );
				var _charmId = ProgressManager.GetEffectiveSelectedCharmIds().FirstOrDefault() ?? "";
				var _charmDef = !string.IsNullOrEmpty( _charmId )
					? ProgressManager.ShopItems.FirstOrDefault( x => x.Id == _charmId )
					: default;
				var _equippedGems = ProgressManager.GetEffectiveEquippedGems();
			}

			@if ( _gunDef.Id != null )
			{
				<div class="equip_item">
					<div class="equip_icon" style="background-image:url(@_gunDef.IconPath);"></div>
					<div class="equip_info">
						<label class="equip_name">@_gunDef.Name</label>
						@if ( _gunDef.ItemDescription != null )
						{
							<RichText class="equip_desc" Text=@_gunDef.ItemDescription() />
						}
					</div>
				</div>
			}

			@if ( _charmDef.Id != null )
			{
				<div class="equip_item">
					<div class="equip_icon" style="background-image:url(@_charmDef.IconPath);"></div>
					<div class="equip_info">
						<label class="equip_name">@_charmDef.Name</label>
						@if ( _charmDef.ItemDescription != null )
						{
							<RichText class="equip_desc" Text=@_charmDef.ItemDescription() />
						}
					</div>
				</div>
			}

			@foreach ( var _gemId in _equippedGems )
			{
				var _gemDef = ProgressManager.ShopItems.FirstOrDefault( x => x.Id == _gemId );
				if ( _gemDef.Id != null )
				{
					<div class="equip_item">
						<div class="equip_icon" style="background-image:url(@_gemDef.IconPath);"></div>
						<div class="equip_info">
							<label class="equip_name">@_gemDef.Name <span class="equip_level">Lv @ProgressManager.GetGemDisplayLevel( _gemId )</span></label>
							@if ( _gemDef.GemDescription != null )
							{
								<RichText class="equip_desc" Text=@_gemDef.GemDescription( ProgressManager.GetGemDisplayLevel( _gemId ) ) />
							}
						</div>
					</div>
				}
			}
		</div> *@
		@if(Manager.Instance.IsMultiplayer && Networking.IsHost)
		{
			<div class="col">
				@* <label>HOST SETTINGS</label> *@
				<button class="@(Manager.Instance.IsPaused ? "unpause_button" : "pause_button")" style="opacity:@(Networking.IsHost ? 1f : 0.1f);" onclick=@(() => TogglePause())></button>
			</div>
		}
		<div class="row">
			<div class="col">
				<button class="resume_button @(Input.UsingController ? "controller" : "") @(_resumeActivated ? "ctrl-activated" : _resumeHeld ? "ctrl-held" : "")" onclick=@ResumeButton>
				</button>
				<button class="options_button @(Input.UsingController ? "controller" : "") @(_optionsActivated ? "ctrl-activated" : _optionsHeld ? "ctrl-held" : "")" onclick=@(() => Options())>
				</button>
			</div>
			<div class="col">
				<button class="@(Networking.IsHost ? "restart_button" : "restart_button_client") @(Input.UsingController && Networking.IsHost ? "controller" : "") @(_restartActivated ? "ctrl-activated" : _restartHeld ? "ctrl-held" : "")" onclick=@(() => Restart())>
				</button>
				<button class="@(Networking.IsHost ? "quit_to_lobby_button" : "leave_button") @(Input.UsingController ? "controller" : "") @(_quitHeld ? "ctrl-held" : _quitActivated ? "ctrl-activated" : "")" onclick=@(() => ReturnToLobby())>
				</button>
			</div>
		</div>
	</div>
	@if ( AreYouSureAction  is not null)
	{
		<div class="are-you-sure" onclick=@(() => { Manager.Instance.PlaySfxUI("click", pitch: 0.75f, volume: 0.6f); AreYouSureAction = null; })>
			<div class="panel">
				<label class="title">@AreYouSureText</label>
				<div class="row">
					<button class="confirm_no_button" style="width: 25%;" onclick=@(() => { AreYouSureAction = null; })>
						@if(Input.UsingController) { <InputHint class="inputbutton" Button="Back" /> }
					</button>
					<button class="confirm_yes_button" style="width: 25%;" onclick=@(() => { AreYouSureAction?.Invoke(); AreYouSureAction = null; })>
						@if(Input.UsingController) { <InputHint class="inputbutton" Button="LB" /> }
					</button>
				</div>
			</div>
		</div>
	}
</root>

@code
{
	Action AreYouSureAction = null;
	string AreYouSureText = "Are you sure?";
	bool _resumeHeld;
	bool _resumeActivated;
	bool _restartHeld;
	bool _restartActivated;
	bool _quitHeld;
	bool _quitActivated;
	bool _optionsHeld;
	bool _optionsActivated;


	public void Restart()
	{
		if (!Networking.IsHost)
			return;

		if( Manager.Instance.DontConfirmRestart )
		{
			DoRestart();
		}
		else
		{
			AreYouSureText = "Restart the run?";
			AreYouSureAction = () =>
			{
				DoRestart();
			};
		}
	}

	void DoRestart()
	{
		Manager.Instance.PlaySfxUIRpc("restart", pitch: Game.Random.Float(1.15f, 1.2f), volume: 0.8f);
		Manager.Instance.SetEscMenuOpen(false);

		DoRestartAsync();
	}

	async void DoRestartAsync()
	{
		Manager.Instance.FadeRpc(fadeIn: false);

		await Task.Frame();

		Manager.Instance.Restart();
	}

	void ResumeButton()
	{
		Manager.Instance.SetEscMenuOpen( false );
	}

	public void ReturnToLobby()
	{
		if(Manager.Instance.DontConfirmRestart)
		{
			DoReturnToLobby();
		}
		else
		{
			if(Networking.IsHost)
			{
				AreYouSureText = Manager.Instance.IsMultiplayer
					? "Quit the run and return party to lobby?"
					: "Quit the run and return to lobby?";
			}
			else
			{
				AreYouSureText = "Quit the run and abandon party?";
			}
			AreYouSureAction = () =>
			{
				DoReturnToLobby();
			};
		}
	}

	void DoReturnToLobby()
	{
		if(!Networking.IsHost)
		{
			Networking.Disconnect();
			Scene.LoadFromFile("scenes/game.scene");
			return;
		}

		Manager.Instance.SetEscMenuOpen(false);

		DoReturnToLobbyAsync();
	}

	async void DoReturnToLobbyAsync()
	{
		Manager.Instance.FadeRpc(fadeIn: false);

		await Task.Frame();

		Manager.Instance.SetGameState(GameState.Lobby);
	}

	public void TogglePause()
	{
		if (!Networking.IsHost)
			return;

		if(Manager.Instance.IsPaused)
			Manager.Instance.SetEscMenuOpen(false);

		Manager.Instance.SetPaused(!Manager.Instance.IsPaused);
	}

	public void Options()
	{
		Manager.Instance.SetOptionsMenuOpen(true);
	}

	public override void Tick()
	{
		base.Tick();
		if (!Input.UsingController)
		{
			_resumeHeld = false;
			_resumeActivated = false;
			_restartHeld = false;
			_restartActivated = false;
			_quitHeld = false;
			_quitActivated = false;
			_optionsHeld = false;
			_optionsActivated = false;
			return;
		}

		if (AreYouSureAction is not null)
		{
			if(Input.Pressed("LB")) { Manager.Instance.PlaySfxUI("click", pitch: 1.15f, volume: 0.75f); AreYouSureAction?.Invoke(); AreYouSureAction = null; }
			else if (Input.Pressed("Back") || Input.Pressed("Dash")) { Manager.Instance.PlaySfxUI("click", pitch: 0.75f, volume: 0.6f); AreYouSureAction = null; }
			return;
		}

		if (_resumeActivated)
		{
			_resumeActivated = false;
			ResumeButton();
			return;
		}

		bool resumeDown = Input.Down("R");
		if (_resumeHeld && !resumeDown)
		{
			_resumeHeld = false;
			_resumeActivated = true;
			Manager.Instance.PlaySfxUI("click", pitch: 1.15f, volume: 0.75f);
		}
		else
		{
			if (!_resumeHeld && resumeDown) Manager.Instance.PlaySfxUI("click", pitch: 0.85f, volume: 0.6f);
			_resumeHeld = resumeDown;
		}

		if (_restartActivated)
		{
			_restartActivated = false;
			Restart();
			return;
		}

		bool restartDown = Input.Down("LB");
		if (_restartHeld && !restartDown)
		{
			_restartHeld = false;
			_restartActivated = true;
			Manager.Instance.PlaySfxUI("click", pitch: 1.15f, volume: 0.75f);
		}
		else
		{
			if (!_restartHeld && restartDown) Manager.Instance.PlaySfxUI("click", pitch: 0.85f, volume: 0.6f);
			_restartHeld = restartDown;
		}

		if (_quitActivated)
		{
			_quitActivated = false;
			ReturnToLobby();
			return;
		}

		bool quitDown = Input.Down("banish");
		if (_quitHeld && !quitDown)
		{
			_quitHeld = false;
			_quitActivated = true;
			Manager.Instance.PlaySfxUI("click", pitch: 1.15f, volume: 0.75f);
		}
		else
		{
			if (!_quitHeld && quitDown) Manager.Instance.PlaySfxUI("click", pitch: 0.85f, volume: 0.6f);
			_quitHeld = quitDown;
		}

		if (_optionsActivated)
		{
			_optionsActivated = false;
			Options();
			return;
		}

		bool optionsDown = Input.Down("DpadUp");
		if (_optionsHeld && !optionsDown)
		{
			_optionsHeld = false;
			_optionsActivated = true;
			Manager.Instance.PlaySfxUI("click", pitch: 1.15f, volume: 0.75f);
		}
		else
		{
			if (!_optionsHeld && optionsDown) Manager.Instance.PlaySfxUI("click", pitch: 0.85f, volume: 0.6f);
			_optionsHeld = optionsDown;
		}
	}

	protected override int BuildHash()
	{
		int buttonHash = System.HashCode.Combine(
			_resumeHeld,
			_resumeActivated,
			_restartHeld,
			_restartActivated,
			_quitHeld,
			_quitActivated,
			_optionsHeld,
			_optionsActivated
		);

		var gunId = ProgressManager.GetEffectiveSelectedGunId() ?? "";
		var charmId = ProgressManager.GetEffectiveSelectedCharmIds().FirstOrDefault() ?? "";
		var gemsHash = string.Join( ",", ProgressManager.GetEffectiveEquippedGems() ).GetHashCode();

		return System.HashCode.Combine(
			Manager.Instance.IsEscMenuOpen,
			Manager.Instance.IsMultiplayer,
			Input.UsingController,
			AreYouSureAction is not null,
			buttonHash,
			gunId,
			charmId,
			gemsHash
		);
	}
}