A Razor UI component for a player nameplate. It renders player name, optional placement medal, and a chevron, choosing color and name from either a Car component, a GhostPlayer, or fallback properties and hiding/fading logic based on local player and game state.
@using Sandbox;
@using Sandbox.UI;
@using Machines.Player;
@using Machines.Components;
@using Machines.Ghost;
@using Machines.GameModes;
@namespace Machines.UI
@inherits PanelComponent
@{
var color = GetPlayerColor();
var playerName = GetPlayerName();
}
<root class="nameplate @(IsFaded() ? "ghost" : "")">
@if ( !ShouldHide() )
{
<div class="info" style="border-color: @color.WithAlpha( 0.9f ).Hex; background-color: @color.WithAlpha( 0.8f ).Hex">
<div class="row">
@if ( ShowPlacement )
{
<div class="medal @MedalClass">@(PlacementIndex + 1)</div>
}
<span class="player-name">@playerName</span>
</div>
</div>
}
<div class="chevron" style="color: @color.Hex">▼</div>
</root>
@code
{
private static readonly Color GhostColor = new( 0.6f, 0.6f, 0.6f );
// Fallback values when there is no Car/Ghost parent (e.g. podium clones).
[Property] public string PlayerName { get; set; } = "Player";
[Property] public Color Color { get; set; } = Color.White;
/// <summary>
/// Finishing placement for the medal (0 = gold, 1 = silver, 2 = bronze, -1 = hidden).
/// </summary>
[Property] public int PlacementIndex { get; set; } = -1;
private static readonly string[] MedalClasses = { "gold", "silver", "bronze" };
private bool ShowPlacement => PlacementIndex >= 0 && PlacementIndex < MedalClasses.Length;
private string MedalClass => ShowPlacement ? MedalClasses[PlacementIndex] : "";
private Car Car => GameObject.GetComponentInParent<Car>();
private GhostPlayer Ghost => GameObject.GetComponentInParent<GhostPlayer>();
protected override int BuildHash()
{
return HashCode.Combine( Car, Ghost, ShouldHide(), PlayerName, Color, PlacementIndex );
}
private bool IsFaded() => Ghost.IsValid();
private bool ShouldHide()
{
if ( Ghost.IsValid() )
return false;
var car = Car;
if ( !car.IsValid() || !car.IsLocalPlayer )
return false;
// Always show during waiting/countdown.
if ( BaseGameMode.Current.IsValid() )
{
var state = BaseGameMode.Current.State;
if ( state == GameModeState.WaitingForPlayers || state == GameModeState.Countdown )
return false;
}
// Hide the local player's own nameplate.
return true;
}
private Color GetPlayerColor()
{
if ( Ghost.IsValid() )
return GhostColor;
var car = Car;
if ( car.IsValid() && car.Slot >= 0 )
return car.PlayerColor;
return Color;
}
private string GetPlayerName()
{
var ghost = Ghost;
if ( ghost.IsValid() )
return string.IsNullOrEmpty( ghost.PlayerName ) ? "Player" : ghost.PlayerName;
var car = Car;
if ( car.IsValid() && car.Slot >= 0 )
return car.DisplayName;
return PlayerName;
}
}