diff --git a/src/b_bot.c b/src/b_bot.c
index d1465f891d481dd8314507b8f6b5e2c795abb1dd..57f7623042d318df981af823ac59d1865c44fb75 100644
--- a/src/b_bot.c
+++ b/src/b_bot.c
@@ -631,7 +631,8 @@ void B_HandleFlightIndicator(player_t *player)
 	}
 
 	// otherwise, update its visibility
-	if (P_IsLocalPlayer(player->botleader))
+	tails->hnext->drawonlyforplayer = player->botleader; // Hide it from the other player in splitscreen, and yourself when spectating
+	if (P_IsLocalPlayer(player->botleader)) // Only display it on your own view. Don't display it for spectators
 		tails->hnext->flags2 &= ~MF2_DONTDRAW;
 	else
 		tails->hnext->flags2 |= MF2_DONTDRAW;
diff --git a/src/g_game.c b/src/g_game.c
index 15b1e10816318ffef1f1ed530a6c9cc0751b2c46..8264fbe6bb0826409d142d0b519ad7fa2d319ca2 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1441,6 +1441,7 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 			// I assume this is netgame-safe because gunslinger spawns this for only the local player...... *sweats intensely*
 			newtarget = P_SpawnMobj(ticcmd_ztargetfocus[forplayer]->x, ticcmd_ztargetfocus[forplayer]->y, ticcmd_ztargetfocus[forplayer]->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
 			P_SetTarget(&newtarget->target, ticcmd_ztargetfocus[forplayer]);
+			newtarget->drawonlyforplayer = player; // Hide it from the other player in splitscreen, and yourself when spectating
 
 			if (player->mo && P_AproxDistance(
 				player->mo->x - ticcmd_ztargetfocus[forplayer]->x,
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index ed5736fa7f06292be4df17759de81bc2a517ced1..838e2f53ba6088e599bbedc4fbd7b20bfa1edfcb 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -689,11 +689,12 @@ static int lib_pSpawnLockOn(lua_State *L)
 		return LUA_ErrInvalid(L, "player_t");
 	if (state >= NUMSTATES)
 		return luaL_error(L, "state %d out of range (0 - %d)", state, NUMSTATES-1);
-	if (P_IsLocalPlayer(player)) // Only display it on your own view.
+	if (P_IsLocalPlayer(player)) // Only display it on your own view. Don't display it for spectators
 	{
 		mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
 		P_SetTarget(&visual->target, lockon);
 		visual->flags2 |= MF2_DONTDRAW;
+		visual->drawonlyforplayer = player; // Hide it from the other player in splitscreen, and yourself when spectating
 		P_SetMobjStateNF(visual, state);
 	}
 	return 0;
diff --git a/src/lua_mobjlib.c b/src/lua_mobjlib.c
index 851c569d27f093cd7000f465fa1b60e4ffd4769e..fd8dcec92599b81c8e0cefbc0f5a095181a64d84 100644
--- a/src/lua_mobjlib.c
+++ b/src/lua_mobjlib.c
@@ -44,6 +44,8 @@ enum mobj_e {
 	mobj_spritexoffset,
 	mobj_spriteyoffset,
 	mobj_floorspriteslope,
+	mobj_drawonlyforplayer,
+	mobj_dontdrawforviewmobj,
 	mobj_touching_sectorlist,
 	mobj_subsector,
 	mobj_floorz,
@@ -122,6 +124,8 @@ static const char *const mobj_opt[] = {
 	"spritexoffset",
 	"spriteyoffset",
 	"floorspriteslope",
+	"drawonlyforplayer",
+	"dontdrawforviewmobj",
 	"touching_sectorlist",
 	"subsector",
 	"floorz",
@@ -262,6 +266,17 @@ static int mobj_get(lua_State *L)
 	case mobj_floorspriteslope:
 		LUA_PushUserdata(L, mo->floorspriteslope, META_SLOPE);
 		break;
+	case mobj_drawonlyforplayer:
+		LUA_PushUserdata(L, mo->drawonlyforplayer, META_PLAYER);
+		break;
+	case mobj_dontdrawforviewmobj:
+		if (mo->dontdrawforviewmobj && P_MobjWasRemoved(mo->dontdrawforviewmobj))
+		{ // don't put invalid mobj back into Lua.
+			P_SetTarget(&mo->dontdrawforviewmobj, NULL);
+			return 0;
+		}
+		LUA_PushUserdata(L, mo->dontdrawforviewmobj, META_MOBJ);
+		break;
 	case mobj_touching_sectorlist:
 		return UNIMPLEMENTED;
 	case mobj_subsector:
@@ -551,6 +566,24 @@ static int mobj_set(lua_State *L)
 		break;
 	case mobj_floorspriteslope:
 		return NOSET;
+	case mobj_drawonlyforplayer:
+		if (lua_isnil(L, 3))
+			mo->drawonlyforplayer = NULL;
+		else
+		{
+			player_t *drawonlyforplayer = *((player_t **)luaL_checkudata(L, 3, META_PLAYER));
+			mo->drawonlyforplayer = drawonlyforplayer;
+		}
+		break;
+	case mobj_dontdrawforviewmobj:
+		if (lua_isnil(L, 3))
+			P_SetTarget(&mo->dontdrawforviewmobj, NULL);
+		else
+		{
+			mobj_t *dontdrawforviewmobj = *((mobj_t **)luaL_checkudata(L, 3, META_MOBJ));
+			P_SetTarget(&mo->dontdrawforviewmobj, dontdrawforviewmobj);
+		}
+		break;
 	case mobj_touching_sectorlist:
 		return UNIMPLEMENTED;
 	case mobj_subsector:
diff --git a/src/p_mobj.c b/src/p_mobj.c
index a25b5643bb158992ff2246b31c1ee1bf0d8a1515..4ca59285f7fc46bbfeb6e2373b50eb4eae33637d 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -6882,6 +6882,7 @@ void P_RunOverlays(void)
 		mo->eflags = (mo->eflags & ~MFE_VERTICALFLIP) | (mo->target->eflags & MFE_VERTICALFLIP);
 		mo->scale = mo->destscale = mo->target->scale;
 		mo->angle = (mo->target->player ? mo->target->player->drawangle : mo->target->angle) + mo->movedir;
+		P_SetTarget(&mo->dontdrawforviewmobj, mo->target->dontdrawforviewmobj); // Hide the overlay from the view that its target is hidden from - But don't copy drawonlyforplayer!
 
 		if (!(mo->state->frame & FF_ANIMATE))
 			zoffs = FixedMul(((signed)mo->state->var2)*FRACUNIT, mo->scale);
@@ -7951,11 +7952,6 @@ static void P_MobjSceneryThink(mobj_t *mobj)
 			return;
 		}
 
-		if (!camera.chase)
-			mobj->flags2 |= MF2_DONTDRAW;
-		else
-			mobj->flags2 &= ~MF2_DONTDRAW;
-
 		P_UnsetThingPosition(mobj);
 		{
 			fixed_t radius = FixedMul(10*mobj->info->speed, mobj->target->scale);
diff --git a/src/p_mobj.h b/src/p_mobj.h
index 6a5ab84c410eaaa88a6ec00aeffe1aaf41556622..9c598f59e3ce155e66a3155c4c95bd7712f839c7 100644
--- a/src/p_mobj.h
+++ b/src/p_mobj.h
@@ -334,6 +334,8 @@ typedef struct mobj_s
 	// Player and mobj sprites in multiplayer modes are modified
 	//  using an internal color lookup table for re-indexing.
 	UINT16 color; // This replaces MF_TRANSLATION. Use 0 for default (no translation).
+	struct player_s *drawonlyforplayer; // If set, hides the mobj for everyone except this player and their spectators
+	struct mobj_s *dontdrawforviewmobj; // If set, hides the mobj if dontdrawforviewmobj is the current camera (first-person player or awayviewmobj)
 
 	// Interaction info, by BLOCKMAP.
 	// Links in blocks (if needed).
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 76f1661acee724c8b710939590a8d4dbb139a303..8f11e63e58a25ba43ff6f78f3d9713332330e402 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1553,30 +1553,32 @@ typedef enum
 
 typedef enum
 {
-	MD2_CUSVAL      = 1,
-	MD2_CVMEM       = 1<<1,
-	MD2_SKIN        = 1<<2,
-	MD2_COLOR       = 1<<3,
-	MD2_SCALESPEED  = 1<<4,
-	MD2_EXTVAL1     = 1<<5,
-	MD2_EXTVAL2     = 1<<6,
-	MD2_HNEXT       = 1<<7,
-	MD2_HPREV       = 1<<8,
-	MD2_FLOORROVER  = 1<<9,
-	MD2_CEILINGROVER = 1<<10,
-	MD2_SLOPE        = 1<<11,
-	MD2_COLORIZED    = 1<<12,
-	MD2_MIRRORED     = 1<<13,
-	MD2_SPRITEROLL   = 1<<14,
-	MD2_SHADOWSCALE  = 1<<15,
-	MD2_RENDERFLAGS  = 1<<16,
-	MD2_BLENDMODE    = 1<<17,
-	MD2_SPRITEXSCALE = 1<<18,
-	MD2_SPRITEYSCALE = 1<<19,
-	MD2_SPRITEXOFFSET = 1<<20,
-	MD2_SPRITEYOFFSET = 1<<21,
-	MD2_FLOORSPRITESLOPE = 1<<22,
-	MD2_DISPOFFSET = 1<<23
+	MD2_CUSVAL              = 1,
+	MD2_CVMEM               = 1<<1,
+	MD2_SKIN                = 1<<2,
+	MD2_COLOR               = 1<<3,
+	MD2_SCALESPEED          = 1<<4,
+	MD2_EXTVAL1             = 1<<5,
+	MD2_EXTVAL2             = 1<<6,
+	MD2_HNEXT               = 1<<7,
+	MD2_HPREV               = 1<<8,
+	MD2_FLOORROVER          = 1<<9,
+	MD2_CEILINGROVER        = 1<<10,
+	MD2_SLOPE               = 1<<11,
+	MD2_COLORIZED           = 1<<12,
+	MD2_MIRRORED            = 1<<13,
+	MD2_SPRITEROLL          = 1<<14,
+	MD2_SHADOWSCALE         = 1<<15,
+	MD2_RENDERFLAGS         = 1<<16,
+	MD2_BLENDMODE           = 1<<17,
+	MD2_SPRITEXSCALE        = 1<<18,
+	MD2_SPRITEYSCALE        = 1<<19,
+	MD2_SPRITEXOFFSET       = 1<<20,
+	MD2_SPRITEYOFFSET       = 1<<21,
+	MD2_FLOORSPRITESLOPE    = 1<<22,
+	MD2_DISPOFFSET          = 1<<23,
+	MD2_DRAWONLYFORPLAYER   = 1<<24,
+	MD2_DONTDRAWFORVIEWMOBJ = 1<<25
 } mobj_diff2_t;
 
 typedef enum
@@ -1811,6 +1813,10 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		|| (slope->normal.z != FRACUNIT))
 			diff2 |= MD2_FLOORSPRITESLOPE;
 	}
+	if (mobj->drawonlyforplayer)
+		diff2 |= MD2_DRAWONLYFORPLAYER;
+	if (mobj->dontdrawforviewmobj)
+		diff2 |= MD2_DONTDRAWFORVIEWMOBJ;
 	if (mobj->dispoffset != mobj->info->dispoffset)
 		diff2 |= MD2_DISPOFFSET;
 
@@ -1988,6 +1994,10 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		WRITEFIXED(save_p, slope->normal.y);
 		WRITEFIXED(save_p, slope->normal.z);
 	}
+	if (diff2 & MD2_DRAWONLYFORPLAYER)
+		WRITEUINT8(save_p, mobj->drawonlyforplayer-players);
+	if (diff2 & MD2_DONTDRAWFORVIEWMOBJ)
+		WRITEUINT32(save_p, mobj->dontdrawforviewmobj->mobjnum);
 	if (diff2 & MD2_DISPOFFSET)
 		WRITEINT32(save_p, mobj->dispoffset);
 
@@ -3044,6 +3054,10 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 		slope->normal.y = READFIXED(save_p);
 		slope->normal.z = READFIXED(save_p);
 	}
+	if (diff2 & MD2_DRAWONLYFORPLAYER)
+		mobj->drawonlyforplayer = &players[READUINT8(save_p)];
+	if (diff2 & MD2_DONTDRAWFORVIEWMOBJ)
+		mobj->dontdrawforviewmobj = (mobj_t *)(size_t)READUINT32(save_p);
 	if (diff2 & MD2_DISPOFFSET)
 		mobj->dispoffset = READINT32(save_p);
 	else
@@ -4063,6 +4077,13 @@ static void P_RelinkPointers(void)
 		if (mobj->type == MT_HOOP || mobj->type == MT_HOOPCOLLIDE || mobj->type == MT_HOOPCENTER)
 			continue;
 
+		if (mobj->dontdrawforviewmobj)
+		{
+			temp = (UINT32)(size_t)mobj->dontdrawforviewmobj;
+			mobj->dontdrawforviewmobj = NULL;
+			if (!P_SetTarget(&mobj->dontdrawforviewmobj, P_FindNewPosition(temp)))
+				CONS_Debug(DBG_GAMELOGIC, "dontdrawforviewmobj not found on %d\n", mobj->type);
+		}
 		if (mobj->tracer)
 		{
 			temp = (UINT32)(size_t)mobj->tracer;
diff --git a/src/p_user.c b/src/p_user.c
index 62dd2fcb24e23367f55349fed517ea5c67925c46..889e887c14ea2e28898896118b951782312965c8 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -398,6 +398,7 @@ void P_GiveFinishFlags(player_t *player)
 		angle += FixedAngle(120*FRACUNIT);
 
 		P_SetTarget(&flag->target, player->mo);
+		P_SetTarget(&flag->dontdrawforviewmobj, player->mo); // Hide the flag in first-person
 	}
 }
 
