Zombies/MeuchlerBoss.cs

Boss NPC component for the Meúchler boss in an NZombies game mode. Implements movement, ambush (burrow/raise), enraging, shove reaction, flight (flee) behavior, damage scaling and networked sound/particle events, plus console commands for diagnostics and tuning.

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

namespace NZombies;

/// <summary>
/// The Meúchler — CoD WWII's assassin, ported 2026-10-07 from GMod nZombies' `nz_zombie_boss_assassin` (GhostlyMoo's port), the
/// third batch. Spec: `Sbox nzombies/Docs/boss_specs/meuchler.md`. Two bodies, one rolled per spawn (`meuchler.zvar` lists both,
/// upstream's 50/50): the elite is the same rig, clips and events in other meshes, so this one component runs both.
///
///   • HE RISES (`stand_spawn_idle`) in an orange burst — upstream's `doom_caco_blast` — with a zap, and his alert heard map-wide.
///   • HE WALKS (one of three walks, upstream's roll: 97 or 35.1 u/s) UNTIL HE IS ANGRY — you in his sight within <see
///     cref="EnrageRange"/>, the first hit on him, or <see cref="EnrageAfter"/> s after he arrives: `walk_2_sprint`, then one of
///     five sprints (220-276 u/s) for good. AT HALF HEALTH he moves x<see cref="HalfSpeed"/>, for good.
///   • THE AMBUSH, when you are past <see cref="AmbushRange"/> or out of his sight, at most every <see cref="AmbushGapMin"/>-<see
///     cref="AmbushGapMax"/> s (counted from its start, upstream's; the first 1 s after he arrives): he crawls into the ground
///     (`trav_crawl_exit`) and out of it at the open spawn nearest you (`trav_crawl_enter`), ~5 s in all, with a click heard
///     map-wide as he goes down and as he comes up.
///   • THE SHOVE, upstream's counterplay: a melee hit staggers him away from where it came from (`stand_stumble_*`; from the
///     front, one time in three, the knockdown) — at most <see cref="ShovesPerWindow"/> a <see cref="ShoveWindow"/> s window, the
///     next window 2 s longer for each, and never while he is busy with a move of his own.
///   • HIS SWING (the variant's attacks, 75 against a walker's 50), and the LATER BLOW of a two-blow sprint swing, through the
///     hit window (upstream's `IgnoreImmunity`).
///   • HE TAKES x<see cref="BulletScale"/> FROM BULLETS, x<see cref="MeleeScale"/> FROM MELEE, x<see cref="OtherScale"/> FROM
///     ANYTHING ELSE (upstream's table), and nothing for his first <see cref="SpawnProtection"/> s (the base's).
///   • THE FLIGHT (2026-10-07, the user: *"everytime it loses 20% of its max hp, it runs away into a normal zombie spawnpoint
///     insanely quickly, and after a random ammount from 10-40 seconds later, it spawns again in the nearest normal zombie spawn
///     point from a random player with the hp it had before disapearing"*): at 80, 60, 40 and 20% of his health (<see
///     cref="FleeStep"/>), each once, he breaks off — no swing, no player — and runs at <see cref="FleeSpeed"/> u/s to the
///     round's nearest zombie spawn (not a special's or a boss's) that is <see cref="FleeClear"/> from everyone up (none, the
///     nearest), and into the ground there, the ambush's way. <see cref="FleeMin"/>-<see cref="FleeMax"/> s later he comes up out
///     of the zombie spawn nearest a random player (nobody up: he tries again every 2 s), the ambush's rise, with the health he
///     went down with. While away he is hidden, untouchable and still; no ambush, shove or enrage starts until he is back up.
///
/// ⚠️ THE AMBUSH STARTS WHERE HE STANDS. Upstream walks to the crawl spawner nearest him first (giving up after 3 s and snapping
/// there); a spawn here has no crawl type, and both crawl clips came through the port in place — into and out of the floor,
/// their 121 and 146 u of travel stripped — so he goes down where he is and comes up at the open zombie, special or boss spawn
/// nearest you (upstream's `FindHiddenSpawn`: doors only, not the round or the power). Upstream's checks on the entry spawner
/// go with it (the spec reads its visibility test as inverted); ⚠️ instead he only goes when that spawn is <see
/// cref="AmbushGain"/> nearer you than he is — a crawl that brings him no closer is no ambush.
/// ⚠️ UNTOUCHABLE WHILE ALL OF HIM IS UNDER THE FLOOR: upstream sets no flag, but its crawling mesh was up to ~150 u from its 30 u
/// hit bounds, so bullets missed it. The visible part of each crawl takes hits as ever.
/// ⛔ ONE SWING IS ONE ATTACK: the later blow and the AI's first one together never take more than <see cref="ComboCap"/> x
/// `MaxHit` from a player (the AI's blow counted as landed in full). Upstream's lands in full — 150 from a two-blow swing, a full
/// bar — which the user's rule forbids (*"we cant have any attack insta kill like that"*). From round 16 his first blow alone is
/// at the cap (base health 150, the match's x1 damage), so the later one is felt in the early rounds only, unless
/// `nz_meuchler_set combo` says otherwise.
/// ⚠️ NO STANDING SWINGS: a variant states one attack set a tier (`ZombieAI.AttackClipsForNow`) and the AI keeps closing through
/// a variant's swing, so a `stand_attack_*` would glide. Walk attacks walking, sprint attacks sprinting; the 2- and 3-blow
/// standing swings are out.
/// ⚠️ NO TURN CLIPS: `react_turn_90_*` turn his mesh inside the clip while the AI turns his body to his target during any
/// special — the two would add up and snap back. A target found again gets `walk_2_sprint` toward them, with the turn's taunt
/// when they stand to his side.
/// ⚠️ `walk_2_sprint`'s 93 u are carried by an arc (the port strips a clip's travel). The stumbles' push-back (34-92 u) is not:
/// an arc faces along its course, and he reels backward.
/// ⚠️ AT HALF HEALTH HIS LEGS KEEP UP: upstream raised only the move speed, so the feet slid; here every sprint lands in his
/// fastest tier (`sprint_01`) with its legs sped up to match.
/// ⚠️ THE FLIGHT IS TWO HOLDS. The run is `sprint_01` at the flight's speed (x2.35 at 650) and the burrow the ambush's crawl into
/// the ground at that same rate — a hold plays every clip at the rate it began with — so the exit is quick too: gone in 0.94 s,
/// 1.18 s in all. The time away and the rise are a second hold at x1, so he comes up at the ambush's own pace.
/// ⚠️ ONLY A SPAWN HE CAN REACH: of the nearest eight, the first with a whole path on the navmesh, clear of everyone first. None,
/// he goes into the ground where he stands, as he does when he gets there, stops short, or runs out of <see cref="FleeTimeout"/>.
/// ⚠️ A FLIGHT WAITS UNTIL HE IS FREE: not mid-stagger, mid-ambush, on a link or at a window (the ambush's own rule). A share lost
/// while it waits, while he runs, or while he is away is spent with it: one flight.
/// ⚠️ NOT SOLID WHILE HIDDEN, on every machine (`ZombieAI.SolidBody`, keyed on the hiding itself): 10-40 s at a spawn would be an
/// invisible wall. The ambush's hidden moment goes the same way.
/// ⚠️ AWAY, HE STILL CLICKS: his idle voice (`nz.meuchler.clickfar`, every 8-15 s) is the AI's, with no switch, and comes from
/// where he went down.
///
/// ⚠️ HOST AUTHORITY, EVERY MACHINE DRAWS: the clips are relayed (`PlayShared`, `SwapClip`), the vanishing is `NZNet.BossHidden`,
/// the burst `NZNet.BossBurstFx`, the dirt `SpawnDirt`'s own announcement, the voices `NZSound.PlayShared`. The flight's pose
/// held under the floor while away is `NZNet.ZombieFreeze` on the other machines, sent again with the hiding every 2 s (a
/// player who joins meanwhile), and its run re-sent each stride (a relayed clip plays once on a watching machine).
/// ⛔ NO HIT OF HIS DOWNS ANYONE FROM FULL HEALTH (`BossBase.MaxHit`), Oberon's rule — the swing's later blow included.
/// </summary>
public sealed class MeuchlerBoss : BossBase
{
	protected override string Tag => "[nz-meuchler]";

