Console command helpers for the building table Easter egg. Provides commands to place a build table at the player's feet, list configured tables, clear all configured tables, and force a rebuild of table entities from the config.
using Sandbox;
namespace NZombies;
/// <summary>
/// Console access to the building table.
///
/// ⚠️ EVERY BUTTON GETS ONE — the Q-menu tool and these commands place the same config record, so
/// a table dropped either way saves and loads identically. The tool is for aiming at a spot; the
/// console is for dropping one where you stand without leaving the keyboard.
/// </summary>
public static class BuildTableCommands
{
static NZPlayer Player
=> PlayerCharacters.Local()
?? NZPlayer.Local;
/// <summary>Drop one at your feet: `nz_buildtable`.</summary>
[ConCmd( "nz_buildtable" )]
public static void Place()
{
var p = Player;
if ( !p.IsValid() ) { Log.Warning( "[nz-build] no player" ); return; }
var cfg = ActiveConfig.Current;
if ( cfg is null ) { Log.Warning( "[nz-build] no config" ); return; }
// ⚠️ `+ 180` SO IT FACES YOU. The yaw stored is the table's heading, and dropping one at
// your feet with your own heading puts its back to you — the same turn `nz_trade` makes.
cfg.BuildTables.Add( new BuildTableSpot
{
Position = p.WorldPosition,
Yaw = p.EyeAngles.yaw + 180f,
Normal = Vector3.Up,
} );
BuildTableManager.Ensure()?.Rebuild();
Log.Info( $"[nz-build] placed — {cfg.BuildTables.Count} total" );
}
/// <summary>What is placed: `nz_buildtable_list`.</summary>
[ConCmd( "nz_buildtable_list" )]
public static void List()
{
var list = ActiveConfig.Current?.BuildTables;
if ( list is null || list.Count == 0 )
{
Log.Info( "[nz-build] none placed — nz_buildtable, or"
+ " Q > Easter egg > Collection > Building table" );
return;
}
for ( int i = 0; i < list.Count; i++ )
{
var s = list[i];
// ⚠ THE MODE AND THE SPENT FLAG BOTH SHOW, because a one-time bench that has already
// been claimed is indistinguishable from an unbuilt one by walking up to it — it is
// silent on purpose unless you are carrying a full set.
var live = BuildTable.Find( i );
Log.Info( $"[nz-build] [{i}] {s.Position} facing {s.Yaw:0}°"
+ (s.Permanent ? " permanent" : " one-time")
+ (s.RequiresPower ? " ⚡power" : "")
+ $" flag {DoorLinks.Display( s.Link )}"
+ (live is null ? " (not standing)"
: live.Spent ? " — SPENT"
: live.Built ? $" — holding the {BuildParts.WeaponName}" : "") );
}
// ⚠️ STANDING vs CONFIGURED, said separately. A model that fails to load leaves a spot in
// the config and nothing in the world; one number would hide that.
Log.Info( $"[nz-build] {list.Count} configured, "
+ $"{BuildTableManager.Instance?.Built ?? 0} standing" );
}
/// <summary>Remove them all: `nz_buildtable_clear`.</summary>
[ConCmd( "nz_buildtable_clear" )]
public static void Clear()
{
var list = ActiveConfig.Current?.BuildTables;
if ( list is null || list.Count == 0 ) { Log.Info( "[nz-build] none placed" ); return; }
var n = list.Count;
list.Clear();
BuildTableManager.Ensure()?.Rebuild();
Log.Info( $"[nz-build] cleared {n}" );
}
/// <summary>
/// Rebuild them from the config: `nz_buildtable_rebuild`.
/// </summary>
///
/// ⚠️ IT EXISTS FOR THE MODEL. The one failure this placeable has is the mesh not loading, and
/// after fixing a path you want to see it without reloading the map.
[ConCmd( "nz_buildtable_rebuild" )]
public static void RebuildCmd()
{
BuildTableManager.Ensure()?.Rebuild();
Log.Info( $"[nz-build] model {BuildTableManager.ModelPath}" );
}
}