- adapted Blood to the new level progression features and simplified several parts, particularly the clumsy retrieval of the next level.

Some cheats in Duke will not work correctly with this commit!
This commit is contained in:
Christoph Oelckers 2021-05-02 09:08:57 +02:00
parent 93bfc35ad6
commit e9385ed4e8
11 changed files with 100 additions and 105 deletions

View file

@ -294,7 +294,7 @@ static MapRecord* levelwarp_common(FCommandLine& argv, const char *cmdname, cons
Printf(PRINT_BOLD, "Invalid level! Numbers must be > 0\n"); Printf(PRINT_BOLD, "Invalid level! Numbers must be > 0\n");
return nullptr; 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 (!map)
{ {
if (numparm == 2) Printf(PRINT_BOLD, "Level E%s L%s not found!\n", argv[1], argv[2]); if (numparm == 2) Printf(PRINT_BOLD, "Level E%s L%s not found!\n", argv[1], argv[2]);

View file

@ -84,10 +84,10 @@ CCMD(mapinfo)
if (clus.intro.isdefined() || clus.outro.isdefined()) if (clus.intro.isdefined() || clus.outro.isdefined())
{ {
Printf("Cluster %d\n\tName = '%s'\n", clus.index, clus.name.GetChars()); 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.function.IsNotEmpty()) Printf("\tIntro function = %s\n", clus.intro.function.GetChars());
if (clus.intro.video.IsNotEmpty()) Printf("\tIntro video = %d\n", clus.intro.video.GetChars()); if (clus.intro.video.IsNotEmpty()) Printf("\tIntro video = %s\n", clus.intro.video.GetChars());
if (clus.outro.function.IsNotEmpty()) Printf("\tOutro function = %d\n", clus.outro.function.GetChars()); if (clus.outro.function.IsNotEmpty()) Printf("\tOutro function = %s\n", clus.outro.function.GetChars());
if (clus.outro.video.IsNotEmpty()) Printf("\tOutro video = %d\n", clus.outro.video.GetChars()); if (clus.outro.video.IsNotEmpty()) Printf("\tOutro video = %s\n", clus.outro.video.GetChars());
Printf("}\n"); Printf("}\n");
} }
} }
@ -108,10 +108,10 @@ CCMD(mapinfo)
if (map->cdSongId > 0) Printf("\tCD track = %d\n", map->cdSongId); if (map->cdSongId > 0) Printf("\tCD track = %d\n", map->cdSongId);
if (map->parTime) Printf("\tPar Time = %d\n", map->parTime); if (map->parTime) Printf("\tPar Time = %d\n", map->parTime);
if (map->designerTime) Printf("\tPar Time = %d\n", map->designerTime); 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.function.IsNotEmpty()) Printf("\tIntro function = %s\n", map->intro.function.GetChars());
if (map->intro.video.IsNotEmpty()) Printf("\tIntro video = %d\n", map->intro.video.GetChars()); if (map->intro.video.IsNotEmpty()) Printf("\tIntro video = %s\n", map->intro.video.GetChars());
if (map->outro.function.IsNotEmpty()) Printf("\tOutro function = %d\n", map->outro.function.GetChars()); if (map->outro.function.IsNotEmpty()) Printf("\tOutro function = %s\n", map->outro.function.GetChars());
if (map->outro.video.IsNotEmpty()) Printf("\tOutro video = %d\n", map->outro.video.GetChars()); if (map->outro.video.IsNotEmpty()) Printf("\tOutro video = %s\n", map->outro.video.GetChars());
Printf("}\n"); Printf("}\n");
} }
else else
@ -173,6 +173,41 @@ VolumeRecord* AllocateVolume()
{ {
return &volumes[volumes.Reserve(1)]; 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. // return a map whose cluster and map number matches.
// if there's only one map with the given level number return that. // if there's only one map with the given level number return that.
MapRecord* FindMapByClusterAndLevelNum(int cluster, int num) MapRecord* FindMapByClusterAndLevelNum(int cluster, int num)
@ -195,12 +230,6 @@ MapRecord* FindMapByClusterAndLevelNum(int cluster, int num)
return nullptr; 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) bool SetMusicForMap(const char* mapname, const char* music, bool namehack)
{ {
static const char* specials[] = { "intro", "briefing", "loading" }; static const char* specials[] = { "intro", "briefing", "loading" };

View file

@ -205,9 +205,11 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack = fals
MapRecord *FindMapByName(const char *nm); MapRecord *FindMapByName(const char *nm);
MapRecord *FindMapByLevelNum(int num); 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); MapRecord* FindMapByClusterAndLevelNum(int clst, int num);
inline MapRecord* FindMapByIndexOnly(int clst, int num) { return FindMapByClusterAndLevelNum(clst, num); }
MapRecord *FindNextMap(MapRecord *thismap); MapRecord *FindNextMap(MapRecord *thismap);
MapRecord* FindNextSecretMap(MapRecord* thismap);
MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic = nullptr); MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic = nullptr);
MapRecord* AllocateMap(); MapRecord* AllocateMap();

