diff --git a/src/b_bot.c b/src/b_bot.c
index 35b14bd780c7cd0effede6a73db004d6be411785..151aaa633e2282b094d325c8921cc864603937de 100644
--- a/src/b_bot.c
+++ b/src/b_bot.c
@@ -18,29 +18,41 @@
 #include "b_bot.h"
 #include "lua_hook.h"
 
-// If you want multiple bots, variables like this will
-// have to be stuffed in something accessible through player_t.
-static boolean lastForward = false;
-static boolean lastBlocked = false;
-static boolean blocked = false;
-
-static boolean jump_last = false;
-static boolean spin_last = false;
-static UINT8 anxiety = 0;
-static boolean panic = false;
-static UINT8 flymode = 0;
-static boolean spinmode = false;
-static boolean thinkfly = false;
-
-static inline void B_ResetAI(void)
+void B_UpdateBotleader(player_t *player)
 {
-	jump_last = false;
-	spin_last = false;
-	anxiety = 0;
-	panic = false;
-	flymode = 0;
-	spinmode = false;
-	thinkfly = false;
+	UINT32 i;
+	fixed_t dist;
+	fixed_t neardist = INT32_MAX;
+	player_t *nearplayer = NULL;
+	//Find new botleader
+	//if (!player->botleader)
+	//{
+	for (i = 0; i < MAXPLAYERS; i++)
+	{
+		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
+		{
+			player->botleader = &players[i];
+			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)
+		{
+			neardist = dist;
+			nearplayer = &players[i];
+		}
+	}
+	//Set botleader to best candidate (or null if none available)
+	player->botleader = nearplayer;
+	//}
+}
+
+static inline void B_ResetAI(botmem_t *mem)
+{
+	mem->thinkstate = AI_FOLLOW;
+	mem->catchup_tics = 0;
 }
 
 static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
@@ -49,39 +61,48 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 
 	player_t *player = sonic->player, *bot = tails->player;
 	ticcmd_t *pcmd = &player->cmd;
-	boolean water = tails->eflags & MFE_UNDERWATER;
+	botmem_t *mem = &bot->botmem;
+	boolean water = (tails->eflags & MFE_UNDERWATER);
 	SINT8 flip = P_MobjFlip(tails);
 	boolean _2d = (tails->flags2 & MF2_TWOD) || twodlevel;
 	fixed_t scale = tails->scale;
+	boolean jump_last = (bot->lastbuttons & BT_JUMP);
+	boolean spin_last = (bot->lastbuttons & BT_SPIN);
 
 	fixed_t dist = P_AproxDistance(sonic->x - tails->x, sonic->y - tails->y);
 	fixed_t zdist = flip * (sonic->z - tails->z);
 	angle_t ang = sonic->angle;
 	fixed_t pmom = P_AproxDistance(sonic->momx, sonic->momy);
 	fixed_t bmom = P_AproxDistance(tails->momx, tails->momy);
-	fixed_t followmax = 128 * 8 * scale; // Max follow distance before AI begins to enter "panic" state
+	fixed_t followmax = 128 * 8 * scale; // Max follow distance before AI begins to enter catchup state
 	fixed_t followthres = 92 * scale; // Distance that AI will try to reach
 	fixed_t followmin = 32 * scale;
 	fixed_t comfortheight = 96 * scale;
 	fixed_t touchdist = 24 * scale;
 	boolean stalled = (bmom < scale >> 1) && dist > followthres; // Helps to see if the AI is having trouble catching up
 	boolean samepos = (sonic->x == tails->x && sonic->y == tails->y);
-
+	boolean blocked = bot->blocked;
+	
 	if (!samepos)
 		ang = R_PointToAngle2(tails->x, tails->y, sonic->x, sonic->y);
 
-	// We can't follow Sonic if he's not around!
-	if (!sonic || sonic->health <= 0)
-		return;
-
 	// Lua can handle it!
 	if (LUAh_BotAI(sonic, tails, cmd))
 		return;
 
