From 43f2dbe786b959b4dabe3197358eb0862b3570ab Mon Sep 17 00:00:00 2001 From: toaster Date: Mon, 21 Oct 2024 22:42:13 +0100 Subject: [PATCH 1/7] Level Select block: Draw relevant Medals on map thumbnail - Medals in Time Attack - Spray Can & Ancient Shrine in Match Race --- src/k_menudraw.c | 79 ++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 69 insertions(+), 10 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 787b04947..481393f6e 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -3393,6 +3393,8 @@ static void M_DrawHighLowLevelTitle(INT16 x, INT16 y, INT16 map) V_DrawLSTitleLowString(x2, y+28, 0, word2); } +static INT32 M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y, boolean allowtime, boolean allowencore, boolean allowspb, boolean allowbonus, boolean draw); + static void M_DrawLevelSelectBlock(INT16 x, INT16 y, UINT16 map, boolean redblink, boolean greyscale) { UINT8 *colormap = NULL; @@ -3426,6 +3428,49 @@ static void M_DrawLevelSelectBlock(INT16 x, INT16 y, UINT16 map, boolean redblin ) ); } + else if (!levellist.netgame) + { + x += 80+2; + y += 50-1; + + const boolean allowencore = ( + !levellist.levelsearch.timeattack + && M_SecretUnlocked(SECRET_ENCORE, true) + ); + const boolean allowspb = ( + levellist.levelsearch.timeattack + && M_SecretUnlocked(SECRET_SPBATTACK, true) + ); + + INT32 width = x - M_DrawMapMedals(map, x, y, + levellist.levelsearch.timeattack, + allowencore, + allowspb, + !levellist.levelsearch.timeattack, + false + ); + + if (width > 2) + { + width -= 2; // minor poke + + V_DrawFill(x + 7 - width, y - 2, width, 9, 31); + + UINT8 i; + for (i = 1; i < 7; i++) + { + V_DrawFill(x + 7 - width - i, y + i - 2, 1, 9 - i, 31); + } + + M_DrawMapMedals(map, x, y, + levellist.levelsearch.timeattack, + allowencore, + allowspb, + !levellist.levelsearch.timeattack, + true + ); + } + } } void M_DrawLevelSelect(void) @@ -8028,15 +8073,14 @@ challengedesc: #define STATSSTEP 10 -static void M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y, boolean allowtime, boolean allowencore, boolean allowspb) +static INT32 M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y, boolean allowtime, boolean allowencore, boolean allowspb, boolean allowbonus, boolean draw) { UINT8 lasttype = UINT8_MAX, curtype; // M_GetLevelEmblems is ONE-indexed, urgh emblem_t *emblem = M_GetLevelEmblems(mapnum+1); - const boolean hasmedals = (emblem != NULL); - boolean collected = false; + boolean collected = false, hasmedals = false; while (emblem) { @@ -8083,11 +8127,19 @@ static void M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y, boolean allowtime, b } // Shift over if emblem is of a different discipline - if (lasttype != UINT8_MAX && lasttype != curtype) - x -= 4; - lasttype = curtype; + if (lasttype != curtype) + { + if (lasttype != UINT8_MAX) + x -= 4; + else + hasmedals = true; - if (collected) + lasttype = curtype; + } + + if (!draw) + ; + else if (collected) V_DrawMappedPatch(x, y, 0, W_CachePatchName(M_GetEmblemPatch(emblem, false), PU_CACHE), R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(emblem), GTC_MENUCACHE)); else @@ -8097,6 +8149,9 @@ static void M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y, boolean allowtime, b x -= 8; } + if (!allowbonus) + return x; + if (hasmedals) x -= 4; @@ -8104,7 +8159,7 @@ static void M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y, boolean allowtime, b { UINT16 col = gamedata->spraycans[mapheaderinfo[mapnum]->cache_spraycan].col; - if (col < numskincolors) + if (draw && col < numskincolors) { V_DrawMappedPatch(x, y, 0, W_CachePatchName("GOTCAN", PU_CACHE), R_GetTranslationColormap(TC_DEFAULT, col, GTC_MENUCACHE)); @@ -8115,9 +8170,13 @@ static void M_DrawMapMedals(INT32 mapnum, INT32 x, INT32 y, boolean allowtime, b if (mapheaderinfo[mapnum]->records.mapvisited & MV_MYSTICMELODY) { - V_DrawScaledPatch(x, y, 0, W_CachePatchName("GOTMEL", PU_CACHE)); + if (draw) + V_DrawScaledPatch(x, y, 0, W_CachePatchName("GOTMEL", PU_CACHE)); + x -= 8; } + + return x; } static void M_DrawStatsMaps(void) @@ -8284,7 +8343,7 @@ static void M_DrawStatsMaps(void) ); } - M_DrawMapMedals(mnum, medalspos - 8, y, allowtime, allowencore, allowspb); + M_DrawMapMedals(mnum, medalspos - 8, y, allowtime, allowencore, allowspb, true, true); if (mapheaderinfo[mnum]->menuttl[0]) { From 7e484150bd7dad4ec5744db3c3c62186c109849e Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 22 Oct 2024 20:15:48 +0100 Subject: [PATCH 2/7] Cup Select preview: Draw strongly abbreviated Medal list in Time Attack --- src/k_menudraw.c | 108 ++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 106 insertions(+), 2 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 481393f6e..82eaf5406 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -3271,11 +3271,115 @@ void M_DrawCupSelect(void) } INT16 ty = M_EaseWithTransition(Easing_Linear, 5 * 24); - V_DrawFill(0, 146 + ty, BASEVIDWIDTH, 54, 31); - M_DrawCupPreview(146 + ty, &templevelsearch); + y = 146 + ty; + V_DrawFill(0, y, BASEVIDWIDTH, 54, 31); + M_DrawCupPreview(y, &templevelsearch); M_DrawCupTitle(120 - ty, &templevelsearch); + if (templevelsearch.timeattack) + { + if (templevelsearch.cup != &dummy_lostandfound) + { + templevelsearch.checklocked = false; + } + + // The following makes a HUGE assumption that we're + // never going to have more than ~9 Race Courses + // (with Medals) in Lost & Found. Which is almost + // certainly true for Krew, but is very likely to + // be violated by the long tail of modding. To those + // finding this eventually: I'M SORRY ~toast 221024 + + emblem_t *medal_array[CUPCACHE_MAX]; + + i = j = 0; + + INT16 map = M_GetFirstLevelInList(&i, &templevelsearch); + emblem_t *emblem = NULL; + + boolean incj; + + while (map < nummapheaders && j < CUPCACHE_MAX) + { + if (map < basenummapheaders) + { + emblem = M_GetLevelEmblems(map+1); + + medal_array[j] = NULL; + incj = false; + + while (emblem) + { + if (emblem->type == ET_TIME) + { + incj = true; + + if (gamedata->collected[emblem-emblemlocations]) + { + if (!medal_array[j] + || ( + (medal_array[j]->type == ET_TIME) + && (medal_array[j]->tag < emblem->tag) + ) + ) + { + medal_array[j] = emblem; + } + } + } + else if ((emblem->type == ET_MAP) + && (emblem->flags & ME_SPBATTACK)) + { + incj = true; + + if ((gamedata->collected[emblem-emblemlocations]) + && (skullAnimCounter & 2)) + { + medal_array[j] = emblem; + } + } + + emblem = M_GetLevelEmblems(-1); + } + + if (incj) + j++; + } + + map = M_GetNextLevelInList(map, &i, &templevelsearch); + } + + if (j) + { + x = (BASEVIDWIDTH - (j*10))/2 + 1; + y += 2; + + V_DrawFill(x - 4, y, (j*10) + 6, 3, 31); + for (i = 1; i <= 6; i++) + { + V_DrawFill(x + i - 4, y+2+i, (j*10) + 6 - (i*2), 1, 31); + } + + y--; + + for (i = 0; i < j; i++) + { + if (medal_array[i]) + { + V_DrawMappedPatch(x, y, 0, W_CachePatchName(M_GetEmblemPatch(medal_array[i], false), PU_CACHE), + R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(medal_array[i]), GTC_MENUCACHE)); + } + else + { + V_DrawScaledPatch(x, y, 0, W_CachePatchName("NEEDIT", PU_CACHE)); + } + + x += 10; + } + } + } + if (cupgrid.numpages > 1) { x = 3 - (skullAnimCounter/5); From 405c6cb3e2d0c44c955f52fee944827dd363e9ba Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 22 Oct 2024 20:17:51 +0100 Subject: [PATCH 3/7] M_GetConditionString, UC_EMBLEM: Prevent Medal ID out-of-bounds --- src/m_cond.c | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/m_cond.c b/src/m_cond.c index 6399f0eac..bfeaced91 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -2442,10 +2442,12 @@ static const char *M_GetConditionString(condition_t *cn) case UC_EMBLEM: // Requires emblem x to be obtained { - INT32 checkLevel; + INT32 checkLevel = NEXTMAP_INVALID; i = cn->requirement-1; - checkLevel = M_EmblemMapNum(&emblemlocations[i]); + + if (i >= 0 && i < numemblems) + checkLevel = M_EmblemMapNum(&emblemlocations[i]); if (checkLevel >= nummapheaders || !mapheaderinfo[checkLevel] || emblemlocations[i].type == ET_NONE) return va("INVALID MEDAL MAP \"%d:%d\"", cn->requirement, checkLevel); From beacbca72a4e2aa5acfe27cdc3f76e9d576449b0 Mon Sep 17 00:00:00 2001 From: toaster Date: Tue, 22 Oct 2024 20:18:35 +0100 Subject: [PATCH 4/7] M_EmblemMapNum: Free medal string once no longer relevant --- src/m_cond.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/m_cond.c b/src/m_cond.c index bfeaced91..8126254c6 100644 --- a/src/m_cond.c +++ b/src/m_cond.c @@ -3871,7 +3871,7 @@ UINT16 M_UnlockableMapNum(unlockable_t *unlock) UINT16 M_EmblemMapNum(emblem_t *emblem) { - if (emblem->levelCache == NEXTMAP_INVALID) + if (emblem->levelCache == NEXTMAP_INVALID && emblem->level) { UINT16 result = G_MapNumber(emblem->level); @@ -3879,6 +3879,8 @@ UINT16 M_EmblemMapNum(emblem_t *emblem) return result; emblem->levelCache = result; + Z_Free(emblem->level); + emblem->level = NULL; } return emblem->levelCache; From 89afe780dfed2e49bf15c67769cce94bf18e284e Mon Sep 17 00:00:00 2001 From: toaster Date: Thu, 20 Feb 2025 22:32:22 +0000 Subject: [PATCH 5/7] Cup Select preview: Draw Spray Can (+ Ancient Shrine) list in Match Race Expansion of previously implemented UI for Time Attack, but for showing gotten cans/shrines only. Also: - Halved flicker rate for SPB attack medals - Made invalid cups not display Lost & Found's associated medals OR cans/shrines --- src/k_menudraw.c | 102 ++++++++++++++++++++++++++++++++--------------- 1 file changed, 69 insertions(+), 33 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index 82eaf5406..b760b41d4 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -3277,7 +3277,7 @@ void M_DrawCupSelect(void) M_DrawCupTitle(120 - ty, &templevelsearch); - if (templevelsearch.timeattack) + if (templevelsearch.grandprix == false && templevelsearch.cup != NULL) { if (templevelsearch.cup != &dummy_lostandfound) { @@ -3291,56 +3291,83 @@ void M_DrawCupSelect(void) // be violated by the long tail of modding. To those // finding this eventually: I'M SORRY ~toast 221024 - emblem_t *medal_array[CUPCACHE_MAX]; + struct work_array_t { + emblem_t *medal; + UINT16 col; + UINT8 mapvisited; + } work_array[CUPCACHE_MAX]; + + boolean incj = false; + boolean showalternate = (skullAnimCounter & 4); i = j = 0; INT16 map = M_GetFirstLevelInList(&i, &templevelsearch); emblem_t *emblem = NULL; - boolean incj; - while (map < nummapheaders && j < CUPCACHE_MAX) { if (map < basenummapheaders) { - emblem = M_GetLevelEmblems(map+1); - - medal_array[j] = NULL; + emblem = NULL; incj = false; - while (emblem) - { - if (emblem->type == ET_TIME) - { - incj = true; + work_array[j].medal = NULL; + work_array[j].col = UINT16_MAX; + work_array[j].mapvisited = 0; - if (gamedata->collected[emblem-emblemlocations]) + if (templevelsearch.timeattack) + { + emblem = M_GetLevelEmblems(map+1); + + while (emblem) + { + if (emblem->type == ET_TIME) { - if (!medal_array[j] - || ( - (medal_array[j]->type == ET_TIME) - && (medal_array[j]->tag < emblem->tag) - ) - ) + incj = true; + + if (gamedata->collected[emblem-emblemlocations]) { - medal_array[j] = emblem; + if (!work_array[j].medal + || ( + (work_array[j].medal->type == ET_TIME) + && (work_array[j].medal->tag < emblem->tag) + ) + ) + { + work_array[j].medal = emblem; + } } } - } - else if ((emblem->type == ET_MAP) - && (emblem->flags & ME_SPBATTACK)) - { - incj = true; - - if ((gamedata->collected[emblem-emblemlocations]) - && (skullAnimCounter & 2)) + else if ((emblem->type == ET_MAP) + && (emblem->flags & ME_SPBATTACK)) { - medal_array[j] = emblem; + incj = true; + + if ((gamedata->collected[emblem-emblemlocations]) + && showalternate) + { + work_array[j].medal = emblem; + } } + + emblem = M_GetLevelEmblems(-1); + } + } + else + { + if (mapheaderinfo[map]->cache_spraycan < gamedata->numspraycans) + { + work_array[j].col = gamedata->spraycans[mapheaderinfo[map]->cache_spraycan].col; + incj = true; } - emblem = M_GetLevelEmblems(-1); + if ((mapheaderinfo[map]->records.mapvisited & MV_MYSTICMELODY) + && (incj == false || showalternate)) + { + work_array[j].mapvisited |= MV_MYSTICMELODY; + incj = true; + } } if (incj) @@ -3365,10 +3392,19 @@ void M_DrawCupSelect(void) for (i = 0; i < j; i++) { - if (medal_array[i]) + if (work_array[i].medal) { - V_DrawMappedPatch(x, y, 0, W_CachePatchName(M_GetEmblemPatch(medal_array[i], false), PU_CACHE), - R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(medal_array[i]), GTC_MENUCACHE)); + V_DrawMappedPatch(x, y, 0, W_CachePatchName(M_GetEmblemPatch(work_array[i].medal, false), PU_CACHE), + R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(work_array[i].medal), GTC_MENUCACHE)); + } + else if (work_array[i].mapvisited & MV_MYSTICMELODY) + { + V_DrawScaledPatch(x, y, 0, W_CachePatchName("GOTMEL", PU_CACHE)); + } + else if (work_array[i].col < numskincolors) + { + V_DrawMappedPatch(x, y, 0, W_CachePatchName("GOTCAN", PU_CACHE), + R_GetTranslationColormap(TC_DEFAULT, work_array[i].col, GTC_MENUCACHE)); } else { From 5fad786b8fab7377dd2904e24865966059d408ff Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 22 Feb 2025 22:32:15 +0000 Subject: [PATCH 6/7] Second pass at representing stacked information Lots of internal discussion on my last approach, so here's a cleaner alternative - No more offset with two complex flickering graphics on top of each other - Instead, "less common" secondary info is represented by a coloured dot overlaid on the primary icon - Red for SPB Attack, Turquoise for Mystic Melody - Only show Spray Can/Mystic Melody for Race-compatible courses - Show courses where you haven't picked up a Spray Can with a dot --- src/k_menudraw.c | 70 ++++++++++++++++++++++++++++++------------------ 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index b760b41d4..d9ace8e28 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -3294,11 +3294,10 @@ void M_DrawCupSelect(void) struct work_array_t { emblem_t *medal; UINT16 col; - UINT8 mapvisited; + UINT16 dotcol; } work_array[CUPCACHE_MAX]; boolean incj = false; - boolean showalternate = (skullAnimCounter & 4); i = j = 0; @@ -3313,8 +3312,7 @@ void M_DrawCupSelect(void) incj = false; work_array[j].medal = NULL; - work_array[j].col = UINT16_MAX; - work_array[j].mapvisited = 0; + work_array[j].col = work_array[j].dotcol = UINT16_MAX; if (templevelsearch.timeattack) { @@ -3344,29 +3342,27 @@ void M_DrawCupSelect(void) { incj = true; - if ((gamedata->collected[emblem-emblemlocations]) - && showalternate) + if (gamedata->collected[emblem-emblemlocations]) { - work_array[j].medal = emblem; + work_array[j].dotcol = M_GetEmblemColor(emblem); } } emblem = M_GetLevelEmblems(-1); } } - else + else if ((gamedata->gotspraycans > 0) && (mapheaderinfo[map]->typeoflevel & TOL_RACE)) { + incj = true; + if (mapheaderinfo[map]->cache_spraycan < gamedata->numspraycans) { work_array[j].col = gamedata->spraycans[mapheaderinfo[map]->cache_spraycan].col; - incj = true; } - if ((mapheaderinfo[map]->records.mapvisited & MV_MYSTICMELODY) - && (incj == false || showalternate)) + if (mapheaderinfo[map]->records.mapvisited & MV_MYSTICMELODY) { - work_array[j].mapvisited |= MV_MYSTICMELODY; - incj = true; + work_array[j].dotcol = SKINCOLOR_TURQUOISE; } } @@ -3392,23 +3388,45 @@ void M_DrawCupSelect(void) for (i = 0; i < j; i++) { - if (work_array[i].medal) + if (templevelsearch.timeattack) { - V_DrawMappedPatch(x, y, 0, W_CachePatchName(M_GetEmblemPatch(work_array[i].medal, false), PU_CACHE), - R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(work_array[i].medal), GTC_MENUCACHE)); - } - else if (work_array[i].mapvisited & MV_MYSTICMELODY) - { - V_DrawScaledPatch(x, y, 0, W_CachePatchName("GOTMEL", PU_CACHE)); - } - else if (work_array[i].col < numskincolors) - { - V_DrawMappedPatch(x, y, 0, W_CachePatchName("GOTCAN", PU_CACHE), - R_GetTranslationColormap(TC_DEFAULT, work_array[i].col, GTC_MENUCACHE)); + if (work_array[i].medal) + { + // Primary Medal + + V_DrawMappedPatch(x, y, 0, W_CachePatchName(M_GetEmblemPatch(work_array[i].medal, false), PU_CACHE), + R_GetTranslationColormap(TC_DEFAULT, M_GetEmblemColor(work_array[i].medal), GTC_MENUCACHE)); + } + else + { + // Need it! + + V_DrawScaledPatch(x, y, 0, W_CachePatchName("NEEDIT", PU_CACHE)); + } } else { - V_DrawScaledPatch(x, y, 0, W_CachePatchName("NEEDIT", PU_CACHE)); + if (work_array[i].col < numskincolors) + { + // Spray Can + + V_DrawMappedPatch(x, y, 0, W_CachePatchName("GOTCAN", PU_CACHE), + R_GetTranslationColormap(TC_DEFAULT, work_array[i].col, GTC_MENUCACHE)); + } + else + { + // Need it! + + V_DrawFill(x+3, y+3, 2, 2, 20); + } + } + + if (work_array[i].dotcol < numskincolors) + { + // Bonus (Secondary Medal or Ancient Shrine) + + V_DrawMappedPatch(x+4, y+7, 0, W_CachePatchName("COLORSP1", PU_CACHE), + R_GetTranslationColormap(TC_DEFAULT, work_array[i].dotcol, GTC_MENUCACHE)); } x += 10; From a831fc3592f0af730b010ef60bd7cca0782908da Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 22 Feb 2025 23:28:08 +0000 Subject: [PATCH 7/7] Adjust shade of no-Can dot to avoid matching any 2x2 region on the existing sprite --- src/k_menudraw.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/k_menudraw.c b/src/k_menudraw.c index d9ace8e28..3a607c40f 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -3417,7 +3417,7 @@ void M_DrawCupSelect(void) { // Need it! - V_DrawFill(x+3, y+3, 2, 2, 20); + V_DrawFill(x+3, y+3, 2, 2, 6); } }