- final cleanup on new cutscene interfacw

* fixed frame gaps when exiting a cutscene. This must be properly synchronized with the state handler in the main loop.
* reactivated the loading screens. With texture precaching working the delay when loading a map becomes significant enough to require a visual feedback.
* RR does not play 'bonusmusic' on its summary screen.
This commit is contained in:
Christoph Oelckers 2021-04-28 01:12:07 +02:00
parent ad01aee5ec
commit cdccdec3e7
10 changed files with 77 additions and 213 deletions

View file

@ -242,7 +242,7 @@ void changeMap(int player, uint8_t** stream, bool skip)
void endScreenJob(int player, uint8_t** stream, bool skip)
{
if (!skip) EndScreenJob();
if (!skip) gameaction = ga_endscreenjob;
}
//---------------------------------------------------------------------------

View file

@ -43,6 +43,7 @@ enum gameaction_t : int
ga_nextlevel, // Actually start the next level.
ga_loadgamehidecon,
ga_newgamenostopsound, // start a new game
ga_endscreenjob,
ga_fullconsole,
};

View file

@ -133,23 +133,6 @@ void G_BuildTiccmd(ticcmd_t* cmd)
//==========================================================================
bool newGameStarted;
void NewGame(MapRecord* map, int skill, bool ns = false)
{
newGameStarted = true;
int volume = map->cluster - 1;
if (volume >= 0 && volume < MAXVOLUMES)
{
if (StartCutscene(volumeList[volume].intro, 0, [=](bool) { gi->NewGame(map, skill, ns); })) return;
}
gi->NewGame(map, skill, ns);
}
//==========================================================================
//
//
//
//==========================================================================
static void GameTicker()
{
int i;
@ -176,6 +159,7 @@ static void GameTicker()
FX_SetReverb(0);
gi->FreeLevelData();
gameaction = ga_level;
newGameStarted = true;
NewGame(g_nextmap, -1);
BackupSaveGame = "";
}
@ -213,6 +197,7 @@ static void GameTicker()
C_FlushDisplay();
gameaction = ga_level;
BackupSaveGame = "";
newGameStarted = true;
NewGame(g_nextmap, g_nextskill, ga == ga_newgamenostopsound);
break;
@ -269,6 +254,10 @@ static void GameTicker()
gameaction = ga_nothing;
break;
case ga_endscreenjob:
EndScreenJob();
break;
// for later
// case ga_recordgame, // start a new demo recording (later)
// case ga_loadgameplaydemo, // load a savegame and play a demo.
@ -659,6 +648,7 @@ void MainLoop ()
userConfig.CommandMap = "";
if (maprecord)
{
newGameStarted = true;
NewGame(maprecord, /*userConfig.skill*/2); // todo: fix the skill.
}
}

View file

@ -64,6 +64,7 @@ struct GlobalCutscenes
CutsceneDef DefaultMapIntro;
CutsceneDef DefaultMapOutro;
CutsceneDef SharewareEnd;
CutsceneDef LoadingScreen;
FString MPSummaryScreen;
FString SummaryScreen;
};

View file

