UI/HUD/GladiatorScoreboard.razor
@namespace Sandbox.UI
@using System;
@using System.Collections.Generic;
@using System.Linq;
@using Sandbox;
@using Sandbox.UI;
@inherits PanelComponent
<root class="@( _isOpen ? "open" : "hidden" )">
<style>
GladiatorScoreboard {
position: absolute;
inset: 0;
justify-content: center;
align-items: center;
background-color: rgba(5, 6, 8, 0.75);
display: none;
pointer-events: none;
font-family: serif; // Typographie antique / gravée
&.open {
display: flex;
pointer-events: all;
}
&.hidden {
display: none !important;
pointer-events: none !important;
}
}
.board-window {
width: 820px;
background-color: rgba(16, 14, 12, 0.92);
border: 2px solid #8c6a2d;
border-radius: 2px;
box-shadow: 0 30px 80px rgba(0, 0, 0, 0.95), inset 0 0 40px rgba(0, 0, 0, 0.8);
padding: 32px 36px;
flex-direction: column;
background-size: cover;
}
.board-header {
flex-direction: column;
align-items: center;
border-bottom: 2px solid rgba(140, 106, 45, 0.4);
padding-bottom: 14px;
margin-bottom: 20px;
}
.board-title {
font-size: 26px;
font-weight: 900;
color: #d4af37; // Or impérial
letter-spacing: 6px;
text-transform: uppercase;
text-shadow: 0 2px 10px rgba(0, 0, 0, 0.9);
}
.board-subtitle {
font-size: 11px;
font-family: sans-serif;
font-weight: 700;
color: #8c8275;
letter-spacing: 3px;
text-transform: uppercase;
margin-top: 4px;
}
.table-head {
flex-direction: row;
padding: 8px 14px;
border-top: 1px solid rgba(140, 106, 45, 0.25);
border-bottom: 1px solid rgba(140, 106, 45, 0.25);
background-color: rgba(0, 0, 0, 0.4);
color: #9a8a78;
font-family: sans-serif;
font-size: 10px;
font-weight: 800;
letter-spacing: 2px;
text-transform: uppercase;
}
.table-row {
flex-direction: row;
align-items: center;
padding: 8px 14px;
border-bottom: 1px solid rgba(255, 255, 255, 0.03);
background-color: rgba(22, 19, 16, 0.45);
margin-top: 2px;
&.is-player {
background-color: rgba(140, 106, 45, 0.18);
border: 1px solid rgba(212, 175, 55, 0.5);
}
}
.col-rank {
width: 44px;
font-size: 14px;
font-weight: 900;
color: #d4af37;
}
.col-avatar {
width: 64px;
align-items: center;
}
.avatar-frame {
width: 34px;
height: 34px;
border: 1px solid #8c6a2d;
border-radius: 2px;
justify-content: center;
align-items: center;
overflow: hidden;
box-shadow: inset 0 0 8px rgba(0, 0, 0, 0.8);
}
.avatar-img {
width: 26px;
height: 26px;
}
.col-name {
flex-grow: 1;
font-size: 15px;
font-weight: 700;
letter-spacing: 1px;
color: #ede8df;
}
.col-health {
width: 130px;
font-family: sans-serif;
font-size: 11px;
font-weight: 800;
letter-spacing: 1.5px;
text-transform: uppercase;
}
.col-aura {
width: 90px;
text-align: right;
font-family: monospace;
font-size: 16px;
font-weight: 900;
color: #d4af37;
}
</style>
<div class="board-window" style="@GetWindowBackgroundStyle()">
<div class="board-header">
<span class="board-title">✦ CROWD FAVOR ✦</span>
<span class="board-subtitle">ARENA STANDINGS & GLORY</span>
</div>
<div class="table-head">
<span class="col-rank">#</span>
<span class="col-avatar">LEGION</span>
<span class="col-name">GLADIATOR</span>
<span class="col-health">CONDITION</span>
<span class="col-aura">AURA</span>
</div>
@for ( int i = 0; i < _rankedFighters.Count; i++ )
{
var entry = _rankedFighters[i];
<div class="table-row @(entry.IsPlayer ? "is-player" : "")">
<span class="col-rank">@(i + 1)</span>
<div class="col-avatar">
<div class="avatar-frame" style="background-color: @entry.LegionColorHex;">
@if ( !string.IsNullOrEmpty( GladiatorIconPath ) && FileSystem.Mounted.FileExists( GladiatorIconPath ) )
{
<img class="avatar-img" src="@GladiatorIconPath" />
}
</div>
</div>
<span class="col-name">@entry.Name @(entry.IsPlayer ? " (YOU)" : "")</span>
<span class="col-health" style="color: @entry.HealthColor;">@entry.HealthStatus</span>
<span class="col-aura">@($"{entry.Aura:F0}%")</span>
</div>
}
</div>
</root>
@code {
[Property, Group( "Custom Textures" ), Title( "Background Texture (Stone/Parchment)" )]
public string CustomBackgroundTexture { get; set; } = "";
[Property, Group( "Custom Textures" ), Title( "Gladiator Helmet Icon (PNG)" )]
public string GladiatorIconPath { get; set; } = "/ui/gladiator_helmet.png";
[Property] public string ToggleAction { get; set; } = "Score";
private bool _isOpen = false;
private readonly struct ScoreEntry
{
public readonly string Name;
public readonly float Aura;
public readonly string HealthStatus;
public readonly string HealthColor;
public readonly string LegionColorHex;
public readonly bool IsPlayer;
public ScoreEntry( string name, float aura, string status, string healthColor, string legionHex, bool isPlayer )
{
Name = name;
Aura = aura;
HealthStatus = status;
HealthColor = healthColor;
LegionColorHex = legionHex;
IsPlayer = isPlayer;
}
}
private List<ScoreEntry> _rankedFighters = new();
private string GetWindowBackgroundStyle()
{
if ( !string.IsNullOrEmpty( CustomBackgroundTexture ) && FileSystem.Mounted.FileExists( CustomBackgroundTexture ) )
{
return $"background-image: url('{CustomBackgroundTexture}');";
}
return "";
}
protected override void OnUpdate()
{
if ( Input.Pressed( ToggleAction ) )
{
_isOpen = !_isOpen;
if ( _isOpen )
{
UIStateManager.RequestFocus( this );
RefreshRankings();
}
else
{
UIStateManager.ReleaseFocus( this );
}
}
}
private void RefreshRankings()
{
var fighters = Scene.GetAllComponents<Fighter>().Where( f => f.IsValid && !f.IsDead ).ToList();
var director = Scene.GetAllComponents<ArenaDirector>().FirstOrDefault();
var list = new List<ScoreEntry>();
foreach ( var f in fighters )
{
var aura = f.Components.Get<FighterAura>()?.CurrentAura ?? 0f;
var identity = f.Components.Get<GladiatorIdentity>();
bool isPlayer = f.Components.Get<PlayerController>() != null;
string status = "PRISTINE";
string color = "#4ade80"; // Vert patiné
if ( f.HealthRatio < 0.35f )
{
status = "CRITICAL";
color = "#ef4444"; // Sang
}
else if ( f.HealthRatio < 0.70f )
{
status = "BLOODIED";
color = "#f59e0b"; // Ocre blessé
}
string hexColor = "#3e3529";
if ( director != null && director.LegionColors.Length > 0 && identity != null )
{
var col = director.LegionColors[identity.LegionColorIndex % director.LegionColors.Length];
hexColor = col.Hex;
}
string displayName = identity?.FighterName ?? f.GameObject.Name;
list.Add( new ScoreEntry( displayName, aura, status, color, hexColor, isPlayer ) );
}
_rankedFighters = list.OrderByDescending( x => x.Aura ).ToList();
}
protected override int BuildHash() => HashCode.Combine( _isOpen, _rankedFighters.Count, _rankedFighters.FirstOrDefault().Aura );
}