Static helper that tracks whether this copy is treated as published or an editor preview, controls a persistent "preview" (player-side) flag, and provides console commands to toggle it. It updates related systems when switching, like forgetting map headers, gamemodes, changing game mode out of Creative, and opening the lobby.
using Sandbox;
namespace NZombies;
/// <summary>
/// WHICH COPY OF THE GAME THIS IS: the editor, where maps are built, or the published game, where they are played.
///
/// ⛔ THE USER, 2026-10-05: *"in the editor I want to be able to edit maps as usual, but not in the published version … there
/// is no load and save config, instead it will be replaced with a 'gamemode' tab where I can select the config"*. So a
/// published copy has no Creative, no build menu (Q) and no Load, Save or Delete config. Its lobby lists the configs the map
/// SHIPS as gamemodes (`Gamemodes`), and it reads no config but those (`MapConfig.Readers`).
///
/// ⚠️ `Game.IsEditor` DECIDES, AND NOTHING TYPED IN A PUBLISHED COPY CHANGES IT. `nz_side` switches the editor between its own
/// side and the player's, so the published lobby can be tried before a publish (*"A command that switched between the
/// developer side and player side"*). In a published copy the command refuses: a console switch back to building would be
/// one every player could type.
/// </summary>
public static class Edition
{
/// <summary>The published game, or the editor on the player's side (<see cref="Preview"/>).</summary>
public static bool IsPublished => !Game.IsEditor || Preview;
/// <summary>May maps be built here: Creative, the build menu, Load / Save / Delete config? In the editor, on its own side.</summary>
public static bool CanBuild => !IsPublished;
/// <summary>
/// The editor on the player's side (`nz_side`).
///
/// ⚠️ A STATIC, SO IT OUTLIVES A HOTLOAD AND A PLAY RESTART: the player's side stays on until it is switched back, which
/// is what trying the lobby over several restarts wants. The lobby says so while it is on (`LobbyMenu`, `.sidetag`), or
/// a lobby with no Creative in it would look broken.
/// </summary>
public static bool Preview { get; private set; }
/// <summary>The editor on the player's side: what the lobby's tag is drawn for. Never true in a published copy.</summary>
public static bool Previewing => Game.IsEditor && Preview;
/// <summary>
/// `nz_side [player|dev]` — switch the editor between the developer's side (its own lobby: Load/Save config, Creative, the
/// build menu) and the player's (the published game's lobby: a Gamemode row, nothing to build). No argument flips it.
/// </summary>
[ConCmd( "nz_side" )]
public static void SideCmd( string side = "" )
{
var s = (side ?? "").Trim().ToLowerInvariant();
if ( s is "" or "toggle" or "flip" ) Switch( !Preview );
else if ( s is "player" or "players" or "p" or "published" or "1" ) Switch( true );
else if ( s is "dev" or "developer" or "editor" or "d" or "0" ) Switch( false );
else Log.Info( $"[nz-side] '{side}'? nz_side flips it; nz_side player or nz_side dev picks one" );
}
/// <summary>`nz_published [0|1]` — the same switch under its first name. No argument flips it.</summary>
[ConCmd( "nz_published" )]
public static void PreviewCmd( int on = -1 ) => Switch( on < 0 ? !Preview : on != 0 );
/// <summary>
/// Put the editor on the player's side, or back on its own, and show the lobby so the change can be seen at once.
/// </summary>
public static void Switch( bool player )
{
if ( !Game.IsEditor )
{
Log.Info( "[nz-side] this is the published game: the player's side is all it has — maps are built in the editor" );
return;
}
Preview = player;
// ⚠️ EVERY HEADER READ SO FAR GOES, EVEN WHEN NOTHING CHANGED. What the lists read changes with the switch (the
// player's lobby reads only what ships), and switching to the player's side again is how a gamemode name that
// `ship_data.py` has just copied into Assets shows up.
MapConfig.ForgetHeaders();
Gamemodes.Forget();
// ⚠️ OUT OF CREATIVE, which the published game has none of, and into the lobby, which `SetMode` opens. The config
// stays as it is, unsaved edits and all: the player's side loads a gamemode only where nothing playable is loaded.
if ( player && NZGame.IsCreative ) NZGame.SetMode( GameMode.Lobby );
// ⚠️ AND THE LOBBY UP, from wherever this was typed, so the side just switched to is what is on screen
LobbyState.SetOpen?.Invoke( true );
Log.Info( player
? "[nz-side] PLAYER SIDE — the published game's lobby: a Gamemode row in place of Load/Save config, no Creative, "
+ "no build menu. It lists Assets/configs, which is what ships: ship_data.py --apply for configs saved since. "
+ "nz_side to go back"
: "[nz-side] DEVELOPER SIDE — the editor's lobby: Load/Save config, Creative and the build menu. nz_side for the player's" );
}
}