From 7178ae5916afdf233127096a3696ddb412be03e0 Mon Sep 17 00:00:00 2001 From: toasterbabe Date: Fri, 27 Jan 2017 14:42:57 +0000 Subject: [PATCH] Hefty refactor. * Named all the new functions/etc "Platter" instead of "NewList", to make maintenence easier and also because it sounds delicious. * The code, as a whole, is much cleaner. * Allows for multiple items under the same heading now, with highlights and spacing consistently handled. * Allows for picking up where you left off between menus now (assuming the same map is available on multiple screens). * Defined M_CanShowLevelInList in terms of the two compartmentalised functions cleaved from it, in order to ensure consistency and prevent code duplication. --- src/m_menu.c | 309 ++++++++++++++++++++++++--------------------------- src/m_menu.h | 3 +- 2 files changed, 146 insertions(+), 166 deletions(-) diff --git a/src/m_menu.c b/src/m_menu.c index 072c29e84..97f396207 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -309,7 +309,7 @@ static void M_DrawChecklist(void); static void M_DrawEmblemHints(void); static void M_DrawPauseMenu(void); static void M_DrawServerMenu(void); -static void M_DrawLevelSelectMenu(void); +static void M_DrawLevelPlatterMenu(void); static void M_DrawImageDef(void); static void M_DrawLoad(void); static void M_DrawLevelStats(void); @@ -338,6 +338,7 @@ static boolean M_CancelConnect(void); #endif static boolean M_ExitPandorasBox(void); static boolean M_QuitMultiPlayerMenu(void); +static void M_HandleLevelPlatter(INT32 choice); static void M_HandleSoundTest(INT32 choice); static void M_HandleImageDef(INT32 choice); static void M_HandleLoadSave(INT32 choice); @@ -347,7 +348,6 @@ static void M_HandleLevelStats(INT32 choice); static void M_HandleConnectIP(INT32 choice); #endif static void M_HandleSetupMultiPlayer(INT32 choice); -static void M_HandleNewLevelSelect(INT32 choice); #ifdef HWRENDER static void M_HandleFogColor(INT32 choice); #endif @@ -644,7 +644,7 @@ static menuitem_t SR_MainMenu[] = static menuitem_t SR_LevelSelectMenu[] = { - {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleNewLevelSelect, '\0'}, // dummy menuitem for the control func + {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelPlatter, '\0'}, // dummy menuitem for the control func /* {IT_STRING|IT_CVAR, NULL, "Level", &cv_nextmap, 60}, {IT_WHITESTRING|IT_CALL, NULL, "Start", M_LevelSelectWarp, 120},*/ @@ -1437,7 +1437,7 @@ menu_t SR_LevelSelectDef = sizeof (SR_LevelSelectMenu)/sizeof (menuitem_t), &SR_MainDef, SR_LevelSelectMenu, - M_DrawLevelSelectMenu, + M_DrawLevelPlatterMenu, 40, 40, 0, NULL @@ -3443,38 +3443,36 @@ static void M_PatchSkinNameTable(void) // Handle Level Select static levelselect_t levelselect = {0, NULL}; -static UINT8 levelselectselect[3]; +static UINT8 levelselectselect[4]; static patch_t *levselp[4]; static INT32 lsoffs[2]; #define lsrow levelselectselect[0] #define lscol levelselectselect[1] #define lstic levelselectselect[2] +#define lshli levelselectselect[3] #define hseperation 101 -#define vseperation 82 +#define basevseperation 62 +#define headingheight 16 +#define getheadingoffset(row) (levelselect.rows[row].header[0] ? headingheight : 0) +#define vseperation(row) basevseperation + getheadingoffset(row) -static boolean M_LevelUnlockedInNewList(INT32 mapnum) +// +// M_LevelAvailableOnPlatter +// +// Okay, you know that the level SHOULD show up on the platter already. +// The only question is whether it should be as a question mark, +// (hinting as to its existence), or as its pure, unfettered self. +// +static boolean M_LevelAvailableOnPlatter(INT32 mapnum) { if (M_MapLocked(mapnum+1)) return false; // not unlocked switch (levellistmode) { - case LLM_CREATESERVER: - return true; - - case LLM_LEVELSELECT: - return true; - case LLM_RECORDATTACK: - if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) - return true; - - if (!mapvisited[mapnum]) - return false; - - return true; case LLM_NIGHTSATTACK: if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) return true; @@ -3482,18 +3480,22 @@ static boolean M_LevelUnlockedInNewList(INT32 mapnum) if (!mapvisited[mapnum]) return false; + // intentional fallthrough + case LLM_CREATESERVER: + case LLM_LEVELSELECT: + default: return true; } return true; } // -// M_CanShowLevelInNewList +// M_CanShowLevelOnPlatter // // Determines whether to show a given map in the various level-select lists. // Set gt = -1 to ignore gametype. // -boolean M_CanShowLevelInNewList(INT32 mapnum, INT32 gt) +static boolean M_CanShowLevelOnPlatter(INT32 mapnum, INT32 gt) { // Does the map exist? if (!mapheaderinfo[mapnum]) @@ -3542,23 +3544,11 @@ boolean M_CanShowLevelInNewList(INT32 mapnum, INT32 gt) if (!(mapheaderinfo[mapnum]->menuflags & LF2_RECORDATTACK)) return false; - /*if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) - return true; - - if (!mapvisited[mapnum]) - return false;*/ - return true; case LLM_NIGHTSATTACK: if (!(mapheaderinfo[mapnum]->menuflags & LF2_NIGHTSATTACK)) return false; - /*if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) - return true; - - if (!mapvisited[mapnum]) - return false;*/ - return true; } @@ -3566,24 +3556,24 @@ boolean M_CanShowLevelInNewList(INT32 mapnum, INT32 gt) return false; } -/*static INT32 M_CountLevelsToShowInNewList(INT32 gt) +/*static INT32 M_CountLevelsToShowOnPlatter(INT32 gt) { INT32 mapnum, count = 0; for (mapnum = 0; mapnum < NUMMAPS; mapnum++) - if (M_CanShowLevelInList(mapnum, gt)) + if (M_CanShowLevelInPlatter(mapnum, gt)) count++; return count; }*/ -static INT32 M_CountRowsToShowInNewList(INT32 gt) +static INT32 M_CountRowsToShowOnPlatter(INT32 gt) { INT32 mapnum, prevmapnum, col = 0, rows = 0; for (mapnum = 0; mapnum < NUMMAPS; mapnum++) { - if (M_CanShowLevelInNewList(mapnum, gt)) + if (M_CanShowLevelOnPlatter(mapnum, gt)) { if (rows == 0) rows++; @@ -3605,9 +3595,9 @@ static INT32 M_CountRowsToShowInNewList(INT32 gt) return rows; } -static boolean M_PrepareNewLevelSelect(INT32 gt) +static boolean M_PrepareLevelPlatter(INT32 gt) { - INT32 numrows = M_CountRowsToShowInNewList(gt); + INT32 numrows = M_CountRowsToShowOnPlatter(gt); INT32 mapnum, col = 0, row = 0; if (!numrows) @@ -3620,13 +3610,17 @@ static boolean M_PrepareNewLevelSelect(INT32 gt) levelselect.numrows = numrows; levelselect.rows = Z_Realloc(levelselect.rows, numrows*sizeof(levelselectrow_t), PU_STATIC, NULL); if (!levelselect.rows) - I_Error("Insufficient memory to prepare level select platter"); + I_Error("Insufficient memory to prepare level platter"); + + // done here so lsrow and lscol can be set if cv_nextmap is on the platter + lsrow = lscol = lstic = lshli = lsoffs[0] = lsoffs[1] = 0; for (mapnum = 0; mapnum < NUMMAPS; mapnum++) { - if (M_CanShowLevelInNewList(mapnum, gt)) + if (M_CanShowLevelOnPlatter(mapnum, gt)) { const INT32 actnum = mapheaderinfo[mapnum]->actnum; + const boolean headingisname = (fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[mapnum]->lvlttl)); // preparing next position to drop mapnum into if (levelselect.rows[0].maplist[0]) @@ -3642,27 +3636,33 @@ static boolean M_PrepareNewLevelSelect(INT32 gt) } levelselect.rows[row].maplist[col] = mapnum+1; // putting the map on the platter - levelselect.rows[row].mapavailable[col] = M_LevelUnlockedInNewList(mapnum); + levelselect.rows[row].mapavailable[col] = M_LevelAvailableOnPlatter(mapnum); + + if (cv_nextmap.value == mapnum+1) // A little quality of life improvement. + { + lsrow = row; + lscol = col; + } // individual map name if (!levelselect.rows[row].mapavailable[col]) sprintf(levelselect.rows[row].mapnames[col], "???"); else if (actnum) sprintf(levelselect.rows[row].mapnames[col], "ACT %d", actnum); + else if (headingisname) + sprintf(levelselect.rows[row].mapnames[col], "THE ACT", actnum); else - { sprintf(levelselect.rows[row].mapnames[col], "%s", mapheaderinfo[mapnum]->lvlttl); - } // creating header text - if (!col && (!row || (fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[levelselect.rows[row].maplist[0]-1]->selectheading)))) + if (!col && (!row || !(fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[levelselect.rows[row-1].maplist[0]-1]->selectheading)))) { if (!levelselect.rows[row].mapavailable[col]) sprintf(levelselect.rows[row].header, "???"); else { sprintf(levelselect.rows[row].header, "%s", mapheaderinfo[mapnum]->selectheading); - if (!(mapheaderinfo[mapnum]->levelflags & LF_NOZONE) && (fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[mapnum]->lvlttl))) + if (!(mapheaderinfo[mapnum]->levelflags & LF_NOZONE) && headingisname) { sprintf(levelselect.rows[row].header + strlen(levelselect.rows[row].header), " ZONE"); } @@ -3671,8 +3671,6 @@ static boolean M_PrepareNewLevelSelect(INT32 gt) } } - lsrow = lscol = lstic = lsoffs[0] = lsoffs[1] = 0; - if (levselp[0]) // never going to have some provided but not all, saves individually checking { W_UnlockCachedPatch(levselp[0]); @@ -3689,7 +3687,14 @@ static boolean M_PrepareNewLevelSelect(INT32 gt) return true; } -static void M_HandleNewLevelSelect(INT32 choice) +#define selectvalnextmapnobrace(column) selectval = levelselect.rows[lsrow].maplist[column];\ + if (selectval && levelselect.rows[lsrow].mapavailable[column])\ + {\ + CV_SetValue(&cv_nextmap, selectval); + +#define selectvalnextmap(column) selectvalnextmapnobrace(column)} + +static void M_HandleLevelPlatter(INT32 choice) { boolean exitmenu = false; // exit to previous menu INT32 selectval; @@ -3700,24 +3705,50 @@ static void M_HandleNewLevelSelect(INT32 choice) lsrow++; if (lsrow == levelselect.numrows) lsrow = 0; - lsoffs[0] = vseperation; + + lsoffs[0] = vseperation(lsrow); + + if (levelselect.rows[lsrow].header[0]) + lshli = lsrow; + // no else needed - headerless lines associate upwards, so moving down to a row without a header is identity + S_StartSound(NULL,sfx_s3kb7); + + selectvalnextmap(lscol) else selectvalnextmap(0) break; case KEY_UPARROW: + lsoffs[0] = -vseperation(lsrow); + lsrow--; if (lsrow == UINT8_MAX) lsrow = levelselect.numrows-1; - lsoffs[0] = -vseperation; + + if (levelselect.rows[lsrow].header[0]) + lshli = lsrow; + else + { + UINT8 iter = lsrow; + do + iter = ((iter == 0) ? levelselect.numrows-1 : iter-1); + while ((iter != lsrow) && !(levelselect.rows[iter].header[0])); + lshli = iter; + } + S_StartSound(NULL,sfx_s3kb7); + + selectvalnextmap(lscol) else selectvalnextmap(0) break; case KEY_LEFTARROW: if (lscol > 0) { lscol--; + lsoffs[1] = hseperation; S_StartSound(NULL,sfx_s3kb7); + + selectvalnextmap(lscol) else selectvalnextmap(0) } break; @@ -3725,20 +3756,23 @@ static void M_HandleNewLevelSelect(INT32 choice) if (lscol < 2) { lscol++; + lsoffs[1] = -hseperation; S_StartSound(NULL,sfx_s3kb7); + + selectvalnextmap(lscol) else selectvalnextmap(0) } break; case KEY_ENTER: - selectval = levelselect.rows[lsrow].maplist[lscol]; - if (selectval && levelselect.rows[lsrow].mapavailable[lscol]) - { - CV_SetValue(&cv_nextmap, selectval); + selectvalnextmapnobrace(lscol) + M_LevelSelectWarp(0); + + lsoffs[0] = lsoffs[1] = 0; S_StartSound(NULL,sfx_menu1); } - else + else if (!lsoffs[0]) // prevent sound spam { lsoffs[0] = -8; S_StartSound(NULL,sfx_s3kb2); @@ -3762,21 +3796,29 @@ static void M_HandleNewLevelSelect(INT32 choice) } } -static void M_DrawLevelSelectRow(UINT8 row, INT32 y) +static void M_DrawLevelPlatterRow(UINT8 row, INT32 y) { UINT8 col; - const boolean highlight = (row == lsrow); - y -= 16; + const boolean rowhighlight = (row == lsrow); if (levelselect.rows[row].header[0]) { - V_DrawString(19, y-4, (highlight ? V_YELLOWMAP : 0), levelselect.rows[row].header); - if ((y > 0) && (y < 200)) + const boolean headerhighlight = (rowhighlight || (row == lshli)); + + y += headingheight - 12; + V_DrawString(19, y, (headerhighlight ? V_YELLOWMAP : 0), levelselect.rows[row].header); + y += 9; + if ((y >= 0) && (y < 200)) { - V_DrawFill(19, y+5, 282, 2, 26); - V_DrawFill(19, y+5, 281, 1, (highlight ? yellowmap[3] : 3)); + V_DrawFill(19, y, 281, 1, (headerhighlight ? yellowmap[3] : 3)); + V_DrawFill(300, y, 1, 1, 26); } + y++; + if ((y >= 0) && (y < 200)) + { + V_DrawFill(19, y, 282, 1, 26); + } + y += 2; } - y += 8; for (col = 0; col < 3; col++) { INT32 x = 19+(col*hseperation); @@ -3795,50 +3837,66 @@ static void M_DrawLevelSelectRow(UINT8 row, INT32 y) patch = levselp[2]; // don't flash to indicate that it's just a normal level V_DrawSmallScaledPatch(x, y, 0, patch); - W_UnlockCachedPatch(patch); - if (strlen(levelselect.rows[row].mapnames[col]) > 6) // "EGG ROCK CORE" - V_DrawThinString(x, y+50, ((highlight && col == lscol) ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]); - else // "ACT 19" - V_DrawString(x, y+50, ((highlight && col == lscol) ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]); + if (strlen(levelselect.rows[row].mapnames[col]) > 6) // "AERIAL GARDEN" vs "ACT 18" - "THE ACT" intentionally compressed + V_DrawThinString(x, y+50, ((rowhighlight && col == lscol) ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]); + else + V_DrawString(x, y+50, ((rowhighlight && col == lscol) ? V_YELLOWMAP : 0), levelselect.rows[row].mapnames[col]); } } -static void M_DrawLevelSelectMenu(void) +#define basey 59+headingheight + +static void M_DrawLevelPlatterMenu(void) { - UINT8 prev = ((lsrow == 0) ? levelselect.numrows-1 : lsrow-1); - UINT8 next = ((lsrow == levelselect.numrows-1) ? 0 : lsrow+1); + UINT8 iter = lsrow; + INT32 y = basey + lsoffs[0] - getheadingoffset(lsrow); if (++lstic == 32) lstic = 0; - M_DrawLevelSelectRow(prev, lsoffs[0]); - M_DrawLevelSelectRow(lsrow, vseperation + lsoffs[0]); - M_DrawLevelSelectRow(next, 2*vseperation + lsoffs[0]); + // finds row at top of the screen + while (y > 0) + { + iter = ((iter == 0) ? levelselect.numrows-1 : iter-1); + y -= vseperation(iter); + } - if (lsoffs[0] > vseperation/3) - M_DrawLevelSelectRow( ((prev == 0) ? levelselect.numrows-1 : prev-1), -vseperation + lsoffs[0]); - else if (lsoffs[0] < -vseperation/3) - M_DrawLevelSelectRow( ((next == levelselect.numrows-1) ? 0 : next+1), 3*vseperation + lsoffs[0]); + // draw from top to bottom + while (y < 200) + { + M_DrawLevelPlatterRow(iter, y); + y += vseperation(iter); + iter = ((iter == levelselect.numrows-1) ? 0 : iter+1); + } + // handle movement of cursor box if (abs(lsoffs[0]) > 1) lsoffs[0] = 2*lsoffs[0]/3; else lsoffs[0] = 0; if (abs(lsoffs[1]) > 1) - lsoffs[1] >>= 2; + lsoffs[1] = 2*lsoffs[1]/3; else lsoffs[1] = 0; - V_DrawSmallScaledPatch(19+(lscol*hseperation) + lsoffs[1], vseperation-8, 0, ((lstic & 8) ? levselp[0] : levselp[1])); + // draw cursor box + V_DrawSmallScaledPatch(19+(lscol*hseperation) + lsoffs[1], basey, 0, ((lstic & 8) ? levselp[0] : levselp[1])); } -#undef hseperation -#undef vseperation +#undef basey #undef lsrow #undef lscol +#undef lstic +#undef lshli + +#undef hseperation +#undef basevseperation +#undef headingheight +#undef getheadingoffset +#undef vseperation // Call before showing any level-select menus static void M_PrepareLevelSelect(void) @@ -3852,89 +3910,12 @@ static void M_PrepareLevelSelect(void) // // M_CanShowLevelInList // -// Determines whether to show a given map in the various level-select lists. +// Determines whether to show a given map in level-select lists where you don't want to see locked levels. // Set gt = -1 to ignore gametype. // boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt) { - // Does the map exist? - if (!mapheaderinfo[mapnum]) - return false; - - // Does the map have a name? - if (!mapheaderinfo[mapnum]->lvlttl[0]) - return false; - - switch (levellistmode) - { - case LLM_CREATESERVER: - // Should the map be hidden? - if (mapheaderinfo[mapnum]->menuflags & LF2_HIDEINMENU) - return false; - - if (M_MapLocked(mapnum+1)) - return false; // not unlocked - - if (gt == GT_COOP && (mapheaderinfo[mapnum]->typeoflevel & TOL_COOP)) - return true; - - if (gt == GT_COMPETITION && (mapheaderinfo[mapnum]->typeoflevel & TOL_COMPETITION)) - return true; - - if (gt == GT_CTF && (mapheaderinfo[mapnum]->typeoflevel & TOL_CTF)) - return true; - - if ((gt == GT_MATCH || gt == GT_TEAMMATCH) && (mapheaderinfo[mapnum]->typeoflevel & TOL_MATCH)) - return true; - - if ((gt == GT_TAG || gt == GT_HIDEANDSEEK) && (mapheaderinfo[mapnum]->typeoflevel & TOL_TAG)) - return true; - - if (gt == GT_RACE && (mapheaderinfo[mapnum]->typeoflevel & TOL_RACE)) - return true; - - return false; - - case LLM_LEVELSELECT: - if (mapheaderinfo[mapnum]->levelselect != maplistoption) - return false; - - if (M_MapLocked(mapnum+1)) - return false; // not unlocked - - return true; - case LLM_RECORDATTACK: - if (!(mapheaderinfo[mapnum]->menuflags & LF2_RECORDATTACK)) - return false; - - if (M_MapLocked(mapnum+1)) - return false; // not unlocked - - if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) - return true; - - if (!mapvisited[mapnum]) - return false; - - return true; - case LLM_NIGHTSATTACK: - if (!(mapheaderinfo[mapnum]->menuflags & LF2_NIGHTSATTACK)) - return false; - - if (M_MapLocked(mapnum+1)) - return false; // not unlocked - - if (mapheaderinfo[mapnum]->menuflags & LF2_NOVISITNEEDED) - return true; - - if (!mapvisited[mapnum]) - return false; - - return true; - } - - // Hmm? Couldn't decide? - return false; + return (M_CanShowLevelOnPlatter(mapnum, gt) && M_LevelAvailableOnPlatter(mapnum)); } static INT32 M_CountLevelsToShowInList(void) @@ -4669,7 +4650,7 @@ static void M_CustomLevelSelect(INT32 choice) SR_LevelSelectDef.prevMenu = currentMenu; levellistmode = LLM_LEVELSELECT; maplistoption = (UINT8)(unlockables[ul].variable); - if (!M_PrepareNewLevelSelect(-1)) + if (!M_PrepareLevelPlatter(-1)) { M_StartMessage(M_GetText("No selectable levels found.\n"),NULL,MM_NOTHING); return; diff --git a/src/m_menu.h b/src/m_menu.h index 76eabd729..5c07d8ae8 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -66,9 +66,8 @@ void M_StartMessage(const char *string, void *routine, menumessagetype_t itemtyp // Called by linux_x/i_video_xshm.c void M_QuitResponse(INT32 ch); -// Determines whether to show a level in the list +// Determines whether to show a level in the list (platter version does not need to be exposed) boolean M_CanShowLevelInList(INT32 mapnum, INT32 gt); -boolean M_CanShowLevelInNewList(INT32 mapnum, INT32 gt); // flags for items in the menu