diff --git a/src/g_game.c b/src/g_game.c
index 80f189381d7ce73691f7caed4d0e8c7312a82aeb..9dd63e0b7ae4cb081ddb5616ea086df9694c8185 100644
--- a/src/g_game.c
+++ b/src/g_game.c
@@ -2891,10 +2891,7 @@ static boolean G_AreTeamStartsAvailableForPlayer(INT32 playernum)
 mapthing_t *G_FindTeamStart(INT32 playernum)
 {
 	UINT8 team = players[playernum].ctfteam;
-	if (team == TEAM_NONE || team >= numteams)
-		return G_FindMatchStart(playernum);
-
-	if (G_AreTeamStartsAvailable(team))
+	if (team != TEAM_NONE && G_AreTeamStartsAvailable(team))
 	{
 		for (INT32 j = 0; j < MAXPLAYERS; j++)
 		{
@@ -2946,10 +2943,12 @@ static mapthing_t *G_FindBestStart(INT32 playernum, INT32 type)
 	{
 	case PLAYER_START_TYPE_COOP:
 		return G_FindCoopStart(playernum);
+	case PLAYER_START_TYPE_TEAM:
+		if (players[playernum].ctfteam > TEAM_NONE && players[playernum].ctfteam < numteams)
+			return G_FindTeamStart(playernum);
+		/* FALLTHRU */
 	case PLAYER_START_TYPE_MATCH:
 		return G_FindMatchStart(playernum);
-	case PLAYER_START_TYPE_TEAM:
-		return G_FindTeamStart(playernum);
 	}
 	return NULL;
 }
@@ -3078,8 +3077,8 @@ mapthing_t *G_FindMapStart(INT32 playernum)
 	mapthing_t *spawnpoint = NULL;
 
 	// -- Spectators --
-	// Order in platform gametypes: Coop->DM->CTF
-	// Otherwise: DM->CTF->Coop
+	// Order in platform gametypes: Coop->DM->Team
+	// Otherwise: DM->Team->Coop
 	if (player->spectator)
 	{
 		// In platform gametypes, spawn in Co-op starts first
@@ -3091,17 +3090,17 @@ mapthing_t *G_FindMapStart(INT32 playernum)
 	}
 
 	// -- CTF --
-	// Order: CTF->DM->Coop
-	else if (gametyperules & GTR_TEAMFLAGS)
+	// Order: Team->DM->Coop
+	else if (gametyperules & GTR_TEAMFLAGS && G_GametypeHasTeams())
 	{
-		if (player->ctfteam)
+		if (player->ctfteam > TEAM_NONE && player->ctfteam < numteams)
 			spawnpoint = G_GetTeamStartForSpawning(playernum);
 		else
 			spawnpoint = G_GetMatchStartForSpawning(playernum);
 	}
 
 	// -- DM/Tag/etc --
-	// Order: DM->CTF->Coop
+	// Order: DM->Team->Coop
 	else if (gametyperules & GTR_DEATHMATCHSTARTS)
 	{
 		// If the current gametype has teams, but isn't CTF, then this looks for a team start first
@@ -3110,20 +3109,20 @@ mapthing_t *G_FindMapStart(INT32 playernum)
 		{
 			spawnpoint = G_FindTeamStart(playernum);
 
-			// If no spawn point was found, but team starts are available, this means there is no good location
+			// If no spawn point was returned, but team starts are available, this means there is no good location
 			// for the player to spawn at. In that situation, we display a message telling the player so.
 			if (spawnpoint == NULL && G_AreTeamStartsAvailable(player->ctfteam) && P_IsLocalPlayer(player))
 				CONS_Alert(CONS_WARNING, M_GetText("Could not spawn at any %s starts!\n"), G_GetTeamName(player->ctfteam));
 		}
 
 		// If that failed, no warning is shown. Instead, this will look for a match start, which may
-		// then display a warning if no suitable map starts were found.
+		// then display a warning if no suitable map start was found.
 		if (spawnpoint == NULL)
 			spawnpoint = G_GetMatchStartForSpawning(playernum);
 	}
 
 	// -- Other game modes --
-	// Order: Coop->DM->CTF
+	// Order: Coop->DM->Team
 	else
 		spawnpoint = G_GetCoopStartForSpawning(playernum);
 
@@ -3146,6 +3145,52 @@ mapthing_t *G_FindMapStart(INT32 playernum)
 	return spawnpoint;
 }
 
+mapthing_t *G_FindBestPlayerStart(INT32 playernum)
+{
+	if (!playeringame[playernum])
+		return NULL;
+
+	player_t *player = &players[playernum];
+
+	// Spectator
+	if (player->spectator)
+	{
+		if (G_PlatformGametype() && !(gametyperules & GTR_DEATHMATCHSTARTS))
+			return G_FindCoopStart(playernum);
+		else
+			return G_FindMatchStart(playernum);
+	}
+	// CTF
+	else if (gametyperules & GTR_TEAMFLAGS && G_GametypeHasTeams())
+	{
+		mapthing_t *mapthing = NULL;
+
+		if (player->ctfteam > TEAM_NONE && player->ctfteam < numteams)
+			mapthing = G_FindTeamStart(playernum);
+
+		if (mapthing)
+			return mapthing;
+		else
+			return G_FindMatchStart(playernum);
+	}
+	// Gametypes that use match starts
+	else if (gametyperules & GTR_DEATHMATCHSTARTS)
+	{
+		mapthing_t *mapthing = NULL;
+
+		if (G_GametypeHasTeams() && player->ctfteam > TEAM_NONE && player->ctfteam < numteams)
+			mapthing = G_FindTeamStart(playernum);
+
+		if (mapthing)
+			return mapthing;
+		else
+			return G_FindMatchStart(playernum);
+	}
+
+	// Everything else (just uses Co-Op)
+	return G_FindCoopStart(playernum);
+}
+
 // Go back through all the projectiles and remove all references to the old
 // player mobj, replacing them with the new one.
 void G_ChangePlayerReferences(mobj_t *oldmo, mobj_t *newmo)
diff --git a/src/g_game.h b/src/g_game.h
index aa1588f31b1fddaf3968d4605873344ac16b53d7..5801b06a4a8d2e2f50ab83bf01c9dbc408adfe5d 100644
--- a/src/g_game.h
+++ b/src/g_game.h
@@ -166,11 +166,11 @@ void G_FreeMapSearch(mapsearchfreq_t *freq, INT32 freqc);
 /* Match map name by search + 2 digit map code or map number. */
 INT32 G_FindMapByNameOrCode(const char *query, char **foundmapnamep);
 
-// XMOD spawning
-mapthing_t *G_FindTeamStart(INT32 playernum);
-mapthing_t *G_FindMatchStart(INT32 playernum);
-mapthing_t *G_FindCoopStart(INT32 playernum);
 mapthing_t *G_FindMapStart(INT32 playernum);
+mapthing_t *G_FindBestPlayerStart(INT32 playernum);
+mapthing_t *G_FindCoopStart(INT32 playernum);
+mapthing_t *G_FindMatchStart(INT32 playernum);
+mapthing_t *G_FindTeamStart(INT32 playernum);
 void G_MovePlayerToSpawnOrStarpost(INT32 playernum);
 void G_SpawnPlayer(INT32 playernum);
 
diff --git a/src/lua_baselib.c b/src/lua_baselib.c
index ac3d081eec2f7d20bf15a6bc2c41638d4f959b12..5c1a9415df696c32289a26dcc290a650eabb5239 100644
--- a/src/lua_baselib.c
+++ b/src/lua_baselib.c
@@ -4239,6 +4239,50 @@ static int lib_gGetMostDisadvantagedTeam(lua_State *L)
 	return 1;
 }
 
