Feedback Update #2

-Introduced new function 'PauseWarp_Idle' now that 'PauseWarp_Main' is no longer called every frame
-Added C wrapper to access 'GameInteractor::IsSaveLoaded' and scrapped the 'IsStateValid' function
-Added 'PauseWarp_Idle' to the the 'RegisterPauseWarp' function
-Refactored the code some
This commit is contained in:
mckinlee 2023-09-22 17:21:30 -04:00
commit 362f6426ec
3 changed files with 52 additions and 69 deletions

View file

@ -45,6 +45,11 @@ bool GameInteractor::IsSaveLoaded() {
return (gPlayState == NULL || player == NULL || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2) ? false : true; return (gPlayState == NULL || player == NULL || gSaveContext.fileNum < 0 || gSaveContext.fileNum > 2) ? false : true;
} }
extern "C" bool IsSaveLoadedWrapper() {
GameInteractor* gameInteractor = GameInteractor::Instance;
return gameInteractor->IsSaveLoaded();
}
bool GameInteractor::IsGameplayPaused() { bool GameInteractor::IsGameplayPaused() {
Player* player = GET_PLAYER(gPlayState); Player* player = GET_PLAYER(gPlayState);
return (Player_InBlockingCsMode(gPlayState, player) || gPlayState->pauseCtx.state != 0 || gPlayState->msgCtx.msgMode != 0) ? true : false; return (Player_InBlockingCsMode(gPlayState, player) || gPlayState->pauseCtx.state != 0 || gPlayState->msgCtx.msgMode != 0) ? true : false;

View file

@ -33,6 +33,7 @@ extern PlayState* gPlayState;
extern void Overlay_DisplayText(float duration, const char* text); extern void Overlay_DisplayText(float duration, const char* text);
uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum); uint32_t ResourceMgr_IsSceneMasterQuest(s16 sceneNum);
void PauseWarp_Main(); void PauseWarp_Main();
void PauseWarp_Idle();
} }
bool performDelayedSave = false; bool performDelayedSave = false;
bool performSave = false; bool performSave = false;
@ -979,6 +980,10 @@ void RegisterPauseWarp() {
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPauseMenu>([]() { GameInteractor::Instance->RegisterGameHook<GameInteractor::OnPauseMenu>([]() {
PauseWarp_Main(); PauseWarp_Main();
}); });
GameInteractor::Instance->RegisterGameHook<GameInteractor::OnGameFrameUpdate>([]() {
PauseWarp_Idle();
});
} }
void InitMods() { void InitMods() {

View file

@ -1,110 +1,83 @@
// Importing necessary libraries and headers
#include <stdbool.h> #include <stdbool.h>
#include <stdint.h> #include <stdint.h>
#include "soh/Enhancements/gameconsole.h" #include "soh/Enhancements/gameconsole.h"
#include "global.h" #include "global.h"
#include <soh/Enhancements/custom-message/CustomMessageTypes.h> #include "soh/Enhancements/custom-message/CustomMessageTypes.h"
#include "luslog.h"
// Defining the structure for the pause warp state, which holds different flags and cooldowns
typedef struct { typedef struct {
bool warpInitiated, textboxInitiated, inChoosingState, textboxIsOpen, isTextboxClosing; bool warpInitiated, textboxInitiated, inChoosingState, textboxIsOpen, isTextboxClosing;
int aButtonCooldown, textboxCheckCooldown, textboxOpenCooldown; int aButtonCooldown, textboxCheckCooldown, textboxOpenCooldown;
} PauseWarpState; } PauseWarpState;
// Mapping the song messages. Each song corresponds to a specific text message.
static const int songMessageMap[] = { TEXT_WARP_MINUET_OF_FOREST, TEXT_WARP_BOLERO_OF_FIRE, TEXT_WARP_SERENADE_OF_WATER, TEXT_WARP_REQUIEM_OF_SPIRIT, TEXT_WARP_NOCTURNE_OF_SHADOW, TEXT_WARP_PRELUDE_OF_LIGHT }; static const int songMessageMap[] = { TEXT_WARP_MINUET_OF_FOREST, TEXT_WARP_BOLERO_OF_FIRE, TEXT_WARP_SERENADE_OF_WATER, TEXT_WARP_REQUIEM_OF_SPIRIT, TEXT_WARP_NOCTURNE_OF_SHADOW, TEXT_WARP_PRELUDE_OF_LIGHT };
// Mapping the song audio. Each song corresponds to a specific audio ID.
static const int songAudioMap[] = { NA_BGM_OCA_MINUET, NA_BGM_OCA_BOLERO, NA_BGM_OCA_SERENADE, NA_BGM_OCA_REQUIEM, NA_BGM_OCA_NOCTURNE, NA_BGM_OCA_LIGHT }; static const int songAudioMap[] = { NA_BGM_OCA_MINUET, NA_BGM_OCA_BOLERO, NA_BGM_OCA_SERENADE, NA_BGM_OCA_REQUIEM, NA_BGM_OCA_NOCTURNE, NA_BGM_OCA_LIGHT };
// Forward declaring the functions used in this file extern bool IsSaveLoadedWrapper();
bool IsStateValid(PlayState* play, Player* player, PauseWarpState* state);
void ResetStateFlags(PauseWarpState* state);
void InitiateWarp(PlayState* play, Player* player, int song, PauseWarpState* state);
void HandleWarpConfirmation(PlayState* play, Player* player, PauseWarpState* state);
void HandleCooldowns(PauseWarpState* state);
// Checking if the state is valid. This is a sanity check to ensure we're not operating on null pointers. static PauseWarpState state;
bool IsStateValid(PlayState* play, Player* player, PauseWarpState* state) {
return play && player && state; bool IsPauseWarpEnabled() {
return CVarGetInteger("gPauseWarpEnabled", 0) && IsSaveLoadedWrapper();
} }
// Resetting all the flags in the state to their default values (which is mostly 'false' for booleans and '0' for integers)
void ResetStateFlags(PauseWarpState* state) { void ResetStateFlags(PauseWarpState* state) {
*state = (PauseWarpState){0}; *state = (PauseWarpState){0};
} }
// Initiating the warp process. Here we set all the required flags and disable player input. void InitiateWarp(PlayState* play, Player* player, int song) {
void InitiateWarp(PlayState* play, Player* player, int song, PauseWarpState* state) { int idx = song - QUEST_SONG_MINUET;
int idx = song - QUEST_SONG_MINUET; // Calculating the song index Audio_SetSoundBanksMute(0x20);
Audio_SetSoundBanksMute(0x20); //Mute current BGM Audio_PlayFanfare(songAudioMap[idx]);
Audio_PlayFanfare(songAudioMap[idx]); // Play the corresponding fanfare play->msgCtx.lastPlayedSong = idx;
play->msgCtx.lastPlayedSong = idx; // Storing the last played song play->pauseCtx.state = 0x12;
play->pauseCtx.state = 0x12; // Setting the pause state Message_StartTextbox(play, songMessageMap[idx], NULL);
Message_StartTextbox(play, songMessageMap[idx], NULL); // Starting the textbox with the appropriate message player->stateFlags1 |= PLAYER_STATE1_IN_CUTSCENE;
player->stateFlags1 |= PLAYER_STATE1_IN_CUTSCENE; // Disabling player input state = (PauseWarpState){.warpInitiated = true, .textboxOpenCooldown = 10, .aButtonCooldown = 10, .textboxCheckCooldown = 5, .textboxIsOpen = true};
*state = (PauseWarpState){.warpInitiated = true, .textboxOpenCooldown = 10, .aButtonCooldown = 10, .textboxCheckCooldown = 5, .textboxIsOpen = true}; // Setting the flags for warp play->msgCtx.choiceIndex = 0;
play->msgCtx.choiceIndex = 0; // Resetting the choice index
} }
// Handling the warp confirmation. This is the part where the player actually gets teleported if they confirmed. void HandleWarpConfirmation(PlayState* play, Player* player) {
void HandleWarpConfirmation(PlayState* play, Player* player, PauseWarpState* state) { if (play->msgCtx.choiceIndex == 0) Entrance_SetWarpSongEntrance();
if (play->msgCtx.choiceIndex == 0) Entrance_SetWarpSongEntrance(); // Teleporting the player if 'ok' was selected player->stateFlags1 &= ~PLAYER_STATE1_IN_CUTSCENE;
player->stateFlags1 &= ~PLAYER_STATE1_IN_CUTSCENE; // Re-enabling player input ResetStateFlags(&state);
ResetStateFlags(state); // Resetting the state flags
} }
// Managing the cooldowns for different actions and transitions void HandleCooldowns() {
void HandleCooldowns(PauseWarpState* state) { if (state.aButtonCooldown > 0) state.aButtonCooldown--;
// Decreasing the cooldowns if they're greater than zero if (state.textboxCheckCooldown > 0) state.textboxCheckCooldown--;
if (state->aButtonCooldown > 0) state->aButtonCooldown--; if (state.textboxOpenCooldown > 0) state.textboxOpenCooldown--;
if (state->textboxCheckCooldown > 0) state->textboxCheckCooldown--; if (state.isTextboxClosing) ResetStateFlags(&state);
if (state->textboxOpenCooldown > 0) state->textboxOpenCooldown--;
// If the textbox is closing, reset the flag
if (state->isTextboxClosing) *state = (PauseWarpState){.isTextboxClosing = false};
} }
// The main function that gets called every frame
void PauseWarp_Main() { void PauseWarp_Main() {
LUSLOG_CRITICAL("PauseWarp_Main Called"); if (!IsPauseWarpEnabled()) return;
static PauseWarpState state; // The state is static so it retains its value between function calls
// Checking if the pause warp feature is enabled
int pauseWarpEnabled = CVarGetInteger("gPauseWarpEnabled", 0);
PlayState* play = gPlayState; PlayState* play = gPlayState;
Player* player = play ? GET_PLAYER(play) : NULL; Player* player = GET_PLAYER(play);
// If pause warp is not enabled or the state is invalid, reset the state and exit
if (!pauseWarpEnabled || !IsStateValid(play, player, &state)) return ResetStateFlags(&state);
// Check if a Ocarina in the Ocarina slot
if (gSaveContext.inventory.items[SLOT_OCARINA] == ITEM_NONE) return ResetStateFlags(&state);
// Checking if the 'A' button is pressed
int aButtonPressed = CHECK_BTN_ALL(play->state.input->press.button, BTN_A); int aButtonPressed = CHECK_BTN_ALL(play->state.input->press.button, BTN_A);
// If 'A' is pressed and the cooldowns are zero, and we're not already warping, initiate the warp
if (aButtonPressed && !state.aButtonCooldown && !(state.warpInitiated || state.textboxInitiated || state.inChoosingState)) { if (aButtonPressed && !state.aButtonCooldown && !(state.warpInitiated || state.textboxInitiated || state.inChoosingState)) {
int song = play->pauseCtx.cursorPoint[PAUSE_QUEST]; int song = play->pauseCtx.cursorPoint[PAUSE_QUEST];
if (CHECK_QUEST_ITEM(song) && song >= QUEST_SONG_MINUET && song <= QUEST_SONG_PRELUDE) {
// Check if the player has the selected warp song InitiateWarp(play, player, song);
if (!CHECK_QUEST_ITEM(song)) return; }
// Initiate warp if the song is within the valid range
if (song >= QUEST_SONG_MINUET && song <= QUEST_SONG_PRELUDE) InitiateWarp(play, player, song, &state);
} }
}
void PauseWarp_Idle() {
if (!IsPauseWarpEnabled()) return;
PlayState* play = gPlayState;
Player* player = GET_PLAYER(play);
if (gSaveContext.inventory.items[SLOT_OCARINA] == ITEM_NONE) return ResetStateFlags(&state);
// Depending on the message mode, update the state flags
switch (play->msgCtx.msgMode) { switch (play->msgCtx.msgMode) {
case 6: if (state.warpInitiated) state.textboxInitiated = true; break; case 6: if (state.warpInitiated) state.textboxInitiated = true; break;
case 53: if (state.warpInitiated && state.textboxInitiated) state.inChoosingState = true; break; case 53: if (state.warpInitiated && state.textboxInitiated) state.inChoosingState = true; break;
case 54: if (state.warpInitiated && state.textboxInitiated && state.inChoosingState) HandleWarpConfirmation(play, player, &state); break; case 54: if (state.warpInitiated && state.textboxInitiated && state.inChoosingState) HandleWarpConfirmation(play, player); break;
case 0: ResetStateFlags(&state); break; case 0: ResetStateFlags(&state); break;
} }
// Finally, handle any cooldowns for the next frame HandleCooldowns();
HandleCooldowns(&state);
} }