From 71ce8aa79a83c824629961b87532c6b5634aea7c Mon Sep 17 00:00:00 2001 From: Christoph Oelckers Date: Mon, 6 Jun 2022 12:30:47 +0200 Subject: [PATCH] - block wipes when streaming movies are playing. For synchronization purposes they cannot be interrupted by the wipe loop. This also groups the cutscene state in a struct for readability purposes. --- src/common/cutscenes/screenjob.cpp | 77 ++++++++++++++--------- src/common/cutscenes/screenjob.h | 13 ++-- src/g_level.cpp | 32 +++++----- wadsrc/static/zscript/engine/screenjob.zs | 8 +++ 4 files changed, 80 insertions(+), 50 deletions(-) diff --git a/src/common/cutscenes/screenjob.cpp b/src/common/cutscenes/screenjob.cpp index 840b9f55e..901cf5f7d 100644 --- a/src/common/cutscenes/screenjob.cpp +++ b/src/common/cutscenes/screenjob.cpp @@ -54,10 +54,7 @@ CVAR(Bool, inter_subtitles, false, CVAR_ARCHIVE | CVAR_GLOBALCONFIG); -DObject* runner; -PClass* runnerclass; -PType* runnerclasstype; -CompletionFunc completion; +CutsceneState cutscene; static int ticks; //============================================================================= @@ -72,11 +69,11 @@ void Job_Init() if (!done) { done = true; - GC::AddMarkerFunc([] { GC::Mark(runner); }); + GC::AddMarkerFunc([] { GC::Mark(cutscene.runner); }); } - runnerclass = PClass::FindClass("ScreenJobRunner"); - if (!runnerclass) I_FatalError("ScreenJobRunner not defined"); - runnerclasstype = NewPointer(runnerclass); + cutscene.runnerclass = PClass::FindClass("ScreenJobRunner"); + if (!cutscene.runnerclass) I_FatalError("ScreenJobRunner not defined"); + cutscene.runnerclasstype = NewPointer(cutscene.runnerclass); } //============================================================================= @@ -115,7 +112,7 @@ 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); + if (func->Proto->ArgumentTypes[0] != cutscene.runnerclasstype) I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference.", qname); VMValue val = runner; VMCall(func, &val, 1, nullptr, 0); } @@ -128,7 +125,7 @@ void CallCreateFunction(const char* qname, DObject* runner) DObject* CreateRunner(bool clearbefore) { - auto obj = runnerclass->CreateNew(); + auto obj = cutscene.runnerclass->CreateNew(); auto func = LookupFunction("ScreenJobRunner.Init", false); VMValue val[3] = { obj, clearbefore, false }; VMCall(func, val, 3, nullptr, 0); @@ -182,15 +179,15 @@ void CutsceneDef::Create(DObject* runner) void DeleteScreenJob() { - if (runner) runner->Destroy(); - runner = nullptr; + if (cutscene.runner) cutscene.runner->Destroy(); + cutscene.runner = nullptr; } void EndScreenJob() { DeleteScreenJob(); - if (completion) completion(false); - completion = nullptr; + if (cutscene.completion) cutscene.completion(false); + cutscene.completion = nullptr; } @@ -213,12 +210,12 @@ bool ScreenJobResponder(event_t* ev) } } FInputEvent evt = ev; - if (runner) + if (cutscene.runner) { - IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnEvent) + IFVIRTUALPTRNAME(cutscene.runner, NAME_ScreenJobRunner, OnEvent) { int result = 0; - VMValue parm[] = { runner, &evt }; + VMValue parm[] = { cutscene.runner, &evt }; VMReturn ret(&result); VMCall(func, parm, 2, &ret, 1); return result; @@ -236,12 +233,12 @@ bool ScreenJobResponder(event_t* ev) bool ScreenJobTick() { ticks++; - if (runner) + if (cutscene.runner) { - IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, OnTick) + IFVIRTUALPTRNAME(cutscene.runner, NAME_ScreenJobRunner, OnTick) { int result = 0; - VMValue parm[] = { runner }; + VMValue parm[] = { cutscene.runner }; VMReturn ret(&result); VMCall(func, parm, 1, &ret, 1); return result; @@ -260,12 +257,12 @@ void ScreenJobDraw() { double smoothratio = I_GetTimeFrac(); - if (runner) + if (cutscene.runner) { twod->ClearScreen(); - IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, RunFrame) + IFVIRTUALPTRNAME(cutscene.runner, NAME_ScreenJobRunner, RunFrame) { - VMValue parm[] = { runner, smoothratio }; + VMValue parm[] = { cutscene.runner, smoothratio }; VMCall(func, parm, 2, nullptr, 0); } } @@ -279,12 +276,12 @@ void ScreenJobDraw() bool ScreenJobValidate() { - if (runner) + if (cutscene.runner) { - IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, Validate) + IFVIRTUALPTRNAME(cutscene.runner, NAME_ScreenJobRunner, Validate) { int res; - VMValue parm[] = { runner }; + VMValue parm[] = { cutscene.runner }; VMReturn ret(&res); VMCall(func, parm, 1, &ret, 1); I_ResetFrameTime(); @@ -304,12 +301,12 @@ 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); + cutscene.completion = completion_; + cutscene.runner = CreateRunner(); + GC::WriteBarrier(cutscene.runner); try { - cs.Create(runner); + cs.Create(cutscene.runner); if (!ScreenJobValidate()) { DeleteScreenJob(); @@ -349,6 +346,26 @@ DEFINE_ACTION_FUNCTION(DScreenJobRunner, setTransition) return 0; } +//============================================================================= +// +// to block wipes on cutscenes that cannot handle it +// +//============================================================================= + +bool CanWipe() +{ + if (cutscene.runner == nullptr) return true; + IFVM(ScreenJobRunner, CanWipe) + { + int can; + VMReturn ret(&can); + VMValue param = cutscene.runner; + VMCall(func, ¶m, 1, &ret, 1); + return can; + } + return true; +} + //============================================================================= // // diff --git a/src/common/cutscenes/screenjob.h b/src/common/cutscenes/screenjob.h index 92cb766e8..dce55e3b0 100644 --- a/src/common/cutscenes/screenjob.h +++ b/src/common/cutscenes/screenjob.h @@ -43,14 +43,19 @@ bool ScreenJobValidate(); struct CutsceneDef; bool StartCutscene(const char* s, int flags, const CompletionFunc& completion); bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion_); +bool CanWipe(); 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); +struct CutsceneState +{ + DObject* runner; + PClass* runnerclass; + PType* runnerclasstype; + CompletionFunc completion; +}; -extern DObject* runner; -extern PClass* runnerclass; -extern PType* runnerclasstype; -extern CompletionFunc completion; +extern CutsceneState cutscene; diff --git a/src/g_level.cpp b/src/g_level.cpp index 6e7322459..15c000cc6 100644 --- a/src/g_level.cpp +++ b/src/g_level.cpp @@ -205,7 +205,7 @@ static void CallCreateMapFunction(const char* qname, DObject* runner, level_info auto func = LookupFunction(qname); if (func->Proto->ArgumentTypes.Size() == 1) return CallCreateFunction(qname, runner); // accept functions without map parameter as well here. if (func->Proto->ArgumentTypes.Size() != 2) I_Error("Bad map-cutscene function %s. Must receive precisely two arguments.", qname); - if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != maprecordtype) + if (func->Proto->ArgumentTypes[0] != cutscene.runnerclasstype && func->Proto->ArgumentTypes[1] != maprecordtype) I_Error("Bad cutscene function %s. Must receive ScreenJobRunner and LevelInfo reference.", qname); VMValue val[2] = { runner, map }; VMCall(func, val, 2, nullptr, 0); @@ -264,19 +264,19 @@ void G_DeferedInitNew (FNewGameStartup *gs) if (AllEpisodes[gs->Episode].mIntro.isdefined()) { - runner = CreateRunner(false); - GC::WriteBarrier(runner); + cutscene.runner = CreateRunner(false); + GC::WriteBarrier(cutscene.runner); - if (!CreateCutscene(&AllEpisodes[gs->Episode].mIntro, runner, nullptr)) + if (!CreateCutscene(&AllEpisodes[gs->Episode].mIntro, cutscene.runner, nullptr)) { return; } - completion = [](bool) { gameaction = ga_newgame2; }; + cutscene.completion = [](bool) { gameaction = ga_newgame2; }; if (!ScreenJobValidate()) { DeleteScreenJob(); - completion = nullptr; + cutscene.completion = nullptr; return; } gameaction = ga_intermission; @@ -1025,9 +1025,9 @@ void RunIntermission(level_info_t* fromMap, level_info_t* toMap, DIntermissionCo completionf(false); return; } - runner = CreateRunner(false); - GC::WriteBarrier(runner); - completion = std::move(completionf); + cutscene.runner = CreateRunner(false); + GC::WriteBarrier(cutscene.runner); + cutscene.completion = std::move(completionf); // retrieve cluster relations for cluster-based cutscenes. cluster_info_t* fromcluster = nullptr, *tocluster = nullptr; @@ -1037,9 +1037,9 @@ void RunIntermission(level_info_t* fromMap, level_info_t* toMap, DIntermissionCo if (fromMap) { - if (!CreateCutscene(&fromMap->outro, runner, fromMap)) + if (!CreateCutscene(&fromMap->outro, cutscene.runner, fromMap)) { - if (fromcluster != nullptr) CreateCutscene(&fromcluster->outro, runner, fromMap); + if (fromcluster != nullptr) CreateCutscene(&fromcluster->outro, cutscene.runner, fromMap); } } @@ -1048,22 +1048,22 @@ void RunIntermission(level_info_t* fromMap, level_info_t* toMap, DIntermissionCo { I_Error("Script function 'DoomCutscenes.BuildMapTransition' not found"); } - VMValue val[3] = { runner, intermissionScreen, statusScreen }; + VMValue val[3] = { cutscene.runner, intermissionScreen, statusScreen }; VMCall(func, val, 3, nullptr, 0); if (toMap) { - if (!CreateCutscene(&toMap->intro, runner, toMap)) + if (!CreateCutscene(&toMap->intro, cutscene.runner, toMap)) { - if (tocluster != nullptr) CreateCutscene(&tocluster->intro, runner, toMap); + if (tocluster != nullptr) CreateCutscene(&tocluster->intro, cutscene.runner, toMap); } } if (!ScreenJobValidate()) { DeleteScreenJob(); - if (completion) completion(false); - completion = nullptr; + if (cutscene.completion) cutscene.completion(false); + cutscene.completion = nullptr; return; } gameaction = ga_intermission; diff --git a/wadsrc/static/zscript/engine/screenjob.zs b/wadsrc/static/zscript/engine/screenjob.zs index e11234fd9..7981418c0 100644 --- a/wadsrc/static/zscript/engine/screenjob.zs +++ b/wadsrc/static/zscript/engine/screenjob.zs @@ -9,6 +9,7 @@ class ScreenJob : Object UI int jobstate; bool skipover; + bool nowipe; enum EJobState { @@ -229,6 +230,7 @@ class MoviePlayerJob : SkippableScreenJob Super.Init(); flag = flags; player = mp; + nowipe = true; // due to synchronization issues wipes must be disabled on any movie. return self; } @@ -360,6 +362,12 @@ class ScreenJobRunner : Object UI return jobs.Size() > 0; } + bool CanWipe() + { + if (index < jobs.Size()) return !jobs[index].nowipe; + return true; + } + //--------------------------------------------------------------------------- // //