From d1dd113fe4e528b20b10aad58560ad1d3ca4f6af Mon Sep 17 00:00:00 2001 From: Garrett Cox Date: Thu, 23 May 2024 19:40:48 -0500 Subject: [PATCH] Add fast ocarina playback enhancement Co-authored-by: Patrick12115 <115201185+Patrick12115@users.noreply.github.com> --- mm/2s2h/BenGui/BenMenuBar.cpp | 2 ++ mm/src/code/z_message.c | 20 +++++++++-- .../actors/ovl_Eff_Change/z_eff_change.c | 18 ++++++---- .../overlays/actors/ovl_En_Test6/z_en_test6.c | 12 +++++++ .../overlays/actors/ovl_En_Test7/z_en_test7.c | 18 ++++++++-- .../actors/ovl_En_Torch2/z_en_torch2.c | 6 +++- .../actors/ovl_Oceff_Storm/z_oceff_storm.c | 34 ++++++++++-------- .../actors/ovl_Oceff_Wipe5/z_oceff_wipe5.c | 3 +- .../actors/ovl_player_actor/z_player.c | 36 ++++++++++++++++--- 9 files changed, 117 insertions(+), 32 deletions(-) diff --git a/mm/2s2h/BenGui/BenMenuBar.cpp b/mm/2s2h/BenGui/BenMenuBar.cpp index 5cea0a4439..d96dfb7cb0 100644 --- a/mm/2s2h/BenGui/BenMenuBar.cpp +++ b/mm/2s2h/BenGui/BenMenuBar.cpp @@ -570,6 +570,8 @@ void DrawEnhancementsMenu() { { .tooltip = "Enables using the Dpad for Ocarina playback." }); UIWidgets::CVarCheckbox("Prevent Dropped Ocarina Inputs", "gEnhancements.Playback.NoDropOcarinaInput", { .tooltip = "Prevent dropping inputs when playing the ocarina quickly" }); + UIWidgets::CVarCheckbox("Faster song playbacks", "gEnhancements.Playback.FastSongPlayback", + { .tooltip = "Makes song playback faster" }); ImGui::EndMenu(); } diff --git a/mm/src/code/z_message.c b/mm/src/code/z_message.c index e5696d12ac..6822e22a51 100644 --- a/mm/src/code/z_message.c +++ b/mm/src/code/z_message.c @@ -4025,7 +4025,13 @@ void Message_SpawnSongEffect(PlayState* play) { (msgCtx->songPlayed != OCARINA_SONG_GORON_LULLABY_INTRO) && !((msgCtx->ocarinaAction >= OCARINA_ACTION_PROMPT_WIND_FISH_HUMAN) && (msgCtx->ocarinaAction <= OCARINA_ACTION_PROMPT_WIND_FISH_DEKU))) { - msgCtx->ocarinaSongEffectActive = true; + if (CVarGetInteger( + "gEnhancements.Playback.FastSongPlayback", + 0)) { // Set to false to allow subsequent ocarina actions without considering this effect as active + msgCtx->ocarinaSongEffectActive = false; + } else { + msgCtx->ocarinaSongEffectActive = true; + } if (msgCtx->songPlayed != OCARINA_SONG_SCARECROW_SPAWN) { Actor_Spawn(&play->actorCtx, play, sOcarinaEffectActorIds[msgCtx->songPlayed], player->actor.world.pos.x, player->actor.world.pos.y, player->actor.world.pos.z, 0, 0, 0, @@ -4765,7 +4771,10 @@ void Message_DrawMain(PlayState* play, Gfx** gfxP) { AudioOcarina_SetPlaybackSong(msgCtx->ocarinaAction - OCARINA_ACTION_SCARECROW_LONG_RECORDING, 1); } else { AudioOcarina_SetInstrument(sPlayerFormOcarinaInstruments[CUR_FORM]); - AudioOcarina_SetPlaybackSong((u8)msgCtx->songPlayed + 1, 1); + if (!CVarGetInteger("gEnhancements.Playback.FastSongPlayback", + 0)) { // Skips the visual note playback after playing a song + AudioOcarina_SetPlaybackSong((u8)msgCtx->songPlayed + 1, 1); + } if (msgCtx->songPlayed != OCARINA_SONG_SCARECROW_SPAWN) { Audio_PlayFanfareWithPlayerIOPort7((u16)sOcarinaSongFanfares[msgCtx->songPlayed], (u8)sOcarinaSongFanfareIoData[CUR_FORM]); @@ -4791,7 +4800,12 @@ void Message_DrawMain(PlayState* play, Gfx** gfxP) { } Message_Decode(play); msgCtx->msgMode = MSGMODE_16; - msgCtx->stateTimer = 20; + if (CVarGetInteger("gEnhancements.Playback.FastSongPlayback", + 0)) { // Speeds up the time it shows the song name after playback + msgCtx->stateTimer = 1; + } else { + msgCtx->stateTimer = 20; + } Message_DrawText(play, &gfx); break; diff --git a/mm/src/overlays/actors/ovl_Eff_Change/z_eff_change.c b/mm/src/overlays/actors/ovl_Eff_Change/z_eff_change.c index 2e0c7b1346..286d7fc6f2 100644 --- a/mm/src/overlays/actors/ovl_Eff_Change/z_eff_change.c +++ b/mm/src/overlays/actors/ovl_Eff_Change/z_eff_change.c @@ -56,7 +56,10 @@ void EffChange_Init(Actor* thisx, PlayState* play) { Keyframe_FlexPlayOnce(&this->skeletonInfo, gGameplayKeepKFAnim_281DC); this->step = 0; this->actor.shape.rot.y = 0; - this->skeletonInfo.frameCtrl.speed = (2.0f / 3.0f); + this->skeletonInfo.frameCtrl.speed = + (2.0f / 3.0f) + (CVarGetInteger("gEnhancements.Playback.FastSongPlayback", 0) + ? 1.0f + : 0.0f); // Speeds up the spawning of the statue to ensure it keeps the switch pressed down CutsceneManager_Queue(CS_ID_GLOBAL_ELEGY); } @@ -110,11 +113,14 @@ void func_80A4C5CC(EffChange* this, PlayState* play) { phi_fv0 = 0.0f; } Environment_AdjustLights(play, phi_fv0, 850.0f, 0.2f, 0.0f); - if (CutsceneManager_GetCurrentCsId() != CS_ID_GLOBAL_ELEGY) { - if (CutsceneManager_IsNext(CS_ID_GLOBAL_ELEGY)) { - CutsceneManager_Start(CS_ID_GLOBAL_ELEGY, &this->actor); - } else { - CutsceneManager_Queue(CS_ID_GLOBAL_ELEGY); + if (!CVarGetInteger("gEnhancements.Playback.FastSongPlayback", + 0)) { // skips the elegy cutscene, allowing you to instanly move away from the statue + if (CutsceneManager_GetCurrentCsId() != CS_ID_GLOBAL_ELEGY) { + if (CutsceneManager_IsNext(CS_ID_GLOBAL_ELEGY)) { + CutsceneManager_Start(CS_ID_GLOBAL_ELEGY, &this->actor); + } else { + CutsceneManager_Queue(CS_ID_GLOBAL_ELEGY); + } } } } diff --git a/mm/src/overlays/actors/ovl_En_Test6/z_en_test6.c b/mm/src/overlays/actors/ovl_En_Test6/z_en_test6.c index bfa096a382..4f762429f5 100644 --- a/mm/src/overlays/actors/ovl_En_Test6/z_en_test6.c +++ b/mm/src/overlays/actors/ovl_En_Test6/z_en_test6.c @@ -478,6 +478,12 @@ void EnTest6_InvertedSoTCutscene(EnTest6* this, PlayState* play) { subCam = Play_GetCamera(play, this->subCamId); mainCam = Play_GetCamera(play, CAM_ID_MAIN); + // Check if fast song playback is enabled and skip the cutscene if true + if (CVarGetInteger("gEnhancements.Playback.FastSongPlayback", 0)) { + EnTest6_StopInvertedSoTCutscene(this, play); + return; + } + // Update cutscene effects switch (this->cueId) { case SOTCS_CUEID_INV_INIT: @@ -727,6 +733,12 @@ void EnTest6_DoubleSoTCutscene(EnTest6* this, PlayState* play) { s16 subCamId; s16 pad2; + // Check if fast song playback is enabled and skip the cutscene if true + if (CVarGetInteger("gEnhancements.Playback.FastSongPlayback", 0)) { + EnTest6_StopDoubleSoTCutscene(this, play); + return; + } + if (this->timer > 115) { this->doubleSoTEnvLerp += 0.2f; EnTest6_EnableWhiteFillScreen(play, this->doubleSoTEnvLerp); diff --git a/mm/src/overlays/actors/ovl_En_Test7/z_en_test7.c b/mm/src/overlays/actors/ovl_En_Test7/z_en_test7.c index a9416eb7cc..fbee4b187e 100644 --- a/mm/src/overlays/actors/ovl_En_Test7/z_en_test7.c +++ b/mm/src/overlays/actors/ovl_En_Test7/z_en_test7.c @@ -406,6 +406,12 @@ void EnTest7_Init(Actor* thisx, PlayState* play2) { if (ENTEST7_GET(&this->actor) == ENTEST7_MINUS1) { func_80AF082C(this, func_80AF2938); EnTest7_SetupAction(this, NULL); + } else if (CVarGetInteger("gEnhancements.Playback.FastSongPlayback", + 0)) { // Skips the wings cutscene for song of soaring. Must be below previous lines or + // warping to entrance of temples will warp to Mayors Residence instead + func_80AF082C(this, func_80AF2350); + EnTest7_SetupAction(this, NULL); + Audio_PlayBgm_StorePrevBgm(NA_BGM_SONG_OF_SOARING); } else { func_80AF082C(this, func_80AF19A8); EnTest7_SetupAction(this, func_80AF2854); @@ -766,7 +772,10 @@ void func_80AF2938(EnTest7* this, PlayState* play) { player->stateFlags2 |= PLAYER_STATE2_20000000; this->unk_144 |= 2; this->unk_148.unk_04 = 30.0f; - if (play->roomCtx.curRoom.behaviorType1 != ROOM_BEHAVIOR_TYPE1_1) { + if ((play->roomCtx.curRoom.behaviorType1 != ROOM_BEHAVIOR_TYPE1_1) && + (!CVarGetInteger("gEnhancements.Playback.FastSongPlayback", + 0))) { // When CVar is true, it will play the temple entrance spawning cutscene instead of the + // longer overworld one func_80AF082C(this, func_80AF2AE8); } else { func_80AF082C(this, func_80AF2EC8); @@ -884,7 +893,12 @@ void func_80AF2EC8(EnTest7* this, PlayState* play) { subCam = Play_GetCamera(play, CutsceneManager_GetCurrentSubCamId(play->playerCsIds[PLAYER_CS_ID_SONG_WARP])); this->subCamEye = subCam->eye; this->subCamAt = subCam->at; - this->unk_1E54 = 40; + if (CVarGetInteger("gEnhancements.Playback.FastSongPlayback", + 0)) { // Skips part of the feather animation when you spawn in + this->unk_1E54 = 60; + } else { + this->unk_1E54 = 40; + } func_80AF2DB4(this, play); } diff --git a/mm/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c b/mm/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c index 7980c80940..be9b8a0bf0 100644 --- a/mm/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c +++ b/mm/src/overlays/actors/ovl_En_Torch2/z_en_torch2.c @@ -127,7 +127,11 @@ void EnTorch2_Update(Actor* thisx, PlayState* play) { CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); targetAlpha = 255; } - Math_StepToS(&this->alpha, targetAlpha, 8); + Math_StepToS(&this->alpha, targetAlpha, + 8 + (CVarGetInteger("gEnhancements.Playback.FastSongPlayback", 0) + ? 56 + : 0)); // speeds up the speed of which the elegy statue becomes solid. Also needed to + // ensure it keeps the switches pushed down. } } diff --git a/mm/src/overlays/actors/ovl_Oceff_Storm/z_oceff_storm.c b/mm/src/overlays/actors/ovl_Oceff_Storm/z_oceff_storm.c index caaeab0cc9..94211a0bf6 100644 --- a/mm/src/overlays/actors/ovl_Oceff_Storm/z_oceff_storm.c +++ b/mm/src/overlays/actors/ovl_Oceff_Storm/z_oceff_storm.c @@ -205,26 +205,30 @@ void OceffStorm_Draw(Actor* thisx, PlayState* play) { Vtx* vtxPtr = ResourceMgr_LoadVtxByName(sSongOfStormsCylinderVtx); // #endregion - OPEN_DISPS(play->state.gfxCtx); - - Gfx_SetupDL25_Xlu(play->state.gfxCtx); + if (!CVarGetInteger("gEnhancements.Playback.FastSongPlayback", + 0)) { // Prevents the beam of light around Link from drawing, as it will awkwardly stay in place + // when the player moves. + OPEN_DISPS(play->state.gfxCtx); - gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 200, 255); - gDPSetEnvColor(POLY_XLU_DISP++, 150, 150, 0, 128); - gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_NOISE); - gDPSetColorDither(POLY_XLU_DISP++, G_CD_NOISE); + Gfx_SetupDL25_Xlu(play->state.gfxCtx); - vtxPtr[0].v.cn[3] = vtxPtr[6].v.cn[3] = vtxPtr[16].v.cn[3] = vtxPtr[25].v.cn[3] = this->vtxAlpha >> 1; - vtxPtr[10].v.cn[3] = vtxPtr[22].v.cn[3] = this->vtxAlpha; + gDPSetPrimColor(POLY_XLU_DISP++, 0x80, 0x80, 255, 255, 200, 255); + gDPSetEnvColor(POLY_XLU_DISP++, 150, 150, 0, 128); + gDPSetAlphaDither(POLY_XLU_DISP++, G_AD_NOISE); + gDPSetColorDither(POLY_XLU_DISP++, G_CD_NOISE); - gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + vtxPtr[0].v.cn[3] = vtxPtr[6].v.cn[3] = vtxPtr[16].v.cn[3] = vtxPtr[25].v.cn[3] = this->vtxAlpha >> 1; + vtxPtr[10].v.cn[3] = vtxPtr[22].v.cn[3] = this->vtxAlpha; - gSPDisplayList(POLY_XLU_DISP++, &sSongOfStormsCylinderMaterialDL); - gSPDisplayList(POLY_XLU_DISP++, Gfx_TwoTexScroll(play->state.gfxCtx, G_TX_RENDERTILE, scroll * 4, (0 - scroll) * 8, - 32, 32, 1, scroll * 8, (0 - scroll) * 12, 32, 32)); - gSPDisplayList(POLY_XLU_DISP++, &sSongOfStormsCylinderModelDL); + gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); - CLOSE_DISPS(play->state.gfxCtx); + gSPDisplayList(POLY_XLU_DISP++, &sSongOfStormsCylinderMaterialDL); + gSPDisplayList(POLY_XLU_DISP++, + Gfx_TwoTexScroll(play->state.gfxCtx, G_TX_RENDERTILE, scroll * 4, (0 - scroll) * 8, 32, 32, 1, + scroll * 8, (0 - scroll) * 12, 32, 32)); + gSPDisplayList(POLY_XLU_DISP++, &sSongOfStormsCylinderModelDL); + CLOSE_DISPS(play->state.gfxCtx); + } OceffStorm_Draw2(&this->actor, play); } diff --git a/mm/src/overlays/actors/ovl_Oceff_Wipe5/z_oceff_wipe5.c b/mm/src/overlays/actors/ovl_Oceff_Wipe5/z_oceff_wipe5.c index d1825686d7..80ce8dd7e0 100644 --- a/mm/src/overlays/actors/ovl_Oceff_Wipe5/z_oceff_wipe5.c +++ b/mm/src/overlays/actors/ovl_Oceff_Wipe5/z_oceff_wipe5.c @@ -51,10 +51,11 @@ void OceffWipe5_Destroy(Actor* thisx, PlayState* play) { void OceffWipe5_Update(Actor* thisx, PlayState* play) { OceffWipe5* this = THIS; + s32 fastPlayback = CVarGetInteger("gEnhancements.Playback.FastSongPlayback", 0); this->actor.world.pos = GET_ACTIVE_CAM(play)->eye; if (this->counter < 100) { - this->counter++; + this->counter += fastPlayback ? 2 : 1; // Speeds up the ocarina effect } else { Actor_Kill(&this->actor); } diff --git a/mm/src/overlays/actors/ovl_player_actor/z_player.c b/mm/src/overlays/actors/ovl_player_actor/z_player.c index 84e74fd502..93ef6c88b1 100644 --- a/mm/src/overlays/actors/ovl_player_actor/z_player.c +++ b/mm/src/overlays/actors/ovl_player_actor/z_player.c @@ -13468,7 +13468,12 @@ void func_80848640(PlayState* play, Player* this) { Math_Vec3f_Copy(&torch2->actor.home.pos, &this->actor.world.pos); torch2->actor.home.rot.y = this->actor.shape.rot.y; torch2->state = 0; - torch2->framesUntilNextState = 20; + if (CVarGetInteger("gEnhancements.Playback.FastSongPlayback", + 0)) { // Speeds up the spawning of the elegy statue + torch2->framesUntilNextState = 1; + } else { + torch2->framesUntilNextState = 20; + } } else { torch2 = (EnTorch2*)Actor_Spawn(&play->actorCtx, play, ACTOR_EN_TORCH2, this->actor.world.pos.x, this->actor.world.pos.y, this->actor.world.pos.z, 0, this->actor.shape.rot.y, 0, @@ -17107,13 +17112,28 @@ void Player_Action_63(Player* this, PlayState* play) { (play->msgCtx.ocarinaMode == OCARINA_MODE_APPLY_INV_SOT_SLOW)) { if (play->msgCtx.ocarinaMode == OCARINA_MODE_APPLY_SOT) { if (!func_8082DA90(play)) { - if (gSaveContext.save.saveInfo.playerData.threeDayResetCount == 1) { + if (CVarGetInteger("gEnhancements.Playback.FastSongPlayback", + 0)) { // Ensures proper time reset whenever fast playback is active. Probably + // a better way to do this. + play->nextEntrance = ENTRANCE(SOUTH_CLOCK_TOWN, 0); + gSaveContext.save.timeSpeedOffset = 0; + gSaveContext.save.eventDayCount = 0; + gSaveContext.save.day = 0; + gSaveContext.save.time = CLOCK_TIME(6, 0) - 1; + + } else if (gSaveContext.save.saveInfo.playerData.threeDayResetCount == 1) { play->nextEntrance = ENTRANCE(CUTSCENE, 1); } else { play->nextEntrance = ENTRANCE(CUTSCENE, 0); } - gSaveContext.nextCutsceneIndex = 0xFFF7; + if (CVarGetInteger("gEnhancements.Playback.FastSongPlayback", + 0)) { // Ensures the player spawns back at the door of the clock tower when + // fast playback is active. + gSaveContext.nextCutsceneIndex = 0; + } else { + gSaveContext.nextCutsceneIndex = 0xFFF7; + } play->transitionTrigger = TRANS_TRIGGER_START; } } else { @@ -18395,7 +18415,15 @@ void Player_Action_87(Player* this, PlayState* play) { } void Player_Action_88(Player* this, PlayState* play) { - if (this->av2.actionVar2++ > 90) { + if (CVarGetInteger("gEnhancements.Playback.FastSongPlayback", + 0)) { // Speeds up the ocarina waiting timer, allowing the player to move sooner + if (this->av2.actionVar2++ > 1) { + play->msgCtx.ocarinaMode = OCARINA_MODE_END; + func_8085B384(this, play); + } else if (this->av2.actionVar2 == 1) { + func_80848640(play, this); + } + } else if (this->av2.actionVar2++ > 90) { play->msgCtx.ocarinaMode = OCARINA_MODE_END; func_8085B384(this, play); } else if (this->av2.actionVar2 == 10) {