diff --git a/src/d_clisrv.c b/src/d_clisrv.c
index ed0b8e528ef1462ada824a9f7764168b18a96b1e..c01ca35dbb0d83c4d8f866477e8a8537308c59db 100644
--- a/src/d_clisrv.c
+++ b/src/d_clisrv.c
@@ -522,6 +522,9 @@ static inline void resynch_write_player(resynch_pak *rsp, const size_t i)
 	rsp->pflags = (UINT32)LONG(players[i].pflags); //pflags_t
 	rsp->panim  = (UINT8)players[i].panim; //panim_t
 
+	rsp->angleturn = (INT16)SHORT(players[i].angleturn);
+	rsp->oldrelangleturn = (INT16)SHORT(players[i].oldrelangleturn);
+
 	rsp->aiming = (angle_t)LONG(players[i].aiming);
 	rsp->currentweapon = LONG(players[i].currentweapon);
 	rsp->ringweapons = LONG(players[i].ringweapons);
@@ -663,6 +666,9 @@ static void resynch_read_player(resynch_pak *rsp)
 	players[i].pflags = (UINT32)LONG(rsp->pflags); //pflags_t
 	players[i].panim  = (UINT8)rsp->panim; //panim_t
 
+	players[i].angleturn = (INT16)SHORT(rsp->angleturn);
+	players[i].oldrelangleturn = (INT16)SHORT(rsp->oldrelangleturn);
+
 	players[i].aiming = (angle_t)LONG(rsp->aiming);
 	players[i].currentweapon = LONG(rsp->currentweapon);
 	players[i].ringweapons = LONG(rsp->ringweapons);
@@ -3346,6 +3352,7 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
 			displayplayer = newplayernum;
 			secondarydisplayplayer = newplayernum;
 			DEBFILE("spawning me\n");
+			ticcmd_oldangleturn[0] = newplayer->oldrelangleturn;
 		}
 		else
 		{
@@ -3353,7 +3360,9 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
 			DEBFILE("spawning my brother\n");
 			if (botingame)
 				newplayer->bot = 1;
+			ticcmd_oldangleturn[1] = newplayer->oldrelangleturn;
 		}
+		P_ForceLocalAngle(newplayer, (angle_t)(newplayer->angleturn << 16));
 		D_SendPlayerConfig();
 		addedtogame = true;
 
