/*
	client/menu.qc

	menu stuff (very ugly)

	Copyright (C) 2021-2022 NZ:P Team

	This program is free software; you can redistribute it and/or
	modify it under the terms of the GNU General Public License
	as published by the Free Software Foundation; either version 2
	of the License, or (at your option) any later version.

	This program is distributed in the hope that it will be useful,
	but WITHOUT ANY WARRANTY; without even the implied warranty of
	MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.

	See the GNU General Public License for more details.

	You should have received a copy of the GNU General Public License
	along with this program; if not, write to:

		Free Software Foundation, Inc.
		59 Temple Place - Suite 330
		Boston, MA  02111-1307, USA

*/

float current_custom_map_page;
float custom_map_pages;
float last_active_custom_select;
float active_custom_select;

string temp_player_name;
float editing_player_name;

string temp_server_name;
float editing_server_id;

string temp_password;
float editing_password;

string temp_hostname;
float editing_hostname;

void() menu_single =
{
	in_menu = MENU_SINGLE;
	time_in_menu = 0;
	matchmake_enabled = false;
	localcmd("sv_public 0\n");

	if (platform_is_web)
		localcmd("sv_listen_qw 0\n");
};

void() menu_create =
{
	in_menu = MENU_CREATE;
	time_in_menu = 0;
};

void() menu_matchmake =
{
	in_menu = MENU_SINGLE;
	time_in_menu = 0;
	matchmake_enabled = true;
	localcmd("sv_public 2\n");

	if (platform_is_web)
		localcmd("sv_listen_qw 1\n");
};

void() menu_restart =
{
	in_menu = MENU_RES;
};

void() menu_resy =
{
	if (platform_is_web == false)
		localcmd("restart\n");
	else
		localcmd("qc_soft_restart\n");
};

void() menu_resn =
{
	in_menu = MENU_PAUSE;
};

void() menu_multi =
{
	in_menu = MENU_MULTI;
	time_in_menu = 0;
	temp_player_name = cvar_string("name");
};

void() menu_settings =
{
	if (in_menu == MENU_PAUSE)
		in_menu = MENU_IGS;
	else
		in_menu = MENU_SETTINGS;
};

void() menu_about =
{
	in_menu = MENU_ABOUT;
	time_in_menu = 0;
};

void() menu_quit =
{
	localcmd("quit\n");
};

void() menu_back =
{
	if (in_menu == MENU_CSETTINGS)
		localcmd("saveconfig\n");

	if (in_menu == MENU_GSETTINGS || in_menu == MENU_CSETTINGS ||
	in_menu == MENU_AUDSETTINGS || in_menu == MENU_CONSETTINGS) {
		if (wasigs == TRUE)
			in_menu = MENU_IGS;
		else
			in_menu = MENU_SETTINGS;

		wasigs = FALSE;
	} else if (in_menu == MENU_IGS) {
		in_menu = MENU_PAUSE;
	} else if (in_menu == MENU_CUSTOMS) {
		in_menu = MENU_SINGLE;
	} else if (in_menu == MENU_SINGLE && matchmake_enabled == true) {
		in_menu = MENU_MULTI;
	} else if (in_menu == MENU_CREATE || in_menu == MENU_JOIN) {
		in_menu = MENU_MULTI;
	} else
		in_menu = MENU_MAIN;

	editing_player_name = editing_server_id = editing_password = editing_hostname = false;
};

void() menu_loadndu =
{
	loadscreen_timetrigger = cltime + 0.6;
	loadscreen_mapname = "ndu";
	loadscreen_maptitle = "Nacht der Untoten";
};

void() menu_loadwh =
{
	loadscreen_timetrigger = cltime + 0.6;
	loadscreen_mapname = "nzp_warehouse";
	loadscreen_maptitle = "Warehouse (Classic)";
};

void() menu_loadwh2 =
{
	loadscreen_timetrigger = cltime + 0.6;
	loadscreen_mapname = "nzp_warehouse2";
	loadscreen_maptitle = "Warehouse";
};

void() menu_loadch =
{
	loadscreen_timetrigger = cltime + 0.6;
	loadscreen_mapname = "christmas_special";
	loadscreen_maptitle = "Christmas Special";
};

void() menu_loadwn =
{
	//localcmd("map wahnsinn\n");
};

void() menu_loadkn =
{
	//localcmd("map kino\n");
};

void() menu_join =
{
	in_menu = MENU_JOIN;
	time_in_menu = 0;
};

void() game_join =
{	
	localcmd("cmd joingame\n");
	in_menu = MENU_NONE;
	setcursormode(FALSE);
};

void() game_spec =
{
	localcmd("cmd specgame\n");
	in_menu = MENU_NONE;
	setcursormode(FALSE);
};

void() menu_resume =
{
	if (player_count == 0)
		localcmd("pause");
	in_menu = MENU_NONE;
	setcursormode(FALSE);
};

void() menu_main =
{
	in_menu = MENU_MAIN;
	localcmd("disconnect\n");
};

void() menu_graphics =
{
	if (in_menu == MENU_IGS)
		wasigs = TRUE;
	else
		wasigs = FALSE;

	in_menu = MENU_GSETTINGS;
};

void() menu_consettings =
{
	if (in_menu == MENU_IGS)
		wasigs = TRUE;
	else
		wasigs = FALSE;

	in_menu = MENU_CONSETTINGS;
}

void() menu_audsettings =
{
	if (in_menu == MENU_IGS)
		wasigs = TRUE;
	else
		wasigs = FALSE;

	in_menu = MENU_AUDSETTINGS;
}

void() menu_controls =
{
	if (in_menu == MENU_IGS)
		wasigs = TRUE;
	else
		wasigs = FALSE;

	in_menu = MENU_CSETTINGS;
}

void() menu_customs =
{
	in_menu = MENU_CUSTOMS;
	customs_interact = FALSE;
}

void() menu_achievements =
{
    in_menu = MENU_ACHIEVEMENTS;
}

void() menu_waypoint =
{	
	localcmd("waypoint_mode 1");
};

//
//settings adjustments
//

void() settings_fps = {
	local float fps = cvar("show_fps");

	if (fps == 0)
		fps = 1;
	else
		fps = 0;

	localcmd(strcat("seta show_fps ", ftos(fps)));
}

void() settings_rescale = {
	float renderscale = cvar("r_renderscale");

	renderscale += 0.05;

	if (renderscale > 1)
		renderscale = 1;

	localcmd(strcat("seta r_renderscale ", ftos(renderscale)));
}

void() settings_rescale2 = {
	float renderscale = cvar("r_renderscale");

	renderscale -= 0.05;

	if (renderscale < 0.10)
		renderscale = 0.10;

	localcmd(strcat("seta r_renderscale ", ftos(renderscale)));
}

void() settings_brite = {
	local float br = cvar("gamma");

	br += 0.1;

	if (br > 2) {
		br = 2;
	}

	localcmd(strcat("seta gamma ", ftos(br)));
}

void() settings_brite2 = {
	local float br = cvar("gamma");

	br -= 0.1;

	if (br < 0.5) {
		br = 0.5;
	}

	localcmd(strcat("seta gamma ", ftos(br)));
}

void() settings_fov = {
	// Increase FOV by 5
	local float fov = cvar("fov");

	if (fov < 120)
		fov += 5;

	localcmd(strcat("seta fov ", ftos(fov)));
}

void() settings_fov2 = {
	// Decrease FOV by 5
	local float fov = cvar("fov");

	if (fov > 50)
		fov -= 5;

	localcmd(strcat("seta fov ", ftos(fov)));
}

void() settings_mfps = {
	// Increase max fps by 5
	if (cvar("vid_vsync"))
		return;

	local float fps = cvar("cl_maxfps");

	if (fps < 500)
		fps += 5;

	localcmd(strcat("seta cl_maxfps ", ftos(fps)));
}

void() settings_mfps2 = {
	// Decrease max fps by 5
	if (cvar("vid_vsync"))
		return;

	local float fps = cvar("cl_maxfps");

	if (fps > 20)
		fps -= 5;

	localcmd(strcat("seta cl_maxfps ", ftos(fps)));
}

void() settings_vs = {
	local float vs = cvar("vid_vsync");

	if (vs == 0)
		vs = 1;
	else
		vs = 0;

	localcmd(strcat("seta vid_vsync ", ftos(vs)));
}

void() settings_fs = {
	if (fullscreenval == 0)
		fullscreenval = 1;
	else
		fullscreenval = 0;
}

void() settings_xb = {
	if (cvar("vid_ultrawide_limiter")) {
		localcmd("seta vid_ultrawide_limiter 0");
	} else {
		localcmd("seta vid_ultrawide_limiter 1");
	}
}

void() settings_pt = {
	local float pt = cvar("nzp_particles");

	if (pt == 0)
		pt = 1;
	else
		pt = 0;

	localcmd(strcat("seta nzp_particles ", ftos(pt)));
}

void() settings_fb = {
	local float fb = cvar("r_fullbright");

	if (fb == 0)
		fb = 1;
	else
		fb = 0;

	localcmd(strcat("seta r_fullbright ", ftos(fb)));
}

void() settings_ro = {
	if (cvar_string("gl_texturemode") == "gl_nearest")
		localcmd("seta gl_texturemode gl_linear_mipmap_linear");
	else
		localcmd("seta gl_texturemode gl_nearest");
}