	/// <summary>His head, hunched: 47-58 u through his idle, walk and sprint.</summary>
	protected override float VoiceHeight => 56f;

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

	static float? _ambushRange, _ambushChance, _ambushGapMin, _ambushGapMax, _ambushGain;
	/// <summary>He ambushes a target past 575 or out of his sight (upstream's `!TargetInRange(575) or IsAttackBlocked()`).</summary>
	public static float AmbushRange { get => _ambushRange ?? 575f; set => _ambushRange = value; }
	/// <summary>The chance one pass (every 0.25 s) goes: 0.49, upstream's `math.random(100) &lt; 50` a pass.</summary>
	public static float AmbushChance { get => _ambushChance ?? 0.49f; set => _ambushChance = value; }
	/// <summary>Then not again for 9.46-13.34 s, counted from when he goes down (upstream's).</summary>
	public static float AmbushGapMin { get => _ambushGapMin ?? 9.46f; set => _ambushGapMin = value; }
	public static float AmbushGapMax { get => _ambushGapMax ?? 13.34f; set => _ambushGapMax = value; }
	/// <summary>⚠️ Not upstream's: how much nearer his target the spawn he comes out of must be than he is — 150.</summary>
	public static float AmbushGain { get => _ambushGain ?? 150f; set => _ambushGain = value; }

	static float? _enrageRange, _enrageAfter, _halfSpeed;
	/// <summary>He enrages at a target in his sight within 750, at the first hit, or 20 s after he arrives (upstream's).</summary>
	public static float EnrageRange { get => _enrageRange ?? 750f; set => _enrageRange = value; }
	public static float EnrageAfter { get => _enrageAfter ?? 20f; set => _enrageAfter = value; }
	/// <summary>At half health he moves x1.35, for good (upstream's `MovementSpeedMultiplier`).</summary>
	public static float HalfSpeed { get => _halfSpeed ?? 1.35f; set => _halfSpeed = value; }

	static float? _bullet, _melee, _other, _protect, _window, _shoves, _combo;
	/// <summary>What he takes from bullets: 0.085; from melee: 0.95; from anything else: 0.5 (upstream's `PostTookDamage`).</summary>
	public static float BulletScale { get => _bullet ?? 0.085f; set => _bullet = value; }
	public static float MeleeScale { get => _melee ?? 0.95f; set => _melee = value; }
	public static float OtherScale { get => _other ?? 0.5f; set => _other = value; }
	/// <summary>Nothing at all for his first second (the base's spawn protection), and no anger from it.</summary>
	public static float SpawnProtection { get => _protect ?? 1f; set => _protect = value; }
	/// <summary>A melee hit staggers him at most twice a 10 s window, the next window 2 s longer for each (upstream's `ShoveCount`).</summary>
	public static float ShoveWindow { get => _window ?? 10f; set => _window = value; }
	public static float ShovesPerWindow { get => _shoves ?? 2f; set => _shoves = value; }
	/// <summary>
	/// ⛔ The most one swing's blows take from one player together, against `MaxHit`: 1 — the user's rule, a swing is one attack.
	/// Under 1.5 no swing downs anyone from full base health; upstream's every-blow-lands is about 3.
	/// </summary>
	public static float ComboCap { get => _combo ?? 1f; set => _combo = value; }

	static float? _fleeSpeed, _fleeMin, _fleeMax, _fleeStep, _fleeTime, _fleeClear;
	/// <summary>
	/// The flight (2026-10-07): 650 u/s — the user's *"insanely quickly"*, 2.35x his fastest sprint (276) and 1.74x that at half
	/// health (373). His legs keep up to x4 (`sprint_01` at <see cref="FleeSpeed"/> / 276, never under x1).
	/// </summary>
	public static float FleeSpeed { get => _fleeSpeed ?? 650f; set => _fleeSpeed = value; }
	/// <summary>Then away 10-40 s, the user's, before he comes back.</summary>
	public static float FleeMin { get => _fleeMin ?? 10f; set => _fleeMin = value; }
	public static float FleeMax { get => _fleeMax ?? 40f; set => _fleeMax = value; }
	/// <summary>A flight each time this share of his health goes: 0.2, the user's 20% — at 80, 60, 40 and 20%.</summary>
	public static float FleeStep { get => _fleeStep ?? 0.2f; set => _fleeStep = value; }
	/// <summary>At most 5 s running (3,250 u at 650); then he goes into the ground wherever he got to.</summary>
	public static float FleeTimeout { get => _fleeTime ?? 5f; set => _fleeTime = value; }
	/// <summary>The spawn he runs to stands 500 from everyone up, when one he can reach does.</summary>
	public static float FleeClear { get => _fleeClear ?? 500f; set => _fleeClear = value; }

	// his clips (30 fps) and their events — ⛔ `const`, never `static readonly` (INSTRUCTIONS.md §1)
	const string ClipToSprint = "nz_s2_zom_asn_walk_2_sprint", ClipCrawlOut = "nz_s2_zom_asn_trav_crawl_exit",
		ClipCrawlIn = "nz_s2_zom_asn_trav_crawl_enter", ClipMantle = "nz_s2_zom_asn_trav_mantle_40",
		ClipKnockdown = "nz_s2_zom_asn_stand_pain_react_knockdown", Stumble = "nz_s2_zom_asn_stand_stumble_";
	const float ToSprintLen = 62f / 30f, CrawlOutLen = 83f / 30f, CrawlInLen = 68f / 30f, StumbleLen = 60f / 30f,
		KnockdownLen = 100f / 30f, DeathFall = 20f / 30f;

	/// <summary>
	/// The travel `walk_2_sprint` carries (stripped by the port), and the crawl-out frame from which all of him is under the floor
	/// (its highest bone below 0, measured on the clip).
	/// </summary>
	const float ToSprintTravel = 93f, SunkFrame = 66f;

	/// <summary>His clips' own speeds — `meuchler.zvar`'s tiers are picked by them: walk_03, walk_01/02, sprint_03/04/05, sprint_02, sprint_01.</summary>
	const float WalkSlow = 35.1f, Walk = 97f, Sprint = 220.4f, SprintB = 251.5f, SprintA = 276.3f;

	/// <summary>
	/// The flight's run (2026-10-07): his fastest sprint, `sprint_01` (a 62-frame cycle, its `walkframe` — the clip's own length is
	/// read first) — and what ends it: within 48 u of the spot, under 40 u gained in half a second (stopped short), and at most 8
	/// spawns asked for a path.
	/// </summary>
	const string ClipFlee = "nz_s2_zom_asn_sprint_01";
	const float FleeCycle = 62f / 30f, FleeArrive = 48f, FleeStall = 40f;
	const int FleePaths = 8;

	/// <summary>Upstream's arrival burst, `doom_caco_blast`'s orange (255, 136, 8).</summary>
	static Color Orange => new( 1f, 0.53f, 0.03f );

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

	bool _enraged, _hurt, _halfSpeedOn, _crawling, _under, _hadTarget = true, _blowDone, _fell;
	float _pace, _bornAt, _enrageAt, _nextThink, _nextAmbush, _windowEnd, _idleSince, _clipT, _diedAt;
	int _shovesTaken;
	string _clip;
	Vector3 _exit;

	/// <summary>
	/// The flight (2026-10-07). ⚠️ FIELDS A HOTLOAD LEAVES AT 0: `_fleeArmed` false means "not counted yet" — the first look counts
	/// the shares already gone as spent, with no flight (`TickShares`); `_fleeRate` 0 reads as 1.
	/// </summary>
	Flight _flight;
	bool _fleePending, _fleeArmed, _awayHeld, _ghost;
	int _fleesSpent;
	float _fleeRate, _fleeStart, _fleeEnds, _fleeRepathAt, _fleeCheckAt, _relayAt, _backAt, _fleeHp, _refreshAt, _fleeRetryAt;
	Vector3 _fleeTo, _fleeFrom, _fleeCheckPos;

	enum Beat { Alert, Click, Crawl, Bodyfall, Fall, Dirt, Under, Emerge, Out, Gone }

	/// <summary>The flight's steps: running, going into the ground, away (hidden, held), coming back up.</summary>
	enum Flight { None, Run, Dig, Away, Rise }