@@ -3361,11 +3370,6 @@ static void Got_AddPlayer(UINT8 **p, INT32 playernum)
 		{
 			if (newplayer->mo)
 			{
-				if (!splitscreenplayer)
-					localangle = newplayer->mo->angle;
-				else
-					localangle2 = newplayer->mo->angle;
-
 				newplayer->viewheight = 41*newplayer->height/48;
 
 				if (newplayer->mo->eflags & MFE_VERTICALFLIP)
@@ -4721,41 +4725,6 @@ static void Local_Maketic(INT32 realtics)
 	localcmds2.angleturn |= TICCMD_RECEIVED;
 }
 
-// This function is utter bullshit and is responsible for
-// the random desynch that happens when a player spawns.
-// This is because ticcmds are resent to clients if a packet
-// was dropped, and thus modifying them can lead to several
-// clients having their ticcmds set to different values.
-void SV_SpawnPlayer(INT32 playernum, INT32 x, INT32 y, angle_t angle)
-{
-	tic_t tic;
-	UINT8 numadjust = 0;
-
-	(void)x;
-	(void)y;
-
-	// Revisionist history: adjust the angles in the ticcmds received
-	// for this player, because they actually preceded the player
-	// spawning, but will be applied afterwards.
-
-	for (tic = server ? maketic : (neededtic - 1); tic >= gametic; tic--)
-	{
-		if (numadjust++ == BACKUPTICS)
-		{
-			DEBFILE(va("SV_SpawnPlayer: All netcmds for player %d adjusted!\n", playernum));
-			// We already adjusted them all, waste of time doing the same thing over and over
-			// This shouldn't happen normally though, either gametic was 0 (which is handled now anyway)
-			// or maketic >= gametic + BACKUPTICS
-			// -- Monster Iestyn 16/01/18
-			break;
-		}
-		netcmds[tic%BACKUPTICS][playernum].angleturn = (INT16)((angle>>16) | TICCMD_RECEIVED);
-
-		if (!tic) // failsafe for gametic == 0 -- Monster Iestyn 16/01/18
-			break;
-	}
-}
-
 // create missed tic
 static void SV_Maketic(void)
 {
diff --git a/src/d_clisrv.h b/src/d_clisrv.h
index 463240a2adc87289f8e00f319c7f90df062bf5f2..25274e97c43870dfdc033396a7036f4de26ed63c 100644
--- a/src/d_clisrv.h
+++ b/src/d_clisrv.h
@@ -171,6 +171,9 @@ typedef struct
 	UINT32 pflags; // pflags_t
 	UINT8 panim; // panim_t
 
+	INT16 angleturn;
+	INT16 oldrelangleturn;
+
 	angle_t aiming;
 	INT32 currentweapon;
 	INT32 ringweapons;
@@ -534,7 +537,6 @@ void NetUpdate(void);
 
 void SV_StartSinglePlayerServer(void);
 boolean SV_SpawnServer(void);
-void SV_SpawnPlayer(INT32 playernum, INT32 x, INT32 y, angle_t angle);
 void SV_StopServer(void);
 void SV_ResetServer(void);
 void CL_AddSplitscreenPlayer(void);
diff --git a/src/d_player.h b/src/d_player.h
index e5c7e72980d644dc926ca8e792f1136a54fd3b16..745f7b42f5977846b007d25695931285550cc1c2 100644
--- a/src/d_player.h
+++ b/src/d_player.h
@@ -332,6 +332,9 @@ typedef struct player_s
 
 	angle_t viewrollangle;
 
+	INT16 angleturn;
+	INT16 oldrelangleturn;
+
 	// Mouse aiming, where the guy is looking at!
 	// It is updated with cmd->aiming.
 	angle_t aiming;
diff --git a/src/g_demo.c b/src/g_demo.c
index a901e8dea9b135f093940f3aa6d68896ffb592a8..b070cbd51450d95175b35c029b8a836c4026751d 100644
--- a/src/g_demo.c
+++ b/src/g_demo.c
@@ -165,7 +165,6 @@ void G_LoadMetal(UINT8 **buffer)
 void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
 {
 	UINT8 ziptic;
-	(void)playernum;
 
 	if (!demo_p || !demo_start)
 		return;
@@ -183,6 +182,7 @@ void G_ReadDemoTiccmd(ticcmd_t *cmd, INT32 playernum)
 		oldcmd.aiming = READINT16(demo_p);
 
 	G_CopyTiccmd(cmd, &oldcmd, 1);
+	players[playernum].angleturn = cmd->angleturn;
 
 	if (!(demoflags & DF_GHOST) && *demo_p == DEMOMARKER)
 	{
diff --git a/src/g_game.c b/src/g_game.c
index 5bcf9f580ad2612fa64619499c0e634e8ad630ba..2560c2b323e2064e6f1f6023bd31724e289e2277 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1065,6 +1065,7 @@ static fixed_t forwardmove[2] = {25<<FRACBITS>>16, 50<<FRACBITS>>16};
 static fixed_t sidemove[2] = {25<<FRACBITS>>16, 50<<FRACBITS>>16}; // faster!
 static fixed_t angleturn[3] = {640, 1280, 320}; // + slow turn
 
+INT16 ticcmd_oldangleturn[2];
 boolean ticcmd_centerviewdown[2]; // For simple controls, lock the camera behind the player
 mobj_t *ticcmd_ztargetfocus[2]; // Locking onto an object?
 void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
@@ -1140,7 +1141,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 	if (paused || P_AutoPause() || (gamestate == GS_LEVEL && (player->playerstate == PST_REBORN || ((gametyperules & GTR_TAG)
 	&& (leveltime < hidetime * TICRATE) && (player->pflags & PF_TAGIT)))))
 	{//@TODO splitscreen player
-		cmd->angleturn = (INT16)(*myangle >> 16);
+		cmd->angleturn = ticcmd_oldangleturn[forplayer];
 		cmd->aiming = G_ClipAimingPitch(myaiming);
 		return;
 	}
@@ -1361,7 +1362,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 		if (controlstyle == CS_SIMPLE && !ticcmd_centerviewdown[forplayer] && !G_RingSlingerGametype())
 		{
 			CV_SetValue(&cv_directionchar[forplayer], 2);
-			*myangle = player->mo->angle;
+			cmd->angleturn = (INT16)((player->mo->angle - *myangle) >> 16);
 			*myaiming = 0;
 
 			if (cv_cam_lockonboss[forplayer].value)
@@ -1430,7 +1431,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 				else if (anglediff < -maxturn)
 					anglediff = -maxturn;
 
-				*myangle += anglediff;
+				cmd->angleturn = (INT16)(cmd->angleturn + (anglediff >> 16));
 			}
 		}
 	}
@@ -1567,19 +1568,23 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 		B_HandleFlightIndicator(player);
 	}
 	else if (player->bot == 2)
-		*myangle = localangle; // Fix offset angle for P2-controlled Tailsbot when P2's controls are set to non-Legacy
+		// Fix offset angle for P2-controlled Tailsbot when P2's controls are set to non-Legacy
+		cmd->angleturn = (INT16)((localangle - *myangle) >> 16);
+
+	*myangle += (cmd->angleturn<<16);
 
 	if (controlstyle == CS_LMAOGALOG) {
+		angle_t angle;
+
 		if (player->awayviewtics)
-			cmd->angleturn = (INT16)(player->awayviewmobj->angle >> 16);
+			angle = player->awayviewmobj->angle;
 		else
-			cmd->angleturn = (INT16)(thiscam->angle >> 16);
+			angle = thiscam->angle;
+
+		cmd->angleturn = (INT16)((angle - (ticcmd_oldangleturn[forplayer] << 16)) >> 16);
 	}
 	else
 	{
-		*myangle += (cmd->angleturn<<16);
-		cmd->angleturn = (INT16)(*myangle >> 16);
-
 		// Adjust camera angle by player input
 		if (controlstyle == CS_SIMPLE && !forcestrafe && thiscam->chase && !turnheld[forplayer] && !ticcmd_centerviewdown[forplayer] && !player->climbing && player->powers[pw_carry] != CR_MINECART)
 		{
@@ -1589,13 +1594,17 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 			{
 				fixed_t sine = FINESINE((R_PointToAngle2(0, 0, player->rmomx, player->rmomy) - localangle)>>ANGLETOFINESHIFT);
 				fixed_t factor;
+				INT16 camadjust;
 
 				if ((sine > 0) == (cmd->sidemove > 0))
 					sine = 0; // Prevent jerking right when braking from going left, or vice versa
 
 				factor = min(40, FixedMul(player->speed, abs(sine))*2 / FRACUNIT);
 
-				*myangle -= cmd->sidemove * factor * camadjustfactor;
+				camadjust = (cmd->sidemove * factor * camadjustfactor) >> 16;
+
+				*myangle -= camadjust << 16;
+				cmd->angleturn = (INT16)(cmd->angleturn - camadjust);
 			}
 
 			if (ticcmd_centerviewdown[forplayer] && (cv_cam_lockedinput[forplayer].value || (player->pflags & PF_STARTDASH)))
@@ -1632,9 +1641,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 			{
 				angle_t controlangle;
 				INT32 anglediff;
+				INT16 camadjust;
 
 				if ((cmd->forwardmove || cmd->sidemove) && !(player->pflags & PF_SPINNING))
-					controlangle = (cmd->angleturn<<16) + R_PointToAngle2(0, 0, cmd->forwardmove << FRACBITS, -cmd->sidemove << FRACBITS);
+					controlangle = *myangle + R_PointToAngle2(0, 0, cmd->forwardmove << FRACBITS, -cmd->sidemove << FRACBITS);
 				else
 					controlangle = player->drawangle + drawangleoffset;
 
@@ -1651,7 +1661,10 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 					anglediff = FixedMul(anglediff, sine);
 				}
 
-				*myangle += FixedMul(anglediff, camadjustfactor);
+				camadjust = FixedMul(anglediff, camadjustfactor) >> 16;
+
+				*myangle += camadjust << 16;
+				cmd->angleturn = (INT16)(cmd->angleturn + camadjust);
 			}
 		}
 	}
