/* m_options.c
**
** New options menu code.
**
** Sorry this got so convoluted. It was originally much cleaner until
** I started adding all sorts of gadgets to the menus. I might someday
** make a project of rewriting the entire menu system using Amiga-style
** taglists to describe each menu item. We'll see...
*/

#include "m_alloc.h"

#include "doomdef.h"
#include "dstrings.h"

#include "c_console.h"
#include "c_dispatch.h"
#include "c_bind.h"

#include "d_main.h"

#include "i_system.h"
#include "i_video.h"
#include "z_zone.h"
#include "v_video.h"
#include "v_text.h"
#include "w_wad.h"

#include "r_local.h"

#include "p_local.h"
#include "hu_stuff.h"

#include "g_game.h"

#include "m_argv.h"
#include "m_swap.h"

#include "s_sound.h"

#include "doomstat.h"

#include "m_misc.h"

#include "version.h"	// [BC]
#include "d_player.h"
#include "c_cvars.h"
#include "sv_main.h"
#include "i_net.h"

// Data.
#include "m_menu.h"

//
// defaulted values
//
CVAR (mouse_sensitivity, "1.0", CVAR_ARCHIVE)

// Show messages has default, 0 = off, 1 = on
CVAR (show_messages, "1", CVAR_ARCHIVE)

extern BOOL				OptionsActive;

extern int				screenSize;
extern short			skullAnimCounter;
int cl_dmflags;
int cl_dmflags2;

static int current = 1;
char *temp = NULL;
char *conip = " ";

EXTERN_CVAR (cl_run)
EXTERN_CVAR (invertmouse)
EXTERN_CVAR (lookspring)
EXTERN_CVAR (lookstrafe)
EXTERN_CVAR (crosshair)
EXTERN_CVAR (freelook)

void M_ChangeMessages(void);
void M_SizeDisplay(float diff);
void M_StartControlPanel(void);

int  M_StringHeight(char *string);
void M_ClearMenus (void);



value_t YesNo[2] = {
	{ 0.0, "No", 0 },
	{ 1.0, "Yes", 0 }
};

value_t NoYes[2] = {
	{ 0.0, "Yes", 0 },
	{ 1.0, "No", 0 }
};

value_t OnOff[2] = {
	{ 0.0, "Off", 0 },
	{ 1.0, "On", 0 }
};

menu_t  *CurrentMenu;
int		CurrentItem;
static BOOL	WaitingForKey;
static char	   *OldMessage;
static itemtype OldType;
static BOOL StrInput;	// [BC]

/*=======================================
 *
 * Options Menu
 *
 *=======================================*/

static void CustomizeControls (void);
static void GameplayOptions (void);
static void CLGameplayOptions (void);
static void MoreGameplayOptions (void);	// [BC]
static void MoreCLGameplayOptions (void);	// [BC]
static void BotSetup (void);	// [BC]
static void VideoOptions (void);
static void MapRotation (void);		// [BC]
static void MoreVideoOptions (void);	// [BC]
static void MoreControlsOptions (void);	// [BC]
static void GoToConsole (void);
static void Skirmish (void);	// [BC]
static void StartSkirmishGame (void);	// [BC]
static void Server (void);
static void Client (void);
static void StartServer (void);
static void JoinGame (void);
static void M_SetupNewGame (void);
void PlayerSetup (void);	// [BC]
void TeamSetup (void);	// [BC]
void Cinematics (void);	// [BC]
void Reset2Defaults (void);
void Reset2Saved (void);

void M_DrawDMFlags (void);
void M_DrawDMFlags2 (void);
void M_DrawCLDMFlags (void);
void M_DrawCLDMFlags2 (void);
void M_DrawSTVer (void);
void M_DrawLvlStuff (void);
void M_DrawSVStuff (void);

EXTERN_CVAR (snd_midivolume)

static void SetVidMode (void);

// [BC] Multiplayer menu
static menuitem_t MultiItems[] =
{
	{ more,		"Skirmish",				{NULL},					{0.0}, {0.0},	{0.0}, {(value_t *)Skirmish} },
	{ more,		"Start a game",			{NULL},					{0.0}, {0.0},	{0.0}, {(value_t *)Server} },
	{ more,		"Join a game",			{NULL},					{0.0}, {0.0},	{0.0}, {(value_t *)Client} },
	{ more,		"Team setup",			{NULL},					{0.0}, {0.0},	{0.0}, {(value_t *)TeamSetup} },
	{ more,		"Player setup",			{NULL},					{0.0}, {0.0},	{0.0}, {(value_t *)PlayerSetup} },
	{ redtext,	" ",					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ discrete,	"Dedicated",			{&cl_run},				{2.0}, {0.0},	{0.0}, {OnOff} },
};

menu_t MultiMenu = {
	{ 'M','_','M','P','L','Y','R' },
	0,
	7,
	177,
	MultiItems,
	M_DrawSTVer,
	0,
};

static menuitem_t OptionItems[] =
{
	{ more,		"Customize Controls",	{NULL},					{0.0}, {0.0},	{0.0}, {(value_t *)CustomizeControls} },
	{ more,		"View cinematics",		{NULL},					{0.0}, {0.0},	{0.0}, {(value_t *)Cinematics} },
	{ more,		"Go to console",		{NULL},					{0.0}, {0.0},	{0.0}, {(value_t *)GoToConsole} },
	{ more,		"Gameplay Options",		{NULL},					{0.0}, {0.0},	{0.0}, {(value_t *)GameplayOptions} },
	{ more,		"Display Options",		{NULL},					{0.0}, {0.0},	{0.0}, {(value_t *)VideoOptions} },
	{ more,		"Set video mode",		{NULL},					{0.0}, {0.0},	{0.0}, {(value_t *)SetVidMode} },
	{ redtext,	" ",					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ slider,	"Mouse speed",			{&mouse_sensitivity},	{0.5}, {2.5},	{0.1}, {NULL} },
	{ slider,	"MIDI music volume",	{&snd_midivolume},		{0.0}, {1.0},	{0.05}, {NULL} },
	{ slider,	"MOD music volume",		{&snd_musicvolume},		{0.0}, {64.0},	{1.0}, {NULL} },
	{ slider,	"Sound volume",			{&snd_sfxvolume},		{0.0}, {15.0},	{1.0}, {NULL} },
	{ discrete,	"Always Run",			{&cl_run},				{2.0}, {0.0},	{0.0}, {OnOff} },
	{ discrete, "Always Mouselook",		{&freelook},			{2.0}, {0.0},	{0.0}, {OnOff} },
	{ discrete, "Invert Mouse",			{&invertmouse},			{2.0}, {0.0},	{0.0}, {OnOff} },
	{ discrete, "Lookspring",			{&lookspring},			{2.0}, {0.0},	{0.0}, {OnOff} },
	{ discrete, "Lookstrafe",			{&lookstrafe},			{2.0}, {0.0},	{0.0}, {OnOff} },
	{ redtext,	" ",					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ more,		"Reset to defaults",	{NULL},					{0.0}, {0.0},	{0.0}, {(value_t *)Reset2Defaults} },
	{ more,		"Reset to last saved",	{NULL},					{0.0}, {0.0},	{0.0}, {(value_t *)Reset2Saved} }
};

menu_t OptionMenu = {
	{ 'M','_','O','P','T','T','T','L' },
	0,
	16,	// [BC]
	177,
	OptionItems,
	M_DrawSTVer,
	0,
};


/*=======================================
 *
 * Controls Menu
 *
 *=======================================*/

static menuitem_t ControlsItems[] = {
	{ whitetext,"ENTER to change, BACKSPACE to clear", NULL, 0.0, 0.0, 0.0, NULL },
	{ redtext,	" ",					NULL, 0.0, 0.0,	0.0, NULL },
	{ control,	"Attack",				NULL, 0.0, 0.0, 0.0, (value_t *)"+attack" },
	{ control,	"Next Weapon",			NULL, 0.0, 0.0, 0.0, (value_t *)"weapnext" },	// Was already here
	{ control,	"Previous Weapon",		NULL, 0.0, 0.0, 0.0, (value_t *)"weapprev" },	// TIJ
	{ control,	"Use / Open",			NULL, 0.0, 0.0, 0.0, (value_t *)"+use" },
	{ control,	"Jump",					NULL, 0.0, 0.0, 0.0, (value_t *)"+jump" },
	{ control,	"Walk forward",			NULL, 0.0, 0.0, 0.0, (value_t *)"+forward" },
	{ control,	"Backpedal",			NULL, 0.0, 0.0, 0.0, (value_t *)"+back" },
	{ control,	"Turn left",			NULL, 0.0, 0.0, 0.0, (value_t *)"+left" },
	{ control,	"Turn right",			NULL, 0.0, 0.0, 0.0, (value_t *)"+right" },
	{ control,	"Run",					NULL, 0.0, 0.0, 0.0, (value_t *)"+speed" },
	{ control,	"Step left",			NULL, 0.0, 0.0, 0.0, (value_t *)"+moveleft" },
	{ control,	"Step right",			NULL, 0.0, 0.0, 0.0, (value_t *)"+moveright" },
	{ control,	"Sidestep",				NULL, 0.0, 0.0, 0.0, (value_t *)"+strafe" },	// [BC] Put some in the second menu
	{ redtext,	" ",					NULL, 0.0, 0.0,	0.0, NULL },
	{ more,		"More options",		NULL, 0.0, 0.0,	0.0, (value_t *)MoreControlsOptions }
};

menu_t ControlsMenu = {
	{ 'M','_','C','O','N','T','R','O' },
	1,
	17, // [BC]
	0,
	ControlsItems,
	NULL,
	0,
};

// [BC]
static menuitem_t MoreControlsItems[] = {
	{ whitetext,"ENTER to change, BACKSPACE to clear", NULL, 0.0, 0.0, 0.0, NULL },
	{ redtext,	" ",					NULL,					0.0, 0.0,	0.0, NULL },
	{ control,	"Move up",				NULL, 0.0, 0.0, 0.0, (value_t *)"+moveup" },
	{ control,	"Move down",			NULL, 0.0, 0.0, 0.0, (value_t *)"+movedown" },
	{ control,	"Look up",				NULL, 0.0, 0.0, 0.0, (value_t *)"+lookup" },
	{ control,	"Look down",			NULL, 0.0, 0.0, 0.0, (value_t *)"+lookdown" },
	{ control,	"Center view",			NULL, 0.0, 0.0, 0.0, (value_t *)"centerview" },
	{ control,	"Mouse look",			NULL, 0.0, 0.0, 0.0, (value_t *)"+mlook" },
	{ control,	"Keyboard look",		NULL, 0.0, 0.0, 0.0, (value_t *)"+klook" },
	{ control,	"Toggle automap",		NULL, 0.0, 0.0, 0.0, (value_t *)"togglemap" },
	{ control,	"Chasecam",				NULL, 0.0, 0.0, 0.0, (value_t *)"chase" },
	{ control,	"Drop rune",			NULL, 0.0, 0.0, 0.0, (value_t *)"droprune" },
	{ control,	"Taunt",				NULL, 0.0, 0.0, 0.0, (value_t *)"+taunt" },
	{ control,	"Showmedals",			NULL, 0.0, 0.0, 0.0, (value_t *)"+showmedals" },
};

// [BC]
menu_t MoreControlsMenu = {
	{ 'M','_','C','O','N','T','R','O' },
	1,
	14,
	0,
	MoreControlsItems,
	NULL,
	0,
};

/*=======================================
 *
 * Display Options Menu
 *
 *=======================================*/
static void StartMessagesMenu (void);
EXTERN_CVAR (am_rotate)
EXTERN_CVAR (am_overlay)
EXTERN_CVAR (st_scale)
EXTERN_CVAR (am_usecustomcolors)
EXTERN_CVAR (am_dmscores)
EXTERN_CVAR (r_detail)
EXTERN_CVAR (r_stretchsky)
EXTERN_CVAR (r_columnmethod)
EXTERN_CVAR (r_drawfuzz)
EXTERN_CVAR (cl_rockettrails)
EXTERN_CVAR (cl_grenadetrails)	// [BC]
EXTERN_CVAR (cl_pufftype)
EXTERN_CVAR (cl_bloodtype)
EXTERN_CVAR (cl_gamemode)	// [BC]
EXTERN_CVAR (autoaim)
EXTERN_CVAR (name)
EXTERN_CVAR (color)
EXTERN_CVAR (skin)
EXTERN_CVAR (team)
EXTERN_CVAR (gender)
EXTERN_CVAR (neverswitchonpickup)
EXTERN_CVAR (rocketpreferred)
EXTERN_CVAR (plasmapreferred)
EXTERN_CVAR (teamname)
EXTERN_CVAR (teamcolor)
EXTERN_CVAR (railcolor)
EXTERN_CVAR (expert)

