PushHud.razor

UI Razor component for the game's HUD and menus. It renders score cards, scoreboard, killfeed, hotbar, chat, spawn/workshop menus, settings and leaderboard, and wires UI actions to game systems (PushAbility, SaveManager, Leaderboards, WeaponManager, etc.).

NetworkingHttp Calls
@using Sandbox;
@using Sandbox.UI;
@using Sandbox.Services;
@using System;
@using System.Linq;
@using System.Collections.Generic;
@using System.Threading.Tasks;
@inherits PanelComponent
@attribute [StyleSheet( "PushHud.scss" )]

<root class="hud-root">

    <div class="top-header-container">
        <div class="score-box">
            <div class="score-card time">
                <label class="score-title">@L( "Время", "Time" )</label>
                <label class="score-value">@GetFormattedTime()</label>
            </div>
            <div class="score-card">
                <label class="score-title">@L( "Сбито", "Score" )</label>
                <label class="score-value">@GetScore()</label>
            </div>
            <div class="score-card death">
                <label class="score-title">@L( "Смерти", "Deaths" )</label>
                <label class="score-value">@GetDeaths()</label>
            </div>
            <div class="score-card highlight">
                <label class="score-title">@L( "Рекорд", "Best" )</label>
                <label class="score-value">@GetHighScore()</label>
            </div>
        </div>

        <div class="round-badge">
            @L( "РАУНД", "ROUND" ) @(RoundManager.Instance?.CurrentRound ?? 1)
        </div>

        @if ( MapEventManager.Instance != null && MapEventManager.Instance.CurrentEvent != WeatherType.None )
        {
            <div class="weather-banner @GetWeatherClass( MapEventManager.Instance.CurrentEvent )">
                <label class="weather-icon">@GetWeatherIcon( MapEventManager.Instance.CurrentEvent )</label>
                <div class="weather-info">
                    <label class="weather-title">@GetWeatherTitle( MapEventManager.Instance.CurrentEvent )</label>
                    <label class="weather-sub">@L( "АКТИВНАЯ АНОМАЛИЯ", "ACTIVE ANOMALY" )</label>
                </div>
                <div class="weather-timer-badge">
                    <label class="timer-text">@($"{MapEventManager.Instance.TimeRemaining:F1}s")</label>
                </div>
            </div>
        }
    </div>

    <div class="scoreboard-box">
        <div class="scoreboard-header">
            <label class="header-title">@L( "ИГРОКИ", "PLAYERS" )</label>
        </div>
        <div class="player-list">
            @foreach ( var p in GetPlayerList() )
            {
                <div class="player-row @(p.IsLocal ? "local" : "")">
                    <label class="player-name">@p.Name</label>
                    <label class="player-score">@p.Score</label>
                </div>
            }
        </div>
    </div>

    <div class="killfeed-container">
        @if ( Killfeed.Instance != null )
        {
            @foreach ( var entry in Killfeed.Instance.Entries )
            {
                <div class="kill-entry">
                    <label class="killer">@entry.KillerName</label>
                    <label class="icon">💥 [@entry.Method]</label>
                    <label class="victim">@entry.VictimName</label>
                </div>
            }
        }
    </div>

    <div class="hotbar-container">
        @if ( ShopManager.Instance != null )
        {
            @foreach ( var item in ShopManager.Instance.Items )
            {
                bool isSelected = item == ShopManager.Instance.SelectedItem;
                
                <div class="slot-wrapper">
                    <div class="hotbar-slot @(isSelected ? "selected" : "") @(!item.IsUnlocked ? "locked" : "")" 
                         onclick="@(() => ShopManager.Instance.TryBuyOrSelect(item))">
                        
                        <label class="slot-name">@item.GetName( CurrentLanguage )</label>
                        
                        @if ( !item.IsUnlocked )
                        {
                            <label class="slot-cost">💰 @item.Cost</label>
                        }
                    </div>
                    <div class="key-badge @(isSelected ? "selected" : "")">@item.Key</div>
                </div>
            }
        }
    </div>

    <div class="top-left-box">
        <div class="chat-box @(IsChatOpen ? "open" : "")">
            <div class="chat-messages">
                @foreach ( var msg in CanvasMessages )
                {
                    <div class="chat-row">
                        <label class="chat-author">@msg.Author:</label>
                        <label class="chat-text">@msg.Text</label>
                    </div>
                }
            </div>

            @if ( IsChatOpen )
            {
                <div class="chat-input-row">
                    <textentry @ref="ChatInput" onsubmit="@SendChatMessage" placeholder="@L( "Введите сообщение...", "Type a message..." )"></textentry>
                </div>
            }
            else
            {
                <div class="hints-row">
                    <label class="chat-hint" onclick="@OpenChat">@L( "Нажмите [Enter] для чата", "Press [Enter] to chat" )</label>
                    <label class="chat-hint prop-hint" onclick="@ToggleWorkshopMenu">@L( "[E] Спавн-меню | [R] Мастерская", "[E] Spawnmenu | [R] Workshop" )</label>
                </div>
            }
        </div>
    </div>

    <div class="player-status-panel">
        <div class="status-box health-box">
            <div class="status-header">
                <label class="status-title">❤️ @L( "ЗДОРОВЬЕ", "HEALTH" )</label>
                <label class="status-val">@($"{GetHealth():F0}") / @($"{GetMaxHealth():F0}")</label>
            </div>
            <div class="bar-background">
                <div class="bar-fill health" style="width: @($"{GetHealthPercent():F1}%");"></div>
            </div>
        </div>

        <div class="status-box cooldown-box">
            <div class="status-header">
                <label class="status-title">⚡ @L( "ПЕРЕЗАРЯДКА", "COOLDOWN" )</label>
            </div>
            <div class="bar-background">
                <div class="bar-fill" style="width: @($"{GetPercent():F1}%");"></div>
            </div>
        </div>
    </div>

    @if ( PushAbility != null && PushAbility.IsCharging )
    {
        <div class="charge-box">
            <label class="charge-title">@L( "ЗАРЯД", "CHARGE" )</label>
            <div class="bar-background">
                <div class="bar-fill charge" style="width: @($"{PushAbility.ChargeProgress * 100f:F1}%");"></div>
            </div>
        </div>
    }

    @if ( LocalHealth != null && LocalHealth.IsDead )
    {
        <div class="respawn-overlay">
            <div class="respawn-card">
                <label class="death-title">💀 @L( "ВЫ ПОГИБЛИ!", "YOU DIED!" )</label>
                <label class="respawn-subtitle">
                    @L( "Возрождение через:", "Respawning in:" )
                </label>
                <label class="respawn-timer">@($"{Math.Max( 0, LocalHealth.RespawnTimer ):F1}s")</label>
            </div>
        </div>
    }

    @if ( !IsMenuOpen && !IsSettingsOpen && !IsWorkshopMenuOpen && !IsQSpawnMenuOpen )
    {
        <button class="side-settings-btn" onclick="@OpenMenu">
            <label class="key-hint">[ESC]</label>
            <label class="icon">⚙</label>
        </button>
    }

    @if ( IsQSpawnMenuOpen )
    {
        <div class="spawnmenu-overlay" onclick="@CloseQSpawnMenu">
            <div class="spawnmenu-window" onclick="@((e) => e.StopPropagation())">
                
                <div class="spawnmenu-topbar">
                    <div class="brand-badge">
                        <label class="brand-text">ONE MORE PUSH</label>
                        <label class="sub-text">SPAWN MENU</label>
                    </div>

                    <div class="topbar-nav">
                        <button class="topbar-tab active">📦 @L( "ПРОПСЫ И АРЕНА", "PROPS & ARENA" )</button>
                        <button class="topbar-tab" onclick="@ToggleWorkshopMenu">⚡ @L( "МАСТЕРСКАЯ [R]", "WORKSHOP [R]" )</button>
                    </div>

                    <div class="topbar-credits">
                        <label class="credits-label">@L( "БАЛАНС:", "BALANCE:" )</label>
                        <label class="credits-val">💰 @GetCredits()</label>
                    </div>
                </div>

                <div class="spawnmenu-body">
                    <div class="spawnmenu-sidebar">
                        <label class="sidebar-title">@L( "КАТЕГОРИИ", "CATEGORIES" )</label>
                        <div class="sidebar-item active">💥 @L( "Интерактив", "Interactive" )</div>
                        <div class="sidebar-item">🛢️ @L( "Опасные зоны", "Hazard Zones" )</div>
                        <div class="sidebar-item">⚓ @L( "Тяжелые объекты", "Heavy Objects" )</div>
                        <div class="sidebar-item">🌀 @L( "Ловушки & Батуты", "Traps & Springs" )</div>
                    </div>

                    <div class="spawnmenu-content">
                        <div class="content-header">
                            <label class="section-title">@L( "СПАВН ОБЪЕКТОВ", "SPAWN OBJECTS" )</label>
                            <label class="section-desc">@L( "Выберите предмет для создания на карте", "Select an item to spawn on the map" )</label>
                        </div>

                        <div class="gmod-prop-grid">
                            <div class="gmod-prop-card" onclick="@(() => SelectAndSpawnProp(0))">
                                <div class="card-glow"></div>
                                <div class="prop-icon">💥</div>
                                <label class="prop-label">@L( "Взрывная Бочка", "Explosive Barrel" )</label>
                                <label class="prop-price">💰 15</label>
                            </div>

                            <div class="gmod-prop-card" onclick="@(() => SelectAndSpawnProp(1))">
                                <div class="card-glow"></div>
                                <div class="prop-icon">🛢️</div>
                                <label class="prop-label">@L( "Зона Смолы", "Tar Zone" )</label>
                                <label class="prop-price">💰 20</label>
                            </div>

                            <div class="gmod-prop-card" onclick="@(() => SelectAndSpawnProp(2))">
                                <div class="card-glow"></div>
                                <div class="prop-icon">⚓</div>
                                <label class="prop-label">@L( "Наковальня", "Anvil" )</label>
                                <label class="prop-price">💰 25</label>
                            </div>

                            <div class="gmod-prop-card" onclick="@(() => SelectAndSpawnProp(3))">
                                <div class="card-glow"></div>
                                <div class="prop-icon">🌀</div>
                                <label class="prop-label">@L( "Пружинный Батут", "Spring Pad" )</label>
                                <label class="prop-price">💰 20</label>
                            </div>
                        </div>
                    </div>

                    <div class="spawnmenu-tools">
                        <label class="tools-title">@L( "ИНСТРУМЕНТЫ", "TOOLS" )</label>
                        
                        <div class="tool-group">
                            <label class="group-title">@L( "Взаимодействие", "Interaction" )</label>
                            <div class="tool-item active">👉 @L( "Спавн-меню [E]", "Spawn Menu [E]" )</div>
                            <div class="tool-item">🚀 @L( "Силовая волна", "Force Wave" )</div>
                        </div>

                        <div class="tool-group">
                            <label class="group-title">@L( "Управление", "Controls" )</label>
                            <div class="tool-hint">@L( "Отпустите [E] для закрытия меню", "Release [E] to close menu" )</div>
                        </div>
                    </div>
                </div>
            </div>
        </div>
    }

    @if ( IsWorkshopMenuOpen )
    {
        <div class="menu-overlay" onclick="@CloseWorkshopMenu">
            <div class="prop-menu-card" onclick="@((e) => e.StopPropagation())">
                
                <div class="workshop-header">
                    <label class="menu-title">@L( "МАСТЕРСКАЯ АРЕНЫ", "ARENA WORKSHOP" )</label>
                    <div class="credits-badge">💰 @GetCredits()</div>
                </div>

                <div class="tab-selector">
                    <button class="tab-btn @(ActiveTab == "PROPS" ? "active" : "")" onclick="@(() => SelectTab("PROPS"))">
                        📦 @L( "ПРОПСЫ", "PROPS" )
                    </button>
                    <button class="tab-btn @(ActiveTab == "UPGRADES" ? "active" : "")" onclick="@(() => SelectTab("UPGRADES"))">
                        ⚡ @L( "ПРОКАЧКА", "UPGRADES" )
                    </button>
                    <button class="tab-btn @(ActiveTab == "WEAPONS" ? "active" : "")" onclick="@(() => SelectTab("WEAPONS"))">
                        🔫 @L( "ОРУЖИЕ", "WEAPONS" )
                    </button>
                    <button class="tab-btn @(ActiveTab == "PRESTIGE" ? "active" : "")" onclick="@(() => SelectTab("PRESTIGE"))">
                        👑 @L( "ПРЕСТИЖ", "PRESTIGE" )
                    </button>
                </div>

                @if ( ActiveTab == "PROPS" )
                {
                    <div class="prop-grid">
                        <div class="prop-item" onclick="@(() => SelectAndSpawnProp(0))">
                            <label class="prop-name">💥 @L( "Бочка", "Barrel" )</label>
                            <label class="prop-cost">💰 15</label>
                        </div>
                        <div class="prop-item" onclick="@(() => SelectAndSpawnProp(1))">
                            <label class="prop-name">🛢️ @L( "Смола", "Tar Zone" )</label>
                            <label class="prop-cost">💰 20</label>
                        </div>
                        <div class="prop-item" onclick="@(() => SelectAndSpawnProp(2))">
                            <label class="prop-name">⚓ @L( "Наковальня", "Anvil" )</label>
                            <label class="prop-cost">💰 25</label>
                        </div>
                        <div class="prop-item" onclick="@(() => SelectAndSpawnProp(3))">
                            <label class="prop-name">🌀 @L( "Батут", "Spring" )</label>
                            <label class="prop-cost">💰 20</label>
                        </div>
                    </div>
                }

                @if ( ActiveTab == "UPGRADES" )
                {
                    <div class="upgrades-list">
                        <div class="upgrade-row">
                            <div class="info">
                                <label class="name">💪 @L( "СИЛА УДАРА", "PUSH FORCE" )</label>
                                <label class="level">LVL @GetForceLevel()</label>
                            </div>
                            <button class="buy-btn" onclick="@TryUpgradeForce">
                                <label class="btn-text">@L( "КУПИТЬ", "BUY" )</label>
                                <label class="btn-cost">@GetForceCost() 💰</label>
                            </button>
                        </div>

                        <div class="upgrade-row">
                            <div class="info">
                                <label class="name">🏋️ @L( "МАССА ИГРОКА", "PLAYER MASS" )</label>
                                <label class="level">LVL @GetMassLevel()</label>
                            </div>
                            <button class="buy-btn" onclick="@TryUpgradeMass">
                                <label class="btn-text">@L( "КУПИТЬ", "BUY" )</label>
                                <label class="btn-cost">@GetMassCost() 💰</label>
                            </button>
                        </div>

                        <div class="upgrade-row">
                            <div class="info">
                                <label class="name">⚡ @L( "ЭНЕРГИЯ И РЫВОК", "ENERGY RECOVERY" )</label>
                                <label class="level">LVL @GetEnergyLevel()</label>
                            </div>
                            <button class="buy-btn" onclick="@TryUpgradeEnergy">
                                <label class="btn-text">@L( "КУПИТЬ", "BUY" )</label>
                                <label class="btn-cost">@GetEnergyCost() 💰</label>
                            </button>
                        </div>
                    </div>
                }

                @if ( ActiveTab == "WEAPONS" )
                {
                    <div class="upgrades-list">
                        @if ( WeaponManager.Instance != null )
                        {
                            @foreach ( var w in WeaponManager.Instance.Weapons )
                            {
                                bool isEquipped = WeaponManager.Instance.CurrentWeapon == w;

                                <div class="upgrade-row">
                                    <div class="info">
                                        <label class="name">@(CurrentLanguage == "RU" ? w.NameRu : w.NameEn)</label>
                                        <label class="level">
                                            @(isEquipped ? L( "ЭКИПИРОВАНО", "EQUIPPED" ) : (w.IsUnlocked ? L( "КУПЛЕНО", "UNLOCKED" ) : L( "ЗАКРЫТО", "LOCKED" )))
                                        </label>
                                    </div>

                                    @if ( isEquipped )
                                    {
                                        <button class="buy-btn equipped">
                                            <label class="btn-text">✔ @L( "АКТИВНО", "ACTIVE" )</label>
                                        </button>
                                    }
                                    else if ( w.IsUnlocked )
                                    {
                                        <button class="buy-btn equip" onclick="@(() => WeaponManager.Instance.TryBuyOrEquip(w))">
                                            <label class="btn-text">⚡ @L( "ВЗЯТЬ", "EQUIP" )</label>
                                        </button>
                                    }
                                    else
                                    {
                                        <button class="buy-btn" onclick="@(() => WeaponManager.Instance.TryBuyOrEquip(w))">
                                            <label class="btn-text">@L( "КУПИТЬ", "BUY" )</label>
                                            <label class="btn-cost">@w.Cost 💰</label>
                                        </button>
                                    }
                                </div>
                            }
                        }
                    </div>
                }

                @if ( ActiveTab == "PRESTIGE" )
                {
                    <div class="prestige-card-container">
                        <div class="prestige-info-box">
                            <label class="prestige-lvl">👑 @L( "УРОВЕНЬ ПРЕСТИЖА:", "PRESTIGE LEVEL:" ) @GetPrestigeLevel()</label>
                            <label class="prestige-bonus">✨ @L( "БОНУС К КРЕДИТАМ:", "CREDIT BONUS:" ) +@(GetPrestigeLevel() * 50)%</label>
                            <label class="prestige-tokens">🔮 @L( "ОЧКИ ПРЕСТИЖА:", "PRESTIGE TOKENS:" ) @GetPrestigeTokens()</label>
                        </div>

                        <div class="prestige-desc">
                            <label class="desc-text">
                                @L( "Сбросьте уровни Силы, Массы и Энергии, чтобы получить Очко Престижа и постоянный бонус +50% к заработку!", 
                                   "Reset Force, Mass, and Energy levels to get a Prestige Token and a permanent +50% credit bonus!" )
                            </label>
                            <label class="req-text">
                                @L( "ТРЕБОВАНИЕ: Сумма уровней прокачки 15+", "REQUIREMENT: Total Upgrade Levels 15+" )
                            </label>
                        </div>

                        <button class="prestige-btn @(CanPrestige() ? "ready" : "disabled")" onclick="@TryPrestige">
                            <label class="btn-text">@L( "СБРОСИТЬ И ПОЛУЧИТЬ ПРЕСТИЖ", "PRESTIGE RESET" )</label>
                        </button>
                    </div>
                }

                <button class="menu-btn" onclick="@CloseWorkshopMenu">@L( "ЗАКРЫТЬ [R]", "CLOSE [R]" )</button>
            </div>
        </div>
    }

    @if ( IsMenuOpen )
    {
        <div class="main-menu-screen">
            <div class="main-menu-sidebar">
                <div class="brand-section">
                    <div class="logo-glow"></div>
                    <label class="brand-title">ONE MORE PUSH</label>
                    <label class="brand-subtitle">PHYSICS ARENA BRAWLER</label>
                </div>

                <div class="nav-buttons-group">
                    <button class="epic-btn play" onclick="@StartGame">
                        <div class="btn-glow"></div>
                        <label class="btn-icon">▶</label>
                        <div class="btn-content">
                            <label class="btn-title">@L( "В БОЙ", "PLAY NOW" )</label>
                            <label class="btn-sub">@L( "Выйти на арену", "Enter the Arena" )</label>
                        </div>
                    </button>

                    <button class="epic-btn" onclick="@OpenSettings">
                        <label class="btn-icon">⚙</label>
                        <div class="btn-content">
                            <label class="btn-title">@L( "НАСТРОЙКИ", "SETTINGS" )</label>
                            <label class="btn-sub">@L( "Параметры и управление", "Game options" )</label>
                        </div>
                    </button>

                    <button class="epic-btn quit" onclick="@QuitGame">
                        <label class="btn-icon">✖</label>
                        <div class="btn-content">
                            <label class="btn-title">@L( "ВЫХОД", "QUIT" )</label>
                            <label class="btn-sub">@L( "Покинуть игру", "Close Game" )</label>
                        </div>
                    </button>
                </div>

                <div class="player-profile-card">
                    <div class="profile-icon">🎮</div>
                    <div class="profile-info">
                        <label class="profile-name">@(Connection.Local?.DisplayName ?? L( "Боец", "Fighter" ))</label>
                        <label class="profile-stats">💰 @GetCredits() | 🏆 @GetHighScore()</label>
                    </div>
                </div>
            </div>

            <div class="main-menu-leaderboard-panel">
                <div class="lb-header">
                    <label class="lb-title">🏆 @L( "ТАБЛИЦА ЛИДЕРОВ", "GLOBAL LEADERBOARD" )</label>
                    <button class="lb-refresh-btn" onclick="@RefreshLeaderboard">🔄</button>
                </div>

                <div class="lb-tabs">
                    <button class="tab-btn @(CurrentLbStat == "pushes_count" ? "active" : "")" onclick="@(() => SelectLbStat("pushes_count"))">
                        🚀 @L( "ТОЛЧКИ", "PUSHES" )
                    </button>
                    <button class="tab-btn @(CurrentLbStat == "rounds_won" ? "active" : "")" onclick="@(() => SelectLbStat("rounds_won"))">
                        👑 @L( "ПОБЕДЫ", "WINS" )
                    </button>
                </div>

                <div class="lb-body">
                    @if ( IsLbLoading )
                    {
                        <div class="lb-status">@L( "Загрузка...", "LOADING..." )</div>
                    }
                    else if ( LbEntries == null || LbEntries.Count == 0 )
                    {
                        <div class="lb-status">@L( "Нет данных", "NO RECORD FOUND" )</div>
                    }
                    else
                    {
                        <div class="lb-table">
                            <div class="lb-table-header">
                                <span class="col-rank">#</span>
                                <span class="col-player">@L( "ИГРОК", "PLAYER" )</span>
                                <span class="col-score">@L( "СЧЕТ", "SCORE" )</span>
                            </div>
                            <div class="lb-table-rows">
                                @foreach ( var entry in LbEntries )
                                {
                                    <div class="lb-row @(entry.SteamId == (Connection.Local?.SteamId ?? 0) ? "is-me" : "")">
                                        <span class="col-rank">@GetRankBadge( entry.Rank )</span>
                                        <span class="col-player">@entry.DisplayName</span>
                                        <span class="col-score">@entry.Value.ToString("N0")</span>
                                    </div>
                                }
                            </div>
                        </div>
                    }
                </div>
            </div>

            <div class="main-menu-footer-badge">
                <label class="footer-ver">v1.0.0 RELEASE</label>
                <label class="footer-engine">POWERED BY SOURCE 2 & s&box</label>
            </div>
        </div>
    }

    @if ( IsSettingsOpen )
    {
        <div class="main-menu-screen">
            <div class="main-menu-sidebar settings-wide">
                <div class="brand-section">
                    <label class="brand-title">@L( "НАСТРОЙКИ", "SETTINGS" )</label>
                    <label class="brand-subtitle">CONFIGURATION PANEL</label>
                </div>

                <div class="settings-list-container">
                    <div class="setting-row-card">
                        <div class="setting-info">
                            <label class="title">🌐 @L( "Язык интерфейса", "Language" )</label>
                            <label class="desc">@L( "Выберите язык отображения", "Select UI Language" )</label>
                        </div>
                        <div class="lang-selector">
                            <button class="lang-btn @(CurrentLanguage == "EN" ? "active" : "")" onclick="@(() => SetLanguage("EN"))">EN</button>
                            <button class="lang-btn @(CurrentLanguage == "RU" ? "active" : "")" onclick="@(() => SetLanguage("RU"))">RU</button>
                        </div>
                    </div>

                    <div class="setting-row-card">
                        <div class="setting-info">
                            <label class="title">🤖 @L( "Спавн Ботов", "Bot Spawner" )</label>
                            <label class="desc">@L( "Автоматически добавлять ботов на карту", "Spawn AI bots in game" )</label>
                        </div>
                        <button class="lang-btn @(BotSpawner.EnableBots ? "active" : "")" onclick="@ToggleBots">
                            @(BotSpawner.EnableBots ? L( "ВКЛ", "ON" ) : L( "ВЫКЛ", "OFF" ))
                        </button>
                    </div>

                    <div class="setting-row-card">
                        <div class="setting-info">
                            <label class="title">🎵 @L( "Фоновая Музыка", "Music" )</label>
                            <label class="desc">@L( "Включить динамический саундтрек", "Toggle battle music" )</label>
                        </div>
                        <button class="lang-btn @(EnableMusic ? "active" : "")" onclick="@ToggleMusic">
                            @(EnableMusic ? L( "ВКЛ", "ON" ) : L( "ВЫКЛ", "OFF" ))
                        </button>
                    </div>

                    <div class="setting-row-card">
                        <div class="setting-info">
                            <label class="title">🎯 @L( "Чувствительность", "Sensitivity" )</label>
                            <label class="desc">@L( "Скорость поворота камеры", "Mouse sensitivity factor" )</label>
                        </div>
                        <label class="sens-badge">1.0</label>
                    </div>
                </div>

                <button class="epic-btn back" onclick="@CloseSettings">
                    <label class="btn-icon">⬅</label>
                    <div class="btn-content">
                        <label class="btn-title">@L( "НАЗАД", "BACK" )</label>
                    </div>
                </button>
            </div>
        </div>
    }

