ui/SpectatorPanel.razor

A Blazor-style UI panel (SpectatorPanel) for the game's spectator HUD. It displays the currently selected player's display name, left/right navigation hints when multiple players exist, and adjusts layout when paused. It also handles input to cycle the spectated player and contributes a UI hash for redraw optimization.

@namespace Sandbox
@using Sandbox;
@using Sandbox.UI;
@using System;
@inherits Panel

@{
	var player = Manager.Instance.SelectedPlayer;
	var displayName = player.IsValid()
		? player.Network?.Owner?.DisplayName ?? ""
		: "";
	var multiPlayer = Manager.Instance.Players.Count > 1;
}

<root>
	<div class="top bar" style="padding-top: @(Manager.Instance.IsPaused ? 232 : 32)px;">
		<div class="header">
			@if ( multiPlayer )
			{
				<div class="nav-hint">
					<label class="arrow-label">←</label>
					<InputHint class="cycle-hint" Button="SpectateLeft" />
				</div>
			}
			<label class="spectating-label">Spectating</label>
			<label class="player-name">@displayName</label>
			@if ( multiPlayer )
			{
				<div class="nav-hint">
					<InputHint class="cycle-hint" Button="SpectateRight" />
					<label class="arrow-label">→</label>
				</div>
			}
		</div>
		@* <label class="subheader">You will join the next game if there are enough slots</label> *@
	</div>
	<div class="filler">
	</div>
</root>

@code
{
	void CyclePlayer( int direction )
	{
		var players = Manager.Instance.Players;
		if ( players.Count <= 1 ) return;

		var current = Manager.Instance.SelectedPlayer;
		var index = current.IsValid() ? players.IndexOf( current ) : 0;
		index = (index + direction + players.Count) % players.Count;
		Manager.Instance.SelectedPlayer = players[index];
	}

	public override void Tick()
	{
		base.Tick();

		if ( !Manager.Instance.IsSpectator ) return;
		if ( Manager.Instance.Players.Count <= 1 ) return;

		var pressedLeft = Input.Pressed( "SpectateLeft" ) || Input.Keyboard.Pressed( "left" );
		var pressedRight = Input.Pressed( "SpectateRight" ) || Input.Keyboard.Pressed( "right" );

		if ( pressedLeft ) CyclePlayer( -1 );
		else if ( pressedRight ) CyclePlayer( 1 );
	}

	protected override int BuildHash()
	{
		return System.HashCode.Combine(
			Manager.Instance.IsPaused,
			Manager.Instance.SelectedPlayer,
			Manager.Instance.SelectedPlayer?.Network?.Owner?.DisplayName,
			Manager.Instance.Players.Count,
			Input.UsingController
		);
	}
}