s&box Package Code Search

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

Showing code results for query: * (27 total matches found)
seilordstudio.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;
    }
}
seilordstudio.one_more_push / ScoreManager.cs
Game game
using Sandbox;
using System; // Добавлено для работы Math.Max

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

    public int Score { get; private set; } = 0;
    public int Deaths { get; private set; } = 0; 

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

    public void AddScore( int amount = 1 )
    {
        Score += amount;
        SaveManager.TryUpdateHighScore( Score ); 
    }

    public void AddDeath( int amount = 1 )
    {
        Deaths += amount;

       
        Score = Math.Max( 0, Score - 20 );
    }

    public void ResetScore()
    {
        Score = 0;
    }
}
seilordstudio.one_more_push / StickyTarZone.cs
Game game
using Sandbox;
using System.Collections.Generic;

public sealed class StickyTarZone : Component, Component.ITriggerListener
{
    [Property] public float SpeedMultiplier { get; set; } = 0.4f; 
    [Property] public float RigidbodyDrag { get; set; } = 0.85f;  
    [Property] public Color TarTint { get; set; } = new Color( 0.1f, 0.1f, 0.1f, 1f ); 

    private readonly HashSet<GameObject> _insideObjects = new();
    private readonly Dictionary<ModelRenderer, Color> _originalColors = new();

    public void OnTriggerEnter( Collider other )
    {
        if ( !other.GameObject.IsValid() ) return;

        var rootObj = other.GameObject.Root;
        _insideObjects.Add( rootObj );

        
        var renderers = rootObj.Components.GetAll<ModelRenderer>();
        foreach ( var mr in renderers )
        {
            if ( mr.IsValid() && !_originalColors.ContainsKey( mr ) )
            {
                _originalColors[mr] = mr.Tint;
                mr.Tint = TarTint;
            }
        }
    }

    public void OnTriggerExit( Collider other )
    {
        if ( !other.GameObject.IsValid() ) return;

        var rootObj = other.GameObject.Root;
        _insideObjects.Remove( rootObj );

        
        var renderers = rootObj.Components.GetAll<ModelRenderer>();
        foreach ( var mr in renderers )
        {
            if ( mr.IsValid() && _originalColors.TryGetValue( mr, out var origColor ) )
            {
                mr.Tint = origColor;
                _originalColors.Remove( mr );
            }
        }
    }

    protected override void OnFixedUpdate()
    {
        
        _insideObjects.RemoveWhere( obj => !obj.IsValid() );

        foreach ( var targetObj in _insideObjects )
        {
            
            var rb = targetObj.Components.Get<Rigidbody>();
            if ( rb.IsValid() && rb.PhysicsBody.IsValid() )
                {
                    rb.PhysicsBody.Velocity *= 0.5f; // Замедление в смоле
                }

            
            var cc = targetObj.Components.Get<CharacterController>();
            if ( cc.IsValid() )
            {
                cc.Velocity *= SpeedMultiplier;
            }
        }
    }
}
seilordstudio.one_more_push / TimeFreezeAbility.cs
Game game
using Sandbox;
using System.Collections.Generic;
using System.Linq;

public sealed class TimeFreezeAbility : Component
{
    [Property] public float FreezeDuration { get; set; } = 4f;
    [Property] public float Cooldown { get; set; } = 20f;
    [Property] public float FreezeRadius { get; set; } = 500f;
    [Property] public GameObject FreezeParticle { get; set; }
    [Property] public SoundEvent FreezeSound { get; set; }

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

    private TimeSince timeSinceActivated;
    private TimeSince timeSinceCooldown;
    private bool isCoolingDown = false;

    private List<Rigidbody> frozenRigidbodies = new();
    private List<CharacterController> frozenControllers = new();
    private Dictionary<ModelRenderer, Color> originalRendererColors = new();

    protected override void OnUpdate()
    {
        if ( IsFreezeActive && timeSinceActivated >= FreezeDuration )
        {
            UnfreezeTime();
        }

        if ( Input.Pressed( "use" ) )
        {
            TryActivateFreeze();
        }

        if ( IsFreezeActive )
        {
            foreach ( var rb in frozenRigidbodies )
            {
                if ( rb.IsValid() )
                {
                    rb.Velocity = Vector3.Zero;
                    rb.AngularVelocity = Vector3.Zero;
                }
            }

            foreach ( var cc in frozenControllers )
            {
                if ( cc.IsValid() )
                {
                    cc.Velocity = Vector3.Zero;
                }
            }
        }
    }

    public void TryActivateFreeze()
    {
        if ( IsFreezeActive ) return;
        if ( isCoolingDown && timeSinceCooldown < Cooldown ) return;

        IsFreezeActive = true;
        timeSinceActivated = 0;
        frozenRigidbodies.Clear();
        frozenControllers.Clear();
        originalRendererColors.Clear();

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

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

        var startPos = WorldPosition;
        var endPos = WorldPosition + Vector3.Up * 1f;

        var hits = Scene.Trace.Sphere( FreezeRadius, startPos, endPos )
            .IgnoreGameObject( GameObject )
            .RunAll();

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

            var targetObj = hit.GameObject.Root;

            var rb = targetObj.Components.Get<Rigidbody>();
            if ( rb.IsValid() && !frozenRigidbodies.Contains( rb ) )
            {
                frozenRigidbodies.Add( rb );
            }

            var cc = targetObj.Components.Get<CharacterController>();
            if ( cc.IsValid() && !frozenControllers.Contains( cc ) )
            {
                frozenControllers.Add( cc );
            }

            var renderers = targetObj.Components.GetAll<ModelRenderer>();
            foreach ( var mr in renderers )
            {
                if ( mr.IsValid() && !originalRendererColors.ContainsKey( mr ) )
                {
                    originalRendererColors[mr] = mr.Tint;
                    mr.Tint = new Color( 0.3f, 0.7f, 1.0f, 1.0f );
                }
            }
        }

        Log.Info( $"[TimeFreeze] Frozen {frozenRigidbodies.Count} Rigidbodies and {frozenControllers.Count} Controllers!" );
    }

    private void UnfreezeTime()
    {
        IsFreezeActive = false;
        isCoolingDown = true;
        timeSinceCooldown = 0;

        foreach ( var kvp in originalRendererColors )
        {
            if ( kvp.Key.IsValid() )
            {
                kvp.Key.Tint = kvp.Value;
            }
        }

        frozenRigidbodies.Clear();
        frozenControllers.Clear();
        originalRendererColors.Clear();

        Log.Info( "[TimeFreeze] Time Freeze Expired!" );
    }
}
seilordstudio.one_more_push / CustomTopDownController.cs
Game game
public sealed class CustomTopDownController : Component
{
	[RequireComponent] PlayerController Controller { get; set; }

	protected override void OnFixedUpdate()
	{
		// Lets make the player move when we press WASD
		var speed = Input.Down( "Run" ) ? Controller.RunSpeed : Controller.WalkSpeed;
		Controller.WishVelocity = Input.AnalogMove * speed;

		// And rotate the player to face the direction of the movement
		if ( Controller.WishVelocity.LengthSquared > 0 )
		{
			var targetAngle = Controller.WishVelocity.EulerAngles;
			Controller.EyeAngles = Rotation.Slerp( Controller.EyeAngles, targetAngle, Time.Delta * 10f );
		}
	}

	protected override void OnPreRender()
	{
		// This will update the camera's position so that it's up in the air and back a bit, looking down at an angle.
		Scene.Camera.WorldPosition = Controller.WorldPosition + Vector3.Up * 1024f + Vector3.Backward * 256f;
		Scene.Camera.WorldRotation = new Angles( 75, 0, 0 );
	}
}



seilordstudio.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;

        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;
            }
        }
    }
}
seilordstudio.one_more_push / pushhud.razor.scss
Game game
@font-face {
    font-family: 'Russo One';
    src: url( 'prefab/RussoOne-Regular.ttf' );
}

PushHud, root, .hud-root {
    position: absolute;
    left: 0px;
    top: 0px;
    width: 100%;
    height: 100%;
    background-color: transparent;
    pointer-events: none;
    font-family: 'Russo One', sans-serif;
}

/* --- ВЕРХНЯЯ ПАНЕЛЬ СЧЕТЧИКОВ И РАУНДА --- */
.top-header-container {
    position: absolute;
    top: 20px;
    left: 50%;
    transform: translateX(-50%);
    flex-direction: column;
    align-items: center;
    gap: 10px;
}

.score-box {
    flex-direction: row;
    align-items: center;
    gap: 28px;
    background-color: rgba(12, 14, 20, 0.95);
    border: 2.5px solid #ffaa00;
    border-radius: 12px;
    padding: 10px 30px 12px 30px;
    box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.85);

    .score-card {
        flex-direction: column;
        align-items: center;
        min-width: 75px;

        .score-title {
            font-size: 13px;
            color: #9aa0a6;
            letter-spacing: 1px;
            text-shadow: 0px 1px 3px rgba(0, 0, 0, 0.9);
        }

        .score-value {
            font-size: 32px;
            color: #ffffff;
            margin-top: -2px;
            text-shadow: 0px 2px 5px rgba(0, 0, 0, 0.9);
        }

        &.time {
            .score-title { color: #ffb700; }
            .score-value { color: #ffd000; }
        }

        &.death .score-value {
            color: #ff4757;
        }

        &.highlight {
            .score-title { color: #ffaa00; }
            .score-value { color: #ffaa00; }
        }
    }
}

.round-badge {
    padding: 6px 24px;
    background-color: rgba(15, 18, 24, 0.95);
    border: 2px solid #ff9900;
    border-radius: 20px;
    color: #ffaa00;
    font-size: 13px;
    font-weight: bold;
    letter-spacing: 2px;
    text-shadow: 0px 0px 10px rgba(255, 170, 0, 0.7);
    box-shadow: 0px 4px 14px rgba(0, 0, 0, 0.7);
}

/* --- ПОГОДНЫЕ ИВЕНТЫ / АНОМАЛИИ --- */
.weather-banner {
    flex-direction: row;
    align-items: center;
    gap: 14px;
    background-color: rgba(14, 16, 22, 0.95);
    border: 2.5px solid rgba(255, 255, 255, 0.25);
    border-radius: 10px;
    padding: 8px 20px;
    margin-top: 6px;
    box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.85);

    .weather-icon {
        font-size: 24px;
    }

    .weather-info {
        flex-direction: column;

        .weather-title {
            font-size: 16px;
            color: #ffffff;
            letter-spacing: 0.5px;
            text-shadow: 0px 1px 3px rgba(0, 0, 0, 0.9);
        }

        .weather-sub {
            font-size: 10px;
            color: #8a929a;
            letter-spacing: 1.5px;
        }
    }

    .weather-timer-badge {
        background-color: rgba(255, 255, 255, 0.12);
        border: 1px solid rgba(255, 255, 255, 0.25);
        border-radius: 6px;
        padding: 6px 12px;

        .timer-text {
            font-size: 15px;
            color: #ffffff;
            text-shadow: 0px 1px 3px rgba(0, 0, 0, 0.9);
        }
    }

    &.wind {
        border-color: #ff9900;
        .weather-title, .timer-text { color: #ffaa00; }
    }

    &.ice {
        border-color: #00e5ff;
        .weather-title, .timer-text { color: #00e5ff; }
    }

    &.gravity {
        border-color: #d670ff;
        .weather-title, .timer-text { color: #d670ff; }
    }
}

/* --- КНОПКА МЕНЮ [ESC] --- */
.side-settings-btn {
    position: absolute;
    top: 25px;
    right: 25px;
    height: 48px;
    padding: 0 18px;
    background-color: rgba(12, 14, 20, 0.95);
    border: 2px solid rgba(255, 255, 255, 0.3);
    border-radius: 10px;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 10px;
    pointer-events: all;
    cursor: pointer;

    .key-hint {
        font-size: 13px;
        color: #9aa0a6;
    }

    .icon {
        font-size: 22px;
        color: #ffffff;
    }

    &:hover {
        background-color: rgba(255, 255, 255, 0.2);
        border-color: #ffaa00;

        .key-hint, .icon {
            color: #ffaa00;
        }
    }
}

/* --- ТАБЛИЦА ИГРОКОВ (СПРАВА ВВЕРХУ ПОД ESC) --- */
.scoreboard-box {
    position: absolute;
    top: 85px; 
    right: 25px;
    width: 250px;
    flex-direction: column;
    background-color: rgba(12, 14, 20, 0.95);
    border: 1.5px solid rgba(255, 255, 255, 0.25);
    border-radius: 12px;
    padding: 12px 16px;
    gap: 10px;
    box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.8);

    .scoreboard-header {
        border-bottom: 1px solid rgba(255, 255, 255, 0.15);
        padding-bottom: 6px;

        .header-title {
            font-size: 12px;
            color: #7f8c8d;
            letter-spacing: 1.5px;
        }
    }

    .player-list {
        flex-direction: column;
        gap: 6px;

        .player-row {
            flex-direction: row;
            justify-content: space-between;
            align-items: center;
            padding: 6px 10px;
            border-radius: 6px;
            background-color: rgba(255, 255, 255, 0.05);

            .player-name {
                font-size: 15px;
                color: #ffffff;
                text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.8);
            }

            .player-score {
                font-size: 16px;
                color: #ffaa00;
                text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.8);
            }

            &.local {
                background-color: rgba(0, 255, 157, 0.18);
                border: 1px solid rgba(0, 255, 157, 0.5);

                .player-name, .player-score {
                    color: #00ff9d;
                }
            }
        }
    }
}

/* --- ЧАТ И УВЕДОМЛЕНИЯ (СЛЕВА ВВЕРХУ) --- */
.top-left-box {
    position: absolute;
    top: 25px;
    left: 25px;
    flex-direction: column;
    gap: 12px;
    pointer-events: none;

    .chat-box {
        width: 380px;
        flex-direction: column;
        gap: 10px;

        .chat-messages {
            flex-direction: column;
            gap: 6px;

            .chat-row {
                flex-direction: row;
                gap: 10px;
                background-color: rgba(10, 12, 18, 0.92);
                padding: 6px 12px;
                border-radius: 8px;
                border-left: 4px solid #ffaa00;

                .chat-author {
                    font-size: 14px;
                    color: #ffaa00;
                }

                .chat-text {
                    font-size: 14px;
                    color: #ffffff;
                    text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.8);
                }
            }
        }

        .hints-row {
            flex-direction: row;
            gap: 10px;
        }

        .chat-hint {
            pointer-events: all;
            font-size: 13px;
            color: #d1d5db;
            background-color: rgba(14, 18, 24, 0.95);
            border: 1.5px solid rgba(255, 255, 255, 0.25);
            border-radius: 8px;
            padding: 8px 14px;
            cursor: pointer;
            text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.9);

            &:hover {
                color: #00ff9d;
                border-color: #00ff9d;
            }

            &.prop-hint {
                color: #ffaa00;
                border-color: rgba(255, 170, 0, 0.6);

                &:hover {
                    color: #ffd000;
                    border-color: #ffaa00;
                }
            }
        }

        .chat-input-row {
            pointer-events: all;
            
            textentry {
                width: 100%;
                height: 40px;
                background-color: rgba(10, 12, 18, 0.95);
                border: 2px solid #ffaa00;
                color: #ffffff;
                padding: 0 12px;
                font-size: 15px;
                border-radius: 8px;
            }
        }
    }
}