	public bool Enraged => _enraged;
	public bool Crawling => _crawling;
	public bool Fleeing => _flight != Flight.None;
	public bool Elite => Body.IsValid() && (Body.Model?.ResourcePath?.Contains( "meuchler_elite" ) ?? false);

	/// <summary>
	/// Carried by something the AI drives — a nav link, a window (held at one, or climbing through it on his mantle), an arc —
	/// upstream's `GetIsBusy`: no move of his starts, no shove lands, and his gait waits.
	/// </summary>
	bool Busy => Ai.CrossingLink || Ai.OnLink || Ai.ParkedAt is not null || Ai.Arcing || Ai.CurrentClip == ClipMantle;

	/// <summary>Free for a move of his own: walking, swinging or idle, and not carried by anything (<see cref="Busy"/>).</summary>
	bool Free => Ai.State is (ZombieState.Chasing or ZombieState.Attacking or ZombieState.Idle) && !Busy;

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

		// ⛔ NONE OF THE WALKER'S CLIMB CLIPS ON HIS MODEL: his own mantle, upstream's every barricade jump
		Ai.ClimbClipOverride = ClipMantle;

		_bornAt = Time.Now;
		_enrageAt = Time.Now + EnrageAfter;
		_nextAmbush = Time.Now + 1f;
		_windowEnd = Time.Now + 5f;

		// the flight's shares counted from full health: none spent
		_fleeArmed = true;
		_fleesSpent = 0;

		// upstream's `SpeedChanged`: one of his three walks (walk_01/02 share a tier, so the AI rolls between them)
		_pace = Game.Random.Int( 0, 2 ) == 0 ? WalkSlow : Walk;
		ApplyPace();

		// upstream's arrival (`doom_caco_blast`, an orange electric burst; the zap is the variant's `SpawnSound`), and his alert
		// at frame 17 of his rise
		BossFx.BurstShared( WorldPosition + Vector3.Up * 50f * Size, 140f * Size, Orange, 14 );
		Queue( Frames( 17f ), (int)Beat.Alert );

