From d6548549538301363ed50da9f715cffb1236e4c5 Mon Sep 17 00:00:00 2001
From: Spoike <acceptthis@users.sourceforge.net>
Date: Mon, 13 Jul 2020 21:11:22 +0000
Subject: [PATCH] Try to make menusys a little more QSS-friendly.

git-svn-id: https://svn.code.sf.net/p/fteqw/code/trunk@5727 fc73d0e0-1445-4013-8a0c-d673dee63da5
---
 quakec/menusys/menu.src                     |  13 +++
 quakec/menusys/menu/options.qc              |   7 +-
 quakec/menusys/menu/options_audio.qc        | 109 ++++++++++++--------
 quakec/menusys/menu/options_basic.qc        |  90 ++++++++++++----
 quakec/menusys/menu/options_effects.qc      |  94 ++++++++++++-----
 quakec/menusys/menu/options_particles.qc    |   7 +-
 quakec/menusys/menu/options_video.qc        |  22 ++--
 quakec/menusys/menusys/mitem_colours.qc     |  51 +++++++--
 quakec/menusys/menusys/mitem_desktop.qc     |  24 ++---
 quakec/menusys/menusys/mitem_spinnymodel.qc |   6 +-
 10 files changed, 300 insertions(+), 123 deletions(-)

diff --git a/quakec/menusys/menu.src b/quakec/menusys/menu.src
index 8f7629e76..d772b9e3b 100644
--- a/quakec/menusys/menu.src
+++ b/quakec/menusys/menu.src
@@ -1,5 +1,6 @@
 #pragma progs_dat "../menu.dat"
 
+
 //#pragma target fte
 
 #define MENU		//select the module
@@ -21,6 +22,11 @@ menusys/mitem_bind.qc		//key binding thingie
 menusys/mitem_spinnymodel.qc	//menu art
 #endlist
 
+enum
+{
+	E_FTE,
+	E_QSS,
+} engine; //For use ONLY with known possible cvar values.
 
 
 //might as well put this here.
@@ -112,6 +118,12 @@ var float autocvar_dp_workarounds_allow = TRUE;
 var float autocvar_dp_workarounds_force = FALSE;
 void() m_init =
 {
+	string e = cvar_string("pr_engine");
+	if (!strncmp(e, "QSS ", 4))
+		engine = E_QSS;
+	else
+		engine = E_FTE;
+
 	desktop = spawn(mitem_desktop);
 
 	if (checkbuiltin(registercommand))
@@ -166,3 +178,4 @@ void(string cstr) GameCommand =
 	}
 	items_updategrabs(TRUE);
 };