@@ -1665,6 +1678,9 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 		LUAh_ViewpointSwitch(player, &players[consoleplayer], true);
 		displayplayer = consoleplayer;
 	}
+
+	cmd->angleturn = (INT16)(cmd->angleturn + ticcmd_oldangleturn[forplayer]);
+	ticcmd_oldangleturn[forplayer] = cmd->angleturn;
 }
 
 ticcmd_t *G_CopyTiccmd(ticcmd_t* dest, const ticcmd_t* src, const size_t n)
@@ -2205,11 +2221,16 @@ void G_Ticker(boolean run)
 
 	buf = gametic % BACKUPTICS;
 
-	// read/write demo and check turbo cheat
 	for (i = 0; i < MAXPLAYERS; i++)
 	{
 		if (playeringame[i])
+		{
 			G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1);
+
+			players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn;
+			players[i].oldrelangleturn = players[i].cmd.angleturn;
+			players[i].cmd.angleturn = players[i].angleturn;
+		}
 	}
 
 	// do main actions
@@ -2392,6 +2413,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
 	SINT8 pity;
 	INT16 rings;
 	INT16 spheres;
+	INT16 playerangleturn;
+	INT16 oldrelangleturn;
 
 	score = players[player].score;
 	lives = players[player].lives;
@@ -2403,6 +2426,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
 	spectator = players[player].spectator;
 	outofcoop = players[player].outofcoop;
 	pflags = (players[player].pflags & (PF_FLIPCAM|PF_ANALOGMODE|PF_DIRECTIONCHAR|PF_AUTOBRAKE|PF_TAGIT|PF_GAMETYPEOVER));
+	playerangleturn = players[player].angleturn;
+	oldrelangleturn = players[player].oldrelangleturn;
 
 	if (!betweenmaps)
 		pflags |= (players[player].pflags & PF_FINISHED);
@@ -2474,6 +2499,8 @@ void G_PlayerReborn(INT32 player, boolean betweenmaps)
 	p->quittime = quittime;
 	p->spectator = spectator;
 	p->outofcoop = outofcoop;
+	p->angleturn = playerangleturn;
+	p->oldrelangleturn = oldrelangleturn;
 
 	// save player config truth reborn
 	p->skincolor = skincolor;
diff --git a/src/g_game.h b/src/g_game.h
index df0c9392e69c87cf3b139e29f97f34d950b28af2..21fa682b7c544b106b2213289fbfced2f497d7f0 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -93,6 +93,7 @@ typedef enum
 // build an internal map name MAPxx from map number
 const char *G_BuildMapName(INT32 map);
 
+extern INT16 ticcmd_oldangleturn[2];
 extern boolean ticcmd_centerviewdown[2]; // For simple controls, lock the camera behind the player
 extern mobj_t *ticcmd_ztargetfocus[2]; // Locking onto an object?
 void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer);
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 81729b7889a4dcabe9f2a394a53e76afb05eafbb..62b5d736fe0150e0e1bcd81007055b82dfcb5ec6 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -449,10 +449,7 @@ static int mobj_set(lua_State *L)
 		return UNIMPLEMENTED;
 	case mobj_angle:
 		mo->angle = luaL_checkangle(L, 3);
