diff --git a/src/command.c b/src/command.c
index e525f5e9743176884231efe77b7bc997b08ec3fc..d72f845ad37cfbf942ed278990ecac749382c44e 100644
--- a/src/command.c
+++ b/src/command.c
@@ -1561,34 +1561,27 @@ void CV_AddValue(consvar_t *var, INT32 increment)
 			if (var == &cv_chooseskin)
 			{
 				// Special case for the chooseskin variable, used only directly from the menu
-				if (increment > 0) // Going up!
+				newvalue = var->value - 1;
+				do
 				{
-					newvalue = var->value - 1;
-					do
+					if (increment > 0) // Going up!
 					{
 						newvalue++;
 						if (newvalue == MAXSKINS)
 							newvalue = 0;
-					} while (var->PossibleValue[newvalue].strvalue == NULL);
-					var->value = newvalue + 1;
-					var->string = var->PossibleValue[newvalue].strvalue;
-					var->func();
-					return;
-				}
-				else if (increment < 0) // Going down!
-				{
-					newvalue = var->value - 1;
-					do
+					}
+					else if (increment < 0) // Going down!
 					{
 						newvalue--;
 						if (newvalue == -1)
 							newvalue = MAXSKINS-1;
-					} while (var->PossibleValue[newvalue].strvalue == NULL);
-					var->value = newvalue + 1;
-					var->string = var->PossibleValue[newvalue].strvalue;
-					var->func();
-					return;
-				}
+					}
+				} while (var->PossibleValue[newvalue].strvalue == NULL);
+
+				var->value = newvalue + 1;
+				var->string = var->PossibleValue[newvalue].strvalue;
+				var->func();
+				return;
 			}
 #ifdef PARANOIA
 			if (currentindice == -1)
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 3013fef8201637a792535699cdc3e4a86a0bc573..6b97eb5b7c7eae08460a26476ef69ffb92b18a36 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1549,10 +1549,13 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese
 	// The supplied data are assumed to be good.
 	I_Assert(delay >= 0 && delay <= 2);
 
+	if (mapnum != -1)
+		CV_SetValue(&cv_nextmap, mapnum);
+
 	CONS_Debug(DBG_GAMELOGIC, "Map change: mapnum=%d gametype=%d ultmode=%d resetplayers=%d delay=%d skipprecutscene=%d\n",
 	           mapnum, newgametype, pultmode, resetplayers, delay, skipprecutscene);
 
-	if (netgame || multiplayer)
+	if ((netgame || multiplayer) && !((gametype == newgametype) && (newgametype == GT_COOP)))
 		FLS = false;
 
 	if (delay != 2)
@@ -1718,9 +1721,19 @@ static void Command_Map_f(void)
 		}
 	}
 
+	// Prevent warping to locked levels
+	// ... unless you're in a dedicated server.  Yes, technically this means you can view any level by
+	// running a dedicated server and joining it yourself, but that's better than making dedicated server's
+	// lives hell.
+	if (!dedicated && M_MapLocked(newmapnum))
+	{
+		CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
+		return;
+	}
+
 	// don't use a gametype the map doesn't support
 	if (cv_debug || COM_CheckParm("-force") || cv_skipmapcheck.value)
-		; // The player wants us to trek on anyway.  Do so.
+		fromlevelselect = false; // The player wants us to trek on anyway.  Do so.
 	// G_TOLFlag handles both multiplayer gametype and ignores it for !multiplayer
 	// Alternatively, bail if the map header is completely missing anyway.
 	else if (!mapheaderinfo[newmapnum-1]
@@ -1739,19 +1752,10 @@ static void Command_Map_f(void)
 		CONS_Alert(CONS_WARNING, M_GetText("%s doesn't support %s mode!\n(Use -force to override)\n"), mapname, gametypestring);
 		return;
 	}
+	else
+		fromlevelselect = ((netgame || multiplayer) && ((gametype == newgametype) && (newgametype == GT_COOP)));
 