+
diff --git a/quakec/menusys/menu/options.qc b/quakec/menusys/menu/options.qc
index bf0ae9969..d00bb50e2 100644
--- a/quakec/menusys/menu/options.qc
+++ b/quakec/menusys/menu/options.qc
@@ -48,7 +48,7 @@ nonstatic void(mitem_desktop desktop) M_Options =
 		{fr.add(spawn(mitem_text, item_text:"Hud",			item_command:"m_pop;m_hud", 		item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);	pos += 16;}
 	if (checkbuiltin2(buf_cvarlist, TRUE))
 		{fr.add(spawn(mitem_text, item_text:"Advanced Guru Settings",	item_command:"m_pop;m_cvars", 		item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);	pos += 16;}
-	if (checkcommand2("cvarreset", FALSE))
+	if (checkcommand2("cvarreset", FALSE) || checkcommand2("resetall", FALSE))
 		{fr.add(spawn(mitem_text, item_text:"Reset to Defaults", 	item_command:"m_reset", 		item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);	pos += 16;}
 	if (checkcommand2("cfg_save", FALSE))
 		{fr.add(spawn(mitem_text, item_text:"Save Settings", 		item_command:"cfg_save", 		item_scale:16, item_flags:IF_CENTERALIGN), fl, [0, pos], [0, 16]);	pos += 16;}
@@ -89,5 +89,8 @@ static void(mitem_desktop desktop, string question, string affirmitive, string a
 
 nonstatic void(mitem_desktop desktop) M_Reset =
 {
-	M_SimplePrompt(desktop, "Really Reset All Settings?", "Yes!", "m_pop;cvarreset *;exec default.cfg", "NOOOO! MY PRECIOUS!!!", "m_pop");
+	string cmd = "m_pop;cvarreset *;exec default.cfg";
+	if (!checkcommand2("cvarreset", FALSE))
+		cmd = "m_pop;resetall;exec default.cfg";
+	M_SimplePrompt(desktop, "Really Reset All Settings?", "Reset All", cmd, "Cancel", "m_pop");
 };
diff --git a/quakec/menusys/menu/options_audio.qc b/quakec/menusys/menu/options_audio.qc
index ae509e472..a9d0d09da 100644
--- a/quakec/menusys/menu/options_audio.qc
+++ b/quakec/menusys/menu/options_audio.qc
@@ -1,5 +1,11 @@
 static class audiomenu : mitem_exmenu
 {
+	virtual float(string key) isvalid =
+	{
+		if (key == "sndspeed")
+			return super::isvalid(key) && stof(super::get("snd_mixspeed"))==44100;
+		return super::isvalid(key);
+	};
 	virtual string(string key) get =
 	{
 		if (key == "s_device" || key == "cl_voip_capturedevice")
@@ -44,55 +50,74 @@ nonstatic void(mitem_desktop desktop) M_Options_Audio =
 	//add the options
 	fr.add(spawn(mitem_text, item_text:_("Restart Sound"), 	item_command:"snd_restart", item_scale:8, item_flags:IF_RIGHTALIGN),		RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [0, pos], [-8, 8]); pos += 8;
 	pos += 8;
-	fr.add(menuitemcombo_spawn(_("Sound Device"),	"s_device",			'280 8', cvar_string("_s_device_opts")),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Master Volume"),	"volume",			'0.0 1 0.1', 		'280 8'),		fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Ambient Volume"),"ambient_level",		'0 0.5 0.05', 		'280 8'),		fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Self Volume"),	"s_localvolume",		'0 1 0.1', 		'280 8'),		fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Music Volume"),	"bgmvolume",			'0 0.5 0.05', 		'280 8'),		fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Channels"),	"s_numspeakers",		'1 6 1', 		'280 8'),		fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcombo_spawn(_("Audio Quality"),	"s_khz",						'280 8', _(
-												"8	\"8000hz (telephone quality)\" "
-												"11.025	\"11025hz (vanilla quake)\" "
-												"22.05	\"22050hz\" "
-												"44.1	\"44100hz (cd quality)\" "
-												"48	\"48000hz (dvd quality)\" "
-												//higher values are probably pointless when source data doesn't go that high, so not going to list them.
-												//"96	\"96000hz (blu-ray quality)\" "
-												//"192	\"192000hz (professional quality)\" "
-																)),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcheck_spawn(_("Doppler"),	"s_doppler", 						'280 8'),		fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcheck_spawn(_("8bit audio"),	"s_loadas8bit", 					'280 8'),		fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcheck_spawn(_("Swap Speakers"),	"s_swapstereo", 					'280 8'),		fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Latency"),	"s_mixahead",			'0.1 0.3 0.01',		'280 8'),		fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcheck_spawn(_("Disable Sound"),	"nosound", 						'280 8'),		fl, [0, pos], [0, 8]); pos += 8;
+	if (engine!=E_QSS)
+	fr.add(menuitemcombo_spawn(_("Sound Device"),		"s_device",							'280 8', cvar_string("_s_device_opts")),	fl, [0, pos], [0, 8]); pos += 8;
+	fr.add(menuitemslider_spawn(_("Master Volume"),		"volume",			'0.0 1 0.1', 	'280 8'),		fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemslider_spawn(_("Ambient Volume"),	"ambient_level",	'0 0.5 0.05', 	'280 8'),		fl, [0, pos], [0, 8]), pos += 8;
+	if (engine!=E_QSS)
+	fr.add(menuitemslider_spawn(_("Self Volume"),		"s_localvolume",	'0 1 0.1', 		'280 8'),		fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemslider_spawn(_("Music Volume"),		"bgmvolume",		'0 0.5 0.05', 	'280 8'),		fl, [0, pos], [0, 8]), pos += 8;
+	if (engine!=E_QSS)
+	fr.add(menuitemcombo_spawn(_("Channels"),			"s_numspeakers",					'280 8', _(
+																"1 \"Mono\" "
+																"2 \"Stereo\" "
+																"4 \"Quad\" "
+																"6 \"Surround\" "
+																)),		fl, [0, pos], [0, 8]), pos += 8;
+	if (engine==E_QSS)
+	fr.add(menuitemcombo_spawn(_("Audio Filtering"),	"sndspeed",	'280 8', _(
+																"\"\"	\"Off\" "
+																"11025	\"On\" "
+																)),	fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemcombo_spawn(_("Audio Quality"),		cv2("snd_mixspeed"/*qs*/, "s_khz"),	'280 8', _(
+																"8000	\"8000hz (telephone quality)\" "
+																"11025	\"11025hz (vanilla quake)\" "
+																"22050	\"22050hz\" "
+																"44100	\"44100hz (cd quality)\" "
+																"48000	\"48000hz (dvd quality)\" "
+																//higher values are probably pointless when source data doesn't go that high, so not going to list them.
+																//"96000	\"96000hz (blu-ray quality)\" "
+																//"192000	\"192000hz (professional quality)\" "
+																)),	fl, [0, pos], [0, 8]), pos += 8;
+	if (engine!=E_QSS)
+	fr.add(menuitemcheck_spawn(_("Doppler"),			"s_doppler", 						'280 8'),		fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemcheck_spawn(_("8bit audio"),			cv2("s_loadas8bit"/*fte*/, "loadas8bit"), 					'280 8'),		fl, [0, pos], [0, 8]), pos += 8;
+	if (engine!=E_QSS)
+	fr.add(menuitemcheck_spawn(_("Swap Speakers"),		"s_swapstereo", 					'280 8'),		fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemslider_spawn(_("Latency"),			cv2("s_mixahead"/*fte*/, "_snd_mixahead"),	'0.1 0.3 0.01',	'280 8'),		fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemcheck_spawn(_("Disable Sound"),		"nosound",							'280 8'),		fl, [0, pos], [0, 8]), pos += 8;
 	//ambient fade
-	fr.add(menuitemcheck_spawn(_("Static Sounds"),	"cl_staticsounds", 					'280 8'),		fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcheck_spawn(_("Mix in Background"),"s_inactive", 					'280 8'),		fl, [0, pos], [0, 8]); pos += 8;
+	if (engine!=E_QSS)
+	fr.add(menuitemcheck_spawn(_("Static Sounds"),		"cl_staticsounds", 					'280 8'),		fl, [0, pos], [0, 8]), pos += 8;
+	if (engine!=E_QSS)
+	fr.add(menuitemcheck_spawn(_("Mix in Background"),	"s_inactive",						'280 8'),		fl, [0, pos], [0, 8]), pos += 8;
 
 	pos += 8;
-	fr.add(menuitemcombo_spawn(_("Microphone Device"),	"cl_voip_capturedevice",		'280 8', cvar_string("_cl_voip_capturedevice_opts")),
-																fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("VOIP Playback Vol"),"cl_voip_play",		'0 2 0.1', 	'280 8'),		fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcheck_spawn(_("VOIP Test"),		"cl_voip_test", 			'280 8'),		fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("VOIP Record Vol"),	"cl_voip_micamp",	'0 4 0.1', 	'280 8'),		fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcombo_spawn(_("VOIP Mode"),		"cl_voip_send",				'280 8', _(
-											"0 \"Push-To-Talk\" 1 "
-											"\"Voice Activation\" "
-											"2 \"Continuous\""
-														)),		fl, [0, pos], [0, 8]); pos += 8;
+	if (engine!=E_QSS)
+	fr.add(menuitemcombo_spawn(_("Microphone Device"),	"cl_voip_capturedevice",			'280 8', cvar_string("_cl_voip_capturedevice_opts")),
+																fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemslider_spawn(_("VOIP Playback Vol"),	"cl_voip_play",		'0 2 0.1',		'280 8'),		fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemcheck_spawn(_("VOIP Test"),			"cl_voip_test",						'280 8'),		fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemslider_spawn(_("VOIP Record Vol"),	"cl_voip_micamp",	'0 4 0.1',		'280 8'),		fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemcombo_spawn(_("VOIP Mode"),			"cl_voip_send",						'280 8', _(
+															"0 \"Push-To-Talk\" 1 "
+															"\"Voice Activation\" "
+															"2 \"Continuous\""
+															)),		fl, [0, pos], [0, 8]), pos += 8;
 	//VAD threshhold
 	//ducking
 	//noise cancelation
