"Special" time attack mode for 1P.

* Both GT_SPECIAL and GT_VERSUS.
* Access controlled by SECRET_SPECIALATTACK. (You're blue now.)

Related changes to precipitate:
* Cups that only have one map in them get selected immediately, rather than off-the-cuff.
    * Done by seperating out a new function M_LevelSelected from M_LevelSelectHandler
* Maps that only have one lap in them don't have a visible lap timestamp sticker.
* Fix a cup with *no* valid maps for the current ruleset being hypothetically selectable
This commit is contained in:
toaster 2022-12-27 22:34:02 +00:00
parent 15587417c7
commit 04f2ac4121
6 changed files with 162 additions and 104 deletions

View file

@ -2260,6 +2260,8 @@ void readunlockable(MYFILE *f, INT32 num)
unlockables[num].type = SECRET_TIMEATTACK;
else if (fastcmp(word2, "BREAKTHECAPSULES"))
unlockables[num].type = SECRET_BREAKTHECAPSULES;
else if (fastcmp(word2, "SPECIALATTACK"))
unlockables[num].type = SECRET_SPECIALATTACK;
else if (fastcmp(word2, "SOUNDTEST"))
unlockables[num].type = SECRET_SOUNDTEST;
else if (fastcmp(word2, "ALTTITLE"))

View file

@ -709,6 +709,7 @@ extern struct levellist_s {
UINT16 dest;
INT16 choosemap;
UINT8 newgametype;
UINT8 guessgt;
levelsearch_t levelsearch;
boolean netgame; // Start the game in an actual server
} levellist;

View file

@ -95,6 +95,9 @@ menuitem_t PLAY_GamemodesMenu[] =
{IT_STRING | IT_CALL, "Capsules", "Bust up all of the capsules in record time!",
NULL, {.routine = M_LevelSelectInit}, 1, GT_BATTLE},
{IT_STRING | IT_CALL, "Special", "Strike your target and secure the prize!",
NULL, {.routine = M_LevelSelectInit}, 1, GT_SPECIAL},
{IT_STRING | IT_CALL, "Back", NULL, NULL, {.routine = M_GoBack}, 0, 0},
};

View file

