Merge branch 'next' of https://git.magicalgirl.moe/STJr/SRB2 into playerthink-hook

This commit is contained in:
Zachary McAlpin 2020-01-01 17:11:19 -06:00
commit dc51ba6b64
21 changed files with 396 additions and 227 deletions

View File

@ -1293,7 +1293,8 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime)
netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers(); netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers();
netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value; netbuffer->u.serverinfo.maxplayer = (UINT8)cv_maxplayers.value;
netbuffer->u.serverinfo.gametype = (UINT8)gametype; strncpy(netbuffer->u.serverinfo.gametypename, Gametype_Names[gametype],
sizeof netbuffer->u.serverinfo.gametypename);
netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame; netbuffer->u.serverinfo.modifiedgame = (UINT8)modifiedgame;
netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled(); netbuffer->u.serverinfo.cheatsenabled = CV_CheatsEnabled();
netbuffer->u.serverinfo.isdedicated = (UINT8)dedicated; netbuffer->u.serverinfo.isdedicated = (UINT8)dedicated;
@ -2122,13 +2123,10 @@ static void CL_ConnectToServer(boolean viams)
if (i != -1) if (i != -1)
{ {
UINT16 num = serverlist[i].info.gametype; char *gametypestr = serverlist[i].info.gametypename;
const char *gametypestr = NULL;
CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername); CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername);
if (num < gametypecount) gametypestr[sizeof serverlist[i].info.gametypename - 1] = '\0';
gametypestr = Gametype_Names[num]; CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr);
if (gametypestr)
CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr);
CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100, CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100,
serverlist[i].info.version%100, serverlist[i].info.subversion); serverlist[i].info.version%100, serverlist[i].info.subversion);
} }
@ -3656,6 +3654,8 @@ static void HandleServerInfo(SINT8 node)
netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0; netbuffer->u.serverinfo.servername[MAXSERVERNAME-1] = 0;
netbuffer->u.serverinfo.application netbuffer->u.serverinfo.application
[sizeof netbuffer->u.serverinfo.application - 1] = '\0'; [sizeof netbuffer->u.serverinfo.application - 1] = '\0';
netbuffer->u.serverinfo.gametypename
[sizeof netbuffer->u.serverinfo.gametypename - 1] = '\0';
SL_InsertServer(&netbuffer->u.serverinfo, node); SL_InsertServer(&netbuffer->u.serverinfo, node);
} }

View File

@ -27,7 +27,7 @@ This version is independent of the mod name, and standard
version and subversion. It should only account for the version and subversion. It should only account for the
basic fields of the packet, and change infrequently. basic fields of the packet, and change infrequently.
*/ */
#define PACKETVERSION 0 #define PACKETVERSION 1
// Network play related stuff. // Network play related stuff.
// There is a data struct that stores network // There is a data struct that stores network
@ -361,7 +361,7 @@ typedef struct
UINT8 subversion; UINT8 subversion;
UINT8 numberofplayer; UINT8 numberofplayer;
UINT8 maxplayer; UINT8 maxplayer;
UINT8 gametype; char gametypename[24];
UINT8 modifiedgame; UINT8 modifiedgame;
UINT8 cheatsenabled; UINT8 cheatsenabled;
UINT8 isdedicated; UINT8 isdedicated;

View File