@@ -1840,6 +1841,7 @@ void P_SpawnShieldOrb(player_t *player)
 	shieldobj = P_SpawnMobj(player->mo->x, player->mo->y, player->mo->z, orbtype);
 	shieldobj->flags2 |= MF2_SHIELD;
 	P_SetTarget(&shieldobj->target, player->mo);
+	P_SetTarget(&shieldobj->dontdrawforviewmobj, player->mo); // Hide the shield in first-person
 	if ((player->powers[pw_shield] & SH_NOSTACK) == SH_PINK)
 	{
 		shieldobj->color = SKINCOLOR_PINK;
@@ -1964,6 +1966,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
 	mobj_t *ghost = P_SpawnMobj(mobj->x, mobj->y, mobj->z, MT_GHOST);
 
 	P_SetTarget(&ghost->target, mobj);
+	P_SetTarget(&ghost->dontdrawforviewmobj, mobj); // Hide the ghost in first-person
 
 	P_SetScale(ghost, mobj->scale);
 	ghost->destscale = mobj->scale;
@@ -2009,6 +2012,7 @@ mobj_t *P_SpawnGhostMobj(mobj_t *mobj)
 		mobj_t *ghost2 = P_SpawnGhostMobj(mobj->player->followmobj);
 		P_SetTarget(&ghost2->tracer, ghost);
 		P_SetTarget(&ghost->tracer, ghost2);
+		P_SetTarget(&ghost2->dontdrawforviewmobj, mobj); // Hide the follow-ghost for the non-follow object
 		ghost2->flags2 |= (mobj->player->followmobj->flags2 & MF2_LINKDRAW);
 	}
 