-		if (mo->player == &players[consoleplayer])
-			localangle = mo->angle;
-		else if (mo->player == &players[secondarydisplayplayer])
-			localangle2 = mo->angle;
+		P_SetPlayerAngle(mo->player, mo->angle);
 		break;
 	case mobj_rollangle:
 		mo->rollangle = luaL_checkangle(L, 3);
diff --git a/src/m_cheat.c b/src/m_cheat.c
index 156c5db16bbc7d92a4a89151d1c8b05344ef27f8..98e309a559b214b66977fa7db4aebda79586cc29 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -555,7 +555,8 @@ void Command_Teleport_f(void)
 				p->mo->flags2 &= ~MF2_OBJECTFLIP;
 			}
 
-			localangle = p->mo->angle = p->drawangle = FixedAngle(mt->angle<<FRACBITS);
+			p->mo->angle = p->drawangle = FixedAngle(mt->angle<<FRACBITS);
+			P_SetPlayerAngle(p, p->mo->angle);
 		}
 		else // scan the thinkers to find starposts...
 		{
@@ -619,7 +620,8 @@ void Command_Teleport_f(void)
 				p->mo->flags2 &= ~MF2_OBJECTFLIP;
 			}
 
-			localangle = p->mo->angle = p->drawangle = mo2->angle;
+			p->mo->angle = p->drawangle = mo2->angle;
+			P_SetPlayerAngle(p, p->mo->angle);
 		}
 
 		CONS_Printf(M_GetText("Teleporting to checkpoint %d, %d...\n"), starpostnum, starpostpath);
@@ -673,7 +675,10 @@ void Command_Teleport_f(void)
 
 		i = COM_CheckParm("-ang");
 		if (i)
-			localangle = p->drawangle = p->mo->angle = FixedAngle(atoi(COM_Argv(i + 1))<<FRACBITS);
+		{
+			p->drawangle = p->mo->angle = FixedAngle(atoi(COM_Argv(i + 1))<<FRACBITS);
+			P_SetPlayerAngle(p, p->mo->angle);
+		}
 
 		i = COM_CheckParm("-aim");
 		if (i)
diff --git a/src/p_inter.c b/src/p_inter.c
index 3d2c5e45e4f46017fe3284b5831da4d8aca04123..052ecc0da78e741ecaf487b3f96bc76c7e61332d 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -1143,10 +1143,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 					toucher->angle = special->angle;
 
-					if (player == &players[consoleplayer])
-						localangle = toucher->angle;
-					else if (player == &players[secondarydisplayplayer])
-						localangle2 = toucher->angle;
+					P_SetPlayerAngle(player, toucher->angle);
 
 					P_ResetPlayer(player);
 
@@ -1564,10 +1561,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 
 #if 0 // camera redirection - deemed unnecessary
 			toucher->angle = special->angle;
-			if (player == &players[consoleplayer])
-				localangle = toucher->angle;
-			else if (player == &players[secondarydisplayplayer])
-				localangle2 = toucher->angle;
+			P_SetPlayerAngle(player, toucher->angle);
 #endif
 
 			S_StartSound(toucher, special->info->attacksound); // home run
diff --git a/src/p_local.h b/src/p_local.h
index becb045f70e976ac8457efb4aa1577d12efa1529..e89343ca882ed38def1197d924a42d7436e57ac1 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -138,6 +138,10 @@ void P_DoPlayerPain(player_t *player, mobj_t *source, mobj_t *inflictor);
 void P_ResetPlayer(player_t *player);
 boolean P_PlayerCanDamage(player_t *player, mobj_t *thing);
 boolean P_IsLocalPlayer(player_t *player);
+void P_SetPlayerAngle(player_t *player, angle_t angle);
+angle_t P_GetLocalAngle(player_t *player);
+void P_SetLocalAngle(player_t *player, angle_t angle);
+void P_ForceLocalAngle(player_t *player, angle_t angle);
 
 boolean P_IsObjectInGoop(mobj_t *mo);
 boolean P_IsObjectOnGround(mobj_t *mo);
diff --git a/src/p_map.c b/src/p_map.c
index 6e4992473be3c81f03040175bd4c82c6f1373b63..e3038e87fb32d7d82c83b3cfc3fe2907d14dd684 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -372,12 +372,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 			object->angle = object->player->drawangle = spring->angle;
 
 			if (!demoplayback || P_ControlStyle(object->player) == CS_LMAOGALOG)
-			{
-				if (object->player == &players[consoleplayer])
-					localangle = spring->angle;
-				else if (object->player == &players[secondarydisplayplayer])
-					localangle2 = spring->angle;
-			}
+				P_SetPlayerAngle(object->player, spring->angle);
 		}
 
 		if (object->player->pflags & PF_GLIDING)
@@ -1310,12 +1305,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			thing->angle = tmthing->angle;
 
 			if (!demoplayback || P_ControlStyle(thing->player) == CS_LMAOGALOG)
