diff --git a/src/d_main.c b/src/d_main.c
index 7ffa7da7e71769b6928ff67292610aa5dc42b469..bbc5f532108d2279365ee43ba3c031b340626368 100644
--- a/src/d_main.c
+++ b/src/d_main.c
@@ -668,7 +668,6 @@ void D_AdvanceDemo(void)
 void D_StartTitle(void)
 {
 	INT32 i;
-	boolean tutorialpostprompt = false;
 
 	S_StopMusic();
 
@@ -713,16 +712,6 @@ void D_StartTitle(void)
 	// reset modeattacking
 	modeattacking = ATTACKING_NONE;
 
-	// The title screen is obviously not a tutorial! (Unless I'm mistaken)
-	if (tutorialmode)
-	{
-		// check if retained controls are custom
-		tutorialpostprompt = (G_GetControlScheme(gamecontroldefault[gcs_custom], gclist_tutorial, num_gclist_tutorial) == gcs_custom
-			&& G_GetControlScheme(gamecontrol, gclist_tutorial, num_gclist_tutorial) != gcs_custom);
-		G_CopyControls(gamecontrol, gamecontroldefault[gcs_custom], gclist_tutorial, num_gclist_tutorial); // using gcs_custom as temp storage
-	}
-	tutorialmode = false;
-
 	// empty maptol so mario/etc sounds don't play in sound test when they shouldn't
 	maptol = 0;
 
@@ -746,9 +735,18 @@ void D_StartTitle(void)
 	if (rendermode != render_none)
 		V_SetPaletteLump("PLAYPAL");
 
-	if (tutorialpostprompt)
-		M_StartMessage("Do you want to \x82save the recommended \x82movement controls?\x80\n\nPress 'Y' or 'Enter' to confirm, \nor any key to keep \nyour current controls.",
+	// The title screen is obviously not a tutorial! (Unless I'm mistaken)
+	if (tutorialmode && tutorialgcs)
+	{
+		G_CopyControls(gamecontrol, gamecontroldefault[gcs_custom], gclist_tutorial, num_gclist_tutorial); // using gcs_custom as temp storage
+		CV_SetValue(&cv_usemouse, tutorialusemouse);
+		CV_SetValue(&cv_alwaysfreelook, tutorialfreelook);
+		CV_SetValue(&cv_mousemove, tutorialmousemove);
+		CV_SetValue(&cv_analog, tutorialanalog);
+		M_StartMessage("Do you want to \x82save the recommended \x82movement controls?\x80\n\nPress 'Y' or 'Enter' to confirm\nPress 'N' or any key to keep \nyour current controls",
 			M_TutorialSaveControlResponse, MM_YESNO);
+	}
+	tutorialmode = false;
 }
 
 //
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index b584906fdb9606b7051d3ec8781359f7552afa86..dffb1510b4a19609f94207ce2502f21b393f2de3 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -1809,8 +1809,14 @@ static void Command_Map_f(void)
 	else
 		fromlevelselect = ((netgame || multiplayer) && ((gametype == newgametype) && (newgametype == GT_COOP)));
 
-	if (tutorialmode)
+	if (tutorialmode && tutorialgcs)
+	{
 		G_CopyControls(gamecontrol, gamecontroldefault[gcs_custom], gclist_tutorial, num_gclist_tutorial); // using gcs_custom as temp storage
+		CV_SetValue(&cv_usemouse, tutorialusemouse);
+		CV_SetValue(&cv_alwaysfreelook, tutorialfreelook);
+		CV_SetValue(&cv_mousemove, tutorialmousemove);
+		CV_SetValue(&cv_analog, tutorialanalog);
+	}
 	tutorialmode = false; // warping takes us out of tutorial mode
 
 	D_MapChange(newmapnum, newgametype, false, newresetplayers, 0, false, fromlevelselect);
diff --git a/src/doomstat.h b/src/doomstat.h
index 421b6579fd856d5d43e77ece03926241dede1021..7660547685426bd01cfb84ef5aa5f28b49b6fad2 100644
--- a/src/doomstat.h
+++ b/src/doomstat.h
@@ -131,6 +131,11 @@ extern INT16 bootmap; //bootmap for loading a map on startup
 
 extern INT16 tutorialmap; // map to load for tutorial
 extern boolean tutorialmode; // are we in a tutorial right now?
+extern INT32 tutorialgcs; // which control scheme is loaded?
+extern INT32 tutorialusemouse; // store cv_usemouse user value
+extern INT32 tutorialfreelook; // store cv_alwaysfreelook user value
+extern INT32 tutorialmousemove; // store cv_mousemove user value
+extern INT32 tutorialanalog; // store cv_analog user value
 
 extern boolean looptitle;
 
diff --git a/src/g_game.c b/src/g_game.c
index 38289bcb234f9ed46021a3c803619ff52041d410..4fb56abaf95e1a5e12d9eb748120e10e478dc04f 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -129,6 +129,11 @@ INT16 bootmap; //bootmap for loading a map on startup
 
 INT16 tutorialmap = 0; // map to load for tutorial
 boolean tutorialmode = false; // are we in a tutorial right now?
+INT32 tutorialgcs = gcs_custom; // which control scheme is loaded?
+INT32 tutorialusemouse = 0; // store cv_usemouse user value
+INT32 tutorialfreelook = 0; // store cv_alwaysfreelook user value
+INT32 tutorialmousemove = 0; // store cv_mousemove user value
+INT32 tutorialanalog = 0; // store cv_analog user value
 
 boolean looptitle = false;
 
diff --git a/src/g_input.c b/src/g_input.c
index d8a99c97dd9d9d81062c1bba5a2de21e1177a50a..35a0b82cf7cc427935c802d4bf5d5c2d78dff743 100644
--- a/src/g_input.c
+++ b/src/g_input.c
@@ -659,20 +659,20 @@ void G_DefineDefaultControls(void)
 	gamecontroldefault[gcs_fps][gc_firenormal ][0] = 'c';
 
 	// Platform game controls (arrow keys)
-	// gamecontroldefault[gcs_platform][gc_forward    ][0] = KEY_UPARROW;
-	// gamecontroldefault[gcs_platform][gc_backward   ][0] = KEY_DOWNARROW;
-	// gamecontroldefault[gcs_platform][gc_strafeleft ][0] = 'a';
-	// gamecontroldefault[gcs_platform][gc_straferight][0] = 'd';
-	// gamecontroldefault[gcs_platform][gc_lookup     ][0] = KEY_PGUP;
-	// gamecontroldefault[gcs_platform][gc_lookdown   ][0] = KEY_PGDN;
-	// gamecontroldefault[gcs_platform][gc_turnleft   ][0] = KEY_LEFTARROW;
-	// gamecontroldefault[gcs_platform][gc_turnright  ][0] = KEY_RIGHTARROW;
-	// gamecontroldefault[gcs_platform][gc_centerview ][0] = KEY_END;
-	// gamecontroldefault[gcs_platform][gc_jump       ][0] = KEY_SPACE;
-	// gamecontroldefault[gcs_platform][gc_use        ][0] = KEY_LSHIFT;
-	// gamecontroldefault[gcs_platform][gc_fire       ][0] = 's';
-	// gamecontroldefault[gcs_platform][gc_fire       ][1] = KEY_MOUSE1+0;
-	// gamecontroldefault[gcs_platform][gc_firenormal ][0] = 'w';
+	gamecontroldefault[gcs_platform][gc_forward    ][0] = KEY_UPARROW;
+	gamecontroldefault[gcs_platform][gc_backward   ][0] = KEY_DOWNARROW;
+	gamecontroldefault[gcs_platform][gc_strafeleft ][0] = 'a';
+	gamecontroldefault[gcs_platform][gc_straferight][0] = 'd';
+	gamecontroldefault[gcs_platform][gc_lookup     ][0] = KEY_PGUP;
+	gamecontroldefault[gcs_platform][gc_lookdown   ][0] = KEY_PGDN;
+	gamecontroldefault[gcs_platform][gc_turnleft   ][0] = KEY_LEFTARROW;
+	gamecontroldefault[gcs_platform][gc_turnright  ][0] = KEY_RIGHTARROW;
+	gamecontroldefault[gcs_platform][gc_centerview ][0] = KEY_END;
+	gamecontroldefault[gcs_platform][gc_jump       ][0] = KEY_SPACE;
+	gamecontroldefault[gcs_platform][gc_use        ][0] = KEY_LSHIFT;
+	gamecontroldefault[gcs_platform][gc_fire       ][0] = 's';
+	gamecontroldefault[gcs_platform][gc_fire       ][1] = KEY_MOUSE1+0;
+	gamecontroldefault[gcs_platform][gc_firenormal ][0] = 'w';
 
 	for (i = 1; i < num_gamecontrolschemes; i++) // skip gcs_custom (0)
 	{
diff --git a/src/g_input.h b/src/g_input.h
index 01acf3ae0b90f3b17c44b54259a960728ba920fc..ed347705aa596154ed785b6e7cc4e73c4bd5b42c 100644
--- a/src/g_input.h
+++ b/src/g_input.h
@@ -103,7 +103,7 @@ typedef enum
 {
 	gcs_custom,
 	gcs_fps,
-	//gcs_platform,
+	gcs_platform,
 	num_gamecontrolschemes
 } gamecontrolschemes_e;
 
diff --git a/src/m_menu.c b/src/m_menu.c
index e6bba723d8febe955febe26d081d4593b61f0554..547db7699fb30ecad1a951dd64e72660faf0280d 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -6141,7 +6141,11 @@ void M_TutorialSaveControlResponse(INT32 ch)
 {
 	if (ch == 'y' || ch == KEY_ENTER)
 	{
-		G_CopyControls(gamecontrol, gamecontroldefault[gcs_fps], gclist_tutorial, num_gclist_tutorial);
+		G_CopyControls(gamecontrol, gamecontroldefault[tutorialgcs], gclist_tutorial, num_gclist_tutorial);
+		CV_Set(&cv_usemouse, cv_usemouse.defaultvalue);
+		CV_Set(&cv_alwaysfreelook, cv_alwaysfreelook.defaultvalue);
+		CV_Set(&cv_mousemove, cv_mousemove.defaultvalue);
+		CV_Set(&cv_analog, cv_analog.defaultvalue);
 		S_StartSound(NULL, sfx_itemup);
 	}
 	else
@@ -6150,17 +6154,35 @@ void M_TutorialSaveControlResponse(INT32 ch)
 
 static void M_TutorialControlResponse(INT32 ch)
 {
-	if (ch == 'y' || ch == KEY_ENTER)
+	if (ch != KEY_ESCAPE)
 	{
-		G_CopyControls(gamecontroldefault[gcs_custom], gamecontrol, NULL, 0);
-		G_CopyControls(gamecontrol, gamecontroldefault[gcs_fps], gclist_tutorial, num_gclist_tutorial);
-		//S_StartSound(NULL, sfx_itemup);
+		G_CopyControls(gamecontroldefault[gcs_custom], gamecontrol, NULL, 0); // using gcs_custom as temp storage for old controls
+		if (ch == 'y' || ch == KEY_ENTER)
+		{
+			tutorialgcs = gcs_fps;
+			tutorialusemouse = cv_usemouse.value;
+			tutorialfreelook = cv_alwaysfreelook.value;
+			tutorialmousemove = cv_mousemove.value;
+			tutorialanalog = cv_analog.value;
+
+			G_CopyControls(gamecontrol, gamecontroldefault[tutorialgcs], gclist_tutorial, num_gclist_tutorial);
+			CV_Set(&cv_usemouse, cv_usemouse.defaultvalue);
+			CV_Set(&cv_alwaysfreelook, cv_alwaysfreelook.defaultvalue);
+			CV_Set(&cv_mousemove, cv_mousemove.defaultvalue);
+			CV_Set(&cv_analog, cv_analog.defaultvalue);
+
+			//S_StartSound(NULL, sfx_itemup);
+		}
+		else
+		{
+			tutorialgcs = gcs_custom;
+			S_StartSound(NULL, sfx_menu1);
+		}
+
+		M_StartTutorial(INT32_MAX);
 	}
 	else
 		S_StartSound(NULL, sfx_menu1);
-
-	if (ch != KEY_ESCAPE)
-		M_StartTutorial(INT32_MAX);
 }
 
 // Starts up the tutorial immediately (tbh I wasn't sure where else to put this)
@@ -6171,11 +6193,11 @@ static void M_StartTutorial(INT32 choice)
 
 	if (choice != INT32_MAX && G_GetControlScheme(gamecontrol, gclist_tutorial_check, num_gclist_tutorial_check) == gcs_custom)
 	{
-		M_StartMessage("Do you want to try the \202recommended \202movement controls\x80?\n\nWe will set them just for this tutorial.\n\nPress 'Y' or 'Enter' to confirm, \nor any key to keep \nyour current controls.\n",M_TutorialControlResponse,MM_YESNO);
+		M_StartMessage("Do you want to try the \202recommended \202movement controls\x80?\n\nWe will set them just for this tutorial.\n\nPress 'Y' or 'Enter' to confirm\nPress 'N' or any key to keep \nyour current controls.\n",M_TutorialControlResponse,MM_YESNO);
 		return;
 	}
 	else if (choice != INT32_MAX)
-		G_CopyControls(gamecontroldefault[gcs_custom], gamecontrol, NULL, 0);
+		tutorialgcs = gcs_custom;
 
 	CV_SetValue(&cv_postfirsttime, 1);
 
@@ -9288,9 +9310,17 @@ static void M_DrawControl(void)
 	// draw title (or big pic)
 	M_DrawMenuTitle();
 
-	M_CentreText(30,
-		 (setupcontrols_secondaryplayer ? "SET CONTROLS FOR SECONDARY PLAYER" :
-		                                  "PRESS ENTER TO CHANGE, BACKSPACE TO CLEAR"));
+	if (tutorialmode && tutorialgcs)
+	{
+		if ((gametic / TICRATE) % 2)
+			M_CentreText(30, "\202EXIT THE TUTORIAL TO CHANGE THE CONTROLS");
+		else
+			M_CentreText(30, "EXIT THE TUTORIAL TO CHANGE THE CONTROLS");
+	}
+	else
+		M_CentreText(30,
+		    (setupcontrols_secondaryplayer ? "SET CONTROLS FOR SECONDARY PLAYER" :
+		                                     "PRESS ENTER TO CHANGE, BACKSPACE TO CLEAR"));
 
 	if (i)
 		V_DrawString(currentMenu->x - 16, y-(skullAnimCounter/5), V_YELLOWMAP, "\x1A"); // up arrow
@@ -9425,6 +9455,9 @@ static void M_ChangeControl(INT32 choice)
 {
 	static char tmp[55];
 
+	if (tutorialmode && tutorialgcs) // don't allow control changes if temp control override is active
+		return;
+
 	controltochange = currentMenu->menuitems[choice].alphaKey;
 	sprintf(tmp, M_GetText("Hit the new key for\n%s\nESC for Cancel"),
 		currentMenu->menuitems[choice].text);
diff --git a/src/m_misc.c b/src/m_misc.c
index cc9ae2999c5ab86c08ce5c5f7088d5ef3e7898bc..92c2aeea9dd3458d66edc0f8495119542859998c 100644
--- a/src/m_misc.c
+++ b/src/m_misc.c
@@ -539,10 +539,24 @@ void M_SaveConfig(const char *filename)
 
 	// FIXME: save key aliases if ever implemented..
 
-	CV_SaveVariables(f);
+	if (tutorialmode && tutorialgcs)
+	{
+		CV_SetValue(&cv_usemouse, tutorialusemouse);
+		CV_SetValue(&cv_alwaysfreelook, tutorialfreelook);
+		CV_SetValue(&cv_mousemove, tutorialmousemove);
+		CV_SetValue(&cv_analog, tutorialanalog);
+		CV_SaveVariables(f);
+		CV_Set(&cv_usemouse, cv_usemouse.defaultvalue);
+		CV_Set(&cv_alwaysfreelook, cv_alwaysfreelook.defaultvalue);
+		CV_Set(&cv_mousemove, cv_mousemove.defaultvalue);
+		CV_Set(&cv_analog, cv_analog.defaultvalue);
+	}
+	else
+		CV_SaveVariables(f);
+
 	if (!dedicated)
 	{
-		if (tutorialmode)
+		if (tutorialmode && tutorialgcs)
 			G_SaveKeySetting(f, gamecontroldefault[gcs_custom], gamecontrolbis); // using gcs_custom as temp storage
 		else
 			G_SaveKeySetting(f, gamecontrol, gamecontrolbis);
diff --git a/src/p_user.c b/src/p_user.c
index 57ee9169769a782bfb751ca962284d0efc8cc32a..e0534395a5ac754c2c68e1950cda6a9a663b3df0 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -8848,7 +8848,16 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 	if (P_CameraThinker(player, thiscam, resetcalled))
 		return true;
 
-	if (thiscam == &camera)
+	if (tutorialmode)
+	{
+		// force defaults because we have a camera look section
+		camspeed = (INT32)(atof(cv_cam_speed.defaultvalue) * FRACUNIT);
+		camstill = (!stricmp(cv_cam_still.defaultvalue, "off")) ? false : true;
+		camrotate = atoi(cv_cam_rotate.defaultvalue);
+		camdist = FixedMul((INT32)(atof(cv_cam_dist.defaultvalue) * FRACUNIT), mo->scale);
+		camheight = FixedMul((INT32)(atof(cv_cam_height.defaultvalue) * FRACUNIT), FixedMul(player->camerascale, mo->scale));
+	}
+	else if (thiscam == &camera)
 	{
 		camspeed = cv_cam_speed.value;
 		camstill = cv_cam_still.value;