UI/TownScreen.razor
@using System;
@using Sandbox;
@using Sandbox.UI;

@* The town is a 3D street; the clickable names float over the buildings (StageHotspots in the scene). *@
<root class="screen">
	@foreach ( var h in Spots )
	{
		var spot = h;
		var at = At( spot );
		<div class="hotspot @(spot.Target == GameScreen.Arena ? "arena" : "")" style="left: @(at.x)px; top: @(at.y)px;" onclick=@(() => Gm.Go( spot.Target ))>@spot.Label</div>
	}

	<div class="tabs">
		<div class="tab" onclick=@(() => Gm.Go( GameScreen.Stats ))>@(P.StatPoints > 0 ? $"stats (+{P.StatPoints})" : "stats")</div>
		<div class="tab" onclick=@(() => Gm.QuitToTitle())>@("save & quit")</div>
	</div>

	<div class="who outlined">@P.Name · Level @P.Level · @(P.Enslaved ? $"slave ({P.DuelWins}/{Catalog.DuelsToFreedom} duels won)" : $"{P.Wins} wins")</div>

	<div class="gold-bar"><div class="coin"></div>@P.Gold</div>
</root>

@code
{
	class Spot
	{
		public StageHotspot Source;
		public GameScreen Target;
		public string Label;
	}

	GameManager Gm => GameManager.Instance;
	Profile P => Profile.Current;

	List<Spot> spots;

	List<Spot> Spots
	{
		get
		{
			if ( spots is not null ) return spots;
			var set = World3D.Instance?.CurrentSet;
			if ( !set.IsValid() ) return new();
			spots = set.Hotspots.Select( h => new Spot { Source = h, Target = h.Target, Label = Label( h ) } ).ToList();
			return spots;
		}
	}

	// a slave's way in is the dungeon
	string Label( StageHotspot h ) => h.Target == GameScreen.Arena && P.Enslaved ? "Dungeon Duel" : h.Label;

	/// <summary>Where a building's label goes on screen (it follows the 3D camera).</summary>
	static Vector2 At( Spot s )
	{
		var w = World3D.Instance;
		if ( !w.IsValid() || !s.Source.IsValid() ) return new Vector2( -500, -500 );
		var p = w.ToStage( s.Source.WorldPosition );
		return new Vector2( MathF.Round( p.x ), MathF.Round( p.y ) );
	}

	protected override int BuildHash() => HashCode.Combine( P?.Gold, P?.Level, P?.StatPoints, P?.Name, spots is null, World3D.Instance?.CurrentSet, (int)(RealTime.Now * 20) );
}