void() UpdateResolutions = {
	switch(aspectratio) {
		case 0:
			active_swidth = screen_resolution_4x3[0].width;
			active_sheight = screen_resolution_4x3[0].height;
			break;
		case 1:
			active_swidth = screen_resolution_5x4[0].width;
			active_sheight = screen_resolution_5x4[0].height;
			break;
		case 2:
			active_swidth = screen_resolution_16x9[0].width;
			active_sheight = screen_resolution_16x9[0].height;
			break;
		case 3:
			active_swidth = screen_resolution_16x10[0].width;
			active_sheight = screen_resolution_16x10[0].height;
			break;
		case 4:
			active_swidth = screen_resolution_21x9[0].width;
			active_sheight = screen_resolution_21x9[0].height;
			break;
	}
}

void() settings_ar = {
	// aspect ratio up
	// 0 - 4:3
	// 1 - 5:4
	// 2 - 16:9
	// 3 - 16:10
	// 4 - 21:9
	aspectratio++;

	if (aspectratio > 4)
		aspectratio = 0;

	UpdateResolutions();
}

void() settings_ar2 = {
	// aspect ratio down
	// 0 - 4:3
	// 1 - 5:4
	// 2 - 16:9
	// 3 - 16:10
	// 4 - 21:9
	aspectratio--;

	if (aspectratio < 0)
		aspectratio = 4;

	UpdateResolutions();
}

void() settings_re = {
	float neww, newh;
	neww = 0;
	newh = 0;

	switch(aspectratio) {
		case 0:
			if (active_swidth == screen_resolution_4x3[screen_resolution_4x3.length - 1].width) {
				neww = screen_resolution_4x3[0].width;
				newh = screen_resolution_4x3[0].height;
			} else {
				for (int i = 0; i < screen_resolution_4x3.length; i++) {
					if (active_swidth == screen_resolution_4x3[i].width) {
						neww = screen_resolution_4x3[i + 1].width;
						newh = screen_resolution_4x3[i + 1].height;
					}
				}

				// if still 0, different aspect ratio
				if (neww == 0) {
					neww = screen_resolution_4x3[0].width;
					newh = screen_resolution_4x3[0].height;
				}
			}
			break;
		case 1:
			if (active_swidth == screen_resolution_5x4[screen_resolution_5x4.length - 1].width) {
				neww = screen_resolution_5x4[0].width;
				newh = screen_resolution_5x4[0].height;
			} else {
				for (int i = 0; i < screen_resolution_5x4.length; i++) {
					if (active_swidth == screen_resolution_5x4[i].width) {
						neww = screen_resolution_5x4[i + 1].width;
						newh = screen_resolution_5x4[i + 1].height;
					}
				}

				// if still 0, different aspect ratio
				if (neww == 0) {
					neww = screen_resolution_5x4[0].width;
					newh = screen_resolution_5x4[0].height;
				}
			}
			break;
		case 2:
			if (active_swidth == screen_resolution_16x9[screen_resolution_16x9.length - 1].width) {
				neww = screen_resolution_16x9[0].width;
				newh = screen_resolution_16x9[0].height;
			} else {
				for (int i = 0; i < screen_resolution_16x9.length; i++) {
					if (active_swidth == screen_resolution_16x9[i].width) {
						neww = screen_resolution_16x9[i + 1].width;
						newh = screen_resolution_16x9[i + 1].height;
					}
				}

				// if still 0, different aspect ratio
				if (neww == 0) {
					neww = screen_resolution_16x9[0].width;
					newh = screen_resolution_16x9[0].height;
				}
			}
			break;
		case 3:
			if (active_swidth == screen_resolution_16x10[screen_resolution_16x10.length - 1].width) {
				neww = screen_resolution_16x10[0].width;
				newh = screen_resolution_16x10[0].height;
			} else {
				for (int i = 0; i < screen_resolution_16x10.length; i++) {
					if (active_swidth == screen_resolution_16x10[i].width) {
						neww = screen_resolution_16x10[i + 1].width;
						newh = screen_resolution_16x10[i + 1].height;
					}
				}

				// if still 0, different aspect ratio
				if (neww == 0) {
					neww = screen_resolution_16x10[0].width;
					newh = screen_resolution_16x10[0].height;
				}
			}
			break;
		case 4:
			if (active_swidth == screen_resolution_21x9[screen_resolution_21x9.length - 1].width) {
				neww = screen_resolution_21x9[0].width;
				newh = screen_resolution_21x9[0].height;
			} else {
				for (int i = 0; i < screen_resolution_21x9.length; i++) {
					if (active_swidth == screen_resolution_21x9[i].width) {
						neww = screen_resolution_21x9[i + 1].width;
						newh = screen_resolution_21x9[i + 1].height;
					}
				}

				// if still 0, different aspect ratio
				if (neww == 0) {
					neww = screen_resolution_21x9[0].width;
					newh = screen_resolution_21x9[0].height;
				}
			}
			break;
		default:
			break;
	}

	active_swidth = neww;
	active_sheight = newh;
}

void() settings_apply = {
	if (in_menu == MENU_GSETTINGS) {
		localcmd(strcat("vid_width ", ftos(active_swidth), "\n"));
		localcmd(strcat("vid_height ", ftos(active_sheight), "\n"));
		localcmd(strcat("vid_fullscreen ", ftos(fullscreenval), "\n"));
		localcmd("vid_restart\n");
	}
	localcmd("saveconfig\n");
}

void() bind_walkf = {
	if (editBind[0] == FALSE)
		editBind[0] = TRUE;
	else
		editBind[0] = FALSE;
}

void() bind_walkb = {
	if (editBind[1] == FALSE)
		editBind[1] = TRUE;
	else
		editBind[1] = FALSE;
}

void() bind_walkl = {
	if (editBind[2] == FALSE)
		editBind[2] = TRUE;
	else
		editBind[2] = FALSE;
}

void() bind_walkr = {
	if (editBind[3] == FALSE)
		editBind[3] = TRUE;
	else
		editBind[3] = FALSE;
}

void() bind_jump = {
	if (editBind[4] == FALSE)
		editBind[4] = TRUE;
	else
		editBind[4] = FALSE;
}

void() bind_sprint = {
	if (editBind[5] == FALSE)
		editBind[5] = TRUE;
	else
		editBind[5] = FALSE;
}

void() bind_stance = {
	if (editBind[6] == FALSE)
		editBind[6] = TRUE;
	else
		editBind[6] = FALSE;
}

void() bind_wpnn = {
	if (editBind[7] == FALSE)
		editBind[7] = TRUE;
	else
		editBind[7] = FALSE;
}

void() bind_wpnp = {
	if (editBind[8] == FALSE)
		editBind[8] = TRUE;
	else
		editBind[8] = FALSE;
}

void() bind_interact = {
	if (editBind[9] == FALSE)
		editBind[9] = TRUE;
	else
		editBind[9] = FALSE;
}

void() bind_reload = {
	if (editBind[10] == FALSE)
		editBind[10] = TRUE;
	else
		editBind[10] = FALSE;
}

void() bind_knife = {
	if (editBind[11] == FALSE)
		editBind[11] = TRUE;
	else
		editBind[11] = FALSE;
}

void() bind_nade = {
	if (editBind[12] == FALSE)
		editBind[12] = TRUE;
	else
		editBind[12] = FALSE;
}

void() bind_switchnade = {
	if (editBind[13] == FALSE)
		editBind[13] = TRUE;
	else
		editBind[13] = FALSE;
}

float next_gray_out;
float back_gray_out;

void() menu_custom_next =
{
	if (in_menu == MENU_CUSTOMS) {
		if (current_custom_map_page < custom_map_pages) {
			current_custom_map_page++;
			back_gray_out = 0;

			if (current_custom_map_page == custom_map_pages)
				next_gray_out = 1;
		}
	} else {
		if (current_achievement_page < achievement_pages) {
			current_achievement_page++;
			back_gray_out = 0;

			if (current_achievement_page == achievement_pages)
				next_gray_out = 1;
		}
	}
}

void() menu_custom_back =
{
	if (in_menu == MENU_CUSTOMS) {
		if (current_custom_map_page > 1) {
			current_custom_map_page--;
			next_gray_out = 0;

			if (current_custom_map_page == 1)
				back_gray_out = 1;
		}
	} else {
		if (current_achievement_page > 1) {
			current_achievement_page--;
			next_gray_out = 0;

			if (current_achievement_page == 1)
				back_gray_out = 1;
		}
	}	
}

void() settings_sens = {
	float sens = cvar("sensitivity");

	sens += 1;

	if (sens > 15)
		sens = 15;

	localcmd(strcat("seta sensitivity ", ftos(sens)));
}

void() settings_sens2 = {
	float sens = cvar("sensitivity");

	sens -= 1;

	if (sens < 0)
		sens = 0;

	localcmd(strcat("seta sensitivity ", ftos(sens)));
}

void() settings_adsmode =
{
	float adsmode = cvar("cl_adsmode");

	localcmd(strcat("seta cl_adsmode ", ftos(!adsmode)));
}

void() setting_mastervol =
{
	float vol = cvar("volume");

	vol += 0.1;

	if (vol > 1)
		vol = 1;

	localcmd(strcat("seta volume ", ftos(vol)));
}