EXTERN_CVAR (wipetype)
EXTERN_CVAR (vid_palettehack)
EXTERN_CVAR (screenblocks)
EXTERN_CVAR (cl_namedraw)
EXTERN_CVAR (cl_showscores)
EXTERN_CVAR (cl_onekey)

// [BC]
CVAR (cl_gamemode, "1", CVAR_ARCHIVE)
CVAR (cl_timelimit, "0", CVAR_ARCHIVE)
CVAR (cl_fraglimit, "25", CVAR_ARCHIVE)
CVAR (cl_scorelimit, "0", CVAR_ARCHIVE)
CVAR (cl_pointlimit, "0", CVAR_ARCHIVE)

static value_t Crosshairs[] =
{
	{ 0.0, "None", 0 },
	{ 1.0, "Cross 1", 0 },
	{ 2.0, "Cross 2", 0 },
	{ 3.0, "X", 0 },
	{ 4.0, "Diamond", 0 },
	{ 5.0, "Dot", 0 },
	{ 6.0, "Box", 0 },
	{ 7.0, "Angle", 0 },
	{ 8.0, "Big Thing", 0 }
};

static value_t DetailModes[] =
{
	{ 0.0, "Normal", 0 },
	{ 1.0, "Double Horizontally", 0 },
	{ 2.0, "Double Vertically", 0 },
	{ 3.0, "Double Horiz and Vert", 0 }
};

static value_t ColumnMethods[] = {
	{ 0.0, "Original", 0 },
	{ 1.0, "Optimized", 0 }
};

static value_t BloodTypes[] = {
	{ 0.0, "Sprites", 0 },
	{ 1.0, "Sprites and Particles", 0 },
	{ 2.0, "Particles", 0 }
};

static value_t PuffTypes[] = {
	{ 0.0, "Sprites", 0 },
	{ 1.0, "Particles", 0 }
};

static value_t Wipes[] = {
	{ 0.0, "None", 0 },
	{ 1.0, "Melt", 0 },
	{ 2.0, "Burn", 0 },
	{ 3.0, "Crossfade", 0 }
};
// [BC]
static value_t GameModes[] = {
	{ 0.0, "Normal", 0 },
	{ 1.0, "Deathmatch", 0 },
	{ 2.0, "Teamgame", 0 }
};
//[BC]
static value_t TrailColors[] = {
	{ 0.0, "Blue", 0 },
	{ 1.0, "Red", 0 },
	{ 2.0, "Yellow", 0 },
	{ 3.0, "Black", 0 },
	{ 4.0, "Silver", 0 },
	{ 5.0, "Gold", 0 },
	{ 6.0, "Green", 0 },
	{ 7.0, "White", 0 },
	{ 8.0, "Purple", 0 },
	{ 9.0, "Orange", 0 },
	{ 10.0, "Rainbow", 0 }
};

// [BC]
value_t GameSkill[5] = {
	{ 0.0, "I'm too young to die.", 0 },
	{ 1.0, "Hey, not too rough..", 0 },
	{ 2.0, "Hurt me plenty.", 0 },
	{ 3.0, "Ultra-Violence.", 0 },
	{ 4.0, "Nightmare!", 0 }
};
// [BC]
value_t BotSkill[5] = {
	{ 0.0, "I want my mommy!", 0 },
	{ 1.0, "I'm allergic to pain.", 0 },
	{ 2.0, "Bring it on.", 0 },
	{ 3.0, "I thrive off pain.", 0 },
	{ 4.0, "Nightmare!", 0 }
};

// [BC]
value_t MapVals[] = {
	{ 1.0, "Entryway", LV_NORMAL|LV_DEATHMATCH },
	{ 2.0, "Underhalls", LV_NORMAL|LV_DEATHMATCH },
	{ 3.0, "The Gantlet", LV_NORMAL|LV_DEATHMATCH },
	{ 4.0, "The Focus", LV_NORMAL|LV_DEATHMATCH },
	{ 5.0, "The Waste Tunnels", LV_NORMAL|LV_DEATHMATCH },
	{ 6.0, "The Crusher", LV_NORMAL|LV_DEATHMATCH },
	{ 7.0, "Dead Simple", LV_NORMAL|LV_DEATHMATCH },
	{ 8.0, "Tricks and Traps", LV_NORMAL|LV_DEATHMATCH },
	{ 9.0, "The Pit", LV_NORMAL|LV_DEATHMATCH },
	{ 10.0, "Refueling Base", LV_NORMAL|LV_DEATHMATCH },
	{ 11.0, "'o' of Destruction!", LV_NORMAL|LV_DEATHMATCH },
	{ 12.0, "The Factory", LV_NORMAL|LV_DEATHMATCH },
	{ 13.0, "Downtown", LV_NORMAL|LV_DEATHMATCH },
	{ 14.0, "The Inmost Dens", LV_NORMAL|LV_DEATHMATCH },
	{ 15.0, "Industrial Zone", LV_NORMAL|LV_DEATHMATCH },
	{ 16.0, "Suburbs", LV_NORMAL|LV_DEATHMATCH },
	{ 17.0, "Tenements", LV_NORMAL|LV_DEATHMATCH },
	{ 18.0, "The Courtyard", LV_NORMAL|LV_DEATHMATCH },
	{ 19.0, "The Citadel", LV_NORMAL|LV_DEATHMATCH },
	{ 20.0, "Gotcha!", LV_NORMAL|LV_DEATHMATCH },
	{ 21.0, "Nirvana", LV_NORMAL|LV_DEATHMATCH },
	{ 22.0, "The Catacombs", LV_NORMAL|LV_DEATHMATCH },
	{ 23.0, "Barrels o' Fun", LV_NORMAL|LV_DEATHMATCH },
	{ 24.0, "The Chasm", LV_NORMAL|LV_DEATHMATCH },
	{ 25.0, "Bloodfalls", LV_NORMAL|LV_DEATHMATCH },
	{ 26.0, "The Abandoned Mines", LV_NORMAL|LV_DEATHMATCH },
	{ 27.0, "Monster Condo", LV_NORMAL|LV_DEATHMATCH },
	{ 28.0, "The Spirit World", LV_NORMAL|LV_DEATHMATCH },
	{ 29.0, "The Living End", LV_NORMAL|LV_DEATHMATCH },
	{ 30.0, "Icon of Sin", LV_NORMAL|LV_DEATHMATCH },
	{ 31.0, "Wolfenstein", LV_NORMAL|LV_DEATHMATCH },
	{ 32.0, "Grosse", LV_NORMAL|LV_DEATHMATCH },
	{ 33.0, "Training Center", LV_DEATHMATCH },
	{ 34.0, "Testing Center", LV_DEATHMATCH },
	{ 35.0, "Judgement Day", LV_DEATHMATCH },
	{ 36.0, "Linguica's Lair", LV_DEATHMATCH },
	{ 37.0, "Some Secret Level", LV_DEATHMATCH },
	{ 38.0, "McKinley Heritage", LV_TEAMGAME },
	{ 39.0, "Mt. Capture", LV_TEAMGAME },
	{ 40.0, "Verflutcher Temple", LV_TEAMGAME },
	{ 41.0, "Area -51 Degrees Celsius", LV_TEAMGAME },
};
// [BC]
value_t ModeVals[] = {
	{ 0.0, "Normal", 0 },
	{ 1.0, "Deathmatch", 0 },
	{ 2.0, "Teamplay deathmatch", 0 },
	{ 3.0, "Survival deathmatch", 0 },
	{ 4.0, "Score mode deathmatch", 0 },
	{ 5.0, "Instagib deathmatch", 0 },
	{ 6.0, "Terminator deathmatch", 0 },
	{ 7.0, "Oldschool deathmatch", 0 },
	{ 8.0, "Teamgame", 0 }
};

// [BC]
value_t AutoaimVals[7] = {
	{ 0.0, "Never", 0 },
	{ 1.0, "Very Low", 0 },
	{ 2.0, "Low", 0 },
	{ 3.0, "Medium", 0 },
	{ 4.0, "High", 0 },
	{ 5.0, "Very high", 0 },
	{ 6.0, "Always", 0 }
};

// [BC]
value_t EpVals[] = {
	{ 0.0, "Hell on Earth", 0 },
	{ 1.0, "The Mortal Conflict", 0 },
	{ 2.0, "Capture the Flag", 0 }
};

// [BC]
value_t BotVals[] = {
	{ 0.0, "None", 0 },
	{ 1.0, "Chubbs", 0 },
	{ 2.0, "Deimos", 0 },
	{ 3.0, "Crash", 0 }, 
	{ 4.0, "Charon", 0 },
	{ 5.0, "Pluto", 0 },
	{ 6.0, "Gamma", 0 },
	{ 7.0, "Orion", 0 },
	{ 8.0, "h4x0r", 0 },
	{ 9.0, "Massmouth", 0 },
	{ 10.0, "Doomcrate", 0 },
	{ 11.0, "Zombieman", 0 },
	{ 12.0, "Shotgunner", 0 },
	{ 13.0, "Chaingunner", 0 },
};

value_t HeSheIt[] = {
	{ 0.0, "Male", 0 },
	{ 1.0, "Female", 0 },
	{ 2.0, "Neither", 0 },
};

// [BC]
static menuitem_t MapRotationItems[] = {
	{ number,	"Timelimit",				&cl_timelimit,		6.0, 0.0,	0.0, NULL },
	{ number,	"Fraglimit",				&cl_fraglimit,		6.0, 0.0,	0.0, NULL },
	{ number,	"Pointlimit",				&cl_pointlimit,		6.0, 0.0,	0.0, NULL },
	{ discrete,	"Map types",				&cl_gamemode,		3.0, 0.0,	0.0, GameModes },	// [BC]
	{ redtext,	" ",						NULL,				0.0, 0.0,	0.0, NULL },
	{ redtext,	"Maps available",			NULL,				0.0, 0.0,	0.0, NULL },
	{ redtext,	" ",						NULL,				0.0, 0.0,	0.0, NULL },
//	{ mapspot,	NULL,						NULL,				0.0, 0.0,	0.0, NULL },
//	{ mapspot,	NULL,						NULL,				0.0, 0.0,	0.0, NULL },
//	{ mapspot,	NULL,						NULL,				0.0, 0.0,	0.0, NULL },
//	{ mapspot,	NULL,						NULL,				0.0, 0.0,	0.0, NULL },
//	{ mapspot,	NULL,						NULL,				0.0, 0.0,	0.0, NULL },
//	{ mapspot,	NULL,						NULL,				0.0, 0.0,	0.0, NULL },
//	{ mapspot,	NULL,						NULL,				0.0, 0.0,	0.0, NULL },
//	{ mapspot,	NULL,						NULL,				0.0, 0.0,	0.0, NULL },
//	{ mapspot,	NULL,						NULL,				0.0, 0.0,	0.0, NULL },
//	{ mapspot,	NULL,						NULL,				0.0, 0.0,	0.0, NULL },
	{ redtext,	" ",						NULL,				0.0, 0.0,	0.0, NULL },
	{ redtext,	"Add map(s) to list",		NULL,				0.0, 0.0,	0.0, NULL },
	{ redtext,	"Remove map(s) to list",	NULL,				0.0, 0.0,	0.0, NULL },
};

// [BC]
menu_t MapRotationMenu = {
	{ 'M','_','R','O','T','A','T','E' },
	0,
	10,
	0,
	MapRotationItems,
	NULL,
	0,
};

CVAR (cl_mode, "0", CVAR_ARCHIVE)
CVAR (cl_episode, "1", CVAR_ARCHIVE)
CVAR (cl_skill, "2", CVAR_ARCHIVE)
CVAR (cl_botskill, "2", CVAR_ARCHIVE)

