diff --git a/src/doomstat.h b/src/doomstat.h index 95cb9f6a8..f0bf5d6a9 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -634,14 +634,33 @@ extern tic_t hidetime; // WAD, partly set at startup time. extern tic_t gametic; + #define localgametic leveltime +enum +{ + PLAYER_START_TYPE_COOP, + PLAYER_START_TYPE_MATCH, + PLAYER_START_TYPE_TEAM +}; + +typedef struct +{ + size_t count; + size_t capacity; + mapthing_t **list; +} playerstarts_t; + +#define MAX_DM_STARTS 64 + // Player spawn spots. -extern mapthing_t *playerstarts[MAXPLAYERS]; // Cooperative -extern mapthing_t *teamstarts[MAXTEAMS][MAXPLAYERS]; // CTF +extern playerstarts_t playerstarts; +extern playerstarts_t deathmatchstarts; +extern playerstarts_t teamstarts[MAXTEAMS]; #define WAYPOINTSEQUENCESIZE 256 #define NUMWAYPOINTSEQUENCES 256 + extern mobj_t *waypoints[NUMWAYPOINTSEQUENCES][WAYPOINTSEQUENCESIZE]; extern UINT16 numwaypoints[NUMWAYPOINTSEQUENCES]; diff --git a/src/f_finale.c b/src/f_finale.c index d03795dc4..2d93bc5bd 100644 --- a/src/f_finale.c +++ b/src/f_finale.c @@ -2441,13 +2441,8 @@ void F_StartTitleScreen(void) players[displayplayer].playerstate = PST_DEAD; // Don't spawn the player in dummy (I'm still a filthy cheater) - // Set Default Position - if (playerstarts[0]) - startpos = playerstarts[0]; - else if (deathmatchstarts[0]) - startpos = deathmatchstarts[0]; - else - startpos = NULL; + // Set initial camera position + startpos = G_GetInitialSpawnPoint(); if (startpos) { diff --git a/src/g_demo.c b/src/g_demo.c index 4b9ff56e8..a6597051d 100644 --- a/src/g_demo.c +++ b/src/g_demo.c @@ -2564,7 +2564,7 @@ void G_AddGhost(char *defdemoname) ghosts = gh; gh->version = ghostversion; - mthing = playerstarts[0]; + mthing = G_GetPlayerStart(0); I_Assert(mthing); { // A bit more complex than P_SpawnPlayer because ghosts aren't solid and won't just push themselves out of the ceiling. fixed_t z,f,c; diff --git a/src/g_game.c b/src/g_game.c index 0764f5669..f950a6b19 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -153,10 +153,13 @@ INT16 nextmapoverride; UINT8 skipstats; INT16 nextgametype = -1; -// Pointers to each CTF flag -mobj_t *flagmobjs[MAXTEAMS]; -// Pointers to CTF spawn location -mapthing_t *flagpoints[MAXTEAMS]; +// Maintain single and multi player starting spots. +playerstarts_t playerstarts; +playerstarts_t deathmatchstarts; +playerstarts_t teamstarts[MAXTEAMS]; + +mobj_t *flagmobjs[MAXTEAMS]; // Pointers to each CTF flag +mapthing_t *flagpoints[MAXTEAMS]; // Pointers to CTF flag spawn locations struct quake quake; @@ -2849,29 +2852,134 @@ void G_MovePlayerToSpawnOrStarpost(INT32 playernum) P_ResetCamera(&players[playernum], &camera2); } -enum -{ - PLAYER_START_TYPE_COOP, - PLAYER_START_TYPE_MATCH, - PLAYER_START_TYPE_TEAM -}; - static boolean G_AreCoopStartsAvailable(void) { - return numcoopstarts != 0; + return playerstarts.count != 0; } static boolean G_AreMatchStartsAvailable(void) { - return numdmstarts != 0; + return deathmatchstarts.count != 0; } static boolean G_AreTeamStartsAvailable(UINT8 team) { - if (team >= numteams) + if (team == TEAM_NONE || team >= numteams) return false; - return numteamstarts[team] != 0; + return teamstarts[team].count != 0; +} + +mapthing_t *G_GetPlayerStart(INT32 i) +{ + if (i < 0 || i >= (signed)playerstarts.count || playerstarts.list == NULL) + return NULL; + + return playerstarts.list[i]; +} + +mapthing_t *G_GetMatchPlayerStart(INT32 i) +{ + if (i < 0 || i >= (signed)deathmatchstarts.count || deathmatchstarts.list == NULL) + return NULL; + + return deathmatchstarts.list[i]; +} + +mapthing_t *G_GetTeamPlayerStart(UINT8 team, INT32 i) +{ + if (team == TEAM_NONE || team >= numteams) + return NULL; + + if (i < 0 || i >= (signed)teamstarts[team].count || teamstarts[team].list == NULL) + return NULL; + + return teamstarts[team].list[i]; +} + +mapthing_t *G_GetInitialSpawnPoint(void) +{ + if (gametyperules & GTR_DEATHMATCHSTARTS) + return G_GetMatchPlayerStart(0); + else + return G_GetPlayerStart(0); +} + +void G_InitSpawnPointList(playerstarts_t *starts, size_t capacity) +{ + Z_Free(starts->list); + starts->list = NULL; + starts->capacity = capacity; + starts->count = 0; +} + +void G_AddSpawnPointToList(playerstarts_t *starts, mapthing_t *mthing) +{ + if (starts->list == NULL || starts->count == starts->capacity) + { + if (starts->capacity == 0 || starts->list != NULL) + starts->capacity += 16; + + if (starts->list != NULL) + starts->list = Z_Realloc(starts->list, sizeof(mapthing_t *) * starts->capacity, PU_STATIC, NULL); + else + starts->list = Z_Calloc(sizeof(mapthing_t *) * starts->capacity, PU_STATIC, NULL); + } + + starts->list[starts->count++] = mthing; +} + +void G_InitSpawnPoints(void) +{ + G_InitSpawnPointList(&playerstarts, MAXPLAYERS); + G_InitSpawnPointList(&deathmatchstarts, MAX_DM_STARTS); + + for (UINT8 i = 1; i < MAXTEAMS; i++) + G_InitSpawnPointList(&teamstarts[i], MAXPLAYERS); +} + +void G_AddPlayerStart(int index, mapthing_t *mthing) +{ + if (playerstarts.list == NULL) + { + if (playerstarts.capacity == 0) + playerstarts.capacity = MAXPLAYERS; + + playerstarts.list = Z_Calloc(sizeof(mapthing_t *) * playerstarts.capacity, PU_STATIC, NULL); + } + + if (index >= 0 && index < (signed)playerstarts.capacity) + playerstarts.list[index] = mthing; +} + +void G_AddMatchPlayerStart(mapthing_t *mthing) +{ + G_AddSpawnPointToList(&deathmatchstarts, mthing); +} + +void G_AddTeamPlayerStart(UINT8 team, mapthing_t *mthing) +{ + if (team == TEAM_NONE || team >= numteams) + return; + + G_AddSpawnPointToList(&teamstarts[team], mthing); +} + +void G_CountPlayerStarts(void) +{ + playerstarts.count = 0; + + if (playerstarts.list == NULL) + return; + + for (; playerstarts.count < playerstarts.capacity; playerstarts.count++) + if (!playerstarts.list[playerstarts.count]) + break; +} + +boolean G_IsSpawnPointThingType(UINT16 mthingtype) +{ + return mthingtype >= 1 && mthingtype <= 35; } static boolean G_AreTeamStartsAvailableForPlayer(INT32 playernum) @@ -2885,13 +2993,14 @@ static boolean G_AreTeamStartsAvailableForPlayer(INT32 playernum) mapthing_t *G_FindTeamStart(INT32 playernum) { UINT8 team = players[playernum].ctfteam; + if (team != TEAM_NONE && G_AreTeamStartsAvailable(team)) { for (INT32 j = 0; j < MAXPLAYERS; j++) { - INT32 i = P_RandomKey(numteamstarts[team]); - if (G_CheckSpot(playernum, teamstarts[team][i])) - return teamstarts[team][i]; + INT32 i = P_RandomKey(teamstarts[team].count); + if (G_CheckSpot(playernum, teamstarts[team].list[i])) + return teamstarts[team].list[i]; } } @@ -2900,15 +3009,13 @@ mapthing_t *G_FindTeamStart(INT32 playernum) mapthing_t *G_FindMatchStart(INT32 playernum) { - INT32 i, j; - if (G_AreMatchStartsAvailable()) { - for (j = 0; j < 64; j++) + for (INT32 j = 0; j < 64; j++) { - i = P_RandomKey(numdmstarts); - if (G_CheckSpot(playernum, deathmatchstarts[i])) - return deathmatchstarts[i]; + INT32 i = P_RandomKey(deathmatchstarts.count); + if (G_CheckSpot(playernum, deathmatchstarts.list[i])) + return deathmatchstarts.list[i]; } } @@ -2919,13 +3026,14 @@ mapthing_t *G_FindCoopStart(INT32 playernum) { if (G_AreCoopStartsAvailable()) { - //if there's 6 players in a map with 3 player starts, this spawns them 1/2/3/1/2/3. - if (G_CheckSpot(playernum, playerstarts[playernum % numcoopstarts])) - return playerstarts[playernum % numcoopstarts]; + // if there's 6 players in a map with 3 player starts, this spawns them 1/2/3/1/2/3. + mapthing_t *spawnpoint = G_GetPlayerStart(playernum % playerstarts.count); + if (G_CheckSpot(playernum, spawnpoint)) + return spawnpoint; - //Don't bother checking to see if the player 1 start is open. - //Just spawn there. - return playerstarts[0]; + // Don't bother checking to see if the player 1 start is open. + // Just spawn there. + return G_GetPlayerStart(0); } return NULL; diff --git a/src/g_game.h b/src/g_game.h index 3cba4901c..bb427d341 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -164,6 +164,21 @@ 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); +void G_InitSpawnPointList(playerstarts_t *starts, size_t capacity); +void G_AddSpawnPointToList(playerstarts_t *starts, mapthing_t *mthing); +boolean G_IsSpawnPointThingType(UINT16 mthingtype); +mapthing_t *G_GetInitialSpawnPoint(void); + +mapthing_t *G_GetPlayerStart(INT32 i); +mapthing_t *G_GetMatchPlayerStart(INT32 i); +mapthing_t *G_GetTeamPlayerStart(UINT8 team, INT32 i); + +void G_InitSpawnPoints(void); +void G_AddPlayerStart(int index, mapthing_t *mthing); +void G_AddMatchPlayerStart(mapthing_t *mthing); +void G_AddTeamPlayerStart(UINT8 team, mapthing_t *mthing); +void G_CountPlayerStarts(void); + mapthing_t *G_FindMapStart(INT32 playernum); mapthing_t *G_FindBestPlayerStart(INT32 playernum); mapthing_t *G_FindCoopStart(INT32 playernum); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index ef440beea..18429a2b4 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -158,6 +158,8 @@ static const struct { {META_GAMETYPE, "gametype_t"}, {META_TEAM, "team_t"}, {META_TEAMLIST, "teamlist_t"}, + {META_TEAMSCORES, "teamscores"}, + {META_PLAYERSTARTS, "playerstarts"}, {META_SPRITEINFO, "spriteinfo_t"}, {META_PIVOTLIST, "spriteframepivot_t[]"}, {META_FRAMEPIVOT, "spriteframepivot_t"}, diff --git a/src/lua_infolib.c b/src/lua_infolib.c index 22f9a2218..a14081688 100644 --- a/src/lua_infolib.c +++ b/src/lua_infolib.c @@ -1910,7 +1910,7 @@ static int lib_getGametypes(lua_State *L) i = luaL_checkinteger(L, 1); if (i < 0 || i >= gametypecount) - return luaL_error(L, "gametypes[] index %d out of range (0 - %d)", i, gametypecount-1); + return luaL_error(L, "gametypes[] index %d out of range (0 - %d)", i, gametypecount - 1); LUA_PushUserdata(L, &gametypes[i], META_GAMETYPE); return 1; } @@ -2138,7 +2138,7 @@ static int lib_getTeams(lua_State *L) i = luaL_checkinteger(L, 1); if (i < 0 || i >= numteams) - return luaL_error(L, "teams[] index %d out of range (0 - %d)", i, numteams-1); + return luaL_error(L, "teams[] index %d out of range (0 - %d)", i, max(0, (int)numteams - 1)); LUA_PushUserdata(L, &teams[i], META_GAMETYPE); return 1; } @@ -2255,7 +2255,7 @@ static int lib_setTeams(lua_State *L) { teamnum = luaL_checkinteger(L, 1); if (teamnum >= numteams) - return luaL_error(L, "teams[] index %d out of range (0 - %d)", teamnum, numteams-1); + return luaL_error(L, "teams[] index %d out of range (0 - %d)", teamnum, max(0, (int)numteams - 1)); team = &teams[teamnum]; } luaL_checktype(L, 2, LUA_TTABLE); @@ -2433,7 +2433,7 @@ static int teamscores_get(lua_State *L) UINT32 *scoreslist = *((UINT32 **)luaL_checkudata(L, 1, META_TEAMSCORES)); int i = luaL_checkinteger(L, 2); if (i < 0 || i >= numteams) - return luaL_error(L, "array index %d out of range (0 - %d)", i, numteams - 1); + return luaL_error(L, "array index %d out of range (0 - %d)", i, max(0, (int)numteams - 1)); lua_pushinteger(L, scoreslist[i]); return 1; } @@ -2444,7 +2444,7 @@ static int teamscores_set(lua_State *L) int i = luaL_checkinteger(L, 2); UINT32 score = (UINT32)luaL_checkinteger(L, 3); if (i < 0 || i >= numteams) - return luaL_error(L, "array index %d out of range (0 - %d)", i, numteams - 1); + return luaL_error(L, "array index %d out of range (0 - %d)", i, max(0, (int)numteams - 1)); scoreslist[i] = score; return 0; } @@ -2455,6 +2455,61 @@ static int teamscores_len(lua_State *L) return 1; } +/////////////////// +// PLAYER STARTS // +/////////////////// + +static int playerstarts_get(lua_State *L) +{ + playerstarts_t *starts = *((playerstarts_t **)luaL_checkudata(L, 1, META_PLAYERSTARTS)); + int i = luaL_checkinteger(L, 2); + if (i < 0 || i >= (signed)starts->count) + return luaL_error(L, "player start index %d out of range (0 - %d)", i, max(0, (int)starts->count - 1)); + LUA_PushUserdata(L, starts->list[i], META_MAPTHING); + return 1; +} + +static int playerstarts_set(lua_State *L) +{ + playerstarts_t *starts = *((playerstarts_t **)luaL_checkudata(L, 1, META_PLAYERSTARTS)); + int i = luaL_checkinteger(L, 2); + mapthing_t *mthing = *((mapthing_t **)luaL_checkudata(L, 3, META_MAPTHING)); + if (i < 0 || i >= (signed)starts->count) + return luaL_error(L, "player start index %d out of range (0 - %d)", i, max(0, (int)starts->count - 1)); + if (!mthing) + return LUA_ErrInvalid(L, "mapthing_t"); + starts->list[i] = mthing; + return 0; +} + +static int playerstarts_len(lua_State *L) +{ + playerstarts_t *starts = *((playerstarts_t **)luaL_checkudata(L, 1, META_PLAYERSTARTS)); + lua_pushinteger(L, starts->count); + return 1; +} + +static int lib_getTeamstarts(lua_State *L) +{ + int i; + lua_remove(L, 1); + + i = luaL_checkinteger(L, 1); + if (i <= 0 || i >= numteams) + return luaL_error(L, "team index %d out of range (1 - %d)", i, numteams - 1); + + LUA_PushUserdata(L, &teamstarts[i], META_PLAYERSTARTS); + + return 1; +} + +// #teamstarts -> MAXTEAMS +static int lib_teamstartslen(lua_State *L) +{ + lua_pushinteger(L, MAXTEAMS); + return 1; +} + ////////////////////////////// // // Now push all these functions into the Lua state! @@ -2542,6 +2597,17 @@ int LUA_InfoLib(lua_State *L) lua_setfield(L, -2, "__len"); lua_pop(L, 1); + luaL_newmetatable(L, META_PLAYERSTARTS); + lua_pushcfunction(L, playerstarts_get); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, playerstarts_set); + lua_setfield(L, -2, "__newindex"); + + lua_pushcfunction(L, playerstarts_len); + lua_setfield(L, -2, "__len"); + lua_pop(L, 1); + luaL_newmetatable(L, META_SKINCOLOR); lua_pushcfunction(L, skincolor_get); lua_setfield(L, -2, "__index"); @@ -2731,6 +2797,16 @@ int LUA_InfoLib(lua_State *L) lua_setmetatable(L, -2); lua_setglobal(L, "teams"); + lua_newuserdata(L, 0); + lua_createtable(L, 0, 2); + lua_pushcfunction(L, lib_getTeamstarts); + lua_setfield(L, -2, "__index"); + + lua_pushcfunction(L, lib_teamstartslen); + lua_setfield(L, -2, "__len"); + lua_setmetatable(L, -2); + lua_setglobal(L, "teamstarts"); + luaL_newmetatable(L, META_LUABANKS); lua_pushcfunction(L, lib_getluabanks); lua_setfield(L, -2, "__index"); diff --git a/src/lua_libs.h b/src/lua_libs.h index f06daf4b6..39d7ebe57 100644 --- a/src/lua_libs.h +++ b/src/lua_libs.h @@ -32,6 +32,7 @@ extern boolean mousegrabbedbylua; #define META_TEAM "TEAM_T*" #define META_TEAMLIST "TEAMLIST_T*" #define META_TEAMSCORES "TEAMSCORES" +#define META_PLAYERSTARTS "PLAYERSTARTS" #define META_PIVOTLIST "SPRITEFRAMEPIVOT_T[]" #define META_FRAMEPIVOT "SPRITEFRAMEPIVOT_T*" diff --git a/src/lua_script.c b/src/lua_script.c index 2badde0e6..7dc692600 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -213,6 +213,12 @@ int LUA_PushGlobals(lua_State *L, const char *word) } else if (fastcmp(word,"paused")) { lua_pushboolean(L, paused); return 1; + } else if (fastcmp(word, "playerstarts")) { + LUA_PushUserdata(L, &playerstarts, META_PLAYERSTARTS); + return 1; + } else if (fastcmp(word, "matchstarts")) { + LUA_PushUserdata(L, &deathmatchstarts, META_PLAYERSTARTS); + return 1; } else if (fastcmp(word,"bluescore")) { lua_pushinteger(L, teamscores[G_GetTeam(2)]); return 1; diff --git a/src/m_cheat.c b/src/m_cheat.c index 920e4df49..fe51a9576 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -513,13 +513,14 @@ void Command_Teleport_f(void) mapthing_t *mt; fixed_t offset; - if (starpostpath >= numcoopstarts) + int numstarts = (signed)playerstarts.count; + if (starpostpath >= numstarts) { - CONS_Alert(CONS_NOTICE, M_GetText("Player %d spawnpoint not found (%d max).\n"), starpostpath+1, numcoopstarts-1); + CONS_Alert(CONS_NOTICE, M_GetText("Player %d spawnpoint not found (%d max).\n"), starpostpath+1, numstarts-1); return; } - mt = playerstarts[starpostpath]; // Given above check, should never be NULL. + mt = G_GetPlayerStart(starpostpath); // Given above check, should never be NULL. intx = mt->x<y<z<scale); } -static void P_SetTeamStart(UINT8 team, mapthing_t *mthing) -{ - if (team != TEAM_NONE && team < numteams && numteamstarts[team] < MAXPLAYERS) - { - teamstarts[team][numteamstarts[team]] = mthing; - numteamstarts[team]++; - numteamstarts[0]++; - mthing->type = 0; - } -} - static boolean P_SpawnNonMobjMapThing(mapthing_t *mthing) { #if MAXPLAYERS > 32 @@ -11953,33 +11944,28 @@ static boolean P_SpawnNonMobjMapThing(mapthing_t *mthing) { // save spots for respawning in network games if (!metalrecording) - playerstarts[mthing->type - 1] = mthing; + G_AddPlayerStart(mthing->type - 1, mthing); return true; } else if (mthing->type == 33) // Match starts { - if (numdmstarts < MAX_DM_STARTS) - { - deathmatchstarts[numdmstarts] = mthing; - mthing->type = 0; - numdmstarts++; - } + G_AddMatchPlayerStart(mthing); return true; } else if (mthing->type == 34) // Red CTF starts { - P_SetTeamStart(G_GetTeam(TEAM_RED), mthing); + G_AddTeamPlayerStart(G_GetTeam(TEAM_RED), mthing); return true; } else if (mthing->type == 35) // Blue CTF starts { - P_SetTeamStart(G_GetTeam(TEAM_BLUE), mthing); + G_AddTeamPlayerStart(G_GetTeam(TEAM_BLUE), mthing); return true; } else if (metalrecording && mthing->type == mobjinfo[MT_METALSONIC_RACE].doomednum) { // If recording, you ARE Metal Sonic. Do not spawn it, do not save normal spawnpoints. - playerstarts[0] = mthing; + G_AddPlayerStart(0, mthing); return true; } else if (mthing->type == 750 // Slope vertex point (formerly chaos spawn) @@ -13402,11 +13388,6 @@ static mobj_t *P_SpawnMobjFromMapThing(mapthing_t *mthing, fixed_t x, fixed_t y, return mobj; } -// -// P_SpawnMapThing -// The fields of the mapthing should -// already be in host byte order. -// mobj_t *P_SpawnMapThing(mapthing_t *mthing) { mobjtype_t i; @@ -13443,6 +13424,14 @@ mobj_t *P_SpawnMapThing(mapthing_t *mthing) return P_SpawnMobjFromMapThing(mthing, x, y, z, i); } +static mobj_t *P_RespawnMapThing(mapthing_t *mthing) +{ + if (G_IsSpawnPointThingType(mthing->type)) + return NULL; + + return P_SpawnMapThing(mthing); +} + void P_SpawnHoop(mapthing_t *mthing) { if (metalrecording) diff --git a/src/p_saveg.c b/src/p_saveg.c index 33e3aa464..568ab221c 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -4300,6 +4300,101 @@ static inline void P_UnArchiveSPGame(INT16 mapoverride) playeringame[consoleplayer] = true; } +static void ArchiveSpawnPoints(void) +{ + // Write player starts + WRITEUINT8(save_p, playerstarts.capacity); + for (size_t i = 0; i < playerstarts.capacity; i++) + { + if (playerstarts.list[i] == NULL) + WRITEUINT16(save_p, 0xFFFF); + else + { + size_t mapthingnum = playerstarts.list[i] - mapthings; + WRITEUINT16(save_p, mapthingnum); + } + } + + // Write Match starts + WRITEUINT8(save_p, deathmatchstarts.count); + for (size_t i = 0; i < deathmatchstarts.count; i++) + { + size_t mapthingnum = deathmatchstarts.list[i] - mapthings; + WRITEUINT16(save_p, mapthingnum); + } + + // Write team starts + for (UINT8 team = 1; team < numteams; team++) + { + WRITEUINT8(save_p, teamstarts[team].count); + for (size_t i = 0; i < teamstarts[team].count; i++) + { + size_t mapthingnum = teamstarts[team].list[i] - mapthings; + WRITEUINT16(save_p, mapthingnum); + } + } +} + +static boolean ReadPlayerStarts(playerstarts_t *starts) +{ + size_t count = (size_t)READUINT8(save_p); + + for (size_t i = 0; i < count; i++) + { + UINT16 mapthingnum = READUINT16(save_p); + if (mapthingnum < nummapthings) + G_AddSpawnPointToList(starts, &mapthings[mapthingnum]); + else + { + CONS_Alert(CONS_ERROR, "Player start %d has invalid map thing number %d\n", (int)i, mapthingnum); + return false; + } + } + + return true; +} + +static boolean RestoreSpawnPoints(void) +{ + // Read player starts + playerstarts.capacity = READUINT8(save_p); + + for (size_t i = 0; i < playerstarts.capacity; i++) + { + UINT16 mapthingnum = READUINT16(save_p); + if (mapthingnum == 0xFFFF) + G_AddPlayerStart(i, NULL); + else if (mapthingnum < nummapthings) + G_AddPlayerStart(i, &mapthings[mapthingnum]); + else + { + CONS_Alert(CONS_ERROR, "Player start %d has invalid map thing number %d\n", (int)i, mapthingnum); + return false; + } + } + + G_CountPlayerStarts(); + + // Read Match starts + if (!ReadPlayerStarts(&deathmatchstarts)) + { + CONS_Alert(CONS_ERROR, "Failed to read Match player starts\n"); + return false; + } + + // Read team starts + for (UINT8 team = 1; team < numteams; team++) + { + if (!ReadPlayerStarts(&teamstarts[team])) + { + CONS_Alert(CONS_ERROR, "Failed to read team %d player starts\n", team); + return false; + } + } + + return true; +} + static void P_NetArchiveMisc(boolean resending) { INT32 i; @@ -4349,6 +4444,8 @@ static void P_NetArchiveMisc(boolean resending) for (i = 0; i < MAXTEAMS; i++) WRITEUINT32(save_p, teamscores[i]); + ArchiveSpawnPoints(); + WRITEINT32(save_p, modulothing); WRITEINT16(save_p, autobalance); @@ -4444,6 +4541,9 @@ static inline boolean P_NetUnArchiveMisc(boolean reloading) for (i = 0; i < MAXTEAMS; i++) teamscores[i] = READUINT32(save_p); + if (!RestoreSpawnPoints()) + I_Error("Savegame corrupted"); + modulothing = READINT32(save_p); autobalance = READINT16(save_p); diff --git a/src/p_setup.c b/src/p_setup.c index 1b3f5b4ef..8cf3fdd65 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -143,13 +143,6 @@ mobj_t **blocklinks; // UINT8 *rejectmatrix; -// Maintain single and multi player starting spots. -INT32 numdmstarts, numcoopstarts, numteamstarts[MAXTEAMS]; - -mapthing_t *deathmatchstarts[MAX_DM_STARTS]; -mapthing_t *playerstarts[MAXPLAYERS]; -mapthing_t *teamstarts[MAXTEAMS][MAXPLAYERS]; - // Maintain waypoints mobj_t *waypoints[NUMWAYPOINTSEQUENCES][WAYPOINTSEQUENCESIZE]; UINT16 numwaypoints[NUMWAYPOINTSEQUENCES]; @@ -870,7 +863,7 @@ static void P_SpawnEmeraldHunt(void) } } -static void P_SpawnMapThings(boolean spawnemblems) +static void P_SpawnMapThings(boolean respawning, boolean fromnetsave) { size_t i; mapthing_t *mt; @@ -895,22 +888,31 @@ static void P_SpawnMapThings(boolean spawnemblems) for (i = 0, mt = mapthings; i < nummapthings; i++, mt++) { - if (mt->type == 1700 // MT_AXIS - || mt->type == 1701 // MT_AXISTRANSFER - || mt->type == 1702) // MT_AXISTRANSFERLINE + UINT16 type = mt->type; + + if (type == 1700 // MT_AXIS + || type == 1701 // MT_AXISTRANSFER + || type == 1702) // MT_AXISTRANSFERLINE continue; // These were already spawned - if (!spawnemblems && mt->type == mobjinfo[MT_EMBLEM].doomednum) + if (fromnetsave && type == mobjinfo[MT_EMBLEM].doomednum) continue; mt->mobj = NULL; - if (mt->type >= 600 && mt->type <= 611) // item patterns + if (type >= 600 && type <= 611) // item patterns P_SpawnItemPattern(mt, false); - else if (mt->type == 1713) // hoops + else if (type == 1713) // hoops P_SpawnHoop(mt); else // Everything else + { + // Don't respawn player starts if the game is either respawning all things, + // or if the game is loading a savegame. + if (G_IsSpawnPointThingType(mt->type) && (respawning || fromnetsave)) + continue; + P_SpawnMapThing(mt); + } } // random emeralds for hunt @@ -7158,7 +7160,7 @@ void P_RespawnThings(void) localaiming = 0; localaiming2 = 0; - P_SpawnMapThings(true); + P_SpawnMapThings(true, false); // restore skybox viewpoint/centerpoint if necessary, set them to defaults if we can't do that skyboxmo[0] = skyboxviewpnts[(viewid >= 0) ? viewid : 0]; @@ -7231,24 +7233,9 @@ static void P_ForceCharacter(const char *forcecharskin) static void P_ResetSpawnpoints(void) { - UINT8 i, j; + UINT8 i; - numdmstarts = 0; - - // reset the player starts - for (i = 0; i < MAXPLAYERS; i++) - playerstarts[i] = NULL; - - for (i = 0; i < MAX_DM_STARTS; i++) - deathmatchstarts[i] = NULL; - - for (i = 0; i < MAXTEAMS; i++) - { - numteamstarts[i] = 0; - - for (j = 0; j < MAXPLAYERS; j++) - teamstarts[i][j] = NULL; - } + G_InitSpawnPoints(); for (i = 0; i < 2; i++) skyboxmo[i] = NULL; @@ -7435,12 +7422,7 @@ static void P_SetupCamera(void) } else { - mapthing_t *thing; - - if (gametyperules & GTR_DEATHMATCHSTARTS) - thing = deathmatchstarts[0]; - else - thing = playerstarts[0]; + mapthing_t *thing = G_GetInitialSpawnPoint(); if (thing) { @@ -7877,13 +7859,12 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) P_SpawnSlopes(fromnetsave); - P_SpawnMapThings(!fromnetsave); + P_SpawnMapThings(false, fromnetsave); skyboxmo[0] = skyboxviewpnts[0]; skyboxmo[1] = skyboxcenterpnts[0]; - for (numcoopstarts = 0; numcoopstarts < MAXPLAYERS; numcoopstarts++) - if (!playerstarts[numcoopstarts]) - break; + if (!fromnetsave) + G_CountPlayerStarts(); // set up world state P_SpawnSpecials(fromnetsave); diff --git a/src/p_setup.h b/src/p_setup.h index 6e00a5b5c..63bd90117 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -21,11 +21,6 @@ // map md5, sent to players via PT_SERVERINFO extern unsigned char mapmd5[16]; -// Player spawn spots for deathmatch. -#define MAX_DM_STARTS 64 -extern mapthing_t *deathmatchstarts[MAX_DM_STARTS]; -extern INT32 numdmstarts, numcoopstarts, numteamstarts[MAXTEAMS]; - extern boolean levelloading; extern UINT8 levelfadecol;