/* --- ШКАЛА ЗАРЯДА И ПЕРЕЗАРЯДКА --- */
.cooldown-box {
    position: absolute;
    left: 25px;
    bottom: 25px;
    flex-direction: column;
    align-items: flex-start;
    gap: 6px;

    .cooldown-title {
        font-size: 13px;
        color: #9aa0a6;
        letter-spacing: 1px;
        text-shadow: 0px 1px 3px rgba(0, 0, 0, 0.9);
    }

    .bar-background {
        width: 300px;
        height: 22px;
        background-color: rgba(10, 12, 18, 0.95);
        border: 2px solid #ffffff;
        border-radius: 8px;
        overflow: hidden;

        .bar-fill {
            height: 100%;
            background-color: #ffaa00;
        }
    }
}

.charge-box {
    position: absolute;
    bottom: 150px;
    left: 50%;
    transform: translateX(-50%);
    width: 380px;
    flex-direction: column;

    .charge-title {
        color: #ff9900;
        font-size: 15px;
        font-weight: bold;
        letter-spacing: 2px;
        text-align: center;
        margin-bottom: 8px;
        text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.9);
    }

    .bar-background {
        width: 100%;
        height: 18px;
        background-color: rgba(10, 12, 18, 0.95);
        border: 2px solid #ff9900;
        border-radius: 8px;
        overflow: hidden;

        .bar-fill.charge {
            height: 100%;
            background: linear-gradient(to right, #ffaa00, #ff3300);
            transition: width 0.05s ease-out;
        }
    }
}

/* --- ХОТБАР СПОСОБНОСТЕЙ (СНИЗУ) --- */
.hotbar-container {
    position: absolute;
    bottom: 25px;
    left: 50%;
    transform: translateX(-50%);
    flex-direction: row;
    gap: 16px;
    pointer-events: all;

    .slot-wrapper {
        flex-direction: column;
        align-items: center;
        gap: 8px;

        .hotbar-slot {
            width: 80px;
            height: 80px;
            background-color: rgba(14, 16, 22, 0.95);
            background-image: linear-gradient(to bottom, #2b2b2b, #121212);
            border: 2px solid rgba(255, 255, 255, 0.25);
            border-radius: 10px;
            flex-direction: column;
            justify-content: center;
            align-items: center;
            cursor: pointer;

            &.selected {
                border-color: #ffaa00;
                box-shadow: 0px 0px 16px rgba(255, 170, 0, 0.65);
            }

            &.locked {
                opacity: 0.5;
                background-color: rgba(8, 8, 12, 0.95);
            }

            .slot-name {
                font-size: 13px;
                color: #ffffff;
                text-align: center;
                text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.9);
            }

            .slot-cost {
                font-size: 13px;
                color: #ffd000;
                margin-top: 4px;
                font-weight: bold;
            }
        }

        .key-badge {
            width: 36px;
            height: 36px;
            background-image: linear-gradient(to bottom, #ffaa00, #cc6600);
            border: 1.5px solid #ffe082;
            border-radius: 8px;
            justify-content: center;
            align-items: center;
            font-size: 16px;
            font-weight: bold;
            color: #ffffff;
            text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.9);
            box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.6);

            &.selected {
                background-image: linear-gradient(to bottom, #ffbb00, #e65100);
                border-color: #ffffff;
            }
        }
    }
}

/* --- ОВЕРЛЕЙ И МЕНЮ --- */
.menu-overlay {
    position: absolute;
    left: 0px;
    top: 0px;
    width: 100%;
    height: 100%;
    background-color: rgba(8, 10, 14, 0.82);
    justify-content: center;
    align-items: center;
    pointer-events: all;
    z-index: 100;
}

.menu-card {
    flex-direction: column;
    align-items: center;
    gap: 18px;
    background-color: rgba(14, 16, 22, 0.98);
    padding: 36px 60px;
    border-radius: 16px;
    border: 2px solid rgba(255, 255, 255, 0.25);
    min-width: 380px;
    box-shadow: 0px 12px 40px rgba(0, 0, 0, 0.9);

    .menu-title {
        color: #ffffff;
        font-size: 30px;
        font-weight: bold;
        letter-spacing: 2px;
        margin-bottom: 10px;
        text-shadow: 0px 2px 6px rgba(0, 0, 0, 0.9);
    }

    .menu-btn {
        width: 100%;
        padding: 14px 28px;
        background-color: rgba(255, 255, 255, 0.1);
        border: 1.5px solid rgba(255, 255, 255, 0.25);
        color: #ffffff;
        font-size: 18px;
        font-weight: bold;
        border-radius: 10px;
        align-items: center;
        justify-content: center;
        cursor: pointer;

        &:hover {
            background-color: rgba(255, 170, 0, 0.25);
            border-color: #ffaa00;
            color: #ffaa00;
        }

        &.play {
            background-color: #00ff9d;
            border-color: #00ff9d;
            color: #000000;

            &:hover {
                background-color: #00cc7d;
                border-color: #00cc7d;
                color: #000000;
            }
        }

        &.quit:hover {
            background-color: rgba(255, 71, 87, 0.45);
            border-color: #ff4757;
            color: #ffffff;
        }
    }
}

/* --- МЕНЮ СПАВНА ПРОПСОВ --- */
.prop-menu-card {
    flex-direction: column;
    align-items: center;
    gap: 24px;
    background-color: rgba(14, 16, 22, 0.98);
    padding: 32px 48px;
    border-radius: 16px;
    border: 2.5px solid #ffaa00;
    min-width: 460px;
    box-shadow: 0px 14px 45px rgba(0, 0, 0, 0.95);

    .menu-title {
        color: #ffaa00;
        font-size: 24px;
        font-weight: bold;
        letter-spacing: 2px;
        text-shadow: 0px 2px 5px rgba(0, 0, 0, 0.9);
    }

    .prop-grid {
        display: flex;
        flex-direction: row;
        flex-wrap: wrap;
        justify-content: center;
        gap: 16px;
        width: 100%;

        .prop-item {
            width: 180px;
            padding: 16px 14px;
            background-color: rgba(255, 255, 255, 0.08);
            border: 1.5px solid rgba(255, 170, 0, 0.35);
            border-radius: 12px;
            flex-direction: column;
            align-items: center;
            justify-content: center;
            gap: 8px;
            cursor: pointer;

            .prop-name {
                font-size: 16px;
                color: #ffffff;
                text-align: center;
                text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.9);
            }

            .prop-cost {
                font-size: 14px;
                font-weight: bold;
                color: #ffd000;
            }

            &:hover {
                background-color: rgba(255, 170, 0, 0.25);
                border-color: #ffaa00;

                .prop-name {
                    color: #ffaa00;
                }
            }
        }
    }
}

/* --- НАСТРОЙКИ --- */
.settings-card {
    min-width: 440px;

    .settings-content {
        flex-direction: column;
        gap: 16px;
        width: 100%;
        margin-bottom: 14px;

        .setting-item {
            color: #e0e0e0;
            font-size: 16px;
            font-weight: bold;
            text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.9);
        }

        .lang-row {
            justify-content: space-between;
            align-items: center;
            width: 100%;
        }

        .lang-selector {
            gap: 10px;

            .lang-btn {
                padding: 8px 18px;
                background-color: rgba(255, 255, 255, 0.12);
                border: 1.5px solid rgba(255, 255, 255, 0.25);
                border-radius: 8px;
                color: #b0b0b0;
                font-size: 14px;
                font-weight: bold;
                cursor: pointer;

                &:hover {
                    background-color: rgba(255, 255, 255, 0.25);
                    color: #ffffff;
                }

                &.active {
                    background-color: #00ff9d;
                    border-color: #00ff9d;
                    color: #000000;
                }
            }
        }
    }
}
seilordstudio.one_more_push / ManeuverAbility.cs
Game game
using Sandbox;
using System;

public sealed class ManeuverAbility : Component
{
    [Property] public float DashForce { get; set; } = 900f;
    [Property] public float DashCooldown { get; set; } = 1.2f;

    [Property] public float ParryWindow { get; set; } = 0.4f;
    [Property] public float ParryCooldown { get; set; } = 2.5f;

    [Property] public SoundEvent DashSound { get; set; }
    [Property] public SoundEvent ParrySound { get; set; }
    [Property] public SoundEvent ReflectSound { get; set; }
    [Property] public GameObject DashParticle { get; set; }
    [Property] public GameObject ParryParticle { get; set; }

    private TimeSince _lastDash;
    private TimeSince _lastParry;
    private TimeSince _parryTimer;

    public bool IsParrying => _parryTimer < ParryWindow;
    public bool IsDashReady => _lastDash >= DashCooldown;
    public bool IsParryReady => _lastParry >= ParryCooldown;

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

        if ( Input.Pressed( "run" ) && IsDashReady )
        {
            ExecuteDash();
        }

