diff --git a/src/k_hud.c b/src/k_hud.c index 73625dba7..5037132d8 100644 --- a/src/k_hud.c +++ b/src/k_hud.c @@ -69,7 +69,7 @@ static patch_t *kp_racefinish[6]; static patch_t *kp_positionnum[10][2][2]; // number, overlay or underlay, splitscreen static patch_t *kp_facenum[MAXPLAYERS+1]; -static patch_t *kp_facehighlight[8]; +patch_t *kp_facehighlight[8]; static patch_t *kp_nocontestminimap; static patch_t *kp_spbminimap; diff --git a/src/k_hud.h b/src/k_hud.h index 4f84f9dd3..7f8e37cf6 100644 --- a/src/k_hud.h +++ b/src/k_hud.h @@ -39,4 +39,6 @@ void K_drawKartFreePlay(void); void K_drawKartTimestamp(tic_t drawtime, INT32 TX, INT32 TY, INT16 emblemmap, UINT8 mode); void K_DrawTabRankings(INT32 x, INT32 y, playersort_t *tab, INT32 scorelines, INT32 whiteplayer, INT32 hilicol); +extern patch_t *kp_facehighlight[8]; + #endif diff --git a/src/k_menu.h b/src/k_menu.h index 6ee3e6c4d..7156e07c5 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -1091,11 +1091,15 @@ extern struct challengesmenu_s { UINT8 currentunlock; tic_t unlockanim; + UINT8 row, col, hilix, hiliy; + UINT8 *extradata; boolean pending; } challengesmenu; +menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu); +void M_Challenges(INT32 choice); void M_DrawChallenges(void); void M_ChallengesTick(void); boolean M_ChallengesInputs(INT32 ch); diff --git a/src/k_menudef.c b/src/k_menudef.c index 9e10560a3..f355a3e34 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -1496,14 +1496,14 @@ menuitem_t EXTRAS_Main[] = {IT_STRING | IT_CALL, "Addons", "Add files to customize your experience.", NULL, {.routine = M_Addons}, 0, 0}, + {IT_STRING | IT_CALL, "Challenges", "View the requirements for some of the secret content you can unlock!", + NULL, {.routine = M_Challenges}, 0, 0}, + {IT_STRING | IT_CALL, "Replay Hut", "Play the replays you've saved throughout your many races & battles!", NULL, {.routine = M_ReplayHut}, 0, 0}, {IT_STRING | IT_CALL, "Statistics", "Look back on some of your greatest achievements such as your playtime and wins!", NULL, {NULL}, 0, 0}, - - {IT_STRING | IT_TRANSTEXT, "Extras Checklist", "View the requirement for some of the secret content you can unlock!", - NULL, {NULL}, 0, 0}, }; // the extras menu essentially reuses the options menu stuff diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 784b2eaba..dcec6b86c 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -600,10 +600,10 @@ static void M_DrawMenuTooltips(void) // Used for the secrets menu, to hide yet-to-be-unlocked stuff. static const char *M_CreateSecretMenuOption(const char *str) { - static char qbuf[32]; + static char qbuf[64]; int i; - for (i = 0; i < 31; ++i) + for (i = 0; i < 63; ++i) { if (!str[i]) { @@ -616,7 +616,7 @@ static const char *M_CreateSecretMenuOption(const char *str) qbuf[i] = ' '; } - qbuf[31] = '\0'; + qbuf[63] = '\0'; return qbuf; } @@ -4471,19 +4471,21 @@ void M_DrawChallenges(void) if (challengesmenu.currentunlock < MAXUNLOCKABLES) { - V_DrawThinString(x, y, V_ALLOWLOWERCASE, unlockables[challengesmenu.currentunlock].name); - - if (challengesmenu.unlockanim >= UNLOCKTIME) - V_DrawThinString(x, y + 10, V_ALLOWLOWERCASE, "Press (A)"); + const char *str = unlockables[challengesmenu.currentunlock].name; + if (!gamedata->unlocked[challengesmenu.currentunlock]) + { + str = "???"; //M_CreateSecretMenuOption(str); + } + V_DrawThinString(x, y, V_ALLOWLOWERCASE, str); } else { - V_DrawThinString(x, y, V_ALLOWLOWERCASE, va("pending = %c", challengesmenu.pending ? 'T' : 'F')); - - if (challengesmenu.unlockanim >= UNLOCKTIME) - V_DrawThinString(x, y + 10, V_ALLOWLOWERCASE, "Press (B)"); + V_DrawThinString(x, y, V_ALLOWLOWERCASE, "---"); } + if (challengesmenu.unlockanim >= UNLOCKTIME) + V_DrawThinString(x, y + 10, V_ALLOWLOWERCASE, va("Press (%c)", challengesmenu.pending ? 'A' : 'B')); + x = currentMenu->x; y = currentMenu->y; @@ -4501,7 +4503,7 @@ void M_DrawChallenges(void) y += 16; id = (i * CHALLENGEGRIDHEIGHT) + j; - if (challengesmenu.extradata[id] == CHE_DONTDRAW) + if (challengesmenu.extradata[id] & CHE_DONTDRAW) { continue; } @@ -4512,7 +4514,8 @@ void M_DrawChallenges(void) if (num >= MAXUNLOCKABLES) { V_DrawFill(x, y, 16, 16, 27); - continue; + ref = NULL; + goto drawborder; } // Okay, this is what we want to draw. @@ -4526,7 +4529,7 @@ void M_DrawChallenges(void) V_DrawFill(x, y, 16*work, 16*work, ((challengesmenu.extradata[id] == CHE_HINT) ? 130 : 12) + ((i & work) != (j & work) ? 0 : 2) + (work-1)); // funny checkerboard pattern - continue; + goto drawborder; } switch (ref->type) @@ -4551,6 +4554,19 @@ void M_DrawChallenges(void) V_DrawString(x, y, V_ALLOWLOWERCASE, va("%c", ref->name[0])); break; } + +drawborder: + if (i != challengesmenu.hilix) + continue; + if (j != challengesmenu.hiliy) + continue; + + V_DrawFixedPatch( + x*FRACUNIT, y*FRACUNIT, + ((ref != NULL && ref->majorunlock) ? FRACUNIT*2 : FRACUNIT), + 0, kp_facehighlight[(challengesmenu.ticker / 4) % 8], + NULL + ); } x += 16; } diff --git a/src/k_menufunc.c b/src/k_menufunc.c index abba32891..f8e80bb15 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -775,22 +775,6 @@ static boolean M_PrevOpt(void) return true; } - static menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu) -{ - M_UpdateUnlockablesAndExtraEmblems(false); - - if (M_GetNextAchievedUnlock(false) < MAXUNLOCKABLES) - { - MISC_ChallengesDef.prevMenu = desiredmenu; - challengesmenu.pending = true; - challengesmenu.currentunlock = MAXUNLOCKABLES; - M_PopulateChallengeGrid(); - return &MISC_ChallengesDef; - } - - return desiredmenu; -} - // // M_Responder // @@ -6826,19 +6810,122 @@ void M_Manual(INT32 choice) struct challengesmenu_s challengesmenu; +menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu) +{ + M_UpdateUnlockablesAndExtraEmblems(false); + + if (M_GetNextAchievedUnlock(false) < MAXUNLOCKABLES) + { + challengesmenu.pending = true; + MISC_ChallengesDef.prevMenu = desiredmenu; + } + + if (challengesmenu.pending || desiredmenu == NULL) + { + challengesmenu.currentunlock = MAXUNLOCKABLES; + M_PopulateChallengeGrid(); + return &MISC_ChallengesDef; + } + + return desiredmenu; +} + +void M_Challenges(INT32 choice) +{ + UINT8 i; + (void)choice; + + M_InterruptMenuWithChallenges(NULL); + MISC_ChallengesDef.prevMenu = currentMenu; + + if (gamedata->challengegrid) + { + UINT8 selection[MAXUNLOCKABLES]; + UINT8 numunlocks = 0; + + // Get a random available unlockable. + for (i = 0; i < MAXUNLOCKABLES; i++) + { + if (!unlockables[i].conditionset) + { + continue; + } + + if (!gamedata->unlocked[i]) + { + continue; + } + + selection[numunlocks++] = i; + } + + if (!numunlocks) + { + // ...OK, get a random unlockable. + for (i = 0; i < MAXUNLOCKABLES; i++) + { + if (!unlockables[i].conditionset) + { + continue; + } + + selection[numunlocks++] = i; + } + } + + challengesmenu.currentunlock = selection[M_RandomKey(numunlocks)]; + + for (i = 0; i < (CHALLENGEGRIDHEIGHT * gamedata->challengegridwidth); i++) + { + if (gamedata->challengegrid[i] != challengesmenu.currentunlock) + { + continue; + } + + challengesmenu.col = challengesmenu.hilix = i/CHALLENGEGRIDHEIGHT; + challengesmenu.row = challengesmenu.hiliy = i%CHALLENGEGRIDHEIGHT; + break; + } + } + + M_SetupNextMenu(&MISC_ChallengesDef, false); +} + void M_ChallengesTick(void) { + UINT8 i, newunlock = MAXUNLOCKABLES; + challengesmenu.ticker++; if (challengesmenu.pending && challengesmenu.currentunlock >= MAXUNLOCKABLES) { - if ((challengesmenu.currentunlock = M_GetNextAchievedUnlock(true)) >= MAXUNLOCKABLES) + if ((newunlock = M_GetNextAchievedUnlock(true)) < MAXUNLOCKABLES) + { + challengesmenu.currentunlock = newunlock; + challengesmenu.unlockanim = 0; + + if (gamedata->challengegrid) + { + for (i = 0; i < (CHALLENGEGRIDHEIGHT * gamedata->challengegridwidth); i++) + { + if (gamedata->challengegrid[i] != challengesmenu.currentunlock) + { + continue; + } + + challengesmenu.col = challengesmenu.hilix = i/CHALLENGEGRIDHEIGHT; + challengesmenu.row = challengesmenu.hiliy = i%CHALLENGEGRIDHEIGHT; + break; + } + } + } + else { challengesmenu.pending = false; G_SaveGameData(); } Z_Free(challengesmenu.extradata); - challengesmenu.extradata = M_ChallengeGridExtraData(); + challengesmenu.extradata = NULL; } else if (challengesmenu.unlockanim >= UNLOCKTIME) { @@ -6848,14 +6935,25 @@ void M_ChallengesTick(void) { challengesmenu.unlockanim++; } + + if (challengesmenu.extradata == NULL) + { + challengesmenu.extradata = M_ChallengeGridExtraData(); + } } boolean M_ChallengesInputs(INT32 ch) { const UINT8 pid = 0; - boolean start = M_MenuButtonPressed(pid, MBT_START); + UINT8 i; + const boolean start = M_MenuButtonPressed(pid, MBT_START); + const boolean move = (menucmd[pid].dpad_ud || menucmd[pid].dpad_lr); (void) ch; + if (!challengesmenu.extradata) + { + ; + } if (challengesmenu.unlockanim < UNLOCKTIME) { ; @@ -6870,6 +6968,21 @@ boolean M_ChallengesInputs(INT32 ch) Z_Free(challengesmenu.extradata); challengesmenu.extradata = M_ChallengeGridExtraData(); challengesmenu.unlockanim = 0; + + if (challengesmenu.currentunlock != MAXUNLOCKABLES) + { + for (i = 0; i < (CHALLENGEGRIDHEIGHT * gamedata->challengegridwidth); i++) + { + if (gamedata->challengegrid[i] != challengesmenu.currentunlock) + { + continue; + } + + challengesmenu.col = challengesmenu.hilix = i/CHALLENGEGRIDHEIGHT; + challengesmenu.row = challengesmenu.hiliy = i%CHALLENGEGRIDHEIGHT; + break; + } + } return true; } #endif @@ -6878,19 +6991,142 @@ boolean M_ChallengesInputs(INT32 ch) if ((M_MenuConfirmPressed(pid) || start)) { challengesmenu.currentunlock = MAXUNLOCKABLES; - challengesmenu.unlockanim = 0; } return true; } - else if (M_MenuBackPressed(pid) || start) + else { - M_GoBack(0); - M_SetMenuDelay(pid); + if (M_MenuBackPressed(pid) || start) + { + M_GoBack(0); + M_SetMenuDelay(pid); - Z_Free(challengesmenu.extradata); - challengesmenu.extradata = NULL; + Z_Free(challengesmenu.extradata); + challengesmenu.extradata = NULL; - return true; + return true; + } + + // Determine movement around the grid + if (move) + { + if (menucmd[pid].dpad_ud > 0) + { + i = 2; + while (i > 0) + { + if (challengesmenu.row < CHALLENGEGRIDHEIGHT-1) + { + challengesmenu.row++; + } + else + { + challengesmenu.row = 0; + } + if (!(challengesmenu.extradata[ + (challengesmenu.col * CHALLENGEGRIDHEIGHT) + + challengesmenu.row] + & CHE_CONNECTEDUP)) + { + break; + } + i--; + } + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } + else if (menucmd[pid].dpad_ud < 0) + { + i = (challengesmenu.extradata[ + (challengesmenu.col * CHALLENGEGRIDHEIGHT) + + challengesmenu.row] + & CHE_CONNECTEDUP) ? 2 : 1; + while (i > 0) + { + if (challengesmenu.row > 0) + { + challengesmenu.row--; + } + else + { + challengesmenu.row = CHALLENGEGRIDHEIGHT-1; + } + i--; + } + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } + + if (menucmd[pid].dpad_lr > 0) + { + i = 2; + while (i > 0) + { + if (challengesmenu.col < gamedata->challengegridwidth-1) + { + challengesmenu.col++; + } + else + { + challengesmenu.col = 0; + } + if (!(challengesmenu.extradata[ + (challengesmenu.col * CHALLENGEGRIDHEIGHT) + + challengesmenu.row] + & CHE_CONNECTEDLEFT)) + { + break; + } + i--; + } + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } + else if (menucmd[pid].dpad_lr < 0) + { + i = (challengesmenu.extradata[ + (challengesmenu.col * CHALLENGEGRIDHEIGHT) + + challengesmenu.row] + & CHE_CONNECTEDLEFT) ? 2 : 1; + while (i > 0) + { + if (challengesmenu.col > 0) + { + challengesmenu.col--; + } + else + { + challengesmenu.col = gamedata->challengegridwidth-1; + } + i--; + } + S_StartSound(NULL, sfx_s3k5b); + M_SetMenuDelay(pid); + } + + // After movement has been determined, figure out the current selection. + i = (challengesmenu.col * CHALLENGEGRIDHEIGHT) + challengesmenu.row; + challengesmenu.currentunlock = (gamedata->challengegrid[i]); + + challengesmenu.hilix = challengesmenu.col; + challengesmenu.hiliy = challengesmenu.row; + if (challengesmenu.hiliy > 0 && (challengesmenu.extradata[i] & CHE_CONNECTEDUP)) + { + challengesmenu.hiliy--; + } + + if ((challengesmenu.extradata[i] & CHE_CONNECTEDLEFT)) + { + if (challengesmenu.hilix > 0) + { + challengesmenu.hilix--; + } + else + { + challengesmenu.hilix = gamedata->challengegridwidth-1; + } + } + } } return true; diff --git a/src/m_cond.c b/src/m_cond.c index edad6b4c5..3f9e6e86c 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -278,12 +278,13 @@ UINT8 *M_ChallengeGridExtraData(void) work = gamedata->challengegrid[tempid]; if (work == num) { - extradata[id] = CHE_DONTDRAW; + extradata[id] = CHE_CONNECTEDUP; // Get the id to write extra hint data to. // This check is safe because extradata's order of population - if (extradata[tempid] == CHE_DONTDRAW) + if (extradata[tempid] & CHE_CONNECTEDLEFT) { + extradata[id] |= CHE_CONNECTEDLEFT; //CONS_Printf(" %d - %d above %d is invalid, check to left\n", num, tempid, id); if (i > 0) { @@ -333,7 +334,7 @@ UINT8 *M_ChallengeGridExtraData(void) { extradata[tempid] = CHE_HINT; } - extradata[id] = CHE_DONTDRAW; + extradata[id] = CHE_CONNECTEDLEFT; id = tempid; } /*else diff --git a/src/m_cond.h b/src/m_cond.h index 1447e2206..5f3500813 100644 --- a/src/m_cond.h +++ b/src/m_cond.h @@ -176,9 +176,11 @@ extern UINT32 unlocktriggers; void M_NewGameDataStruct(void); void M_PopulateChallengeGrid(void); UINT8 *M_ChallengeGridExtraData(void); -#define CHE_NONE 0 -#define CHE_HINT 1 -#define CHE_DONTDRAW 2 +#define CHE_NONE 0 +#define CHE_HINT 1 +#define CHE_CONNECTEDLEFT 2 +#define CHE_CONNECTEDUP 4 +#define CHE_DONTDRAW (CHE_CONNECTEDLEFT|CHE_CONNECTEDUP) // Condition set setup void M_AddRawCondition(UINT8 set, UINT8 id, conditiontype_t c, INT32 r, INT16 x1, INT16 x2);