From e560bffcf76a296fd5f57fd3c86347012697251d Mon Sep 17 00:00:00 2001 From: SinnamonLat Date: Thu, 18 Nov 2021 15:19:25 +0100 Subject: [PATCH] Pause menu start + map switching --- src/k_menu.h | 29 +++++++++- src/k_menudef.c | 91 +++++++++++++++++++++++++++++- src/k_menudraw.c | 142 +++++++++++++++++++++++++++++++++++++++++++++++ src/k_menufunc.c | 115 +++++++++++++++++++++++++++++++++----- 4 files changed, 361 insertions(+), 16 deletions(-) diff --git a/src/k_menu.h b/src/k_menu.h index b9dacc5fe..33d63b52a 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -133,7 +133,7 @@ typedef struct menu_s void (*drawroutine)(void); // draw routine void (*tickroutine)(void); // ticker routine boolean (*quitroutine)(void); // called before quit a menu return true if we can - boolean (*inputroutine)(INT32); // if set, called every frame in the input handler. Returning true overwrites normal input handling. + boolean (*inputroutine)(INT32); // if set, called every frame in the input handler. Returning true overwrites normal input handling. } menu_t; typedef enum @@ -196,6 +196,12 @@ extern menu_t PLAY_MP_RoomSelectDef; extern menuitem_t PLAY_BattleGamemodesMenu[]; extern menu_t PLAY_BattleGamemodesDef; +extern menuitem_t PAUSE_Main[]; +extern menu_t PAUSE_MainDef; + +extern menuitem_t PAUSE_GamemodesMenu[]; +extern menu_t PAUSE_GamemodesDef; + extern menuitem_t PAUSE_PlaybackMenu[]; extern menu_t PAUSE_PlaybackMenuDef; @@ -402,6 +408,24 @@ void M_MPRoomSelect(INT32 choice); void M_MPRoomSelectTick(void); void M_MPRoomSelectInit(INT32 choice); +// Pause menu: + +// Keep track of some pause menu data for visual goodness. +extern struct pausemenu_s { + + tic_t ticker; // How long the menu's been open for + INT16 offset; // To make the icons move smoothly when we transition! + + INT16 openoffset; // Used when you open / close the menu to slide everything in. + boolean closing; // When this is set, the open offset goes backwards to close the menu smoothly. +} pausemenu; + +void M_OpenPauseMenu(void); +void M_QuitPauseMenu(void); + +boolean M_PauseInputs(INT32 ch); +void M_PauseTick(void); + // Replay Playback extern tic_t playback_last_menu_interaction_leveltime; @@ -443,6 +467,9 @@ void M_DrawMPHost(void); void M_DrawMPJoinIP(void); void M_DrawMPRoomSelect(void); +// Pause menu: +void M_DrawPause(void); + // Replay Playback void M_DrawPlaybackMenu(void); diff --git a/src/k_menudef.c b/src/k_menudef.c index d98c3b028..f1ff28350 100644 --- a/src/k_menudef.c +++ b/src/k_menudef.c @@ -252,10 +252,10 @@ menuitem_t PLAY_MP_JoinIP[] = NULL, NULL, 0, 0}, {IT_STRING, "servip2", "The last 3 IPs you've succesfully joined are displayed here.", - NULL, NULL, 0, 0}, + NULL, NULL, 0, 0}, {IT_STRING, "servip3", "The last 3 IPs you've succesfully joined are displayed here.", - NULL, NULL, 0, 0}, + NULL, NULL, 0, 0}, }; @@ -296,6 +296,93 @@ menu_t PLAY_MP_RoomSelectDef = { // In-game/pause menus // ------------------- +// ESC pause menu +// Since there's no descriptions to each item, we'll use the descriptions as the names of the patches we want to draw for each option :) + +menuitem_t PAUSE_Main[] = +{ + + {IT_STRING | IT_CALL, "ADDONS", "M_ICOADD", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_SUBMENU, "CHANGE MAP", "M_ICOMAP", + NULL, &PAUSE_GamemodesDef, 0, 0}, + +#ifdef HAVE_DISCORDRPC + {IT_STRING | IT_CALL, "DISCORD REQUESTS", "M_ICODIS", + NULL, NULL, 0, 0}, +#endif + + {IT_STRING | IT_CALL, "RESUME GAME", "M_ICOUNP", + NULL, M_QuitPauseMenu, 0, 0}, + + {IT_STRING | IT_CALL, "SPECTATE", "M_ICOSPC", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CALL, "ENTER GAME", "M_ICOENT", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CALL, "CANCEL JOIN", "M_ICOSPC", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CALL, "PLAYER SETUP", "M_ICOCHR", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CALL, "OPTIONS", "M_ICOOPT", + NULL, NULL, 0, 0}, + + {IT_STRING | IT_CALL, "EXIT GAME", "M_ICOEXT", + NULL, NULL, 0, 0}, +}; + +// We'll need this since we're gonna have to dynamically enable and disable options depending on which state we're in. +typedef enum +{ + mpause_addons = 0, + mpause_switchmap, +#ifdef HAVE_DISCORDRPC + mpause_discordrequests, +#endif + + mpause_continue, + mpause_spectate, + mpause_entergame, + mpause_canceljoin, + mpause_psetup, + mpause_options, + + mpause_title, +} mpause_e; + + +menu_t PAUSE_MainDef = { + sizeof (PAUSE_Main) / sizeof (menuitem_t), + NULL, + 0, + PAUSE_Main, + 0, 0, + 1, 10, // For transition with some menus! + M_DrawPause, + M_PauseTick, + NULL, + M_PauseInputs +}; + +// PAUSE : Map switching gametype selection (In case you want to pick from battle / race...) +menuitem_t PAUSE_GamemodesMenu[] = +{ + {IT_STRING | IT_CALL, "Race", "Select which gamemode to choose a new map from.", + NULL, M_LevelSelectInit, 0, GT_RACE}, + + {IT_STRING | IT_CALL, "Battle", "Select which gamemode to choose a new map from.", + NULL, M_LevelSelectInit, 0, GT_BATTLE}, + + {IT_STRING | IT_CALL, "Back", NULL, NULL, M_GoBack, 0, 0}, +}; + +menu_t PAUSE_GamemodesDef = KARTGAMEMODEMENU(PAUSE_GamemodesMenu, &PAUSE_MainDef); + +// Replay popup menu menuitem_t PAUSE_PlaybackMenu[] = { {IT_CALL | IT_STRING, "Hide Menu (Esc)", NULL, "M_PHIDE", M_SelectableClearMenus, 0, 0}, diff --git a/src/k_menudraw.c b/src/k_menudraw.c index f4d6ad08a..fb0653459 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -1589,6 +1589,148 @@ void M_DrawMPRoomSelect(void) // INGAME / PAUSE MENUS // +// PAUSE + +// PAUSE MAIN MENU +void M_DrawPause(void) +{ + + SINT8 i; + SINT8 itemsdrawn = 0; + SINT8 countdown = 0; + INT16 ypos = -50; // Draw 3 items from selected item (y=100 - 3 items spaced by 50 px each... you get the idea.) + INT16 dypos; + + INT16 offset = menutransition.tics ? floor(pow(2, (double)menutransition.tics)) : pausemenu.openoffset; + INT16 arrxpos = 150 + 2*offset; // To draw the background arrow. + + INT16 j = 0; + char word1[MAXSTRINGLENGTH]; + INT16 word1len = 0; + char word2[MAXSTRINGLENGTH]; + INT16 word2len = 0; + boolean sok = false; + + patch_t *pausebg = W_CachePatchName("M_STRIPU", PU_CACHE); + patch_t *vertbg = W_CachePatchName("M_STRIPV", PU_CACHE); + patch_t *pausetext = W_CachePatchName("M_PAUSET", PU_CACHE); + + patch_t *arrstart = W_CachePatchName("M_PTIP", PU_CACHE); + patch_t *arrfill = W_CachePatchName("M_PFILL", PU_CACHE); + + //V_DrawFadeScreen(0xFF00, 16); + + // "PAUSED" + V_DrawFixedPatch(-offset*FRACUNIT, 0, FRACUNIT, V_ADD, pausebg, NULL); + V_DrawFixedPatch(-offset*FRACUNIT, 0, FRACUNIT, 0, pausetext, NULL); + + // Vertical Strip: + V_DrawFixedPatch((230 + offset)<numitems-1; + } + + // Aaaaand now we can start drawing! + // Reminder that we set the patches of the options to the description since we're not using that. I'm smart, I know... + + // Draw the background arrow + V_DrawFixedPatch(arrxpos<width) < BASEVIDWIDTH) + { + V_DrawFixedPatch(arrxpos<width; + } + + while (itemsdrawn < 7) + { + + switch (currentMenu->menuitems[i].status & IT_DISPLAY) + { + case IT_STRING: + { + + char iconame[9]; // 8 chars + \0 + patch_t *pp; + + if (i == itemOn) + { + strcpy(iconame, currentMenu->menuitems[i].tooltip); + iconame[7] = '2'; // Yes this is a stupid hack. Replace the last character with a 2 when we're selecting this graphic. + + pp = W_CachePatchName(iconame, PU_CACHE); + } + else + pp = W_CachePatchName(currentMenu->menuitems[i].tooltip, PU_CACHE); + + // 294 - 261 = 33 + // We need to move 33 px in 50 tics which means we move 33/50 = 0.66 px every tic = 2/3 of the offset. + // trust me i graduated highschool!!!! + + // Multiply by -1 or 1 depending on whether we're below or above 100 px. + // This double ternary is awful, yes. + + dypos = ypos + pausemenu.offset; + V_DrawFixedPatch( ((i == itemOn ? (294 - pausemenu.offset*2/3 * (dypos > 100 ? 1 : -1)) : 261) + offset) << FRACBITS, (dypos)*FRACUNIT, FRACUNIT, 0, pp, NULL); + + ypos += 50; + itemsdrawn++; // We drew that! + break; + } + } + + + i++; // Regardless of whether we drew or not, go to the next item in the menu. + if (i > currentMenu->numitems) + i = 0; + } + + // Draw the string! + // ...but first get what we need to get. + while (currentMenu->menuitems[itemOn].text[j] && j < MAXSTRINGLENGTH) + { + char c = currentMenu->menuitems[itemOn].text[j]; + + if (c == ' ') + { + sok = true; + j++; + continue; // We don't care about this :moyai: + } + + if (sok) + { + word2[word2len] = c; + word2len++; + } + else + { + word1[word1len] = c; + word1len++; + } + + j++; + } + + word1[word1len] = '\0'; + word2[word2len] = '\0'; + + // If there's no 2nd word, take this opportunity to center this line of text. + if (word1len) + V_DrawCenteredLSTitleHighString(220 + offset*2, 75 + (!word2len ? 10 : 0), 0, word1); + + if (word2len) + V_DrawCenteredLSTitleLowString(220 + offset*2, 103, 0, word2); +} + tic_t playback_last_menu_interaction_leveltime = 0; void M_DrawPlaybackMenu(void) diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 6c25233bd..458bfd441 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -1219,6 +1219,12 @@ void M_StartControlPanel(void) currentMenu = &MainDef; itemOn = 0; } + else + { + // For now let's just always open the same pause menu. + M_OpenPauseMenu(); + } + #if 0 else if (modeattacking) { @@ -2354,7 +2360,8 @@ static void M_LevelListFromGametype(INT16 gt) } // Init level select for use in local play using the last choice we made. -// For the online MP version, see M_MPSetupNetgameMapSelect() +// For the online MP version used to START HOSTING A GAME, see M_MPSetupNetgameMapSelect() +// (We still use this one midgame) void M_LevelSelectInit(INT32 choice) { @@ -2465,9 +2472,15 @@ void M_CupSelectHandler(INT32 choice) grandprixinfo.initalize = true; paused = false; - SV_StartSinglePlayerServer(); - multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... - netgame = levellist.netgame; // ^ ditto. + + // Don't restart the server if we're already in a game lol + if (gamestate == GS_MENU) + { + SV_StartSinglePlayerServer(); + multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... + netgame = levellist.netgame; // ^ ditto. + } + D_MapChange( grandprixinfo.cup->levellist[0] + 1, GT_RACE, @@ -2593,16 +2606,20 @@ void M_LevelSelectHandler(INT32 choice) S_StartSound(NULL, sfx_s3k63); - // Early fadeout to let the sound finish playing - F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - F_WipeEndScreen(); - F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); - paused = false; - SV_StartSinglePlayerServer(); - multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... - netgame = levellist.netgame; // ^ ditto. + + if (gamestate == GS_MENU) // Don't restart the server if we're already in a game lol. Don't fade out either. + { + // Early fadeout to let the sound finish playing + F_WipeStartScreen(); + V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); + F_WipeEndScreen(); + F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); + + SV_StartSinglePlayerServer(); + multiplayer = true; // yeah, SV_StartSinglePlayerServer clobbers this... + netgame = levellist.netgame; // ^ ditto. + } D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); @@ -2855,6 +2872,78 @@ void M_EndModeAttackRun(void) #endif } +struct pausemenu_s pausemenu; + +// Pause menu! +void M_OpenPauseMenu(void) +{ + currentMenu = &PAUSE_MainDef; + + // Ready the variables + pausemenu.ticker = 0; + + pausemenu.offset = 0; + pausemenu.openoffset = 256; + pausemenu.closing = false; +} + +void M_QuitPauseMenu(void) +{ + // M_PauseTick actually handles the quitting when it's been long enough. + pausemenu.closing = true; + pausemenu.openoffset = 4; +} + +void M_PauseTick(void) +{ + pausemenu.offset /= 2; + + if (pausemenu.closing) + { + pausemenu.openoffset *= 2; + if (pausemenu.openoffset > 255) + M_ClearMenus(true); + + } + else + pausemenu.openoffset /= 2; +} + +boolean M_PauseInputs(INT32 ch) +{ + + if (pausemenu.closing) + return true; // Don't allow inputs. + + switch (ch) + { + + case KEY_UPARROW: + { + pausemenu.offset -= 50; // Each item is spaced by 50 px + M_PrevOpt(); + return true; + } + + case KEY_DOWNARROW: + { + pausemenu.offset += 50; // Each item is spaced by 50 px + M_NextOpt(); + return true; + } + + case KEY_ESCAPE: + { + M_QuitPauseMenu(); + return true; + } + + } + + return false; +} + + // Replay Playback Menu void M_SetPlaybackMenuPointer(void) {