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.
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² < 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 <key> <value>` — 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 );
}
}