Merge branch 'rqdx-menu' into 'replay-kms'

Round Queue UI & Menu-to-Level transition cleanup

See merge request kart-krew-dev/ring-racers-internal!2599
This commit is contained in:
Oni VelocitOni 2025-06-01 04:05:14 +00:00
commit e03c5c0927
21 changed files with 640 additions and 216 deletions

View file

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

View file

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

View file

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

View file

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

View file

@ -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);
}
}
@ -4096,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)
{
@ -5379,7 +5390,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 +5842,7 @@ boolean G_GetExitGameFlag(void)
// Same deal with retrying.
void G_SetRetryFlag(void)
{
if (retrying == false)
if (retrying == false && grandprixinfo.gp)
{
grandprixinfo.rank.continuesUsed++;
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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,14 +658,22 @@ 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");
CV_StealthSet(&cv_kartspeed, (cv_dummykartspeed.value == KARTSPEED_NORMAL) ? "Auto Gear" : cv_dummykartspeed.string);
break;
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");
@ -676,10 +685,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))
@ -689,7 +702,41 @@ void M_LevelSelectInit(INT32 choice)
}
}
void M_LevelSelected(INT16 add, boolean menuupdate)
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);
}
INT16 M_LevelFromScrolledList(INT16 add)
{
UINT8 i = 0;
INT16 map = M_GetFirstLevelInList(&i, &levellist.levelsearch);
@ -706,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
@ -733,75 +787,116 @@ 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);
}
}
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;
@ -883,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;
@ -894,6 +1027,11 @@ void M_LevelSelectHandler(INT32 choice)
return;
}
if (menuqueue.sending)
{
return;
}
if (menucmd[pid].dpad_ud > 0)
{
levellist.cursor++;
@ -926,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))
{
@ -944,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 || gamestate != GS_LEVEL)
{
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);
}

View file

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

View file

@ -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;
}
//

View file

@ -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<patch_t*>(W_CachePatchName("R_RRMRK4", PU_PATCH));
prize_dot[BPP_DONE] = static_cast<patch_t*>(W_CachePatchName("R_RRMRK6", PU_PATCH));
patch_t *rpmark[2];
rpmark[0] = static_cast<patch_t*>(W_CachePatchName("R_RPMARK", PU_PATCH));
rpmark[1] = static_cast<patch_t*>(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<patch_t*>(W_CachePatchName("R_RPMARK", PU_PATCH));
rpmark[1] = static_cast<patch_t*>(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)
{

View file

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