diff --git a/src/deh_soc.c b/src/deh_soc.c index fed01aa6d..c44b158d0 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -1041,10 +1041,10 @@ void readlevelheader(MYFILE *f, char * name) continue; } - if (fastcmp(word, "SUBTITLE")) + if (fastcmp(word, "MENUTITLE")) { - deh_strlcpy(mapheaderinfo[num]->subttl, word2, - sizeof(mapheaderinfo[num]->subttl), va("Level header %d: subtitle", num)); + deh_strlcpy(mapheaderinfo[num]->menuttl, word2, + sizeof(mapheaderinfo[num]->menuttl), va("Level header %d: menutitle", num)); continue; } @@ -3496,6 +3496,11 @@ void readcupheader(MYFILE *f, cupheader_t *cup) else cup->monitor = (word2[0] - 'A') + 10; } + else if (fastcmp(word, "REALNAME")) + { + deh_strlcpy(cup->realname, word2, + sizeof(cup->realname), va("%s Cup: realname", cup->name)); + } else if (fastcmp(word, "ICON")) { deh_strlcpy(cup->icon, word2, diff --git a/src/dehacked.c b/src/dehacked.c index f6ac70fe1..c63ab376b 100644 --- a/src/dehacked.c +++ b/src/dehacked.c @@ -495,6 +495,15 @@ static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile) sizeof(cup->name), va("Cup header %s: name", word2)); cup->namehash = hash; + char *start = strchr(word2, '_'); + if (start) + start++; + else + start = word2; + + deh_strlcpy(cup->realname, start, + sizeof(cup->realname), va("%s Cup: realname (default)", cup->name)); + // Check to see if we have any custom cup record data that we could substitute in. unloaded_cupheader_t *unloadedcup, *unloadedprev = NULL; for (unloadedcup = unloadedcupheaders; unloadedcup; unloadedprev = unloadedcup, unloadedcup = unloadedcup->next) diff --git a/src/doomstat.h b/src/doomstat.h index e6e1bc190..dcb29be47 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -396,6 +396,8 @@ struct cupheader_t char name[MAXCUPNAME]; ///< Cup title UINT32 namehash; ///< Cup title hash + char realname[MAXCUPNAME]; ///< Cup nomme de gurre + char icon[9]; ///< Name of the icon patch char *levellist[CUPCACHE_MAX]; ///< List of levels that belong to this cup INT16 cachedlevels[CUPCACHE_MAX]; ///< IDs in levellist, bonusgame, and specialstage @@ -464,7 +466,7 @@ struct mapheader_t // Titlecard information char lvlttl[22]; ///< Level name without "Zone". (21 character limit instead of 32, 21 characters can display on screen max anyway) - char subttl[33]; ///< Subtitle for level + char menuttl[22]; ///< Menu title for level char zonttl[22]; ///< "ZONE" replacement name UINT8 actnum; ///< Act number or 0 for none. diff --git a/src/g_game.c b/src/g_game.c index d4b916f4a..873ac0cf6 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4742,7 +4742,7 @@ void G_LoadGameSettings(void) } #define GD_VERSIONCHECK 0xBA5ED123 // Change every major version, as usual -#define GD_VERSIONMINOR 4 // Change every format update +#define GD_VERSIONMINOR 5 // Change every format update typedef enum { @@ -5024,8 +5024,6 @@ void G_LoadGameData(void) dummyrecord.wins = READUINT32(save.p); dummyrecord._saveid = i; - CONS_Printf(" (TEMPORARY DISPLAY) skinname is \"%s\", has %u wins\n", skinname, dummyrecord.wins); - tempskinreferences[i].id = MAXSKINS; if (skin != -1) @@ -5129,7 +5127,19 @@ void G_LoadGameData(void) cupwindata_t dummywindata[4]; // Find the relevant cup. - READSTRINGL(save.p, cupname, sizeof(cupname)); + if (versionMinor < 5) + { + // Before this version cups were called things like RING. + // Now that example cup would be called RR_RING instead. + cupname[0] = cupname[1] = 'R'; + cupname[2] = '_'; + READSTRINGL(save.p, (cupname + 3), sizeof(cupname) - 3); + } + else + { + READSTRINGL(save.p, cupname, sizeof(cupname)); + } + UINT32 hash = quickncasehash(cupname, MAXCUPNAME); for (cup = kartcupheaders; cup; cup = cup->next) { diff --git a/src/k_hud.c b/src/k_hud.c index 77ef7ab9b..20da0419c 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -162,7 +162,7 @@ static patch_t *kp_localtag[4][2]; static patch_t *kp_talk; static patch_t *kp_typdot; -static patch_t *kp_eggnum[6]; +patch_t *kp_eggnum[6]; static patch_t *kp_flameshieldmeter[FLAMESHIELD_MAX][2]; static patch_t *kp_flameshieldmeter_bg[FLAMESHIELD_MAX][2]; @@ -1793,7 +1793,7 @@ void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT32 splitflags, U } if (mode && !drawtime) - V_DrawTimerString(TX, TY+3, splitflags, va("--'--\"--")); + V_DrawTimerString(TX, TY+3, splitflags, "--'--\"--"); else { // minutes time 00 __ __ diff --git a/src/k_hud.h b/src/k_hud.h index 498a6295f..041243d5b 100644 --- a/src/k_hud.h +++ b/src/k_hud.h @@ -75,6 +75,8 @@ extern patch_t *kp_button_down[2]; extern patch_t *kp_button_right[2]; extern patch_t *kp_button_left[2]; +extern patch_t *kp_eggnum[6]; + #ifdef __cplusplus } // extern "C" #endif diff --git a/src/k_menu.h b/src/k_menu.h index ab3a26443..70f6b79c3 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -1259,10 +1259,23 @@ void M_DrawChallenges(void); void M_ChallengesTick(void); boolean M_ChallengesInputs(INT32 ch); +typedef enum +{ + //statisticspage_overview = 0, + statisticspage_chars = 0, + statisticspage_gp, + statisticspage_maps, + statisticspage_max +} statisticspage_t; + extern struct statisticsmenu_s { + statisticspage_t page; INT32 location; INT32 nummaps; + INT32 gotmedals; + INT32 nummedals; INT32 numextramedals; + UINT32 statgridplayed[9][9]; INT32 maxscroll; UINT16 *maplist; } statisticsmenu; @@ -1271,6 +1284,9 @@ void M_Statistics(INT32 choice); void M_DrawStatistics(void); boolean M_StatisticsInputs(INT32 ch); +void M_DrawCharacterIconAndEngine(INT32 x, INT32 y, UINT8 skin, UINT8 *colormap, boolean dot); +fixed_t M_DrawCupWinData(INT32 rankx, INT32 ranky, cupheader_t *cup, UINT8 difficulty, boolean flash, boolean statsmode); + #define MAXWRONGPLAYER MAXSPLITSCREENPLAYERS #define WRONGPLAYEROFFSCREEN 48 diff --git a/src/k_menudraw.c b/src/k_menudraw.c index be39be922..b789a97f4 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -162,7 +162,7 @@ static fixed_t bgImageScroll = 0; static char bgImageName[9]; #define MENUBG_TEXTSCROLL 6 -#define MENUBG_IMAGESCROLL 32 +#define MENUBG_IMAGESCROLL 36 void M_UpdateMenuBGImage(boolean forceReset) { @@ -181,7 +181,7 @@ void M_UpdateMenuBGImage(boolean forceReset) if (forceReset == false && strcmp(bgImageName, oldName)) { - bgImageScroll = (BASEVIDWIDTH / 2)*FRACUNIT; + bgImageScroll = (3 * BASEVIDWIDTH) * (FRACUNIT / 4); } } @@ -205,15 +205,18 @@ void M_DrawMenuBackground(void) V_DrawFixedPatch(8 * FRACUNIT, -bgText1Scroll + text1loop, FRACUNIT, V_SUBTRACT, text1, NULL); - bgText1Scroll += (MENUBG_TEXTSCROLL*renderdeltatics); - while (bgText1Scroll > text1loop) - bgText1Scroll -= text1loop; - V_DrawFixedPatch(-bgText2Scroll, (BASEVIDHEIGHT-8) * FRACUNIT, FRACUNIT, V_ADD, text2, NULL); V_DrawFixedPatch(-bgText2Scroll + text2loop, (BASEVIDHEIGHT-8) * FRACUNIT, FRACUNIT, V_ADD, text2, NULL); + if (renderdeltatics > 2*FRACUNIT) + return; // wipe hitch... + + bgText1Scroll += (MENUBG_TEXTSCROLL*renderdeltatics); + while (bgText1Scroll > text1loop) + bgText1Scroll -= text1loop; + bgText2Scroll += (MENUBG_TEXTSCROLL*renderdeltatics); while (bgText2Scroll > text2loop) bgText2Scroll -= text2loop; @@ -2394,7 +2397,7 @@ static void M_DrawCupTitle(INT16 y, levelsearch_t *levelsearch) boolean unlocked = (M_GetFirstLevelInList(&temp, levelsearch) != NEXTMAP_INVALID); UINT8 *colormap = R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_GREY, GTC_MENUCACHE); patch_t *icon = W_CachePatchName(levelsearch->cup->icon, PU_CACHE); - const char *str = (unlocked ? va("%s Cup", levelsearch->cup->name) : "???"); + const char *str = (unlocked ? va("%s Cup", levelsearch->cup->realname) : "???"); INT16 offset = V_LSTitleLowStringWidth(str, 0) / 2; V_DrawLSTitleLowString(BASEVIDWIDTH/2 - offset, y+6, 0, str); @@ -2415,6 +2418,175 @@ static void M_DrawCupTitle(INT16 y, levelsearch_t *levelsearch) } } +fixed_t M_DrawCupWinData(INT32 rankx, INT32 ranky, cupheader_t *cup, UINT8 difficulty, boolean flash, boolean statsmode) +{ + INT32 rankw = 14 + 1 + 12 + 1 + 12; + + if (!cup) + return 0; + + boolean noemerald = (!gamedata->everseenspecial || difficulty == KARTSPEED_EASY); + + if (statsmode) + { + rankw += 10 + 1; + + if (noemerald) + { + rankw -= (12 + 1); + rankx += 7; // vibes-based maths, as tyron puts it + } + } + + rankx += 19 - (rankw / 2); + + cupwindata_t *windata = &(cup->windata[difficulty]); + if (windata->best_placement == 0) + { + if (statsmode) + { + V_DrawCharacter((10-4)/2 + rankx, ranky, '.' | V_GRAYMAP, false); + rankx += 10 + 1; + V_DrawCharacter((14-4)/2 + rankx, ranky, '.' | V_GRAYMAP, false); + rankx += 14 + 1; + V_DrawCharacter((12-4)/2 + rankx, ranky, '.' | V_GRAYMAP, false); + + if (!noemerald) + { + rankx += 12 + 1; + V_DrawCharacter((12-4)/2 + rankx, ranky, '.' | V_GRAYMAP, false); + } + } + return rankw; + } + + UINT8 *colormap = NULL; + + if (statsmode) + { + patch_t *monPat = W_CachePatchName(va("CUPMON%c%c", '0' + cup->monitor, 'C'), PU_CACHE); + UINT16 moncolor = SKINCOLOR_NONE; + + switch (windata->best_placement) + { + case 1: + moncolor = SKINCOLOR_GOLD; + break; + case 2: + moncolor = SKINCOLOR_SILVER; + break; + case 3: + moncolor = SKINCOLOR_BRONZE; + break; + default: + moncolor = SKINCOLOR_BEIGE; + break; + } + + if (moncolor != SKINCOLOR_NONE) + colormap = R_GetTranslationColormap(TC_RAINBOW, moncolor, GTC_MENUCACHE); + + if (monPat) + V_DrawFixedPatch((rankx)*FRACUNIT, (ranky)*FRACUNIT, FRACUNIT, 0, monPat, colormap); + + rankx += 10 + 1; + + colormap = NULL; + } + + const gp_rank_e grade = windata->best_grade; // (cupgrid.previewanim/TICRATE) % (GRADE_S + 1); -- testing + patch_t *gradePat = NULL; + UINT16 gradecolor = K_GetGradeColor(grade); + + if (gradecolor != SKINCOLOR_NONE) + colormap = R_GetTranslationColormap(TC_DEFAULT, gradecolor, GTC_MENUCACHE); + + switch (grade) + { + case GRADE_E: + gradePat = W_CachePatchName("R_CUPRNE", PU_CACHE); + break; + case GRADE_D: + gradePat = W_CachePatchName("R_CUPRND", PU_CACHE); + break; + case GRADE_C: + gradePat = W_CachePatchName("R_CUPRNC", PU_CACHE); + break; + case GRADE_B: + gradePat = W_CachePatchName("R_CUPRNB", PU_CACHE); + break; + case GRADE_A: + gradePat = W_CachePatchName("R_CUPRNA", PU_CACHE); + break; + case GRADE_S: + gradePat = W_CachePatchName("R_CUPRNS", PU_CACHE); + break; + default: + break; + } + + if (gradePat) + V_DrawFixedPatch((rankx)*FRACUNIT, (ranky)*FRACUNIT, FRACUNIT, 0, gradePat, colormap); + + rankx += 14 + 1; + + patch_t *charPat = NULL; + + if ((windata->best_skin.unloaded != NULL) + || (windata->best_skin.id > numskins)) + { + colormap = NULL; + + charPat = W_CachePatchName("HUHMAP", PU_CACHE); + } + else + { + UINT8 skin = windata->best_skin.id; + + colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); + + charPat = faceprefix[skin][FACE_MINIMAP]; + } + + if (charPat) + V_DrawFixedPatch((rankx)*FRACUNIT, (ranky)*FRACUNIT, FRACUNIT, 0, charPat, colormap); + + rankx += 12 + 1; + + if (noemerald) + ; + else if (windata->got_emerald == true) + { + if (cup->emeraldnum == 0) + V_DrawCharacter(rankx+2, ranky+2, '+', false); + else + { + colormap = NULL; + + if (!flash) + { + UINT16 col = SKINCOLOR_CHAOSEMERALD1 + (cup->emeraldnum-1) % 7; + + colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); + } + + const char *emname = va( + "%sMAP%c", + (cup->emeraldnum > 7) ? "SUP" : "EME", + colormap ? '\0' : 'B' + ); + + V_DrawFixedPatch((rankx)*FRACUNIT, (ranky)*FRACUNIT, FRACUNIT, 0, W_CachePatchName(emname, PU_CACHE), colormap); + } + } + else if (statsmode) + { + V_DrawCharacter((12-4)/2 + rankx, ranky, '.' | V_GRAYMAP, false); + } + + return rankw; +} + void M_DrawCupSelect(void) { UINT8 i, j, temp = 0; @@ -2520,100 +2692,16 @@ void M_DrawCupSelect(void) V_DrawScaledPatch(x + 32, y + 32, 0, W_CachePatchName("CUPBKUP1", PU_CACHE)); } - if (!windata) - ; - else if (windata->best_placement != 0) + if (windata && windata->best_placement != 0) { - const INT32 rankw = 14 + 12 + 12 + 2; - INT32 rankx = (x + 19) - (rankw / 2); - const INT32 ranky = 8 + (j*100) - (30*menutransition.tics); - - patch_t *gradePat = NULL; - colormap = NULL; - - const gp_rank_e grade = windata->best_grade; // (cupgrid.previewanim/TICRATE) % (GRADE_S + 1); -- testing - UINT16 gradecolor = K_GetGradeColor(grade); - - if (gradecolor != SKINCOLOR_NONE) - colormap = R_GetTranslationColormap(TC_DEFAULT, gradecolor, GTC_MENUCACHE); - - switch (grade) - { - case GRADE_E: - gradePat = W_CachePatchName("R_CUPRNE", PU_CACHE); - break; - case GRADE_D: - gradePat = W_CachePatchName("R_CUPRND", PU_CACHE); - break; - case GRADE_C: - gradePat = W_CachePatchName("R_CUPRNC", PU_CACHE); - break; - case GRADE_B: - gradePat = W_CachePatchName("R_CUPRNB", PU_CACHE); - break; - case GRADE_A: - gradePat = W_CachePatchName("R_CUPRNA", PU_CACHE); - break; - case GRADE_S: - gradePat = W_CachePatchName("R_CUPRNS", PU_CACHE); - break; - default: - break; - } - - if (gradePat) - V_DrawFixedPatch((rankx)*FRACUNIT, (ranky)*FRACUNIT, FRACUNIT, 0, gradePat, colormap); - - rankx += 14 + 1; - - patch_t *charPat = NULL; - - if ((windata->best_skin.unloaded != NULL) - || (windata->best_skin.id > numskins)) - { - colormap = NULL; - - charPat = W_CachePatchName("HUHMAP", PU_CACHE); - } - else - { - UINT8 skin = windata->best_skin.id; - - colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); - - charPat = faceprefix[skin][FACE_MINIMAP]; - } - - if (charPat) - V_DrawFixedPatch((rankx)*FRACUNIT, (ranky)*FRACUNIT, FRACUNIT, 0, charPat, colormap); - - if (cv_dummygpdifficulty.value > 0 - && windata->got_emerald == true) - { - rankx += 12 + 1; - - if (templevelsearch.cup->emeraldnum == 0) - V_DrawCharacter(rankx+2, ranky+2, '+', false); - else - { - colormap = NULL; - - if (!(cupgrid.previewanim & 1)) - { - UINT16 col = SKINCOLOR_CHAOSEMERALD1 + (templevelsearch.cup->emeraldnum-1) % 7; - - colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); - } - - const char *emname = va( - "%sMAP%c", - (templevelsearch.cup->emeraldnum > 7) ? "SUP" : "EME", - colormap ? '\0' : 'B' - ); - - V_DrawFixedPatch((rankx)*FRACUNIT, (ranky)*FRACUNIT, FRACUNIT, 0, W_CachePatchName(emname, PU_CACHE), colormap); - } - } + M_DrawCupWinData( + x, + 8 + (j*100) - (30*menutransition.tics), + templevelsearch.cup, + cv_dummygpdifficulty.value, + (cupgrid.previewanim & 1), + false + ); } } } @@ -2656,16 +2744,22 @@ void M_DrawCupSelect(void) static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) { char word1[22]; - char word2[22]; + char word2[22 + 2]; // actnum UINT8 word1len = 0; UINT8 word2len = 0; INT16 x2 = x; UINT8 i; - if (!mapheaderinfo[map] || !mapheaderinfo[map]->lvlttl[0]) + if (!mapheaderinfo[map] + || ( + !mapheaderinfo[map]->menuttl[0] + && !mapheaderinfo[map]->lvlttl[0] + ) + ) return; - if (mapheaderinfo[map]->zonttl[0]) + if (!mapheaderinfo[map]->menuttl[0] + && mapheaderinfo[map]->zonttl[0]) { boolean one = true; boolean two = true; @@ -2696,12 +2790,17 @@ static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) { boolean donewithone = false; + char *ttlsource = + mapheaderinfo[map]->menuttl[0] + ? mapheaderinfo[map]->menuttl + : mapheaderinfo[map]->lvlttl; + for (i = 0; i < 22; i++) { - if (!mapheaderinfo[map]->lvlttl[i]) + if (!ttlsource[i]) break; - if (mapheaderinfo[map]->lvlttl[i] == ' ') + if (ttlsource[i] == ' ') { if (!donewithone) { @@ -2712,18 +2811,18 @@ static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) if (donewithone) { - word2[word2len] = mapheaderinfo[map]->lvlttl[i]; + word2[word2len] = ttlsource[i]; word2len++; } else { - word1[word1len] = mapheaderinfo[map]->lvlttl[i]; + word1[word1len] = ttlsource[i]; word1len++; } } } - if (mapheaderinfo[map]->actnum) + if (!mapheaderinfo[map]->menuttl[0] && mapheaderinfo[map]->actnum) { word2[word2len] = ' '; word2len++; @@ -5280,6 +5379,41 @@ drawborder: #define challengetransparentstrength 8 +void M_DrawCharacterIconAndEngine(INT32 x, INT32 y, UINT8 skin, UINT8 *colormap, boolean dot) +{ + V_DrawFixedPatch(x*FRACUNIT, y*FRACUNIT, + FRACUNIT, + 0, faceprefix[skin][FACE_RANK], + colormap); + + if (dot) + { + V_DrawScaledPatch(x, y + 11, 0, W_CachePatchName("ALTSDOT", PU_CACHE)); + } + + V_DrawFadeFill(x+16, y, 16, 16, 0, 31, challengetransparentstrength); + + V_DrawFill(x+16+5, y+1, 1, 14, 0); + V_DrawFill(x+16+5+5, y+1, 1, 14, 0); + V_DrawFill(x+16+1, y+5, 14, 1, 0); + V_DrawFill(x+16+1, y+5+5, 14, 1, 0); + + // The following is a partial duplication of R_GetEngineClass + { + INT32 s = (skins[skin].kartspeed - 1)/3; + INT32 w = (skins[skin].kartweight - 1)/3; + + #define LOCKSTAT(stat) \ + if (stat < 0) { stat = 0; } \ + if (stat > 2) { stat = 2; } + LOCKSTAT(s); + LOCKSTAT(w); + #undef LOCKSTAT + + V_DrawFill(x+16 + (s*5), y + (w*5), 6, 6, 0); + } +} + static void M_DrawChallengePreview(INT32 x, INT32 y) { unlockable_t *ref = NULL; @@ -5346,37 +5480,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) break; } - V_DrawFixedPatch(4*FRACUNIT, (BASEVIDHEIGHT-(4+16))*FRACUNIT, - FRACUNIT, - 0, faceprefix[i][FACE_RANK], - colormap); - - if (i != skin) - { - V_DrawScaledPatch(4, (11 + BASEVIDHEIGHT-(4+16)), 0, W_CachePatchName("ALTSDOT", PU_CACHE)); - } - - V_DrawFadeFill(4+16, (BASEVIDHEIGHT-(4+16)), 16, 16, 0, 31, challengetransparentstrength); - - V_DrawFill(4+16+5, (BASEVIDHEIGHT-(4+16))+1, 1, 14, 0); - V_DrawFill(4+16+5+5, (BASEVIDHEIGHT-(4+16))+1, 1, 14, 0); - V_DrawFill(4+16+1, (BASEVIDHEIGHT-(4+16))+5, 14, 1, 0); - V_DrawFill(4+16+1, (BASEVIDHEIGHT-(4+16))+5+5, 14, 1, 0); - - // The following is a partial duplication of R_GetEngineClass - { - INT32 s = (skins[skin].kartspeed - 1)/3; - INT32 w = (skins[skin].kartweight - 1)/3; - - #define LOCKSTAT(stat) \ - if (stat < 0) { stat = 0; } \ - if (stat > 2) { stat = 2; } - LOCKSTAT(s); - LOCKSTAT(w); - #undef LOCKSTAT - - V_DrawFill(4+16 + (s*5), (BASEVIDHEIGHT-(4+16)) + (w*5), 6, 6, 0); - } + M_DrawCharacterIconAndEngine(4, BASEVIDHEIGHT-(4+16), i, colormap, (i == skin)); } break; } @@ -5884,6 +5988,10 @@ challengedesc: keyholdrotation = 360 * ((challengesmenu.chaokeyhold - CHAOHOLD_BEGIN)) * (FRACUNIT/(CHAOHOLD_MAX - (CHAOHOLD_BEGIN + CHAOHOLD_END))); + + INT32 time = 3 - (keyholdrotation - 1) / (90 * FRACUNIT); + if (time <= 5 && time >= 0) + V_DrawScaledPatch(selectx + 2, selecty - 2, 0, kp_eggnum[time]); } else { @@ -5994,15 +6102,74 @@ static void M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y) static void M_DrawStatsMaps(void) { - INT32 y = 80, i = -1; + INT32 y = 70, i; INT16 mnum; boolean dotopname = true, dobottomarrow = (statisticsmenu.location < statisticsmenu.maxscroll); INT32 location = statisticsmenu.location; + tic_t besttime = 0; + + if (!statisticsmenu.maplist) + { + V_DrawCenteredThinString(BASEVIDWIDTH/2, 70, 0, "No maps!?"); + return; + } + + INT32 mapsunfinished = 0; + + V_DrawThinString(30, 60, 0, va("x %d/%d", statisticsmenu.gotmedals, statisticsmenu.nummedals)); + V_DrawSmallMappedPatch(20, 60, 0, W_CachePatchName("GOTITA", PU_CACHE), + R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_GOLD, GTC_MENUCACHE)); + + INT32 medalspos = BASEVIDWIDTH - 20; + + boolean timeattack[3]; + timeattack[0] = M_SecretUnlocked(SECRET_TIMEATTACK, true); + timeattack[1] = M_SecretUnlocked(SECRET_PRISONBREAK, true); + timeattack[2] = M_SecretUnlocked(SECRET_SPECIALATTACK, true); + + if (timeattack[0] || timeattack[1] || timeattack[2]) + { + medalspos -= 64; + + for (i = 0; i < nummapheaders; i++) + { + // Check for no visibility + if (!mapheaderinfo[i] || (mapheaderinfo[i]->menuflags & (LF2_NOTIMEATTACK|LF2_HIDEINSTATS|LF2_HIDEINMENU))) + continue; + + // Has to be accessible via time attack + if (!(mapheaderinfo[i]->typeoflevel & (TOL_RACE|TOL_BATTLE|TOL_SPECIAL|TOL_VERSUS))) + continue; + + if (mapheaderinfo[i]->records.time <= 0) + { + mapsunfinished++; + continue; + } + + besttime += mapheaderinfo[i]->records.time; + } + + V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 60, 0, + va( + "Combined time: %c%i:%02i:%02i.%02i (%s)", + (mapsunfinished ? '\x85' : '\x80'), + G_TicsToHours(besttime), + G_TicsToMinutes(besttime, false), + G_TicsToSeconds(besttime), + G_TicsToCentiseconds(besttime), + (mapsunfinished ? "incomplete" : "complete") + ) + ); + } + if (location) - V_DrawCharacter(10, y-(skullAnimCounter/5), + V_DrawCharacter(10, 80-(skullAnimCounter/5), '\x1A' | highlightflags, false); // up arrow + i = -1; + while ((mnum = statisticsmenu.maplist[++i]) != NEXTMAP_INVALID) { if (location) @@ -6024,8 +6191,10 @@ static void M_DrawStatsMaps(void) { const char *str; - if (mapheaderinfo[mnum]->cup) - str = va("%s CUP", mapheaderinfo[mnum]->cup->name); + if (mapheaderinfo[mnum]->typeoflevel & TOL_TUTORIAL) + str = "TUTORIAL MODE"; + else if (mapheaderinfo[mnum]->cup) + str = va("%s CUP", mapheaderinfo[mnum]->cup->realname); else str = "LOST AND FOUND"; @@ -6034,7 +6203,11 @@ static void M_DrawStatsMaps(void) if (dotopname) { - V_DrawRightAlignedThinString(BASEVIDWIDTH-20, y, highlightflags, "MEDALS"); + V_DrawRightAlignedThinString(medalspos, y, highlightflags, "MEDALS"); + + if (timeattack[0] || timeattack[1] || timeattack[2]) + V_DrawRightAlignedThinString((BASEVIDWIDTH-20), y, highlightflags, "TIME"); + dotopname = false; } @@ -6045,8 +6218,41 @@ static void M_DrawStatsMaps(void) continue; } - M_DrawMapMedals(mnum+1, 291, y); + V_DrawFadeFill(24, y + 5, (BASEVIDWIDTH - 24) - 24, 3, 0, 31, 8 - (i & 1)*2); + if (!(mapheaderinfo[mnum]->menuflags & LF2_NOTIMEATTACK) + && ( + (timeattack[0] && (mapheaderinfo[mnum]->typeoflevel & TOL_RACE)) + || (timeattack[1] && (mapheaderinfo[mnum]->typeoflevel & TOL_BATTLE)) + || (timeattack[2] && (mapheaderinfo[mnum]->typeoflevel & (TOL_SPECIAL|TOL_VERSUS))) + ) + ) + { + besttime = mapheaderinfo[mnum]->records.time; + + if (besttime) + { + V_DrawRightAlignedString((BASEVIDWIDTH-24), y+1, 0, + va("%02d'%02d\"%02d", + G_TicsToMinutes(besttime, true), + G_TicsToSeconds(besttime), + G_TicsToCentiseconds(besttime) + ) + ); + } + else + { + V_DrawRightAlignedString((BASEVIDWIDTH-24), y+1, V_GRAYMAP, "--'--\"--"); + } + } + + M_DrawMapMedals(mnum+1, medalspos - 8, y); + + if (mapheaderinfo[mnum]->menuttl[0]) + { + V_DrawThinString(24, y, V_FORCEUPPERCASE, mapheaderinfo[mnum]->menuttl); + } + else { char *title = G_BuildMapTitle(mnum+1); V_DrawThinString(24, y, V_FORCEUPPERCASE, title); @@ -6126,26 +6332,258 @@ static void M_DrawStatsMaps(void) } bottomarrow: if (dobottomarrow) - V_DrawCharacter(10, y-STATSSTEP + (skullAnimCounter/5), + V_DrawCharacter(10, BASEVIDHEIGHT-20 + (skullAnimCounter/5), '\x1B' | highlightflags, false); // down arrow } +#undef STATSSTEP +#define STATSSTEP 18 + +static void M_DrawStatsChars(void) +{ + INT32 y = 80, i, j; + INT16 skin; + boolean dobottomarrow = (statisticsmenu.location < statisticsmenu.maxscroll); + INT32 location = statisticsmenu.location; + + if (!statisticsmenu.maplist || !statisticsmenu.nummaps) + { + V_DrawCenteredThinString(BASEVIDWIDTH/2, 70, 0, "No chars!?"); + return; + } + + if (location) + V_DrawCharacter(10, y-(skullAnimCounter/5), + '\x1A' | highlightflags, false); // up arrow + + i = -1; + + V_DrawThinString(20, y - 10, highlightflags, "CHARACTER"); + V_DrawRightAlignedThinString(BASEVIDWIDTH/2 + 34, y - 10, highlightflags, "WINS"); + + while ((skin = statisticsmenu.maplist[++i]) < numskins) + { + if (location) + { + --location; + continue; + } + + { + UINT8 *colormap = R_GetTranslationColormap(skin, skins[skin].prefcolor, GTC_MENUCACHE); + + M_DrawCharacterIconAndEngine(24, y, skin, colormap, false); + } + + V_DrawThinString(24+32+2, y+3, 0, skins[skin].realname); + + V_DrawRightAlignedThinString(BASEVIDWIDTH/2 + 30, y+3, 0, va("%d", skins[skin].records.wins)); + + y += STATSSTEP; + + if (y >= BASEVIDHEIGHT-STATSSTEP) + goto bottomarrow; + } + +bottomarrow: + if (dobottomarrow) + V_DrawCharacter(10, BASEVIDHEIGHT-20 + (skullAnimCounter/5), + '\x1B' | highlightflags, false); // down arrow + + UINT32 x = BASEVIDWIDTH - 20 - 90; + y = 88; + + V_DrawCenteredThinString(x + 45, y - 10, highlightflags, "HEATMAP"); + + V_DrawFadeFill(x, y, 91, 91, 0, 31, 8); // challengetransparentstrength + + V_DrawFill(x+30, y+1, 1, 89, 0); + V_DrawFill(x+60, y+1, 1, 89, 0); + V_DrawFill(x+1, y+30, 89, 1, 0); + V_DrawFill(x+1, y+60, 89, 1, 0); + + x++; + y++; + + for (i = 0; i < 9; i++) + { + for (j = 0; j < 9; j++) + { + if (statisticsmenu.statgridplayed[i][j] == 0) + continue; + + V_DrawFill( + x + (i * 10), + y + (j * 10), + 9, + 9, + 31 - ((statisticsmenu.statgridplayed[i][j] - 1) * 32) / FRACUNIT + ); + } + } +} + +#undef STATSSTEP +#define STATSSTEP 21 + +static void M_DrawStatsGP(void) +{ + INT32 y = 80, i, x, j, endj; + INT16 id; + boolean dobottomarrow = (statisticsmenu.location < statisticsmenu.maxscroll); + INT32 location = statisticsmenu.location; + + if (!statisticsmenu.maplist || !statisticsmenu.nummaps) + { + V_DrawCenteredThinString(BASEVIDWIDTH/2, 70, 0, "No cups!?"); + return; + } + + if (location) + V_DrawCharacter(10, y-(skullAnimCounter/5), + '\x1A' | highlightflags, false); // up arrow + + const INT32 width = 53; + + endj = KARTSPEED_NORMAL; + if (M_SecretUnlocked(SECRET_HARDSPEED, true)) + { + endj = M_SecretUnlocked(SECRET_MASTERMODE, true) + ? KARTGP_MASTER + : KARTSPEED_HARD; + } + + const INT32 h = (21 * min(5, statisticsmenu.nummaps)) - 1; + + x = 7 + BASEVIDWIDTH - 20 - width; + for (j = endj; j >= KARTSPEED_EASY; j--, x -= width) + { + if (j == KARTSPEED_EASY || !gamedata->everseenspecial) + { + V_DrawFadeFill(x + 6, y + 1, width - (12 + 1), h, 0, 31, 6 + (j & 1)*2); + V_DrawCenteredThinString(x + 19 + 7, y - 10, highlightflags|V_FORCEUPPERCASE, gpdifficulty_cons_t[j].strvalue); + x += (12 + 1); + } + else + { + V_DrawFadeFill(x - 7, y + 1, width, h, 0, 31, 6 + (j & 1)*2); + V_DrawCenteredThinString(x + 19, y - 10, highlightflags|V_FORCEUPPERCASE, gpdifficulty_cons_t[j].strvalue); + } + } + + i = -1; + + V_DrawThinString(20, y - 10, highlightflags, "CUP"); + + cupheader_t *cup = kartcupheaders; + + while ((id = statisticsmenu.maplist[++i]) < numkartcupheaders) + { + if (location) + { + --location; + continue; + } + + // If we have ANY sort of sorting other than instantiation, this won't work + while (cup && cup->id != id) + { + cup = cup->next; + } + + if (!cup) + { + goto bottomarrow; + } + + V_DrawFill(24, y+1, 21, 20, 31); + + V_DrawScaledPatch(24-1, y, 0, W_CachePatchName(cup->icon, PU_CACHE)); + V_DrawScaledPatch(24-1, y, 0, W_CachePatchName("CUPBOX", PU_CACHE)); + + V_DrawThinString(24+21+2, y + 7, 0, cup->realname); + + x = 7 + BASEVIDWIDTH - 20 - width; + for (j = endj; j >= KARTSPEED_EASY; j--) + { + x -= (M_DrawCupWinData(x, y + 5, cup, j, false, true) + 2); + } + + y += STATSSTEP; + + if (y >= BASEVIDHEIGHT-STATSSTEP) + goto bottomarrow; + } + +bottomarrow: + if (dobottomarrow) + V_DrawCharacter(10, BASEVIDHEIGHT-20 + (skullAnimCounter/5), + '\x1B' | highlightflags, false); // down arrow +} + +#undef STATSSTEP + void M_DrawStatistics(void) { char beststr[256]; - tic_t besttime = 0; - INT32 i; - INT32 mapsunfinished = 0; - { patch_t *bg = W_CachePatchName("M_XTRABG", PU_CACHE); V_DrawFixedPatch(0, 0, FRACUNIT, 0, bg, NULL); } + { + const char *pagename = NULL; + INT32 pagenamewidth = 0; + + V_DrawFixedPatch(0, 0, FRACUNIT, 0, W_CachePatchName("MENUHINT", PU_CACHE), NULL); + + switch (statisticsmenu.page) + { + + case statisticspage_gp: + { + pagename = gamedata->everseenspecial + ? "GRAND PRIX & EMERALDS" + : "GRAND PRIX"; + M_DrawStatsGP(); + break; + } + + case statisticspage_maps: + { + pagename = "LEVELS & MEDALS"; + M_DrawStatsMaps(); + break; + } + + case statisticspage_chars: + { + pagename = "CHARACTERS & ENGINE CLASSES"; + M_DrawStatsChars(); + break; + } + + default: + break; + } + + if (pagename) + { + pagenamewidth = V_ThinStringWidth(pagename, 0); + V_DrawThinString((BASEVIDWIDTH - pagenamewidth)/2, 12, 0, pagename); + } + + V_DrawCharacter((BASEVIDWIDTH - pagenamewidth)/2 - 10 - (skullAnimCounter/5), 12, + '\x1C', false); // left arrow + + V_DrawCharacter((BASEVIDWIDTH + pagenamewidth)/2 + 2 + (skullAnimCounter/5), 12, + '\x1D', false); // right arrow + } + beststr[0] = 0; - V_DrawThinString(20, 22, highlightflags, "Total Play Time:"); + V_DrawThinString(20, 30, highlightflags, "Total Play Time:"); besttime = G_TicsToHours(gamedata->totalplaytime); if (besttime) { @@ -6164,10 +6602,10 @@ void M_DrawStatistics(void) } besttime = G_TicsToSeconds(gamedata->totalplaytime); strcat(beststr, va("%i second%s", besttime, (besttime == 1 ? "" : "s"))); - V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 22, 0, beststr); + V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 30, 0, beststr); beststr[0] = 0; - V_DrawThinString(20, 32, highlightflags, "Total Rings:"); + V_DrawThinString(20, 40, highlightflags, "Total Rings:"); if (gamedata->totalrings > GDMAX_RINGS) { sprintf(beststr, "%c999,999,999+", '\x82'); @@ -6184,10 +6622,10 @@ void M_DrawStatistics(void) { sprintf(beststr, "%u", gamedata->totalrings); } - V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 32, 0, va("%s collected", beststr)); + V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 40, 0, va("%s collected", beststr)); beststr[0] = 0; - V_DrawThinString(20, 42, highlightflags, "Total Rounds:"); + V_DrawThinString(20, 50, highlightflags, "Total Rounds:"); strcat(beststr, va("%u Race", gamedata->roundsplayed[GDGT_RACE])); @@ -6208,49 +6646,9 @@ void M_DrawStatistics(void) strcat(beststr, va(", %u Custom", gamedata->roundsplayed[GDGT_CUSTOM])); } - V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 42, 0, beststr); - - if (!statisticsmenu.maplist) - { - V_DrawCenteredThinString(BASEVIDWIDTH/2, 62, 0, "No maps!?"); - return; - } - - besttime = 0; - - for (i = 0; i < nummapheaders; i++) - { - if (!mapheaderinfo[i] || (mapheaderinfo[i]->menuflags & (LF2_NOTIMEATTACK|LF2_HIDEINSTATS|LF2_HIDEINMENU))) - continue; - - if (mapheaderinfo[i]->records.time <= 0) - { - mapsunfinished++; - continue; - } - - besttime += mapheaderinfo[i]->records.time; - } - - V_DrawThinString(20, 60, 0, "Combined time records:"); - - sprintf(beststr, "%i:%02i:%02i.%02i", G_TicsToHours(besttime), G_TicsToMinutes(besttime, false), G_TicsToSeconds(besttime), G_TicsToCentiseconds(besttime)); - V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 60, (mapsunfinished ? V_REDMAP : 0), beststr); - - if (mapsunfinished) - V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 70, V_REDMAP, va("(%d unfinished)", mapsunfinished)); - else - V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 70, 0, "(complete)"); - - V_DrawThinString(32, 70, 0, va("x %d/%d", M_CountMedals(false, false), M_CountMedals(true, false))); - V_DrawSmallMappedPatch(20, 70, 0, W_CachePatchName("GOTITA", PU_CACHE), - R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_GOLD, GTC_MENUCACHE)); - - M_DrawStatsMaps(); + V_DrawRightAlignedThinString(BASEVIDWIDTH-20, 50, 0, beststr); } -#undef STATSSTEP - static INT32 M_WrongWarpFallingHelper(INT32 y, INT32 falltime) { if (wrongwarp.ticker < falltime) diff --git a/src/lua_maplib.c b/src/lua_maplib.c index c9ae8fca0..b7926fcca 100644 --- a/src/lua_maplib.c +++ b/src/lua_maplib.c @@ -2487,8 +2487,8 @@ static int mapheaderinfo_get(lua_State *L) const char *field = luaL_checkstring(L, 2); if (fastcmp(field,"lvlttl")) lua_pushstring(L, header->lvlttl); - else if (fastcmp(field,"subttl")) - lua_pushstring(L, header->subttl); + else if (fastcmp(field,"menuttl")) + lua_pushstring(L, header->menuttl); else if (fastcmp(field,"zonttl")) lua_pushstring(L, header->zonttl); else if (fastcmp(field,"actnum")) diff --git a/src/m_cond.c b/src/m_cond.c index 828845823..22c9e7db9 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -1432,7 +1432,7 @@ static const char *M_GetConditionString(condition_t *cn) continue; return va("%s%s %s CUP", completetype, orbetter, - (M_CupLocked(cup) ? "???" : cup->name) + (M_CupLocked(cup) ? "???" : cup->realname) ); } return va("INVALID CUP CONDITION \"%d:%d\"", cn->type, cn->requirement); diff --git a/src/menus/extras-statistics.c b/src/menus/extras-statistics.c index bc4843b27..7e774315e 100644 --- a/src/menus/extras-statistics.c +++ b/src/menus/extras-statistics.c @@ -5,10 +5,11 @@ #include "../z_zone.h" #include "../m_cond.h" // Condition Sets #include "../s_sound.h" +#include "../r_skins.h" struct statisticsmenu_s statisticsmenu; -static boolean M_StatisticsAddMap(UINT16 map, cupheader_t *cup, boolean *headerexists) +static boolean M_StatisticsAddMap(UINT16 map, cupheader_t *cup, boolean *headerexists, boolean tutorial) { if (!mapheaderinfo[map]) return false; @@ -16,12 +17,11 @@ static boolean M_StatisticsAddMap(UINT16 map, cupheader_t *cup, boolean *headere if (mapheaderinfo[map]->cup != cup) return false; - // Check for no visibility - if (mapheaderinfo[map]->menuflags & (LF2_NOTIMEATTACK|LF2_HIDEINSTATS|LF2_HIDEINMENU)) + if (((mapheaderinfo[map]->typeoflevel & TOL_TUTORIAL) == TOL_TUTORIAL) != tutorial) return false; - // No TEST RUN, as that's another exception to Time Attack too - if (!mapheaderinfo[map]->typeoflevel) + // Check for no visibility + if (mapheaderinfo[map]->menuflags & (LF2_HIDEINSTATS|LF2_HIDEINMENU)) return false; // Check for completion @@ -43,17 +43,16 @@ static boolean M_StatisticsAddMap(UINT16 map, cupheader_t *cup, boolean *headere return true; } -void M_Statistics(INT32 choice) +static void M_StatisticsMaps(void) { cupheader_t *cup; UINT16 i; boolean headerexists; - (void)choice; - statisticsmenu.maplist = Z_Malloc(sizeof(UINT16) * (nummapheaders+1 + numkartcupheaders), PU_STATIC, NULL); statisticsmenu.nummaps = 0; + // Cups for (cup = kartcupheaders; cup; cup = cup->next) { headerexists = false; @@ -66,28 +65,199 @@ void M_Statistics(INT32 choice) if (cup->cachedlevels[i] >= nummapheaders) continue; - M_StatisticsAddMap(cup->cachedlevels[i], cup, &headerexists); + M_StatisticsAddMap(cup->cachedlevels[i], cup, &headerexists, false); } } + // Lost and Found headerexists = false; - for (i = 0; i < nummapheaders; i++) { - M_StatisticsAddMap(i, NULL, &headerexists); + M_StatisticsAddMap(i, NULL, &headerexists, false); } - if ((i = statisticsmenu.numextramedals = M_CountMedals(true, true)) != 0) + // Tutorial + headerexists = false; + for (i = 0; i < nummapheaders; i++) + { + M_StatisticsAddMap(i, NULL, &headerexists, true); + } + + if ((i = statisticsmenu.numextramedals) != 0) i += 2; statisticsmenu.maplist[statisticsmenu.nummaps] = NEXTMAP_INVALID; - statisticsmenu.maxscroll = (statisticsmenu.nummaps + i) - 11; + statisticsmenu.maxscroll = (statisticsmenu.nummaps + i) - 12; statisticsmenu.location = 0; if (statisticsmenu.maxscroll < 0) { statisticsmenu.maxscroll = 0; } +} + +static void M_StatisticsChars(void) +{ + UINT16 i; + + statisticsmenu.maplist = Z_Malloc(sizeof(UINT16) * (1 + numskins), PU_STATIC, NULL); + statisticsmenu.nummaps = 0; + + UINT32 beststat = 0; + + for (i = 0; i < numskins; i++) + { + if (!R_SkinUsable(-1, i, false)) + continue; + + statisticsmenu.maplist[statisticsmenu.nummaps++] = i; + + if (skins[i].records.wins == 0) + continue; + + // The following is a partial duplication of R_GetEngineClass + { + if (skins[i].flags & SF_IRONMAN) + continue; // does not add to any engine class + + INT32 s = (skins[i].kartspeed - 1); + INT32 w = (skins[i].kartweight - 1); + + #define LOCKSTAT(stat) \ + if (stat < 0) { continue; } \ + if (stat > 8) { continue; } + LOCKSTAT(s); + LOCKSTAT(w); + #undef LOCKSTAT + + if ( + statisticsmenu.statgridplayed[s][w] > skins[i].records.wins + && (UINT32_MAX - statisticsmenu.statgridplayed[s][w]) < skins[i].records.wins + ) + continue; // overflow protection + + statisticsmenu.statgridplayed[s][w] += skins[i].records.wins; + + if (beststat >= statisticsmenu.statgridplayed[s][w]) + continue; + + beststat = statisticsmenu.statgridplayed[s][w]; + } + } + + statisticsmenu.maplist[statisticsmenu.nummaps] = MAXSKINS; + + statisticsmenu.location = 0; + statisticsmenu.maxscroll = statisticsmenu.nummaps - 6; + + if (statisticsmenu.maxscroll < 0) + { + statisticsmenu.maxscroll = 0; + } + + if (beststat != 0) + { + UINT16 j; + UINT8 shif = 0; + + // Done this way to ensure ample precision but also prevent overflow + while (beststat < FRACUNIT) + { + beststat <<= 1; + shif++; + } + + for (i = 0; i < 9; i++) + { + for (j = 0; j < 9; j++) + { + if (statisticsmenu.statgridplayed[i][j] == 0) + continue; + + statisticsmenu.statgridplayed[i][j] = + FixedDiv( + statisticsmenu.statgridplayed[i][j] << shif, + beststat + ); + + if (statisticsmenu.statgridplayed[i][j] == 0) + statisticsmenu.statgridplayed[i][j] = 1; + } + } + } +} + +static void M_StatisticsGP(void) +{ + statisticsmenu.maplist = Z_Malloc(sizeof(UINT16) * (1 + numkartcupheaders), PU_STATIC, NULL); + statisticsmenu.nummaps = 0; + + cupheader_t *cup; + + for (cup = kartcupheaders; cup; cup = cup->next) + { + if (M_CupLocked(cup)) + continue; + + statisticsmenu.maplist[statisticsmenu.nummaps++] = cup->id; + } + + statisticsmenu.maplist[statisticsmenu.nummaps] = UINT16_MAX; + + statisticsmenu.location = 0; + statisticsmenu.maxscroll = statisticsmenu.nummaps - 5; + + if (statisticsmenu.maxscroll < 0) + { + statisticsmenu.maxscroll = 0; + } +} + +static void M_StatisticsPageInit(void) +{ + switch (statisticsmenu.page) + { + case statisticspage_maps: + { + M_StatisticsMaps(); + break; + } + + case statisticspage_chars: + { + M_StatisticsChars(); + break; + } + + case statisticspage_gp: + { + M_StatisticsGP(); + break; + } + + default: + break; + } +} + +static void M_StatisticsPageClear(void) +{ + if (statisticsmenu.maplist != NULL) + { + Z_Free(statisticsmenu.maplist); + statisticsmenu.maplist = NULL; + } +} + +void M_Statistics(INT32 choice) +{ + (void)choice; + + statisticsmenu.gotmedals = M_CountMedals(false, false); + statisticsmenu.nummedals = M_CountMedals(true, false); + statisticsmenu.numextramedals = M_CountMedals(true, true); + + M_StatisticsPageInit(); MISC_StatisticsDef.prevMenu = currentMenu; M_SetupNextMenu(&MISC_StatisticsDef, false); @@ -104,12 +274,36 @@ boolean M_StatisticsInputs(INT32 ch) M_GoBack(0); M_SetMenuDelay(pid); - Z_Free(statisticsmenu.maplist); - statisticsmenu.maplist = NULL; + M_StatisticsPageClear(); return true; } + if (menucmd[pid].dpad_lr != 0) + { + M_StatisticsPageClear(); + + statisticsmenu.page += + statisticspage_max + + ( + (menucmd[pid].dpad_lr > 0) + ? 1 + : -1 + ); + + statisticsmenu.page %= statisticspage_max; + + M_StatisticsPageInit(); + + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + + return true; + } + + if (statisticsmenu.maplist == NULL) + return true; + if (M_MenuExtraPressed(pid)) { if (statisticsmenu.location > 0) diff --git a/src/menus/play-local-race-difficulty.c b/src/menus/play-local-race-difficulty.c index d9c00994e..4055cd3b8 100644 --- a/src/menus/play-local-race-difficulty.c +++ b/src/menus/play-local-race-difficulty.c @@ -7,11 +7,11 @@ menuitem_t PLAY_RaceDifficulty[] = { // For GP - {IT_STRING | IT_CVAR, "Difficulty", "Select the game difficulty", + {IT_STRING | IT_CVAR, "Difficulty", "Select the game difficulty.", "MENUI004", {.cvar = &cv_dummygpdifficulty}, 0, 0}, // Match Race - {IT_STRING | IT_CVAR, "Difficulty", "Select the game speed", + {IT_STRING | IT_CVAR, "Difficulty", "Select the game speed.", "MENUI005", {.cvar = &cv_dummykartspeed}, 0, 0}, // DISABLE THAT OPTION OUTSIDE OF MATCH RACE diff --git a/src/menus/play-local-race-time-attack.c b/src/menus/play-local-race-time-attack.c index 701ec94c4..9ae5c5199 100644 --- a/src/menus/play-local-race-time-attack.c +++ b/src/menus/play-local-race-time-attack.c @@ -59,11 +59,11 @@ boolean M_TimeAttackInputs(INT32 ch) // see ta_e menuitem_t PLAY_TimeAttack[] = { - {IT_STRING | IT_SUBMENU, "Replay...", NULL, NULL, {.submenu = &PLAY_TAReplayDef}, 0, 0}, - {IT_STRING | IT_SUBMENU, "Guest...", NULL, NULL, {.submenu = &PLAY_TAReplayGuestDef}, 0, 0}, - {IT_STRING | IT_SUBMENU, "Ghosts...", NULL, NULL, {.submenu = &PLAY_TAGhostsDef}, 0, 0}, + {IT_STRING | IT_SUBMENU, "Replay...", NULL, "MENUI006", {.submenu = &PLAY_TAReplayDef}, 0, 0}, + {IT_STRING | IT_SUBMENU, "Guest...", NULL, "MENUI006", {.submenu = &PLAY_TAReplayGuestDef}, 0, 0}, + {IT_STRING | IT_SUBMENU, "Ghosts...", NULL, "MENUI006", {.submenu = &PLAY_TAGhostsDef}, 0, 0}, {IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0}, - {IT_STRING | IT_CALL, "Start", NULL, NULL, {.routine = M_StartTimeAttack}, 0, 0}, + {IT_STRING | IT_CALL, "Start", NULL, "MENUI006", {.routine = M_StartTimeAttack}, 0, 0}, }; menu_t PLAY_TimeAttackDef = { @@ -98,15 +98,15 @@ typedef enum menuitem_t PLAY_TAReplay[] = { - {IT_STRING | IT_CALL, "Replay Best Time", NULL, NULL, {.routine = M_ReplayTimeAttack}, 0, 0}, - {IT_STRING | IT_CALL, "Replay Best Lap", NULL, NULL, {.routine = M_ReplayTimeAttack}, 0, 0}, + {IT_STRING | IT_CALL, "Replay Best Time", NULL, "MENUI006", {.routine = M_ReplayTimeAttack}, 0, 0}, + {IT_STRING | IT_CALL, "Replay Best Lap", NULL, "MENUI006", {.routine = M_ReplayTimeAttack}, 0, 0}, {IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0}, - {IT_STRING | IT_CALL, "Replay Last", NULL, NULL, {.routine = M_ReplayTimeAttack}, 0, 0}, - {IT_STRING | IT_CALL, "Replay Guest", NULL, NULL, {.routine = M_ReplayTimeAttack}, 0, 0}, - {IT_STRING | IT_ARROWS, "Replay Staff", NULL, NULL, {.routine = M_HandleStaffReplay}, 0, 0}, + {IT_STRING | IT_CALL, "Replay Last", NULL, "MENUI006", {.routine = M_ReplayTimeAttack}, 0, 0}, + {IT_STRING | IT_CALL, "Replay Guest", NULL, "MENUI006", {.routine = M_ReplayTimeAttack}, 0, 0}, + {IT_STRING | IT_ARROWS, "Replay Staff", NULL, "MENUI006", {.routine = M_HandleStaffReplay}, 0, 0}, {IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0}, - {IT_STRING | IT_SUBMENU, "Back", NULL, NULL, {.submenu = &PLAY_TimeAttackDef}, 0, 0}, + {IT_STRING | IT_SUBMENU, "Back", NULL, "MENUI006", {.submenu = &PLAY_TimeAttackDef}, 0, 0}, }; menu_t PLAY_TAReplayDef = { @@ -140,17 +140,17 @@ typedef enum menuitem_t PLAY_TAReplayGuest[] = { - {IT_HEADERTEXT|IT_HEADER, "Save as guest...", NULL, NULL, {NULL}, 0, 0}, + {IT_HEADERTEXT|IT_HEADER, "Save as guest...", NULL, "MENUI006", {NULL}, 0, 0}, - {IT_STRING | IT_CALL, "Best Time", NULL, NULL, {.routine = M_SetGuestReplay}, 0, 0}, - {IT_STRING | IT_CALL, "Best Lap", NULL, NULL, {.routine = M_SetGuestReplay}, 0, 0}, - {IT_STRING | IT_CALL, "Last Run", NULL, NULL, {.routine = M_SetGuestReplay}, 0, 0}, + {IT_STRING | IT_CALL, "Best Time", NULL, "MENUI006", {.routine = M_SetGuestReplay}, 0, 0}, + {IT_STRING | IT_CALL, "Best Lap", NULL, "MENUI006", {.routine = M_SetGuestReplay}, 0, 0}, + {IT_STRING | IT_CALL, "Last Run", NULL, "MENUI006", {.routine = M_SetGuestReplay}, 0, 0}, {IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0}, - {IT_STRING | IT_CALL, "Delete Guest", NULL, NULL, {.routine = M_SetGuestReplay}, 0, 0}, + {IT_STRING | IT_CALL, "Delete Guest", NULL, "MENUI006", {.routine = M_SetGuestReplay}, 0, 0}, {IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0}, - {IT_STRING | IT_SUBMENU, "Back", NULL, NULL, {.submenu = &PLAY_TimeAttackDef}, 0, 0}, + {IT_STRING | IT_SUBMENU, "Back", NULL, "MENUI006", {.submenu = &PLAY_TimeAttackDef}, 0, 0}, }; @@ -184,14 +184,14 @@ typedef enum menuitem_t PLAY_TAGhosts[] = { - {IT_STRING | IT_CVAR, "Best Time", NULL, NULL, {.cvar = &cv_ghost_besttime}, 0, 0}, - {IT_STRING | IT_CVAR, "Best Lap", NULL, NULL, {.cvar = &cv_ghost_bestlap}, 0, 0}, - {IT_STRING | IT_CVAR, "Last", NULL, NULL, {.cvar = &cv_ghost_last}, 0, 0}, - {IT_DISABLED, "Guest", NULL, NULL, {.cvar = &cv_ghost_guest}, 0, 0}, - {IT_DISABLED, "Staff", NULL, NULL, {.cvar = &cv_ghost_staff}, 0, 0}, + {IT_STRING | IT_CVAR, "Best Time", NULL, "MENUI006", {.cvar = &cv_ghost_besttime}, 0, 0}, + {IT_STRING | IT_CVAR, "Best Lap", NULL, "MENUI006", {.cvar = &cv_ghost_bestlap}, 0, 0}, + {IT_STRING | IT_CVAR, "Last", NULL, "MENUI006", {.cvar = &cv_ghost_last}, 0, 0}, + {IT_DISABLED, "Guest", NULL, "MENUI006", {.cvar = &cv_ghost_guest}, 0, 0}, + {IT_DISABLED, "Staff", NULL, "MENUI006", {.cvar = &cv_ghost_staff}, 0, 0}, {IT_HEADERTEXT|IT_HEADER, "", NULL, NULL, {NULL}, 0, 0}, - {IT_STRING | IT_SUBMENU, "Back", NULL, NULL, {.submenu = &PLAY_TimeAttackDef}, 0, 0}, + {IT_STRING | IT_SUBMENU, "Back", NULL, "MENUI006", {.submenu = &PLAY_TimeAttackDef}, 0, 0}, }; menu_t PLAY_TAGhostsDef = { diff --git a/src/menus/transient/level-select.c b/src/menus/transient/level-select.c index 13eed9aef..cf5343b98 100644 --- a/src/menus/transient/level-select.c +++ b/src/menus/transient/level-select.c @@ -252,6 +252,13 @@ boolean M_LevelListFromGametype(INT16 gt) PLAY_LevelSelectDef.music = \ PLAY_TimeAttackDef.music = \ currentMenu->music; + + if (gamestate == GS_MENU) + { + PLAY_CupSelectDef.menuitems[0].patch = \ + PLAY_LevelSelectDef.menuitems[0].patch = \ + currentMenu->menuitems[itemOn].patch; + } } // Obviously go to Cup Select in gametypes that have cups. diff --git a/src/p_setup.c b/src/p_setup.c index a886e1e93..ac1edeca0 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -406,7 +406,7 @@ void P_DeleteHeaderFollowers(UINT16 i) static void P_ClearSingleMapHeaderInfo(INT16 num) { mapheaderinfo[num]->lvlttl[0] = '\0'; - mapheaderinfo[num]->subttl[0] = '\0'; + mapheaderinfo[num]->menuttl[0] = '\0'; mapheaderinfo[num]->zonttl[0] = '\0'; mapheaderinfo[num]->actnum = 0; mapheaderinfo[num]->typeoflevel = 0;