View file

@ -80,7 +80,7 @@ void EndLevel(void)
seqKillAll(); seqKillAll();
} }
void StartLevel(MapRecord* level) void StartLevel(MapRecord* level, bool newgame)
{ {
if (!level) return; if (!level) return;
gFrameCount = 0; gFrameCount = 0;
@ -97,14 +97,14 @@ void StartLevel(MapRecord* level)
/////// ///////
} }
#if 0 #if 0
else if (gGameOptions.nGameType > 0 && !(gGameOptions.uGameFlags & GF_AdvanceLevel)) else if (gGameOptions.nGameType > 0 && newgame)
{ {
// todo // todo
gBlueFlagDropped = false; gBlueFlagDropped = false;
gRedFlagDropped = false; gRedFlagDropped = false;
} }
#endif #endif
if (gGameOptions.uGameFlags & GF_AdvanceLevel) if (!newgame)
{ {
for (int i = connecthead; i >= 0; i = connectpoint2[i]) for (int i = connecthead; i >= 0; i = connectpoint2[i])
{ {
@ -182,13 +182,13 @@ void StartLevel(MapRecord* level)
evInit(); evInit();
for (int i = connecthead; i >= 0; i = connectpoint2[i]) for (int i = connecthead; i >= 0; i = connectpoint2[i])
{ {
if (!(gGameOptions.uGameFlags & GF_AdvanceLevel)) if (newgame)
{ {
playerInit(i, 0); playerInit(i, 0);
} }
playerStart(i, 1); playerStart(i, 1);
} }
if (gGameOptions.uGameFlags & GF_AdvanceLevel) if (!newgame)
{ {
for (int i = connecthead; i >= 0; i = connectpoint2[i]) for (int i = connecthead; i >= 0; i = connectpoint2[i])
{ {
@ -205,7 +205,6 @@ void StartLevel(MapRecord* level)
pPlayer->nextWeapon = gPlayerTemp[i].nextWeapon; pPlayer->nextWeapon = gPlayerTemp[i].nextWeapon;
} }
} }
gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame);
PreloadCache(); PreloadCache();
InitMirrors(); InitMirrors();
trInit(); 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; if (skill != -1) gGameOptions.nDifficulty = skill;
gSkill = gGameOptions.nDifficulty; gSkill = gGameOptions.nDifficulty;
StartLevel(sng); StartLevel(sng, newgame);
gameaction = ga_level; gameaction = ga_level;
} }
@ -236,13 +235,12 @@ void GameInterface::NewGame(MapRecord *sng, int skill, bool)
{ {
gGameOptions.uGameFlags = 0; gGameOptions.uGameFlags = 0;
cheatReset(); cheatReset();
NewLevel(sng, skill); NewLevel(sng, skill, true);
} }
void GameInterface::NextLevel(MapRecord *map, int skill) void GameInterface::NextLevel(MapRecord *map, int skill)
{ {
gGameOptions.uGameFlags = GF_AdvanceLevel; NewLevel(map, skill, false);
NewLevel(map, skill);
} }
void GameInterface::Ticker() void GameInterface::Ticker()
@ -313,25 +311,12 @@ void GameInterface::Ticker()
team_ticker[i] = 0; team_ticker[i] = 0;
} }
int gf = gGameOptions.uGameFlags; if (gGameOptions.uGameFlags & GF_AdvanceLevel)
if (gf & GF_AdvanceLevel)
{ {
gGameOptions.uGameFlags &= ~GF_AdvanceLevel;
seqKillAll(); seqKillAll();
STAT_Update(gNextLevel == nullptr);
if (gf & GF_EndGame) CompleteLevel(gNextLevel);
{
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);
}
} }
r_NoInterpolate = false; r_NoInterpolate = false;
} }

View file

@ -55,7 +55,6 @@ void GameInterface::LevelCompleted(MapRecord *map, int skill)
{ {
soundEngine->StopAllChannels(); soundEngine->StopAllChannels();
gameaction = map? ga_nextlevel : ga_creditsmenu; gameaction = map? ga_nextlevel : ga_creditsmenu;
if (!map) gGameOptions.uGameFlags &= ~(GF_AdvanceLevel | GF_EndGame);
}); });
} }

