diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 55a3b30f9..1cc1adf8b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -1532,6 +1532,8 @@ void D_MapChange(INT32 mapnum, INT32 newgametype, boolean pultmode, boolean rese // The supplied data are assumed to be good. I_Assert(delay >= 0 && delay <= 2); + CV_SetValue(&cv_nextmap, mapnum); + CONS_Debug(DBG_GAMELOGIC, "Map change: mapnum=%d gametype=%d ultmode=%d resetplayers=%d delay=%d skipprecutscene=%d\n", mapnum, newgametype, pultmode, resetplayers, delay, skipprecutscene); diff --git a/src/m_menu.c b/src/m_menu.c index c9d8d0ca9..6895f674f 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -238,7 +238,9 @@ menu_t MISC_ScrambleTeamDef, MISC_ChangeTeamDef; // Single Player static void M_LoadGame(INT32 choice); +static void M_TimeAttackLevelSelect(INT32 choice); static void M_TimeAttack(INT32 choice); +static void M_NightsAttackLevelSelect(INT32 choice); static void M_NightsAttack(INT32 choice); static void M_Statistics(INT32 choice); static void M_ReplayTimeAttack(INT32 choice); @@ -354,6 +356,7 @@ static void M_HandleFogColor(INT32 choice); static void M_HandleVideoMode(INT32 choice); // Consvar onchange functions +static boolean M_SetNextMapOnPlatter(void); static void Nextmap_OnChange(void); static void Newgametype_OnChange(void); static void Dummymares_OnChange(void); @@ -666,10 +669,10 @@ static menuitem_t SR_EmblemHintMenu[] = // Single Player Main static menuitem_t SP_MainMenu[] = { - {IT_CALL | IT_STRING, NULL, "Start Game", M_LoadGame, 92}, - {IT_SECRET, NULL, "Record Attack", M_TimeAttack, 100}, - {IT_SECRET, NULL, "NiGHTS Mode", M_NightsAttack, 108}, - {IT_CALL | IT_STRING | IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 116}, + {IT_CALL | IT_STRING, NULL, "Start Game", M_LoadGame, 92}, + {IT_SECRET, NULL, "Record Attack", M_TimeAttackLevelSelect, 100}, + {IT_SECRET, NULL, "NiGHTS Mode", M_NightsAttackLevelSelect, 108}, + {IT_CALL | IT_STRING | IT_CALL_NOTMODIFIED, NULL, "Statistics", M_Statistics, 116}, }; enum @@ -692,6 +695,12 @@ static menuitem_t SP_LevelSelectMenu[] = {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelPlatter, '\0'}, // dummy menuitem for the control func }; +// Single Player Time Attack Level Select +static menuitem_t SP_TimeAttackLevelSelectMenu[] = +{ + {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelPlatter, '\0'}, // dummy menuitem for the control func +}; + // Single Player Time Attack static menuitem_t SP_TimeAttackMenu[] = { @@ -784,6 +793,12 @@ static menuitem_t SP_NightsGhostMenu[] = {IT_WHITESTRING|IT_SUBMENU, NULL, "Back", &SP_NightsAttackDef, 50} }; +// Single Player Nights Attack Level Select +static menuitem_t SP_NightsAttackLevelSelectMenu[] = +{ + {IT_KEYHANDLER | IT_NOTHING, NULL, "", M_HandleLevelPlatter, '\0'}, // dummy menuitem for the control func +}; + // Single Player Nights Attack static menuitem_t SP_NightsAttackMenu[] = { @@ -1478,7 +1493,7 @@ menu_t SP_LevelSelectDef = { NULL, sizeof (SP_LevelSelectMenu)/sizeof (menuitem_t), - &SP_LoadDef, + &MainDef, // Doesn't matter. SP_LevelSelectMenu, M_DrawLevelPlatterMenu, 0, 0, @@ -1509,6 +1524,17 @@ menu_t SP_LevelStatsDef = NULL }; +menu_t SP_TimeAttackLevelSelectDef = +{ + "M_ATTACK", + sizeof (SP_TimeAttackLevelSelectMenu)/sizeof (menuitem_t), + &MainDef, // Doesn't matter. + SP_TimeAttackLevelSelectMenu, + M_DrawLevelPlatterMenu, + 0, 0, + 0, + NULL +}; static menu_t SP_TimeAttackDef = { "M_ATTACK", @@ -1518,7 +1544,7 @@ static menu_t SP_TimeAttackDef = M_DrawTimeAttackMenu, 32, 40, 0, - NULL + M_SetNextMapOnPlatter }; static menu_t SP_ReplayDef = { @@ -1554,6 +1580,17 @@ static menu_t SP_GhostDef = NULL }; +menu_t SP_NightsAttackLevelSelectDef = +{ + "M_NIGHTS", // HAMALAYAN + sizeof (SP_NightsAttackLevelSelectMenu)/sizeof (menuitem_t), + &MainDef, // Doesn't matter. + SP_NightsAttackLevelSelectMenu, + M_DrawLevelPlatterMenu, + 0, 0, + 0, + NULL +}; static menu_t SP_NightsAttackDef = { "M_NIGHTS", @@ -1563,7 +1600,7 @@ static menu_t SP_NightsAttackDef = M_DrawNightsAttackMenu, 32, 40, 0, - NULL + M_SetNextMapOnPlatter }; static menu_t SP_NightsReplayDef = { @@ -1775,6 +1812,7 @@ static void Nextmap_OnChange(void) if (currentMenu == &SP_NightsAttackDef) { + M_SetNextMapOnPlatter(); CV_StealthSetValue(&cv_dummymares, 0); // Hide the record changing CVAR if only one mare is available. if (!nightsrecords[cv_nextmap.value-1] || nightsrecords[cv_nextmap.value-1]->nummares < 2) @@ -2414,7 +2452,7 @@ boolean M_Responder(event_t *ev) multiplayer = false; } - if (currentMenu == &SP_TimeAttackDef || currentMenu == &SP_NightsAttackDef) + if ((currentMenu->prevMenu == &MainDef) && (currentMenu == &SP_TimeAttackDef || currentMenu == &SP_NightsAttackDef)) { // D_StartTitle does its own wipe, since GS_TIMEATTACK is now a complete gamestate. menuactive = false; @@ -3604,6 +3642,27 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt) return rows; } +static boolean M_SetNextMapOnPlatter(void) +{ + INT32 row, col = 0; + while (col < 3) + { + row = 0; + while (row < levelselect.numrows) + { + if (levelselect.rows[row].maplist[col] == cv_nextmap.value) + { + lsrow = row; + lscol = col; + return true; + } + row++; + } + col++; + } + return true; +} + // // M_PrepareLevelPlatter // @@ -3613,7 +3672,7 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt) static boolean M_PrepareLevelPlatter(INT32 gt) { INT32 numrows = M_CountRowsToShowOnPlatter(gt); - INT32 mapnum, desiredmap, col = 0, row = 0; + INT32 mapnum, col = 0, row = 0; if (!numrows) return false; @@ -3630,8 +3689,6 @@ static boolean M_PrepareLevelPlatter(INT32 gt) // done here so lsrow and lscol can be set if cv_nextmap is on the platter lsrow = lscol = lstic = lshli = lsoffs[0] = lsoffs[1] = 0; - desiredmap = ((Playing()) ? gamemap: cv_nextmap.value); - for (mapnum = 0; mapnum < NUMMAPS; mapnum++) { if (M_CanShowLevelOnPlatter(mapnum, gt)) @@ -3655,7 +3712,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt) levelselect.rows[row].maplist[col] = mapnum+1; // putting the map on the platter levelselect.rows[row].mapavailable[col] = M_LevelAvailableOnPlatter(mapnum); - if (desiredmap == mapnum+1) // A little quality of life improvement. + if (cv_nextmap.value == mapnum+1) // A little quality of life improvement. { lsrow = row; lscol = col; @@ -3810,10 +3867,17 @@ static void M_HandleLevelPlatter(INT32 choice) case KEY_ENTER: selectvalnextmapnobrace(lscol) - M_LevelSelectWarp(0); - lsoffs[0] = lsoffs[1] = 0; S_StartSound(NULL,sfx_menu1); + if (gamestate == GS_TIMEATTACK) + { + if (currentMenu == &SP_TimeAttackLevelSelectDef) + M_TimeAttack(-1); + else + M_NightsAttack(-1); + } + else + M_LevelSelectWarp(0); } else if (!lsoffs[0]) // prevent sound spam { @@ -3833,7 +3897,16 @@ static void M_HandleLevelPlatter(INT32 choice) if (exitmenu) { if (currentMenu->prevMenu) - M_SetupNextMenu (currentMenu->prevMenu); + { + if (gamestate == GS_TIMEATTACK) + { + // D_StartTitle does its own wipe, since GS_TIMEATTACK is now a complete gamestate. + menuactive = false; + D_StartTitle(); + } + else + M_SetupNextMenu (currentMenu->prevMenu); + } else M_ClearMenus(true); } @@ -3917,6 +3990,9 @@ static void M_DrawLevelPlatterMenu(void) if (++lstic == 32) lstic = 0; + if (gamestate == GS_TIMEATTACK) + V_DrawPatchFill(W_CachePatchName("SRB2BACK", PU_CACHE)); + // finds row at top of the screen while (y > 0) { @@ -3948,6 +4024,8 @@ static void M_DrawLevelPlatterMenu(void) lsoffs[1] = 2*lsoffs[1]/3; else lsoffs[1] = 0; + + M_DrawMenuTitle(); } #undef basey @@ -5825,33 +5903,63 @@ void M_DrawTimeAttackMenu(void) } } -// Going to Time Attack menu... -static void M_TimeAttack(INT32 choice) +static void M_TimeAttackLevelSelect(INT32 choice) { (void)choice; + levellistmode = LLM_RECORDATTACK; - memset(skins_cons_t, 0, sizeof (skins_cons_t)); - - levellistmode = LLM_RECORDATTACK; // Don't be dependent on cv_newgametype - - if (M_CountLevelsToShowInList() == 0) + if (!M_PrepareLevelPlatter(-1)) { M_StartMessage(M_GetText("No record-attackable levels found.\n"),NULL,MM_NOTHING); return; } + memset(skins_cons_t, 0, sizeof (skins_cons_t)); M_PatchSkinNameTable(); - M_PrepareLevelSelect(); - M_SetupNextMenu(&SP_TimeAttackDef); - Nextmap_OnChange(); - - itemOn = tastart; // "Start" is selected. + M_SetupNextMenu(&SP_TimeAttackLevelSelectDef); G_SetGamestate(GS_TIMEATTACK); S_ChangeMusicInternal("_inter", true); } +// Going to Time Attack menu... +static void M_TimeAttack(INT32 choice) +{ + const boolean direct = (choice != -1); // Are we coming from SP_TimeAtttackLevelSelect? + + if (direct) + { + SP_TimeAttackDef.prevMenu = &MainDef; + levellistmode = LLM_RECORDATTACK; // Don't be dependent on cv_newgametype + + if (M_CountLevelsToShowInList() == 0) + { + M_StartMessage(M_GetText("No record-attackable levels found.\n"),NULL,MM_NOTHING); + return; + } + + memset(skins_cons_t, 0, sizeof (skins_cons_t)); + M_PatchSkinNameTable(); + + M_PrepareLevelSelect(); + } + else + SP_TimeAttackDef.prevMenu = currentMenu; + + M_SetupNextMenu(&SP_TimeAttackDef); + + if (direct) + { + Nextmap_OnChange(); + + G_SetGamestate(GS_TIMEATTACK); + S_ChangeMusicInternal("_inter", true); + } + + itemOn = tastart; // "Start" is selected. +} + // Drawing function for Nights Attack void M_DrawNightsAttackMenu(void) { @@ -5957,34 +6065,64 @@ void M_DrawNightsAttackMenu(void) } } -// Going to Nights Attack menu... -static void M_NightsAttack(INT32 choice) +static void M_NightsAttackLevelSelect(INT32 choice) { (void)choice; + levellistmode = LLM_NIGHTSATTACK; - memset(skins_cons_t, 0, sizeof (skins_cons_t)); - - levellistmode = LLM_NIGHTSATTACK; // Don't be dependent on cv_newgametype - - if (M_CountLevelsToShowInList() == 0) + if (!M_PrepareLevelPlatter(-1)) { M_StartMessage(M_GetText("No NiGHTS-attackable levels found.\n"),NULL,MM_NOTHING); return; } - // This is really just to make sure Sonic is the played character, just in case + memset(skins_cons_t, 0, sizeof (skins_cons_t)); M_PatchSkinNameTable(); - M_PrepareLevelSelect(); - M_SetupNextMenu(&SP_NightsAttackDef); - Nextmap_OnChange(); - - itemOn = nastart; // "Start" is selected. + M_SetupNextMenu(&SP_NightsAttackLevelSelectDef); G_SetGamestate(GS_TIMEATTACK); S_ChangeMusicInternal("_inter", true); } +// Going to Nights Attack menu... +static void M_NightsAttack(INT32 choice) +{ + const boolean direct = (choice != -1); // Are we coming from SP_TimeAtttackLevelSelect? + + if (direct) + { + SP_NightsAttackDef.prevMenu = &MainDef; + levellistmode = LLM_NIGHTSATTACK; // Don't be dependent on cv_newgametype + + if (M_CountLevelsToShowInList() == 0) + { + M_StartMessage(M_GetText("No NiGHTS-attackable levels found.\n"),NULL,MM_NOTHING); + return; + } + + // This is really just to make sure Sonic is the played character, just in case + memset(skins_cons_t, 0, sizeof (skins_cons_t)); + M_PatchSkinNameTable(); + + M_PrepareLevelSelect(); + } + else + SP_NightsAttackDef.prevMenu = currentMenu; + + M_SetupNextMenu(&SP_NightsAttackDef); + + if (direct) + { + Nextmap_OnChange(); + + G_SetGamestate(GS_TIMEATTACK); + S_ChangeMusicInternal("_inter", true); + } + + itemOn = nastart; // "Start" is selected. +} + // Player has selected the "START" from the nights attack screen static void M_ChooseNightsAttack(INT32 choice) { diff --git a/src/sdl/i_video.c b/src/sdl/i_video.c index aa572e6e0..7d33f2554 100644 --- a/src/sdl/i_video.c +++ b/src/sdl/i_video.c @@ -909,7 +909,7 @@ static inline boolean I_SkipFrame(void) case GS_LEVEL: if (!paused) return false; - case GS_TIMEATTACK: + //case GS_TIMEATTACK: -- sorry optimisation but now we have a cool level platter and that being laggardly looks terrible case GS_WAITINGPLAYERS: return skip; // Skip odd frames default: diff --git a/src/sdl12/i_video.c b/src/sdl12/i_video.c index 197924eda..1fa80e7d4 100644 --- a/src/sdl12/i_video.c +++ b/src/sdl12/i_video.c @@ -1311,7 +1311,7 @@ static inline boolean I_SkipFrame(void) case GS_LEVEL: if (!paused) return false; - case GS_TIMEATTACK: + //case GS_TIMEATTACK: -- sorry optimisation but now we have a cool level platter and that being laggardly looks terrible case GS_WAITINGPLAYERS: return skip; // Skip odd frames default: diff --git a/src/win32/win_vid.c b/src/win32/win_vid.c index 0960bb6dd..31d1b8120 100644 --- a/src/win32/win_vid.c +++ b/src/win32/win_vid.c @@ -322,7 +322,7 @@ static inline boolean I_SkipFrame(void) case GS_LEVEL: if (!paused) return false; - case GS_TIMEATTACK: + //case GS_TIMEATTACK: -- sorry optimisation but now we have a cool level platter and that being laggardly looks terrible #ifndef CLIENT_LOADINGSCREEN case GS_WAITINGPLAYERS: #endif