void() setting_mastervol2 =
{
	float vol = cvar("volume");

	vol -= 0.1;

	if (vol < 0)
		vol = 0;

	localcmd(strcat("seta volume ", ftos(vol)));
}

void() setting_playername =
{
	editing_player_name = !editing_player_name;
	editing_server_id = editing_password = editing_hostname = false;
}

void() setting_serverid =
{
	editing_server_id = !editing_server_id;
	editing_player_name = editing_password = editing_hostname = false;
}

void() setting_password =
{
	editing_password = !editing_password;
	editing_player_name = editing_server_id = editing_hostname = false;
}

void() setting_servername =
{
	editing_hostname = !editing_hostname;
	editing_player_name = editing_server_id = editing_password = false;
}

void() setting_connect =
{
	localcmd(strcat("connect ", temp_server_name));
}

//rmb null
void() null = {
	return;
}

/*

	MENU GUIDELINES (to preserve consistency)
	
	* Buttons that belong to the same category should have a spacing
	difference of 20.

	* Buttons that do not belong to the same category should have
	a spacing difference of 30.

*/

//struct for all buttons, note scale is going to be multiplied w/ width/height in draw step, keep between 0 and 1
var struct
{
	vector	pos;
	string 	text;
	float 	active; 				// used for mouseover hilight (positive values) and alpha (negative values)
	void() 	action; 				// function that is called when the button is pressed with lmb
	void() 	action2; 				// rmb function
	float 	group; 					// a bit field, button will be usable/visible when these bits are active
	float 	gray_out; 				// if not to be accessed
	int		platform_dependency; 	// what platforms is this option visible on
} buttons[] =
{
	// Main
	{[6, 75], "Solo", -1, menu_single, null, MENU_MAIN, 0, OPTION_WEB_AND_EXE}, // 0
	{[6, 95], "Cooperative", -1, menu_multi, null, MENU_MAIN, 0, OPTION_WEB_AND_EXE}, // 1
	// ...
	{[6, 125], "Settings", -1, menu_settings, null, MENU_MAIN, 0, OPTION_WEB_AND_EXE}, // 2
	{[6, 145], "Achievements", -1, menu_achievements, null, MENU_MAIN, 0, OPTION_WEB_AND_EXE}, // 3
	// ...
	{[6, 175], "Credits", -1, menu_about, null, MENU_MAIN, 0, OPTION_WEB_AND_EXE}, // 4
	// ...
	{[6, 205], "Exit", -1, menu_quit, null, MENU_MAIN, 0, OPTION_EXE_ONLY}, // 5

	// Solo
	{[6, 75], "Nacht der Untoten", -1, menu_loadndu, null, MENU_SINGLE, 0, OPTION_WEB_AND_EXE}, // 6
	// ..
	{[6, 105], "Warehouse", -1, menu_loadwh2, null, MENU_SINGLE, 0, OPTION_WEB_AND_EXE}, // 7
	{[6, 125], "Warehouse (Classic)", -1, menu_loadwh, null, MENU_SINGLE, 0, OPTION_WEB_AND_EXE}, // 8
	{[6, 145], "Wahnsinn", -1, menu_loadwh, null, MENU_SINGLE, 1, OPTION_WEB_AND_EXE}, // 9
	{[6, 165], "Christmas Special", -1, menu_loadch, null, MENU_SINGLE, 0, OPTION_WEB_AND_EXE}, // 10
	// ...
	{[6, 195], "Custom Maps", -1, menu_customs, null, MENU_SINGLE, 0, OPTION_WEB_AND_EXE}, // 11

	// Pause
	{[6, 75], "Resume Carnage", -1, menu_resume, null, MENU_PAUSE, 0, OPTION_WEB_AND_EXE}, // 12
	{[6, 95], "Restart", -1, menu_restart, null, MENU_PAUSE, 0, OPTION_EXE_ONLY}, // 13
	{[6, 115], "Settings", -1, menu_settings, null, MENU_PAUSE, 0, OPTION_WEB_AND_EXE}, // 14
	{[6, 135], "Main Menu", -1, menu_main, null, MENU_PAUSE, 0, OPTION_WEB_AND_EXE}, // 15
	{[6, 0], "Waypoint Menu", -1, menu_waypoint, null, MENU_PAUSE, 0, OPTION_EXE_ONLY}, // 16

	// Restart
	{[6, 75], "Yes", -1, menu_resy, null, MENU_RES, 0, OPTION_WEB_AND_EXE}, // 17
	{[6, 95], "No", -1, menu_resn, null, MENU_RES, 0, OPTION_WEB_AND_EXE}, // 18

	// Settings
	{[6, 75], "Graphics Settings", -1, menu_graphics, null, MENU_SETTINGS + MENU_IGS, 0, OPTION_WEB_AND_EXE}, // 19
	{[6, 95], "Controls", -1, menu_controls, null, MENU_SETTINGS + MENU_IGS, 0, OPTION_WEB_AND_EXE}, // 20
	{[6, 115], "Control Settings", -1, menu_consettings, null, MENU_SETTINGS + MENU_IGS, 0, OPTION_WEB_AND_EXE}, // 21
	{[6, 135], "Audio Settings", -1, menu_audsettings, null, MENU_SETTINGS + MENU_IGS, 0, OPTION_WEB_AND_EXE}, // 22

	// Graphics
	{[6, 75, 0], "Render Scale", -1, settings_rescale, settings_rescale2, MENU_GSETTINGS, 0, OPTION_WEB_ONLY}, // 23
	{[6, 95], "Resolution", -1, settings_re, settings_re, MENU_GSETTINGS, 0, OPTION_EXE_ONLY}, // 24
	{[6, 115], "Aspect Ratio", -1, settings_ar, settings_ar2, MENU_GSETTINGS, 0, OPTION_EXE_ONLY}, // 25
	{[6, 135], "Fullscreen", -1, settings_fs, settings_fs, MENU_GSETTINGS, 0, OPTION_WEB_AND_EXE}, // 26
	{[6, 155], "Show FPS", -1, settings_fps, settings_fps, MENU_GSETTINGS, 0, OPTION_WEB_AND_EXE}, // 27
	{[6, 175], "Max FPS", -1, settings_mfps, settings_mfps2, MENU_GSETTINGS, 0, OPTION_WEB_AND_EXE}, // 28
	{[6, 195], "VSync", -1, settings_vs, settings_vs, MENU_GSETTINGS, 0, OPTION_EXE_ONLY}, // 29
	{[6, 215], "Field of View", -1, settings_fov, settings_fov2, MENU_GSETTINGS, 0, OPTION_WEB_AND_EXE}, // 30
	{[6, 235], "Brightness", -1, settings_brite, settings_brite2, MENU_GSETTINGS, 0, OPTION_WEB_AND_EXE}, // 31
	{[6, 255], "Ultrawide Mode", -1, settings_xb, settings_xb, MENU_GSETTINGS, 0, OPTION_WEB_AND_EXE}, // 32
	{[6, 275], "Particles", -1, settings_pt, settings_pt, MENU_GSETTINGS, 0, OPTION_WEB_AND_EXE}, // 33
	{[6, 295], "Fullbright", -1, settings_fb, settings_fb, MENU_GSETTINGS, 0, OPTION_WEB_AND_EXE}, // 34
	{[6, 315], "Retro", -1, settings_ro, settings_ro, MENU_GSETTINGS, 0, OPTION_WEB_AND_EXE}, // 35
	{[6, -1], "Save & Apply", -1, settings_apply, settings_apply, MENU_GSETTINGS + MENU_CONSETTINGS +
																			MENU_AUDSETTINGS, 0, OPTION_WEB_AND_EXE}, // 36

	// Controls
	{[6, 75], "Walk Forward", -1, bind_walkf, null, MENU_CSETTINGS, 0, OPTION_WEB_AND_EXE}, // 37
	{[6, 95], "Walk Backward", -1, bind_walkb, null, MENU_CSETTINGS, 0, OPTION_WEB_AND_EXE}, // 38
	{[6, 115], "Walk Left", -1, bind_walkl, null, MENU_CSETTINGS, 0, OPTION_WEB_AND_EXE}, // 39
	{[6, 135], "Walk Right", -1, bind_walkr, null, MENU_CSETTINGS, 0, OPTION_WEB_AND_EXE}, // 40
	{[6, 155], "Jump", -1, bind_jump, null, MENU_CSETTINGS, 0, OPTION_WEB_AND_EXE}, // 41
	{[6, 175], "Sprint", -1, bind_sprint, null, MENU_CSETTINGS, 0, OPTION_WEB_AND_EXE}, // 42
	{[6, 195], "Change Stance", -1, bind_stance, null, MENU_CSETTINGS, 0, OPTION_WEB_AND_EXE}, // 43
	{[6, 215], "Weapon Next", -1, bind_wpnn, null, MENU_CSETTINGS, 0, OPTION_WEB_AND_EXE}, // 44
	{[6, 235], "Weapon Previous", -1, bind_wpnp, null, MENU_CSETTINGS, 0, OPTION_WEB_AND_EXE}, // 45
	{[6, 255], "Interact", -1, bind_interact, null, MENU_CSETTINGS, 0, OPTION_WEB_AND_EXE}, // 46
	{[6, 275], "Reload", -1, bind_reload, null, MENU_CSETTINGS, 0, OPTION_WEB_AND_EXE}, // 47
	{[6, 295], "Melee", -1, bind_knife, null, MENU_CSETTINGS, 0, OPTION_WEB_AND_EXE}, // 48
	{[6, 315], "Grenade", -1, bind_nade, null, MENU_CSETTINGS, 0, OPTION_WEB_AND_EXE}, // 49
	{[6, 335], "Secondary Grenade", -1, bind_switchnade, null, MENU_CSETTINGS, 0, OPTION_WEB_AND_EXE}, // 50

	// Custom Maps
	{[6, -2], "Next Page", -1, menu_custom_next, null, MENU_CUSTOMS + MENU_ACHIEVEMENTS, 0, OPTION_WEB_AND_EXE}, // 51
	{[6, -1], "Previous Page", -1, menu_custom_back, null, MENU_CUSTOMS + MENU_ACHIEVEMENTS, 1, OPTION_WEB_AND_EXE}, // 52

	// Everything, pretty much
	{[6, 0], "Back", -1, menu_back, null, MENU_SINGLE + MENU_MULTI + MENU_JOIN + MENU_SETTINGS + MENU_ABOUT + 
													 MENU_IGS + MENU_GSETTINGS + MENU_CSETTINGS + MENU_CUSTOMS +
                                                     MENU_ACHIEVEMENTS + MENU_CREATE + MENU_CONSETTINGS + MENU_AUDSETTINGS, 0, 
													 OPTION_WEB_AND_EXE}, // 53

	// Control Settings
	{[6, 75], "ADS Mode", -1, settings_adsmode, null, MENU_CONSETTINGS, 0, OPTION_WEB_AND_EXE}, // 54
	{[6, 95], "Look Sensitivity", -1, settings_sens, settings_sens2, MENU_CONSETTINGS, 0, OPTION_WEB_AND_EXE}, // 55

	// Audio Settings
	{[6, 75], "Master Volume", -1, setting_mastervol, setting_mastervol2, MENU_AUDSETTINGS, 0, OPTION_WEB_AND_EXE}, // 56

	// Co-Op
	{[6, 75], "Player Name", -1, setting_playername, null, MENU_MULTI, 0, OPTION_WEB_AND_EXE}, // 57
	{[6, 115], "Join Game", -1, menu_join, null, MENU_MULTI, 0, OPTION_WEB_AND_EXE}, // 58
	{[6, 135], "Create Game", -1, menu_create, null, MENU_MULTI, 0, OPTION_WEB_AND_EXE}, // 59

	// Join Game
	{[6, 75], "Server Name/Room ID", -1, setting_serverid, null, MENU_JOIN, 0, OPTION_WEB_AND_EXE}, // 60
	{[6, 95], "Server Password", -1, setting_password, null, MENU_JOIN, 0, OPTION_WEB_AND_EXE}, // 61
	{[6, 115], "Connect to Server", -1, setting_connect, null, MENU_JOIN, 0, OPTION_WEB_AND_EXE}, // 62

	// Create Game
	{[6, 75], "Server Password", -1, setting_password, null, MENU_CREATE, 0, OPTION_WEB_AND_EXE}, // 63
	{[6, 95], "Server Name", -1, setting_servername, null, MENU_CREATE, 0, OPTION_WEB_AND_EXE}, // 64
	{[6, 115], "Choose Map", -1, menu_matchmake, null, MENU_CREATE, 0, OPTION_WEB_AND_EXE} // 65
};