-	// Prevent warping to locked levels
-	// ... unless you're in a dedicated server.  Yes, technically this means you can view any level by
-	// running a dedicated server and joining it yourself, but that's better than making dedicated server's
-	// lives hell.
-	if (!dedicated && M_MapLocked(newmapnum))
-	{
-		CONS_Alert(CONS_NOTICE, M_GetText("You need to unlock this level before you can warp to it!\n"));
-		return;
-	}
-
-	fromlevelselect = false;
-	D_MapChange(newmapnum, newgametype, false, newresetplayers, 0, false, false);
+	D_MapChange(newmapnum, newgametype, false, newresetplayers, 0, false, fromlevelselect);
 }
 
 /** Receives a map command and changes the map.
@@ -1817,17 +1821,14 @@ static void Got_Mapcmd(UINT8 **cp, INT32 playernum)
 	if (demoplayback && !timingdemo)
 		precache = false;
 
-	if (resetplayer)
-	{
-		if (!FLS || (netgame || multiplayer))
-			emeralds = 0;
-	}
+	if (resetplayer && !FLS)
+		emeralds = 0;
 
 #ifdef HAVE_BLUA
 	LUAh_MapChange();
 #endif
 
-	G_InitNew(ultimatemode, mapname, resetplayer, skipprecutscene);
+	G_InitNew(ultimatemode, mapname, resetplayer, skipprecutscene, FLS);
 	if (demoplayback && !timingdemo)
 		precache = true;
 	CON_ToggleOff();
diff --git a/src/dehacked.c b/src/dehacked.c
index fa246b88c0eb5438e2d32822ca32c545cf11d38a..32b1c1ca30cbc29c1a74557272dd756648e8f77d 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -1203,6 +1203,12 @@ static void readlevelheader(MYFILE *f, INT32 num)
 			{
 				deh_strlcpy(mapheaderinfo[num-1]->lvlttl, word2,
 					sizeof(mapheaderinfo[num-1]->lvlttl), va("Level header %d: levelname", num));
+				strlcpy(mapheaderinfo[num-1]->selectheading, word2, sizeof(mapheaderinfo[num-1]->selectheading)); // not deh_ so only complains once
+			}
+			else if (fastcmp(word, "SELECTHEADING"))
+			{
+				deh_strlcpy(mapheaderinfo[num-1]->selectheading, word2,
+					sizeof(mapheaderinfo[num-1]->selectheading), va("Level header %d: selectheading", num));
 			}
 			else if (fastcmp(word, "SCRIPTNAME"))
 			{
@@ -1410,6 +1416,13 @@ static void readlevelheader(MYFILE *f, INT32 num)
 				else
 					mapheaderinfo[num-1]->menuflags &= ~LF2_NOVISITNEEDED;
 			}
+			else if (fastcmp(word, "WIDEICON"))
+			{
+				if (i || word2[0] == 'T' || word2[0] == 'Y')
+					mapheaderinfo[num-1]->menuflags |= LF2_WIDEICON;
+				else
+					mapheaderinfo[num-1]->menuflags &= ~LF2_WIDEICON;
+			}
 			else
 				deh_warning("Level header %d: unknown word '%s'", num, word);
 		}
@@ -7298,6 +7311,7 @@ struct {
 	{"LF2_RECORDATTACK",LF2_RECORDATTACK},
 	{"LF2_NIGHTSATTACK",LF2_NIGHTSATTACK},
 	{"LF2_NOVISITNEEDED",LF2_NOVISITNEEDED},
+	{"LF2_WIDEICON",LF2_WIDEICON},
 
 	// NiGHTS grades
 	{"GRADE_F",GRADE_F},
@@ -7670,6 +7684,7 @@ struct {
 	{"V_70TRANS",V_70TRANS},
 	{"V_80TRANS",V_80TRANS},
 	{"V_90TRANS",V_90TRANS},
+	{"V_STATIC",V_STATIC},
 	{"V_HUDTRANSHALF",V_HUDTRANSHALF},
 	{"V_HUDTRANS",V_HUDTRANS},
 	{"V_HUDTRANSDOUBLE",V_HUDTRANSDOUBLE},
diff --git a/src/doomstat.h b/src/doomstat.h
index f1b7d2169516c2020ecd20fdd0b12cae7a356234..7ee0382b25a0c01abc08b70877b9ffcab9b7c5dd 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -241,6 +241,8 @@ typedef struct
 	UINT8 levelflags;     ///< LF_flags:  merged eight booleans into one UINT8 for space, see below
 	UINT8 menuflags;      ///< LF2_flags: options that affect record attack / nights mode menus
 
+	char selectheading[22]; ///< Level select heading. Allows for controllable grouping.
+
 	// Freed animals stuff.
 	UINT8 numFlickies;     ///< Internal. For freed flicky support.
 	mobjtype_t *flickies;  ///< List of freeable flickies in this level. Allocated dynamically for space reasons. Be careful.
@@ -267,6 +269,7 @@ typedef struct
 #define LF2_RECORDATTACK   4 ///< Show this map in Time Attack
 #define LF2_NIGHTSATTACK   8 ///< Show this map in NiGHTS mode menu
 #define LF2_NOVISITNEEDED 16 ///< Available in time attack/nights mode without visiting the level
+#define LF2_WIDEICON      32 ///< If you're in a circumstance where it fits, use a wide map icon
 
 extern mapheader_t* mapheaderinfo[NUMMAPS];
 
@@ -311,7 +314,7 @@ enum GameType
 
 	NUMGAMETYPES
 };
-// If you alter this list, update gametype_cons_t in m_menu.c
+// If you alter this list, update dehacked.c, and gametype_cons_t and MISC_ChangeGameTypeMenu in m_menu.c
 
 extern tic_t totalplaytime;
 
diff --git a/src/g_game.c b/src/g_game.c
index 0d011bd7f4f102f00ab3cbb1d82d6bc1afc8ccc4..8bd71d123dfd0905cbf8ebea8434bb6c67d43bcb 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -697,8 +697,7 @@ void G_SetNightsRecords(void)
 	free(gpath);
 
 	// If the mare count changed, this will update the score display
-	CV_AddValue(&cv_nextmap, 1);
-	CV_AddValue(&cv_nextmap, -1);
+	Nextmap_OnChange();
 }
 
 // for consistency among messages: this modifies the game and removes savemoddata.
@@ -3532,7 +3531,7 @@ void G_DeferedInitNew(boolean pultmode, const char *mapname, INT32 pickedchar, b
 // This is the map command interpretation something like Command_Map_f
 //
 // called at: map cmd execution, doloadgame, doplaydemo
-void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean skipprecutscene)
+void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean skipprecutscene, boolean FLS)
 {
 	INT32 i;
 
@@ -3562,7 +3561,8 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
 
 			if (netgame || multiplayer)
 			{
-				players[i].lives = cv_startinglives.value;
+				if (!FLS || (players[i].lives < cv_startinglives.value))
+					players[i].lives = cv_startinglives.value;
 				players[i].continues = 0;
 			}
 			else if (pultmode)
@@ -3576,13 +3576,16 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
 				players[i].continues = 1;
 			}
 
+			if (!((netgame || multiplayer) && (FLS)))
+				players[i].score = 0;
+
 			// The latter two should clear by themselves, but just in case
 			players[i].pflags &= ~(PF_TAGIT|PF_TAGGED|PF_FULLSTASIS);
 
 			// Clear cheatcodes too, just in case.
 			players[i].pflags &= ~(PF_GODMODE|PF_NOCLIP|PF_INVIS);
 
-			players[i].score = players[i].xtralife = 0;
+			players[i].xtralife = 0;
 		}
 
 		// Reset unlockable triggers
@@ -5127,7 +5130,7 @@ void G_DoPlayDemo(char *defdemoname)
 	memset(playeringame,0,sizeof(playeringame));
 	playeringame[0] = true;
 	P_SetRandSeed(randseed);
-	G_InitNew(false, G_BuildMapName(gamemap), true, true);
+	G_InitNew(false, G_BuildMapName(gamemap), true, true, false);
 
 	// Set skin
 	SetPlayerSkin(0, skin);
diff --git a/src/g_game.h b/src/g_game.h
index 6d41255171a93995d54d65b21d7f5fd94d6f8a79..bfde7698a367de43067de3a6b8c9ac5f1a861c2a 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -89,7 +89,7 @@ void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo);
 void G_DoReborn(INT32 playernum);
 void G_PlayerReborn(INT32 player);
 void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer,
-	boolean skipprecutscene);
+	boolean skipprecutscene, boolean FLS);
 char *G_BuildMapTitle(INT32 mapnum);
 
 // XMOD spawning
diff --git a/src/hardware/hw_draw.c b/src/hardware/hw_draw.c
index f23753ee53ff4ef65833cd3c661b80391e45526a..a00bf3aeb9f706a96182d33067de191865eb629b 100644
--- a/src/hardware/hw_draw.c
+++ b/src/hardware/hw_draw.c
@@ -152,7 +152,9 @@ void HWR_DrawFixedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscale,
 	float pdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f*FIXED_TO_FLOAT(pscale);
 	float pdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f*FIXED_TO_FLOAT(pscale);
 
-	if (alphalevel >= 10 && alphalevel < 13)
+	if (alphalevel == 12)
+		alphalevel = 0;
+	else if (alphalevel >= 10 && alphalevel < 13)
 		return;
 
 	// make patch ready in hardware cache
@@ -252,7 +254,9 @@ void HWR_DrawCroppedPatch(GLPatch_t *gpatch, fixed_t x, fixed_t y, fixed_t pscal
 	float pdupx = FIXED_TO_FLOAT(vid.fdupx)*2.0f*FIXED_TO_FLOAT(pscale);
 	float pdupy = FIXED_TO_FLOAT(vid.fdupy)*2.0f*FIXED_TO_FLOAT(pscale);
 
-	if (alphalevel >= 10 && alphalevel < 13)
+	if (alphalevel == 12)
+		alphalevel = 0;
+	else if (alphalevel >= 10 && alphalevel < 13)
 		return;
 
 	// make patch ready in hardware cache
diff --git a/src/m_menu.c b/src/m_menu.c
index 6ca6355580004507b78b92291aaeff649dd030c7..bbdaee6217a3297fb76a78cd98093ff8a87f8f14 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -53,6 +53,7 @@
 #include "byteptr.h"
 #include "st_stuff.h"
 #include "i_sound.h"
+#include "fastcmp.h"
 
 // Condition Sets
 #include "m_cond.h"
@@ -190,6 +191,7 @@ static INT32 vidm_column_size;
 // PROTOTYPES
 //
 
+static void M_GoBack(INT32 choice);
 static void M_StopMessage(INT32 choice);
 
 #ifndef NONET
@@ -207,6 +209,27 @@ menu_t MessageDef;
 
 menu_t SPauseDef;
 
+// Level Select
+static levelselect_t levelselect = {0, NULL};
+static UINT8 levelselectselect[4];
+static patch_t *levselp[2][3];
+static INT32 lsoffs[2];
+
+#define lsrow levelselectselect[0]
+#define lscol levelselectselect[1]
+#define lstic levelselectselect[2]
+#define lshli levelselectselect[3]
+
+#define lshseperation 101
+#define lsbasevseperation 62
+#define lsheadingheight 16
+#define getheadingoffset(row) (levelselect.rows[row].header[0] ? lsheadingheight : 0)
+#define lsvseperation(row) lsbasevseperation + getheadingoffset(row)
+#define lswide(row) levelselect.rows[row].mapavailable[3]
+
+#define lsbasex 19
+#define lsbasey 59+lsheadingheight
+
 // Sky Room
 static void M_CustomLevelSelect(INT32 choice);
 static void M_CustomWarp(INT32 choice);
@@ -226,6 +249,7 @@ static void M_Options(INT32 choice);
 static void M_SelectableClearMenus(INT32 choice);
 static void M_Retry(INT32 choice);
 static void M_EndGame(INT32 choice);
+static void M_GameTypeChange(INT32 choice);
 static void M_MapChange(INT32 choice);
 static void M_ChangeLevel(INT32 choice);
 static void M_ConfirmSpectate(INT32 choice);
@@ -240,7 +264,9 @@ menu_t MISC_ScrambleTeamDef, MISC_ChangeTeamDef;
 
 // Single Player
 static void M_LoadGame(INT32 choice);
+static void M_TimeAttackLevelSelect(INT32 choice);
 static void M_TimeAttack(INT32 choice);
+static void M_NightsAttackLevelSelect(INT32 choice);
 static void M_NightsAttack(INT32 choice);
 static void M_Statistics(INT32 choice);
 static void M_ReplayTimeAttack(INT32 choice);
@@ -263,6 +289,7 @@ static void M_ConnectIPMenu(INT32 choice);
 #endif
 static void M_StartSplitServerMenu(INT32 choice);
 static void M_StartServer(INT32 choice);
+static void M_ServerOptions(INT32 choice);
 #ifndef NONET
 static void M_Refresh(INT32 choice);
 static void M_Connect(INT32 choice);
@@ -311,8 +338,9 @@ static void M_DrawSkyRoom(void);
 static void M_DrawChecklist(void);
 static void M_DrawEmblemHints(void);
 static void M_DrawPauseMenu(void);
+static void M_DrawGameTypeMenu(void);
 static void M_DrawServerMenu(void);
-static void M_DrawLevelSelectMenu(void);
+static void M_DrawLevelPlatterMenu(void);
 static void M_DrawImageDef(void);
 static void M_DrawLoad(void);
 static void M_DrawLevelStats(void);
@@ -341,6 +369,7 @@ static boolean M_CancelConnect(void);
 #endif
 static boolean M_ExitPandorasBox(void);
 static boolean M_QuitMultiPlayerMenu(void);
+static void M_HandleLevelPlatter(INT32 choice);
 static void M_HandleSoundTest(INT32 choice);
 static void M_HandleImageDef(INT32 choice);
 static void M_HandleLoadSave(INT32 choice);
@@ -356,7 +385,6 @@ static void M_HandleFogColor(INT32 choice);
 static void M_HandleVideoMode(INT32 choice);
 
 // Consvar onchange functions
-static void Nextmap_OnChange(void);
 static void Newgametype_OnChange(void);
 static void Dummymares_OnChange(void);
 
@@ -374,7 +402,7 @@ static CV_PossibleValue_t skins_cons_t[MAXSKINS+1] = {{1, DEFAULTSKIN}};
 consvar_t cv_chooseskin = {"chooseskin", DEFAULTSKIN, CV_HIDEN|CV_CALL, skins_cons_t, Nextmap_OnChange, 0, NULL, NULL, 0, 0, NULL};
 
 // This gametype list is integral for many different reasons.
-// When you add gametypes here, don't forget to update them in CV_AddValue!
+// When you add gametypes here, don't forget to update them in dehacked.c and doomstat.h!
 CV_PossibleValue_t gametype_cons_t[] =
 {
 	{GT_COOP, "Co-op"},
@@ -386,7 +414,7 @@ CV_PossibleValue_t gametype_cons_t[] =
 	{GT_TEAMMATCH, "Team Match"},
 
 	{GT_TAG, "Tag"},
-	{GT_HIDEANDSEEK, "Hide and Seek"},
+	{GT_HIDEANDSEEK, "Hide & Seek"},
 
 	{GT_CTF, "CTF"},
 	{0, NULL}
@@ -486,21 +514,21 @@ typedef enum
 // ---------------------
 static menuitem_t MPauseMenu[] =
 {
-	{IT_STRING  | IT_SUBMENU, NULL, "Scramble Teams...", &MISC_ScrambleTeamDef, 16},
-	{IT_STRING  | IT_CALL,    NULL, "Switch Map..."    , M_MapChange,           24},
+	{IT_STRING  | IT_SUBMENU, NULL, "Scramble Teams...",        &MISC_ScrambleTeamDef, 16},
+	{IT_STRING  | IT_CALL,    NULL, "Switch Gametype/Level...", M_GameTypeChange,      24},
 
-	{IT_CALL | IT_STRING,    NULL, "Continue",             M_SelectableClearMenus,40},
-	{IT_CALL | IT_STRING,    NULL, "Player 1 Setup",       M_SetupMultiPlayer,    48}, // splitscreen
-	{IT_CALL | IT_STRING,    NULL, "Player 2 Setup",       M_SetupMultiPlayer2,   56}, // splitscreen
+	{IT_CALL | IT_STRING,    NULL, "Continue",                  M_SelectableClearMenus,40},
+	{IT_CALL | IT_STRING,    NULL, "Player 1 Setup",            M_SetupMultiPlayer,    48}, // splitscreen
+	{IT_CALL | IT_STRING,    NULL, "Player 2 Setup",            M_SetupMultiPlayer2,   56}, // splitscreen
 
-	{IT_STRING | IT_CALL,    NULL, "Spectate",             M_ConfirmSpectate,     48},
-	{IT_STRING | IT_CALL,    NULL, "Enter Game",           M_ConfirmEnterGame,    48},
-	{IT_STRING | IT_SUBMENU, NULL, "Switch Team...",       &MISC_ChangeTeamDef,   48},
-	{IT_CALL | IT_STRING,    NULL, "Player Setup",         M_SetupMultiPlayer,    56}, // alone
-	{IT_CALL | IT_STRING,    NULL, "Options",              M_Options,             64},
+	{IT_STRING | IT_CALL,    NULL, "Spectate",                  M_ConfirmSpectate,     48},
+	{IT_STRING | IT_CALL,    NULL, "Enter Game",                M_ConfirmEnterGame,    48},
+	{IT_STRING | IT_SUBMENU, NULL, "Switch Team...",            &MISC_ChangeTeamDef,   48},
+	{IT_CALL | IT_STRING,    NULL, "Player Setup",              M_SetupMultiPlayer,    56}, // alone
+	{IT_CALL | IT_STRING,    NULL, "Options",                   M_Options,             64},
 
-	{IT_CALL | IT_STRING,    NULL, "Return to Title",      M_EndGame,            80},
-	{IT_CALL | IT_STRING,    NULL, "Quit Game",            M_QuitSRB2,           88},
+	{IT_CALL | IT_STRING,    NULL, "Return to Title",           M_EndGame,             80},
+	{IT_CALL | IT_STRING,    NULL, "Quit Game",                 M_QuitSRB2,            88},
 };
 
 typedef enum
@@ -568,11 +596,37 @@ static menuitem_t MISC_ChangeTeamMenu[] =
 	{IT_WHITESTRING|IT_CALL,         NULL, "Confirm",           M_ConfirmTeamChange,    90},
 };
 
+static menuitem_t MISC_ChangeGameTypeMenu[] =
+{
+	{IT_STRING|IT_CALL,              NULL, "Co-op",            M_MapChange,  0},
+
+	{IT_STRING|IT_CALL,              NULL, "Competition",      M_MapChange, 12},
+	{IT_STRING|IT_CALL,              NULL, "Race",             M_MapChange, 20},
+
+	{IT_STRING|IT_CALL,              NULL, "Match",            M_MapChange, 32},
+	{IT_STRING|IT_CALL,              NULL, "Team Match",       M_MapChange, 40},
+
+	{IT_STRING|IT_CALL,              NULL, "Tag",              M_MapChange, 52},
+	{IT_STRING|IT_CALL,              NULL, "Hide & Seek",      M_MapChange, 60},
+
+	{IT_STRING|IT_CALL,              NULL, "Capture the Flag", M_MapChange, 72},
+};
+
+static const gtdesc_t gametypedesc[] =
+{
+	{"Play through the single-player campaign with your friends, teaming up to beat Dr Eggman's nefarious challenges!"},
+	{"Speed your way through the main acts, competing in several different categories to see who's the best."},
+	{"There's not much to it - zoom through the level faster than everyone else."},
+	{"Sling rings at your foes in a free-for-all battle. Use the special weapon rings to your advantage!"},
+	{"Sling rings at your foes in a color-coded battle. Use the special weapon rings to your advantage!"},
+	{"Whoever's IT has to hunt down everyone else. If you get caught, you have to turn on your former friends!"},
+	{"Try and find a good hiding place in these maps - we dare you."},
+	{"Steal the flag from the enemy's base and bring it back to your own, but watch out - they could just as easily steal yours!"},
+};
+
 static menuitem_t MISC_ChangeLevelMenu[] =
 {
-	{IT_STRING|IT_CVAR,              NULL, "Game Type",             &cv_newgametype,    30},
-	{IT_STRING|IT_CVAR,              NULL, "Level",                 &cv_nextmap,        60},
-	{IT_WHITESTRING|IT_CALL,         NULL, "Change Level",          M_ChangeLevel,     120},
+	{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelPlatter, 0},     // dummy menuitem for the control func
 };
 
 static menuitem_t MISC_HelpMenu[] =
@@ -646,9 +700,7 @@ static menuitem_t SR_MainMenu[] =
 
 static menuitem_t SR_LevelSelectMenu[] =
 {
-	{IT_STRING|IT_CVAR,              NULL, "Level",                 &cv_nextmap,        60},
-
-	{IT_WHITESTRING|IT_CALL,         NULL, "Start",                 M_LevelSelectWarp,     120},
+	{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelPlatter, 0},     // dummy menuitem for the control func
 };
 
 static menuitem_t SR_UnlockChecklistMenu[] =
@@ -670,10 +722,10 @@ static menuitem_t SR_EmblemHintMenu[] =
 // Single Player Main
 static menuitem_t SP_MainMenu[] =
 {
-	{IT_CALL | IT_STRING,                       NULL, "Start Game",    M_LoadGame,        92},
-	{IT_SECRET,                                 NULL, "Record Attack", M_TimeAttack,     100},
-	{IT_SECRET,                                 NULL, "NiGHTS Mode",   M_NightsAttack,   108},
-	{IT_CALL | IT_STRING | IT_CALL_NOTMODIFIED, NULL, "Statistics",    M_Statistics,     116},
+	{IT_CALL | IT_STRING,                       NULL, "Start Game",    M_LoadGame,                 92},
+	{IT_SECRET,                                 NULL, "Record Attack", M_TimeAttack,              100},
+	{IT_SECRET,                                 NULL, "NiGHTS Mode",   M_NightsAttack,            108},
+	{IT_CALL | IT_STRING | IT_CALL_NOTMODIFIED, NULL, "Statistics",    M_Statistics,              116},
 };
 
 enum
@@ -687,26 +739,30 @@ enum
 // Single Player Load Game
 static menuitem_t SP_LoadGameMenu[] =
 {
-	{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLoadSave, '\0'},     // dummy menuitem for the control func
+	{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLoadSave, 0},     // dummy menuitem for the control func
 };
 
 // Single Player Level Select
 static menuitem_t SP_LevelSelectMenu[] =
 {
-	{IT_STRING|IT_CVAR,              NULL, "Level",                 &cv_nextmap,        60},
+	{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelPlatter, 0},     // dummy menuitem for the control func
+};
 
-	{IT_WHITESTRING|IT_CALL,         NULL, "Start",                 M_LevelSelectWarp,     120},
+// Single Player Time Attack Level Select
+static menuitem_t SP_TimeAttackLevelSelectMenu[] =
+{
+	{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelPlatter, 0},     // dummy menuitem for the control func
 };
 
 // Single Player Time Attack
 static menuitem_t SP_TimeAttackMenu[] =
 {
-	{IT_STRING|IT_CVAR,        NULL, "Level",      &cv_nextmap,          52},
-	{IT_STRING|IT_CVAR,        NULL, "Player",     &cv_chooseskin,       62},
+	{IT_STRING|IT_CALL,        NULL, "Level Select...", &M_TimeAttackLevelSelect,   52},
+	{IT_STRING|IT_CVAR,        NULL, "Character",       &cv_chooseskin,             62},
 
 	{IT_DISABLED,              NULL, "Guest Option...", &SP_GuestReplayDef, 100},
-	{IT_DISABLED,              NULL, "Replay...",     &SP_ReplayDef,        110},
-	{IT_DISABLED,              NULL, "Ghosts...",     &SP_GhostDef,         120},
+	{IT_DISABLED,              NULL, "Replay...",       &SP_ReplayDef,      110},
+	{IT_DISABLED,              NULL, "Ghosts...",       &SP_GhostDef,       120},
 	{IT_WHITESTRING|IT_CALL|IT_CALL_NOTMODIFIED,   NULL, "Start",         M_ChooseTimeAttack,   130},
 };
 
@@ -790,16 +846,22 @@ static menuitem_t SP_NightsGhostMenu[] =
 	{IT_WHITESTRING|IT_SUBMENU, NULL, "Back",       &SP_NightsAttackDef,  50}
 };
 
+// Single Player Nights Attack Level Select
+static menuitem_t SP_NightsAttackLevelSelectMenu[] =
+{
+	{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelPlatter, 0},     // dummy menuitem for the control func
+};
+
 // Single Player Nights Attack
 static menuitem_t SP_NightsAttackMenu[] =
 {
-	{IT_STRING|IT_CVAR,        NULL, "Level",            &cv_nextmap,          44},
-	{IT_STRING|IT_CVAR,        NULL, "Show Records For", &cv_dummymares,       54},
+	{IT_STRING|IT_CALL,        NULL, "Level Select...",  &M_NightsAttackLevelSelect,  52},
+	{IT_STRING|IT_CVAR,        NULL, "Show Records For", &cv_dummymares,              62},
 
-	{IT_DISABLED,              NULL, "Guest Option...",  &SP_NightsGuestReplayDef,   108},
-	{IT_DISABLED,              NULL, "Replay...",        &SP_NightsReplayDef,        118},
-	{IT_DISABLED,              NULL, "Ghosts...",        &SP_NightsGhostDef,         128},
-	{IT_WHITESTRING|IT_CALL|IT_CALL_NOTMODIFIED,   NULL, "Start",            M_ChooseNightsAttack, 138},
+	{IT_DISABLED,              NULL, "Guest Option...",  &SP_NightsGuestReplayDef,    100},
+	{IT_DISABLED,              NULL, "Replay...",        &SP_NightsReplayDef,         110},
+	{IT_DISABLED,              NULL, "Ghosts...",        &SP_NightsGhostDef,          120},
+	{IT_WHITESTRING|IT_CALL|IT_CALL_NOTMODIFIED, NULL, "Start", M_ChooseNightsAttack, 130},
 };
 
 enum
@@ -816,12 +878,12 @@ enum
 // Statistics
 static menuitem_t SP_GameStatsMenu[] =
 {
-	{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleGameStats, '\0'},     // dummy menuitem for the control func
+	{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleGameStats, 0},     // dummy menuitem for the control func
 };
 
 static menuitem_t SP_LevelStatsMenu[] =
 {
-	{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelStats, '\0'},     // dummy menuitem for the control func
+	{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelStats, 0},     // dummy menuitem for the control func
 };
 
 // Player menu dummy
@@ -849,28 +911,40 @@ static menuitem_t MP_MainMenu[] =
 
 static menuitem_t MP_ServerMenu[] =
 {
-	{IT_STRING|IT_CVAR,              NULL, "Game Type",             &cv_newgametype,    10},
+	{IT_DISABLED|IT_NOTHING, NULL, "", NULL, 0},
 #ifndef NONET
-	{IT_STRING|IT_CALL,              NULL, "Room...",               M_RoomMenu,         20},
-	{IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name",           &cv_servername,     30},
+	{IT_STRING|IT_CALL,              NULL, "Room...",                  M_RoomMenu,        10},
+	{IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name",              &cv_servername,    20},
+	{IT_STRING|IT_CVAR,              NULL, "Max Players",              &cv_maxplayers,    46},
+	{IT_STRING|IT_CVAR,              NULL, "Allow WAD Downloading",    &cv_downloading,   56},
 #endif
-
-	{IT_STRING|IT_CVAR,              NULL, "Level",                 &cv_nextmap,        80},
-
-	{IT_WHITESTRING|IT_CALL,         NULL, "Start",                 M_StartServer,     130},
+	{IT_STRING|IT_CALL,              NULL, "Select Gametype/Level...", M_GameTypeChange, 100},
+	{IT_STRING|IT_CALL,              NULL, "More Options...",          M_ServerOptions,  130},
+	{IT_WHITESTRING|IT_CALL,         NULL, "Start",                    M_StartServer,    140},
 };
 
 enum
 {
-	mp_server_gametype = 0,
+	mp_server_dummy = 0, // exists solely so zero-indexed in both NONET and not NONET
 #ifndef NONET
 	mp_server_room,
 	mp_server_name,
+	mp_server_maxpl,
+	mp_server_waddl,
 #endif
-	mp_server_level,
+	mp_server_levelgt,
+	mp_server_options,
 	mp_server_start
 };
 
+// Separated splitscreen and normal servers.
+static menuitem_t MP_SplitServerMenu[] =
+{
+	{IT_STRING|IT_CALL,              NULL, "Select Gametype/Level...", M_GameTypeChange, 100},
+	{IT_STRING|IT_CALL,              NULL, "More Options...",          M_ServerOptions,  130},
+	{IT_WHITESTRING|IT_CALL,         NULL, "Start",                    M_StartServer,    140},
+};
+
 #ifndef NONET
 static menuitem_t MP_ConnectMenu[] =
 {
@@ -929,14 +1003,6 @@ static menuitem_t MP_ConnectIPMenu[] =
 };
 #endif
 
-// Separated splitscreen and normal servers.
-static menuitem_t MP_SplitServerMenu[] =
-{
-	{IT_STRING|IT_CVAR,              NULL, "Game Type",             &cv_newgametype,    10},
-	{IT_STRING|IT_CVAR,              NULL, "Level",                 &cv_nextmap,        80},
-	{IT_WHITESTRING|IT_CALL,         NULL, "Start",                 M_StartServer,     130},
-};
-
 static menuitem_t MP_PlayerSetupMenu[] =
 {
 	{IT_KEYHANDLER | IT_STRING,   NULL, "Your name",   M_HandleSetupMultiPlayer,   0},
@@ -950,14 +1016,14 @@ static menuitem_t MP_PlayerSetupMenu[] =
 // Prefix: OP_
 static menuitem_t OP_MainMenu[] =
 {
-	{IT_SUBMENU | IT_STRING, NULL, "Setup Controls...",     &OP_ControlsDef,      10},
+	{IT_SUBMENU | IT_STRING, NULL, "Setup Controls...", &OP_ControlsDef,      10},
 
-	{IT_SUBMENU | IT_STRING, NULL, "Video Options...",      &OP_VideoOptionsDef,  30},
-	{IT_SUBMENU | IT_STRING, NULL, "Sound Options...",      &OP_SoundOptionsDef,  40},
-	{IT_SUBMENU | IT_STRING, NULL, "Data Options...",       &OP_DataOptionsDef,   50},
+	{IT_SUBMENU | IT_STRING, NULL, "Video Options...",  &OP_VideoOptionsDef,  30},
+	{IT_SUBMENU | IT_STRING, NULL, "Sound Options...",  &OP_SoundOptionsDef,  40},
+	{IT_SUBMENU | IT_STRING, NULL, "Data Options...",   &OP_DataOptionsDef,   50},
 
-	{IT_SUBMENU | IT_STRING, NULL, "Game Options...",       &OP_GameOptionsDef,   70},
-	{IT_SUBMENU | IT_STRING, NULL, "Server Options...",     &OP_ServerOptionsDef, 80},
+	{IT_SUBMENU | IT_STRING, NULL, "Game Options...",   &OP_GameOptionsDef,   70},
+	{IT_CALL | IT_STRING,    NULL, "Server Options...", M_ServerOptions,      80},
 };
 
 static menuitem_t OP_ControlsMenu[] =
@@ -1140,7 +1206,7 @@ static menuitem_t OP_VideoOptionsMenu[] =
 
 static menuitem_t OP_VideoModeMenu[] =
 {
-	{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleVideoMode, '\0'},     // dummy menuitem for the control func
+	{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleVideoMode, 0},     // dummy menuitem for the control func
 };
 
 #ifdef HWRENDER
@@ -1373,7 +1439,31 @@ menu_t MPauseDef = PAUSEMENUSTYLE(MPauseMenu, 40, 72);
 // Misc Main Menu
 menu_t MISC_ScrambleTeamDef = DEFAULTMENUSTYLE(NULL, MISC_ScrambleTeamMenu, &MPauseDef, 27, 40);
 menu_t MISC_ChangeTeamDef = DEFAULTMENUSTYLE(NULL, MISC_ChangeTeamMenu, &MPauseDef, 27, 40);
-menu_t MISC_ChangeLevelDef = MAPICONMENUSTYLE(NULL, MISC_ChangeLevelMenu, &MPauseDef);
+
+// MP Gametype and map change menu
+menu_t MISC_ChangeGameTypeDef =
+{
+	NULL,
+	sizeof (MISC_ChangeGameTypeMenu)/sizeof (menuitem_t),
+	&MainDef,  // Doesn't matter.
+	MISC_ChangeGameTypeMenu,
+	M_DrawGameTypeMenu,
+	30, (200 - (72+8))/2, // vertically centering
+	0,
+	NULL
+};
+menu_t MISC_ChangeLevelDef =
+{
+	NULL,
+	sizeof (MISC_ChangeLevelMenu)/sizeof (menuitem_t),
+	&MISC_ChangeGameTypeDef,
+	MISC_ChangeLevelMenu,
+	M_DrawLevelPlatterMenu,
+	0, 0,
+	0,
+	NULL
+};
+
 menu_t MISC_HelpDef = IMAGEDEF(MISC_HelpMenu);
 
 // Sky Room
@@ -1399,17 +1489,9 @@ menu_t SR_MainDef =
 	0,
 	NULL
 };
-menu_t SR_LevelSelectDef =
-{
-	0,
-	sizeof (SR_LevelSelectMenu)/sizeof (menuitem_t),
-	&SR_MainDef,
-	SR_LevelSelectMenu,
-	M_DrawLevelSelectMenu,
-	40, 40,
-	0,
-	NULL
-};
+
+menu_t SR_LevelSelectDef = MAPPLATTERMENUSTYLE(NULL, SR_LevelSelectMenu);
+
 menu_t SR_UnlockChecklistDef =
 {
 	NULL,
@@ -1446,7 +1528,8 @@ menu_t SP_LoadDef =
 	0,
 	NULL
 };
-menu_t SP_LevelSelectDef = MAPICONMENUSTYLE(NULL, SP_LevelSelectMenu, &SP_LoadDef);
+
+menu_t SP_LevelSelectDef = MAPPLATTERMENUSTYLE(NULL, SP_LevelSelectMenu);
 
 menu_t SP_GameStatsDef =
 {
@@ -1471,6 +1554,8 @@ menu_t SP_LevelStatsDef =
 	NULL
 };
 
+menu_t SP_TimeAttackLevelSelectDef = MAPPLATTERMENUSTYLE("M_ATTACK", SP_TimeAttackLevelSelectMenu);
+
 static menu_t SP_TimeAttackDef =
 {
 	"M_ATTACK",
@@ -1516,6 +1601,8 @@ static menu_t SP_GhostDef =
 	NULL
 };
 
+menu_t SP_NightsAttackLevelSelectDef = MAPPLATTERMENUSTYLE("M_NIGHTS", SP_NightsAttackLevelSelectMenu);
+
 static menu_t SP_NightsAttackDef =
 {
 	"M_NIGHTS",
@@ -1576,7 +1663,35 @@ menu_t SP_PlayerDef =
 
 // Multiplayer
 menu_t MP_MainDef = DEFAULTMENUSTYLE("M_MULTI", MP_MainMenu, &MainDef, 60, 40);
-menu_t MP_ServerDef = MAPICONMENUSTYLE("M_MULTI", MP_ServerMenu, &MP_MainDef);
+
+menu_t MP_ServerDef =
+{
+	"M_MULTI",
+	sizeof (MP_ServerMenu)/sizeof (menuitem_t),
+	&MP_MainDef,
+	MP_ServerMenu,
+	M_DrawServerMenu,
+	27, 30
+#ifdef NONET
+	- 50
+#endif
+	,
+	0,
+	NULL
+};
+
+menu_t MP_SplitServerDef =
+{
+	"M_MULTI",
+	sizeof (MP_SplitServerMenu)/sizeof (menuitem_t),
+	&MP_MainDef,
+	MP_SplitServerMenu,
+	M_DrawServerMenu,
+	27, 30 - 50,
+	0,
+	NULL
+};
+
 #ifndef NONET
 menu_t MP_ConnectDef =
 {
@@ -1612,7 +1727,7 @@ menu_t MP_RoomDef =
 	NULL
 };
 #endif
-menu_t MP_SplitServerDef = MAPICONMENUSTYLE("M_MULTI", MP_SplitServerMenu, &MP_MainDef);
+
 menu_t MP_PlayerSetupDef =
 {
 	"M_SPLAYR",
@@ -1719,11 +1834,11 @@ menu_t OP_EraseDataDef = DEFAULTMENUSTYLE("M_DATA", OP_EraseDataMenu, &OP_DataOp
 // (there's only a couple anyway)
 
 // Prototypes
-static INT32 M_FindFirstMap(INT32 gtype);
-static INT32 M_GetFirstLevelInList(void);
+static INT32 M_GetFirstLevelInList(INT32 gt);
+static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt);
 
-// Nextmap.  Used for Time Attack.
-static void Nextmap_OnChange(void)
+// Nextmap.  Used for Level select.
+void Nextmap_OnChange(void)
 {
 	char *leveltitle;
 	char tabase[256];
@@ -1867,12 +1982,7 @@ static void Newgametype_OnChange(void)
 		if(!mapheaderinfo[cv_nextmap.value-1])
 			P_AllocMapHeader((INT16)(cv_nextmap.value-1));
 
-		if ((cv_newgametype.value == GT_COOP && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_COOP)) ||
-			(cv_newgametype.value == GT_COMPETITION && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_COMPETITION)) ||
-			(cv_newgametype.value == GT_RACE && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_RACE)) ||
-			((cv_newgametype.value == GT_MATCH || cv_newgametype.value == GT_TEAMMATCH) && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_MATCH)) ||
-			((cv_newgametype.value == GT_TAG || cv_newgametype.value == GT_HIDEANDSEEK) && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_TAG)) ||
-			(cv_newgametype.value == GT_CTF && !(mapheaderinfo[cv_nextmap.value-1]->typeoflevel & TOL_CTF)))
+		if (!M_CanShowLevelOnPlatter(cv_nextmap.value-1, cv_newgametype.value))
 		{
 			INT32 value = 0;
 
@@ -1900,9 +2010,7 @@ static void Newgametype_OnChange(void)
 					break;
 			}
 
-			CV_SetValue(&cv_nextmap, M_FindFirstMap(value));
-			CV_AddValue(&cv_nextmap, -1);
-			CV_AddValue(&cv_nextmap, 1);
+			CV_SetValue(&cv_nextmap, M_GetFirstLevelInList(value));
 		}
 	}
 }
@@ -1947,6 +2055,34 @@ menu_t *currentMenu = &MainDef;
 // BASIC MENU HANDLING
 // =========================================================================
 
+static void M_GoBack(INT32 choice)
+{
+	(void)choice;
+
+	if (currentMenu->prevMenu)
+	{
+		//If we entered the game search menu, but didn't enter a game,
+		//make sure the game doesn't still think we're in a netgame.
+		if (!Playing() && netgame && multiplayer)
+		{
+			MSCloseUDPSocket();		// Clean up so we can re-open the connection later.
+			netgame = false;
+			multiplayer = false;
+		}
+
+		if ((currentMenu->prevMenu == &MainDef) && (currentMenu == &SP_TimeAttackDef || currentMenu == &SP_NightsAttackDef))
+		{
+			// D_StartTitle does its own wipe, since GS_TIMEATTACK is now a complete gamestate.
+			menuactive = false;
+			D_StartTitle();
+		}
+		else
+			M_SetupNextMenu(currentMenu->prevMenu);
+	}
+	else
+		M_ClearMenus(true);
+}
+
 static void M_ChangeCvar(INT32 choice)
 {
 	consvar_t *cv = (consvar_t *)currentMenu->menuitems[itemOn].itemaction;
@@ -2287,11 +2423,15 @@ boolean M_Responder(event_t *ev)
 		case KEY_DOWNARROW:
 			M_NextOpt();
 			S_StartSound(NULL, sfx_menu1);
+			if (currentMenu == &MISC_ChangeGameTypeDef)
+				Z_Free(char_notes);
 			return true;
 
 		case KEY_UPARROW:
 			M_PrevOpt();
 			S_StartSound(NULL, sfx_menu1);
+			if (currentMenu == &MISC_ChangeGameTypeDef)
+				Z_Free(char_notes);
 			return true;
 
 		case KEY_LEFTARROW:
@@ -2351,28 +2491,8 @@ boolean M_Responder(event_t *ev)
 		case KEY_ESCAPE:
 			noFurtherInput = true;
 			currentMenu->lastOn = itemOn;
-			if (currentMenu->prevMenu)
-			{
-				//If we entered the game search menu, but didn't enter a game,
-				//make sure the game doesn't still think we're in a netgame.
-				if (!Playing() && netgame && multiplayer)
-				{
-					MSCloseUDPSocket();		// Clean up so we can re-open the connection later.
-					netgame = false;
-					multiplayer = false;
-				}
 
-				if (currentMenu == &SP_TimeAttackDef || currentMenu == &SP_NightsAttackDef)
-				{
-					// D_StartTitle does its own wipe, since GS_TIMEATTACK is now a complete gamestate.
-					menuactive = false;
-					D_StartTitle();
-				}
-				else
-					M_SetupNextMenu(currentMenu->prevMenu);
-			}
-			else
-				M_ClearMenus(true);
+			M_GoBack(0);
 
 			return true;
 
@@ -3388,31 +3508,50 @@ static void M_PatchSkinNameTable(void)
 		}
 	}
 
-	CV_SetValue(&cv_chooseskin, cv_chooseskin.value); // This causes crash sometimes?!
-
 	CV_SetValue(&cv_chooseskin, 1);
-	CV_AddValue(&cv_chooseskin, -1);
-	CV_AddValue(&cv_chooseskin, 1);
+	Nextmap_OnChange();
 
 	return;
 }
 
-// Call before showing any level-select menus
-static void M_PrepareLevelSelect(void)
+//
+// M_LevelAvailableOnPlatter
+//
+// Okay, you know that the level SHOULD show up on the platter already.
+// The only question is whether it should be as a question mark,
+// (hinting as to its existence), or as its pure, unfettered self.
+//
+static boolean M_LevelAvailableOnPlatter(INT32 mapnum)
 {
-	if (levellistmode != LLM_CREATESERVER)
-		CV_SetValue(&cv_nextmap, M_GetFirstLevelInList());
-	else
-		Newgametype_OnChange(); // Make sure to start on an appropriate map if wads have been added
+	if (M_MapLocked(mapnum+1))
+		return false; // not unlocked
+
+	switch (levellistmode)
+	{
+		case LLM_RECORDATTACK:
+		case LLM_NIGHTSATTACK:
+			if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED)
+				return true;
+
+			if (!mapvisited[mapnum])
+				return false;
+
+			// intentional fallthrough
+		case LLM_CREATESERVER:
+		case LLM_LEVELSELECT:
+		default:
+			return true;
+	}
+	return true;
 }
 
 //
-// M_CanShowLevelInList
+// M_CanShowLevelOnPlatter
 //
 // Determines whether to show a given map in the various level-select lists.
 // Set gt = -1 to ignore gametype.
 //
-boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt)
+static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt)
 {
 	// Does the map exist?
 	if (!mapheaderinfo[mapnum])
@@ -3422,6 +3561,9 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt)
 	if (!mapheaderinfo[mapnum]->lvlttl[0])
 		return false;
 
+	/*if (M_MapLocked(mapnum+1))
+		return false; // not unlocked*/
+
 	switch (levellistmode)
 	{
 		case LLM_CREATESERVER:
@@ -3429,9 +3571,6 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt)
 			if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU)
 				return false;
 
