diff --git a/src/b_bot.c b/src/b_bot.c
index cdd74fc0757522ac2a7c30dfe3dec50237c446ea..bf2dbbb68586aab8390557568622d2e9e6f89e0a 100644
--- a/src/b_bot.c
+++ b/src/b_bot.c
@@ -29,11 +29,16 @@ void B_UpdateBotleader(player_t *player)
 	{
 		if (players[i].bot || players[i].playerstate != PST_LIVE || players[i].spectator || !players[i].mo)
 			continue;
-		if (!player->mo) //Can't do distance calculations if there's no player object, so we'll just take the first we find
+		
+		if (!player->botleader)
 		{
-			player->botleader = &players[i];
+			player->botleader = &players[i]; // set default
 			return;
 		}
+
+		if (!player->mo)
+			return;
+
 		//Update best candidate based on nearest distance
 		dist = R_PointToDist2(player->mo->x, player->mo->y, players[i].mo->x, players[i].mo->y);
 		if (neardist > dist)
diff --git a/src/d_netcmd.c b/src/d_netcmd.c
index cca3102d085aac427b5a3469dd00f3085a3fee3d..fe7e7678fe447cfc186b9eaf4134cb6eeeb6db27 100644
--- a/src/d_netcmd.c
+++ b/src/d_netcmd.c
@@ -3763,7 +3763,7 @@ static void Command_ListWADS_f(void)
 		nameonly(tempname = va("%s", wadfiles[i]->filename));
 		if (!i)
 			CONS_Printf("\x82 IWAD\x80: %s\n", tempname);
-		else if (i <= mainwads)
+		else if (i < mainwads)
 			CONS_Printf("\x82 * %.2d\x80: %s\n", i, tempname);
 		else if (!wadfiles[i]->important)
 			CONS_Printf("\x86   %.2d: %s\n", i, tempname);
diff --git a/src/deh_tables.c b/src/deh_tables.c
index e07d44453b24a1f7315c32dd33f10d916dd445fa..cfc98f631e9d475f403e104425eae189949157bf 100644
--- a/src/deh_tables.c
+++ b/src/deh_tables.c
@@ -5099,6 +5099,7 @@ struct int_const_s const INT_CONST[] = {
 	{"PAL_MIXUP",PAL_MIXUP},
 	{"PAL_RECYCLE",PAL_RECYCLE},
 	{"PAL_NUKE",PAL_NUKE},
+	{"PAL_INVERT",PAL_INVERT},
 	// for P_DamageMobj
 	//// Damage types
 	{"DMG_WATER",DMG_WATER},
diff --git a/src/g_game.c b/src/g_game.c
index 3955834b2170fa203222a5c76f8f9ead60e74fa6..e671eb2d757196466abd0edfe14a8d4b363061f8 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -3300,7 +3300,7 @@ boolean G_EnoughPlayersFinished(void)
 
 	for (i = 0; i < MAXPLAYERS; i++)
 	{
-		if (!playeringame[i] || players[i].spectator || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN)
+		if (!playeringame[i] || players[i].spectator || players[i].bot)
 			continue;
 		if (players[i].quittime > 30 * TICRATE)
 			continue;
diff --git a/src/hu_stuff.c b/src/hu_stuff.c
index 12972458581e5d3162a02c586b3e65018308f1ea..cf7118fbe0694c206a2d26497196f2cc81d5787f 100644
--- a/src/hu_stuff.c
+++ b/src/hu_stuff.c
@@ -2476,7 +2476,7 @@ static void HU_Draw32TeamTabRankings(playersort_t *tab, INT32 whiteplayer)
 		if (!players[tab[i].num].quittime || (leveltime / (TICRATE/2) & 1))
 			V_DrawString(x + 10, y,
 			             ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
-			             | (greycheck ? 0 : V_TRANSLUCENT)
+			             | (greycheck ? V_TRANSLUCENT : 0)
 			             | V_ALLOWLOWERCASE, name);
 
 		if (gametyperules & GTR_TEAMFLAGS)
@@ -2737,12 +2737,12 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
 			if (circuitmap)
 			{
 				if (players[tab[i].num].exiting)
-					V_DrawRightAlignedThinString(x+146, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime)));
+					V_DrawRightAlignedThinString(x+100, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime)));
 				else
-					V_DrawRightAlignedThinString(x+146, y, (greycheck ? V_TRANSLUCENT : 0), va("%u", tab[i].count));
+					V_DrawRightAlignedThinString(x+100, y, (greycheck ? V_TRANSLUCENT : 0), va("%u", tab[i].count));
 			}
 			else
-				V_DrawRightAlignedThinString(x+146, y, (greycheck ? V_TRANSLUCENT : 0), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
+				V_DrawRightAlignedThinString(x+100, y, (greycheck ? V_TRANSLUCENT : 0), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
 		}
 		else
 			V_DrawRightAlignedThinString(x+100, y, (greycheck ? V_TRANSLUCENT : 0), va("%u", tab[i].count));
@@ -2790,7 +2790,7 @@ static void HU_Draw32TabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scor
 		if (!players[tab[i].num].quittime || (leveltime / (TICRATE/2) & 1))
 			V_DrawString(x + 10, y,
 			             ((tab[i].num == whiteplayer) ? V_YELLOWMAP : 0)
-			             | (greycheck ? 0 : V_TRANSLUCENT)
+			             | (greycheck ? V_TRANSLUCENT : 0)
 			             | V_ALLOWLOWERCASE, name);
 
 		if (G_GametypeUsesLives()) //show lives
@@ -2850,13 +2850,13 @@ static void HU_Draw32TabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scor
 				if (players[tab[i].num].exiting)
 					V_DrawRightAlignedThinString(x+128, y, 0, va("%i:%02i.%02i", G_TicsToMinutes(players[tab[i].num].realtime,true), G_TicsToSeconds(players[tab[i].num].realtime), G_TicsToCentiseconds(players[tab[i].num].realtime)));
 				else
-					V_DrawRightAlignedThinString(x+128, y, (greycheck ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
+					V_DrawRightAlignedThinString(x+128, y, (greycheck ? V_TRANSLUCENT : 0), va("%u", tab[i].count));
 			}
 			else
-				V_DrawRightAlignedThinString(x+128, y, (greycheck ? 0 : V_TRANSLUCENT), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
+				V_DrawRightAlignedThinString(x+128, y, (greycheck ? V_TRANSLUCENT : 0), va("%i:%02i.%02i", G_TicsToMinutes(tab[i].count,true), G_TicsToSeconds(tab[i].count), G_TicsToCentiseconds(tab[i].count)));
 		}
 		else
-			V_DrawRightAlignedThinString(x+128, y, (greycheck ? 0 : V_TRANSLUCENT), va("%u", tab[i].count));
+			V_DrawRightAlignedThinString(x+128, y, (greycheck ? V_TRANSLUCENT : 0), va("%u", tab[i].count));
 
 		y += 9;
 		if (i == 16)
@@ -3095,7 +3095,7 @@ static void HU_DrawRankings(void)
 		HU_DrawTeamTabRankings(tab, whiteplayer);
 	else if (scorelines <= 9 && !cv_compactscoreboard.value)
 		HU_DrawTabRankings(40, 32, tab, scorelines, whiteplayer);
-	else if (scorelines <= 20 && !cv_compactscoreboard.value)
+	else if (scorelines <= 18 && !cv_compactscoreboard.value)
 		HU_DrawDualTabRankings(32, 32, tab, scorelines, whiteplayer);
 	else
 		HU_Draw32TabRankings(14, 28, tab, scorelines, whiteplayer);
diff --git a/src/info.c b/src/info.c
index f56e5d78e3e786b33806a1a596f0051a182144fd..331e114b6381cf2161fb83d121ec2b77c9b7853b 100644
--- a/src/info.c
+++ b/src/info.c
@@ -5198,11 +5198,11 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		0,              // speed
 		24*FRACUNIT,    // radius
 		34*FRACUNIT,    // height
-		0,              // display offset
+		1,              // display offset
 		DMG_FIRE,       // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOGRAVITY|MF_NOBLOCKMAP|MF_FIRE|MF_PAIN, // flags
+		MF_NOGRAVITY|MF_NOBLOCKMAP|MF_NOCLIPHEIGHT|MF_FIRE|MF_PAIN, // flags
 		S_NULL          // raisestate
 	},
 
@@ -7977,7 +7977,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		DMG_SPIKE,      // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_SCENERY|MF_NOCLIPHEIGHT,  // flags
+		MF_SOLID|MF_SCENERY,  // flags
 		S_NULL          // raisestate
 	},
 
@@ -8004,7 +8004,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		DMG_SPIKE,      // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SCENERY|MF_NOCLIPHEIGHT|MF_PAPERCOLLISION,  // flags
+		MF_SOLID|MF_NOGRAVITY|MF_SCENERY|MF_PAPERCOLLISION,  // flags
 		S_NULL          // raisestate
 	},
 
@@ -8031,7 +8031,7 @@ mobjinfo_t mobjinfo[NUMMOBJTYPES] =
 		4,              // mass
 		0,              // damage
 		sfx_None,       // activesound
-		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIP|MF_NOCLIPTHING,  // flags
+		MF_NOBLOCKMAP|MF_NOGRAVITY|MF_SCENERY|MF_NOCLIP|MF_NOCLIPTHING,  // flags
 		S_NULL          // raisestate
 	},
 
diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c
index a72b22b5a62b953bbccde13f271c76fb6e24cad1..287a185bc36b06b5d04cc18d5667b9e869047452 100644
--- a/src/lua_hooklib.c
+++ b/src/lua_hooklib.c
@@ -1117,7 +1117,7 @@ int LUA_HookMusicChange(const char *oldname, struct MusicChange *param)
 		lua_pushstring(gL, oldname);/* the only constant value */
 		lua_pushstring(gL, param->newname);/* semi constant */
 
-		for (k = 0; k <= map->numHooks; ++k)
+		for (k = 0; k < map->numHooks; ++k)
 		{
 			get_hook(&hook, map->ids, k);
 
diff --git a/src/lua_mathlib.c b/src/lua_mathlib.c
index e6f8c98c1371e81295a64d8d14a4039239bc4522..bd9218a3d54bc076093c8596ae502c0075464602 100644
--- a/src/lua_mathlib.c
+++ b/src/lua_mathlib.c
@@ -88,6 +88,12 @@ static int lib_finetangent(lua_State *L)
 	return 1;
 }
 