@@ -3123,39 +3127,41 @@ static void P_DoBubbleBreath(player_t *player)
 //
 static void P_DoPlayerHeadSigns(player_t *player)
 {
+	mobj_t *sign = NULL;
+
 	if (G_TagGametype())
 	{
-		// If you're "IT", show a big "IT" over your head for others to see.
-		if (player->pflags & PF_TAGIT && !P_IsLocalPlayer(player))
+		// If you're "IT", show a big "IT" over your head for others to see, including spectators
+		// (and even yourself if you spectate someone else).
+		if (player->pflags & PF_TAGIT && (!P_IsLocalPlayer(player) || consoleplayer != displayplayer || splitscreen))
 		{
-			mobj_t* it = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_TAG);
-			it->x = player->mo->x;
-			it->y = player->mo->y;
-			it->z = player->mo->z;
-			it->old_x = player->mo->old_x;
-			it->old_y = player->mo->old_y;
-			it->old_z = player->mo->old_z;
+			sign = P_SpawnMobjFromMobj(player->mo, 0, 0, 0, MT_TAG);
+			sign->x = player->mo->x;
+			sign->y = player->mo->y;
+			sign->z = player->mo->z;
+			sign->old_x = player->mo->old_x;
+			sign->old_y = player->mo->old_y;
+			sign->old_z = player->mo->old_z;
 
 			if (!(player->mo->eflags & MFE_VERTICALFLIP))
 			{
-				it->z += player->mo->height;
-				it->old_z += player->mo->height;
+				sign->z += player->mo->height;
+				sign->old_z += player->mo->height;
 			}
 			else
 			{
-				it->z -= mobjinfo[MT_TAG].height;
-				it->old_z -= mobjinfo[MT_TAG].height;
+				sign->z -= mobjinfo[MT_TAG].height;
+				sign->old_z -= mobjinfo[MT_TAG].height;
 			}
 		}
 	}
 	else if ((gametyperules & GTR_TEAMFLAGS) && (player->gotflag & (GF_REDFLAG|GF_BLUEFLAG))) // If you have the flag (duh).
 	{
-		// Spawn a got-flag message over the head of the player that
-		// has it (but not on your own screen if you have the flag).
-		if (splitscreen || player != &players[consoleplayer])
+		// Spawn a got-flag message over the head of the player that has it
+		// (but not on your own screen if you have the flag, unless you're spectating).
+		if (!P_IsLocalPlayer(player) || consoleplayer != displayplayer || splitscreen)
 		{
 			fixed_t zofs;
-			mobj_t *sign;
 			boolean player_is_flipped = (player->mo->eflags & MFE_VERTICALFLIP) > 0;
 
 			zofs = player->mo->momz;
@@ -3187,6 +3193,43 @@ static void P_DoPlayerHeadSigns(player_t *player)
 				sign->frame = 2|FF_FULLBRIGHT;
 		}
 	}
