Zombies/TeslaBoss.cs

A Boss component (TeslaBoss) and a TeslaCrackle visual effect. TeslaBoss implements a Tesla-themed boss zombie: spawn effects, sprint/attack selection, a timed sonic scream that dazes players, stuns and charges nearby walker zombies to a top speed, and host/client handling for visual/audio/network events. TeslaCrackle is a per-machine non-networked component that shows sparks and a chest light on a charged walker for a short time.

NetworkingFile Access
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;

namespace NZombies;

/// <summary>
/// The Tesla Zombie — a zombie in an HEV suit, ported 2026-10-07 from GMod nZombies' `nz_zombie_boss_hev` (Wavy's), the third
/// batch. Spec: `Sbox nzombies/Docs/boss_specs/tesla.md`. "Tesla" is the look — a strike of lightning, a blue light, electric
/// sounds; what he does is sprint, swing and scream.
///
///   • HE ARRIVES IN A STRIKE OF LIGHTNING (upstream's `driese_tp_arrival_phase2`): bolts out of the sky (the Director's beam), a
///     ring and sparks, the thunder (upstream's four layers) and his alert — and climbs out of the ground (`nz_spawn_ground_jumpout`,
///     1.33 s) UNTOUCHABLE until he is out (upstream's `SetInvulnerable` over it, and the base's 1 s of spawn protection).
///   • HE SPRINTS, one of eight sprints per boss (248-262 u/s; the variant's one tier), a deep blue light on his chest (upstream's
///     `DynamicLight` on `j_spineupper`). ⚠️ A spark off the suit now and then is added (`BossGlow`: Avogadro's sparks).
///   • THE SONIC SCREAM, his one special: at a player in his sight within <see cref="ScreamRange"/>, on a coin flip every
///     <see cref="ThinkEvery"/> s (upstream's `math.random(2)` on each pass of its AI), no sooner than 3 s after he arrives and
///     <see cref="ScreamCooldown"/> s after the last one ENDED — one of three `nz_sonic_attack` clips (2.5-3.1 s) facing them, and
///     at its `sonic_scream` frame (0.57 or 0.93 s):
///       – a player he can see within <see cref="ScreamRadius"/>: dazed <see cref="Daze"/> s (upstream's `NZSonicBlind(2)` — in
///         GMod nothing reads that status; here it is the Shrieker's daze, as for Avogadro's bolt and Zaballa's mines);
///       – ⚠️ EVERY WALKER WITHIN <see cref="BoostRadius"/> (300, feet to feet) CHARGED — at the top of the game's zombie speed for
///         the rest of its life (rating <see cref="SprintTo"/>, the round curve's cap: the super-sprint tier, 315-374 u/s), and a
///         crackle on it on every machine (<see cref="TeslaCrackle"/>) so you see who was charged. 2026-10-07, the user: *"the
///         tesla zombie's pulse should make all zombies in a 300u radius around it become at their fastest speed in game"*.
///         ⚠️ Upstream's `SetRunSpeed(250)` reached his 175 only — the same tier, a shorter reach. ⚠️ NO SPECIAL, NO BOSS (upstream's
///         `!IsMooSpecial`): a hound already runs its top tier from round 1 (`MinSpeedRating` 200), and the pest's and the
///         Shrieker's one clip would go to 1.6 x their own 1.5 x — ~485 u/s, past a fully augmented sprint. ⚠️ NONE STILL CLIMBING
///         OUT, and no crawler (none exist yet: `ZombieAI.IsCrawler` — a crawler has no sprint);
///       – those within <see cref="ScreamRadius"/> also stunned <see cref="StunMin"/>-<see cref="StunMax"/> s (upstream's
///         `PerformStun`). ⚠️ The walker has no `nz_sparky` electrocution loop: the `stun` status holds it, the crackle on it.
///     No damage. ⚠️ ITS BURST IS DRAWN AT HIS NECK: upstream attaches it to attachment 10, which this model lacks (it never showed
///     in GMod); `head_fx_tag` (`j_neck`) is where it was meant. ⚠️ He screams in place: the clips' 18-62 u of creep is root motion
///     the port strips.
///   • HIS SWING, a walker's 50 (the variant's): the sprint attacks, or the standing ones when his target is all but still (under
///     <see cref="StandSpeed"/> u/s) within <see cref="StandRange"/> — upstream's base `Attack`, here through `ZombieAI.AttackOverride`.
///   • HE TAKES FULL DAMAGE (no table upstream), and dies electrocuted (`nz_death_elec_1..4`): the heart monitor flatlines, the suit
///     shuts down, and the electricity fades out of him (Avogadro's crackle).
///   • LEFT OUT: the electric hum (dead code upstream — `EffectsAndSounds` never runs — and HL2's `electric_loop.wav` is in no
///     archive), and the plank handler that jammed windows (a boss here ignores barricades).
///
/// ⚠️ HOST AUTHORITY, EVERY MACHINE DRAWS: the bursts are `NZNet.BossBurstFx`, each daze `NZNet.BossDaze` (only that player's machine
/// acts), each stun a status (`StatusEffects.Apply` sends it on), each charge `NZNet.TeslaCharged` (the crackle everywhere, and a
/// watching machine's puppet raised to the same rating so its legs take the new gait — `FeelCharge`); his light and the beam he
/// arrives in are each machine's own.
/// ⛔ NO HIT OF HIS DOWNS ANYONE FROM FULL HEALTH (`BossBase.MaxHit`), Oberon's rule — and the scream deals none.
/// </summary>
public sealed class TeslaBoss : BossBase
{
	protected override string Tag => "[nz-tesla]";