-			if (M_MapLocked(mapnum+1))
-				return false; // not unlocked
-
 			if (gt == GT_COOP && (mapheaderinfo[mapnum]->typeoflevel & TOL_COOP))
 				return true;
 
@@ -3456,37 +3595,16 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt)
 			if (mapheaderinfo[mapnum]->levelselect != maplistoption)
 				return false;
 
-			if (M_MapLocked(mapnum+1))
-				return false; // not unlocked
-
 			return true;
 		case LLM_RECORDATTACK:
 			if (!(mapheaderinfo[mapnum]->menuflags & LF2_RECORDATTACK))
 				return false;
 
-			if (M_MapLocked(mapnum+1))
-				return false; // not unlocked
-
-			if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED)
-				return true;
-
-			if (!mapvisited[mapnum])
-				return false;
-
 			return true;
 		case LLM_NIGHTSATTACK:
 			if (!(mapheaderinfo[mapnum]->menuflags & LF2_NIGHTSATTACK))
 				return false;
 
-			if (M_MapLocked(mapnum+1))
-				return false; // not unlocked
-
-			if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED)
-				return true;
-
-			if (!mapvisited[mapnum])
-				return false;
-
 			return true;
 	}
 
@@ -3494,111 +3612,626 @@ boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt)
 	return false;
 }
 
-static INT32 M_CountLevelsToShowInList(void)
+#if 0
+static INT32 M_CountLevelsToShowOnPlatter(INT32 gt)
 {
 	INT32 mapnum, count = 0;
 
 	for (mapnum = 0; mapnum < NUMMAPS; mapnum++)
-		if (M_CanShowLevelInList(mapnum, -1))
+		if (M_CanShowLevelOnPlatter(mapnum, gt))
 			count++;
 
 	return count;
 }
+#endif
 
-static INT32 M_GetFirstLevelInList(void)
+#if 0
+static boolean M_SetNextMapOnPlatter(void)
 {
-	INT32 mapnum;
+	INT32 row, col = 0;
+	while (col < 3)
+	{
+		row = 0;
+		while (row < levelselect.numrows)
+		{
+			if (levelselect.rows[row].maplist[col] == cv_nextmap.value)
+			{
+				lsrow = row;
+				lscol = col;
+				return true;
+			}
+			row++;
+		}
+		col++;
+	}
+	return true;
+}
+#endif
 
-	for (mapnum = 0; mapnum < NUMMAPS; mapnum++)
-		if (M_CanShowLevelInList(mapnum, -1))
-			return mapnum + 1;
+static INT32 M_CountRowsToShowOnPlatter(INT32 gt)
+{
+	INT32 mapnum = 0, prevmapnum = 0, col = 0, rows = 0;
 
-	return 1;
-}
+	while (mapnum < NUMMAPS)
+	{
+		if (M_CanShowLevelOnPlatter(mapnum, gt))
+		{
+			if (rows == 0)
+				rows++;
+			else
+			{
+				if (col == 2
+				|| (mapheaderinfo[prevmapnum]->menuflags & LF2_WIDEICON)
+				|| (mapheaderinfo[mapnum]->menuflags & LF2_WIDEICON)
+				|| !(fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[prevmapnum]->selectheading)))
+				{
+					col = 0;
+					rows++;
+				}
+				else
+					col++;
+			}
+			prevmapnum = mapnum;
+		}
+		mapnum++;
+	}
 
-// ==================================================
-// MESSAGE BOX (aka: a hacked, cobbled together menu)
-// ==================================================
-static void M_DrawMessageMenu(void);
+	return rows;
+}
 
-// Because this is just a hack-ish 'menu', I'm not putting this with the others
-static menuitem_t MessageMenu[] =
+//
+// M_PrepareLevelPlatter
+//
+// Prepares a tasty dish of zones and acts!
+// Call before any attempt to access a level platter.
+//
+static boolean M_PrepareLevelPlatter(INT32 gt)
 {
-	// TO HACK
-	{0,NULL, NULL, NULL,0}
-};
+	INT32 numrows = M_CountRowsToShowOnPlatter(gt);
+	INT32 mapnum = 0, prevmapnum = 0, col = 0, row = 0;
 
-menu_t MessageDef =
-{
-	NULL,               // title
-	1,                  // # of menu items
-	NULL,               // previous menu       (TO HACK)
-	MessageMenu,        // menuitem_t ->
-	M_DrawMessageMenu,  // drawing routine ->
-	0, 0,               // x, y                (TO HACK)
-	0,                  // lastOn, flags       (TO HACK)
-	NULL
-};
+	if (!numrows)
+		return false;
 
+	if (levelselect.rows)
+		Z_Free(levelselect.rows);
+	levelselect.rows = NULL;
 
-void M_StartMessage(const char *string, void *routine,
-	menumessagetype_t itemtype)
-{
-	size_t max = 0, start = 0, i, strlines;
-	static char *message = NULL;
-	Z_Free(message);
-	message = Z_StrDup(string);
-	DEBFILE(message);
+	levelselect.numrows = numrows;
+	levelselect.rows = Z_Realloc(levelselect.rows, numrows*sizeof(levelselectrow_t), PU_STATIC, NULL);
+	if (!levelselect.rows)
+		I_Error("Insufficient memory to prepare level platter");
 
-	// Rudementary word wrapping.
-	// Simple and effective. Does not handle nonuniform letter sizes, colors, etc. but who cares.
-	strlines = 0;
-	for (i = 0; message[i]; i++)
+	// done here so lsrow and lscol can be set if cv_nextmap is on the platter
+	lsrow = lscol = lstic = lshli = lsoffs[0] = lsoffs[1] = 0;
+
+	while (mapnum < NUMMAPS)
 	{
-		if (message[i] == ' ')
-		{
-			start = i;
-			max += 4;
-		}
-		else if (message[i] == '\n')
+		if (M_CanShowLevelOnPlatter(mapnum, gt))
 		{
-			strlines = i;
-			start = 0;
-			max = 0;
-			continue;
-		}
-		else
-			max += 8;
+			const INT32 actnum = mapheaderinfo[mapnum]->actnum;
+			const boolean headingisname = (fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[mapnum]->lvlttl));
+			const boolean wide = (mapheaderinfo[mapnum]->menuflags & LF2_WIDEICON);
 
-		// Start trying to wrap if presumed length exceeds the screen width.
-		if (max >= BASEVIDWIDTH && start > 0)
-		{
-			message[start] = '\n';
-			max -= (start-strlines)*8;
-			strlines = start;
-			start = 0;
-		}
-	}
+			// preparing next position to drop mapnum into
+			if (levelselect.rows[0].maplist[0])
+			{
+				if (col == 2 // no more space on the row?
+				|| wide
+				|| (mapheaderinfo[prevmapnum]->menuflags & LF2_WIDEICON)
+				|| !(fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[prevmapnum]->selectheading))) // a new heading is starting?
+				{
+					col = 0;
+					row++;
+				}
+				else
+					col++;
+			}
 
-	start = 0;
-	max = 0;
+			levelselect.rows[row].maplist[col] = mapnum+1; // putting the map on the platter
+			levelselect.rows[row].mapavailable[col] = M_LevelAvailableOnPlatter(mapnum);
 
-	M_StartControlPanel(); // can't put menuactive to true
+			if ((lswide(row) = wide)) // intentionally assignment
+			{
+				levelselect.rows[row].maplist[2] = levelselect.rows[row].maplist[1] = levelselect.rows[row].maplist[0];
+				levelselect.rows[row].mapavailable[2] = levelselect.rows[row].mapavailable[1] = levelselect.rows[row].mapavailable[0];
+			}
 
-	if (currentMenu == &MessageDef) // Prevent recursion
-		MessageDef.prevMenu = &MainDef;
-	else
-		MessageDef.prevMenu = currentMenu;
+			if (cv_nextmap.value == mapnum+1) // A little quality of life improvement.
+			{
+				lsrow = row;
+				lscol = col;
+			}
 
