cupwindata_t

Save your best GP stats across sessions.
- Saved into gamedata
- Deliniated per difficulty option
- Draw onto cupgrid in GP cup select
    - Best grade
    - Whether you've ever gotten the Emerald
        - TODO: Always shows Chaos Emerald, will need updating when Super Emerald graphics are created
    - Monitor status changes depending on recorded position
        - 1st: Gold, shiny
        - 2nd: Silver, shiny
        - 3rd: Bronze, shiny
        - 4th and downwards: Beige, barely different
- Wiped with G_ClearRecords

Also, to avoid circular dependencies:
- KARTSPEED/KARTGP constants moved from command.h to doomstat.h
- gp_rank_e enums moved from k_rank.h to doomstat.h
This commit is contained in:
toaster 2023-03-10 20:20:48 +00:00
parent f3cde6140a
commit dfe75726df
7 changed files with 226 additions and 36 deletions

View file

@ -174,11 +174,7 @@ extern CV_PossibleValue_t CV_Unsigned[];
extern CV_PossibleValue_t CV_Natural[];
// SRB2kart
#define KARTSPEED_AUTO -1
#define KARTSPEED_EASY 0
#define KARTSPEED_NORMAL 1
#define KARTSPEED_HARD 2
#define KARTGP_MASTER 3 // Not a speed setting, gives the hardest speed with maxed out bots
// the KARTSPEED and KARTGP were previously defined here, but moved to doomstat to avoid circular dependencies
extern CV_PossibleValue_t kartspeed_cons_t[], dummykartspeed_cons_t[], gpdifficulty_cons_t[];
extern consvar_t cv_execversion;

View file

@ -120,6 +120,30 @@ struct recorddata_t
//UINT16 rings; ///< Rings when the level was finished.
};
#define KARTSPEED_AUTO -1
#define KARTSPEED_EASY 0
#define KARTSPEED_NORMAL 1
#define KARTSPEED_HARD 2
#define KARTGP_MASTER 3 // Not a speed setting, gives the hardest speed with maxed out bots
#define KARTGP_MAX 4
typedef enum
{
GRADE_E,
GRADE_D,
GRADE_C,
GRADE_B,
GRADE_A,
GRADE_S
} gp_rank_e;
struct cupwindata_t
{
UINT8 best_placement;
gp_rank_e best_grade;
boolean got_emerald;
};
// mapvisited is now a set of flags that says what we've done in the map.
#define MV_VISITED (1)
#define MV_BEATEN (1<<1)
@ -358,6 +382,7 @@ struct cupheader_t
UINT8 numlevels; ///< Number of levels defined in levellist
UINT8 numbonus; ///< Number of bonus stages defined
UINT8 emeraldnum; ///< ID of Emerald to use for special stage (1-7 for Chaos Emeralds, 8-14 for Super Emeralds, 0 for no emerald)
cupwindata_t windata[4]; ///< Data for cup visitation
cupheader_t *next; ///< Next cup in linked list
};

View file