+	// We can't follow Sonic if he's not around!
+	if (!sonic || sonic->health <= 0)
+	{
+		mem->thinkstate = AI_STANDBY;
+		return;
+	}
+	else if (mem->thinkstate == AI_STANDBY)
+		mem->thinkstate = AI_FOLLOW;
+
 	if (tails->player->powers[pw_carry] == CR_MACESPIN || tails->player->powers[pw_carry] == CR_GENERIC)
 	{
 		boolean isrelevant = (sonic->player->powers[pw_carry] == CR_MACESPIN || sonic->player->powers[pw_carry] == CR_GENERIC);
-		dist = P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y);
+		//dist = P_AproxDistance(tails->x-sonic->x, tails->y-sonic->y); //! This is totally redundant.
 		if (sonic->player->cmd.buttons & BT_JUMP && (sonic->player->pflags & PF_JUMPED) && isrelevant)
 			cmd->buttons |= BT_JUMP;
 		if (isrelevant)
@@ -103,56 +124,57 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 		followmin = 0;
 		followthres = 16*scale;
 		followmax >>= 1;
-		thinkfly = false;
+		if (mem->thinkstate == AI_THINKFLY)
+			mem->thinkstate = AI_FOLLOW;
 	}
 
-	// Check anxiety
-	if (spinmode)
+	// Update catchup_tics
+	if (mem->thinkstate == AI_SPINFOLLOW)
 	{
-		anxiety = 0;
-		panic = false;
+		mem-> catchup_tics = 0;
 	}
 	else if (dist > followmax || zdist > comfortheight || stalled)
 	{
-		anxiety = min(anxiety + 2, 70);
-		if (anxiety >= 70)
-			panic = true;
+		mem-> catchup_tics = min(mem-> catchup_tics + 2, 70);
+		if (mem-> catchup_tics >= 70)
+			mem->thinkstate = AI_CATCHUP;
 	}
 	else
 	{
-		anxiety = max(anxiety - 1, 0);
-		panic = false;
+		mem-> catchup_tics = max(mem-> catchup_tics - 1, 0);
+		if (mem->thinkstate == AI_CATCHUP)
+			mem->thinkstate = AI_FOLLOW;
 	}
 
 	// Orientation
+	// cmd->angleturn won't be relative to player angle, since we're not going through G_BuildTiccmd.
 	if (bot->pflags & (PF_SPINNING|PF_STARTDASH))
 	{
-		cmd->angleturn = (sonic->angle - tails->angle) >> 16; // NOT FRACBITS DAMNIT
+		cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT
 	}
-	else if (flymode == 2)
+	else if (mem->thinkstate == AI_FLYCARRY)
 	{
-		cmd->angleturn = sonic->player->cmd.angleturn - (tails->angle >> 16);
+		cmd->angleturn = sonic->player->cmd.angleturn;
 	}
 	else
 	{
-		cmd->angleturn = (ang - tails->angle) >> 16; // NOT FRACBITS DAMNIT
+		cmd->angleturn = (ang) >> 16; // NOT FRACBITS DAMNIT
 	}
 
 	// ********
 	// FLY MODE
-	// spinmode check
-	if (spinmode || player->exiting)
-		thinkfly = false;
+	// exiting check
+	if (player->exiting && mem->thinkstate == AI_THINKFLY)
+		mem->thinkstate = AI_FOLLOW;
 	else
 	{
 		// Activate co-op flight
-		if (thinkfly && player->pflags & PF_JUMPED)
+		if (mem->thinkstate == AI_THINKFLY && player->pflags & PF_JUMPED)
 		{
 			if (!jump_last)
 			{
 				jump = true;
-				flymode = 1;
-				thinkfly = false;
+				mem->thinkstate = AI_FLYSTANDBY;
 				bot->pflags |= PF_CANCARRY;
 			}
 		}
@@ -165,20 +187,19 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 			&& P_IsObjectOnGround(sonic) && P_IsObjectOnGround(tails)
 			&& !(player->pflags & PF_STASIS)
 			&& bot->charability == CA_FLY)
-				thinkfly = true;
-		else
-			thinkfly = false;
+				mem->thinkstate = AI_THINKFLY;
+		else if (mem->thinkstate == AI_THINKFLY)
+			mem->thinkstate = AI_FOLLOW;
 
 		// Set carried state
 		if (player->powers[pw_carry] == CR_PLAYER && sonic->tracer == tails)
 		{
-			flymode = 2;
+			mem->thinkstate = AI_FLYCARRY;
 		}
 
 		// Ready for takeoff
