From d4fff4c5fb182e26601f030386a4ae3bc6910649 Mon Sep 17 00:00:00 2001 From: Archez Date: Wed, 8 May 2024 15:10:12 -0400 Subject: [PATCH] Add skip title opening and create save with debug inventory enhancements (#312) * add skip title opening and save with debug inv enhancements * feedback --- mm/2s2h/BenGui/BenMenuBar.cpp | 23 +++- mm/2s2h/DeveloperTools/DeveloperTools.cpp | 101 +++++++++++++++++- mm/2s2h/DeveloperTools/DeveloperTools.h | 16 +-- .../GameInteractor/GameInteractor.cpp | 4 + .../GameInteractor/GameInteractor.h | 4 +- mm/src/code/z_sram_NES.c | 2 + .../overlays/gamestates/ovl_title/z_title.c | 9 +- 7 files changed, 143 insertions(+), 16 deletions(-) diff --git a/mm/2s2h/BenGui/BenMenuBar.cpp b/mm/2s2h/BenGui/BenMenuBar.cpp index 8d37b4233..e3930f1b6 100644 --- a/mm/2s2h/BenGui/BenMenuBar.cpp +++ b/mm/2s2h/BenGui/BenMenuBar.cpp @@ -27,6 +27,12 @@ static const std::unordered_map textureFilteringMap = { { FILTER_NONE, "None" }, }; +static const std::unordered_map debugSaveOptions = { + { DEBUG_SAVE_INFO_COMPLETE, "100\% save" }, + { DEBUG_SAVE_INFO_VANILLA_DEBUG, "Vanilla debug save" }, + { DEBUG_SAVE_INFO_NONE, "Empty save" }, +}; + static const std::unordered_map audioBackendsMap = { { Ship::AudioBackend::WASAPI, "Windows Audio Session API" }, { Ship::AudioBackend::SDL, "SDL" }, @@ -301,6 +307,9 @@ void DrawEnhancementsMenu() { if (UIWidgets::BeginMenu("Cutscenes")) { UIWidgets::CVarCheckbox("Hide Title Cards", "gEnhancements.Cutscenes.HideTitleCards"); UIWidgets::CVarCheckbox("Skip Entrance Cutscenes", "gEnhancements.Cutscenes.SkipEntranceCutscenes"); + UIWidgets::CVarCheckbox( + "Skip to File Select", "gEnhancements.Cutscenes.SkipToFileSelect", + { .tooltip = "Skip the opening title sequence and go straight to the file select menu after boot" }); ImGui::EndMenu(); } @@ -444,7 +453,19 @@ void DrawDeveloperToolsMenu() { UIWidgets::CVarCheckbox("Debug Mode", "gDeveloperTools.DebugEnabled", { .tooltip = "Enables Debug Mode, allowing you to select maps with L + R + Z." }); - + + if (CVarGetInteger("gDeveloperTools.DebugEnabled", 0)) { + if (UIWidgets::CVarCombobox( + "Debug Save File Mode", "gDeveloperTools.DebugSaveFileMode", debugSaveOptions, + { .tooltip = + "Change the behavior of creating saves while debug mode is enabled:\n\n" + "- Empty Save: The default 3 heart save file in first cycle\n" + "- Vanilla Debug Save: Uses the title screen save info (8 hearts, all items and masks)\n" + "- 100\% Save: All items, equipment, mask, quast status and bombers notebook complete" })) { + RegisterDebugSaveCreate(); + } + } + UIWidgets::CVarCheckbox("Better Map Select", "gDeveloperTools.BetterMapSelect.Enabled"); if (UIWidgets::CVarCheckbox("Prevent Actor Update", "gDeveloperTools.PreventActorUpdate")) { RegisterPreventActorUpdateHooks(); diff --git a/mm/2s2h/DeveloperTools/DeveloperTools.cpp b/mm/2s2h/DeveloperTools/DeveloperTools.cpp index 78a41cb1a..6caef923b 100644 --- a/mm/2s2h/DeveloperTools/DeveloperTools.cpp +++ b/mm/2s2h/DeveloperTools/DeveloperTools.cpp @@ -1,10 +1,108 @@ #include "DeveloperTools.h" -#include +#include "BenPort.h" +#include #include #include "2s2h/Enhancements/GameInteractor/GameInteractor.h" extern "C" { +#include "macros.h" #include "z64actor.h" +#include "z64save.h" +#include "variables.h" +#include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" + +void Sram_InitDebugSave(void); +void Flags_SetWeekEventReg(s32 flag); +void Inventory_ChangeUpgrade(s16 upgrade, u32 value); + +extern u16 sPersistentCycleWeekEventRegs[ARRAY_COUNT(gSaveContext.save.saveInfo.weekEventReg)]; +} + +void RegisterDebugSaveCreate() { + static HOOK_ID hookId = 0; + if (hookId != 0) { + GameInteractor::Instance->UnregisterGameHook(hookId); + hookId = 0; + } + + if (CVarGetInteger("gDeveloperTools.DebugSaveFileMode", DEBUG_SAVE_INFO_NONE) != DEBUG_SAVE_INFO_NONE) { + hookId = GameInteractor::Instance->RegisterGameHook([](s16 fileNum) { + u8 playerName[8]; + + // Copy player name and set back after debug save init + memcpy(playerName, gSaveContext.save.saveInfo.playerData.playerName, sizeof(playerName)); + + Sram_InitDebugSave(); + + memcpy(gSaveContext.save.saveInfo.playerData.playerName, playerName, sizeof(playerName)); + + // Place link at entrance from clock tower + gSaveContext.save.entrance = ENTRANCE(SOUTH_CLOCK_TOWN, 0); + gSaveContext.save.cutsceneIndex = 0; + gSaveContext.save.saveInfo.checksum = 1; + + if (CVarGetInteger("gDeveloperTools.DebugSaveFileMode", DEBUG_SAVE_INFO_NONE) == DEBUG_SAVE_INFO_COMPLETE) { + gSaveContext.save.saveInfo.playerData.doubleDefense = true; + gSaveContext.save.saveInfo.playerData.health = 20 * 0x10; + gSaveContext.save.saveInfo.playerData.healthCapacity = 20 * 0x10; + gSaveContext.save.saveInfo.playerData.isDoubleMagicAcquired = true; + gSaveContext.save.saveInfo.playerData.magicLevel = 2; + gSaveContext.save.saveInfo.playerData.magic = MAGIC_DOUBLE_METER; + gSaveContext.save.saveInfo.playerData.owlActivationFlags = (1 << (OWL_WARP_STONE_TOWER + 1)) - 1; + gSaveContext.save.saveInfo.playerData.unk_20 = OWL_WARP_CLOCK_TOWN; + + gSaveContext.save.saveInfo.inventory.defenseHearts = 20; + gSaveContext.save.saveInfo.worldMapCloudVisibility = (1 << WORLD_MAP_NUM_CLOUDS) - 1; + gSaveContext.save.saveInfo.regionsVisited = (1 << REGION_MAX) - 1; + gSaveContext.magicCapacity = MAGIC_DOUBLE_METER; + + Inventory_ChangeUpgrade(UPG_WALLET, 2); + Inventory_ChangeUpgrade(UPG_BOMB_BAG, 3); + Inventory_ChangeUpgrade(UPG_QUIVER, 3); + + gSaveContext.save.saveInfo.playerData.rupees = CUR_CAPACITY(UPG_WALLET); + AMMO(ITEM_BOW) = CUR_CAPACITY(UPG_QUIVER); + AMMO(ITEM_BOMB) = AMMO(ITEM_BOMBCHU) = CUR_CAPACITY(UPG_BOMB_BAG); + AMMO(ITEM_DEKU_STICK) = CUR_CAPACITY(UPG_DEKU_STICKS); + AMMO(ITEM_DEKU_NUT) = CUR_CAPACITY(UPG_DEKU_NUTS); + AMMO(ITEM_MAGIC_BEANS) = 20; + AMMO(ITEM_POWDER_KEG) = 1; + + SET_EQUIP_VALUE(EQUIP_TYPE_SHIELD, EQUIP_VALUE_SHIELD_MIRROR); + SET_EQUIP_VALUE(EQUIP_TYPE_SWORD, EQUIP_VALUE_SWORD_GILDED); + BUTTON_ITEM_EQUIP(0, EQUIP_SLOT_B) = ITEM_SWORD_GILDED; + + for (int32_t i = SLOT_BOTTLE_1; i <= SLOT_BOTTLE_6; i++) { + if (gSaveContext.save.saveInfo.inventory.items[i] == ITEM_NONE) { + gSaveContext.save.saveInfo.inventory.items[i] = ITEM_BOTTLE; + } + } + + for (int32_t i = QUEST_REMAINS_ODOLWA; i <= QUEST_BOMBERS_NOTEBOOK; i++) { + if (i != QUEST_SHIELD && i != QUEST_SWORD && i != QUEST_SONG_SARIA && i != QUEST_SONG_SUN) { + SET_QUEST_ITEM(i); + } + } + + // Use the persistent cycle events to set what a 100% save would normally keep + for (int32_t i = 0; i < ARRAY_COUNT(sPersistentCycleWeekEventRegs); i++) { + u16 isPersistentBits = sPersistentCycleWeekEventRegs[i]; + + // Force all bits on + gSaveContext.save.saveInfo.weekEventReg[i] = 0xFF; + + // Then unset any bits that aren't persistent + for (int32_t j = 0; j < 8; j++) { + if (!(isPersistentBits & 3)) { + gSaveContext.save.saveInfo.weekEventReg[i] = + gSaveContext.save.saveInfo.weekEventReg[i] & (0xFF ^ (1 << j)); + } + isPersistentBits >>= 2; + } + } + } + }); + } } void RegisterPreventActorUpdateHooks() { @@ -50,6 +148,7 @@ void RegisterPreventActorInitHooks() { } void InitDeveloperTools() { + RegisterDebugSaveCreate(); RegisterPreventActorUpdateHooks(); RegisterPreventActorDrawHooks(); RegisterPreventActorInitHooks(); diff --git a/mm/2s2h/DeveloperTools/DeveloperTools.h b/mm/2s2h/DeveloperTools/DeveloperTools.h index e52744d1d..3723d75d6 100644 --- a/mm/2s2h/DeveloperTools/DeveloperTools.h +++ b/mm/2s2h/DeveloperTools/DeveloperTools.h @@ -1,14 +1,16 @@ -#pragma once +#ifndef DEVELOPER_TOOLS_H +#define DEVELOPER_TOOLS_H -#ifdef __cplusplus -extern "C" { -#endif +enum DebugSaveInfo { + DEBUG_SAVE_INFO_NONE, + DEBUG_SAVE_INFO_VANILLA_DEBUG, + DEBUG_SAVE_INFO_COMPLETE, +}; +void RegisterDebugSaveCreate(); void RegisterPreventActorUpdateHooks(); void RegisterPreventActorDrawHooks(); void RegisterPreventActorInitHooks(); void InitDeveloperTools(); -#ifdef __cplusplus -} -#endif \ No newline at end of file +#endif // DEVELOPER_TOOLS_H diff --git a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp index 72cc84543..75a521bd6 100644 --- a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp +++ b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp @@ -18,6 +18,10 @@ void GameInteractor_ExecuteOnGameStateUpdate() { GameInteractor::Instance->ExecuteHooks(); } +void GameInteractor_ExecuteOnSaveInit(s16 fileNum) { + GameInteractor::Instance->ExecuteHooks(fileNum); +} + void GameInteractor_ExecuteBeforeEndOfCycleSave() { GameInteractor::Instance->ExecuteHooks(); } diff --git a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h index e72059282..3cce15a83 100644 --- a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h +++ b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h @@ -215,6 +215,7 @@ public: DEFINE_HOOK(OnGameStateMainFinish, ()); DEFINE_HOOK(OnGameStateDrawFinish, ()); DEFINE_HOOK(OnGameStateUpdate, ()); + DEFINE_HOOK(OnSaveInit, (s16 fileNum)); DEFINE_HOOK(BeforeEndOfCycleSave, ()); DEFINE_HOOK(AfterEndOfCycleSave, ()); @@ -248,6 +249,7 @@ extern "C" { void GameInteractor_ExecuteOnGameStateMainFinish(); void GameInteractor_ExecuteOnGameStateDrawFinish(); void GameInteractor_ExecuteOnGameStateUpdate(); +void GameInteractor_ExecuteOnSaveInit(s16 fileNum); void GameInteractor_ExecuteBeforeEndOfCycleSave(); void GameInteractor_ExecuteAfterEndOfCycleSave(); @@ -280,4 +282,4 @@ bool GameInteractor_Should(GIVanillaBehavior flag, bool result, void* optionalAr } #endif -#endif // GAME_INTERACTOR_H \ No newline at end of file +#endif // GAME_INTERACTOR_H diff --git a/mm/src/code/z_sram_NES.c b/mm/src/code/z_sram_NES.c index 35094b015..96ef6be70 100644 --- a/mm/src/code/z_sram_NES.c +++ b/mm/src/code/z_sram_NES.c @@ -1810,6 +1810,8 @@ void Sram_InitSave(FileSelectState* fileSelect2, SramContext* sramCtx) { gSaveContext.save.saveInfo.checksum = Sram_CalcChecksum(&gSaveContext.save, sizeof(Save)); + GameInteractor_ExecuteOnSaveInit(fileSelect->buttonIndex); + memcpy(sramCtx->saveBuf, &gSaveContext.save, sizeof(Save)); memcpy(&sramCtx->saveBuf[0x2000], &gSaveContext.save, sizeof(Save)); diff --git a/mm/src/overlays/gamestates/ovl_title/z_title.c b/mm/src/overlays/gamestates/ovl_title/z_title.c index 599e0ec1d..3357d1928 100644 --- a/mm/src/overlays/gamestates/ovl_title/z_title.c +++ b/mm/src/overlays/gamestates/ovl_title/z_title.c @@ -11,7 +11,7 @@ #include "overlays/gamestates/ovl_opening/z_opening.h" #include "misc/nintendo_rogo_static/nintendo_rogo_static.h" -#include "overlays/gamestates/ovl_select/z_select.h" +#include "overlays/gamestates/ovl_file_choose/z_file_select.h" #include "build.h" #include "BenPort.h" #include @@ -201,11 +201,8 @@ void ConsoleLogo_Main(GameState* thisx) { gSaveContext.gameMode = GAMEMODE_TITLE_SCREEN; STOP_GAMESTATE(&this->state); - // #region 2S2H [Debug] Eventually we'll get rid of this - if (CVarGetInteger("gDeveloperTools.DebugEnabled", 0)) { - gSaveContext.gameMode = GAMEMODE_NORMAL; - SET_NEXT_GAMESTATE(&this->state, MapSelect_Init, sizeof(MapSelectState)); - // #endregion + if (CVarGetInteger("gEnhancements.Cutscenes.SkipToFileSelect", 0)) { + SET_NEXT_GAMESTATE(&this->state, FileSelect_Init, sizeof(FileSelectState)); } else { SET_NEXT_GAMESTATE(&this->state, TitleSetup_Init, sizeof(TitleSetupState)); }