diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index 5f0c8278408eb59435596a96fd8763a9c7da0a91..6d9705b40d74a8891b505137741b962719d04e5b 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -669,8 +669,29 @@ void D_RegisterClientCommands(void)
 	CV_RegisterVar(&cv_resetmusic);
 
 	// FIXME: not to be here.. but needs be done for config loading
-	CV_RegisterVar(&cv_usegamma);
-	CV_RegisterVar(&cv_usesaturation);
+	CV_RegisterVar(&cv_globalgamma);
+	CV_RegisterVar(&cv_globalsaturation);
+
+	CV_RegisterVar(&cv_rhue);
+	CV_RegisterVar(&cv_yhue);
+	CV_RegisterVar(&cv_ghue);
+	CV_RegisterVar(&cv_chue);
+	CV_RegisterVar(&cv_bhue);
+	CV_RegisterVar(&cv_mhue);
+
+	CV_RegisterVar(&cv_rgamma);
+	CV_RegisterVar(&cv_ygamma);
+	CV_RegisterVar(&cv_ggamma);
+	CV_RegisterVar(&cv_cgamma);
+	CV_RegisterVar(&cv_bgamma);
+	CV_RegisterVar(&cv_mgamma);
+
+	CV_RegisterVar(&cv_rsaturation);
+	CV_RegisterVar(&cv_ysaturation);
+	CV_RegisterVar(&cv_gsaturation);
+	CV_RegisterVar(&cv_csaturation);
+	CV_RegisterVar(&cv_bsaturation);
+	CV_RegisterVar(&cv_msaturation);
 
 	// m_menu.c
 	CV_RegisterVar(&cv_crosshair);
diff --git a/src/m_menu.c b/src/m_menu.c
index 283b76b883cdabb5d47b0fdf5a2c2ae44dd9dc12..8dfc2ee0aa9eb636278a05c603c42fb6d651c413 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -315,7 +315,7 @@ static void M_AssignJoystick(INT32 choice);
 static void M_ChangeControl(INT32 choice);
 
 // Video & Sound
-menu_t OP_VideoOptionsDef, OP_VideoModeDef;
+menu_t OP_VideoOptionsDef, OP_VideoModeDef, OP_ColorOptionsDef;
 #ifdef HWRENDER
 menu_t OP_OpenGLOptionsDef, OP_OpenGLFogDef, OP_OpenGLColorDef;
 #endif
@@ -353,6 +353,7 @@ static void M_DrawSetupChoosePlayerMenu(void);
 static void M_DrawControl(void);
 static void M_DrawMainVideoMenu(void);
 static void M_DrawVideoMode(void);
+static void M_DrawColorMenu(void);
 static void M_DrawSoundMenu(void);
 static void M_DrawMonitorToggles(void);
 #ifdef HWRENDER
@@ -387,6 +388,8 @@ static void M_HandleFogColor(INT32 choice);
 #endif
 static void M_HandleVideoMode(INT32 choice);
 
+static void M_ResetCvars(void);
+
 // Consvar onchange functions
 static void Newgametype_OnChange(void);
 static void Dummymares_OnChange(void);
@@ -1164,40 +1167,39 @@ static menuitem_t OP_Mouse2OptionsMenu[] =
 
 static menuitem_t OP_VideoOptionsMenu[] =
 {
-	{IT_STRING | IT_CALL,  NULL, "Set Resolution...", M_VideoModeMenu,        0},
+	{IT_STRING | IT_CALL,  NULL, "Set Resolution...", M_VideoModeMenu,                0},
 
 #if (defined (__unix__) && !defined (MSDOS)) || defined (UNIXCOMMON) || defined (HAVE_SDL)
-	{IT_STRING|IT_CVAR,      NULL, "Fullscreen",       &cv_fullscreen,       5},
+	{IT_STRING|IT_CVAR,      NULL, "Fullscreen",       &cv_fullscreen,                5},
 #endif
 
 #ifdef HWRENDER
-	{IT_SUBMENU|IT_STRING, NULL, "3D Card Options...", &OP_OpenGLOptionsDef,  10},
+	{IT_SUBMENU|IT_STRING, NULL, "3D Card Options...", &OP_OpenGLOptionsDef,         10},
 #endif
 
-	{IT_STRING | IT_CVAR | IT_CV_SLIDER,
-	                         NULL, "Brightness",      &cv_usegamma,          15},
-	{IT_STRING | IT_CVAR | IT_CV_SLIDER,
-	                         NULL, "Saturation",      &cv_usesaturation,     20},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Brightness",   &cv_globalgamma,      15},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Saturation",   &cv_globalsaturation, 20},
+	{IT_SUBMENU|IT_STRING, NULL, "Advanced Color Settings...", &OP_ColorOptionsDef,  25},
 
