diff --git a/src/d_clisrv.c b/src/d_clisrv.c index af2be129f..302294557 100644 --- a/src/d_clisrv.c +++ b/src/d_clisrv.c @@ -1293,7 +1293,8 @@ static void SV_SendServerInfo(INT32 node, tic_t servertime) netbuffer->u.serverinfo.numberofplayer = (UINT8)D_NumPlayers(); 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.cheatsenabled = CV_CheatsEnabled(); netbuffer->u.serverinfo.isdedicated = (UINT8)dedicated; @@ -2122,13 +2123,10 @@ static void CL_ConnectToServer(boolean viams) if (i != -1) { - UINT16 num = serverlist[i].info.gametype; - const char *gametypestr = NULL; + char *gametypestr = serverlist[i].info.gametypename; CONS_Printf(M_GetText("Connecting to: %s\n"), serverlist[i].info.servername); - if (num < gametypecount) - gametypestr = Gametype_Names[num]; - if (gametypestr) - CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr); + gametypestr[sizeof serverlist[i].info.gametypename - 1] = '\0'; + CONS_Printf(M_GetText("Gametype: %s\n"), gametypestr); CONS_Printf(M_GetText("Version: %d.%d.%u\n"), serverlist[i].info.version/100, 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.application [sizeof netbuffer->u.serverinfo.application - 1] = '\0'; + netbuffer->u.serverinfo.gametypename + [sizeof netbuffer->u.serverinfo.gametypename - 1] = '\0'; SL_InsertServer(&netbuffer->u.serverinfo, node); } diff --git a/src/d_clisrv.h b/src/d_clisrv.h index 49f8afc76..c797e5ca8 100644 --- a/src/d_clisrv.h +++ b/src/d_clisrv.h @@ -27,7 +27,7 @@ This version is independent of the mod name, and standard version and subversion. It should only account for the basic fields of the packet, and change infrequently. */ -#define PACKETVERSION 0 +#define PACKETVERSION 1 // Network play related stuff. // There is a data struct that stores network @@ -361,7 +361,7 @@ typedef struct UINT8 subversion; UINT8 numberofplayer; UINT8 maxplayer; - UINT8 gametype; + char gametypename[24]; UINT8 modifiedgame; UINT8 cheatsenabled; UINT8 isdedicated; diff --git a/src/d_main.c b/src/d_main.c index 51c04e17b..e47ff38bf 100644 --- a/src/d_main.c +++ b/src/d_main.c @@ -766,7 +766,7 @@ void D_StartTitle(void) if (netgame) { - if (gametype == GT_COOP) + if (gametyperules & GTR_CAMPAIGN) { G_SetGamestate(GS_WAITINGPLAYERS); // hack to prevent a command repeat diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 02ba1f2a3..336fd1ac8 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3747,7 +3747,7 @@ static void CoopStarposts_OnChange(void) { INT32 i; - if (!(netgame || multiplayer) || gametype != GT_COOP) + if (!(netgame || multiplayer) || !G_GametypeUsesCoopStarposts()) return; switch (cv_coopstarposts.value) @@ -3802,7 +3802,7 @@ static void CoopLives_OnChange(void) { INT32 i; - if (!(netgame || multiplayer) || gametype != GT_COOP) + if (!(netgame || multiplayer) || !G_GametypeUsesCoopLives()) return; switch (cv_cooplives.value) diff --git a/src/dehacked.c b/src/dehacked.c index 125086d44..4b6d76a6a 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -8904,32 +8904,35 @@ static const char *const GAMETYPERULE_LIST[] = { "CAMPAIGN", "RINGSLINGER", "SPECTATORS", - "FRIENDLYFIRE", "LIVES", "TEAMS", + "FIRSTPERSON", + "POWERSTONES", + "TEAMFLAGS", + "FRIENDLY", + "SPECIALSTAGES", + "EMERALDTOKENS", + "EMERALDHUNT", "RACE", "TAG", "POINTLIMIT", "TIMELIMIT", - "HIDETIME", + "OVERTIME", + "HURTMESSAGES", + "FRIENDLYFIRE", + "STARTCOUNTDOWN", "HIDEFROZEN", "BLINDFOLDED", - "FIRSTPERSON", - "MATCHEMERALDS", - "TEAMFLAGS", + "RESPAWNDELAY", "PITYSHIELD", "DEATHPENALTY", "NOSPECTATORSPAWN", "DEATHMATCHSTARTS", - "SPECIALSTAGES", - "EMERALDTOKENS", - "EMERALDHUNT", + "SPAWNINVUL", "SPAWNENEMIES", "ALLOWEXIT", "NOTITLECARD", - "OVERTIME", - "HURTMESSAGES", - "SPAWNINVUL", + "CUTSCENES", NULL }; diff --git a/src/doomstat.h b/src/doomstat.h index b7bb7a362..6d1d6eb36 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -397,32 +397,35 @@ enum GameTypeRules 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_SPECTATORS = 1<<2, // Outside of Co-op, Competition, and Race - GTR_FRIENDLYFIRE = 1<<3, // Always allow friendly fire - GTR_LIVES = 1<<4, // Co-op and Competition - GTR_TEAMS = 1<<5, // Team Match, CTF - GTR_RACE = 1<<6, // Race and Competition - GTR_TAG = 1<<7, // Tag and Hide and Seek - GTR_POINTLIMIT = 1<<8, // Ringslinger point limit - GTR_TIMELIMIT = 1<<9, // Ringslinger time limit - GTR_HIDETIME = 1<<10, // Hide time (Tag and Hide and Seek) - GTR_HIDEFROZEN = 1<<11, // Frozen after hide time (Hide and Seek, but not Tag) - GTR_BLINDFOLDED = 1<<12, // Blindfolded view (Tag and Hide and Seek) - GTR_FIRSTPERSON = 1<<13, // First person camera - GTR_MATCHEMERALDS = 1<<14, // Ringslinger emeralds (Match and CTF) - GTR_TEAMFLAGS = 1<<15, // Gametype has team flags (CTF) - GTR_PITYSHIELD = 1<<16, // Award pity shield - GTR_DEATHPENALTY = 1<<17, // Death score penalty - GTR_NOSPECTATORSPAWN = 1<<18, // Use with GTR_SPECTATORS, spawn in the map instead of with the spectators - GTR_DEATHMATCHSTARTS = 1<<19, // Use deathmatch starts - GTR_SPECIALSTAGES = 1<<20, // Allow special stages - GTR_EMERALDTOKENS = 1<<21, // Spawn emerald tokens - GTR_EMERALDHUNT = 1<<22, // Emerald Hunt - GTR_SPAWNENEMIES = 1<<23, // Spawn enemies - GTR_ALLOWEXIT = 1<<24, // Allow exit sectors - GTR_NOTITLECARD = 1<<25, // Don't show the title card - GTR_OVERTIME = 1<<26, // Allow overtime - GTR_HURTMESSAGES = 1<<27, // Hit and death messages - GTR_SPAWNINVUL = 1<<28, // Babysitting deterrent + GTR_LIVES = 1<<3, // Co-op and Competition + GTR_TEAMS = 1<<4, // Team Match, CTF + GTR_FIRSTPERSON = 1<<5, // First person camera + GTR_POWERSTONES = 1<<6, // Power stones (Match and CTF) + GTR_TEAMFLAGS = 1<<7, // Gametype has team flags (CTF) + GTR_FRIENDLY = 1<<8, // Co-op + GTR_SPECIALSTAGES = 1<<9, // Allow special stages + GTR_EMERALDTOKENS = 1<<10, // Spawn emerald tokens + GTR_EMERALDHUNT = 1<<11, // Emerald Hunt + GTR_RACE = 1<<12, // Race and Competition + GTR_TAG = 1<<13, // Tag and Hide and Seek + GTR_POINTLIMIT = 1<<14, // Ringslinger point limit + GTR_TIMELIMIT = 1<<15, // Ringslinger time limit + GTR_OVERTIME = 1<<16, // Allow overtime + GTR_HURTMESSAGES = 1<<17, // Hit and death messages + GTR_FRIENDLYFIRE = 1<<18, // Always allow friendly fire + GTR_STARTCOUNTDOWN = 1<<19, // Hide time countdown (Tag and Hide and Seek) + GTR_HIDEFROZEN = 1<<20, // Frozen after hide time (Hide and Seek, but not Tag) + GTR_BLINDFOLDED = 1<<21, // Blindfolded view (Tag and Hide and Seek) + GTR_RESPAWNDELAY = 1<<22, // Respawn delay + GTR_PITYSHIELD = 1<<23, // Award pity shield + GTR_DEATHPENALTY = 1<<24, // Death score penalty + GTR_NOSPECTATORSPAWN = 1<<25, // Use with GTR_SPECTATORS, spawn in the map instead of with the spectators + GTR_DEATHMATCHSTARTS = 1<<26, // Use deathmatch starts + GTR_SPAWNINVUL = 1<<27, // 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 diff --git a/src/g_game.c b/src/g_game.c index 79ea2c70b..36d67b324 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -2689,8 +2689,7 @@ void G_SpawnPlayer(INT32 playernum, boolean starpost) // -- DM/Tag/CTF-spectator/etc -- // Order: DM->CTF->Coop - else if ((gametyperules & GTR_DEATHMATCHSTARTS) || gametype == GT_MATCH || gametype == GT_TEAMMATCH || gametype == GT_CTF - || ((gametype == GT_TAG || gametype == GT_HIDEANDSEEK) && !(players[playernum].pflags & PF_TAGIT))) + else if ((gametyperules & GTR_DEATHMATCHSTARTS) && !(players[playernum].pflags & PF_TAGIT)) { if (!(spawnpoint = G_FindMatchStart(playernum)) // find a DM 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)) 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; - 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++) { @@ -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++) { @@ -3006,7 +3005,7 @@ void G_DoReborn(INT32 playernum) } // 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 tic_t maxstarposttime = 0; @@ -3077,7 +3076,7 @@ void G_AddPlayer(INT32 playernum) if (!players[i].exiting) 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; p->starpostscale = players[i].starpostscale; @@ -3194,24 +3193,24 @@ const char *Gametype_ConstantNames[NUMGAMETYPES] = UINT32 gametypedefaultrules[NUMGAMETYPES] = { // 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 GTR_RACE|GTR_LIVES|GTR_SPAWNENEMIES|GTR_EMERALDTOKENS|GTR_SPAWNINVUL|GTR_ALLOWEXIT, // Race GTR_RACE|GTR_SPAWNENEMIES|GTR_SPAWNINVUL|GTR_ALLOWEXIT, // 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 - 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 - 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 - 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 - 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) { - char *gtconst = Z_Malloc(strlen(newgtconst) + 3, PU_STATIC, NULL); - // Copy GT_ and the gametype name. - strcpy(gtconst, "GT_"); - strcat(gtconst, newgtconst); + size_t r = 0; // read + size_t w = 0; // write + char *gtconst = Z_Calloc(strlen(newgtconst) + 3, PU_STATIC, NULL); + char *tmpconst = Z_Calloc(strlen(newgtconst), PU_STATIC, NULL); + + // Copy the gametype name. + strcpy(tmpconst, newgtconst); + // Make uppercase. - strupr(gtconst); - // Remove characters. -#define REMOVECHAR(chr) \ - { \ - char *chrfind = strchr(gtconst, chr); \ - while (chrfind) \ - { \ - *chrfind = '_'; \ - chrfind = strchr(chrfind, chr); \ - } \ + strupr(tmpconst); + + // Prepare to write the new constant string now. + strcpy(gtconst, "GT_"); + + // Remove characters that will not be allowed in the constant string. + for (; r < strlen(tmpconst); r++) + { + 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 - REMOVECHAR(' ') - // 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 + // Free the temporary string. + Z_Free(tmpconst); // Finally, set the constant string. Gametype_ConstantNames[gtype] = gtconst; @@ -3440,6 +3457,28 @@ boolean G_GametypeUsesLives(void) 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 // @@ -3493,6 +3532,16 @@ boolean G_TagGametype(void) 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. * In single-player, this always returns TOL_SP. * \param gametype The gametype for which support is desired. @@ -3734,7 +3783,7 @@ void G_AfterIntermission(void) 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); else { @@ -3844,7 +3893,7 @@ static void G_DoContinued(void) void G_EndGame(void) { // Only do evaluation and credits in coop games. - if (gametype == GT_COOP) + if (gametyperules & GTR_CUTSCENES) { 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; 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); else G_DoLoadLevel(resetplayer); diff --git a/src/g_game.h b/src/g_game.h index 238dd1964..68e78789f 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -218,11 +218,14 @@ void G_SetGametypeDescription(INT16 gtype, char *descriptiontext, UINT8 leftcolo INT32 G_GetGametypeByName(const char *gametypestr); boolean G_IsSpecialStage(INT32 mapnum); boolean G_GametypeUsesLives(void); +boolean G_GametypeUsesCoopLives(void); +boolean G_GametypeUsesCoopStarposts(void); boolean G_GametypeHasTeams(void); boolean G_GametypeHasSpectators(void); boolean G_RingSlingerGametype(void); boolean G_PlatformGametype(void); boolean G_TagGametype(void); +boolean G_CompetitionGametype(void); boolean G_EnoughPlayersFinished(void); void G_ExitLevel(void); void G_NextLevel(void); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index 657a0018b..347afb735 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -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)); 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) | 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)); else if (G_TagGametype() && players[tab[i].num].pflags & PF_TAGIT) V_DrawSmallScaledPatch(x-28, y-4, 0, tagico); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 695e9367e..2a82ec512 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -2854,6 +2854,22 @@ static int lib_gGametypeUsesLives(lua_State *L) 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) { //HUDSAFE @@ -2894,6 +2910,14 @@ static int lib_gTagGametype(lua_State *L) 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) { tic_t rtic = luaL_checkinteger(L, 1); @@ -3139,11 +3163,14 @@ static luaL_Reg lib[] = { {"G_ExitLevel",lib_gExitLevel}, {"G_IsSpecialStage",lib_gIsSpecialStage}, {"G_GametypeUsesLives",lib_gGametypeUsesLives}, + {"G_GametypeUsesCoopLives",lib_gGametypeUsesCoopLives}, + {"G_GametypeUsesCoopStarposts",lib_gGametypeUsesCoopStarposts}, {"G_GametypeHasTeams",lib_gGametypeHasTeams}, {"G_GametypeHasSpectators",lib_gGametypeHasSpectators}, {"G_RingSlingerGametype",lib_gRingSlingerGametype}, {"G_PlatformGametype",lib_gPlatformGametype}, {"G_TagGametype",lib_gTagGametype}, + {"G_CompetitionGametype",lib_gCompetitionGametype}, {"G_TicsToHours",lib_gTicsToHours}, {"G_TicsToMinutes",lib_gTicsToMinutes}, {"G_TicsToSeconds",lib_gTicsToSeconds}, diff --git a/src/lua_hook.h b/src/lua_hook.h index e2e704bb2..e6aa3688a 100644 --- a/src/lua_hook.h +++ b/src/lua_hook.h @@ -53,6 +53,7 @@ enum hook { hook_IntermissionThinker, hook_TeamSwitch, hook_ViewpointSwitch, + hook_SeenPlayer, hook_PlayerThink, 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 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 +#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 #endif diff --git a/src/lua_hooklib.c b/src/lua_hooklib.c index 02075eb2b..d2d26cc0b 100644 --- a/src/lua_hooklib.c +++ b/src/lua_hooklib.c @@ -64,6 +64,7 @@ const char *const hookNames[hook_MAX+1] = { "IntermissionThinker", "TeamSwitch", "ViewpointSwitch", + "SeenPlayer", "PlayerThink", NULL }; @@ -208,6 +209,7 @@ static int lib_addHook(lua_State *L) case hook_PlayerCanDamage: case hook_TeamSwitch: case hook_ViewpointSwitch: + case hook_SeenPlayer: case hook_ShieldSpawn: case hook_ShieldSpecial: case hook_PlayerThink: @@ -1414,7 +1416,7 @@ UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean return 0; lua_settop(gL, 0); - hud_running = true; + hud_running = true; // local hook for (hookp = playerhooks; hookp; hookp = hookp->next) { @@ -1455,4 +1457,49 @@ UINT8 LUAh_ViewpointSwitch(player_t *player, player_t *newdisplayplayer, boolean 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 diff --git a/src/lua_script.c b/src/lua_script.c index 18d9a87c2..eb1afaf09 100644 --- a/src/lua_script.c +++ b/src/lua_script.c @@ -299,9 +299,7 @@ int LUA_PushGlobals(lua_State *L, const char *word) // See the above. int LUA_CheckGlobals(lua_State *L, const char *word) { - if (fastcmp(word, "gametyperules")) - gametyperules = (UINT32)luaL_checkinteger(L, 2); - else if (fastcmp(word, "redscore")) + if (fastcmp(word, "redscore")) redscore = (UINT32)luaL_checkinteger(L, 2); else if (fastcmp(word, "bluescore")) bluescore = (UINT32)luaL_checkinteger(L, 2); diff --git a/src/m_menu.c b/src/m_menu.c index 6ef9a5e19..06cbc9988 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -4735,7 +4735,7 @@ static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt) if (gt == GT_RACE && (mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) 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 false; @@ -9971,7 +9971,7 @@ static void M_DrawRoomMenu(void) static void M_DrawConnectMenu(void) { UINT16 i; - const char *gt = "Unknown"; + char *gt; INT32 numPages = (serverlistcount+(SERVERS_PER_PAGE-1))/SERVERS_PER_PAGE; 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, va("Ping: %u", (UINT32)LONG(serverlist[slindex].info.time))); - gt = "Unknown"; - if (serverlist[slindex].info.gametype < gametypecount) - gt = Gametype_Names[serverlist[slindex].info.gametype]; + gt = serverlist[slindex].info.gametypename; V_DrawSmallString(currentMenu->x+46,S_LINEY(i)+8, globalflags, 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; } @@ -10063,7 +10066,15 @@ SERVER_LIST_ENTRY_COMPARATOR(time) SERVER_LIST_ENTRY_COMPARATOR(numberofplayer) SERVER_LIST_ENTRY_COMPARATOR_REVERSE(numberofplayer) 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. 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); break; case 5: // Gametype. - qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametype); + qsort(serverlist, serverlistcount, sizeof(serverelem_t), ServerListEntryComparator_gametypename); break; } #endif diff --git a/src/p_inter.c b/src/p_inter.c index dc52cd3c0..71740822e 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -1451,7 +1451,7 @@ void P_TouchSpecialThing(mobj_t *special, mobj_t *toucher, boolean heightcheck) if (player->starpostnum >= special->health) 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++) { @@ -2521,7 +2521,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget target->colorized = false; 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) && 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 { boolean gameovermus = false; - if ((netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value != 1)) + if ((netgame || multiplayer) && G_GametypeUsesCoopLives() && (cv_cooplives.value != 1)) { INT32 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; // 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); - P_PlayerEmeraldBurst(player, false); + if ((gametyperules & GTR_RINGSLINGER) && !(gametyperules & GTR_TAG)) + P_PlayerRingBurst(player, player->rings); + if (gametyperules & GTR_POWERSTONES) + P_PlayerEmeraldBurst(player, false); } // Get rid of shield diff --git a/src/p_map.c b/src/p_map.c index cd4ac3ca8..b291c4841 100644 --- a/src/p_map.c +++ b/src/p_map.c @@ -745,9 +745,8 @@ static boolean PIT_CheckThing(mobj_t *thing) // So that NOTHING ELSE can see MT_NAMECHECK because it is client-side. if (tmthing->type == MT_NAMECHECK) { - // 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 || thing->player->spectator || (tmthing->target && thing->player == tmthing->target->player)) + // Ignore things that aren't players, ignore spectators, ignore yourself. + if (!thing->player || !(tmthing->target && tmthing->target->player) || thing->player->spectator || (tmthing->target && thing->player == tmthing->target->player)) return true; // 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) return true; // underneath +#ifdef HAVE_BLUA + // REX HAS SEEN YOU + if (!LUAh_SeenPlayer(tmthing->target->player, thing->player)) + return false; +#endif + seenplayer = thing->player; return false; } diff --git a/src/p_mobj.c b/src/p_mobj.c index 5c0ed1292..e8461957f 100644 --- a/src/p_mobj.c +++ b/src/p_mobj.c @@ -11823,7 +11823,7 @@ static boolean P_AllowMobjSpawn(mapthing_t* mthing, mobjtype_t i) if (!cv_powerstones.value) return false; - if (!(gametyperules & GTR_MATCHEMERALDS)) + if (!(gametyperules & GTR_POWERSTONES)) return false; runemeraldmanager = true; diff --git a/src/p_setup.c b/src/p_setup.c index d556193b9..366fc5372 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -2446,7 +2446,7 @@ static void P_InitLevelSettings(void) // earthquake camera 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++) { @@ -2464,7 +2464,7 @@ static void P_InitLevelSettings(void) { 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. players[i].lives = cv_startinglives.value; @@ -2754,17 +2754,10 @@ static void P_SetupCamera(void) { mapthing_t *thing; - switch (gametype) - { - case GT_MATCH: - case GT_TAG: + if (gametyperules & GTR_DEATHMATCHSTARTS) thing = deathmatchstarts[0]; - break; - - default: + else thing = playerstarts[0]; - break; - } if (thing) { @@ -2979,7 +2972,7 @@ static void P_InitGametype(void) P_InitPlayers(); // 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 tic_t maxstarposttime = 0; @@ -3023,6 +3016,7 @@ boolean P_LoadLevel(boolean fromnetsave) // This is needed. Don't touch. maptol = mapheaderinfo[gamemap-1]->typeoflevel; + gametyperules = gametypedefaultrules[gametype]; CON_Drawer(); // let the user know what we are going to do I_FinishUpdate(); // page flip or blit buffer diff --git a/src/p_user.c b/src/p_user.c index f3e6d4c97..f8f722343 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -1230,13 +1230,13 @@ void P_GivePlayerLives(player_t *player, INT32 numlives) 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); 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); if (player->lives - prevlives >= numlives) @@ -1267,7 +1267,7 @@ docooprespawn: 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); if (sound) @@ -1395,7 +1395,7 @@ void P_AddPlayerScore(player_t *player, UINT32 amount) player->score = MAXSCORE; // 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_PlayLivesJingle(player); @@ -9305,7 +9305,7 @@ boolean P_GetLives(player_t *player) { INT32 i, maxlivesplayer = -1, livescheck = 1; if (!(netgame || multiplayer) - || (gametype != GT_COOP) + || !G_GametypeUsesCoopLives() || (player->lives == INFLIVES)) return true; @@ -9454,7 +9454,7 @@ static void P_DeathThink(player_t *player) 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! { - if (gametype == GT_COOP && (netgame || multiplayer) && cv_coopstarposts.value == 2) + if (G_GametypeUsesCoopStarposts() && (netgame || multiplayer) && cv_coopstarposts.value == 2) { P_ConsiderAllGone(); 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. 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; - else switch(gametype) { - case GT_COOP: - case GT_COMPETITION: - case GT_RACE: - if (player->deadtimer > TICRATE) - player->playerstate = PST_REBORN; - break; - default: - if (player->deadtimer > cv_respawntime.value*TICRATE) - player->playerstate = PST_REBORN; - break; + else + { + // Give me one second. + INT32 respawndelay = TICRATE; + + // Non-platform gametypes + if (gametyperules & GTR_RESPAWNDELAY) + respawndelay = (cv_respawntime.value*TICRATE); + + // You've been dead for enough time. + // 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; // In a net/multiplayer game, and out of lives - if (gametype == GT_COMPETITION) + if (G_CompetitionGametype()) { for (i = 0; i < MAXPLAYERS; i++) { @@ -11560,7 +11563,7 @@ void P_PlayerThink(player_t *player) #else if (player->spectator && #endif - gametype == GT_COOP && (netgame || multiplayer) && cv_coopstarposts.value == 2) + G_GametypeUsesCoopStarposts() && (netgame || multiplayer) && cv_coopstarposts.value == 2) P_ConsiderAllGone(); if (player->playerstate == PST_DEAD) diff --git a/src/st_stuff.c b/src/st_stuff.c index 9c4f0abd5..53c00b4c8 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -694,7 +694,7 @@ static void ST_drawTime(void) else { // 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); if (tics < 3*TICRATE) @@ -705,7 +705,7 @@ static void ST_drawTime(void) else { // 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); // Time limit? @@ -723,7 +723,7 @@ static void ST_drawTime(void) downwards = true; } // Post-hidetime normal. - else if (gametyperules & GTR_TAG) + else if (gametyperules & GTR_STARTCOUNTDOWN) tics = stplyr->realtime - hidetime*TICRATE; // "Shadow! What are you doing? Hurry and get back here // 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); } - // Lives number + // Metal Sonic recording if (metalrecording) { if (((2*leveltime)/TICRATE) & 1) V_DrawRightAlignedString(hudinfo[HUD_LIVES].x+58, hudinfo[HUD_LIVES].y+8, 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 else if (stplyr->spectator) v_colmap = V_GRAYMAP; @@ -934,6 +878,82 @@ static void ST_drawLivesArea(void) 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 v_colmap |= (V_HUDTRANS|hudinfo[HUD_LIVES].f|V_PERPLAYER); @@ -2202,7 +2222,7 @@ static void ST_drawTextHUD(void) 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; @@ -2226,7 +2246,7 @@ static void ST_drawTextHUD(void) textHUDdraw(M_GetText("\x82""Wait for the stage to end...")) else if (G_PlatformGametype()) { - if (gametype == GT_COOP) + if (G_GametypeUsesCoopLives()) { if (stplyr->lives <= 0 && cv_cooplives.value == 2 @@ -2637,7 +2657,7 @@ static void ST_overlayDrawer(void) INT32 i = MAXPLAYERS; INT32 deadtimer = stplyr->spectator ? TICRATE : (stplyr->deadtimer-(TICRATE<<1)); - if ((gametype == GT_COOP) + if (G_GametypeUsesCoopLives() && (netgame || multiplayer) && (cv_cooplives.value != 1)) { diff --git a/src/y_inter.c b/src/y_inter.c index be0550e72..214dee92e 100644 --- a/src/y_inter.c +++ b/src/y_inter.c @@ -1997,7 +1997,7 @@ static void Y_AwardCoopBonuses(void) 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)); } } @@ -2052,7 +2052,7 @@ static void Y_AwardSpecialStageBonus(void) 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)); // Continues related