diff --git a/src/doomdata.h b/src/doomdata.h index 64ac86e5e..e9b86781e 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -405,6 +405,7 @@ extern TArray deathmatchstarts; // Player spawn spots. extern FMapThing playerstarts[MAXPLAYERS]; +extern TArray AllPlayerStarts; #endif // __DOOMDATA__ diff --git a/src/g_game.cpp b/src/g_game.cpp index 934e36d5e..976876087 100644 --- a/src/g_game.cpp +++ b/src/g_game.cpp @@ -87,6 +87,7 @@ static FRandom pr_dmspawn ("DMSpawn"); +static FRandom pr_pspawn ("PlayerSpawn"); const int SAVEPICWIDTH = 216; const int SAVEPICHEIGHT = 162; @@ -1505,8 +1506,8 @@ void G_DeathMatchSpawnPlayer (int playernum) { // No good spot, so the player will probably get stuck. // We were probably using select farthest above, and all // the spots were taken. - spot = &playerstarts[playernum]; - if (spot == NULL || spot->type == 0) + spot = G_PickPlayerStart(playernum, PPS_FORCERANDOM); + if (!G_CheckSpot(playernum, spot)) { // This map doesn't have enough coop spots for this player // to use one. spot = SelectRandomDeathmatchSpot(playernum, selections); @@ -1520,11 +1521,41 @@ void G_DeathMatchSpawnPlayer (int playernum) } } } - AActor *mo = P_SpawnPlayer(spot, playernum); if (mo != NULL) P_PlayerStartStomp(mo); } +// +// G_PickPlayerStart +// +FMapThing *G_PickPlayerStart(int playernum, int flags) +{ + if ((level.flags2 & LEVEL2_RANDOMPLAYERSTARTS) || (flags & PPS_FORCERANDOM)) + { + if (!(flags & PPS_NOBLOCKINGCHECK)) + { + TArray good_starts; + unsigned int i; + + // Find all unblocked player starts. + for (i = 0; i < AllPlayerStarts.Size(); ++i) + { + if (G_CheckSpot(playernum, &AllPlayerStarts[i])) + { + good_starts.Push(&AllPlayerStarts[i]); + } + } + if (good_starts.Size() > 0) + { // Pick an open spot at random. + return good_starts[pr_pspawn(good_starts.Size())]; + } + } + // Pick a spot at random, whether it's open or not. + return &AllPlayerStarts[pr_pspawn(AllPlayerStarts.Size())]; + } + return &playerstarts[playernum]; +} + // // G_QueueBody // @@ -1576,8 +1607,6 @@ void G_DoReborn (int playernum, bool freshbot) else { // respawn at the start - int i; - // first disassociate the corpse if (players[playernum].mo) { @@ -1585,32 +1614,23 @@ void G_DoReborn (int playernum, bool freshbot) players[playernum].mo->player = NULL; } - // spawn at random spot if in death match + // spawn at random spot if in deathmatch if (deathmatch) { G_DeathMatchSpawnPlayer (playernum); return; } - if (G_CheckSpot (playernum, &playerstarts[playernum]) ) + if (!(level.flags2 & LEVEL2_RANDOMPLAYERSTARTS) && + G_CheckSpot (playernum, &playerstarts[playernum])) { AActor *mo = P_SpawnPlayer(&playerstarts[playernum], playernum); if (mo != NULL) P_PlayerStartStomp(mo); } else - { - // try to spawn at one of the other players' spots - for (i = 0; i < MAXPLAYERS; i++) - { - if (G_CheckSpot (playernum, &playerstarts[i]) ) - { - AActor *mo = P_SpawnPlayer(&playerstarts[i], playernum); - if (mo != NULL) P_PlayerStartStomp(mo); - return; - } - // he's going to be inside something. Too bad. - } - AActor *mo = P_SpawnPlayer(&playerstarts[playernum], playernum); + { // try to spawn at any random player's spot + FMapThing *start = G_PickPlayerStart(playernum, PPS_FORCERANDOM); + AActor *mo = P_SpawnPlayer(start, playernum); if (mo != NULL) P_PlayerStartStomp(mo); } } @@ -1624,8 +1644,6 @@ void G_ScreenShot (char *filename) - - // // G_InitFromSavegame // Can be called by the startup code or the menu task. diff --git a/src/g_game.h b/src/g_game.h index 65ae77563..d5aec5ead 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -32,6 +32,13 @@ struct PNGHandle; // void G_DeathMatchSpawnPlayer (int playernum); +struct FMapThing *G_PickPlayerStart (int playernum, int flags = 0); +enum +{ + PPS_FORCERANDOM = 1, + PPS_NOBLOCKINGCHECK = 2, +}; + void G_DeferedPlayDemo (const char* demo); // Can be called by the startup code or M_Responder, diff --git a/src/g_hexen/a_hexenmisc.cpp b/src/g_hexen/a_hexenmisc.cpp index 61e3f31f8..fa07a6759 100644 --- a/src/g_hexen/a_hexenmisc.cpp +++ b/src/g_hexen/a_hexenmisc.cpp @@ -19,6 +19,7 @@ #include "ravenshared.h" #include "farchive.h" #include "v_palette.h" +#include "g_game.h" // Include all the Hexen stuff here to reduce compile time #include "a_bats.cpp" diff --git a/src/g_hexen/a_teleportother.cpp b/src/g_hexen/a_teleportother.cpp index 8bd17b62b..ea99f48f0 100644 --- a/src/g_hexen/a_teleportother.cpp +++ b/src/g_hexen/a_teleportother.cpp @@ -155,19 +155,13 @@ int ATelOtherFX1::DoSpecialDamage (AActor *target, int damage, FName damagetype) void P_TeleportToPlayerStarts (AActor *victim) { - int i,selections=0; fixed_t destX,destY; angle_t destAngle; - for (i = 0; i < MAXPLAYERS;i++) - { - if (!playeringame[i]) continue; - selections++; - } - i = pr_telestarts() % selections; - destX = playerstarts[i].x; - destY = playerstarts[i].y; - destAngle = ANG45 * (playerstarts[i].angle/45); + FMapThing *start = G_PickPlayerStart(0, PPS_FORCERANDOM | PPS_NOBLOCKINGCHECK); + destX = start->x; + destY = start->y; + destAngle = ANG45 * (start->angle/45); P_Teleport (victim, destX, destY, ONFLOORZ, destAngle, true, true, false); } diff --git a/src/g_level.cpp b/src/g_level.cpp index 6f2c5e164..e6b8bad91 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -1121,7 +1121,7 @@ void G_FinishTravel () // The player being spawned here is a short lived dummy and // must not start any ENTER script or big problems will happen. - pawndup = P_SpawnPlayer (&playerstarts[pawn->player - players], pawn->player - players, true); + pawndup = P_SpawnPlayer (&playerstarts[pawn->player - players], int(pawn->player - players), true); if (!(changeflags & CHANGELEVEL_KEEPFACING)) { pawn->angle = pawndup->angle; diff --git a/src/g_level.h b/src/g_level.h index 522ea2478..bc30b5603 100644 --- a/src/g_level.h +++ b/src/g_level.h @@ -136,7 +136,7 @@ enum ELevelFlags LEVEL_SPECLOWERFLOOR = 0x00000100, LEVEL_SPECOPENDOOR = 0x00000200, - LEVEL_SPECLOWERFLOORTOHIGHEST= 0x00000300, + LEVEL_SPECLOWERFLOORTOHIGHEST=0x00000300, LEVEL_SPECACTIONSMASK = 0x00000300, LEVEL_MONSTERSTELEFRAG = 0x00000400, @@ -170,13 +170,13 @@ enum ELevelFlags LEVEL_VISITED = 0x80000000, // Used for intermission map // The flags QWORD is now split into 2 DWORDs - //LEVEL2_DEATHSLIDESHOW = 0x00000001, // Slideshow on death + LEVEL2_RANDOMPLAYERSTARTS = 0x00000001, // Select single player starts randomnly (no voodoo dolls) LEVEL2_ALLMAP = 0x00000002, // The player picked up a map on this level LEVEL2_LAXMONSTERACTIVATION = 0x00000004, // Monsters can open doors depending on the door speed LEVEL2_LAXACTIVATIONMAPINFO = 0x00000008, // LEVEL_LAXMONSTERACTIVATION is not a default. - LEVEL2_MISSILESACTIVATEIMPACT = 0x00000010, // Missiles are the activators of SPAC_IMPACT events, not their shooters + LEVEL2_MISSILESACTIVATEIMPACT=0x00000010, // Missiles are the activators of SPAC_IMPACT events, not their shooters LEVEL2_FROZEN = 0x00000020, // Game is frozen by a TimeFreezer LEVEL2_KEEPFULLINVENTORY = 0x00000040, // doesn't reduce the amount of inventory items to 1 diff --git a/src/g_mapinfo.cpp b/src/g_mapinfo.cpp index 51ae640ee..38440c8f0 100644 --- a/src/g_mapinfo.cpp +++ b/src/g_mapinfo.cpp @@ -1209,12 +1209,13 @@ MapFlagHandlers[] = { "noallies", MITYPE_SETFLAG, LEVEL_NOALLIES, 0 }, { "filterstarts", MITYPE_SETFLAG, LEVEL_FILTERSTARTS, 0 }, { "useplayerstartz", MITYPE_SETFLAG, LEVEL_USEPLAYERSTARTZ, 0 }, + { "randomplayerstarts", MITYPE_SETFLAG2, LEVEL2_RANDOMPLAYERSTARTS, 0 }, { "activateowndeathspecials", MITYPE_SETFLAG, LEVEL_ACTOWNSPECIAL, 0 }, { "killeractivatesdeathspecials", MITYPE_CLRFLAG, LEVEL_ACTOWNSPECIAL, 0 }, { "missilesactivateimpactlines", MITYPE_SETFLAG2, LEVEL2_MISSILESACTIVATEIMPACT, 0 }, { "missileshootersactivetimpactlines",MITYPE_CLRFLAG2, LEVEL2_MISSILESACTIVATEIMPACT, 0 }, { "noinventorybar", MITYPE_SETFLAG, LEVEL_NOINVENTORYBAR, 0 }, - { "deathslideshow", MITYPE_SETFLAG2, 0, 0 }, + { "deathslideshow", MITYPE_IGNORE, 0, 0 }, { "strictmonsteractivation", MITYPE_CLRFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO }, { "laxmonsteractivation", MITYPE_SETFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO }, { "additive_scrollers", MITYPE_COMPATFLAG, COMPATF_BOOMSCROLL, 0 }, diff --git a/src/g_raven/a_artitele.cpp b/src/g_raven/a_artitele.cpp index 9828f9581..9f4bb939c 100644 --- a/src/g_raven/a_artitele.cpp +++ b/src/g_raven/a_artitele.cpp @@ -7,6 +7,7 @@ #include "s_sound.h" #include "m_random.h" #include "doomstat.h" +#include "g_game.h" static FRandom pr_tele ("TeleportSelf"); @@ -37,9 +38,10 @@ bool AArtiTeleport::Use (bool pickup) } else { - destX = playerstarts[Owner->player - players].x; - destY = playerstarts[Owner->player - players].y; - destAngle = ANG45 * (playerstarts[Owner->player - players].angle/45); + FMapThing *start = G_PickPlayerStart(int(Owner->player - players)); + destX = start->x; + destY = start->y; + destAngle = ANG45 * (start->angle/45); } P_Teleport (Owner, destX, destY, ONFLOORZ, destAngle, true, true, false); bool canlaugh = true; diff --git a/src/p_mobj.cpp b/src/p_mobj.cpp index 4da09b01b..4cfc25a8e 100644 --- a/src/p_mobj.cpp +++ b/src/p_mobj.cpp @@ -4481,9 +4481,11 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position) // save spots for respawning in network games playerstarts[pnum] = *mthing; - if (!deathmatch) + AllPlayerStarts.Push(*mthing); + if (!deathmatch && !(level.flags2 & LEVEL2_RANDOMPLAYERSTARTS)) + { return P_SpawnPlayer(&playerstarts[pnum], pnum); - + } return NULL; } diff --git a/src/p_setup.cpp b/src/p_setup.cpp index f38213d71..4c98c7d90 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -184,6 +184,7 @@ bool ForceNodeBuild; // Maintain single and multi player starting spots. TArray deathmatchstarts (16); FMapThing playerstarts[MAXPLAYERS]; +TArray AllPlayerStarts; static void P_AllocateSideDefs (int count); @@ -3910,7 +3911,9 @@ void P_SetupLevel (char *lumpname, int position) for (i = 0; i < BODYQUESIZE; i++) bodyque[i] = NULL; - deathmatchstarts.Clear (); + deathmatchstarts.Clear(); + AllPlayerStarts.Clear(); + memset(playerstarts, 0, sizeof(playerstarts)); if (!buildmap) { @@ -3977,6 +3980,19 @@ void P_SetupLevel (char *lumpname, int position) } } } + // the same, but for random single/coop player starts + else if (level.flags2 & LEVEL2_RANDOMPLAYERSTARTS) + { + for (i = 0; i < MAXPLAYERS; ++i) + { + if (playeringame[i]) + { + players[i].mo = NULL; + FMapThing *mthing = G_PickPlayerStart(i); + P_SpawnPlayer(mthing, i); + } + } + } // Don't count monsters in end-of-level sectors if option is on if (dmflags2 & DF2_NOCOUNTENDMONST)