diff --git a/src/d_netcmd.c b/src/d_netcmd.c index 7f408a2b5..b6558c58b 100644 --- a/src/d_netcmd.c +++ b/src/d_netcmd.c @@ -3895,7 +3895,28 @@ static void Command_ExitLevel_f(void) else if (gamestate != GS_LEVEL || demoplayback) CONS_Printf(M_GetText("You must be in a level to use this.\n")); else + { + if ((netgame || multiplayer) + && ((mapheaderinfo[gamemap-1]->nextlevel <= 0) + || (mapheaderinfo[gamemap-1]->nextlevel > NUMMAPS) + || !(mapvisited[mapheaderinfo[gamemap-1]->nextlevel-1]))) + { + UINT8 i; + for (i = 0; i < MAXPLAYERS; i++) + { + if (playeringame[i] && players[i].exiting) + break; + } + + if (i == MAXPLAYERS) + { + CONS_Printf(M_GetText("Someone must finish the level for you to use this.\n")); + return; + } + } + SendNetXCmd(XD_EXITLEVEL, NULL, 0); + } } static void Got_ExitLevelcmd(UINT8 **cp, INT32 playernum) diff --git a/src/m_cheat.c b/src/m_cheat.c index f988c0fd5..2c17203bc 100644 --- a/src/m_cheat.c +++ b/src/m_cheat.c @@ -131,6 +131,10 @@ static cheatseq_t cheat_ultimate_joy = { SCRAMBLE(KEY_ENTER), 0xff } }; +#ifndef DEVELOP +Probably time to change these cheats? +#endif + static cheatseq_t cheat_warp = { 0, cheatf_warp, { SCRAMBLE('r'), SCRAMBLE('e'), SCRAMBLE('d'), SCRAMBLE('x'), SCRAMBLE('v'), SCRAMBLE('i'), 0xff } diff --git a/src/m_menu.c b/src/m_menu.c index d196c739d..c1b1479fb 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -254,7 +254,6 @@ static void M_Options(INT32 choice); static void M_SelectableClearMenus(INT32 choice); static void M_Retry(INT32 choice); static void M_EndGame(INT32 choice); -static void M_GameTypeChange(INT32 choice); static void M_MapChange(INT32 choice); static void M_ChangeLevel(INT32 choice); static void M_ConfirmSpectate(INT32 choice); @@ -352,7 +351,6 @@ static void M_DrawSkyRoom(void); static void M_DrawChecklist(void); static void M_DrawEmblemHints(void); static void M_DrawPauseMenu(void); -static void M_DrawGameTypeMenu(void); static void M_DrawServerMenu(void); static void M_DrawLevelPlatterMenu(void); static void M_DrawImageDef(void); @@ -545,7 +543,7 @@ static menuitem_t MPauseMenu[] = { {IT_STRING | IT_CALL, NULL, "Add-ons...", M_Addons, 8}, {IT_STRING | IT_SUBMENU, NULL, "Scramble Teams...", &MISC_ScrambleTeamDef, 16}, - {IT_STRING | IT_CALL, NULL, "Switch Gametype/Level...", M_GameTypeChange, 24}, + {IT_STRING | IT_CALL, NULL, "Switch Gametype/Level...", M_MapChange, 24}, {IT_STRING | IT_CALL, NULL, "Continue", M_SelectableClearMenus,40}, {IT_STRING | IT_CALL, NULL, "Player 1 Setup", M_SetupMultiPlayer, 48}, // splitscreen @@ -628,32 +626,16 @@ static menuitem_t MISC_ChangeTeamMenu[] = {IT_WHITESTRING|IT_CALL, NULL, "Confirm", M_ConfirmTeamChange, 90}, }; -static menuitem_t MISC_ChangeGameTypeMenu[] = -{ - {IT_STRING|IT_CALL, NULL, "Co-op", M_MapChange, 0}, - - {IT_STRING|IT_CALL, NULL, "Competition", M_MapChange, 12}, - {IT_STRING|IT_CALL, NULL, "Race", M_MapChange, 20}, - - {IT_STRING|IT_CALL, NULL, "Match", M_MapChange, 32}, - {IT_STRING|IT_CALL, NULL, "Team Match", M_MapChange, 40}, - - {IT_STRING|IT_CALL, NULL, "Tag", M_MapChange, 52}, - {IT_STRING|IT_CALL, NULL, "Hide & Seek", M_MapChange, 60}, - - {IT_STRING|IT_CALL, NULL, "Capture the Flag", M_MapChange, 72}, -}; - static const gtdesc_t gametypedesc[] = { - {"Play through the single-player campaign with your friends, teaming up to beat Dr Eggman's nefarious challenges!"}, - {"Speed your way through the main acts, competing in several different categories to see who's the best."}, - {"There's not much to it - zoom through the level faster than everyone else."}, - {"Sling rings at your foes in a free-for-all battle. Use the special weapon rings to your advantage!"}, - {"Sling rings at your foes in a color-coded battle. Use the special weapon rings to your advantage!"}, - {"Whoever's IT has to hunt down everyone else. If you get caught, you have to turn on your former friends!"}, - {"Try and find a good hiding place in these maps - we dare you."}, - {"Steal the flag from the enemy's base and bring it back to your own, but watch out - they could just as easily steal yours!"}, + {{ 54, 54}, "Play through the single-player campaign with your friends, teaming up to beat Dr Eggman's nefarious challenges!"}, + {{103, 103}, "Speed your way through the main acts, competing in several different categories to see who's the best."}, + {{190, 190}, "There's not much to it - zoom through the level faster than everyone else."}, + {{ 66, 66}, "Sling rings at your foes in a free-for-all battle. Use the special weapon rings to your advantage!"}, + {{153, 37}, "Sling rings at your foes in a color-coded battle. Use the special weapon rings to your advantage!"}, + {{123, 123}, "Whoever's IT has to hunt down everyone else. If you get caught, you have to turn on your former friends!"}, + {{135, 135}, "Try and find a good hiding place in these maps - we dare you."}, + {{ 37, 153}, "Steal the flag from the enemy's base and bring it back to your own, but watch out - they could just as easily steal yours!"}, }; static menuitem_t MISC_ChangeLevelMenu[] = @@ -930,7 +912,7 @@ static menuitem_t SP_PlayerMenu[] = // Separated splitscreen and normal servers. static menuitem_t MP_SplitServerMenu[] = { - {IT_STRING|IT_CALL, NULL, "Select Gametype/Level...", M_GameTypeChange, 100}, + {IT_STRING|IT_CALL, NULL, "Select Gametype/Level...", M_MapChange, 100}, #ifdef NONET // In order to keep player setup accessible. {IT_STRING|IT_CALL, NULL, "Player 1 setup...", M_SetupMultiPlayer, 110}, {IT_STRING|IT_CALL, NULL, "Player 2 setup...", M_SetupMultiPlayer2, 120}, @@ -960,7 +942,7 @@ static menuitem_t MP_ServerMenu[] = {IT_STRING|IT_CVAR|IT_CV_STRING, NULL, "Server Name", &cv_servername, 20}, {IT_STRING|IT_CVAR, NULL, "Max Players", &cv_maxplayers, 46}, {IT_STRING|IT_CVAR, NULL, "Allow Add-on Downloading", &cv_downloading, 56}, - {IT_STRING|IT_CALL, NULL, "Select Gametype/Level...", M_GameTypeChange, 100}, + {IT_STRING|IT_CALL, NULL, "Select Gametype/Level...", M_MapChange, 100}, {IT_STRING|IT_CALL, NULL, "More Options...", M_ServerOptions, 130}, {IT_WHITESTRING|IT_CALL, NULL, "Start", M_StartServer, 140}, }; @@ -1507,22 +1489,11 @@ menu_t MISC_ScrambleTeamDef = DEFAULTMENUSTYLE(NULL, MISC_ScrambleTeamMenu, &MPa menu_t MISC_ChangeTeamDef = DEFAULTMENUSTYLE(NULL, MISC_ChangeTeamMenu, &MPauseDef, 27, 40); // MP Gametype and map change menu -menu_t MISC_ChangeGameTypeDef = -{ - NULL, - sizeof (MISC_ChangeGameTypeMenu)/sizeof (menuitem_t), - &MainDef, // Doesn't matter. - MISC_ChangeGameTypeMenu, - M_DrawGameTypeMenu, - 30, (200 - (72+8))/2, // vertically centering - 0, - NULL -}; menu_t MISC_ChangeLevelDef = { NULL, sizeof (MISC_ChangeLevelMenu)/sizeof (menuitem_t), - &MISC_ChangeGameTypeDef, + &MainDef, // Doesn't matter. MISC_ChangeLevelMenu, M_DrawLevelPlatterMenu, 0, 0, @@ -2538,21 +2509,11 @@ boolean M_Responder(event_t *ev) case KEY_DOWNARROW: M_NextOpt(); S_StartSound(NULL, sfx_menu1); - if (currentMenu == &MISC_ChangeGameTypeDef) - { - Z_Free(char_notes); - char_notes = NULL; - } return true; case KEY_UPARROW: M_PrevOpt(); S_StartSound(NULL, sfx_menu1); - if (currentMenu == &MISC_ChangeGameTypeDef) - { - Z_Free(char_notes); - char_notes = NULL; - } return true; case KEY_LEFTARROW: @@ -3981,6 +3942,9 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt) mapnum++; } + if (levellistmode == LLM_CREATESERVER) + rows++; + return rows; } @@ -3990,10 +3954,10 @@ static INT32 M_CountRowsToShowOnPlatter(INT32 gt) // Prepares a tasty dish of zones and acts! // Call before any attempt to access a level platter. // -static boolean M_PrepareLevelPlatter(INT32 gt) +static boolean M_PrepareLevelPlatter(INT32 gt, boolean nextmappick) { INT32 numrows = M_CountRowsToShowOnPlatter(gt); - INT32 mapnum = 0, prevmapnum = 0, col = 0, row = 0; + INT32 mapnum = 0, prevmapnum = 0, col = 0, row = 0, startrow = 0; if (!numrows) return false; @@ -4010,6 +3974,17 @@ static boolean M_PrepareLevelPlatter(INT32 gt) // done here so lsrow and lscol can be set if cv_nextmap is on the platter lsrow = lscol = lshli = lsoffs[0] = lsoffs[1] = 0; + if (levellistmode == LLM_CREATESERVER) + { + sprintf(levelselect.rows[0].header, "Gametype"); + lswide(0) = true; + levelselect.rows[row].mapavailable[2] = levelselect.rows[row].mapavailable[1] = levelselect.rows[row].mapavailable[0] = false; + startrow = row = 1; + + Z_Free(char_notes); + char_notes = NULL; + } + while (mapnum < NUMMAPS) { if (M_CanShowLevelOnPlatter(mapnum, gt)) @@ -4019,7 +3994,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt) const boolean wide = (mapheaderinfo[mapnum]->menuflags & LF2_WIDEICON); // preparing next position to drop mapnum into - if (levelselect.rows[0].maplist[0]) + if (levelselect.rows[startrow].maplist[0]) { if (col == 2 // no more space on the row? || wide @@ -4042,7 +4017,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt) levelselect.rows[row].mapavailable[2] = levelselect.rows[row].mapavailable[1] = levelselect.rows[row].mapavailable[0]; } - if (cv_nextmap.value == mapnum+1) // A little quality of life improvement. + if (nextmappick && cv_nextmap.value == mapnum+1) // A little quality of life improvement. { lsrow = row; lscol = col; @@ -4084,7 +4059,7 @@ static boolean M_PrepareLevelPlatter(INT32 gt) sprintf(levelselect.rows[row].mapnames[col], "???"); // creating header text - if (!col && (!row || !(fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[levelselect.rows[row-1].maplist[0]-1]->selectheading)))) + if (!col && ((row == startrow) || !(fastcmp(mapheaderinfo[mapnum]->selectheading, mapheaderinfo[levelselect.rows[row-1].maplist[0]-1]->selectheading)))) { if (!levelselect.rows[row].mapavailable[col]) sprintf(levelselect.rows[row].header, "???"); @@ -4126,12 +4101,11 @@ static boolean M_PrepareLevelPlatter(INT32 gt) return true; } -#define selectvalnextmapnobrace(column) selectval = levelselect.rows[lsrow].maplist[column];\ - if (selectval && levelselect.rows[lsrow].mapavailable[column])\ +#define ifselectvalnextmapnobrace(column) if ((selectval = levelselect.rows[lsrow].maplist[column]) && levelselect.rows[lsrow].mapavailable[column])\ {\ CV_SetValue(&cv_nextmap, selectval); -#define selectvalnextmap(column) selectvalnextmapnobrace(column)} +#define ifselectvalnextmap(column) ifselectvalnextmapnobrace(column)} // // M_HandleLevelPlatter @@ -4158,7 +4132,7 @@ static void M_HandleLevelPlatter(INT32 choice) S_StartSound(NULL,sfx_s3kb7); - selectvalnextmap(lscol) else selectvalnextmap(0) + ifselectvalnextmap(lscol) else ifselectvalnextmap(0) break; case KEY_UPARROW: @@ -4181,35 +4155,59 @@ static void M_HandleLevelPlatter(INT32 choice) S_StartSound(NULL,sfx_s3kb7); - selectvalnextmap(lscol) else selectvalnextmap(0) + ifselectvalnextmap(lscol) else ifselectvalnextmap(0) break; - case KEY_LEFTARROW: - if (lscol > 0) + case KEY_ENTER: + if (!(levellistmode == LLM_CREATESERVER && !lsrow)) { - lscol--; - - lsoffs[1] = (lswide(lsrow) ? -8 : lshseperation); - S_StartSound(NULL,sfx_s3kb7); - - selectvalnextmap(lscol) else selectvalnextmap(0) + ifselectvalnextmapnobrace(lscol) + lsoffs[0] = lsoffs[1] = 0; + S_StartSound(NULL,sfx_menu1); + if (gamestate == GS_TIMEATTACK) + M_SetupNextMenu(currentMenu->prevMenu); + else if (currentMenu == &MISC_ChangeLevelDef) + { + if (currentMenu->prevMenu && currentMenu->prevMenu != &MPauseDef) + M_SetupNextMenu(currentMenu->prevMenu); + else + M_ChangeLevel(0); + Z_Free(levelselect.rows); + levelselect.rows = NULL; + } + else + M_LevelSelectWarp(0); + Nextmap_OnChange(); + } + else if (!lsoffs[0]) // prevent sound spam + { + lsoffs[0] = -8; + S_StartSound(NULL,sfx_s3kb2); + } + break; } - else if (!lsoffs[1]) // prevent sound spam - { - lsoffs[1] = -8; - S_StartSound(NULL,sfx_s3kb7); - } - break; - + // intentionall fallthrough case KEY_RIGHTARROW: - if (lscol < 2) + if (levellistmode == LLM_CREATESERVER && !lsrow) + { + CV_AddValue(&cv_newgametype, 1); + S_StartSound(NULL,sfx_menu1); + lscol = 0; + + Z_Free(char_notes); + char_notes = NULL; + + if (!M_PrepareLevelPlatter(cv_newgametype.value, false)) + I_Error("Unidentified level platter failure!"); + } + else if (lscol < 2) { lscol++; lsoffs[1] = (lswide(lsrow) ? 8 : -lshseperation); S_StartSound(NULL,sfx_s3kb7); - selectvalnextmap(lscol) else selectvalnextmap(0) + ifselectvalnextmap(lscol) else ifselectvalnextmap(0) } else if (!lsoffs[1]) // prevent sound spam { @@ -4218,30 +4216,32 @@ static void M_HandleLevelPlatter(INT32 choice) } break; - case KEY_ENTER: - selectvalnextmapnobrace(lscol) - - lsoffs[0] = lsoffs[1] = 0; - S_StartSound(NULL,sfx_menu1); - if (gamestate == GS_TIMEATTACK) - M_SetupNextMenu(currentMenu->prevMenu); - else if (currentMenu == &MISC_ChangeLevelDef) - { - if (currentMenu->prevMenu && currentMenu->prevMenu->prevMenu != &MPauseDef) - M_SetupNextMenu(currentMenu->prevMenu->prevMenu); - else - M_ChangeLevel(0); - Z_Free(levelselect.rows); - levelselect.rows = NULL; - } - else - M_LevelSelectWarp(0); - Nextmap_OnChange(); - } - else if (!lsoffs[0]) // prevent sound spam + case KEY_LEFTARROW: + if (levellistmode == LLM_CREATESERVER && !lsrow) { - lsoffs[0] = -8; - S_StartSound(NULL,sfx_s3kb2); + CV_AddValue(&cv_newgametype, -1); + S_StartSound(NULL,sfx_menu1); + lscol = 0; + + Z_Free(char_notes); + char_notes = NULL; + + if (!M_PrepareLevelPlatter(cv_newgametype.value, false)) + I_Error("Unidentified level platter failure!"); + } + else if (lscol > 0) + { + lscol--; + + lsoffs[1] = (lswide(lsrow) ? -8 : lshseperation); + S_StartSound(NULL,sfx_s3kb7); + + ifselectvalnextmap(lscol) else ifselectvalnextmap(0) + } + else if (!lsoffs[1]) // prevent sound spam + { + lsoffs[1] = -8; + S_StartSound(NULL,sfx_s3kb7); } break; @@ -4262,8 +4262,12 @@ static void M_HandleLevelPlatter(INT32 choice) } else M_ClearMenus(true); + Z_Free(levelselect.rows); levelselect.rows = NULL; + + Z_Free(char_notes); + char_notes = NULL; } } @@ -4377,7 +4381,28 @@ static void M_DrawLevelPlatterRow(UINT8 row, INT32 y) y += lsheadingheight; } - if (lswide(row)) + if (levellistmode == LLM_CREATESERVER && !row) + { + if (!char_notes) + char_notes = V_WordWrap(0, 282 - 8, V_ALLOWLOWERCASE, gametypedesc[cv_newgametype.value].notes); + + V_DrawFill(lsbasex, y, 282, 50, 27); + V_DrawString(lsbasex + 4, y + 4, V_RETURN8|V_ALLOWLOWERCASE, char_notes); + + V_DrawFill(lsbasex, y+50, 141, 8, gametypedesc[cv_newgametype.value].col[0]); + V_DrawFill(lsbasex+141, y+50, 141, 8, gametypedesc[cv_newgametype.value].col[1]); + + V_DrawString(lsbasex, y+50, 0, gametype_cons_t[cv_newgametype.value].strvalue); + + if (!lsrow) + { + V_DrawCharacter(lsbasex - 10 - (skullAnimCounter/5), y+25, + '\x1C' | V_YELLOWMAP, false); + V_DrawCharacter(lsbasex+282 + 2 + (skullAnimCounter/5), y+25, + '\x1D' | V_YELLOWMAP, false); + } + } + else if (lswide(row)) M_DrawLevelPlatterWideMap(row, 0, lsbasex, y, rowhighlight); else { @@ -4411,10 +4436,13 @@ static void M_DrawLevelPlatterMenu(void) } // draw cursor box - V_DrawSmallScaledPatch(lsbasex + cursorx + lsoffs[1], lsbasey, 0, (levselp[sizeselect][((skullAnimCounter/4) ? 1 : 0)])); + if (levellistmode != LLM_CREATESERVER || lsrow) + V_DrawSmallScaledPatch(lsbasex + cursorx + lsoffs[1], lsbasey, 0, (levselp[sizeselect][((skullAnimCounter/4) ? 1 : 0)])); +#if 0 if (levelselect.rows[lsrow].maplist[lscol]) V_DrawScaledPatch(lsbasex + cursorx-17, lsbasey+50+lsoffs[0], 0, W_CachePatchName("M_CURSOR", PU_CACHE)); +#endif // handle movement of cursor box if (abs(lsoffs[0]) > 1) @@ -6012,7 +6040,7 @@ static void M_CustomLevelSelect(INT32 choice) levellistmode = LLM_LEVELSELECT; maplistoption = (UINT8)(unlockables[ul].variable); - if (!M_PrepareLevelPlatter(-1)) + if (!M_PrepareLevelPlatter(-1, true)) { M_StartMessage(M_GetText("No selectable levels found.\n"),NULL,MM_NOTHING); return; @@ -6044,7 +6072,7 @@ static void M_LoadGameLevelSelect(INT32 choice) levellistmode = LLM_LEVELSELECT; maplistoption = 1; - if (!M_PrepareLevelPlatter(-1)) + if (!M_PrepareLevelPlatter(-1, true)) { M_StartMessage(M_GetText("No selectable levels found.\n"),NULL,MM_NOTHING); return; @@ -7181,7 +7209,7 @@ static void M_TimeAttack(INT32 choice) SP_TimeAttackDef.prevMenu = &MainDef; levellistmode = LLM_RECORDATTACK; // Don't be dependent on cv_newgametype - if (!M_PrepareLevelPlatter(-1)) + if (!M_PrepareLevelPlatter(-1, true)) { M_StartMessage(M_GetText("No record-attackable levels found.\n"),NULL,MM_NOTHING); return; @@ -7358,7 +7386,7 @@ static void M_NightsAttack(INT32 choice) SP_NightsAttackDef.prevMenu = &MainDef; levellistmode = LLM_NIGHTSATTACK; // Don't be dependent on cv_newgametype - if (!M_PrepareLevelPlatter(-1)) + if (!M_PrepareLevelPlatter(-1, true)) { M_StartMessage(M_GetText("No NiGHTS-attackable levels found.\n"),NULL,MM_NOTHING); return; @@ -8063,42 +8091,17 @@ static void M_DrawServerMenu(void) } } -static void M_GameTypeChange(INT32 choice) +static void M_MapChange(INT32 choice) { (void)choice; - MISC_ChangeGameTypeDef.prevMenu = currentMenu; - M_SetupNextMenu(&MISC_ChangeGameTypeDef); - if (Playing()) - itemOn = gametype; - - Z_Free(char_notes); - char_notes = NULL; -} - -void M_DrawGameTypeMenu(void) -{ - M_DrawGenericMenu(); - M_DrawLevelPlatterHeader(currentMenu->y - lsheadingheight, "Select Gametype", true, false); - - if (!char_notes) - char_notes = V_WordWrap(0, (160 - 30) - 8, V_ALLOWLOWERCASE, gametypedesc[itemOn].notes); - - V_DrawFill(160, currentMenu->y, (160 - 30), 72 + 8, 159); - V_DrawString(164, currentMenu->y + 4, V_RETURN8|V_ALLOWLOWERCASE, char_notes); -} - -static void M_MapChange(INT32 choice) -{ MISC_ChangeLevelDef.prevMenu = currentMenu; levellistmode = LLM_CREATESERVER; - CV_SetValue(&cv_newgametype, choice); - - if (Playing() && !(M_CanShowLevelOnPlatter(cv_nextmap.value-1, choice)) && (M_CanShowLevelOnPlatter(gamemap-1, choice))) + if (Playing() && !(M_CanShowLevelOnPlatter(cv_nextmap.value-1, cv_newgametype.value)) && (M_CanShowLevelOnPlatter(gamemap-1, cv_newgametype.value))) CV_SetValue(&cv_nextmap, gamemap); - if (!M_PrepareLevelPlatter(choice)) + if (!M_PrepareLevelPlatter(cv_newgametype.value, (currentMenu == &MPauseDef))) { M_StartMessage(M_GetText("No selectable levels found.\n"),NULL,MM_NOTHING); return; diff --git a/src/m_menu.h b/src/m_menu.h index 53dc266d1..5d15dde19 100644 --- a/src/m_menu.h +++ b/src/m_menu.h @@ -201,6 +201,7 @@ typedef struct // descriptions for gametype select screen typedef struct { + UINT8 col[2]; char notes[441]; } gtdesc_t;