diff --git a/src/deh_soc.c b/src/deh_soc.c index 0e904dda40dd010fa6677f5b56aa6ab290213e87..5960ac33693a8a307cb5e829d6e689c31d16d43a 100644 --- a/src/deh_soc.c +++ b/src/deh_soc.c @@ -1340,10 +1340,10 @@ void readgametype(MYFILE *f, char *gtname) static INT32 ParseNextLevelName(const char *name) { - if (fastcmp(name, "TITLE")) return MAP_TITLE; - else if (fastcmp(name, "EVALUATION")) return MAP_EVALUATION; - else if (fastcmp(name, "CREDITS")) return MAP_CREDITS; - else if (fastcmp(name, "ENDING")) return MAP_ENDING; + if (fastcmp(name, "TITLE")) return NEXTMAP_TITLE; + else if (fastcmp(name, "EVALUATION")) return NEXTMAP_EVALUATION; + else if (fastcmp(name, "CREDITS")) return NEXTMAP_CREDITS; + else if (fastcmp(name, "ENDING")) return NEXTMAP_ENDING; else { // Support using the actual map name, @@ -1356,16 +1356,19 @@ static INT32 ParseNextLevelName(const char *name) static INT32 ConvertLevelHeaderMapNum(INT32 mapnum) { - if (mapnum == 1100) - return MAP_TITLE; - else if (mapnum == 1101) - return MAP_EVALUATION; - else if (mapnum == 1102) - return MAP_CREDITS; - else if (mapnum == 1103) - return MAP_ENDING; - else + switch (mapnum) + { + case 1100: + return NEXTMAP_TITLE; + case 1101: + return NEXTMAP_EVALUATION; + case 1102: + return NEXTMAP_CREDITS; + case 1103: + return NEXTMAP_ENDING; + default: return mapnum; + } } void readlevelheader(MYFILE *f, INT32 num) diff --git a/src/doomdata.h b/src/doomdata.h index 33051da656b9237a4ae11c3fb43398422dd4cec6..0b6210d2fcb768c2c8bf210f0fd0dc9e70e450b4 100644 --- a/src/doomdata.h +++ b/src/doomdata.h @@ -223,15 +223,4 @@ typedef struct #define ZSHIFT 4 -#define MAXMAPS 16386 - -#define MAX_MAP_NAME_SIZE 256 // This is an arbitrary limit to prevent exceedingly long map names. - -#define MAP_TITLE (MAXMAPS) -#define MAP_EVALUATION (MAXMAPS+2) -#define MAP_CREDITS (MAXMAPS+3) -#define MAP_ENDING (MAXMAPS+4) - -#define NUMBASEMAPS 1035 // MAP01 to MAPZZ - #endif // __DOOMDATA__ diff --git a/src/doomdef.h b/src/doomdef.h index 2626e8f54d6e4e7e27b44227ee83fda3a24ddeb6..415854aa6c42772294f2933c16204d4bb90fca2d 100644 --- a/src/doomdef.h +++ b/src/doomdef.h @@ -237,6 +237,19 @@ extern char logfilename[1024]; #define PLAYERSMASK (MAXPLAYERS-1) #define MAXPLAYERNAME 21 +#define MAXMAPS 16386 + +#define MAX_MAP_NAME_SIZE 256 // This is an arbitrary limit to prevent exceedingly long map names. + +#define NEXTMAP_TITLE (MAXMAPS) +#define NEXTMAP_EVALUATION (MAXMAPS+2) +#define NEXTMAP_CREDITS (MAXMAPS+3) +#define NEXTMAP_ENDING (MAXMAPS+4) + +#define NUM_NEXTMAPS 4 + +#define NUMBASEMAPS 1035 // MAP01 to MAPZZ + #define COLORRAMPSIZE 16 #define MAXCOLORNAME 32 #define NUMCOLORFREESLOTS 1024 diff --git a/src/doomstat.h b/src/doomstat.h index 3141e7cc4446098ed3b5aed16bd4237969e7c90c..3ad304452ca1e4df0705d21efa6e10ae8c162851 100644 --- a/src/doomstat.h +++ b/src/doomstat.h @@ -281,7 +281,13 @@ extern struct quake typedef struct { UINT32 hash; - char *name; + size_t length; + char *chars; +} mapname_t; + +typedef struct +{ + mapname_t name; char *thumbnail; char *thumbnail_wide; char *music; @@ -311,7 +317,7 @@ typedef struct char subttl[33]; ///< Subtitle for level UINT8 actnum; ///< Act number or 0 for none. UINT32 typeoflevel; ///< Combination of typeoflevel flags. - INT16 nextlevel; ///< Map number of next level, or 1100-1102 to end. + INT16 nextlevel; ///< Map number of next level, or NEXTMAP_* to end. INT16 marathonnext; ///< See nextlevel, but for Marathon mode. Necessary to support hub worlds ala SUGOI. char keywords[33]; ///< Keywords separated by space to search for. 32 characters. char musname[MAX_MUSIC_NAME+1]; ///< Music track to play. "" for no music. diff --git a/src/g_game.c b/src/g_game.c index 28e7646a8b7fd1b5b0ad437848364e41be39ae28..d11cbedbaff09ebec4888434214aed0a15a044a0 100644 --- a/src/g_game.c +++ b/src/g_game.c @@ -178,6 +178,9 @@ mapheader_t* mapheaderinfo[MAXMAPS] = {NULL}; gamemap_t gamemaps[MAXMAPS]; UINT16 numgamemaps = 0; +static mapname_t nextmapnames[NUM_NEXTMAPS]; +static INT16 nextmapids[NUM_NEXTMAPS] = { NEXTMAP_TITLE, NEXTMAP_EVALUATION, NEXTMAP_CREDITS, NEXTMAP_ENDING }; + static boolean exitgame = false; static boolean retrying = false; static boolean retryingmodeattack = false; @@ -818,7 +821,7 @@ void G_SetUsedCheats(boolean silent) const char *G_BuildMapName(INT32 map) { if (map > 0 && map <= numgamemaps) - return gamemaps[map - 1].name; + return gamemaps[map - 1].name.chars; return "UNKNOWN"; } @@ -852,6 +855,19 @@ const char *G_BuildClassicMapName(INT32 map) return mapname; } +static UINT32 G_HashMapNameString(const char *name, size_t name_length) +{ + return quickncasehash(name, name_length); +} + +static void G_MakeMapName(mapname_t *name, const char *string) +{ + name->length = strlen(string); + name->hash = G_HashMapNameString(string, name->length); + name->chars = Z_StrDup(string); + strupr(name->chars); +} + void G_InitMaps(void) { for (UINT16 i = 0; i < NUMBASEMAPS; i++) @@ -859,29 +875,53 @@ void G_InitMaps(void) const char *name = G_BuildClassicMapName(i + 1); G_AddMap(name); } + + G_MakeMapName(&nextmapnames[0], "SCENE_TITLE"); + G_MakeMapName(&nextmapnames[1], "SCENE_EVALUATION"); + G_MakeMapName(&nextmapnames[2], "SCENE_CREDITS"); + G_MakeMapName(&nextmapnames[3], "SCENE_ENDING"); } -UINT16 G_GetMapNumber(const char *name) +static UINT16 MapIDForHashedString(const char *name, size_t name_length, UINT32 name_hash) { - size_t name_len = strlen(name); - // Special case - if (name_len == 2 && name[0] >= 'A' && name[0] <= 'Z') - { + if (name_length == 2 && name[0] >= 'A' && name[0] <= 'Z') return M_MapNumber(name[0], name[1]); - } - - UINT32 hash = quickncasehash(name, name_len); for (UINT16 i = 0; i < numgamemaps; i++) { - if (hash == gamemaps[i].hash && stricmp(gamemaps[i].name, name) == 0) + if (gamemaps[i].name.length == name_length + && gamemaps[i].name.hash == name_hash + && stricmp(name, gamemaps[i].name.chars) == 0) return i + 1; } return 0; } +UINT16 G_GetMapNumber(const char *name) +{ + size_t name_length = strlen(name); + + return MapIDForHashedString(name, name_length, G_HashMapNameString(name, name_length)); +} + +UINT16 G_GetNextMapNumber(const char *name) +{ + size_t name_length = strlen(name); + UINT32 name_hash = G_HashMapNameString(name, name_length); + + for (UINT16 i = 0; i < NUM_NEXTMAPS; i++) + { + if (nextmapnames[i].length == name_length + && nextmapnames[i].hash == name_hash + && stricmp(name, nextmapnames[i].chars) == 0) + return nextmapids[i]; + } + + return MapIDForHashedString(name, name_length, name_hash); +} + UINT16 G_AddMap(const char *name) { if (numgamemaps == MAXMAPS) @@ -898,10 +938,7 @@ UINT16 G_AddMap(const char *name) if (name_len > MAX_MAP_NAME_SIZE) return 0; - gamemaps[numgamemaps].hash = quickncasehash(name, name_len); - gamemaps[numgamemaps].name = Z_StrDup(name); - - strupr(gamemaps[numgamemaps].name); + G_MakeMapName(&gamemaps[numgamemaps].name, name); numgamemaps++; @@ -925,7 +962,7 @@ boolean G_IsValidMapName(const char *name) static char *BuildCombinedMapString(INT16 map, const char *newfmt, const char *oldfmt) { - const char *mapname = gamemaps[map].name; + const char *mapname = gamemaps[map].name.chars; const char *fmt = (map < NUMBASEMAPS) ? oldfmt : newfmt; size_t len = strlen(mapname) + strlen(fmt) + 1; @@ -994,10 +1031,10 @@ boolean G_IsGameEndMap(INT16 mapnum) { switch (mapnum) { - case MAP_TITLE: - case MAP_EVALUATION: - case MAP_CREDITS: - case MAP_ENDING: + case NEXTMAP_TITLE: + case NEXTMAP_EVALUATION: + case NEXTMAP_CREDITS: + case NEXTMAP_ENDING: return true; default: return false; @@ -4144,7 +4181,7 @@ static void G_HandleSaveLevel(void) G_UpdateAllVisited(); // do this before running the intermission or custom cutscene, mostly for the sake of marathon mode but it also massively reduces redundant file save events in f_finale.c - if (nextmap >= 1100-1) + if (nextmap >= NEXTMAP_TITLE-1) { if (!gamecomplete) gamecomplete = 2; // special temporary mode to prevent using SP level select in pause menu until the intermission is over without restricting it in every intermission @@ -4215,7 +4252,7 @@ static void G_DoCompleted(void) { nextmap = (INT16)(mapheaderinfo[gamemap-1]->nextlevel-1); if (marathonmode && nextmap == spmarathon_start-1) - nextmap = 1100-1; // No infinite loop for you + nextmap = NEXTMAP_TITLE-1; // No infinite loop for you } INT16 gametype_to_use; @@ -4247,7 +4284,7 @@ static void G_DoCompleted(void) else cm = (INT16)(mapheaderinfo[cm]->nextlevel-1); - if (cm >= numgamemaps || cm < 0) // out of range (either 1100ish or error) + if (cm >= numgamemaps || cm < 0) // out of range (either NEXTMAP_* or error) { cm = nextmap; //Start the loop again so that the error checking below is executed. @@ -4487,24 +4524,21 @@ void G_EndGame(void) // Only do evaluation and credits in coop games. if (gametyperules & GTR_CUTSCENES) { - if (nextmap+1 == MAP_ENDING) // end game with ending + switch (nextmap+1) { + case NEXTMAP_ENDING: F_StartEnding(); return; - } - else if (nextmap+1 == MAP_CREDITS) // end game with credits - { + case NEXTMAP_CREDITS: F_StartCredits(); return; - } - else if (nextmap+1 == MAP_EVALUATION) // end game with evaluation - { + case NEXTMAP_EVALUATION: F_StartGameEvaluation(); return; } } - // 1100 or competitive multiplayer, so go back to title screen. + // NEXTMAP_TITLE or competitive multiplayer, so go back to title screen. D_StartTitle(); } diff --git a/src/g_game.h b/src/g_game.h index 6e59b2cd2473b416512d5775c7b5f4bb9e01ee33..930bc0b990d079668476f415263c27c98b5f12e9 100644 --- a/src/g_game.h +++ b/src/g_game.h @@ -119,6 +119,7 @@ const char *G_BuildMapName(INT32 map); void G_InitMaps(void); UINT16 G_GetMapNumber(const char *name); +UINT16 G_GetNextMapNumber(const char *name); UINT16 G_AddMap(const char *name); boolean G_MapFileExists(const char *name); boolean G_IsValidMapName(const char *name); diff --git a/src/lua_baselib.c b/src/lua_baselib.c index 87afc772c04a6c7fabce14de17ae4ad87a1d90df..c4d4dea0609cee168f32054b8bade54e048e493b 100644 --- a/src/lua_baselib.c +++ b/src/lua_baselib.c @@ -3846,6 +3846,25 @@ static int GetMapNameOrNumber(lua_State *L, int idx) return luaL_checkinteger(L, idx); } +static int GetNextMapNameOrNumber(lua_State *L, int idx) +{ + if (lua_type(L, idx) == LUA_TSTRING) + { + const char *mapname = luaL_checkstring(L, idx); + INT16 mapnum = G_GetNextMapNumber(mapname); + if (mapnum == 0) + { + return luaL_error(L, + "%s is not a valid game map.", + mapname + ); + } + return mapnum; + } + else + return luaL_checkinteger(L, idx); +} + static int Lcheckmapnumber (lua_State *L, int idx, const char *fun) { if (ISINLEVEL) @@ -4078,8 +4097,8 @@ static int lib_gSetCustomExitVars(lua_State *L) { if (!lua_isnoneornil(L, 1)) { - INT16 mapnum = GetMapNameOrNumber(L, 1); - if (mapnum < 1 || mapnum > numgamemaps) + INT16 mapnum = GetNextMapNameOrNumber(L, 1); + if (mapnum < 1 || (mapnum > numgamemaps && !G_IsGameEndMap(mapnum))) { return luaL_error(L, "map number %d out of range (1 - %d)", diff --git a/src/m_menu.c b/src/m_menu.c index 7dc5056ac221ecad45199ff23e87f87241d68e83..170ff482746e2fdd896615097dc09b3efe3b4422 100644 --- a/src/m_menu.c +++ b/src/m_menu.c @@ -8235,7 +8235,7 @@ static void M_SinglePlayerMenu(INT32 choice) if (mapheaderinfo[spmarathon_start-1] && !mapheaderinfo[spmarathon_start-1]->marathonnext && (mapheaderinfo[spmarathon_start-1]->nextlevel == spmarathon_start - || mapheaderinfo[spmarathon_start-1]->nextlevel >= 1100)) + || G_IsGameEndMap(mapheaderinfo[spmarathon_start-1]->nextlevel))) { SP_MainMenu[spmarathon].status = IT_NOTHING|IT_DISABLED; // Hide and disable the Marathon Run option... // ...and lower the above options' display positions by 8 pixels to close the gap diff --git a/src/p_setup.c b/src/p_setup.c index 40be9babab87d4c715874202df838634eefffd25..0ab8974073c1338972915e031f3eecab13894c52 100644 --- a/src/p_setup.c +++ b/src/p_setup.c @@ -8119,7 +8119,7 @@ static lumpinfo_t* FindFolder(const char *folName, UINT16 *start, UINT16 *end, l return lumpinfo; } -void P_LoadMapsFromFile(UINT16 wadnum, boolean is_pwad) +void P_LoadMapsFromFile(UINT16 wadnum, boolean added_ingame) { boolean mapsadded = false; @@ -8158,10 +8158,10 @@ void P_LoadMapsFromFile(UINT16 wadnum, boolean is_pwad) break; } //If you replaced the map you're on, end the level when done. - else if (num == gamemap) + else if (gamestate == GS_LEVEL && num == gamemap) replacedcurrentmap = true; - if (is_pwad) + if (added_ingame) CONS_Printf("%s\n", name); mapsadded = true; } @@ -8179,17 +8179,17 @@ void P_LoadMapsFromFile(UINT16 wadnum, boolean is_pwad) num = (INT16)M_MapNumber(name[3], name[4]); //If you replaced the map you're on, end the level when done. - if (num == gamemap) + if (gamestate == GS_LEVEL && num == gamemap) replacedcurrentmap = true; - if (is_pwad) + if (added_ingame) CONS_Printf("%s\n", name); mapsadded = true; } } } - if (!mapsadded && is_pwad) + if (!mapsadded && added_ingame) CONS_Printf(M_GetText("No maps added\n")); } @@ -8365,7 +8365,7 @@ static boolean P_LoadAddon(UINT16 numlumps) if (gamestate == GS_LEVEL && (netgame || multiplayer)) { - CONS_Printf(M_GetText("Current map %d replaced by added file, ending the level to ensure consistency.\n"), gamemap); + CONS_Printf(M_GetText("Current map %s replaced by added file, ending the level to ensure consistency.\n"), G_BuildMapName(gamemap)); if (server) D_SendExitLevel(false); } diff --git a/src/p_setup.h b/src/p_setup.h index 23c6d4b1c8883675e6100e4664ad3197097d40d4..a3f850ff6b66de423d936ab2af1cc62e299fd46e 100644 --- a/src/p_setup.h +++ b/src/p_setup.h @@ -107,7 +107,7 @@ boolean P_AddFolder(const char *folderpath); boolean P_RunSOC(const char *socfilename); void P_LoadSoundsRange(UINT16 wadnum, UINT16 first, UINT16 num); void P_LoadMusicsRange(UINT16 wadnum, UINT16 first, UINT16 num); -void P_LoadMapsFromFile(UINT16 wadnum, boolean is_pwad); +void P_LoadMapsFromFile(UINT16 wadnum, boolean added_ingame); //void P_WriteThings(void); size_t P_PrecacheLevelFlats(void); void P_AllocMapHeader(INT16 i);