Diagnostics/PenProbe.cs

A static diagnostics utility that samples and reports the distance between HitPosition and EndPosition for penetrated bodies to determine whether that distance represents a trace radius or actual thickness. It accumulates count, min/max/mean, histogram bins, and exposes a console command nz_pen_probe that prints or resets the collected statistics.

NetworkingFile Access
using Sandbox;
using System;

namespace NZombies;

/// <summary>
/// PEN/PROBE — what the penetration budget actually charges per body.
///
/// ⛔ THIS EXISTS BECAUSE TWO READINGS OF ONE LINE DISAGREE, AND THE ANSWER CHANGES GAMEPLAY.
/// `HitScan` spends the budget as:
///
///     penBudget -= bulletTr.EndPosition.Distance( bulletTr.HitPosition ) + 1.0f;
///
/// `DtapAugments.BodyDepth` says that distance is "the THICKNESS ACTUALLY CROSSED", about ten
/// units for a torso, and calibrates its "+8 zombies" promise on 11 units per body. Reading the
/// trace API instead suggests `HitPosition` is the contact point on the surface while `EndPosition`
/// is where the swept shape's centre came to rest — which would make the distance roughly the
/// RADIUS, about 2, not a thickness at all.
///
/// ⛔ AND THE BULLET IS NOW A RAY, WHICH MAKES THE DIFFERENCE URGENT. At radius 0 those two points
/// should coincide, so the distance collapses toward zero and the budget is charged only the 1.0
/// surcharge per body. If the radius reading is the right one, pierce depth just went from about
/// three bodies to about nine — a large balance change nobody asked for, arriving silently.
///
/// ⚠️ MEASURES, CHANGES NOTHING. Every value is recorded and reported; none is fed back.
///
/// ⚠️ ALLOCATION-FREE AND ALWAYS ON. Running count/min/max/sum plus a coarse histogram, all in
/// fields — this sits on the hottest line in the damage path, so it must not become the thing worth
/// profiling. Compare CpuScope, which measured its own overhead at 179ns and was still fine.
/// </summary>
public static class PenProbe
{
	static int _n;
	static float _min = float.MaxValue;
	static float _max;
	static double _sum;

	/// <summary>Buckets: &lt;0.5, &lt;1.5, &lt;3, &lt;6, &lt;12, &lt;25, 25+.</summary>
	static readonly int[] _bins = new int[7];

	static readonly float[] Edges = { 0.5f, 1.5f, 3f, 6f, 12f, 25f };
	static readonly string[] Labels =
	{
		"< 0.5   (a ray — the two points coincide)",
		"< 1.5",
		"< 3     (about the old sphere radius)",
		"< 6",
		"< 12    (about one torso — the BodyDepth reading)",
		"< 25",
		"25+",
	};

	/// <summary>
	/// Record one body's charge against the budget.
	///
	/// ⚠️ CALLED WITH THE RAW DISTANCE, NOT THE CHARGE. The `+ 1.0f` surcharge is added by the
	/// caller and is not in question; what is in question is the term before it.
	/// </summary>
	public static void Sample( float distance )
	{
		_n++;
		_sum += distance;
		if ( distance < _min ) _min = distance;
		if ( distance > _max ) _max = distance;

		for ( int i = 0; i < Edges.Length; i++ )
		{
			if ( distance < Edges[i] ) { _bins[i]++; return; }
		}

		_bins[6]++;
	}

	/// <summary>`nz_pen_probe [reset]` — what the budget has been charging per body.</summary>
	[ConCmd( "nz_pen_probe" )]
	public static void Report( string arg = "" )
	{
		if ( arg.Equals( "reset", StringComparison.OrdinalIgnoreCase ) )
		{
			_n = 0; _min = float.MaxValue; _max = 0f; _sum = 0;
			Array.Clear( _bins, 0, _bins.Length );
			Log.Info( "[nz-pen] cleared" );
			return;
		}

		if ( _n == 0 )
		{
			Log.Info( "[nz-pen] no samples yet — shoot something that penetrates, then run this" );
			return;
		}

		var mean = (float)(_sum / _n);
		Log.Info( $"[nz-pen] EndPosition.Distance( HitPosition ) over {_n} penetrated bodies:" );
		Log.Info( $"[nz-pen]   min {_min:0.###}   mean {mean:0.###}   max {_max:0.###}" );

		for ( int i = 0; i < _bins.Length; i++ )
		{
			if ( _bins[i] == 0 ) continue;
			Log.Info( $"[nz-pen]   {Labels[i],-45} {_bins[i],6}  ({100f * _bins[i] / _n:0.#}%)" );
		}

		// ⚠️ THE VERDICT IS STATED, because the whole point is to settle which of two readings is
		// right — and a table of numbers invites the same argument all over again next week.
		Log.Info( mean < 0.5f
			? "[nz-pen] VERDICT: near zero — the budget charges ~1.0 per body (the surcharge alone),"
				+ " so PenetrationDepth is a BODY COUNTER, not a depth. DtapAugments.BodyDepth = 11"
				+ " is calibrated against something this is not."
			: mean < 4f
				? "[nz-pen] VERDICT: about the trace radius — the distance is a shape artefact, not a"
					+ " thickness. Pierce depth therefore CHANGES with nz_trace_radius, which is a"
					+ " coupling nobody intended."
				: "[nz-pen] VERDICT: large enough to be a real thickness — DtapAugments.BodyDepth's"
					+ " reading holds and the budget behaves as documented." );

		Log.Info( $"[nz-pen]   (trace radius is currently {SWB.Base.Weapon.TraceRadius:0.##};"
			+ " bodies-per-bullet is n_dmg_hit / n_shot_bullet in the perf log)" );
	}
}