diff --git a/src/b_bot.c b/src/b_bot.c
index bf2dbbb68586aab8390557568622d2e9e6f89e0a..82075eb8efebe09aad15d75573a02b09c56addb6 100644
--- a/src/b_bot.c
+++ b/src/b_bot.c
@@ -17,6 +17,7 @@
 #include "p_local.h"
 #include "b_bot.h"
 #include "lua_hook.h"
+#include "i_system.h" // I_BaseTiccmd
 
 void B_UpdateBotleader(player_t *player)
 {
@@ -132,17 +133,17 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 	// Update catchup_tics
 	if (mem->thinkstate == AI_SPINFOLLOW)
 	{
-		mem-> catchup_tics = 0;
+		mem->catchup_tics = 0;
 	}
 	else if (dist > followmax || zdist > comfortheight || stalled)
 	{
-		mem-> catchup_tics = min(mem-> catchup_tics + 2, 70);
-		if (mem-> catchup_tics >= 70)
+		mem->catchup_tics = min(mem->catchup_tics + 2, 70);
+		if (mem->catchup_tics >= 70)
 			mem->thinkstate = AI_CATCHUP;
 	}
 	else
 	{
-		mem-> catchup_tics = max(mem-> catchup_tics - 1, 0);
+		mem->catchup_tics = max(mem->catchup_tics - 1, 0);
 		if (mem->thinkstate == AI_CATCHUP)
 			mem->thinkstate = AI_FOLLOW;
 	}
@@ -317,7 +318,6 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 		{
 			// Copy inputs
 			cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT
-			bot->drawangle = ang;
 			cmd->forwardmove = 8 * pcmd->forwardmove / 10;
 			cmd->sidemove = 8 * pcmd->sidemove / 10;
 		}
@@ -344,7 +344,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 		else if (!jump_last && !(bot->pflags & PF_JUMPED) //&& !(player->pflags & PF_SPINNING)
 			&& ((zdist > 32*scale && player->pflags & PF_JUMPED) // Following
 				|| (zdist > 64*scale && mem->thinkstate == AI_CATCHUP) // Vertical catch-up
-				|| (stalled && mem-> catchup_tics > 20 && bot->powers[pw_carry] == CR_NONE)
+				|| (stalled && mem->catchup_tics > 20 && bot->powers[pw_carry] == CR_NONE)
 				//|| (bmom < scale>>3 && dist > followthres && !(bot->powers[pw_carry])) // Stopped & not in carry state
 				|| (bot->pflags & PF_SPINNING && !(bot->pflags & PF_JUMPED)))) // Spinning
 					jump = true;
@@ -371,6 +371,8 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 
 void B_BuildTiccmd(player_t *player, ticcmd_t *cmd)
 {
+	G_CopyTiccmd(cmd, I_BaseTiccmd(), 1); // empty, or external driver
+
 	// Can't build a ticcmd if we aren't spawned...
 	if (!player->mo)
 		return;
@@ -390,7 +392,7 @@ void B_BuildTiccmd(player_t *player, ticcmd_t *cmd)
 		return;
 
 	// Make sure we have a valid main character to follow
-	 B_UpdateBotleader(player);
+	B_UpdateBotleader(player);
 	if (!player->botleader)
 		return;
 
@@ -403,7 +405,7 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward
 {
 	player_t *player = mo->player;
 	// don't try to do stuff if your sonic is in a minecart or something
-	if (&player->botleader && player->botleader->powers[pw_carry] && player->botleader->powers[pw_carry] != CR_PLAYER)
+	if (player->botleader && player->botleader->powers[pw_carry] && player->botleader->powers[pw_carry] != CR_PLAYER)
 		return;
 	// Turn the virtual keypresses into ticcmd_t.
 	if (twodlevel || mo->flags2 & MF2_TWOD) {
@@ -593,25 +595,43 @@ void B_HandleFlightIndicator(player_t *player)
 {
 	mobj_t *tails = player->mo;
 	botmem_t *mem = &player->botmem;
+	boolean shouldExist;
+
 	if (!tails)
 		return;
 
-	if (mem->thinkstate == AI_THINKFLY && player->bot == BOT_2PAI && tails->health)
+	shouldExist = (mem->thinkstate == AI_THINKFLY) && player->botleader
+		&& player->bot == BOT_2PAI && player->playerstate == PST_LIVE;
+
+	// check whether the indicator doesn't exist
+	if (P_MobjWasRemoved(tails->hnext))
 	{
-		if (!tails->hnext)
-		{
-			P_SetTarget(&tails->hnext, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY));
-			if (tails->hnext)
-			{
-				P_SetTarget(&tails->hnext->target, tails);
-				P_SetTarget(&tails->hnext->hprev, tails);
-				P_SetMobjState(tails->hnext, S_FLIGHTINDICATOR);
-			}
-		}
+		// if it shouldn't exist, everything is fine
+		if (!shouldExist)
+			return;
+
+		// otherwise, spawn it
+		P_SetTarget(&tails->hnext, P_SpawnMobjFromMobj(tails, 0, 0, 0, MT_OVERLAY));
+		P_SetTarget(&tails->hnext->target, tails);
+		P_SetTarget(&tails->hnext->hprev, tails);
+		P_SetMobjState(tails->hnext, S_FLIGHTINDICATOR);
 	}
-	else if (tails->hnext && tails->hnext->type == MT_OVERLAY && tails->hnext->state == states+S_FLIGHTINDICATOR)
+
+	// if the mobj isn't a flight indicator, let's not mess with it
+	if (tails->hnext->type != MT_OVERLAY || (tails->hnext->state != states+S_FLIGHTINDICATOR))
+		return;
+
+	// if it shouldn't exist, remove it
+	if (!shouldExist)
 	{
 		P_RemoveMobj(tails->hnext);
 		P_SetTarget(&tails->hnext, NULL);
+		return;
 	}
+
+	// otherwise, update its visibility
+	if (P_IsLocalPlayer(player->botleader))
+		tails->hnext->flags2 &= ~MF2_DONTDRAW;
+	else
+		tails->hnext->flags2 |= MF2_DONTDRAW;
 }
diff --git a/src/g_game.c b/src/g_game.c
index 99c8e5d3fb52bb28cbf6c519c491508447f780d8..fa0900b02ff0602c3ac0a7af095769026db85cdd 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -1547,12 +1547,17 @@ void G_BuildTiccmd(ticcmd_t *cmd, INT32 realtics, UINT8 ssplayer)
 	cmd->sidemove = (SINT8)(cmd->sidemove + side);
 
 	// Note: Majority of botstuffs are handled in G_Ticker now.
-	if (player->bot == BOT_2PHUMAN) //Player-controlled bot
+	if (player->bot == BOT_2PAI
+		&& !player->powers[pw_tailsfly]
+		&& (cmd->forwardmove || cmd->sidemove || cmd->buttons))
 	{
-		// Fix offset angle for P2-controlled Tailsbot when P2's controls are set to non-Strafe
-		cmd->angleturn = (INT16)((localangle - *myangle) >> 16);
+		player->bot = BOT_2PHUMAN; // A player-controlled bot. Returns to AI when it respawns.
+		CV_SetValue(&cv_analog[1], true);
 	}
 
+	if (player->bot == BOT_2PHUMAN)
+		cmd->angleturn = (INT16)((localangle - *myangle) >> 16);
+	
 	*myangle += (cmd->angleturn<<16);
 
 	if (controlstyle == CS_LMAOGALOG) {
@@ -2307,65 +2312,44 @@ void G_Ticker(boolean run)
 
 	buf = gametic % BACKUPTICS;
 
+	// Generate ticcmds for bots FIRST, then copy received ticcmds for players.
+	// This emulates pre-2.2.10 behaviour where the bot referenced their leader's last copied ticcmd,
+	// which is desirable because P_PlayerThink can override inputs (e.g. while PF_STASIS is applied or in a waterslide),
+	// and the bot AI needs to respect that.
+#define ISHUMAN (players[i].bot == BOT_NONE || players[i].bot == BOT_2PHUMAN)
 	for (i = 0; i < MAXPLAYERS; i++)
 	{
-		if (playeringame[i])
+		if (playeringame[i] && !ISHUMAN) // Less work is required if we're building a bot ticcmd.
 		{
-			INT16 received;
-			// Save last frame's button readings
-			players[i].lastbuttons = players[i].cmd.buttons;
+			players[i].lastbuttons = players[i].cmd.buttons; // Save last frame's button readings
+			B_BuildTiccmd(&players[i], &players[i].cmd);
+
+			// Since bot TicCmd is pre-determined for both the client and server, the latency and packet checks are simplified.
+			players[i].cmd.latency = 0;
+			P_SetPlayerAngle(&players[i], players[i].cmd.angleturn << 16);
+		}
+	}
 
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		if (playeringame[i] && ISHUMAN)
+		{
+			players[i].lastbuttons = players[i].cmd.buttons; // Save last frame's button readings
 			G_CopyTiccmd(&players[i].cmd, &netcmds[buf][i], 1);
-			// Bot ticcmd handling
-			// Yes, ordinarily this would be handled in G_BuildTiccmd...
-			// ...however, bot players won't have a corresponding consoleplayer or splitscreen player 2 to send that information.
-			// Therefore, this has to be done after ticcmd sends are received.
-			if (players[i].bot == BOT_2PAI) { // Tailsbot for P2
-				if (!players[i].powers[pw_tailsfly] && (players[i].cmd.forwardmove || players[i].cmd.sidemove || players[i].cmd.buttons))
-				{
-					players[i].bot = BOT_2PHUMAN; // A player-controlled bot. Returns to AI when it respawns.
-					CV_SetValue(&cv_analog[1], true);
-				}
-				else
-				{
-					B_BuildTiccmd(&players[i], &players[i].cmd);
-				}
-				B_HandleFlightIndicator(&players[i]);
-			}
-			else if (players[i].bot == BOT_MPAI) {
-				B_BuildTiccmd(&players[i], &players[i].cmd);
-			}
+
+			// Use the leveltime sent in the player's ticcmd to determine control lag
+			players[i].cmd.latency = min(((leveltime & 0xFF) - players[i].cmd.latency) & 0xFF, MAXPREDICTTICS-1);
 
 			// Do angle adjustments.
-			if (players[i].bot == BOT_NONE || players[i].bot == BOT_2PHUMAN)
-			{
-				received = (players[i].cmd.angleturn & TICCMD_RECEIVED);
-				players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn;
-				players[i].oldrelangleturn = players[i].cmd.angleturn;
-				if (P_ControlStyle(&players[i]) == CS_LMAOGALOG)
-					P_ForceLocalAngle(&players[i], players[i].angleturn << 16);
-				else
-					players[i].cmd.angleturn = players[i].angleturn;
-    			if (P_ControlStyle(&players[i]) == CS_LMAOGALOG)
-    				P_ForceLocalAngle(&players[i], players[i].angleturn << 16);
-    			else
-    				players[i].cmd.angleturn = players[i].angleturn;
-
-    			players[i].cmd.angleturn &= ~TICCMD_RECEIVED;
-				// Use the leveltime sent in the player's ticcmd to determine control lag
-    			players[i].cmd.latency = min(((leveltime & 0xFF) - players[i].cmd.latency) & 0xFF, MAXPREDICTTICS-1);
-			}
-			else // Less work is required if we're building a bot ticcmd.
-			{
-    			// Since bot TicCmd is pre-determined for both the client and server, the latency and packet checks are simplified.
-    			received = 1;
-    			players[i].cmd.latency = 0;
-				players[i].angleturn = players[i].cmd.angleturn;
-				players[i].oldrelangleturn = players[i].cmd.angleturn;
-			}
-			players[i].cmd.angleturn |= received;
+			players[i].angleturn += players[i].cmd.angleturn - players[i].oldrelangleturn;
+			players[i].oldrelangleturn = players[i].cmd.angleturn;
+			if (P_ControlStyle(&players[i]) == CS_LMAOGALOG)
+				P_ForceLocalAngle(&players[i], players[i].angleturn << 16);
+			else
+				players[i].cmd.angleturn = (players[i].angleturn & ~TICCMD_RECEIVED) | (players[i].cmd.angleturn & TICCMD_RECEIVED);
 		}
 	}
+#undef ISHUMAN
 
 	// do main actions
 	switch (gamestate)
diff --git a/src/p_user.c b/src/p_user.c
index 83eb4ea02555c6fc45622fa1f32a1632b0cce4fa..a4852469f68d934482a7ddc3f9f930bae9dee934 100644
--- a/src/p_user.c
+++ b/src/p_user.c
@@ -11441,6 +11441,8 @@ void P_PlayerThink(player_t *player)
 		{
 			if (B_CheckRespawn(player))
 				player->playerstate = PST_REBORN;
+			else
+				B_HandleFlightIndicator(player);
 		}
 		if (player->playerstate == PST_REBORN)
 		{