-			{
-				if (thing->player == &players[consoleplayer])
-					localangle = thing->angle;
-				else if (thing->player == &players[secondarydisplayplayer])
-					localangle2 = thing->angle;
-			}
+				P_SetPlayerAngle(thing->player, thing->angle);
 
 			return true;
 		}
@@ -3409,12 +3399,7 @@ isblocking:
 			{
 				slidemo->angle = climbangle;
 				/*if (!demoplayback || P_ControlStyle(slidemo->player) == CS_LMAOGALOG)
-				{
-					if (slidemo->player == &players[consoleplayer])
-						localangle = slidemo->angle;
-					else if (slidemo->player == &players[secondarydisplayplayer])
-						localangle2 = slidemo->angle;
-				}*/
+					P_SetPlayerAngle(slidemo->player, slidemo->angle);*/
 
 				if (!slidemo->player->climbing)
 				{
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 4124c47a9f954b8faf4c54189bc63f5d9b9dc176..3d50b04393077d6678f116c0d5b0b27660041977 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -11388,10 +11388,7 @@ void P_AfterPlayerSpawn(INT32 playernum)
 	player_t *p = &players[playernum];
 	mobj_t *mobj = p->mo;
 
-	if (playernum == consoleplayer)
-		localangle = mobj->angle;
-	else if (playernum == secondarydisplayplayer)
-		localangle2 = mobj->angle;
+	P_SetPlayerAngle(p, mobj->angle);
 
 	p->viewheight = 41*p->height/48;
 
@@ -11408,7 +11405,6 @@ void P_AfterPlayerSpawn(INT32 playernum)
 		HU_Start();
 	}
 
-	SV_SpawnPlayer(playernum, mobj->x, mobj->y, mobj->angle);
 	p->drawangle = mobj->angle;
 
 	if (camera.chase)
diff --git a/src/p_polyobj.c b/src/p_polyobj.c
index 3b6195285bd2cea5cb36e7f380b0ef422ccb21d4..ca21a36cc8d3c38c1c0e08ee7b2d6aa2530d97fc 100644
--- a/src/p_polyobj.c
+++ b/src/p_polyobj.c
@@ -1156,10 +1156,7 @@ static void Polyobj_rotateThings(polyobj_t *po, vertex_t origin, angle_t delta,
 
 					if (turnthings == 2 || (turnthings == 1 && !mo->player)) {
 						mo->angle += delta;
-						if (mo->player == &players[consoleplayer])
-							localangle += delta;
-						else if (mo->player == &players[secondarydisplayplayer])
-							localangle2 += delta;
+						P_SetPlayerAngle(mo->player, (angle_t)(mo->player->angleturn << 16) + delta);
 					}
 				}
 			}
diff --git a/src/p_saveg.c b/src/p_saveg.c
index bec64ed35773e3082845084c1993faf16e101740..a4cdd514854af6c381eb50ea12b965a09e9beada 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -103,6 +103,8 @@ static void P_NetArchivePlayers(void)
 
 		// no longer send ticcmds, player name, skin, or color
 
+		WRITEINT16(save_p, players[i].angleturn);
+		WRITEINT16(save_p, players[i].oldrelangleturn);
 		WRITEANGLE(save_p, players[i].aiming);
 		WRITEANGLE(save_p, players[i].drawangle);
 		WRITEANGLE(save_p, players[i].viewrollangle);
@@ -311,6 +313,8 @@ static void P_NetUnArchivePlayers(void)
 		// sending player names, skin and color should not be necessary at all!
 		// (that data is handled in the server config now)
 
+		players[i].angleturn = READINT16(save_p);
+		players[i].oldrelangleturn = READINT16(save_p);
 		players[i].aiming = READANGLE(save_p);
 		players[i].drawangle = READANGLE(save_p);
 		players[i].viewrollangle = READANGLE(save_p);
@@ -2547,11 +2551,6 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 		i = READUINT8(save_p);
 		mobj->player = &players[i];
 		mobj->player->mo = mobj;
-		// added for angle prediction
-		if (consoleplayer == i)
-			localangle = mobj->angle;
-		if (secondarydisplayplayer == i)
-			localangle2 = mobj->angle;
 	}
 	if (diff & MD_MOVEDIR)
 		mobj->movedir = READANGLE(save_p);
diff --git a/src/p_spec.c b/src/p_spec.c
index d45a33c47d5b3bee989d6d6e00d67af1ef15f64e..85546a97ad98478bc55bfce70872b578988b96ab 100644
--- a/src/p_spec.c
+++ b/src/p_spec.c
@@ -4602,12 +4602,7 @@ DoneSection2:
 				player->mo->angle = player->drawangle = lineangle;
 
 				if (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG)