@ -100,6 +100,10 @@ void parseDefineCutscene(FScanner& sc, FScriptPosition& pos)
{
parseCutscene(sc, globalCutscenes.SharewareEnd);
}
else if (sc.Compare("loading")) // sets the loading screen when entering a level.
{
parseCutscene(sc, globalCutscenes.LoadingScreen);
}
else if (sc.Compare({ "episode", "volume", "cluster" }))
{
FScanner::SavedPos eblockend;

View file

@ -356,7 +356,7 @@ bool ScreenJobValidate()
bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion_)
{
if (cs.function.IsNotEmpty() && cs.video.IsNotEmpty()cs.function.CompareNoCase("none") != 0)
if (cs.function.IsNotEmpty() && cs.video.IsNotEmpty() && cs.function.CompareNoCase("none") != 0)
{
completion = completion_;
runner = CreateRunner();
@ -440,6 +440,43 @@ void ShowScoreboard(int numplayers, const CompletionFunc& completion_)
gameaction = ga_intermission;
}
//==========================================================================
//
//
//
//==========================================================================
void NewGame(MapRecord* map, int skill, bool ns)
{
completion = [=](bool) { gi->NewGame(map, skill, ns); };
runner = CreateRunner();
GC::WriteBarrier(runner);
try
{
int volume = map->cluster - 1;
if (volume >= 0 && volume < MAXVOLUMES)
volumeList[volume].intro.Create(runner);
globalCutscenes.LoadingScreen.Create(runner, map);
if (!ScreenJobValidate())
{
runner->Destroy();
runner = nullptr;
completion(false);
completion = nullptr;
return;
}
gameaction = ga_intermission;
}
catch (...)
{
if (runner) runner->Destroy();
runner = nullptr;
throw;
}
}
//---------------------------------------------------------------------------
//
//
@ -468,8 +505,10 @@ void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, C
if (toMap)
{
if (!toMap->intro.Create(runner, fromMap))
globalCutscenes.DefaultMapIntro.Create(runner, fromMap);
if (!toMap->intro.Create(runner, toMap))
globalCutscenes.DefaultMapIntro.Create(runner, toMap);
globalCutscenes.LoadingScreen.Create(runner, toMap);
}
else if (isShareware())
{
@ -502,9 +541,7 @@ CCMD(testcutscene)
}
try
{
CutsceneDef def;
def.function = argv[1];
if (StartCutscene(def, 0, [](bool) {}))
if (StartCutscene(argv[1], 0, [](bool) {}))
{
C_HideConsole();
}

View file

@ -7,193 +7,14 @@
#include "gamestate.h"
using CompletionFunc = std::function<void(bool)>;
struct JobDesc;
class ScreenJobRunner;
void Job_Init();
#if 0
class DScreenJob : public DObject
{
DECLARE_CLASS(DScreenJob, DObject)
public:
const int flags;
const float fadetime; // in milliseconds
int fadestate = fadein;
friend class ScreenJobRunner;
//protected:
int ticks = 0;
int state = running;
bool pausable = true;
public:
enum
{
running = 1, // normal operation
skipped = 2, // finished by user skipping
finished = 3, // finished by completing its sequence
stopping = 4, // running ending animations / fadeout, etc. Will not accept more input.
stopped = 5, // we're done here.
};
enum
{
visible = 0,
fadein = 1,
fadeout = 2,
stopmusic = 4,
stopsound = 8,
};
DScreenJob(int fade = 0, float fadet = 250.f) : flags(fade), fadetime(fadet) {}
virtual bool ProcessInput()
{
return false;
}
virtual void Start() {}
virtual bool OnEvent(event_t* evt) { return false; }
virtual void OnTick() { /*state = finished;*/ }
virtual void Draw(double smoothratio) {}
int DrawFrame(double smoothratio)
{
if (state != running) smoothratio = 1; // this is necessary because the ticker won't be incremented anymore to avoid having a negative time span.
Draw(smoothratio);
if (state == skipped) return -1;
if (state == finished) return 0;
return 1;
}
int GetFadeState() const { return fadestate; }
virtual void OnDestroy() override;
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DSkippableScreenJob : public DScreenJob
{
DECLARE_CLASS(DSkippableScreenJob, DScreenJob)
public:
DSkippableScreenJob(int fade = 0, float fadet = 250.f) : DScreenJob(fade, fadet)
{}
bool OnEvent(event_t* evt) override;
virtual void Skipped() {}
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DBlackScreen : public DScreenJob
{
DECLARE_CLASS(DBlackScreen, DScreenJob)
public:
int wait;
bool cleared = false;
public:
DBlackScreen(int w, int flags = 0) : DScreenJob(flags & ~(fadein|fadeout)), wait(w) {}
void OnTick() override;
void Draw(double smooth) override;
};
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class DImageScreen : public DSkippableScreenJob
{
DECLARE_CLASS(DImageScreen, DScreenJob)
public:
int tilenum = -1;
int trans;
int waittime; // in ms.
bool cleared = false;
FTextureID texid;
FGameTexture* tex = nullptr;
public:
DImageScreen(FGameTexture* tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000, int translation = 0) : DSkippableScreenJob(fade), waittime(wait)
{
tex = tile;
trans = translation;
}
DImageScreen(int tile, int fade = DScreenJob::fadein | DScreenJob::fadeout, int wait = 3000, int translation = 0) : DSkippableScreenJob(fade), waittime(wait)
{
tilenum = tile;
trans = translation;
}
void OnTick() override;
void Draw(double smooth) override;
};
#endif
#if 0
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
class ScreenJobRunner
{
enum
{
State_Clear,
State_Run,
State_Fadeout
};
TArray<DScreenJob*> jobs;
CompletionFunc completion;
int index = -1;
float screenfade;
bool clearbefore;
bool skipall;
int actionState;
int terminateState;
int fadeticks = 0;
int last_paused_tic = -1;
public:
ScreenJobRunner(TArray<DScreenJob*>& jobs, CompletionFunc completion_, bool clearbefore_, bool skipall_);
~ScreenJobRunner();
void DeleteJobs();
void AdvanceJob(bool skip);
int DisplayFrame(double smoothratio);
int FadeoutFrame(double smoothratio);
bool OnEvent(event_t* ev);
void OnFinished();
void OnTick();
bool RunFrame();
};
#endif
enum
{
SJ_BLOCKUI = 1,
};
#if 0
void RunScreenJob(TArray<DScreenJob*>& jobs, CompletionFunc completion, int flags = 0);
#endif
void EndScreenJob();
void DeleteScreenJob();
bool ScreenJobResponder(event_t* ev);
@ -203,8 +24,8 @@ void ScreenJobDraw();
struct CutsceneDef;
struct MapRecord;
struct SummaryInfo;
bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion);
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 NewGame(MapRecord* map, int skill, bool ns = false);

View file

@ -54,6 +54,11 @@ definecutscene sharewareend
function DukeCutscenes.BuildSharewareEnd
}
definecutscene loading
{
function DukeCutscenes.BuildLoading
}
definecutscene summary DukeCutscenes.BuildSPSummary
definecutscene mpsummary DukeCutscenes.BuildMPSummary

View file

@ -22,10 +22,15 @@ definecutscene episode 2
}
}
definecutscene summary RRCutscenes.BuildSPSummary
definecutscene mpsummary DukeCutscenes.BuildMPSummary // identical with Duke's
definecutscene mapintro
{
function RRCutscenes.BuildMapIntro // this plays the 'travel' animation.
}
definecutscene loading
{
function DukeCutscenes.BuildLoading // identical with Duke's
}
definecutscene summary RRCutscenes.BuildSPSummary
definecutscene mpsummary DukeCutscenes.BuildMPSummary // identical with Duke's

View file

@ -459,7 +459,7 @@ class DukeMultiplayerBonusScreen : SkippableScreenJob
override void Start()
{
Duke.PlayBonusMusic();
if (!Raze.isRR()) Duke.PlayBonusMusic();
}
override void Draw(double smoothratio)
@ -853,11 +853,6 @@ class RRLevelSummaryScreen : SummaryScreenBase
return false;
}
override void Start()
{
Duke.PlayBonusMusic();
}
override void OnTick()
{
if ((displaystate & printStatsAll) != printStatsAll)
@ -1031,11 +1026,16 @@ class DukeLoadScreen : ScreenJob
ScreenJob Init(MapRecord maprec)
{
Super.Init();
Super.Init(fadein);
rec = maprec;
return self;
}
override void OnTick()
{
if (fadestate == visible) jobstate = finished;
}
override void Draw(double sr)
{
Screen.DrawTexture(TexMan.CheckForTexture("LOADSCREEN"), false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal);