From 17e45d92c5b742f00bd7ace3618a4dcaf55751c5 Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Fri, 19 Nov 2021 23:58:35 +0100 Subject: [PATCH] Pause: addons menu --- src/k_menu.h | 20 +++ src/k_menudef.c | 25 +++- src/k_menudraw.c | 227 ++++++++++++++++++++++++++++++++- src/k_menufunc.c | 326 +++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 592 insertions(+), 6 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index 20dfcab17..713b37837 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -199,6 +199,9 @@ extern menu_t PLAY_BattleGamemodesDef; extern menuitem_t PAUSE_Main[]; extern menu_t PAUSE_MainDef; +extern menuitem_t MISC_Addons[]; +extern menu_t MISC_AddonsDef; + // We'll need this since we're gonna have to dynamically enable and disable options depending on which state we're in. typedef enum { @@ -471,8 +474,20 @@ void M_PlaybackQuit(INT32 choice); void M_ReplayHut(INT32 choice); +// Misc menus: +#define numaddonsshown 4 +void M_Addons(INT32 choice); +boolean M_AddonsRefresh(void); +void M_HandleAddons(INT32 choice); +char *M_AddonsHeaderPath(void); + // M_MENUDRAW.C +// flags for text highlights +#define highlightflags V_ORANGEMAP +#define recommendedflags V_GREENMAP +#define warningflags V_GRAYMAP + void M_UpdateMenuBGImage(boolean forceReset); void M_DrawMenuBackground(void); void M_DrawMenuForeground(void); @@ -501,6 +516,11 @@ void M_DrawPause(void); // Replay Playback void M_DrawPlaybackMenu(void); +// Misc menus: +#define LOCATIONSTRING1 "Visit \x83SRB2.ORG/MODS\x80 to get & make addons!" +#define LOCATIONSTRING2 "Visit \x88SRB2.ORG/MODS\x80 to get & make addons!" +void M_DrawAddons(void); + // These defines make it a little easier to make menus #define DEFAULTMENUSTYLE(source, prev, x, y)\ {\ diff --git a/src/k_menudef.c b/src/k_menudef.c index 55beeac82..f4f6fe7c3 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -303,7 +303,7 @@ menuitem_t PAUSE_Main[] = { {IT_STRING | IT_CALL, "ADDONS", "M_ICOADD", - NULL, NULL, 0, 0}, + NULL, M_Addons, 0, 0}, {IT_STRING | IT_SUBMENU, "CHANGE MAP", "M_ICOMAP", NULL, &PAUSE_GamemodesDef, 0, 0}, @@ -399,3 +399,26 @@ menu_t PAUSE_PlaybackMenuDef = { NULL, NULL }; + + +// Other misc menus: + +// Addons menu! (Just a straight port for now) +menuitem_t MISC_AddonsMenu[] = +{ + {IT_KEYHANDLER | IT_NOTHING, NULL, NULL, + NULL, M_HandleAddons, 0, 0}, // dummy menuitem for the control func +}; + +menu_t MISC_AddonsDef = { + sizeof (MISC_AddonsMenu)/sizeof (menuitem_t), + NULL, + 0, + MISC_AddonsMenu, + 50, 28, + 0, 0, + M_DrawAddons, + NULL, + NULL, + NULL +}; diff --git a/src/k_menudraw.c b/src/k_menudraw.c index d6efce47d..ec6d9af64 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -71,11 +71,6 @@ int snprintf(char *str, size_t n, const char *fmt, ...); //int vsnprintf(char *str, size_t n, const char *fmt, va_list ap); #endif -// flags for text highlights -#define highlightflags V_ORANGEMAP -#define recommendedflags V_GREENMAP -#define warningflags V_GRAYMAP - #define SKULLXOFF -32 #define LINEHEIGHT 16 #define STRINGHEIGHT 8 @@ -85,6 +80,8 @@ int snprintf(char *str, size_t n, const char *fmt, ...); #define SLIDER_WIDTH (8*SLIDER_RANGE+6) #define SERVERS_PER_PAGE 11 +static patch_t *addonsp[NUM_EXT+5]; + static UINT32 bgTextScroll = 0; static UINT32 bgImageScroll = 0; static char bgImageName[9]; @@ -1877,3 +1874,223 @@ void M_DrawPlaybackMenu(void) } } } + + +// Draw misc menus: + +// Addons (this is merely copypasted, original code by toaster) + +#define lsheadingheight 16 + +#define width 4 +#define vpadding 27 +#define h (BASEVIDHEIGHT-(2*vpadding)) +#define NUMCOLOURS 8 // when toast's coding it's british english hacker fucker +static void M_DrawTemperature(INT32 x, fixed_t t) +{ + INT32 y; + + // bounds check + if (t > FRACUNIT) + t = FRACUNIT; + /*else if (t < 0) -- not needed + t = 0;*/ + + // scale + if (t > 1) + t = (FixedMul(h<>FRACBITS); + + // border + V_DrawFill(x - 1, vpadding, 1, h, 0); + V_DrawFill(x + width, vpadding, 1, h, 0); + V_DrawFill(x - 1, vpadding-1, width+2, 1, 0); + V_DrawFill(x - 1, vpadding+h, width+2, 1, 0); + + // bar itself + y = h; + if (t) + for (t = h - t; y > 0; y--) + { + UINT8 colours[NUMCOLOURS] = {135, 133, 92, 77, 114, 178, 161, 162}; + UINT8 c; + if (y <= t) break; + if (y+vpadding >= BASEVIDHEIGHT/2) + c = 185; + else + c = colours[(NUMCOLOURS*(y-1))/(h/2)]; + V_DrawFill(x, y-1 + vpadding, width, 1, c); + } + + // fill the rest of the backing + if (y) + V_DrawFill(x, vpadding, width, y, 30); +} +#undef width +#undef vpadding +#undef h +#undef NUMCOLOURS + + +// Just do this here instead. +static void M_CacheAddonPatches(void) +{ + addonsp[EXT_FOLDER] = W_CachePatchName("M_FFLDR", PU_STATIC); + addonsp[EXT_UP] = W_CachePatchName("M_FBACK", PU_STATIC); + addonsp[EXT_NORESULTS] = W_CachePatchName("M_FNOPE", PU_STATIC); + addonsp[EXT_TXT] = W_CachePatchName("M_FTXT", PU_STATIC); + addonsp[EXT_CFG] = W_CachePatchName("M_FCFG", PU_STATIC); + addonsp[EXT_WAD] = W_CachePatchName("M_FWAD", PU_STATIC); + #ifdef USE_KART + addonsp[EXT_KART] = W_CachePatchName("M_FKART", PU_STATIC); + #endif + addonsp[EXT_PK3] = W_CachePatchName("M_FPK3", PU_STATIC); + addonsp[EXT_SOC] = W_CachePatchName("M_FSOC", PU_STATIC); + addonsp[EXT_LUA] = W_CachePatchName("M_FLUA", PU_STATIC); + addonsp[NUM_EXT] = W_CachePatchName("M_FUNKN", PU_STATIC); + addonsp[NUM_EXT+1] = W_CachePatchName("M_FSEL", PU_STATIC); + addonsp[NUM_EXT+2] = W_CachePatchName("M_FLOAD", PU_STATIC); + addonsp[NUM_EXT+3] = W_CachePatchName("M_FSRCH", PU_STATIC); + addonsp[NUM_EXT+4] = W_CachePatchName("M_FSAVE", PU_STATIC); +} + + +void M_DrawAddons(void) +{ + INT32 x, y; + ssize_t i, m; + const UINT8 *flashcol = NULL; + UINT8 hilicol; + + M_CacheAddonPatches(); + + // hack - need to refresh at end of frame to handle addfile... + if (refreshdirmenu & M_AddonsRefresh()) + { + M_DrawMessageMenu(); + return; + } + + if (Playing()) + V_DrawCenteredString(BASEVIDWIDTH/2, 5, warningflags, "Adding files mid-game may cause problems."); + else + V_DrawCenteredString(BASEVIDWIDTH/2, 5, 0, (recommendedflags == V_SKYMAP ? LOCATIONSTRING2 : LOCATIONSTRING1)); + + if (numwadfiles <= mainwads+1) + y = 0; + else if (numwadfiles >= MAX_WADFILES) + y = FRACUNIT; + else + { + y = FixedDiv(((ssize_t)(numwadfiles) - (ssize_t)(mainwads+1))< FRACUNIT) // happens because of how we're shrinkin' it a little + y = FRACUNIT; + } + + M_DrawTemperature(BASEVIDWIDTH - 19 - 5, y); + + // DRAW MENU + x = currentMenu->x; + y = currentMenu->y + 1; + + hilicol = V_GetStringColormap(highlightflags)[0]; + + V_DrawString(x-21, (y - 16) + (lsheadingheight - 12), highlightflags|V_ALLOWLOWERCASE, M_AddonsHeaderPath()); + V_DrawFill(x-21, (y - 16) + (lsheadingheight - 3), MAXSTRINGLENGTH*8+6, 1, hilicol); + V_DrawFill(x-21, (y - 16) + (lsheadingheight - 2), MAXSTRINGLENGTH*8+6, 1, 30); + + m = (BASEVIDHEIGHT - currentMenu->y + 2) - (y - 1); + V_DrawFill(x - 21, y - 1, MAXSTRINGLENGTH*8+6, m, 159); + + // scrollbar! + if (sizedirmenu <= (2*numaddonsshown + 1)) + i = 0; + else + { + ssize_t q = m; + m = ((2*numaddonsshown + 1) * m)/sizedirmenu; + if (dir_on[menudepthleft] <= numaddonsshown) // all the way up + i = 0; + else if (sizedirmenu <= (dir_on[menudepthleft] + numaddonsshown + 1)) // all the way down + i = q-m; + else + i = ((dir_on[menudepthleft] - numaddonsshown) * (q-m))/(sizedirmenu - (2*numaddonsshown + 1)); + } + + V_DrawFill(x + MAXSTRINGLENGTH*8+5 - 21, (y - 1) + i, 1, m, hilicol); + + // get bottom... + m = dir_on[menudepthleft] + numaddonsshown + 1; + if (m > (ssize_t)sizedirmenu) + m = sizedirmenu; + + // then compute top and adjust bottom if needed! + if (m < (2*numaddonsshown + 1)) + { + m = min(sizedirmenu, 2*numaddonsshown + 1); + i = 0; + } + else + i = m - (2*numaddonsshown + 1); + + if (i != 0) + V_DrawString(19, y+4 - (skullAnimCounter/5), highlightflags, "\x1A"); + + if (skullAnimCounter < 4) + flashcol = V_GetStringColormap(highlightflags); + + for (; i < m; i++) + { + UINT32 flags = V_ALLOWLOWERCASE; + if (y > BASEVIDHEIGHT) break; + if (dirmenu[i]) +#define type (UINT8)(dirmenu[i][DIR_TYPE]) + { + if (type & EXT_LOADED) + { + flags |= V_TRANSLUCENT; + V_DrawSmallScaledPatch(x-(16+4), y, V_TRANSLUCENT, addonsp[(type & ~EXT_LOADED)]); + V_DrawSmallScaledPatch(x-(16+4), y, 0, addonsp[NUM_EXT+2]); + } + else + V_DrawSmallScaledPatch(x-(16+4), y, 0, addonsp[(type & ~EXT_LOADED)]); + + if ((size_t)i == dir_on[menudepthleft]) + { + V_DrawFixedPatch((x-(16+4))< (charsonside*2 + 3)) + V_DrawString(x, y+4, flags, va("%.*s...%s", charsonside, dirmenu[i]+DIR_STRING, dirmenu[i]+DIR_STRING+dirmenu[i][DIR_LEN]-(charsonside+1))); +#undef charsonside + else + V_DrawString(x, y+4, flags, dirmenu[i]+DIR_STRING); + } +#undef type + y += 16; + } + + if (m != (ssize_t)sizedirmenu) + V_DrawString(19, y-12 + (skullAnimCounter/5), highlightflags, "\x1B"); + + y = BASEVIDHEIGHT - currentMenu->y + 1; + + M_DrawTextBox(x - (21 + 5), y, MAXSTRINGLENGTH, 1); + if (menusearch[0]) + V_DrawString(x - 18, y + 8, V_ALLOWLOWERCASE, menusearch+1); + else + V_DrawString(x - 18, y + 8, V_ALLOWLOWERCASE|V_TRANSLUCENT, "Type to search..."); + if (skullAnimCounter < 4) + V_DrawCharacter(x - 18 + V_StringWidth(menusearch+1, 0), y + 8, + '_' | 0x80, false); + + x -= (21 + 5 + 16); + V_DrawSmallScaledPatch(x, y + 4, (menusearch[0] ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+3]); + + x = BASEVIDWIDTH - x - 16; + V_DrawSmallScaledPatch(x, y + 4, ((!majormods) ? 0 : V_TRANSLUCENT), addonsp[NUM_EXT+4]); + + if (modifiedgame) + V_DrawSmallScaledPatch(x, y + 4, 0, addonsp[NUM_EXT+2]); +} diff --git a/src/k_menufunc.c b/src/k_menufunc.c index c9c085a32..fe4807e0d 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -3262,3 +3262,329 @@ static void Splitplayers_OnChange(void) setupm_pselect = 1; #endif } + +// Misc menus + +// Addons menu: (Merely copypasted, original code by toaster) + +void M_Addons(INT32 choice) +{ + const char *pathname = "."; + + (void)choice; + +#if 1 + if (cv_addons_option.value == 0) + pathname = usehome ? srb2home : srb2path; + else if (cv_addons_option.value == 1) + pathname = srb2home; + else if (cv_addons_option.value == 2) + pathname = srb2path; + else +#endif + if (cv_addons_option.value == 3 && *cv_addons_folder.string != '\0') + pathname = cv_addons_folder.string; + + strlcpy(menupath, pathname, 1024); + menupathindex[(menudepthleft = menudepth-1)] = strlen(menupath) + 1; + + if (menupath[menupathindex[menudepthleft]-2] != PATHSEP[0]) + { + menupath[menupathindex[menudepthleft]-1] = PATHSEP[0]; + menupath[menupathindex[menudepthleft]] = 0; + } + else + --menupathindex[menudepthleft]; + + if (!preparefilemenu(false, false)) + { + M_StartMessage(va("No files/folders found.\n\n%s\n\n(Press a key)\n", LOCATIONSTRING1),NULL,MM_NOTHING); + return; + } + else + dir_on[menudepthleft] = 0; + + MISC_AddonsDef.prevMenu = currentMenu; + M_SetupNextMenu(&MISC_AddonsDef, false); +} + + +char *M_AddonsHeaderPath(void) +{ + UINT32 len; + static char header[1024]; + + strlcpy(header, va("%s folder%s", cv_addons_option.string, menupath+menupathindex[menudepth-1]-1), 1024); + len = strlen(header); + if (len > 34) + { + len = len-34; + header[len] = header[len+1] = header[len+2] = '.'; + } + else + len = 0; + + return header+len; +} + +#define UNEXIST S_StartSound(NULL, sfx_s26d);\ + M_SetupNextMenu(MISC_AddonsDef.prevMenu, false);\ + M_StartMessage(va("\x82%s\x80\nThis folder no longer exists!\nAborting to main menu.\n\n(Press a key)\n", M_AddonsHeaderPath()),NULL,MM_NOTHING) + +#define CLEARNAME Z_Free(refreshdirname);\ + refreshdirname = NULL + +static boolean prevmajormods = false; + +static void M_AddonsClearName(INT32 choice) +{ + if (!majormods || prevmajormods) + { + CLEARNAME; + } + M_StopMessage(choice); +} + +// returns whether to do message draw +boolean M_AddonsRefresh(void) +{ + if ((refreshdirmenu & REFRESHDIR_NORMAL) && !preparefilemenu(true, false)) + { + UNEXIST; + if (refreshdirname) + { + CLEARNAME; + } + return true; + } + + if (!majormods && prevmajormods) + prevmajormods = false; + + if ((refreshdirmenu & REFRESHDIR_ADDFILE) || (majormods && !prevmajormods)) + { + char *message = NULL; + + if (refreshdirmenu & REFRESHDIR_NOTLOADED) + { + S_StartSound(NULL, sfx_s26d); + if (refreshdirmenu & REFRESHDIR_MAX) + message = va("%c%s\x80\nMaximum number of addons reached.\nA file could not be loaded.\nIf you wish to play with this addon, restart the game to clear existing ones.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); + else + message = va("%c%s\x80\nA file was not loaded.\nCheck the console log for more information.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); + } + else if (refreshdirmenu & (REFRESHDIR_WARNING|REFRESHDIR_ERROR)) + { + S_StartSound(NULL, sfx_s224); + message = va("%c%s\x80\nA file was loaded with %s.\nCheck the console log for more information.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname, ((refreshdirmenu & REFRESHDIR_ERROR) ? "errors" : "warnings")); + } + else if (majormods && !prevmajormods) + { + S_StartSound(NULL, sfx_s221); + message = va("%c%s\x80\nYou've loaded a gameplay-modifying addon.\n\nRecord Attack has been disabled, but you\ncan still play alone in local Multiplayer.\n\nIf you wish to play Record Attack mode, restart the game to disable loaded addons.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), refreshdirname); + prevmajormods = majormods; + } + + if (message) + { + M_StartMessage(message,M_AddonsClearName,MM_EVENTHANDLER); + return true; + } + + S_StartSound(NULL, sfx_s221); + CLEARNAME; + } + + return false; +} + +static void M_AddonExec(INT32 ch) +{ + if (ch != 'y' && ch != KEY_ENTER) + return; + + S_StartSound(NULL, sfx_zoom); + COM_BufAddText(va("exec \"%s%s\"", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING)); +} + +#define len menusearch[0] +static boolean M_ChangeStringAddons(INT32 choice) +{ + if (shiftdown && choice >= 32 && choice <= 127) + choice = shiftxform[choice]; + + switch (choice) + { + case KEY_DEL: + if (len) + { + len = menusearch[1] = 0; + return true; + } + break; + case KEY_BACKSPACE: + if (len) + { + menusearch[1+--len] = 0; + return true; + } + break; + default: + if (choice >= 32 && choice <= 127) + { + if (len < MAXSTRINGLENGTH - 1) + { + menusearch[1+len++] = (char)choice; + menusearch[1+len] = 0; + return true; + } + } + break; + } + return false; +} +#undef len + +void M_HandleAddons(INT32 choice) +{ + boolean exitmenu = false; // exit to previous menu + + if (M_ChangeStringAddons(choice)) + { + char *tempname = NULL; + if (dirmenu && dirmenu[dir_on[menudepthleft]]) + tempname = Z_StrDup(dirmenu[dir_on[menudepthleft]]+DIR_STRING); // don't need to I_Error if can't make - not important, just QoL +#if 0 // much slower + if (!preparefilemenu(true, false)) + { + UNEXIST; + return; + } +#else // streamlined + searchfilemenu(tempname); +#endif + } + + switch (choice) + { + case KEY_DOWNARROW: + if (dir_on[menudepthleft] < sizedirmenu-1) + dir_on[menudepthleft]++; + S_StartSound(NULL, sfx_menu1); + break; + case KEY_UPARROW: + if (dir_on[menudepthleft]) + dir_on[menudepthleft]--; + S_StartSound(NULL, sfx_menu1); + break; + case KEY_PGDN: + { + UINT8 i; + for (i = numaddonsshown; i && (dir_on[menudepthleft] < sizedirmenu-1); i--) + dir_on[menudepthleft]++; + } + S_StartSound(NULL, sfx_menu1); + break; + case KEY_PGUP: + { + UINT8 i; + for (i = numaddonsshown; i && (dir_on[menudepthleft]); i--) + dir_on[menudepthleft]--; + } + S_StartSound(NULL, sfx_menu1); + break; + case KEY_ENTER: + { + boolean refresh = true; + if (!dirmenu[dir_on[menudepthleft]]) + S_StartSound(NULL, sfx_s26d); + else + { + switch (dirmenu[dir_on[menudepthleft]][DIR_TYPE]) + { + case EXT_FOLDER: + strcpy(&menupath[menupathindex[menudepthleft]],dirmenu[dir_on[menudepthleft]]+DIR_STRING); + if (menudepthleft) + { + menupathindex[--menudepthleft] = strlen(menupath); + menupath[menupathindex[menudepthleft]] = 0; + + if (!preparefilemenu(false, false)) + { + S_StartSound(NULL, sfx_s224); + M_StartMessage(va("%c%s\x80\nThis folder is empty.\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING); + menupath[menupathindex[++menudepthleft]] = 0; + + if (!preparefilemenu(true, false)) + { + UNEXIST; + return; + } + } + else + { + S_StartSound(NULL, sfx_menu1); + dir_on[menudepthleft] = 1; + } + refresh = false; + } + else + { + S_StartSound(NULL, sfx_s26d); + M_StartMessage(va("%c%s\x80\nThis folder is too deep to navigate to!\n\n(Press a key)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), M_AddonsHeaderPath()),NULL,MM_NOTHING); + menupath[menupathindex[menudepthleft]] = 0; + } + break; + case EXT_UP: + S_StartSound(NULL, sfx_menu1); + menupath[menupathindex[++menudepthleft]] = 0; + if (!preparefilemenu(false, false)) + { + UNEXIST; + return; + } + break; + case EXT_TXT: + M_StartMessage(va("%c%s\x80\nThis file may not be a console script.\nAttempt to run anyways? \n\n(Press 'Y' to confirm)\n", ('\x80' + (highlightflags>>V_CHARCOLORSHIFT)), dirmenu[dir_on[menudepthleft]]+DIR_STRING),M_AddonExec,MM_YESNO); + break; + case EXT_CFG: + M_AddonExec(KEY_ENTER); + break; + case EXT_LUA: + case EXT_SOC: + case EXT_WAD: +#ifdef USE_KART + case EXT_KART: +#endif + case EXT_PK3: + COM_BufAddText(va("addfile \"%s%s\"", menupath, dirmenu[dir_on[menudepthleft]]+DIR_STRING)); + break; + default: + S_StartSound(NULL, sfx_s26d); + } + } + if (refresh) + refreshdirmenu |= REFRESHDIR_NORMAL; + } + break; + + case KEY_ESCAPE: + exitmenu = true; + break; + + default: + break; + } + if (exitmenu) + { + closefilemenu(true); + + // Secret menu! + //MainMenu[secrets].status = (M_AnySecretUnlocked()) ? (IT_STRING | IT_CALL) : (IT_DISABLED); + + if (currentMenu->prevMenu) + M_SetupNextMenu(currentMenu->prevMenu, false); + else + M_ClearMenus(true); + } +}