-				{
-					if (player == &players[consoleplayer])
-						localangle = player->mo->angle;
-					else if (player == &players[secondarydisplayplayer])
-						localangle2 = player->mo->angle;
-				}
+					P_SetPlayerAngle(player, player->mo->angle);
 
 				if (!(lines[i].flags & ML_EFFECT4))
 				{
@@ -8865,24 +8860,12 @@ void T_Pusher(pusher_t *p)
 
 				if (!demoplayback || P_ControlStyle(thing->player) == CS_LMAOGALOG)
 				{
-					if (thing->player == &players[consoleplayer])
-					{
-						if (thing->angle - localangle > ANGLE_180)
-							localangle -= (localangle - thing->angle) / 8;
-						else
-							localangle += (thing->angle - localangle) / 8;
-					}
-					else if (thing->player == &players[secondarydisplayplayer])
-					{
-						if (thing->angle - localangle2 > ANGLE_180)
-							localangle2 -= (localangle2 - thing->angle) / 8;
-						else
-							localangle2 += (thing->angle - localangle2) / 8;
-					}
-					/*if (thing->player == &players[consoleplayer])
-						localangle = thing->angle;
-					else if (thing->player == &players[secondarydisplayplayer])
-						localangle2 = thing->angle;*/
+					angle_t angle = thing->player->angleturn << 16;
+					if (thing->angle - angle > ANGLE_180)
+						P_SetPlayerAngle(thing->player, angle - (angle - thing->angle) / 8);
+					else
+						P_SetPlayerAngle(thing->player, angle + (thing->angle - angle) / 8);
+					//P_SetPlayerAngle(thing->player, thing->angle);
 				}
 			}
 
diff --git a/src/p_telept.c b/src/p_telept.c
index 600d40c883535d245631c439666fb9c2ced331f3..f6feddf4b513a2cb356baca7d60c160c465d9605 100644
--- a/src/p_telept.c
+++ b/src/p_telept.c
@@ -63,10 +63,7 @@ void P_MixUp(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle,
 			thing->reactiontime = TICRATE/2; // don't move for about half a second
 
 		// absolute angle position
-		if (thing == players[consoleplayer].mo)
-			localangle = angle;
-		if (thing == players[secondarydisplayplayer].mo)
-			localangle2 = angle;
+		P_SetPlayerAngle(thing->player, angle);
 
 		// move chasecam at new player location
 		if (splitscreen && camera2.chase
@@ -165,10 +162,7 @@ boolean P_Teleport(mobj_t *thing, fixed_t x, fixed_t y, fixed_t z, angle_t angle
 			thing->player->drawangle += (angle - thing->angle);
 
 		// absolute angle position
-		if (thing->player == &players[consoleplayer])
-			localangle = angle;
-		if (thing->player == &players[secondarydisplayplayer])
-			localangle2 = angle;
+		P_SetPlayerAngle(thing->player, angle);
 
 		// move chasecam at new player location
 		if (splitscreen && camera2.chase && thing->player == &players[secondarydisplayplayer])
diff --git a/src/p_tick.c b/src/p_tick.c
index 7ea6edb2d5c4090f996e9ad6e1ff744ccb1e97b5..55b545856c018600d831d8287932bfd5eaa64be7 100644
--- a/src/p_tick.c
+++ b/src/p_tick.c
@@ -770,7 +770,9 @@ void P_PreTicker(INT32 frames)
 				memcpy(&temptic, &players[i].cmd, sizeof(ticcmd_t));
 				memset(&players[i].cmd, 0, sizeof(ticcmd_t));
 				// correct angle on spawn...
-				players[i].cmd.angleturn = temptic.angleturn;
+				players[i].angleturn += temptic.angleturn - players[i].oldrelangleturn;
+				players[i].oldrelangleturn = temptic.angleturn;
+				players[i].cmd.angleturn = players[i].angleturn;
 
 				P_PlayerThink(&players[i]);
 
diff --git a/src/p_user.c b/src/p_user.c
index ca1001f577abb032f336e0a59a8fa3b2f55ec41c..c22e2939714b4ff61051d2c42884d1800bbe839b 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -3557,22 +3557,11 @@ static void P_DoClimbing(player_t *player)
 #define CLIMBCONEMAX FixedAngle(90*FRACUNIT)
 	if (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG)
 	{
-		if (player == &players[consoleplayer])
-		{
-			angle_t angdiff = localangle - player->mo->angle;
-			if (angdiff < ANGLE_180 && angdiff > CLIMBCONEMAX)
-				localangle = player->mo->angle + CLIMBCONEMAX;
-			else if (angdiff > ANGLE_180 && angdiff < InvAngle(CLIMBCONEMAX))
-				localangle = player->mo->angle - CLIMBCONEMAX;
-		}
-		else if (player == &players[secondarydisplayplayer])
-		{
-			angle_t angdiff = localangle2 - player->mo->angle;
-			if (angdiff < ANGLE_180 && angdiff > CLIMBCONEMAX)
-				localangle2 = player->mo->angle + CLIMBCONEMAX;
-			else if (angdiff > ANGLE_180 && angdiff < InvAngle(CLIMBCONEMAX))
-				localangle2 = player->mo->angle - CLIMBCONEMAX;
-		}
+		angle_t angdiff = P_GetLocalAngle(player) - player->mo->angle;
+		if (angdiff < ANGLE_180 && angdiff > CLIMBCONEMAX)
+			P_SetLocalAngle(player, player->mo->angle + CLIMBCONEMAX);
+		else if (angdiff > ANGLE_180 && angdiff < InvAngle(CLIMBCONEMAX))
+			P_SetLocalAngle(player, player->mo->angle - CLIMBCONEMAX);
 	}
 
 	if (player->climbing == 0)
@@ -4358,12 +4347,7 @@ void P_DoJump(player_t *player, boolean soundandstate)
 		player->drawangle = player->mo->angle = player->mo->angle - ANGLE_180; // Turn around from the wall you were climbing.
 
 		if (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG)
-		{
-			if (player == &players[consoleplayer])
-				localangle = player->mo->angle; // Adjust the local control angle.
-			else if (player == &players[secondarydisplayplayer])
-				localangle2 = player->mo->angle;
-		}
+			P_SetPlayerAngle(player, player->mo->angle);
 
 		player->climbing = 0; // Stop climbing, duh!
 		P_InstaThrust(player->mo, player->mo->angle, FixedMul(6*FRACUNIT, player->mo->scale)); // Jump off the wall.
@@ -4686,12 +4670,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
 								player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockon->x, lockon->y);
 								bullet = P_SpawnPointMissile(player->mo, lockon->x, lockon->y, zpos(lockon), player->revitem, player->mo->x, player->mo->y, zpos(player->mo));
 								if (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG)
-								{
-									if (player == &players[consoleplayer])
-										localangle = player->mo->angle;
-									else if (player == &players[secondarydisplayplayer])
-										localangle2 = player->mo->angle;
-								}
+									P_SetPlayerAngle(player, player->mo->angle);
 							}
 							else
 							{
@@ -5311,9 +5290,9 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 						/*if (!demoplayback)
 						{
 							if (player == &players[consoleplayer] && cv_cam_turnfacingability[0].value > 0 && !(PLAYER1INPUTDOWN(gc_turnleft) || PLAYER1INPUTDOWN(gc_turnright)))
-								localangle = player->mo->angle;
+								P_SetPlayerAngle(player, player->mo->angle);;
 							else if (player == &players[secondarydisplayplayer] && cv_cam_turnfacingability[1].value > 0 && !(PLAYER2INPUTDOWN(gc_turnleft) || PLAYER2INPUTDOWN(gc_turnright)))
-								localangle2 = player->mo->angle;
+								P_SetPlayerAngle(player, player->mo->angle);
 						}*/
 					}
 					break;
@@ -5718,10 +5697,7 @@ static void P_2dMovement(player_t *player)
 			player->mo->angle = ANGLE_180;
 	}
 