-		if (flymode == 1)
+		if (mem->thinkstate == AI_FLYSTANDBY)
 		{
-			thinkfly = false;
 			if (zdist < -64*scale || (flip * tails->momz) > scale) // Make sure we're not too high up
 				spin = true;
 			else if (!jump_last)
@@ -186,10 +207,10 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 
 			// Abort if the player moves away or spins
 			if (dist > followthres || player->dashspeed)
-				flymode = 0;
+				mem->thinkstate = AI_FOLLOW;
 		}
 		// Read player inputs while carrying
-		else if (flymode == 2)
+		else if (mem->thinkstate == AI_FLYCARRY)
 		{
 			cmd->forwardmove = pcmd->forwardmove;
 			cmd->sidemove = pcmd->sidemove;
@@ -203,19 +224,19 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 			// End flymode
 			if (player->powers[pw_carry] != CR_PLAYER)
 			{
-				flymode = 0;
+				mem->thinkstate = AI_FOLLOW;
 			}
 		}
 	}
 
-	if (flymode && P_IsObjectOnGround(tails) && !(pcmd->buttons & BT_JUMP))
-		flymode = 0;
+	if (P_IsObjectOnGround(tails) && !(pcmd->buttons & BT_JUMP) && (mem->thinkstate == AI_FLYSTANDBY || mem->thinkstate == AI_FLYCARRY))
+		mem->thinkstate = AI_FOLLOW;
 
 	// ********
 	// SPINNING
-	if (panic || flymode || !(player->pflags & PF_SPINNING) || (player->pflags & PF_JUMPED))
-		spinmode = false;
-	else
+	if (!(player->pflags & (PF_SPINNING|PF_STARTDASH)) && mem->thinkstate == AI_SPINFOLLOW)
+		mem->thinkstate = AI_FOLLOW;
+	else if (mem->thinkstate == AI_FOLLOW || mem->thinkstate == AI_SPINFOLLOW)
 	{
 		if (!_2d)
 		{
@@ -224,21 +245,21 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 			{
 				if (dist < followthres && dist > touchdist) // Do positioning
 				{
-					cmd->angleturn = (ang - tails->angle) >> 16; // NOT FRACBITS DAMNIT
+					cmd->angleturn = (ang) >> 16; // NOT FRACBITS DAMNIT
 					cmd->forwardmove = 50;
-					spinmode = true;
+					mem->thinkstate = AI_SPINFOLLOW;
 				}
 				else if (dist < touchdist)
 				{
 					if (!bmom && (!(bot->pflags & PF_SPINNING) || (bot->dashspeed && bot->pflags & PF_SPINNING)))
 					{
-						cmd->angleturn = (sonic->angle - tails->angle) >> 16; // NOT FRACBITS DAMNIT
+						cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT
 						spin = true;
 					}
-					spinmode = true;
+					mem->thinkstate = AI_SPINFOLLOW;
 				}
 				else
-					spinmode = false;
+					mem->thinkstate = AI_FOLLOW;
 			}
 			// Spin
 			else if (player->dashspeed == bot->dashspeed && player->pflags & PF_SPINNING)
@@ -246,12 +267,12 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 				if (bot->pflags & PF_SPINNING || !spin_last)
 				{
 					spin = true;
-					cmd->angleturn = (sonic->angle - tails->angle) >> 16; // NOT FRACBITS DAMNIT
+					cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT
 					cmd->forwardmove = MAXPLMOVE;
-					spinmode = true;
+					mem->thinkstate = AI_SPINFOLLOW;
 				}
 				else
-					spinmode = false;
+					mem->thinkstate = AI_FOLLOW;
 			}
 		}
 		// 2D mode
@@ -261,17 +282,19 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 				&& ((bot->pflags & PF_SPINNING) || !spin_last))
 			{
 				spin = true;
-				spinmode = true;
+				mem->thinkstate = AI_SPINFOLLOW;
 			}
+			else
+				mem->thinkstate = AI_FOLLOW;
 		}
 	}
 
 	// ********
 	// FOLLOW