//REMEMBER TO UPDATE THIS CONST IF YOU ADD BUTTONS
float BUTTONS_COUNT = 66;
float lastActive;


//this function handles drawing the buttons and checking if they should be active/hilighted
void(float index) Update_Button = 
{
	// Don't bother continuing if this button isn't available on our platform.
	if (platform_is_web && buttons[index].platform_dependency == OPTION_EXE_ONLY)
		return;
	else if (!platform_is_web && buttons[index].platform_dependency == OPTION_WEB_ONLY)
		return;

	//float btnscale = 0.030*g_height;

	if(in_menu != in_menu & buttons[index].group) 
		buttons[index].active = -1;
		
	if(buttons[index].active == -1)
	{
		if(in_menu == in_menu & buttons[index].group)
			buttons[index].active = 1;
		else
			return;
	}
	
	vector pos = buttons[index].pos;

	if (pos_y == 0)
		pos_y = g_height - 22;
	else if (pos_y == -1)
		pos_y = g_height - 75;
	else if (pos_y == -2)
		pos_y = g_height - 95;
	//pos_x *= g_width;
	//pos_y *= g_height;

	if (next_gray_out == 1)
		buttons[51].gray_out = 1;
	else
		buttons[51].gray_out = 0;

	if (back_gray_out == 1)
		buttons[52].gray_out = 1;
	else
		buttons[52].gray_out = 0;

	if (buttons[index].gray_out == 0) {
		if(buttons[index].active > 0) {
			drawstring(pos, buttons[index].text, [14, 14], [1, 0, 0], 1, 0);
		} else {
			drawstring(pos, buttons[index].text, [14, 14], [1, 1, 1], 1, 0);
		}
	} else {
		drawstring(pos, buttons[index].text, [14, 14], [0.25, 0.25, 0.25], 1, 0);
	}
	

	if (cursor_pos_x > pos_x && cursor_pos_x < pos_x + 300 && cursor_pos_y > pos_y && cursor_pos_y < pos_y + 14 && buttons[index].gray_out == 0) {
		if (index != lastActive)
			localsound_enhanced("sounds/menu/navigate.wav", CHAN_SFX, 1);
		
		buttons[index].active = 1;
		lastActive = index;
		last_active_custom_select = active_custom_select = -1;
	}

	if (lastActive == -1) {
		buttons[index].active = 0;
	} else {
		buttons[lastActive].active = 1;
	}
		

	if(buttons[index].active > 0)
	{
		buttons[index].active = 0;
	}
};

float xd;
void() Controller_UpdateButton =
{
	/*if (GPActive[0]) {

	}*/
	drawstring([0.025*g_width, 0.375*g_height, 0], ftos(GPActive[2]), [g_height * 0.020, g_height * 0.020, 1], [0.8,0.8,0.8], 1, 0);
}

float button_timer;
void(float index, float type) Button_Click = 
{
	// don't click if not active
	if(buttons[index].active == -1)
		return;

	// also don't click if it's not on this platform
	if (platform_is_web && buttons[index].platform_dependency == OPTION_EXE_ONLY)
		return;
	if (!platform_is_web && buttons[index].platform_dependency == OPTION_WEB_ONLY)
		return;
		
	vector pos = buttons[index].pos;

	if (pos_y == 0)
		pos_y = g_height - 22;
	else if (pos_y == -1)
		pos_y = g_height - 75;
	else if (pos_y == -2)
		pos_y = g_height - 95;
	
	if (cursor_pos_x > pos_x && cursor_pos_x < pos_x + 300 && cursor_pos_y > pos_y && cursor_pos_y < pos_y + 14) {
		if (button_timer > cltime)
			return;

		if (type == 0) { //left click
			buttons[index].action();
		} else { //right click
			buttons[index].action2();
		}
		
		lastActive = -1;
		localsound_enhanced("sounds/menu/enter.wav", CHAN_SFX, 1);
		button_timer = cltime + 0.25;
	}

	if (in_menu == MENU_CUSTOMS)
		customs_interact = FALSE;
};


void(float type) Menu_Click =
{
	float i;
	for(i = 0; i < BUTTONS_COUNT; i++)
	{
		Button_Click(i, type);
	}
};

void() Menu_Click_Custom =
{
	customs_interact = TRUE;
};

void() Draw_Extra_Main =
{
	string main_desc = "";

	// Division lines
	drawfill ([6, 115], [270, 4], [0.5, 0.5, 0.5], 1, 0);
	drawfill ([6, 165], [270, 4], [0.5, 0.5, 0.5], 1, 0);
	
	if (platform_is_web == false)
		drawfill ([6, 195], [270, 4], [0.5, 0.5, 0.5], 1, 0);

	// Get description for menu option
	switch(lastActive) {
		case 0:
			main_desc = "Take on the Hordes by yourself.";
			break;
		case 1:
			main_desc = "Play NZ:P Cooperatively with up to four Players.";
			break;
		case 2:
			main_desc = "Adjust your Settings to Optimize your Experience.";
			break;
		case 3:
			main_desc = "View locked or unlocked Achievements.";
			break;
		case 4:
			main_desc = "See who made NZ:P possible.";
			break;
		case 5:
			main_desc = "Return to System.";
			break;
		default:
			main_desc = "";
			break;
	}

	// Draw desc
	drawstring([6, g_height - 42, 0], main_desc, [12, 12], [1, 1, 1], 1, 0);

	// Social Links
	// Discord doesn't allow permanant invites anymore, "bummer"..
	//drawsubpic([g_width - 32, g_height - 32], [28, 28], "gfx/menu/social.tga", [0, 0], [0.5, 0.5], [1, 1, 1], 1); // Discord
	drawsubpic([g_width - 32, g_height - 32], [28, 28], "gfx/menu/social.tga", [0.5, 0], [0.5, 0.5], [1, 1, 1], 1); // YouTube
	drawsubpic([g_width - 32, g_height - 64], [28, 28], "gfx/menu/social.tga", [0, 0.5], [0.5, 0.5], [1, 1, 1], 1); // Twitter
	drawsubpic([g_width - 32, g_height - 96], [28, 28], "gfx/menu/social.tga", [0.5, 0.5], [0.5, 0.5], [1, 1, 1], 1); // Patreon

	// Draw text when hovering over specific social icons
	if (cursor_pos_x >= g_width - 32 && cursor_pos_y >= g_height - 96) {
		string advert_text;

		if (cursor_pos_y >= g_height - 32) {
			advert_text = "youtube.com/@nzpteam";
		} else if (cursor_pos_y >= g_height - 64) {
			advert_text = "twitter.com/NZPTeam";
		} else {
			advert_text = "patreon.com/cypressimplex";
		}

		drawfill([cursor_pos_x - (strlen(advert_text) * 10) - 5, cursor_pos_y + 2], [strlen(advert_text) * 10, 10], [0, 0, 0], 0.90);
		drawstring([cursor_pos_x - (strlen(advert_text) * 10) - 5, cursor_pos_y + 2], advert_text, [10, 10], [1, 1, 0], 1, 0);
	}
}

