UI/World/BotOverheadDebug.razor
@namespace Sandbox.UI
@using System;
@using Sandbox;
@using Sandbox.UI;
@inherits PanelComponent
<root>
<style>
BotOverheadDebug {
width: 260px;
flex-direction: column;
pointer-events: none;
font-family: sans-serif;
}
.overhead-card {
background-color: rgba(12, 14, 18, 0.94);
border: 1px solid rgba(229, 169, 60, 0.4);
border-radius: 4px;
padding: 8px 10px;
flex-direction: column;
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.85);
}
.bot-header {
flex-direction: row;
justify-content: space-between;
align-items: center;
border-bottom: 1px solid rgba(255, 255, 255, 0.12);
padding-bottom: 4px;
margin-bottom: 6px;
}
.bot-name {
font-size: 11px;
font-weight: 800;
color: #f1f5f9;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.bot-state {
font-size: 10px;
font-weight: 700;
color: #e5a93c;
background-color: rgba(229, 169, 60, 0.15);
padding: 2px 6px;
border-radius: 3px;
}
.action-badges {
flex-direction: row;
gap: 4px;
margin-bottom: 6px;
}
.action-badge {
padding: 2px 6px;
border-radius: 3px;
background-color: rgba(255, 255, 255, 0.06);
font-size: 9px;
font-weight: 800;
color: #64748b;
}
.action-badge.atk-active {
background-color: #ef4444;
color: #ffffff;
box-shadow: 0 0 6px rgba(239, 68, 68, 0.8);
}
.action-badge.kick-active {
background-color: #f97316;
color: #ffffff;
box-shadow: 0 0 6px rgba(249, 115, 22, 0.8);
}
.action-badge.parry-active {
background-color: #3b82f6;
color: #ffffff;
box-shadow: 0 0 6px rgba(59, 130, 246, 0.8);
}
.action-badge.stagger-active {
background-color: #b91c1c;
color: #fecaca;
box-shadow: 0 0 8px rgba(185, 28, 28, 1);
}
</style>
@if ( BotFighter != null && !BotFighter.IsDead )
{
<div class="overhead-card">
<!-- Entête : Nom et État IA actif -->
<div class="bot-header">
<span class="bot-name">@(BotFighter.GameObject.Name)</span>
<span class="bot-state">@(Brain?.ActiveStateName ?? "Idle")</span>
</div>
<!-- Badges d'action réactifs en direct -->
<div class="action-badges">
<span class="action-badge @(AnimAttack ? "atk-active" : "")">ATK</span>
<span class="action-badge @(AnimKick ? "kick-active" : "")">KICK</span>
<span class="action-badge @(BotFighter.IsParrying ? "parry-active" : "")">PARADE</span>
@if ( BotFighter.Status == FighterStatus.Staggered )
{
<span class="action-badge stagger-active">RUPTURE DE GARDE</span>
}
</div>
<!-- Les 3 Jauges Modulaires (StatBar) -->
<StatBar Label="HP"
Ratio="@BotFighter.HealthRatio"
Current="@BotFighter.CurrentHealth"
Max="@BotFighter.MaxHealth"
BarType="health"
CssClass="mini" />
<StatBar Label="BAL"
Ratio="@BotFighter.BalanceRatio"
Current="@BotFighter.CurrentBalance"
Max="@BotFighter.MaxBalance"
BarType="balance"
CssClass="mini" />
<StatBar Label="ADR"
Ratio="@BotFighter.AdrenalineRatio"
Current="@BotFighter.CurrentAdrenaline"
Max="@BotFighter.MaxAdrenaline"
BarType="adrenaline"
CssClass="mini" />
</div>
}
</root>
@code {
private Fighter BotFighter;
private AiBrain Brain;
private SkinnedModelRenderer Renderer;
private bool AnimAttack => Renderer?.GetBool( "attack" ) ?? false;
private bool AnimKick => Renderer?.GetBool( "is_kicking" ) ?? false;
protected override void OnStart()
{
ResolveComponents();
}
protected override void OnUpdate()
{
if ( BotFighter == null || !BotFighter.IsValid )
{
ResolveComponents();
}
}
private void ResolveComponents()
{
// Récupère les composants du bot parent
BotFighter = Components.Get<Fighter>( FindMode.EverythingInSelfAndAncestors );
Brain = Components.Get<AiBrain>( FindMode.EverythingInSelfAndAncestors );
Renderer = Components.Get<SkinnedModelRenderer>( FindMode.EverythingInSelfAndAncestors )
?? Components.Get<SkinnedModelRenderer>( FindMode.EverythingInSelfAndDescendants );
}
protected override int BuildHash()
{
if ( BotFighter == null ) return 0;
// Arrondi entier pour ne reconstruire le DOM qu'aux changements significatifs
return HashCode.Combine(
(int)BotFighter.CurrentHealth,
(int)BotFighter.CurrentBalance,
(int)BotFighter.CurrentAdrenaline,
BotFighter.Status,
BotFighter.IsParrying,
Brain?.ActiveStateName,
AnimAttack,
AnimKick
);
}
}