// [BC]
static menuitem_t SkirMenuItems[] = {
	{ maplist,	"Level",					NULL,					41.0, 0.0, 0.0, MapVals  },
	{ discrete,	"Episode",					&cl_episode,			3.0, 0.0, 1.0, EpVals  },
	{ discrete,	"Game mode",				&cl_mode,				9.0, 0.0, 1.0, ModeVals  },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	{ number,	"Timelimit",				&cl_timelimit,			0.0, 100.0, 1.0, NULL  },
	{ number,	"Fraglimit",				&cl_fraglimit,			0.0, 100.0, 5.0, NULL  },
	{ number,	"Pointlimit",				&cl_pointlimit,			0.0, 100.0, 1.0, NULL  },
	{ number,	"Scorelimit",				&cl_scorelimit,			0.0, 1000.0, 50.0, NULL  },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	{ discrete,	"Skill",					&cl_skill,				5.0, 0.0, 1.0, GameSkill },
	{ discrete,	"Botskill",					&cl_botskill,			5.0, 0.0, 1.0, BotSkill },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	{ more,		"Bot setup...",				NULL,					0.0, 0.0, 0.0, (value_t *)BotSetup },
	{ more,		"Game options...",			NULL,					0.0, 0.0, 0.0, (value_t *)CLGameplayOptions },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	{ more,		"Start game!",				NULL,					2.0, 0.0, 0.0, (value_t *)StartSkirmishGame },
};

// [BC]
menu_t SkirMenu = {
	"M_SKIRM",
	0,
	16,
	0,
	SkirMenuItems,
	M_DrawLvlStuff,
	MNF_ALIGNLEFT,
};

// [BC]
static menuitem_t ServMenuItems[] = {
	{ maplist,	"Level",					NULL,					41.0, 0.0, 0.0, MapVals  },
	{ discrete,	"Episode",					&cl_episode,			3.0, 0.0, 1.0, EpVals  },
	{ discrete,	"Game mode",				&cl_mode,				9.0, 0.0, 1.0, ModeVals  },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	{ number,	"Timelimit",				&cl_timelimit,			0.0, 100.0, 1.0, NULL  },
	{ number,	"Fraglimit",				&cl_fraglimit,			0.0, 100.0, 5.0, NULL  },
	{ number,	"Pointlimit",				&cl_pointlimit,			0.0, 100.0, 1.0, NULL  },
	{ number,	"Scorelimit",				&cl_scorelimit,			0.0, 1000.0, 50.0, NULL  },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	//{ number,	"Max. clients",				&maxclients,			0.0, (float)MAXPLAYERS, 1.0, NULL  },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	{ discrete,	"Skill",					&cl_skill,				5.0, 0.0, 1.0, GameSkill },
	{ discrete,	"Botskill",					&cl_botskill,			5.0, 0.0, 1.0, BotSkill },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	{ more,		"Bot setup...",				NULL,					0.0, 0.0, 0.0, (value_t *)BotSetup },
	{ more,		"Game options...",			NULL,					0.0, 0.0, 0.0, (value_t *)CLGameplayOptions },
	{ more,		"Map rotation...",			NULL,					0.0, 0.0, 0.0, (value_t *)MapRotation },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	{ more,		"Start game!",				NULL,					2.0, 0.0, 0.0, (value_t *)StartServer },
};

// [BC]
menu_t ServMenu = {
	"M_SKIRM",
	0,
	19,
	0,
	ServMenuItems,
	NULL, //M_DrawSVStuff,
	MNF_ALIGNLEFT,
};

// [BC]
static menuitem_t ClientMenuItems[] = {
	{ string,	"Server IP",				NULL,					0.0, 0.0, 0.0, NULL  },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	{ more,		"Start game!",				NULL,					2.0, 0.0, 0.0, (value_t *)JoinGame },
};

// [BC]
menu_t ClientMenu = {
	"M_SKIRM",
	0,
	3,
	0,
	ClientMenuItems,
	NULL, //M_DrawSVStuff,
	MNF_ALIGNLEFT,
};

// [BC]
static menuitem_t BSMenuItems[] = {
	{ discrete,	"Botskill",					&cl_botskill,			5.0, 0.0, 1.0, BotSkill },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	{ botslot,	"Slot 1:",					NULL,					14.0, 0.0, 1.0, BotVals },
	{ botslot,	"Slot 2:",					NULL,					14.0, 0.0, 1.0, BotVals },
	{ botslot,	"Slot 3:",					NULL,					14.0, 0.0, 1.0, BotVals },
	{ botslot,	"Slot 4:",					NULL,					14.0, 0.0, 1.0, BotVals },
	{ botslot,	"Slot 5:",					NULL,					14.0, 0.0, 1.0, BotVals },
	{ botslot,	"Slot 6:",					NULL,					14.0, 0.0, 1.0, BotVals },
	{ botslot,	"Slot 7:",					NULL,					14.0, 0.0, 1.0, BotVals },
	{ botslot,	"Slot 8:",					NULL,					14.0, 0.0, 1.0, BotVals },
	{ botslot,	"Slot 9:",					NULL,					14.0, 0.0, 1.0, BotVals },
	{ botslot,	"Slot 10:",					NULL,					14.0, 0.0, 1.0, BotVals },
	{ botslot,	"Slot 11:",					NULL,					14.0, 0.0, 1.0, BotVals },
	{ botslot,	"Slot 12:",					NULL,					14.0, 0.0, 1.0, BotVals },
	{ botslot,	"Slot 13:",					NULL,					14.0, 0.0, 1.0, BotVals },
	{ botslot,	"Slot 14:",					NULL,					14.0, 0.0, 1.0, BotVals },
	{ botslot,	"Slot 15:",					NULL,					14.0, 0.0, 1.0, BotVals },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	{ more,		"Start game!",				NULL,					0.0, 0.0, 0.0, (value_t *)StartSkirmishGame },
};

// [BC]
menu_t BSMenu = {
	"M_PSTTL",
	0,
	19,
	0,
	BSMenuItems,
	NULL,
	MNF_ALIGNLEFT,
};

static menuitem_t VideoItems[] = {
	{ more,		"Messages",				{NULL},					{0.0}, {0.0},	{0.0}, {(value_t *)StartMessagesMenu} },
	{ redtext,	" ",					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ slider,	"Screen size",			{&screenblocks},	   	{3.0}, {12.0},	{1.0}, {NULL} },
	{ slider,	"Brightness",			{&Gamma},			   	{1.0}, {3.0},	{0.1}, {NULL} },
	{ discrete,	"Crosshair",			{&crosshair},		   	{9.0}, {0.0},	{0.0}, {Crosshairs} },
	{ discrete, "Column render mode",	{&r_columnmethod},		{2.0}, {0.0},	{0.0}, {ColumnMethods} },
	{ discrete, "Detail mode",			{&r_detail},		   	{4.0}, {0.0},	{0.0}, {DetailModes} },
	{ discrete, "Stretch short skies",	{&r_stretchsky},	   	{2.0}, {0.0},	{0.0}, {OnOff} },
	{ discrete, "Stretch status bar",	{&st_scale},			{2.0}, {0.0},	{0.0}, {OnOff} },
	{ discrete, "Screen wipe style",	{&wipetype},			{4.0}, {0.0},	{0.0}, {Wipes} },
#ifdef _WIN32
	{ discrete, "DirectDraw palette hack", {&vid_palettehack},	{2.0}, {0.0},	{0.0}, {OnOff} },
#endif
	{ redtext,	" ",					NULL,					0.0, 0.0,	0.0, NULL },
	{ more,		"More options",			NULL,					0.0, 0.0,	0.0, (value_t *)MoreVideoOptions },	// [BC]
};
// [BC]
static menuitem_t MoreVideoItems[] = {
	{ more,		"Messages",				NULL,					0.0, 0.0, 0.0, (value_t *)StartMessagesMenu },
	{ redtext,	" ",					NULL,					0.0, 0.0, 0.0, NULL },
	{ discrete, "Use fuzz effect",		&r_drawfuzz,			2.0, 0.0, 0.0, YesNo },
	{ discrete, "Rocket Trails",		&cl_rockettrails,		2.0, 0.0, 0.0, OnOff },
	{ discrete, "Grenade Trails",		&cl_grenadetrails,		2.0, 0.0, 0.0, OnOff },
	{ discrete, "Blood Type",			&cl_bloodtype,			3.0, 0.0, 0.0, BloodTypes },
	{ discrete, "Bullet Puff Type",		&cl_pufftype,			2.0, 0.0, 0.0, PuffTypes },
	{ redtext,	" ",					NULL,					0.0, 0.0, 0.0, NULL },
	{ discrete, "Rotate automap",		&am_rotate,				2.0, 0.0, 0.0, OnOff },
	{ discrete, "Overlay automap",		&am_overlay,			2.0, 0.0, 0.0, OnOff },
	{ discrete, "Standard map colors",	&am_usecustomcolors,	2.0, 0.0, 0.0, NoYes },
	{ redtext,	" ",					NULL,					0.0, 0.0, 0.0, NULL },
	{ discrete, "Show team scores",		&cl_showscores,			2.0, 0.0, 0.0, YesNo },
	{ discrete, "Show deathmatch info",	&am_dmscores,			2.0, 0.0, 0.0, YesNo },
	{ discrete, "Display player names",	&cl_namedraw,			2.0, 0.0, 0.0, YesNo },
	{ redtext,	" ",					NULL,					0.0, 0.0, 0.0, NULL },
	{ discrete, "One key display",		&cl_onekey,				2.0, 0.0, 0.0, YesNo },
};

menu_t VideoMenu = {
	"M_VIDEO",
	0,
#ifdef _WIN32
	13,	// [BC]
#else
	12,	// [BC]
#endif
	0,
	VideoItems,
	NULL,
	0,
};
// [BC]
menu_t MoreVideoMenu = {
	"M_VIDEO",
	0,
	17,
	0,
	MoreVideoItems,
	NULL,
	0,
};

// [BC]
static menuitem_t PSMenuItems[] = {
	{ string,	"Name",						&name,					2.0, 0.0, 0.0, NULL  },
	{ string,	"Team",						&team,					2.0, 0.0, 0.0, NULL  },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	{ slider,	"Red",						&color,					0.0, 255.0, 1.0, NULL  },
	{ slider,	"Green",					&color,					0.0, 255.0, 1.0, NULL  },
	{ slider,	"Blue",						&color,					0.0, 255.0, 1.0, NULL  },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	{ skintype,	"Skin",						&skin,					2.0, 0.0, 0.0, NULL	 },
	{ discrete, "Gender",					&gender,				3.0, 0.0, 0.0, HeSheIt },
	{ discrete,	"Autoaim",					&autoaim,				6.0, 0.0, 0.0, AutoaimVals },
	{ discrete, "Railgun color",			&railcolor,				11.0, 0.0, 0.0, TrailColors },
	{ discrete, "R. launcher first",		&rocketpreferred,		2.0, 0.0, 0.0, YesNo },
	{ discrete, "Plasma gun first",			&plasmapreferred,		2.0, 0.0, 0.0, YesNo },
	{ discrete, "Weapon pickup",			&neverswitchonpickup,	2.0, 0.0, 0.0, YesNo },
	{ discrete, "Expert mode",				&expert,				2.0, 0.0, 0.0, OnOff },
};

// [BC]
menu_t PSMenu = {
	"M_PSTTL",
	0,
	15,
	0,
	PSMenuItems,
	M_PlayerSetupDrawer,
	MNF_ALIGNLEFT,
};

// [BC]
static menuitem_t TSMenuItems[] = {
	{ string,	"Team",						&name,					2.0, 0.0, 0.0, NULL  },
	{ string,	"Name",						&team,					2.0, 0.0, 0.0, NULL  },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	{ slider,	"Red",						&color,					0.0, 255.0, 1.0, NULL  },
	{ slider,	"Green",					&color,					0.0, 255.0, 1.0, NULL  },
	{ slider,	"Blue",						&color,					0.0, 255.0, 1.0, NULL  },
	{ redtext,	" ",						NULL,					0.0, 0.0, 0.0, NULL  },
	{ skintype,	"Skin",						&skin,					2.0, 0.0, 0.0, NULL	 },
	{ discrete, "Leader",					&gender,				2.0, 0.0, 0.0, YesNo },
};

// [BC]
menu_t TSMenu = {
	"M_PSTTL",
	0,
	8,
	0,
	TSMenuItems,
	M_PlayerSetupDrawer,
	MNF_ALIGNLEFT,
};


/*=======================================
 *
 * Messages Menu
 *
 *=======================================*/
EXTERN_CVAR (con_scaletext)
EXTERN_CVAR (msg0color)
EXTERN_CVAR (msg1color)
EXTERN_CVAR (msg2color)
EXTERN_CVAR (msg3color)
EXTERN_CVAR (msg4color)
EXTERN_CVAR (msgmidcolor)
EXTERN_CVAR (msglevel)

static value_t TextColors[] =
{
	{ 0.0, "brick" },
	{ 1.0, "tan" },
	{ 2.0, "gray" },
	{ 3.0, "green" },
	{ 4.0, "brown" },
	{ 5.0, "gold" },
	{ 6.0, "red" },
	{ 7.0, "blue" }
};

