- 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.
This commit is contained in:
Christoph Oelckers 2022-06-06 12:30:47 +02:00
parent c62e14d2c1
commit 71ce8aa79a
4 changed files with 80 additions and 50 deletions

View file

@ -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, &param, 1, &ret, 1);
return can;
}
return true;
}
//=============================================================================
//
//

View file

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

View file

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

View file

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