- Added MAPINFO flag RandomPlayerStarts. In this mode, no voodoo dolls are spawned. Instead, all

player starts are added to a pool, and players spawn at a random spot.

SVN r3749 (trunk)
This commit is contained in:
Randy Heit 2012-07-08 01:43:47 +00:00
parent 390fd5dd6c
commit 71601f91d1
11 changed files with 85 additions and 43 deletions

View file

@ -405,6 +405,7 @@ extern TArray<FMapThing> deathmatchstarts;
// Player spawn spots. // Player spawn spots.
extern FMapThing playerstarts[MAXPLAYERS]; extern FMapThing playerstarts[MAXPLAYERS];
extern TArray<FMapThing> AllPlayerStarts;
#endif // __DOOMDATA__ #endif // __DOOMDATA__

View file

@ -87,6 +87,7 @@
static FRandom pr_dmspawn ("DMSpawn"); static FRandom pr_dmspawn ("DMSpawn");
static FRandom pr_pspawn ("PlayerSpawn");
const int SAVEPICWIDTH = 216; const int SAVEPICWIDTH = 216;
const int SAVEPICHEIGHT = 162; const int SAVEPICHEIGHT = 162;
@ -1505,8 +1506,8 @@ void G_DeathMatchSpawnPlayer (int playernum)
{ // No good spot, so the player will probably get stuck. { // No good spot, so the player will probably get stuck.
// We were probably using select farthest above, and all // We were probably using select farthest above, and all
// the spots were taken. // the spots were taken.
spot = &playerstarts[playernum]; spot = G_PickPlayerStart(playernum, PPS_FORCERANDOM);
if (spot == NULL || spot->type == 0) if (!G_CheckSpot(playernum, spot))
{ // This map doesn't have enough coop spots for this player { // This map doesn't have enough coop spots for this player
// to use one. // to use one.
spot = SelectRandomDeathmatchSpot(playernum, selections); spot = SelectRandomDeathmatchSpot(playernum, selections);
@ -1520,11 +1521,41 @@ void G_DeathMatchSpawnPlayer (int playernum)
} }
} }
} }
AActor *mo = P_SpawnPlayer(spot, playernum); AActor *mo = P_SpawnPlayer(spot, playernum);
if (mo != NULL) P_PlayerStartStomp(mo); 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<FMapThing *> 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 // G_QueueBody
// //
@ -1576,8 +1607,6 @@ void G_DoReborn (int playernum, bool freshbot)
else else
{ {
// respawn at the start // respawn at the start
int i;
// first disassociate the corpse // first disassociate the corpse
if (players[playernum].mo) if (players[playernum].mo)
{ {
@ -1592,25 +1621,16 @@ void G_DoReborn (int playernum, bool freshbot)
return; return;
} }
if (G_CheckSpot (playernum, &playerstarts[playernum]) ) if (!(level.flags2 & LEVEL2_RANDOMPLAYERSTARTS) &&
G_CheckSpot (playernum, &playerstarts[playernum]))
{ {
AActor *mo = P_SpawnPlayer(&playerstarts[playernum], playernum); AActor *mo = P_SpawnPlayer(&playerstarts[playernum], playernum);
if (mo != NULL) P_PlayerStartStomp(mo); if (mo != NULL) P_PlayerStartStomp(mo);
} }
else else
{ { // try to spawn at any random player's spot
// try to spawn at one of the other players' spots FMapThing *start = G_PickPlayerStart(playernum, PPS_FORCERANDOM);
for (i = 0; i < MAXPLAYERS; i++) AActor *mo = P_SpawnPlayer(start, playernum);
{
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);
if (mo != NULL) P_PlayerStartStomp(mo); if (mo != NULL) P_PlayerStartStomp(mo);
} }
} }
@ -1624,8 +1644,6 @@ void G_ScreenShot (char *filename)
// //
// G_InitFromSavegame // G_InitFromSavegame
// Can be called by the startup code or the menu task. // Can be called by the startup code or the menu task.

View file

@ -32,6 +32,13 @@ struct PNGHandle;
// //
void G_DeathMatchSpawnPlayer (int playernum); 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); void G_DeferedPlayDemo (const char* demo);
// Can be called by the startup code or M_Responder, // Can be called by the startup code or M_Responder,

