Add a few initial cutscene skips (#399)

This commit is contained in:
Garrett Cox 2024-05-19 22:07:54 -05:00
parent 3462b7cb87
commit 72625182a4
17 changed files with 256 additions and 20 deletions

View File

@ -361,6 +361,9 @@ void DrawEnhancementsMenu() {
UIWidgets::CVarCheckbox(
"Skip to File Select", "gEnhancements.Cutscenes.SkipToFileSelect",
{ .tooltip = "Skip the opening title sequence and go straight to the file select menu after boot" });
UIWidgets::CVarCheckbox("Skip Intro Sequence", "gEnhancements.Cutscenes.SkipIntroSequence");
UIWidgets::CVarCheckbox("Skip Story Cutscenes", "gEnhancements.Cutscenes.SkipStoryCutscenes");
UIWidgets::CVarCheckbox("Skip Misc Interactions", "gEnhancements.Cutscenes.SkipMiscInteractions");
ImGui::EndMenu();
}

View File

@ -0,0 +1,15 @@
#include "Cutscenes.h"
void RegisterCutscenes() {
// MiscInteractions
RegisterSkipDekuSalesman();
RegisterSkipScarecrowDance();
RegisterSkipTatlInterrupts();
// StoryCutscenes
RegisterSkipClockTowerOpen();
RegisterHideTitleCards();
RegisterSkipEntranceCutscenes();
RegisterSkipIntroSequence();
}

View File

@ -0,0 +1,18 @@
#ifndef CUTSCENES_H
#define CUTSCENES_H
void RegisterCutscenes();
// MiscInteractions
void RegisterSkipDekuSalesman();
void RegisterSkipScarecrowDance();
void RegisterSkipTatlInterrupts();
// StoryCutscenes
void RegisterSkipClockTowerOpen();
void RegisterHideTitleCards();
void RegisterSkipEntranceCutscenes();
void RegisterSkipIntroSequence();
#endif // CUTSCENES_H

View File

@ -1,6 +0,0 @@
#ifndef CUTSCENES_HIDE_TITLE_CARDS_H
#define CUTSCENES_HIDE_TITLE_CARDS_H
void RegisterHideTitleCards();
#endif // CUTSCENES_HIDE_TITLE_CARDS_H

View File

@ -0,0 +1,17 @@
#include <libultraship/bridge.h>
#include "Enhancements/GameInteractor/GameInteractor.h"
extern "C" {
#include "z64.h"
extern SaveContext gSaveContext;
void Flags_SetWeekEventReg(s32 flag);
}
void RegisterSkipDekuSalesman() {
// prevents him from doing his "fly in" animation
GameInteractor::Instance->RegisterGameHookForID<GameInteractor::ShouldActorInit>(ACTOR_EN_SELLNUTS, [](Actor* actor, bool* should) {
if (CVarGetInteger("gEnhancements.Cutscenes.SkipMiscInteractions", 0) && !CHECK_WEEKEVENTREG(WEEKEVENTREG_73_04)) {
SET_WEEKEVENTREG(WEEKEVENTREG_73_04);
}
});
}

View File

@ -0,0 +1,37 @@
#include <libultraship/bridge.h>
#include "Enhancements/GameInteractor/GameInteractor.h"
extern "C" {
#include "z64.h"
#include "src/overlays/actors/ovl_En_Kakasi/z_en_kakasi.h"
}
void RegisterSkipScarecrowDance() {
// Skips to end of "passing time" dance with the scarecrow
GameInteractor::Instance->RegisterGameHookForID<GameInteractor::OnActorInit>(ACTOR_EN_KAKASI, [](Actor* outerActor) {
static uint32_t enKakasiUpdateHook = 0;
static uint32_t enKakasiKillHook = 0;
GameInteractor::Instance->UnregisterGameHookForPtr<GameInteractor::OnActorUpdate>(enKakasiUpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(enKakasiKillHook);
enKakasiUpdateHook = 0;
enKakasiKillHook = 0;
enKakasiUpdateHook = GameInteractor::Instance->RegisterGameHookForPtr<GameInteractor::OnActorUpdate>((uintptr_t)outerActor, [](Actor* actor) {
EnKakasi* enKakasi = (EnKakasi*)actor;
if (
CVarGetInteger("gEnhancements.Cutscenes.SkipMiscInteractions", 0) &&
enKakasi->actionFunc == EnKakasi_DancingNightAway &&
enKakasi->unk190 == 0
) {
enKakasi->unk190 = 13;
enKakasi->unk204 = 0;
}
});
enKakasiKillHook = GameInteractor::Instance->RegisterGameHook<GameInteractor::OnSceneInit>([](s8 sceneId, s8 spawnNum) {
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnActorUpdate>(enKakasiUpdateHook);
GameInteractor::Instance->UnregisterGameHook<GameInteractor::OnSceneInit>(enKakasiKillHook);
enKakasiUpdateHook = 0;
enKakasiKillHook = 0;
});
});
}

View File

@ -0,0 +1,89 @@
#include <libultraship/bridge.h>
#include "Enhancements/GameInteractor/GameInteractor.h"
#define ELFMSG3_GET_SWITCH_FLAG(thisx) (((thisx)->params & 0x7F00) >> 8)
extern "C" {
#include "z64.h"
extern PlayState* gPlayState;
extern SaveContext gSaveContext;
extern u32 gBitFlags[];
void Flags_SetWeekEventReg(s32 flag);
void Flags_SetSwitch(PlayState* play, s32 flag);
void Actor_Kill(Actor* actor);
}
void RegisterSkipTatlInterrupts() {
// First time entering Clock Town Interupt
REGISTER_VB_SHOULD(GI_VB_PLAY_TRANSITION_CS, {
if (
gSaveContext.save.entrance == ENTRANCE(SOUTH_CLOCK_TOWN, 0) &&
gSaveContext.save.cutsceneIndex == 0 &&
!CHECK_WEEKEVENTREG(WEEKEVENTREG_59_04) &&
CVarGetInteger("gEnhancements.Cutscenes.SkipMiscInteractions", 0)
) {
Flags_SetWeekEventReg(WEEKEVENTREG_59_04);
}
});
// General interupt
REGISTER_VB_SHOULD(GI_VB_TATL_INTERUPT_MSG3, {
if (CVarGetInteger("gEnhancements.Cutscenes.SkipMiscInteractions", 0) && *should) {
Actor* actor = static_cast<Actor*>(opt);
*should = false;
if (ELFMSG3_GET_SWITCH_FLAG(actor) != 0x7F) {
Flags_SetSwitch(gPlayState, ELFMSG3_GET_SWITCH_FLAG(actor));
}
Actor_Kill(actor);
}
});
// General interupt 2 (the flags were directly copied from the original code)
REGISTER_VB_SHOULD(GI_VB_TATL_INTERUPT_MSG6, {
if (CVarGetInteger("gEnhancements.Cutscenes.SkipMiscInteractions", 0) && *should) {
Actor* actor = static_cast<Actor*>(opt);
*should = false;
switch (actor->textId) {
case 0x224:
SET_WEEKEVENTREG(WEEKEVENTREG_79_10);
break;
case 0x25B:
SET_WEEKEVENTREG(WEEKEVENTREG_88_20);
break;
case 0x216:
SET_WEEKEVENTREG(WEEKEVENTREG_31_04);
break;
case 0x231:
SET_WEEKEVENTREG(WEEKEVENTREG_31_01);
break;
case 0x232:
SET_WEEKEVENTREG(WEEKEVENTREG_31_02);
break;
case 0x233:
SET_WEEKEVENTREG(WEEKEVENTREG_80_04);
break;
}
if (CHECK_QUEST_ITEM(QUEST_REMAINS_ODOLWA)) {
SET_WEEKEVENTREG(WEEKEVENTREG_87_10);
}
if (CHECK_QUEST_ITEM(QUEST_REMAINS_GOHT)) {
SET_WEEKEVENTREG(WEEKEVENTREG_87_20);
}
if (CHECK_QUEST_ITEM(QUEST_REMAINS_GYORG)) {
SET_WEEKEVENTREG(WEEKEVENTREG_87_40);
}
if (CHECK_QUEST_ITEM(QUEST_REMAINS_TWINMOLD)) {
SET_WEEKEVENTREG(WEEKEVENTREG_87_80);
}
Actor_Kill(actor);
}
});
}

View File

@ -1,6 +0,0 @@
#ifndef CUTSCENES_SKIP_ENTRANCE_CUTSCENES_H
#define CUTSCENES_SKIP_ENTRANCE_CUTSCENES_H
void RegisterSkipEntranceCutscenes();
#endif // CUTSCENES_SKIP_ENTRANCE_CUTSCENES_H

View File

@ -0,0 +1,28 @@
#include <libultraship/bridge.h>
#include "Enhancements/GameInteractor/GameInteractor.h"
extern "C" {
#include "z64.h"
extern PlayState* gPlayState;
extern SaveContext gSaveContext;
}
void RegisterSkipIntroSequence() {
REGISTER_VB_SHOULD(GI_VB_PLAY_TRANSITION_CS, {
// Intro cutscene
if (!(gSaveContext.save.entrance == ENTRANCE(CUTSCENE, 0) && gSaveContext.save.cutsceneIndex == 0)) return;
if (CVarGetInteger("gEnhancements.Cutscenes.SkipIntroSequence", 0)) {
gSaveContext.save.entrance = ENTRANCE(SOUTH_CLOCK_TOWN, 0);
gSaveContext.save.hasTatl = true;
gSaveContext.cycleSceneFlags[SCENE_INSIDETOWER].switch0 |= (1 << 0);
gSaveContext.cycleSceneFlags[SCENE_OPENINGDAN].switch0 |= (1 << 2);
gSaveContext.cycleSceneFlags[SCENE_OPENINGDAN].switch0 |= (1 << 0);
gSaveContext.save.playerForm = PLAYER_FORM_DEKU;
// Mark chest as opened and give the player the Deku Nuts
gSaveContext.cycleSceneFlags[SCENE_OPENINGDAN].chest |= (1 << 0);
Item_Give(gPlayState, ITEM_DEKU_NUTS_10);
}
});
}

View File

@ -0,0 +1,24 @@
#include <libultraship/bridge.h>
#include "Enhancements/GameInteractor/GameInteractor.h"
extern "C" {
#include "z64.h"
void Flags_SetWeekEventReg(s32 flag);
extern SaveContext gSaveContext;
}
void RegisterSkipClockTowerOpen() {
REGISTER_VB_SHOULD(GI_VB_PLAY_TRANSITION_CS, {
// Cutscene where clock tower opens. Will only show if the player is in Clock Town
if (
ENTRANCE(SOUTH_CLOCK_TOWN, 0) &&
gSaveContext.save.cutsceneIndex == 0xFFF1 &&
CVarGetInteger("gEnhancements.Cutscenes.SkipStoryCutscenes", 0)
) {
// Setting the respawn flag to 2 will respawn the player where they were before the cutscene, consistent with the normal behavior
gSaveContext.respawnFlag = 2;
gSaveContext.save.cutsceneIndex = 0;
SET_WEEKEVENTREG(WEEKEVENTREG_CLOCK_TOWER_OPENED);
}
});
}

View File

@ -33,8 +33,7 @@ void InitEnhancements() {
RegisterTatlISG();
// Cutscenes
RegisterSkipEntranceCutscenes();
RegisterHideTitleCards();
RegisterCutscenes();
// Modes
RegisterPlayAsKafei();

View File

@ -12,8 +12,7 @@
#include "Masks/NoBlastMaskCooldown.h"
#include "Masks/FastTransformation.h"
#include "Minigames/AlwaysWinDoggyRace.h"
#include "Cutscenes/SkipEntranceCutscenes.h"
#include "Cutscenes/HideTitleCards.h"
#include "Cutscenes/Cutscenes.h"
#include "Restorations/PowerCrouchStab.h"
#include "Restorations/SideRoll.h"
#include "Restorations/TatlISG.h"

View File

@ -44,6 +44,10 @@ typedef enum {
GI_VB_PREVENT_CLOCK_DISPLAY,
GI_VB_SONG_AVAILABLE_TO_PLAY,
GI_VB_USE_CUSTOM_CAMERA,
GI_VB_PLAY_TRANSITION_CS,
GI_VB_TATL_INTERUPT_MSG3,
GI_VB_TATL_INTERUPT_MSG6
} GIVanillaBehavior;
#ifdef __cplusplus

View File

@ -1573,7 +1573,15 @@ void Cutscene_HandleEntranceTriggers(PlayState* play) {
}
}
// 2S2H [Enhancements] This is equivalent to Cutscene_HandleConditionalTriggers in OoT. It's unused normally, but we
// use it for VB. In VB changing *should to false has no effect, but you instead modify the entrance & cutsceneIndex to
// override the behavior.
void func_800EDDB0(PlayState* play) {
LUSLOG_INFO("Cutscene_HandleConditionalTriggers: entrance: %d, cutsceneIndex: 0x%X", gSaveContext.save.entrance, gSaveContext.save.cutsceneIndex);
if (!GameInteractor_Should(GI_VB_PLAY_TRANSITION_CS, true, NULL)) {
return;
}
}
void func_800EDDBC(UNK_TYPE arg0, UNK_TYPE arg1) {

View File

@ -6,6 +6,7 @@
#include "z_elf_msg3.h"
#include "overlays/actors/ovl_En_Elf/z_en_elf.h"
#include "2s2h/Enhancements/GameInteractor/GameInteractor.h"
#define FLAGS (ACTOR_FLAG_10)
@ -104,11 +105,11 @@ void func_80A2CF7C(ElfMsg3* this, PlayState* play) {
Player* player = GET_PLAYER(play);
EnElf* tatl = (EnElf*)player->tatlActor;
if (((((player->tatlActor != NULL) &&
if (GameInteractor_Should(GI_VB_TATL_INTERUPT_MSG3, (((((player->tatlActor != NULL) &&
(fabsf(player->actor.world.pos.x - this->actor.world.pos.x) < (100.0f * this->actor.scale.x))) &&
(this->actor.world.pos.y <= player->actor.world.pos.y)) &&
((player->actor.world.pos.y - this->actor.world.pos.y) < (100.0f * this->actor.scale.y))) &&
(fabsf(player->actor.world.pos.z - this->actor.world.pos.z) < (100.0f * this->actor.scale.z))) {
(fabsf(player->actor.world.pos.z - this->actor.world.pos.z) < (100.0f * this->actor.scale.z))), this)) {
player->tatlTextId = func_80A2CF50(this);
CutsceneManager_Queue(CS_ID_GLOBAL_TALK);
tatl->elfMsg = &this->actor;

View File

@ -6,6 +6,7 @@
#include "z_elf_msg6.h"
#include "overlays/actors/ovl_En_Elf/z_en_elf.h"
#include "2s2h/Enhancements/GameInteractor/GameInteractor.h"
#define FLAGS (ACTOR_FLAG_10)
@ -236,8 +237,8 @@ void ElfMsg6_Destroy(Actor* thisx, PlayState* play) {
}
s32 func_80BA1C00(ElfMsg6* this) {
return (this->actor.xzDistToPlayer < (100.0f * this->actor.scale.x)) &&
((this->actor.playerHeightRel >= 0.0f) && (this->actor.playerHeightRel < (100.0f * this->actor.scale.y)));
return GameInteractor_Should(GI_VB_TATL_INTERUPT_MSG6, ((this->actor.xzDistToPlayer < (100.0f * this->actor.scale.x)) &&
((this->actor.playerHeightRel >= 0.0f) && (this->actor.playerHeightRel < (100.0f * this->actor.scale.y)))), this);
}
void func_80BA1C88(ElfMsg6* this, PlayState* play, s16 arg2) {

View File

@ -1,7 +1,9 @@
#ifndef Z_EN_KAKASI_H
#define Z_EN_KAKASI_H
#ifndef __cplusplus
#include "global.h"
#endif
#include "z64snap.h"
struct EnKakasi;
@ -46,4 +48,7 @@ typedef struct EnKakasi {
#define KAKASI_GET_ABOVE_GROUND(thisx) ((thisx)->params & 0x1)
#define KAKASI_GET_TARGETMODE(thisx) ((thisx)->world.rot.x - 1)
// 2S2H [Enhancements] Externed for time savers
void EnKakasi_DancingNightAway(EnKakasi* thisx, PlayState* play);
#endif // Z_EN_KAKASI_H