-	if (player == &players[consoleplayer])
-		localangle = player->mo->angle;
-	else if (player == &players[secondarydisplayplayer])
-		localangle2 = player->mo->angle;
+	P_SetPlayerAngle(player, player->mo->angle);
 
 	if (player->pflags & PF_GLIDING)
 		movepushangle = player->mo->angle;
@@ -7558,10 +7534,7 @@ static void P_NiGHTSMovement(player_t *player)
 	else
 		player->mo->rollangle = rollangle;
 
-	if (player == &players[consoleplayer])
-		localangle = player->mo->angle;
-	else if (player == &players[secondarydisplayplayer])
-		localangle2 = player->mo->angle;
+	P_SetPlayerAngle(player, player->mo->angle);
 
 	// Check for crushing in our new location
 	if ((player->mo->ceilingz - player->mo->floorz < player->mo->height)
@@ -8510,10 +8483,7 @@ static void P_MovePlayer(player_t *player)
 			player->mo->angle = R_PointToAngle2(0, 0, player->rmomx, player->rmomy);
 
 		// Update the local angle control.
-		if (player == &players[consoleplayer])
-			localangle = player->mo->angle;
-		else if (player == &players[secondarydisplayplayer])
-			localangle2 = player->mo->angle;
+		P_SetPlayerAngle(player, player->mo->angle);
 	}
 
 	if (player->climbing == 1)
@@ -8789,11 +8759,7 @@ static void P_DoZoomTube(player_t *player)
 	if (player->mo->tracer)
 	{
 		player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, player->mo->tracer->x, player->mo->tracer->y);
-
-		if (player == &players[consoleplayer])
-			localangle = player->mo->angle;
-		else if (player == &players[secondarydisplayplayer])
-			localangle2 = player->mo->angle;
+		P_SetPlayerAngle(player, player->mo->angle);
 	}
 }
 
@@ -9250,12 +9216,7 @@ boolean P_HomingAttack(mobj_t *source, mobj_t *enemy) // Home in on your target
 	{
 		source->player->drawangle = source->angle;
 		if (!demoplayback || P_ControlStyle(source->player) == CS_LMAOGALOG)
-		{
-			if (source->player == &players[consoleplayer])
-				localangle = source->angle;
-			else if (source->player == &players[secondarydisplayplayer])
-				localangle2 = source->angle;
-		}
+			P_SetPlayerAngle(source->player, source->angle);
 	}
 
 	// change slope
