UI/Hud.razor

A Razor UI component for the game's HUD. It renders HUD subcomponents (money, event log, inventory, crosshair, chat input, hints, death overlay, etc.), handles chat input focus/submit, and updates build hash for change detection.

Reflection
@using System
@using Sandbox
@using Sandbox.UI
@namespace BrickJam.UI
@inherits PanelComponent

<root>
	@{
		var player = Player.Local;

		var isSpectator = player?.Spectating ?? false;
		var isPlayer = player?.IsValid() ?? false;
	}

	<MoneyCounter />
	<Eventlog />
	<PlayerCounter />
	<StunIndicator />
	<QuickPing />
	<SubtitlesList />
	<Lockpicker />
	<BlackScreen />
	
	<div class="input-hints">
		@if ( isSpectator )
		{
			<div class="hint"><inputglyph action="StopFollowing"/>Stop following</div>
			<div class="hint"><inputglyph action="FollowNext"/>Next player</div>
			<div class="hint"><inputglyph action="FollowPrevious"/>Previous player</div>
		}
		else
		{
			<div class="hint"><inputglyph action="inventory"/>Inventory</div>
			<div class="hint"><inputglyph action="chat"/>Chat</div>
			<div class="hint"><inputglyph action="ping"/>Quick Ping</div>
		}
	</div>

	@if ( player.IsValid() )
	{
		<Inventory />
		<Shop />
		<TextInput Ghost="Say something..." class="chat-input" @ref="ChatInput" onsubmit=@OnChatSubmit />

		@if ( !player.SeenTips )
		{
			<TutorialTips />
		}
	}

	@if ( player.IsValid() && player.IsAlive && !LockpickerBus.IsOpen )
	{
		<div class="crosshair">
			<div class="mark">+</div>
			<RadialProgress />
			<InteractionTip />
		</div>
	}

	@if ( player.IsValid() && !player.IsAlive )
	{
		<div class="death">
			<span class="title">YOU'RE DEAD!</span>
		</div>
	}
</root>

@code {
	private TextInput ChatInput { get; set; }

	protected override void OnUpdate()
	{
		if ( ChatInput is null )
			return;

		if ( Input.Pressed( "chat" ) )
		{
			ChatInput.Focus();
			ChatInput.AddClass( "visible" );
		}
	}

	private void OnChatSubmit()
	{
		if ( ChatInput is null )
			return;

		var text = ChatInput.Text;
		if ( !string.IsNullOrWhiteSpace( text ) )
			Player.Local?.Say( text );

		ChatInput.Text = "";
		ChatInput.RemoveClass( "visible" );
	}

	protected override int BuildHash() => HashCode.Combine(
		Player.Local,
		Player.Local?.IsAlive ?? false,
		LockpickerBus.IsOpen );
}

<style>
	Hud {
		position: absolute;
		top: 0;
		left: 0;
		width: 100%;
		height: 100%;
		display: flex;
		flex-direction: column;
		align-items: center;
		font-family: "alagard";
		justify-content: flex-start;

		.crosshair {
			position: absolute;
			top: 0; left: 0;
			width: 100%;
			height: 100%;
			flex-direction: column;
			align-items: center;
			justify-content: center;

			.mark {
				color: white;
				font-size: 28px;
				text-shadow: 2px 2px 0px black;
			}
		}

		.input-hints {
			position: absolute;
			bottom: 30px;
			right: 0px;
			align-items: flex-end;
			flex-direction: column;
			z-index: 3;

			.hint {
				color: white;
				height: 32px;
				justify-content: center;
				font-size: 32px;
				padding-left: 50px;
				padding-top: 10px;
				padding-bottom: 40px;
				padding-right: 10px;
				background: linear-gradient(to left, rgba(black, 0.5) 0%, rgba(black, 0.2) 75%, rgba(black, 0) 100%);
				margin-top: 5px;
				text-shadow: 3px 3px 0px black;

				InputGlyph {
					width: 32px;
					aspect-ratio: 1;
					margin-right: 10px;
				}
			}
		}

		.chat-input {
			position: absolute;
			width: 450px;
			min-height: 20px;
			font-size: 24px;
			opacity: 0;
			pointer-events: all;
			background-color: rgba(37, 64, 98, 1);
			box-shadow: 3px 3px 0px 0px rgba(17, 44, 78, 1);
			transition: opacity 0.2s ease-in-out, transform 0.2s ease-in-out;
			transform: translate(-50% 200px);
			text-shadow: 3px 3px 0px black;
			left: 50%;
			top: 50%;
			margin-top: 40px;

			&.visible {
				opacity: 1;
				transform: translate(-50% 0px);
			}
		}

		.death {
			position: absolute;
			width: 100%;
			height: 100%;
			left: 0px;
			top: 0px;
			color: white;
			font-size: 32px;
			z-index: 2;
			backdrop-filter: grayscale(100%);
			justify-content: center;
			transition: opacity 1s ease-in-out;
			opacity: 0;
			text-shadow: 4px 4px 0px black;

			&.visible {
				opacity: 1;
			}

			.container {
				flex-direction: column;
				align-items: center;
			}

			span {
				background: linear-gradient(to left, rgba(black, 0) 0%, rgba(black, 0.4) 20%, rgba(black, 0.4) 80%, rgba(black, 0) 100%);
				padding: 10px;
				padding-left: 40px;
				padding-right: 40px;
			}

			.title {
				color: red;
				font-size: 64px;
				margin-top: 150px;
				margin-bottom: 20px;
			}
		}
	}
</style>