s&box Package Code Search

Search C# source code, UI razor templates, shaders, and configs across s&box packages.

Showing code results for query: * (51 total matches found)
cherkbyte.one_more_push / BotAI.cs
Game game
using Sandbox;
using System;
using System.Linq;

public sealed class BotAI : Component
{
    [Property] public float Speed { get; set; } = 220f;
    [Property] public float AttackRange { get; set; } = 220f;

    private CharacterController _cc;
    private PushAbility _push;
    private ManeuverAbility _maneuver;
    private TimeUntil _nextDecision;
    private Vector3 _moveDir;

    protected override void OnAwake()
    {
        _cc = Components.Get<CharacterController>();
        _push = Components.Get<PushAbility>();
        _maneuver = Components.Get<ManeuverAbility>();
    }

    protected override void OnUpdate()
    {
        if ( IsProxy ) return;

        var target = FindTarget();

        if ( target.IsValid() )
        {
            _moveDir = (target.WorldPosition - WorldPosition).WithZ( 0 ).Normal;

            if ( _cc.IsValid() )
            {
                _cc.Velocity = _moveDir * Speed;
                WorldRotation = Rotation.LookAt( _moveDir );
            }

            float dist = Vector3.DistanceBetween( WorldPosition, target.WorldPosition );
            if ( dist <= AttackRange && _push.IsValid() && _push.IsReady )
            {
                _push.TryExecuteBotPush();
            }
        }
        else
        {
            if ( _nextDecision )
            {
                _nextDecision = Game.Random.Float( 2f, 4f );
                float angle = Game.Random.Float( 0f, MathF.PI * 2f );
                _moveDir = new Vector3( MathF.Cos( angle ), MathF.Sin( angle ), 0f );
            }

            if ( _cc.IsValid() )
            {
                _cc.Velocity = _moveDir * (Speed * 0.6f);
            }
        }

        if ( _cc.IsValid() && !_cc.IsOnGround && _maneuver.IsValid() && _maneuver.IsDashReady )
        {
            _maneuver.TryExecuteBotDash( -_cc.Velocity.Normal );
        }
    }

    private GameObject FindTarget()
    {
        return Scene.GetAllComponents<CharacterController>()
            .Where( c => c.GameObject != GameObject && c.GameObject.IsValid() )
            .OrderBy( c => Vector3.DistanceBetween( WorldPosition, c.WorldPosition ) )
            .FirstOrDefault()?.GameObject;
    }
}
cherkbyte.one_more_push / BouncyShield.cs
Game game
using Sandbox;

public sealed class BouncyShield : Component, Component.ITriggerListener
{
	[Property] public float BounceForce { get; set; } = 1500f;
	[Property] public SoundEvent BounceSound { get; set; }

	public void OnTriggerEnter( Collider other )
	{
		var rootObj = other.GameObject.Root;
		var pushDir = (rootObj.WorldPosition - WorldPosition).Normal;
		pushDir.z = 0.3f;

		bool bounced = false;

		var cc = rootObj.Components.Get<CharacterController>();
		if ( cc.IsValid() )
		{
			cc.Velocity = pushDir * BounceForce;
			bounced = true;
		}

		var rb = rootObj.Components.Get<Rigidbody>();
		if ( rb.IsValid() )
		{
			rb.Velocity = pushDir * BounceForce;
			bounced = true;
		}

		if ( bounced )
		{
			if ( BounceSound != null )
			{
				Sound.Play( BounceSound, WorldPosition );
			}
			CameraJuice.Instance?.Shake( 12f );
		}
	}
}
cherkbyte.one_more_push / EmoteWheel.cs
Game game
using Sandbox;

public sealed class EmoteWheel : Component
{
	[Property] public string CurrentEmote { get; private set; } = "";
	[Property] public float EmoteDuration { get; set; } = 2.5f;

	private TimeSince _emoteTimer;

	public void PlayEmote( string emoteIcon )
	{
		CurrentEmote = emoteIcon;
		_emoteTimer = 0;
	}

	protected override void OnUpdate()
	{
		if ( !string.IsNullOrEmpty( CurrentEmote ) && _emoteTimer > EmoteDuration )
		{
			CurrentEmote = "";
		}
	}
}
cherkbyte.one_more_push / PlayerHealth.cs
Game game
using Sandbox;
using System;
using System.Linq;

public sealed class PlayerHealth : Component
{
	[Property] public float MaxHealth { get; set; } = 100f;
	[Property] public float CurrentHealth { get; private set; } = 100f;

	public bool IsDead => CurrentHealth <= 0f;
	public float RespawnTimer { get; private set; } = 0f;
	public float RespawnDuration { get; set; } = 3f;

	private Vector3 _lastDamageDirection = Vector3.Zero;

	protected override void OnStart()
	{
		CurrentHealth = MaxHealth;
	}

	protected override void OnUpdate()
	{
		if ( IsDead )
		{
			RespawnTimer -= Time.Delta;
			if ( RespawnTimer <= 0f )
			{
				Respawn();
			}
		}
	}

	public void TakeDamage( float damage )
	{
		TakeDamage( damage, Vector3.Zero );
	}

	public void TakeDamage( float damage, Vector3 direction )
	{
		if ( IsDead ) return;

		_lastDamageDirection = direction;
		CurrentHealth = MathX.Clamp( CurrentHealth - damage, 0f, MaxHealth );

		if ( CurrentHealth <= 0f )
		{
			Die();
		}
	}

	public void Heal( float amount )
	{
		if ( IsDead ) return;
		CurrentHealth = MathX.Clamp( CurrentHealth + amount, 0f, MaxHealth );
	}

	private void Die()
	{
		RespawnTimer = RespawnDuration;

		if ( ScoreManager.Instance != null )
		{
			ScoreManager.Instance.AddDeath();
		}

		var ragdoll = Components.Get<PlayerRagdoll>();
		if ( ragdoll.IsValid() )
		{
			ragdoll.SpawnRagdoll( _lastDamageDirection * 2500f );
		}

		var skm = Components.Get<SkinnedModelRenderer>();
		if ( skm.IsValid() ) skm.Enabled = false;

		var mr = Components.Get<ModelRenderer>();
		if ( mr.IsValid() ) mr.Enabled = false;

		var cc = Components.Get<CharacterController>();
		if ( cc.IsValid() ) cc.Enabled = false;

		var rb = Components.Get<Rigidbody>();
		if ( rb.IsValid() ) rb.Enabled = false;

		CameraJuice.Instance?.Shake( 30f, 6f );

		Log.Info( "[PlayerHealth] Игрок погиб!" );
	}

