From 5e484bddad364704858e3624424925b5127497e5 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Mon, 14 Sep 2020 15:14:22 +1000
Subject: [PATCH 01/41] - Blood: Make `gInput` static within controls.cpp.

---
 source/blood/src/blood.cpp    |  1 -
 source/blood/src/blood.h      |  2 +-
 source/blood/src/controls.cpp | 32 ++++++++++++++++----------------
 source/blood/src/controls.h   |  1 -
 4 files changed, 17 insertions(+), 19 deletions(-)

diff --git a/source/blood/src/blood.cpp b/source/blood/src/blood.cpp
index cd9e9d01b..776079042 100644
--- a/source/blood/src/blood.cpp
+++ b/source/blood/src/blood.cpp
@@ -100,7 +100,6 @@ void StartLevel(MapRecord* level)
 	gFrameClock = 0;
 	STAT_Update(0);
 	EndLevel();
-	gInput = {};
 	currentLevel = level;
 
 	if (gGameOptions.nGameType == 0)
diff --git a/source/blood/src/blood.h b/source/blood/src/blood.h
index 433c9ddd1..1ae1a0add 100644
--- a/source/blood/src/blood.h
+++ b/source/blood/src/blood.h
@@ -88,7 +88,7 @@ struct GameInterface : ::GameInterface
 	FString GetCoordString() override;
 	ReservedSpace GetReservedScreenSpace(int viewsize) override;
 	void UpdateSounds() override;
-	void GetInput(InputPacket* gInput, ControlInfo* const hidInput) override;
+	void GetInput(InputPacket* packet, ControlInfo* const hidInput) override;
 	void Ticker() override;
 	void DrawBackground() override;
 	void Startup() override;
diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index a54d92bba..35aa44f6e 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -38,7 +38,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 BEGIN_BLD_NS
 
-InputPacket gInput;
+static InputPacket gInput;
 bool bSilentAim = false;
 
 int iTurnCount = 0;
@@ -50,7 +50,7 @@ float gViewAngleAdjust;
 float gViewLookAdjust;
 int gViewLookRecenter;
 
-void GetInputInternal(InputPacket &inputParm, ControlInfo* const hidInput)
+static void GetInputInternal(ControlInfo* const hidInput)
 {
     int prevPauseState = paused;
 
@@ -64,22 +64,22 @@ void GetInputInternal(InputPacket &inputParm, ControlInfo* const hidInput)
 
     InputPacket input = {};
 
-    ApplyGlobalInput(inputParm, hidInput);
+    ApplyGlobalInput(gInput, hidInput);
 
-    bool mouseaim = !(inputParm.actions & SB_AIMMODE);
-    if (!mouseaim) inputParm.actions |= SB_CENTERVIEW;
+    bool mouseaim = !(gInput.actions & SB_AIMMODE);
+    if (!mouseaim) gInput.actions |= SB_CENTERVIEW;
 
     if (gPlayer[myconnectindex].nextWeapon == 0)
     {
     }
 
-    if (inputParm.actions & (SB_LOOK_UP|SB_LOOK_DOWN))
-        inputParm.actions |= SB_CENTERVIEW;
+    if (gInput.actions & (SB_LOOK_UP|SB_LOOK_DOWN))
+        gInput.actions |= SB_CENTERVIEW;
 
-    int const run = !!(inputParm.actions & SB_RUN);
+    int const run = !!(gInput.actions & SB_RUN);
     int const keyMove = (1 + run) << 10;
 
-    if (inputParm.fvel < keyMove && inputParm.fvel > -keyMove)
+    if (gInput.fvel < keyMove && gInput.fvel > -keyMove)
     {
         if (buttonMap.ButtonDown(gamefunc_Move_Forward))
             input.fvel += keyMove;
@@ -88,7 +88,7 @@ void GetInputInternal(InputPacket &inputParm, ControlInfo* const hidInput)
             input.fvel -= keyMove;
     }
 
-    if (inputParm.svel < keyMove && inputParm.svel > -keyMove)
+    if (gInput.svel < keyMove && gInput.svel > -keyMove)
     {
         if (buttonMap.ButtonDown(gamefunc_Strafe_Left))
             input.svel += keyMove;
@@ -101,7 +101,7 @@ void GetInputInternal(InputPacket &inputParm, ControlInfo* const hidInput)
 
     if (buttonMap.ButtonDown(gamefunc_Strafe))
     {
-        if (inputParm.svel < keyMove && inputParm.svel > -keyMove)
+        if (gInput.svel < keyMove && gInput.svel > -keyMove)
         {
             if (buttonMap.ButtonDown(gamefunc_Turn_Left))
                 input.svel += keyMove;
@@ -163,10 +163,10 @@ void GetInputInternal(InputPacket &inputParm, ControlInfo* const hidInput)
 
     input.q16horz -= FloatToFixed(scaleAdjustmentToInterval(hidInput->dpitch / mlookScale));
 
-    inputParm.fvel = clamp(inputParm.fvel + input.fvel, -2048, 2048);
-    inputParm.svel = clamp(inputParm.svel + input.svel, -2048, 2048);
-    inputParm.q16avel += input.q16avel;
-    inputParm.q16horz = clamp(inputParm.q16horz + input.q16horz, IntToFixed(-127) >> 2, IntToFixed(127) >> 2);
+    gInput.fvel = clamp(gInput.fvel + input.fvel, -2048, 2048);
+    gInput.svel = clamp(gInput.svel + input.svel, -2048, 2048);
+    gInput.q16avel += input.q16avel;
+    gInput.q16horz = clamp(gInput.q16horz + input.q16horz, IntToFixed(-127) >> 2, IntToFixed(127) >> 2);
 
     if (gMe && gMe->pXSprite && gMe->pXSprite->health != 0 && !paused)
     {
@@ -192,7 +192,7 @@ void GetInputInternal(InputPacket &inputParm, ControlInfo* const hidInput)
 
 void GameInterface::GetInput(InputPacket* packet, ControlInfo* const hidInput)
 {
-    GetInputInternal(gInput, hidInput);
+    GetInputInternal(hidInput);
     if (packet)
     {
         *packet = gInput;
diff --git a/source/blood/src/controls.h b/source/blood/src/controls.h
index 6849e7ebc..97b0364b6 100644
--- a/source/blood/src/controls.h
+++ b/source/blood/src/controls.h
@@ -26,7 +26,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 BEGIN_BLD_NS
 
-extern InputPacket gInput;
 extern bool bSilentAim;
 
 extern fixed_t gViewLook, gViewAngle;

From 20f94c58952b4ce95a40c8ec2eefae08656889ab Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Tue, 15 Sep 2020 15:24:00 +1000
Subject: [PATCH 02/41] - Blood: Use backend input scaling.

---
 source/blood/src/controls.cpp | 38 ++++++++++++++---------------------
 1 file changed, 15 insertions(+), 23 deletions(-)

diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index 35aa44f6e..6b2f31afd 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -52,16 +52,9 @@ int gViewLookRecenter;
 
 static void GetInputInternal(ControlInfo* const hidInput)
 {
+    double const scaleAdjust = InputScale();
     int prevPauseState = paused;
 
-    static double lastInputTicks;
-    auto const    currentHiTicks    = I_msTimeF();
-    double const  elapsedInputTicks = currentHiTicks - lastInputTicks;
-
-    lastInputTicks = currentHiTicks;
-
-    auto scaleAdjustmentToInterval = [=](double x) { return x * kTicsPerSec / (1000.0 / elapsedInputTicks); };
-
     InputPacket input = {};
 
     ApplyGlobalInput(gInput, hidInput);
@@ -133,35 +126,34 @@ static void GetInputInternal(ControlInfo* const hidInput)
         turnHeldTime = 0;
 
     if (turnLeft)
-        input.q16avel -= FloatToFixed(scaleAdjustmentToInterval(ClipHigh(12 * turnHeldTime, gTurnSpeed)>>2));
+        input.q16avel -= FloatToFixed(scaleAdjust * (ClipHigh(12 * turnHeldTime, gTurnSpeed) >> 2));
     if (turnRight)
-        input.q16avel += FloatToFixed(scaleAdjustmentToInterval(ClipHigh(12 * turnHeldTime, gTurnSpeed)>>2));
+        input.q16avel += FloatToFixed(scaleAdjust * (ClipHigh(12 * turnHeldTime, gTurnSpeed) >> 2));
 
     if (run && turnHeldTime > 24)
         input.q16avel <<= 1;
 
     if (buttonMap.ButtonDown(gamefunc_Strafe))
     {
-        input.svel -= hidInput->mousex * 32.f;
-        input.svel -= scaleAdjustmentToInterval(hidInput->dyaw * keyMove);
+        input.svel -= xs_CRoundToInt((hidInput->mousex * 32.) + (scaleAdjust * (hidInput->dyaw * keyMove)));
     }
     else
     {
-        input.q16avel += FloatToFixed(hidInput->mousex);
-        input.q16avel += FloatToFixed(scaleAdjustmentToInterval(hidInput->dyaw));
+        input.q16avel += FloatToFixed(hidInput->mousex + (scaleAdjust * (hidInput->dyaw)));
     }
 
-    input.svel  -= scaleAdjustmentToInterval(hidInput->dx * keyMove);
-    input.fvel -= scaleAdjustmentToInterval(hidInput->dz * keyMove);
+    input.svel -= xs_CRoundToInt(scaleAdjust * (hidInput->dx * keyMove));
+    input.fvel -= xs_CRoundToInt(scaleAdjust * (hidInput->dz * keyMove));
 
     if (mouseaim)
         input.q16horz += FloatToFixed(hidInput->mousey / mlookScale);
     else
-        input.fvel -= hidInput->mousey * 64.f;
+        input.fvel -= xs_CRoundToInt(hidInput->mousey * 64.);
+
     if (!in_mouseflip)
         input.q16horz = -input.q16horz;
 
-    input.q16horz -= FloatToFixed(scaleAdjustmentToInterval(hidInput->dpitch / mlookScale));
+    input.q16horz -= FloatToFixed(scaleAdjust * (hidInput->dpitch / mlookScale));
 
     gInput.fvel = clamp(gInput.fvel + input.fvel, -2048, 2048);
     gInput.svel = clamp(gInput.svel + input.svel, -2048, 2048);
@@ -174,19 +166,19 @@ static void GetInputInternal(ControlInfo* const hidInput)
         int downAngle = -347;
         double lookStepUp = 4.0*upAngle/60.0;
         double lookStepDown = -4.0*downAngle/60.0;
-        gViewAngle = (gViewAngle + input.q16avel + FloatToFixed(scaleAdjustmentToInterval(gViewAngleAdjust))) & 0x7ffffff;
+        gViewAngle = (gViewAngle + input.q16avel + FloatToFixed(scaleAdjust * gViewAngleAdjust)) & 0x7ffffff;
         if (gViewLookRecenter)
         {
             if (gViewLook < 0)
-                gViewLook = min(gViewLook+FloatToFixed(scaleAdjustmentToInterval(lookStepDown)), 0);
+                gViewLook = min(gViewLook + FloatToFixed(scaleAdjust * lookStepDown), 0);
             if (gViewLook > 0)
-                gViewLook = max(gViewLook-FloatToFixed(scaleAdjustmentToInterval(lookStepUp)), 0);
+                gViewLook = max(gViewLook - FloatToFixed(scaleAdjust * lookStepUp), 0);
         }
         else
         {
-            gViewLook = clamp(gViewLook+FloatToFixed(scaleAdjustmentToInterval(gViewLookAdjust)), IntToFixed(downAngle), IntToFixed(upAngle));
+            gViewLook = clamp(gViewLook + FloatToFixed(scaleAdjust * gViewLookAdjust), IntToFixed(downAngle), IntToFixed(upAngle));
         }
-        gViewLook = clamp(gViewLook+(input.q16horz << 3), IntToFixed(downAngle), IntToFixed(upAngle));
+        gViewLook = clamp(gViewLook + (input.q16horz << 3), IntToFixed(downAngle), IntToFixed(upAngle));
     }
 }
 

From c835d9cadcedfc438cc15264622ee8a346a1bcfd Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Wed, 16 Sep 2020 10:06:50 +1000
Subject: [PATCH 03/41] - Blood: Revert upstream unsynchronised input
 implementation.

---
 source/blood/src/blood.cpp      |  1 -
 source/blood/src/controls.cpp   | 32 ------------
 source/blood/src/controls.h     | 38 --------------
 source/blood/src/credits.cpp    |  1 -
 source/blood/src/loadsave.cpp   |  4 --
 source/blood/src/osdcmd.cpp     |  2 -
 source/blood/src/player.cpp     | 88 +++++++--------------------------
 source/blood/src/player.h       |  1 -
 source/blood/src/prediction.cpp | 70 ++------------------------
 source/blood/src/view.cpp       |  9 ----
 source/blood/src/view.h         |  1 -
 11 files changed, 22 insertions(+), 225 deletions(-)
 delete mode 100644 source/blood/src/controls.h

diff --git a/source/blood/src/blood.cpp b/source/blood/src/blood.cpp
index 776079042..dc618103c 100644
--- a/source/blood/src/blood.cpp
+++ b/source/blood/src/blood.cpp
@@ -33,7 +33,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 #include "db.h"
 #include "blood.h"
 #include "choke.h"
-#include "controls.h"
 #include "dude.h"
 #include "endgame.h"
 #include "eventq.h"
diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index 6b2f31afd..1d0e55c03 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -28,7 +28,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 #include "gamecontrol.h"
 #include "common_game.h"
 #include "blood.h"
-#include "controls.h"
 #include "globals.h"
 #include "levels.h"
 #include "view.h"
@@ -39,16 +38,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 BEGIN_BLD_NS
 
 static InputPacket gInput;
-bool bSilentAim = false;
-
-int iTurnCount = 0;
-
-int32_t mouseyaxismode = -1;
-
-fixed_t gViewLook, gViewAngle;
-float gViewAngleAdjust;
-float gViewLookAdjust;
-int gViewLookRecenter;
 
 static void GetInputInternal(ControlInfo* const hidInput)
 {
@@ -159,27 +148,6 @@ static void GetInputInternal(ControlInfo* const hidInput)
     gInput.svel = clamp(gInput.svel + input.svel, -2048, 2048);
     gInput.q16avel += input.q16avel;
     gInput.q16horz = clamp(gInput.q16horz + input.q16horz, IntToFixed(-127) >> 2, IntToFixed(127) >> 2);
-
-    if (gMe && gMe->pXSprite && gMe->pXSprite->health != 0 && !paused)
-    {
-        int upAngle = 289;
-        int downAngle = -347;
-        double lookStepUp = 4.0*upAngle/60.0;
-        double lookStepDown = -4.0*downAngle/60.0;
-        gViewAngle = (gViewAngle + input.q16avel + FloatToFixed(scaleAdjust * gViewAngleAdjust)) & 0x7ffffff;
-        if (gViewLookRecenter)
-        {
-            if (gViewLook < 0)
-                gViewLook = min(gViewLook + FloatToFixed(scaleAdjust * lookStepDown), 0);
-            if (gViewLook > 0)
-                gViewLook = max(gViewLook - FloatToFixed(scaleAdjust * lookStepUp), 0);
-        }
-        else
-        {
-            gViewLook = clamp(gViewLook + FloatToFixed(scaleAdjust * gViewLookAdjust), IntToFixed(downAngle), IntToFixed(upAngle));
-        }
-        gViewLook = clamp(gViewLook + (input.q16horz << 3), IntToFixed(downAngle), IntToFixed(upAngle));
-    }
 }
 
 void GameInterface::GetInput(InputPacket* packet, ControlInfo* const hidInput)
diff --git a/source/blood/src/controls.h b/source/blood/src/controls.h
deleted file mode 100644
index 97b0364b6..000000000
--- a/source/blood/src/controls.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//-------------------------------------------------------------------------
-/*
-Copyright (C) 2010-2019 EDuke32 developers and contributors
-Copyright (C) 2019 Nuke.YKT
-
-This file is part of NBlood.
-
-NBlood is free software; you can redistribute it and/or
-modify it under the terms of the GNU General Public License version 2
-as published by the Free Software Foundation.
-
-This program is distributed in the hope that it will be useful,
-but WITHOUT ANY WARRANTY; without even the implied warranty of
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
-
-See the GNU General Public License for more details.
-
-You should have received a copy of the GNU General Public License
-along with this program; if not, write to the Free Software
-Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
-*/
-//-------------------------------------------------------------------------
-#pragma once
-
-#include "packet.h"
-
-BEGIN_BLD_NS
-
-extern bool bSilentAim;
-
-extern fixed_t gViewLook, gViewAngle;
-extern float gViewAngleAdjust;
-extern float gViewLookAdjust;
-extern int gViewLookRecenter;
-
-void ctrlGetInput();
-
-END_BLD_NS
diff --git a/source/blood/src/credits.cpp b/source/blood/src/credits.cpp
index 868cba1fc..7e963f4d2 100644
--- a/source/blood/src/credits.cpp
+++ b/source/blood/src/credits.cpp
@@ -28,7 +28,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 #include "SmackerDecoder.h"
 #include "common_game.h"
 #include "blood.h"
-#include "controls.h"
 #include "globals.h"
 #include "sound.h"
 #include "view.h"
diff --git a/source/blood/src/loadsave.cpp b/source/blood/src/loadsave.cpp
index b63803baa..56e80fb10 100644
--- a/source/blood/src/loadsave.cpp
+++ b/source/blood/src/loadsave.cpp
@@ -645,8 +645,6 @@ void MyLoadSave::Load(void)
     Read(&gMapRev, sizeof(gMapRev));
     Read(&gSongId, sizeof(gSkyCount));
     Read(&gFogMode, sizeof(gFogMode));
-    Read(&gViewAngle, sizeof(gViewAngle));
-    Read(&gViewLook, sizeof(gViewLook));
 #ifdef NOONE_EXTENSIONS
     Read(&gModernMap, sizeof(gModernMap));
 #endif
@@ -733,8 +731,6 @@ void MyLoadSave::Save(void)
     Write(&gMapRev, sizeof(gMapRev));
     Write(&gSongId, sizeof(gSkyCount));
     Write(&gFogMode, sizeof(gFogMode));
-    Write(&gViewAngle, sizeof(gViewAngle));
-    Write(&gViewLook, sizeof(gViewLook));
 #ifdef NOONE_EXTENSIONS
     Write(&gModernMap, sizeof(gModernMap));
 #endif
diff --git a/source/blood/src/osdcmd.cpp b/source/blood/src/osdcmd.cpp
index f1b1d75ae..a12b4fe07 100644
--- a/source/blood/src/osdcmd.cpp
+++ b/source/blood/src/osdcmd.cpp
@@ -56,9 +56,7 @@ static int osdcmd_warptocoords(CCmdFuncPtr parm)
 
     if (parm->numparms == 5)
     {
-        // fix me, I'm broken.
         pPlayer->q16horiz = gView->q16horiz = IntToFixed(atoi(parm->parms[4]));
-        gViewAngle = FloatToFixed(atan2(atoi(parm->parms[4]), 100) * (1024. / pi::pi()));
     }
 
     viewBackupView(pPlayer->nPlayer);
diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index bee04f7fc..dfc3542b2 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -31,7 +31,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 #include "actor.h"
 #include "blood.h"
 #include "callback.h"
-#include "controls.h"
 #include "eventq.h"
 #include "fx.h"
 #include "gib.h"
@@ -807,8 +806,6 @@ void playerStart(int nPlayer, int bNewLevel)
     if (pPlayer == gMe)
     {
         viewInitializePrediction();
-        gViewLook = pPlayer->q16look;
-        gViewAngle = pPlayer->q16ang;
     }
     if (IsUnderwaterSector(pSprite->sectnum))
     {
@@ -1323,13 +1320,6 @@ void ProcessInput(PLAYER *pPlayer)
     POSTURE *pPosture = &pPlayer->pPosture[pPlayer->lifeMode][pPlayer->posture];
     InputPacket *pInput = &pPlayer->input;
 
-    if (pPlayer == gMe && numplayers == 1)
-    {
-        gViewAngleAdjust = 0.f;
-        gViewLookRecenter = false;
-        gViewLookAdjust = 0.f;
-    }
-
     pPlayer->isRunning = !!(pInput->actions & SB_RUN);
     if ((pInput->actions & SB_BUTTON_MASK) || pInput->fvel || pInput->svel || pInput->q16avel)
         pPlayer->restTime = 0;
@@ -1447,11 +1437,7 @@ void ProcessInput(PLAYER *pPlayer)
             speed = 128;
         pPlayer->spin = min(pPlayer->spin+speed, 0);
         pPlayer->q16ang += IntToFixed(speed);
-        if (pPlayer == gMe && numplayers == 1)
-            gViewAngleAdjust += float(speed);
     }
-    if (pPlayer == gMe && numplayers == 1)
-        gViewAngleAdjust += float(pSprite->ang - pPlayer->angold);
     pPlayer->q16ang = (pPlayer->q16ang+IntToFixed(pSprite->ang-pPlayer->angold))&0x7ffffff;
     pPlayer->angold = pSprite->ang = FixedToInt(pPlayer->q16ang);
     if (!(pInput->actions & SB_JUMP))
@@ -1559,69 +1545,29 @@ void ProcessInput(PLAYER *pPlayer)
         }
         pInput->actions &= ~SB_OPEN;
     }
-    if (bVanilla)
+    if ((pInput->actions & SB_CENTERVIEW) && !(pInput->actions & (SB_LOOK_UP | SB_LOOK_DOWN)))
     {
-        if ((pInput->actions & SB_CENTERVIEW) && !(pInput->actions & (SB_LOOK_UP | SB_LOOK_DOWN)))
-        {
-            if (pPlayer->q16look < 0)
-                pPlayer->q16look = min(pPlayer->q16look+IntToFixed(4), 0);
-            if (pPlayer->q16look > 0)
-                pPlayer->q16look = max(pPlayer->q16look-IntToFixed(4), 0);
-            if (!pPlayer->q16look)
-                pInput->actions &= ~SB_CENTERVIEW;
-        }
-        else
-        {
-            if (pInput->actions & (SB_LOOK_UP|SB_AIM_UP))
-                pPlayer->q16look = min(pPlayer->q16look+IntToFixed(4), IntToFixed(60));
-            if (pInput->actions & (SB_LOOK_DOWN|SB_AIM_DOWN))
-                pPlayer->q16look = max(pPlayer->q16look-IntToFixed(4), IntToFixed(-60));
-        }
-        pPlayer->q16look = clamp(pPlayer->q16look+pInput->q16horz, IntToFixed(-60), IntToFixed(60));
+        if (pPlayer->q16look < 0)
+            pPlayer->q16look = min(pPlayer->q16look+IntToFixed(4), 0);
         if (pPlayer->q16look > 0)
-            pPlayer->q16horiz = IntToFixed(mulscale30(120, Sin(FixedToInt(pPlayer->q16look)<<3)));
-        else if (pPlayer->q16look < 0)
-            pPlayer->q16horiz = IntToFixed(mulscale30(180, Sin(FixedToInt(pPlayer->q16look)<<3)));
-        else
-            pPlayer->q16horiz = 0;
+            pPlayer->q16look = max(pPlayer->q16look-IntToFixed(4), 0);
+        if (!pPlayer->q16look)
+            pInput->actions &= ~SB_CENTERVIEW;
     }
     else
     {
-        int upAngle = 289;
-        int downAngle = -347;
-        double lookStepUp = 4.0*upAngle/60.0;
-        double lookStepDown = -4.0*downAngle/60.0;
-        if ((pInput->actions & SB_CENTERVIEW) && !(pInput->actions & (SB_LOOK_UP | SB_LOOK_DOWN)))
-        {
-            if (pPlayer->q16look < 0)
-                pPlayer->q16look = min(pPlayer->q16look+FloatToFixed(lookStepDown), 0);
-            if (pPlayer->q16look > 0)
-                pPlayer->q16look = max(pPlayer->q16look-FloatToFixed(lookStepUp), 0);
-            if (!pPlayer->q16look)
-                pInput->actions &= ~SB_CENTERVIEW;
-        }
-        else
-        {
-            if (pInput->actions & (SB_LOOK_UP | SB_AIM_UP))
-                pPlayer->q16look = min(pPlayer->q16look+FloatToFixed(lookStepUp), IntToFixed(upAngle));
-            if (pInput->actions & (SB_LOOK_DOWN | SB_AIM_DOWN))
-                pPlayer->q16look = max(pPlayer->q16look-FloatToFixed(lookStepDown), IntToFixed(downAngle));
-        }
-        if (pPlayer == gMe && numplayers == 1)
-        {
-            if (pInput->actions & (SB_LOOK_UP | SB_AIM_UP))
-            {
-                gViewLookAdjust += float(lookStepUp);
-            }
-            if (pInput->actions & (SB_LOOK_DOWN | SB_AIM_DOWN))
-            {
-                gViewLookAdjust -= float(lookStepDown);
-            }
-            gViewLookRecenter = ((pInput->actions & SB_CENTERVIEW) && !(pInput->actions & (SB_LOOK_UP | SB_LOOK_DOWN)));
-        }
-        pPlayer->q16look = clamp(pPlayer->q16look+(pInput->q16horz<<3), IntToFixed(downAngle), IntToFixed(upAngle));
-        pPlayer->q16horiz = FloatToFixed(100.f*tanf(FixedToFloat(pPlayer->q16look)*fPI/1024.f));
+        if (pInput->actions & (SB_LOOK_UP|SB_AIM_UP))
+            pPlayer->q16look = min(pPlayer->q16look+IntToFixed(4), IntToFixed(60));
+        if (pInput->actions & (SB_LOOK_DOWN|SB_AIM_DOWN))
+            pPlayer->q16look = max(pPlayer->q16look-IntToFixed(4), IntToFixed(-60));
     }
+    pPlayer->q16look = clamp(pPlayer->q16look+pInput->q16horz, IntToFixed(-60), IntToFixed(60));
+    if (pPlayer->q16look > 0)
+        pPlayer->q16horiz = IntToFixed(mulscale30(120, Sin(FixedToInt(pPlayer->q16look)<<3)));
+    else if (pPlayer->q16look < 0)
+        pPlayer->q16horiz = IntToFixed(mulscale30(180, Sin(FixedToInt(pPlayer->q16look)<<3)));
+    else
+        pPlayer->q16horiz = 0;
     int nSector = pSprite->sectnum;
     int florhit = gSpriteHit[pSprite->extra].florhit & 0xc000;
     char va;
diff --git a/source/blood/src/player.h b/source/blood/src/player.h
index 908766a40..6aa96752a 100644
--- a/source/blood/src/player.h
+++ b/source/blood/src/player.h
@@ -27,7 +27,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 #include "common_game.h"
 #include "compat.h"
 #include "globals.h"
-#include "controls.h"
 #include "db.h"
 #include "dude.h"
 #include "levels.h"
diff --git a/source/blood/src/prediction.cpp b/source/blood/src/prediction.cpp
index 718621aaf..0f0cf4111 100644
--- a/source/blood/src/prediction.cpp
+++ b/source/blood/src/prediction.cpp
@@ -93,11 +93,6 @@ void viewInitializePrediction(void)
 	predict.at40 = gMe->zWeapon;
 	predict.at44 = gMe->zWeaponVel;
     predictOld = predict;
-    if (numplayers != 1)
-    {
-        gViewAngle = predict.at30;
-        gViewLook = predict.at20;
-    }
 }
 
 void viewUpdatePrediction(InputPacket *pInput)
@@ -110,11 +105,6 @@ void viewUpdatePrediction(InputPacket *pInput)
     gMe->pSprite->cstat = bakCstat;
     //predictFifo[gPredictTail&255] = predict;
     //gPredictTail++;
-    if (numplayers != 1)
-    {
-        gViewAngle = predict.at30;
-        gViewLook = predict.at20;
-    }
 }
 
 static void sub_158B4(PLAYER *pPlayer)
@@ -127,13 +117,6 @@ static void fakeProcessInput(PLAYER *pPlayer, InputPacket *pInput)
 {
     POSTURE *pPosture = &pPlayer->pPosture[pPlayer->lifeMode][predict.at48];
 
-    if (numplayers > 1 && gPrediction)
-    {
-        gViewAngleAdjust = 0.f;
-        gViewLookRecenter = false;
-        gViewLookAdjust = 0.f;
-    }
-
     predict.at70 = !!(gMe->input.actions & SB_RUN);
     predict.at71 = !!(gMe->input.actions & SB_JUMP);
     if (predict.at48 == 1)
@@ -202,8 +185,6 @@ static void fakeProcessInput(PLAYER *pPlayer, InputPacket *pInput)
 
         predict.at4c = min(predict.at4c+speed, 0);
         predict.at30 += IntToFixed(speed);
-        if (numplayers > 1 && gPrediction)
-            gViewAngleAdjust += float(speed);
     }
 
     if (!predict.at71)
@@ -231,8 +212,8 @@ static void fakeProcessInput(PLAYER *pPlayer, InputPacket *pInput)
             predict.at48 = 2;
         break;
     }
-#if 0
-    if (predict.at6e && !pInput->syncFlags.lookUp && !pInput->syncFlags.lookDown)
+
+    if (predict.at6e && !(pInput->actions & (SB_LOOK_UP | SB_LOOK_DOWN)))
     {
         if (predict.at20 < 0)
             predict.at20 = min(predict.at20+IntToFixed(4), 0);
@@ -243,12 +224,12 @@ static void fakeProcessInput(PLAYER *pPlayer, InputPacket *pInput)
     }
     else
     {
-        if (pInput->syncFlags.lookUp)
+        if (pInput->actions & SB_LOOK_UP)
             predict.at20 = min(predict.at20+IntToFixed(4), IntToFixed(60));
-        if (pInput->syncFlags.lookDown)
+        if (pInput->actions & SB_LOOK_DOWN)
             predict.at20 = max(predict.at20-IntToFixed(4), IntToFixed(-60));
     }
-    predict.at20 = clamp(predict.at20+pInput->q16mlook, IntToFixed(-60), IntToFixed(60));
+    predict.at20 = clamp(predict.at20+pInput->q16horz, IntToFixed(-60), IntToFixed(60));
 
     if (predict.at20 > 0)
         predict.at24 = mulscale30(IntToFixed(120), Sin(FixedToInt(predict.at20<<3)));
@@ -256,41 +237,6 @@ static void fakeProcessInput(PLAYER *pPlayer, InputPacket *pInput)
         predict.at24 = mulscale30(IntToFixed(180), Sin(FixedToInt(predict.at20<<3)));
     else
         predict.at24 = 0;
-#endif
-    int upAngle = 289;
-    int downAngle = -347;
-    double lookStepUp = 4.0*upAngle/60.0;
-    double lookStepDown = -4.0*downAngle/60.0;
-    if (predict.at6e && !(pInput->actions & (SB_LOOK_UP | SB_LOOK_DOWN)))
-    {
-        if (predict.at20 < 0)
-            predict.at20 = min(predict.at20+FloatToFixed(lookStepDown), 0);
-        if (predict.at20 > 0)
-            predict.at20 = max(predict.at20-FloatToFixed(lookStepUp), 0);
-        if (predict.at20 == 0)
-            predict.at6e = 0;
-    }
-    else
-    {
-        if (pInput->actions & (SB_LOOK_UP | SB_AIM_UP))
-            predict.at20 = min(predict.at20+FloatToFixed(lookStepUp), IntToFixed(upAngle));
-        if (pInput->actions & (SB_LOOK_DOWN | SB_AIM_DOWN))
-            predict.at20 = max(predict.at20-FloatToFixed(lookStepDown), IntToFixed(downAngle));
-    }
-    if (numplayers > 1 && gPrediction)
-    {
-        if (pInput->actions & (SB_LOOK_UP | SB_AIM_UP))
-        {
-            gViewLookAdjust += float(lookStepUp);
-        }
-        if (pInput->actions & (SB_LOOK_DOWN | SB_AIM_DOWN))
-        {
-            gViewLookAdjust -= float(lookStepDown);
-        }
-        gViewLookRecenter = predict.at6e && !(pInput->actions & (SB_LOOK_UP | SB_LOOK_DOWN));
-    }
-    predict.at20 = clamp(predict.at20+(pInput->q16horz<<3), IntToFixed(downAngle), IntToFixed(upAngle));
-    predict.at24 = FloatToFixed(100.f*tanf(FixedToFloat(predict.at20)*fPI/1024.f));
 
     int nSector = predict.at68;
     int florhit = predict.at75.florhit & 0xc000;
@@ -706,12 +652,6 @@ void fakeActProcessSprites(void)
 void viewCorrectPrediction(void)
 {
 #if 0
-    if (numplayers == 1)
-    {
-        gViewLook = gMe->q16look;
-        gViewAngle = gMe->q16ang;
-        return;
-    }
     spritetype *pSprite = gMe->pSprite;
     VIEW *pView = &predictFifo[(gNetFifoTail-1)&255];
     if (gMe->q16ang != pView->at30 || pView->at24 != gMe->q16horiz || pView->at50 != pSprite->x || pView->at54 != pSprite->y || pView->at58 != pSprite->z)
diff --git a/source/blood/src/view.cpp b/source/blood/src/view.cpp
index cc3080621..bc536fb44 100644
--- a/source/blood/src/view.cpp
+++ b/source/blood/src/view.cpp
@@ -681,15 +681,6 @@ void viewDrawScreen(bool sceneonly)
                 v48 = finterpolate(pView->at18, v48, gInterpolate);
             }
         }
-        if (!cl_syncinput && gView == gMe && (numplayers <= 1 || gPrediction) && gView->pXSprite->health != 0 && !VanillaMode())
-        {
-            int upAngle = 289;
-            int downAngle = -347;
-            fixed_t q16look;
-            cA = gViewAngle;
-            q16look = gViewLook;
-            q16horiz = FloatToFixed(100.f * tanf(FixedToFloat(q16look) * fPI / 1024.f));
-        }
         viewUpdateShake();
         q16horiz += IntToFixed(shakeHoriz);
         cA += IntToFixed(shakeAngle);
diff --git a/source/blood/src/view.h b/source/blood/src/view.h
index 656f0db07..f987e278e 100644
--- a/source/blood/src/view.h
+++ b/source/blood/src/view.h
@@ -25,7 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 #include "build.h"
 #include "palette.h"
 #include "common_game.h"
-#include "controls.h"
 #include "messages.h"
 #include "player.h"
 

From 4d47fe7e85b9c812e5303eda30d4f89573134b24 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Wed, 16 Sep 2020 10:14:04 +1000
Subject: [PATCH 04/41] - Blood: Remove `bVanilla`.

---
 source/blood/src/actor.cpp    | 7 +------
 source/blood/src/blood.cpp    | 5 ++---
 source/blood/src/db.cpp       | 5 ++---
 source/blood/src/globals.cpp  | 1 -
 source/blood/src/globals.h    | 1 -
 source/blood/src/loadsave.cpp | 3 +--
 source/blood/src/player.cpp   | 7 +------
 7 files changed, 7 insertions(+), 22 deletions(-)

diff --git a/source/blood/src/actor.cpp b/source/blood/src/actor.cpp
index b7cd59b95..464072dc9 100644
--- a/source/blood/src/actor.cpp
+++ b/source/blood/src/actor.cpp
@@ -4728,12 +4728,7 @@ void MoveDude(spritetype *pSprite)
     {
         GetZRange(pSprite, &ceilZ, &ceilHit, &floorZ, &floorHit, wd, CLIPMASK0, PARALLAXCLIP_CEILING|PARALLAXCLIP_FLOOR);
         if (pPlayer)
-        {
-            if (bVanilla)
-                playerResetInertia(pPlayer);
-            else
-                playerCorrectInertia(pPlayer, &oldpos);
-        }
+            playerCorrectInertia(pPlayer, &oldpos);
         switch (nLink) {
         case kMarkerLowStack:
             if (pPlayer == gView)
diff --git a/source/blood/src/blood.cpp b/source/blood/src/blood.cpp
index dc618103c..58e8b1afa 100644
--- a/source/blood/src/blood.cpp
+++ b/source/blood/src/blood.cpp
@@ -123,7 +123,6 @@ void StartLevel(MapRecord* level)
 			gHealthTemp[i] = xsprite[gPlayer[i].pSprite->extra].health;
 		}
 	}
-	bVanilla = false;
 	memset(xsprite, 0, sizeof(xsprite));
 	memset(sprite, 0, kMaxSprites * sizeof(spritetype));
 	//drawLoadingScreen();
@@ -228,7 +227,7 @@ void StartLevel(MapRecord* level)
 	PreloadCache();
 	InitMirrors();
 	trInit();
-	if (!bVanilla && !gMe->packSlots[1].isActive) // if diving suit is not active, turn off reverb sound effect
+	if (!gMe->packSlots[1].isActive) // if diving suit is not active, turn off reverb sound effect
 		sfxSetReverb(0);
 	ambInit();
 	Net_ClearFifo();
@@ -442,7 +441,7 @@ void GameInterface::app_init()
 	gChoke.init(518, sub_84230);
 	UpdateDacs(0, true);
 
-	enginecompatibility_mode = ENGINECOMPATIBILITY_19960925;//bVanilla;
+	enginecompatibility_mode = ENGINECOMPATIBILITY_19960925;
 }
 
 static void gameInit()
diff --git a/source/blood/src/db.cpp b/source/blood/src/db.cpp
index 4fac8c7aa..f84d21632 100644
--- a/source/blood/src/db.cpp
+++ b/source/blood/src/db.cpp
@@ -162,7 +162,7 @@ void qinitspritelists(void) // Replace
     {
         headspritestat[i] = -1;
     }
-    int const nMaxSprites = bVanilla ? 4096 : kMaxSprites;
+    int const nMaxSprites = kMaxSprites;
     for (short i = 0; i < nMaxSprites; i++)
     {
         sprite[i].sectnum = -1;
@@ -285,8 +285,7 @@ unsigned short dbInsertXSprite(int nSprite)
         ThrowError("Out of free XSprites");
     }
     memset(&xsprite[nXSprite], 0, sizeof(XSPRITE));
-    if (!bVanilla)
-        memset(&gSpriteHit[nXSprite], 0, sizeof(SPRITEHIT));
+    memset(&gSpriteHit[nXSprite], 0, sizeof(SPRITEHIT));
     xsprite[nXSprite].reference = nSprite;
     sprite[nSprite].extra = nXSprite;
     return nXSprite;
diff --git a/source/blood/src/globals.cpp b/source/blood/src/globals.cpp
index c517b7a51..282bfa52d 100644
--- a/source/blood/src/globals.cpp
+++ b/source/blood/src/globals.cpp
@@ -32,7 +32,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 BEGIN_BLD_NS
 
-bool bVanilla = false;
 int gFrameClock;
 int gFrameCount;
 
diff --git a/source/blood/src/globals.h b/source/blood/src/globals.h
index 5b784e21f..03bd9a744 100644
--- a/source/blood/src/globals.h
+++ b/source/blood/src/globals.h
@@ -32,7 +32,6 @@ BEGIN_BLD_NS
 
 extern int gFrameClock;
 extern int gFrameCount;
-extern bool bVanilla;
 
 #define MAXPLAYERNAME 16
 
diff --git a/source/blood/src/loadsave.cpp b/source/blood/src/loadsave.cpp
index 56e80fb10..fd6eaae28 100644
--- a/source/blood/src/loadsave.cpp
+++ b/source/blood/src/loadsave.cpp
@@ -498,7 +498,7 @@ bool GameInterface::LoadGame(FSaveGameNode* node)
     InitSectorFX();
     viewInitializePrediction();
     PreloadCache();
-    if (!bVanilla && !gMe->packSlots[1].isActive) // if diving suit is not active, turn off reverb sound effect
+    if (!gMe->packSlots[1].isActive) // if diving suit is not active, turn off reverb sound effect
         sfxSetReverb(0);
     ambInit();
     for (int i = 0; i < gNetPlayers; i++)
@@ -506,7 +506,6 @@ bool GameInterface::LoadGame(FSaveGameNode* node)
 	viewSetErrorMessage("");
     Net_ClearFifo();
     paused = 0;
-    bVanilla = false;
     
 
 #ifdef USE_OPENGL
diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index dfc3542b2..42ab50308 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1336,12 +1336,7 @@ void ProcessInput(PLAYER *pPlayer)
         }
         pPlayer->deathTime += 4;
         if (!bSeqStat)
-        {
-            if (bVanilla)
-                pPlayer->q16horiz = IntToFixed(mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime*8, 1024))>>15), 120));
-            else
-                pPlayer->q16horiz = mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime*8, 1024))>>15), IntToFixed(120));
-        }
+            pPlayer->q16horiz = mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime<<3, 1024))>>15), IntToFixed(120));
         if (pPlayer->curWeapon)
             pInput->setNewWeapon(pPlayer->curWeapon);
         if (pInput->actions & SB_OPEN)

From 20c73e124987773f48d39d92003a15ee10d662f6 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Wed, 16 Sep 2020 10:34:53 +1000
Subject: [PATCH 05/41] - Blood: Remove `r_horizcenter` and standardise horizon
 in game to that of the default in the backend (currently 100).

* The use of this will go when the input is re-factored to operate on the standard horizon of 100, not 0.
---
 source/blood/src/hudsprites.cpp | 2 +-
 source/blood/src/view.cpp       | 9 ++-------
 source/blood/src/view.h         | 2 +-
 source/core/gamecvars.cpp       | 2 --
 source/core/gamecvars.h         | 1 -
 5 files changed, 4 insertions(+), 12 deletions(-)

diff --git a/source/blood/src/hudsprites.cpp b/source/blood/src/hudsprites.cpp
index 130c2f1a4..e969c1323 100644
--- a/source/blood/src/hudsprites.cpp
+++ b/source/blood/src/hudsprites.cpp
@@ -96,7 +96,7 @@ static void viewBurnTime(int gScale)
 }
 
 
-void hudDraw(PLAYER *gView, int nSectnum, int defaultHoriz, double bobx, double boby, double zDelta, int basepal, int smoothratio)
+void hudDraw(PLAYER *gView, int nSectnum, double bobx, double boby, double zDelta, int basepal, int smoothratio)
 {
 	if (gViewPos == 0)
 	{
diff --git a/source/blood/src/view.cpp b/source/blood/src/view.cpp
index bc536fb44..8b4425325 100644
--- a/source/blood/src/view.cpp
+++ b/source/blood/src/view.cpp
@@ -74,7 +74,7 @@ INTERPOLATE gInterpolation[kMaxInterpolations];
 
 int gScreenTilt;
 
-
+int const defaultHoriz = polymostcenterhoriz;
 
 FFont *gFont[kFontNum];
 
@@ -586,7 +586,6 @@ static void DrawMap(spritetype* pSprite)
 void viewDrawScreen(bool sceneonly)
 {
     int nPalette = 0;
-    int defaultHoriz = r_horizcenter ? 100 : 90;
 	
 	if (TestBitString(gotpic, 2342))
 	{
@@ -594,10 +593,6 @@ void viewDrawScreen(bool sceneonly)
 		ClearBitString(gotpic, 2342);
 	}
 
-
-#ifdef USE_OPENGL
-    polymostcenterhoriz = defaultHoriz;
-#endif
     if (!paused && (!M_Active() || gGameOptions.nGameType != 0))
     {
         gInterpolate = I_GetTimeFrac() * MaxSmoothRatio;
@@ -957,7 +952,7 @@ void viewDrawScreen(bool sceneonly)
             }
         }
 #endif
-        hudDraw(gView, nSectnum, defaultHoriz, v4c, v48, zDelta, basepal, (int)gInterpolate);
+        hudDraw(gView, nSectnum, v4c, v48, zDelta, basepal, (int)gInterpolate);
     }
     UpdateDacs(0, true);    // keep the view palette active only for the actual 3D view and its overlays.
     if (automapMode != am_off)
diff --git a/source/blood/src/view.h b/source/blood/src/view.h
index f987e278e..0cba77c2a 100644
--- a/source/blood/src/view.h
+++ b/source/blood/src/view.h
@@ -140,7 +140,7 @@ extern LOCATION gPrevSpriteLoc[kMaxSprites];
 extern int gLastPal;
 extern double gInterpolate;
 
-void hudDraw(PLAYER* gView, int nSectnum, int defaultHoriz, double bobx, double boby, double zDelta, int basepal, int smoothratio);
+void hudDraw(PLAYER* gView, int nSectnum, double bobx, double boby, double zDelta, int basepal, int smoothratio);
 void viewInitializePrediction(void);
 void viewUpdatePrediction(InputPacket *pInput);
 void viewCorrectPrediction(void);
diff --git a/source/core/gamecvars.cpp b/source/core/gamecvars.cpp
index a8951b08a..c7c4548f7 100644
--- a/source/core/gamecvars.cpp
+++ b/source/core/gamecvars.cpp
@@ -207,8 +207,6 @@ CUSTOM_CVARD(Int, r_fov, 90, CVAR_ARCHIVE|CVAR_GLOBALCONFIG, "change the field o
 	else if (self > 140) self = 140;
 }
 
-CVARD(Bool, r_horizcenter, false, CVAR_ARCHIVE, "enable/disable centered horizon line") // only present in Blood.
-
 CVARD(Bool, in_mousemode, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG, "toggles vertical mouse view")
 
 CVAR(Bool, silentmouseaimtoggle, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
diff --git a/source/core/gamecvars.h b/source/core/gamecvars.h
index ae8dc6ccc..bcc11b94d 100644
--- a/source/core/gamecvars.h
+++ b/source/core/gamecvars.h
@@ -71,7 +71,6 @@ EXTERN_CVAR(Bool, am_nameontop)
 
 
 EXTERN_CVAR(Int, r_fov)
-EXTERN_CVAR(Bool, r_horizcenter)
 EXTERN_CVAR(Int, r_drawweapon)
 EXTERN_CVAR(Int, r_showfps)
 EXTERN_CVAR(Int, r_showfpsperiod)

From c38c1cb1f1422c8795f2929d803694c904e8a81f Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Wed, 16 Sep 2020 10:59:28 +1000
Subject: [PATCH 06/41] - Blood: Re-do interpolating code in `viewDrawScreen()`
 in preparation for returning unsynchronised input.

* Changed config so `cl_interpolate` bool doesn't apply to player's overall movement. While it might be handy for debugging moving sector objects, I see no valid reason why there'd be a need to ever un-interpolate the player's position.
---
 source/blood/src/view.cpp | 83 ++++++++++++++++++++++-----------------
 1 file changed, 46 insertions(+), 37 deletions(-)

diff --git a/source/blood/src/view.cpp b/source/blood/src/view.cpp
index 8b4425325..3b8e46192 100644
--- a/source/blood/src/view.cpp
+++ b/source/blood/src/view.cpp
@@ -631,51 +631,60 @@ void viewDrawScreen(bool sceneonly)
         int v1 = xs_CRoundToInt(double(viewingrange) * tan(r_fov * (PI / 360.)));
 
         renderSetAspect(v1, yxaspect);
-        int cX = gView->pSprite->x;
-        int cY = gView->pSprite->y;
-        int cZ = gView->zView;
-        double zDelta = gView->zWeapon - gView->zView - (12 << 8);
-        fixed_t cA = gView->q16ang;
-        fixed_t q16horiz = gView->q16horiz;
-        fixed_t q16slopehoriz = gView->q16slopehoriz;
-        int v74 = gView->bobWidth;
-        int v8c = gView->bobHeight;
-        double v4c = gView->swayWidth;
-        double v48 = gView->swayHeight;
+
+        int cX, cY, cZ, v74, v8c;
+        fixed_t cA, q16horiz, q16slopehoriz;
+        double zDelta, v4c, v48;
         int nSectnum = gView->pSprite->sectnum;
-        if (cl_interpolate)
+        if (numplayers > 1 && gView == gMe && gPrediction && gMe->pXSprite->health > 0)
         {
-            if (numplayers > 1 && gView == gMe && gPrediction && gMe->pXSprite->health > 0)
+            nSectnum = predict.at68;
+            cX = interpolate(predictOld.at50, predict.at50, gInterpolate);
+            cY = interpolate(predictOld.at54, predict.at54, gInterpolate);
+            cZ = interpolate(predictOld.at38, predict.at38, gInterpolate);
+            zDelta = finterpolate(predictOld.at34, predict.at34, gInterpolate);
+            q16slopehoriz = interpolate(predictOld.at28, predict.at28, gInterpolate);
+            v74 = interpolate(predictOld.atc, predict.atc, gInterpolate);
+            v8c = interpolate(predictOld.at8, predict.at8, gInterpolate);
+            v4c = finterpolate(predictOld.at1c, predict.at1c, gInterpolate);
+            v48 = finterpolate(predictOld.at18, predict.at18, gInterpolate);
+
+            if (!cl_syncinput)
             {
-                nSectnum = predict.at68;
-                cX = interpolate(predictOld.at50, predict.at50, gInterpolate);
-                cY = interpolate(predictOld.at54, predict.at54, gInterpolate);
-                cZ = interpolate(predictOld.at38, predict.at38, gInterpolate);
-                zDelta = finterpolate(predictOld.at34, predict.at34, gInterpolate);
-                cA = interpolateangfix16(predictOld.at30, predict.at30, gInterpolate);
-                q16horiz = interpolate(predictOld.at24, predict.at24, gInterpolate);
-                q16slopehoriz = interpolate(predictOld.at28, predict.at28, gInterpolate);
-                v74 = interpolate(predictOld.atc, predict.atc, gInterpolate);
-                v8c = interpolate(predictOld.at8, predict.at8, gInterpolate);
-                v4c = finterpolate(predictOld.at1c, predict.at1c, gInterpolate);
-                v48 = finterpolate(predictOld.at18, predict.at18, gInterpolate);
+                cA = predict.at30;
+                q16horiz = predict.at24;
             }
             else
             {
-                VIEW* pView = &gPrevView[gViewIndex];
-                cX = interpolate(pView->at50, cX, gInterpolate);
-                cY = interpolate(pView->at54, cY, gInterpolate);
-                cZ = interpolate(pView->at38, cZ, gInterpolate);
-                zDelta = finterpolate(pView->at34, zDelta, gInterpolate);
-                cA = interpolateangfix16(pView->at30, cA, gInterpolate);
-                q16horiz = interpolate(pView->at24, q16horiz, gInterpolate);
-                q16slopehoriz = interpolate(pView->at28, q16slopehoriz, gInterpolate);
-                v74 = interpolate(pView->atc, v74, gInterpolate);
-                v8c = interpolate(pView->at8, v8c, gInterpolate);
-                v4c = finterpolate(pView->at1c, v4c, gInterpolate);
-                v48 = finterpolate(pView->at18, v48, gInterpolate);
+                cA = interpolateangfix16(predictOld.at30, predict.at30, gInterpolate);
+                q16horiz = interpolate(predictOld.at24, predict.at24, gInterpolate);
             }
         }
+        else
+        {
+            VIEW* pView = &gPrevView[gViewIndex];
+            cX = interpolate(pView->at50, gView->pSprite->x, gInterpolate);
+            cY = interpolate(pView->at54, gView->pSprite->y, gInterpolate);
+            cZ = interpolate(pView->at38, gView->zView, gInterpolate);
+            zDelta = finterpolate(pView->at34, gView->zWeapon - gView->zView - (12 << 8), gInterpolate);
+            q16slopehoriz = interpolate(pView->at28, gView->q16slopehoriz, gInterpolate);
+            v74 = interpolate(pView->atc, gView->bobWidth, gInterpolate);
+            v8c = interpolate(pView->at8, gView->bobHeight, gInterpolate);
+            v4c = finterpolate(pView->at1c, gView->swayWidth, gInterpolate);
+            v48 = finterpolate(pView->at18, gView->swayHeight, gInterpolate);
+
+            if (!cl_syncinput)
+            {
+                cA = gView->q16ang;
+                q16horiz = gView->q16horiz;
+            }
+            else
+            {
+                cA = interpolateangfix16(pView->at30, gView->q16ang, gInterpolate);
+                q16horiz = interpolate(pView->at24, gView->q16horiz, gInterpolate);
+            }
+        }
+
         viewUpdateShake();
         q16horiz += IntToFixed(shakeHoriz);
         cA += IntToFixed(shakeAngle);

From 547bf110b2b8cfeb64212e7bd63488a4802b8493 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Wed, 16 Sep 2020 12:31:32 +1000
Subject: [PATCH 07/41] - Blood: Create high-precision `Sinf()` and `Cosf()`
 inlines and use for player's horizon, using `calcSinTableValue()` from the
 backend, bypassing game's `costable[]`.

---
 source/blood/src/misc.h         | 10 ++++++++++
 source/blood/src/player.cpp     |  4 ++--
 source/blood/src/prediction.cpp |  4 ++--
 3 files changed, 14 insertions(+), 4 deletions(-)

diff --git a/source/blood/src/misc.h b/source/blood/src/misc.h
index 6a3e9b442..3b539d1d0 100644
--- a/source/blood/src/misc.h
+++ b/source/blood/src/misc.h
@@ -90,6 +90,16 @@ inline int Cos(int ang)
     return costable[ang & 2047];
 }
 
+inline double Sinf(double ang)
+{
+    return (1 << 30) * sin(BANG2RAD * ang);
+}
+
+inline double Cosf(double ang)
+{
+    return (1 << 30) * sin(BANG2RAD * (ang + 512.));
+}
+
 inline int SinScale16(int ang)
 {
     return FixedToInt(costable[(ang - 512) & 2047]);
diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index 42ab50308..6606e4498 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1558,9 +1558,9 @@ void ProcessInput(PLAYER *pPlayer)
     }
     pPlayer->q16look = clamp(pPlayer->q16look+pInput->q16horz, IntToFixed(-60), IntToFixed(60));
     if (pPlayer->q16look > 0)
-        pPlayer->q16horiz = IntToFixed(mulscale30(120, Sin(FixedToInt(pPlayer->q16look)<<3)));
+        pPlayer->q16horiz = FloatToFixed(fmulscale30(120., Sinf(FixedToFloat(pPlayer->q16look) * 8.)));
     else if (pPlayer->q16look < 0)
-        pPlayer->q16horiz = IntToFixed(mulscale30(180, Sin(FixedToInt(pPlayer->q16look)<<3)));
+        pPlayer->q16horiz = FloatToFixed(fmulscale30(180., Sinf(FixedToFloat(pPlayer->q16look) * 8.)));
     else
         pPlayer->q16horiz = 0;
     int nSector = pSprite->sectnum;
diff --git a/source/blood/src/prediction.cpp b/source/blood/src/prediction.cpp
index 0f0cf4111..03cfa333f 100644
--- a/source/blood/src/prediction.cpp
+++ b/source/blood/src/prediction.cpp
@@ -232,9 +232,9 @@ static void fakeProcessInput(PLAYER *pPlayer, InputPacket *pInput)
     predict.at20 = clamp(predict.at20+pInput->q16horz, IntToFixed(-60), IntToFixed(60));
 
     if (predict.at20 > 0)
-        predict.at24 = mulscale30(IntToFixed(120), Sin(FixedToInt(predict.at20<<3)));
+        predict.at24 = FloatToFixed(fmulscale30(120., Sinf(FixedToFloat(predict.at20) * 8.)));
     else if (predict.at20 < 0)
-        predict.at24 = mulscale30(IntToFixed(180), Sin(FixedToInt(predict.at20<<3)));
+        predict.at24 = FloatToFixed(fmulscale30(180., Sinf(FixedToFloat(predict.at20) * 8.)));
     else
         predict.at24 = 0;
 

From ae5b6b6a91c7115242283176ad99a2b105e2000e Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Wed, 16 Sep 2020 18:22:01 +1000
Subject: [PATCH 08/41] - Blood: Re-implement unsynchronised q16ang.

---
 source/blood/src/controls.cpp |  7 +++++
 source/blood/src/player.cpp   | 57 ++++++++++++++++++++++++-----------
 2 files changed, 46 insertions(+), 18 deletions(-)

diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index 1d0e55c03..47cadf2fb 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -39,6 +39,8 @@ BEGIN_BLD_NS
 
 static InputPacket gInput;
 
+void applylook(PLAYER *pPlayer, fixed_t const q16avel, double const scaleAdjust);
+
 static void GetInputInternal(ControlInfo* const hidInput)
 {
     double const scaleAdjust = InputScale();
@@ -148,6 +150,11 @@ static void GetInputInternal(ControlInfo* const hidInput)
     gInput.svel = clamp(gInput.svel + input.svel, -2048, 2048);
     gInput.q16avel += input.q16avel;
     gInput.q16horz = clamp(gInput.q16horz + input.q16horz, IntToFixed(-127) >> 2, IntToFixed(127) >> 2);
+
+    if (!cl_syncinput && gamestate == GS_LEVEL)
+    {
+        applylook(&gPlayer[myconnectindex], input.q16avel, scaleAdjust);
+    }
 }
 
 void GameInterface::GetInput(InputPacket* packet, ControlInfo* const hidInput)
diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index 6606e4498..40422f69d 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1304,6 +1304,41 @@ int ActionScan(PLAYER *pPlayer, int *a2, int *a3)
     return -1;
 }
 
+void applylook(PLAYER *pPlayer, fixed_t const q16avel, double const scaleAdjust)
+{
+    spritetype *pSprite = pPlayer->pSprite;
+    InputPacket *pInput = &pPlayer->input;
+
+    if (q16avel)
+    {
+        pPlayer->q16ang = (pPlayer->q16ang + q16avel) & 0x7FFFFFF;
+    }
+
+    if (pInput->actions & SB_TURNAROUND)
+    {
+        if (pPlayer->spin == 0.)
+        {
+            pPlayer->spin = -1024.;
+        }
+        pInput->actions &= ~SB_TURNAROUND;
+    }
+
+    if (pPlayer->spin < 0.)
+    {
+        double const speed = scaleAdjust * (pPlayer->posture == 1 ? 64. : 128.);
+        pPlayer->spin = min(pPlayer->spin + speed, 0.);
+        pPlayer->q16ang += FloatToFixed(speed);
+
+        if (pPlayer->spin > -1.)
+        {
+            pPlayer->spin = 0.;
+        }
+    }
+
+    pPlayer->q16ang = (pPlayer->q16ang + IntToFixed(pSprite->ang - pPlayer->angold)) & 0x7FFFFFF;
+    pPlayer->angold = pSprite->ang = FixedToInt(pPlayer->q16ang);
+}
+
 void ProcessInput(PLAYER *pPlayer)
 {
     enum
@@ -1415,26 +1450,12 @@ void ProcessInput(PLAYER *pPlayer)
             yvel[nSprite] -= mulscale30(strafe, x);
         }
     }
-    if (pInput->q16avel)
-        pPlayer->q16ang = (pPlayer->q16ang+pInput->q16avel)&0x7ffffff;
-    if (pInput->actions & SB_TURNAROUND)
+
+    if (cl_syncinput)
     {
-        if (!pPlayer->spin)
-            pPlayer->spin = -1024;
-        pInput->actions &= ~SB_TURNAROUND;
+        applylook(pPlayer, pInput->q16avel, 1);
     }
-    if (pPlayer->spin < 0)
-    {
-        int speed;
-        if (pPlayer->posture == 1)
-            speed = 64;
-        else
-            speed = 128;
-        pPlayer->spin = min(pPlayer->spin+speed, 0);
-        pPlayer->q16ang += IntToFixed(speed);
-    }
-    pPlayer->q16ang = (pPlayer->q16ang+IntToFixed(pSprite->ang-pPlayer->angold))&0x7ffffff;
-    pPlayer->angold = pSprite->ang = FixedToInt(pPlayer->q16ang);
+
     if (!(pInput->actions & SB_JUMP))
         pPlayer->cantJump = 0;
 

From eb0bd9638b40e806596d681d3a929dd301acc588 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Wed, 16 Sep 2020 18:32:39 +1000
Subject: [PATCH 09/41] - Blood: Re-implement unsynchronised q16horiz.

---
 source/blood/src/controls.cpp |  2 ++
 source/blood/src/player.cpp   | 65 ++++++++++++++++++++++++-----------
 2 files changed, 46 insertions(+), 21 deletions(-)

diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index 47cadf2fb..ed650be3b 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -40,6 +40,7 @@ BEGIN_BLD_NS
 static InputPacket gInput;
 
 void applylook(PLAYER *pPlayer, fixed_t const q16avel, double const scaleAdjust);
+void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust);
 
 static void GetInputInternal(ControlInfo* const hidInput)
 {
@@ -154,6 +155,7 @@ static void GetInputInternal(ControlInfo* const hidInput)
     if (!cl_syncinput && gamestate == GS_LEVEL)
     {
         applylook(&gPlayer[myconnectindex], input.q16avel, scaleAdjust);
+        sethorizon(&gPlayer[myconnectindex], input.q16horz, scaleAdjust);
     }
 }
 
diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index 40422f69d..1bf1b3e29 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1339,6 +1339,46 @@ void applylook(PLAYER *pPlayer, fixed_t const q16avel, double const scaleAdjust)
     pPlayer->angold = pSprite->ang = FixedToInt(pPlayer->q16ang);
 }
 
+void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust)
+{
+    InputPacket *pInput = &pPlayer->input;
+
+    if ((pInput->actions & SB_CENTERVIEW) && !(pInput->actions & (SB_LOOK_UP | SB_LOOK_DOWN)))
+    {
+        if (pPlayer->q16look < 0)
+            pPlayer->q16look = min(pPlayer->q16look + FloatToFixed(scaleAdjust * 4.), 0);
+
+        if (pPlayer->q16look > 0)
+            pPlayer->q16look = max(pPlayer->q16look - FloatToFixed(scaleAdjust * 4.), 0);
+
+        if (!pPlayer->q16look)
+            pInput->actions &= ~SB_CENTERVIEW;
+    }
+    else
+    {
+        if (pInput->actions & (SB_LOOK_UP|SB_AIM_UP))
+            pPlayer->q16look = min(pPlayer->q16look + FloatToFixed(scaleAdjust * 4.), IntToFixed(60));
+
+        if (pInput->actions & (SB_LOOK_DOWN|SB_AIM_DOWN))
+            pPlayer->q16look = max(pPlayer->q16look - FloatToFixed(scaleAdjust * 4.), IntToFixed(-60));
+    }
+
+    pPlayer->q16look = clamp(pPlayer->q16look + q16horz, IntToFixed(-60), IntToFixed(60));
+
+    if (pPlayer->q16look > 0)
+    {
+        pPlayer->q16horiz = FloatToFixed(fmulscale30(120., Sinf(FixedToFloat(pPlayer->q16look) * 8.)));
+    }
+    else if (pPlayer->q16look < 0)
+    {
+        pPlayer->q16horiz = FloatToFixed(fmulscale30(180., Sinf(FixedToFloat(pPlayer->q16look) * 8.)));
+    }
+    else
+    {
+        pPlayer->q16horiz = 0;
+    }
+}
+
 void ProcessInput(PLAYER *pPlayer)
 {
     enum
@@ -1561,29 +1601,12 @@ void ProcessInput(PLAYER *pPlayer)
         }
         pInput->actions &= ~SB_OPEN;
     }
-    if ((pInput->actions & SB_CENTERVIEW) && !(pInput->actions & (SB_LOOK_UP | SB_LOOK_DOWN)))
+
+    if (cl_syncinput)
     {
-        if (pPlayer->q16look < 0)
-            pPlayer->q16look = min(pPlayer->q16look+IntToFixed(4), 0);
-        if (pPlayer->q16look > 0)
-            pPlayer->q16look = max(pPlayer->q16look-IntToFixed(4), 0);
-        if (!pPlayer->q16look)
-            pInput->actions &= ~SB_CENTERVIEW;
+        sethorizon(pPlayer, pInput->q16horz, 1);
     }
-    else
-    {
-        if (pInput->actions & (SB_LOOK_UP|SB_AIM_UP))
-            pPlayer->q16look = min(pPlayer->q16look+IntToFixed(4), IntToFixed(60));
-        if (pInput->actions & (SB_LOOK_DOWN|SB_AIM_DOWN))
-            pPlayer->q16look = max(pPlayer->q16look-IntToFixed(4), IntToFixed(-60));
-    }
-    pPlayer->q16look = clamp(pPlayer->q16look+pInput->q16horz, IntToFixed(-60), IntToFixed(60));
-    if (pPlayer->q16look > 0)
-        pPlayer->q16horiz = FloatToFixed(fmulscale30(120., Sinf(FixedToFloat(pPlayer->q16look) * 8.)));
-    else if (pPlayer->q16look < 0)
-        pPlayer->q16horiz = FloatToFixed(fmulscale30(180., Sinf(FixedToFloat(pPlayer->q16look) * 8.)));
-    else
-        pPlayer->q16horiz = 0;
+
     int nSector = pSprite->sectnum;
     int florhit = gSpriteHit[pSprite->extra].florhit & 0xc000;
     char va;

From 9c56dfffe0fec38942a226de71b2e50c0184fe38 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Wed, 16 Sep 2020 20:08:56 +1000
Subject: [PATCH 10/41] - Blood: Apply horizon input outside of
 `pPlayer->q16look` and fix some incorrectly declared types.

* Looking up/down is a bit rough at first as q16look is disproportionate to where the player is currently looking.
* Won't be an issue with a unified horizon algorithm in the backend.
---
 source/blood/src/controls.cpp | 8 ++------
 source/blood/src/player.cpp   | 4 ++--
 source/blood/src/player.h     | 4 ++--
 3 files changed, 6 insertions(+), 10 deletions(-)

diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index ed650be3b..497d147e4 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -106,10 +106,6 @@ static void GetInputInternal(ControlInfo* const hidInput)
     static int32_t lastInputClock;  // MED
     int32_t const  elapsedTics = gFrameClock - lastInputClock;
 
-    // Blood's q16mlook scaling is different from the other games, therefore use the below constant to attenuate
-    // the speed to match the other games.
-    float const mlookScale = 3.25f;
-
     lastInputClock = gFrameClock;
 
     if (turnLeft || turnRight)
@@ -138,14 +134,14 @@ static void GetInputInternal(ControlInfo* const hidInput)
     input.fvel -= xs_CRoundToInt(scaleAdjust * (hidInput->dz * keyMove));
 
     if (mouseaim)
-        input.q16horz += FloatToFixed(hidInput->mousey / mlookScale);
+        input.q16horz += FloatToFixed(hidInput->mousey);
     else
         input.fvel -= xs_CRoundToInt(hidInput->mousey * 64.);
 
     if (!in_mouseflip)
         input.q16horz = -input.q16horz;
 
-    input.q16horz -= FloatToFixed(scaleAdjust * (hidInput->dpitch / mlookScale));
+    input.q16horz -= FloatToFixed(scaleAdjust * hidInput->dpitch);
 
     gInput.fvel = clamp(gInput.fvel + input.fvel, -2048, 2048);
     gInput.svel = clamp(gInput.svel + input.svel, -2048, 2048);
diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index 1bf1b3e29..a2982cfc1 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1363,7 +1363,7 @@ void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust
             pPlayer->q16look = max(pPlayer->q16look - FloatToFixed(scaleAdjust * 4.), IntToFixed(-60));
     }
 
-    pPlayer->q16look = clamp(pPlayer->q16look + q16horz, IntToFixed(-60), IntToFixed(60));
+    pPlayer->q16look = clamp(pPlayer->q16look, IntToFixed(-60), IntToFixed(60));
 
     if (pPlayer->q16look > 0)
     {
@@ -1375,7 +1375,7 @@ void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust
     }
     else
     {
-        pPlayer->q16horiz = 0;
+        pPlayer->q16horiz = clamp(pPlayer->q16horiz + q16horz, IntToFixed(-179), IntToFixed(119));
     }
 }
 
diff --git a/source/blood/src/player.h b/source/blood/src/player.h
index 6aa96752a..0a4cdd947 100644
--- a/source/blood/src/player.h
+++ b/source/blood/src/player.h
@@ -109,8 +109,8 @@ struct PLAYER
     int                 zWeapon;
     int                 zWeaponVel;
     fixed_t             q16look;
-    int                 q16horiz;       // horiz
-    int                 q16slopehoriz;  // horizoff
+    fixed_t             q16horiz;       // horiz
+    fixed_t             q16slopehoriz;  // horizoff
     int                 slope;
     bool                isUnderwater;
     bool                hasKey[8];

From db7793bf5437f6995ce899d4ac6270d9a453d551 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Wed, 16 Sep 2020 20:23:59 +1000
Subject: [PATCH 11/41] - Blood: Clean up remainder of control.cpp in-line with
 Duke/SW.

* Split input bits and movement into separate functions.
* Reordered movement to be more like Duke/SW for ease of comparison.
* Removed a global.
* Removed a few includes.
---
 source/blood/src/blood.cpp    |   2 +-
 source/blood/src/controls.cpp | 211 ++++++++++++++++++----------------
 source/blood/src/globals.cpp  |   1 -
 source/blood/src/globals.h    |   1 -
 4 files changed, 115 insertions(+), 100 deletions(-)

diff --git a/source/blood/src/blood.cpp b/source/blood/src/blood.cpp
index 58e8b1afa..796d199a8 100644
--- a/source/blood/src/blood.cpp
+++ b/source/blood/src/blood.cpp
@@ -337,7 +337,7 @@ void GameInterface::Ticker()
 		thinktime.Unclock();
 
 		gFrameCount++;
-		gFrameClock += 4;
+		gFrameClock += kTicsPerFrame;
 		if (gFrameClock == 8) gameaction = ga_autosave;	// let the game run for 1 frame before saving.
 
 		for (int i = 0; i < 8; i++)
diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index 497d147e4..b76bb37ee 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -23,48 +23,115 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 #include "ns.h"	// Must come before everything else!
 
-#include "compat.h"
 #include "mmulti.h"
-#include "gamecontrol.h"
-#include "common_game.h"
-#include "blood.h"
-#include "globals.h"
-#include "levels.h"
 #include "view.h"
-#include "d_event.h"
 #include "gamestate.h"
-#include "sound.h"
+#include "menu.h"
 
 BEGIN_BLD_NS
 
+static const double gTurnSpeed = 92.;
 static InputPacket gInput;
+static double turnHeldTime;
+
+enum
+{
+    MAXFVEL     = 2048,
+    MAXSVEL     = 2048,
+    MAXHORIZVEL = 32
+};
 
 void applylook(PLAYER *pPlayer, fixed_t const q16avel, double const scaleAdjust);
 void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust);
 
-static void GetInputInternal(ControlInfo* const hidInput)
+//---------------------------------------------------------------------------
+//
+// handles the input bits
+//
+//---------------------------------------------------------------------------
+
+static void processInputBits(ControlInfo* const hidInput, bool* mouseaim)
+{
+    ApplyGlobalInput(gInput, hidInput);
+    *mouseaim = !(gInput.actions & SB_AIMMODE);
+
+    if (!mouseaim || (gInput.actions & (SB_LOOK_UP|SB_LOOK_DOWN)))
+    {
+        gInput.actions |= SB_CENTERVIEW;
+    }
+}
+
+//---------------------------------------------------------------------------
+//
+// handles movement
+//
+//---------------------------------------------------------------------------
+
+static void processMovement(ControlInfo* const hidInput, bool const mouseaim)
 {
     double const scaleAdjust = InputScale();
-    int prevPauseState = paused;
-
-    InputPacket input = {};
-
-    ApplyGlobalInput(gInput, hidInput);
-
-    bool mouseaim = !(gInput.actions & SB_AIMMODE);
-    if (!mouseaim) gInput.actions |= SB_CENTERVIEW;
-
-    if (gPlayer[myconnectindex].nextWeapon == 0)
-    {
-    }
-
-    if (gInput.actions & (SB_LOOK_UP|SB_LOOK_DOWN))
-        gInput.actions |= SB_CENTERVIEW;
-
     int const run = !!(gInput.actions & SB_RUN);
     int const keyMove = (1 + run) << 10;
+    InputPacket input = {};
 
-    if (gInput.fvel < keyMove && gInput.fvel > -keyMove)
+    if (buttonMap.ButtonDown(gamefunc_Strafe))
+    {
+        input.svel -= xs_CRoundToInt((hidInput->mousex * 32.) + (scaleAdjust * (hidInput->dyaw * keyMove)));
+    }
+    else
+    {
+        input.q16avel += FloatToFixed(hidInput->mousex + (scaleAdjust * hidInput->dyaw));
+    }
+
+    if (mouseaim)
+    {
+        input.q16horz += FloatToFixed(hidInput->mousey);
+    }
+    else
+    {
+        input.fvel -= xs_CRoundToInt(hidInput->mousey * 64.);
+    }
+
+    if (!in_mouseflip)
+        input.q16horz = -input.q16horz;
+
+    input.q16horz -= FloatToFixed(scaleAdjust * hidInput->dpitch);
+    input.svel -= xs_CRoundToInt(scaleAdjust * (hidInput->dx * keyMove));
+    input.fvel -= xs_CRoundToInt(scaleAdjust * (hidInput->dz * keyMove));
+
+    if (buttonMap.ButtonDown(gamefunc_Strafe))
+    {
+        if (gInput.svel < keyMove && gInput.svel > -keyMove)
+        {
+            if (buttonMap.ButtonDown(gamefunc_Turn_Left))
+                input.svel += keyMove;
+
+            if (buttonMap.ButtonDown(gamefunc_Turn_Right))
+                input.svel -= keyMove;
+        }
+    }
+    else
+    {
+        if (buttonMap.ButtonDown(gamefunc_Turn_Left))
+        {
+            turnHeldTime += scaleAdjust * kTicsPerFrame;
+            input.q16avel -= FloatToFixed(scaleAdjust * (min(12. * turnHeldTime, gTurnSpeed) / 4.));
+        }
+        else if (buttonMap.ButtonDown(gamefunc_Turn_Right))
+        {
+            turnHeldTime += scaleAdjust * kTicsPerFrame;
+            input.q16avel += FloatToFixed(scaleAdjust * (min(12. * turnHeldTime, gTurnSpeed) / 4.));
+        }
+        else
+        {
+            turnHeldTime = 0;
+        }
+    }
+
+    if (run && turnHeldTime > 24.)
+        input.q16avel <<= 1;
+
+    if (abs(gInput.fvel) < keyMove)
     {
         if (buttonMap.ButtonDown(gamefunc_Move_Forward))
             input.fvel += keyMove;
@@ -73,91 +140,41 @@ static void GetInputInternal(ControlInfo* const hidInput)
             input.fvel -= keyMove;
     }
 
-    if (gInput.svel < keyMove && gInput.svel > -keyMove)
+    if (abs(gInput.svel) < keyMove)
     {
         if (buttonMap.ButtonDown(gamefunc_Strafe_Left))
             input.svel += keyMove;
+
         if (buttonMap.ButtonDown(gamefunc_Strafe_Right))
             input.svel -= keyMove;
     }
 
-
-    char turnLeft = 0, turnRight = 0;
-
-    if (buttonMap.ButtonDown(gamefunc_Strafe))
-    {
-        if (gInput.svel < keyMove && gInput.svel > -keyMove)
-        {
-            if (buttonMap.ButtonDown(gamefunc_Turn_Left))
-                input.svel += keyMove;
-            if (buttonMap.ButtonDown(gamefunc_Turn_Right))
-                input.svel -= keyMove;
-        }
-    }
-    else
-    {
-        if (buttonMap.ButtonDown(gamefunc_Turn_Left))
-            turnLeft = 1;
-        if (buttonMap.ButtonDown(gamefunc_Turn_Right))
-            turnRight = 1;
-    }
-
-    static int32_t turnHeldTime;
-    static int32_t lastInputClock;  // MED
-    int32_t const  elapsedTics = gFrameClock - lastInputClock;
-
-    lastInputClock = gFrameClock;
-
-    if (turnLeft || turnRight)
-        turnHeldTime += elapsedTics;
-    else
-        turnHeldTime = 0;
-
-    if (turnLeft)
-        input.q16avel -= FloatToFixed(scaleAdjust * (ClipHigh(12 * turnHeldTime, gTurnSpeed) >> 2));
-    if (turnRight)
-        input.q16avel += FloatToFixed(scaleAdjust * (ClipHigh(12 * turnHeldTime, gTurnSpeed) >> 2));
-
-    if (run && turnHeldTime > 24)
-        input.q16avel <<= 1;
-
-    if (buttonMap.ButtonDown(gamefunc_Strafe))
-    {
-        input.svel -= xs_CRoundToInt((hidInput->mousex * 32.) + (scaleAdjust * (hidInput->dyaw * keyMove)));
-    }
-    else
-    {
-        input.q16avel += FloatToFixed(hidInput->mousex + (scaleAdjust * (hidInput->dyaw)));
-    }
-
-    input.svel -= xs_CRoundToInt(scaleAdjust * (hidInput->dx * keyMove));
-    input.fvel -= xs_CRoundToInt(scaleAdjust * (hidInput->dz * keyMove));
-
-    if (mouseaim)
-        input.q16horz += FloatToFixed(hidInput->mousey);
-    else
-        input.fvel -= xs_CRoundToInt(hidInput->mousey * 64.);
-
-    if (!in_mouseflip)
-        input.q16horz = -input.q16horz;
-
-    input.q16horz -= FloatToFixed(scaleAdjust * hidInput->dpitch);
-
-    gInput.fvel = clamp(gInput.fvel + input.fvel, -2048, 2048);
-    gInput.svel = clamp(gInput.svel + input.svel, -2048, 2048);
-    gInput.q16avel += input.q16avel;
-    gInput.q16horz = clamp(gInput.q16horz + input.q16horz, IntToFixed(-127) >> 2, IntToFixed(127) >> 2);
-
     if (!cl_syncinput && gamestate == GS_LEVEL)
     {
-        applylook(&gPlayer[myconnectindex], input.q16avel, scaleAdjust);
-        sethorizon(&gPlayer[myconnectindex], input.q16horz, scaleAdjust);
+        PLAYER* pPlayer = &gPlayer[myconnectindex];
+        applylook(pPlayer, input.q16avel, scaleAdjust);
+        sethorizon(pPlayer, input.q16horz, scaleAdjust);
     }
+
+    gInput.fvel = clamp(gInput.fvel + input.fvel, -MAXFVEL, MAXFVEL);
+    gInput.svel = clamp(gInput.svel + input.svel, -MAXSVEL, MAXSVEL);
+    gInput.q16avel += input.q16avel;
+    gInput.q16horz = clamp(gInput.q16horz + input.q16horz, -IntToFixed(MAXHORIZVEL), IntToFixed(MAXHORIZVEL));
 }
 
 void GameInterface::GetInput(InputPacket* packet, ControlInfo* const hidInput)
 {
-    GetInputInternal(hidInput);
+    if (paused || M_Active())
+    {
+        gInput = {};
+        return;
+    }
+
+    bool mouseaim;
+
+    processInputBits(hidInput, &mouseaim);
+    processMovement(hidInput, mouseaim);
+
     if (packet)
     {
         *packet = gInput;
diff --git a/source/blood/src/globals.cpp b/source/blood/src/globals.cpp
index 282bfa52d..14eebbde3 100644
--- a/source/blood/src/globals.cpp
+++ b/source/blood/src/globals.cpp
@@ -38,7 +38,6 @@ int gFrameCount;
 static const char *_module;
 static int _line;
 
-int32_t gTurnSpeed = 92;
 int32_t gDetail = 4;
 bool gNoClip;
 bool gInfiniteAmmo;
diff --git a/source/blood/src/globals.h b/source/blood/src/globals.h
index 03bd9a744..c69935c1b 100644
--- a/source/blood/src/globals.h
+++ b/source/blood/src/globals.h
@@ -35,7 +35,6 @@ extern int gFrameCount;
 
 #define MAXPLAYERNAME 16
 
-extern int32_t gTurnSpeed;
 extern int32_t gDetail;
 extern bool gNoClip;
 extern bool gInfiniteAmmo;

From 1900cf1fcc08dd316c327b4edc7cf6faf2232958 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Wed, 16 Sep 2020 21:46:03 +1000
Subject: [PATCH 12/41] - Move Duke's `getincangle()` function to the backend,
 create Q16.16 variant (`getincangleq16()`) and replace Shadow Warrior's
 discrete implementations shared versions.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

* Need SW's input helpers available for Blood but therefore also need an angle delta function that does not seem to exist in Blood.
* Realise that gamecontrol.h/cpp might not be the most appropriate place, but it's a shared location and these will go into binaryangle.h when its utilisation can be more realised.
* Because SW's logic was reversed, in that param #1 was the new angle and param #2 was the current, all calls have been reversed.
* By happenstance, also fixes an issue with multiple 180° turns in quick succession.
---
 source/core/gamecontrol.cpp      | 41 +++++++++++++++++++++++
 source/core/gamecontrol.h        |  3 ++
 source/games/duke/src/actors.cpp | 25 --------------
 source/games/duke/src/funct.h    |  1 -
 source/sw/src/draw.cpp           |  2 +-
 source/sw/src/game.h             | 14 ++++----
 source/sw/src/input.cpp          |  2 +-
 source/sw/src/jsector.cpp        |  4 +--
 source/sw/src/player.cpp         | 57 +++++---------------------------
 source/sw/src/sprite.cpp         |  1 -
 source/sw/src/track.cpp          | 14 ++++----
 source/sw/src/weapon.cpp         |  8 ++---
 12 files changed, 74 insertions(+), 98 deletions(-)

diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp
index e2a242c37..36110d3b1 100644
--- a/source/core/gamecontrol.cpp
+++ b/source/core/gamecontrol.cpp
@@ -1446,3 +1446,44 @@ void LoadDefinitions()
 
 }
 
+//---------------------------------------------------------------------------
+//
+// code fron gameexec/conrun
+//
+//---------------------------------------------------------------------------
+
+int getincangle(int a, int na)
+{
+	a &= 2047;
+	na &= 2047;
+
+	if(abs(a-na) < 1024)
+		return (na-a);
+	else
+	{
+		if(na > 1024) na -= 2048;
+		if(a > 1024) a -= 2048;
+
+		na -= 2048;
+		a -= 2048;
+		return (na-a);
+	}
+}
+
+fixed_t getincangleq16(fixed_t a, fixed_t na)
+{
+	a &= 0x7FFFFFF;
+	na &= 0x7FFFFFF;
+
+	if(abs(a-na) < IntToFixed(1024))
+		return (na-a);
+	else
+	{
+		if(na > IntToFixed(1024)) na -= IntToFixed(2048);
+		if(a > IntToFixed(1024)) a -= IntToFixed(2048);
+
+		na -= IntToFixed(2048);
+		a -= IntToFixed(2048);
+		return (na-a);
+	}
+}
diff --git a/source/core/gamecontrol.h b/source/core/gamecontrol.h
index 244e077ce..4461c91b4 100644
--- a/source/core/gamecontrol.h
+++ b/source/core/gamecontrol.h
@@ -63,6 +63,9 @@ void DeferedStartGame(MapRecord* map, int skill);
 void ChangeLevel(MapRecord* map, int skill);
 void CompleteLevel(MapRecord* map);
 
+int getincangle(int c, int n);
+fixed_t getincangleq16(fixed_t c, fixed_t n);
+
 struct UserConfig
 {
 	FString gamegrp;
diff --git a/source/games/duke/src/actors.cpp b/source/games/duke/src/actors.cpp
index e8426b306..058943ea7 100644
--- a/source/games/duke/src/actors.cpp
+++ b/source/games/duke/src/actors.cpp
@@ -4667,31 +4667,6 @@ void handle_se130(int i, int countmax, int EXPLOSION2)
 	}
 }
 
-
-//---------------------------------------------------------------------------
-//
-// code fron gameexec/conrun
-//
-//---------------------------------------------------------------------------
-
-int getincangle(int a,int na)
-{
-	a &= 2047;
-	na &= 2047;
-
-	if(abs(a-na) < 1024)
-		return (na-a);
-	else
-	{
-		if(na > 1024) na -= 2048;
-		if(a > 1024) a -= 2048;
-
-		na -= 2048;
-		a -= 2048;
-		return (na-a);
-	}
-}
-
 //---------------------------------------------------------------------------
 //
 // 
diff --git a/source/games/duke/src/funct.h b/source/games/duke/src/funct.h
index 8a857584c..c440f890a 100644
--- a/source/games/duke/src/funct.h
+++ b/source/games/duke/src/funct.h
@@ -165,7 +165,6 @@ void execute(int s, int p, int d);
 void makeitfall(int s);
 int furthestangle(int snum, int angDiv);
 void getglobalz(int s);
-int getincangle(int c, int n);
 void OnEvent(int id, int pnum = -1, int snum = -1, int dist = -1);
 
 short EGS(short whatsect, int s_x, int s_y, int s_z, short s_pn, signed char s_s, signed char s_xr, signed char s_yr, short s_a, short s_ve, int s_zv, short s_ow, signed char s_ss);
diff --git a/source/sw/src/draw.cpp b/source/sw/src/draw.cpp
index 0b49ab388..6293460e3 100644
--- a/source/sw/src/draw.cpp
+++ b/source/sw/src/draw.cpp
@@ -1283,7 +1283,7 @@ void CameraView(PLAYERp pp, int *tx, int *ty, int *tz, short *tsectnum, fixed_t
             sp = &sprite[i];
 
             ang = getangle(*tx - sp->x, *ty - sp->y);
-            ang_test  = GetDeltaAngle(sp->ang, ang) < sp->lotag;
+            ang_test = getincangle(ang, sp->ang) < sp->lotag;
 
             FAFcansee_test =
                 (FAFcansee(sp->x, sp->y, sp->z, sp->sectnum, *tx, *ty, *tz, pp->cursectnum) ||
diff --git a/source/sw/src/game.h b/source/sw/src/game.h
index 4c1241e3c..85e3344a9 100644
--- a/source/sw/src/game.h
+++ b/source/sw/src/game.h
@@ -355,13 +355,13 @@ int StdRandomRange(int range);
 #define SQ(val) ((val) * (val))
 
 #define KENFACING_PLAYER(pp,sp) (sintable[NORM_ANGLE(sp->ang+512)]*(pp->posy-sp->y) >= sintable[NORM_ANGLE(sp-ang)]*(pp->posx-sp->x))
-#define FACING_PLAYER(pp,sp) (abs(GetDeltaAngle((sp)->ang, NORM_ANGLE(getangle((pp)->posx - (sp)->x, (pp)->posy - (sp)->y)))) < 512)
-#define PLAYER_FACING(pp,sp) (abs(GetDeltaAngle(FixedToInt((pp)->q16ang), NORM_ANGLE(getangle((sp)->x - (pp)->posx, (sp)->y - (pp)->posy)))) < 320)
-#define FACING(sp1,sp2) (abs(GetDeltaAngle((sp2)->ang, NORM_ANGLE(getangle((sp1)->x - (sp2)->x, (sp1)->y - (sp2)->y)))) < 512)
+#define FACING_PLAYER(pp,sp) (abs(getincangle(getangle((pp)->posx - (sp)->x, (pp)->posy - (sp)->y), (sp)->ang)) < 512)
+#define PLAYER_FACING(pp,sp) (abs(getincangle(getangle((sp)->x - (pp)->posx, (sp)->y - (pp)->posy), FixedToInt((pp)->q16ang))) < 320)
+#define FACING(sp1,sp2) (abs(getincangle(getangle((sp1)->x - (sp2)->x, (sp1)->y - (sp2)->y), (sp2)->ang)) < 512)
 
-#define FACING_PLAYER_RANGE(pp,sp,range) (abs(GetDeltaAngle((sp)->ang, NORM_ANGLE(getangle((pp)->posx - (sp)->x, (pp)->posy - (sp)->y)))) < (range))
-#define PLAYER_FACING_RANGE(pp,sp,range) (abs(GetDeltaAngle(FixedToInt((pp)->q16ang), NORM_ANGLE(getangle((sp)->x - (pp)->posx, (sp)->y - (pp)->posy)))) < (range))
-#define FACING_RANGE(sp1,sp2,range) (abs(GetDeltaAngle((sp2)->ang, NORM_ANGLE(getangle((sp1)->x - (sp2)->x, (sp1)->y - (sp2)->y)))) < (range))
+#define FACING_PLAYER_RANGE(pp,sp,range) (abs(getincangle(getangle((pp)->posx - (sp)->x, (pp)->posy - (sp)->y), (sp)->ang)) < (range))
+#define PLAYER_FACING_RANGE(pp,sp,range) (abs(getincangle(getangle((sp)->x - (pp)->posx, (sp)->y - (pp)->posy), FixedToInt((pp)->q16ang))) < (range))
+#define FACING_RANGE(sp1,sp2,range) (abs(getincangle(getangle((sp1)->x - (sp2)->x, (sp1)->y - (sp2)->y), (sp2)->ang)) < (range))
 
 // two vectors
 // can determin direction
@@ -1846,8 +1846,6 @@ extern SECTOR_OBJECT SectorObject[MAX_SECTOR_OBJECTS];
 ANIMATOR NullAnimator;
 
 int Distance(int x1, int y1, int x2, int y2);
-short GetDeltaAngle(short, short);
-fixed_t GetDeltaQ16Angle(fixed_t, fixed_t);
 
 int SetActorRotation(short SpriteNum,int,int);
 int NewStateGroup(short SpriteNum, STATEp SpriteGroup[]);
diff --git a/source/sw/src/input.cpp b/source/sw/src/input.cpp
index f4c6fb594..a1a70d553 100644
--- a/source/sw/src/input.cpp
+++ b/source/sw/src/input.cpp
@@ -314,7 +314,7 @@ static void processMovement(PLAYERp const pp, ControlInfo* const hidInput, bool
 
         if (pp->angTarget)
         {
-            fixed_t angDelta = GetDeltaQ16Angle(pp->angTarget, pp->q16ang);
+            fixed_t angDelta = getincangleq16(pp->q16ang, pp->angTarget);
             pp->q16ang = (pp->q16ang + xs_CRoundToInt(scaleAdjust * angDelta));
 
             if (abs(pp->q16ang - pp->angTarget) < FRACUNIT)
diff --git a/source/sw/src/jsector.cpp b/source/sw/src/jsector.cpp
index eb4dcf09d..5d123b4d6 100644
--- a/source/sw/src/jsector.cpp
+++ b/source/sw/src/jsector.cpp
@@ -653,7 +653,7 @@ void JS_DrawCameras(PLAYERp pp, int tx, int ty, int tz)
                             SP_TAG5(sp) = NORM_ANGLE((SP_TAG5(sp) + oscilation_delta));
 
                             // TAG6 = Turn radius
-                            if (klabs(GetDeltaAngle(SP_TAG5(sp), sp->ang)) >= SP_TAG6(sp))
+                            if (klabs(getincangle(sp->ang, SP_TAG5(sp))) >= SP_TAG6(sp))
                             {
                                 SP_TAG5(sp) = NORM_ANGLE((SP_TAG5(sp) - oscilation_delta));
                                 RESET_BOOL3(sp);    // Reverse turn
@@ -666,7 +666,7 @@ void JS_DrawCameras(PLAYERp pp, int tx, int ty, int tz)
                             SP_TAG5(sp) = NORM_ANGLE((SP_TAG5(sp) - oscilation_delta));
 
                             // TAG6 = Turn radius
-                            if (klabs(GetDeltaAngle(SP_TAG5(sp), sp->ang)) >= SP_TAG6(sp))
+                            if (klabs(getincangle(sp->ang, SP_TAG5(sp))) >= SP_TAG6(sp))
                             {
                                 SP_TAG5(sp) = NORM_ANGLE((SP_TAG5(sp) + oscilation_delta));
                                 SET_BOOL3(sp);      // Reverse turn
diff --git a/source/sw/src/player.cpp b/source/sw/src/player.cpp
index 52e429a0d..087669af6 100644
--- a/source/sw/src/player.cpp
+++ b/source/sw/src/player.cpp
@@ -1124,45 +1124,6 @@ void pSetVisNorm(PANEL_SPRITEp psp)
 //    SetVisNorm();
 }
 
-short
-GetDeltaAngle(short ang1, short ang2)
-{
-    // Look at the smaller angle if > 1024 (180 degrees)
-    if (labs(ang1 - ang2) > 1024)
-    {
-        if (ang1 <= 1024)
-            ang1 += 2048;
-
-        if (ang2 <= 1024)
-            ang2 += 2048;
-    }
-
-    //if (ang1 - ang2 == -1024)
-    //    return(1024);
-
-    return ang1 - ang2;
-
-}
-
-fixed_t
-GetDeltaQ16Angle(fixed_t ang1, fixed_t ang2)
-{
-    // Look at the smaller angle if > 1024 (180 degrees)
-    if (abs(ang1 - ang2) > IntToFixed(1024))
-    {
-        if (ang1 <= IntToFixed(1024))
-            ang1 += IntToFixed(2048);
-
-        if (ang2 <= IntToFixed(1024))
-            ang2 += IntToFixed(2048);
-    }
-
-    //if (ang1 - ang2 == -IntToFixed(1024))
-    //    return(IntToFixed(1024));
-
-    return ang1 - ang2;
-}
-
 TARGET_SORT TargetSort[MAX_TARGET_SORT];
 unsigned TargetSortCount;
 
@@ -1240,7 +1201,7 @@ DoPickTarget(SPRITEp sp, uint32_t max_delta_ang, int skip_targets)
             // Get the angle difference
             // delta_ang = labs(FixedToInt(pp->q16ang) - angle2);
 
-            delta_ang = labs(GetDeltaAngle(sp->ang, angle2));
+            delta_ang = labs(getincangle(angle2, sp->ang));
 
             // If delta_ang not in the range skip this one
             if (delta_ang > (int)max_delta_ang)
@@ -1561,7 +1522,7 @@ DoPlayerTurn(PLAYERp pp, fixed_t const q16avel, double const scaleAdjust)
 
                 // make the first turn in the clockwise direction
                 // the rest will follow
-                delta_ang = labs(GetDeltaQ16Angle(pp->turn180_target, pp->q16ang)) >> TURN_SHIFT;
+                delta_ang = labs(getincangleq16(pp->q16ang, pp->turn180_target)) >> TURN_SHIFT;
                 pp->q16ang = (pp->q16ang + xs_CRoundToInt(scaleAdjust * delta_ang)) & 0x7FFFFFF;
 
                 SET(pp->Flags, PF_TURN_180);
@@ -1577,7 +1538,7 @@ DoPlayerTurn(PLAYERp pp, fixed_t const q16avel, double const scaleAdjust)
     {
         fixed_t delta_ang;
 
-        delta_ang = GetDeltaQ16Angle(pp->turn180_target, pp->q16ang) >> TURN_SHIFT;
+        delta_ang = getincangleq16(pp->q16ang, pp->turn180_target) >> TURN_SHIFT;
         pp->q16ang = (pp->q16ang + xs_CRoundToInt(scaleAdjust * delta_ang)) & 0x7FFFFFF;
 
         sprite[pp->PlayerSprite].ang = FixedToInt(pp->q16ang);
@@ -1588,7 +1549,7 @@ DoPlayerTurn(PLAYERp pp, fixed_t const q16avel, double const scaleAdjust)
         }
 
         // get new delta to see how close we are
-        delta_ang = GetDeltaQ16Angle(pp->turn180_target, pp->q16ang);
+        delta_ang = getincangleq16(pp->q16ang, pp->turn180_target);
 
         if (labs(delta_ang) < (IntToFixed(3) << TURN_SHIFT))
         {
@@ -1723,7 +1684,7 @@ DoPlayerTurnTurret(PLAYERp pp, fixed_t q16avel)
 
         if (sop->limit_ang_center >= 0)
         {
-            diff = GetDeltaQ16Angle(new_ang, IntToFixed(sop->limit_ang_center));
+            diff = getincangleq16(IntToFixed(sop->limit_ang_center), new_ang);
 
             if (labs(diff) >= IntToFixed(sop->limit_ang_delta))
             {
@@ -6393,7 +6354,7 @@ void DoPlayerDeathFollowKiller(PLAYERp pp)
 
         if (FAFcansee(kp->x, kp->y, SPRITEp_TOS(kp), kp->sectnum, pp->posx, pp->posy, pp->posz, pp->cursectnum))
         {
-            playerAddAngle(pp, GetDeltaQ16Angle(gethiq16angle(kp->x - pp->posx, kp->y - pp->posy), pp->q16ang) / (double)(FRACUNIT << 4));
+            playerAddAngle(pp, getincangleq16(pp->q16ang, gethiq16angle(kp->x - pp->posx, kp->y - pp->posy)) / (double)(FRACUNIT << 4));
         }
     }
 }
@@ -6585,7 +6546,7 @@ void DoPlayerDeathMoveHead(PLAYERp pp)
 
 
             wall_ang = NORM_ANGLE(hsp->ang);
-            dang = GetDeltaAngle(u->slide_ang, wall_ang);
+            dang = getincangle(wall_ang, u->slide_ang);
             u->slide_ang = NORM_ANGLE(wall_ang + 1024 - dang);
 
             SpawnShrap(pp->PlayerSprite, -1);
@@ -6602,7 +6563,7 @@ void DoPlayerDeathMoveHead(PLAYERp pp)
             nw = wall[w].point2;
             wall_ang = NORM_ANGLE(getangle(wall[nw].x - wall[w].x, wall[nw].y - wall[w].y)-512);
 
-            dang = GetDeltaAngle(u->slide_ang, wall_ang);
+            dang = getincangle(wall_ang, u->slide_ang);
             u->slide_ang = NORM_ANGLE(wall_ang + 1024 - dang);
 
             SpawnShrap(pp->PlayerSprite, -1);
@@ -7817,7 +7778,7 @@ void playerSetAngle(PLAYERp pp, double ang)
             ang += 0.1;
         }
 
-        pp->angTarget = pp->q16ang + GetDeltaQ16Angle(FloatToFixed(ang), pp->q16ang);
+        pp->angTarget = pp->q16ang + getincangleq16(pp->q16ang, FloatToFixed(ang));
     }
     else
     {
diff --git a/source/sw/src/sprite.cpp b/source/sw/src/sprite.cpp
index cf3dd6a8e..1f7795f64 100644
--- a/source/sw/src/sprite.cpp
+++ b/source/sw/src/sprite.cpp
@@ -108,7 +108,6 @@ int MissileZrange(short SpriteNum);
 #define ACTIVE_CHECK_TIME (3*120)
 
 /*
-short GetDeltaAngle(short ang1, short ang2);
 short GetRotation(short sn);
 int StateControl(int16_t SpriteNum);
 void PreCacheRange(short, short);
diff --git a/source/sw/src/track.cpp b/source/sw/src/track.cpp
index 76e0fa8eb..41e2234c2 100644
--- a/source/sw/src/track.cpp
+++ b/source/sw/src/track.cpp
@@ -931,7 +931,7 @@ SectorObjectSetupBounds(SECTOR_OBJECTp sop)
                         sop->clipbox_vdist[sop->clipbox_num] = ksqrt(SQ(sop->xmid - sp->x) + SQ(sop->ymid - sp->y));
 
                         ang2 = getangle(sp->x - sop->xmid, sp->y - sop->ymid);
-                        sop->clipbox_ang[sop->clipbox_num] = GetDeltaAngle(sop->ang, ang2);
+                        sop->clipbox_ang[sop->clipbox_num] = getincangle(ang2, sop->ang);
 
                         sop->clipbox_num++;
                         KillSprite(sp_num);
@@ -1678,7 +1678,7 @@ MovePlayer(PLAYERp pp, SECTOR_OBJECTp sop, int nx, int ny)
 
     // New angle is formed by taking last known angle and
     // adjusting by the delta angle
-    playerAddAngle(pp, GetDeltaQ16Angle(pp->RevolveQ16Ang + IntToFixed(pp->RevolveDeltaAng), pp->q16ang));
+    playerAddAngle(pp, getincangleq16(pp->q16ang, pp->RevolveQ16Ang + IntToFixed(pp->RevolveDeltaAng)));
 
     UpdatePlayerSprite(pp);
 }
@@ -1891,7 +1891,7 @@ PlayerPart:
                 setspritez(sop->sp_num[i], (vec3_t *)sp);
         }
 
-        u->oangdiff += GetDeltaAngle(sp->ang, oldang);
+        u->oangdiff += getincangle(oldang, sp->ang);
 
         if (TEST(sp->extra, SPRX_BLADE))
         {
@@ -2384,7 +2384,7 @@ MoveSectorObjects(SECTOR_OBJECTp sop, short locktics)
         DoTrack(sop, locktics, &nx, &ny);
 
     // get delta to target angle
-    delta_ang = GetDeltaAngle(sop->ang_tgt, sop->ang);
+    delta_ang = getincangle(sop->ang, sop->ang_tgt);
 
     sop->ang = NORM_ANGLE(sop->ang + (delta_ang >> sop->turn_speed));
     delta_ang = delta_ang >> sop->turn_speed;
@@ -2896,7 +2896,7 @@ void TornadoSpin(SECTOR_OBJECTp sop)
     short locktics = synctics;
 
     // get delta to target angle
-    delta_ang = GetDeltaAngle(sop->ang_tgt, sop->ang);
+    delta_ang = getincangle(sop->ang, sop->ang_tgt);
 
     sop->ang = NORM_ANGLE(sop->ang + (delta_ang >> sop->turn_speed));
     delta_ang = delta_ang >> sop->turn_speed;
@@ -3026,7 +3026,7 @@ DoAutoTurretObject(SECTOR_OBJECTp sop)
         sop->ang_tgt = getangle(u->tgt_sp->x - sop->xmid,  u->tgt_sp->y - sop->ymid);
 
         // get delta to target angle
-        delta_ang = GetDeltaAngle(sop->ang_tgt, sop->ang);
+        delta_ang = getincangle(sop->ang, sop->ang_tgt);
 
         //sop->ang += delta_ang >> 4;
         sop->ang = NORM_ANGLE(sop->ang + (delta_ang >> 3));
@@ -3034,7 +3034,7 @@ DoAutoTurretObject(SECTOR_OBJECTp sop)
 
         if (sop->limit_ang_center >= 0)
         {
-            diff = GetDeltaAngle(sop->ang, sop->limit_ang_center);
+            diff = getincangle(sop->limit_ang_center, sop->ang);
 
             if (labs(diff) >= sop->limit_ang_delta)
             {
diff --git a/source/sw/src/weapon.cpp b/source/sw/src/weapon.cpp
index 85378c970..cfdd48e11 100644
--- a/source/sw/src/weapon.cpp
+++ b/source/sw/src/weapon.cpp
@@ -8310,9 +8310,9 @@ MissileSeek(int16_t Weapon, int16_t delay_tics, int16_t aware_range/*, int16_t d
         hp = &sprite[User[Weapon]->WpnGoal];
 
         // move to correct angle
-        ang2tgt = getangle(hp->x - sp->x, hp->y - sp->y);
+        ang2tgt = getangle(hp->y - sp->y, hp->x - sp->x);
 
-        delta_ang = GetDeltaAngle(sp->ang, ang2tgt);
+        delta_ang = getincangle(sp->ang, ang2tgt);
 
         if (labs(delta_ang) > 32)
         {
@@ -8386,7 +8386,7 @@ ComboMissileSeek(int16_t Weapon, int16_t delay_tics, int16_t aware_range/*, int1
         // move to correct angle
         ang2tgt = getangle(hp->x - sp->x, hp->y - sp->y);
 
-        delta_ang = GetDeltaAngle(sp->ang, ang2tgt);
+        delta_ang = getincangle(ang2tgt, sp->ang);
 
         if (labs(delta_ang) > 32)
         {
@@ -18951,7 +18951,7 @@ InitTurretMgun(SECTOR_OBJECTp sop)
                 daang = 512;
                 if ((hitinfo.sprite = WeaponAutoAimHitscan(sp, &daz, &daang, false)) != -1)
                 {
-                    delta = labs(GetDeltaAngle(daang, sp->ang));
+                    delta = labs(getincangle(sp->ang, daang));
                     if (delta > 128)
                     {
                         // don't shoot if greater than 128

From 85e81c94c7dbc2f5378e74924d71f95abaaf5233 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Wed, 16 Sep 2020 21:59:19 +1000
Subject: [PATCH 13/41] - Blood: Bring in SW's input helpers and hook up within
 input code.

---
 source/blood/src/controls.cpp | 34 +++++++++++++
 source/blood/src/player.cpp   | 91 +++++++++++++++++++++++++++++++++++
 source/blood/src/player.h     |  8 +++
 3 files changed, 133 insertions(+)

diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index b76bb37ee..669263c56 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -152,8 +152,42 @@ static void processMovement(ControlInfo* const hidInput, bool const mouseaim)
     if (!cl_syncinput && gamestate == GS_LEVEL)
     {
         PLAYER* pPlayer = &gPlayer[myconnectindex];
+
         applylook(pPlayer, input.q16avel, scaleAdjust);
+
+        if (pPlayer->angTarget)
+        {
+            fixed_t angDelta = getincangleq16(pPlayer->q16ang, pPlayer->angTarget);
+            pPlayer->q16ang = (pPlayer->q16ang + xs_CRoundToInt(scaleAdjust * angDelta));
+
+            if (abs(pPlayer->q16ang - pPlayer->angTarget) < FRACUNIT)
+            {
+                pPlayer->q16ang = pPlayer->angTarget;
+                pPlayer->angTarget = 0;
+            }
+        }
+        else if (pPlayer->angAdjust)
+        {
+            pPlayer->q16ang = (pPlayer->q16ang + FloatToFixed(scaleAdjust * pPlayer->angAdjust)) & 0x7FFFFFF;
+        }
+
         sethorizon(pPlayer, input.q16horz, scaleAdjust);
+
+        if (pPlayer->horizTarget)
+        {
+            fixed_t horizDelta = pPlayer->horizTarget - pPlayer->q16horiz;
+            pPlayer->q16horiz += xs_CRoundToInt(scaleAdjust * horizDelta);
+
+            if (abs(pPlayer->q16horiz - pPlayer->horizTarget) < FRACUNIT)
+            {
+                pPlayer->q16horiz = pPlayer->horizTarget;
+                pPlayer->horizTarget = 0;
+            }
+        }
+        else if (pPlayer->horizAdjust)
+        {
+            pPlayer->q16horiz += FloatToFixed(scaleAdjust * pPlayer->horizAdjust);
+        }
     }
 
     gInput.fvel = clamp(gInput.fvel + input.fvel, -MAXFVEL, MAXFVEL);
diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index a2982cfc1..7885e1184 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1304,6 +1304,12 @@ int ActionScan(PLAYER *pPlayer, int *a2, int *a3)
     return -1;
 }
 
+//---------------------------------------------------------------------------
+//
+// Player's angle function, called in processInput() or from gi->GetInput() as required.
+//
+//---------------------------------------------------------------------------
+
 void applylook(PLAYER *pPlayer, fixed_t const q16avel, double const scaleAdjust)
 {
     spritetype *pSprite = pPlayer->pSprite;
@@ -1339,6 +1345,12 @@ void applylook(PLAYER *pPlayer, fixed_t const q16avel, double const scaleAdjust)
     pPlayer->angold = pSprite->ang = FixedToInt(pPlayer->q16ang);
 }
 
+//---------------------------------------------------------------------------
+//
+// Player's horizon function, called in processInput() or from gi->GetInput() as required.
+//
+//---------------------------------------------------------------------------
+
 void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust)
 {
     InputPacket *pInput = &pPlayer->input;
@@ -1379,6 +1391,85 @@ void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust
     }
 }
 
+//---------------------------------------------------------------------------
+//
+// Unsynchronised input helpers.
+//
+//---------------------------------------------------------------------------
+
+void resetinputhelpers(PLAYER* pPlayer)
+{
+    pPlayer->horizAdjust = 0;
+    pPlayer->angAdjust = 0;
+    pPlayer->pitchAdjust = 0;
+}
+
+void playerAddAngle(PLAYER* pPlayer, double ang)
+{
+    if (!cl_syncinput)
+    {
+        pPlayer->angAdjust += ang;
+    }
+    else
+    {
+        pPlayer->addang(ang);
+    }
+}
+
+void playerSetAngle(PLAYER* pPlayer, double ang)
+{
+    if (!cl_syncinput)
+    {
+        // Cancel out any angle adjustments as we're setting angle now.
+        pPlayer->angAdjust = 0;
+
+        // Add slight offset if input angle is coming in as absolute 0.
+        if (ang == 0)
+        {
+            ang += 0.1;
+        }
+
+        pPlayer->angTarget = pPlayer->q16ang + getincangleq16(pPlayer->q16ang, FloatToFixed(ang));
+    }
+    else
+    {
+        pPlayer->setang(ang);
+    }
+}
+
+void playerAddHoriz(PLAYER* pPlayer, double horiz)
+{
+    if (!cl_syncinput)
+    {
+        pPlayer->horizAdjust += horiz;
+    }
+    else
+    {
+        pPlayer->addhoriz(horiz);
+    }
+}
+
+void playerSetHoriz(PLAYER* pPlayer, double horiz)
+{
+    if (!cl_syncinput)
+    {
+        // Cancel out any horizon adjustments as we're setting horizon now.
+        pPlayer->horizAdjust = 0;
+
+        // Add slight offset if input horizon is coming in as absolute 0.
+        if (horiz == 0)
+        {
+            horiz += 0.1;
+        }
+
+        pPlayer->horizTarget = FloatToFixed(horiz);
+    }
+    else
+    {
+        pPlayer->sethoriz(horiz);
+    }
+}
+
 void ProcessInput(PLAYER *pPlayer)
 {
     enum
diff --git a/source/blood/src/player.h b/source/blood/src/player.h
index 0a4cdd947..0b25ec5d4 100644
--- a/source/blood/src/player.h
+++ b/source/blood/src/player.h
@@ -185,6 +185,14 @@ struct PLAYER
     int                 player_par;
     int                 nWaterPal;
     POSTURE             pPosture[kModeMax][kPostureMax];
+
+    // Input helper variables and setters.
+    double horizAdjust, angAdjust, pitchAdjust;
+    fixed_t horizTarget, angTarget;
+    void addang(int v) { q16ang = (q16ang + IntToFixed(v)) & 0x7FFFFFF; }
+    void setang(int v) { q16ang = IntToFixed(v); }
+    void addhoriz(int v) { q16horiz += (IntToFixed(v)); }
+    void sethoriz(int v) { q16horiz = IntToFixed(v); }    
 };
 
 struct PROFILE

From 6e4ee9e97560c704a662baba27517e736d76092a Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Wed, 16 Sep 2020 22:02:05 +1000
Subject: [PATCH 14/41] - Blood: Forbid unsynchronised input if dead.

---
 source/blood/src/controls.cpp | 11 ++++++++---
 1 file changed, 8 insertions(+), 3 deletions(-)

diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index 669263c56..bf552c499 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -153,8 +153,14 @@ static void processMovement(ControlInfo* const hidInput, bool const mouseaim)
     {
         PLAYER* pPlayer = &gPlayer[myconnectindex];
 
-        applylook(pPlayer, input.q16avel, scaleAdjust);
+        // Perform unsynchronised angle/horizon if not dead.
+        if (gView->pXSprite->health != 0)
+        {
+            applylook(pPlayer, input.q16avel, scaleAdjust);
+            sethorizon(pPlayer, input.q16horz, scaleAdjust);
+        }
 
+        // Process angle amendments from the game's ticker.
         if (pPlayer->angTarget)
         {
             fixed_t angDelta = getincangleq16(pPlayer->q16ang, pPlayer->angTarget);
@@ -171,8 +177,7 @@ static void processMovement(ControlInfo* const hidInput, bool const mouseaim)
             pPlayer->q16ang = (pPlayer->q16ang + FloatToFixed(scaleAdjust * pPlayer->angAdjust)) & 0x7FFFFFF;
         }
 
-        sethorizon(pPlayer, input.q16horz, scaleAdjust);
-
+        // Process horizon amendments from the game's ticker.
         if (pPlayer->horizTarget)
         {
             fixed_t horizDelta = pPlayer->horizTarget - pPlayer->q16horiz;

From 40bbb612396b0d5820759254d9f745661ea2b885 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Wed, 16 Sep 2020 22:18:22 +1000
Subject: [PATCH 15/41] - Blood: Get death chase-cam going while unsynchronised
 using input helpers.

---
 source/blood/src/player.cpp | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index 7885e1184..118264e55 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1480,6 +1480,8 @@ void ProcessInput(PLAYER *pPlayer)
         Item_JumpBoots = 3
     };
 
+    resetinputhelpers(pPlayer);
+
     spritetype *pSprite = pPlayer->pSprite;
     XSPRITE *pXSprite = pPlayer->pXSprite;
     int nSprite = pPlayer->nSprite;
@@ -1498,11 +1500,11 @@ void ProcessInput(PLAYER *pPlayer)
         if (pPlayer->fraggerId != -1)
         {
             pPlayer->angold = pSprite->ang = getangle(sprite[pPlayer->fraggerId].x - pSprite->x, sprite[pPlayer->fraggerId].y - pSprite->y);
-            pPlayer->q16ang = IntToFixed(pSprite->ang);
+            playerSetAngle(pPlayer, pSprite->ang);
         }
         pPlayer->deathTime += 4;
         if (!bSeqStat)
-            pPlayer->q16horiz = mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime<<3, 1024))>>15), IntToFixed(120));
+            playerSetHoriz(pPlayer, FixedToFloat(mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime<<3, 1024))>>15), IntToFixed(120))));
         if (pPlayer->curWeapon)
             pInput->setNewWeapon(pPlayer->curWeapon);
         if (pInput->actions & SB_OPEN)

From 6a091deb6706d21b40bc14e1b87c2a1548012956 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Thu, 17 Sep 2020 07:25:19 +1000
Subject: [PATCH 16/41] - Blood: Re-apply input scaling removed in
 495c522096e0c9b31bdf6952e612bc3d03030da2 until the entire return to centre
 function is standardised across games.

---
 source/blood/src/player.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index 118264e55..d75eb4b1e 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1375,7 +1375,7 @@ void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust
             pPlayer->q16look = max(pPlayer->q16look - FloatToFixed(scaleAdjust * 4.), IntToFixed(-60));
     }
 
-    pPlayer->q16look = clamp(pPlayer->q16look, IntToFixed(-60), IntToFixed(60));
+    pPlayer->q16look = clamp(pPlayer->q16look + xs_CRoundToInt(q16horz / 3.25), IntToFixed(-60), IntToFixed(60));
 
     if (pPlayer->q16look > 0)
     {
@@ -1387,7 +1387,7 @@ void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust
     }
     else
     {
-        pPlayer->q16horiz = clamp(pPlayer->q16horiz + q16horz, IntToFixed(-179), IntToFixed(119));
+        pPlayer->q16horiz = 0;
     }
 }
 

From f2b48fe79ce7286e0bc4b1121c689d3820530937 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Thu, 17 Sep 2020 15:11:21 +1000
Subject: [PATCH 17/41] - SW: Uplift `DoPlayerHorizon()` with changes to serve
 as basis for standardised backend player horizon function.

* Pre-requisite to make it possible to stop using Q16.16 for horizon input.
* Perform look up/down and aim up/down using true pitch without the need for a helper.
* Adapt SW's return to center function to work based on true pitch (Duke's is a bit too fast for my liking).
* Duke's aim up/down and look up/down is 6 & 12 respectively, SW's is 8 & 16 respectively. Let's go half-way at 7 & 14.
---
 source/sw/src/player.cpp | 28 +++++++++++++---------------
 1 file changed, 13 insertions(+), 15 deletions(-)

diff --git a/source/sw/src/player.cpp b/source/sw/src/player.cpp
index 087669af6..18fd9776a 100644
--- a/source/sw/src/player.cpp
+++ b/source/sw/src/player.cpp
@@ -123,7 +123,7 @@ extern bool DebugOperate;
 enum
 {
     TURN_SHIFT = 2,
-    HORIZ_SPEED = 16
+    HORIZ_SPEED = 14
 };
 
 //unsigned char synctics, lastsynctics;
@@ -1785,9 +1785,12 @@ DoPlayerHorizon(PLAYERp pp, fixed_t const q16horz, double const scaleAdjust)
     if (cl_slopetilting)
         PlayerAutoLook(pp, scaleAdjust);
 
+    // Calculate adjustment as true pitch (Fixed point math really sucks...)
+    double horizAngle = clamp(atan2(pp->q16horizbase - IntToFixed(100), IntToFixed(128)) * (512. / pi::pi()), -180, 180);
+
     if (q16horz)
     {
-        pp->q16horizbase += q16horz;
+        horizAngle += FixedToFloat(q16horz);
         SET(pp->Flags, PF_LOCK_HORIZ | PF_LOOKING);
     }
 
@@ -1799,11 +1802,11 @@ DoPlayerHorizon(PLAYERp pp, fixed_t const q16horz, double const scaleAdjust)
 
         // adjust q16horiz negative
         if (pp->input.actions & SB_AIM_DOWN)
-            pp->q16horizbase -= FloatToFixed(scaleAdjust * (HORIZ_SPEED >> 1));
+            horizAngle -= scaleAdjust * (HORIZ_SPEED >> 1);
 
         // adjust q16horiz positive
         if (pp->input.actions & SB_AIM_UP)
-            pp->q16horizbase += FloatToFixed(scaleAdjust * (HORIZ_SPEED >> 1));
+            horizAngle += scaleAdjust * (HORIZ_SPEED >> 1);
     }
 
     // this is the unlocked type
@@ -1814,11 +1817,11 @@ DoPlayerHorizon(PLAYERp pp, fixed_t const q16horz, double const scaleAdjust)
 
         // adjust q16horiz negative
         if (pp->input.actions & SB_LOOK_DOWN)
-            pp->q16horizbase -= FloatToFixed(scaleAdjust * HORIZ_SPEED);
+            horizAngle -= scaleAdjust * HORIZ_SPEED;
 
         // adjust q16horiz positive
         if (pp->input.actions & SB_LOOK_UP)
-            pp->q16horizbase += FloatToFixed(scaleAdjust * HORIZ_SPEED);
+            horizAngle += scaleAdjust * HORIZ_SPEED;
 
         if (pp->input.actions & SB_CENTERVIEW)
             pp->q16horizoff = 0;
@@ -1829,14 +1832,10 @@ DoPlayerHorizon(PLAYERp pp, fixed_t const q16horz, double const scaleAdjust)
         if (!(pp->input.actions & (SB_LOOK_UP|SB_LOOK_DOWN)))
         {
             // not pressing the q16horiz keys
-            if (pp->q16horizbase != IntToFixed(100))
+            if (horizAngle != 0)
             {
                 // move q16horiz back to 100
-                for (int i = 1; i; i--)
-                {
-                    // this formula does not work for q16horiz = 101-103
-                    pp->q16horizbase += xs_CRoundToInt(scaleAdjust * (IntToFixed(25) - (pp->q16horizbase >> 2)));
-                }
+                horizAngle += scaleAdjust * ((1. / 65536.) - (horizAngle * 0.25));
             }
             else
             {
@@ -1846,9 +1845,8 @@ DoPlayerHorizon(PLAYERp pp, fixed_t const q16horz, double const scaleAdjust)
         }
     }
 
-    // bound the base
-    pp->q16horizbase = max(pp->q16horizbase, IntToFixed(PLAYER_HORIZ_MIN));
-    pp->q16horizbase = min(pp->q16horizbase, IntToFixed(PLAYER_HORIZ_MAX));
+    // Convert back to Build's horizon and clamp.
+    pp->q16horizbase = clamp(IntToFixed(100) + xs_CRoundToInt(IntToFixed(128) * tan(horizAngle * (pi::pi() / 512.))), IntToFixed(PLAYER_HORIZ_MIN), IntToFixed(PLAYER_HORIZ_MAX));
 
     // bound adjust q16horizoff
     if (pp->q16horizbase + pp->q16horizoff < IntToFixed(PLAYER_HORIZ_MIN))

From d2501007f27dc0ed79bbd5195bf66d1900dd610a Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Thu, 17 Sep 2020 16:41:09 +1000
Subject: [PATCH 18/41] - Blood: Adapt uplifted `DoPlayerHorizon()` from SW to
 Blood.

* Horizon now standardised on 100 like the other games.
* Need to determine where/why/how the player's horizon is starting out at 0 and get it to init at 100 like the other games.
---
 source/blood/src/controls.cpp | 27 ++---------
 source/blood/src/player.cpp   | 84 ++++++++++++++++++++++++-----------
 source/blood/src/view.cpp     | 12 +++--
 3 files changed, 66 insertions(+), 57 deletions(-)

diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index bf552c499..a441c8d19 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -44,30 +44,13 @@ enum
 void applylook(PLAYER *pPlayer, fixed_t const q16avel, double const scaleAdjust);
 void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust);
 
-//---------------------------------------------------------------------------
-//
-// handles the input bits
-//
-//---------------------------------------------------------------------------
-
-static void processInputBits(ControlInfo* const hidInput, bool* mouseaim)
-{
-    ApplyGlobalInput(gInput, hidInput);
-    *mouseaim = !(gInput.actions & SB_AIMMODE);
-
-    if (!mouseaim || (gInput.actions & (SB_LOOK_UP|SB_LOOK_DOWN)))
-    {
-        gInput.actions |= SB_CENTERVIEW;
-    }
-}
-
 //---------------------------------------------------------------------------
 //
 // handles movement
 //
 //---------------------------------------------------------------------------
 
-static void processMovement(ControlInfo* const hidInput, bool const mouseaim)
+static void processMovement(ControlInfo* const hidInput)
 {
     double const scaleAdjust = InputScale();
     int const run = !!(gInput.actions & SB_RUN);
@@ -83,7 +66,7 @@ static void processMovement(ControlInfo* const hidInput, bool const mouseaim)
         input.q16avel += FloatToFixed(hidInput->mousex + (scaleAdjust * hidInput->dyaw));
     }
 
-    if (mouseaim)
+    if (!(gInput.actions & SB_AIMMODE))
     {
         input.q16horz += FloatToFixed(hidInput->mousey);
     }
@@ -209,10 +192,8 @@ void GameInterface::GetInput(InputPacket* packet, ControlInfo* const hidInput)
         return;
     }
 
-    bool mouseaim;
-
-    processInputBits(hidInput, &mouseaim);
-    processMovement(hidInput, mouseaim);
+    ApplyGlobalInput(gInput, hidInput);
+    processMovement(hidInput);
 
     if (packet)
     {
diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index d75eb4b1e..3f6878e9c 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -721,9 +721,9 @@ void playerStart(int nPlayer, int bNewLevel)
     pPlayer->pXSprite->health = pDudeInfo->startHealth<<4;
     pPlayer->pSprite->cstat &= (unsigned short)~32768;
     pPlayer->bloodlust = 0;
-    pPlayer->q16horiz = 0;
+    pPlayer->q16horiz = 100;
     pPlayer->q16slopehoriz = 0;
-    pPlayer->q16look = 0;
+    pPlayer->q16look = 100;
     pPlayer->slope = 0;
     pPlayer->fraggerId = -1;
     pPlayer->underwaterTime = 1200;
@@ -1304,6 +1304,13 @@ int ActionScan(PLAYER *pPlayer, int *a2, int *a3)
     return -1;
 }
 
+enum
+{
+    HORIZ_SPEED = 14,
+    PLAYER_HORIZ_MIN = -79,
+    PLAYER_HORIZ_MAX = 219
+};
+
 //---------------------------------------------------------------------------
 //
 // Player's angle function, called in processInput() or from gi->GetInput() as required.
@@ -1355,40 +1362,63 @@ void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust
 {
     InputPacket *pInput = &pPlayer->input;
 
-    if ((pInput->actions & SB_CENTERVIEW) && !(pInput->actions & (SB_LOOK_UP | SB_LOOK_DOWN)))
+    // Calculate adjustment as true pitch (Fixed point math really sucks...)
+    double horizAngle = clamp(atan2(pPlayer->q16look - IntToFixed(100), IntToFixed(128)) * (512. / pi::pi()), -180, 180);
+
+    if (q16horz)
     {
-        if (pPlayer->q16look < 0)
-            pPlayer->q16look = min(pPlayer->q16look + FloatToFixed(scaleAdjust * 4.), 0);
-
-        if (pPlayer->q16look > 0)
-            pPlayer->q16look = max(pPlayer->q16look - FloatToFixed(scaleAdjust * 4.), 0);
-
-        if (!pPlayer->q16look)
-            pInput->actions &= ~SB_CENTERVIEW;
-    }
-    else
-    {
-        if (pInput->actions & (SB_LOOK_UP|SB_AIM_UP))
-            pPlayer->q16look = min(pPlayer->q16look + FloatToFixed(scaleAdjust * 4.), IntToFixed(60));
-
-        if (pInput->actions & (SB_LOOK_DOWN|SB_AIM_DOWN))
-            pPlayer->q16look = max(pPlayer->q16look - FloatToFixed(scaleAdjust * 4.), IntToFixed(-60));
+        pInput->actions &= ~SB_CENTERVIEW;
+        horizAngle += FixedToFloat(q16horz);
     }
 
-    pPlayer->q16look = clamp(pPlayer->q16look + xs_CRoundToInt(q16horz / 3.25), IntToFixed(-60), IntToFixed(60));
+    // this is the locked type
+    if (pInput->actions & (SB_AIM_UP|SB_AIM_DOWN))
+    {
+        pInput->actions &= ~SB_CENTERVIEW;
 
-    if (pPlayer->q16look > 0)
-    {
-        pPlayer->q16horiz = FloatToFixed(fmulscale30(120., Sinf(FixedToFloat(pPlayer->q16look) * 8.)));
+        // adjust q16horiz negative
+        if (pInput->actions & SB_AIM_DOWN)
+            horizAngle -= scaleAdjust * (HORIZ_SPEED >> 1);
+
+        // adjust q16horiz positive
+        if (pInput->actions & SB_AIM_UP)
+            horizAngle += scaleAdjust * (HORIZ_SPEED >> 1);
     }
-    else if (pPlayer->q16look < 0)
+
+    // this is the unlocked type
+    if (pInput->actions & (SB_LOOK_UP|SB_LOOK_DOWN))
     {
-        pPlayer->q16horiz = FloatToFixed(fmulscale30(180., Sinf(FixedToFloat(pPlayer->q16look) * 8.)));
+        pInput->actions |= SB_CENTERVIEW;
+
+        // adjust q16horiz negative
+        if (pInput->actions & SB_LOOK_DOWN)
+            horizAngle -= scaleAdjust * HORIZ_SPEED;
+
+        // adjust q16horiz positive
+        if (pInput->actions & SB_LOOK_UP)
+            horizAngle += scaleAdjust * HORIZ_SPEED;
     }
-    else
+
+    if (pInput->actions & SB_CENTERVIEW)
     {
-        pPlayer->q16horiz = 0;
+        if (!(pInput->actions & (SB_LOOK_UP|SB_LOOK_DOWN)))
+        {
+            // not pressing the q16horiz keys
+            if (horizAngle != 0)
+            {
+                // move q16horiz back to 100
+                horizAngle += scaleAdjust * ((1. / 65536.) - (horizAngle * (10. / GameTicRate)));
+            }
+            else
+            {
+                // not looking anymore because q16horiz is back at 100
+                pInput->actions &= ~SB_CENTERVIEW;
+            }
+        }
     }
+
+    // Convert back to Build's horizon and clamp.
+    pPlayer->q16horiz = pPlayer->q16look = clamp(IntToFixed(100) + xs_CRoundToInt(IntToFixed(128) * tan(horizAngle * (pi::pi() / 512.))), IntToFixed(PLAYER_HORIZ_MIN), IntToFixed(PLAYER_HORIZ_MAX));
 }
 
 //---------------------------------------------------------------------------
diff --git a/source/blood/src/view.cpp b/source/blood/src/view.cpp
index 3b8e46192..0e0e81931 100644
--- a/source/blood/src/view.cpp
+++ b/source/blood/src/view.cpp
@@ -74,8 +74,6 @@ INTERPOLATE gInterpolation[kMaxInterpolations];
 
 int gScreenTilt;
 
-int const defaultHoriz = polymostcenterhoriz;
-
 FFont *gFont[kFontNum];
 
 void FontSet(int id, int tile, int space)
@@ -806,8 +804,8 @@ void viewDrawScreen(bool sceneonly)
             for (int i = 0; i < 16; i++)
                 ror_status[i] = TestBitString(gotpic, 4080 + i);
             yax_preparedrawrooms();
-            DrawMirrors(vd8, vd4, vd0, IntToFixed(v50), IntToFixed(v54 + defaultHoriz), gInterpolate, -1);
-            drawrooms(vd8, vd4, vd0, v50, v54 + defaultHoriz, vcc);
+            DrawMirrors(vd8, vd4, vd0, IntToFixed(v50), IntToFixed(v54), gInterpolate, -1);
+            drawrooms(vd8, vd4, vd0, v50, v54, vcc);
             yax_drawrooms(viewProcessSprites, vcc, 0, gInterpolate);
             bool do_ror_hack = false;
             for (int i = 0; i < 16; i++)
@@ -881,7 +879,7 @@ void viewDrawScreen(bool sceneonly)
         for (int i = 0; i < 16; i++)
             ror_status[i] = TestBitString(gotpic, 4080 + i);
         fixed_t deliriumPitchI = interpolate(IntToFixed(deliriumPitchO), IntToFixed(deliriumPitch), gInterpolate);
-        DrawMirrors(cX, cY, cZ, cA, q16horiz + IntToFixed(defaultHoriz) + deliriumPitchI, gInterpolate, gViewIndex);
+        DrawMirrors(cX, cY, cZ, cA, q16horiz + deliriumPitchI, gInterpolate, gViewIndex);
         int bakCstat = gView->pSprite->cstat;
         if (gViewPos == 0)
         {
@@ -892,7 +890,7 @@ void viewDrawScreen(bool sceneonly)
             gView->pSprite->cstat |= 514;
         }
 
-        renderDrawRoomsQ16(cX, cY, cZ, cA, q16horiz + IntToFixed(defaultHoriz) + deliriumPitchI, nSectnum);
+        renderDrawRoomsQ16(cX, cY, cZ, cA, q16horiz + deliriumPitchI, nSectnum);
         viewProcessSprites(cX, cY, cZ, FixedToInt(cA), gInterpolate);
         bool do_ror_hack = false;
         for (int i = 0; i < 16; i++)
@@ -950,7 +948,7 @@ void viewDrawScreen(bool sceneonly)
         int v8 = byte_1CE5C2 > 0 && (sector[tmpSect].ceilingstat & 1);
         if (gWeather.at12d8 > 0 || v8)
         {
-            gWeather.Draw(cX, cY, cZ, cA, q16horiz + defaultHoriz + deliriumPitch, gWeather.at12d8);
+            gWeather.Draw(cX, cY, cZ, cA, q16horiz + deliriumPitch, gWeather.at12d8);
             if (v8)
             {
                 gWeather.at12d8 = ClipRange(delta * 8 + gWeather.at12d8, 0, 4095);

From f7957fc237b338fd0d464acde960c48fe8e3e95c Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Thu, 17 Sep 2020 17:06:19 +1000
Subject: [PATCH 19/41] - Blood: Factor in game's tic-rate when aiming/looking
 up/down.

* Doesn't matter for Blood, but will matter for SW in a unified approach. Might as well get it right here.
* Appreciate the literal doubles in lieu of the enums isn't great, but this function will go into the backend in due course.
---
 source/blood/src/player.cpp | 9 ++++-----
 1 file changed, 4 insertions(+), 5 deletions(-)

diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index 3f6878e9c..724cd1470 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1306,7 +1306,6 @@ int ActionScan(PLAYER *pPlayer, int *a2, int *a3)
 
 enum
 {
-    HORIZ_SPEED = 14,
     PLAYER_HORIZ_MIN = -79,
     PLAYER_HORIZ_MAX = 219
 };
@@ -1378,11 +1377,11 @@ void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust
 
         // adjust q16horiz negative
         if (pInput->actions & SB_AIM_DOWN)
-            horizAngle -= scaleAdjust * (HORIZ_SPEED >> 1);
+            horizAngle -= scaleAdjust * (210. / GameTicRate);
 
         // adjust q16horiz positive
         if (pInput->actions & SB_AIM_UP)
-            horizAngle += scaleAdjust * (HORIZ_SPEED >> 1);
+            horizAngle += scaleAdjust * (210. / GameTicRate);
     }
 
     // this is the unlocked type
@@ -1392,11 +1391,11 @@ void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust
 
         // adjust q16horiz negative
         if (pInput->actions & SB_LOOK_DOWN)
-            horizAngle -= scaleAdjust * HORIZ_SPEED;
+            horizAngle -= scaleAdjust * (420. / GameTicRate);
 
         // adjust q16horiz positive
         if (pInput->actions & SB_LOOK_UP)
-            horizAngle += scaleAdjust * HORIZ_SPEED;
+            horizAngle += scaleAdjust * (420. / GameTicRate);
     }
 
     if (pInput->actions & SB_CENTERVIEW)

From 0c4fe5f91b71ef78a01b9ec621b4901dddd11801 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Thu, 17 Sep 2020 17:20:33 +1000
Subject: [PATCH 20/41] - Blood: Fix remaining issues such as starting horizon,
 remove q16look and clean up some remaining bits in nnexts.cpp.

---
 source/blood/src/nnexts.cpp     | 25 +++++++++++++++----------
 source/blood/src/player.cpp     |  7 +++----
 source/blood/src/player.h       |  1 -
 source/blood/src/prediction.cpp |  1 -
 4 files changed, 18 insertions(+), 16 deletions(-)

diff --git a/source/blood/src/nnexts.cpp b/source/blood/src/nnexts.cpp
index 4349e9bab..40176f6ca 100644
--- a/source/blood/src/nnexts.cpp
+++ b/source/blood/src/nnexts.cpp
@@ -1482,17 +1482,22 @@ void trPlayerCtrlSetScreenEffect(XSPRITE* pXSource, PLAYER* pPlayer) {
 
 }
 
-void trPlayerCtrlSetLookAngle(XSPRITE* pXSource, PLAYER* pPlayer) {
-    
-    const int upAngle = 289;
-	const int downAngle = -347;
-    const double lookStepUp = 4.0 * upAngle / 60.0;
-    const double lookStepDown = -4.0 * downAngle / 60.0;
-
+void trPlayerCtrlSetLookAngle(XSPRITE* pXSource, PLAYER* pPlayer)
+{
     int look = pXSource->data2 << 5;
-    if (look > 0) pPlayer->q16look = min(mulscale8(FloatToFixed(lookStepUp), look), FloatToFixed(upAngle));
-    else if (look < 0) pPlayer->q16look = -max(mulscale8(FloatToFixed(lookStepDown), abs(look)), FloatToFixed(downAngle));
-    else pPlayer->q16look = 0;
+
+    if (abs(look) > 0)
+    {
+        if (pPlayer->q16horiz != IntToFixed(100))
+        {
+            // move q16horiz back to 100
+            pPlayer->q16horiz += IntToFixed(25) - (pPlayer->q16horiz >> 2);
+        }
+    }
+    else
+    {
+        pPlayer->q16horiz = IntToFixed(100);
+    }
 
 }
 
diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index 724cd1470..99a7234ac 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -721,9 +721,8 @@ void playerStart(int nPlayer, int bNewLevel)
     pPlayer->pXSprite->health = pDudeInfo->startHealth<<4;
     pPlayer->pSprite->cstat &= (unsigned short)~32768;
     pPlayer->bloodlust = 0;
-    pPlayer->q16horiz = 100;
+    pPlayer->q16horiz = IntToFixed(100);
     pPlayer->q16slopehoriz = 0;
-    pPlayer->q16look = 100;
     pPlayer->slope = 0;
     pPlayer->fraggerId = -1;
     pPlayer->underwaterTime = 1200;
@@ -1362,7 +1361,7 @@ void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust
     InputPacket *pInput = &pPlayer->input;
 
     // Calculate adjustment as true pitch (Fixed point math really sucks...)
-    double horizAngle = clamp(atan2(pPlayer->q16look - IntToFixed(100), IntToFixed(128)) * (512. / pi::pi()), -180, 180);
+    double horizAngle = clamp(atan2(pPlayer->q16horiz - IntToFixed(100), IntToFixed(128)) * (512. / pi::pi()), -180, 180);
 
     if (q16horz)
     {
@@ -1417,7 +1416,7 @@ void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust
     }
 
     // Convert back to Build's horizon and clamp.
-    pPlayer->q16horiz = pPlayer->q16look = clamp(IntToFixed(100) + xs_CRoundToInt(IntToFixed(128) * tan(horizAngle * (pi::pi() / 512.))), IntToFixed(PLAYER_HORIZ_MIN), IntToFixed(PLAYER_HORIZ_MAX));
+    pPlayer->q16horiz = clamp(IntToFixed(100) + xs_CRoundToInt(IntToFixed(128) * tan(horizAngle * (pi::pi() / 512.))), IntToFixed(PLAYER_HORIZ_MIN), IntToFixed(PLAYER_HORIZ_MAX));
 }
 
 //---------------------------------------------------------------------------
diff --git a/source/blood/src/player.h b/source/blood/src/player.h
index 0b25ec5d4..8dc84bd86 100644
--- a/source/blood/src/player.h
+++ b/source/blood/src/player.h
@@ -108,7 +108,6 @@ struct PLAYER
     int                 zViewVel;
     int                 zWeapon;
     int                 zWeaponVel;
-    fixed_t             q16look;
     fixed_t             q16horiz;       // horiz
     fixed_t             q16slopehoriz;  // horizoff
     int                 slope;
diff --git a/source/blood/src/prediction.cpp b/source/blood/src/prediction.cpp
index 03cfa333f..6908981c7 100644
--- a/source/blood/src/prediction.cpp
+++ b/source/blood/src/prediction.cpp
@@ -58,7 +58,6 @@ static VIEW predictFifo[256];
 void viewInitializePrediction(void)
 {
 	predict.at30 = gMe->q16ang;
-	predict.at20 = gMe->q16look;
 	predict.at24 = gMe->q16horiz;
 	predict.at28 = gMe->q16slopehoriz;
 	predict.at2c = gMe->slope;

From 0ab3b33a6c1ee98b8d28369a824be0dfa0950003 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Sun, 20 Sep 2020 20:01:05 +1000
Subject: [PATCH 21/41] - Blood/Duke/RR/SW: Unify the player's horizon
 function.

* For Duke/SW, we continually apply `SB_CENTERVIEW` only if it was previously a toggled action, similar to Blood.
* For SW, we remove two SW-specific bits (`PF_LOCK_HORIZ` and `PF_LOOKING`) that are no longer needed.
* For Duke, we remove `return_to_center` and just use the `SB_CENTERVIEW` action bit as required.
* For `sethorizon()`, feature set and adjustment speeds are an averaged out accumulation across Duke/SW:
** GameTicRate is factored in for adjustment decisions to provide consistency for SW being the faster game.
** Adjustment amounts are half way between Duke/SW.
---
 source/blood/src/blood.h             |  2 +
 source/blood/src/controls.cpp        |  3 +-
 source/blood/src/player.cpp          | 72 +--------------------------
 source/blood/src/player.h            |  2 +-
 source/blood/src/view.cpp            |  2 +-
 source/core/gamecontrol.cpp          | 66 +++++++++++++++++++++++++
 source/core/gamecontrol.h            |  2 +
 source/core/gamestruct.h             |  2 +
 source/games/duke/src/actors_d.cpp   |  2 +-
 source/games/duke/src/actors_r.cpp   |  2 +-
 source/games/duke/src/conlabeldef.h  |  1 -
 source/games/duke/src/funct.h        |  1 -
 source/games/duke/src/gameexec.cpp   | 11 ++---
 source/games/duke/src/gameloop.cpp   |  2 +
 source/games/duke/src/input.cpp      | 11 +++--
 source/games/duke/src/player.cpp     | 73 +++++++++------------------
 source/games/duke/src/player_d.cpp   |  2 +-
 source/games/duke/src/player_r.cpp   |  2 +-
 source/games/duke/src/prediction.cpp |  2 -
 source/games/duke/src/premap.cpp     |  2 +-
 source/games/duke/src/savegame.cpp   |  2 -
 source/games/duke/src/types.h        |  3 +-
 source/sw/src/game.cpp               |  1 +
 source/sw/src/game.h                 |  4 +-
 source/sw/src/player.cpp             | 74 +++-------------------------
 25 files changed, 126 insertions(+), 220 deletions(-)

diff --git a/source/blood/src/blood.h b/source/blood/src/blood.h
index 1ae1a0add..c188e49b3 100644
--- a/source/blood/src/blood.h
+++ b/source/blood/src/blood.h
@@ -99,6 +99,8 @@ struct GameInterface : ::GameInterface
 	void LevelCompleted(MapRecord* map, int skill) override;
 	bool DrawAutomapPlayer(int x, int y, int z, int a) override;
 	void SetTileProps(int til, int surf, int vox, int shade) override;
+	fixed_t playerHorizMin() override { return IntToFixed(-79); }
+	fixed_t playerHorizMax() override { return IntToFixed(219); }
 
 	GameStats getStats() override;
 };
diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index a441c8d19..15b2c4c27 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -42,7 +42,6 @@ enum
 };
 
 void applylook(PLAYER *pPlayer, fixed_t const q16avel, double const scaleAdjust);
-void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust);
 
 //---------------------------------------------------------------------------
 //
@@ -140,7 +139,7 @@ static void processMovement(ControlInfo* const hidInput)
         if (gView->pXSprite->health != 0)
         {
             applylook(pPlayer, input.q16avel, scaleAdjust);
-            sethorizon(pPlayer, input.q16horz, scaleAdjust);
+            sethorizon(&pPlayer->q16horiz, input.q16horz, &pPlayer->input.actions, scaleAdjust);
         }
 
         // Process angle amendments from the game's ticker.
diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index 99a7234ac..b277f4b42 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1350,75 +1350,6 @@ void applylook(PLAYER *pPlayer, fixed_t const q16avel, double const scaleAdjust)
     pPlayer->angold = pSprite->ang = FixedToInt(pPlayer->q16ang);
 }
 
-//---------------------------------------------------------------------------
-//
-// Player's horizon function, called in processInput() or from gi->GetInput() as required.
-//
-//---------------------------------------------------------------------------
-
-void sethorizon(PLAYER *pPlayer, fixed_t const q16horz, double const scaleAdjust)
-{
-    InputPacket *pInput = &pPlayer->input;
-
-    // Calculate adjustment as true pitch (Fixed point math really sucks...)
-    double horizAngle = clamp(atan2(pPlayer->q16horiz - IntToFixed(100), IntToFixed(128)) * (512. / pi::pi()), -180, 180);
-
-    if (q16horz)
-    {
-        pInput->actions &= ~SB_CENTERVIEW;
-        horizAngle += FixedToFloat(q16horz);
-    }
-
-    // this is the locked type
-    if (pInput->actions & (SB_AIM_UP|SB_AIM_DOWN))
-    {
-        pInput->actions &= ~SB_CENTERVIEW;
-
-        // adjust q16horiz negative
-        if (pInput->actions & SB_AIM_DOWN)
-            horizAngle -= scaleAdjust * (210. / GameTicRate);
-
-        // adjust q16horiz positive
-        if (pInput->actions & SB_AIM_UP)
-            horizAngle += scaleAdjust * (210. / GameTicRate);
-    }
-
-    // this is the unlocked type
-    if (pInput->actions & (SB_LOOK_UP|SB_LOOK_DOWN))
-    {
-        pInput->actions |= SB_CENTERVIEW;
-
-        // adjust q16horiz negative
-        if (pInput->actions & SB_LOOK_DOWN)
-            horizAngle -= scaleAdjust * (420. / GameTicRate);
-
-        // adjust q16horiz positive
-        if (pInput->actions & SB_LOOK_UP)
-            horizAngle += scaleAdjust * (420. / GameTicRate);
-    }
-
-    if (pInput->actions & SB_CENTERVIEW)
-    {
-        if (!(pInput->actions & (SB_LOOK_UP|SB_LOOK_DOWN)))
-        {
-            // not pressing the q16horiz keys
-            if (horizAngle != 0)
-            {
-                // move q16horiz back to 100
-                horizAngle += scaleAdjust * ((1. / 65536.) - (horizAngle * (10. / GameTicRate)));
-            }
-            else
-            {
-                // not looking anymore because q16horiz is back at 100
-                pInput->actions &= ~SB_CENTERVIEW;
-            }
-        }
-    }
-
-    // Convert back to Build's horizon and clamp.
-    pPlayer->q16horiz = clamp(IntToFixed(100) + xs_CRoundToInt(IntToFixed(128) * tan(horizAngle * (pi::pi() / 512.))), IntToFixed(PLAYER_HORIZ_MIN), IntToFixed(PLAYER_HORIZ_MAX));
-}
-
 //---------------------------------------------------------------------------
 //
 // Unsynchronised input helpers.
@@ -1429,7 +1360,6 @@ void resetinputhelpers(PLAYER* pPlayer)
 {
     pPlayer->horizAdjust = 0;
     pPlayer->angAdjust = 0;
-    pPlayer->pitchAdjust = 0;
 }
 
 void playerAddAngle(PLAYER* pPlayer, double ang)
@@ -1725,7 +1655,7 @@ void ProcessInput(PLAYER *pPlayer)
 
     if (cl_syncinput)
     {
-        sethorizon(pPlayer, pInput->q16horz, 1);
+        sethorizon(&pPlayer->q16horiz, pInput->q16horz, &pInput->actions, 1);
     }
 
     int nSector = pSprite->sectnum;
diff --git a/source/blood/src/player.h b/source/blood/src/player.h
index 8dc84bd86..3eb72a34e 100644
--- a/source/blood/src/player.h
+++ b/source/blood/src/player.h
@@ -186,7 +186,7 @@ struct PLAYER
     POSTURE             pPosture[kModeMax][kPostureMax];
 
     // Input helper variables and setters.
-    double horizAdjust, angAdjust, pitchAdjust;
+    double horizAdjust, angAdjust;
     fixed_t horizTarget, angTarget;
     void addang(int v) { q16ang = (q16ang + IntToFixed(v)) & 0x7FFFFFF; }
     void setang(int v) { q16ang = IntToFixed(v); }
diff --git a/source/blood/src/view.cpp b/source/blood/src/view.cpp
index 0e0e81931..443f3f399 100644
--- a/source/blood/src/view.cpp
+++ b/source/blood/src/view.cpp
@@ -873,7 +873,7 @@ void viewDrawScreen(bool sceneonly)
         {
             cZ = vfc + (gLowerLink[nSectnum] >= 0 ? 0 : (8 << 8));
         }
-        q16horiz = ClipRange(q16horiz, IntToFixed(-200), IntToFixed(200));
+        q16horiz = ClipRange(q16horiz, gi->playerHorizMin(), gi->playerHorizMax());
     RORHACK:
         int ror_status[16];
         for (int i = 0; i < 16; i++)
diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp
index 36110d3b1..d7d825104 100644
--- a/source/core/gamecontrol.cpp
+++ b/source/core/gamecontrol.cpp
@@ -1487,3 +1487,69 @@ fixed_t getincangleq16(fixed_t a, fixed_t na)
 		return (na-a);
 	}
 }
+
+//---------------------------------------------------------------------------
+//
+// Player's horizon function, called in processInput() or from gi->GetInput() as required.
+//
+//---------------------------------------------------------------------------
+
+void sethorizon(fixed_t* q16horiz, fixed_t const q16horz, ESyncBits* actions, double const scaleAdjust)
+{
+	// Calculate adjustment as true pitch (Fixed point math really sucks...)
+	double horizAngle = atan2(*q16horiz - IntToFixed(100), IntToFixed(128)) * (512. / pi::pi());
+
+	if (q16horz)
+	{
+		*actions &= ~SB_CENTERVIEW;
+		horizAngle = clamp(horizAngle + FixedToFloat(q16horz), -180, 180);
+	}
+
+	// this is the locked type
+	if (*actions & (SB_AIM_UP|SB_AIM_DOWN))
+	{
+		*actions &= ~SB_CENTERVIEW;
+		double const amount = 250. / GameTicRate;
+
+		if (*actions & SB_AIM_DOWN)
+			horizAngle -= scaleAdjust * amount;
+
+		if (*actions & SB_AIM_UP)
+			horizAngle += scaleAdjust * amount;
+	}
+
+	// this is the unlocked type
+	if (*actions & (SB_LOOK_UP|SB_LOOK_DOWN))
+	{
+		*actions |= SB_CENTERVIEW;
+		double const amount = 500. / GameTicRate;
+
+		if (*actions & SB_LOOK_DOWN)
+			horizAngle -= scaleAdjust * amount;
+
+		if (*actions & SB_LOOK_UP)
+			horizAngle += scaleAdjust * amount;
+	}
+
+	// convert back to Build's horizon
+	*q16horiz = IntToFixed(100) + xs_CRoundToInt(IntToFixed(128) * tan(horizAngle * (pi::pi() / 512.)));
+
+	// return to center if conditions met.
+	if ((*actions & SB_CENTERVIEW) && !(*actions & (SB_LOOK_UP|SB_LOOK_DOWN)))
+	{
+		if (*q16horiz < FloatToFixed(99.75) || *q16horiz > FloatToFixed(100.25))
+		{
+			// move *q16horiz back to 100
+			*q16horiz += xs_CRoundToInt(scaleAdjust * (((1000. / GameTicRate) * FRACUNIT) - (*q16horiz * (10. / GameTicRate))));
+		}
+		else
+		{
+			// not looking anymore because *q16horiz is back at 100
+			*q16horiz = IntToFixed(100);
+			*actions &= ~SB_CENTERVIEW;
+		}
+	}
+
+	// clamp before returning
+	*q16horiz = clamp(*q16horiz, gi->playerHorizMin(), gi->playerHorizMax());
+}
diff --git a/source/core/gamecontrol.h b/source/core/gamecontrol.h
index 4461c91b4..9b7fdf8b1 100644
--- a/source/core/gamecontrol.h
+++ b/source/core/gamecontrol.h
@@ -66,6 +66,8 @@ void CompleteLevel(MapRecord* map);
 int getincangle(int c, int n);
 fixed_t getincangleq16(fixed_t c, fixed_t n);
 
+void sethorizon(fixed_t* q16horiz, fixed_t const q16horz, ESyncBits* actions, double const scaleAdjust);
+
 struct UserConfig
 {
 	FString gamegrp;
diff --git a/source/core/gamestruct.h b/source/core/gamestruct.h
index 91aa9e9af..e4d442be0 100644
--- a/source/core/gamestruct.h
+++ b/source/core/gamestruct.h
@@ -108,6 +108,8 @@ struct GameInterface
 	virtual void LevelCompleted(MapRecord* map, int skill) {}
 	virtual bool DrawAutomapPlayer(int x, int y, int z, int a) { return false; }
 	virtual void SetTileProps(int tile, int surf, int vox, int shade) {}
+	virtual fixed_t playerHorizMin() { return IntToFixed(-99); }
+	virtual fixed_t playerHorizMax() { return IntToFixed(299); }
 
 	virtual FString statFPS()
 	{
diff --git a/source/games/duke/src/actors_d.cpp b/source/games/duke/src/actors_d.cpp
index 04ccab479..c06230e9a 100644
--- a/source/games/duke/src/actors_d.cpp
+++ b/source/games/duke/src/actors_d.cpp
@@ -1853,7 +1853,7 @@ void moveweapons_d(void)
 						if (s->picnum == SPIT)
 						{
 							playerAddHoriz(&ps[p], 32);
-							ps[p].return_to_center = 8;
+							sync[p].actions |= SB_CENTERVIEW;
 
 							if (ps[p].loogcnt == 0)
 							{
diff --git a/source/games/duke/src/actors_r.cpp b/source/games/duke/src/actors_r.cpp
index f55e8b22d..61f517ba7 100644
--- a/source/games/duke/src/actors_r.cpp
+++ b/source/games/duke/src/actors_r.cpp
@@ -1400,7 +1400,7 @@ void moveweapons_r(void)
 							}
 
 							playerAddHoriz(&ps[p], 32);
-							ps[p].return_to_center = 8;
+							sync[p].actions |= SB_CENTERVIEW;
 
 							if (ps[p].loogcnt == 0)
 							{
diff --git a/source/games/duke/src/conlabeldef.h b/source/games/duke/src/conlabeldef.h
index a842c4cee..a103d21b3 100644
--- a/source/games/duke/src/conlabeldef.h
+++ b/source/games/duke/src/conlabeldef.h
@@ -261,7 +261,6 @@ LABELS playerlabels[]=
 	{ "pals", PLAYER_PALS, LABEL_HASPARM2, 3 },
 	{ "max_actors_killed", PLAYER_MAX_ACTORS_KILLED, 0, 0 },
 	{ "actors_killed", PLAYER_ACTORS_KILLED, 0, 0 },
-	{ "return_to_center", PLAYER_RETURN_TO_CENTER, 0, 0 },
 	
   { "", -1, 0, 0  }	// END OF LIST
 	
diff --git a/source/games/duke/src/funct.h b/source/games/duke/src/funct.h
index c440f890a..80affff71 100644
--- a/source/games/duke/src/funct.h
+++ b/source/games/duke/src/funct.h
@@ -229,7 +229,6 @@ int playercolor2lookup(int color);
 void PlayerColorChanged(void);
 void apply_seasick(player_struct* p, double scalefactor);
 void calcviewpitch(player_struct* p, double factor);
-void sethorizon(int snum, ESyncBits actions, double factor, fixed_t adjustment);
 bool movementBlocked(int snum);
 void loadcons();
 void recordoldspritepos();
diff --git a/source/games/duke/src/gameexec.cpp b/source/games/duke/src/gameexec.cpp
index 7ce0e45e9..c2b583c04 100644
--- a/source/games/duke/src/gameexec.cpp
+++ b/source/games/duke/src/gameexec.cpp
@@ -84,7 +84,7 @@ void fakebubbaspawn(int g_i, int g_p);
 void tearitup(int sect);
 void destroyit(int g_i);
 void mamaspawn(int g_i);
-void forceplayerangle(struct player_struct* p);
+void forceplayerangle(int snum);
 
 bool killthesprite = false;
 
@@ -909,11 +909,6 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, int sActor, int sPl
 		else SetGameVarID((int)lVar2, ps[iPlayer].actors_killed, sActor, sPlayer);
 		break;
 
-	case PLAYER_RETURN_TO_CENTER:
-		if (bSet) ps[iPlayer].return_to_center = lValue;
-		else SetGameVarID((int)lVar2, ps[iPlayer].return_to_center, sActor, sPlayer);
-		break;
-
 	default:
 		if (!bSet) SetGameVarID((int)lVar2, 0, sActor, sPlayer);
 		break;
@@ -2461,14 +2456,14 @@ int ParseState::parse(void)
 		break;
 	case concmd_slapplayer:
 		insptr++;
-		forceplayerangle(&ps[g_p]);
+		forceplayerangle(g_p);
 		ps[g_p].posxv -= sintable[(ps[g_p].getang() + 512) & 2047] << 7;
 		ps[g_p].posyv -= sintable[ps[g_p].getang() & 2047] << 7;
 		return 0;
 	case concmd_wackplayer:
 		insptr++;
 		if (!isRR())
-			forceplayerangle(&ps[g_p]);
+			forceplayerangle(g_p);
 		else
 		{
 			ps[g_p].posxv -= sintable[(ps[g_p].getang() + 512) & 2047] << 10;
diff --git a/source/games/duke/src/gameloop.cpp b/source/games/duke/src/gameloop.cpp
index 028ae3dc3..2f5a95958 100644
--- a/source/games/duke/src/gameloop.cpp
+++ b/source/games/duke/src/gameloop.cpp
@@ -49,7 +49,9 @@ void GameInterface::Ticker()
 	// Make copies so that the originals do not have to be modified.
 	for (int i = 0; i < MAXPLAYERS; i++)
 	{
+		auto oldactions = sync[i].actions;
 		sync[i] = playercmds[i].ucmd;
+		if (oldactions & SB_CENTERVIEW) sync[i].actions |= SB_CENTERVIEW;
 	}
 	if (rtsplaying > 0) rtsplaying--;
 
diff --git a/source/games/duke/src/input.cpp b/source/games/duke/src/input.cpp
index 0df2701f9..dbcf20ba9 100644
--- a/source/games/duke/src/input.cpp
+++ b/source/games/duke/src/input.cpp
@@ -67,7 +67,7 @@ void hud_input(int snum)
 	i = p->aim_mode;
 	p->aim_mode = !PlayerInput(snum, SB_AIMMODE);
 	if (p->aim_mode < i)
-		p->return_to_center = 9;
+		sync[snum].actions |= SB_CENTERVIEW;
 
 	// Backup weapon here as hud_input() is the first function where any one of the weapon variables can change.
 	backupweapon(p);
@@ -965,7 +965,7 @@ static void FinalizeInput(int playerNum, InputPacket& input, bool vehicle)
 			loc.q16avel = input.q16avel = 0;
 		}
 
-		if (p->newowner == -1 && p->return_to_center <= 0)
+		if (p->newowner == -1 && !(sync[playerNum].actions & SB_CENTERVIEW))
 		{
 			loc.q16horz = clamp(loc.q16horz + input.q16horz, IntToFixed(-MAXHORIZVEL), IntToFixed(MAXHORIZVEL));
 		}
@@ -1025,7 +1025,12 @@ static void GetInputInternal(InputPacket &locInput, ControlInfo* const hidInput)
 		// Do these in the same order as the old code.
 		calcviewpitch(p, scaleAdjust);
 		applylook(myconnectindex, scaleAdjust, input.q16avel);
-		sethorizon(myconnectindex, loc.actions, scaleAdjust, input.q16horz);
+		sethorizon(&p->q16horiz, input.q16horz, &sync[myconnectindex].actions, scaleAdjust);
+
+		if (p->horizAdjust)
+        {
+            p->q16horiz += FloatToFixed(scaleAdjust * p->horizAdjust);
+        }
 	}
 }
 
diff --git a/source/games/duke/src/player.cpp b/source/games/duke/src/player.cpp
index 843375871..b6dc30f41 100644
--- a/source/games/duke/src/player.cpp
+++ b/source/games/duke/src/player.cpp
@@ -157,14 +157,15 @@ void quickkill(struct player_struct* p)
 //
 //---------------------------------------------------------------------------
 
-void forceplayerangle(struct player_struct* p)
+void forceplayerangle(int snum)
 {
+	player_struct* p = &ps[snum];
 	int n;
 
 	n = 128 - (krand() & 255);
 
 	playerAddHoriz(p, 64);
-	p->return_to_center = 9;
+	sync[snum].actions |= SB_CENTERVIEW;
 	p->setlookang(n >> 1);
 	p->setrotscrnang(n >> 1);
 }
@@ -406,7 +407,7 @@ void dokneeattack(int snum, int pi, const std::initializer_list<int> & respawnli
 	{
 		p->knee_incs++;
 		playerAddHoriz(p, -48);
-		p->return_to_center = 9;
+		sync[snum].actions |= SB_CENTERVIEW;
 		if (p->knee_incs > 15)
 		{
 			p->knee_incs = 0;
@@ -920,7 +921,6 @@ void resetinputhelpers(player_struct* p)
 {
 	p->horizAdjust = 0;
 	p->angAdjust = 0;
-	p->pitchAdjust = 0;
 }
 
 //---------------------------------------------------------------------------
@@ -1002,43 +1002,6 @@ void checklook(int snum, ESyncBits actions)
 //
 //---------------------------------------------------------------------------
 
-void sethorizon(int snum, ESyncBits actions, double factor, fixed_t adjustment)
-{
-	auto p = &ps[snum];
-
-	// Calculate adjustment as true pitch (Fixed point math really sucks...)
-	double horizAngle = clamp(atan2(p->q16horiz - IntToFixed(100), IntToFixed(128)) * (512. / pi::pi()) + (factor * p->pitchAdjust) + (adjustment / 65536.), -180, 180);
-
-	if (p->return_to_center > 0 && (actions & (SB_LOOK_UP | SB_LOOK_DOWN)) == 0) // only snap back if no relevant button is pressed.
-	{
-		p->return_to_center += -factor * (p->return_to_center / 2);
-		horizAngle += -factor * (horizAngle / 2);
-
-		if (horizAngle > -0.5 && horizAngle < 0.5)
-		{
-			horizAngle = 0.;
-			p->return_to_center = 0.;
-		}
-	}
-
-	// Convert back to Build's horizon.
-	p->q16horiz = IntToFixed(100) + xs_CRoundToInt(IntToFixed(128) * tan(horizAngle * (pi::pi() / 512.)));
-
-	// Add horizAdjust if input is unsynchronised.
-	if (!cl_syncinput)
-	{
-		p->q16horiz += xs_CRoundToInt(factor * (p->horizAdjust * 65536.));
-	}
-
-	p->q16horiz = clamp(p->q16horiz, IntToFixed(HORIZ_MIN), IntToFixed(HORIZ_MAX));
-}
-
-//---------------------------------------------------------------------------
-//
-//
-//
-//---------------------------------------------------------------------------
-
 void playerCenterView(int snum)
 {
 	auto p = &ps[snum];
@@ -1046,7 +1009,11 @@ void playerCenterView(int snum)
 	OnEvent(EVENT_RETURNTOCENTER, p->i, snum, -1);
 	if (GetGameVarID(g_iReturnVarID, p->i, snum) == 0)
 	{
-		p->return_to_center = 9;
+		sync[snum].actions |= SB_CENTERVIEW;
+	}
+	else
+	{
+		sync[snum].actions &= ~SB_CENTERVIEW;
 	}
 }
 
@@ -1057,8 +1024,11 @@ void playerLookUp(int snum, ESyncBits actions)
 	OnEvent(EVENT_LOOKUP, p->i, snum, -1);
 	if (GetGameVarID(g_iReturnVarID, p->i, snum) == 0)
 	{
-		p->return_to_center = 9;
-		p->pitchAdjust += (actions & SB_RUN) ? 12 : 24;
+		sync[snum].actions |= SB_CENTERVIEW;
+	}
+	else
+	{
+		sync[snum].actions &= ~SB_LOOK_UP;
 	}
 }
 
@@ -1069,8 +1039,11 @@ void playerLookDown(int snum, ESyncBits actions)
 	OnEvent(EVENT_LOOKDOWN, p->i, snum, -1);
 	if (GetGameVarID(g_iReturnVarID, p->i, snum) == 0)
 	{
-		p->return_to_center = 9;
-		p->pitchAdjust -= (actions & SB_RUN) ? 12 : 24;
+		sync[snum].actions |= SB_CENTERVIEW;
+	}
+	else
+	{
+		sync[snum].actions &= ~SB_LOOK_DOWN;
 	}
 }
 
@@ -1079,9 +1052,9 @@ void playerAimUp(int snum, ESyncBits actions)
 	auto p = &ps[snum];
 	SetGameVarID(g_iReturnVarID, 0, p->i, snum);
 	OnEvent(EVENT_AIMUP, p->i, snum, -1);
-	if (GetGameVarID(g_iReturnVarID, p->i, snum) == 0)
+	if (GetGameVarID(g_iReturnVarID, p->i, snum) != 0)
 	{
-		p->pitchAdjust += (actions & SB_RUN) ? 6 : 12;
+		sync[snum].actions &= ~SB_AIM_UP;
 	}
 }
 
@@ -1090,9 +1063,9 @@ void playerAimDown(int snum, ESyncBits actions)
 	auto p = &ps[snum];
 	SetGameVarID(g_iReturnVarID, 0, p->i, snum);
 	OnEvent(EVENT_AIMDOWN, p->i, snum, -1);
-	if (GetGameVarID(g_iReturnVarID, p->i, snum) == 0)
+	if (GetGameVarID(g_iReturnVarID, p->i, snum) != 0)
 	{
-		p->pitchAdjust -= (actions & SB_RUN) ? 6 : 12;
+		sync[snum].actions &= ~SB_AIM_DOWN;
 	}
 }
 
diff --git a/source/games/duke/src/player_d.cpp b/source/games/duke/src/player_d.cpp
index 97d4a8e4f..700ab020f 100644
--- a/source/games/duke/src/player_d.cpp
+++ b/source/games/duke/src/player_d.cpp
@@ -3078,7 +3078,7 @@ HORIZONLY:
 
 	if (cl_syncinput)
 	{
-		sethorizon(snum, actions, 1, PlayerHorizon(snum));
+		sethorizon(&p->q16horiz, PlayerHorizon(snum), &actions, 1);
 	}
 
 	checkhardlanding(p);
diff --git a/source/games/duke/src/player_r.cpp b/source/games/duke/src/player_r.cpp
index 3cf212da0..9b3cd60b6 100644
--- a/source/games/duke/src/player_r.cpp
+++ b/source/games/duke/src/player_r.cpp
@@ -4091,7 +4091,7 @@ HORIZONLY:
 
 	if (cl_syncinput)
 	{
-		sethorizon(snum, actions, 1, PlayerHorizon(snum));
+		sethorizon(&p->q16horiz, PlayerHorizon(snum), &actions, 1);
 	}
 
 	checkhardlanding(p);
diff --git a/source/games/duke/src/prediction.cpp b/source/games/duke/src/prediction.cpp
index 8bddff24a..0a777b6c6 100644
--- a/source/games/duke/src/prediction.cpp
+++ b/source/games/duke/src/prediction.cpp
@@ -62,7 +62,6 @@ void resetmys()
 	myjumpingtoggle = ps[myconnectindex].jumping_toggle;
 	myonground = ps[myconnectindex].on_ground;
 	myhardlanding = ps[myconnectindex].hard_landing;
-	myreturntocenter = ps[myconnectindex].return_to_center;
 }
 
 #if 0 // todo: fix this when networking works again
@@ -90,7 +89,6 @@ void fakedomovethingscorrect(void)
 	 myjumpingtoggle = p->jumping_toggle;
 	 myonground = p->on_ground;
 	 myhardlanding = p->hard_landing;
-	 myreturntocenter = p->return_to_center;
 
 	 fakemovefifoplc = movefifoplc;
 	 while (fakemovefifoplc < movefifoend[myconnectindex])
diff --git a/source/games/duke/src/premap.cpp b/source/games/duke/src/premap.cpp
index ea3e1978b..051c44835 100644
--- a/source/games/duke/src/premap.cpp
+++ b/source/games/duke/src/premap.cpp
@@ -119,7 +119,7 @@ void resetplayerstats(int snum)
     p->bobcounter       = 0;
     p->on_ground        = 0;
     p->player_par       = 0;
-    p->return_to_center = 9;
+    sync[snum].actions |= SB_CENTERVIEW;
     p->airleft          = 15*26;
     p->rapid_fire_hold  = 0;
     p->toggle_key_flag  = 0;
diff --git a/source/games/duke/src/savegame.cpp b/source/games/duke/src/savegame.cpp
index 219283e94..14fc52b17 100644
--- a/source/games/duke/src/savegame.cpp
+++ b/source/games/duke/src/savegame.cpp
@@ -229,7 +229,6 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, player_struct& w,
 			("walking_snd_toggle", w.walking_snd_toggle)
 			("palookup", w.palookup)
 			("hard_landing", w.hard_landing)
-			("return_to_center", w.return_to_center)
 			("max_secret_rooms", w.max_secret_rooms)
 			("secret_rooms", w.secret_rooms)
 			("max_actors_killed", w.max_actors_killed)
@@ -299,7 +298,6 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, player_struct& w,
 		w.ohard_landing = w.hard_landing;
 		w.horizAdjust = 0;
 		w.angAdjust = 0;
-		w.pitchAdjust = 0;
 		w.lookLeft = false;
 		w.lookRight = false;
 	}
diff --git a/source/games/duke/src/types.h b/source/games/duke/src/types.h
index 1cf7ef5d9..812af4888 100644
--- a/source/games/duke/src/types.h
+++ b/source/games/duke/src/types.h
@@ -169,7 +169,6 @@ struct player_struct
 
 	unsigned char toggle_key_flag, knuckle_incs; // ,select_dir;
 	unsigned char walking_snd_toggle, palookup;
-	double return_to_center;
 	bool quick_kick_msg;
 
 	int max_secret_rooms, secret_rooms, max_actors_killed, actors_killed;
@@ -208,7 +207,7 @@ struct player_struct
 	int8_t crouch_toggle;
 
 	// input stuff.
-	double  horizAdjust, angAdjust, pitchAdjust;
+	double  horizAdjust, angAdjust;
 	bool    lookLeft, lookRight;
 
 
diff --git a/source/sw/src/game.cpp b/source/sw/src/game.cpp
index e379ee2d3..642cb8148 100644
--- a/source/sw/src/game.cpp
+++ b/source/sw/src/game.cpp
@@ -625,6 +625,7 @@ void GameInterface::Ticker(void)
         auto pp = Player + i;
         pp->lastinput = pp->input;
         pp->input = playercmds[i].ucmd;
+        if (pp->lastinput.actions & SB_CENTERVIEW) pp->input.actions |= SB_CENTERVIEW;
     }
 
     domovethings();
diff --git a/source/sw/src/game.h b/source/sw/src/game.h
index 85e3344a9..63c7c4eea 100644
--- a/source/sw/src/game.h
+++ b/source/sw/src/game.h
@@ -1008,7 +1008,7 @@ struct PLAYERstruct
     char WpnReloadState;
 
     // Input helper variables and setters.
-    double horizAdjust, angAdjust, pitchAdjust;
+    double horizAdjust, angAdjust;
     fixed_t horizTarget, angTarget;
     void addang(int v) { q16ang = (q16ang + IntToFixed(v)) & 0x7FFFFFF; }
     void setang(int v) { q16ang = IntToFixed(v); }
@@ -1029,8 +1029,6 @@ enum
     PF_JUMPING                  = (BIT(2)),
     PF_FALLING                  = (BIT(3)),
     PF_LOCK_CRAWL               = (BIT(4)),
-    PF_LOCK_HORIZ               = (BIT(5)),
-    PF_LOOKING                  = (BIT(6)),
     PF_PLAYER_MOVED             = (BIT(7)),
     PF_PLAYER_RIDING            = (BIT(8)),
     PF_AUTO_AIM                 = (BIT(9)),
diff --git a/source/sw/src/player.cpp b/source/sw/src/player.cpp
index 18fd9776a..622b3d3ec 100644
--- a/source/sw/src/player.cpp
+++ b/source/sw/src/player.cpp
@@ -1785,68 +1785,8 @@ DoPlayerHorizon(PLAYERp pp, fixed_t const q16horz, double const scaleAdjust)
     if (cl_slopetilting)
         PlayerAutoLook(pp, scaleAdjust);
 
-    // Calculate adjustment as true pitch (Fixed point math really sucks...)
-    double horizAngle = clamp(atan2(pp->q16horizbase - IntToFixed(100), IntToFixed(128)) * (512. / pi::pi()), -180, 180);
-
-    if (q16horz)
-    {
-        horizAngle += FixedToFloat(q16horz);
-        SET(pp->Flags, PF_LOCK_HORIZ | PF_LOOKING);
-    }
-
-    // this is the locked type
-    if (pp->input.actions & (SB_AIM_UP|SB_AIM_DOWN))
-    {
-        // set looking because player is manually looking.
-        SET(pp->Flags, PF_LOCK_HORIZ | PF_LOOKING);
-
-        // adjust q16horiz negative
-        if (pp->input.actions & SB_AIM_DOWN)
-            horizAngle -= scaleAdjust * (HORIZ_SPEED >> 1);
-
-        // adjust q16horiz positive
-        if (pp->input.actions & SB_AIM_UP)
-            horizAngle += scaleAdjust * (HORIZ_SPEED >> 1);
-    }
-
-    // this is the unlocked type
-    if (pp->input.actions & (SB_LOOK_UP|SB_LOOK_DOWN|SB_CENTERVIEW))
-    {
-        RESET(pp->Flags, PF_LOCK_HORIZ);
-        SET(pp->Flags, PF_LOOKING);
-
-        // adjust q16horiz negative
-        if (pp->input.actions & SB_LOOK_DOWN)
-            horizAngle -= scaleAdjust * HORIZ_SPEED;
-
-        // adjust q16horiz positive
-        if (pp->input.actions & SB_LOOK_UP)
-            horizAngle += scaleAdjust * HORIZ_SPEED;
-
-        if (pp->input.actions & SB_CENTERVIEW)
-            pp->q16horizoff = 0;
-    }
-
-    if (!TEST(pp->Flags, PF_LOCK_HORIZ))
-    {
-        if (!(pp->input.actions & (SB_LOOK_UP|SB_LOOK_DOWN)))
-        {
-            // not pressing the q16horiz keys
-            if (horizAngle != 0)
-            {
-                // move q16horiz back to 100
-                horizAngle += scaleAdjust * ((1. / 65536.) - (horizAngle * 0.25));
-            }
-            else
-            {
-                // not looking anymore because q16horiz is back at 100
-                RESET(pp->Flags, PF_LOOKING);
-            }
-        }
-    }
-
-    // Convert back to Build's horizon and clamp.
-    pp->q16horizbase = clamp(IntToFixed(100) + xs_CRoundToInt(IntToFixed(128) * tan(horizAngle * (pi::pi() / 512.))), IntToFixed(PLAYER_HORIZ_MIN), IntToFixed(PLAYER_HORIZ_MAX));
+    // apply default horizon from backend
+    sethorizon(&pp->q16horizbase, q16horz, &pp->input.actions, scaleAdjust);
 
     // bound adjust q16horizoff
     if (pp->q16horizbase + pp->q16horizoff < IntToFixed(PLAYER_HORIZ_MIN))
@@ -3408,10 +3348,9 @@ DoPlayerFall(PLAYERp pp)
         }
         else if (pp->jump_speed > 1300)
         {
-            if (TEST(pp->Flags, PF_LOCK_HORIZ))
+            if (!(pp->input.actions & SB_CENTERVIEW))
             {
-                RESET(pp->Flags, PF_LOCK_HORIZ);
-                SET(pp->Flags, PF_LOOKING);
+                pp->input.actions |= SB_CENTERVIEW;
             }
         }
 
@@ -6139,7 +6078,7 @@ DoPlayerBeginDie(PLAYERp pp)
     // Get rid of all panel spells that are currently working
     KillAllPanelInv(pp);
 
-    SET(pp->Flags, PF_LOCK_HORIZ);
+    pp->input.actions &= ~SB_CENTERVIEW;
 
     pp->friction = PLAYER_RUN_FRICTION;
     pp->slide_xvect = pp->slide_yvect = 0;
@@ -6402,9 +6341,9 @@ void DoPlayerDeathCheckKeys(PLAYERp pp)
 
         RESET(pp->Flags, PF_WEAPON_DOWN|PF_WEAPON_RETRACT);
         RESET(pp->Flags, PF_DEAD);
-        RESET(pp->Flags, PF_LOCK_HORIZ);
         RESET(sp->cstat, CSTAT_SPRITE_YCENTER);
         SET(sp->cstat, CSTAT_SPRITE_BLOCK|CSTAT_SPRITE_BLOCK_HITSCAN);
+        pp->input.actions |= SB_CENTERVIEW;
         sp->xrepeat = PLAYER_NINJA_XREPEAT;
         sp->yrepeat = PLAYER_NINJA_YREPEAT;
 
@@ -7748,7 +7687,6 @@ void resetinputhelpers(PLAYERp pp)
 {
     pp->horizAdjust = 0;
     pp->angAdjust = 0;
-    pp->pitchAdjust = 0;
 }
 
 void playerAddAngle(PLAYERp pp, double ang)

From 7bf1cacc7f9fe71f24f965f9ac8d63a54ebba832 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Mon, 21 Sep 2020 17:00:07 +1000
Subject: [PATCH 22/41] - Blood/Duke/RR/SW: Unify the player's angle function.

* For Blood/SW, exposes `SB_LOOK_LEFT`/`SB_LOOK_RIGHT` to games, hooking up `q16look_ang` and `q16rotscrnang` within.
* For SW, use Duke & Blood's return to center function and remove `PF_TURN_180` bit.
* For RR, replace a few misused bits with some bools inside of `player_struct`.
* Since bulk of functionality is sourced from Duke (30Hz), apply proper scaling so SW speed matches (40Hz).
---
 source/blood/src/controls.cpp         |  5 +-
 source/blood/src/hudsprites.cpp       | 11 ++--
 source/blood/src/player.cpp           | 40 ++----------
 source/blood/src/player.h             |  4 +-
 source/blood/src/view.cpp             | 20 +++---
 source/blood/src/view.h               |  4 +-
 source/core/gamecontrol.cpp           | 59 +++++++++++++++++-
 source/core/gamecontrol.h             | 12 ++++
 source/games/duke/src/funct.h         |  2 +-
 source/games/duke/src/game_misc.cpp   |  2 +-
 source/games/duke/src/hudweapon_d.cpp |  2 +-
 source/games/duke/src/hudweapon_r.cpp |  2 +-
 source/games/duke/src/inlines.h       |  8 +--
 source/games/duke/src/input.cpp       | 23 ++++---
 source/games/duke/src/player.cpp      | 64 +++----------------
 source/games/duke/src/player_d.cpp    |  3 +-
 source/games/duke/src/player_r.cpp    | 28 +++++----
 source/games/duke/src/savegame.cpp    |  2 -
 source/games/duke/src/types.h         |  2 +-
 source/sw/src/draw.cpp                | 15 +++--
 source/sw/src/game.h                  |  3 +-
 source/sw/src/panel.cpp               |  7 ++-
 source/sw/src/player.cpp              | 90 +++++----------------------
 wadsrc/static/engine/menudef.txt      |  2 +-
 24 files changed, 182 insertions(+), 228 deletions(-)

diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index 15b2c4c27..8df024e9a 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -41,7 +41,7 @@ enum
     MAXHORIZVEL = 32
 };
 
-void applylook(PLAYER *pPlayer, fixed_t const q16avel, double const scaleAdjust);
+void UpdatePlayerSpriteAngle(PLAYER* pPlayer);
 
 //---------------------------------------------------------------------------
 //
@@ -138,7 +138,8 @@ static void processMovement(ControlInfo* const hidInput)
         // Perform unsynchronised angle/horizon if not dead.
         if (gView->pXSprite->health != 0)
         {
-            applylook(pPlayer, input.q16avel, scaleAdjust);
+            applylook(&pPlayer->q16ang, &pPlayer->q16look_ang, &pPlayer->q16rotscrnang, &pPlayer->spin, input.q16avel, &pPlayer->input.actions, scaleAdjust, gView->pXSprite->health == 0, pPlayer->posture != 0);
+            UpdatePlayerSpriteAngle(pPlayer);
             sethorizon(&pPlayer->q16horiz, input.q16horz, &pPlayer->input.actions, scaleAdjust);
         }
 
diff --git a/source/blood/src/hudsprites.cpp b/source/blood/src/hudsprites.cpp
index e969c1323..33aa3a9ae 100644
--- a/source/blood/src/hudsprites.cpp
+++ b/source/blood/src/hudsprites.cpp
@@ -96,14 +96,17 @@ static void viewBurnTime(int gScale)
 }
 
 
-void hudDraw(PLAYER *gView, int nSectnum, double bobx, double boby, double zDelta, int basepal, int smoothratio)
+void hudDraw(PLAYER *gView, VIEW *pView, int nSectnum, double bobx, double boby, double zDelta, int basepal, double smoothratio)
 {
 	if (gViewPos == 0)
 	{
-		DrawCrosshair(kCrosshairTile, gView->pXSprite->health >> 4, 0, 2);
+		double look_anghalf = getHalfLookAng(pView->q16look_ang, gView->q16look_ang, cl_syncinput, smoothratio);
+		double looking_arc = fabs(look_anghalf) / 4.5;
 
-		double cX = 160;
-		double cY = 220;
+		DrawCrosshair(kCrosshairTile, gView->pXSprite->health >> 4, -look_anghalf, 2);
+
+		double cX = 160 - look_anghalf;
+		double cY = 220 + looking_arc;
 		if (cl_weaponsway)
 		{
 			cX += (bobx / 256.);
diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index b277f4b42..098aa8b97 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1303,48 +1303,15 @@ int ActionScan(PLAYER *pPlayer, int *a2, int *a3)
     return -1;
 }
 
-enum
-{
-    PLAYER_HORIZ_MIN = -79,
-    PLAYER_HORIZ_MAX = 219
-};
-
 //---------------------------------------------------------------------------
 //
-// Player's angle function, called in processInput() or from gi->GetInput() as required.
+// Player's sprite angle function, called in ProcessInput() or from gi->GetInput() as required.
 //
 //---------------------------------------------------------------------------
 
-void applylook(PLAYER *pPlayer, fixed_t const q16avel, double const scaleAdjust)
+void UpdatePlayerSpriteAngle(PLAYER *pPlayer)
 {
     spritetype *pSprite = pPlayer->pSprite;
-    InputPacket *pInput = &pPlayer->input;
-
-    if (q16avel)
-    {
-        pPlayer->q16ang = (pPlayer->q16ang + q16avel) & 0x7FFFFFF;
-    }
-
-    if (pInput->actions & SB_TURNAROUND)
-    {
-        if (pPlayer->spin == 0.)
-        {
-            pPlayer->spin = -1024.;
-        }
-        pInput->actions &= ~SB_TURNAROUND;
-    }
-
-    if (pPlayer->spin < 0.)
-    {
-        double const speed = scaleAdjust * (pPlayer->posture == 1 ? 64. : 128.);
-        pPlayer->spin = min(pPlayer->spin + speed, 0.);
-        pPlayer->q16ang += FloatToFixed(speed);
-
-        if (pPlayer->spin > -1.)
-        {
-            pPlayer->spin = 0.;
-        }
-    }
 
     pPlayer->q16ang = (pPlayer->q16ang + IntToFixed(pSprite->ang - pPlayer->angold)) & 0x7FFFFFF;
     pPlayer->angold = pSprite->ang = FixedToInt(pPlayer->q16ang);
@@ -1544,7 +1511,8 @@ void ProcessInput(PLAYER *pPlayer)
 
     if (cl_syncinput)
     {
-        applylook(pPlayer, pInput->q16avel, 1);
+        applylook(&pPlayer->q16ang, &pPlayer->q16look_ang, &pPlayer->q16rotscrnang, &pPlayer->spin, pInput->q16avel, &pInput->actions, 1, gView->pXSprite->health == 0, pPlayer->posture != 0);
+        UpdatePlayerSpriteAngle(pPlayer);
     }
 
     if (!(pInput->actions & SB_JUMP))
diff --git a/source/blood/src/player.h b/source/blood/src/player.h
index 3eb72a34e..531c12d64 100644
--- a/source/blood/src/player.h
+++ b/source/blood/src/player.h
@@ -154,7 +154,7 @@ struct PLAYER
     int                 restTime;
     int                 kickPower;
     int                 laughCount;
-    int                 spin;  // turning around
+    fixed_t             spin;  // turning around
     bool                godMode;
     bool                fallScream;
     bool                cantJump;
@@ -184,6 +184,8 @@ struct PLAYER
     int                 player_par;
     int                 nWaterPal;
     POSTURE             pPosture[kModeMax][kPostureMax];
+    fixed_t             q16look_ang;
+    fixed_t             q16rotscrnang;
 
     // Input helper variables and setters.
     double horizAdjust, angAdjust;
diff --git a/source/blood/src/view.cpp b/source/blood/src/view.cpp
index 443f3f399..be7c50bb3 100644
--- a/source/blood/src/view.cpp
+++ b/source/blood/src/view.cpp
@@ -116,6 +116,8 @@ void viewBackupView(int nPlayer)
     pView->atc = pPlayer->bobWidth;
     pView->at18 = pPlayer->swayHeight;
     pView->at1c = pPlayer->swayWidth;
+    pView->q16look_ang = pPlayer->q16look_ang;
+    pView->q16rotscrnang = pPlayer->q16rotscrnang;
 }
 
 void viewCorrectViewOffsets(int nPlayer, vec3_t const *oldpos)
@@ -631,7 +633,7 @@ void viewDrawScreen(bool sceneonly)
         renderSetAspect(v1, yxaspect);
 
         int cX, cY, cZ, v74, v8c;
-        fixed_t cA, q16horiz, q16slopehoriz;
+        fixed_t cA, q16horiz, q16slopehoriz, q16rotscrnang;
         double zDelta, v4c, v48;
         int nSectnum = gView->pSprite->sectnum;
         if (numplayers > 1 && gView == gMe && gPrediction && gMe->pXSprite->health > 0)
@@ -649,13 +651,15 @@ void viewDrawScreen(bool sceneonly)
 
             if (!cl_syncinput)
             {
-                cA = predict.at30;
+                cA = predict.at30 + predict.q16look_ang;
                 q16horiz = predict.at24;
+                q16rotscrnang = predict.q16rotscrnang;
             }
             else
             {
-                cA = interpolateangfix16(predictOld.at30, predict.at30, gInterpolate);
+                cA = interpolateangfix16(predictOld.at30 + predictOld.q16look_ang, predict.at30 + predict.q16look_ang, gInterpolate);
                 q16horiz = interpolate(predictOld.at24, predict.at24, gInterpolate);
+                q16rotscrnang = interpolateangfix16(predictOld.q16rotscrnang, predict.q16rotscrnang, gInterpolate);
             }
         }
         else
@@ -673,13 +677,15 @@ void viewDrawScreen(bool sceneonly)
 
             if (!cl_syncinput)
             {
-                cA = gView->q16ang;
+                cA = gView->q16ang + gView->q16look_ang;
                 q16horiz = gView->q16horiz;
+                q16rotscrnang = gView->q16rotscrnang;
             }
             else
             {
-                cA = interpolateangfix16(pView->at30, gView->q16ang, gInterpolate);
+                cA = interpolateangfix16(pView->at30 + pView->q16look_ang, gView->q16ang + gView->q16look_ang, gInterpolate);
                 q16horiz = interpolate(pView->at24, gView->q16horiz, gInterpolate);
+                q16rotscrnang = interpolateangfix16(pView->q16rotscrnang, gView->q16rotscrnang, gInterpolate);
             }
         }
 
@@ -727,7 +733,7 @@ void viewDrawScreen(bool sceneonly)
         //int tiltcs, tiltdim;
         uint8_t v4 = powerupCheck(gView, kPwUpCrystalBall) > 0;
 #ifdef USE_OPENGL
-        renderSetRollAngle(0);
+        renderSetRollAngle(FixedToFloat(q16rotscrnang));
 #endif
         if (v78 || bDelirium)
         {
@@ -959,7 +965,7 @@ void viewDrawScreen(bool sceneonly)
             }
         }
 #endif
-        hudDraw(gView, nSectnum, v4c, v48, zDelta, basepal, (int)gInterpolate);
+        hudDraw(gView, &gPrevView[gViewIndex], nSectnum, v4c, v48, zDelta, basepal, gInterpolate);
     }
     UpdateDacs(0, true);    // keep the view palette active only for the actual 3D view and its overlays.
     if (automapMode != am_off)
diff --git a/source/blood/src/view.h b/source/blood/src/view.h
index 0cba77c2a..48de4fd9c 100644
--- a/source/blood/src/view.h
+++ b/source/blood/src/view.h
@@ -66,6 +66,8 @@ struct VIEW {
     char at72; // underwater
     short at73; // sprite flags
     SPRITEHIT at75;
+    fixed_t q16look_ang;
+    fixed_t q16rotscrnang;
 };
 
 extern VIEW gPrevView[kMaxPlayers];
@@ -140,7 +142,7 @@ extern LOCATION gPrevSpriteLoc[kMaxSprites];
 extern int gLastPal;
 extern double gInterpolate;
 
-void hudDraw(PLAYER* gView, int nSectnum, double bobx, double boby, double zDelta, int basepal, int smoothratio);
+void hudDraw(PLAYER* gView, VIEW *pView, int nSectnum, double bobx, double boby, double zDelta, int basepal, double smoothratio);
 void viewInitializePrediction(void);
 void viewUpdatePrediction(InputPacket *pInput);
 void viewCorrectPrediction(void);
diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp
index d7d825104..f7cd115f7 100644
--- a/source/core/gamecontrol.cpp
+++ b/source/core/gamecontrol.cpp
@@ -1490,7 +1490,7 @@ fixed_t getincangleq16(fixed_t a, fixed_t na)
 
 //---------------------------------------------------------------------------
 //
-// Player's horizon function, called in processInput() or from gi->GetInput() as required.
+// Player's horizon function, called from game's ticker or from gi->GetInput() as required.
 //
 //---------------------------------------------------------------------------
 
@@ -1553,3 +1553,60 @@ void sethorizon(fixed_t* q16horiz, fixed_t const q16horz, ESyncBits* actions, do
 	// clamp before returning
 	*q16horiz = clamp(*q16horiz, gi->playerHorizMin(), gi->playerHorizMax());
 }
+
+//---------------------------------------------------------------------------
+//
+// Player's angle function, called from game's ticker or from gi->GetInput() as required.
+//
+//---------------------------------------------------------------------------
+
+void applylook(fixed_t* q16ang, fixed_t* q16look_ang, fixed_t* q16rotscrnang, fixed_t* spin, fixed_t const q16avel, ESyncBits* actions, double const scaleAdjust, bool const dead, bool const crouching)
+{
+	if (!dead)
+	{
+		*q16rotscrnang -= xs_CRoundToInt(scaleAdjust * (*q16rotscrnang * (15. / GameTicRate)));
+		if (abs(*q16rotscrnang) < (FRACUNIT >> 2)) *q16rotscrnang = 0;
+
+		*q16look_ang -= xs_CRoundToInt(scaleAdjust * (*q16look_ang * (7.5 / GameTicRate)));
+		if (abs(*q16look_ang) < (FRACUNIT >> 2)) *q16look_ang = 0;
+
+		if (*actions & SB_LOOK_LEFT)
+		{
+			*q16look_ang -= FloatToFixed(scaleAdjust * (4560. / GameTicRate));
+			*q16rotscrnang += FloatToFixed(scaleAdjust * (720. / GameTicRate));
+		}
+
+		if (*actions & SB_LOOK_RIGHT)
+		{
+			*q16look_ang += FloatToFixed(scaleAdjust * (4560. / GameTicRate));
+			*q16rotscrnang -= FloatToFixed(scaleAdjust * (720. / GameTicRate));
+		}
+
+		if (*actions & SB_TURNAROUND)
+		{
+			if (*spin == 0)
+			{
+				*spin = IntToFixed(-1024);
+			}
+			*actions &= ~SB_TURNAROUND;
+		}
+
+		if (*spin < 0)
+		{
+			fixed_t add = FloatToFixed(scaleAdjust * ((!crouching ? 3840. : 1920.) / GameTicRate));
+			*spin += add;
+			if (*spin > 0)
+			{
+				// Don't overshoot our target. With variable factor this is possible.
+				add -= *spin;
+				*spin = 0;
+			}
+			*q16ang += add;
+		}
+
+		if (q16avel)
+		{
+			*q16ang = (*q16ang + q16avel) & 0x7FFFFFF;
+		}
+	}
+}
diff --git a/source/core/gamecontrol.h b/source/core/gamecontrol.h
index 9b7fdf8b1..58bfac0af 100644
--- a/source/core/gamecontrol.h
+++ b/source/core/gamecontrol.h
@@ -11,6 +11,7 @@
 #include "stats.h"
 #include "i_time.h"
 #include "palentry.h"
+#include "pragmas.h"
 
 extern FString currentGame;
 extern FString LumpFilter;
@@ -67,6 +68,7 @@ int getincangle(int c, int n);
 fixed_t getincangleq16(fixed_t c, fixed_t n);
 
 void sethorizon(fixed_t* q16horiz, fixed_t const q16horz, ESyncBits* actions, double const scaleAdjust);
+void applylook(fixed_t* q16ang, fixed_t* q16look_ang, fixed_t* q16rotscrnang, fixed_t* spin, fixed_t const q16avel, ESyncBits* actions, double const scaleAdjust, bool const dead, bool const crouching);
 
 struct UserConfig
 {
@@ -229,3 +231,13 @@ extern int chatmodeon;
 extern bool sendPause;
 extern int lastTic;
 
+//---------------------------------------------------------------------------
+//
+// Return half player's q16look_ang directly or interpolated as required.
+//
+//---------------------------------------------------------------------------
+
+inline double getHalfLookAng(fixed_t const oq16look_ang, fixed_t const q16look_ang, bool interpolate, double smoothratio)
+{
+	return (!interpolate ? q16look_ang : oq16look_ang + fmulscale16(q16look_ang - oq16look_ang, smoothratio)) * (0.5 / FRACUNIT);
+}
diff --git a/source/games/duke/src/funct.h b/source/games/duke/src/funct.h
index 80affff71..7fd6a94db 100644
--- a/source/games/duke/src/funct.h
+++ b/source/games/duke/src/funct.h
@@ -109,7 +109,7 @@ void footprints(int snum);
 int makepainsounds(int snum, int type);
 void playerCrouch(int snum);
 void playerJump(int snum, int fz, int cz);
-void applylook(int snum, double factor, fixed_t adjustment); 
+void processq16avel(player_struct* p, fixed_t* q16avel); 
 void checklook(int snum, ESyncBits actions);
 void playerCenterView(int snum);
 void playerLookUp(int snum, ESyncBits actions);
diff --git a/source/games/duke/src/game_misc.cpp b/source/games/duke/src/game_misc.cpp
index 229bc2a43..1a61e29e7 100644
--- a/source/games/duke/src/game_misc.cpp
+++ b/source/games/duke/src/game_misc.cpp
@@ -300,7 +300,7 @@ void drawoverlays(double smoothratio)
 
 	if (ps[myconnectindex].newowner == -1 && ud.camerasprite == -1)
 	{
-		DrawCrosshair(TILE_CROSSHAIR, ps[screenpeek].last_extra, -getHalfLookAng(screenpeek, cl_syncinput, smoothratio), isRR() ? 0.5 : 1);
+		DrawCrosshair(TILE_CROSSHAIR, ps[screenpeek].last_extra, -getHalfLookAng(pp->oq16look_ang, pp->q16look_ang, cl_syncinput, smoothratio), isRR() ? 0.5 : 1);
 	}
 
 	if (paused == 2)
diff --git a/source/games/duke/src/hudweapon_d.cpp b/source/games/duke/src/hudweapon_d.cpp
index 506a9e49d..1424a9c1f 100644
--- a/source/games/duke/src/hudweapon_d.cpp
+++ b/source/games/duke/src/hudweapon_d.cpp
@@ -289,7 +289,7 @@ void displayweapon_d(int snum, double smoothratio)
 	o = 0;
 
 	horiz16th = get16thOfHoriz(snum, cl_syncinput, smoothratio);
-	look_anghalf = getHalfLookAng(snum, cl_syncinput, smoothratio);
+	look_anghalf = getHalfLookAng(p->oq16look_ang, p->q16look_ang, cl_syncinput, smoothratio);
 	looking_arc = fabs(look_anghalf) / 4.5;
 	weapon_sway = p->oweapon_sway + fmulscale16(p->weapon_sway - p->oweapon_sway, smoothratio);
 	kickback_pic = p->okickback_pic + fmulscale16(*kb - p->okickback_pic, smoothratio);
diff --git a/source/games/duke/src/hudweapon_r.cpp b/source/games/duke/src/hudweapon_r.cpp
index 9c973ee76..9a89e0bbd 100644
--- a/source/games/duke/src/hudweapon_r.cpp
+++ b/source/games/duke/src/hudweapon_r.cpp
@@ -124,7 +124,7 @@ void displayweapon_r(int snum, double smoothratio)
 
 	o = 0;
 
-	look_anghalf = getHalfLookAng(snum, cl_syncinput, smoothratio);
+	look_anghalf = getHalfLookAng(p->oq16look_ang, p->q16look_ang, cl_syncinput, smoothratio);
 	looking_arc = fabs(look_anghalf) / 4.5;
 	weapon_sway = p->oweapon_sway + fmulscale16((p->weapon_sway - p->oweapon_sway), smoothratio);
 	TiltStatus = !cl_syncinput ? p->TiltStatus : p->oTiltStatus + fmulscale16((p->TiltStatus - p->oTiltStatus), smoothratio);
diff --git a/source/games/duke/src/inlines.h b/source/games/duke/src/inlines.h
index c6d7e428d..85d778fcc 100644
--- a/source/games/duke/src/inlines.h
+++ b/source/games/duke/src/inlines.h
@@ -202,13 +202,7 @@ inline void backupplayer(player_struct* p)
 	backupview(p);
 }
 
-// the weapon display code uses these.
-inline double getHalfLookAng(int snum, bool interpolate, double smoothratio)
-{
-	struct player_struct *p = &ps[snum];
-	return (!interpolate ? p->q16look_ang : p->oq16look_ang + fmulscale16(p->q16look_ang - p->oq16look_ang, smoothratio)) * (0.5 / FRACUNIT);
-}
-
+// the weapon display code uses this.
 inline double get16thOfHoriz(int snum, bool interpolate, double smoothratio)
 {
 	struct player_struct *p = &ps[snum];
diff --git a/source/games/duke/src/input.cpp b/source/games/duke/src/input.cpp
index dbcf20ba9..536f208b7 100644
--- a/source/games/duke/src/input.cpp
+++ b/source/games/duke/src/input.cpp
@@ -477,13 +477,13 @@ void hud_input(int snum)
 			}
 		}
 
-		if (PlayerInput(snum, SB_TURNAROUND) && p->one_eighty_count == 0)
+		if (PlayerInput(snum, SB_TURNAROUND) && p->one_eighty_count == 0 && p->on_crane < 0)
 		{
 			SetGameVarID(g_iReturnVarID, 0, -1, snum);
 			OnEvent(EVENT_TURNAROUND, -1, snum, -1);
-			if (GetGameVarID(g_iReturnVarID, -1, snum) == 0)
+			if (GetGameVarID(g_iReturnVarID, -1, snum) != 0)
 			{
-				p->one_eighty_count = -IntToFixed(1024);
+				sync[snum].actions &= ~SB_TURNAROUND;
 			}
 		}
 	}
@@ -884,15 +884,13 @@ static void processVehicleInput(player_struct *p, ControlInfo* const hidInput, I
 		if (buttonMap.ButtonDown(gamefunc_Move_Forward) || buttonMap.ButtonDown(gamefunc_Strafe))
 			loc.actions |= SB_JUMP;
 		if (buttonMap.ButtonDown(gamefunc_Move_Backward))
-			loc.actions |= SB_AIM_UP;
+			p->vehicle_backwards = true;
 		if (loc.actions & SB_RUN)
 			loc.actions |= SB_CROUCH;
 	}
 
-	if (turnl)
-		loc.actions |= SB_AIM_DOWN;
-	if (turnr)
-		loc.actions |= SB_LOOK_LEFT;
+	if (turnl) p->vehicle_turnl = true;
+	if (turnr) p->vehicle_turnr = true;
 
 	double turnvel;
 
@@ -1024,9 +1022,16 @@ static void GetInputInternal(InputPacket &locInput, ControlInfo* const hidInput)
 	{
 		// Do these in the same order as the old code.
 		calcviewpitch(p, scaleAdjust);
-		applylook(myconnectindex, scaleAdjust, input.q16avel);
+		processq16avel(p, &input.q16avel);
+		applylook(&p->q16ang, &p->q16look_ang, &p->q16rotscrnang, &p->one_eighty_count, input.q16avel, &sync[myconnectindex].actions, scaleAdjust, p->dead_flag != 0, p->crouch_toggle || sync[myconnectindex].actions & SB_CROUCH);
+		apply_seasick(p, scaleAdjust);
 		sethorizon(&p->q16horiz, input.q16horz, &sync[myconnectindex].actions, scaleAdjust);
 
+		if (p->angAdjust)
+		{
+			p->q16ang += FloatToFixed(scaleAdjust * p->angAdjust);
+		}
+
 		if (p->horizAdjust)
         {
             p->q16horiz += FloatToFixed(scaleAdjust * p->horizAdjust);
diff --git a/source/games/duke/src/player.cpp b/source/games/duke/src/player.cpp
index b6dc30f41..169b99f71 100644
--- a/source/games/duke/src/player.cpp
+++ b/source/games/duke/src/player.cpp
@@ -763,7 +763,7 @@ void playerJump(int snum, int fz, int cz)
 
 void apply_seasick(player_struct* p, double factor)
 {
-	if (isRRRA() && p->SeaSick)
+	if (isRRRA() && p->SeaSick && p->dead_flag == 0)
 	{
 		if (p->SeaSick < 250)
 		{
@@ -787,63 +787,17 @@ void apply_seasick(player_struct* p, double factor)
 //
 //---------------------------------------------------------------------------
 
-void applylook(int snum, double factor, fixed_t adjustment)
+void processq16avel(player_struct* p, fixed_t* q16avel)
 {
-	auto p = &ps[snum];
-	fixed_t q16avel;
-
-	if (p->dead_flag == 0)
-	{
-		p->addrotscrnang(factor * -0.5 * FixedToFloat(p->q16rotscrnang));
-		if (abs(p->q16rotscrnang) < FRACUNIT) p->q16rotscrnang = 0;
-
-		p->addlookang(factor * -0.25 * FixedToFloat(p->q16look_ang));
-		if (abs(p->q16look_ang) < FRACUNIT) p->q16look_ang = 0;
-
-		if (p->lookLeft)
-		{
-			p->addlookang(factor * -152);
-			p->addrotscrnang(factor * 24);
-		}
-
-		if (p->lookRight)
-		{
-			p->addlookang(factor * 152);
-			p->addrotscrnang(factor * -24);
-		}
-
-		if (p->one_eighty_count < 0 && p->on_crane < 0)
-		{
-			fixed_t add = FloatToFixed(factor * 128);
-			p->one_eighty_count += add;
-			if (p->one_eighty_count > 0)
-			{
-				// Don't overshoot our target. With variable factor this is possible.
-				add -= p->one_eighty_count;
-				p->one_eighty_count = 0;
-			}
-			p->q16ang += add;
-		}
-		apply_seasick(p, factor);
-	}
-
-	// Add angAdjust if input is unsynchronised.
-	if (!cl_syncinput)
-	{
-		p->q16ang += FloatToFixed(factor * p->angAdjust);
-	}
-
 	// Taken from processinput() for use with applying look while cl_syncinput is 0.
 	if (p->psectlotag == ST_2_UNDERWATER)
 	{
-		q16avel = (adjustment - (adjustment >> 3)) * sgn(TICSPERFRAME);
+		*q16avel = (*q16avel - (*q16avel >> 3)) * sgn(TICSPERFRAME);
 	}
 	else
 	{
-		q16avel = adjustment * sgn(TICSPERFRAME);
+		*q16avel = *q16avel * sgn(TICSPERFRAME);
 	}
-
-	p->q16ang = (p->q16ang + q16avel) & 0x7FFFFFF;
 }
 
 //---------------------------------------------------------------------------
@@ -972,15 +926,13 @@ void checklook(int snum, ESyncBits actions)
 {
 	auto p = &ps[snum];
 
-	p->lookLeft = false;
-	p->lookRight = false;
 	if ((actions & SB_LOOK_LEFT) && !p->OnMotorcycle)
 	{
 		SetGameVarID(g_iReturnVarID, 0, p->i, snum);
 		OnEvent(EVENT_LOOKLEFT, p->i, snum, -1);
-		if (GetGameVarID(g_iReturnVarID, p->i, snum) == 0)
+		if (GetGameVarID(g_iReturnVarID, p->i, snum) != 0)
 		{
-			p->lookLeft = true;
+			actions &= ~SB_LOOK_LEFT;
 		}
 	}
 
@@ -988,9 +940,9 @@ void checklook(int snum, ESyncBits actions)
 	{
 		SetGameVarID(g_iReturnVarID, 0, p->i, snum);
 		OnEvent(EVENT_LOOKRIGHT, p->i, snum, -1);
-		if (GetGameVarID(g_iReturnVarID, p->i, snum) == 0)
+		if (GetGameVarID(g_iReturnVarID, p->i, snum) != 0)
 		{
-			p->lookRight = true;
+			actions &= ~SB_LOOK_RIGHT;
 		}
 	}
 	backuplook(p);
diff --git a/source/games/duke/src/player_d.cpp b/source/games/duke/src/player_d.cpp
index 700ab020f..80d09468b 100644
--- a/source/games/duke/src/player_d.cpp
+++ b/source/games/duke/src/player_d.cpp
@@ -2846,7 +2846,8 @@ void processinput_d(int snum)
 		//ENGINE calculates angvel for you
 		// may still be needed later for demo recording
 
-		applylook(snum, 1, sb_avel);
+		processq16avel(p, &sb_avel);
+		applylook(&p->q16ang, &p->q16look_ang, &p->q16rotscrnang, &p->one_eighty_count, sb_avel, &actions, 1, p->dead_flag != 0, p->crouch_toggle || actions & SB_CROUCH);
 	}
 
 	if (p->spritebridge == 0)
diff --git a/source/games/duke/src/player_r.cpp b/source/games/duke/src/player_r.cpp
index 9b3cd60b6..806c976f3 100644
--- a/source/games/duke/src/player_r.cpp
+++ b/source/games/duke/src/player_r.cpp
@@ -1611,29 +1611,29 @@ static void onMotorcycle(int snum, ESyncBits &actions)
 		if (!S_CheckActorSoundPlaying(pi, 189) && !S_CheckActorSoundPlaying(pi, 187))
 			S_PlayActorSound(187, pi);
 	}
-	if (actions & SB_AIM_UP)
+	if (p->vehicle_backwards)
 	{
 		var6c = 1;
-		actions &= ~SB_AIM_UP;
+		p->vehicle_backwards = false;
 	}
 	else
 		var6c = 0;
-	if (actions & SB_AIM_DOWN)
+	if (p->vehicle_turnl)
 	{
 		var70 = 1;
 		var74 = 1;
-		actions &= ~SB_AIM_DOWN;
+		p->vehicle_turnl = false;
 	}
 	else
 	{
 		var70 = 0;
 		var74 = 0;
 	}
-	if (actions & SB_LOOK_LEFT)
+	if (p->vehicle_turnr)
 	{
 		var78 = 1;
 		var7c = 1;
-		actions &= ~SB_LOOK_LEFT;
+		p->vehicle_turnr = false;
 	}
 	else
 	{
@@ -1914,17 +1914,17 @@ static void onBoat(int snum, ESyncBits &actions)
 	}
 	else
 		varb0 = 0;
-	if (actions & SB_AIM_UP)
+	if (p->vehicle_backwards)
 	{
 		varb4 = 1;
-		actions &= ~SB_AIM_UP;
+		p->vehicle_backwards = false;
 	}
 	else varb4 = 0;
-	if (actions & SB_AIM_DOWN)
+	if (p->vehicle_turnl)
 	{
 		varb8 = 1;
 		varbc = 1;
-		actions &= ~SB_AIM_DOWN;
+		p->vehicle_turnl = false;
 		if (!S_CheckActorSoundPlaying(pi, 91) && p->MotoSpeed > 30 && !p->NotOnWater)
 			S_PlayActorSound(91, pi);
 	}
@@ -1933,11 +1933,11 @@ static void onBoat(int snum, ESyncBits &actions)
 		varb8 = 0;
 		varbc = 0;
 	}
-	if (actions & SB_LOOK_LEFT)
+	if (p->vehicle_turnr)
 	{
 		varc0 = 1;
 		varc4 = 1;
-		actions &= ~SB_LOOK_LEFT;
+		p->vehicle_turnr = false;
 		if (!S_CheckActorSoundPlaying(pi, 91) && p->MotoSpeed > 30 && !p->NotOnWater)
 			S_PlayActorSound(91, pi);
 	}
@@ -3736,7 +3736,9 @@ void processinput_r(int snum)
 		//ENGINE calculates angvel for you
 		// may still be needed later for demo recording
 
-		applylook(snum, 1, sb_avel);
+		processq16avel(p, &sb_avel);
+		applylook(&p->q16ang, &p->q16look_ang, &p->q16rotscrnang, &p->one_eighty_count, sb_avel, &actions, 1, p->dead_flag != 0, p->crouch_toggle || actions & SB_CROUCH);
+		apply_seasick(p, 1);
 	}
 
 	if (p->spritebridge == 0)
diff --git a/source/games/duke/src/savegame.cpp b/source/games/duke/src/savegame.cpp
index 14fc52b17..f0de68fc0 100644
--- a/source/games/duke/src/savegame.cpp
+++ b/source/games/duke/src/savegame.cpp
@@ -298,8 +298,6 @@ FSerializer& Serialize(FSerializer& arc, const char* keyname, player_struct& w,
 		w.ohard_landing = w.hard_landing;
 		w.horizAdjust = 0;
 		w.angAdjust = 0;
-		w.lookLeft = false;
-		w.lookRight = false;
 	}
 	return arc;
 }
diff --git a/source/games/duke/src/types.h b/source/games/duke/src/types.h
index 812af4888..d537685f5 100644
--- a/source/games/duke/src/types.h
+++ b/source/games/duke/src/types.h
@@ -203,12 +203,12 @@ struct player_struct
 	uint8_t hurt_delay2, nocheat;
 	uint8_t OnMotorcycle, OnBoat, moto_underwater, NotOnWater, MotoOnGround;
 	uint8_t moto_do_bump, moto_bump_fast, moto_on_oil, moto_on_mud;
+	bool vehicle_turnl, vehicle_turnr, vehicle_backwards;
 
 	int8_t crouch_toggle;
 
 	// input stuff.
 	double  horizAdjust, angAdjust;
-	bool    lookLeft, lookRight;
 
 
 	// Access helpers for the widened angle and horizon fields.
diff --git a/source/sw/src/draw.cpp b/source/sw/src/draw.cpp
index 6293460e3..94771db49 100644
--- a/source/sw/src/draw.cpp
+++ b/source/sw/src/draw.cpp
@@ -1262,7 +1262,7 @@ void DrawCrosshair(PLAYERp pp)
     if (!(CameraTestMode) && !TEST(pp->Flags, PF_VIEW_FROM_OUTSIDE))
     {
         USERp u = User[pp->PlayerSprite];
-        ::DrawCrosshair(2326, u->Health, 0, 2, shadeToLight(10));
+        ::DrawCrosshair(2326, u->Health, -getHalfLookAng(pp->oq16look_ang, pp->q16look_ang, cl_syncinput, smoothratio), 2, shadeToLight(10));
     }
 }
 
@@ -1611,7 +1611,7 @@ drawscreen(PLAYERp pp, double smoothratio)
 {
     extern bool CameraTestMode;
     int tx, ty, tz;
-    fixed_t tq16horiz, tq16ang;
+    fixed_t tq16horiz, tq16ang, tq16rotscrnang;
     short tsectnum;
     short i,j;
     int bob_amt = 0;
@@ -1654,16 +1654,23 @@ drawscreen(PLAYERp pp, double smoothratio)
     // This isn't needed for the turret as it was fixable, but moving sector objects are problematic.
     if (cl_syncinput || pp != Player+myconnectindex || (!cl_syncinput && pp->sop && !TEST(pp->Flags2, PF2_INPUT_CAN_TURN_TURRET)))
     {
-        tq16ang = camerapp->oq16ang + xs_CRoundToInt(fmulscale16(NORM_Q16ANGLE(camerapp->q16ang + IntToFixed(1024) - camerapp->oq16ang) - IntToFixed(1024), smoothratio));
+        fixed_t dang = IntToFixed(1024);
+        fixed_t oang = camerapp->oq16ang + camerapp->oq16look_ang;
+        fixed_t ang = camerapp->q16ang + camerapp->q16look_ang;
+        tq16ang = oang + xs_CRoundToInt(fmulscale16(NORM_Q16ANGLE(ang + dang - oang) - dang, smoothratio));
         tq16horiz = camerapp->oq16horiz + xs_CRoundToInt(fmulscale16(camerapp->q16horiz - camerapp->oq16horiz, smoothratio));
+        tq16rotscrnang = camerapp->oq16rotscrnang + xs_CRoundToInt(fmulscale16(NORM_Q16ANGLE(camerapp->q16rotscrnang + dang - camerapp->oq16rotscrnang) - dang, smoothratio));
     }
     else
     {
-        tq16ang = pp->q16ang;
+        tq16ang = pp->q16ang + pp->q16look_ang;
         tq16horiz = pp->q16horiz;
+        tq16rotscrnang = pp->q16rotscrnang;
     }
     tsectnum = camerapp->cursectnum;
 
+    renderSetRollAngle(FixedToFloat(tq16rotscrnang));
+
     COVERupdatesector(tx, ty, &tsectnum);
 
     if (tsectnum >= 0)
diff --git a/source/sw/src/game.h b/source/sw/src/game.h
index 63c7c4eea..8fd9ea8a2 100644
--- a/source/sw/src/game.h
+++ b/source/sw/src/game.h
@@ -832,6 +832,7 @@ struct PLAYERstruct
     int
         oposx, oposy, oposz;
     fixed_t oq16horiz, oq16ang;
+    fixed_t oq16look_ang, oq16rotscrnang;
 
     // holds last valid move position
     short lv_sectnum;
@@ -882,6 +883,7 @@ struct PLAYERstruct
     // variables that do not fit into sprite structure
     int hvel,tilt,tilt_dest;
     fixed_t q16horiz, q16horizbase, q16horizoff, q16ang;
+    fixed_t q16look_ang, q16rotscrnang;
     short recoil_amt;
     short recoil_speed;
     short recoil_ndx;
@@ -1042,7 +1044,6 @@ enum
     PF_DIVING                   = (BIT(17)),
     PF_DIVING_IN_LAVA           = (BIT(18)),
     PF_TWO_UZI                  = (BIT(19)),
-    PF_TURN_180                 = (BIT(21)),
     PF_DEAD_HEAD                = (BIT(22)), // are your a dead head
     PF_HEAD_CONTROL             = (BIT(23)), // have control of turning when a head?
     PF_CLIP_CHEAT               = (BIT(24)), // cheat for wall clipping
diff --git a/source/sw/src/panel.cpp b/source/sw/src/panel.cpp
index f9fbf3b61..aa80a0419 100644
--- a/source/sw/src/panel.cpp
+++ b/source/sw/src/panel.cpp
@@ -6921,13 +6921,16 @@ pDisplaySprites(PLAYERp pp, double smoothratio)
     short ang;
     int flags;
 
+    double look_anghalf = getHalfLookAng(pp->oq16look_ang, pp->q16look_ang, cl_syncinput, smoothratio);
+    double looking_arc = fabs(look_anghalf) / 4.5;
+
     TRAVERSE(&pp->PanelSpriteList, psp, next)
     {
         ang = psp->rotate_ang;
         shade = 0;
         flags = 0;
-        x = psp->ox + fmulscale16(psp->x - psp->ox, smoothratio);
-        y = psp->oy + fmulscale16(psp->y - psp->oy, smoothratio);
+        x = (psp->ox + fmulscale16(psp->x - psp->ox, smoothratio)) - look_anghalf;
+        y = (psp->oy + fmulscale16(psp->y - psp->oy, smoothratio)) + looking_arc;
         // initilize pal here - jack with it below
         pal = psp->pal;
 
diff --git a/source/sw/src/player.cpp b/source/sw/src/player.cpp
index 622b3d3ec..ce9721a88 100644
--- a/source/sw/src/player.cpp
+++ b/source/sw/src/player.cpp
@@ -120,12 +120,6 @@ char PlayerGravity = PLAYER_JUMP_GRAV;
 
 extern bool DebugOperate;
 
-enum
-{
-    TURN_SHIFT = 2,
-    HORIZ_SPEED = 14
-};
-
 //unsigned char synctics, lastsynctics;
 
 int ChopTics;
@@ -1505,78 +1499,22 @@ DoPlayerCrawlHeight(PLAYERp pp)
     pp->posz = pp->posz - (DIV4(diff) + DIV8(diff));
 }
 
+void
+UpdatePlayerSpriteAngle(PLAYERp pp)
+{
+    sprite[pp->PlayerSprite].ang = FixedToInt(pp->q16ang);
+
+    if (!Prediction && pp->PlayerUnderSprite >= 0)
+    {
+        sprite[pp->PlayerUnderSprite].ang = FixedToInt(pp->q16ang);
+    }
+}
+
 void
 DoPlayerTurn(PLAYERp pp, fixed_t const q16avel, double const scaleAdjust)
 {
-    if (!TEST(pp->Flags, PF_TURN_180))
-    {
-        if (pp->input.actions & SB_TURNAROUND)
-        {
-            if (pp->KeyPressBits & SB_TURNAROUND)
-            {
-                fixed_t delta_ang;
-
-                pp->KeyPressBits &= ~SB_TURNAROUND;
-
-                pp->turn180_target = pp->q16ang + IntToFixed(1024);
-
-                // make the first turn in the clockwise direction
-                // the rest will follow
-                delta_ang = labs(getincangleq16(pp->q16ang, pp->turn180_target)) >> TURN_SHIFT;
-                pp->q16ang = (pp->q16ang + xs_CRoundToInt(scaleAdjust * delta_ang)) & 0x7FFFFFF;
-
-                SET(pp->Flags, PF_TURN_180);
-            }
-        }
-        else
-        {
-            pp->KeyPressBits |= SB_TURNAROUND;
-        }
-    }
-
-    if (TEST(pp->Flags, PF_TURN_180))
-    {
-        fixed_t delta_ang;
-
-        delta_ang = getincangleq16(pp->q16ang, pp->turn180_target) >> TURN_SHIFT;
-        pp->q16ang = (pp->q16ang + xs_CRoundToInt(scaleAdjust * delta_ang)) & 0x7FFFFFF;
-
-        sprite[pp->PlayerSprite].ang = FixedToInt(pp->q16ang);
-
-        if (!Prediction && pp->PlayerUnderSprite >= 0)
-        {
-            sprite[pp->PlayerUnderSprite].ang = FixedToInt(pp->q16ang);
-        }
-
-        // get new delta to see how close we are
-        delta_ang = getincangleq16(pp->q16ang, pp->turn180_target);
-
-        if (labs(delta_ang) < (IntToFixed(3) << TURN_SHIFT))
-        {
-            pp->q16ang = pp->turn180_target;
-            RESET(pp->Flags, PF_TURN_180);
-        }
-        else
-        {
-            return;
-        }
-    }
-
-    if (q16avel != 0)
-    {
-        pp->q16ang = (pp->q16ang + q16avel) & 0x7FFFFFF;
-
-        // update players sprite angle
-        // NOTE: It's also updated in UpdatePlayerSprite, but needs to be
-        // here to cover
-        // all cases.
-        sprite[pp->PlayerSprite].ang = FixedToInt(pp->q16ang);
-
-        if (!Prediction && pp->PlayerUnderSprite >= 0)
-        {
-            sprite[pp->PlayerUnderSprite].ang = FixedToInt(pp->q16ang);
-        }
-    }
+    applylook(&pp->q16ang, &pp->q16look_ang, &pp->q16rotscrnang, &pp->turn180_target, q16avel, &pp->input.actions, scaleAdjust, TEST(pp->Flags, PF_DEAD), pp->input.actions & (SB_CROUCH|SB_CROUCH_LOCK));
+    UpdatePlayerSpriteAngle(pp);
 }
 
 #if 0
@@ -7022,6 +6960,8 @@ MoveSkipSavePos(void)
         pp->oq16ang = pp->q16ang;
         pp->oq16horiz = pp->q16horiz;
         pp->obob_z = pp->bob_z;
+        pp->oq16look_ang = pp->q16look_ang;
+        pp->oq16rotscrnang = pp->q16rotscrnang;
     }
 
     // save off stats for skip4
diff --git a/wadsrc/static/engine/menudef.txt b/wadsrc/static/engine/menudef.txt
index c50fce455..1ea064533 100644
--- a/wadsrc/static/engine/menudef.txt
+++ b/wadsrc/static/engine/menudef.txt
@@ -520,7 +520,7 @@ OptionMenu "ActionControlsMenu"// protected
 	Control    "$CNTRLMNU_AIMDOWN"         , "+aim_down"
 	Control    "$CNTRLMNU_LOOKUP"           , "+look_up"
 	Control    "$CNTRLMNU_LOOKDOWN"         , "+look_down"
-	ifgame(Duke, Nam, WW2GI, Redneck, RedneckRides)
+	ifnotgame(Exhumed)
 	{
 		Control    "$CNTRLMNU_LOOKLEFT"           , "+look_left"
 		Control    "$CNTRLMNU_LOOKRIGHT"         , "+look_right"

From f806cdcec6288d2546e97ca07cf71f72d844e69b Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Mon, 21 Sep 2020 13:41:16 +1000
Subject: [PATCH 23/41] - Blood/Duke/RR/SW: Unify the player's angle/horizon
 helper functions.

---
 source/blood/src/controls.cpp      |  34 +--------
 source/blood/src/player.cpp        |  72 +-------------------
 source/blood/src/player.h          |   6 +-
 source/core/gamecontrol.cpp        | 103 ++++++++++++++++++++++++++++
 source/core/gamecontrol.h          |   5 ++
 source/games/duke/src/actors.cpp   |  10 +--
 source/games/duke/src/actors_d.cpp |   2 +-
 source/games/duke/src/actors_r.cpp |   2 +-
 source/games/duke/src/funct.h      |   5 --
 source/games/duke/src/gameexec.cpp |   4 +-
 source/games/duke/src/input.cpp    |  11 +--
 source/games/duke/src/player.cpp   |  60 +---------------
 source/games/duke/src/player_r.cpp |  34 ++++-----
 source/games/duke/src/types.h      |   1 +
 source/sw/src/game.h               |   6 +-
 source/sw/src/input.cpp            |  34 +--------
 source/sw/src/player.cpp           | 106 ++++++-----------------------
 source/sw/src/player.h             |   6 --
 source/sw/src/track.cpp            |   2 +-
 19 files changed, 168 insertions(+), 335 deletions(-)

diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index 8df024e9a..005fa9d0c 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -143,39 +143,7 @@ static void processMovement(ControlInfo* const hidInput)
             sethorizon(&pPlayer->q16horiz, input.q16horz, &pPlayer->input.actions, scaleAdjust);
         }
 
-        // Process angle amendments from the game's ticker.
-        if (pPlayer->angTarget)
-        {
-            fixed_t angDelta = getincangleq16(pPlayer->q16ang, pPlayer->angTarget);
-            pPlayer->q16ang = (pPlayer->q16ang + xs_CRoundToInt(scaleAdjust * angDelta));
-
-            if (abs(pPlayer->q16ang - pPlayer->angTarget) < FRACUNIT)
-            {
-                pPlayer->q16ang = pPlayer->angTarget;
-                pPlayer->angTarget = 0;
-            }
-        }
-        else if (pPlayer->angAdjust)
-        {
-            pPlayer->q16ang = (pPlayer->q16ang + FloatToFixed(scaleAdjust * pPlayer->angAdjust)) & 0x7FFFFFF;
-        }
-
-        // Process horizon amendments from the game's ticker.
-        if (pPlayer->horizTarget)
-        {
-            fixed_t horizDelta = pPlayer->horizTarget - pPlayer->q16horiz;
-            pPlayer->q16horiz += xs_CRoundToInt(scaleAdjust * horizDelta);
-
-            if (abs(pPlayer->q16horiz - pPlayer->horizTarget) < FRACUNIT)
-            {
-                pPlayer->q16horiz = pPlayer->horizTarget;
-                pPlayer->horizTarget = 0;
-            }
-        }
-        else if (pPlayer->horizAdjust)
-        {
-            pPlayer->q16horiz += FloatToFixed(scaleAdjust * pPlayer->horizAdjust);
-        }
+        playerProcessHelpers(&pPlayer->q16ang, &pPlayer->angAdjust, &pPlayer->angTarget, &pPlayer->q16horiz, &pPlayer->horizAdjust, &pPlayer->horizTarget, scaleAdjust);
     }
 
     gInput.fvel = clamp(gInput.fvel + input.fvel, -MAXFVEL, MAXFVEL);
diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index 098aa8b97..46fca1854 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1323,78 +1323,12 @@ void UpdatePlayerSpriteAngle(PLAYER *pPlayer)
 //
 //---------------------------------------------------------------------------
 
-void resetinputhelpers(PLAYER* pPlayer)
+static void resetinputhelpers(PLAYER* pPlayer)
 {
     pPlayer->horizAdjust = 0;
     pPlayer->angAdjust = 0;
 }
 
-void playerAddAngle(PLAYER* pPlayer, double ang)
-{
-    if (!cl_syncinput)
-    {
-        pPlayer->angAdjust += ang;
-    }
-    else
-    {
-        pPlayer->addang(ang);
-    }
-}
-
-void playerSetAngle(PLAYER* pPlayer, double ang)
-{
-    if (!cl_syncinput)
-    {
-        // Cancel out any angle adjustments as we're setting angle now.
-        pPlayer->angAdjust = 0;
-
-        // Add slight offset if input angle is coming in as absolute 0.
-        if (ang == 0)
-        {
-            ang += 0.1;
-        }
-
-        pPlayer->angTarget = pPlayer->q16ang + getincangleq16(pPlayer->q16ang, FloatToFixed(ang));
-    }
-    else
-    {
-        pPlayer->setang(ang);
-    }
-}
-
-void playerAddHoriz(PLAYER* pPlayer, double horiz)
-{
-    if (!cl_syncinput)
-    {
-        pPlayer->horizAdjust += horiz;
-    }
-    else
-    {
-        pPlayer->addhoriz(horiz);
-    }
-}
-
-void playerSetHoriz(PLAYER* pPlayer, double horiz)
-{
-    if (!cl_syncinput)
-    {
-        // Cancel out any horizon adjustments as we're setting horizon now.
-        pPlayer->horizAdjust = 0;
-
-        // Add slight offset if input horizon is coming in as absolute 0.
-        if (horiz == 0)
-        {
-            horiz += 0.1;
-        }
-
-        pPlayer->horizTarget = FloatToFixed(horiz);
-    }
-    else
-    {
-        pPlayer->sethoriz(horiz);
-    }
-}
-
 void ProcessInput(PLAYER *pPlayer)
 {
     enum
@@ -1425,11 +1359,11 @@ void ProcessInput(PLAYER *pPlayer)
         if (pPlayer->fraggerId != -1)
         {
             pPlayer->angold = pSprite->ang = getangle(sprite[pPlayer->fraggerId].x - pSprite->x, sprite[pPlayer->fraggerId].y - pSprite->y);
-            playerSetAngle(pPlayer, pSprite->ang);
+            playerSetAngle(&pPlayer->q16ang, &pPlayer->angTarget, pSprite->ang);
         }
         pPlayer->deathTime += 4;
         if (!bSeqStat)
-            playerSetHoriz(pPlayer, FixedToFloat(mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime<<3, 1024))>>15), IntToFixed(120))));
+            playerSetHoriz(&pPlayer->q16horiz, &pPlayer->horizTarget, FixedToFloat(mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime<<3, 1024))>>15), IntToFixed(120))));
         if (pPlayer->curWeapon)
             pInput->setNewWeapon(pPlayer->curWeapon);
         if (pInput->actions & SB_OPEN)
diff --git a/source/blood/src/player.h b/source/blood/src/player.h
index 531c12d64..d9d558788 100644
--- a/source/blood/src/player.h
+++ b/source/blood/src/player.h
@@ -187,13 +187,9 @@ struct PLAYER
     fixed_t             q16look_ang;
     fixed_t             q16rotscrnang;
 
-    // Input helper variables and setters.
+    // Input helper variables.
     double horizAdjust, angAdjust;
     fixed_t horizTarget, angTarget;
-    void addang(int v) { q16ang = (q16ang + IntToFixed(v)) & 0x7FFFFFF; }
-    void setang(int v) { q16ang = IntToFixed(v); }
-    void addhoriz(int v) { q16horiz += (IntToFixed(v)); }
-    void sethoriz(int v) { q16horiz = IntToFixed(v); }    
 };
 
 struct PROFILE
diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp
index f7cd115f7..cb838ada8 100644
--- a/source/core/gamecontrol.cpp
+++ b/source/core/gamecontrol.cpp
@@ -1610,3 +1610,106 @@ void applylook(fixed_t* q16ang, fixed_t* q16look_ang, fixed_t* q16rotscrnang, fi
 		}
 	}
 }
+
+//---------------------------------------------------------------------------
+//
+// Player's ticrate helper functions.
+//
+//---------------------------------------------------------------------------
+
+void playerAddAngle(fixed_t* q16ang, double* helper, double adjustment)
+{
+	if (!cl_syncinput)
+	{
+		*helper += adjustment;
+	}
+	else
+	{
+		*q16ang = (*q16ang + FloatToFixed(adjustment)) & 0x7FFFFFF;
+	}
+}
+
+void playerSetAngle(fixed_t* q16ang, fixed_t* helper, double adjustment)
+{
+	if (!cl_syncinput)
+	{
+		// Add slight offset if adjustment is coming in as absolute 0.
+		if (adjustment == 0) adjustment += (1. / FRACUNIT);
+
+		*helper = *q16ang + getincangleq16(*q16ang, FloatToFixed(adjustment));
+	}
+	else
+	{
+		*q16ang = FloatToFixed(adjustment);
+	}
+}
+
+void playerAddHoriz(fixed_t* q16horiz, double* helper, double adjustment)
+{
+	if (!cl_syncinput)
+	{
+		*helper += adjustment;
+	}
+	else
+	{
+		*q16horiz += FloatToFixed(adjustment);
+	}
+}
+
+void playerSetHoriz(fixed_t* q16horiz, fixed_t* helper, double adjustment)
+{
+	if (!cl_syncinput)
+	{
+		// Add slight offset if adjustment is coming in as absolute 0.
+		if (adjustment == 0) adjustment += (1. / FRACUNIT);
+
+		*helper = FloatToFixed(adjustment);
+	}
+	else
+	{
+		*q16horiz = FloatToFixed(adjustment);
+	}
+}
+
+//---------------------------------------------------------------------------
+//
+// Player's ticrate helper processor.
+//
+//---------------------------------------------------------------------------
+
+void playerProcessHelpers(fixed_t* q16ang, double* angAdjust, fixed_t* angTarget, fixed_t* q16horiz, double* horizAdjust, fixed_t* horizTarget, double const scaleAdjust)
+{
+	// Process angle amendments from the game's ticker.
+	if (*angTarget)
+	{
+		fixed_t angDelta = getincangleq16(*q16ang, *angTarget);
+		*q16ang = (*q16ang + xs_CRoundToInt(scaleAdjust * angDelta));
+
+		if (abs(*q16ang - *angTarget) < FRACUNIT)
+		{
+			*q16ang = *angTarget;
+			*angTarget = 0;
+		}
+	}
+	else if (*angAdjust)
+	{
+		*q16ang = (*q16ang + FloatToFixed(scaleAdjust * *angAdjust)) & 0x7FFFFFF;
+	}
+
+	// Process horizon amendments from the game's ticker.
+	if (*horizTarget)
+	{
+		fixed_t horizDelta = *horizTarget - *q16horiz;
+		*q16horiz += xs_CRoundToInt(scaleAdjust * horizDelta);
+
+		if (abs(*q16horiz - *horizTarget) < FRACUNIT)
+		{
+			*q16horiz = *horizTarget;
+			*horizTarget = 0;
+		}
+	}
+	else if (*horizAdjust)
+	{
+		*q16horiz += FloatToFixed(scaleAdjust * *horizAdjust);
+	}
+}
diff --git a/source/core/gamecontrol.h b/source/core/gamecontrol.h
index 58bfac0af..4939875e3 100644
--- a/source/core/gamecontrol.h
+++ b/source/core/gamecontrol.h
@@ -69,6 +69,11 @@ fixed_t getincangleq16(fixed_t c, fixed_t n);
 
 void sethorizon(fixed_t* q16horiz, fixed_t const q16horz, ESyncBits* actions, double const scaleAdjust);
 void applylook(fixed_t* q16ang, fixed_t* q16look_ang, fixed_t* q16rotscrnang, fixed_t* spin, fixed_t const q16avel, ESyncBits* actions, double const scaleAdjust, bool const dead, bool const crouching);
+void playerAddAngle(fixed_t* q16ang, double* helper, double adjustment);
+void playerSetAngle(fixed_t* q16ang, fixed_t* helper, double adjustment);
+void playerAddHoriz(fixed_t* q16horiz, double* helper, double adjustment);
+void playerSetHoriz(fixed_t* q16horiz, fixed_t* helper, double adjustment);
+void playerProcessHelpers(fixed_t* q16ang, double* angAdjust, fixed_t* angTarget, fixed_t* q16horiz, double* horizAdjust, fixed_t* horizTarget, double const scaleAdjust);
 
 struct UserConfig
 {
diff --git a/source/games/duke/src/actors.cpp b/source/games/duke/src/actors.cpp
index 058943ea7..03d9c44b1 100644
--- a/source/games/duke/src/actors.cpp
+++ b/source/games/duke/src/actors.cpp
@@ -468,7 +468,7 @@ void moveplayers(void) //Players
 
 				if (p->actorsqu >= 0)
 				{
-					playerAddAngle(p, getincangle(p->getang(), getangle(sprite[p->actorsqu].x - p->posx, sprite[p->actorsqu].y - p->posy)) >> 2);
+					playerAddAngle(&p->q16ang, &p->angAdjust, getincangle(p->getang(), getangle(sprite[p->actorsqu].x - p->posx, sprite[p->actorsqu].y - p->posy)) >> 2);
 				}
 
 				if (s->extra > 0)
@@ -491,7 +491,7 @@ void moveplayers(void) //Players
 
 					if (p->wackedbyactor >= 0 && sprite[p->wackedbyactor].statnum < MAXSTATUS)
 					{
-						playerAddAngle(p, getincangle(p->getang(), getangle(sprite[p->wackedbyactor].x - p->posx, sprite[p->wackedbyactor].y - p->posy)) >> 1);
+						playerAddAngle(&p->q16ang, &p->angAdjust, getincangle(p->getang(), getangle(sprite[p->wackedbyactor].x - p->posx, sprite[p->wackedbyactor].y - p->posy)) >> 1);
 					}
 				}
 				s->ang = p->getang();
@@ -748,7 +748,7 @@ void movecrane(int i, int crane)
 				s->owner = -2;
 				ps[p].on_crane = i;
 				S_PlayActorSound(isRR() ? 390 : DUKE_GRUNT, ps[p].i);
-				playerSetAngle(&ps[p], s->ang + 1024);
+				playerSetAngle(&ps[p].q16ang, &ps[p].angTarget, s->ang + 1024);
 			}
 			else
 			{
@@ -2674,7 +2674,7 @@ void handle_se00(int i, int LASERLINE)
 		{
 			if (ps[p].cursectnum == s->sectnum && ps[p].on_ground == 1)
 			{
-				playerAddAngle(&ps[p], l * q);
+				playerAddAngle(&ps[p].q16ang, &ps[p].angAdjust, l * q);
 
 				ps[p].posz += zchange;
 
@@ -2866,7 +2866,7 @@ void handle_se14(int i, bool checkstat, int RPG, int JIBS6)
 					ps[p].bobposx += m;
 					ps[p].bobposy += x;
 
-					playerAddAngle(&ps[p], q);
+					playerAddAngle(&ps[p].q16ang, &ps[p].angAdjust, q);
 
 					if (numplayers > 1)
 					{
diff --git a/source/games/duke/src/actors_d.cpp b/source/games/duke/src/actors_d.cpp
index c06230e9a..f13d10cf3 100644
--- a/source/games/duke/src/actors_d.cpp
+++ b/source/games/duke/src/actors_d.cpp
@@ -1852,7 +1852,7 @@ void moveweapons_d(void)
 
 						if (s->picnum == SPIT)
 						{
-							playerAddHoriz(&ps[p], 32);
+							playerAddHoriz(&ps[p].q16horiz, &ps[p].horizAdjust, 32);
 							sync[p].actions |= SB_CENTERVIEW;
 
 							if (ps[p].loogcnt == 0)
diff --git a/source/games/duke/src/actors_r.cpp b/source/games/duke/src/actors_r.cpp
index 61f517ba7..79f60ecf0 100644
--- a/source/games/duke/src/actors_r.cpp
+++ b/source/games/duke/src/actors_r.cpp
@@ -1399,7 +1399,7 @@ void moveweapons_r(void)
 								guts_r(s, RABBITJIBC, 2, myconnectindex);
 							}
 
-							playerAddHoriz(&ps[p], 32);
+							playerAddHoriz(&ps[p].q16horiz, &ps[p].horizAdjust, 32);
 							sync[p].actions |= SB_CENTERVIEW;
 
 							if (ps[p].loogcnt == 0)
diff --git a/source/games/duke/src/funct.h b/source/games/duke/src/funct.h
index 7fd6a94db..eae5f08e3 100644
--- a/source/games/duke/src/funct.h
+++ b/source/games/duke/src/funct.h
@@ -248,9 +248,4 @@ void resetinputhelpers(player_struct* p);
 void checkhardlanding(player_struct* p);
 void playerweaponsway(player_struct* p, spritetype* s);
 
-void playerAddAngle(player_struct* p, int ang);
-void playerSetAngle(player_struct* p, int ang);
-void playerAddHoriz(player_struct* p, int horiz);
-void playerSetHoriz(player_struct* p, int horiz);
-
 END_DUKE_NS
diff --git a/source/games/duke/src/gameexec.cpp b/source/games/duke/src/gameexec.cpp
index c2b583c04..77b62973e 100644
--- a/source/games/duke/src/gameexec.cpp
+++ b/source/games/duke/src/gameexec.cpp
@@ -330,7 +330,7 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, int sActor, int sPl
 		break;
 
 	case PLAYER_HORIZ:
-		if (bSet) playerSetHoriz(&ps[iPlayer], lValue);
+		if (bSet) playerSetHoriz(&ps[iPlayer].q16horiz, &ps[iPlayer].horizTarget, lValue);
 		else SetGameVarID((int)lVar2, FixedToInt(ps[iPlayer].q16horiz), sActor, sPlayer);
 		break;
 
@@ -452,7 +452,7 @@ void DoPlayer(bool bSet, int lVar1, int lLabelID, int lVar2, int sActor, int sPl
 		break;
 
 	case PLAYER_ANG:
-		if (bSet) playerSetAngle(&ps[iPlayer], lValue);
+		if (bSet) playerSetAngle(&ps[iPlayer].q16ang, &ps[iPlayer].angTarget, lValue);
 		else SetGameVarID((int)lVar2, ps[iPlayer].getang(), sActor, sPlayer);
 		break;
 
diff --git a/source/games/duke/src/input.cpp b/source/games/duke/src/input.cpp
index 536f208b7..02c57ca7b 100644
--- a/source/games/duke/src/input.cpp
+++ b/source/games/duke/src/input.cpp
@@ -1026,16 +1026,7 @@ static void GetInputInternal(InputPacket &locInput, ControlInfo* const hidInput)
 		applylook(&p->q16ang, &p->q16look_ang, &p->q16rotscrnang, &p->one_eighty_count, input.q16avel, &sync[myconnectindex].actions, scaleAdjust, p->dead_flag != 0, p->crouch_toggle || sync[myconnectindex].actions & SB_CROUCH);
 		apply_seasick(p, scaleAdjust);
 		sethorizon(&p->q16horiz, input.q16horz, &sync[myconnectindex].actions, scaleAdjust);
-
-		if (p->angAdjust)
-		{
-			p->q16ang += FloatToFixed(scaleAdjust * p->angAdjust);
-		}
-
-		if (p->horizAdjust)
-        {
-            p->q16horiz += FloatToFixed(scaleAdjust * p->horizAdjust);
-        }
+		playerProcessHelpers(&p->q16ang, &p->angAdjust, &p->angTarget, &p->q16horiz, &p->horizAdjust, &p->horizTarget, scaleAdjust);
 	}
 }
 
diff --git a/source/games/duke/src/player.cpp b/source/games/duke/src/player.cpp
index 169b99f71..3741ea8bc 100644
--- a/source/games/duke/src/player.cpp
+++ b/source/games/duke/src/player.cpp
@@ -164,7 +164,7 @@ void forceplayerangle(int snum)
 
 	n = 128 - (krand() & 255);
 
-	playerAddHoriz(p, 64);
+	playerAddHoriz(&p->q16horiz, &p->horizAdjust, 64);
 	sync[snum].actions |= SB_CENTERVIEW;
 	p->setlookang(n >> 1);
 	p->setrotscrnang(n >> 1);
@@ -406,7 +406,7 @@ void dokneeattack(int snum, int pi, const std::initializer_list<int> & respawnli
 	if (p->knee_incs > 0)
 	{
 		p->knee_incs++;
-		playerAddHoriz(p, -48);
+		playerAddHoriz(&p->q16horiz, &p->horizAdjust, -48);
 		sync[snum].actions |= SB_CENTERVIEW;
 		if (p->knee_incs > 15)
 		{
@@ -887,7 +887,7 @@ void checkhardlanding(player_struct* p)
 {
 	if (p->hard_landing > 0)
 	{
-		playerAddHoriz(p, -(p->hard_landing << 4));
+		playerAddHoriz(&p->q16horiz, &p->horizAdjust, -(p->hard_landing << 4));
 		p->hard_landing--;
 	}
 }
@@ -1021,60 +1021,6 @@ void playerAimDown(int snum, ESyncBits actions)
 	}
 }
 
-//---------------------------------------------------------------------------
-//
-//
-//
-//---------------------------------------------------------------------------
-
-void playerAddAngle(player_struct* p, int ang)
-{
-	if (!cl_syncinput)
-	{
-		p->angAdjust += ang;
-	}
-	else
-	{
-		p->addang(ang);
-	}
-}
-
-void playerSetAngle(player_struct* p, int ang)
-{
-	if (!cl_syncinput)
-	{
-		p->angAdjust += -1. * ((p->q16ang / 65536.) - ang);
-	}
-	else
-	{
-		p->setang(ang);
-	}
-}
-
-void playerAddHoriz(player_struct* p, int horiz)
-{
-	if (!cl_syncinput)
-	{
-		p->horizAdjust += horiz;
-	}
-	else
-	{
-		p->addhoriz(horiz);
-	}
-}
-
-void playerSetHoriz(player_struct* p, int horiz)
-{
-	if (!cl_syncinput)
-	{
-		p->horizAdjust += -1. * ((p->q16horiz / 65536.) - horiz);
-	}
-	else
-	{
-		p->sethoriz(horiz);
-	}
-}
-
 //---------------------------------------------------------------------------
 //
 // split out so that the weapon check can be done right.
diff --git a/source/games/duke/src/player_r.cpp b/source/games/duke/src/player_r.cpp
index 806c976f3..26d231e24 100644
--- a/source/games/duke/src/player_r.cpp
+++ b/source/games/duke/src/player_r.cpp
@@ -1733,7 +1733,7 @@ static void onMotorcycle(int snum, ESyncBits &actions)
 			p->TurbCount--;
 			p->moto_drink = (krand() & 3) - 2;
 		}
-		playerSetHoriz(p, horiz);
+		playerSetHoriz(&p->q16horiz, &p->horizTarget, horiz);
 	}
 	else if (p->VBumpTarget > p->VBumpNow)
 	{
@@ -1743,7 +1743,7 @@ static void onMotorcycle(int snum, ESyncBits &actions)
 			p->VBumpNow++;
 		if (p->VBumpTarget < p->VBumpNow)
 			p->VBumpNow = p->VBumpTarget;
-		playerSetHoriz(p, 100 + p->VBumpNow / 3);
+		playerSetHoriz(&p->q16horiz, &p->horizTarget, 100 + p->VBumpNow / 3);
 	}
 	else if (p->VBumpTarget < p->VBumpNow)
 	{
@@ -1753,7 +1753,7 @@ static void onMotorcycle(int snum, ESyncBits &actions)
 			p->VBumpNow--;
 		if (p->VBumpTarget > p->VBumpNow)
 			p->VBumpNow = p->VBumpTarget;
-		playerSetHoriz(p, 100 + p->VBumpNow / 3);
+		playerSetHoriz(&p->q16horiz, &p->horizTarget, 100 + p->VBumpNow / 3);
 	}
 	else
 	{
@@ -1812,7 +1812,7 @@ static void onMotorcycle(int snum, ESyncBits &actions)
 				ang = var98 >> 7;
 			}
 		}
-		playerSetAngle(p, (var90 - ang) & 2047);
+		playerSetAngle(&p->q16ang, &p->angTarget, (var90 - ang) & 2047);
 	}
 	else if (p->MotoSpeed >= 20 && p->on_ground == 1 && (p->moto_on_mud || p->moto_on_oil))
 	{
@@ -2057,7 +2057,7 @@ static void onBoat(int snum, ESyncBits &actions)
 			p->TurbCount--;
 			p->moto_drink = (krand() & 3) - 2;
 		}
-		playerSetHoriz(p, horiz);
+		playerSetHoriz(&p->q16horiz, &p->horizTarget, horiz);
 	}
 	else if (p->VBumpTarget > p->VBumpNow)
 	{
@@ -2067,7 +2067,7 @@ static void onBoat(int snum, ESyncBits &actions)
 			p->VBumpNow++;
 		if (p->VBumpTarget < p->VBumpNow)
 			p->VBumpNow = p->VBumpTarget;
-		playerSetHoriz(p, 100 + p->VBumpNow / 3);
+		playerSetHoriz(&p->q16horiz, &p->horizTarget, 100 + p->VBumpNow / 3);
 	}
 	else if (p->VBumpTarget < p->VBumpNow)
 	{
@@ -2077,7 +2077,7 @@ static void onBoat(int snum, ESyncBits &actions)
 			p->VBumpNow--;
 		if (p->VBumpTarget > p->VBumpNow)
 			p->VBumpNow = p->VBumpTarget;
-		playerSetHoriz(p, 100 + p->VBumpNow / 3);
+		playerSetHoriz(&p->q16horiz, &p->horizTarget, 100 + p->VBumpNow / 3);
 	}
 	else
 	{
@@ -2111,7 +2111,7 @@ static void onBoat(int snum, ESyncBits &actions)
 			p->posyv += (vard4 >> 7) * (sintable[(vardc * -51 + vard8) & 2047] << 4);
 			ang = vare0 >> 6;
 		}
-		playerSetAngle(p, (vard8 - ang) & 2047);
+		playerSetAngle(&p->q16ang, &p->angTarget, (vard8 - ang) & 2047);
 	}
 	if (p->NotOnWater)
 		if (p->MotoSpeed > 50)
@@ -2455,7 +2455,7 @@ void onMotorcycleMove(int snum, int psect, int j)
 		ang = -(p->MotoSpeed >> 1);
 		break;
 	}
-	playerAddAngle(p, ang);
+	playerAddAngle(&p->q16ang, &p->angAdjust, ang);
 	if (var10c >= 441 && var10c <= 581)
 	{
 		var104 = (p->MotoSpeed * p->MotoSpeed) >> 8;
@@ -2522,7 +2522,7 @@ void onBoatMove(int snum, int psect, int j)
 		ang = -(p->MotoSpeed >> 2);
 		break;
 	}
-	playerAddAngle(p, ang);
+	playerAddAngle(&p->q16ang, &p->angAdjust, ang);
 	if (var118 >= 441 && var118 <= 581)
 	{
 		p->MotoSpeed = ((p->MotoSpeed >> 1) + (p->MotoSpeed >> 2)) >> 2;
@@ -3056,7 +3056,7 @@ static void operateweapon(int snum, ESyncBits actions, int psect)
 	case RIFLEGUN_WEAPON:
 
 		p->kickback_pic++;
-		playerAddHoriz(p, 1);
+		playerAddHoriz(&p->q16horiz, &p->horizAdjust, 1);
 		p->recoil++;
 
 		if (p->kickback_pic <= 12)
@@ -3146,11 +3146,11 @@ static void operateweapon(int snum, ESyncBits actions, int psect)
 		}
 		if (p->kickback_pic == 2)
 		{
-			playerAddAngle(p, 16);
+			playerAddAngle(&p->q16ang, &p->angAdjust, 16);
 		}
 		else if (p->kickback_pic == 4)
 		{
-			playerAddAngle(p, -16);
+			playerAddAngle(&p->q16ang, &p->angAdjust, -16);
 		}
 		if (p->kickback_pic > 4)
 			p->kickback_pic = 1;
@@ -3176,11 +3176,11 @@ static void operateweapon(int snum, ESyncBits actions, int psect)
 		}
 		if (p->kickback_pic == 2)
 		{
-			playerAddAngle(p, 4);
+			playerAddAngle(&p->q16ang, &p->angAdjust, 4);
 		}
 		else if (p->kickback_pic == 4)
 		{
-			playerAddAngle(p, -4);
+			playerAddAngle(&p->q16ang, &p->angAdjust, -4);
 		}
 		if (p->kickback_pic > 4)
 			p->kickback_pic = 1;
@@ -3228,7 +3228,7 @@ static void operateweapon(int snum, ESyncBits actions, int psect)
 		{
 			p->posxv -= sintable[(p->getang() + 512) & 2047] << 4;
 			p->posyv -= sintable[p->getang() & 2047] << 4;
-			playerAddHoriz(p, 20);
+			playerAddHoriz(&p->q16horiz, &p->horizAdjust, 20);
 			p->recoil += 20;
 		}
 		if (p->kickback_pic > 20)
@@ -4088,7 +4088,7 @@ HORIZONLY:
 		if (!d)
 			d = 1;
 		p->recoil -= d;
-		playerAddHoriz(p, -d);
+		playerAddHoriz(&p->q16horiz, &p->horizAdjust, -d);
 	}
 
 	if (cl_syncinput)
diff --git a/source/games/duke/src/types.h b/source/games/duke/src/types.h
index d537685f5..3e4cd1e40 100644
--- a/source/games/duke/src/types.h
+++ b/source/games/duke/src/types.h
@@ -209,6 +209,7 @@ struct player_struct
 
 	// input stuff.
 	double  horizAdjust, angAdjust;
+	fixed_t horizTarget, angTarget;
 
 
 	// Access helpers for the widened angle and horizon fields.
diff --git a/source/sw/src/game.h b/source/sw/src/game.h
index 8fd9ea8a2..e297550c7 100644
--- a/source/sw/src/game.h
+++ b/source/sw/src/game.h
@@ -1009,13 +1009,9 @@ struct PLAYERstruct
 
     char WpnReloadState;
 
-    // Input helper variables and setters.
+    // Input helper variables.
     double horizAdjust, angAdjust;
     fixed_t horizTarget, angTarget;
-    void addang(int v) { q16ang = (q16ang + IntToFixed(v)) & 0x7FFFFFF; }
-    void setang(int v) { q16ang = IntToFixed(v); }
-    void addhoriz(int v) { q16horiz += (IntToFixed(v)); }
-    void sethoriz(int v) { q16horiz = IntToFixed(v); }
 };
 
 extern PLAYER Player[MAX_SW_PLAYERS_REG+1];
diff --git a/source/sw/src/input.cpp b/source/sw/src/input.cpp
index a1a70d553..9034e77a6 100644
--- a/source/sw/src/input.cpp
+++ b/source/sw/src/input.cpp
@@ -291,43 +291,11 @@ static void processMovement(PLAYERp const pp, ControlInfo* const hidInput, bool
             DoPlayerHorizon(pp, q16horz, scaleAdjust);
         }
 
-        if (pp->horizTarget)
-        {
-            fixed_t horizDelta = pp->horizTarget - pp->q16horiz;
-            pp->q16horiz += xs_CRoundToInt(scaleAdjust * horizDelta);
-
-            if (abs(pp->q16horiz - pp->horizTarget) < FRACUNIT)
-            {
-                pp->q16horiz = pp->horizTarget;
-                pp->horizTarget = 0;
-            }
-        }
-        else if (pp->horizAdjust)
-        {
-            pp->q16horiz += FloatToFixed(scaleAdjust * pp->horizAdjust);
-        }
-
         if (TEST(pp->Flags2, PF2_INPUT_CAN_TURN_GENERAL))
         {
             DoPlayerTurn(pp, q16avel, scaleAdjust);
         }
 
-        if (pp->angTarget)
-        {
-            fixed_t angDelta = getincangleq16(pp->q16ang, pp->angTarget);
-            pp->q16ang = (pp->q16ang + xs_CRoundToInt(scaleAdjust * angDelta));
-
-            if (abs(pp->q16ang - pp->angTarget) < FRACUNIT)
-            {
-                pp->q16ang = pp->angTarget;
-                pp->angTarget = 0;
-            }
-        }
-        else if (pp->angAdjust)
-        {
-            pp->q16ang = (pp->q16ang + FloatToFixed(scaleAdjust * pp->angAdjust)) & 0x7FFFFFF;
-        }
-
         if (TEST(pp->Flags2, PF2_INPUT_CAN_TURN_VEHICLE))
         {
             DoPlayerTurnVehicle(pp, q16avel, pp->posz + Z(10), labs(pp->posz + Z(10) - pp->sop->floor_loz));
@@ -337,6 +305,8 @@ static void processMovement(PLAYERp const pp, ControlInfo* const hidInput, bool
         {
             DoPlayerTurnTurret(pp, q16avel);
         }
+
+        playerProcessHelpers(&pp->q16ang, &pp->angAdjust, &pp->angTarget, &pp->q16horiz, &pp->horizAdjust, &pp->horizTarget, scaleAdjust);
     }
 
     loc.fvel = clamp(loc.fvel + fvel, -MAXFVEL, MAXFVEL);
diff --git a/source/sw/src/player.cpp b/source/sw/src/player.cpp
index ce9721a88..50f05604e 100644
--- a/source/sw/src/player.cpp
+++ b/source/sw/src/player.cpp
@@ -1059,6 +1059,18 @@ STATEp sg_PlayerNinjaFly[] =
 
 /////////////////////////////////////////////////////////////////////////////
 
+//---------------------------------------------------------------------------
+//
+// Unsynchronised input helper.
+//
+//---------------------------------------------------------------------------
+
+static void resetinputhelpers(PLAYERp pp)
+{
+    pp->horizAdjust = 0;
+    pp->angAdjust = 0;
+}
+
 void
 DoPlayerSpriteThrow(PLAYERp pp)
 {
@@ -3657,7 +3669,7 @@ DoPlayerClimb(PLAYERp pp)
             pp->lx = lsp->x + nx * 5;
             pp->ly = lsp->y + ny * 5;
 
-            playerSetAngle(pp, pp->LadderAngle);
+            playerSetAngle(&pp->q16ang, &pp->angTarget, pp->LadderAngle);
         }
     }
 }
@@ -4116,7 +4128,7 @@ PlayerOnLadder(PLAYERp pp)
     pp->lx = lsp->x + nx * 5;
     pp->ly = lsp->y + ny * 5;
 
-    playerSetAngle(pp, pp->LadderAngle);
+    playerSetAngle(&pp->q16ang, &pp->angTarget, pp->LadderAngle);
 
     return true;
 }
@@ -5355,7 +5367,7 @@ DoPlayerBeginOperate(PLAYERp pp)
     pp->sop = pp->sop_control = sop;
     sop->controller = pp->SpriteP;
 
-    playerSetAngle(pp, sop->ang);
+    playerSetAngle(&pp->q16ang, &pp->angTarget, sop->ang);
     pp->posx = sop->xmid;
     pp->posy = sop->ymid;
     COVERupdatesector(pp->posx, pp->posy, &pp->cursectnum);
@@ -5442,7 +5454,7 @@ DoPlayerBeginRemoteOperate(PLAYERp pp, SECTOR_OBJECTp sop)
 
     save_sectnum = pp->cursectnum;
 
-    playerSetAngle(pp, sop->ang);
+    playerSetAngle(&pp->q16ang, &pp->angTarget, sop->ang);
     pp->posx = sop->xmid;
     pp->posy = sop->ymid;
     COVERupdatesector(pp->posx, pp->posy, &pp->cursectnum);
@@ -6127,12 +6139,12 @@ DoPlayerDeathHoriz(PLAYERp pp, short target, short speed)
 {
     if ((pp->q16horiz - IntToFixed(target)) > FRACUNIT)
     {   
-        playerAddHoriz(pp, -speed);
+        playerAddHoriz(&pp->q16horiz, &pp->horizAdjust, -speed);
     }
 
     if ((IntToFixed(target) - pp->q16horiz) > FRACUNIT)
     {
-        playerAddHoriz(pp, speed);
+        playerAddHoriz(&pp->q16horiz, &pp->horizAdjust, speed);
     }
 }
 
@@ -6229,7 +6241,7 @@ void DoPlayerDeathFollowKiller(PLAYERp pp)
 
         if (FAFcansee(kp->x, kp->y, SPRITEp_TOS(kp), kp->sectnum, pp->posx, pp->posy, pp->posz, pp->cursectnum))
         {
-            playerAddAngle(pp, getincangleq16(pp->q16ang, gethiq16angle(kp->x - pp->posx, kp->y - pp->posy)) / (double)(FRACUNIT << 4));
+            playerAddAngle(&pp->q16ang, &pp->angAdjust, getincangleq16(pp->q16ang, gethiq16angle(kp->x - pp->posx, kp->y - pp->posy)) / (double)(FRACUNIT << 4));
         }
     }
 }
@@ -7269,7 +7281,7 @@ domovethings(void)
         // auto tracking mode for single player multi-game
         if (numplayers <= 1 && PlayerTrackingMode && pnum == screenpeek && screenpeek != myconnectindex)
         {
-            playerSetAngle(&Player[screenpeek], FixedToFloat(gethiq16angle(Player[myconnectindex].posx - Player[screenpeek].posx, Player[myconnectindex].posy - Player[screenpeek].posy)));
+            playerSetAngle(&Player[screenpeek].q16ang, &Player[screenpeek].angTarget, FixedToFloat(gethiq16angle(Player[myconnectindex].posx - Player[screenpeek].posx, Player[myconnectindex].posy - Player[screenpeek].posy)));
         }
 
         if (!TEST(pp->Flags, PF_DEAD))
@@ -7617,84 +7629,6 @@ void CheckFootPrints(PLAYERp pp)
     }
 }
 
-//---------------------------------------------------------------------------
-//
-// Unsynchronised input helpers.
-//
-//---------------------------------------------------------------------------
-
-void resetinputhelpers(PLAYERp pp)
-{
-    pp->horizAdjust = 0;
-    pp->angAdjust = 0;
-}
-
-void playerAddAngle(PLAYERp pp, double ang)
-{
-    if (!cl_syncinput)
-    {
-        pp->angAdjust += ang;
-    }
-    else
-    {
-        pp->addang(ang);
-    }
-}
-
-void playerSetAngle(PLAYERp pp, double ang)
-{
-    if (!cl_syncinput)
-    {
-        // Cancel out any angle adjustments as we're setting angle now.
-        pp->angAdjust = 0;
-
-        // Add slight offset if input angle is coming in as absolute 0.
-        if (ang == 0)
-        {
-            ang += 0.1;
-        }
-
-        pp->angTarget = pp->q16ang + getincangleq16(pp->q16ang, FloatToFixed(ang));
-    }
-    else
-    {
-        pp->setang(ang);
-    }
-}
-
-void playerAddHoriz(PLAYERp pp, double horiz)
-{
-    if (!cl_syncinput)
-    {
-        pp->horizAdjust += horiz;
-    }
-    else
-    {
-        pp->addhoriz(horiz);
-    }
-}
-
-void playerSetHoriz(PLAYERp pp, double horiz)
-{
-    if (!cl_syncinput)
-    {
-        // Cancel out any horizon adjustments as we're setting horizon now.
-        pp->horizAdjust = 0;
-
-        // Add slight offset if input horizon is coming in as absolute 0.
-        if (horiz == 0)
-        {
-            horiz += 0.1;
-        }
-
-        pp->horizTarget = FloatToFixed(horiz);
-    }
-    else
-    {
-        pp->sethoriz(horiz);
-    }
-}
-
 //---------------------------------------------------------------------------
 //
 // 
diff --git a/source/sw/src/player.h b/source/sw/src/player.h
index 527b50742..9f3ca775b 100644
--- a/source/sw/src/player.h
+++ b/source/sw/src/player.h
@@ -144,12 +144,6 @@ void PlaySOsound(short sectnum,short sound_num);
 void DoSpawnTeleporterEffectPlace(SPRITEp sp);
 void FindMainSector(SECTOR_OBJECTp sop);
 
-void resetinputhelpers(PLAYERp pp);
-void playerAddAngle(PLAYERp pp, double ang);
-void playerSetAngle(PLAYERp pp, double ang);
-void playerAddHoriz(PLAYERp pp, double horiz);
-void playerSetHoriz(PLAYERp pp, double horiz);
-
 END_SW_NS
 
 #endif
diff --git a/source/sw/src/track.cpp b/source/sw/src/track.cpp
index 41e2234c2..b36178bf2 100644
--- a/source/sw/src/track.cpp
+++ b/source/sw/src/track.cpp
@@ -1678,7 +1678,7 @@ MovePlayer(PLAYERp pp, SECTOR_OBJECTp sop, int nx, int ny)
 
     // New angle is formed by taking last known angle and
     // adjusting by the delta angle
-    playerAddAngle(pp, getincangleq16(pp->q16ang, pp->RevolveQ16Ang + IntToFixed(pp->RevolveDeltaAng)));
+    playerAddAngle(&pp->q16ang, &pp->angAdjust, FixedToFloat(getincangleq16(pp->q16ang, pp->RevolveQ16Ang + IntToFixed(pp->RevolveDeltaAng))));
 
     UpdatePlayerSprite(pp);
 }

From 59e4fae0643130daa5b96277253fb00d2ecf5d41 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Mon, 21 Sep 2020 15:51:33 +1000
Subject: [PATCH 24/41] - Game input: Fix miscellaneous issues.

* Duke/RR: Fix `SB_CENTERVIEW` not clearing while `cl_syncinput 1`.
* Duke/RR: Remove superfluous call to `apply_seasick()`.
* RR: Change two calls from `playerSetAngle()` to `playerAddAngle()` Updated version of `playerSetAngle()` doesn't stop setting angle until target is reached, a bit too strict for this and compromised vehicle turning.
* `applylook()`: Remove dead flag. Was only used with Duke, no other game called the function when dead anyway. Since the input helpers are processed outside of `applylook()` now this is not needed.
* `applylook()`: Extend function with a bit of commentary.
---
 source/blood/src/controls.cpp      |  2 +-
 source/blood/src/player.cpp        |  2 +-
 source/core/gamecontrol.cpp        | 84 ++++++++++++++++--------------
 source/core/gamecontrol.h          |  2 +-
 source/games/duke/src/input.cpp    | 15 +++---
 source/games/duke/src/player_d.cpp |  4 +-
 source/games/duke/src/player_r.cpp |  8 +--
 source/sw/src/player.cpp           |  2 +-
 8 files changed, 63 insertions(+), 56 deletions(-)

diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index 005fa9d0c..b69c0a7d7 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -138,7 +138,7 @@ static void processMovement(ControlInfo* const hidInput)
         // Perform unsynchronised angle/horizon if not dead.
         if (gView->pXSprite->health != 0)
         {
-            applylook(&pPlayer->q16ang, &pPlayer->q16look_ang, &pPlayer->q16rotscrnang, &pPlayer->spin, input.q16avel, &pPlayer->input.actions, scaleAdjust, gView->pXSprite->health == 0, pPlayer->posture != 0);
+            applylook(&pPlayer->q16ang, &pPlayer->q16look_ang, &pPlayer->q16rotscrnang, &pPlayer->spin, input.q16avel, &pPlayer->input.actions, scaleAdjust, pPlayer->posture != 0);
             UpdatePlayerSpriteAngle(pPlayer);
             sethorizon(&pPlayer->q16horiz, input.q16horz, &pPlayer->input.actions, scaleAdjust);
         }
diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index 46fca1854..b1d072023 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1445,7 +1445,7 @@ void ProcessInput(PLAYER *pPlayer)
 
     if (cl_syncinput)
     {
-        applylook(&pPlayer->q16ang, &pPlayer->q16look_ang, &pPlayer->q16rotscrnang, &pPlayer->spin, pInput->q16avel, &pInput->actions, 1, gView->pXSprite->health == 0, pPlayer->posture != 0);
+        applylook(&pPlayer->q16ang, &pPlayer->q16look_ang, &pPlayer->q16rotscrnang, &pPlayer->spin, pInput->q16avel, &pInput->actions, 1, pPlayer->posture != 0);
         UpdatePlayerSpriteAngle(pPlayer);
     }
 
diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp
index cb838ada8..cc0867b0a 100644
--- a/source/core/gamecontrol.cpp
+++ b/source/core/gamecontrol.cpp
@@ -1560,54 +1560,58 @@ void sethorizon(fixed_t* q16horiz, fixed_t const q16horz, ESyncBits* actions, do
 //
 //---------------------------------------------------------------------------
 
-void applylook(fixed_t* q16ang, fixed_t* q16look_ang, fixed_t* q16rotscrnang, fixed_t* spin, fixed_t const q16avel, ESyncBits* actions, double const scaleAdjust, bool const dead, bool const crouching)
+void applylook(fixed_t* q16ang, fixed_t* q16look_ang, fixed_t* q16rotscrnang, fixed_t* spin, fixed_t const q16avel, ESyncBits* actions, double const scaleAdjust, bool const crouching)
 {
-	if (!dead)
+	// return q16rotscrnang to 0 and set to 0 if less than a quarter of a FRACUNIT (16384)
+	*q16rotscrnang -= xs_CRoundToInt(scaleAdjust * (*q16rotscrnang * (15. / GameTicRate)));
+	if (abs(*q16rotscrnang) < (FRACUNIT >> 2)) *q16rotscrnang = 0;
+
+	// return q16look_ang to 0 and set to 0 if less than a quarter of a FRACUNIT (16384)
+	*q16look_ang -= xs_CRoundToInt(scaleAdjust * (*q16look_ang * (7.5 / GameTicRate)));
+	if (abs(*q16look_ang) < (FRACUNIT >> 2)) *q16look_ang = 0;
+
+	if (*actions & SB_LOOK_LEFT)
 	{
-		*q16rotscrnang -= xs_CRoundToInt(scaleAdjust * (*q16rotscrnang * (15. / GameTicRate)));
-		if (abs(*q16rotscrnang) < (FRACUNIT >> 2)) *q16rotscrnang = 0;
+		// start looking left
+		*q16look_ang -= FloatToFixed(scaleAdjust * (4560. / GameTicRate));
+		*q16rotscrnang += FloatToFixed(scaleAdjust * (720. / GameTicRate));
+	}
 
-		*q16look_ang -= xs_CRoundToInt(scaleAdjust * (*q16look_ang * (7.5 / GameTicRate)));
-		if (abs(*q16look_ang) < (FRACUNIT >> 2)) *q16look_ang = 0;
+	if (*actions & SB_LOOK_RIGHT)
+	{
+		// start looking right
+		*q16look_ang += FloatToFixed(scaleAdjust * (4560. / GameTicRate));
+		*q16rotscrnang -= FloatToFixed(scaleAdjust * (720. / GameTicRate));
+	}
 
-		if (*actions & SB_LOOK_LEFT)
+	if (*actions & SB_TURNAROUND)
+	{
+		if (*spin == 0)
 		{
-			*q16look_ang -= FloatToFixed(scaleAdjust * (4560. / GameTicRate));
-			*q16rotscrnang += FloatToFixed(scaleAdjust * (720. / GameTicRate));
+			// currently not spinning, so start a spin
+			*spin = IntToFixed(-1024);
 		}
+		*actions &= ~SB_TURNAROUND;
+	}
 
-		if (*actions & SB_LOOK_RIGHT)
+	if (*spin < 0)
+	{
+		// return spin to 0
+		fixed_t add = FloatToFixed(scaleAdjust * ((!crouching ? 3840. : 1920.) / GameTicRate));
+		*spin += add;
+		if (*spin > 0)
 		{
-			*q16look_ang += FloatToFixed(scaleAdjust * (4560. / GameTicRate));
-			*q16rotscrnang -= FloatToFixed(scaleAdjust * (720. / GameTicRate));
+			// Don't overshoot our target. With variable factor this is possible.
+			add -= *spin;
+			*spin = 0;
 		}
+		*q16ang += add;
+	}
 
-		if (*actions & SB_TURNAROUND)
-		{
-			if (*spin == 0)
-			{
-				*spin = IntToFixed(-1024);
-			}
-			*actions &= ~SB_TURNAROUND;
-		}
-
-		if (*spin < 0)
-		{
-			fixed_t add = FloatToFixed(scaleAdjust * ((!crouching ? 3840. : 1920.) / GameTicRate));
-			*spin += add;
-			if (*spin > 0)
-			{
-				// Don't overshoot our target. With variable factor this is possible.
-				add -= *spin;
-				*spin = 0;
-			}
-			*q16ang += add;
-		}
-
-		if (q16avel)
-		{
-			*q16ang = (*q16ang + q16avel) & 0x7FFFFFF;
-		}
+	if (q16avel)
+	{
+		// add player's input
+		*q16ang = (*q16ang + q16avel) & 0x7FFFFFF;
 	}
 }
 
@@ -1634,7 +1638,7 @@ void playerSetAngle(fixed_t* q16ang, fixed_t* helper, double adjustment)
 	if (!cl_syncinput)
 	{
 		// Add slight offset if adjustment is coming in as absolute 0.
-		if (adjustment == 0) adjustment += (1. / FRACUNIT);
+		if (adjustment == 0) adjustment += (1. / (FRACUNIT >> 1));
 
 		*helper = *q16ang + getincangleq16(*q16ang, FloatToFixed(adjustment));
 	}
@@ -1661,7 +1665,7 @@ void playerSetHoriz(fixed_t* q16horiz, fixed_t* helper, double adjustment)
 	if (!cl_syncinput)
 	{
 		// Add slight offset if adjustment is coming in as absolute 0.
-		if (adjustment == 0) adjustment += (1. / FRACUNIT);
+		if (adjustment == 0) adjustment += (1. / (FRACUNIT >> 1));
 
 		*helper = FloatToFixed(adjustment);
 	}
diff --git a/source/core/gamecontrol.h b/source/core/gamecontrol.h
index 4939875e3..7268f657b 100644
--- a/source/core/gamecontrol.h
+++ b/source/core/gamecontrol.h
@@ -68,7 +68,7 @@ int getincangle(int c, int n);
 fixed_t getincangleq16(fixed_t c, fixed_t n);
 
 void sethorizon(fixed_t* q16horiz, fixed_t const q16horz, ESyncBits* actions, double const scaleAdjust);
-void applylook(fixed_t* q16ang, fixed_t* q16look_ang, fixed_t* q16rotscrnang, fixed_t* spin, fixed_t const q16avel, ESyncBits* actions, double const scaleAdjust, bool const dead, bool const crouching);
+void applylook(fixed_t* q16ang, fixed_t* q16look_ang, fixed_t* q16rotscrnang, fixed_t* spin, fixed_t const q16avel, ESyncBits* actions, double const scaleAdjust, bool const crouching);
 void playerAddAngle(fixed_t* q16ang, double* helper, double adjustment);
 void playerSetAngle(fixed_t* q16ang, fixed_t* helper, double adjustment);
 void playerAddHoriz(fixed_t* q16horiz, double* helper, double adjustment);
diff --git a/source/games/duke/src/input.cpp b/source/games/duke/src/input.cpp
index 02c57ca7b..9dd2d17c7 100644
--- a/source/games/duke/src/input.cpp
+++ b/source/games/duke/src/input.cpp
@@ -1020,12 +1020,15 @@ static void GetInputInternal(InputPacket &locInput, ControlInfo* const hidInput)
 
 	if (!cl_syncinput)
 	{
-		// Do these in the same order as the old code.
-		calcviewpitch(p, scaleAdjust);
-		processq16avel(p, &input.q16avel);
-		applylook(&p->q16ang, &p->q16look_ang, &p->q16rotscrnang, &p->one_eighty_count, input.q16avel, &sync[myconnectindex].actions, scaleAdjust, p->dead_flag != 0, p->crouch_toggle || sync[myconnectindex].actions & SB_CROUCH);
-		apply_seasick(p, scaleAdjust);
-		sethorizon(&p->q16horiz, input.q16horz, &sync[myconnectindex].actions, scaleAdjust);
+		if (p->dead_flag == 0)
+		{
+			// Do these in the same order as the old code.
+			calcviewpitch(p, scaleAdjust);
+			processq16avel(p, &input.q16avel);
+			applylook(&p->q16ang, &p->q16look_ang, &p->q16rotscrnang, &p->one_eighty_count, input.q16avel, &sync[myconnectindex].actions, scaleAdjust, p->crouch_toggle || sync[myconnectindex].actions & SB_CROUCH);
+			sethorizon(&p->q16horiz, input.q16horz, &sync[myconnectindex].actions, scaleAdjust);
+		}
+
 		playerProcessHelpers(&p->q16ang, &p->angAdjust, &p->angTarget, &p->q16horiz, &p->horizAdjust, &p->horizTarget, scaleAdjust);
 	}
 }
diff --git a/source/games/duke/src/player_d.cpp b/source/games/duke/src/player_d.cpp
index 80d09468b..1142ffb6a 100644
--- a/source/games/duke/src/player_d.cpp
+++ b/source/games/duke/src/player_d.cpp
@@ -2847,7 +2847,7 @@ void processinput_d(int snum)
 		// may still be needed later for demo recording
 
 		processq16avel(p, &sb_avel);
-		applylook(&p->q16ang, &p->q16look_ang, &p->q16rotscrnang, &p->one_eighty_count, sb_avel, &actions, 1, p->dead_flag != 0, p->crouch_toggle || actions & SB_CROUCH);
+		applylook(&p->q16ang, &p->q16look_ang, &p->q16rotscrnang, &p->one_eighty_count, sb_avel, &sync[snum].actions, 1, p->crouch_toggle || actions & SB_CROUCH);
 	}
 
 	if (p->spritebridge == 0)
@@ -3079,7 +3079,7 @@ HORIZONLY:
 
 	if (cl_syncinput)
 	{
-		sethorizon(&p->q16horiz, PlayerHorizon(snum), &actions, 1);
+		sethorizon(&p->q16horiz, PlayerHorizon(snum), &sync[snum].actions, 1);
 	}
 
 	checkhardlanding(p);
diff --git a/source/games/duke/src/player_r.cpp b/source/games/duke/src/player_r.cpp
index 26d231e24..978d07198 100644
--- a/source/games/duke/src/player_r.cpp
+++ b/source/games/duke/src/player_r.cpp
@@ -1812,7 +1812,7 @@ static void onMotorcycle(int snum, ESyncBits &actions)
 				ang = var98 >> 7;
 			}
 		}
-		playerSetAngle(&p->q16ang, &p->angTarget, (var90 - ang) & 2047);
+		playerAddAngle(&p->q16ang, &p->angAdjust, FixedToFloat(getincangleq16(p->q16ang, IntToFixed(var90 - ang))));
 	}
 	else if (p->MotoSpeed >= 20 && p->on_ground == 1 && (p->moto_on_mud || p->moto_on_oil))
 	{
@@ -2111,7 +2111,7 @@ static void onBoat(int snum, ESyncBits &actions)
 			p->posyv += (vard4 >> 7) * (sintable[(vardc * -51 + vard8) & 2047] << 4);
 			ang = vare0 >> 6;
 		}
-		playerSetAngle(&p->q16ang, &p->angTarget, (vard8 - ang) & 2047);
+		playerAddAngle(&p->q16ang, &p->angAdjust, FixedToFloat(getincangleq16(p->q16ang, IntToFixed(vard8 - ang))));
 	}
 	if (p->NotOnWater)
 		if (p->MotoSpeed > 50)
@@ -3737,7 +3737,7 @@ void processinput_r(int snum)
 		// may still be needed later for demo recording
 
 		processq16avel(p, &sb_avel);
-		applylook(&p->q16ang, &p->q16look_ang, &p->q16rotscrnang, &p->one_eighty_count, sb_avel, &actions, 1, p->dead_flag != 0, p->crouch_toggle || actions & SB_CROUCH);
+		applylook(&p->q16ang, &p->q16look_ang, &p->q16rotscrnang, &p->one_eighty_count, sb_avel, &sync[snum].actions, 1, p->crouch_toggle || actions & SB_CROUCH);
 		apply_seasick(p, 1);
 	}
 
@@ -4093,7 +4093,7 @@ HORIZONLY:
 
 	if (cl_syncinput)
 	{
-		sethorizon(&p->q16horiz, PlayerHorizon(snum), &actions, 1);
+		sethorizon(&p->q16horiz, PlayerHorizon(snum), &sync[snum].actions, 1);
 	}
 
 	checkhardlanding(p);
diff --git a/source/sw/src/player.cpp b/source/sw/src/player.cpp
index 50f05604e..24234708e 100644
--- a/source/sw/src/player.cpp
+++ b/source/sw/src/player.cpp
@@ -1525,7 +1525,7 @@ UpdatePlayerSpriteAngle(PLAYERp pp)
 void
 DoPlayerTurn(PLAYERp pp, fixed_t const q16avel, double const scaleAdjust)
 {
-    applylook(&pp->q16ang, &pp->q16look_ang, &pp->q16rotscrnang, &pp->turn180_target, q16avel, &pp->input.actions, scaleAdjust, TEST(pp->Flags, PF_DEAD), pp->input.actions & (SB_CROUCH|SB_CROUCH_LOCK));
+    applylook(&pp->q16ang, &pp->q16look_ang, &pp->q16rotscrnang, &pp->turn180_target, q16avel, &pp->input.actions, scaleAdjust, pp->input.actions & (SB_CROUCH|SB_CROUCH_LOCK));
     UpdatePlayerSpriteAngle(pp);
 }
 

From 3de787235d7804de3cbe3c472ba981ff7589abb3 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Mon, 21 Sep 2020 18:36:04 +1000
Subject: [PATCH 25/41] - Exhumed: Initial uplift of player input. Still more
 to do.

---
 source/exhumed/src/exhumed.cpp |   5 +-
 source/exhumed/src/exhumed.h   |   3 +-
 source/exhumed/src/input.cpp   | 164 ++++++++++-----------------------
 source/exhumed/src/player.h    |   7 +-
 4 files changed, 60 insertions(+), 119 deletions(-)

diff --git a/source/exhumed/src/exhumed.cpp b/source/exhumed/src/exhumed.cpp
index 18827e30a..7c3ff7485 100644
--- a/source/exhumed/src/exhumed.cpp
+++ b/source/exhumed/src/exhumed.cpp
@@ -439,10 +439,13 @@ void GameInterface::Ticker()
         // make weapon selection persist until it gets used up.
         sPlayerInput[nLocalPlayer].buttons = lLocalCodes;
         int weap = sPlayerInput[nLocalPlayer].getNewWeapon();
-        sPlayerInput[nLocalPlayer].actions = localInput.actions;
         if (weap2 <= 0 || weap2 > 7) sPlayerInput[nLocalPlayer].SetNewWeapon(weap);
         sPlayerInput[nLocalPlayer].nTarget = besttarget;
 
+        auto oldactions = sPlayerInput[nLocalPlayer].actions;
+        sPlayerInput[nLocalPlayer].actions = localInput.actions;
+        if (oldactions & SB_CENTERVIEW) sPlayerInput[nLocalPlayer].actions |= SB_CENTERVIEW;
+
         Ra[nLocalPlayer].nTarget = besttarget;
 
         lLocalCodes = 0;
diff --git a/source/exhumed/src/exhumed.h b/source/exhumed/src/exhumed.h
index d4a5113ba..a63f5b2f9 100644
--- a/source/exhumed/src/exhumed.h
+++ b/source/exhumed/src/exhumed.h
@@ -260,7 +260,8 @@ struct GameInterface : ::GameInterface
 	void LevelCompleted(MapRecord *map, int skill) override;
 	void NextLevel(MapRecord *map, int skill) override;
     bool DrawAutomapPlayer(int x, int y, int z, int a) override;
-
+    fixed_t playerHorizMin() override { return 0; }
+    fixed_t playerHorizMax() override { return IntToFixed(184); }
 
 	::GameStats getStats() override;
 };
diff --git a/source/exhumed/src/input.cpp b/source/exhumed/src/input.cpp
index 0e2af7cf3..7a6f61bfe 100644
--- a/source/exhumed/src/input.cpp
+++ b/source/exhumed/src/input.cpp
@@ -17,21 +17,20 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 //-------------------------------------------------------------------------
 #include "ns.h"
 #include "ps_input.h"
-#include "engine.h"
 #include "exhumed.h"
 #include "player.h"
-#include "aistuff.h"
 #include "status.h"
 #include "view.h"
-#include "gamecontrol.h"
-#include <string.h>
-#include "v_video.h"
+#include "menu.h"
 
 BEGIN_PS_NS
 
 extern short bPlayerPan;
 extern short bLockPan;
 
+static int turn;
+static int counter;
+
 short nInputStack = 0;
 
 short bStackNode[kMaxPlayers];
@@ -98,64 +97,40 @@ void CheckKeys2()
 }
 
 
-static void PlayerInterruptKeys(bool after, ControlInfo* const hidInput)
+static void processMovement(ControlInfo* const hidInput)
 {
-    static double lastInputTicks;
-    auto const    currentHiTicks = I_msTimeF();
-    double const  elapsedInputTicks = currentHiTicks - lastInputTicks;
-
-    lastInputTicks = currentHiTicks;
-
-    auto scaleAdjustmentToInterval = [=](double x) { return x * (120 / 4) / (1000.0 / elapsedInputTicks); };
-
-    if (paused)
-        return;
-
-    InputPacket tempinput{};
-    fixed_t input_angle = 0;
-
-    if (!after)
-    {
-        localInput = {};
-        ApplyGlobalInput(localInput, hidInput);
-        if (PlayerList[nLocalPlayer].nHealth == 0) localInput.actions &= SB_OPEN;
-    }
-
-    if (PlayerList[nLocalPlayer].nHealth == 0)
-    {
-        lPlayerYVel = 0;
-        lPlayerXVel = 0;
-        nPlayerDAng = 0;
-        return;
-    }
-
     // JBF: Run key behaviour is selectable
     int const playerRunning = !!(localInput.actions & SB_RUN);
     int const turnAmount = playerRunning ? 12 : 8;
     int const keyMove = playerRunning ? 12 : 6;
+    bool const mouseaim = !(localInput.actions & SB_AIMMODE);
+    double const scaleAdjust = InputScale();
+    InputPacket tempinput {};
 
     if (buttonMap.ButtonDown(gamefunc_Strafe))
     {
-        tempinput.svel -= hidInput->mousex * 4.f;
-        tempinput.svel -= hidInput->dyaw * keyMove;
+        tempinput.svel -= xs_CRoundToInt((hidInput->mousex * 32.) + (scaleAdjust * (hidInput->dyaw * keyMove)));
     }
     else
     {
-        input_angle += FloatToFixed(hidInput->mousex + scaleAdjustmentToInterval(hidInput->dyaw));
+        tempinput.q16avel += FloatToFixed(hidInput->mousex + (scaleAdjust * hidInput->dyaw));
     }
 
-    bool mouseaim = !(localInput.actions & SB_AIMMODE);
-
     if (mouseaim)
+    {
         tempinput.q16horz += FloatToFixed(hidInput->mousey);
+    }
     else
-        tempinput.fvel -= hidInput->mousey * 8.f;
+    {
+        tempinput.fvel -= xs_CRoundToInt(hidInput->mousey * 8.);
+    }
 
-    if (!in_mouseflip) tempinput.q16horz = -tempinput.q16horz;
+    if (!in_mouseflip) 
+        tempinput.q16horz = -tempinput.q16horz;
 
-    tempinput.q16horz -= FloatToFixed(scaleAdjustmentToInterval(hidInput->dpitch));
-    tempinput.svel -= hidInput->dx * keyMove;
-    tempinput.fvel -= hidInput->dz * keyMove;
+    tempinput.q16horz -= FloatToFixed(scaleAdjust * hidInput->dpitch);
+    tempinput.svel -= xs_CRoundToInt(scaleAdjust * (hidInput->dx * keyMove));
+    tempinput.fvel -= xs_CRoundToInt(scaleAdjust * (hidInput->dz * keyMove));
 
     if (buttonMap.ButtonDown(gamefunc_Strafe))
     {
@@ -167,9 +142,6 @@ static void PlayerInterruptKeys(bool after, ControlInfo* const hidInput)
     }
     else
     {
-        static int turn = 0;
-        static int counter = 0;
-        // normal, non strafing movement
         if (buttonMap.ButtonDown(gamefunc_Turn_Left))
         {
             turn -= 2;
@@ -200,7 +172,7 @@ static void PlayerInterruptKeys(bool after, ControlInfo* const hidInput)
         }
 
         //if ((counter++) % 4 == 0) // what was this for???
-        input_angle += FloatToFixed(scaleAdjustmentToInterval(turn * 2));
+        tempinput.q16avel += FloatToFixed(scaleAdjust * (turn * 2));
 
     }
 
@@ -218,81 +190,43 @@ static void PlayerInterruptKeys(bool after, ControlInfo* const hidInput)
 
     localInput.fvel = clamp(localInput.fvel + tempinput.fvel, -12, 12);
     localInput.svel = clamp(localInput.svel + tempinput.svel, -12, 12);
-    localInput.q16avel += input_angle;
+    localInput.q16avel += tempinput.q16avel;
+    localInput.q16horz += tempinput.q16horz;
 
-    if (!nFreeze)
+    if (!cl_syncinput && !nFreeze)
     {
-        PlayerList[nLocalPlayer].q16angle = (PlayerList[nLocalPlayer].q16angle + input_angle) & 0x7FFFFFF;
+        Player* pPlayer = &PlayerList[nLocalPlayer];
 
-        // A horiz diff of 128 equal 45 degrees,
-        // so we convert horiz to 1024 angle units
-
-        float const horizAngle = clamp(atan2f(PlayerList[nLocalPlayer].q16horiz - IntToFixed(92), IntToFixed(128)) * (512.f / fPI) + FixedToFloat(tempinput.q16horz), -255.f, 255.f);
-        auto newq16horiz = IntToFixed(92) + xs_CRoundToInt(IntToFixed(128) * tanf(horizAngle * (fPI / 512.f)));
-        if (PlayerList[nLocalPlayer].q16horiz != newq16horiz)
-        {
-            bLockPan = true;
-            PlayerList[nLocalPlayer].q16horiz = newq16horiz;
-            nDestVertPan[nLocalPlayer] = PlayerList[nLocalPlayer].q16horiz;
-        }
-
-        // Look/aim up/down functions.
-        if (localInput.actions & (SB_LOOK_UP|SB_AIM_UP))
-        {
-            bLockPan |= (localInput.actions & SB_LOOK_UP);
-            if (PlayerList[nLocalPlayer].q16horiz < IntToFixed(180)) {
-                PlayerList[nLocalPlayer].q16horiz += FloatToFixed(scaleAdjustmentToInterval(4));
-            }
-
-            bPlayerPan = true;
-            nDestVertPan[nLocalPlayer] = PlayerList[nLocalPlayer].q16horiz;
-        }
-        else if (localInput.actions & (SB_LOOK_DOWN|SB_AIM_DOWN))
-        {
-            bLockPan |= (localInput.actions & SB_LOOK_DOWN);
-            if (PlayerList[nLocalPlayer].q16horiz > IntToFixed(4)) {
-                PlayerList[nLocalPlayer].q16horiz -= FloatToFixed(scaleAdjustmentToInterval(4));
-            }
-
-            bPlayerPan = true;
-            nDestVertPan[nLocalPlayer] = PlayerList[nLocalPlayer].q16horiz;
-        }
+        applylook(&pPlayer->q16angle, &pPlayer->q16look_ang, &pPlayer->q16rotscrnang, &pPlayer->spin, tempinput.q16avel, &sPlayerInput[nLocalPlayer].actions, scaleAdjust, false);
+        sethorizon(&pPlayer->q16horiz, tempinput.q16horz, &sPlayerInput[nLocalPlayer].actions, scaleAdjust);
     }
-
-    // loc_1C048:
-    if (totalvel[nLocalPlayer] > 20) {
-        bPlayerPan = false;
-    }
-    if (nFreeze) return;
-
-    // loc_1C05E
-    fixed_t dVertPan = nDestVertPan[nLocalPlayer] - PlayerList[nLocalPlayer].q16horiz;
-    if (dVertPan != 0 && !bLockPan)
-    {
-        int val = dVertPan / 4;
-        if (abs(val) >= 4)
-        {
-            if (val >= 4)
-                PlayerList[nLocalPlayer].q16horiz += IntToFixed(4);
-            else if (val <= -4)
-                PlayerList[nLocalPlayer].q16horiz -= IntToFixed(4);
-        }
-        else if (abs(dVertPan) >= FRACUNIT)
-            PlayerList[nLocalPlayer].q16horiz += dVertPan / 2.0f;
-        else
-        {
-            if (mouseaim) bLockPan = true;
-            PlayerList[nLocalPlayer].q16horiz = nDestVertPan[nLocalPlayer];
-        }
-    }
-    else bLockPan = mouseaim;
-    PlayerList[nLocalPlayer].q16horiz = clamp(PlayerList[nLocalPlayer].q16horiz, 0, IntToFixed(184));
 }
 
 
 void GameInterface::GetInput(InputPacket* packet, ControlInfo* const hidInput)
 {
-    PlayerInterruptKeys(packet == nullptr, hidInput);
+    if (paused || M_Active())
+    {
+        localInput = {};
+        return;
+    }
+
+    if (PlayerList[nLocalPlayer].nHealth == 0)
+    {
+        lPlayerYVel = 0;
+        lPlayerXVel = 0;
+        nPlayerDAng = 0;
+        return;
+    }
+
+    if (packet != nullptr)
+    {
+        localInput = {};
+        ApplyGlobalInput(localInput, hidInput);
+        if (PlayerList[nLocalPlayer].nHealth == 0) localInput.actions &= SB_OPEN;
+    }
+
+    processMovement(hidInput);
     if (packet) *packet = localInput;
 }
 
diff --git a/source/exhumed/src/player.h b/source/exhumed/src/player.h
index 7c1989572..2e07b0e29 100644
--- a/source/exhumed/src/player.h
+++ b/source/exhumed/src/player.h
@@ -70,8 +70,11 @@ struct Player
     short field_3C;
     short nRun;
 
-    fixed_t q16angle;
-    fixed_t q16horiz;
+    fixed_t oq16angle, q16angle;
+    fixed_t oq16horiz, q16horiz;
+    fixed_t oq16look_ang, q16look_ang;
+    fixed_t oq16rotscrnang, q16rotscrnang;
+    fixed_t spin;
     vec3_t opos;
 };
 

From bdc53ed7b074222298f592ae95bce5065abe5084 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Mon, 21 Sep 2020 18:41:56 +1000
Subject: [PATCH 26/41] - Exhumed: Standardise horizon on 100 and remove
 backend extern'd `polymostcenterhoriz` variable.

---
 source/build/include/polymost.h |  2 --
 source/build/src/engine.cpp     |  1 -
 source/build/src/polymost.cpp   |  4 ++--
 source/exhumed/src/exhumed.cpp  |  5 ++---
 source/exhumed/src/player.cpp   | 18 +++++++++---------
 source/exhumed/src/view.cpp     |  9 ++-------
 source/exhumed/src/view.h       |  1 -
 7 files changed, 15 insertions(+), 25 deletions(-)

diff --git a/source/build/include/polymost.h b/source/build/include/polymost.h
index 41c151db6..22b8e912f 100644
--- a/source/build/include/polymost.h
+++ b/source/build/include/polymost.h
@@ -45,8 +45,6 @@ enum {
 
 extern float curpolygonoffset;
 
-extern int32_t polymostcenterhoriz;
-
 extern int16_t globalpicnum;
 
 #define POLYMOST_CHOOSE_FOG_PAL(fogpal, pal) \
diff --git a/source/build/src/engine.cpp b/source/build/src/engine.cpp
index 7be582938..46bddc0a5 100644
--- a/source/build/src/engine.cpp
+++ b/source/build/src/engine.cpp
@@ -48,7 +48,6 @@ int32_t r_rortexture = 0;
 int32_t r_rortexturerange = 0;
 int32_t r_rorphase = 0;
 int32_t mdtims, omdtims;
-int32_t polymostcenterhoriz = 100;
 
 float fcosglobalang, fsinglobalang;
 float fxdim, fydim, fydimen, fviewingrange;
diff --git a/source/build/src/polymost.cpp b/source/build/src/polymost.cpp
index 42df205bd..fab984e89 100644
--- a/source/build/src/polymost.cpp
+++ b/source/build/src/polymost.cpp
@@ -2484,7 +2484,7 @@ void polymost_drawrooms()
     ghalfy = (float)(ydimen>>1);
     grhalfxdown10 = 1.f/(ghalfx*1024.f);
     ghoriz = FixedToFloat(qglobalhoriz);
-    ghorizcorrect = FixedToFloat((100-polymostcenterhoriz)*divscale16(xdimenscale, viewingrange));
+    ghorizcorrect = FixedToFloat(divscale16(xdimenscale, viewingrange));
 
     GLInterface.SetShadeInterpolate(hw_shadeinterpolate);
 
@@ -2884,7 +2884,7 @@ void polymost_prepareMirror(int32_t dax, int32_t day, int32_t daz, fixed_t daang
     ghalfy = (float)(ydimen>>1);
     grhalfxdown10 = 1.f/(ghalfx*1024.f);
     ghoriz = FixedToFloat(qglobalhoriz);
-    ghorizcorrect = FixedToFloat((100-polymostcenterhoriz)*divscale16(xdimenscale, viewingrange));
+    ghorizcorrect = FixedToFloat(divscale16(xdimenscale, viewingrange));
     resizeglcheck();
     if (r_yshearing)
     {
diff --git a/source/exhumed/src/exhumed.cpp b/source/exhumed/src/exhumed.cpp
index 7c3ff7485..5b91880a0 100644
--- a/source/exhumed/src/exhumed.cpp
+++ b/source/exhumed/src/exhumed.cpp
@@ -425,8 +425,8 @@ void GameInterface::Ticker()
         {
             bLockPan = false;
             bPlayerPan = false;
-            //PlayerList[nLocalPlayer].q16horiz = IntToFixed(92);
-            nDestVertPan[nLocalPlayer] = IntToFixed(92);
+            //PlayerList[nLocalPlayer].q16horiz = IntToFixed(100);
+            nDestVertPan[nLocalPlayer] = IntToFixed(100);
         }
         if (localInput.actions & SB_TURNAROUND)
         {
@@ -528,7 +528,6 @@ void GameInterface::app_init()
 
     TileFiles.SetBackup();
 
-    InitView();
     InitFX();
     seq_LoadSequences();
     InitStatus();
diff --git a/source/exhumed/src/player.cpp b/source/exhumed/src/player.cpp
index f13651971..ac62afe90 100644
--- a/source/exhumed/src/player.cpp
+++ b/source/exhumed/src/player.cpp
@@ -437,8 +437,8 @@ void RestartPlayer(short nPlayer)
     nYDamage[nPlayer] = 0;
     nXDamage[nPlayer] = 0;
 
-    PlayerList[nPlayer].q16horiz = IntToFixed(92);
-    nDestVertPan[nPlayer] = IntToFixed(92);
+    PlayerList[nPlayer].q16horiz = IntToFixed(100);
+    nDestVertPan[nPlayer] = IntToFixed(100);
     nBreathTimer[nPlayer] = 90;
 
     nTauntTimer[nPlayer] = RandomSize(3) + 3;
@@ -539,7 +539,7 @@ void StartDeathSeq(int nPlayer, int nVal)
 
     StopFiringWeapon(nPlayer);
 
-    PlayerList[nPlayer].q16horiz = IntToFixed(92);
+    PlayerList[nPlayer].q16horiz = IntToFixed(100);
     oeyelevel[nPlayer] = eyelevel[nPlayer] = -14080;
     nPlayerInvisible[nPlayer] = 0;
     dVertPan[nPlayer] = 15;
@@ -1036,7 +1036,7 @@ void FuncPlayer(int a, int nDamage, int nRun)
                     PlayerList[nPlayer].q16angle = IntToFixed(GetAngleToSprite(nPlayerSprite, nSpiritSprite) & kAngleMask);
                     sprite[nPlayerSprite].ang = FixedToInt(PlayerList[nPlayer].q16angle);
 
-                    PlayerList[nPlayer].q16horiz = IntToFixed(92);
+                    PlayerList[nPlayer].q16horiz = IntToFixed(100);
 
                     lPlayerXVel = 0;
                     lPlayerYVel = 0;
@@ -1054,7 +1054,7 @@ void FuncPlayer(int a, int nDamage, int nRun)
                         StopLocalSound();
                         InitSpiritHead();
 
-                        nDestVertPan[nPlayer] = IntToFixed(92);
+                        nDestVertPan[nPlayer] = IntToFixed(100);
 
                         if (currentLevel->levelNumber == 11)
                         {
@@ -1091,7 +1091,7 @@ void FuncPlayer(int a, int nDamage, int nRun)
                         }
 
                         if (zVelB > 512 && !bLockPan) {
-                            nDestVertPan[nPlayer] = IntToFixed(92);
+                            nDestVertPan[nPlayer] = IntToFixed(100);
                         }
                     }
 
@@ -1192,7 +1192,7 @@ void FuncPlayer(int a, int nDamage, int nRun)
 loc_1AB8E:
             if (!bPlayerPan && !bLockPan)
             {
-                fixed_t nPanVal = IntToFixed(spr_z - sprite[nPlayerSprite].z) / 32 + IntToFixed(92);
+                fixed_t nPanVal = IntToFixed(spr_z - sprite[nPlayerSprite].z) / 32 + IntToFixed(100);
 
                 if (nPanVal < 0) {
                     nPanVal = 0;
@@ -2791,7 +2791,7 @@ loc_1BD2E:
                 }
                 else
                 {
-                    if (PlayerList[nPlayer].q16horiz < IntToFixed(92))
+                    if (PlayerList[nPlayer].q16horiz < IntToFixed(100))
                     {
                         PlayerList[nPlayer].q16horiz = IntToFixed(91);
                         eyelevel[nPlayer] -= (dVertPan[nPlayer] << 8);
@@ -2803,7 +2803,7 @@ loc_1BD2E:
                         {
                             PlayerList[nPlayer].q16horiz = IntToFixed(199);
                         }
-                        else if (PlayerList[nPlayer].q16horiz <= IntToFixed(92))
+                        else if (PlayerList[nPlayer].q16horiz <= IntToFixed(100))
                         {
                             if (!(SectFlag[sprite[nPlayerSprite].sectnum] & kSectUnderwater))
                             {
diff --git a/source/exhumed/src/view.cpp b/source/exhumed/src/view.cpp
index d47302a17..5c2f399da 100644
--- a/source/exhumed/src/view.cpp
+++ b/source/exhumed/src/view.cpp
@@ -120,11 +120,6 @@ void viewRestoreInterpolations(void)  //Stick at end of drawscreen
     for (; i>=0; i--) *curipos[i] = bakipos[i];
 }
 
-void InitView()
-{
-    polymostcenterhoriz = 92;
-}
-
 // NOTE - not to be confused with Ken's analyzesprites()
 static void analyzesprites()
 {
@@ -297,7 +292,7 @@ void DrawView(double smoothRatio, bool sceneonly)
     {
         if (nSnakeCam >= 0 && !sceneonly)
         {
-            pan = IntToFixed(92);
+            pan = IntToFixed(100);
             viewz = playerZ;
         }
         else
@@ -321,7 +316,7 @@ void DrawView(double smoothRatio, bool sceneonly)
             -2000 * Sin(inita),
             4, 0, 0, CLIPMASK1);
 
-        pan = IntToFixed(92);
+        pan = IntToFixed(100);
         viewz = playerZ;
     }
 
diff --git a/source/exhumed/src/view.h b/source/exhumed/src/view.h
index 7343a51cd..56e3deef7 100644
--- a/source/exhumed/src/view.h
+++ b/source/exhumed/src/view.h
@@ -27,7 +27,6 @@ extern short bSubTitles;
 extern short besttarget;
 extern short bCamera;
 
-void InitView();
 void DrawStatusBar();
 void DrawView(double smoothRatio, bool sceneonly = false);
 void ResetView();

From 8a4d4b6905d38cd05be3844f74e5cbde32f3f991 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Mon, 21 Sep 2020 18:48:38 +1000
Subject: [PATCH 27/41] - Exhumed: Open game's constrained horizon as much as
 sky tile in first level permits.

---
 source/exhumed/src/exhumed.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/exhumed/src/exhumed.h b/source/exhumed/src/exhumed.h
index a63f5b2f9..113f9d764 100644
--- a/source/exhumed/src/exhumed.h
+++ b/source/exhumed/src/exhumed.h
@@ -260,8 +260,8 @@ struct GameInterface : ::GameInterface
 	void LevelCompleted(MapRecord *map, int skill) override;
 	void NextLevel(MapRecord *map, int skill) override;
     bool DrawAutomapPlayer(int x, int y, int z, int a) override;
-    fixed_t playerHorizMin() override { return 0; }
-    fixed_t playerHorizMax() override { return IntToFixed(184); }
+    fixed_t playerHorizMin() override { return IntToFixed(-50); }
+    fixed_t playerHorizMax() override { return IntToFixed(250); }
 
 	::GameStats getStats() override;
 };

From fa1ce1c021dfb5cee2afda0776061888b5f5931f Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Mon, 21 Sep 2020 19:28:39 +1000
Subject: [PATCH 28/41] - Exhumed: Hooked up tic-rate input helpers. Also
 removed `nDestVertPan[]`.

---
 source/exhumed/src/exhumed.cpp | 18 +++++-----------
 source/exhumed/src/input.cpp   | 11 +++++++---
 source/exhumed/src/player.cpp  | 39 +++++++++++++---------------------
 source/exhumed/src/player.h    |  2 ++
 source/exhumed/src/ramses.cpp  |  2 +-
 source/exhumed/src/view.cpp    |  2 --
 source/exhumed/src/view.h      |  1 -
 7 files changed, 31 insertions(+), 44 deletions(-)

diff --git a/source/exhumed/src/exhumed.cpp b/source/exhumed/src/exhumed.cpp
index 5b91880a0..ce830df61 100644
--- a/source/exhumed/src/exhumed.cpp
+++ b/source/exhumed/src/exhumed.cpp
@@ -421,19 +421,6 @@ void GameInterface::Ticker()
             }
         }
 
-        if (localInput.actions & SB_CENTERVIEW)
-        {
-            bLockPan = false;
-            bPlayerPan = false;
-            //PlayerList[nLocalPlayer].q16horiz = IntToFixed(100);
-            nDestVertPan[nLocalPlayer] = IntToFixed(100);
-        }
-        if (localInput.actions & SB_TURNAROUND)
-        {
-            // todo
-        }
-
-
         sPlayerInput[nLocalPlayer].xVel = lPlayerXVel;
         sPlayerInput[nLocalPlayer].yVel = lPlayerYVel;
         // make weapon selection persist until it gets used up.
@@ -446,6 +433,11 @@ void GameInterface::Ticker()
         sPlayerInput[nLocalPlayer].actions = localInput.actions;
         if (oldactions & SB_CENTERVIEW) sPlayerInput[nLocalPlayer].actions |= SB_CENTERVIEW;
 
+        if (cl_syncinput)
+        {
+            ;// To be completed...
+        }
+
         Ra[nLocalPlayer].nTarget = besttarget;
 
         lLocalCodes = 0;
diff --git a/source/exhumed/src/input.cpp b/source/exhumed/src/input.cpp
index 7a6f61bfe..f0d7aa368 100644
--- a/source/exhumed/src/input.cpp
+++ b/source/exhumed/src/input.cpp
@@ -193,12 +193,17 @@ static void processMovement(ControlInfo* const hidInput)
     localInput.q16avel += tempinput.q16avel;
     localInput.q16horz += tempinput.q16horz;
 
-    if (!cl_syncinput && !nFreeze)
+    if (!cl_syncinput)
     {
         Player* pPlayer = &PlayerList[nLocalPlayer];
 
-        applylook(&pPlayer->q16angle, &pPlayer->q16look_ang, &pPlayer->q16rotscrnang, &pPlayer->spin, tempinput.q16avel, &sPlayerInput[nLocalPlayer].actions, scaleAdjust, false);
-        sethorizon(&pPlayer->q16horiz, tempinput.q16horz, &sPlayerInput[nLocalPlayer].actions, scaleAdjust);
+        if (!nFreeze)
+        {
+            applylook(&pPlayer->q16angle, &pPlayer->q16look_ang, &pPlayer->q16rotscrnang, &pPlayer->spin, tempinput.q16avel, &sPlayerInput[nLocalPlayer].actions, scaleAdjust, false);
+            sethorizon(&pPlayer->q16horiz, tempinput.q16horz, &sPlayerInput[nLocalPlayer].actions, scaleAdjust);
+        }
+
+        playerProcessHelpers(&pPlayer->q16angle, &pPlayer->angAdjust, &pPlayer->angTarget, &pPlayer->q16horiz, &pPlayer->horizAdjust, &pPlayer->horizTarget, scaleAdjust);
     }
 }
 
diff --git a/source/exhumed/src/player.cpp b/source/exhumed/src/player.cpp
index ac62afe90..6a914ef67 100644
--- a/source/exhumed/src/player.cpp
+++ b/source/exhumed/src/player.cpp
@@ -438,7 +438,6 @@ void RestartPlayer(short nPlayer)
     nXDamage[nPlayer] = 0;
 
     PlayerList[nPlayer].q16horiz = IntToFixed(100);
-    nDestVertPan[nPlayer] = IntToFixed(100);
     nBreathTimer[nPlayer] = 90;
 
     nTauntTimer[nPlayer] = RandomSize(3) + 3;
@@ -1033,10 +1032,11 @@ void FuncPlayer(int a, int nDamage, int nRun)
             {
                 if (nTotalPlayers <= 1)
                 {
-                    PlayerList[nPlayer].q16angle = IntToFixed(GetAngleToSprite(nPlayerSprite, nSpiritSprite) & kAngleMask);
-                    sprite[nPlayerSprite].ang = FixedToInt(PlayerList[nPlayer].q16angle);
+                    auto ang = GetAngleToSprite(nPlayerSprite, nSpiritSprite) & kAngleMask;
+                    playerSetAngle(&PlayerList[nPlayer].q16angle, &PlayerList[nPlayer].angTarget, ang);
+                    sprite[nPlayerSprite].ang = ang;
 
-                    PlayerList[nPlayer].q16horiz = IntToFixed(100);
+                    playerSetHoriz(&PlayerList[nPlayer].q16horiz, &PlayerList[nPlayer].horizTarget, 100);
 
                     lPlayerXVel = 0;
                     lPlayerYVel = 0;
@@ -1054,15 +1054,15 @@ void FuncPlayer(int a, int nDamage, int nRun)
                         StopLocalSound();
                         InitSpiritHead();
 
-                        nDestVertPan[nPlayer] = IntToFixed(100);
+                        playerSetHoriz(&PlayerList[nPlayer].q16horiz, &PlayerList[nPlayer].horizTarget, 100);
 
                         if (currentLevel->levelNumber == 11)
                         {
-                            nDestVertPan[nPlayer] += IntToFixed(46);
+                            playerAddHoriz(&PlayerList[nPlayer].q16horiz, &PlayerList[nPlayer].horizAdjust, 46);
                         }
                         else
                         {
-                            nDestVertPan[nPlayer] += IntToFixed(11);
+                            playerAddHoriz(&PlayerList[nPlayer].q16horiz, &PlayerList[nPlayer].horizAdjust, 11);
                         }
                     }
                 }
@@ -1091,7 +1091,7 @@ void FuncPlayer(int a, int nDamage, int nRun)
                         }
 
                         if (zVelB > 512 && !bLockPan) {
-                            nDestVertPan[nPlayer] = IntToFixed(100);
+                            playerSetHoriz(&PlayerList[nPlayer].q16horiz, &PlayerList[nPlayer].horizTarget, 100);
                         }
                     }
 
@@ -1190,19 +1190,9 @@ void FuncPlayer(int a, int nDamage, int nRun)
             }
 
 loc_1AB8E:
-            if (!bPlayerPan && !bLockPan)
+            if (cl_syncinput)
             {
-                fixed_t nPanVal = IntToFixed(spr_z - sprite[nPlayerSprite].z) / 32 + IntToFixed(100);
-
-                if (nPanVal < 0) {
-                    nPanVal = 0;
-                }
-                else if (nPanVal > IntToFixed(183))
-                {
-                    nPanVal = IntToFixed(183);
-                }
-
-                nDestVertPan[nPlayer] = nPanVal;
+                ;// To be completed.
             }
 
             playerX -= sprite[nPlayerSprite].x;
@@ -2793,15 +2783,16 @@ loc_1BD2E:
                 {
                     if (PlayerList[nPlayer].q16horiz < IntToFixed(100))
                     {
-                        PlayerList[nPlayer].q16horiz = IntToFixed(91);
+                        playerSetHoriz(&PlayerList[nPlayer].q16horiz, &PlayerList[nPlayer].horizTarget, 100);
                         eyelevel[nPlayer] -= (dVertPan[nPlayer] << 8);
                     }
                     else
                     {
-                        PlayerList[nPlayer].q16horiz += IntToFixed(dVertPan[nPlayer]);
-                        if (PlayerList[nPlayer].q16horiz >= IntToFixed(200))
+                        playerAddHoriz(&PlayerList[nPlayer].q16horiz, &PlayerList[nPlayer].horizAdjust, dVertPan[nPlayer]);
+
+                        if (PlayerList[nPlayer].q16horiz > gi->playerHorizMax())
                         {
-                            PlayerList[nPlayer].q16horiz = IntToFixed(199);
+                            playerSetHoriz(&PlayerList[nPlayer].q16horiz, &PlayerList[nPlayer].horizTarget, gi->playerHorizMax());
                         }
                         else if (PlayerList[nPlayer].q16horiz <= IntToFixed(100))
                         {
diff --git a/source/exhumed/src/player.h b/source/exhumed/src/player.h
index 2e07b0e29..ca0925093 100644
--- a/source/exhumed/src/player.h
+++ b/source/exhumed/src/player.h
@@ -75,6 +75,8 @@ struct Player
     fixed_t oq16look_ang, q16look_ang;
     fixed_t oq16rotscrnang, q16rotscrnang;
     fixed_t spin;
+    fixed_t angTarget, horizTarget;
+    double angAdjust, horizAdjust;
     vec3_t opos;
 };
 
diff --git a/source/exhumed/src/ramses.cpp b/source/exhumed/src/ramses.cpp
index 0e5ec0e04..f3a2f2647 100644
--- a/source/exhumed/src/ramses.cpp
+++ b/source/exhumed/src/ramses.cpp
@@ -209,7 +209,7 @@ void DoSpiritHead()
 {
     static short dimSectCount = 0;
 
-    PlayerList[0].q16horiz += (nDestVertPan[0] - PlayerList[0].q16horiz) / 4;
+    PlayerList[0].q16horiz += PlayerList[0].q16horiz / 4;
     TileFiles.InvalidateTile(kTileRamsesWorkTile);
     int totalclock = leveltime * 4;
 
diff --git a/source/exhumed/src/view.cpp b/source/exhumed/src/view.cpp
index 5c2f399da..c17d7b82d 100644
--- a/source/exhumed/src/view.cpp
+++ b/source/exhumed/src/view.cpp
@@ -36,7 +36,6 @@ short bSubTitles = true;
 
 int zbob;
 
-fixed_t nDestVertPan[kMaxPlayers] = { 0 };
 short dVertPan[kMaxPlayers];
 int nCamerax;
 int nCameray;
@@ -497,7 +496,6 @@ static SavegameHelper sgh("view",
     SV(viewz),
     SV(enemy),
     SV(nEnemyPal),
-    SA(nDestVertPan),
     SA(dVertPan),
     SA(nQuake),
     SV(g_interpolationCnt),
diff --git a/source/exhumed/src/view.h b/source/exhumed/src/view.h
index 56e3deef7..c537bd99a 100644
--- a/source/exhumed/src/view.h
+++ b/source/exhumed/src/view.h
@@ -39,7 +39,6 @@ void viewDoInterpolations(int smoothRatio);
 void viewUpdateInterpolations(void);
 void viewRestoreInterpolations(void);
 
-extern fixed_t nDestVertPan[];
 extern short dVertPan[];
 extern short nQuake[];
 extern int nCamerax;

From 5324d9680bb14ef793d93bfbcdc2f8b19b2207a8 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Mon, 21 Sep 2020 19:59:20 +1000
Subject: [PATCH 29/41] - Exhumed: Fixed a few missed constants when changing
 horizon to 100.

---
 source/exhumed/src/gun.cpp | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/source/exhumed/src/gun.cpp b/source/exhumed/src/gun.cpp
index 00789f297..4f3f47883 100644
--- a/source/exhumed/src/gun.cpp
+++ b/source/exhumed/src/gun.cpp
@@ -739,7 +739,7 @@ loc_flag:
                 // loc_27266:
                 case kWeaponSword:
                 {
-                    nHeight += (92 - FixedToInt(sPlayerInput[nPlayer].horizon)) << 6;
+                    nHeight += (100 - FixedToInt(sPlayerInput[nPlayer].horizon)) << 6;
 
                     theZ += nHeight;
 
@@ -844,7 +844,7 @@ loc_flag:
                 }
                 case kWeaponPistol:
                 {
-                    int var_50 = (FixedToInt(sPlayerInput[nPlayer].horizon) - 92) << 2;
+                    int var_50 = (FixedToInt(sPlayerInput[nPlayer].horizon) - 100) << 2;
                     nHeight -= var_50;
 
                     if (sPlayerInput[nPlayer].nTarget >= 0 && cl_autoaim)
@@ -859,7 +859,7 @@ loc_flag:
 
                 case kWeaponGrenade:
                 {
-                    ThrowGrenade(nPlayer, ebp, ebx, nHeight - 2560, FixedToInt(sPlayerInput[nPlayer].horizon) - 92);
+                    ThrowGrenade(nPlayer, ebp, ebx, nHeight - 2560, FixedToInt(sPlayerInput[nPlayer].horizon) - 100);
                     break;
                 }
                 case kWeaponStaff:

From d46787cd9684eb92d196f357803cb281327c5042 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Mon, 21 Sep 2020 20:37:09 +1000
Subject: [PATCH 30/41] - Exhumed: Restore synchronised input, hook up rest of
 backend input functions and get rid of `nPlayerDAng`.

---
 source/exhumed/src/exhumed.cpp  | 10 ++------
 source/exhumed/src/gameloop.cpp |  7 +++++-
 source/exhumed/src/gun.cpp      | 12 +++++++---
 source/exhumed/src/init.cpp     |  1 -
 source/exhumed/src/input.cpp    |  2 +-
 source/exhumed/src/player.cpp   | 42 +++++++++++++++++++++++----------
 source/exhumed/src/player.h     |  2 +-
 source/exhumed/src/ps_input.h   |  3 ++-
 source/exhumed/src/sequence.cpp |  2 +-
 source/exhumed/src/status.cpp   |  1 -
 source/exhumed/src/view.cpp     | 33 ++++++++++++++++++++++++--
 11 files changed, 82 insertions(+), 33 deletions(-)

diff --git a/source/exhumed/src/exhumed.cpp b/source/exhumed/src/exhumed.cpp
index ce830df61..842839342 100644
--- a/source/exhumed/src/exhumed.cpp
+++ b/source/exhumed/src/exhumed.cpp
@@ -339,7 +339,6 @@ void GameInterface::Ticker()
 	}
     else if (EndLevel == 0)
     {
-        nPlayerDAng += localInput.q16avel;
         inita &= kAngleMask;
 
         for (int i = 0; i < 4; i++)
@@ -433,17 +432,12 @@ void GameInterface::Ticker()
         sPlayerInput[nLocalPlayer].actions = localInput.actions;
         if (oldactions & SB_CENTERVIEW) sPlayerInput[nLocalPlayer].actions |= SB_CENTERVIEW;
 
-        if (cl_syncinput)
-        {
-            ;// To be completed...
-        }
+        sPlayerInput[nLocalPlayer].nAngle = localInput.q16avel;
+        sPlayerInput[nLocalPlayer].pan = localInput.q16horz;
 
         Ra[nLocalPlayer].nTarget = besttarget;
 
         lLocalCodes = 0;
-        nPlayerDAng = 0;
-
-        sPlayerInput[nLocalPlayer].horizon = PlayerList[nLocalPlayer].q16horiz;
 
         leveltime++;
         if (leveltime == 2) gameaction = ga_autosave;	// let the game run for 1 frame before saving.
diff --git a/source/exhumed/src/gameloop.cpp b/source/exhumed/src/gameloop.cpp
index dd73b7ab3..65856a30e 100644
--- a/source/exhumed/src/gameloop.cpp
+++ b/source/exhumed/src/gameloop.cpp
@@ -102,8 +102,13 @@ void GameInterface::Render()
         DrawClock();
     }
 
-    DrawView(calc_smoothratio());
+    double const smoothratio = calc_smoothratio();
+    double const look_anghalf = getHalfLookAng(PlayerList[nLocalPlayer].oq16look_ang, PlayerList[nLocalPlayer].q16look_ang, cl_syncinput, smoothratio);
+
+    DrawView(smoothratio);
     DrawStatusBar();
+    DrawCrosshair(MAXTILES, PlayerList[nLocalPlayer].nHealth >> 3, -look_anghalf, 1);
+
     if (paused && !M_Active())
     {
         auto tex = GStrings("TXTB_PAUSED");
diff --git a/source/exhumed/src/gun.cpp b/source/exhumed/src/gun.cpp
index 4f3f47883..52550f7ad 100644
--- a/source/exhumed/src/gun.cpp
+++ b/source/exhumed/src/gun.cpp
@@ -739,7 +739,7 @@ loc_flag:
                 // loc_27266:
                 case kWeaponSword:
                 {
-                    nHeight += (100 - FixedToInt(sPlayerInput[nPlayer].horizon)) << 6;
+                    nHeight += (100 - FixedToInt(PlayerList[nLocalPlayer].q16horiz)) << 6;
 
                     theZ += nHeight;
 
@@ -844,7 +844,7 @@ loc_flag:
                 }
                 case kWeaponPistol:
                 {
-                    int var_50 = (FixedToInt(sPlayerInput[nPlayer].horizon) - 100) << 2;
+                    int var_50 = (FixedToInt(PlayerList[nLocalPlayer].q16horiz) - 100) << 2;
                     nHeight -= var_50;
 
                     if (sPlayerInput[nPlayer].nTarget >= 0 && cl_autoaim)
@@ -859,7 +859,7 @@ loc_flag:
 
                 case kWeaponGrenade:
                 {
-                    ThrowGrenade(nPlayer, ebp, ebx, nHeight - 2560, FixedToInt(sPlayerInput[nPlayer].horizon) - 100);
+                    ThrowGrenade(nPlayer, ebp, ebx, nHeight - 2560, FixedToInt(PlayerList[nLocalPlayer].q16horiz) - 100);
                     break;
                 }
                 case kWeaponStaff:
@@ -985,6 +985,12 @@ void DrawWeapons(double smooth)
         nShade = sprite[PlayerList[nLocalPlayer].nSprite].shade;
     }
 
+    double const look_anghalf = getHalfLookAng(PlayerList[nLocalPlayer].oq16look_ang, PlayerList[nLocalPlayer].q16look_ang, cl_syncinput, smooth);
+    double const looking_arc = fabs(look_anghalf) / 4.5;
+
+    xOffset -= look_anghalf;
+    yOffset += looking_arc;
+
     seq_DrawGunSequence(var_28, PlayerList[nLocalPlayer].field_3FOUR, xOffset, yOffset, nShade, nPal);
 
     if (nWeapon != kWeaponM60)
diff --git a/source/exhumed/src/init.cpp b/source/exhumed/src/init.cpp
index 9283f061d..404022984 100644
--- a/source/exhumed/src/init.cpp
+++ b/source/exhumed/src/init.cpp
@@ -196,7 +196,6 @@ void InitLevel(int level) // todo: use a map record
     totalmoves = 0;
     GrabPalette();
     ResetMoveFifo();
-    nPlayerDAng = 0;
     lPlayerXVel = 0;
     lPlayerYVel = 0;
     movefifopos = movefifoend;
diff --git a/source/exhumed/src/input.cpp b/source/exhumed/src/input.cpp
index f0d7aa368..be6bcdf4f 100644
--- a/source/exhumed/src/input.cpp
+++ b/source/exhumed/src/input.cpp
@@ -201,6 +201,7 @@ static void processMovement(ControlInfo* const hidInput)
         {
             applylook(&pPlayer->q16angle, &pPlayer->q16look_ang, &pPlayer->q16rotscrnang, &pPlayer->spin, tempinput.q16avel, &sPlayerInput[nLocalPlayer].actions, scaleAdjust, false);
             sethorizon(&pPlayer->q16horiz, tempinput.q16horz, &sPlayerInput[nLocalPlayer].actions, scaleAdjust);
+            UpdatePlayerSpriteAngle(pPlayer);
         }
 
         playerProcessHelpers(&pPlayer->q16angle, &pPlayer->angAdjust, &pPlayer->angTarget, &pPlayer->q16horiz, &pPlayer->horizAdjust, &pPlayer->horizTarget, scaleAdjust);
@@ -220,7 +221,6 @@ void GameInterface::GetInput(InputPacket* packet, ControlInfo* const hidInput)
     {
         lPlayerYVel = 0;
         lPlayerXVel = 0;
-        nPlayerDAng = 0;
         return;
     }
 
diff --git a/source/exhumed/src/player.cpp b/source/exhumed/src/player.cpp
index 6a914ef67..f8b957127 100644
--- a/source/exhumed/src/player.cpp
+++ b/source/exhumed/src/player.cpp
@@ -51,7 +51,6 @@ struct PlayerSave
 
 int lPlayerXVel = 0;
 int lPlayerYVel = 0;
-fixed_t nPlayerDAng = 0;
 short obobangle = 0, bobangle  = 0;
 short bPlayerPan = 0;
 short bLockPan  = 0;
@@ -338,6 +337,10 @@ void RestartPlayer(short nPlayer)
     }
 
     PlayerList[nPlayer].opos = sprite[nSprite].pos;
+    PlayerList[nPlayer].oq16angle = PlayerList[nPlayer].q16angle;
+    PlayerList[nPlayer].oq16horiz = PlayerList[nPlayer].q16horiz;
+    PlayerList[nPlayer].oq16look_ang = PlayerList[nPlayer].q16look_ang = 0;
+    PlayerList[nPlayer].oq16rotscrnang = PlayerList[nPlayer].q16rotscrnang = 0;
 
     nPlayerFloorSprite[nPlayer] = floorspr;
 
@@ -437,7 +440,7 @@ void RestartPlayer(short nPlayer)
     nYDamage[nPlayer] = 0;
     nXDamage[nPlayer] = 0;
 
-    PlayerList[nPlayer].q16horiz = IntToFixed(100);
+    PlayerList[nPlayer].oq16horiz = PlayerList[nPlayer].q16horiz = IntToFixed(100);
     nBreathTimer[nPlayer] = 90;
 
     nTauntTimer[nPlayer] = RandomSize(3) + 3;
@@ -454,7 +457,6 @@ void RestartPlayer(short nPlayer)
     if (nPlayer == nLocalPlayer)
     {
         nLocalSpr = nSprite;
-        nPlayerDAng = 0;
 
         SetMagicFrame();
         RestoreGreenPal();
@@ -538,7 +540,7 @@ void StartDeathSeq(int nPlayer, int nVal)
 
     StopFiringWeapon(nPlayer);
 
-    PlayerList[nPlayer].q16horiz = IntToFixed(100);
+    PlayerList[nPlayer].oq16horiz = PlayerList[nPlayer].q16horiz = IntToFixed(100);
     oeyelevel[nPlayer] = eyelevel[nPlayer] = -14080;
     nPlayerInvisible[nPlayer] = 0;
     dVertPan[nPlayer] = 15;
@@ -703,6 +705,11 @@ static void pickupMessage(int no)
     }
 }
 
+void UpdatePlayerSpriteAngle(Player* pPlayer)
+{
+    sprite[pPlayer->nSprite].ang = FixedToInt(pPlayer->q16angle);
+}
+
 void FuncPlayer(int a, int nDamage, int nRun)
 {
     int var_48 = 0;
@@ -724,6 +731,10 @@ void FuncPlayer(int a, int nDamage, int nRun)
     short nSprite2;
 
     PlayerList[nPlayer].opos = sprite[nPlayerSprite].pos;
+    PlayerList[nPlayer].oq16angle = PlayerList[nPlayer].q16angle;
+    PlayerList[nPlayer].oq16horiz = PlayerList[nPlayer].q16horiz;
+    PlayerList[nPlayer].oq16look_ang = PlayerList[nPlayer].q16look_ang;
+    PlayerList[nPlayer].oq16rotscrnang = PlayerList[nPlayer].q16rotscrnang;
     oeyelevel[nPlayer] = eyelevel[nPlayer];
 
     switch (nMessage)
@@ -937,7 +948,12 @@ void FuncPlayer(int a, int nDamage, int nRun)
             }
 
             // loc_1A494:
-            sprite[nPlayerSprite].ang = FixedToInt(PlayerList[nPlayer].q16angle);
+            if (cl_syncinput)
+            {
+                Player* pPlayer = &PlayerList[nPlayer];
+                applylook(&pPlayer->q16angle, &pPlayer->q16look_ang, &pPlayer->q16rotscrnang, &pPlayer->spin, sPlayerInput[nPlayer].nAngle, &sPlayerInput[nLocalPlayer].actions, 1, false);
+                UpdatePlayerSpriteAngle(pPlayer);
+            }
 
             // sprite[nPlayerSprite].zvel is modified within Gravity()
             short zVel = sprite[nPlayerSprite].zvel;
@@ -1034,9 +1050,11 @@ void FuncPlayer(int a, int nDamage, int nRun)
                 {
                     auto ang = GetAngleToSprite(nPlayerSprite, nSpiritSprite) & kAngleMask;
                     playerSetAngle(&PlayerList[nPlayer].q16angle, &PlayerList[nPlayer].angTarget, ang);
+                    PlayerList[nPlayer].oq16angle = PlayerList[nPlayer].q16angle;
                     sprite[nPlayerSprite].ang = ang;
 
                     playerSetHoriz(&PlayerList[nPlayer].q16horiz, &PlayerList[nPlayer].horizTarget, 100);
+                    PlayerList[nPlayer].oq16horiz = PlayerList[nPlayer].q16horiz;
 
                     lPlayerXVel = 0;
                     lPlayerYVel = 0;
@@ -1045,8 +1063,6 @@ void FuncPlayer(int a, int nDamage, int nRun)
                     sprite[nPlayerSprite].yvel = 0;
                     sprite[nPlayerSprite].zvel = 0;
 
-                    nPlayerDAng = 0;
-
                     if (nFreeze < 1)
                     {
                         nFreeze = 1;
@@ -1190,11 +1206,6 @@ void FuncPlayer(int a, int nDamage, int nRun)
             }
 
 loc_1AB8E:
-            if (cl_syncinput)
-            {
-                ;// To be completed.
-            }
-
             playerX -= sprite[nPlayerSprite].x;
             playerY -= sprite[nPlayerSprite].y;
 
@@ -2663,6 +2674,12 @@ loc_1BD2E:
                     PlayerList[nPlayer].nAction = nActionB;
                     PlayerList[nPlayer].field_2 = 0;
                 }
+
+                if (cl_syncinput)
+                {
+                    Player* pPlayer = &PlayerList[nPlayer];
+                    sethorizon(&pPlayer->q16horiz, sPlayerInput[nPlayer].pan, &sPlayerInput[nLocalPlayer].actions, 1);
+                }
             }
             else // else, player's health is less than 0
             {
@@ -2833,7 +2850,6 @@ loc_1BD2E:
 static SavegameHelper sgh("player",
     SV(lPlayerXVel),
     SV(lPlayerYVel),
-    SV(nPlayerDAng),
     SV(obobangle),
     SV(bobangle),
     SV(bPlayerPan),
diff --git a/source/exhumed/src/player.h b/source/exhumed/src/player.h
index ca0925093..40293001a 100644
--- a/source/exhumed/src/player.h
+++ b/source/exhumed/src/player.h
@@ -43,7 +43,6 @@ extern int nLocalPlayer;
 
 extern int lPlayerXVel;
 extern int lPlayerYVel;
-extern fixed_t nPlayerDAng;
 
 struct Player
 {
@@ -127,6 +126,7 @@ short GetPlayerFromSprite(short nSprite);
 void SetPlayerMummified(int nPlayer, int bIsMummified);
 int AddAmmo(int nPlayer, int nWeapon, int nAmmoAmount);
 void ShootStaff(int nPlayer);
+void UpdatePlayerSpriteAngle(Player* pPlayer);
 
 END_PS_NS
 
diff --git a/source/exhumed/src/ps_input.h b/source/exhumed/src/ps_input.h
index 564fc7359..9ecc7f9f8 100644
--- a/source/exhumed/src/ps_input.h
+++ b/source/exhumed/src/ps_input.h
@@ -38,7 +38,8 @@ struct PlayerInput
     int yVel;
     uint16_t buttons;
     short nTarget;
-    fixed_t horizon;
+    fixed_t nAngle;
+    fixed_t pan;
     int8_t nItem;
     ESyncBits actions;
 
diff --git a/source/exhumed/src/sequence.cpp b/source/exhumed/src/sequence.cpp
index 64914b19b..5c795ed23 100644
--- a/source/exhumed/src/sequence.cpp
+++ b/source/exhumed/src/sequence.cpp
@@ -376,7 +376,7 @@ void seq_DrawPilotLightSeq(double xOffset, double yOffset)
             double x = ChunkXpos[nFrameBase] + (160 + xOffset);
             double y = ChunkYpos[nFrameBase] + (100 + yOffset);
 
-            hud_drawsprite(x, y, 65536, fmod(-2 * FixedToFloat(nPlayerDAng), kAngleMask + 1), nTile, 0, 0, 1);
+            hud_drawsprite(x, y, 65536, fmod(-2 * FixedToFloat(PlayerList[nLocalPlayer].q16angle), kAngleMask + 1), nTile, 0, 0, 1);
             nFrameBase++;
         }
     }
diff --git a/source/exhumed/src/status.cpp b/source/exhumed/src/status.cpp
index 930b0c86b..dde120f70 100644
--- a/source/exhumed/src/status.cpp
+++ b/source/exhumed/src/status.cpp
@@ -1002,7 +1002,6 @@ void DrawStatusBar()
     }
     DExhumedStatusBar sbar;
     sbar.Draw();
-    DrawCrosshair(MAXTILES, PlayerList[nLocalPlayer].nHealth >> 3, 0, 1);
 }
 
 
diff --git a/source/exhumed/src/view.cpp b/source/exhumed/src/view.cpp
index c17d7b82d..a5702401f 100644
--- a/source/exhumed/src/view.cpp
+++ b/source/exhumed/src/view.cpp
@@ -237,6 +237,9 @@ void DrawView(double smoothRatio, bool sceneonly)
     short nSector;
     fixed_t nAngle;
     fixed_t pan;
+    fixed_t q16rotscrnang;
+
+    fixed_t dang = IntToFixed(1024);
 
     zbob = Sin(2 * bobangle) >> 3;
 
@@ -276,13 +279,32 @@ void DrawView(double smoothRatio, bool sceneonly)
                 + interpolate16(oeyelevel[nLocalPlayer], eyelevel[nLocalPlayer], smoothRatio);
         nSector = nPlayerViewSect[nLocalPlayer];
         updatesector(playerX, playerY, &nSector);
-        nAngle  = PlayerList[nLocalPlayer].q16angle;
+
+        if (!cl_syncinput)
+        {
+            nAngle = PlayerList[nLocalPlayer].q16angle + PlayerList[nLocalPlayer].q16look_ang;
+            q16rotscrnang = PlayerList[nLocalPlayer].q16rotscrnang;
+        }
+        else
+        {
+            fixed_t oang, ang;
+
+            oang = PlayerList[nLocalPlayer].oq16angle + PlayerList[nLocalPlayer].oq16look_ang;
+            ang = PlayerList[nLocalPlayer].q16angle + PlayerList[nLocalPlayer].q16look_ang;
+            nAngle = oang + xs_CRoundToInt(fmulscale16(((ang + dang - oang) & 0x7FFFFFF) - dang, smoothRatio));
+
+            oang = PlayerList[nLocalPlayer].oq16rotscrnang + PlayerList[nLocalPlayer].oq16rotscrnang;
+            ang = PlayerList[nLocalPlayer].q16rotscrnang + PlayerList[nLocalPlayer].q16rotscrnang;
+            q16rotscrnang = oang + xs_CRoundToInt(fmulscale16(((ang + dang - oang) & 0x7FFFFFF) - dang, smoothRatio));
+        }
 
         if (!bCamera)
         {
             sprite[nPlayerSprite].cstat |= CSTAT_SPRITE_INVISIBLE;
             sprite[nDoppleSprite[nLocalPlayer]].cstat |= CSTAT_SPRITE_INVISIBLE;
         }
+
+        renderSetRollAngle(FixedToFloat(q16rotscrnang));
     }
 
     nCameraa = nAngle;
@@ -299,7 +321,14 @@ void DrawView(double smoothRatio, bool sceneonly)
             viewz = playerZ + nQuake[nLocalPlayer];
             int floorZ = sector[sprite[nPlayerSprite].sectnum].floorz;
 
-            pan = PlayerList[nLocalPlayer].q16horiz;
+            if (!cl_syncinput)
+            {
+                pan = PlayerList[nLocalPlayer].q16horiz;
+            }
+            else
+            {
+                pan = PlayerList[nLocalPlayer].oq16horiz + xs_CRoundToInt(fmulscale16(PlayerList[nLocalPlayer].q16horiz - PlayerList[nLocalPlayer].oq16horiz, smoothRatio));
+            }
 
             if (viewz > floorZ)
                 viewz = floorZ;

From f8210ef8eebe566b5e8b045e9eb36431bc1437e5 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Mon, 21 Sep 2020 20:44:02 +1000
Subject: [PATCH 31/41] - Exhumed: Clean out some old bools regarding old
 panning code.

---
 source/exhumed/src/exhumed.cpp |  4 ----
 source/exhumed/src/input.cpp   |  3 ---
 source/exhumed/src/player.cpp  | 11 ++---------
 3 files changed, 2 insertions(+), 16 deletions(-)

diff --git a/source/exhumed/src/exhumed.cpp b/source/exhumed/src/exhumed.cpp
index 842839342..8cd209610 100644
--- a/source/exhumed/src/exhumed.cpp
+++ b/source/exhumed/src/exhumed.cpp
@@ -50,10 +50,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 BEGIN_PS_NS
 
-extern short bPlayerPan;
-extern short bLockPan;
-
-
 static MapRecord* NextMap;
 
 void uploadCinemaPalettes();
diff --git a/source/exhumed/src/input.cpp b/source/exhumed/src/input.cpp
index be6bcdf4f..4504fe808 100644
--- a/source/exhumed/src/input.cpp
+++ b/source/exhumed/src/input.cpp
@@ -25,9 +25,6 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
 
 BEGIN_PS_NS
 
-extern short bPlayerPan;
-extern short bLockPan;
-
 static int turn;
 static int counter;
 
diff --git a/source/exhumed/src/player.cpp b/source/exhumed/src/player.cpp
index f8b957127..fa2293d4b 100644
--- a/source/exhumed/src/player.cpp
+++ b/source/exhumed/src/player.cpp
@@ -52,8 +52,6 @@ struct PlayerSave
 int lPlayerXVel = 0;
 int lPlayerYVel = 0;
 short obobangle = 0, bobangle  = 0;
-short bPlayerPan = 0;
-short bLockPan  = 0;
 
 static actionSeq ActionSeq[] = {
     {18,  0}, {0,   0}, {9,   0}, {27,  0}, {63,  0},
@@ -460,9 +458,6 @@ void RestartPlayer(short nPlayer)
 
         SetMagicFrame();
         RestoreGreenPal();
-
-        bPlayerPan = 0;
-        bLockPan = 0;
     }
 
     sprintf(playerNames[nPlayer], "JOE%d", nPlayer);
@@ -1106,8 +1101,8 @@ void FuncPlayer(int a, int nDamage, int nRun)
                             zVelB = -zVelB;
                         }
 
-                        if (zVelB > 512 && !bLockPan) {
-                            playerSetHoriz(&PlayerList[nPlayer].q16horiz, &PlayerList[nPlayer].horizTarget, 100);
+                        if (zVelB > 512 && PlayerList[nPlayer].q16angle != IntToFixed(100) && !(sPlayerInput[nPlayer].actions & (SB_AIM_UP|SB_AIM_DOWN))) {
+                            sPlayerInput[nPlayer].actions |= SB_CENTERVIEW;
                         }
                     }
 
@@ -2852,8 +2847,6 @@ static SavegameHelper sgh("player",
     SV(lPlayerYVel),
     SV(obobangle),
     SV(bobangle),
-    SV(bPlayerPan),
-    SV(bLockPan),
     SV(nStandHeight),
     SV(PlayerCount),
     SV(nNetStartSprites),

From 5c097c4a7481753cd6a6fdb19ed6cca7064480a1 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Tue, 22 Sep 2020 07:13:48 +1000
Subject: [PATCH 32/41] - Blood: Fix player's slope with horizon now based on
 100 and not 0.

---
 source/blood/src/player.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index b1d072023..4c46c0ffa 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1586,7 +1586,7 @@ void ProcessInput(PLAYER *pPlayer)
         if (klabs(pPlayer->q16slopehoriz) < 4)
             pPlayer->q16slopehoriz = 0;
     }
-    pPlayer->slope = (-FixedToInt(pPlayer->q16horiz))<<7;
+    pPlayer->slope = -(FixedToInt(pPlayer->q16horiz) - 100)<<7;
     if (pInput->actions & SB_INVPREV)
     {
         pInput->actions&= ~SB_INVPREV;

From 31bb9e4d8d9cf62982169733b19f5061c0d081ad Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Tue, 22 Sep 2020 07:46:30 +1000
Subject: [PATCH 33/41] - Blood: Open up MAXHORIZVEL to SW levels to compensate
 for removal of q16horz scaling in previous horizon code.

---
 source/blood/src/controls.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/blood/src/controls.cpp b/source/blood/src/controls.cpp
index b69c0a7d7..a2108ca33 100644
--- a/source/blood/src/controls.cpp
+++ b/source/blood/src/controls.cpp
@@ -38,7 +38,7 @@ enum
 {
     MAXFVEL     = 2048,
     MAXSVEL     = 2048,
-    MAXHORIZVEL = 32
+    MAXHORIZVEL = 128
 };
 
 void UpdatePlayerSpriteAngle(PLAYER* pPlayer);

From 46810ec490f336fea4de7dfe840daa9512b429a6 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Tue, 22 Sep 2020 15:51:48 +1000
Subject: [PATCH 34/41] - fix a few places where Q16.16 values were downscaled,
 only to be scaled back up again.

---
 source/blood/src/player.cpp     | 2 +-
 source/blood/src/prediction.cpp | 2 +-
 source/exhumed/src/gun.cpp      | 4 ++--
 3 files changed, 4 insertions(+), 4 deletions(-)

diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index 4c46c0ffa..5ae6c8325 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1586,7 +1586,7 @@ void ProcessInput(PLAYER *pPlayer)
         if (klabs(pPlayer->q16slopehoriz) < 4)
             pPlayer->q16slopehoriz = 0;
     }
-    pPlayer->slope = -(FixedToInt(pPlayer->q16horiz) - 100)<<7;
+    pPlayer->slope = -(pPlayer->q16horiz - IntToFixed(100)) >> 9;
     if (pInput->actions & SB_INVPREV)
     {
         pInput->actions&= ~SB_INVPREV;
diff --git a/source/blood/src/prediction.cpp b/source/blood/src/prediction.cpp
index 6908981c7..a3925808b 100644
--- a/source/blood/src/prediction.cpp
+++ b/source/blood/src/prediction.cpp
@@ -263,7 +263,7 @@ static void fakeProcessInput(PLAYER *pPlayer, InputPacket *pInput)
         if (klabs(predict.at28) < 4)
             predict.at28 = 0;
     }
-    predict.at2c = (-FixedToInt(predict.at24))<<7;
+    predict.at2c = -(predict.at24 - IntToFixed(100)) >> 9;
 }
 
 void fakePlayerProcess(PLAYER *pPlayer, InputPacket *pInput)
diff --git a/source/exhumed/src/gun.cpp b/source/exhumed/src/gun.cpp
index 52550f7ad..c6afa4fb8 100644
--- a/source/exhumed/src/gun.cpp
+++ b/source/exhumed/src/gun.cpp
@@ -739,7 +739,7 @@ loc_flag:
                 // loc_27266:
                 case kWeaponSword:
                 {
-                    nHeight += (100 - FixedToInt(PlayerList[nLocalPlayer].q16horiz)) << 6;
+                    nHeight += (IntToFixed(100) - PlayerList[nLocalPlayer].q16horiz) >> 10;
 
                     theZ += nHeight;
 
@@ -844,7 +844,7 @@ loc_flag:
                 }
                 case kWeaponPistol:
                 {
-                    int var_50 = (FixedToInt(PlayerList[nLocalPlayer].q16horiz) - 100) << 2;
+                    int var_50 = (PlayerList[nLocalPlayer].q16horiz - IntToFixed(100)) >> 14;
                     nHeight -= var_50;
 
                     if (sPlayerInput[nPlayer].nTarget >= 0 && cl_autoaim)

From d338e608413845a3f78dc4cdf82acf671976ceba Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Tue, 22 Sep 2020 16:06:12 +1000
Subject: [PATCH 35/41] - Exhumed: Hook up crouch action to `applylook()` that
 was forgotten during initial implementation.

---
 source/exhumed/src/input.cpp  | 2 +-
 source/exhumed/src/player.cpp | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/exhumed/src/input.cpp b/source/exhumed/src/input.cpp
index 4504fe808..d8a99829f 100644
--- a/source/exhumed/src/input.cpp
+++ b/source/exhumed/src/input.cpp
@@ -196,7 +196,7 @@ static void processMovement(ControlInfo* const hidInput)
 
         if (!nFreeze)
         {
-            applylook(&pPlayer->q16angle, &pPlayer->q16look_ang, &pPlayer->q16rotscrnang, &pPlayer->spin, tempinput.q16avel, &sPlayerInput[nLocalPlayer].actions, scaleAdjust, false);
+            applylook(&pPlayer->q16angle, &pPlayer->q16look_ang, &pPlayer->q16rotscrnang, &pPlayer->spin, tempinput.q16avel, &sPlayerInput[nLocalPlayer].actions, scaleAdjust, eyelevel[nLocalPlayer] > -14080);
             sethorizon(&pPlayer->q16horiz, tempinput.q16horz, &sPlayerInput[nLocalPlayer].actions, scaleAdjust);
             UpdatePlayerSpriteAngle(pPlayer);
         }
diff --git a/source/exhumed/src/player.cpp b/source/exhumed/src/player.cpp
index fa2293d4b..26cd14d6f 100644
--- a/source/exhumed/src/player.cpp
+++ b/source/exhumed/src/player.cpp
@@ -946,7 +946,7 @@ void FuncPlayer(int a, int nDamage, int nRun)
             if (cl_syncinput)
             {
                 Player* pPlayer = &PlayerList[nPlayer];
-                applylook(&pPlayer->q16angle, &pPlayer->q16look_ang, &pPlayer->q16rotscrnang, &pPlayer->spin, sPlayerInput[nPlayer].nAngle, &sPlayerInput[nLocalPlayer].actions, 1, false);
+                applylook(&pPlayer->q16angle, &pPlayer->q16look_ang, &pPlayer->q16rotscrnang, &pPlayer->spin, sPlayerInput[nPlayer].nAngle, &sPlayerInput[nLocalPlayer].actions, 1, eyelevel[nLocalPlayer] > -14080);
                 UpdatePlayerSpriteAngle(pPlayer);
             }
 

From 2bc4291817717ce1b90d7656b8a641c11c1d6b01 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Tue, 22 Sep 2020 19:53:24 +1000
Subject: [PATCH 36/41] - menudef.txt: Remove a few Exhumed exclusions.

* These didn't seem to apply (as in, these functions worked out of the box for Exhumed) so I forgot about checking this.
---
 wadsrc/static/engine/menudef.txt | 12 +++---------
 1 file changed, 3 insertions(+), 9 deletions(-)

diff --git a/wadsrc/static/engine/menudef.txt b/wadsrc/static/engine/menudef.txt
index 1ea064533..0f650f468 100644
--- a/wadsrc/static/engine/menudef.txt
+++ b/wadsrc/static/engine/menudef.txt
@@ -498,10 +498,7 @@ OptionMenu "ActionControlsMenu"// protected
 	StaticText ""
 	Control    "$CNTRLMNU_TURNLEFT"         , "+turn_left"
 	Control    "$CNTRLMNU_TURNRIGHT"        , "+turn_right"
-	ifnotgame(Exhumed)
-	{
-		Control    "$CNTRLMNU_TURN180"          , "turnaround"
-	}
+	Control    "$CNTRLMNU_TURN180"          , "turnaround"
 
 	StaticText ""
 	Control    "$CNTRLMNU_JUMP"             , "+jump"
@@ -520,11 +517,8 @@ OptionMenu "ActionControlsMenu"// protected
 	Control    "$CNTRLMNU_AIMDOWN"         , "+aim_down"
 	Control    "$CNTRLMNU_LOOKUP"           , "+look_up"
 	Control    "$CNTRLMNU_LOOKDOWN"         , "+look_down"
-	ifnotgame(Exhumed)
-	{
-		Control    "$CNTRLMNU_LOOKLEFT"           , "+look_left"
-		Control    "$CNTRLMNU_LOOKRIGHT"         , "+look_right"
-	}
+	Control    "$CNTRLMNU_LOOKLEFT"           , "+look_left"
+	Control    "$CNTRLMNU_LOOKRIGHT"         , "+look_right"
 	Control    "$CNTRLMNU_CENTERVIEW"       , "centerview"
 
 	StaticText ""

From 77265b31385b8e7f0483869523a240d79fffa466 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Tue, 22 Sep 2020 20:02:58 +1000
Subject: [PATCH 37/41] - SW: Fix regression from
 1900cf1fcc08dd316c327b4edc7cf6faf2232958 where the conversion from
 `GetDeltaAngle()` to `getincangle()` got muddled up.

---
 source/sw/src/weapon.cpp | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/sw/src/weapon.cpp b/source/sw/src/weapon.cpp
index cfdd48e11..e516181fe 100644
--- a/source/sw/src/weapon.cpp
+++ b/source/sw/src/weapon.cpp
@@ -8310,9 +8310,9 @@ MissileSeek(int16_t Weapon, int16_t delay_tics, int16_t aware_range/*, int16_t d
         hp = &sprite[User[Weapon]->WpnGoal];
 
         // move to correct angle
-        ang2tgt = getangle(hp->y - sp->y, hp->x - sp->x);
+        ang2tgt = getangle(hp->x - sp->x, hp->y - sp->y);
 
-        delta_ang = getincangle(sp->ang, ang2tgt);
+        delta_ang = getincangle(ang2tgt, sp->ang);
 
         if (labs(delta_ang) > 32)
         {

From ac635ebd0fc3ad26e5a571f92753d75304bc3472 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Tue, 22 Sep 2020 22:50:27 +1000
Subject: [PATCH 38/41] - Blood/RR: Replace a few `playerSet*()` calls with
 `playerAdd*()`. Because the setters continually set until target is reached,
 can cause some perceivable smoothness issues if ticker re-targets's player's
 angle/horizon before input code has reached target.

---
 source/blood/src/player.cpp        |  7 ++++---
 source/games/duke/src/player_r.cpp | 26 ++++++++++++++++++--------
 2 files changed, 22 insertions(+), 11 deletions(-)

diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index 5ae6c8325..6d0fd2781 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1358,12 +1358,13 @@ void ProcessInput(PLAYER *pPlayer)
         char bSeqStat = playerSeqPlaying(pPlayer, 16);
         if (pPlayer->fraggerId != -1)
         {
-            pPlayer->angold = pSprite->ang = getangle(sprite[pPlayer->fraggerId].x - pSprite->x, sprite[pPlayer->fraggerId].y - pSprite->y);
-            playerSetAngle(&pPlayer->q16ang, &pPlayer->angTarget, pSprite->ang);
+            fixed_t fraggerAng = gethiq16angle(sprite[pPlayer->fraggerId].x - pSprite->x, sprite[pPlayer->fraggerId].y - pSprite->y);
+            pPlayer->angold = pSprite->ang = FixedToInt(fraggerAng);
+            playerAddAngle(&pPlayer->q16ang, &pPlayer->angAdjust, FixedToFloat(getincangleq16(pPlayer->q16ang, fraggerAng)));
         }
         pPlayer->deathTime += 4;
         if (!bSeqStat)
-            playerSetHoriz(&pPlayer->q16horiz, &pPlayer->horizTarget, FixedToFloat(mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime<<3, 1024))>>15), IntToFixed(120))));
+            playerAddHoriz(&pPlayer->q16horiz, &pPlayer->horizAdjust, FixedToFloat(mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime<<3, 1024))>>15), IntToFixed(120)) - pPlayer->q16horiz));
         if (pPlayer->curWeapon)
             pInput->setNewWeapon(pPlayer->curWeapon);
         if (pInput->actions & SB_OPEN)
diff --git a/source/games/duke/src/player_r.cpp b/source/games/duke/src/player_r.cpp
index 978d07198..e6c885e19 100644
--- a/source/games/duke/src/player_r.cpp
+++ b/source/games/duke/src/player_r.cpp
@@ -1717,9 +1717,10 @@ static void onMotorcycle(int snum, ESyncBits &actions)
 				p->moto_drink--;
 		}
 	}
+
+	int horiz = 0;
 	if (p->TurbCount)
 	{
-		int horiz;
 		if (p->TurbCount <= 1)
 		{
 			horiz = 100;
@@ -1733,7 +1734,6 @@ static void onMotorcycle(int snum, ESyncBits &actions)
 			p->TurbCount--;
 			p->moto_drink = (krand() & 3) - 2;
 		}
-		playerSetHoriz(&p->q16horiz, &p->horizTarget, horiz);
 	}
 	else if (p->VBumpTarget > p->VBumpNow)
 	{
@@ -1743,7 +1743,7 @@ static void onMotorcycle(int snum, ESyncBits &actions)
 			p->VBumpNow++;
 		if (p->VBumpTarget < p->VBumpNow)
 			p->VBumpNow = p->VBumpTarget;
-		playerSetHoriz(&p->q16horiz, &p->horizTarget, 100 + p->VBumpNow / 3);
+		horiz = 100 + p->VBumpNow / 3;
 	}
 	else if (p->VBumpTarget < p->VBumpNow)
 	{
@@ -1753,13 +1753,18 @@ static void onMotorcycle(int snum, ESyncBits &actions)
 			p->VBumpNow--;
 		if (p->VBumpTarget > p->VBumpNow)
 			p->VBumpNow = p->VBumpTarget;
-		playerSetHoriz(&p->q16horiz, &p->horizTarget, 100 + p->VBumpNow / 3);
+		horiz = 100 + p->VBumpNow / 3;
 	}
 	else
 	{
 		p->VBumpTarget = 0;
 		p->moto_bump_fast = 0;
 	}
+	if (horiz != 0)
+	{
+		playerAddHoriz(&p->q16horiz, &p->horizAdjust, horiz - FixedToFloat(p->q16horiz));
+	}
+
 	if (p->MotoSpeed >= 20 && p->on_ground == 1 && (var74 || var7c))
 	{
 		short var8c, var90, var94, var98;
@@ -2041,9 +2046,10 @@ static void onBoat(int snum, ESyncBits &actions)
 				p->moto_drink--;
 		}
 	}
+
+	int horiz = 0;
 	if (p->TurbCount)
 	{
-		int horiz;
 		if (p->TurbCount <= 1)
 		{
 			horiz = 100;
@@ -2057,7 +2063,6 @@ static void onBoat(int snum, ESyncBits &actions)
 			p->TurbCount--;
 			p->moto_drink = (krand() & 3) - 2;
 		}
-		playerSetHoriz(&p->q16horiz, &p->horizTarget, horiz);
 	}
 	else if (p->VBumpTarget > p->VBumpNow)
 	{
@@ -2067,7 +2072,7 @@ static void onBoat(int snum, ESyncBits &actions)
 			p->VBumpNow++;
 		if (p->VBumpTarget < p->VBumpNow)
 			p->VBumpNow = p->VBumpTarget;
-		playerSetHoriz(&p->q16horiz, &p->horizTarget, 100 + p->VBumpNow / 3);
+		horiz = 100 + p->VBumpNow / 3;
 	}
 	else if (p->VBumpTarget < p->VBumpNow)
 	{
@@ -2077,13 +2082,18 @@ static void onBoat(int snum, ESyncBits &actions)
 			p->VBumpNow--;
 		if (p->VBumpTarget > p->VBumpNow)
 			p->VBumpNow = p->VBumpTarget;
-		playerSetHoriz(&p->q16horiz, &p->horizTarget, 100 + p->VBumpNow / 3);
+		horiz = 100 + p->VBumpNow / 3;
 	}
 	else
 	{
 		p->VBumpTarget = 0;
 		p->moto_bump_fast = 0;
 	}
+	if (horiz != 0)
+	{
+		playerAddHoriz(&p->q16horiz, &p->horizAdjust, horiz - FixedToFloat(p->q16horiz));
+	}
+
 	if (p->MotoSpeed > 0 && p->on_ground == 1 && (varbc || varc4))
 	{
 		short vard4, vard8, vardc, vare0;

From 639ad46af12b9b57a769b3e8fca18d44639ecc28 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Tue, 22 Sep 2020 22:52:17 +1000
Subject: [PATCH 39/41] - Blood: When dead, ensure player's horizon scales to
 max horizon value like before (old horizon was based on 0, not 100, so
 100+120 is gi->playerHorizMax().

---
 source/blood/src/player.cpp | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/source/blood/src/player.cpp b/source/blood/src/player.cpp
index 6d0fd2781..6ebadac23 100644
--- a/source/blood/src/player.cpp
+++ b/source/blood/src/player.cpp
@@ -1364,7 +1364,7 @@ void ProcessInput(PLAYER *pPlayer)
         }
         pPlayer->deathTime += 4;
         if (!bSeqStat)
-            playerAddHoriz(&pPlayer->q16horiz, &pPlayer->horizAdjust, FixedToFloat(mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime<<3, 1024))>>15), IntToFixed(120)) - pPlayer->q16horiz));
+            playerAddHoriz(&pPlayer->q16horiz, &pPlayer->horizAdjust, FixedToFloat(mulscale16(0x8000-(Cos(ClipHigh(pPlayer->deathTime<<3, 1024))>>15), gi->playerHorizMax()) - pPlayer->q16horiz));
         if (pPlayer->curWeapon)
             pInput->setNewWeapon(pPlayer->curWeapon);
         if (pInput->actions & SB_OPEN)

From 2f9783150ce52adcce7ccada8b081ffa407b3a94 Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Tue, 22 Sep 2020 22:55:23 +1000
Subject: [PATCH 40/41] - Blood: Correctly set `playerHorizMin()` and
 `playerHorizMax()` with respect to horizon changing from 0 to 100 like the
 other games (min used to be -180, max used to be 120).

---
 source/blood/src/blood.h | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/source/blood/src/blood.h b/source/blood/src/blood.h
index c188e49b3..203742226 100644
--- a/source/blood/src/blood.h
+++ b/source/blood/src/blood.h
@@ -99,8 +99,8 @@ struct GameInterface : ::GameInterface
 	void LevelCompleted(MapRecord* map, int skill) override;
 	bool DrawAutomapPlayer(int x, int y, int z, int a) override;
 	void SetTileProps(int til, int surf, int vox, int shade) override;
-	fixed_t playerHorizMin() override { return IntToFixed(-79); }
-	fixed_t playerHorizMax() override { return IntToFixed(219); }
+	fixed_t playerHorizMin() override { return IntToFixed(-80); }
+	fixed_t playerHorizMax() override { return IntToFixed(220); }
 
 	GameStats getStats() override;
 };

From 7bb3ab818840827fcdc196839ff6c902f898ee5d Mon Sep 17 00:00:00 2001
From: Mitchell Richters <mjr4077au@gmail.com>
Date: Tue, 22 Sep 2020 23:01:45 +1000
Subject: [PATCH 41/41] - Build: Remove unused inline function in-case it
 accidentally gets used...

---
 source/build/include/build.h | 5 -----
 1 file changed, 5 deletions(-)

diff --git a/source/build/include/build.h b/source/build/include/build.h
index 546364b9a..35bb3cef5 100644
--- a/source/build/include/build.h
+++ b/source/build/include/build.h
@@ -690,11 +690,6 @@ int32_t   ksqrt(uint32_t num);
 int32_t   getangle(int32_t xvect, int32_t yvect);
 fixed_t   gethiq16angle(int32_t xvect, int32_t yvect);
 
-static FORCE_INLINE fixed_t getq16angle(int32_t xvect, int32_t yvect)
-{
-    return IntToFixed(getangle(xvect, yvect));
-}
-
 static FORCE_INLINE constexpr uint32_t uhypsq(int32_t const dx, int32_t const dy)
 {
     return (uint32_t)dx*dx + (uint32_t)dy*dy;