@ -463,6 +463,8 @@ void G_AllocMainRecordData(INT16 i)
void G_ClearRecords(void)
{
INT16 i;
cupheader_t *cup;
for (i = 0; i < nummapheaders; ++i)
{
if (mapheaderinfo[i]->mainrecord)
@ -471,6 +473,11 @@ void G_ClearRecords(void)
mapheaderinfo[i]->mainrecord = NULL;
}
}
for (cup = kartcupheaders; cup; cup = cup->next)
{
memset(&cup->windata, 0, sizeof(cup->windata));
}
}
// For easy retrieval of records
@ -4498,6 +4505,7 @@ void G_LoadGameData(void)
//For records
UINT32 numgamedatamapheaders;
UINT32 numgamedatacups;
// Stop saving, until we successfully load it again.
gamedata->loaded = false;
@ -4696,11 +4704,46 @@ void G_LoadGameData(void)
}
}
if (versionMinor > 1)
{
numgamedatacups = READUINT32(save.p);
for (i = 0; i < numgamedatacups; i++)
{
char cupname[16];
cupheader_t *cup;
READSTRINGN(save.p, cupname, sizeof(cupname));
for (cup = kartcupheaders; cup; cup = cup->next)
{
if (strcmp(cup->name, cupname))
continue;
for (j = 0; j < KARTGP_MAX; j++)
{
rtemp = READUINT8(save.p);
cup->windata[j].best_placement = (rtemp & 0x0F);
cup->windata[j].best_grade = (rtemp & 0x70)>>4;
if (rtemp & 0x80)
{
if (j == 0)
goto datacorrupt;
cup->windata[j].got_emerald = true;
}
}
break;
}
}
}
// done
P_SaveBufferFree(&save);
finalisegamedata:
{
// Don't consider loaded until it's a success!
// It used to do this much earlier, but this would cause the gamedata to
// save over itself when it I_Errors from the corruption landing point below,
@ -4711,6 +4754,7 @@ finalisegamedata:
M_UpdateUnlockablesAndExtraEmblems(false);
return;
}
// Landing point for corrupt gamedata
datacorrupt:
@ -4730,7 +4774,8 @@ finalisegamedata:
void G_SaveGameData(boolean dirty)
{
size_t length;
INT32 i, j;
INT32 i, j, numcups;
cupheader_t *cup;
UINT8 btemp;
savebuffer_t save = {0};
@ -4752,12 +4797,20 @@ void G_SaveGameData(boolean dirty)
4+1+1+1+1+
1+1+4+
(MAXEMBLEMS+(MAXUNLOCKABLES*2)+MAXCONDITIONSETS)+
4+4+2);
4+2);
if (gamedata->challengegrid)
{
length += gamedata->challengegridwidth * CHALLENGEGRIDHEIGHT;
}
length += nummapheaders * (MAXMAPLUMPNAME+1+4+4);
length += 4 + (nummapheaders * (MAXMAPLUMPNAME+1+4+4));
numcups = 0;
for (cup = kartcupheaders; cup; cup = cup->next)
{
numcups++;
}
length += 4 + (numcups * 4);
if (P_SaveBufferAlloc(&save, length) == false)
{
@ -4851,7 +4904,7 @@ void G_SaveGameData(boolean dirty)
for (i = 0; i < nummapheaders; i++) // nummapheaders * (255+1+4+4)
{
// For figuring out which header to assing it to on load
// For figuring out which header to assign it to on load
WRITESTRINGN(save.p, mapheaderinfo[i]->lumpname, MAXMAPLUMPNAME);
WRITEUINT8(save.p, (mapheaderinfo[i]->mapvisited & MV_MAX));
@ -4868,6 +4921,24 @@ void G_SaveGameData(boolean dirty)
}
}
WRITEUINT32(save.p, numcups); // 4
for (cup = kartcupheaders; cup; cup = cup->next)
{
// For figuring out which header to assign it to on load
WRITESTRINGN(save.p, cup->name, 16);
for (i = 0; i < KARTGP_MAX; i++)
{
btemp = min(cup->windata[i].best_placement, 0x0F);
btemp |= (cup->windata[i].best_grade<<4);
if (i != 0 && cup->windata[i].got_emerald == true)
btemp |= 0x80;
WRITEUINT8(save.p, btemp); // 4 * numcups
}
}
length = save.p - save.buffer;
FIL_WriteFile(va(pandf, srb2home, gamedatafilename), save.buffer, length);

View file

@ -2113,6 +2113,8 @@ static void M_DrawCupTitle(INT16 y, levelsearch_t *levelsearch)
void M_DrawCupSelect(void)
{
UINT8 i, j, temp = 0;
UINT8 *colormap = NULL;
cupwindata_t *windata = NULL;
levelsearch_t templevelsearch = levellist.levelsearch; // full copy
for (i = 0; i < CUPMENU_COLUMNS; i++)
@ -2123,26 +2125,62 @@ void M_DrawCupSelect(void)
patch_t *patch = NULL;
INT16 x, y;
INT16 icony = 7;
char status = 'A';
UINT8 monitor = 1;
INT32 rankx = 0;
if (!cupgrid.builtgrid[id])
break;
templevelsearch.cup = cupgrid.builtgrid[id];
/*if (templevelsearch.cup->emeraldnum == 0)
patch = W_CachePatchName("CUPMON3A", PU_CACHE);
else*/ if (templevelsearch.cup->emeraldnum > 7)
if (cupgrid.grandprix
&& (cv_dummygpdifficulty.value >= 0 && cv_dummygpdifficulty.value < KARTGP_MAX))
{
patch = W_CachePatchName("CUPMON2A", PU_CACHE);
icony = 5;
UINT16 col = SKINCOLOR_NONE;
windata = &templevelsearch.cup->windata[cv_dummygpdifficulty.value];
switch (windata->best_placement)
{
case 0:
break;
case 1:
col = SKINCOLOR_GOLD;
status = 'B';
break;
case 2:
col = SKINCOLOR_SILVER;
status = 'B';
break;
case 3:
col = SKINCOLOR_BRONZE;
status = 'B';
break;
default:
col = SKINCOLOR_BEIGE;
break;
}
if (col != SKINCOLOR_NONE)
colormap = R_GetTranslationColormap(TC_RAINBOW, col, GTC_MENUCACHE);
else
patch = W_CachePatchName("CUPMON1A", PU_CACHE);
colormap = NULL;
}
if (templevelsearch.cup->emeraldnum > 7)
{
monitor = 2;
icony = 5;
rankx = 2;
}
patch = W_CachePatchName(va("CUPMON%d%c", monitor, status), PU_CACHE);
x = 14 + (i*42);
y = 20 + (j*44) - (30*menutransition.tics);
V_DrawScaledPatch(x, y, 0, patch);
V_DrawFixedPatch((x)*FRACUNIT, (y)<<FRACBITS, FRACUNIT, 0, patch, colormap);
if (M_GetFirstLevelInList(&temp, &templevelsearch) == NEXTMAP_INVALID)
{
@ -2153,6 +2191,39 @@ void M_DrawCupSelect(void)
{
V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName(templevelsearch.cup->icon, PU_CACHE));
V_DrawScaledPatch(x + 8, y + icony, 0, W_CachePatchName("CUPBOX", PU_CACHE));
if (!windata)
;
else if (windata->best_placement != 0)
{
char gradeChar = '?';
switch (windata->best_grade)
{
case GRADE_E: { gradeChar = 'E'; break; }
case GRADE_D: { gradeChar = 'D'; break; }
case GRADE_C: { gradeChar = 'C'; break; }
case GRADE_B: { gradeChar = 'B'; break; }
case GRADE_A: { gradeChar = 'A'; break; }
case GRADE_S: { gradeChar = 'S'; break; }
default: { break; }
}
V_DrawCharacter(x + 5 + rankx, y + icony + 14, gradeChar, false); // rank
if (windata->got_emerald == true)
{
if (templevelsearch.cup->emeraldnum == 0)
V_DrawCharacter(x + 26 - rankx, y + icony + 14, '*', false); // rank
else
{
UINT16 col = SKINCOLOR_CHAOSEMERALD1 + (templevelsearch.cup->emeraldnum-1) % 7;
colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE);
V_DrawFixedPatch((x + 26 - rankx)*FRACUNIT, (y + icony + 13)*FRACUNIT, FRACUNIT, 0, W_CachePatchName("K_EMERC", PU_CACHE), colormap);
}
}
}
}
}
}

