diff --git a/soh/soh/Enhancements/Text/BetterOwl.cpp b/soh/soh/Enhancements/Text/BetterOwl.cpp new file mode 100644 index 0000000000..863664fc32 --- /dev/null +++ b/soh/soh/Enhancements/Text/BetterOwl.cpp @@ -0,0 +1,22 @@ +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/ShipInit.hpp" + +extern "C" { +#include "z64.h" +extern PlayState* gPlayState; +} + +#define CVAR_BETTER_OWL_NAME CVAR_ENHANCEMENT("BetterOwl") +#define CVAR_BETTER_OWL_VALUE CVarGetInteger(CVAR_BETTER_OWL_NAME, 0) + +static void RegisterBetterOwl() { + COND_VB_SHOULD(VB_OWL_CHOOSE_BETTER, CVAR_BETTER_OWL_VALUE, { + MessageContext* msgCtx = &gPlayState->msgCtx; + if ((msgCtx->textId == 0x2066 || msgCtx->textId == 0x607B || msgCtx->textId == 0x10C2 || + msgCtx->textId == 0x10C6 || msgCtx->textId == 0x206A)) { + msgCtx->choiceIndex = 1; + } + }); +} + +static RegisterShipInitFunc initFunc(RegisterBetterOwl, { CVAR_BETTER_OWL_NAME }); diff --git a/soh/soh/Enhancements/Text/TextSpeed.cpp b/soh/soh/Enhancements/Text/TextSpeed.cpp new file mode 100644 index 0000000000..4842265489 --- /dev/null +++ b/soh/soh/Enhancements/Text/TextSpeed.cpp @@ -0,0 +1,90 @@ +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/ShipInit.hpp" + +extern "C" { +#include "z64.h" +extern PlayState* gPlayState; +} + +// Text Speed which fills whole box in one frame +static constexpr int32_t TEXT_SPEED_INSTANT = 6; + +#define CVAR_TEXT_SPEED_NAME CVAR_ENHANCEMENT("TextSpeed") +#define CVAR_SLOW_TEXT_SPEED_NAME CVAR_ENHANCEMENT("SlowTextSpeed") + +#define TEXT_SPEED CVarGetInteger(CVAR_TEXT_SPEED_NAME, 1) +#define SLOW_TEXT_SPEED CVarGetInteger(CVAR_SLOW_TEXT_SPEED_NAME, TEXT_SPEED) + +static bool ShouldAdvanceQuickText(u16 textPos) { + MessageContext* msgCtx = &gPlayState->msgCtx; + + if (textPos + TEXT_SPEED < msgCtx->textDrawPos) { + return false; + } + + if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING || + (msgCtx->msgMode >= MSGMODE_OCARINA_STARTING && msgCtx->msgMode < MSGMODE_SCARECROW_LONG_RECORDING_START)) { + return true; + } + + return false; +} + +static void FastTextCrawl(u16 textPos, bool* should) { + MessageContext* msgCtx = &gPlayState->msgCtx; + if (msgCtx->textDelay == 0) { + msgCtx->textDrawPos = textPos + TEXT_SPEED; + if (msgCtx->textDrawPos > msgCtx->decodedTextLen) { + msgCtx->textDrawPos = msgCtx->decodedTextLen + 1; + } + *should = true; + } +} + +static void SlowTextCrawl(bool* should) { + MessageContext* msgCtx = &gPlayState->msgCtx; + if (msgCtx->textDelayTimer <= 0) { + return; + } + *should = true; + if (msgCtx->textDelayTimer > SLOW_TEXT_SPEED) { + msgCtx->textDelayTimer -= SLOW_TEXT_SPEED; + } else { + msgCtx->textDelayTimer = 0; + } +} + +static void RegisterTextSpeedModifiers() { + COND_VB_SHOULD(VB_ENABLE_QUICKTEXT, TEXT_SPEED > 1, { + u16 textPos = va_arg(args, int); + if (!*should && ShouldAdvanceQuickText(textPos)) { + *should = true; + } + }); + + COND_VB_SHOULD(VB_FIX_TEXT_SPEED_SOFTLOCK, TEXT_SPEED > 1, { + MessageContext* msgCtx = &gPlayState->msgCtx; + u16 nextTextPos = va_arg(args, int); + + *should = !*should || (nextTextPos > msgCtx->textDrawPos); + if (*should) { + msgCtx->textDrawPos = nextTextPos; + } + }); + + COND_VB_SHOULD(VB_TEXT_CRAWL_FASTER, TEXT_SPEED >= TEXT_SPEED_INSTANT, { + MessageContext* msgCtx = &gPlayState->msgCtx; + msgCtx->textDrawPos = msgCtx->decodedTextLen + 1; + *should = true; + }); + + COND_VB_SHOULD(VB_TEXT_CRAWL_FASTER, TEXT_SPEED > 1 && TEXT_SPEED < TEXT_SPEED_INSTANT, { + u16 textPos = va_arg(args, int); + FastTextCrawl(textPos, should); + }); + + COND_VB_SHOULD(VB_TEXT_CRAWL_FASTER, SLOW_TEXT_SPEED > 1 && TEXT_SPEED < TEXT_SPEED_INSTANT, + { SlowTextCrawl(should); }); +} + +static RegisterShipInitFunc initFunc(RegisterTextSpeedModifiers, { CVAR_TEXT_SPEED_NAME, CVAR_SLOW_TEXT_SPEED_NAME }); diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index c111ee9249..1a38871a9e 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -660,14 +660,6 @@ typedef enum { // - `int32_t` (item - promoted from `u8`) VB_EMPTY_BOTTLE_TO_HALF_MILK, - // #### `result` - // ```c - // (Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play) - // ``` - // #### `args` - // - None - VB_END_GERUDO_MEMBERSHIP_TALK, - // #### `result` // ```c // true @@ -676,6 +668,25 @@ typedef enum { // - `*EnArrow` VB_EN_ARROW_MAGIC_CONSUMPTION, + // #### `result` + // ```c + // i + 1 == msgCtx->textDrawPos && + // (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING || + // (msgCtx->msgMode >= MSGMODE_OCARINA_STARTING && + // msgCtx->msgMode < MSGMODE_SCARECROW_LONG_RECORDING_START)) + // ``` + // #### `args` + // - `u16` (text position) + VB_ENABLE_QUICKTEXT, + + // #### `result` + // ```c + // (Message_GetState(&play->msgCtx) == TEXT_STATE_EVENT) && Message_ShouldAdvance(play) + // ``` + // #### `args` + // - None + VB_END_GERUDO_MEMBERSHIP_TALK, + // #### `result` // ```c // !(this->stateFlags3 & PLAYER_STATE3_PAUSE_ACTION_FUNC) @@ -717,6 +728,12 @@ typedef enum { // - `*EnElf` VB_FAIRY_HEAL, + // #### `result` + // True if the next text position must be beyond the current position; false otherwise + // #### `args` + // - `u16` (next text position) + VB_FIX_TEXT_SPEED_SOFTLOCK, + // #### `result` // ```c // false @@ -1658,6 +1675,14 @@ typedef enum { // - `*uint16_t` (overrideTextId) VB_OVERRIDE_LINK_THE_GORON_DIALOGUE, + // #### `result` + // ```c + // false + // ``` + // #### `args` + // - `None` + VB_OWL_CHOOSE_BETTER, + // #### `result` // ```c // this->actor.xzDistToPlayer < targetDist @@ -2640,6 +2665,14 @@ typedef enum { // - s32 - background id` VB_TARGETABLE_HOOKSHOT_RETICLE, + // #### `result` + // ```c + // false + // ``` + // #### `args` + // - `u16` (text position) + VB_TEXT_CRAWL_FASTER, + // #### `result` // ```c // (this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) && (this->heldActor != NULL) && diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c index 4c690445af..8dac99c738 100644 --- a/soh/src/code/z_message_PAL.c +++ b/soh/src/code/z_message_PAL.c @@ -15,9 +15,6 @@ #include "soh/SaveManager.h" #include "soh/ResourceManagerHelpers.h" -// SOH [Enhancement] Text Speed which fills whole box in one frame -#define TEXT_SPEED_INSTANT 6 - // #region SOH [NTSC] - Allows custom messages to work on japanese static bool sDisplayNextMessageAsEnglish = false; static u8 sLastLanguage = LANGUAGE_ENG; @@ -956,7 +953,6 @@ void Message_DrawTextJPN(PlayState* play, Gfx** gfxP) { u16 i; u16 charTexIdx; Gfx* gfx = *gfxP; - int gTextSpeed, gSlowTextSpeed; play->msgCtx.textPosX = R_TEXT_INIT_XPOS; play->msgCtx.textPosY = R_TEXT_INIT_YPOS; @@ -970,9 +966,6 @@ void Message_DrawTextJPN(PlayState* play, Gfx** gfxP) { msgCtx->unk_E3D0 = 0; charTexIdx = 0; - gTextSpeed = CVarGetInteger(CVAR_ENHANCEMENT("TextSpeed"), 1); - gSlowTextSpeed = CVarGetInteger(CVAR_ENHANCEMENT("SlowTextSpeed"), gTextSpeed); - for (i = 0; i < msgCtx->textDrawPos; i++) { character = msgCtx->msgBufDecodedWide[i]; @@ -1012,11 +1005,7 @@ void Message_DrawTextJPN(PlayState* play, Gfx** gfxP) { msgCtx->textPosX += msgCtx->msgBufDecodedWide[++i]; break; case MESSAGE_TEXTID_JPN: - // #region SOH [General] Fixes softlock for higher text speeds - if (gTextSpeed > 1) { - msgCtx->textDrawPos = i + 1; - } - // #endregion + GameInteractor_Should(VB_FIX_TEXT_SPEED_SOFTLOCK, false, i + 1); msgCtx->textboxEndType = TEXTBOX_ENDTYPE_HAS_NEXT; if (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING) { Audio_PlaySoundGeneral(0, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, @@ -1027,10 +1016,12 @@ void Message_DrawTextJPN(PlayState* play, Gfx** gfxP) { *gfxP = gfx; return; case MESSAGE_QUICKTEXT_ENABLE_JPN: - if ((i + 1 == msgCtx->textDrawPos || (gTextSpeed > 1 && i + gTextSpeed >= msgCtx->textDrawPos)) && - (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING || - (msgCtx->msgMode >= MSGMODE_OCARINA_STARTING && - msgCtx->msgMode < MSGMODE_SCARECROW_LONG_RECORDING_START))) { + if (GameInteractor_Should(VB_ENABLE_QUICKTEXT, + i + 1 == msgCtx->textDrawPos && + (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING || + (msgCtx->msgMode >= MSGMODE_OCARINA_STARTING && + msgCtx->msgMode < MSGMODE_SCARECROW_LONG_RECORDING_START)), + i)) { j = i; while (true) { character = msgCtx->msgBufDecodedWide[j]; @@ -1043,7 +1034,8 @@ void Message_DrawTextJPN(PlayState* play, Gfx** gfxP) { break; } } - if (j > msgCtx->textDrawPos) { + + if (GameInteractor_Should(VB_FIX_TEXT_SPEED_SOFTLOCK, true, j)) { i = j - 1; msgCtx->textDrawPos = j; } @@ -1151,12 +1143,7 @@ void Message_DrawTextJPN(PlayState* play, Gfx** gfxP) { msgCtx->choiceTextId = msgCtx->textId; msgCtx->stateTimer = 4; msgCtx->choiceIndex = 0; - if (CVarGetInteger(CVAR_ENHANCEMENT("BetterOwl"), 0)) { - if ((msgCtx->textId == 0x2066 || msgCtx->textId == 0x607B || msgCtx->textId == 0x10C2 || - msgCtx->textId == 0x10C6 || msgCtx->textId == 0x206A)) { - msgCtx->choiceIndex = 1; - } - } + GameInteractor_Should(VB_OWL_CHOOSE_BETTER, false); Font_LoadMessageBoxIcon(font, TEXTBOX_ICON_ARROW); } break; @@ -1184,11 +1171,7 @@ void Message_DrawTextJPN(PlayState* play, Gfx** gfxP) { *gfxP = gfx; return; case MESSAGE_OCARINA_JPN: - // #region SOH [General] Fixes softlock for higher text speeds - if (gTextSpeed > 1) { - msgCtx->textDrawPos = i + 1; - } - // #endregion + GameInteractor_Should(VB_FIX_TEXT_SPEED_SOFTLOCK, false, i + 1); if (i + 1 == msgCtx->textDrawPos) { Message_HandleOcarina(play); *gfxP = gfx; @@ -1279,20 +1262,13 @@ void Message_DrawTextJPN(PlayState* play, Gfx** gfxP) { } } - if (gTextSpeed >= TEXT_SPEED_INSTANT) { - msgCtx->textDrawPos = msgCtx->decodedTextLen + 1; - } else if (msgCtx->textDelay == 0) { - msgCtx->textDrawPos = i + gTextSpeed; - if (msgCtx->textDrawPos > msgCtx->decodedTextLen) { - msgCtx->textDrawPos = msgCtx->decodedTextLen + 1; - } + if (GameInteractor_Should(VB_TEXT_CRAWL_FASTER, false, i)) { + // Logic handled within hooks } else if (msgCtx->textDelayTimer == 0) { msgCtx->textDrawPos = i + 1; msgCtx->textDelayTimer = msgCtx->textDelay; - } else if (msgCtx->textDelayTimer <= gSlowTextSpeed) { - msgCtx->textDelayTimer = 0; } else { - msgCtx->textDelayTimer -= gSlowTextSpeed; + msgCtx->textDelayTimer--; } *gfxP = gfx; } @@ -1309,13 +1285,12 @@ void Message_DrawText(PlayState* play, Gfx** gfxP) { u16 i; u16 sfxHi; u16 charTexIdx; - int gTextSpeed, gSlowTextSpeed; Font* font = &play->msgCtx.font; Gfx* gfx = *gfxP; play->msgCtx.textPosX = R_TEXT_INIT_XPOS; - if (sTextIsCredits == false) { + if (!sTextIsCredits) { msgCtx->textPosY = R_TEXT_INIT_YPOS; } else { msgCtx->textPosY = YREG(1); @@ -1332,9 +1307,6 @@ void Message_DrawText(PlayState* play, Gfx** gfxP) { msgCtx->unk_E3D0 = 0; charTexIdx = 0; - gTextSpeed = CVarGetInteger(CVAR_ENHANCEMENT("TextSpeed"), 1); - gSlowTextSpeed = CVarGetInteger(CVAR_ENHANCEMENT("SlowTextSpeed"), gTextSpeed); - for (i = 0; i < msgCtx->textDrawPos; i++) { character = msgCtx->msgBufDecoded[i]; @@ -1384,10 +1356,12 @@ void Message_DrawText(PlayState* play, Gfx** gfxP) { *gfxP = gfx; return; case MESSAGE_QUICKTEXT_ENABLE: - if ((i + 1 == msgCtx->textDrawPos || (gTextSpeed > 1 && i + gTextSpeed >= msgCtx->textDrawPos)) && - (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING || - (msgCtx->msgMode >= MSGMODE_OCARINA_STARTING && - msgCtx->msgMode < MSGMODE_SCARECROW_LONG_RECORDING_START))) { + if (GameInteractor_Should(VB_ENABLE_QUICKTEXT, + i + 1 == msgCtx->textDrawPos && + (msgCtx->msgMode == MSGMODE_TEXT_DISPLAYING || + (msgCtx->msgMode >= MSGMODE_OCARINA_STARTING && + msgCtx->msgMode < MSGMODE_SCARECROW_LONG_RECORDING_START)), + i)) { j = i; while (true) { lookAheadCharacter = msgCtx->msgBufDecoded[j]; @@ -1404,12 +1378,11 @@ void Message_DrawText(PlayState* play, Gfx** gfxP) { break; } } - if (j > msgCtx->textDrawPos) { + + if (GameInteractor_Should(VB_FIX_TEXT_SPEED_SOFTLOCK, true, j)) { i = j - 1; msgCtx->textDrawPos = j; } - - if (character) {} } case MESSAGE_QUICKTEXT_DISABLE: break; @@ -1526,12 +1499,7 @@ void Message_DrawText(PlayState* play, Gfx** gfxP) { msgCtx->choiceTextId = msgCtx->textId; msgCtx->stateTimer = 4; msgCtx->choiceIndex = 0; - if (CVarGetInteger(CVAR_ENHANCEMENT("BetterOwl"), 0)) { - if ((msgCtx->textId == 0x2066 || msgCtx->textId == 0x607B || msgCtx->textId == 0x10C2 || - msgCtx->textId == 0x10C6 || msgCtx->textId == 0x206A)) { - msgCtx->choiceIndex = 1; - } - } + GameInteractor_Should(VB_OWL_CHOOSE_BETTER, false); Font_LoadMessageBoxIcon(font, TEXTBOX_ICON_ARROW); } break; @@ -1559,11 +1527,7 @@ void Message_DrawText(PlayState* play, Gfx** gfxP) { *gfxP = gfx; return; case MESSAGE_OCARINA: - // #region SOH [General] Fixes softlock for higher text speeds - if (gTextSpeed > 1) { - msgCtx->textDrawPos = i + 1; - } - // #endregion + GameInteractor_Should(VB_FIX_TEXT_SPEED_SOFTLOCK, false, i + 1); if (i + 1 == msgCtx->textDrawPos) { Message_HandleOcarina(play); *gfxP = gfx; @@ -1631,17 +1595,13 @@ void Message_DrawText(PlayState* play, Gfx** gfxP) { break; } } - if (gTextSpeed >= TEXT_SPEED_INSTANT) { - msgCtx->textDrawPos = msgCtx->decodedTextLen + 1; - } else if (msgCtx->textDelay == 0) { - msgCtx->textDrawPos = i + gTextSpeed; + if (GameInteractor_Should(VB_TEXT_CRAWL_FASTER, false, i)) { + // Logic handled within hooks } else if (msgCtx->textDelayTimer == 0) { msgCtx->textDrawPos = i + 1; msgCtx->textDelayTimer = msgCtx->textDelay; - } else if (msgCtx->textDelayTimer <= gSlowTextSpeed) { - msgCtx->textDelayTimer = 0; } else { - msgCtx->textDelayTimer -= gSlowTextSpeed; + msgCtx->textDelayTimer--; } *gfxP = gfx; }