mirror of
https://github.com/ZDoom/qzdoom.git
synced 2024-12-01 00:21:43 +00:00
- 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:
parent
390fd5dd6c
commit
71601f91d1
11 changed files with 85 additions and 43 deletions
|
@ -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__
|
||||||
|
|
|
@ -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)
|
||||||
{
|
{
|
||||||
|
@ -1585,32 +1614,23 @@ void G_DoReborn (int playernum, bool freshbot)
|
||||||
players[playernum].mo->player = NULL;
|
players[playernum].mo->player = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
// spawn at random spot if in death match
|
// spawn at random spot if in deathmatch
|
||||||
if (deathmatch)
|
if (deathmatch)
|
||||||
{
|
{
|
||||||
G_DeathMatchSpawnPlayer (playernum);
|
G_DeathMatchSpawnPlayer (playernum);
|
||||||
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.
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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"
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -136,7 +136,7 @@ enum ELevelFlags
|
||||||
|
|
||||||
LEVEL_SPECLOWERFLOOR = 0x00000100,
|
LEVEL_SPECLOWERFLOOR = 0x00000100,
|
||||||
LEVEL_SPECOPENDOOR = 0x00000200,
|
LEVEL_SPECOPENDOOR = 0x00000200,
|
||||||
LEVEL_SPECLOWERFLOORTOHIGHEST= 0x00000300,
|
LEVEL_SPECLOWERFLOORTOHIGHEST=0x00000300,
|
||||||
LEVEL_SPECACTIONSMASK = 0x00000300,
|
LEVEL_SPECACTIONSMASK = 0x00000300,
|
||||||
|
|
||||||
LEVEL_MONSTERSTELEFRAG = 0x00000400,
|
LEVEL_MONSTERSTELEFRAG = 0x00000400,
|
||||||
|
@ -170,13 +170,13 @@ 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
|
||||||
LEVEL2_LAXACTIVATIONMAPINFO = 0x00000008, // LEVEL_LAXMONSTERACTIVATION is not a default.
|
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_FROZEN = 0x00000020, // Game is frozen by a TimeFreezer
|
||||||
|
|
||||||
LEVEL2_KEEPFULLINVENTORY = 0x00000040, // doesn't reduce the amount of inventory items to 1
|
LEVEL2_KEEPFULLINVENTORY = 0x00000040, // doesn't reduce the amount of inventory items to 1
|
||||||
|
|
|
@ -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 },
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
@ -3910,7 +3911,9 @@ void P_SetupLevel (char *lumpname, int position)
|
||||||
for (i = 0; i < BODYQUESIZE; i++)
|
for (i = 0; i < BODYQUESIZE; i++)
|
||||||
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)
|
||||||
|
|
Loading…
Reference in a new issue