-	fr.add(menuitemcombo_spawn(_("VOIP Codec"),		"cl_voip_codec",			'280 8',_(
-											"0	\"speex (narrow 11khz)\" "
-											//"1	\"raw (wasteful)\" "
-											"2	\"opus\" "
-											"3	\"speex (narrow 8khz)\" "
-											"4	\"speex (wide 16khz)\" "
-											"5	\"speex (ultrawide 32khz)\" "
-														)),		fl, [0, pos], [0, 8]); pos += 8;
+	fr.add(menuitemcombo_spawn(_("VOIP Codec"),			"cl_voip_codec",			'280 8',_(
+															"\"\"	\"Auto\" "
+															"0	\"speex (narrow 11khz)\" "
+															//"1	\"raw (wasteful)\" "
+															"2	\"opus\" "
+															"3	\"speex (narrow 8khz)\" "
+															"4	\"speex (wide 16khz)\" "
+															"5	\"speex (ultrawide 32khz)\" "
+															)),		fl, [0, pos], [0, 8]), pos += 8;
 														
-	fr.add(menuitemslider_spawn(_("Opus bitrate"),		"cl_voip_bitrate",	'0.5 128 0.5','280 8'),		fl, [0, pos], [0, 8]); pos += 8;
+	fr.add(menuitemslider_spawn(_("Opus bitrate"),		"cl_voip_bitrate",	'0.5 128 0.5','280 8'),		fl, [0, pos], [0, 8]), pos += 8;
 
 	addmenuback(m);
 };
\ No newline at end of file
diff --git a/quakec/menusys/menu/options_basic.qc b/quakec/menusys/menu/options_basic.qc
index 82a4d18ca..02445c4ab 100644
--- a/quakec/menusys/menu/options_basic.qc
+++ b/quakec/menusys/menu/options_basic.qc
@@ -5,7 +5,7 @@ class mitem_playerpreview : mitem_spinnymodel
 		if (checkbuiltin2(setcustomskin, FALSE))
 		{
 			//if you wanted to get more advanced, you could use q3 skins here.
-			if (cvar("noskins"))
+			if (cvar("noskins")==1)
 				setcustomskin(self, "", sprintf("q1upper \"%s\"\nq1lower \"%s\"\n\n", cvar_string("topcolor"), cvar_string("bottomcolor")));
 			else if (cvar_string("cl_teamskin") != "")
 				setcustomskin(self, "", sprintf("q1upper \"%s\"\nq1lower \"%s\"\nqwskin \"%s\"\n", cvar_string("topcolor"), cvar_string("bottomcolor"), cvar_string("cl_teamskin")));
@@ -20,13 +20,30 @@ class mitem_playerpreview : mitem_spinnymodel
 static string() skinopts =
 {
 	string opts = "";
-	float s = search_begin("skins/*.pcx", TRUE, TRUE);
+	float s = search_begin("skins/*.*", TRUE, TRUE);
 	if (s < 0)
 		return opts;
 	float n = search_getsize(s);
 	for (float i = 0; i < n; i++)
 	{
 		string f = substring(search_getfilename(s, i), 6, -5);
+
+		//urgh!
+		if (strstrofs(f, "_pants") >= 0)
+			continue;
+		if (strstrofs(f, "_shirt") >= 0)
+			continue;
+		if (strstrofs(f, "_bump") >= 0)
+			continue;
+		if (strstrofs(f, "_norm") >= 0)
+			continue;
+		if (strstrofs(f, "_luma") >= 0)
+			continue;
+		if (strstrofs(f, "_glow") >= 0)
+			continue;
+		if (strstrofs(f, "_gloss") >= 0)
+			continue;
+
 		opts = strcat(opts, "\"", f, "\" \"", f, "\" ");
 	}
 	return opts;
@@ -41,6 +58,13 @@ class options_basic : mitem_exmenu
 			return TRUE;
 		if (key == "cl_run")
 			return TRUE;
+		if (engine == E_QSS)
+		{
+			if (key == "topcolor")
+				return super::isvalid("_cl_color");
+			if (key == "bottomcolor")
+				return super::isvalid("_cl_color");
+		}
 		return super::isvalid(key);
 	};
 	virtual string(string key) get =
@@ -49,6 +73,13 @@ class options_basic : mitem_exmenu
 			return (autocvar_m_pitch<0)?"1":"0";
 		if (key == "cl_run")
 			return (stof(super::get("cl_forwardspeed")) > 200)?"1":"0";
+		if (engine == E_QSS)
+		{
+			if (key == "topcolor")
+				return ftos(floor(stof(super::get("_cl_color"))/16));
+			if (key == "bottomcolor")
+				return ftos(stof(super::get("_cl_color"))&15);
+		}
 		return super::get(key);
 	};
 	virtual void(string key, string newval) set =