@@ -9696,7 +9657,7 @@ void P_ResetCamera(player_t *player, camera_t *thiscam)
 	if ((thiscam == &camera && G_ControlStyle(1) == CS_SIMPLE)
 	|| (thiscam == &camera2 && G_ControlStyle(2) == CS_SIMPLE))
 	{
-		thiscam->angle = (thiscam == &camera) ? localangle : localangle2;
+		thiscam->angle = P_GetLocalAngle(player);
 		thiscam->aiming = (thiscam == &camera) ? localaiming : localaiming2;
 	}
 	else if (!(thiscam == &camera && (cv_cam_still.value || cv_analog[0].value))
@@ -9897,9 +9858,9 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 		if (player == &players[consoleplayer])
 		{
 			if (focusangle >= localangle)
-				localangle += abs((signed)(focusangle - localangle))>>5;
+				P_ForceLocalAngle(player, localangle + (abs((signed)(focusangle - localangle))>>5));
 			else
-				localangle -= abs((signed)(focusangle - localangle))>>5;
+				P_ForceLocalAngle(player, localangle - (abs((signed)(focusangle - localangle))>>5));
 		}
 	}
 	else
@@ -10873,21 +10834,13 @@ static void P_MinecartThink(player_t *player)
 
 		if (angdiff + minecart->angle != player->mo->angle && (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG))
 		{
-			angle_t *ang = NULL;
+			angdiff = P_GetLocalAngle(player) - minecart->angle;
+			if (angdiff < ANGLE_180 && angdiff > MINECARTCONEMAX)
+				P_SetLocalAngle(player, minecart->angle + MINECARTCONEMAX);
+			else if (angdiff > ANGLE_180 && angdiff < InvAngle(MINECARTCONEMAX))
+				P_SetLocalAngle(player, minecart->angle - MINECARTCONEMAX);
 
-			if (player == &players[consoleplayer])
-				ang = &localangle;
-			else if (player == &players[secondarydisplayplayer])
-				ang = &localangle2;
 
-			if (ang)
-			{
-				angdiff = *ang - minecart->angle;
-				if (angdiff < ANGLE_180 && angdiff > MINECARTCONEMAX)
-					*ang = minecart->angle + MINECARTCONEMAX;
-				else if (angdiff > ANGLE_180 && angdiff < InvAngle(MINECARTCONEMAX))
-					*ang = minecart->angle - MINECARTCONEMAX;
-			}
 		}
 	}
 
@@ -10964,10 +10917,7 @@ static void P_MinecartThink(player_t *player)
 			if (angdiff && (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG))  // maintain relative angle on turns
 			{
 				player->mo->angle += angdiff;
-				if (player == &players[consoleplayer])
-					localangle += angdiff;
-				else if (player == &players[secondarydisplayplayer])
-					localangle2 += angdiff;
+				P_SetPlayerAngle(player, (angle_t)(player->angleturn << 16) + angdiff);
 			}
 
 			// Sideways detection
@@ -12520,12 +12470,7 @@ void P_PlayerAfterThink(player_t *player)
 					player->mo->angle = tails->angle;
 
 					if (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG)
-					{
-						if (player == &players[consoleplayer])
-							localangle = player->mo->angle;
-						else if (player == &players[secondarydisplayplayer])
-							localangle2 = player->mo->angle;
-					}
+						P_SetPlayerAngle(player, player->mo->angle);
 				}
 
 				if (P_AproxDistance(player->mo->x - tails->x, player->mo->y - tails->y) > player->mo->radius)
@@ -12609,12 +12554,7 @@ void P_PlayerAfterThink(player_t *player)
 						player->mo->angle += cmd->sidemove<<ANGLETOFINESHIFT; // 2048 --> ANGLE_MAX
 
 						if (!demoplayback || P_ControlStyle(player) == CS_LMAOGALOG)
-						{
-							if (player == &players[consoleplayer])
-								localangle = player->mo->angle; // Adjust the local control angle.
-							else if (player == &players[secondarydisplayplayer])
-								localangle2 = player->mo->angle;
-						}
+							P_SetPlayerAngle(player, player->mo->angle);
 					}
 				}
 				break;
@@ -12826,3 +12766,43 @@ void P_PlayerAfterThink(player_t *player)
 		}
 	}
 }
+
+void P_SetPlayerAngle(player_t *player, angle_t angle)
+{
+	INT16 delta = (INT16)(angle >> 16) - player->angleturn;
+
+	P_ForceLocalAngle(player, P_GetLocalAngle(player) + (delta << 16));
+	player->angleturn += delta;
+}
+
+void P_SetLocalAngle(player_t *player, angle_t angle)
+{
+	INT16 delta = (INT16)((angle - P_GetLocalAngle(player)) >> 16);
+
+	P_ForceLocalAngle(player, P_GetLocalAngle(player) + (angle_t)(delta << 16));
+
+	if (player == &players[consoleplayer])
+		ticcmd_oldangleturn[0] += delta;
+	else if (player == &players[secondarydisplayplayer])
+		ticcmd_oldangleturn[1] += delta;
+}
+
+angle_t P_GetLocalAngle(player_t *player)
+{
+	if (player == &players[consoleplayer])
+		return localangle;
+	else if (player == &players[secondarydisplayplayer])
+		return localangle2;
+	else
+		return 0;
+}
+
+void P_ForceLocalAngle(player_t *player, angle_t angle)
+{
+	angle = angle >> 16 << 16;
+
+	if (player == &players[consoleplayer])
+		localangle = angle;
+	else if (player == &players[secondarydisplayplayer])
+		localangle2 = angle;
+}