mirror of
https://github.com/KartKrewDev/RingRacers.git
synced 2025-10-30 08:01:28 +00:00
gamedata->roundsplayed: Split into multiple roughly-gametype-aligned categories
- Race, Capsule, Battle, Special, Custom
- All categories can now be used for Rounds Played condition
- UCRP_SEALEDSTAR now hides if you haven't beaten a single Sealed Star yet
In addition, introduce M_ClearStats
- As more statistics get added, clearing them manually in G_LoadGameData and M_EraseDataResponse is just going to get annoying
- Change around the options on the Erase Data screen to
- Make it clear that erasing all game data won't clear your Profiles
- Add an option to clear stats by themselves, rather than only permitting via complete gamedata wipe
- Move to the "Challenges" terminology over SRB2's "Secrets" terminology
- Move some entries that were previously handled in M_ClearSecrets into M_ClearStats
This commit is contained in:
parent
22f9467e71
commit
e994b920c6
6 changed files with 197 additions and 47 deletions
|
|
@ -2363,13 +2363,44 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
|
|||
return;
|
||||
}
|
||||
|
||||
if ((offset=0) || fastcmp(params[0], "PLAYTIME")
|
||||
|| (++offset && fastcmp(params[0], "MATCHESPLAYED")))
|
||||
if (fastcmp(params[0], "PLAYTIME"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_PLAYTIME + offset;
|
||||
re = atoi(params[1]);
|
||||
}
|
||||
else if (fastcmp(params[0], "ROUNDSPLAYED"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
ty = UC_ROUNDSPLAYED;
|
||||
re = atoi(params[1]);
|
||||
x1 = GDGT_MAX;
|
||||
|
||||
if (re == 0)
|
||||
{
|
||||
deh_warning("Rounds played requirement is %d for condition ID %d", re, id+1);
|
||||
return;
|
||||
}
|
||||
|
||||
if (params[2])
|
||||
{
|
||||
if (fastcmp(params[2], "RACE"))
|
||||
x1 = GDGT_RACE;
|
||||
else if (fastcmp(params[2], "BATTLE"))
|
||||
x1 = GDGT_BATTLE;
|
||||
else if (fastcmp(params[2], "CAPSULE"))
|
||||
x1 = GDGT_CAPSULES;
|
||||
else if (fastcmp(params[2], "SPECIAL"))
|
||||
x1 = GDGT_SPECIAL;
|
||||
else if (fastcmp(params[2], "CUSTOM"))
|
||||
x1 = GDGT_CUSTOM;
|
||||
else
|
||||
{
|
||||
deh_warning("gametype requirement \"%s\" invalid for condition ID %d", params[2], id+1);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (fastcmp(params[0], "TOTALRINGS"))
|
||||
{
|
||||
PARAMCHECK(1);
|
||||
|
|
|
|||
35
src/g_game.c
35
src/g_game.c
|
|
@ -3994,8 +3994,18 @@ static void G_DoCompleted(void)
|
|||
|
||||
if (legitimateexit && !demo.playback && !mapreset) // (yes you're allowed to unlock stuff this way when the game is modified)
|
||||
{
|
||||
UINT8 roundtype = GDGT_CUSTOM;
|
||||
|
||||
if (gametype == GT_RACE)
|
||||
roundtype = GDGT_RACE;
|
||||
else if (gametype == GT_BATTLE)
|
||||
roundtype = (battlecapsules ? GDGT_CAPSULES : GDGT_BATTLE);
|
||||
else if (gametype == GT_SPECIAL || gametype == GT_VERSUS)
|
||||
roundtype = GDGT_SPECIAL;
|
||||
|
||||
gamedata->roundsplayed[roundtype]++;
|
||||
|
||||
// Done before forced addition of PF_NOCONTEST to make UCRP_NOCONTEST harder to achieve
|
||||
gamedata->matchesplayed++;
|
||||
M_UpdateUnlockablesAndExtraEmblems(true);
|
||||
gamedata->deferredsave = true;
|
||||
}
|
||||
|
|
@ -4348,12 +4358,9 @@ void G_LoadGameData(void)
|
|||
// to new gamedata
|
||||
// see also M_EraseDataResponse
|
||||
G_ClearRecords(); // records
|
||||
M_ClearStats(); // statistics
|
||||
M_ClearSecrets(); // emblems, unlocks, maps visited, etc
|
||||
|
||||
gamedata->totalplaytime = 0;
|
||||
gamedata->matchesplayed = 0;
|
||||
gamedata->totalrings = 0;
|
||||
|
||||
if (M_CheckParm("-nodata"))
|
||||
{
|
||||
// Don't load at all.
|
||||
|
|
@ -4398,16 +4405,24 @@ void G_LoadGameData(void)
|
|||
}
|
||||
|
||||
gamedata->totalplaytime = READUINT32(save.p);
|
||||
gamedata->matchesplayed = READUINT32(save.p);
|
||||
|
||||
if (versionMinor > 1)
|
||||
{
|
||||
gamedata->totalrings = READUINT32(save.p);
|
||||
|
||||
for (i = 0; i < GDGT_MAX; i++)
|
||||
{
|
||||
gamedata->roundsplayed[i] = READUINT32(save.p);
|
||||
}
|
||||
|
||||
gamedata->crashflags = READUINT8(save.p);
|
||||
if (gamedata->crashflags & GDCRASH_LAST)
|
||||
gamedata->crashflags |= GDCRASH_ANY;
|
||||
}
|
||||
else
|
||||
{
|
||||
save.p += 4; // no direct equivalent to matchesplayed
|
||||
}
|
||||
|
||||
{
|
||||
// Quick & dirty hash for what mod this save file is for.
|
||||
|
|
@ -4574,7 +4589,7 @@ void G_SaveGameData(boolean dirty)
|
|||
return;
|
||||
}
|
||||
|
||||
length = (4+1+4+4+4+1+4+(MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+4+4+2);
|
||||
length = (4+1+4+4+(4*GDGT_MAX)+1+4+(MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+4+4+2);
|
||||
if (gamedata->challengegrid)
|
||||
{
|
||||
length += gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT;
|
||||
|
|
@ -4592,9 +4607,13 @@ void G_SaveGameData(boolean dirty)
|
|||
WRITEUINT32(save.p, GD_VERSIONCHECK); // 4
|
||||
WRITEUINT8(save.p, GD_VERSIONMINOR); // 1
|
||||
WRITEUINT32(save.p, gamedata->totalplaytime); // 4
|
||||
WRITEUINT32(save.p, gamedata->matchesplayed); // 4
|
||||
WRITEUINT32(save.p, gamedata->totalrings); // 4
|
||||
|
||||
for (i = 0; i < GDGT_MAX; i++) // 4 * GDGT_MAX
|
||||
{
|
||||
WRITEUINT32(save.p, gamedata->roundsplayed[i]);
|
||||
}
|
||||
|
||||
{
|
||||
UINT8 crashflags = (gamedata->crashflags & GDCRASH_ANY);
|
||||
if (dirty)
|
||||
|
|
|
|||
|
|
@ -5523,10 +5523,30 @@ void M_DrawStatistics(void)
|
|||
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));
|
||||
beststr[0] = 0;
|
||||
V_DrawThinString(20, 42, V_6WIDTHSPACE|V_ALLOWLOWERCASE|highlightflags, "Total Rounds:");
|
||||
|
||||
strcat(beststr, va("%u Race", gamedata->roundsplayed[GDGT_RACE]));
|
||||
|
||||
if (gamedata->roundsplayed[GDGT_CAPSULES] > 0)
|
||||
{
|
||||
strcat(beststr, va(", %u Capsule", gamedata->roundsplayed[GDGT_CAPSULES]));
|
||||
}
|
||||
|
||||
strcat(beststr, va(", %u Battle", gamedata->roundsplayed[GDGT_BATTLE]));
|
||||
|
||||
if (gamedata->roundsplayed[GDGT_SPECIAL] > 0)
|
||||
{
|
||||
strcat(beststr, va(", %u Special", gamedata->roundsplayed[GDGT_SPECIAL]));
|
||||
}
|
||||
|
||||
if (gamedata->roundsplayed[GDGT_CUSTOM] > 0)
|
||||
{
|
||||
strcat(beststr, va(", %u Custom", gamedata->roundsplayed[GDGT_CUSTOM]));
|
||||
}
|
||||
|
||||
V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 42, V_6WIDTHSPACE, beststr);
|
||||
|
||||
if (!statisticsmenu.maplist)
|
||||
{
|
||||
|
|
|
|||
80
src/m_cond.c
80
src/m_cond.c
|
|
@ -524,6 +524,17 @@ void M_ClearConditionSet(UINT8 set)
|
|||
}
|
||||
|
||||
// Clear ALL secrets.
|
||||
void M_ClearStats(void)
|
||||
{
|
||||
UINT8 i;
|
||||
gamedata->totalplaytime = 0;
|
||||
gamedata->totalrings = 0;
|
||||
for (i = 0; i < GDGT_MAX; ++i)
|
||||
gamedata->roundsplayed[i] = 0;
|
||||
gamedata->timesBeaten = 0;
|
||||
gamedata->crashflags = 0;
|
||||
}
|
||||
|
||||
void M_ClearSecrets(void)
|
||||
{
|
||||
INT32 i;
|
||||
|
|
@ -544,9 +555,6 @@ void M_ClearSecrets(void)
|
|||
gamedata->challengegrid = NULL;
|
||||
gamedata->challengegridwidth = 0;
|
||||
|
||||
gamedata->timesBeaten = 0;
|
||||
gamedata->crashflags = 0;
|
||||
|
||||
// Re-unlock any always unlocked things
|
||||
M_UpdateUnlockablesAndExtraEmblems(false);
|
||||
}
|
||||
|
|
@ -627,8 +635,22 @@ boolean M_CheckCondition(condition_t *cn, player_t *player)
|
|||
{
|
||||
case UC_PLAYTIME: // Requires total playing time >= x
|
||||
return (gamedata->totalplaytime >= (unsigned)cn->requirement);
|
||||
case UC_MATCHESPLAYED: // Requires any level completed >= x times
|
||||
return (gamedata->matchesplayed >= (unsigned)cn->requirement);
|
||||
case UC_ROUNDSPLAYED: // Requires any level completed >= x times
|
||||
{
|
||||
if (cn->extrainfo1 == GDGT_MAX)
|
||||
{
|
||||
UINT8 i;
|
||||
UINT32 sum = 0;
|
||||
|
||||
for (i = 0; i < GDGT_MAX; i++)
|
||||
{
|
||||
sum += gamedata->roundsplayed[i];
|
||||
}
|
||||
|
||||
return (sum >= (unsigned)cn->requirement);
|
||||
}
|
||||
return (gamedata->roundsplayed[cn->extrainfo1] >= (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
|
||||
|
|
@ -834,33 +856,70 @@ static const char *M_GetConditionString(condition_t *cn)
|
|||
switch (cn->type)
|
||||
{
|
||||
case UC_PLAYTIME: // Requires total playing time >= x
|
||||
|
||||
return va("Play for %i:%02i:%02i",
|
||||
G_TicsToHours(cn->requirement),
|
||||
G_TicsToMinutes(cn->requirement, false),
|
||||
G_TicsToSeconds(cn->requirement));
|
||||
case UC_MATCHESPLAYED: // Requires any level completed >= x times
|
||||
return va("Play %d Rounds", cn->requirement);
|
||||
|
||||
case UC_ROUNDSPLAYED: // Requires any level completed >= x times
|
||||
|
||||
if (cn->extrainfo1 == GDGT_MAX)
|
||||
work = "";
|
||||
else if (cn->extrainfo1 != GDGT_RACE
|
||||
&& cn->extrainfo1 != GDGT_BATTLE
|
||||
&& cn->extrainfo1 != GDGT_CUSTOM
|
||||
&& gamedata->roundsplayed[cn->extrainfo1] == 0)
|
||||
work = " ???";
|
||||
else switch (cn->extrainfo1)
|
||||
{
|
||||
case GDGT_RACE:
|
||||
work = " Race";
|
||||
break;
|
||||
case GDGT_CAPSULES:
|
||||
work = " Capsule";
|
||||
break;
|
||||
case GDGT_BATTLE:
|
||||
work = " Battle";
|
||||
break;
|
||||
case GDGT_SPECIAL:
|
||||
work = " Special";
|
||||
break;
|
||||
case GDGT_CUSTOM:
|
||||
work = " custom gametype";
|
||||
break;
|
||||
default:
|
||||
return va("INVALID GAMETYPE CONDITION \"%d:%d:%d\"", cn->type, cn->extrainfo1, cn->requirement);
|
||||
}
|
||||
|
||||
return va("Play %d%s Round%s", cn->requirement, work,
|
||||
(cn->requirement == 1 ? "" : "s"));
|
||||
|
||||
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)
|
||||
? "Race"
|
||||
: "Battle");
|
||||
|
||||
case UC_GAMECLEAR: // Requires game beaten >= x times
|
||||
if (cn->requirement > 1)
|
||||
return va("Beat game %d times", cn->requirement);
|
||||
else
|
||||
return va("Beat the game");
|
||||
|
||||
case UC_OVERALLTIME: // Requires overall time <= x
|
||||
return va("Get overall time of %i:%02i:%02i",
|
||||
G_TicsToHours(cn->requirement),
|
||||
G_TicsToMinutes(cn->requirement, false),
|
||||
G_TicsToSeconds(cn->requirement));
|
||||
|
||||
case UC_MAPVISITED: // Requires map x to be visited
|
||||
case UC_MAPBEATEN: // Requires map x to be beaten
|
||||
case UC_MAPENCORE: // Requires map x to be beaten in encore
|
||||
|
|
@ -876,6 +935,7 @@ static const char *M_GetConditionString(condition_t *cn)
|
|||
Z_Free(title);
|
||||
return work;
|
||||
}
|
||||
|
||||
case UC_MAPTIME: // Requires time on map <= x
|
||||
{
|
||||
if (cn->extrainfo1 >= nummapheaders || !mapheaderinfo[cn->extrainfo1])
|
||||
|
|
@ -890,8 +950,10 @@ static const char *M_GetConditionString(condition_t *cn)
|
|||
Z_Free(title);
|
||||
return work;
|
||||
}
|
||||
|
||||
case UC_TOTALMEDALS: // Requires number of emblems >= x
|
||||
return va("Get %d medals", cn->requirement);
|
||||
|
||||
case UC_EMBLEM: // Requires emblem x to be obtained
|
||||
{
|
||||
INT32 checkLevel;
|
||||
|
|
@ -987,7 +1049,9 @@ static const char *M_GetConditionString(condition_t *cn)
|
|||
case UCRP_PREFIX_BREAKTHECAPSULES:
|
||||
return "BREAK THE CAPSULES:";
|
||||
case UCRP_PREFIX_SEALEDSTAR:
|
||||
return "SEALED STAR:";
|
||||
if (gamedata->roundsplayed[GDGT_SPECIAL] == 0)
|
||||
return NULL;
|
||||
return "SEALED STARS:";
|
||||
|
||||
case UCRP_PREFIX_ISMAP:
|
||||
if (cn->requirement >= nummapheaders || !mapheaderinfo[cn->requirement])
|
||||
|
|
|
|||
14
src/m_cond.h
14
src/m_cond.h
|
|
@ -29,7 +29,7 @@ extern "C" {
|
|||
typedef enum
|
||||
{
|
||||
UC_PLAYTIME, // PLAYTIME [tics]
|
||||
UC_MATCHESPLAYED, // SRB2Kart: MATCHESPLAYED [x played]
|
||||
UC_ROUNDSPLAYED, // ROUNDSPLAYED [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 <x times>
|
||||
|
|
@ -191,6 +191,15 @@ typedef enum
|
|||
// This is the largest number of 9s that will fit in UINT32.
|
||||
#define GDMAX_RINGS 999999999
|
||||
|
||||
typedef enum {
|
||||
GDGT_RACE,
|
||||
GDGT_BATTLE,
|
||||
GDGT_CAPSULES,
|
||||
GDGT_SPECIAL,
|
||||
GDGT_CUSTOM,
|
||||
GDGT_MAX
|
||||
} roundsplayed_t;
|
||||
|
||||
// GAMEDATA STRUCTURE
|
||||
// Everything that would get saved in gamedata.dat
|
||||
struct gamedata_t
|
||||
|
|
@ -218,7 +227,7 @@ struct gamedata_t
|
|||
|
||||
// PLAY TIME
|
||||
UINT32 totalplaytime;
|
||||
UINT32 matchesplayed;
|
||||
UINT32 roundsplayed[GDGT_MAX];
|
||||
UINT32 totalrings;
|
||||
|
||||
// Funny
|
||||
|
|
@ -267,6 +276,7 @@ void M_UpdateConditionSetsPending(void);
|
|||
// Clearing secrets
|
||||
void M_ClearConditionSet(UINT8 set);
|
||||
void M_ClearSecrets(void);
|
||||
void M_ClearStats(void);
|
||||
|
||||
// Updating conditions and unlockables
|
||||
boolean M_CheckCondition(condition_t *cn, player_t *player);
|
||||
|
|
|
|||
|
|
@ -6,27 +6,31 @@
|
|||
#include "../m_cond.h" // Condition Sets
|
||||
#include "../f_finale.h"
|
||||
|
||||
#define EC_CHALLENGES 0x01
|
||||
#define EC_STATISTICS 0x02
|
||||
#define EC_TIMEATTACK 0x04
|
||||
#define EC_ALLGAME (EC_CHALLENGES|EC_STATISTICS|EC_TIMEATTACK)
|
||||
|
||||
menuitem_t OPTIONS_DataErase[] =
|
||||
{
|
||||
{IT_STRING | IT_CALL, "Erase Challenges Data", "Be careful! What's deleted is gone forever!",
|
||||
NULL, {.routine = M_EraseData}, EC_CHALLENGES, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "Erase Statistics Data", "Be careful! What's deleted is gone forever!",
|
||||
NULL, {.routine = M_EraseData}, EC_STATISTICS, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "Erase Time Attack Data", "Be careful! What's deleted is gone forever!",
|
||||
NULL, {.routine = M_EraseData}, 0, 0},
|
||||
NULL, {.routine = M_EraseData}, EC_TIMEATTACK, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "Erase Unlockable Data", "Be careful! What's deleted is gone forever!",
|
||||
NULL, {.routine = M_EraseData}, 0, 0},
|
||||
{IT_STRING | IT_CALL, "\x85\x45rase all Game Data", "Be careful! What's deleted is gone forever!",
|
||||
NULL, {.routine = M_EraseData}, EC_ALLGAME, 0},
|
||||
|
||||
{IT_SPACE | IT_NOTHING, NULL, NULL,
|
||||
NULL, {NULL}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "Erase Profile Data...", "Select a Profile to erase.",
|
||||
{IT_STRING | IT_CALL, "Erase a Profile...", "Select a Profile to erase.",
|
||||
NULL, {.routine = M_CheckProfileData}, 0, 0},
|
||||
|
||||
{IT_SPACE | IT_NOTHING, NULL, NULL,
|
||||
NULL, {NULL}, 0, 0},
|
||||
|
||||
{IT_STRING | IT_CALL, "\x85\x45rase all Data", "Be careful! What's deleted is gone forever!",
|
||||
NULL, {.routine = M_EraseData}, 0, 0},
|
||||
|
||||
};
|
||||
|
||||
menu_t OPTIONS_DataEraseDef = {
|
||||
|
|
@ -54,16 +58,12 @@ static void M_EraseDataResponse(INT32 ch)
|
|||
|
||||
// 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)
|
||||
// We do these in backwards order to prevent things from being immediately re-unlocked.
|
||||
if (optionsmenu.erasecontext & EC_TIMEATTACK)
|
||||
G_ClearRecords();
|
||||
if (optionsmenu.erasecontext != 0)
|
||||
if (optionsmenu.erasecontext & EC_STATISTICS)
|
||||
M_ClearStats();
|
||||
if (optionsmenu.erasecontext & EC_CHALLENGES)
|
||||
M_ClearSecrets();
|
||||
|
||||
F_StartIntro();
|
||||
|
|
@ -73,15 +73,21 @@ static void M_EraseDataResponse(INT32 ch)
|
|||
void M_EraseData(INT32 choice)
|
||||
{
|
||||
const char *eschoice, *esstr = M_GetText("Are you sure you want to erase\n%s?\n\nPress (A) to confirm or (B) to cancel\n");
|
||||
(void)choice;
|
||||
|
||||
optionsmenu.erasecontext = (UINT8)choice;
|
||||
optionsmenu.erasecontext = (UINT8)currentMenu->menuitems[itemOn].mvar1;
|
||||
|
||||
if (choice == 0)
|
||||
if (optionsmenu.erasecontext == EC_CHALLENGES)
|
||||
eschoice = M_GetText("Challenges data");
|
||||
else if (optionsmenu.erasecontext == EC_STATISTICS)
|
||||
eschoice = M_GetText("Statistics data");
|
||||
else if (optionsmenu.erasecontext == EC_TIMEATTACK)
|
||||
eschoice = M_GetText("Time Attack data");
|
||||
else if (choice == 1)
|
||||
eschoice = M_GetText("Secrets data");
|
||||
else
|
||||
else if (optionsmenu.erasecontext == EC_ALLGAME)
|
||||
eschoice = M_GetText("ALL game data");
|
||||
else
|
||||
eschoice = va("[misconfigured erasecontext %d]", optionsmenu.erasecontext);
|
||||
|
||||
|
||||
M_StartMessage(va(esstr, eschoice), FUNCPTRCAST(M_EraseDataResponse), MM_YESNO);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue