From 71e1179030aea0271793ddc420a0db67d85c8f6c Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 May 2025 14:57:06 +0100 Subject: [PATCH 1/5] M_MenuToLevelPreamble: Seriously clean up the process of starting a course from the menu A lot of messy, copypasted boilerplate has been bundled together into a single function. Programmers can now fire up a Match Race, the most basic type of gameplay, from menu code in only four steps (other modes take a little more attention): - **M_MenuToLevelPreamble(UINT8 - splitscreen players #, boolean - false to extend wipe/true for short wipe)** - set restoremenu - D_MapChange(...) - M_ClearMenus(...) Includes the following fixes: - Encore no longer has over-long wipes when started from menu, only standard-length - "Boss Intro" and Encore start-of-round sounds will always play, even if no Title Card is drawn - No long wipe when restarting a Time Attack run - Auto Encore and Auto Gamespeed are no longer accidentially forced if you've manually changed them to Off and Gear 2 before starting netgame --- src/g_game.c | 40 +++++--- src/k_menu.h | 1 + src/menus/play-local-race-time-attack.c | 40 ++++---- src/menus/transient/cup-select.c | 25 +---- src/menus/transient/level-select.c | 120 ++++++++++++------------ src/p_setup.cpp | 2 +- src/st_stuff.c | 2 + 7 files changed, 109 insertions(+), 121 deletions(-) diff --git a/src/g_game.c b/src/g_game.c index 8f05f30ab..2ae89c184 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -1245,26 +1245,36 @@ void G_StartTitleCard(void) // prepare status bar ST_startTitleCard(); // <-- always must be called to init some variables - // The title card has been disabled for this map. - // Oh well. - if (demo.simplerewind || !G_IsTitleCardAvailable()) - { - WipeStageTitle = false; + if (demo.simplerewind) return; + + sfxenum_t kstart = 0; + + if (K_CheckBossIntro() == true) + { + kstart = sfx_ssa021; } + else if (encoremode) + { + kstart = sfx_ruby2; + } + + if (kstart) + { + // Play the guaranteed alt sounds + S_StartSound(NULL, kstart); + } + + if (!G_IsTitleCardAvailable()) + return; // start the title card WipeStageTitle = (gamestate == GS_LEVEL); - // play the sound - if (WipeStageTitle) + if (WipeStageTitle && !kstart) { - sfxenum_t kstart = sfx_kstart; - if (K_CheckBossIntro() == true) - kstart = sfx_ssa021; - else if (encoremode == true) - kstart = sfx_ruby2; - S_StartSound(NULL, kstart); + // Play the standard titlecard sound + S_StartSound(NULL, sfx_kstart); } } @@ -5379,7 +5389,7 @@ void G_InitNew(UINT8 pencoremode, INT32 map, boolean resetplayer, boolean skippr S_ResumeAudio(); } - prevencoremode = ((!Playing()) ? false : encoremode); + prevencoremode = encoremode; encoremode = pencoremode; legitimateexit = false; // SRB2Kart @@ -5831,7 +5841,7 @@ boolean G_GetExitGameFlag(void) // Same deal with retrying. void G_SetRetryFlag(void) { - if (retrying == false) + if (retrying == false && grandprixinfo.gp) { grandprixinfo.rank.continuesUsed++; } diff --git a/src/k_menu.h b/src/k_menu.h index 1a4dc3066..fbf8594b8 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -944,6 +944,7 @@ void M_CupSelectTick(void); void M_LevelSelectHandler(INT32 choice); void M_LevelSelectTick(void); +void M_MenuToLevelPreamble(UINT8 ssplayers, boolean nowipe); void M_LevelSelected(INT16 add, boolean menuupdate); boolean M_LevelSelectCupSwitch(boolean next, boolean skipones); diff --git a/src/menus/play-local-race-time-attack.c b/src/menus/play-local-race-time-attack.c index b682cfe07..7ac7343bf 100644 --- a/src/menus/play-local-race-time-attack.c +++ b/src/menus/play-local-race-time-attack.c @@ -614,34 +614,19 @@ void M_StartTimeAttack(INT32 choice) { modeattacking |= ATTACKING_SPB; } + + if (gamestate == GS_MENU) + { + encoremode = true; // guarantees short wipe + } + modeprefix = "spb-"; } // DON'T SOFTLOCK CON_ToggleOff(); - // Still need to reset devmode - cht_debug = 0; - - if (demo.playback) - G_StopDemo(); - - splitscreen = 0; - SplitScreen_OnChange(); - - S_StartSound(NULL, sfx_s3k63); - - paused = false; - - S_StopMusicCredit(); - - // Early fadeout to let the sound finish playing - F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - F_WipeEndScreen(); - F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); - - SV_StartSinglePlayerServer(levellist.newgametype, false); + M_MenuToLevelPreamble(0, (gamestate != GS_MENU || cv_dummyspbattack.value == 1)); gpath = va("%s"PATHSEP"media"PATHSEP"replay"PATHSEP"%s", srb2home, timeattackfolder); @@ -659,8 +644,17 @@ void M_StartTimeAttack(INT32 choice) restoreMenu = &PLAY_TimeAttackDef; + D_MapChange( + levellist.choosemap+1, + levellist.newgametype, + (cv_dummyspbattack.value == 1), + true, + 1, + false, + false + ); + M_ClearMenus(true); - D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_dummyspbattack.value == 1), 1, 1, false, false); G_UpdateTimeStickerMedals(levellist.choosemap, true); } diff --git a/src/menus/transient/cup-select.c b/src/menus/transient/cup-select.c index 8fa830094..f8c5266f3 100644 --- a/src/menus/transient/cup-select.c +++ b/src/menus/transient/cup-select.c @@ -57,26 +57,7 @@ static void M_StartCup(UINT8 entry) entry = UINT8_MAX; } - S_StartSound(NULL, sfx_s3k63); - - paused = false; - - S_StopMusicCredit(); - - // Early fadeout to let the sound finish playing - F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - F_WipeEndScreen(); - F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); - - if (cv_maxconnections.value < ssplayers+1) - CV_SetValue(&cv_maxconnections, ssplayers+1); - - if (splitscreen != ssplayers) - { - splitscreen = ssplayers; - SplitScreen_OnChange(); - } + M_MenuToLevelPreamble(ssplayers, false); if (entry == UINT8_MAX) { @@ -135,10 +116,6 @@ static void M_StartCup(UINT8 entry) } } - paused = false; - - SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame); - M_ClearMenus(true); restoreMenu = &PLAY_CupSelectDef; diff --git a/src/menus/transient/level-select.c b/src/menus/transient/level-select.c index a0c57ae8c..5745c82b9 100644 --- a/src/menus/transient/level-select.c +++ b/src/menus/transient/level-select.c @@ -657,6 +657,11 @@ void M_LevelSelectInit(INT32 choice) case 0: levellist.levelsearch.grandprix = false; levellist.levelsearch.timeattack = false; + + CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string); + CV_StealthSet(&cv_kartencore, (cv_dummygpencore.value == 1) ? "On" : "Auto"); + CV_StealthSet(&cv_kartspeed, (cv_dummykartspeed.value == KARTSPEED_NORMAL) ? "Auto Gear" : cv_dummykartspeed.string); + break; case 1: levellist.levelsearch.grandprix = false; @@ -689,6 +694,40 @@ void M_LevelSelectInit(INT32 choice) } } +void M_MenuToLevelPreamble(UINT8 ssplayers, boolean nowipe) +{ + cht_debug = 0; + + if (demo.playback) + G_StopDemo(); + + if (cv_maxconnections.value < ssplayers+1) + CV_SetValue(&cv_maxconnections, ssplayers+1); + + if (splitscreen != ssplayers) + { + splitscreen = ssplayers; + SplitScreen_OnChange(); + } + + paused = false; + + S_StopMusicCredit(); + + if (!nowipe) + { + 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(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); + } + + SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame); +} + void M_LevelSelected(INT16 add, boolean menuupdate) { UINT8 i = 0; @@ -733,70 +772,35 @@ void M_LevelSelected(INT16 add, boolean menuupdate) { if (gamestate == GS_MENU) { - UINT8 ssplayers = levellist.levelsearch.tutorial ? 0 : cv_splitplayers.value-1; - - netgame = false; multiplayer = true; - strlcpy(connectedservername, cv_servername.string, MAXSERVERNAME); + M_MenuToLevelPreamble( + (levellist.levelsearch.tutorial + ? 0 + : cv_splitplayers.value-1 + ), + ( + (cv_kartencore.value == 1) + && (gametypes[levellist.newgametype]->rules & GTR_ENCORE) + ) + ); - // Still need to reset devmode - cht_debug = 0; - - if (demo.playback) - G_StopDemo(); - - /*if (levellist.choosemap == 0) - levellist.choosemap = G_RandMap(G_TOLFlag(levellist.newgametype), -1, 0, 0, false, NULL);*/ - - if (cv_maxconnections.value < ssplayers+1) - CV_SetValue(&cv_maxconnections, ssplayers+1); - - if (splitscreen != ssplayers) - { - splitscreen = ssplayers; - SplitScreen_OnChange(); - } - - S_StartSound(NULL, sfx_s3k63); - - paused = false; - - S_StopMusicCredit(); - - // Early fadeout to let the sound finish playing - F_WipeStartScreen(); - V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31); - F_WipeEndScreen(); - F_RunWipe(wipe_level_toblack, wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false); - - SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame); - - if (!levellist.netgame) - CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string); - - CV_StealthSet(&cv_kartencore, (cv_dummygpencore.value == 1) ? "On" : "Auto"); - CV_StealthSet(&cv_kartspeed, (cv_dummykartspeed.value == KARTSPEED_NORMAL) ? "Auto Gear" : cv_dummykartspeed.string); - - D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); - - if (levellist.netgame == true) - { - restoreMenu = &PLAY_MP_OptSelectDef; - } - else /*if (!M_GameTrulyStarted() || - levellist.levelsearch.tutorial)*/ - { - restoreMenu = currentMenu; - } + restoreMenu = (levellist.netgame) + ? &PLAY_MP_OptSelectDef + : currentMenu; restorelevellist = levellist; } - else - { - // directly do the map change - D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false); - } + + D_MapChange( + levellist.choosemap+1, + levellist.newgametype, + (cv_kartencore.value == 1), + true, + 1, + false, + false + ); M_ClearMenus(true); } diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 8d8b6e55a..2d11cc8d5 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -8513,7 +8513,7 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) wipegamestate = gamestate; // Don't fade if reloading the gamestate // Encore mode fade to pink to white // This is handled BEFORE sounds are stopped. - else if (encoremode && !prevencoremode && modeattacking == ATTACKING_NONE && !demo.simplerewind) + else if (encoremode && !prevencoremode && !demo.simplerewind) { if (rendermode != render_none) { diff --git a/src/st_stuff.c b/src/st_stuff.c index e8196e57e..44703e839 100644 --- a/src/st_stuff.c +++ b/src/st_stuff.c @@ -737,6 +737,8 @@ void ST_startTitleCard(void) lt_ticker = lt_exitticker = lt_lasttic = 0; lt_endtime = 4*TICRATE; // + (10*NEWTICRATERATIO); lt_fade = 0; + + WipeStageTitle = false; } // From 57cdb4fcb50a73069210ea54b6622a5567b4b9ff Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 May 2025 15:03:46 +0100 Subject: [PATCH 2/5] Time Attack respawn button: Specify hold behaviour Now that TA wipes are their intended length, holding Respawn during Time Attack for more than a single frame repeatedly restarts the run. This is not ideal, and frankly wasteful of CPU. Here is the specified replacement for this case: - Holding gc_respawn will hold on a black(/white) screen before mapload. - Allows for taking a conscious breather (or opportunity to curse) in the middle of long Time Attack sessions. - Ticcmdbuilder will not interpret gc_respawn into BT_ constants in Time Attack contexts at all. - Fixes the occasionally visible E-Brake when coming out of this breather state. --- src/g_build_ticcmd.cpp | 5 ++++- src/k_menufunc.c | 2 +- src/p_setup.cpp | 32 ++++++++++++++++++++++++++++++++ 3 files changed, 37 insertions(+), 2 deletions(-) diff --git a/src/g_build_ticcmd.cpp b/src/g_build_ticcmd.cpp index b8d45e3dc..f32c1f509 100644 --- a/src/g_build_ticcmd.cpp +++ b/src/g_build_ticcmd.cpp @@ -404,7 +404,10 @@ class TiccmdBuilder map(gc_item, BT_ATTACK); // fire map(gc_lookback, BT_LOOKBACK); // rear view - map(gc_respawn, BT_RESPAWN | (freecam() ? 0 : BT_EBRAKEMASK)); // respawn + if (!modeattacking) + { + map(gc_respawn, BT_RESPAWN | (freecam() ? 0 : BT_EBRAKEMASK)); // respawn + } map(gc_vote, BT_VOTE); // mp general function button // lua buttons a thru c diff --git a/src/k_menufunc.c b/src/k_menufunc.c index 0e3b1385d..3631441b0 100644 --- a/src/k_menufunc.c +++ b/src/k_menufunc.c @@ -466,7 +466,7 @@ boolean M_Responder(event_t *ev) if (Playing() && !demo.playback) { // Quick Retry (Y in modeattacking) - if (modeattacking && G_PlayerInputDown(0, gc_y, splitscreen + 1) == true) + if (modeattacking && G_PlayerInputDown(0, gc_respawn, splitscreen + 1) == true) { M_TryAgain(0); return true; diff --git a/src/p_setup.cpp b/src/p_setup.cpp index 2d11cc8d5..27e808c7c 100644 --- a/src/p_setup.cpp +++ b/src/p_setup.cpp @@ -8681,6 +8681,38 @@ boolean P_LoadLevel(boolean fromnetsave, boolean reloadinggamestate) } F_RunWipe(wipetype, wipedefs[wipetype], false, ((levelfadecol == 0) ? "FADEMAP1" : "FADEMAP0"), false, false); + + // Hold respawn to keep waiting until you're ready + if (G_IsModeAttackRetrying() && !demo.playback) + { + nowtime = lastwipetic; + while (G_PlayerInputDown(0, gc_respawn, splitscreen + 1) == true) + { + while (!((nowtime = I_GetTime()) - lastwipetic)) + { + I_Sleep(cv_sleep.value); + I_UpdateTime(); + } \ + + I_OsPolling(); + G_ResetAllDeviceResponding(); + + for (; eventtail != eventhead; eventtail = (eventtail+1) & (MAXEVENTS-1)) + { + HandleGamepadDeviceEvents(&events[eventtail]); + G_MapEventsToControls(&events[eventtail]); + } + + lastwipetic = nowtime; + if (moviemode && rendermode == render_opengl) + M_LegacySaveFrame(); + else if (moviemode && rendermode == render_soft) + I_CaptureVideoFrame(); + NetKeepAlive(); + } + + //wipestyleflags |= (WSF_FADEOUT|WSF_TOWHITE); + } } } From 5bfbfccdaebf96e5a339611ad496ec5d23b62442 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 May 2025 22:17:23 +0100 Subject: [PATCH 3/5] M_LevelSelectInit: Catch potentially invalid skin dereference for Hivolt message, just in case --- src/menus/transient/level-select.c | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/menus/transient/level-select.c b/src/menus/transient/level-select.c index 5745c82b9..78709305c 100644 --- a/src/menus/transient/level-select.c +++ b/src/menus/transient/level-select.c @@ -681,10 +681,14 @@ void M_LevelSelectInit(INT32 choice) gt = menugametype; } - if (levellist.levelsearch.timeattack && gt == GT_RACE && skins[R_SkinAvailableEx(cv_skin[0].string, false)].flags & SF_HIVOLT) + if (levellist.levelsearch.timeattack && gt == GT_RACE) { - M_StartMessage("A long-forgotten power...", "You are using a \x82prototype engine\x80.\nRecords will not be saved.", NULL, MM_NOTHING, NULL, NULL); - S_StartSound(NULL, sfx_s3k81); + const INT32 skinid = R_SkinAvailableEx(cv_skin[0].string, false); + if (skinid >= 0 && (skins[skinid].flags & SF_HIVOLT)) + { + M_StartMessage("A long-forgotten power...", "You are using a \x82prototype engine\x80.\nRecords will not be saved.", NULL, MM_NOTHING, NULL, NULL); + S_StartSound(NULL, sfx_s3k81); + } } if (!M_LevelListFromGametype(gt)) From 830cfe76da28877be4be273a3883800c8e9543d3 Mon Sep 17 00:00:00 2001 From: toaster Date: Sat, 31 May 2025 22:25:52 +0100 Subject: [PATCH 4/5] Round Queue UI on Level Select - Only works in Match Race and Online level selects - Shows in-progress state on Pause and Cup Grid, but no direct influence available on those screens - Z to queue current highlighted map - Pending until you press A, then sent to server - When done online, tries to send one at a time to avoid overloading connection - C to clear - Removes one from pending first - If you're out of pending maps, prompts whether you want to clear server's queue --- src/d_main.cpp | 1 + src/d_netcmd.c | 2 +- src/d_netcmd.h | 1 + src/g_game.c | 1 + src/g_game.h | 9 + src/k_hud.cpp | 40 +++++ src/k_hud.h | 1 + src/k_menu.h | 2 + src/k_menudraw.c | 85 ++++----- src/k_vote.c | 24 +-- src/menus/main-goner.cpp | 1 + src/menus/play-online-host.c | 1 + src/menus/transient/level-select.c | 273 ++++++++++++++++++++++++++++- src/y_inter.cpp | 134 +++++++++++--- src/y_inter.h | 2 +- 15 files changed, 487 insertions(+), 90 deletions(-) diff --git a/src/d_main.cpp b/src/d_main.cpp index e36e8f805..98590c30a 100644 --- a/src/d_main.cpp +++ b/src/d_main.cpp @@ -1213,6 +1213,7 @@ void D_ClearState(void) // Reset GP and roundqueue memset(&grandprixinfo, 0, sizeof(struct grandprixinfo)); memset(&roundqueue, 0, sizeof(struct roundqueue)); + memset(&menuqueue, 0, sizeof(struct menuqueue)); // empty some other semi-important state maptol = 0; diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 56c1eeef9..2be94c176 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -2984,7 +2984,7 @@ static void Command_RestartLevel(void) D_MapChange(gamemap, g_lastgametype, newencore, false, 0, false, false); } -static void Handle_MapQueueSend(UINT16 newmapnum, UINT16 newgametype, boolean newencoremode) +void Handle_MapQueueSend(UINT16 newmapnum, UINT16 newgametype, boolean newencoremode) { UINT8 flags = 0; diff --git a/src/d_netcmd.h b/src/d_netcmd.h index 50b5339d7..7391d0921 100644 --- a/src/d_netcmd.h +++ b/src/d_netcmd.h @@ -206,6 +206,7 @@ size_t WeaponPref_Parse(const UINT8 *p, INT32 playernum); void D_SendPlayerConfig(UINT8 n); void Command_ExitGame_f(void); void Command_Retry_f(void); +void Handle_MapQueueSend(UINT16 newmapnum, UINT16 newgametype, boolean newencoremode); boolean G_GamestateUsesExitLevel(void); void D_GameTypeChanged(INT32 lastgametype); // not a real _OnChange function anymore void D_MapChange(UINT16 pmapnum, INT32 pgametype, boolean pencoremode, boolean presetplayers, INT32 pdelay, boolean pskipprecutscene, boolean pforcespecialstage); diff --git a/src/g_game.c b/src/g_game.c index 2ae89c184..a081e437b 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -4106,6 +4106,7 @@ doremove: // Next map apparatus struct roundqueue roundqueue; +struct menuqueue menuqueue; void G_MapSlipIntoRoundQueue(UINT8 position, UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted) { diff --git a/src/g_game.h b/src/g_game.h index 14ee6494a..7996a8e61 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -75,6 +75,15 @@ extern struct roundqueue roundentry_t entries[ROUNDQUEUE_MAX]; // Entries in the round queue } roundqueue; +extern struct menuqueue +{ + // Degenerate version of roundqueue exclusively for menu use. + UINT8 size; + UINT8 sending; + UINT8 anchor; + roundentry_t entries[ROUNDQUEUE_MAX]; +} menuqueue; + void G_MapSlipIntoRoundQueue(UINT8 position, UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted); void G_MapIntoRoundQueue(UINT16 map, UINT8 setgametype, boolean setencore, boolean rankrestricted); void G_GPCupIntoRoundQueue(cupheader_t *cup, UINT8 setgametype, boolean setencore); diff --git a/src/k_hud.cpp b/src/k_hud.cpp index 91bd054f1..bba3d1d9b 100644 --- a/src/k_hud.cpp +++ b/src/k_hud.cpp @@ -1536,6 +1536,46 @@ void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, p ); } +void K_DrawMapAsFace(INT32 x, INT32 y, UINT32 flags, UINT16 map, const UINT8 *colormap) +{ + const fixed_t iconHeight = (14 << FRACBITS); + const fixed_t iconWidth = (iconHeight * 320) / 200; + INT32 unit = 1; + fixed_t mul = FRACUNIT; + if (flags & V_NOSCALESTART) + { + unit = (vid.dupx < vid.dupy ? vid.dupx : vid.dupy); + mul = 1; + } + + V_DrawFill( + x, + y, + 16 * unit, + 16 * unit, + (flags & ~V_FLIP) + ); + + V_SetClipRect( + (x + unit) * mul, + (y + unit) * mul, + (14 * unit) * mul, + (14 * unit) * mul, + (flags & ~V_FLIP) + ); + + K_DrawMapThumbnail( + ((x + unit) * FRACUNIT) - (iconWidth - iconHeight)/2, + ((y + unit) * FRACUNIT), + iconWidth, + flags, + map, + colormap + ); + + V_ClearClipRect(); +} + // see also K_DrawNameTagItemSpy static void K_drawKartItem(void) { diff --git a/src/k_hud.h b/src/k_hud.h index 038be77f2..6a991bddf 100644 --- a/src/k_hud.h +++ b/src/k_hud.h @@ -60,6 +60,7 @@ void K_drawKart4PTimestamp(void); void K_drawEmeraldWin(boolean overlay); void K_DrawMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, UINT16 map, const UINT8 *colormap); void K_DrawLikeMapThumbnail(fixed_t x, fixed_t y, fixed_t width, UINT32 flags, patch_t *patch, const UINT8 *colormap); +void K_DrawMapAsFace(INT32 x, INT32 y, UINT32 flags, UINT16 map, const UINT8 *colormap); void K_drawTargetHUD(const vector3_t *origin, player_t *player); void K_drawButton(fixed_t x, fixed_t y, INT32 flags, patch_t *button[2], boolean pressed); void K_drawButtonAnim(INT32 x, INT32 y, INT32 flags, patch_t *button[2], tic_t animtic); diff --git a/src/k_menu.h b/src/k_menu.h index fbf8594b8..28db4810e 100644 --- a/src/k_menu.h +++ b/src/k_menu.h @@ -923,6 +923,7 @@ typedef struct levellist_s { UINT8 guessgt; levelsearch_t levelsearch; boolean netgame; // Start the game in an actual server + boolean canqueue; menu_t *backMenu; } levellist_t; @@ -944,6 +945,7 @@ void M_CupSelectTick(void); void M_LevelSelectHandler(INT32 choice); void M_LevelSelectTick(void); +INT16 M_LevelFromScrolledList(INT16 add); void M_MenuToLevelPreamble(UINT8 ssplayers, boolean nowipe); void M_LevelSelected(INT16 add, boolean menuupdate); boolean M_LevelSelectCupSwitch(boolean next, boolean skipones); diff --git a/src/k_menudraw.c b/src/k_menudraw.c index a06a6fec1..0c02b15ba 100644 --- a/src/k_menudraw.c +++ b/src/k_menudraw.c @@ -755,33 +755,40 @@ static void M_DrawMenuTyping(void) } -// Largely replaced by boxed drawing mode in K_DrawGameControl and rich text -/* -static void M_DrawMediocreKeyboardKey(const char *text, INT32 *workx, INT32 worky, boolean push, boolean rightaligned) +static void M_DrawPauseRoundQueue(INT16 offset, boolean canqueue) { - INT32 buttonwidth = V_StringWidth(text, 0) + 2; + y_data_t standings; + memset(&standings, 0, sizeof (standings)); - if (rightaligned) + if (gamestate == GS_MENU) { - (*workx) -= buttonwidth; - } - - if (push) - { - worky += 2; + standings.mainplayer = MAXPLAYERS; } else { - V_DrawFill((*workx)-1, worky+10, buttonwidth, 2, 24); + standings.mainplayer = (demo.playback ? displayplayers[0] : consoleplayer); } - V_DrawFill((*workx)-1, worky, buttonwidth, 10, 16); - V_DrawString( - (*workx), worky + 1, - 0, text - ); + // See also G_GetNextMap, Y_CalculateMatchData + if ( + canqueue == false + && grandprixinfo.gp == true + && netgame == false // TODO netgame Special Mode support + && grandprixinfo.gamespeed >= KARTSPEED_NORMAL + && roundqueue.size > 1 + && roundqueue.entries[roundqueue.size - 1].rankrestricted == true + && ( + gamedata->everseenspecial == true + || roundqueue.position == roundqueue.size + ) + ) + { + // Additional cases in which it should always be shown. + standings.showrank = true; + } + + Y_RoundQueueDrawer(&standings, offset, false, false, canqueue); } -*/ // Draw the message popup submenu void M_DrawMenuMessage(void) @@ -991,6 +998,16 @@ void M_Drawer(void) // Draw message overlay when needed M_DrawMenuMessage(); + + if ( + ( + currentMenu == &PLAY_LevelSelectDef + || currentMenu == &PLAY_CupSelectDef + ) && levellist.canqueue + ) + { + M_DrawPauseRoundQueue(0, true); + } } if (menuwipe) @@ -6288,30 +6305,11 @@ void M_DrawPause(void) V_DrawCenteredLSTitleLowString(220 + offset*2, 103, mainflags, word2); } + const boolean rulescheck = (K_CanChangeRules(false) && (server || IsPlayerAdmin(consoleplayer))); + boolean drawqueue = (rulescheck && (menuqueue.size > 0)); + if (gamestate != GS_INTERMISSION && roundqueue.size > 0) { - y_data_t standings; - memset(&standings, 0, sizeof (standings)); - - standings.mainplayer = (demo.playback ? displayplayers[0] : consoleplayer); - - // See also G_GetNextMap, Y_CalculateMatchData - if ( - grandprixinfo.gp == true - && netgame == false // TODO netgame Special Mode support - && grandprixinfo.gamespeed >= KARTSPEED_NORMAL - && roundqueue.size > 1 - && roundqueue.entries[roundqueue.size - 1].rankrestricted == true - && ( - gamedata->everseenspecial == true - || roundqueue.position == roundqueue.size - ) - ) - { - // Additional cases in which it should always be shown. - standings.showrank = true; - } - if (roundqueue.position > 0 && roundqueue.position <= roundqueue.size) { patch_t *smallroundpatch = ST_getRoundPicture(true); @@ -6328,7 +6326,7 @@ void M_DrawPause(void) V_DrawCenteredMenuString(24, 167 + offset/2, V_YELLOWMAP, M_GetGameplayMode()); - Y_RoundQueueDrawer(&standings, offset/2, false, false); + drawqueue = true; } else if (gametype == GT_TUTORIAL) { @@ -6347,6 +6345,11 @@ void M_DrawPause(void) { V_DrawMenuString(4, 188 + offset/2, V_YELLOWMAP, M_GetGameplayMode()); } + + if (drawqueue) + { + M_DrawPauseRoundQueue(offset/2, rulescheck); + } } void M_DrawKickHandler(void) diff --git a/src/k_vote.c b/src/k_vote.c index 419d0feec..e5eeb5c63 100644 --- a/src/k_vote.c +++ b/src/k_vote.c @@ -700,35 +700,13 @@ static void Y_DrawVoteThumbnail(fixed_t center_x, fixed_t center_y, fixed_t widt } else if (vote.stage_striking == false) // Angry map { - const fixed_t iconHeight = (14 << FRACBITS); - const fixed_t iconWidth = (iconHeight * 320) / 200; - - V_DrawFill( + K_DrawMapAsFace( fx + fw - whiteSq + dupx, fy + fh - whiteSq + dupy, - whiteSq, - whiteSq, - 0|flags|V_NOSCALESTART - ); - - V_SetClipRect( - fx + fw - whiteSq + (2 * dupx), - fy + fh - whiteSq + (2 * dupy), - whiteSq - (2 * dupx), - whiteSq - (2 * dupy), - flags|V_NOSCALESTART - ); - - K_DrawMapThumbnail( - ((fx + fw - whiteSq + (2 * dupx)) * FRACUNIT) - (iconWidth - iconHeight), - (fy + fh - whiteSq + (2 * dupy)) * FRACUNIT, - iconWidth, flags | V_NOSCALESTART | ((encore == true) ? V_FLIP : 0), g_voteLevels[v][0], NULL ); - - V_ClearClipRect(); } } } diff --git a/src/menus/main-goner.cpp b/src/menus/main-goner.cpp index 5539ed5a2..75b41d172 100644 --- a/src/menus/main-goner.cpp +++ b/src/menus/main-goner.cpp @@ -1272,6 +1272,7 @@ void M_GonerTutorial(INT32 choice) // Please also see M_LevelSelectInit as called in extras-1.c levellist.netgame = false; + levellist.canqueue = false; levellist.levelsearch.checklocked = true; levellist.levelsearch.grandprix = false; levellist.levelsearch.timeattack = false; diff --git a/src/menus/play-online-host.c b/src/menus/play-online-host.c index a52f04822..ccc3214c2 100644 --- a/src/menus/play-online-host.c +++ b/src/menus/play-online-host.c @@ -172,6 +172,7 @@ void M_MPSetupNetgameMapSelect(INT32 choice) // Yep, we'll be starting a netgame. levellist.netgame = true; + levellist.canqueue = true; // Make sure we reset those levellist.levelsearch.timeattack = false; levellist.levelsearch.checklocked = true; diff --git a/src/menus/transient/level-select.c b/src/menus/transient/level-select.c index 78709305c..34069e02b 100644 --- a/src/menus/transient/level-select.c +++ b/src/menus/transient/level-select.c @@ -21,6 +21,7 @@ #include "../../v_video.h" #include "../../g_game.h" // G_GetBackupCupData #include "../../p_saveg.h" // cupsavedata +#include "../../d_netcmd.h" // Handle_MapQueueSend cupheader_t dummy_lostandfound; @@ -657,6 +658,7 @@ void M_LevelSelectInit(INT32 choice) case 0: levellist.levelsearch.grandprix = false; levellist.levelsearch.timeattack = false; + levellist.canqueue = true; CV_StealthSet(&cv_kartbot, cv_dummymatchbots.string); CV_StealthSet(&cv_kartencore, (cv_dummygpencore.value == 1) ? "On" : "Auto"); @@ -666,10 +668,12 @@ void M_LevelSelectInit(INT32 choice) case 1: levellist.levelsearch.grandprix = false; levellist.levelsearch.timeattack = true; + levellist.canqueue = false; break; case 2: levellist.levelsearch.grandprix = true; levellist.levelsearch.timeattack = false; + levellist.canqueue = false; break; default: CONS_Alert(CONS_WARNING, "Bad level select init\n"); @@ -732,7 +736,7 @@ void M_MenuToLevelPreamble(UINT8 ssplayers, boolean nowipe) SV_StartSinglePlayerServer(levellist.newgametype, levellist.netgame); } -void M_LevelSelected(INT16 add, boolean menuupdate) +INT16 M_LevelFromScrolledList(INT16 add) { UINT8 i = 0; INT16 map = M_GetFirstLevelInList(&i, &levellist.levelsearch); @@ -749,6 +753,13 @@ void M_LevelSelected(INT16 add, boolean menuupdate) add--; } + return map; +} + +void M_LevelSelected(INT16 add, boolean menuupdate) +{ + INT16 map = M_LevelFromScrolledList(add); + if (map >= nummapheaders) { // This shouldn't happen @@ -810,6 +821,82 @@ void M_LevelSelected(INT16 add, boolean menuupdate) } } +static void M_MenuQueueStopSend(INT32 ch) +{ + (void)ch; + + memset(&menuqueue, 0, sizeof(struct menuqueue)); +} + +static void M_MenuQueueSelectedLocal(void) +{ + UINT8 i = 0; + + for (; i < menuqueue.size; i++) + { + G_MapIntoRoundQueue( + menuqueue.entries[i].mapnum, + menuqueue.entries[i].gametype, + menuqueue.entries[i].encore, + false + ); + } + + roundqueue.netcommunicate = true; + + if (gamestate == GS_MENU) + { + levellist.choosemap = roundqueue.entries[0].mapnum; + + multiplayer = true; + + M_MenuToLevelPreamble( + cv_splitplayers.value-1, + ( + roundqueue.entries[0].encore + && (gametypes[roundqueue.entries[0].gametype]->rules & GTR_ENCORE) + ) + ); + + restoreMenu = (levellist.netgame) + ? &PLAY_MP_OptSelectDef + : currentMenu; + + restorelevellist = levellist; + + roundqueue.position = roundqueue.roundnum = 1; + + D_MapChange( + roundqueue.entries[0].mapnum + 1, + roundqueue.entries[0].gametype, + roundqueue.entries[0].encore, + true, + 1, + false, + roundqueue.entries[0].rankrestricted + ); + + M_MenuQueueStopSend(MA_NONE); + + M_ClearMenus(true); + } + else + { + if (gamestate == GS_LEVEL && roundqueue.position == 0) + { + menuqueue.sending = UINT8_MAX; + } + else + { + S_StartSound(NULL, sfx_chchng); + + M_MenuQueueStopSend(MA_NONE); + + M_ClearMenus(true); + } + } +} + boolean M_LevelSelectCupSwitch(boolean next, boolean skipones) { levelsearch_t templevelsearch = levellist.levelsearch; @@ -891,6 +978,44 @@ boolean M_LevelSelectCupSwitch(boolean next, boolean skipones) } } +static void M_MenuQueueResponse(INT32 ch) +{ + M_ClearMenus(false); + + if (ch != MA_YES) + return; + + if (!(server || (IsPlayerAdmin(consoleplayer)))) + return; + + if (!(gamestate == GS_LEVEL && roundqueue.position == 0)) + return; + + SendNetXCmd(XD_EXITLEVEL, NULL, 0); +} + + +static void M_ClearQueueResponse(INT32 ch) +{ + if (ch != MA_YES) + return; + + if (!(server || (IsPlayerAdmin(consoleplayer)))) + return; + + S_StartSound(NULL, sfx_slip); + + if (netgame) + { + if (roundqueue.size) + { + Handle_MapQueueSend(0, ROUNDQUEUE_CMD_CLEAR, false); + } + return; + } + + memset(&roundqueue, 0, sizeof(struct roundqueue)); +} void M_LevelSelectHandler(INT32 choice) { const UINT8 pid = 0; @@ -902,6 +1027,11 @@ void M_LevelSelectHandler(INT32 choice) return; } + if (menuqueue.sending) + { + return; + } + if (menucmd[pid].dpad_ud > 0) { levellist.cursor++; @@ -934,10 +1064,96 @@ void M_LevelSelectHandler(INT32 choice) M_LevelSelectScrollDest(); - if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/) + if (M_MenuConfirmPressed(pid)) { + // Starting immediately OR importing queue + M_SetMenuDelay(pid); - M_LevelSelected(levellist.cursor, true); + + while ((menuqueue.size + roundqueue.size) > ROUNDQUEUE_MAX) + menuqueue.size--; + + if (!levellist.canqueue || !menuqueue.size) + { + M_LevelSelected(levellist.cursor, true); + } + else if (netgame) + { + menuqueue.anchor = roundqueue.size; + menuqueue.sending = 1; + + M_StartMessage("Queueing Rounds", + va(M_GetText( + "Attempting to send %d Round%s...\n" + "\n" + "If this is taking longer than you\n" + "expect, exit out of this message.\n" + ), menuqueue.size, (menuqueue.size == 1 ? "" : "s") + ), &M_MenuQueueStopSend, MM_NOTHING, + NULL, + "This is taking too long..." + ); + } + else + { + M_MenuQueueSelectedLocal(); + } + } + else if (levellist.canqueue && M_MenuButtonPressed(pid, MBT_Z)) + { + // Adding to queue + + INT16 map = NEXTMAP_INVALID; + + M_SetMenuDelay(pid); + + if ((roundqueue.size + menuqueue.size) < ROUNDQUEUE_MAX) + { + map = M_LevelFromScrolledList(levellist.cursor); + } + + if (map < nummapheaders) + { + memset(menuqueue.entries+menuqueue.size, 0, sizeof(roundentry_t)); + menuqueue.entries[menuqueue.size].mapnum = map; + menuqueue.entries[menuqueue.size].gametype = levellist.newgametype; + menuqueue.entries[menuqueue.size].encore = (cv_kartencore.value == 1); + + menuqueue.size++; + + S_StartSound(NULL, sfx_s3k4a); + } + else + { + S_StartSound(NULL, sfx_s3kb2); + } + } + else if (levellist.canqueue && M_MenuExtraPressed(pid)) + { + while ((menuqueue.size + roundqueue.size) > ROUNDQUEUE_MAX) + menuqueue.size--; + + if (menuqueue.size) + { + S_StartSound(NULL, sfx_shldls); + menuqueue.size--; + } + else if (roundqueue.size) + { + M_StartMessage("Queue Clearing", + va(M_GetText( + "There %s %d Round%s of play queued.\n" + "\n" + "Do you want to empty the queue?\n" + ), + (roundqueue.size == 1 ? "is" : "are"), + roundqueue.size, + (roundqueue.size == 1 ? "" : "s") + ), &M_ClearQueueResponse, MM_YESNO, + "Time to start fresh", + "Not right now" + ); + } } else if (M_MenuBackPressed(pid)) { @@ -952,4 +1168,55 @@ void M_LevelSelectHandler(INT32 choice) void M_LevelSelectTick(void) { + if (!menuqueue.sending) + return; + + if ((menuqueue.sending <= menuqueue.size) // Sending + && (roundqueue.size >= menuqueue.anchor)) // Didn't get it wiped + { + if (!netgame) + return; + + const UINT8 idcount = (roundqueue.size - menuqueue.anchor); + + if (idcount == menuqueue.sending-1) + { + Handle_MapQueueSend( + menuqueue.entries[idcount].mapnum, + menuqueue.entries[idcount].gametype, + menuqueue.entries[idcount].encore + ); + } + if (idcount >= menuqueue.sending-1) + { + menuqueue.sending++; + } + return; + } + + if (menuqueue.size) + { + S_StartSound(NULL, sfx_chchng); + + if (roundqueue.position) + { + M_StopMessage(MA_NONE); + } + else + { + M_StartMessage("Round Queue", + va(M_GetText( + "You just queued %d Round%s of play.\n" + "\n" + "Do you want to skip the current\n" + "course and start them immediately?\n" + ), menuqueue.size, (menuqueue.size == 1 ? "" : "s") + ), &M_MenuQueueResponse, MM_YESNO, + "Let's get going!", + "Not right now" + ); + } + } + + M_MenuQueueStopSend(MA_NONE); } diff --git a/src/y_inter.cpp b/src/y_inter.cpp index b2159285a..d2c509424 100644 --- a/src/y_inter.cpp +++ b/src/y_inter.cpp @@ -971,11 +971,15 @@ void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset) // Handles drawing the bottom-of-screen progression. // Currently requires intermission y_data for animation only. // -void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, boolean widescreen) +void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, boolean widescreen, boolean adminmode) { if (roundqueue.size == 0) { - return; + if (!adminmode + || menuqueue.size == 0) + { + return; + } } // The following is functionally a hack. @@ -1041,6 +1045,10 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, prize_dot[BPP_AHEAD] = static_cast(W_CachePatchName("R_RRMRK4", PU_PATCH)); prize_dot[BPP_DONE] = static_cast(W_CachePatchName("R_RRMRK6", PU_PATCH)); + patch_t *rpmark[2]; + rpmark[0] = static_cast(W_CachePatchName("R_RPMARK", PU_PATCH)); + rpmark[1] = static_cast(W_CachePatchName("R_R2MARK", PU_PATCH)); + UINT8 *colormap = NULL, *oppositemap = NULL; fixed_t playerx = 0, playery = 0; UINT8 pskin = MAXSKINS; @@ -1079,10 +1087,37 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, upwa = true; } - workingqueuesize--; + if (!adminmode) + { + workingqueuesize--; + } } - INT32 widthofroundqueue = 24*(workingqueuesize - 1); + INT32 widthofroundqueue, totalsteps; + + INT32 menusendoffset = 0; + if (menuqueue.sending) + { + if (menuqueue.sending > menuqueue.size) + { + menusendoffset = menuqueue.size; + } + else + { + menusendoffset = menuqueue.sending-1; + } + } + + if (adminmode) + { + totalsteps = std::min(workingqueuesize + (menuqueue.size - menusendoffset), ROUNDQUEUE_MAX); + } + else + { + totalsteps = workingqueuesize; + } + + widthofroundqueue = (totalsteps - 1) * 24; INT32 x = (BASEVIDWIDTH - widthofroundqueue) / 2; INT32 y, basey = 167 + offset; @@ -1091,7 +1126,7 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, // The following block handles horizontal easing of the // progression bar on the last non-rankrestricted round. - if (standings->showrank == true) + if (!adminmode && standings->showrank == true) { fixed_t percentslide = 0; SINT8 deferxoffs = 0; @@ -1151,12 +1186,22 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, V_DrawMappedPatch(xiter, basey, baseflags, queuebg_upwa, greymap); } + // Draw to left side of screen while (xiter > -bufferspace) { xiter -= 24; V_DrawMappedPatch(xiter, basey, baseflags, queuebg_flat, greymap); } + // Draw to right side of screen + xiter = x + widthofroundqueue; + while (xiter < BASEVIDWIDTH + bufferspace) + { + xiter += 24; + V_DrawMappedPatch(xiter, basey, baseflags, queuebg_flat, greymap); + } + + // Actually queued maps for (i = 0; i < workingqueuesize; i++) { // Draw the background, and grab the appropriate line, to the right of the dot @@ -1186,7 +1231,13 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, } else { - V_DrawMappedPatch(x, basey, baseflags, queuebg_flat, greymap); + V_DrawMappedPatch( + x, + basey, + baseflags, + ((workingqueuesize == totalsteps) ? queuebg_flat : queuebg_upwa), + greymap + ); } } @@ -1321,17 +1372,9 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, } else { - // No more line! Fill in background to right edge of screen - xiter = x; - while (xiter < BASEVIDWIDTH + bufferspace) - { - xiter += 24; - V_DrawMappedPatch(xiter, basey, baseflags, queuebg_flat, greymap); - } - // Handle special entry on the end // (has to be drawn before the semifinal dot due to overlap) - if (standings->showrank == true) + if (!adminmode && standings->showrank == true) { const fixed_t x2 = x + spacetospecial; @@ -1342,7 +1385,7 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, } else if ( doanimations == true - && roundqueue.position == roundqueue.size-1 + && roundqueue.position == workingqueuesize && timer - interpoffs <= 2*TICRATE ) { @@ -1522,13 +1565,62 @@ void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, x += 24; } + totalsteps -= i; + + // Maps in the progress of being queued on the menu + if (adminmode && totalsteps) + { + for (i = menusendoffset; i < (totalsteps + menusendoffset); i++) + { + upwa ^= true; + if (upwa == false) + { + y = basey + 4; + + V_DrawMappedPatch(x, basey, baseflags, queuebg_down, greymap); + } + else + { + y = basey + 12; + + if (i+1 != menuqueue.size) // no more line? + { + V_DrawMappedPatch(x, basey, baseflags, queuebg_upwa, greymap); + } + else + { + V_DrawMappedPatch(x, basey, baseflags, queuebg_flat, greymap); + } + } + + V_DrawMappedPatch( + x - 8, y, + baseflags, + level_dot[BPP_AHEAD], + NULL + ); + + V_DrawMappedPatch( + x - 10, y - 14, + baseflags, + rpmark[0], + NULL + ); + + K_DrawMapAsFace( + x - 9, y - 13, + (baseflags|((menuqueue.entries[i].encore) ? V_FLIP : 0)), + menuqueue.entries[i].mapnum, + NULL + ); + + x += 24; + } + } + // Draw the player position through the round queue! if (playery != 0) { - patch_t *rpmark[2]; - rpmark[0] = static_cast(W_CachePatchName("R_RPMARK", PU_PATCH)); - rpmark[1] = static_cast(W_CachePatchName("R_R2MARK", PU_PATCH)); - // Change alignment playerx -= (10 * FRACUNIT); playery -= (14 * FRACUNIT); @@ -1934,7 +2026,7 @@ skiptallydrawer: goto finalcounter; // Returns early if there's no roundqueue entries to draw - Y_RoundQueueDrawer(&data, 0, true, false); + Y_RoundQueueDrawer(&data, 0, true, false, false); if (netgame) { diff --git a/src/y_inter.h b/src/y_inter.h index 0a92758ee..b98c130d3 100644 --- a/src/y_inter.h +++ b/src/y_inter.h @@ -54,7 +54,7 @@ void Y_Ticker(void); // Specific sub-drawers void Y_PlayerStandingsDrawer(y_data_t *standings, INT32 xoffset); -void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, boolean widescreen); +void Y_RoundQueueDrawer(y_data_t *standings, INT32 offset, boolean doanimations, boolean widescreen, boolean adminmode); void Y_DrawIntermissionButton(INT32 startslide, INT32 through, boolean widescreen); void Y_DrawRankMode(INT32 x, INT32 y, boolean center); From d9faf64463184c900a0c58a914fd6dcda79f54b5 Mon Sep 17 00:00:00 2001 From: toaster Date: Sun, 1 Jun 2025 01:08:55 +0100 Subject: [PATCH 5/5] Don't show the "start queue?" message outside of level play --- src/menus/transient/level-select.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/menus/transient/level-select.c b/src/menus/transient/level-select.c index 34069e02b..30c39ef75 100644 --- a/src/menus/transient/level-select.c +++ b/src/menus/transient/level-select.c @@ -1198,7 +1198,7 @@ void M_LevelSelectTick(void) { S_StartSound(NULL, sfx_chchng); - if (roundqueue.position) + if (roundqueue.position || gamestate != GS_LEVEL) { M_StopMessage(MA_NONE); }