        if ( Input.Pressed( "attack2" ) && IsParryReady )
        {
            ExecuteParry();
        }
    }

    private void ExecuteDash()
    {
        var cc = Components.Get<CharacterController>();
        if ( !cc.IsValid() ) return;

        var moveInput = Input.AnalogMove;
        var wishDir = (WorldTransform.Forward * moveInput.x + WorldTransform.Left * moveInput.y).Normal;

        if ( wishDir.IsNearlyZero() )
        {
            wishDir = WorldTransform.Forward;
        }

        PerformDash( wishDir );
    }

    private void ExecuteParry()
    {
        PerformParry();
    }

    // --- МЕТОДЫ ДЛЯ БОТОВ И ВНЕШНИХ ВЫЗОВОВ ---

    public void TryExecuteBotDash( Vector3 direction )
    {
        if ( !IsDashReady ) return;
        PerformDash( direction );
    }

    public void TryExecuteBotParry()
    {
        if ( !IsParryReady ) return;
        PerformParry();
    }

    private void PerformDash( Vector3 direction )
    {
        _lastDash = 0;

        var cc = Components.Get<CharacterController>();
        if ( cc.IsValid() )
        {
            cc.Velocity += direction.Normal * DashForce;

            if ( DashSound != null ) Sound.Play( DashSound, WorldPosition );
            if ( DashParticle.IsValid() ) DashParticle.Clone( WorldPosition );
        }
    }

    private void PerformParry()
    {
        _lastParry = 0;
        _parryTimer = 0;

        if ( ParrySound != null ) Sound.Play( ParrySound, WorldPosition );
        if ( ParryParticle.IsValid() ) ParryParticle.Clone( WorldPosition );
    }

    public void OnSuccessfulReflect( Vector3 reflectOrigin )
    {
        if ( ReflectSound != null )
        {
            Sound.Play( ReflectSound, WorldPosition );
        }
    }
}
seilordstudio.one_more_push / MusicManager.cs
Game game
using Sandbox;

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

    [Property] public SoundEvent MusicSound { get; set; }

    private SoundHandle _musicHandle;

    protected override void OnStart()
    {
        Instance = this;

        if ( PushHud.EnableMusic )
        {
            PlayMusic();
        }
    }

    public void ToggleMusic( bool enable )
    {
        if ( enable )
        {
            PlayMusic();
        }
        else
        {
            StopMusic();
        }
    }

    public void PlayMusic()
    {
        if ( _musicHandle.IsValid() ) return;

        if ( MusicSound != null )
        {
            _musicHandle = Sound.Play( MusicSound );
        }
    }

    public void StopMusic()
    {
        if ( _musicHandle.IsValid() )
        {
            _musicHandle.Stop();
        }
    }
}
seilordstudio.one_more_push / ShieldAbility.cs
Game game
using Sandbox;

public sealed class ShieldAbility : Component
{
    [Property] public float ShieldDuration { get; set; } = 5f;
    [Property] public float Cooldown { get; set; } = 15f;
    [Property] public GameObject ShieldVisual { get; set; }

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

    private TimeSince timeSinceActivated;
    private TimeSince timeSinceCooldown;
    private bool isCoolingDown = false;

    protected override void OnStart()
    {
        if ( ShieldVisual.IsValid() )
        {
            ShieldVisual.Enabled = false;
        }
    }

    protected override void OnUpdate()
    {
        if ( IsShieldActive && timeSinceActivated >= ShieldDuration )
        {
            DeactivateShield();
        }

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

    public void TryActivateShield()
    {
        if ( IsShieldActive ) return;
        if ( isCoolingDown && timeSinceCooldown < Cooldown ) return;

        IsShieldActive = true;
        timeSinceActivated = 0;

        if ( ShieldVisual.IsValid() )
        {
            ShieldVisual.Enabled = true;
        }

        Log.Info( "[ShieldAbility] Shield Activated!" );
    }

    private void DeactivateShield()
    {
        IsShieldActive = false;
        isCoolingDown = true;
        timeSinceCooldown = 0;

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

        Log.Info( "[ShieldAbility] Shield Expired!" );
    }
}
seilordstudio.one_more_push / ArenaTile.cs
Game game
using Sandbox;

public sealed class ArenaTile : Component
{
    private Vector3 initialPosition;
    public bool IsFalling { get; private set; } = false;

    [Property] public float FallSpeed { get; set; } = 500f;

    private ModelRenderer modelRenderer;
    private Color originalColor = Color.White;

    protected override void OnStart()
    {
        initialPosition = WorldPosition;
        modelRenderer = Components.Get<ModelRenderer>();

        if ( modelRenderer.IsValid() )
        {
            originalColor = modelRenderer.Tint;
        }
    }

    public void HighlightWarning( bool enable )
    {
        if ( !modelRenderer.IsValid() ) return;
        modelRenderer.Tint = enable ? Color.Red : originalColor;
    }

    public void Drop()
    {
        IsFalling = true;
    }

    protected override void OnUpdate()
    {
        if ( IsFalling )
        {
            WorldPosition += Vector3.Down * FallSpeed * Time.Delta;
        }
    }

    public void ResetTile()
    {
        IsFalling = false;
        WorldPosition = initialPosition;
        HighlightWarning( false );
    }
}
seilordstudio.one_more_push / Assembly.cs
Game game
global using Sandbox;
global using System.Collections.Generic;
global using System.Linq;
seilordstudio.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();
        }
    }
}
seilordstudio.one_more_push / BotSpawner.cs
Game game
using Sandbox;
using System;
using System.Collections.Generic;

public sealed class BotSpawner : Component
{
    [Property] public GameObject BotPrefab { get; set; }
    [Property] public float SpawnInterval { get; set; } = 4f;
    [Property] public int MaxBots { get; set; } = 5;

    public static bool EnableBots { get; set; } = true;

    private List<GameObject> _activeBots = new();
    private TimeSince _timeSinceLastSpawn = 0;

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

        // Если боты выключены в настройках — удаляем уже заспавненных
        if ( !EnableBots )
        {
            if ( _activeBots.Count > 0 )
            {
                foreach ( var bot in _activeBots )
                {
                    if ( bot.IsValid() ) bot.Destroy();
                }
                _activeBots.Clear();
            }
            return;
        }

        if ( _timeSinceLastSpawn > SpawnInterval )
        {
            _timeSinceLastSpawn = 0;
            SpawnBot();
        }
    }

    private void SpawnBot()
    {
        if ( !BotPrefab.IsValid() )
        {
            Log.Warning( "[BotSpawner] BotPrefab не назначен в Инспекторе!" );
            return;
        }

        _activeBots.RemoveAll( bot => !bot.IsValid() );

        if ( _activeBots.Count >= MaxBots ) return;

        // Спавн относительно позиции спавнера
        var randomX = Random.Shared.Float( -150f, 150f );
        var randomY = Random.Shared.Float( -150f, 150f );
        var spawnPos = WorldPosition + new Vector3( randomX, randomY, 200f );

        var botInstance = BotPrefab.Clone( spawnPos );
        if ( !botInstance.IsValid() ) return;

        _activeBots.Add( botInstance );

        if ( Network.Active )
        {
            botInstance.NetworkSpawn();
        }

        Log.Info( $"[BotSpawner] Бот заспавнен в {spawnPos}" );
    }
}
seilordstudio.one_more_push / SaveManager.cs
Game game
using Sandbox;

public class PlayerSaveData
{
	public int HighScore { get; set; } = 0;
	public int TotalPushes { get; set; } = 0;
}

public static class SaveManager
{
	private const string SaveFileName = "player_save.json";
	public static PlayerSaveData Current { get; private set; } = new();

	// Загрузка сохраненных данных
	public static void Load()
	{
		if ( FileSystem.Data.FileExists( SaveFileName ) )
		{
			Current = FileSystem.Data.ReadJson<PlayerSaveData>( SaveFileName ) ?? new PlayerSaveData();
		}
		else
		{
			Current = new PlayerSaveData();
			Save();
		}
	}

	// Сохранение данных на диск
	public static void Save()
	{
		FileSystem.Data.WriteJson( SaveFileName, Current );
	}

	// Проверка и запись нового рекорда
	public static void TryUpdateHighScore( int currentScore )
	{
		if ( currentScore > Current.HighScore )
		{
			Current.HighScore = currentScore;
			Save();
		}
	}
}
seilordstudio.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;
    }
}
seilordstudio.one_more_push / ArenaShrinker.cs
Game game
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;

public sealed class ArenaShrinker : Component
{
    [Property] public float RoundTime { get; set; } = 120f;
    [Property] public float ShrinkInterval { get; set; } = 15f;
    [Property] public float WarningDuration { get; set; } = 3f;

    public float TimeRemaining { get; private set; }

    private float nextShrinkTime;
    private bool isWarningActive = false;
    private List<ArenaTile> activeWarningTiles = new();

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

    public void StartRound()
    {
        TimeRemaining = RoundTime;
        nextShrinkTime = RoundTime - ShrinkInterval;
        isWarningActive = false;
        activeWarningTiles.Clear();

        var tiles = Scene.GetAllComponents<ArenaTile>();
        foreach ( var tile in tiles )
        {
            tile.ResetTile();
        }

        Log.Info( "[ArenaShrinker] Round started!" );
    }

    protected override void OnUpdate()
    {
        if ( TimeRemaining <= 0 )
        {
            if ( RoundManager.Instance != null && RoundManager.Instance.IsInRound )
            {
                _ = RoundManager.Instance.EndRound();
            }
            return;
        }

        TimeRemaining -= Time.Delta;

        if ( !isWarningActive && TimeRemaining <= (nextShrinkTime + WarningDuration) )
        {
            TriggerWarning();
        }

        if ( isWarningActive && TimeRemaining <= nextShrinkTime )
        {
            DropWarnedTiles();
        }
    }

    private void TriggerWarning()
    {
        isWarningActive = true;
        activeWarningTiles.Clear();

        var availableTiles = Scene.GetAllComponents<ArenaTile>()
            .Where( t => !t.IsFalling )
            .ToList();

        if ( availableTiles.Count <= 2 ) return;

        Vector3 center = Vector3.Zero;
        foreach ( var tile in availableTiles )
        {
            center += tile.WorldPosition;
        }
        center /= availableTiles.Count;

        var sortedTiles = availableTiles
            .OrderByDescending( t => Vector3.DistanceBetween( t.WorldPosition, center ) )
            .ToList();

        int tilesToDropCount = Math.Max( 1, (int)(sortedTiles.Count * 0.25f) );

        if ( sortedTiles.Count - tilesToDropCount < 2 )
        {
            tilesToDropCount = Math.Max( 0, sortedTiles.Count - 2 );
        }

        if ( tilesToDropCount <= 0 ) return;

        activeWarningTiles = sortedTiles.Take( tilesToDropCount ).ToList();

        foreach ( var tile in activeWarningTiles )
        {
            tile.HighlightWarning( true );
        }

        Log.Info( $"[ArenaShrinker] Warning started for {activeWarningTiles.Count} outer tiles!" );
    }

