Component attached to the Shrek joke boss. Controls summoning Helldonkey minions, an enrage roar, invulnerability during summons, tuning parameters, and diagnostic console commands.
using Sandbox;
using System;
using System.Collections.Generic;
using System.Linq;
namespace NZombies;
/// <summary>
/// SHREK — the joke boss nZombies Rezzurrection never finished (`nz_zombie_boss_shrek`; its model's .vtx was never shipped),
/// for whimsical maps: *"defocus is a very wimsy map full of color and kind of unserious, it's the correct map to use joke
/// bosses and specials"* (2026-10-01).
///
/// His body is Brutus's. `shrek.vmdl` is Brutus's skeleton, hitboxes and clips with the SuperSlam Shrek mesh rebuilt onto
/// them, so he walks, swings and dies as Brutus does, and `shrek.zvar` sings as he arrives and taunts as he comes. This adds
/// what the Lua adds on top: **"I SUMMON... DONKEY!"** Now and then, with his target near, he stops, sings, and two
/// Helldonkeys appear at his sides. He can't be hurt until the summon ends.
///
/// ⚠️ THE HOST'S ALONE. `ZombieAI.Target` is only ever set on the host (everyone else watches a puppet), so the host decides,
/// spawns the donkeys (`ZombieCommands.SpawnAt` network-spawns them) and sends the song with `PlayShared`.
/// ⚠️ THE TUNABLES ARE NULLABLE-BACKED (INSTRUCTIONS §1).
/// </summary>
public sealed class ShrekBoss : Component
{
/// <summary>The summon: Brutus's own `summondogs`, which Shrek's body plays as it is.</summary>
public const string SummonClip = "nz_base_zombie_cellbreaker_summondogs";
/// <summary>The song he sings as he summons.</summary>
public const string SongCue = "nz.shrek.song";
/// <summary>The line he says as he arrives, over his song (the Lua's `taunt1` at spawn).</summary>
public const string ArriveCue = "nz.shrek.arrive";
/// <summary>The line he shouts as he enrages (the Lua's `taunt2`, once, when he is badly hurt).</summary>
public const string EnrageCue = "nz.shrek.enrage";
/// <summary>The enrage: Brutus's own `enrage_start` roar, 51 frames at 30 fps.</summary>
public const string EnrageClip = "nz_base_zombie_cellbreaker_enrage_start";
/// <summary>How long the enrage roar roots him.</summary>
public static float EnrageSeconds { get => _enrageSeconds ?? 1.7f; set => _enrageSeconds = value; }
static float? _enrageSeconds;
/// <summary>
/// The share of his health below which he enrages, once.
///
/// ⚠️ A SHARE, NOT THE LUA'S 2,000. Upstream's boss health is its own; ours scales with the round, so a fixed number
/// would enrage him at once on a late round and never on an early one.
/// </summary>
public static float EnrageAt { get => _enrageAt ?? 0.35f; set => _enrageAt = value; }
static float? _enrageAt;
/// <summary>
/// His speed once enraged, absolute: bare Brutus's 220. The Lua's 300 against its 150–180 was the same jump of a
/// little under double.
/// </summary>
public static float EnragedSpeed { get => _enragedSpeed ?? 220f; set => _enragedSpeed = value; }
static float? _enragedSpeed;
/// <summary>How long the summon roots him, untouchable: the clip's 110 frames at 30 fps.</summary>
public static float SummonSeconds { get => _summonSeconds ?? 3.6f; set => _summonSeconds = value; }
static float? _summonSeconds;
/// <summary>Seconds after he arrives before the first summon can come.</summary>
public static float FirstSummonDelay { get => _firstDelay ?? 8f; set => _firstDelay = value; }
static float? _firstDelay;
/// <summary>The cooldown after a summon, rolled between these two: the Lua's 10–13 s.</summary>
public static float CooldownMin { get => _cooldownMin ?? 10f; set => _cooldownMin = value; }
static float? _cooldownMin;
/// <summary>See <see cref="CooldownMin"/>.</summary>
public static float CooldownMax { get => _cooldownMax ?? 13f; set => _cooldownMax = value; }
static float? _cooldownMax;
/// <summary>Once he has cooled down, the chance each second that he summons: the Lua's one in six.</summary>
public static float ChancePerSecond { get => _chance ?? 1f / 6f; set => _chance = value; }
static float? _chance;
/// <summary>How near his target must be, in units, for him to bother.</summary>
public static float Reach { get => _reach ?? 1000f; set => _reach = value; }
static float? _reach;
/// <summary>How far to each side the donkeys appear: the Lua's 65.</summary>
public static float Spread { get => _spread ?? 65f; set => _spread = value; }
static float? _spread;
/// <summary>
/// The most of HIS donkeys alive at once.
///
/// ⚠️ NOT IN THE LUA, which has no cap. It is here so a long fight cannot fill the map: a summon that would pass it is
/// skipped, and its cooldown starts anyway.
/// </summary>
public static int MaxDonkeys { get => _maxDonkeys ?? 6; set => _maxDonkeys = value; }
static int? _maxDonkeys;
ZombieAI _ai;
Health _hp;
float _cooldown;
float _nextRoll;
float _invulnerableUntil;
bool _enraged;
TimeSince _alive;
readonly List<GameObject> _donkeys = new();
protected override void OnStart()
{
_ai = Components.Get<ZombieAI>( FindMode.EverythingInSelfAndAncestors );
_hp = Components.Get<Health>( FindMode.EverythingInSelfAndAncestors );
_cooldown = FirstSummonDelay;
_alive = 0f;
// ⚠️ THE HOST SAYS IT FOR EVERYONE, so a client's copy of this component (`ZombieAI` attaches it on every
// machine) must not say it again.
if ( NZGame.IsHost )
NZSound.PlayShared( ArriveCue, WorldPosition + Vector3.Up * 60f );
}
protected override void OnDestroy()
{
if ( _hp.IsValid() && _invulnerableUntil > 0f ) _hp.Invulnerable = false;
}
protected override void OnUpdate()
{
if ( !_ai.IsValid() || !_hp.IsValid() ) return;
// ⛔ NEVER LEFT UNTOUCHABLE. However the summon ended — played out, cancelled, the clip refused — the flag comes off
// on time.
if ( _invulnerableUntil > 0f && Time.Now >= _invulnerableUntil )
{
_hp.Invulnerable = false;
_invulnerableUntil = 0f;
}
if ( NZGame.IsClient || _hp.IsDead || _ai.InSpecial ) return;
// ⚠️ THE ENRAGE WAITS FOR A SUMMON TO END (the `InSpecial` return above), so the roar can never cut the song
// short or free him from the summon's pose early.
// ⚠️ NOT IN HIS FIRST SECONDS: his health is filled in as he spawns, and a reading taken before that would enrage
// him on arrival.
if ( !_enraged && _alive > 2f && _hp.Max > 0f && _hp.Current < _hp.Max * EnrageAt )
{
Enrage();
return;
}
if ( _cooldown > 0f )
{
_cooldown -= Time.Delta;
return;
}
if ( Time.Now < _nextRoll ) return;
_nextRoll = Time.Now + 1f;
var target = _ai.Target;
if ( !target.IsValid() ) return;
if ( _ai.WorldPosition.Distance( target.WorldPosition ) > Reach ) return;
if ( Game.Random.Float() > ChancePerSecond ) return;
Summon();
}
/// <summary>
/// "I SUMMON... DONKEY!" — now, target or no target.
///
/// ⚠️ SPLIT OUT SO `nz_shrek_summon` CAN REACH IT. In Creative nothing gives him a target, so without the command the
/// summon could only be seen by fighting him properly.
/// </summary>
public void Summon()
{
if ( NZGame.IsClient || !_ai.IsValid() || !_hp.IsValid() || _hp.IsDead ) return;
_cooldown = Game.Random.Float( CooldownMin, MathF.Max( CooldownMin, CooldownMax ) );
_donkeys.RemoveAll( d => !d.IsValid() );
var room = MaxDonkeys - _donkeys.Count;
if ( room <= 0 )
{
Log.Info( $"[nz-shrek] summon skipped — {_donkeys.Count} of his donkeys still alive (cap {MaxDonkeys})" );
return;
}
// ⚠️ IF THE CLIP WILL NOT PLAY THE DONKEYS STILL COME. `PlaySpecial` refuses on a model that lacks the clip and
// says so; losing the animation should cost the pose, not the summon.
_ai.PlaySpecial( SummonClip, SummonSeconds );
_hp.Invulnerable = true;
_invulnerableUntil = Time.Now + SummonSeconds;
NZSound.PlayShared( SongCue, WorldPosition + Vector3.Up * 60f );
var variant = SpecialEnemies.VariantFor( SpecialEnemies.Helldonkey );
var scene = Scene;
var came = 0;
if ( variant is not null && scene.IsValid() )
{
foreach ( var side in new[] { 1f, -1f } )
{
if ( came >= room ) break;
var pos = WorldPosition + WorldRotation.Right * Spread * side;
var z = ZombieCommands.SpawnAt( scene, pos, variant );
if ( z is null ) continue;
z.WorldRotation = WorldRotation;
z.GameObject.Name = SpecialEnemies.Helldonkey;
_donkeys.Add( z.GameObject );
came++;
}
}
else
Log.Warning( "[nz-shrek] no Helldonkey to summon — zombies/helldonkey.zvar did not load" );
Log.Info( $"[nz-shrek] I SUMMON... DONKEY! — {came} came, untouchable for {SummonSeconds:0.#}s,"
+ $" next in {_cooldown:0.#}s" );
}
/// <summary>
/// The enrage, once: he roars, shouts, and runs at <see cref="EnragedSpeed"/> from then on.
///
/// ⛔ AN ABSOLUTE SPEED THROUGH `SpeedOverride`, AS BRUTUS'S HELMET BREAK SETS HIS, and `RepickAnimations` to make
/// it take: the speed is only derived when animations are picked (see `BrutusHelmet.Break`).
/// </summary>
public void Enrage()
{
if ( NZGame.IsClient || _enraged || !_ai.IsValid() || !_hp.IsValid() || _hp.IsDead ) return;
_enraged = true;
// ⚠️ IF THE CLIP WILL NOT PLAY HE STILL SPEEDS UP: losing the roar should cost the pose, not the enrage.
_ai.PlaySpecial( EnrageClip, EnrageSeconds );
NZSound.PlayShared( EnrageCue, WorldPosition + Vector3.Up * 60f );
_ai.SpeedOverride = MathF.Max( 1f, EnragedSpeed );
_ai.RepickAnimations();
Log.Info( $"[nz-shrek] ENRAGED at {_hp.Current:0} of {_hp.Max:0} — {EnragedSpeed:0} u/s from now on" );
}
// ══ diagnostics ══════════════════════════════════════════════════════════
/// <summary>`nz_shrek` — every Shrek alive, and the summon's tuning.</summary>
[ConCmd( "nz_shrek" )]
public static void Report()
{
Log.Info( $"[nz-shrek] summon every {CooldownMin:0.#}–{CooldownMax:0.#}s, then 1 in {1f / MathF.Max( ChancePerSecond, 0.001f ):0.#}"
+ $" a second within {Reach:0}u · {SummonSeconds:0.#}s untouchable · 2 at ±{Spread:0}u, at most {MaxDonkeys} of his alive"
+ $" · enrages below {EnrageAt * 100f:0}% health, then {EnragedSpeed:0} u/s" );
var all = Game.ActiveScene?.GetAllComponents<ShrekBoss>().ToList() ?? new List<ShrekBoss>();
Log.Info( $"[nz-shrek] {all.Count} alive" );
foreach ( var s in all )
{
s._donkeys.RemoveAll( d => !d.IsValid() );
Log.Info( $"[nz-shrek] {s.GameObject.Name} · health {s._hp?.Current ?? 0:0} · cooldown {MathF.Max( s._cooldown, 0f ):0.#}s"
+ $" · {s._donkeys.Count} donkey(s) alive{(s._invulnerableUntil > 0f ? " · SUMMONING" : "")}"
+ (s._enraged ? " · ENRAGED" : "") );
}
}
/// <summary>`nz_shrek_enrage` — every Shrek alive enrages now (host).</summary>
[ConCmd( "nz_shrek_enrage" )]
public static void EnrageCmd()
{
if ( NZGame.IsClient ) { Log.Warning( "[nz-shrek] the host decides the enrage — run it on the host" ); return; }
var all = Game.ActiveScene?.GetAllComponents<ShrekBoss>().ToList() ?? new List<ShrekBoss>();
if ( all.Count == 0 ) { Log.Info( "[nz-shrek] no Shrek alive — nz_special_spawn shrek, or a boss round" ); return; }
foreach ( var s in all ) s.Enrage();
}
/// <summary>`nz_shrek_summon` — every Shrek alive summons now (host).</summary>
[ConCmd( "nz_shrek_summon" )]
public static void SummonCmd()
{
if ( NZGame.IsClient ) { Log.Warning( "[nz-shrek] the host decides the summon — run it on the host" ); return; }
var all = Game.ActiveScene?.GetAllComponents<ShrekBoss>().ToList() ?? new List<ShrekBoss>();
if ( all.Count == 0 ) { Log.Info( "[nz-shrek] no Shrek alive — nz_special_spawn shrek, or a boss round" ); return; }
foreach ( var s in all ) s.Summon();
}
}