	/// <summary>His eyes: the QC's `$eyeposition 0 0 70`.</summary>
	protected override float VoiceHeight => 70f;

	// ══ tuning ═══════════════════════════════════════════════════════════════
	//
	// ⛔ NULLABLE-BACKED, INSTRUCTIONS.md §1.

	static float? _range, _radius, _cooldown, _chance, _think, _daze, _shock;
	/// <summary>He screams at a player in his sight this near: 325 (upstream's `TargetInRange(325)`, origin to origin).</summary>
	public static float ScreamRange { get => _range ?? 325f; set => _range = value; }
	/// <summary>What the scream's daze and stun reach: 175 round him (upstream's `FindInSphere(GetPos(), 175)`).</summary>
	public static float ScreamRadius { get => _radius ?? 175f; set => _radius = value; }
	/// <summary>Seconds after a scream ENDS before the next may start: 9, upstream's.</summary>
	public static float ScreamCooldown { get => _cooldown ?? 9f; set => _cooldown = value; }
	/// <summary>His roll each think, once he may: 0.5 (upstream's `math.random(2) == 1`).</summary>
	public static float ScreamChance { get => _chance ?? 0.5f; set => _chance = value; }
	/// <summary>How often he rolls: 0.75 s (upstream asks once a pass of its AI — every 0.5-1 s — and after each swing).</summary>
	public static float ThinkEvery { get => _think ?? 0.75f; set => _think = value; }
	/// <summary>The daze on a player the scream reaches in his sight: 2 s (upstream's `NZSonicBlind(2)`).</summary>
	public static float Daze { get => _daze ?? 2f; set => _daze = value; }
	/// <summary>
	/// Seconds the scream also leaves them SHOCKED — no shooting (Avogadro's `PlayerShock`): 0, upstream's — his scream blinds, it
	/// does not shock. `nz_tesla_set shock 2` makes it Avogadro's too.
	/// </summary>
	public static float ScreamShock { get => _shock ?? 0f; set => _shock = value; }

	static float? _stunMin, _stunMax, _sprintTo, _standRange, _standSpeed;
	/// <summary>How long a walker the scream reaches is stunned: 1-2 s (upstream's `PerformStun(math.Rand(1, 2))`).</summary>
	public static float StunMin { get => _stunMin ?? 1f; set => _stunMin = value; }
	public static float StunMax { get => _stunMax ?? 2f; set => _stunMax = value; }
	/// <summary>
	/// The speed rating a charged walker is raised to, for good: by default <see cref="TopRating"/>, the top of the game (2026-10-07,
	/// the user's "fastest speed in game"; it was 250, upstream's `SetRunSpeed(250)` — the same super-sprint tier). A floor, never a
	/// slow-down (`ZombieAI.RaiseSpeedRating`). `nz_tesla_set sprint 0` puts the default back.
	/// </summary>
	public static float SprintTo { get => _sprintTo ?? TopRating; set => _sprintTo = value; }
	/// <summary>He plants for a standing swing at a target this near and this still: 65, and 3.9 u/s (upstream's `speed² &lt; 15`).</summary>
	public static float StandRange { get => _standRange ?? 65f; set => _standRange = value; }
	public static float StandSpeed { get => _standSpeed ?? 3.9f; set => _standSpeed = value; }