-	if (!(flymode || spinmode))
+	if (mem->thinkstate == AI_FOLLOW || mem->thinkstate == AI_CATCHUP)
 	{
 		// Too far
-		if (panic || dist > followthres)
+		if (mem->thinkstate == AI_CATCHUP || dist > followthres)
 		{
 			if (!_2d)
 				cmd->forwardmove = MAXPLMOVE;
@@ -281,7 +304,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 				cmd->sidemove = -MAXPLMOVE;
 		}
 		// Within threshold
-		else if (!panic && dist > followmin && abs(zdist) < 192*scale)
+		else if (dist > followmin && abs(zdist) < 192*scale)
 		{
 			if (!_2d)
 				cmd->forwardmove = FixedHypot(pcmd->forwardmove, pcmd->sidemove);
@@ -292,7 +315,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 		else if (dist < followmin)
 		{
 			// Copy inputs
-			cmd->angleturn = (sonic->angle - tails->angle) >> 16; // NOT FRACBITS DAMNIT
+			cmd->angleturn = (sonic->angle) >> 16; // NOT FRACBITS DAMNIT
 			bot->drawangle = ang;
 			cmd->forwardmove = 8 * pcmd->forwardmove / 10;
 			cmd->sidemove = 8 * pcmd->sidemove / 10;
@@ -301,7 +324,7 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 
 	// ********
 	// JUMP
-	if (!(flymode || spinmode))
+	if (mem->thinkstate == AI_FOLLOW || mem->thinkstate == AI_CATCHUP || (mem->thinkstate == AI_SPINFOLLOW && player->pflags & PF_JUMPED))
 	{
 		// Flying catch-up
 		if (bot->pflags & PF_THOKKED)
@@ -319,31 +342,30 @@ static void B_BuildTailsTiccmd(mobj_t *sonic, mobj_t *tails, ticcmd_t *cmd)
 		// Start jump
 		else if (!jump_last && !(bot->pflags & PF_JUMPED) //&& !(player->pflags & PF_SPINNING)
 			&& ((zdist > 32*scale && player->pflags & PF_JUMPED) // Following
-				|| (zdist > 64*scale && panic) // Vertical catch-up
-				|| (stalled && anxiety > 20 && bot->powers[pw_carry] == CR_NONE)
+				|| (zdist > 64*scale && mem->thinkstate == AI_CATCHUP) // Vertical catch-up
+				|| (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;
 		// Hold jump
-		else if (bot->pflags & PF_JUMPED && jump_last && tails->momz*flip > 0 && (zdist > 0 || panic))
+		else if (bot->pflags & PF_JUMPED && jump_last && tails->momz*flip > 0 && (zdist > 0 || mem->thinkstate == AI_CATCHUP))
 			jump = true;
 		// Start flying
-		else if (bot->pflags & PF_JUMPED && panic && !jump_last && bot->charability == CA_FLY)
+		else if (bot->pflags & PF_JUMPED && mem->thinkstate == AI_CATCHUP && !jump_last && bot->charability == CA_FLY)
 			jump = true;
 	}
 
 	// ********
 	// HISTORY
-	jump_last = jump;
-	spin_last = spin;
+	//jump_last = jump;
+	//spin_last = spin;
 
 	// Turn the virtual keypresses into ticcmd_t.
 	B_KeysToTiccmd(tails, cmd, forward, backward, left, right, false, false, jump, spin);
 
 	// Update our status
-	lastForward = forward;
-	lastBlocked = blocked;
-	blocked = false;
+	mem->lastForward = forward;
+	mem->lastBlocked = blocked;
 }
 
 void B_BuildTiccmd(player_t *player, ticcmd_t *cmd)
@@ -362,32 +384,31 @@ void B_BuildTiccmd(player_t *player, ticcmd_t *cmd)
 	// Bot AI isn't programmed in analog.
 	CV_SetValue(&cv_analog[1], false);
 
-	// Bot cmd functions and hooks are not currently netplay compatible
-	// Necessary failsafe, as a bot in the P2 position in a netgame can inherit
-	// the last input from a hook triggered in splitscreen or SP.
-	if (netgame)
-		return; 
-
 	// Let Lua scripts build ticcmds
 	if (LUAh_BotTiccmd(player, cmd))
 		return;
 
-	// We don't have any main character AI, sorry. D:
-	if (player-players == consoleplayer)
+	// Make sure we have a valid main character to follow
+	 B_UpdateBotleader(player);
+	if (!player->botleader)
 		return;
 
-	// Basic Tails AI
-	B_BuildTailsTiccmd(players[consoleplayer].mo, player->mo, cmd);
+	// Single Player Tails AI
+	//B_BuildTailsTiccmd(players[consoleplayer].mo, player->mo, cmd);
+	B_BuildTailsTiccmd(player->botleader->mo, player->mo, cmd);
 }
 
 void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward, boolean left, boolean right, boolean strafeleft, boolean straferight, boolean jump, boolean spin)
 {
+	player_t *player = mo->player;
 	// don't try to do stuff if your sonic is in a minecart or something
-	if (players[consoleplayer].powers[pw_carry] && players[consoleplayer].powers[pw_carry] != CR_PLAYER)
+	//if (players[consoleplayer].powers[pw_carry] && players[consoleplayer].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) {
-		if (players[consoleplayer].climbing
+		if (player->botleader->climbing
 		|| mo->player->pflags & PF_GLIDING) {
 			// Don't mess with bot inputs during these unhandled movement conditions.
 			// The normal AI doesn't use abilities, so custom AI should be sending us exactly what it wants anyway.
@@ -426,10 +447,10 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward
 			cmd->forwardmove += MAXPLMOVE<<FRACBITS>>16;
 		if (backward)
 			cmd->forwardmove -= MAXPLMOVE<<FRACBITS>>16;
-		if (left)
+ 		if (left)
 			cmd->angleturn += 1280;
 		if (right)
-			cmd->angleturn -= 1280;
+			cmd->angleturn -= 1280; 
 		if (strafeleft)
 			cmd->sidemove -= MAXPLMOVE<<FRACBITS>>16;
 		if (straferight)
@@ -453,14 +474,19 @@ void B_KeysToTiccmd(mobj_t *mo, ticcmd_t *cmd, boolean forward, boolean backward
 void B_MoveBlocked(player_t *player)
 {
 	(void)player;
-	blocked = true;
+	player->blocked = true;
 }
 
 boolean B_CheckRespawn(player_t *player)
 {
-	mobj_t *sonic = players[consoleplayer].mo;
+	mobj_t *sonic;
 	mobj_t *tails = player->mo;
 
+	//We don't have a main player to spawn to!
+	if (!player->botleader)
+		return false;
+	
+	sonic = player->botleader->mo;
 	// We can't follow Sonic if he's not around!
 	if (!sonic || sonic->health <= 0)
 		return false;
@@ -511,15 +537,19 @@ void B_RespawnBot(INT32 playernum)
 {
 	player_t *player = &players[playernum];
 	fixed_t x,y,z;
-	mobj_t *sonic = players[consoleplayer].mo;
+	mobj_t *sonic;
 	mobj_t *tails;
 
+	if (!player->botleader)
+		return;
+
+	sonic = player->botleader->mo;
 	if (!sonic || sonic->health <= 0)
 		return;
 
-	B_ResetAI();
+	B_ResetAI(&player->botmem);
 
-	player->bot = 1;
+	player->bot = BOT_2PAI;
 	P_SpawnPlayer(playernum);
 	tails = player->mo;
 
@@ -546,10 +576,7 @@ void B_RespawnBot(INT32 playernum)
 	player->powers[pw_spacetime] = sonic->player->powers[pw_spacetime];
 	player->powers[pw_gravityboots] = sonic->player->powers[pw_gravityboots];
 	player->powers[pw_nocontrol] = sonic->player->powers[pw_nocontrol];
-	player->acceleration = sonic->player->acceleration;
-	player->accelstart = sonic->player->accelstart;
-	player->thrustfactor = sonic->player->thrustfactor;
-	player->normalspeed = sonic->player->normalspeed;
+	//!!! Nuke the speed equivalencies
 	player->pflags |= PF_AUTOBRAKE|(sonic->player->pflags & PF_DIRECTIONCHAR);
 
 	P_TeleportMove(tails, x, y, z);
@@ -567,11 +594,11 @@ void B_RespawnBot(INT32 playernum)
 void B_HandleFlightIndicator(player_t *player)
 {
 	mobj_t *tails = player->mo;
-
+	botmem_t *mem = &player->botmem;
 	if (!tails)
 		return;
 
-	if (thinkfly && player->bot == 1 && tails->health)
+	if (mem->thinkstate == AI_THINKFLY && player->bot == BOT_2PAI && tails->health)
 	{
 		if (!tails->hnext)
 		{