-	{IT_STRING | IT_CVAR, NULL, "Display HUD",        &cv_showhud,           30},
+	{IT_STRING | IT_CVAR, NULL, "Display HUD",                 &cv_showhud,          35},
 	{IT_STRING | IT_CVAR | IT_CV_SLIDER,
-	                      NULL, "HUD Transparency",   &cv_translucenthud,    35},
-	{IT_STRING | IT_CVAR, NULL, "Time Display",       &cv_timetic,           40},
+	                      NULL, "HUD Transparency",            &cv_translucenthud,   40},
+	{IT_STRING | IT_CVAR, NULL, "Time Display",                &cv_timetic,          45},
 #ifdef SEENAMES
-	{IT_STRING | IT_CVAR, NULL, "Show HUD player names",  &cv_seenames,      45},
+	{IT_STRING | IT_CVAR, NULL, "Show HUD player names",       &cv_seenames,         50},
 #endif
 
-	{IT_STRING | IT_CVAR, NULL, "Console Background", &cons_backcolor,       55},
-	{IT_STRING | IT_CVAR, NULL, "Console Text Size",  &cv_constextsize,      60},
+	{IT_STRING | IT_CVAR, NULL, "Console Background",          &cons_backcolor,      60},
+	{IT_STRING | IT_CVAR, NULL, "Console Text Size",           &cv_constextsize,     65},
 
-	{IT_STRING | IT_CVAR, NULL, "Draw Distance",      &cv_drawdist,          70},
-	{IT_STRING | IT_CVAR, NULL, "NiGHTS Draw Dist.",  &cv_drawdist_nights,   75},
-	{IT_STRING | IT_CVAR, NULL, "Weather Draw Dist.", &cv_drawdist_precip,   80},
-	{IT_STRING | IT_CVAR, NULL, "Weather Density",    &cv_precipdensity,     85},
+	{IT_STRING | IT_CVAR, NULL, "Draw Distance",               &cv_drawdist,         75},
+	{IT_STRING | IT_CVAR, NULL, "NiGHTS Draw Dist.",           &cv_drawdist_nights,  80},
+	{IT_STRING | IT_CVAR, NULL, "Weather Draw Dist.",          &cv_drawdist_precip,  85},
+	{IT_STRING | IT_CVAR, NULL, "Weather Density",             &cv_precipdensity,    90},
 
-	{IT_STRING | IT_CVAR, NULL, "Show FPS",           &cv_ticrate,           95},
-	{IT_STRING | IT_CVAR, NULL, "Clear Before Redraw",&cv_homremoval,       100},
-	{IT_STRING | IT_CVAR, NULL, "Vertical Sync",      &cv_vidwait,          105},
+	{IT_STRING | IT_CVAR, NULL, "Show FPS",                    &cv_ticrate,         100},
+	{IT_STRING | IT_CVAR, NULL, "Clear Before Redraw",         &cv_homremoval,      105},
+	{IT_STRING | IT_CVAR, NULL, "Vertical Sync",               &cv_vidwait,         110},
 };
 
 static menuitem_t OP_VideoModeMenu[] =
@@ -1205,6 +1207,47 @@ static menuitem_t OP_VideoModeMenu[] =
 	{IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleVideoMode, 0},     // dummy menuitem for the control func
 };
 