		Say( $"spawned — the {(Elite ? "elite" : "plain")} body · {Hp?.Max ?? 0f:0} hp (x{BulletScale:0.###} from bullets, x{MeleeScale:0.##}"
			+ $" melee, x{OtherScale:0.##} else) · walks at {_pace:0} · ambush past {AmbushRange:0} or unseen, {OpenSpawns().Count()} open"
			+ $" spawn(s) · flees at {FleeLeft()} to {NormalSpawns().Count} zombie spawn(s) · hitboxes {Ai.HitboxCount}" );
	}

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

		WatchClip();
		TickWindow();
		TickHalfHealth();
		TickShares();

		// ⚠️ THE FLIGHT OWNS HIM from the run to the end of his rise (2026-10-07): no ambush, enrage or shove starts meanwhile
		if ( _flight != Flight.None )
		{
			TickFlight();
			return;
		}

		// the ambush owns him until he is out of the ground — a hold that ended early hands him back, seen
		if ( _crawling )
		{
			if ( Ai.State != ZombieState.Special ) EndCrawl();
			return;
		}

		// a flight that is due goes the moment he is free, before anything of his own
		if ( _fleePending && Time.Now >= _fleeRetryAt && Free && BeginFlight() ) return;

		if ( Time.Now < _nextThink ) return;
		_nextThink = Time.Now + 0.25f;
		Think();
	}

	/// <summary>
	/// ⚠️ NOT SOLID WHILE HIDDEN (2026-10-07), on every machine: the capsule a player walks into is off while the body is down
	/// to its shadow (`BossFx.SetHidden`, which reaches every machine), so 10-40 s away at a spawn is no invisible wall. Keyed on
	/// the hiding itself, so it needs no message of its own. His `SolidBody` is the default `true`; nothing else writes it.
	/// </summary>
	protected override void EveryFrame()
	{
		if ( !Body.IsValid() ) return;
		var hidden = Body.RenderType == ModelRenderer.ShadowRenderType.ShadowsOnly;
		if ( hidden == _ghost ) return;
		_ghost = hidden;
		Ai.SolidBody = !hidden;
	}

	protected override void Died( bool host )
	{
		_diedAt = Time.Now;
		if ( !host ) return;
		// under the floor, the ambush's or away on the flight (only a kill that ignores his invulnerability): seen again
		if ( _under ) Surface();
		Say( "killed" );
	}

	/// <summary>The heavy bodyfall of his death clips (their frames 19-21), on every machine.</summary>
	protected override void DeadFrame()
	{
		if ( _fell || Time.Now < _diedAt + DeathFall ) return;
		_fell = true;
		NZSound.Play( "nz.meuchler.fall", WorldPosition );
	}

	/// <summary>
	/// His table, upstream's `PostTookDamage`: x<see cref="BulletScale"/> from bullets, x<see cref="MeleeScale"/> from melee (and the
	/// shove), x<see cref="OtherScale"/> from anything else — and every hit angers him. Nothing for his first second, nothing while
	/// all of him is under the floor. THE HOST, once a hit.
	/// </summary>
	protected override float ScaleDamage( float amount, in DamageInfo damage )
	{
		if ( _under || Time.Now < _bornAt + SpawnProtection ) return 0f;
		_hurt = true;

		if ( Health.IsMelee( damage ) )
		{
			TryShove( damage.Attacker.IsValid() ? damage.Attacker.WorldPosition : damage.Position );
			return MeleeScale;
		}

		return (damage.Tags?.Has( SWB.Shared.TagsHelper.Bullet ) ?? false) ? BulletScale : OtherScale;
	}

	protected override void OnBeat( int beat )
	{
		switch ( (Beat)beat )
		{
			case Beat.Alert:
				NZSound.PlayShared( "nz.meuchler.alert", Voice );
				break;

			case Beat.Click:
				NZSound.PlayShared( "nz.meuchler.click", Voice );
				break;

			case Beat.Crawl:
				NZSound.PlayShared( "nz.meuchler.crawl", WorldPosition );
				break;

			case Beat.Bodyfall:
				NZSound.PlayShared( "nz.meuchler.bodyfall", WorldPosition );
				break;

			case Beat.Fall:
				NZSound.PlayShared( "nz.meuchler.fall", WorldPosition );
				break;

			case Beat.Dirt:
				// the ground he goes into and comes out of, on every machine (`SpawnDirt` announces itself)
				SpawnDirt.Burst( Scene, WorldPosition, 10 );
				break;

			case Beat.Under:
				// all of him under the floor: hidden on every machine, and untouchable — the ambush's, or the flight's burrow
				if ( !_crawling && _flight != Flight.Dig ) break;
				_under = true;
				SetHidden( true );
				if ( Hp.IsValid() ) Hp.Invulnerable = true;
				break;

			case Beat.Emerge:
				Emerge();
				break;

			case Beat.Out:
				_crawling = false;
				if ( _flight == Flight.Rise ) EndFlight();
				break;

			case Beat.Gone:
				// the flight's burrow is over: away
				if ( _flight == Flight.Dig ) BeginAway();
				break;
		}
	}

	// ══ the host's pass ══════════════════════════════════════════════════════

	/// <summary>Upstream's `AI()`, a pass every quarter second: the enrage first, then the ambush, then a target found again. THE HOST.</summary>
	void Think()
	{
		var p = TargetPlayer;
		var has = IsUp( p );
		var found = has && !_hadTarget && Time.Now - _idleSince >= 1f;
		if ( !has && _hadTarget ) _idleSince = Time.Now;
		_hadTarget = has;

		if ( Ai.State != ZombieState.Chasing || Busy ) return;
		if ( TryEnrage( p ) || TryAmbush( p ) ) return;
		if ( found && CanSee( p.GameObject ) ) React( p );
	}

	void ApplyPace() => SetSpeed( _pace * (_halfSpeedOn ? HalfSpeed : 1f) );

	/// <summary>At half health, x<see cref="HalfSpeed"/> for good — once he is free to change his gait. THE HOST.</summary>
	void TickHalfHealth()
	{
		if ( _halfSpeedOn || !Hp.IsValid() || Hp.Max <= 1f || Hp.Current > Hp.Max * 0.5f ) return;
		// ⚠️ NOT MID-FLIGHT (2026-10-07): the hold hands him back for a frame while he is away, and his gait waits until he is up
		if ( Ai.State is not (ZombieState.Chasing or ZombieState.Attacking) || Busy || _flight != Flight.None ) return;
		_halfSpeedOn = true;
		ApplyPace();
		Say( $"half health — x{HalfSpeed:0.##}, {Ai.SpeedOverride:0} u/s" );
	}

	// ══ the enrage ═══════════════════════════════════════════════════════════

	bool TryEnrage( NZPlayer p )
	{
		if ( _enraged ) return false;
		var seen = IsUp( p ) && WorldPosition.Distance( p.WorldPosition ) <= EnrageRange * Size && CanSee( p.GameObject );
		if ( !seen && !_hurt && Time.Now < _enrageAt ) return false;
		Enrage();
		return true;
	}

	/// <summary>Angry, once: `walk_2_sprint`, then one of his five sprints for good (upstream's `SetRunSpeed(71)`). THE HOST.</summary>
	void Enrage()
	{
		if ( _enraged ) return;
		_enraged = true;
		ToSprint( true );

		// one of five, picked once (sprint_03/04/05 share a tier, so the AI rolls between them)
		var roll = Game.Random.Int( 0, 4 );
		_pace = roll == 0 ? SprintA : roll == 1 ? SprintB : Sprint;
		ApplyPace();
		Say( $"ENRAGED — sprinting at {Ai.SpeedOverride:0} u/s" );
	}

	/// <summary>
	/// `walk_2_sprint`, its alert at frame 15 — and its 93 u forward, which the port stripped: an arc along his facing, short of any
	/// wall (the Panzerhund's dash). THE HOST.
	/// </summary>
	bool ToSprint( bool alert )
	{
		if ( !PlayShared( ClipToSprint, ToSprintLen ) ) return false;
		if ( alert ) Queue( Frames( 15f ), (int)Beat.Alert );

		var dir = Facing;
		var from = WorldPosition + Vector3.Up * 40f * Size;
		var reach = ToSprintTravel * Size;
		var wall = Scene.Trace.Ray( from, from + dir * reach ).IgnoreGameObjectHierarchy( GameObject )
			.WithoutTags( "zombie", "player", "trigger", "ragdoll" ).Run();
		if ( wall.Hit ) reach = MathF.Max( 0f, wall.Distance - 30f * Size );
		if ( reach > 16f ) Ai.BeginArc( WorldPosition + dir * reach, ToSprintLen, 0f );
		return true;
	}

	/// <summary>
	/// A target found after none (upstream's `IsIdle` reaction): `walk_2_sprint` straight at them — with the taunt their turn clips
	/// voiced when they stand to his side, his alert when they do not. THE HOST.
	/// </summary>
	void React( NZPlayer p )
	{
		var (dot, dot2) = Sides( p.WorldPosition );
		var side = (dot2 < -0.5f && dot >= -0.5f) || (dot2 > 0.5f && dot <= 0.5f);
		Face( p.WorldPosition );
		if ( !ToSprint( !side ) ) return;
		if ( side ) NZSound.PlayShared( "nz.meuchler.taunt", Voice );
		Say( "found his target again" );
	}

	/// <summary>
	/// Upstream's side test, from <paramref name="from"/> to him: `Dot` against his forward, `Dot2` against his right — Dot below 0,
	/// it is in front of him; Dot2 below -0.5, on his right; above 0.5, on his left.
	/// </summary>
	(float Dot, float Dot2) Sides( Vector3 from )
	{
		var normal = (WorldPosition - from).WithZ( 0 );
		normal = normal.Length > 1f ? normal.Normal : -Facing;
		var right = Vector3.Cross( Facing, Vector3.Up );
		return (Facing.Dot( normal ), right.Dot( normal ));
	}

	// ══ the ambush ═══════════════════════════════════════════════════════════

	bool TryAmbush( NZPlayer p )
	{
		if ( Time.Now < _nextAmbush || !IsUp( p ) ) return false;

		var d = WorldPosition.Distance( p.WorldPosition );
		if ( d <= AmbushRange * Size && CanSee( p.GameObject ) ) return false;
		if ( Game.Random.Float( 0f, 1f ) >= AmbushChance ) return false;

		// ⚠️ ONLY WHEN IT BRINGS HIM NEARER — and a failed look waits a second, so a map without open spawns costs no search a pass
		var exit = ExitNear( p.WorldPosition );
		if ( exit is null || exit.Value.Distance( p.WorldPosition ) + AmbushGain * Size > d )
		{
			_nextAmbush = Time.Now + 1f;
			return false;
		}

		return BeginAmbush( exit.Value );
	}

	/// <summary>
	/// Into the ground where he stands (`trav_crawl_exit`, in place: he drops, crawls, turns and sinks) with the click as he goes,
	/// heard map-wide — and out at <paramref name="exit"/> when it ends (<see cref="Emerge"/>). THE HOST.
	/// </summary>
	bool BeginAmbush( Vector3 exit )
	{
		// ⚠️ THE WHOLE ACT IS ONE HOLD: down, gone, up — the rise re-arms it for its own length (Zaballa's teleport)
		if ( !PlayShared( ClipCrawlOut, CrawlOutLen + CrawlInLen + 0.5f ) ) return false;

		_crawling = true;
		_exit = exit;
		// ⚠️ FROM THE START, upstream's: another is possible ~4-8 s after he comes up
		_nextAmbush = Time.Now + Game.Random.Float( AmbushGapMin, AmbushGapMax );

		NZSound.PlayShared( "nz.meuchler.click", Voice );
		Queue( Frames( 10f ), (int)Beat.Bodyfall );
		Queue( Frames( 29f ), (int)Beat.Crawl );
		Queue( Frames( 41f ), (int)Beat.Crawl );
		Queue( Frames( 48f ), (int)Beat.Dirt );
		Queue( Frames( SunkFrame ), (int)Beat.Under );
		Queue( CrawlOutLen, (int)Beat.Emerge );

		Say( $"ambush — into the ground, out {exit.Distance( WorldPosition ):0}u away" );
		return true;
	}

	/// <summary>
	/// Out of the ground at the open spawn nearest his target NOW (upstream re-picks it, B:478; none, the first pick), facing them:
	/// `trav_crawl_enter`, in place — he rises through the floor — and the click at its frame 10. THE HOST.
	/// </summary>
	void Emerge()
	{
		if ( !_crawling ) return;

		var p = TargetPlayer;
		var up = IsUp( p );
		Rise( (up ? ExitNear( p.WorldPosition ) : null) ?? _exit, up ? p : null );
	}

	/// <summary>
	/// Up out of the floor at <paramref name="spot"/>, facing <paramref name="facing"/> if any — the ambush's rise, and the flight's
	/// return (2026-10-07). The hold he is in re-armed for the clip's length; <see cref="Beat.Out"/> at its end. THE HOST.
	/// </summary>
	void Rise( Vector3 spot, NZPlayer facing )
	{
		Warp( spot );
		if ( facing.IsValid() ) Face( facing.WorldPosition );

		// ⚠️ THE CLIP BEFORE HE IS SHOWN: its first frame has all of him under the floor, so no machine sees the old pose
		SwapClip( ClipCrawlIn );
		Ai.HoldSpecialFor( CrawlInLen );
		Surface();

		Queue( Frames( 4f ), (int)Beat.Dirt );
		Queue( Frames( 10f ), (int)Beat.Click );
		Queue( Frames( 17f ), (int)Beat.Crawl );
		Queue( Frames( 31f ), (int)Beat.Crawl );
		Queue( Frames( 44f ), (int)Beat.Crawl );
		Queue( CrawlInLen, (int)Beat.Out );
	}

	/// <summary>Seen and touchable again, on every machine. THE HOST.</summary>
	void Surface()
	{
		_under = false;
		SetHidden( false );
		if ( Hp.IsValid() ) Hp.Invulnerable = false;
	}

	/// <summary>The ambush cut short, however: him back, seen, and its leftover beats forgotten. THE HOST.</summary>
	void EndCrawl()
	{
		_crawling = false;
		ClearBeats();
		if ( _under ) Surface();
	}

	/// <summary>
	/// Every spawn a zombie could come out of now: zombie, special and boss spawns whose door is open — upstream's
	/// `GetZombieSpawnArray` with its link test (it ignores a spawner's round and power too; a spawn here has no crawl type).
	/// </summary>
	IEnumerable<SpawnPoint> OpenSpawns()
	{
		var cfg = ActiveConfig.Current;
		if ( cfg is null ) yield break;
		foreach ( var list in new[] { cfg.ZombieSpawns, cfg.SpecialSpawns, cfg.BossSpawns } )
		{
			if ( list is null ) continue;
			foreach ( var s in list )
				if ( s is not null && DoorLinks.AnyOpen( s.Link, s.Link2, s.Link3 ) ) yield return s;
		}
	}

	/// <summary>
	/// Upstream's `FindHiddenSpawn`: the open spawn nearest <paramref name="to"/> (no sight test — upstream's is commented out), and
	/// where a zombie from it stands (`ZombieAI.NavGround`, on its own level). Null when none is open.
	/// </summary>
	Vector3? ExitNear( Vector3 to ) => StandNear( OpenSpawns(), to );

	/// <summary>Of <paramref name="spawns"/>, where a zombie from the one nearest <paramref name="to"/> stands; null, none (the ambush's lookup, and the flight's return).</summary>
	Vector3? StandNear( IEnumerable<SpawnPoint> spawns, Vector3 to )
	{
		SpawnPoint best = null;
		var bestD = float.MaxValue;
		foreach ( var s in spawns )
		{
			var d = s.Position.DistanceSquared( to );
			if ( d >= bestD ) continue;
			bestD = d;
			best = s;
		}

		if ( best is null ) return null;
		return ZombieAI.NavGround( Scene, best.Position );
	}

	// ══ the flight (2026-10-07) ══════════════════════════════════════════════

	/// <summary>The share of his health one flight costs, kept sane: 0.05-0.95.</summary>
	static float Step => Math.Clamp( FleeStep, 0.05f, 0.95f );

	/// <summary>How many shares above 0 a step makes: 4 at 0.2 (80, 60, 40, 20%).</summary>
	static int Shares( float step ) => Math.Max( 0, (int)MathF.Ceiling( 1f / step - 1e-4f ) - 1 );

	/// <summary>His legs' rate for the run, and so the burrow's: the flight's speed over `sprint_01`'s, x1-x4 — x2.35 at 650.</summary>
	static float RunRate => Math.Clamp( FleeSpeed / SprintA, 1f, 4f );

	/// <summary>This flight's rate (0 after a hotload reads as 1).</summary>
	float FleeRate => _fleeRate > 0.01f ? _fleeRate : 1f;

	/// <summary>One cycle of the run at its rate — how often a watching machine is sent it again.</summary>
	float Stride => (Body.IsValid() && Body.Sequence.Name == ClipFlee && Body.Sequence.Duration > 0.01f ? Body.Sequence.Duration : FleeCycle)
		/ FleeRate;

	/// <summary>The shares still to come, "60/40/20%", or "none".</summary>
	string FleeLeft()
	{
		var step = Step;
		var all = Shares( step );
		if ( _fleesSpent >= all ) return "none";
		return string.Join( "/", Enumerable.Range( _fleesSpent + 1, all - _fleesSpent ).Select( k => $"{(1f - k * step) * 100f:0}" ) ) + "%";
	}

	/// <summary>
	/// The round's zombie spawns, as its wave picks from them (`RoundManager.EligibleSpawnsFor`: link open, power on if they ask,
	/// round reached) — no special's or boss's. Before a round, round 1's.
	/// </summary>
	static List<SpawnPoint> NormalSpawns()
	{
		if ( ActiveConfig.Current?.ZombieSpawns is null ) return new();
		return RoundManager.EligibleSpawnsFor( Math.Max( 1, RoundManager.Instance?.Round ?? 1 ) );
	}

	/// <summary>
	/// The shares of his health gone, against those spent, every frame: a new one is a flight, once. Several in one hit, one
	/// flight; one gone while a flight is due, under way or away, spent with it and no other. Counted from the top, so a new
	/// <see cref="FleeStep"/> counts the spent ones in its own shares. THE HOST.
	/// </summary>
	void TickShares()
	{
		if ( !Hp.IsValid() || Hp.Max <= 1f ) return;

		var step = Step;
		var gone = Math.Clamp( (int)MathF.Floor( (1f - Hp.Current / Hp.Max) / step + 1e-4f ), 0, Shares( step ) );

		// ⚠️ A HOTLOAD INTO A LIVE ONE (`_fleeArmed` still false): what is already gone counts as spent, with no flight
		if ( !_fleeArmed )
		{
			_fleeArmed = true;
			_fleesSpent = gone;
			return;
		}

		if ( gone <= _fleesSpent ) return;
		var n = gone - _fleesSpent;
		_fleesSpent = gone;

		var what = $"{Hp.Current / Hp.Max * 100f:0}% health, {n} share(s) gone";
		if ( _flight != Flight.None || _fleePending )
		{
			Say( $"{what} — spent with the flight {(_fleePending ? "due" : "under way")}, no other" );
			return;
		}

		_fleePending = true;
		_fleeRetryAt = 0f;
		Say( $"{what} — a flight" );
	}

	/// <summary>
	/// Off at <see cref="FleeSpeed"/> to the spawn <see cref="FleeSpot"/> picks, in his fastest sprint sped to match, with the pain
	/// vox as he breaks off — the swing he was in dropped. With nowhere he can reach (or already there), straight into the ground.
	/// ⚠️ ONE HOLD for the run and the burrow, so the burrow plays at the run's rate. False when the hold would not start (it
	/// tries again in a second). THE HOST.
	/// </summary>
	bool BeginFlight()
	{
		var rate = RunRate;
		var to = FleeSpot( out var clear );
		var here = to is null || (to.Value - WorldPosition).WithZ( 0 ).Length <= FleeArrive * Size;

		if ( !PlayShared( here ? ClipCrawlOut : ClipFlee, MathF.Max( 0f, FleeTimeout ) + CrawlOutLen / rate + 1.5f, rate ) )
		{
			_fleeRetryAt = Time.Now + 1f;
			return false;
		}

		_fleePending = false;
		_flight = Flight.Run;
		_fleeRate = rate;
		_fleeFrom = WorldPosition;
		_fleeTo = to ?? WorldPosition;
		_fleeStart = Time.Now;
		_fleeEnds = Time.Now + MathF.Max( 0f, FleeTimeout );
		_fleeRepathAt = 0f;
		_fleeCheckAt = Time.Now + 0.75f;
		_fleeCheckPos = WorldPosition;
		_relayAt = Time.Now + Stride;

		NZSound.PlayShared( "nz.meuchler.pain", Voice );

		if ( here )
		{
			Dig( to is null ? "no zombie spawn he can reach" : "already at one", swap: false );
			return true;
		}

		Drive();
		Say( $"FLEEING at {FleeSpeed * Size:0} u/s (legs x{rate:0.##}) to the zombie spawn {_fleeTo.Distance( WorldPosition ):0}u away"
			+ (clear ? "" : $" (none {FleeClear * Size:0} from everyone up that he can reach: the nearest)") );
		return true;
	}

	/// <summary>
	/// Where he runs: of the round's zombie spawns (<see cref="NormalSpawns"/>), where a zombie from each stands
	/// (`ZombieAI.NavGround`), nearest him first — the first <see cref="FleeClear"/> from everyone up, else the nearest.
	/// ⚠️ ONLY ONE HE HAS A WHOLE PATH TO, the nearest <see cref="FleePaths"/> asked (a path is not free). Null, none he can
	/// reach. <paramref name="clear"/>: the spot is clear of everyone. THE HOST.
	/// </summary>
	Vector3? FleeSpot( out bool clear )
	{
		clear = false;
		var from = WorldPosition;
		var start = ZombieAI.NavGround( Scene, from );
		var players = Scene.GetAllComponents<NZPlayer>().Where( IsUp ).Select( p => p.WorldPosition ).ToList();
		var away = FleeClear * Size;

		Vector3? nearest = null;
		var asked = 0;
		foreach ( var s in NormalSpawns().OrderBy( sp => sp.Position.DistanceSquared( from ) ) )
		{
			var isClear = players.All( p => p.Distance( s.Position ) >= away );
			if ( !isClear && nearest is not null ) continue;
			if ( asked++ >= FleePaths ) break;

			var to = ZombieAI.NavGround( Scene, s.Position );
			if ( !Reaches( start, to ) ) continue;

			nearest ??= to;
			if ( !isClear ) continue;
			clear = true;
			return to;
		}

		return nearest;
	}

	/// <summary>A whole path on the navmesh from <paramref name="from"/> to <paramref name="to"/>. No navmesh, no opinion.</summary>
	bool Reaches( Vector3 from, Vector3 to )
	{
		var nav = Scene.NavMesh;
		if ( nav is null || !nav.IsEnabled || from.Distance( to ) < 1f ) return true;
		var path = nav.CalculatePath( new Sandbox.Navigation.CalculatePathRequest { Start = from, Target = to } );
		return path.IsValid && path.Status == Sandbox.Navigation.NavMeshPathStatus.Complete;
	}

	/// <summary>The flight, a frame at a time. THE HOST.</summary>
	void TickFlight()
	{
		switch ( _flight )
		{
			case Flight.Run:
				TickRun();
				break;

			case Flight.Dig:
				// the hold lost before he was gone (nothing of his ends it — a safety): gone at once
				if ( Ai.State != ZombieState.Special ) Vanish();
				break;

			case Flight.Away:
				TickAway();
				break;

			case Flight.Rise:
				// the rise's hold ended early: him back, seen, as the ambush's `EndCrawl`
				if ( Ai.State != ZombieState.Special )
				{
					ClearBeats();
					EndFlight();
				}
				break;
		}
	}

	/// <summary>
	/// The run: into the ground when he is there (within 48 u), out of <see cref="FleeTimeout"/>, or stopped short (under 40 u
	/// gained in half a second: his path ends before the spot). Hurt as ever meanwhile, his table's own. THE HOST.
	/// </summary>
	void TickRun()
	{
		if ( Ai.State != ZombieState.Special )
		{
			Vanish();
			return;
		}

		// ⚠️ CARRIED (a link): its own clip and its own course. Nothing ends the run mid-air, and the agent is told again after.
		if ( Busy )
		{
			_fleeRepathAt = 0f;
			_fleeCheckAt = Time.Now + 0.5f;
			_fleeCheckPos = WorldPosition;
			return;
		}

		// his run back after a link's clip took the renderer
		if ( Body.IsValid() && Body.Sequence.Name != ClipFlee )
		{
			SwapClip( ClipFlee, FleeRate );
			_relayAt = Time.Now + Stride;
		}

		var there = (WorldPosition - _fleeTo).WithZ( 0 ).Length <= FleeArrive * Size && MathF.Abs( WorldPosition.z - _fleeTo.z ) < 72f;
		if ( there ) { Dig( "there" ); return; }
		if ( Time.Now >= _fleeEnds ) { Dig( "out of time" ); return; }
		if ( Time.Now >= _fleeCheckAt )
		{
			if ( WorldPosition.Distance( _fleeCheckPos ) < FleeStall * Size ) { Dig( "stopped short" ); return; }
			_fleeCheckAt = Time.Now + 0.5f;
			_fleeCheckPos = WorldPosition;
		}

		Drive();

		// ⚠️ A RELAYED CLIP PLAYS ONCE on a watching machine (`PlayClipAsPuppet`), then its own walk at its rate cap, sliding: the
		// stride sent again as each one ends
		if ( Networking.IsActive && Time.Now >= _relayAt )
		{
			NZNet.ZombieClip( GameObject.Id, ClipFlee, FleeRate );
			_relayAt = Time.Now + MathF.Max( 0.25f, Stride );
		}
	}

	/// <summary>
	/// His agent at the flight's speed, told where again every quarter second — the AI's own orders (a retarget, a wander) may
	/// have told it otherwise. ⚠️ THE AGENT, NOT THE AI: a hold (`ZombieState.Special`) keeps the AI from chasing or swinging, and
	/// its frame faces him along his course. THE HOST.
	/// </summary>
	void Drive()
	{
		var agent = Components.Get<NavMeshAgent>( FindMode.EverythingInSelfAndDescendants );
		if ( !agent.IsValid() ) return;

		var speed = MathF.Max( 1f, FleeSpeed * Size );
		if ( MathF.Abs( agent.MaxSpeed - speed ) > 0.5f ) agent.MaxSpeed = speed;

		if ( Time.Now < _fleeRepathAt ) return;
		_fleeRepathAt = Time.Now + 0.25f;
		agent.MoveTo( _fleeTo );
	}

	/// <summary>His agent stopped where he stands, as a hold leaves it (`ZombieAI.PlaySpecial`: `StopMoving`, speed 0). THE HOST.</summary>
	void Halt()
	{
		var agent = Components.Get<NavMeshAgent>( FindMode.EverythingInSelfAndDescendants );
		if ( !agent.IsValid() ) return;
		agent.MaxSpeed = 0f;
		agent.MoveTo( WorldPosition );
	}

	/// <summary>
	/// Into the ground where he is: the ambush's burrow (`trav_crawl_exit` — its click, bodyfall, crawls and dirt) at the run's
	/// rate, all of him under the floor at its frame 66 (hidden, untouchable: <see cref="Beat.Under"/>), away at its end
	/// (<see cref="Beat.Gone"/>). THE HOST.
	/// </summary>
	void Dig( string why, bool swap = true )
	{
		_flight = Flight.Dig;
		Halt();

		var r = FleeRate;
		if ( swap ) SwapClip( ClipCrawlOut, r );
		Ai.HoldSpecialFor( CrawlOutLen / r + 1f );

		NZSound.PlayShared( "nz.meuchler.click", Voice );
		Queue( Frames( 10f ) / r, (int)Beat.Bodyfall );
		Queue( Frames( 29f ) / r, (int)Beat.Crawl );
		Queue( Frames( 41f ) / r, (int)Beat.Crawl );
		Queue( Frames( 48f ) / r, (int)Beat.Dirt );
		Queue( Frames( SunkFrame ) / r, (int)Beat.Under );
		Queue( CrawlOutLen / r, (int)Beat.Gone );

		Say( $"fled {WorldPosition.Distance( _fleeFrom ):0}u in {Time.Now - _fleeStart:0.0}s ({why}) — into the ground" );
	}

	/// <summary>Gone at once, the hold lost mid-run or mid-burrow (a safety): a burst where he was, the click, then away. THE HOST.</summary>
	void Vanish()
	{
		ClearBeats();
		if ( !_under ) BossFx.BurstShared( WorldPosition + Vector3.Up * 40f * Size, 120f * Size, Orange, 10 );
		NZSound.PlayShared( "nz.meuchler.click", Voice );
		BeginAway();
	}

	/// <summary>
	/// Away: hidden and untouchable on every machine (the ambush's way, if the burrow had not done it), held at crawl-in's first
	/// frame — all of him under the floor, so not even his shadow shows — his health noted, and the run's hold handed back so the
	/// away can hold at x1 and his rise play at the ambush's pace (<see cref="TickAway"/>). Back in <see cref="FleeMin"/>-<see
	/// cref="FleeMax"/> s. THE HOST.
	/// </summary>
	void BeginAway()
	{
		ClearBeats();
		_flight = Flight.Away;
		_awayHeld = false;

		if ( !_under )
		{
			_under = true;
			SetHidden( true );
			if ( Hp.IsValid() ) Hp.Invulnerable = true;
		}

		Halt();
		if ( Ai.State == ZombieState.Special )
		{
			SwapClip( ClipCrawlIn );
			Ai.CancelSpecial();
		}
		HoldUnder();

		_fleeHp = Hp.IsValid() ? Hp.Current : 0f;
		var lo = MathF.Max( 0f, MathF.Min( FleeMin, FleeMax ) );
		var hi = MathF.Max( lo, MathF.Max( FleeMin, FleeMax ) );
		_backAt = Time.Now + Game.Random.Float( lo, hi );
		_refreshAt = Time.Now + 2f;

		Say( $"AWAY with {_fleeHp:0}/{Hp?.Max ?? 0f:0} hp — back in {_backAt - Time.Now:0.#}s" );
	}

	/// <summary>
	/// ⚠️ THE OTHER MACHINES HOLD HIM AT THE FRAME HE SHOWS, crawl-in's first (`NZNet.ZombieFreeze`, the pratfall's): a relayed clip
	/// plays once and a puppet then walks, and its shadow would stand at the spawn. Re-sent every 2 s while away. THE HOST.
	/// </summary>
	void HoldUnder()
	{
		if ( Networking.IsActive ) NZNet.ZombieFreeze( GameObject.Id, 60f );
	}

	/// <summary>
	/// Away, a frame at a time: his own hold at x1 once the run's has gone (re-armed every frame, so it lasts however long he is
	/// away; the AI neither chases nor swings meanwhile), the pose pinned under the floor, his agent kept still (with nobody to
	/// chase, the AI's wander would walk him), the hiding sent again every 2 s for anyone who joined, and back when the time is
	/// up. THE HOST.
	/// </summary>
	void TickAway()
	{
		if ( Ai.State != ZombieState.Special )
		{
			// ⚠️ THE ONE FRAME THE AI HAS HIM between the two holds — hidden, untouchable, and handed straight back
			if ( !PlayShared( ClipCrawlIn, 5f, 1f ) ) return;
			_awayHeld = true;
			HoldUnder();
		}
		else if ( _awayHeld ) Ai.HoldSpecialFor( 5f );

		// crawl-in's first frame — all of him under the floor — however long the hold runs
		if ( Body.IsValid() && Body.Sequence.Name == ClipCrawlIn ) Body.Sequence.Time = 0f;

		var agent = Components.Get<NavMeshAgent>( FindMode.EverythingInSelfAndDescendants );
		if ( agent.IsValid() && agent.MaxSpeed > 0.01f ) Halt();

		if ( Networking.IsActive && Time.Now >= _refreshAt )
		{
			_refreshAt = Time.Now + 2f;
			SetHidden( true );
			NZNet.ZombieClip( GameObject.Id, ClipCrawlIn, 1f );
			HoldUnder();
		}

		if ( _awayHeld && Time.Now >= _backAt ) ComeBack();
	}

	/// <summary>
	/// Back: up out of the round's zombie spawn nearest a random player who is up (nobody up: again in 2 s), facing them — the
	/// ambush's rise (<see cref="Rise"/>) — with the health he went down with: nothing on the way back sets it, and nothing could
	/// take it while he was away. No zombie spawn open, the ambush's own (any open spawn); none at all, where he went down. THE HOST.
	/// </summary>
	void ComeBack()
	{
		var up = Scene.GetAllComponents<NZPlayer>().Where( IsUp ).ToList();
		if ( up.Count == 0 )
		{
			_backAt = Time.Now + 2f;
			Say( "nobody up to come back for — again in 2s" );
			return;
		}

		var p = Game.Random.FromList( up );
		var spot = StandNear( NormalSpawns(), p.WorldPosition ) ?? ExitNear( p.WorldPosition ) ?? WorldPosition;

		_flight = Flight.Rise;
		_awayHeld = false;

		// ⚠️ HIS OWN MOVES PICK UP FROM HERE: no ambush for another 9.46-13.34 s, as after one of its own
		_nextAmbush = Time.Now + Game.Random.Float( AmbushGapMin, AmbushGapMax );

		// the other machines let go of the held pose before the rise reaches them
		if ( Networking.IsActive ) NZNet.ZombieFreeze( GameObject.Id, 0f );
		Rise( spot, p );

		var hp = Hp.IsValid() ? Hp.Current : 0f;
		Say( $"BACK at the zombie spawn {spot.Distance( p.WorldPosition ):0}u from a player · {hp:0}/{Hp?.Max ?? 0f:0} hp"
			+ (MathF.Abs( hp - _fleeHp ) < 0.5f ? " (as he went)" : $" (!! he went with {_fleeHp:0})") );
	}

	/// <summary>The flight over, however it ended: him seen and touchable, his own moves his again. THE HOST.</summary>
	void EndFlight()
	{
		_flight = Flight.None;
		_awayHeld = false;
		if ( _under ) Surface();
	}

	// ══ the shove ════════════════════════════════════════════════════════════

	/// <summary>The window's count back to 0 when it runs out, the next window 2 s longer for each shove it had (upstream's `ShoveCooldown`).</summary>
	void TickWindow()
	{
		if ( Time.Now <= _windowEnd ) return;
		_windowEnd = Time.Now + ShoveWindow + 2f * _shovesTaken;
		_shovesTaken = 0;
	}

	/// <summary>
	/// Upstream's L4D-style shove: a melee hit from <paramref name="from"/> staggers him away from it — from his right he reels left,
	/// from his left right, from the front back (one time in three the knockdown), from behind forward. Not mid-move, mid-air or
	/// mid-window (<see cref="Busy"/>), and at most <see cref="ShovesPerWindow"/> a window. THE HOST.
	/// </summary>
	void TryShove( Vector3 from )
	{
		if ( _shovesTaken >= ShovesPerWindow || Busy ) return;
		if ( Ai.State is ZombieState.Special or ZombieState.Spawning or ZombieState.Dead ) return;

		var (dot, dot2) = Sides( from );
		var clip = dot2 < -0.5f && dot >= -0.5f ? Stumble + "l"
			: dot2 > 0.5f && dot <= 0.5f ? Stumble + "r"
			: dot < 0f ? (Game.Random.Int( 0, 2 ) == 2 ? ClipKnockdown : Stumble + "f")
			: Stumble + "b";
		if ( clip != ClipKnockdown && Game.Random.Int( 0, 1 ) == 1 ) clip += "_alt";

		var knockdown = clip == ClipKnockdown;
		if ( !PlayShared( clip, knockdown ? KnockdownLen : StumbleLen ) ) return;
		_shovesTaken++;

		NZSound.PlayShared( "nz.meuchler.pain", Voice );
		if ( knockdown )
		{
			// down on the floor at frame 22, and back up on his hands
			Queue( Frames( 22f ), (int)Beat.Fall );
			Queue( Frames( 53f ), (int)Beat.Crawl );
			Queue( Frames( 73f ), (int)Beat.Crawl );
			Queue( Frames( 81f ), (int)Beat.Crawl );
		}
		Say( $"shoved ({clip}) — {_shovesTaken} this window" );
	}

	// ══ the clip he is in ════════════════════════════════════════════════════

	/// <summary>
	/// The clip the AI is playing him in, watched on the host — a model's events never reach code: the later blow of a two-blow
	/// sprint swing, and the click as he climbs (upstream's `asn_vox_click`, frame 8 of his mantle).
	/// </summary>
	void WatchClip()
	{
		if ( !Body.IsValid() ) return;

		var clip = Body.Sequence.Name;
		var t = Body.Sequence.TimeNormalized;
		if ( clip != _clip || t + 0.05f < _clipT )
		{
			// a new play of a clip: another swing, or the same one again
			_clip = clip;
			_blowDone = false;
			if ( clip == ClipMantle )
			{
				NZSound.PlayShared( "nz.meuchler.click", Voice );
				NZSound.PlayShared( "nz.meuchler.crawl", WorldPosition );
			}
		}
		_clipT = t;

		if ( _blowDone || Ai.State != ZombieState.Attacking ) return;
		var at = LaterBlow( clip );
		if ( at <= 0f || t < at ) return;
		_blowDone = true;
		LandLaterBlow();
	}

	/// <summary>Where a sprint swing's second `melee` event falls, as a share of the clip (frame / last frame); -1, none.</summary>
	static float LaterBlow( string clip ) => clip switch
	{
		"nz_s2_zom_asn_sprint_attack_melee_02" => 24f / 38f,
		"nz_s2_zom_asn_sprint_attack_melee_03" => 36f / 51f,
		"nz_s2_zom_asn_sprint_attack_melee_04" => 29f / 48f,
		"nz_s2_zom_asn_sprint_attack_melee_05" => 12f / 26f,
		_ => -1f,
	};

	/// <summary>
	/// The swing's later blow — upstream's `IgnoreImmunity`: it lands through the hit window and opens none (`tick`), within the AI's
	/// own reach. ⛔ ONE SWING, ONE CAP: the AI's first blow counted as landed in full, the two never pass <see cref="ComboCap"/> x
	/// `MaxHit`. THE HOST.
	/// </summary>
	void LandLaterBlow()
	{
		// the AI's own rule: a webbed or stunned swing lands nothing
		if ( StatusEffects.IsDisarmed( GameObject ) ) return;

		var p = TargetPlayer;
		if ( !IsUp( p ) ) return;
		var reach = (Ai.AttackRange + Ai.AttackRangePadding) * Ai.ScaledAttackReach;
		if ( WorldPosition.Distance( p.WorldPosition ) > reach ) return;

		var blow = SwingDamage( Ai.SwingScale );
		var damage = MathF.Min( blow, MaxHit * ComboCap - MathF.Min( blow, MaxHit ) );
		if ( damage < 1f ) return;
		if ( HurtPlayer( p, damage, tick: true, source: "meuchler later blow" ) )
			NZSound.PlayShared( "nz.meuchler.whoosh", WorldPosition + Vector3.Up * 40f * Size );
	}

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

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

	/// <summary>`nz_meuchler` — every Meúchler alive, and the tuning.</summary>
	[ConCmd( "nz_meuchler" )]
	public static void Report()
	{
		Log.Info( $"[nz-meuchler] ambush past {AmbushRange:0} or unseen, x{AmbushChance:0.##} a pass, every {AmbushGapMin:0.##}-{AmbushGapMax:0.##}s,"
			+ $" exit {AmbushGain:0}u nearer · enrage in sight within {EnrageRange:0}, at a hit or after {EnrageAfter:0}s · half health x{HalfSpeed:0.##}"
			+ $" · x{BulletScale:0.###} bullets, x{MeleeScale:0.##} melee, x{OtherScale:0.##} else, x0 for {SpawnProtection:0.#}s"
			+ $" · {ShovesPerWindow:0} shoves a {ShoveWindow:0}s window · a swing x{ComboCap:0.##} the hit cap {MaxHit:0}" );
		Log.Info( $"[nz-meuchler] flight every {Step * 100f:0}% of his health lost · {FleeSpeed:0} u/s (legs x{RunRate:0.##}) to a zombie spawn"
			+ $" {FleeClear:0} from everyone up, {FleeTimeout:0.#}s at most · back in {FleeMin:0.#}-{FleeMax:0.#}s at the zombie spawn nearest a random player"
			+ $" · {NormalSpawns().Count} zombie spawn(s) open" );

		var list = All.ToList();
		Log.Info( $"[nz-meuchler] {list.Count} alive" );
		foreach ( var m in list )
			Log.Info( $"[nz-meuchler]   {(m.Elite ? "elite" : "plain")} · hp {m.Hp?.Current ?? 0f:0}/{m.Hp?.Max ?? 0f:0} · state {m.Ai?.State}"
				+ $" · speed {m.Ai?.MoveSpeed ?? 0f:0}{(m._enraged ? " · ENRAGED" : "")}{(m._halfSpeedOn ? " · HALF HEALTH" : "")}"
				+ (m._flight switch
				{
					Flight.Run or Flight.Dig => " · FLEEING",
					Flight.Away => $" · AWAY (back in {MathF.Max( 0f, m._backAt - Time.Now ):0}s)",
					Flight.Rise => " · back, rising",
					_ => m._fleePending ? " · flight due" : m._under ? " · UNDER THE FLOOR" : m._crawling ? " · crawling" : "",
				})
				+ $" · flees at {m.FleeLeft()}"
				+ $" · next ambush in {MathF.Max( 0f, m._nextAmbush - Time.Now ):0.#}s · shoves {m._shovesTaken} · open spawns {m.OpenSpawns().Count()}" );
	}

	/// <summary>`nz_meuchler_flee` — every Meúchler runs for a zombie spawn now (busy: as soon as he is free). It spends no share of his health.</summary>
	[ConCmd( "nz_meuchler_flee" )]
	public static void FleeCmd()
	{
		if ( NZGame.IsClient ) { Log.Info( "[nz-meuchler] the host decides" ); return; }
		foreach ( var m in All.Where( m => m.Ai.IsValid() && !m.Dead && m._flight == Flight.None ).ToList() )
		{
			m._fleePending = true;
			m._fleeRetryAt = 0f;
			if ( !m._crawling && m.Free && m.BeginFlight() ) continue;
			m.Say( "flight due — he goes as soon as he is free" );
		}
	}

	/// <summary>`nz_meuchler_ambush` — every Meúchler goes into the ground now and comes out near his target (no open spawn: 150 u from them).</summary>
	[ConCmd( "nz_meuchler_ambush" )]
	public static void AmbushCmd()
	{
		if ( NZGame.IsClient ) { Log.Info( "[nz-meuchler] the host decides" ); return; }
		foreach ( var m in All.Where( m => m.Ai.IsValid() && !m.Dead && !m._crawling && !m.Busy && m._flight == Flight.None ).ToList() )
		{
			var p = m.TargetPlayer;
			if ( !IsUp( p ) ) continue;
			var toward = (m.WorldPosition - p.WorldPosition).WithZ( 0 );
			var exit = m.ExitNear( p.WorldPosition )
				?? ZombieAI.NavGround( m.Scene, p.WorldPosition + (toward.Length > 1f ? toward.Normal : Vector3.Forward) * 150f );
			m.BeginAmbush( exit );
		}
	}

	/// <summary>`nz_meuchler_enrage` — every Meúchler enraged now.</summary>
	[ConCmd( "nz_meuchler_enrage" )]
	public static void EnrageCmd()
	{
		if ( NZGame.IsClient ) { Log.Info( "[nz-meuchler] the host decides" ); return; }
		foreach ( var m in All.Where( m => m.Ai.IsValid() && !m.Dead && !m._enraged && !m._crawling && !m.Busy && m._flight == Flight.None ).ToList() )
			m.Enrage();
	}

	/// <summary>`nz_meuchler_shove` — every Meúchler staggers now as if knifed by his target (the window still counts; not mid-flight).</summary>
	[ConCmd( "nz_meuchler_shove" )]
	public static void ShoveCmd()
	{
		if ( NZGame.IsClient ) { Log.Info( "[nz-meuchler] the host decides" ); return; }
		foreach ( var m in All.Where( m => m.Ai.IsValid() && !m.Dead && m._flight == Flight.None ).ToList() )
		{
			var p = m.TargetPlayer;
			m.TryShove( IsUp( p ) ? p.WorldPosition : m.WorldPosition + m.Facing * 50f );
		}
	}

	/// <summary>`nz_meuchler_set &lt;key&gt; &lt;value&gt;` — retune him live.</summary>
	[ConCmd( "nz_meuchler_set" )]
	public static void SetCmd( string key = "", float value = 0f )
	{
		switch ( key.ToLowerInvariant() )
		{
			case "range": AmbushRange = value; break;
			case "chance": AmbushChance = value; break;
			case "gapmin": AmbushGapMin = value; break;
			case "gapmax": AmbushGapMax = value; break;
			case "gain": AmbushGain = value; break;
			case "enrage": EnrageRange = value; break;
			case "after": EnrageAfter = value; break;
			case "half": HalfSpeed = value; break;
			case "bullet": BulletScale = value; break;
			case "melee": MeleeScale = value; break;
			case "other": OtherScale = value; break;
			case "protect": SpawnProtection = value; break;
			case "window": ShoveWindow = value; break;
			case "shoves": ShovesPerWindow = value; break;
			case "combo": ComboCap = value; break;
			case "fleespeed": FleeSpeed = value; break;
			case "fleemin": FleeMin = value; break;
			case "fleemax": FleeMax = value; break;
			case "fleestep": FleeStep = value; break;
			case "fleetime": FleeTimeout = value; break;
			case "fleeclear": FleeClear = value; break;
			default:
				Log.Info( "[nz-meuchler] nz_meuchler_set <range|chance|gapmin|gapmax|gain|enrage|after|half|bullet|melee|other|protect|window|shoves|combo"
					+ "|fleespeed|fleemin|fleemax|fleestep|fleetime|fleeclear> <value>" );
				return;
		}
		Log.Info( $"[nz-meuchler] {key} = {value:0.###}" );
		Report();
	}
}