Pause menu start + map switching

This commit is contained in:
SinnamonLat 2021-11-18 15:19:25 +01:00
parent b750ef11dd
commit e560bffcf7
4 changed files with 361 additions and 16 deletions

View file

@ -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);

View file

@ -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},

View file

@ -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)<<FRACBITS, 0, FRACUNIT, V_ADD, vertbg, NULL);
// Okay that's cool but which icon do we draw first? let's roll back from itemOn!
// At most we'll draw 7 items, 1 in the center, 3 above, 3 below.
// Which means... let's count down from itemOn
for (i = itemOn; countdown < 3; countdown++)
{
//CONS_Printf("pass %d: %d\n", countdown, i);
i--;
if (i < 0)
i = currentMenu->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<<FRACBITS, 100<<FRACBITS, FRACUNIT, 0, arrstart, NULL);
while ((arrxpos - arrfill->width) < BASEVIDWIDTH)
{
V_DrawFixedPatch(arrxpos<<FRACBITS, 100<<FRACBITS, FRACUNIT, 0, arrfill, NULL);
arrxpos += arrfill->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)

View file

@ -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)
{