+static menuitem_t OP_ColorOptionsMenu[] =
+{
+	{IT_STRING | IT_CALL, NULL, "Reset all", M_ResetCvars, 0},
+
+	{IT_HEADER, NULL, "Red", NULL, 9},
+	{IT_DISABLED, NULL, NULL, NULL, 35},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Hue",          &cv_rhue,         15},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Saturation",   &cv_rsaturation,  20},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Brightness",   &cv_rgamma,       25},
+
+	{IT_HEADER, NULL, "Yellow", NULL, 34},
+	{IT_DISABLED, NULL, NULL, NULL, 73},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Hue",          &cv_yhue,         40},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Saturation",   &cv_ysaturation,  45},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Brightness",   &cv_ygamma,       50},
+
+	{IT_HEADER, NULL, "Green", NULL, 59},
+	{IT_DISABLED, NULL, NULL, NULL, 112},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Hue",          &cv_ghue,         65},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Saturation",   &cv_gsaturation,  70},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Brightness",   &cv_ggamma,       75},
+
+	{IT_HEADER, NULL, "Cyan", NULL, 84},
+	{IT_DISABLED, NULL, NULL, NULL, 255},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Hue",          &cv_chue,         90},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Saturation",   &cv_csaturation,  95},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Brightness",   &cv_cgamma,      100},
+
+	{IT_HEADER, NULL, "Blue", NULL, 109},
+	{IT_DISABLED, NULL, NULL, NULL, 152},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Hue",          &cv_bhue,        115},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Saturation",   &cv_bsaturation, 120},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Brightness",   &cv_bgamma,      125},
+
+	{IT_HEADER, NULL, "Magenta", NULL, 134},
+	{IT_DISABLED, NULL, NULL, NULL, 181},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Hue",          &cv_mhue,        140},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Saturation",   &cv_msaturation, 145},
+	{IT_STRING | IT_CVAR | IT_CV_SLIDER, NULL, "Brightness",   &cv_mgamma,      150},
+};
+
 #ifdef HWRENDER
 static menuitem_t OP_OpenGLOptionsMenu[] =
 {
@@ -1360,18 +1403,19 @@ static menuitem_t OP_ServerOptionsMenu[] =
 static menuitem_t OP_MonitorToggleMenu[] =
 {
 	// Printing handled by drawing function
-	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Recycler",          &cv_recycler,      20},
-	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Teleporters",       &cv_teleporters,   30},
-	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Super Ring",        &cv_superring,     40},
-	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Super Sneakers",    &cv_supersneakers, 50},
-	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Invincibility",     &cv_invincibility, 60},
-	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Jump Shield",       &cv_jumpshield,    70},
-	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Elemental Shield",  &cv_watershield,   80},
-	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Attraction Shield", &cv_ringshield,    90},
-	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Force Shield",      &cv_forceshield,  100},
-	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Armageddon Shield", &cv_bombshield,   110},
-	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "1 Up",              &cv_1up,          120},
-	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Eggman Box",        &cv_eggmanbox,    130},
+	{IT_STRING|IT_CALL, NULL, "Reset all", M_ResetCvars, 15},
+	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Recycler",          &cv_recycler,      30},
+	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Teleporters",       &cv_teleporters,   40},
+	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Super Ring",        &cv_superring,     50},
+	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Super Sneakers",    &cv_supersneakers, 60},
+	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Invincibility",     &cv_invincibility, 70},
+	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Jump Shield",       &cv_jumpshield,    80},
+	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Elemental Shield",  &cv_watershield,   90},
+	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Attraction Shield", &cv_ringshield,   100},
+	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Force Shield",      &cv_forceshield,  110},
+	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Armageddon Shield", &cv_bombshield,   120},
+	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "1 Up",              &cv_1up,          130},
+	{IT_STRING|IT_CVAR|IT_CV_INVISSLIDER, NULL, "Eggman Box",        &cv_eggmanbox,    140},
 };
 
 // ==========================================================================
@@ -1721,6 +1765,17 @@ menu_t OP_VideoModeDef =
 	0,
 	NULL
 };
+menu_t OP_ColorOptionsDef =
+{
+	"M_VIDEO",
+	sizeof (OP_ColorOptionsMenu)/sizeof (menuitem_t),
+	&OP_VideoOptionsDef,
+	OP_ColorOptionsMenu,
+	M_DrawColorMenu,
+	30, 30,
+	0,
+	NULL
+};
 menu_t OP_SoundOptionsDef =
 {
 	"M_SOUND",
@@ -2099,6 +2154,19 @@ static boolean M_ChangeStringCvar(INT32 choice)
 	return false;
 }
 