View file

@ -39,7 +39,7 @@ GAMEOPTIONS gSingleGameOptions = {
}; };
int gSkill = 2; int gSkill = 2;
int gNextLevel; // fixme: let this contain a full level number. MapRecord* gNextLevel;
char BloodIniFile[BMAX_PATH] = "BLOOD.INI"; char BloodIniFile[BMAX_PATH] = "BLOOD.INI";
bool bINIOverride = false; 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]; char buffer[16];
pLevelInfo->SetName(pIni->GetKeyString(pzSection, "Title", pLevelInfo->labelName)); pLevelInfo->SetName(pIni->GetKeyString(pzSection, "Title", pLevelInfo->labelName));
pLevelInfo->Author = pIni->GetKeyString(pzSection, "Author", ""); pLevelInfo->Author = pIni->GetKeyString(pzSection, "Author", "");
pLevelInfo->music = pIni->GetKeyString(pzSection, "Song", ""); DefaultExtension(pLevelInfo->music, ".mid"); pLevelInfo->music = pIni->GetKeyString(pzSection, "Song", ""); DefaultExtension(pLevelInfo->music, ".mid");
pLevelInfo->cdSongId = pIni->GetKeyInt(pzSection, "Track", -1); pLevelInfo->cdSongId = pIni->GetKeyInt(pzSection, "Track", -1);
pLevelInfo->nextLevel = pIni->GetKeyInt(pzSection, "EndingA", -1); *nextmap = pIni->GetKeyInt(pzSection, "EndingA", 0);
pLevelInfo->nextSecret = pIni->GetKeyInt(pzSection, "EndingB", -1); *nextsecret = pIni->GetKeyInt(pzSection, "EndingB", 0);
pLevelInfo->fog = pIni->GetKeyInt(pzSection, "Fog", -0); pLevelInfo->fog = pIni->GetKeyInt(pzSection, "Fog", -0);
pLevelInfo->weather = pIni->GetKeyInt(pzSection, "Weather", -0); pLevelInfo->weather = pIni->GetKeyInt(pzSection, "Weather", -0);
for (int i = 0; i < kMaxMessages; i++) for (int i = 0; i < kMaxMessages; i++)
@ -178,11 +179,11 @@ void levelLoadDefaults(void)
csB.sound = soundEngine->FindSound(cleanPath(BloodINI->GetKeyString(buffer, "CutWavB", ""))); csB.sound = soundEngine->FindSound(cleanPath(BloodINI->GetKeyString(buffer, "CutWavB", "")));
//pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0); //pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0);
cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0) - 1; cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0);
if (cutALevel < 0) cutALevel = 0; if (cutALevel < 1) cutALevel = 1;
int j; int nextmaps[kMaxLevels]{}, nextsecrets[kMaxLevels]{};
for (j = 1; j <= kMaxLevels; j++) for (int j = 1; j <= kMaxLevels; j++)
{ {
sprintf(buffer2, "Map%d", j); sprintf(buffer2, "Map%d", j);
if (!BloodINI->KeyExists(buffer, buffer2)) if (!BloodINI->KeyExists(buffer, buffer2))
@ -190,13 +191,13 @@ void levelLoadDefaults(void)
auto pLevelInfo = AllocateMap(); auto pLevelInfo = AllocateMap();
const char *pMap = BloodINI->GetKeyString(buffer, buffer2, NULL); const char *pMap = BloodINI->GetKeyString(buffer, buffer2, NULL);
CheckSectionAbend(pMap); CheckSectionAbend(pMap);
SetLevelNum(pLevelInfo, makelevelnum(i-1, j-1)); SetLevelNum(pLevelInfo, makelevelnum(i, j));
pLevelInfo->cluster = i; pLevelInfo->cluster = i;
pLevelInfo->mapindex = j; pLevelInfo->mapindex = j;
pLevelInfo->labelName = pMap; pLevelInfo->labelName = pMap;
if (j == 1) volume->startmap = pLevelInfo->labelName; if (j == 1) volume->startmap = pLevelInfo->labelName;
pLevelInfo->fileName.Format("%s.map", pMap); 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) if (j == cutALevel)
{ {
CutsceneDef& csA = pLevelInfo->intro; CutsceneDef& csA = pLevelInfo->intro;
@ -206,47 +207,34 @@ void levelLoadDefaults(void)
csA.sound = soundEngine->FindSound(cleanPath(BloodINI->GetKeyString(buffer, "CutWavA", ""))); 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) void levelEndLevel(int secret)
{ {
int nEndingA, nEndingB;
auto episode = volfromlevelnum(currentLevel->levelNumber);
gGameOptions.uGameFlags |= GF_AdvanceLevel; gGameOptions.uGameFlags |= GF_AdvanceLevel;
levelGetNextLevels(&nEndingA, &nEndingB); if (!secret) gNextLevel = FindNextMap(currentLevel);
switch (secret) else gNextLevel = FindNextSecretMap(currentLevel);
{
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;
}
} }
void levelTryPlayMusic() void levelTryPlayMusic()

View file

@ -41,7 +41,6 @@ enum
enum EGameFlag enum EGameFlag
{ {
GF_AdvanceLevel = 1, GF_AdvanceLevel = 1,
GF_EndGame = 2,
// 4 was for playing intro cutscenes but is no longer used. // 4 was for playing intro cutscenes but is no longer used.
GF_PlayCutscene = 8, GF_PlayCutscene = 8,
}; };
@ -72,7 +71,7 @@ extern GAMEOPTIONS gGameOptions;
extern int gSkill; extern int gSkill;
extern char BloodIniFile[]; extern char BloodIniFile[];
extern bool bINIOverride; extern bool bINIOverride;
extern int gNextLevel; extern MapRecord* gNextLevel;
extern bool gGameStarted; extern bool gGameStarted;
void levelInitINI(const char *pzIni); void levelInitINI(const char *pzIni);

View file

@ -641,7 +641,6 @@ void SerializeState(FSerializer& arc)
("modern", gModernMap) ("modern", gModernMap)
#endif #endif
("cheating", bPlayerCheated) ("cheating", bPlayerCheated)
("nextlevel", gNextLevel)
("skyhoriz", pSky->horizfrac) ("skyhoriz", pSky->horizfrac)
("skyy", pSky->yoffs) ("skyy", pSky->yoffs)
("scale", pSky->yscale) ("scale", pSky->yscale)

View file

@ -268,7 +268,7 @@ static int parseArgs(char *pzArgs, int *nArg1, int *nArg2)
if (!nArg1 || !nArg2 || strlen(pzArgs) < 3) if (!nArg1 || !nArg2 || strlen(pzArgs) < 3)
return -1; return -1;
*nArg1 = pzArgs[0] - '0'; *nArg1 = pzArgs[0] - '0';
*nArg2 = (pzArgs[1] - '0')*10+(pzArgs[2]-'0') - 1; *nArg2 = (pzArgs[1] - '0')*10+(pzArgs[2]-'0');
return 2; return 2;
} }
@ -421,7 +421,7 @@ static bool cheatMario(cheatseq_t* c)
int nEpisode, nLevel; int nEpisode, nLevel;
if (parseArgs((char*)c->Args, &nEpisode, &nLevel) == 2) if (parseArgs((char*)c->Args, &nEpisode, &nLevel) == 2)
{ {
auto map = FindMapByLevelNum(makelevelnum(nEpisode, nLevel)); auto map = FindMapByIndex(nEpisode, nLevel);
if (map) DeferedStartGame(map, -1); if (map) DeferedStartGame(map, -1);
} }
return true; return true;

View file

@ -5215,13 +5215,7 @@ void seqSpawnerOffSameTx(XSPRITE* pXSource) {
void levelEndLevelCustom(int nLevel) { void levelEndLevelCustom(int nLevel) {
gGameOptions.uGameFlags |= GF_AdvanceLevel; gGameOptions.uGameFlags |= GF_AdvanceLevel;
gNextLevel = FindMapByIndex(currentLevel->cluster, nLevel + 1);
if (nLevel >= 16 || nLevel < 0) {
gGameOptions.uGameFlags |= GF_EndGame;
return;
}
gNextLevel = nLevel;
} }
void callbackUniMissileBurst(int nSprite) // 22 void callbackUniMissileBurst(int nSprite) // 22

View file

@ -292,11 +292,11 @@ static bool cheatItems(int player)
static bool cheatLevel(cheatseq_t *s) static bool cheatLevel(cheatseq_t *s)
{ {
int volnume,levnume; int volnume,levnume;
volnume = s->Args[0] - '0' - 1; volnume = s->Args[0] - '0';
levnume = (s->Args[1] - '0')*10+(s->Args[2]-'0') - 1; 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. // 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) if (map)
{ {
ChangeLevel(map, -1); ChangeLevel(map, -1);