combat/slomo/TimeManager.cs
using System;
using Sandbox;

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

    [Property, Group( "1. Kick Armé" )]
    public TimeImpactProfile KickWindupProfile { get; set; }

    [Property, Group( "2. Jet d'Arme Connecté" )]
    public TimeImpactProfile WeaponThrowHitProfile { get; set; }

    [Property, Group( "3. Knockdown Infligé" )]
    public TimeImpactProfile KnockdownProfile { get; set; }

    [Property, Group( "4. Coup Fatal (Kill)" )]
    public TimeImpactProfile KillProfile { get; set; }

    private TimeImpactProfile _activeProfile;
    private RealTimeSince _realTimeSinceImpact = 100f;
    private float _currentTargetTimeScale = 1.0f;
    private float _currentDuration = 0f;
    private float _currentRecoverySpeed = 8f;
    private int _currentPriority = 0;
    private bool _isImpactActive = false;

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

    protected override void OnStart()
    {
        CombatEvents.OnPlayerKickWindup += HandleKickWindup;
        CombatEvents.OnPlayerKickReleased += HandleKickReleased;
        CombatEvents.OnPlayerWeaponThrowHit += HandleWeaponThrowHit;
        CombatEvents.OnPlayerKnockdownEnemy += HandleKnockdownEnemy;
        CombatEvents.OnPlayerKillEnemy += HandleKillEnemy;
    }

    protected override void OnDestroy()
    {
        CombatEvents.OnPlayerKickWindup -= HandleKickWindup;
        CombatEvents.OnPlayerKickReleased -= HandleKickReleased;
        CombatEvents.OnPlayerWeaponThrowHit -= HandleWeaponThrowHit;
        CombatEvents.OnPlayerKnockdownEnemy -= HandleKnockdownEnemy;
        CombatEvents.OnPlayerKillEnemy -= HandleKillEnemy;

        if ( Scene != null && Scene.IsValid )
            Scene.TimeScale = 1.0f;
    }

    private void HandleKickWindup()
    {
        var profile = KickWindupProfile ?? new TimeImpactProfile
        {
            TargetTimeScale = 0.25f,
            RealDuration = 0.35f,
            RecoverySpeed = 14f,
            Priority = 1
        };
        ApplySlowdown( profile );
    }

    private void HandleKickReleased()
    {
        if ( _isImpactActive && _currentPriority <= 1 )
        {
            Scene.TimeScale = 1.0f;
            _isImpactActive = false;
            _currentPriority = 0;
            _activeProfile = null;
        }
    }

    private void HandleWeaponThrowHit( Weapon weapon, Fighter victim, DamageContext ctx, bool willImpale )
    {
        var profile = WeaponThrowHitProfile ?? new TimeImpactProfile
        {
            TargetTimeScale = willImpale ? 0.08f : 0.12f,
            RealDuration = willImpale ? 0.28f : 0.22f,
            RecoverySpeed = 10f,
            Priority = 2
        };
        ApplySlowdown( profile );
    }

    private void HandleKnockdownEnemy( Fighter victim, DamageContext ctx )
    {
        var profile = KnockdownProfile ?? new TimeImpactProfile
        {
            TargetTimeScale = 0.10f,
            RealDuration = 0.28f,
            RecoverySpeed = 8f,
            Priority = 3
        };
        ApplySlowdown( profile );
    }

    private void HandleKillEnemy( Fighter victim, DamageContext ctx, bool victimWasUnarmed )
    {
        var profile = KillProfile ?? new TimeImpactProfile
        {
            TargetTimeScale = 0.05f,
            RealDuration = 0.38f,
            RecoverySpeed = 6f,
            Priority = 4
        };
        ApplySlowdown( profile );
    }

    public void ApplySlowdown( TimeImpactProfile profile )
    {
        if ( profile == null ) return;

        if ( _isImpactActive && _currentPriority > profile.Priority && _realTimeSinceImpact < _currentDuration )
            return;

        _activeProfile = profile;
        _currentTargetTimeScale = profile.TargetTimeScale;
        _currentDuration = profile.RealDuration;
        _currentRecoverySpeed = profile.RecoverySpeed;
        _currentPriority = profile.Priority;
        _realTimeSinceImpact = 0;
        _isImpactActive = true;

        Scene.TimeScale = _currentTargetTimeScale;
    }

    protected override void OnUpdate()
    {
        if ( !_isImpactActive ) return;

        if ( _realTimeSinceImpact >= _currentDuration )
        {
            Scene.TimeScale = MathX.Lerp( Scene.TimeScale, 1.0f, _currentRecoverySpeed * RealTime.Delta );

            if ( MathF.Abs( Scene.TimeScale - 1.0f ) < 0.02f )
            {
                Scene.TimeScale = 1.0f;
                _isImpactActive = false;
                _currentPriority = 0;
                _activeProfile = null;
            }
        }
        else
        {
            Scene.TimeScale = _currentTargetTimeScale;
        }
    }
}