UI/PowerTremor.cs

Static utility controlling a local ‘power tremor’ effect when power turns on. It stores tunable nullable-backed settings (Enabled, Level, Seconds), triggers camera rumble, plays a UI sound and sheds ceiling dust, and exposes a console command to tweak or toggle it.

NetworkingFile Access
using Sandbox;

namespace NZombies;

/// <summary>
/// THE POWER'S TREMOR — as the power comes on, the ground shakes and the ceiling sheds its dust: *"make the map shake on power on
/// with the dust on the ceiling too"* (2026-09-28). The spawn-in's own parts (`SpawnTremor`): a held rumble of the view
/// (`CameraShake.Rumble`), the tremor's rumble of moving rock, and `CeilingDust` over this machine's player.
///
/// ⚠️ EACH MACHINE, ITS OWN VIEW, AND NOTHING SENT. `Power` calls it where the power comes on for that machine: `ApplyPowered` on
/// the host (and solo), `TurnOnFromHost` on a client, on the host's own word.
/// ⚠️ ONCE, WHEN THE POWER COMES ON — not for each lever of several (`Power.Flip` plays the lever's clunk until the last), and not
/// on a map with no switch, where the power is simply on from the start.
/// ⚠️ THE TUNABLES ARE NULLABLE-BACKED, as `SpawnTremor`'s are (INSTRUCTIONS §1).
/// </summary>
public static class PowerTremor
{
	/// <summary>`nz_power_tremor 0` switches it off (for this session).</summary>
	public static bool Enabled
	{
		get => _enabled ?? true;
		set => _enabled = value;
	}

	static bool? _enabled;

	/// <summary>How hard the view shakes at its height, as trauma (0-1) — a touch above the spawn-in's 0.08: the whole map waking.</summary>
	public static float Level
	{
		get => _level ?? 0.1f;
		set => _level = value;
	}

	static float? _level;

	/// <summary>How long it shakes, easing in and then out over its last half, as the spawn-in's does.</summary>
	public static float Seconds
	{
		get => _seconds ?? 4f;
		set => _seconds = value;
	}

	static float? _seconds;

	/// <summary>
	/// The power's, from `Power`: only on a map whose config asks for it (`Gameplay.PowerOnTremor`, basalt's).
	///
	/// ⛔ IT HAD NO SUCH CHECK and shook every map with a switch (2026-10-01). `nz_power_tremor` still plays it anywhere, to feel it.
	/// </summary>
	public static void OnPowerOn()
	{
		if ( ActiveConfig.Current?.Gameplay?.PowerOnTremor != true ) return;
		Play();
	}

	/// <summary>The tremor, now, on this machine: the view shaking, the rock moving, the dust falling.</summary>
	public static void Play()
	{
		if ( !Enabled ) return;

		CameraShake.Rumble( Level, Seconds );
		NZSound.PlayUi( SpawnTremor.Cue );
		CeilingDust.Shed( Seconds );

		Log.Info( $"[nz-power] the power's tremor — shaking at {Level:0.00} for {Seconds:0.#}s, the ceiling shedding" );
	}

	/// <summary>
	/// `nz_power_tremor [level] [seconds]` — the power's tremor now, to feel it; numbers retune it first (this session). `nz_power_tremor 0`
	/// switches it off, `nz_power_tremor 1` back on.
	/// </summary>
	[ConCmd( "nz_power_tremor" )]
	public static void Cmd( float level = -1f, float seconds = -1f )
	{
		if ( level == 0f ) { Enabled = false; Log.Info( "[nz-power] the power's tremor is OFF — nz_power_tremor 1 for it back" ); return; }
		if ( level == 1f && seconds < 0f ) { Enabled = true; Log.Info( "[nz-power] the power's tremor is ON" ); Play(); return; }

		if ( level > 0f ) Level = level;
		if ( seconds > 0f ) Seconds = seconds;

		Enabled = true;
		Play();
	}
}