TimeFreezeAbility.cs

A game component that implements a time-freeze ability. When activated it finds nearby game objects within a sphere, stores their Rigidbody and CharacterController components, zeros their velocities each update while active, tints ModelRenderer components, plays a particle and sound, and then restores state when the duration ends and starts a cooldown.

NetworkingFile Access
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!" );
    }
}