-	MessageDef.menuitems[0].text     = message;
-	MessageDef.menuitems[0].alphaKey = (UINT8)itemtype;
-	if (!routine && itemtype != MM_NOTHING) itemtype = MM_NOTHING;
-	switch (itemtype)
-	{
-		case MM_NOTHING:
-			MessageDef.menuitems[0].status     = IT_MSGHANDLER;
-			MessageDef.menuitems[0].itemaction = M_StopMessage;
-			break;
+			// individual map name
+			if (levelselect.rows[row].mapavailable[col])
+			{
+				if (headingisname)
+				{
+					if (actnum)
+						sprintf(levelselect.rows[row].mapnames[col], "ACT %d", actnum);
+					else
+						sprintf(levelselect.rows[row].mapnames[col], "THE ACT");
+				}
+				else if (wide)
+				{
+					// Yes, with LF2_WIDEICON it'll continue on over into the next 17+1 char block. That's alright; col is always zero, the string is contiguous, and the maximum length is lvlttl[22] + ' ' + ZONE + ' ' + INT32, which is about 39 or so - barely crossing into the third column.
+					char* mapname = G_BuildMapTitle(mapnum+1);
+					strcpy(levelselect.rows[row].mapnames[col], (const char *)mapname);
+					Z_Free(mapname);
+				}
+				else
+				{
+					char mapname[22+1+11]; // lvlttl[22] + ' ' + INT32
+
+					if (actnum)
+						sprintf(mapname, "%s %d", mapheaderinfo[mapnum]->lvlttl, actnum);
+					else
+						sprintf(mapname, "%s", mapheaderinfo[mapnum]->lvlttl);
+
+					if (strlen(mapname) >= 17)
+						sprintf(mapname+17-3, "...");
+
+					strcpy(levelselect.rows[row].mapnames[col], (const char *)mapname);
+				}
+			}
+			else
+				sprintf(levelselect.rows[row].mapnames[col], "???");
+
+			// creating header text
+			if (!col && (!row || !(fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[levelselect.rows[row-1].maplist[0]-1]->selectheading))))
+			{
+				if (!levelselect.rows[row].mapavailable[col])
+					sprintf(levelselect.rows[row].header, "???");
+				else
+				{
+					sprintf(levelselect.rows[row].header, "%s", mapheaderinfo[mapnum]->selectheading);
+					if (!(mapheaderinfo[mapnum]->levelflags & LF_NOZONE) && headingisname)
+					{
+						sprintf(levelselect.rows[row].header + strlen(levelselect.rows[row].header), " ZONE");
+					}
+				}
+			}
+
+			prevmapnum = mapnum;
+		}
+
+		mapnum++;
+	}
+
+	if (levselp[0][0]) // never going to have some provided but not all, saves individually checking
+	{
+		W_UnlockCachedPatch(levselp[0][0]);
+		W_UnlockCachedPatch(levselp[0][1]);
+		W_UnlockCachedPatch(levselp[0][2]);
+
+		W_UnlockCachedPatch(levselp[1][0]);
+		W_UnlockCachedPatch(levselp[1][1]);
+		W_UnlockCachedPatch(levselp[1][2]);
+	}
+
+	levselp[0][0] = W_CachePatchName("SLCT1LVL", PU_STATIC);
+	levselp[0][1] = W_CachePatchName("SLCT2LVL", PU_STATIC);
+	levselp[0][2] = W_CachePatchName("BLANKLVL", PU_STATIC);
+
+	levselp[1][0] = W_CachePatchName("SLCT1LVW", PU_STATIC);
+	levselp[1][1] = W_CachePatchName("SLCT2LVW", PU_STATIC);
+	levselp[1][2] = W_CachePatchName("BLANKLVW", PU_STATIC);
+
+	return true;
+}
+
+#define selectvalnextmapnobrace(column) selectval = levelselect.rows[lsrow].maplist[column];\
+			if (selectval && levelselect.rows[lsrow].mapavailable[column])\
+			{\
+				CV_SetValue(&cv_nextmap, selectval);
+
+#define selectvalnextmap(column) selectvalnextmapnobrace(column)}
+
+//
+// M_HandleLevelPlatter
+//
+// Reacts to your key inputs. Basically a mini menu thinker.
+//
+static void M_HandleLevelPlatter(INT32 choice)
+{
+	boolean exitmenu = false;  // exit to previous menu
+	INT32 selectval;
+
+	switch (choice)
+	{
+		case KEY_DOWNARROW:
+			lsrow++;
+			if (lsrow == levelselect.numrows)
+				lsrow = 0;
+
+			lsoffs[0] = lsvseperation(lsrow);
+
+			if (levelselect.rows[lsrow].header[0])
+				lshli = lsrow;
+			// no else needed - headerless lines associate upwards, so moving down to a row without a header is identity
+
+			S_StartSound(NULL,sfx_s3kb7);
+
+			selectvalnextmap(lscol) else selectvalnextmap(0)
+			break;
+
+		case KEY_UPARROW:
+			lsoffs[0] = -lsvseperation(lsrow);
+
+			lsrow--;
+			if (lsrow == UINT8_MAX)
+				lsrow = levelselect.numrows-1;
+
+			if (levelselect.rows[lsrow].header[0])
+				lshli = lsrow;
+			else
+			{
+				UINT8 iter = lsrow;
+				do
+					iter = ((iter == 0) ? levelselect.numrows-1 : iter-1);
+				while ((iter != lsrow) && !(levelselect.rows[iter].header[0]));
+				lshli = iter;
+			}
+
+			S_StartSound(NULL,sfx_s3kb7);
+
+			selectvalnextmap(lscol) else selectvalnextmap(0)
+			break;
+
+		case KEY_LEFTARROW:
+			if (lscol > 0)
+			{
+				lscol--;
+
+				lsoffs[1] = (lswide(lsrow) ? -8 : lshseperation);
+				S_StartSound(NULL,sfx_s3kb7);
+
+				selectvalnextmap(lscol) else selectvalnextmap(0)
+			}
+			else if (!lsoffs[1]) //  prevent sound spam
+			{
+				lsoffs[1] = -8;
+				S_StartSound(NULL,sfx_s3kb7);
+			}
+			break;
+
+		case KEY_RIGHTARROW:
+			if (lscol < 2)
+			{
+				lscol++;
+
+				lsoffs[1] = (lswide(lsrow) ? 8 : -lshseperation);
+				S_StartSound(NULL,sfx_s3kb7);
+
+				selectvalnextmap(lscol) else selectvalnextmap(0)
+			}
+			else if (!lsoffs[1]) //  prevent sound spam
+			{
+				lsoffs[1] = 8;
+				S_StartSound(NULL,sfx_s3kb7);
+			}
+			break;
+
+		case KEY_ENTER:
+			selectvalnextmapnobrace(lscol)
+
+				lsoffs[0] = lsoffs[1] = 0;
+				S_StartSound(NULL,sfx_menu1);
+				if (gamestate == GS_TIMEATTACK)
+					M_SetupNextMenu(currentMenu->prevMenu);
+				else if (currentMenu == &MISC_ChangeLevelDef)
+				{
+					if (currentMenu->prevMenu && currentMenu->prevMenu->prevMenu != &MPauseDef)
+						M_SetupNextMenu(currentMenu->prevMenu->prevMenu);
+					else
+						M_ChangeLevel(0);
+				}
+				else
+					M_LevelSelectWarp(0);
+				Nextmap_OnChange();
+			}
+			else if (!lsoffs[0]) //  prevent sound spam
+			{
+				lsoffs[0] = -8;
+				S_StartSound(NULL,sfx_s3kb2);
+			}
+			break;
+
+		case KEY_ESCAPE:
+			exitmenu = true;
+			break;
+
+		default:
+			break;
+	}
+
+	if (exitmenu)
+	{
+		if (currentMenu->prevMenu)
+		{
+			M_SetupNextMenu(currentMenu->prevMenu);
+			Nextmap_OnChange();
+		}
+		else
+			M_ClearMenus(true);
+	}
+}
+
+static void M_DrawLevelPlatterHeader(INT32 y, const char *header, boolean headerhighlight)
+{
+	y += lsheadingheight - 12;
+	V_DrawString(19, y, (headerhighlight ? V_YELLOWMAP : 0), header);
+	y += 9;
+	if ((y >= 0) && (y < 200))
+	{
+		V_DrawFill(19, y, 281, 1, (headerhighlight ? yellowmap[3] : 3));
+		V_DrawFill(300, y, 1, 1, 26);
+	}
+	y++;
+	if ((y >= 0) && (y < 200))
+	{
+		V_DrawFill(19, y, 282, 1, 26);
+	}
+	y += 2;
+}
+
+static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolean highlight)
+{
+	patch_t *patch;
+
+	INT32 map = levelselect.rows[row].maplist[col];
+	if (!map)
+		return;
+
+	//  A 564x100 image of the level as entry MAPxxW
+	if (!(levelselect.rows[row].mapavailable[col]))
+		V_DrawSmallScaledPatch(x, y, V_STATIC, levselp[1][2]); // static - make secret maps look ENTICING
+	else
+	{
+		if (W_CheckNumForName(va("%sW", G_BuildMapName(map))) != LUMPERROR)
+			patch = W_CachePatchName(va("%sW", G_BuildMapName(map)), PU_CACHE);
+		else
+			patch = levselp[1][2]; // don't static to indicate that it's just a normal level
+
+		V_DrawSmallScaledPatch(x, y, 0, patch);
+	}
+
+	if ((y+50) < 200)
+	{
+		INT32 topy = (y+50), h = 8;
+
+		if (topy < 0)
+		{
+			h += topy;
+			topy = 0;
+		}
+		else if (topy + h >= 200)
+			h = 200 - y;
+		if (h > 0)
+			V_DrawFill(x, topy, 282, h,
+			((mapheaderinfo[map-1]->unlockrequired < 0)
+			? 159 : 63));
+	}
+
+	V_DrawString(x, y+50, (highlight ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]);
+}
+
+static void M_DrawLevelPlatterMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolean highlight)
+{
+	patch_t *patch;
+
+	INT32 map = levelselect.rows[row].maplist[col];
+	if (!map)
+		return;
+
+	//  A 160x100 image of the level as entry MAPxxP
+	if (!(levelselect.rows[row].mapavailable[col]))
+		V_DrawSmallScaledPatch(x, y, V_STATIC, levselp[0][2]); // static - make secret maps look ENTICING
+	else
+	{
+		if (W_CheckNumForName(va("%sP", G_BuildMapName(map))) != LUMPERROR)
+			patch = W_CachePatchName(va("%sP", G_BuildMapName(map)), PU_CACHE);
+		else
+			patch = levselp[0][2]; // don't static to indicate that it's just a normal level
+
+		V_DrawSmallScaledPatch(x, y, 0, patch);
+	}
+
+	if ((y+50) < 200)
+	{
+		INT32 topy = (y+50), h = 8;
+
+		if (topy < 0)
+		{
+			h += topy;
+			topy = 0;
+		}
+		else if (topy + h >= 200)
+			h = 200 - y;
+		if (h > 0)
+			V_DrawFill(x, topy, 80, h,
+			((mapheaderinfo[map-1]->unlockrequired < 0)
+			? 159 : 63));
+	}
+
+	if (strlen(levelselect.rows[row].mapnames[col]) > 6) // "AERIAL GARDEN" vs "ACT 18" - "THE ACT" intentionally compressed
+		V_DrawThinString(x, y+50, (highlight ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]);
+	else
+		V_DrawString(x, y+50, (highlight ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]);
+}
+
+static void M_DrawLevelPlatterRow(UINT8 row, INT32 y)
+{
+	UINT8 col;
+	const boolean rowhighlight = (row == lsrow);
+	if (levelselect.rows[row].header[0])
+	{
+		M_DrawLevelPlatterHeader(y, levelselect.rows[row].header, (rowhighlight || (row == lshli)));
+		y += lsheadingheight;
+	}
+
+	if (lswide(row))
+		M_DrawLevelPlatterWideMap(row, 0, lsbasex, y, rowhighlight);
+	else
+	{
+		for (col = 0; col < 3; col++)
+			M_DrawLevelPlatterMap(row, col, lsbasex+(col*lshseperation), y, (rowhighlight && (col == lscol)));
+	}
+}
+
+static void M_DrawLevelPlatterMenu(void)
+{
+	UINT8 iter = lsrow, sizeselect = (lswide(lsrow) ? 1 : 0);
+	INT32 y = lsbasey + lsoffs[0] - getheadingoffset(lsrow);
+	const INT32 cursorx = (sizeselect ? 0 : (lscol*lshseperation));
+
+	if (++lstic == 32)
+		lstic = 0;
+
+	if (gamestate == GS_TIMEATTACK)
+		V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE));
+
+	// finds row at top of the screen
+	while (y > 0)
+	{
+		iter = ((iter == 0) ? levelselect.numrows-1 : iter-1);
+		y -= lsvseperation(iter);
+	}
+
+	// draw from top to bottom
+	while (y < 200)
+	{
+		M_DrawLevelPlatterRow(iter, y);
+		y += lsvseperation(iter);
+		iter = ((iter == levelselect.numrows-1) ? 0 : iter+1);
+	}
+
+	// draw cursor box
+	V_DrawSmallScaledPatch(lsbasex + cursorx + lsoffs[1], lsbasey, 0, ((lstic & 8) ? levselp[sizeselect][0] : levselp[sizeselect][1]));
+
+	if (levelselect.rows[lsrow].maplist[lscol])
+		V_DrawScaledPatch(lsbasex + cursorx-17, lsbasey+50+lsoffs[0], 0, W_CachePatchName("M_CURSOR", PU_CACHE));
+
+	// handle movement of cursor box
+	if (abs(lsoffs[0]) > 1)
+		lsoffs[0] = 2*lsoffs[0]/3;
+	else
+		lsoffs[0] = 0;
+
+	if (abs(lsoffs[1]) > 1)
+		lsoffs[1] = 2*lsoffs[1]/3;
+	else
+		lsoffs[1] = 0;
+
+	M_DrawMenuTitle();
+}
+
+//
+// M_CanShowLevelInList
+//
+// Determines whether to show a given map in level-select lists where you don't want to see locked levels.
+// Set gt = -1 to ignore gametype.
+//
+boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt)
+{
+	return (M_CanShowLevelOnPlatter(mapnum, gt) && M_LevelAvailableOnPlatter(mapnum));
+}
+
+static INT32 M_GetFirstLevelInList(INT32 gt)
+{
+	INT32 mapnum;
+
+	for (mapnum = 0; mapnum < NUMMAPS; mapnum++)
+		if (M_CanShowLevelInList(mapnum, gt))
+			return mapnum + 1;
+
+	return 1;
+}
+
+// ==================================================
+// MESSAGE BOX (aka: a hacked, cobbled together menu)
+// ==================================================
+static void M_DrawMessageMenu(void);
+
+// Because this is just a hack-ish 'menu', I'm not putting this with the others
+static menuitem_t MessageMenu[] =
+{
+	// TO HACK
+	{0,NULL, NULL, NULL,0}
+};
+
+menu_t MessageDef =
+{
+	NULL,               // title
+	1,                  // # of menu items
+	NULL,               // previous menu       (TO HACK)
+	MessageMenu,        // menuitem_t ->
+	M_DrawMessageMenu,  // drawing routine ->
+	0, 0,               // x, y                (TO HACK)
+	0,                  // lastOn, flags       (TO HACK)
+	NULL
+};
+
+
+void M_StartMessage(const char *string, void *routine,
+	menumessagetype_t itemtype)
+{
+	size_t max = 0, start = 0, i, strlines;
+	static char *message = NULL;
+	Z_Free(message);
+	message = Z_StrDup(string);
+	DEBFILE(message);
+
+	// Rudementary word wrapping.
+	// Simple and effective. Does not handle nonuniform letter sizes, colors, etc. but who cares.
+	strlines = 0;
+	for (i = 0; message[i]; i++)
+	{
+		if (message[i] == ' ')
+		{
+			start = i;
+			max += 4;
+		}
+		else if (message[i] == '\n')
+		{
+			strlines = i;
+			start = 0;
+			max = 0;
+			continue;
+		}
+		else
+			max += 8;
+
+		// Start trying to wrap if presumed length exceeds the screen width.
+		if (max >= BASEVIDWIDTH && start > 0)
+		{
+			message[start] = '\n';
+			max -= (start-strlines)*8;
+			strlines = start;
+			start = 0;
+		}
+	}
+
+	start = 0;
+	max = 0;
+
+	M_StartControlPanel(); // can't put menuactive to true
+
+	if (currentMenu == &MessageDef) // Prevent recursion
+		MessageDef.prevMenu = &MainDef;
+	else
+		MessageDef.prevMenu = currentMenu;
+
+	MessageDef.menuitems[0].text     = message;
+	MessageDef.menuitems[0].alphaKey = (UINT8)itemtype;
+	if (!routine && itemtype != MM_NOTHING) itemtype = MM_NOTHING;
+	switch (itemtype)
+	{
+		case MM_NOTHING:
+			MessageDef.menuitems[0].status     = IT_MSGHANDLER;
+			MessageDef.menuitems[0].itemaction = M_StopMessage;
+			break;
 		case MM_YESNO:
 			MessageDef.menuitems[0].status     = IT_MSGHANDLER;
 			MessageDef.menuitems[0].itemaction = routine;
@@ -3868,7 +4501,7 @@ static void M_Options(INT32 choice)
 	(void)choice;
 
 	// if the player is not admin or server, disable server options
-	OP_MainMenu[5].status = (Playing() && !(server || adminplayer == consoleplayer)) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU);
+	OP_MainMenu[5].status = (Playing() && !(server || adminplayer == consoleplayer)) ? (IT_GRAYEDOUT) : (IT_STRING|IT_CALL);
 
 	// if the player is playing _at all_, disable the erase data options
 	OP_DataOptionsMenu[1].status = (Playing()) ? (IT_GRAYEDOUT) : (IT_STRING|IT_SUBMENU);
@@ -4044,27 +4677,6 @@ static void M_DrawEmblemHints(void)
 	M_DrawGenericMenu();
 }
 
