diff --git a/src/dehacked.c b/src/dehacked.c
index 2661fc2fd8cb031e0d910f2811e68b51af6b3030..61ecc5f6bf807e9421de63c3563ab70f08f99fff 100644
--- a/src/dehacked.c
+++ b/src/dehacked.c
@@ -5910,16 +5910,16 @@ static const char *const STATE_LIST[] = { // array length left dynamic for sanit
 	"S_FOUR2",
 	"S_FIVE2",
 
-	"S_LOCKON",
+	"S_LOCKON1",
+	"S_LOCKON2",
 
 	// Tag Sign
 	"S_TTAG",
 
 	// Got Flag Sign
-	"S_GOTREDFLAG1",
-	"S_GOTREDFLAG2",
-	"S_GOTBLUEFLAG1",
-	"S_GOTBLUEFLAG2",
+	"S_GOTFLAG",
+	"S_GOTREDFLAG",
+	"S_GOTBLUEFLAG",
 
 	"S_CORK",
 
@@ -6714,8 +6714,7 @@ static const char *const MOBJTYPE_LIST[] = {  // array length left dynamic for s
 	"MT_GOTEMERALD", // Chaos Emerald (intangible)
 	"MT_LOCKON", // Target
 	"MT_TAG", // Tag Sign
-	"MT_GOTREDFLAG", // Got Flag sign
-	"MT_GOTBLUEFLAG", // Got Flag sign
+	"MT_GOTFLAG", // Got Flag sign
 
 	// Ambient Sounds
 	"MT_AWATERA", // Ambient Water Sound 1
diff --git a/src/info.c b/src/info.c
index 491e7cdb0d5c5ba7921ff4b164f250949f1cb28b..2d1694a058672eee857b8b98e6f506d703d0493e 100644
--- a/src/info.c
+++ b/src/info.c
@@ -2530,15 +2530,15 @@ state_t states[NUMSTATES] =
 	{SPR_DRWN, 10, 40, {NULL}, 0, 0, S_NULL}, // S_FOUR2
 	{SPR_DRWN, 11, 40, {NULL}, 0, 0, S_NULL}, // S_FIVE2
 
-	{SPR_LCKN, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON
+	{SPR_LCKN,   FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON1
+	{SPR_LCKN, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_LOCKON2
 
 	{SPR_TTAG, FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_TTAG
 
 	// CTF Sign
-	{SPR_GFLG, 0, 1, {NULL}, 0, 0, S_GOTREDFLAG2}, // S_GOTREDFLAG1
-	{SPR_GFLG, 1, 1, {NULL}, 0, 0, S_NULL},     // S_GOTREDFLAG2
-	{SPR_GFLG, 0, 1, {NULL}, 0, 0, S_GOTBLUEFLAG2}, // S_GOTBLUEFLAG1
-	{SPR_GFLG, 2, 1, {NULL}, 0, 0, S_NULL},     // S_GOTBLUEFLAG2
+	{SPR_GFLG,   FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_GOTFLAG
+	{SPR_GFLG, 1|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_GOTREDFLAG
+	{SPR_GFLG, 2|FF_FULLBRIGHT, 2, {NULL}, 0, 0, S_NULL}, // S_GOTBLUEFLAG
 
 	{SPR_CORK, 0, -1, {NULL}, 0, 0, S_NULL}, // S_CORK
 
@@ -12068,7 +12068,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 
 	{           // MT_LOCKON
 		-1,             // doomednum
-		S_LOCKON,       // spawnstate
+		S_LOCKON1,       // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12089,7 +12089,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		16,             // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
+		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOCLIPHEIGHT|MF_NOGRAVITY|MF_SCENERY, // flags
 		S_NULL          // raisestate
 	},
 
@@ -12120,36 +12120,9 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL          // raisestate
 	},
 
-	{           // MT_GOTREDFLAG
-		-1,             // doomednum
-		S_GOTREDFLAG1,  // spawnstate
-		1000,           // spawnhealth
-		S_NULL,         // seestate
-		sfx_None,       // seesound
-		8,              // reactiontime
-		sfx_None,       // attacksound
-		S_NULL,         // painstate
-		0,              // painchance
-		sfx_None,       // painsound
-		S_NULL,         // meleestate
-		S_NULL,         // missilestate
-		S_NULL,         // deathstate
-		S_NULL,         // xdeathstate
-		sfx_None,       // deathsound
-		8,              // speed
-		64*FRACUNIT,    // radius
-		32*FRACUNIT,    // height
-		111,            // display offset
-		16,             // mass
-		0,              // damage
-		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOCLIP|MF_NOGRAVITY|MF_SCENERY, // flags
-		S_NULL          // raisestate
-	},
-
-	{           // MT_GOTBLUEFLAG2
+	{           // MT_GOTFLAG
 		-1,             // doomednum
-		S_GOTBLUEFLAG1, // spawnstate
+		S_GOTFLAG,      // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
 		sfx_None,       // seesound
@@ -12457,7 +12430,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_CORK,         // spawnstate
 		1000,           // spawnhealth
 		S_NULL,         // seestate
-		sfx_itemup,     // seesound
+		sfx_corkp,      // seesound
 		0,              // reactiontime
 		sfx_None,       // attacksound
 		S_NULL,         // painstate
@@ -12467,7 +12440,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		S_NULL,         // missilestate
 		S_SMOKE1,       // deathstate
 		S_NULL,         // xdeathstate
-		sfx_itemup,     // deathsound
+		sfx_corkh,      // deathsound
 		60*FRACUNIT,    // speed
 		16*FRACUNIT,    // radius
 		16*FRACUNIT,    // height
diff --git a/src/info.h b/src/info.h
index 4501357b768475e0e8691278659d60c0ef8f8b00..4b21e98ec43e234283dfbe441d399257063df869 100644
--- a/src/info.h
+++ b/src/info.h
@@ -2720,16 +2720,16 @@ typedef enum state
 	S_FOUR2,
 	S_FIVE2,
 
-	S_LOCKON,
+	S_LOCKON1,
+	S_LOCKON2,
 
 	// Tag Sign
 	S_TTAG,
 
 	// Got Flag Sign
-	S_GOTREDFLAG1,
-	S_GOTREDFLAG2,
-	S_GOTBLUEFLAG1,
-	S_GOTBLUEFLAG2,
+	S_GOTFLAG,
+	S_GOTREDFLAG,
+	S_GOTBLUEFLAG,
 
 	S_CORK,
 
@@ -3543,8 +3543,7 @@ typedef enum mobj_type
 	MT_GOTEMERALD, // Chaos Emerald (intangible)
 	MT_LOCKON, // Target
 	MT_TAG, // Tag Sign
-	MT_GOTREDFLAG, // Got Flag sign
-	MT_GOTBLUEFLAG, // Got Flag sign
+	MT_GOTFLAG, // Got Flag sign
 
 	// Ambient Sounds
 	MT_AWATERA, // Ambient Water Sound 1
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index 53fa9bc55d89d52b4706009d879a6e32e434529a..ef8e025b89ef565d736d64ce01eb835197bc5ae8 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -449,6 +449,28 @@ static int lib_pIsValidSprite2(lua_State *L)
 	return 1;
 }
 
+// P_SpawnLockOn doesn't exist either, but we want to expose making a local mobj without encouraging hacks.
+
+static int lib_pSpawnLockOn(lua_State *L)
+{
+	player_t *player = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+	mobj_t *lockon = *((mobj_t **)luaL_checkudata(L, 2, META_MOBJ));
+	statenum_t state = luaL_checkinteger(L, 3);
+	NOHUD
+	INLEVEL
+	if (!lockon)
+		return LUA_ErrInvalid(L, "mobj_t");
+	if (!player)
+		return LUA_ErrInvalid(L, "player_t");
+	if (player == &players[consoleplayer] || player == &players[secondarydisplayplayer] || player == &players[displayplayer]) // Only display it on your own view.
+	{
+		mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
+		visual->target = lockon;
+		P_SetMobjStateNF(visual, state);
+	}
+	return 0;
+}
+
 static int lib_pSpawnMissile(lua_State *L)
 {
 	mobj_t *source = *((mobj_t **)luaL_checkudata(L, 1, META_MOBJ));
@@ -2328,6 +2350,7 @@ static luaL_Reg lib[] = {
 	{"P_SpawnMobj",lib_pSpawnMobj},
 	{"P_RemoveMobj",lib_pRemoveMobj},
 	{"P_IsValidSprite2", lib_pIsValidSprite2},
+	{"P_SpawnLockOn", lib_pSpawnLockOn},
 	{"P_SpawnMissile",lib_pSpawnMissile},
 	{"P_SpawnXYZMissile",lib_pSpawnXYZMissile},
 	{"P_SpawnPointMissile",lib_pSpawnPointMissile},
diff --git a/src/p_floor.c b/src/p_floor.c
index 071ebcb86abd8668ed8932e9b416814bd79a8907..d16c8b9ffe76bdf55d625a8fc956651f9c7d7aad 100644
--- a/src/p_floor.c
+++ b/src/p_floor.c
@@ -1778,8 +1778,7 @@ static mobj_t *SearchMarioNode(msecnode_t *node)
 		case MT_GOTEMERALD:
 		case MT_LOCKON:
 		case MT_TAG:
-		case MT_GOTREDFLAG:
-		case MT_GOTBLUEFLAG:
+		case MT_GOTFLAG:
 		case MT_HOOP:
 		case MT_HOOPCOLLIDE:
 		case MT_NIGHTSCORE:
diff --git a/src/p_inter.c b/src/p_inter.c
index 36e800bf0239cc3c74e14c1cd9d3f82755a0059b..f5255a2f7d1d74ebbdead3d6100dc4474a9bcffd 100644
--- a/src/p_inter.c
+++ b/src/p_inter.c
@@ -1348,6 +1348,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 					player->pflags &= ~(PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE);
 					P_SetPlayerMobjState(toucher, S_PLAY_FALL);
 				}
+				player->homing = 0;
 
 				// Play a bounce sound?
 				S_StartSound(toucher, special->info->painsound);
@@ -1408,6 +1409,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
 						player->pflags &= ~(PF_GLIDING|PF_JUMPED|PF_NOJUMPDAMAGE);
 						P_SetPlayerMobjState(toucher, S_PLAY_FALL);
 					}
+					player->homing = 0;
 
 					// Play a bounce sound?
 					S_StartSound(toucher, special->info->painsound);
diff --git a/src/p_mobj.c b/src/p_mobj.c
index a235af5b3464c6c80e6687b322f5004ee724a491..2fde531d15e1dadfbc91a631fe6755c73d46f855 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -6868,7 +6868,7 @@ void P_MobjThinker(mobj_t *mobj)
 					return;
 				if (/*(mobj->target) -- the following is implicit by P_AddShield
 				&& (mobj->target->player)
-				&& */ (mobj->target->player->homing))
+				&& */ (mobj->target->player->homing) && (mobj->target->player->pflags & PF_SHIELDABILITY))
 				{
 					P_SetMobjState(mobj, mobj->info->painstate);
 					mobj->tics++;
@@ -7012,13 +7012,15 @@ void P_MobjThinker(mobj_t *mobj)
 				mobj->x = mobj->target->x;
 				mobj->y = mobj->target->y;
 
+				mobj->eflags |= (mobj->target->eflags & MFE_VERTICALFLIP);
+
 				mobj->destscale = mobj->target->destscale;
 				P_SetScale(mobj, mobj->target->scale);
 
-				if (!(mobj->target->eflags & MFE_VERTICALFLIP))
-					mobj->z = mobj->target->z + mobj->target->height + FixedMul(16*FRACUNIT, mobj->target->scale);
+				if (!(mobj->eflags & MFE_VERTICALFLIP))
+					mobj->z = mobj->target->z + mobj->target->height + FixedMul((16 + abs((leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->target->scale);
 				else
-					mobj->z = mobj->target->z - FixedMul(16*FRACUNIT, mobj->target->scale) - mobj->height;
+					mobj->z = mobj->target->z - FixedMul((16 + abs((leveltime % TICRATE) - TICRATE/2))*FRACUNIT, mobj->target->scale) - mobj->height;
 				break;
 			case MT_DROWNNUMBERS:
 				if (!mobj->target)
diff --git a/src/p_user.c b/src/p_user.c
index 80906e1a4967b0a5c6bae6980450f3ff1f4af723..f60edf79d1cfefac57bcdacc4e8e0ba90a33a25b 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -2349,12 +2349,17 @@ static void P_DoPlayerHeadSigns(player_t *player)
 		// has it (but not on your own screen if you have the flag).
 		if (splitscreen || player != &players[consoleplayer])
 		{
-			if (!(player->mo->eflags & MFE_VERTICALFLIP))
-				P_SpawnMobj(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy,
-					player->mo->z+P_GetPlayerHeight(player)+FixedMul(16*FRACUNIT, player->mo->scale)+player->mo->momz, ((player->gotflag & GF_REDFLAG) ? MT_GOTREDFLAG : MT_GOTBLUEFLAG));
+			mobj_t *sign = P_SpawnMobj(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy,
+					player->mo->z+player->mo->momz, MT_GOTFLAG);
+			if (player->mo->eflags & MFE_VERTICALFLIP)
+			{
+				sign->z += player->mo->height-P_GetPlayerHeight(player)-mobjinfo[MT_GOTFLAG].height-FixedMul(16*FRACUNIT, player->mo->scale);
+				sign->eflags |= MFE_VERTICALFLIP;
+			}
 			else
-				P_SpawnMobj(player->mo->x+player->mo->momx, player->mo->y+player->mo->momy,
-					player->mo->z+player->mo->height-P_GetPlayerHeight(player)-mobjinfo[MT_GOTREDFLAG].height-FixedMul(16*FRACUNIT, player->mo->scale)+player->mo->momz, ((player->gotflag & GF_REDFLAG) ? MT_GOTREDFLAG : MT_GOTBLUEFLAG))->eflags |= MFE_VERTICALFLIP; // yes, MT_GOTREDFLAG's height is used for both of them. Doesn't really matter - they should both always be the same height.
+				sign->z += P_GetPlayerHeight(player)+FixedMul(16*FRACUNIT, player->mo->scale);
+			if (leveltime & 1)
+				P_SetMobjStateNF(sign, (player->gotflag & GF_REDFLAG) ? S_GOTREDFLAG : S_GOTBLUEFLAG);
 		}
 	}
 }
@@ -3843,8 +3848,7 @@ static void P_DoSpinAbility(player_t *player, ticcmd_t *cmd)
 						{
 							if (player == &players[consoleplayer] || player == &players[secondarydisplayplayer] || player == &players[displayplayer]) // Only display it on your own view.
 							{
-								mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON);
-								visual->eflags |= (lockon->eflags & MFE_VERTICALFLIP);
+								mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
 								visual->target = lockon;
 							}
 						}
@@ -4102,9 +4106,20 @@ void P_Telekinesis(player_t *player, fixed_t thrust, fixed_t range)
 //
 static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 {
+	mobj_t *lockon = NULL;
+
 	if (player->pflags & PF_JUMPSTASIS)
 		return;
 
+	if ((player->charability == CA_HOMINGTHOK) && !player->homing && (player->pflags & PF_JUMPED) && (!(player->pflags & PF_THOKKED) || (player->charability2 == CA2_MULTIABILITY)) && (lockon = P_LookForEnemies(player, true, false)) && !((leveltime & 1) && (lockon->flags & (MF_ENEMY|MF_BOSS)) && player->powers[pw_shield] == SH_ATTRACT))
+	{
+		if (player == &players[consoleplayer] || player == &players[secondarydisplayplayer] || player == &players[displayplayer]) // Only display it on your own view.
+		{
+			mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
+			visual->target = lockon;
+		}
+	}
+
 	if (cmd->buttons & BT_USE && !(player->pflags & PF_JUMPDOWN) && !player->exiting && !P_PlayerInPain(player))
 	{
 		if (player->mo->tracer && player->powers[pw_carry] == CR_MACESPIN)
@@ -4255,9 +4270,8 @@ static void P_DoJumpStuff(player_t *player, ticcmd_t *cmd)
 
 						P_SpawnThokMobj(player);
 
-						if (player->charability == CA_HOMINGTHOK && !player->homing)
+						if (player->charability == CA_HOMINGTHOK)
 						{
-							mobj_t *lockon = P_LookForEnemies(player, true, false);
 							if (lockon)
 							{
 								P_SetTarget(&player->mo->target, P_SetTarget(&player->mo->tracer, lockon));
@@ -7161,6 +7175,19 @@ static void P_MovePlayer(player_t *player)
 
 	if (player->pflags & PF_JUMPED && !player->exiting && player->mo->health)
 	{
+		mobj_t *lockon = NULL;
+		if (!player->powers[pw_super] && player->powers[pw_shield] == SH_ATTRACT && !(player->pflags & PF_THOKKED))
+		{
+			if ((lockon = P_LookForEnemies(player, false, false)) && !(!(leveltime & 1) && player->charability == CA_HOMINGTHOK))
+			{
+				if (player == &players[consoleplayer] || player == &players[secondarydisplayplayer] || player == &players[displayplayer]) // Only display it on your own view.
+				{
+					mobj_t *visual = P_SpawnMobj(lockon->x, lockon->y, lockon->z, MT_LOCKON); // positioning, flip handled in P_SceneryThinker
+					visual->target = lockon;
+					P_SetMobjStateNF(visual, visual->info->spawnstate+1);
+				}
+			}
+		}
 		if (cmd->buttons & BT_USE) // Spin button effects
 		{
 			if (player->powers[pw_super]) // Super can't use shield actives, only passives
@@ -7214,20 +7241,16 @@ static void P_MovePlayer(player_t *player)
 							case SH_ATTRACT:
 								player->pflags |= PF_THOKKED|PF_SHIELDABILITY;
 								player->homing = 2;
+								if (lockon)
 								{
-									mobj_t *lockon = P_LookForEnemies(player, false, false);
-									if (lockon)
-									{
-										P_SetTarget(&player->mo->target, P_SetTarget(&player->mo->tracer, lockon));
-										player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockon->x, lockon->y);
-										player->pflags &= ~PF_NOJUMPDAMAGE;
-										P_SetPlayerMobjState(player->mo, S_PLAY_ROLL);
-										S_StartSound(player->mo, sfx_s3k40);
-										player->homing = 3*TICRATE;
-									}
-									else
-										S_StartSound(player->mo, sfx_s3ka6);
+									P_SetTarget(&player->mo->target, P_SetTarget(&player->mo->tracer, lockon));
+									player->mo->angle = R_PointToAngle2(player->mo->x, player->mo->y, lockon->x, lockon->y);
+									player->pflags &= ~PF_NOJUMPDAMAGE;
+									S_StartSound(player->mo, sfx_s3k40);
+									player->homing = 3*TICRATE;
 								}
+								else
+									S_StartSound(player->mo, sfx_s3ka6);
 								break;
 							// Elemental/Bubblewrap shield activation
 							case SH_ELEMENTAL:
@@ -7886,7 +7909,8 @@ mobj_t *P_LookForEnemies(player_t *player, boolean nonenemies, boolean bullet)
 
 		mo = (mobj_t *)think;
 		if (!(mo->flags & targetmask
-		|| mo->type == MT_FAKEMOBILE)) // hehehehe
+		|| mo->type == MT_FAKEMOBILE // hehehehe
+		|| mo->type == MT_EGGSHIELD))
 			continue; // not a valid target
 
 		if (mo->health <= 0) // dead
@@ -7974,7 +7998,7 @@ void P_HomingAttack(mobj_t *source, mobj_t *enemy) // Home in on your target
 	}
 
 	// change slope
-	zdist = (P_MobjFlip(source) ? (enemy->z + enemy->height) - (source->z + source->height) : (enemy->z - source->z));
+	zdist = ((P_MobjFlip(source) == -1) ? (enemy->z + enemy->height) - (source->z + source->height) : (enemy->z - source->z));
 	dist = P_AproxDistance(P_AproxDistance(enemy->x - source->x, enemy->y - source->y), zdist);
 
 	if (dist < 1)
diff --git a/src/sounds.c b/src/sounds.c
index b2758923de73a6bea96ec8d8f35b6e14670a6dd9..395015296001e17d7bc528ea49bfb249c83157d0 100644
--- a/src/sounds.c
+++ b/src/sounds.c
@@ -189,6 +189,8 @@ sfxinfo_t S_sfx[NUMSFX] =
   {"mswarp", false,  60, 16, -1, NULL, 0,        -1,  -1, LUMPERROR},
   {"mspogo", false,  60,  8, -1, NULL, 0,        -1,  -1, LUMPERROR},
   {"boingf", false,  60,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
+  {"corkp",  false,  64,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
+  {"corkh",  false,  32,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
 
   // Menu, interface
   {"chchng", false, 120,  0, -1, NULL, 0,        -1,  -1, LUMPERROR},
diff --git a/src/sounds.h b/src/sounds.h
index 42eeee31f628895c6ffc6f181fa04b6ddc0ec3a2..0442ebb055cb57e9a257bc1aa47e8dfb16bfeed8 100644
--- a/src/sounds.h
+++ b/src/sounds.h
@@ -252,6 +252,8 @@ typedef enum
 	sfx_mswarp,
 	sfx_mspogo,
 	sfx_boingf,
+	sfx_corkp,
+	sfx_corkh,
 
 	// Menu, interface
 	sfx_chchng,