	static float? _boostRadius, _crackle;
	/// <summary>
	/// How far round him the scream CHARGES the walkers — top speed for good: 300, feet to feet, x his size (2026-10-07, the user:
	/// *"all zombies in a 300u radius around it"*; it was the scream's own 175).
	/// </summary>
	public static float BoostRadius { get => _boostRadius ?? 300f; set => _boostRadius = value; }
	/// <summary>How long the crackle on a charged walker lasts: 1 s (<see cref="TeslaCrackle"/>; 0, none).</summary>
	public static float CrackleSeconds { get => _crackle ?? 1f; set => _crackle = value; }

	/// <summary>
	/// THE TOP OF THE GAME'S ZOMBIE SPEED, as a rating: the round curve's own cap (`ZombieSettings.SpeedCap`, 300 — the most it ever
	/// hands a zombie, at its highest rounds), and never under the super-sprint tier's 155. ⚠️ Past 155 a walker moves no faster
	/// (`ZombieStats.SpeedForRound`: a rating only picks a tier, and super-sprint is the last), so this is the super-sprint gait:
	/// its clips' 197-234 u/s x `SuperSprintSpeedScale` 1.6 = 315-374 u/s, x the lobby's move speed — what the whole horde runs
	/// at from round 60 (`SpeedCapRound`). The 300 rather than 155 is the Director's speed buff's number too (upstream's 300).
	/// </summary>
	public static float TopRating => MathF.Max( WalkerAnimations.SuperSprintRating, ActiveConfig.Zombies?.SpeedCap ?? ZombieStats.SpeedCap );

	// his clips (30 fps) and their events — ⛔ `const`, never `static readonly` (INSTRUCTIONS.md §1)
	const string ClipScream1 = "nz_sonic_attack_01", ClipScream2 = "nz_sonic_attack_02", ClipScream3 = "nz_sonic_attack_03",
		ClipVault = "nz_barricade_sprint_1";
	const float Scream1Len = 94f / 30f, Scream2Len = 76f / 30f, Scream3Len = 80f / 30f, Scream12At = 17f / 30f, Scream3At = 28f / 30f;

	// the standing swings (upstream's `StandAttackSequences`); the sprint ones are the variant's
	const string StandL1 = "nz_iw7_cp_zom_stand_attack_l_01", StandL2 = "nz_iw7_cp_zom_stand_attack_l_02",
		StandR1 = "nz_iw7_cp_zom_stand_attack_r_01", StandR2 = "nz_iw7_cp_zom_stand_attack_r_02",
		Stand2h1 = "nz_zom_core_stand_attack_2h_01", Stand2h2 = "nz_zom_core_stand_attack_2h_02";

	/// <summary>His first scream no sooner than 3 s after he arrives (upstream's `Cooldown = CurTime() + 3`); 1 s of protection at least.</summary>
	const float ScreamFirst = 3f, SpawnProtection = 1f;
	/// <summary>Late enough in a swing to scream instead of swinging again: its hit has landed (10% in, `ZombieAI.AttackDamagePoint`).</summary>
	const float SwingDone = 0.5f;
	/// <summary>The strike's ring (upstream's shock rings start at ~190), and the most stunned-but-uncharged walkers that get a flash each.</summary>
	const float StrikeRadius = 190f;
	const int MaxFlashes = 16;

	// upstream's colours: his light (0, 25, 255), the strike's blue-white (177, 188, 255), the scream's (110, 120, 255)
	static Color SuitBlue => new( 0f, 0.1f, 1f );
	static Color StrikeWhite => new( 0.69f, 0.74f, 1f );
	static Color ScreamBlue => new( 0.43f, 0.47f, 1f );
	static Color SparkBlue => new( 0.6f, 0.8f, 1f );

	// ══ state ════════════════════════════════════════════════════════════════

	bool _screaming, _shielded;
	float _nextScream, _nextThink, _shieldUntil;
	GameObject _victim;
	List<string> _standing;

	// his target's ground speed, measured frame to frame (a client's body arrives as positions only — Avogadro's note); 999 until
	// measured, so a target he has only just picked never reads as standing still
	GameObject _tracked;
	Vector3 _trackedAt;
	float _targetSpeed = 999f;

	enum Beat { Scream }

	public bool Screaming => _screaming;

