From 7c7bfe605442b21610680e59049120bacbaab7cb Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Aug 2025 20:38:18 +0100 Subject: [PATCH 01/19] Adjust how major and minor challenges affect percieved challenge completion - The Chao Medal in the top right corner will be completely filled if you've unlocked everything, even if that was via skips - You basically never saw the silver Chao Medal. Major challenges *are* designed to be skipped if you don't want to, only 101% should be gruelling (complementary) - The Scroll Bar at the top will darken pixels of complete columns ever so slightly even if minor challenges aren't yet achieved, to make it easier to find conditions to go back and fulfil if you're truly hunting for everything --- src/k_menudraw.c | 11 +++-------- src/menus/extras-challenges.c | 13 ++++++++++++- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 94357b020..80a5a20b6 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -7914,11 +7914,6 @@ static void M_DrawChallengeScrollBar(UINT8 *flashmap) V_DrawFill(barx + hilix, bary-1, hiliw, 1, 0); V_DrawFill(barx + hilix, bary+barh, hiliw, 1, 0); - INT32 mindiscouragement = 2; // skipping major unlocks is just a LITTLE cringe - if (challengesmenu.unlockcount[CMC_PERCENT] == 100 - && challengesmenu.unlockcount[CMC_MAJORSKIPPED] == 0) - mindiscouragement = 1; // so someone looking for 101% isn't hunting forever - // unbounded so that we can do the last remaining completionamount draw nextstep = numincolumn = completionamount = skiplevel = 0; for (i = 0; ; i++) @@ -7932,7 +7927,7 @@ static void M_DrawChallengeScrollBar(UINT8 *flashmap) if (completionamount >= numincolumn) { // If any have been skipped, we subtract a little for awareness... - completionamount = (skiplevel >= mindiscouragement) ? 9 : 10; + completionamount = skiplevel ? 9 : 10; } else { @@ -7978,9 +7973,9 @@ static void M_DrawChallengeScrollBar(UINT8 *flashmap) unlockable_t *ref = &unlockables[gamedata->challengegrid[i]]; - if (skiplevel < 2 && M_Achieved(ref->conditionset - 1) == false) + if (!skiplevel && M_Achieved(ref->conditionset - 1) == false) { - skiplevel = ref->majorunlock ? 2 : 1; + skiplevel = 1; } } diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index 64b07d8a8..c00d4e75b 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -82,6 +82,10 @@ static void M_UpdateChallengeGridVisuals(void) challengesmenu.unlockcount[CMC_UNLOCKED] = 0; challengesmenu.unlockcount[CMC_TOTAL] = 0; + challengesmenu.unlockcount[CMC_KEYED] = 0; + challengesmenu.unlockcount[CMC_MAJORSKIPPED] = 0; + +//#define MAJORDISTINCTION -- The "basic" medal is basically never seen because Major challenges are usually completed last before 101%. Correct that with this for (i = 0; i < MAXUNLOCKABLES; i++) { @@ -106,12 +110,14 @@ static void M_UpdateChallengeGridVisuals(void) challengesmenu.unlockcount[CMC_KEYED]++; +#ifdef MAJORDISTINCTION if (unlockables[i].majorunlock == false) { continue; } challengesmenu.unlockcount[CMC_MAJORSKIPPED]++; +#endif } challengesmenu.unlockcount[CMC_PERCENT] = @@ -125,7 +131,9 @@ static void M_UpdateChallengeGridVisuals(void) challengesmenu.unlockcount[CMC_MEDALFILLED] = (medalheight * ( challengesmenu.unlockcount[CMC_UNLOCKED] +#ifdef MAJORDISTINCTION - challengesmenu.unlockcount[CMC_MAJORSKIPPED] +#endif )) / challengesmenu.unlockcount[CMC_TOTAL]; if (challengesmenu.unlockcount[CMC_PERCENT] == 100) @@ -135,7 +143,10 @@ static void M_UpdateChallengeGridVisuals(void) challengesmenu.unlockcount[CMC_MEDALID] = 2; challengesmenu.unlockcount[CMC_PERCENT]++; // 101% } - else if (challengesmenu.unlockcount[CMC_MAJORSKIPPED] == 0) + else +#ifdef MAJORDISTINCTION + if (challengesmenu.unlockcount[CMC_MAJORSKIPPED] == 0) +#endif { challengesmenu.unlockcount[CMC_MEDALID] = 1; } From 248541833a21f0aa6e9edaf7229f0f6aa76333d1 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Aug 2025 21:42:54 +0100 Subject: [PATCH 02/19] extras-challenges: Clean up open and close - Dedicated function for cleaning up Challenges Menu memory - Adjust the required function slate to open Challenges menu without malformed data - Permits it to be used with restoreMenu, where it would silently infinitely loop before --- src/menus/extras-challenges.c | 45 ++++++++++++++++++++--------------- 1 file changed, 26 insertions(+), 19 deletions(-) diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index c00d4e75b..d714dd342 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -375,10 +375,13 @@ menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu) if ((challengesmenu.pending = (newunlock != MAXUNLOCKABLES))) { Music_StopAll(); - MISC_ChallengesDef.prevMenu = desiredmenu; + if (desiredmenu && desiredmenu != &MISC_ChallengesDef) + { + MISC_ChallengesDef.prevMenu = desiredmenu; + } } - if (challengesmenu.pending || desiredmenu == NULL) + if (challengesmenu.pending || desiredmenu == &MISC_ChallengesDef) { static boolean firstopen = true; @@ -413,9 +416,14 @@ menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu) if (challengesmenu.pending) M_ChallengesAutoFocus(newunlock, true); - else if (newunlock >= MAXUNLOCKABLES && gamedata->pendingkeyrounds > 0 - && (gamedata->chaokeys < GDMAX_CHAOKEYS)) - challengesmenu.chaokeyadd = true; + else + { + if (newunlock >= MAXUNLOCKABLES && gamedata->pendingkeyrounds > 0 + && (gamedata->chaokeys < GDMAX_CHAOKEYS)) + challengesmenu.chaokeyadd = true; + + M_ChallengesAutoFocus(UINT16_MAX, true); + } M_CacheChallengeTiles(); @@ -429,17 +437,23 @@ void M_Challenges(INT32 choice) { (void)choice; - M_InterruptMenuWithChallenges(NULL); + M_InterruptMenuWithChallenges(&MISC_ChallengesDef); MISC_ChallengesDef.prevMenu = currentMenu; - if (gamedata->challengegrid != NULL && !challengesmenu.pending) - { - M_ChallengesAutoFocus(UINT16_MAX, true); - } - M_SetupNextMenu(&MISC_ChallengesDef, false); } +static void M_CloseChallenges(void) +{ + Music_Stop("challenge_altmusic"); + + Z_Free(challengesmenu.extradata); + challengesmenu.extradata = NULL; + + Z_Free(challengesmenu.unlockcondition); + challengesmenu.unlockcondition = NULL; +} + boolean M_CanKeyHiliTile(void) { // No tile data? @@ -954,19 +968,12 @@ boolean M_ChallengesInputs(INT32 ch) { if (M_MenuBackPressed(pid) || start) { - Music_Stop("challenge_altmusic"); - currentMenu->prevMenu = M_SpecificMenuRestore(currentMenu->prevMenu); M_GoBack(0); M_SetMenuDelay(pid); - Z_Free(challengesmenu.extradata); - challengesmenu.extradata = NULL; - - if (challengesmenu.unlockcondition) - Z_Free(challengesmenu.unlockcondition); - challengesmenu.unlockcondition = NULL; + M_CloseChallenges(); return true; } From 9074eaf3c8c95af33a5bb47d898b6bd6a93a300c Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Aug 2025 21:48:34 +0100 Subject: [PATCH 03/19] Add the ability to play Tutorial courses from the Challenges menu - Only works in parties of 1 - Currently intentionally disabled for other types of Match Race, as they tend to have too many options and I don't want to risk infinite menu loops --- src/k_menudraw.c | 47 +++++++++++++++++++++++------------ src/menus/extras-challenges.c | 45 ++++++++++++++++++++++++++++++++- 2 files changed, 75 insertions(+), 17 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 80a5a20b6..646f258fa 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -7346,6 +7346,8 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) const char *gtname = "Find your prize..."; UINT16 mapnum = M_UnlockableMapNum(ref); + y = BASEVIDHEIGHT-(9+3); + if (mapnum >= nummapheaders || mapheaderinfo[mapnum] == NULL || mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU) @@ -7389,7 +7391,17 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) guessgt = GT_SPECIAL; } - if (guessgt == GT_SPECIAL && !M_SecretUnlocked(SECRET_SPECIALATTACK, true)) + if (setup_numplayers <= 1 && guessgt == GT_TUTORIAL) + { + // Only for 1p + K_DrawGameControl( + 1, y, 0, + " Play Tutorial", + 0, TINY_FONT, 0 + ); + gtname = NULL; + } + else if (guessgt == GT_SPECIAL && !M_SecretUnlocked(SECRET_SPECIALATTACK, true)) { gtname = "???"; } @@ -7409,7 +7421,10 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) NULL); } - V_DrawThinString(1, BASEVIDHEIGHT-(9+3), 0, gtname); + if (gtname) + { + V_DrawThinString(1, y, 0, gtname); + } break; } @@ -7589,13 +7604,13 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) pushed = strcmp(song, mapheaderinfo[map]->encoremusname[musicid]) == 0; } - if (!pushed) - K_DrawGameControl(x, y, 0, " E Side", 0, TINY_FONT, 0); - else - K_DrawGameControl(x, y, 0, " E Side", 0, TINY_FONT, 0); - // K_drawButton(x&FRACUNIT, y*FRACUNIT, 0, kp_button_l, pushed); - // x += SHORT(kp_button_l[0]->width); - // V_DrawThinString(x, y + 1, (pushed ? V_GRAYMAP : highlightflags), "E Side"); + K_DrawGameControl( + x, y, 0, + (!pushed) + ? " E Side" + : " E Side", + 0, TINY_FONT, 0 + ); x = 8; y -= 10; @@ -7614,13 +7629,13 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) pushed = strcmp(song, mapheaderinfo[map]->musname[musicid]) == 0; } - if (!pushed) - K_DrawGameControl(x, y, 0, " Play CD", 0, TINY_FONT, 0); - else - K_DrawGameControl(x, y, 0, " Play CD", 0, TINY_FONT, 0); - // K_drawButton(x*FRACUNIT, y*FRACUNIT, 0, kp_button_a[1], pushed); - // x += SHORT(kp_button_a[1][0]->width); - // V_DrawThinString(x, y + 1, (pushed ? V_GRAYMAP : highlightflags), "Play CD"); + K_DrawGameControl( + x, y, 0, + (!pushed) + ? " Play CD" + : " Play CD", + 0, TINY_FONT, 0 + ); } } default: diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index d714dd342..27d20606b 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -1155,8 +1155,51 @@ boolean M_ChallengesInputs(INT32 ch) if (challengesmenu.currentunlock < MAXUNLOCKABLES && gamedata->unlocked[challengesmenu.currentunlock]) { + unlockable_t *ref = &unlockables[challengesmenu.currentunlock]; switch (unlockables[challengesmenu.currentunlock].type) { + case SECRET_MAP: + { + // Only for 1p + if (setup_numplayers <= 1 && M_MenuConfirmPressed(pid)) + { + // Map exists... + UINT16 mapnum = M_UnlockableMapNum(ref); + if (mapnum < nummapheaders && mapheaderinfo[mapnum]) + { + // is tutorial... + INT32 guessgt = G_GuessGametypeByTOL(mapheaderinfo[mapnum]->typeoflevel); + if (guessgt == GT_TUTORIAL) + { + M_SetMenuDelay(pid); + + multiplayer = true; + + restoreMenu = currentMenu; + restorelevellist = levellist; + + // mild hack + levellist.newgametype = guessgt; + levellist.netgame = false; + M_MenuToLevelPreamble(0, false); + + D_MapChange( + mapnum+1, + guessgt, + false, + true, + 1, + false, + false + ); + + M_CloseChallenges(); + M_ClearMenus(true); + } + } + } + break; + } case SECRET_ALTTITLE: { if (M_MenuConfirmPressed(pid)) @@ -1185,7 +1228,7 @@ boolean M_ChallengesInputs(INT32 ch) { const char *trymusname = NULL; - UINT16 map = M_UnlockableMapNum(&unlockables[challengesmenu.currentunlock]); + UINT16 map = M_UnlockableMapNum(ref); if (map >= nummapheaders || !mapheaderinfo[map]) { From 7b6b26fe30f3e843d3f2da61762c624dea344585 Mon Sep 17 00:00:00 2001 From: toaster Date: Wed, 13 Aug 2025 22:22:40 +0100 Subject: [PATCH 04/19] Add skincolor Profile set Qol too --- src/k_menudraw.c | 17 +++++++++++++++++ src/menus/extras-challenges.c | 25 +++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 646f258fa..e69271dfd 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -7264,6 +7264,23 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) // Draw reference for character bathed in coloured slime M_DrawCharacterSprite(x, y, skin, SPR2_STIN, 7, 0, 0, colormap); + + if (setup_numplayers <= 1 && cv_lastprofile[0].value != PROFILE_GUEST) + { + profile_t *pr = PR_GetProfile(cv_lastprofile[0].value); + + if (pr) + { + K_DrawGameControl( + 8, BASEVIDHEIGHT-16, 0, + (pr->color != colorid) + ? " Set on Profile" + : " Set on Profile", + 0, TINY_FONT, 0 + ); + } + } + break; } case SECRET_CUP: diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index 27d20606b..59f90f780 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -1211,6 +1211,31 @@ boolean M_ChallengesInputs(INT32 ch) } break; } + case SECRET_COLOR: + { + if (setup_numplayers <= 1 && cv_lastprofile[0].value != PROFILE_GUEST && M_MenuConfirmPressed(pid)) + { + INT32 colorid = M_UnlockableColorNum(ref); + if (colorid != SKINCOLOR_NONE) + { + profile_t *pr = PR_GetProfile(cv_lastprofile[0].value); + + if (pr && pr->color != colorid) + { + pr->color = colorid; + CV_SetValue(&cv_playercolor[0], colorid); + if (setup_numplayers) + { + G_SetPlayerGamepadIndicatorToPlayerColor(0); + } + + S_StartSound(NULL, sfx_s3k63); + M_SetMenuDelay(pid); + } + } + } + break; + } case SECRET_ALTMUSIC: { UINT8 trymus = 0, musicid = MAXMUSNAMES; From 4fcdb3c5fe49240f2cc1aaa7d512f07fcab1a92a Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 14 Aug 2025 16:35:39 +0100 Subject: [PATCH 05/19] Challenges menu QoL: to set Driver and Follower Also adjusts coordinates of other Challenges UI previews with control inputs to match --- src/k_menudraw.c | 61 ++++++++++++++++++++++++++++------- src/menus/extras-challenges.c | 42 ++++++++++++++++++++++++ 2 files changed, 91 insertions(+), 12 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index e69271dfd..ad4403166 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -7207,6 +7207,25 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) colormap = R_GetTranslationColormap(skin, skins[skin]->prefcolor, GTC_MENUCACHE); M_DrawCharacterSprite(x, y, skin, SPR2_STIN, 7, 0, 0, colormap); + y = (BASEVIDHEIGHT-14); + + if (setup_numplayers <= 1 && cv_lastprofile[0].value != PROFILE_GUEST) + { + profile_t *pr = PR_GetProfile(cv_lastprofile[0].value); + + if (pr) + { + K_DrawGameControl( + 4, y, 0, + strcmp(pr->skinname, skins[skin]->name) + ? " Set on Profile" + : " Set on Profile", + 0, TINY_FONT, 0 + ); + y -= 14; + } + } + for (i = 0; i < skin; i++) { if (!R_SkinUsable(-1, i, false)) @@ -7220,7 +7239,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) break; } - M_DrawCharacterIconAndEngine(4, BASEVIDHEIGHT-(4+16), i, colormap, skin); + M_DrawCharacterIconAndEngine(4, y-6, i, colormap, skin); } break; } @@ -7242,9 +7261,28 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) colormap = R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE); M_DrawFollowerSprite(x - 16, y, fskin, false, 0, colormap, NULL); + y = (BASEVIDHEIGHT-14); + + if (setup_numplayers <= 1 && cv_lastprofile[0].value != PROFILE_GUEST) + { + profile_t *pr = PR_GetProfile(cv_lastprofile[0].value); + + if (pr) + { + K_DrawGameControl( + 4, y, 0, + strcmp(pr->follower, followers[fskin].name) + ? " Set on Profile" + : " Set on Profile", + 0, TINY_FONT, 0 + ); + y -= 14; + } + } + if (followers[fskin].category < numfollowercategories) { - V_DrawFixedPatch(4*FRACUNIT, (BASEVIDHEIGHT-(4+16))*FRACUNIT, + V_DrawFixedPatch(4*FRACUNIT, (y - 6)*FRACUNIT, FRACUNIT, 0, W_CachePatchName(followercategories[followers[fskin].category].icon, PU_CACHE), NULL); @@ -7272,7 +7310,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) if (pr) { K_DrawGameControl( - 8, BASEVIDHEIGHT-16, 0, + 4, (BASEVIDHEIGHT-14), 0, (pr->color != colorid) ? " Set on Profile" : " Set on Profile", @@ -7363,7 +7401,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) const char *gtname = "Find your prize..."; UINT16 mapnum = M_UnlockableMapNum(ref); - y = BASEVIDHEIGHT-(9+3); + y = (BASEVIDHEIGHT-14); if (mapnum >= nummapheaders || mapheaderinfo[mapnum] == NULL @@ -7412,7 +7450,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) { // Only for 1p K_DrawGameControl( - 1, y, 0, + 4, y, 0, " Play Tutorial", 0, TINY_FONT, 0 ); @@ -7440,7 +7478,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) if (gtname) { - V_DrawThinString(1, y, 0, gtname); + V_DrawThinString(4, y, 0, gtname); } break; @@ -7551,8 +7589,8 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) } case SECRET_ALTTITLE: { - x = 8; - y = BASEVIDHEIGHT-16; + x = 4; + y = BASEVIDHEIGHT-14; V_DrawGamemodeString(x, y - 33, 0, R_GetTranslationColormap(TC_RAINBOW, SKINCOLOR_PLAGUE, GTC_MENUCACHE), M_UseAlternateTitleScreen() ? "On" : "Off"); K_DrawGameControl(x, y, 0, " Toggle", 0, TINY_FONT, 0); @@ -7605,8 +7643,8 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) V_DrawFixedPatch(x*FRACUNIT, (y+2)*FRACUNIT, FRACUNIT/2, addflags, patch, NULL); } - x = 8; - y = BASEVIDHEIGHT-16; + x = 4; + y = (BASEVIDHEIGHT-14); const boolean thismusplaying = Music_Playing("challenge_altmusic"); boolean pushed = false; @@ -7629,8 +7667,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) 0, TINY_FONT, 0 ); - x = 8; - y -= 10; + y -= 14; } if (musicid < mapheaderinfo[map]->musname_size) diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index 59f90f780..a1f83b756 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -1211,6 +1211,48 @@ boolean M_ChallengesInputs(INT32 ch) } break; } + case SECRET_SKIN: + { + if (setup_numplayers <= 1 && cv_lastprofile[0].value != PROFILE_GUEST && M_MenuConfirmPressed(pid)) + { + INT32 skin = M_UnlockableSkinNum(ref); + if (skin != -1) + { + profile_t *pr = PR_GetProfile(cv_lastprofile[0].value); + + if (pr && strcmp(pr->skinname, skins[skin]->name)) + { + strcpy(pr->skinname, skins[skin]->name); + CV_Set(&cv_skin[0], skins[skin]->name); + + S_StartSound(NULL, sfx_s3k63); + M_SetMenuDelay(pid); + } + } + } + break; + } + case SECRET_FOLLOWER: + { + if (setup_numplayers <= 1 && cv_lastprofile[0].value != PROFILE_GUEST && M_MenuConfirmPressed(pid)) + { + INT32 fskin = M_UnlockableFollowerNum(ref); + if (fskin != -1) + { + profile_t *pr = PR_GetProfile(cv_lastprofile[0].value); + + if (pr && strcmp(pr->follower, followers[fskin].name)) + { + strcpy(pr->follower, followers[fskin].name); + CV_Set(&cv_follower[0], followers[fskin].name); + + S_StartSound(NULL, sfx_s3k63); + M_SetMenuDelay(pid); + } + } + } + break; + } case SECRET_COLOR: { if (setup_numplayers <= 1 && cv_lastprofile[0].value != PROFILE_GUEST && M_MenuConfirmPressed(pid)) From bc75a1702e57297bfebd97f87c3660c74c575918 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 14 Aug 2025 17:48:26 +0100 Subject: [PATCH 06/19] Extra polish to SECRET_ALTMUSIC on Challenges menu If playing a given altmusic unlock... - If selected, spin the disc - If not selected, constantly flip the relevant tile on the board - Either way, flash the relevant pixel in the scroll bar --- src/k_menu.h | 1 + src/k_menudraw.c | 111 +++++++++++++++++++++------------- src/menus/extras-challenges.c | 19 +++--- 3 files changed, 82 insertions(+), 49 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 4847a988f..38a7edb07 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -1461,6 +1461,7 @@ extern struct challengesmenu_s { UINT16 tutorialfound; boolean requestflip; + UINT16 nowplayingtile; UINT16 unlockcount[CMC_MAX]; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index ad4403166..00bc9d8f3 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -7622,48 +7622,78 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) return; } - spritedef_t *sprdef = &sprites[SPR_ALTM]; - spriteframe_t *sprframe; - patch_t *patch; - UINT32 addflags = 0; + const char *tune = "challenge_altmusic"; - x -= 10; - y += 15; + SINT8 pushed = 0; + const boolean epossible = (M_SecretUnlocked(SECRET_ENCORE, true) + && musicid < mapheaderinfo[map]->encoremusname_size); - if (sprdef->numframes) + if (challengesmenu.nowplayingtile == ((challengesmenu.hilix * CHALLENGEGRIDHEIGHT) + challengesmenu.hiliy) + && Music_Playing(tune)) { - sprframe = &sprdef->spriteframes[0]; - patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE); + const char *song = Music_Song(tune); + if (epossible + && strcmp(song, mapheaderinfo[map]->encoremusname[musicid]) == 0) + pushed = -1; + else if (musicid < mapheaderinfo[map]->musname_size + && strcmp(song, mapheaderinfo[map]->musname[musicid]) == 0) + pushed = 1; + } - if (sprframe->flip & 1) // Only for first sprite + // Draw CD + { + spritedef_t *sprdef = &sprites[SPR_ALTM]; + spriteframe_t *sprframe; + patch_t *patch = NULL; + UINT32 addflags = 0; + + x -= 10; + y += 15; + + if (sprdef->numframes) { - addflags ^= V_FLIP; // This sprite is left/right flipped! - } +#ifdef ROTSPRITE + spriteinfo_t *sprinfo = &spriteinfo[SPR_ALTM]; + INT32 rollangle = 0; + if (pushed != 0) + { + rollangle = (Music_Elapsed(tune) % (ROTANGLES/2))*2; + if (pushed > 0) + rollangle = ((ROTANGLES-1) - rollangle); + } +#endif - V_DrawFixedPatch(x*FRACUNIT, (y+2)*FRACUNIT, FRACUNIT/2, addflags, patch, NULL); + sprframe = &sprdef->spriteframes[0]; + +#ifdef ROTSPRITE + if (rollangle) + { + patch = Patch_GetRotatedSprite(sprframe, 0, 0, (sprframe->flip & 1), false, sprinfo, rollangle); + } +#endif + if (!patch) + { + patch = W_CachePatchNum(sprframe->lumppat[0], PU_CACHE); + if (sprframe->flip & 1) // Only for first sprite + { + addflags ^= V_FLIP; // This sprite is left/right flipped! + } + } + + V_DrawFixedPatch(x*FRACUNIT, (y+2)*FRACUNIT, FRACUNIT/2, addflags, patch, NULL); + } } x = 4; y = (BASEVIDHEIGHT-14); - const boolean thismusplaying = Music_Playing("challenge_altmusic"); - boolean pushed = false; - const char *song = NULL; - - if (M_SecretUnlocked(SECRET_ENCORE, true) - && musicid < mapheaderinfo[map]->encoremusname_size) + if (epossible) { - if (thismusplaying) - { - song = Music_Song("challenge_altmusic"); - pushed = strcmp(song, mapheaderinfo[map]->encoremusname[musicid]) == 0; - } - K_DrawGameControl( x, y, 0, - (!pushed) - ? " E Side" - : " E Side", + (pushed < 0) + ? " E Stop" + : " E Side", 0, TINY_FONT, 0 ); @@ -7672,22 +7702,11 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) if (musicid < mapheaderinfo[map]->musname_size) { - if (pushed || !thismusplaying) - { - pushed = false; - } - else - { - if (!song) - song = Music_Song("challenge_altmusic"); - pushed = strcmp(song, mapheaderinfo[map]->musname[musicid]) == 0; - } - K_DrawGameControl( x, y, 0, - (!pushed) - ? " Play CD" - : " Play CD", + (pushed > 0) + ? " Stop CD" + : " Play CD", 0, TINY_FONT, 0 ); } @@ -8033,6 +8052,14 @@ static void M_DrawChallengeScrollBar(UINT8 *flashmap) } #endif + if (i == challengesmenu.nowplayingtile && Music_Playing("challenge_altmusic")) + { + V_DrawFill(barx + hilix, bary, hiliw, barh, (challengesmenu.ticker & 2) ? 177 : 122); + + // The now-playing fill overrides everything else. + completionamount = -1; + } + if (completionamount == -1) continue; diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index a1f83b756..bd8bdad4b 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -397,6 +397,7 @@ menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu) if (firstopen) { challengesmenu.currentunlock = MAXUNLOCKABLES; + challengesmenu.nowplayingtile = UINT16_MAX; firstopen = false; } @@ -446,6 +447,7 @@ void M_Challenges(INT32 choice) static void M_CloseChallenges(void) { Music_Stop("challenge_altmusic"); + challengesmenu.nowplayingtile = UINT16_MAX; Z_Free(challengesmenu.extradata); challengesmenu.extradata = NULL; @@ -599,8 +601,8 @@ void M_ChallengesTick(void) for (i = 0; i < (CHALLENGEGRIDHEIGHT * gamedata->challengegridwidth); i++) { allthewaythrough = (!seeeveryone && !challengesmenu.pending && i != id); - maxflip = ((seeeveryone || !allthewaythrough) ? (TILEFLIP_MAX/2) : TILEFLIP_MAX); - if ((seeeveryone || (i == id) || (challengesmenu.extradata[i].flip > 0)) + maxflip = (allthewaythrough ? TILEFLIP_MAX : (TILEFLIP_MAX/2)); + if ((seeeveryone || (i == id) || (i == challengesmenu.nowplayingtile) || (challengesmenu.extradata[i].flip > 0)) && (challengesmenu.extradata[i].flip != maxflip)) { challengesmenu.extradata[i].flip++; @@ -1324,15 +1326,18 @@ boolean M_ChallengesInputs(INT32 ch) if (trymusname) { - if (!Music_Playing("challenge_altmusic") - || strcmp(Music_Song("challenge_altmusic"), trymusname)) + const char *tune = "challenge_altmusic"; + if (!Music_Playing(tune) + || strcmp(Music_Song(tune), trymusname)) { - Music_Remap("challenge_altmusic", trymusname); - Music_Play("challenge_altmusic"); + Music_Remap(tune, trymusname); + Music_Play(tune); + challengesmenu.nowplayingtile = (challengesmenu.hilix * CHALLENGEGRIDHEIGHT) + challengesmenu.hiliy; } else { - Music_Stop("challenge_altmusic"); + Music_Stop(tune); + challengesmenu.nowplayingtile = UINT16_MAX; } M_SetMenuDelay(pid); From ee76f539e91392408fd127b85f277fd0e502ec99 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 14 Aug 2025 21:51:05 +0100 Subject: [PATCH 07/19] M_DrawChallengeTile: Fix brief flickers of invalid side caused by interpolation --- src/k_menudraw.c | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 00bc9d8f3..2a28d54c6 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -6722,7 +6722,6 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, UINT8 *flash fixed_t siz, accordion; UINT16 id, num; boolean unlockedyet; - boolean categoryside; id = (i * CHALLENGEGRIDHEIGHT) + j; num = gamedata->challengegrid[id]; @@ -6797,13 +6796,20 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, UINT8 *flash accordion = FRACUNIT; - if (challengesmenu.extradata[id].flip != 0 + boolean categoryside = (challengesmenu.extradata[id].flip == 0); + + if (!categoryside // optimised, this is not the true value with anything but instaflip && challengesmenu.extradata[id].flip != (TILEFLIP_MAX/2)) { - angle_t bad = (FixedAngle(FixedMul(challengesmenu.extradata[id].flip * FRACUNIT + rendertimefrac, 360*FRACUNIT/TILEFLIP_MAX)) >> ANGLETOFINESHIFT) & FINEMASK; - accordion = FINECOSINE(bad); + fixed_t bad = challengesmenu.extradata[id].flip * FRACUNIT + rendertimefrac; + angle_t worse = (FixedAngle(FixedMul(bad, 360*FRACUNIT/TILEFLIP_MAX)) >> ANGLETOFINESHIFT) & FINEMASK; + accordion = FINECOSINE(worse); if (accordion < 0) accordion = -accordion; + + // NOW we set it in an interp-friendly way + categoryside = (bad <= FRACUNIT*TILEFLIP_MAX/4 + || bad > (3*FRACUNIT*TILEFLIP_MAX)/4); } pat = W_CachePatchName( @@ -6822,9 +6828,6 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, UINT8 *flash pat = missingpat; - categoryside = (challengesmenu.extradata[id].flip <= TILEFLIP_MAX/4 - || challengesmenu.extradata[id].flip > (3*TILEFLIP_MAX)/4); - #ifdef DEVELOP if (cv_debugchallenges.value) { From d98b88a61fa58e46e194d763f127f98e81ef325d Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 14 Aug 2025 21:52:27 +0100 Subject: [PATCH 08/19] M_DrawChallengeTile: The non-category size of SECRET_MAP will now show a "map face", ala Map Anger/menu Round Queue --- src/k_hud.cpp | 48 ++++++++++++++++++---------- src/k_hud.h | 7 ++-- src/k_menudraw.c | 83 ++++++++++++++++++++++++++++++++++-------------- src/k_vote.c | 2 +- src/y_inter.cpp | 2 +- 5 files changed, 97 insertions(+), 45 deletions(-) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 183afba6d..512b6db40 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -1526,7 +1526,7 @@ static void K_initKartHUD(void) } } -void K_DrawMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, UINT16 map, const UINT8 *colormap) +void K_DrawMapThumbnail2(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, UINT16 map, const UINT8 *colormap, fixed_t accordion) { patch_t *PictureOfLevel = NULL; @@ -1543,58 +1543,72 @@ void K_DrawMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, UINT1 PictureOfLevel = static_cast(mapheaderinfo[map]->thumbnailPic); } - K_DrawLikeMapThumbnail(x, y, width, flags, PictureOfLevel, colormap); + K_DrawLikeMapThumbnail(x, y, width, flags, PictureOfLevel, colormap, accordion); } -void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, patch_t *patch, const UINT8 *colormap) +void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, patch_t *patch, const UINT8 *colormap, fixed_t accordion) { - if (flags & V_FLIP) - x += width; + fixed_t scale = FixedDiv(width, (320 << FRACBITS)); - V_DrawFixedPatch( + if (flags & V_FLIP) + x += FixedMul(width, accordion); + + V_DrawStretchyFixedPatch( x, y, - FixedDiv(width, (320 << FRACBITS)), + FixedMul(scale, accordion), + scale, flags, patch, colormap ); } -void K_DrawMapAsFace(INT32 x, INT32 y, UINT32 flags, UINT16 map, const UINT8 *colormap) +void K_DrawMapAsFace(INT32 x, INT32 y, UINT32 flags, UINT16 map, const UINT8 *colormap, fixed_t accordion, INT32 unit) { - const fixed_t iconHeight = (14 << FRACBITS); + const fixed_t iconHeight = (14 * unit) << FRACBITS; const fixed_t iconWidth = (iconHeight * 320) / 200; - INT32 unit = 1; fixed_t mul = FRACUNIT; if (flags & V_NOSCALESTART) { - unit = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy); + unit *= (vid.dupx < vid.dupy ? vid.dupx : vid.dupy); mul = 1; } + INT32 hunit = (unit * accordion); + V_DrawFill( x, y, - 16 * unit, + (16 * hunit)/FRACUNIT, 16 * unit, (flags & ~V_FLIP) ); + if (flags & V_NOSCALESTART) + { + hunit /= FRACUNIT; + } + else + { + hunit = FixedMul(hunit, mul); + } + V_SetClipRect( - (x + unit) * mul, + (x * mul) + hunit, (y + unit) * mul, - (14 * unit) * mul, + (14 * hunit), (14 * unit) * mul, (flags & ~V_FLIP) ); - K_DrawMapThumbnail( - ((x + unit) * FRACUNIT) - (iconWidth - iconHeight)/2, + K_DrawMapThumbnail2( + (x * FRACUNIT) + hunit - FixedMul(iconWidth - iconHeight, accordion)/2, ((y + unit) * FRACUNIT), iconWidth, flags, map, - colormap + colormap, + accordion ); V_ClearClipRect(); diff --git a/src/k_hud.h b/src/k_hud.h index af5a30b68..c251fc137 100644 --- a/src/k_hud.h +++ b/src/k_hud.h @@ -58,9 +58,10 @@ INT32 K_drawKartMicroTime(const char *todrawtext, INT32 workx, INT32 worky, INT3 void K_drawKart2PTimestamp(void); void K_drawKart4PTimestamp(void); void K_drawEmeraldWin(boolean overlay); -void K_DrawMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, UINT16 map, const UINT8 *colormap); -void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, patch_t *patch, const UINT8 *colormap); -void K_DrawMapAsFace(INT32 x, INT32 y, UINT32 flags, UINT16 map, const UINT8 *colormap); +void K_DrawMapThumbnail2(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, UINT16 map, const UINT8 *colormap, fixed_t accordion); +#define K_DrawMapThumbnail(x, y, w, f, m, c) K_DrawMapThumbnail2(x, y, w, f, m, c, FRACUNIT) +void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, patch_t *patch, const UINT8 *colormap, fixed_t accordion); +void K_DrawMapAsFace(INT32 x, INT32 y, UINT32 flags, UINT16 map, const UINT8 *colormap, fixed_t accordion, INT32 unit); void K_drawTargetHUD(const vector3_t *origin, player_t *player); void K_drawButton(fixed_t x, fixed_t y, INT32 flags, patch_t *button[2], boolean pressed); void K_drawButtonAnim(INT32 x, INT32 y, INT32 flags, patch_t *button[2], tic_t animtic); diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 2a28d54c6..358c26210 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -6933,8 +6933,42 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, UINT8 *flash } case SECRET_MAP: - iconid = 14; + { + UINT16 mapnum = M_UnlockableMapNum(ref); + if (mapnum < nummapheaders && mapheaderinfo[mapnum] + && ( + ( // Check for visitation + (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) + || (mapheaderinfo[mapnum]->records.mapvisited & MV_VISITED) + ) && ( // Check for completion + !(mapheaderinfo[mapnum]->menuflags & LF2_FINISHNEEDED) + || (mapheaderinfo[mapnum]->records.mapvisited & MV_BEATEN) + ) + )) + { + if (ref->majorunlock) + { + K_DrawMapAsFace( + (x + 5) + (32*(FRACUNIT-accordion))/(2*FRACUNIT), (y + 5), + tileflags, + mapnum, + NULL, accordion, 2 + ); + } + else + { + K_DrawMapAsFace( + (x + 2) + (16*(FRACUNIT-accordion))/(2*FRACUNIT), (y + 2), + tileflags, + mapnum, + NULL, accordion, 1 + ); + } + pat = NULL; + } + iconid = 0; //14; -- This one suits a little better for "go complete this level normally" break; + } case SECRET_ALTMUSIC: iconid = 16; break; @@ -7004,29 +7038,32 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, UINT8 *flash } } - siz = (SHORT(pat->width) << FRACBITS); + if (pat) + { + siz = (SHORT(pat->width) << FRACBITS); - if (!siz) - ; // prevent div/0 - else if (ref->majorunlock) - { - V_DrawStretchyFixedPatch( - ((x + 5)*FRACUNIT) + (32*(FRACUNIT-accordion)/2), (y + 5)*FRACUNIT, - FixedDiv(32*accordion, siz), - FixedDiv(32 << FRACBITS, siz), - tileflags, pat, - colormap - ); - } - else - { - V_DrawStretchyFixedPatch( - ((x + 2)*FRACUNIT) + (16*(FRACUNIT-accordion)/2), (y + 2)*FRACUNIT, - FixedDiv(16*accordion, siz), - FixedDiv(16 << FRACBITS, siz), - tileflags, pat, - colormap - ); + if (!siz) + ; // prevent div/0 + else if (ref->majorunlock) + { + V_DrawStretchyFixedPatch( + ((x + 5)*FRACUNIT) + (32*(FRACUNIT-accordion))/2, (y + 5)*FRACUNIT, + FixedDiv(32*accordion, siz), + FixedDiv(32 << FRACBITS, siz), + tileflags, pat, + colormap + ); + } + else + { + V_DrawStretchyFixedPatch( + ((x + 2)*FRACUNIT) + (16*(FRACUNIT-accordion))/2, (y + 2)*FRACUNIT, + FixedDiv(16*accordion, siz), + FixedDiv(16 << FRACBITS, siz), + tileflags, pat, + colormap + ); + } } drawborder: diff --git a/src/k_vote.c b/src/k_vote.c index 21b1494f5..13ce0b22e 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -705,7 +705,7 @@ static void Y_DrawVoteThumbnail(fixed_t center_x, fixed_t center_y, fixed_t widt fy + fh - whiteSq + dupy, flags | V_NOSCALESTART | ((encore == true) ? V_FLIP : 0), g_voteLevels[v][0], - NULL + NULL, FRACUNIT, 1 ); } } diff --git a/src/y_inter.cpp b/src/y_inter.cpp index a7668b1b2..a096b71f9 100644 --- a/src/y_inter.cpp +++ b/src/y_inter.cpp @@ -1615,7 +1615,7 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, x - 9, y - 13, (baseflags|((menuqueue.entries[i].encore) ? V_FLIP : 0)), menuqueue.entries[i].mapnum, - NULL + NULL, FRACUNIT, 1 ); x += 24; From a7f119328e3c0c2bee1ecebb20c01e6cc44051a8 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 14 Aug 2025 22:26:49 +0100 Subject: [PATCH 09/19] Challenge Preview for SECRET_ALTMUSIC: Consistency - Since we're only using 50% of rollangle steps at 35fps, we can smooth it out under interp conditions like the flipping tile - Don't grey out the text/stop the button animation for the input prompt, since pressing it again will stop the music, whereas grey text on Drivers/Followers/Colors means no function - Keep the tile flipping even when not hovered over --- src/k_menudraw.c | 15 +++++++++++---- src/menus/extras-challenges.c | 13 ++++++++++--- 2 files changed, 21 insertions(+), 7 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 358c26210..9781b07b1 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -7698,8 +7698,15 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) if (pushed != 0) { rollangle = (Music_Elapsed(tune) % (ROTANGLES/2))*2; + if (rendertimefrac >= FRACUNIT/2) + { + // A fun interp ability: inbetweens + rollangle++; + } if (pushed > 0) + { rollangle = ((ROTANGLES-1) - rollangle); + } } #endif @@ -7732,8 +7739,8 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) K_DrawGameControl( x, y, 0, (pushed < 0) - ? " E Stop" - : " E Side", + ? " E Stop" + : " E Side", 0, TINY_FONT, 0 ); @@ -7745,8 +7752,8 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) K_DrawGameControl( x, y, 0, (pushed > 0) - ? " Stop CD" - : " Play CD", + ? " Stop CD" + : " Play CD", 0, TINY_FONT, 0 ); } diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index bd8bdad4b..733f44383 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -596,12 +596,19 @@ void M_ChallengesTick(void) { UINT16 id = (challengesmenu.hilix * CHALLENGEGRIDHEIGHT) + challengesmenu.hiliy; boolean seeeveryone = challengesmenu.requestflip; - boolean allthewaythrough; + boolean allthewaythrough = allthewaythrough = (!seeeveryone && !challengesmenu.pending); + UINT8 maxflip; + + if (id == challengesmenu.nowplayingtile) + { + // Don't permit the active song to stop spinning + id = UINT16_MAX; + } + for (i = 0; i < (CHALLENGEGRIDHEIGHT * gamedata->challengegridwidth); i++) { - allthewaythrough = (!seeeveryone && !challengesmenu.pending && i != id); - maxflip = (allthewaythrough ? TILEFLIP_MAX : (TILEFLIP_MAX/2)); + maxflip = ((allthewaythrough && i != id) ? TILEFLIP_MAX : (TILEFLIP_MAX/2)); if ((seeeveryone || (i == id) || (i == challengesmenu.nowplayingtile) || (challengesmenu.extradata[i].flip > 0)) && (challengesmenu.extradata[i].flip != maxflip)) { From 2e0c8fab33deb9626739aa354df506666cc5675d Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 14 Aug 2025 22:52:56 +0100 Subject: [PATCH 10/19] Force a Challenge tile flip if you activate the effect of a given Challenge (profile set and alt title) Racers and Followers will also play their unique sound too --- src/menus/extras-challenges.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index 733f44383..0af2095cb 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -1165,6 +1165,9 @@ boolean M_ChallengesInputs(INT32 ch) && gamedata->unlocked[challengesmenu.currentunlock]) { unlockable_t *ref = &unlockables[challengesmenu.currentunlock]; + + boolean forceflip = false; + switch (unlockables[challengesmenu.currentunlock].type) { case SECRET_MAP: @@ -1217,6 +1220,8 @@ boolean M_ChallengesInputs(INT32 ch) CV_AddValue(&cv_alttitle, 1); S_StartSound(NULL, sfx_s3kc3s); M_SetMenuDelay(pid); + + forceflip = true; } break; } @@ -1235,7 +1240,10 @@ boolean M_ChallengesInputs(INT32 ch) CV_Set(&cv_skin[0], skins[skin]->name); S_StartSound(NULL, sfx_s3k63); + S_StartSound(NULL, skins[skin]->soundsid[S_sfx[sfx_kattk1].skinsound]); M_SetMenuDelay(pid); + + forceflip = true; } } } @@ -1256,7 +1264,10 @@ boolean M_ChallengesInputs(INT32 ch) CV_Set(&cv_follower[0], followers[fskin].name); S_StartSound(NULL, sfx_s3k63); + S_StartSound(NULL, followers[fskin].hornsound); M_SetMenuDelay(pid); + + forceflip = true; } } } @@ -1282,6 +1293,8 @@ boolean M_ChallengesInputs(INT32 ch) S_StartSound(NULL, sfx_s3k63); M_SetMenuDelay(pid); + + forceflip = true; } } } @@ -1357,6 +1370,16 @@ boolean M_ChallengesInputs(INT32 ch) break; } + if (forceflip) + { + UINT16 id = (challengesmenu.hilix * CHALLENGEGRIDHEIGHT) + challengesmenu.hiliy; + // This construction helps pressing too early + if (challengesmenu.extradata[id].flip <= TILEFLIP_MAX/2) + { + challengesmenu.extradata[id].flip = 1 + (TILEFLIP_MAX/2); + } + } + return true; } } From fb2224e9b1b6557262cfd78df96e29ce2bc8a733 Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 14 Aug 2025 23:42:05 +0100 Subject: [PATCH 11/19] Allow pressing again while a follower is on your Profile (or you have a local party and can't make any profile edits from here) to play horn... with goofy bonus text from the only obvious source --- src/k_menu.h | 2 ++ src/k_menudraw.c | 45 ++++++++++++++++++++++++++++------- src/menus/extras-challenges.c | 33 +++++++++++++++++-------- 3 files changed, 61 insertions(+), 19 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 38a7edb07..1fd0e93bf 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -1467,6 +1467,8 @@ extern struct challengesmenu_s { UINT8 fade; + UINT8 hornposting; + boolean cache_secondrowlocked; patch_t *tile_category[10][2]; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 9781b07b1..4e2610fc2 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -7303,23 +7303,50 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) y = (BASEVIDHEIGHT-14); + const char *actiontext = NULL; + if (setup_numplayers <= 1 && cv_lastprofile[0].value != PROFILE_GUEST) { profile_t *pr = PR_GetProfile(cv_lastprofile[0].value); - if (pr) + if (pr && strcmp(pr->follower, followers[fskin].name)) { - K_DrawGameControl( - 4, y, 0, - strcmp(pr->follower, followers[fskin].name) - ? " Set on Profile" - : " Set on Profile", - 0, TINY_FONT, 0 - ); - y -= 14; + actiontext = (followers[fskin].hornsound == sfx_melody) + ? " Set on Profile" + : " Set on Profile"; } } + if (!actiontext) + { + if (followers[fskin].hornsound == sfx_melody) + { + actiontext = " Play Ancient Melody?"; + } + else switch (challengesmenu.hornposting % 4) + { + default: + actiontext = " Say hello"; + break; + case 1: + actiontext = " Express your feelings"; + break; + case 2: + actiontext = " Celebrate victory"; + break; + case 3: + actiontext = " Announce you are pressing horn"; + break; + } + } + + K_DrawGameControl( + 4, y, 0, + actiontext, + 0, TINY_FONT, 0 + ); + y -= 14; + if (followers[fskin].category < numfollowercategories) { V_DrawFixedPatch(4*FRACUNIT, (y - 6)*FRACUNIT, diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index 0af2095cb..60cbc9944 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -393,6 +393,7 @@ menu_t *M_InterruptMenuWithChallenges(menu_t *desiredmenu) challengesmenu.tutorialfound = NEXTMAP_INVALID; challengesmenu.chaokeyhold = 0; challengesmenu.unlockcondition = NULL; + challengesmenu.hornposting = 0; if (firstopen) { @@ -1251,24 +1252,36 @@ boolean M_ChallengesInputs(INT32 ch) } case SECRET_FOLLOWER: { - if (setup_numplayers <= 1 && cv_lastprofile[0].value != PROFILE_GUEST && M_MenuConfirmPressed(pid)) + if (M_MenuConfirmPressed(pid)) { INT32 fskin = M_UnlockableFollowerNum(ref); if (fskin != -1) { - profile_t *pr = PR_GetProfile(cv_lastprofile[0].value); - - if (pr && strcmp(pr->follower, followers[fskin].name)) + if (setup_numplayers <= 1 && cv_lastprofile[0].value != PROFILE_GUEST) { - strcpy(pr->follower, followers[fskin].name); - CV_Set(&cv_follower[0], followers[fskin].name); + profile_t *pr = PR_GetProfile(cv_lastprofile[0].value); - S_StartSound(NULL, sfx_s3k63); - S_StartSound(NULL, followers[fskin].hornsound); - M_SetMenuDelay(pid); + if (pr && strcmp(pr->follower, followers[fskin].name)) + { + strcpy(pr->follower, followers[fskin].name); + CV_Set(&cv_follower[0], followers[fskin].name); - forceflip = true; + challengesmenu.hornposting = 0; + + S_StartSound(NULL, sfx_s3k63); + forceflip = true; + } } + + if (!forceflip) + { + challengesmenu.hornposting++; + } + + S_StartSound(NULL, followers[fskin].hornsound); + M_SetMenuDelay(pid); + + forceflip = true; } } break; From 69315d9163ab1a2cdbd37bc104093e04d8a453cc Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 15 Aug 2025 16:24:45 +0100 Subject: [PATCH 12/19] M_CheckUnlockConditions: Fix initialisation --- src/m_cond.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/m_cond.c b/src/m_cond.c index 54d5725e5..9dba8e565 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -3204,7 +3204,7 @@ static boolean M_CheckUnlockConditions(player_t *player) { UINT32 i; conditionset_t *c; - boolean ret; + boolean ret = false; for (i = 0; i < MAXCONDITIONSETS; ++i) { From cba929296073a26237b9fcaf30ac8bbfe37c44fc Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 15 Aug 2025 17:53:01 +0100 Subject: [PATCH 13/19] Extract K_DrawGameControl calls from M_DrawChallengePreview Done to fix weird layering issues with Challenge hint text --- src/k_menudraw.c | 133 ++++++++++++++++++++++++----------------------- 1 file changed, 67 insertions(+), 66 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 4e2610fc2..a88418f46 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -7194,7 +7194,7 @@ void M_DrawCharacterIconAndEngine(INT32 x, INT32 y, UINT8 skin, UINT8 *colormap, V_DrawFill(x+16 + (s*5), y + (w*5), 6, 6, 0); } -static void M_DrawChallengePreview(INT32 x, INT32 y) +static const char* M_DrawChallengePreview(INT32 x, INT32 y) { unlockable_t *ref = NULL; UINT8 *colormap = NULL; @@ -7202,12 +7202,9 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) if (challengesmenu.currentunlock >= MAXUNLOCKABLES) { - return; + return NULL; } - // Okay, this is what we want to draw. - ref = &unlockables[challengesmenu.currentunlock]; - // Funny question mark? if (!gamedata->unlocked[challengesmenu.currentunlock]) { @@ -7219,7 +7216,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) if (!sprdef->numframes) { - return; + return NULL; } useframe = (challengesmenu.ticker / 2) % sprdef->numframes; @@ -7233,9 +7230,14 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) } V_DrawFixedPatch(x*FRACUNIT, (y+2)*FRACUNIT, FRACUNIT, addflags, patch, NULL); - return; + return NULL; } + // Okay, this is what we want to draw. + ref = &unlockables[challengesmenu.currentunlock]; + + const char *actiontext = NULL; + switch (ref->type) { case SECRET_SKIN: @@ -7253,17 +7255,11 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) { profile_t *pr = PR_GetProfile(cv_lastprofile[0].value); - if (pr) - { - K_DrawGameControl( - 4, y, 0, - strcmp(pr->skinname, skins[skin]->name) - ? " Set on Profile" - : " Set on Profile", - 0, TINY_FONT, 0 - ); - y -= 14; - } + actiontext = (pr && strcmp(pr->skinname, skins[skin]->name)) + ? " Set on Profile" + : " Set on Profile"; + + y -= 14; } for (i = 0; i < skin; i++) @@ -7303,8 +7299,6 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) y = (BASEVIDHEIGHT-14); - const char *actiontext = NULL; - if (setup_numplayers <= 1 && cv_lastprofile[0].value != PROFILE_GUEST) { profile_t *pr = PR_GetProfile(cv_lastprofile[0].value); @@ -7340,11 +7334,6 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) } } - K_DrawGameControl( - 4, y, 0, - actiontext, - 0, TINY_FONT, 0 - ); y -= 14; if (followers[fskin].category < numfollowercategories) @@ -7374,16 +7363,9 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) { profile_t *pr = PR_GetProfile(cv_lastprofile[0].value); - if (pr) - { - K_DrawGameControl( - 4, (BASEVIDHEIGHT-14), 0, - (pr->color != colorid) - ? " Set on Profile" - : " Set on Profile", - 0, TINY_FONT, 0 - ); - } + actiontext = (pr && pr->color != colorid) + ? " Set on Profile" + : " Set on Profile"; } break; @@ -7516,11 +7498,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) if (setup_numplayers <= 1 && guessgt == GT_TUTORIAL) { // Only for 1p - K_DrawGameControl( - 4, y, 0, - " Play Tutorial", - 0, TINY_FONT, 0 - ); + actiontext = " Play Tutorial"; gtname = NULL; } else if (guessgt == GT_SPECIAL && !M_SecretUnlocked(SECRET_SPECIALATTACK, true)) @@ -7674,7 +7652,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) if (map >= nummapheaders || !mapheaderinfo[map]) { - return; + break; } UINT8 musicid; @@ -7686,7 +7664,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) if (musicid == MAXMUSNAMES) { - return; + break; } const char *tune = "challenge_altmusic"; @@ -7758,31 +7736,32 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) } } - x = 4; - y = (BASEVIDHEIGHT-14); + if (musicid < mapheaderinfo[map]->musname_size) + { + actiontext = (pushed > 0) + ? " Stop CD" + : " Play CD"; + } if (epossible) { - K_DrawGameControl( - x, y, 0, - (pushed < 0) - ? " E Stop" - : " E Side", - 0, TINY_FONT, 0 - ); - - y -= 14; - } - - if (musicid < mapheaderinfo[map]->musname_size) - { - K_DrawGameControl( - x, y, 0, - (pushed > 0) - ? " Stop CD" - : " Play CD", - 0, TINY_FONT, 0 - ); + const char *secondtext = (pushed < 0) + ? " E Stop" + : " E Side"; + if (actiontext) + { + // weird encoded height + actiontext = va("\x1""%s\n%s", + (pushed < 0) + ? " E Stop" + : " E Side", + actiontext + ); + } + else + { + actiontext = secondtext; + } } } default: @@ -7792,7 +7771,7 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) } if (specialmap == NEXTMAP_INVALID || !ref) - return; + return actiontext; x -= 50; y = 146+2; @@ -7840,6 +7819,8 @@ static void M_DrawChallengePreview(INT32 x, INT32 y) W_CachePatchName("K_LAPE02", PU_CACHE), colormap); } + + return actiontext; } #define challengesgridstep 22 @@ -8396,9 +8377,10 @@ challengedesc: y = BASEVIDHEIGHT-16; // Unlock preview - M_DrawChallengePreview(x, y); + const char *actiontext = M_DrawChallengePreview(x, y); // Conditions for unlock + // { -- please don't call va() anywhere between here... i = (challengesmenu.hilix * CHALLENGEGRIDHEIGHT) + challengesmenu.hiliy; if (challengesmenu.unlockcondition != NULL @@ -8411,6 +8393,25 @@ challengedesc: { V_DrawCenteredThinString(BASEVIDWIDTH/2, 120 + 32, 0, challengesmenu.unlockcondition); } + + // Extracted from M_DrawCharSelectPreview for ordering reasons + if (actiontext && actiontext[0]) + { + x = 4; + y = (BASEVIDHEIGHT-14); + if (actiontext[0] < '\x5') + { + // weird encoded height, supports max 5 rows + y -= (13 * actiontext[0]); + actiontext++; + } + K_DrawGameControl( + x, y, 0, + actiontext, + 0, TINY_FONT, 0 + ); + // } -- ...and here (since actiontext needs it) + } } #undef challengetransparentstrength From 9bd213ac78f3ddcf8b86bd4caa237e4a5fc17c2d Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 15 Aug 2025 20:10:03 +0100 Subject: [PATCH 14/19] Bugfixing for interactions between Profile charsel and Challenge Board - Editing the current profile for a player will update their Driver so map command and Challenge Driver have proper comparison available - Re-order M_CharacterSelectInit so driver/follower info is available/valid when M_SetupProfileGridPos is called --- src/menus/options-profiles-edit-1.c | 5 ++ src/menus/play-char-select.c | 82 ++++++++++++++--------------- 2 files changed, 46 insertions(+), 41 deletions(-) diff --git a/src/menus/options-profiles-edit-1.c b/src/menus/options-profiles-edit-1.c index fa5eb661c..948e9178a 100644 --- a/src/menus/options-profiles-edit-1.c +++ b/src/menus/options-profiles-edit-1.c @@ -109,6 +109,11 @@ static void M_ProfileEditApply(void) if (belongsto > -1 && belongsto < MAXSPLITSCREENPLAYERS) { PR_ApplyProfileToggles(optionsmenu.profilen, belongsto); + if (gamestate == GS_MENU) + { + // Safe to apply skin, etc here. + PR_ApplyProfileLight(optionsmenu.profilen, belongsto); + } } // Reapply player 1's real profile ID. diff --git a/src/menus/play-char-select.c b/src/menus/play-char-select.c index 27f2c8494..9a1e32692 100644 --- a/src/menus/play-char-select.c +++ b/src/menus/play-char-select.c @@ -304,47 +304,6 @@ void M_CharacterSelectInit(void) memset(setup_explosions, 0, sizeof(setup_explosions)); setup_animcounter = 0; - for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) - { - // Default to no follower / match colour. - setup_player[i].followern = -1; - setup_player[i].followercategory = -1; - setup_player[i].followercolor = SKINCOLOR_NONE; - - setup_player[i].profilen_slide.start = 0; - setup_player[i].profilen_slide.dist = 0; - - // If we're on prpfile select, skip straight to CSSTEP_CHARS - // do the same if we're midgame, but make sure to consider splitscreen properly. - if (optionsmenu.profile && i == 0) - { - setup_player[i].profilen = optionsmenu.profilen; - //PR_ApplyProfileLight(setup_player[i].profilen, 0); - M_SetupProfileGridPos(&setup_player[i]); - setup_player[i].mdepth = CSSTEP_CHARS; - } - else - { - // Set default selected profile to the last used profile for each player: - // (Make sure we don't overshoot it somehow if we deleted profiles or whatnot) - setup_player[i].profilen = min(cv_lastprofile[i].value, PR_GetNumProfiles()); - - if (gamestate != GS_MENU && i <= splitscreen) - { - M_SetupMidGameGridPos(&setup_player[i], i); - setup_player[i].mdepth = CSSTEP_CHARS; - } - else - { - // Un-set devices - G_SetDeviceForPlayer(i, -1); -#ifdef CHARSELECT_DEVICEDEBUG - CONS_Printf("M_CharacterSelectInit: Device for %d set to %d\n", i, -1); -#endif - } - } - } - for (i = 0; i < numskins; i++) { UINT8 x = skins[i]->kartspeed-1; @@ -391,6 +350,47 @@ void M_CharacterSelectInit(void) } setup_page = 0; + + for (i = 0; i < MAXSPLITSCREENPLAYERS; i++) + { + // Default to no follower / match colour. + setup_player[i].followern = -1; + setup_player[i].followercategory = -1; + setup_player[i].followercolor = SKINCOLOR_NONE; + + setup_player[i].profilen_slide.start = 0; + setup_player[i].profilen_slide.dist = 0; + + // If we're on prpfile select, skip straight to CSSTEP_CHARS + // do the same if we're midgame, but make sure to consider splitscreen properly. + if (optionsmenu.profile && i == 0) + { + setup_player[i].profilen = optionsmenu.profilen; + //PR_ApplyProfileLight(setup_player[i].profilen, 0); + M_SetupProfileGridPos(&setup_player[i]); + setup_player[i].mdepth = CSSTEP_CHARS; + } + else + { + // Set default selected profile to the last used profile for each player: + // (Make sure we don't overshoot it somehow if we deleted profiles or whatnot) + setup_player[i].profilen = min(cv_lastprofile[i].value, PR_GetNumProfiles()); + + if (gamestate != GS_MENU && i <= splitscreen) + { + M_SetupMidGameGridPos(&setup_player[i], i); + setup_player[i].mdepth = CSSTEP_CHARS; + } + else + { + // Un-set devices + G_SetDeviceForPlayer(i, -1); +#ifdef CHARSELECT_DEVICEDEBUG + CONS_Printf("M_CharacterSelectInit: Device for %d set to %d\n", i, -1); +#endif + } + } + } } From 2618232260914dca0f06e698ff2c1a6fe1547636 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 15 Aug 2025 20:58:57 +0100 Subject: [PATCH 15/19] Challenge Grid tile border colours Something subtle and tasteful to break up the solid field of white without turning it into Windows 8 Desktop Dayglo --- src/k_menudraw.c | 109 +++++++++++++++++++++++++++-------------------- 1 file changed, 62 insertions(+), 47 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index a88418f46..cf33e21f5 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -6816,7 +6816,61 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, UINT8 *flash (ref->majorunlock ? "UN_BORDB" : "UN_BORDA"), PU_CACHE); - bgmap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_SILVER, GTC_MENUCACHE); + UINT8 iconid = 0; + + { + UINT16 bcol = SKINCOLOR_SILVER; + switch (ref->type) + { + case SECRET_SKIN: + bcol = SKINCOLOR_NOVA; + iconid = 1; + break; + case SECRET_FOLLOWER: + bcol = SKINCOLOR_SAPPHIRE; + iconid = 2; + break; + case SECRET_COLOR: + //bcol = SKINCOLOR_SILVER; + iconid = 3; + break; + case SECRET_CUP: + bcol = SKINCOLOR_GOLD; + iconid = 4; + break; + case SECRET_MAP: + bcol = SKINCOLOR_PURPLE; + iconid = 8; + break; + case SECRET_HARDSPEED: + case SECRET_MASTERMODE: + case SECRET_ENCORE: + bcol = SKINCOLOR_RUBY; + iconid = 5; + break; + case SECRET_ONLINE: + case SECRET_ADDONS: + case SECRET_EGGTV: + case SECRET_SOUNDTEST: + case SECRET_ALTTITLE: + bcol = SKINCOLOR_BLUEBERRY; + iconid = 6; + break; + case SECRET_TIMEATTACK: + case SECRET_PRISONBREAK: + case SECRET_SPECIALATTACK: + case SECRET_SPBATTACK: + bcol = SKINCOLOR_PERIDOT; + iconid = 7; + break; + case SECRET_ALTMUSIC: + bcol = SKINCOLOR_MAGENTA; + iconid = 9; + break; + } + + bgmap = R_GetTranslationColormap(TC_DEFAULT, bcol, GTC_MENUCACHE); + } V_DrawStretchyFixedPatch( (x*FRACUNIT) + (SHORT(pat->width)*(FRACUNIT-accordion)/2), y*FRACUNIT, @@ -6831,59 +6885,20 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, UINT8 *flash #ifdef DEVELOP if (cv_debugchallenges.value) { - // Show the content of every tile without needing to - // flip them. + // Show the content of every tile without needing to flip them. categoryside = false; } #endif if (categoryside) { - char categoryid = '0'; - colormap = bgmap; - switch (ref->type) - { - case SECRET_SKIN: - categoryid = '1'; - break; - case SECRET_FOLLOWER: - categoryid = '2'; - break; - case SECRET_COLOR: - categoryid = '3'; - break; - case SECRET_CUP: - categoryid = '4'; - break; - case SECRET_MAP: - categoryid = '8'; - break; - case SECRET_HARDSPEED: - case SECRET_MASTERMODE: - case SECRET_ENCORE: - categoryid = '5'; - break; - case SECRET_ONLINE: - case SECRET_ADDONS: - case SECRET_EGGTV: - case SECRET_SOUNDTEST: - case SECRET_ALTTITLE: - categoryid = '6'; - break; - case SECRET_TIMEATTACK: - case SECRET_PRISONBREAK: - case SECRET_SPECIALATTACK: - case SECRET_SPBATTACK: - categoryid = '7'; - break; - case SECRET_ALTMUSIC: - categoryid = '9'; - break; - } - pat = challengesmenu.tile_category[categoryid - '0'][ref->majorunlock ? 1 : 0]; + colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_SILVER, GTC_MENUCACHE); + + // iconid is already prepopulated because we had to draw the border + pat = challengesmenu.tile_category[iconid][ref->majorunlock ? 1 : 0]; if (pat == missingpat) { - pat = challengesmenu.tile_category[categoryid - '0'][ref->majorunlock ? 0 : 1]; + pat = challengesmenu.tile_category[iconid][ref->majorunlock ? 0 : 1]; } } else if (ref->icon != NULL && ref->icon[0]) @@ -6896,7 +6911,7 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, UINT8 *flash } else { - UINT8 iconid = 0; + iconid = 0; // reuse switch (ref->type) { case SECRET_SKIN: From 2f6d7230f0b8f828d64a09cd315bd13e710900a5 Mon Sep 17 00:00:00 2001 From: toaster Date: Fri, 15 Aug 2025 22:44:53 +0100 Subject: [PATCH 16/19] Dumb post alert: Ease off the horn If we're bringing the spice we have to bring the whole pepper --- src/d_netcmd.c | 9 ++++++- src/k_follower.c | 2 ++ src/k_follower.h | 2 ++ src/k_menu.h | 2 ++ src/k_menudraw.c | 46 ++++++++++++++++++++++++++++++++--- src/menus/extras-challenges.c | 14 ++++++++--- src/menus/play-char-select.c | 19 ++++++++++----- src/p_setup.cpp | 2 +- 8 files changed, 81 insertions(+), 15 deletions(-) diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 683487c97..b089ac13b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1048,7 +1048,14 @@ static void SendNameAndColor(const UINT8 n) WRITESTRINGN(p, cv_playername[n].zstring, MAXPLAYERNAME); WRITEUINT16(p, sendColor); WRITEUINT8(p, (UINT8)cv_skin[n].value); - WRITEINT16(p, (INT16)cv_follower[n].value); + if (horngoner) + { + WRITEINT16(p, (-1)); + } + else + { + WRITEINT16(p, (INT16)cv_follower[n].value); + } //CONS_Printf("Sending follower id %d\n", (INT16)cv_follower[n].value); WRITEUINT16(p, sendFollowerColor); diff --git a/src/k_follower.c b/src/k_follower.c index d80835f61..847a95c50 100644 --- a/src/k_follower.c +++ b/src/k_follower.c @@ -32,6 +32,8 @@ follower_t followers[MAXFOLLOWERS]; INT32 numfollowercategories; followercategory_t followercategories[MAXFOLLOWERCATEGORIES]; +boolean horngoner = false; + CV_PossibleValue_t Followercolor_cons_t[MAXSKINCOLORS+3]; // +3 to account for "Match", "Opposite" & NULL /*-------------------------------------------------- diff --git a/src/k_follower.h b/src/k_follower.h index dc5ca9a72..78dbc9b0e 100644 --- a/src/k_follower.h +++ b/src/k_follower.h @@ -115,6 +115,8 @@ struct followercategory_t extern INT32 numfollowercategories; extern followercategory_t followercategories[MAXFOLLOWERCATEGORIES]; +extern boolean horngoner; + /*-------------------------------------------------- INT32 K_FollowerAvailable(const char *name) diff --git a/src/k_menu.h b/src/k_menu.h index 1fd0e93bf..bc69c2299 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -1428,6 +1428,8 @@ typedef enum #define CHAOHOLD_END (3) #define CHAOHOLD_PADDING (CHAOHOLD_BEGIN + CHAOHOLD_END) +#define EASEOFFHORN 50 + extern struct timeattackmenu_s { tic_t ticker; // How long the menu's been open for diff --git a/src/k_menudraw.c b/src/k_menudraw.c index cf33e21f5..09bc1e8b4 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1862,6 +1862,9 @@ static boolean M_DrawFollowerSprite(INT16 x, INT16 y, INT32 num, boolean charfli follower_t *fl; UINT8 rotation = (charflip ? 1 : 7); + if (horngoner) + return false; + if (p != NULL) followernum = p->followern; else @@ -2406,7 +2409,7 @@ void M_DrawProfileCard(INT32 x, INT32 y, boolean greyedout, profile_t *p) V_DrawMappedPatch(x+14, y+66, 0, faceprefix[skinnum][FACE_RANK], ccolormap); } - if (fln >= 0) + if (!horngoner && fln >= 0) { UINT16 fcol = K_GetEffectiveFollowerColor( p->followercolor, @@ -6827,8 +6830,15 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, UINT8 *flash iconid = 1; break; case SECRET_FOLLOWER: - bcol = SKINCOLOR_SAPPHIRE; - iconid = 2; + if (horngoner) + { + bcol = SKINCOLOR_BLACK; + } + else + { + bcol = SKINCOLOR_SAPPHIRE; + iconid = 2; + } break; case SECRET_COLOR: //bcol = SKINCOLOR_SILVER; @@ -6890,7 +6900,9 @@ static void M_DrawChallengeTile(INT16 i, INT16 j, INT32 x, INT32 y, UINT8 *flash } #endif - if (categoryside) + if (horngoner && ref->type == SECRET_FOLLOWER) + goto drawborder; + else if (categoryside) { colormap = R_GetTranslationColormap(TC_DEFAULT, SKINCOLOR_SILVER, GTC_MENUCACHE); @@ -7305,6 +7317,11 @@ static const char* M_DrawChallengePreview(INT32 x, INT32 y) colormap = R_GetTranslationColormap(TC_BLINK, SKINCOLOR_BLACK, GTC_MENUCACHE); M_DrawCharacterSprite(x, y, skin, SPR2_STIN, 7, 0, 0, colormap); + if (horngoner) + { + return " MISSING."; + } + // Draw follower next to them if (fskin != -1) { @@ -7332,6 +7349,27 @@ static const char* M_DrawChallengePreview(INT32 x, INT32 y) { actiontext = " Play Ancient Melody?"; } + else if (challengesmenu.hornposting >= EASEOFFHORN) + actiontext = " Time to die"; + else if (challengesmenu.hornposting >= (EASEOFFHORN-5)) + { + if (challengesmenu.hornposting == EASEOFFHORN) + actiontext = "Time to die"; + else + actiontext = "I asked politely"; + actiontext = va("%s%s", + (M_MenuConfirmPressed(0) + ? " " + : " " + ), actiontext + ); + } + else if (challengesmenu.hornposting >= (EASEOFFHORN-10)) + { + actiontext = M_MenuConfirmPressed(0) + ? " Ease off the horn" + : " Ease off the horn"; + } else switch (challengesmenu.hornposting % 4) { default: diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index 60cbc9944..a732e3e3e 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -1252,7 +1252,7 @@ boolean M_ChallengesInputs(INT32 ch) } case SECRET_FOLLOWER: { - if (M_MenuConfirmPressed(pid)) + if (!horngoner && M_MenuConfirmPressed(pid)) { INT32 fskin = M_UnlockableFollowerNum(ref); if (fskin != -1) @@ -1274,11 +1274,19 @@ boolean M_ChallengesInputs(INT32 ch) } if (!forceflip) - { challengesmenu.hornposting++; + + if (challengesmenu.hornposting > EASEOFFHORN) + { + challengesmenu.hornposting = 0; + horngoner = true; + S_StartSound(NULL, sfx_s3k72); + } + else + { + S_StartSound(NULL, followers[fskin].hornsound); } - S_StartSound(NULL, followers[fskin].hornsound); M_SetMenuDelay(pid); forceflip = true; diff --git a/src/menus/play-char-select.c b/src/menus/play-char-select.c index 9a1e32692..1a055a7b5 100644 --- a/src/menus/play-char-select.c +++ b/src/menus/play-char-select.c @@ -777,7 +777,7 @@ static boolean M_HandleBeginningColors(setup_player_t *p) static void M_HandleBeginningFollowers(setup_player_t *p) { - if (setup_numfollowercategories == 0) + if (horngoner || setup_numfollowercategories == 0) { p->followern = -1; M_HandlePlayerFinalise(p); @@ -1392,7 +1392,9 @@ static void M_MPConfirmCharacterSelection(void) CV_StealthSetValue(&cv_playercolor[i], col); // follower - if (setup_player[i].followern < 0) + if (horngoner) + ; + else if (setup_player[i].followern < 0) CV_StealthSet(&cv_follower[i], "None"); else CV_StealthSet(&cv_follower[i], followers[setup_player[i].followern].name); @@ -1457,9 +1459,12 @@ void M_CharacterSelectTick(void) strcpy(optionsmenu.profile->skinname, skins[setup_player[0].skin]->name); optionsmenu.profile->color = setup_player[0].color; - // save follower - strcpy(optionsmenu.profile->follower, followers[setup_player[0].followern].name); - optionsmenu.profile->followercolor = setup_player[0].followercolor; + if (!horngoner) // so you don't lose your choice after annoying the game + { + // save follower + strcpy(optionsmenu.profile->follower, followers[setup_player[0].followern].name); + optionsmenu.profile->followercolor = setup_player[0].followercolor; + } // reset setup_player memset(setup_player, 0, sizeof(setup_player)); @@ -1475,7 +1480,9 @@ void M_CharacterSelectTick(void) CV_StealthSet(&cv_skin[i], skins[setup_player[i].skin]->name); CV_StealthSetValue(&cv_playercolor[i], setup_player[i].color); - if (setup_player[i].followern < 0) + if (horngoner) + ; + else if (setup_player[i].followern < 0) CV_StealthSet(&cv_follower[i], "None"); else CV_StealthSet(&cv_follower[i], followers[setup_player[i].followern].name); diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 690002841..7254f38a7 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -8099,7 +8099,7 @@ static void P_InitPlayers(void) skin = 0; } - if (netgame) + if (netgame || horngoner) ; // shouldn't happen but at least attempt to sync if it does else for (i = 0; i < numfollowers; i++) { From 2033c75500309f711e30e879f0ce384513761a5f Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 17 Aug 2025 13:10:26 +0100 Subject: [PATCH 17/19] UC_EMBLEM: Require emblem to be valid before reading `collected` --- src/m_cond.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/m_cond.c b/src/m_cond.c index 9dba8e565..c1a4ffb9f 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -1655,7 +1655,12 @@ boolean M_CheckCondition(condition_t *cn, player_t *player) case UC_TOTALMEDALS: // Requires number of emblems >= x return (M_GotEnoughMedals(cn->requirement)); case UC_EMBLEM: // Requires emblem x to be obtained - return gamedata->collected[cn->requirement-1]; + { + INT32 i = cn->requirement-1; + if (i >= 0 && i < numemblems && emblemlocations[i].type != ET_NONE) + return gamedata->collected[cn->requirement-1]; + return false; + } case UC_UNLOCKABLE: // Requires unlockable x to be obtained return gamedata->unlocked[cn->requirement-1]; case UC_CONDITIONSET: // requires condition set x to already be achieved From 7568acd592c24324f3ee791522765290ca657a21 Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 18 Aug 2025 11:10:32 +0100 Subject: [PATCH 18/19] Don't do any further stuff if going to Tutorial course --- src/menus/extras-challenges.c | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/menus/extras-challenges.c b/src/menus/extras-challenges.c index a732e3e3e..aeae6aae2 100644 --- a/src/menus/extras-challenges.c +++ b/src/menus/extras-challenges.c @@ -1208,6 +1208,8 @@ boolean M_ChallengesInputs(INT32 ch) M_CloseChallenges(); M_ClearMenus(true); + + return false; // DO NOT } } } From d3ab730401c143ac78b0e0e1c38fc5f125ab566d Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 18 Aug 2025 12:22:19 +0100 Subject: [PATCH 19/19] K_DrawMapAsFace improvements - Match the "large" faces with only 1px border when scalefactor is 2 - Cleaner, clearer-to-read code with fewer obfuscating pre-optimistions --- src/k_hud.cpp | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 512b6db40..281589de5 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -1563,47 +1563,54 @@ void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, p ); } -void K_DrawMapAsFace(INT32 x, INT32 y, UINT32 flags, UINT16 map, const UINT8 *colormap, fixed_t accordion, INT32 unit) +void K_DrawMapAsFace(INT32 x, INT32 y, UINT32 flags, UINT16 map, const UINT8 *colormap, fixed_t accordion, INT32 scalefactor) { - const fixed_t iconHeight = (14 * unit) << FRACBITS; + const fixed_t iconHeight = ((16 * scalefactor) - 2) << FRACBITS; const fixed_t iconWidth = (iconHeight * 320) / 200; fixed_t mul = FRACUNIT; + + INT32 dup = 1; if (flags & V_NOSCALESTART) { - unit *= (vid.dupx < vid.dupy ? vid.dupx : vid.dupy); + dup = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy); mul = 1; } - INT32 hunit = (unit * accordion); + INT32 hdup = (dup * accordion); V_DrawFill( x, y, - (16 * hunit)/FRACUNIT, - 16 * unit, + (16 * scalefactor * hdup)/FRACUNIT, + 16 * scalefactor * dup, (flags & ~V_FLIP) ); + INT32 xclip = ((16 * scalefactor) - 2) * dup * mul; + if (flags & V_NOSCALESTART) { - hunit /= FRACUNIT; + hdup /= FRACUNIT; } else { - hunit = FixedMul(hunit, mul); + hdup = FixedMul(hdup, mul); + xclip = FixedMul(xclip, accordion); } + dup *= mul; + V_SetClipRect( - (x * mul) + hunit, - (y + unit) * mul, - (14 * hunit), - (14 * unit) * mul, + (x * mul) + hdup, + (y * mul) + dup, + xclip, + ((16 * scalefactor) - 2) * dup, (flags & ~V_FLIP) ); K_DrawMapThumbnail2( - (x * FRACUNIT) + hunit - FixedMul(iconWidth - iconHeight, accordion)/2, - ((y + unit) * FRACUNIT), + (x * mul) + hdup - FixedMul(iconWidth - iconHeight, accordion)/2, + (y * mul) + dup, iconWidth, flags, map,