- split the screen job code into a generic and a Raze specific part.

Preparations for porting this to GZDoom.
This commit is contained in:
Christoph Oelckers 2021-05-22 01:34:00 +02:00
parent 1dff0502b0
commit e10bcf6294
27 changed files with 485 additions and 418 deletions

View file

@ -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/.+")

View file

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

View file

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

View file

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

View file

@ -429,3 +429,5 @@ inline int S_FindSound(const char* name)
{
return soundEngine->FindSound(name);
}
int SoundEnabled();

View file

@ -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 <vpx/vpx_decoder.h>
#include <vpx/vp8dx.h>
#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<int>& 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.

View file

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

View file

@ -0,0 +1,55 @@
#pragma once
#include <functional>
#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(bool)>;
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;

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -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 <vpx/vpx_decoder.h>
#include <vpx/vp8dx.h>
#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 <buildfunction>\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;
*/

View file

@ -1,31 +1,14 @@
#pragma once
#include <functional>
#include "dobject.h"
#include "v_2ddrawer.h"
#include "d_eventbase.h"
#include "s_soundinternal.h"
#include "gamestate.h"
using CompletionFunc = std::function<void(bool)>;
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();

View file

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

View file

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

View file

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

View file

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

View file

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

View file

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