ui/LobbyMenu.razor

A Razor UI component for the lobby menu. It renders coin display, player slots/nametags, various panels (quest, shop, loadout, perks, leaderboard), and play/quit buttons, and handles input for controller/keyboard to start or quit the game and toggle subpanels.

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

<root>
	@if ( !Manager.HideProgressionSystem )
	{
	<div class="coin_display">
		<div class="coin_display_icon"></div>
		<label class="coin_display_amount">@ProgressManager.GetCoins()</label>
	</div>
	}

	@foreach( var slot in Game.ActiveScene.GetAllComponents<LobbyPlayerSlot>() )
	{
		var player = slot.OccupyingPlayer;
		int left = 0;
		int bottom = 0;
		switch(slot.SlotIndex)
		{
			case 0:
				left = 50;
				bottom = 40;
				break;
			case 1:
				left = 40;
				bottom = 65;
				break;
			case 2:
				left = 60;
				bottom = 65;
				break;
		}

		@if(player is not null)
		{
			var name = player.Network.Owner.DisplayName;
			var steamId = player.Network.Owner.SteamId;
			<LobbyNametagPanel style="left: @(left)%; bottom: @(bottom)px; transform: translate(-50%, 0%);" PlayerInfo=@(new PlayerInfo(name, steamId, level: -1)) />
		}
		else
		{
			<LobbySlotPanel Index=@(slot.SlotIndex) style="left: @(left)%; bottom: @(bottom)px; transform: translate(-50%, 0%);" />
		}
	}

	@if(Manager.Instance.ShowQuestPanel)
	{
		<QuestPanel />
	}
	else if(Manager.Instance.ShowShopPanel)
	{
		<ShopPanel />
	}
	else if(Manager.Instance.ShowLoadoutPanel)
	{
		<LoadoutPanel />
	}
	else if(Manager.Instance.ShowPerkUnlockPanel)
	{
		<PerkUnlockPanel />
	}
	else if(Manager.Instance.PlayerProfileToShow != null)
	{
		<PlayerProfilePanel />
	}
	else if(Manager.Instance.RunEntryToShow != null)
	{
		<LeaderboardRunInfoPanel />
	}
	else
	{
		<div class="upper_right">
			<DifficultyPanel />
			<LeaderboardPanel style="position: relative; top: 0px; right: 0px; width: 500px;" [email protected] IsOnMainMenu=@true />
		</div>

		@if ( !Manager.HideProgressionSystem )
		{
		<div class="top_left_buttons">
			<button class="quest_button" onclick=@(() => ClickQuestButton())>Quests
				@{
					var questReadyCount = ProgressManager.Quests.Count(q => ProgressManager.IsQuestReadyToCollect(q.Id))
						+ ProgressManager.Achievements.Count(a => ProgressManager.IsAchievementUnlocked(a.Name) && !ProgressManager.IsAchievementClaimed(a.Name));
				}
				@if(questReadyCount > 0)
				{
					<div class="quest_badge">@questReadyCount</div>
				}
			</button>
			<button class="shop_button" onclick=@(() => ClickShopButton())>Shop</button>
			<button class="loadout_button" onclick=@(() => ClickLoadoutButton())>Loadout</button>
			<button class="perks_button" onclick=@(() => ClickPerksButton())>Perks</button>
		</div>
		}

		<div class="content">
			@if(Networking.IsHost && Scene.GetAllComponents<Player>().Count() > 0)
			{
				<button class="play_button @(Input.UsingController ? "controller" : "") @(_playActivated ? "ctrl-activated" : _playHeld ? "ctrl-held" : "")" onclick=@(() => Play())>
				</button>
			}
			else
			{
				<button class="play_button_client"></button>
			}

			<button class="quit_button @(Input.UsingController ? "controller" : "") @(_quitActivated ? "ctrl-activated" : _quitHeld ? "ctrl-held" : "")" onclick=@(() => Quit())>
			</button>
		</div>

		// hack to make player levels update properly when viewing leaderboard
		// todo: unnecessary unless player levels return
		@* if(Manager.Instance.SkipShowingLeaderboardFrames > 0)
		{
			Manager.Instance.SkipShowingLeaderboardFrames--;
		}
		else
		{
			<LeaderboardPanel [email protected]></LeaderboardPanel>
		} *@
	}

	@if(!string.IsNullOrEmpty(Manager.Instance.LobbyHoveredPlayerName ))
	{
		<PlayerTooltip [email protected] ShowIcon=@false />
	}

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

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

		StartGame();
	}

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

		await Task.Frame();

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

	public void Quit()
	{
		Game.Close();
	}

	bool _playHeld;
	bool _playActivated;
	bool _quitHeld;
	bool _quitActivated;

	public override void Tick()
	{
		base.Tick();
		if (!Input.UsingController)
		{
			_playHeld = false;
			_playActivated = false;
			_quitHeld = false;
			_quitActivated = false;
			if (Input.Pressed("banish")) Quit();
			return;
		}

		if (_playActivated)
		{
			_playActivated = false;
			Play();
			return;
		}

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

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

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

	protected override int BuildHash()
	{
		var leaderboardPerkHash = Manager.Instance.RunEntryToShow?.GetHashCode() ?? 0;

		var showHash = System.HashCode.Combine(
			Manager.Instance.ShowQuestPanel,
			Manager.Instance.ShowShopPanel,
			Manager.Instance.ShowLoadoutPanel,
			Manager.Instance.ShowPerkUnlockPanel
		);

		return System.HashCode.Combine(
			Manager.Instance.IsEscMenuOpen,
			leaderboardPerkHash,
			showHash,
			Input.UsingController,
			ProgressManager.StateVersion,
			Time.Now // todo: ?
		);
	}

	void ClickQuestButton()
	{
		Manager.Instance.ShowQuestPanel = true;
	}

	void ClickShopButton()
	{
		Manager.Instance.ShowShopPanel = true;
	}

	void ClickLoadoutButton()
	{
		Manager.Instance.ShowLoadoutPanel = true;
	}

	void ClickPerksButton()
	{
		Manager.Instance.ShowPerkUnlockPanel = true;
	}
}