@@ -84,6 +115,15 @@ class options_basic : mitem_exmenu
 				super::set("cl_movespeedkey", "2.0");
 			}
 		}
+		else if (engine == E_QSS && (key == "topcolor" || key == "bottomcolor"))
+		{
+			float c = stof(super::get("_cl_color"));
+			if (key == "topcolor")
+				c = (c&~0xf0)|(stof(newval)*16);
+			else
+				c = (c&~0x0f)|stof(newval);
+			super::set("_cl_color", ftos(c));
+		}
 		else
 			super::set(key, newval);
 	};
@@ -107,25 +147,35 @@ nonstatic void(mitem_desktop desktop) M_Options_Basic =
 	float pos = 0;
 	
 
-	fr.add(menuitemeditt_spawn(_("Player Name"), 		cv2("_cl_name", "name"), 			'280 8'), 	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemeditt_spawn(_("Player Team"), 		"team",						'280 8'), 	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcombo_spawn(_("Player Skin"), 		"skin",						'280 8', 	skinopts()), fl, [0, pos], [0, 8]); pos += 8;
-																	
-																	
-	fr.add(menuitemcolour_spawn(_("Upper Colour"), 	"topcolor", 					'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcolour_spawn(_("Lower Colour"), 	"bottomcolor", 					'280 8'),	fl, [0, pos], [0, 8]); pos += 8;	/*aka: arse colour*/
+	fr.add(menuitemeditt_spawn(_("Player Name"), 		cv2("_cl_name"/*nq*/, "name"),					'280 8'), 	fl, [0, pos], [0, 8]), pos += 8;
+	if (engine!=E_QSS)
+	fr.add(menuitemeditt_spawn(_("Player Team"), 		"team",											'280 8'), 	fl, [0, pos], [0, 8]), pos += 8;
+	if (engine!=E_QSS)
+	fr.add(menuitemcombo_spawn(_("Player Skin"), 		"skin",											'280 8', 	skinopts()), fl, [0, pos], [0, 8]), pos += 8;
+
+
+	fr.add(menuitemcolour_spawn(_("Upper Colour"),		"topcolor",						engine==E_QSS,	'280 8'),	fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemcolour_spawn(_("Team Colour"),		"bottomcolor",					engine==E_QSS,	'280 8'),	fl, [0, pos], [0, 8]), pos += 8;	/*aka: arse colour, used for .team field in nq*/
 	pos += 8;
-	fr.add(menuitemcheck_spawn (_("Always Run"),		"cl_run", 					'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcheck_spawn (_("Invert Mouse"),		"m_pitchsign", 					'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Sensitivity"),		"sensitivity", 			'3 20 1',	'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Fov"),			"fov", 				'80 130 5',	'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Viewmodel Fov"),		"r_viewmodel_fov", 		'80 130 5',	'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Gamma"),			cv2("v_gamma", "gamma"), 	'0.4 1.3 0.1',	'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Contrast"),		cv2("v_contrast", "contrast"), 	'0.8 1.8 0.1', 	'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Brightness"),		cv2("v_brightness", "brightness"),'0.0 0.5 0.1', '280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Crosshair"),		"crosshair", 			'0.0 19 1', 	'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	
-	m.add(spawn (mitem_playerpreview, item_text: "progs/player.mdl", firstframe:0, framecount:6, shootframe:119, shootframes:6), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-200, 12*-16/2], [-40, 12*16/2]);
+	fr.add(menuitemcheck_spawn (_("Always Run"),		"cl_run",										'280 8'),	fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemcheck_spawn (_("Invert Mouse"),		"m_pitchsign",									'280 8'),	fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemslider_spawn(_("Sensitivity"),		"sensitivity",					'3 20 1',		'280 8'),	fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemslider_spawn(_("Fov"),				"fov",							'80 130 5',		'280 8'),	fl, [0, pos], [0, 8]), pos += 8;
+	if (engine!=E_QSS)
+	fr.add(menuitemslider_spawn(_("Viewmodel Fov"),		"r_viewmodel_fov",				'80 130 5',		'280 8'),	fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemslider_spawn(_("Gamma"),				cv2("v_gamma", "gamma"),		'0.4 1.3 0.1',	'280 8'),	fl, [0, pos], [0, 8]), pos += 8;
+	fr.add(menuitemslider_spawn(_("Contrast"),			cv2("v_contrast", "contrast"),	'0.8 1.8 0.1',	'280 8'),	fl, [0, pos], [0, 8]), pos += 8;
+	if (engine!=E_QSS)
+	fr.add(menuitemslider_spawn(_("Brightness"),		cv2("v_brightness", "brightness"),'0.0 0.5 0.1','280 8'),	fl, [0, pos], [0, 8]), pos += 8;
+	if (engine==E_FTE)
+		fr.add(menuitemslider_spawn(_("Crosshair"),		"crosshair",					'0.0 19 1',		'280 8'),	fl, [0, pos], [0, 8]), pos += 8;
+	else
+		fr.add(menuitemcheck_spawn(_("Crosshair"),		"crosshair",									'280 8'),	fl, [0, pos], [0, 8]), pos += 8;
+
+	if (engine==E_QSS)	//lerps are kinda broken so don't do the running+shooting+spinning thing.
+		m.add(spawn (mitem_playerpreview, item_text: "progs/player.mdl", firstframe:12, framecount:5, angles_y:random(90,270)), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-200, 12*-16/2], [-40, 12*16/2]);
+	else
+		m.add(spawn (mitem_playerpreview, item_text: "progs/player.mdl", firstframe:0, framecount:6, shootframe:119, shootframes:6), RS_X_MIN_PARENT_MID|RS_Y_MIN_PARENT_MID | RS_X_MAX_PARENT_MID|RS_Y_MAX_PARENT_MID, [-200, 12*-16/2], [-40, 12*16/2]);
 
 	addmenuback(m);
 };
