diff --git a/source/core/cheats.cpp b/source/core/cheats.cpp index 72812e623..6cd00d6cd 100644 --- a/source/core/cheats.cpp +++ b/source/core/cheats.cpp @@ -294,7 +294,7 @@ static MapRecord* levelwarp_common(FCommandLine& argv, const char *cmdname, cons Printf(PRINT_BOLD, "Invalid level! Numbers must be > 0\n"); return nullptr; } - auto map = FindMapByLevelNum(numparm == 1 ? m : makelevelnum(e - 1, m - 1)); + auto map = FindMapByLevelNum(numparm == 1 ? m : makelevelnum(e, m)); if (!map) { if (numparm == 2) Printf(PRINT_BOLD, "Level E%s L%s not found!\n", argv[1], argv[2]); diff --git a/source/core/mapinfo.cpp b/source/core/mapinfo.cpp index 74ed92fe2..253d1a092 100644 --- a/source/core/mapinfo.cpp +++ b/source/core/mapinfo.cpp @@ -84,10 +84,10 @@ CCMD(mapinfo) if (clus.intro.isdefined() || clus.outro.isdefined()) { Printf("Cluster %d\n\tName = '%s'\n", clus.index, clus.name.GetChars()); - if (clus.intro.function.IsNotEmpty()) Printf("\tIntro function = %d\n", clus.intro.function.GetChars()); - if (clus.intro.video.IsNotEmpty()) Printf("\tIntro video = %d\n", clus.intro.video.GetChars()); - if (clus.outro.function.IsNotEmpty()) Printf("\tOutro function = %d\n", clus.outro.function.GetChars()); - if (clus.outro.video.IsNotEmpty()) Printf("\tOutro video = %d\n", clus.outro.video.GetChars()); + if (clus.intro.function.IsNotEmpty()) Printf("\tIntro function = %s\n", clus.intro.function.GetChars()); + if (clus.intro.video.IsNotEmpty()) Printf("\tIntro video = %s\n", clus.intro.video.GetChars()); + if (clus.outro.function.IsNotEmpty()) Printf("\tOutro function = %s\n", clus.outro.function.GetChars()); + if (clus.outro.video.IsNotEmpty()) Printf("\tOutro video = %s\n", clus.outro.video.GetChars()); Printf("}\n"); } } @@ -108,10 +108,10 @@ CCMD(mapinfo) if (map->cdSongId > 0) Printf("\tCD track = %d\n", map->cdSongId); if (map->parTime) Printf("\tPar Time = %d\n", map->parTime); if (map->designerTime) Printf("\tPar Time = %d\n", map->designerTime); - if (map->intro.function.IsNotEmpty()) Printf("\tIntro function = %d\n", map->intro.function.GetChars()); - if (map->intro.video.IsNotEmpty()) Printf("\tIntro video = %d\n", map->intro.video.GetChars()); - if (map->outro.function.IsNotEmpty()) Printf("\tOutro function = %d\n", map->outro.function.GetChars()); - if (map->outro.video.IsNotEmpty()) Printf("\tOutro video = %d\n", map->outro.video.GetChars()); + if (map->intro.function.IsNotEmpty()) Printf("\tIntro function = %s\n", map->intro.function.GetChars()); + if (map->intro.video.IsNotEmpty()) Printf("\tIntro video = %s\n", map->intro.video.GetChars()); + if (map->outro.function.IsNotEmpty()) Printf("\tOutro function = %s\n", map->outro.function.GetChars()); + if (map->outro.video.IsNotEmpty()) Printf("\tOutro video = %s\n", map->outro.video.GetChars()); Printf("}\n"); } else @@ -173,6 +173,41 @@ VolumeRecord* AllocateVolume() { return &volumes[volumes.Reserve(1)]; } + +MapRecord* FindMapByIndexOnly(int cluster, int num) +{ + for (auto& map : mapList) + { + if (map->mapindex == num && map->cluster == cluster) return map.Data(); + } + return nullptr; +} + +MapRecord* FindMapByIndex(int cluster, int num) +{ + auto map = FindMapByLevelNum(num); + if (!map) map = FindMapByIndexOnly(cluster, num); // modern definitions take precedence. + return map; +} + +MapRecord* FindNextMap(MapRecord* thismap) +{ + MapRecord* next = nullptr; + if (!thismap->NextMap.Compare("-")) return nullptr; // '-' means to forcibly end the game here. + if (thismap->NextMap.IsNotEmpty()) next = FindMapByName(thismap->NextMap); + if (!next) next = FindMapByLevelNum(thismap->levelNumber); + return next; +} + +MapRecord* FindNextSecretMap(MapRecord* thismap) +{ + MapRecord* next = nullptr; + if (!thismap->NextSecret.Compare("-")) return nullptr; // '-' means to forcibly end the game here. + if (thismap->NextSecret.IsNotEmpty()) next = FindMapByName(thismap->NextSecret); + return next? next : FindNextMap(thismap); +} + + // return a map whose cluster and map number matches. // if there's only one map with the given level number return that. MapRecord* FindMapByClusterAndLevelNum(int cluster, int num) @@ -195,12 +230,6 @@ MapRecord* FindMapByClusterAndLevelNum(int cluster, int num) return nullptr; } -MapRecord *FindNextMap(MapRecord *thismap) -{ - if (thismap->nextLevel != -1) return FindMapByLevelNum(thismap->nextLevel); - return FindMapByLevelNum(thismap->levelNumber+1); -} - bool SetMusicForMap(const char* mapname, const char* music, bool namehack) { static const char* specials[] = { "intro", "briefing", "loading" }; diff --git a/source/core/mapinfo.h b/source/core/mapinfo.h index 35e451d54..76d44a95a 100644 --- a/source/core/mapinfo.h +++ b/source/core/mapinfo.h @@ -205,9 +205,11 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack = fals MapRecord *FindMapByName(const char *nm); MapRecord *FindMapByLevelNum(int num); +MapRecord* FindMapByIndexOnly(int clst, int num); // this is for map setup where fallbacks are undesirable. +MapRecord* FindMapByIndex(int clst, int num); MapRecord* FindMapByClusterAndLevelNum(int clst, int num); -inline MapRecord* FindMapByIndexOnly(int clst, int num) { return FindMapByClusterAndLevelNum(clst, num); } MapRecord *FindNextMap(MapRecord *thismap); +MapRecord* FindNextSecretMap(MapRecord* thismap); MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic = nullptr); MapRecord* AllocateMap(); diff --git a/source/games/blood/src/blood.cpp b/source/games/blood/src/blood.cpp index 9c7ddf997..4af549d52 100644 --- a/source/games/blood/src/blood.cpp +++ b/source/games/blood/src/blood.cpp @@ -80,7 +80,7 @@ void EndLevel(void) seqKillAll(); } -void StartLevel(MapRecord* level) +void StartLevel(MapRecord* level, bool newgame) { if (!level) return; gFrameCount = 0; @@ -97,14 +97,14 @@ void StartLevel(MapRecord* level) /////// } #if 0 - else if (gGameOptions.nGameType > 0 && !(gGameOptions.uGameFlags & GF_AdvanceLevel)) + else if (gGameOptions.nGameType > 0 && newgame) { // todo gBlueFlagDropped = false; gRedFlagDropped = false; } #endif - if (gGameOptions.uGameFlags & GF_AdvanceLevel) + if (!newgame) { for (int i = connecthead; i >= 0; i = connectpoint2[i]) { @@ -182,13 +182,13 @@ void StartLevel(MapRecord* level) evInit(); for (int i = connecthead; i >= 0; i = connectpoint2[i]) { - if (!(gGameOptions.uGameFlags & GF_AdvanceLevel)) + if (newgame) { playerInit(i, 0); } playerStart(i, 1); } - if (gGameOptions.uGameFlags & GF_AdvanceLevel) + if (!newgame) { for (int i = connecthead; i >= 0; i = connectpoint2[i]) { @@ -205,7 +205,6 @@ void StartLevel(MapRecord* level) pPlayer->nextWeapon = gPlayerTemp[i].nextWeapon; } } - gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame); PreloadCache(); InitMirrors(); trInit(); @@ -224,11 +223,11 @@ void StartLevel(MapRecord* level) } -void NewLevel(MapRecord *sng, int skill) +void NewLevel(MapRecord *sng, int skill, bool newgame) { if (skill != -1) gGameOptions.nDifficulty = skill; gSkill = gGameOptions.nDifficulty; - StartLevel(sng); + StartLevel(sng, newgame); gameaction = ga_level; } @@ -236,13 +235,12 @@ void GameInterface::NewGame(MapRecord *sng, int skill, bool) { gGameOptions.uGameFlags = 0; cheatReset(); - NewLevel(sng, skill); + NewLevel(sng, skill, true); } void GameInterface::NextLevel(MapRecord *map, int skill) { - gGameOptions.uGameFlags = GF_AdvanceLevel; - NewLevel(map, skill); + NewLevel(map, skill, false); } void GameInterface::Ticker() @@ -313,25 +311,12 @@ void GameInterface::Ticker() team_ticker[i] = 0; } - int gf = gGameOptions.uGameFlags; - - if (gf & GF_AdvanceLevel) + if (gGameOptions.uGameFlags & GF_AdvanceLevel) { + gGameOptions.uGameFlags &= ~GF_AdvanceLevel; seqKillAll(); - - if (gf & GF_EndGame) - { - STAT_Update(true); - CompleteLevel(nullptr); - } - else - { - STAT_Update(false); - // Fixme: Link maps, not episode/level pairs. - int ep = volfromlevelnum(currentLevel->levelNumber); - auto map = FindMapByLevelNum(makelevelnum(ep, gNextLevel)); - CompleteLevel(map); - } + STAT_Update(gNextLevel == nullptr); + CompleteLevel(gNextLevel); } r_NoInterpolate = false; } diff --git a/source/games/blood/src/endgame.cpp b/source/games/blood/src/endgame.cpp index 497e3046b..38b73a489 100644 --- a/source/games/blood/src/endgame.cpp +++ b/source/games/blood/src/endgame.cpp @@ -55,7 +55,6 @@ void GameInterface::LevelCompleted(MapRecord *map, int skill) { soundEngine->StopAllChannels(); gameaction = map? ga_nextlevel : ga_creditsmenu; - if (!map) gGameOptions.uGameFlags &= ~(GF_AdvanceLevel | GF_EndGame); }); } diff --git a/source/games/blood/src/levels.cpp b/source/games/blood/src/levels.cpp index 7d42fbe43..b69ab4527 100644 --- a/source/games/blood/src/levels.cpp +++ b/source/games/blood/src/levels.cpp @@ -39,7 +39,7 @@ GAMEOPTIONS gSingleGameOptions = { }; int gSkill = 2; -int gNextLevel; // fixme: let this contain a full level number. +MapRecord* gNextLevel; char BloodIniFile[BMAX_PATH] = "BLOOD.INI"; bool bINIOverride = false; @@ -91,15 +91,16 @@ void CheckKeyAbend(const char *pzSection, const char *pzKey) } -void levelLoadMapInfo(IniFile *pIni, MapRecord *pLevelInfo, const char *pzSection, int epinum, int mapnum) + +void levelLoadMapInfo(IniFile* pIni, MapRecord* pLevelInfo, const char* pzSection, int epinum, int mapnum, int* nextmap, int* nextsecret) { char buffer[16]; pLevelInfo->SetName(pIni->GetKeyString(pzSection, "Title", pLevelInfo->labelName)); pLevelInfo->Author = pIni->GetKeyString(pzSection, "Author", ""); pLevelInfo->music = pIni->GetKeyString(pzSection, "Song", ""); DefaultExtension(pLevelInfo->music, ".mid"); pLevelInfo->cdSongId = pIni->GetKeyInt(pzSection, "Track", -1); - pLevelInfo->nextLevel = pIni->GetKeyInt(pzSection, "EndingA", -1); - pLevelInfo->nextSecret = pIni->GetKeyInt(pzSection, "EndingB", -1); + *nextmap = pIni->GetKeyInt(pzSection, "EndingA", 0); + *nextsecret = pIni->GetKeyInt(pzSection, "EndingB", 0); pLevelInfo->fog = pIni->GetKeyInt(pzSection, "Fog", -0); pLevelInfo->weather = pIni->GetKeyInt(pzSection, "Weather", -0); for (int i = 0; i < kMaxMessages; i++) @@ -178,11 +179,11 @@ void levelLoadDefaults(void) csB.sound = soundEngine->FindSound(cleanPath(BloodINI->GetKeyString(buffer, "CutWavB", ""))); //pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0); - cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0) - 1; - if (cutALevel < 0) cutALevel = 0; + cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0); + if (cutALevel < 1) cutALevel = 1; - int j; - for (j = 1; j <= kMaxLevels; j++) + int nextmaps[kMaxLevels]{}, nextsecrets[kMaxLevels]{}; + for (int j = 1; j <= kMaxLevels; j++) { sprintf(buffer2, "Map%d", j); if (!BloodINI->KeyExists(buffer, buffer2)) @@ -190,13 +191,13 @@ void levelLoadDefaults(void) auto pLevelInfo = AllocateMap(); const char *pMap = BloodINI->GetKeyString(buffer, buffer2, NULL); CheckSectionAbend(pMap); - SetLevelNum(pLevelInfo, makelevelnum(i-1, j-1)); + SetLevelNum(pLevelInfo, makelevelnum(i, j)); pLevelInfo->cluster = i; pLevelInfo->mapindex = j; pLevelInfo->labelName = pMap; if (j == 1) volume->startmap = pLevelInfo->labelName; pLevelInfo->fileName.Format("%s.map", pMap); - levelLoadMapInfo(BloodINI, pLevelInfo, pMap, i, j); + levelLoadMapInfo(BloodINI, pLevelInfo, pMap, i, j, &nextmaps[j - 1], &nextsecrets[j - 1]); if (j == cutALevel) { CutsceneDef& csA = pLevelInfo->intro; @@ -206,47 +207,34 @@ void levelLoadDefaults(void) csA.sound = soundEngine->FindSound(cleanPath(BloodINI->GetKeyString(buffer, "CutWavA", ""))); } } + // Now resolve the level links + for (int j = 1; j <= kMaxLevels; j++) + { + auto map = FindMapByIndexOnly(i, j); + if (map) + { + if (nextmaps[j - 1] > 0) + { + auto nmap = FindMapByIndexOnly(i, nextmaps[j - 1]); + if (nmap) map->NextMap = nmap->labelName; + else map->NextMap = "-"; + } + if (nextsecrets[j - 1] > 0) + { + auto nmap = FindMapByIndexOnly(i, nextsecrets[j - 1]); + if (nmap) map->NextSecret = nmap->labelName; + else map->NextSecret = "-"; + } + } + } } } -void levelGetNextLevels(int *pnEndingA, int *pnEndingB) -{ - assert(pnEndingA != NULL && pnEndingB != NULL); - int nEndingA = currentLevel->nextLevel; - if (nEndingA >= 0) - nEndingA--; - int nEndingB = currentLevel->nextSecret; - if (nEndingB >= 0) - nEndingB--; - *pnEndingA = nEndingA; - *pnEndingB = nEndingB; -} - void levelEndLevel(int secret) { - int nEndingA, nEndingB; - auto episode = volfromlevelnum(currentLevel->levelNumber); gGameOptions.uGameFlags |= GF_AdvanceLevel; - levelGetNextLevels(&nEndingA, &nEndingB); - switch (secret) - { - case 0: - if (nEndingA == -1) - { - gGameOptions.uGameFlags |= GF_EndGame; - } - else - gNextLevel = nEndingA; - break; - case 1: - if (nEndingB == -1) - { - gGameOptions.uGameFlags |= GF_EndGame; - } - else - gNextLevel = nEndingB; - break; - } + if (!secret) gNextLevel = FindNextMap(currentLevel); + else gNextLevel = FindNextSecretMap(currentLevel); } void levelTryPlayMusic() diff --git a/source/games/blood/src/levels.h b/source/games/blood/src/levels.h index 6f72284bf..fdd0fc019 100644 --- a/source/games/blood/src/levels.h +++ b/source/games/blood/src/levels.h @@ -41,7 +41,6 @@ enum enum EGameFlag { GF_AdvanceLevel = 1, - GF_EndGame = 2, // 4 was for playing intro cutscenes but is no longer used. GF_PlayCutscene = 8, }; @@ -72,7 +71,7 @@ extern GAMEOPTIONS gGameOptions; extern int gSkill; extern char BloodIniFile[]; extern bool bINIOverride; -extern int gNextLevel; +extern MapRecord* gNextLevel; extern bool gGameStarted; void levelInitINI(const char *pzIni); diff --git a/source/games/blood/src/loadsave.cpp b/source/games/blood/src/loadsave.cpp index db6c86062..5cea9d695 100644 --- a/source/games/blood/src/loadsave.cpp +++ b/source/games/blood/src/loadsave.cpp @@ -641,7 +641,6 @@ void SerializeState(FSerializer& arc) ("modern", gModernMap) #endif ("cheating", bPlayerCheated) - ("nextlevel", gNextLevel) ("skyhoriz", pSky->horizfrac) ("skyy", pSky->yoffs) ("scale", pSky->yscale) diff --git a/source/games/blood/src/messages.cpp b/source/games/blood/src/messages.cpp index 42b92b344..8ff7ad3f7 100644 --- a/source/games/blood/src/messages.cpp +++ b/source/games/blood/src/messages.cpp @@ -268,7 +268,7 @@ static int parseArgs(char *pzArgs, int *nArg1, int *nArg2) if (!nArg1 || !nArg2 || strlen(pzArgs) < 3) return -1; *nArg1 = pzArgs[0] - '0'; - *nArg2 = (pzArgs[1] - '0')*10+(pzArgs[2]-'0') - 1; + *nArg2 = (pzArgs[1] - '0')*10+(pzArgs[2]-'0'); return 2; } @@ -421,7 +421,7 @@ static bool cheatMario(cheatseq_t* c) int nEpisode, nLevel; if (parseArgs((char*)c->Args, &nEpisode, &nLevel) == 2) { - auto map = FindMapByLevelNum(makelevelnum(nEpisode, nLevel)); + auto map = FindMapByIndex(nEpisode, nLevel); if (map) DeferedStartGame(map, -1); } return true; diff --git a/source/games/blood/src/nnexts.cpp b/source/games/blood/src/nnexts.cpp index a708623e0..9e5ef9fd0 100644 --- a/source/games/blood/src/nnexts.cpp +++ b/source/games/blood/src/nnexts.cpp @@ -5215,13 +5215,7 @@ void seqSpawnerOffSameTx(XSPRITE* pXSource) { void levelEndLevelCustom(int nLevel) { gGameOptions.uGameFlags |= GF_AdvanceLevel; - - if (nLevel >= 16 || nLevel < 0) { - gGameOptions.uGameFlags |= GF_EndGame; - return; - } - - gNextLevel = nLevel; + gNextLevel = FindMapByIndex(currentLevel->cluster, nLevel + 1); } void callbackUniMissileBurst(int nSprite) // 22 diff --git a/source/games/duke/src/cheats.cpp b/source/games/duke/src/cheats.cpp index 7dbce0077..c76f8c96e 100644 --- a/source/games/duke/src/cheats.cpp +++ b/source/games/duke/src/cheats.cpp @@ -292,11 +292,11 @@ static bool cheatItems(int player) static bool cheatLevel(cheatseq_t *s) { int volnume,levnume; - volnume = s->Args[0] - '0' - 1; - levnume = (s->Args[1] - '0')*10+(s->Args[2]-'0') - 1; + volnume = s->Args[0] - '0'; + levnume = (s->Args[1] - '0')*10+(s->Args[2]-'0'); // Instead of hard coded range checks on volume and level, let's just check if the level is defined. - auto map = FindMapByLevelNum(makelevelnum(volnume, levnume)); + auto map = FindMapByIndex(volnume, levnume); if (map) { ChangeLevel(map, -1);