Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Mid-level cutscenes (both ACS and ZScript methods) #2725

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions src/common/cutscenes/screenjob.h
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ enum

struct CutsceneDef
{
FString cutsceneName;
FString video;
FString function;
FString soundName;
Expand All @@ -30,6 +31,7 @@ struct CutsceneDef

void Create(DObject* runner);
bool isdefined() { return video.IsNotEmpty() || function.IsNotEmpty(); }
const char* GetName() { return cutsceneName.GetChars(); }
int GetSound();
};

Expand Down
1 change: 1 addition & 0 deletions src/common/engine/gamestate.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ enum gamestate_t : int
GS_TITLELEVEL, // [RH] A combination of GS_LEVEL and GS_DEMOSCREEN
GS_INTRO,
GS_CUTSCENE,
GS_MIDLEVELCUTSCENE,

GS_MENUSCREEN = GS_DEMOSCREEN,

Expand Down
2 changes: 2 additions & 0 deletions src/d_event.h
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@ enum gameaction_t : int
ga_intro,
ga_intermission,
ga_titleloop,
ga_playmidlevelcutscene,
ga_endmidlevelcutscene,
};

extern gameaction_t gameaction;
Expand Down
1 change: 1 addition & 0 deletions src/d_main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1079,6 +1079,7 @@ void D_Display ()
break;

case GS_CUTSCENE:
case GS_MIDLEVELCUTSCENE:
case GS_INTRO:
ScreenJobDraw();
break;
Expand Down
17 changes: 14 additions & 3 deletions src/g_game.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -988,7 +988,7 @@ bool G_Responder (event_t *ev)
if (ev->type != EV_Mouse && primaryLevel->localEventManager->Responder(ev)) // [ZZ] ZScript ate the event // update 07.03.17: mouse events are handled directly
return true;

if (gamestate == GS_INTRO || gamestate == GS_CUTSCENE)
if (gamestate == GS_INTRO || gamestate == GS_CUTSCENE || gamestate == GS_MIDLEVELCUTSCENE)
{
return ScreenJobResponder(ev);
}
Expand Down Expand Up @@ -1238,8 +1238,17 @@ void G_Ticker ()
gameaction = ga_nothing;
C_HideConsole(); // On some systems, console is open during intro
break;


case ga_playmidlevelcutscene: // This should never be reached in multiplayer
buttonMap.ResetButtonStates(); // clear cmd building stuff
gamestate = GS_MIDLEVELCUTSCENE;
gameaction = ga_nothing;
break;
case ga_endmidlevelcutscene: // This should never be reached in multiplayer
buttonMap.ResetButtonStates(); // clear cmd building stuff
primaryLevel->SetMusic();
gamestate = GS_LEVEL;
gameaction = ga_nothing;
break;

default:
case ga_nothing:
Expand Down Expand Up @@ -1343,6 +1352,7 @@ void G_Ticker ()
break;

case GS_CUTSCENE:
case GS_MIDLEVELCUTSCENE:
case GS_INTRO:
if (ScreenJobTick())
{
Expand Down Expand Up @@ -2236,6 +2246,7 @@ CUSTOM_CVAR (Int, quicksaverotationcount, 4, CVAR_ARCHIVE|CVAR_GLOBALCONFIG)
if (self < 1)
self = 1;
}
CVAR (Bool, enablemidlevelcutscenes, true, CVAR_ARCHIVE | CVAR_GLOBALCONFIG)

