A Blazor-style UI component that renders the in-game scoreboard. It builds a table per team showing player level, avatar, name, points (damage+heals), kills, deaths and ping, and toggles visibility while the Score input is pressed.
@using System;
@using Sandbox.UI;
@using KOTH.PlayerExp;
@namespace KOTH.UI
@inherits Panel
@attribute [StyleSheet]
@if (!GameMode.Instance.GameStats.IsValid())
{
return;
}
<root class="hidden flex absolute inset-0 align-center justify-center">
@* <div class="title">
@GameMode.Instance.Title
</div> *@
@RenderTableSection(PlayerState.Local.Team)
@RenderTableSection(PlayerState.Local.Team.GetOpponents())
</root>
@code
{
private RenderFragment RenderTableSection(Team Team) => Builder =>
{
var ColourString = "rgba(200, 200, 200, 0.25)";
if (Team == Team.Terrorist)
{
ColourString = "rgba(0, 255, 0, 0.25)";
}
if (Team == Team.CounterTerrorist)
{
ColourString = "rgba(255, 0, 0, 0.25)";
}
Builder.OpenElement(0, "table");
Builder.AddAttribute(1, "class", "with-padding-lg layout");
Builder.AddAttribute(2, "style", $"background-color: {ColourString};");
// Table Header Row
Builder.OpenElement(3, "div");
Builder.AddAttribute(4, "class", "row header with-px-lg with-py-sm");
// Level Column
Builder.OpenElement(5, "label");
Builder.AddAttribute(6, "class", "col stat-little");
Builder.AddContent(7, "◎");
Builder.CloseElement();
// Avatar Column
Builder.OpenElement(8, "label");
Builder.AddAttribute(9, "class", "col stat-avatar");
Builder.CloseElement();
// Team Name Column
Builder.OpenElement(10, "label");
Builder.AddAttribute(11, "class", "col stat-large");
Builder.AddContent(12, $"{Team.GetName()}");
Builder.CloseElement();
// Stat Columns
Builder.OpenElement(13, "label");
Builder.AddAttribute(14, "class", "col stat");
Builder.AddContent(15, "Points");
Builder.CloseElement();
Builder.OpenElement(16, "label");
Builder.AddAttribute(17, "class", "col stat");
Builder.AddContent(18, "Kills");
Builder.CloseElement();
Builder.OpenElement(19, "label");
Builder.AddAttribute(20, "class", "col stat");
Builder.AddContent(21, "Deaths");
Builder.CloseElement();
// Level Column
Builder.OpenElement(22, "label");
Builder.AddAttribute(23, "class", "col stat-little");
Builder.CloseElement();
// Close Header Row
Builder.CloseElement();
var OrderedPlayers = GameUtils.AllPlayers.OrderByDescending(PlayerState =>
{
FPlayerGameStat Stats = GameMode.Instance.GameStats.PlayerStateStats[PlayerState];
return Stats.Damage;
}).ToList();
foreach (var PlayerState in OrderedPlayers)
{
if (PlayerState.IsValid() && PlayerState.Team == Team)
{
GeneratePlayerRow(PlayerState).Invoke(Builder);
}
}
// Close the table element
Builder.CloseElement();
};
private RenderFragment GeneratePlayerRow(PlayerState PlayerState) => Builder =>
{
if (!PlayerState.IsValid())
{
return;
}
FPlayerGameStat PlayerStats = GameMode.Instance.GameStats.PlayerStateStats[PlayerState];
// Open the row div
Builder.OpenElement(0, "div");
Builder.AddAttribute(1, "class", "row header with-px-lg with-py-sm");
// builder.AddAttribute(2, "onclick", EventCallback.Factory.Create(this, () => Log.Info("clicky")));
// Level Column
Builder.OpenElement(3, "label");
Builder.AddAttribute(4, "class", "col stat-little");
Builder.AddContent(5, ExpManager.GetLevelString(PlayerState.SteamId));
Builder.CloseElement();
// Avatar Column
Builder.OpenElement(6, "label");
Builder.AddAttribute(7, "class", "col stat-avatar");
Builder.AddAttribute(8, "style", $"background-image: url(avatar:{PlayerState.SteamId});");
if (!PlayerState.PlayerPawn.IsValid())
{
Builder.AddAttribute(26, "class", "avatar-overlay");
}
Builder.CloseElement();
// Player Name Column
Builder.OpenElement(9, "label");
Builder.AddAttribute(10, "class", "col stat-large");
Builder.AddContent(11, PlayerState.SteamName);
Builder.CloseElement();
// Stat Columns
Builder.OpenElement(12, "label");
Builder.AddAttribute(13, "class", "col stat");
Builder.AddContent(14, ((int)PlayerStats.Damage + (int)PlayerStats.Heals));
Builder.CloseElement();
Builder.OpenElement(15, "label");
Builder.AddAttribute(16, "class", "col stat");
Builder.AddContent(17, PlayerStats.Kills);
Builder.CloseElement();
Builder.OpenElement(18, "label");
Builder.AddAttribute(19, "class", "col stat");
Builder.AddContent(20, PlayerStats.Deaths);
Builder.CloseElement();
// Ping Column
Builder.OpenElement(21, "label");
Builder.AddAttribute(22, "class", "col stat-little");
Builder.AddContent(23, PlayerState.PingString);
Builder.CloseElement();
// Close the row div
Builder.CloseElement();
};
public bool IsActive { get; set; }
protected override int BuildHash()
{
return HashCode.Combine(Time.Now);
}
public override void Tick()
{
if (!PlayerState.Local.IsValid() || !GameMode.Instance.IsValid())
{
return;
}
if (Input.Pressed("Score"))
{
IsActive = Input.Pressed("Score");
SetClass("visible", true);
}
if (Input.Released("Score"))
{
SetClass("visible", false);
IsActive = false;
}
}
}