UI/StandingsPills.razor

A Blazor-like Sandbox UI component that renders race standings as pill rows. It queries RaceStandings from the current game mode or falls back to player car components, builds a 3-position window around the local player (with a detached leader), and renders avatars, colors, ghost rows and initials.

NetworkingFile Access
@using Sandbox;
@using Sandbox.UI;
@using Machines.Components;
@using Machines.GameModes;

@namespace Machines.UI
@inherits Panel

<root class="standings-rail">
    @foreach ( var row in GetRows() )
    {
        var standing = row.Standing;
        var position = standing.Position;

        @if ( standing.IsGhost )
        {
            <div class="standing-row ghost @(row.Detached ? "detached" : "")">
                <div class="row-pos">@position</div>
                <div class="row-pill">
                    <div class="color-bar" style="background-color: rgba(180, 180, 255, 0.8)"></div>
                    <span class="row-name">@(standing.GhostName ?? "Ghost")</span>
                </div>
            </div>
        }
        else
        {
            var color = standing.Color;
            var r = (int)(color.r * 255);
            var g = (int)(color.g * 255);
            var b = (int)(color.b * 255);
            var barStyle = $"background-color: rgba({r}, {g}, {b}, 1)";

            <div class="standing-row @(row.IsLocal ? "local" : "") @(row.Detached ? "detached" : "")">
                <div class="row-pos">@position</div>
                <div class="row-pill">
                    <div class="color-bar" style="@barStyle"></div>
                    @if ( row.IsLocal )
                    {
                        @if ( standing.IsBot )
                        {
                            <div class="player-initial" style="@barStyle">@GetInitial( standing.Name )</div>
                        }
                        else
                        {
                            var steamId = standing.SteamId != 0 ? standing.SteamId : Sandbox.Game.SteamId.ValueUnsigned;
                            <div class="avatar-wrap">
                                <image class="avatar" src="avatar:@(steamId)" />
                                <div class="avatar-fade"></div>
                            </div>
                        }
                    }
                    <span class="row-name">@standing.Name</span>
                </div>
            </div>
        }
    }
</root>

@code
{
    private struct Row
    {
        public RaceStandings.Standing Standing;
        public bool IsLocal;
        public bool Detached; // leader shown above the local window
    }

    /// <summary>
    /// Three positions around the local player, plus detached leader when outside that window.
    /// </summary>
    private List<Row> GetRows()
    {
        var standings = GetStandings()
            .Where( s => s.IsGhost || s.Car.IsValid() )
            .ToList();

        // Hide the ghost once real racers exist; it's just clutter then.
        var hasOtherRacers = standings.Count( s => !s.IsGhost ) > 1;
        standings = standings.Where( s => !s.IsGhost || !hasOtherRacers ).ToList();

        if ( standings.Count == 0 )
            return new List<Row>();

        var maxPos = standings.Max( s => s.Position );
        var local = standings.FirstOrDefault( s => !s.IsGhost && s.IsLocal );
        var localPos = local.IsLocal ? local.Position : 1;

        // Three-position window centred on the local player.
        var lo = Math.Clamp( localPos - 1, 1, Math.Max( 1, maxPos - 2 ) );
        var hi = Math.Min( maxPos, lo + 2 );
        var leaderDetached = lo > 1;

        return standings
            .Where( s => s.Position == 1 && leaderDetached || (s.Position >= lo && s.Position <= hi) )
            .OrderBy( s => s.Position )
            .Select( s => new Row
            {
                Standing = s,
                IsLocal = !s.IsGhost && s.IsLocal,
                Detached = leaderDetached && s.Position == 1,
            } )
            .ToList();
    }

    private List<RaceStandings.Standing> GetStandings()
    {
        var standings = BaseGameMode.Current?.GetComponent<RaceStandings>();
        if ( standings.IsValid() )
            return standings.GetStandings();

        // No race mode: fall back to slot order.
        return Scene.GetAllComponents<Machines.Player.Car>()
            .Where( c => c.IsValid() && c.Slot >= 0 )
            .OrderBy( c => c.Slot )
            .Select( ( c, i ) => new RaceStandings.Standing
            {
                Car = c,
                Slot = c.Slot,
                Name = c.DisplayName,
                Color = c.PlayerColor,
                IsBot = c.IsBot,
                IsLocal = c.IsLocalPlayer,
                Position = i + 1,
                GapToAhead = -1f
            } )
            .ToList();
    }

    private string GetInitial( string name )
    {
        return string.IsNullOrEmpty( name ) ? "?" : name[0].ToString().ToUpper();
    }

    protected override int BuildHash()
    {
        return HashCode.Combine( Connection.All.Count, (Time.Now * 10f).FloorToInt() );
    }
}