void G_DoAutoSave ()
{
Expand Down
3 changes: 2 additions & 1 deletion src/g_game.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ struct event_t;
#include "dobjgc.h"
#include "name.h"
#include "gamestate.h"

#include "screenjob.h"

// wipegamestate can be set to -1
// to force a wipe on the next draw
Expand Down Expand Up @@ -108,6 +108,7 @@ FBaseCVar* G_GetUserCVar(int playernum, const char* cvarname);

class DIntermissionController;
struct level_info_t;
void RunMidLevelCutscene(level_info_t* inMap, CutsceneDef* inCutscene);
void RunIntermission(level_info_t* oldlevel, level_info_t* newlevel, DIntermissionController* intermissionScreen, DObject* statusScreen, std::function<void(bool)> completionf);

extern const AActor *SendItemUse, *SendItemDrop;
Expand Down
74 changes: 74 additions & 0 deletions src/g_level.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,7 @@ EXTERN_CVAR (Float, sv_gravity)
EXTERN_CVAR (Float, sv_aircontrol)
EXTERN_CVAR (Int, disableautosave)
EXTERN_CVAR (String, playerclass)
EXTERN_CVAR (Bool, enablemidlevelcutscenes)

extern uint8_t globalfreeze, globalchangefreeze;

Expand Down Expand Up @@ -1040,6 +1041,35 @@ DIntermissionController* FLevelLocals::CreateIntermission()
//
//=============================================================================

void RunMidLevelCutscene(level_info_t* inMap, CutsceneDef* inCutscene)
{
if (!multiplayer && !deathmatch) // single-player mode only
{
S_PauseMusic();
cutscene.runner = CreateRunner(false);
GC::WriteBarrier(cutscene.runner);
cutscene.completion = [](bool) { gameaction = ga_endmidlevelcutscene; };

CreateCutscene(inCutscene, cutscene.runner, inMap);

if (!ScreenJobValidate())
{
DeleteScreenJob();
if (cutscene.completion) cutscene.completion(false);
cutscene.completion = nullptr;
S_ChangeMusic(inMap->Music.GetChars(), inMap->musicorder);
return;
}
gameaction = ga_playmidlevelcutscene;
}
}

//=============================================================================
//
//
//
//=============================================================================

void RunIntermission(level_info_t* fromMap, level_info_t* toMap, DIntermissionController* intermissionScreen, DObject* statusScreen, std::function<void(bool)> completionf)
{
cutscene.runner = CreateRunner(false);
Expand Down Expand Up @@ -2245,6 +2275,50 @@ void FLevelLocals::Tick ()
//
//==========================================================================

void FLevelLocals::PlayMidLevelCutscene(const char *SceneName)
{
if (enablemidlevelcutscenes && info->cutscenes.Size() > 0 && gamestate == GS_LEVEL)
{
unsigned int iter;
for (iter = 0; iter < info->cutscenes.Size(); iter++)
{
if (strcmp(info->cutscenes[iter].GetName(), SceneName) == 0)
{
break;
}
}
if (iter < info->cutscenes.Size()) // Cutscene found
{
// Printf("Playing Cutscene %s ...", SceneName);
RunMidLevelCutscene(info, &info->cutscenes[iter]);
// Printf(" done.\n");
}
}
}

//==========================================================================
//
//
//==========================================================================

void FLevelLocals::PlayMidLevelCutsceneNum(int iter)
{
if (enablemidlevelcutscenes && info->cutscenes.Size() > 0 && iter > -1 && gamestate == GS_LEVEL)
{
if (iter < info->cutscenes.Size()) // Cutscene found
{
// Printf("Playing Cutscene %s ...", SceneName);
RunMidLevelCutscene(info, &info->cutscenes[iter]);
// Printf(" done.\n");
}
}
}

//==========================================================================
//
//
//==========================================================================

void FLevelLocals::Mark()
{
if (SectorMarker == nullptr && (sectors.Size() > 0 || Polyobjects.Size() > 0 || sides.Size() > 0))
Expand Down
3 changes: 3 additions & 0 deletions src/g_levellocals.h
Original file line number Diff line number Diff line change
Expand Up @@ -248,6 +248,9 @@ struct FLevelLocals
void Serialize(FSerializer &arc, bool hubload);
DThinker *FirstThinker (int statnum);

void PlayMidLevelCutscene(const char *SceneName);
void PlayMidLevelCutsceneNum(int iter);

// g_Game
void PlayerReborn (int player);
bool CheckSpot (int playernum, FPlayerStart *mthing);
Expand Down
9 changes: 9 additions & 0 deletions src/gamedata/g_mapinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -325,6 +325,7 @@ void level_info_t::Reset()
skyrotatevector2 = FVector3(0, 0, 1);
lightblendmode = ELightBlendMode::DEFAULT;
tonemap = ETonemapMode::None;
cutscenes.Clear();
}


Expand Down Expand Up @@ -805,6 +806,7 @@ void FMapInfoParser::ParseCutscene(CutsceneDef& cdef)
else if (sc.Compare("sound")) { ParseAssign(); sc.MustGetString(); cdef.soundName = sc.String; }
else if (sc.Compare("soundid")) { ParseAssign(); sc.MustGetNumber(); cdef.soundID = sc.Number; }
else if (sc.Compare("fps")) { ParseAssign(); sc.MustGetNumber(); cdef.framespersec = sc.Number; }
else if (sc.Compare("name")) { ParseAssign(); sc.MustGetString(); cdef.cutsceneName = sc.String; }
//else if (sc.Compare("transitiononly")) cdef.transitiononly = true;
else if (sc.Compare("delete")) { cdef.function = "none"; cdef.video = ""; } // this means 'play nothing', not 'not defined'.
else if (sc.Compare("clear")) cdef = {};
Expand Down Expand Up @@ -1732,6 +1734,13 @@ DEFINE_MAP_OPTION(outro, true)
parse.ParseCutscene(info->outro);
}

DEFINE_MAP_OPTION(cutscene, true)
{
CutsceneDef tempcutscene;
parse.ParseCutscene(tempcutscene);
info->cutscenes.Push(tempcutscene);
}


//==========================================================================
//
Expand Down
1 change: 1 addition & 0 deletions src/gamedata/g_mapinfo.h
Original file line number Diff line number Diff line change
Expand Up @@ -427,6 +427,7 @@ struct level_info_t
ETonemapMode tonemap;

CutsceneDef intro, outro;
TArray<CutsceneDef> cutscenes;


level_info_t()
Expand Down
2 changes: 1 addition & 1 deletion src/gamedata/statistics.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -595,7 +595,7 @@ CCMD(printstats)

CCMD(finishgame)
{
bool gamestatecheck = gamestate == GS_LEVEL || gamestate == GS_CUTSCENE;
bool gamestatecheck = gamestate == GS_LEVEL || gamestate == GS_CUTSCENE || gamestate == GS_MIDLEVELCUTSCENE;
if (!gamestatecheck)
{
Printf("Cannot use 'finishgame' while not in a game!\n");
Expand Down
35 changes: 35 additions & 0 deletions src/maploader/udmf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,19 @@ namespace
}
#define CHECK_N(f) if (!(namespace_bits&(f))) break;

static int P_GetCutsceneNum(FLevelLocals *lev, const char* SceneName)
{
unsigned int iter;
for (iter = 0; iter < lev->info->cutscenes.Size(); iter++)
{
if (strcmp(lev->info->cutscenes[iter].GetName(), SceneName) == 0)
{
break;
}
}
return (int)iter;
}

//===========================================================================
//
// Common parsing routines
Expand Down Expand Up @@ -821,6 +834,17 @@ class UDMFParser : public UDMFParserBase
{
th->args[1] = -FName(arg1str).GetIndex();
}
if (th->special == PlayCutscene)
{
if (arg0str.IsNotEmpty())
{
th->args[0] = P_GetCutsceneNum(Level, arg0str.GetChars());
}
if (arg1str.IsNotEmpty())
{
th->args[1] = P_GetCutsceneNum(Level, arg1str.GetChars());
}
}
// Thing specials are only valid in namespaces with Hexen-type specials
// and in ZDoomTranslated - which will use the translator on them.
if (namespc == NAME_ZDoomTranslated)
Expand Down Expand Up @@ -1238,6 +1262,17 @@ class UDMFParser : public UDMFParserBase
{
ld->args[1] = -FName(arg1str).GetIndex();
}
if (ld->special == PlayCutscene)
{
if (arg0str.IsNotEmpty())
{
ld->args[0] = P_GetCutsceneNum(Level, arg0str.GetChars());
}
if (arg1str.IsNotEmpty())
{
ld->args[1] = P_GetCutsceneNum(Level, arg1str.GetChars());
}
}
if ((ld->flags & ML_3DMIDTEX_IMPASS) && !(ld->flags & ML_3DMIDTEX)) // [TP]
{
Printf ("Line %d has midtex3dimpassible without midtex3d.\n", index);
Expand Down
26 changes: 26 additions & 0 deletions src/maploader/usdf.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -321,6 +321,8 @@ class USDFParser : public UDMFParserBase
FString SpeakerName;
FString Dialogue;
FString Goodbye;
FString CutsceneName;
int iter;

while (!sc.CheckToken('}'))
{
Expand All @@ -337,6 +339,30 @@ class USDFParser : public UDMFParserBase
node->ThisNodeName = CheckString(key);
break;

case NAME_Cutscenenum:
if (namespace_bits != Gz)
sc.ScriptMessage("'CutsceneNum' keyword only supported in the GZDoom namespace!");
else
node->CutsceneNum = CheckInt(key);
break;

case NAME_Cutscenename:
if (namespace_bits != Gz)
sc.ScriptMessage("'CutsceneName' keyword only supported in the GZDoom namespace!");
else
{
CutsceneName = CheckString(key);
for (iter = 0; iter < Level->info->cutscenes.Size(); iter++)
{
if (strcmp(Level->info->cutscenes[iter].GetName(), CutsceneName.GetChars()) == 0)
{
break;
}
}
node->CutsceneNum = iter;
}
break;

case NAME_Name:
SpeakerName = CheckString(key);
break;
Expand Down
2 changes: 2 additions & 0 deletions src/namedef_custom.h
Original file line number Diff line number Diff line change
Expand Up @@ -856,6 +856,8 @@ xx(Exclude)
xx(Userstring)
xx(Sky)
xx(Pagename)
xx(Cutscenename)
xx(Cutscenenum)

// Lightmap/ZDRay keywords
xx(lm_sampledist)
Expand Down
9 changes: 8 additions & 1 deletion src/p_conversation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,7 @@ static void PickConversationReply (int replyindex);
static void TerminalResponse (const char *str);

CVAR(Bool, dlg_vgafont, false, CVAR_ARCHIVE)
EXTERN_CVAR (Bool, enablemidlevelcutscenes)

//============================================================================
//
Expand Down Expand Up @@ -394,8 +395,13 @@ void P_StartConversation (AActor *npc, AActor *pc, bool facetalker, bool saveang
}
}

if (enablemidlevelcutscenes && CurNode->CutsceneNum > -1)
{
Level->PlayMidLevelCutsceneNum(CurNode->CutsceneNum);
CurNode->CutsceneNum = -1; // Don't ever play this again
}
// [Nash] Play voice clip from the actor so that positional audio can be heard by all players
if (CurNode->SpeakerVoice != NO_SOUND) S_Sound(npc, CHAN_VOICE, CHANF_NOPAUSE, CurNode->SpeakerVoice, 1, ATTN_NORM);
else if (CurNode->SpeakerVoice != NO_SOUND) S_Sound(npc, CHAN_VOICE, CHANF_NOPAUSE, CurNode->SpeakerVoice, 1, ATTN_NORM);

// The rest is only done when the conversation is actually displayed.
if (pc->player == Level->GetConsolePlayer())
Expand Down Expand Up @@ -740,6 +746,7 @@ DEFINE_FIELD(FStrifeDialogueNode, ItemCheckNode);
DEFINE_FIELD(FStrifeDialogueNode, SpeakerType);
DEFINE_FIELD(FStrifeDialogueNode, SpeakerName);
DEFINE_FIELD(FStrifeDialogueNode, SpeakerVoice);
DEFINE_FIELD(FStrifeDialogueNode, CutsceneNum);
DEFINE_FIELD(FStrifeDialogueNode, Backdrop);
DEFINE_FIELD(FStrifeDialogueNode, Dialogue);
DEFINE_FIELD(FStrifeDialogueNode, Goodbye);
Expand Down
1 change: 1 addition & 0 deletions src/p_conversation.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ struct FStrifeDialogueNode
PClassActor *SpeakerType = nullptr;
FString SpeakerName;
FSoundID SpeakerVoice = NO_SOUND;
int CutsceneNum = -1; // cutscene index in Level->info->cutscenes array
FString Backdrop;
FString Dialogue;
FString Goodbye; // must init to null for binary scripts to work as intended
Expand Down
Loading