diff --git a/soh/soh/Enhancements/Cheats/EasyFrameAdvance.cpp b/soh/soh/Enhancements/Cheats/EasyFrameAdvance.cpp new file mode 100644 index 00000000000..ab7bba3776b --- /dev/null +++ b/soh/soh/Enhancements/Cheats/EasyFrameAdvance.cpp @@ -0,0 +1,40 @@ +#include +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" + +extern "C" { +#include "variables.h" +#include "overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h" +} + +#define CVAR_FRAME_ADVANCE_NAME CVAR_CHEAT("EasyFrameAdvance") +#define CVAR_FRAME_ADVANCE_DEFAULT 0 +#define CVAR_FRAME_ADVANCE_VALUE CVarGetInteger(CVAR_FRAME_ADVANCE_NAME, CVAR_FRAME_ADVANCE_DEFAULT) + +static int frameAdvanceTimer = 0; +#define PAUSE_STATE_OFF 0 +#define PAUSE_STATE_UNPAUSE_CLOSE 19 + +void RegisterEasyFrameAdvance() { + COND_HOOK(OnGameStateMainStart, CVAR_FRAME_ADVANCE_VALUE, []() { + if (gPlayState == NULL) { + return; + } + + Input* input = &gPlayState->state.input[0]; + PauseContext* pauseCtx = &gPlayState->pauseCtx; + + if (frameAdvanceTimer > 0 && pauseCtx->state == PAUSE_STATE_OFF) { + frameAdvanceTimer--; + if (frameAdvanceTimer == 0 && CHECK_BTN_ALL(input->cur.button, BTN_START)) { + input->press.button |= BTN_START; + } + } + + if (pauseCtx->state == PAUSE_STATE_UNPAUSE_CLOSE) { + frameAdvanceTimer = 2; + } + }); +} + +static RegisterShipInitFunc initFunc(RegisterEasyFrameAdvance, { CVAR_FRAME_ADVANCE_NAME }); diff --git a/soh/soh/Enhancements/Restorations/PauseBufferInputs.cpp b/soh/soh/Enhancements/Restorations/PauseBufferInputs.cpp new file mode 100644 index 00000000000..4604c00ca11 --- /dev/null +++ b/soh/soh/Enhancements/Restorations/PauseBufferInputs.cpp @@ -0,0 +1,83 @@ +#include +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" + +extern "C" { +#include "variables.h" +#include "overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h" +} + +#define CVAR_BUFFER_NAME CVAR_ENHANCEMENT("PauseBufferWindow") +#define CVAR_BUFFER_DEFAULT 0 +#define CVAR_BUFFER_VALUE CVarGetInteger(CVAR_BUFFER_NAME, CVAR_BUFFER_DEFAULT) + +#define CVAR_INCLUDE_NAME CVAR_ENHANCEMENT("IncludeHeldInputsBufferWindow") +#define CVAR_INCLUDE_DEFAULT 0 +#define CVAR_INCLUDE_VALUE CVarGetInteger(CVAR_INCLUDE_NAME, CVAR_INCLUDE_DEFAULT) + +#define CVAR_FRAME_ADVANCE_NAME CVAR_CHEAT("EasyFrameAdvance") +#define CVAR_FRAME_ADVANCE_DEFAULT 0 +#define CVAR_FRAME_ADVANCE_VALUE CVarGetInteger(CVAR_FRAME_ADVANCE_NAME, CVAR_FRAME_ADVANCE_DEFAULT) + +static u16 inputBufferTimer = 0; +static u16 prePauseInputs = 0; +static u16 pauseInputs = 0; +#define PAUSE_STATE_OFF 0 +#define PAUSE_STATE_OPENING_1 2 +#define PAUSE_STATE_UNPAUSE_SETUP 18 + +void RegisterPauseBufferInputs() { + COND_VB_SHOULD(VB_KALEIDO_UNPAUSE_CLOSE, CVAR_BUFFER_VALUE || CVAR_INCLUDE_VALUE, { + Input* input = &gPlayState->state.input[0]; + + // Store all inputs that were pressed during the buffer window + pauseInputs |= input->press.button; + + // If the user opts to include held inputs in the buffer window, store the held inputs, minus the held inputs when the pause menu was opened + if (CVAR_INCLUDE_VALUE && inputBufferTimer == 0) { + pauseInputs |= input->cur.button & ~prePauseInputs; + prePauseInputs = 0; + } + + // Wait a specified number of frames before continuing the unpause + inputBufferTimer++; + if (inputBufferTimer < CVAR_BUFFER_VALUE) { + *should = false; + } + }); + + COND_HOOK(OnGameStateMainStart, CVAR_BUFFER_VALUE || CVAR_INCLUDE_VALUE, []() { + if (gPlayState == NULL) { + return; + } + + Input* input = &gPlayState->state.input[0]; + PauseContext* pauseCtx = &gPlayState->pauseCtx; + + // if the input buffer timer is not 0 and the pause state is off, then the player just unpaused + if (inputBufferTimer != 0 && pauseCtx->state == PAUSE_STATE_OFF) { + inputBufferTimer = 0; + + // If the user opts into easy frame advance, remove START input + if (CVAR_FRAME_ADVANCE_VALUE) { + pauseInputs &= ~BTN_START; + } + + // So we need to re-apply the inputs that were pressed during the buffer window + input->press.button |= pauseInputs; + } + + // Reset the timer and stored inputs at the beginning of the unpause process + if (pauseCtx->state == PAUSE_STATE_UNPAUSE_SETUP && pauseCtx->unk_1F4 != 160.0f) { + inputBufferTimer = 0; + pauseInputs = 0; + } + + // If the user opts to include held inputs in the buffer window, store the held inputs at the beginning of the pause process, minus the START input + if (pauseCtx->state == PAUSE_STATE_OPENING_1 && CVAR_INCLUDE_VALUE) { + prePauseInputs = input->cur.button & ~BTN_START; + } + }); +} + +static RegisterShipInitFunc initFunc(RegisterPauseBufferInputs, { CVAR_BUFFER_NAME, CVAR_INCLUDE_NAME }); diff --git a/soh/soh/Enhancements/bootcommands.c b/soh/soh/Enhancements/bootcommands.c index c010067da07..85467c6eab6 100644 --- a/soh/soh/Enhancements/bootcommands.c +++ b/soh/soh/Enhancements/bootcommands.c @@ -28,8 +28,6 @@ void BootCommands_Init() CVarClear(CVAR_GENERAL("OnFileSelectNameEntry")); // Clear when soh is killed on the file name entry page CVarClear(CVAR_GENERAL("BetterDebugWarpScreenMQMode")); CVarClear(CVAR_GENERAL("BetterDebugWarpScreenMQModeScene")); - CVarClear(CVAR_GENERAL("CheatEasyPauseBufferLastInputs")); - CVarClear(CVAR_GENERAL("CheatEasyPauseBufferTimer")); #if defined(__SWITCH__) || defined(__WIIU__) CVarRegisterInteger(CVAR_IMGUI_CONTROLLER_NAV, 1); // always enable controller nav on switch/wii u #endif diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor.h b/soh/soh/Enhancements/game-interactor/GameInteractor.h index 8810a23017e..572f3199ef4 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor.h @@ -286,6 +286,7 @@ typedef enum { VB_RENDER_YES_ON_CONTINUE_PROMPT, // Vanilla condition: CHECK_BTN_ALL(input->press.button, BTN_START) VB_CLOSE_PAUSE_MENU, + VB_KALEIDO_UNPAUSE_CLOSE, // Vanilla condition: true VB_SPAWN_BLUE_WARP, // Vanilla condition: this->warpTimer > sWarpTimerTarget && gSaveContext.nextCutsceneIndex == 0xFFEF diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index a2d5c56ec52..5c7b9909082 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -7,6 +7,7 @@ */ DEFINE_HOOK(OnLoadGame, (int32_t fileNum)); DEFINE_HOOK(OnExitGame, (int32_t fileNum)); +DEFINE_HOOK(OnGameStateMainStart, ()); DEFINE_HOOK(OnGameFrameUpdate, ()); DEFINE_HOOK(OnItemReceive, (GetItemEntry itemEntry)); DEFINE_HOOK(OnSaleEnd, (GetItemEntry itemEntry)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 39fc298a889..44391cac5db 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -10,6 +10,10 @@ void GameInteractor_ExecuteOnExitGame(int32_t fileNum) { GameInteractor::Instance->ExecuteHooks(fileNum); } +void GameInteractor_ExecuteOnGameStateMainStart() { + GameInteractor::Instance->ExecuteHooks(); +} + void GameInteractor_ExecuteOnGameFrameUpdate() { GameInteractor::Instance->ExecuteHooks(); } diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index cb238539fc3..ff39e69c5a5 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -9,6 +9,7 @@ extern "C" { // MARK: - Gameplay void GameInteractor_ExecuteOnLoadGame(int32_t fileNum); void GameInteractor_ExecuteOnExitGame(int32_t fileNum); +void GameInteractor_ExecuteOnGameStateMainStart(); void GameInteractor_ExecuteOnGameFrameUpdate(); void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry); void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry); diff --git a/soh/soh/Enhancements/presets.h b/soh/soh/Enhancements/presets.h index 78e754d582c..31df928310d 100644 --- a/soh/soh/Enhancements/presets.h +++ b/soh/soh/Enhancements/presets.h @@ -342,8 +342,6 @@ const std::vector cheatCvars = { CVAR_CHEAT("EasyISG"), CVAR_CHEAT("EasyQPA"), CVAR_CHEAT("TimelessEquipment"), - CVAR_CHEAT("EasyPauseBuffer"), - CVAR_CHEAT("EasyInputBuffer"), CVAR_CHEAT("NoRestrictItems"), CVAR_CHEAT("FreezeTime"), CVAR_GENERAL("PrevTime"), @@ -1009,7 +1007,6 @@ const std::vector spockRacePresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("MinimumFishWeightChild"), 3), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("GoronPot"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("ForgeTime"), 0), - PRESET_ENTRY_S32(CVAR_CHEAT("EasyPauseBuffer"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("DampeAllNight"), 1), PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("10GSHint"), 1), PRESET_ENTRY_S32(CVAR_RANDOMIZER_SETTING("20GSHint"), 1), @@ -1061,7 +1058,6 @@ const std::vector spockRaceNoLogicPresetEntries = { PRESET_ENTRY_S32(CVAR_ENHANCEMENT("AdultMasks"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("MinimumFishWeightAdult"), 6), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("AssignableTunicsAndBoots"), 1), - PRESET_ENTRY_S32(CVAR_CHEAT("EasyPauseBuffer"), 1), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("MinimumFishWeightChild"), 3), PRESET_ENTRY_S32(CVAR_ENHANCEMENT("ClimbSpeed"), 4), PRESET_ENTRY_S32(CVAR_COSMETIC("Goron.NeckLength"), 1000), diff --git a/soh/soh/SohMenuBar.cpp b/soh/soh/SohMenuBar.cpp index 1bb47877b46..6269d2937aa 100644 --- a/soh/soh/SohMenuBar.cpp +++ b/soh/soh/SohMenuBar.cpp @@ -310,9 +310,6 @@ void DrawSettingsMenu() { ImGui::PopStyleColor(1); ImGui::PopStyleVar(3); - UIWidgets::PaddedEnhancementSliderInt("Simulated Input Lag: %d frames", "##SimulatedInputLag", CVAR_SIMULATED_INPUT_LAG, 0, 6, "", 0, true, true, false); - UIWidgets::Tooltip("Buffers your inputs to be executed a specified amount of frames later"); - ImGui::EndMenu(); } @@ -1571,7 +1568,11 @@ void DrawEnhancementsMenu() { UIWidgets::Tooltip("Restores a bug from NTSC 1.0/1.1 that allows you to obtain the eyeball frog from King Zora instead of the Zora Tunic by holding shield."); UIWidgets::PaddedEnhancementCheckbox("Pulsate boss icon", CVAR_ENHANCEMENT("PulsateBossIcon"), true, false); UIWidgets::Tooltip("Restores an unfinished feature to pulsate the boss room icon when you are in the boss room."); - + UIWidgets::PaddedEnhancementSliderInt("Pause Buffer Input Window: %d", "##PauseBufferWindow", CVAR_ENHANCEMENT("PauseBufferWindow"), 0, 40, "", 0, true, true, false); + UIWidgets::PaddedEnhancementCheckbox("Include held inputs at start of Buffer Input Window", CVAR_ENHANCEMENT("IncludeHeldInputsBufferWindow"), true, false); + UIWidgets::Tooltip("Typically, inputs that are held prior to the buffer window are not included in the buffer. This setting changes that behavior to include them. This may cause some inputs to be re-triggered undesireably, for instance Z-Targetting something you might not want to."); + UIWidgets::PaddedEnhancementSliderInt("Simulated Input Lag: %d frames", "##SimulatedInputLag", CVAR_SIMULATED_INPUT_LAG, 0, 6, "", 0, true, true, false); + UIWidgets::Tooltip("Buffers your inputs to be executed a specified amount of frames later"); ImGui::EndMenu(); } @@ -1885,11 +1886,8 @@ void DrawCheatsMenu() { UIWidgets::Tooltip("Makes every surface in the game climbable"); UIWidgets::PaddedEnhancementCheckbox("Moon Jump on L", CVAR_CHEAT("MoonJumpOnL"), true, false); UIWidgets::Tooltip("Holding L makes you float into the air"); - UIWidgets::PaddedEnhancementCheckbox("Easy Frame Advancing", CVAR_CHEAT("EasyPauseBuffer"), true, false); - UIWidgets::Tooltip("Continue holding START button when unpausing to only advance a single frame and then re-pause"); - const bool bEasyFrameAdvanceEnabled = CVarGetInteger(CVAR_CHEAT("EasyPauseBuffer"), 0); - UIWidgets::PaddedEnhancementCheckbox("Easy Input Buffering", CVAR_CHEAT("EasyInputBuffer"), true, false, bEasyFrameAdvanceEnabled, "Forced enabled when Easy Frame Advancing is enabled", UIWidgets::CheckboxGraphics::Checkmark); - UIWidgets::Tooltip("Inputs that are held down while the Subscreen is closing will be pressed when the game is resumed"); + UIWidgets::PaddedEnhancementCheckbox("New Easy Frame Advancing", CVAR_CHEAT("EasyFrameAdvance"), true, false); + UIWidgets::Tooltip("Continue holding START button when unpausing to only advance a single frame and then re-pause."); UIWidgets::PaddedEnhancementCheckbox("Drops Don't Despawn", CVAR_CHEAT("DropsDontDie"), true, false); UIWidgets::Tooltip("Drops from enemies, grass, etc. don't disappear after a set amount of time"); UIWidgets::PaddedEnhancementCheckbox("Fish Don't despawn", CVAR_CHEAT("NoFishDespawn"), true, false); diff --git a/soh/soh/UIWidgets.cpp b/soh/soh/UIWidgets.cpp index 050c6714c23..6d9ca7a6527 100644 --- a/soh/soh/UIWidgets.cpp +++ b/soh/soh/UIWidgets.cpp @@ -400,6 +400,7 @@ namespace UIWidgets { if (changed && (oldVal != val)) { CVarSetInteger(cvarName, val); Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + ShipInit::Init(cvarName); } else { changed = false; } diff --git a/soh/src/code/game.c b/soh/src/code/game.c index a1426671993..d7524009b3f 100644 --- a/soh/src/code/game.c +++ b/soh/src/code/game.c @@ -255,6 +255,8 @@ void GameState_Update(GameState* gameState) { GameState_SetFrameBuffer(gfxCtx); + GameInteractor_ExecuteOnGameStateMainStart(); + gameState->main(gameState); func_800C4344(gameState); diff --git a/soh/src/code/padmgr.c b/soh/src/code/padmgr.c index 8665fb44882..a8bf778af4f 100644 --- a/soh/src/code/padmgr.c +++ b/soh/src/code/padmgr.c @@ -286,13 +286,6 @@ void PadMgr_ProcessInputs(PadMgr* padMgr) { Fault_AddHungupAndCrash(__FILE__, __LINE__); } - // When 3 frames are left on easy pause buffer, re-apply the last held inputs to the prev inputs - // to compute the pressed difference. This makes it so previously held inputs are continued as "held", - // but new inputs when unpausing are "pressed" out of the pause menu. - if (CVarGetInteger(CVAR_GENERAL("CheatEasyPauseBufferTimer"), 0) == 3) { - input->prev.button = CVarGetInteger(CVAR_GENERAL("CheatEasyPauseBufferLastInputs"), 0); - } - buttonDiff = input->prev.button ^ input->cur.button; input->press.button |= (u16)(buttonDiff & input->cur.button); input->rel.button |= (u16)(buttonDiff & input->prev.button); diff --git a/soh/src/code/z_kaleido_setup.c b/soh/src/code/z_kaleido_setup.c index 69ea3030f91..45992279d31 100644 --- a/soh/src/code/z_kaleido_setup.c +++ b/soh/src/code/z_kaleido_setup.c @@ -18,23 +18,11 @@ void KaleidoSetup_Update(PlayState* play) { play->shootingGalleryStatus <= 1 && gSaveContext.magicState != MAGIC_STATE_STEP_CAPACITY && gSaveContext.magicState != MAGIC_STATE_FILL && (play->sceneNum != SCENE_BOMBCHU_BOWLING_ALLEY || !Flags_GetSwitch(play, 0x38))) { - u8 easyPauseBufferEnabled = CVarGetInteger(CVAR_CHEAT("EasyPauseBuffer"), 0); - u8 easyPauseBufferTimer = CVarGetInteger(CVAR_GENERAL("CheatEasyPauseBufferTimer"), 0); - - // If start is not seen as pressed on the 2nd to last frame then we should end the easy frame advance flow - if (easyPauseBufferEnabled && easyPauseBufferTimer == 2 && - !CHECK_BTN_ALL(input->press.button, BTN_START)) { - CVarSetInteger(CVAR_GENERAL("CheatEasyPauseBufferTimer"), 0); - } - if (CHECK_BTN_ALL(input->cur.button, BTN_L) && CHECK_BTN_ALL(input->press.button, BTN_CUP)) { if (BREG(0)) { pauseCtx->debugState = 3; } - } else if ((CHECK_BTN_ALL(input->press.button, BTN_START) && (!easyPauseBufferEnabled || !easyPauseBufferTimer)) || - (easyPauseBufferEnabled && easyPauseBufferTimer == 1)) { // Force Kaleido open when easy pause buffer reaches 0 - // Remember last held buttons for pause buffer cheat (minus start so easy frame advance works) - CVarSetInteger(CVAR_GENERAL("CheatEasyPauseBufferLastInputs"), input->cur.button & ~(BTN_START)); + } else if (CHECK_BTN_ALL(input->press.button, BTN_START)) { gSaveContext.unk_13EE = gSaveContext.unk_13EA; diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c index cc7af190d7f..2b4743417ee 100644 --- a/soh/src/code/z_play.c +++ b/soh/src/code/z_play.c @@ -1668,11 +1668,6 @@ time_t Play_GetRealTime() { void Play_Main(GameState* thisx) { PlayState* play = (PlayState*)thisx; - // Decrease the easy pause buffer timer every frame - if (CVarGetInteger(CVAR_GENERAL("CheatEasyPauseBufferTimer"), 0) > 0) { - CVarSetInteger(CVAR_GENERAL("CheatEasyPauseBufferTimer"), CVarGetInteger(CVAR_GENERAL("CheatEasyPauseBufferTimer"), 0) - 1); - } - if (play->envCtx.unk_EE[2] == 0 && CVarGetInteger(CVAR_GENERAL("LetItSnow"), 0)) { play->envCtx.unk_EE[3] = 64; Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_OBJECT_KANKYO, 0, 0, 0, 0, 0, 0, 3, 0); diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c index 217ec24e3fa..e76576f85fa 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c @@ -3992,10 +3992,6 @@ void KaleidoScope_Update(PlayState* play) switch (pauseCtx->unk_1E4) { case 0: if (GameInteractor_Should(VB_CLOSE_PAUSE_MENU, CHECK_BTN_ALL(input->press.button, BTN_START))) { - if (CVarGetInteger(CVAR_CHEAT("EasyPauseBuffer"), 0) || CVarGetInteger(CVAR_CHEAT("EasyInputBuffer"), 0)) { - // Easy pause buffer is 13 frames, 12 for kaledio to end, and one more to advance a single frame - CVarSetInteger(CVAR_GENERAL("CheatEasyPauseBufferTimer"), 13); - } Interface_SetDoAction(play, DO_ACTION_NONE); pauseCtx->state = 0x12; WREG(2) = -6240; @@ -4546,6 +4542,10 @@ void KaleidoScope_Update(PlayState* play) break; case 0x13: + if (!GameInteractor_Should(VB_KALEIDO_UNPAUSE_CLOSE, true)) { + break; + } + pauseCtx->state = 0; R_UPDATE_RATE = 3; R_PAUSE_MENU_MODE = 0;