diff --git a/src/hardware/hw_main.c b/src/hardware/hw_main.c
index 1f0793f70ad8033e6aed9ca8839419d1affb8bbd..fa3f27595bb92be3bede385a17daee01694bc914 100644
--- a/src/hardware/hw_main.c
+++ b/src/hardware/hw_main.c
@@ -5161,6 +5161,8 @@ static void HWR_ProjectSprite(mobj_t *thing)
 	spriteyscale = FIXED_TO_FLOAT(interp.spriteyscale);
 
 	// transform the origin point
+	if (thing->type == MT_OVERLAY) // Handle overlays
+		R_ThingOffsetOverlay(thing, &interp.x, &interp.y);
 	tr_x = FIXED_TO_FLOAT(interp.x) - gl_viewx;
 	tr_y = FIXED_TO_FLOAT(interp.y) - gl_viewy;
 
@@ -5477,6 +5479,8 @@ static void HWR_ProjectSprite(mobj_t *thing)
 
 		// calculate tz for tracer, same way it is calculated for this sprite
 		// transform the origin point
+		if (thing->tracer->type == MT_OVERLAY) // Handle overlays
+			R_ThingOffsetOverlay(thing->tracer, &tracer_interp.x, &tracer_interp.y);
 		tr_x = FIXED_TO_FLOAT(tracer_interp.x) - gl_viewx;
 		tr_y = FIXED_TO_FLOAT(tracer_interp.y) - gl_viewy;
 
diff --git a/src/hardware/hw_md2.c b/src/hardware/hw_md2.c
index 3e080105cbde44fd66775c1d4065c10c57fb7041..03a5cc60ca6c681667a26d2af4762475bceabcbb 100644
--- a/src/hardware/hw_md2.c
+++ b/src/hardware/hw_md2.c
@@ -1577,6 +1577,8 @@ boolean HWR_DrawModel(gl_vissprite_t *spr)
 #undef INTERPOLERATION_LIMIT
 #endif
 
+		if (spr->mobj->type == MT_OVERLAY) // Handle overlays
+			R_ThingOffsetOverlay(spr->mobj, &interp.x, &interp.y);
 		//Hurdler: it seems there is still a small problem with mobj angle
 		p.x = FIXED_TO_FLOAT(interp.x);
 		p.y = FIXED_TO_FLOAT(interp.y)+md2->offset;
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 2605f6777abc4a0c2bfa69aae98b8b37dea19331..cd19c4114a475cacd650c7a6b80edc0e19f7c4c1 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -6838,15 +6838,33 @@ static boolean P_ShieldLook(mobj_t *thing, shieldtype_t shield)
 
 	P_SetScale(thing, FixedMul(thing->target->scale, thing->target->player->shieldscale));
 	thing->destscale = thing->scale;
+	thing->old_scale = FixedMul(thing->target->old_scale, thing->target->player->shieldscale);
+
+#define NewMH(mobj)   mobj->height // Ugly mobj-height and player-height defines, for the sake of prettier code
+#define NewPH(player) P_GetPlayerHeight(player)
+#define OldMH(mobj)   FixedMul(mobj->height, FixedDiv(mobj->old_scale, mobj->scale))
+#define OldPH(player) FixedMul(player->height, player->mo->old_scale)
 	P_UnsetThingPosition(thing);
 	thing->x = thing->target->x;
 	thing->y = thing->target->y;
+	thing->old_x = thing->target->old_x;
+	thing->old_y = thing->target->old_y;
 	if (thing->eflags & MFE_VERTICALFLIP)
-		thing->z = thing->target->z + (thing->target->height - thing->height + FixedDiv(P_GetPlayerHeight(thing->target->player) - thing->target->height, 3*FRACUNIT)) - FixedMul(2*FRACUNIT, thing->target->scale);
+	{
+		thing->z     = thing->target->z     + NewMH(thing->target) - NewMH(thing) + ((NewPH(thing->target->player) - NewMH(thing->target)) / 3) - (thing->target->scale     * 2);
+		thing->old_z = thing->target->old_z + OldMH(thing->target) - OldMH(thing) + ((OldPH(thing->target->player) - OldMH(thing->target)) / 3) - (thing->target->old_scale * 2);
+	}
 	else