+
+	if (!P_MobjWasRemoved(sign) && splitscreen) // Hide the sign from yourself in splitscreen - In single-screen, it wouldn't get spawned if it shouldn't be visible
+	{
+		if (player == &players[displayplayer])
+			sign->drawonlyforplayer = &players[secondarydisplayplayer];
+		else
+			sign->drawonlyforplayer = &players[displayplayer];
+
+#ifdef QUADS
+		if (splitscreen > 1) // Can be seen by at least two local views, so we need an extra copy of the sign
+		{
+			UINT32 signframe = sign->frame; // Copy the flag frame
+			sign = P_SpawnMobjFromMobj(sign, 0, 0, 0, MT_TAG);
+			if (P_MobjWasRemoved(sign))
+				return;
+
+			sign->frame = signframe;
+			if (player == &players[displayplayer] || player == &players[secondarydisplayplayer])
+				sign->drawonlyforplayer = &players[thirddisplayplayer];
+			else
+				sign->drawonlyforplayer = &players[secondarydisplayplayer];
+
+			if (splitscreen > 2) // Can be seen by three local views
+			{
+				sign = P_SpawnMobjFromMobj(sign, 0, 0, 0, MT_TAG);
+				if (P_MobjWasRemoved(sign))
+					return;
+
+				sign->frame = signframe;
+				if (player != &players[fourthdisplayplayer])
+					sign->drawonlyforplayer = &players[fourthdisplayplayer];
+				else
+					sign->drawonlyforplayer = &players[thirddisplayplayer];
+			}
+		}
+#endif
+	}
 }
 
 //