static value_t MessageLevels[] = {
	{ 0.0, "Item Pickup" },
	{ 1.0, "Obituaries" },
	{ 2.0, "Critical Messages" }
};

static menuitem_t MessagesItems[] = {
	{ discrete,	"Scale text in high res", {&con_scaletext},		{2.0}, {0.0}, 	{0.0}, {OnOff} },
	{ discrete, "Minimum message level", {&msglevel},		   	{3.0}, {0.0},   {0.0}, {MessageLevels} },
	{ redtext,	" ",					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ whitetext, "Message Colors",		{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ redtext,	" ",					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ cdiscrete, "Item Pickup",			{&msg0color},		   	{8.0}, {0.0},	{0.0}, {TextColors} },
	{ cdiscrete, "Obituaries",			{&msg1color},		   	{8.0}, {0.0},	{0.0}, {TextColors} },
	{ cdiscrete, "Critical Messages",	{&msg2color},		   	{8.0}, {0.0},	{0.0}, {TextColors} },
	{ cdiscrete, "Chat Messages",		{&msg3color},		   	{8.0}, {0.0},	{0.0}, {TextColors} },
	{ cdiscrete, "Team Messages",		{&msg4color},		   	{8.0}, {0.0},	{0.0}, {TextColors} },
	{ cdiscrete, "Centered Messages",	{&msgmidcolor},			{8.0}, {0.0},	{0.0}, {TextColors} }
};

menu_t MessagesMenu = {
	"M_MESS",
	0,
	11,
	0,
	MessagesItems,
	NULL,
	0,
};


/*=======================================
 *
 * Video Modes Menu
 *
 *=======================================*/

extern BOOL setmodeneeded;
extern int NewWidth, NewHeight, NewBits;
extern int DisplayBits;

int testingmode;		// Holds time to revert to old mode
int OldWidth, OldHeight, OldBits;

static void BuildModesList (int hiwidth, int hiheight, int hi_id);
static BOOL GetSelectedSize (int line, int *width, int *height);
static void SetModesMenu (int w, int h, int bits);

EXTERN_CVAR (vid_defwidth)
EXTERN_CVAR (vid_defheight)
EXTERN_CVAR (vid_defbits)

static cvar_t DummyDepthCvar (NULL, NULL, 0);

EXTERN_CVAR (fullscreen)

static value_t Depths[22];

static char VMEnterText[] = "Press ENTER to set mode";
static char VMTestText[] = "T to test mode for 5 seconds";

static menuitem_t ModesItems[] = {
	{ discrete, "Screen mode",			{&DummyDepthCvar},		{0.0}, {0.0},	{0.0}, {Depths} },
	{ redtext,	" ",					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ discrete, "Fullscreen",			{&fullscreen},			{2.0}, {0.0},	{0.0}, {YesNo} },
	{ redtext,	" ",					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ screenres,{NULL},					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ screenres,{NULL},					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ screenres,{NULL},					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ screenres,{NULL},					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ screenres,{NULL},					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ screenres,{NULL},					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ screenres,{NULL},					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ screenres,{NULL},					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ screenres,{NULL},					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ whitetext,"Note: Only 8 bpp modes are supported",{NULL},	{0.0}, {0.0},	{0.0}, {NULL} },
	{ redtext,  VMEnterText,			{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ redtext,	" ",					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ redtext,  VMTestText,				{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ redtext,	" ",					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ redtext,  {NULL},					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} },
	{ redtext,  {NULL},					{NULL},					{0.0}, {0.0},	{0.0}, {NULL} }
};

#define VM_DEPTHITEM	0
#define VM_RESSTART		4
#define VM_ENTERLINE	14
#define VM_TESTLINE		16
#define VM_MAKEDEFLINE	18
#define VM_CURDEFLINE	19

menu_t ModesMenu = {
	{ 'M','_','V','I','D','M','O','D' },
#ifndef DJGPP
	2,
#else
	4,
#endif
	20,
	130,
	ModesItems,
	NULL,
	0,
};

/*=======================================
 *
 * Gameplay Options (dmflags) Menu
 *
 *=======================================*/

static cvar_t *flagsvar;
// [BC] Changed (DM)'s to (DM/ST), except for one
static menuitem_t DMFlagsItems[] = {
	{ bitflag,	"Falling damage",		(cvar_t *)DF_YES_FALLING,		0, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Weapons stay (DM/ST)",	(cvar_t *)DF_WEAPONS_STAY,		0, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Respawn all powerups",	(cvar_t *)DF_RESPAWN_SUPER,		0, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Allow powerups (DM/ST)",	(cvar_t *)DF_NO_ITEMS,		1, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Allow health (DM/ST)",	(cvar_t *)DF_NO_HEALTH,			1, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Allow armor (DM/ST)",	(cvar_t *)DF_NO_ARMOR,			1, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Spawn farthest (DM)",	(cvar_t *)DF_SPAWN_FARTHEST,	0, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Same map (DM/ST)",		(cvar_t *)DF_SAME_LEVEL,		0, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Force respawn (DM/ST)",(cvar_t *)DF_FORCE_RESPAWN,		0, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Allow exit (DM/ST)",	(cvar_t *)DF_NO_EXIT,			1, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Infinite ammo",		(cvar_t *)DF_INFINITE_AMMO,		0, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"No monsters",			(cvar_t *)DF_NO_MONSTERS,		0, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Monsters respawn",		(cvar_t *)DF_MONSTERS_RESPAWN,	0, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Items respawn",		(cvar_t *)DF_ITEMS_RESPAWN,		0, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Fast monsters",		(cvar_t *)DF_FAST_MONSTERS,		0, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Allow jump",			(cvar_t *)DF_NO_JUMP,			1, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Allow freelook",		(cvar_t *)DF_NO_FREELOOK,		1, 0, 0, (value_t *)&dmflags },
	{ bitflag,	"Friendly fire",		(cvar_t *)DF_NO_FRIENDLY_FIRE,	1, 0, 0, (value_t *)&dmflags },
	{ more,		"More options",			{NULL},							0, 0, 0, (value_t *)MoreGameplayOptions },
};

static cvar_t *flags2var;	// [BC]
static menuitem_t DMFlags2Items[] = {
	{ bitflag,	"Impaling damage",				(cvar_t *)DF2_YES_IMPALING,		0, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"Drop weapons (DM/ST)",			(cvar_t *)DF2_YES_WEAPONDROP,	0, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"Allow runes (DM/ST)",			(cvar_t *)DF2_NO_RUNES,			1, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"Instant skull return (ST)",	(cvar_t *)DF2_YES_IRETURN,		0, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"Return sitting skulls (ST)",	(cvar_t *)DF2_YES_RETURN,		1, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"Use team colors (ST)",			(cvar_t *)DF2_YES_TEAMCOLORS,	0, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"Allow team switch (ST)",		(cvar_t *)DF2_NO_SWITCH,		1, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"Random teams (ST)",			(cvar_t *)DF2_FORCE_RANDOM,		0, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"Drop runes (DM/ST)",			(cvar_t *)DF2_YES_RUNEDROP,		0, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"200% health/armor max.",		(cvar_t *)DF2_YES_200MAX,		0, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"Double ammo",					(cvar_t *)DF2_YES_DOUBLEAMMO,	0, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"Clear frags (DM/ST)",			(cvar_t *)DF2_NO_CLEARFRAGS,	1, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"No respawn (DM/ST)",			(cvar_t *)DF2_FORCE_NORESPAWN,	0, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"Degeneration (DM/ST)",			(cvar_t *)DF2_YES_DEGENERATION,	0, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"Lose frag when killed (DM)",	(cvar_t *)DF2_YES_LOSEFRAG,		0, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"No BFG free aiming",			(cvar_t *)DF2_NO_FREEAIMBFG,	0, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"Barrels respawn (DM/ST)",		(cvar_t *)DF2_BARRELS_RESPAWN,	0, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"Invulerable on respawn",		(cvar_t *)DF2_YES_INVUL,		0, 0, 0, (value_t *)&dmflags2 },
	{ bitflag,	"(Empty slot)",					(cvar_t *)DF2_EMPTY,			0, 0, 0, (value_t *)&dmflags2 }
};


static menu_t DMFlagsMenu = {
	{ 'M','_','G','M','P','L','A','Y' },
	0,
	19,
	0,
	DMFlagsItems,
	M_DrawDMFlags,
	0,
};

static menu_t DMFlags2Menu = {
	{ 'M','_','G','M','P','L','A','Y' },
	0,
	19,
	0,
	DMFlags2Items,
	M_DrawDMFlags2,
	0,
};

// [BC] Changed (DM)'s to (DM/ST), except for one
static cvar_t *clflagsvar;	// [BC]
static menuitem_t CLDMFlagsItems[] = {
	{ bitflag,	"Falling damage",		(cvar_t *)DF_YES_FALLING,		0, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Weapons stay (DM/ST)",	(cvar_t *)DF_WEAPONS_STAY,		0, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Respawn all powerups",	(cvar_t *)DF_RESPAWN_SUPER,		0, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Allow powerups (DM/ST)",	(cvar_t *)DF_NO_ITEMS,		1, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Allow health (DM/ST)",	(cvar_t *)DF_NO_HEALTH,			1, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Allow armor (DM/ST)",	(cvar_t *)DF_NO_ARMOR,			1, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Spawn farthest (DM)",	(cvar_t *)DF_SPAWN_FARTHEST,	0, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Same map (DM/ST)",		(cvar_t *)DF_SAME_LEVEL,		0, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Force respawn (DM/ST)",(cvar_t *)DF_FORCE_RESPAWN,		0, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Allow exit (DM/ST)",	(cvar_t *)DF_NO_EXIT,			1, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Infinite ammo",		(cvar_t *)DF_INFINITE_AMMO,		0, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"No monsters",			(cvar_t *)DF_NO_MONSTERS,		0, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Monsters respawn",		(cvar_t *)DF_MONSTERS_RESPAWN,	0, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Items respawn",		(cvar_t *)DF_ITEMS_RESPAWN,		0, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Fast monsters",		(cvar_t *)DF_FAST_MONSTERS,		0, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Allow jump",			(cvar_t *)DF_NO_JUMP,			1, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Allow freelook",		(cvar_t *)DF_NO_FREELOOK,		1, 0, 0, (value_t *)&cl_dmflags },
	{ bitflag,	"Friendly fire",		(cvar_t *)DF_NO_FRIENDLY_FIRE,	1, 0, 0, (value_t *)&cl_dmflags },
	{ more,		"More options",			{NULL},							0, 0, 0, (value_t *)MoreCLGameplayOptions },
};

static cvar_t *clflags2var;	// [BC]
static menuitem_t CLDMFlags2Items[] = {
	{ bitflag,	"Impaling damage",				(cvar_t *)DF2_YES_IMPALING,		0, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"Drop weapons (DM/ST)",			(cvar_t *)DF2_YES_WEAPONDROP,	0, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"Allow runes (DM/ST)",			(cvar_t *)DF2_NO_RUNES,			1, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"Instant skull return (ST)",	(cvar_t *)DF2_YES_IRETURN,		0, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"Return sitting skulls (ST)",	(cvar_t *)DF2_YES_RETURN,		1, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"Use team colors (ST)",			(cvar_t *)DF2_YES_TEAMCOLORS,	0, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"Allow team switch (ST)",		(cvar_t *)DF2_NO_SWITCH,		1, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"Random teams (ST)",			(cvar_t *)DF2_FORCE_RANDOM,		0, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"Drop runes (DM/ST)",			(cvar_t *)DF2_YES_RUNEDROP,		0, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"200% health/armor max.",		(cvar_t *)DF2_YES_200MAX,		0, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"Double ammo",					(cvar_t *)DF2_YES_DOUBLEAMMO,	0, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"Clear frags (DM/ST)",			(cvar_t *)DF2_NO_CLEARFRAGS,	1, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"No respawn (DM/ST)",			(cvar_t *)DF2_FORCE_NORESPAWN,	0, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"Degeneration (DM/ST)",			(cvar_t *)DF2_YES_DEGENERATION,	0, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"Lose frag when killed (DM)",	(cvar_t *)DF2_YES_LOSEFRAG,		0, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"No BFG free aiming",			(cvar_t *)DF2_NO_FREEAIMBFG,	0, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"Barrels respawn (DM/ST)",		(cvar_t *)DF2_BARRELS_RESPAWN,	0, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"Invulerable on respawn",		(cvar_t *)DF2_YES_INVUL,		0, 0, 0, (value_t *)&cl_dmflags2 },
	{ bitflag,	"(Empty slot)",					(cvar_t *)DF2_EMPTY,			0, 0, 0, (value_t *)&cl_dmflags2 }
};


static menu_t CLDMFlagsMenu = {
	{ 'M','_','G','M','P','L','A','Y' },
	0,
	19,
	0,
	CLDMFlagsItems,
	M_DrawCLDMFlags,
	0,
};

static menu_t CLDMFlags2Menu = {
	{ 'M','_','G','M','P','L','A','Y' },
	0,
	19,
	0,
	CLDMFlags2Items,
	M_DrawCLDMFlags2,
	0,
};

void M_FreeValues (value_t **values, int num)
{
	int i;

	if (*values)
	{
		for (i = 0; i < num; i++)
		{
			if ((*values)[i].name)
				free ((*values)[i].name);
		}

		free (*values);
		*values = NULL;
	}
}

//
//		Set some stuff up for the video modes menu
//
static byte BitTranslate[16];

void M_OptInit (void)
{
	int currval = 0, dummy1, dummy2, i;
	char name[24];

	for (i = 1; i < 32; i++)
	{
		I_StartModeIterator (i);
		if (I_NextMode (&dummy1, &dummy2))
		{
			Depths[currval].value = currval;
			sprintf (name, "%d bit", i);
			Depths[currval].name = copystring (name);
			BitTranslate[currval] = i;
			currval++;
		}
	}

	ModesItems[VM_DEPTHITEM].b.min = (float)currval;

	switch (I_DisplayType ())
	{
	case DISPLAY_FullscreenOnly:
		ModesItems[2].type = nochoice;
		ModesItems[2].b.min = 1.f;
		break;
	case DISPLAY_WindowOnly:
		ModesItems[2].type = nochoice;
		ModesItems[2].b.min = 0.f;
		break;
	default:
		break;
	}
}


//
//		Toggle messages on/off
//
void M_ChangeMessages (void)
{
	if (show_messages.value)
	{
		Printf (128, "%s\n", MSGOFF);
		show_messages.Set (0.0f);
	}
	else
	{
		Printf (128, "%s\n", MSGON);
		show_messages.Set (1.0f);
	}
}

BEGIN_COMMAND (togglemessages)
{
	M_ChangeMessages ();
}
END_COMMAND (togglemessages)

	void M_SizeDisplay (float diff)
{
	// changing screenblocks automatically resizes the display
	screenblocks.Set (screenblocks.value + diff);
}

BEGIN_COMMAND (sizedown)
{
	M_SizeDisplay (-1.0);
	S_Sound (CHAN_VOICE, "plats/pt1_mid", 1, ATTN_NONE);
}
END_COMMAND (sizedown)

	BEGIN_COMMAND (sizeup)
{
	M_SizeDisplay(1.0);
	S_Sound (CHAN_VOICE, "plats/pt1_mid", 1, ATTN_NONE);
}
END_COMMAND (sizeup)

	void M_BuildKeyList (menuitem_t *item, int numitems)
{
	int i;

	for (i = 0; i < numitems; i++, item++)
	{
		if (item->type == control)
			C_GetKeysForCommand (item->e.command, &item->b.key1, &item->c.key2);
	}
}

void M_SwitchMenu (menu_t *menu)
{
	int i, widest = 0, thiswidth;
	menuitem_t *item;

	MenuStack[MenuStackDepth].menu.newmenu = menu;
	MenuStack[MenuStackDepth].isNewStyle = true;
	MenuStack[MenuStackDepth].drawSkull = false;
	MenuStackDepth++;

	CurrentMenu = menu;
	CurrentItem = menu->lastOn;

	if (menu == &ControlsMenu)
		I_ResumeMouse ();

	if (!menu->indent)
	{
		for (i = 0; i < menu->numitems; i++)
		{
			item = menu->items + i;
			if (item->type != whitetext && item->type != redtext)
			{
				thiswidth = V_StringWidth (item->label);
				if (thiswidth > widest)
					widest = thiswidth;
			}
		}
		menu->indent = widest + 6;
	}

	flagsvar = NULL;
	flags2var = NULL;	// [BC]
	clflagsvar = NULL;
	clflags2var = NULL;	// [BC]
}

BOOL M_StartOptionsMenu (void)
{
	M_SwitchMenu (&OptionMenu);
	return true;
}

// [BC]
BOOL M_StartMultiplayerMenu (void)
{
	M_SwitchMenu (&MultiMenu);
	return true;
}

void M_DrawSlider (int x, int y, float min, float max, float cur)
{
	float range;
	int i;

	range = max - min;

	if (cur > max)
		cur = max;
	else if (cur < min)
		cur = min;

	cur -= min;

	screen->DrawPatchClean ((patch_t *)W_CacheLumpName ("LSLIDE", PU_CACHE), x, y);
	for (i = 1; i < 11; i++)
		screen->DrawPatchClean ((patch_t *)W_CacheLumpName ("MSLIDE", PU_CACHE), x + i*8, y);
	screen->DrawPatchClean ((patch_t *)W_CacheLumpName ("RSLIDE", PU_CACHE), x + 88, y);

	screen->DrawPatchClean ((patch_t *)W_CacheLumpName ("CSLIDE", PU_CACHE), x + 5 + (int)((cur * 78.0) / range), y);
}

int M_FindCurVal (float cur, value_t *values, int numvals)
{
	int v;

	for (v = 0; v < numvals; v++)
		if (values[v].value == cur)
			break;

	return v;
}

void M_OptDrawer (void)
{
	int color;
	int y, width, i, x;
	menuitem_t *item;
	patch_t *title;

	title = (patch_t *)W_CacheLumpName (CurrentMenu->title, PU_CACHE);

	// [BC] Do drawing routine if there is one
	if (CurrentMenu->routine)
		CurrentMenu->routine ();
	
	screen->DrawPatchClean (title, 160-SHORT(title->width)/2, 10);

//	for (i = 0, y = 20 + title->height; i < CurrentMenu->numitems; i++, y += 8)	{
	for (i = 0, y = 15 + title->height; i < CurrentMenu->numitems; i++, y += 8)	// TIJ
	{
		item = CurrentMenu->items + i;

		if (item->type != screenres)
		{
			width = V_StringWidth (item->label);
			switch (item->type)
			{
			case more:
				x = CurrentMenu->indent - width;
				color = CR_GREY;
				break;

			case redtext:
				x = 160 - width / 2;
				color = CR_RED;
				break;

			case whitetext:
				x = 160 - width / 2;
				color = CR_GREY;
				break;

			case listelement:
				x = CurrentMenu->indent + 14;
				color = CR_RED;
				break;

			default:
				x = CurrentMenu->indent - width;
				color = CR_RED;
				break;
			}
			if (CurrentMenu->flags & MNF_ALIGNLEFT)	// [BC]
				x = 32;

			screen->DrawTextCleanMove (color, x, y, item->label);

			if (CurrentMenu->flags & MNF_ALIGNLEFT)	// [BC]
				x = 32 + width + 6;
			else
				x = CurrentMenu->indent + 14;

			switch (item->type)
			{
			case discrete:
			case cdiscrete:
			{
				int v, vals;

				vals = (int)item->b.min;
				v = M_FindCurVal (item->a.cvar->value, item->e.values, vals);

				// [BC] autoaim hack
				if (item->a.cvar == &autoaim)
				{
					static const float ranges[] = { 0, 0.25, 0.5, 1, 2, 3, 5000 };

					for (int j=0; j<7; j++)
						if (item->a.cvar->value == ranges[j])
							break;

					if (j<7)
						screen->DrawTextCleanMove (CR_GREY, x, y, item->e.values[j].name);
					else
						screen->DrawTextCleanMove (CR_GREY, x, y, "Unknown");

				}
				else if (v == vals)
				{
					screen->DrawTextCleanMove (CR_GREY, x, y, "Unknown");
				}
				else
				{
					if (item->type == cdiscrete)
						screen->DrawTextCleanMove (v, x, y, item->e.values[v].name);
					else
						screen->DrawTextCleanMove (CR_GREY, x, y, item->e.values[v].name);
				}

			}
			break;

			case number:
			{
				screen->DrawTextCleanMove (CR_GRAY, x, y, item->a.cvar->string);
			}
			break;

			case maplist:
			{
				int cur, vals;
				
				vals = (int)item->b.min;
				cur = M_FindCurVal (current, item->e.values, vals);

				switch ((int)cl_episode.value)
				{
				case 0:
				default:
					if (cur < 0)
						cur = 31;
					if (cur > 31)
						cur = 0;

					break;
				case 1:
					if (cur <= 32)
						cur = 32;
					if (cur >= 36)
						cur = 36;
					break;
				case 2:
					if (cur <= 37)
						cur = 37;
					if (cur >= vals)
						cur = vals;

				}

				current = item->e.values[cur].value;

				screen->DrawTextCleanMove (CR_GREY, x, y, item->e.values[cur].name);
			}
			break;

			case string:
			{
				if (StrInput && i == CurrentItem)
				{
					screen->DrawTextCleanMove (CR_GRAY, x, y, temp);
					screen->DrawTextCleanMove (CR_GRAY, x+V_StringWidth(temp), y, "_");
				}
				else
				{
					// [BC] Hack hack
					if (!stricmp (item->label, "Server IP"))
						screen->DrawTextCleanMove (CR_GRAY, x, y, conip);
					else
						screen->DrawTextCleanMove (CR_GRAY, x, y, item->a.cvar->string);
				}
			}
			break;

			case nochoice:
				screen->DrawTextCleanMove (CR_GOLD, x, y,
										   (item->e.values[(int)item->b.min]).name);
				break;

			case slider:
				if (!strcmp (item->label, "Red"))
				{
					int x2 = V_StringWidth ("Green") + 8 + 32;
					int cr = players[consoleplayer].userinfo.color;

					M_DrawSlider (x2, y, 0.0f, 255.0f, RPART(cr));
				}
				else if (!strcmp (item->label, "Green"))
				{
					int x2 = V_StringWidth ("Green") + 8 + 32;
					int cr = players[consoleplayer].userinfo.color;

					M_DrawSlider (x2, y, 0.0f, 255.0f, GPART(cr));
				}
				else if (!strcmp (item->label, "Blue"))
				{
					int x2 = V_StringWidth ("Green") + 8 + 32;
					int cr = players[consoleplayer].userinfo.color;

					M_DrawSlider (x2, y, 0.0f, 255.0f, BPART(cr));
				}
				else
					M_DrawSlider (x, y, item->b.min, item->c.max, item->a.cvar->value);
				break;

			case control:
			{
				char description[64];

				C_NameKeys (description, item->b.key1, item->c.key2);
				screen->DrawTextCleanMove (CR_GREY, CurrentMenu->indent + 14, y, description);
			}
			break;

			case bitflag:
			{
				value_t *value;
				char *str;

				if (item->b.min)
					value = NoYes;
				else
					value = YesNo;

				if (*item->e.flagint & item->a.flagmask)
					str = value[1].name;
				else
					str = value[0].name;

				screen->DrawTextCleanMove (CR_GREY, x, y, str);
			}
			break;

			// [BC}
			case skintype:
				screen->DrawTextCleanMove (CR_GREY, x, y, skins[players[consoleplayer].userinfo.skin].name);
				break;

			case botslot:
				if (!botspawn[i].name)
					screen->DrawTextCleanMove (CR_GREY, x, y, "-");
				else
					screen->DrawTextCleanMove (CR_GREY, x, y, botspawn[i].name);
				break;

			default:
				break;
			}

			if (i == CurrentItem && (skullAnimCounter < 6 || WaitingForKey))
			{
				if (CurrentMenu->flags & MNF_ALIGNLEFT)	// [BC]
				{
					patch_t *p = (patch_t *)W_CacheLumpName ("LITLCURS", PU_CACHE);
					screen->DrawPatchClean (p, 32 - p->width - 3, y);
				}
				else
					screen->DrawPatchClean ((patch_t *)W_CacheLumpName ("LITLCURS", PU_CACHE), CurrentMenu->indent + 3, y);
			}
		}
		else
		{
			char *str = NULL;

			for (x = 0; x < 3; x++)
			{
				switch (x)
				{
				case 0:
					str = item->b.res1;
					break;
				case 1:
					str = item->c.res2;
					break;
				case 2:
					str = item->d.res3;
					break;
				}
				if (str)
				{
					if (x == item->e.highlight)
						color = CR_GREY;
					else
						color = CR_RED;

					screen->DrawTextCleanMove (color, 104 * x + 20, y, str);
				}
			}

			if (i == CurrentItem && ((item->a.selmode != -1 && (skullAnimCounter < 6 || WaitingForKey)) || testingmode))
			{
				screen->DrawPatchClean ((patch_t *)W_CacheLumpName ("LITLCURS", PU_CACHE), item->a.selmode * 104 + 8, y);
			}

		}
	}
}

void M_OptResponder (event_t *ev)
{
	menuitem_t *item;
	int ch = ev->data1;
	
	item = CurrentMenu->items + CurrentItem;

	if (WaitingForKey)
	{
		if (ch != KEY_ESCAPE)
		{
			C_ChangeBinding (item->e.command, ch);
			M_BuildKeyList (CurrentMenu->items, CurrentMenu->numitems);
		}
		WaitingForKey = false;
		CurrentMenu->items[0].label = OldMessage;
		CurrentMenu->items[0].type = OldType;
		return;
	}

	// [BC] String input
	if (StrInput)
	{
		char *str;
		
		if (!stricmp (item->label, "Server IP"))
			str = conip;
		else
			str = (char *)item->a.cvar->string;

		for (int i=0;; i++)
//			if (!item->a.cvar->string[i])
			if (!str[i])
				break;

		switch(ch)
		{
		  case KEY_BACKSPACE:
			if (i)
			{
				str[--i] = '\0';
				//item->a.cvar->Set (str);
			}
			break;

		  case KEY_ESCAPE:
			StrInput = false;
			break;
								
		  case KEY_ENTER:
			  if (!stricmp (item->label, "Server IP"))
				  conip = temp;
			  else
				item->a.cvar->Set (temp);
			StrInput = false;
			break;
								
		  default:
//			if (!stricmp (item->a.cvar->name, "Name") || !stricmp (item->a.cvar->name, "Team"))
//				if (i >= MAXPLAYERNAME+1)
//					break;
			ch = toupper(ev->data3);	// [RH] Use user keymap
			if (ch != 32)
				if (ch-HU_FONTSTART < 0 || ch-HU_FONTSTART >= HU_FONTSIZE)
					break;
			if (ch >= 32 && ch <= 127)
			{
				str[i] = ch;
				//item->a.cvar->Set (str);
			}

			break;
		}
		return;
	}

	if (item->type == bitflag && flagsvar &&
		(ch == KEY_LEFTARROW || ch == KEY_RIGHTARROW || ch == KEY_ENTER)
		&& !demoplayback)
	{
			int newflags = *item->e.flagint ^ item->a.flagmask;
			char val[16];

			sprintf (val, "%d", newflags);
			flagsvar->Set (val);
			return;
	}

// [BC] Fix me
	if (item->type == bitflag && flags2var &&
		(ch == KEY_LEFTARROW || ch == KEY_RIGHTARROW || ch == KEY_ENTER)
		&& !demoplayback)
	{
			int newflags = *item->e.flagint ^ item->a.flagmask;
			char val[16];

			sprintf (val, "%d", newflags);
			flags2var->Set (val);
			return;
	}

	// [BC] Grrrr......
	if (item->type == bitflag && clflagsvar &&
		(ch == KEY_LEFTARROW || ch == KEY_RIGHTARROW || ch == KEY_ENTER))
	{
			int newflags = *item->e.flagint ^ item->a.flagmask;
			char val[16];

			sprintf (val, "%d", newflags);
			clflagsvar->Set (val);
			return;
	}

	if (item->type == bitflag && clflags2var &&
		(ch == KEY_LEFTARROW || ch == KEY_RIGHTARROW || ch == KEY_ENTER))
	{
			int newflags = *item->e.flagint ^ item->a.flagmask;
			char val[16];

			sprintf (val, "%d", newflags);
			clflags2var->Set (val);
			return;
	}

	switch (ch)
	{
		case KEY_DOWNARROW:
			{
				int modecol;

				if (item->type == screenres)
				{
					modecol = item->a.selmode;
					item->a.selmode = -1;
				}
				else
				{
					modecol = 0;
				}

				do
				{
					if (++CurrentItem == CurrentMenu->numitems)
						CurrentItem = 0;
				} while (CurrentMenu->items[CurrentItem].type == redtext ||
						 CurrentMenu->items[CurrentItem].type == whitetext ||
						 (CurrentMenu->items[CurrentItem].type == screenres &&
						  !CurrentMenu->items[CurrentItem].b.res1));

				if (CurrentMenu->items[CurrentItem].type == screenres)
					CurrentMenu->items[CurrentItem].a.selmode = modecol;

				S_Sound (CHAN_VOICE, "plats/pt1_stop", 1, ATTN_NONE);
			}
			break;

		case KEY_UPARROW:
			{
				int modecol;

				if (item->type == screenres)
				{
					modecol = item->a.selmode;
					item->a.selmode = -1;
				}
				else
				{
					modecol = 0;
				}

				do
				{
					if (--CurrentItem < 0)
						CurrentItem = CurrentMenu->numitems - 1;
				} while (CurrentMenu->items[CurrentItem].type == redtext ||
						 CurrentMenu->items[CurrentItem].type == whitetext ||
						 (CurrentMenu->items[CurrentItem].type == screenres &&
						  !CurrentMenu->items[CurrentItem].b.res1));

				if (CurrentMenu->items[CurrentItem].type == screenres)
					CurrentMenu->items[CurrentItem].a.selmode = modecol;

				S_Sound (CHAN_VOICE, "plats/pt1_stop", 1, ATTN_NONE);
			}
			break;

		case KEY_LEFTARROW:
			switch (item->type)
			{
				case slider:
					if (!stricmp (item->label, "Red"))
					{
						int cr = players[consoleplayer].userinfo.color;
						int red = RPART(cr);

						red -= 16;
						if (red < 0)
							red = 0;
						SendNewColor (red, GPART(cr), BPART(cr));
					}
					else if (!stricmp (item->label, "Green"))
					{
						int cr = players[consoleplayer].userinfo.color;
						int green = GPART(cr);

						green -= 16;
						if (green < 0)
							green = 0;
						SendNewColor (RPART(cr), green, BPART(cr));
					}
					else if (!stricmp (item->label, "Blue"))
					{
						int cr = players[consoleplayer].userinfo.color;
						int blue = BPART(cr);

						blue -= 16;
						if (blue < 0)
							blue = 0;
						SendNewColor (RPART(cr), GPART(cr), blue);
					}
					else
					{
						float newval = item->a.cvar->value - item->d.step;

						if (newval < item->b.min)
							newval = item->b.min;

						if (item->e.cfunc)
							item->e.cfunc (item->a.cvar, newval);
						else
							item->a.cvar->Set (newval);
					}
					S_Sound (CHAN_VOICE, "plats/pt1_mid", 1, ATTN_NONE);
					break;

				case discrete:
				case cdiscrete:
					{
						int cur;
						int numvals;

						numvals = (int)item->b.min;
						cur = M_FindCurVal (item->a.cvar->value, item->e.values, numvals);
						if (--cur < 0)
							cur = numvals - 1;

						// [BC] autoaim hack
						if (item->a.cvar == &autoaim)
						{
							static const float ranges[] = { 0, 0.25, 0.5, 1, 2, 3, 5000 };

							for (int j=0; j<7; j++)
							{
								if (item->a.cvar->value == ranges[j])
									break;
							}
							if (j)
								item->a.cvar->Set (ranges[j-1]);
						}
						else							
							item->a.cvar->Set (item->e.values[cur].value);

						// Hack hack. Rebuild list of resolutions
						if (item->e.values == Depths)
							BuildModesList (screen->width, screen->height, DisplayBits);
					}
					S_Sound (CHAN_VOICE, "plats/pt1_mid", 1, ATTN_NONE);
					break;

					// [BC] Generic number
				case number:
					{
						int cur;

						cur = item->a.cvar->value;
						cur -= item->d.step;

						if (cur < (int)item->b.min)
							cur = item->c.max;

						item->a.cvar->Set (cur);
					}
					S_Sound (CHAN_VOICE, "plats/pt1_mid", 1, ATTN_NONE);
					break;

				case maplist:
					{
						int cur;
						int numvals;

						numvals = (int)item->b.min;
						cur = M_FindCurVal (current, item->e.values, numvals);

						switch ((int)cl_episode.value)
						{
						case 0:
						default:
							if (--cur < 0)
								cur = 31;
							break;
						case 1:
							if (--cur <= 31)
								cur = 36;
							break;
						case 2:
							if (--cur <= 36)
								cur = numvals - 1;
							break;
						}

						current = item->e.values[cur].value;
					}
					S_Sound (CHAN_VOICE, "plats/pt1_mid", 1, ATTN_NONE);
					break;

				case screenres:
					{
						int col;

						col = item->a.selmode - 1;
						if (col < 0)
						{
							if (CurrentItem > 0)
							{
								if (CurrentMenu->items[CurrentItem - 1].type == screenres)
								{
									item->a.selmode = -1;
									CurrentMenu->items[--CurrentItem].a.selmode = 2;
								}
							}
						}
						else
						{
							item->a.selmode = col;
						}
					}
					S_Sound (CHAN_VOICE, "plats/pt1_stop", 1, ATTN_NONE);
					break;

				// [BC]
				case skintype:
					{
						int skin = players[consoleplayer].userinfo.skin;

						// Don't allow hidden skins to be selectable
						do
						{
							skin--;

							if (skin < 0)
								skin = (int)numskins - 1;
						} while (skins[skin].hidden);

						cvar_set ("skin", skins[skin].name);
							
						if (skins[skin].color[0] != ' ')
							cvar_set ("color", skins[skin].color);

						if (skins[skin].gender != -1)
							gender.Set (skins[skin].gender);
					}
					S_Sound (CHAN_VOICE, "plats/pt1_mid", 1, ATTN_NONE);
					break;

				// [BC]
				case botslot:
					{
						int cur;

						if (!botspawn[CurrentItem].name)
							cur = -1;
						else for (int x=0; x<NUMTOTALBOTS; x++)
							if ((x != NUMBOTTYPES) && !stricmp (botspawn[CurrentItem].name, botinfo[x].name))
								cur = x;

						if (cur == NUMTOTALBOTS)
							cur = -1;

						do
						{
							cur--;

							if (cur == NUMBOTTYPES)
								cur--;

							if (cur < -1)
								cur = NUMTOTALBOTS - 1;

							if (cur == -1)
								botspawn[CurrentItem].name = NULL;
							else
								botspawn[CurrentItem].name = botinfo[cur].name;

						} while (!botinfo[cur].revealed);
					}
					S_Sound (CHAN_VOICE, "plats/pt1_mid", 1, ATTN_NONE);
					break;


				default:
					break;
			}
			break;

		case KEY_RIGHTARROW:
			switch (item->type)
			{
				case slider:
					if (!stricmp (item->label, "Red"))
					{
						int cr = players[consoleplayer].userinfo.color;
						int red = RPART(cr);

						red += 16;
						if (red > 255)
							red = 255;
						SendNewColor (red, GPART(cr), BPART(cr));
					}
					else if (!stricmp (item->label, "Green"))
					{
						int cr = players[consoleplayer].userinfo.color;
						int green = GPART(cr);

						green += 16;
						if (green > 255)
							green = 255;
						SendNewColor (RPART(cr), green, BPART(cr));
					}
					else if (!stricmp (item->label, "Blue"))
					{
						int cr = players[consoleplayer].userinfo.color;
						int blue = BPART(cr);

						blue += 16;
						if (blue > 255)
							blue = 255;
						SendNewColor (RPART(cr), GPART(cr), blue);
					}
					else
					{
						float newval = item->a.cvar->value + item->d.step;

						if (newval > item->c.max)
							newval = item->c.max;

						if (item->e.cfunc)
							item->e.cfunc (item->a.cvar, newval);
						else
							item->a.cvar->Set (newval);
					}
					S_Sound (CHAN_VOICE, "plats/pt1_mid", 1, ATTN_NONE);
					break;

				case discrete:
				case cdiscrete:
					{
						int cur;
						int numvals;
						
						numvals = (int)item->b.min;
						cur = M_FindCurVal (item->a.cvar->value, item->e.values, numvals);
						if (++cur >= numvals)
							cur = 0;

						// [BC] autoaim hack
						if (item->a.cvar == &autoaim)
						{
							static const float ranges[] = { 0, 0.25, 0.5, 1, 2, 3, 5000 };

							for (int j=0; j<7; j++)
							{
								if (item->a.cvar->value == ranges[j])
									break;
							}
							if (j<6)
								item->a.cvar->Set (ranges[j+1]);
						}
						else							
							item->a.cvar->Set (item->e.values[cur].value);

						// Hack hack. Rebuild list of resolutions
						if (item->e.values == Depths)
							BuildModesList (screen->width, screen->height, DisplayBits);
					}
					S_Sound (CHAN_VOICE, "plats/pt1_mid", 1, ATTN_NONE);
					break;

					// [BC] Generic number
				case number:
					{
						int cur;

						cur = item->a.cvar->value;

						cur += item->d.step;

						if (cur > (int)item->c.max)
							cur = item->b.min;

						item->a.cvar->Set (cur);
					}
					S_Sound (CHAN_VOICE, "plats/pt1_mid", 1, ATTN_NONE);
					break;

				case maplist:
					{
						int cur;
						int numvals;

						numvals = (int)item->b.min;
						cur = M_FindCurVal (current, item->e.values, numvals);

						switch ((int)cl_episode.value)
						{
						case 0:
						default:
							if (++cur > 31)
								cur = 0;
							break;
						case 1:
							if (++cur > 36)
								cur = 32;
							break;
						case 2:
							if (++cur >= numvals)
								cur = 37;
							break;
						}
							

						current = item->e.values[cur].value;
					}
					S_Sound (CHAN_VOICE, "plats/pt1_mid", 1, ATTN_NONE);
					break;

				case screenres:
					{
						int col;

						col = item->a.selmode + 1;
						if ((col > 2) || (col == 2 && !item->d.res3) || (col == 1 && !item->c.res2))
						{
							if (CurrentMenu->numitems - 1 > CurrentItem)
							{
								if (CurrentMenu->items[CurrentItem + 1].type == screenres)
								{
									if (CurrentMenu->items[CurrentItem + 1].b.res1)
									{
										item->a.selmode = -1;
										CurrentMenu->items[++CurrentItem].a.selmode = 0;
									}
								}
							}
						}
						else
						{
							item->a.selmode = col;
						}
					}
					S_Sound (CHAN_VOICE, "plats/pt1_stop", 1, ATTN_NONE);
					break;

				// [BC]
				case skintype:
					{
						int skin = players[consoleplayer].userinfo.skin;

						// Don't allow hidden skins to be selectable
						do
						{
							skin++;

							if (skin > (int)numskins)
								skin = 0;
						} while (skins[skin].hidden);

						cvar_set ("skin", skins[skin].name);
							
						if (skins[skin].color[0] != ' ')
							cvar_set ("color", skins[skin].color);

						if (skins[skin].gender != -1)
							gender.Set(skins[skin].gender);
					}
					S_Sound (CHAN_VOICE, "plats/pt1_mid", 1, ATTN_NONE);
					break;

				// [BC]
				case botslot:
					{
						int cur;

						if (!botspawn[CurrentItem].name)
							cur = -1;
						else for (int x=0; x<NUMTOTALBOTS; x++)
							if ((x != NUMBOTTYPES) && !stricmp (botspawn[CurrentItem].name, botinfo[x].name))
								cur = x;

						if (cur == NUMTOTALBOTS)
							cur = -1;

						do
						{
							cur++;

							if (cur >= NUMTOTALBOTS)
								cur = -1;

							if (cur == -1)
								botspawn[CurrentItem].name = NULL;
							else
								botspawn[CurrentItem].name = botinfo[cur].name;
						} while (!botinfo[cur].revealed);
					}
					S_Sound (CHAN_VOICE, "plats/pt1_mid", 1, ATTN_NONE);
					break;

				default:
					break;
			}
			break;

		case KEY_BACKSPACE:
			if (item->type == control)
			{
				C_UnbindACommand (item->e.command);
				item->b.key1 = item->c.key2 = 0;
			}
			break;

		case KEY_ENTER:
			if (CurrentMenu == &ModesMenu)
			{
				if (!(item->type == screenres && GetSelectedSize (CurrentItem, &NewWidth, &NewHeight)))
				{
					NewWidth = screen->width;
					NewHeight = screen->height;
				}
				NewBits = BitTranslate[(int)DummyDepthCvar.value];
				setmodeneeded = true;
				S_Sound (CHAN_VOICE, "weapons/pistol", 1, ATTN_NONE);
				SetModesMenu (NewWidth, NewHeight, NewBits);
			}
			else if (item->type == more && item->e.mfunc)
			{
				CurrentMenu->lastOn = CurrentItem;
				S_Sound (CHAN_VOICE, "weapons/pistol", 1, ATTN_NONE);
				item->e.mfunc();
			}
			else if (item->type == discrete || item->type == cdiscrete)
			{
				int cur;
				int numvals;

				numvals = (int)item->b.min;
				cur = M_FindCurVal (item->a.cvar->value, item->e.values, numvals);
				if (++cur >= numvals)
					cur = 0;

				item->a.cvar->Set (item->e.values[cur].value);

				// Hack hack. Rebuild list of resolutions
				if (item->e.values == Depths)
					BuildModesList (screen->width, screen->height, DisplayBits);

				S_Sound (CHAN_VOICE, "plats/pt1_mid", 1, ATTN_NONE);
			}
			else if (item->type == control)
			{
				WaitingForKey = true;
				OldMessage = CurrentMenu->items[0].label;
				OldType = CurrentMenu->items[0].type;
				CurrentMenu->items[0].label = "Press new key for control or ESC to cancel";
				CurrentMenu->items[0].type = redtext;
			}
			else if (item->type == listelement)
			{
				CurrentMenu->lastOn = CurrentItem;
				S_Sound (CHAN_VOICE, "weapons/pistol", 1, ATTN_NONE);
				item->e.lfunc (CurrentItem);
			}
			else if (item->type == screenres)
			{
			}
			else if (item->type == string)	// [BC]
			{
				if (!stricmp (item->label, "Server IP"))
					temp = conip;
				else
					temp = (char *)item->a.cvar->string;

				StrInput = true;
			}
			break;

		case KEY_ESCAPE:
			CurrentMenu->lastOn = CurrentItem;
			M_PopMenuStack ();
			break;

		default:
			if (ev->data2 == 't')
			{
				// Test selected resolution
				if (CurrentMenu == &ModesMenu)
				{
					if (!(item->type == screenres &&
						GetSelectedSize (CurrentItem, &NewWidth, &NewHeight)))
					{
						NewWidth = screen->width;
						NewHeight = screen->height;
					}
					OldWidth = screen->width;
					OldHeight = screen->height;
					OldBits = DisplayBits;
					NewBits = BitTranslate[(int)DummyDepthCvar.value];
					setmodeneeded = true;
					testingmode = I_GetTime() + 5 * TICRATE;
					S_Sound (CHAN_VOICE, "weapons/pistol", 1, ATTN_NONE);
					SetModesMenu (NewWidth, NewHeight, NewBits);
				}
			}
			else if (ev->data2 == 'd' && CurrentMenu == &ModesMenu)
			{
				// Make current resolution the default
				vid_defwidth.Set ((float)screen->width);
				vid_defheight.Set ((float)screen->height);
				vid_defbits.Set ((float)DisplayBits);
				SetModesMenu (screen->width, screen->height, DisplayBits);
			}
			break;
	}
}

// [BC]
void M_DrawDMFlags (void)
{
	char flagsblah[64];

	sprintf (flagsblah, "dmflags = %i", dmflags);
	screen->DrawTextCleanMove (CR_GREY, 160 - (V_StringWidth (flagsblah) >> 1), 190, flagsblah);
}

void M_DrawDMFlags2 (void)
{
	char flagsblah[64];

	sprintf (flagsblah, "dmflags2 = %i", dmflags2);
	screen->DrawTextCleanMove (CR_GREY, 160 - (V_StringWidth (flagsblah) >> 1), 190, flagsblah);
}

// [BC]
void M_DrawCLDMFlags (void)
{
	char flagsblah[64];

	sprintf (flagsblah, "dmflags = %i", cl_dmflags);
	screen->DrawTextCleanMove (CR_GREY, 160 - (V_StringWidth (flagsblah) >> 1), 190, flagsblah);
}

void M_DrawCLDMFlags2 (void)
{
	char flagsblah[64];

	sprintf (flagsblah, "dmflags2 = %i", cl_dmflags2);
	screen->DrawTextCleanMove (CR_GREY, 160 - (V_StringWidth (flagsblah) >> 1), 190, flagsblah);
}

void M_DrawSTVer (void)
{
	char line[64];

	sprintf (line, "Skull Tag v%sb", DOTVERSIONSTR);
	screen->DrawTextCleanMove (CR_GREY, 160 - (V_StringWidth (line) >> 1), 182, line);
	sprintf (line, "\"Special Hissy Edition\"", DOTVERSIONSTR);
	screen->DrawTextCleanMove (CR_GREY, 160 - (V_StringWidth (line) >> 1), 190, line);
}

// [BC] Draw little level warning messages
void M_DrawLvlStuff (void)
{
	char line[64];

	switch ((int)cl_mode.value)
	{
	case gm_normal:
		if (!(MapVals[current-1].flags & LV_NORMAL))
		{
			sprintf (line, "Warning: Normal may", DOTVERSIONSTR);
			screen->DrawTextCleanMove (CR_GREY, 160 - (V_StringWidth (line) >> 1), 182, line);
			sprintf (line, "not be supported on this level!", DOTVERSIONSTR);
			screen->DrawTextCleanMove (CR_GREY, 160 - (V_StringWidth (line) >> 1), 190, line);
		}
		break;
	case gm_deathmatch:
	case gm_teamplay:
	case gm_survival:
	case gm_scoremode:
	case gm_instagib:
	case gm_terminator:
	case gm_oldschool:
		if (!(MapVals[current-1].flags & LV_DEATHMATCH))
		{
			sprintf (line, "Warning: Deathmatch may", DOTVERSIONSTR);
			screen->DrawTextCleanMove (CR_GREY, 160 - (V_StringWidth (line) >> 1), 182, line);
			sprintf (line, "not be supported on this level!", DOTVERSIONSTR);
			screen->DrawTextCleanMove (CR_GREY, 160 - (V_StringWidth (line) >> 1), 190, line);
		}
		break;
	case gm_ctf:
		if (!(MapVals[current-1].flags & LV_TEAMGAME))
		{
			sprintf (line, "Warning: Teamgame may", DOTVERSIONSTR);
			screen->DrawTextCleanMove (CR_GREY, 160 - (V_StringWidth (line) >> 1), 182, line);
			sprintf (line, "not be supported on this level!", DOTVERSIONSTR);
			screen->DrawTextCleanMove (CR_GREY, 160 - (V_StringWidth (line) >> 1), 190, line);
		}
		break;
	default:
		break;
	}
}

void M_DrawSVStuff (void)
{
	char line[64];

	sprintf (line, "Local IP: %s", NET_AdrToString(NET_GetLocalAddress()));
	screen->DrawTextCleanMove (CR_GREY, 160 - (V_StringWidth (line) >> 1), 190, line);
}

static void GoToConsole (void)
{
	M_ClearMenus ();
	C_ToggleConsole ();
}

static void UpdateStuff (void)
{
	M_SizeDisplay (0.0);
}

void Reset2Defaults (void)
{
	AddCommandString ("unbindall; binddefaults");
	C_SetCVarsToDefaults ();
	UpdateStuff();
}

void Reset2Saved (void)
{
	char *execcommand;
	char *configfile = GetConfigPath ();

	execcommand = new char[strlen (configfile) + 8];
	sprintf (execcommand, "exec \"%s\"", configfile);
	delete[] configfile;
	AddCommandString (execcommand);
	delete[] execcommand;
	UpdateStuff();
}

// [BC]
void Cinematics (void)
{
	M_StartMessage("You'll just have to wait for this!\n\npress a key.",NULL,false);
	return;
}

static void StartMessagesMenu (void)
{
	M_SwitchMenu (&MessagesMenu);
}

static void M_SetupNewGame (void)
{
	int flags = cl_dmflags;
	int flags2 = cl_dmflags2;

	timelimit.Set (cl_timelimit.value);
	fraglimit.Set (cl_fraglimit.value);
	pointlimit.Set (cl_pointlimit.value);
	scorelimit.Set (cl_scorelimit.value);

	deathmatch.Set (0.0f);
	teamgame.Set (0.0f);
	teamplay.Set (0.0f);
	scoremode.Set (0.0f);
	survival.Set (0.0f);
	instagib.Set (0.0f);
	terminator.Set (0.0f);
	oldschool.Set (0.0f);

	switch ((int)cl_mode.value)
	{
	case gm_deathmatch:
		deathmatch.Set (1.0f);
		flags |= DF_WEAPONS_STAY | DF_ITEMS_RESPAWN | DF_NO_MONSTERS;
		break;
	case gm_teamplay:
		deathmatch.Set (1.0f);
		teamplay.Set (1.0f);
		flags |= DF_WEAPONS_STAY | DF_ITEMS_RESPAWN | DF_NO_MONSTERS;
		break;
	case gm_survival:
		deathmatch.Set (1.0f);
		survival.Set (1.0f);
		flags |= DF_WEAPONS_STAY | DF_ITEMS_RESPAWN | DF_FORCE_RESPAWN | DF_NO_MONSTERS;
		flags2 |= DF2_YES_DOUBLEAMMO | DF2_YES_LOSEFRAG | DF2_YES_INVUL;
		break;
	case gm_scoremode:
		deathmatch.Set (1.0f);
		scoremode.Set (1.0f);
		flags |= DF_WEAPONS_STAY | DF_ITEMS_RESPAWN | DF_NO_MONSTERS;
		flags2 |= DF2_YES_DOUBLEAMMO | DF2_YES_INVUL | DF2_YES_WEAPONDROP;
		break;
	case gm_instagib:
		deathmatch.Set (1.0f);
		instagib.Set (1.0f);
		flags |= DF_WEAPONS_STAY | DF_ITEMS_RESPAWN | DF_NO_MONSTERS;
		flags2 |= DF2_YES_DOUBLEAMMO | DF2_YES_INVUL | DF2_YES_WEAPONDROP;
		break;
	case gm_terminator:
		deathmatch.Set (1.0f);
		terminator.Set (1.0f);
		flags |= DF_WEAPONS_STAY | DF_ITEMS_RESPAWN | DF_NO_MONSTERS;
		flags2 |= DF2_YES_DOUBLEAMMO | DF2_YES_INVUL;
		break;
	case gm_oldschool:
		deathmatch.Set (1.0f);
		oldschool.Set (1.0f);
		flags = DF_WEAPONS_STAY | DF_ITEMS_RESPAWN | DF_NO_MONSTERS | DF_NO_FREELOOK | DF_NO_JUMP;
		flags2 = 0;
		break;
	case gm_ctf:
		teamgame.Set (1.0f);
		flags |= DF_WEAPONS_STAY | DF_ITEMS_RESPAWN | DF_NO_MONSTERS;
		flags2 |= DF2_YES_DOUBLEAMMO | DF2_YES_INVUL | DF2_YES_WEAPONDROP;
		break;
	default:
		break;
	}

	gameskill.Set (cl_skill.value);
	botskill.Set (cl_botskill.value);

	dmflags = flags;
	dmflags2 = flags2;

	UnlatchCVars ();
}

static void StartSkirmishGame (void)
{
	char str[8];
	int tics = 1*TICRATE;

	netstate = NS_SKIRMISH;

	for (int i=0; i<MAXPLAYERS; i++)
	{
		if (botspawn[i].name)
		{
			botspawn[i].tics = tics;
			tics += 1*TICRATE;
		}
	}

	M_SetupNewGame();

	if (current <= 32)
		sprintf (str, "MAP%02d", current);
	else if (current >= 38)
		sprintf (str, "D2CTF%d", current-37);
	else
	{
		switch (current)
		{
		case 33:
			sprintf (str, "D2DM1");
			break;
		case 34:
			sprintf (str, "D2DM2");
			break;
		case 35:
			sprintf (str, "D2DM7");
			break;
		case 36:
			sprintf (str, "D2DM9");
			break;
		case 37:
			sprintf (str, "D2DMSEC");
			break;
		default:
			sprintf (str, "D2DM1");
			break;
		}
	}

	M_ClearMenus ();
	G_InitNew (str);

	AddCommandString ("removebots");
}

static void StartServer (void)
{
	char str[8];
	int tics = 1*TICRATE;

	SV_InitNetwork ();

	for (int i=0; i<MAXPLAYERS; i++)
	{
		if (botspawn[i].name)
		{
			botspawn[i].tics = tics;
			tics += 1*TICRATE;
		}
	}

	M_SetupNewGame();

	if (current <= 32)
		sprintf (str, "MAP%02d", current);
	else if (current >= 38)
		sprintf (str, "D2CTF%d", current-37);
	else
	{
		switch (current)
		{
		case 33:
			sprintf (str, "D2DM1");
			break;
		case 34:
			sprintf (str, "D2DM2");
			break;
		case 35:
			sprintf (str, "D2DM7");
			break;
		case 36:
			sprintf (str, "D2DM9");
			break;
		case 37:
			sprintf (str, "D2DMSEC");
			break;
		default:
			sprintf (str, "D2DM1");
			break;
		}
	}

	M_ClearMenus ();
	G_InitNew (str);

	AddCommandString ("removebots");
}

static void JoinGame (void)
{
	char str[128];
	CL_InitNetwork ();
	sprintf (str, "connect %s", conip);
	AddCommandString (str);
	M_ClearMenus ();
	gameaction = ga_fullconsole;
}

static void CustomizeControls (void)
{
	M_BuildKeyList (ControlsMenu.items, ControlsMenu.numitems);
	M_SwitchMenu (&ControlsMenu);
}

BEGIN_COMMAND (menu_keys)
{
	M_StartControlPanel ();
	S_Sound (CHAN_VOICE, "switches/normbutn", 1, ATTN_NONE);
	OptionsActive = true;
	CustomizeControls();
}
END_COMMAND (menu_keys)

// [BC]
EXTERN_CVAR (cl_dmflagsvar)
EXTERN_CVAR (cl_dmflags2var)

static void GameplayOptions (void)
{
	M_SwitchMenu (&DMFlagsMenu);
	flagsvar = &dmflagsvar;
}
// [BC]
static void MoreGameplayOptions (void)
{
	M_SwitchMenu (&DMFlags2Menu);
	flags2var = &dmflags2var;
}

// [BC] Hack hacks
static void CLGameplayOptions (void)
{
	M_SwitchMenu (&CLDMFlagsMenu);
	clflagsvar = &cl_dmflagsvar;
}
static void MoreCLGameplayOptions (void)
{
	M_SwitchMenu (&CLDMFlags2Menu);
	clflags2var = &cl_dmflags2var;
}

BEGIN_COMMAND (menu_gameplay)
{
	M_StartControlPanel ();
	S_Sound (CHAN_VOICE, "switches/normbutn", 1, ATTN_NONE);
	OptionsActive = true;
	GameplayOptions ();
}
END_COMMAND (menu_gameplay)
// [BC]
BEGIN_COMMAND (menu_moregameplay)
{
	M_StartControlPanel ();
	S_Sound (CHAN_VOICE, "switches/normbutn", 1, ATTN_NONE);
	OptionsActive = true;
	MoreGameplayOptions ();
}
END_COMMAND (menu_moregameplay)

// [BC]
static void BotSetup (void)
{
	M_SwitchMenu (&BSMenu);
}
// [BC]
static void MapRotation (void)
{
	M_SwitchMenu (&MapRotationMenu);
}
static void VideoOptions (void)
{
	M_SwitchMenu (&VideoMenu);
}

// [BC]
static void MoreVideoOptions (void)
{
	M_SwitchMenu (&MoreVideoMenu);
}

// [BC]
static void MoreControlsOptions (void)
{
	M_BuildKeyList (MoreControlsMenu.items, MoreControlsMenu.numitems);
	M_SwitchMenu (&MoreControlsMenu);
}

// [BC]
static void PlayerSetup (void)
{
	P_BuildColor (consoleplayer);
	M_SwitchMenu (&PSMenu);
}

// [BC]
static void TeamSetup (void)
{
	P_BuildColor (consoleplayer);
	M_SwitchMenu (&TSMenu);
}

// [BC]
static void Skirmish (void)
{
	M_SwitchMenu (&SkirMenu);
}

// [BC]
static void Server (void)
{
	M_StartMessage ("Please use the launcher for now.\n\npress a key.", NULL, false);
	return;

	M_SwitchMenu (&ServMenu);
}

// [BC]
static void Client (void)
{
	M_StartMessage ("Please use the launcher for now.\n\npress a key.", NULL, false);
	return;

	M_SwitchMenu (&ClientMenu);
}

BEGIN_COMMAND (menu_display)
{
	M_StartControlPanel ();
	S_Sound (CHAN_VOICE, "switches/normbutn", 1, ATTN_NONE);
	OptionsActive = true;
	M_SwitchMenu (&VideoMenu);
}
END_COMMAND (menu_display)

static void BuildModesList (int hiwidth, int hiheight, int hi_bits)
{
	char strtemp[32], **str;
	int	 i, c;
	int	 width, height, showbits;

	showbits = BitTranslate[(int)DummyDepthCvar.value];

	I_StartModeIterator (showbits);

	for (i = VM_RESSTART; ModesItems[i].type == screenres; i++)
	{
		ModesItems[i].e.highlight = -1;
		for (c = 0; c < 3; c++)
		{
			switch (c)
			{
				case 0:
					str = &ModesItems[i].b.res1;
					break;
				case 1:
					str = &ModesItems[i].c.res2;
					break;
				case 2:
					str = &ModesItems[i].d.res3;
					break;
			}
			if (I_NextMode (&width, &height))
			{
				if (/* hi_bits == showbits && */ width == hiwidth && height == hiheight)
					ModesItems[i].e.highlight = ModesItems[i].a.selmode = c;
				
				sprintf (strtemp, "%dx%d", width, height);
				ReplaceString (str, strtemp);
			}
			else
			{
				if (*str)
				{
					free (*str);
					*str = NULL;
				}
			}
		}
	}
}

void M_RefreshModesList ()
{
	BuildModesList (screen->width, screen->height, DisplayBits);
}

static BOOL GetSelectedSize (int line, int *width, int *height)
{
	int i, stopat;

	if (ModesItems[line].type != screenres)
	{
		return false;
	}
	else
	{
		I_StartModeIterator (BitTranslate[(int)DummyDepthCvar.value]);

		stopat = (line - VM_RESSTART) * 3 + ModesItems[line].a.selmode + 1;

		for (i = 0; i < stopat; i++)
			if (!I_NextMode (width, height))
				return false;
	}

	return true;
}

static int FindBits (int bits)
{
	int i;

	for (i = 0; i < 22; i++)
	{
		if (BitTranslate[i] == bits)
			return i;
	}

	return 0;
}

static void SetModesMenu (int w, int h, int bits)
{
	char strtemp[64];

	DummyDepthCvar.Set ((float)FindBits (bits));

	if (!testingmode)
	{
		if (ModesItems[VM_ENTERLINE].label != VMEnterText)
			free (ModesItems[VM_ENTERLINE].label);
		ModesItems[VM_ENTERLINE].label = VMEnterText;
		ModesItems[VM_TESTLINE].label = VMTestText;

		sprintf (strtemp, "D to make %dx%dx%d the default", w, h, bits);
		ReplaceString (&ModesItems[VM_MAKEDEFLINE].label, strtemp);

		sprintf (strtemp, "Current default is %dx%dx%d",
				 (int)vid_defwidth.value,
				 (int)vid_defheight.value,
				 (int)vid_defbits.value);
		ReplaceString (&ModesItems[VM_CURDEFLINE].label, strtemp);
	}
	else
	{
		sprintf (strtemp, "TESTING %dx%dx%d", w, h, bits);
		ModesItems[VM_ENTERLINE].label = copystring (strtemp);
		ModesItems[VM_TESTLINE].label = "Please wait 5 seconds...";
		ModesItems[VM_MAKEDEFLINE].label = copystring (" ");
		ModesItems[VM_CURDEFLINE].label = copystring (" ");
	}

	BuildModesList (w, h, bits);
}

void M_RestoreMode (void)
{
	NewWidth = OldWidth;
	NewHeight = OldHeight;
	NewBits = OldBits;
	setmodeneeded = true;
	testingmode = 0;
	SetModesMenu (OldWidth, OldHeight, OldBits);
}

static void SetVidMode (void)
{
	SetModesMenu (screen->width, screen->height, DisplayBits);
	if (ModesMenu.items[ModesMenu.lastOn].type == screenres)
	{
		if (ModesMenu.items[ModesMenu.lastOn].a.selmode == -1)
		{
			ModesMenu.items[ModesMenu.lastOn].a.selmode++;
		}
	}
	M_SwitchMenu (&ModesMenu);
}

BEGIN_COMMAND (menu_video)
{
	M_StartControlPanel ();
	S_Sound (CHAN_VOICE, "switches/normbutn", 1, ATTN_NONE);
	OptionsActive = true;
	SetVidMode ();
}
END_COMMAND (menu_video)
