UI/MapCarousel.razor

A Blazor-style UI Razor component for a map carousel in the lobby. It picks the hero (currently browsed) map and the adjacent previous/next maps from the mode's map list and renders MapCard components for them. It also overrides BuildHash to include lobby selection and per-connection browsed map info so the UI re-renders when votes change.

Networking
@using Sandbox;
@using Sandbox.UI;
@using Machines.Components;
@using Machines.Resources;

@namespace Machines.UI
@inherits Panel

@{
	var flow = LobbyFlow.Current;
	var maps = flow?.MapsForMode( flow.SelectedMode );
	var count = maps?.Count ?? 0;

	MapResource hero = flow?.LocalBrowsedMap;
	MapResource prev = null;
	MapResource next = null;

	if ( count >= 2 && hero != null )
	{
		var heroIdx = -1;
		for ( int i = 0; i < count; i++ )
		{
			if ( maps[i].ResourcePath == hero.ResourcePath ) { heroIdx = i; break; }
		}
		if ( heroIdx >= 0 )
		{
			prev = maps[(heroIdx - 1 + count) % count];
			next = maps[(heroIdx + 1) % count];
			if ( count == 2 ) prev = null; // avoid showing the same map on both sides
		}
	}
}

<div class="carousel">
	@if ( prev != null )
	{
		<MapCard @key=@($"prev-{prev.ResourcePath}") Map=@prev IsSide=@true class="card-prev" />
	}

	<MapCard @key=@($"hero-{hero?.ResourcePath ?? "none"}") Map=@hero IsSide=@false class="card-hero" />

	@if ( next != null )
	{
		<MapCard @key=@($"next-{next.ResourcePath}") Map=@next IsSide=@true class="card-next" />
	}
</div>

@code
{
	protected override int BuildHash()
	{
		var flow = LobbyFlow.Current;
		var h = System.HashCode.Combine( flow?.SelectedMode, flow?.SelectedMapIdent, flow?.LocalBrowsedMapIdent, flow?.State, Connection.All.Count );

		// Re-render when votes change so the carousel and avatars update.
		if ( flow != null )
		{
			foreach ( var c in Connection.All )
				h = System.HashCode.Combine( h, c.SteamId, flow.BrowsedIdentFor( c ) );
		}

		return h;
	}
}