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

View file

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

View file

@ -659,21 +659,35 @@ const char *G_BuildMapName(INT32 map)
*
* \param name Map name;
* \return Map number.
* \sa G_BuildMapName
* \sa G_BuildMapName, nextmapspecial_t
*/
INT32 G_MapNumber(const char * name)
{
INT32 map;
for (map = 0; map < nummapheaders; ++map)
if (strncasecmp("NEXTMAP_", name, 8) != 0)
{
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.
@ -3709,17 +3723,21 @@ static void G_DoCompleted(void)
{
if (grandprixinfo.roundnum >= grandprixinfo.cup->numlevels) // On final map
{
nextmap = 1101; // ceremonymap
nextmap = NEXTMAP_CEREMONY; // ceremonymap
}
else
{
// Proceed to next map
const INT32 cupLevelNum = G_MapNumber(grandprixinfo.cup->levellist[grandprixinfo.roundnum]);
if (mapheaderinfo[cupLevelNum])
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
{
nextmap = cupLevelNum;
}
else
{
nextmap = prevmap; // Prevent uninitialised use
}
grandprixinfo.roundnum++;
}
@ -3733,7 +3751,7 @@ static void G_DoCompleted(void)
{
nextmap = (INT16)nextNum;
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
// the current gametype. (Helps avoid playing boss levels in Race,
// 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;
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));
if (!mapheaderinfo[cm])
cm = -1; // guarantee error execution
else if (marathonmode && mapheaderinfo[cm]->marathonnext)
{
const INT32 mNextNum = G_MapNumber(mapheaderinfo[gamemap-1]->marathonnext);
if (!mapheaderinfo[mNextNum])
cm = -1; // guarantee error execution
else
cm = (INT16)mNextNum;
cm = G_MapNumber(mapheaderinfo[cm]->marathonnext);
}
else
{
const INT32 nextNum = G_MapNumber(mapheaderinfo[gamemap-1]->nextlevel);
if (!mapheaderinfo[nextNum])
cm = -1; // guarantee error execution
else
cm = (INT16)nextNum;
cm = G_MapNumber(mapheaderinfo[cm]->nextlevel);
}
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.
//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;
break;
}
@ -3797,28 +3813,17 @@ static void G_DoCompleted(void)
// We got stuck in a loop, came back to the map we started on
// without finding one supporting the current gametype.
// 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;
}
}
Z_Free(visitedmap);
nextmap = cm;
}
// wrap around in race
if (nextmap >= 1100-1 && nextmap <= 1102-1 && !(gametyperules & GTR_CAMPAIGN))
// wrap around
else if (!(gametyperules & GTR_CAMPAIGN))
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.
{
nextmap = prevmap;
@ -3832,7 +3837,7 @@ static void G_DoCompleted(void)
// We are committed to this map now.
// We may as well allocate its header if it doesn't exist
// (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);
// Set up power level gametype scrambles
@ -3899,7 +3904,7 @@ void G_AfterIntermission(void)
F_StartCustomCutscene(mapheaderinfo[gamemap-1]->cutscenenum-1, false, false);
else
{
if (nextmap < 1100-1)
if (nextmap < NEXTMAP_SPECIAL)
G_NextLevel();
else
G_EndGame();
@ -4043,17 +4048,17 @@ void G_EndGame(void)
// Only do evaluation and credits in coop games.
if (gametyperules & GTR_CAMPAIGN)
{
if (nextmap == 1103-1) // end game with ending
if (nextmap == NEXTMAP_CEREMONY) // end game with ending
{
F_StartEnding();
return;
}
if (nextmap == 1102-1) // end game with credits
if (nextmap == NEXTMAP_CREDITS) // end game with credits
{
F_StartCredits();
return;
}
if (nextmap == 1101-1) // end game with evaluation
if (nextmap == NEXTMAP_EVALUATION) // end game with evaluation
{
F_StartGameEvaluation();
return;

View file

@ -36,6 +36,18 @@ extern tic_t levelstarttic;
// for modding?
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 UINT8 ammoremovaltics;
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]);
patch_t *PictureOfLevel = NULL;
if (mapheaderinfo[cupLevelNum])
if (cupLevelNum < nummapheaders && mapheaderinfo[cupLevelNum])
{
PictureOfLevel = mapheaderinfo[cupLevelNum]->thumbnailPic;
}

View file

@ -305,7 +305,7 @@ UINT8 M_CheckLevelEmblems(void)
checkLevel = G_MapNumber(emblemlocations[i].level);
if (!mapheaderinfo[checkLevel])
if (checkLevel >= nummapheaders || !mapheaderinfo[checkLevel])
continue;
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);
if (!mapheaderinfo[checkLevel])
if (checkLevel >= nummapheaders || !mapheaderinfo[checkLevel])
continue;
levelnum = checkLevel;
@ -472,7 +472,7 @@ UINT8 M_GotLowEnoughTime(INT32 tictime)
INT32 curtics = 0;
INT32 i;
for (i = 0; i < NUMMAPS; ++i)
for (i = 0; i < nummapheaders; ++i)
{
if (!mapheaderinfo[i] || (mapheaderinfo[i]->menuflags & LF2_NOTIMEATTACK))
continue;
@ -508,7 +508,7 @@ emblem_t *M_GetLevelEmblems(INT32 mapnum)
{
INT32 checkLevel = G_MapNumber(emblemlocations[i].level);
if (!mapheaderinfo[checkLevel])
if (checkLevel >= nummapheaders || !mapheaderinfo[checkLevel])
continue;
if (checkLevel == map)