</root>

@code {
    public class EntryItem
    {
        public int Rank { get; set; }
        public string DisplayName { get; set; }
        public double Value { get; set; }
        public ulong SteamId { get; set; }
    }

    [Property] public PushAbility PushAbility { get; set; }
    public PlayerHealth LocalHealth { get; private set; }

    public bool IsMenuOpen { get; set; } = true;
    public bool IsSettingsOpen { get; set; } = false;
    public bool IsWorkshopMenuOpen { get; set; } = false;
    public bool IsQSpawnMenuOpen { get; set; } = false;

    public string ActiveTab { get; set; } = "PROPS";
    public static string CurrentLanguage { get; set; } = "EN";

    public bool IsLbLoading { get; set; } = true;
    public string CurrentLbStat { get; set; } = "pushes_count";
    public List<EntryItem> LbEntries { get; set; } = new();

    public struct ChatMessage
    {
        public string Author { get; set; }
        public string Text { get; set; }
    }

    public List<ChatMessage> CanvasMessages { get; set; } = new();
    public bool IsChatOpen { get; set; } = false;
    private TextEntry ChatInput { get; set; }

    public struct PlayerInfo
    {
        public string Name { get; set; }
        public int Score { get; set; }
        public bool IsLocal { get; set; }
    }

    private string L( string ru, string en ) => CurrentLanguage == "RU" ? ru : en;
    private bool IsReady => PushAbility?.IsReady ?? false;

    private MetaProgression GetMeta() => Scene.GetAllComponents<MetaProgression>().FirstOrDefault();
    private int GetCredits() => GetMeta()?.Credits ?? 0;
    private int GetForceLevel() => GetMeta()?.ForceLevel ?? 0;
    private int GetMassLevel() => GetMeta()?.MassLevel ?? 0;
    private int GetEnergyLevel() => GetMeta()?.EnergyLevel ?? 0;

    private int GetPrestigeLevel() => GetMeta()?.PrestigeLevel ?? 0;
    private int GetPrestigeTokens() => GetMeta()?.PrestigeTokens ?? 0;
    private bool CanPrestige() => GetMeta()?.CanPrestige() ?? false;
    private void TryPrestige() => GetMeta()?.TryPrestige();

    private int GetForceCost() => GetMeta()?.GetUpgradeCost(GetForceLevel()) ?? 0;
    private int GetMassCost() => GetMeta()?.GetUpgradeCost(GetMassLevel()) ?? 0;
    private int GetEnergyCost() => GetMeta()?.GetUpgradeCost(GetEnergyLevel()) ?? 0;

    private void TryUpgradeForce() => GetMeta()?.TryUpgradeForce();
    private void TryUpgradeMass() => GetMeta()?.TryUpgradeMass();
    private void TryUpgradeEnergy() => GetMeta()?.TryUpgradeEnergy();

    private float GetHealth() => LocalHealth?.CurrentHealth ?? 100f;
    private float GetMaxHealth() => LocalHealth?.MaxHealth ?? 100f;
    private float GetHealthPercent() => MathX.Clamp( (GetHealth() / GetMaxHealth()) * 100f, 0f, 100f );

    private void SelectTab( string tab )
    {
        ActiveTab = tab;
        StateHasChanged();
    }

    protected override void OnEnabled()
    {
        base.OnEnabled();
        AddMessage( L( "Система", "System" ), L( "Чат готов. Нажмите Enter для ввода.", "Chat ready. Press Enter to type." ) );
        ApplyMouseState();
        _ = RefreshLeaderboard();
    }

    private async Task SelectLbStat( string statIdent )
    {
        if ( CurrentLbStat == statIdent ) return;
        CurrentLbStat = statIdent;
        await RefreshLeaderboard();
    }

    private async Task RefreshLeaderboard()
    {
        IsLbLoading = true;
        StateHasChanged();

        try
        {
            var board = Sandbox.Services.Leaderboards.GetFromStat( "seilordstudio.one_more_push", CurrentLbStat );
            board.SetAggregationSum();
            board.MaxEntries = 10;
            await board.Refresh();

            if ( board.Entries != null )
            {
                LbEntries = board.Entries.Select( x => new EntryItem
                {
                    Rank = Convert.ToInt32( x.Rank ),
                    DisplayName = x.DisplayName ?? "Player",
                    Value = Convert.ToDouble( x.Value ),
                    SteamId = Convert.ToUInt64( x.SteamId )
                } ).ToList();
            }
            else
            {
                LbEntries = new();
            }
        }
        catch ( System.Exception ex )
        {
            Log.Warning( $"Leaderboard fetch error: {ex.Message}" );
        }

        IsLbLoading = false;
        StateHasChanged();
    }

    private string GetRankBadge( int rank )
    {
        return rank switch
        {
            1 => "🥇 1",
            2 => "🥈 2",
            3 => "🥉 3",
            _ => rank.ToString()
        };
    }

    private bool IsEPressed()
    {
        return Input.Down( "use" ) || Input.Down( "e" ) || Input.Down( "E" );
    }

    protected override void OnUpdate()
    {
        base.OnUpdate();

        if ( PushAbility == null )
        {
            PushAbility = Scene.GetAllComponents<PushAbility>().FirstOrDefault( x => !x.IsProxy );
        }

        if ( LocalHealth == null )
        {
            LocalHealth = Scene.GetAllComponents<PlayerHealth>().FirstOrDefault( x => !x.IsProxy );
        }

        bool eDown = IsEPressed();
        if ( eDown )
        {
            if ( !IsQSpawnMenuOpen && !IsChatOpen && !IsSettingsOpen && !IsMenuOpen )
            {
                IsWorkshopMenuOpen = false;
                IsQSpawnMenuOpen = true;
                ApplyMouseState();
                StateHasChanged();
            }
        }
        else
        {
            if ( IsQSpawnMenuOpen )
            {
                CloseQSpawnMenu();
            }
        }

        if ( Input.EscapePressed || Input.Pressed( "menu" ) )
        {
            Input.EscapePressed = false;
            ToggleMenu();
        }

        if ( Input.Pressed( "reload" ) || Input.Pressed( "r" ) || Input.Pressed( "R" ) )
        {
            if ( !IsMenuOpen && !IsSettingsOpen && !IsChatOpen && !IsQSpawnMenuOpen )
            {
                ToggleWorkshopMenu();
            }
        }

        if ( Input.Pressed( "chat" ) || Input.Pressed( "enter" ) )
        {
            if ( !IsMenuOpen && !IsSettingsOpen && !IsWorkshopMenuOpen && !IsQSpawnMenuOpen )
            {
                if ( !IsChatOpen ) OpenChat();
            }
        }

        ApplyMouseState();
    }

    private void SetLanguage( string lang )
    {
        CurrentLanguage = lang;
        StateHasChanged();
    }

    private void ApplyMouseState()
    {
        bool needsMouse = IsMenuOpen || IsSettingsOpen || IsChatOpen || IsWorkshopMenuOpen || IsQSpawnMenuOpen;
        Mouse.Visibility = needsMouse ? MouseVisibility.Visible : MouseVisibility.Hidden;

        if ( !needsMouse && Panel != null )
        {
            Panel.Focus();
        }
    }

    private void ToggleWorkshopMenu()
    {
        IsWorkshopMenuOpen = !IsWorkshopMenuOpen;
        IsQSpawnMenuOpen = false;
        ApplyMouseState();
        StateHasChanged();
    }

    private void CloseWorkshopMenu()
    {
        IsWorkshopMenuOpen = false;
        ApplyMouseState();
        StateHasChanged();
    }

    private void CloseQSpawnMenu()
    {
        IsQSpawnMenuOpen = false;
        ApplyMouseState();
        StateHasChanged();
    }

    private void SelectAndSpawnProp( int index )
    {
        if ( PropSpawner.Instance != null )
        {
            PropSpawner.Instance.SpawnByIndex( index );
        }
        if ( IsWorkshopMenuOpen ) CloseWorkshopMenu();
    }

    private void ToggleMenu()
    {
        if ( IsSettingsOpen ) CloseSettings();
        else if ( IsWorkshopMenuOpen ) CloseWorkshopMenu();
        else if ( IsQSpawnMenuOpen ) CloseQSpawnMenu();
        else if ( IsMenuOpen ) StartGame();
        else OpenMenu();
    }

    private void StartGame()
    {
        IsMenuOpen = false;
        IsSettingsOpen = false;
        IsWorkshopMenuOpen = false;
        IsQSpawnMenuOpen = false;
        ApplyMouseState();
    }

    private void OpenMenu()
    {
        IsMenuOpen = true;
        IsSettingsOpen = false;
        IsWorkshopMenuOpen = false;
        IsQSpawnMenuOpen = false;
        ApplyMouseState();
        _ = RefreshLeaderboard();
    }

    private void OpenSettings()
    {
        IsMenuOpen = false;
        IsSettingsOpen = true;
        IsWorkshopMenuOpen = false;
        IsQSpawnMenuOpen = false;
        ApplyMouseState();
    }

    private void CloseSettings()
    {
        IsSettingsOpen = false;
        IsMenuOpen = true;
        IsWorkshopMenuOpen = false;
        IsQSpawnMenuOpen = false;
        ApplyMouseState();
    }

    private void QuitGame() => Game.Close();
    private int GetScore() => ScoreManager.Instance?.Score ?? 0;

    private string GetFormattedTime()
    {
        var shrinker = Scene.GetAllComponents<ArenaShrinker>().FirstOrDefault();
        if ( !shrinker.IsValid() ) return "02:00";

        int minutes = (int)Math.Max( 0, shrinker.TimeRemaining ) / 60;
        int seconds = (int)Math.Max( 0, shrinker.TimeRemaining ) % 60;
        return $"{minutes:00}:{seconds:00}";
    }

    private float GetPercent()
    {
        if ( PushAbility == null ) return 0f;
        return MathX.Clamp( PushAbility.CooldownProgress * 100f, 0f, 100f );
    }

    private void ToggleBots()
    {
        BotSpawner.EnableBots = !BotSpawner.EnableBots;
        StateHasChanged();
    }

    private int GetHighScore() => SaveManager.Current?.HighScore ?? 0;

    private void OpenChat()
    {
        IsChatOpen = true;
        IsWorkshopMenuOpen = false;
        IsQSpawnMenuOpen = false;
        ApplyMouseState();
        StateHasChanged();
        ChatInput?.Focus();
    }

    private void SendChatMessage()
    {
        if ( ChatInput == null || string.IsNullOrWhiteSpace( ChatInput.Text ) )
        {
            IsChatOpen = false;
            ApplyMouseState();
            return;
        }

        string name = Connection.Local?.DisplayName ?? L( "Вы", "You" );
        AddMessage( name, ChatInput.Text );

        ChatInput.Text = "";
        IsChatOpen = false;
        ApplyMouseState();
    }

    public void AddMessage( string author, string text )
    {
        CanvasMessages.Add( new ChatMessage { Author = author, Text = text } );
        if ( CanvasMessages.Count > 6 )
        {
            CanvasMessages.RemoveAt( 0 );
        }
        StateHasChanged();
    }

    private IEnumerable<PlayerInfo> GetPlayerList()
    {
        if ( Connection.All != null && Connection.All.Any() )
        {
            foreach ( var conn in Connection.All )
            {
                bool isLocal = conn == Connection.Local;
                int score = isLocal ? GetScore() : 0;

                yield return new PlayerInfo
                {
                    Name = string.IsNullOrEmpty( conn.DisplayName ) ? "Player" : conn.DisplayName,
                    Score = score,
                    IsLocal = isLocal
                };
            }
        }
        else
        {
            yield return new PlayerInfo
            {
                Name = L( "Вы", "You" ),
                Score = GetScore(),
                IsLocal = true
            };
        }
    }

    private string GetWeatherTitle( WeatherType type ) => type switch
    {
        WeatherType.LowGravity => L( "НЕВЕСОМОСТЬ", "LOW GRAVITY" ),
        WeatherType.SlipperyIce => L( "ГОЛОЛЁД", "SLIPPERY ICE" ),
        WeatherType.StrongWind => L( "ШТОРМОВОЙ ВЕТЕР", "STRONG WIND" ),
        _ => ""
    };

    private string GetWeatherIcon( WeatherType type ) => type switch
    {
        WeatherType.LowGravity => "🚀",
        WeatherType.SlipperyIce => "❄️",
        WeatherType.StrongWind => "🌪️",
        _ => "⚠️"
    };

    private string GetWeatherClass( WeatherType type ) => type switch
    {
        WeatherType.LowGravity => "gravity",
        WeatherType.SlipperyIce => "ice",
        WeatherType.StrongWind => "wind",
        _ => ""
    };

    private int GetDeaths() => ScoreManager.Instance?.Deaths ?? 0;
    public static bool EnableMusic { get; set; } = true;

    private void ToggleMusic()
    {
        EnableMusic = !EnableMusic;
        MusicManager.Instance?.ToggleMusic( EnableMusic );
        StateHasChanged();
    }

    protected override int BuildHash()
    {
        var hash = new HashCode();
        hash.Add( GetPercent() );
        hash.Add( GetHealthPercent() );
        hash.Add( LocalHealth?.IsDead );
        hash.Add( (int)(LocalHealth?.RespawnTimer * 10f ?? 0) );
        hash.Add( IsReady );
        hash.Add( MapEventManager.Instance?.CurrentEvent );
        hash.Add( (int)(MapEventManager.Instance?.TimeRemaining * 10f ?? 0) );
        hash.Add( EnableMusic );
        hash.Add( PushAbility?.ChargeProgress );
        hash.Add( PushAbility?.IsCharging );
        hash.Add( GetScore() );
        hash.Add( GetDeaths() ); 
        hash.Add( GetFormattedTime() );
        hash.Add( RoundManager.Instance?.CurrentRound );
        hash.Add( IsMenuOpen );
        hash.Add( IsWorkshopMenuOpen );
        hash.Add( IsQSpawnMenuOpen );
        hash.Add( ActiveTab );
        hash.Add( GetCredits() );
        hash.Add( GetForceLevel() );
        hash.Add( GetMassLevel() );
        hash.Add( GetEnergyLevel() );
        hash.Add( GetPrestigeLevel() );
        hash.Add( GetPrestigeTokens() );
        hash.Add( CanPrestige() );
        hash.Add( WeaponManager.Instance?.CurrentWeapon?.Id );
        hash.Add( ShopManager.Instance?.SelectedItem?.Id );
        hash.Add( IsSettingsOpen );
        hash.Add( CurrentLanguage );
        hash.Add( IsChatOpen );
        hash.Add( CanvasMessages.Count );
        hash.Add( Connection.All?.Count() ?? 0 );
        hash.Add( Killfeed.Instance?.Entries.Count );
        hash.Add( CurrentLbStat );
        hash.Add( IsLbLoading );
        hash.Add( LbEntries.Count );
        return hash.ToHashCode();
    }
}