+// resets all cvars on a menu - assumes that all that have itemactions are cvars
+static void M_ResetCvars(void)
+{
+	INT32 i;
+	consvar_t *cv;
+	for (i = 0; i < currentMenu->numitems; i++)
+	{
+		if (!(currentMenu->menuitems[i].status & IT_CVAR) || !(cv = (consvar_t *)currentMenu->menuitems[i].itemaction))
+			continue;
+		CV_SetValue(cv, atoi(cv->defaultvalue));
+	}
+}
+
 static void M_NextOpt(void)
 {
 	INT16 oldItemOn = itemOn; // prevent infinite loop
@@ -2303,7 +2371,7 @@ boolean M_Responder(event_t *ev)
 				return true;
 
 			case KEY_F11: // Gamma Level
-				CV_AddValue(&cv_usegamma, 1);
+				CV_AddValue(&cv_globalgamma, 1);
 				return true;
 
 			// Spymode on F12 handled in game logic
@@ -3205,13 +3273,13 @@ static void M_DrawGenericScrollMenu(void)
 
 	for (i = 0; i < currentMenu->numitems; i++)
 	{
-		if (currentMenu->menuitems[i].alphaKey*2 + tempcentery >= currentMenu->y)
+		if (currentMenu->menuitems[i].status != IT_DISABLED && currentMenu->menuitems[i].alphaKey*2 + tempcentery >= currentMenu->y)
 			break;
 	}
 
 	for (max = currentMenu->numitems; max > 0; max--)
 	{
-		if (currentMenu->menuitems[max-1].alphaKey*2 + tempcentery <= (currentMenu->y + 2*scrollareaheight))
+		if (currentMenu->menuitems[max-1].status != IT_DISABLED && currentMenu->menuitems[max-1].alphaKey*2 + tempcentery <= (currentMenu->y + 2*scrollareaheight))
 			break;
 	}
 
@@ -3296,8 +3364,6 @@ static void M_DrawGenericScrollMenu(void)
 		W_CachePatchName("M_CURSOR", PU_CACHE));
 }
 
-#undef scrollareaheight
-
 static void M_DrawPauseMenu(void)
 {
 	if (!netgame && !multiplayer && (gamestate == GS_LEVEL || gamestate == GS_INTERMISSION))
@@ -4068,7 +4134,6 @@ void M_DrawLevelPlatterHeader(INT32 y, const char *header, boolean headerhighlig
 	{
 		V_DrawFill(19, y, 282, 1, 26);
 	}
-	y += 2;
 }
 
 static void M_DrawLevelPlatterWideMap(UINT8 row, UINT8 col, INT32 x, INT32 y, boolean highlight)
@@ -8182,6 +8247,117 @@ static void M_DrawVideoMode(void)
 		W_CachePatchName("M_CURSOR", PU_CACHE));
 }
 
