From f465de8f8c965a8a237662c51ad9b0f47faf3ccd Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Mon, 1 Aug 2016 21:07:01 +0100
Subject: [PATCH 01/41] Nev3r's vertical sky distortion

---
 src/r_plane.c | 1 +
 1 file changed, 1 insertion(+)

diff --git a/src/r_plane.c b/src/r_plane.c
index 19007d88f..1474474b9 100644
--- a/src/r_plane.c
+++ b/src/r_plane.c
@@ -709,6 +709,7 @@ void R_DrawPlanes(void)
 					if (dc_yl <= dc_yh)
 					{
 						angle = (pl->viewangle + xtoviewangle[x])>>ANGLETOSKYSHIFT;
+						dc_iscale = FixedMul(skyscale, FINECOSINE(xtoviewangle[x]>>ANGLETOFINESHIFT));
 						dc_x = x;
 						dc_source =
 							R_GetColumn(skytexture,

From 76e53ee3b675c9ff8b5819a685d0cfa8c82140af Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Mon, 15 Aug 2016 20:54:05 +0100
Subject: [PATCH 02/41] this is shitty and glitchy but it compiles

remove all the places where i've set vis->scalestep to anything other than 0 to see something that LOOKS okay, but doesn't fulfil exactly what i want (that being a sprite that looks exactly like a midtexture)
---
 src/r_things.c | 30 ++++++++++++++++++++++++++----
 src/r_things.h |  1 +
 2 files changed, 27 insertions(+), 4 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index 108589493..44cb81132 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -913,6 +913,9 @@ static void R_DrawVisSprite(vissprite_t *vis)
 	for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale)
 	{
 #ifdef RANGECHECK
+		sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
+		dc_iscale = 0xffffffffu / (unsigned)spryscale;
+
 		texturecolumn = frac>>FRACBITS;
 
 		if (texturecolumn < 0 || texturecolumn >= SHORT(patch->width))
@@ -925,6 +928,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
 			R_DrawFlippedMaskedColumn(column, patch->height);
 		else
 			R_DrawMaskedColumn(column);
+		spryscale += vis->scalestep;
 	}
 
 	colfunc = basecolfunc;
@@ -1109,6 +1113,7 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	angle_t ang;
 	fixed_t iscale;
+	fixed_t scalestep = 0; // toast '16
 
 	//SoM: 3/17/2000
 	fixed_t gz, gzt;
@@ -1116,6 +1121,8 @@ static void R_ProjectSprite(mobj_t *thing)
 	INT32 light = 0;
 	fixed_t this_scale = thing->scale;
 
+	fixed_t ang_scale = FRACUNIT;
+
 	// transform the origin point
 	tr_x = thing->x - viewx;
 	tr_y = thing->y - viewy;
@@ -1186,6 +1193,15 @@ static void R_ProjectSprite(mobj_t *thing)
 		I_Error("R_ProjectSprite: sprframes NULL for sprite %d\n", thing->sprite);
 #endif
 
+	if (1) //(sprframe->rotate != SRF_SINGLE || flatsprite)
+		ang = R_PointToAngle (thing->x, thing->y) - thing->angle;
+
+	if (1) //(flatsprite)
+	{
+		ang_scale = abs(FINESINE(ang>>ANGLETOFINESHIFT));
+		scalestep = (FINECOSINE(ang>>ANGLETOFINESHIFT));
+	}
+
 	if (sprframe->rotate == SRF_SINGLE)
 	{
 		// use single rotation for all views
@@ -1196,7 +1212,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	else
 	{
 		// choose a different rotation based on player view
-		ang = R_PointToAngle (thing->x, thing->y) - thing->angle;
+		//ang = R_PointToAngle (thing->x, thing->y) - thing->angle;
 
 		if ((sprframe->rotate & SRF_RIGHT) && (ang < ANGLE_180)) // See from right
 			rot = 6; // F7 slot
@@ -1217,22 +1233,27 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	// calculate edges of the shape
 	if (flip)
-		tx -= FixedMul(spritecachedinfo[lump].width-spritecachedinfo[lump].offset, this_scale);
+		tx -= FixedMul(spritecachedinfo[lump].width-spritecachedinfo[lump].offset, FixedMul(this_scale, ang_scale));
 	else
-		tx -= FixedMul(spritecachedinfo[lump].offset, this_scale);
+		tx -= FixedMul(spritecachedinfo[lump].offset, FixedMul(this_scale, ang_scale));
 	x1 = (centerxfrac + FixedMul (tx,xscale)) >>FRACBITS;
 
 	// off the right side?
 	if (x1 > viewwidth)
 		return;
 
-	tx += FixedMul(spritecachedinfo[lump].width, this_scale);
+	tx += FixedMul(spritecachedinfo[lump].width, FixedMul(this_scale, ang_scale));
 	x2 = ((centerxfrac + FixedMul (tx,xscale)) >>FRACBITS) - 1;
 
 	// off the left side
 	if (x2 < 0)
 		return;
 
+	if (1) // (flatsprite)
+		yscale = yscale - (x2 - x1)*scalestep/2;
+
+	xscale = FixedMul(xscale, ang_scale);
+
 	// PORTAL SPRITE CLIPPING
 	if (portalrender)
 	{
@@ -1323,6 +1344,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	vis->pz = thing->z;
 	vis->pzt = vis->pz + vis->thingheight;
 	vis->texturemid = vis->gzt - viewz;
+	vis->scalestep = scalestep;
 
 	vis->mobj = thing; // Easy access! Tails 06-07-2002
 
diff --git a/src/r_things.h b/src/r_things.h
index 7b8b3eb0b..0941082aa 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -136,6 +136,7 @@ typedef struct vissprite_s
 
 	fixed_t startfrac; // horizontal position of x1
 	fixed_t scale;
+	fixed_t scalestep; // only for flat sprites, 0 otherwise
 	fixed_t xiscale; // negative if flipped
 
 	fixed_t texturemid;

From 27cdef0075ba4b8424092c2bd13caaf61890c716 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Tue, 16 Aug 2016 23:17:45 +0100
Subject: [PATCH 03/41] WOOPS, did not mean to leave it in the rangecheck, now
 it works close to desired

some complicated mathemagic leads to something which... seems CLOSE, but not perfectly accurate, so i think i need to tweak it more

http://gfycat.com/JovialSpitefulAmericancrayfish for current behaviour
---
 src/r_things.c | 41 +++++++++++++++++++++++++++++++++++------
 1 file changed, 35 insertions(+), 6 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index 44cb81132..5898eb21f 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -913,8 +913,6 @@ static void R_DrawVisSprite(vissprite_t *vis)
 	for (dc_x = vis->x1; dc_x <= vis->x2; dc_x++, frac += vis->xiscale)
 	{
 #ifdef RANGECHECK
-		sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
-		dc_iscale = 0xffffffffu / (unsigned)spryscale;
 
 		texturecolumn = frac>>FRACBITS;
 
@@ -924,6 +922,11 @@ static void R_DrawVisSprite(vissprite_t *vis)
 #else
 		column = (column_t *)((UINT8 *)patch + LONG(patch->columnofs[frac>>FRACBITS]));
 #endif
+		if (vis->scalestep)
+		{
+			sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
+			dc_iscale = FixedMul((0xffffffffu / (unsigned)spryscale), this_scale);
+		}
 		if (vis->vflip)
 			R_DrawFlippedMaskedColumn(column, patch->height);
 		else
@@ -1114,6 +1117,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	angle_t ang;
 	fixed_t iscale;
 	fixed_t scalestep = 0; // toast '16
+	fixed_t leftoffset;
 
 	//SoM: 3/17/2000
 	fixed_t gz, gzt;
@@ -1199,7 +1203,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	if (1) //(flatsprite)
 	{
 		ang_scale = abs(FINESINE(ang>>ANGLETOFINESHIFT));
-		scalestep = (FINECOSINE(ang>>ANGLETOFINESHIFT));
+		//scalestep = (FINECOSINE(ang>>ANGLETOFINESHIFT));
 	}
 
 	if (sprframe->rotate == SRF_SINGLE)
@@ -1233,9 +1237,10 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	// calculate edges of the shape
 	if (flip)
-		tx -= FixedMul(spritecachedinfo[lump].width-spritecachedinfo[lump].offset, FixedMul(this_scale, ang_scale));
+		leftoffset = spritecachedinfo[lump].width-spritecachedinfo[lump].offset;
 	else
-		tx -= FixedMul(spritecachedinfo[lump].offset, FixedMul(this_scale, ang_scale));
+		leftoffset = spritecachedinfo[lump].offset;
+	tx -= FixedMul(leftoffset, FixedMul(this_scale, ang_scale));
 	x1 = (centerxfrac + FixedMul (tx,xscale)) >>FRACBITS;
 
 	// off the right side?
@@ -1250,7 +1255,31 @@ static void R_ProjectSprite(mobj_t *thing)
 		return;
 
 	if (1) // (flatsprite)
-		yscale = yscale - (x2 - x1)*scalestep/2;
+	{
+		fixed_t yscale2;
+		INT32 range;
+
+		tr_x = thing->x + FixedMul(-leftoffset, FINECOSINE(ang>>ANGLETOFINESHIFT)) - viewx;
+		tr_y = thing->y + FixedMul(-leftoffset, FINESINE(ang>>ANGLETOFINESHIFT)) - viewy;
+		gxt = FixedMul(tr_x, viewcos);
+		gyt = -FixedMul(tr_y, viewsin);
+		tz = gxt-gyt;
+		yscale = FixedDiv(projectiony, tz);
+
+		leftoffset += spritecachedinfo[lump].width;
+		tr_x = thing->x + FixedMul(leftoffset, FINECOSINE(ang>>ANGLETOFINESHIFT)) - viewx;
+		tr_y = thing->y + FixedMul(leftoffset, FINESINE(ang>>ANGLETOFINESHIFT)) - viewy;
+		gxt = FixedMul(tr_x, viewcos);
+		gyt = -FixedMul(tr_y, viewsin);
+		tz = gxt-gyt;
+		yscale2 = FixedDiv(projectiony, tz);
+
+		if (x2 > x1)
+			range = (x2 - x1);
+		else
+			range = 1;
+		scalestep = (yscale2 - yscale)/range;
+	}
 
 	xscale = FixedMul(xscale, ang_scale);
 

From a2b26c3bf440e98971dde220a28e6bcf71bf7a5a Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Tue, 16 Aug 2016 23:25:01 +0100
Subject: [PATCH 04/41] Forgot a border of screen consideration

---
 src/r_things.c | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/src/r_things.c b/src/r_things.c
index 5898eb21f..f17b4ec52 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1413,7 +1413,10 @@ static void R_ProjectSprite(mobj_t *thing)
 	}
 
 	if (vis->x1 > x1)
+	{
 		vis->startfrac += FixedDiv(vis->xiscale, this_scale)*(vis->x1-x1);
+		vis->scale += scalestep*(vis->x1 - x1);
+	}
 
 	//Fab: lumppat is the lump number of the patch to use, this is different
 	//     than lumpid for sprites-in-pwad : the graphics are patched

From 10a8682620c547f5b187633e12ea282b2eae76c6 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Tue, 16 Aug 2016 23:38:59 +0100
Subject: [PATCH 05/41] resolved issue where the sides had the opposite scales
 over ANGLE_180.

also, i've sussed out WHAT'S going wrong here - the topleft pixel of the sprite will always be rendered at the height on the screen it would be rendered otherwise, which is causing the waving. now to figure out what to change to get that to move appropriately...
---
 src/r_things.c | 14 ++++++++++++--
 1 file changed, 12 insertions(+), 2 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index f17b4ec52..6cb5bac6a 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1256,6 +1256,7 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	if (1) // (flatsprite)
 	{
+		fixed_t yscale1;
 		fixed_t yscale2;
 		INT32 range;
 
@@ -1264,7 +1265,7 @@ static void R_ProjectSprite(mobj_t *thing)
 		gxt = FixedMul(tr_x, viewcos);
 		gyt = -FixedMul(tr_y, viewsin);
 		tz = gxt-gyt;
-		yscale = FixedDiv(projectiony, tz);
+		yscale1 = FixedDiv(projectiony, tz);
 
 		leftoffset += spritecachedinfo[lump].width;
 		tr_x = thing->x + FixedMul(leftoffset, FINECOSINE(ang>>ANGLETOFINESHIFT)) - viewx;
@@ -1274,11 +1275,20 @@ static void R_ProjectSprite(mobj_t *thing)
 		tz = gxt-gyt;
 		yscale2 = FixedDiv(projectiony, tz);
 
+		if (ang >= ANGLE_180)
+		{
+			fixed_t temp = yscale2;
+			yscale2 = yscale1;
+			yscale1 = temp;
+		}
+
 		if (x2 > x1)
 			range = (x2 - x1);
 		else
 			range = 1;
-		scalestep = (yscale2 - yscale)/range;
+		scalestep = (yscale2 - yscale1)/range;
+		yscale = yscale1;
+		//this_scale = FixedMul(this_scale, FixedDiv(yscale, yscale1));
 	}
 
 	xscale = FixedMul(xscale, ang_scale);

From 84c39c24f90da15fe637c80ae8cce32ef96baac8 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Wed, 17 Aug 2016 00:03:09 +0100
Subject: [PATCH 06/41] Renamed leftoffset to offset, to refer to its
 multifaceted uses.

Also, discovered another undesirable bug, but don't understand exactly what's going on so won't describe it here.
---
 src/r_things.c | 18 +++++++++---------
 1 file changed, 9 insertions(+), 9 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index 6cb5bac6a..522171d7d 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1117,7 +1117,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	angle_t ang;
 	fixed_t iscale;
 	fixed_t scalestep = 0; // toast '16
-	fixed_t leftoffset;
+	fixed_t offset;
 
 	//SoM: 3/17/2000
 	fixed_t gz, gzt;
@@ -1237,10 +1237,10 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	// calculate edges of the shape
 	if (flip)
-		leftoffset = spritecachedinfo[lump].width-spritecachedinfo[lump].offset;
+		offset = spritecachedinfo[lump].width-spritecachedinfo[lump].offset;
 	else
-		leftoffset = spritecachedinfo[lump].offset;
-	tx -= FixedMul(leftoffset, FixedMul(this_scale, ang_scale));
+		offset = spritecachedinfo[lump].offset;
+	tx -= FixedMul(offset, FixedMul(this_scale, ang_scale));
 	x1 = (centerxfrac + FixedMul (tx,xscale)) >>FRACBITS;
 
 	// off the right side?
@@ -1260,16 +1260,16 @@ static void R_ProjectSprite(mobj_t *thing)
 		fixed_t yscale2;
 		INT32 range;
 
-		tr_x = thing->x + FixedMul(-leftoffset, FINECOSINE(ang>>ANGLETOFINESHIFT)) - viewx;
-		tr_y = thing->y + FixedMul(-leftoffset, FINESINE(ang>>ANGLETOFINESHIFT)) - viewy;
+		tr_x = thing->x + FixedMul(-offset, FINECOSINE(ang>>ANGLETOFINESHIFT)) - viewx;
+		tr_y = thing->y + FixedMul(-offset, FINESINE(ang>>ANGLETOFINESHIFT)) - viewy;
 		gxt = FixedMul(tr_x, viewcos);
 		gyt = -FixedMul(tr_y, viewsin);
 		tz = gxt-gyt;
 		yscale1 = FixedDiv(projectiony, tz);
 
-		leftoffset += spritecachedinfo[lump].width;
-		tr_x = thing->x + FixedMul(leftoffset, FINECOSINE(ang>>ANGLETOFINESHIFT)) - viewx;
-		tr_y = thing->y + FixedMul(leftoffset, FINESINE(ang>>ANGLETOFINESHIFT)) - viewy;
+		offset += spritecachedinfo[lump].width;
+		tr_x = thing->x + FixedMul(offset, FINECOSINE(ang>>ANGLETOFINESHIFT)) - viewx;
+		tr_y = thing->y + FixedMul(offset, FINESINE(ang>>ANGLETOFINESHIFT)) - viewy;
 		gxt = FixedMul(tr_x, viewcos);
 		gyt = -FixedMul(tr_y, viewsin);
 		tz = gxt-gyt;

From 63e58a02f8a5709d061472f7809db6b1788c7c05 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Wed, 17 Aug 2016 00:15:23 +0100
Subject: [PATCH 07/41] Fixed the issue mentioned in last commit, but still not
 the one from before that. Hrm.

---
 src/r_things.c | 8 ++++----
 1 file changed, 4 insertions(+), 4 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index 522171d7d..5dec34d1d 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1260,16 +1260,16 @@ static void R_ProjectSprite(mobj_t *thing)
 		fixed_t yscale2;
 		INT32 range;
 
-		tr_x = thing->x + FixedMul(-offset, FINECOSINE(ang>>ANGLETOFINESHIFT)) - viewx;
-		tr_y = thing->y + FixedMul(-offset, FINESINE(ang>>ANGLETOFINESHIFT)) - viewy;
+		tr_x = thing->x + FixedMul(-offset, FINECOSINE(thing->angle>>ANGLETOFINESHIFT)) - viewx;
+		tr_y = thing->y + FixedMul(-offset, FINESINE(thing->angle>>ANGLETOFINESHIFT)) - viewy;
 		gxt = FixedMul(tr_x, viewcos);
 		gyt = -FixedMul(tr_y, viewsin);
 		tz = gxt-gyt;
 		yscale1 = FixedDiv(projectiony, tz);
 
 		offset += spritecachedinfo[lump].width;
-		tr_x = thing->x + FixedMul(offset, FINECOSINE(ang>>ANGLETOFINESHIFT)) - viewx;
-		tr_y = thing->y + FixedMul(offset, FINESINE(ang>>ANGLETOFINESHIFT)) - viewy;
+		tr_x = thing->x + FixedMul(offset, FINECOSINE(thing->angle>>ANGLETOFINESHIFT)) - viewx;
+		tr_y = thing->y + FixedMul(offset, FINESINE(thing->angle>>ANGLETOFINESHIFT)) - viewy;
 		gxt = FixedMul(tr_x, viewcos);
 		gyt = -FixedMul(tr_y, viewsin);
 		tz = gxt-gyt;

From 2244e9162be351030438e1e4203812e6c5936a26 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Wed, 17 Aug 2016 19:09:59 +0100
Subject: [PATCH 08/41] Some overflow checks. They're not proper like the other
 overflow checks, but they remove all the situations I've been able to
 discover without making stuff unnecessarily disappear.

---
 src/r_things.c | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/src/r_things.c b/src/r_things.c
index 5dec34d1d..155e3efdc 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1266,6 +1266,7 @@ static void R_ProjectSprite(mobj_t *thing)
 		gyt = -FixedMul(tr_y, viewsin);
 		tz = gxt-gyt;
 		yscale1 = FixedDiv(projectiony, tz);
+		if (yscale1 < 64) return; // Fix some funky visuals
 
 		offset += spritecachedinfo[lump].width;
 		tr_x = thing->x + FixedMul(offset, FINECOSINE(thing->angle>>ANGLETOFINESHIFT)) - viewx;
@@ -1274,6 +1275,7 @@ static void R_ProjectSprite(mobj_t *thing)
 		gyt = -FixedMul(tr_y, viewsin);
 		tz = gxt-gyt;
 		yscale2 = FixedDiv(projectiony, tz);
+		if (yscale2 < 64) return; // Fix some funky visuals
 
 		if (ang >= ANGLE_180)
 		{
@@ -1287,8 +1289,8 @@ static void R_ProjectSprite(mobj_t *thing)
 		else
 			range = 1;
 		scalestep = (yscale2 - yscale1)/range;
-		yscale = yscale1;
 		//this_scale = FixedMul(this_scale, FixedDiv(yscale, yscale1));
+		yscale = yscale1;
 	}
 
 	xscale = FixedMul(xscale, ang_scale);

From 709665945059f9bc733e5ed6358ba71d62fcab80 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Wed, 17 Aug 2016 21:11:05 +0100
Subject: [PATCH 09/41] Thanks to MI for helping me notice a scaling issue by
 code alone

---
 src/r_things.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/r_things.c b/src/r_things.c
index 155e3efdc..7000dc05b 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -925,7 +925,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
 		if (vis->scalestep)
 		{
 			sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
-			dc_iscale = FixedMul((0xffffffffu / (unsigned)spryscale), this_scale);
+			dc_iscale = FixedDiv((0xffffffffu / (unsigned)spryscale), this_scale);
 		}
 		if (vis->vflip)
 			R_DrawFlippedMaskedColumn(column, patch->height);

From 6c559946f0a4af39fda31c743f5349a5fe8738b9 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Wed, 17 Aug 2016 21:14:01 +0100
Subject: [PATCH 10/41] ok no i misunderstood what he was getting at

---
 src/r_things.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/src/r_things.c b/src/r_things.c
index 7000dc05b..e581b124b 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -884,6 +884,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
 		if (!vis->isScaled)
 		{
 			vis->scale = FixedMul(vis->scale, this_scale);
+			vis->scalestep = FixedMul(vis->scalestep, this_scale);
 			spryscale = vis->scale;
 			dc_iscale = FixedDiv(FRACUNIT, vis->scale);
 			vis->xiscale = FixedDiv(vis->xiscale,this_scale);
@@ -925,7 +926,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
 		if (vis->scalestep)
 		{
 			sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
-			dc_iscale = FixedDiv((0xffffffffu / (unsigned)spryscale), this_scale);
+			dc_iscale = (0xffffffffu / (unsigned)spryscale)
 		}
 		if (vis->vflip)
 			R_DrawFlippedMaskedColumn(column, patch->height);

From 66a737a7f0839cd729eaec05679b06ecba26081f Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Wed, 17 Aug 2016 21:24:53 +0100
Subject: [PATCH 11/41] fuuuuuuuu

---
 src/r_things.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/r_things.c b/src/r_things.c
index e581b124b..ee441b7d5 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -926,7 +926,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
 		if (vis->scalestep)
 		{
 			sprtopscreen = (centeryfrac - FixedMul(dc_texturemid, spryscale));
-			dc_iscale = (0xffffffffu / (unsigned)spryscale)
+			dc_iscale = (0xffffffffu / (unsigned)spryscale);
 		}
 		if (vis->vflip)
 			R_DrawFlippedMaskedColumn(column, patch->height);

From 9231a4653c400a77bfc47ad132bf6841f5b00d15 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Wed, 17 Aug 2016 22:19:28 +0100
Subject: [PATCH 12/41] SORTED, THANKS MI

http://gfycat.com/SimpleShallowDeviltasmanian

now to put this behind some sort of flag and optimise it
---
 src/r_things.c | 23 ++++++++++-------------
 1 file changed, 10 insertions(+), 13 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index ee441b7d5..7892dbb7b 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1238,10 +1238,10 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	// calculate edges of the shape
 	if (flip)
-		offset = spritecachedinfo[lump].width-spritecachedinfo[lump].offset;
+		offset = spritecachedinfo[lump].offset - spritecachedinfo[lump].width;
 	else
-		offset = spritecachedinfo[lump].offset;
-	tx -= FixedMul(offset, FixedMul(this_scale, ang_scale));
+		offset = -spritecachedinfo[lump].offset;
+	tx += FixedMul(offset, FixedMul(this_scale, ang_scale));
 	x1 = (centerxfrac + FixedMul (tx,xscale)) >>FRACBITS;
 
 	// off the right side?
@@ -1257,17 +1257,16 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	if (1) // (flatsprite)
 	{
-		fixed_t yscale1;
 		fixed_t yscale2;
 		INT32 range;
 
-		tr_x = thing->x + FixedMul(-offset, FINECOSINE(thing->angle>>ANGLETOFINESHIFT)) - viewx;
-		tr_y = thing->y + FixedMul(-offset, FINESINE(thing->angle>>ANGLETOFINESHIFT)) - viewy;
+		tr_x = thing->x + FixedMul(offset, FINECOSINE(thing->angle>>ANGLETOFINESHIFT)) - viewx;
+		tr_y = thing->y + FixedMul(offset, FINESINE(thing->angle>>ANGLETOFINESHIFT)) - viewy;
 		gxt = FixedMul(tr_x, viewcos);
 		gyt = -FixedMul(tr_y, viewsin);
 		tz = gxt-gyt;
-		yscale1 = FixedDiv(projectiony, tz);
-		if (yscale1 < 64) return; // Fix some funky visuals
+		yscale = FixedDiv(projectiony, tz);
+		if (yscale < 64) return; // Fix some funky visuals
 
 		offset += spritecachedinfo[lump].width;
 		tr_x = thing->x + FixedMul(offset, FINECOSINE(thing->angle>>ANGLETOFINESHIFT)) - viewx;
@@ -1281,17 +1280,15 @@ static void R_ProjectSprite(mobj_t *thing)
 		if (ang >= ANGLE_180)
 		{
 			fixed_t temp = yscale2;
-			yscale2 = yscale1;
-			yscale1 = temp;
+			yscale2 = yscale;
+			yscale = temp;
 		}
 
 		if (x2 > x1)
 			range = (x2 - x1);
 		else
 			range = 1;
-		scalestep = (yscale2 - yscale1)/range;
-		//this_scale = FixedMul(this_scale, FixedDiv(yscale, yscale1));
-		yscale = yscale1;
+		scalestep = (yscale2 - yscale)/range;
 	}
 
 	xscale = FixedMul(xscale, ang_scale);

From 2d3ebc5e49d63d7427bf155023322b1af9ee5a4c Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Thu, 18 Aug 2016 12:40:45 +0100
Subject: [PATCH 13/41] Fixed the last of the odd stretching by: * recognising
 that the offsets weren't going to be accurate if you just SWAPPED yscale and
 yscale2 over 180 degrees * taking scale into account consistently

also, some optimisations
---
 src/r_things.c | 45 ++++++++++++++++++++++++---------------------
 1 file changed, 24 insertions(+), 21 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index 7892dbb7b..3d84813bb 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1117,8 +1117,9 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	angle_t ang;
 	fixed_t iscale;
-	fixed_t scalestep = 0; // toast '16
-	fixed_t offset;
+	fixed_t scalestep; // toast '16
+	fixed_t offset, offset2;
+	boolean flatsprite = true; //(thing->flags2 & MF2_PAPER);
 
 	//SoM: 3/17/2000
 	fixed_t gz, gzt;
@@ -1198,13 +1199,11 @@ static void R_ProjectSprite(mobj_t *thing)
 		I_Error("R_ProjectSprite: sprframes NULL for sprite %d\n", thing->sprite);
 #endif
 
-	if (1) //(sprframe->rotate != SRF_SINGLE || flatsprite)
-		ang = R_PointToAngle (thing->x, thing->y) - thing->angle;
-
-	if (1) //(flatsprite)
+	if (sprframe->rotate != SRF_SINGLE || flatsprite)
 	{
-		ang_scale = abs(FINESINE(ang>>ANGLETOFINESHIFT));
-		//scalestep = (FINECOSINE(ang>>ANGLETOFINESHIFT));
+		ang = R_PointToAngle (thing->x, thing->y) - thing->angle;
+		if (flatsprite)
+			ang_scale = abs(FINESINE(ang>>ANGLETOFINESHIFT));
 	}
 
 	if (sprframe->rotate == SRF_SINGLE)
@@ -1241,25 +1240,34 @@ static void R_ProjectSprite(mobj_t *thing)
 		offset = spritecachedinfo[lump].offset - spritecachedinfo[lump].width;
 	else
 		offset = -spritecachedinfo[lump].offset;
-	tx += FixedMul(offset, FixedMul(this_scale, ang_scale));
+	offset = FixedMul(offset, this_scale);
+	tx += FixedMul(offset, ang_scale);
 	x1 = (centerxfrac + FixedMul (tx,xscale)) >>FRACBITS;
 
 	// off the right side?
 	if (x1 > viewwidth)
 		return;
 
-	tx += FixedMul(spritecachedinfo[lump].width, FixedMul(this_scale, ang_scale));
+	offset2 = FixedMul(spritecachedinfo[lump].width, this_scale);
+	tx += FixedMul(offset2, ang_scale);
 	x2 = ((centerxfrac + FixedMul (tx,xscale)) >>FRACBITS) - 1;
 
 	// off the left side
 	if (x2 < 0)
 		return;
 
-	if (1) // (flatsprite)
+	if (flatsprite)
 	{
 		fixed_t yscale2;
 		INT32 range;
 
+		if (ang >= ANGLE_180)
+		{
+			offset *= -1;
+			offset2 *= -1;
+		}
+		offset2 += offset;
+
 		tr_x = thing->x + FixedMul(offset, FINECOSINE(thing->angle>>ANGLETOFINESHIFT)) - viewx;
 		tr_y = thing->y + FixedMul(offset, FINESINE(thing->angle>>ANGLETOFINESHIFT)) - viewy;
 		gxt = FixedMul(tr_x, viewcos);
@@ -1268,28 +1276,23 @@ static void R_ProjectSprite(mobj_t *thing)
 		yscale = FixedDiv(projectiony, tz);
 		if (yscale < 64) return; // Fix some funky visuals
 
-		offset += spritecachedinfo[lump].width;
-		tr_x = thing->x + FixedMul(offset, FINECOSINE(thing->angle>>ANGLETOFINESHIFT)) - viewx;
-		tr_y = thing->y + FixedMul(offset, FINESINE(thing->angle>>ANGLETOFINESHIFT)) - viewy;
+		tr_x = thing->x + FixedMul(offset2, FINECOSINE(thing->angle>>ANGLETOFINESHIFT)) - viewx;
+		tr_y = thing->y + FixedMul(offset2, FINESINE(thing->angle>>ANGLETOFINESHIFT)) - viewy;
 		gxt = FixedMul(tr_x, viewcos);
 		gyt = -FixedMul(tr_y, viewsin);
 		tz = gxt-gyt;
 		yscale2 = FixedDiv(projectiony, tz);
 		if (yscale2 < 64) return; // Fix some funky visuals
 
-		if (ang >= ANGLE_180)
-		{
-			fixed_t temp = yscale2;
-			yscale2 = yscale;
-			yscale = temp;
-		}
-
 		if (x2 > x1)
 			range = (x2 - x1);
 		else
 			range = 1;
+
 		scalestep = (yscale2 - yscale)/range;
 	}
+	else
+		scalestep = 0;
 
 	xscale = FixedMul(xscale, ang_scale);
 

From ce8ae48222594dfc07f3dfc1e30e350d1c91422a Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Thu, 18 Aug 2016 12:55:04 +0100
Subject: [PATCH 14/41] I think this is as optimised as it's gonna get. Now
 onto the flag!

---
 src/r_things.c | 16 +++++++++-------
 1 file changed, 9 insertions(+), 7 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index 3d84813bb..c8667ca40 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1258,7 +1258,7 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	if (flatsprite)
 	{
-		fixed_t yscale2;
+		fixed_t yscale2, cosmul, sinmul;
 		INT32 range;
 
 		if (ang >= ANGLE_180)
@@ -1266,23 +1266,25 @@ static void R_ProjectSprite(mobj_t *thing)
 			offset *= -1;
 			offset2 *= -1;
 		}
-		offset2 += offset;
 
-		tr_x = thing->x + FixedMul(offset, FINECOSINE(thing->angle>>ANGLETOFINESHIFT)) - viewx;
-		tr_y = thing->y + FixedMul(offset, FINESINE(thing->angle>>ANGLETOFINESHIFT)) - viewy;
+		cosmul = FINECOSINE(thing->angle>>ANGLETOFINESHIFT);
+		sinmul = FINESINE(thing->angle>>ANGLETOFINESHIFT);
+
+		tr_x += FixedMul(offset, cosmul);
+		tr_y += FixedMul(offset, sinmul);
 		gxt = FixedMul(tr_x, viewcos);
 		gyt = -FixedMul(tr_y, viewsin);
 		tz = gxt-gyt;
 		yscale = FixedDiv(projectiony, tz);
 		if (yscale < 64) return; // Fix some funky visuals
 
-		tr_x = thing->x + FixedMul(offset2, FINECOSINE(thing->angle>>ANGLETOFINESHIFT)) - viewx;
-		tr_y = thing->y + FixedMul(offset2, FINESINE(thing->angle>>ANGLETOFINESHIFT)) - viewy;
+		tr_x += FixedMul(offset2, cosmul);
+		tr_y += FixedMul(offset2, sinmul);
 		gxt = FixedMul(tr_x, viewcos);
 		gyt = -FixedMul(tr_y, viewsin);
 		tz = gxt-gyt;
 		yscale2 = FixedDiv(projectiony, tz);
-		if (yscale2 < 64) return; // Fix some funky visuals
+		if (yscale2 < 64) return; // ditto
 
 		if (x2 > x1)
 			range = (x2 - x1);

From 430d7cfbd2c35e6aeb2ad46336f12f149afe631f Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Thu, 18 Aug 2016 15:45:44 +0100
Subject: [PATCH 15/41] Noticed some sorting issues, so introduced the
 sortscale struct variable. (SORRY, NO FLAG YET)

---
 src/r_things.c | 28 ++++++++++++++++------------
 src/r_things.h |  2 +-
 2 files changed, 17 insertions(+), 13 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index c8667ca40..96224fd62 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1027,7 +1027,7 @@ static void R_SplitSprite(vissprite_t *sprite, mobj_t *thing)
 		if (testheight <= sprite->gz)
 			return;
 
-		cutfrac = (INT16)((centeryfrac - FixedMul(testheight - viewz, sprite->scale))>>FRACBITS);
+		cutfrac = (INT16)((centeryfrac - FixedMul(testheight - viewz, sprite->sortscale))>>FRACBITS);
 		if (cutfrac < 0)
 			continue;
 		if (cutfrac > viewheight)
@@ -1100,7 +1100,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	fixed_t tr_x, tr_y;
 	fixed_t gxt, gyt;
 	fixed_t tx, tz;
-	fixed_t xscale, yscale; //added : 02-02-98 : aaargll..if I were a math-guy!!!
+	fixed_t xscale, yscale, sortscale; //added : 02-02-98 : aaargll..if I were a math-guy!!!
 
 	INT32 x1, x2;
 
@@ -1152,7 +1152,7 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	// aspect ratio stuff
 	xscale = FixedDiv(projection, tz);
-	yscale = FixedDiv(projectiony, tz);
+	sortscale = FixedDiv(projectiony, tz);
 
 	// decide which patch to use for sprite relative to player
 #ifdef RANGECHECK
@@ -1294,7 +1294,10 @@ static void R_ProjectSprite(mobj_t *thing)
 		scalestep = (yscale2 - yscale)/range;
 	}
 	else
+	{
+		yscale = sortscale;
 		scalestep = 0;
+	}
 
 	xscale = FixedMul(xscale, ang_scale);
 
@@ -1379,6 +1382,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	vis->heightsec = heightsec; //SoM: 3/17/2000
 	vis->mobjflags = thing->flags;
 	vis->scale = yscale; //<<detailshift;
+	vis->sortscale = sortscale;
 	vis->dispoffset = thing->info->dispoffset; // Monster Iestyn: 23/11/15
 	vis->gx = thing->x;
 	vis->gy = thing->y;
@@ -1778,14 +1782,14 @@ void R_SortVisSprites(void)
 		bestscale = bestdispoffset = INT32_MAX;
 		for (ds = unsorted.next; ds != &unsorted; ds = ds->next)
 		{
-			if (ds->scale < bestscale)
+			if (ds->sortscale < bestscale)
 			{
-				bestscale = ds->scale;
+				bestscale = ds->sortscale;
 				bestdispoffset = ds->dispoffset;
 				best = ds;
 			}
 			// order visprites of same scale by dispoffset, smallest first
-			else if (ds->scale == bestscale && ds->dispoffset < bestdispoffset)
+			else if (ds->sortscale == bestscale && ds->dispoffset < bestdispoffset)
 			{
 				bestdispoffset = ds->dispoffset;
 				best = ds;
@@ -2039,8 +2043,8 @@ static void R_CreateDrawNodes(void)
 				if (r2->sprite->szt > rover->sz || r2->sprite->sz < rover->szt)
 					continue;
 
-				if (r2->sprite->scale > rover->scale
-				 || (r2->sprite->scale == rover->scale && r2->sprite->dispoffset > rover->dispoffset))
+				if (r2->sprite->sortscale > rover->scale
+				 || (r2->sprite->sortscale == rover->scale && r2->sprite->dispoffset > rover->dispoffset))
 				{
 					entry = R_CreateDrawNode(NULL);
 					(entry->prev = r2->prev)->next = entry;
@@ -2193,8 +2197,8 @@ void R_ClipSprites(void)
 				scale = ds->scale2;
 			}
 
-			if (scale < spr->scale ||
-			    (lowscale < spr->scale &&
+			if (scale < spr->sortscale ||
+			    (lowscale < spr->sortscale &&
 			     !R_PointOnSegSide (spr->gx, spr->gy, ds->curline)))
 			{
 				// masked mid texture?
@@ -2245,7 +2249,7 @@ void R_ClipSprites(void)
 			fixed_t mh, h;
 			INT32 phs = viewplayer->mo->subsector->sector->heightsec;
 			if ((mh = sectors[spr->heightsec].floorheight) > spr->gz &&
-				(h = centeryfrac - FixedMul(mh -= viewz, spr->scale)) >= 0 &&
+				(h = centeryfrac - FixedMul(mh -= viewz, spr->sortscale)) >= 0 &&
 				(h >>= FRACBITS) < viewheight)
 			{
 				if (mh <= 0 || (phs != -1 && viewz > sectors[phs].floorheight))
@@ -2263,7 +2267,7 @@ void R_ClipSprites(void)
 			}
 
 			if ((mh = sectors[spr->heightsec].ceilingheight) < spr->gzt &&
-			    (h = centeryfrac - FixedMul(mh-viewz, spr->scale)) >= 0 &&
+			    (h = centeryfrac - FixedMul(mh-viewz, spr->sortscale)) >= 0 &&
 			    (h >>= FRACBITS) < viewheight)
 			{
 				if (phs != -1 && viewz >= sectors[phs].ceilingheight)
diff --git a/src/r_things.h b/src/r_things.h
index 0941082aa..360ead433 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -135,7 +135,7 @@ typedef struct vissprite_s
 	fixed_t pz, pzt; // physical bottom/top for sorting with 3D floors
 
 	fixed_t startfrac; // horizontal position of x1
-	fixed_t scale;
+	fixed_t scale, sortscale; // sortscale only differs from scale for flat sprites
 	fixed_t scalestep; // only for flat sprites, 0 otherwise
 	fixed_t xiscale; // negative if flipped
 

From 7786ef43e876c6280e2f53c0ce32569bb855c5f6 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Thu, 18 Aug 2016 20:09:35 +0100
Subject: [PATCH 16/41] Okay, did a bunch of stuff. Getting ready to create a
 merge request, but not quite there yet.

* MF_AMBUSH is now MF2_AMBUSH, because it's something you turn on in a map editor, not with a SOC definition.
* Where MF_AMBUSH was is now MF_PAPER.
* MF_PAPER accesses all the stuff I did previously in this branch...
* ...as well as turn on paper-thin collision detection between mobjs, which I've gotten working but isn't perfect but it's still good enough for non-solid objects!!
---
 src/dehacked.c |  3 ++-
 src/m_cheat.c  |  2 +-
 src/p_enemy.c  | 18 +++++++-------
 src/p_inter.c  |  2 +-
 src/p_map.c    | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++
 src/p_mobj.c   | 30 ++++++++++-------------
 src/p_mobj.h   |  8 +++----
 src/p_user.c   |  8 +++----
 src/r_things.c |  2 +-
 9 files changed, 99 insertions(+), 38 deletions(-)

diff --git a/src/dehacked.c b/src/dehacked.c
index f2b906c23..6b189a3ea 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -6691,7 +6691,7 @@ static const char *const MOBJFLAG_LIST[] = {
 	"SHOOTABLE",
 	"NOSECTOR",
 	"NOBLOCKMAP",
-	"AMBUSH",
+	"PAPER",
 	"PUSHABLE",
 	"BOSS",
 	"SPAWNCEILING",
@@ -6748,6 +6748,7 @@ static const char *const MOBJFLAG2_LIST[] = {
 	"BOSSNOTRAP",	// No Egg Trap after boss
 	"BOSSFLEE",		// Boss is fleeing!
 	"BOSSDEAD",		// Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
+	"AMBUSH",       // Alternate behaviour typically set by MTF_AMBUSH
 	NULL
 };
 
diff --git a/src/m_cheat.c b/src/m_cheat.c
index 1c5f835cb..76f4356df 100644
--- a/src/m_cheat.c
+++ b/src/m_cheat.c
@@ -1054,7 +1054,7 @@ void OP_NightsObjectplace(player_t *player)
 		if (!OP_HeightOkay(player, false))
 			return;
 
-		if (player->mo->target->flags & MF_AMBUSH)
+		if (player->mo->target->flags2 & MF2_AMBUSH)
 			angle = (UINT16)player->anotherflyangle;
 		else
 		{
diff --git a/src/p_enemy.c b/src/p_enemy.c
index 4e11dc494..445acc359 100644
--- a/src/p_enemy.c
+++ b/src/p_enemy.c
@@ -2625,7 +2625,7 @@ for (i = cvar.value; i; --i) spawnchance[numchoices++] = type
 			newbox = spawnchance[P_RandomKey(numchoices)];
 			item = mobjinfo[newbox].damage;
 
-			remains->flags &= ~MF_AMBUSH;
+			remains->flags2 &= ~MF2_AMBUSH;
 			break;
 		}
 		default:
@@ -3444,7 +3444,7 @@ void A_BubbleSpawn(mobj_t *actor)
 	}
 	actor->flags2 &= ~MF2_DONTDRAW;
 
-	if (!(actor->flags & MF_AMBUSH))
+	if (!(actor->flags2 & MF2_AMBUSH))
 	{
 		// Quick! Look through players!
 		// Don't spawn bubbles unless a player is relatively close by (var2).
@@ -3492,7 +3492,7 @@ void A_FanBubbleSpawn(mobj_t *actor)
 	if (!(actor->eflags & MFE_UNDERWATER))
 		return;
 
-	if (!(actor->flags & MF_AMBUSH))
+	if (!(actor->flags2 & MF2_AMBUSH))
 	{
 	// Quick! Look through players!
 	// Don't spawn bubbles unless a player is relatively close by (var2).
@@ -4038,7 +4038,7 @@ void A_JetChase(mobj_t *actor)
 		return;
 #endif
 
-	if (actor->flags & MF_AMBUSH)
+	if (actor->flags2 & MF2_AMBUSH)
 		return;
 
 	if (actor->z >= actor->waterbottom && actor->watertop > actor->floorz
@@ -4931,7 +4931,7 @@ void A_SlingAppear(mobj_t *actor)
 		if (firsttime)
 		{
 			// This is the outermost link in the chain
-			spawnee->flags |= MF_AMBUSH;
+			spawnee->flags2 |= MF2_AMBUSH;
 			firsttime = false;
 		}
 
@@ -5916,7 +5916,7 @@ void A_Boss2Chase(mobj_t *actor)
 	{
 		actor->watertop = -actor->watertop;
 		actor->extravalue1 = 18;
-		if (actor->flags & MF_AMBUSH)
+		if (actor->flags2 & MF2_AMBUSH)
 			actor->extravalue1 -= (actor->info->spawnhealth - actor->health)*2;
 		actor->extravalue2 = actor->extravalue1;
 	}
@@ -5942,7 +5942,7 @@ void A_Boss2Chase(mobj_t *actor)
 	else
 	{
 		// Only speed up if you have the 'Deaf' flag.
-		if (actor->flags & MF_AMBUSH)
+		if (actor->flags2 & MF2_AMBUSH)
 			speedvar = actor->health;
 		else
 			speedvar = actor->info->spawnhealth;
@@ -6533,7 +6533,7 @@ void A_BuzzFly(mobj_t *actor)
 	if (LUA_CallAction("A_BuzzFly", actor))
 		return;
 #endif
-	if (actor->flags & MF_AMBUSH)
+	if (actor->flags2 & MF2_AMBUSH)
 		return;
 
 	if (actor->reactiontime)
@@ -6673,7 +6673,7 @@ void A_GuardChase(mobj_t *actor)
 		return; // got a new target
 
 	// chase towards player
-	if (--actor->movecount < 0 || !P_Move(actor, (actor->flags & MF_AMBUSH) ? actor->info->speed * 2 : actor->info->speed))
+	if (--actor->movecount < 0 || !P_Move(actor, (actor->flags2 & MF2_AMBUSH) ? actor->info->speed * 2 : actor->info->speed))
 	{
 		P_NewChaseDir(actor);
 		actor->movecount += 5; // Increase tics before change in direction allowed.
diff --git a/src/p_inter.c b/src/p_inter.c
index b0d8e4aac..237112ea5 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -1327,7 +1327,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 		case MT_SMALLMACECHAIN:
 		case MT_BIGMACECHAIN:
 			// Is this the last link in the chain?
-			if (toucher->momz > 0 || !(special->flags & MF_AMBUSH)
+			if (toucher->momz > 0 || !(special->flags2 & MF2_AMBUSH)
 				|| (player->pflags & PF_ITEMHANG) || (player->pflags & PF_MACESPIN))
 				return;
 
diff --git a/src/p_map.c b/src/p_map.c
index 9bcd1b29e..452d8c222 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -498,6 +498,70 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
 		return true; // didn't hit it
 
+	if (thing->flags & MF_PAPER)
+	{
+		fixed_t cosradius, sinradius;
+		vertex_t v1, v2; // fake vertexes
+		line_t junk; // fake linedef
+
+		cosradius = FixedMul(thing->radius, FINECOSINE(thing->angle>>ANGLETOFINESHIFT));
+		sinradius = FixedMul(thing->radius, FINESINE(thing->angle>>ANGLETOFINESHIFT));
+
+		v1.x = thing->x - cosradius;
+		v1.y = thing->y - sinradius;
+		v2.x = thing->x + cosradius;
+		v2.y = thing->y + sinradius;
+
+		junk.v1 = &v1;
+		junk.v2 = &v2;
+		junk.dx = v2.x - v1.x;
+		junk.dy = v2.y - v1.y;
+
+		if (tmthing->flags & MF_PAPER)
+		{
+			cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
+			sinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
+			if (P_PointOnLineSide(tmx - cosradius, tmy - sinradius, &junk)
+			== P_PointOnLineSide(tmx + cosradius, tmy + sinradius, &junk))
+				return true; // the line doesn't cross between collider's start or end
+		}
+		else
+		{
+			if ((P_PointOnLineSide(tmx - tmthing->radius, tmy - tmthing->radius, &junk)
+			== P_PointOnLineSide(tmx + tmthing->radius, tmy + tmthing->radius, &junk))
+			&& (P_PointOnLineSide(tmx + tmthing->radius, tmy - tmthing->radius, &junk)
+			== P_PointOnLineSide(tmx - tmthing->radius, tmy + tmthing->radius, &junk)))
+				return true; // the line doesn't cross between either pair of opposite corners
+		}
+	}
+	else if (tmthing->flags & MF_PAPER)
+	{
+		fixed_t cosradius, sinradius;
+		vertex_t v1, v2; // fake vertexes
+		line_t junk; // fake linedef
+
+		cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
+		sinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
+
+		v1.x = tmx - cosradius;
+		v1.y = tmy - sinradius;
+		v2.x = tmx + cosradius;
+		v2.y = tmy + sinradius;
+
+		junk.v1 = &v1;
+		junk.v2 = &v2;
+		junk.dx = v2.x - v1.x;
+		junk.dy = v2.y - v1.y;
+
+		// no need to check whether thing has MF_PAPER, since checked above
+
+		if ((P_PointOnLineSide(thing->x - thing->radius, thing->y - thing->radius, &junk)
+		== P_PointOnLineSide(thing->x + thing->radius, thing->y + thing->radius, &junk))
+		&& (P_PointOnLineSide(thing->x + thing->radius, thing->y - thing->radius, &junk)
+		== P_PointOnLineSide(thing->x - thing->radius, thing->y + thing->radius, &junk)))
+			return true; // the line doesn't cross between either pair of opposite corners
+	}
+
 #ifdef HAVE_BLUA
 	{
 		UINT8 shouldCollide = LUAh_MobjCollide(thing, tmthing); // checks hook for thing's type
diff --git a/src/p_mobj.c b/src/p_mobj.c
index fe09f6c01..50d90834a 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -2653,7 +2653,7 @@ static boolean P_ZMovement(mobj_t *mo)
 						&& abs(mom.y) < FixedMul(STOPSPEED, mo->scale)
 						&& abs(mom.z) < FixedMul(STOPSPEED*3, mo->scale))
 					{
-						if (mo->flags & MF_AMBUSH)
+						if (mo->flags2 & MF2_AMBUSH)
 						{
 							// If deafed, give the tumbleweed another random kick if it runs out of steam.
 							mom.z += P_MobjFlip(mo)*FixedMul(6*FRACUNIT, mo->scale);
@@ -6582,7 +6582,7 @@ void P_MobjThinker(mobj_t *mobj)
 
 					flame->angle = mobj->angle;
 
-					if (mobj->flags & MF_AMBUSH) // Wave up and down instead of side-to-side
+					if (mobj->flags2 & MF2_AMBUSH) // Wave up and down instead of side-to-side
 						flame->momz = mobj->fuse << (FRACBITS-2);
 					else
 						flame->angle += FixedAngle(mobj->fuse*FRACUNIT);
@@ -6617,7 +6617,7 @@ void P_MobjThinker(mobj_t *mobj)
 					strength -= ((20*FRACUNIT)/16)*mobj->movedir;
 
 					// If deaf'd, the object spawns on the ceiling.
-					if (mobj->flags & MF_AMBUSH)
+					if (mobj->flags2 & MF2_AMBUSH)
 					{
 						mobj->z = mobj->ceilingz-mobj->height;
 						flame->momz = -strength;
@@ -7473,7 +7473,7 @@ void P_MobjThinker(mobj_t *mobj)
 				case MT_EGGMANBOX: // Eggman box
 				case MT_GRAVITYBOX: // Gravity box
 				case MT_QUESTIONBOX:
-					if ((mobj->flags & MF_AMBUSH || mobj->flags2 & MF2_STRONGBOX) && mobj->type != MT_QUESTIONBOX)
+					if ((mobj->flags2 & (MF2_AMBUSH|MF2_STRONGBOX)) && mobj->type != MT_QUESTIONBOX)
 					{
 						mobjtype_t spawnchance[64];
 						INT32 numchoices = 0, i = 0;
@@ -7501,11 +7501,7 @@ for (i = ((mobj->flags2 & MF2_STRONGBOX) ? strongboxamt : weakboxamt); i; --i) s
 						i = P_RandomKey(numchoices); // Gotta love those random numbers!
 						newmobj = P_SpawnMobj(mobj->x, mobj->y, mobj->z, spawnchance[i]);
 
-						// If the monitor respawns randomly, transfer the flag.
-						if (mobj->flags & MF_AMBUSH)
-							newmobj->flags |= MF_AMBUSH;
-
-						// Transfer flags2 (strongbox, objectflip)
+						// Transfer flags2 (strongbox, objectflip, ambush)
 						newmobj->flags2 = mobj->flags2;
 					}
 					else
@@ -9322,7 +9318,7 @@ ML_NOCLIMB : Direction not controllable
 			if (firsttime)
 			{
 				// This is the outermost link in the chain
-				spawnee->flags |= MF_AMBUSH;
+				spawnee->flags2 |= MF2_AMBUSH;
 				firsttime = false;
 			}
 
@@ -9394,7 +9390,7 @@ ML_NOCLIMB : Direction not controllable
 		{
 			// Inverted if uppermost bit is set
 			if (mthing->angle & 16384)
-				mobj->flags |= MF_AMBUSH;
+				mobj->flags2 |= MF2_AMBUSH;
 
 			if (mthing->angle > 0)
 				mobj->radius = (mthing->angle & 16383)*FRACUNIT;
@@ -9571,7 +9567,7 @@ ML_NOCLIMB : Direction not controllable
 					mthing->type == mobjinfo[MT_YELLOWTV].doomednum || mthing->type == mobjinfo[MT_BLUETV].doomednum ||
 					mthing->type == mobjinfo[MT_BLACKTV].doomednum || mthing->type == mobjinfo[MT_PITYTV].doomednum ||
 					mthing->type == mobjinfo[MT_RECYCLETV].doomednum || mthing->type == mobjinfo[MT_MIXUPBOX].doomednum)
-						mobj->flags |= MF_AMBUSH;
+						mobj->flags2 |= MF2_AMBUSH;
 			}
 
 			else if (mthing->type != mobjinfo[MT_AXIS].doomednum &&
@@ -9579,7 +9575,7 @@ ML_NOCLIMB : Direction not controllable
 				mthing->type != mobjinfo[MT_AXISTRANSFERLINE].doomednum &&
 				mthing->type != mobjinfo[MT_NIGHTSBUMPER].doomednum &&
 				mthing->type != mobjinfo[MT_STARPOST].doomednum)
-				mobj->flags |= MF_AMBUSH;
+				mobj->flags2 |= MF2_AMBUSH;
 		}
 
 		if (mthing->options & MTF_OBJECTSPECIAL)
@@ -9918,7 +9914,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 			P_SetMobjState(mobj, mobj->info->seestate);
 
 		mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
-		mobj->flags |= MF_AMBUSH;
+		mobj->flags2 |= MF2_AMBUSH;
 		mthing->mobj = mobj;
 	}
 	// All manners of rings and coins
@@ -9992,7 +9988,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 		}
 
 		mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
-		mobj->flags |= MF_AMBUSH;
+		mobj->flags2 |= MF2_AMBUSH;
 		mthing->mobj = mobj;
 	}
 	// ***
@@ -10048,7 +10044,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 
 			mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
 			if (mthing->options & MTF_AMBUSH)
-				mobj->flags |= MF_AMBUSH;
+				mobj->flags2 |= MF2_AMBUSH;
 		}
 	}
 	// Diagonal rings (handles both types)
@@ -10106,7 +10102,7 @@ void P_SpawnHoopsAndRings(mapthing_t *mthing)
 
 			mobj->angle = FixedAngle(mthing->angle*FRACUNIT);
 			if (mthing->options & MTF_AMBUSH)
-				mobj->flags |= MF_AMBUSH;
+				mobj->flags2 |= MF2_AMBUSH;
 		}
 	}
 	// Rings of items (all six of them)
diff --git a/src/p_mobj.h b/src/p_mobj.h
index b6592cb3d..c6738d372 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -107,8 +107,8 @@ typedef enum
 	MF_NOSECTOR         = 1<<3,
 	// Don't use the blocklinks (inert but displayable)
 	MF_NOBLOCKMAP       = 1<<4,
-	// Not to be activated by sound, deaf monster.
-	MF_AMBUSH           = 1<<5,
+	// Paper-thin. Drawn like a midtexture, has a flat collision bound.
+	MF_PAPER            = 1<<5,
 	// You can push this object. It can activate switches and things by pushing it on top.
 	MF_PUSHABLE         = 1<<6,
 	// Object is a boss.
@@ -151,10 +151,9 @@ typedef enum
 	MF_PAIN             = 1<<24,
 	// This mobj will stick to any surface or solid object it touches.
 	MF_STICKY           = 1<<25,
-	// NiGHTS hidden item.  Goes to seestate and turns MF_SPECIAL when paralooped.
+	// NiGHTS hidden item. Goes to seestate and turns MF_SPECIAL when paralooped.
 	MF_NIGHTSITEM       = 1<<26,
 	// for chase camera, don't be blocked by things (partial clipping)
-	// (need comma at end of this for SOC editor)
 	MF_NOCLIPTHING      = 1<<27,
 	// Missile bounces like a grenade.
 	MF_GRENADEBOUNCE    = 1<<28,
@@ -192,6 +191,7 @@ typedef enum
 	MF2_BOSSNOTRAP     = 1<<24, // No Egg Trap after boss
 	MF2_BOSSFLEE       = 1<<25, // Boss is fleeing!
 	MF2_BOSSDEAD       = 1<<26, // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
+	MF2_AMBUSH         = 1<<27, // Alternate behaviour typically set by MTF_AMBUSH
 	// free: to and including 1<<31
 } mobjflag2_t;
 
diff --git a/src/p_user.c b/src/p_user.c
index d99d86dea..5a591394d 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -629,7 +629,7 @@ static void P_DeNightserizePlayer(player_t *player)
 		if (!(mo2->type == MT_NIGHTSDRONE))
 			continue;
 
-		if (mo2->flags & MF_AMBUSH)
+		if (mo2->flags2 & MF2_AMBUSH)
 			P_DamageMobj(player->mo, NULL, NULL, 1, DMG_INSTAKILL);
 
 		break;
@@ -4964,7 +4964,7 @@ static void P_NightsTransferPoints(player_t *player, fixed_t xspeed, fixed_t rad
 		boolean transfer1last = false;
 		boolean transfer2last = false;
 		vertex_t vertices[4];
-		fixed_t truexspeed = xspeed*(!(player->pflags & PF_TRANSFERTOCLOSEST) && player->mo->target->flags & MF_AMBUSH ? -1 : 1);
+		fixed_t truexspeed = xspeed*(!(player->pflags & PF_TRANSFERTOCLOSEST) && player->mo->target->flags2 & MF2_AMBUSH ? -1 : 1);
 
 		// Find next waypoint
 		for (th = thinkercap.next; th != &thinkercap; th = th->next)
@@ -5629,7 +5629,7 @@ static void P_NiGHTSMovement(player_t *player)
 
 	// The 'ambush' flag says you should rotate
 	// the other way around the axis.
-	if (player->mo->target->flags & MF_AMBUSH)
+	if (player->mo->target->flags2 & MF2_AMBUSH)
 		backwardaxis = true;
 
 	player->angle_pos = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y);
@@ -7976,7 +7976,7 @@ boolean P_MoveChaseCamera(player_t *player, camera_t *thiscam, boolean resetcall
 		}
 		else if (player->mo->target)
 		{
-			if (player->mo->target->flags & MF_AMBUSH)
+			if (player->mo->target->flags2 & MF2_AMBUSH)
 				angle = R_PointToAngle2(player->mo->target->x, player->mo->target->y, player->mo->x, player->mo->y);
 			else
 				angle = R_PointToAngle2(player->mo->x, player->mo->y, player->mo->target->x, player->mo->target->y);
diff --git a/src/r_things.c b/src/r_things.c
index 96224fd62..97c1b5d7b 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1119,7 +1119,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	fixed_t iscale;
 	fixed_t scalestep; // toast '16
 	fixed_t offset, offset2;
-	boolean flatsprite = true; //(thing->flags2 & MF2_PAPER);
+	boolean flatsprite = (thing->flags & MF_PAPER);
 
 	//SoM: 3/17/2000
 	fixed_t gz, gzt;

From 430a2e07cc09deeb14c1b19b2fab59278bb9d6a1 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Thu, 18 Aug 2016 20:55:24 +0100
Subject: [PATCH 17/41] Added linedef collision and fixed the following
 clipping bug, shown in gfy form.
 http://gfycat.com/BiodegradableNaturalHadrosaurus

---
 src/p_map.c | 21 +++++++++++++++++----
 1 file changed, 17 insertions(+), 4 deletions(-)

diff --git a/src/p_map.c b/src/p_map.c
index 452d8c222..1a79d8d5e 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -498,7 +498,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
 		return true; // didn't hit it
 
-	if (thing->flags & MF_PAPER)
+	if (thing->flags & MF_PAPER) // CAUTION! Very easy to get stuck inside MF_SOLID objects. Giving the player MF_PAPER is a bad idea unless you know what you're doing.
 	{
 		fixed_t cosradius, sinradius;
 		vertex_t v1, v2; // fake vertexes
@@ -517,12 +517,16 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		junk.dx = v2.x - v1.x;
 		junk.dy = v2.y - v1.y;
 
-		if (tmthing->flags & MF_PAPER)
+		if (tmthing->flags & MF_PAPER) // more strenuous checking to prevent clipping issues
 		{
+			INT32 check1, check2, check3, check4;
 			cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
 			sinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
-			if (P_PointOnLineSide(tmx - cosradius, tmy - sinradius, &junk)
-			== P_PointOnLineSide(tmx + cosradius, tmy + sinradius, &junk))
+			check1 = P_PointOnLineSide(tmx - cosradius, tmy - sinradius, &junk);
+			check2 = P_PointOnLineSide(tmx + cosradius, tmy + sinradius, &junk);
+			check3 = P_PointOnLineSide(tmx + tmthing->momx - cosradius, tmy + tmthing->momy - sinradius, &junk);
+			check4 = P_PointOnLineSide(tmx + tmthing->momx + cosradius, tmy + tmthing->momy + sinradius, &junk);
+			if ((check1 == check2) && (check2 == check3) && (check3 == check4))
 				return true; // the line doesn't cross between collider's start or end
 		}
 		else
@@ -1226,6 +1230,15 @@ static boolean PIT_CheckLine(line_t *ld)
 	if (P_BoxOnLineSide(tmbbox, ld) != -1)
 		return true;
 
+	if (tmthing->flags & MF_PAPER) // Caution! Turning whilst up against a wall will get you stuck. You probably shouldn't give the player this flag.
+	{
+		fixed_t cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
+		fixed_t sinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
+		if (P_PointOnLineSide(tmx - cosradius, tmy - sinradius, ld)
+		== P_PointOnLineSide(tmx + cosradius, tmy + sinradius, ld))
+			return true; // the line doesn't cross between collider's start or end
+	}
+
 	// A line has been hit
 
 	// The moving thing's destination position will cross

From ff362534b4592afa277c11e650a43600211ea929 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Thu, 18 Aug 2016 22:20:42 +0100
Subject: [PATCH 18/41] An experimental attempt at collision correction, put
 behind a #define because it's buggy as SHIT.

---
 src/doomdef.h |  3 +++
 src/p_map.c   | 21 +++++++++++++++++++--
 2 files changed, 22 insertions(+), 2 deletions(-)

diff --git a/src/doomdef.h b/src/doomdef.h
index 4428ef617..90c1233d6 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -498,4 +498,7 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
 /// \note	You should leave this enabled unless you're working with a future SRB2 version.
 #define MUSICSLOT_COMPATIBILITY
 
+/// Experimental attempts at preventing MF2_PAPER objects from getting stuck in walls.
+//#define PAPER_COLLISIONCORRECTION
+
 #endif // __DOOMDEF__
diff --git a/src/p_map.c b/src/p_map.c
index 1a79d8d5e..0488c7b8c 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -1232,11 +1232,28 @@ static boolean PIT_CheckLine(line_t *ld)
 
 	if (tmthing->flags & MF_PAPER) // Caution! Turning whilst up against a wall will get you stuck. You probably shouldn't give the player this flag.
 	{
-		fixed_t cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
-		fixed_t sinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
+		fixed_t cosradius, sinradius;
+		cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
+		sinradius = FixedMul(tmthing->radius, FINESINE(tmthing->angle>>ANGLETOFINESHIFT));
 		if (P_PointOnLineSide(tmx - cosradius, tmy - sinradius, ld)
 		== P_PointOnLineSide(tmx + cosradius, tmy + sinradius, ld))
 			return true; // the line doesn't cross between collider's start or end
+#ifdef PAPER_COLLISIONCORRECTION
+		{
+			fixed_t dist;
+			vertex_t result;
+			angle_t langle;
+			P_ClosestPointOnLine(tmx, tmy, ld, &result);
+			langle = R_PointToAngle2(ld->v1->x, ld->v1->y, ld->v2->x, ld->v2->y);
+			langle += ANGLE_90*(P_PointOnLineSide(tmx, tmy, ld) ? -1 : 1);
+			dist = abs(FixedMul(tmthing->radius, FINECOSINE((tmthing->angle - langle)>>ANGLETOFINESHIFT)));
+			cosradius = FixedMul(dist, FINECOSINE(langle>>ANGLETOFINESHIFT));
+			sinradius = FixedMul(dist, FINESINE(langle>>ANGLETOFINESHIFT));
+			tmthing->flags |= MF_NOCLIP;
+			P_TeleportMove(tmthing, result.x + cosradius - tmthing->momx, result.y + sinradius - tmthing->momy, tmthing->z);
+			tmthing->flags &= ~MF_NOCLIP;
+		}
+#endif
 	}
 
 	// A line has been hit

From 66a845a33a23b11fe2e96a59bc406e0ed1e6eb87 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Thu, 18 Aug 2016 22:24:36 +0100
Subject: [PATCH 19/41] silly me

---
 src/doomdef.h | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/doomdef.h b/src/doomdef.h
index 90c1233d6..7cf3cff04 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -498,7 +498,7 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
 /// \note	You should leave this enabled unless you're working with a future SRB2 version.
 #define MUSICSLOT_COMPATIBILITY
 
-/// Experimental attempts at preventing MF2_PAPER objects from getting stuck in walls.
+/// Experimental attempts at preventing MF_PAPER objects from getting stuck in walls.
 //#define PAPER_COLLISIONCORRECTION
 
 #endif // __DOOMDEF__

From fbff05bd17b8b636a93363f8c2ee5bf147645901 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Fri, 19 Aug 2016 12:26:26 +0100
Subject: [PATCH 20/41] Fixed the between-objects sorting problem previously
 mentioned in the merge request. Now they're sorted by whichever sprite has an
 end closest to the camera, instead of the middle point previously used.

http://i.imgur.com/UyOKX5u.png <-- this common glitch with crawlas given MF_PAPER (THEY'RE NOT GOOD AT TURNING NEAR EDGES) used to show the behind-crawlas in front of the front-crawlas.

Unfortunately, I've just discovered this issue (which happens with the old version of the sorting code too): http://i.imgur.com/QNjbATB.png but to be fair these crawlas have gotten stuck inside the edges of this platform, so I'm not sure I can do anything about this without cutting off Sonic's feet when he stands on the ground? shrug
---
 src/r_things.c | 6 ++++--
 1 file changed, 4 insertions(+), 2 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index 97c1b5d7b..970c59b3d 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1152,7 +1152,7 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	// aspect ratio stuff
 	xscale = FixedDiv(projection, tz);
-	sortscale = FixedDiv(projectiony, tz);
+	yscale = FixedDiv(projectiony, tz);
 
 	// decide which patch to use for sprite relative to player
 #ifdef RANGECHECK
@@ -1292,11 +1292,13 @@ static void R_ProjectSprite(mobj_t *thing)
 			range = 1;
 
 		scalestep = (yscale2 - yscale)/range;
+
+		sortscale = max(yscale, yscale2);
 	}
 	else
 	{
-		yscale = sortscale;
 		scalestep = 0;
+		sortscale = yscale;
 	}
 
 	xscale = FixedMul(xscale, ang_scale);

From 552a67200c51872baf981c59eafa9a41e6ebf115 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Fri, 19 Aug 2016 15:06:10 +0100
Subject: [PATCH 21/41] A revert of the sorting because it produced better (but
 not perfect) results for paper and normal mobj interaction.

Also, I added more sortscale handling in the places where I forgot it.

I probably need some help with the maths here to get this to work nicely. http://gfycat.com/LimpAgedDowitcher
---
 src/r_things.c | 13 +++++++------
 1 file changed, 7 insertions(+), 6 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index 970c59b3d..4c307ef4c 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1152,7 +1152,7 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	// aspect ratio stuff
 	xscale = FixedDiv(projection, tz);
-	yscale = FixedDiv(projectiony, tz);
+	sortscale = FixedDiv(projectiony, tz);
 
 	// decide which patch to use for sprite relative to player
 #ifdef RANGECHECK
@@ -1293,12 +1293,13 @@ static void R_ProjectSprite(mobj_t *thing)
 
 		scalestep = (yscale2 - yscale)/range;
 
-		sortscale = max(yscale, yscale2);
+		//sortscale = yscale + scalestep*((centerxfrac>>FRACBITS) - x1);
+		//sortscale = max(yscale, yscale2);
 	}
 	else
 	{
 		scalestep = 0;
-		sortscale = yscale;
+		yscale = sortscale;
 	}
 
 	xscale = FixedMul(xscale, ang_scale);
@@ -1412,8 +1413,8 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	vis->xscale = xscale; //SoM: 4/17/2000
 	vis->sector = thing->subsector->sector;
-	vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, yscale))>>FRACBITS);
-	vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, yscale))>>FRACBITS);
+	vis->szt = (INT16)((centeryfrac - FixedMul(vis->gzt - viewz, sortscale))>>FRACBITS);
+	vis->sz = (INT16)((centeryfrac - FixedMul(vis->gz - viewz, sortscale))>>FRACBITS);
 	vis->cut = SC_NONE;
 	if (thing->subsector->sector->numlights)
 		vis->extra_colormap = thing->subsector->sector->lightlist[light].extra_colormap;
@@ -1596,7 +1597,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
 
 	// store information in a vissprite
 	vis = R_NewVisSprite();
-	vis->scale = yscale; //<<detailshift;
+	vis->scale = vis->sortscale = yscale; //<<detailshift;
 	vis->dispoffset = 0; // Monster Iestyn: 23/11/15
 	vis->gx = thing->x;
 	vis->gy = thing->y;

From 8df146b713e7f44d9cc660eae0b0a1c2e05181f2 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Sat, 20 Aug 2016 01:03:35 +0100
Subject: [PATCH 22/41] An additional proper overflow check, and also a little
 something I forgot earlier.

---
 src/r_things.c | 8 ++++++++
 1 file changed, 8 insertions(+)

diff --git a/src/r_things.c b/src/r_things.c
index 4c307ef4c..698e24b0d 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -806,6 +806,13 @@ static void R_DrawVisSprite(vissprite_t *vis)
 	if (overflow_test < 0) overflow_test = -overflow_test;
 	if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) return; // fixed point mult would overflow
 
+	if (vis->scalestep) // handles right edge too
+	{
+		overflow_test = (INT64)centeryfrac - (((INT64)vis->texturemid*(vis->scale + (vis->scalestep*(vis->x2 - vis->x1))))>>FRACBITS);
+		if (overflow_test < 0) overflow_test = -overflow_test;
+		if ((UINT64)overflow_test&0xFFFFFFFF80000000ULL) return; // ditto
+	}
+
 	colfunc = basecolfunc; // hack: this isn't resetting properly somewhere.
 	dc_colormap = vis->colormap;
 	if ((vis->mobj->flags & MF_BOSS) && (vis->mobj->flags2 & MF2_FRET) && (leveltime & 1)) // Bosses "flash"
@@ -1607,6 +1614,7 @@ static void R_ProjectPrecipitationSprite(precipmobj_t *thing)
 	vis->pz = thing->z;
 	vis->pzt = vis->pz + vis->thingheight;
 	vis->texturemid = vis->gzt - viewz;
+	vis->scalestep = 0;
 
 	vis->x1 = x1 < 0 ? 0 : x1;
 	vis->x2 = x2 >= viewwidth ? viewwidth-1 : x2;

From 7d5bda709aa0fa93b2c31bb51345c9ac63ad8e66 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Sat, 20 Aug 2016 03:02:40 +0100
Subject: [PATCH 23/41] Sorting is fixed! Turns out rover isn't a FOF, it's
 another sprite. Thanks @RedEnchilada for noticing this oversight!!

http://gfycat.com/EsteemedPleasedDuck <-- so great
---
 src/r_things.c | 14 +++++++-------
 1 file changed, 7 insertions(+), 7 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index 698e24b0d..e1527d6af 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1962,7 +1962,7 @@ static void R_CreateDrawNodes(void)
 
 				for (i = x1; i <= x2; i++)
 				{
-					if (r2->seg->frontscale[i] > rover->scale)
+					if (r2->seg->frontscale[i] > rover->sortscale)
 						break;
 				}
 				if (i > x2)
@@ -1981,10 +1981,10 @@ static void R_CreateDrawNodes(void)
 					continue;
 
 				scale = r2->thickseg->scale1 > r2->thickseg->scale2 ? r2->thickseg->scale1 : r2->thickseg->scale2;
-				if (scale <= rover->scale)
+				if (scale <= rover->sortscale)
 					continue;
 				scale = r2->thickseg->scale1 + (r2->thickseg->scalestep * (sintersect - r2->thickseg->x1));
-				if (scale <= rover->scale)
+				if (scale <= rover->sortscale)
 					continue;
 
 #ifdef ESLOPE
@@ -2034,11 +2034,11 @@ static void R_CreateDrawNodes(void)
 					continue;
 
 				scale = r2->seg->scale1 > r2->seg->scale2 ? r2->seg->scale1 : r2->seg->scale2;
-				if (scale <= rover->scale)
+				if (scale <= rover->sortscale)
 					continue;
 				scale = r2->seg->scale1 + (r2->seg->scalestep * (sintersect - r2->seg->x1));
 
-				if (rover->scale < scale)
+				if (rover->sortscale < scale)
 				{
 					entry = R_CreateDrawNode(NULL);
 					(entry->prev = r2->prev)->next = entry;
@@ -2054,8 +2054,8 @@ static void R_CreateDrawNodes(void)
 				if (r2->sprite->szt > rover->sz || r2->sprite->sz < rover->szt)
 					continue;
 
-				if (r2->sprite->sortscale > rover->scale
-				 || (r2->sprite->sortscale == rover->scale && r2->sprite->dispoffset > rover->dispoffset))
+				if (r2->sprite->sortscale > rover->sortscale
+				 || (r2->sprite->sortscale == rover->sortscale && r2->sprite->dispoffset > rover->dispoffset))
 				{
 					entry = R_CreateDrawNode(NULL);
 					(entry->prev = r2->prev)->next = entry;

From ef6c2510a269daa61b89f6f742d5d42e17c385ae Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Sat, 20 Aug 2016 12:18:00 +0100
Subject: [PATCH 24/41] Did a few things to make this better.

* Reorganised R_DrawVisSprite to do less needless variable calculation.
* Corrected some clipping oversights.
* Made a few comments clearer.
---
 src/r_things.c | 37 +++++++++++++++++++------------------
 1 file changed, 19 insertions(+), 18 deletions(-)

diff --git a/src/r_things.c b/src/r_things.c
index e1527d6af..33b834ada 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -817,7 +817,7 @@ static void R_DrawVisSprite(vissprite_t *vis)
 	dc_colormap = vis->colormap;
 	if ((vis->mobj->flags & MF_BOSS) && (vis->mobj->flags2 & MF2_FRET) && (leveltime & 1)) // Bosses "flash"
 	{
-		// translate green skin to another color
+		// translate certain pixels to white
 		colfunc = transcolfunc;
 		if (vis->mobj->type == MT_CYBRAKDEMON)
 			dc_translation = R_GetTranslationColormap(TC_ALLWHITE, 0, GTC_CACHE);
@@ -873,13 +873,10 @@ static void R_DrawVisSprite(vissprite_t *vis)
 	if (!dc_colormap)
 		dc_colormap = colormaps;
 
-	dc_iscale = FixedDiv(FRACUNIT, vis->scale);
 	dc_texturemid = vis->texturemid;
 	dc_texheight = 0;
 
 	frac = vis->startfrac;
-	spryscale = vis->scale;
-	sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
 	windowtop = windowbottom = sprbotscreen = INT32_MAX;
 
 	if (vis->mobj->skin && ((skin_t *)vis->mobj->skin)->flags & SF_HIRES)
@@ -892,28 +889,28 @@ static void R_DrawVisSprite(vissprite_t *vis)
 		{
 			vis->scale = FixedMul(vis->scale, this_scale);
 			vis->scalestep = FixedMul(vis->scalestep, this_scale);
-			spryscale = vis->scale;
-			dc_iscale = FixedDiv(FRACUNIT, vis->scale);
 			vis->xiscale = FixedDiv(vis->xiscale,this_scale);
 			vis->isScaled = true;
 		}
 		dc_texturemid = FixedDiv(dc_texturemid,this_scale);
+	}
 
-		//Oh lordy, mercy me. Don't freak out if sprites go offscreen!
-		/*if (vis->xiscale > 0)
-			frac = FixedDiv(frac, this_scale);
-		else if (vis->x1 <= 0)
-			frac = (vis->x1 - vis->x2) * vis->xiscale;*/
+	spryscale = vis->scale;
 
+	if (!(vis->scalestep))
+	{
 		sprtopscreen = centeryfrac - FixedMul(dc_texturemid, spryscale);
-		//dc_hires = 1;
+		dc_iscale = FixedDiv(FRACUNIT, vis->scale);
 	}
 
 	x1 = vis->x1;
 	x2 = vis->x2;
 
 	if (vis->x1 < 0)
+	{
+		spryscale += vis->scalestep*(-vis->x1);
 		vis->x1 = 0;
+	}
 
 	if (vis->x2 >= vid.width)
 		vis->x2 = vid.width-1;
@@ -1146,7 +1143,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	tz = gxt-gyt;
 
 	// thing is behind view plane?
-	if (tz < FixedMul(MINZ, this_scale))
+	if (!(flatsprite) && (tz < FixedMul(MINZ, this_scale))) // flatsprite clipping is handled later
 		return;
 
 	gxt = -FixedMul(tr_x, viewsin);
@@ -1265,7 +1262,7 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	if (flatsprite)
 	{
-		fixed_t yscale2, cosmul, sinmul;
+		fixed_t yscale2, cosmul, sinmul, tz2;
 		INT32 range;
 
 		if (ang >= ANGLE_180)
@@ -1289,10 +1286,13 @@ static void R_ProjectSprite(mobj_t *thing)
 		tr_y += FixedMul(offset2, sinmul);
 		gxt = FixedMul(tr_x, viewcos);
 		gyt = -FixedMul(tr_y, viewsin);
-		tz = gxt-gyt;
-		yscale2 = FixedDiv(projectiony, tz);
+		tz2 = gxt-gyt;
+		yscale2 = FixedDiv(projectiony, tz2);
 		if (yscale2 < 64) return; // ditto
 
+		if (max(tz, tz2) < FixedMul(MINZ, this_scale)) // non-flatsprite clipping is handled earlier
+			return;
+
 		if (x2 > x1)
 			range = (x2 - x1);
 		else
@@ -1300,8 +1300,9 @@ static void R_ProjectSprite(mobj_t *thing)
 
 		scalestep = (yscale2 - yscale)/range;
 
-		//sortscale = yscale + scalestep*((centerxfrac>>FRACBITS) - x1);
-		//sortscale = max(yscale, yscale2);
+		// The following two are alternate sorting methods which might be more applicable in some circumstances. TODO - maybe enable via MF2?
+		// sortscale = max(yscale, yscale2);
+		// sortscale = min(yscale, yscale2);
 	}
 	else
 	{

From c08e9674be3e9cc504ebbb393761e873eb5ba68a Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Sat, 20 Aug 2016 15:15:48 +0100
Subject: [PATCH 25/41] As requested by Nev3r and VAda, seperating MF_PAPER
 into FF_PAPERSPRITE and MF_PAPERCOLLISION.

---
 src/dehacked.c |  1 +
 src/doomdef.h  |  2 +-
 src/p_map.c    | 10 +++++-----
 src/p_mobj.h   |  4 ++--
 src/p_pspr.h   |  4 +++-
 src/r_things.c | 12 ++++++------
 6 files changed, 18 insertions(+), 15 deletions(-)

diff --git a/src/dehacked.c b/src/dehacked.c
index 6b189a3ea..25c28d852 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -7045,6 +7045,7 @@ struct {
 
 	// Frame settings
 	{"FF_FRAMEMASK",FF_FRAMEMASK},
+	{"FF_PAPERSPRITE",FF_PAPERSPRITE},
 	{"FF_ANIMATE",FF_ANIMATE},
 	{"FF_FULLBRIGHT",FF_FULLBRIGHT},
 	{"FF_TRANSMASK",FF_TRANSMASK},
diff --git a/src/doomdef.h b/src/doomdef.h
index 7cf3cff04..7ca379d99 100644
--- a/src/doomdef.h
+++ b/src/doomdef.h
@@ -498,7 +498,7 @@ extern const char *compdate, *comptime, *comprevision, *compbranch;
 /// \note	You should leave this enabled unless you're working with a future SRB2 version.
 #define MUSICSLOT_COMPATIBILITY
 
-/// Experimental attempts at preventing MF_PAPER objects from getting stuck in walls.
+/// Experimental attempts at preventing MF_PAPERCOLLISION objects from getting stuck in walls.
 //#define PAPER_COLLISIONCORRECTION
 
 #endif // __DOOMDEF__
diff --git a/src/p_map.c b/src/p_map.c
index 0488c7b8c..5946f56d9 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -498,7 +498,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	if (abs(thing->x - tmx) >= blockdist || abs(thing->y - tmy) >= blockdist)
 		return true; // didn't hit it
 
-	if (thing->flags & MF_PAPER) // CAUTION! Very easy to get stuck inside MF_SOLID objects. Giving the player MF_PAPER is a bad idea unless you know what you're doing.
+	if (thing->flags & MF_PAPERCOLLISION) // CAUTION! Very easy to get stuck inside MF_SOLID objects. Giving the player MF_PAPERCOLLISION is a bad idea unless you know what you're doing.
 	{
 		fixed_t cosradius, sinradius;
 		vertex_t v1, v2; // fake vertexes
@@ -517,7 +517,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		junk.dx = v2.x - v1.x;
 		junk.dy = v2.y - v1.y;
 
-		if (tmthing->flags & MF_PAPER) // more strenuous checking to prevent clipping issues
+		if (tmthing->flags & MF_PAPERCOLLISION) // more strenuous checking to prevent clipping issues
 		{
 			INT32 check1, check2, check3, check4;
 			cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
@@ -538,7 +538,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 				return true; // the line doesn't cross between either pair of opposite corners
 		}
 	}
-	else if (tmthing->flags & MF_PAPER)
+	else if (tmthing->flags & MF_PAPERCOLLISION)
 	{
 		fixed_t cosradius, sinradius;
 		vertex_t v1, v2; // fake vertexes
@@ -557,7 +557,7 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		junk.dx = v2.x - v1.x;
 		junk.dy = v2.y - v1.y;
 
-		// no need to check whether thing has MF_PAPER, since checked above
+		// no need to check whether thing has MF_PAPERCOLLISION, since checked above
 
 		if ((P_PointOnLineSide(thing->x - thing->radius, thing->y - thing->radius, &junk)
 		== P_PointOnLineSide(thing->x + thing->radius, thing->y + thing->radius, &junk))
@@ -1230,7 +1230,7 @@ static boolean PIT_CheckLine(line_t *ld)
 	if (P_BoxOnLineSide(tmbbox, ld) != -1)
 		return true;
 
-	if (tmthing->flags & MF_PAPER) // Caution! Turning whilst up against a wall will get you stuck. You probably shouldn't give the player this flag.
+	if (tmthing->flags & MF_PAPERCOLLISION) // Caution! Turning whilst up against a wall will get you stuck. You probably shouldn't give the player this flag.
 	{
 		fixed_t cosradius, sinradius;
 		cosradius = FixedMul(tmthing->radius, FINECOSINE(tmthing->angle>>ANGLETOFINESHIFT));
diff --git a/src/p_mobj.h b/src/p_mobj.h
index c6738d372..50af8acc9 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -107,8 +107,8 @@ typedef enum
 	MF_NOSECTOR         = 1<<3,
 	// Don't use the blocklinks (inert but displayable)
 	MF_NOBLOCKMAP       = 1<<4,
-	// Paper-thin. Drawn like a midtexture, has a flat collision bound.
-	MF_PAPER            = 1<<5,
+	// Thin, paper-like collision bound (for visual equivalent, see FF_PAPERSPRITE)
+	MF_PAPERCOLLISION            = 1<<5,
 	// You can push this object. It can activate switches and things by pushing it on top.
 	MF_PUSHABLE         = 1<<6,
 	// Object is a boss.
diff --git a/src/p_pspr.h b/src/p_pspr.h
index 2fb232e73..c0064bc3e 100644
--- a/src/p_pspr.h
+++ b/src/p_pspr.h
@@ -36,7 +36,9 @@
 #endif
 
 /// \brief Frame flags: only the frame number
-#define FF_FRAMEMASK 0x3fff
+#define FF_FRAMEMASK 0x1ff
+/// \brief Frame flags: Thin, paper-like sprite (for collision equivalent, see MF_PAPERCOLLISION)
+#define FF_PAPERSPRITE 0x800
 /// \brief Frame flags: Simple stateless animation
 #define FF_ANIMATE 0x4000
 /// \brief Frame flags: frame always appears full bright
diff --git a/src/r_things.c b/src/r_things.c
index 33b834ada..763f0cf86 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1123,7 +1123,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	fixed_t iscale;
 	fixed_t scalestep; // toast '16
 	fixed_t offset, offset2;
-	boolean flatsprite = (thing->flags & MF_PAPER);
+	boolean papersprite = (thing->frame & FF_PAPERSPRITE);
 
 	//SoM: 3/17/2000
 	fixed_t gz, gzt;
@@ -1143,7 +1143,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	tz = gxt-gyt;
 
 	// thing is behind view plane?
-	if (!(flatsprite) && (tz < FixedMul(MINZ, this_scale))) // flatsprite clipping is handled later
+	if (!(papersprite) && (tz < FixedMul(MINZ, this_scale))) // papersprite clipping is handled later
 		return;
 
 	gxt = -FixedMul(tr_x, viewsin);
@@ -1203,10 +1203,10 @@ static void R_ProjectSprite(mobj_t *thing)
 		I_Error("R_ProjectSprite: sprframes NULL for sprite %d\n", thing->sprite);
 #endif
 
-	if (sprframe->rotate != SRF_SINGLE || flatsprite)
+	if (sprframe->rotate != SRF_SINGLE || papersprite)
 	{
 		ang = R_PointToAngle (thing->x, thing->y) - thing->angle;
-		if (flatsprite)
+		if (papersprite)
 			ang_scale = abs(FINESINE(ang>>ANGLETOFINESHIFT));
 	}
 
@@ -1260,7 +1260,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	if (x2 < 0)
 		return;
 
-	if (flatsprite)
+	if (papersprite)
 	{
 		fixed_t yscale2, cosmul, sinmul, tz2;
 		INT32 range;
@@ -1290,7 +1290,7 @@ static void R_ProjectSprite(mobj_t *thing)
 		yscale2 = FixedDiv(projectiony, tz2);
 		if (yscale2 < 64) return; // ditto
 
-		if (max(tz, tz2) < FixedMul(MINZ, this_scale)) // non-flatsprite clipping is handled earlier
+		if (max(tz, tz2) < FixedMul(MINZ, this_scale)) // non-papersprite clipping is handled earlier
 			return;
 
 		if (x2 > x1)

From 79297d0cd7d1dbc9210e1ce651e8956c7171a051 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Sat, 20 Aug 2016 17:34:59 +0100
Subject: [PATCH 26/41] Woops.

---
 src/dehacked.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/dehacked.c b/src/dehacked.c
index 25c28d852..ae3099c58 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -6691,7 +6691,7 @@ static const char *const MOBJFLAG_LIST[] = {
 	"SHOOTABLE",
 	"NOSECTOR",
 	"NOBLOCKMAP",
-	"PAPER",
+	"PAPERCOLLISION",
 	"PUSHABLE",
 	"BOSS",
 	"SPAWNCEILING",

From ead52294ab62a059c6b5da63e1fcaf747f1299f4 Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Sun, 21 Aug 2016 21:17:47 +0100
Subject: [PATCH 27/41] THE LINES LIVE AGAIN (initial horizon lines work
 commit)

---
 src/r_segs.c | 29 +++++++++++++++++------------
 1 file changed, 17 insertions(+), 12 deletions(-)

diff --git a/src/r_segs.c b/src/r_segs.c
index cb78743b6..6817ab8b8 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -2622,22 +2622,27 @@ void R_StoreWallRange(INT32 start, INT32 stop)
 	worldbottomslope >>= 4;
 #endif
 
-	topstep = -FixedMul (rw_scalestep, worldtop);
-	topfrac = (centeryfrac>>4) - FixedMul (worldtop, rw_scale);
+	if (linedef->special == 41) { // HORIZON LINES
+		topstep = bottomstep = 0;
+		topfrac = bottomfrac = (centeryfrac>>4);
+	} else {
+		topstep = -FixedMul (rw_scalestep, worldtop);
+		topfrac = (centeryfrac>>4) - FixedMul (worldtop, rw_scale);
 
-	bottomstep = -FixedMul (rw_scalestep,worldbottom);
-	bottomfrac = (centeryfrac>>4) - FixedMul (worldbottom, rw_scale);
+		bottomstep = -FixedMul (rw_scalestep,worldbottom);
+		bottomfrac = (centeryfrac>>4) - FixedMul (worldbottom, rw_scale);
 
 #ifdef ESLOPE
-	if (frontsector->c_slope) {
-		fixed_t topfracend = (centeryfrac>>4) - FixedMul (worldtopslope, ds_p->scale2);
-		topstep = (topfracend-topfrac)/(range);
-	}
-	if (frontsector->f_slope) {
-		fixed_t bottomfracend = (centeryfrac>>4) - FixedMul (worldbottomslope, ds_p->scale2);
-		bottomstep = (bottomfracend-bottomfrac)/(range);
-	}
+		if (frontsector->c_slope) {
+			fixed_t topfracend = (centeryfrac>>4) - FixedMul (worldtopslope, ds_p->scale2);
+			topstep = (topfracend-topfrac)/(range);
+		}
+		if (frontsector->f_slope) {
+			fixed_t bottomfracend = (centeryfrac>>4) - FixedMul (worldbottomslope, ds_p->scale2);
+			bottomstep = (bottomfracend-bottomfrac)/(range);
+		}
 #endif
+	}
 
 	dc_numlights = 0;
 

From 41b3b3c5bdfdc9a0b9f001160d18b3e63f4661e1 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Mon, 22 Aug 2016 23:42:06 +0100
Subject: [PATCH 28/41] GCC 4.6 and lower fix

---
 src/r_things.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/r_things.c b/src/r_things.c
index 763f0cf86..c5b47194c 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1119,7 +1119,7 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	vissprite_t *vis;
 
-	angle_t ang;
+	angle_t ang = 0;
 	fixed_t iscale;
 	fixed_t scalestep; // toast '16
 	fixed_t offset, offset2;

From 74e7433a92b6fa8d33ac8362c7f4f0fc148be423 Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Mon, 29 Aug 2016 22:42:06 +0100
Subject: [PATCH 29/41] Upper Unpegged on an FOF's control linedef now enables
 skewing of walls with respect to slopes

Skewing direction is decided per in-level wall by the Lower Unpegged flag on in-level linedefs themselves, since they already decide the stuff for FOF wall pegging as it is. That is unless Transfer Line is involved which moves everything to the control sector linedefs instead...
---
 src/r_segs.c | 73 +++++++++++++++++++++++++++++++++++++++++++++-------
 1 file changed, 64 insertions(+), 9 deletions(-)

diff --git a/src/r_segs.c b/src/r_segs.c
index cb78743b6..3e525bf33 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -728,6 +728,12 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 	// Render FOF sides kinda like normal sides, with the frac and step and everything
 	// NOTE: INT64 instead of fixed_t because overflow concerns
 	INT64         top_frac, top_step, bottom_frac, bottom_step;
+	// skew FOF walls with slopes?
+	boolean	      slopeskew = false;
+	fixed_t       ffloortextureslide = 0;
+	INT32         oldx = -1;
+	fixed_t       left_top, left_bottom; // needed here for slope skewing
+	pslope_t      *skewslope = NULL;
 #endif
 
 	void (*colfunc_2s) (column_t *);
@@ -951,21 +957,68 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 	mceilingclip = ds->sprtopclip;
 	dc_texheight = textureheight[texnum]>>FRACBITS;
 
+#ifdef ESLOPE
+	// calculate both left ends
+	if (*pfloor->t_slope)
+		left_top = P_GetZAt(*pfloor->t_slope, ds->leftpos.x, ds->leftpos.y) - viewz;
+	else
+		left_top = *pfloor->topheight - viewz;
+
+	if (*pfloor->b_slope)
+		left_bottom = P_GetZAt(*pfloor->b_slope, ds->leftpos.x, ds->leftpos.y) - viewz;
+	else
+		left_bottom = *pfloor->bottomheight - viewz;
+	dc_texturemid = left_top;
+	skewslope = *pfloor->t_slope; // skew using top slope by default
+#else
 	dc_texturemid = *pfloor->topheight - viewz;
+#endif
 
 	if (newline)
 	{
 		offsetvalue = sides[newline->sidenum[0]].rowoffset;
 		if (newline->flags & ML_DONTPEGBOTTOM)
+#ifdef ESLOPE
+		{
+			dc_texturemid = left_bottom;
+			skewslope = *pfloor->b_slope; // skew using bottom slope
+		}
+#else
 			offsetvalue -= *pfloor->topheight - *pfloor->bottomheight;
+#endif
+#ifdef ESLOPE
+		if (newline->flags & ML_DONTPEGTOP)
+			slopeskew = true;
+#endif
 	}
 	else
 	{
 		offsetvalue = sides[pfloor->master->sidenum[0]].rowoffset;
 		if (curline->linedef->flags & ML_DONTPEGBOTTOM)
+#ifdef ESLOPE
+		{
+			dc_texturemid = left_bottom;
+			skewslope = *pfloor->b_slope; // skew using bottom slope
+		}
+#else
 			offsetvalue -= *pfloor->topheight - *pfloor->bottomheight;
+#endif
+#ifdef ESLOPE
+		if (pfloor->master->flags & ML_DONTPEGTOP) // use control linedef's flags
+			slopeskew = true;
+#endif
 	}
 
+#ifdef ESLOPE
+	if (slopeskew)
+	{
+		angle_t lineangle = R_PointToAngle2(curline->v1->x, curline->v1->y, curline->v2->x, curline->v2->y);
+
+		if (skewslope)
+			ffloortextureslide = FixedMul(skewslope->zdelta, FINECOSINE((lineangle-skewslope->xydirection)>>ANGLETOFINESHIFT));
+	}
+#endif
+
 	dc_texturemid += offsetvalue;
 
 	//faB: handle case where multipatch texture is drawn on a 2sided wall, multi-patch textures
@@ -981,23 +1034,18 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 #ifdef ESLOPE
 	// Set heights according to plane, or slope, whichever
 	{
-		fixed_t left_top, right_top, left_bottom, right_bottom;
+		fixed_t right_top, right_bottom;
 
+		// calculate right ends now
 		if (*pfloor->t_slope)
-		{
-			left_top = P_GetZAt(*pfloor->t_slope, ds->leftpos.x, ds->leftpos.y) - viewz;
 			right_top = P_GetZAt(*pfloor->t_slope, ds->rightpos.x, ds->rightpos.y) - viewz;
-		}
 		else
-			left_top = right_top = *pfloor->topheight - viewz;
+			right_top = *pfloor->topheight - viewz;
 
 		if (*pfloor->b_slope)
-		{
-			left_bottom = P_GetZAt(*pfloor->b_slope, ds->leftpos.x, ds->leftpos.y) - viewz;
 			right_bottom = P_GetZAt(*pfloor->b_slope, ds->rightpos.x, ds->rightpos.y) - viewz;
-		}
 		else
-			left_bottom = right_bottom = *pfloor->bottomheight - viewz;
+			right_bottom = *pfloor->bottomheight - viewz;
 
 		// using INT64 to avoid 32bit overflow
 		top_frac =    (INT64)centeryfrac - (((INT64)left_top     * ds->scale1) >> FRACBITS);
@@ -1021,6 +1069,13 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 	{
 		if (maskedtexturecol[dc_x] != INT16_MAX)
 		{
+#ifdef ESLOPE
+			if (ffloortextureslide) { // skew FOF walls
+				if (oldx != -1)
+					dc_texturemid += FixedMul(ffloortextureslide, (maskedtexturecol[oldx]-maskedtexturecol[dc_x])<<FRACBITS);
+				oldx = dc_x;
+			}
+#endif
 			// SoM: New code does not rely on R_DrawColumnShadowed_8 which
 			// will (hopefully) put less strain on the stack.
 			if (dc_numlights)

From df7c8482e494bfab3864ecf0b7ee029a5c92685e Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Mon, 29 Aug 2016 23:21:57 +0100
Subject: [PATCH 30/41] If NOT skewing FOF walls, make sure dc_texturemid
 reverts to using unsloped FOF topheight/bottomheight rather than actual left
 side top/bottom heights

---
 src/r_segs.c | 42 +++++++++++++++++++++---------------------
 1 file changed, 21 insertions(+), 21 deletions(-)

diff --git a/src/r_segs.c b/src/r_segs.c
index 3e525bf33..148d0b8f7 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -968,45 +968,45 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 		left_bottom = P_GetZAt(*pfloor->b_slope, ds->leftpos.x, ds->leftpos.y) - viewz;
 	else
 		left_bottom = *pfloor->bottomheight - viewz;
-	dc_texturemid = left_top;
 	skewslope = *pfloor->t_slope; // skew using top slope by default
-#else
-	dc_texturemid = *pfloor->topheight - viewz;
+	if (newline && newline->flags & ML_DONTPEGTOP)
+		slopeskew = true;
+	else if (pfloor->master->flags & ML_DONTPEGTOP)
+		slopeskew = true;
+
+	if (slopeskew)
+		dc_texturemid = left_top;
+	else
 #endif
+	dc_texturemid = *pfloor->topheight - viewz;
 
 	if (newline)
 	{
 		offsetvalue = sides[newline->sidenum[0]].rowoffset;
 		if (newline->flags & ML_DONTPEGBOTTOM)
-#ifdef ESLOPE
 		{
-			dc_texturemid = left_bottom;
-			skewslope = *pfloor->b_slope; // skew using bottom slope
-		}
-#else
-			offsetvalue -= *pfloor->topheight - *pfloor->bottomheight;
-#endif
 #ifdef ESLOPE
-		if (newline->flags & ML_DONTPEGTOP)
-			slopeskew = true;
+			skewslope = *pfloor->b_slope; // skew using bottom slope
+			if (slopeskew)
+				dc_texturemid = left_bottom;
+			else
 #endif
+			offsetvalue -= *pfloor->topheight - *pfloor->bottomheight;
+		}
 	}
 	else
 	{
 		offsetvalue = sides[pfloor->master->sidenum[0]].rowoffset;
 		if (curline->linedef->flags & ML_DONTPEGBOTTOM)
-#ifdef ESLOPE
 		{
-			dc_texturemid = left_bottom;
-			skewslope = *pfloor->b_slope; // skew using bottom slope
-		}
-#else
-			offsetvalue -= *pfloor->topheight - *pfloor->bottomheight;
-#endif
 #ifdef ESLOPE
-		if (pfloor->master->flags & ML_DONTPEGTOP) // use control linedef's flags
-			slopeskew = true;
+			skewslope = *pfloor->b_slope; // skew using bottom slope
+			if (slopeskew)
+				dc_texturemid = left_bottom;
+			else
 #endif
+			offsetvalue -= *pfloor->topheight - *pfloor->bottomheight;
+		}
 	}
 
 #ifdef ESLOPE

From 5e6bf9e340c18b8289385714de193601e9708315 Mon Sep 17 00:00:00 2001
From: Monster Iestyn <iestynjealous@ntlworld.com>
Date: Mon, 29 Aug 2016 23:24:59 +0100
Subject: [PATCH 31/41] probably best if we did this instead actually

---
 src/r_segs.c | 7 +++++--
 1 file changed, 5 insertions(+), 2 deletions(-)

diff --git a/src/r_segs.c b/src/r_segs.c
index 148d0b8f7..463325812 100644
--- a/src/r_segs.c
+++ b/src/r_segs.c
@@ -969,8 +969,11 @@ void R_RenderThickSideRange(drawseg_t *ds, INT32 x1, INT32 x2, ffloor_t *pfloor)
 	else
 		left_bottom = *pfloor->bottomheight - viewz;
 	skewslope = *pfloor->t_slope; // skew using top slope by default
-	if (newline && newline->flags & ML_DONTPEGTOP)
-		slopeskew = true;
+	if (newline)
+	{
+		if (newline->flags & ML_DONTPEGTOP)
+			slopeskew = true;
+	}
 	else if (pfloor->master->flags & ML_DONTPEGTOP)
 		slopeskew = true;
 

From db6e7fd985c4d56ffafe070c207d2cd77763f7bb Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Fri, 16 Sep 2016 17:09:33 +0100
Subject: [PATCH 32/41] Introducing MF2_LINKDRAW! I put it in this branch
 because it (ab)uses some structural changes I made for papersprites.

* Sets the sortscale of the mobj to that of its tracer.
* Basically, Smiles' tails won't clip through shields thanks to this.
* http://gfycat.com/GraveGlassEwe
* Also has support for chains of MF2_LINKDRAW!
---
 src/dehacked.c |  1 +
 src/p_mobj.h   |  1 +
 src/r_things.c | 38 +++++++++++++++++++++++++++++++++++---
 src/r_things.h |  4 ++--
 4 files changed, 39 insertions(+), 5 deletions(-)

diff --git a/src/dehacked.c b/src/dehacked.c
index ae3099c58..b2290e2ff 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -6749,6 +6749,7 @@ static const char *const MOBJFLAG2_LIST[] = {
 	"BOSSFLEE",		// Boss is fleeing!
 	"BOSSDEAD",		// Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
 	"AMBUSH",       // Alternate behaviour typically set by MTF_AMBUSH
+	"LINKDRAW",
 	NULL
 };
 
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 50af8acc9..69e0e11aa 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -192,6 +192,7 @@ typedef enum
 	MF2_BOSSFLEE       = 1<<25, // Boss is fleeing!
 	MF2_BOSSDEAD       = 1<<26, // Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
 	MF2_AMBUSH         = 1<<27, // Alternate behaviour typically set by MTF_AMBUSH
+	MF2_LINKDRAW       = 1<<28, // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
 	// free: to and including 1<<31
 } mobjflag2_t;
 
diff --git a/src/r_things.c b/src/r_things.c
index 13d232d78..2360cc23f 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1119,12 +1119,14 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	vissprite_t *vis;
 
-	angle_t ang = 0;
+	angle_t ang = 0; // compiler complaints
 	fixed_t iscale;
-	fixed_t scalestep; // toast '16
+	fixed_t scalestep;
 	fixed_t offset, offset2;
 	boolean papersprite = (thing->frame & FF_PAPERSPRITE);
 
+	INT32 dispoffset = thing->info->dispoffset;
+
 	//SoM: 3/17/2000
 	fixed_t gz, gzt;
 	INT32 heightsec, phs;
@@ -1312,6 +1314,36 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	xscale = FixedMul(xscale, ang_scale);
 
+	if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer) // toast 16/09/16 (SYMMETRY)
+	{
+		mobj_t *link, *link2;
+		fixed_t linkscale;
+
+		for (link = thing->tracer; (link->tracer && (link->flags2 & MF2_LINKDRAW)); link = link->tracer)
+			link->flags2 &= ~MF2_LINKDRAW; // to prevent infinite loops, otherwise would just be a ;
+
+		for (link2 = thing->tracer; (link2->tracer && (link2 != link)); link2 = link2->tracer)
+			link->flags2 |= MF2_LINKDRAW; // only needed for correction of the above
+
+		if (link->flags2 & MF2_LINKDRAW)
+			link->flags2 &= ~MF2_LINKDRAW; // let's try and make sure this doesn't happen again...
+
+		tr_x = link->x - viewx;
+		tr_y = link->y - viewy;
+		gxt = FixedMul(tr_x, viewcos);
+		gyt = -FixedMul(tr_y, viewsin);
+		tz = gxt-gyt;
+		linkscale = FixedDiv(projectiony, tz);
+
+		if (tz < FixedMul(MINZ, this_scale))
+			return;
+
+		if (sortscale < linkscale)
+			dispoffset *= -1; // if it's physically behind, make sure it's ordered behind (if dispoffset > 0)
+
+		sortscale = linkscale; // now make sure it's linked 
+	}
+
 	// PORTAL SPRITE CLIPPING
 	if (portalrender)
 	{
@@ -1394,7 +1426,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	vis->mobjflags = thing->flags;
 	vis->scale = yscale; //<<detailshift;
 	vis->sortscale = sortscale;
-	vis->dispoffset = thing->info->dispoffset; // Monster Iestyn: 23/11/15
+	vis->dispoffset = dispoffset; // Monster Iestyn: 23/11/15
 	vis->gx = thing->x;
 	vis->gy = thing->y;
 	vis->gz = gz;
diff --git a/src/r_things.h b/src/r_things.h
index 360ead433..01cfc8903 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -135,8 +135,8 @@ typedef struct vissprite_s
 	fixed_t pz, pzt; // physical bottom/top for sorting with 3D floors
 
 	fixed_t startfrac; // horizontal position of x1
-	fixed_t scale, sortscale; // sortscale only differs from scale for flat sprites
-	fixed_t scalestep; // only for flat sprites, 0 otherwise
+	fixed_t scale, sortscale; // sortscale only differs from scale for paper sprites and MF2_LINKDRAW
+	fixed_t scalestep; // only for paper sprites, 0 otherwise
 	fixed_t xiscale; // negative if flipped
 
 	fixed_t texturemid;

From 54f463ce817e9225e3e07122cefa781c6e0cd3ac Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Fri, 16 Sep 2016 17:15:16 +0100
Subject: [PATCH 33/41] Forgot dehacked.c description!

---
 src/dehacked.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/dehacked.c b/src/dehacked.c
index b2290e2ff..33cd61fb5 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -6749,7 +6749,7 @@ static const char *const MOBJFLAG2_LIST[] = {
 	"BOSSFLEE",		// Boss is fleeing!
 	"BOSSDEAD",		// Boss is dead! (Not necessarily fleeing, if a fleeing point doesn't exist.)
 	"AMBUSH",       // Alternate behaviour typically set by MTF_AMBUSH
-	"LINKDRAW",
+	"LINKDRAW",     // Draw vissprite of mobj immediately before/after tracer's vissprite (dependent on dispoffset and position)
 	NULL
 };
 

From 28523760c37aea8ab0b080e8b8d14b52c82e3227 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Sat, 24 Sep 2016 14:23:00 +0100
Subject: [PATCH 34/41] FF_VERTICALFLIP, since I've been messing around with
 sprite stuff.

* flips the sprite ala MFE_VERTICALFLIP except you don't need to flip the direction of gravity for the object just to draw upside down
* stacks properly with reverse gravity
---
 src/dehacked.c         |  1 +
 src/hardware/hw_main.c |  9 ++++-----
 src/hardware/hw_md2.c  |  4 ++--
 src/p_pspr.h           |  2 ++
 src/r_things.c         | 10 ++++------
 5 files changed, 13 insertions(+), 13 deletions(-)

diff --git a/src/dehacked.c b/src/dehacked.c
index 33cd61fb5..81246c39e 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -7046,6 +7046,7 @@ struct {
 
 	// Frame settings
 	{"FF_FRAMEMASK",FF_FRAMEMASK},
+	{"FF_VERTICALFLIP",FF_VERTICALFLIP},
 	{"FF_PAPERSPRITE",FF_PAPERSPRITE},
 	{"FF_ANIMATE",FF_ANIMATE},
 	{"FF_FULLBRIGHT",FF_FULLBRIGHT},
diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index e69a74558..95968777f 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -5040,6 +5040,8 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	size_t lumpoff;
 	unsigned rot;
 	UINT8 flip;
+	boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP));
+
 	angle_t ang;
 	INT32 heightsec, phs;
 
@@ -5139,7 +5141,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	tx += FIXED_TO_FLOAT(spritecachedinfo[lumpoff].width) * this_scale;
 	x2 = gr_windowcenterx + (tx * gr_centerx / tz);
 
-	if (thing->eflags & MFE_VERTICALFLIP)
+	if (vflip)
 	{
 		gz = FIXED_TO_FLOAT(thing->z+thing->height) - FIXED_TO_FLOAT(spritecachedinfo[lumpoff].topoffset) * this_scale;
 		gzt = gz + FIXED_TO_FLOAT(spritecachedinfo[lumpoff].height) * this_scale;
@@ -5216,10 +5218,7 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	//CONS_Debug(DBG_RENDER, "------------------\nH: sprite  : %d\nH: frame   : %x\nH: type    : %d\nH: sname   : %s\n\n",
 	//            thing->sprite, thing->frame, thing->type, sprnames[thing->sprite]);
 
-	if (thing->eflags & MFE_VERTICALFLIP)
-		vis->vflip = true;
-	else
-		vis->vflip = false;
+	vis->vflip = vflip;
 
 	vis->precip = false;
 }
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 6628d1317..7f2864dbc 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1230,7 +1230,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
 		UINT32 durs = spr->mobj->state->tics;
 		UINT32 tics = spr->mobj->tics;
 		md2_frame_t *curr, *next = NULL;
-		const UINT8 flip = (UINT8)((spr->mobj->eflags & MFE_VERTICALFLIP) == MFE_VERTICALFLIP);
+		const UINT8 flip = (UINT8)(!(spr->mobj->eflags & MFE_VERTICALFLIP) != !(spr->mobj->frame & FF_VERTICALFLIP));
 		spritedef_t *sprdef;
 		spriteframe_t *sprframe;
 		float finalscale;
@@ -1345,7 +1345,7 @@ void HWR_DrawMD2(gr_vissprite_t *spr)
 		p.x = FIXED_TO_FLOAT(spr->mobj->x);
 		p.y = FIXED_TO_FLOAT(spr->mobj->y)+md2->offset;
 
-		if (spr->mobj->eflags & MFE_VERTICALFLIP)
+		if (flip)
 			p.z = FIXED_TO_FLOAT(spr->mobj->z + spr->mobj->height);
 		else
 			p.z = FIXED_TO_FLOAT(spr->mobj->z);
diff --git a/src/p_pspr.h b/src/p_pspr.h
index c0064bc3e..82d43f281 100644
--- a/src/p_pspr.h
+++ b/src/p_pspr.h
@@ -37,6 +37,8 @@
 
 /// \brief Frame flags: only the frame number
 #define FF_FRAMEMASK 0x1ff
+/// \brief Frame flags: Flip sprite vertically (relative to what it should be for its gravity)
+#define FF_VERTICALFLIP 0x400
 /// \brief Frame flags: Thin, paper-like sprite (for collision equivalent, see MF_PAPERCOLLISION)
 #define FF_PAPERSPRITE 0x800
 /// \brief Frame flags: Simple stateless animation
diff --git a/src/r_things.c b/src/r_things.c
index 2360cc23f..8a9c43e2c 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1114,6 +1114,7 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	size_t rot;
 	UINT8 flip;
+	boolean vflip = (!(thing->eflags & MFE_VERTICALFLIP) != !(thing->frame & FF_VERTICALFLIP));
 
 	INT32 lindex;
 
@@ -1123,7 +1124,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	fixed_t iscale;
 	fixed_t scalestep;
 	fixed_t offset, offset2;
-	boolean papersprite = (thing->frame & FF_PAPERSPRITE);
+	boolean papersprite = !!(thing->frame & FF_PAPERSPRITE);
 
 	INT32 dispoffset = thing->info->dispoffset;
 
@@ -1355,7 +1356,7 @@ static void R_ProjectSprite(mobj_t *thing)
 	}
 
 	//SoM: 3/17/2000: Disregard sprites that are out of view..
-	if (thing->eflags & MFE_VERTICALFLIP)
+	if (vflip)
 	{
 		// When vertical flipped, draw sprites from the top down, at least as far as offsets are concerned.
 		// sprite height - sprite topoffset is the proper inverse of the vertical offset, of course.
@@ -1516,10 +1517,7 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	vis->precip = false;
 
-	if (thing->eflags & MFE_VERTICALFLIP)
-		vis->vflip = true;
-	else
-		vis->vflip = false;
+	vis->vflip = vflip;
 
 	vis->isScaled = false;
 

From 99f60544db088c2a5cf5561b74e8ab509ec823d9 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Wed, 28 Sep 2016 16:26:29 +0100
Subject: [PATCH 35/41] Fixed various issues arising from collision with
 exclusively horizontal springs. Of note:

* If you hold down your jump button whilst jumping into it, you no longer immediately use your ability.
* Characters with CA_DOUBLEJUMP and CA2_MULTIABILITY no longer lose track of their jump count.
---
 src/p_map.c | 22 ++++++++++++++--------
 1 file changed, 14 insertions(+), 8 deletions(-)

diff --git a/src/p_map.c b/src/p_map.c
index 765f88a7d..e6d37fa98 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -115,6 +115,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 	fixed_t offx, offy;
 	fixed_t vertispeed = spring->info->mass;
 	fixed_t horizspeed = spring->info->damage;
+	UINT8 jumping, secondjump;
 
 	if (object->eflags & MFE_SPRUNG) // Object was already sprung this tic
 		return false;
@@ -203,25 +204,30 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 		}
 
 		pflags = object->player->pflags & (PF_JUMPED|PF_SPINNING|PF_THOKKED); // I still need these.
+		jumping = object->player->jumping;
+		secondjump = object->player->secondjump;
 		P_ResetPlayer(object->player);
 
-		if (P_MobjFlip(object)*vertispeed > 0)
+		if (spring->info->painchance)
+		{
+			object->player->pflags |= PF_JUMPED;
+			P_SetPlayerMobjState(object, S_PLAY_JUMP);
+		}
+		else if (P_MobjFlip(object)*vertispeed > 0)
 			P_SetPlayerMobjState(object, S_PLAY_SPRING);
 		else if (P_MobjFlip(object)*vertispeed < 0)
 			P_SetPlayerMobjState(object, S_PLAY_FALL);
 		else // horizontal spring
 		{
 			if (pflags & (PF_JUMPED|PF_SPINNING) && (object->player->panim == PA_ROLL || object->player->panim == PA_JUMP || object->player->panim == PA_FALL))
-				object->player->pflags = pflags;
+			{
+				object->player->pflags |= pflags;
+				object->player->jumping = jumping;
+				object->player->secondjump = secondjump;
+			}
 			else
 				P_SetPlayerMobjState(object, S_PLAY_WALK);
 		}
-
-		if (spring->info->painchance)
-		{
-			object->player->pflags |= PF_JUMPED;
-			P_SetPlayerMobjState(object, S_PLAY_JUMP);
-		}
 	}
 	return true;
 }

From 8445a1068e94475037829843a5297251618e4b19 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Wed, 28 Sep 2016 16:31:14 +0100
Subject: [PATCH 36/41] attempt

---
 src/p_map.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_map.c b/src/p_map.c
index e6d37fa98..4214f32e9 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -216,7 +216,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 		else if (P_MobjFlip(object)*vertispeed > 0)
 			P_SetPlayerMobjState(object, S_PLAY_SPRING);
 		else if (P_MobjFlip(object)*vertispeed < 0)
-			P_SetPlayerMobjState(object, S_PLAY_FALL);
+			P_SetPlayerMobjState(object, S_PLAY_FALL)
 		else // horizontal spring
 		{
 			if (pflags & (PF_JUMPED|PF_SPINNING) && (object->player->panim == PA_ROLL || object->player->panim == PA_JUMP || object->player->panim == PA_FALL))

From afb22b968be2ed311aed7f7e99a5a09a6cfb22ab Mon Sep 17 00:00:00 2001
From: Inuyasha <MattWCSTRFAN@gmail.com>
Date: Fri, 30 Sep 2016 03:20:57 -0700
Subject: [PATCH 37/41] FF_GLOBALANIM for synced animations and a few other
 frame flag changes

---
 src/dehacked.c |  4 ++-
 src/info.c     | 68 +++++++++++++++++++++++++-------------------------
 src/p_mobj.c   | 49 ++++++++++++++++++++++++++++--------
 src/p_pspr.h   | 22 ++++++++++------
 4 files changed, 90 insertions(+), 53 deletions(-)

diff --git a/src/dehacked.c b/src/dehacked.c
index 43391cde3..2bf4012bd 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -6974,8 +6974,10 @@ struct {
 	// Frame settings
 	{"FF_FRAMEMASK",FF_FRAMEMASK},
 	{"FF_SPR2ENDSTATE",FF_SPR2ENDSTATE},
-	{"FF_MIDDLESTARTCHANCE",FF_MIDDLESTARTCHANCE},
+	{"FF_SPR2MIDSTART",FF_SPR2MIDSTART},
 	{"FF_ANIMATE",FF_ANIMATE},
+	{"FF_RANDOMANIM",FF_RANDOMANIM},
+	{"FF_GLOBALANIM",FF_GLOBALANIM},
 	{"FF_FULLBRIGHT",FF_FULLBRIGHT},
 	{"FF_TRANSMASK",FF_TRANSMASK},
 	{"FF_TRANSSHIFT",FF_TRANSSHIFT},
diff --git a/src/info.c b/src/info.c
index 884217611..c73a5798f 100644
--- a/src/info.c
+++ b/src/info.c
@@ -474,8 +474,8 @@ enum playersprite free_spr2 = SPR2_FIRSTFREESLOT;
 state_t states[NUMSTATES] =
 {
 	// frame is masked through FF_FRAMEMASK
-	// FF_ANIMATE (0x4000) makes simple state animations (var1 #frames, var2 tic delay)
-	// FF_FULLBRIGHT (0x8000) activates the fullbright colormap
+	// FF_ANIMATE makes simple state animations (var1 #frames, var2 tic delay)
+	// FF_FULLBRIGHT activates the fullbright colormap
 	// use FF_TRANS10 - FF_TRANS90 for easy translucency
 	// (or tr_trans10<<FF_TRANSSHIFT if you want to make it hard on yourself)
 
@@ -1392,7 +1392,7 @@ state_t states[NUMSTATES] =
 	{SPR_MSCF, FF_FULLBRIGHT|FF_TRANS30|11, 1, {NULL}, 0, 0, S_MSSHIELD_F1},  // S_MSSHIELD_F12
 
 	// Ring
-	{SPR_RING, FF_ANIMATE, -1, {NULL}, 23, 1, S_RING}, // S_RING
+	{SPR_RING, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 23, 1, S_RING}, // S_RING
 
 	// Blue Sphere Replacement for special stages
 	{SPR_BBAL, 0, -1, {NULL}, 0, 0, S_NULL}, // S_BLUEBALL
@@ -1408,7 +1408,7 @@ state_t states[NUMSTATES] =
 	{SPR_GWLR, 2, 1, {NULL}, 0, 0, S_GRAVWELLRED},    // S_GRAVWELLRED3
 
 	// Individual Team Rings (now with shield attracting action! =P)
-	{SPR_TRNG, FF_ANIMATE, -1, {NULL}, 23, 1, S_TEAMRING},  // S_TEAMRING1
+	{SPR_TRNG, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 23, 1, S_TEAMRING},  // S_TEAMRING1
 
 	// Special Stage Token
 	{SPR_EMMY, FF_ANIMATE|FF_FULLBRIGHT, -1, {NULL}, 6, 2, S_EMMY}, // S_EMMY
@@ -2294,7 +2294,7 @@ state_t states[NUMSTATES] =
 	// Extra Large Bubble goes POP!
 	{SPR_BUBL, 5, 16, {NULL}, 0, 0, S_NULL}, // S_POP1
 
-	{SPR_WZAP, FF_TRANS10|FF_ANIMATE|FF_MIDDLESTARTCHANCE, 4, {NULL}, 3, 2, S_NULL},  // S_WATERZAP
+	{SPR_WZAP, FF_TRANS10|FF_ANIMATE|FF_RANDOMANIM, 4, {NULL}, 3, 2, S_NULL},  // S_WATERZAP
 
 	{SPR_TFOG, FF_FULLBRIGHT|FF_TRANS50,    2, {NULL}, 0, 0, S_FOG2},  // S_FOG1
 	{SPR_TFOG, FF_FULLBRIGHT|FF_TRANS50|1,  2, {NULL}, 0, 0, S_FOG3},  // S_FOG2
@@ -2363,16 +2363,16 @@ state_t states[NUMSTATES] =
 	{SPR_RRNG, FF_FULLBRIGHT|6, 1, {A_ThrownRing}, 0, 0, S_RRNG1}, // S_RRNG7
 
 	// Weapon Ring Ammo
-	{SPR_RNGB, FF_ANIMATE, -1, {NULL}, 34, 1, S_BOUNCERINGAMMO},    // S_BOUNCERINGAMMO
-	{SPR_RNGR, FF_ANIMATE, -1, {NULL}, 34, 1, S_RAILRINGAMMO},      // S_RAILRINGAMMO
-	{SPR_RNGI, FF_ANIMATE, -1, {NULL}, 34, 1, S_INFINITYRINGAMMO},  // S_INFINITYRINGAMMO
-	{SPR_RNGA, FF_ANIMATE, -1, {NULL}, 34, 1, S_AUTOMATICRINGAMMO}, // S_AUTOMATICRINGAMMO
-	{SPR_RNGE, FF_ANIMATE, -1, {NULL}, 34, 1, S_EXPLOSIONRINGAMMO}, // S_EXPLOSIONRINGAMMO
-	{SPR_RNGS, FF_ANIMATE, -1, {NULL}, 34, 1, S_SCATTERRINGAMMO},   // S_SCATTERRINGAMMO
-	{SPR_RNGG, FF_ANIMATE, -1, {NULL}, 34, 1, S_GRENADERINGAMMO},   // S_GRENADERINGAMMO
+	{SPR_RNGB, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 34, 1, S_BOUNCERINGAMMO},    // S_BOUNCERINGAMMO
+	{SPR_RNGR, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 34, 1, S_RAILRINGAMMO},      // S_RAILRINGAMMO
+	{SPR_RNGI, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 34, 1, S_INFINITYRINGAMMO},  // S_INFINITYRINGAMMO
+	{SPR_RNGA, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 34, 1, S_AUTOMATICRINGAMMO}, // S_AUTOMATICRINGAMMO
+	{SPR_RNGE, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 34, 1, S_EXPLOSIONRINGAMMO}, // S_EXPLOSIONRINGAMMO
+	{SPR_RNGS, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 34, 1, S_SCATTERRINGAMMO},   // S_SCATTERRINGAMMO
+	{SPR_RNGG, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 34, 1, S_GRENADERINGAMMO},   // S_GRENADERINGAMMO
 
 	// Bounce Ring Pickup
-	{SPR_PIKB, FF_ANIMATE, -1, {NULL}, 15, 1, S_BOUNCEPICKUP},  // S_BOUNCEPICKUP
+	{SPR_PIKB, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 15, 1, S_BOUNCEPICKUP},  // S_BOUNCEPICKUP
 
 	{SPR_PIKB,  0, 1, {NULL}, 0, 0, S_BOUNCEPICKUPFADE2}, // S_BOUNCEPICKUPFADE1
 	{SPR_PIKB,  2, 1, {NULL}, 0, 0, S_BOUNCEPICKUPFADE3}, // S_BOUNCEPICKUPFADE2
@@ -2384,7 +2384,7 @@ state_t states[NUMSTATES] =
 	{SPR_PIKB, 14, 1, {NULL}, 0, 0, S_BOUNCEPICKUPFADE1}, // S_BOUNCEPICKUPFADE8
 
 	// Rail Ring Pickup
-	{SPR_PIKR, FF_ANIMATE, -1, {NULL}, 15, 1, S_RAILPICKUP},  // S_RAILPICKUP
+	{SPR_PIKR, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 15, 1, S_RAILPICKUP},  // S_RAILPICKUP
 
 	{SPR_PIKR,  0, 1, {NULL}, 0, 0, S_RAILPICKUPFADE2}, // S_RAILPICKUPFADE1
 	{SPR_PIKR,  2, 1, {NULL}, 0, 0, S_RAILPICKUPFADE3}, // S_RAILPICKUPFADE2
@@ -2396,7 +2396,7 @@ state_t states[NUMSTATES] =
 	{SPR_PIKR, 14, 1, {NULL}, 0, 0, S_RAILPICKUPFADE1}, // S_RAILPICKUPFADE8
 
 	// Auto Ring Pickup
-	{SPR_PIKA, FF_ANIMATE, -1, {NULL}, 15, 1, S_AUTOPICKUP},  // S_AUTOPICKUP
+	{SPR_PIKA, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 15, 1, S_AUTOPICKUP},  // S_AUTOPICKUP
 
 	{SPR_PIKA,  0, 1, {NULL}, 0, 0, S_AUTOPICKUPFADE2}, // S_AUTOPICKUPFADE1
 	{SPR_PIKA,  2, 1, {NULL}, 0, 0, S_AUTOPICKUPFADE3}, // S_AUTOPICKUPFADE2
@@ -2408,7 +2408,7 @@ state_t states[NUMSTATES] =
 	{SPR_PIKA, 14, 1, {NULL}, 0, 0, S_AUTOPICKUPFADE1}, // S_AUTOPICKUPFADE8
 
 	// Explode Ring Pickup
-	{SPR_PIKE, FF_ANIMATE, -1, {NULL}, 15, 1, S_EXPLODEPICKUP},  // S_EXPLODEPICKUP
+	{SPR_PIKE, FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 15, 1, S_EXPLODEPICKUP},  // S_EXPLODEPICKUP
 
 	{SPR_PIKE,  0, 1, {NULL}, 0, 0, S_EXPLODEPICKUPFADE2}, // S_EXPLODEPICKUPFADE1
 	{SPR_PIKE,  2, 1, {NULL}, 0, 0, S_EXPLODEPICKUPFADE3}, // S_EXPLODEPICKUPFADE2
@@ -2420,7 +2420,7 @@ state_t states[NUMSTATES] =
 	{SPR_PIKE, 14, 1, {NULL}, 0, 0, S_EXPLODEPICKUPFADE1}, // S_EXPLODEPICKUPFADE8
 
 	// Scatter Ring Pickup
-	{SPR_PIKS,  FF_ANIMATE, -1, {NULL}, 15, 1, S_SCATTERPICKUP},  // S_SCATTERPICKUP
+	{SPR_PIKS,  FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 15, 1, S_SCATTERPICKUP},  // S_SCATTERPICKUP
 
 	{SPR_PIKS,  0, 1, {NULL}, 0, 0, S_SCATTERPICKUPFADE2}, // S_SCATTERPICKUPFADE1
 	{SPR_PIKS,  2, 1, {NULL}, 0, 0, S_SCATTERPICKUPFADE3}, // S_SCATTERPICKUPFADE2
@@ -2432,7 +2432,7 @@ state_t states[NUMSTATES] =
 	{SPR_PIKS, 14, 1, {NULL}, 0, 0, S_SCATTERPICKUPFADE1}, // S_SCATTERPICKUPFADE8
 
 	// Grenade Ring Pickup
-	{SPR_PIKG,  FF_ANIMATE, -1, {NULL}, 15, 1, S_GRENADEPICKUP},  // S_GRENADEPICKUP
+	{SPR_PIKG,  FF_ANIMATE|FF_GLOBALANIM, -1, {NULL}, 15, 1, S_GRENADEPICKUP},  // S_GRENADEPICKUP
 
 	{SPR_PIKG,  0, 1, {NULL}, 0, 0, S_GRENADEPICKUPFADE2}, // S_GRENADEPICKUPFADE1
 	{SPR_PIKG,  2, 1, {NULL}, 0, 0, S_GRENADEPICKUPFADE3}, // S_GRENADEPICKUPFADE2
@@ -2753,22 +2753,22 @@ state_t states[NUMSTATES] =
 
 	{SPR_NULL, 0, 1, {A_RockSpawn}, 0, 0, S_ROCKSPAWN}, // S_ROCKSPAWN
 
-	{SPR_ROIA, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEA}, // S_ROCKCRUMBLEA
-	{SPR_ROIB, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEB}, // S_ROCKCRUMBLEB
-	{SPR_ROIC, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEC}, // S_ROCKCRUMBLEC
-	{SPR_ROID, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLED}, // S_ROCKCRUMBLED
-	{SPR_ROIE, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEE}, // S_ROCKCRUMBLEE
-	{SPR_ROIF, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEF}, // S_ROCKCRUMBLEF
-	{SPR_ROIG, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEG}, // S_ROCKCRUMBLEG
-	{SPR_ROIH, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEH}, // S_ROCKCRUMBLEH
-	{SPR_ROII, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEI}, // S_ROCKCRUMBLEI
-	{SPR_ROIJ, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEJ}, // S_ROCKCRUMBLEJ
-	{SPR_ROIK, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEK}, // S_ROCKCRUMBLEK
-	{SPR_ROIL, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEL}, // S_ROCKCRUMBLEL
-	{SPR_ROIM, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEM}, // S_ROCKCRUMBLEM
-	{SPR_ROIN, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEN}, // S_ROCKCRUMBLEN
-	{SPR_ROIO, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEO}, // S_ROCKCRUMBLEO
-	{SPR_ROIP, FF_ANIMATE, -1, {NULL}, 4, 2, S_ROCKCRUMBLEP}, // S_ROCKCRUMBLEP
+	{SPR_ROIA, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEA}, // S_ROCKCRUMBLEA
+	{SPR_ROIB, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEB}, // S_ROCKCRUMBLEB
+	{SPR_ROIC, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEC}, // S_ROCKCRUMBLEC
+	{SPR_ROID, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLED}, // S_ROCKCRUMBLED
+	{SPR_ROIE, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEE}, // S_ROCKCRUMBLEE
+	{SPR_ROIF, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEF}, // S_ROCKCRUMBLEF
+	{SPR_ROIG, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEG}, // S_ROCKCRUMBLEG
+	{SPR_ROIH, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEH}, // S_ROCKCRUMBLEH
+	{SPR_ROII, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEI}, // S_ROCKCRUMBLEI
+	{SPR_ROIJ, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEJ}, // S_ROCKCRUMBLEJ
+	{SPR_ROIK, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEK}, // S_ROCKCRUMBLEK
+	{SPR_ROIL, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEL}, // S_ROCKCRUMBLEL
+	{SPR_ROIM, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEM}, // S_ROCKCRUMBLEM
+	{SPR_ROIN, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEN}, // S_ROCKCRUMBLEN
+	{SPR_ROIO, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEO}, // S_ROCKCRUMBLEO
+	{SPR_ROIP, FF_ANIMATE|FF_RANDOMANIM, -1, {NULL}, 4, 2, S_ROCKCRUMBLEP}, // S_ROCKCRUMBLEP
 
 	{SPR_SRBA, 0, 5, {A_Look}, 0, 0, S_SRB1_CRAWLA1}, // S_SRB1_CRAWLA1
 	{SPR_SRBA, 0, 3, {A_Chase}, 0, 0, S_SRB1_CRAWLA3}, // S_SRB1_CRAWLA2
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 6f655c15a..f54c48105 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -81,6 +81,36 @@ void P_AddCachedAction(mobj_t *mobj, INT32 statenum)
 	actioncachehead.prev = newaction;
 }
 
+//
+// P_SetupStateAnimation
+//
+FUNCINLINE static ATTRINLINE void P_SetupStateAnimation(mobj_t *mobj, state_t *st)
+{
+	if (!(st->frame & FF_ANIMATE))
+		return;
+	if (st->var1 == 0 || st->var2 == 0)
+	{
+		mobj->frame &= ~FF_ANIMATE;
+		return; // Crash/stupidity prevention
+	}
+
+	mobj->anim_duration = (UINT16)st->var2;
+
+	if (st->frame & FF_GLOBALANIM)
+	{
+		// Attempt to account for the pre-ticker for objects spawned on load
+		if (!leveltime) return;
+
+		mobj->anim_duration -= (leveltime + 2) % st->var2;            // Duration synced to timer
+		mobj->frame += ((leveltime + 2) / st->var2) % (st->var1 + 1); // Frame synced to timer (duration taken into account)
+	}
+	else if (st->frame & FF_RANDOMANIM)
+	{
+		mobj->frame += P_RandomKey(st->var1 + 1);     // Random starting frame
+		mobj->anim_duration -= P_RandomKey(st->var2); // Random duration for first frame
+	}
+}
+
 //
 // P_CycleStateAnimation
 //
@@ -588,7 +618,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 			}
 			else if (mobj->sprite2 != spr2)
 			{
-				if ((st->frame & FF_MIDDLESTARTCHANCE) && numframes && P_RandomChance(FRACUNIT/2))
+				if ((st->frame & FF_SPR2MIDSTART) && numframes && P_RandomChance(FRACUNIT/2))
 					frame = numframes/2;
 				else
 					frame = 0;
@@ -621,8 +651,7 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 		{
 			mobj->sprite = st->sprite;
 			mobj->frame = st->frame;
-			if ((st->frame & (FF_ANIMATE|FF_MIDDLESTARTCHANCE)) == (FF_ANIMATE|FF_MIDDLESTARTCHANCE))
-				mobj->frame += P_RandomKey(st->var1+1);
+			P_SetupStateAnimation(mobj, st);
 		}
 
 		// Modified handling.
@@ -689,7 +718,6 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
 		st = &states[state];
 		mobj->state = st;
 		mobj->tics = st->tics;
-		mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
 
 		// Player animations
 		if (st->sprite == SPR_PLAY)
@@ -714,7 +742,7 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
 			}
 			else if (mobj->sprite2 != spr2)
 			{
-				if ((st->frame & FF_MIDDLESTARTCHANCE) && numframes && P_RandomChance(FRACUNIT/2))
+				if ((st->frame & FF_SPR2MIDSTART) && numframes && P_RandomChance(FRACUNIT/2))
 					frame = numframes/2;
 				else
 					frame = 0;
@@ -736,8 +764,7 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
 		{
 			mobj->sprite = st->sprite;
 			mobj->frame = st->frame;
-			if ((st->frame & (FF_ANIMATE|FF_MIDDLESTARTCHANCE)) == (FF_ANIMATE|FF_MIDDLESTARTCHANCE))
-				mobj->frame += P_RandomKey(st->var1+1);
+			P_SetupStateAnimation(mobj, st);
 		}
 
 		// Modified handling.
@@ -792,7 +819,7 @@ boolean P_SetMobjStateNF(mobj_t *mobj, statenum_t state)
 	mobj->tics = st->tics;
 	mobj->sprite = st->sprite;
 	mobj->frame = st->frame;
-	mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
+	P_SetupStateAnimation(mobj, st);
 
 	return true;
 }
@@ -811,7 +838,7 @@ static boolean P_SetPrecipMobjState(precipmobj_t *mobj, statenum_t state)
 	mobj->tics = st->tics;
 	mobj->sprite = st->sprite;
 	mobj->frame = st->frame;
-	mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
+	P_SetupStateAnimation((mobj_t*)mobj, st);
 
 	return true;
 }
@@ -8009,7 +8036,7 @@ mobj_t *P_SpawnMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype_t type)
 	mobj->tics = st->tics;
 	mobj->sprite = st->sprite;
 	mobj->frame = st->frame; // FF_FRAMEMASK for frame, and other bits..
-	mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
+	P_SetupStateAnimation(mobj, st);
 
 	mobj->friction = ORIG_FRICTION;
 
@@ -8235,7 +8262,7 @@ static precipmobj_t *P_SpawnPrecipMobj(fixed_t x, fixed_t y, fixed_t z, mobjtype
 	mobj->tics = st->tics;
 	mobj->sprite = st->sprite;
 	mobj->frame = st->frame; // FF_FRAMEMASK for frame, and other bits..
-	mobj->anim_duration = (UINT16)st->var2; // only used if FF_ANIMATE is set
+	P_SetupStateAnimation((mobj_t*)mobj, st);
 
 	// set subsector and/or block links
 	P_SetPrecipitationThingPosition(mobj);
diff --git a/src/p_pspr.h b/src/p_pspr.h
index 9420796ff..85489ecc7 100644
--- a/src/p_pspr.h
+++ b/src/p_pspr.h
@@ -37,14 +37,12 @@
 
 /// \brief Frame flags: only the frame number - 0 to 511 (Frames from 0 to 63, Sprite2 number uses full range)
 #define FF_FRAMEMASK 0x1ff
-/// \brief Frame flags: A change of state at the end of Sprite2 animation
+
+/// \brief Frame flags - SPR2: A change of state at the end of Sprite2 animation
 #define FF_SPR2ENDSTATE 0x1000
-/// \brief Frame flags: 50% of starting in middle of animation (Sprite2 and FF_ANIMATE)
-#define FF_MIDDLESTARTCHANCE 0x2000
-/// \brief Frame flags: Simple stateless animation
-#define FF_ANIMATE 0x4000
-/// \brief Frame flags: frame always appears full bright
-#define FF_FULLBRIGHT 0x8000
+/// \brief Frame flags - SPR2: 50% of starting in middle of Sprite2 animation
+#define FF_SPR2MIDSTART 0x2000
+
 /// \brief Frame flags: 0 = no trans(opaque), 1-15 = transl. table
 #define FF_TRANSMASK 0xf0000
 /// \brief shift for FF_TRANSMASK
@@ -60,6 +58,16 @@
 #define FF_TRANS80 (tr_trans80<<FF_TRANSSHIFT)
 #define FF_TRANS90 (tr_trans90<<FF_TRANSSHIFT)
 
+/// \brief Frame flags: frame always appears full bright
+#define FF_FULLBRIGHT 0x00100000
+
+/// \brief Frame flags - Animate: Simple stateless animation
+#define FF_ANIMATE 0x01000000
+/// \brief Frame flags - Animate: Start at a random place in the animation (mutually exclusive with below)
+#define FF_RANDOMANIM 0x02000000
+/// \brief Frame flags - Animate: Sync animation to global timer (mutually exclusive with above)
+#define FF_GLOBALANIM 0x04000000
+
 /**	\brief translucency tables
 
 	\todo add another asm routine which use the fg and bg indexes in the

From f6fc99b06dce94e3064cff6c97fd8ab71da299e1 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Fri, 30 Sep 2016 14:38:56 +0100
Subject: [PATCH 38/41] How did this disappear?

---
 src/p_map.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_map.c b/src/p_map.c
index 8a55bcc1d..b404473d3 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -216,7 +216,7 @@ boolean P_DoSpring(mobj_t *spring, mobj_t *object)
 		else if (P_MobjFlip(object)*vertispeed > 0)
 			P_SetPlayerMobjState(object, S_PLAY_SPRING);
 		else if (P_MobjFlip(object)*vertispeed < 0)
-			P_SetPlayerMobjState(object, S_PLAY_FALL)
+			P_SetPlayerMobjState(object, S_PLAY_FALL);
 		else // horizontal spring
 		{
 			if (pflags & (PF_JUMPED|PF_SPINNING) && (object->player->panim == PA_ROLL || object->player->panim == PA_JUMP || object->player->panim == PA_FALL))

From cdaab7ec9f527f79c3c07d6cdd8ed887295a0333 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Fri, 30 Sep 2016 16:00:34 +0100
Subject: [PATCH 39/41] * Upped speed of info.c's red and yellow horizontal
 springs * Turn off friction for the one tic after touching a spring

---
 src/info.c   | 4 ++--
 src/p_mobj.c | 2 +-
 2 files changed, 3 insertions(+), 3 deletions(-)

diff --git a/src/info.c b/src/info.c
index 884217611..bbda94afc 100644
--- a/src/info.c
+++ b/src/info.c
@@ -5610,7 +5610,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		32*FRACUNIT,    // height
 		0,              // display offset
 		0,              // mass
-		16*FRACUNIT,    // damage
+		36*FRACUNIT,    // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SPRING|MF_NOGRAVITY, // flags
 		S_YHORIZ2       // raisestate
@@ -5637,7 +5637,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		32*FRACUNIT,    // height
 		0,              // display offset
 		0,              // mass
-		64*FRACUNIT,    // damage
+		72*FRACUNIT,    // damage
 		sfx_None,       // activesound
 		MF_SOLID|MF_SPRING|MF_NOGRAVITY, // flags
 		S_RHORIZ2       // raisestate
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 6f655c15a..988cb2f0e 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -1858,7 +1858,7 @@ static void P_XYFriction(mobj_t *mo, fixed_t oldx, fixed_t oldy)
 			mo->momx = player->cmomx;
 			mo->momy = player->cmomy;
 		}
-		else
+		else if (!(mo->eflags & MFE_SPRUNG))
 		{
 			if (oldx == mo->x && oldy == mo->y) // didn't go anywhere
 			{

From 664cfa2a9d8fd9da9fba94aa44521a5cabcf8c27 Mon Sep 17 00:00:00 2001
From: toasterbabe <rollerorbital@gmail.com>
Date: Sun, 2 Oct 2016 18:17:23 +0100
Subject: [PATCH 40/41] Disabled MF2_LINKDRAW's ability to work in chains
 because it required modifying flags2 to prevent infinite loops, and that
 isn't network safe at all.

---
 src/r_things.c | 7 ++++++-
 1 file changed, 6 insertions(+), 1 deletion(-)

diff --git a/src/r_things.c b/src/r_things.c
index fb4499c44..e8aa2956f 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1319,8 +1319,9 @@ static void R_ProjectSprite(mobj_t *thing)
 
 	if ((thing->flags2 & MF2_LINKDRAW) && thing->tracer) // toast 16/09/16 (SYMMETRY)
 	{
-		mobj_t *link, *link2;
 		fixed_t linkscale;
+#if 0 // support for chains of linkdraw - probably not network safe to modify mobjs during rendering
+		mobj_t *link, *link2;
 
 		for (link = thing->tracer; (link->tracer && (link->flags2 & MF2_LINKDRAW)); link = link->tracer)
 			link->flags2 &= ~MF2_LINKDRAW; // to prevent infinite loops, otherwise would just be a ;
@@ -1333,6 +1334,10 @@ static void R_ProjectSprite(mobj_t *thing)
 
 		tr_x = link->x - viewx;
 		tr_y = link->y - viewy;
+#else
+		tr_x = thing->tracer->x - viewx;
+		tr_y = thing->tracer->y - viewy;
+#endif
 		gxt = FixedMul(tr_x, viewcos);
 		gyt = -FixedMul(tr_y, viewsin);
 		tz = gxt-gyt;

From 482f60e5bc073e4e9663374f5465ab0d077f095c Mon Sep 17 00:00:00 2001
From: Inuyasha <MattWCSTRFAN@gmail.com>
Date: Mon, 3 Oct 2016 00:51:18 -0700
Subject: [PATCH 41/41] warning: suggest parentheses around arithmetic in
 operand of '|'

---
 src/p_mobj.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/p_mobj.c b/src/p_mobj.c
index cac1c2ccc..fb648c1d5 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -7596,7 +7596,7 @@ void P_MobjThinker(mobj_t *mobj)
 			{
 				// Special case for ALL monitors.
 				// If a box's speed is nonzero, it's allowed to respawn as a WRM/SRM.
-				if (mobj->info->speed != 0 && (mobj->flags2 & MF2_AMBUSH|MF2_STRONGBOX))
+				if (mobj->info->speed != 0 && (mobj->flags2 & (MF2_AMBUSH|MF2_STRONGBOX)))
 				{
 					mobjtype_t spawnchance[64];
 					INT32 numchoices = 0, i = 0;