ui/OptionsMenu.razor

A Razor UI panel for the options menu. It renders sliders and toggles for audio, crosshair scale, chat and restart confirmation, handles input for closing the menu with controller buttons, and calls Manager and GameSettingsSystem methods to apply and save changes.

NetworkingFile Access
@namespace Sandbox
@using Sandbox;
@using Sandbox.UI;
@using System;
@inherits Panel
@attribute [StyleSheet("OptionsMenu.razor.scss")]

<root>
	<div class="content">
		@* <div class="title">Options</div> *@

		<div class="header">Volume</div>

		<div class="option_row slider_row">
			<div class="option_label">Overall</div>
			<SliderControl style="min-width: 250px;" Value:bind="@GameSettingsSystem.Current.MasterVolume" Min=@(0f) Max=@(100f) Step=@(1) OnValueChanged=@SetMasterVolume></SliderControl>
		</div>

		<div class="option_row slider_row">
			<div class="option_label">SFX</div>
			<SliderControl style="min-width: 250px;" Value:bind="@GameSettingsSystem.Current.SfxVolume" Min=@(0f) Max=@(100f) Step=@(1) OnValueChanged=@SetSfxVolume></SliderControl>
		</div>

		<div class="option_row slider_row">
			<div class="option_label">Music</div>
			<SliderControl style="min-width: 250px;" Value:bind="@GameSettingsSystem.Current.MusicVolume" Min=@(0f) Max=@(100f) Step=@(1) OnValueChanged=@SetMusicVolume></SliderControl>
		</div>

		<div class="option_row slider_row crosshair_row">
			<div class="option_label crosshair_label">Crosshair Size</div>
			<SliderControl style="min-width: 250px;" Value:bind="@GameSettingsSystem.Current.CrosshairScale" Min=@(0.5f) Max=@(3f) Step=@(0.05f) OnValueChanged=@SetCrosshairScale></SliderControl>
		</div>

		<div class="section_spacer"></div>

		<div class="option_row toggle_row">
			<div class="option_label toggle_label" style="min-width: 350px; text-shadow: 4px 4px 0px black;">Don't Confirm Restart</div>
			<SwitchControl Value:bind="@DontConfirmRestartValue"></SwitchControl>
		</div>

		<div class="option_row toggle_row second_toggle_row">
			<div class="option_label toggle_label" style="min-width: 350px; text-shadow: 4px 4px 0px black;">Disable Chat</div>
			<SwitchControl Value:bind="@DisableChatValue"></SwitchControl>
		</div>

		@* <div class="option_row" style="margin-top: -2px;">
			<div class="header" style="font-size: 28px; padding-top: 0; color: #ccc;">Lobby Privacy</div>
			<DropDown style="margin-left: 12px; margin-right: -40px;" Value:bind=@LobbyPrivacyValue Options=@LobbyPrivacyOptions></DropDown>

		</div> *@


		@* <div class="blank"></div> *@

		<button class="sbox_options_button" onclick=@OpenSystemSettings>@("s&box options")</button>
	</div>
	<button class="close_button @(Input.UsingController ? "controller" : "") @(_closeActivated ? "ctrl-activated" : _closeHeld ? "ctrl-held" : "")" onclick=@(() => Manager.Instance.SetOptionsMenuOpen(false))>
	</button>
</root>

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

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

		Manager.Instance.Restart();

		Manager.Instance.SetEscMenuOpen(false);
	}

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

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

	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);
	}

	void OpenSystemSettings()
	{
		Game.Overlay.ShowSettingsModal();
	}

	void SetMasterVolume(float value)
	{
		Manager.Instance.RefreshMasterMixerVolume();
		GameSettingsSystem.Save();
	}

	void SetSfxVolume(float value)
	{
		Manager.Instance.RefreshSfxMixerVolume();
		GameSettingsSystem.Save();
	}

	void SetMusicVolume(float value)
	{
		Manager.Instance.RefreshMusicMixerVolume();
		GameSettingsSystem.Save();
	}

	void SetCrosshairScale(float value)
	{
		var clamped = Math.Clamp( value, 0.5f, 3f );
		GameSettingsSystem.Current.CrosshairScale = clamped;

		var crosshair = Manager.Instance?.Components.Get<CrosshairComponent>();
		if ( crosshair != null )
			crosshair.Scale = clamped;

		GameSettingsSystem.Save();
	}

	bool _closeHeld;
	bool _closeActivated;

	public override void Tick()
	{
		base.Tick();
		if (!Input.UsingController)
		{
			_closeHeld = false;
			_closeActivated = false;
			return;
		}

		if (_closeActivated)
		{
			_closeActivated = false;
			Manager.Instance.SetOptionsMenuOpen(false);
			return;
		}

		bool closeDown = Input.Down("R") || Input.Down("Back");
		if (_closeHeld && !closeDown)
		{
			_closeHeld = false;
			_closeActivated = true;
			Manager.Instance.PlaySfxUI("click", pitch: 1.15f, volume: 0.75f);
		}
		else
		{
			if (!_closeHeld && closeDown) Manager.Instance.PlaySfxUI("click", pitch: 0.85f, volume: 0.6f);
			_closeHeld = closeDown;
		}
	}

	protected override int BuildHash()
	{
		return System.HashCode.Combine(
			Manager.Instance.IsOptionsMenuOpen,
			Input.UsingController,
			_closeHeld,
			_closeActivated
		);
	}

	void SetDontConfirmRestart(bool value)
	{
		Manager.Instance.DontConfirmRestart = value;
		GameSettingsSystem.Current.DontConfirmRestart = value;
		GameSettingsSystem.Save();
	}

	void SetDisableChat(bool value)
	{
		GameSettingsSystem.Current.DisableChat = value;
		GameSettingsSystem.Save();
	}

	bool DontConfirmRestartValue
	{
		get => GameSettingsSystem.Current.DontConfirmRestart;
		set => SetDontConfirmRestart( value );
	}

	bool DisableChatValue
	{
		get => GameSettingsSystem.Current.DisableChat;
		set => SetDisableChat( value );
	}

	List<Option> LobbyPrivacyOptions = new List<Option>()
	{
		new Option("Public", 0),
		new Option("Friends", 2),
		new Option("Private", 1),
	};

	int LobbyPrivacyValue
	{
		get => Manager.Instance.LobbyPrivacy;
		set
		{
			Manager.Instance.LobbyPrivacy = value;
			GameSettingsSystem.Save();

			if ( Networking.IsActive )
			{
				Networking.Disconnect();
				Manager.Instance.CreateLobby();
			}
		}
	}
}