Console command tool that reports how headshot damage is calculated for the currently held weapon. It finds the equipped Weapon, a live zombie Health component if present, the owning NZPlayer and perks, then prints the weapon HeadMultiplier, zombie HeadshotDamageScale, perk multiplier and computed base and total headshot multipliers and damage.
using Sandbox;
using SWB.Base;
using System.Linq;
namespace NZombies;
/// <summary>
/// `nz_headshot` — the whole headshot chain for the gun in your hands, one factor per
/// line.
///
/// ⛔ THIS EXISTS BECAUSE A MERGED NUMBER COST A WRONG ANSWER. The weapon card printed
/// a sniper as one figure, and reasoning about it from the wrong copy of the project
/// produced a confident "the base is already 5x, the extra is Death Perception" when
/// the prefabs were in fact authored at 3.0 and 3.0 x 2.5 = 7.5 with no perk at all.
/// A product with its factors hidden invites exactly that; print the factors.
///
/// ⚠️ EVERYTHING IS READ LIVE. `HeadshotDamageScale` is a Property on Health and
/// `HeadMultiplier` is authored per prefab; a hardcoded copy here would agree with
/// this file forever and disagree with the game the moment either changed — precisely
/// the failure this is meant to catch.
/// </summary>
public static class HeadshotReport
{
[ConCmd( "nz_headshot" )]
public static void Report()
{
var scene = Game.ActiveScene;
if ( scene is null ) return;
// ⚠️ `Owner is not null` is the "actually held" test — same one the weapon card
// uses. A wallbuy's display gun and anything lying in the world have no owner
// and must not answer here.
var w = scene.GetAllComponents<Weapon>()
.FirstOrDefault( x => x.IsValid() && x.GameObject.Enabled && x.Owner is not null );
if ( !w.IsValid() || w.Primary is null )
{
Log.Info( "[nz-head] nothing held — equip a weapon first" );
return;
}
// ⚠️ Off a live zombie, not a literal. If none is up we say so rather than
// quietly substituting 2.5 and reporting a number nothing measured.
var hp = scene.GetAllComponents<Health>().FirstOrDefault( h => h.IsValid()
&& h.Components.Get<ZombieAI>( FindMode.EverythingInSelfAndAncestors ).IsValid() );
var zombie = hp.IsValid() ? hp.HeadshotDamageScale : 2.5f;
var zsrc = hp.IsValid() ? $"live '{hp.GameObject.Name}'" : "DEFAULT — no zombie in the scene";
// ⛔ THE PERK COMES OFF THE GUN'S OWNER, not the first player in the scene.
var nz = w.Owner?.GameObject?.Components
.Get<NZPlayer>( FindMode.EverythingInSelfAndAncestors );
var perk = PerkEffects.HeadshotScaleMultiplier( nz );
var owned = nz.IsValid() && nz.Perks.Count > 0 ? string.Join( ", ", nz.Perks ) : "none";
var gun = w.Primary.HeadMultiplier;
var body = w.Primary.Damage * w.Primary.DamageMultiplier;
Log.Info( $"[nz-head] {w.ClassName} — perks: {owned}" );
Log.Info( $"[nz-head] weapon HeadMultiplier x{gun:0.##} (authored on the prefab)" );
Log.Info( $"[nz-head] zombie HeadshotDamageScale x{zombie:0.##} ({zsrc})" );
Log.Info( $"[nz-head] Death Perception x{perk:0.##}" );
Log.Info( $"[nz-head] -- base, no perks x{gun * zombie:0.###}" );
Log.Info( $"[nz-head] -- total as equipped x{gun * zombie * perk:0.###}" );
Log.Info( $"[nz-head] body {body:0.#} -> head {body * gun * zombie * perk:0.#}" );
}
}