+// Just M_DrawGenericScrollMenu but showing a backing behind the headers.
+static void M_DrawColorMenu(void)
+{
+	INT32 x, y, i, max, tempcentery, cursory = 0;
+
+	// DRAW MENU
+	x = currentMenu->x;
+	y = currentMenu->y;
+
+	if ((currentMenu->menuitems[itemOn].alphaKey*2 - currentMenu->menuitems[0].alphaKey*2) <= scrollareaheight)
+		tempcentery = currentMenu->y - currentMenu->menuitems[0].alphaKey*2;
+	else if ((currentMenu->menuitems[currentMenu->numitems-1].alphaKey*2 - currentMenu->menuitems[itemOn].alphaKey*2) <= scrollareaheight)
+		tempcentery = currentMenu->y - currentMenu->menuitems[currentMenu->numitems-1].alphaKey*2 + 2*scrollareaheight;
+	else
+		tempcentery = currentMenu->y - currentMenu->menuitems[itemOn].alphaKey*2 + scrollareaheight;
+
+	for (i = 0; i < currentMenu->numitems; i++)
+	{
+		if (currentMenu->menuitems[i].status != IT_DISABLED && currentMenu->menuitems[i].alphaKey*2 + tempcentery >= currentMenu->y)
+			break;
+	}
+
+	for (max = currentMenu->numitems; max > 0; max--)
+	{
+		if (currentMenu->menuitems[max].status != IT_DISABLED && currentMenu->menuitems[max-1].alphaKey*2 + tempcentery <= (currentMenu->y + 2*scrollareaheight))
+			break;
+	}
+
+	if (i)
+		V_DrawString(currentMenu->x - 20, currentMenu->y, V_YELLOWMAP, "\x1A"); // up arrow
+	if (max != currentMenu->numitems)
+		V_DrawString(currentMenu->x - 20, currentMenu->y + 2*scrollareaheight, V_YELLOWMAP, "\x1B"); // down arrow
+
+	// draw title (or big pic)
+	M_DrawMenuTitle();
+
+	for (; i < max; i++)
+	{
+		y = currentMenu->menuitems[i].alphaKey*2 + tempcentery;
+		if (i == itemOn)
+			cursory = y;
+		switch (currentMenu->menuitems[i].status & IT_DISPLAY)
+		{
+			case IT_PATCH:
+			case IT_DYBIGSPACE:
+			case IT_BIGSLIDER:
+			case IT_STRING2:
+			case IT_DYLITLSPACE:
+			case IT_GRAYPATCH:
+			case IT_TRANSTEXT2:
+				// unsupported
+				break;
+			case IT_NOTHING:
+				break;
+			case IT_STRING:
+			case IT_WHITESTRING:
+				if (i != itemOn && (currentMenu->menuitems[i].status & IT_DISPLAY)==IT_STRING)
+					V_DrawString(x, y, 0, currentMenu->menuitems[i].text);
+				else
+					V_DrawString(x, y, V_YELLOWMAP, currentMenu->menuitems[i].text);
+
+				// Cvar specific handling
+				switch (currentMenu->menuitems[i].status & IT_TYPE)
+					case IT_CVAR:
+					{
+						consvar_t *cv = (consvar_t *)currentMenu->menuitems[i].itemaction;
+						switch (currentMenu->menuitems[i].status & IT_CVARTYPE)
+						{
+							case IT_CV_SLIDER:
+								M_DrawSlider(x, y, cv);
+							case IT_CV_NOPRINT: // color use this
+							case IT_CV_INVISSLIDER: // monitor toggles use this
+								break;
+							case IT_CV_STRING:
+								if (y + 12 > (currentMenu->y + 2*scrollareaheight))
+									break;
+								M_DrawTextBox(x, y + 4, MAXSTRINGLENGTH, 1);
+								V_DrawString(x + 8, y + 12, V_ALLOWLOWERCASE, cv->string);
+								if (skullAnimCounter < 4 && i == itemOn)
+									V_DrawCharacter(x + 8 + V_StringWidth(cv->string, 0), y + 12,
+										'_' | 0x80, false);
+								y += 16;
+								break;
+							default:
+								V_DrawRightAlignedString(BASEVIDWIDTH - x, y,
+									((cv->flags & CV_CHEAT) && !CV_IsSetToDefault(cv) ? V_REDMAP : V_YELLOWMAP), cv->string);
+								break;
+						}
+						break;
+					}
+					break;
+			case IT_TRANSTEXT:
+				V_DrawString(x, y, V_TRANSLUCENT, currentMenu->menuitems[i].text);
+				break;
+			case IT_QUESTIONMARKS:
+				V_DrawString(x, y, V_TRANSLUCENT|V_OLDSPACING, M_CreateSecretMenuOption(currentMenu->menuitems[i].text));
+				break;
+			case IT_HEADERTEXT:
+				//V_DrawString(x-16, y, V_YELLOWMAP, currentMenu->menuitems[i].text);
+				V_DrawFill(19, y, 281, 9, currentMenu->menuitems[i+1].alphaKey);
+				V_DrawFill(300, y, 1, 9, 26);
+				M_DrawLevelPlatterHeader(y - (lsheadingheight - 12), currentMenu->menuitems[i].text, false);
+				break;
+		}
+	}
+
+	// DRAW THE SKULL CURSOR
+	V_DrawScaledPatch(currentMenu->x - 24, cursory, 0,
+		W_CachePatchName("M_CURSOR", PU_CACHE));
+}
+
 // special menuitem key handler for video mode list
 static void M_HandleVideoMode(INT32 ch)
 {
@@ -8270,7 +8446,8 @@ static void M_DrawMonitorToggles(void)
 	// Assumes all are cvar type.
 	for (i = 0; i < currentMenu->numitems; ++i)
 	{
-		cv = (consvar_t *)currentMenu->menuitems[i].itemaction;
+		if (!(currentMenu->menuitems[i].status & IT_CVAR) || !(cv = (consvar_t *)currentMenu->menuitems[i].itemaction))
+			continue;
 		sum += cv->value;
 
 		if (!CV_IsSetToDefault(cv))
@@ -8279,7 +8456,8 @@ static void M_DrawMonitorToggles(void)
 
 	for (i = 0; i < currentMenu->numitems; ++i)
 	{
-		cv = (consvar_t *)currentMenu->menuitems[i].itemaction;
+		if (!(currentMenu->menuitems[i].status & IT_CVAR) || !(cv = (consvar_t *)currentMenu->menuitems[i].itemaction))
+			continue;
 		y = currentMenu->y + currentMenu->menuitems[i].alphaKey;
 
 		M_DrawSlider(currentMenu->x + 20, y, cv);
diff --git a/src/v_video.c b/src/v_video.c
index 522f3eda905a97ba3ebd69aa18e6ac6f02abaab6..9e6a2745ee322d3f7c35556f404969a4c8aafbb8 100644
--- a/src/v_video.c
+++ b/src/v_video.c
@@ -38,14 +38,39 @@ UINT8 *screens[5];
 // screens[3] = fade screen start
 // screens[4] = fade screen end, postimage tempoarary buffer
 
-static CV_PossibleValue_t gamma_cons_t[] = {{-5, "MIN"}, {5, "MAX"}, {0, NULL}};
-static void CV_usegamma_OnChange(void);
-
 consvar_t cv_ticrate = {"showfps", "No", 0, CV_YesNo, NULL, 0, NULL, NULL, 0, 0, NULL};
-consvar_t cv_usegamma = {"gamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_usegamma_OnChange, 0, NULL, NULL, 0, 0, NULL};
+
+static void CV_palette_OnChange(void);
+
+static CV_PossibleValue_t gamma_cons_t[] = {{-15, "MIN"}, {5, "MAX"}, {0, NULL}};
+consvar_t cv_globalgamma = {"gamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
 
 static CV_PossibleValue_t saturation_cons_t[] = {{0, "MIN"}, {10, "MAX"}, {0, NULL}};
-consvar_t cv_usesaturation = {"saturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_usegamma_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_globalsaturation = {"saturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+
+#define huecoloursteps 4
+
+static CV_PossibleValue_t hue_cons_t[] = {{0, "MIN"}, {(huecoloursteps*6)-1, "MAX"}, {0, NULL}};
+consvar_t cv_rhue = {"rhue",  "0", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_yhue = {"yhue",  "4", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_ghue = {"ghue",  "8", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_chue = {"chue", "12", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_bhue = {"bhue", "16", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_mhue = {"mhue", "20", CV_SAVE|CV_CALL, hue_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+
+consvar_t cv_rgamma = {"rgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_ygamma = {"ygamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_ggamma = {"ggamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_cgamma = {"cgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_bgamma = {"bgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_mgamma = {"mgamma", "0", CV_SAVE|CV_CALL, gamma_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+
+consvar_t cv_rsaturation = {"rsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_ysaturation = {"ysaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_gsaturation = {"gsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_csaturation = {"csaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_bsaturation = {"bsaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
+consvar_t cv_msaturation = {"msaturation", "10", CV_SAVE|CV_CALL, saturation_cons_t, CV_palette_OnChange, 0, NULL, NULL, 0, 0, NULL};
 
 static CV_PossibleValue_t constextsize_cons_t[] = {
 	{V_NOSCALEPATCH, "Small"}, {V_SMALLSCALEPATCH, "Medium"}, {V_MEDSCALEPATCH, "Large"}, {0, "Huge"},
@@ -124,54 +149,136 @@ static boolean InitCube(void)
 			}
 		}
 	};
+	float desatur[3]; // grey
+	float globalgammamul, globalgammaoffs;
+	boolean doinggamma;
+
+#define diffcons(cv) (cv.value != atoi(cv.defaultvalue))
+
+	doinggamma = diffcons(cv_globalgamma);
 
-	if (cv_usegamma.value)
-	{
 #define gammascale 8
-		float gammamul = (255 - (gammascale*abs(cv_usegamma.value)))/255.0;
-		float gammaoffs = ((cv_usegamma.value > 0) ? ((gammascale*cv_usegamma.value)/255.0) : 0.0);
-#undef gammascale
+	globalgammamul = (cv_globalgamma.value ? ((255 - (gammascale*abs(cv_globalgamma.value)))/255.0) : 1.0);
+	globalgammaoffs = ((cv_globalgamma.value > 0) ? ((gammascale*cv_globalgamma.value)/255.0) : 0.0);
+	desatur[0] = desatur[1] = desatur[2] = globalgammaoffs + (0.33*globalgammamul);
+
+	if (doinggamma
+		|| diffcons(cv_rhue)
+		|| diffcons(cv_yhue)
+		|| diffcons(cv_ghue)
+		|| diffcons(cv_chue)
+		|| diffcons(cv_bhue)
+		|| diffcons(cv_mhue)
+		|| diffcons(cv_rgamma)
+		|| diffcons(cv_ygamma)
+		|| diffcons(cv_ggamma)
+		|| diffcons(cv_cgamma)
+		|| diffcons(cv_bgamma)
+		|| diffcons(cv_mgamma))
+	{
+		float mod, tempgammamul, tempgammaoffs;
 
 		apply = true;
 
-		#define dogamma(i, j, k, l) \
-			working[i][j][k][l]*= gammamul;\
-			working[i][j][k][l] += gammaoffs
-		for (q = 0; q < 3; q++)
-		{
-			dogamma(0, 0, 0, q);
-			dogamma(1, 0, 0, q);
-			dogamma(0, 1, 0, q);
-			dogamma(1, 1, 0, q);
-			dogamma(0, 0, 1, q);
-			dogamma(1, 0, 1, q);
-			dogamma(0, 1, 1, q);
-			dogamma(1, 1, 1, q);
+		working[0][0][0][0] = working[0][0][0][1] = working[0][0][0][2] = globalgammaoffs;
+		working[1][1][1][0] = working[1][1][1][1] = working[1][1][1][2] = globalgammaoffs+globalgammamul;
+
+#define dohue(hue, gamma, loc) \
+		tempgammamul = (gamma ? ((255 - (gammascale*abs(gamma)))/255.0)*globalgammamul : globalgammamul);\
+		tempgammaoffs = ((gamma > 0) ? ((gammascale*gamma)/255.0) + globalgammaoffs : globalgammaoffs);\
+		mod = ((hue % huecoloursteps)*(tempgammamul)/huecoloursteps);\
+		switch (hue/huecoloursteps)\
+		{\
+			case 0:\
+			default:\
+				loc[0] = tempgammaoffs+tempgammamul;\
+				loc[1] = tempgammaoffs+mod;\
+				loc[2] = tempgammaoffs;\
+				break;\
+			case 1:\
+				loc[0] = tempgammaoffs+tempgammamul-mod;\
+				loc[1] = tempgammaoffs+tempgammamul;\
+				loc[2] = tempgammaoffs;\
+				break;\
+			case 2:\
+				loc[0] = tempgammaoffs;\
+				loc[1] = tempgammaoffs+tempgammamul;\
+				loc[2] = tempgammaoffs+mod;\
+				break;\
+			case 3:\
+				loc[0] = tempgammaoffs;\
+				loc[1] = tempgammaoffs+tempgammamul-mod;\
+				loc[2] = tempgammaoffs+tempgammamul;\
+				break;\
+			case 4:\
+				loc[0] = tempgammaoffs+mod;\
+				loc[1] = tempgammaoffs;\
+				loc[2] = tempgammaoffs+tempgammamul;\
+				break;\
+			case 5:\
+				loc[0] = tempgammaoffs+tempgammamul;\
+				loc[1] = tempgammaoffs;\
+				loc[2] = tempgammaoffs+tempgammamul-mod;\
+				break;\
 		}
-#undef dogamma
+		dohue(cv_rhue.value, cv_rgamma.value, working[1][0][0]);
+		dohue(cv_yhue.value, cv_ygamma.value, working[1][1][0]);
+		dohue(cv_ghue.value, cv_ggamma.value, working[0][1][0]);
+		dohue(cv_chue.value, cv_cgamma.value, working[0][1][1]);
+		dohue(cv_bhue.value, cv_bgamma.value, working[0][0][1]);
+		dohue(cv_mhue.value, cv_mgamma.value, working[1][0][1]);
+#undef dohue
 	}
 
-	if (cv_usesaturation.value != 10)
+#define dosaturation(a, e) a = ((1 - work)*e + work*a)
+#define docvsat(cv_sat, hue, gamma, r, g, b) \
+	if diffcons(cv_sat)\
+	{\
+		float work, mod, tempgammamul, tempgammaoffs;\
+		apply = true;\
+		work = (cv_sat.value/10.0);\
+		mod = ((hue % huecoloursteps)*(1.0)/huecoloursteps);\
+		if (hue & huecoloursteps)\
+			mod = 2-mod;\
+		else\
+			mod += 1;\
+		tempgammamul = (gamma ? ((255 - (gammascale*abs(gamma)))/255.0)*globalgammamul : globalgammamul);\
+		tempgammaoffs = ((gamma > 0) ? ((gammascale*gamma)/255.0) + globalgammaoffs : globalgammaoffs);\
+		for (q = 0; q < 3; q++)\
+			dosaturation(working[r][g][b][q], (tempgammaoffs+(desatur[q]*mod*tempgammamul)));\
+	}
+
+	docvsat(cv_rsaturation, cv_rhue.value, cv_rgamma.value, 1, 0, 0);
+	docvsat(cv_ysaturation, cv_yhue.value, cv_ygamma.value, 1, 1, 0);
+	docvsat(cv_gsaturation, cv_ghue.value, cv_ggamma.value, 0, 1, 0);
+	docvsat(cv_csaturation, cv_chue.value, cv_cgamma.value, 0, 1, 1);
+	docvsat(cv_bsaturation, cv_bhue.value, cv_bgamma.value, 0, 0, 1);
+	docvsat(cv_msaturation, cv_mhue.value, cv_mgamma.value, 1, 0, 1);
+
+#undef gammascale
+
+	if diffcons(cv_globalsaturation)
 	{
-		float desatur[3] = {0.33, 0.33, 0.33}; // grey
-		float work = (cv_usesaturation.value/10.0);
+		float work = (cv_globalsaturation.value/10.0);
 
 		apply = true;
 
-#define dosaturation(a, e) a = ((1 - work)*e + work*a)
 		for (q = 0; q < 3; q++)
 		{
-			dosaturation(working[0][0][1][q], desatur[q]);
-			dosaturation(working[0][1][0][q], desatur[q]);
 			dosaturation(working[1][0][0][q], desatur[q]);
+			dosaturation(working[0][1][0][q], desatur[q]);
+			dosaturation(working[0][0][1][q], desatur[q]);
 
 			dosaturation(working[1][1][0][q], 2*desatur[q]);
-			dosaturation(working[1][0][1][q], 2*desatur[q]);
 			dosaturation(working[0][1][1][q], 2*desatur[q]);
+			dosaturation(working[1][0][1][q], 2*desatur[q]);
 		}
-#undef dosaturation
 	}
 
+#undef dosaturation
+
+#undef diffcons
+
 	if (!apply)
 		return false;
 
@@ -315,7 +422,7 @@ void V_SetPaletteLump(const char *pal)
 		I_SetPalette(pLocalPalette);
 }
 
-static void CV_usegamma_OnChange(void)
+static void CV_palette_OnChange(void)
 {
 	// reload palette
 	LoadMapPalette();
diff --git a/src/v_video.h b/src/v_video.h
index f1761162f12994f81118f99d5c8bc6f4bedfb0ed..5d47152354ed278bbca9e7d85e191671e97abf90 100644
--- a/src/v_video.h
+++ b/src/v_video.h
@@ -27,7 +27,11 @@
 
 extern UINT8 *screens[5];
 
-extern consvar_t cv_ticrate, cv_usegamma, cv_usesaturation, cv_constextsize;
+extern consvar_t cv_ticrate, cv_constextsize,\
+cv_globalgamma, cv_globalsaturation, \
+cv_rhue, cv_yhue, cv_ghue, cv_chue, cv_bhue, cv_mhue,\
+cv_rgamma, cv_ygamma, cv_ggamma, cv_cgamma, cv_bgamma, cv_mgamma, \
+cv_rsaturation, cv_ysaturation, cv_gsaturation, cv_csaturation, cv_bsaturation, cv_msaturation;
 
 // Allocates buffer screens, call before R_Init.
 void V_Init(void);