@@ -4707,10 +4750,11 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
 						mobj_t *lockon = P_LookForEnemies(player, false, true);
 						if (lockon)
 						{
-							if (P_IsLocalPlayer(player)) // Only display it on your own view.
+							if (P_IsLocalPlayer(player)) // Only display it on your own view. Don't display it for spectators
 							{
 								mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
 								P_SetTarget(&visual->target, lockon);
+								visual->drawonlyforplayer = player; // Hide it from the other player in splitscreen, and yourself when spectating
 							}
 						}
 						if ((cmd->buttons & BT_SPIN) && !(player->pflags & PF_SPINDOWN))
@@ -5054,7 +5098,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock
 		{
 			if ((lockonshield = P_LookForEnemies(player, false, false)))
 			{
-				if (P_IsLocalPlayer(player)) // Only display it on your own view.
+				if (P_IsLocalPlayer(player)) // Only display it on your own view. Don't display it for spectators
 				{
 					boolean dovis = true;
 					if (lockonshield == lockonthok)
@@ -5068,6 +5112,7 @@ static boolean P_PlayerShieldThink(player_t *player, ticcmd_t *cmd, mobj_t *lock
 					{
 						visual = P_SpawnMobj(lockonshield->x, lockonshield->y, lockonshield->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
 						P_SetTarget(&visual->target, lockonshield);
+						visual->drawonlyforplayer = player; // Hide it from the other player in splitscreen, and yourself when spectating
 						P_SetMobjStateNF(visual, visual->info->spawnstate+1);
 					}
 				}
@@ -5171,10 +5216,11 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 
 	if ((player->charability == CA_HOMINGTHOK) && !player->homing && (player->pflags & PF_JUMPED) && (!(player->pflags & PF_THOKKED) || (player->charflags & SF_MULTIABILITY)) && (lockonthok = P_LookForEnemies(player, true, false)))
 	{
-		if (P_IsLocalPlayer(player)) // Only display it on your own view.
+		if (P_IsLocalPlayer(player)) // Only display it on your own view. Don't display it for spectators
 		{
 			visual = P_SpawnMobj(lockonthok->x, lockonthok->y, lockonthok->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
 			P_SetTarget(&visual->target, lockonthok);
+			visual->drawonlyforplayer = player; // Hide it from the other player in splitscreen, and yourself when spectating
 		}
 	}
 
@@ -11084,17 +11130,30 @@ static void P_MinecartThink(player_t *player)
 
 			// Mark interpolation; the old positions need to be relative to the displacement from the minecart _after_ it's moved.
 			// This isn't quite correct (it captures the landing wobble) but it works well enough
+			// Additionally, hide other players' marks
 			if (detleft)
 			{
-				detleft->old_x = detleft->x - (minecart->old_x - minecart->old_x2);
-				detleft->old_y = detleft->y - (minecart->old_y - minecart->old_y2);
-				detleft->old_z = detleft->z - (minecart->old_z - minecart->old_z2);
+				if (P_IsLocalPlayer(player))
+				{
+					detleft->old_x = detleft->x - (minecart->old_x - minecart->old_x2);
+					detleft->old_y = detleft->y - (minecart->old_y - minecart->old_y2);
+					detleft->old_z = detleft->z - (minecart->old_z - minecart->old_z2);
+					detleft->drawonlyforplayer = player; // Hide it from the other player in splitscreen, and yourself when spectating
+				}
+				else // Don't see others' marks when spectating others
+					P_RemoveMobj(detleft); // Lock-on markers are only spawned client-side, so this SHOULD be safe too...
 			}
 			if (detright)
 			{
-				detright->old_x = detright->x - (minecart->old_x - minecart->old_x2);
-				detright->old_y = detright->y - (minecart->old_y - minecart->old_y2);
-				detright->old_z = detright->z - (minecart->old_z - minecart->old_z2);
+				if (P_IsLocalPlayer(player))
+				{
+					detright->old_x = detright->x - (minecart->old_x - minecart->old_x2);
+					detright->old_y = detright->y - (minecart->old_y - minecart->old_y2);
+					detright->old_z = detright->z - (minecart->old_z - minecart->old_z2);
+					detright->drawonlyforplayer = player;
+				}
+				else
+					P_RemoveMobj(detleft);
 			}
 		}
 		else
@@ -11386,12 +11445,15 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume)
 
 			for (i = -1; i < 2; i += 2)
 			{
+				mobj_t *bubble;
 				offsetH = i*P_ReturnThrustX(fume, fume->movedir, radiusV);
 				offsetV = i*P_ReturnThrustY(fume, fume->movedir, radiusV);
 				x = mo->x + radiusX + FixedMul(offsetH, factorX);
 				y = mo->y + radiusY + FixedMul(offsetH, factorY);
 				z = mo->z + heightoffset + offsetV;
-				P_SpawnMobj(x, y, z, MT_SMALLBUBBLE)->scale = mo->scale >> 1;
+				bubble = P_SpawnMobj(x, y, z, MT_SMALLBUBBLE);
+				bubble->scale = mo->scale >> 1;
+				P_SetTarget(&bubble->dontdrawforviewmobj, mo); // Hide the bubble in first-person
 			}
 
 			fume->movefactor = 0;
@@ -11460,7 +11522,11 @@ static void P_DoMetalJetFume(player_t *player, mobj_t *fume)
 
 	// If dashmode is high enough, spawn a trail
 	if (player->normalspeed >= skins[player->skin].normalspeed*2)
-		P_SpawnGhostMobj(fume);
+	{
+		mobj_t *ghost = P_SpawnGhostMobj(fume);
+		if (!P_MobjWasRemoved(ghost))
+			P_SetTarget(&ghost->dontdrawforviewmobj, mo); // Hide the trail in first-person
+	}
 }
 
 //
@@ -12104,14 +12170,6 @@ void P_PlayerThink(player_t *player)
 				gmobj->tracer->frame |= tr_trans70<<FF_TRANSSHIFT;
 			}
 		}