+static int lib_gFindPlayerStart(lua_State *L)
+{
+	player_t *plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+	INLEVEL
+	if (!plr)
+		return LUA_ErrInvalid(L, "player_t");
+	mapthing_t *mapthing = G_FindBestPlayerStart(plr - players);
+	if (mapthing)
+		LUA_PushUserdata(L, mapthing, META_MAPTHING);
+	else
+		lua_pushnil(L);
+	return 1;
+}
+
+static int lib_gFindMatchStart(lua_State *L)
+{
+	player_t *plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+	INLEVEL
+	if (!plr)
+		return LUA_ErrInvalid(L, "player_t");
+	mapthing_t *mapthing = G_FindMatchStart(plr - players);
+	if (mapthing)
+		LUA_PushUserdata(L, mapthing, META_MAPTHING);
+	else
+		lua_pushnil(L);
+	return 1;
+}
+
+static int lib_gFindTeamStart(lua_State *L)
+{
+	player_t *plr = *((player_t **)luaL_checkudata(L, 1, META_PLAYER));
+	INLEVEL
+	if (!plr)
+		return LUA_ErrInvalid(L, "player_t");
+	mapthing_t *mapthing = NULL;
+	if (plr->ctfteam > TEAM_NONE && plr->ctfteam < numteams)
+		mapthing = G_FindTeamStart(plr - players);
+	if (mapthing)
+		LUA_PushUserdata(L, mapthing, META_MAPTHING);
+	else
+		lua_pushnil(L);
+	return 1;
+}
+
 static int lib_gTicsToHours(lua_State *L)
 {
 	tic_t rtic = luaL_checkinteger(L, 1);
@@ -4582,6 +4626,9 @@ static luaL_Reg lib[] = {
 	{"G_GetWorstPerformingTeam",lib_gGetWorstPerformingTeam},
 	{"G_GetMostAdvantagedTeam",lib_gGetMostAdvantagedTeam},
 	{"G_GetMostDisadvantagedTeam",lib_gGetMostDisadvantagedTeam},
+	{"G_FindPlayerStart",lib_gFindPlayerStart},
+	{"G_FindMatchStart",lib_gFindMatchStart},
+	{"G_FindTeamStart",lib_gFindTeamStart},
 	{"G_TicsToHours",lib_gTicsToHours},
 	{"G_TicsToMinutes",lib_gTicsToMinutes},
 	{"G_TicsToSeconds",lib_gTicsToSeconds},