Pause Warp Enhancement

This commit introduces the PauseWarp mod, a feature that allows players to warp to different locations in the game directly from the pause menu.

- Add PauseWarpState structure to manage flags and cooldowns for the pause warp feature.
- Implement IsStateValid function for state validation.
- Implement ResetStateFlags function to reset all state flags to default values.
- Add InitiateWarp function to handle the initiation of warp sequences.
- Implement HandleWarpConfirmation function to confirm and execute warp actions.
- Implement HandleCooldowns function to manage various cooldown timers.
- Add PauseWarp_Main function as the main logic, called every frame to handle pause warp functionality.
- Map warp song messages to in-game text messages.
This commit is contained in:
mckinlee 2023-08-31 21:52:40 -04:00
parent bb643661f6
commit ee0d0b0929
2 changed files with 100 additions and 1 deletions

View File

@ -0,0 +1,97 @@
// Importing necessary libraries and headers
#include <stdbool.h>
#include <stdint.h>
#include "soh/Enhancements/gameconsole.h"
#include "global.h"
#include "../custom-message/CustomMessageTypes.h"
// Defining the structure for the pause warp state, which holds different flags and cooldowns
typedef struct {
bool warpInitiated, textboxInitiated, inChoosingState, textboxIsOpen, isTextboxClosing;
int aButtonCooldown, textboxCheckCooldown, textboxOpenCooldown;
} 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 };
// Forward declaring the functions used in this file
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.
bool IsStateValid(PlayState* play, Player* player, PauseWarpState* state) {
return play && player && state;
}
// 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) {
*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, PauseWarpState* state) {
int idx = song - QUEST_SONG_MINUET; // Calculating the song index
play->msgCtx.lastPlayedSong = idx; // Storing the last played song
play->pauseCtx.state = 0x12; // Setting the pause state
Message_StartTextbox(play, songMessageMap[idx], NULL); // Starting the textbox with the appropriate message
player->stateFlags1 |= PLAYER_STATE1_INPUT_DISABLED; // Disabling player input
*state = (PauseWarpState){.warpInitiated = true, .textboxOpenCooldown = 10, .aButtonCooldown = 10, .textboxCheckCooldown = 5, .textboxIsOpen = true}; // Setting the flags for warp
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, PauseWarpState* state) {
if (play->msgCtx.choiceIndex == 0) Entrance_SetWarpSongEntrance(); // Teleporting the player if 'ok' was selected
player->stateFlags1 &= ~PLAYER_STATE1_INPUT_DISABLED; // Re-enabling player input
ResetStateFlags(state); // Resetting the state flags
}
// Managing the cooldowns for different actions and transitions
void HandleCooldowns(PauseWarpState* state) {
// Decreasing the cooldowns if they're greater than zero
if (state->aButtonCooldown > 0) state->aButtonCooldown--;
if (state->textboxCheckCooldown > 0) state->textboxCheckCooldown--;
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() {
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;
Player* player = play ? GET_PLAYER(play) : NULL;
// 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);
// 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)) {
int song = play->pauseCtx.cursorPoint[PAUSE_QUEST];
if (song >= QUEST_SONG_MINUET && song <= QUEST_SONG_PRELUDE) InitiateWarp(play, player, song, &state);
}
// Depending on the message mode, update the state flags
switch (play->msgCtx.msgMode) {
case 6: if (state.warpInitiated) state.textboxInitiated = 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 0: ResetStateFlags(&state); break;
}
// Finally, handle any cooldowns for the next frame
HandleCooldowns(&state);
}

View File

@ -560,11 +560,13 @@ void DrawEnhancementsMenu() {
"- Obtained the Master Sword\n"
"- Not within range of Time Block\n"
"- Not within range of Ocarina playing spots");
UIWidgets::PaddedEnhancementCheckbox("Pause Warp", "gPauseWarpEnabled", false);
UIWidgets::Tooltip("Warp by selecing warp song in pause menu.");
ImGui::EndMenu();
}
UIWidgets::Spacer(0);
if (ImGui::BeginMenu("Items"))
{
UIWidgets::PaddedEnhancementCheckbox("Instant Putaway", "gInstantPutaway", true, false);