-
-		// Hide the mobj from our sights if we're the displayplayer and chasecam is off,
-		// or secondarydisplayplayer and chasecam2 is off.
-		// Why not just not spawn the mobj?  Well, I'd rather only flirt with
-		// consistency so much...
-		if ((player == &players[displayplayer] && !camera.chase)
-		|| (splitscreen && player == &players[secondarydisplayplayer] && !camera2.chase))
-			gmobj->flags2 |= MF2_DONTDRAW;
 	}
 #endif
 
diff --git a/src/r_things.c b/src/r_things.c
index 248357e11fb0dc54f7c727cc75d01616dfb42d63..eee284d46f8e2ddb0383b97be71584a801c27386 100644
--- a/src/r_things.c
+++ b/src/r_things.c
@@ -3322,9 +3322,14 @@ void R_ClipSprites(drawseg_t* dsstart, portal_t* portal)
 boolean R_ThingVisible (mobj_t *thing)
 {
 	return (!(
-				thing->sprite == SPR_NULL ||
-				( thing->flags2 & (MF2_DONTDRAW) ) ||
-				(r_viewmobj && (thing == r_viewmobj || (r_viewmobj->player && r_viewmobj->player->followmobj == thing)))
+		(thing->sprite == SPR_NULL) || // Don't draw null-sprites
+		(thing->flags2 & MF2_DONTDRAW) || // Don't draw MF2_LINKDRAW objects
+		(thing->drawonlyforplayer && thing->drawonlyforplayer != viewplayer) || // Don't draw other players' personal objects
+		(r_viewmobj && (
+		  (r_viewmobj == thing) || // Don't draw first-person players or awayviewmobj objects
+		  (r_viewmobj->player && r_viewmobj->player->followmobj == thing) || // Don't draw first-person players' followmobj
+		  (r_viewmobj == thing->dontdrawforviewmobj) // Don't draw objects that are hidden for the current view
+		))
 	));
 }