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.
This commit is contained in:
toasterbabe 2017-08-18 00:58:16 +01:00
parent 3795a15164
commit fd63db0aaf
13 changed files with 184 additions and 80 deletions

View File

@ -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

View File

@ -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)))

View File

@ -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);

View File

@ -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);

View File

@ -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

View File

@ -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

View File

@ -226,7 +226,6 @@ typedef struct
INT32 lives;
INT32 continues;
INT32 gamemap;
UINT8 netgame;
} saveinfo_t;
extern description_t description[32];

View File

@ -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;

View File

@ -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)

View File

@ -38,6 +38,7 @@ typedef struct
INT32 lives;
INT32 continues;
UINT16 emeralds;
UINT8 numgameovers;
} savedata_t;
extern savedata_t savedata;

View File

@ -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;

View File

@ -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;

View File

@ -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)