	public void Respawn()
	{
		CurrentHealth = MaxHealth;

		var skm = Components.Get<SkinnedModelRenderer>();
		if ( skm.IsValid() ) skm.Enabled = true;

		var mr = Components.Get<ModelRenderer>();
		if ( mr.IsValid() ) mr.Enabled = true;

		var cc = Components.Get<CharacterController>();
		if ( cc.IsValid() ) cc.Enabled = true;

		var rb = Components.Get<Rigidbody>();
		if ( rb.IsValid() ) rb.Enabled = true;

		var spawnPoint = Scene.GetAllComponents<SpawnPoint>().FirstOrDefault();
		if ( spawnPoint.IsValid() )
		{
			Transform.World = spawnPoint.Transform.World;
		}
		else
		{
			Transform.LocalPosition = Vector3.Up * 50f;
		}

		Log.Info( "[PlayerHealth] Игрок возродился!" );
	}
}
cherkbyte.one_more_push / PushHud.razor
Game game
@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( "В МЕНЮ", "MAIN MENU" )</label>
                            <label class="btn-sub">@L( "Вернуться в главное меню", "Return to Main Menu" )</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.ActiveScene.LoadFromFile( "scenes/mainmenu.scene" );
    }

    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();
    }
}
cherkbyte.one_more_push / .obj/__compiler_extra.cs
Game game
global using static Sandbox.Internal.GlobalGameNamespace;
global using Microsoft.AspNetCore.Components;
global using Microsoft.AspNetCore.Components.Rendering;
[assembly: global::System.Reflection.AssemblyMetadata( "AddonTitle", "One More Push" )]
[assembly: global::System.Reflection.AssemblyMetadata( "AddonIdent", "one_more_push" )]
[assembly: global::System.Reflection.AssemblyMetadata( "OrgIdent", "cherkbyte" )]
[assembly: global::System.Reflection.AssemblyMetadata( "Ident", "cherkbyte.one_more_push" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineVersion", "29" )]
[assembly: global::System.Reflection.AssemblyMetadata( "EngineMinorVersion", "1" )]

[assembly: System.Runtime.Versioning.TargetFramework( ".NETCoreApp,Version=v9.0", FrameworkDisplayName = ".NET 9.0" )]
[assembly: global::System.Reflection.AssemblyMetadata( "CompileTime", "2026-10-06T04:15:32.2273688Z" )]
[assembly: global::System.Reflection.AssemblyVersion("0.0.110.0")]
[assembly: global::System.Reflection.AssemblyFileVersion("0.0.110.0")]
cherkbyte.one_more_push / ArenaRoundManeger1.cs
Game game
using Sandbox;
using System.Linq;

public enum ArenaRoundState
{
    WaitingForPlayers,
    Countdown,
    InGame,
    RoundEnd
}

public sealed class ArenaRoundManager : Component
{
    [Property] public float CountdownDuration { get; set; } = 5.0f;
    [Property] public float RoundDuration { get; set; } = 60.0f;
    [Property] public float EndPauseDuration { get; set; } = 4.0f;

    public ArenaRoundState CurrentState { get; private set; } = ArenaRoundState.WaitingForPlayers;
    public TimeUntil StateTimer { get; private set; }

    protected override void OnStart()
    {
        SetState( ArenaRoundState.WaitingForPlayers );
    }

    protected override void OnUpdate()
    {
        switch ( CurrentState )
        {
            case ArenaRoundState.WaitingForPlayers:
                if ( Connection.All.Count >= 1 )
                {
                    SetState( ArenaRoundState.Countdown );
                }
                break;

            case ArenaRoundState.Countdown:
                if ( StateTimer )
                {
                    SetState( ArenaRoundState.InGame );
                }
                break;

            case ArenaRoundState.InGame:
                if ( StateTimer )
                {
                    SetState( ArenaRoundState.RoundEnd );
                }
                break;

            case ArenaRoundState.RoundEnd:
                if ( StateTimer )
                {
                    SetState( ArenaRoundState.WaitingForPlayers );
                }
                break;
        }
    }

    private void SetState( ArenaRoundState newState )
    {
        CurrentState = newState;

        switch ( newState )
        {
            case ArenaRoundState.WaitingForPlayers:
                Log.Info( "[ArenaRoundManager] Ожидание игроков..." );
                break;

            case ArenaRoundState.Countdown:
                StateTimer = CountdownDuration;
                Log.Info( $"[ArenaRoundManager] Отсчёт! До старта: {CountdownDuration} сек." );
                break;

            case ArenaRoundState.InGame:
                StateTimer = RoundDuration;
                Log.Info( "[ArenaRoundManager] РАУНД НАЧАЛСЯ!" );
                break;

            case ArenaRoundState.RoundEnd:
                StateTimer = EndPauseDuration;
                Log.Info( "[ArenaRoundManager] Раунд окончен!" );
                break;
        }
    }
}
cherkbyte.one_more_push / ExplosiveBarrel.cs
Game game
using Sandbox;
using System;
using System.Linq;

public sealed class ExplosiveBarrel : Component, Component.ICollisionListener
{
    [Property] public float ExplosionRadius { get; set; } = 450f;
    [Property] public float ExplosionForce { get; set; } = 65000f;
    [Property] public float ImpactThreshold { get; set; } = 250f;
    [Property] public GameObject ExplosionParticle { get; set; }
    [Property] public SoundEvent ExplosionSound { get; set; }

    private bool _hasExploded = false;

    public void Explode()
    {
        if ( _hasExploded || !GameObject.IsValid() ) return;
        _hasExploded = true;

        if ( ExplosionParticle.IsValid() )
        {
            ExplosionParticle.Clone( WorldPosition );
        }

        if ( ExplosionSound != null )
        {
            Sound.Play( ExplosionSound, WorldPosition );
        }

        var hits = Scene.Trace.Sphere( ExplosionRadius, WorldPosition, WorldPosition + Vector3.Up * 2f )
            .IgnoreGameObject( GameObject )
            .RunAll();

        foreach ( var hit in hits )
        {
            if ( !hit.GameObject.IsValid() ) continue;

            var targetObj = hit.GameObject.Root;

            var shield = targetObj.Components.Get<ShieldAbility>( true );
            if ( shield.IsValid() && shield.IsShieldActive ) continue;

            var chainBarrel = targetObj.Components.Get<ExplosiveBarrel>();
            if ( chainBarrel.IsValid() && chainBarrel != this )
            {
                chainBarrel.Explode();
                continue;
            }

            var rb = targetObj.Components.Get<Rigidbody>();
            if ( rb.IsValid() )
            {
                var dir = (hit.GameObject.WorldPosition - WorldPosition).Normal;
                dir.z = MathF.Max( dir.z, 0.4f );
                rb.ApplyImpulse( dir * ExplosionForce );
            }
        }

        Log.Info( $"[ExplosiveBarrel] Booom! Barrel exploded at {WorldPosition}" );
        GameObject.Destroy();
    }

    public void OnCollisionStart( Collision collision )
    {
        if ( collision.Contact.Speed.Length > ImpactThreshold )
        {
            Explode();
        }
    }
}
cherkbyte.one_more_push / HeavyAnvil.cs
Game game
using Sandbox;

public sealed class HeavyAnvil : Component, Component.ICollisionListener
{
    [Property] public float PushResistanceMultiplier { get; set; } = 0.25f;
    [Property] public float CrushSpeedThreshold { get; set; } = 300f;
    [Property] public float CrushImpactForce { get; set; } = 75000f;
    [Property] public SoundEvent ImpactSound { get; set; }

    public void OnCollisionStart( Collision collision )
    {
        if ( collision.Contact.Speed.Length < CrushSpeedThreshold ) return;

        var targetObj = collision.Other.GameObject?.Root;
        if ( !targetObj.IsValid() || targetObj == GameObject.Root ) return;

        if ( ImpactSound != null )
        {
            Sound.Play( ImpactSound, WorldPosition );
        }

        var cc = targetObj.Components.Get<CharacterController>();
        if ( cc.IsValid() )
        {
            var dir = (targetObj.WorldPosition - WorldPosition).Normal;
            dir.z = 0.5f;
            cc.Velocity += dir * 900f;
        }

        var rb = targetObj.Components.Get<Rigidbody>();
        if ( rb.IsValid() )
        {
            var dir = (targetObj.WorldPosition - WorldPosition).Normal;
            dir.z = 0.5f;
            rb.ApplyImpulse( dir * CrushImpactForce );
        }
    }
}
cherkbyte.one_more_push / Killfeed.cs
Game game
using Sandbox;
using System.Collections.Generic;

public sealed class Killfeed : Component
{
	public static Killfeed Instance { get; private set; }

	public struct KillEntry
	{
		public string KillerName { get; set; }
		public string VictimName { get; set; }
		public string Method { get; set; }
		public float Time { get; set; }
	}

	public List<KillEntry> Entries { get; private set; } = new();

	protected override void OnStart()
	{
		Instance = this;
	}

	public void AddEntry( string killer, string victim, string method = "Push" )
	{
		Entries.Add( new KillEntry
		{
			KillerName = killer,
			VictimName = victim,
			Method = method,
			Time = RealTime.Now
		} );

		if ( Entries.Count > 5 )
		{
			Entries.RemoveAt( 0 );
		}
	}
}
cherkbyte.one_more_push / ObjectGrabber.cs
Game game
using Sandbox;

public sealed class ObjectGrabber : Component
{
	[Property] public float MaxReachDistance { get; set; } = 300f;
	[Property] public float HoldDistance { get; set; } = 150f;
	[Property] public float ThrowForce { get; set; } = 1200f;

	private GameObject _heldObject;
	private Rigidbody _heldRigidbody;

	protected override void OnUpdate()
	{
		if ( IsProxy ) return;

		if ( Input.Pressed( "use" ) || Input.Pressed( "e" ) )
		{
			if ( _heldObject.IsValid() )
			{
				DropObject();
			}
			else
			{
				TryGrabObject();
			}
		}

		if ( Input.Pressed( "attack1" ) && _heldObject.IsValid() )
		{
			ThrowObject();
		}

		if ( _heldObject.IsValid() )
		{
			UpdateHeldObjectPosition();
		}
	}

	private void TryGrabObject()
	{
		var cam = Components.Get<CameraComponent>() ?? Scene.GetAllComponents<CameraComponent>().FirstOrDefault();
		if ( !cam.IsValid() ) return;

		var ray = cam.Transform.World.Forward;
		var start = cam.WorldPosition;
		var end = start + ray * MaxReachDistance;

		var tr = Scene.Trace.Ray( start, end )
			.IgnoreGameObject( GameObject )
			.Run();

		if ( tr.Hit && tr.GameObject.IsValid() )
		{
			var rootObj = tr.GameObject.Root;
			var rb = rootObj.Components.Get<Rigidbody>() ?? tr.GameObject.Components.Get<Rigidbody>();

			if ( rb.IsValid() && !rootObj.Tags.Has( "player" ) )
			{
				_heldObject = rootObj;
				_heldRigidbody = rb;
				_heldRigidbody.MotionEnabled = true;
			}
		}
	}

	private void UpdateHeldObjectPosition()
	{
		var cam = Components.Get<CameraComponent>() ?? Scene.GetAllComponents<CameraComponent>().FirstOrDefault();
		if ( !cam.IsValid() ) return;

		var targetPos = cam.WorldPosition + cam.Transform.World.Forward * HoldDistance;
		var dir = targetPos - _heldObject.WorldPosition;

		if ( _heldRigidbody.IsValid() )
		{
			_heldRigidbody.Velocity = dir * 10f;
		}
		else
		{
			_heldObject.WorldPosition = Vector3.Lerp( _heldObject.WorldPosition, targetPos, Time.Delta * 15f );
		}
	}

	private void DropObject()
	{
		_heldObject = null;
		_heldRigidbody = null;
	}

	private void ThrowObject()
	{
		if ( !_heldObject.IsValid() ) return;

		var cam = Components.Get<CameraComponent>() ?? Scene.GetAllComponents<CameraComponent>().FirstOrDefault();
		var throwDir = cam.IsValid() ? cam.Transform.World.Forward : Transform.World.Forward;

		if ( _heldRigidbody.IsValid() )
		{
			_heldRigidbody.Velocity = throwDir * ThrowForce;
		}

		CameraJuice.Instance?.Shake( 8f );

		_heldObject = null;
		_heldRigidbody = null;
	}
}
cherkbyte.one_more_push / Assembly.cs
Game game
global using Sandbox;
global using System.Collections.Generic;
global using System.Linq;
cherkbyte.one_more_push / MapEventManager.cs
Game game
using Sandbox;
using System;
using System.Linq;

public enum WeatherType
{
    None,
    LowGravity,
    SlipperyIce,
    StrongWind
}

public sealed class MapEventManager : Component
{
    public static MapEventManager Instance { get; private set; }

    [Property] public float EventDuration { get; set; } = 12f;
    [Property] public float EventInterval { get; set; } = 25f;
    [Property] public float LowGravityForce { get; set; } = 450f;
    [Property] public float WindForce { get; set; } = 400f;

    public WeatherType CurrentEvent { get; private set; } = WeatherType.None;
    public Vector3 CurrentWindDirection { get; private set; } = Vector3.Right;
    public float TimeRemaining { get; private set; }

    private TimeUntil _nextEventTime;
    private TimeUntil _eventEndTime;

    protected override void OnAwake()
    {
        Instance = this;
        _nextEventTime = EventInterval;
    }

    protected override void OnUpdate()
    {
        if ( IsProxy ) return;

        if ( CurrentEvent == WeatherType.None && _nextEventTime )
        {
            StartRandomEvent();
        }
        else if ( CurrentEvent != WeatherType.None )
        {
            TimeRemaining = _eventEndTime;
            if ( _eventEndTime )
            {
                EndEvent();
            }
        }
    }

    protected override void OnFixedUpdate()
    {
        if ( CurrentEvent == WeatherType.None ) return;

        var players = Scene.GetAllComponents<CharacterController>();
        var rigidbodies = Scene.GetAllComponents<Rigidbody>();

        if ( CurrentEvent == WeatherType.LowGravity )
        {
            foreach ( var cc in players )
            {
                if ( cc.IsValid() )
                {
                    cc.Velocity += Vector3.Up * LowGravityForce * Time.Delta;
                }
            }
            foreach ( var rb in rigidbodies )
            {
                if ( rb.IsValid() && !rb.IsProxy )
                {
                    rb.ApplyImpulse( Vector3.Up * (rb.PhysicsBody.Mass * LowGravityForce * Time.Delta) );
                }
            }
        }
        else if ( CurrentEvent == WeatherType.StrongWind )
        {
            foreach ( var cc in players )
            {
                if ( cc.IsValid() )
                {
                    cc.Velocity += CurrentWindDirection * WindForce * Time.Delta;
                }
            }
            foreach ( var rb in rigidbodies )
            {
                if ( rb.IsValid() && !rb.IsProxy )
                {
                    rb.ApplyImpulse( CurrentWindDirection * WindForce * 15f * Time.Delta );
                }
            }
        }
        else if ( CurrentEvent == WeatherType.SlipperyIce )
        {
            foreach ( var cc in players )
            {
                if ( cc.IsValid() && cc.IsOnGround )
                {
                    var vel = cc.Velocity;
                    vel.x *= 1.015f;
                    vel.y *= 1.015f;
                    cc.Velocity = vel;
                }
            }
        }
    }

    private void StartRandomEvent()
    {
        var values = Enum.GetValues( typeof( WeatherType ) ).Cast<WeatherType>().Where( w => w != WeatherType.None ).ToArray();
        CurrentEvent = values[Random.Shared.Next( values.Length )];
        _eventEndTime = EventDuration;

        if ( CurrentEvent == WeatherType.StrongWind )
        {
            float angle = Random.Shared.NextSingle() * MathF.PI * 2f;
            CurrentWindDirection = new Vector3( MathF.Cos( angle ), MathF.Sin( angle ), 0f ).Normal;
        }

        Log.Info( $"[MapEventManager] Запущен погодный ивент: {CurrentEvent}" );
    }

    private void EndEvent()
    {
        Log.Info( $"[MapEventManager] Завершён ивент: {CurrentEvent}" );
        CurrentEvent = WeatherType.None;
        _nextEventTime = EventInterval;
    }
}
cherkbyte.one_more_push / PlayerAbilities.cs
Game game
using Sandbox;
using System;

public sealed class PlayerAbilities : Component
{
    [Property] public GameObject HeavyObjectPrefab { get; set; }

    [Property] public float DropCooldown { get; set; } = 8f;
    [Property] public float ShieldCooldown { get; set; } = 6f;
    [Property] public float ShieldDuration { get; set; } = 2f;

    public bool IsShieldActive { get; private set; } = false;

    private TimeSince _timeSinceDrop = 100f;
    private TimeSince _timeSinceShield = 100f;
    private TimeSince _shieldActiveTimer = 0f;

    private Rigidbody _rb;
    private float _originalMass = 0f;

    protected override void OnStart()
    {
        _rb = Components.Get<Rigidbody>();
        if ( _rb.IsValid() )
        {
            _originalMass = _rb.MassOverride;
        }
    }

    protected override void OnUpdate()
    {
        if ( IsProxy ) return;

        if ( IsShieldActive && _shieldActiveTimer > ShieldDuration )
        {
            DeactivateShield();
        }

        if ( Input.Pressed( "attack2" ) || Input.Pressed( "use" ) )
        {
            TryDropObject();
        }

        if ( Input.Pressed( "run" ) )
        {
            TryActivateShield();
        }
    }

    private void TryDropObject()
    {
        if ( _timeSinceDrop < DropCooldown )
        {
            Log.Info( $"[Способности] Сброс на перезарядке! Осталось: {DropCooldown - _timeSinceDrop:F1} сек." );
            return;
        }

        var headPos = WorldPosition + Vector3.Up * 50f;
        var forwardPos = headPos + WorldTransform.Forward * 600f;

        var tr = Scene.Trace.Ray( headPos, forwardPos )
                            .IgnoreGameObjectHierarchy( GameObject )
                            .Run();

        if ( tr.Hit && tr.GameObject.IsValid() )
        {
            var targetPos = tr.GameObject.WorldPosition;
            var spawnPos = targetPos + Vector3.Up * 180f;

            if ( HeavyObjectPrefab.IsValid() )
            {
                var obj = HeavyObjectPrefab.Clone( spawnPos );
                if ( obj.IsValid() && Network.Active )
                {
                    obj.NetworkSpawn();
                }

                _timeSinceDrop = 0;
                Log.Info( "[Способности] Тяжелый объект сброшен прямо на врага!" );
            }
            else
            {
                Log.Warning( "[Способности] HeavyObjectPrefab не назначен в Инспекторе!" );
            }
        }
        else
        {
            Log.Info( "[Способности] Никого не нашли перед собой для атаки!" );
        }
    }

    private void TryActivateShield()
    {
        if ( _timeSinceShield < ShieldCooldown || IsShieldActive ) return;

        IsShieldActive = true;
        _shieldActiveTimer = 0;
        _timeSinceShield = 0;

        if ( _rb.IsValid() )
        {
            _originalMass = _rb.MassOverride;
            _rb.MassOverride = 1000f; 
        }

        Log.Info( "[Способности] ЩИТ АКТИВИРОВАН!" );
    }

    private void DeactivateShield()
    {
        IsShieldActive = false;

        if ( _rb.IsValid() )
        {
            _rb.MassOverride = _originalMass; 
        }

        Log.Info( "[Способности] Действие щита закончилось." );
    }
}
cherkbyte.one_more_push / LeaderboardModal.razor
Game game
@using Sandbox;
@using Sandbox.UI;
@using Sandbox.Services;
@using System;
@using System.Threading.Tasks;
@using System.Collections.Generic;
@using System.Linq;
@inherits Panel

<root class="lb-root @(IsOpen ? "open" : "")">
    <div class="lb-card">
        <div class="lb-header">
            <div class="lb-title">GLOBAL LEADERBOARD</div>
            <button class="lb-close" onclick="@Toggle">✕</button>
        </div>

        <div class="lb-tabs">
            <button class="tab-btn @(CurrentStat == "pushes_count" ? "active" : "")" onclick="@(() => SelectStat("pushes_count"))">
                PUSHES
            </button>
            <button class="tab-btn @(CurrentStat == "rounds_won" ? "active" : "")" onclick="@(() => SelectStat("rounds_won"))">
                WINS
            </button>
        </div>

        <div class="lb-content">
            @if (IsLoading)
            {
                <div class="lb-status">LOADING DATA...</div>
            }
            else if (Entries == null || Entries.Count == 0)
            {
                <div class="lb-status">NO RECORD FOUND</div>
            }
            else
            {
                <div class="lb-table">
                    <div class="table-header">
                        <span class="col-rank">#</span>
                        <span class="col-player">PLAYER</span>
                        <span class="col-score">SCORE</span>
                    </div>
                    <div class="table-body">
                        @foreach (var entry in Entries)
                        {
                            <div class="table-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>
</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; }
    }

    public bool IsOpen { get; set; } = false;
    public bool IsLoading { get; set; } = true;
    public string CurrentStat { get; set; } = "pushes_count";
    public List<EntryItem> Entries { get; set; } = new();

    protected override void OnAfterTreeRender( bool firstTime )
    {
        base.OnAfterTreeRender( firstTime );
        if ( firstTime )
        {
            _ = RefreshData();
        }
    }

    public void Toggle()
    {
        IsOpen = !IsOpen;
        if (IsOpen)
        {
            _ = RefreshData();
        }
        StateHasChanged();
    }

    private async Task SelectStat(string statIdent)
    {
        if (CurrentStat == statIdent) return;
        CurrentStat = statIdent;
        await RefreshData();
    }

    private async Task RefreshData()
    {
        IsLoading = true;
        StateHasChanged();

        try
        {
            var board = Leaderboards.GetFromStat("seilordstudio.one_more_push", CurrentStat);
            board.SetAggregationSum();
            board.MaxEntries = 20;
            await board.Refresh();

            if (board.Entries != null)
            {
                Entries = 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
            {
                Entries = new();
            }
        }
        catch (System.Exception ex)
        {
            Log.Warning($"Leaderboard fetch failed: {ex.Message}");
        }

        IsLoading = false;
        StateHasChanged();
    }

    private string GetRankBadge(int rank)
    {
        return rank switch
        {
            1 => "🥇 1",
            2 => "🥈 2",
            3 => "🥉 3",
            _ => rank.ToString()
        };
    }
}
cherkbyte.one_more_push / RoundManager.cs
Game game
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

public sealed class RoundManager : Component
{
    public static RoundManager Instance { get; private set; }

    [Property] public float RoundTime { get; set; } = 120f;
    [Sync] public float TimeLeft { get; set; }
    [Sync] public bool IsInRound { get; set; }
    [Sync] public int CurrentRound { get; set; } = 1;

    protected override void OnAwake()
    {
        Instance = this;
    }

    protected override void OnStart()
    {
        StartNewRound();
    }

    public void StartNewRound()
    {
        TimeLeft = RoundTime;
        IsInRound = true;

        ResetArena();
        RespawnAllPlayers();

        var shrinker = Scene.GetAllComponents<ArenaShrinker>().FirstOrDefault();
        if ( shrinker.IsValid() )
        {
            shrinker.StartRound();
        }

        Log.Info( $"[RoundManager] Раунд {CurrentRound} начат!" );
    }

    public void OnPlayerEliminated( GameObject player )
    {
        if ( !IsInRound ) return;

        if ( player.IsValid() )
        {
            player.Enabled = false;
        }

        var activePlayers = Scene.GetAllComponents<PushAbility>()
            .Where( p => p.GameObject.IsValid() && p.GameObject.Enabled );

        if ( activePlayers.Count() <= 1 )
        {
            _ = EndRound();
        }
    }

    public async Task EndRound()
    {
        if ( !IsInRound ) return;

        var winner = Scene.GetAllComponents<PushAbility>()
            .FirstOrDefault( p => p.GameObject.IsValid() && p.GameObject.Enabled );

        if ( winner.IsValid() && !winner.IsProxy )
        {
            GameServices.RecordRoundWin();
        }

        _ = GameServices.SaveStatsAsync();

        IsInRound = false;
        CurrentRound++;

        Log.Info( $"Раунд {CurrentRound - 1} завершен! Перезапуск через 3 секунды..." );

        await Task.DelaySeconds( 3f );

        StartNewRound();
    }

    private void ResetArena()
    {
        var tiles = Scene.GetAllComponents<ArenaTile>();
        foreach ( var tile in tiles )
        {
            if ( tile.IsValid() )
            {
                tile.ResetTile();
            }
        }
    }

    private void RespawnAllPlayers()
    {
        var spawn = Scene.GetAllComponents<SpawnPoint>().FirstOrDefault();
        Vector3 spawnPos = spawn.IsValid() ? spawn.WorldPosition : new Vector3( 0, 0, 250 );

        var players = Scene.GetAllComponents<PushAbility>();
        foreach ( var player in players )
        {
            if ( !player.IsValid() ) continue;

            var root = player.GameObject.Root;
            root.Enabled = true;
            root.WorldPosition = spawnPos;

            var cc = root.Components.Get<CharacterController>( true );
            if ( cc.IsValid() )
            {
                cc.Velocity = Vector3.Zero;
            }

            var rb = root.Components.Get<Rigidbody>( true );
            if ( rb.IsValid() )
            {
                rb.Velocity = Vector3.Zero;
                rb.AngularVelocity = Vector3.Zero;
            }
        }
    }
}
cherkbyte.one_more_push / ShopManager.cs
Game game
using Sandbox;
using System.Collections.Generic;
using System.Linq;

public class PushItem
{
    public string Id { get; set; }
    public string NameRu { get; set; }
    public string NameEn { get; set; }
    public int Cost { get; set; }
    public float ForceMultiplier { get; set; }
    public string Key { get; set; }
    public bool IsUnlocked { get; set; }

    public string GetName( string lang ) => lang == "EN" ? NameEn : NameRu;
}

public class ShopManager : Component
{
    public static ShopManager Instance { get; private set; }

    public List<PushItem> Items { get; private set; } = new()
    {
        new PushItem { Id = "basic", NameRu = "Обычный", NameEn = "Basic", Cost = 0, ForceMultiplier = 1.0f, Key = "1", IsUnlocked = true },
        new PushItem { Id = "heavy", NameRu = "Тяжелый", NameEn = "Heavy", Cost = 10, ForceMultiplier = 1.5f, Key = "2", IsUnlocked = false },
        new PushItem { Id = "force", NameRu = "Импульс", NameEn = "Impulse", Cost = 25, ForceMultiplier = 2.2f, Key = "3", IsUnlocked = false },
        new PushItem { Id = "mega", NameRu = "Мега-толчок", NameEn = "Mega Push", Cost = 50, ForceMultiplier = 3.5f, Key = "4", IsUnlocked = false }
    };

    public PushItem SelectedItem { get; private set; }

    protected override void OnStart()
    {
        Instance = this;
        SelectedItem = Items.FirstOrDefault( x => x.IsUnlocked );
    }

    protected override void OnUpdate()
    {
        if ( Input.Pressed( "slot1" ) ) SelectByKey( "1" );
        if ( Input.Pressed( "slot2" ) ) SelectByKey( "2" );
        if ( Input.Pressed( "slot3" ) ) SelectByKey( "3" );
        if ( Input.Pressed( "slot4" ) ) SelectByKey( "4" );
    }

    public void SelectByKey( string key )
    {
        var item = Items.FirstOrDefault( x => x.Key == key );
        if ( item != null ) TryBuyOrSelect( item );
    }

    public bool TryBuyOrSelect( PushItem item )
    {
        if ( item.IsUnlocked )
        {
            SelectedItem = item;
            return true;
        }

        int currentScore = ScoreManager.Instance?.Score ?? 0;
        if ( currentScore >= item.Cost )
        {
            item.IsUnlocked = true;
            SelectedItem = item;
            return true;
        }

        return false;
    }

    public float GetCurrentForceMultiplier()
    {
        return SelectedItem?.ForceMultiplier ?? 1.0f;
    }
}
cherkbyte.one_more_push / MainMenu.razor
Game game
@namespace Sandbox
@using Sandbox;
@using Sandbox.UI;
@inherits PanelComponent

<root>
    <div class="background-image"></div>
    <div class="background-overlay"></div>

    <div class="header">
        <div class="logo">
            <span class="logo-text">@GetText("game_title")</span>
        </div>
        <div class="header-actions">
            <button class="icon-button" onclick="@OnSettingsClicked" onmouseover="@PlayHoverSound">
                <span>⚙️️ @GetText("settings_btn")</span>
            </button>
            <button class="icon-button lang" onclick="@OnLangClicked" onmouseover="@PlayHoverSound">
                <span>🌐 @GetText("lang_btn")</span>
            </button>
            <button class="icon-button exit-btn" onclick="@OnQuitClicked" onmouseover="@PlayHoverSound">
                <span>🚪 @GetText("exit_btn")</span>
            </button>
        </div>
    </div>

    <div class="main-content">
        <div class="modes-section">
            <div class="title-group">
                <div class="main-title">@GetText("title")</div>
                <div class="subtitle">@GetText("subtitle")</div>
            </div>

            <div class="modes-wrapper">
                <div class="mode-card sumo" onclick="@(() => OnModeClicked(GameMode.SumoPush))" onmouseover="@PlayHoverSound">
                    <div class="card-badge sumo-badge">@GetText("sumo_tag")</div>
                    <div class="card-icon-wrapper">
                        <span class="card-icon">🤼</span>
                    </div>
                    <div class="card-content">
                        <div class="mode-title">@GetText("sumo_title")</div>
                        <div class="mode-desc">@GetText("sumo_desc")</div>
                    </div>
                </div>

                <div class="mode-card nextbot disabled">
                    <div class="card-badge nextbot-badge disabled-badge">@GetText("nextbot_tag")</div>
                    <div class="card-icon-wrapper">
                        <span class="card-icon">🏃</span>
                    </div>
                    <div class="card-content">
                        <div class="mode-title">@GetText("nextbot_title")</div>
                        <div class="mode-desc">@GetText("nextbot_desc")</div>
                    </div>
                </div>
            </div>
        </div>

        <div class="news-panel">
            <div class="news-header">
                <span class="news-title">@GetText("news_title")</span>
                <span class="news-badge">@GetText("news_tag_new")</span>
            </div>
            <div class="news-list">
                <div class="news-item item-highlight" onmouseover="@PlayHoverSound">
                    <div class="news-item-title">🔥 @GetText("news_item1_title")</div>
                    <div class="news-item-desc">@GetText("news_item1_desc")</div>
                </div>
                <div class="news-item" onmouseover="@PlayHoverSound">
                    <div class="news-item-title">✨ @GetText("news_item2_title")</div>
                    <div class="news-item-desc">@GetText("news_item2_desc")</div>
                </div>
                <div class="news-item" onmouseover="@PlayHoverSound">
                    <div class="news-item-title">⚡ @GetText("news_item3_title")</div>
                    <div class="news-item-desc">@GetText("news_item3_desc")</div>
                </div>
            </div>
        </div>
    </div>

    @if ( IsSettingsOpen )
    {
        <div class="modal-overlay">
            <div class="modal-window">
                <div class="modal-header">
                    <div class="modal-title">@GetText("settings_title")</div>
                    <button class="close-x" onclick="@OnCloseSettingsClicked" onmouseover="@PlayHoverSound">✕</button>
                </div>

                <div class="modal-body">
                    <div class="setting-item">
                        <div class="setting-label">@GetText("volume_label"): @((int)(Volume * 100))%</div>
                        <div class="volume-buttons">
                            <button class="vol-btn" onclick="@(() => OnVolumeBtnClicked(0.25f))" onmouseover="@PlayHoverSound">25%</button>
                            <button class="vol-btn" onclick="@(() => OnVolumeBtnClicked(0.50f))" onmouseover="@PlayHoverSound">50%</button>
                            <button class="vol-btn" onclick="@(() => OnVolumeBtnClicked(0.75f))" onmouseover="@PlayHoverSound">75%</button>
                            <button class="vol-btn" onclick="@(() => OnVolumeBtnClicked(1.00f))" onmouseover="@PlayHoverSound">100%</button>
                        </div>
                    </div>

                    <div class="controls-section">
                        <div class="controls-title">@GetText("controls_title")</div>
                        <div class="control-row">@GetText("ctrl_move")</div>
                        <div class="control-row">@GetText("ctrl_action")</div>
                        <div class="control-row">@GetText("ctrl_space")</div>
                    </div>
                </div>

                <div class="modal-footer">
                    <button class="modal-close-btn" onclick="@OnCloseSettingsClicked" onmouseover="@PlayHoverSound">@GetText("close_btn")</button>
                </div>
            </div>
        </div>
    }

    @if ( IsLoading )
    {
        <div class="loading-overlay">
            <div class="loading-box">
                <div class="loading-mode-title">@(TargetMode == GameMode.SumoPush ? GetText("sumo_title") : GetText("nextbot_title"))</div>
                <div class="loading-status">@GetText("loading_title")</div>
                <div class="loading-sub">@GetText("loading_sub")</div>

                <div class="progress-bar-bg">
                    <div class="progress-bar-fill" style="width: @(LoadingProgress)%;"></div>
                </div>
                <div class="loading-percent">@((int)LoadingProgress)%</div>

                <div class="tip-box">
                    <span class="tip-label">@GetText("tip_label")</span>
                    <span class="tip-text">@(TargetMode == GameMode.SumoPush ? GetText("tip_sumo") : GetText("tip_nextbot"))</span>
                </div>
            </div>
        </div>
    }
</root>

@code {
    [Property] public SoundEvent HoverSound { get; set; }
    [Property] public SoundEvent ClickSound { get; set; }

    private bool IsSettingsOpen { get; set; } = false;
    private bool IsLoading { get; set; } = false;
    private GameMode TargetMode { get; set; }
    private float LoadingProgress { get; set; } = 0f;
    private TimeSince TimeSinceLoadingStarted;
    private float Volume { get; set; } = 1.0f;
    private const float TotalLoadingTime = 6.0f;

    private void PlayHoverSound()
    {
        if ( HoverSound != null )
            Sound.Play( HoverSound );
        else
            Sound.Play( "ui.button.hover" );
    }

    private void PlayClickSound()
    {
        if ( ClickSound != null )
            Sound.Play( ClickSound );
        else
            Sound.Play( "ui.button.press" );
    }

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

        if ( IsLoading )
        {
            float elapsed = TimeSinceLoadingStarted;
            LoadingProgress = MathX.Clamp( (elapsed / TotalLoadingTime) * 100f, 0f, 100f );

            if ( elapsed >= TotalLoadingTime )
            {
                IsLoading = false;
                if ( ModeController.Instance != null )
                {
                    ModeController.Instance.SelectMode( TargetMode );
                }
            }
        }
    }

    private void OnModeClicked( GameMode mode )
    {
        PlayClickSound();
        StartLoading( mode );
    }

    private void OnSettingsClicked()
    {
        PlayClickSound();
        ToggleSettings();
    }

    private void OnCloseSettingsClicked()
    {
        PlayClickSound();
        ToggleSettings();
    }

    private void OnLangClicked()
    {
        PlayClickSound();
        OnToggleLanguage();
    }

    private void OnQuitClicked()
    {
        PlayClickSound();
        Game.Close();
    }

    private void OnVolumeBtnClicked( float val )
    {
        PlayClickSound();
        SetVolume( val );
    }

    private void StartLoading( GameMode mode )
    {
        if ( IsLoading ) return;

        TargetMode = mode;
        IsLoading = true;
        LoadingProgress = 0f;
        TimeSinceLoadingStarted = 0;
    }

    private void ToggleSettings()
    {
        if ( IsLoading ) return;
        IsSettingsOpen = !IsSettingsOpen;
    }

    private void SetVolume( float val )
    {
        Volume = val;
        if ( ModeController.Instance != null )
        {
            ModeController.Instance.MasterVolume = val;
        }
    }

    private void OnToggleLanguage()
    {
        if ( IsLoading ) return;
        if ( ModeController.Instance != null )
        {
            ModeController.Instance.ToggleLanguage();
        }
    }

    private string GetText( string key )
    {
        return ModeController.Instance?.GetText( key ) ?? key;
    }
}
cherkbyte.one_more_push / MetaProgression.cs
Game game
using Sandbox;
using System;

public sealed class MetaProgression : Component
{
	[Property] public int Credits { get; private set; } = 0;

	// Уровни мета-прокачки
	[Property] public int ForceLevel { get; private set; } = 0;
	[Property] public int MassLevel { get; private set; } = 0;
	[Property] public int EnergyLevel { get; private set; } = 0;

	// Престиж
	[Property] public int PrestigeLevel { get; private set; } = 0;
	[Property] public int PrestigeTokens { get; private set; } = 0;

	// Требования для сброса (сумма уровней прокачки)
	public const int REQUIRED_TOTAL_LEVELS_FOR_PRESTIGE = 15;

	private const string SAVE_FILE = "one_more_push_meta.json";

	protected override void OnStart()
	{
		LoadProgress();
	}

	public void AddCredits( int amount )
	{
		// Множитель престижа (+50% за каждый уровень престижа)
		float multiplier = 1.0f + (PrestigeLevel * 0.5f);
		Credits += (int)(amount * multiplier);
		SaveProgress();
	}

	public int GetUpgradeCost( int currentLevel )
	{
		return (int)(100 * Math.Pow( 1.5, currentLevel ));
	}

	public bool TryUpgradeForce()
	{
		int cost = GetUpgradeCost( ForceLevel );
		if ( Credits >= cost )
		{
			Credits -= cost;
			ForceLevel++;
			SaveProgress();
			return true;
		}
		return false;
	}

	public bool TryUpgradeMass()
	{
		int cost = GetUpgradeCost( MassLevel );
		if ( Credits >= cost )
		{
			Credits -= cost;
			MassLevel++;
			SaveProgress();
			return true;
		}
		return false;
	}

	public bool TryUpgradeEnergy()
	{
		int cost = GetUpgradeCost( EnergyLevel );
		if ( Credits >= cost )
		{
			Credits -= cost;
			EnergyLevel++;
			SaveProgress();
			return true;
		}
		return false;
	}

	// Проверка: готов ли игрок к престижу
	public bool CanPrestige()
	{
		int totalLevels = ForceLevel + MassLevel + EnergyLevel;
		return totalLevels >= REQUIRED_TOTAL_LEVELS_FOR_PRESTIGE;
	}

	// Сброс прогресса ради Престижа
	public bool TryPrestige()
	{
		if ( !CanPrestige() ) return false;

		// Сбрасываем обычные уровни и кредиты
		ForceLevel = 0;
		MassLevel = 0;
		EnergyLevel = 0;
		Credits = 0;

		// Повышаем уровень престижа и выдаем токены
		PrestigeLevel++;
		PrestigeTokens += 1;

		SaveProgress();
		return true;
	}

	public void SaveProgress()
	{
		var data = new ProgressData
		{
			Credits = this.Credits,
			ForceLevel = this.ForceLevel,
			MassLevel = this.MassLevel,
			EnergyLevel = this.EnergyLevel,
			PrestigeLevel = this.PrestigeLevel,
			PrestigeTokens = this.PrestigeTokens
		};

		FileSystem.Data.WriteJson( SAVE_FILE, data );
	}

	public void LoadProgress()
	{
		if ( FileSystem.Data.FileExists( SAVE_FILE ) )
		{
			var data = FileSystem.Data.ReadJson<ProgressData>( SAVE_FILE );
			if ( data != null )
			{
				Credits = data.Credits;
				ForceLevel = data.ForceLevel;
				MassLevel = data.MassLevel;
				EnergyLevel = data.EnergyLevel;
				PrestigeLevel = data.PrestigeLevel;
				PrestigeTokens = data.PrestigeTokens;
			}
		}
	}

	private class ProgressData
	{
		public int Credits { get; set; }
		public int ForceLevel { get; set; }
		public int MassLevel { get; set; }
		public int EnergyLevel { get; set; }
		public int PrestigeLevel { get; set; }
		public int PrestigeTokens { get; set; }
	}
}
cherkbyte.one_more_push / ModeController.cs
Game game
using Sandbox;

public enum GameMode
{
    SumoPush,
    NextbotRun
}

public enum Language
{
    English,
    Russian
}

public sealed class ModeController : Component
{
    public static ModeController Instance { get; private set; }

    [Property] public GameMode SelectedMode { get; set; } = GameMode.SumoPush;
    [Property] public Language CurrentLanguage { get; set; } = Language.Russian;
    [Property] public float MasterVolume { get; set; } = 1.0f;

    [Property] public SceneFile SumoScene { get; set; }
    [Property] public SceneFile NextbotScene { get; set; }

    protected override void OnAwake()
    {
        Instance = this;
    }

    public void SelectMode( GameMode mode )
    {
        SelectedMode = mode;

        if ( mode == GameMode.SumoPush )
        {
            if ( SumoScene != null )
                Game.ActiveScene.Load( SumoScene );
            else
                Game.ActiveScene.LoadFromFile( "scenes/onemore.scene" );
        }
        else if ( mode == GameMode.NextbotRun )
        {
            if ( NextbotScene != null )
                Game.ActiveScene.Load( NextbotScene );
            else
                Game.ActiveScene.LoadFromFile( "scenes/onemores2.scene" );
        }
    }

    public void ToggleLanguage()
    {
        CurrentLanguage = CurrentLanguage == Language.Russian ? Language.English : Language.Russian;
    }

    public string GetText( string key )
    {
        bool isRu = CurrentLanguage == Language.Russian;

        return key switch
        {
            "game_title" => "ONE MORE PUSH",
            "title" => isRu ? "ВЫБЕРИТЕ РЕЖИМ" : "SELECT GAME MODE",
            "subtitle" => isRu ? "Выберите режим и отправляйтесь на арену" : "Choose your mode and jump into action",
            "sumo_title" => "SUMO PUSH",
            "sumo_tag" => isRu ? "КЛАССИКА" : "CLASSIC",
            "sumo_desc" => isRu ? "Толкай врагов и ботов с платформы, пока она сужается!" : "Push enemies and bots off the platform before it shrinks!",
            "nextbot_title" => "NEXTBOT RUN",
            "nextbot_tag" => isRu ? "СКОРО" : "COMING SOON",
            "nextbot_desc" => isRu ? "Убегай от некстботов, спасай нокаутированных союзников и выживай!" : "Run from nextbots, revive fallen teammates, and survive!",
            "lang_btn" => isRu ? "RU" : "EN",
            "settings_btn" => isRu ? "НАСТРОЙКИ" : "SETTINGS",
            "exit_btn" => isRu ? "ВЫХОД" : "EXIT",
            "settings_title" => isRu ? "НАСТРОЙКИ" : "SETTINGS",
            "volume_label" => isRu ? "Громкость звука" : "Master Volume",
            "controls_title" => isRu ? "Управление" : "Controls",
            "ctrl_move" => isRu ? "WASD — Перемещение" : "WASD — Movement",
            "ctrl_action" => isRu ? "ЛКМ / E — Атака / Спасение" : "LMB / E — Push / Revive",
            "ctrl_space" => isRu ? "Пробел — Прыжок" : "Space — Jump",
            "close_btn" => isRu ? "ЗАКРЫТЬ" : "CLOSE",
            "news_title" => isRu ? "НОВОСТИ И ОБНОВЛЕНИЯ" : "NEWS & UPDATES",
            "news_tag_new" => isRu ? "ПАТЧ 1.2" : "PATCH 1.2",
            "news_item1_title" => isRu ? "Режим Nextbot Run" : "Nextbot Run Mode",
            "news_item1_desc" => isRu ? "Режим находится в разработке и скоро будет доступен!" : "Mode in development and coming soon!",
            "news_item2_title" => isRu ? "Обновлённый Glass UI" : "New Glass UI",
            "news_item2_desc" => isRu ? "Современный интерфейс, настройки звука, выбор языка и экран загрузки." : "Sleek glass UI design, audio settings, multi-language support & loading screen.",
            "news_item3_title" => isRu ? "Баланс Sumo Push" : "Sumo Push Balance",
            "news_item3_desc" => isRu ? "Улучшена физика толчков и ускорена динамика сужения платформы!" : "Improved push physics and faster platform shrinking dynamics!",
            "loading_title" => isRu ? "ЗАГРУЗКА АРЕНЫ..." : "LOADING ARENA...",
            "loading_sub" => isRu ? "Подготовка геометрии и генерация объектов..." : "Preparing geometry and spawning objects...",
            "tip_label" => isRu ? "СОВЕТ:" : "TIP:",
            "tip_sumo" => isRu ? "Следите за мигающими блоками платформы — они упадут первыми!" : "Watch out for flashing platform blocks — they fall first!",
            "tip_nextbot" => isRu ? "Держаться вместе с союзниками проще: вы сможете быстрее поднять друг друга!" : "Stick together with teammates: reviving is much faster when close!",
            _ => key
        };
    }
}
Debug: View Raw JSON Response
{
    "TotalCount": 51,
    "Files": [
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "BotAI.cs",
            "FileName": "BotAI.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System;\r\nusing System.Linq;\r\n\r\npublic sealed class BotAI : Component\r\n{\r\n    [Property] public float Speed { get; set; } = 220f;\r\n    [Property] public float AttackRange { get; set; } = 220f;\r\n\r\n    private CharacterController _cc;\r\n    private PushAbility _push;\r\n    private ManeuverAbility _maneuver;\r\n    private TimeUntil _nextDecision;\r\n    private Vector3 _moveDir;\r\n\r\n    protected override void OnAwake()\r\n    {\r\n        _cc = Components.Get<CharacterController>();\r\n        _push = Components.Get<PushAbility>();\r\n        _maneuver = Components.Get<ManeuverAbility>();\r\n    }\r\n\r\n    protected override void OnUpdate()\r\n    {\r\n        if ( IsProxy ) return;\r\n\r\n        var target = FindTarget();\r\n\r\n        if ( target.IsValid() )\r\n        {\r\n            _moveDir = (target.WorldPosition - WorldPosition).WithZ( 0 ).Normal;\r\n\r\n            if ( _cc.IsValid() )\r\n            {\r\n                _cc.Velocity = _moveDir * Speed;\r\n                WorldRotation = Rotation.LookAt( _moveDir );\r\n            }\r\n\r\n            float dist = Vector3.DistanceBetween( WorldPosition, target.WorldPosition );\r\n            if ( dist <= AttackRange && _push.IsValid() && _push.IsReady )\r\n            {\r\n                _push.TryExecuteBotPush();\r\n            }\r\n        }\r\n        else\r\n        {\r\n            if ( _nextDecision )\r\n            {\r\n                _nextDecision = Game.Random.Float( 2f, 4f );\r\n                float angle = Game.Random.Float( 0f, MathF.PI * 2f );\r\n                _moveDir = new Vector3( MathF.Cos( angle ), MathF.Sin( angle ), 0f );\r\n            }\r\n\r\n            if ( _cc.IsValid() )\r\n            {\r\n                _cc.Velocity = _moveDir * (Speed * 0.6f);\r\n            }\r\n        }\r\n\r\n        if ( _cc.IsValid() && !_cc.IsOnGround && _maneuver.IsValid() && _maneuver.IsDashReady )\r\n        {\r\n            _maneuver.TryExecuteBotDash( -_cc.Velocity.Normal );\r\n        }\r\n    }\r\n\r\n    private GameObject FindTarget()\r\n    {\r\n        return Scene.GetAllComponents<CharacterController>()\r\n            .Where( c => c.GameObject != GameObject && c.GameObject.IsValid() )\r\n            .OrderBy( c => Vector3.DistanceBetween( WorldPosition, c.WorldPosition ) )\r\n            .FirstOrDefault()?.GameObject;\r\n    }\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "BouncyShield.cs",
            "FileName": "BouncyShield.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\n\r\npublic sealed class BouncyShield : Component, Component.ITriggerListener\r\n{\r\n\t[Property] public float BounceForce { get; set; } = 1500f;\r\n\t[Property] public SoundEvent BounceSound { get; set; }\r\n\r\n\tpublic void OnTriggerEnter( Collider other )\r\n\t{\r\n\t\tvar rootObj = other.GameObject.Root;\r\n\t\tvar pushDir = (rootObj.WorldPosition - WorldPosition).Normal;\r\n\t\tpushDir.z = 0.3f;\r\n\r\n\t\tbool bounced = false;\r\n\r\n\t\tvar cc = rootObj.Components.Get<CharacterController>();\r\n\t\tif ( cc.IsValid() )\r\n\t\t{\r\n\t\t\tcc.Velocity = pushDir * BounceForce;\r\n\t\t\tbounced = true;\r\n\t\t}\r\n\r\n\t\tvar rb = rootObj.Components.Get<Rigidbody>();\r\n\t\tif ( rb.IsValid() )\r\n\t\t{\r\n\t\t\trb.Velocity = pushDir * BounceForce;\r\n\t\t\tbounced = true;\r\n\t\t}\r\n\r\n\t\tif ( bounced )\r\n\t\t{\r\n\t\t\tif ( BounceSound != null )\r\n\t\t\t{\r\n\t\t\t\tSound.Play( BounceSound, WorldPosition );\r\n\t\t\t}\r\n\t\t\tCameraJuice.Instance?.Shake( 12f );\r\n\t\t}\r\n\t}\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "EmoteWheel.cs",
            "FileName": "EmoteWheel.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\n\r\npublic sealed class EmoteWheel : Component\r\n{\r\n\t[Property] public string CurrentEmote { get; private set; } = \"\";\r\n\t[Property] public float EmoteDuration { get; set; } = 2.5f;\r\n\r\n\tprivate TimeSince _emoteTimer;\r\n\r\n\tpublic void PlayEmote( string emoteIcon )\r\n\t{\r\n\t\tCurrentEmote = emoteIcon;\r\n\t\t_emoteTimer = 0;\r\n\t}\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\tif ( !string.IsNullOrEmpty( CurrentEmote ) && _emoteTimer > EmoteDuration )\r\n\t\t{\r\n\t\t\tCurrentEmote = \"\";\r\n\t\t}\r\n\t}\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "PlayerHealth.cs",
            "FileName": "PlayerHealth.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System;\r\nusing System.Linq;\r\n\r\npublic sealed class PlayerHealth : Component\r\n{\r\n\t[Property] public float MaxHealth { get; set; } = 100f;\r\n\t[Property] public float CurrentHealth { get; private set; } = 100f;\r\n\r\n\tpublic bool IsDead => CurrentHealth <= 0f;\r\n\tpublic float RespawnTimer { get; private set; } = 0f;\r\n\tpublic float RespawnDuration { get; set; } = 3f;\r\n\r\n\tprivate Vector3 _lastDamageDirection = Vector3.Zero;\r\n\r\n\tprotected override void OnStart()\r\n\t{\r\n\t\tCurrentHealth = MaxHealth;\r\n\t}\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\tif ( IsDead )\r\n\t\t{\r\n\t\t\tRespawnTimer -= Time.Delta;\r\n\t\t\tif ( RespawnTimer <= 0f )\r\n\t\t\t{\r\n\t\t\t\tRespawn();\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tpublic void TakeDamage( float damage )\r\n\t{\r\n\t\tTakeDamage( damage, Vector3.Zero );\r\n\t}\r\n\r\n\tpublic void TakeDamage( float damage, Vector3 direction )\r\n\t{\r\n\t\tif ( IsDead ) return;\r\n\r\n\t\t_lastDamageDirection = direction;\r\n\t\tCurrentHealth = MathX.Clamp( CurrentHealth - damage, 0f, MaxHealth );\r\n\r\n\t\tif ( CurrentHealth <= 0f )\r\n\t\t{\r\n\t\t\tDie();\r\n\t\t}\r\n\t}\r\n\r\n\tpublic void Heal( float amount )\r\n\t{\r\n\t\tif ( IsDead ) return;\r\n\t\tCurrentHealth = MathX.Clamp( CurrentHealth + amount, 0f, MaxHealth );\r\n\t}\r\n\r\n\tprivate void Die()\r\n\t{\r\n\t\tRespawnTimer = RespawnDuration;\r\n\r\n\t\tif ( ScoreManager.Instance != null )\r\n\t\t{\r\n\t\t\tScoreManager.Instance.AddDeath();\r\n\t\t}\r\n\r\n\t\tvar ragdoll = Components.Get<PlayerRagdoll>();\r\n\t\tif ( ragdoll.IsValid() )\r\n\t\t{\r\n\t\t\tragdoll.SpawnRagdoll( _lastDamageDirection * 2500f );\r\n\t\t}\r\n\r\n\t\tvar skm = Components.Get<SkinnedModelRenderer>();\r\n\t\tif ( skm.IsValid() ) skm.Enabled = false;\r\n\r\n\t\tvar mr = Components.Get<ModelRenderer>();\r\n\t\tif ( mr.IsValid() ) mr.Enabled = false;\r\n\r\n\t\tvar cc = Components.Get<CharacterController>();\r\n\t\tif ( cc.IsValid() ) cc.Enabled = false;\r\n\r\n\t\tvar rb = Components.Get<Rigidbody>();\r\n\t\tif ( rb.IsValid() ) rb.Enabled = false;\r\n\r\n\t\tCameraJuice.Instance?.Shake( 30f, 6f );\r\n\r\n\t\tLog.Info( \"[PlayerHealth] \u0418\u0433\u0440\u043e\u043a \u043f\u043e\u0433\u0438\u0431!\" );\r\n\t}\r\n\r\n\tpublic void Respawn()\r\n\t{\r\n\t\tCurrentHealth = MaxHealth;\r\n\r\n\t\tvar skm = Components.Get<SkinnedModelRenderer>();\r\n\t\tif ( skm.IsValid() ) skm.Enabled = true;\r\n\r\n\t\tvar mr = Components.Get<ModelRenderer>();\r\n\t\tif ( mr.IsValid() ) mr.Enabled = true;\r\n\r\n\t\tvar cc = Components.Get<CharacterController>();\r\n\t\tif ( cc.IsValid() ) cc.Enabled = true;\r\n\r\n\t\tvar rb = Components.Get<Rigidbody>();\r\n\t\tif ( rb.IsValid() ) rb.Enabled = true;\r\n\r\n\t\tvar spawnPoint = Scene.GetAllComponents<SpawnPoint>().FirstOrDefault();\r\n\t\tif ( spawnPoint.IsValid() )\r\n\t\t{\r\n\t\t\tTransform.World = spawnPoint.Transform.World;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tTransform.LocalPosition = Vector3.Up * 50f;\r\n\t\t}\r\n\r\n\t\tLog.Info( \"[PlayerHealth] \u0418\u0433\u0440\u043e\u043a \u0432\u043e\u0437\u0440\u043e\u0434\u0438\u043b\u0441\u044f!\" );\r\n\t}\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "PushHud.razor",
            "FileName": "PushHud.razor",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "@using Sandbox;\r\n@using Sandbox.UI;\r\n@using Sandbox.Services;\r\n@using System;\r\n@using System.Linq;\r\n@using System.Collections.Generic;\r\n@using System.Threading.Tasks;\r\n@inherits PanelComponent\r\n@attribute [StyleSheet( \"PushHud.scss\" )]\r\n\r\n<root class=\"hud-root\">\r\n\r\n    <div class=\"top-header-container\">\r\n        <div class=\"score-box\">\r\n            <div class=\"score-card time\">\r\n                <label class=\"score-title\">@L( \"\u0412\u0440\u0435\u043c\u044f\", \"Time\" )</label>\r\n                <label class=\"score-value\">@GetFormattedTime()</label>\r\n            </div>\r\n            <div class=\"score-card\">\r\n                <label class=\"score-title\">@L( \"\u0421\u0431\u0438\u0442\u043e\", \"Score\" )</label>\r\n                <label class=\"score-value\">@GetScore()</label>\r\n            </div>\r\n            <div class=\"score-card death\">\r\n                <label class=\"score-title\">@L( \"\u0421\u043c\u0435\u0440\u0442\u0438\", \"Deaths\" )</label>\r\n                <label class=\"score-value\">@GetDeaths()</label>\r\n            </div>\r\n            <div class=\"score-card highlight\">\r\n                <label class=\"score-title\">@L( \"\u0420\u0435\u043a\u043e\u0440\u0434\", \"Best\" )</label>\r\n                <label class=\"score-value\">@GetHighScore()</label>\r\n            </div>\r\n        </div>\r\n\r\n        <div class=\"round-badge\">\r\n            @L( \"\u0420\u0410\u0423\u041d\u0414\", \"ROUND\" ) @(RoundManager.Instance?.CurrentRound ?? 1)\r\n        </div>\r\n\r\n        @if ( MapEventManager.Instance != null && MapEventManager.Instance.CurrentEvent != WeatherType.None )\r\n        {\r\n            <div class=\"weather-banner @GetWeatherClass( MapEventManager.Instance.CurrentEvent )\">\r\n                <label class=\"weather-icon\">@GetWeatherIcon( MapEventManager.Instance.CurrentEvent )</label>\r\n                <div class=\"weather-info\">\r\n                    <label class=\"weather-title\">@GetWeatherTitle( MapEventManager.Instance.CurrentEvent )</label>\r\n                    <label class=\"weather-sub\">@L( \"\u0410\u041a\u0422\u0418\u0412\u041d\u0410\u042f \u0410\u041d\u041e\u041c\u0410\u041b\u0418\u042f\", \"ACTIVE ANOMALY\" )</label>\r\n                </div>\r\n                <div class=\"weather-timer-badge\">\r\n                    <label class=\"timer-text\">@($\"{MapEventManager.Instance.TimeRemaining:F1}s\")</label>\r\n                </div>\r\n            </div>\r\n        }\r\n    </div>\r\n\r\n    <div class=\"scoreboard-box\">\r\n        <div class=\"scoreboard-header\">\r\n            <label class=\"header-title\">@L( \"\u0418\u0413\u0420\u041e\u041a\u0418\", \"PLAYERS\" )</label>\r\n        </div>\r\n        <div class=\"player-list\">\r\n            @foreach ( var p in GetPlayerList() )\r\n            {\r\n                <div class=\"player-row @(p.IsLocal ? \"local\" : \"\")\">\r\n                    <label class=\"player-name\">@p.Name</label>\r\n                    <label class=\"player-score\">@p.Score</label>\r\n                </div>\r\n            }\r\n        </div>\r\n    </div>\r\n\r\n    <div class=\"killfeed-container\">\r\n        @if ( Killfeed.Instance != null )\r\n        {\r\n            @foreach ( var entry in Killfeed.Instance.Entries )\r\n            {\r\n                <div class=\"kill-entry\">\r\n                    <label class=\"killer\">@entry.KillerName</label>\r\n                    <label class=\"icon\">\ud83d\udca5 [@entry.Method]</label>\r\n                    <label class=\"victim\">@entry.VictimName</label>\r\n                </div>\r\n            }\r\n        }\r\n    </div>\r\n\r\n    <div class=\"hotbar-container\">\r\n        @if ( ShopManager.Instance != null )\r\n        {\r\n            @foreach ( var item in ShopManager.Instance.Items )\r\n            {\r\n                bool isSelected = item == ShopManager.Instance.SelectedItem;\r\n                \r\n                <div class=\"slot-wrapper\">\r\n                    <div class=\"hotbar-slot @(isSelected ? \"selected\" : \"\") @(!item.IsUnlocked ? \"locked\" : \"\")\" \r\n                         onclick=\"@(() => ShopManager.Instance.TryBuyOrSelect(item))\">\r\n                        \r\n                        <label class=\"slot-name\">@item.GetName( CurrentLanguage )</label>\r\n                        \r\n                        @if ( !item.IsUnlocked )\r\n                        {\r\n                            <label class=\"slot-cost\">\ud83d\udcb0 @item.Cost</label>\r\n                        }\r\n                    </div>\r\n                    <div class=\"key-badge @(isSelected ? \"selected\" : \"\")\">@item.Key</div>\r\n                </div>\r\n            }\r\n        }\r\n    </div>\r\n\r\n    <div class=\"top-left-box\">\r\n        <div class=\"chat-box @(IsChatOpen ? \"open\" : \"\")\">\r\n            <div class=\"chat-messages\">\r\n                @foreach ( var msg in CanvasMessages )\r\n                {\r\n                    <div class=\"chat-row\">\r\n                        <label class=\"chat-author\">@msg.Author:</label>\r\n                        <label class=\"chat-text\">@msg.Text</label>\r\n                    </div>\r\n                }\r\n            </div>\r\n\r\n            @if ( IsChatOpen )\r\n            {\r\n                <div class=\"chat-input-row\">\r\n                    <textentry @ref=\"ChatInput\" onsubmit=\"@SendChatMessage\" placeholder=\"@L( \"\u0412\u0432\u0435\u0434\u0438\u0442\u0435 \u0441\u043e\u043e\u0431\u0449\u0435\u043d\u0438\u0435...\", \"Type a message...\" )\"></textentry>\r\n                </div>\r\n            }\r\n            else\r\n            {\r\n                <div class=\"hints-row\">\r\n                    <label class=\"chat-hint\" onclick=\"@OpenChat\">@L( \"\u041d\u0430\u0436\u043c\u0438\u0442\u0435 [Enter] \u0434\u043b\u044f \u0447\u0430\u0442\u0430\", \"Press [Enter] to chat\" )</label>\r\n                    <label class=\"chat-hint prop-hint\" onclick=\"@ToggleWorkshopMenu\">@L( \"[E] \u0421\u043f\u0430\u0432\u043d-\u043c\u0435\u043d\u044e | [R] \u041c\u0430\u0441\u0442\u0435\u0440\u0441\u043a\u0430\u044f\", \"[E] Spawnmenu | [R] Workshop\" )</label>\r\n                </div>\r\n            }\r\n        </div>\r\n    </div>\r\n\r\n    <div class=\"player-status-panel\">\r\n        <div class=\"status-box health-box\">\r\n            <div class=\"status-header\">\r\n                <label class=\"status-title\">\u2764\ufe0f @L( \"\u0417\u0414\u041e\u0420\u041e\u0412\u042c\u0415\", \"HEALTH\" )</label>\r\n                <label class=\"status-val\">@($\"{GetHealth():F0}\") / @($\"{GetMaxHealth():F0}\")</label>\r\n            </div>\r\n            <div class=\"bar-background\">\r\n                <div class=\"bar-fill health\" style=\"width: @($\"{GetHealthPercent():F1}%\");\"></div>\r\n            </div>\r\n        </div>\r\n\r\n        <div class=\"status-box cooldown-box\">\r\n            <div class=\"status-header\">\r\n                <label class=\"status-title\">\u26a1 @L( \"\u041f\u0415\u0420\u0415\u0417\u0410\u0420\u042f\u0414\u041a\u0410\", \"COOLDOWN\" )</label>\r\n            </div>\r\n            <div class=\"bar-background\">\r\n                <div class=\"bar-fill\" style=\"width: @($\"{GetPercent():F1}%\");\"></div>\r\n            </div>\r\n        </div>\r\n    </div>\r\n\r\n    @if ( PushAbility != null && PushAbility.IsCharging )\r\n    {\r\n        <div class=\"charge-box\">\r\n            <label class=\"charge-title\">@L( \"\u0417\u0410\u0420\u042f\u0414\", \"CHARGE\" )</label>\r\n            <div class=\"bar-background\">\r\n                <div class=\"bar-fill charge\" style=\"width: @($\"{PushAbility.ChargeProgress * 100f:F1}%\");\"></div>\r\n            </div>\r\n        </div>\r\n    }\r\n\r\n    @if ( LocalHealth != null && LocalHealth.IsDead )\r\n    {\r\n        <div class=\"respawn-overlay\">\r\n            <div class=\"respawn-card\">\r\n                <label class=\"death-title\">\ud83d\udc80 @L( \"\u0412\u042b \u041f\u041e\u0413\u0418\u0411\u041b\u0418!\", \"YOU DIED!\" )</label>\r\n                <label class=\"respawn-subtitle\">\r\n                    @L( \"\u0412\u043e\u0437\u0440\u043e\u0436\u0434\u0435\u043d\u0438\u0435 \u0447\u0435\u0440\u0435\u0437:\", \"Respawning in:\" )\r\n                </label>\r\n                <label class=\"respawn-timer\">@($\"{Math.Max( 0, LocalHealth.RespawnTimer ):F1}s\")</label>\r\n            </div>\r\n        </div>\r\n    }\r\n\r\n    @if ( !IsMenuOpen && !IsSettingsOpen && !IsWorkshopMenuOpen && !IsQSpawnMenuOpen )\r\n    {\r\n        <button class=\"side-settings-btn\" onclick=\"@OpenMenu\">\r\n            <label class=\"key-hint\">[ESC]</label>\r\n            <label class=\"icon\">\u2699</label>\r\n        </button>\r\n    }\r\n\r\n    @if ( IsQSpawnMenuOpen )\r\n    {\r\n        <div class=\"spawnmenu-overlay\" onclick=\"@CloseQSpawnMenu\">\r\n            <div class=\"spawnmenu-window\" onclick=\"@((e) => e.StopPropagation())\">\r\n                \r\n                <div class=\"spawnmenu-topbar\">\r\n                    <div class=\"brand-badge\">\r\n                        <label class=\"brand-text\">ONE MORE PUSH</label>\r\n                        <label class=\"sub-text\">SPAWN MENU</label>\r\n                    </div>\r\n\r\n                    <div class=\"topbar-nav\">\r\n                        <button class=\"topbar-tab active\">\ud83d\udce6 @L( \"\u041f\u0420\u041e\u041f\u0421\u042b \u0418 \u0410\u0420\u0415\u041d\u0410\", \"PROPS & ARENA\" )</button>\r\n                        <button class=\"topbar-tab\" onclick=\"@ToggleWorkshopMenu\">\u26a1 @L( \"\u041c\u0410\u0421\u0422\u0415\u0420\u0421\u041a\u0410\u042f [R]\", \"WORKSHOP [R]\" )</button>\r\n                    </div>\r\n\r\n                    <div class=\"topbar-credits\">\r\n                        <label class=\"credits-label\">@L( \"\u0411\u0410\u041b\u0410\u041d\u0421:\", \"BALANCE:\" )</label>\r\n                        <label class=\"credits-val\">\ud83d\udcb0 @GetCredits()</label>\r\n                    </div>\r\n                </div>\r\n\r\n                <div class=\"spawnmenu-body\">\r\n                    <div class=\"spawnmenu-sidebar\">\r\n                        <label class=\"sidebar-title\">@L( \"\u041a\u0410\u0422\u0415\u0413\u041e\u0420\u0418\u0418\", \"CATEGORIES\" )</label>\r\n                        <div class=\"sidebar-item active\">\ud83d\udca5 @L( \"\u0418\u043d\u0442\u0435\u0440\u0430\u043a\u0442\u0438\u0432\", \"Interactive\" )</div>\r\n                        <div class=\"sidebar-item\">\ud83d\udee2\ufe0f @L( \"\u041e\u043f\u0430\u0441\u043d\u044b\u0435 \u0437\u043e\u043d\u044b\", \"Hazard Zones\" )</div>\r\n                        <div class=\"sidebar-item\">\u2693 @L( \"\u0422\u044f\u0436\u0435\u043b\u044b\u0435 \u043e\u0431\u044a\u0435\u043a\u0442\u044b\", \"Heavy Objects\" )</div>\r\n                        <div class=\"sidebar-item\">\ud83c\udf00 @L( \"\u041b\u043e\u0432\u0443\u0448\u043a\u0438 & \u0411\u0430\u0442\u0443\u0442\u044b\", \"Traps & Springs\" )</div>\r\n                    </div>\r\n\r\n                    <div class=\"spawnmenu-content\">\r\n                        <div class=\"content-header\">\r\n                            <label class=\"section-title\">@L( \"\u0421\u041f\u0410\u0412\u041d \u041e\u0411\u042a\u0415\u041a\u0422\u041e\u0412\", \"SPAWN OBJECTS\" )</label>\r\n                            <label class=\"section-desc\">@L( \"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u043f\u0440\u0435\u0434\u043c\u0435\u0442 \u0434\u043b\u044f \u0441\u043e\u0437\u0434\u0430\u043d\u0438\u044f \u043d\u0430 \u043a\u0430\u0440\u0442\u0435\", \"Select an item to spawn on the map\" )</label>\r\n                        </div>\r\n\r\n                        <div class=\"gmod-prop-grid\">\r\n                            <div class=\"gmod-prop-card\" onclick=\"@(() => SelectAndSpawnProp(0))\">\r\n                                <div class=\"card-glow\"></div>\r\n                                <div class=\"prop-icon\">\ud83d\udca5</div>\r\n                                <label class=\"prop-label\">@L( \"\u0412\u0437\u0440\u044b\u0432\u043d\u0430\u044f \u0411\u043e\u0447\u043a\u0430\", \"Explosive Barrel\" )</label>\r\n                                <label class=\"prop-price\">\ud83d\udcb0 15</label>\r\n                            </div>\r\n\r\n                            <div class=\"gmod-prop-card\" onclick=\"@(() => SelectAndSpawnProp(1))\">\r\n                                <div class=\"card-glow\"></div>\r\n                                <div class=\"prop-icon\">\ud83d\udee2\ufe0f</div>\r\n                                <label class=\"prop-label\">@L( \"\u0417\u043e\u043d\u0430 \u0421\u043c\u043e\u043b\u044b\", \"Tar Zone\" )</label>\r\n                                <label class=\"prop-price\">\ud83d\udcb0 20</label>\r\n                            </div>\r\n\r\n                            <div class=\"gmod-prop-card\" onclick=\"@(() => SelectAndSpawnProp(2))\">\r\n                                <div class=\"card-glow\"></div>\r\n                                <div class=\"prop-icon\">\u2693</div>\r\n                                <label class=\"prop-label\">@L( \"\u041d\u0430\u043a\u043e\u0432\u0430\u043b\u044c\u043d\u044f\", \"Anvil\" )</label>\r\n                                <label class=\"prop-price\">\ud83d\udcb0 25</label>\r\n                            </div>\r\n\r\n                            <div class=\"gmod-prop-card\" onclick=\"@(() => SelectAndSpawnProp(3))\">\r\n                                <div class=\"card-glow\"></div>\r\n                                <div class=\"prop-icon\">\ud83c\udf00</div>\r\n                                <label class=\"prop-label\">@L( \"\u041f\u0440\u0443\u0436\u0438\u043d\u043d\u044b\u0439 \u0411\u0430\u0442\u0443\u0442\", \"Spring Pad\" )</label>\r\n                                <label class=\"prop-price\">\ud83d\udcb0 20</label>\r\n                            </div>\r\n                        </div>\r\n                    </div>\r\n\r\n                    <div class=\"spawnmenu-tools\">\r\n                        <label class=\"tools-title\">@L( \"\u0418\u041d\u0421\u0422\u0420\u0423\u041c\u0415\u041d\u0422\u042b\", \"TOOLS\" )</label>\r\n                        \r\n                        <div class=\"tool-group\">\r\n                            <label class=\"group-title\">@L( \"\u0412\u0437\u0430\u0438\u043c\u043e\u0434\u0435\u0439\u0441\u0442\u0432\u0438\u0435\", \"Interaction\" )</label>\r\n                            <div class=\"tool-item active\">\ud83d\udc49 @L( \"\u0421\u043f\u0430\u0432\u043d-\u043c\u0435\u043d\u044e [E]\", \"Spawn Menu [E]\" )</div>\r\n                            <div class=\"tool-item\">\ud83d\ude80 @L( \"\u0421\u0438\u043b\u043e\u0432\u0430\u044f \u0432\u043e\u043b\u043d\u0430\", \"Force Wave\" )</div>\r\n                        </div>\r\n\r\n                        <div class=\"tool-group\">\r\n                            <label class=\"group-title\">@L( \"\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435\", \"Controls\" )</label>\r\n                            <div class=\"tool-hint\">@L( \"\u041e\u0442\u043f\u0443\u0441\u0442\u0438\u0442\u0435 [E] \u0434\u043b\u044f \u0437\u0430\u043a\u0440\u044b\u0442\u0438\u044f \u043c\u0435\u043d\u044e\", \"Release [E] to close menu\" )</div>\r\n                        </div>\r\n                    </div>\r\n                </div>\r\n            </div>\r\n        </div>\r\n    }\r\n\r\n    @if ( IsWorkshopMenuOpen )\r\n    {\r\n        <div class=\"menu-overlay\" onclick=\"@CloseWorkshopMenu\">\r\n            <div class=\"prop-menu-card\" onclick=\"@((e) => e.StopPropagation())\">\r\n                \r\n                <div class=\"workshop-header\">\r\n                    <label class=\"menu-title\">@L( \"\u041c\u0410\u0421\u0422\u0415\u0420\u0421\u041a\u0410\u042f \u0410\u0420\u0415\u041d\u042b\", \"ARENA WORKSHOP\" )</label>\r\n                    <div class=\"credits-badge\">\ud83d\udcb0 @GetCredits()</div>\r\n                </div>\r\n\r\n                <div class=\"tab-selector\">\r\n                    <button class=\"tab-btn @(ActiveTab == \"PROPS\" ? \"active\" : \"\")\" onclick=\"@(() => SelectTab(\"PROPS\"))\">\r\n                        \ud83d\udce6 @L( \"\u041f\u0420\u041e\u041f\u0421\u042b\", \"PROPS\" )\r\n                    </button>\r\n                    <button class=\"tab-btn @(ActiveTab == \"UPGRADES\" ? \"active\" : \"\")\" onclick=\"@(() => SelectTab(\"UPGRADES\"))\">\r\n                        \u26a1 @L( \"\u041f\u0420\u041e\u041a\u0410\u0427\u041a\u0410\", \"UPGRADES\" )\r\n                    </button>\r\n                    <button class=\"tab-btn @(ActiveTab == \"WEAPONS\" ? \"active\" : \"\")\" onclick=\"@(() => SelectTab(\"WEAPONS\"))\">\r\n                        \ud83d\udd2b @L( \"\u041e\u0420\u0423\u0416\u0418\u0415\", \"WEAPONS\" )\r\n                    </button>\r\n                    <button class=\"tab-btn @(ActiveTab == \"PRESTIGE\" ? \"active\" : \"\")\" onclick=\"@(() => SelectTab(\"PRESTIGE\"))\">\r\n                        \ud83d\udc51 @L( \"\u041f\u0420\u0415\u0421\u0422\u0418\u0416\", \"PRESTIGE\" )\r\n                    </button>\r\n                </div>\r\n\r\n                @if ( ActiveTab == \"PROPS\" )\r\n                {\r\n                    <div class=\"prop-grid\">\r\n                        <div class=\"prop-item\" onclick=\"@(() => SelectAndSpawnProp(0))\">\r\n                            <label class=\"prop-name\">\ud83d\udca5 @L( \"\u0411\u043e\u0447\u043a\u0430\", \"Barrel\" )</label>\r\n                            <label class=\"prop-cost\">\ud83d\udcb0 15</label>\r\n                        </div>\r\n                        <div class=\"prop-item\" onclick=\"@(() => SelectAndSpawnProp(1))\">\r\n                            <label class=\"prop-name\">\ud83d\udee2\ufe0f @L( \"\u0421\u043c\u043e\u043b\u0430\", \"Tar Zone\" )</label>\r\n                            <label class=\"prop-cost\">\ud83d\udcb0 20</label>\r\n                        </div>\r\n                        <div class=\"prop-item\" onclick=\"@(() => SelectAndSpawnProp(2))\">\r\n                            <label class=\"prop-name\">\u2693 @L( \"\u041d\u0430\u043a\u043e\u0432\u0430\u043b\u044c\u043d\u044f\", \"Anvil\" )</label>\r\n                            <label class=\"prop-cost\">\ud83d\udcb0 25</label>\r\n                        </div>\r\n                        <div class=\"prop-item\" onclick=\"@(() => SelectAndSpawnProp(3))\">\r\n                            <label class=\"prop-name\">\ud83c\udf00 @L( \"\u0411\u0430\u0442\u0443\u0442\", \"Spring\" )</label>\r\n                            <label class=\"prop-cost\">\ud83d\udcb0 20</label>\r\n                        </div>\r\n                    </div>\r\n                }\r\n\r\n                @if ( ActiveTab == \"UPGRADES\" )\r\n                {\r\n                    <div class=\"upgrades-list\">\r\n                        <div class=\"upgrade-row\">\r\n                            <div class=\"info\">\r\n                                <label class=\"name\">\ud83d\udcaa @L( \"\u0421\u0418\u041b\u0410 \u0423\u0414\u0410\u0420\u0410\", \"PUSH FORCE\" )</label>\r\n                                <label class=\"level\">LVL @GetForceLevel()</label>\r\n                            </div>\r\n                            <button class=\"buy-btn\" onclick=\"@TryUpgradeForce\">\r\n                                <label class=\"btn-text\">@L( \"\u041a\u0423\u041f\u0418\u0422\u042c\", \"BUY\" )</label>\r\n                                <label class=\"btn-cost\">@GetForceCost() \ud83d\udcb0</label>\r\n                            </button>\r\n                        </div>\r\n\r\n                        <div class=\"upgrade-row\">\r\n                            <div class=\"info\">\r\n                                <label class=\"name\">\ud83c\udfcb\ufe0f @L( \"\u041c\u0410\u0421\u0421\u0410 \u0418\u0413\u0420\u041e\u041a\u0410\", \"PLAYER MASS\" )</label>\r\n                                <label class=\"level\">LVL @GetMassLevel()</label>\r\n                            </div>\r\n                            <button class=\"buy-btn\" onclick=\"@TryUpgradeMass\">\r\n                                <label class=\"btn-text\">@L( \"\u041a\u0423\u041f\u0418\u0422\u042c\", \"BUY\" )</label>\r\n                                <label class=\"btn-cost\">@GetMassCost() \ud83d\udcb0</label>\r\n                            </button>\r\n                        </div>\r\n\r\n                        <div class=\"upgrade-row\">\r\n                            <div class=\"info\">\r\n                                <label class=\"name\">\u26a1 @L( \"\u042d\u041d\u0415\u0420\u0413\u0418\u042f \u0418 \u0420\u042b\u0412\u041e\u041a\", \"ENERGY RECOVERY\" )</label>\r\n                                <label class=\"level\">LVL @GetEnergyLevel()</label>\r\n                            </div>\r\n                            <button class=\"buy-btn\" onclick=\"@TryUpgradeEnergy\">\r\n                                <label class=\"btn-text\">@L( \"\u041a\u0423\u041f\u0418\u0422\u042c\", \"BUY\" )</label>\r\n                                <label class=\"btn-cost\">@GetEnergyCost() \ud83d\udcb0</label>\r\n                            </button>\r\n                        </div>\r\n                    </div>\r\n                }\r\n\r\n                @if ( ActiveTab == \"WEAPONS\" )\r\n                {\r\n                    <div class=\"upgrades-list\">\r\n                        @if ( WeaponManager.Instance != null )\r\n                        {\r\n                            @foreach ( var w in WeaponManager.Instance.Weapons )\r\n                            {\r\n                                bool isEquipped = WeaponManager.Instance.CurrentWeapon == w;\r\n\r\n                                <div class=\"upgrade-row\">\r\n                                    <div class=\"info\">\r\n                                        <label class=\"name\">@(CurrentLanguage == \"RU\" ? w.NameRu : w.NameEn)</label>\r\n                                        <label class=\"level\">\r\n                                            @(isEquipped ? L( \"\u042d\u041a\u0418\u041f\u0418\u0420\u041e\u0412\u0410\u041d\u041e\", \"EQUIPPED\" ) : (w.IsUnlocked ? L( \"\u041a\u0423\u041f\u041b\u0415\u041d\u041e\", \"UNLOCKED\" ) : L( \"\u0417\u0410\u041a\u0420\u042b\u0422\u041e\", \"LOCKED\" )))\r\n                                        </label>\r\n                                    </div>\r\n\r\n                                    @if ( isEquipped )\r\n                                    {\r\n                                        <button class=\"buy-btn equipped\">\r\n                                            <label class=\"btn-text\">\u2714 @L( \"\u0410\u041a\u0422\u0418\u0412\u041d\u041e\", \"ACTIVE\" )</label>\r\n                                        </button>\r\n                                    }\r\n                                    else if ( w.IsUnlocked )\r\n                                    {\r\n                                        <button class=\"buy-btn equip\" onclick=\"@(() => WeaponManager.Instance.TryBuyOrEquip(w))\">\r\n                                            <label class=\"btn-text\">\u26a1 @L( \"\u0412\u0417\u042f\u0422\u042c\", \"EQUIP\" )</label>\r\n                                        </button>\r\n                                    }\r\n                                    else\r\n                                    {\r\n                                        <button class=\"buy-btn\" onclick=\"@(() => WeaponManager.Instance.TryBuyOrEquip(w))\">\r\n                                            <label class=\"btn-text\">@L( \"\u041a\u0423\u041f\u0418\u0422\u042c\", \"BUY\" )</label>\r\n                                            <label class=\"btn-cost\">@w.Cost \ud83d\udcb0</label>\r\n                                        </button>\r\n                                    }\r\n                                </div>\r\n                            }\r\n                        }\r\n                    </div>\r\n                }\r\n\r\n                @if ( ActiveTab == \"PRESTIGE\" )\r\n                {\r\n                    <div class=\"prestige-card-container\">\r\n                        <div class=\"prestige-info-box\">\r\n                            <label class=\"prestige-lvl\">\ud83d\udc51 @L( \"\u0423\u0420\u041e\u0412\u0415\u041d\u042c \u041f\u0420\u0415\u0421\u0422\u0418\u0416\u0410:\", \"PRESTIGE LEVEL:\" ) @GetPrestigeLevel()</label>\r\n                            <label class=\"prestige-bonus\">\u2728 @L( \"\u0411\u041e\u041d\u0423\u0421 \u041a \u041a\u0420\u0415\u0414\u0418\u0422\u0410\u041c:\", \"CREDIT BONUS:\" ) +@(GetPrestigeLevel() * 50)%</label>\r\n                            <label class=\"prestige-tokens\">\ud83d\udd2e @L( \"\u041e\u0427\u041a\u0418 \u041f\u0420\u0415\u0421\u0422\u0418\u0416\u0410:\", \"PRESTIGE TOKENS:\" ) @GetPrestigeTokens()</label>\r\n                        </div>\r\n\r\n                        <div class=\"prestige-desc\">\r\n                            <label class=\"desc-text\">\r\n                                @L( \"\u0421\u0431\u0440\u043e\u0441\u044c\u0442\u0435 \u0443\u0440\u043e\u0432\u043d\u0438 \u0421\u0438\u043b\u044b, \u041c\u0430\u0441\u0441\u044b \u0438 \u042d\u043d\u0435\u0440\u0433\u0438\u0438, \u0447\u0442\u043e\u0431\u044b \u043f\u043e\u043b\u0443\u0447\u0438\u0442\u044c \u041e\u0447\u043a\u043e \u041f\u0440\u0435\u0441\u0442\u0438\u0436\u0430 \u0438 \u043f\u043e\u0441\u0442\u043e\u044f\u043d\u043d\u044b\u0439 \u0431\u043e\u043d\u0443\u0441 +50% \u043a \u0437\u0430\u0440\u0430\u0431\u043e\u0442\u043a\u0443!\", \r\n                                   \"Reset Force, Mass, and Energy levels to get a Prestige Token and a permanent +50% credit bonus!\" )\r\n                            </label>\r\n                            <label class=\"req-text\">\r\n                                @L( \"\u0422\u0420\u0415\u0411\u041e\u0412\u0410\u041d\u0418\u0415: \u0421\u0443\u043c\u043c\u0430 \u0443\u0440\u043e\u0432\u043d\u0435\u0439 \u043f\u0440\u043e\u043a\u0430\u0447\u043a\u0438 15+\", \"REQUIREMENT: Total Upgrade Levels 15+\" )\r\n                            </label>\r\n                        </div>\r\n\r\n                        <button class=\"prestige-btn @(CanPrestige() ? \"ready\" : \"disabled\")\" onclick=\"@TryPrestige\">\r\n                            <label class=\"btn-text\">@L( \"\u0421\u0411\u0420\u041e\u0421\u0418\u0422\u042c \u0418 \u041f\u041e\u041b\u0423\u0427\u0418\u0422\u042c \u041f\u0420\u0415\u0421\u0422\u0418\u0416\", \"PRESTIGE RESET\" )</label>\r\n                        </button>\r\n                    </div>\r\n                }\r\n\r\n                <button class=\"menu-btn\" onclick=\"@CloseWorkshopMenu\">@L( \"\u0417\u0410\u041a\u0420\u042b\u0422\u042c [R]\", \"CLOSE [R]\" )</button>\r\n            </div>\r\n        </div>\r\n    }\r\n\r\n    @if ( IsMenuOpen )\r\n    {\r\n        <div class=\"main-menu-screen\">\r\n            <div class=\"main-menu-sidebar\">\r\n                <div class=\"brand-section\">\r\n                    <div class=\"logo-glow\"></div>\r\n                    <label class=\"brand-title\">ONE MORE PUSH</label>\r\n                    <label class=\"brand-subtitle\">PHYSICS ARENA BRAWLER</label>\r\n                </div>\r\n\r\n                <div class=\"nav-buttons-group\">\r\n                    <button class=\"epic-btn play\" onclick=\"@StartGame\">\r\n                        <div class=\"btn-glow\"></div>\r\n                        <label class=\"btn-icon\">\u25b6</label>\r\n                        <div class=\"btn-content\">\r\n                            <label class=\"btn-title\">@L( \"\u0412 \u0411\u041e\u0419\", \"PLAY NOW\" )</label>\r\n                            <label class=\"btn-sub\">@L( \"\u0412\u044b\u0439\u0442\u0438 \u043d\u0430 \u0430\u0440\u0435\u043d\u0443\", \"Enter the Arena\" )</label>\r\n                        </div>\r\n                    </button>\r\n\r\n                    <button class=\"epic-btn\" onclick=\"@OpenSettings\">\r\n                        <label class=\"btn-icon\">\u2699</label>\r\n                        <div class=\"btn-content\">\r\n                            <label class=\"btn-title\">@L( \"\u041d\u0410\u0421\u0422\u0420\u041e\u0419\u041a\u0418\", \"SETTINGS\" )</label>\r\n                            <label class=\"btn-sub\">@L( \"\u041f\u0430\u0440\u0430\u043c\u0435\u0442\u0440\u044b \u0438 \u0443\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435\", \"Game options\" )</label>\r\n                        </div>\r\n                    </button>\r\n\r\n                    <button class=\"epic-btn quit\" onclick=\"@QuitGame\">\r\n                        <label class=\"btn-icon\">\u2716</label>\r\n                        <div class=\"btn-content\">\r\n                            <label class=\"btn-title\">@L( \"\u0412 \u041c\u0415\u041d\u042e\", \"MAIN MENU\" )</label>\r\n                            <label class=\"btn-sub\">@L( \"\u0412\u0435\u0440\u043d\u0443\u0442\u044c\u0441\u044f \u0432 \u0433\u043b\u0430\u0432\u043d\u043e\u0435 \u043c\u0435\u043d\u044e\", \"Return to Main Menu\" )</label>\r\n                        </div>\r\n                    </button>\r\n                </div>\r\n\r\n                <div class=\"player-profile-card\">\r\n                    <div class=\"profile-icon\">\ud83c\udfae</div>\r\n                    <div class=\"profile-info\">\r\n                        <label class=\"profile-name\">@(Connection.Local?.DisplayName ?? L( \"\u0411\u043e\u0435\u0446\", \"Fighter\" ))</label>\r\n                        <label class=\"profile-stats\">\ud83d\udcb0 @GetCredits() | \ud83c\udfc6 @GetHighScore()</label>\r\n                    </div>\r\n                </div>\r\n            </div>\r\n\r\n            <div class=\"main-menu-leaderboard-panel\">\r\n                <div class=\"lb-header\">\r\n                    <label class=\"lb-title\">\ud83c\udfc6 @L( \"\u0422\u0410\u0411\u041b\u0418\u0426\u0410 \u041b\u0418\u0414\u0415\u0420\u041e\u0412\", \"GLOBAL LEADERBOARD\" )</label>\r\n                    <button class=\"lb-refresh-btn\" onclick=\"@RefreshLeaderboard\">\ud83d\udd04</button>\r\n                </div>\r\n\r\n                <div class=\"lb-tabs\">\r\n                    <button class=\"tab-btn @(CurrentLbStat == \"pushes_count\" ? \"active\" : \"\")\" onclick=\"@(() => SelectLbStat(\"pushes_count\"))\">\r\n                        \ud83d\ude80 @L( \"\u0422\u041e\u041b\u0427\u041a\u0418\", \"PUSHES\" )\r\n                    </button>\r\n                    <button class=\"tab-btn @(CurrentLbStat == \"rounds_won\" ? \"active\" : \"\")\" onclick=\"@(() => SelectLbStat(\"rounds_won\"))\">\r\n                        \ud83d\udc51 @L( \"\u041f\u041e\u0411\u0415\u0414\u042b\", \"WINS\" )\r\n                    </button>\r\n                </div>\r\n\r\n                <div class=\"lb-body\">\r\n                    @if ( IsLbLoading )\r\n                    {\r\n                        <div class=\"lb-status\">@L( \"\u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430...\", \"LOADING...\" )</div>\r\n                    }\r\n                    else if ( LbEntries == null || LbEntries.Count == 0 )\r\n                    {\r\n                        <div class=\"lb-status\">@L( \"\u041d\u0435\u0442 \u0434\u0430\u043d\u043d\u044b\u0445\", \"NO RECORD FOUND\" )</div>\r\n                    }\r\n                    else\r\n                    {\r\n                        <div class=\"lb-table\">\r\n                            <div class=\"lb-table-header\">\r\n                                <span class=\"col-rank\">#</span>\r\n                                <span class=\"col-player\">@L( \"\u0418\u0413\u0420\u041e\u041a\", \"PLAYER\" )</span>\r\n                                <span class=\"col-score\">@L( \"\u0421\u0427\u0415\u0422\", \"SCORE\" )</span>\r\n                            </div>\r\n                            <div class=\"lb-table-rows\">\r\n                                @foreach ( var entry in LbEntries )\r\n                                {\r\n                                    <div class=\"lb-row @(entry.SteamId == (Connection.Local?.SteamId ?? 0) ? \"is-me\" : \"\")\">\r\n                                        <span class=\"col-rank\">@GetRankBadge( entry.Rank )</span>\r\n                                        <span class=\"col-player\">@entry.DisplayName</span>\r\n                                        <span class=\"col-score\">@entry.Value.ToString(\"N0\")</span>\r\n                                    </div>\r\n                                }\r\n                            </div>\r\n                        </div>\r\n                    }\r\n                </div>\r\n            </div>\r\n\r\n            <div class=\"main-menu-footer-badge\">\r\n                <label class=\"footer-ver\">v1.0.0 RELEASE</label>\r\n                <label class=\"footer-engine\">POWERED BY SOURCE 2 & s&box</label>\r\n            </div>\r\n        </div>\r\n    }\r\n\r\n    @if ( IsSettingsOpen )\r\n    {\r\n        <div class=\"main-menu-screen\">\r\n            <div class=\"main-menu-sidebar settings-wide\">\r\n                <div class=\"brand-section\">\r\n                    <label class=\"brand-title\">@L( \"\u041d\u0410\u0421\u0422\u0420\u041e\u0419\u041a\u0418\", \"SETTINGS\" )</label>\r\n                    <label class=\"brand-subtitle\">CONFIGURATION PANEL</label>\r\n                </div>\r\n\r\n                <div class=\"settings-list-container\">\r\n                    <div class=\"setting-row-card\">\r\n                        <div class=\"setting-info\">\r\n                            <label class=\"title\">\ud83c\udf10 @L( \"\u042f\u0437\u044b\u043a \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441\u0430\", \"Language\" )</label>\r\n                            <label class=\"desc\">@L( \"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u044f\u0437\u044b\u043a \u043e\u0442\u043e\u0431\u0440\u0430\u0436\u0435\u043d\u0438\u044f\", \"Select UI Language\" )</label>\r\n                        </div>\r\n                        <div class=\"lang-selector\">\r\n                            <button class=\"lang-btn @(CurrentLanguage == \"EN\" ? \"active\" : \"\")\" onclick=\"@(() => SetLanguage(\"EN\"))\">EN</button>\r\n                            <button class=\"lang-btn @(CurrentLanguage == \"RU\" ? \"active\" : \"\")\" onclick=\"@(() => SetLanguage(\"RU\"))\">RU</button>\r\n                        </div>\r\n                    </div>\r\n\r\n                    <div class=\"setting-row-card\">\r\n                        <div class=\"setting-info\">\r\n                            <label class=\"title\">\ud83e\udd16 @L( \"\u0421\u043f\u0430\u0432\u043d \u0411\u043e\u0442\u043e\u0432\", \"Bot Spawner\" )</label>\r\n                            <label class=\"desc\">@L( \"\u0410\u0432\u0442\u043e\u043c\u0430\u0442\u0438\u0447\u0435\u0441\u043a\u0438 \u0434\u043e\u0431\u0430\u0432\u043b\u044f\u0442\u044c \u0431\u043e\u0442\u043e\u0432 \u043d\u0430 \u043a\u0430\u0440\u0442\u0443\", \"Spawn AI bots in game\" )</label>\r\n                        </div>\r\n                        <button class=\"lang-btn @(BotSpawner.EnableBots ? \"active\" : \"\")\" onclick=\"@ToggleBots\">\r\n                            @(BotSpawner.EnableBots ? L( \"\u0412\u041a\u041b\", \"ON\" ) : L( \"\u0412\u042b\u041a\u041b\", \"OFF\" ))\r\n                        </button>\r\n                    </div>\r\n\r\n                    <div class=\"setting-row-card\">\r\n                        <div class=\"setting-info\">\r\n                            <label class=\"title\">\ud83c\udfb5 @L( \"\u0424\u043e\u043d\u043e\u0432\u0430\u044f \u041c\u0443\u0437\u044b\u043a\u0430\", \"Music\" )</label>\r\n                            <label class=\"desc\">@L( \"\u0412\u043a\u043b\u044e\u0447\u0438\u0442\u044c \u0434\u0438\u043d\u0430\u043c\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u0441\u0430\u0443\u043d\u0434\u0442\u0440\u0435\u043a\", \"Toggle battle music\" )</label>\r\n                        </div>\r\n                        <button class=\"lang-btn @(EnableMusic ? \"active\" : \"\")\" onclick=\"@ToggleMusic\">\r\n                            @(EnableMusic ? L( \"\u0412\u041a\u041b\", \"ON\" ) : L( \"\u0412\u042b\u041a\u041b\", \"OFF\" ))\r\n                        </button>\r\n                    </div>\r\n\r\n                    <div class=\"setting-row-card\">\r\n                        <div class=\"setting-info\">\r\n                            <label class=\"title\">\ud83c\udfaf @L( \"\u0427\u0443\u0432\u0441\u0442\u0432\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u044c\", \"Sensitivity\" )</label>\r\n                            <label class=\"desc\">@L( \"\u0421\u043a\u043e\u0440\u043e\u0441\u0442\u044c \u043f\u043e\u0432\u043e\u0440\u043e\u0442\u0430 \u043a\u0430\u043c\u0435\u0440\u044b\", \"Mouse sensitivity factor\" )</label>\r\n                        </div>\r\n                        <label class=\"sens-badge\">1.0</label>\r\n                    </div>\r\n                </div>\r\n\r\n                <button class=\"epic-btn back\" onclick=\"@CloseSettings\">\r\n                    <label class=\"btn-icon\">\u2b05</label>\r\n                    <div class=\"btn-content\">\r\n                        <label class=\"btn-title\">@L( \"\u041d\u0410\u0417\u0410\u0414\", \"BACK\" )</label>\r\n                    </div>\r\n                </button>\r\n            </div>\r\n        </div>\r\n    }\r\n\r\n</root>\r\n\r\n@code {\r\n    public class EntryItem\r\n    {\r\n        public int Rank { get; set; }\r\n        public string DisplayName { get; set; }\r\n        public double Value { get; set; }\r\n        public ulong SteamId { get; set; }\r\n    }\r\n\r\n    [Property] public PushAbility PushAbility { get; set; }\r\n    public PlayerHealth LocalHealth { get; private set; }\r\n\r\n    public bool IsMenuOpen { get; set; } = true;\r\n    public bool IsSettingsOpen { get; set; } = false;\r\n    public bool IsWorkshopMenuOpen { get; set; } = false;\r\n    public bool IsQSpawnMenuOpen { get; set; } = false;\r\n\r\n    public string ActiveTab { get; set; } = \"PROPS\";\r\n    public static string CurrentLanguage { get; set; } = \"EN\";\r\n\r\n    public bool IsLbLoading { get; set; } = true;\r\n    public string CurrentLbStat { get; set; } = \"pushes_count\";\r\n    public List<EntryItem> LbEntries { get; set; } = new();\r\n\r\n    public struct ChatMessage\r\n    {\r\n        public string Author { get; set; }\r\n        public string Text { get; set; }\r\n    }\r\n\r\n    public List<ChatMessage> CanvasMessages { get; set; } = new();\r\n    public bool IsChatOpen { get; set; } = false;\r\n    private TextEntry ChatInput { get; set; }\r\n\r\n    public struct PlayerInfo\r\n    {\r\n        public string Name { get; set; }\r\n        public int Score { get; set; }\r\n        public bool IsLocal { get; set; }\r\n    }\r\n\r\n    private string L( string ru, string en ) => CurrentLanguage == \"RU\" ? ru : en;\r\n    private bool IsReady => PushAbility?.IsReady ?? false;\r\n\r\n    private MetaProgression GetMeta() => Scene.GetAllComponents<MetaProgression>().FirstOrDefault();\r\n    private int GetCredits() => GetMeta()?.Credits ?? 0;\r\n    private int GetForceLevel() => GetMeta()?.ForceLevel ?? 0;\r\n    private int GetMassLevel() => GetMeta()?.MassLevel ?? 0;\r\n    private int GetEnergyLevel() => GetMeta()?.EnergyLevel ?? 0;\r\n\r\n    private int GetPrestigeLevel() => GetMeta()?.PrestigeLevel ?? 0;\r\n    private int GetPrestigeTokens() => GetMeta()?.PrestigeTokens ?? 0;\r\n    private bool CanPrestige() => GetMeta()?.CanPrestige() ?? false;\r\n    private void TryPrestige() => GetMeta()?.TryPrestige();\r\n\r\n    private int GetForceCost() => GetMeta()?.GetUpgradeCost(GetForceLevel()) ?? 0;\r\n    private int GetMassCost() => GetMeta()?.GetUpgradeCost(GetMassLevel()) ?? 0;\r\n    private int GetEnergyCost() => GetMeta()?.GetUpgradeCost(GetEnergyLevel()) ?? 0;\r\n\r\n    private void TryUpgradeForce() => GetMeta()?.TryUpgradeForce();\r\n    private void TryUpgradeMass() => GetMeta()?.TryUpgradeMass();\r\n    private void TryUpgradeEnergy() => GetMeta()?.TryUpgradeEnergy();\r\n\r\n    private float GetHealth() => LocalHealth?.CurrentHealth ?? 100f;\r\n    private float GetMaxHealth() => LocalHealth?.MaxHealth ?? 100f;\r\n    private float GetHealthPercent() => MathX.Clamp( (GetHealth() / GetMaxHealth()) * 100f, 0f, 100f );\r\n\r\n    private void SelectTab( string tab )\r\n    {\r\n        ActiveTab = tab;\r\n        StateHasChanged();\r\n    }\r\n\r\n    protected override void OnEnabled()\r\n    {\r\n        base.OnEnabled();\r\n        AddMessage( L( \"\u0421\u0438\u0441\u0442\u0435\u043c\u0430\", \"System\" ), L( \"\u0427\u0430\u0442 \u0433\u043e\u0442\u043e\u0432. \u041d\u0430\u0436\u043c\u0438\u0442\u0435 Enter \u0434\u043b\u044f \u0432\u0432\u043e\u0434\u0430.\", \"Chat ready. Press Enter to type.\" ) );\r\n        ApplyMouseState();\r\n        _ = RefreshLeaderboard();\r\n    }\r\n\r\n    private async Task SelectLbStat( string statIdent )\r\n    {\r\n        if ( CurrentLbStat == statIdent ) return;\r\n        CurrentLbStat = statIdent;\r\n        await RefreshLeaderboard();\r\n    }\r\n\r\n    private async Task RefreshLeaderboard()\r\n    {\r\n        IsLbLoading = true;\r\n        StateHasChanged();\r\n\r\n        try\r\n        {\r\n            var board = Sandbox.Services.Leaderboards.GetFromStat( \"seilordstudio.one_more_push\", CurrentLbStat );\r\n            board.SetAggregationSum();\r\n            board.MaxEntries = 10;\r\n            await board.Refresh();\r\n\r\n            if ( board.Entries != null )\r\n            {\r\n                LbEntries = board.Entries.Select( x => new EntryItem\r\n                {\r\n                    Rank = Convert.ToInt32( x.Rank ),\r\n                    DisplayName = x.DisplayName ?? \"Player\",\r\n                    Value = Convert.ToDouble( x.Value ),\r\n                    SteamId = Convert.ToUInt64( x.SteamId )\r\n                } ).ToList();\r\n            }\r\n            else\r\n            {\r\n                LbEntries = new();\r\n            }\r\n        }\r\n        catch ( System.Exception ex )\r\n        {\r\n            Log.Warning( $\"Leaderboard fetch error: {ex.Message}\" );\r\n        }\r\n\r\n        IsLbLoading = false;\r\n        StateHasChanged();\r\n    }\r\n\r\n    private string GetRankBadge( int rank )\r\n    {\r\n        return rank switch\r\n        {\r\n            1 => \"\ud83e\udd47 1\",\r\n            2 => \"\ud83e\udd48 2\",\r\n            3 => \"\ud83e\udd49 3\",\r\n            _ => rank.ToString()\r\n        };\r\n    }\r\n\r\n    private bool IsEPressed()\r\n    {\r\n        return Input.Down( \"use\" ) || Input.Down( \"e\" ) || Input.Down( \"E\" );\r\n    }\r\n\r\n    protected override void OnUpdate()\r\n    {\r\n        base.OnUpdate();\r\n\r\n        if ( PushAbility == null )\r\n        {\r\n            PushAbility = Scene.GetAllComponents<PushAbility>().FirstOrDefault( x => !x.IsProxy );\r\n        }\r\n\r\n        if ( LocalHealth == null )\r\n        {\r\n            LocalHealth = Scene.GetAllComponents<PlayerHealth>().FirstOrDefault( x => !x.IsProxy );\r\n        }\r\n\r\n        bool eDown = IsEPressed();\r\n        if ( eDown )\r\n        {\r\n            if ( !IsQSpawnMenuOpen && !IsChatOpen && !IsSettingsOpen && !IsMenuOpen )\r\n            {\r\n                IsWorkshopMenuOpen = false;\r\n                IsQSpawnMenuOpen = true;\r\n                ApplyMouseState();\r\n                StateHasChanged();\r\n            }\r\n        }\r\n        else\r\n        {\r\n            if ( IsQSpawnMenuOpen )\r\n            {\r\n                CloseQSpawnMenu();\r\n            }\r\n        }\r\n\r\n        if ( Input.EscapePressed || Input.Pressed( \"menu\" ) )\r\n        {\r\n            Input.EscapePressed = false;\r\n            ToggleMenu();\r\n        }\r\n\r\n        if ( Input.Pressed( \"reload\" ) || Input.Pressed( \"r\" ) || Input.Pressed( \"R\" ) )\r\n        {\r\n            if ( !IsMenuOpen && !IsSettingsOpen && !IsChatOpen && !IsQSpawnMenuOpen )\r\n            {\r\n                ToggleWorkshopMenu();\r\n            }\r\n        }\r\n\r\n        if ( Input.Pressed( \"chat\" ) || Input.Pressed( \"enter\" ) )\r\n        {\r\n            if ( !IsMenuOpen && !IsSettingsOpen && !IsWorkshopMenuOpen && !IsQSpawnMenuOpen )\r\n            {\r\n                if ( !IsChatOpen ) OpenChat();\r\n            }\r\n        }\r\n\r\n        ApplyMouseState();\r\n    }\r\n\r\n    private void SetLanguage( string lang )\r\n    {\r\n        CurrentLanguage = lang;\r\n        StateHasChanged();\r\n    }\r\n\r\n    private void ApplyMouseState()\r\n    {\r\n        bool needsMouse = IsMenuOpen || IsSettingsOpen || IsChatOpen || IsWorkshopMenuOpen || IsQSpawnMenuOpen;\r\n        Mouse.Visibility = needsMouse ? MouseVisibility.Visible : MouseVisibility.Hidden;\r\n\r\n        if ( !needsMouse && Panel != null )\r\n        {\r\n            Panel.Focus();\r\n        }\r\n    }\r\n\r\n    private void ToggleWorkshopMenu()\r\n    {\r\n        IsWorkshopMenuOpen = !IsWorkshopMenuOpen;\r\n        IsQSpawnMenuOpen = false;\r\n        ApplyMouseState();\r\n        StateHasChanged();\r\n    }\r\n\r\n    private void CloseWorkshopMenu()\r\n    {\r\n        IsWorkshopMenuOpen = false;\r\n        ApplyMouseState();\r\n        StateHasChanged();\r\n    }\r\n\r\n    private void CloseQSpawnMenu()\r\n    {\r\n        IsQSpawnMenuOpen = false;\r\n        ApplyMouseState();\r\n        StateHasChanged();\r\n    }\r\n\r\n    private void SelectAndSpawnProp( int index )\r\n    {\r\n        if ( PropSpawner.Instance != null )\r\n        {\r\n            PropSpawner.Instance.SpawnByIndex( index );\r\n        }\r\n        if ( IsWorkshopMenuOpen ) CloseWorkshopMenu();\r\n    }\r\n\r\n    private void ToggleMenu()\r\n    {\r\n        if ( IsSettingsOpen ) CloseSettings();\r\n        else if ( IsWorkshopMenuOpen ) CloseWorkshopMenu();\r\n        else if ( IsQSpawnMenuOpen ) CloseQSpawnMenu();\r\n        else if ( IsMenuOpen ) StartGame();\r\n        else OpenMenu();\r\n    }\r\n\r\n    private void StartGame()\r\n    {\r\n        IsMenuOpen = false;\r\n        IsSettingsOpen = false;\r\n        IsWorkshopMenuOpen = false;\r\n        IsQSpawnMenuOpen = false;\r\n        ApplyMouseState();\r\n    }\r\n\r\n    private void OpenMenu()\r\n    {\r\n        IsMenuOpen = true;\r\n        IsSettingsOpen = false;\r\n        IsWorkshopMenuOpen = false;\r\n        IsQSpawnMenuOpen = false;\r\n        ApplyMouseState();\r\n        _ = RefreshLeaderboard();\r\n    }\r\n\r\n    private void OpenSettings()\r\n    {\r\n        IsMenuOpen = false;\r\n        IsSettingsOpen = true;\r\n        IsWorkshopMenuOpen = false;\r\n        IsQSpawnMenuOpen = false;\r\n        ApplyMouseState();\r\n    }\r\n\r\n    private void CloseSettings()\r\n    {\r\n        IsSettingsOpen = false;\r\n        IsMenuOpen = true;\r\n        IsWorkshopMenuOpen = false;\r\n        IsQSpawnMenuOpen = false;\r\n        ApplyMouseState();\r\n    }\r\n\r\n    private void QuitGame()\r\n    {\r\n        Game.ActiveScene.LoadFromFile( \"scenes/mainmenu.scene\" );\r\n    }\r\n\r\n    private int GetScore() => ScoreManager.Instance?.Score ?? 0;\r\n\r\n    private string GetFormattedTime()\r\n    {\r\n        var shrinker = Scene.GetAllComponents<ArenaShrinker>().FirstOrDefault();\r\n        if ( !shrinker.IsValid() ) return \"02:00\";\r\n\r\n        int minutes = (int)Math.Max( 0, shrinker.TimeRemaining ) / 60;\r\n        int seconds = (int)Math.Max( 0, shrinker.TimeRemaining ) % 60;\r\n        return $\"{minutes:00}:{seconds:00}\";\r\n    }\r\n\r\n    private float GetPercent()\r\n    {\r\n        if ( PushAbility == null ) return 0f;\r\n        return MathX.Clamp( PushAbility.CooldownProgress * 100f, 0f, 100f );\r\n    }\r\n\r\n    private void ToggleBots()\r\n    {\r\n        BotSpawner.EnableBots = !BotSpawner.EnableBots;\r\n        StateHasChanged();\r\n    }\r\n\r\n    private int GetHighScore() => SaveManager.Current?.HighScore ?? 0;\r\n\r\n    private void OpenChat()\r\n    {\r\n        IsChatOpen = true;\r\n        IsWorkshopMenuOpen = false;\r\n        IsQSpawnMenuOpen = false;\r\n        ApplyMouseState();\r\n        StateHasChanged();\r\n        ChatInput?.Focus();\r\n    }\r\n\r\n    private void SendChatMessage()\r\n    {\r\n        if ( ChatInput == null || string.IsNullOrWhiteSpace( ChatInput.Text ) )\r\n        {\r\n            IsChatOpen = false;\r\n            ApplyMouseState();\r\n            return;\r\n        }\r\n\r\n        string name = Connection.Local?.DisplayName ?? L( \"\u0412\u044b\", \"You\" );\r\n        AddMessage( name, ChatInput.Text );\r\n\r\n        ChatInput.Text = \"\";\r\n        IsChatOpen = false;\r\n        ApplyMouseState();\r\n    }\r\n\r\n    public void AddMessage( string author, string text )\r\n    {\r\n        CanvasMessages.Add( new ChatMessage { Author = author, Text = text } );\r\n        if ( CanvasMessages.Count > 6 )\r\n        {\r\n            CanvasMessages.RemoveAt( 0 );\r\n        }\r\n        StateHasChanged();\r\n    }\r\n\r\n    private IEnumerable<PlayerInfo> GetPlayerList()\r\n    {\r\n        if ( Connection.All != null && Connection.All.Any() )\r\n        {\r\n            foreach ( var conn in Connection.All )\r\n            {\r\n                bool isLocal = conn == Connection.Local;\r\n                int score = isLocal ? GetScore() : 0;\r\n\r\n                yield return new PlayerInfo\r\n                {\r\n                    Name = string.IsNullOrEmpty( conn.DisplayName ) ? \"Player\" : conn.DisplayName,\r\n                    Score = score,\r\n                    IsLocal = isLocal\r\n                };\r\n            }\r\n        }\r\n        else\r\n        {\r\n            yield return new PlayerInfo\r\n            {\r\n                Name = L( \"\u0412\u044b\", \"You\" ),\r\n                Score = GetScore(),\r\n                IsLocal = true\r\n            };\r\n        }\r\n    }\r\n\r\n    private string GetWeatherTitle( WeatherType type ) => type switch\r\n    {\r\n        WeatherType.LowGravity => L( \"\u041d\u0415\u0412\u0415\u0421\u041e\u041c\u041e\u0421\u0422\u042c\", \"LOW GRAVITY\" ),\r\n        WeatherType.SlipperyIce => L( \"\u0413\u041e\u041b\u041e\u041b\u0401\u0414\", \"SLIPPERY ICE\" ),\r\n        WeatherType.StrongWind => L( \"\u0428\u0422\u041e\u0420\u041c\u041e\u0412\u041e\u0419 \u0412\u0415\u0422\u0415\u0420\", \"STRONG WIND\" ),\r\n        _ => \"\"\r\n    };\r\n\r\n    private string GetWeatherIcon( WeatherType type ) => type switch\r\n    {\r\n        WeatherType.LowGravity => \"\ud83d\ude80\",\r\n        WeatherType.SlipperyIce => \"\u2744\ufe0f\",\r\n        WeatherType.StrongWind => \"\ud83c\udf2a\ufe0f\",\r\n        _ => \"\u26a0\ufe0f\"\r\n    };\r\n\r\n    private string GetWeatherClass( WeatherType type ) => type switch\r\n    {\r\n        WeatherType.LowGravity => \"gravity\",\r\n        WeatherType.SlipperyIce => \"ice\",\r\n        WeatherType.StrongWind => \"wind\",\r\n        _ => \"\"\r\n    };\r\n\r\n    private int GetDeaths() => ScoreManager.Instance?.Deaths ?? 0;\r\n    public static bool EnableMusic { get; set; } = true;\r\n\r\n    private void ToggleMusic()\r\n    {\r\n        EnableMusic = !EnableMusic;\r\n        MusicManager.Instance?.ToggleMusic( EnableMusic );\r\n        StateHasChanged();\r\n    }\r\n\r\n    protected override int BuildHash()\r\n    {\r\n        var hash = new HashCode();\r\n        hash.Add( GetPercent() );\r\n        hash.Add( GetHealthPercent() );\r\n        hash.Add( LocalHealth?.IsDead );\r\n        hash.Add( (int)(LocalHealth?.RespawnTimer * 10f ?? 0) );\r\n        hash.Add( IsReady );\r\n        hash.Add( MapEventManager.Instance?.CurrentEvent );\r\n        hash.Add( (int)(MapEventManager.Instance?.TimeRemaining * 10f ?? 0) );\r\n        hash.Add( EnableMusic );\r\n        hash.Add( PushAbility?.ChargeProgress );\r\n        hash.Add( PushAbility?.IsCharging );\r\n        hash.Add( GetScore() );\r\n        hash.Add( GetDeaths() ); \r\n        hash.Add( GetFormattedTime() );\r\n        hash.Add( RoundManager.Instance?.CurrentRound );\r\n        hash.Add( IsMenuOpen );\r\n        hash.Add( IsWorkshopMenuOpen );\r\n        hash.Add( IsQSpawnMenuOpen );\r\n        hash.Add( ActiveTab );\r\n        hash.Add( GetCredits() );\r\n        hash.Add( GetForceLevel() );\r\n        hash.Add( GetMassLevel() );\r\n        hash.Add( GetEnergyLevel() );\r\n        hash.Add( GetPrestigeLevel() );\r\n        hash.Add( GetPrestigeTokens() );\r\n        hash.Add( CanPrestige() );\r\n        hash.Add( WeaponManager.Instance?.CurrentWeapon?.Id );\r\n        hash.Add( ShopManager.Instance?.SelectedItem?.Id );\r\n        hash.Add( IsSettingsOpen );\r\n        hash.Add( CurrentLanguage );\r\n        hash.Add( IsChatOpen );\r\n        hash.Add( CanvasMessages.Count );\r\n        hash.Add( Connection.All?.Count() ?? 0 );\r\n        hash.Add( Killfeed.Instance?.Entries.Count );\r\n        hash.Add( CurrentLbStat );\r\n        hash.Add( IsLbLoading );\r\n        hash.Add( LbEntries.Count );\r\n        return hash.ToHashCode();\r\n    }\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": ".obj/__compiler_extra.cs",
            "FileName": "__compiler_extra.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "global using static Sandbox.Internal.GlobalGameNamespace;\r\nglobal using Microsoft.AspNetCore.Components;\r\nglobal using Microsoft.AspNetCore.Components.Rendering;\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"AddonTitle\", \"One More Push\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"AddonIdent\", \"one_more_push\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"OrgIdent\", \"cherkbyte\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"Ident\", \"cherkbyte.one_more_push\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"EngineVersion\", \"29\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"EngineMinorVersion\", \"1\" )]\r\n\r\n[assembly: System.Runtime.Versioning.TargetFramework( \".NETCoreApp,Version=v9.0\", FrameworkDisplayName = \".NET 9.0\" )]\r\n[assembly: global::System.Reflection.AssemblyMetadata( \"CompileTime\", \"2026-10-06T04:15:32.2273688Z\" )]\r\n[assembly: global::System.Reflection.AssemblyVersion(\"0.0.110.0\")]\r\n[assembly: global::System.Reflection.AssemblyFileVersion(\"0.0.110.0\")]"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "ArenaRoundManeger1.cs",
            "FileName": "ArenaRoundManeger1.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Linq;\r\n\r\npublic enum ArenaRoundState\r\n{\r\n    WaitingForPlayers,\r\n    Countdown,\r\n    InGame,\r\n    RoundEnd\r\n}\r\n\r\npublic sealed class ArenaRoundManager : Component\r\n{\r\n    [Property] public float CountdownDuration { get; set; } = 5.0f;\r\n    [Property] public float RoundDuration { get; set; } = 60.0f;\r\n    [Property] public float EndPauseDuration { get; set; } = 4.0f;\r\n\r\n    public ArenaRoundState CurrentState { get; private set; } = ArenaRoundState.WaitingForPlayers;\r\n    public TimeUntil StateTimer { get; private set; }\r\n\r\n    protected override void OnStart()\r\n    {\r\n        SetState( ArenaRoundState.WaitingForPlayers );\r\n    }\r\n\r\n    protected override void OnUpdate()\r\n    {\r\n        switch ( CurrentState )\r\n        {\r\n            case ArenaRoundState.WaitingForPlayers:\r\n                if ( Connection.All.Count >= 1 )\r\n                {\r\n                    SetState( ArenaRoundState.Countdown );\r\n                }\r\n                break;\r\n\r\n            case ArenaRoundState.Countdown:\r\n                if ( StateTimer )\r\n                {\r\n                    SetState( ArenaRoundState.InGame );\r\n                }\r\n                break;\r\n\r\n            case ArenaRoundState.InGame:\r\n                if ( StateTimer )\r\n                {\r\n                    SetState( ArenaRoundState.RoundEnd );\r\n                }\r\n                break;\r\n\r\n            case ArenaRoundState.RoundEnd:\r\n                if ( StateTimer )\r\n                {\r\n                    SetState( ArenaRoundState.WaitingForPlayers );\r\n                }\r\n                break;\r\n        }\r\n    }\r\n\r\n    private void SetState( ArenaRoundState newState )\r\n    {\r\n        CurrentState = newState;\r\n\r\n        switch ( newState )\r\n        {\r\n            case ArenaRoundState.WaitingForPlayers:\r\n                Log.Info( \"[ArenaRoundManager] \u041e\u0436\u0438\u0434\u0430\u043d\u0438\u0435 \u0438\u0433\u0440\u043e\u043a\u043e\u0432...\" );\r\n                break;\r\n\r\n            case ArenaRoundState.Countdown:\r\n                StateTimer = CountdownDuration;\r\n                Log.Info( $\"[ArenaRoundManager] \u041e\u0442\u0441\u0447\u0451\u0442! \u0414\u043e \u0441\u0442\u0430\u0440\u0442\u0430: {CountdownDuration} \u0441\u0435\u043a.\" );\r\n                break;\r\n\r\n            case ArenaRoundState.InGame:\r\n                StateTimer = RoundDuration;\r\n                Log.Info( \"[ArenaRoundManager] \u0420\u0410\u0423\u041d\u0414 \u041d\u0410\u0427\u0410\u041b\u0421\u042f!\" );\r\n                break;\r\n\r\n            case ArenaRoundState.RoundEnd:\r\n                StateTimer = EndPauseDuration;\r\n                Log.Info( \"[ArenaRoundManager] \u0420\u0430\u0443\u043d\u0434 \u043e\u043a\u043e\u043d\u0447\u0435\u043d!\" );\r\n                break;\r\n        }\r\n    }\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "ExplosiveBarrel.cs",
            "FileName": "ExplosiveBarrel.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System;\r\nusing System.Linq;\r\n\r\npublic sealed class ExplosiveBarrel : Component, Component.ICollisionListener\r\n{\r\n    [Property] public float ExplosionRadius { get; set; } = 450f;\r\n    [Property] public float ExplosionForce { get; set; } = 65000f;\r\n    [Property] public float ImpactThreshold { get; set; } = 250f;\r\n    [Property] public GameObject ExplosionParticle { get; set; }\r\n    [Property] public SoundEvent ExplosionSound { get; set; }\r\n\r\n    private bool _hasExploded = false;\r\n\r\n    public void Explode()\r\n    {\r\n        if ( _hasExploded || !GameObject.IsValid() ) return;\r\n        _hasExploded = true;\r\n\r\n        if ( ExplosionParticle.IsValid() )\r\n        {\r\n            ExplosionParticle.Clone( WorldPosition );\r\n        }\r\n\r\n        if ( ExplosionSound != null )\r\n        {\r\n            Sound.Play( ExplosionSound, WorldPosition );\r\n        }\r\n\r\n        var hits = Scene.Trace.Sphere( ExplosionRadius, WorldPosition, WorldPosition + Vector3.Up * 2f )\r\n            .IgnoreGameObject( GameObject )\r\n            .RunAll();\r\n\r\n        foreach ( var hit in hits )\r\n        {\r\n            if ( !hit.GameObject.IsValid() ) continue;\r\n\r\n            var targetObj = hit.GameObject.Root;\r\n\r\n            var shield = targetObj.Components.Get<ShieldAbility>( true );\r\n            if ( shield.IsValid() && shield.IsShieldActive ) continue;\r\n\r\n            var chainBarrel = targetObj.Components.Get<ExplosiveBarrel>();\r\n            if ( chainBarrel.IsValid() && chainBarrel != this )\r\n            {\r\n                chainBarrel.Explode();\r\n                continue;\r\n            }\r\n\r\n            var rb = targetObj.Components.Get<Rigidbody>();\r\n            if ( rb.IsValid() )\r\n            {\r\n                var dir = (hit.GameObject.WorldPosition - WorldPosition).Normal;\r\n                dir.z = MathF.Max( dir.z, 0.4f );\r\n                rb.ApplyImpulse( dir * ExplosionForce );\r\n            }\r\n        }\r\n\r\n        Log.Info( $\"[ExplosiveBarrel] Booom! Barrel exploded at {WorldPosition}\" );\r\n        GameObject.Destroy();\r\n    }\r\n\r\n    public void OnCollisionStart( Collision collision )\r\n    {\r\n        if ( collision.Contact.Speed.Length > ImpactThreshold )\r\n        {\r\n            Explode();\r\n        }\r\n    }\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "HeavyAnvil.cs",
            "FileName": "HeavyAnvil.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\n\r\npublic sealed class HeavyAnvil : Component, Component.ICollisionListener\r\n{\r\n    [Property] public float PushResistanceMultiplier { get; set; } = 0.25f;\r\n    [Property] public float CrushSpeedThreshold { get; set; } = 300f;\r\n    [Property] public float CrushImpactForce { get; set; } = 75000f;\r\n    [Property] public SoundEvent ImpactSound { get; set; }\r\n\r\n    public void OnCollisionStart( Collision collision )\r\n    {\r\n        if ( collision.Contact.Speed.Length < CrushSpeedThreshold ) return;\r\n\r\n        var targetObj = collision.Other.GameObject?.Root;\r\n        if ( !targetObj.IsValid() || targetObj == GameObject.Root ) return;\r\n\r\n        if ( ImpactSound != null )\r\n        {\r\n            Sound.Play( ImpactSound, WorldPosition );\r\n        }\r\n\r\n        var cc = targetObj.Components.Get<CharacterController>();\r\n        if ( cc.IsValid() )\r\n        {\r\n            var dir = (targetObj.WorldPosition - WorldPosition).Normal;\r\n            dir.z = 0.5f;\r\n            cc.Velocity += dir * 900f;\r\n        }\r\n\r\n        var rb = targetObj.Components.Get<Rigidbody>();\r\n        if ( rb.IsValid() )\r\n        {\r\n            var dir = (targetObj.WorldPosition - WorldPosition).Normal;\r\n            dir.z = 0.5f;\r\n            rb.ApplyImpulse( dir * CrushImpactForce );\r\n        }\r\n    }\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "Killfeed.cs",
            "FileName": "Killfeed.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Collections.Generic;\r\n\r\npublic sealed class Killfeed : Component\r\n{\r\n\tpublic static Killfeed Instance { get; private set; }\r\n\r\n\tpublic struct KillEntry\r\n\t{\r\n\t\tpublic string KillerName { get; set; }\r\n\t\tpublic string VictimName { get; set; }\r\n\t\tpublic string Method { get; set; }\r\n\t\tpublic float Time { get; set; }\r\n\t}\r\n\r\n\tpublic List<KillEntry> Entries { get; private set; } = new();\r\n\r\n\tprotected override void OnStart()\r\n\t{\r\n\t\tInstance = this;\r\n\t}\r\n\r\n\tpublic void AddEntry( string killer, string victim, string method = \"Push\" )\r\n\t{\r\n\t\tEntries.Add( new KillEntry\r\n\t\t{\r\n\t\t\tKillerName = killer,\r\n\t\t\tVictimName = victim,\r\n\t\t\tMethod = method,\r\n\t\t\tTime = RealTime.Now\r\n\t\t} );\r\n\r\n\t\tif ( Entries.Count > 5 )\r\n\t\t{\r\n\t\t\tEntries.RemoveAt( 0 );\r\n\t\t}\r\n\t}\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "ObjectGrabber.cs",
            "FileName": "ObjectGrabber.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\n\r\npublic sealed class ObjectGrabber : Component\r\n{\r\n\t[Property] public float MaxReachDistance { get; set; } = 300f;\r\n\t[Property] public float HoldDistance { get; set; } = 150f;\r\n\t[Property] public float ThrowForce { get; set; } = 1200f;\r\n\r\n\tprivate GameObject _heldObject;\r\n\tprivate Rigidbody _heldRigidbody;\r\n\r\n\tprotected override void OnUpdate()\r\n\t{\r\n\t\tif ( IsProxy ) return;\r\n\r\n\t\tif ( Input.Pressed( \"use\" ) || Input.Pressed( \"e\" ) )\r\n\t\t{\r\n\t\t\tif ( _heldObject.IsValid() )\r\n\t\t\t{\r\n\t\t\t\tDropObject();\r\n\t\t\t}\r\n\t\t\telse\r\n\t\t\t{\r\n\t\t\t\tTryGrabObject();\r\n\t\t\t}\r\n\t\t}\r\n\r\n\t\tif ( Input.Pressed( \"attack1\" ) && _heldObject.IsValid() )\r\n\t\t{\r\n\t\t\tThrowObject();\r\n\t\t}\r\n\r\n\t\tif ( _heldObject.IsValid() )\r\n\t\t{\r\n\t\t\tUpdateHeldObjectPosition();\r\n\t\t}\r\n\t}\r\n\r\n\tprivate void TryGrabObject()\r\n\t{\r\n\t\tvar cam = Components.Get<CameraComponent>() ?? Scene.GetAllComponents<CameraComponent>().FirstOrDefault();\r\n\t\tif ( !cam.IsValid() ) return;\r\n\r\n\t\tvar ray = cam.Transform.World.Forward;\r\n\t\tvar start = cam.WorldPosition;\r\n\t\tvar end = start + ray * MaxReachDistance;\r\n\r\n\t\tvar tr = Scene.Trace.Ray( start, end )\r\n\t\t\t.IgnoreGameObject( GameObject )\r\n\t\t\t.Run();\r\n\r\n\t\tif ( tr.Hit && tr.GameObject.IsValid() )\r\n\t\t{\r\n\t\t\tvar rootObj = tr.GameObject.Root;\r\n\t\t\tvar rb = rootObj.Components.Get<Rigidbody>() ?? tr.GameObject.Components.Get<Rigidbody>();\r\n\r\n\t\t\tif ( rb.IsValid() && !rootObj.Tags.Has( \"player\" ) )\r\n\t\t\t{\r\n\t\t\t\t_heldObject = rootObj;\r\n\t\t\t\t_heldRigidbody = rb;\r\n\t\t\t\t_heldRigidbody.MotionEnabled = true;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tprivate void UpdateHeldObjectPosition()\r\n\t{\r\n\t\tvar cam = Components.Get<CameraComponent>() ?? Scene.GetAllComponents<CameraComponent>().FirstOrDefault();\r\n\t\tif ( !cam.IsValid() ) return;\r\n\r\n\t\tvar targetPos = cam.WorldPosition + cam.Transform.World.Forward * HoldDistance;\r\n\t\tvar dir = targetPos - _heldObject.WorldPosition;\r\n\r\n\t\tif ( _heldRigidbody.IsValid() )\r\n\t\t{\r\n\t\t\t_heldRigidbody.Velocity = dir * 10f;\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\t_heldObject.WorldPosition = Vector3.Lerp( _heldObject.WorldPosition, targetPos, Time.Delta * 15f );\r\n\t\t}\r\n\t}\r\n\r\n\tprivate void DropObject()\r\n\t{\r\n\t\t_heldObject = null;\r\n\t\t_heldRigidbody = null;\r\n\t}\r\n\r\n\tprivate void ThrowObject()\r\n\t{\r\n\t\tif ( !_heldObject.IsValid() ) return;\r\n\r\n\t\tvar cam = Components.Get<CameraComponent>() ?? Scene.GetAllComponents<CameraComponent>().FirstOrDefault();\r\n\t\tvar throwDir = cam.IsValid() ? cam.Transform.World.Forward : Transform.World.Forward;\r\n\r\n\t\tif ( _heldRigidbody.IsValid() )\r\n\t\t{\r\n\t\t\t_heldRigidbody.Velocity = throwDir * ThrowForce;\r\n\t\t}\r\n\r\n\t\tCameraJuice.Instance?.Shake( 8f );\r\n\r\n\t\t_heldObject = null;\r\n\t\t_heldRigidbody = null;\r\n\t}\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "Assembly.cs",
            "FileName": "Assembly.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "global using Sandbox;\r\nglobal using System.Collections.Generic;\r\nglobal using System.Linq;\r\n"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "MapEventManager.cs",
            "FileName": "MapEventManager.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System;\r\nusing System.Linq;\r\n\r\npublic enum WeatherType\r\n{\r\n    None,\r\n    LowGravity,\r\n    SlipperyIce,\r\n    StrongWind\r\n}\r\n\r\npublic sealed class MapEventManager : Component\r\n{\r\n    public static MapEventManager Instance { get; private set; }\r\n\r\n    [Property] public float EventDuration { get; set; } = 12f;\r\n    [Property] public float EventInterval { get; set; } = 25f;\r\n    [Property] public float LowGravityForce { get; set; } = 450f;\r\n    [Property] public float WindForce { get; set; } = 400f;\r\n\r\n    public WeatherType CurrentEvent { get; private set; } = WeatherType.None;\r\n    public Vector3 CurrentWindDirection { get; private set; } = Vector3.Right;\r\n    public float TimeRemaining { get; private set; }\r\n\r\n    private TimeUntil _nextEventTime;\r\n    private TimeUntil _eventEndTime;\r\n\r\n    protected override void OnAwake()\r\n    {\r\n        Instance = this;\r\n        _nextEventTime = EventInterval;\r\n    }\r\n\r\n    protected override void OnUpdate()\r\n    {\r\n        if ( IsProxy ) return;\r\n\r\n        if ( CurrentEvent == WeatherType.None && _nextEventTime )\r\n        {\r\n            StartRandomEvent();\r\n        }\r\n        else if ( CurrentEvent != WeatherType.None )\r\n        {\r\n            TimeRemaining = _eventEndTime;\r\n            if ( _eventEndTime )\r\n            {\r\n                EndEvent();\r\n            }\r\n        }\r\n    }\r\n\r\n    protected override void OnFixedUpdate()\r\n    {\r\n        if ( CurrentEvent == WeatherType.None ) return;\r\n\r\n        var players = Scene.GetAllComponents<CharacterController>();\r\n        var rigidbodies = Scene.GetAllComponents<Rigidbody>();\r\n\r\n        if ( CurrentEvent == WeatherType.LowGravity )\r\n        {\r\n            foreach ( var cc in players )\r\n            {\r\n                if ( cc.IsValid() )\r\n                {\r\n                    cc.Velocity += Vector3.Up * LowGravityForce * Time.Delta;\r\n                }\r\n            }\r\n            foreach ( var rb in rigidbodies )\r\n            {\r\n                if ( rb.IsValid() && !rb.IsProxy )\r\n                {\r\n                    rb.ApplyImpulse( Vector3.Up * (rb.PhysicsBody.Mass * LowGravityForce * Time.Delta) );\r\n                }\r\n            }\r\n        }\r\n        else if ( CurrentEvent == WeatherType.StrongWind )\r\n        {\r\n            foreach ( var cc in players )\r\n            {\r\n                if ( cc.IsValid() )\r\n                {\r\n                    cc.Velocity += CurrentWindDirection * WindForce * Time.Delta;\r\n                }\r\n            }\r\n            foreach ( var rb in rigidbodies )\r\n            {\r\n                if ( rb.IsValid() && !rb.IsProxy )\r\n                {\r\n                    rb.ApplyImpulse( CurrentWindDirection * WindForce * 15f * Time.Delta );\r\n                }\r\n            }\r\n        }\r\n        else if ( CurrentEvent == WeatherType.SlipperyIce )\r\n        {\r\n            foreach ( var cc in players )\r\n            {\r\n                if ( cc.IsValid() && cc.IsOnGround )\r\n                {\r\n                    var vel = cc.Velocity;\r\n                    vel.x *= 1.015f;\r\n                    vel.y *= 1.015f;\r\n                    cc.Velocity = vel;\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    private void StartRandomEvent()\r\n    {\r\n        var values = Enum.GetValues( typeof( WeatherType ) ).Cast<WeatherType>().Where( w => w != WeatherType.None ).ToArray();\r\n        CurrentEvent = values[Random.Shared.Next( values.Length )];\r\n        _eventEndTime = EventDuration;\r\n\r\n        if ( CurrentEvent == WeatherType.StrongWind )\r\n        {\r\n            float angle = Random.Shared.NextSingle() * MathF.PI * 2f;\r\n            CurrentWindDirection = new Vector3( MathF.Cos( angle ), MathF.Sin( angle ), 0f ).Normal;\r\n        }\r\n\r\n        Log.Info( $\"[MapEventManager] \u0417\u0430\u043f\u0443\u0449\u0435\u043d \u043f\u043e\u0433\u043e\u0434\u043d\u044b\u0439 \u0438\u0432\u0435\u043d\u0442: {CurrentEvent}\" );\r\n    }\r\n\r\n    private void EndEvent()\r\n    {\r\n        Log.Info( $\"[MapEventManager] \u0417\u0430\u0432\u0435\u0440\u0448\u0451\u043d \u0438\u0432\u0435\u043d\u0442: {CurrentEvent}\" );\r\n        CurrentEvent = WeatherType.None;\r\n        _nextEventTime = EventInterval;\r\n    }\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "PlayerAbilities.cs",
            "FileName": "PlayerAbilities.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System;\r\n\r\npublic sealed class PlayerAbilities : Component\r\n{\r\n    [Property] public GameObject HeavyObjectPrefab { get; set; }\r\n\r\n    [Property] public float DropCooldown { get; set; } = 8f;\r\n    [Property] public float ShieldCooldown { get; set; } = 6f;\r\n    [Property] public float ShieldDuration { get; set; } = 2f;\r\n\r\n    public bool IsShieldActive { get; private set; } = false;\r\n\r\n    private TimeSince _timeSinceDrop = 100f;\r\n    private TimeSince _timeSinceShield = 100f;\r\n    private TimeSince _shieldActiveTimer = 0f;\r\n\r\n    private Rigidbody _rb;\r\n    private float _originalMass = 0f;\r\n\r\n    protected override void OnStart()\r\n    {\r\n        _rb = Components.Get<Rigidbody>();\r\n        if ( _rb.IsValid() )\r\n        {\r\n            _originalMass = _rb.MassOverride;\r\n        }\r\n    }\r\n\r\n    protected override void OnUpdate()\r\n    {\r\n        if ( IsProxy ) return;\r\n\r\n        if ( IsShieldActive && _shieldActiveTimer > ShieldDuration )\r\n        {\r\n            DeactivateShield();\r\n        }\r\n\r\n        if ( Input.Pressed( \"attack2\" ) || Input.Pressed( \"use\" ) )\r\n        {\r\n            TryDropObject();\r\n        }\r\n\r\n        if ( Input.Pressed( \"run\" ) )\r\n        {\r\n            TryActivateShield();\r\n        }\r\n    }\r\n\r\n    private void TryDropObject()\r\n    {\r\n        if ( _timeSinceDrop < DropCooldown )\r\n        {\r\n            Log.Info( $\"[\u0421\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438] \u0421\u0431\u0440\u043e\u0441 \u043d\u0430 \u043f\u0435\u0440\u0435\u0437\u0430\u0440\u044f\u0434\u043a\u0435! \u041e\u0441\u0442\u0430\u043b\u043e\u0441\u044c: {DropCooldown - _timeSinceDrop:F1} \u0441\u0435\u043a.\" );\r\n            return;\r\n        }\r\n\r\n        var headPos = WorldPosition + Vector3.Up * 50f;\r\n        var forwardPos = headPos + WorldTransform.Forward * 600f;\r\n\r\n        var tr = Scene.Trace.Ray( headPos, forwardPos )\r\n                            .IgnoreGameObjectHierarchy( GameObject )\r\n                            .Run();\r\n\r\n        if ( tr.Hit && tr.GameObject.IsValid() )\r\n        {\r\n            var targetPos = tr.GameObject.WorldPosition;\r\n            var spawnPos = targetPos + Vector3.Up * 180f;\r\n\r\n            if ( HeavyObjectPrefab.IsValid() )\r\n            {\r\n                var obj = HeavyObjectPrefab.Clone( spawnPos );\r\n                if ( obj.IsValid() && Network.Active )\r\n                {\r\n                    obj.NetworkSpawn();\r\n                }\r\n\r\n                _timeSinceDrop = 0;\r\n                Log.Info( \"[\u0421\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438] \u0422\u044f\u0436\u0435\u043b\u044b\u0439 \u043e\u0431\u044a\u0435\u043a\u0442 \u0441\u0431\u0440\u043e\u0448\u0435\u043d \u043f\u0440\u044f\u043c\u043e \u043d\u0430 \u0432\u0440\u0430\u0433\u0430!\" );\r\n            }\r\n            else\r\n            {\r\n                Log.Warning( \"[\u0421\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438] HeavyObjectPrefab \u043d\u0435 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0432 \u0418\u043d\u0441\u043f\u0435\u043a\u0442\u043e\u0440\u0435!\" );\r\n            }\r\n        }\r\n        else\r\n        {\r\n            Log.Info( \"[\u0421\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438] \u041d\u0438\u043a\u043e\u0433\u043e \u043d\u0435 \u043d\u0430\u0448\u043b\u0438 \u043f\u0435\u0440\u0435\u0434 \u0441\u043e\u0431\u043e\u0439 \u0434\u043b\u044f \u0430\u0442\u0430\u043a\u0438!\" );\r\n        }\r\n    }\r\n\r\n    private void TryActivateShield()\r\n    {\r\n        if ( _timeSinceShield < ShieldCooldown || IsShieldActive ) return;\r\n\r\n        IsShieldActive = true;\r\n        _shieldActiveTimer = 0;\r\n        _timeSinceShield = 0;\r\n\r\n        if ( _rb.IsValid() )\r\n        {\r\n            _originalMass = _rb.MassOverride;\r\n            _rb.MassOverride = 1000f; \r\n        }\r\n\r\n        Log.Info( \"[\u0421\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438] \u0429\u0418\u0422 \u0410\u041a\u0422\u0418\u0412\u0418\u0420\u041e\u0412\u0410\u041d!\" );\r\n    }\r\n\r\n    private void DeactivateShield()\r\n    {\r\n        IsShieldActive = false;\r\n\r\n        if ( _rb.IsValid() )\r\n        {\r\n            _rb.MassOverride = _originalMass; \r\n        }\r\n\r\n        Log.Info( \"[\u0421\u043f\u043e\u0441\u043e\u0431\u043d\u043e\u0441\u0442\u0438] \u0414\u0435\u0439\u0441\u0442\u0432\u0438\u0435 \u0449\u0438\u0442\u0430 \u0437\u0430\u043a\u043e\u043d\u0447\u0438\u043b\u043e\u0441\u044c.\" );\r\n    }\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "LeaderboardModal.razor",
            "FileName": "LeaderboardModal.razor",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "@using Sandbox;\r\n@using Sandbox.UI;\r\n@using Sandbox.Services;\r\n@using System;\r\n@using System.Threading.Tasks;\r\n@using System.Collections.Generic;\r\n@using System.Linq;\r\n@inherits Panel\r\n\r\n<root class=\"lb-root @(IsOpen ? \"open\" : \"\")\">\r\n    <div class=\"lb-card\">\r\n        <div class=\"lb-header\">\r\n            <div class=\"lb-title\">GLOBAL LEADERBOARD</div>\r\n            <button class=\"lb-close\" onclick=\"@Toggle\">\u2715</button>\r\n        </div>\r\n\r\n        <div class=\"lb-tabs\">\r\n            <button class=\"tab-btn @(CurrentStat == \"pushes_count\" ? \"active\" : \"\")\" onclick=\"@(() => SelectStat(\"pushes_count\"))\">\r\n                PUSHES\r\n            </button>\r\n            <button class=\"tab-btn @(CurrentStat == \"rounds_won\" ? \"active\" : \"\")\" onclick=\"@(() => SelectStat(\"rounds_won\"))\">\r\n                WINS\r\n            </button>\r\n        </div>\r\n\r\n        <div class=\"lb-content\">\r\n            @if (IsLoading)\r\n            {\r\n                <div class=\"lb-status\">LOADING DATA...</div>\r\n            }\r\n            else if (Entries == null || Entries.Count == 0)\r\n            {\r\n                <div class=\"lb-status\">NO RECORD FOUND</div>\r\n            }\r\n            else\r\n            {\r\n                <div class=\"lb-table\">\r\n                    <div class=\"table-header\">\r\n                        <span class=\"col-rank\">#</span>\r\n                        <span class=\"col-player\">PLAYER</span>\r\n                        <span class=\"col-score\">SCORE</span>\r\n                    </div>\r\n                    <div class=\"table-body\">\r\n                        @foreach (var entry in Entries)\r\n                        {\r\n                            <div class=\"table-row @(entry.SteamId == (Connection.Local?.SteamId ?? 0) ? \"is-me\" : \"\")\">\r\n                                <span class=\"col-rank\">@GetRankBadge(entry.Rank)</span>\r\n                                <span class=\"col-player\">@entry.DisplayName</span>\r\n                                <span class=\"col-score\">@entry.Value.ToString(\"N0\")</span>\r\n                            </div>\r\n                        }\r\n                    </div>\r\n                </div>\r\n            }\r\n        </div>\r\n    </div>\r\n</root>\r\n\r\n@code {\r\n    public class EntryItem\r\n    {\r\n        public int Rank { get; set; }\r\n        public string DisplayName { get; set; }\r\n        public double Value { get; set; }\r\n        public ulong SteamId { get; set; }\r\n    }\r\n\r\n    public bool IsOpen { get; set; } = false;\r\n    public bool IsLoading { get; set; } = true;\r\n    public string CurrentStat { get; set; } = \"pushes_count\";\r\n    public List<EntryItem> Entries { get; set; } = new();\r\n\r\n    protected override void OnAfterTreeRender( bool firstTime )\r\n    {\r\n        base.OnAfterTreeRender( firstTime );\r\n        if ( firstTime )\r\n        {\r\n            _ = RefreshData();\r\n        }\r\n    }\r\n\r\n    public void Toggle()\r\n    {\r\n        IsOpen = !IsOpen;\r\n        if (IsOpen)\r\n        {\r\n            _ = RefreshData();\r\n        }\r\n        StateHasChanged();\r\n    }\r\n\r\n    private async Task SelectStat(string statIdent)\r\n    {\r\n        if (CurrentStat == statIdent) return;\r\n        CurrentStat = statIdent;\r\n        await RefreshData();\r\n    }\r\n\r\n    private async Task RefreshData()\r\n    {\r\n        IsLoading = true;\r\n        StateHasChanged();\r\n\r\n        try\r\n        {\r\n            var board = Leaderboards.GetFromStat(\"seilordstudio.one_more_push\", CurrentStat);\r\n            board.SetAggregationSum();\r\n            board.MaxEntries = 20;\r\n            await board.Refresh();\r\n\r\n            if (board.Entries != null)\r\n            {\r\n                Entries = board.Entries.Select(x => new EntryItem\r\n                {\r\n                    Rank = Convert.ToInt32(x.Rank),\r\n                    DisplayName = x.DisplayName ?? \"Player\",\r\n                    Value = Convert.ToDouble(x.Value),\r\n                    SteamId = Convert.ToUInt64(x.SteamId)\r\n                }).ToList();\r\n            }\r\n            else\r\n            {\r\n                Entries = new();\r\n            }\r\n        }\r\n        catch (System.Exception ex)\r\n        {\r\n            Log.Warning($\"Leaderboard fetch failed: {ex.Message}\");\r\n        }\r\n\r\n        IsLoading = false;\r\n        StateHasChanged();\r\n    }\r\n\r\n    private string GetRankBadge(int rank)\r\n    {\r\n        return rank switch\r\n        {\r\n            1 => \"\ud83e\udd47 1\",\r\n            2 => \"\ud83e\udd48 2\",\r\n            3 => \"\ud83e\udd49 3\",\r\n            _ => rank.ToString()\r\n        };\r\n    }\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "RoundManager.cs",
            "FileName": "RoundManager.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\nusing System.Threading.Tasks;\r\n\r\npublic sealed class RoundManager : Component\r\n{\r\n    public static RoundManager Instance { get; private set; }\r\n\r\n    [Property] public float RoundTime { get; set; } = 120f;\r\n    [Sync] public float TimeLeft { get; set; }\r\n    [Sync] public bool IsInRound { get; set; }\r\n    [Sync] public int CurrentRound { get; set; } = 1;\r\n\r\n    protected override void OnAwake()\r\n    {\r\n        Instance = this;\r\n    }\r\n\r\n    protected override void OnStart()\r\n    {\r\n        StartNewRound();\r\n    }\r\n\r\n    public void StartNewRound()\r\n    {\r\n        TimeLeft = RoundTime;\r\n        IsInRound = true;\r\n\r\n        ResetArena();\r\n        RespawnAllPlayers();\r\n\r\n        var shrinker = Scene.GetAllComponents<ArenaShrinker>().FirstOrDefault();\r\n        if ( shrinker.IsValid() )\r\n        {\r\n            shrinker.StartRound();\r\n        }\r\n\r\n        Log.Info( $\"[RoundManager] \u0420\u0430\u0443\u043d\u0434 {CurrentRound} \u043d\u0430\u0447\u0430\u0442!\" );\r\n    }\r\n\r\n    public void OnPlayerEliminated( GameObject player )\r\n    {\r\n        if ( !IsInRound ) return;\r\n\r\n        if ( player.IsValid() )\r\n        {\r\n            player.Enabled = false;\r\n        }\r\n\r\n        var activePlayers = Scene.GetAllComponents<PushAbility>()\r\n            .Where( p => p.GameObject.IsValid() && p.GameObject.Enabled );\r\n\r\n        if ( activePlayers.Count() <= 1 )\r\n        {\r\n            _ = EndRound();\r\n        }\r\n    }\r\n\r\n    public async Task EndRound()\r\n    {\r\n        if ( !IsInRound ) return;\r\n\r\n        var winner = Scene.GetAllComponents<PushAbility>()\r\n            .FirstOrDefault( p => p.GameObject.IsValid() && p.GameObject.Enabled );\r\n\r\n        if ( winner.IsValid() && !winner.IsProxy )\r\n        {\r\n            GameServices.RecordRoundWin();\r\n        }\r\n\r\n        _ = GameServices.SaveStatsAsync();\r\n\r\n        IsInRound = false;\r\n        CurrentRound++;\r\n\r\n        Log.Info( $\"\u0420\u0430\u0443\u043d\u0434 {CurrentRound - 1} \u0437\u0430\u0432\u0435\u0440\u0448\u0435\u043d! \u041f\u0435\u0440\u0435\u0437\u0430\u043f\u0443\u0441\u043a \u0447\u0435\u0440\u0435\u0437 3 \u0441\u0435\u043a\u0443\u043d\u0434\u044b...\" );\r\n\r\n        await Task.DelaySeconds( 3f );\r\n\r\n        StartNewRound();\r\n    }\r\n\r\n    private void ResetArena()\r\n    {\r\n        var tiles = Scene.GetAllComponents<ArenaTile>();\r\n        foreach ( var tile in tiles )\r\n        {\r\n            if ( tile.IsValid() )\r\n            {\r\n                tile.ResetTile();\r\n            }\r\n        }\r\n    }\r\n\r\n    private void RespawnAllPlayers()\r\n    {\r\n        var spawn = Scene.GetAllComponents<SpawnPoint>().FirstOrDefault();\r\n        Vector3 spawnPos = spawn.IsValid() ? spawn.WorldPosition : new Vector3( 0, 0, 250 );\r\n\r\n        var players = Scene.GetAllComponents<PushAbility>();\r\n        foreach ( var player in players )\r\n        {\r\n            if ( !player.IsValid() ) continue;\r\n\r\n            var root = player.GameObject.Root;\r\n            root.Enabled = true;\r\n            root.WorldPosition = spawnPos;\r\n\r\n            var cc = root.Components.Get<CharacterController>( true );\r\n            if ( cc.IsValid() )\r\n            {\r\n                cc.Velocity = Vector3.Zero;\r\n            }\r\n\r\n            var rb = root.Components.Get<Rigidbody>( true );\r\n            if ( rb.IsValid() )\r\n            {\r\n                rb.Velocity = Vector3.Zero;\r\n                rb.AngularVelocity = Vector3.Zero;\r\n            }\r\n        }\r\n    }\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "ShopManager.cs",
            "FileName": "ShopManager.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\n\r\npublic class PushItem\r\n{\r\n    public string Id { get; set; }\r\n    public string NameRu { get; set; }\r\n    public string NameEn { get; set; }\r\n    public int Cost { get; set; }\r\n    public float ForceMultiplier { get; set; }\r\n    public string Key { get; set; }\r\n    public bool IsUnlocked { get; set; }\r\n\r\n    public string GetName( string lang ) => lang == \"EN\" ? NameEn : NameRu;\r\n}\r\n\r\npublic class ShopManager : Component\r\n{\r\n    public static ShopManager Instance { get; private set; }\r\n\r\n    public List<PushItem> Items { get; private set; } = new()\r\n    {\r\n        new PushItem { Id = \"basic\", NameRu = \"\u041e\u0431\u044b\u0447\u043d\u044b\u0439\", NameEn = \"Basic\", Cost = 0, ForceMultiplier = 1.0f, Key = \"1\", IsUnlocked = true },\r\n        new PushItem { Id = \"heavy\", NameRu = \"\u0422\u044f\u0436\u0435\u043b\u044b\u0439\", NameEn = \"Heavy\", Cost = 10, ForceMultiplier = 1.5f, Key = \"2\", IsUnlocked = false },\r\n        new PushItem { Id = \"force\", NameRu = \"\u0418\u043c\u043f\u0443\u043b\u044c\u0441\", NameEn = \"Impulse\", Cost = 25, ForceMultiplier = 2.2f, Key = \"3\", IsUnlocked = false },\r\n        new PushItem { Id = \"mega\", NameRu = \"\u041c\u0435\u0433\u0430-\u0442\u043e\u043b\u0447\u043e\u043a\", NameEn = \"Mega Push\", Cost = 50, ForceMultiplier = 3.5f, Key = \"4\", IsUnlocked = false }\r\n    };\r\n\r\n    public PushItem SelectedItem { get; private set; }\r\n\r\n    protected override void OnStart()\r\n    {\r\n        Instance = this;\r\n        SelectedItem = Items.FirstOrDefault( x => x.IsUnlocked );\r\n    }\r\n\r\n    protected override void OnUpdate()\r\n    {\r\n        if ( Input.Pressed( \"slot1\" ) ) SelectByKey( \"1\" );\r\n        if ( Input.Pressed( \"slot2\" ) ) SelectByKey( \"2\" );\r\n        if ( Input.Pressed( \"slot3\" ) ) SelectByKey( \"3\" );\r\n        if ( Input.Pressed( \"slot4\" ) ) SelectByKey( \"4\" );\r\n    }\r\n\r\n    public void SelectByKey( string key )\r\n    {\r\n        var item = Items.FirstOrDefault( x => x.Key == key );\r\n        if ( item != null ) TryBuyOrSelect( item );\r\n    }\r\n\r\n    public bool TryBuyOrSelect( PushItem item )\r\n    {\r\n        if ( item.IsUnlocked )\r\n        {\r\n            SelectedItem = item;\r\n            return true;\r\n        }\r\n\r\n        int currentScore = ScoreManager.Instance?.Score ?? 0;\r\n        if ( currentScore >= item.Cost )\r\n        {\r\n            item.IsUnlocked = true;\r\n            SelectedItem = item;\r\n            return true;\r\n        }\r\n\r\n        return false;\r\n    }\r\n\r\n    public float GetCurrentForceMultiplier()\r\n    {\r\n        return SelectedItem?.ForceMultiplier ?? 1.0f;\r\n    }\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "MainMenu.razor",
            "FileName": "MainMenu.razor",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "@namespace Sandbox\r\n@using Sandbox;\r\n@using Sandbox.UI;\r\n@inherits PanelComponent\r\n\r\n<root>\r\n    <div class=\"background-image\"></div>\r\n    <div class=\"background-overlay\"></div>\r\n\r\n    <div class=\"header\">\r\n        <div class=\"logo\">\r\n            <span class=\"logo-text\">@GetText(\"game_title\")</span>\r\n        </div>\r\n        <div class=\"header-actions\">\r\n            <button class=\"icon-button\" onclick=\"@OnSettingsClicked\" onmouseover=\"@PlayHoverSound\">\r\n                <span>\u2699\ufe0f\ufe0f @GetText(\"settings_btn\")</span>\r\n            </button>\r\n            <button class=\"icon-button lang\" onclick=\"@OnLangClicked\" onmouseover=\"@PlayHoverSound\">\r\n                <span>\ud83c\udf10 @GetText(\"lang_btn\")</span>\r\n            </button>\r\n            <button class=\"icon-button exit-btn\" onclick=\"@OnQuitClicked\" onmouseover=\"@PlayHoverSound\">\r\n                <span>\ud83d\udeaa @GetText(\"exit_btn\")</span>\r\n            </button>\r\n        </div>\r\n    </div>\r\n\r\n    <div class=\"main-content\">\r\n        <div class=\"modes-section\">\r\n            <div class=\"title-group\">\r\n                <div class=\"main-title\">@GetText(\"title\")</div>\r\n                <div class=\"subtitle\">@GetText(\"subtitle\")</div>\r\n            </div>\r\n\r\n            <div class=\"modes-wrapper\">\r\n                <div class=\"mode-card sumo\" onclick=\"@(() => OnModeClicked(GameMode.SumoPush))\" onmouseover=\"@PlayHoverSound\">\r\n                    <div class=\"card-badge sumo-badge\">@GetText(\"sumo_tag\")</div>\r\n                    <div class=\"card-icon-wrapper\">\r\n                        <span class=\"card-icon\">\ud83e\udd3c</span>\r\n                    </div>\r\n                    <div class=\"card-content\">\r\n                        <div class=\"mode-title\">@GetText(\"sumo_title\")</div>\r\n                        <div class=\"mode-desc\">@GetText(\"sumo_desc\")</div>\r\n                    </div>\r\n                </div>\r\n\r\n                <div class=\"mode-card nextbot disabled\">\r\n                    <div class=\"card-badge nextbot-badge disabled-badge\">@GetText(\"nextbot_tag\")</div>\r\n                    <div class=\"card-icon-wrapper\">\r\n                        <span class=\"card-icon\">\ud83c\udfc3</span>\r\n                    </div>\r\n                    <div class=\"card-content\">\r\n                        <div class=\"mode-title\">@GetText(\"nextbot_title\")</div>\r\n                        <div class=\"mode-desc\">@GetText(\"nextbot_desc\")</div>\r\n                    </div>\r\n                </div>\r\n            </div>\r\n        </div>\r\n\r\n        <div class=\"news-panel\">\r\n            <div class=\"news-header\">\r\n                <span class=\"news-title\">@GetText(\"news_title\")</span>\r\n                <span class=\"news-badge\">@GetText(\"news_tag_new\")</span>\r\n            </div>\r\n            <div class=\"news-list\">\r\n                <div class=\"news-item item-highlight\" onmouseover=\"@PlayHoverSound\">\r\n                    <div class=\"news-item-title\">\ud83d\udd25 @GetText(\"news_item1_title\")</div>\r\n                    <div class=\"news-item-desc\">@GetText(\"news_item1_desc\")</div>\r\n                </div>\r\n                <div class=\"news-item\" onmouseover=\"@PlayHoverSound\">\r\n                    <div class=\"news-item-title\">\u2728 @GetText(\"news_item2_title\")</div>\r\n                    <div class=\"news-item-desc\">@GetText(\"news_item2_desc\")</div>\r\n                </div>\r\n                <div class=\"news-item\" onmouseover=\"@PlayHoverSound\">\r\n                    <div class=\"news-item-title\">\u26a1 @GetText(\"news_item3_title\")</div>\r\n                    <div class=\"news-item-desc\">@GetText(\"news_item3_desc\")</div>\r\n                </div>\r\n            </div>\r\n        </div>\r\n    </div>\r\n\r\n    @if ( IsSettingsOpen )\r\n    {\r\n        <div class=\"modal-overlay\">\r\n            <div class=\"modal-window\">\r\n                <div class=\"modal-header\">\r\n                    <div class=\"modal-title\">@GetText(\"settings_title\")</div>\r\n                    <button class=\"close-x\" onclick=\"@OnCloseSettingsClicked\" onmouseover=\"@PlayHoverSound\">\u2715</button>\r\n                </div>\r\n\r\n                <div class=\"modal-body\">\r\n                    <div class=\"setting-item\">\r\n                        <div class=\"setting-label\">@GetText(\"volume_label\"): @((int)(Volume * 100))%</div>\r\n                        <div class=\"volume-buttons\">\r\n                            <button class=\"vol-btn\" onclick=\"@(() => OnVolumeBtnClicked(0.25f))\" onmouseover=\"@PlayHoverSound\">25%</button>\r\n                            <button class=\"vol-btn\" onclick=\"@(() => OnVolumeBtnClicked(0.50f))\" onmouseover=\"@PlayHoverSound\">50%</button>\r\n                            <button class=\"vol-btn\" onclick=\"@(() => OnVolumeBtnClicked(0.75f))\" onmouseover=\"@PlayHoverSound\">75%</button>\r\n                            <button class=\"vol-btn\" onclick=\"@(() => OnVolumeBtnClicked(1.00f))\" onmouseover=\"@PlayHoverSound\">100%</button>\r\n                        </div>\r\n                    </div>\r\n\r\n                    <div class=\"controls-section\">\r\n                        <div class=\"controls-title\">@GetText(\"controls_title\")</div>\r\n                        <div class=\"control-row\">@GetText(\"ctrl_move\")</div>\r\n                        <div class=\"control-row\">@GetText(\"ctrl_action\")</div>\r\n                        <div class=\"control-row\">@GetText(\"ctrl_space\")</div>\r\n                    </div>\r\n                </div>\r\n\r\n                <div class=\"modal-footer\">\r\n                    <button class=\"modal-close-btn\" onclick=\"@OnCloseSettingsClicked\" onmouseover=\"@PlayHoverSound\">@GetText(\"close_btn\")</button>\r\n                </div>\r\n            </div>\r\n        </div>\r\n    }\r\n\r\n    @if ( IsLoading )\r\n    {\r\n        <div class=\"loading-overlay\">\r\n            <div class=\"loading-box\">\r\n                <div class=\"loading-mode-title\">@(TargetMode == GameMode.SumoPush ? GetText(\"sumo_title\") : GetText(\"nextbot_title\"))</div>\r\n                <div class=\"loading-status\">@GetText(\"loading_title\")</div>\r\n                <div class=\"loading-sub\">@GetText(\"loading_sub\")</div>\r\n\r\n                <div class=\"progress-bar-bg\">\r\n                    <div class=\"progress-bar-fill\" style=\"width: @(LoadingProgress)%;\"></div>\r\n                </div>\r\n                <div class=\"loading-percent\">@((int)LoadingProgress)%</div>\r\n\r\n                <div class=\"tip-box\">\r\n                    <span class=\"tip-label\">@GetText(\"tip_label\")</span>\r\n                    <span class=\"tip-text\">@(TargetMode == GameMode.SumoPush ? GetText(\"tip_sumo\") : GetText(\"tip_nextbot\"))</span>\r\n                </div>\r\n            </div>\r\n        </div>\r\n    }\r\n</root>\r\n\r\n@code {\r\n    [Property] public SoundEvent HoverSound { get; set; }\r\n    [Property] public SoundEvent ClickSound { get; set; }\r\n\r\n    private bool IsSettingsOpen { get; set; } = false;\r\n    private bool IsLoading { get; set; } = false;\r\n    private GameMode TargetMode { get; set; }\r\n    private float LoadingProgress { get; set; } = 0f;\r\n    private TimeSince TimeSinceLoadingStarted;\r\n    private float Volume { get; set; } = 1.0f;\r\n    private const float TotalLoadingTime = 6.0f;\r\n\r\n    private void PlayHoverSound()\r\n    {\r\n        if ( HoverSound != null )\r\n            Sound.Play( HoverSound );\r\n        else\r\n            Sound.Play( \"ui.button.hover\" );\r\n    }\r\n\r\n    private void PlayClickSound()\r\n    {\r\n        if ( ClickSound != null )\r\n            Sound.Play( ClickSound );\r\n        else\r\n            Sound.Play( \"ui.button.press\" );\r\n    }\r\n\r\n    protected override void OnUpdate()\r\n    {\r\n        base.OnUpdate();\r\n\r\n        if ( IsLoading )\r\n        {\r\n            float elapsed = TimeSinceLoadingStarted;\r\n            LoadingProgress = MathX.Clamp( (elapsed / TotalLoadingTime) * 100f, 0f, 100f );\r\n\r\n            if ( elapsed >= TotalLoadingTime )\r\n            {\r\n                IsLoading = false;\r\n                if ( ModeController.Instance != null )\r\n                {\r\n                    ModeController.Instance.SelectMode( TargetMode );\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    private void OnModeClicked( GameMode mode )\r\n    {\r\n        PlayClickSound();\r\n        StartLoading( mode );\r\n    }\r\n\r\n    private void OnSettingsClicked()\r\n    {\r\n        PlayClickSound();\r\n        ToggleSettings();\r\n    }\r\n\r\n    private void OnCloseSettingsClicked()\r\n    {\r\n        PlayClickSound();\r\n        ToggleSettings();\r\n    }\r\n\r\n    private void OnLangClicked()\r\n    {\r\n        PlayClickSound();\r\n        OnToggleLanguage();\r\n    }\r\n\r\n    private void OnQuitClicked()\r\n    {\r\n        PlayClickSound();\r\n        Game.Close();\r\n    }\r\n\r\n    private void OnVolumeBtnClicked( float val )\r\n    {\r\n        PlayClickSound();\r\n        SetVolume( val );\r\n    }\r\n\r\n    private void StartLoading( GameMode mode )\r\n    {\r\n        if ( IsLoading ) return;\r\n\r\n        TargetMode = mode;\r\n        IsLoading = true;\r\n        LoadingProgress = 0f;\r\n        TimeSinceLoadingStarted = 0;\r\n    }\r\n\r\n    private void ToggleSettings()\r\n    {\r\n        if ( IsLoading ) return;\r\n        IsSettingsOpen = !IsSettingsOpen;\r\n    }\r\n\r\n    private void SetVolume( float val )\r\n    {\r\n        Volume = val;\r\n        if ( ModeController.Instance != null )\r\n        {\r\n            ModeController.Instance.MasterVolume = val;\r\n        }\r\n    }\r\n\r\n    private void OnToggleLanguage()\r\n    {\r\n        if ( IsLoading ) return;\r\n        if ( ModeController.Instance != null )\r\n        {\r\n            ModeController.Instance.ToggleLanguage();\r\n        }\r\n    }\r\n\r\n    private string GetText( string key )\r\n    {\r\n        return ModeController.Instance?.GetText( key ) ?? key;\r\n    }\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "MetaProgression.cs",
            "FileName": "MetaProgression.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\nusing System;\r\n\r\npublic sealed class MetaProgression : Component\r\n{\r\n\t[Property] public int Credits { get; private set; } = 0;\r\n\r\n\t// \u0423\u0440\u043e\u0432\u043d\u0438 \u043c\u0435\u0442\u0430-\u043f\u0440\u043e\u043a\u0430\u0447\u043a\u0438\r\n\t[Property] public int ForceLevel { get; private set; } = 0;\r\n\t[Property] public int MassLevel { get; private set; } = 0;\r\n\t[Property] public int EnergyLevel { get; private set; } = 0;\r\n\r\n\t// \u041f\u0440\u0435\u0441\u0442\u0438\u0436\r\n\t[Property] public int PrestigeLevel { get; private set; } = 0;\r\n\t[Property] public int PrestigeTokens { get; private set; } = 0;\r\n\r\n\t// \u0422\u0440\u0435\u0431\u043e\u0432\u0430\u043d\u0438\u044f \u0434\u043b\u044f \u0441\u0431\u0440\u043e\u0441\u0430 (\u0441\u0443\u043c\u043c\u0430 \u0443\u0440\u043e\u0432\u043d\u0435\u0439 \u043f\u0440\u043e\u043a\u0430\u0447\u043a\u0438)\r\n\tpublic const int REQUIRED_TOTAL_LEVELS_FOR_PRESTIGE = 15;\r\n\r\n\tprivate const string SAVE_FILE = \"one_more_push_meta.json\";\r\n\r\n\tprotected override void OnStart()\r\n\t{\r\n\t\tLoadProgress();\r\n\t}\r\n\r\n\tpublic void AddCredits( int amount )\r\n\t{\r\n\t\t// \u041c\u043d\u043e\u0436\u0438\u0442\u0435\u043b\u044c \u043f\u0440\u0435\u0441\u0442\u0438\u0436\u0430 (+50% \u0437\u0430 \u043a\u0430\u0436\u0434\u044b\u0439 \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u043f\u0440\u0435\u0441\u0442\u0438\u0436\u0430)\r\n\t\tfloat multiplier = 1.0f + (PrestigeLevel * 0.5f);\r\n\t\tCredits += (int)(amount * multiplier);\r\n\t\tSaveProgress();\r\n\t}\r\n\r\n\tpublic int GetUpgradeCost( int currentLevel )\r\n\t{\r\n\t\treturn (int)(100 * Math.Pow( 1.5, currentLevel ));\r\n\t}\r\n\r\n\tpublic bool TryUpgradeForce()\r\n\t{\r\n\t\tint cost = GetUpgradeCost( ForceLevel );\r\n\t\tif ( Credits >= cost )\r\n\t\t{\r\n\t\t\tCredits -= cost;\r\n\t\t\tForceLevel++;\r\n\t\t\tSaveProgress();\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\tpublic bool TryUpgradeMass()\r\n\t{\r\n\t\tint cost = GetUpgradeCost( MassLevel );\r\n\t\tif ( Credits >= cost )\r\n\t\t{\r\n\t\t\tCredits -= cost;\r\n\t\t\tMassLevel++;\r\n\t\t\tSaveProgress();\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\tpublic bool TryUpgradeEnergy()\r\n\t{\r\n\t\tint cost = GetUpgradeCost( EnergyLevel );\r\n\t\tif ( Credits >= cost )\r\n\t\t{\r\n\t\t\tCredits -= cost;\r\n\t\t\tEnergyLevel++;\r\n\t\t\tSaveProgress();\r\n\t\t\treturn true;\r\n\t\t}\r\n\t\treturn false;\r\n\t}\r\n\r\n\t// \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430: \u0433\u043e\u0442\u043e\u0432 \u043b\u0438 \u0438\u0433\u0440\u043e\u043a \u043a \u043f\u0440\u0435\u0441\u0442\u0438\u0436\u0443\r\n\tpublic bool CanPrestige()\r\n\t{\r\n\t\tint totalLevels = ForceLevel + MassLevel + EnergyLevel;\r\n\t\treturn totalLevels >= REQUIRED_TOTAL_LEVELS_FOR_PRESTIGE;\r\n\t}\r\n\r\n\t// \u0421\u0431\u0440\u043e\u0441 \u043f\u0440\u043e\u0433\u0440\u0435\u0441\u0441\u0430 \u0440\u0430\u0434\u0438 \u041f\u0440\u0435\u0441\u0442\u0438\u0436\u0430\r\n\tpublic bool TryPrestige()\r\n\t{\r\n\t\tif ( !CanPrestige() ) return false;\r\n\r\n\t\t// \u0421\u0431\u0440\u0430\u0441\u044b\u0432\u0430\u0435\u043c \u043e\u0431\u044b\u0447\u043d\u044b\u0435 \u0443\u0440\u043e\u0432\u043d\u0438 \u0438 \u043a\u0440\u0435\u0434\u0438\u0442\u044b\r\n\t\tForceLevel = 0;\r\n\t\tMassLevel = 0;\r\n\t\tEnergyLevel = 0;\r\n\t\tCredits = 0;\r\n\r\n\t\t// \u041f\u043e\u0432\u044b\u0448\u0430\u0435\u043c \u0443\u0440\u043e\u0432\u0435\u043d\u044c \u043f\u0440\u0435\u0441\u0442\u0438\u0436\u0430 \u0438 \u0432\u044b\u0434\u0430\u0435\u043c \u0442\u043e\u043a\u0435\u043d\u044b\r\n\t\tPrestigeLevel++;\r\n\t\tPrestigeTokens += 1;\r\n\r\n\t\tSaveProgress();\r\n\t\treturn true;\r\n\t}\r\n\r\n\tpublic void SaveProgress()\r\n\t{\r\n\t\tvar data = new ProgressData\r\n\t\t{\r\n\t\t\tCredits = this.Credits,\r\n\t\t\tForceLevel = this.ForceLevel,\r\n\t\t\tMassLevel = this.MassLevel,\r\n\t\t\tEnergyLevel = this.EnergyLevel,\r\n\t\t\tPrestigeLevel = this.PrestigeLevel,\r\n\t\t\tPrestigeTokens = this.PrestigeTokens\r\n\t\t};\r\n\r\n\t\tFileSystem.Data.WriteJson( SAVE_FILE, data );\r\n\t}\r\n\r\n\tpublic void LoadProgress()\r\n\t{\r\n\t\tif ( FileSystem.Data.FileExists( SAVE_FILE ) )\r\n\t\t{\r\n\t\t\tvar data = FileSystem.Data.ReadJson<ProgressData>( SAVE_FILE );\r\n\t\t\tif ( data != null )\r\n\t\t\t{\r\n\t\t\t\tCredits = data.Credits;\r\n\t\t\t\tForceLevel = data.ForceLevel;\r\n\t\t\t\tMassLevel = data.MassLevel;\r\n\t\t\t\tEnergyLevel = data.EnergyLevel;\r\n\t\t\t\tPrestigeLevel = data.PrestigeLevel;\r\n\t\t\t\tPrestigeTokens = data.PrestigeTokens;\r\n\t\t\t}\r\n\t\t}\r\n\t}\r\n\r\n\tprivate class ProgressData\r\n\t{\r\n\t\tpublic int Credits { get; set; }\r\n\t\tpublic int ForceLevel { get; set; }\r\n\t\tpublic int MassLevel { get; set; }\r\n\t\tpublic int EnergyLevel { get; set; }\r\n\t\tpublic int PrestigeLevel { get; set; }\r\n\t\tpublic int PrestigeTokens { get; set; }\r\n\t}\r\n}"
        },
        {
            "Ident": "cherkbyte.one_more_push",
            "Path": "ModeController.cs",
            "FileName": "ModeController.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 410999,
            "IsPrivate": false,
            "Code": "using Sandbox;\r\n\r\npublic enum GameMode\r\n{\r\n    SumoPush,\r\n    NextbotRun\r\n}\r\n\r\npublic enum Language\r\n{\r\n    English,\r\n    Russian\r\n}\r\n\r\npublic sealed class ModeController : Component\r\n{\r\n    public static ModeController Instance { get; private set; }\r\n\r\n    [Property] public GameMode SelectedMode { get; set; } = GameMode.SumoPush;\r\n    [Property] public Language CurrentLanguage { get; set; } = Language.Russian;\r\n    [Property] public float MasterVolume { get; set; } = 1.0f;\r\n\r\n    [Property] public SceneFile SumoScene { get; set; }\r\n    [Property] public SceneFile NextbotScene { get; set; }\r\n\r\n    protected override void OnAwake()\r\n    {\r\n        Instance = this;\r\n    }\r\n\r\n    public void SelectMode( GameMode mode )\r\n    {\r\n        SelectedMode = mode;\r\n\r\n        if ( mode == GameMode.SumoPush )\r\n        {\r\n            if ( SumoScene != null )\r\n                Game.ActiveScene.Load( SumoScene );\r\n            else\r\n                Game.ActiveScene.LoadFromFile( \"scenes/onemore.scene\" );\r\n        }\r\n        else if ( mode == GameMode.NextbotRun )\r\n        {\r\n            if ( NextbotScene != null )\r\n                Game.ActiveScene.Load( NextbotScene );\r\n            else\r\n                Game.ActiveScene.LoadFromFile( \"scenes/onemores2.scene\" );\r\n        }\r\n    }\r\n\r\n    public void ToggleLanguage()\r\n    {\r\n        CurrentLanguage = CurrentLanguage == Language.Russian ? Language.English : Language.Russian;\r\n    }\r\n\r\n    public string GetText( string key )\r\n    {\r\n        bool isRu = CurrentLanguage == Language.Russian;\r\n\r\n        return key switch\r\n        {\r\n            \"game_title\" => \"ONE MORE PUSH\",\r\n            \"title\" => isRu ? \"\u0412\u042b\u0411\u0415\u0420\u0418\u0422\u0415 \u0420\u0415\u0416\u0418\u041c\" : \"SELECT GAME MODE\",\r\n            \"subtitle\" => isRu ? \"\u0412\u044b\u0431\u0435\u0440\u0438\u0442\u0435 \u0440\u0435\u0436\u0438\u043c \u0438 \u043e\u0442\u043f\u0440\u0430\u0432\u043b\u044f\u0439\u0442\u0435\u0441\u044c \u043d\u0430 \u0430\u0440\u0435\u043d\u0443\" : \"Choose your mode and jump into action\",\r\n            \"sumo_title\" => \"SUMO PUSH\",\r\n            \"sumo_tag\" => isRu ? \"\u041a\u041b\u0410\u0421\u0421\u0418\u041a\u0410\" : \"CLASSIC\",\r\n            \"sumo_desc\" => isRu ? \"\u0422\u043e\u043b\u043a\u0430\u0439 \u0432\u0440\u0430\u0433\u043e\u0432 \u0438 \u0431\u043e\u0442\u043e\u0432 \u0441 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b, \u043f\u043e\u043a\u0430 \u043e\u043d\u0430 \u0441\u0443\u0436\u0430\u0435\u0442\u0441\u044f!\" : \"Push enemies and bots off the platform before it shrinks!\",\r\n            \"nextbot_title\" => \"NEXTBOT RUN\",\r\n            \"nextbot_tag\" => isRu ? \"\u0421\u041a\u041e\u0420\u041e\" : \"COMING SOON\",\r\n            \"nextbot_desc\" => isRu ? \"\u0423\u0431\u0435\u0433\u0430\u0439 \u043e\u0442 \u043d\u0435\u043a\u0441\u0442\u0431\u043e\u0442\u043e\u0432, \u0441\u043f\u0430\u0441\u0430\u0439 \u043d\u043e\u043a\u0430\u0443\u0442\u0438\u0440\u043e\u0432\u0430\u043d\u043d\u044b\u0445 \u0441\u043e\u044e\u0437\u043d\u0438\u043a\u043e\u0432 \u0438 \u0432\u044b\u0436\u0438\u0432\u0430\u0439!\" : \"Run from nextbots, revive fallen teammates, and survive!\",\r\n            \"lang_btn\" => isRu ? \"RU\" : \"EN\",\r\n            \"settings_btn\" => isRu ? \"\u041d\u0410\u0421\u0422\u0420\u041e\u0419\u041a\u0418\" : \"SETTINGS\",\r\n            \"exit_btn\" => isRu ? \"\u0412\u042b\u0425\u041e\u0414\" : \"EXIT\",\r\n            \"settings_title\" => isRu ? \"\u041d\u0410\u0421\u0422\u0420\u041e\u0419\u041a\u0418\" : \"SETTINGS\",\r\n            \"volume_label\" => isRu ? \"\u0413\u0440\u043e\u043c\u043a\u043e\u0441\u0442\u044c \u0437\u0432\u0443\u043a\u0430\" : \"Master Volume\",\r\n            \"controls_title\" => isRu ? \"\u0423\u043f\u0440\u0430\u0432\u043b\u0435\u043d\u0438\u0435\" : \"Controls\",\r\n            \"ctrl_move\" => isRu ? \"WASD \u2014 \u041f\u0435\u0440\u0435\u043c\u0435\u0449\u0435\u043d\u0438\u0435\" : \"WASD \u2014 Movement\",\r\n            \"ctrl_action\" => isRu ? \"\u041b\u041a\u041c / E \u2014 \u0410\u0442\u0430\u043a\u0430 / \u0421\u043f\u0430\u0441\u0435\u043d\u0438\u0435\" : \"LMB / E \u2014 Push / Revive\",\r\n            \"ctrl_space\" => isRu ? \"\u041f\u0440\u043e\u0431\u0435\u043b \u2014 \u041f\u0440\u044b\u0436\u043e\u043a\" : \"Space \u2014 Jump\",\r\n            \"close_btn\" => isRu ? \"\u0417\u0410\u041a\u0420\u042b\u0422\u042c\" : \"CLOSE\",\r\n            \"news_title\" => isRu ? \"\u041d\u041e\u0412\u041e\u0421\u0422\u0418 \u0418 \u041e\u0411\u041d\u041e\u0412\u041b\u0415\u041d\u0418\u042f\" : \"NEWS & UPDATES\",\r\n            \"news_tag_new\" => isRu ? \"\u041f\u0410\u0422\u0427 1.2\" : \"PATCH 1.2\",\r\n            \"news_item1_title\" => isRu ? \"\u0420\u0435\u0436\u0438\u043c Nextbot Run\" : \"Nextbot Run Mode\",\r\n            \"news_item1_desc\" => isRu ? \"\u0420\u0435\u0436\u0438\u043c \u043d\u0430\u0445\u043e\u0434\u0438\u0442\u0441\u044f \u0432 \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0435 \u0438 \u0441\u043a\u043e\u0440\u043e \u0431\u0443\u0434\u0435\u0442 \u0434\u043e\u0441\u0442\u0443\u043f\u0435\u043d!\" : \"Mode in development and coming soon!\",\r\n            \"news_item2_title\" => isRu ? \"\u041e\u0431\u043d\u043e\u0432\u043b\u0451\u043d\u043d\u044b\u0439 Glass UI\" : \"New Glass UI\",\r\n            \"news_item2_desc\" => isRu ? \"\u0421\u043e\u0432\u0440\u0435\u043c\u0435\u043d\u043d\u044b\u0439 \u0438\u043d\u0442\u0435\u0440\u0444\u0435\u0439\u0441, \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0438 \u0437\u0432\u0443\u043a\u0430, \u0432\u044b\u0431\u043e\u0440 \u044f\u0437\u044b\u043a\u0430 \u0438 \u044d\u043a\u0440\u0430\u043d \u0437\u0430\u0433\u0440\u0443\u0437\u043a\u0438.\" : \"Sleek glass UI design, audio settings, multi-language support & loading screen.\",\r\n            \"news_item3_title\" => isRu ? \"\u0411\u0430\u043b\u0430\u043d\u0441 Sumo Push\" : \"Sumo Push Balance\",\r\n            \"news_item3_desc\" => isRu ? \"\u0423\u043b\u0443\u0447\u0448\u0435\u043d\u0430 \u0444\u0438\u0437\u0438\u043a\u0430 \u0442\u043e\u043b\u0447\u043a\u043e\u0432 \u0438 \u0443\u0441\u043a\u043e\u0440\u0435\u043d\u0430 \u0434\u0438\u043d\u0430\u043c\u0438\u043a\u0430 \u0441\u0443\u0436\u0435\u043d\u0438\u044f \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b!\" : \"Improved push physics and faster platform shrinking dynamics!\",\r\n            \"loading_title\" => isRu ? \"\u0417\u0410\u0413\u0420\u0423\u0417\u041a\u0410 \u0410\u0420\u0415\u041d\u042b...\" : \"LOADING ARENA...\",\r\n            \"loading_sub\" => isRu ? \"\u041f\u043e\u0434\u0433\u043e\u0442\u043e\u0432\u043a\u0430 \u0433\u0435\u043e\u043c\u0435\u0442\u0440\u0438\u0438 \u0438 \u0433\u0435\u043d\u0435\u0440\u0430\u0446\u0438\u044f \u043e\u0431\u044a\u0435\u043a\u0442\u043e\u0432...\" : \"Preparing geometry and spawning objects...\",\r\n            \"tip_label\" => isRu ? \"\u0421\u041e\u0412\u0415\u0422:\" : \"TIP:\",\r\n            \"tip_sumo\" => isRu ? \"\u0421\u043b\u0435\u0434\u0438\u0442\u0435 \u0437\u0430 \u043c\u0438\u0433\u0430\u044e\u0449\u0438\u043c\u0438 \u0431\u043b\u043e\u043a\u0430\u043c\u0438 \u043f\u043b\u0430\u0442\u0444\u043e\u0440\u043c\u044b \u2014 \u043e\u043d\u0438 \u0443\u043f\u0430\u0434\u0443\u0442 \u043f\u0435\u0440\u0432\u044b\u043c\u0438!\" : \"Watch out for flashing platform blocks \u2014 they fall first!\",\r\n            \"tip_nextbot\" => isRu ? \"\u0414\u0435\u0440\u0436\u0430\u0442\u044c\u0441\u044f \u0432\u043c\u0435\u0441\u0442\u0435 \u0441 \u0441\u043e\u044e\u0437\u043d\u0438\u043a\u0430\u043c\u0438 \u043f\u0440\u043e\u0449\u0435: \u0432\u044b \u0441\u043c\u043e\u0436\u0435\u0442\u0435 \u0431\u044b\u0441\u0442\u0440\u0435\u0435 \u043f\u043e\u0434\u043d\u044f\u0442\u044c \u0434\u0440\u0443\u0433 \u0434\u0440\u0443\u0433\u0430!\" : \"Stick together with teammates: reviving is much faster when close!\",\r\n            _ => key\r\n        };\r\n    }\r\n}"
        }
    ]
}