diff --git a/soh/soh/Enhancements/FrameAdvanceAltScheme.cpp b/soh/soh/Enhancements/FrameAdvanceAltScheme.cpp new file mode 100644 index 000000000..11e94f50f --- /dev/null +++ b/soh/soh/Enhancements/FrameAdvanceAltScheme.cpp @@ -0,0 +1,58 @@ +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/ShipInit.hpp" +#include "soh/OTRGlobals.h" + +#include + +extern "C" { +#include "functions.h" +#include "macros.h" +#include "variables.h" + +extern PlayState* gPlayState; +} + +#define CVAR_FRAMEADVANCEALTSCHEME_NAME CVAR_DEVELOPER_TOOLS("FrameAdvanceAltScheme") +#define CVAR_FRAMEADVANCEALTSCHEME_DEFAULT 0 +#define CVAR_FRAMEADVANCEALTSCHEME_VALUE \ + CVarGetInteger(CVAR_FRAMEADVANCEALTSCHEME_NAME, CVAR_FRAMEADVANCEALTSCHEME_DEFAULT) + +void RegisterFrameAdvanceAltScheme() { + COND_VB_SHOULD(VB_FRAME_ADVANCE_BE_VANILLA, CVAR_FRAMEADVANCEALTSCHEME_VALUE, { + // Do not run vanilla Frame Advance codes. + *should = false; + }); + + COND_VB_SHOULD(VB_FRAME_ADVANCE_FREEZE_FRAME, CVAR_FRAMEADVANCEALTSCHEME_VALUE, { + FrameAdvanceContext* frameAdvCtx = va_arg(args, FrameAdvanceContext*); + Input* input = gPlayState->state.input; + bool runFrame = false; + + // Push START to toggle the frame advance mode. + if (CHECK_BTN_ALL(input[3].press.button, BTN_START)) { + frameAdvCtx->enabled = !frameAdvCtx->enabled; + if (frameAdvCtx->enabled) { + SPDLOG_DEBUG("Frame Advance is now enabled"); + } else { + SPDLOG_DEBUG("Frame Advance is now disabled"); + } + } + + // Push A to advance one frame. + // Hold L to run normally until L is released. + // Hold R to advance a frame every half second. + if (!frameAdvCtx->enabled || CVarGetInteger(CVAR_DEVELOPER_TOOLS("FrameAdvanceTick"), 0) || + CHECK_BTN_ALL(input[3].press.button, BTN_A) || CHECK_BTN_ALL(input[3].cur.button, BTN_L) || + CHECK_BTN_ALL(input[3].press.button, BTN_R) || + (CHECK_BTN_ALL(input[3].cur.button, BTN_R) && (++frameAdvCtx->timer >= 9))) { + CVarClear(CVAR_DEVELOPER_TOOLS("FrameAdvanceTick")); + frameAdvCtx->timer = 0; + runFrame = true; + } + + *should = !runFrame; + }); +} + +static RegisterShipInitFunc initFunc_FrameAdvanceAltScheme(RegisterFrameAdvanceAltScheme, + { CVAR_FRAMEADVANCEALTSCHEME_NAME }); diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 09defd088..b5cff9971 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -535,6 +535,22 @@ typedef enum { // - None VB_FIX_SAW_SOFTLOCK, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*FrameAdvanceContext` + VB_FRAME_ADVANCE_BE_VANILLA, + + // #### `result` + // ```c + // false + // ``` + // #### `args` + // - `*FrameAdvanceContext` + VB_FRAME_ADVANCE_FREEZE_FRAME, + // #### `result` // ```c // true diff --git a/soh/soh/SohGui/SohMenuDevTools.cpp b/soh/soh/SohGui/SohMenuDevTools.cpp index 2a0fcd62a..3065e3214 100644 --- a/soh/soh/SohGui/SohMenuDevTools.cpp +++ b/soh/soh/SohGui/SohMenuDevTools.cpp @@ -105,6 +105,13 @@ void SohMenu::AddMenuDevTools() { } }) .SameLine(true); + AddWidget(path, "Frame Advance Alternative Control Scheme", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_DEVELOPER_TOOLS("FrameAdvanceAltScheme")) + .Options(CheckboxOptions().Tooltip("Remaps Frame Advance controls. Uses Controller Port 4.\n" + "Push START button to toggle Frame Advance.\n" + "Push A button to advance a frame.\n" + "Hold L button to run the game normally until L button is released.\n" + "Hold R button to advance a frame every half second.")); AddWidget(path, "Log Level", WIDGET_CVAR_COMBOBOX) .CVar(CVAR_DEVELOPER_TOOLS("LogLevel")) .Options(ComboboxOptions() diff --git a/soh/src/code/z_frame_advance.c b/soh/src/code/z_frame_advance.c index ecddb55e6..54aaff21f 100644 --- a/soh/src/code/z_frame_advance.c +++ b/soh/src/code/z_frame_advance.c @@ -1,4 +1,6 @@ #include "global.h" +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" void FrameAdvance_Init(FrameAdvanceContext* frameAdvCtx) { frameAdvCtx->timer = 0; @@ -14,18 +16,28 @@ void FrameAdvance_Init(FrameAdvanceContext* frameAdvCtx) { * This function returns true when frame advance is not active (game will run normally) */ s32 FrameAdvance_Update(FrameAdvanceContext* frameAdvCtx, Input* input) { - if (CHECK_BTN_ALL(input->cur.button, BTN_R) && CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { - frameAdvCtx->enabled = !frameAdvCtx->enabled; + if (GameInteractor_Should(VB_FRAME_ADVANCE_BE_VANILLA, true, frameAdvCtx)) { + // Vanilla Frame Advance + if (CHECK_BTN_ALL(input->cur.button, BTN_R) && CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { + frameAdvCtx->enabled = !frameAdvCtx->enabled; + } + + if (!frameAdvCtx->enabled || CVarGetInteger(CVAR_DEVELOPER_TOOLS("FrameAdvanceTick"), 0) || + (CHECK_BTN_ALL(input->cur.button, BTN_Z) && + (CHECK_BTN_ALL(input->press.button, BTN_R) || + (CHECK_BTN_ALL(input->cur.button, BTN_R) && (++frameAdvCtx->timer >= 9))))) { + CVarClear(CVAR_DEVELOPER_TOOLS("FrameAdvanceTick")); + frameAdvCtx->timer = 0; + return true; + } + + return false; } - if (!frameAdvCtx->enabled || CVarGetInteger(CVAR_DEVELOPER_TOOLS("FrameAdvanceTick"), 0) || - (CHECK_BTN_ALL(input->cur.button, BTN_Z) && - (CHECK_BTN_ALL(input->press.button, BTN_R) || - (CHECK_BTN_ALL(input->cur.button, BTN_R) && (++frameAdvCtx->timer >= 9))))) { - CVarClear(CVAR_DEVELOPER_TOOLS("FrameAdvanceTick")); - frameAdvCtx->timer = 0; - return true; + // Call hooks and ask if we should freeze the frame + if (GameInteractor_Should(VB_FRAME_ADVANCE_FREEZE_FRAME, false, frameAdvCtx)) { + return false; } - - return false; + // No hooks said we should freeze the frame, so run the game normally + return true; }