diff options
| author | Garrett Cox <garrettjcox@gmail.com> | 2026-07-27 16:13:57 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-27 16:13:57 -0500 |
| commit | 2c83cc890d70f4f68e1ca1e7bee753905efca467 (patch) | |
| tree | 6807f4b539320cac588e6523c874588deee01b58 | |
| parent | e3310fe1b327c51a39e40a0f09d12b43bf7a0cb5 (diff) | |
Option to auto advance text to credits (#1821)
| -rw-r--r-- | mm/2s2h/BenGui/BenMenu.cpp | 5 | ||||
| -rw-r--r-- | mm/2s2h/Enhancements/Cutscenes/AutoAdvanceEndingText.cpp | 42 | ||||
| -rw-r--r-- | mm/2s2h/GameInteractor/GameInteractor_VanillaBehavior.h | 8 | ||||
| -rw-r--r-- | mm/2s2h/PresetManager/PresetManager.cpp | 1 | ||||
| -rw-r--r-- | mm/src/code/z_message.c | 32 |
5 files changed, 76 insertions, 12 deletions
diff --git a/mm/2s2h/BenGui/BenMenu.cpp b/mm/2s2h/BenGui/BenMenu.cpp index 81537b383..56dad0d58 100644 --- a/mm/2s2h/BenGui/BenMenu.cpp +++ b/mm/2s2h/BenGui/BenMenu.cpp @@ -1572,6 +1572,11 @@ void BenMenu::AddEnhancements() { AddWidget(path, "Skip Bottle Pickup Messages", WIDGET_CVAR_CHECKBOX) .CVar("gEnhancements.Dialogue.SkipBottlePickupMessages") .Options(CheckboxOptions().Tooltip("Skip pickup messages for bottle swipes.")); + AddWidget(path, "Auto Advance Ending Text", WIDGET_CVAR_CHECKBOX) + .CVar("gEnhancements.Cutscenes.AutoAdvanceEndingText") + .Options(CheckboxOptions().Tooltip( + "After completing the game, textboxes shown on the way to the credits advance on their own " + "once they've been up long enough to read, so the ending plays out without controller input.")); path.column = SECTION_COLUMN_3; AddWidget(path, "Other", WIDGET_SEPARATOR_TEXT); diff --git a/mm/2s2h/Enhancements/Cutscenes/AutoAdvanceEndingText.cpp b/mm/2s2h/Enhancements/Cutscenes/AutoAdvanceEndingText.cpp new file mode 100644 index 000000000..54ae72173 --- /dev/null +++ b/mm/2s2h/Enhancements/Cutscenes/AutoAdvanceEndingText.cpp @@ -0,0 +1,42 @@ +#include <libultraship/bridge/consolevariablebridge.h> +#include "2s2h/GameInteractor/GameInteractor.h" +#include "2s2h/ShipInit.hpp" + +extern "C" { +#include "variables.h" +} + +#define CVAR_NAME "gEnhancements.Cutscenes.AutoAdvanceEndingText" +#define CVAR CVarGetInteger(CVAR_NAME, 0) + +static bool sAdvanceMessages = false; +static u32 sTextboxTimer = 0; + +static bool IsWaitingOnPlayer(MessageContext* msgCtx) { + return (msgCtx->msgLength != 0) && + ((msgCtx->msgMode == MSGMODE_TEXT_AWAIT_INPUT) || (msgCtx->msgMode == MSGMODE_TEXT_AWAIT_NEXT) || + (msgCtx->msgMode == MSGMODE_TEXT_DONE)); +} + +static RegisterShipInitFunc initFunc( + []() { + // Unconditionally registering these in case the user forgets to turn this on prior to finishing + COND_HOOK(OnGameCompletion, true, []() { sAdvanceMessages = true; }); + COND_HOOK(OnSaveLoad, true, [](s16 fileNum) { sAdvanceMessages = false; }); + + COND_HOOK(OnGameStateUpdate, CVAR, []() { + if (sAdvanceMessages && (gPlayState != NULL) && IsWaitingOnPlayer(&gPlayState->msgCtx)) { + sTextboxTimer++; + } else { + sTextboxTimer = 0; + } + }); + + COND_VB_SHOULD(VB_MSG_ADVANCE, CVAR, { + if (sAdvanceMessages && (gPlayState != NULL) && (sTextboxTimer >= 20)) { + sTextboxTimer = 0; + *should = true; + } + }); + }, + { CVAR_NAME }); diff --git a/mm/2s2h/GameInteractor/GameInteractor_VanillaBehavior.h b/mm/2s2h/GameInteractor/GameInteractor_VanillaBehavior.h index 143c1d1a2..8103fe6b1 100644 --- a/mm/2s2h/GameInteractor/GameInteractor_VanillaBehavior.h +++ b/mm/2s2h/GameInteractor/GameInteractor_VanillaBehavior.h @@ -1422,6 +1422,14 @@ typedef enum { // #### `result` // ```c + // CHECK_BTN_ALL(controller->press.button, BTN_A) || ... + // ``` + // #### `args` + // - None + VB_MSG_ADVANCE, + + // #### `result` + // ```c // false // ``` // #### `args` diff --git a/mm/2s2h/PresetManager/PresetManager.cpp b/mm/2s2h/PresetManager/PresetManager.cpp index f61c8a94e..44dea18be 100644 --- a/mm/2s2h/PresetManager/PresetManager.cpp +++ b/mm/2s2h/PresetManager/PresetManager.cpp @@ -69,6 +69,7 @@ nlohmann::json curatedPresetJ = R"( "FixTargettingCameraSnap": 1 }, "Cutscenes": { + "AutoAdvanceEndingText": 1, "HideTitleCards": 1, "SkipEnemyCutscenes": 1, "SkipEntranceCutscenes": 1, diff --git a/mm/src/code/z_message.c b/mm/src/code/z_message.c index 1e0a6eed6..c1450cdf3 100644 --- a/mm/src/code/z_message.c +++ b/mm/src/code/z_message.c @@ -291,40 +291,48 @@ void Message_ResetOcarinaButtonState(PlayState* play) { bool Message_ShouldAdvance(PlayState* play) { MessageContext* msgCtx = &play->msgCtx; Input* controller = CONTROLLER1(&play->state); + bool shouldAdvance; if ((msgCtx->textboxEndType == TEXTBOX_ENDTYPE_TWO_CHOICE) || (msgCtx->textboxEndType == TEXTBOX_ENDTYPE_THREE_CHOICE)) { if (CHECK_BTN_ALL(controller->press.button, BTN_A)) { Audio_PlaySfx(NA_SE_SY_MESSAGE_PASS); } - return CHECK_BTN_ALL(controller->press.button, BTN_A); + shouldAdvance = CHECK_BTN_ALL(controller->press.button, BTN_A); } else { if (CHECK_BTN_ALL(controller->press.button, BTN_A) || CHECK_BTN_ALL(controller->press.button, BTN_B) || CHECK_BTN_ALL(controller->press.button, BTN_CUP)) { Audio_PlaySfx(NA_SE_SY_MESSAGE_PASS); } - return CHECK_BTN_ALL(controller->press.button, BTN_A) || CHECK_BTN_ALL(controller->press.button, BTN_B) || - // 2S2H [Enhancement] When fast text is on, we want to check if B is held instead of only if it was just - // pressed - (CVarGetInteger("gEnhancements.Dialogue.FastText", 0) && CHECK_BTN_ALL(controller->cur.button, BTN_B)) || - CHECK_BTN_ALL(controller->press.button, BTN_CUP); + shouldAdvance = + CHECK_BTN_ALL(controller->press.button, BTN_A) || CHECK_BTN_ALL(controller->press.button, BTN_B) || + // 2S2H [Enhancement] When fast text is on, we want to check if B is held instead of only if it was just + // pressed + (CVarGetInteger("gEnhancements.Dialogue.FastText", 0) && CHECK_BTN_ALL(controller->cur.button, BTN_B)) || + CHECK_BTN_ALL(controller->press.button, BTN_CUP); } + + return GameInteractor_Should(VB_MSG_ADVANCE, shouldAdvance); } bool Message_ShouldAdvanceSilent(PlayState* play) { MessageContext* msgCtx = &play->msgCtx; Input* controller = CONTROLLER1(&play->state); + bool shouldAdvance; if ((msgCtx->textboxEndType == TEXTBOX_ENDTYPE_TWO_CHOICE) || (msgCtx->textboxEndType == TEXTBOX_ENDTYPE_THREE_CHOICE)) { - return CHECK_BTN_ALL(controller->press.button, BTN_A); + shouldAdvance = CHECK_BTN_ALL(controller->press.button, BTN_A); } else { - return CHECK_BTN_ALL(controller->press.button, BTN_A) || CHECK_BTN_ALL(controller->press.button, BTN_B) || - // 2S2H [Enhancement] When fast text is on, we want to check if B is held instead of only if it was just - // pressed - (CVarGetInteger("gEnhancements.Dialogue.FastText", 0) && CHECK_BTN_ALL(controller->cur.button, BTN_B)) || - CHECK_BTN_ALL(controller->press.button, BTN_CUP); + shouldAdvance = + CHECK_BTN_ALL(controller->press.button, BTN_A) || CHECK_BTN_ALL(controller->press.button, BTN_B) || + // 2S2H [Enhancement] When fast text is on, we want to check if B is held instead of only if it was just + // pressed + (CVarGetInteger("gEnhancements.Dialogue.FastText", 0) && CHECK_BTN_ALL(controller->cur.button, BTN_B)) || + CHECK_BTN_ALL(controller->press.button, BTN_CUP); } + + return GameInteractor_Should(VB_MSG_ADVANCE, shouldAdvance); } void Message_CloseTextbox(PlayState* play) { |