+static int lib_fixedasin(lua_State *L)
+{
+	lua_pushangle(L, -FixedAcos(luaL_checkfixed(L, 1)) + ANGLE_90);
+	return 1;
+}
+
 static int lib_fixedacos(lua_State *L)
 {
 	lua_pushangle(L, FixedAcos(luaL_checkfixed(L, 1)));
@@ -199,6 +205,7 @@ static luaL_Reg lib_math[] = {
 	{"sin", lib_finesine},
 	{"cos", lib_finecosine},
 	{"tan", lib_finetangent},
+	{"asin", lib_fixedasin},
 	{"acos", lib_fixedacos},
 	{"FixedAngle", lib_fixedangle},
 	{"fixangle"  , lib_fixedangle},
diff --git a/src/m_menu.c b/src/m_menu.c
index 30772ab8fdc69ae81519d5c854ce1777f1305052..85d093b943e22a50add4bb3aa111b72fc7a8ef1f 100644
--- a/src/m_menu.c
+++ b/src/m_menu.c
@@ -11588,9 +11588,7 @@ static void M_ServerOptions(INT32 choice)
 		OP_ServerOptionsMenu[ 2].status = IT_STRING | IT_CVAR;
 		OP_ServerOptionsMenu[ 3].status = IT_STRING | IT_CVAR;
 		OP_ServerOptionsMenu[ 4].status = IT_STRING | IT_CVAR;
-		OP_ServerOptionsMenu[36].status = (netgame
-			? IT_GRAYEDOUT
-			: (IT_STRING | IT_CVAR | IT_CV_STRING));
+		OP_ServerOptionsMenu[36].status = IT_STRING | IT_CVAR | IT_CV_STRING;
 		OP_ServerOptionsMenu[37].status = IT_STRING | IT_CVAR;
 		OP_ServerOptionsMenu[38].status = IT_STRING | IT_CVAR;
 	}
diff --git a/src/p_floor.c b/src/p_floor.c
index 263644f702bb7a8a26f66c6989b57deb2b225ccc..dd9331e73ffdc671252d10d4fbf0b196dc38aa37 100644
--- a/src/p_floor.c
+++ b/src/p_floor.c
@@ -1516,8 +1516,8 @@ void T_EachTimeThinker(eachtime_t *eachtime)
 	{
 		for (i = 0; i < MAXPLAYERS; i++)
 		{
-			if (P_IsPlayerValid(i) && playersArea[i])
-				continue;
+			if (P_IsPlayerValid(i) && !playersArea[i])
+				return;
 		}
 	}
 
diff --git a/src/p_local.h b/src/p_local.h
index 1fcd3050d92fde6fd1056f86d81137be89b93902..28a77afe5c9a5022a5e5f92641df34806f32b91d 100644
--- a/src/p_local.h
+++ b/src/p_local.h
@@ -349,6 +349,7 @@ void P_FlashPal(player_t *pl, UINT16 type, UINT16 duration);
 #define PAL_MIXUP    2
 #define PAL_RECYCLE  3
 #define PAL_NUKE     4
+#define PAL_INVERT   5
 
 //
 // P_ENEMY
diff --git a/src/p_map.c b/src/p_map.c
index 836e75c4e8bb08937e79fadf5f572125c9841b90..329224d0bd353c8a345f5483ffd5142695b9f0e3 100644
--- a/src/p_map.c
+++ b/src/p_map.c
@@ -1152,9 +1152,9 @@ static boolean PIT_CheckThing(mobj_t *thing)
 			return true; // underneath
 
 		if (tmthing->eflags & MFE_VERTICALFLIP)
-			thing->z = tmthing->z - thing->height - FixedMul(FRACUNIT, tmthing->scale);
+			P_TeleportMove(thing, thing->x, thing->y, tmthing->z - thing->height - FixedMul(FRACUNIT, tmthing->scale));
 		else
-			thing->z = tmthing->z + tmthing->height + FixedMul(FRACUNIT, tmthing->scale);
+			P_TeleportMove(thing, thing->x, thing->y, tmthing->z + tmthing->height + FixedMul(FRACUNIT, tmthing->scale));
 		if (thing->flags & MF_SHOOTABLE)
 			P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE);
 		return true;
@@ -1465,86 +1465,6 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		return true;
 	}
 