-		thing->z = thing->target->z - (FixedDiv(P_GetPlayerHeight(thing->target->player) - thing->target->height, 3*FRACUNIT)) + FixedMul(2*FRACUNIT, thing->target->scale);
+	{
+		thing->z     = thing->target->z     - ((NewPH(thing->target->player) - NewMH(thing->target)) / 3) + (thing->target->scale     * 2);
+		thing->old_z = thing->target->old_z - ((OldPH(thing->target->player) - OldMH(thing->target)) / 3) + (thing->target->old_scale * 2);
+	}
 	P_SetThingPosition(thing);
 	P_CheckPosition(thing, thing->x, thing->y);
+#undef NewMH
+#undef NewPH
+#undef OldMH
+#undef OldPH
 
 	if (P_MobjWasRemoved(thing))
 		return false;
@@ -6912,10 +6930,11 @@ void P_RunOverlays(void)
 {
 	// run overlays
 	mobj_t *mo, *next = NULL;
-	fixed_t destx,desty,zoffs;
 
 	for (mo = overlaycap; mo; mo = next)
 	{
+		fixed_t zoffs;
+
 		I_Assert(!P_MobjWasRemoved(mo));
 
 		// grab next in chain, then unset the chain target
@@ -6931,31 +6950,11 @@ void P_RunOverlays(void)
 			continue;
 		}
 
-		if (!splitscreen /*&& rendermode != render_soft*/)
-		{
-			angle_t viewingangle;
-
-			if (players[displayplayer].awayviewtics && players[displayplayer].awayviewmobj != NULL && !P_MobjWasRemoved(players[displayplayer].awayviewmobj))
-				viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, players[displayplayer].awayviewmobj->x, players[displayplayer].awayviewmobj->y);
-			else if (!camera.chase && players[displayplayer].mo)
-				viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, players[displayplayer].mo->x, players[displayplayer].mo->y);
-			else
-				viewingangle = R_PointToAngle2(mo->target->x, mo->target->y, camera.x, camera.y);
-
-			if (!(mo->state->frame & FF_ANIMATE) && mo->state->var1)
-				viewingangle += ANGLE_180;
-			destx = mo->target->x + P_ReturnThrustX(mo->target, viewingangle, FixedMul(FRACUNIT/4, mo->scale));
-			desty = mo->target->y + P_ReturnThrustY(mo->target, viewingangle, FixedMul(FRACUNIT/4, mo->scale));
-		}
-		else
-		{
-			destx = mo->target->x;
-			desty = mo->target->y;
-		}
-
 		mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP) | (mo->target->eflags & MFE_VERTICALFLIP);
 		mo->scale = mo->destscale = mo->target->scale;
+		mo->old_scale = mo->target->old_scale;
 		mo->angle = (mo->target->player ? mo->target->player->drawangle : mo->target->angle) + mo->movedir;
+		mo->old_angle = (mo->target->player ? mo->target->player->old_drawangle : mo->target->old_angle) + mo->movedir;
 
 		if (!(mo->state->frame & FF_ANIMATE))
 			zoffs = FixedMul(((signed)mo->state->var2)*FRACUNIT, mo->scale);
@@ -6965,15 +6964,26 @@ void P_RunOverlays(void)
 			zoffs = 0;
 
 		P_UnsetThingPosition(mo);
-		mo->x = destx;
-		mo->y = desty;
+		mo->x = mo->target->x;
+		mo->y = mo->target->y;
+		mo->old_x = mo->target->old_x;
+		mo->old_y = mo->target->old_y;
 		mo->radius = mo->target->radius;
 		mo->height = mo->target->height;
 		if (mo->eflags & MFE_VERTICALFLIP)
-			mo->z = (mo->target->z + mo->target->height - mo->height) - zoffs;
+		{
+			mo->z         = mo->target->z     + mo->target->height - mo->height - zoffs;
+			if (mo->scale == mo->old_scale)
+				mo->old_z = mo->target->old_z + mo->target->height - mo->height - zoffs;
+			else // Interpolate height scale changes - mo and mo->target have the same scales here, so don't interpolate them individually
+				mo->old_z = mo->target->old_z + FixedMul(mo->target->height - mo->height, FixedDiv(mo->old_scale, mo->scale)) - zoffs;
+		}
 		else
-			mo->z = mo->target->z + zoffs;
-		if (mo->state->var1)
+		{
+			mo->z     = mo->target->z     + zoffs;
+			mo->old_z = mo->target->old_z + zoffs;
+		}
+		if (!(mo->state->frame & FF_ANIMATE) && mo->state->var1)
 			P_SetUnderlayPosition(mo);
 		else
 			P_SetThingPosition(mo);
