OverdriveHeadBurst.cs
using Sandbox;
using System;
namespace PaintballBaddies;

// Pieces cut from Terry's actual head, with preserved skin UVs and filling.
// Native particles collide visually without becoming networked gameplay bodies.
public sealed class OverdriveHeadBurst : Component
{
    public float Age {get;private set;}
    public int Shards=>Components.Get<ParticleEffect>()?.ParticleCount ?? 0;
    private Vector3 impulse;
    private int seed;
    private int variant;
    public static void Spawn(Scene scene,Vector3 point,Vector3 direction,int variation,int headVariant=0)
    {
        foreach(var old in scene.GetAllComponents<OverdriveHeadBurst>().OrderByDescending(x=>x.Age).SkipLast(3).ToArray())old.GameObject.Destroy();
        var go=new GameObject(false,"Terry sausage burst"){WorldPosition=point,NetworkMode=NetworkMode.Never};
        go.Tags.Add("paintball_debris");
        var burst=go.Components.Create<OverdriveHeadBurst>();burst.impulse=direction;burst.seed=variation;burst.variant=headVariant.Clamp(0,2);go.Enabled=true;
        OverdriveAudio.Play("burst",point);
    }
    protected override void OnStart()
    {
        var particles=Components.Create<ParticleEffect>();
        particles.MaxParticles=14;particles.Lifetime=1.35f;particles.LocalSpace=0;
        particles.ApplyRotation=false;particles.ApplyShape=false;particles.ApplyAlpha=false;particles.ApplyColor=false;
        particles.Force=true;particles.ForceDirection=Vector3.Down*650;particles.ForceScale=1;
        particles.ForceSpace=ParticleEffect.SimulationSpace.World;particles.Damping=.35f;
        particles.Collision=true;particles.CollisionRadius=.7f;particles.Bounce=.28f;particles.Friction=.7f;particles.PushStrength=0;
        particles.CollisionIgnore=new TagSet();particles.CollisionIgnore.Add("paintball_actor");particles.CollisionIgnore.Add("paintball_debris");
        var render=Components.Create<ParticleModelRenderer>();
        render.Choices=Enumerable.Range(0,3).Select(i=>new ParticleModelRenderer.ModelEntry{Model=Model.Load($"models/sausage/grumpy/head_piece_{variant}_{i}.vmdl")}).ToList();
        render.Scale=1;render.CastShadows=false;render.RotateWithGameObject=false;
        particles.OnStep=(p,dt)=>{p.Angles+=new Angles(170,260,130)*dt;p.Alpha=((1.35f-p.Age)/.35f).Clamp(0,1);};
        var random=new Random(seed);
        for(int i=0;i<14;i++)
        {
            var direction=new Vector3(MathF.Cos(i*2.399f+seed),MathF.Sin(i*2.399f+seed),-.3f+(float)random.NextDouble()*1.3f).Normal;
            var p=particles.Emit(WorldPosition+direction*2,0);float size=.055f+(float)random.NextDouble()*.13f;p.Size=new Vector3(size);
            p.Angles=new Angles(i*27,i*47,i*19);p.StartAngles=p.Angles;p.Color=Color.White;
            // Most material exits along the shot, smaller crumbs spread wider.
            p.Velocity=direction*(65+(float)random.NextDouble()*130)+impulse.Normal*170+Vector3.Up*50;
        }
        for(int i=0;i<3;i++)SausageHeavyChunk.Spawn(WorldPosition,impulse.Normal,seed+i,i,variant);
    }
    protected override void OnUpdate(){Age+=Time.Delta;if(Age>2.4f)GameObject.Destroy();}
}

public sealed class SausageHeavyChunk : Component,Component.ICollisionListener
{
    private float age;
    private bool landed;
    public static int Landings {get;private set;}
    public static void Spawn(Vector3 point,Vector3 impulse,int seed,int size,int variant=0)
    {
        var scene=Game.ActiveScene;
        foreach(var old in scene.GetAllComponents<SausageHeavyChunk>().OrderByDescending(c=>c.age).SkipLast(17).ToArray())old.GameObject.Destroy();
        var go=new GameObject(false,"Sausage chunk"){NetworkMode=NetworkMode.Never,WorldPosition=point};go.Tags.Add("paintball_debris");
        var mesh=new GameObject(go,true,"Textured Terry fragment"){NetworkMode=NetworkMode.Never,LocalScale=new Vector3(size==0 ? .65f : size==1 ? .85f : .55f)};
        mesh.Components.Create<ModelRenderer>().Model=Model.Load($"models/sausage/grumpy/head_piece_{variant}_{size}.vmdl");
        var collider=go.Components.Create<SphereCollider>();collider.Radius=1.2f+size*.65f;collider.Static=false;
        var body=go.Components.Create<Rigidbody>();body.MassOverride=.5f+size*.8f;body.LinearDamping=.2f;
        go.Components.Create<SausageHeavyChunk>();go.Enabled=true;
        body.Velocity=Rotation.FromYaw(seed*137).Forward*(75-size*12)+impulse*175+Vector3.Up*(100+size*20);
        body.AngularVelocity=new Vector3(4+size,8-size,seed%6+2);
    }
    void Component.ICollisionListener.OnCollisionStart(Collision collision)
    {
        if(landed || age<.12f)return;landed=true;Landings++;
        var sound=SausageAudio.Play("landing",WorldPosition);if(sound is not null){sound.Volume=.42f;sound.Pitch=.82f;}
    }
    protected override void OnUpdate(){age+=Time.Delta;if(age>3.8f)GameObject.Destroy();}
}