View file

@ -19,6 +19,7 @@
#include "ravenshared.h" #include "ravenshared.h"
#include "farchive.h" #include "farchive.h"
#include "v_palette.h" #include "v_palette.h"
#include "g_game.h"
// Include all the Hexen stuff here to reduce compile time // Include all the Hexen stuff here to reduce compile time
#include "a_bats.cpp" #include "a_bats.cpp"

View file

@ -155,19 +155,13 @@ int ATelOtherFX1::DoSpecialDamage (AActor *target, int damage, FName damagetype)
void P_TeleportToPlayerStarts (AActor *victim) void P_TeleportToPlayerStarts (AActor *victim)
{ {
int i,selections=0;
fixed_t destX,destY; fixed_t destX,destY;
angle_t destAngle; angle_t destAngle;
for (i = 0; i < MAXPLAYERS;i++) FMapThing *start = G_PickPlayerStart(0, PPS_FORCERANDOM | PPS_NOBLOCKINGCHECK);
{ destX = start->x;
if (!playeringame[i]) continue; destY = start->y;
selections++; destAngle = ANG45 * (start->angle/45);
}
i = pr_telestarts() % selections;
destX = playerstarts[i].x;
destY = playerstarts[i].y;
destAngle = ANG45 * (playerstarts[i].angle/45);
P_Teleport (victim, destX, destY, ONFLOORZ, destAngle, true, true, false); P_Teleport (victim, destX, destY, ONFLOORZ, destAngle, true, true, false);
} }

View file

@ -1121,7 +1121,7 @@ void G_FinishTravel ()
// The player being spawned here is a short lived dummy and // The player being spawned here is a short lived dummy and
// must not start any ENTER script or big problems will happen. // 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)) if (!(changeflags & CHANGELEVEL_KEEPFACING))
{ {
pawn->angle = pawndup->angle; pawn->angle = pawndup->angle;

View file

@ -170,7 +170,7 @@ enum ELevelFlags
LEVEL_VISITED = 0x80000000, // Used for intermission map LEVEL_VISITED = 0x80000000, // Used for intermission map
// The flags QWORD is now split into 2 DWORDs // 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_ALLMAP = 0x00000002, // The player picked up a map on this level
LEVEL2_LAXMONSTERACTIVATION = 0x00000004, // Monsters can open doors depending on the door speed LEVEL2_LAXMONSTERACTIVATION = 0x00000004, // Monsters can open doors depending on the door speed

View file

@ -1209,12 +1209,13 @@ MapFlagHandlers[] =
{ "noallies", MITYPE_SETFLAG, LEVEL_NOALLIES, 0 }, { "noallies", MITYPE_SETFLAG, LEVEL_NOALLIES, 0 },
{ "filterstarts", MITYPE_SETFLAG, LEVEL_FILTERSTARTS, 0 }, { "filterstarts", MITYPE_SETFLAG, LEVEL_FILTERSTARTS, 0 },
{ "useplayerstartz", MITYPE_SETFLAG, LEVEL_USEPLAYERSTARTZ, 0 }, { "useplayerstartz", MITYPE_SETFLAG, LEVEL_USEPLAYERSTARTZ, 0 },
{ "randomplayerstarts", MITYPE_SETFLAG2, LEVEL2_RANDOMPLAYERSTARTS, 0 },
{ "activateowndeathspecials", MITYPE_SETFLAG, LEVEL_ACTOWNSPECIAL, 0 }, { "activateowndeathspecials", MITYPE_SETFLAG, LEVEL_ACTOWNSPECIAL, 0 },
{ "killeractivatesdeathspecials", MITYPE_CLRFLAG, LEVEL_ACTOWNSPECIAL, 0 }, { "killeractivatesdeathspecials", MITYPE_CLRFLAG, LEVEL_ACTOWNSPECIAL, 0 },
{ "missilesactivateimpactlines", MITYPE_SETFLAG2, LEVEL2_MISSILESACTIVATEIMPACT, 0 }, { "missilesactivateimpactlines", MITYPE_SETFLAG2, LEVEL2_MISSILESACTIVATEIMPACT, 0 },
{ "missileshootersactivetimpactlines",MITYPE_CLRFLAG2, LEVEL2_MISSILESACTIVATEIMPACT, 0 }, { "missileshootersactivetimpactlines",MITYPE_CLRFLAG2, LEVEL2_MISSILESACTIVATEIMPACT, 0 },
{ "noinventorybar", MITYPE_SETFLAG, LEVEL_NOINVENTORYBAR, 0 }, { "noinventorybar", MITYPE_SETFLAG, LEVEL_NOINVENTORYBAR, 0 },
{ "deathslideshow", MITYPE_SETFLAG2, 0, 0 }, { "deathslideshow", MITYPE_IGNORE, 0, 0 },
{ "strictmonsteractivation", MITYPE_CLRFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO }, { "strictmonsteractivation", MITYPE_CLRFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO },
{ "laxmonsteractivation", MITYPE_SETFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO }, { "laxmonsteractivation", MITYPE_SETFLAG2, LEVEL2_LAXMONSTERACTIVATION, LEVEL2_LAXACTIVATIONMAPINFO },
{ "additive_scrollers", MITYPE_COMPATFLAG, COMPATF_BOOMSCROLL, 0 }, { "additive_scrollers", MITYPE_COMPATFLAG, COMPATF_BOOMSCROLL, 0 },

View file

@ -7,6 +7,7 @@
#include "s_sound.h" #include "s_sound.h"
#include "m_random.h" #include "m_random.h"
#include "doomstat.h" #include "doomstat.h"
#include "g_game.h"
static FRandom pr_tele ("TeleportSelf"); static FRandom pr_tele ("TeleportSelf");
@ -37,9 +38,10 @@ bool AArtiTeleport::Use (bool pickup)
} }
else else
{ {
destX = playerstarts[Owner->player - players].x; FMapThing *start = G_PickPlayerStart(int(Owner->player - players));
destY = playerstarts[Owner->player - players].y; destX = start->x;
destAngle = ANG45 * (playerstarts[Owner->player - players].angle/45); destY = start->y;
destAngle = ANG45 * (start->angle/45);
} }
P_Teleport (Owner, destX, destY, ONFLOORZ, destAngle, true, true, false); P_Teleport (Owner, destX, destY, ONFLOORZ, destAngle, true, true, false);
bool canlaugh = true; bool canlaugh = true;