-	// Sprite Spikes!
-	// Do not return because solidity code comes below.
-	if (tmthing->type == MT_SPIKE && tmthing->flags & MF_SOLID && thing->player) // moving spike rams into player?!
-	{
-		if (tmthing->eflags & MFE_VERTICALFLIP)
-		{
-			if (thing->z + thing->height <= tmthing->z + FixedMul(FRACUNIT, tmthing->scale)
-			&& thing->z + thing->height + thing->momz  >= tmthing->z + FixedMul(FRACUNIT, tmthing->scale) + tmthing->momz
-			&& !(thing->player->charability == CA_BOUNCE && thing->player->panim == PA_ABILITY && thing->eflags & MFE_VERTICALFLIP))
-				P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE);
-		}
-		else if (thing->z >= tmthing->z + tmthing->height - FixedMul(FRACUNIT, tmthing->scale)
-		&& thing->z + thing->momz <= tmthing->z + tmthing->height - FixedMul(FRACUNIT, tmthing->scale) + tmthing->momz
-		&& !(thing->player->charability == CA_BOUNCE && thing->player->panim == PA_ABILITY && !(thing->eflags & MFE_VERTICALFLIP)))
-			P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE);
-	}
-	else if (thing->type == MT_SPIKE && thing->flags & MF_SOLID && tmthing->player) // unfortunate player falls into spike?!
-	{
-		if (thing->eflags & MFE_VERTICALFLIP)
-		{
-			if (tmthing->z + tmthing->height <= thing->z - FixedMul(FRACUNIT, thing->scale)
-			&& tmthing->z + tmthing->height + tmthing->momz >= thing->z - FixedMul(FRACUNIT, thing->scale)
-			&& !(tmthing->player->charability == CA_BOUNCE && tmthing->player->panim == PA_ABILITY && tmthing->eflags & MFE_VERTICALFLIP))
-				P_DamageMobj(tmthing, thing, thing, 1, DMG_SPIKE);
-		}
-		else if (tmthing->z >= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale)
-		&& tmthing->z + tmthing->momz <= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale)
-		&& !(tmthing->player->charability == CA_BOUNCE && tmthing->player->panim == PA_ABILITY && !(tmthing->eflags & MFE_VERTICALFLIP)))
-			P_DamageMobj(tmthing, thing, thing, 1, DMG_SPIKE);
-	}
-
-	if (tmthing->type == MT_WALLSPIKE && tmthing->flags & MF_SOLID && thing->player) // wall spike impales player
-	{
-		fixed_t bottomz, topz;
-		bottomz = tmthing->z;
-		topz = tmthing->z + tmthing->height;
-		if (tmthing->eflags & MFE_VERTICALFLIP)
-			bottomz -= FixedMul(FRACUNIT, tmthing->scale);
-		else
-			topz += FixedMul(FRACUNIT, tmthing->scale);
-
-		if (thing->z + thing->height > bottomz // above bottom
-		&&  thing->z < topz) // below top
-		// don't check angle, the player was clearly in the way in this case
-			P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE);
-	}
-	else if (thing->type == MT_WALLSPIKE && thing->flags & MF_SOLID && tmthing->player)
-	{
-		fixed_t bottomz, topz;
-		angle_t touchangle = R_PointToAngle2(thing->tracer->x, thing->tracer->y, tmthing->x, tmthing->y);
-
-		if (P_PlayerInPain(tmthing->player) && (tmthing->momx || tmthing->momy))
-		{
-			angle_t playerangle = R_PointToAngle2(0, 0, tmthing->momx, tmthing->momy) - touchangle;
-			if (playerangle > ANGLE_180)
-				playerangle = InvAngle(playerangle);
-			if (playerangle < ANGLE_90)
-				return true; // Yes, this is intentionally outside the z-height check. No standing on spikes whilst moving away from them.
-		}
-
-		bottomz = thing->z;
-		topz = thing->z + thing->height;
-
-		if (thing->eflags & MFE_VERTICALFLIP)
-			bottomz -= FixedMul(FRACUNIT, thing->scale);
-		else
-			topz += FixedMul(FRACUNIT, thing->scale);
-
-		if (tmthing->z + tmthing->height > bottomz // above bottom
-		&&  tmthing->z < topz // below top
-		&& !P_MobjWasRemoved(thing->tracer)) // this probably wouldn't work if we didn't have a tracer
-		{ // use base as a reference point to determine what angle you touched the spike at
-			touchangle = thing->angle - touchangle;
-			if (touchangle > ANGLE_180)
-				touchangle = InvAngle(touchangle);
-			if (touchangle <= ANGLE_22h) // if you touched it at this close an angle, you get poked!
-				P_DamageMobj(tmthing, thing, thing, 1, DMG_SPIKE);
-		}
-	}
-
 	if (thing->flags & MF_PUSHABLE)
 	{
 		if (tmthing->type == MT_FAN || tmthing->type == MT_STEAM)
@@ -1623,6 +1543,22 @@ static boolean PIT_CheckThing(mobj_t *thing)
 
 	if (thing->player)
 	{
+		if (tmthing->type == MT_WALLSPIKE && (tmthing->flags & MF_SOLID)) // wall spike impales player
+		{
+			fixed_t bottomz, topz;
+			bottomz = tmthing->z;
+			topz = tmthing->z + tmthing->height;
+			if (tmthing->eflags & MFE_VERTICALFLIP)
+				bottomz -= FixedMul(FRACUNIT, tmthing->scale);
+			else
+				topz += FixedMul(FRACUNIT, tmthing->scale);
+
+			if (thing->z + thing->height > bottomz // above bottom
+			&&  thing->z < topz) // below top
+			// don't check angle, the player was clearly in the way in this case
+				P_DamageMobj(thing, tmthing, tmthing, 1, DMG_SPIKE);
+		}
+
 		// Doesn't matter what gravity player's following! Just do your stuff in YOUR direction only
 		if (tmthing->eflags & MFE_VERTICALFLIP
 		&& (tmthing->z + tmthing->height + tmthing->momz < thing->z
@@ -1657,6 +1593,55 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		if (!tmthing->health)
 			return true;
 
+		if (thing->type == MT_SPIKE && (thing->flags & MF_SOLID)) // unfortunate player falls into spike?!
+		{
+			if (thing->eflags & MFE_VERTICALFLIP)
+			{
+				if (tmthing->z + tmthing->height <= thing->z - FixedMul(FRACUNIT, thing->scale)
+				&& tmthing->z + tmthing->height + tmthing->momz >= thing->z - FixedMul(FRACUNIT, thing->scale)
+				&& !(tmthing->player->charability == CA_BOUNCE && tmthing->player->panim == PA_ABILITY && tmthing->eflags & MFE_VERTICALFLIP))
+					P_DamageMobj(tmthing, thing, thing, 1, DMG_SPIKE);
+			}
+			else if (tmthing->z >= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale)
+			&& tmthing->z + tmthing->momz <= thing->z + thing->height + FixedMul(FRACUNIT, thing->scale)
+			&& !(tmthing->player->charability == CA_BOUNCE && tmthing->player->panim == PA_ABILITY && !(tmthing->eflags & MFE_VERTICALFLIP)))
+				P_DamageMobj(tmthing, thing, thing, 1, DMG_SPIKE);
+		}
+
+		if (thing->type == MT_WALLSPIKE && (thing->flags & MF_SOLID))
+		{
+			fixed_t bottomz, topz;
+			angle_t touchangle = R_PointToAngle2(thing->tracer->x, thing->tracer->y, tmthing->x, tmthing->y);
+
+			if (P_PlayerInPain(tmthing->player) && (tmthing->momx || tmthing->momy))
+			{
+				angle_t playerangle = R_PointToAngle2(0, 0, tmthing->momx, tmthing->momy) - touchangle;
+				if (playerangle > ANGLE_180)
+					playerangle = InvAngle(playerangle);
+				if (playerangle < ANGLE_90)
+					return true; // Yes, this is intentionally outside the z-height check. No standing on spikes whilst moving away from them.
+			}
+
+			bottomz = thing->z;
+			topz = thing->z + thing->height;
+
+			if (thing->eflags & MFE_VERTICALFLIP)
+				bottomz -= FixedMul(FRACUNIT, thing->scale);
+			else
+				topz += FixedMul(FRACUNIT, thing->scale);
+
+			if (tmthing->z + tmthing->height > bottomz // above bottom
+			&&  tmthing->z < topz // below top
+			&& !P_MobjWasRemoved(thing->tracer)) // this probably wouldn't work if we didn't have a tracer
+			{ // use base as a reference point to determine what angle you touched the spike at
+				touchangle = thing->angle - touchangle;
+				if (touchangle > ANGLE_180)
+					touchangle = InvAngle(touchangle);
+				if (touchangle <= ANGLE_22h) // if you touched it at this close an angle, you get poked!
+					P_DamageMobj(tmthing, thing, thing, 1, DMG_SPIKE);
+			}
+		}
+
 		if (thing->type == MT_FAN || thing->type == MT_STEAM)
 			P_DoFanAndGasJet(thing, tmthing);
 		else if (thing->flags & MF_SPRING && tmthing->player->powers[pw_carry] != CR_MINECART)
@@ -1721,8 +1706,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
 		}
 	}
 
-	if ((tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM || tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) && (thing->player))
-		; // springs, gas jets and springs should never be able to step up onto a player
+	if ((thing->player) && (tmthing->flags & MF_SPRING || tmthing->type == MT_STEAM))
+		; // springs and gas jets should never be able to step up onto a player
 	// z checking at last
 	// Treat noclip things as non-solid!
 	else if ((thing->flags & (MF_SOLID|MF_NOCLIP)) == MF_SOLID
@@ -1730,6 +1715,9 @@ static boolean PIT_CheckThing(mobj_t *thing)
 	{
 		fixed_t topz, tmtopz;
 
+		if (tmthing->type == MT_SPIKE || tmthing->type == MT_WALLSPIKE) // do not run height checks if you are a spike
+			return true;
+
 		if (tmthing->eflags & MFE_VERTICALFLIP)
 		{
 			// pass under
@@ -2727,6 +2715,16 @@ boolean P_TryMove(mobj_t *thing, fixed_t x, fixed_t y, boolean allowdropoff)
 				&& P_MobjFlip(thing)*thing->momz > FixedMul(FRACUNIT, thing->scale))
 					maxstep = 0;
 			}
+			else if (thing->flags & MF_PUSHABLE)
+			{
+				// If using type Section1:13, double the maxstep.
+				if (GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 13)
+					maxstep <<= 1;
+
+				// If using type Section1:14, no maxstep.
+				if (GETSECSPECIAL(R_PointInSubsector(x, y)->sector->special, 1) == 14)
+					maxstep = 0;
+			}
 
 			if (thing->type == MT_SKIM)
 				maxstep = 0;
diff --git a/src/p_mobj.c b/src/p_mobj.c
index 83f9ebf3c7a5f27d18a65416900be70df38e911b..39c6731b81ab41b378f748eb42fe28762642c350 100644
--- a/src/p_mobj.c
+++ b/src/p_mobj.c
@@ -395,13 +395,13 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 			if (skin)
 			{
 				UINT16 stateframe = st->frame;
-				
+
 				// Add/Remove FF_SPR2SUPER based on certain conditions
 				if (player->charflags & SF_NOSUPERSPRITES)
 					stateframe = stateframe & ~FF_SPR2SUPER;
 				else if (player->powers[pw_super])
 					stateframe = stateframe | FF_SPR2SUPER;
-				
+
 				if (stateframe & FF_SPR2SUPER)
 				{
 					if (mobj->eflags & MFE_FORCENOSUPER)
@@ -409,11 +409,11 @@ boolean P_SetPlayerMobjState(mobj_t *mobj, statenum_t state)
 				}
 				else if (mobj->eflags & MFE_FORCESUPER)
 					stateframe = stateframe | FF_SPR2SUPER;
-					
+
 				// Get the sprite2 and frame number
 				spr2 = P_GetSkinSprite2(skin, (stateframe & FF_FRAMEMASK), mobj->player);
 				numframes = skin->sprites[spr2].numframes;
-				
+
 				if (state == S_PLAY_STND && (spr2 & FF_SPR2SUPER) && skin->sprites[SPR2_WAIT|FF_SPR2SUPER].numframes == 0)
 					mobj->tics = -1;	// If no super wait, don't wait at all
 			}
@@ -541,7 +541,7 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
 			if (skin)
 			{
 				UINT16 stateframe = st->frame;
-				
+
 				// Add/Remove FF_SPR2SUPER based on certain conditions
 				if (stateframe & FF_SPR2SUPER)
 				{
@@ -550,11 +550,11 @@ boolean P_SetMobjState(mobj_t *mobj, statenum_t state)
 				}
 				else if (mobj->eflags & MFE_FORCESUPER)
 					stateframe = stateframe | FF_SPR2SUPER;
-					
+
 				// Get the sprite2 and frame number
 				spr2 = P_GetSkinSprite2(skin, (stateframe & FF_FRAMEMASK), NULL);
 				numframes = skin->sprites[spr2].numframes;
-				
+
 				if (state == S_PLAY_STND && (spr2 & FF_SPR2SUPER) && skin->sprites[SPR2_WAIT|FF_SPR2SUPER].numframes == 0)
 					mobj->tics = -1;	// If no super wait, don't wait at all
 			}
@@ -1872,7 +1872,7 @@ void P_XYMovement(mobj_t *mo)
 		// blocked move
 		moved = false;
 
-		if (player) 
+		if (player)
 			B_MoveBlocked(player);
 
 		if (LUA_HookMobjMoveBlocked(mo, tmhitthing, blockingline))
@@ -2522,7 +2522,7 @@ boolean P_ZMovement(mobj_t *mo)
 					{
 						P_KillMobj(mo, NULL, NULL, 0);
 					}
-					return false;
+					return !P_MobjWasRemoved(mo); // allows explosion states to run
 				}
 				else
 				{
@@ -3323,7 +3323,7 @@ void P_MobjCheckWater(mobj_t *mobj)
 			{ // Water removes electric and non-water fire shields...
 			    if (electric)
 				    P_FlashPal(p, PAL_WHITE, 1);
-				
+
 				p->powers[pw_shield] = p->powers[pw_shield] & SH_STACK;
 			}
 		}
@@ -7059,6 +7059,8 @@ static void P_PyreFlyBurn(mobj_t *mobj, fixed_t hoffs, INT16 vrange, mobjtype_t
 	fixed_t zoffs = P_RandomRange(-vrange, vrange)*FRACUNIT;
 	mobj_t *particle = P_SpawnMobjFromMobj(mobj, xoffs, yoffs, zoffs, mobjtype);
 	particle->momz = momz;
+	particle->flags2 |= MF2_LINKDRAW;
+	P_SetTarget(&particle->tracer, mobj);
 }
 
 static void P_MobjScaleThink(mobj_t *mobj)
@@ -7842,6 +7844,48 @@ static void P_MobjSceneryThink(mobj_t *mobj)
 		if (P_MobjFlip(mobj)*mobj->momz < mobj->info->speed)
 			mobj->momz = P_MobjFlip(mobj)*mobj->info->speed;
 		break;
+	case MT_SPIKE:
+		if (mobj->fuse)
+		{
+			mobj->fuse--;
+			break;
+		}
+		P_SetMobjState(mobj, mobj->state->nextstate);
+		mobj->fuse = mobj->info->speed;
+		if (mobj->spawnpoint)
+			mobj->fuse += mobj->spawnpoint->angle;
+		break;
+	case MT_WALLSPIKE:
+		if (mobj->fuse)
+		{
+			mobj->fuse--;
+			break;
+		}
+		P_SetMobjState(mobj, mobj->state->nextstate);
+		mobj->fuse = mobj->info->speed;
+		if (mobj->spawnpoint)
+			mobj->fuse += (mobj->spawnpoint->angle / 360);
+		break;
+	case MT_WALLSPIKEBASE:
+		if (!mobj->target)
+		{
+			P_RemoveMobj(mobj);
+			return;
+		}
+		mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|(mobj->target->frame & FF_FRAMEMASK);
+#if 0
+		if (mobj->angle != mobj->target->angle + ANGLE_90) // reposition if not the correct angle
+		{
+			mobj_t* target = mobj->target; // shortcut
+			const fixed_t baseradius = target->radius - (target->scale/2); //FixedMul(FRACUNIT/2, target->scale);
+			P_UnsetThingPosition(mobj);
+			mobj->x = target->x - P_ReturnThrustX(target, target->angle, baseradius);
+			mobj->y = target->y - P_ReturnThrustY(target, target->angle, baseradius);
+			P_SetThingPosition(mobj);
+			mobj->angle = target->angle + ANGLE_90;
+		}
+#endif
+		break;
 	case MT_ROCKCRUMBLE1:
 	case MT_ROCKCRUMBLE2:
 	case MT_ROCKCRUMBLE3:
@@ -9238,25 +9282,6 @@ static boolean P_MobjRegularThink(mobj_t *mobj)
 
 	switch (mobj->type)
 	{
-	case MT_WALLSPIKEBASE:
-		if (!mobj->target) {
-			P_RemoveMobj(mobj);
-			return false;
-		}
-		mobj->frame = (mobj->frame & ~FF_FRAMEMASK)|(mobj->target->frame & FF_FRAMEMASK);
-#if 0
-		if (mobj->angle != mobj->target->angle + ANGLE_90) // reposition if not the correct angle
-		{
-			mobj_t* target = mobj->target; // shortcut
-			const fixed_t baseradius = target->radius - (target->scale/2); //FixedMul(FRACUNIT/2, target->scale);
-			P_UnsetThingPosition(mobj);
-			mobj->x = target->x - P_ReturnThrustX(target, target->angle, baseradius);
-			mobj->y = target->y - P_ReturnThrustY(target, target->angle, baseradius);
-			P_SetThingPosition(mobj);
-			mobj->angle = target->angle + ANGLE_90;
-		}
-#endif
-		break;
 	case MT_FALLINGROCK:
 		// Despawn rocks here in case zmovement code can't do so (blame slopes)
 		if (!mobj->momx && !mobj->momy && !mobj->momz
@@ -9942,18 +9967,6 @@ static boolean P_FuseThink(mobj_t *mobj)
 		break;
 	case MT_METALSONIC_BATTLE:
 		break; // don't remove
-	case MT_SPIKE:
-		P_SetMobjState(mobj, mobj->state->nextstate);
-		mobj->fuse = mobj->info->speed;
-		if (mobj->spawnpoint)
-			mobj->fuse += mobj->spawnpoint->angle;
-		break;
-	case MT_WALLSPIKE:
-		P_SetMobjState(mobj, mobj->state->nextstate);
-		mobj->fuse = mobj->info->speed;
-		if (mobj->spawnpoint)
-			mobj->fuse += (mobj->spawnpoint->angle / 360);
-		break;
 	case MT_NIGHTSCORE:
 		P_RemoveMobj(mobj);
 		return false;
@@ -10422,7 +10435,7 @@ static fixed_t P_DefaultMobjShadowScale (mobj_t *thing)
 
 		case MT_RING:
 		case MT_FLINGRING:
-		
+
 		case MT_COIN:
 		case MT_FLINGCOIN:
 
@@ -12956,17 +12969,18 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
 		// Pop up spikes!
 		if (mthing->options & MTF_OBJECTSPECIAL)
 		{
-			mobj->flags &= ~MF_SCENERY;
 			mobj->fuse = (16 - mthing->extrainfo)*(mthing->angle + mobj->info->speed)/16;
 			if (mthing->options & MTF_EXTRA)
 				P_SetMobjState(mobj, mobj->info->meleestate);
 		}
-		// Use per-thing collision for spikes if the deaf flag isn't checked.
-		if (!(mthing->options & MTF_AMBUSH) && !metalrecording)
+		else
+			mobj->flags |= MF_NOTHINK;
+		// no collision for spikes if the ambush flag is checked
+		if ((mthing->options & MTF_AMBUSH) || metalrecording)
 		{
 			P_UnsetThingPosition(mobj);
-			mobj->flags &= ~(MF_NOBLOCKMAP|MF_NOGRAVITY|MF_NOCLIPHEIGHT);
-			mobj->flags |= MF_SOLID;
+			mobj->flags |= (MF_NOBLOCKMAP|MF_NOCLIPHEIGHT);
+			mobj->flags &= ~MF_SOLID;
 			P_SetThingPosition(mobj);
 		}
 		break;
@@ -12974,20 +12988,20 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
 		// Pop up spikes!
 		if (mthing->options & MTF_OBJECTSPECIAL)
 		{
-			mobj->flags &= ~MF_SCENERY;
 			mobj->fuse = (16 - mthing->extrainfo)*((mthing->angle/360) + mobj->info->speed)/16;
 			if (mthing->options & MTF_EXTRA)
 				P_SetMobjState(mobj, mobj->info->meleestate);
 		}
-		// Use per-thing collision for spikes if the deaf flag isn't checked.
-		if (!(mthing->options & MTF_AMBUSH) && !metalrecording)
+		else
+			mobj->flags |= MF_NOTHINK;
+		// no collision for spikes if the ambush flag is checked
+		if ((mthing->options & MTF_AMBUSH) || metalrecording)
 		{
 			P_UnsetThingPosition(mobj);
-			mobj->flags &= ~(MF_NOBLOCKMAP | MF_NOCLIPHEIGHT);
-			mobj->flags |= MF_SOLID;
+			mobj->flags |= (MF_NOBLOCKMAP|MF_NOCLIPHEIGHT);
+			mobj->flags &= ~MF_SOLID;
 			P_SetThingPosition(mobj);
 		}
-
 		// spawn base
 		{
 			const angle_t mobjangle = FixedAngle(mthing->angle << FRACBITS); // the mobj's own angle hasn't been set quite yet so...
@@ -13001,6 +13015,8 @@ static boolean P_SetupSpawnedMapThing(mapthing_t *mthing, mobj_t *mobj, boolean
 			P_SetScale(base, mobj->scale);
 			P_SetTarget(&base->target, mobj);
 			P_SetTarget(&mobj->tracer, base);
+			if (!(mthing->options & MTF_OBJECTSPECIAL))
+				base->flags |= MF_NOTHINK;
 		}
 		break;
 	case MT_RING_BOX:
diff --git a/src/p_saveg.c b/src/p_saveg.c
index 1270064c01f1494abc582fc7a61c8a80791f4635..722340f41f6c4fe86f2a70ebfd597bb0a142c290 100644
--- a/src/p_saveg.c
+++ b/src/p_saveg.c
@@ -1603,7 +1603,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 	diff2 = 0;
 
 	// not the default but the most probable
-	if (mobj->momx != 0 || mobj->momy != 0 || mobj->momz != 0)
+	if (mobj->momx != 0 || mobj->momy != 0 || mobj->momz != 0 || mobj->pmomz !=0)
 		diff |= MD_MOM;
 	if (mobj->radius != mobj->info->radius)
 		diff |= MD_RADIUS;
@@ -1778,6 +1778,7 @@ static void SaveMobjThinker(const thinker_t *th, const UINT8 type)
 		WRITEFIXED(save_p, mobj->momx);
 		WRITEFIXED(save_p, mobj->momy);
 		WRITEFIXED(save_p, mobj->momz);
+		WRITEFIXED(save_p, mobj->pmomz);
 	}
 	if (diff & MD_RADIUS)
 		WRITEFIXED(save_p, mobj->radius);
@@ -2776,6 +2777,7 @@ static thinker_t* LoadMobjThinker(actionf_p1 thinker)
 		mobj->momx = READFIXED(save_p);
 		mobj->momy = READFIXED(save_p);
 		mobj->momz = READFIXED(save_p);
+		mobj->pmomz = READFIXED(save_p);
 	} // otherwise they're zero, and the memset took care of it
 
 	if (diff & MD_RADIUS)
diff --git a/src/p_setup.c b/src/p_setup.c
index 6f06abf1b2fabd75bfb1815ba0d042bbb2ae21d3..4f21922a0c57b77bd9870814c8160cf8c46171d0 100644
--- a/src/p_setup.c
+++ b/src/p_setup.c
@@ -3064,48 +3064,55 @@ static void P_AddBinaryMapTags(void)
 {
 	size_t i;
 
-	for (i = 0; i < numlines; i++)
-	{
-		// 96: Apply Tag to Tagged Sectors
+	for (i = 0; i < numlines; i++) {
 		// 97: Apply Tag to Front Sector
 		// 98: Apply Tag to Back Sector
 		// 99: Apply Tag to Front and Back Sectors
-		if (lines[i].special == 96) {
-			size_t j;
-			mtag_t tag = Tag_FGet(&lines[i].frontsector->tags);
-			mtag_t target_tag = Tag_FGet(&lines[i].tags);
-			mtag_t offset_tags[4];
-			memset(offset_tags, 0, sizeof(mtag_t)*4);
-			if (lines[i].flags & ML_EFFECT6) {
-				offset_tags[0] = (INT32)sides[lines[i].sidenum[0]].textureoffset / FRACUNIT;
-				offset_tags[1] = (INT32)sides[lines[i].sidenum[0]].rowoffset / FRACUNIT;
-			}
-			if (lines[i].flags & ML_TFERLINE) {
-				offset_tags[2] = (INT32)sides[lines[i].sidenum[1]].textureoffset / FRACUNIT;
-				offset_tags[3] = (INT32)sides[lines[i].sidenum[1]].rowoffset / FRACUNIT;
-			}
+		if (lines[i].special == 97 || lines[i].special == 99)
+			P_AddBinaryMapTagsFromLine(lines[i].frontsector, &lines[i]);
+		if (lines[i].special == 98 || lines[i].special == 99)
+			P_AddBinaryMapTagsFromLine(lines[i].backsector, &lines[i]);
+	}
 
-			for (j = 0; j < numsectors; j++) {
-				boolean matches_target_tag = target_tag && Tag_Find(&sectors[j].tags, target_tag);
-				size_t k; for (k = 0; k < 4; k++) {
-					if (lines[i].flags & ML_EFFECT5) {
-						if (matches_target_tag || (offset_tags[k] && Tag_Find(&sectors[j].tags, offset_tags[k]))) {
-							Tag_Add(&sectors[j].tags, tag);
-							break;
-						}
-					} else if (matches_target_tag) {
-						if (k == 0)
-							Tag_Add(&sectors[j].tags, tag);
-						if (offset_tags[k])
-							Tag_Add(&sectors[j].tags, offset_tags[k]);
+	// Run this loop after the 97-99 loop to ensure that 96 can search through all of the
+	// 97-99-applied tags.
+	for (i = 0; i < numlines; i++) {
+		size_t j;
+		mtag_t tag, target_tag;
+		mtag_t offset_tags[4];
+
+		// 96: Apply Tag to Tagged Sectors
+		if (lines[i].special != 96)
+			continue;
+
+		tag = Tag_FGet(&lines[i].frontsector->tags);
+		target_tag = Tag_FGet(&lines[i].tags);
+		memset(offset_tags, 0, sizeof(mtag_t)*4);
+		if (lines[i].flags & ML_EFFECT6) {
+			offset_tags[0] = (INT32)sides[lines[i].sidenum[0]].textureoffset / FRACUNIT;
+			offset_tags[1] = (INT32)sides[lines[i].sidenum[0]].rowoffset / FRACUNIT;
+		}
+		if (lines[i].flags & ML_TFERLINE) {
+			offset_tags[2] = (INT32)sides[lines[i].sidenum[1]].textureoffset / FRACUNIT;
+			offset_tags[3] = (INT32)sides[lines[i].sidenum[1]].rowoffset / FRACUNIT;
+		}
+
+		for (j = 0; j < numsectors; j++) {
+			boolean matches_target_tag = target_tag && Tag_Find(&sectors[j].tags, target_tag);
+			size_t k;
+			for (k = 0; k < 4; k++) {
+				if (lines[i].flags & ML_EFFECT5) {
+					if (matches_target_tag || (offset_tags[k] && Tag_Find(&sectors[j].tags, offset_tags[k]))) {
+						Tag_Add(&sectors[j].tags, tag);
+						break;
 					}
+				} else if (matches_target_tag) {
+					if (k == 0)
+						Tag_Add(&sectors[j].tags, tag);
+					if (offset_tags[k])
+						Tag_Add(&sectors[j].tags, offset_tags[k]);
 				}
 			}
-		} else {
-			if (lines[i].special == 97 || lines[i].special == 99)
-				P_AddBinaryMapTagsFromLine(lines[i].frontsector, &lines[i]);
-			if (lines[i].special == 98 || lines[i].special == 99)
-				P_AddBinaryMapTagsFromLine(lines[i].backsector, &lines[i]);
 		}
 	}
 }
diff --git a/src/p_user.c b/src/p_user.c
index f21118a81fb2f4f088758a9b92ac52444d289e77..9b48442fe2bf76c5adf9dffa84e262380a0dbe6b 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -11588,7 +11588,7 @@ void P_PlayerThink(player_t *player)
 
 			for (i = 0; i < MAXPLAYERS; i++)
 			{
-				if (!playeringame[i] || players[i].spectator || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN)
+				if (!playeringame[i] || players[i].spectator || players[i].bot)
 					continue;
 				if (players[i].lives <= 0)
 					continue;
@@ -11620,7 +11620,7 @@ void P_PlayerThink(player_t *player)
 
 			for (i = 0; i < MAXPLAYERS; i++)
 			{ 
-				if (!playeringame[i] || players[i].spectator || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN)
+				if (!playeringame[i] || players[i].spectator || players[i].bot)
 					continue;
 				if (players[i].quittime > 30 * TICRATE)
 					continue;
@@ -12206,7 +12206,7 @@ void P_PlayerThink(player_t *player)
 		player->losstime--;
 
 	// Flash player after being hit.
-	if (player->powers[pw_flashing] > 0 && player->powers[pw_flashing] < flashingtics && (leveltime & 1))
+	if (player->powers[pw_flashing] > 0 && player->powers[pw_flashing] < flashingtics && (leveltime & 1) && player->playerstate == PST_LIVE)
 		player->mo->flags2 |= MF2_DONTDRAW;
 	else
 		player->mo->flags2 &= ~MF2_DONTDRAW;
@@ -12744,12 +12744,12 @@ void P_PlayerAfterThink(player_t *player)
 				if (!ptera->movefactor)
 					goto dropoff;
 
-				if (ptera->cusval >= 50)
+				if (ptera->cusval >= 30)
 				{
 					player->powers[pw_carry] = CR_NONE;
 					P_SetTarget(&player->mo->tracer, NULL);
 					P_KillMobj(ptera, player->mo, player->mo, 0);
-					player->mo->momz = 9*FRACUNIT;
+					P_SetObjectMomZ(player->mo, 12*FRACUNIT, false);
 					player->pflags |= PF_APPLYAUTOBRAKE|PF_JUMPED|PF_THOKKED;
 					P_SetMobjState(player->mo, S_PLAY_ROLL);
 					break;
diff --git a/src/st_stuff.c b/src/st_stuff.c
index a328d669e51169ba18d8ae8d046774d3b298eed3..ebf188a06f78978e2039ff1ef053c65be38a72c9 100644
--- a/src/st_stuff.c
+++ b/src/st_stuff.c
@@ -2291,7 +2291,7 @@ static void ST_drawTextHUD(void)
 
 			for (i = 0; i < MAXPLAYERS; i++)
 			{
-				if (!playeringame[i] || players[i].spectator)
+				if (!playeringame[i] || players[i].spectator || players[i].bot)
 					continue;
 				if (players[i].lives <= 0)
 					continue;
diff --git a/src/y_inter.c b/src/y_inter.c
index f24436d4082e645fbbd9fe103a7ed33e741a18b7..288a821e6f33209408ce708b1666bc93aabee93d 100644
--- a/src/y_inter.c
+++ b/src/y_inter.c
@@ -2023,7 +2023,7 @@ static void Y_AwardCoopBonuses(void)
 
 	for (i = 0; i < MAXPLAYERS; ++i)
 	{
-		if (!playeringame[i] || players[i].lives < 1) // not active or game over
+		if (!playeringame[i] || players[i].lives < 1 || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN) // not active, game over or tails bot
 			bonusnum = 0; // all null
 		else
 			bonusnum = mapheaderinfo[prevmap]->bonustype + 1; // -1 is none
@@ -2073,7 +2073,7 @@ static void Y_AwardSpecialStageBonus(void)
 	{
 		oldscore = players[i].score;
 
-		if (!playeringame[i] || players[i].lives < 1) // not active or game over
+		if (!playeringame[i] || players[i].lives < 1 || players[i].bot == BOT_2PAI || players[i].bot == BOT_2PHUMAN) // not active, game over or tails bot
 		{
 			Y_SetNullBonus(&players[i], &localbonuses[0]);
 			Y_SetNullBonus(&players[i], &localbonuses[1]);