- most of the summary screen is working

This commit is contained in:
Christoph Oelckers 2021-04-28 00:51:28 +02:00
parent 0dc6980e5c
commit 6d743ce921
21 changed files with 266 additions and 276 deletions

View file

@ -1498,6 +1498,7 @@ DEFINE_FIELD_X(MapRecord, MapRecord, music)
DEFINE_FIELD_X(MapRecord, MapRecord, cdSongId) DEFINE_FIELD_X(MapRecord, MapRecord, cdSongId)
DEFINE_FIELD_X(MapRecord, MapRecord, flags) DEFINE_FIELD_X(MapRecord, MapRecord, flags)
DEFINE_FIELD_X(MapRecord, MapRecord, levelNumber) DEFINE_FIELD_X(MapRecord, MapRecord, levelNumber)
DEFINE_FIELD_X(MapRecord, MapRecord, cluster)
DEFINE_FIELD_X(MapRecord, MapRecord, nextLevel) DEFINE_FIELD_X(MapRecord, MapRecord, nextLevel)
DEFINE_FIELD_X(MapRecord, MapRecord, nextSecret) DEFINE_FIELD_X(MapRecord, MapRecord, nextSecret)
//native readonly String messages[MAX_MESSAGES]; //native readonly String messages[MAX_MESSAGES];

View file

@ -93,6 +93,28 @@ MapRecord *FindMapByLevelNum(int num)
return nullptr; return nullptr;
} }
// return a map whose cluster and map number matches.
// if there's only one map with the given level number return that.
MapRecord* FindMapByClusterAndLevelNum(int cluster, int num)
{
MapRecord* mr = nullptr;
int mapfound = 0;
for (auto& map : mapList)
{
if (map.levelNumber == num)
{
if (map.cluster == cluster) return ↦
else
{
mr = ↦
mapfound++;
}
}
}
if (mapfound == 1) return mr;
return nullptr;
}
MapRecord *FindNextMap(MapRecord *thismap) MapRecord *FindNextMap(MapRecord *thismap)
{ {
if (thismap->nextLevel != -1) return FindMapByLevelNum(thismap->nextLevel); if (thismap->nextLevel != -1) return FindMapByLevelNum(thismap->nextLevel);

View file

@ -44,13 +44,8 @@ enum {
MAX_MESSAGES = 32 MAX_MESSAGES = 32
}; };
// Cutscene rules for maps are as follows:
// * when an episode is started, the episode intro will play, if none is defined, the map's intro will play.
// * when an episde is ended, the episode outro will play after the summary screen.
// * when a map ends, its own outro scene will play before the summary screen, if none is defined, use the default map outro handler.
// * when a new map starts after the summary screen, its own intro scene will play, if none is defined, use the default map intro handler.
// * setting any of these fields to 'none' will override any default and play nothing, even if a default is set.
class DObject; class DObject;
struct MapRecord;
struct CutsceneDef struct CutsceneDef
{ {
@ -60,6 +55,7 @@ struct CutsceneDef
int framespersec = 0; // only relevant for ANM. int framespersec = 0; // only relevant for ANM.
void Create(DObject* runner); void Create(DObject* runner);
bool Create(DObject* runner, MapRecord* map);
}; };
struct GlobalCutscenes struct GlobalCutscenes
@ -67,6 +63,7 @@ struct GlobalCutscenes
CutsceneDef Intro; CutsceneDef Intro;
CutsceneDef DefaultMapIntro; CutsceneDef DefaultMapIntro;
CutsceneDef DefaultMapOutro; CutsceneDef DefaultMapOutro;
CutsceneDef SharewareEnd;
FString MPSummaryScreen; FString MPSummaryScreen;
FString SummaryScreen; FString SummaryScreen;
}; };
@ -159,6 +156,7 @@ bool SetMusicForMap(const char* mapname, const char* music, bool namehack = fals
MapRecord *FindMapByName(const char *nm); MapRecord *FindMapByName(const char *nm);
MapRecord *FindMapByLevelNum(int num); MapRecord *FindMapByLevelNum(int num);
MapRecord* FindMapByClusterAndLevelNum(int clst, int num);
MapRecord *FindNextMap(MapRecord *thismap); MapRecord *FindNextMap(MapRecord *thismap);
MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic = nullptr); MapRecord* SetupUserMap(const char* boardfilename, const char *defaultmusic = nullptr);
MapRecord* AllocateMap(); MapRecord* AllocateMap();

View file

