MainHud/KeyboardWarriorsHUD.razor

Razor UI component for the main HUD of the Keyboard Warriors game. It renders selection menus, leaderboards, combat UI, tooltips, status badges, logs, and manages hover/selection state, periodic leaderboard refresh, simple animations and interactions with GameManager and ShopHUD.

NetworkingExternal Download
@using Sandbox;
@using System;
@using Sandbox.Services;
@using System.Threading.Tasks;
@using Keyboard_Warriors_.Systems;
@using Keyboard_Warriors_.Models;
@using Keyboard_Warriors_.Managers;

@inherits PanelComponent

@attribute [Sandbox.UI.StyleSheet( "/ui/KeyboardWarriorsHUD.razor.scss" )]

<div class="animated-bg">
    <!-- 🚀 Swapped units to vw and vh to bypass the integer-snapping limitation -->
    <div class="bg-image-layer" 
         style="background-image: url('/@(Scene.GetAllComponents<GameManager>().FirstOrDefault()?.BackgroundImage)'); 
                transform: translate( @(_currentX)vw, @(_currentY)vh ) scale( @_currentScale ) rotate( @(_currentRotation)deg );">
    </div>
</div>

@if ( CurrentPhase == GamePhase.WeaponSelection )
{
    <div class="selection-menu-wrapper">
        <div class="selection-header">Choose Your Weapon</div>
        
        <!-- Keep your grid completely separated from the leaderboard row -->
        <div class="selection-grid">
            @foreach ( var weapon in Weapons )
            {

				var localWeapon = weapon;

                <button class="selection-button" 
                        onclick="@(() => SelectWeapon(weapon))"
                        @onmouseover="@(() => TrackWeaponHover(weapon))"
                        @onmouseout="@(ClearHoverTelemetry)">
                    <span class="btn-title text-blue">@weapon.name.ToUpper()</span>
                    <span class="btn-desc">Equip this combat tool</span>
                </button>
            }
        </div>

        <!-- LEADERBOARDS ROW: Self-contained at the very bottom -->
        <div class="leaderboard-panel-row">
            <!-- Left Panel: Personal Highs -->
            <div class="leaderboard-panel">
                <div class="leaderboard-header">
                    <span class="leaderboard-icon">👑</span>
                    <h2 class="leaderboard-title">PERSONAL ALL-TIME HIGHS</h2>
                </div>
                <div class="leaderboard-rows">
                    <div class="leaderboard-row">
                        <span class="stat-name">✨ HIGHEST PRESTIGE</span>
                        <span class="stat-value gold-glow">RANK @(GetManager()?.MaxPrestige ?? 0)</span>
                    </div>
                    <div class="leaderboard-row">
                        <span class="stat-name">💰 MOST POINTS HELD</span>
                        <span class="stat-value cyan-glow">@(GetManager()?.MaxPoints ?? 0) PTS</span>
                    </div>
                    <div class="leaderboard-row">
                        <span class="stat-name">🔥 MAX WIN STREAK</span>
                        <span class="stat-value green-glow">@(GetManager()?.MaxWinStreak ?? 0) WINS</span>
                    </div>
                    <div class="leaderboard-row">
                        <span class="stat-name">💀 MAX LOSS STREAK</span>
                        <span class="stat-value red-glow">@(GetManager()?.MaxLossStreak ?? 0) LOSSES</span>
                    </div>
                </div>
            </div>

            <!-- Right Panel: Global Cloud Rankings -->
            <div class="leaderboard-panel global-leaderboard">
                <div class="leaderboard-header">
                    <span class="leaderboard-icon">🌍</span>
                    <h2 class="leaderboard-title">GLOBAL PRESTIGE LEADERBOARD</h2>
                </div>
                <div class="leaderboard-rows scrollable-rows">
    @if ( IsReadingGlobalBoard )
    {
        <div class="loading-text">Retrieving cloud rankings...</div>
    }
    else if ( GlobalPrestigeEntries != null && GlobalPrestigeEntries.Length > 0 )
    {
        @foreach ( var entry in GlobalPrestigeEntries )
        {
            bool isMe = entry.SteamId == Sandbox.Utility.Steam.SteamId;

            <div class="leaderboard-row @(isMe ? "local-user-highlight" : "")">
                <span class="stat-name">#@entry.Rank @entry.DisplayName.ToUpper()</span>
                <span class="stat-value gold-glow">LV @entry.Value</span>
            </div>
        }
    }
    else
    {
        <!-- Displays an arcade style fallback string during local editor tests -->
        <div class="loading-text">NO GLOBAL RECORDS FOUND [STANDBY]</div>
    }
</div>
            </div>
        </div> <!-- Closes leaderboard-panel-row -->
    </div> <!-- Closes selection-menu-wrapper -->
}
else if ( CurrentPhase == GamePhase.EnchantmentSelection )
{
    <div class="selection-menu-wrapper">
        <div class="selection-header">Select An Enchantment</div>
        <div class="selection-grid">
            @foreach ( var enchantment in Enchantments )
            {
                <button class="selection-button" 
                        onclick="@(() => SelectEnchantment(enchantment))"
                        @onmouseover="@(() => TrackEnchantmentHover(enchantment))"
                        @onmouseout="@(ClearHoverTelemetry)">
                    <span class="btn-title text-gold">@GetEnchantName(enchantment).ToUpper()</span>
                    <span class="btn-desc">Solder code parameters</span>
                </button>
            }
        </div>

        <!-- DROP GLOBAL DATA DIRECTLY INTO THE ENCHANTMENT SCREEN TOO -->
        <div class="leaderboard-panel-row">
            <div class="leaderboard-panel global-leaderboard full-width-lb">
                <div class="leaderboard-header">
                    <span class="leaderboard-icon">🌍</span>
                    <h2 class="leaderboard-title">GLOBAL PRESTIGE LEADERBOARD</h2>
                </div>
                <div class="leaderboard-rows scrollable-rows">
                    @if ( IsReadingGlobalBoard )
                    {
                        <div class="loading-text">Retrieving cloud rankings...</div>
                    }
                    else if ( GlobalPrestigeEntries != null )
                    {
                        @foreach ( var entry in GlobalPrestigeEntries )
{
    // Fix: Compares the entry's SteamID to your local client profile safely
    bool isMe = entry.SteamId == Sandbox.Utility.Steam.SteamId;

    <div class="leaderboard-row @(isMe ? "local-user-highlight" : "")">
        <span class="stat-name">#@entry.Rank @entry.DisplayName.ToUpper()</span>
        <span class="stat-value gold-glow">LV @entry.Value</span>
    </div>
}
                    }
                </div>
            </div>
        </div>
    </div>
}
else if ( CurrentPhase == GamePhase.Combat )
{
    <div class="keyboard-warriors">
        <div class="combat-layout-wrapper">
            
            <div class="game-header">
                <div class="prestige-panel">
                    <button class="prestige-btn @(GetManager()?.PlayerPoints >= GetManager()?.PointsRequiredForPrestige ? "ready-to-ascend" : "")" 
                            onclick="@(() => GetManager()?.TriggerPrestige())">
                        ⚡ PRESTIGE @(GetManager()?.CurrentPrestige ?? 0)
                    </button>

                    <div class="progression-tracker">
                        <div class="wallet-display">WALLET: <span class="wallet-pts">@(GetManager()?.PlayerPoints ?? 0) PTS</span></div>
                        
                        @if ((GetManager()?.PlayerPoints ?? 0) >= (GetManager()?.PointsRequiredForPrestige ?? 1000))
                        {
                            <div class="ascend-alert">READY TO ASCEND!</div>
                        }
                        else
                        {
                            var needed = (GetManager()?.PointsRequiredForPrestige ?? 1000) - (GetManager()?.PlayerPoints ?? 0);
                            <div class="requirement-alert">REQ: <span class="req-weight">@needed more PTS to advance</span></div>
                        }
                    </div>
                </div>

                <pre class="header-ascii player-weapon-art">@GetWeaponArt(GetManager()?.LocalPlayer?.equippedWeapon?.name)</pre>
                
                <div class="header-title-center-zone">
                    <pre class="header-ascii">
                 _                         _ __    __                _                
  /\ /\___ _   _| |__   ___   __ _ _ __ __| / / /\ \ \__ _ _ __ _ __(_) ___  _ __ ___ 
 / //_/ _ \ | | | '_ \ / _ \ / _` | '__/ _` \ \/  \/ / _` | '__| '__| |/ _ \| '__/ __|
/ __ \  __/ |_| | |_) | (_) | (_| | | | (_| |\  /\  / (_| | |  | |  | | (_) | |  \__ \
\/  \/\___|\__, |_.__/ \___/ \__,_|_|  \__,_| \/  \/ \__,_|_|  |_|  |_|\___/|_|  |___/
           |___/
                    </pre>
                </div>

                <pre class="header-ascii ai-weapon-art">@GetWeaponArt(GetManager()?.AIEnemy?.equippedWeapon?.name)</pre>
				<button class="shop-trigger-btn" onclick="@OpenShopMenu">
				🛒 OPEN SHOP
				</button>
            </div>

            <div class="stats-container">
				<!-- ========================================== -->
				<!-- 👤 PLAYER FIGHTER CARD                     -->
				<!-- ========================================== -->
				<div class="fighter-card player">
					<img class="avatar-placeholder player-side" src="avatar:@(Connection.Local?.SteamId ?? 0)" />
					<div class="fighter-info">
						<span class="name player-name">@(Connection.Local?.Name?.ToUpper() ?? "PLAYER")</span>
            
						<div class="stat-bar-row">
							<span class="stat-lbl">HP</span>
							<div class="hp-bar" style="width: @((GetManager()?.LocalPlayer?.hp * 100.0f / GetManager()?.LocalPlayer?.maxHp) ?? 0)%;"></div>
							<span class="stat-val">@(GetManager()?.LocalPlayer?.hp ?? 0) / @(GetManager()?.LocalPlayer?.maxHp ?? 1)</span>
						</div>
            
						<div class="stat-bar-row">
							<span class="stat-lbl">ARMOR</span>
							<div class="armor-bar" style="width: @((GetManager()?.LocalPlayer?.defense * 100.0f / GetManager()?.LocalPlayer?.maxDefense) ?? 0)%;"></div>
							<span class="stat-val">@(GetManager()?.LocalPlayer?.defense ?? 0) / @(GetManager()?.LocalPlayer?.maxDefense ?? 1)</span>
						</div>
            
						<div class="stat-bar-row">
							<span class="stat-lbl">MP</span>
							<div class="bar"><div class="mp-bar" style="width: @(GetManager()?.LocalPlayer?.magic ?? 0)%;"></div></div>
							<span class="stat-val">@(GetManager()?.LocalPlayer?.magic ?? 0) / 100</span>
						</div>

						<!-- 📊 PLAYER ACTIVE STATUS MODIFIER BAR -->
						<div class="status-effect-bar">
						 @{
							 var pManager = GetManager();
							 var playerObj = pManager?.LocalPlayer;
							 int pSessionStreak = CombatSession.WinStreak;
	 
							 // 🌐 NEW DEFIANCE MATRIX VARIABLES (Update 1.5.8)
							 int currentPrestige = pManager != null ? pManager.CurrentPrestige : 0;
							 bool holdsBackupToken = pManager != null && !pManager.HasDefiedDeathThisMatch;
						 }
						 @if ( playerObj != null )
						 {
							 <div class="status-badge overclock @(playerObj.magic >= 75 ? "is-active" : "")">⚡ OVERCLOCK</div>
							 <div class="status-badge momentum @(pSessionStreak >= 3 ? "is-active" : "")">🔥 STREAK @pSessionStreak</div>
							 <div class="status-badge poison @(playerObj.cursed ? "is-active" : "")">🤢 POISONED</div>
							 <div class="status-badge event-buff @(playerObj.eventAttackBuff > 0 ? "is-active" : "")">🎲 +@playerObj.eventAttackBuff ATK</div>
							 <div class="status-badge weight-debuff @(playerObj.eventAttackBuff < 0 ? "is-active" : "")">📦 DEBUFF DRAG</div>
	 
							 <!-- 🔒 PRESTIGE 3 TRACKER LAYER -->
							 @if ( currentPrestige >= 3 )
							 {
								 <!-- Glows radioactive emerald green when online, or fades to dark gray when consumed -->
								 <div class="status-badge defiance-core @(holdsBackupToken ? "is-active" : "")">
									 🔄 DEFIANCE @(holdsBackupToken ? "READY" : "OFFLINE")
								 </div>
							 }

							 <!-- 🔒 PRESTIGE 4 REQUIREMENT: Passive Counter-Strike Tracker -->
							@if ( currentPrestige >= 4 )
							{
								<div class="status-badge malware-core is-active">👾 COUNTER-STRIKE</div>
							}

							<!-- 🔒 PRESTIGE 6 REQUIREMENT: Kinetic Rebound Shield Tracker -->
							@if ( currentPrestige >= 6 )
							{
								<div class="status-badge rebound-core is-active">⚡ REBOUND ACTIVE</div>
							}

							@if ( currentPrestige >= 9 )
							{
								<div class="status-badge leak-core is-active">🔮 MAGIC STEAL</div>
							}
						 }
						</div>
					</div>
				</div>

				<div class="vs-badge">VS</div>

				<!-- ========================================== -->
				<!-- 👹 AI OVERLORD FIGHTER CARD                 -->
				<!-- ========================================== -->
				<div class="fighter-card ai">
					<div class="fighter-info">
						<span class="name ai-name">@(GetManager()?.AIEnemy?.name ?? "AI OVERLORD")</span>
            
						<div class="stat-bar-row">
							<span class="stat-lbl">HP</span>
							<div class="hp-bar" style="width: @((GetManager()?.AIEnemy?.hp * 100.0f / GetManager()?.AIEnemy?.maxHp) ?? 0)%;"></div>
							<span class="stat-val">@(GetManager()?.AIEnemy?.hp ?? 0) / @(GetManager()?.AIEnemy?.maxHp ?? 1)</span>
						</div>
            
						<div class="stat-bar-row">
							<span class="stat-lbl">ARMOR</span>
							<div class="armor-bar" style="width: @((GetManager()?.AIEnemy?.defense * 100.0f / GetManager()?.AIEnemy?.maxDefense) ?? 0)%;"></div>
							<span class="stat-val">@(GetManager()?.AIEnemy?.defense ?? 0) / @(GetManager()?.AIEnemy?.maxDefense ?? 1)</span>
						</div>
            
						<div class="stat-bar-row">
							<span class="stat-lbl">MP</span>
							<div class="bar"><div class="mp-bar" style="width: @(GetManager()?.AIEnemy?.magic ?? 0)%;"></div></div>
							<span class="stat-val">@(GetManager()?.AIEnemy?.magic ?? 0) / 100</span>
						</div>

						<!-- 📊 AI ACTIVE STATUS MODIFIER BAR -->
						<div class="status-effect-bar">
						 @{
										 var aManager = GetManager();
										 var aiObj = aManager?.AIEnemy;
										 int aiSessionStreak = CombatSession.LossStreak;
										 int playerPrestige = aManager != null ? aManager.CurrentPrestige : 0;
										 string currentStance = aManager?.AICurrentStance ?? "SECURE CONNECTION";
										 int hiddenAIAction = aManager?.AICurrentMoveIndex ?? 0;
										 bool isBreachSuccessful = aManager?.IsAIActionIntercepted ?? false;

										 // 🌐 AI DEFIANCE VARIABLE HANDSHAKE (Update 1.5.8)
										 bool holdsAIBackupToken = aManager != null && !aManager.HasAIDefiedDeathThisMatch;
						 }
						 @if ( aiObj != null )
						 {
										 <div class="status-badge overclock @(aiObj.magic >= 75 ? "is-active" : "")">⚡ OVERCLOCK</div>
										 <div class="status-badge momentum @(aiSessionStreak >= 3 ? "is-active" : "")">🔥 STREAK @aiSessionStreak</div>
										 <div class="status-badge poison @(aiObj.cursed ? "is-active" : "")">🤢 POISONED</div>
										 <div class="status-badge event-buff @(aiObj.eventAttackBuff > 0 ? "is-active" : "")">🎲 +@aiObj.eventAttackBuff ATK</div>
				 
										 <!-- 🔒 AI PRESTIGE 3 TRACKER LAYER -->
										 @if ( playerPrestige >= 3 )
										 {
											 <!-- Reuses your exact defiance-core SCSS classes to keep your layout dimensions compact -->
											 <div class="status-badge defiance-core @(holdsAIBackupToken ? "is-active" : "")">
												 🔄 MAINFRAME @(holdsAIBackupToken ? "READY" : "OFFLINE")
											 </div>
										 }

										 @if ( playerPrestige >= 4 )
										 {
											 <div class="status-badge malware-core is-active">👾 MALWARE DETECT</div>
										 }

										 <!-- 🔒 AI PRESTIGE 6 REQUIREMENT: Kinetic Rebound Shield Tracker -->
										 @if ( playerPrestige >= 6 )
										 {
											 <div class="status-badge rebound-core is-active">⚡ REBOUND FIELD</div>
										 }

										 @if ( playerPrestige >= 1 )
										 {
											 <div class="scanner-text-line">
												 <span class="blink-dot">●</span> STANCE: <span class="stance-value">@currentStance</span>
 
												 <!-- 🚀 PRESTIGE 5 REWARD: Inline File Sniffer -->
												 @if ( playerPrestige >= 5 )
												 {
													 @if ( isBreachSuccessful )
													 {
														 <span class="action-value"> // PACKET: @(hiddenAIAction == 1 ? "ATTACK.EXE" :
																		  hiddenAIAction == 2 ? "BLOCK.SYS" :
																		  hiddenAIAction == 3 ? "REPAIR.BAT" :
																		  hiddenAIAction == 4 ? "SPECIAL.EXE" :
																		  hiddenAIAction == 5 ? "MANA.SYS" :
																		  hiddenAIAction == 6 ? "FORTIFY.DRV" :
																		  hiddenAIAction == 7 ? "REGEN.MSI" :
																		  hiddenAIAction == 8 ? "PURGE.EXE" : "LOADING...")</span>
													 }
													 else
													 {
														 <span class="action-value blocked"> // PACKET: [ENCRYPTED]</span>
													 }
												 }
											 </div>
										 }
						 }
						</div>
					</div>
					<div class="avatar-placeholder ai-side">👹</div>
				</div>
			</div>

            <div class="middle-layout-row">
                <div class="side-spell-panel player-spells">
                    <span class="panel-title text-blue">PLAYER LOADOUT</span>
                    <div class="weapon-status" style="border-bottom: 1px dashed #00d2ff; padding-bottom: 5px; margin-bottom: 10px; font-size: 11px; color: white;">
                        ⚔️ WEAPON: <span class="text-yellow" style="font-weight: bold;">@(GetManager()?.LocalPlayer?.equippedWeapon?.name ?? "None")</span>
                        <div>✨ ENCHANTMENT: <span class="highlight-red" style="font-weight: bold;">@(SelectedEnchantment ?? "None")</span></div>
                    </div>
                    <div class="spell-item offensive-glow">
                        <span class="spell-emoji">@GetSpellEmoji(GetManager()?.LocalPlayer?.offensiveSpells?.FirstOrDefault())</span> 
                        <span>@(GetManager()?.LocalPlayer?.offensiveSpells?.FirstOrDefault() ?? "Loading...")</span>
                    </div>
                    <div class="spell-item defensive-glow">
                        <span class="spell-emoji">@GetSpellEmoji(GetManager()?.LocalPlayer?.defensiveSpells?.FirstOrDefault())</span> 
                        <span>@(GetManager()?.LocalPlayer?.defensiveSpells?.FirstOrDefault() ?? "Loading...")</span>
                    </div>
                    <div class="spell-item healing-glow">
                        <span class="spell-emoji">@GetSpellEmoji(GetManager()?.LocalPlayer?.healingSpells?.FirstOrDefault())</span> 
                        <span>@(GetManager()?.LocalPlayer?.healingSpells?.FirstOrDefault() ?? "Loading...")</span>
                    </div>
                    <div class="spell-item ultimate-glow">
                        <span class="spell-emoji">@GetSpellEmoji(GetManager()?.LocalPlayer?.specialSpells?.FirstOrDefault())</span> 
                        <span>@(GetManager()?.LocalPlayer?.specialSpells?.FirstOrDefault() ?? "Loading...")</span>
                    </div>
                </div>

                <div class="log-container">
					<div class="log-header">--- COMBAT LOG ---</div>
					<div class="log-scroll-area" @ref="MyLogPanel">
						<div class="log-entry system text-yellow">Welcome to Keyboard Warriors!</div>
						@if ( GetManager()?.CombatLogs != null )
						{
							@foreach ( var log in GetManager().CombatLogs )
							{
								<!-- Dynamically combines your base item styles with your custom color class tags -->
								<div class="log-entry @log.ColorClass @log.AnimationClass">@log.Message</div>
							}
						}
						else
						{
							<div class="log-entry system text-muted">Synchronizing with CombatLogs...</div>
						}
					</div>
				</div>

                <div class="side-spell-panel ai-spells">
                    <span class="panel-title text-red">AI LOADOUT</span>
                    <div class="weapon-status" style="border-bottom: 1px dashed #ff5500; padding-bottom: 5px; margin-bottom: 10px; font-size: 11px; color: white;">
                        ⚔️ WEAPON: <span class="text-yellow" style="font-weight: bold;">@(GetManager()?.AIEnemy?.equippedWeapon?.name ?? "None")</span>
                        <div>ENCHANTMENT: <span class="highlight-red" style="font-weight: bold; text-shadow: 0 0 8px #ff4444;">@(GetManager()?.AIEnemy.ActiveEnchantment.ToString())</span></div>
                    </div>
                    <div class="spell-item offensive-glow">
                        <span class="spell-emoji">@GetSpellEmoji(GetManager()?.AIEnemy?.offensiveSpells?.FirstOrDefault())</span> 
                        <span>@(GetManager()?.AIEnemy?.offensiveSpells?.FirstOrDefault() ?? "Loading...")</span>
                    </div>
                    <div class="spell-item defensive-glow">
                        <span class="spell-emoji">@GetSpellEmoji(GetManager()?.AIEnemy?.defensiveSpells?.FirstOrDefault())</span> 
                        <span>@(GetManager()?.AIEnemy?.defensiveSpells?.FirstOrDefault() ?? "Loading...")</span>
                    </div>
                    <div class="spell-item healing-glow">
                        <span class="spell-emoji">@GetSpellEmoji(GetManager()?.AIEnemy?.healingSpells?.FirstOrDefault())</span> 
                        <span>@(GetManager()?.AIEnemy?.healingSpells?.FirstOrDefault() ?? "Loading...")</span>
                    </div>
                    <div class="spell-item ultimate-glow">
                        <span class="spell-emoji">@GetSpellEmoji(GetManager()?.AIEnemy?.specialSpells?.FirstOrDefault())</span> 
                        <span>@(GetManager()?.AIEnemy?.specialSpells?.FirstOrDefault() ?? "Loading...")</span>
                    </div>
                </div>
            </div>

            <div class="actions-container">
                @if ( IsGameOver )
                {
                    <button class="btn rematch" onclick="@OnRematchPressed">Rematch</button>
                }
                else
                {
                    <div class="button-row">
                        <button class="combat-btn border-red" onclick="@(() => OnActionPressed("Attack"))">
                            <span class="btn-title text-red">ATTACK</span>
                            <span class="btn-desc">Deal damage to enemy</span>
                        </button>
                        <button class="combat-btn border-blue" onclick="@(() => OnActionPressed("Defend"))">
                            <span class="btn-title text-blue">DEFEND</span>
                            <span class="btn-desc">Reduce incoming damage</span>
                        </button>
                        <button class="combat-btn border-green" onclick="@(() => OnActionPressed("Heal"))">
                            <span class="btn-title text-green">HEAL</span>
                            <span class="btn-desc">Restore health</span>
                        </button>
                        <button class="combat-btn border-yellow" onclick="@(() => OnActionPressed("Special"))">
                            <span class="btn-title text-yellow">SPECIAL</span>
                            <span class="btn-desc">Use weapon special</span>
                        </button>
                    </div>

                    <div class="button-row">
                        <button class="combat-btn border-purple" onclick="@(() => OnActionPressed("Offensive Spell"))">
                            <span class="btn-title text-purple">OFFENSIVE SPELL</span>
                            <span class="btn-desc">Cast offensive spell</span>
                        </button>
                        <button class="combat-btn border-blue" onclick="@(() => OnActionPressed("Defensive Spell"))">
                            <span class="btn-title text-blue">DEFENSIVE SPELL</span>
                            <span class="btn-desc">Cast defensive spell</span>
                        </button>
                        <button class="combat-btn border-green" onclick="@(() => OnActionPressed("Healing Spell"))">
                            <span class="btn-title text-green">HEALING SPELL</span>
                            <span class="btn-desc">Cast healing spell</span>
                        </button>
                        <button class="combat-btn border-orange" onclick="@(() => OnActionPressed("Ultimate"))">
                            <span class="btn-title text-orange">ULTIMATE SPELL</span>
                            <span class="btn-desc">Cast ultimate spell</span>
                        </button>
                    </div>
                }
            </div>

            <div class="footer-hint">Press ESC to exit  |  Good luck, warrior!</div>
        </div>
    </div>
}

<ShopHUD @ref="shopRef" />

@if ( GetManager() != null && GetManager().IsChoiceEventActive && GetManager().ActiveChoiceEvent != null )
{
    var currentEvent = GetManager().ActiveChoiceEvent;

    <div class="event-modal-backdrop">
        <div class="event-choice-box">
            <h2 class="event-title text-red">@currentEvent.Title</h2>
			@if ( currentEvent.HasTimeLimit )
            {
                // Calculate current remaining percentage fraction (0.0 to 100.0)
                float pct = (currentEvent.TimeRemaining / currentEvent.TotalDuration) * 100f;
                
                <div class="timer-bar-wrapper">
                    <!-- Binding the width percentage directly into the style string updates it smoothly -->
                    <div class="timer-bar-fill" style="width: @(pct)%;"></div>
                    <span class="timer-text-overlay">FIREWALL CLOSING: @(((int)currentEvent.TimeRemaining) + 1)s</span>
                </div>
            }
            <p class="event-prompt">@currentEvent.PromptText</p>
            
            <!-- The flex container shifts layout automatically based on button counts -->
            <div class="event-button-row">
                @for ( int i = 0; i < currentEvent.Options.Count; i++ )
                {
                    // Local variable capture is critical inside Razor loop callbacks!
                    int buttonIndex = i; 
                    var currentButton = currentEvent.Options[i];

                    <button class="choice-option-btn dynamic-color-@buttonIndex" 
                            onclick="@(() => GetManager().ResolveChoice(buttonIndex))">
                        @currentButton.ButtonText
                    </button>
                }
            </div>
        </div>
    </div>
}

@if ( ActiveHoverType != HoverType.None && Panel != null && (CurrentPhase == GamePhase.WeaponSelection || CurrentPhase == GamePhase.EnchantmentSelection) )
{
    <div class="telemetry-tooltip-hud" style="left: @(Panel.MousePosition.x + 20)px; top: @(Panel.MousePosition.y + 15)px;">
        @if ( ActiveHoverType == HoverType.Weapon && SelectedHoverWeapon != null )
		{
			<div class="tooltip-header text-blue">📊 WEAPON CORE TELEMETRY</div>
			
			<!-- 🚀 THE DEFINITIVE DATA SYNC: Fallback properties check! -->
			<!-- If enchantPower is 0 on the base template, we check your alternative damage fields automatically -->
			@if ( SelectedHoverWeapon.enchantPower > 0 )
			{
				<div class="tooltip-row"><span class="label">💥 POWER RATING:</span> <span class="val">@SelectedHoverWeapon.enchantPower</span></div>
			}
			else
			{
				// 🛠️ Adjust this keyword to match your weapon class property name if needed (e.g., baseDamage, damage, power, attack)
				<div class="tooltip-row"><span class="label">💥 BASE DAMAGE:</span> <span class="val">@SelectedHoverWeapon.damageBonus</span></div>
			}
			
			<div class="tooltip-row"><span class="label">❤️ HEAL BONUS:</span> <span class="val">@SelectedHoverWeapon.healBonus</span></div>
			<div class="tooltip-divider"></div>
			<div class="tooltip-sub">Asset: @SelectedHoverWeapon.name</div>
		}
		else if ( ActiveHoverType == HoverType.Enchantment )
		{
			<div class="tooltip-header text-gold">✨ CASING ANOMALY PROFILE</div>
			<div class="tooltip-sub">@GetEnchantName(HoveredEnchantType.ToString())</div>
			<div class="tooltip-body">@GetEnchantDescription(HoveredEnchantType.ToString())</div>
			<div class="tooltip-divider"></div>
			<div class="tooltip-row"><span class="label">⚡ TRIGGER ACCURACY:</span> <span class="val text-gold">@GetEnchantTriggerChance(HoveredEnchantType.ToString())</span></div>
		}
    </div>
}

@if ( GetManager() != null && GetManager().IsPerkEventActive && GetManager().ActivePerkChoices != null && GetManager().ActivePerkChoices.Count > 0 )
{
    var currentPerkList = GetManager().ActivePerkChoices;

    <div class="event-modal-backdrop">
        <!-- Expanded width slightly to 700px to give your columns maximum elbow room -->
        <div class="event-choice-box" style="width: 700px; max-width: 90vw; height: auto; display: flex; flex-direction: column; padding: 24px; box-sizing: border-box;">
            
            <h2 class="event-title text-red" style="margin-bottom: 8px;">⚠️ MATRIX CORE UPGRADE</h2>
            <p class="event-prompt" style="margin-bottom: 24px;">Select an offensive parameter injection routine. Modifications will scale permanently for both combatants.</p>
            
            <!-- 🚀 THE COLUMNS FIX: We force the container wrapper to lay out horizontally across the card screen -->
            <div class="perk-columns-container" style="display: flex; flex-direction: row; gap: 16px; width: 100%; justify-content: space-between; box-sizing: border-box;">
                @for ( int i = 0; i < currentPerkList.Count; i++ )
                {
                    int buttonIndex = i; 
                    var currentPerk = currentPerkList[i];

                    <!-- 🚀 COLUMN CARDS: 'flex: 1;' forces s&box to make every option card equal width.
                         We use inline styling rules to force clean blocks and prevent font crunching. -->
                    <div class="choice-option-btn dynamic-color-@buttonIndex" 
                         style="display: flex; flex-direction: column; align-items: stretch; text-align: center; padding: 18px 14px; min-height: 180px; gap: 8px; flex: 1; min-width: 0; pointer-events: all; cursor: pointer; box-sizing: border-box; background-color: #14141a;"
                         onclick="@(() => GetManager().ResolvePerkChoice(buttonIndex))">
                        
                        <!-- Header Line: Emoji Icon + Title -->
                        <div style="font-weight: bold; font-size: 14px; display: flex; align-items: center; justify-content: center; gap: 6px; width: 100%; text-transform: uppercase; border-bottom: 1px solid rgba(255,255,255,0.08); padding-bottom: 6px;">
                            <span>@currentPerk.Icon</span>
                            <span class="text-blue">@currentPerk.Title</span>
                        </div>
                        
                        <!-- Core Description Text (Perfect Paragraph Fluidity) -->
                        <div style="font-size: 11px; color: #b5b5c5; line-height: 1.4; width: 100%; white-space: normal !important; word-wrap: break-word !important; text-align: left; flex-grow: 1;">
                            @currentPerk.Description
                        </div>
                        
                        <!-- Penalty Tag Info Row (Pushed down dynamically) -->
                        <div style="font-size: 10px; font-weight: bold; color: #ff0055; width: 100%; white-space: normal; text-align: left; border-top: 1px solid rgba(255,255,255,0.05); padding-top: 4px; margin-top: auto;">
                            @currentPerk.DrawbackText
                        </div>
                        
                    </div>
                }
            </div>
            
        </div>
    </div>
}

@code {

	private enum HoverType { None, Weapon, Enchantment }
    private HoverType ActiveHoverType = HoverType.None;
    
    private Weapon SelectedHoverWeapon = null;
    private Weapon.EnchantmentType HoveredEnchantType = Weapon.EnchantmentType.None;

    private void TrackWeaponHover( Weapon targetWeapon )
    {
        // 🚀 THE PHASE GATE: Only allow the HUD to show if you are actively choosing a weapon!
        if ( CurrentPhase != GamePhase.WeaponSelection ) 
        {
            ClearHoverTelemetry();
            return;
        }

        SelectedHoverWeapon = targetWeapon;
        ActiveHoverType = HoverType.Weapon;
        StateHasChanged();
    }

    private void TrackEnchantmentHover( string enchantmentString )
    {
        // 🚀 THE PHASE GATE: Only allow the HUD to show if you are actively choosing an enchantment!
        if ( CurrentPhase != GamePhase.EnchantmentSelection ) 
        {
            ClearHoverTelemetry();
            return;
        }

        ActiveHoverType = HoverType.Enchantment;

        if ( Enum.TryParse<Weapon.EnchantmentType>( enchantmentString, true, out var parsedType ) )
        {
            HoveredEnchantType = parsedType;
        }
        else
        {
            HoveredEnchantType = Weapon.EnchantmentType.None;
        }

        StateHasChanged();
    }

    private void ClearHoverTelemetry()
    {
        ActiveHoverType = HoverType.None;
        SelectedHoverWeapon = null;
        HoveredEnchantType = Weapon.EnchantmentType.None;
        StateHasChanged();
    }

    // 🕊️ 1. Dynamic Casing Names
    private string GetEnchantName( string enchantmentString )
    {
        if ( !Enum.TryParse<Weapon.EnchantmentType>( enchantmentString, true, out var type ) )
            return "Null Kernel";

        return type switch
        {
            Weapon.EnchantmentType.Fire        => "Fire",
            Weapon.EnchantmentType.Poison      => "Poison Coating",
            Weapon.EnchantmentType.Ice         => "Ice",
            Weapon.EnchantmentType.Sparks      => "Sparks",
            Weapon.EnchantmentType.DamageHP    => "DamageHP",
            Weapon.EnchantmentType.Stun        => "Electrical Stun",
            Weapon.EnchantmentType.LifeSteal   => "Lifesteal Core",
            Weapon.EnchantmentType.ShieldBreak => "ShieldBreak",
            Weapon.EnchantmentType.None        => "Null Kernel",
            
            // 🩸 RESTORED: Given its true specialized name!
            Weapon.EnchantmentType.Bleed       => "Bleed Matrix",
            _                                  => "Null Kernel"
        };
    }

    private string GetEnchantDescription( string enchantmentString )
    {
        if ( !Enum.TryParse<Weapon.EnchantmentType>( enchantmentString, true, out var type ) )
            return "Standard framework reinforcement module.";

        return type switch
        {
            Weapon.EnchantmentType.Fire        => "Ignites target for thermal damage. Deals 1.5x damage straight to armor shields.",
            Weapon.EnchantmentType.Poison      => "Injects permanent background toxin logic, dealing ticking damage over time.",
            Weapon.EnchantmentType.Ice         => "Freezes the core matrix, causing the AI's active defensive wall loops to fail completely.",
            Weapon.EnchantmentType.Sparks      => "Static current arcs backwards to recharge your local health framework grid.",
            Weapon.EnchantmentType.DamageHP    => "True kinetic puncture that completely bypasses armor grids to strike physical health.",
            Weapon.EnchantmentType.Stun        => "Overloads target processor threads, force-locking their client actions next turn.",
            Weapon.EnchantmentType.LifeSteal   => "Siphons health packets from weapon strikes, restoring your local grid stability.",
            Weapon.EnchantmentType.ShieldBreak => "Catastrophic barrier rupture subroutine that triggers massive damage against active blocks.",
            
            // 🩸 RESTORED: Direct code-logic description for bleeding wounds!
            Weapon.EnchantmentType.Bleed       => "Ruptures underlying system components on contact. Causes an ongoing hemorrhage loop that tears away physical health out-of-turn.",
            
            Weapon.EnchantmentType.None        => "Standard framework reinforcement module. No specialized status modifiers active.",
            _                                  => "Standard framework reinforcement module."
        };
    }

    private string GetEnchantTriggerChance( string enchantmentString )
    {
        if ( !Enum.TryParse<Weapon.EnchantmentType>( enchantmentString, true, out var type ) )
            return "0%";

        var manager = Sandbox.Game.ActiveScene.Get<GameManager>();
        var currentWeaponContext = SelectedHoverWeapon ?? manager?.ChosenWeapon;
        int powerValue = currentWeaponContext != null ? currentWeaponContext.enchantPower : 30;

        return type switch
        {
            Weapon.EnchantmentType.Fire        => "80%",
            Weapon.EnchantmentType.Poison      => "100%",
            Weapon.EnchantmentType.Ice         => manager?.LocalPlayer?.magic >= 75 ? "40%" : "25%",
            Weapon.EnchantmentType.Sparks      => "20%",
            Weapon.EnchantmentType.DamageHP    => "60%",
            Weapon.EnchantmentType.Stun        => "25%",
            Weapon.EnchantmentType.LifeSteal   => "100%",
            Weapon.EnchantmentType.ShieldBreak => (manager != null && manager.WinStreak >= 3) ? $"{powerValue + 20}%" : $"{powerValue}%",
            
            // 🩸 RESTORED: Scaled perfectly to match your weapon power rating criteria!
            Weapon.EnchantmentType.Bleed       => "80%",
            
            Weapon.EnchantmentType.None        => "0%",
            _                                  => "0%"
        };
    }

	private float _time = 0f;
	private float _currentX = 0f;
	private float _currentY = 0f;
	private float _currentScale = 1.05f;
	private float _currentRotation = 0f;

	protected override void OnTreeBuilt()
    {
        base.OnTreeBuilt();
        
        // 🚀 ACTIVATE RUNTIME OVERRIDE
        // 1. Ensure the mouse cursor is configured to display normally
        Mouse.Visibility = MouseVisibility.Auto; 
        
        // 2. Pass your registered Project Settings string key name
        Mouse.CursorType = "KW Cursor"; 
    }

	protected override void OnUpdate()
	{
		_time += RealTime.Delta;

		// 1. Calculate ultra-slow paths using viewport percentages instead of raw pixels
		// 🐢 Shifting by 1.5vw means 1.5% of the total screen width.
		float desiredX = MathF.Sin( _time * 0.08f ) * 1.5f; 
		float desiredY = MathF.Cos( _time * 0.06f ) * 1.0f; 
		float desiredScale = 1.06f + (MathF.Sin( _time * 0.04f ) * 0.015f);
		float desiredRotation = MathF.Sin( _time * 0.02f ) * 0.8f;

		// 2. Keep the smooth Lerp dampening intact
		_currentX = MathX.Lerp( _currentX, desiredX, RealTime.Delta * 1.2f );
		_currentY = MathX.Lerp( _currentY, desiredY, RealTime.Delta * 1.2f );
		_currentScale = MathX.Lerp( _currentScale, desiredScale, RealTime.Delta * 1.2f );
		_currentRotation = MathX.Lerp( _currentRotation, desiredRotation, RealTime.Delta * 1.2f );

		StateHasChanged();
	}

	private Sandbox.Services.Leaderboards.Board2.Entry[] GlobalPrestigeEntries { get; set; }
	private System.Threading.CancellationTokenSource leaderboardLoopToken;

	public KeyboardWarriorsHUD()
    {
        var manager = GetManager();
        if (manager != null)
        {
            // Subscribe using a delegate
            manager.OnStateChanged += HandleStateChanged;
        }
    }

	private ShopHUD shopRef { get; set; }

    // Cloud Leaderboard Properties
    private bool IsReadingGlobalBoard { get; set; } = true;
    // FIXED: The old duplicate IReadOnlyList GlobalPrestigeEntries variable was removed from here!

    private void OpenShopMenu()
    {
        shopRef?.Open();
    }

    private void HandleStateChanged()
    {
        StateHasChanged();
    }

    public enum GamePhase { WeaponSelection, EnchantmentSelection, Combat }
    public GamePhase CurrentPhase { get; set; } = GamePhase.WeaponSelection;

    public Weapon SelectedWeapon { get; set; }
    public string SelectedEnchantment { get; set; }

    // FIX: Kept the name 'Weapons' exactly as your markup expects, while using the Weapon object type
    private List<Weapon> Weapons = new() 
    { 
        new Weapon("Sword", 5, 1.0f, 5),
        new Weapon("Axe", 10, 0.8f, 2),
        new Weapon("Dagger", 3, 1.2f, 4),
        new Weapon("Shield", 0, 1.5f, 8)
    };

    private List<string> Enchantments = new() { "None", "Fire", "Poison", "Ice", "Sparks", "DamageHP", "Stun", "Bleed", "LifeSteal", "ShieldBreak" };

    private bool IsGameOver => GetManager() != null && !GetManager().IsBattleActive;

	public static string GetSpellEmoji(string spellName)
	{
		if (string.IsNullOrEmpty(spellName)) return "🔮";
		string lower = spellName.ToLower();

		if (lower.Contains("fireball")) return "🔥";
		if (lower.Contains("lightning")) return "⚡";
		if (lower.Contains("poison bolt")) return "🤢";
		
		if (lower.Contains("shield")) return "🛡️";
		if (lower.Contains("barrier")) return "🚧";
		if (lower.Contains("reflect")) return "🪞";
		if (lower.Contains("magic wall")) return "🧱";
		if (lower.Contains("fortify")) return "🏰";

		if (lower.Contains("heal")) return "💚";
		if (lower.Contains("regen")) return "⏳";
		if (lower.Contains("cleanse")) return "🧼";
		if (lower.Contains("divine light")) return "✨";
		if (lower.Contains("recovery pulse")) return "💗";

		if (lower.Contains("system overdrive")) return "🚀";
		if (lower.Contains("emp blast")) return "💥";
		if (lower.Contains("data corruption")) return "👾";

		return "🔮";
	}

    private GameManager GetManager()
    {
        if ( GameManager.Instance != null ) return GameManager.Instance;
        return Scene?.Components?.GetAll<GameManager>().FirstOrDefault();
    }

    protected override void OnStart()
    {
        // Check if the component's main UI Panel canvas surface exists
        if ( Panel != null )
        {
            Panel.AcceptsFocus = true;
            Panel.Style.PointerEvents = Sandbox.UI.PointerEvents.All;

            // FIXED: Spawns your child shop directly onto the main 'Panel' canvas surface
            shopRef = Panel.AddChild<ShopHUD>();
        }

        // 2. Initialize the token system for the silent background loop
        leaderboardLoopToken = new System.Threading.CancellationTokenSource();

        // 3. Start the repeating 60-second auto-refresh thread
        _ = StartLeaderboardAutoRefreshLoop( leaderboardLoopToken.Token );

        StartNewGame();
    }

    private async System.Threading.Tasks.Task FetchGlobalLeaderboardData()
{
    try
    {
        IsReadingGlobalBoard = true;
        StateHasChanged();

        // Pulls the board based on your game stat key
        var cloudBoard = Sandbox.Services.Leaderboards.GetFromStat( "highest_prestige" );
        
        // 🛡️ CRITICAL SAFETY FIX: If testing locally or game is unregistered, cloudBoard is null
        if ( cloudBoard != null )
        {
            cloudBoard.MaxEntries = 10; // Cap at Top 10 to keep screens scannable
            await cloudBoard.Refresh();

            GlobalPrestigeEntries = cloudBoard.Entries;
        }
        else
        {
            // Create a fake placeholder array for local testing if the cloud is unreachable
            GlobalPrestigeEntries = System.Array.Empty<Sandbox.Services.Leaderboards.Board2.Entry>();
        }
    }
    catch ( System.Exception ex )
    {
        Log.Error( $"Cloud Leaderboard failed to load: {ex.Message}" );
    }
    finally
    {
        IsReadingGlobalBoard = false;
        StateHasChanged(); // Force the HTML tree to draw the fresh layout
    }
}

    private async System.Threading.Tasks.Task StartLeaderboardAutoRefreshLoop( System.Threading.CancellationToken token )
    {
        while ( !token.IsCancellationRequested )
        {
            try
            {
                // Wait silently in the background for 60 seconds without dropping your frame rate
                await System.Threading.Tasks.Task.Delay( TimeSpan.FromSeconds( 60 ), token );
                
                if ( token.IsCancellationRequested ) break;

                Log.Info( "🔄 Silent Auto-Refresh: Fetching live cloud leaderboard data..." );
                
                // Calls your method to download fresh rows
                await FetchGlobalLeaderboardData();
            }
            catch ( System.Threading.Tasks.TaskCanceledException )
            {
                break; // Normal exit when the game stops mid-sleep
            }
            catch ( System.Exception ex )
            {
                Log.Error( $"Leaderboard loop hit an issue: {ex.Message}" );
                await System.Threading.Tasks.Task.Delay( TimeSpan.FromSeconds( 5 ), token ); // Avoid spamming errors
            }
        }
    }

    /// <summary>
    /// Native s&box cleanup loop. Protects your project editor from slow background memory leaks.
    /// </summary>
    protected override void OnDestroy()
    {
        leaderboardLoopToken?.Cancel();
        leaderboardLoopToken?.Dispose();
    }

    void StartNewGame()
    {

		ClearHoverTelemetry();

        CurrentPhase = GamePhase.WeaponSelection;
        SelectedWeapon = null;
        SelectedEnchantment = string.Empty;

        var manager = GetManager();
        if ( manager != null )
        {
            manager.StartBattle();
        }
    }

    void SelectWeapon( Weapon weapon ) 
	{ 

		ClearHoverTelemetry();

		SelectedWeapon = weapon; 
    
		var manager = GetManager();
		if (manager != null)
			{
				// FIX: Assign it to ChosenWeapon so ProcessTurnSlice can see it!
				manager.ChosenWeapon = weapon;

				if (manager.LocalPlayer != null)
				{
					manager.LocalPlayer.equippedWeapon = weapon;
				}
			}

			GameManager.AddLog($"Equipped: {weapon.name}");
			CurrentPhase = GamePhase.EnchantmentSelection; 
	}
    
    void SelectEnchantment( string enchantment ) 
    {
	
		ClearHoverTelemetry();

        SelectedEnchantment = enchantment; 
        
        // Find the active, live weapon reference inside the backend engine
        var manager = GetManager();
        var activeWeapon = manager?.ChosenWeapon ?? SelectedWeapon;

        if ( activeWeapon != null )
        {
            // Parse the clean UI text ("Fire", "Poison") straight into Weapon.EnchantmentType.Fire
            if ( Enum.TryParse<Weapon.EnchantmentType>( enchantment, true, out var parsedEnum ) )
            {
                activeWeapon.enchantment = parsedEnum;
                activeWeapon.enchantPower = 25; // Base probability rate used by your backend switch
            }
        }

        CurrentPhase = GamePhase.Combat; 
        
        if ( manager?.CombatLogs != null )
		{
			manager.CombatLogs.Add( new GameManager.CombatLogEntry 
			{ 
				Message = $"⚔️ Combat initialized! Weapon: {activeWeapon?.name} ({SelectedEnchantment})", 
				ColorClass = "system-text" 
			} );
		}
    }

	private Sandbox.UI.Panel MyLogPanel { get; set; }

    private async void OnActionPressed( string action )
{
    var manager = GetManager();
    if ( manager == null ) return;

    // 1. Add the turn header to the log list immediately
    if ( manager.CombatLogs != null )
	{
		manager.CombatLogs.Add( new GameManager.CombatLogEntry 
		{ 
			Message = $"--- [ TURN {manager.Round} : {action.ToUpper()} ] ---", 
			ColorClass = "system-text" 
		} );
	}

    // 2. Convert action strings to backend indices
    int moveIndex = action switch
    {
        "Attack"          => 1,
        "Defend"          => 2,
        "Heal"            => 3,
        "Special"         => 4,
        "Offensive Spell" => 5,
        "Defensive Spell" => 6,
        "Healing Spell"   => 7,
        "Ultimate"        => 8,
        _                 => 1
    };

    // 3. Process the backend combat loop simulation
    manager.ProcessTurnSlice( moveIndex );

    // 4. Tell Razor to update the UI tree elements
    StateHasChanged();

    // 5. PASS 1: Wait 30ms for s&box to generate the text strings, then snap down
    await System.Threading.Tasks.Task.Delay( 30 );
    if ( MyLogPanel != null )
    {
        MyLogPanel.TryScrollToBottom();
    }

    // 6. PASS 2: Wait another 30ms to catch layout changes (like game over banners popping in)
    await System.Threading.Tasks.Task.Delay( 30 );
    if ( MyLogPanel != null )
    {
        MyLogPanel.TryScrollToBottom();
    }
}
    
    private async void OnRematchPressed()
    {
        StartNewGame();
        StateHasChanged();
		await System.Threading.Tasks.Task.Delay( 50 );

		if ( MyLogPanel != null )
		{
			MyLogPanel.TryScrollToBottom();
		}
    }

	public static string GetWeaponArt( string weaponName )
	{
		
		if ( string.IsNullOrEmpty( weaponName ) ) return "";

		if ( weaponName.Equals( "Sword", StringComparison.OrdinalIgnoreCase ) )
		{
			return @"
     /\
    //\\
    ||||
    ||||
    ||||
    ||||
    ||||
    ||||
    ||||
    ||||
    ||||
    ||||
    ||||
    ||||
 _  ||||  _
/(  ||||  )\
\\\_[\/]_///
 \\_<()>_//
  \/[\\]\/
    [//]
    [\\]
    [//]
    [\\]
    [//]
    (__)
";
		}
		else if ( weaponName.Equals( "Axe", StringComparison.OrdinalIgnoreCase ) )
		{
			return @"
 //`-||-'\\\
(| -=||=- |)
 \\,-||-.//
  `  ||  '
     ||
     ||
     ||
     ||
     ||
     ()
";
		}

		else if ( weaponName.Equals( "Shield", StringComparison.OrdinalIgnoreCase ) )
		{
			return @"

  |`-._/\_.-`|
  |    ||    |
  |___o()o___|
  |__((<>))__|
  \   o\/o   /
   \   ||   /
    \  ||  /
     '.||.'
       ``
";
		}
		else if ( weaponName.Equals( "Dagger", StringComparison.OrdinalIgnoreCase ) )
		{
			return @"
       /\
      //\\
     //  \\
     \\  //    
      )  (
      )  (
      )  (
 ^    )  (    ^
/ \   )  (   / \
) (   )  (   ) (
\  \_/ /\ \_/  /
 \__  _)(_  __/
    \ \  / /
     ) \/ (

     | /\ |
     | )( |
     | )( |
     | \/ |
     )____(
    /      \
    \______/
";
		}

		return "";
	}

protected override int BuildHash()
{
    var manager = GetManager();

    // Bucket 1: Local Player Tracking Metrics (8/8 Max Capacity)
    int playerHash = System.HashCode.Combine(
        CurrentPhase,
        manager?.LocalPlayer?.hp,
        manager?.LocalPlayer?.defense,
        manager?.LocalPlayer?.magic,
        manager?.LocalPlayer?.cursed ?? false,      
        manager?.LocalPlayer?.eventAttackBuff ?? 0, 
        manager?.PlayerPoints, 
        IsGameOver ? 1 : 0    
    );

    // Bucket 2: Enemy & Global Core Metrics (8/8 Max Capacity)
    int enemyAndLogHash = System.HashCode.Combine(
        manager?.AIEnemy?.hp,
        manager?.AIEnemy?.defense,
        manager?.AIEnemy?.magic,
        manager?.AIEnemy?.cursed ?? false,          
        manager?.AIEnemy?.eventAttackBuff ?? 0,     
        CombatSession.WinStreak,                
        CombatSession.LossStreak,
        manager?.CombatLogs?.Count 
    );

    // Bucket 3: Leaderboard Sync Status (2/8 Capacity)
    int cloudHash = System.HashCode.Combine( IsReadingGlobalBoard, GlobalPrestigeEntries?.Length ?? 0 );

    
    int firewallHash = System.HashCode.Combine(
        manager?.AICurrentStance,
		manager?.IsAIActionIntercepted,
        manager?.AICurrentMoveIndex
    );
    
    int perkTrackingHash = System.HashCode.Combine(
        manager?.IsPerkEventActive ?? false,
        manager?.ActivePerkChoices?.Count ?? 0
    );

    // Add all unified sub-hashes together cleanly to pass the UI rendering check
    return playerHash + enemyAndLogHash + cloudHash + firewallHash + perkTrackingHash;
}
}