From 38acd5a1774366b6ff59abaa57eee21614354c75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= <159546+serprex@users.noreply.github.com> Date: Sun, 16 Aug 2026 17:18:01 +0000 Subject: [PATCH] Intro Skip: adjust Link's position & camera to match values after watching intro (#7077) --- .../TimeSavers/SkipCutscene/SkipIntro.cpp | 31 ++++++++++++++++++- 1 file changed, 30 insertions(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/TimeSavers/SkipCutscene/SkipIntro.cpp b/soh/soh/Enhancements/TimeSavers/SkipCutscene/SkipIntro.cpp index 09bdd727e6..e8d94d9e42 100644 --- a/soh/soh/Enhancements/TimeSavers/SkipCutscene/SkipIntro.cpp +++ b/soh/soh/Enhancements/TimeSavers/SkipCutscene/SkipIntro.cpp @@ -3,6 +3,7 @@ #include "soh/ShipInit.hpp" extern "C" { +#include "macros.h" #include "z64save.h" #include "functions.h" #include "soh/Enhancements/randomizer/randomizer_entrance.h" @@ -10,6 +11,28 @@ extern PlayState* gPlayState; extern SaveContext gSaveContext; } +// Where intro leaves Link, instead of scene's spawn point (1, 0, 95) +static const Vec3f sIntroEndPos = { 0.0f, 0.0f, 60.0f }; + +static HOOK_ID sIntroSpawnHook = 0; + +// Puts Link where intro would've left him with top-down camera +static void SkipIntro_OnSceneSpawnActors() { + GameInteractor::Instance->UnregisterGameHook(sIntroSpawnHook); + sIntroSpawnHook = 0; + + if (gPlayState->sceneNum == SCENE_LINKS_HOUSE) { + Player* player = GET_PLAYER(gPlayState); + player->actor.world.pos = sIntroEndPos; + player->actor.prevPos = sIntroEndPos; + player->actor.home.pos = sIntroEndPos; + gPlayState->unk_1242B = 1; // VIEWPOINT_LOCKED + // Watching the intro leaves this visit on the cutscene layer, which stops func_80083108 + // from running & dimming the buttons. Zeroing cutsceneIndex loses that, so put it back + gSaveContext.sceneLayer = SCENE_LAYER_CUTSCENE_FIRST + 1; + } +} + void RegisterSkipIntro() { bool shouldRegister = CVarGetInteger(CVAR_ENHANCEMENT("TimeSavers.SkipCutscene.Intro"), 0) || IS_RANDO; COND_VB_SHOULD(VB_PLAY_TRANSITION_CS, shouldRegister, { @@ -41,8 +64,14 @@ void RegisterSkipIntro() { // Skip the intro cutscene for whatever the spawnEntrance is calculated to be. if (gSaveContext.entranceIndex == spawnEntrance) { gSaveContext.cutsceneIndex = 0; - if (!IS_RANDO) + if (!IS_RANDO) { gSaveContext.dayTime = 0x8000; + if (sIntroSpawnHook == 0) { + sIntroSpawnHook = + GameInteractor::Instance->RegisterGameHook( + SkipIntro_OnSceneSpawnActors); + } + } *should = false; } }