diff --git a/src/deh_soc.c b/src/deh_soc.c index 5ff18f3bf..42dde0a2f 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -2370,6 +2370,18 @@ static void readcondition(UINT8 set, UINT32 id, char *word2) ty = UC_PLAYTIME + offset; re = atoi(params[1]); } + else if (fastcmp(params[0], "TOTALRINGS")) + { + PARAMCHECK(1); + ty = UC_TOTALRINGS; + re = atoi(params[1]); + + if (re < 2 || re > GDMAX_RINGS) + { + deh_warning("Total Rings requirement %d out of range (%d - %d) for condition ID %d", re, 2, GDMAX_RINGS, id+1); + return; + } + } else if (fastcmp(params[0], "POWERLEVEL")) { PARAMCHECK(2); diff --git a/src/g_game.c b/src/g_game.c index c9660da52..80a187cfc 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4346,12 +4346,13 @@ void G_LoadGameData(void) // Clear things so previously read gamedata doesn't transfer // to new gamedata + // see also M_EraseDataResponse G_ClearRecords(); // records M_ClearSecrets(); // emblems, unlocks, maps visited, etc - gamedata->totalplaytime = 0; // total play time (separate from all) - gamedata->matchesplayed = 0; // SRB2Kart: matches played & finished - gamedata->crashflags = 0; + gamedata->totalplaytime = 0; + gamedata->matchesplayed = 0; + gamedata->totalrings = 0; if (M_CheckParm("-nodata")) { @@ -4401,6 +4402,8 @@ void G_LoadGameData(void) if (versionMinor > 1) { + gamedata->totalrings = READUINT32(save.p); + gamedata->crashflags = READUINT8(save.p); if (gamedata->crashflags & GDCRASH_LAST) gamedata->crashflags |= GDCRASH_ANY; @@ -4571,7 +4574,7 @@ void G_SaveGameData(boolean dirty) return; } - length = (4+1+4+4+1+4+(MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+4+4+2); + length = (4+1+4+4+4+1+4+(MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+4+4+2); if (gamedata->challengegrid) { length += gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT; @@ -4590,6 +4593,7 @@ void G_SaveGameData(boolean dirty) WRITEUINT8(save.p, GD_VERSIONMINOR); // 1 WRITEUINT32(save.p, gamedata->totalplaytime); // 4 WRITEUINT32(save.p, gamedata->matchesplayed); // 4 + WRITEUINT32(save.p, gamedata->totalrings); // 4 { UINT8 crashflags = (gamedata->crashflags & GDCRASH_ANY); diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 49fd7a8d7..bb6786cec 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -5471,7 +5471,7 @@ bottomarrow: void M_DrawStatistics(void) { - char beststr[40]; + char beststr[256]; tic_t besttime = 0; @@ -5483,12 +5483,48 @@ void M_DrawStatistics(void) V_DrawFixedPatch(0, 0, FRACUNIT, 0, bg, NULL); } + beststr[0] = 0; V_DrawThinString(20, 22, V_6WIDTHSPACE|V_ALLOWLOWERCASE|highlightflags, "Total Play Time:"); - V_DrawCenteredThinString(BASEVIDWIDTH/2, 32, V_6WIDTHSPACE, - va("%i hours, %i minutes, %i seconds", - G_TicsToHours(gamedata->totalplaytime), - G_TicsToMinutes(gamedata->totalplaytime, false), - G_TicsToSeconds(gamedata->totalplaytime))); + besttime = G_TicsToHours(gamedata->totalplaytime); + if (besttime) + { + if (besttime >= 24) + { + strcat(beststr, va("%u days, ", besttime/24)); + besttime %= 24; + } + + strcat(beststr, va("%u hours, ", besttime)); + } + besttime = G_TicsToMinutes(gamedata->totalplaytime, false); + if (besttime) + { + strcat(beststr, va("%u minutes, ", besttime)); + } + strcat(beststr, va("%i seconds", G_TicsToSeconds(gamedata->totalplaytime))); + V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 22, V_6WIDTHSPACE, beststr); + beststr[0] = 0; + + V_DrawThinString(20, 32, V_6WIDTHSPACE|V_ALLOWLOWERCASE|highlightflags, "Total Rings:"); + if (gamedata->totalrings > GDMAX_RINGS) + { + sprintf(beststr, "%c999,999,999+", '\x82'); + } + else if (gamedata->totalrings >= 1000000) + { + sprintf(beststr, "%u,%u,%u", (gamedata->totalrings/1000000), (gamedata->totalrings/1000)%1000, (gamedata->totalrings%1000)); + } + else if (gamedata->totalrings >= 1000) + { + sprintf(beststr, "%u,%u", (gamedata->totalrings/1000), (gamedata->totalrings%1000)); + } + else + { + sprintf(beststr, "%u", gamedata->totalrings); + } + V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 32, V_6WIDTHSPACE, va("%s collected", beststr)); + beststr[0] = 0; + V_DrawThinString(20, 42, V_6WIDTHSPACE|V_ALLOWLOWERCASE|highlightflags, "Total Matches:"); V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 42, V_6WIDTHSPACE, va("%i played", gamedata->matchesplayed)); @@ -5498,6 +5534,8 @@ void M_DrawStatistics(void) return; } + besttime = 0; + for (i = 0; i < nummapheaders; i++) { if (!mapheaderinfo[i] || (mapheaderinfo[i]->menuflags & (LF2_NOTIMEATTACK|LF2_HIDEINSTATS|LF2_HIDEINMENU))) diff --git a/src/m_cond.c b/src/m_cond.c index 55dbe98a6..f043d8454 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -624,6 +624,8 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) return (gamedata->totalplaytime >= (unsigned)cn->requirement); case UC_MATCHESPLAYED: // Requires any level completed >= x times return (gamedata->matchesplayed >= (unsigned)cn->requirement); + case UC_TOTALRINGS: // Requires grabbing >= x rings + return (gamedata->totalrings >= (unsigned)cn->requirement); case UC_POWERLEVEL: // Requires power level >= x on a certain gametype { UINT8 i; @@ -832,7 +834,13 @@ static const char *M_GetConditionString(condition_t *cn) G_TicsToMinutes(cn->requirement, false), G_TicsToSeconds(cn->requirement)); case UC_MATCHESPLAYED: // Requires any level completed >= x times - return va("Play %d matches", cn->requirement); + return va("Play %d Rounds", cn->requirement); + case UC_TOTALRINGS: // Requires collecting >= x rings + if (cn->requirement >= 1000000) + return va("Collect %u,%u,%u Rings", (cn->requirement/1000000), (cn->requirement/1000)%1000, (cn->requirement%1000)); + if (cn->requirement >= 1000) + return va("Collect %u,%u Rings", (cn->requirement/1000), (cn->requirement%1000)); + return va("Collect %u Rings", cn->requirement); case UC_POWERLEVEL: // Requires power level >= x on a certain gametype return va("Get a PWR of %d in %s", cn->requirement, (cn->extrainfo1 == PWRLV_RACE) diff --git a/src/m_cond.h b/src/m_cond.h index 59162f592..f19f9c93b 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -30,6 +30,7 @@ typedef enum { UC_PLAYTIME, // PLAYTIME [tics] UC_MATCHESPLAYED, // SRB2Kart: MATCHESPLAYED [x played] + UC_TOTALRINGS, // TOTALRINGS [x collected] UC_POWERLEVEL, // SRB2Kart: POWERLEVEL [power level to reach] [gametype, "0" for race, "1" for battle] UC_GAMECLEAR, // GAMECLEAR UC_OVERALLTIME, // OVERALLTIME [time to beat, tics] @@ -187,6 +188,9 @@ typedef enum #define GDCRASH_ANY 0x02 #define GDCRASH_LOSERCLUB 0x04 +// This is the largest number of 9s that will fit in UINT32. +#define GDMAX_RINGS 999999999 + // GAMEDATA STRUCTURE // Everything that would get saved in gamedata.dat struct gamedata_t @@ -215,6 +219,7 @@ struct gamedata_t // PLAY TIME UINT32 totalplaytime; UINT32 matchesplayed; + UINT32 totalrings; // Funny UINT8 crashflags; diff --git a/src/menus/options-data-erase-1.c b/src/menus/options-data-erase-1.c index f40e106c6..d301f29f4 100644 --- a/src/menus/options-data-erase-1.c +++ b/src/menus/options-data-erase-1.c @@ -53,11 +53,13 @@ static void M_EraseDataResponse(INT32 ch) S_StartSound(NULL, sfx_itrole); // bweh heh heh // Delete the data + // see also G_LoadGameData if (optionsmenu.erasecontext == 2) { // SRB2Kart: This actually needs to be done FIRST, so that you don't immediately regain playtime/matches secrets gamedata->totalplaytime = 0; gamedata->matchesplayed = 0; + gamedata->totalrings = 0; } if (optionsmenu.erasecontext != 1) G_ClearRecords(); diff --git a/src/p_user.c b/src/p_user.c index b3c605159..ba3481130 100644 --- a/src/p_user.c +++ b/src/p_user.c @@ -505,6 +505,11 @@ INT32 P_GivePlayerRings(player_t *player, INT32 num_rings) if ((gametyperules & GTR_SPHERES)) // No rings in Battle Mode return 0; + if (gamedata && num_rings > 0 && P_IsLocalPlayer(player) && gamedata->totalrings <= GDMAX_RINGS) + { + gamedata->totalrings += num_rings; + } + test = player->rings + num_rings; if (test > 20) // Caps at 20 rings, sorry! num_rings -= (test-20);