    private void DropWarnedTiles()
    {
        foreach ( var tile in activeWarningTiles )
        {
            tile.HighlightWarning( false );
            tile.Drop();
        }

        activeWarningTiles.Clear();
        isWarningActive = false;
        nextShrinkTime -= ShrinkInterval;

        Log.Info( "[ArenaShrinker] Outer ring dropped successfully!" );
    }
}
seilordstudio.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 );
        }
    }
}
seilordstudio.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;
    }
}
seilordstudio.one_more_push / PushHud.razor
Game game
@using Sandbox;
@using Sandbox.UI;
@using System;
@using System.Linq;
@using System.Collections.Generic;
@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="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="@TogglePropMenu">@L( "Нажмите [R] Пропсы", "Press [R] Props" )</label>
                </div>
            }
        </div>
    </div>

    @if ( IsPropMenuOpen )
    {
        <div class="menu-overlay" onclick="@ClosePropMenu">
            <div class="prop-menu-card" onclick="@((e) => e.StopPropagation())">
                <label class="menu-title">@L( "СПАВН ПРОПСОВ", "SPAWN PROPS" )</label>

                <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>

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

    <div class="cooldown-box">
        <label class="cooldown-title">@L( "Перезарядка", "Cooldown" )</label>
        <div class="bar-background">
            <div class="bar-fill" style="width: @($"{GetPercent():F1}%");"></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 ( !IsMenuOpen && !IsSettingsOpen && !IsPropMenuOpen )
    {
        <button class="side-settings-btn" onclick="@OpenMenu">
            <label class="key-hint">[ESC]</label>
            <label class="icon">⚙</label>
        </button>
    }

    @if ( IsMenuOpen )
    {
        <div class="menu-overlay">
            <div class="menu-card">
                <label class="menu-title">ONE MORE PUSH</label>
                <button class="menu-btn play" onclick="@StartGame">@L( "ИГРАТЬ", "PLAY" )</button>
                <button class="menu-btn" onclick="@OpenSettings">@L( "НАСТРОЙКИ", "SETTINGS" )</button>
                <button class="menu-btn quit" onclick="@QuitGame">@L( "ВЫХОД", "QUIT" )</button>
            </div>
        </div>
    }

    @if ( IsSettingsOpen )
    {
        <div class="menu-overlay">
            <div class="menu-card settings-card">
                <label class="menu-title">@L( "НАСТРОЙКИ", "SETTINGS" )</label>
                <div class="settings-content">
                    <div class="setting-item lang-row">
                        <label class="setting-label">@L( "ЯЗЫК:", "LANGUAGE:" )</label>
                        <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-item lang-row">
                        <label class="setting-label">@L( "СПАВН БОТОВ:", "SPAWN BOTS:" )</label>
                        <button class="lang-btn @(BotSpawner.EnableBots ? "active" : "")" onclick="@ToggleBots">
                            @(BotSpawner.EnableBots ? L( "ВКЛ", "ON" ) : L( "ВЫКЛ", "OFF" ))
                        </button>
                    </div>

                    <div class="setting-item lang-row">
                        <label class="setting-label">@L( "МУЗЫКА:", "MUSIC:" )</label>
                        <button class="lang-btn @(EnableMusic ? "active" : "")" onclick="@ToggleMusic">
                            @(EnableMusic ? L( "ВКЛ", "ON" ) : L( "ВЫКЛ", "OFF" ))
                        </button>
                    </div>

                    <label class="setting-item">@L( "ЧУВСТВИТЕЛЬНОСТЬ: 1.0", "SENSITIVITY: 1.0" )</label>
                </div>
                <button class="menu-btn" onclick="@CloseSettings">@L( "НАЗАД", "BACK" )</button>
            </div>
        </div>
    }

</root>

@code {
    [Property] public PushAbility PushAbility { get; set; }

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

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

    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 )
    {
        return CurrentLanguage == "RU" ? ru : en;
    }

    private bool IsReady => PushAbility?.IsReady ?? false;

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

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

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

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

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

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

        ApplyMouseState();
    }

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

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

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

    private void TogglePropMenu()
    {
        IsPropMenuOpen = !IsPropMenuOpen;
        ApplyMouseState();
        StateHasChanged();
    }

    private void ClosePropMenu()
    {
        IsPropMenuOpen = false;
        ApplyMouseState();
        StateHasChanged();
    }

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

    private void ToggleMenu()
    {
        if ( IsSettingsOpen ) CloseSettings();
        else if ( IsPropMenuOpen ) ClosePropMenu();
        else if ( IsMenuOpen ) StartGame();
        else OpenMenu();
    }

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

    private void OpenMenu()
    {
        IsMenuOpen = true;
        IsSettingsOpen = false;
        IsPropMenuOpen = false;
        ApplyMouseState();
    }

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

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

    private void QuitGame()
    {
        Game.Close();
    }

    private int GetScore()
    {
        return 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()
    {
        return SaveManager.Current?.HighScore ?? 0;
    }

    private void OpenChat()
    {
        IsChatOpen = true;
        IsPropMenuOpen = 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 )
    {
        return type switch
        {
            WeatherType.LowGravity => L( "НЕВЕСОМОСТЬ", "LOW GRAVITY" ),
            WeatherType.SlipperyIce => L( "ГОЛОЛЁД", "SLIPPERY ICE" ),
            WeatherType.StrongWind => L( "ШТОРМОВОЙ ВЕТЕР", "STRONG WIND" ),
            _ => ""
        };
    }

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

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

    private int GetDeaths()
    {
        return 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( 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( IsPropMenuOpen );
        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 );
        return hash.ToHashCode();
    }
}
Debug: View Raw JSON Response
{
    "TotalCount": 27,
    "Files": [
        {
            "Ident": "seilordstudio.one_more_push",
            "Path": "MapEventManager.cs",
            "FileName": "MapEventManager.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "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": "seilordstudio.one_more_push",
            "Path": "ScoreManager.cs",
            "FileName": "ScoreManager.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "Code": "using Sandbox;\r\nusing System; // \u0414\u043e\u0431\u0430\u0432\u043b\u0435\u043d\u043e \u0434\u043b\u044f \u0440\u0430\u0431\u043e\u0442\u044b Math.Max\r\n\r\npublic class ScoreManager : Component\r\n{\r\n    public static ScoreManager Instance { get; private set; }\r\n\r\n    public int Score { get; private set; } = 0;\r\n    public int Deaths { get; private set; } = 0; \r\n\r\n    protected override void OnStart()\r\n    {\r\n        Instance = this;\r\n        SaveManager.Load(); \r\n    }\r\n\r\n    public void AddScore( int amount = 1 )\r\n    {\r\n        Score += amount;\r\n        SaveManager.TryUpdateHighScore( Score ); \r\n    }\r\n\r\n    public void AddDeath( int amount = 1 )\r\n    {\r\n        Deaths += amount;\r\n\r\n       \r\n        Score = Math.Max( 0, Score - 20 );\r\n    }\r\n\r\n    public void ResetScore()\r\n    {\r\n        Score = 0;\r\n    }\r\n}"
        },
        {
            "Ident": "seilordstudio.one_more_push",
            "Path": "StickyTarZone.cs",
            "FileName": "StickyTarZone.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "Code": "using Sandbox;\r\nusing System.Collections.Generic;\r\n\r\npublic sealed class StickyTarZone : Component, Component.ITriggerListener\r\n{\r\n    [Property] public float SpeedMultiplier { get; set; } = 0.4f; \r\n    [Property] public float RigidbodyDrag { get; set; } = 0.85f;  \r\n    [Property] public Color TarTint { get; set; } = new Color( 0.1f, 0.1f, 0.1f, 1f ); \r\n\r\n    private readonly HashSet<GameObject> _insideObjects = new();\r\n    private readonly Dictionary<ModelRenderer, Color> _originalColors = new();\r\n\r\n    public void OnTriggerEnter( Collider other )\r\n    {\r\n        if ( !other.GameObject.IsValid() ) return;\r\n\r\n        var rootObj = other.GameObject.Root;\r\n        _insideObjects.Add( rootObj );\r\n\r\n        \r\n        var renderers = rootObj.Components.GetAll<ModelRenderer>();\r\n        foreach ( var mr in renderers )\r\n        {\r\n            if ( mr.IsValid() && !_originalColors.ContainsKey( mr ) )\r\n            {\r\n                _originalColors[mr] = mr.Tint;\r\n                mr.Tint = TarTint;\r\n            }\r\n        }\r\n    }\r\n\r\n    public void OnTriggerExit( Collider other )\r\n    {\r\n        if ( !other.GameObject.IsValid() ) return;\r\n\r\n        var rootObj = other.GameObject.Root;\r\n        _insideObjects.Remove( rootObj );\r\n\r\n        \r\n        var renderers = rootObj.Components.GetAll<ModelRenderer>();\r\n        foreach ( var mr in renderers )\r\n        {\r\n            if ( mr.IsValid() && _originalColors.TryGetValue( mr, out var origColor ) )\r\n            {\r\n                mr.Tint = origColor;\r\n                _originalColors.Remove( mr );\r\n            }\r\n        }\r\n    }\r\n\r\n    protected override void OnFixedUpdate()\r\n    {\r\n        \r\n        _insideObjects.RemoveWhere( obj => !obj.IsValid() );\r\n\r\n        foreach ( var targetObj in _insideObjects )\r\n        {\r\n            \r\n            var rb = targetObj.Components.Get<Rigidbody>();\r\n            if ( rb.IsValid() && rb.PhysicsBody.IsValid() )\r\n                {\r\n                    rb.PhysicsBody.Velocity *= 0.5f; // \u0417\u0430\u043c\u0435\u0434\u043b\u0435\u043d\u0438\u0435 \u0432 \u0441\u043c\u043e\u043b\u0435\r\n                }\r\n\r\n            \r\n            var cc = targetObj.Components.Get<CharacterController>();\r\n            if ( cc.IsValid() )\r\n            {\r\n                cc.Velocity *= SpeedMultiplier;\r\n            }\r\n        }\r\n    }\r\n}"
        },
        {
            "Ident": "seilordstudio.one_more_push",
            "Path": "TimeFreezeAbility.cs",
            "FileName": "TimeFreezeAbility.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "Code": "using Sandbox;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\n\r\npublic sealed class TimeFreezeAbility : Component\r\n{\r\n    [Property] public float FreezeDuration { get; set; } = 4f;\r\n    [Property] public float Cooldown { get; set; } = 20f;\r\n    [Property] public float FreezeRadius { get; set; } = 500f;\r\n    [Property] public GameObject FreezeParticle { get; set; }\r\n    [Property] public SoundEvent FreezeSound { get; set; }\r\n\r\n    public bool IsFreezeActive { get; private set; } = false;\r\n\r\n    private TimeSince timeSinceActivated;\r\n    private TimeSince timeSinceCooldown;\r\n    private bool isCoolingDown = false;\r\n\r\n    private List<Rigidbody> frozenRigidbodies = new();\r\n    private List<CharacterController> frozenControllers = new();\r\n    private Dictionary<ModelRenderer, Color> originalRendererColors = new();\r\n\r\n    protected override void OnUpdate()\r\n    {\r\n        if ( IsFreezeActive && timeSinceActivated >= FreezeDuration )\r\n        {\r\n            UnfreezeTime();\r\n        }\r\n\r\n        if ( Input.Pressed( \"use\" ) )\r\n        {\r\n            TryActivateFreeze();\r\n        }\r\n\r\n        if ( IsFreezeActive )\r\n        {\r\n            foreach ( var rb in frozenRigidbodies )\r\n            {\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            foreach ( var cc in frozenControllers )\r\n            {\r\n                if ( cc.IsValid() )\r\n                {\r\n                    cc.Velocity = Vector3.Zero;\r\n                }\r\n            }\r\n        }\r\n    }\r\n\r\n    public void TryActivateFreeze()\r\n    {\r\n        if ( IsFreezeActive ) return;\r\n        if ( isCoolingDown && timeSinceCooldown < Cooldown ) return;\r\n\r\n        IsFreezeActive = true;\r\n        timeSinceActivated = 0;\r\n        frozenRigidbodies.Clear();\r\n        frozenControllers.Clear();\r\n        originalRendererColors.Clear();\r\n\r\n        if ( FreezeParticle.IsValid() )\r\n        {\r\n            FreezeParticle.Clone( WorldPosition );\r\n        }\r\n\r\n        if ( FreezeSound != null )\r\n        {\r\n            Sound.Play( FreezeSound, WorldPosition );\r\n        }\r\n\r\n        var startPos = WorldPosition;\r\n        var endPos = WorldPosition + Vector3.Up * 1f;\r\n\r\n        var hits = Scene.Trace.Sphere( FreezeRadius, startPos, endPos )\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 rb = targetObj.Components.Get<Rigidbody>();\r\n            if ( rb.IsValid() && !frozenRigidbodies.Contains( rb ) )\r\n            {\r\n                frozenRigidbodies.Add( rb );\r\n            }\r\n\r\n            var cc = targetObj.Components.Get<CharacterController>();\r\n            if ( cc.IsValid() && !frozenControllers.Contains( cc ) )\r\n            {\r\n                frozenControllers.Add( cc );\r\n            }\r\n\r\n            var renderers = targetObj.Components.GetAll<ModelRenderer>();\r\n            foreach ( var mr in renderers )\r\n            {\r\n                if ( mr.IsValid() && !originalRendererColors.ContainsKey( mr ) )\r\n                {\r\n                    originalRendererColors[mr] = mr.Tint;\r\n                    mr.Tint = new Color( 0.3f, 0.7f, 1.0f, 1.0f );\r\n                }\r\n            }\r\n        }\r\n\r\n        Log.Info( $\"[TimeFreeze] Frozen {frozenRigidbodies.Count} Rigidbodies and {frozenControllers.Count} Controllers!\" );\r\n    }\r\n\r\n    private void UnfreezeTime()\r\n    {\r\n        IsFreezeActive = false;\r\n        isCoolingDown = true;\r\n        timeSinceCooldown = 0;\r\n\r\n        foreach ( var kvp in originalRendererColors )\r\n        {\r\n            if ( kvp.Key.IsValid() )\r\n            {\r\n                kvp.Key.Tint = kvp.Value;\r\n            }\r\n        }\r\n\r\n        frozenRigidbodies.Clear();\r\n        frozenControllers.Clear();\r\n        originalRendererColors.Clear();\r\n\r\n        Log.Info( \"[TimeFreeze] Time Freeze Expired!\" );\r\n    }\r\n}"
        },
        {
            "Ident": "seilordstudio.one_more_push",
            "Path": "CustomTopDownController.cs",
            "FileName": "CustomTopDownController.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "Code": "public sealed class CustomTopDownController : Component\r\n{\r\n\t[RequireComponent] PlayerController Controller { get; set; }\r\n\r\n\tprotected override void OnFixedUpdate()\r\n\t{\r\n\t\t// Lets make the player move when we press WASD\r\n\t\tvar speed = Input.Down( \"Run\" ) ? Controller.RunSpeed : Controller.WalkSpeed;\r\n\t\tController.WishVelocity = Input.AnalogMove * speed;\r\n\r\n\t\t// And rotate the player to face the direction of the movement\r\n\t\tif ( Controller.WishVelocity.LengthSquared > 0 )\r\n\t\t{\r\n\t\t\tvar targetAngle = Controller.WishVelocity.EulerAngles;\r\n\t\t\tController.EyeAngles = Rotation.Slerp( Controller.EyeAngles, targetAngle, Time.Delta * 10f );\r\n\t\t}\r\n\t}\r\n\r\n\tprotected override void OnPreRender()\r\n\t{\r\n\t\t// This will update the camera's position so that it's up in the air and back a bit, looking down at an angle.\r\n\t\tScene.Camera.WorldPosition = Controller.WorldPosition + Vector3.Up * 1024f + Vector3.Backward * 256f;\r\n\t\tScene.Camera.WorldRotation = new Angles( 75, 0, 0 );\r\n\t}\r\n}\r\n\r\n\r\n\r\n"
        },
        {
            "Ident": "seilordstudio.one_more_push",
            "Path": "RoundManager.cs",
            "FileName": "RoundManager.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "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        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": "seilordstudio.one_more_push",
            "Path": "pushhud.razor.scss",
            "FileName": "pushhud.razor.scss",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "Code": "@font-face {\r\n    font-family: 'Russo One';\r\n    src: url( 'prefab/RussoOne-Regular.ttf' );\r\n}\r\n\r\nPushHud, root, .hud-root {\r\n    position: absolute;\r\n    left: 0px;\r\n    top: 0px;\r\n    width: 100%;\r\n    height: 100%;\r\n    background-color: transparent;\r\n    pointer-events: none;\r\n    font-family: 'Russo One', sans-serif;\r\n}\r\n\r\n/* --- \u0412\u0415\u0420\u0425\u041d\u042f\u042f \u041f\u0410\u041d\u0415\u041b\u042c \u0421\u0427\u0415\u0422\u0427\u0418\u041a\u041e\u0412 \u0418 \u0420\u0410\u0423\u041d\u0414\u0410 --- */\r\n.top-header-container {\r\n    position: absolute;\r\n    top: 20px;\r\n    left: 50%;\r\n    transform: translateX(-50%);\r\n    flex-direction: column;\r\n    align-items: center;\r\n    gap: 10px;\r\n}\r\n\r\n.score-box {\r\n    flex-direction: row;\r\n    align-items: center;\r\n    gap: 28px;\r\n    background-color: rgba(12, 14, 20, 0.95);\r\n    border: 2.5px solid #ffaa00;\r\n    border-radius: 12px;\r\n    padding: 10px 30px 12px 30px;\r\n    box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.85);\r\n\r\n    .score-card {\r\n        flex-direction: column;\r\n        align-items: center;\r\n        min-width: 75px;\r\n\r\n        .score-title {\r\n            font-size: 13px;\r\n            color: #9aa0a6;\r\n            letter-spacing: 1px;\r\n            text-shadow: 0px 1px 3px rgba(0, 0, 0, 0.9);\r\n        }\r\n\r\n        .score-value {\r\n            font-size: 32px;\r\n            color: #ffffff;\r\n            margin-top: -2px;\r\n            text-shadow: 0px 2px 5px rgba(0, 0, 0, 0.9);\r\n        }\r\n\r\n        &.time {\r\n            .score-title { color: #ffb700; }\r\n            .score-value { color: #ffd000; }\r\n        }\r\n\r\n        &.death .score-value {\r\n            color: #ff4757;\r\n        }\r\n\r\n        &.highlight {\r\n            .score-title { color: #ffaa00; }\r\n            .score-value { color: #ffaa00; }\r\n        }\r\n    }\r\n}\r\n\r\n.round-badge {\r\n    padding: 6px 24px;\r\n    background-color: rgba(15, 18, 24, 0.95);\r\n    border: 2px solid #ff9900;\r\n    border-radius: 20px;\r\n    color: #ffaa00;\r\n    font-size: 13px;\r\n    font-weight: bold;\r\n    letter-spacing: 2px;\r\n    text-shadow: 0px 0px 10px rgba(255, 170, 0, 0.7);\r\n    box-shadow: 0px 4px 14px rgba(0, 0, 0, 0.7);\r\n}\r\n\r\n/* --- \u041f\u041e\u0413\u041e\u0414\u041d\u042b\u0415 \u0418\u0412\u0415\u041d\u0422\u042b / \u0410\u041d\u041e\u041c\u0410\u041b\u0418\u0418 --- */\r\n.weather-banner {\r\n    flex-direction: row;\r\n    align-items: center;\r\n    gap: 14px;\r\n    background-color: rgba(14, 16, 22, 0.95);\r\n    border: 2.5px solid rgba(255, 255, 255, 0.25);\r\n    border-radius: 10px;\r\n    padding: 8px 20px;\r\n    margin-top: 6px;\r\n    box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.85);\r\n\r\n    .weather-icon {\r\n        font-size: 24px;\r\n    }\r\n\r\n    .weather-info {\r\n        flex-direction: column;\r\n\r\n        .weather-title {\r\n            font-size: 16px;\r\n            color: #ffffff;\r\n            letter-spacing: 0.5px;\r\n            text-shadow: 0px 1px 3px rgba(0, 0, 0, 0.9);\r\n        }\r\n\r\n        .weather-sub {\r\n            font-size: 10px;\r\n            color: #8a929a;\r\n            letter-spacing: 1.5px;\r\n        }\r\n    }\r\n\r\n    .weather-timer-badge {\r\n        background-color: rgba(255, 255, 255, 0.12);\r\n        border: 1px solid rgba(255, 255, 255, 0.25);\r\n        border-radius: 6px;\r\n        padding: 6px 12px;\r\n\r\n        .timer-text {\r\n            font-size: 15px;\r\n            color: #ffffff;\r\n            text-shadow: 0px 1px 3px rgba(0, 0, 0, 0.9);\r\n        }\r\n    }\r\n\r\n    &.wind {\r\n        border-color: #ff9900;\r\n        .weather-title, .timer-text { color: #ffaa00; }\r\n    }\r\n\r\n    &.ice {\r\n        border-color: #00e5ff;\r\n        .weather-title, .timer-text { color: #00e5ff; }\r\n    }\r\n\r\n    &.gravity {\r\n        border-color: #d670ff;\r\n        .weather-title, .timer-text { color: #d670ff; }\r\n    }\r\n}\r\n\r\n/* --- \u041a\u041d\u041e\u041f\u041a\u0410 \u041c\u0415\u041d\u042e [ESC] --- */\r\n.side-settings-btn {\r\n    position: absolute;\r\n    top: 25px;\r\n    right: 25px;\r\n    height: 48px;\r\n    padding: 0 18px;\r\n    background-color: rgba(12, 14, 20, 0.95);\r\n    border: 2px solid rgba(255, 255, 255, 0.3);\r\n    border-radius: 10px;\r\n    flex-direction: row;\r\n    align-items: center;\r\n    justify-content: center;\r\n    gap: 10px;\r\n    pointer-events: all;\r\n    cursor: pointer;\r\n\r\n    .key-hint {\r\n        font-size: 13px;\r\n        color: #9aa0a6;\r\n    }\r\n\r\n    .icon {\r\n        font-size: 22px;\r\n        color: #ffffff;\r\n    }\r\n\r\n    &:hover {\r\n        background-color: rgba(255, 255, 255, 0.2);\r\n        border-color: #ffaa00;\r\n\r\n        .key-hint, .icon {\r\n            color: #ffaa00;\r\n        }\r\n    }\r\n}\r\n\r\n/* --- \u0422\u0410\u0411\u041b\u0418\u0426\u0410 \u0418\u0413\u0420\u041e\u041a\u041e\u0412 (\u0421\u041f\u0420\u0410\u0412\u0410 \u0412\u0412\u0415\u0420\u0425\u0423 \u041f\u041e\u0414 ESC) --- */\r\n.scoreboard-box {\r\n    position: absolute;\r\n    top: 85px; \r\n    right: 25px;\r\n    width: 250px;\r\n    flex-direction: column;\r\n    background-color: rgba(12, 14, 20, 0.95);\r\n    border: 1.5px solid rgba(255, 255, 255, 0.25);\r\n    border-radius: 12px;\r\n    padding: 12px 16px;\r\n    gap: 10px;\r\n    box-shadow: 0px 8px 24px rgba(0, 0, 0, 0.8);\r\n\r\n    .scoreboard-header {\r\n        border-bottom: 1px solid rgba(255, 255, 255, 0.15);\r\n        padding-bottom: 6px;\r\n\r\n        .header-title {\r\n            font-size: 12px;\r\n            color: #7f8c8d;\r\n            letter-spacing: 1.5px;\r\n        }\r\n    }\r\n\r\n    .player-list {\r\n        flex-direction: column;\r\n        gap: 6px;\r\n\r\n        .player-row {\r\n            flex-direction: row;\r\n            justify-content: space-between;\r\n            align-items: center;\r\n            padding: 6px 10px;\r\n            border-radius: 6px;\r\n            background-color: rgba(255, 255, 255, 0.05);\r\n\r\n            .player-name {\r\n                font-size: 15px;\r\n                color: #ffffff;\r\n                text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.8);\r\n            }\r\n\r\n            .player-score {\r\n                font-size: 16px;\r\n                color: #ffaa00;\r\n                text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.8);\r\n            }\r\n\r\n            &.local {\r\n                background-color: rgba(0, 255, 157, 0.18);\r\n                border: 1px solid rgba(0, 255, 157, 0.5);\r\n\r\n                .player-name, .player-score {\r\n                    color: #00ff9d;\r\n                }\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\n/* --- \u0427\u0410\u0422 \u0418 \u0423\u0412\u0415\u0414\u041e\u041c\u041b\u0415\u041d\u0418\u042f (\u0421\u041b\u0415\u0412\u0410 \u0412\u0412\u0415\u0420\u0425\u0423) --- */\r\n.top-left-box {\r\n    position: absolute;\r\n    top: 25px;\r\n    left: 25px;\r\n    flex-direction: column;\r\n    gap: 12px;\r\n    pointer-events: none;\r\n\r\n    .chat-box {\r\n        width: 380px;\r\n        flex-direction: column;\r\n        gap: 10px;\r\n\r\n        .chat-messages {\r\n            flex-direction: column;\r\n            gap: 6px;\r\n\r\n            .chat-row {\r\n                flex-direction: row;\r\n                gap: 10px;\r\n                background-color: rgba(10, 12, 18, 0.92);\r\n                padding: 6px 12px;\r\n                border-radius: 8px;\r\n                border-left: 4px solid #ffaa00;\r\n\r\n                .chat-author {\r\n                    font-size: 14px;\r\n                    color: #ffaa00;\r\n                }\r\n\r\n                .chat-text {\r\n                    font-size: 14px;\r\n                    color: #ffffff;\r\n                    text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.8);\r\n                }\r\n            }\r\n        }\r\n\r\n        .hints-row {\r\n            flex-direction: row;\r\n            gap: 10px;\r\n        }\r\n\r\n        .chat-hint {\r\n            pointer-events: all;\r\n            font-size: 13px;\r\n            color: #d1d5db;\r\n            background-color: rgba(14, 18, 24, 0.95);\r\n            border: 1.5px solid rgba(255, 255, 255, 0.25);\r\n            border-radius: 8px;\r\n            padding: 8px 14px;\r\n            cursor: pointer;\r\n            text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.9);\r\n\r\n            &:hover {\r\n                color: #00ff9d;\r\n                border-color: #00ff9d;\r\n            }\r\n\r\n            &.prop-hint {\r\n                color: #ffaa00;\r\n                border-color: rgba(255, 170, 0, 0.6);\r\n\r\n                &:hover {\r\n                    color: #ffd000;\r\n                    border-color: #ffaa00;\r\n                }\r\n            }\r\n        }\r\n\r\n        .chat-input-row {\r\n            pointer-events: all;\r\n            \r\n            textentry {\r\n                width: 100%;\r\n                height: 40px;\r\n                background-color: rgba(10, 12, 18, 0.95);\r\n                border: 2px solid #ffaa00;\r\n                color: #ffffff;\r\n                padding: 0 12px;\r\n                font-size: 15px;\r\n                border-radius: 8px;\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\n/* --- \u0428\u041a\u0410\u041b\u0410 \u0417\u0410\u0420\u042f\u0414\u0410 \u0418 \u041f\u0415\u0420\u0415\u0417\u0410\u0420\u042f\u0414\u041a\u0410 --- */\r\n.cooldown-box {\r\n    position: absolute;\r\n    left: 25px;\r\n    bottom: 25px;\r\n    flex-direction: column;\r\n    align-items: flex-start;\r\n    gap: 6px;\r\n\r\n    .cooldown-title {\r\n        font-size: 13px;\r\n        color: #9aa0a6;\r\n        letter-spacing: 1px;\r\n        text-shadow: 0px 1px 3px rgba(0, 0, 0, 0.9);\r\n    }\r\n\r\n    .bar-background {\r\n        width: 300px;\r\n        height: 22px;\r\n        background-color: rgba(10, 12, 18, 0.95);\r\n        border: 2px solid #ffffff;\r\n        border-radius: 8px;\r\n        overflow: hidden;\r\n\r\n        .bar-fill {\r\n            height: 100%;\r\n            background-color: #ffaa00;\r\n        }\r\n    }\r\n}\r\n\r\n.charge-box {\r\n    position: absolute;\r\n    bottom: 150px;\r\n    left: 50%;\r\n    transform: translateX(-50%);\r\n    width: 380px;\r\n    flex-direction: column;\r\n\r\n    .charge-title {\r\n        color: #ff9900;\r\n        font-size: 15px;\r\n        font-weight: bold;\r\n        letter-spacing: 2px;\r\n        text-align: center;\r\n        margin-bottom: 8px;\r\n        text-shadow: 0px 2px 4px rgba(0, 0, 0, 0.9);\r\n    }\r\n\r\n    .bar-background {\r\n        width: 100%;\r\n        height: 18px;\r\n        background-color: rgba(10, 12, 18, 0.95);\r\n        border: 2px solid #ff9900;\r\n        border-radius: 8px;\r\n        overflow: hidden;\r\n\r\n        .bar-fill.charge {\r\n            height: 100%;\r\n            background: linear-gradient(to right, #ffaa00, #ff3300);\r\n            transition: width 0.05s ease-out;\r\n        }\r\n    }\r\n}\r\n\r\n/* --- \u0425\u041e\u0422\u0411\u0410\u0420 \u0421\u041f\u041e\u0421\u041e\u0411\u041d\u041e\u0421\u0422\u0415\u0419 (\u0421\u041d\u0418\u0417\u0423) --- */\r\n.hotbar-container {\r\n    position: absolute;\r\n    bottom: 25px;\r\n    left: 50%;\r\n    transform: translateX(-50%);\r\n    flex-direction: row;\r\n    gap: 16px;\r\n    pointer-events: all;\r\n\r\n    .slot-wrapper {\r\n        flex-direction: column;\r\n        align-items: center;\r\n        gap: 8px;\r\n\r\n        .hotbar-slot {\r\n            width: 80px;\r\n            height: 80px;\r\n            background-color: rgba(14, 16, 22, 0.95);\r\n            background-image: linear-gradient(to bottom, #2b2b2b, #121212);\r\n            border: 2px solid rgba(255, 255, 255, 0.25);\r\n            border-radius: 10px;\r\n            flex-direction: column;\r\n            justify-content: center;\r\n            align-items: center;\r\n            cursor: pointer;\r\n\r\n            &.selected {\r\n                border-color: #ffaa00;\r\n                box-shadow: 0px 0px 16px rgba(255, 170, 0, 0.65);\r\n            }\r\n\r\n            &.locked {\r\n                opacity: 0.5;\r\n                background-color: rgba(8, 8, 12, 0.95);\r\n            }\r\n\r\n            .slot-name {\r\n                font-size: 13px;\r\n                color: #ffffff;\r\n                text-align: center;\r\n                text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.9);\r\n            }\r\n\r\n            .slot-cost {\r\n                font-size: 13px;\r\n                color: #ffd000;\r\n                margin-top: 4px;\r\n                font-weight: bold;\r\n            }\r\n        }\r\n\r\n        .key-badge {\r\n            width: 36px;\r\n            height: 36px;\r\n            background-image: linear-gradient(to bottom, #ffaa00, #cc6600);\r\n            border: 1.5px solid #ffe082;\r\n            border-radius: 8px;\r\n            justify-content: center;\r\n            align-items: center;\r\n            font-size: 16px;\r\n            font-weight: bold;\r\n            color: #ffffff;\r\n            text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.9);\r\n            box-shadow: 0px 3px 8px rgba(0, 0, 0, 0.6);\r\n\r\n            &.selected {\r\n                background-image: linear-gradient(to bottom, #ffbb00, #e65100);\r\n                border-color: #ffffff;\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\n/* --- \u041e\u0412\u0415\u0420\u041b\u0415\u0419 \u0418 \u041c\u0415\u041d\u042e --- */\r\n.menu-overlay {\r\n    position: absolute;\r\n    left: 0px;\r\n    top: 0px;\r\n    width: 100%;\r\n    height: 100%;\r\n    background-color: rgba(8, 10, 14, 0.82);\r\n    justify-content: center;\r\n    align-items: center;\r\n    pointer-events: all;\r\n    z-index: 100;\r\n}\r\n\r\n.menu-card {\r\n    flex-direction: column;\r\n    align-items: center;\r\n    gap: 18px;\r\n    background-color: rgba(14, 16, 22, 0.98);\r\n    padding: 36px 60px;\r\n    border-radius: 16px;\r\n    border: 2px solid rgba(255, 255, 255, 0.25);\r\n    min-width: 380px;\r\n    box-shadow: 0px 12px 40px rgba(0, 0, 0, 0.9);\r\n\r\n    .menu-title {\r\n        color: #ffffff;\r\n        font-size: 30px;\r\n        font-weight: bold;\r\n        letter-spacing: 2px;\r\n        margin-bottom: 10px;\r\n        text-shadow: 0px 2px 6px rgba(0, 0, 0, 0.9);\r\n    }\r\n\r\n    .menu-btn {\r\n        width: 100%;\r\n        padding: 14px 28px;\r\n        background-color: rgba(255, 255, 255, 0.1);\r\n        border: 1.5px solid rgba(255, 255, 255, 0.25);\r\n        color: #ffffff;\r\n        font-size: 18px;\r\n        font-weight: bold;\r\n        border-radius: 10px;\r\n        align-items: center;\r\n        justify-content: center;\r\n        cursor: pointer;\r\n\r\n        &:hover {\r\n            background-color: rgba(255, 170, 0, 0.25);\r\n            border-color: #ffaa00;\r\n            color: #ffaa00;\r\n        }\r\n\r\n        &.play {\r\n            background-color: #00ff9d;\r\n            border-color: #00ff9d;\r\n            color: #000000;\r\n\r\n            &:hover {\r\n                background-color: #00cc7d;\r\n                border-color: #00cc7d;\r\n                color: #000000;\r\n            }\r\n        }\r\n\r\n        &.quit:hover {\r\n            background-color: rgba(255, 71, 87, 0.45);\r\n            border-color: #ff4757;\r\n            color: #ffffff;\r\n        }\r\n    }\r\n}\r\n\r\n/* --- \u041c\u0415\u041d\u042e \u0421\u041f\u0410\u0412\u041d\u0410 \u041f\u0420\u041e\u041f\u0421\u041e\u0412 --- */\r\n.prop-menu-card {\r\n    flex-direction: column;\r\n    align-items: center;\r\n    gap: 24px;\r\n    background-color: rgba(14, 16, 22, 0.98);\r\n    padding: 32px 48px;\r\n    border-radius: 16px;\r\n    border: 2.5px solid #ffaa00;\r\n    min-width: 460px;\r\n    box-shadow: 0px 14px 45px rgba(0, 0, 0, 0.95);\r\n\r\n    .menu-title {\r\n        color: #ffaa00;\r\n        font-size: 24px;\r\n        font-weight: bold;\r\n        letter-spacing: 2px;\r\n        text-shadow: 0px 2px 5px rgba(0, 0, 0, 0.9);\r\n    }\r\n\r\n    .prop-grid {\r\n        display: flex;\r\n        flex-direction: row;\r\n        flex-wrap: wrap;\r\n        justify-content: center;\r\n        gap: 16px;\r\n        width: 100%;\r\n\r\n        .prop-item {\r\n            width: 180px;\r\n            padding: 16px 14px;\r\n            background-color: rgba(255, 255, 255, 0.08);\r\n            border: 1.5px solid rgba(255, 170, 0, 0.35);\r\n            border-radius: 12px;\r\n            flex-direction: column;\r\n            align-items: center;\r\n            justify-content: center;\r\n            gap: 8px;\r\n            cursor: pointer;\r\n\r\n            .prop-name {\r\n                font-size: 16px;\r\n                color: #ffffff;\r\n                text-align: center;\r\n                text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.9);\r\n            }\r\n\r\n            .prop-cost {\r\n                font-size: 14px;\r\n                font-weight: bold;\r\n                color: #ffd000;\r\n            }\r\n\r\n            &:hover {\r\n                background-color: rgba(255, 170, 0, 0.25);\r\n                border-color: #ffaa00;\r\n\r\n                .prop-name {\r\n                    color: #ffaa00;\r\n                }\r\n            }\r\n        }\r\n    }\r\n}\r\n\r\n/* --- \u041d\u0410\u0421\u0422\u0420\u041e\u0419\u041a\u0418 --- */\r\n.settings-card {\r\n    min-width: 440px;\r\n\r\n    .settings-content {\r\n        flex-direction: column;\r\n        gap: 16px;\r\n        width: 100%;\r\n        margin-bottom: 14px;\r\n\r\n        .setting-item {\r\n            color: #e0e0e0;\r\n            font-size: 16px;\r\n            font-weight: bold;\r\n            text-shadow: 0px 1px 2px rgba(0, 0, 0, 0.9);\r\n        }\r\n\r\n        .lang-row {\r\n            justify-content: space-between;\r\n            align-items: center;\r\n            width: 100%;\r\n        }\r\n\r\n        .lang-selector {\r\n            gap: 10px;\r\n\r\n            .lang-btn {\r\n                padding: 8px 18px;\r\n                background-color: rgba(255, 255, 255, 0.12);\r\n                border: 1.5px solid rgba(255, 255, 255, 0.25);\r\n                border-radius: 8px;\r\n                color: #b0b0b0;\r\n                font-size: 14px;\r\n                font-weight: bold;\r\n                cursor: pointer;\r\n\r\n                &:hover {\r\n                    background-color: rgba(255, 255, 255, 0.25);\r\n                    color: #ffffff;\r\n                }\r\n\r\n                &.active {\r\n                    background-color: #00ff9d;\r\n                    border-color: #00ff9d;\r\n                    color: #000000;\r\n                }\r\n            }\r\n        }\r\n    }\r\n}"
        },
        {
            "Ident": "seilordstudio.one_more_push",
            "Path": "ManeuverAbility.cs",
            "FileName": "ManeuverAbility.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "Code": "using Sandbox;\r\nusing System;\r\n\r\npublic sealed class ManeuverAbility : Component\r\n{\r\n    [Property] public float DashForce { get; set; } = 900f;\r\n    [Property] public float DashCooldown { get; set; } = 1.2f;\r\n\r\n    [Property] public float ParryWindow { get; set; } = 0.4f;\r\n    [Property] public float ParryCooldown { get; set; } = 2.5f;\r\n\r\n    [Property] public SoundEvent DashSound { get; set; }\r\n    [Property] public SoundEvent ParrySound { get; set; }\r\n    [Property] public SoundEvent ReflectSound { get; set; }\r\n    [Property] public GameObject DashParticle { get; set; }\r\n    [Property] public GameObject ParryParticle { get; set; }\r\n\r\n    private TimeSince _lastDash;\r\n    private TimeSince _lastParry;\r\n    private TimeSince _parryTimer;\r\n\r\n    public bool IsParrying => _parryTimer < ParryWindow;\r\n    public bool IsDashReady => _lastDash >= DashCooldown;\r\n    public bool IsParryReady => _lastParry >= ParryCooldown;\r\n\r\n    protected override void OnUpdate()\r\n    {\r\n        if ( IsProxy ) return;\r\n\r\n        if ( Input.Pressed( \"run\" ) && IsDashReady )\r\n        {\r\n            ExecuteDash();\r\n        }\r\n\r\n        if ( Input.Pressed( \"attack2\" ) && IsParryReady )\r\n        {\r\n            ExecuteParry();\r\n        }\r\n    }\r\n\r\n    private void ExecuteDash()\r\n    {\r\n        var cc = Components.Get<CharacterController>();\r\n        if ( !cc.IsValid() ) return;\r\n\r\n        var moveInput = Input.AnalogMove;\r\n        var wishDir = (WorldTransform.Forward * moveInput.x + WorldTransform.Left * moveInput.y).Normal;\r\n\r\n        if ( wishDir.IsNearlyZero() )\r\n        {\r\n            wishDir = WorldTransform.Forward;\r\n        }\r\n\r\n        PerformDash( wishDir );\r\n    }\r\n\r\n    private void ExecuteParry()\r\n    {\r\n        PerformParry();\r\n    }\r\n\r\n    // --- \u041c\u0415\u0422\u041e\u0414\u042b \u0414\u041b\u042f \u0411\u041e\u0422\u041e\u0412 \u0418 \u0412\u041d\u0415\u0428\u041d\u0418\u0425 \u0412\u042b\u0417\u041e\u0412\u041e\u0412 ---\r\n\r\n    public void TryExecuteBotDash( Vector3 direction )\r\n    {\r\n        if ( !IsDashReady ) return;\r\n        PerformDash( direction );\r\n    }\r\n\r\n    public void TryExecuteBotParry()\r\n    {\r\n        if ( !IsParryReady ) return;\r\n        PerformParry();\r\n    }\r\n\r\n    private void PerformDash( Vector3 direction )\r\n    {\r\n        _lastDash = 0;\r\n\r\n        var cc = Components.Get<CharacterController>();\r\n        if ( cc.IsValid() )\r\n        {\r\n            cc.Velocity += direction.Normal * DashForce;\r\n\r\n            if ( DashSound != null ) Sound.Play( DashSound, WorldPosition );\r\n            if ( DashParticle.IsValid() ) DashParticle.Clone( WorldPosition );\r\n        }\r\n    }\r\n\r\n    private void PerformParry()\r\n    {\r\n        _lastParry = 0;\r\n        _parryTimer = 0;\r\n\r\n        if ( ParrySound != null ) Sound.Play( ParrySound, WorldPosition );\r\n        if ( ParryParticle.IsValid() ) ParryParticle.Clone( WorldPosition );\r\n    }\r\n\r\n    public void OnSuccessfulReflect( Vector3 reflectOrigin )\r\n    {\r\n        if ( ReflectSound != null )\r\n        {\r\n            Sound.Play( ReflectSound, WorldPosition );\r\n        }\r\n    }\r\n}"
        },
        {
            "Ident": "seilordstudio.one_more_push",
            "Path": "MusicManager.cs",
            "FileName": "MusicManager.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "Code": "using Sandbox;\r\n\r\npublic class MusicManager : Component\r\n{\r\n    public static MusicManager Instance { get; private set; }\r\n\r\n    [Property] public SoundEvent MusicSound { get; set; }\r\n\r\n    private SoundHandle _musicHandle;\r\n\r\n    protected override void OnStart()\r\n    {\r\n        Instance = this;\r\n\r\n        if ( PushHud.EnableMusic )\r\n        {\r\n            PlayMusic();\r\n        }\r\n    }\r\n\r\n    public void ToggleMusic( bool enable )\r\n    {\r\n        if ( enable )\r\n        {\r\n            PlayMusic();\r\n        }\r\n        else\r\n        {\r\n            StopMusic();\r\n        }\r\n    }\r\n\r\n    public void PlayMusic()\r\n    {\r\n        if ( _musicHandle.IsValid() ) return;\r\n\r\n        if ( MusicSound != null )\r\n        {\r\n            _musicHandle = Sound.Play( MusicSound );\r\n        }\r\n    }\r\n\r\n    public void StopMusic()\r\n    {\r\n        if ( _musicHandle.IsValid() )\r\n        {\r\n            _musicHandle.Stop();\r\n        }\r\n    }\r\n}"
        },
        {
            "Ident": "seilordstudio.one_more_push",
            "Path": "ShieldAbility.cs",
            "FileName": "ShieldAbility.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "Code": "using Sandbox;\r\n\r\npublic sealed class ShieldAbility : Component\r\n{\r\n    [Property] public float ShieldDuration { get; set; } = 5f;\r\n    [Property] public float Cooldown { get; set; } = 15f;\r\n    [Property] public GameObject ShieldVisual { get; set; }\r\n\r\n    public bool IsShieldActive { get; private set; } = false;\r\n\r\n    private TimeSince timeSinceActivated;\r\n    private TimeSince timeSinceCooldown;\r\n    private bool isCoolingDown = false;\r\n\r\n    protected override void OnStart()\r\n    {\r\n        if ( ShieldVisual.IsValid() )\r\n        {\r\n            ShieldVisual.Enabled = false;\r\n        }\r\n    }\r\n\r\n    protected override void OnUpdate()\r\n    {\r\n        if ( IsShieldActive && timeSinceActivated >= ShieldDuration )\r\n        {\r\n            DeactivateShield();\r\n        }\r\n\r\n        if ( Input.Pressed( \"Flashlight\" ) )\r\n        {\r\n            TryActivateShield();\r\n        }\r\n    }\r\n\r\n    public void TryActivateShield()\r\n    {\r\n        if ( IsShieldActive ) return;\r\n        if ( isCoolingDown && timeSinceCooldown < Cooldown ) return;\r\n\r\n        IsShieldActive = true;\r\n        timeSinceActivated = 0;\r\n\r\n        if ( ShieldVisual.IsValid() )\r\n        {\r\n            ShieldVisual.Enabled = true;\r\n        }\r\n\r\n        Log.Info( \"[ShieldAbility] Shield Activated!\" );\r\n    }\r\n\r\n    private void DeactivateShield()\r\n    {\r\n        IsShieldActive = false;\r\n        isCoolingDown = true;\r\n        timeSinceCooldown = 0;\r\n\r\n        if ( ShieldVisual.IsValid() )\r\n        {\r\n            ShieldVisual.Enabled = false;\r\n        }\r\n\r\n        Log.Info( \"[ShieldAbility] Shield Expired!\" );\r\n    }\r\n}"
        },
        {
            "Ident": "seilordstudio.one_more_push",
            "Path": "ArenaTile.cs",
            "FileName": "ArenaTile.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "Code": "using Sandbox;\r\n\r\npublic sealed class ArenaTile : Component\r\n{\r\n    private Vector3 initialPosition;\r\n    public bool IsFalling { get; private set; } = false;\r\n\r\n    [Property] public float FallSpeed { get; set; } = 500f;\r\n\r\n    private ModelRenderer modelRenderer;\r\n    private Color originalColor = Color.White;\r\n\r\n    protected override void OnStart()\r\n    {\r\n        initialPosition = WorldPosition;\r\n        modelRenderer = Components.Get<ModelRenderer>();\r\n\r\n        if ( modelRenderer.IsValid() )\r\n        {\r\n            originalColor = modelRenderer.Tint;\r\n        }\r\n    }\r\n\r\n    public void HighlightWarning( bool enable )\r\n    {\r\n        if ( !modelRenderer.IsValid() ) return;\r\n        modelRenderer.Tint = enable ? Color.Red : originalColor;\r\n    }\r\n\r\n    public void Drop()\r\n    {\r\n        IsFalling = true;\r\n    }\r\n\r\n    protected override void OnUpdate()\r\n    {\r\n        if ( IsFalling )\r\n        {\r\n            WorldPosition += Vector3.Down * FallSpeed * Time.Delta;\r\n        }\r\n    }\r\n\r\n    public void ResetTile()\r\n    {\r\n        IsFalling = false;\r\n        WorldPosition = initialPosition;\r\n        HighlightWarning( false );\r\n    }\r\n}"
        },
        {
            "Ident": "seilordstudio.one_more_push",
            "Path": "Assembly.cs",
            "FileName": "Assembly.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "Code": "global using Sandbox;\r\nglobal using System.Collections.Generic;\r\nglobal using System.Linq;\r\n"
        },
        {
            "Ident": "seilordstudio.one_more_push",
            "Path": "ExplosiveBarrel.cs",
            "FileName": "ExplosiveBarrel.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "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": "seilordstudio.one_more_push",
            "Path": "BotSpawner.cs",
            "FileName": "BotSpawner.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "Code": "using Sandbox;\r\nusing System;\r\nusing System.Collections.Generic;\r\n\r\npublic sealed class BotSpawner : Component\r\n{\r\n    [Property] public GameObject BotPrefab { get; set; }\r\n    [Property] public float SpawnInterval { get; set; } = 4f;\r\n    [Property] public int MaxBots { get; set; } = 5;\r\n\r\n    public static bool EnableBots { get; set; } = true;\r\n\r\n    private List<GameObject> _activeBots = new();\r\n    private TimeSince _timeSinceLastSpawn = 0;\r\n\r\n    protected override void OnUpdate()\r\n    {\r\n        if ( IsProxy ) return;\r\n\r\n        // \u0415\u0441\u043b\u0438 \u0431\u043e\u0442\u044b \u0432\u044b\u043a\u043b\u044e\u0447\u0435\u043d\u044b \u0432 \u043d\u0430\u0441\u0442\u0440\u043e\u0439\u043a\u0430\u0445 \u2014 \u0443\u0434\u0430\u043b\u044f\u0435\u043c \u0443\u0436\u0435 \u0437\u0430\u0441\u043f\u0430\u0432\u043d\u0435\u043d\u043d\u044b\u0445\r\n        if ( !EnableBots )\r\n        {\r\n            if ( _activeBots.Count > 0 )\r\n            {\r\n                foreach ( var bot in _activeBots )\r\n                {\r\n                    if ( bot.IsValid() ) bot.Destroy();\r\n                }\r\n                _activeBots.Clear();\r\n            }\r\n            return;\r\n        }\r\n\r\n        if ( _timeSinceLastSpawn > SpawnInterval )\r\n        {\r\n            _timeSinceLastSpawn = 0;\r\n            SpawnBot();\r\n        }\r\n    }\r\n\r\n    private void SpawnBot()\r\n    {\r\n        if ( !BotPrefab.IsValid() )\r\n        {\r\n            Log.Warning( \"[BotSpawner] BotPrefab \u043d\u0435 \u043d\u0430\u0437\u043d\u0430\u0447\u0435\u043d \u0432 \u0418\u043d\u0441\u043f\u0435\u043a\u0442\u043e\u0440\u0435!\" );\r\n            return;\r\n        }\r\n\r\n        _activeBots.RemoveAll( bot => !bot.IsValid() );\r\n\r\n        if ( _activeBots.Count >= MaxBots ) return;\r\n\r\n        // \u0421\u043f\u0430\u0432\u043d \u043e\u0442\u043d\u043e\u0441\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u043f\u043e\u0437\u0438\u0446\u0438\u0438 \u0441\u043f\u0430\u0432\u043d\u0435\u0440\u0430\r\n        var randomX = Random.Shared.Float( -150f, 150f );\r\n        var randomY = Random.Shared.Float( -150f, 150f );\r\n        var spawnPos = WorldPosition + new Vector3( randomX, randomY, 200f );\r\n\r\n        var botInstance = BotPrefab.Clone( spawnPos );\r\n        if ( !botInstance.IsValid() ) return;\r\n\r\n        _activeBots.Add( botInstance );\r\n\r\n        if ( Network.Active )\r\n        {\r\n            botInstance.NetworkSpawn();\r\n        }\r\n\r\n        Log.Info( $\"[BotSpawner] \u0411\u043e\u0442 \u0437\u0430\u0441\u043f\u0430\u0432\u043d\u0435\u043d \u0432 {spawnPos}\" );\r\n    }\r\n}"
        },
        {
            "Ident": "seilordstudio.one_more_push",
            "Path": "SaveManager.cs",
            "FileName": "SaveManager.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "Code": "using Sandbox;\r\n\r\npublic class PlayerSaveData\r\n{\r\n\tpublic int HighScore { get; set; } = 0;\r\n\tpublic int TotalPushes { get; set; } = 0;\r\n}\r\n\r\npublic static class SaveManager\r\n{\r\n\tprivate const string SaveFileName = \"player_save.json\";\r\n\tpublic static PlayerSaveData Current { get; private set; } = new();\r\n\r\n\t// \u0417\u0430\u0433\u0440\u0443\u0437\u043a\u0430 \u0441\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u043d\u044b\u0445 \u0434\u0430\u043d\u043d\u044b\u0445\r\n\tpublic static void Load()\r\n\t{\r\n\t\tif ( FileSystem.Data.FileExists( SaveFileName ) )\r\n\t\t{\r\n\t\t\tCurrent = FileSystem.Data.ReadJson<PlayerSaveData>( SaveFileName ) ?? new PlayerSaveData();\r\n\t\t}\r\n\t\telse\r\n\t\t{\r\n\t\t\tCurrent = new PlayerSaveData();\r\n\t\t\tSave();\r\n\t\t}\r\n\t}\r\n\r\n\t// \u0421\u043e\u0445\u0440\u0430\u043d\u0435\u043d\u0438\u0435 \u0434\u0430\u043d\u043d\u044b\u0445 \u043d\u0430 \u0434\u0438\u0441\u043a\r\n\tpublic static void Save()\r\n\t{\r\n\t\tFileSystem.Data.WriteJson( SaveFileName, Current );\r\n\t}\r\n\r\n\t// \u041f\u0440\u043e\u0432\u0435\u0440\u043a\u0430 \u0438 \u0437\u0430\u043f\u0438\u0441\u044c \u043d\u043e\u0432\u043e\u0433\u043e \u0440\u0435\u043a\u043e\u0440\u0434\u0430\r\n\tpublic static void TryUpdateHighScore( int currentScore )\r\n\t{\r\n\t\tif ( currentScore > Current.HighScore )\r\n\t\t{\r\n\t\t\tCurrent.HighScore = currentScore;\r\n\t\t\tSave();\r\n\t\t}\r\n\t}\r\n}"
        },
        {
            "Ident": "seilordstudio.one_more_push",
            "Path": "ShopManager.cs",
            "FileName": "ShopManager.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "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": "seilordstudio.one_more_push",
            "Path": "ArenaShrinker.cs",
            "FileName": "ArenaShrinker.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "Code": "using Sandbox;\r\nusing System;\r\nusing System.Collections.Generic;\r\nusing System.Linq;\r\n\r\npublic sealed class ArenaShrinker : Component\r\n{\r\n    [Property] public float RoundTime { get; set; } = 120f;\r\n    [Property] public float ShrinkInterval { get; set; } = 15f;\r\n    [Property] public float WarningDuration { get; set; } = 3f;\r\n\r\n    public float TimeRemaining { get; private set; }\r\n\r\n    private float nextShrinkTime;\r\n    private bool isWarningActive = false;\r\n    private List<ArenaTile> activeWarningTiles = new();\r\n\r\n    protected override void OnStart()\r\n    {\r\n        StartRound();\r\n    }\r\n\r\n    public void StartRound()\r\n    {\r\n        TimeRemaining = RoundTime;\r\n        nextShrinkTime = RoundTime - ShrinkInterval;\r\n        isWarningActive = false;\r\n        activeWarningTiles.Clear();\r\n\r\n        var tiles = Scene.GetAllComponents<ArenaTile>();\r\n        foreach ( var tile in tiles )\r\n        {\r\n            tile.ResetTile();\r\n        }\r\n\r\n        Log.Info( \"[ArenaShrinker] Round started!\" );\r\n    }\r\n\r\n    protected override void OnUpdate()\r\n    {\r\n        if ( TimeRemaining <= 0 )\r\n        {\r\n            if ( RoundManager.Instance != null && RoundManager.Instance.IsInRound )\r\n            {\r\n                _ = RoundManager.Instance.EndRound();\r\n            }\r\n            return;\r\n        }\r\n\r\n        TimeRemaining -= Time.Delta;\r\n\r\n        if ( !isWarningActive && TimeRemaining <= (nextShrinkTime + WarningDuration) )\r\n        {\r\n            TriggerWarning();\r\n        }\r\n\r\n        if ( isWarningActive && TimeRemaining <= nextShrinkTime )\r\n        {\r\n            DropWarnedTiles();\r\n        }\r\n    }\r\n\r\n    private void TriggerWarning()\r\n    {\r\n        isWarningActive = true;\r\n        activeWarningTiles.Clear();\r\n\r\n        var availableTiles = Scene.GetAllComponents<ArenaTile>()\r\n            .Where( t => !t.IsFalling )\r\n            .ToList();\r\n\r\n        if ( availableTiles.Count <= 2 ) return;\r\n\r\n        Vector3 center = Vector3.Zero;\r\n        foreach ( var tile in availableTiles )\r\n        {\r\n            center += tile.WorldPosition;\r\n        }\r\n        center /= availableTiles.Count;\r\n\r\n        var sortedTiles = availableTiles\r\n            .OrderByDescending( t => Vector3.DistanceBetween( t.WorldPosition, center ) )\r\n            .ToList();\r\n\r\n        int tilesToDropCount = Math.Max( 1, (int)(sortedTiles.Count * 0.25f) );\r\n\r\n        if ( sortedTiles.Count - tilesToDropCount < 2 )\r\n        {\r\n            tilesToDropCount = Math.Max( 0, sortedTiles.Count - 2 );\r\n        }\r\n\r\n        if ( tilesToDropCount <= 0 ) return;\r\n\r\n        activeWarningTiles = sortedTiles.Take( tilesToDropCount ).ToList();\r\n\r\n        foreach ( var tile in activeWarningTiles )\r\n        {\r\n            tile.HighlightWarning( true );\r\n        }\r\n\r\n        Log.Info( $\"[ArenaShrinker] Warning started for {activeWarningTiles.Count} outer tiles!\" );\r\n    }\r\n\r\n    private void DropWarnedTiles()\r\n    {\r\n        foreach ( var tile in activeWarningTiles )\r\n        {\r\n            tile.HighlightWarning( false );\r\n            tile.Drop();\r\n        }\r\n\r\n        activeWarningTiles.Clear();\r\n        isWarningActive = false;\r\n        nextShrinkTime -= ShrinkInterval;\r\n\r\n        Log.Info( \"[ArenaShrinker] Outer ring dropped successfully!\" );\r\n    }\r\n}"
        },
        {
            "Ident": "seilordstudio.one_more_push",
            "Path": "HeavyAnvil.cs",
            "FileName": "HeavyAnvil.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "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": "seilordstudio.one_more_push",
            "Path": "BotAI.cs",
            "FileName": "BotAI.cs",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "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": "seilordstudio.one_more_push",
            "Path": "PushHud.razor",
            "FileName": "PushHud.razor",
            "PackageType": "game",
            "CodeKind": "Game",
            "AssetVersionId": 381888,
            "Code": "@using Sandbox;\r\n@using Sandbox.UI;\r\n@using System;\r\n@using System.Linq;\r\n@using System.Collections.Generic;\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=\"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=\"@TogglePropMenu\">@L( \"\u041d\u0430\u0436\u043c\u0438\u0442\u0435 [R] \u041f\u0440\u043e\u043f\u0441\u044b\", \"Press [R] Props\" )</label>\r\n                </div>\r\n            }\r\n        </div>\r\n    </div>\r\n\r\n    @if ( IsPropMenuOpen )\r\n    {\r\n        <div class=\"menu-overlay\" onclick=\"@ClosePropMenu\">\r\n            <div class=\"prop-menu-card\" onclick=\"@((e) => e.StopPropagation())\">\r\n                <label class=\"menu-title\">@L( \"\u0421\u041f\u0410\u0412\u041d \u041f\u0420\u041e\u041f\u0421\u041e\u0412\", \"SPAWN PROPS\" )</label>\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                <button class=\"menu-btn\" onclick=\"@ClosePropMenu\">@L( \"\u0417\u0410\u041a\u0420\u042b\u0422\u042c [R]\", \"CLOSE [R]\" )</button>\r\n            </div>\r\n        </div>\r\n    }\r\n\r\n    <div class=\"cooldown-box\">\r\n        <label class=\"cooldown-title\">@L( \"\u041f\u0435\u0440\u0435\u0437\u0430\u0440\u044f\u0434\u043a\u0430\", \"Cooldown\" )</label>\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\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 ( !IsMenuOpen && !IsSettingsOpen && !IsPropMenuOpen )\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 ( IsMenuOpen )\r\n    {\r\n        <div class=\"menu-overlay\">\r\n            <div class=\"menu-card\">\r\n                <label class=\"menu-title\">ONE MORE PUSH</label>\r\n                <button class=\"menu-btn play\" onclick=\"@StartGame\">@L( \"\u0418\u0413\u0420\u0410\u0422\u042c\", \"PLAY\" )</button>\r\n                <button class=\"menu-btn\" onclick=\"@OpenSettings\">@L( \"\u041d\u0410\u0421\u0422\u0420\u041e\u0419\u041a\u0418\", \"SETTINGS\" )</button>\r\n                <button class=\"menu-btn quit\" onclick=\"@QuitGame\">@L( \"\u0412\u042b\u0425\u041e\u0414\", \"QUIT\" )</button>\r\n            </div>\r\n        </div>\r\n    }\r\n\r\n    @if ( IsSettingsOpen )\r\n    {\r\n        <div class=\"menu-overlay\">\r\n            <div class=\"menu-card settings-card\">\r\n                <label class=\"menu-title\">@L( \"\u041d\u0410\u0421\u0422\u0420\u041e\u0419\u041a\u0418\", \"SETTINGS\" )</label>\r\n                <div class=\"settings-content\">\r\n                    <div class=\"setting-item lang-row\">\r\n                        <label class=\"setting-label\">@L( \"\u042f\u0417\u042b\u041a:\", \"LANGUAGE:\" )</label>\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-item lang-row\">\r\n                        <label class=\"setting-label\">@L( \"\u0421\u041f\u0410\u0412\u041d \u0411\u041e\u0422\u041e\u0412:\", \"SPAWN BOTS:\" )</label>\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-item lang-row\">\r\n                        <label class=\"setting-label\">@L( \"\u041c\u0423\u0417\u042b\u041a\u0410:\", \"MUSIC:\" )</label>\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                    <label class=\"setting-item\">@L( \"\u0427\u0423\u0412\u0421\u0422\u0412\u0418\u0422\u0415\u041b\u042c\u041d\u041e\u0421\u0422\u042c: 1.0\", \"SENSITIVITY: 1.0\" )</label>\r\n                </div>\r\n                <button class=\"menu-btn\" onclick=\"@CloseSettings\">@L( \"\u041d\u0410\u0417\u0410\u0414\", \"BACK\" )</button>\r\n            </div>\r\n        </div>\r\n    }\r\n\r\n</root>\r\n\r\n@code {\r\n    [Property] public PushAbility PushAbility { get; set; }\r\n\r\n    public bool IsMenuOpen { get; set; } = true;\r\n    public bool IsSettingsOpen { get; set; } = false;\r\n    public bool IsPropMenuOpen { get; set; } = false;\r\n\r\n    public static string CurrentLanguage { get; set; } = \"EN\";\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 )\r\n    {\r\n        return CurrentLanguage == \"RU\" ? ru : en;\r\n    }\r\n\r\n    private bool IsReady => PushAbility?.IsReady ?? false;\r\n\r\n    protected override void OnEnabled()\r\n    {\r\n        base.OnEnabled();\r\n        \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    }\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>()\r\n                .FirstOrDefault( x => !x.IsProxy );\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\" ) )\r\n        {\r\n            if ( !IsMenuOpen && !IsSettingsOpen && !IsChatOpen )\r\n            {\r\n                TogglePropMenu();\r\n            }\r\n        }\r\n\r\n        if ( Input.Pressed( \"chat\" ) || Input.Pressed( \"enter\" ) )\r\n        {\r\n            if ( !IsMenuOpen && !IsSettingsOpen && !IsPropMenuOpen )\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 || IsPropMenuOpen;\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 TogglePropMenu()\r\n    {\r\n        IsPropMenuOpen = !IsPropMenuOpen;\r\n        ApplyMouseState();\r\n        StateHasChanged();\r\n    }\r\n\r\n    private void ClosePropMenu()\r\n    {\r\n        IsPropMenuOpen = 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        ClosePropMenu();\r\n    }\r\n\r\n    private void ToggleMenu()\r\n    {\r\n        if ( IsSettingsOpen ) CloseSettings();\r\n        else if ( IsPropMenuOpen ) ClosePropMenu();\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        IsPropMenuOpen = 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        IsPropMenuOpen = false;\r\n        ApplyMouseState();\r\n    }\r\n\r\n    private void OpenSettings()\r\n    {\r\n        IsMenuOpen = false;\r\n        IsSettingsOpen = true;\r\n        IsPropMenuOpen = 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        IsPropMenuOpen = false;\r\n        ApplyMouseState();\r\n    }\r\n\r\n    private void QuitGame()\r\n    {\r\n        Game.Close();\r\n    }\r\n\r\n    private int GetScore()\r\n    {\r\n        return ScoreManager.Instance?.Score ?? 0;\r\n    }\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()\r\n    {\r\n        return SaveManager.Current?.HighScore ?? 0;\r\n    }\r\n\r\n    private void OpenChat()\r\n    {\r\n        IsChatOpen = true;\r\n        IsPropMenuOpen = 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 )\r\n    {\r\n        return 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\r\n    private string GetWeatherIcon( WeatherType type )\r\n    {\r\n        return 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\r\n    private string GetWeatherClass( WeatherType type )\r\n    {\r\n        return 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\r\n    private int GetDeaths()\r\n    {\r\n        return ScoreManager.Instance?.Deaths ?? 0;\r\n    }\r\n\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( 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( IsPropMenuOpen );\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        return hash.ToHashCode();\r\n    }\r\n}"
        }
    ]
}