diff --git a/source/core/mainloop.cpp b/source/core/mainloop.cpp index 6969ebdf5..7db914ed9 100644 --- a/source/core/mainloop.cpp +++ b/source/core/mainloop.cpp @@ -220,6 +220,7 @@ static void GameTicker() case ga_mainmenu: FX_StopAllSounds(); + if (isBlood()) Mus_Stop(); case ga_mainmenunostopsound: gi->FreeLevelData(); gamestate = GS_MENUSCREEN; diff --git a/source/core/movie/movieplayer.cpp b/source/core/movie/movieplayer.cpp index 813ee0062..dbf20e5ff 100644 --- a/source/core/movie/movieplayer.cpp +++ b/source/core/movie/movieplayer.cpp @@ -246,9 +246,10 @@ public: public: bool isvalid() { return !failed; } - VpxPlayer(FileReader& fr_, TArray& animSnd_, int flags, int origframedelay, FString& error) : animSnd(std::move(animSnd_)) + VpxPlayer(FileReader& fr_, TArray& animSnd_, int flags_, int origframedelay, FString& error) : animSnd(std::move(animSnd_)) { fr = std::move(fr_); + flags = flags_; if (!ReadIVFHeader(origframedelay)) { @@ -552,13 +553,14 @@ public: } - SmkPlayer(const char *fn, TArray& ans, int flags) : animSnd(std::move(ans)) + SmkPlayer(const char *fn, TArray& ans, int flags_) : animSnd(std::move(ans)) { hSMK = Smacker_Open(fn); if (!hSMK.isValid) { return; } + flags = flags_; Smacker_GetFrameSize(hSMK, nWidth, nHeight); pFrame.Resize(nWidth * nHeight + std::max(nWidth, nHeight)); nFrameRate = Smacker_GetFrameRate(hSMK); diff --git a/source/core/screenjob.cpp b/source/core/screenjob.cpp index 03e79b95a..1e0e3c3ce 100644 --- a/source/core/screenjob.cpp +++ b/source/core/screenjob.cpp @@ -357,7 +357,7 @@ bool ScreenJobValidate() bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion_) { - if (cs.function.IsNotEmpty() && cs.video.IsNotEmpty() && cs.function.CompareNoCase("none") != 0) + if ((cs.function.IsNotEmpty() || cs.video.IsNotEmpty()) && cs.function.CompareNoCase("none") != 0) { completion = completion_; runner = CreateRunner(); @@ -407,7 +407,9 @@ void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic) } else { - if (!StartCutscene(globalCutscenes.Intro, SJ_BLOCKUI, [=](bool) { gameaction = complete_ga; })) gameaction = def_ga; + if (!StartCutscene(globalCutscenes.Intro, SJ_BLOCKUI, [=](bool) { + gameaction = complete_ga; + })) gameaction = def_ga; } } diff --git a/source/games/blood/all.cpp b/source/games/blood/all.cpp index 23658ca4d..87b125f15 100644 --- a/source/games/blood/all.cpp +++ b/source/games/blood/all.cpp @@ -31,7 +31,6 @@ #include "src/callback.cpp" #include "src/choke.cpp" #include "src/controls.cpp" -#include "src/credits.cpp" #include "src/db.cpp" #include "src/dude.cpp" #include "src/endgame.cpp" diff --git a/source/games/blood/src/blood.cpp b/source/games/blood/src/blood.cpp index 20130881d..97d78fb1a 100644 --- a/source/games/blood/src/blood.cpp +++ b/source/games/blood/src/blood.cpp @@ -49,6 +49,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "v_draw.h" #include "texturemanager.h" #include "statusbar.h" +#include "vm.h" BEGIN_BLD_NS @@ -225,28 +226,10 @@ void StartLevel(MapRecord* level) void NewLevel(MapRecord *sng, int skill) { - auto completion = [=](bool = false) - { - if (skill != -1) gGameOptions.nDifficulty = skill; - gSkill = gGameOptions.nDifficulty; - StartLevel(sng); - gameaction = ga_level; - }; - - bool startedCutscene = false; - if (!(sng->flags & MI_USERMAP)) - { - int episode = volfromlevelnum(sng->levelNumber); - int level = mapfromlevelnum(sng->levelNumber); - if (gEpisodeInfo[episode].cutALevel == level && gEpisodeInfo[episode].cutsceneAName[0]) - { - levelPlayIntroScene(episode, completion); - startedCutscene = true; - } - - } - if (!startedCutscene) completion(false); - + if (skill != -1) gGameOptions.nDifficulty = skill; + gSkill = gGameOptions.nDifficulty; + StartLevel(sng); + gameaction = ga_level; } void GameInterface::NewGame(MapRecord *sng, int skill, bool) @@ -330,35 +313,20 @@ void GameInterface::Ticker() team_ticker[i] = 0; } - if ((gGameOptions.uGameFlags & GF_AdvanceLevel) != 0) + int gf = gGameOptions.uGameFlags; + + if (gf & GF_AdvanceLevel) { seqKillAll(); - if (gGameOptions.uGameFlags & GF_EndGame) + + if (gf & GF_EndGame) { STAT_Update(true); - if (gGameOptions.nGameType == 0) - { - auto completion = [](bool) { - gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame); - gameaction = ga_creditsmenu; - }; - - if (gGameOptions.uGameFlags & GF_PlayCutscene) - { - levelPlayEndScene(volfromlevelnum(currentLevel->levelNumber), completion); - } - else completion(false); - } - else - { - gGameOptions.uGameFlags &= ~(GF_AdvanceLevel|GF_EndGame); - } + CompleteLevel(nullptr); } else { STAT_Update(false); - EndLevel(); - Mus_Stop(); // Fixme: Link maps, not episode/level pairs. int ep = volfromlevelnum(currentLevel->levelNumber); auto map = FindMapByLevelNum(levelnum(ep, gNextLevel)); @@ -474,13 +442,24 @@ void GameInterface::app_init() if (!tileInit(0, NULL)) I_FatalError("TILES###.ART files not found"); + //---------- + // There's a small problem here. We have a nasty circular dependency thanks to .def's all-inclusive mentality. + // snd may depend on rffdefineid in .def + // .def depends on level definitions. + // level definitions depend on sound to resolve cutscene sounds. + // Conclusion: All map related definitions need to be taken out of .def - meaning 'definecutscene' cannot be done in there + // Unless that is done no sound related data can be done with rffdefineid. + Printf(PRINT_NONOTIFY, "Initializing sound system\n"); + sndInit(); + levelLoadDefaults(); LoadDefinitions(); + + //--------- SetTileNames(); C_InitConback(TexMan.CheckForTexture("BACKTILE", ETextureType::Any), true, 0.25); TileFiles.SetBackup(); - powerupInit(); Printf(PRINT_NONOTIFY, "Loading cosine table\n"); trigInit(); Printf(PRINT_NONOTIFY, "Initializing view subsystem\n"); @@ -496,9 +475,6 @@ void GameInterface::app_init() gGameOptions.nGameType = 0; UpdateNetworkMenus(); - Printf(PRINT_NONOTIFY, "Initializing sound system\n"); - sndInit(); - gChoke.init(518, chokeCallback); UpdateDacs(0, true); @@ -573,4 +549,48 @@ ReservedSpace GameInterface::GetReservedScreenSpace(int viewsize) return new GameInterface; } +enum +{ + kLoadScreenCRC = -2051908571, + kLoadScreenWideBackWidth = 256, + kLoadScreenWideSideWidth = 128, + +}; + + +DEFINE_ACTION_FUNCTION(_Blood, OriginalLoadScreen) +{ + static int bLoadScreenCrcMatch = -1; + if (bLoadScreenCrcMatch == -1) bLoadScreenCrcMatch = tileGetCRC32(kLoadScreen) == kLoadScreenCRC; + ACTION_RETURN_INT(bLoadScreenCrcMatch); +} + +DEFINE_ACTION_FUNCTION(_Blood, PlayIntroMusic) +{ + Mus_Play(nullptr, "PESTIS.MID", false); + return 0; +} + +DEFINE_ACTION_FUNCTION(_Blood, sndStartSample) +{ + PARAM_PROLOGUE; + PARAM_INT(id); + PARAM_INT(vol); + PARAM_INT(chan); + PARAM_BOOL(looped); + PARAM_INT(chanflags); + sndStartSample(id, vol, chan, looped, EChanFlags::FromInt(chanflags)); + return 0; +} + +DEFINE_ACTION_FUNCTION(_Blood, sndStartSampleNamed) +{ + PARAM_PROLOGUE; + PARAM_STRING(id); + PARAM_INT(vol); + PARAM_INT(chan); + sndStartSample(id, vol, chan); + return 0; +} + END_BLD_NS diff --git a/source/games/blood/src/credits.cpp b/source/games/blood/src/credits.cpp deleted file mode 100644 index 2fa3fa12c..000000000 --- a/source/games/blood/src/credits.cpp +++ /dev/null @@ -1,164 +0,0 @@ -//------------------------------------------------------------------------- -/* -Copyright (C) 2010-2019 EDuke32 developers and contributors -Copyright (C) 2019 Nuke.YKT - -This file is part of NBlood. - -NBlood is free software; you can redistribute it and/or -modify it under the terms of the GNU General Public License version 2 -as published by the Free Software Foundation. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. - -See the GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. -*/ -//------------------------------------------------------------------------- - -#include "ns.h" // Must come before everything else! - -#include "build.h" -#include "compat.h" -#include "SmackerDecoder.h" -#include "blood.h" -#include "animtexture.h" -#include "raze_sound.h" -#include "v_2ddrawer.h" -#include "screenjob.h" -#include "gamestate.h" -#include "razemenu.h" - -BEGIN_BLD_NS - -#if 0 -class DBloodIntroImage : public DImageScreen -{ - bool mus; - -public: - DBloodIntroImage(int tilenum, int flags = 0, bool withmusic = false) : DImageScreen(tilenum, flags), mus(withmusic) - {} - - void Start() override - { - sndStartSample("THUNDER2", 128, -1); - if (mus) sndPlaySpecialMusicOrNothing(MUS_INTRO); - } -}; -#endif - -//--------------------------------------------------------------------------- -// -// -// -//--------------------------------------------------------------------------- - -void playlogos() -{ -#if 0 - TArray jobs; - static AnimSound logosound[] = - { - { 1, -1 }, - { -1, -1 }, - { 1, -1 }, - { -1,-1 } - }; - - if (logosound[0].soundnum == -1) - { - logosound[0].soundnum = S_FindSound("logo.wav"); - logosound[2].soundnum = S_FindSound("gt.wav"); - } - - - if (!userConfig.nologo) - { - if (fileSystem.FindFile("logo.smk") != -1) - { - jobs.Push(PlayVideo("logo.smk", &logosound[0], 0)); - } - else - { - jobs.Push(Create(2050)); - } - if (fileSystem.FindFile("gti.smk") != -1) - { - jobs.Push(PlayVideo("gti.smk", &logosound[2], 0)); - } - else - { - jobs.Push(Create(2052)); - } - } - jobs.Push(Create(1)); - jobs.Push(Create(2518, DScreenJob::fadein, true)); - - RunScreenJob(jobs, [](bool) { - Mus_Stop(); - gameaction = ga_mainmenu; - }, SJ_BLOCKUI); -#endif -} - -void playSmk(const char *smk, const char *wav, int wavid, CompletionFunc func) -{ -#if 0 - TArray jobs; - static AnimSound smksound[] = - { - { 1, -1 }, - { -1, -1 }, - }; - int id = S_FindSoundByResID(wavid); - if (id <= 0) - { - FString wavv = wav; - FixPathSeperator(wavv); - id = S_FindSound(wavv); - // Strip the drive letter and retry. - if (id <= 0 && wavv.Len() > 3 && wavv[1] == ':' && isalpha(wavv[0]) && wavv[2] == '/') - { - id = S_FindSound(wavv.GetChars() + 3); - } - } - FString smkk = smk; - FixPathSeperator(smkk); - smksound[0].soundnum = id; - jobs.Push(PlayVideo(smkk, smksound, nullptr)); - RunScreenJob(jobs, func); -#endif -} - -void levelPlayIntroScene(int nEpisode, CompletionFunc completion) -{ - Mus_Stop(); - sndKillAllSounds(); - sfxKillAllSounds(); - ambKillAll(); - seqKillAll(); - EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode]; - playSmk(pEpisode->cutsceneAName, pEpisode->cutsceneASound, pEpisode->cutsceneAWave, completion); -} - -void levelPlayEndScene(int nEpisode, CompletionFunc completion) -{ - gGameOptions.uGameFlags &= ~GF_PlayCutscene; - Mus_Stop(); - sndKillAllSounds(); - sfxKillAllSounds(); - ambKillAll(); - seqKillAll(); - EPISODEINFO *pEpisode = &gEpisodeInfo[nEpisode]; - playSmk(pEpisode->cutsceneBName, pEpisode->cutsceneBSound, pEpisode->cutsceneBWave, completion); -} - - - -END_BLD_NS diff --git a/source/games/blood/src/endgame.cpp b/source/games/blood/src/endgame.cpp index 27d19dce5..497e3046b 100644 --- a/source/games/blood/src/endgame.cpp +++ b/source/games/blood/src/endgame.cpp @@ -36,158 +36,27 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. BEGIN_BLD_NS -enum -{ - kLoadScreenCRC = -2051908571, - kLoadScreenWideBackWidth = 256, - kLoadScreenWideSideWidth = 128, - -}; - -static int bLoadScreenCrcMatch = -1; - -static void drawTextScreenBackground(void) -{ - if (bLoadScreenCrcMatch == -1) bLoadScreenCrcMatch = tileGetCRC32(kLoadScreen) == kLoadScreenCRC; - - if (bLoadScreenCrcMatch) - { - if (ActiveRatio(twod->GetWidth(), twod->GetHeight()) < 1.34f) - { - DrawTexture(twod, tileGetTexture(kLoadScreen), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE); - } - else - { - int width = scale(twod->GetWidth(), 240, twod->GetHeight()); - int nCount = (width + kLoadScreenWideBackWidth - 1) / kLoadScreenWideBackWidth; - for (int i = 0; i < nCount; i++) - { - DrawTexture(twod, tileGetTexture(kLoadScreenWideBack), (i * kLoadScreenWideBackWidth), 0, - DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE); - } - DrawTexture(twod, tileGetTexture(kLoadScreenWideLeft), 0, 0, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, DTA_TopLeft, true, TAG_DONE); - DrawTexture(twod, tileGetTexture(kLoadScreenWideRight), width - tileWidth(kLoadScreenWideRight), 0, DTA_TopLeft, true, - DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE); - DrawTexture(twod, tileGetTexture(kLoadScreenWideMiddle), (width - tileWidth(kLoadScreenWideMiddle)) / 2, 0, DTA_TopLeft, true, - DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, TAG_DONE); - } - } - else - { - DrawTexture(twod, tileGetTexture(kLoadScreen), 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, TAG_DONE); - } -} - -// One these screens get scriptified this should use the version in BloodMenuDelegate. -static void DrawCaption(const char* text) -{ - double scalex = 1.; // Expand the box if the text is longer - int width = BigFont->StringWidth(text); - int boxwidth = tileWidth(2038); - if (boxwidth - 10 < width) scalex = double(width) / (boxwidth - 10); - - DrawTexture(twod, tileGetTexture(2038, true), 160, 20, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_CenterOffsetRel, true, DTA_ScaleX, scalex, TAG_DONE); - DrawText(twod, BigFont, CR_UNDEFINED, 160 - width / 2, 20 - tileHeight(4193) / 2, text, DTA_FullscreenScale, FSMode_Fit320x200Top, TAG_DONE); -} - - -#if 0 // public DScreenJob -class DBloodSummaryScreen : public DSkippableScreenJob -{ - void DrawKills(void) - { - char pBuffer[40]; - if (gGameOptions.nGameType == 0) - { - viewDrawText(1, FStringf("%s:", GStrings("KILLS")), 75, 50, -128, 0, 0, 1); - mysnprintf(pBuffer, 40,"%2d", gKillMgr.Kills); - viewDrawText(1, pBuffer, 160, 50, -128, 0, 0, 1); - viewDrawText(1, GStrings("OF"), 190, 50, -128, 0, 0, 1); - mysnprintf(pBuffer, 40, "%2d", gKillMgr.TotalKills); - viewDrawText(1, pBuffer, 220, 50, -128, 0, 0, 1); - } - else - { - viewDrawText(3, "#", 85, 35, -128, 0, 0, 1); - viewDrawText(3, GStrings("NAME"), 100, 35, -128, 0, 0, 1); - viewDrawText(3, GStrings("FRAGS"), 210, 35, -128, 0, 0, 1); - int nStart = 0; - int nEnd = kMaxPlayers; - - for (int i = nStart; i < nEnd; i++) if (playeringame[i]) - { - mysnprintf(pBuffer, 40, "%-2d", i); - viewDrawText(3, pBuffer, 85, 50 + 8 * i, -128, 0, 0, 1); - mysnprintf(pBuffer, 40, "%s", PlayerName(i)); - viewDrawText(3, pBuffer, 100, 50 + 8 * i, -128, 0, 0, 1); - mysnprintf(pBuffer, 40, "%d", gPlayer[i].fragCount); - viewDrawText(3, pBuffer, 210, 50 + 8 * i, -128, 0, 0, 1); - } - } - } - - void DrawSecrets(void) - { - char pBuffer[40]; - viewDrawText(1, FStringf("%s:", GStrings("TXT_SECRETS")), 75, 70, -128, 0, 0, 1); - mysnprintf(pBuffer, 40, "%2d", gSecretMgr.Founds); - viewDrawText(1, pBuffer, 160, 70, -128, 0, 0, 1); - viewDrawText(1, GStrings("OF"), 190, 70, -128, 0, 0, 1); - mysnprintf(pBuffer, 40, "%2d", gSecretMgr.Total); - viewDrawText(1, pBuffer, 220, 70, -128, 0, 0, 1); - if (gSecretMgr.Super > 0) - viewDrawText(1, GStrings("TXT_SUPERSECRET"), 160, 100, -128, 2, 1, 1); - } - - - void Draw(double) override - { - drawTextScreenBackground(); - if (gGameOptions.nGameType == 0) - { - DrawCaption(GStrings("TXTB_LEVELSTATS")); - if (bPlayerCheated) - { - auto text = GStrings("TXTB_CHEATED"); - int font = 3; - if (!SmallFont2->CanPrint(text)) font = 0; - viewDrawText(font, text, 160, 32, -128, 0, 1, font == 3); - } - DrawKills(); - DrawSecrets(); - } - else - { - DrawCaption(GStrings("TXTB_FRAGSTATS")); - DrawKills(); - } - int myclock = ticks * 120 / GameTicRate; - if ((myclock & 32)) - { - auto text = GStrings("PRESSKEY"); - int font = 3; - if (!SmallFont2->CanPrint(text)) font = 0; - viewDrawText(font, text, 160, 134, -128, 0, 1, font == 3); - } - } -}; -#endif void GameInterface::LevelCompleted(MapRecord *map, int skill) { -#if 0 - TArray job(1, true); - job[0] = Create(); - sndStartSample(268, 128, -1, false, CHANF_UI); + EndLevel(); Mus_Stop(); - RunScreenJob(job, [=](bool) + + SummaryInfo info{}; + + info.kills = gKillMgr.Kills; + info.maxkills = gKillMgr.TotalKills; + info.secrets = gSecretMgr.Founds; + info.maxsecrets = gSecretMgr.Total; + info.time = gSecretMgr.Super; + info.endofgame = map == nullptr; + + ShowIntermission(currentLevel, map, &info, [=](bool) { soundEngine->StopAllChannels(); - gameaction = ga_nextlevel; + gameaction = map? ga_nextlevel : ga_creditsmenu; + if (!map) gGameOptions.uGameFlags &= ~(GF_AdvanceLevel | GF_EndGame); }); -#else - gameaction = ga_nextlevel; -#endif } @@ -278,40 +147,4 @@ void SerializeGameStats(FSerializer& arc) CSecretMgr gSecretMgr; CKillMgr gKillMgr; -#if 0 -class DBloodLoadScreen : public DScreenJob -{ - const char* pzLoadingScreenText1; - MapRecord* rec; - -public: - DBloodLoadScreen(const char* caption, MapRecord* maprec) : DScreenJob(), rec(maprec) - { - if (gGameOptions.nGameType == 0) pzLoadingScreenText1 = GStrings("TXTB_LLEVEL"); - else pzLoadingScreenText1 = GStrings(FStringf("TXTB_NETGT%d", gGameOptions.nGameType)); - } - - void Draw(double) override - { - twod->ClearScreen(); - drawTextScreenBackground(); - DrawCaption(pzLoadingScreenText1); - viewDrawText(1, rec->DisplayName(), 160, 50, -128, 0, 1, 1); - - auto text = GStrings("TXTB_PLSWAIT"); - int font = 3; - if (!SmallFont2->CanPrint(text)) font = 0; - - viewDrawText(font, GStrings("TXTB_PLSWAIT"), 160, 134, -128, 0, 1, font == 3); - } -}; - -void loadscreen(const char *caption, MapRecord* rec, CompletionFunc func) -{ - TArray job(1, true); - job[0] = Create(caption, rec); - RunScreenJob(job, func); -} -#endif - END_BLD_NS diff --git a/source/games/blood/src/levels.cpp b/source/games/blood/src/levels.cpp index a64fac507..b977578ce 100644 --- a/source/games/blood/src/levels.cpp +++ b/source/games/blood/src/levels.cpp @@ -38,10 +38,7 @@ GAMEOPTIONS gSingleGameOptions = { 0, 2, 0, 0, 0, 0, 0, 0, 2, 3600, 1800, 1800, 7200 }; -EPISODEINFO gEpisodeInfo[kMaxEpisodes+1]; - int gSkill = 2; -int gEpisodeCount; int gNextLevel; // fixme: let this contain a full level number. char BloodIniFile[BMAX_PATH] = "BLOOD.INI"; @@ -142,39 +139,45 @@ static const char* DefFile(void) return userConfig.DefaultCon.IsNotEmpty() ? userConfig.DefaultCon.GetChars() : "blood.ini"; } +static FString cleanPath(const char* pth) +{ + FString path = pth; + FixPathSeperator(path); + if (FileExists(path)) return path; + if (path.Len() > 3 && path[1] == ':' && isalpha(path[0]) && path[2] == '/') + { + return path.Mid(3); + } + return path; +} + void levelLoadDefaults(void) { char buffer[64]; char buffer2[16]; + + int cutALevel = 0; + levelInitINI(DefFile()); - memset(gEpisodeInfo, 0, sizeof(gEpisodeInfo)); - quoteMgr.InitializeQuote(MUS_INTRO, "PESTIS.MID"); int i; for (i = 0; i < kMaxEpisodes; i++) { sprintf(buffer, "Episode%d", i+1); if (!BloodINI->SectionExists(buffer)) break; - EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[i]; - auto ep_str = BloodINI->GetKeyString(buffer, "Title", buffer); + CutsceneDef &csB = volumeList[i].outro; + auto ep_str = BloodINI->GetKeyString(buffer, "Title", buffer); volumeList[i].name = ep_str; - strncpy(pEpisodeInfo->cutsceneAName, BloodINI->GetKeyString(buffer, "CutSceneA", ""), BMAX_PATH); - pEpisodeInfo->cutsceneAWave = BloodINI->GetKeyInt(buffer, "CutWavA", -1); - if (pEpisodeInfo->cutsceneAWave == 0) - strncpy(pEpisodeInfo->cutsceneASound, BloodINI->GetKeyString(buffer, "CutWavA", ""), BMAX_PATH); - else - pEpisodeInfo->cutsceneASound[0] = 0; - strncpy(pEpisodeInfo->cutsceneBName, BloodINI->GetKeyString(buffer, "CutSceneB", ""), BMAX_PATH); - pEpisodeInfo->cutsceneBWave = BloodINI->GetKeyInt(buffer, "CutWavB", -1); - if (pEpisodeInfo->cutsceneBWave == 0) - strncpy(pEpisodeInfo->cutsceneBSound, BloodINI->GetKeyString(buffer, "CutWavB", ""), BMAX_PATH); - else - pEpisodeInfo->cutsceneBSound[0] = 0; - pEpisodeInfo->bloodbath = BloodINI->GetKeyInt(buffer, "BloodBathOnly", 0); - pEpisodeInfo->cutALevel = BloodINI->GetKeyInt(buffer, "CutSceneALevel", 0); - if (pEpisodeInfo->cutALevel > 0) - pEpisodeInfo->cutALevel--; + csB.video = cleanPath(BloodINI->GetKeyString(buffer, "CutSceneB", "")); + csB.sound = soundEngine->FindSoundByResID(BloodINI->GetKeyInt(buffer, "CutWavB", -1) + 0x40000000); + if (csB.sound == 0) + 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; + int j; for (j = 0; j < kMaxLevels; j++) { @@ -189,10 +192,16 @@ void levelLoadDefaults(void) pLevelInfo->labelName = pMap; pLevelInfo->fileName.Format("%s.map", pMap); levelLoadMapInfo(BloodINI, pLevelInfo, pMap, i, j); + if (j == cutALevel) + { + CutsceneDef& csA = pLevelInfo->intro; + csA.video = cleanPath(BloodINI->GetKeyString(buffer, "CutSceneA", "")); + csA.sound = soundEngine->FindSoundByResID(BloodINI->GetKeyInt(buffer, "CutWavA", -1) + 0x40000000); + if (csA.sound == 0) + csA.sound = soundEngine->FindSound(cleanPath(BloodINI->GetKeyString(buffer, "CutWavA", ""))); + } } - pEpisodeInfo->nLevels = j; } - gEpisodeCount = i; } void levelGetNextLevels(int *pnEndingA, int *pnEndingB) @@ -208,20 +217,17 @@ void levelGetNextLevels(int *pnEndingA, int *pnEndingB) *pnEndingB = nEndingB; } -void levelEndLevel(int arg) +void levelEndLevel(int secret) { int nEndingA, nEndingB; auto episode = volfromlevelnum(currentLevel->levelNumber); - EPISODEINFO *pEpisodeInfo = &gEpisodeInfo[episode]; gGameOptions.uGameFlags |= GF_AdvanceLevel; levelGetNextLevels(&nEndingA, &nEndingB); - switch (arg) + switch (secret) { case 0: if (nEndingA == -1) { - if (pEpisodeInfo->cutsceneBName[0]) - gGameOptions.uGameFlags |= GF_PlayCutscene; gGameOptions.uGameFlags |= GF_EndGame; } else @@ -230,16 +236,7 @@ void levelEndLevel(int arg) case 1: if (nEndingB == -1) { - if (episode + 1 < gEpisodeCount) - { - if (pEpisodeInfo->cutsceneBName[0]) - gGameOptions.uGameFlags |= GF_PlayCutscene; - gGameOptions.uGameFlags |= GF_EndGame; - } - else - { - gGameOptions.uGameFlags |= GF_AdvanceLevel; - } + gGameOptions.uGameFlags |= GF_EndGame; } else gNextLevel = nEndingB; diff --git a/source/games/blood/src/levels.h b/source/games/blood/src/levels.h index eb707c396..6f72284bf 100644 --- a/source/games/blood/src/levels.h +++ b/source/games/blood/src/levels.h @@ -67,38 +67,16 @@ struct GAMEOPTIONS { #pragma pack(pop) -enum { - MUS_INTRO = 0, - MUS_LOADING = 1, -}; - -struct EPISODEINFO -{ - int nLevels; - unsigned int bloodbath : 1; - unsigned int cutALevel : 4; - char cutsceneAName[BMAX_PATH]; - char cutsceneBName[BMAX_PATH]; - int cutsceneAWave; - int cutsceneBWave; - char cutsceneASound[BMAX_PATH]; - char cutsceneBSound[BMAX_PATH]; -}; - -extern EPISODEINFO gEpisodeInfo[]; extern GAMEOPTIONS gSingleGameOptions; extern GAMEOPTIONS gGameOptions; extern int gSkill; extern char BloodIniFile[]; extern bool bINIOverride; -extern int gEpisodeCount; extern int gNextLevel; extern bool gGameStarted; void levelInitINI(const char *pzIni); void levelOverrideINI(const char *pzIni); -void levelPlayIntroScene(int nEpisode, CompletionFunc completion); -void levelPlayEndScene(int nEpisode, CompletionFunc completion); void levelSetupSecret(int nCount); void levelTriggerSecret(int nSecret); void CheckSectionAbend(const char *pzSection); diff --git a/source/games/blood/src/messages.cpp b/source/games/blood/src/messages.cpp index 3fc2fd938..aaf741687 100644 --- a/source/games/blood/src/messages.cpp +++ b/source/games/blood/src/messages.cpp @@ -267,10 +267,8 @@ static int parseArgs(char *pzArgs, int *nArg1, int *nArg2) { if (!nArg1 || !nArg2 || strlen(pzArgs) < 3) return -1; - *nArg1 = pzArgs[0] - '0' - 1; + *nArg1 = pzArgs[0] - '0'; *nArg2 = (pzArgs[1] - '0')*10+(pzArgs[2]-'0') - 1; - *nArg1 = ClipRange(*nArg1, 0, gEpisodeCount-1); - *nArg2 = ClipRange(*nArg2, 0, gEpisodeInfo[*nArg1].nLevels-1); return 2; } diff --git a/source/games/blood/src/namelist.h b/source/games/blood/src/namelist.h index b93e997e4..89ed1a7b4 100644 --- a/source/games/blood/src/namelist.h +++ b/source/games/blood/src/namelist.h @@ -3,6 +3,7 @@ x(MENUBAR, 2038) x(BackTile, 253) x(Monolithscreen, 2050) x(GTIScreen, 2052) +x(TitleScreen, 2518) x(CrosshairTile, 2319) x(LoadScreen, 2049) diff --git a/source/games/blood/src/player.cpp b/source/games/blood/src/player.cpp index 3fd2f6aae..70a9dafcc 100644 --- a/source/games/blood/src/player.cpp +++ b/source/games/blood/src/player.cpp @@ -400,10 +400,6 @@ void powerupClear(PLAYER *pPlayer) } } -void powerupInit(void) -{ -} - int packItemToPowerup(int nPack) { int nPowerUp = -1; diff --git a/source/games/blood/src/player.h b/source/games/blood/src/player.h index 53f4dc4a3..c5f72d49d 100644 --- a/source/games/blood/src/player.h +++ b/source/games/blood/src/player.h @@ -241,7 +241,6 @@ void powerupDeactivate(PLAYER *pPlayer, int nPowerUp); void powerupSetState(PLAYER *pPlayer, int nPowerUp, char bState); void powerupProcess(PLAYER *pPlayer); void powerupClear(PLAYER *pPlayer); -void powerupInit(void); int packItemToPowerup(int nPack); int powerupToPackItem(int nPowerUp); char packAddItem(PLAYER *pPlayer, unsigned int nPack); diff --git a/source/games/duke/src/sounds.h b/source/games/duke/src/sounds.h index 4862ee28c..c0c894cf1 100644 --- a/source/games/duke/src/sounds.h +++ b/source/games/duke/src/sounds.h @@ -63,7 +63,6 @@ inline bool S_IsSoundValid(int num) void S_PlayRRMusic(int newTrack = -1); void S_PlayBonusMusic(); void S_PlayLevelMusic(MapRecord* mi); -void S_PlaySpecialMusic(unsigned int); void S_ContinueLevelMusic(void); // Placeholders. diff --git a/wadsrc/static/filter/blood/engine/engine.def b/wadsrc/static/filter/blood/engine/engine.def index e4843725b..5dbab9f07 100644 --- a/wadsrc/static/filter/blood/engine/engine.def +++ b/wadsrc/static/filter/blood/engine/engine.def @@ -59,3 +59,18 @@ tilefromtexture 9277 { file "tiles/9277.png" } tilefromtexture 9278 { file "tiles/9278.png" } tilefromtexture 9279 { file "tiles/9279.png" } tilefromtexture 9280 { file "tiles/9280.png" } + +// Cutscene definitions for Blood + +definecutscene intro +{ + function BloodCutscenes.BuildIntro +} + +definecutscene loading +{ + function BloodCutscenes.BuildLoading +} + +definecutscene summary BloodCutscenes.BuildSPSummary +definecutscene mpsummary BloodCutscenes.BuildMPSummary diff --git a/wadsrc/static/filter/redneck/engine/engine.def b/wadsrc/static/filter/redneck/engine/engine.def index 104188d77..1ec0d03d2 100644 --- a/wadsrc/static/filter/redneck/engine/engine.def +++ b/wadsrc/static/filter/redneck/engine/engine.def @@ -5,8 +5,7 @@ definecutscene intro function RRCutscenes.BuildIntro } -// The 'E1End' cutscene is tied to the level as it is not an end-of-game event. -definecutscene map e1l7 +definecutscene episode 1 { outro { diff --git a/wadsrc/static/zscript.txt b/wadsrc/static/zscript.txt index 087a11145..32b768850 100644 --- a/wadsrc/static/zscript.txt +++ b/wadsrc/static/zscript.txt @@ -33,6 +33,8 @@ version "4.3" #include "zscript/games/duke/ui/screens.zs" #include "zscript/games/duke/ui/cutscenes.zs" #include "zscript/games/duke/ui/menu.zs" +#include "zscript/games/blood/bloodgame.zs" #include "zscript/games/blood/ui/menu.zs" +#include "zscript/games/blood/ui/screens.zs" #include "zscript/games/sw/ui/menu.zs" #include "zscript/games/exhumed/ui/menu.zs" diff --git a/wadsrc/static/zscript/games/blood/bloodgame.zs b/wadsrc/static/zscript/games/blood/bloodgame.zs new file mode 100644 index 000000000..839b2f6cb --- /dev/null +++ b/wadsrc/static/zscript/games/blood/bloodgame.zs @@ -0,0 +1,8 @@ +// contains all global Blood definitions +struct Blood native +{ + native static void PlayIntroMusic(); + native static bool OriginalLoadScreen(); // doing it generically would necessitate exporting the tile manage which we do not want. + native static void sndStartSample(int resid, int volume, int channel, bool loop = false, int chanflags = 0); + native static void sndStartSampleNamed(String sname, int volume, int channel); +} diff --git a/wadsrc/static/zscript/games/blood/ui/menu.zs b/wadsrc/static/zscript/games/blood/ui/menu.zs index dbcb2a947..72664e9d6 100644 --- a/wadsrc/static/zscript/games/blood/ui/menu.zs +++ b/wadsrc/static/zscript/games/blood/ui/menu.zs @@ -9,25 +9,7 @@ class BloodMenuDelegate : RazeMenuDelegate { override int DrawCaption(String title, Font fnt, int y, bool drawit) { - let font = generic_ui? NewConsoleFont : BigFont; // this ignores the passed font intentionally. - let texid = TexMan.CheckForTexture("MENUBAR"); - let texsize = TexMan.GetScaledSize(texid); - let fonth = font.GetGlyphHeight("A"); - if (drawit) - { - int width = font.StringWidth(title); - if (texid.isValid()) - { - double scalex = 1.; // Expand the box if the text is longer - if (texsize.X - 10 < width) scalex = width / (texsize.X - 10); - screen.DrawTexture(texid, false, 160, 20, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_CenterOffsetRel, true, DTA_ScaleX, scalex); - } - screen.DrawText(font, Font.CR_UNDEFINED, 160 - width / 2, 20 - fonth / 2, title, DTA_FullscreenScale, FSMode_Fit320x200Top); - } - double fx, fy, fw, fh; - [fx, fy, fw, fh] = Screen.GetFullscreenRect(320, 200, FSMode_ScaleToFit43Top); - int h = texid.isValid() && texsize.Y < 40? texsize.Y : fonth; - return int((y+h) * fh / 200); // This must be the covered height of the header in true pixels. + return BloodScreen.DrawCaption(title, y, drawit); // this ignores the passed font intentionally. } override bool DrawSelector(ListMenuDescriptor desc) diff --git a/wadsrc/static/zscript/games/blood/ui/screens.zs b/wadsrc/static/zscript/games/blood/ui/screens.zs new file mode 100644 index 000000000..e368722cd --- /dev/null +++ b/wadsrc/static/zscript/games/blood/ui/screens.zs @@ -0,0 +1,327 @@ + + +class BloodIntroImage : ImageScreen +{ + bool mus; + + ScreenJob Init(String texture, int flags = 0, bool withmusic = false) + { + Super.InitNamed(texture, flags); + mus = withmusic; + return self; + } + + override void Start() + { + Blood.sndStartSampleNamed("THUNDER2", 128, -1); + if (mus) Blood.PlayIntroMusic(); // this is script defined. + } +} + +struct BloodScreen +{ + enum EConstants + { + kLoadScreenWideBackWidth = 256, + kLoadScreenWideSideWidth = 128, + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void DrawBackground() + { + if (Blood.OriginalLoadScreen() && Screen.GetAspectRatio() >= 1.34) + { + int width = screen.GetWidth() * 240 / screen.GetHeight(); + int nCount = (width + kLoadScreenWideBackWidth - 1) / kLoadScreenWideBackWidth; + for (int i = 0; i < nCount; i++) + { + Screen.DrawTexture(TexMan.CheckForTexture("LoadScreenWideBack"), false, (i * kLoadScreenWideBackWidth), 0, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true); + } + Screen.DrawTexture(TexMan.CheckForTexture("LoadScreenWideLeft"), false, 0, 0, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true, DTA_TopLeft, true); + let texid = TexMan.CheckForTexture("LoadScreenWideRight"); + let size = TexMan.GetScaledSize(texid); + Screen.DrawTexture(texid, false, width - size.x, 0, DTA_TopLeft, true, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true); + texid = TexMan.CheckForTexture("LoadScreenWideMiddle"); + size = TexMan.GetScaledSize(texid); + Screen.DrawTexture(texid, false, (width - size.x) / 2, 0, DTA_TopLeft, true, DTA_VirtualWidth, width, DTA_VirtualHeight, 200, DTA_KeepRatio, true); + } + else + { + Screen.DrawTexture(TexMan.CheckForTexture("LoadScreen"), false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43); + } + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static int DrawCaption(String title, int y, bool drawit) + { + let font = generic_ui? NewConsoleFont : BigFont; + let texid = TexMan.CheckForTexture("MENUBAR"); + let texsize = TexMan.GetScaledSize(texid); + let fonth = font.GetGlyphHeight("A"); + if (drawit) + { + int width = font.StringWidth(title); + if (texid.isValid()) + { + double scalex = 1.; // Expand the box if the text is longer + if (texsize.X - 10 < width) scalex = width / (texsize.X - 10); + screen.DrawTexture(texid, false, 160, 20, DTA_FullscreenScale, FSMode_Fit320x200Top, DTA_CenterOffsetRel, true, DTA_ScaleX, scalex); + } + screen.DrawText(font, Font.CR_UNDEFINED, 160 - width / 2, 20 - fonth / 2, title, DTA_FullscreenScale, FSMode_Fit320x200Top); + } + double fx, fy, fw, fh; + [fx, fy, fw, fh] = Screen.GetFullscreenRect(320, 200, FSMode_ScaleToFit43Top); + int h = texid.isValid() && texsize.Y < 40? texsize.Y : fonth; + return int((y+h) * fh / 200); // This must be the covered height of the header in true pixels. + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void DrawText(Font pFont, String pString, int x, int y, int position = 0, int nShade = 0, int nPalette = 0, bool shadow = true, float alpha = 1.) + { + if (position > 0) x -= pFont.StringWidth(pString) * position / 2; + if (shadow) Screen.DrawText(pFont, Font.CR_UNDEFINED, x+1, y+1, pString, DTA_FullscreenScale, FSMode_Fit320x200, DTA_Color, 0xff000000, DTA_Alpha, 0.5); + Screen.DrawText(pFont, Font.CR_UNDEFINED, x, y, pString, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, Translation.MakeID(Translation_Remap, nPalette), + DTA_Color, Raze.shadeToLight(nShade), DTA_Alpha, alpha); + + } + +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class BloodSummaryScreen : SummaryScreenBase +{ + ScreenJob Init(MapRecord map, SummaryInfo info) + { + Super.Init(fadein|fadeout); + SetParameters(map, info); + return self; + } + + override void Start() + { + Blood.sndStartSample(268, 128, -1, false, CHANF_UI); + } + + void DrawKills() + { + String pBuffer; + BloodScreen.DrawText(BigFont, Stringtable.Localize("$KILLS") .. ":", 75, 50); + pBuffer = String.Format("%2d", stats.Kills); + BloodScreen.DrawText(BigFont, pBuffer, 160, 50); + BloodScreen.DrawText(BigFont, "$OF", 190, 50); + pBuffer = String.Format( "%2d", stats.MaxKills); + BloodScreen.DrawText(BigFont, pBuffer, 220, 50); + } + + void DrawSecrets() + { + String pBuffer; + BloodScreen.DrawText(BigFont, StringTable.Localize("$TXT_SECRETS") .. ":", 75, 70); + pBuffer = String.Format( "%2d", stats.secrets); + BloodScreen.DrawText(BigFont, pBuffer, 160, 70); + BloodScreen.DrawText(BigFont, "$OF", 190, 70); + pBuffer = String.Format( "%2d", stats.maxsecrets); + BloodScreen.DrawText(BigFont, pBuffer, 220, 70); + if (stats.SuperSecrets > 0) BloodScreen.DrawText(BigFont, "$TXT_SUPERSECRET", 160, 100, 1, 0, 2); + } + + override void Draw(double sm) + { + BloodScreen.DrawBackground(); + BloodScreen.DrawCaption("$TXTB_LEVELSTATS", 0, true); + if (stats.cheated) + { + let text = StringTable.Localize("$TXTB_CHEATED"); + let font = SmallFont2.CanPrint(text)? SmallFont2 : SmallFont; + BloodScreen.DrawText(font, text, 160, 32, 1, shadow:font == SmallFont2); + } + DrawKills(); + DrawSecrets(); + + int myclock = ticks * 120 / GameTicRate; + if ((myclock & 32)) + { + let text = StringTable.Localize("$PRESSKEY"); + let font = SmallFont2.CanPrint(text)? SmallFont2 : SmallFont; + BloodScreen.DrawText(font, text, 160, 134, 1, shadow:font == SmallFont2); + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class BloodMPSummaryScreen : SkippableScreenJob +{ + int numplayers; + ScreenJob Init(int numplayer) + { + Super.Init(fadein|fadeout); + numplayers = numplayer; + return self; + } + + override void Start() + { + Blood.sndStartSample(268, 128, -1, false, CHANF_UI); + } + + void DrawKills() + { + String pBuffer; + BloodScreen.DrawText(SmallFont2, "#", 85, 35); + BloodScreen.DrawText(SmallFont2, "$NAME", 100, 35); + BloodScreen.DrawText(SmallFont2, "$FRAGS", 210, 35); + + for (int i = 0; i < numplayers; i++) + { + pBuffer = String.Format( "%-2d", i); + BloodScreen.DrawText(SmallFont2, pBuffer, 85, 50 + 8 * i); + pBuffer = String.Format( "%s", Raze.PlayerName(i)); + BloodScreen.DrawText(SmallFont2, pBuffer, 100, 50 + 8 * i); + pBuffer = String.Format( "%d", Raze.playerFrags(i, -1)); + BloodScreen.DrawText(SmallFont2, pBuffer, 210, 50 + 8 * i); + } + } + + override void Draw(double sr) + { + BloodScreen.DrawBackground(); + BloodScreen.DrawCaption("$TXTB_FRAGSTATS", 0, true); + DrawKills(); + + int myclock = ticks * 120 / GameTicRate; + if ((myclock & 32)) + { + let text = StringTable.Localize("$PRESSKEY"); + let font = SmallFont2.CanPrint(text)? SmallFont2 : SmallFont; + BloodScreen.DrawText(font, text, 160, 134, 1, shadow:font == SmallFont2); + } + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class BloodLoadScreen : ScreenJob +{ + String loadtext; + MapRecord rec; + + ScreenJob Init(MapRecord maprec) + { + Super.Init(fadein); + + /*if (gGameOptions.nGameType == 0)*/ loadtext = "$TXTB_LLEVEL"; + //else loadText = String.Format("$TXTB_NETGT%d", gGameOptions.nGameType)); + + rec = maprec; + return self; + } + + override void Draw(double sr) + { + BloodScreen.DrawBackground(); + BloodScreen.DrawCaption(loadtext, 0, true); + BloodScreen.DrawText(BigFont, rec.DisplayName(), 160, 50, 1); + + let text = StringTable.Localize("$TXTB_PLSWAIT"); + let font = SmallFont2.CanPrint(text)? SmallFont2 : SmallFont; + BloodScreen.DrawText(font, text, 160, 134, 1, shadow:font == SmallFont2); + } +} + +//--------------------------------------------------------------------------- +// +// +// +//--------------------------------------------------------------------------- + +class BloodCutscenes +{ + static void BuildIntro(ScreenJobRunner runner) + { + if (!userConfig.nologo) + { + Array soundinfo; + if (Wads.CheckNumForFullName("logo.smk") != -1) + { + String s = "logo.wav"; // sound name must be converted at run-time, not compile time! + runner.Append(MoviePlayerJob.CreateWithSound("logo.smk", s, MoviePlayer.FIXEDVIEWPORT)); + } + else + { + runner.Append(new("BloodIntroImage").Init("MonolithScreen", ScreenJob.fadein|ScreenJob.fadeout)); + } + if (Wads.CheckNumForFullName("gti.smk") != -1) + { + String s = "gt.wav"; + runner.Append(MoviePlayerJob.CreateWithSound("gti.smk", s, MoviePlayer.FIXEDVIEWPORT)); + } + else + { + runner.Append(new("BloodIntroImage").Init("GTIScreen", ScreenJob.fadein|ScreenJob.fadeout)); + } + } + runner.Append(new("BloodIntroImage").Init("Titlescreen", ScreenJob.fadein, true)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildMPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + runner.Append(new("BloodMPSummaryScreen").Init(stats.playercount)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildSPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) + { + runner.Append(new("BloodSummaryScreen").Init(map, stats)); + } + + //--------------------------------------------------------------------------- + // + // + // + //--------------------------------------------------------------------------- + + static void BuildLoading(ScreenJobRunner runner, MapRecord map) + { + runner.Append(new("BloodLoadScreen").Init(map)); + } +}