TargetBurstReview.cs
using Sandbox;
namespace PaintballBaddies;

/// <summary>Opt-in repeated physical hits against the actual practice plate.</summary>
public sealed class TargetBurstReview : Component
{
    private PaintballMarker weapon;
    private PaintballTarget target;
    private PlayerController player;
    private float elapsed,lastShot=-1,minInterval=float.MaxValue;
    private int fired,samples,initialHits;
    private bool stable=true,done;
    private Vector3 position;
    private Rotation rotation;
    private Color tint;
    protected override void OnUpdate()
    {
        elapsed+=Time.Delta;
        if(!weapon.IsValid() && elapsed>1)
        {
            player=Scene.GetAllComponents<PlayerController>().First();
            weapon=player.Components.Get<PaintballMarker>();weapon.AcceptInput=false;
            player.UseInputControls=false;player.WishVelocity=Vector3.Zero;
            target=Scene.GetAllComponents<PaintballTarget>().First(x=>System.MathF.Abs(x.WorldPosition.y)<1);
            initialHits=target.HitsReceived;
            position=target.WorldPosition;rotation=target.WorldRotation;tint=target.Components.Get<ModelRenderer>().Tint;
        }
        if(!target.IsValid() || done)return;
        samples++;
        stable &= (target.WorldPosition-position).Length<.001f && target.WorldRotation==rotation && target.Components.Get<ModelRenderer>().Tint==tint;
        if(elapsed<4 && weapon.FireAt(position))
        {
            if(lastShot>=0)minInterval=System.MathF.Min(minInterval,elapsed-lastShot);
            lastShot=elapsed;fired++;
        }
        if(elapsed>5)
        {
            var impacts=PaintImpactSystem.Find(Scene);
            int hits=target.HitsReceived-initialHits;
            Log.Info("TARGET_BURST "+Json.Serialize(new{fired,hits,minInterval,samples,stable,decals=impacts.ActiveCount,droplets=impacts.ActiveDroplets,
                passed=fired>=8 && hits==fired && minInterval>=PaintballFlight.ShotInterval-.002f && stable && impacts.ActiveCount>=fired && impacts.ActiveDroplets==0}));
            done=true;
        }
    }
    protected override void OnDestroy()
    {
        if(weapon.IsValid())weapon.AcceptInput=true;
        if(player.IsValid())player.UseInputControls=true;
    }
}