	protected override void BossStart()
	{
		// ⚠️ EVERY MACHINE: his light (upstream's `DynamicLight`, (0, 25, 255) on `j_spineupper`, redrawn every frame; `noworld`, so
		// it lit models only — kept small here), and a spark off the suit now and then
		var glow = Components.Create<BossGlow>();
		glow.Bone = "j_spineupper";
		glow.Colour = SuitBlue;
		glow.Radius = 90f;
		glow.Brightness = 3f;
		glow.SparkEvery = 0.3f;
		glow.SparkReach = 14f;
		glow.SparkColour = SparkBlue;

		// the strike he arrives in: bolts out of the sky onto where he stands (the Director's summoning beam), each machine its own —
		// no message draws one; the ring, the sparks and the thunder are the host's (`HostStart`)
		if ( Ai.IsValid() && Ai.State != ZombieState.Dead ) DirectorFx.Beam( WorldPosition );
	}

	protected override void HostStart()
	{
		Ai.MaxHitDamage = MaxHit;

		// ⛔ NONE OF THE WALKER'S CLIMB CLIPS ON HIS MODEL (Oberon's note): his own vault (upstream's `JumpSequences`)
		Ai.ClimbClipOverride = ClipVault;

		_standing = new List<string> { StandL1, StandL2, StandR1, StandR2, Stand2h1, Stand2h2 };
		_nextScream = Time.Now + ScreamFirst;

		// untouchable while he climbs out (upstream's `SetInvulnerable` over the jump-out, and the base's spawn protection)
		_shielded = true;
		_shieldUntil = Time.Now + SpawnProtection;
		if ( Hp.IsValid() ) Hp.Invulnerable = true;

		// upstream's `driese_tp_arrival_phase2`: its ring and sparks on every machine; its four sounds, layered, and his alert
		var at = WorldPosition;
		BossFx.BurstShared( at + Vector3.Up * 16f, StrikeRadius * Size, StrikeWhite, 14 );
		NZSound.PlayShared( "nz.tesla.thunder", at + Vector3.Up * 60f );
		NZSound.PlayShared( "nz.tesla.flash", at + Vector3.Up * 60f );
		NZSound.PlayShared( "nz.tesla.snap", at + Vector3.Up * 60f );
		NZSound.PlayShared( "nz.tesla.impact", at );
		NZSound.PlayShared( "nz.tesla.alert", Voice );
		NZNet.ShakeAt( at, 0.35f, 900f );

		Say( $"spawned — {Hp?.Max ?? 0f:0} hp · screams at a player in sight within {ScreamRange:0} ({ScreamChance * 100f:0}% every"
			+ $" {ThinkEvery:0.##}s, {ScreamCooldown:0}s after one) · charges the walkers within {BoostRadius:0} to rating {SprintTo:0}"
			+ $" · within {ScreamRadius:0}: daze {Daze:0.#}s, walkers stunned {StunMin:0.#}-{StunMax:0.#}s · hitboxes {Ai.HitboxCount}" );
	}

	protected override void HostFrame()
	{
		Ai.MaxHitDamage = MaxHit;
		TickShield();
		if ( Ai.State == ZombieState.Spawning ) return;

		TrackTarget();

		if ( _screaming )
		{
			// facing his target for the whole scream, upstream's `FaceEnemy`
			if ( Ai.State != ZombieState.Special ) _screaming = false;
			else if ( _victim.IsValid() ) Face( _victim.WorldPosition );
			return;
		}

		PickAttackSet();
		TickScream();
	}

	protected override void OnBeat( int beat )
	{
		if ( beat == (int)Beat.Scream && _screaming && Ai.State == ZombieState.Special ) SonicScream();
	}

	protected override void Died( bool host )
	{
		_screaming = false;

		// ⚠️ EVERY MACHINE, its own copy: the heart monitor flatlining and the suit shutting down beside his death cry (upstream's),
		// and the electricity going out of him over his electrocuted fall — Avogadro's crackle, fading over 3.5 s (upstream's light
		// stays on until he turns to a ragdoll, 2.9-3.3 s in)
		NZSound.Play( "nz.tesla.flatline", Voice );
		NZSound.Play( "nz.tesla.shutdown", Voice );
		Components.GetOrCreate<AvogadroCrackle>().Fading = true;

		if ( host ) Say( "killed" );
	}

	// ══ the host ═════════════════════════════════════════════════════════════

	/// <summary>Touchable again once he is out of the ground and the spawn protection is over. THE HOST.</summary>
	void TickShield()
	{
		if ( !_shielded || Ai.State == ZombieState.Spawning || Time.Now < _shieldUntil ) return;
		_shielded = false;
		if ( Hp.IsValid() ) Hp.Invulnerable = false;
	}

