- 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");
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]);

View file

@ -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" };

View file

@ -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();

View file

@ -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;
}

View file

@ -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);
});
}

View file

@ -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()

View file

@ -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);

View file

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

View file

@ -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;

View file

@ -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

View file

@ -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);