diff --git a/source/CMakeLists.txt b/source/CMakeLists.txt index 8a3a38159..32b012e0b 100644 --- a/source/CMakeLists.txt +++ b/source/CMakeLists.txt @@ -614,6 +614,7 @@ file( GLOB HEADER_FILES common/audio/music/*.h* common/2d/*.h common/console/*.h + common/cutscenes/*.h common/utility/*.h common/engine/*.h common/menu/*.h @@ -993,8 +994,6 @@ set (PCH_SOURCES build/src/mdsprite.cpp build/src/polymost.cpp - core/movie/playmve.cpp - core/movie/movieplayer.cpp core/automap.cpp core/cheats.cpp core/cheathandler.cpp @@ -1136,6 +1135,9 @@ set (PCH_SOURCES common/console/c_notifybufferbase.cpp common/console/c_tabcomplete.cpp common/console/c_expr.cpp + common/cutscenes/playmve.cpp + common/cutscenes/movieplayer.cpp + common/cutscenes/screenjob.cpp common/utility/engineerrors.cpp common/utility/i_module.cpp common/utility/m_alloc.cpp @@ -1522,6 +1524,7 @@ source_group("Common\\Console" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/ source_group("Common\\Utility" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/utility/.+") source_group("Common\\Engine" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/engine/.+") source_group("Common\\2D" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/2d/.+") +source_group("Common\\Cutscenes" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/cutscenes/.+") source_group("Common\\Objects" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/objects/.+") source_group("Common\\Menu" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/menu/.+") source_group("Common\\Fonts" REGULAR_EXPRESSION "^${CMAKE_CURRENT_SOURCE_DIR}/common/fonts/.+") diff --git a/source/common/audio/music/music.cpp b/source/common/audio/music/music.cpp index f81b2396c..bc0359cb8 100644 --- a/source/common/audio/music/music.cpp +++ b/source/common/audio/music/music.cpp @@ -102,6 +102,11 @@ void S_SetMusicCallbacks(MusicCallbacks* cb) if (mus_cb.OpenMusic == nullptr) mus_cb.OpenMusic = DefaultOpenMusic; // without this we are dead in the water. } +int MusicEnabled() // int return is for scripting +{ + return mus_enabled && !nomusic; +} + //========================================================================== // // @@ -629,7 +634,7 @@ static void CheckReplayGain(const char *musicname, EMidiDevice playertype, const bool S_ChangeMusic(const char* musicname, int order, bool looping, bool force) { - if (nomusic) return false; // skip the entire procedure if music is globally disabled. + if (!MusicEnabled()) return false; // skip the entire procedure if music is globally disabled. if (!force && PlayList.GetNumSongs()) { // Don't change if a playlist is active diff --git a/source/common/audio/music/s_music.h b/source/common/audio/music/s_music.h index a76189fc1..fc1b86ca0 100644 --- a/source/common/audio/music/s_music.h +++ b/source/common/audio/music/s_music.h @@ -11,6 +11,7 @@ class FileReader; class SoundStream; +int MusicEnabled(); typedef bool(*StreamCallback)(SoundStream* stream, void* buff, int len, void* userdata); SoundStream *S_CreateCustomStream(size_t size, int samplerate, int numchannels, StreamCallback cb, void *userdata); void S_StopCustomStream(SoundStream* stream); diff --git a/source/common/audio/sound/s_sound.cpp b/source/common/audio/sound/s_sound.cpp index 8ec5fc76b..f6acbb885 100644 --- a/source/common/audio/sound/s_sound.cpp +++ b/source/common/audio/sound/s_sound.cpp @@ -43,7 +43,14 @@ #include "s_music.h" #include "m_random.h" #include "printf.h" +#include "c_cvars.h" +CVARD(Bool, snd_enabled, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG, "enables/disables sound effects") + +int SoundEnabled() +{ + return snd_enabled && !nosound && !nosfx; +} enum { @@ -382,7 +389,7 @@ FSoundChan *SoundEngine::StartSound(int type, const void *source, FVector3 pos, vel; FRolloffInfo *rolloff; - if (sound_id <= 0 || volume <= 0 || nosfx || nosound || blockNewSounds) + if (sound_id <= 0 || volume <= 0 || nosfx || !SoundEnabled() || blockNewSounds) return NULL; // prevent crashes. diff --git a/source/common/audio/sound/s_soundinternal.h b/source/common/audio/sound/s_soundinternal.h index df6943c5d..d35cb524a 100644 --- a/source/common/audio/sound/s_soundinternal.h +++ b/source/common/audio/sound/s_soundinternal.h @@ -429,3 +429,5 @@ inline int S_FindSound(const char* name) { return soundEngine->FindSound(name); } + +int SoundEnabled(); diff --git a/source/core/movie/movieplayer.cpp b/source/common/cutscenes/movieplayer.cpp similarity index 97% rename from source/core/movie/movieplayer.cpp rename to source/common/cutscenes/movieplayer.cpp index 579c2d84c..e2e2ec24c 100644 --- a/source/core/movie/movieplayer.cpp +++ b/source/common/cutscenes/movieplayer.cpp @@ -32,7 +32,6 @@ */ #include "types.h" -#include "build.h" #include "screenjob.h" #include "i_time.h" #include "v_2ddrawer.h" @@ -41,16 +40,13 @@ #include "s_soundinternal.h" #include "animtexture.h" #include "gamestate.h" -#include "razemenu.h" -#include "raze_sound.h" #include "SmackerDecoder.h" -#include "movie/playmve.h" -#include "gamecontrol.h" +#include "playmve.h" #include #include -#include "raze_music.h" +#include "filesystem.h" #include "vm.h" - +#include "printf.h" class MoviePlayer { @@ -143,7 +139,7 @@ public: int sound = animSnd[i+1]; if (sound == -1) soundEngine->StopAllChannels(); - else if (SoundEnabled()) + else soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound? CHANF_UI : CHANF_NONE, sound, 1.f, ATTN_NONE); } } @@ -419,7 +415,7 @@ public: { if (soundtrack > 0) { - Mus_Play(fileSystem.GetFileFullName(soundtrack, false), false); + S_ChangeMusic(fileSystem.GetFileFullName(soundtrack, false), 0, false); } animtex.SetSize(AnimTexture::YUV, width, height); } @@ -462,7 +458,7 @@ public: int sound = animSnd[i + 1]; if (sound == -1) soundEngine->StopAllChannels(); - else if (SoundEnabled()) + else soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound ? CHANF_UI : CHANF_NONE, sound, 1.f, ATTN_NONE); } } @@ -475,7 +471,7 @@ public: void Stop() { - Mus_Stop(); + S_StopMusic(true); bool nostopsound = (flags & NOSOUNDCUTOFF); if (!nostopsound) soundEngine->StopAllChannels(); } @@ -624,7 +620,7 @@ public: Smacker_GetPalette(hSMK, palette); Smacker_GetFrame(hSMK, pFrame.Data()); animtex.SetFrame(palette, pFrame.Data()); - if (numAudioTracks) + if (numAudioTracks && SoundEnabled()) { auto read = Smacker_GetAudioData(hSMK, 0, (int16_t*)audioBuffer.Data()); if (adata.inf.bitsPerSample == 8) copy8bitSamples(read); @@ -648,7 +644,7 @@ public: int sound = animSnd[i + 1]; if (sound == -1) soundEngine->StopAllChannels(); - else if (SoundEnabled()) + else soundEngine->StartSound(SOURCE_None, nullptr, nullptr, CHAN_AUTO, nostopsound ? CHANF_UI : CHANF_NONE, sound, 1.f, ATTN_NONE); } } @@ -756,7 +752,10 @@ MoviePlayer* OpenMovie(const char* filename, TArray& ans, const int* framet delete anm; return nullptr; } - anm->soundtrack = LookupMusic(filename, true); + // VPX files have no sound track, so look for a same-named sound file with a known extension as the soundtrack to be played. + static const char* knownSoundExts[] = { "OGG", "FLAC", "MP3", "OPUS", "WAV" }; + FString name = StripExtension(filename); + anm->soundtrack = fileSystem.FindFileWithExtensions(name, knownSoundExts, countof(knownSoundExts)); return anm; } // add more formats here. diff --git a/source/core/movie/playmve.cpp b/source/common/cutscenes/playmve.cpp similarity index 100% rename from source/core/movie/playmve.cpp rename to source/common/cutscenes/playmve.cpp diff --git a/source/core/movie/playmve.h b/source/common/cutscenes/playmve.h similarity index 100% rename from source/core/movie/playmve.h rename to source/common/cutscenes/playmve.h diff --git a/source/common/cutscenes/screenjob.cpp b/source/common/cutscenes/screenjob.cpp new file mode 100644 index 000000000..305710eb5 --- /dev/null +++ b/source/common/cutscenes/screenjob.cpp @@ -0,0 +1,364 @@ +/* +** screenjob.cpp +** +** Generic cutscene display +** +**--------------------------------------------------------------------------- +** Copyright 2020 Christoph Oelckers +** All rights reserved. +** +** Redistribution and use in source and binary forms, with or without +** modification, are permitted provided that the following conditions +** are met: +** +** 1. Redistributions of source code must retain the above copyright +** notice, this list of conditions and the following disclaimer. +** 2. Redistributions in binary form must reproduce the above copyright +** notice, this list of conditions and the following disclaimer in the +** documentation and/or other materials provided with the distribution. +** 3. The name of the author may not be used to endorse or promote products +** derived from this software without specific prior written permission. +** +** THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR +** IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES +** OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. +** IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +** INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +** NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +** THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +**--------------------------------------------------------------------------- +** +*/ + +#include "types.h" +#include "screenjob.h" +#include "i_time.h" +#include "v_2ddrawer.h" +#include "animlib.h" +#include "v_draw.h" +#include "s_soundinternal.h" +#include "animtexture.h" +#include "gamestate.h" +#include "vm.h" +#include "c_bind.h" +#include "c_console.h" +#include "gamestate.h" +#include "printf.h" +#include "c_dispatch.h" +#include "s_music.h" +#include "m_argv.h" + +DObject* runner; +PClass* runnerclass; +PType* runnerclasstype; +CompletionFunc completion; +static int ticks; +int intermissiondelay; + +//============================================================================= +// +// +// +//============================================================================= + +void Job_Init() +{ + static bool done = false; + if (!done) + { + done = true; + GC::AddMarkerFunc([] { GC::Mark(runner); }); + } + runnerclass = PClass::FindClass("ScreenJobRunner"); + if (!runnerclass) I_FatalError("ScreenJobRunner not defined"); + runnerclasstype = NewPointer(runnerclass); +} + +//============================================================================= +// +// +// +//============================================================================= + +VMFunction* LookupFunction(const char* qname, bool validate) +{ + size_t p = strcspn(qname, "."); + if (p == 0) I_Error("Call to undefined function %s", qname); + FString clsname(qname, p); + FString funcname = qname + p + 1; + + auto func = PClass::FindFunction(clsname, funcname); + if (func == nullptr) I_Error("Call to undefined function %s", qname); + if (validate) + { + // these conditions must be met by all functions for this interface. + if (func->Proto->ReturnTypes.Size() != 0) I_Error("Bad cutscene function %s. Return value not allowed", qname); + if (func->ImplicitArgs != 0) I_Error("Bad cutscene function %s. Must be static", qname); + } + return func; +} + +//============================================================================= +// +// +// +//============================================================================= + +void CallCreateFunction(const char* qname, DObject* runner) +{ + auto func = LookupFunction(qname); + if (func->Proto->ArgumentTypes.Size() != 1) I_Error("Bad cutscene function %s. Must receive precisely one argument.", qname); + if (func->Proto->ArgumentTypes[0] != runnerclasstype) I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference.", qname); + VMValue val = runner; + VMCall(func, &val, 1, nullptr, 0); +} + +//============================================================================= +// +// +// +//============================================================================= + +DObject* CreateRunner(bool clearbefore) +{ + auto obj = runnerclass->CreateNew(); + auto func = LookupFunction("ScreenJobRunner.Init", false); + VMValue val[3] = { obj, clearbefore, false }; + VMCall(func, val, 3, nullptr, 0); + return obj; +} + +//============================================================================= +// +// +// +//============================================================================= + +void AddGenericVideo(DObject* runner, const FString& fn, int soundid, int fps) +{ + auto obj = runnerclass->CreateNew(); + auto func = LookupFunction("ScreenJobRunner.AddGenericVideo", false); + VMValue val[] = { runner, &fn, soundid, fps }; + VMCall(func, val, 4, nullptr, 0); +} + +//============================================================================= +// +// +// +//============================================================================= + +int CutsceneDef::GetSound() +{ + int id; + if (soundName.IsNotEmpty()) id = soundEngine->FindSound(soundName); + if (id <= 0) id = soundEngine->FindSoundByResID(soundID); + return id; +} + +void CutsceneDef::Create(DObject* runner) +{ + if (function.IsNotEmpty()) + { + CallCreateFunction(function, runner); + } + else if (video.IsNotEmpty()) + { + AddGenericVideo(runner, video, GetSound(), framespersec); + } +} + +//============================================================================= +// +// +// +//============================================================================= + +void DeleteScreenJob() +{ + if (runner) runner->Destroy(); + runner = nullptr; +} + +void EndScreenJob() +{ + DeleteScreenJob(); + if (completion) completion(false); + completion = nullptr; +} + + +//============================================================================= +// +// +// +//============================================================================= + +bool ScreenJobResponder(event_t* ev) +{ + if (ev->type == EV_KeyDown) + { + // We never reach the key binding checks in G_Responder, so for the console we have to check for ourselves here. + auto binding = Bindings.GetBinding(ev->data1); + if (binding.CompareNoCase("toggleconsole") == 0) + { + C_ToggleConsole(); + return true; + } + } + FInputEvent evt = ev; + if (runner) + { + IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnEvent) + { + int result = 0; + VMValue parm[] = { runner, &evt }; + VMReturn ret(&result); + VMCall(func, parm, 2, &ret, 1); + return result; + } + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool ScreenJobTick() +{ + ticks++; + if (runner) + { + IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnTick) + { + int result = 0; + VMValue parm[] = { runner }; + VMReturn ret(&result); + VMCall(func, parm, 1, &ret, 1); + return result; + } + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +void ScreenJobDraw() +{ + double smoothratio = I_GetTimeFrac(); + + if (runner) + { + twod->ClearScreen(); + IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, RunFrame) + { + VMValue parm[] = { runner, smoothratio }; + VMCall(func, parm, 2, nullptr, 0); + } + } +} + +//============================================================================= +// +// +// +//============================================================================= + +bool ScreenJobValidate() +{ + if (runner) + { + IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, Validate) + { + int res; + VMValue parm[] = { runner }; + VMReturn ret(&res); + VMCall(func, parm, 1, &ret, 1); + return res; + } + } + return false; +} + +//============================================================================= +// +// +// +//============================================================================= + +bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion_) +{ + if ((cs.function.IsNotEmpty() || cs.video.IsNotEmpty()) && cs.function.CompareNoCase("none") != 0) + { + completion = completion_; + runner = CreateRunner(); + GC::WriteBarrier(runner); + try + { + cs.Create(runner); + if (!ScreenJobValidate()) + { + runner->Destroy(); + runner = nullptr; + return false; + } + if (flags & SJ_DELAY) intermissiondelay = 10; // need to wait a bit at the start to let the timer catch up. + else intermissiondelay = 0; + gameaction = (flags & SJ_BLOCKUI) ? ga_intro : ga_intermission; + } + catch (...) + { + if (runner) runner->Destroy(); + runner = nullptr; + throw; + } + return true; + } + return false; +} + +bool StartCutscene(const char* s, int flags, const CompletionFunc& completion) +{ + CutsceneDef def; + def.function = s; + return StartCutscene(def, 0, completion); +} + +//============================================================================= +// +// +// +//============================================================================= + +CCMD(testcutscene) +{ + if (argv.argc() < 2) + { + Printf("Usage: testcutscene \n"); + return; + } + try + { + if (StartCutscene(argv[1], 0, [](bool) {})) + { + C_HideConsole(); + } + } + catch (const CRecoverableError& err) + { + Printf(TEXTCOLOR_RED "Unable to play cutscene: %s\n", err.what()); + } +} + + + diff --git a/source/common/cutscenes/screenjob.h b/source/common/cutscenes/screenjob.h new file mode 100644 index 000000000..be39bd25f --- /dev/null +++ b/source/common/cutscenes/screenjob.h @@ -0,0 +1,55 @@ +#pragma once +#include +#include "dobject.h" +#include "v_2ddrawer.h" +#include "d_eventbase.h" +#include "s_soundinternal.h" +#include "gamestate.h" +#include "zstring.h" + +using CompletionFunc = std::function; + +void Job_Init(); + +enum +{ + SJ_BLOCKUI = 1, + SJ_DELAY = 2, +}; + +struct CutsceneDef +{ + FString video; + FString function; + FString soundName; + int soundID = -1; // ResID not SoundID! + int framespersec = 0; // only relevant for ANM. + bool transitiononly = false; // only play when transitioning between maps, but not when starting on a map or ending a game. + + void Create(DObject* runner); + bool isdefined() { return video.IsNotEmpty() || function.IsNotEmpty(); } + int GetSound(); +}; + +void EndScreenJob(); +void DeleteScreenJob(); +bool ScreenJobResponder(event_t* ev); +bool ScreenJobTick(); +void ScreenJobDraw(); +bool ScreenJobValidate(); + +struct CutsceneDef; +bool StartCutscene(const char* s, int flags, const CompletionFunc& completion); +bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion_); + +VMFunction* LookupFunction(const char* qname, bool validate = true); +void CallCreateFunction(const char* qname, DObject* runner); +DObject* CreateRunner(bool clearbefore = true); +void AddGenericVideo(DObject* runner, const FString& fn, int soundid, int fps); + + +extern int intermissiondelay; +extern DObject* runner; +extern PClass* runnerclass; +extern PType* runnerclasstype; +extern CompletionFunc completion; diff --git a/source/common/filesystem/filesystem.cpp b/source/common/filesystem/filesystem.cpp index d700679f0..4c5a5b910 100644 --- a/source/common/filesystem/filesystem.cpp +++ b/source/common/filesystem/filesystem.cpp @@ -635,7 +635,7 @@ int FileSystem::GetNumForFullName (const char *name) // // FindFile // -// Looks up a file by name, either eith or without path and extension +// Looks up a file by name, either with or without path and extension // //========================================================================== diff --git a/source/core/gamecontrol.cpp b/source/core/gamecontrol.cpp index 6836f548a..5a4dc2a22 100644 --- a/source/core/gamecontrol.cpp +++ b/source/core/gamecontrol.cpp @@ -65,7 +65,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "startupinfo.h" #include "mapinfo.h" #include "menustate.h" -#include "screenjob.h" +#include "screenjob_.h" #include "statusbar.h" #include "uiinput.h" #include "d_net.h" @@ -983,6 +983,7 @@ int RunGame() StartScreen->Progress(); SetDefaultStrings(); Job_Init(); + Local_Job_Init(); if (Args->CheckParm("-sounddebug")) C_DoCommand("stat sounddebug"); diff --git a/source/core/gamecontrol.h b/source/core/gamecontrol.h index a9c163947..9b07c862f 100644 --- a/source/core/gamecontrol.h +++ b/source/core/gamecontrol.h @@ -72,18 +72,6 @@ struct UserConfig extern UserConfig userConfig; -extern int nomusic; -extern bool nosound; -inline int MusicEnabled() // int return is for scripting -{ - return mus_enabled && !nomusic; -} - -inline int SoundEnabled() -{ - return snd_enabled && !nosound; -} - enum { diff --git a/source/core/gamecvars.cpp b/source/core/gamecvars.cpp index 6eaec0d76..c31fde167 100644 --- a/source/core/gamecvars.cpp +++ b/source/core/gamecvars.cpp @@ -104,7 +104,6 @@ CUSTOM_CVARD(Bool, snd_ambience, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG | CVAR_N { gi->SetAmbience(self); } -CVARD(Bool, snd_enabled, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG, "enables/disables sound effects") CVARD(Bool, snd_tryformats, true, CVAR_ARCHIVE|CVAR_GLOBALCONFIG, "enables/disables automatic discovery of replacement sounds and music in .flac and .ogg formats") CVARD(Bool, mus_restartonload, false, CVAR_ARCHIVE, "restart the music when loading a saved game with the same map or not") diff --git a/source/core/gamecvars.h b/source/core/gamecvars.h index 7e3a19259..f79221d89 100644 --- a/source/core/gamecvars.h +++ b/source/core/gamecvars.h @@ -36,12 +36,10 @@ EXTERN_CVAR(Bool, demorec_force_cvar) EXTERN_CVAR(Int, demorec_difftics_cvar) EXTERN_CVAR(Bool, snd_ambience) -EXTERN_CVAR(Bool, snd_enabled) EXTERN_CVAR(Bool, snd_tryformats) EXTERN_CVAR(Bool, mus_enabled) EXTERN_CVAR(Bool, mus_restartonload) EXTERN_CVAR(Bool, mus_redbook) -EXTERN_CVAR(Int, snd_mixrate) EXTERN_CVAR(Int, snd_numchannels) EXTERN_CVAR(Int, snd_numvoices) EXTERN_CVAR(Int, snd_speech) diff --git a/source/core/mainloop.cpp b/source/core/mainloop.cpp index 124a7e61b..7ea245007 100644 --- a/source/core/mainloop.cpp +++ b/source/core/mainloop.cpp @@ -73,7 +73,7 @@ #include "raze_music.h" #include "vm.h" #include "gamestate.h" -#include "screenjob.h" +#include "screenjob_.h" #include "c_console.h" #include "uiinput.h" #include "v_video.h" @@ -104,7 +104,6 @@ bool r_NoInterpolate; int entertic; int oldentertics; int gametic; -int intermissiondelay; FString savename; FString BackupSaveGame; diff --git a/source/core/mapinfo.cpp b/source/core/mapinfo.cpp index d327d5587..fb38e0d2c 100644 --- a/source/core/mapinfo.cpp +++ b/source/core/mapinfo.cpp @@ -68,15 +68,6 @@ CCMD(listmaps) } } -int CutsceneDef::GetSound() -{ - int id; - if (soundName.IsNotEmpty()) id = soundEngine->FindSound(soundName); - if (id <= 0) id = soundEngine->FindSoundByResID(soundID); - return id; -} - - MapRecord *FindMapByName(const char *nm) { for (auto& map : mapList) diff --git a/source/core/mapinfo.h b/source/core/mapinfo.h index dc4a0a5cd..5b2f6b006 100644 --- a/source/core/mapinfo.h +++ b/source/core/mapinfo.h @@ -5,6 +5,8 @@ #include "quotemgr.h" #include "palentry.h" #include "vectors.h" +#include "screenjob.h" + #ifdef GetMessage #undef GetMessage // Windows strikes... #endif @@ -79,21 +81,6 @@ enum { class DObject; struct MapRecord; -struct CutsceneDef -{ - FString video; - FString function; - FString soundName; - int soundID = -1; // ResID not SoundID! - int framespersec = 0; // only relevant for ANM. - bool transitiononly = false; // only play when transitioning between maps, but not when starting on a map or ending a game. - - void Create(DObject* runner); - bool Create(DObject* runner, MapRecord* map, bool transition); - bool isdefined() { return video.IsNotEmpty() || function.IsNotEmpty(); } - int GetSound(); -}; - struct GlobalCutscenes { CutsceneDef Intro; diff --git a/source/core/raze_music.cpp b/source/core/raze_music.cpp index 2453e8e40..2423ad25d 100644 --- a/source/core/raze_music.cpp +++ b/source/core/raze_music.cpp @@ -198,10 +198,6 @@ int Mus_Play(const char *fn, bool loop) // Store the requested names for resuming. lastMusic = fn; - if (!MusicEnabled()) - { - return 1; - } return S_ChangeMusic(fn, 0, loop, true); } diff --git a/source/core/screenjob.cpp b/source/core/screenjob.cpp index 5f43ea138..276389e4e 100644 --- a/source/core/screenjob.cpp +++ b/source/core/screenjob.cpp @@ -35,7 +35,7 @@ #include "types.h" #include "build.h" -#include "screenjob.h" +#include "screenjob_.h" #include "i_time.h" #include "v_2ddrawer.h" #include "animlib.h" @@ -45,19 +45,11 @@ #include "gamestate.h" #include "razemenu.h" #include "raze_sound.h" -#include "SmackerDecoder.h" -#include "movie/playmve.h" #include "gamecontrol.h" -#include -#include #include "raze_music.h" #include "vm.h" #include "mapinfo.h" -static DObject* runner; -static SummaryInfo sinfo; -static PClass* runnerclass; -static PType* runnerclasstype; static PType* maprecordtype; static PType* summaryinfotype; static CompletionFunc completion; @@ -70,61 +62,11 @@ static SummaryInfo summaryinfo; // //============================================================================= -void Job_Init() +void Local_Job_Init() { - static bool done = false; - if (!done) - { - done = true; - GC::AddMarkerFunc([] { GC::Mark(runner); }); - } - runnerclass = PClass::FindClass("ScreenJobRunner"); - if (!runnerclass) I_FatalError("ScreenJobRunner not defined"); - runnerclasstype = NewPointer(runnerclass); - maprecordtype = NewPointer(NewStruct("MapRecord", nullptr, true)); summaryinfotype = NewPointer(NewStruct("SummaryInfo", nullptr, true)); } - -//============================================================================= -// -// -// -//============================================================================= - -static VMFunction* LookupFunction(const char* qname, bool validate = true) -{ - size_t p = strcspn(qname, "."); - if (p == 0) I_Error("Call to undefined function %s", qname); - FString clsname(qname, p); - FString funcname = qname + p + 1; - - auto func = PClass::FindFunction(clsname, funcname); - if (func == nullptr) I_Error("Call to undefined function %s", qname); - if (validate) - { - // these conditions must be met by all functions for this interface. - if (func->Proto->ReturnTypes.Size() != 0) I_Error("Bad cutscene function %s. Return value not allowed", qname); - if (func->ImplicitArgs != 0) I_Error("Bad cutscene function %s. Must be static", qname); - } - return func; -} - -//============================================================================= -// -// -// -//============================================================================= - -void CallCreateFunction(const char* qname, DObject* runner) -{ - auto func = LookupFunction(qname); - if (func->Proto->ArgumentTypes.Size() != 1) I_Error("Bad cutscene function %s. Must receive precisely one argument.", qname); - if (func->Proto->ArgumentTypes[0] != runnerclasstype) I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference.", qname); - VMValue val = runner; - VMCall(func, &val, 1, nullptr, 0); -} - //============================================================================= // // @@ -168,66 +110,19 @@ void CallCreateSummaryFunction(const char* qname, DObject* runner, MapRecord* ma // //============================================================================= -DObject* CreateRunner(bool clearbefore = true) +bool CreateCutscene(CutsceneDef* cs, DObject* runner, MapRecord* map, bool transition) { - auto obj = runnerclass->CreateNew(); - auto func = LookupFunction("ScreenJobRunner.Init", false); - VMValue val[3] = { obj, clearbefore, false }; - VMCall(func, val, 3, nullptr, 0); - return obj; -} - -//============================================================================= -// -// -// -//============================================================================= - -void AddGenericVideo(DObject* runner, const FString& fn, int soundid, int fps) -{ - auto obj = runnerclass->CreateNew(); - auto func = LookupFunction("ScreenJobRunner.AddGenericVideo", false); - VMValue val[] = { runner, &fn, soundid, fps }; - VMCall(func, val, 4, nullptr, 0); -} - -//============================================================================= -// -// -// -//============================================================================= - -void CutsceneDef::Create(DObject* runner) -{ - if (function.IsNotEmpty()) - { - CallCreateFunction(function, runner); - } - else if (video.IsNotEmpty()) - { - AddGenericVideo(runner, video, GetSound(), framespersec); - } -} - -//============================================================================= -// -// -// -//============================================================================= - -bool CutsceneDef::Create(DObject* runner, MapRecord* map, bool transition) -{ - if (!transition && transitiononly) return false; - if (function.CompareNoCase("none") == 0) + if (!transition && cs->transitiononly) return false; + if (cs->function.CompareNoCase("none") == 0) return true; // play nothing but return as being validated - if (function.IsNotEmpty()) + if (cs->function.IsNotEmpty()) { - CallCreateMapFunction(function, runner, map); + CallCreateMapFunction(cs->function, runner, map); return true; } - else if (video.IsNotEmpty()) + else if (cs->video.IsNotEmpty()) { - AddGenericVideo(runner, video, GetSound(), framespersec); + AddGenericVideo(runner, cs->video, cs->GetSound(), cs->framespersec); return true; } return false; @@ -239,169 +134,6 @@ bool CutsceneDef::Create(DObject* runner, MapRecord* map, bool transition) // //============================================================================= -void DeleteScreenJob() -{ - if (runner) runner->Destroy(); - runner = nullptr; -} - -void EndScreenJob() -{ - DeleteScreenJob(); - if (completion) completion(false); - completion = nullptr; -} - - -//============================================================================= -// -// -// -//============================================================================= - -bool ScreenJobResponder(event_t* ev) -{ - if (ev->type == EV_KeyDown) - { - // We never reach the key binding checks in G_Responder, so for the console we have to check for ourselves here. - auto binding = Bindings.GetBinding(ev->data1); - if (binding.CompareNoCase("toggleconsole") == 0) - { - C_ToggleConsole(); - return true; - } - } - FInputEvent evt = ev; - if (runner) - { - IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnEvent) - { - int result = 0; - VMValue parm[] = { runner, &evt }; - VMReturn ret(&result); - VMCall(func, parm, 2, &ret, 1); - return result; - } - } - return false; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool ScreenJobTick() -{ - ticks++; - if (runner) - { - IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnTick) - { - int result = 0; - VMValue parm[] = { runner }; - VMReturn ret(&result); - VMCall(func, parm, 1, &ret, 1); - return result; - } - } - return false; -} - -//============================================================================= -// -// -// -//============================================================================= - -void ScreenJobDraw() -{ - double smoothratio = I_GetTimeFrac(); - - if (runner) - { - twod->ClearScreen(); - IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, RunFrame) - { - VMValue parm[] = { runner, smoothratio }; - VMCall(func, parm, 2, nullptr, 0); - } - } -} - -//============================================================================= -// -// -// -//============================================================================= - -bool ScreenJobValidate() -{ - if (runner) - { - IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, Validate) - { - int res; - VMValue parm[] = { runner }; - VMReturn ret(&res); - VMCall(func, parm, 1, &ret, 1); - return res; - } - } - return false; -} - -//============================================================================= -// -// -// -//============================================================================= - -bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion_) -{ - if ((cs.function.IsNotEmpty() || cs.video.IsNotEmpty()) && cs.function.CompareNoCase("none") != 0) - { - completion = completion_; - runner = CreateRunner(); - GC::WriteBarrier(runner); - try - { - cs.Create(runner); - if (!ScreenJobValidate()) - { - runner->Destroy(); - runner = nullptr; - return false; - } - if (flags & SJ_DELAY) intermissiondelay = 10; // need to wait a bit at the start to let the timer catch up. - else intermissiondelay = 0; - gameaction = (flags & SJ_BLOCKUI) ? ga_intro : ga_intermission; - } - catch (...) - { - if (runner) runner->Destroy(); - runner = nullptr; - throw; - } - return true; - } - return false; -} - -bool StartCutscene(const char* s, int flags, const CompletionFunc& completion) -{ - CutsceneDef def; - def.function = s; - return StartCutscene(def, 0, completion); -} - -//============================================================================= -// -// -// -//============================================================================= - void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic) { Mus_Stop(); @@ -471,10 +203,10 @@ void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, C { if (fromMap) { - if (!fromMap->outro.Create(runner, fromMap, !!toMap)) + if (!CreateCutscene(&fromMap->outro, runner, fromMap, !!toMap)) { - if (fromcluster == nullptr || !fromcluster->outro.Create(runner, fromMap, !!toMap)) - globalCutscenes.DefaultMapOutro.Create(runner, fromMap, !!toMap); + if (fromcluster == nullptr || !CreateCutscene(&fromcluster->outro, runner, fromMap, !!toMap)) + CreateCutscene(&globalCutscenes.DefaultMapOutro, runner, fromMap, !!toMap); } } @@ -483,16 +215,16 @@ void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, C if (toMap) { - if (!toMap->intro.Create(runner, toMap, !!fromMap)) + if (!CreateCutscene(&toMap->intro, runner, toMap, !!fromMap)) { - if (tocluster == nullptr || !tocluster->intro.Create(runner, toMap, !!fromMap)) - globalCutscenes.DefaultMapIntro.Create(runner, toMap, !!fromMap); + if (tocluster == nullptr || !CreateCutscene(&tocluster->intro, runner, toMap, !!fromMap)) + CreateCutscene(&globalCutscenes.DefaultMapIntro, runner, toMap, !!fromMap); } // Skip the load screen if the level is started from the console. // In this case the load screen is not helpful as it blocks the actual level start, // requiring closing and reopening the console first before entering any commands that need the level. if (ConsoleState == c_up || ConsoleState == c_rising) - globalCutscenes.LoadingScreen.Create(runner, toMap, true); + CreateCutscene(&globalCutscenes.LoadingScreen, runner, toMap, true); } else if (isShareware()) { @@ -515,50 +247,4 @@ void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, C throw; } } - -CCMD(testcutscene) -{ - if (argv.argc() < 2) - { - Printf("Usage: testcutscene \n"); - return; - } - try - { - if (StartCutscene(argv[1], 0, [](bool) {})) - { - C_HideConsole(); - } - } - catch (const CRecoverableError& err) - { - Printf(TEXTCOLOR_RED "Unable to play cutscene: %s\n", err.what()); - } -} - - - -/* -Blood: - if (!userConfig.nologo && gGameOptions.nGameType == 0) playlogos(); - else - { - gameaction = ga_mainmenu; - } - RunScreenJob(jobs, [](bool) { - Mus_Stop(); - gameaction = ga_mainmenu; - }, SJ_BLOCKUI); - -Exhumed: - if (!userConfig.nologo) DoTitle([](bool) { gameaction = ga_mainmenu; }); - else gameaction = ga_mainmenu; - -SW: - if (!userConfig.nologo) Logo([](bool) - { - gameaction = ga_mainmenunostopsound; - }); - else gameaction = ga_mainmenu; - -*/ \ No newline at end of file + \ No newline at end of file diff --git a/source/core/screenjob.h b/source/core/screenjob_.h similarity index 54% rename from source/core/screenjob.h rename to source/core/screenjob_.h index 94c445158..e7bbc40e0 100644 --- a/source/core/screenjob.h +++ b/source/core/screenjob_.h @@ -1,31 +1,14 @@ #pragma once -#include -#include "dobject.h" #include "v_2ddrawer.h" #include "d_eventbase.h" #include "s_soundinternal.h" #include "gamestate.h" - -using CompletionFunc = std::function; - -void Job_Init(); - -enum -{ - SJ_BLOCKUI = 1, - SJ_DELAY = 2, -}; - -void EndScreenJob(); -void DeleteScreenJob(); -bool ScreenJobResponder(event_t* ev); -bool ScreenJobTick(); -void ScreenJobDraw(); +#include "screenjob.h" struct CutsceneDef; struct MapRecord; struct SummaryInfo; -bool StartCutscene(const char* s, int flags, const CompletionFunc& completion); void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic); void ShowScoreboard(int numplayers, const CompletionFunc& completion_); void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_); +void Local_Job_Init(); diff --git a/source/games/blood/src/blood.cpp b/source/games/blood/src/blood.cpp index d18842885..2477b81a6 100644 --- a/source/games/blood/src/blood.cpp +++ b/source/games/blood/src/blood.cpp @@ -41,7 +41,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "raze_sound.h" #include "secrets.h" #include "gamestate.h" -#include "screenjob.h" +#include "screenjob_.h" #include "mapinfo.h" #include "d_net.h" #include "v_video.h" diff --git a/source/games/duke/src/gameloop.cpp b/source/games/duke/src/gameloop.cpp index ee05dcc9a..d18172984 100644 --- a/source/games/duke/src/gameloop.cpp +++ b/source/games/duke/src/gameloop.cpp @@ -28,6 +28,7 @@ Modifications for JonoF's port by Jonathon Fowler (jf@jonof.id.au) #include "ns.h" // Must come before everything else! +#include "screenjob_.h" #include "gamestate.h" #include "duke3d.h" #include "m_argv.h" diff --git a/source/games/duke/src/premap.cpp b/source/games/duke/src/premap.cpp index 2f9dbef2e..2e1193b03 100644 --- a/source/games/duke/src/premap.cpp +++ b/source/games/duke/src/premap.cpp @@ -37,6 +37,7 @@ Prepared for public release: 03/21/2003 - Charlie Wiederhold, 3D Realms #include "interpolate.h" #include "precache.h" #include "render.h" +#include "screenjob_.h" BEGIN_DUKE_NS diff --git a/source/games/duke/src/sounds.cpp b/source/games/duke/src/sounds.cpp index 3e1a61df1..9f886749d 100644 --- a/source/games/duke/src/sounds.cpp +++ b/source/games/duke/src/sounds.cpp @@ -47,6 +47,7 @@ source as it is released. #include "names_d.h" #include "i_music.h" #include "vm.h" +#include "s_music.h" CVAR(Bool, wt_forcemidi, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // quick hack to disable the oggs, which are of lower quality than playing the MIDIs with a good synth and sound font. CVAR(Bool, wt_forcevoc, false, CVAR_ARCHIVE|CVAR_GLOBALCONFIG) // The same for sound effects. The re-recordings are rather poor and disliked diff --git a/source/games/exhumed/src/gameloop.cpp b/source/games/exhumed/src/gameloop.cpp index 04142292e..dcbefbb6f 100644 --- a/source/games/exhumed/src/gameloop.cpp +++ b/source/games/exhumed/src/gameloop.cpp @@ -39,7 +39,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. #include "c_dispatch.h" #include "raze_sound.h" #include "gamestate.h" -#include "screenjob.h" +#include "screenjob_.h" #include "c_console.h" #include "cheathandler.h" #include "statistics.h" diff --git a/source/games/sw/src/game.cpp b/source/games/sw/src/game.cpp index ef08d5ea9..71eb49d3f 100644 --- a/source/games/sw/src/game.cpp +++ b/source/games/sw/src/game.cpp @@ -73,7 +73,7 @@ Prepared for public release: 03/28/2005 - Charlie Wiederhold, 3D Realms #include "raze_sound.h" #include "secrets.h" -#include "screenjob.h" +#include "screenjob_.h" #include "inputstate.h" #include "gamestate.h" #include "d_net.h"