	/// <summary>His target's ground speed, frame to frame, smoothed — what decides a standing swing. THE HOST.</summary>
	void TrackTarget()
	{
		var t = Ai.Target;
		if ( !t.IsValid() ) { _tracked = null; _targetSpeed = 999f; return; }

		var at = t.WorldPosition;
		if ( t != _tracked )
		{
			_tracked = t;
			_trackedAt = at;
			_targetSpeed = 999f;
			return;
		}

		var dt = MathF.Max( 0.001f, Time.Delta );
		var v = (at - _trackedAt).WithZ( 0 ).Length / dt;
		_trackedAt = at;
		if ( v > 1200f ) return;   // a teleport or a respawn, not a run
		_targetSpeed += (v - _targetSpeed) * (1f - MathF.Exp( -10f * dt ));
	}

	/// <summary>
	/// Which swings his next attack draws from — upstream's base `Attack` (M L4586): the standing set at a target all but still
	/// within <see cref="StandRange"/>, else the variant's sprint attacks. The AI reads it as each swing starts. THE HOST.
	/// </summary>
	void PickAttackSet()
	{
		var t = Ai.Target;
		var stand = t.IsValid() && _targetSpeed < StandSpeed && WorldPosition.Distance( t.WorldPosition ) <= StandRange * Size;
		Ai.AttackOverride = stand ? _standing : null;
	}

	/// <summary>Free to scream: walking at his target, or late in a swing — upstream asks after each swing; here its hit lands 10% in.</summary>
	bool Free => Ai.State == ZombieState.Chasing
		|| (Ai.State == ZombieState.Attacking && Body.IsValid() && Body.Sequence.TimeNormalized >= SwingDone);

	/// <summary>His roll, every <see cref="ThinkEvery"/> s once he may: a player in his sight within <see cref="ScreamRange"/>. THE HOST.</summary>
	void TickScream()
	{
		var now = Time.Now;
		if ( now < _nextScream || now < _nextThink || !Free ) return;
		_nextThink = now + ThinkEvery;

		var p = TargetPlayer;
		if ( !IsUp( p ) || WorldPosition.Distance( p.WorldPosition ) > ScreamRange * Size || !CanSee( p.GameObject, 64f ) ) return;
		if ( Game.Random.Float( 0f, 1f ) >= ScreamChance ) return;
		Scream( p );
	}

	/// <summary>One of his three screams, facing <paramref name="p"/> (none: straight ahead). THE HOST.</summary>
	bool Scream( NZPlayer p )
	{
		var pick = Game.Random.Int( 0, 2 );
		var clip = pick == 0 ? ClipScream1 : pick == 1 ? ClipScream2 : ClipScream3;
		var len = pick == 0 ? Scream1Len : pick == 1 ? Scream2Len : Scream3Len;

		if ( p.IsValid() ) Face( p.WorldPosition );
		if ( !PlayShared( clip, len ) ) return false;

		_screaming = true;
		_victim = p.IsValid() ? p.GameObject : null;
		// ⚠️ THE COOLDOWN RUNS FROM THE END OF THE CLIP, upstream's (`Cooldown = CurTime() + 9` after `PlaySequenceAndMove`)
		_nextScream = Time.Now + len + ScreamCooldown;
		Queue( pick == 2 ? Scream3At : Scream12At, (int)Beat.Scream );
		return true;
	}