@ -766,7 +766,7 @@ void D_StartTitle(void)
if (netgame) if (netgame)
{ {
if (gametype == GT_COOP) if (gametyperules & GTR_CAMPAIGN)
{ {
G_SetGamestate(GS_WAITINGPLAYERS); // hack to prevent a command repeat G_SetGamestate(GS_WAITINGPLAYERS); // hack to prevent a command repeat

View File

@ -3747,7 +3747,7 @@ static void CoopStarposts_OnChange(void)
{ {
INT32 i; INT32 i;
if (!(netgame || multiplayer) || gametype != GT_COOP) if (!(netgame || multiplayer) || !G_GametypeUsesCoopStarposts())
return; return;
switch (cv_coopstarposts.value) switch (cv_coopstarposts.value)
@ -3802,7 +3802,7 @@ static void CoopLives_OnChange(void)
{ {
INT32 i; INT32 i;
if (!(netgame || multiplayer) || gametype != GT_COOP) if (!(netgame || multiplayer) || !G_GametypeUsesCoopLives())
return; return;
switch (cv_cooplives.value) switch (cv_cooplives.value)

View File

@ -8904,32 +8904,35 @@ static const char *const GAMETYPERULE_LIST[] = {
"CAMPAIGN", "CAMPAIGN",
"RINGSLINGER", "RINGSLINGER",
"SPECTATORS", "SPECTATORS",
"FRIENDLYFIRE",
"LIVES", "LIVES",
"TEAMS", "TEAMS",
"FIRSTPERSON",
"POWERSTONES",
"TEAMFLAGS",
"FRIENDLY",
"SPECIALSTAGES",
"EMERALDTOKENS",
"EMERALDHUNT",
"RACE", "RACE",
"TAG", "TAG",
"POINTLIMIT", "POINTLIMIT",
"TIMELIMIT", "TIMELIMIT",
"HIDETIME", "OVERTIME",
"HURTMESSAGES",
"FRIENDLYFIRE",
"STARTCOUNTDOWN",
"HIDEFROZEN", "HIDEFROZEN",
"BLINDFOLDED", "BLINDFOLDED",
"FIRSTPERSON", "RESPAWNDELAY",
"MATCHEMERALDS",
"TEAMFLAGS",
"PITYSHIELD", "PITYSHIELD",
"DEATHPENALTY", "DEATHPENALTY",
"NOSPECTATORSPAWN", "NOSPECTATORSPAWN",
"DEATHMATCHSTARTS", "DEATHMATCHSTARTS",
"SPECIALSTAGES", "SPAWNINVUL",
"EMERALDTOKENS",
"EMERALDHUNT",
"SPAWNENEMIES", "SPAWNENEMIES",
"ALLOWEXIT", "ALLOWEXIT",
"NOTITLECARD", "NOTITLECARD",
"OVERTIME", "CUTSCENES",
"HURTMESSAGES",
"SPAWNINVUL",
NULL NULL
}; };

View File

@ -397,32 +397,35 @@ enum GameTypeRules
GTR_CAMPAIGN = 1, // Linear Co-op map progression, don't allow random maps GTR_CAMPAIGN = 1, // Linear Co-op map progression, don't allow random maps
GTR_RINGSLINGER = 1<<1, // Outside of Co-op, Competition, and Race (overriden by cv_ringslinger) GTR_RINGSLINGER = 1<<1, // Outside of Co-op, Competition, and Race (overriden by cv_ringslinger)
GTR_SPECTATORS = 1<<2, // Outside of Co-op, Competition, and Race GTR_SPECTATORS = 1<<2, // Outside of Co-op, Competition, and Race
GTR_FRIENDLYFIRE = 1<<3, // Always allow friendly fire GTR_LIVES = 1<<3, // Co-op and Competition
GTR_LIVES = 1<<4, // Co-op and Competition GTR_TEAMS = 1<<4, // Team Match, CTF
GTR_TEAMS = 1<<5, // Team Match, CTF GTR_FIRSTPERSON = 1<<5, // First person camera
GTR_RACE = 1<<6, // Race and Competition GTR_POWERSTONES = 1<<6, // Power stones (Match and CTF)
GTR_TAG = 1<<7, // Tag and Hide and Seek GTR_TEAMFLAGS = 1<<7, // Gametype has team flags (CTF)
GTR_POINTLIMIT = 1<<8, // Ringslinger point limit GTR_FRIENDLY = 1<<8, // Co-op
GTR_TIMELIMIT = 1<<9, // Ringslinger time limit GTR_SPECIALSTAGES = 1<<9, // Allow special stages
GTR_HIDETIME = 1<<10, // Hide time (Tag and Hide and Seek) GTR_EMERALDTOKENS = 1<<10, // Spawn emerald tokens
GTR_HIDEFROZEN = 1<<11, // Frozen after hide time (Hide and Seek, but not Tag) GTR_EMERALDHUNT = 1<<11, // Emerald Hunt
GTR_BLINDFOLDED = 1<<12, // Blindfolded view (Tag and Hide and Seek) GTR_RACE = 1<<12, // Race and Competition
GTR_FIRSTPERSON = 1<<13, // First person camera GTR_TAG = 1<<13, // Tag and Hide and Seek
GTR_MATCHEMERALDS = 1<<14, // Ringslinger emeralds (Match and CTF) GTR_POINTLIMIT = 1<<14, // Ringslinger point limit
GTR_TEAMFLAGS = 1<<15, // Gametype has team flags (CTF) GTR_TIMELIMIT = 1<<15, // Ringslinger time limit
GTR_PITYSHIELD = 1<<16, // Award pity shield GTR_OVERTIME = 1<<16, // Allow overtime
GTR_DEATHPENALTY = 1<<17, // Death score penalty GTR_HURTMESSAGES = 1<<17, // Hit and death messages
GTR_NOSPECTATORSPAWN = 1<<18, // Use with GTR_SPECTATORS, spawn in the map instead of with the spectators GTR_FRIENDLYFIRE = 1<<18, // Always allow friendly fire
GTR_DEATHMATCHSTARTS = 1<<19, // Use deathmatch starts GTR_STARTCOUNTDOWN = 1<<19, // Hide time countdown (Tag and Hide and Seek)
GTR_SPECIALSTAGES = 1<<20, // Allow special stages GTR_HIDEFROZEN = 1<<20, // Frozen after hide time (Hide and Seek, but not Tag)
GTR_EMERALDTOKENS = 1<<21, // Spawn emerald tokens GTR_BLINDFOLDED = 1<<21, // Blindfolded view (Tag and Hide and Seek)
GTR_EMERALDHUNT = 1<<22, // Emerald Hunt GTR_RESPAWNDELAY = 1<<22, // Respawn delay
GTR_SPAWNENEMIES = 1<<23, // Spawn enemies GTR_PITYSHIELD = 1<<23, // Award pity shield
GTR_ALLOWEXIT = 1<<24, // Allow exit sectors GTR_DEATHPENALTY = 1<<24, // Death score penalty
GTR_NOTITLECARD = 1<<25, // Don't show the title card GTR_NOSPECTATORSPAWN = 1<<25, // Use with GTR_SPECTATORS, spawn in the map instead of with the spectators
GTR_OVERTIME = 1<<26, // Allow overtime GTR_DEATHMATCHSTARTS = 1<<26, // Use deathmatch starts
GTR_HURTMESSAGES = 1<<27, // Hit and death messages GTR_SPAWNINVUL = 1<<27, // Babysitting deterrent
GTR_SPAWNINVUL = 1<<28, // Babysitting deterrent GTR_SPAWNENEMIES = 1<<28, // Spawn enemies
GTR_ALLOWEXIT = 1<<29, // Allow exit sectors
GTR_NOTITLECARD = 1<<30, // Don't show the title card
GTR_CUTSCENES = 1<<31, // Play cutscenes, ending, credits, and evaluation
}; };
// String names for gametypes // String names for gametypes

View File

@ -2689,8 +2689,7 @@ void G_SpawnPlayer(INT32 playernum, boolean starpost)
// -- DM/Tag/CTF-spectator/etc -- // -- DM/Tag/CTF-spectator/etc --
// Order: DM->CTF->Coop // Order: DM->CTF->Coop
else if ((gametyperules & GTR_DEATHMATCHSTARTS) || gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF else if ((gametyperules & GTR_DEATHMATCHSTARTS) && !(players[playernum].pflags & PF_TAGIT))
|| ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && !(players[playernum].pflags & PF_TAGIT)))
{ {
if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM start if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM start
&& !(spawnpoint = G_FindCTFStart(playernum))) // find a CTF start && !(spawnpoint = G_FindCTFStart(playernum))) // find a CTF start
@ -2891,11 +2890,11 @@ void G_DoReborn(INT32 playernum)
if (countdowntimeup || (!(netgame || multiplayer) && gametype == GT_COOP)) if (countdowntimeup || (!(netgame || multiplayer) && gametype == GT_COOP))
resetlevel = true; resetlevel = true;
else if (gametype == GT_COOP && (netgame || multiplayer) && !G_IsSpecialStage(gamemap)) else if ((G_GametypeUsesCoopLives() || G_GametypeUsesCoopStarposts()) && (netgame || multiplayer) && !G_IsSpecialStage(gamemap))
{ {
boolean notgameover = true; boolean notgameover = true;
if (cv_cooplives.value != 0 && player->lives <= 0) // consider game over first if (G_GametypeUsesCoopLives() && (cv_cooplives.value != 0 && player->lives <= 0)) // consider game over first
{ {
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
@ -2930,7 +2929,7 @@ void G_DoReborn(INT32 playernum)
} }
} }
if (notgameover && cv_coopstarposts.value == 2) if (G_GametypeUsesCoopStarposts() && (notgameover && cv_coopstarposts.value == 2))
{ {
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
@ -3006,7 +3005,7 @@ void G_DoReborn(INT32 playernum)
} }
// restore time in netgame (see also p_setup.c) // restore time in netgame (see also p_setup.c)
if ((netgame || multiplayer) && gametype == GT_COOP && cv_coopstarposts.value == 2) if ((netgame || multiplayer) && G_GametypeUsesCoopStarposts() && cv_coopstarposts.value == 2)
{ {
// is this a hack? maybe // is this a hack? maybe
tic_t maxstarposttime = 0; tic_t maxstarposttime = 0;
@ -3077,7 +3076,7 @@ void G_AddPlayer(INT32 playernum)
if (!players[i].exiting) if (!players[i].exiting)
notexiting++; notexiting++;
if (!(cv_coopstarposts.value && (gametype == GT_COOP) && (p->starpostnum < players[i].starpostnum))) if (!(cv_coopstarposts.value && G_GametypeUsesCoopStarposts() && (p->starpostnum < players[i].starpostnum)))
continue; continue;
p->starpostscale = players[i].starpostscale; p->starpostscale = players[i].starpostscale;
@ -3194,24 +3193,24 @@ const char *Gametype_ConstantNames[NUMGAMETYPES] =
UINT32 gametypedefaultrules[NUMGAMETYPES] = UINT32 gametypedefaultrules[NUMGAMETYPES] =
{ {
// Co-op // Co-op
GTR_CAMPAIGN|GTR_LIVES|GTR_SPAWNENEMIES|GTR_ALLOWEXIT|GTR_EMERALDHUNT|GTR_EMERALDTOKENS|GTR_SPECIALSTAGES, GTR_CAMPAIGN|GTR_LIVES|GTR_FRIENDLY|GTR_SPAWNENEMIES|GTR_ALLOWEXIT|GTR_EMERALDHUNT|GTR_EMERALDTOKENS|GTR_SPECIALSTAGES|GTR_CUTSCENES,
// Competition // Competition
GTR_RACE|GTR_LIVES|GTR_SPAWNENEMIES|GTR_EMERALDTOKENS|GTR_SPAWNINVUL|GTR_ALLOWEXIT, GTR_RACE|GTR_LIVES|GTR_SPAWNENEMIES|GTR_EMERALDTOKENS|GTR_SPAWNINVUL|GTR_ALLOWEXIT,
// Race // Race
GTR_RACE|GTR_SPAWNENEMIES|GTR_SPAWNINVUL|GTR_ALLOWEXIT, GTR_RACE|GTR_SPAWNENEMIES|GTR_SPAWNINVUL|GTR_ALLOWEXIT,
// Match // Match
GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_MATCHEMERALDS|GTR_SPAWNINVUL|GTR_PITYSHIELD|GTR_DEATHPENALTY, GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD|GTR_DEATHPENALTY,
// Team Match // Team Match
GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_SPAWNINVUL|GTR_PITYSHIELD, GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD,
// Tag // Tag
GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_HIDETIME|GTR_BLINDFOLDED|GTR_SPAWNINVUL, GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY,
// Hide and Seek // Hide and Seek
GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_HIDETIME|GTR_BLINDFOLDED|GTR_SPAWNINVUL, GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_TAG|GTR_SPECTATORS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_STARTCOUNTDOWN|GTR_BLINDFOLDED|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY,
// CTF // CTF
GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_TEAMFLAGS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_MATCHEMERALDS|GTR_SPAWNINVUL|GTR_PITYSHIELD, GTR_RINGSLINGER|GTR_FIRSTPERSON|GTR_SPECTATORS|GTR_TEAMS|GTR_TEAMFLAGS|GTR_POINTLIMIT|GTR_TIMELIMIT|GTR_POWERSTONES|GTR_DEATHMATCHSTARTS|GTR_SPAWNINVUL|GTR_RESPAWNDELAY|GTR_PITYSHIELD,
}; };
// //
@ -3252,50 +3251,68 @@ INT16 G_AddGametype(UINT32 rules)
// //
void G_AddGametypeConstant(INT16 gtype, const char *newgtconst) void G_AddGametypeConstant(INT16 gtype, const char *newgtconst)
{ {
char *gtconst = Z_Malloc(strlen(newgtconst) + 3, PU_STATIC, NULL); size_t r = 0; // read
// Copy GT_ and the gametype name. size_t w = 0; // write
strcpy(gtconst, "GT_"); char *gtconst = Z_Calloc(strlen(newgtconst) + 3, PU_STATIC, NULL);
strcat(gtconst, newgtconst); char *tmpconst = Z_Calloc(strlen(newgtconst), PU_STATIC, NULL);
// Copy the gametype name.
strcpy(tmpconst, newgtconst);
// Make uppercase. // Make uppercase.
strupr(gtconst); strupr(tmpconst);
// Remove characters.
#define REMOVECHAR(chr) \ // Prepare to write the new constant string now.
{ \ strcpy(gtconst, "GT_");
char *chrfind = strchr(gtconst, chr); \
while (chrfind) \ // Remove characters that will not be allowed in the constant string.
{ \ for (; r < strlen(tmpconst); r++)
*chrfind = '_'; \ {
chrfind = strchr(chrfind, chr); \ boolean writechar = true;
} \ char rc = tmpconst[r];
switch (rc)
{
// Space, at sign and question mark
case ' ':
case '@':
case '?':
// Used for operations
case '+':
case '-':
case '*':
case '/':
case '%':
case '^':
case '&':
case '!':
// Part of Lua's syntax
case '#':
case '=':
case '~':
case '<':
case '>':
case '(':
case ')':
case '{':
case '}':
case '[':
case ']':
case ':':
case ';':
case ',':
case '.':
writechar = false;
break;
}
if (writechar)
{
gtconst[3 + w] = rc;
w++;
}
} }
// Space // Free the temporary string.
REMOVECHAR(' ') Z_Free(tmpconst);
// Used for operations
REMOVECHAR('+')
REMOVECHAR('-')
REMOVECHAR('*')
REMOVECHAR('/')
REMOVECHAR('%')
REMOVECHAR('^')
// Part of Lua's syntax
REMOVECHAR('#')
REMOVECHAR('=')
REMOVECHAR('~')
REMOVECHAR('<')
REMOVECHAR('>')
REMOVECHAR('(')
REMOVECHAR(')')
REMOVECHAR('{')
REMOVECHAR('}')
REMOVECHAR('[')
REMOVECHAR(']')
REMOVECHAR(':')
REMOVECHAR(';')
REMOVECHAR(',')
REMOVECHAR('.')
#undef REMOVECHAR
// Finally, set the constant string. // Finally, set the constant string.
Gametype_ConstantNames[gtype] = gtconst; Gametype_ConstantNames[gtype] = gtconst;
@ -3440,6 +3457,28 @@ boolean G_GametypeUsesLives(void)
return false; return false;
} }
//
// G_GametypeUsesCoopLives
//
// Returns true if the current gametype uses
// the cooplives CVAR. False otherwise.
//
boolean G_GametypeUsesCoopLives(void)
{
return (gametyperules & (GTR_LIVES|GTR_FRIENDLY)) == (GTR_LIVES|GTR_FRIENDLY);
}
//
// G_GametypeUsesCoopStarposts
//
// Returns true if the current gametype uses
// the coopstarposts CVAR. False otherwise.
//
boolean G_GametypeUsesCoopStarposts(void)
{
return (gametyperules & GTR_FRIENDLY);
}
// //
// G_GametypeHasTeams // G_GametypeHasTeams
// //
@ -3493,6 +3532,16 @@ boolean G_TagGametype(void)
return (gametyperules & GTR_TAG); return (gametyperules & GTR_TAG);
} }
//
// G_CompetitionGametype
//
// For gametypes that are race gametypes, and have lives.
//
boolean G_CompetitionGametype(void)
{
return ((gametyperules & GTR_RACE) && (gametyperules & GTR_LIVES));
}
/** Get the typeoflevel flag needed to indicate support of a gametype. /** Get the typeoflevel flag needed to indicate support of a gametype.
* In single-player, this always returns TOL_SP. * In single-player, this always returns TOL_SP.
* \param gametype The gametype for which support is desired. * \param gametype The gametype for which support is desired.
@ -3734,7 +3783,7 @@ void G_AfterIntermission(void)
HU_ClearCEcho(); HU_ClearCEcho();
if (mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1) // Start a custom cutscene. if ((gametyperules & GTR_CUTSCENES) && mapheaderinfo[gamemap-1]->cutscenenum && !modeattacking && skipstats <= 1) // Start a custom cutscene.
F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false); F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false);
else else
{ {
@ -3844,7 +3893,7 @@ static void G_DoContinued(void)
void G_EndGame(void) void G_EndGame(void)
{ {
// Only do evaluation and credits in coop games. // Only do evaluation and credits in coop games.
if (gametype == GT_COOP) if (gametyperules & GTR_CUTSCENES)
{ {
if (nextmap == 1103-1) // end game with ending if (nextmap == 1103-1) // end game with ending
{ {
@ -4547,7 +4596,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean
automapactive = false; automapactive = false;
imcontinuing = false; imcontinuing = false;
if (!skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking) // Start a custom cutscene. if ((gametyperules & GTR_CUTSCENES) && !skipprecutscene && mapheaderinfo[gamemap-1]->precutscenenum && !modeattacking) // Start a custom cutscene.
F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer); F_StartCustomCutscene(mapheaderinfo[gamemap-1]->precutscenenum-1, true, resetplayer);
else else
G_DoLoadLevel(resetplayer); G_DoLoadLevel(resetplayer);

View File

@ -218,11 +218,14 @@ void G_SetGametypeDescription(INT16 gtype, char *descriptiontext, UINT8 leftcolo
INT32 G_GetGametypeByName(const char *gametypestr); INT32 G_GetGametypeByName(const char *gametypestr);
boolean G_IsSpecialStage(INT32 mapnum); boolean G_IsSpecialStage(INT32 mapnum);
boolean G_GametypeUsesLives(void); boolean G_GametypeUsesLives(void);
boolean G_GametypeUsesCoopLives(void);
boolean G_GametypeUsesCoopStarposts(void);
boolean G_GametypeHasTeams(void); boolean G_GametypeHasTeams(void);
boolean G_GametypeHasSpectators(void); boolean G_GametypeHasSpectators(void);
boolean G_RingSlingerGametype(void); boolean G_RingSlingerGametype(void);
boolean G_PlatformGametype(void); boolean G_PlatformGametype(void);
boolean G_TagGametype(void); boolean G_TagGametype(void);
boolean G_CompetitionGametype(void);
boolean G_EnoughPlayersFinished(void); boolean G_EnoughPlayersFinished(void);
void G_ExitLevel(void); void G_ExitLevel(void);
void G_NextLevel(void); void G_NextLevel(void);

View File

@ -2437,7 +2437,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I
} }
} }
if (G_GametypeUsesLives() && !(gametyperankings[gametype] == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //show lives if (G_GametypeUsesLives() && !(G_GametypeUsesCoopLives() && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //show lives
V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE|(greycheck ? V_60TRANS : 0), va("%dx", players[tab[i].num].lives)); V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE|(greycheck ? V_60TRANS : 0), va("%dx", players[tab[i].num].lives));
else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT) else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT)
{ {
@ -2746,7 +2746,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline
| (greycheck ? V_TRANSLUCENT : 0) | (greycheck ? V_TRANSLUCENT : 0)
| V_ALLOWLOWERCASE, name); | V_ALLOWLOWERCASE, name);
if (G_GametypeUsesLives() && !(gametyperankings[gametype] == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //show lives if (G_GametypeUsesLives() && !(G_GametypeUsesCoopLives() && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != INFLIVES)) //show lives
V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE, va("%dx", players[tab[i].num].lives)); V_DrawRightAlignedString(x, y+4, V_ALLOWLOWERCASE, va("%dx", players[tab[i].num].lives));
else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT) else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT)
V_DrawSmallScaledPatch(x-28, y-4, 0, tagico); V_DrawSmallScaledPatch(x-28, y-4, 0, tagico);

View File

@ -2854,6 +2854,22 @@ static int lib_gGametypeUsesLives(lua_State *L)
return 1; return 1;
} }
static int lib_gGametypeUsesCoopLives(lua_State *L)
{
//HUDSAFE
INLEVEL
lua_pushboolean(L, G_GametypeUsesCoopLives());
return 1;
}
static int lib_gGametypeUsesCoopStarposts(lua_State *L)
{
//HUDSAFE
INLEVEL
lua_pushboolean(L, G_GametypeUsesCoopStarposts());
return 1;
}
static int lib_gGametypeHasTeams(lua_State *L) static int lib_gGametypeHasTeams(lua_State *L)
{ {
//HUDSAFE //HUDSAFE
@ -2894,6 +2910,14 @@ static int lib_gTagGametype(lua_State *L)
return 1; return 1;
} }
static int lib_gCompetitionGametype(lua_State *L)
{
//HUDSAFE
INLEVEL
lua_pushboolean(L, G_CompetitionGametype());
return 1;
}
static int lib_gTicsToHours(lua_State *L) static int lib_gTicsToHours(lua_State *L)
{ {
tic_t rtic = luaL_checkinteger(L, 1); tic_t rtic = luaL_checkinteger(L, 1);
@ -3139,11 +3163,14 @@ static luaL_Reg lib[] = {
{"G_ExitLevel",lib_gExitLevel}, {"G_ExitLevel",lib_gExitLevel},
{"G_IsSpecialStage",lib_gIsSpecialStage}, {"G_IsSpecialStage",lib_gIsSpecialStage},
{"G_GametypeUsesLives",lib_gGametypeUsesLives}, {"G_GametypeUsesLives",lib_gGametypeUsesLives},
{"G_GametypeUsesCoopLives",lib_gGametypeUsesCoopLives},
{"G_GametypeUsesCoopStarposts",lib_gGametypeUsesCoopStarposts},
{"G_GametypeHasTeams",lib_gGametypeHasTeams}, {"G_GametypeHasTeams",lib_gGametypeHasTeams},
{"G_GametypeHasSpectators",lib_gGametypeHasSpectators}, {"G_GametypeHasSpectators",lib_gGametypeHasSpectators},
{"G_RingSlingerGametype",lib_gRingSlingerGametype}, {"G_RingSlingerGametype",lib_gRingSlingerGametype},
{"G_PlatformGametype",lib_gPlatformGametype}, {"G_PlatformGametype",lib_gPlatformGametype},
{"G_TagGametype",lib_gTagGametype}, {"G_TagGametype",lib_gTagGametype},
{"G_CompetitionGametype",lib_gCompetitionGametype},
{"G_TicsToHours",lib_gTicsToHours}, {"G_TicsToHours",lib_gTicsToHours},
{"G_TicsToMinutes",lib_gTicsToMinutes}, {"G_TicsToMinutes",lib_gTicsToMinutes},
{"G_TicsToSeconds",lib_gTicsToSeconds}, {"G_TicsToSeconds",lib_gTicsToSeconds},

View File

@ -53,6 +53,7 @@ enum hook {
hook_IntermissionThinker, hook_IntermissionThinker,
hook_TeamSwitch, hook_TeamSwitch,
hook_ViewpointSwitch, hook_ViewpointSwitch,
hook_SeenPlayer,
hook_PlayerThink, hook_PlayerThink,
hook_MAX // last hook hook_MAX // last hook
@ -98,6 +99,9 @@ void LUAh_PlayerQuit(player_t *plr, int reason); // Hook for player quitting
void LUAh_IntermissionThinker(void); // Hook for Y_Ticker void LUAh_IntermissionThinker(void); // Hook for Y_Ticker
boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble); // Hook for team switching in... uh.... boolean LUAh_TeamSwitch(player_t *player, int newteam, boolean fromspectators, boolean tryingautobalance, boolean tryingscramble); // Hook for team switching in... uh....
UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); // Hook for spy mode UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean forced); // Hook for spy mode
#ifdef SEENAMES
boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend); // Hook for MT_NAMECHECK
#endif
#define LUAh_PlayerThink(player) LUAh_PlayerHook(player, hook_PlayerThink) // Hook for P_PlayerThink #define LUAh_PlayerThink(player) LUAh_PlayerHook(player, hook_PlayerThink) // Hook for P_PlayerThink
#endif #endif

View File

@ -64,6 +64,7 @@ const char *const hookNames[hook_MAX+1] = {
"IntermissionThinker", "IntermissionThinker",
"TeamSwitch", "TeamSwitch",
"ViewpointSwitch", "ViewpointSwitch",
"SeenPlayer",
"PlayerThink", "PlayerThink",
NULL NULL
}; };
@ -208,6 +209,7 @@ static int lib_addHook(lua_State *L)
case hook_PlayerCanDamage: case hook_PlayerCanDamage:
case hook_TeamSwitch: case hook_TeamSwitch:
case hook_ViewpointSwitch: case hook_ViewpointSwitch:
case hook_SeenPlayer:
case hook_ShieldSpawn: case hook_ShieldSpawn:
case hook_ShieldSpecial: case hook_ShieldSpecial:
case hook_PlayerThink: case hook_PlayerThink:
@ -1414,7 +1416,7 @@ UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean
return 0; return 0;
lua_settop(gL, 0); lua_settop(gL, 0);
hud_running = true; hud_running = true; // local hook
for (hookp = playerhooks; hookp; hookp = hookp->next) for (hookp = playerhooks; hookp; hookp = hookp->next)
{ {
@ -1455,4 +1457,49 @@ UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean
return canSwitchView; return canSwitchView;
} }
// Hook for MT_NAMECHECK
#ifdef SEENAMES
boolean LUAh_SeenPlayer(player_t *player, player_t *seenfriend)
{
hook_p hookp;
boolean hasSeenPlayer = true;
if (!gL || !(hooksAvailable[hook_SeenPlayer/8] & (1<<(hook_SeenPlayer%8))))
return 0;
lua_settop(gL, 0);
hud_running = true; // local hook
for (hookp = playerhooks; hookp; hookp = hookp->next)
{
if (hookp->type != hook_SeenPlayer)
continue;
if (lua_gettop(gL) == 0)
{
LUA_PushUserdata(gL, player, META_PLAYER);
LUA_PushUserdata(gL, seenfriend, META_PLAYER);
}
lua_pushfstring(gL, FMT_HOOKID, hookp->id);
lua_gettable(gL, LUA_REGISTRYINDEX);
lua_pushvalue(gL, -3);
lua_pushvalue(gL, -3);
if (lua_pcall(gL, 2, 1, 0)) {
if (!hookp->error || cv_debug & DBG_LUA)
CONS_Alert(CONS_WARNING,"%s\n",lua_tostring(gL, -1));
lua_pop(gL, 1);
hookp->error = true;
continue;
}
if (!lua_isnil(gL, -1) && !lua_toboolean(gL, -1))
hasSeenPlayer = false; // Hasn't seen player
lua_pop(gL, 1);
}
lua_settop(gL, 0);
hud_running = false;
return hasSeenPlayer;
}
#endif // SEENAMES
#endif #endif

View File

@ -299,9 +299,7 @@ int LUA_PushGlobals(lua_State *L, const char *word)
// See the above. // See the above.
int LUA_CheckGlobals(lua_State *L, const char *word) int LUA_CheckGlobals(lua_State *L, const char *word)
{ {
if (fastcmp(word, "gametyperules")) if (fastcmp(word, "redscore"))
gametyperules = (UINT32)luaL_checkinteger(L, 2);
else if (fastcmp(word, "redscore"))
redscore = (UINT32)luaL_checkinteger(L, 2); redscore = (UINT32)luaL_checkinteger(L, 2);
else if (fastcmp(word, "bluescore")) else if (fastcmp(word, "bluescore"))
bluescore = (UINT32)luaL_checkinteger(L, 2); bluescore = (UINT32)luaL_checkinteger(L, 2);

View File

@ -4735,7 +4735,7 @@ static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt)
if (gt == GT_RACE && (mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) if (gt == GT_RACE && (mapheaderinfo[mapnum]->typeoflevel & TOL_RACE))
return true; return true;
if (gt > 0 && gt < gametypecount && (mapheaderinfo[mapnum]->typeoflevel & gametypetol[gt])) if (gt >= 0 && gt < gametypecount && (mapheaderinfo[mapnum]->typeoflevel & gametypetol[gt]))
return true; return true;
return false; return false;
@ -9971,7 +9971,7 @@ static void M_DrawRoomMenu(void)
static void M_DrawConnectMenu(void) static void M_DrawConnectMenu(void)
{ {
UINT16 i; UINT16 i;
const char *gt = "Unknown"; char *gt;
INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE; INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE;
for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++) for (i = FIRSTSERVERLINE; i < min(localservercount, SERVERS_PER_PAGE)+FIRSTSERVERLINE; i++)
@ -10015,14 +10015,17 @@ static void M_DrawConnectMenu(void)
V_DrawSmallString(currentMenu->x, S_LINEY(i)+8, globalflags, V_DrawSmallString(currentMenu->x, S_LINEY(i)+8, globalflags,
va("Ping: %u", (UINT32)LONG(serverlist[slindex].info.time))); va("Ping: %u", (UINT32)LONG(serverlist[slindex].info.time)));
gt = "Unknown"; gt = serverlist[slindex].info.gametypename;
if (serverlist[slindex].info.gametype < gametypecount)
gt = Gametype_Names[serverlist[slindex].info.gametype];
V_DrawSmallString(currentMenu->x+46,S_LINEY(i)+8, globalflags, V_DrawSmallString(currentMenu->x+46,S_LINEY(i)+8, globalflags,
va("Players: %02d/%02d", serverlist[slindex].info.numberofplayer, serverlist[slindex].info.maxplayer)); va("Players: %02d/%02d", serverlist[slindex].info.numberofplayer, serverlist[slindex].info.maxplayer));
V_DrawSmallString(currentMenu->x+112, S_LINEY(i)+8, globalflags, va("Gametype: %s", gt)); if (strlen(gt) > 11)
gt = va("Gametype: %.11s...", gt);
else
gt = va("Gametype: %s", gt);
V_DrawSmallString(currentMenu->x+112, S_LINEY(i)+8, globalflags, gt);
MP_ConnectMenu[i+FIRSTSERVERLINE].status = IT_STRING | IT_CALL; MP_ConnectMenu[i+FIRSTSERVERLINE].status = IT_STRING | IT_CALL;
} }
@ -10063,7 +10066,15 @@ SERVER_LIST_ENTRY_COMPARATOR(time)
SERVER_LIST_ENTRY_COMPARATOR(numberofplayer) SERVER_LIST_ENTRY_COMPARATOR(numberofplayer)
SERVER_LIST_ENTRY_COMPARATOR_REVERSE(numberofplayer) SERVER_LIST_ENTRY_COMPARATOR_REVERSE(numberofplayer)
SERVER_LIST_ENTRY_COMPARATOR_REVERSE(maxplayer) SERVER_LIST_ENTRY_COMPARATOR_REVERSE(maxplayer)
SERVER_LIST_ENTRY_COMPARATOR(gametype)
static int ServerListEntryComparator_gametypename(const void *entry1, const void *entry2)
{
const serverelem_t *sa = (const serverelem_t*)entry1, *sb = (const serverelem_t*)entry2;
int c;
if (( c = strcasecmp(sa->info.gametypename, sb->info.gametypename) ))
return c;
return strcmp(sa->info.servername, sb->info.servername); \
}
// Special one for modified state. // Special one for modified state.
static int ServerListEntryComparator_modified(const void *entry1, const void *entry2) static int ServerListEntryComparator_modified(const void *entry1, const void *entry2)
@ -10103,7 +10114,7 @@ void M_SortServerList(void)
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_maxplayer_reverse); qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_maxplayer_reverse);
break; break;
case 5: // Gametype. case 5: // Gametype.
qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametype); qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametypename);
break; break;
} }
#endif #endif

View File

@ -1451,7 +1451,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck)
if (player->starpostnum >= special->health) if (player->starpostnum >= special->health)
return; // Already hit this post return; // Already hit this post
if (cv_coopstarposts.value && gametype == GT_COOP && (netgame || multiplayer)) if (cv_coopstarposts.value && G_GametypeUsesCoopStarposts() && (netgame || multiplayer))
{ {
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
@ -2521,7 +2521,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
target->colorized = false; target->colorized = false;
G_GhostAddColor(GHC_NORMAL); G_GhostAddColor(GHC_NORMAL);
if ((target->player->lives <= 1) && (netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value == 0)) if ((target->player->lives <= 1) && (netgame || multiplayer) && G_GametypeUsesCoopLives() && (cv_cooplives.value == 0))
; ;
else if (!target->player->bot && !target->player->spectator && (target->player->lives != INFLIVES) else if (!target->player->bot && !target->player->spectator && (target->player->lives != INFLIVES)
&& G_GametypeUsesLives()) && G_GametypeUsesLives())
@ -2531,7 +2531,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget
if (target->player->lives <= 0) // Tails 03-14-2000 if (target->player->lives <= 0) // Tails 03-14-2000
{ {
boolean gameovermus = false; boolean gameovermus = false;
if ((netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value != 1)) if ((netgame || multiplayer) && G_GametypeUsesCoopLives() && (cv_cooplives.value != 1))
{ {
INT32 i; INT32 i;
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
@ -3199,10 +3199,12 @@ static void P_KillPlayer(player_t *player, mobj_t *source, INT32 damage)
player->powers[pw_carry] = CR_NONE; player->powers[pw_carry] = CR_NONE;
// Burst weapons and emeralds in Match/CTF only // Burst weapons and emeralds in Match/CTF only
if (source && (gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF)) if (source)
{ {
P_PlayerRingBurst(player, player->rings); if ((gametyperules & GTR_RINGSLINGER) && !(gametyperules & GTR_TAG))
P_PlayerEmeraldBurst(player, false); P_PlayerRingBurst(player, player->rings);
if (gametyperules & GTR_POWERSTONES)
P_PlayerEmeraldBurst(player, false);
} }
// Get rid of shield // Get rid of shield

View File

@ -745,9 +745,8 @@ static boolean PIT_CheckThing(mobj_t *thing)
// So that NOTHING ELSE can see MT_NAMECHECK because it is client-side. // So that NOTHING ELSE can see MT_NAMECHECK because it is client-side.
if (tmthing->type == MT_NAMECHECK) if (tmthing->type == MT_NAMECHECK)
{ {
// Ignore things that aren't players, ignore spectators, ignore yourself. // Ignore things that aren't players, ignore spectators, ignore yourself.
// (also don't bother to check that tmthing->target->player is non-NULL because we're not actually using it here.) if (!thing->player || !(tmthing->target && tmthing->target->player) || thing->player->spectator || (tmthing->target && thing->player == tmthing->target->player))
if (!thing->player || thing->player->spectator || (tmthing->target && thing->player == tmthing->target->player))
return true; return true;
// Now check that you actually hit them. // Now check that you actually hit them.
@ -760,6 +759,12 @@ static boolean PIT_CheckThing(mobj_t *thing)
if (tmthing->z + tmthing->height < thing->z) if (tmthing->z + tmthing->height < thing->z)
return true; // underneath return true; // underneath
#ifdef HAVE_BLUA
// REX HAS SEEN YOU
if (!LUAh_SeenPlayer(tmthing->target->player, thing->player))
return false;
#endif
seenplayer = thing->player; seenplayer = thing->player;
return false; return false;
} }

View File

@ -11823,7 +11823,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i)
if (!cv_powerstones.value) if (!cv_powerstones.value)
return false; return false;
if (!(gametyperules & GTR_MATCHEMERALDS)) if (!(gametyperules & GTR_POWERSTONES))
return false; return false;
runemeraldmanager = true; runemeraldmanager = true;

View File

@ -2446,7 +2446,7 @@ static void P_InitLevelSettings(void)
// earthquake camera // earthquake camera
memset(&quake,0,sizeof(struct quake)); memset(&quake,0,sizeof(struct quake));
if ((netgame || multiplayer) && gametype == GT_COOP && cv_coopstarposts.value == 2) if ((netgame || multiplayer) && G_GametypeUsesCoopStarposts() && cv_coopstarposts.value == 2)
{ {
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
@ -2464,7 +2464,7 @@ static void P_InitLevelSettings(void)
{ {
G_PlayerReborn(i, true); G_PlayerReborn(i, true);
if (canresetlives && (netgame || multiplayer) && playeringame[i] && (gametype == GT_COMPETITION || players[i].lives <= 0)) if (canresetlives && (netgame || multiplayer) && playeringame[i] && (G_CompetitionGametype() || players[i].lives <= 0))
{ {
// In Co-Op, replenish a user's lives if they are depleted. // In Co-Op, replenish a user's lives if they are depleted.
players[i].lives = cv_startinglives.value; players[i].lives = cv_startinglives.value;
@ -2754,17 +2754,10 @@ static void P_SetupCamera(void)
{ {
mapthing_t *thing; mapthing_t *thing;
switch (gametype) if (gametyperules & GTR_DEATHMATCHSTARTS)
{
case GT_MATCH:
case GT_TAG:
thing = deathmatchstarts[0]; thing = deathmatchstarts[0];
break; else
default:
thing = playerstarts[0]; thing = playerstarts[0];
break;
}
if (thing) if (thing)
{ {
@ -2979,7 +2972,7 @@ static void P_InitGametype(void)
P_InitPlayers(); P_InitPlayers();
// restore time in netgame (see also g_game.c) // restore time in netgame (see also g_game.c)
if ((netgame || multiplayer) && gametype == GT_COOP && cv_coopstarposts.value == 2) if ((netgame || multiplayer) && G_GametypeUsesCoopStarposts() && cv_coopstarposts.value == 2)
{ {
// is this a hack? maybe // is this a hack? maybe
tic_t maxstarposttime = 0; tic_t maxstarposttime = 0;
@ -3023,6 +3016,7 @@ boolean P_LoadLevel(boolean fromnetsave)
// This is needed. Don't touch. // This is needed. Don't touch.
maptol = mapheaderinfo[gamemap-1]->typeoflevel; maptol = mapheaderinfo[gamemap-1]->typeoflevel;
gametyperules = gametypedefaultrules[gametype];
CON_Drawer(); // let the user know what we are going to do CON_Drawer(); // let the user know what we are going to do
I_FinishUpdate(); // page flip or blit buffer I_FinishUpdate(); // page flip or blit buffer

View File

@ -1230,13 +1230,13 @@ void P_GivePlayerLives(player_t *player, INT32 numlives)
if (gamestate == GS_LEVEL) if (gamestate == GS_LEVEL)
{ {
if (player->lives == INFLIVES || (gametype != GT_COOP && gametype != GT_COMPETITION)) if (player->lives == INFLIVES || !(gametyperules & GTR_LIVES))
{ {
P_GivePlayerRings(player, 100*numlives); P_GivePlayerRings(player, 100*numlives);
return; return;
} }
if ((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) if ((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 0)
{ {
P_GivePlayerRings(player, 100*numlives); P_GivePlayerRings(player, 100*numlives);
if (player->lives - prevlives >= numlives) if (player->lives - prevlives >= numlives)
@ -1267,7 +1267,7 @@ docooprespawn:
void P_GiveCoopLives(player_t *player, INT32 numlives, boolean sound) void P_GiveCoopLives(player_t *player, INT32 numlives, boolean sound)
{ {
if (!((netgame || multiplayer) && gametype == GT_COOP)) if (!((netgame || multiplayer) && G_GametypeUsesCoopLives()))
{ {
P_GivePlayerLives(player, numlives); P_GivePlayerLives(player, numlives);
if (sound) if (sound)
@ -1395,7 +1395,7 @@ void P_AddPlayerScore(player_t *player, UINT32 amount)
player->score = MAXSCORE; player->score = MAXSCORE;
// check for extra lives every 50000 pts // check for extra lives every 50000 pts
if (!ultimatemode && !modeattacking && player->score > oldscore && player->score % 50000 < amount && (gametype == GT_COMPETITION || gametype == GT_COOP)) if (!ultimatemode && !modeattacking && player->score > oldscore && player->score % 50000 < amount && (gametyperules & GTR_LIVES))
{ {
P_GivePlayerLives(player, (player->score/50000) - (oldscore/50000)); P_GivePlayerLives(player, (player->score/50000) - (oldscore/50000));
P_PlayLivesJingle(player); P_PlayLivesJingle(player);
@ -9305,7 +9305,7 @@ boolean P_GetLives(player_t *player)
{ {
INT32 i, maxlivesplayer = -1, livescheck = 1; INT32 i, maxlivesplayer = -1, livescheck = 1;
if (!(netgame || multiplayer) if (!(netgame || multiplayer)
|| (gametype != GT_COOP) || !G_GametypeUsesCoopLives()
|| (player->lives == INFLIVES)) || (player->lives == INFLIVES))
return true; return true;
@ -9454,7 +9454,7 @@ static void P_DeathThink(player_t *player)
player->playerstate = PST_REBORN; player->playerstate = PST_REBORN;
else if ((player->lives > 0 || j != MAXPLAYERS) && !(!(netgame || multiplayer) && G_IsSpecialStage(gamemap))) // Don't allow "click to respawn" in special stages! else if ((player->lives > 0 || j != MAXPLAYERS) && !(!(netgame || multiplayer) && G_IsSpecialStage(gamemap))) // Don't allow "click to respawn" in special stages!
{ {
if (gametype == GT_COOP && (netgame || multiplayer) && cv_coopstarposts.value == 2) if (G_GametypeUsesCoopStarposts() && (netgame || multiplayer) && cv_coopstarposts.value == 2)
{ {
P_ConsiderAllGone(); P_ConsiderAllGone();
if ((player->deadtimer > TICRATE<<1) || ((cmd->buttons & BT_JUMP) && (player->deadtimer > TICRATE))) if ((player->deadtimer > TICRATE<<1) || ((cmd->buttons & BT_JUMP) && (player->deadtimer > TICRATE)))
@ -9469,19 +9469,22 @@ static void P_DeathThink(player_t *player)
// Respawn with jump button, force respawn time (3 second default, cheat protected) in shooter modes. // Respawn with jump button, force respawn time (3 second default, cheat protected) in shooter modes.
if (cmd->buttons & BT_JUMP) if (cmd->buttons & BT_JUMP)
{ {
if (gametype != GT_COOP && player->spectator) // You're a spectator, so respawn right away.
if ((gametyperules & GTR_SPECTATORS) && player->spectator)
player->playerstate = PST_REBORN; player->playerstate = PST_REBORN;
else switch(gametype) { else
case GT_COOP: {
case GT_COMPETITION: // Give me one second.
case GT_RACE: INT32 respawndelay = TICRATE;
if (player->deadtimer > TICRATE)
player->playerstate = PST_REBORN; // Non-platform gametypes
break; if (gametyperules & GTR_RESPAWNDELAY)
default: respawndelay = (cv_respawntime.value*TICRATE);
if (player->deadtimer > cv_respawntime.value*TICRATE)
player->playerstate = PST_REBORN; // You've been dead for enough time.
break; // You may now respawn.
if (player->deadtimer > respawndelay)
player->playerstate = PST_REBORN;
} }
} }
@ -9495,7 +9498,7 @@ static void P_DeathThink(player_t *player)
INT32 i, deadtimercheck = INT32_MAX; INT32 i, deadtimercheck = INT32_MAX;
// In a net/multiplayer game, and out of lives // In a net/multiplayer game, and out of lives
if (gametype == GT_COMPETITION) if (G_CompetitionGametype())
{ {
for (i = 0; i < MAXPLAYERS; i++) for (i = 0; i < MAXPLAYERS; i++)
{ {
@ -11560,7 +11563,7 @@ void P_PlayerThink(player_t *player)
#else #else
if (player->spectator && if (player->spectator &&
#endif #endif
gametype == GT_COOP && (netgame || multiplayer) && cv_coopstarposts.value == 2) G_GametypeUsesCoopStarposts() && (netgame || multiplayer) && cv_coopstarposts.value == 2)
P_ConsiderAllGone(); P_ConsiderAllGone();
if (player->playerstate == PST_DEAD) if (player->playerstate == PST_DEAD)

View File

@ -694,7 +694,7 @@ static void ST_drawTime(void)
else else
{ {
// Counting down the hidetime? // Counting down the hidetime?
if ((gametyperules & GTR_HIDETIME) && (stplyr->realtime <= (hidetime*TICRATE))) if ((gametyperules & GTR_STARTCOUNTDOWN) && (stplyr->realtime <= (hidetime*TICRATE)))
{ {
tics = (hidetime*TICRATE - stplyr->realtime); tics = (hidetime*TICRATE - stplyr->realtime);
if (tics < 3*TICRATE) if (tics < 3*TICRATE)
@ -705,7 +705,7 @@ static void ST_drawTime(void)
else else
{ {
// Hidetime finish! // Hidetime finish!
if ((gametyperules & GTR_HIDETIME) && (stplyr->realtime < ((hidetime+1)*TICRATE))) if ((gametyperules & GTR_STARTCOUNTDOWN) && (stplyr->realtime < ((hidetime+1)*TICRATE)))
ST_drawRaceNum(hidetime*TICRATE - stplyr->realtime); ST_drawRaceNum(hidetime*TICRATE - stplyr->realtime);
// Time limit? // Time limit?
@ -723,7 +723,7 @@ static void ST_drawTime(void)
downwards = true; downwards = true;
} }
// Post-hidetime normal. // Post-hidetime normal.
else if (gametyperules & GTR_TAG) else if (gametyperules & GTR_STARTCOUNTDOWN)
tics = stplyr->realtime - hidetime*TICRATE; tics = stplyr->realtime - hidetime*TICRATE;
// "Shadow! What are you doing? Hurry and get back here // "Shadow! What are you doing? Hurry and get back here
// right now before the island blows up with you on it!" // right now before the island blows up with you on it!"
@ -845,69 +845,13 @@ static void ST_drawLivesArea(void)
hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, faceprefix[stplyr->skin], colormap); hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, faceprefix[stplyr->skin], colormap);
} }
// Lives number // Metal Sonic recording
if (metalrecording) if (metalrecording)
{ {
if (((2*leveltime)/TICRATE) & 1) if (((2*leveltime)/TICRATE) & 1)
V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8,
hudinfo[HUD_LIVES].f|V_PERPLAYER|V_REDMAP|V_HUDTRANS, "REC"); hudinfo[HUD_LIVES].f|V_PERPLAYER|V_REDMAP|V_HUDTRANS, "REC");
} }
else if (G_GametypeUsesLives() || gametype == GT_RACE)
{
// x
V_DrawScaledPatch(hudinfo[HUD_LIVES].x+22, hudinfo[HUD_LIVES].y+10,
hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, stlivex);
// lives number
if (gametype == GT_RACE)
{
livescount = INFLIVES;
notgreyedout = true;
}
else if ((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 3)
{
INT32 i;
livescount = 0;
notgreyedout = (stplyr->lives > 0);
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].lives < 1)
continue;
if (players[i].lives > 1)
notgreyedout = true;
if (players[i].lives == INFLIVES)
{
livescount = INFLIVES;
break;
}
else if (livescount < 99)
livescount += (players[i].lives);
}
}
else
{
livescount = (((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) ? INFLIVES : stplyr->lives);
notgreyedout = true;
}
if (livescount == INFLIVES)
V_DrawCharacter(hudinfo[HUD_LIVES].x+50, hudinfo[HUD_LIVES].y+8,
'\x16' | 0x80 | hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, false);
else
{
if (stplyr->playerstate == PST_DEAD && !(stplyr->spectator) && (livescount || stplyr->deadtimer < (TICRATE<<1)))
livescount++;
if (livescount > 99)
livescount = 99;
V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8,
hudinfo[HUD_LIVES].f|V_PERPLAYER|(notgreyedout ? V_HUDTRANS : V_HUDTRANSHALF), va("%d",livescount));
}
}
// Spectator // Spectator
else if (stplyr->spectator) else if (stplyr->spectator)
v_colmap = V_GRAYMAP; v_colmap = V_GRAYMAP;
@ -934,6 +878,82 @@ static void ST_drawLivesArea(void)
v_colmap = V_BLUEMAP; v_colmap = V_BLUEMAP;
} }
} }
// Lives number
else
{
boolean candrawlives = true;
// Co-op and Competition, normal life counter
if (G_GametypeUsesLives())
{
// Handle cooplives here
if ((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 3)
{
INT32 i;
livescount = 0;
notgreyedout = (stplyr->lives > 0);
for (i = 0; i < MAXPLAYERS; i++)
{
if (!playeringame[i])
continue;
if (players[i].lives < 1)
continue;
if (players[i].lives > 1)
notgreyedout = true;
if (players[i].lives == INFLIVES)
{
livescount = INFLIVES;
break;
}
else if (livescount < 99)
livescount += (players[i].lives);
}
}
else
{
livescount = (((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 0) ? INFLIVES : stplyr->lives);
notgreyedout = true;
}
}
// Infinity symbol (Race)
else if (G_PlatformGametype() && !(gametyperules & GTR_LIVES))
{
livescount = INFLIVES;
notgreyedout = true;
}
// Otherwise nothing, sorry.
// Special Stages keep not showing lives,
// as G_GametypeUsesLives() returns false in
// Special Stages, and the infinity symbol
// cannot show up because Special Stages
// still have the GTR_LIVES gametype rule
// by default.
else
candrawlives = false;
// Draw the lives counter here.
if (candrawlives)
{
// x
V_DrawScaledPatch(hudinfo[HUD_LIVES].x+22, hudinfo[HUD_LIVES].y+10, hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, stlivex);
if (livescount == INFLIVES)
V_DrawCharacter(hudinfo[HUD_LIVES].x+50, hudinfo[HUD_LIVES].y+8,
'\x16' | 0x80 | hudinfo[HUD_LIVES].f|V_PERPLAYER|V_HUDTRANS, false);
else
{
if (stplyr->playerstate == PST_DEAD && !(stplyr->spectator) && (livescount || stplyr->deadtimer < (TICRATE<<1)))
livescount++;
if (livescount > 99)
livescount = 99;
V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8,
hudinfo[HUD_LIVES].f|V_PERPLAYER|(notgreyedout ? V_HUDTRANS : V_HUDTRANSHALF), va("%d",livescount));
}
}
#undef ST_drawLivesX
}
// name // name
v_colmap |= (V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER); v_colmap |= (V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER);
@ -2202,7 +2222,7 @@ static void ST_drawTextHUD(void)
donef12 = true; donef12 = true;
} }
} }
else if (!G_PlatformGametype() && stplyr->playerstate == PST_DEAD && stplyr->lives) // Death overrides spectator text. else if ((gametyperules & GTR_RESPAWNDELAY) && stplyr->playerstate == PST_DEAD && stplyr->lives) // Death overrides spectator text.
{ {
INT32 respawntime = cv_respawntime.value - stplyr->deadtimer/TICRATE; INT32 respawntime = cv_respawntime.value - stplyr->deadtimer/TICRATE;
@ -2226,7 +2246,7 @@ static void ST_drawTextHUD(void)
textHUDdraw(M_GetText("\x82""Wait for the stage to end...")) textHUDdraw(M_GetText("\x82""Wait for the stage to end..."))
else if (G_PlatformGametype()) else if (G_PlatformGametype())
{ {
if (gametype == GT_COOP) if (G_GametypeUsesCoopLives())
{ {
if (stplyr->lives <= 0 if (stplyr->lives <= 0
&& cv_cooplives.value == 2 && cv_cooplives.value == 2
@ -2637,7 +2657,7 @@ static void ST_overlayDrawer(void)
INT32 i = MAXPLAYERS; INT32 i = MAXPLAYERS;
INT32 deadtimer = stplyr->spectator ? TICRATE : (stplyr->deadtimer-(TICRATE<<1)); INT32 deadtimer = stplyr->spectator ? TICRATE : (stplyr->deadtimer-(TICRATE<<1));
if ((gametype == GT_COOP) if (G_GametypeUsesCoopLives()
&& (netgame || multiplayer) && (netgame || multiplayer)
&& (cv_cooplives.value != 1)) && (cv_cooplives.value != 1))
{ {

View File

@ -1997,7 +1997,7 @@ static void Y_AwardCoopBonuses(void)
if (i == consoleplayer) if (i == consoleplayer)
{ {
data.coop.gotlife = (((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) ? 0 : ptlives); data.coop.gotlife = (((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 0) ? 0 : ptlives);
M_Memcpy(&data.coop.bonuses, &localbonuses, sizeof(data.coop.bonuses)); M_Memcpy(&data.coop.bonuses, &localbonuses, sizeof(data.coop.bonuses));
} }
} }
@ -2052,7 +2052,7 @@ static void Y_AwardSpecialStageBonus(void)
if (i == consoleplayer) if (i == consoleplayer)
{ {
data.spec.gotlife = (((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 0) ? 0 : ptlives); data.spec.gotlife = (((netgame || multiplayer) && G_GametypeUsesCoopLives() && cv_cooplives.value == 0) ? 0 : ptlives);
M_Memcpy(&data.spec.bonuses, &localbonuses, sizeof(data.spec.bonuses)); M_Memcpy(&data.spec.bonuses, &localbonuses, sizeof(data.spec.bonuses));
// Continues related // Continues related