-static void M_DrawLevelSelectMenu(void)
-{
-	M_DrawGenericMenu();
-
-	if (cv_nextmap.value)
-	{
-		lumpnum_t lumpnum;
-		patch_t *PictureOfLevel;
-
-		//  A 160x100 image of the level as entry MAPxxP
-		lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
-
-		if (lumpnum != LUMPERROR)
-			PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_CACHE);
-		else
-			PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE);
-
-		V_DrawSmallScaledPatch(200, 110, 0, PictureOfLevel);
-	}
-}
-
 static void M_DrawSkyRoom(void)
 {
 	INT32 i, y = 0;
@@ -4247,13 +4859,13 @@ static void M_CustomLevelSelect(INT32 choice)
 	SR_LevelSelectDef.prevMenu = currentMenu;
 	levellistmode = LLM_LEVELSELECT;
 	maplistoption = (UINT8)(unlockables[ul].variable);
-	if (M_CountLevelsToShowInList() == 0)
+
+	if (!M_PrepareLevelPlatter(-1))
 	{
 		M_StartMessage(M_GetText("No selectable levels found.\n"),NULL,MM_NOTHING);
 		return;
 	}
 
-	M_PrepareLevelSelect();
 	M_SetupNextMenu(&SR_LevelSelectDef);
 }
 
@@ -4275,17 +4887,17 @@ static void M_SinglePlayerMenu(INT32 choice)
 static void M_LoadGameLevelSelect(INT32 choice)
 {
 	(void)choice;
+
+	SP_LevelSelectDef.prevMenu = currentMenu;
 	levellistmode = LLM_LEVELSELECT;
 	maplistoption = 1;
-	if (M_CountLevelsToShowInList() == 0)
+
+	if (!M_PrepareLevelPlatter(-1))
 	{
 		M_StartMessage(M_GetText("No selectable levels found.\n"),NULL,MM_NOTHING);
 		return;
 	}
 
-	SP_LevelSelectDef.prevMenu = currentMenu;
-
-	M_PrepareLevelSelect();
 	M_SetupNextMenu(&SP_LevelSelectDef);
 }
 
@@ -5256,9 +5868,7 @@ void M_DrawTimeAttackMenu(void)
 {
 	INT32 i, x, y, cursory = 0;
 	UINT16 dispstatus;
-	patch_t *PictureOfLevel, *PictureOfUrFace;
-	lumpnum_t lumpnum;
-	char beststr[40];
+	patch_t *PictureOfUrFace;
 
 	S_ChangeMusicInternal("_inter", true); // Eww, but needed for when user hits escape during demo playback
 
@@ -5302,16 +5912,6 @@ void M_DrawTimeAttackMenu(void)
 	V_DrawScaledPatch(currentMenu->x - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_CACHE));
 	V_DrawString(currentMenu->x, cursory, V_YELLOWMAP, currentMenu->menuitems[itemOn].text);
 
-	//  A 160x100 image of the level as entry MAPxxP
-	lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
-
-	if (lumpnum != LUMPERROR)
-		PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_CACHE);
-	else
-		PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE);
-
-	V_DrawSmallScaledPatch(208, 32, 0, PictureOfLevel);
-
 	// Character face!
 	if (W_CheckNumForName(skins[cv_chooseskin.value-1].charsel) != LUMPERROR)
 	{
@@ -5327,16 +5927,31 @@ void M_DrawTimeAttackMenu(void)
 	{
 		emblem_t *em;
 		INT32 yHeight;
+		patch_t *PictureOfLevel;
+		lumpnum_t lumpnum;
+		char beststr[40];
+
+		M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true);
 
-		V_DrawCenteredString(104, 32, 0, "* LEVEL RECORDS *");
+		//  A 160x100 image of the level as entry MAPxxP
+		lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
+
+		if (lumpnum != LUMPERROR)
+			PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_CACHE);
+		else
+			PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE);
+
+		V_DrawSmallScaledPatch(208, 32+lsheadingheight, 0, PictureOfLevel);
+
+		V_DrawString(104 - 72, 32+lsheadingheight/2, 0, "* LEVEL RECORDS *");
 
 		if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->score)
 			sprintf(beststr, "(none)");
 		else
 			sprintf(beststr, "%u", mainrecords[cv_nextmap.value-1]->score);
 
-		V_DrawString(104-72, 48, V_YELLOWMAP, "SCORE:");
-		V_DrawRightAlignedString(104+72, 48, V_ALLOWLOWERCASE, beststr);
+		V_DrawString(104-72, 48+lsheadingheight/2, V_YELLOWMAP, "SCORE:");
+		V_DrawRightAlignedString(104+72, 48+lsheadingheight/2, V_ALLOWLOWERCASE, beststr);
 
 		if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->time)
 			sprintf(beststr, "(none)");
@@ -5345,16 +5960,16 @@ void M_DrawTimeAttackMenu(void)
 			                                 G_TicsToSeconds(mainrecords[cv_nextmap.value-1]->time),
 			                                 G_TicsToCentiseconds(mainrecords[cv_nextmap.value-1]->time));
 
-		V_DrawString(104-72, 58, V_YELLOWMAP, "TIME:");
-		V_DrawRightAlignedString(104+72, 58, V_ALLOWLOWERCASE, beststr);
+		V_DrawString(104-72, 58+lsheadingheight/2, V_YELLOWMAP, "TIME:");
+		V_DrawRightAlignedString(104+72, 58+lsheadingheight/2, V_ALLOWLOWERCASE, beststr);
 
 		if (!mainrecords[cv_nextmap.value-1] || !mainrecords[cv_nextmap.value-1]->rings)
 			sprintf(beststr, "(none)");
 		else
 			sprintf(beststr, "%hu", mainrecords[cv_nextmap.value-1]->rings);
 
-		V_DrawString(104-72, 68, V_YELLOWMAP, "RINGS:");
-		V_DrawRightAlignedString(104+72, 68, V_ALLOWLOWERCASE, beststr);
+		V_DrawString(104-72, 68+lsheadingheight/2, V_YELLOWMAP, "RINGS:");
+		V_DrawRightAlignedString(104+72, 68+lsheadingheight/2, V_ALLOWLOWERCASE, beststr);
 
 		// Draw record emblems.
 		em = M_GetLevelEmblems(cv_nextmap.value);
@@ -5370,17 +5985,17 @@ void M_DrawTimeAttackMenu(void)
 			}
 
 			if (em->collected)
-				V_DrawSmallMappedPatch(104+76, yHeight, 0, W_CachePatchName(M_GetEmblemPatch(em), PU_CACHE),
+				V_DrawSmallMappedPatch(104+76, yHeight+lsheadingheight/2, 0, W_CachePatchName(M_GetEmblemPatch(em), PU_CACHE),
 				                       R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE));
 			else
-				V_DrawSmallScaledPatch(104+76, yHeight, 0, W_CachePatchName("NEEDIT", PU_CACHE));
+				V_DrawSmallScaledPatch(104+76, yHeight+lsheadingheight/2, 0, W_CachePatchName("NEEDIT", PU_CACHE));
 
 			skipThisOne:
 			em = M_GetLevelEmblems(-1);
 		}
 	}
 
-	// ALWAYS DRAW level name and skin even when not on this menu!
+	// ALWAYS DRAW level and skin even when not on this menu!
 	if (currentMenu != &SP_TimeAttackDef)
 	{
 		consvar_t *ncv;
@@ -5388,27 +6003,30 @@ void M_DrawTimeAttackMenu(void)
 		x = SP_TimeAttackDef.x;
 		y = SP_TimeAttackDef.y;
 
-		for (i = 0; i < 2; ++i)
-		{
-			ncv = (consvar_t *)SP_TimeAttackMenu[i].itemaction;
+		V_DrawString(x, y + SP_TimeAttackMenu[talevel].alphaKey, V_TRANSLUCENT, SP_TimeAttackMenu[talevel].text);
 
-			V_DrawString(x, y + SP_TimeAttackMenu[i].alphaKey, V_TRANSLUCENT, SP_TimeAttackMenu[i].text);
-			V_DrawString(BASEVIDWIDTH - x - V_StringWidth(ncv->string, 0),
-			             y + SP_TimeAttackMenu[i].alphaKey, V_YELLOWMAP|V_TRANSLUCENT, ncv->string);
-		}
+		ncv = (consvar_t *)SP_TimeAttackMenu[taplayer].itemaction;
+		V_DrawString(x, y + SP_TimeAttackMenu[taplayer].alphaKey, V_TRANSLUCENT, SP_TimeAttackMenu[taplayer].text);
+		V_DrawString(BASEVIDWIDTH - x - V_StringWidth(ncv->string, 0), y + SP_TimeAttackMenu[taplayer].alphaKey, V_YELLOWMAP|V_TRANSLUCENT, ncv->string);
 	}
 }
 
+static void M_TimeAttackLevelSelect(INT32 choice)
+{
+	(void)choice;
+	SP_TimeAttackLevelSelectDef.prevMenu = currentMenu;
+	M_SetupNextMenu(&SP_TimeAttackLevelSelectDef);
+}
+
 // Going to Time Attack menu...
 static void M_TimeAttack(INT32 choice)
 {
 	(void)choice;
 
-	memset(skins_cons_t, 0, sizeof (skins_cons_t));
-
+	SP_TimeAttackDef.prevMenu = &MainDef;
 	levellistmode = LLM_RECORDATTACK; // Don't be dependent on cv_newgametype
 
-	if (M_CountLevelsToShowInList() == 0)
+	if (!M_PrepareLevelPlatter(-1))
 	{
 		M_StartMessage(M_GetText("No record-attackable levels found.\n"),NULL,MM_NOTHING);
 		return;
@@ -5416,119 +6034,158 @@ static void M_TimeAttack(INT32 choice)
 
 	M_PatchSkinNameTable();
 
-	M_PrepareLevelSelect();
 	M_SetupNextMenu(&SP_TimeAttackDef);
-	Nextmap_OnChange();
-
-	itemOn = tastart; // "Start" is selected.
+	if (!M_CanShowLevelInList(cv_nextmap.value-1, -1) && levelselect.rows[0].maplist[0])
+		CV_SetValue(&cv_nextmap, levelselect.rows[0].maplist[0]);
+	else
+		Nextmap_OnChange();
 
 	G_SetGamestate(GS_TIMEATTACK);
 	S_ChangeMusicInternal("_inter", true);
+
+	itemOn = tastart; // "Start" is selected.
 }
 
 // Drawing function for Nights Attack
 void M_DrawNightsAttackMenu(void)
 {
-	patch_t *PictureOfLevel;
-	lumpnum_t lumpnum;
-	char beststr[40];
+	INT32 i, x, y, cursory = 0;
+	UINT16 dispstatus;
 
 	S_ChangeMusicInternal("_inter", true); // Eww, but needed for when user hits escape during demo playback
 
 	V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE));
 