	/// <summary>
	/// `sonic_scream`: his cry, a burst at his neck with its ring at <see cref="ScreamRadius"/>, the players in it he can see dazed;
	/// every walker within <see cref="BoostRadius"/> CHARGED — at the top of the game's speed for good, a crackle on it — and those
	/// within <see cref="ScreamRadius"/> also stunned. No damage. THE HOST.
	/// </summary>
	void SonicScream()
	{
		var neck = Body.IsValid() && Body.TryGetBoneTransform( "j_neck", out var bone ) ? bone.Position : Voice;
		var radius = ScreamRadius * Size;

		NZSound.PlayShared( "nz.tesla.alert", Voice );
		NZSound.PlayShared( "nz.tesla.scream", Voice );
		// ⚠️ HL2's `ambient/energy/zap1..3` IS IN NO ARCHIVE: Dead Wire's zap stands in
		NZSound.PlayShared( "nz.pop.deadwire.shock", neck );
		BossFx.BurstShared( neck, radius, ScreamBlue, 16 );

		// players in it AND in his sight (upstream's `IsAttackEntBlocked`), each dazed on their own machine
		var dazed = 0;
		foreach ( var p in PlayersWithin( WorldPosition + Vector3.Up * 40f * Size, radius ).ToList() )
		{
			if ( !CanSee( p.GameObject, 40f ) ) continue;
			NZNet.BossDaze( p.GameObject.Id, Daze, ScreamShock );
			dazed++;
		}

		// ⚠️ THE WALKERS ONLY (upstream's `!IsMooSpecial`): no special, no boss — see the class remarks for why
		var top = SprintTo;
		var reach = BoostRadius * Size;
		int charged = 0, faster = 0, stunned = 0, flashes = 0;
		foreach ( var z in Scene.GetAllComponents<ZombieAI>().ToList() )
		{
			if ( !z.IsValid() || z == Ai || z.State == ZombieState.Dead ) continue;
			if ( (z.Variant?.IsBoss ?? false) || SpecialEnemies.IsSpecial( z.Variant ) ) continue;
			var d = z.WorldPosition.Distance( WorldPosition );

			// CHARGED (2026-10-07, the user's 300): within the reach, out of the ground, on its feet — raised for good (a floor: one
			// already there keeps its rating), and the crackle on it on every machine, where a puppet is raised with it (`FeelCharge`).
			// ⚠️ Sent again for one an earlier scream charged: it crackles again (you see who is charged), and a puppet that rolled
			// its own lower tier at its spawn catches up.
			var charge = d <= reach && z.State != ZombieState.Spawning && !z.IsCrawler;
			if ( charge )
			{
				var was = z.SpeedRating;
				z.RaiseSpeedRating( top );
				if ( was < WalkerAnimations.SuperSprintRating && z.SpeedRating >= WalkerAnimations.SuperSprintRating ) faster++;
				NZNet.TeslaCharged( z.GameObject.Id, top, CrackleSeconds );
				charged++;
			}

			// stunned within the scream's own reach (upstream's); a flash only on one the charge missed (a reach tuned under 175)
			if ( d > radius || z.State is ZombieState.Spawning or ZombieState.Special ) continue;
			StatusEffects.Apply( z.GameObject, "stun", GameObject, seconds: Game.Random.Float( StunMin, MathF.Max( StunMin, StunMax ) ) );
			stunned++;
			if ( !charge && ++flashes <= MaxFlashes ) BossFx.BurstShared( z.WorldPosition + Vector3.Up * 40f, 20f, ScreamBlue, 4 );
		}

		Say( $"SCREAM — {dazed} player(s) dazed · {charged} walker(s) charged within {BoostRadius:0} (rating {top:0};"
			+ $" {faster} newly at top speed) · {stunned} stunned within {ScreamRadius:0}" );
	}

	// ══ every machine ════════════════════════════════════════════════════════

	/// <summary>
	/// One walker his scream charged, on THIS machine (`NZNet.TeslaCharged` — every machine, the host included): the crackle on it
	/// for <paramref name="seconds"/> (<see cref="TeslaCrackle"/>), and on a WATCHING machine its puppet raised to the host's
	/// <paramref name="rating"/>. ⚠️ THE SPEED IS THE HOST'S (the body arrives as positions), BUT THE GAIT IS EACH MACHINE'S: a puppet
	/// picks its own walk loop (`ZombieAI.TickPuppet`) and drives it at measured speed / that clip's own, capped at 2.5 x — so a
	/// walker charged from a 46 u/s walk would skate along at 340 with its legs at 2.5 x a walk. Raised here it takes a super-sprint
	/// clip, at ~1.6 x. (The Director's speed buff, Misery and the last-few sprint send nothing of the kind.)
	/// </summary>
	public static void FeelCharge( Guid id, float rating, float seconds )
	{
		var go = Game.ActiveScene?.Directory.FindByGuid( id );
		if ( !go.IsValid() ) return;

		var z = go.Components.Get<ZombieAI>( FindMode.EverythingInSelf );
		if ( z.IsValid() && z.IsPuppet && z.State != ZombieState.Dead ) z.RaiseSpeedRating( rating );

		TeslaCrackle.On( go, seconds );
	}

	// ══ diagnostics ══════════════════════════════════════════════════════════

	static IEnumerable<TeslaBoss> All
		=> Game.ActiveScene?.GetAllComponents<TeslaBoss>() ?? Enumerable.Empty<TeslaBoss>();