diff --git a/src/r_things.c b/src/r_things.c
index 557e1681439cd13d6bd871f7c33a789a0d2ba2be..d49b8e3179c01d5bce5c466d52d99cc9c4cb38db 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -1635,6 +1635,8 @@ static void R_ProjectSprite(mobj_t *thing)
 	height = interp.height; // Ditto
 
 	// transform the origin point
+	if (thing->type == MT_OVERLAY) // Handle overlays
+		R_ThingOffsetOverlay(thing, &interp.x, &interp.y);
 	tr_x = interp.x - viewx;
 	tr_y = interp.y - viewy;
 
@@ -1980,6 +1982,8 @@ static void R_ProjectSprite(mobj_t *thing)
 		radius = tracer_interp.radius; // For drop shadows
 		height = tracer_interp.height; // Ditto
 
+		if (thing->type == MT_OVERLAY) // Handle overlays
+			R_ThingOffsetOverlay(thing, &tracer_interp.x, &tracer_interp.y);
 		tr_x = (tracer_interp.x + sort_x) - viewx;
 		tr_y = (tracer_interp.y + sort_y) - viewy;
 		tz = FixedMul(tr_x, viewcos) + FixedMul(tr_y, viewsin);
@@ -3574,6 +3578,50 @@ boolean R_ThingIsFullDark(mobj_t *thing)
 	return ((thing->frame & FF_BRIGHTMASK) == FF_FULLDARK || (thing->renderflags & RF_BRIGHTMASK) == RF_FULLDARK);
 }
 
+// Offsets MT_OVERLAY towards the camera at render-time - Works in splitscreen!
+// The &x and &y arguments should be pre-interpolated, and will be modified
+void R_ThingOffsetOverlay(mobj_t *thing, fixed_t *x, fixed_t *y)
+{
+	mobj_t *mobj = thing;
+	INT16 offset = 0; // Offset towards or away from the camera, and how much
+	fixed_t offsetscale = thing->scale; // Occasionally needs to be interpolated
+	angle_t viewingangle;
+	UINT8 looplimit = 255; // Prevent infinite loops - A chain of 255 connected overlays is enough for any sane use case
+
+#ifdef PARANOIA
+	if (P_MobjWasRemoved(mobj) || !x || !y)
+		I_Error("R_ThingOffsetOverlay: thing, x, or y is invalid");
+#endif
+
+	do // Get the overlay's offset
+	{
+		// Does the overlay use FF_ANIMATE? If not, if var1 is non-zero, it's an underlay instead of an overlay
+		if (!(mobj->state->frame & FF_ANIMATE) && mobj->state->var1)
+			offset += 1; // Underlay below the target, away from the camera
+		else
+			offset -= 1; // Overlay on top of the target, towards the camera
+
+		looplimit -= 1;
+		mobj = mobj->target;
+	} while (!P_MobjWasRemoved(mobj) && mobj->type == MT_OVERLAY && looplimit > 0); // Handle overlays following other overlays
+
+	// Does the offset scale need to be interpolated?
+	if (thing->scale != thing->old_scale && R_UsingFrameInterpolation() && !paused)
+	{
+		interpmobjstate_t interp = {0};
+		R_InterpolateMobjState(thing, rendertimefrac, &interp);
+		offsetscale = interp.scale;
+	}
+
+
+	// Get the angle from the camera to the X and Y coordinates
+	viewingangle = R_PointToAngle(*x, *y);
+
+	// Finally, offset the X and Y coordinates towards or away from the camera
+	*x += P_ReturnThrustX(thing, viewingangle, FixedMul(offset * (FRACUNIT/4), offsetscale));
+	*y += P_ReturnThrustY(thing, viewingangle, FixedMul(offset * (FRACUNIT/4), offsetscale));
+}
+
 //
 // R_DrawMasked
 //
diff --git a/src/r_things.h b/src/r_things.h
index 318234886bbbfa2b603f1883d50bbfea4c67d370..ed2156baab4b15784360c0664352a1b8b2461d19 100644
--- a/src/r_things.h
+++ b/src/r_things.h
@@ -88,6 +88,8 @@ boolean R_ThingIsFullBright (mobj_t *thing);
 boolean R_ThingIsSemiBright (mobj_t *thing);
 boolean R_ThingIsFullDark (mobj_t *thing);
 
+void R_ThingOffsetOverlay (mobj_t *thing, fixed_t *outx, fixed_t *outy);
+
 // --------------
 // MASKED DRAWING
 // --------------