From fd63db0aaff3488cde8374235297b7d14a027aff Mon Sep 17 00:00:00 2001 From: toasterbabe Date: Fri, 18 Aug 2017 00:58:16 +0100 Subject: [PATCH] Starting lives revamp, per the 2.2 priorities topic list! * Each time you die, the number of game overs you've had is counted. * Your save file updates to record this. * The number of startinglives is determined by the number of times you'ved game-overed, with the maximum being infinity lives (thereby providing a cap on the number of game overs you can go through in a typical game). Requires a new patch.dta, but I'm not uploading that yet because not happy with the icon we've got going for infinity lives on the save select menu. --- src/doomstat.h | 4 ++ src/g_game.c | 116 ++++++++++++++++++++++++++++++++++++++++++++----- src/g_game.h | 2 + src/hu_stuff.c | 6 ++- src/hu_stuff.h | 1 + src/m_menu.c | 12 +++-- src/m_menu.h | 1 - src/p_inter.c | 9 +++- src/p_saveg.c | 14 +++--- src/p_saveg.h | 1 + src/p_setup.c | 1 + src/p_user.c | 12 +++-- src/st_stuff.c | 85 +++++++++++++++--------------------- 13 files changed, 184 insertions(+), 80 deletions(-) diff --git a/src/doomstat.h b/src/doomstat.h index a24bad79d..a172eae43 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -45,6 +45,10 @@ extern INT32 cursaveslot; extern INT16 lastmaploaded; extern boolean gamecomplete; +#define maxgameovers 13 +extern UINT8 numgameovers; +extern SINT8 startinglivesbalance[maxgameovers+1]; + #define PRECIP_NONE 0 #define PRECIP_STORM 1 #define PRECIP_SNOW 2 diff --git a/src/g_game.c b/src/g_game.c index e996938ab..e16816e44 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -81,6 +81,9 @@ INT32 cursaveslot = -1; // Auto-save 1p savegame slot INT16 lastmaploaded = 0; // Last map the game loaded boolean gamecomplete = false; +UINT8 numgameovers = 0; // for startinglives balance +SINT8 startinglivesbalance[maxgameovers+1] = {3, 5, 7, 9, 12, 15, 20, 25, 30, 40, 50, 75, 99, 0x7F}; + UINT16 mainwads = 0; boolean modifiedgame; // Set if homebrew PWAD stuff has been added. boolean savemoddata = false; @@ -3130,7 +3133,7 @@ static void G_DoContinued(void) token = 0; // Reset # of lives - pl->lives = (ultimatemode) ? 1 : 3; + pl->lives = (ultimatemode) ? 1 : startinglivesbalance[numgameovers]; D_MapChange(gamemap, gametype, ultimatemode, false, 0, false, false); @@ -3614,13 +3617,13 @@ void G_LoadGame(UINT32 slot, INT16 mapoverride) // G_SaveGame // Saves your game. // -void G_SaveGame(UINT32 savegameslot) +void G_SaveGame(UINT32 slot) { boolean saved; char savename[256] = ""; const char *backup; - sprintf(savename, savegamename, savegameslot); + sprintf(savename, savegamename, slot); backup = va("%s",savename); // save during evaluation or credits? game's over, folks! @@ -3656,9 +3659,103 @@ void G_SaveGame(UINT32 savegameslot) if (cv_debug && saved) CONS_Printf(M_GetText("Game saved.\n")); else if (!saved) - CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, savegameslot, savegamename); + CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, savegamename); } +#define BADSAVE goto cleanup; +#define CHECKPOS if (save_p >= end_p) BADSAVE +void G_SaveGameOver(UINT32 slot) +{ + boolean saved = false; + size_t length; + char vcheck[VERSIONSIZE]; + char savename[255]; + const char *backup; + + sprintf(savename, savegamename, slot); + backup = va("%s",savename); + + length = FIL_ReadFile(savename, &savebuffer); + if (!length) + { + CONS_Printf(M_GetText("Couldn't read file %s\n"), savename); + return; + } + + { + char temp[sizeof(timeattackfolder)]; + INT32 fake; // Dummy variable + UINT8 *end_p = savebuffer + length; + UINT8 *lives_p; + SINT8 pllives; + + save_p = savebuffer; + // Version check + memset(vcheck, 0, sizeof (vcheck)); + sprintf(vcheck, "version %d", VERSION); +#ifndef SAVEGAMES_OTHERVERSIONS + if (strcmp((const char *)save_p, (const char *)vcheck)) + BADSAVE; +#endif + save_p += VERSIONSIZE; + + // P_UnArchiveMisc() + fake = READINT16(save_p); + CHECKPOS + (void)READUINT16(save_p); // emeralds + CHECKPOS + READSTRINGN(save_p, temp, sizeof(temp)); // mod it belongs to + if (strcmp(temp, timeattackfolder)) BADSAVE + CHECKPOS + (void)READUINT8(save_p); + CHECKPOS + (void)READUINT8(save_p); + CHECKPOS + + WRITEUINT8(save_p, numgameovers); + CHECKPOS + + lives_p = save_p; + pllives = READSINT8(save_p); // lives + CHECKPOS + if (pllives < startinglivesbalance[numgameovers]) + { + pllives = startinglivesbalance[numgameovers]; + WRITESINT8(lives_p, pllives); + } + + (void)READINT32(save_p); // Score + CHECKPOS + (void)READINT32(save_p); // continues + + if (fake & (1<<10)) + { + CHECKPOS + (void)READUINT8(save_p); + CHECKPOS + (void)READUINT8(save_p); // because why not. + } + + // File end marker check + CHECKPOS + if (READUINT8(save_p) != 0x1d) BADSAVE; + + // done + saved = FIL_WriteFile(backup, savebuffer, length); + } + +cleanup: + if (cv_debug && saved) + CONS_Printf(M_GetText("Game saved.\n")); + else if (!saved) + CONS_Alert(CONS_ERROR, M_GetText("Error while writing to %s for save slot %u, base: %s\n"), backup, slot, savegamename); + Z_Free(savebuffer); + save_p = savebuffer = NULL; + +} +#undef CHECKPOS +#undef BADSAVE + // // G_DeferedInitNew // Can be called by the startup code or the menu task, @@ -3722,7 +3819,7 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean if (resetplayer) { // Clear a bunch of variables - tokenlist = token = sstimer = redscore = bluescore = lastmap = 0; + numgameovers = tokenlist = token = sstimer = redscore = bluescore = lastmap = 0; countdown = countdown2 = 0; for (i = 0; i < MAXPLAYERS; i++) @@ -3737,15 +3834,10 @@ void G_InitNew(UINT8 pultmode, const char *mapname, boolean resetplayer, boolean players[i].lives = cv_startinglives.value; players[i].continues = 0; } - else if (pultmode) - { - players[i].lives = 1; - players[i].continues = 0; - } else { - players[i].lives = 3; - players[i].continues = 1; + players[i].lives = (pultmode) ? 1 : startinglivesbalance[0]; + players[i].continues = (pultmode) ? 0 : 1; } if (!((netgame || multiplayer) && (FLS))) diff --git a/src/g_game.h b/src/g_game.h index 72a6f3d6e..4ac78ab10 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -116,6 +116,8 @@ void G_SaveGameData(void); void G_SaveGame(UINT32 slot); +void G_SaveGameOver(UINT32 slot); + // Only called by startup code. void G_RecordDemo(const char *name); void G_RecordMetal(void); diff --git a/src/hu_stuff.c b/src/hu_stuff.c index e92b96995..b49d3eb96 100644 --- a/src/hu_stuff.c +++ b/src/hu_stuff.c @@ -83,6 +83,7 @@ patch_t *rmatcico; patch_t *bmatcico; patch_t *tagico; patch_t *tallminus; +patch_t *tallinfin; //------------------------------------------- // coop hud @@ -235,6 +236,7 @@ void HU_LoadGraphics(void) // minus for negative tallnums tallminus = (patch_t *)W_CachePatchName("STTMINUS", PU_HUDGFX); + tallinfin = (patch_t *)W_CachePatchName("STTINFIN", PU_HUDGFX); // cache the crosshairs, don't bother to know which one is being used, // just cache all 3, they're so small anyway. @@ -1250,7 +1252,7 @@ void HU_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, I } } - if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3))) //show lives + if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != 0x7f)) //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) { @@ -1388,7 +1390,7 @@ void HU_DrawDualTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scoreline | (greycheck ? V_TRANSLUCENT : 0) | V_ALLOWLOWERCASE, name); - if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3))) //show lives + if (G_GametypeUsesLives() && !(gametype == GT_COOP && (cv_cooplives.value == 0 || cv_cooplives.value == 3)) && (players[tab[i].num].lives != 0x7f)) //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/hu_stuff.h b/src/hu_stuff.h index e757db85a..2dbeb556d 100644 --- a/src/hu_stuff.h +++ b/src/hu_stuff.h @@ -71,6 +71,7 @@ extern patch_t *rmatcico; extern patch_t *bmatcico; extern patch_t *tagico; extern patch_t *tallminus; +extern patch_t *tallinfin; extern patch_t *tokenicon; // set true when entering a chat message diff --git a/src/m_menu.c b/src/m_menu.c index 64255e71a..af3b0c7ca 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -6097,7 +6097,10 @@ static void M_DrawLoadGameData(void) // Use the big face pic for lives, duh. :3 V_DrawScaledPatch(ecks + 12, 175, 0, W_CachePatchName("STLIVEX", PU_HUDGFX)); - V_DrawTallNum(ecks + 40, 172, 0, savegameinfo[saveSlotSelected].lives); + if (savegameinfo[saveSlotSelected].lives == 0x7F) + V_DrawScaledPatch(ecks + 40 - 18, 172, 0, tallinfin); + else + V_DrawTallNum(ecks + 40, 172, 0, savegameinfo[saveSlotSelected].lives); // Absolute ridiculousness, condensed into another function. V_DrawContinueIcon(ecks + 58, 182, 0, savegameinfo[saveSlotSelected].skinnum, savegameinfo[saveSlotSelected].skincolor); @@ -6291,10 +6294,11 @@ static void M_ReadSavegameInfo(UINT32 slot) savegameinfo[slot].skinnum = READUINT8(save_p); CHECKPOS - (void)READINT32(save_p); // Score - + (void)READUINT8(save_p); // numgameovers CHECKPOS - savegameinfo[slot].lives = READINT32(save_p); // lives + savegameinfo[slot].lives = READSINT8(save_p); // lives + CHECKPOS + (void)READINT32(save_p); // Score CHECKPOS savegameinfo[slot].continues = READINT32(save_p); // continues diff --git a/src/m_menu.h b/src/m_menu.h index 53dc266d1..2d2d3378b 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -226,7 +226,6 @@ typedef struct INT32 lives; INT32 continues; INT32 gamemap; - UINT8 netgame; } saveinfo_t; extern description_t description[32]; diff --git a/src/p_inter.c b/src/p_inter.c index d2101ca57..284003c31 100644 --- a/src/p_inter.c +++ b/src/p_inter.c @@ -2259,7 +2259,7 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget if ((target->player->lives <= 1) && (netgame || multiplayer) && (gametype == GT_COOP) && (cv_cooplives.value == 0)) ; - else if (!target->player->bot && !target->player->spectator && !G_IsSpecialStage(gamemap) + else if (!target->player->bot && !target->player->spectator && !G_IsSpecialStage(gamemap) && (target->player->lives != 0x7f) && G_GametypeUsesLives()) { target->player->lives -= 1; // Lose a life Tails 03-11-2000 @@ -2289,6 +2289,13 @@ void P_KillMobj(mobj_t *target, mobj_t *inflictor, mobj_t *source, UINT8 damaget S_StopMusic(); // Stop the Music! Tails 03-14-2000 S_ChangeMusicInternal("_gover", false); // Yousa dead now, Okieday? Tails 03-14-2000 } + + if (!(netgame || multiplayer || demoplayback || demorecording || metalrecording || modeattacking) && numgameovers < maxgameovers) + { + numgameovers++; + if ((!modifiedgame || savemoddata) && cursaveslot >= 0) + G_SaveGameOver((UINT32)cursaveslot); + } } } target->player->playerstate = PST_DEAD; diff --git a/src/p_saveg.c b/src/p_saveg.c index 2d3412e65..9e1206c49 100644 --- a/src/p_saveg.c +++ b/src/p_saveg.c @@ -64,15 +64,16 @@ typedef enum static inline void P_ArchivePlayer(void) { const player_t *player = &players[consoleplayer]; - INT32 pllives = player->lives; - if (pllives < 3) // Bump up to 3 lives if the player - pllives = 3; // has less than that. + SINT8 pllives = player->lives; + if (pllives < startinglivesbalance[numgameovers]) // Bump up to 3 lives if the player + pllives = startinglivesbalance[numgameovers]; // has less than that. WRITEUINT8(save_p, player->skincolor); WRITEUINT8(save_p, player->skin); + WRITEUINT8(save_p, numgameovers); + WRITESINT8(save_p, pllives); WRITEUINT32(save_p, player->score); - WRITEINT32(save_p, pllives); WRITEINT32(save_p, player->continues); if (botskin) @@ -90,8 +91,9 @@ static inline void P_UnArchivePlayer(void) savedata.skincolor = READUINT8(save_p); savedata.skin = READUINT8(save_p); - savedata.score = READINT32(save_p); - savedata.lives = READINT32(save_p); + savedata.numgameovers = READUINT8(save_p); + savedata.lives = READSINT8(save_p); + savedata.score = READUINT32(save_p); savedata.continues = READINT32(save_p); if (savedata.botcolor) diff --git a/src/p_saveg.h b/src/p_saveg.h index 3670d3503..376552c95 100644 --- a/src/p_saveg.h +++ b/src/p_saveg.h @@ -38,6 +38,7 @@ typedef struct INT32 lives; INT32 continues; UINT16 emeralds; + UINT8 numgameovers; } savedata_t; extern savedata_t savedata; diff --git a/src/p_setup.c b/src/p_setup.c index 9c4bede74..c131f6677 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -3026,6 +3026,7 @@ boolean P_SetupLevel(boolean skipprecip) if (savedata.lives > 0) { + numgameovers = savedata.numgameovers; players[consoleplayer].continues = savedata.continues; players[consoleplayer].lives = savedata.lives; players[consoleplayer].score = savedata.score; diff --git a/src/p_user.c b/src/p_user.c index 09cafa0b3..482bcc65d 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -950,6 +950,8 @@ void P_GivePlayerRings(player_t *player, INT32 num_rings) // void P_GivePlayerLives(player_t *player, INT32 numlives) { + if (player->lives == 0x7f) return; + player->lives += numlives; if (player->lives > 99) @@ -1153,7 +1155,9 @@ void P_PlayLivesJingle(player_t *player) if (player && !P_IsLocalPlayer(player)) return; - if (gametype == GT_COOP && (netgame || multiplayer) && cv_cooplives.value == 0) + if ((player && player->lives == 0x7f) + || (!player && &players[consoleplayer] && players[consoleplayer].lives == 0x7f) + || (gametype == GT_COOP && (netgame || multiplayer) && cv_cooplives.value == 0)) S_StartSound(NULL, sfx_lose); else if (use1upSound) S_StartSound(NULL, sfx_oneup); @@ -8160,7 +8164,8 @@ boolean P_GetLives(player_t *player) INT32 i, maxlivesplayer = -1, livescheck = 1; if (!(netgame || multiplayer) || (gametype != GT_COOP) - || (cv_cooplives.value == 1)) + || (cv_cooplives.value == 1) + || (player->lives == 0x7f)) return true; if ((cv_cooplives.value == 2 || cv_cooplives.value == 0) && player->lives > 0) @@ -8187,7 +8192,8 @@ boolean P_GetLives(player_t *player) { if (cv_cooplives.value == 2 && (P_IsLocalPlayer(player) || P_IsLocalPlayer(&players[maxlivesplayer]))) S_StartSound(NULL, sfx_jshard); // placeholder - players[maxlivesplayer].lives--; + if (players[maxlivesplayer].lives != 0x7f) + players[maxlivesplayer].lives--; player->lives++; if (player->lives < 1) player->lives = 1; diff --git a/src/st_stuff.c b/src/st_stuff.c index 4515495af..ceef586a4 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -684,6 +684,8 @@ static inline void ST_drawRings(void) static void ST_drawLives(void) { const INT32 v_splitflag = (splitscreen && stplyr == &players[displayplayer] ? V_SPLITSCREEN : 0); + INT32 livescount; + boolean notgreyedout; if (!stplyr->skincolor) return; // Just joined a server, skin isn't loaded yet! @@ -723,66 +725,47 @@ static void ST_drawLives(void) V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS|v_splitflag, stlivex); // lives number - if ((netgame || multiplayer) && gametype == GT_COOP) + if ((netgame || multiplayer) && gametype == GT_COOP && cv_cooplives.value == 3) { - switch (cv_cooplives.value) + INT32 i; + livescount = 0; + notgreyedout = (stplyr->lives > 0); + for (i = 0; i < MAXPLAYERS; i++) { - case 0: - V_DrawCharacter(hudinfo[HUD_LIVESNUM].x - 8, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), '\x16' | 0x80 | V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS|v_splitflag, false); - return; - case 3: - { - INT32 i, sum = 0; - boolean canrespawn = (stplyr->lives > 0); - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; + if (!playeringame[i]) + continue; - if (players[i].lives < 1) - continue; + if (players[i].lives < 1) + continue; - if (players[i].lives > 1) - canrespawn = true; + if (players[i].lives > 1) + notgreyedout = true; - sum += (players[i].lives); - } - V_DrawRightAlignedString(hudinfo[HUD_LIVESNUM].x, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), - V_SNAPTOLEFT|V_SNAPTOBOTTOM|(canrespawn ? V_HUDTRANS : V_HUDTRANSHALF)|v_splitflag, - va("%d",sum)); - return; - } -#if 0 // render the number of lives you COULD steal - case 2: - { - INT32 i, sum = 0; - for (i = 0; i < MAXPLAYERS; i++) - { - if (!playeringame[i]) - continue; - - if (&players[i] == stplyr) - continue; - - if (players[i].lives < 2) - continue; - - sum += (players[i].lives - 1); - } - V_DrawString(hudinfo[HUD_LIVESNUM].x, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), - V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANSHALF|v_splitflag, va("/%d",sum)); - } - // intentional fallthrough -#endif - default: - // don't return so the SP one can be drawn below + if (players[i].lives == 0x7f) + { + livescount = 0x7f; break; + } + else if (livescount < 99) + livescount += (players[i].lives); } } + else + { + livescount = stplyr->lives; + notgreyedout = true; + } - V_DrawRightAlignedString(hudinfo[HUD_LIVESNUM].x, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), - V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS|v_splitflag, - va("%d",stplyr->lives)); + if (livescount == 0x7f) + V_DrawCharacter(hudinfo[HUD_LIVESNUM].x - 8, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), '\x16' | 0x80 | V_SNAPTOLEFT|V_SNAPTOBOTTOM|V_HUDTRANS|v_splitflag, false); + else + { + if (livescount > 99) + livescount = 99; + V_DrawRightAlignedString(hudinfo[HUD_LIVESNUM].x, hudinfo[HUD_LIVESNUM].y + (v_splitflag ? -4 : 0), + V_SNAPTOLEFT|V_SNAPTOBOTTOM|(notgreyedout ? V_HUDTRANS : V_HUDTRANSHALF)|v_splitflag, + ((livescount > 99) ? "!!" : va("%d",livescount))); + } } static void ST_drawLevelTitle(void)