	/// <summary>`nz_tesla` — every Tesla Zombie alive, and the tuning.</summary>
	[ConCmd( "nz_tesla" )]
	public static void Report()
	{
		Log.Info( $"[nz-tesla] scream: a player in sight within {ScreamRange:0}, {ScreamChance * 100f:0}% every {ThinkEvery:0.##}s,"
			+ $" {ScreamCooldown:0}s after one ends · charges the walkers within {BoostRadius:0} to rating {SprintTo:0}"
			+ $"{(_sprintTo is null ? " (the game's top)" : $" (set; the game's top {TopRating:0})")}, crackle {CrackleSeconds:0.##}s"
			+ $" · within {ScreamRadius:0}: daze {Daze:0.#}s{(ScreamShock > 0f ? $" + shock {ScreamShock:0.#}s" : "")},"
			+ $" walkers stunned {StunMin:0.#}-{StunMax:0.#}s · standing swing within {StandRange:0}"
			+ $" under {StandSpeed:0.#} u/s · hit cap {MaxHit:0}" );

		var list = All.ToList();
		Log.Info( $"[nz-tesla] {list.Count} alive" );
		foreach ( var t in list )
			Log.Info( $"[nz-tesla]   hp {t.Hp?.Current ?? 0f:0}/{t.Hp?.Max ?? 0f:0} · state {t.Ai?.State} · speed {t.Ai?.MoveSpeed ?? 0f:0}"
				+ $"{(t._screaming ? " · SCREAMING" : $" · next scream in {MathF.Max( 0f, t._nextScream - Time.Now ):0.#}s")}"
				+ $" · target {MathF.Min( t._targetSpeed, 999f ):0} u/s{(t.Ai?.AttackOverride is not null ? " (standing swing)" : "")}"
				+ $"{(t._shielded ? " · untouchable" : "")} · hitboxes {t.Ai?.HitboxCount ?? 0}"
				+ $" · walkers in reach {t.InReach()} ({t.InReach( true )} at top speed)" );
	}

	/// <summary>The walkers a charge would take now (<paramref name="atTop"/>: those already in the top tier) — for `nz_tesla`.</summary>
	int InReach( bool atTop = false )
	{
		var reach = BoostRadius * Size;
		var n = 0;
		foreach ( var z in Scene.GetAllComponents<ZombieAI>() )
		{
			if ( !z.IsValid() || z == Ai || z.State is ZombieState.Dead or ZombieState.Spawning || z.IsCrawler ) continue;
			if ( (z.Variant?.IsBoss ?? false) || SpecialEnemies.IsSpecial( z.Variant ) ) continue;
			if ( z.WorldPosition.Distance( WorldPosition ) > reach ) continue;
			if ( atTop && z.SpeedRating < WalkerAnimations.SuperSprintRating ) continue;
			n++;
		}
		return n;
	}

	/// <summary>
	/// `nz_tesla_scream` — every Tesla Zombie screams now at whoever he is after, whatever the range, the roll or the cooldown; its
	/// `sonic_scream` frame charges the walkers within <see cref="BoostRadius"/> like any other.
	/// </summary>
	[ConCmd( "nz_tesla_scream" )]
	public static void ScreamCmd()
	{
		if ( NZGame.IsClient ) { Log.Info( "[nz-tesla] the host decides" ); return; }
		var n = 0;
		foreach ( var t in All.Where( t => t.Ai.IsValid() && !t.Dead && !t._screaming ).ToList() )
			if ( t.Scream( t.TargetPlayer ) ) n++;
		Log.Info( $"[nz-tesla] {n} scream(s) · one still climbing out cannot (run it again)" );
	}

	/// <summary>`nz_tesla_set &lt;key&gt; &lt;value&gt;` — retune him live.</summary>
	[ConCmd( "nz_tesla_set" )]
	public static void SetCmd( string key = "", float value = 0f )
	{
		switch ( key.ToLowerInvariant() )
		{
			case "range": ScreamRange = value; break;
			case "radius": ScreamRadius = value; break;
			case "cooldown": ScreamCooldown = value; break;
			case "chance": ScreamChance = value; break;
			case "think": ThinkEvery = value; break;
			case "daze": Daze = value; break;
			case "shock": ScreamShock = value; break;
			case "stunmin": StunMin = value; break;
			case "stunmax": StunMax = value; break;
			// 0 (or less): back to the game's top
			case "sprint": _sprintTo = value > 0f ? value : null; break;
			case "boostradius": BoostRadius = value; break;
			case "crackle": CrackleSeconds = value; break;
			case "standrange": StandRange = value; break;
			case "standspeed": StandSpeed = value; break;
			default:
				Log.Info( "[nz-tesla] nz_tesla_set <range|radius|cooldown|chance|think|daze|shock|stunmin|stunmax|sprint|boostradius|crackle"
					+ "|standrange|standspeed> <value>" );
				return;
		}
		Log.Info( $"[nz-tesla] {key} = {value:0.###}" );
		Report();
	}
}

/// <summary>
/// A walker the Tesla Zombie's scream charged (2026-10-07), on EVERY machine (`NZNet.TeslaCharged` → `TeslaBoss.FeelCharge`): for
/// a second sparks run along its limbs (Avogadro's, `AvogadroSpark.Along`) and a small blue light at its chest flickers out — so
/// the player sees who was charged. Its own object, never networked; it follows the walker, and goes when the time is up or the
/// walker is gone.
///
/// ⚠️ THE WALKER'S BONES (`j_*`, the CoD rig) — a body without them gets sparks round its middle instead (`AvogadroSpark.Between`).
/// </summary>
public sealed class TeslaCrackle : Component
{
	GameObject _zombie;
	SkinnedModelRenderer _body;
	PointLight _light;
	float _born, _life, _nextSpark;

	static Color Blue => new( 0.43f, 0.47f, 1f );
	static Color SparkBlue => new( 0.6f, 0.8f, 1f );

	/// <summary>Bone pairs a spark can run along: the spine, the arms, the legs.</summary>
	static (string A, string B) Pair( int i ) => i switch
	{
		0 => ("j_head", "j_neck"),
		1 => ("j_neck", "j_spineupper"),
		2 => ("j_spineupper", "j_spinelower"),
		3 => ("j_spineupper", "j_elbow_le"),
		4 => ("j_elbow_le", "j_wrist_le"),
		5 => ("j_spineupper", "j_elbow_ri"),
		6 => ("j_elbow_ri", "j_wrist_ri"),
		7 => ("j_mainroot", "j_knee_le"),
		8 => ("j_knee_le", "j_ankle_le"),
		9 => ("j_mainroot", "j_knee_ri"),
		_ => ("j_knee_ri", "j_ankle_ri"),
	};
	const int Pairs = 11;

	/// <summary>The crackle on <paramref name="zombie"/> for <paramref name="seconds"/>, on THIS machine.</summary>
	public static void On( GameObject zombie, float seconds )
	{
		if ( !zombie.IsValid() || seconds <= 0f || !zombie.Scene.IsValid() ) return;

		var go = new GameObject( true, "nz_tesla_crackle" );
		go.Flags |= GameObjectFlags.NotSaved | GameObjectFlags.NotNetworked;
		go.NetworkMode = NetworkMode.Never;
		go.WorldPosition = zombie.WorldPosition;

		var light = go.Components.Create<PointLight>();
		light.LightColor = Color.Black;
		light.Radius = 80f;
		light.Shadows = false;

		var c = go.Components.Create<TeslaCrackle>();
		c._zombie = zombie;
		c._body = zombie.Components.Get<SkinnedModelRenderer>( FindMode.EverythingInSelfAndDescendants );
		c._light = light;
		c._born = Time.Now;
		c._life = seconds;
	}

	protected override void OnUpdate()
	{
		// ⚠️ A HOTLOAD'S ZEROES (`_life` 0) END IT, as does its time or its walker
		var t = _life > 0f ? (Time.Now - _born) / _life : 1f;
		if ( t >= 1f || !_zombie.IsValid() )
		{
			GameObject.Destroy();
			return;
		}

		var size = MathF.Max( 0.1f, _zombie.WorldScale.x );
		var chest = _body.IsValid() && _body.TryGetBoneTransform( "j_spineupper", out var b )
			? b.Position
			: _zombie.WorldPosition + Vector3.Up * 45f * size;
		WorldPosition = chest;

		if ( _light.IsValid() )
		{
			_light.Radius = 80f * size;
			_light.LightColor = Blue * (2.5f * (1f - t) * Game.Random.Float( 0.6f, 1.15f ));
		}

		if ( Time.Now < _nextSpark ) return;
		_nextSpark = Time.Now + Game.Random.Float( 0.04f, 0.09f );

		var (a, z) = Pair( Game.Random.Int( 0, Pairs - 1 ) );
		if ( _body.IsValid() && _body.TryGetBoneTransform( a, out _ ) && _body.TryGetBoneTransform( z, out _ ) )
		{
			AvogadroSpark.Along( _body, a, z, Game.Random.Float( 0.06f, 0.16f ), 0.1f, 2.5f * size );
			return;
		}

		var from = chest + Vector3.Random * 12f * size;
		AvogadroSpark.Between( from, from + Vector3.Random.Normal * 16f * size, Game.Random.Float( 0.06f, 0.14f ), 0.1f, SparkBlue );
	}
}