+	M_DrawMenuTitle();
+
 	// draw menu (everything else goes on top of it)
-	M_DrawGenericMenu();
+	// Sadly we can't just use generic mode menus because we need some extra hacks
+	x = currentMenu->x;
+	y = currentMenu->y;
 
-	//  A 160x100 image of the level as entry MAPxxP
-	lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
+	for (i = 0; i < currentMenu->numitems; ++i)
+	{
+		dispstatus = (currentMenu->menuitems[i].status & IT_DISPLAY);
+		if (dispstatus != IT_STRING && dispstatus != IT_WHITESTRING)
+			continue;
 
-	if (lumpnum != LUMPERROR)
-		PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_CACHE);
-	else
-		PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE);
+		y = currentMenu->y+currentMenu->menuitems[i].alphaKey;
+		if (i == itemOn)
+			cursory = y;
+
+		V_DrawString(x, y, (dispstatus == IT_WHITESTRING) ? V_YELLOWMAP : 0 , currentMenu->menuitems[i].text);
 
-	V_DrawSmallScaledPatch(90, 28, 0, PictureOfLevel);
+		// Cvar specific handling
+		if ((currentMenu->menuitems[i].status & IT_TYPE) == IT_CVAR)
+		{
+			consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction;
+			INT32 soffset = 0;
+
+			// hack to keep the menu from overlapping the overall grade icon
+			if (currentMenu != &SP_NightsAttackDef)
+				soffset = 80;
+
+			// Should see nothing but strings
+			V_DrawString(BASEVIDWIDTH - x - soffset - V_StringWidth(cv->string, 0), y, V_YELLOWMAP, cv->string);
+		}
+	}
+
+	// DRAW THE SKULL CURSOR
+	V_DrawScaledPatch(currentMenu->x - 24, cursory, 0, W_CachePatchName("M_CURSOR", PU_CACHE));
+	V_DrawString(currentMenu->x, cursory, V_YELLOWMAP, currentMenu->menuitems[itemOn].text);
 
 	// Level record list
 	if (cv_nextmap.value)
 	{
 		emblem_t *em;
 		INT32 yHeight;
+		patch_t *PictureOfLevel;
+		lumpnum_t lumpnum;
+		char beststr[40];
 
 		UINT8 bestoverall	= G_GetBestNightsGrade(cv_nextmap.value, 0);
 		UINT8 bestgrade		= G_GetBestNightsGrade(cv_nextmap.value, cv_dummymares.value);
 		UINT32 bestscore	= G_GetBestNightsScore(cv_nextmap.value, cv_dummymares.value);
 		tic_t besttime		= G_GetBestNightsTime(cv_nextmap.value, cv_dummymares.value);
 
-		if (P_HasGrades(cv_nextmap.value, 0))
-			V_DrawScaledPatch(200, 28 + 8, 0, ngradeletters[bestoverall]);
+		M_DrawLevelPlatterHeader(32-lsheadingheight/2, cv_nextmap.string, true);
 
-		if (currentMenu == &SP_NightsAttackDef)
-		{
-			if (P_HasGrades(cv_nextmap.value, cv_dummymares.value))
-			{
-				V_DrawString(160-88, 112, V_YELLOWMAP, "BEST GRADE:");
-				V_DrawSmallScaledPatch(160 + 86 - (ngradeletters[bestgrade]->width/2),
-					112 + 8 - (ngradeletters[bestgrade]->height/2),
-					0, ngradeletters[bestgrade]);
-			}
+		//  A 160x100 image of the level as entry MAPxxP
+		lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
 
-			if (!bestscore)
-				sprintf(beststr, "(none)");
-			else
-				sprintf(beststr, "%u", bestscore);
+		if (lumpnum != LUMPERROR)
+			PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_CACHE);
+		else
+			PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE);
 
-			V_DrawString(160 - 88, 122, V_YELLOWMAP, "BEST SCORE:");
-			V_DrawRightAlignedString(160 + 88, 122, V_ALLOWLOWERCASE, beststr);
+		V_DrawSmallScaledPatch(208, 32+lsheadingheight, 0, PictureOfLevel);
 
-			if (besttime == UINT32_MAX)
-				sprintf(beststr, "(none)");
-			else
-				sprintf(beststr, "%i:%02i.%02i", G_TicsToMinutes(besttime, true),
-																				 G_TicsToSeconds(besttime),
-																				 G_TicsToCentiseconds(besttime));
+		V_DrawString(104 - 72, 32+lsheadingheight/2, 0, "* LEVEL RECORDS *");
 
-			V_DrawString(160-88, 132, V_YELLOWMAP, "BEST TIME:");
-			V_DrawRightAlignedString(160+88, 132, V_ALLOWLOWERCASE, beststr);
+		if (P_HasGrades(cv_nextmap.value, 0))
+			V_DrawScaledPatch(235, 135, 0, ngradeletters[bestoverall]);
+
+		if (P_HasGrades(cv_nextmap.value, cv_dummymares.value))
+			{//make bigger again
+			V_DrawString(104 - 72, 48+lsheadingheight/2, V_YELLOWMAP, "BEST GRADE:");
+			V_DrawSmallScaledPatch(104 + 72 - (ngradeletters[bestgrade]->width/2),
+				48+lsheadingheight/2 + 8 - (ngradeletters[bestgrade]->height/2),
+				0, ngradeletters[bestgrade]);
+		}
 
-			if (cv_dummymares.value == 0) {
-				// Draw record emblems.
-				em = M_GetLevelEmblems(cv_nextmap.value);
-				while (em)
-				{
-					switch (em->type)
-					{
-						case ET_NGRADE: yHeight = 112; break;
-						case ET_NTIME:  yHeight = 132; break;
-						default:
-							goto skipThisOne;
-					}
+		if (!bestscore)
+			sprintf(beststr, "(none)");
+		else
+			sprintf(beststr, "%u", bestscore);
 
-					if (em->collected)
-						V_DrawSmallMappedPatch(160+88, yHeight, 0, W_CachePatchName(M_GetEmblemPatch(em), PU_CACHE),
-																	 R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE));
-					else
-						V_DrawSmallScaledPatch(160+88, yHeight, 0, W_CachePatchName("NEEDIT", PU_CACHE));
+		V_DrawString(104 - 72, 58+lsheadingheight/2, V_YELLOWMAP, "BEST SCORE:");
+		V_DrawRightAlignedString(104 + 72, 58+lsheadingheight/2, V_ALLOWLOWERCASE, beststr);
+
+		if (besttime == UINT32_MAX)
+			sprintf(beststr, "(none)");
+		else
+			sprintf(beststr, "%i:%02i.%02i", G_TicsToMinutes(besttime, true),
+																			 G_TicsToSeconds(besttime),
+																			 G_TicsToCentiseconds(besttime));
+
+		V_DrawString(104 - 72, 68+lsheadingheight/2, V_YELLOWMAP, "BEST TIME:");
+		V_DrawRightAlignedString(104 + 72, 68+lsheadingheight/2, V_ALLOWLOWERCASE, beststr);
 
-					skipThisOne:
-					em = M_GetLevelEmblems(-1);
+		if (cv_dummymares.value == 0) {
+			// Draw record emblems.
+			em = M_GetLevelEmblems(cv_nextmap.value);
+			while (em)
+			{
+				switch (em->type)
+				{
+					case ET_NGRADE: yHeight = 48; break;
+					case ET_NTIME:  yHeight = 68; break;
+					default:
+						goto skipThisOne;
 				}
+
+				if (em->collected)
+					V_DrawSmallMappedPatch(104+76, yHeight+lsheadingheight/2, 0, W_CachePatchName(M_GetEmblemPatch(em), PU_CACHE),
+																 R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(em), GTC_CACHE));
+				else
+					V_DrawSmallScaledPatch(104+76, yHeight+lsheadingheight/2, 0, W_CachePatchName("NEEDIT", PU_CACHE));
+
+				skipThisOne:
+				em = M_GetLevelEmblems(-1);
 			}
 		}
-		// ALWAYS DRAW level name even when not on this menu!
-		else
-		{
-			consvar_t *ncv;
-			INT32 x = SP_NightsAttackDef.x;
-			INT32 y = SP_NightsAttackDef.y;
-
-			ncv = (consvar_t *)SP_NightsAttackMenu[0].itemaction;
-			V_DrawString(x, y + SP_NightsAttackMenu[0].alphaKey, V_TRANSLUCENT, SP_NightsAttackMenu[0].text);
-			V_DrawString(BASEVIDWIDTH - x - V_StringWidth(ncv->string, 0),
-									 y + SP_NightsAttackMenu[0].alphaKey, V_YELLOWMAP|V_TRANSLUCENT, ncv->string);
-		}
 	}
+
+	// ALWAYS DRAW level even when not on this menu!
+	if (currentMenu != &SP_NightsAttackDef)
+		V_DrawString(SP_NightsAttackDef.x, SP_NightsAttackDef.y + SP_TimeAttackMenu[nalevel].alphaKey, V_TRANSLUCENT, SP_NightsAttackMenu[nalevel].text);
+}
+
+static void M_NightsAttackLevelSelect(INT32 choice)
+{
+	(void)choice;
+	SP_NightsAttackLevelSelectDef.prevMenu = currentMenu;
+	M_SetupNextMenu(&SP_NightsAttackLevelSelectDef);
 }
 
 // Going to Nights Attack menu...
@@ -5536,27 +6193,27 @@ static void M_NightsAttack(INT32 choice)
 {
 	(void)choice;
 
-	memset(skins_cons_t, 0, sizeof (skins_cons_t));
-
+	SP_NightsAttackDef.prevMenu = &MainDef;
 	levellistmode = LLM_NIGHTSATTACK; // Don't be dependent on cv_newgametype
 
-	if (M_CountLevelsToShowInList() == 0)
+	if (!M_PrepareLevelPlatter(-1))
 	{
 		M_StartMessage(M_GetText("No NiGHTS-attackable levels found.\n"),NULL,MM_NOTHING);
 		return;
 	}
-
 	// This is really just to make sure Sonic is the played character, just in case
 	M_PatchSkinNameTable();
 
-	M_PrepareLevelSelect();
 	M_SetupNextMenu(&SP_NightsAttackDef);
-	Nextmap_OnChange();
-
-	itemOn = nastart; // "Start" is selected.
+	if (!M_CanShowLevelInList(cv_nextmap.value-1, -1) && levelselect.rows[0].maplist[0])
+		CV_SetValue(&cv_nextmap, levelselect.rows[0].maplist[0]);
+	else
+		Nextmap_OnChange();
 
 	G_SetGamestate(GS_TIMEATTACK);
 	S_ChangeMusicInternal("_inter", true);
+
+	itemOn = nastart; // "Start" is selected.
 }
 
 // Player has selected the "START" from the nights attack screen
@@ -5672,8 +6329,7 @@ static void M_EraseGuest(INT32 choice)
 		M_SetupNextMenu(&SP_NightsAttackDef);
 	else
 		M_SetupNextMenu(&SP_TimeAttackDef);
-	CV_AddValue(&cv_nextmap, -1);
-	CV_AddValue(&cv_nextmap, 1);
+	Nextmap_OnChange();
 	M_StartMessage(M_GetText("Guest replay data erased.\n"),NULL,MM_NOTHING);
 }
 
@@ -5699,8 +6355,7 @@ static void M_OverwriteGuest(const char *which, boolean nights)
 		M_SetupNextMenu(&SP_NightsAttackDef);
 	else
 		M_SetupNextMenu(&SP_TimeAttackDef);
-	CV_AddValue(&cv_nextmap, -1);
-	CV_AddValue(&cv_nextmap, 1);
+	Nextmap_OnChange();
 	M_StartMessage(M_GetText("Guest replay data saved.\n"),NULL,MM_NOTHING);
 }
 
@@ -5791,9 +6446,7 @@ static void M_ModeAttackEndGame(INT32 choice)
 	G_SetGamestate(GS_TIMEATTACK);
 	modeattacking = ATTACKING_NONE;
 	S_ChangeMusicInternal("_inter", true);
-	// Update replay availability.
-	CV_AddValue(&cv_nextmap, 1);
-	CV_AddValue(&cv_nextmap, -1);
+	Nextmap_OnChange();
 }
 
 // ========
@@ -6165,25 +6818,6 @@ static void M_ChooseRoom(INT32 choice)
 // Start Server Menu
 //===========================================================================
 
