Rework special nextmap events.

- G_MapNumber now handles special NEXTMAP_ nextmapspecial_t constants that exist at the end of the available type.
- Cleanup of G_DoCompleted
- Add bounds checking to the various SOC maincfg map starts (spstage_start, etc)
- Add lump checking to titlemap behaviour
This commit is contained in:
toaster 2022-09-17 19:10:04 +01:00
parent 8ea80d64ef
commit b68710faf2
7 changed files with 101 additions and 69 deletions

View file

@ -1711,7 +1711,7 @@ void D_SRB2Main(void)
{ {
pstartmap = G_MapNumber(bootmap)+1; pstartmap = G_MapNumber(bootmap)+1;
if (pstartmap == nummapheaders) if (pstartmap > nummapheaders)
{ {
I_Error("Cannot warp to map %s (not found)\n", bootmap); I_Error("Cannot warp to map %s (not found)\n", bootmap);
} }

View file

@ -2650,7 +2650,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
ty = UC_MAPVISITED + offset; ty = UC_MAPVISITED + offset;
re = G_MapNumber(params[1]); re = G_MapNumber(params[1]);
if (re == nummapheaders) if (re >= nummapheaders)
{ {
deh_warning("Invalid level %s for condition ID %d", params[1], id); deh_warning("Invalid level %s for condition ID %d", params[1], id);
return; return;
@ -2663,7 +2663,7 @@ static void readcondition(UINT8 set, UINT32 id, char *word2)
re = atoi(params[2]); re = atoi(params[2]);
x1 = G_MapNumber(params[1]); x1 = G_MapNumber(params[1]);
if (x1 == nummapheaders) if (x1 >= nummapheaders)
{ {
deh_warning("Invalid level %s for condition ID %d", params[1], id); deh_warning("Invalid level %s for condition ID %d", params[1], id);
return; return;
@ -2867,21 +2867,33 @@ void readmaincfg(MYFILE *f)
// TODO: Use map name string // TODO: Use map name string
// Haven't done it because of how special stage ends are handled // Haven't done it because of how special stage ends are handled
// Though, we likely won't be using these for Kart anyhow // Though, we likely won't be using these for Kart anyhow
spstage_start = spmarathon_start = (INT16)G_MapNumber(word2); INT16 maptmp = G_MapNumber(word2)+1;
if (maptmp <= nummapheaders)
spstage_start = spmarathon_start = maptmp;
} }
else if (fastcmp(word, "SPMARATHON_START")) else if (fastcmp(word, "SPMARATHON_START"))
{ {
spmarathon_start = (INT16)G_MapNumber(word2); INT16 maptmp = G_MapNumber(word2)+1;
if (maptmp <= nummapheaders)
spmarathon_start = maptmp;
} }
else if (fastcmp(word, "SSTAGE_START")) else if (fastcmp(word, "SSTAGE_START"))
{ {
sstage_start = (INT16)G_MapNumber(word2); INT16 maptmp = G_MapNumber(word2)+1;
sstage_end = (INT16)(sstage_start+7); // 7 special stages total plus one weirdo if (maptmp <= nummapheaders)
{
sstage_start = maptmp;
sstage_end = (sstage_start+13); // 14 special stages
}
} }
else if (fastcmp(word, "SMPSTAGE_START")) else if (fastcmp(word, "SMPSTAGE_START"))
{ {
smpstage_start = (INT16)G_MapNumber(word2); INT16 maptmp = G_MapNumber(word2)+1;
smpstage_end = (INT16)(smpstage_start+6); // 7 special stages total if (maptmp <= nummapheaders)
{
smpstage_start = maptmp;
smpstage_end = (smpstage_start+13); // 14 special stages
}
} }
else if (fastcmp(word, "REDTEAM")) else if (fastcmp(word, "REDTEAM"))
{ {

View file

@ -1837,6 +1837,7 @@ static void F_CacheTitleScreen(void)
void F_StartTitleScreen(void) void F_StartTitleScreen(void)
{ {
INT32 titleMapNum;
setup_numplayers = 0; setup_numplayers = 0;
if (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS) if (gamestate != GS_TITLESCREEN && gamestate != GS_WAITINGPLAYERS)
@ -1848,18 +1849,20 @@ void F_StartTitleScreen(void)
else else
wipegamestate = GS_TITLESCREEN; wipegamestate = GS_TITLESCREEN;
if (titlemap) if (titlemap
&& ((titleMapNum = G_MapNumber(titlemap)) < nummapheaders)
&& mapheaderinfo[titleMapNum]
&& mapheaderinfo[titleMapNum]->lumpnum != LUMPERROR)
{ {
mapthing_t *startpos; mapthing_t *startpos;
const INT32 titleMapNum = G_MapNumber(titlemap)+1;
gamestate_t prevwipegamestate = wipegamestate; gamestate_t prevwipegamestate = wipegamestate;
titlemapinaction = TITLEMAP_LOADING; titlemapinaction = TITLEMAP_LOADING;
titlemapcameraref = NULL; titlemapcameraref = NULL;
gamemap = titleMapNum; gamemap = titleMapNum+1;
maptol = mapheaderinfo[gamemap-1]->typeoflevel; maptol = mapheaderinfo[titleMapNum]->typeoflevel;
globalweather = mapheaderinfo[gamemap-1]->weather; globalweather = mapheaderinfo[titleMapNum]->weather;
G_DoLoadLevel(true); G_DoLoadLevel(true);
if (!titleMapNum) if (!titleMapNum)

View file

@ -659,21 +659,35 @@ const char *G_BuildMapName(INT32 map)
* *
* \param name Map name; * \param name Map name;
* \return Map number. * \return Map number.
* \sa G_BuildMapName * \sa G_BuildMapName, nextmapspecial_t
*/ */
INT32 G_MapNumber(const char * name) INT32 G_MapNumber(const char * name)
{ {
INT32 map; if (strncasecmp("NEXTMAP_", name, 8) != 0)
for (map = 0; map < nummapheaders; ++map)
{ {
if (strcasecmp(mapheaderinfo[map]->lumpname, name) == 0) INT32 map;
for (map = 0; map < nummapheaders; ++map)
{ {
break; if (strcasecmp(mapheaderinfo[map]->lumpname, name) == 0)
{
break;
}
} }
return map;
} }
return map; name += 8;
if (strcasecmp("EVALUATION", name) == 0)
return NEXTMAP_EVALUATION;
if (strcasecmp("CREDITS", name) == 0)
return NEXTMAP_CREDITS;
if (strcasecmp("CEREMONY", name) == 0)
return NEXTMAP_CEREMONY;
//if (strcasecmp("TITLE", name) == 0)
return NEXTMAP_TITLE;
} }
/** Clips the console player's mouse aiming to the current view. /** Clips the console player's mouse aiming to the current view.
@ -3709,17 +3723,21 @@ static void G_DoCompleted(void)
{ {
if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map
{ {
nextmap = 1101; // ceremonymap nextmap = NEXTMAP_CEREMONY; // ceremonymap
} }
else else
{ {
// Proceed to next map // Proceed to next map
const INT32 cupLevelNum = G_MapNumber(grandprixinfo.cup->levellist[grandprixinfo.roundnum]); const INT32 cupLevelNum = G_MapNumber(grandprixinfo.cup->levellist[grandprixinfo.roundnum]);
if (mapheaderinfo[cupLevelNum]) if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
{ {
nextmap = cupLevelNum; nextmap = cupLevelNum;
} }
else
{
nextmap = prevmap; // Prevent uninitialised use
}
grandprixinfo.roundnum++; grandprixinfo.roundnum++;
} }
@ -3733,7 +3751,7 @@ static void G_DoCompleted(void)
{ {
nextmap = (INT16)nextNum; nextmap = (INT16)nextNum;
if (marathonmode && nextmap == spmarathon_start-1) if (marathonmode && nextmap == spmarathon_start-1)
nextmap = 1100-1; // No infinite loop for you nextmap = NEXTMAP_TITLE; // No infinite loop for you
} }
} }
@ -3745,48 +3763,46 @@ static void G_DoCompleted(void)
// a map of the proper gametype -- skip levels that don't support // a map of the proper gametype -- skip levels that don't support
// the current gametype. (Helps avoid playing boss levels in Race, // the current gametype. (Helps avoid playing boss levels in Race,
// for instance). // for instance).
if (!modeattacking && grandprixinfo.gp == false && bossinfo.boss == false) if (K_CanChangeRules())
{ {
if (nextmap >= 0 && nextmap < NUMMAPS) if (cv_advancemap.value == 0) // Stay on same map.
{
nextmap = prevmap;
}
else if (cv_advancemap.value == 2) // Go to random map.
{
nextmap = G_RandMap(G_TOLFlag(gametype), prevmap, 0, 0, false, NULL);
}
else if (nextmap < NEXTMAP_SPECIAL)
{ {
register INT16 cm = nextmap; register INT16 cm = nextmap;
UINT32 tolflag = G_TOLFlag(gametype); UINT32 tolflag = G_TOLFlag(gametype);
UINT8 visitedmap[(nummapheaders+7)/8]; UINT8* visitedmap;
memset(visitedmap, 0, sizeof (visitedmap)); visitedmap = Z_Calloc(((nummapheaders+7)/8)*sizeof(UINT8), PU_STATIC, NULL);
while (!mapheaderinfo[cm] || !(mapheaderinfo[cm]->typeoflevel & tolflag)) while (!mapheaderinfo[cm] || mapheaderinfo[cm]->lumpnum == LUMPERROR || !(mapheaderinfo[cm]->typeoflevel & tolflag))
{ {
visitedmap[cm/8] |= (1<<(cm&7)); visitedmap[cm/8] |= (1<<(cm&7));
if (!mapheaderinfo[cm]) if (!mapheaderinfo[cm])
cm = -1; // guarantee error execution cm = -1; // guarantee error execution
else if (marathonmode && mapheaderinfo[cm]->marathonnext) else if (marathonmode && mapheaderinfo[cm]->marathonnext)
{ {
const INT32 mNextNum = G_MapNumber(mapheaderinfo[gamemap-1]->marathonnext); cm = G_MapNumber(mapheaderinfo[cm]->marathonnext);
if (!mapheaderinfo[mNextNum])
cm = -1; // guarantee error execution
else
cm = (INT16)mNextNum;
} }
else else
{ {
const INT32 nextNum = G_MapNumber(mapheaderinfo[gamemap-1]->nextlevel); cm = G_MapNumber(mapheaderinfo[cm]->nextlevel);
if (!mapheaderinfo[nextNum])
cm = -1; // guarantee error execution
else
cm = (INT16)nextNum;
} }
if (cm >= nummapheaders || cm < 0) // out of range (either 1100ish or error) if (cm >= nummapheaders) // out of range (either NEXTMAP_SPECIAL or error)
{ {
cm = nextmap; //Start the loop again so that the error checking below is executed. cm = nextmap; //Start the loop again so that the error checking below is executed.
//Make sure the map actually exists before you try to go to it! //Make sure the map actually exists before you try to go to it!
if (cm < 0 || cm >= nummapheaders || mapheaderinfo[cm]->lumpnum == LUMPERROR) if (cm >= nummapheaders || mapheaderinfo[cm]->lumpnum == LUMPERROR)
{ {
CONS_Alert(CONS_ERROR, M_GetText("Next map given (id %d) doesn't exist! Reverting to id 0.\n"), cm+1); CONS_Alert(CONS_ERROR, M_GetText("Next map given (ID %d) doesn't exist! Reverting to id 0.\n"), cm+1);
cm = 0; cm = 0;
break; break;
} }
@ -3797,28 +3813,17 @@ static void G_DoCompleted(void)
// We got stuck in a loop, came back to the map we started on // We got stuck in a loop, came back to the map we started on
// without finding one supporting the current gametype. // without finding one supporting the current gametype.
// Thus, print a warning, and just use this map anyways. // Thus, print a warning, and just use this map anyways.
CONS_Alert(CONS_WARNING, M_GetText("Can't find a compatible map after map %d; using map %d anyway\n"), prevmap+1, cm+1); CONS_Alert(CONS_WARNING, M_GetText("Can't find a compatible map after ID %d; using ID %d anyway\n"), prevmap, cm);
break; break;
} }
} }
Z_Free(visitedmap);
nextmap = cm; nextmap = cm;
} }
// wrap around
// wrap around in race else if (!(gametyperules & GTR_CAMPAIGN))
if (nextmap >= 1100-1 && nextmap <= 1102-1 && !(gametyperules & GTR_CAMPAIGN))
nextmap = (INT16)(spstage_start-1); nextmap = (INT16)(spstage_start-1);
if (nextmap < 0 || (nextmap >= nummapheaders && nextmap < 1100-1) || nextmap > 1103-1)
I_Error("Followed map %d to invalid map %d\n", prevmap + 1, nextmap + 1);
if (!spec)
lastmap = nextmap; // Remember last map for when you come out of the special stage.
}
automapactive = false;
if (!(gametyperules & GTR_CAMPAIGN))
{
if (cv_advancemap.value == 0) // Stay on same map. if (cv_advancemap.value == 0) // Stay on same map.
{ {
nextmap = prevmap; nextmap = prevmap;
@ -3832,7 +3837,7 @@ static void G_DoCompleted(void)
// We are committed to this map now. // We are committed to this map now.
// We may as well allocate its header if it doesn't exist // We may as well allocate its header if it doesn't exist
// (That is, if it's a real map) // (That is, if it's a real map)
if (nextmap < NUMMAPS && !mapheaderinfo[nextmap]) if (nextmap < NEXTMAP_SPECIAL && (nextmap >= nummapheaders || !mapheaderinfo[nextmap] || mapheaderinfo[nextmap]->lumpnum == LUMPERROR))
I_Error("G_DoCompleted: Internal map ID %d not found (nummapheaders = %d)\n", nextmap, nummapheaders); I_Error("G_DoCompleted: Internal map ID %d not found (nummapheaders = %d)\n", nextmap, nummapheaders);
// Set up power level gametype scrambles // Set up power level gametype scrambles
@ -3899,7 +3904,7 @@ void G_AfterIntermission(void)
F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false); F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false);
else else
{ {
if (nextmap < 1100-1) if (nextmap < NEXTMAP_SPECIAL)
G_NextLevel(); G_NextLevel();
else else
G_EndGame(); G_EndGame();
@ -4043,17 +4048,17 @@ void G_EndGame(void)
// Only do evaluation and credits in coop games. // Only do evaluation and credits in coop games.
if (gametyperules & GTR_CAMPAIGN) if (gametyperules & GTR_CAMPAIGN)
{ {
if (nextmap == 1103-1) // end game with ending if (nextmap == NEXTMAP_CEREMONY) // end game with ending
{ {
F_StartEnding(); F_StartEnding();
return; return;
} }
if (nextmap == 1102-1) // end game with credits if (nextmap == NEXTMAP_CREDITS) // end game with credits
{ {
F_StartCredits(); F_StartCredits();
return; return;
} }
if (nextmap == 1101-1) // end game with evaluation if (nextmap == NEXTMAP_EVALUATION) // end game with evaluation
{ {
F_StartGameEvaluation(); F_StartGameEvaluation();
return; return;

View file

@ -36,6 +36,18 @@ extern tic_t levelstarttic;
// for modding? // for modding?
extern INT16 prevmap, nextmap; extern INT16 prevmap, nextmap;
// see also G_MapNumber
typedef enum
{
NEXTMAP_RESERVED = INT16_MAX, // so nextmap+1 doesn't roll over -- remove when gamemap is made 0-indexed
NEXTMAP_TITLE = INT16_MAX-1,
NEXTMAP_EVALUATION = INT16_MAX-2,
NEXTMAP_CREDITS = INT16_MAX-3,
NEXTMAP_CEREMONY = INT16_MAX-4,
NEXTMAP_SPECIAL = NEXTMAP_CEREMONY
} nextmapspecial_t;
extern INT32 gameovertics; extern INT32 gameovertics;
extern UINT8 ammoremovaltics; extern UINT8 ammoremovaltics;
extern tic_t timeinmap; // Ticker for time spent in level (used for levelcard display) extern tic_t timeinmap; // Ticker for time spent in level (used for levelcard display)

View file

@ -1849,7 +1849,7 @@ static void M_DrawCupPreview(INT16 y, cupheader_t *cup)
INT32 cupLevelNum = G_MapNumber(cup->levellist[i]); INT32 cupLevelNum = G_MapNumber(cup->levellist[i]);
patch_t *PictureOfLevel = NULL; patch_t *PictureOfLevel = NULL;
if (mapheaderinfo[cupLevelNum]) if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
{ {
PictureOfLevel = mapheaderinfo[cupLevelNum]->thumbnailPic; PictureOfLevel = mapheaderinfo[cupLevelNum]->thumbnailPic;
} }

View file

@ -305,7 +305,7 @@ UINT8 M_CheckLevelEmblems(void)
checkLevel = G_MapNumber(emblemlocations[i].level); checkLevel = G_MapNumber(emblemlocations[i].level);
if (!mapheaderinfo[checkLevel]) if (checkLevel >= nummapheaders || !mapheaderinfo[checkLevel])
continue; continue;
levelnum = checkLevel; levelnum = checkLevel;
@ -345,7 +345,7 @@ UINT8 M_CompletionEmblems(void) // Bah! Duplication sucks, but it's for a separa
checkLevel = G_MapNumber(emblemlocations[i].level); checkLevel = G_MapNumber(emblemlocations[i].level);
if (!mapheaderinfo[checkLevel]) if (checkLevel >= nummapheaders || !mapheaderinfo[checkLevel])
continue; continue;
levelnum = checkLevel; levelnum = checkLevel;
@ -472,7 +472,7 @@ UINT8 M_GotLowEnoughTime(INT32 tictime)
INT32 curtics = 0; INT32 curtics = 0;
INT32 i; INT32 i;
for (i = 0; i < NUMMAPS; ++i) for (i = 0; i < nummapheaders; ++i)
{ {
if (!mapheaderinfo[i] || (mapheaderinfo[i]->menuflags & LF2_NOTIMEATTACK)) if (!mapheaderinfo[i] || (mapheaderinfo[i]->menuflags & LF2_NOTIMEATTACK))
continue; continue;
@ -508,7 +508,7 @@ emblem_t *M_GetLevelEmblems(INT32 mapnum)
{ {
INT32 checkLevel = G_MapNumber(emblemlocations[i].level); INT32 checkLevel = G_MapNumber(emblemlocations[i].level);
if (!mapheaderinfo[checkLevel]) if (checkLevel >= nummapheaders || !mapheaderinfo[checkLevel])
continue; continue;
if (checkLevel == map) if (checkLevel == map)