diff --git a/quakec/menusys/menu/options_effects.qc b/quakec/menusys/menu/options_effects.qc
index 4cc7906d5..b85fecb6e 100644
--- a/quakec/menusys/menu/options_effects.qc
+++ b/quakec/menusys/menu/options_effects.qc
@@ -17,39 +17,77 @@ nonstatic void(mitem_desktop desktop) M_Options_Effects =
 	float pos = 0;
 
 	fr.add(menuitemcheck_spawn(_("Show Framerate"),			cv3("showfps", "scr_showfps", "show_fps"), 		'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcheck_spawn(_("Bloom"),				"r_bloom", 				'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcheck_spawn(_("Simple Textures"),		"r_drawflat", 				'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcheck_spawn(_("Paletted Rendering"),		"r_softwarebanding", 			'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcheck_spawn(_("HDR"),				"r_hdr_irisadaptation", 		'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcheck_spawn(_("Coronas"),			"r_coronas", 				'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
 	fr.add(menuitemcheck_spawn(_("High Res Textures"),		"gl_load24bit", 			'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcheck_spawn(_("Relief Mapping"),			"r_glsl_offsetmapping", 		'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcheck_spawn(_("Realtime Dynamic Lights"),	"r_shadow_realtime_dlight", 		'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcheck_spawn(_("Realtime World Lighting"),	"r_shadow_realtime_world", 		'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
 
+	fr.add(menuitemcheck_spawn(_("High Res Textures"),		"gl_load24bit", 			'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
 
+	fr.add(menuitemcombo_spawn(_("Texture Mode"),		"gl_texturemode",			'280 8', _(
+																			"GL_NEAREST	\"Nearest\" "
+																			"GL_NEAREST_MIPMAP_NEAREST	\"Nearest (Nearest mips)\" "
+																			"GL_NEAREST_MIPMAP_LINEAR	\"Nearest (Linear mips)\" "
+																			"GL_LINEAR	\"Linear\" "
+																			"GL_LINEAR_MIPMAP_NEAREST	\"Linear (Nearest mips)\" "
+																			"GL_LINEAR_MIPMAP_LINEAR	\"Linear (Linear mips)\" "
+
+																			"n.l	\"Nearest (Smooth)\" "
+																			//"l.n	\"Linear (Unsmooth)\" "
+
+																			//"nnl	\"Nearest (Nearest mips, Smooth)\" "
+																			//"lnn	\"Linear (Nearest mips, Unsmooth)\" "
+																			"nll	\"Nearest (Smooth mips)\" "
+																			//"lln	\"Linear (Linear mips, Unsmooth)\" "
+																)),	fl, [0, pos], [0, 8]); pos += 8;
+
+	fr.add(menuitemcombo_spawn(_("Anistrophy"),		"gl_texture_anisotropic_filtering",				'280 8', _(
+																			"0	\"Off\" "
+																			"2	\"2\" "
+																			"4	\"4\" "
+																			"8	\"8\" "
+																			"16	\"16\" "
+																)),	fl, [0, pos], [0, 8]); pos += 8;
+
+	fr.add(menuitemcheck_spawn(_("Lightmaps Only"),		"r_lightmap", 				'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
+	if (engine == E_QSS)
+	{
+		fr.add(menuitemcheck_spawn(_("Wireframe"),		"r_showtris", 				'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
+		fr.add(menuitemcheck_spawn(_("Simple DLights"),		"gl_flashblend", 				'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
+
+		fr.add(menuitemslider_spawn(_("Stereo"),		"r_stereo",	'0 8 0.25',	'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
+	}
+	else
+	{
+		fr.add(menuitemcheck_spawn(_("Wireframe"),		"r_wireframe", 				'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
+		fr.add(menuitemcheck_spawn(_("Bloom"),				"r_bloom", 				'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
+		fr.add(menuitemcheck_spawn(_("Simple Textures"),		"r_drawflat", 				'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
+		fr.add(menuitemcheck_spawn(_("Paletted Rendering"),		"r_softwarebanding", 			'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
+		fr.add(menuitemcheck_spawn(_("HDR"),				"r_hdr_irisadaptation", 		'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
+		fr.add(menuitemcheck_spawn(_("Coronas"),			"r_coronas", 				'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
+		fr.add(menuitemcheck_spawn(_("Relief Mapping"),			"r_glsl_offsetmapping", 		'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
+		fr.add(menuitemcheck_spawn(_("RT Dynamic Lights"),	"r_shadow_realtime_dlight", 		'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
+		fr.add(menuitemcheck_spawn(_("RT World Lighting"),	"r_shadow_realtime_world", 		'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
+
+		fr.add(menuitemcombo_spawn(_("Water Effects"),			"r_waterstyle",				'280 8', _(
+																			"1	\"Classic\" "
+																			"2	\"Ripples\" "
+																			"3	\"Reflections\" "
+																)),	fl, [0, pos], [0, 8]); pos += 8;
+		fr.add(menuitemcombo_spawn(_("View Projection"),		"r_projection",				'280 8', _(
+																			"0	\"Standard\" "
+																			"1	\"Stereographic / Pannini\" "
+																			"2	\"Fish-Eye\" "
+																			"3	\"Panoramic\" "
+																			"4	\"Lambert Azimuthal Equal-Area\" "
+																			"5	\"Equirectangular\" "
+																)),	fl, [0, pos], [0, 8]); pos += 8;
+		fr.add(menuitemcombo_spawn(_("View Projection Fov"),		"ffov",					'280 8', _(
+																			"90	\"Normal\" "
+																			"180	\"180 degrees\" "
+																			"270	\"270 degrees\" "
+																			"360	\"360 degrees\" "
+																)),	fl, [0, pos], [0, 8]); pos += 8;
+	}
 	fr.add(menuitemslider_spawn(_("Particle Density"),		"r_part_density",	'0.25 4 0.25',	'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
 
-	fr.add(menuitemcombo_spawn(_("Water Effects"),			"r_waterstyle",				'280 8', _(
-																		"1	\"Classic\" "
-																		"2	\"Ripples\" "
-																		"3	\"Reflections\" "
-															)),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcombo_spawn(_("View Projection"),		"r_projection",				'280 8', _(
-																		"0	\"Standard\" "
-																		"1	\"Stereographic / Pannini\" "
-																		"2	\"Fish-Eye\" "
-																		"3	\"Panoramic\" "
-																		"4	\"Lambert Azimuthal Equal-Area\" "
-																		"5	\"Equirectangular\" "
-															)),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcombo_spawn(_("View Projection Fov"),		"ffov",					'280 8', _(
-																		"90	\"Normal\" "
-																		"180	\"180 degrees\" "
-																		"270	\"270 degrees\" "
-																		"360	\"360 degrees\" "
-															)),	fl, [0, pos], [0, 8]); pos += 8;
-
 	fr.add(spawn(mitem_text, item_text:_("Apply"), 	item_command:"vid_restart", item_scale:8, item_flags:IF_RIGHTALIGN),	RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MID|RS_Y_MAX_OWN_MIN, [0, pos], [-8, 8]); pos += 8;
 
 	addmenuback(m);
diff --git a/quakec/menusys/menu/options_particles.qc b/quakec/menusys/menu/options_particles.qc
index fa52998a3..40ec0ad3d 100644
--- a/quakec/menusys/menu/options_particles.qc
+++ b/quakec/menusys/menu/options_particles.qc
@@ -99,8 +99,11 @@ nonstatic void(mitem_desktop desktop) M_Options_Particles =
 	if (checkextension("FTE_PART_NAMESPACE_EFFECTINFO"))
 	{
 		fr.add(spawn(mitem_text, item_text:"Classic Particles",	item_command:"classic", item_scale:8, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y+=8], '0 8');
-		fr.add(spawn(mitem_text, item_text:"High Quality",	item_command:"high", item_scale:8, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y+=8], '0 8');
-		fr.add(spawn(mitem_text, item_text:"TimeServ's Shaft", item_command:"tsshaft", item_scale:8, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y+=8], '0 8');
+		if (engine==E_FTE)
+		{
+			fr.add(spawn(mitem_text, item_text:"High Quality",	item_command:"high", item_scale:8, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y+=8], '0 8');
+			fr.add(spawn(mitem_text, item_text:"TimeServ's Shaft", item_command:"tsshaft", item_scale:8, item_flags:IF_CENTERALIGN), RS_X_MIN_PARENT_MIN|RS_Y_MIN_PARENT_MIN | RS_X_MAX_PARENT_MAX|RS_Y_MAX_OWN_MIN, [0, y+=8], '0 8');
+		}
 	
 		string efi = GetFirstLineComment("effectinfo.txt", "Effectinfo");
 		if (efi)
diff --git a/quakec/menusys/menu/options_video.qc b/quakec/menusys/menu/options_video.qc
index c0d9d9200..208cd2a01 100644
--- a/quakec/menusys/menu/options_video.qc
+++ b/quakec/menusys/menu/options_video.qc
@@ -77,9 +77,13 @@ nonstatic void(mitem_desktop desktop) M_Options_Video =
 	if (!dp_workarounds)
 	{
 		fr.add(menuitemcombo_spawn(_("Display Mode"),	"vid_fullscreen",				'280 8', 
-																		"0	\"Windowed\" "
-																		"1	\"Fullscreen\" "
-																		"2	\"Borderless Windowed\" "
+																		engine==E_FTE?
+																			"0	\"Windowed\" "
+																			"1	\"Fullscreen\" "
+																			"2	\"Borderless Windowed\" "
+																		:
+																			"0	\"Windowed\" "
+																			"1	\"Fullscreen\" "
 																		),	fl, [0, pos], [0, 8]); pos += 8;
 	}
 	else
@@ -87,7 +91,7 @@ nonstatic void(mitem_desktop desktop) M_Options_Video =
 		fr.add(menuitemcheck_spawn(_("Fullscreen"),		"vid_fullscreen", 			'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
 	}
 	if (cvar_type("vid_resizable")) fr.add(menuitemcheck_spawn(_("Resizable"),	"vid_resizable", 	'280 8'),	fl, [0, pos], [0, 8]), pos += 8;
-	fr.add(menuitemcombo_spawn(_("Anti-Aliasing"),	cv2("vid_samples", "vid_multisample"),			'280 8', 
+	fr.add(menuitemcombo_spawn(_("MSAA"),	cv3("vid_samples"/*dp*/, "vid_fsaa"/*qs*/, "vid_multisample"/*fte*/),			'280 8',
 																		"0	\"Off\" "
 																		"2	\"2x\" "
 																		"4	\"4x\" "
@@ -124,16 +128,16 @@ static const string scaleoptions = _(
 	fr.add(menuitemcheck_spawn(_("Show Framerate"),	cv3("showfps"/*dp*/, "scr_showfps"/*qs*/, "show_fps"/*id/qw*/), 	'280 8'),	fl, [0, pos], [0, 8]); pos += 8;
 	fr.add(menuitemslider_spawn(_("View Size"),	"viewsize",				'50 120 10', '280 8'),	fl, [0, pos], [0, 8]); pos += 8;
 	fr.add(menuitemslider_spawn(_("Field Of View"),	"fov",				'50 140 5', '280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Gamma"),	"gamma",				'1.3 0.5 -0.1', '280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Contrast"),	"contrast",				'0.7 2 0.1', '280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemslider_spawn(_("Brightness"),	"brightness",			'0 0.4 0.05', '280 8'),	fl, [0, pos], [0, 8]); pos += 8;
-	fr.add(menuitemcombo_spawn(_("Hardware Gamma"),	"vid_hardwaregamma",			'280 8', 
+	fr.add(menuitemslider_spawn(_("Gamma"),	cv2("v_gamma"/*dp*/, "gamma"/*quake*/),				'1.3 0.5 -0.1', '280 8'),	fl, [0, pos], [0, 8]); pos += 8;
+	fr.add(menuitemslider_spawn(_("Contrast"),	cv2("v_contrast"/*dp*/, "contrast"/*quake*/),				'0.7 2 0.1', '280 8'),	fl, [0, pos], [0, 8]); pos += 8;
+	if (engine!=E_QSS)fr.add(menuitemslider_spawn(_("Brightness"),	cv2("v_brightness"/*dp*/, "brightness"/*quake*/),			'0 0.4 0.05', '280 8'),	fl, [0, pos], [0, 8]), pos += 8;
+	if (engine!=E_QSS)fr.add(menuitemcombo_spawn(_("Hardware Gamma"),	"vid_hardwaregamma",			'280 8',
 																		"0	\"Off\" "
 																		"1	\"Auto\" "
 																		"2	\"Soft\" "
 																		"3	\"Hard\" "
 																		"4	\"Scene Only\" "
-																		),	fl, [0, pos], [0, 8]); pos += 8;
+																		),	fl, [0, pos], [0, 8]), pos += 8;
 
 	addmenuback(m);
 };
diff --git a/quakec/menusys/menusys/mitem_colours.qc b/quakec/menusys/menusys/mitem_colours.qc
index 1588baba8..8cf7d4b43 100644
--- a/quakec/menusys/menusys/mitem_colours.qc
+++ b/quakec/menusys/menusys/mitem_colours.qc
@@ -6,6 +6,7 @@ Screw Quake colours, they're too brown!
 */
 class mitem_colours : mitem
 {
+	float quakecolours;
 	virtual void(vector pos) item_draw;
 	virtual float(vector pos, float scan, float char, float down) item_keypress;
 	
@@ -144,7 +145,6 @@ void(vector pos) mitem_colours::item_draw =
 	super::item_draw(pos);
 
 	//calculate the rgb from hue at each step across the colour block
-#define STEPS 32
 	pos_x += item_size_x / 2;
 
 	if (ui.mgrabs == this)
@@ -159,6 +159,31 @@ void(vector pos) mitem_colours::item_draw =
 	}
 	curval = get(item_command);
 
+	if (quakecolours)
+	{
+#define STEPS 14
+		stride = (item_size_x / 2) / STEPS;
+		for (step = 0; step < STEPS; step++, pos_x += stride)
+		{
+			string v = ftos(step);
+			rgb = hextorgb(v);
+			if (!(item_flags & IF_SELECTABLE))
+				rgb *= 0.2;
+			rgb *= 0.5;
+			if (v == curval)
+			{
+				ui.drawfill(pos, [stride, item_size_y], rgb*2, item_alpha, 0);
+				rgb *= 1 + sin(time*4)*.2;
+				ui.drawfill(pos+[0,2], [stride, item_size_y-4], rgb, item_alpha, 0);
+			}
+			else
+				ui.drawfill(pos+[0,2], [stride, item_size_y-4], rgb, item_alpha, 0);
+		}
+		return;
+#undef STEPS
+	}
+
+#define STEPS 32
 	stride = (item_size_x / 2 - (item_size_y+4)) / STEPS;
 
 	ui.drawfill(pos, [item_size_y, item_size_y], hextorgb(curval), item_alpha, 0);
@@ -204,21 +229,31 @@ float(vector pos, float scan, float char, float down) mitem_colours::item_keypre
 			ui.mgrabs = __NULL__;
 		return FALSE;
 	}
-	local float curval = rgbtohsv(hextorgb(get(item_command)))[0];
 	if (scan == K_MWHEELUP || scan == K_MWHEELDOWN)
 	{
 		if (mouseinbox(pos, item_size))
 			scan = ((scan == K_MWHEELDOWN)?K_LEFTARROW:K_RIGHTARROW);
 	}
+
+
+	local float curval = rgbtohsv(hextorgb(get(item_command)))[0];
 	if (scan == K_MOUSE1)
 	{
-		pos_x += item_size_x / 2;
-		pos_x += item_size_y+4;
-		curval = (ui.mousepos[0] - pos_x) / (item_size_x / 2 - item_size_y+4);
+		float width = item_size_x / 2;
+		pos_x += width;
+		if (!quakecolours)
+		{
+			pos_x += item_size_y+4;
+			width = width - item_size_y+4;
+		}
+		curval = (ui.mousepos[0] - pos_x) / width;
 		if (curval < 0 || curval > 1)
 			return FALSE;
 		curval = curval;
-		set(item_command, rgbtohex(hsvtorgb([(curval), 1, 1])));
+		if (quakecolours)
+			set(item_command, ftos(floor(curval*14)));
+		else
+			set(item_command, rgbtohex(hsvtorgb([(curval), 1, 1])));
 		ui.mgrabs = this;
 	}
 	else if (scan == K_LEFTARROW || scan == K_SPACE)
@@ -233,13 +268,15 @@ float(vector pos, float scan, float char, float down) mitem_colours::item_keypre
 		return FALSE;
 	return TRUE;
 };
-mitem_colours(string text, string command, vector sz) menuitemcolour_spawn =
+mitem_colours(string text, string command, float limited, vector sz) menuitemcolour_spawn =
 {
 	mitem_colours n = spawn(mitem_colours);
 	n.item_scale = sz_y;
 	n.item_text = text;
 	n.item_size = sz;
 
+	n.quakecolours = limited;
+
 	n.item_command = command;
 	n.item_flags |= IF_SELECTABLE;
 	return n;
diff --git a/quakec/menusys/menusys/mitem_desktop.qc b/quakec/menusys/menusys/mitem_desktop.qc
index 80075638f..ac3d50786 100644
--- a/quakec/menusys/menusys/mitem_desktop.qc
+++ b/quakec/menusys/menusys/mitem_desktop.qc
@@ -263,10 +263,6 @@ void(vector pos) mitem_desktop::item_draw =
 };
 
 
-var string autocvar_cl_cursor = "gfx/cursor.lmp";
-var float autocvar_cl_cursorsize = 32;
-var float autocvar_cl_cursorbias = 4;
-
 static var float oldgrabstate;	//to work around a DP bug (as well as unnecessary spam)
 
 void(float force) items_updategrabs =
@@ -278,11 +274,14 @@ void(float force) items_updategrabs =
 			oldgrabstate = TRUE;
 #ifdef MENU
 			setkeydest(2);
-			setmousetarget(2);
-#else
-			setcursormode(TRUE);
-			//setcursormode(TRUE, autocvar_cl_cursor, autocvar_cl_cursorbias*'1 1', autocvar_cl_cursorscale);
+			if (!checkbuiltin(setcursormode))
+				setmousetarget(2);	//legacy junk
+			else
 #endif
+			if (checkextension("FTE_QC_HARDWARECURSORS"))
+				setcursormode(TRUE, autocvar(cl_cursor, "gfx/cursor.lmp"), autocvar(cl_cursorbias, 4.0)*'1 1', autocvar(cl_cursorscale, 1.0));	//because we can
+			else
+				setcursormode(TRUE);	//because DP sucks.
 		}
 	}
 	else if (oldgrabstate || force)
@@ -290,10 +289,11 @@ void(float force) items_updategrabs =
 		oldgrabstate = FALSE;
 #ifdef MENU
 		setkeydest(0);
-		setmousetarget(1);
-#else
-		setcursormode(FALSE);
+		if (!checkbuiltin(setcursormode))
+			setmousetarget(1);
+		else
 #endif
+			setcursormode(FALSE);
 	}
 };
 
@@ -321,7 +321,7 @@ void(mitem_desktop desktop, vector screensize) items_draw =
 	items_updategrabs(FALSE);
 	if (dp_workarounds && oldgrabstate)
 	{
-//		//hopefully dp isn't broken and reports non-zero sizes for files that failed.
+//		//hopefully dp isn't broken and reports zero sizes for files that failed...
 //		if (drawgetimagesize(autocvar_cl_cursor) == '0 0')
 //			ui.drawpic(ui.mousepos - autocvar_cl_cursorbias*'1 1', autocvar_cl_cursor, autocvar_cl_cursorsize*'1 1', '1 1 1', 1, 0);
 //		else
diff --git a/quakec/menusys/menusys/mitem_spinnymodel.qc b/quakec/menusys/menusys/mitem_spinnymodel.qc
index 2ac8aa4a1..6d1851de8 100644
--- a/quakec/menusys/menusys/mitem_spinnymodel.qc
+++ b/quakec/menusys/menusys/mitem_spinnymodel.qc
@@ -57,6 +57,7 @@ class mitem_spinnymodel : mitem
 	float framecount;
 	float shootframe;
 	float shootframes;
+	float rotatespeed;
 	
 #ifndef EXT_CSQC
 	virtual void(vector pos) item_draw = {};
@@ -74,7 +75,7 @@ class mitem_spinnymodel : mitem
 		origin_z += zbias;
 	
 //		angles_y = cltime*90;//frametime*90;
-		angles_y += frametime*90;
+		angles_y += frametime*rotatespeed;
 
 		clearscene(); //wipe the scene, and apply the default rendering values.
 		setviewprop(VF_MIN, vtodpp(pos)); //min pos
@@ -141,6 +142,9 @@ class mitem_spinnymodel : mitem
 		setmodel(this, item_text);	//use the size information from the engine, woo for unreliability.
 		zbias += (mins_z - maxs_z)/2 - mins_z; //center the model on its z axis, so the whole thing is visible.
 		frame = firstframe;
+		if (!angles_y && !rotatespeed)
+			rotatespeed=90;
+
 	};
 #endif
 };