-//
-// FindFirstMap
-//
-// Finds the first map of a particular gametype
-// Defaults to 1 if nothing found.
-//
-static INT32 M_FindFirstMap(INT32 gtype)
-{
-	INT32 i;
-
-	for (i = 0; i < NUMMAPS; i++)
-	{
-		if (mapheaderinfo[i] && (mapheaderinfo[i]->typeoflevel & gtype))
-			return i + 1;
-	}
-
-	return 1;
-}
-
 static void M_StartServer(INT32 choice)
 {
 	boolean StartSplitScreenGame = (currentMenu == &MP_SplitServerDef);
@@ -6224,15 +6858,13 @@ static void M_StartServer(INT32 choice)
 
 static void M_DrawServerMenu(void)
 {
-	lumpnum_t lumpnum;
-	patch_t *PictureOfLevel;
-
 	M_DrawGenericMenu();
 
 #ifndef NONET
 	// Room name
 	if (currentMenu == &MP_ServerDef)
 	{
+		M_DrawLevelPlatterHeader(currentMenu->y - lsheadingheight/2, "Server settings", true);
 		if (ms_RoomId < 0)
 			V_DrawRightAlignedString(BASEVIDWIDTH - currentMenu->x, currentMenu->y + MP_ServerMenu[mp_server_room].alphaKey,
 			                         V_YELLOWMAP, (itemOn == mp_server_room) ? "<Select to change>" : "<Offline Mode>");
@@ -6242,27 +6874,69 @@ static void M_DrawServerMenu(void)
 	}
 #endif
 
-	//  A 160x100 image of the level as entry MAPxxP
-	lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
+	if (cv_nextmap.value)
+	{
+		patch_t *PictureOfLevel;
+		lumpnum_t lumpnum;
+		char headerstr[40];
 
-	if (lumpnum != LUMPERROR)
-		PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_CACHE);
-	else
-		PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE);
+		sprintf(headerstr, "%s - %s", cv_newgametype.string, cv_nextmap.string);
+
+		M_DrawLevelPlatterHeader(currentMenu->y + MP_ServerMenu[mp_server_levelgt].alphaKey - 10 - lsheadingheight/2, (const char *)headerstr, true);
+
+		//  A 160x100 image of the level as entry MAPxxP
+		lumpnum = W_CheckNumForName(va("%sP", G_BuildMapName(cv_nextmap.value)));
+
+		if (lumpnum != LUMPERROR)
+			PictureOfLevel = W_CachePatchName(va("%sP", G_BuildMapName(cv_nextmap.value)), PU_CACHE);
+		else
+			PictureOfLevel = W_CachePatchName("BLANKLVL", PU_CACHE);
 
-	V_DrawSmallScaledPatch((BASEVIDWIDTH*3/4)-(SHORT(PictureOfLevel->width)/4), ((BASEVIDHEIGHT*3/4)-(SHORT(PictureOfLevel->height)/4)+10), 0, PictureOfLevel);
+		V_DrawSmallScaledPatch(319 - (currentMenu->x + (SHORT(PictureOfLevel->width)/2)), currentMenu->y + MP_ServerMenu[mp_server_levelgt].alphaKey, 0, PictureOfLevel);
+	}
 }
 
-static void M_MapChange(INT32 choice)
+static void M_GameTypeChange(INT32 choice)
 {
 	(void)choice;
 
+	MISC_ChangeGameTypeDef.prevMenu = currentMenu;
+	M_SetupNextMenu(&MISC_ChangeGameTypeDef);
+	if (Playing())
+		itemOn = gametype;
+
+	Z_Free(char_notes);
+	char_notes = NULL;
+}
+
+void M_DrawGameTypeMenu(void)
+{
+	M_DrawGenericMenu();
+	M_DrawLevelPlatterHeader(currentMenu->y - lsheadingheight, "Select Gametype", true);
+
+	if (!char_notes)
+		char_notes = V_WordWrap(0, (160 - 30) - 8, V_ALLOWLOWERCASE, gametypedesc[itemOn].notes);
+
+	V_DrawFill(160, currentMenu->y, (160 - 30), 72 + 8, 159);
+	V_DrawString(164, currentMenu->y + 4, V_RETURN8|V_ALLOWLOWERCASE, char_notes);
+}
+
+static void M_MapChange(INT32 choice)
+{
+	MISC_ChangeLevelDef.prevMenu = currentMenu;
 	levellistmode = LLM_CREATESERVER;
 
-	CV_SetValue(&cv_newgametype, gametype);
-	CV_SetValue(&cv_nextmap, gamemap);
+	CV_SetValue(&cv_newgametype, choice);
+
+	if (Playing() && !(M_CanShowLevelOnPlatter(cv_nextmap.value-1, choice)) && (M_CanShowLevelOnPlatter(gamemap-1, choice)))
+		CV_SetValue(&cv_nextmap, gamemap);
+
+	if (!M_PrepareLevelPlatter(choice))
+	{
+		M_StartMessage(M_GetText("No selectable levels found.\n"),NULL,MM_NOTHING);
+		return;
+	}
 
-	M_PrepareLevelSelect();
 	M_SetupNextMenu(&MISC_ChangeLevelDef);
 }
 
@@ -6270,19 +6944,27 @@ static void M_StartSplitServerMenu(INT32 choice)
 {
 	(void)choice;
 	levellistmode = LLM_CREATESERVER;
-	M_PrepareLevelSelect();
+	Newgametype_OnChange();
 	M_SetupNextMenu(&MP_SplitServerDef);
 }
 
+static void M_ServerOptions(INT32 choice)
+{
+	(void)choice;
+
+	OP_ServerOptionsDef.prevMenu = currentMenu;
+	M_SetupNextMenu(&OP_ServerOptionsDef);
+}
+
 #ifndef NONET
 static void M_StartServerMenu(INT32 choice)
 {
 	(void)choice;
-	levellistmode = LLM_CREATESERVER;
-	M_PrepareLevelSelect();
 	ms_RoomId = -1;
+	levellistmode = LLM_CREATESERVER;
+	Newgametype_OnChange();
 	M_SetupNextMenu(&MP_ServerDef);
-
+	itemOn = 1;
 }
 
 // ==============
diff --git a/src/m_menu.h b/src/m_menu.h
index 1c3b226cf8f000c95f41fbf4089493074f8c3100..2e20789efc09792618ac9650b1c7d74edcd8835e 100644
--- a/src/m_menu.h
+++ b/src/m_menu.h
@@ -66,7 +66,7 @@ void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtyp
 // Called by linux_x/i_video_xshm.c
 void M_QuitResponse(INT32 ch);
 
-// Determines whether to show a level in the list
+// Determines whether to show a level in the list (platter version does not need to be exposed)
 boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt);
 
 
@@ -180,6 +180,28 @@ typedef struct
 	UINT8 next;
 } description_t;
 
+// level select platter
+typedef struct
+{
+	char header[22+5]; // mapheader_t lvltttl max length + " ZONE"
+	INT32 maplist[3];
+	char mapnames[3][17+1];
+	boolean mapavailable[4]; // mapavailable[3] == wide or not
+} levelselectrow_t;
+
+typedef struct
+{
+	UINT8 numrows;
+	levelselectrow_t *rows;
+} levelselect_t;
+// experimental level select end
+
+// descriptions for gametype select screen
+typedef struct
+{
+	char notes[441];
+} gtdesc_t;
+
 // mode descriptions for video mode menu
 typedef struct
 {
@@ -220,6 +242,9 @@ void M_ForceSaveSlotSelected(INT32 sslot);
 
 void M_CheatActivationResponder(INT32 ch);
 
+// Level select updating
+void Nextmap_OnChange(void);
+
 // Screenshot menu updating
 void Moviemode_mode_Onchange(void);
 void Screenshot_option_Onchange(void);
@@ -261,14 +286,14 @@ void Screenshot_option_Onchange(void);
 	NULL\
 }
 
-#define MAPICONMENUSTYLE(header, source, prev)\
+#define MAPPLATTERMENUSTYLE(header, source)\
 {\
 	header,\
 	sizeof (source)/sizeof (menuitem_t),\
-	prev,\
+	&MainDef,\
 	source,\
-	M_DrawServerMenu,\
-	27,40,\
+	M_DrawLevelPlatterMenu,\
+	0,0,\
 	0,\
 	NULL\
 }
diff --git a/src/p_setup.c b/src/p_setup.c
index 975cc600d9be737d6f83537514de38c110635c9b..a0c745e6085f3b6ec00278943b5edd1ab54a62a9 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -199,6 +199,8 @@ static void P_ClearSingleMapHeaderInfo(INT16 i)
 	const INT16 num = (INT16)(i-1);
 	DEH_WriteUndoline("LEVELNAME", mapheaderinfo[num]->lvlttl, UNDO_NONE);
 	mapheaderinfo[num]->lvlttl[0] = '\0';
+	DEH_WriteUndoline("SELECTHEADING", mapheaderinfo[num]->selectheading, UNDO_NONE);
+	mapheaderinfo[num]->selectheading[0] = '\0';
 	DEH_WriteUndoline("SUBTITLE", mapheaderinfo[num]->subttl, UNDO_NONE);
 	mapheaderinfo[num]->subttl[0] = '\0';
 	DEH_WriteUndoline("ACT", va("%d", mapheaderinfo[num]->actnum), UNDO_NONE);
diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c
index aa572e6e036fe41323c4cbcbc3802b5d13aa7c9d..7d33f2554520fa6ccdc82919d3ecb3f1b5803a0f 100644
--- a/src/sdl/i_video.c
+++ b/src/sdl/i_video.c
@@ -909,7 +909,7 @@ static inline boolean I_SkipFrame(void)
 		case GS_LEVEL:
 			if (!paused)
 				return false;
-		case GS_TIMEATTACK:
+		//case GS_TIMEATTACK: -- sorry optimisation but now we have a cool level platter and that being laggardly looks terrible
 		case GS_WAITINGPLAYERS:
 			return skip; // Skip odd frames
 		default:
diff --git a/src/sdl12/i_video.c b/src/sdl12/i_video.c
index 197924edacceed81f86529616221bb649e0b80ae..1fa80e7d4343fd69a921bbe256e6354dffec8e93 100644
--- a/src/sdl12/i_video.c
+++ b/src/sdl12/i_video.c
@@ -1311,7 +1311,7 @@ static inline boolean I_SkipFrame(void)
 		case GS_LEVEL:
 			if (!paused)
 				return false;
-		case GS_TIMEATTACK:
+		//case GS_TIMEATTACK: -- sorry optimisation but now we have a cool level platter and that being laggardly looks terrible
 		case GS_WAITINGPLAYERS:
 			return skip; // Skip odd frames
 		default:
diff --git a/src/v_video.c b/src/v_video.c
index 9109ce5ccf3baf2d1c4be6fb764a6332c91aecb0..fb02dfc968c62d1cae81358b2190eb5c744ff8d0 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -325,6 +325,13 @@ static inline UINT8 transmappedpdraw(const UINT8 *dest, const UINT8 *source, fix
 {
 	return *(v_translevel + (((*(v_colormap + source[ofs>>FRACBITS]))<<8)&0xff00) + (*dest&0xff));
 }
+static inline UINT8 staticpdraw(const UINT8 *dest, const UINT8 *source, fixed_t ofs)
+{
+	UINT8 val = source[ofs>>FRACBITS];
+	(void)dest;
+	if (val < 7) return val;
+	return M_RandomKey(7+1)+(val-7);//M_RandomByte();
+}
 
 // Draws a patch scaled to arbitrary size.
 void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t *patch, const UINT8 *colormap)
@@ -356,22 +363,30 @@ void V_DrawFixedPatch(fixed_t x, fixed_t y, fixed_t pscale, INT32 scrn, patch_t
 	patchdrawfunc = standardpdraw;
 
 	v_translevel = NULL;
-	if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT)))
+	if ((alphalevel = ((scrn & V_ALPHAMASK) >> V_ALPHASHIFT)) == 12) // static
 	{
-		if (alphalevel == 13)
-			alphalevel = hudminusalpha[cv_translucenthud.value];
-		else if (alphalevel == 14)
-			alphalevel = 10 - cv_translucenthud.value;
-		else if (alphalevel == 15)
-			alphalevel = hudplusalpha[cv_translucenthud.value];
-
-		if (alphalevel >= 10)
-			return; // invis
+		alphalevel = 0;
+		patchdrawfunc = staticpdraw;
 	}
-	if (alphalevel)
+	else
 	{
-		v_translevel = transtables + ((alphalevel-1)<<FF_TRANSSHIFT);
-		patchdrawfunc = translucentpdraw;
+		if (alphalevel)
+		{
+			if (alphalevel == 13)
+				alphalevel = hudminusalpha[cv_translucenthud.value];
+			else if (alphalevel == 14)
+				alphalevel = 10 - cv_translucenthud.value;
+			else if (alphalevel == 15)
+				alphalevel = hudplusalpha[cv_translucenthud.value];
+
+			if (alphalevel >= 10)
+				return; // invis
+		}
+		if (alphalevel)
+		{
+			v_translevel = transtables + ((alphalevel-1)<<FF_TRANSSHIFT);
+			patchdrawfunc = translucentpdraw;
+		}
 	}
 
 	v_colormap = NULL;
diff --git a/src/v_video.h b/src/v_video.h
index 353f84c1d973223e642436000f1464211fdeb7a0..ca1f58a302220928b6ffc079bbb7fa7873bfbc20 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -90,6 +90,7 @@ extern RGBA_t *pLocalPalette;
 #define V_70TRANS            0x00070000
 #define V_80TRANS            0x00080000 // used to be V_8020TRANS
 #define V_90TRANS            0x00090000
+#define V_STATIC             0x000C0000 // ogl unsupported kthnxbai
 #define V_HUDTRANSHALF       0x000D0000
 #define V_HUDTRANS           0x000E0000 // draw the hud translucent
 #define V_HUDTRANSDOUBLE     0x000F0000
diff --git a/src/win32/win_vid.c b/src/win32/win_vid.c
index 0960bb6dd62764e53405d7d51bc48854b7871daf..31d1b8120d95174ea8134054a617ee845a5d3af4 100644
--- a/src/win32/win_vid.c
+++ b/src/win32/win_vid.c
@@ -322,7 +322,7 @@ static inline boolean I_SkipFrame(void)
 		case GS_LEVEL:
 			if (!paused)
 				return false;
-		case GS_TIMEATTACK:
+		//case GS_TIMEATTACK: -- sorry optimisation but now we have a cool level platter and that being laggardly looks terrible
 #ifndef CLIENT_LOADINGSCREEN
 		case GS_WAITINGPLAYERS:
 #endif
diff --git a/src/y_inter.c b/src/y_inter.c
index 5548fe346c88a59c9d016601483e7430519a200e..c4f425beb843d726a3efdc9a20b52bc0a5fbb1c0 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -900,8 +900,7 @@ static void Y_UpdateRecordReplays(void)
 		CONS_Printf(M_GetText("\x82" "Earned %hu emblem%s for Record Attack records.\n"), (UINT16)earnedEmblems, earnedEmblems > 1 ? "s" : "");
 
 	// Update timeattack menu's replay availability.
-	CV_AddValue(&cv_nextmap, 1);
-	CV_AddValue(&cv_nextmap, -1);
+	Nextmap_OnChange();
 }
 
 //