diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index c0dec7ff73483152159a3148dc7395051bb91409..cad27f52c199d17e86c750b0069ebc4f4c9e267d 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -2486,6 +2486,17 @@ static void CL_RemovePlayer(INT32 playernum, INT32 reason)
 	(void)reason;
 #endif
 
+	// don't look through someone's view who isn't there
+	if (playernum == displayplayer)
+	{
+#ifdef HAVE_BLUA
+		// Call ViewpointSwitch hooks here.
+		// The viewpoint was forcibly changed.
+		LUAh_ViewpointSwitch(&players[consoleplayer], &players[displayplayer], true);
+#endif
+		displayplayer = consoleplayer;
+	}
+
 	// Reset player data
 	CL_ClearPlayer(playernum);
 
@@ -2503,9 +2514,6 @@ static void CL_RemovePlayer(INT32 playernum, INT32 reason)
 		RemoveAdminPlayer(playernum); // don't stay admin after you're gone
 	}
 
-	if (playernum == displayplayer)
-		displayplayer = consoleplayer; // don't look through someone's view who isn't there
-
 #ifdef HAVE_BLUA
 	LUA_InvalidatePlayer(&players[playernum]);
 #endif
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index be8b90c789f00237df220fd50eb0478d97c33df9..4e14ca25fea0b0d5318af8bbff3fb4bb5105b9a2 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -2900,7 +2900,15 @@ static void Got_Teamchange(UINT8 **cp, INT32 playernum)
 
 	//reset view if you are changed, or viewing someone who was changed.
 	if (playernum == consoleplayer || displayplayer == playernum)
+	{
+#ifdef HAVE_BLUA
+		// Call ViewpointSwitch hooks here.
+		// The viewpoint was forcibly changed.
+		if (displayplayer != consoleplayer) // You're already viewing yourself. No big deal.
+			LUAh_ViewpointSwitch(&players[playernum], &players[displayplayer], true);
+#endif
 		displayplayer = consoleplayer;
+	}
 
 	if (G_GametypeHasTeams())
 	{
diff --git a/src/g_game.c b/src/g_game.c
index 34524c8f996c42d7737220ccee7ab8dbdf083084..ead7e8c5c95a059ed2c537c387275032683b32d4 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1379,7 +1379,14 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics)
 	//Reset away view if a command is given.
 	if ((cmd->forwardmove || cmd->sidemove || cmd->buttons)
 		&& displayplayer != consoleplayer)
+	{
+#ifdef HAVE_BLUA
+		// Call ViewpointSwitch hooks here.
+		// The viewpoint was forcibly changed.
+		LUAh_ViewpointSwitch(player, &players[displayplayer], true);
+#endif
 		displayplayer = consoleplayer;
+	}
 }
 
 // like the g_buildticcmd 1 but using mouse2, gamcontrolbis, ...
@@ -2021,6 +2028,11 @@ boolean G_Responder(event_t *ev)
 	if (gamestate == GS_LEVEL && ev->type == ev_keydown
 		&& (ev->data1 == KEY_F12 || ev->data1 == gamecontrol[gc_viewpoint][0] || ev->data1 == gamecontrol[gc_viewpoint][1]))
 	{
+		// ViewpointSwitch Lua hook.
+#ifdef HAVE_BLUA
+		UINT8 canSwitchView = 0;
+#endif
+
 		if (splitscreen || !netgame)
 			displayplayer = consoleplayer;
 		else
@@ -2036,13 +2048,12 @@ boolean G_Responder(event_t *ev)
 					continue;
 
 #ifdef HAVE_BLUA
-				{
-					UINT8 canSwitchView = LUAh_ViewpointSwitch(&players[consoleplayer], &players[displayplayer]);
-					if (canSwitchView == 1) // Set viewpoint to this player
-						break;
-					else if (canSwitchView == 2) // Skip this player
-						continue;
-				}
+				// Call ViewpointSwitch hooks here.
+				canSwitchView = LUAh_ViewpointSwitch(&players[consoleplayer], &players[displayplayer], false);
+				if (canSwitchView == 1) // Set viewpoint to this player
+					break;
+				else if (canSwitchView == 2) // Skip this player
+					continue;
 #endif
 
 				if (players[displayplayer].spectator)
diff --git a/src/lua_hook.h b/src/lua_hook.h
index 8d780490e6c6c8475736c076dc13c4fe4811daeb..68efbce93d89a0e81cd0d7cd0ede54e2a5e09b05 100644
--- a/src/lua_hook.h
+++ b/src/lua_hook.h
@@ -96,6 +96,6 @@ UINT8 LUAh_PlayerCanDamage(player_t *player, mobj_t *mobj); // Hook for P_Player
 void LUAh_PlayerQuit(player_t *plr, int reason); // Hook for player quitting
 void LUAh_IntermissionThinker(void); // Hook for Y_Ticker
 boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble); // Hook for team switching in... uh....
-UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer); // Hook for spy mode in G_Responder
+UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); // Hook for spy mode
 
 #endif
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index d213bd307ee099f0ea599db4d1295e98e6ee8168..5383cbf0bf5d0ceeeba34d2b8a615c3e2408fd89 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -1397,8 +1397,8 @@ boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, b
 	return canSwitchTeam;
 }
 
-// Hook for spy mode in G_Responder
-UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer)
+// Hook for spy mode
+UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced)
 {
 	hook_p hookp;
 	UINT8 canSwitchView = 0; // 0 = default, 1 = force yes, 2 = force no.
@@ -1417,12 +1417,14 @@ UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer)
 		{
 			LUA_PushUserdata(gL, player, META_PLAYER);
 			LUA_PushUserdata(gL, newdisplayplayer, META_PLAYER);
+			lua_pushboolean(gL, forced);
 		}
 		lua_pushfstring(gL, FMT_HOOKID, hookp->id);
 		lua_gettable(gL, LUA_REGISTRYINDEX);
-		lua_pushvalue(gL, -3);
-		lua_pushvalue(gL, -3);
-		if (lua_pcall(gL, 2, 1, 0)) {
+		lua_pushvalue(gL, -4);
+		lua_pushvalue(gL, -4);
+		lua_pushvalue(gL, -4);
+		if (lua_pcall(gL, 3, 1, 0)) {
 			if (!hookp->error || cv_debug & DBG_LUA)
 				CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
 			lua_pop(gL, 1);
diff --git a/src/p_user.c b/src/p_user.c
index c8a329c358e02c2b65073cf7ba65352fe32a9a8a..4dbea87e3facdb4ec046d72017ffd61c6874ae68 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -10380,7 +10380,14 @@ boolean P_SpectatorJoinGame(player_t *player)
 
 		//Reset away view
 		if (P_IsLocalPlayer(player) && displayplayer != consoleplayer)
+		{
+#ifdef HAVE_BLUA
+			// Call ViewpointSwitch hooks here.
+			// The viewpoint was forcibly changed.
+			LUAh_ViewpointSwitch(player, &players[displayplayer], true);
+#endif
 			displayplayer = consoleplayer;
+		}
 
 		if (changeto == 1)
 			CONS_Printf(M_GetText("%s switched to the %c%s%c.\n"), player_names[player-players], '\x85', M_GetText("Red team"), '\x80');
@@ -10422,7 +10429,14 @@ boolean P_SpectatorJoinGame(player_t *player)
 
 			//Reset away view
 			if (P_IsLocalPlayer(player) && displayplayer != consoleplayer)
+			{
+#ifdef HAVE_BLUA
+				// Call ViewpointSwitch hooks here.
+				// The viewpoint was forcibly changed.
+				LUAh_ViewpointSwitch(player, &players[displayplayer], true);
+#endif
 				displayplayer = consoleplayer;
+			}
 
 			if (gametype != GT_COOP)
 				CONS_Printf(M_GetText("%s entered the game.\n"), player_names[player-players]);