View file

@ -264,6 +264,10 @@ void K_FinishCeremony(void)
}
podiumData.ranking = true;
// Play the noise now
M_UpdateUnlockablesAndExtraEmblems(true);
G_SaveGameData(true);
}
/*--------------------------------------------------
@ -273,6 +277,8 @@ void K_FinishCeremony(void)
--------------------------------------------------*/
void K_ResetCeremony(void)
{
UINT8 i;
memset(&podiumData, 0, sizeof(struct podiumData_s));
if (K_PodiumSequence() == false)
@ -280,8 +286,36 @@ void K_ResetCeremony(void)
return;
}
// Establish rank and grade for this play session.
podiumData.rank = grandprixinfo.rank;
podiumData.grade = K_CalculateGPGrade(&podiumData.rank);
if (!grandprixinfo.cup)
{
return;
}
// Write grade, position, and emerald-having-ness for later sessions!
i = (grandprixinfo.masterbots) ? KARTGP_MASTER : grandprixinfo.gamespeed;
if ((grandprixinfo.cup->windata[i].best_placement == 0) // First run
|| (podiumData.rank.position < grandprixinfo.cup->windata[i].best_placement)) // Later, better run
{
grandprixinfo.cup->windata[i].best_placement = podiumData.rank.position;
// The following will not occour in unmodified builds, but pre-emptively sanitise gamedata if someone just changes MAXPLAYERS and calls it a day
if (grandprixinfo.cup->windata[i].best_placement > 0x0F)
grandprixinfo.cup->windata[i].best_placement = 0x0F;
}
if (podiumData.grade > grandprixinfo.cup->windata[i].best_grade)
grandprixinfo.cup->windata[i].best_grade = podiumData.grade;
if (i != KARTSPEED_EASY && podiumData.rank.specialWon == true)
grandprixinfo.cup->windata[i].got_emerald = true;
// Save before playing the noise
G_SaveGameData(true);
}
/*--------------------------------------------------

View file

@ -44,15 +44,7 @@ struct gpRank_t
boolean specialWon;
};
typedef enum
{
GRADE_E,
GRADE_D,
GRADE_C,
GRADE_B,
GRADE_A,
GRADE_S
} gp_rank_e;
// gp_rank_e was once defined here, but moved to doomstat.h to prevent circular dependency
// 3rd place is neutral, anything below is a penalty
#define RANK_NEUTRAL_POSITION (3)

View file

@ -108,6 +108,7 @@ TYPEDEF (skincolor_t);
// doomstat.h
TYPEDEF (precipprops_t);
TYPEDEF (recorddata_t);
TYPEDEF (cupwindata_t);
TYPEDEF (scene_t);
TYPEDEF (cutscene_t);
TYPEDEF (textpage_t);