@ -1933,7 +1933,7 @@ static void M_DrawCupPreview(INT16 y, levelsearch_t *levelsearch)
V_DrawFill(0, y, BASEVIDWIDTH, 54, 31);
if (levelsearch->cup && !M_CupLocked(levelsearch->cup))
if (levelsearch->cup && !M_CupLocked(levelsearch->cup) && maxlevels > 0)
{
add = (cupgrid.previewanim / 82) % maxlevels;
map = start;
@ -2002,7 +2002,10 @@ static void M_DrawCupTitle(INT16 y, cupheader_t *cup)
else
{
if (currentMenu == &PLAY_LevelSelectDef)
V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, y+6, 0, va("%s Mode", gametypes[levellist.newgametype]->name));
{
UINT8 namedgt = (levellist.guessgt != MAXGAMETYPES) ? levellist.guessgt : levellist.newgametype;
V_DrawCenteredLSTitleLowString(BASEVIDWIDTH/2, y+6, 0, va("%s Mode", gametypes[namedgt]->name));
}
}
}
@ -2261,7 +2264,8 @@ void M_DrawTimeAttack(void)
laprec = mapheaderinfo[map]->mainrecord->lap;
}
if (gametypes[levellist.newgametype]->rules & GTR_CIRCUIT)
if ((gametypes[levellist.newgametype]->rules & GTR_CIRCUIT)
&& (mapheaderinfo[map]->numlaps != 1))
{
V_DrawRightAlignedString(rightedge-12, timeheight, highlightflags, "BEST LAP:");
K_drawKartTimestamp(laprec, 162+t, timeheight+6, 0, 2);

View file

@ -3295,25 +3295,40 @@ void M_SetupGametypeMenu(INT32 choice)
PLAY_GamemodesDef.prevMenu = currentMenu;
// Battle and Capsules disabled
// Battle and Capsules (and Special) disabled
PLAY_GamemodesMenu[1].status = IT_DISABLED;
PLAY_GamemodesMenu[2].status = IT_DISABLED;
PLAY_GamemodesMenu[3].status = IT_DISABLED;
if (cv_splitplayers.value > 1)
{
// Re-add Battle
PLAY_GamemodesMenu[1].status = IT_STRING | IT_CALL;
}
else if (M_SecretUnlocked(SECRET_BREAKTHECAPSULES, true))
{
// Re-add Capsules
PLAY_GamemodesMenu[2].status = IT_STRING | IT_CALL;
}
else
{
// Only one non-Back entry, let's skip straight to Race.
M_SetupRaceMenu(-1);
return;
boolean anyunlocked = false;
if (M_SecretUnlocked(SECRET_BREAKTHECAPSULES, true))
{
// Re-add Capsules
PLAY_GamemodesMenu[2].status = IT_STRING | IT_CALL;
anyunlocked = true;
}
if (M_SecretUnlocked(SECRET_SPECIALATTACK, true))
{
// Re-add Special
PLAY_GamemodesMenu[3].status = IT_STRING | IT_CALL;
anyunlocked = true;
}
if (!anyunlocked)
{
// Only one non-Back entry, let's skip straight to Race.
M_SetupRaceMenu(-1);
return;
}
}
M_SetupNextMenu(&PLAY_GamemodesDef, false);
@ -3533,12 +3548,25 @@ static void M_LevelSelectScrollDest(void)
static void M_LevelListFromGametype(INT16 gt)
{
static boolean first = true;
if (first || gt != levellist.newgametype)
if (first || gt != levellist.newgametype || levellist.guessgt != MAXGAMETYPES)
{
levellist.newgametype = gt;
levellist.levelsearch.typeoflevel = G_TOLFlag(gt);
if (levellist.levelsearch.timeattack == true && gt == GT_SPECIAL)
{
// Sneak in an extra.
levellist.levelsearch.typeoflevel |= G_TOLFlag(GT_VERSUS);
levellist.guessgt = gt;
}
else
{
levellist.guessgt = MAXGAMETYPES;
}
levellist.levelsearch.cupmode = (!(gametypes[gt]->rules & GTR_NOCUPSELECT));
levellist.levelsearch.cup = NULL;
first = false;
}
@ -3680,6 +3708,100 @@ void M_LevelSelectInit(INT32 choice)
M_LevelListFromGametype(currentMenu->menuitems[itemOn].mvar2);
}
static void M_LevelSelected(INT16 add)
{
UINT8 i = 0;
INT16 map = M_GetFirstLevelInList(&i, &levellist.levelsearch);
while (add > 0)
{
map = M_GetNextLevelInList(map, &i, &levellist.levelsearch);
if (map >= nummapheaders)
{
break;
}
add--;
}
if (map >= nummapheaders)
{
// This shouldn't happen
return;
}
levellist.choosemap = map;
if (levellist.levelsearch.timeattack)
{
S_StartSound(NULL, sfx_s3k63);
if (levellist.guessgt != MAXGAMETYPES)
levellist.newgametype = G_GuessGametypeByTOL(levellist.levelsearch.typeoflevel);
PLAY_TimeAttackDef.prevMenu = currentMenu;
M_SetupNextMenu(&PLAY_TimeAttackDef, false);
}
else
{
if (gamestate == GS_MENU)
{
UINT8 ssplayers = cv_splitplayers.value-1;
netgame = false;
multiplayer = true;
strncpy(connectedservername, cv_servername.string, MAXSERVERNAME);
// Still need to reset devmode
cht_debug = 0;
if (demo.playback)
G_StopDemo();
if (metalrecording)
G_StopMetalDemo();
/*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;
// Early fadeout to let the sound finish playing
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
F_WipeEndScreen();
F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
SV_StartSinglePlayerServer(levellist.newgametype, 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" : cv_dummykartspeed.string);
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false);
}
else
{
// directly do the map change
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false);
}
M_ClearMenus(true);
}
}
void M_CupSelectHandler(INT32 choice)
{
const UINT8 pid = 0;
@ -3733,13 +3855,18 @@ void M_CupSelectHandler(INT32 choice)
if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/)
{
INT16 count;
cupheader_t *newcup = cupgrid.builtgrid[CUPMENU_CURSORID];
cupheader_t *oldcup = levellist.levelsearch.cup;
M_SetMenuDelay(pid);
levellist.levelsearch.cup = newcup;
count = M_CountLevelsToShowInList(&levellist.levelsearch);
if ((!newcup)
|| (M_CupLocked(newcup))
|| (newcup->cachedlevels[0] == NEXTMAP_INVALID))
|| (count <= 0)
|| (cupgrid.grandprix == true && newcup->cachedlevels[0] == NEXTMAP_INVALID))
{
S_StartSound(NULL, sfx_s3kb2);
return;
@ -3803,13 +3930,17 @@ void M_CupSelectHandler(INT32 choice)
M_ClearMenus(true);
}
else if (count == 1)
{
PLAY_TimeAttackDef.transitionID = currentMenu->transitionID+1;
M_LevelSelected(0);
}
else
{
// Keep cursor position if you select the same cup again, reset if it's a different cup
if (levellist.levelsearch.cup != newcup)
if (oldcup != newcup || levellist.cursor >= count)
{
levellist.cursor = 0;
levellist.levelsearch.cup = newcup;
}
M_LevelSelectScrollDest();
@ -3868,94 +3999,10 @@ void M_LevelSelectHandler(INT32 choice)
if (M_MenuConfirmPressed(pid) /*|| M_MenuButtonPressed(pid, MBT_START)*/)
{
UINT8 i = 0;
INT16 map = M_GetFirstLevelInList(&i, &levellist.levelsearch);
INT16 add = levellist.cursor;
M_SetMenuDelay(pid);
while (add > 0)
{
map = M_GetNextLevelInList(map, &i, &levellist.levelsearch);
if (map >= nummapheaders)
{
break;
}
add--;
}
if (map >= nummapheaders)
{
// This shouldn't happen
return;
}
levellist.choosemap = map;
if (levellist.levelsearch.timeattack)
{
M_SetupNextMenu(&PLAY_TimeAttackDef, false);
S_StartSound(NULL, sfx_s3k63);
}
else
{
if (gamestate == GS_MENU)
{
UINT8 ssplayers = cv_splitplayers.value-1;
netgame = false;
multiplayer = true;
strncpy(connectedservername, cv_servername.string, MAXSERVERNAME);
// Still need to reset devmode
cht_debug = 0;
if (demo.playback)
G_StopDemo();
if (metalrecording)
G_StopMetalDemo();
/*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;
// Early fadeout to let the sound finish playing
F_WipeStartScreen();
V_DrawFill(0, 0, BASEVIDWIDTH, BASEVIDHEIGHT, 31);
F_WipeEndScreen();
F_RunWipe(wipedefs[wipe_level_toblack], false, "FADEMAP0", false, false);
SV_StartSinglePlayerServer(levellist.newgametype, 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" : cv_dummykartspeed.string);
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false);
}
else
{
// directly do the map change
D_MapChange(levellist.choosemap+1, levellist.newgametype, (cv_kartencore.value == 1), 1, 1, false, false);
}
M_ClearMenus(true);
}
PLAY_TimeAttackDef.transitionID = currentMenu->transitionID;
M_LevelSelected(levellist.cursor);
}
else if (M_MenuBackPressed(pid))
{

View file

@ -116,7 +116,8 @@ typedef enum
// Menu restrictions
SECRET_TIMEATTACK, // Permit Time attack
SECRET_BREAKTHECAPSULES, // Permit SP Capsules
SECRET_BREAKTHECAPSULES, // Permit SP Capsule attack
SECRET_SPECIALATTACK, // Permit Special attack (You're blue now!)
SECRET_SOUNDTEST, // Permit Sound Test
SECRET_ALTTITLE, // Permit alternate titlescreen