@ -96,6 +96,10 @@ void parseDefineCutscene(FScanner& sc, FScriptPosition& pos)
{ {
parseCutscene(sc, globalCutscenes.DefaultMapOutro); parseCutscene(sc, globalCutscenes.DefaultMapOutro);
} }
else if (sc.Compare("sharewareend")) // sets screens to be shown after the shareware version ends.
{
parseCutscene(sc, globalCutscenes.SharewareEnd);
}
else if (sc.Compare({ "episode", "volume", "cluster" })) else if (sc.Compare({ "episode", "volume", "cluster" }))
{ {
FScanner::SavedPos eblockend; FScanner::SavedPos eblockend;

View file

@ -62,6 +62,7 @@ static PType* maprecordtype;
static PType* summaryinfotype; static PType* summaryinfotype;
static CompletionFunc completion; static CompletionFunc completion;
static int ticks; static int ticks;
static SummaryInfo summaryinfo;
//============================================================================= //=============================================================================
// //
@ -133,6 +134,7 @@ void CallCreateFunction(const char* qname, DObject* runner)
void CallCreateMapFunction(const char* qname, DObject* runner, MapRecord* map) void CallCreateMapFunction(const char* qname, DObject* runner, MapRecord* map)
{ {
auto func = LookupFunction(qname); 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.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] != runnerclasstype && func->Proto->ArgumentTypes[1] != maprecordtype)
I_Error("Bad cutscene function %s. Must receive ScreenJobRunner and MapRecord reference.", qname); I_Error("Bad cutscene function %s. Must receive ScreenJobRunner and MapRecord reference.", qname);
@ -152,7 +154,8 @@ void CallCreateSummaryFunction(const char* qname, DObject* runner, MapRecord* ma
if (func->Proto->ArgumentTypes.Size() != 3) I_Error("Bad map-cutscene function %s. Must receive precisely three arguments.", qname); if (func->Proto->ArgumentTypes.Size() != 3) I_Error("Bad map-cutscene function %s. Must receive precisely three arguments.", qname);
if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != maprecordtype && func->Proto->ArgumentTypes[2] != summaryinfotype) if (func->Proto->ArgumentTypes[0] != runnerclasstype && func->Proto->ArgumentTypes[1] != maprecordtype && func->Proto->ArgumentTypes[2] != summaryinfotype)
I_Error("Bad cutscene function %s. Must receive ScreenJobRunner, MapRecord and SummaryInfo reference.", qname); I_Error("Bad cutscene function %s. Must receive ScreenJobRunner, MapRecord and SummaryInfo reference.", qname);
VMValue val[3] = { runner, map, info }; summaryinfo = *info; // must be copied to a persistent location.
VMValue val[3] = { runner, map, &summaryinfo };
VMCall(func, val, 3, nullptr, 0); VMCall(func, val, 3, nullptr, 0);
} }
@ -209,27 +212,28 @@ void CutsceneDef::Create(DObject* runner)
// //
//============================================================================= //=============================================================================
bool StartCutscene(CutsceneDef& cs, int flags, CompletionFunc completion_) bool CutsceneDef::Create(DObject* runner, MapRecord* map)
{ {
if (cs.function.CompareNoCase("none") != 0) if (function.CompareNoCase("none") == 0)
return true; // play nothing but return as being validated
if (function.IsNotEmpty())
{ {
completion = completion_; CallCreateMapFunction(function, runner, map);
runner = CreateRunner(); return true;
GC::WriteBarrier(runner); }
cs.Create(runner); else if (video.IsNotEmpty())
gameaction = (flags & SJ_BLOCKUI) ? ga_intro : ga_intermission; {
AddGenericVideo(runner, video, sound, framespersec);
return true; return true;
} }
return false; return false;
} }
bool StartCutscene(const char* s, int flags, CompletionFunc completion) //=============================================================================
{ //
CutsceneDef def; //
def.function = s; //
return StartCutscene(def, 0, completion); //=============================================================================
}
void DeleteScreenJob() void DeleteScreenJob()
{ {
@ -245,6 +249,12 @@ void EndScreenJob()
} }
//=============================================================================
//
//
//
//=============================================================================
bool ScreenJobResponder(event_t* ev) bool ScreenJobResponder(event_t* ev)
{ {
if (ev->type == EV_KeyDown) if (ev->type == EV_KeyDown)
@ -272,6 +282,12 @@ bool ScreenJobResponder(event_t* ev)
return false; return false;
} }
//=============================================================================
//
//
//
//=============================================================================
bool ScreenJobTick() bool ScreenJobTick()
{ {
ticks++; ticks++;
@ -289,12 +305,19 @@ bool ScreenJobTick()
return false; return false;
} }
//=============================================================================
//
//
//
//=============================================================================
void ScreenJobDraw() void ScreenJobDraw()
{ {
double smoothratio = I_GetTimeFrac(); double smoothratio = I_GetTimeFrac();
if (runner) if (runner)
{ {
twod->ClearScreen();
IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, RunFrame) IFVIRTUALPTRNAME(runner, NAME_ScreenJobRunner, RunFrame)
{ {
VMValue parm[] = { runner, smoothratio }; VMValue parm[] = { runner, smoothratio };
@ -303,6 +326,47 @@ void ScreenJobDraw()
} }
} }
//=============================================================================
//
//
//
//=============================================================================
bool StartCutscene(CutsceneDef& cs, int flags, const CompletionFunc& completion_)
{
if (cs.function.CompareNoCase("none") != 0)
{
completion = completion_;
runner = CreateRunner();
GC::WriteBarrier(runner);
try
{
cs.Create(runner);
}
catch (...)
{
runner->Destroy();
runner = nullptr;
throw;
}
gameaction = (flags & SJ_BLOCKUI) ? ga_intro : ga_intermission;
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) void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic)
{ {
@ -318,6 +382,74 @@ void PlayLogos(gameaction_t complete_ga, gameaction_t def_ga, bool stopmusic)
} }
} }
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void ShowScoreboard(int numplayers, const CompletionFunc& completion_)
{
completion = completion_;
runner = CreateRunner();
GC::WriteBarrier(runner);
const char* qname = globalCutscenes.MPSummaryScreen;
auto func = LookupFunction(qname);
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] != TypeSInt32)
I_Error("Bad cutscene function %s. Must receive ScreenJobRunner reference and integer.", qname);
VMValue val[2] = { runner, numplayers };
VMCall(func, val, 2, nullptr, 0);
gameaction = ga_intermission;
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void ShowIntermission(MapRecord* fromMap, MapRecord* toMap, SummaryInfo* info, CompletionFunc completion_)
{
completion = completion_;
runner = CreateRunner();
GC::WriteBarrier(runner);
// outro: first check the map's own outro.
// if that is empty, check the cluster's outro
// if that also fails, check the default outro
try
{
if (!fromMap->outro.Create(runner, fromMap))
{
auto& cluster = volumeList[fromMap->cluster - 1];
if ((toMap == nullptr || toMap->cluster != fromMap->cluster) && !cluster.outro.Create(runner, fromMap))
globalCutscenes.DefaultMapOutro.Create(runner, fromMap);
}
CallCreateSummaryFunction(globalCutscenes.SummaryScreen, runner, fromMap, info);
if (toMap)
{
if (!toMap->intro.Create(runner, fromMap))
globalCutscenes.DefaultMapIntro.Create(runner, fromMap);
}
else if (isShareware())
{
globalCutscenes.SharewareEnd.Create(runner);
}
gameaction = ga_intermission;
}
catch (...)
{
runner->Destroy();
runner = nullptr;
throw;
}
}
CCMD(testcutscene) CCMD(testcutscene)
{ {
@ -330,16 +462,19 @@ CCMD(testcutscene)
{ {
CutsceneDef def; CutsceneDef def;
def.function = argv[1]; def.function = argv[1];
if (StartCutscene(def, 0, [](bool) { })) if (StartCutscene(def, 0, [](bool) {}))
{ {
C_HideConsole(); C_HideConsole();
} }
} }
catch (const CRecoverableError& err) catch (const CRecoverableError& err)
{ {
Printf("Unable to play cutscene\n"); Printf(TEXTCOLOR_RED "Unable to play cutscene: %s\n", err.what());
} }
} }
/* /*
Duke: Duke:
if (!userConfig.nologo) fi.ShowLogo([](bool) { gameaction = ga_mainmenunostopsound; }); if (!userConfig.nologo) fi.ShowLogo([](bool) { gameaction = ga_mainmenunostopsound; });

View file

@ -201,6 +201,10 @@ bool ScreenJobTick();
void ScreenJobDraw(); void ScreenJobDraw();
struct CutsceneDef; struct CutsceneDef;
bool StartCutscene(CutsceneDef& cs, int flags, CompletionFunc completion); struct MapRecord;
bool StartCutscene(const char* s, int flags, CompletionFunc completion); 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 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_);

View file

@ -131,60 +131,5 @@ void InitFonts_d()
} }
//==========================================================================
//
// wrappers around DrawText to allow easier reuse of the old code.
// The vertical displacements are to have the same positioning as with the original code.
//
//==========================================================================
static void BigText(double x, double y, const char* text, int align = -1, double alpha = 1.)
{
if (align != -1)
x -= BigFont->StringWidth(text) * (align == 0 ? 0.5 : 1);
DrawText(twod, BigFont, CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_Alpha, alpha, TAG_DONE);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void dobonus_d(int bonusonly, const CompletionFunc& completion)
{
#if 0
TArray<DScreenJob*> jobs;
FX_StopAllSounds();
if (bonusonly < 0 && numplayers < 2 && ud.from_bonus == 0)
{
bonussequence_d(volfromlevelnum(currentLevel->levelNumber), jobs);
}
else
Mus_Stop();
if (playerswhenstarted > 1 && ud.coop != 1)
{
jobs.Push(Create<DDukeMultiplayerBonusScreen>(playerswhenstarted));
}
else if (bonusonly <= 0 && ud.multimode <= 1)
{
jobs.Push(Create<DDukeLevelSummaryScreen>());
}
if (jobs.Size())
{
RunScreenJob(jobs, completion);
}
else if (completion) completion(false);
#endif
}
void PrintPaused_d()
{
BigText(160, 100, GStrings("Game Paused"));
}
END_DUKE_NS END_DUKE_NS

View file

@ -123,119 +123,5 @@ void InitFonts_r()
} }
//==========================================================================
//
// wrappers around DrawText to allow easier reuse of the old code.
// The vertical displacements are to have the same positioning as with the original code.
//
//==========================================================================
static void BigText(double x, double y, const char* text, int align, double alpha = 1.)
{
//x *= 2.2; y *= 2.64;
if (align != -1)
x -= BigFont->StringWidth(text) * (align == 0 ? 0.2 : 0.4);
DrawText(twod, BigFont, CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, 0.4, DTA_ScaleY, 0.4, DTA_Alpha, alpha, TAG_DONE);
}
#if 0
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
static void bonussequence_r(int num, TArray<DScreenJob*>& jobs)
{
static const AnimSound turdmov[] =
{
{ 1, 82 + 1 },
{ -1, -1 }
};
static const AnimSound rr_outro[] =
{
{ 1, 35 + 1 },
{ -1, -1 }
};
static const int framespeed[] = { 9, 9, 9 }; // same for all 3 anims
Mus_Stop();
FX_StopAllSounds();
switch (num)
{
case 0:
jobs.Push(PlayVideo("turdmov.anm", turdmov, framespeed));
jobs.Push(Create<DImageScreen>(TENSCREEN));
break;
case 1:
jobs.Push(PlayVideo("rr_outro.anm", rr_outro, framespeed));
jobs.Push(Create<DImageScreen>(TENSCREEN));
break;
default:
break;
}
}
#endif
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
void dobonus_r(int bonusonly, const CompletionFunc& completion)
{
#if 0
TArray<DScreenJob*> jobs;
FX_StopAllSounds();
Mus_Stop();
if (bonusonly < 0 && !isRRRA() && numplayers < 2 && ud.from_bonus == 0)
{
int vol = volfromlevelnum(currentLevel->levelNumber);
bonussequence_r(vol, jobs);
}
if (playerswhenstarted > 1 && ud.coop != 1)
{
jobs.Push(Create<DRRMultiplayerBonusScreen>(playerswhenstarted));
}
else if (bonusonly <= 0 && ud.multimode <= 1)
{
if (isRRRA() && !(currentLevel->flags & MI_USERMAP) && currentLevel->levelNumber < 106) // fixme: The logic here is awful. Shift more control to the map records.
{
jobs.Push(Create<DRRLevelSummaryScreen>(true));
int levnum = clamp((currentLevel->levelNumber / 100) * 7 + (currentLevel->levelNumber % 100), 0, 13);
char fn[20];
mysnprintf(fn, 20, "lvl%d.anm", levnum + 1);
static const int framespeed[] = { 20, 20, 7200 }; // wait for one minute on the final frame so that the video doesn't stop before the user notices.
jobs.Push(PlayVideo(fn, nullptr, framespeed));
if (bonusonly < 0 && currentLevel->levelNumber > 100)
{
jobs.Push(Create<DRRRAEndOfGame>());
}
}
else jobs.Push(Create<DRRLevelSummaryScreen>(false));
}
if (jobs.Size())
RunScreenJob(jobs, completion);
else if (completion) completion(false);
#endif
}
void PrintPaused_r()
{
BigText(160, 100, GStrings("Game Paused"), 0);
}
END_DUKE_NS END_DUKE_NS

View file

@ -107,8 +107,6 @@ void animatesprites_r(int x, int y, int a, int smoothratio);
void InitFonts_d(); void InitFonts_d();
void InitFonts_r(); void InitFonts_r();
void PrintPaused_d();
void PrintPaused_r();
Dispatcher fi; Dispatcher fi;
@ -119,7 +117,6 @@ void SetDispatcher()
{ {
fi = { fi = {
InitFonts_d, InitFonts_d,
PrintPaused_d,
think_d, think_d,
initactorflags_d, initactorflags_d,
@ -165,7 +162,6 @@ void SetDispatcher()
{ {
fi = { fi = {
InitFonts_r, InitFonts_r,
PrintPaused_r,
think_r, think_r,
initactorflags_r, initactorflags_r,

View file

@ -72,7 +72,6 @@ struct Dispatcher
{ {
// global stuff // global stuff
void (*InitFonts)(); void (*InitFonts)();
void (*PrintPaused)();
// sectors_?.cpp // sectors_?.cpp
void (*think)(); void (*think)();

View file

@ -208,8 +208,6 @@ void OffBoat(player_struct *pl);
void cameratext(DDukeActor* i); void cameratext(DDukeActor* i);
void dobonus(int bonusonly, const CompletionFunc& completion); void dobonus(int bonusonly, const CompletionFunc& completion);
void dobonus_d(int bonusonly, const CompletionFunc& completion);
void dobonus_r(int bonusonly, const CompletionFunc& completion);
void drawoverlays(double smoothratio); void drawoverlays(double smoothratio);
void drawbackground(void); void drawbackground(void);

View file

@ -92,13 +92,12 @@ static void endthegame(bool)
void GameInterface::ExitFromMenu() void GameInterface::ExitFromMenu()
{ {
#if 0
// do we really need this scoreboard stuff here?
auto runbonus = [=](auto completion) auto runbonus = [=](auto completion)
{ {
// MP scoreboard // MP scoreboard
if (playerswhenstarted > 1 && !ud.coop) if (playerswhenstarted > 1 && !ud.coop) ShowScoreboard(playerswhenstarted);
{
dobonus(1, completion);
}
else completion(false); else completion(false);
}; };
@ -111,6 +110,11 @@ void GameInterface::ExitFromMenu()
}; };
runbonus([=](bool aborted) { runtwoscreens(endthegame); }); runbonus([=](bool aborted) { runtwoscreens(endthegame); });
#else
if (isShareware() && !isRR())
StartCutscene("DukeCutscenes.BuildSharewareExit", 0, endthegame);
else endthegame(false);
#endif
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -208,7 +212,7 @@ void V_AddBlend (float r, float g, float b, float a, float v_blend[4])
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// 'rest' in this case means everything not part of the 3D scene and its weapon sprite. //
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -297,7 +301,13 @@ void drawoverlays(double smoothratio)
} }
if (paused == 2) if (paused == 2)
fi.PrintPaused(); {
double x = 160, y = 100;
double scale = isRR() ? 0.4 : 1.;
const char* text = GStrings("Game Paused");
x -= BigFont->StringWidth(text) * 0.5 * scale;
DrawText(twod, BigFont, CR_UNTRANSLATED, x, y - 12, text, DTA_FullscreenScale, FSMode_Fit320x200, DTA_ScaleX, scale, DTA_ScaleY, scale, TAG_DONE);
}
} }
@ -342,18 +352,6 @@ void cameratext(DDukeActor *cam)
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void dobonus(int bonusonly, const CompletionFunc& completion)
{
if (isRR()) dobonus_r(bonusonly, completion);
else dobonus_d(bonusonly, completion);
}
//---------------------------------------------------------------------------
//
//
//
//---------------------------------------------------------------------------
int startrts(int lumpNum, int localPlayer) int startrts(int lumpNum, int localPlayer)
{ {
if (SoundEnabled() && if (SoundEnabled() &&

View file

@ -244,7 +244,7 @@ static void DoUserDef(bool bSet, int lVar1, int lLabelID, int lVar2, DDukeActor*
break; break;
case USERDEFS_VOLUME_NUMBER: case USERDEFS_VOLUME_NUMBER:
if (!bSet) SetGameVarID(lVar2, volfromlevelnum(currentLevel->levelNumber), sActor, sPlayer); if (!bSet) SetGameVarID(lVar2, currentLevel->cluster-1, sActor, sPlayer);
break; break;
case USERDEFS_MARKER: case USERDEFS_MARKER:
@ -3572,7 +3572,7 @@ int ParseState::parse(void)
{ {
insptr++; insptr++;
int music_select = *insptr++; int music_select = *insptr++;
auto level = FindMapByLevelNum(levelnum(currentLevel->levelNumber, music_select)); auto level = FindMapByLevelNum(levelnum(currentLevel->cluster, music_select));
if (level) S_PlayLevelMusic(level); if (level) S_PlayLevelMusic(level);
break; break;
} }

View file

@ -1040,29 +1040,32 @@ bool setnextmap(bool checksecretexit)
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
void exitlevel(MapRecord *nextlevel) void exitlevel(MapRecord* nextlevel)
{ {
bool endofgame = nextlevel == nullptr; bool endofgame = nextlevel == nullptr;
STAT_Update(endofgame); STAT_Update(endofgame);
StopCommentary(); StopCommentary();
dobonus(endofgame? -1 : 0, [=](bool) SummaryInfo info{};
{
// Clear potentially loaded per-map ART only after the bonus screens. info.kills = ps[0].actors_killed;
artClearMapArt(); info.maxkills = ps[0].max_actors_killed;
gameaction = ga_level; info.secrets = ps[0].secret_rooms;
ud.eog = false; info.maxsecrets = ps[0].max_secret_rooms;
if (endofgame) info.time = ps[0].player_par;
info.endofgame = endofgame;
Mus_Stop();
if (playerswhenstarted > 1 && ud.coop != 1)
{
// MP scoreboard
ShowScoreboard(playerswhenstarted, [=](bool)
{ {
if (ud.multimode < 2) // Clear potentially loaded per-map ART only after the bonus screens.
{ artClearMapArt();
if (isShareware()) gameaction = ga_level;
StartCutscene("DukeCutscenes.BuildSharewareOrder", 0, [](bool) { gameaction = ga_startup; }); ud.eog = false;
else gameaction = ga_startup; if (endofgame)
return;
}
else
{ {
auto nextlevel = FindMapByLevelNum(0); auto nextlevel = FindMapByLevelNum(0);
if (!nextlevel) if (!nextlevel)
@ -1072,11 +1075,22 @@ void exitlevel(MapRecord *nextlevel)
} }
else gameaction = ga_nextlevel; else gameaction = ga_nextlevel;
} }
} else
else gameaction = ga_nextlevel;
gameaction = ga_nextlevel;
});
}
else if (ud.multimode <= 1)
{
// SP cutscene + summary
ShowIntermission(currentLevel, nextlevel, &info, [=](bool)
{
// Clear potentially loaded per-map ART only after the bonus screens.
artClearMapArt();
ud.eog = false;
gameaction = endofgame? ga_startup : ga_nextlevel;
}); });
}
} }

View file

@ -49,5 +49,11 @@ definecutscene episode 5
} }
} }
definecutscene sharewareend
{
function DukeCutscenes.BuildSharewareEnd
}
definecutscene summary DukeCutscenes.BuildSPSummary definecutscene summary DukeCutscenes.BuildSPSummary
definecutscene mpsummary DukeCutscenes.BuildMPSummary definecutscene mpsummary DukeCutscenes.BuildMPSummary

View file

@ -1,4 +1,4 @@
// Cutscene definitions for Duke // Cutscene definitions for RR
definecutscene intro definecutscene intro
{ {
@ -24,3 +24,4 @@ definecutscene episode 2
definecutscene summary RRCutscenes.BuildSPSummary definecutscene summary RRCutscenes.BuildSPSummary
definecutscene mpsummary DukeCutscenes.BuildMPSummary // identical with Duke's definecutscene mpsummary DukeCutscenes.BuildMPSummary // identical with Duke's
definecutscene mapintro RRCutscenes.BuildMapIntro // this plays the 'travel' animation.

View file

@ -64,6 +64,7 @@ struct Duke native
fsmode = FSMode_Fit640x400; fsmode = FSMode_Fit640x400;
} }
if (align != -1) x -= SmallFont.StringWidth(t) * (align == 0 ? 0.5 : 1); if (align != -1) x -= SmallFont.StringWidth(t) * (align == 0 ? 0.5 : 1);
if (shade) Console.Printf("%s: shade = %d", t, shade);
Screen.DrawText(SmallFont, Font.CR_UNDEFINED, x, y + 2, t, DTA_FullscreenScale, fsmode, DTA_TranslationIndex, Translation.MakeID(Translation_Remap, trans), DTA_Color, Raze.shadeToLight(shade)); Screen.DrawText(SmallFont, Font.CR_UNDEFINED, x, y + 2, t, DTA_FullscreenScale, fsmode, DTA_TranslationIndex, Translation.MakeID(Translation_Remap, trans), DTA_Color, Raze.shadeToLight(shade));
} }

View file

@ -216,8 +216,7 @@ class DukeCutscenes // Note: must be class, not struct, otherwise we cannot easi
static void BuildSPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) static void BuildSPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats)
{ {
let screen = SummaryScreenBase(new("DukeLevelSummaryScreen").Init()); let screen = new("DukeLevelSummaryScreen").Init(map, stats);
if (screen) screen.SetParameters(map, stats);
runner.Append(screen); runner.Append(screen);
} }
@ -235,11 +234,11 @@ class DukeCutscenes // Note: must be class, not struct, otherwise we cannot easi
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// //
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
static void BuildSharewareOrder(ScreenJobRunner runner) static void BuildSharewareEnd(ScreenJobRunner runner)
{ {
runner.Append(ImageScreen.CreateNamed("ORDERING")); runner.Append(ImageScreen.CreateNamed("ORDERING"));
runner.Append(ImageScreen.CreateNamed("ORDERING1")); runner.Append(ImageScreen.CreateNamed("ORDERING1"));
@ -339,10 +338,7 @@ class RRCutscenes
static void BuildSPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats) static void BuildSPSummary(ScreenJobRunner runner, MapRecord map, SummaryInfo stats)
{ {
let sumscreen = new("RRLevelSummaryScreen").Init(!Raze.isRRRA() || stats.endOfGame); runner.Append(new("RRLevelSummaryScreen").Init(map, stats, !Raze.isRRRA() || stats.endOfGame));
let sumscreens = SummaryScreenBase(sumscreen);
if (sumscreens) sumscreens.SetParameters(map, stats);
runner.Append(sumscreen);
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------

View file

@ -57,7 +57,6 @@ class DRealmsScreen : SkippableScreenJob
let tex = TexMan.CheckForTexture("DREALMS"); let tex = TexMan.CheckForTexture("DREALMS");
int translation = TexMan.UseGamePalette(tex)? Translation.MakeID(Translation_BasePalette, Duke.DREALMSPAL) : 0; int translation = TexMan.UseGamePalette(tex)? Translation.MakeID(Translation_BasePalette, Duke.DREALMSPAL) : 0;
screen.ClearScreen();
screen.DrawTexture(tex, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal); screen.DrawTexture(tex, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_TranslationIndex, translation, DTA_LegacyRenderStyle, STYLE_Normal);
} }
} }
@ -119,8 +118,6 @@ class DukeTitleScreen : SkippableScreenJob
int clock = (ticks + smoothratio) * 120 / GameTicRate; int clock = (ticks + smoothratio) * 120 / GameTicRate;
int etrans = Translation.MakeID(Translation_BasePalette, Duke.TITLEPAL); int etrans = Translation.MakeID(Translation_BasePalette, Duke.TITLEPAL);
screen.ClearScreen();
// Only translate if the image depends on the global palette. // Only translate if the image depends on the global palette.
let tex = TexMan.CheckForTexture("BETASCREEN"); let tex = TexMan.CheckForTexture("BETASCREEN");
int trans = TexMan.UseGamePalette(tex)? etrans : 0; int trans = TexMan.UseGamePalette(tex)? etrans : 0;
@ -256,7 +253,6 @@ class Episode1End1 : SkippableScreenJob
{ {
int etrans = Translation.MakeID(Translation_BasePalette, Duke.ENDINGPAL); int etrans = Translation.MakeID(Translation_BasePalette, Duke.ENDINGPAL);
screen.ClearScreen();
let tex = TexMan.CheckForTexture("VICTORY1"); let tex = TexMan.CheckForTexture("VICTORY1");
int trans = TexMan.UseGamePalette(tex)? etrans : 0; int trans = TexMan.UseGamePalette(tex)? etrans : 0;
screen.DrawTexture(tex, false, 0, 50, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, trans, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TopLeft, true); screen.DrawTexture(tex, false, 0, 50, DTA_FullscreenScale, FSMode_Fit320x200, DTA_TranslationIndex, trans, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TopLeft, true);
@ -411,7 +407,6 @@ class Episode4Text : SkippableScreenJob
override void Draw(double sm) override void Draw(double sm)
{ {
Screen.ClearScreen();
Duke.BigText(160, 60, "$Thanks to all our", 0); Duke.BigText(160, 60, "$Thanks to all our", 0);
Duke.BigText(160, 60 + 16, "$fans for giving", 0); Duke.BigText(160, 60 + 16, "$fans for giving", 0);
Duke.BigText(160, 60 + 16 + 16, "$us big heads.", 0); Duke.BigText(160, 60 + 16 + 16, "$us big heads.", 0);
@ -474,7 +469,6 @@ class DukeMultiplayerBonusScreen : SkippableScreenJob
String tempbuf; String tempbuf;
int currentclock = int((ticks + smoothratio) * 120 / GameTicRate); int currentclock = int((ticks + smoothratio) * 120 / GameTicRate);
Screen.ClearScreen();
Screen.DrawTexture(TexMan.CheckForTexture("MENUSCREEN"), false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Color, 0xff808080, DTA_LegacyRenderStyle, STYLE_Normal); Screen.DrawTexture(TexMan.CheckForTexture("MENUSCREEN"), false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_Color, 0xff808080, DTA_LegacyRenderStyle, STYLE_Normal);
Screen.DrawTexture(TexMan.CheckForTexture("INGAMEDUKETHREEDEE"), true, 160, 34, DTA_FullscreenScale, FSMode_Fit320x200, DTA_CenterOffsetRel, true, DTA_ScaleX, titlescale, DTA_ScaleY, titlescale); Screen.DrawTexture(TexMan.CheckForTexture("INGAMEDUKETHREEDEE"), true, 160, 34, DTA_FullscreenScale, FSMode_Fit320x200, DTA_CenterOffsetRel, true, DTA_ScaleX, titlescale, DTA_ScaleY, titlescale);
if (Raze.isPlutoPak()) Screen.DrawTexture(TexMan.CheckForTexture("MENUPLUTOPAKSPRITE"), true, 260, 36, DTA_FullscreenScale, FSMode_Fit320x200, DTA_CenterOffsetRel, true); if (Raze.isPlutoPak()) Screen.DrawTexture(TexMan.CheckForTexture("MENUPLUTOPAKSPRITE"), true, 260, 36, DTA_FullscreenScale, FSMode_Fit320x200, DTA_CenterOffsetRel, true);
@ -581,11 +575,12 @@ class DukeLevelSummaryScreen : SummaryScreenBase
} }
ScreenJob Init() ScreenJob Init(MapRecord m, SummaryInfo s)
{ {
Super.Init(fadein | fadeout); Super.Init(fadein | fadeout);
int vol = level.volumeNum(); SetParameters(m, s);
String basetex = vol == 1? "BONUSSCREEN2" : "BONUSSCREEN"; int vol = level.cluster;
String basetex = vol == 2? "BONUSSCREEN2" : "BONUSSCREEN";
texBg = TexMan.CheckForTexture(basetex); texBg = TexMan.CheckForTexture(basetex);
for(int i = 0; i < 4; i++) for(int i = 0; i < 4; i++)
{ {
@ -737,10 +732,9 @@ class DukeLevelSummaryScreen : SummaryScreenBase
override void Draw(double sr) override void Draw(double sr)
{ {
Screen.ClearScreen();
Screen.DrawTexture(texBg, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); Screen.DrawTexture(texBg, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal);
Duke.GameText(160, 190, "$PRESSKEY", 8 - int(sin(ticks * 12 / GameTicRate) * 8), 0); Duke.GameText(160, 190, "$PRESSKEY", 8 - (sin(ticks * 4) * 8), 0);
if (displaystate & printTimeText) if (displaystate & printTimeText)
{ {
@ -785,15 +779,15 @@ class DukeLevelSummaryScreen : SummaryScreenBase
} }
} }
if (lastmapname) Duke.BigText(160, 20 - 6, lastmapname); if (lastmapname) Duke.BigText(160, 20 - 6, lastmapname, 0);
Duke.BigText(160, 36 - 6, "$Completed"); Duke.BigText(160, 36 - 6, "$Completed", 0);
} }
} }
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
// //
// TBD: fold this into DukeLevelSummaryScreen? //
// //
//--------------------------------------------------------------------------- //---------------------------------------------------------------------------
@ -819,9 +813,10 @@ class RRLevelSummaryScreen : SummaryScreenBase
} }
ScreenJob Init(bool dofadeout = true) ScreenJob Init(MapRecord m, SummaryInfo s, bool dofadeout = true)
{ {
Super.Init(dofadeout? (fadein | fadeout) : fadein); Super.Init(dofadeout? (fadein | fadeout) : fadein);
SetParameters(m, s);
String s; String s;
if (level.flags & MapRecord.USERMAP) if (level.flags & MapRecord.USERMAP)
s = "BONUSPIC01"; s = "BONUSPIC01";
@ -968,11 +963,10 @@ class RRLevelSummaryScreen : SummaryScreenBase
override void Draw(double sr) override void Draw(double sr)
{ {
Screen.ClearScreen();
Screen.DrawTexture(texBg, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); Screen.DrawTexture(texBg, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal);
if (lastmapname) Duke.BigText(80, 16, lastmapname, -1); if (lastmapname) Duke.BigText(80, 16, lastmapname, 0, 0);
Duke.BigText(15, 192, "$PRESSKEY", -1); Duke.BigText(15, 192, "$PRESSKEY", 0, 0);
if (displaystate & printTimeText) if (displaystate & printTimeText)
{ {
@ -1044,7 +1038,6 @@ class DukeLoadScreen : ScreenJob
override void Draw(double sr) override void Draw(double sr)
{ {
Screen.ClearScreen();
Screen.DrawTexture(TexMan.CheckForTexture("LOADSCREEN"), false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal); Screen.DrawTexture(TexMan.CheckForTexture("LOADSCREEN"), false, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal);
if (!Raze.IsRR()) if (!Raze.IsRR())

View file

@ -64,6 +64,7 @@ struct MapRecord native
native readonly int cdSongId; native readonly int cdSongId;
native readonly int flags; native readonly int flags;
native readonly int levelNumber; native readonly int levelNumber;
native readonly int cluster;
// The rest is only used by Blood // The rest is only used by Blood
native readonly int nextLevel; native readonly int nextLevel;
@ -81,10 +82,6 @@ struct MapRecord native
if (name == "") return labelName; if (name == "") return labelName;
return name; return name;
} }
int volumeNum()
{
return levelNumber / 1000;
}
} }
struct SummaryInfo native struct SummaryInfo native

View file

@ -121,7 +121,6 @@ class BlackScreen : ScreenJob
override void Draw(double smooth) override void Draw(double smooth)
{ {
cleared = true; cleared = true;
Screen.ClearScreen();
} }
} }
@ -180,7 +179,6 @@ class ImageScreen : SkippableScreenJob
override void Draw(double smooth) override void Draw(double smooth)
{ {
Screen.ClearScreen();
if (texid.IsValid()) Screen.DrawTexture(texid, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TranslationIndex, trans); if (texid.IsValid()) Screen.DrawTexture(texid, true, 0, 0, DTA_FullscreenEx, FSMode_ScaleToFit43, DTA_LegacyRenderStyle, STYLE_Normal, DTA_TranslationIndex, trans);
cleared = true; cleared = true;
} }
@ -392,7 +390,6 @@ class ScreenJobRunner : Object
{ {
if (jobs.Size() == 0) if (jobs.Size() == 0)
{ {
screen.ClearScreen();
return 1; return 1;
} }
int x = index >= jobs.Size()? jobs.Size()-1 : index; int x = index >= jobs.Size()? jobs.Size()-1 : index;
@ -498,7 +495,6 @@ class ScreenJobRunner : Object
if (actionState == State_Clear) if (actionState == State_Clear)
{ {
actionState = State_Run; actionState = State_Run;
Screen.ClearScreen();
} }
else if (actionState == State_Run) else if (actionState == State_Run)
{ {