void() Draw_Extra_Join =
{
	string join_desc = "";

	// Server ID
	drawfill ([318, 73], [14 * 19, 18], [0.07, 0.07, 0.07], 0.5, 0);
	drawstring([320, 75], temp_server_name, [14, 14], [1, 1, 1], 1, 0);

	// Draw a cute little guy so user knows they're editing their name.
	if (editing_server_id) {
		drawstring([320 + (strlen(temp_server_name)*14), 75], "_", [14, 14], [1, 1, 0], 1, 0);
	}

	// Password
	// Generate a bunch of asterisks lol
	string safe_password = "";
	for(int i = 0; i < strlen(temp_password); i++) {
		safe_password = sprintf("%s%s", safe_password, "*");
	}
	drawfill ([318, 93], [14 * 19, 18], [0.07, 0.07, 0.07], 0.5, 0);
	drawstring([320, 95], safe_password, [14, 14], [1, 1, 1], 1, 0);

	// Draw a cute little guy for password
	if (editing_password) {
		drawstring([320 + (strlen(temp_password)*14), 95], "_", [14, 14], [1, 1, 0], 1, 0);
	}

	// Descriptions
	switch(lastActive) {
		case 60:
			join_desc = "IP or ID for the Server (typically starts with /).";
			break;
		case 61:
			join_desc = "Password for the Match set by the Host.";
			break;
		case 62:
			join_desc = "Attempt to connect to Game.";
			break;
		default:
			join_desc = "";
			break;
	}

	// Draw desc
	drawstring([6, g_height - 42, 0], join_desc, [12, 12], [1, 1, 1], 1, 0);
}

void() Draw_Extra_Create =
{
	string crea_desc = "";

	// Password
	// Generate a bunch of asterisks lol
	string safe_password = "";
	for(int i = 0; i < strlen(temp_password); i++) {
		safe_password = sprintf("%s%s", safe_password, "*");
	}

	drawfill ([318, 73], [14 * 19, 18], [0.07, 0.07, 0.07], 0.5, 0);
	drawstring([320, 75], safe_password, [14, 14], [1, 1, 1], 1, 0);

	// Draw a cute little guy for password
	if (editing_password) {
		drawstring([320 + (strlen(temp_hostname)*14), 75], "_", [14, 14], [1, 1, 0], 1, 0);
	}

	// hostname
	drawfill ([318, 93], [14 * 19, 18], [0.07, 0.07, 0.07], 0.5, 0);
	drawstring([320, 95], temp_hostname, [14, 14], [1, 1, 1], 1, 0);

	// Draw a cute little guy for hostname
	if (editing_hostname) {
		drawstring([320 + (strlen(temp_hostname)*14), 95], "_", [14, 14], [1, 1, 0], 1, 0);
	}

	// Descriptions
	switch(lastActive) {
		case 63:
			crea_desc = "Enter a password to limit who can join.";
			break;
		case 64:
			crea_desc = "Enter a name that will appear in the Server List.";
			break;
		case 65:
			crea_desc = "Select a Map to start the Game.";
			break;
		default:
			crea_desc = "";
			break;
	}

	// Draw desc
	drawstring([6, g_height - 42, 0], crea_desc, [12, 12], [1, 1, 1], 1, 0);
}

void() Draw_Extra_Coop =
{
	string coop_desc = "";

	// Player Name
	drawfill ([318, 73], [14 * 19, 18], [0.07, 0.07, 0.07], 0.5, 0);
	drawstring([320, 75], temp_player_name, [14, 14], [1, 1, 1], 1, 0);

	// Draw a cute little guy so user knows they're editing their name.
	if (editing_player_name) {
		drawstring([320 + (strlen(temp_player_name)*14), 75], "_", [14, 14], [1, 1, 0], 1, 0);
	}

	// Division lines
	drawfill ([6, 100], [270, 4], [0.5, 0.5, 0.5], 1, 0);

	// Descriptions
	switch(lastActive) {
		case 57:
			coop_desc = "Name that appears in-game and on the Scoreboard.";
			break;
		case 58:
			coop_desc = "Join an in-progress Match.";
			break;
		case 59:
			coop_desc = "Create a new Match for others to join.";
			break;
		default:
			coop_desc = "";
			break;
	}

	// Draw desc
	drawstring([6, g_height - 42, 0], coop_desc, [12, 12], [1, 1, 1], 1, 0);
}

void() Draw_Extra_Solo =
{
	string solo_desc = "";
	string solo_desc2 = "";
	string solo_desc3 = "";
	string solo_desc4 = "";
	string solo_desc5 = "";
	string solo_desc6 = "";
	string solo_desc7 = "";
	string solo_desc8 = "";
	string solo_img = "";

	// Division lines
	drawfill ([6, 95], [270, 4], [0.5, 0.5, 0.5], 1, 0);
	drawfill ([6, 185], [270, 4], [0.5, 0.5, 0.5], 1, 0);

	// Map descriptions
	switch(lastActive) {
		case 6:
			solo_desc = "Desolate bunker located on a Ge-";
			solo_desc2 = "rman airfield, stranded after a";
			solo_desc3 = "brutal plane crash surrounded by";
			solo_desc4 = "hordes of undead. Exploit myste-";
			solo_desc5 = "rious forces at play and hold o-";
			solo_desc6 = "ut against relentless waves. Der";
			solo_desc7 = "Anstieg ist jetzt. Will you fall";
			solo_desc8 = "to the overwhelming onslaught?";
			solo_img = "gfx/menu/nacht_der_untoten.png";
			break;
		case 7:
			solo_desc = "Four nameless marines find them-";
			solo_desc2 = "selves at a forsaken warehouse,";
			solo_desc3 = "or is it something more? Fight";
			solo_desc4 = "your way to uncovering its sec-";
			solo_desc5 = "rets, though you may not like";
			solo_desc6 = "what you find..";
			solo_img = "gfx/menu/nzp_warehouse2.png";
			break;
		case 8:
			solo_desc = "Old Warehouse full of Zombies!";
			solo_desc2 = "Fight your way to the Power";
			solo_desc3 = "Switch through the Hordes!";
			solo_img = "gfx/menu/nzp_warehouse.png";
			break;
		case 10:
			solo_desc = "No Santa this year. Though we're";
			solo_desc2 = "sure you will get presents from";
			solo_desc3 = "the undead! Will you accept them?";
			solo_img = "gfx/menu/christmas_special.png";
			break;
		case 11:
			solo_desc = "Custom Maps made by Community on";
			solo_desc2 = "GitHub Discussions!";
			solo_img = "gfx/menu/custom.png";
			break;
		default:
			solo_desc = "";
			solo_img = "";
			break;
	}

	// Map img
	if (solo_img != "")
		drawpic([g_width - 340 - 6, 75], solo_img, [300, 170], [1, 1, 1], 1);

	// Draw desc
	drawstring([g_width - 360, 250, 0], solo_desc, [11, 11], [1, 1, 1], 1, 0);
	drawstring([g_width - 360, 265, 0], solo_desc2, [11, 11], [1, 1, 1], 1, 0);
	drawstring([g_width - 360, 280, 0], solo_desc3, [11, 11], [1, 1, 1], 1, 0);
	drawstring([g_width - 360, 295, 0], solo_desc4, [11, 11], [1, 1, 1], 1, 0);
	drawstring([g_width - 360, 310, 0], solo_desc5, [11, 11], [1, 1, 1], 1, 0);
	drawstring([g_width - 360, 325, 0], solo_desc6, [11, 11], [1, 1, 1], 1, 0);
	drawstring([g_width - 360, 340, 0], solo_desc7, [11, 11], [1, 1, 1], 1, 0);
	drawstring([g_width - 360, 355, 0], solo_desc8, [11, 11], [1, 1, 1], 1, 0);
}

void() Draw_Extra_Settings =
{
	string set_desc = "";

	// Descriptions
	switch(lastActive) {
		case 19:
			set_desc = "Adjust settings relating to Graphical Fidelity.";
			break;
		case 20:
			set_desc = "Customize your Control Scheme.";
			break;
		case 21:
			set_desc = "Adjust settings in relation to how NZ:P Controls.";
			break;
		case 22:
			set_desc = "Adjust Audio Levels.";
			break;
		default:
			set_desc = "";
			break;
	}

	// Draw desc
	drawstring([6, g_height - 42, 0], set_desc, [12, 12], [1, 1, 1], 1, 0);
}

void() Draw_Extra_Credits = 
{
	drawstring([6, 65], "Programming:", [12, 12], TEXT_ORANGE, 1, 0);
	drawstring([6, 85], "Blubs, Jukki, DR_Mabuse1981, Naievil", [12, 12], [1, 1, 1], 1, 0);
	drawstring([6, 105], "Cypress, ScatterBox", [12, 12], [1, 1, 1], 1, 0);

	drawstring([6, 130], "Models:", [12, 12], TEXT_ORANGE, 1, 0);
	drawstring([6, 150], "Blubs, Ju[s]tice, Derped_Crusader", [12, 12], [1, 1, 1], 1, 0);

	drawstring([6, 175], "GFX:", [12, 12], TEXT_ORANGE, 1, 0);
	drawstring([6, 195], "Blubs, Ju[s]tice, Cypress, Derped_Crusader", [12, 12], [1, 1, 1], 1, 0);

	drawstring([6, 220], "Sounds/Music:", [12, 12], TEXT_ORANGE, 1, 0);
	drawstring([6, 240], "Blubs, Biodude, Cypress, Marty P.", [12, 12], [1, 1, 1], 1, 0);

	drawstring([6, 265], "Special Thanks:", [12, 12], TEXT_ORANGE, 1, 0);
	drawstring([6, 285], "- Spike, Eukara:     FTEQW", [12, 12], [1, 1, 1], 1, 0);
	drawstring([6, 305], "- Shpuld:            CleanQC4FTE", [12, 12], [1, 1, 1], 1, 0);
	drawstring([6, 325], "- Crow_Bar, st1x51:  dQuake(plus)", [12, 12], [1, 1, 1], 1, 0);
	drawstring([6, 345], "- fgsfdsfgs:         Quakespasm-NX", [12, 12], [1, 1, 1], 1, 0);
	drawstring([6, 365], "- MasterFeizz:       ctrQuake", [12, 12], [1, 1, 1], 1, 0);
	drawstring([6, 385], "- Rinnegatamante:    Initial VITA Port & Updater", [12, 12], [1, 1, 1], 1, 0);
}

void() Draw_Extra_Restart =
{
	drawstring([0.025*g_width, 0.375*g_height, 0], "Are you sure you want to restart?", [g_height * 0.020, g_height * 0.020, 1], [0.8,0.8,0.8], 1, 0);
	drawstring([0.02*g_width, 0.425*g_height, 0], "Your current progress will be lost!", [g_height * 0.020, g_height * 0.020, 1], [0.8,0.8,0.8], 1, 0);
}

void() Draw_Extra_CSettings =
{
	string cset_desc;
	cset_desc = "";

	// ADS Mode
	if (cvar("cl_adsmode") == 0)
		drawstring([320, 75], "HOLD", [14, 14], [1, 1, 1], 1, 0);
	else
		drawstring([320, 75], "TOGGLE", [14, 14], [1, 1, 1], 1, 0);

	// Look Sensitivity
	drawstring([320, 95], ftos(cvar("sensitivity")), [14, 14], [1, 1, 1], 1, 0);


	// Descriptions
	switch(lastActive) {
		case 54:
			cset_desc = "Switch between Toggle/Hold to Aim Down Sight.";
			break;
		case 55:
			cset_desc = "Change Look Sensitivity to adjust responsiveness.";
			break;
		default:
			cset_desc = "";
			break;
	}

	// Division lines
	drawfill ([6, g_height - 85], [270, 4], [0.5, 0.5, 0.5], 1, 0);

	// Draw desc
	drawstring([6, g_height - 42, 0], cset_desc, [12, 12], [1, 1, 1], 1, 0);
}

void() Draw_Extra_GSettings =
{
	local string fpsval;
	local string vsyncval;
	local string decalval;
	local string partval;
	local string fullval;
	local string retroval;
	string ultraval;
	local string sval;
	local string arval = "";
	local string resval;
	local string gset_desc = "";

	if (platform_is_web == false) {
		// Resolution
		resval = strcat(ftos(active_swidth), "x", ftos(active_sheight));
	
		// Aspect Ratio
		switch(aspectratio) {
			case 0:
				arval = "4:3";
				break;
			case 1:
				arval = "5:4";
				break;
			case 2:
				arval = "16:9";
				break;
			case 3:
				arval = "16:10";
				break;
			case 4:
				arval = "21:9";
				break;
			default:
				arval = "ERR";
				break;
		}
		drawstring([320, 115], arval, [14, 14], [1, 1, 1], 1, 0);
	} else {
		// Render Scale
		resval = strcat(itos(ftoi(cvar("r_renderscale")*100)), "%");
	}

	drawstring([320, 95], resval, [14, 14], [1, 1, 1], 1, 0);
	
	// Fullscreen
	if (fullscreenval == 0)
		sval = S_DISABLED;
	else
		sval = S_ENABLED;

	drawstring([320, 135], sval, [14, 14], [1, 1, 1], 1, 0);

	// Show FPS
	if (cvar("show_fps") == 0)
		fpsval = S_DISABLED;
	else
		fpsval = S_ENABLED;

	drawstring([320, 155], fpsval, [14, 14], [1, 1, 1], 1, 0);

	// Max FPS
	// can't adjust when vsync is enabled, so grey out
	if (!cvar("vid_vsync")) {
		drawstring([320, 175], ftos(cvar("cl_maxfps")), [14, 14], [1, 1, 1], 1, 0);
	} else { 
		drawstring([320, 175], ftos(cvar("cl_maxfps")), [14, 14], [0.25, 0.25, 0.25], 1, 0);
	}

	// VSync
	if (platform_is_web == false) {
		if (cvar("vid_vsync") == 0)
			vsyncval = S_DISABLED;
		else
			vsyncval = S_ENABLED;

		drawstring([320, 195], vsyncval, [14, 14], [1, 1, 1], 1, 0);
	}

	// Field of View
	drawstring([320, 215], ftos(cvar("fov")), [14, 14], [1, 1, 1], 1, 0);

	// Brightness
	// have to use a drawfill because of how gamma values are subtracted.. for some reason
	drawfill ([320, 235], [cvar("gamma")/3*200, 14], [0.8, 0.8, 0.8], 1, 0);

	// Limit X Bounds
	if (cvar("vid_ultrawide_limiter"))
		ultraval = S_ENABLED;
	else
		ultraval = S_DISABLED;

	drawstring([320, 255], ultraval, [14, 14], [1, 1, 1], 1, 0);

	// Particles
	if (cvar("nzp_particles") == 0)
		partval = S_DISABLED;
	else
		partval = S_ENABLED;

	drawstring([320, 275], partval, [14, 14], [1, 1, 1], 1, 0);

	// Fullbright
	if (cvar("r_fullbright") == 0)
		fullval = S_DISABLED;
	else
		fullval = S_ENABLED;

	drawstring([320, 295], fullval, [14, 14], [1, 1, 1], 1, 0);

	// Retro
	if (cvar_string("gl_texturemode") == "gl_nearest")
		retroval = S_ENABLED;
	else
		retroval = S_DISABLED;

	drawstring([320, 315], retroval, [14, 14], [1, 1, 1], 1, 0);

	// Descriptions
	switch(lastActive) {
		case 24:
			gset_desc = "Adjust Window Resolution.";
			break;
		case 25:
			gset_desc = "Change Window Aspect Ratio.";
			break;
		case 26:
			if (platform_is_web == false)
				gset_desc = "Toggle Fullscreen (May fail depending on GPU).";
			else
				gset_desc = "Put Browser in Fullscreen when loaded in World.";
			break;
		case 27:
			gset_desc = "Toggle Framerate Overlay.";
			break;
		case 28:
			gset_desc = "Increase of Decrease Max Frames per Second.";
			break;
		case 29:
			gset_desc = "Toggle Vertical Sync.";
			break;
		case 30:
			gset_desc = "Adjust Game Field of View.";
			break;
		case 31:
			gset_desc = "Increase or Decrease Game Brightness.";
			break;
		case 32:
			gset_desc = "Makes HUD more visible when Ultrawide.";
			break;
		case 33:
			gset_desc = "Toggle Appearence of (most) Particles.";
			break;
		case 34:
			gset_desc = "Toggle all non-realtime lights (Requires Map Restart).";
			break;
		case 35:
			gset_desc = "Toggle texture filtering.";
			break;
		case 23:
			gset_desc = "Adjust scale factor for 3D rendering.";
			break;
		default:
			gset_desc = "";
			break;
	}

	// Division lines
	drawfill ([6, g_height - 85], [270, 4], [0.5, 0.5, 0.5], 1, 0);

	// Draw desc
	drawstring([6, g_height - 42, 0], gset_desc, [12, 12], [1, 1, 1], 1, 0);
}

void() Draw_Extra_Controls =
{
	local string buttoncon[MAX_BINDS];
	local string binding = "_";

	//prevent warning
	buttoncon[0] = "";

	for (int i = 0; i < MAX_BINDS; i += 1) {
		tokenize(findkeysforcommand(buttonBind[i]));

		if (editBind[i] == FALSE)
			buttoncon[i] = strtoupper(keynumtostring(stof(argv(0))));
		else
			buttoncon[i] = binding;

		if (buttoncon[i] == "01")
			buttoncon[i] = "UNBOUND";

		drawstring([320, 75+(20*i)], buttoncon[i], [g_height * 0.030, g_height * 0.030, 1], [0.8,0.8,0.8], 1, 0);
	}
}

// remove prefix and suffix from custom map line
// (maps/) & (.bsp)
string(string s) strip_custominfo =
{
	string s2;
	s2 = substring(s, 5, strlen(s)); // maps/
	s2 = substring(s2, 0, strlen(s2) - 4); // .bsp

	return s2;
}

var struct
{
	float 	occupied;
	float 	map_allow_game_settings;
	float 	map_use_thumbnail;
	string 	map_name;
	string 	map_name_pretty;
	string 	map_desc_1;
	string 	map_desc_2;
	string 	map_desc_3;
	string 	map_desc_4;
	string 	map_desc_5;
	string 	map_desc_6;
	string 	map_desc_7;
	string 	map_desc_8;
	string 	map_author;
} custom_maps[500];

void() Customs_Get =
{
	searchhandle 	maps;
	float 			map_count;

	maps 		= search_begin("maps/*.bsp", 0, 0);
	map_count 	= search_getsize(maps);

	custom_map_pages = ceil(map_count/10);

	for (float i = 0; i < map_count; i++) {
		custom_maps[i].occupied = TRUE;
		custom_maps[i].map_name = strip_custominfo(search_getfilename(maps, i));

		float settings = fopen(strcat("maps/", custom_maps[i].map_name, ".txt"), FILE_READ);
		if (settings != -1) {
			custom_maps[i].map_name_pretty = fgets(settings);
			custom_maps[i].map_desc_1 = fgets(settings);
			custom_maps[i].map_desc_2 = fgets(settings);
			custom_maps[i].map_desc_3 = fgets(settings);
			custom_maps[i].map_desc_4 = fgets(settings);
			custom_maps[i].map_desc_5 = fgets(settings);
			custom_maps[i].map_desc_6 = fgets(settings);
			custom_maps[i].map_desc_7 = fgets(settings);
			custom_maps[i].map_desc_8 = fgets(settings);
			custom_maps[i].map_author = fgets(settings);
			custom_maps[i].map_use_thumbnail = stof(fgets(settings));
			custom_maps[i].map_allow_game_settings = stof(fgets(settings));
			fclose(settings);
		}
		
	}

	current_custom_map_page = 1;

	search_end(maps);
}

void() Draw_Extra_Customs =
{
	string 	custom_map_names[10];
	float 	custom_map_active[10];
	float 	multiplier;

	if (current_custom_map_page > 1)
		multiplier = (current_custom_map_page - 1) * 10;
	else
		multiplier = 0;

	for (float i = 0; i < 10; i++) {
		if (custom_maps[i + multiplier].occupied) {
			custom_map_names[i] = custom_maps[i + multiplier].map_name;
		}
	}

	for (float i = 0; i < 10; i++) {
		if (cursor_pos_x > 6 && cursor_pos_x < 320 && 
			cursor_pos_y > 75 + (20 * i) && cursor_pos_y < 75 + (20 * i) + 14 && button_timer <= cltime) {

			if (i != last_active_custom_select)
				localsound_enhanced("sounds/menu/navigate.wav", CHAN_SFX, 1);

			if (customs_interact) {
				loadscreen_timetrigger = cltime + 0.6;
				loadscreen_mapname = custom_map_names[i];
				loadscreen_maptitle = custom_maps[i + multiplier].map_name_pretty;
			}
			
			if (last_active_custom_select != -1)
				custom_map_active[last_active_custom_select] = FALSE;
			custom_map_active[i] = TRUE;
			last_active_custom_select = i;
			lastActive = -1;
		}
	}

	for (float i = 0; i < 10; i++) {
		if (custom_map_active[i] == TRUE || last_active_custom_select == i) {
			if (custom_maps[i + multiplier].map_name_pretty)
				drawstring([6, 75+(20*i)], custom_maps[i + multiplier].map_name_pretty, [14, 14], [1, 0, 0], 1, 0);
			else
				drawstring([6, 75+(20*i)], custom_map_names[i], [14, 14], [1, 0, 0], 1, 0);

			float line_increment;
			line_increment = 0;

			if (custom_maps[i + multiplier].map_desc_1 != "" && custom_maps[i + multiplier].map_desc_1 != " ") {
				drawstring([g_width - 360, 250, 0], custom_maps[i + multiplier].map_desc_1, [11, 11], [1, 1, 1], 1, 0);
			}
			if (custom_maps[i + multiplier].map_desc_2 != "" && custom_maps[i + multiplier].map_desc_2 != " ") {
				line_increment++;
				drawstring([g_width - 360, 265, 0], custom_maps[i + multiplier].map_desc_2, [11, 11], [1, 1, 1], 1, 0);
			}
			if (custom_maps[i + multiplier].map_desc_3 != "" && custom_maps[i + multiplier].map_desc_3 != " ") {
				line_increment++;
				drawstring([g_width - 360, 280, 0], custom_maps[i + multiplier].map_desc_3, [11, 11], [1, 1, 1], 1, 0);
			}
			if (custom_maps[i + multiplier].map_desc_4 != "" && custom_maps[i + multiplier].map_desc_4 != " ") {
				line_increment++;
				drawstring([g_width - 360, 295, 0], custom_maps[i + multiplier].map_desc_4, [11, 11], [1, 1, 1], 1, 0);
			}
			if (custom_maps[i + multiplier].map_desc_5 != "" && custom_maps[i + multiplier].map_desc_5 != " ") {
				line_increment++;
				drawstring([g_width - 360, 310, 0], custom_maps[i + multiplier].map_desc_5, [11, 11], [1, 1, 1], 1, 0);
			}
			if (custom_maps[i + multiplier].map_desc_6 != "" && custom_maps[i + multiplier].map_desc_6 != " ") {
				line_increment++;
				drawstring([g_width - 360, 325, 0], custom_maps[i + multiplier].map_desc_6, [11, 11], [1, 1, 1], 1, 0);
			}
			if (custom_maps[i + multiplier].map_desc_7 != "" && custom_maps[i + multiplier].map_desc_7 != " ") {
				line_increment++;
				drawstring([g_width - 360, 340, 0], custom_maps[i + multiplier].map_desc_7, [11, 11], [1, 1, 1], 1, 0);
			}
			if (custom_maps[i + multiplier].map_desc_8 != "" && custom_maps[i + multiplier].map_desc_8 != " ") {
				line_increment++;
				drawstring([g_width - 360, 355, 0], custom_maps[i + multiplier].map_desc_8, [11, 11], [1, 1, 1], 1, 0);
			}
			if (custom_maps[i + multiplier].map_author != "" && custom_maps[i + multiplier].map_author != " ") {
				line_increment++;
				drawstring([g_width - 360, 235 + (20*line_increment), 0], custom_maps[i + multiplier].map_author, [11, 11], TEXT_ORANGE, 1, 0);
			}

			if (custom_maps[i + multiplier].map_use_thumbnail) {
				drawpic([g_width - 340 - 6, 75], strcat("menu/custom/", custom_map_names[i], ".png"), [300, 170], [1, 1, 1], 1);
			}

		} else {
			if (custom_maps[i + multiplier].map_name_pretty)
				drawstring([6, 75+(20*i)], custom_maps[i + multiplier].map_name_pretty, [14, 14], [1, 1, 1], 1, 0);
			else
				drawstring([6, 75+(20*i)], custom_map_names[i], [14, 14], [1, 1, 1], 1, 0);
		}
			
	}

	customs_interact = FALSE;
}

void() Draw_Extra_Achievements =
{
	float multiplier;

	if (current_achievement_page > 1) {
		multiplier = (current_achievement_page - 1) * 3;
	} else {
		multiplier = 0;
		back_gray_out = true;
	}

	for (float i = 0; i < 3; i++) {
		if (i + multiplier > MAX_ACHIEVEMENTS - 1)
			continue;

		string name1, name2;
		string desc1, desc2, desc3, desc4;
		name2 = "";
		desc2 = "";
		desc3 = "";
		desc4 = "";

		if (strlen(achievements[i + multiplier].name) >= 33) {
			name1 = substring(achievements[i + multiplier].name, 0, 33);
			name2 = substring(achievements[i + multiplier].name, 33, strlen(achievements[i + multiplier].name));
		} else {
			name1 = achievements[i + multiplier].name;
		}

		if (substring(name2, 0, 1) == " ")
			name2 = substring(name2, 1, strlen(name2));

		desc1 = substring(achievements[i + multiplier].description, 0, 36);
		desc2 = substring(achievements[i + multiplier].description, 36, 34);
		desc3 = substring(achievements[i + multiplier].description, 70, 35);
		desc4 = substring(achievements[i + multiplier].description, 105, strlen(achievements[i + multiplier].description));

		if (substring(desc2, 0, 1) == " ")
			desc2 = substring(desc2, 1, strlen(desc2));
		
		if (substring(desc3, 0, 1) == " ")
			desc3 = substring(desc3, 1, strlen(desc3));

		if (substring(desc4, 0, 1) == " ")
			desc4 = substring(desc4, 1, strlen(desc4));

		if (achievements[i + multiplier].unlocked == false) {
			int lock_width_pos;

			drawstring([0.450*g_height, (0.150 + (0.212 * i))*g_height, 0], name1, [g_height * 0.030, g_height * 0.030, 1], [0.5,0.5,0.5], 1, 0);
			drawstring([0.450*g_height, (0.180 + (0.212 * i))*g_height, 0], name2, [g_height * 0.030, g_height * 0.030, 1], [0.5,0.5,0.5], 1, 0);

			if (name2 == "") {
				lock_width_pos = stringwidth(name1, 0, [g_height * 0.030, g_height * 0.030, 1]);
				drawstring([0.450*g_height + lock_width_pos, (0.150 + (0.212 * i))*g_height, 0], " (LOCKED)", [g_height * 0.030, g_height * 0.030, 1], [1,0,0], 1, 0);
			} else {
				lock_width_pos = stringwidth(name2, 0, [g_height * 0.030, g_height * 0.030, 1]);
				drawstring([0.450*g_height + lock_width_pos, (0.180 + (0.212 * i))*g_height, 0], " (LOCKED)", [g_height * 0.030, g_height * 0.030, 1], [1,0,0], 1, 0);
			}

			drawstring([0.450*g_height, (0.210 + (0.212 * i))*g_height, 0], desc1, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0);
			drawstring([0.450*g_height, (0.240 + (0.212 * i))*g_height, 0], desc2, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0);
			drawstring([0.450*g_height, (0.270 + (0.212 * i))*g_height, 0], desc3, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0);
			drawstring([0.450*g_height, (0.300 + (0.212 * i))*g_height, 0], desc4, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0);

			drawpic([0.025*g_width, (0.150 + (0.212 * i))*g_height, 0], "gfx/achievement/achievement_locked.tga", [0.38*g_height, 0.19*g_height, 0], [1,1,1], 1);
		} else {
			drawstring([0.450*g_height, (0.150 + (0.212 * i))*g_height, 0], name1, [g_height * 0.030, g_height * 0.030, 1], [1,1,0], 1, 0);
			drawstring([0.450*g_height, (0.180 + (0.212 * i))*g_height, 0], name2, [g_height * 0.030, g_height * 0.030, 1], [1,1,0], 1, 0);

			drawstring([0.450*g_height, (0.210 + (0.212 * i))*g_height, 0], desc1, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0);
			drawstring([0.450*g_height, (0.240 + (0.212 * i))*g_height, 0], desc2, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0);
			drawstring([0.450*g_height, (0.270 + (0.212 * i))*g_height, 0], desc3, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0);
			drawstring([0.450*g_height, (0.300 + (0.212 * i))*g_height, 0], desc4, [g_height * 0.030, g_height * 0.030, 1], [1,1,1], 1, 0);

			drawpic([0.025*g_width, (0.150 + (0.212 * i))*g_height, 0], achievements[i + multiplier].img, [0.38*g_height, 0.19*g_height, 0], [1,1,1], 1);
		}
	}
}

void() Draw_Extra_ASettings =
{
	string aset_desc = "";
	float master_volume = cvar("volume");
	float chann1_volume = cvar("snd_channel1volume");
	float chann2_volume = cvar("snd_channel2volume");
	float chann3_volume = cvar("snd_channel3volume");
	float chann4_volume = cvar("snd_channel4volume");
	float chann5_volume = cvar("snd_channel5volume");
	float chann6_volume = cvar("snd_channel6volume");

	// Master Volume
	drawfill ([320, 75], [master_volume/3*200, 14], [0.8, 0.8, 0.8], 1, 0);

	// Descriptions
	switch(lastActive) {
		case 56:
			aset_desc = "Adjusts the overall volume of the game.";
			break;
		default:
			aset_desc = "";
			break;
	}

	// Division lines
	drawfill ([6, g_height - 85], [270, 4], [0.5, 0.5, 0.5], 1, 0);

	// Draw desc
	drawstring([6, g_height - 42, 0], aset_desc, [12, 12], [1, 1, 1], 1, 0);
}

float menu_sound_length;
float menu_sound_time;

string() LoadScreen_GiveTip;
void() Draw_Menu =
{
	// CSQC likes to take us back and forth between UpdateView and UpdateViewLoading,
	// meaning normally you'd see the menu pop in and out of scene. We add a delay
	// before the loading sequence and make sure to draw the first load screen "frame",
	// so when this back and forth shenanigan happens, the client will not notice
	// this stupid hack.
	if (loadscreen_timetrigger) {
		if (!loadscreen_tip)
			loadscreen_tip = LoadScreen_GiveTip();

		drawpic([0, 0], strcat("gfx/lscreen/", loadscreen_mapname), [g_width, g_height], [1, 1, 1], 1);

		drawfill ([0, 0], [g_width, 32, 1], [0, 0, 0], 0.68, 0);
		drawfill ([0, g_height - 32], [g_width, 32, 1], [0, 0, 0], 0.68, 0);

		drawstring([g_width/2 - (stringwidth(loadscreen_tip, 0, [12, 12])/2), g_height - 16, 0], loadscreen_tip, [12, 12], [1, 1, 1], 1, 0);
		drawstring([6, 6], loadscreen_maptitle, [24, 24], TEXT_ORANGE, 1, 0);

		drawfill ([g_width/2 - 160, g_height - 42], [320, 18, 1], [0.27, 0.27, 0.27], 1, 0);
		drawfill ([g_width/2 - 164, g_height - 46], [328, 26, 1], [0, 0, 0], 0.77, 0);
		drawstring([g_width/2 - (stringwidth("Loading...", 0, [14, 14])/2), g_height - 40, 0], "Loading...", [14, 14], [1, 1, 1], 1, 0);

		if (loadscreen_timetrigger < cltime) {
			in_loadscreen = true;
		}
		return;
	}

	// main menu theme iterating
	if (menu_initialized == false && in_menu == MENU_MAIN) {
		localsound_enhanced("tracks/tensioned_by_the_damned.ogg", CHAN_MUSIC, 1);
		menu_sound_length = 490;
		menu_sound_time = 0;
		menu_initialized = true;
	}

	// restart the track if it has ended
	if (menu_initialized == true && (menu_sound_time >= menu_sound_length - 1)) {
		localsound_enhanced("tracks/tensioned_by_the_damned.ogg", CHAN_MUSIC, 1);
		menu_sound_time = 0;
	}

	menu_sound_time += frametime;
	
	// background
	if(serverkey("constate") == "disconnected")
	{
		drawpic([0,0,0], "menu/bg", [g_width, g_height, 1], [1, 1, 1], 1);
	}

	// Darken the BG
	drawfill ([0, 0, 0], [g_width, g_height, 1], [0, 0, 0], 0.35, 0);

	// version string
	if (in_menu == MENU_MAIN) {
		string vers_string;

		vers_string = build_datetime;

		drawstring([(g_width - 6 - stringwidth(vers_string, 0, [12, 12])), 8], vers_string, [12, 12], [1, 1, 1], 1, 0);
	}

	//menu title
	local string title = "";
	switch(in_menu)
	{
		case MENU_MAIN:
			title = "MAIN MENU";
			Draw_Extra_Main();
			break;
		case MENU_SINGLE:
			if (matchmake_enabled)
				title = "CHOOSE MAP";
			else
				title = "SOLO";
			Draw_Extra_Solo();
			break;
		case MENU_MULTI:
			title = "COOPERATIVE";
			Draw_Extra_Coop();
			break;
		case MENU_ABOUT:
			title = "CREDITS";
			Draw_Extra_Credits();
			break;
		case MENU_PAUSE:
			title = "PAUSED";
			break;
		case MENU_IGS:
		case MENU_SETTINGS:
			title = "SETTINGS";
			Draw_Extra_Settings();
			break;
		case MENU_GSETTINGS:
			title = "GRAPHICS SETTINGS";
			Draw_Extra_GSettings();
			break;
		case MENU_CSETTINGS:
			title = "CONTROLS";
			Draw_Extra_Controls();
			break;
		case MENU_RES:
			title = "RESTART";
			Draw_Extra_Restart();
			break;
		case MENU_CUSTOMS:
			if (matchmake_enabled)
				title = "CHOOSE CUSTOM MAP";
			else
				title = "CUSTOM MAPS";
			Draw_Extra_Customs();
			break;
        case MENU_ACHIEVEMENTS:
            title = "ACHIEVEMENTS";
            Draw_Extra_Achievements();
            break;
		case MENU_CONSETTINGS:
			title = "CONTROL SETTINGS";
			Draw_Extra_CSettings();
			break;
		case MENU_AUDSETTINGS:
			title = "AUDIO SETTINGS";
			Draw_Extra_ASettings();
			break;
		case MENU_JOIN:
			title = "JOIN GAME";
			Draw_Extra_Join();
			break;
		case MENU_CREATE:
			title = "CREATE GAME";
			Draw_Extra_Create();
			break;
		default:
			title = "Nazi Zombies: Portable";
	}
	
	drawstring([6, 8, 0], title, [28, 28, 0], [1, 1, 1], 1, 0);

	//Update buttons
	local float i;

	for(i = 0; i < BUTTONS_COUNT; i++)
	{
		Update_Button(i);
	}

	// TODO - Controller Support
	/*if (cvar("in_xinput")) {
		Controller_UpdateButton();
	}*/
};