View file

@ -4481,9 +4481,11 @@ AActor *P_SpawnMapThing (FMapThing *mthing, int position)
// save spots for respawning in network games // save spots for respawning in network games
playerstarts[pnum] = *mthing; playerstarts[pnum] = *mthing;
if (!deathmatch) AllPlayerStarts.Push(*mthing);
if (!deathmatch && !(level.flags2 & LEVEL2_RANDOMPLAYERSTARTS))
{
return P_SpawnPlayer(&playerstarts[pnum], pnum); return P_SpawnPlayer(&playerstarts[pnum], pnum);
}
return NULL; return NULL;
} }

View file

@ -184,6 +184,7 @@ bool ForceNodeBuild;
// Maintain single and multi player starting spots. // Maintain single and multi player starting spots.
TArray<FMapThing> deathmatchstarts (16); TArray<FMapThing> deathmatchstarts (16);
FMapThing playerstarts[MAXPLAYERS]; FMapThing playerstarts[MAXPLAYERS];
TArray<FMapThing> AllPlayerStarts;
static void P_AllocateSideDefs (int count); static void P_AllocateSideDefs (int count);
@ -3911,6 +3912,8 @@ void P_SetupLevel (char *lumpname, int position)
bodyque[i] = NULL; bodyque[i] = NULL;
deathmatchstarts.Clear(); deathmatchstarts.Clear();
AllPlayerStarts.Clear();
memset(playerstarts, 0, sizeof(playerstarts));
if (!buildmap) 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 // Don't count monsters in end-of-level sectors if option is on
if (dmflags2 & DF2_NOCOUNTENDMONST) if (dmflags2 & DF2_NOCOUNTENDMONST)