From 35b485183a1e10de9dfe41fbc80eef74a90613ac Mon Sep 17 00:00:00 2001
From: SSNTails <github@spaddlewit.com>
Date: Tue, 11 Jun 2024 18:32:22 +0000
Subject: [PATCH 1/2] Chasecam during demo playback

---
 p_camera.c | 435 +++++++++++++++++++++++++++++++++++++++++++++++++++++
 p_camera.h |  31 ++++
 2 files changed, 466 insertions(+)
 create mode 100644 p_camera.c
 create mode 100644 p_camera.h

diff --git a/p_camera.c b/p_camera.c
new file mode 100644
index 0000000000..45118d455c
--- /dev/null
+++ b/p_camera.c
@@ -0,0 +1,435 @@
+#include "p_camera.h"
+
+#ifdef USECAMERA
+camera_t camera, camera2;
+//#define NOCLIPCAMERA
+
+static boolean PIT_CameraCheckLine(line_t *ld, pmovework_t *mw)
+{
+   fixed_t   opentop, openbottom, lowfloor;
+   sector_t *front, *back;
+
+   // The moving thing's destination positoin will cross the given line.
+   // If this should not be allowed, return false.
+   if(ld->sidenum[1] == -1)
+      return false; // one-sided line
+
+   if(ld->flags & ML_BLOCKING)
+      return false; // explicitly blocking everything
+
+   front = LD_FRONTSECTOR(ld);
+   back  = LD_BACKSECTOR(ld);
+
+   if(front->ceilingheight == front->floorheight ||
+      back->ceilingheight == back->floorheight)
+   {
+      mw->blockline = ld;
+      return false; // probably a closed door
+   }
+
+   if(front->ceilingheight < back->ceilingheight)
+      opentop = front->ceilingheight;
+   else
+      opentop = back->ceilingheight;
+
+   if(front->floorheight > back->floorheight)
+   {
+      openbottom = front->floorheight;
+      lowfloor   = back->floorheight;
+   }
+   else
+   {
+      openbottom = back->floorheight;
+      lowfloor   = front->floorheight;
+   }
+
+   // adjust floor/ceiling heights
+   if(opentop < mw->tmceilingz)
+      mw->tmceilingz = opentop;
+   if(openbottom >mw->tmfloorz)
+      mw->tmfloorz = openbottom;
+   if(lowfloor < mw->tmdropoffz)
+      mw->tmdropoffz = lowfloor;
+
+   return true;
+}
+
+static boolean PM_CameraCrossCheck(line_t *ld, pmovework_t *mw)
+{
+   if(PM_BoxCrossLine(ld, mw))
+   {
+      if(!PIT_CameraCheckLine(ld, mw))
+         return false;
+   }
+   return true;
+}
+
+static boolean PM_CameraCheckPosition(pmovework_t *mw)
+{
+   int xl, xh, yl, yh, bx, by;
+   mobj_t *tmthing = mw->tmthing;
+   VINT *lvalidcount;
+
+   mw->tmflags = tmthing->flags;
+
+   mw->tmbbox[BOXTOP   ] = mw->tmy + tmthing->radius;
+   mw->tmbbox[BOXBOTTOM] = mw->tmy - tmthing->radius;
+   mw->tmbbox[BOXRIGHT ] = mw->tmx + tmthing->radius;
+   mw->tmbbox[BOXLEFT  ] = mw->tmx - tmthing->radius;
+
+   mw->newsubsec = R_PointInSubsector(mw->tmx, mw->tmy);
+
+   // the base floor/ceiling is from the subsector that contains the point.
+   // Any contacted lines the step closer together will adjust them.
+   mw->tmfloorz   = mw->tmdropoffz = mw->newsubsec->sector->floorheight;
+   mw->tmceilingz = mw->newsubsec->sector->ceilingheight;
+
+   I_GetThreadLocalVar(DOOMTLS_VALIDCOUNT, lvalidcount);
+   *lvalidcount = *lvalidcount + 1;
+   if (*lvalidcount == 0)
+      *lvalidcount = 1;
+
+   mw->blockline = NULL;
+   mw->numspechit = 0;
+
+   if(mw->tmflags & MF_NOCLIP) // thing has no clipping?
+      return true;
+
+   // check lines
+   xl = mw->tmbbox[BOXLEFT  ] - bmaporgx;
+   xh = mw->tmbbox[BOXRIGHT ] - bmaporgx;
+   yl = mw->tmbbox[BOXBOTTOM] - bmaporgy;
+   yh = mw->tmbbox[BOXTOP   ] - bmaporgy;
+
+   if(xl < 0)
+      xl = 0;
+   if(yl < 0)
+      yl = 0;
+	if(yh < 0 || xh < 0)
+		return true;
+
+   xl = (unsigned)xl >> MAPBLOCKSHIFT;
+   xh = (unsigned)xh >> MAPBLOCKSHIFT;
+   yl = (unsigned)yl >> MAPBLOCKSHIFT;
+   yh = (unsigned)yh >> MAPBLOCKSHIFT;
+
+   if(xh >= bmapwidth)
+      xh = bmapwidth - 1;
+   if(yh >= bmapheight)
+      yh = bmapheight - 1;
+
+   for(bx = xl; bx <= xh; bx++)
+   {
+      for(by = yl; by <= yh; by++)
+      {
+         if(!P_BlockLinesIterator(bx, by, (blocklinesiter_t)PM_CameraCrossCheck, mw))
+            return false;
+      }
+   }
+
+   return true;
+}
+
+static boolean P_CameraTryMove2(ptrymove_t *tm, boolean checkposonly)
+{
+   pmovework_t mw;
+   boolean trymove2; // result from P_CameraTryMove2
+   mobj_t *tmthing = tm->tmthing;
+
+   mw.tmx = tm->tmx;
+   mw.tmy = tm->tmy;
+   mw.tmthing = tm->tmthing;
+   mw.spechit = &tm->spechit[0];
+
+   trymove2 = PM_CameraCheckPosition(&mw);
+
+   tm->floatok = false;
+   tm->blockline = mw.blockline;
+   tm->tmfloorz = mw.tmfloorz;
+   tm->tmceilingz = mw.tmceilingz;
+   tm->tmdropoffz = mw.tmdropoffz;
+
+   if(checkposonly)
+      return trymove2;
+
+   if(!trymove2)
+      return false;
+
+   if(!(tmthing->flags & MF_NOCLIP))
+   {
+      if(mw.tmceilingz - mw.tmfloorz < tmthing->height)
+         return false; // doesn't fit
+      tm->floatok = true;
+      if(mw.tmceilingz - tmthing->z < tmthing->height)
+         return false; // mobj must lower itself to fit
+      if(mw.tmfloorz - tmthing->z > 24*FRACUNIT)
+         return false; // too big a step up
+      if(!(tmthing->flags & (MF_DROPOFF|MF_FLOAT)) && mw.tmfloorz - mw.tmdropoffz > 24*FRACUNIT)
+         return false; // don't stand over a dropoff
+   }
+
+   // the move is ok, so link the thing into its new position.
+   tmthing->floorz   = mw.tmfloorz;
+   tmthing->ceilingz = mw.tmceilingz;
+   tmthing->x = mw.tmx;
+   tmthing->y = mw.tmy;
+
+   return true;
+}
+
+static boolean P_CameraTryMove (ptrymove_t *tm, mobj_t *thing, fixed_t x, fixed_t y)
+{
+	tm->tmthing = thing;
+	tm->tmx = x;
+	tm->tmy = y;
+	return P_CameraTryMove2 (tm, false);
+}
+
+//
+// Try to slide the player against walls by finding the closest move available.
+//
+static void P_CameraSlideMove(pslidemove_t *sm)
+{
+   int i;
+   fixed_t dx, dy, rx, ry;
+   fixed_t frac, slide;
+   pslidework_t sw;
+   mobj_t *slidething = sm->slidething;
+
+   dx = slidething->momx;
+   dy = slidething->momy;
+   sw.slidex = slidething->x;
+   sw.slidey = slidething->y;
+   sw.slidething = slidething;
+   sw.numspechit = 0;
+   sw.spechit = &sm->spechit[0];
+
+   // perform a maximum of three bumps
+   for(i = 0; i < 3; i++)
+   {
+      frac = P_CompletableFrac(&sw, dx, dy);
+      if(frac != FRACUNIT)
+         frac -= 0x1000;
+      if(frac < 0)
+         frac = 0;
+
+      rx = FixedMul(frac, dx);
+      ry = FixedMul(frac, dy);
+
+      sw.slidex += rx;
+      sw.slidey += ry;
+
+      // made it the entire way
+      if(frac == FRACUNIT)
+      {
+         slidething->momx = dx;
+         slidething->momy = dy;
+//         SL_CheckSpecialLines(&sw); // Camera doesn't trip lines
+         sm->slidex = sw.slidex;
+         sm->slidey = sw.slidey;
+         return;
+      }
+
+      // project the remaining move along the line that blocked movement
+      dx -= rx;
+      dy -= ry;
+      dx = FixedMul(dx, sw.blocknvx);
+      dy = FixedMul(dy, sw.blocknvy);
+      slide = dx + dy;
+
+      dx = FixedMul(slide, sw.blocknvx);
+      dy = FixedMul(slide, sw.blocknvy);
+   }
+
+   // some hideous situation has happened that won't let the camera slide
+   sm->slidex = slidething->x;
+   sm->slidey = slidething->y;
+   sm->slidething->momx = slidething->momy = 0;
+}
+
+static void P_ResetCamera(player_t *player, camera_t *thiscam)
+{
+	thiscam->x = player->mo->x;
+	thiscam->y = player->mo->y;
+	thiscam->z = player->mo->z + VIEWHEIGHT;
+}
+
+// Process the mobj-ish required functions of the camera
+static void P_CameraThinker(player_t *player, camera_t *thiscam)
+{
+#ifdef NOCLIPCAMERA
+   	thiscam->x += thiscam->momx;
+   	thiscam->y += thiscam->momy;
+   	thiscam->z += thiscam->momz;
+   	return;
+#else
+	// P_CameraXYMovement()
+	if (thiscam->momx || thiscam->momy)
+   	{
+		fixed_t momx, momy;
+      	pslidemove_t sm;
+      	ptrymove_t tm;
+
+		momx = vblsinframe*(thiscam->momx>>2);
+		momy = vblsinframe*(thiscam->momy>>2);
+
+		mobj_t mo; // Our temporary dummy to pretend we are a mobj
+		mo.flags = MF_SOLID|MF_FLOAT;
+		mo.x = thiscam->x;
+		mo.y = thiscam->y;
+		mo.z = thiscam->z;
+		mo.floorz = thiscam->floorz;
+		mo.ceilingz = thiscam->ceilingz;
+		mo.momx = thiscam->momx;
+		mo.momy = thiscam->momy;
+		mo.momz = thiscam->momz;
+		mo.radius = CAM_RADIUS;
+		mo.height = CAM_HEIGHT;
+		sm.slidething = &mo;
+
+		P_CameraSlideMove(&sm);
+
+		if (sm.slidex == mo.x && sm.slidey == mo.y)
+			goto camstairstep;
+
+		if (P_CameraTryMove(&tm, &mo, sm.slidex, sm.slidey))
+			goto camwrapup;
+
+camstairstep:
+		// something fucked up in slidemove, so stairstep
+		if (P_CameraTryMove(&tm, &mo, mo.x, mo.y + momy))
+		{
+			mo.momx = 0;
+			mo.momy = momy;
+			goto camwrapup;
+		}
+
+		if (P_CameraTryMove(&tm, &mo, mo.x + momx, mo.y))
+		{
+			mo.momx = momx;
+			mo.momy = 0;
+			goto camwrapup;
+		}
+
+		mo.momx = mo.momy = 0;
+
+camwrapup:
+		thiscam->x = mo.x;
+		thiscam->y = mo.y;
+		thiscam->z = mo.z;
+		thiscam->floorz = mo.floorz;
+		thiscam->ceilingz = mo.ceilingz;
+		thiscam->momx = mo.momx;
+		thiscam->momy = mo.momy;
+		thiscam->momz = mo.momz;
+   }
+
+	// P_CameraZMovement()
+	if (thiscam->momz)
+	{
+   		thiscam->z += thiscam->momz;
+
+		// Don't go below the floor
+		if (thiscam->z <= thiscam->floorz)
+		{
+			thiscam->z = thiscam->floorz;
+			if (thiscam->z > player->viewz + CAM_HEIGHT + (16 << FRACBITS))
+			{
+				// Camera got stuck above the player
+				P_ResetCamera(player, thiscam);
+			}
+		}
+
+		// Don't go above the ceiling
+		if (thiscam->z + CAM_HEIGHT > thiscam->ceilingz)
+		{
+			if (thiscam->momz > 0)
+				thiscam->momz = 0;
+
+			thiscam->z = thiscam->ceilingz - CAM_HEIGHT;
+
+			if (thiscam->z + CAM_HEIGHT < player->mo->z - player->mo->height)
+			{
+				// Camera got stuck below the player
+				P_ResetCamera(player, thiscam);
+			}
+		}
+	}
+
+   	if (thiscam->ceilingz - thiscam->z < CAM_HEIGHT && thiscam->ceilingz >= thiscam->z)
+	{
+		thiscam->ceilingz = thiscam->z + CAM_HEIGHT;
+		thiscam->floorz = thiscam->z;
+	}
+#endif
+}
+
+void P_MoveChaseCamera(player_t *player, camera_t *thiscam)
+{
+	angle_t angle = 0;
+	angle_t focusangle = 0;
+	fixed_t x, y, z, viewpointx, viewpointy, camspeed, camdist, camheight, pviewheight;
+	fixed_t dist;
+	mobj_t *mo;
+	subsector_t *newsubsec;
+
+	mo = player->mo;
+//	const fixed_t radius = CAM_RADIUS;
+	const fixed_t height = CAM_HEIGHT;
+
+	angle = focusangle = mo->angle;
+
+	P_CameraThinker(player, thiscam);
+
+	camspeed = FRACUNIT / 4;
+	camdist = CAM_DIST;
+	camheight = 20 << FRACBITS;
+
+	if (P_AproxDistance(thiscam->x - mo->x, thiscam->y - mo->y) > camdist * 2)
+	{
+		// Camera is stuck, and the player has gone over twice as far away from it, so let's reset
+		P_ResetCamera(player, thiscam);
+	}
+
+	dist = camdist;
+
+	// If dead, camera is twice as close
+	if (player->health <= 0)
+		dist >>= 1;
+
+	// Destination XY
+	x = mo->x - FixedMul(finecosine((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
+	y = mo->y - FixedMul(finesine((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
+
+	pviewheight = VIEWHEIGHT;
+
+	z = mo->z + pviewheight + camheight;
+
+	// Look at halfway between the camera and player. Is the ceiling lower? Then the camera should try to move down to fit under it
+	newsubsec = R_PointInSubsector(((mo->x>>FRACBITS) + (thiscam->x>>FRACBITS))<<(FRACBITS-1), ((mo->y>>FRACBITS) + (thiscam->y>>FRACBITS))<<(FRACBITS-1));
+
+	{
+		// camera fit?
+		if (newsubsec->sector->ceilingheight != newsubsec->sector->floorheight // Don't try to fit in sectors with equal floor and ceiling heights
+			&& newsubsec->sector->ceilingheight - height < z)
+			z = newsubsec->sector->ceilingheight - height-(11<<FRACBITS);
+	}
+
+	if (thiscam->z < thiscam->floorz)
+		thiscam->z = thiscam->floorz;
+
+	// The camera actually focuses 64 units ahead of where the player is.
+	// This is more aesthetically pleasing.
+	dist = 64 << FRACBITS;
+	viewpointx = mo->x + FixedMul(finecosine((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
+	viewpointy = mo->y + FixedMul(finesine((angle>>ANGLETOFINESHIFT) & FINEMASK), dist);
+
+	thiscam->angle = R_PointToAngle2(thiscam->x, thiscam->y, viewpointx, viewpointy);
+
+	// Set the mom vector, cut by the camera speed, as it tries to move to the destination position
+	thiscam->momx = FixedMul(x - thiscam->x, camspeed);
+	thiscam->momy = FixedMul(y - thiscam->y, camspeed);
+	thiscam->momz = FixedMul(z - thiscam->z, camspeed);
+}
+
+#endif
\ No newline at end of file
diff --git a/p_camera.h b/p_camera.h
new file mode 100644
index 0000000000..52e78adca8
--- /dev/null
+++ b/p_camera.h
@@ -0,0 +1,31 @@
+#ifndef __P_CAMERA_H__
+#define __P_CAMERA_H__
+
+#include "doomdef.h"
+#include "p_local.h"
+
+#ifdef USECAMERA
+typedef struct camera_s
+{
+	// Info for drawing: position.
+	fixed_t x, y, z;
+
+	angle_t angle; // orientation
+	angle_t aiming; // up/down (future)
+
+	struct subsector_s *subsector;
+	fixed_t floorz, ceilingz;
+
+	// Momentums used to update position
+	fixed_t momx, momy, momz;
+} camera_t;
+
+#define CAM_HEIGHT (16<<FRACBITS)
+#define CAM_RADIUS (20<<FRACBITS)
+#define CAM_DIST (128<<FRACBITS)
+
+extern camera_t camera, camera2;
+void P_MoveChaseCamera(player_t *player, camera_t *thiscam);
+#endif
+
+#endif
\ No newline at end of file

From 9a0ce40b711930ff6b8b37de68361439d3054db4 Mon Sep 17 00:00:00 2001
From: SSNTails <github@spaddlewit.com>
Date: Tue, 11 Jun 2024 18:52:18 +0000
Subject: [PATCH 2/2] Chasecam during demo playback

---
 Makefile   |  3 ++-
 p_camera.c |  5 +---
 p_camera.h |  5 +---
 p_local.h  | 40 +++++++++++++++++++++++++++
 p_move.c   | 24 +----------------
 p_slide.c  | 79 ++++++++++++++++++++++++++++++++++++++++++------------
 p_user.c   | 13 +++++++++
 r_main.c   | 28 +++++++++++++++----
 r_phase3.c |  3 +++
 r_phase8.c |  3 +++
 wadbase.s  |  2 +-
 11 files changed, 150 insertions(+), 55 deletions(-)

diff --git a/Makefile b/Makefile
index 37013a3233..b1b07813f9 100644
--- a/Makefile
+++ b/Makefile
@@ -51,6 +51,7 @@ OBJS = \
 	d_main.o \
 	g_game.o \
 	info.o \
+	p_camera.o \
 	p_ceilng.o \
 	p_doors.o \
 	p_enemy.o \
@@ -115,7 +116,7 @@ m68k.bin:
 
 $(TARGET).32x: $(TARGET).elf
 	$(OBJC) -O binary $< temp2.bin
-	$(DD) if=temp2.bin of=temp.bin bs=180K conv=sync
+	$(DD) if=temp2.bin of=temp.bin bs=196K conv=sync
 	rm -f temp3.bin
 	cat temp.bin $(WAD) >>temp3.bin
 	$(DD) if=temp3.bin of=$@ bs=512K conv=sync
diff --git a/p_camera.c b/p_camera.c
index 45118d455c..fe248c05db 100644
--- a/p_camera.c
+++ b/p_camera.c
@@ -1,7 +1,6 @@
 #include "p_camera.h"
 
-#ifdef USECAMERA
-camera_t camera, camera2;
+camera_t camera;
 //#define NOCLIPCAMERA
 
 static boolean PIT_CameraCheckLine(line_t *ld, pmovework_t *mw)
@@ -431,5 +430,3 @@ void P_MoveChaseCamera(player_t *player, camera_t *thiscam)
 	thiscam->momy = FixedMul(y - thiscam->y, camspeed);
 	thiscam->momz = FixedMul(z - thiscam->z, camspeed);
 }
-
-#endif
\ No newline at end of file
diff --git a/p_camera.h b/p_camera.h
index 52e78adca8..0b90ad3867 100644
--- a/p_camera.h
+++ b/p_camera.h
@@ -4,7 +4,6 @@
 #include "doomdef.h"
 #include "p_local.h"
 
-#ifdef USECAMERA
 typedef struct camera_s
 {
 	// Info for drawing: position.
@@ -24,8 +23,6 @@ typedef struct camera_s
 #define CAM_RADIUS (20<<FRACBITS)
 #define CAM_DIST (128<<FRACBITS)
 
-extern camera_t camera, camera2;
+extern camera_t camera;
 void P_MoveChaseCamera(player_t *player, camera_t *thiscam);
 #endif
-
-#endif
\ No newline at end of file
diff --git a/p_local.h b/p_local.h
index 6298ec04eb..ea5e50ea8b 100644
--- a/p_local.h
+++ b/p_local.h
@@ -328,6 +328,46 @@ typedef struct
 
 void P_SlideMove (pslidemove_t *sm);
 
+typedef struct
+{
+	// input
+	mobj_t *tmthing;
+	fixed_t  tmx, tmy;
+
+	fixed_t  tmfloorz;   // current floor z for P_TryMove2
+	fixed_t  tmceilingz; // current ceiling z for P_TryMove2
+	line_t *blockline;  // possibly a special to activate
+
+	fixed_t tmbbox[4];
+	int     tmflags;
+	fixed_t tmdropoffz; // lowest point contacted
+
+	int    	numspechit;
+	line_t **spechit;
+
+	subsector_t *newsubsec; // destination subsector
+} pmovework_t;
+
+typedef struct
+{
+	mobj_t *slidething;
+	fixed_t slidex, slidey;     // the final position
+	fixed_t slidedx, slidedy;   // current move for completable frac
+	fixed_t blockfrac;          // the fraction of the move that gets completed
+	fixed_t blocknvx, blocknvy; // the vector of the line that blocks move
+	fixed_t endbox[4];          // final proposed position
+	fixed_t nvx, nvy;           // normalized line vector
+
+	vertex_t *p1, *p2; // p1, p2 are line endpoints
+	fixed_t p3x, p3y, p4x, p4y; // p3, p4 are move endpoints
+
+	int numspechit;
+	line_t **spechit;
+} pslidework_t;
+
+boolean PM_BoxCrossLine(line_t *ld, pmovework_t *mw) ATTR_DATA_CACHE_ALIGN;
+fixed_t P_CompletableFrac(pslidework_t *sw, fixed_t dx, fixed_t dy);
+
 #endif	/* __P_LOCAL__ */
 
 
diff --git a/p_move.c b/p_move.c
index 43db817f98..ea5e6df9db 100644
--- a/p_move.c
+++ b/p_move.c
@@ -29,29 +29,7 @@
 #include "doomdef.h"
 #include "p_local.h"
 
-// CALICO_TODO: these ought to be in headers.
-typedef struct
-{
-   // input
-   mobj_t  *tmthing;
-   fixed_t  tmx, tmy;
-
-   fixed_t  tmfloorz;   // current floor z for P_TryMove2
-   fixed_t  tmceilingz; // current ceiling z for P_TryMove2
-   line_t  *blockline;  // possibly a special to activate
-
-   fixed_t tmbbox[4];
-   int     tmflags;
-   fixed_t tmdropoffz; // lowest point contacted
-
-	int    	numspechit;
- 	line_t	**spechit;
-
-   subsector_t *newsubsec; // destination subsector
-} pmovework_t;
-
 boolean PIT_CheckThing(mobj_t* thing, pmovework_t *mw) ATTR_DATA_CACHE_ALIGN;
-static boolean PM_BoxCrossLine(line_t* ld, pmovework_t *mw) ATTR_DATA_CACHE_ALIGN;
 static boolean PIT_CheckLine(line_t* ld, pmovework_t *mw) ATTR_DATA_CACHE_ALIGN;
 static boolean PM_CrossCheck(line_t* ld, pmovework_t *mw) ATTR_DATA_CACHE_ALIGN;
 static boolean PM_CheckPosition(pmovework_t *mw) ATTR_DATA_CACHE_ALIGN;
@@ -138,7 +116,7 @@ boolean PIT_CheckThing(mobj_t *thing, pmovework_t *mw)
 //
 // Check if the thing intersects a linedef
 //
-static boolean PM_BoxCrossLine(line_t *ld, pmovework_t *mw)
+boolean PM_BoxCrossLine(line_t *ld, pmovework_t *mw)
 {
    fixed_t x1, x2, y1, y2;
    fixed_t lx, ly, ldx, ldy;
diff --git a/p_slide.c b/p_slide.c
index a11fee9f0e..21097e6b67 100644
--- a/p_slide.c
+++ b/p_slide.c
@@ -29,23 +29,6 @@
 #include "doomdef.h"
 #include "p_local.h"
 
-typedef struct
-{
-   mobj_t *slidething;
-   fixed_t slidex, slidey;     // the final position
-   fixed_t slidedx, slidedy;   // current move for completable frac
-   fixed_t blockfrac;          // the fraction of the move that gets completed
-   fixed_t blocknvx, blocknvy; // the vector of the line that blocks move
-   fixed_t endbox[4];          // final proposed position
-   fixed_t nvx, nvy;           // normalized line vector
-
-   vertex_t *p1, *p2; // p1, p2 are line endpoints
-   fixed_t p3x, p3y, p4x, p4y; // p3, p4 are move endpoints
-
-	int numspechit;
-	line_t **spechit;
-} pslidework_t;
-
 #define CLIPRADIUS 23
 
 enum
@@ -514,5 +497,67 @@ void P_SlideMove(pslidemove_t *sm)
    sm->numspechit = sw.numspechit;
 }
 
+//
+// Try to slide the player against walls by finding the closest move available.
+//
+void P_CameraSlideMove(pslidemove_t *sm)
+{
+    int i;
+    fixed_t dx, dy, rx, ry;
+    fixed_t frac, slide;
+    pslidework_t sw;
+    mobj_t *slidething = sm->slidething;
+
+    dx = slidething->momx;
+    dy = slidething->momy;
+    sw.slidex = slidething->x;
+    sw.slidey = slidething->y;
+    sw.slidething = slidething;
+    sw.numspechit = 0;
+    sw.spechit = &sm->spechit[0];
+
+    // perform a maximum of three bumps
+    for (i = 0; i < 3; i++)
+    {
+        frac = P_CompletableFrac(&sw, dx, dy);
+        if (frac != FRACUNIT)
+            frac -= 0x1000;
+        if (frac < 0)
+            frac = 0;
+
+        rx = FixedMul(frac, dx);
+        ry = FixedMul(frac, dy);
+
+        sw.slidex += rx;
+        sw.slidey += ry;
+
+        // made it the entire way
+        if (frac == FRACUNIT)
+        {
+            slidething->momx = dx;
+            slidething->momy = dy;
+            //         SL_CheckSpecialLines(&sw); // Camera doesn't trip lines
+            sm->slidex = sw.slidex;
+            sm->slidey = sw.slidey;
+            return;
+        }
+
+        // project the remaining move along the line that blocked movement
+        dx -= rx;
+        dy -= ry;
+        dx = FixedMul(dx, sw.blocknvx);
+        dy = FixedMul(dy, sw.blocknvy);
+        slide = dx + dy;
+
+        dx = FixedMul(slide, sw.blocknvx);
+        dy = FixedMul(slide, sw.blocknvy);
+    }
+
+    // some hideous situation has happened that won't let the camera slide
+    sm->slidex = slidething->x;
+    sm->slidey = slidething->y;
+    sm->slidething->momx = slidething->momy = 0;
+}
+
 // EOF
 
diff --git a/p_user.c b/p_user.c
index 358e525720..b7b87001dd 100644
--- a/p_user.c
+++ b/p_user.c
@@ -3,6 +3,7 @@
 #include "doomdef.h"
 #include "p_local.h"
 #include "st_main.h"
+#include "p_camera.h"
 
 
 fixed_t 		forwardmove[2] = {0x40000, 0x60000}; 
@@ -606,6 +607,11 @@ ticphase = 21;
 	
 	if (player->playerstate == PST_DEAD)
 	{
+		if (demoplayback)
+		{
+			if (player == &players[consoleplayer])
+				P_MoveChaseCamera(player, &camera);
+		}
 		P_DeathThink (player);
 		return;
 	}
@@ -619,6 +625,13 @@ ticphase = 22;
 	else
 		P_MovePlayer (player);
 	P_CalcHeight (player);
+
+	if (demoplayback)
+	{
+		if (player == &players[consoleplayer])
+				P_MoveChaseCamera(player, &camera);
+	}
+
 	if (player->mo->subsector->sector->special)
 		P_PlayerInSpecialSector (player);
 		
diff --git a/r_main.c b/r_main.c
index 58e93f74a6..1a887b9d72 100644
--- a/r_main.c
+++ b/r_main.c
@@ -2,6 +2,7 @@
 
 #include "doomdef.h"
 #include "r_local.h"
+#include "p_camera.h"
 #ifdef MARS
 #include "mars.h"
 #include "marshw.h"
@@ -503,17 +504,34 @@ static void R_Setup (int displayplayer, visplane_t *visplanes_,
 
 	player = &players[displayplayer];
 
+	if (demoplayback)
+	{
+		const camera_t *thiscam = NULL;
+
+		if (displayplayer == consoleplayer)
+			thiscam = &camera;
+
+		vd.viewx = thiscam->x;
+		vd.viewy = thiscam->y;
+		vd.viewz = thiscam->z;
+		vd.viewangle = thiscam->angle;
+		vd.lightlevel = thiscam->subsector->sector->lightlevel;
+	}
+	else
+	{
+		vd.viewx = player->mo->x;
+		vd.viewy = player->mo->y;
+		vd.viewz = player->viewz;
+		vd.viewangle = player->mo->angle;
+		vd.lightlevel = player->mo->subsector->sector->lightlevel;
+	}
+
 	vd.viewplayer = player;
-	vd.viewx = player->mo->x;
-	vd.viewy = player->mo->y;
-	vd.viewz = player->viewz;
-	vd.viewangle = player->mo->angle;
 
 	vd.viewsin = finesine(vd.viewangle>>ANGLETOFINESHIFT);
 	vd.viewcos = finecosine(vd.viewangle>>ANGLETOFINESHIFT);
 
 	vd.displayplayer = displayplayer;
-	vd.lightlevel = player->mo->subsector->sector->lightlevel;
 	vd.fixedcolormap = 0;
 
 	vd.clipangle = xtoviewangle[0]<<FRACBITS;
diff --git a/r_phase3.c b/r_phase3.c
index f97e63e757..871007c326 100644
--- a/r_phase3.c
+++ b/r_phase3.c
@@ -182,6 +182,9 @@ static void R_PrepPSprite(pspdef_t *psp)
    fixed_t      tx, x1, x2;
    const state_t* state;
 
+   if (demoplayback)
+      return; // No psprites in camera view
+
    state = &states[psp->state];
    sprdef = &sprites[state->sprite];
    sprframe = &spriteframes[sprdef->firstframe + (state->frame & FF_FRAMEMASK)];
diff --git a/r_phase8.c b/r_phase8.c
index e16103fafa..c82a372de3 100644
--- a/r_phase8.c
+++ b/r_phase8.c
@@ -468,6 +468,9 @@ static void R_DrawPSprites(int sprscreenhalf)
 
     I_SetThreadLocalVar(DOOMTLS_COLORMAP, dc_colormaps);
 
+    if (demoplayback)
+      return; // No psprites in camera view
+
     // draw psprites
     for (spr = vd.lastsprite_p; spr < vd.vissprite_p; spr++)
     {
diff --git a/wadbase.s b/wadbase.s
index 6871e8ca92..ad176dd854 100644
--- a/wadbase.s
+++ b/wadbase.s
@@ -4,5 +4,5 @@
 
         .global _wadBase
 _wadBase:
-        .long   0x202D000
+        .long   0x2031000