From 1279932d6d4bab4298d3a583fdc494609849f86e Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 11 Aug 2024 10:45:25 -0400 Subject: [PATCH 01/52] Initial implementation --- mm/2s2h/BenGui/BenMenuBar.cpp | 3 + mm/2s2h/Enhancements/Enhancements.cpp | 1 + mm/2s2h/Enhancements/Enhancements.h | 1 + .../GameInteractor/GameInteractor.cpp | 21 ++ .../GameInteractor/GameInteractor.h | 11 ++ mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 179 ++++++++++++++++++ mm/2s2h/Enhancements/Masks/EasyMaskEquip.h | 15 ++ mm/include/functions.h | 1 + mm/src/code/z_player_lib.c | 3 + .../actors/ovl_player_actor/z_player.c | 10 +- .../ovl_kaleido_scope/z_kaleido_mask.c | 7 +- .../ovl_kaleido_scope/z_kaleido_scope_NES.c | 10 + 12 files changed, 259 insertions(+), 3 deletions(-) create mode 100644 mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp create mode 100644 mm/2s2h/Enhancements/Masks/EasyMaskEquip.h diff --git a/mm/2s2h/BenGui/BenMenuBar.cpp b/mm/2s2h/BenGui/BenMenuBar.cpp index edf2189099..0c47423421 100644 --- a/mm/2s2h/BenGui/BenMenuBar.cpp +++ b/mm/2s2h/BenGui/BenMenuBar.cpp @@ -588,6 +588,9 @@ void DrawEnhancementsMenu() { UIWidgets::CVarCheckbox("Fierce Deity's Mask Anywhere", "gEnhancements.Masks.FierceDeitysAnywhere", { .tooltip = "Allow using Fierce Deity's mask outside of boss rooms." }); UIWidgets::CVarCheckbox("No Blast Mask Cooldown", "gEnhancements.Masks.NoBlastMaskCooldown", {}); + UIWidgets::CVarCheckbox( + "Easy Mask Equip", "gEnhancements.Masks.EasyMaskEquip", + { .tooltip = "Allows you to equip masks directly from the pause menu by pressing A." }); ImGui::EndMenu(); } diff --git a/mm/2s2h/Enhancements/Enhancements.cpp b/mm/2s2h/Enhancements/Enhancements.cpp index a5163d2aa0..730081f802 100644 --- a/mm/2s2h/Enhancements/Enhancements.cpp +++ b/mm/2s2h/Enhancements/Enhancements.cpp @@ -36,6 +36,7 @@ void InitEnhancements() { RegisterFierceDeityAnywhere(); RegisterBlastMaskKeg(); RegisterNoBlastMaskCooldown(); + RegisterEasyMaskEquip(); // Minigames RegisterAlwaysWinDoggyRace(); diff --git a/mm/2s2h/Enhancements/Enhancements.h b/mm/2s2h/Enhancements/Enhancements.h index 4d6dd518d8..531cf2c03a 100644 --- a/mm/2s2h/Enhancements/Enhancements.h +++ b/mm/2s2h/Enhancements/Enhancements.h @@ -17,6 +17,7 @@ #include "Masks/FierceDeityAnywhere.h" #include "Masks/NoBlastMaskCooldown.h" #include "Masks/FastTransformation.h" +#include "Masks/EasyMaskEquip.h" #include "Minigames/AlwaysWinDoggyRace.h" #include "Cutscenes/Cutscenes.h" #include "Restorations/FlipHopVariable.h" diff --git a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp index d0d9e4218e..c58646f362 100644 --- a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp +++ b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.cpp @@ -27,6 +27,21 @@ void GameInteractor_ExecuteOnKaleidoUpdate(PauseContext* pauseCtx) { GameInteractor::Instance->ExecuteHooks(pauseCtx); } +void GameInteractor_ExecuteBeforeKaleidoDrawPage(PauseContext* pauseCtx, u16 pauseIndex) { + GameInteractor::Instance->ExecuteHooks(pauseCtx, pauseIndex); + GameInteractor::Instance->ExecuteHooksForID(pauseIndex, pauseCtx, + pauseIndex); +} + +void GameInteractor_ExecuteAfterKaleidoDrawPage(PauseContext* pauseCtx, u16 pauseIndex) { + GameInteractor::Instance->ExecuteHooks(pauseCtx, pauseIndex); + GameInteractor::Instance->ExecuteHooksForID(pauseIndex, pauseCtx, pauseIndex); +} + +void GameInteractor_ExecuteOnKaleidoClose() { + GameInteractor::Instance->ExecuteHooks(); +} + void GameInteractor_ExecuteOnSaveInit(s16 fileNum) { GameInteractor::Instance->ExecuteHooks(fileNum); } @@ -116,6 +131,12 @@ void GameInteractor_ExecuteOnActorKill(Actor* actor) { GameInteractor::Instance->ExecuteHooksForFilter(actor); } +void GameInteractor_ExecuteOnPlayerPostLimbDraw(Player* player, s32 limbIndex) { + GameInteractor::Instance->ExecuteHooks(player, limbIndex); + GameInteractor::Instance->ExecuteHooksForID(limbIndex, player, limbIndex); + GameInteractor::Instance->ExecuteHooksForFilter(player, limbIndex); +} + void GameInteractor_ExecuteOnSceneFlagSet(s16 sceneId, FlagType flagType, u32 flag) { SPDLOG_DEBUG("OnSceneFlagSet: sceneId: {}, flagType: {}, flag: {}", sceneId, (u32)flagType, flag); GameInteractor::Instance->ExecuteHooks(sceneId, flagType, flag); diff --git a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h index 365a0f1823..8e769a75c8 100644 --- a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h +++ b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h @@ -260,6 +260,9 @@ class GameInteractor { DEFINE_HOOK(OnGameStateUpdate, ()); DEFINE_HOOK(OnConsoleLogoUpdate, ()); DEFINE_HOOK(OnKaleidoUpdate, (PauseContext * pauseCtx)); + DEFINE_HOOK(BeforeKaleidoDrawPage, (PauseContext * pauseCtx, u16 pauseIndex)); + DEFINE_HOOK(AfterKaleidoDrawPage, (PauseContext * pauseCtx, u16 pauseIndex)); + DEFINE_HOOK(OnKaleidoClose, ()); DEFINE_HOOK(OnSaveInit, (s16 fileNum)); DEFINE_HOOK(BeforeEndOfCycleSave, ()); DEFINE_HOOK(AfterEndOfCycleSave, ()); @@ -276,6 +279,7 @@ class GameInteractor { DEFINE_HOOK(ShouldActorDraw, (Actor * actor, bool* should)); DEFINE_HOOK(OnActorDraw, (Actor * actor)); DEFINE_HOOK(OnActorKill, (Actor * actor)); + DEFINE_HOOK(OnPlayerPostLimbDraw, (Player * player, s32 limbIndex)); DEFINE_HOOK(OnSceneFlagSet, (s16 sceneId, FlagType flagType, u32 flag)); DEFINE_HOOK(OnSceneFlagUnset, (s16 sceneId, FlagType flagType, u32 flag)); @@ -293,6 +297,9 @@ class GameInteractor { DEFINE_HOOK(ShouldItemGive, (u8 item, bool* should)); DEFINE_HOOK(OnItemGive, (u8 item)); + DEFINE_HOOK(OnMaskTransform, (Player * player, PlayerMask maskId)); + DEFINE_HOOK(OnTransformationComplete, (Player * player)); + DEFINE_HOOK(ShouldVanillaBehavior, (GIVanillaBehavior flag, bool* should, void* optionalArg)); }; @@ -304,6 +311,9 @@ void GameInteractor_ExecuteOnGameStateDrawFinish(); void GameInteractor_ExecuteOnGameStateUpdate(); void GameInteractor_ExecuteOnConsoleLogoUpdate(); void GameInteractor_ExecuteOnKaleidoUpdate(PauseContext* pauseCtx); +void GameInteractor_ExecuteBeforeKaleidoDrawPage(PauseContext* pauseCtx, u16 pauseIndex); +void GameInteractor_ExecuteAfterKaleidoDrawPage(PauseContext* pauseCtx, u16 pauseIndex); +void GameInteractor_ExecuteOnKaleidoClose(); void GameInteractor_ExecuteOnSaveInit(s16 fileNum); void GameInteractor_ExecuteBeforeEndOfCycleSave(); void GameInteractor_ExecuteAfterEndOfCycleSave(); @@ -320,6 +330,7 @@ void GameInteractor_ExecuteOnActorUpdate(Actor* actor); bool GameInteractor_ShouldActorDraw(Actor* actor); void GameInteractor_ExecuteOnActorDraw(Actor* actor); void GameInteractor_ExecuteOnActorKill(Actor* actor); +void GameInteractor_ExecuteOnPlayerPostLimbDraw(Player* player, s32 limbIndex); void GameInteractor_ExecuteOnSceneFlagSet(s16 sceneId, FlagType flagType, u32 flag); void GameInteractor_ExecuteOnSceneFlagUnset(s16 sceneId, FlagType flagType, u32 flag); diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp new file mode 100644 index 0000000000..6ad4b9a296 --- /dev/null +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -0,0 +1,179 @@ +#include "EasyMaskEquip.h" +#include +#include "Enhancements/GameInteractor/GameInteractor.h" +#include "Enhancements/FrameInterpolation/FrameInterpolation.h" + +extern "C" { +#include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" +#include "interface/parameter_static/parameter_static.h" +} + +static Vtx* easyMaskEquipVtx; +static s16 sPendingMask = ITEM_NONE; +static bool sIsTransforming = false; + +extern "C" bool EasyMaskEquip_IsEnabled() { + return CVarGetInteger("gEnhancements.Masks.EasyMaskEquip", 0) && + gPlayState->pauseCtx.debugEditor == DEBUG_EDITOR_NONE; +} + +s16 GetEquippedMaskSlot() { + s16 equippedMask = Player_GetCurMaskItemId(gPlayState); + if (equippedMask == ITEM_NONE || sIsTransforming) { + equippedMask = sPendingMask; + } + + // Check for pending mask first + if (sPendingMask != ITEM_NONE) { + for (s16 i = 0; i < MASK_NUM_SLOTS; i++) { + if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] == sPendingMask) { + return i; + } + } + } + + // Check for equipped mask + for (s16 i = 0; i < MASK_NUM_SLOTS; i++) { + if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] == equippedMask) { + return i; + } + } + return -1; +} + +void UpdateEasyMaskEquipVtx(PauseContext* pauseCtx) { + s16 slot = GetEquippedMaskSlot(); + if (slot == -1) { + return; + } + + s16 slotX = slot % MASK_GRID_COLS; + s16 slotY = slot / MASK_GRID_COLS; + s16 initialX = 0 - (MASK_GRID_COLS * MASK_GRID_CELL_WIDTH) / 2; + s16 initialY = (MASK_GRID_ROWS * MASK_GRID_CELL_HEIGHT) / 2 - 6; + s16 vtxX = initialX + (slotX * MASK_GRID_CELL_WIDTH); + s16 vtxY = initialY - (slotY * MASK_GRID_CELL_HEIGHT) + pauseCtx->offsetY; + + easyMaskEquipVtx[0].v.ob[0] = easyMaskEquipVtx[2].v.ob[0] = vtxX; + easyMaskEquipVtx[1].v.ob[0] = easyMaskEquipVtx[3].v.ob[0] = vtxX + MASK_GRID_CELL_WIDTH; + easyMaskEquipVtx[0].v.ob[1] = easyMaskEquipVtx[1].v.ob[1] = vtxY; + easyMaskEquipVtx[2].v.ob[1] = easyMaskEquipVtx[3].v.ob[1] = vtxY - MASK_GRID_CELL_HEIGHT; +} + +void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { + s16 slot = GetEquippedMaskSlot(); + if (slot == -1) { + return; + } + + GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; + + OPEN_DISPS(gfxCtx); + + UpdateEasyMaskEquipVtx(pauseCtx); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + gDPSetCombineLERP(POLY_OPA_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + gSPVertex(POLY_OPA_DISP++, (uintptr_t)easyMaskEquipVtx, 4, 0); + gDPLoadTextureBlock(POLY_OPA_DISP++, gEquippedItemOutlineTex, G_IM_FMT_IA, G_IM_SIZ_8b, 32, 32, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, 0, 2, 3, 1, 0); + + CLOSE_DISPS(gfxCtx); +} + +void AllocateEasyMaskEquipVtx(GraphicsContext* gfxCtx) { + easyMaskEquipVtx = (Vtx*)GRAPH_ALLOC(gfxCtx, 4 * sizeof(Vtx)); + for (int i = 0; i < 4; i++) { + easyMaskEquipVtx[i].v.ob[2] = 0; + easyMaskEquipVtx[i].v.flag = 0; + easyMaskEquipVtx[i].v.tc[0] = (i & 1) ? 32 << 5 : 0; + easyMaskEquipVtx[i].v.tc[1] = (i & 2) ? 32 << 5 : 0; + easyMaskEquipVtx[i].v.cn[0] = easyMaskEquipVtx[i].v.cn[1] = easyMaskEquipVtx[i].v.cn[2] = + easyMaskEquipVtx[i].v.cn[3] = 255; + } +} + +void HandleEasyMaskEquip(PauseContext* pauseCtx) { + if (pauseCtx->state == PAUSE_STATE_MAIN && pauseCtx->mainState == PAUSE_MAIN_STATE_IDLE) { + if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_A)) { + s16 cursorItem = pauseCtx->cursorItem[PAUSE_MASK]; + if (cursorItem != PAUSE_ITEM_NONE) { + sPendingMask = cursorItem; + sIsTransforming = + (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || + cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT); + UpdateEasyMaskEquipVtx(pauseCtx); + } + } else if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_B)) { + sPendingMask = ITEM_NONE; + sIsTransforming = false; + } + } +} + +void RegisterEasyMaskEquip() { + auto gameInteractor = GameInteractor::Instance; + + gameInteractor->RegisterGameHook([](PauseContext* pauseCtx) { + if (EasyMaskEquip_IsEnabled() && pauseCtx->pageIndex == PAUSE_MASK) { + HandleEasyMaskEquip(pauseCtx); + } + }); + + gameInteractor->RegisterGameHookForID( + PAUSE_MASK, [](PauseContext* pauseCtx, u16 pageIndex) { + if (EasyMaskEquip_IsEnabled() && pauseCtx->pageIndex == PAUSE_MASK) { + GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; + OPEN_DISPS(gfxCtx); + AllocateEasyMaskEquipVtx(gfxCtx); + UpdateEasyMaskEquipVtx(pauseCtx); + DrawEasyMaskEquipBorder(pauseCtx); + CLOSE_DISPS(gfxCtx); + } + }); + + gameInteractor->RegisterGameHook([]() { + if (EasyMaskEquip_IsEnabled() && gPlayState->pauseCtx.pageIndex == PAUSE_MASK) { + Player* player = GET_PLAYER(gPlayState); + if (sPendingMask != ITEM_NONE) { + if (sPendingMask != ITEM_MASK_DEKU && sPendingMask != ITEM_MASK_GORON && + sPendingMask != ITEM_MASK_ZORA && sPendingMask != ITEM_MASK_FIERCE_DEITY && + sPendingMask != ITEM_MASK_GIANT) { + if (player->transformation != PLAYER_FORM_HUMAN) { + // Equip the new mask directly if not a transformation mask and player is not human + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sIsTransforming = true; + } else { + // Equip the new mask directly if not a transformation mask and player is human + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sPendingMask = ITEM_NONE; // Clear pending mask after equipping + } + } else { + // Handle transformation masks + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sIsTransforming = true; + } + } + } + }); + + gameInteractor->RegisterGameHook([](Actor* actor) { + if (EasyMaskEquip_IsEnabled() && actor->id == ACTOR_PLAYER) { + Player* player = (Player*)actor; + if (sIsTransforming && player->transformation == PLAYER_FORM_HUMAN) { + if (player->skelAnime.curFrame == player->skelAnime.endFrame) { + if (sPendingMask != ITEM_NONE) { + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sPendingMask = ITEM_NONE; // Clear pending mask after equipping + sIsTransforming = false; + } + } + } + } + }); +} diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.h b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.h new file mode 100644 index 0000000000..72478bcf3c --- /dev/null +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.h @@ -0,0 +1,15 @@ +#ifndef EASY_MASK_EQUIP_H +#define EASY_MASK_EQUIP_H + +#ifdef __cplusplus +extern "C" { +#endif + +bool EasyMaskEquip_IsEnabled(void); +void RegisterEasyMaskEquip(void); + +#ifdef __cplusplus +} +#endif + +#endif // EASY_MASK_EQUIP_H \ No newline at end of file diff --git a/mm/include/functions.h b/mm/include/functions.h index 62ac3cb604..31c3b0fd43 100644 --- a/mm/include/functions.h +++ b/mm/include/functions.h @@ -1328,6 +1328,7 @@ void osContGetReadData(OSContPad* pad); // #region 2S2H [Port] Previously unavailable functions, made available for porting void PadMgr_ThreadEntry(); void Heaps_Alloc(void); +void Player_UseItem(PlayState* play, Player* this, ItemId item); // #endregion // #region 2S2H [Port] New methods added for porting void AudioSeq_SetPortVolumeScale(u8 seqPlayerIndex, f32 volume); diff --git a/mm/src/code/z_player_lib.c b/mm/src/code/z_player_lib.c index 01556acc8e..2dca51ea72 100644 --- a/mm/src/code/z_player_lib.c +++ b/mm/src/code/z_player_lib.c @@ -43,6 +43,7 @@ // Assets for other actors #include "overlays/actors/ovl_En_Arrow/z_en_arrow.h" +#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" void PlayerCall_Init(Actor* thisx, PlayState* play); void PlayerCall_Destroy(Actor* thisx, PlayState* play); @@ -4020,5 +4021,7 @@ void Player_PostLimbDrawGameplay(PlayState* play, s32 limbIndex, Gfx** dList1, G Player_SetFeetPos(play, player, limbIndex); } + GameInteractor_ExecuteOnPlayerPostLimbDraw(player, limbIndex); + func_8012536C(); } diff --git a/mm/src/overlays/actors/ovl_player_actor/z_player.c b/mm/src/overlays/actors/ovl_player_actor/z_player.c index f3a173303d..b4e9f7eb81 100644 --- a/mm/src/overlays/actors/ovl_player_actor/z_player.c +++ b/mm/src/overlays/actors/ovl_player_actor/z_player.c @@ -47,6 +47,8 @@ #include "2s2h/Enhancements/GameInteractor/GameInteractor.h" +#include "2s2h/Enhancements/Masks/EasyMaskEquip.h" + #define THIS ((Player*)thisx) void Player_Init(Actor* thisx, PlayState* play); @@ -3730,8 +3732,12 @@ void Player_ProcessItemButtons(Player* this, PlayState* play) { if ((maskIdMinusOne < PLAYER_MASK_TRUTH - 1) || (maskIdMinusOne >= PLAYER_MASK_MAX - 1)) { maskIdMinusOne = this->currentMask - 1; } - Player_UseItem(play, this, Player_MaskIdToItemId(maskIdMinusOne)); - return; + + // EasyMaskEquip check allows equipping a mask without it being on a C button or D button + if (!EasyMaskEquip_IsEnabled()) { + Player_UseItem(play, this, Player_MaskIdToItemId(maskIdMinusOne)); + return; + } } if ((this->currentMask == PLAYER_MASK_GIANT) && (gSaveContext.save.saveInfo.playerData.magic == 0)) { diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c index 13583678c4..45629e0122 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c @@ -10,6 +10,8 @@ #include "BenGui/HudEditor.h" #include "2s2h/Enhancements/GameInteractor/GameInteractor.h" +#include "2s2h/Enhancements/Masks/EasyMaskEquip.h" + s16 sMaskEquipState = EQUIP_STATE_MAGIC_ARROW_GROW_ORB; // Timer to hold magic arrow icon over magic arrow slot before moving when equipping. @@ -666,9 +668,12 @@ void KaleidoScope_UpdateMaskCursor(PlayState* play) { sMaskEquipState = EQUIP_STATE_MOVE_TO_C_BTN; sMaskEquipAnimTimer = 10; Audio_PlaySfx(NA_SE_SY_DECIDE); + // EasyMaskEquip check disables the mask description textbox from appearing when a mask is selected + // with A button. } else if ((pauseCtx->debugEditor == DEBUG_EDITOR_NONE) && (pauseCtx->state == PAUSE_STATE_MAIN) && (pauseCtx->mainState == PAUSE_MAIN_STATE_IDLE) && - CHECK_BTN_ALL(input->press.button, BTN_A) && (msgCtx->msgLength == 0)) { + CHECK_BTN_ALL(input->press.button, BTN_A) && (msgCtx->msgLength == 0) && + !EasyMaskEquip_IsEnabled()) { // Give description on item through a message box pauseCtx->itemDescriptionOn = true; if (pauseCtx->cursorYIndex[PAUSE_MASK] < 2) { diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c index 5446f710c9..b124190d81 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c @@ -19,6 +19,7 @@ #include "archives/map_name_static/map_name_static.h" #include "2s2h/Enhancements/FrameInterpolation/FrameInterpolation.h" #include "2s2h/Enhancements/Saving/SavingEnhancements.h" +#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" #include "2s2h_assets.h" @@ -832,7 +833,9 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->itemPageVtx, sItemPageBgTextures); + GameInteractor_ExecuteBeforeKaleidoDrawPage(pauseCtx, pauseCtx->pageIndex); KaleidoScope_DrawItemSelect(play); + GameInteractor_ExecuteAfterKaleidoDrawPage(pauseCtx, pauseCtx->pageIndex); } break; @@ -853,6 +856,7 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->mapPageVtx, sMapPageBgTextures); + GameInteractor_ExecuteBeforeKaleidoDrawPage(pauseCtx, pauseCtx->pageIndex); if (sInDungeonScene) { KaleidoScope_DrawDungeonMap(play); Gfx_SetupDL42_Opa(gfxCtx); @@ -882,6 +886,7 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { KaleidoScope_DrawWorldMap(play); } + GameInteractor_ExecuteAfterKaleidoDrawPage(pauseCtx, pauseCtx->pageIndex); break; case PAUSE_QUEST: @@ -904,7 +909,9 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->questPageVtx, sQuestPageBgTextures); + GameInteractor_ExecuteBeforeKaleidoDrawPage(pauseCtx, pauseCtx->pageIndex); KaleidoScope_DrawQuestStatus(play); + GameInteractor_ExecuteAfterKaleidoDrawPage(pauseCtx, pauseCtx->pageIndex); break; case PAUSE_MASK: @@ -925,7 +932,9 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->maskPageVtx, sMaskPageBgTextures); + GameInteractor_ExecuteBeforeKaleidoDrawPage(pauseCtx, pauseCtx->pageIndex); KaleidoScope_DrawMaskSelect(play); + GameInteractor_ExecuteAfterKaleidoDrawPage(pauseCtx, pauseCtx->pageIndex); break; } } @@ -4124,6 +4133,7 @@ void KaleidoScope_Update(PlayState* play) { gSaveContext.hudVisibility = HUD_VISIBILITY_IDLE; Interface_SetHudVisibility(sUnpausedHudVisibility); Audio_SetPauseState(false); + GameInteractor_ExecuteOnKaleidoClose(); break; default: From 544d73d46fc753b68f192419e36ebb56208c9d8c Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 11 Aug 2024 14:31:09 -0400 Subject: [PATCH 02/52] fixed bug where mask was reequipping on next pause menu close --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index 6ad4b9a296..ce839c9e15 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -10,6 +10,7 @@ extern "C" { static Vtx* easyMaskEquipVtx; static s16 sPendingMask = ITEM_NONE; +static s16 sLastEquippedMask = ITEM_NONE; static bool sIsTransforming = false; extern "C" bool EasyMaskEquip_IsEnabled() { @@ -145,17 +146,18 @@ void RegisterEasyMaskEquip() { sPendingMask != ITEM_MASK_ZORA && sPendingMask != ITEM_MASK_FIERCE_DEITY && sPendingMask != ITEM_MASK_GIANT) { if (player->transformation != PLAYER_FORM_HUMAN) { - // Equip the new mask directly if not a transformation mask and player is not human Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sLastEquippedMask = sPendingMask; sIsTransforming = true; } else { - // Equip the new mask directly if not a transformation mask and player is human Player_UseItem(gPlayState, player, static_cast(sPendingMask)); - sPendingMask = ITEM_NONE; // Clear pending mask after equipping + sLastEquippedMask = sPendingMask; + sPendingMask = ITEM_NONE; } } else { - // Handle transformation masks Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sLastEquippedMask = sPendingMask; + sPendingMask = ITEM_NONE; sIsTransforming = true; } } @@ -169,7 +171,8 @@ void RegisterEasyMaskEquip() { if (player->skelAnime.curFrame == player->skelAnime.endFrame) { if (sPendingMask != ITEM_NONE) { Player_UseItem(gPlayState, player, static_cast(sPendingMask)); - sPendingMask = ITEM_NONE; // Clear pending mask after equipping + sLastEquippedMask = sPendingMask; + sPendingMask = ITEM_NONE; sIsTransforming = false; } } From 37dc19275aad35b8cb6dacba85c98213a48a8880 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 11 Aug 2024 15:36:32 -0400 Subject: [PATCH 03/52] added checks to restrict masks from being equipped in some situations plus moved CHECK_GIVEN_MASK_ON_MOON to macros.h --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 18 ++++++++++++++++++ mm/include/macros.h | 3 +++ .../ovl_kaleido_scope/z_kaleido_mask.c | 4 ++-- 3 files changed, 23 insertions(+), 2 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index ce839c9e15..cc1536ddb1 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -6,6 +6,8 @@ extern "C" { #include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" #include "interface/parameter_static/parameter_static.h" +#include "macros.h" +extern u16 sMasksGivenOnMoonBits[]; } static Vtx* easyMaskEquipVtx; @@ -104,6 +106,22 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_A)) { s16 cursorItem = pauseCtx->cursorItem[PAUSE_MASK]; if (cursorItem != PAUSE_ITEM_NONE) { + Player* player = GET_PLAYER(gPlayState); + + // Check if the player is underwater and trying to equip Deku or Goron mask + if ((Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && + (Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE) && + (cursorItem != ITEM_MASK_ZORA)) { + Audio_PlaySfx(NA_SE_SY_ERROR); + return; + } + + // Check if the mask is given on the moon + if (CHECK_GIVEN_MASK_ON_MOON(cursorItem - ITEM_NUM_SLOTS)) { + Audio_PlaySfx(NA_SE_SY_ERROR); + return; + } + sPendingMask = cursorItem; sIsTransforming = (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || diff --git a/mm/include/macros.h b/mm/include/macros.h index 0ed8d52fdf..79031f3926 100644 --- a/mm/include/macros.h +++ b/mm/include/macros.h @@ -115,6 +115,9 @@ #define D_0F000000_TO_SEGMENTED (0x0F000000 | 1) // Used to compare animations. The statically linked global animations can end up with different pointer addresses if an animation gets set from one file and then compared against the static animation in another #define BEN_ANIM_EQUAL(anim1, anim2) ((anim1 == NULL) ? ((anim2 == NULL) ? true : false) : ((anim2 == NULL) ? false : strcmp(anim1, anim2) == 0)) +// Macro to check if a given mask has been given on the moon +#define CHECK_GIVEN_MASK_ON_MOON(maskIndex) \ + (gSaveContext.masksGivenOnMoon[sMasksGivenOnMoonBits[maskIndex] >> 8] & (u8)sMasksGivenOnMoonBits[maskIndex]) // #endregion #endif // MACROS_H diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c index 45629e0122..16f444f312 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c @@ -10,6 +10,8 @@ #include "BenGui/HudEditor.h" #include "2s2h/Enhancements/GameInteractor/GameInteractor.h" +#include "macros.h" + #include "2s2h/Enhancements/Masks/EasyMaskEquip.h" s16 sMaskEquipState = EQUIP_STATE_MAGIC_ARROW_GROW_ORB; @@ -160,8 +162,6 @@ u8 gMaskPlayerFormSlotRestrictions[PLAYER_FORM_MAX][MASK_NUM_SLOTS] = { #define SET_MOON_MASK_BIT(masksGivenOnMoonIndex, masksGivenOnMoonFlag) \ ((masksGivenOnMoonIndex) << 8 | (masksGivenOnMoonFlag)) -#define CHECK_GIVEN_MASK_ON_MOON(maskIndex) \ - (gSaveContext.masksGivenOnMoon[sMasksGivenOnMoonBits[maskIndex] >> 8] & (u8)sMasksGivenOnMoonBits[maskIndex]) u16 sMasksGivenOnMoonBits[] = { SET_MOON_MASK_BIT(1, 0x1), // SLOT_MASK_POSTMAN From 4f08dacd76dcaa29f3e88ab23e7e15baf46916cb Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 11 Aug 2024 17:32:23 -0400 Subject: [PATCH 04/52] adds check to prevent equip in air and adds selection audio --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index cc1536ddb1..484d30e8a6 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -108,6 +108,14 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { if (cursorItem != PAUSE_ITEM_NONE) { Player* player = GET_PLAYER(gPlayState); + // Check if the player is in the air and trying to equip a transformation mask + if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND) && + (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || + cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT)) { + Audio_PlaySfx(NA_SE_SY_ERROR); + return; + } + // Check if the player is underwater and trying to equip Deku or Goron mask if ((Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && (Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE) && @@ -127,6 +135,7 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT); UpdateEasyMaskEquipVtx(pauseCtx); + Audio_PlaySfx(NA_SE_SY_DECIDE); } } else if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_B)) { sPendingMask = ITEM_NONE; From d43daceed2865302a9adc989707b06e7f9c4356d Mon Sep 17 00:00:00 2001 From: mckinlee Date: Mon, 12 Aug 2024 10:22:28 -0400 Subject: [PATCH 05/52] added checks to prevent equipping transformation masks when the player is busy with an action --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index 484d30e8a6..cbaa1a819d 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -130,6 +130,17 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { return; } + // Check if the player is busy with an action and trying to equip a transformation mask + if ((cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || + cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT) && + ((player->stateFlags1 & (PLAYER_STATE1_800 | PLAYER_STATE1_400 | PLAYER_STATE1_20000000)) || + (player->stateFlags2 & (PLAYER_STATE2_10 | PLAYER_STATE2_80)) || + (player->heldItemAction != PLAYER_IA_NONE) || + (player->meleeWeaponState != PLAYER_MELEE_WEAPON_STATE_0))) { + Audio_PlaySfx(NA_SE_SY_ERROR); + return; + } + sPendingMask = cursorItem; sIsTransforming = (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || From a26f082fb60671b11811f1eb6850921dafd72282 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Mon, 12 Aug 2024 12:33:17 -0400 Subject: [PATCH 06/52] bug fix for border appearing on empty mask slot and removed some residual unused code --- mm/2s2h/Enhancements/GameInteractor/GameInteractor.h | 3 --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 5 +++++ 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h index 5c1a4daa21..b3a7ebd923 100644 --- a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h +++ b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h @@ -298,9 +298,6 @@ class GameInteractor { DEFINE_HOOK(ShouldItemGive, (u8 item, bool* should)); DEFINE_HOOK(OnItemGive, (u8 item)); - DEFINE_HOOK(OnMaskTransform, (Player * player, PlayerMask maskId)); - DEFINE_HOOK(OnTransformationComplete, (Player * player)); - DEFINE_HOOK(ShouldVanillaBehavior, (GIVanillaBehavior flag, bool* should, void* optionalArg)); }; diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index cbaa1a819d..9e2cab29fc 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -69,6 +69,11 @@ void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { return; } + // Check if the player owns the mask + if (gSaveContext.save.saveInfo.inventory.items[slot + ITEM_NUM_SLOTS] == ITEM_NONE) { + return; + } + GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; OPEN_DISPS(gfxCtx); From 8619be52e91e09c1e536038527aca1a5e24809b0 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Tue, 13 Aug 2024 13:09:19 -0400 Subject: [PATCH 07/52] bug fix for border not appearing and added check to prevent equipping any mask in the air when wearing a transformation mask --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 41 +++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index 9e2cab29fc..27d3e23c67 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -21,9 +21,10 @@ extern "C" bool EasyMaskEquip_IsEnabled() { } s16 GetEquippedMaskSlot() { - s16 equippedMask = Player_GetCurMaskItemId(gPlayState); - if (equippedMask == ITEM_NONE || sIsTransforming) { - equippedMask = sPendingMask; + s16 maskToCheck = sPendingMask != ITEM_NONE ? sPendingMask : Player_GetCurMaskItemId(gPlayState); + + if (maskToCheck == ITEM_NONE || sIsTransforming) { + maskToCheck = sLastEquippedMask; } // Check for pending mask first @@ -37,10 +38,11 @@ s16 GetEquippedMaskSlot() { // Check for equipped mask for (s16 i = 0; i < MASK_NUM_SLOTS; i++) { - if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] == equippedMask) { + if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] == maskToCheck) { return i; } } + return -1; } @@ -74,6 +76,11 @@ void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { return; } + // If the equipped item is equal to the pending item, remove the border + if (Player_GetCurMaskItemId(gPlayState) == sPendingMask) { + return; + } + GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; OPEN_DISPS(gfxCtx); @@ -111,14 +118,27 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_A)) { s16 cursorItem = pauseCtx->cursorItem[PAUSE_MASK]; if (cursorItem != PAUSE_ITEM_NONE) { + // If sPendingMask is already the cursor item, remove the border and reset sPendingMask + if (sPendingMask == cursorItem) { + sPendingMask = ITEM_NONE; + sIsTransforming = false; + return; + } + Player* player = GET_PLAYER(gPlayState); // Check if the player is in the air and trying to equip a transformation mask - if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND) && - (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || - cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT)) { - Audio_PlaySfx(NA_SE_SY_ERROR); - return; + if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND)) { + if (player->transformation == PLAYER_FORM_HUMAN) { + if (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || + cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT) { + Audio_PlaySfx(NA_SE_SY_ERROR); + return; + } + } else { + Audio_PlaySfx(NA_SE_SY_ERROR); + return; + } } // Check if the player is underwater and trying to equip Deku or Goron mask @@ -156,6 +176,7 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { } else if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_B)) { sPendingMask = ITEM_NONE; sIsTransforming = false; + sLastEquippedMask = ITEM_NONE; } } } @@ -222,4 +243,4 @@ void RegisterEasyMaskEquip() { } } }); -} +} \ No newline at end of file From 5cd8fadde08dbe6915fb3f6726eb89e296cdae0e Mon Sep 17 00:00:00 2001 From: mckinlee Date: Tue, 13 Aug 2024 13:12:38 -0400 Subject: [PATCH 08/52] forgot to run clang format script --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index 27d3e23c67..199e3ede08 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -130,8 +130,9 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { // Check if the player is in the air and trying to equip a transformation mask if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND)) { if (player->transformation == PLAYER_FORM_HUMAN) { - if (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || - cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT) { + if (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || + cursorItem == ITEM_MASK_ZORA || cursorItem == ITEM_MASK_FIERCE_DEITY || + cursorItem == ITEM_MASK_GIANT) { Audio_PlaySfx(NA_SE_SY_ERROR); return; } From d11de2649bf3f2597ccef7552e01481e590d6f6a Mon Sep 17 00:00:00 2001 From: mckinlee Date: Fri, 13 Sep 2024 09:34:03 -0400 Subject: [PATCH 09/52] Refactor EasyMaskEquip related logic - Streamlined mask selection logic with helper functions - Added grayscale indicators for unavailable masks in the UI - Optimized vertex handling for mask border rendering - Improved game hook integrations for reliable mask equipping - Enhanced code readability and maintainability --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 349 +++++++++++-------- mm/include/functions.h | 1 + 2 files changed, 213 insertions(+), 137 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index 199e3ede08..e1608c4400 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -7,38 +7,51 @@ extern "C" { #include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" #include "interface/parameter_static/parameter_static.h" #include "macros.h" +#include "variables.h" extern u16 sMasksGivenOnMoonBits[]; +extern s16 sPauseMenuVerticalOffset; } -static Vtx* easyMaskEquipVtx; +static Vtx* easyMaskEquipVtx = nullptr; static s16 sPendingMask = ITEM_NONE; static s16 sLastEquippedMask = ITEM_NONE; static bool sIsTransforming = false; -extern "C" bool EasyMaskEquip_IsEnabled() { +// Define transformation masks for easy reference +constexpr std::array TransformationMasks = { ITEM_MASK_DEKU, ITEM_MASK_GORON, ITEM_MASK_ZORA, + ITEM_MASK_FIERCE_DEITY, ITEM_MASK_GIANT }; + +// Helper function to check if a mask is a transformation mask +bool IsTransformationMask(ItemId mask) { + return std::find(TransformationMasks.begin(), TransformationMasks.end(), mask) != TransformationMasks.end(); +} + +// Check if EasyMaskEquip is enabled +bool EasyMaskEquip_IsEnabled() { return CVarGetInteger("gEnhancements.Masks.EasyMaskEquip", 0) && gPlayState->pauseCtx.debugEditor == DEBUG_EDITOR_NONE; } +// Get the equipped mask slot, prioritizing pending masks s16 GetEquippedMaskSlot() { - s16 maskToCheck = sPendingMask != ITEM_NONE ? sPendingMask : Player_GetCurMaskItemId(gPlayState); + s16 maskToCheck = + (sPendingMask != ITEM_NONE && !sIsTransforming) ? sPendingMask : Player_GetCurMaskItemId(gPlayState); + maskToCheck = (maskToCheck == ITEM_NONE || sIsTransforming) ? sLastEquippedMask : maskToCheck; - if (maskToCheck == ITEM_NONE || sIsTransforming) { - maskToCheck = sLastEquippedMask; - } + auto& items = gSaveContext.save.saveInfo.inventory.items; - // Check for pending mask first + // Search for pending mask first if (sPendingMask != ITEM_NONE) { - for (s16 i = 0; i < MASK_NUM_SLOTS; i++) { - if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] == sPendingMask) { + for (s16 i = 0; i < MASK_NUM_SLOTS; ++i) { + if (items[i + ITEM_NUM_SLOTS] == sPendingMask) { return i; } } } - // Check for equipped mask - for (s16 i = 0; i < MASK_NUM_SLOTS; i++) { - if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] == maskToCheck) { + // Then search for the equipped mask + for (s16 i = 0; i < MASK_NUM_SLOTS; ++i) { + if (items[i + ITEM_NUM_SLOTS] == maskToCheck) { return i; } } @@ -46,43 +59,87 @@ s16 GetEquippedMaskSlot() { return -1; } +// Update vertex positions based on the equipped mask slot void UpdateEasyMaskEquipVtx(PauseContext* pauseCtx) { s16 slot = GetEquippedMaskSlot(); - if (slot == -1) { + if (slot == -1) return; - } - s16 slotX = slot % MASK_GRID_COLS; - s16 slotY = slot / MASK_GRID_COLS; - s16 initialX = 0 - (MASK_GRID_COLS * MASK_GRID_CELL_WIDTH) / 2; - s16 initialY = (MASK_GRID_ROWS * MASK_GRID_CELL_HEIGHT) / 2 - 6; - s16 vtxX = initialX + (slotX * MASK_GRID_CELL_WIDTH); - s16 vtxY = initialY - (slotY * MASK_GRID_CELL_HEIGHT) + pauseCtx->offsetY; - - easyMaskEquipVtx[0].v.ob[0] = easyMaskEquipVtx[2].v.ob[0] = vtxX; - easyMaskEquipVtx[1].v.ob[0] = easyMaskEquipVtx[3].v.ob[0] = vtxX + MASK_GRID_CELL_WIDTH; - easyMaskEquipVtx[0].v.ob[1] = easyMaskEquipVtx[1].v.ob[1] = vtxY; - easyMaskEquipVtx[2].v.ob[1] = easyMaskEquipVtx[3].v.ob[1] = vtxY - MASK_GRID_CELL_HEIGHT; + const s16 slotX = slot % MASK_GRID_COLS; + const s16 slotY = slot / MASK_GRID_COLS; + const s16 initialX = -(MASK_GRID_COLS * MASK_GRID_CELL_WIDTH) / 2; + const s16 initialY = (MASK_GRID_ROWS * MASK_GRID_CELL_HEIGHT) / 2 - 6; + const s16 vtxX = initialX + (slotX * MASK_GRID_CELL_WIDTH); + const s16 vtxY = initialY - (slotY * MASK_GRID_CELL_HEIGHT) + pauseCtx->offsetY; + + const std::array xCoords = { vtxX, vtxX + MASK_GRID_CELL_WIDTH, vtxX, vtxX + MASK_GRID_CELL_WIDTH }; + const std::array yCoords = { vtxY, vtxY, vtxY - MASK_GRID_CELL_HEIGHT, vtxY - MASK_GRID_CELL_HEIGHT }; + + for (int i = 0; i < 4; ++i) { + easyMaskEquipVtx[i].v.ob[0] = xCoords[i]; + easyMaskEquipVtx[i].v.ob[1] = yCoords[i]; + } } -void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { - s16 slot = GetEquippedMaskSlot(); - if (slot == -1) { - return; +// Determine if a mask should be equipped based on various conditions +bool ShouldEquipMask(s16 cursorItem) { + const Player* player = GET_PLAYER(gPlayState); + + // Check if player is airborne and attempting to equip a transformation mask + bool isAirborne = !(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND); + bool notInWater = !(Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR && + Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE); + if (isAirborne && notInWater) { + if (player->transformation == PLAYER_FORM_HUMAN && IsTransformationMask(static_cast(cursorItem))) { + return true; + } + if (player->transformation != PLAYER_FORM_HUMAN) { + return true; + } } - // Check if the player owns the mask - if (gSaveContext.save.saveInfo.inventory.items[slot + ITEM_NUM_SLOTS] == ITEM_NONE) { - return; + // Check if underwater and trying to equip non-Zora masks + auto hazard = Player_GetEnvironmentalHazard(gPlayState); + if (hazard >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR && hazard <= PLAYER_ENV_HAZARD_UNDERWATER_FREE && + cursorItem != ITEM_MASK_ZORA) { + return true; + } + + // Check if the mask is given on the moon. Need to test this. + if (cursorItem >= ITEM_NUM_SLOTS && (cursorItem - ITEM_NUM_SLOTS) < MASK_NUM_SLOTS && + CHECK_GIVEN_MASK_ON_MOON(cursorItem - ITEM_NUM_SLOTS)) { + return true; } - // If the equipped item is equal to the pending item, remove the border - if (Player_GetCurMaskItemId(gPlayState) == sPendingMask) { + // Check if player is busy and trying to equip a transformation mask + if (IsTransformationMask(static_cast(cursorItem))) { + const auto& flags1 = player->stateFlags1; + const auto& flags2 = player->stateFlags2; + const auto heldAction = player->heldItemAction; + + bool isBusy = flags1 & (PLAYER_STATE1_800 | PLAYER_STATE1_400 | PLAYER_STATE1_20000000) || + flags2 & (PLAYER_STATE2_10 | PLAYER_STATE2_80) || heldAction == PLAYER_IA_BOW || + heldAction == PLAYER_IA_BOW_FIRE || heldAction == PLAYER_IA_BOW_ICE || + heldAction == PLAYER_IA_BOW_LIGHT || heldAction == PLAYER_IA_BOMB || + heldAction == PLAYER_IA_BOMBCHU || heldAction == PLAYER_IA_HOOKSHOT || + heldAction == PLAYER_IA_POWDER_KEG; + + if (isBusy) + return true; + } + + return false; +} + +// Draw the border around the equipped mask +void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { + s16 slot = GetEquippedMaskSlot(); + if (slot == -1 || gSaveContext.save.saveInfo.inventory.items[slot + ITEM_NUM_SLOTS] == ITEM_NONE || + Player_GetCurMaskItemId(gPlayState) == sPendingMask) { return; } GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; - OPEN_DISPS(gfxCtx); UpdateEasyMaskEquipVtx(pauseCtx); @@ -92,7 +149,7 @@ void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { gDPSetCombineLERP(POLY_OPA_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); - gSPVertex(POLY_OPA_DISP++, (uintptr_t)easyMaskEquipVtx, 4, 0); + gSPVertex(POLY_OPA_DISP++, reinterpret_cast(easyMaskEquipVtx), 4, 0); // Fixed cast gDPLoadTextureBlock(POLY_OPA_DISP++, gEquippedItemOutlineTex, G_IM_FMT_IA, G_IM_SIZ_8b, 32, 32, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); @@ -101,98 +158,106 @@ void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { CLOSE_DISPS(gfxCtx); } +// Allocate memory for the vertex buffer void AllocateEasyMaskEquipVtx(GraphicsContext* gfxCtx) { - easyMaskEquipVtx = (Vtx*)GRAPH_ALLOC(gfxCtx, 4 * sizeof(Vtx)); - for (int i = 0; i < 4; i++) { + easyMaskEquipVtx = static_cast(GRAPH_ALLOC(gfxCtx, 4 * sizeof(Vtx))); + for (int i = 0; i < 4; ++i) { easyMaskEquipVtx[i].v.ob[2] = 0; easyMaskEquipVtx[i].v.flag = 0; easyMaskEquipVtx[i].v.tc[0] = (i & 1) ? 32 << 5 : 0; easyMaskEquipVtx[i].v.tc[1] = (i & 2) ? 32 << 5 : 0; - easyMaskEquipVtx[i].v.cn[0] = easyMaskEquipVtx[i].v.cn[1] = easyMaskEquipVtx[i].v.cn[2] = - easyMaskEquipVtx[i].v.cn[3] = 255; + std::fill(std::begin(easyMaskEquipVtx[i].v.cn), std::end(easyMaskEquipVtx[i].v.cn), static_cast(255)); } } +// Apply grayscale to mask icons that shouldn't be equipped +void ApplyMaskIconGrayscale(PauseContext* pauseCtx, GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx); + + for (int i = 0; i < MASK_NUM_SLOTS; ++i) { + const s16 cursorItem = gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS]; + if (cursorItem != ITEM_NONE && ShouldEquipMask(cursorItem)) { + gDPSetGrayscaleColor(POLY_OPA_DISP++, 109, 109, 109, 255); + gSPGrayscale(POLY_OPA_DISP++, true); + gSPVertex(POLY_OPA_DISP++, reinterpret_cast(&pauseCtx->maskVtx[i * 4]), 4, 0); // Fixed cast + KaleidoScope_DrawTexQuadRGBA32(gfxCtx, gItemIcons[cursorItem], 32, 32, 0); + gSPGrayscale(POLY_OPA_DISP++, false); + } + } + + CLOSE_DISPS(gfxCtx); +} + +// Placeholder for additional grayscale functionality on the item page +void ApplyItemPageMaskGrayscale(PauseContext* pauseCtx, GraphicsContext* gfxCtx) { + // TODO: Implement, but I'm at a loss for how to do this. +} + +// Handle equipping masks based on player input void HandleEasyMaskEquip(PauseContext* pauseCtx) { - if (pauseCtx->state == PAUSE_STATE_MAIN && pauseCtx->mainState == PAUSE_MAIN_STATE_IDLE) { - if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_A)) { - s16 cursorItem = pauseCtx->cursorItem[PAUSE_MASK]; - if (cursorItem != PAUSE_ITEM_NONE) { - // If sPendingMask is already the cursor item, remove the border and reset sPendingMask - if (sPendingMask == cursorItem) { - sPendingMask = ITEM_NONE; - sIsTransforming = false; - return; - } - - Player* player = GET_PLAYER(gPlayState); - - // Check if the player is in the air and trying to equip a transformation mask - if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND)) { - if (player->transformation == PLAYER_FORM_HUMAN) { - if (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || - cursorItem == ITEM_MASK_ZORA || cursorItem == ITEM_MASK_FIERCE_DEITY || - cursorItem == ITEM_MASK_GIANT) { - Audio_PlaySfx(NA_SE_SY_ERROR); - return; - } - } else { - Audio_PlaySfx(NA_SE_SY_ERROR); - return; - } - } - - // Check if the player is underwater and trying to equip Deku or Goron mask - if ((Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && - (Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE) && - (cursorItem != ITEM_MASK_ZORA)) { - Audio_PlaySfx(NA_SE_SY_ERROR); - return; - } - - // Check if the mask is given on the moon - if (CHECK_GIVEN_MASK_ON_MOON(cursorItem - ITEM_NUM_SLOTS)) { - Audio_PlaySfx(NA_SE_SY_ERROR); - return; - } - - // Check if the player is busy with an action and trying to equip a transformation mask - if ((cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || - cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT) && - ((player->stateFlags1 & (PLAYER_STATE1_800 | PLAYER_STATE1_400 | PLAYER_STATE1_20000000)) || - (player->stateFlags2 & (PLAYER_STATE2_10 | PLAYER_STATE2_80)) || - (player->heldItemAction != PLAYER_IA_NONE) || - (player->meleeWeaponState != PLAYER_MELEE_WEAPON_STATE_0))) { - Audio_PlaySfx(NA_SE_SY_ERROR); - return; - } - - sPendingMask = cursorItem; - sIsTransforming = - (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || - cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT); - UpdateEasyMaskEquipVtx(pauseCtx); - Audio_PlaySfx(NA_SE_SY_DECIDE); + if (pauseCtx->state != PAUSE_STATE_MAIN || pauseCtx->mainState != PAUSE_MAIN_STATE_IDLE) + return; + + // Directly access the pressed buttons without using a Controller* variable + u16 pressedButtons = CONTROLLER1(&gPlayState->state)->press.button; + + if (CHECK_BTN_ALL(pressedButtons, BTN_A)) { + s16 cursorItem = pauseCtx->cursorItem[PAUSE_MASK]; + if (cursorItem != PAUSE_ITEM_NONE) { + // If sPendingMask is already the cursor item, remove the border and reset sPendingMask + if (sPendingMask == cursorItem) { + sPendingMask = ITEM_NONE; + sIsTransforming = false; + return; } - } else if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_B)) { - sPendingMask = ITEM_NONE; - sIsTransforming = false; - sLastEquippedMask = ITEM_NONE; + + if (ShouldEquipMask(cursorItem)) { + Audio_PlaySfx(NA_SE_SY_ERROR); + return; + } + + sPendingMask = cursorItem; + sIsTransforming = IsTransformationMask(static_cast(cursorItem)); + UpdateEasyMaskEquipVtx(pauseCtx); + Audio_PlaySfx(NA_SE_SY_DECIDE); } + } else if (CHECK_BTN_ALL(pressedButtons, BTN_B)) { + sPendingMask = ITEM_NONE; + sIsTransforming = false; + sLastEquippedMask = ITEM_NONE; } } +// Register hooks for the EasyMaskEquip functionality void RegisterEasyMaskEquip() { auto gameInteractor = GameInteractor::Instance; + // Handle mask equipping logic during KaleidoScope update gameInteractor->RegisterGameHook([](PauseContext* pauseCtx) { if (EasyMaskEquip_IsEnabled() && pauseCtx->pageIndex == PAUSE_MASK) { HandleEasyMaskEquip(pauseCtx); } }); + // Apply grayscale to mask icons after drawing the mask page + gameInteractor->RegisterGameHookForID( + PAUSE_MASK, [](PauseContext* pauseCtx, u16) { + if (EasyMaskEquip_IsEnabled()) { + ApplyMaskIconGrayscale(pauseCtx, gPlayState->state.gfxCtx); + } + }); + + // Apply grayscale to item page masks after drawing the item page + gameInteractor->RegisterGameHookForID( + PAUSE_ITEM, [](PauseContext* pauseCtx, u16) { + if (EasyMaskEquip_IsEnabled()) { + ApplyItemPageMaskGrayscale(pauseCtx, gPlayState->state.gfxCtx); + } + }); + + // Draw the equip border before drawing the mask page gameInteractor->RegisterGameHookForID( - PAUSE_MASK, [](PauseContext* pauseCtx, u16 pageIndex) { + PAUSE_MASK, [](PauseContext* pauseCtx, u16) { if (EasyMaskEquip_IsEnabled() && pauseCtx->pageIndex == PAUSE_MASK) { GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; OPEN_DISPS(gfxCtx); @@ -203,45 +268,55 @@ void RegisterEasyMaskEquip() { } }); + // Handle mask equipping when KaleidoScope closes gameInteractor->RegisterGameHook([]() { - if (EasyMaskEquip_IsEnabled() && gPlayState->pauseCtx.pageIndex == PAUSE_MASK) { - Player* player = GET_PLAYER(gPlayState); - if (sPendingMask != ITEM_NONE) { - if (sPendingMask != ITEM_MASK_DEKU && sPendingMask != ITEM_MASK_GORON && - sPendingMask != ITEM_MASK_ZORA && sPendingMask != ITEM_MASK_FIERCE_DEITY && - sPendingMask != ITEM_MASK_GIANT) { - if (player->transformation != PLAYER_FORM_HUMAN) { - Player_UseItem(gPlayState, player, static_cast(sPendingMask)); - sLastEquippedMask = sPendingMask; - sIsTransforming = true; - } else { - Player_UseItem(gPlayState, player, static_cast(sPendingMask)); - sLastEquippedMask = sPendingMask; - sPendingMask = ITEM_NONE; - } - } else { - Player_UseItem(gPlayState, player, static_cast(sPendingMask)); - sLastEquippedMask = sPendingMask; - sPendingMask = ITEM_NONE; - sIsTransforming = true; - } + if (!EasyMaskEquip_IsEnabled() || gPlayState->pauseCtx.pageIndex != PAUSE_MASK) + return; + + Player* player = GET_PLAYER(gPlayState); + if (sPendingMask == ITEM_NONE) + return; + + bool isTransformation = IsTransformationMask(static_cast(sPendingMask)); + bool isCurrentlyTransformed = (player->transformation != PLAYER_FORM_HUMAN); + + if (!isTransformation) { + if (isCurrentlyTransformed) { + // Equip a regular mask while transformed: transform back to human first + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sLastEquippedMask = sPendingMask; + // Retain sPendingMask to equip after transformation + sIsTransforming = true; + } else { + // Equip a regular mask while already human + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sLastEquippedMask = sPendingMask; + sPendingMask = ITEM_NONE; + sIsTransforming = false; } + } else { + // Equip a transformation mask + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sLastEquippedMask = sPendingMask; + sPendingMask = ITEM_NONE; + sIsTransforming = true; } }); + // Update mask equipping state during actor updates gameInteractor->RegisterGameHook([](Actor* actor) { - if (EasyMaskEquip_IsEnabled() && actor->id == ACTOR_PLAYER) { - Player* player = (Player*)actor; - if (sIsTransforming && player->transformation == PLAYER_FORM_HUMAN) { - if (player->skelAnime.curFrame == player->skelAnime.endFrame) { - if (sPendingMask != ITEM_NONE) { - Player_UseItem(gPlayState, player, static_cast(sPendingMask)); - sLastEquippedMask = sPendingMask; - sPendingMask = ITEM_NONE; - sIsTransforming = false; - } - } + if (!EasyMaskEquip_IsEnabled() || actor->id != ACTOR_PLAYER) + return; + + Player* player = reinterpret_cast(actor); + if (sIsTransforming && player->transformation == PLAYER_FORM_HUMAN && + player->skelAnime.curFrame >= player->skelAnime.endFrame) { + if (sPendingMask != ITEM_NONE) { + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sLastEquippedMask = sPendingMask; + sPendingMask = ITEM_NONE; + sIsTransforming = false; } } }); -} \ No newline at end of file +} diff --git a/mm/include/functions.h b/mm/include/functions.h index c61f74c85f..b62c60fe6f 100644 --- a/mm/include/functions.h +++ b/mm/include/functions.h @@ -1329,6 +1329,7 @@ void osContGetReadData(OSContPad* pad); void PadMgr_ThreadEntry(); void Heaps_Alloc(void); void Player_UseItem(PlayState* play, Player* this, ItemId item); +void KaleidoScope_GrayOutTextureRGBA32(u32* texture, u16 pixelCount); void KaleidoScope_UpdateOwlWarpNamePanel(PlayState* play); void KaleidoScope_UpdateNamePanel(PlayState* play); // #endregion From f185eb0521be11e6b2b621d7a294ee5920327f68 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Fri, 13 Sep 2024 10:08:00 -0400 Subject: [PATCH 10/52] add missing before/after for the other draw, thanks Proxy! --- .../kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c index c06ec9db6a..819bf37a5b 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c @@ -741,7 +741,9 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->itemPageVtx, sItemPageBgTextures); + GameInteractor_ExecuteBeforeKaleidoDrawPage(pauseCtx, PAUSE_ITEM); KaleidoScope_DrawItemSelect(play); + GameInteractor_ExecuteAfterKaleidoDrawPage(pauseCtx, PAUSE_ITEM); } if ((pauseCtx->pageIndex != PAUSE_MAP) && (pauseCtx->pageIndex != PAUSE_MASK)) { @@ -761,6 +763,7 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->mapPageVtx, sMapPageBgTextures); + GameInteractor_ExecuteBeforeKaleidoDrawPage(pauseCtx, PAUSE_MAP); if (sInDungeonScene) { KaleidoScope_DrawDungeonMap(play); Gfx_SetupDL42_Opa(gfxCtx); @@ -769,6 +772,7 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { } else { KaleidoScope_DrawWorldMap(play); } + GameInteractor_ExecuteAfterKaleidoDrawPage(pauseCtx, PAUSE_MAP); } if ((pauseCtx->pageIndex != PAUSE_QUEST) && (pauseCtx->pageIndex != PAUSE_ITEM)) { @@ -790,7 +794,9 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->questPageVtx, sQuestPageBgTextures); + GameInteractor_ExecuteBeforeKaleidoDrawPage(pauseCtx, PAUSE_QUEST); KaleidoScope_DrawQuestStatus(play); + GameInteractor_ExecuteAfterKaleidoDrawPage(pauseCtx, PAUSE_QUEST); } if ((pauseCtx->pageIndex != PAUSE_MASK) && (pauseCtx->pageIndex != PAUSE_MAP)) { @@ -812,7 +818,9 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->maskPageVtx, sMaskPageBgTextures); + GameInteractor_ExecuteBeforeKaleidoDrawPage(pauseCtx, PAUSE_MASK); KaleidoScope_DrawMaskSelect(play); + GameInteractor_ExecuteAfterKaleidoDrawPage(pauseCtx, PAUSE_MASK); } switch (pauseCtx->pageIndex) { From 69cb797dcd8e84791de76cfa75130371d1e49f17 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Fri, 13 Sep 2024 10:58:14 -0400 Subject: [PATCH 11/52] remove redefinition and fix grayscale issue, again thanks Proxy! --- .../Enhancements/GameInteractor/GameInteractor.h | 1 - mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 13 ------------- 2 files changed, 14 deletions(-) diff --git a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h index 96261e13cd..b589eb2db3 100644 --- a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h +++ b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h @@ -285,7 +285,6 @@ class GameInteractor { DEFINE_HOOK(OnActorDraw, (Actor * actor)); DEFINE_HOOK(OnActorKill, (Actor * actor)); DEFINE_HOOK(OnPlayerPostLimbDraw, (Player * player, s32 limbIndex)); - DEFINE_HOOK(OnPlayerPostLimbDraw, (Player * player, s32 limbIndex)); DEFINE_HOOK(OnSceneFlagSet, (s16 sceneId, FlagType flagType, u32 flag)); DEFINE_HOOK(OnSceneFlagUnset, (s16 sceneId, FlagType flagType, u32 flag)); diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index e1608c4400..b36de1d1a3 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -188,11 +188,6 @@ void ApplyMaskIconGrayscale(PauseContext* pauseCtx, GraphicsContext* gfxCtx) { CLOSE_DISPS(gfxCtx); } -// Placeholder for additional grayscale functionality on the item page -void ApplyItemPageMaskGrayscale(PauseContext* pauseCtx, GraphicsContext* gfxCtx) { - // TODO: Implement, but I'm at a loss for how to do this. -} - // Handle equipping masks based on player input void HandleEasyMaskEquip(PauseContext* pauseCtx) { if (pauseCtx->state != PAUSE_STATE_MAIN || pauseCtx->mainState != PAUSE_MAIN_STATE_IDLE) @@ -247,14 +242,6 @@ void RegisterEasyMaskEquip() { } }); - // Apply grayscale to item page masks after drawing the item page - gameInteractor->RegisterGameHookForID( - PAUSE_ITEM, [](PauseContext* pauseCtx, u16) { - if (EasyMaskEquip_IsEnabled()) { - ApplyItemPageMaskGrayscale(pauseCtx, gPlayState->state.gfxCtx); - } - }); - // Draw the equip border before drawing the mask page gameInteractor->RegisterGameHookForID( PAUSE_MASK, [](PauseContext* pauseCtx, u16) { From 7ed6b94ccc707345461167cfebd9cfc50062467d Mon Sep 17 00:00:00 2001 From: mckinlee Date: Fri, 13 Sep 2024 11:46:24 -0400 Subject: [PATCH 12/52] added more checks for when the player is busy and cannot equip a mask --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index b36de1d1a3..a7fb016c57 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -89,6 +89,15 @@ bool ShouldEquipMask(s16 cursorItem) { bool isAirborne = !(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND); bool notInWater = !(Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR && Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE); + + // Add a check for Zora transformation and a small delay after transformation + if (player->transformation == PLAYER_FORM_ZORA) { + // Ensure the player is fully transformed and not in the middle of an animation + if (player->skelAnime.curFrame < player->skelAnime.endFrame) { + return false; // Still transforming, don't allow mask change + } + } + if (isAirborne && notInWater) { if (player->transformation == PLAYER_FORM_HUMAN && IsTransformationMask(static_cast(cursorItem))) { return true; @@ -124,8 +133,11 @@ bool ShouldEquipMask(s16 cursorItem) { heldAction == PLAYER_IA_BOMBCHU || heldAction == PLAYER_IA_HOOKSHOT || heldAction == PLAYER_IA_POWDER_KEG; - if (isBusy) + bool isSwingingWeapon = player->meleeWeaponState != PLAYER_MELEE_WEAPON_STATE_0; + + if (isBusy || isSwingingWeapon) { return true; + } } return false; From 16bdcad2a95a9a32c5561f90378a271630565163 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Fri, 13 Sep 2024 12:37:16 -0400 Subject: [PATCH 13/52] Remove extra function prototype --- mm/2s2h/Enhancements/GameInteractor/GameInteractor.h | 1 - 1 file changed, 1 deletion(-) diff --git a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h index 6b6ba14431..b71c2591f3 100644 --- a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h +++ b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h @@ -338,7 +338,6 @@ void GameInteractor_ExecuteOnActorDraw(Actor* actor); void GameInteractor_ExecuteOnActorKill(Actor* actor); void GameInteractor_ExecuteOnActorDestroy(Actor* actor); void GameInteractor_ExecuteOnPlayerPostLimbDraw(Player* player, s32 limbIndex); -void GameInteractor_ExecuteOnPlayerPostLimbDraw(Player* player, s32 limbIndex); void GameInteractor_ExecuteOnSceneFlagSet(s16 sceneId, FlagType flagType, u32 flag); void GameInteractor_ExecuteOnSceneFlagUnset(s16 sceneId, FlagType flagType, u32 flag); From aa20a863a27424bf2f13e09e35e9e16a82dddc3f Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sat, 14 Sep 2024 18:07:50 -0400 Subject: [PATCH 14/52] rework and document all conditions in ShouldEquipMask --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 115 +++++++++++-------- 1 file changed, 70 insertions(+), 45 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index a7fb016c57..cddd469e22 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -10,6 +10,7 @@ extern "C" { #include "variables.h" extern u16 sMasksGivenOnMoonBits[]; extern s16 sPauseMenuVerticalOffset; +void Player_Action_93(Player* player, PlayState* play); } static Vtx* easyMaskEquipVtx = nullptr; @@ -83,64 +84,88 @@ void UpdateEasyMaskEquipVtx(PauseContext* pauseCtx) { // Determine if a mask should be equipped based on various conditions bool ShouldEquipMask(s16 cursorItem) { - const Player* player = GET_PLAYER(gPlayState); - - // Check if player is airborne and attempting to equip a transformation mask - bool isAirborne = !(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND); - bool notInWater = !(Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR && - Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE); - - // Add a check for Zora transformation and a small delay after transformation - if (player->transformation == PLAYER_FORM_ZORA) { - // Ensure the player is fully transformed and not in the middle of an animation - if (player->skelAnime.curFrame < player->skelAnime.endFrame) { - return false; // Still transforming, don't allow mask change + Player* player = GET_PLAYER(gPlayState); + + // Check if the player is underwater + if ((Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && + (Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE)) { + + // Only allow equipping Zora Mask underwater + if (cursorItem != ITEM_MASK_ZORA) { + return false; // Prevent equipping any mask other than Zora Mask } } - if (isAirborne && notInWater) { - if (player->transformation == PLAYER_FORM_HUMAN && IsTransformationMask(static_cast(cursorItem))) { - return true; - } - if (player->transformation != PLAYER_FORM_HUMAN) { - return true; + // Check if the player is climbing (some of these might be redundant, but it works) + if ((player->stateFlags1 & (PLAYER_STATE1_4 | PLAYER_STATE1_4000 | PLAYER_STATE1_40000 | PLAYER_STATE1_200000)) && + IsTransformationMask(static_cast(cursorItem))) { + // Player is climbing and trying to equip a transformation mask, do not allow + return false; + } + + // Prevent equipping transformation masks if interacting with an object + if (IsTransformationMask(static_cast(cursorItem))) { + if ((player->stateFlags2 & PLAYER_STATE2_10) || // Pushing/Pulling + (player->stateFlags1 & PLAYER_STATE1_800) || // Carrying + (player->stateFlags2 & PLAYER_STATE2_1)) { // Grabbing + return false; } } - // Check if underwater and trying to equip non-Zora masks - auto hazard = Player_GetEnvironmentalHazard(gPlayState); - if (hazard >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR && hazard <= PLAYER_ENV_HAZARD_UNDERWATER_FREE && - cursorItem != ITEM_MASK_ZORA) { - return true; + // Check if the player is interacting with Epona + if (player->rideActor != NULL && player->rideActor->id == ACTOR_EN_HORSE && + IsTransformationMask(static_cast(cursorItem))) { + return false; // Prevent equipping transformation masks while interacting with Epona } - // Check if the mask is given on the moon. Need to test this. - if (cursorItem >= ITEM_NUM_SLOTS && (cursorItem - ITEM_NUM_SLOTS) < MASK_NUM_SLOTS && - CHECK_GIVEN_MASK_ON_MOON(cursorItem - ITEM_NUM_SLOTS)) { - return true; + // Check if a minigame timer is active (prevent equipping masks during minigames) + if (gSaveContext.timerStates[TIMER_ID_MINIGAME_1] != TIMER_STATE_OFF || + gSaveContext.timerStates[TIMER_ID_MINIGAME_2] != TIMER_STATE_OFF) { + // If a minigame timer is active, do not equip any mask + return false; } - // Check if player is busy and trying to equip a transformation mask - if (IsTransformationMask(static_cast(cursorItem))) { - const auto& flags1 = player->stateFlags1; - const auto& flags2 = player->stateFlags2; - const auto heldAction = player->heldItemAction; + // Prevent equipping transformation masks when swinging a melee weapon + if (player->meleeWeaponState != PLAYER_MELEE_WEAPON_STATE_0 && IsTransformationMask(static_cast(cursorItem))) { + return false; + } - bool isBusy = flags1 & (PLAYER_STATE1_800 | PLAYER_STATE1_400 | PLAYER_STATE1_20000000) || - flags2 & (PLAYER_STATE2_10 | PLAYER_STATE2_80) || heldAction == PLAYER_IA_BOW || - heldAction == PLAYER_IA_BOW_FIRE || heldAction == PLAYER_IA_BOW_ICE || - heldAction == PLAYER_IA_BOW_LIGHT || heldAction == PLAYER_IA_BOMB || - heldAction == PLAYER_IA_BOMBCHU || heldAction == PLAYER_IA_HOOKSHOT || - heldAction == PLAYER_IA_POWDER_KEG; + // Check if the player is holding a bow (including ice, fire, light variants) or hookshot + if ((player->heldItemAction == PLAYER_IA_BOW || + player->heldItemAction == PLAYER_IA_BOW_FIRE || + player->heldItemAction == PLAYER_IA_BOW_ICE || + player->heldItemAction == PLAYER_IA_BOW_LIGHT || + Player_IsHoldingHookshot(player)) && + IsTransformationMask(static_cast(cursorItem))) { + // Prevent equipping transformation masks while holding bow or hookshot + return false; + } + + // Check if the mask is Fierce Deity Mask and if the player is in an allowed location + if (cursorItem == ITEM_MASK_FIERCE_DEITY) { + if (gPlayState->sceneId != SCENE_MITURIN_BS && + gPlayState->sceneId != SCENE_HAKUGIN_BS && + gPlayState->sceneId != SCENE_SEA_BS && + gPlayState->sceneId != SCENE_INISIE_BS && + gPlayState->sceneId != SCENE_LAST_BS) { + return false; // Prevent equipping Fierce Deity Mask outside allowed locations + } + } - bool isSwingingWeapon = player->meleeWeaponState != PLAYER_MELEE_WEAPON_STATE_0; + // Check if the player is in the air (not on the ground) and if the mask is a transformation mask + if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND) && + (IsTransformationMask(static_cast(cursorItem)) || player->transformation != PLAYER_FORM_HUMAN)) { + return false; // Player is in the air and in a transformation form, cannot equip any mask + } - if (isBusy || isSwingingWeapon) { - return true; - } + // Check if the player is interacting with a Deku Flower (some might be redundant because of the above check, but it works) + if (player->stateFlags3 & (PLAYER_STATE3_200 | PLAYER_STATE3_2000 | PLAYER_STATE3_100)) { + return false; // Prevent equipping any mask while interacting with a Deku Flower } - return false; + //We can add tons more checks here, but I need help to account for all scenarios + + return true; // Allow equipping other masks } // Draw the border around the equipped mask @@ -188,7 +213,7 @@ void ApplyMaskIconGrayscale(PauseContext* pauseCtx, GraphicsContext* gfxCtx) { for (int i = 0; i < MASK_NUM_SLOTS; ++i) { const s16 cursorItem = gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS]; - if (cursorItem != ITEM_NONE && ShouldEquipMask(cursorItem)) { + if (cursorItem != ITEM_NONE && !ShouldEquipMask(cursorItem)) { gDPSetGrayscaleColor(POLY_OPA_DISP++, 109, 109, 109, 255); gSPGrayscale(POLY_OPA_DISP++, true); gSPVertex(POLY_OPA_DISP++, reinterpret_cast(&pauseCtx->maskVtx[i * 4]), 4, 0); // Fixed cast @@ -218,7 +243,7 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { return; } - if (ShouldEquipMask(cursorItem)) { + if (!ShouldEquipMask(cursorItem)) { Audio_PlaySfx(NA_SE_SY_ERROR); return; } From 252d499eac3520e17cef8159787e152775b82567 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sat, 14 Sep 2024 18:14:55 -0400 Subject: [PATCH 15/52] run clang script --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 40 ++++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index cddd469e22..d81097d7b6 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -89,10 +89,10 @@ bool ShouldEquipMask(s16 cursorItem) { // Check if the player is underwater if ((Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && (Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE)) { - + // Only allow equipping Zora Mask underwater if (cursorItem != ITEM_MASK_ZORA) { - return false; // Prevent equipping any mask other than Zora Mask + return false; // Prevent equipping any mask other than Zora Mask } } @@ -115,7 +115,7 @@ bool ShouldEquipMask(s16 cursorItem) { // Check if the player is interacting with Epona if (player->rideActor != NULL && player->rideActor->id == ACTOR_EN_HORSE && IsTransformationMask(static_cast(cursorItem))) { - return false; // Prevent equipping transformation masks while interacting with Epona + return false; // Prevent equipping transformation masks while interacting with Epona } // Check if a minigame timer is active (prevent equipping masks during minigames) @@ -125,47 +125,45 @@ bool ShouldEquipMask(s16 cursorItem) { return false; } - // Prevent equipping transformation masks when swinging a melee weapon - if (player->meleeWeaponState != PLAYER_MELEE_WEAPON_STATE_0 && IsTransformationMask(static_cast(cursorItem))) { + // Prevent equipping transformation masks when swinging a melee weapon + if (player->meleeWeaponState != PLAYER_MELEE_WEAPON_STATE_0 && + IsTransformationMask(static_cast(cursorItem))) { return false; } - // Check if the player is holding a bow (including ice, fire, light variants) or hookshot - if ((player->heldItemAction == PLAYER_IA_BOW || - player->heldItemAction == PLAYER_IA_BOW_FIRE || - player->heldItemAction == PLAYER_IA_BOW_ICE || - player->heldItemAction == PLAYER_IA_BOW_LIGHT || + // Check if the player is holding a bow (including ice, fire, light variants) or hookshot + if ((player->heldItemAction == PLAYER_IA_BOW || player->heldItemAction == PLAYER_IA_BOW_FIRE || + player->heldItemAction == PLAYER_IA_BOW_ICE || player->heldItemAction == PLAYER_IA_BOW_LIGHT || Player_IsHoldingHookshot(player)) && IsTransformationMask(static_cast(cursorItem))) { // Prevent equipping transformation masks while holding bow or hookshot return false; - } + } // Check if the mask is Fierce Deity Mask and if the player is in an allowed location if (cursorItem == ITEM_MASK_FIERCE_DEITY) { - if (gPlayState->sceneId != SCENE_MITURIN_BS && - gPlayState->sceneId != SCENE_HAKUGIN_BS && - gPlayState->sceneId != SCENE_SEA_BS && - gPlayState->sceneId != SCENE_INISIE_BS && + if (gPlayState->sceneId != SCENE_MITURIN_BS && gPlayState->sceneId != SCENE_HAKUGIN_BS && + gPlayState->sceneId != SCENE_SEA_BS && gPlayState->sceneId != SCENE_INISIE_BS && gPlayState->sceneId != SCENE_LAST_BS) { - return false; // Prevent equipping Fierce Deity Mask outside allowed locations + return false; // Prevent equipping Fierce Deity Mask outside allowed locations } } // Check if the player is in the air (not on the ground) and if the mask is a transformation mask - if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND) && + if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND) && (IsTransformationMask(static_cast(cursorItem)) || player->transformation != PLAYER_FORM_HUMAN)) { return false; // Player is in the air and in a transformation form, cannot equip any mask } - // Check if the player is interacting with a Deku Flower (some might be redundant because of the above check, but it works) + // Check if the player is interacting with a Deku Flower (some might be redundant because of the above check, but it + // works) if (player->stateFlags3 & (PLAYER_STATE3_200 | PLAYER_STATE3_2000 | PLAYER_STATE3_100)) { - return false; // Prevent equipping any mask while interacting with a Deku Flower + return false; // Prevent equipping any mask while interacting with a Deku Flower } - //We can add tons more checks here, but I need help to account for all scenarios + // We can add tons more checks here, but I need help to account for all scenarios - return true; // Allow equipping other masks + return true; // Allow equipping other masks } // Draw the border around the equipped mask From 8c48655699e07f4aa1e63061d760115b8fe317ba Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sat, 14 Sep 2024 20:03:30 -0400 Subject: [PATCH 16/52] added support for unrestricted items/fierce deity anywhere and a check for giant's mask --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index d81097d7b6..ebff76789c 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -10,7 +10,6 @@ extern "C" { #include "variables.h" extern u16 sMasksGivenOnMoonBits[]; extern s16 sPauseMenuVerticalOffset; -void Player_Action_93(Player* player, PlayState* play); } static Vtx* easyMaskEquipVtx = nullptr; @@ -86,6 +85,11 @@ void UpdateEasyMaskEquipVtx(PauseContext* pauseCtx) { bool ShouldEquipMask(s16 cursorItem) { Player* player = GET_PLAYER(gPlayState); + // Allow all masks to be equipped if the Unrestricted Items cheat is enabled + if (CVarGetInteger("gCheats.UnrestrictedItems", 0)) { + return true; + } + // Check if the player is underwater if ((Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && (Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE)) { @@ -142,7 +146,8 @@ bool ShouldEquipMask(s16 cursorItem) { // Check if the mask is Fierce Deity Mask and if the player is in an allowed location if (cursorItem == ITEM_MASK_FIERCE_DEITY) { - if (gPlayState->sceneId != SCENE_MITURIN_BS && gPlayState->sceneId != SCENE_HAKUGIN_BS && + if (!CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0) && + gPlayState->sceneId != SCENE_MITURIN_BS && gPlayState->sceneId != SCENE_HAKUGIN_BS && gPlayState->sceneId != SCENE_SEA_BS && gPlayState->sceneId != SCENE_INISIE_BS && gPlayState->sceneId != SCENE_LAST_BS) { return false; // Prevent equipping Fierce Deity Mask outside allowed locations @@ -161,6 +166,14 @@ bool ShouldEquipMask(s16 cursorItem) { return false; // Prevent equipping any mask while interacting with a Deku Flower } + // Check if the mask is Giant's Mask and if the player is in the allowed location + if (cursorItem == ITEM_MASK_GIANT) { + if (gPlayState->sceneId != SCENE_INISIE_BS) { + return false; // Prevent equipping Giant's Mask outside SCENE_INISIE_BS + } + } + + // We can add tons more checks here, but I need help to account for all scenarios return true; // Allow equipping other masks From ba493996f9048865aee01e411dcd5d43ad780b82 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sat, 14 Sep 2024 20:06:34 -0400 Subject: [PATCH 17/52] clang script --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index ebff76789c..a4903c78cc 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -146,10 +146,9 @@ bool ShouldEquipMask(s16 cursorItem) { // Check if the mask is Fierce Deity Mask and if the player is in an allowed location if (cursorItem == ITEM_MASK_FIERCE_DEITY) { - if (!CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0) && - gPlayState->sceneId != SCENE_MITURIN_BS && gPlayState->sceneId != SCENE_HAKUGIN_BS && - gPlayState->sceneId != SCENE_SEA_BS && gPlayState->sceneId != SCENE_INISIE_BS && - gPlayState->sceneId != SCENE_LAST_BS) { + if (!CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0) && gPlayState->sceneId != SCENE_MITURIN_BS && + gPlayState->sceneId != SCENE_HAKUGIN_BS && gPlayState->sceneId != SCENE_SEA_BS && + gPlayState->sceneId != SCENE_INISIE_BS && gPlayState->sceneId != SCENE_LAST_BS) { return false; // Prevent equipping Fierce Deity Mask outside allowed locations } } @@ -173,7 +172,6 @@ bool ShouldEquipMask(s16 cursorItem) { } } - // We can add tons more checks here, but I need help to account for all scenarios return true; // Allow equipping other masks From 62596cbeb8655caee853c0d05afd40995f9d190e Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 15 Sep 2024 15:27:14 -0400 Subject: [PATCH 18/52] replace held item with first person check, add giant mask checks --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 30 ++++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index a4903c78cc..a1de549829 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -135,15 +135,6 @@ bool ShouldEquipMask(s16 cursorItem) { return false; } - // Check if the player is holding a bow (including ice, fire, light variants) or hookshot - if ((player->heldItemAction == PLAYER_IA_BOW || player->heldItemAction == PLAYER_IA_BOW_FIRE || - player->heldItemAction == PLAYER_IA_BOW_ICE || player->heldItemAction == PLAYER_IA_BOW_LIGHT || - Player_IsHoldingHookshot(player)) && - IsTransformationMask(static_cast(cursorItem))) { - // Prevent equipping transformation masks while holding bow or hookshot - return false; - } - // Check if the mask is Fierce Deity Mask and if the player is in an allowed location if (cursorItem == ITEM_MASK_FIERCE_DEITY) { if (!CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0) && gPlayState->sceneId != SCENE_MITURIN_BS && @@ -165,13 +156,28 @@ bool ShouldEquipMask(s16 cursorItem) { return false; // Prevent equipping any mask while interacting with a Deku Flower } - // Check if the mask is Giant's Mask and if the player is in the allowed location + // Check if the mask is Giant's Mask and if the player is in the allowed location with enough magic if (cursorItem == ITEM_MASK_GIANT) { - if (gPlayState->sceneId != SCENE_INISIE_BS) { - return false; // Prevent equipping Giant's Mask outside SCENE_INISIE_BS + if (gPlayState->sceneId != SCENE_INISIE_BS || gSaveContext.save.saveInfo.playerData.magic == 0) { + return false; // Prevent equipping Giant's Mask outside SCENE_INISIE_BS or if magic is depleted } } + // Prevent equipping any mask if Giant's Mask is already equipped, except for Giant's Mask + if (player->currentMask == PLAYER_MASK_GIANT && cursorItem != ITEM_MASK_GIANT) { + return false; + } + + // Prevent equipping Giant's Mask while wearing a transformation mask + if ((player->transformation != PLAYER_FORM_HUMAN) && cursorItem == ITEM_MASK_GIANT) { + return false; // Prevent equipping Giant's Mask while wearing a transformation mask + } + + // Prevent equipping transformation masks in first-person mode + if ((player->stateFlags1 & PLAYER_STATE1_100000) && IsTransformationMask(static_cast(cursorItem))) { + return false; + } + // We can add tons more checks here, but I need help to account for all scenarios return true; // Allow equipping other masks From 95b5afe7343ba7b3e74b18ee3264ff9ffac9715d Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 15 Sep 2024 15:31:57 -0400 Subject: [PATCH 19/52] one day i won't forget to run the clang script --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index a1de549829..f4cf87f484 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -167,7 +167,7 @@ bool ShouldEquipMask(s16 cursorItem) { if (player->currentMask == PLAYER_MASK_GIANT && cursorItem != ITEM_MASK_GIANT) { return false; } - + // Prevent equipping Giant's Mask while wearing a transformation mask if ((player->transformation != PLAYER_FORM_HUMAN) && cursorItem == ITEM_MASK_GIANT) { return false; // Prevent equipping Giant's Mask while wearing a transformation mask From 755455fa4a86e6558f0aa73a8170beba1d528d40 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 15 Sep 2024 16:53:34 -0400 Subject: [PATCH 20/52] support persistent bunny hood, utilize GI_VB_KALEIDO_DISPLAY_ITEM_TEXT --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 12 ++++++++++++ .../kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c | 9 +-------- 2 files changed, 13 insertions(+), 8 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index f4cf87f484..fc64cc0677 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -251,6 +251,11 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { if (CHECK_BTN_ALL(pressedButtons, BTN_A)) { s16 cursorItem = pauseCtx->cursorItem[PAUSE_MASK]; if (cursorItem != PAUSE_ITEM_NONE) { + // Check if PersistentBunnyHood is enabled and the mask is Bunny Hood + if (CVarGetInteger("gEnhancements.Masks.PersistentBunnyHood.Enabled", 0) && cursorItem == ITEM_MASK_BUNNY) { + return; // Do nothing if trying to equip Bunny Hood and PersistentBunnyHood is enabled + } + // If sPendingMask is already the cursor item, remove the border and reset sPendingMask if (sPendingMask == cursorItem) { sPendingMask = ITEM_NONE; @@ -358,4 +363,11 @@ void RegisterEasyMaskEquip() { } } }); + + // Register the hook for GI_VB_KALEIDO_DISPLAY_ITEM_TEXT + REGISTER_VB_SHOULD(GI_VB_KALEIDO_DISPLAY_ITEM_TEXT, { + if (EasyMaskEquip_IsEnabled()) { + *should = false; + } + }); } diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c index c4e1f2ffd2..6f27ad774e 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c @@ -10,10 +10,6 @@ #include "BenGui/HudEditor.h" #include "2s2h/Enhancements/GameInteractor/GameInteractor.h" -#include "macros.h" - -#include "2s2h/Enhancements/Masks/EasyMaskEquip.h" - s16 sMaskEquipState = EQUIP_STATE_MAGIC_ARROW_GROW_ORB; // Timer to hold magic arrow icon over magic arrow slot before moving when equipping. @@ -674,12 +670,9 @@ void KaleidoScope_UpdateMaskCursor(PlayState* play) { sMaskEquipState = EQUIP_STATE_MOVE_TO_C_BTN; sMaskEquipAnimTimer = 10; Audio_PlaySfx(NA_SE_SY_DECIDE); - // EasyMaskEquip check disables the mask description textbox from appearing when a mask is selected - // with A button. } else if ((pauseCtx->debugEditor == DEBUG_EDITOR_NONE) && (pauseCtx->state == PAUSE_STATE_MAIN) && (pauseCtx->mainState == PAUSE_MAIN_STATE_IDLE) && - CHECK_BTN_ALL(input->press.button, BTN_A) && (msgCtx->msgLength == 0) && - !EasyMaskEquip_IsEnabled()) { + CHECK_BTN_ALL(input->press.button, BTN_A) && (msgCtx->msgLength == 0)) { if (GameInteractor_Should(GI_VB_KALEIDO_DISPLAY_ITEM_TEXT, true, &pauseCtx->cursorItem[PAUSE_MASK])) { // Give description on item through a message box From a00c2f38082de4c1f4bf0fd31d21efda2a50b1b1 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 15 Sep 2024 17:51:04 -0400 Subject: [PATCH 21/52] switched to gameinteractor hook for mask equip C/D button restriction --- mm/2s2h/Enhancements/GameInteractor/GameInteractor.h | 1 + mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 9 ++++++++- mm/src/overlays/actors/ovl_player_actor/z_player.c | 5 +---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h index b71c2591f3..5a28bf88fa 100644 --- a/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h +++ b/mm/2s2h/Enhancements/GameInteractor/GameInteractor.h @@ -60,6 +60,7 @@ typedef enum { GI_VB_CLOCK_TOWER_OPENING_CONSIDER_THIS_FIRST_CYCLE, GI_VB_DRAW_SLIME_BODY_ITEM, GI_VB_ZTARGET_SPEED_CHECK, + GI_VB_ALLOW_EQUIP_MASK, } GIVanillaBehavior; typedef enum { diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index fc64cc0677..eb8bf51cfe 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -364,10 +364,17 @@ void RegisterEasyMaskEquip() { } }); - // Register the hook for GI_VB_KALEIDO_DISPLAY_ITEM_TEXT + // Disable selected item textbox REGISTER_VB_SHOULD(GI_VB_KALEIDO_DISPLAY_ITEM_TEXT, { if (EasyMaskEquip_IsEnabled()) { *should = false; } }); + + // Remove restriction requiring masks be equipped from C or D buttons + REGISTER_VB_SHOULD(GI_VB_ALLOW_EQUIP_MASK, { + if (EasyMaskEquip_IsEnabled()) { + *should = false; + } + }); } diff --git a/mm/src/overlays/actors/ovl_player_actor/z_player.c b/mm/src/overlays/actors/ovl_player_actor/z_player.c index f8ac6e8aae..d952baf49c 100644 --- a/mm/src/overlays/actors/ovl_player_actor/z_player.c +++ b/mm/src/overlays/actors/ovl_player_actor/z_player.c @@ -47,8 +47,6 @@ #include "2s2h/Enhancements/GameInteractor/GameInteractor.h" -#include "2s2h/Enhancements/Masks/EasyMaskEquip.h" - #define THIS ((Player*)thisx) void Player_Init(Actor* thisx, PlayState* play); @@ -3733,8 +3731,7 @@ void Player_ProcessItemButtons(Player* this, PlayState* play) { maskIdMinusOne = this->currentMask - 1; } - // EasyMaskEquip check allows equipping a mask without it being on a C button or D button - if (!EasyMaskEquip_IsEnabled()) { + if (GameInteractor_Should(GI_VB_ALLOW_EQUIP_MASK, false, NULL)) { Player_UseItem(play, this, Player_MaskIdToItemId(maskIdMinusOne)); return; } From ab7a513f2b9387a5917b9f82a4887a796cf6ca6c Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 15 Sep 2024 20:46:38 -0400 Subject: [PATCH 22/52] fix underwater check, fix climbing check --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index eb8bf51cfe..60fa5605b9 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -94,14 +94,17 @@ bool ShouldEquipMask(s16 cursorItem) { if ((Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && (Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE)) { - // Only allow equipping Zora Mask underwater - if (cursorItem != ITEM_MASK_ZORA) { + // Allow equipping Zora Mask underwater + if (cursorItem == ITEM_MASK_ZORA) { + return true; // Explicitly allow equipping the Zora Mask + } else { return false; // Prevent equipping any mask other than Zora Mask } } // Check if the player is climbing (some of these might be redundant, but it works) - if ((player->stateFlags1 & (PLAYER_STATE1_4 | PLAYER_STATE1_4000 | PLAYER_STATE1_40000 | PLAYER_STATE1_200000)) && + if ((player->stateFlags1 & + (PLAYER_STATE1_4 | PLAYER_STATE1_4000 | PLAYER_STATE1_40000 | PLAYER_STATE1_200000 | PLAYER_STATE1_2000)) && IsTransformationMask(static_cast(cursorItem))) { // Player is climbing and trying to equip a transformation mask, do not allow return false; From de164bbbefb6fd9c1ae11e512ca256cffbd2fb62 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Thu, 26 Sep 2024 13:08:45 -0400 Subject: [PATCH 23/52] remove null arg --- mm/src/overlays/actors/ovl_player_actor/z_player.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/src/overlays/actors/ovl_player_actor/z_player.c b/mm/src/overlays/actors/ovl_player_actor/z_player.c index 8db84012cf..904fcc1d7d 100644 --- a/mm/src/overlays/actors/ovl_player_actor/z_player.c +++ b/mm/src/overlays/actors/ovl_player_actor/z_player.c @@ -3731,7 +3731,7 @@ void Player_ProcessItemButtons(Player* this, PlayState* play) { maskIdMinusOne = this->currentMask - 1; } - if (GameInteractor_Should(GI_VB_ALLOW_EQUIP_MASK, false, NULL)) { + if (GameInteractor_Should(GI_VB_ALLOW_EQUIP_MASK, false)) { Player_UseItem(play, this, Player_MaskIdToItemId(maskIdMinusOne)); return; } From 59248265bb41c81cf04b4a23d9efa0c1ec7bcd06 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 11 Aug 2024 10:45:25 -0400 Subject: [PATCH 24/52] Initial implementation --- libultraship | 2 +- mm/2s2h/BenGui/BenMenuBar.cpp | 3 + mm/2s2h/Enhancements/Enhancements.cpp | 1 + mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 179 ++++++++++++++++++ mm/2s2h/Enhancements/Masks/EasyMaskEquip.h | 15 ++ mm/2s2h/GameInteractor/GameInteractor.cpp | 4 + mm/2s2h/GameInteractor/GameInteractor.h | 2 + mm/include/functions.h | 1 + mm/src/code/z_player_lib.c | 1 + .../actors/ovl_player_actor/z_player.c | 10 +- .../ovl_kaleido_scope/z_kaleido_mask.c | 2 + .../ovl_kaleido_scope/z_kaleido_scope_NES.c | 2 + 12 files changed, 219 insertions(+), 3 deletions(-) create mode 100644 mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp create mode 100644 mm/2s2h/Enhancements/Masks/EasyMaskEquip.h diff --git a/libultraship b/libultraship index 4160fa0aa0..7a88bfc08c 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 4160fa0aa07d3946cbed5e397c54e6260f67dc5a +Subproject commit 7a88bfc08cbe44fd682b8f6f66b9d32d44b699bc diff --git a/mm/2s2h/BenGui/BenMenuBar.cpp b/mm/2s2h/BenGui/BenMenuBar.cpp index 081d139558..07ed6c718d 100644 --- a/mm/2s2h/BenGui/BenMenuBar.cpp +++ b/mm/2s2h/BenGui/BenMenuBar.cpp @@ -614,6 +614,9 @@ void DrawEnhancementsMenu() { "'A' on it in the mask menu." })) { UpdatePersistentMasksState(); } + UIWidgets::CVarCheckbox( + "Easy Mask Equip", "gEnhancements.Masks.EasyMaskEquip", + { .tooltip = "Allows you to equip masks directly from the pause menu by pressing A." }); ImGui::EndMenu(); } diff --git a/mm/2s2h/Enhancements/Enhancements.cpp b/mm/2s2h/Enhancements/Enhancements.cpp index f7f1a4a1ac..e472452653 100644 --- a/mm/2s2h/Enhancements/Enhancements.cpp +++ b/mm/2s2h/Enhancements/Enhancements.cpp @@ -44,6 +44,7 @@ void InitEnhancements() { RegisterBlastMaskKeg(); RegisterNoBlastMaskCooldown(); RegisterPersistentMasks(); + RegisterEasyMaskEquip(); // Minigames RegisterAlwaysWinDoggyRace(); diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp new file mode 100644 index 0000000000..6ad4b9a296 --- /dev/null +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -0,0 +1,179 @@ +#include "EasyMaskEquip.h" +#include +#include "Enhancements/GameInteractor/GameInteractor.h" +#include "Enhancements/FrameInterpolation/FrameInterpolation.h" + +extern "C" { +#include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" +#include "interface/parameter_static/parameter_static.h" +} + +static Vtx* easyMaskEquipVtx; +static s16 sPendingMask = ITEM_NONE; +static bool sIsTransforming = false; + +extern "C" bool EasyMaskEquip_IsEnabled() { + return CVarGetInteger("gEnhancements.Masks.EasyMaskEquip", 0) && + gPlayState->pauseCtx.debugEditor == DEBUG_EDITOR_NONE; +} + +s16 GetEquippedMaskSlot() { + s16 equippedMask = Player_GetCurMaskItemId(gPlayState); + if (equippedMask == ITEM_NONE || sIsTransforming) { + equippedMask = sPendingMask; + } + + // Check for pending mask first + if (sPendingMask != ITEM_NONE) { + for (s16 i = 0; i < MASK_NUM_SLOTS; i++) { + if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] == sPendingMask) { + return i; + } + } + } + + // Check for equipped mask + for (s16 i = 0; i < MASK_NUM_SLOTS; i++) { + if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] == equippedMask) { + return i; + } + } + return -1; +} + +void UpdateEasyMaskEquipVtx(PauseContext* pauseCtx) { + s16 slot = GetEquippedMaskSlot(); + if (slot == -1) { + return; + } + + s16 slotX = slot % MASK_GRID_COLS; + s16 slotY = slot / MASK_GRID_COLS; + s16 initialX = 0 - (MASK_GRID_COLS * MASK_GRID_CELL_WIDTH) / 2; + s16 initialY = (MASK_GRID_ROWS * MASK_GRID_CELL_HEIGHT) / 2 - 6; + s16 vtxX = initialX + (slotX * MASK_GRID_CELL_WIDTH); + s16 vtxY = initialY - (slotY * MASK_GRID_CELL_HEIGHT) + pauseCtx->offsetY; + + easyMaskEquipVtx[0].v.ob[0] = easyMaskEquipVtx[2].v.ob[0] = vtxX; + easyMaskEquipVtx[1].v.ob[0] = easyMaskEquipVtx[3].v.ob[0] = vtxX + MASK_GRID_CELL_WIDTH; + easyMaskEquipVtx[0].v.ob[1] = easyMaskEquipVtx[1].v.ob[1] = vtxY; + easyMaskEquipVtx[2].v.ob[1] = easyMaskEquipVtx[3].v.ob[1] = vtxY - MASK_GRID_CELL_HEIGHT; +} + +void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { + s16 slot = GetEquippedMaskSlot(); + if (slot == -1) { + return; + } + + GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; + + OPEN_DISPS(gfxCtx); + + UpdateEasyMaskEquipVtx(pauseCtx); + + gDPPipeSync(POLY_OPA_DISP++); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + gDPSetCombineLERP(POLY_OPA_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); + gSPVertex(POLY_OPA_DISP++, (uintptr_t)easyMaskEquipVtx, 4, 0); + gDPLoadTextureBlock(POLY_OPA_DISP++, gEquippedItemOutlineTex, G_IM_FMT_IA, G_IM_SIZ_8b, 32, 32, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, + G_TX_NOLOD); + gSP1Quadrangle(POLY_OPA_DISP++, 0, 2, 3, 1, 0); + + CLOSE_DISPS(gfxCtx); +} + +void AllocateEasyMaskEquipVtx(GraphicsContext* gfxCtx) { + easyMaskEquipVtx = (Vtx*)GRAPH_ALLOC(gfxCtx, 4 * sizeof(Vtx)); + for (int i = 0; i < 4; i++) { + easyMaskEquipVtx[i].v.ob[2] = 0; + easyMaskEquipVtx[i].v.flag = 0; + easyMaskEquipVtx[i].v.tc[0] = (i & 1) ? 32 << 5 : 0; + easyMaskEquipVtx[i].v.tc[1] = (i & 2) ? 32 << 5 : 0; + easyMaskEquipVtx[i].v.cn[0] = easyMaskEquipVtx[i].v.cn[1] = easyMaskEquipVtx[i].v.cn[2] = + easyMaskEquipVtx[i].v.cn[3] = 255; + } +} + +void HandleEasyMaskEquip(PauseContext* pauseCtx) { + if (pauseCtx->state == PAUSE_STATE_MAIN && pauseCtx->mainState == PAUSE_MAIN_STATE_IDLE) { + if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_A)) { + s16 cursorItem = pauseCtx->cursorItem[PAUSE_MASK]; + if (cursorItem != PAUSE_ITEM_NONE) { + sPendingMask = cursorItem; + sIsTransforming = + (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || + cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT); + UpdateEasyMaskEquipVtx(pauseCtx); + } + } else if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_B)) { + sPendingMask = ITEM_NONE; + sIsTransforming = false; + } + } +} + +void RegisterEasyMaskEquip() { + auto gameInteractor = GameInteractor::Instance; + + gameInteractor->RegisterGameHook([](PauseContext* pauseCtx) { + if (EasyMaskEquip_IsEnabled() && pauseCtx->pageIndex == PAUSE_MASK) { + HandleEasyMaskEquip(pauseCtx); + } + }); + + gameInteractor->RegisterGameHookForID( + PAUSE_MASK, [](PauseContext* pauseCtx, u16 pageIndex) { + if (EasyMaskEquip_IsEnabled() && pauseCtx->pageIndex == PAUSE_MASK) { + GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; + OPEN_DISPS(gfxCtx); + AllocateEasyMaskEquipVtx(gfxCtx); + UpdateEasyMaskEquipVtx(pauseCtx); + DrawEasyMaskEquipBorder(pauseCtx); + CLOSE_DISPS(gfxCtx); + } + }); + + gameInteractor->RegisterGameHook([]() { + if (EasyMaskEquip_IsEnabled() && gPlayState->pauseCtx.pageIndex == PAUSE_MASK) { + Player* player = GET_PLAYER(gPlayState); + if (sPendingMask != ITEM_NONE) { + if (sPendingMask != ITEM_MASK_DEKU && sPendingMask != ITEM_MASK_GORON && + sPendingMask != ITEM_MASK_ZORA && sPendingMask != ITEM_MASK_FIERCE_DEITY && + sPendingMask != ITEM_MASK_GIANT) { + if (player->transformation != PLAYER_FORM_HUMAN) { + // Equip the new mask directly if not a transformation mask and player is not human + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sIsTransforming = true; + } else { + // Equip the new mask directly if not a transformation mask and player is human + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sPendingMask = ITEM_NONE; // Clear pending mask after equipping + } + } else { + // Handle transformation masks + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sIsTransforming = true; + } + } + } + }); + + gameInteractor->RegisterGameHook([](Actor* actor) { + if (EasyMaskEquip_IsEnabled() && actor->id == ACTOR_PLAYER) { + Player* player = (Player*)actor; + if (sIsTransforming && player->transformation == PLAYER_FORM_HUMAN) { + if (player->skelAnime.curFrame == player->skelAnime.endFrame) { + if (sPendingMask != ITEM_NONE) { + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sPendingMask = ITEM_NONE; // Clear pending mask after equipping + sIsTransforming = false; + } + } + } + } + }); +} diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.h b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.h new file mode 100644 index 0000000000..72478bcf3c --- /dev/null +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.h @@ -0,0 +1,15 @@ +#ifndef EASY_MASK_EQUIP_H +#define EASY_MASK_EQUIP_H + +#ifdef __cplusplus +extern "C" { +#endif + +bool EasyMaskEquip_IsEnabled(void); +void RegisterEasyMaskEquip(void); + +#ifdef __cplusplus +} +#endif + +#endif // EASY_MASK_EQUIP_H \ No newline at end of file diff --git a/mm/2s2h/GameInteractor/GameInteractor.cpp b/mm/2s2h/GameInteractor/GameInteractor.cpp index fbaf19930c..7e264e574f 100644 --- a/mm/2s2h/GameInteractor/GameInteractor.cpp +++ b/mm/2s2h/GameInteractor/GameInteractor.cpp @@ -38,6 +38,10 @@ void GameInteractor_ExecuteAfterKaleidoDrawPage(PauseContext* pauseCtx, u16 paus GameInteractor::Instance->ExecuteHooksForID(pauseIndex, pauseCtx, pauseIndex); } +void GameInteractor_ExecuteOnKaleidoClose() { + GameInteractor::Instance->ExecuteHooks(); +} + void GameInteractor_ExecuteOnSaveInit(s16 fileNum) { GameInteractor::Instance->ExecuteHooks(fileNum); } diff --git a/mm/2s2h/GameInteractor/GameInteractor.h b/mm/2s2h/GameInteractor/GameInteractor.h index e3f79ecb63..73c5fb06e4 100644 --- a/mm/2s2h/GameInteractor/GameInteractor.h +++ b/mm/2s2h/GameInteractor/GameInteractor.h @@ -277,6 +277,7 @@ class GameInteractor { DEFINE_HOOK(OnKaleidoUpdate, (PauseContext * pauseCtx)); DEFINE_HOOK(BeforeKaleidoDrawPage, (PauseContext * pauseCtx, u16 pauseIndex)); DEFINE_HOOK(AfterKaleidoDrawPage, (PauseContext * pauseCtx, u16 pauseIndex)); + DEFINE_HOOK(OnKaleidoClose, ()); DEFINE_HOOK(OnSaveInit, (s16 fileNum)); DEFINE_HOOK(BeforeEndOfCycleSave, ()); DEFINE_HOOK(AfterEndOfCycleSave, ()); @@ -327,6 +328,7 @@ void GameInteractor_ExecuteOnConsoleLogoUpdate(); void GameInteractor_ExecuteOnKaleidoUpdate(PauseContext* pauseCtx); void GameInteractor_ExecuteBeforeKaleidoDrawPage(PauseContext* pauseCtx, u16 pauseIndex); void GameInteractor_ExecuteAfterKaleidoDrawPage(PauseContext* pauseCtx, u16 pauseIndex); +void GameInteractor_ExecuteOnKaleidoClose(); void GameInteractor_ExecuteOnSaveInit(s16 fileNum); void GameInteractor_ExecuteBeforeEndOfCycleSave(); void GameInteractor_ExecuteAfterEndOfCycleSave(); diff --git a/mm/include/functions.h b/mm/include/functions.h index c4d91709f5..0747682f48 100644 --- a/mm/include/functions.h +++ b/mm/include/functions.h @@ -1331,6 +1331,7 @@ void Heaps_Alloc(void); void KaleidoScope_UpdateOwlWarpNamePanel(PlayState* play); void KaleidoScope_UpdateNamePanel(PlayState* play); void SkinMatrix_Clear(MtxF* mf); +void Player_UseItem(PlayState* play, Player* this, ItemId item); // #endregion // #region 2S2H [Port] New methods added for porting void AudioSeq_SetPortVolumeScale(u8 seqPlayerIndex, f32 volume); diff --git a/mm/src/code/z_player_lib.c b/mm/src/code/z_player_lib.c index 9f624b93d2..04b2652277 100644 --- a/mm/src/code/z_player_lib.c +++ b/mm/src/code/z_player_lib.c @@ -43,6 +43,7 @@ // Assets for other actors #include "overlays/actors/ovl_En_Arrow/z_en_arrow.h" +#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" #include "2s2h/BenPort.h" #include "2s2h/GameInteractor/GameInteractor.h" diff --git a/mm/src/overlays/actors/ovl_player_actor/z_player.c b/mm/src/overlays/actors/ovl_player_actor/z_player.c index 397dfdd0cb..7756167a1e 100644 --- a/mm/src/overlays/actors/ovl_player_actor/z_player.c +++ b/mm/src/overlays/actors/ovl_player_actor/z_player.c @@ -47,6 +47,8 @@ #include "2s2h/GameInteractor/GameInteractor.h" +#include "2s2h/Enhancements/Masks/EasyMaskEquip.h" + #define THIS ((Player*)thisx) void Player_Init(Actor* thisx, PlayState* play); @@ -3730,8 +3732,12 @@ void Player_ProcessItemButtons(Player* this, PlayState* play) { if ((maskIdMinusOne < PLAYER_MASK_TRUTH - 1) || (maskIdMinusOne >= PLAYER_MASK_MAX - 1)) { maskIdMinusOne = this->currentMask - 1; } - Player_UseItem(play, this, Player_MaskIdToItemId(maskIdMinusOne)); - return; + + // EasyMaskEquip check allows equipping a mask without it being on a C button or D button + if (!EasyMaskEquip_IsEnabled()) { + Player_UseItem(play, this, Player_MaskIdToItemId(maskIdMinusOne)); + return; + } } if ((this->currentMask == PLAYER_MASK_GIANT) && (gSaveContext.save.saveInfo.playerData.magic == 0)) { diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c index b6ebdcbbdf..506be9d948 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c @@ -672,6 +672,8 @@ void KaleidoScope_UpdateMaskCursor(PlayState* play) { sMaskEquipState = EQUIP_STATE_MOVE_TO_C_BTN; sMaskEquipAnimTimer = 10; Audio_PlaySfx(NA_SE_SY_DECIDE); + // EasyMaskEquip check disables the mask description textbox from appearing when a mask is selected + // with A button. } else if ((pauseCtx->debugEditor == DEBUG_EDITOR_NONE) && (pauseCtx->state == PAUSE_STATE_MAIN) && (pauseCtx->mainState == PAUSE_MAIN_STATE_IDLE) && CHECK_BTN_ALL(input->press.button, BTN_A) && (msgCtx->msgLength == 0)) { diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c index 078491f2af..136639def0 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c @@ -19,6 +19,7 @@ #include "archives/map_name_static/map_name_static.h" #include "2s2h/Enhancements/FrameInterpolation/FrameInterpolation.h" #include "2s2h/Enhancements/Saving/SavingEnhancements.h" +#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" #include "2s2h_assets.h" @@ -4138,6 +4139,7 @@ void KaleidoScope_Update(PlayState* play) { gSaveContext.hudVisibility = HUD_VISIBILITY_IDLE; Interface_SetHudVisibility(sUnpausedHudVisibility); Audio_SetPauseState(false); + GameInteractor_ExecuteOnKaleidoClose(); break; default: From 14d38e0673814c073f3eca16cd0c88353e2fad4a Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 11 Aug 2024 14:31:09 -0400 Subject: [PATCH 25/52] fixed bug where mask was reequipping on next pause menu close --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index 6ad4b9a296..ce839c9e15 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -10,6 +10,7 @@ extern "C" { static Vtx* easyMaskEquipVtx; static s16 sPendingMask = ITEM_NONE; +static s16 sLastEquippedMask = ITEM_NONE; static bool sIsTransforming = false; extern "C" bool EasyMaskEquip_IsEnabled() { @@ -145,17 +146,18 @@ void RegisterEasyMaskEquip() { sPendingMask != ITEM_MASK_ZORA && sPendingMask != ITEM_MASK_FIERCE_DEITY && sPendingMask != ITEM_MASK_GIANT) { if (player->transformation != PLAYER_FORM_HUMAN) { - // Equip the new mask directly if not a transformation mask and player is not human Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sLastEquippedMask = sPendingMask; sIsTransforming = true; } else { - // Equip the new mask directly if not a transformation mask and player is human Player_UseItem(gPlayState, player, static_cast(sPendingMask)); - sPendingMask = ITEM_NONE; // Clear pending mask after equipping + sLastEquippedMask = sPendingMask; + sPendingMask = ITEM_NONE; } } else { - // Handle transformation masks Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sLastEquippedMask = sPendingMask; + sPendingMask = ITEM_NONE; sIsTransforming = true; } } @@ -169,7 +171,8 @@ void RegisterEasyMaskEquip() { if (player->skelAnime.curFrame == player->skelAnime.endFrame) { if (sPendingMask != ITEM_NONE) { Player_UseItem(gPlayState, player, static_cast(sPendingMask)); - sPendingMask = ITEM_NONE; // Clear pending mask after equipping + sLastEquippedMask = sPendingMask; + sPendingMask = ITEM_NONE; sIsTransforming = false; } } From c750c6b046fcf74201523c77e57f4770ad714df0 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 11 Aug 2024 15:36:32 -0400 Subject: [PATCH 26/52] added checks to restrict masks from being equipped in some situations plus moved CHECK_GIVEN_MASK_ON_MOON to macros.h --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 18 ++++++++++++++++++ mm/include/macros.h | 3 +++ .../ovl_kaleido_scope/z_kaleido_mask.c | 2 -- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index ce839c9e15..cc1536ddb1 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -6,6 +6,8 @@ extern "C" { #include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" #include "interface/parameter_static/parameter_static.h" +#include "macros.h" +extern u16 sMasksGivenOnMoonBits[]; } static Vtx* easyMaskEquipVtx; @@ -104,6 +106,22 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_A)) { s16 cursorItem = pauseCtx->cursorItem[PAUSE_MASK]; if (cursorItem != PAUSE_ITEM_NONE) { + Player* player = GET_PLAYER(gPlayState); + + // Check if the player is underwater and trying to equip Deku or Goron mask + if ((Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && + (Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE) && + (cursorItem != ITEM_MASK_ZORA)) { + Audio_PlaySfx(NA_SE_SY_ERROR); + return; + } + + // Check if the mask is given on the moon + if (CHECK_GIVEN_MASK_ON_MOON(cursorItem - ITEM_NUM_SLOTS)) { + Audio_PlaySfx(NA_SE_SY_ERROR); + return; + } + sPendingMask = cursorItem; sIsTransforming = (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || diff --git a/mm/include/macros.h b/mm/include/macros.h index 456e5df509..6cef66337e 100644 --- a/mm/include/macros.h +++ b/mm/include/macros.h @@ -115,6 +115,9 @@ #define D_0F000000_TO_SEGMENTED (0x0F000000 | 1) // Used to compare animations. The statically linked global animations can end up with different pointer addresses if an animation gets set from one file and then compared against the static animation in another #define BEN_ANIM_EQUAL(anim1, anim2) ((anim1 == NULL) ? ((anim2 == NULL) ? true : false) : ((anim2 == NULL) ? false : strcmp(anim1, anim2) == 0)) +// Macro to check if a given mask has been given on the moon +#define CHECK_GIVEN_MASK_ON_MOON(maskIndex) \ + (gSaveContext.masksGivenOnMoon[sMasksGivenOnMoonBits[maskIndex] >> 8] & (u8)sMasksGivenOnMoonBits[maskIndex]) // #endregion #endif // MACROS_H diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c index 506be9d948..38c357edaa 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c @@ -158,8 +158,6 @@ u8 gMaskPlayerFormSlotRestrictions[PLAYER_FORM_MAX][MASK_NUM_SLOTS] = { #define SET_MOON_MASK_BIT(masksGivenOnMoonIndex, masksGivenOnMoonFlag) \ ((masksGivenOnMoonIndex) << 8 | (masksGivenOnMoonFlag)) -#define CHECK_GIVEN_MASK_ON_MOON(maskIndex) \ - (gSaveContext.masksGivenOnMoon[sMasksGivenOnMoonBits[maskIndex] >> 8] & (u8)sMasksGivenOnMoonBits[maskIndex]) u16 sMasksGivenOnMoonBits[] = { SET_MOON_MASK_BIT(1, 0x1), // SLOT_MASK_POSTMAN From fafed87cd2b13252db9f2ca35c861d84b5568924 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 11 Aug 2024 17:32:23 -0400 Subject: [PATCH 27/52] adds check to prevent equip in air and adds selection audio --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index cc1536ddb1..484d30e8a6 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -108,6 +108,14 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { if (cursorItem != PAUSE_ITEM_NONE) { Player* player = GET_PLAYER(gPlayState); + // Check if the player is in the air and trying to equip a transformation mask + if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND) && + (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || + cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT)) { + Audio_PlaySfx(NA_SE_SY_ERROR); + return; + } + // Check if the player is underwater and trying to equip Deku or Goron mask if ((Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && (Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE) && @@ -127,6 +135,7 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT); UpdateEasyMaskEquipVtx(pauseCtx); + Audio_PlaySfx(NA_SE_SY_DECIDE); } } else if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_B)) { sPendingMask = ITEM_NONE; From ae4525f452cc6391248543ab5bdac300f319466b Mon Sep 17 00:00:00 2001 From: mckinlee Date: Mon, 12 Aug 2024 10:22:28 -0400 Subject: [PATCH 28/52] added checks to prevent equipping transformation masks when the player is busy with an action --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index 484d30e8a6..cbaa1a819d 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -130,6 +130,17 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { return; } + // Check if the player is busy with an action and trying to equip a transformation mask + if ((cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || + cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT) && + ((player->stateFlags1 & (PLAYER_STATE1_800 | PLAYER_STATE1_400 | PLAYER_STATE1_20000000)) || + (player->stateFlags2 & (PLAYER_STATE2_10 | PLAYER_STATE2_80)) || + (player->heldItemAction != PLAYER_IA_NONE) || + (player->meleeWeaponState != PLAYER_MELEE_WEAPON_STATE_0))) { + Audio_PlaySfx(NA_SE_SY_ERROR); + return; + } + sPendingMask = cursorItem; sIsTransforming = (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || From cc0984f09b1a7db13de6b6fed80e24d15bf03e2f Mon Sep 17 00:00:00 2001 From: mckinlee Date: Mon, 12 Aug 2024 12:33:17 -0400 Subject: [PATCH 29/52] bug fix for border appearing on empty mask slot and removed some residual unused code --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index cbaa1a819d..9e2cab29fc 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -69,6 +69,11 @@ void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { return; } + // Check if the player owns the mask + if (gSaveContext.save.saveInfo.inventory.items[slot + ITEM_NUM_SLOTS] == ITEM_NONE) { + return; + } + GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; OPEN_DISPS(gfxCtx); From 8ad39bcd3a9edd26dbd07fa0285a6ce28261aaa3 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Tue, 13 Aug 2024 13:09:19 -0400 Subject: [PATCH 30/52] bug fix for border not appearing and added check to prevent equipping any mask in the air when wearing a transformation mask --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 41 +++++++++++++++----- 1 file changed, 31 insertions(+), 10 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index 9e2cab29fc..27d3e23c67 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -21,9 +21,10 @@ extern "C" bool EasyMaskEquip_IsEnabled() { } s16 GetEquippedMaskSlot() { - s16 equippedMask = Player_GetCurMaskItemId(gPlayState); - if (equippedMask == ITEM_NONE || sIsTransforming) { - equippedMask = sPendingMask; + s16 maskToCheck = sPendingMask != ITEM_NONE ? sPendingMask : Player_GetCurMaskItemId(gPlayState); + + if (maskToCheck == ITEM_NONE || sIsTransforming) { + maskToCheck = sLastEquippedMask; } // Check for pending mask first @@ -37,10 +38,11 @@ s16 GetEquippedMaskSlot() { // Check for equipped mask for (s16 i = 0; i < MASK_NUM_SLOTS; i++) { - if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] == equippedMask) { + if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] == maskToCheck) { return i; } } + return -1; } @@ -74,6 +76,11 @@ void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { return; } + // If the equipped item is equal to the pending item, remove the border + if (Player_GetCurMaskItemId(gPlayState) == sPendingMask) { + return; + } + GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; OPEN_DISPS(gfxCtx); @@ -111,14 +118,27 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_A)) { s16 cursorItem = pauseCtx->cursorItem[PAUSE_MASK]; if (cursorItem != PAUSE_ITEM_NONE) { + // If sPendingMask is already the cursor item, remove the border and reset sPendingMask + if (sPendingMask == cursorItem) { + sPendingMask = ITEM_NONE; + sIsTransforming = false; + return; + } + Player* player = GET_PLAYER(gPlayState); // Check if the player is in the air and trying to equip a transformation mask - if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND) && - (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || - cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT)) { - Audio_PlaySfx(NA_SE_SY_ERROR); - return; + if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND)) { + if (player->transformation == PLAYER_FORM_HUMAN) { + if (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || + cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT) { + Audio_PlaySfx(NA_SE_SY_ERROR); + return; + } + } else { + Audio_PlaySfx(NA_SE_SY_ERROR); + return; + } } // Check if the player is underwater and trying to equip Deku or Goron mask @@ -156,6 +176,7 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { } else if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_B)) { sPendingMask = ITEM_NONE; sIsTransforming = false; + sLastEquippedMask = ITEM_NONE; } } } @@ -222,4 +243,4 @@ void RegisterEasyMaskEquip() { } } }); -} +} \ No newline at end of file From 772962d823e8a11826a29aa40ce644e29cddf728 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Tue, 13 Aug 2024 13:12:38 -0400 Subject: [PATCH 31/52] forgot to run clang format script --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index 27d3e23c67..199e3ede08 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -130,8 +130,9 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { // Check if the player is in the air and trying to equip a transformation mask if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND)) { if (player->transformation == PLAYER_FORM_HUMAN) { - if (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || - cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT) { + if (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || + cursorItem == ITEM_MASK_ZORA || cursorItem == ITEM_MASK_FIERCE_DEITY || + cursorItem == ITEM_MASK_GIANT) { Audio_PlaySfx(NA_SE_SY_ERROR); return; } From ffa72f22d6b9b41b1c37ac4bbe0639614fe2f0d6 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Fri, 13 Sep 2024 09:34:03 -0400 Subject: [PATCH 32/52] Refactor EasyMaskEquip related logic - Streamlined mask selection logic with helper functions - Added grayscale indicators for unavailable masks in the UI - Optimized vertex handling for mask border rendering - Improved game hook integrations for reliable mask equipping - Enhanced code readability and maintainability --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 349 +++++++++++-------- mm/include/functions.h | 1 + 2 files changed, 213 insertions(+), 137 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index 199e3ede08..e1608c4400 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -7,38 +7,51 @@ extern "C" { #include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" #include "interface/parameter_static/parameter_static.h" #include "macros.h" +#include "variables.h" extern u16 sMasksGivenOnMoonBits[]; +extern s16 sPauseMenuVerticalOffset; } -static Vtx* easyMaskEquipVtx; +static Vtx* easyMaskEquipVtx = nullptr; static s16 sPendingMask = ITEM_NONE; static s16 sLastEquippedMask = ITEM_NONE; static bool sIsTransforming = false; -extern "C" bool EasyMaskEquip_IsEnabled() { +// Define transformation masks for easy reference +constexpr std::array TransformationMasks = { ITEM_MASK_DEKU, ITEM_MASK_GORON, ITEM_MASK_ZORA, + ITEM_MASK_FIERCE_DEITY, ITEM_MASK_GIANT }; + +// Helper function to check if a mask is a transformation mask +bool IsTransformationMask(ItemId mask) { + return std::find(TransformationMasks.begin(), TransformationMasks.end(), mask) != TransformationMasks.end(); +} + +// Check if EasyMaskEquip is enabled +bool EasyMaskEquip_IsEnabled() { return CVarGetInteger("gEnhancements.Masks.EasyMaskEquip", 0) && gPlayState->pauseCtx.debugEditor == DEBUG_EDITOR_NONE; } +// Get the equipped mask slot, prioritizing pending masks s16 GetEquippedMaskSlot() { - s16 maskToCheck = sPendingMask != ITEM_NONE ? sPendingMask : Player_GetCurMaskItemId(gPlayState); + s16 maskToCheck = + (sPendingMask != ITEM_NONE && !sIsTransforming) ? sPendingMask : Player_GetCurMaskItemId(gPlayState); + maskToCheck = (maskToCheck == ITEM_NONE || sIsTransforming) ? sLastEquippedMask : maskToCheck; - if (maskToCheck == ITEM_NONE || sIsTransforming) { - maskToCheck = sLastEquippedMask; - } + auto& items = gSaveContext.save.saveInfo.inventory.items; - // Check for pending mask first + // Search for pending mask first if (sPendingMask != ITEM_NONE) { - for (s16 i = 0; i < MASK_NUM_SLOTS; i++) { - if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] == sPendingMask) { + for (s16 i = 0; i < MASK_NUM_SLOTS; ++i) { + if (items[i + ITEM_NUM_SLOTS] == sPendingMask) { return i; } } } - // Check for equipped mask - for (s16 i = 0; i < MASK_NUM_SLOTS; i++) { - if (gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS] == maskToCheck) { + // Then search for the equipped mask + for (s16 i = 0; i < MASK_NUM_SLOTS; ++i) { + if (items[i + ITEM_NUM_SLOTS] == maskToCheck) { return i; } } @@ -46,43 +59,87 @@ s16 GetEquippedMaskSlot() { return -1; } +// Update vertex positions based on the equipped mask slot void UpdateEasyMaskEquipVtx(PauseContext* pauseCtx) { s16 slot = GetEquippedMaskSlot(); - if (slot == -1) { + if (slot == -1) return; - } - s16 slotX = slot % MASK_GRID_COLS; - s16 slotY = slot / MASK_GRID_COLS; - s16 initialX = 0 - (MASK_GRID_COLS * MASK_GRID_CELL_WIDTH) / 2; - s16 initialY = (MASK_GRID_ROWS * MASK_GRID_CELL_HEIGHT) / 2 - 6; - s16 vtxX = initialX + (slotX * MASK_GRID_CELL_WIDTH); - s16 vtxY = initialY - (slotY * MASK_GRID_CELL_HEIGHT) + pauseCtx->offsetY; - - easyMaskEquipVtx[0].v.ob[0] = easyMaskEquipVtx[2].v.ob[0] = vtxX; - easyMaskEquipVtx[1].v.ob[0] = easyMaskEquipVtx[3].v.ob[0] = vtxX + MASK_GRID_CELL_WIDTH; - easyMaskEquipVtx[0].v.ob[1] = easyMaskEquipVtx[1].v.ob[1] = vtxY; - easyMaskEquipVtx[2].v.ob[1] = easyMaskEquipVtx[3].v.ob[1] = vtxY - MASK_GRID_CELL_HEIGHT; + const s16 slotX = slot % MASK_GRID_COLS; + const s16 slotY = slot / MASK_GRID_COLS; + const s16 initialX = -(MASK_GRID_COLS * MASK_GRID_CELL_WIDTH) / 2; + const s16 initialY = (MASK_GRID_ROWS * MASK_GRID_CELL_HEIGHT) / 2 - 6; + const s16 vtxX = initialX + (slotX * MASK_GRID_CELL_WIDTH); + const s16 vtxY = initialY - (slotY * MASK_GRID_CELL_HEIGHT) + pauseCtx->offsetY; + + const std::array xCoords = { vtxX, vtxX + MASK_GRID_CELL_WIDTH, vtxX, vtxX + MASK_GRID_CELL_WIDTH }; + const std::array yCoords = { vtxY, vtxY, vtxY - MASK_GRID_CELL_HEIGHT, vtxY - MASK_GRID_CELL_HEIGHT }; + + for (int i = 0; i < 4; ++i) { + easyMaskEquipVtx[i].v.ob[0] = xCoords[i]; + easyMaskEquipVtx[i].v.ob[1] = yCoords[i]; + } } -void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { - s16 slot = GetEquippedMaskSlot(); - if (slot == -1) { - return; +// Determine if a mask should be equipped based on various conditions +bool ShouldEquipMask(s16 cursorItem) { + const Player* player = GET_PLAYER(gPlayState); + + // Check if player is airborne and attempting to equip a transformation mask + bool isAirborne = !(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND); + bool notInWater = !(Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR && + Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE); + if (isAirborne && notInWater) { + if (player->transformation == PLAYER_FORM_HUMAN && IsTransformationMask(static_cast(cursorItem))) { + return true; + } + if (player->transformation != PLAYER_FORM_HUMAN) { + return true; + } } - // Check if the player owns the mask - if (gSaveContext.save.saveInfo.inventory.items[slot + ITEM_NUM_SLOTS] == ITEM_NONE) { - return; + // Check if underwater and trying to equip non-Zora masks + auto hazard = Player_GetEnvironmentalHazard(gPlayState); + if (hazard >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR && hazard <= PLAYER_ENV_HAZARD_UNDERWATER_FREE && + cursorItem != ITEM_MASK_ZORA) { + return true; + } + + // Check if the mask is given on the moon. Need to test this. + if (cursorItem >= ITEM_NUM_SLOTS && (cursorItem - ITEM_NUM_SLOTS) < MASK_NUM_SLOTS && + CHECK_GIVEN_MASK_ON_MOON(cursorItem - ITEM_NUM_SLOTS)) { + return true; } - // If the equipped item is equal to the pending item, remove the border - if (Player_GetCurMaskItemId(gPlayState) == sPendingMask) { + // Check if player is busy and trying to equip a transformation mask + if (IsTransformationMask(static_cast(cursorItem))) { + const auto& flags1 = player->stateFlags1; + const auto& flags2 = player->stateFlags2; + const auto heldAction = player->heldItemAction; + + bool isBusy = flags1 & (PLAYER_STATE1_800 | PLAYER_STATE1_400 | PLAYER_STATE1_20000000) || + flags2 & (PLAYER_STATE2_10 | PLAYER_STATE2_80) || heldAction == PLAYER_IA_BOW || + heldAction == PLAYER_IA_BOW_FIRE || heldAction == PLAYER_IA_BOW_ICE || + heldAction == PLAYER_IA_BOW_LIGHT || heldAction == PLAYER_IA_BOMB || + heldAction == PLAYER_IA_BOMBCHU || heldAction == PLAYER_IA_HOOKSHOT || + heldAction == PLAYER_IA_POWDER_KEG; + + if (isBusy) + return true; + } + + return false; +} + +// Draw the border around the equipped mask +void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { + s16 slot = GetEquippedMaskSlot(); + if (slot == -1 || gSaveContext.save.saveInfo.inventory.items[slot + ITEM_NUM_SLOTS] == ITEM_NONE || + Player_GetCurMaskItemId(gPlayState) == sPendingMask) { return; } GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; - OPEN_DISPS(gfxCtx); UpdateEasyMaskEquipVtx(pauseCtx); @@ -92,7 +149,7 @@ void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { gDPSetCombineLERP(POLY_OPA_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); - gSPVertex(POLY_OPA_DISP++, (uintptr_t)easyMaskEquipVtx, 4, 0); + gSPVertex(POLY_OPA_DISP++, reinterpret_cast(easyMaskEquipVtx), 4, 0); // Fixed cast gDPLoadTextureBlock(POLY_OPA_DISP++, gEquippedItemOutlineTex, G_IM_FMT_IA, G_IM_SIZ_8b, 32, 32, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); @@ -101,98 +158,106 @@ void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { CLOSE_DISPS(gfxCtx); } +// Allocate memory for the vertex buffer void AllocateEasyMaskEquipVtx(GraphicsContext* gfxCtx) { - easyMaskEquipVtx = (Vtx*)GRAPH_ALLOC(gfxCtx, 4 * sizeof(Vtx)); - for (int i = 0; i < 4; i++) { + easyMaskEquipVtx = static_cast(GRAPH_ALLOC(gfxCtx, 4 * sizeof(Vtx))); + for (int i = 0; i < 4; ++i) { easyMaskEquipVtx[i].v.ob[2] = 0; easyMaskEquipVtx[i].v.flag = 0; easyMaskEquipVtx[i].v.tc[0] = (i & 1) ? 32 << 5 : 0; easyMaskEquipVtx[i].v.tc[1] = (i & 2) ? 32 << 5 : 0; - easyMaskEquipVtx[i].v.cn[0] = easyMaskEquipVtx[i].v.cn[1] = easyMaskEquipVtx[i].v.cn[2] = - easyMaskEquipVtx[i].v.cn[3] = 255; + std::fill(std::begin(easyMaskEquipVtx[i].v.cn), std::end(easyMaskEquipVtx[i].v.cn), static_cast(255)); } } +// Apply grayscale to mask icons that shouldn't be equipped +void ApplyMaskIconGrayscale(PauseContext* pauseCtx, GraphicsContext* gfxCtx) { + OPEN_DISPS(gfxCtx); + + for (int i = 0; i < MASK_NUM_SLOTS; ++i) { + const s16 cursorItem = gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS]; + if (cursorItem != ITEM_NONE && ShouldEquipMask(cursorItem)) { + gDPSetGrayscaleColor(POLY_OPA_DISP++, 109, 109, 109, 255); + gSPGrayscale(POLY_OPA_DISP++, true); + gSPVertex(POLY_OPA_DISP++, reinterpret_cast(&pauseCtx->maskVtx[i * 4]), 4, 0); // Fixed cast + KaleidoScope_DrawTexQuadRGBA32(gfxCtx, gItemIcons[cursorItem], 32, 32, 0); + gSPGrayscale(POLY_OPA_DISP++, false); + } + } + + CLOSE_DISPS(gfxCtx); +} + +// Placeholder for additional grayscale functionality on the item page +void ApplyItemPageMaskGrayscale(PauseContext* pauseCtx, GraphicsContext* gfxCtx) { + // TODO: Implement, but I'm at a loss for how to do this. +} + +// Handle equipping masks based on player input void HandleEasyMaskEquip(PauseContext* pauseCtx) { - if (pauseCtx->state == PAUSE_STATE_MAIN && pauseCtx->mainState == PAUSE_MAIN_STATE_IDLE) { - if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_A)) { - s16 cursorItem = pauseCtx->cursorItem[PAUSE_MASK]; - if (cursorItem != PAUSE_ITEM_NONE) { - // If sPendingMask is already the cursor item, remove the border and reset sPendingMask - if (sPendingMask == cursorItem) { - sPendingMask = ITEM_NONE; - sIsTransforming = false; - return; - } - - Player* player = GET_PLAYER(gPlayState); - - // Check if the player is in the air and trying to equip a transformation mask - if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND)) { - if (player->transformation == PLAYER_FORM_HUMAN) { - if (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || - cursorItem == ITEM_MASK_ZORA || cursorItem == ITEM_MASK_FIERCE_DEITY || - cursorItem == ITEM_MASK_GIANT) { - Audio_PlaySfx(NA_SE_SY_ERROR); - return; - } - } else { - Audio_PlaySfx(NA_SE_SY_ERROR); - return; - } - } - - // Check if the player is underwater and trying to equip Deku or Goron mask - if ((Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && - (Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE) && - (cursorItem != ITEM_MASK_ZORA)) { - Audio_PlaySfx(NA_SE_SY_ERROR); - return; - } - - // Check if the mask is given on the moon - if (CHECK_GIVEN_MASK_ON_MOON(cursorItem - ITEM_NUM_SLOTS)) { - Audio_PlaySfx(NA_SE_SY_ERROR); - return; - } - - // Check if the player is busy with an action and trying to equip a transformation mask - if ((cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || - cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT) && - ((player->stateFlags1 & (PLAYER_STATE1_800 | PLAYER_STATE1_400 | PLAYER_STATE1_20000000)) || - (player->stateFlags2 & (PLAYER_STATE2_10 | PLAYER_STATE2_80)) || - (player->heldItemAction != PLAYER_IA_NONE) || - (player->meleeWeaponState != PLAYER_MELEE_WEAPON_STATE_0))) { - Audio_PlaySfx(NA_SE_SY_ERROR); - return; - } - - sPendingMask = cursorItem; - sIsTransforming = - (cursorItem == ITEM_MASK_DEKU || cursorItem == ITEM_MASK_GORON || cursorItem == ITEM_MASK_ZORA || - cursorItem == ITEM_MASK_FIERCE_DEITY || cursorItem == ITEM_MASK_GIANT); - UpdateEasyMaskEquipVtx(pauseCtx); - Audio_PlaySfx(NA_SE_SY_DECIDE); + if (pauseCtx->state != PAUSE_STATE_MAIN || pauseCtx->mainState != PAUSE_MAIN_STATE_IDLE) + return; + + // Directly access the pressed buttons without using a Controller* variable + u16 pressedButtons = CONTROLLER1(&gPlayState->state)->press.button; + + if (CHECK_BTN_ALL(pressedButtons, BTN_A)) { + s16 cursorItem = pauseCtx->cursorItem[PAUSE_MASK]; + if (cursorItem != PAUSE_ITEM_NONE) { + // If sPendingMask is already the cursor item, remove the border and reset sPendingMask + if (sPendingMask == cursorItem) { + sPendingMask = ITEM_NONE; + sIsTransforming = false; + return; } - } else if (CHECK_BTN_ALL(CONTROLLER1(&gPlayState->state)->press.button, BTN_B)) { - sPendingMask = ITEM_NONE; - sIsTransforming = false; - sLastEquippedMask = ITEM_NONE; + + if (ShouldEquipMask(cursorItem)) { + Audio_PlaySfx(NA_SE_SY_ERROR); + return; + } + + sPendingMask = cursorItem; + sIsTransforming = IsTransformationMask(static_cast(cursorItem)); + UpdateEasyMaskEquipVtx(pauseCtx); + Audio_PlaySfx(NA_SE_SY_DECIDE); } + } else if (CHECK_BTN_ALL(pressedButtons, BTN_B)) { + sPendingMask = ITEM_NONE; + sIsTransforming = false; + sLastEquippedMask = ITEM_NONE; } } +// Register hooks for the EasyMaskEquip functionality void RegisterEasyMaskEquip() { auto gameInteractor = GameInteractor::Instance; + // Handle mask equipping logic during KaleidoScope update gameInteractor->RegisterGameHook([](PauseContext* pauseCtx) { if (EasyMaskEquip_IsEnabled() && pauseCtx->pageIndex == PAUSE_MASK) { HandleEasyMaskEquip(pauseCtx); } }); + // Apply grayscale to mask icons after drawing the mask page + gameInteractor->RegisterGameHookForID( + PAUSE_MASK, [](PauseContext* pauseCtx, u16) { + if (EasyMaskEquip_IsEnabled()) { + ApplyMaskIconGrayscale(pauseCtx, gPlayState->state.gfxCtx); + } + }); + + // Apply grayscale to item page masks after drawing the item page + gameInteractor->RegisterGameHookForID( + PAUSE_ITEM, [](PauseContext* pauseCtx, u16) { + if (EasyMaskEquip_IsEnabled()) { + ApplyItemPageMaskGrayscale(pauseCtx, gPlayState->state.gfxCtx); + } + }); + + // Draw the equip border before drawing the mask page gameInteractor->RegisterGameHookForID( - PAUSE_MASK, [](PauseContext* pauseCtx, u16 pageIndex) { + PAUSE_MASK, [](PauseContext* pauseCtx, u16) { if (EasyMaskEquip_IsEnabled() && pauseCtx->pageIndex == PAUSE_MASK) { GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; OPEN_DISPS(gfxCtx); @@ -203,45 +268,55 @@ void RegisterEasyMaskEquip() { } }); + // Handle mask equipping when KaleidoScope closes gameInteractor->RegisterGameHook([]() { - if (EasyMaskEquip_IsEnabled() && gPlayState->pauseCtx.pageIndex == PAUSE_MASK) { - Player* player = GET_PLAYER(gPlayState); - if (sPendingMask != ITEM_NONE) { - if (sPendingMask != ITEM_MASK_DEKU && sPendingMask != ITEM_MASK_GORON && - sPendingMask != ITEM_MASK_ZORA && sPendingMask != ITEM_MASK_FIERCE_DEITY && - sPendingMask != ITEM_MASK_GIANT) { - if (player->transformation != PLAYER_FORM_HUMAN) { - Player_UseItem(gPlayState, player, static_cast(sPendingMask)); - sLastEquippedMask = sPendingMask; - sIsTransforming = true; - } else { - Player_UseItem(gPlayState, player, static_cast(sPendingMask)); - sLastEquippedMask = sPendingMask; - sPendingMask = ITEM_NONE; - } - } else { - Player_UseItem(gPlayState, player, static_cast(sPendingMask)); - sLastEquippedMask = sPendingMask; - sPendingMask = ITEM_NONE; - sIsTransforming = true; - } + if (!EasyMaskEquip_IsEnabled() || gPlayState->pauseCtx.pageIndex != PAUSE_MASK) + return; + + Player* player = GET_PLAYER(gPlayState); + if (sPendingMask == ITEM_NONE) + return; + + bool isTransformation = IsTransformationMask(static_cast(sPendingMask)); + bool isCurrentlyTransformed = (player->transformation != PLAYER_FORM_HUMAN); + + if (!isTransformation) { + if (isCurrentlyTransformed) { + // Equip a regular mask while transformed: transform back to human first + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sLastEquippedMask = sPendingMask; + // Retain sPendingMask to equip after transformation + sIsTransforming = true; + } else { + // Equip a regular mask while already human + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sLastEquippedMask = sPendingMask; + sPendingMask = ITEM_NONE; + sIsTransforming = false; } + } else { + // Equip a transformation mask + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sLastEquippedMask = sPendingMask; + sPendingMask = ITEM_NONE; + sIsTransforming = true; } }); + // Update mask equipping state during actor updates gameInteractor->RegisterGameHook([](Actor* actor) { - if (EasyMaskEquip_IsEnabled() && actor->id == ACTOR_PLAYER) { - Player* player = (Player*)actor; - if (sIsTransforming && player->transformation == PLAYER_FORM_HUMAN) { - if (player->skelAnime.curFrame == player->skelAnime.endFrame) { - if (sPendingMask != ITEM_NONE) { - Player_UseItem(gPlayState, player, static_cast(sPendingMask)); - sLastEquippedMask = sPendingMask; - sPendingMask = ITEM_NONE; - sIsTransforming = false; - } - } + if (!EasyMaskEquip_IsEnabled() || actor->id != ACTOR_PLAYER) + return; + + Player* player = reinterpret_cast(actor); + if (sIsTransforming && player->transformation == PLAYER_FORM_HUMAN && + player->skelAnime.curFrame >= player->skelAnime.endFrame) { + if (sPendingMask != ITEM_NONE) { + Player_UseItem(gPlayState, player, static_cast(sPendingMask)); + sLastEquippedMask = sPendingMask; + sPendingMask = ITEM_NONE; + sIsTransforming = false; } } }); -} \ No newline at end of file +} diff --git a/mm/include/functions.h b/mm/include/functions.h index 0747682f48..9dd3872a50 100644 --- a/mm/include/functions.h +++ b/mm/include/functions.h @@ -1328,6 +1328,7 @@ void osContGetReadData(OSContPad* pad); // #region 2S2H [Port] Previously unavailable functions, made available for porting void PadMgr_ThreadEntry(); void Heaps_Alloc(void); +void KaleidoScope_GrayOutTextureRGBA32(u32* texture, u16 pixelCount); void KaleidoScope_UpdateOwlWarpNamePanel(PlayState* play); void KaleidoScope_UpdateNamePanel(PlayState* play); void SkinMatrix_Clear(MtxF* mf); From 27ba5098fed12e762cc7e96d66c911f557107967 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Fri, 13 Sep 2024 10:08:00 -0400 Subject: [PATCH 33/52] add missing before/after for the other draw, thanks Proxy! --- .../kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c index 136639def0..a4c21a5cdb 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c @@ -741,7 +741,9 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->itemPageVtx, sItemPageBgTextures); + GameInteractor_ExecuteBeforeKaleidoDrawPage(pauseCtx, PAUSE_ITEM); KaleidoScope_DrawItemSelect(play); + GameInteractor_ExecuteAfterKaleidoDrawPage(pauseCtx, PAUSE_ITEM); } if ((pauseCtx->pageIndex != PAUSE_MAP) && (pauseCtx->pageIndex != PAUSE_MASK)) { @@ -761,6 +763,7 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->mapPageVtx, sMapPageBgTextures); + GameInteractor_ExecuteBeforeKaleidoDrawPage(pauseCtx, PAUSE_MAP); if (sInDungeonScene) { KaleidoScope_DrawDungeonMap(play); Gfx_SetupDL42_Opa(gfxCtx); @@ -769,6 +772,7 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { } else { KaleidoScope_DrawWorldMap(play); } + GameInteractor_ExecuteAfterKaleidoDrawPage(pauseCtx, PAUSE_MAP); } if ((pauseCtx->pageIndex != PAUSE_QUEST) && (pauseCtx->pageIndex != PAUSE_ITEM)) { @@ -790,7 +794,9 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->questPageVtx, sQuestPageBgTextures); + GameInteractor_ExecuteBeforeKaleidoDrawPage(pauseCtx, PAUSE_QUEST); KaleidoScope_DrawQuestStatus(play); + GameInteractor_ExecuteAfterKaleidoDrawPage(pauseCtx, PAUSE_QUEST); } if ((pauseCtx->pageIndex != PAUSE_MASK) && (pauseCtx->pageIndex != PAUSE_MAP)) { @@ -812,7 +818,9 @@ void KaleidoScope_DrawPages(PlayState* play, GraphicsContext* gfxCtx) { POLY_OPA_DISP = KaleidoScope_DrawPageSections(POLY_OPA_DISP, pauseCtx->maskPageVtx, sMaskPageBgTextures); + GameInteractor_ExecuteBeforeKaleidoDrawPage(pauseCtx, PAUSE_MASK); KaleidoScope_DrawMaskSelect(play); + GameInteractor_ExecuteAfterKaleidoDrawPage(pauseCtx, PAUSE_MASK); } switch (pauseCtx->pageIndex) { From fb452ca68d174ce5410dacd98194b51955656e41 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Fri, 13 Sep 2024 10:58:14 -0400 Subject: [PATCH 34/52] remove redefinition and fix grayscale issue, again thanks Proxy! --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index e1608c4400..b36de1d1a3 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -188,11 +188,6 @@ void ApplyMaskIconGrayscale(PauseContext* pauseCtx, GraphicsContext* gfxCtx) { CLOSE_DISPS(gfxCtx); } -// Placeholder for additional grayscale functionality on the item page -void ApplyItemPageMaskGrayscale(PauseContext* pauseCtx, GraphicsContext* gfxCtx) { - // TODO: Implement, but I'm at a loss for how to do this. -} - // Handle equipping masks based on player input void HandleEasyMaskEquip(PauseContext* pauseCtx) { if (pauseCtx->state != PAUSE_STATE_MAIN || pauseCtx->mainState != PAUSE_MAIN_STATE_IDLE) @@ -247,14 +242,6 @@ void RegisterEasyMaskEquip() { } }); - // Apply grayscale to item page masks after drawing the item page - gameInteractor->RegisterGameHookForID( - PAUSE_ITEM, [](PauseContext* pauseCtx, u16) { - if (EasyMaskEquip_IsEnabled()) { - ApplyItemPageMaskGrayscale(pauseCtx, gPlayState->state.gfxCtx); - } - }); - // Draw the equip border before drawing the mask page gameInteractor->RegisterGameHookForID( PAUSE_MASK, [](PauseContext* pauseCtx, u16) { From 891747540726e9c69fa7370b2c1b0fd4be1bf8fa Mon Sep 17 00:00:00 2001 From: mckinlee Date: Fri, 13 Sep 2024 11:46:24 -0400 Subject: [PATCH 35/52] added more checks for when the player is busy and cannot equip a mask --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index b36de1d1a3..a7fb016c57 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -89,6 +89,15 @@ bool ShouldEquipMask(s16 cursorItem) { bool isAirborne = !(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND); bool notInWater = !(Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR && Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE); + + // Add a check for Zora transformation and a small delay after transformation + if (player->transformation == PLAYER_FORM_ZORA) { + // Ensure the player is fully transformed and not in the middle of an animation + if (player->skelAnime.curFrame < player->skelAnime.endFrame) { + return false; // Still transforming, don't allow mask change + } + } + if (isAirborne && notInWater) { if (player->transformation == PLAYER_FORM_HUMAN && IsTransformationMask(static_cast(cursorItem))) { return true; @@ -124,8 +133,11 @@ bool ShouldEquipMask(s16 cursorItem) { heldAction == PLAYER_IA_BOMBCHU || heldAction == PLAYER_IA_HOOKSHOT || heldAction == PLAYER_IA_POWDER_KEG; - if (isBusy) + bool isSwingingWeapon = player->meleeWeaponState != PLAYER_MELEE_WEAPON_STATE_0; + + if (isBusy || isSwingingWeapon) { return true; + } } return false; From 9e4801a18fb00702222758e3b95fbcc895a42df3 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sat, 14 Sep 2024 18:07:50 -0400 Subject: [PATCH 36/52] rework and document all conditions in ShouldEquipMask --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 115 +++++++++++-------- 1 file changed, 70 insertions(+), 45 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index a7fb016c57..cddd469e22 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -10,6 +10,7 @@ extern "C" { #include "variables.h" extern u16 sMasksGivenOnMoonBits[]; extern s16 sPauseMenuVerticalOffset; +void Player_Action_93(Player* player, PlayState* play); } static Vtx* easyMaskEquipVtx = nullptr; @@ -83,64 +84,88 @@ void UpdateEasyMaskEquipVtx(PauseContext* pauseCtx) { // Determine if a mask should be equipped based on various conditions bool ShouldEquipMask(s16 cursorItem) { - const Player* player = GET_PLAYER(gPlayState); - - // Check if player is airborne and attempting to equip a transformation mask - bool isAirborne = !(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND); - bool notInWater = !(Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR && - Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE); - - // Add a check for Zora transformation and a small delay after transformation - if (player->transformation == PLAYER_FORM_ZORA) { - // Ensure the player is fully transformed and not in the middle of an animation - if (player->skelAnime.curFrame < player->skelAnime.endFrame) { - return false; // Still transforming, don't allow mask change + Player* player = GET_PLAYER(gPlayState); + + // Check if the player is underwater + if ((Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && + (Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE)) { + + // Only allow equipping Zora Mask underwater + if (cursorItem != ITEM_MASK_ZORA) { + return false; // Prevent equipping any mask other than Zora Mask } } - if (isAirborne && notInWater) { - if (player->transformation == PLAYER_FORM_HUMAN && IsTransformationMask(static_cast(cursorItem))) { - return true; - } - if (player->transformation != PLAYER_FORM_HUMAN) { - return true; + // Check if the player is climbing (some of these might be redundant, but it works) + if ((player->stateFlags1 & (PLAYER_STATE1_4 | PLAYER_STATE1_4000 | PLAYER_STATE1_40000 | PLAYER_STATE1_200000)) && + IsTransformationMask(static_cast(cursorItem))) { + // Player is climbing and trying to equip a transformation mask, do not allow + return false; + } + + // Prevent equipping transformation masks if interacting with an object + if (IsTransformationMask(static_cast(cursorItem))) { + if ((player->stateFlags2 & PLAYER_STATE2_10) || // Pushing/Pulling + (player->stateFlags1 & PLAYER_STATE1_800) || // Carrying + (player->stateFlags2 & PLAYER_STATE2_1)) { // Grabbing + return false; } } - // Check if underwater and trying to equip non-Zora masks - auto hazard = Player_GetEnvironmentalHazard(gPlayState); - if (hazard >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR && hazard <= PLAYER_ENV_HAZARD_UNDERWATER_FREE && - cursorItem != ITEM_MASK_ZORA) { - return true; + // Check if the player is interacting with Epona + if (player->rideActor != NULL && player->rideActor->id == ACTOR_EN_HORSE && + IsTransformationMask(static_cast(cursorItem))) { + return false; // Prevent equipping transformation masks while interacting with Epona } - // Check if the mask is given on the moon. Need to test this. - if (cursorItem >= ITEM_NUM_SLOTS && (cursorItem - ITEM_NUM_SLOTS) < MASK_NUM_SLOTS && - CHECK_GIVEN_MASK_ON_MOON(cursorItem - ITEM_NUM_SLOTS)) { - return true; + // Check if a minigame timer is active (prevent equipping masks during minigames) + if (gSaveContext.timerStates[TIMER_ID_MINIGAME_1] != TIMER_STATE_OFF || + gSaveContext.timerStates[TIMER_ID_MINIGAME_2] != TIMER_STATE_OFF) { + // If a minigame timer is active, do not equip any mask + return false; } - // Check if player is busy and trying to equip a transformation mask - if (IsTransformationMask(static_cast(cursorItem))) { - const auto& flags1 = player->stateFlags1; - const auto& flags2 = player->stateFlags2; - const auto heldAction = player->heldItemAction; + // Prevent equipping transformation masks when swinging a melee weapon + if (player->meleeWeaponState != PLAYER_MELEE_WEAPON_STATE_0 && IsTransformationMask(static_cast(cursorItem))) { + return false; + } - bool isBusy = flags1 & (PLAYER_STATE1_800 | PLAYER_STATE1_400 | PLAYER_STATE1_20000000) || - flags2 & (PLAYER_STATE2_10 | PLAYER_STATE2_80) || heldAction == PLAYER_IA_BOW || - heldAction == PLAYER_IA_BOW_FIRE || heldAction == PLAYER_IA_BOW_ICE || - heldAction == PLAYER_IA_BOW_LIGHT || heldAction == PLAYER_IA_BOMB || - heldAction == PLAYER_IA_BOMBCHU || heldAction == PLAYER_IA_HOOKSHOT || - heldAction == PLAYER_IA_POWDER_KEG; + // Check if the player is holding a bow (including ice, fire, light variants) or hookshot + if ((player->heldItemAction == PLAYER_IA_BOW || + player->heldItemAction == PLAYER_IA_BOW_FIRE || + player->heldItemAction == PLAYER_IA_BOW_ICE || + player->heldItemAction == PLAYER_IA_BOW_LIGHT || + Player_IsHoldingHookshot(player)) && + IsTransformationMask(static_cast(cursorItem))) { + // Prevent equipping transformation masks while holding bow or hookshot + return false; + } + + // Check if the mask is Fierce Deity Mask and if the player is in an allowed location + if (cursorItem == ITEM_MASK_FIERCE_DEITY) { + if (gPlayState->sceneId != SCENE_MITURIN_BS && + gPlayState->sceneId != SCENE_HAKUGIN_BS && + gPlayState->sceneId != SCENE_SEA_BS && + gPlayState->sceneId != SCENE_INISIE_BS && + gPlayState->sceneId != SCENE_LAST_BS) { + return false; // Prevent equipping Fierce Deity Mask outside allowed locations + } + } - bool isSwingingWeapon = player->meleeWeaponState != PLAYER_MELEE_WEAPON_STATE_0; + // Check if the player is in the air (not on the ground) and if the mask is a transformation mask + if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND) && + (IsTransformationMask(static_cast(cursorItem)) || player->transformation != PLAYER_FORM_HUMAN)) { + return false; // Player is in the air and in a transformation form, cannot equip any mask + } - if (isBusy || isSwingingWeapon) { - return true; - } + // Check if the player is interacting with a Deku Flower (some might be redundant because of the above check, but it works) + if (player->stateFlags3 & (PLAYER_STATE3_200 | PLAYER_STATE3_2000 | PLAYER_STATE3_100)) { + return false; // Prevent equipping any mask while interacting with a Deku Flower } - return false; + //We can add tons more checks here, but I need help to account for all scenarios + + return true; // Allow equipping other masks } // Draw the border around the equipped mask @@ -188,7 +213,7 @@ void ApplyMaskIconGrayscale(PauseContext* pauseCtx, GraphicsContext* gfxCtx) { for (int i = 0; i < MASK_NUM_SLOTS; ++i) { const s16 cursorItem = gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS]; - if (cursorItem != ITEM_NONE && ShouldEquipMask(cursorItem)) { + if (cursorItem != ITEM_NONE && !ShouldEquipMask(cursorItem)) { gDPSetGrayscaleColor(POLY_OPA_DISP++, 109, 109, 109, 255); gSPGrayscale(POLY_OPA_DISP++, true); gSPVertex(POLY_OPA_DISP++, reinterpret_cast(&pauseCtx->maskVtx[i * 4]), 4, 0); // Fixed cast @@ -218,7 +243,7 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { return; } - if (ShouldEquipMask(cursorItem)) { + if (!ShouldEquipMask(cursorItem)) { Audio_PlaySfx(NA_SE_SY_ERROR); return; } From e293409383529791e8458297b4aa3ded340b5a04 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sat, 14 Sep 2024 18:14:55 -0400 Subject: [PATCH 37/52] run clang script --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 40 ++++++++++---------- 1 file changed, 19 insertions(+), 21 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index cddd469e22..d81097d7b6 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -89,10 +89,10 @@ bool ShouldEquipMask(s16 cursorItem) { // Check if the player is underwater if ((Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && (Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE)) { - + // Only allow equipping Zora Mask underwater if (cursorItem != ITEM_MASK_ZORA) { - return false; // Prevent equipping any mask other than Zora Mask + return false; // Prevent equipping any mask other than Zora Mask } } @@ -115,7 +115,7 @@ bool ShouldEquipMask(s16 cursorItem) { // Check if the player is interacting with Epona if (player->rideActor != NULL && player->rideActor->id == ACTOR_EN_HORSE && IsTransformationMask(static_cast(cursorItem))) { - return false; // Prevent equipping transformation masks while interacting with Epona + return false; // Prevent equipping transformation masks while interacting with Epona } // Check if a minigame timer is active (prevent equipping masks during minigames) @@ -125,47 +125,45 @@ bool ShouldEquipMask(s16 cursorItem) { return false; } - // Prevent equipping transformation masks when swinging a melee weapon - if (player->meleeWeaponState != PLAYER_MELEE_WEAPON_STATE_0 && IsTransformationMask(static_cast(cursorItem))) { + // Prevent equipping transformation masks when swinging a melee weapon + if (player->meleeWeaponState != PLAYER_MELEE_WEAPON_STATE_0 && + IsTransformationMask(static_cast(cursorItem))) { return false; } - // Check if the player is holding a bow (including ice, fire, light variants) or hookshot - if ((player->heldItemAction == PLAYER_IA_BOW || - player->heldItemAction == PLAYER_IA_BOW_FIRE || - player->heldItemAction == PLAYER_IA_BOW_ICE || - player->heldItemAction == PLAYER_IA_BOW_LIGHT || + // Check if the player is holding a bow (including ice, fire, light variants) or hookshot + if ((player->heldItemAction == PLAYER_IA_BOW || player->heldItemAction == PLAYER_IA_BOW_FIRE || + player->heldItemAction == PLAYER_IA_BOW_ICE || player->heldItemAction == PLAYER_IA_BOW_LIGHT || Player_IsHoldingHookshot(player)) && IsTransformationMask(static_cast(cursorItem))) { // Prevent equipping transformation masks while holding bow or hookshot return false; - } + } // Check if the mask is Fierce Deity Mask and if the player is in an allowed location if (cursorItem == ITEM_MASK_FIERCE_DEITY) { - if (gPlayState->sceneId != SCENE_MITURIN_BS && - gPlayState->sceneId != SCENE_HAKUGIN_BS && - gPlayState->sceneId != SCENE_SEA_BS && - gPlayState->sceneId != SCENE_INISIE_BS && + if (gPlayState->sceneId != SCENE_MITURIN_BS && gPlayState->sceneId != SCENE_HAKUGIN_BS && + gPlayState->sceneId != SCENE_SEA_BS && gPlayState->sceneId != SCENE_INISIE_BS && gPlayState->sceneId != SCENE_LAST_BS) { - return false; // Prevent equipping Fierce Deity Mask outside allowed locations + return false; // Prevent equipping Fierce Deity Mask outside allowed locations } } // Check if the player is in the air (not on the ground) and if the mask is a transformation mask - if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND) && + if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND) && (IsTransformationMask(static_cast(cursorItem)) || player->transformation != PLAYER_FORM_HUMAN)) { return false; // Player is in the air and in a transformation form, cannot equip any mask } - // Check if the player is interacting with a Deku Flower (some might be redundant because of the above check, but it works) + // Check if the player is interacting with a Deku Flower (some might be redundant because of the above check, but it + // works) if (player->stateFlags3 & (PLAYER_STATE3_200 | PLAYER_STATE3_2000 | PLAYER_STATE3_100)) { - return false; // Prevent equipping any mask while interacting with a Deku Flower + return false; // Prevent equipping any mask while interacting with a Deku Flower } - //We can add tons more checks here, but I need help to account for all scenarios + // We can add tons more checks here, but I need help to account for all scenarios - return true; // Allow equipping other masks + return true; // Allow equipping other masks } // Draw the border around the equipped mask From bf13f5aa8c83116706e9ed2fe785d2a1ac6af5c1 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sat, 14 Sep 2024 20:03:30 -0400 Subject: [PATCH 38/52] added support for unrestricted items/fierce deity anywhere and a check for giant's mask --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index d81097d7b6..ebff76789c 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -10,7 +10,6 @@ extern "C" { #include "variables.h" extern u16 sMasksGivenOnMoonBits[]; extern s16 sPauseMenuVerticalOffset; -void Player_Action_93(Player* player, PlayState* play); } static Vtx* easyMaskEquipVtx = nullptr; @@ -86,6 +85,11 @@ void UpdateEasyMaskEquipVtx(PauseContext* pauseCtx) { bool ShouldEquipMask(s16 cursorItem) { Player* player = GET_PLAYER(gPlayState); + // Allow all masks to be equipped if the Unrestricted Items cheat is enabled + if (CVarGetInteger("gCheats.UnrestrictedItems", 0)) { + return true; + } + // Check if the player is underwater if ((Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && (Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE)) { @@ -142,7 +146,8 @@ bool ShouldEquipMask(s16 cursorItem) { // Check if the mask is Fierce Deity Mask and if the player is in an allowed location if (cursorItem == ITEM_MASK_FIERCE_DEITY) { - if (gPlayState->sceneId != SCENE_MITURIN_BS && gPlayState->sceneId != SCENE_HAKUGIN_BS && + if (!CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0) && + gPlayState->sceneId != SCENE_MITURIN_BS && gPlayState->sceneId != SCENE_HAKUGIN_BS && gPlayState->sceneId != SCENE_SEA_BS && gPlayState->sceneId != SCENE_INISIE_BS && gPlayState->sceneId != SCENE_LAST_BS) { return false; // Prevent equipping Fierce Deity Mask outside allowed locations @@ -161,6 +166,14 @@ bool ShouldEquipMask(s16 cursorItem) { return false; // Prevent equipping any mask while interacting with a Deku Flower } + // Check if the mask is Giant's Mask and if the player is in the allowed location + if (cursorItem == ITEM_MASK_GIANT) { + if (gPlayState->sceneId != SCENE_INISIE_BS) { + return false; // Prevent equipping Giant's Mask outside SCENE_INISIE_BS + } + } + + // We can add tons more checks here, but I need help to account for all scenarios return true; // Allow equipping other masks From 3d9ea77dcb997c86a4ae1a325564afe72b410417 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sat, 14 Sep 2024 20:06:34 -0400 Subject: [PATCH 39/52] clang script --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index ebff76789c..a4903c78cc 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -146,10 +146,9 @@ bool ShouldEquipMask(s16 cursorItem) { // Check if the mask is Fierce Deity Mask and if the player is in an allowed location if (cursorItem == ITEM_MASK_FIERCE_DEITY) { - if (!CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0) && - gPlayState->sceneId != SCENE_MITURIN_BS && gPlayState->sceneId != SCENE_HAKUGIN_BS && - gPlayState->sceneId != SCENE_SEA_BS && gPlayState->sceneId != SCENE_INISIE_BS && - gPlayState->sceneId != SCENE_LAST_BS) { + if (!CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0) && gPlayState->sceneId != SCENE_MITURIN_BS && + gPlayState->sceneId != SCENE_HAKUGIN_BS && gPlayState->sceneId != SCENE_SEA_BS && + gPlayState->sceneId != SCENE_INISIE_BS && gPlayState->sceneId != SCENE_LAST_BS) { return false; // Prevent equipping Fierce Deity Mask outside allowed locations } } @@ -173,7 +172,6 @@ bool ShouldEquipMask(s16 cursorItem) { } } - // We can add tons more checks here, but I need help to account for all scenarios return true; // Allow equipping other masks From 0b3cc7237ca7c623b0301c3601dfe3dbafbc39f2 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 15 Sep 2024 15:27:14 -0400 Subject: [PATCH 40/52] replace held item with first person check, add giant mask checks --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 30 ++++++++++++-------- 1 file changed, 18 insertions(+), 12 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index a4903c78cc..a1de549829 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -135,15 +135,6 @@ bool ShouldEquipMask(s16 cursorItem) { return false; } - // Check if the player is holding a bow (including ice, fire, light variants) or hookshot - if ((player->heldItemAction == PLAYER_IA_BOW || player->heldItemAction == PLAYER_IA_BOW_FIRE || - player->heldItemAction == PLAYER_IA_BOW_ICE || player->heldItemAction == PLAYER_IA_BOW_LIGHT || - Player_IsHoldingHookshot(player)) && - IsTransformationMask(static_cast(cursorItem))) { - // Prevent equipping transformation masks while holding bow or hookshot - return false; - } - // Check if the mask is Fierce Deity Mask and if the player is in an allowed location if (cursorItem == ITEM_MASK_FIERCE_DEITY) { if (!CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0) && gPlayState->sceneId != SCENE_MITURIN_BS && @@ -165,13 +156,28 @@ bool ShouldEquipMask(s16 cursorItem) { return false; // Prevent equipping any mask while interacting with a Deku Flower } - // Check if the mask is Giant's Mask and if the player is in the allowed location + // Check if the mask is Giant's Mask and if the player is in the allowed location with enough magic if (cursorItem == ITEM_MASK_GIANT) { - if (gPlayState->sceneId != SCENE_INISIE_BS) { - return false; // Prevent equipping Giant's Mask outside SCENE_INISIE_BS + if (gPlayState->sceneId != SCENE_INISIE_BS || gSaveContext.save.saveInfo.playerData.magic == 0) { + return false; // Prevent equipping Giant's Mask outside SCENE_INISIE_BS or if magic is depleted } } + // Prevent equipping any mask if Giant's Mask is already equipped, except for Giant's Mask + if (player->currentMask == PLAYER_MASK_GIANT && cursorItem != ITEM_MASK_GIANT) { + return false; + } + + // Prevent equipping Giant's Mask while wearing a transformation mask + if ((player->transformation != PLAYER_FORM_HUMAN) && cursorItem == ITEM_MASK_GIANT) { + return false; // Prevent equipping Giant's Mask while wearing a transformation mask + } + + // Prevent equipping transformation masks in first-person mode + if ((player->stateFlags1 & PLAYER_STATE1_100000) && IsTransformationMask(static_cast(cursorItem))) { + return false; + } + // We can add tons more checks here, but I need help to account for all scenarios return true; // Allow equipping other masks From 8bc183f9dd72c5747e4c2dde445f403322477235 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 15 Sep 2024 15:31:57 -0400 Subject: [PATCH 41/52] one day i won't forget to run the clang script --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index a1de549829..f4cf87f484 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -167,7 +167,7 @@ bool ShouldEquipMask(s16 cursorItem) { if (player->currentMask == PLAYER_MASK_GIANT && cursorItem != ITEM_MASK_GIANT) { return false; } - + // Prevent equipping Giant's Mask while wearing a transformation mask if ((player->transformation != PLAYER_FORM_HUMAN) && cursorItem == ITEM_MASK_GIANT) { return false; // Prevent equipping Giant's Mask while wearing a transformation mask From f8fd7b01420c7afda8e39856b1cd8b4c4816c817 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 15 Sep 2024 16:53:34 -0400 Subject: [PATCH 42/52] support persistent bunny hood, utilize GI_VB_KALEIDO_DISPLAY_ITEM_TEXT --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 12 ++++++++++++ .../kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c | 5 ++--- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index f4cf87f484..fc64cc0677 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -251,6 +251,11 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { if (CHECK_BTN_ALL(pressedButtons, BTN_A)) { s16 cursorItem = pauseCtx->cursorItem[PAUSE_MASK]; if (cursorItem != PAUSE_ITEM_NONE) { + // Check if PersistentBunnyHood is enabled and the mask is Bunny Hood + if (CVarGetInteger("gEnhancements.Masks.PersistentBunnyHood.Enabled", 0) && cursorItem == ITEM_MASK_BUNNY) { + return; // Do nothing if trying to equip Bunny Hood and PersistentBunnyHood is enabled + } + // If sPendingMask is already the cursor item, remove the border and reset sPendingMask if (sPendingMask == cursorItem) { sPendingMask = ITEM_NONE; @@ -358,4 +363,11 @@ void RegisterEasyMaskEquip() { } } }); + + // Register the hook for GI_VB_KALEIDO_DISPLAY_ITEM_TEXT + REGISTER_VB_SHOULD(GI_VB_KALEIDO_DISPLAY_ITEM_TEXT, { + if (EasyMaskEquip_IsEnabled()) { + *should = false; + } + }); } diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c index 38c357edaa..0abfb921b7 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c @@ -670,12 +670,11 @@ void KaleidoScope_UpdateMaskCursor(PlayState* play) { sMaskEquipState = EQUIP_STATE_MOVE_TO_C_BTN; sMaskEquipAnimTimer = 10; Audio_PlaySfx(NA_SE_SY_DECIDE); - // EasyMaskEquip check disables the mask description textbox from appearing when a mask is selected - // with A button. } else if ((pauseCtx->debugEditor == DEBUG_EDITOR_NONE) && (pauseCtx->state == PAUSE_STATE_MAIN) && (pauseCtx->mainState == PAUSE_MAIN_STATE_IDLE) && CHECK_BTN_ALL(input->press.button, BTN_A) && (msgCtx->msgLength == 0)) { - if (GameInteractor_Should(VB_KALEIDO_DISPLAY_ITEM_TEXT, true, &pauseCtx->cursorItem[PAUSE_MASK])) { + if (GameInteractor_Should(GI_VB_KALEIDO_DISPLAY_ITEM_TEXT, true, + &pauseCtx->cursorItem[PAUSE_MASK])) { // Give description on item through a message box pauseCtx->itemDescriptionOn = true; if (pauseCtx->cursorYIndex[PAUSE_MASK] < 2) { From 1054cddf3edc92f340f71b5e66f85fb7ebf1596b Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 15 Sep 2024 17:51:04 -0400 Subject: [PATCH 43/52] switched to gameinteractor hook for mask equip C/D button restriction --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 9 ++++++++- mm/2s2h/GameInteractor/GameInteractor.h | 1 + mm/src/overlays/actors/ovl_player_actor/z_player.c | 5 +---- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index fc64cc0677..eb8bf51cfe 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -364,10 +364,17 @@ void RegisterEasyMaskEquip() { } }); - // Register the hook for GI_VB_KALEIDO_DISPLAY_ITEM_TEXT + // Disable selected item textbox REGISTER_VB_SHOULD(GI_VB_KALEIDO_DISPLAY_ITEM_TEXT, { if (EasyMaskEquip_IsEnabled()) { *should = false; } }); + + // Remove restriction requiring masks be equipped from C or D buttons + REGISTER_VB_SHOULD(GI_VB_ALLOW_EQUIP_MASK, { + if (EasyMaskEquip_IsEnabled()) { + *should = false; + } + }); } diff --git a/mm/2s2h/GameInteractor/GameInteractor.h b/mm/2s2h/GameInteractor/GameInteractor.h index 73c5fb06e4..93c02ed243 100644 --- a/mm/2s2h/GameInteractor/GameInteractor.h +++ b/mm/2s2h/GameInteractor/GameInteractor.h @@ -63,6 +63,7 @@ typedef enum { VB_CLOCK_TOWER_OPENING_CONSIDER_THIS_FIRST_CYCLE, VB_DRAW_SLIME_BODY_ITEM, VB_ZTARGET_SPEED_CHECK, + GI_VB_ALLOW_EQUIP_MASK, } GIVanillaBehavior; typedef enum { diff --git a/mm/src/overlays/actors/ovl_player_actor/z_player.c b/mm/src/overlays/actors/ovl_player_actor/z_player.c index 7756167a1e..760df2c289 100644 --- a/mm/src/overlays/actors/ovl_player_actor/z_player.c +++ b/mm/src/overlays/actors/ovl_player_actor/z_player.c @@ -47,8 +47,6 @@ #include "2s2h/GameInteractor/GameInteractor.h" -#include "2s2h/Enhancements/Masks/EasyMaskEquip.h" - #define THIS ((Player*)thisx) void Player_Init(Actor* thisx, PlayState* play); @@ -3733,8 +3731,7 @@ void Player_ProcessItemButtons(Player* this, PlayState* play) { maskIdMinusOne = this->currentMask - 1; } - // EasyMaskEquip check allows equipping a mask without it being on a C button or D button - if (!EasyMaskEquip_IsEnabled()) { + if (GameInteractor_Should(GI_VB_ALLOW_EQUIP_MASK, false, NULL)) { Player_UseItem(play, this, Player_MaskIdToItemId(maskIdMinusOne)); return; } From e64c7ae0329b85b3511ecf8aa44277a08cf86090 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 15 Sep 2024 20:46:38 -0400 Subject: [PATCH 44/52] fix underwater check, fix climbing check --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index eb8bf51cfe..60fa5605b9 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -94,14 +94,17 @@ bool ShouldEquipMask(s16 cursorItem) { if ((Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && (Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE)) { - // Only allow equipping Zora Mask underwater - if (cursorItem != ITEM_MASK_ZORA) { + // Allow equipping Zora Mask underwater + if (cursorItem == ITEM_MASK_ZORA) { + return true; // Explicitly allow equipping the Zora Mask + } else { return false; // Prevent equipping any mask other than Zora Mask } } // Check if the player is climbing (some of these might be redundant, but it works) - if ((player->stateFlags1 & (PLAYER_STATE1_4 | PLAYER_STATE1_4000 | PLAYER_STATE1_40000 | PLAYER_STATE1_200000)) && + if ((player->stateFlags1 & + (PLAYER_STATE1_4 | PLAYER_STATE1_4000 | PLAYER_STATE1_40000 | PLAYER_STATE1_200000 | PLAYER_STATE1_2000)) && IsTransformationMask(static_cast(cursorItem))) { // Player is climbing and trying to equip a transformation mask, do not allow return false; From 2f7d4226de4f04ffc17cbe5cfd8d2fb0b8eb2e23 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Thu, 26 Sep 2024 13:08:45 -0400 Subject: [PATCH 45/52] remove null arg --- mm/src/overlays/actors/ovl_player_actor/z_player.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mm/src/overlays/actors/ovl_player_actor/z_player.c b/mm/src/overlays/actors/ovl_player_actor/z_player.c index 760df2c289..9f657c97e6 100644 --- a/mm/src/overlays/actors/ovl_player_actor/z_player.c +++ b/mm/src/overlays/actors/ovl_player_actor/z_player.c @@ -3731,7 +3731,7 @@ void Player_ProcessItemButtons(Player* this, PlayState* play) { maskIdMinusOne = this->currentMask - 1; } - if (GameInteractor_Should(GI_VB_ALLOW_EQUIP_MASK, false, NULL)) { + if (GameInteractor_Should(GI_VB_ALLOW_EQUIP_MASK, false)) { Player_UseItem(play, this, Player_MaskIdToItemId(maskIdMinusOne)); return; } From 722455c67e5e1cb72a7d9ced3ff37adbad954cef Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 6 Oct 2024 18:09:56 -0400 Subject: [PATCH 46/52] some changes --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 6 +++--- mm/2s2h/Enhancements/Masks/EasyMaskEquip.h | 15 --------------- mm/2s2h/Enhancements/Masks/Masks.h | 1 + mm/src/code/z_player_lib.c | 2 +- .../ovl_kaleido_scope/z_kaleido_mask.c | 2 +- .../ovl_kaleido_scope/z_kaleido_scope_NES.c | 1 - 6 files changed, 6 insertions(+), 21 deletions(-) delete mode 100644 mm/2s2h/Enhancements/Masks/EasyMaskEquip.h diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index 60fa5605b9..aedcc7d219 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -1,6 +1,6 @@ -#include "EasyMaskEquip.h" +#include "2s2h/Enhancements/Enhancements.h" #include -#include "Enhancements/GameInteractor/GameInteractor.h" +#include "2s2h/GameInteractor/GameInteractor.h" #include "Enhancements/FrameInterpolation/FrameInterpolation.h" extern "C" { @@ -368,7 +368,7 @@ void RegisterEasyMaskEquip() { }); // Disable selected item textbox - REGISTER_VB_SHOULD(GI_VB_KALEIDO_DISPLAY_ITEM_TEXT, { + REGISTER_VB_SHOULD(VB_KALEIDO_DISPLAY_ITEM_TEXT, { if (EasyMaskEquip_IsEnabled()) { *should = false; } diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.h b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.h deleted file mode 100644 index 72478bcf3c..0000000000 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef EASY_MASK_EQUIP_H -#define EASY_MASK_EQUIP_H - -#ifdef __cplusplus -extern "C" { -#endif - -bool EasyMaskEquip_IsEnabled(void); -void RegisterEasyMaskEquip(void); - -#ifdef __cplusplus -} -#endif - -#endif // EASY_MASK_EQUIP_H \ No newline at end of file diff --git a/mm/2s2h/Enhancements/Masks/Masks.h b/mm/2s2h/Enhancements/Masks/Masks.h index f65949ef07..1231534c75 100644 --- a/mm/2s2h/Enhancements/Masks/Masks.h +++ b/mm/2s2h/Enhancements/Masks/Masks.h @@ -7,5 +7,6 @@ void RegisterFierceDeityAnywhere(); void RegisterNoBlastMaskCooldown(); void RegisterPersistentMasks(); void UpdatePersistentMasksState(); +void RegisterEasyMaskEquip(); #endif // MASKS_H diff --git a/mm/src/code/z_player_lib.c b/mm/src/code/z_player_lib.c index 04b2652277..b7a7f31ad6 100644 --- a/mm/src/code/z_player_lib.c +++ b/mm/src/code/z_player_lib.c @@ -43,7 +43,7 @@ // Assets for other actors #include "overlays/actors/ovl_En_Arrow/z_en_arrow.h" -#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" +#include "2s2h/GameInteractor/GameInteractor.h" #include "2s2h/BenPort.h" #include "2s2h/GameInteractor/GameInteractor.h" diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c index 0abfb921b7..ac8675bc5e 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c @@ -673,7 +673,7 @@ void KaleidoScope_UpdateMaskCursor(PlayState* play) { } else if ((pauseCtx->debugEditor == DEBUG_EDITOR_NONE) && (pauseCtx->state == PAUSE_STATE_MAIN) && (pauseCtx->mainState == PAUSE_MAIN_STATE_IDLE) && CHECK_BTN_ALL(input->press.button, BTN_A) && (msgCtx->msgLength == 0)) { - if (GameInteractor_Should(GI_VB_KALEIDO_DISPLAY_ITEM_TEXT, true, + if (GameInteractor_Should(VB_KALEIDO_DISPLAY_ITEM_TEXT, true, &pauseCtx->cursorItem[PAUSE_MASK])) { // Give description on item through a message box pauseCtx->itemDescriptionOn = true; diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c index a4c21a5cdb..06644720ca 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c @@ -19,7 +19,6 @@ #include "archives/map_name_static/map_name_static.h" #include "2s2h/Enhancements/FrameInterpolation/FrameInterpolation.h" #include "2s2h/Enhancements/Saving/SavingEnhancements.h" -#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" #include "2s2h_assets.h" From 00a19ae81d3eeff55c518c4062b9b55c5e7e64d8 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 6 Oct 2024 18:22:41 -0400 Subject: [PATCH 47/52] some more changes --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.h | 15 --------------- mm/include/macros.h | 3 --- .../ovl_kaleido_scope/z_kaleido_mask.c | 5 +++-- .../ovl_kaleido_scope/z_kaleido_scope_NES.c | 1 - 4 files changed, 3 insertions(+), 21 deletions(-) delete mode 100644 mm/2s2h/Enhancements/Masks/EasyMaskEquip.h diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.h b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.h deleted file mode 100644 index 72478bcf3c..0000000000 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.h +++ /dev/null @@ -1,15 +0,0 @@ -#ifndef EASY_MASK_EQUIP_H -#define EASY_MASK_EQUIP_H - -#ifdef __cplusplus -extern "C" { -#endif - -bool EasyMaskEquip_IsEnabled(void); -void RegisterEasyMaskEquip(void); - -#ifdef __cplusplus -} -#endif - -#endif // EASY_MASK_EQUIP_H \ No newline at end of file diff --git a/mm/include/macros.h b/mm/include/macros.h index 6cef66337e..456e5df509 100644 --- a/mm/include/macros.h +++ b/mm/include/macros.h @@ -115,9 +115,6 @@ #define D_0F000000_TO_SEGMENTED (0x0F000000 | 1) // Used to compare animations. The statically linked global animations can end up with different pointer addresses if an animation gets set from one file and then compared against the static animation in another #define BEN_ANIM_EQUAL(anim1, anim2) ((anim1 == NULL) ? ((anim2 == NULL) ? true : false) : ((anim2 == NULL) ? false : strcmp(anim1, anim2) == 0)) -// Macro to check if a given mask has been given on the moon -#define CHECK_GIVEN_MASK_ON_MOON(maskIndex) \ - (gSaveContext.masksGivenOnMoon[sMasksGivenOnMoonBits[maskIndex] >> 8] & (u8)sMasksGivenOnMoonBits[maskIndex]) // #endregion #endif // MACROS_H diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c index ac8675bc5e..b6ebdcbbdf 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c @@ -158,6 +158,8 @@ u8 gMaskPlayerFormSlotRestrictions[PLAYER_FORM_MAX][MASK_NUM_SLOTS] = { #define SET_MOON_MASK_BIT(masksGivenOnMoonIndex, masksGivenOnMoonFlag) \ ((masksGivenOnMoonIndex) << 8 | (masksGivenOnMoonFlag)) +#define CHECK_GIVEN_MASK_ON_MOON(maskIndex) \ + (gSaveContext.masksGivenOnMoon[sMasksGivenOnMoonBits[maskIndex] >> 8] & (u8)sMasksGivenOnMoonBits[maskIndex]) u16 sMasksGivenOnMoonBits[] = { SET_MOON_MASK_BIT(1, 0x1), // SLOT_MASK_POSTMAN @@ -673,8 +675,7 @@ void KaleidoScope_UpdateMaskCursor(PlayState* play) { } else if ((pauseCtx->debugEditor == DEBUG_EDITOR_NONE) && (pauseCtx->state == PAUSE_STATE_MAIN) && (pauseCtx->mainState == PAUSE_MAIN_STATE_IDLE) && CHECK_BTN_ALL(input->press.button, BTN_A) && (msgCtx->msgLength == 0)) { - if (GameInteractor_Should(VB_KALEIDO_DISPLAY_ITEM_TEXT, true, - &pauseCtx->cursorItem[PAUSE_MASK])) { + if (GameInteractor_Should(VB_KALEIDO_DISPLAY_ITEM_TEXT, true, &pauseCtx->cursorItem[PAUSE_MASK])) { // Give description on item through a message box pauseCtx->itemDescriptionOn = true; if (pauseCtx->cursorYIndex[PAUSE_MASK] < 2) { diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c index a4c21a5cdb..06644720ca 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope_NES.c @@ -19,7 +19,6 @@ #include "archives/map_name_static/map_name_static.h" #include "2s2h/Enhancements/FrameInterpolation/FrameInterpolation.h" #include "2s2h/Enhancements/Saving/SavingEnhancements.h" -#include "2s2h/Enhancements/GameInteractor/GameInteractor.h" #include "2s2h_assets.h" From 97e2876045dfa835765258d4b039fc9e64e2e440 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 6 Oct 2024 18:29:14 -0400 Subject: [PATCH 48/52] bump lus --- libultraship | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/libultraship b/libultraship index 7a88bfc08c..4160fa0aa0 160000 --- a/libultraship +++ b/libultraship @@ -1 +1 @@ -Subproject commit 7a88bfc08cbe44fd682b8f6f66b9d32d44b699bc +Subproject commit 4160fa0aa07d3946cbed5e397c54e6260f67dc5a From 35335545e786c3bf49e03cfbc97b5fba562b4732 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Sun, 6 Oct 2024 19:54:52 -0400 Subject: [PATCH 49/52] add checkbox to modern menu --- mm/2s2h/BenGui/SearchableMenuItems.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/mm/2s2h/BenGui/SearchableMenuItems.h b/mm/2s2h/BenGui/SearchableMenuItems.h index 682b1fd1e6..995bf860a7 100644 --- a/mm/2s2h/BenGui/SearchableMenuItems.h +++ b/mm/2s2h/BenGui/SearchableMenuItems.h @@ -1086,7 +1086,9 @@ void AddEnhancements() { {}, [](widgetInfo& info) { UpdatePersistentMasksState(); } }, { "No Blast Mask Cooldown", "gEnhancements.Masks.NoBlastMaskCooldown", - "Eliminates the Cooldown between Blast Mask usage.", WIDGET_CVAR_CHECKBOX } }, + "Eliminates the Cooldown between Blast Mask usage.", WIDGET_CVAR_CHECKBOX }, + { "Easy Mask Equip", "gEnhancements.Masks.EasyMaskEquip", + "Allows you to equip masks directly from the pause menu by pressing A.", WIDGET_CVAR_CHECKBOX } }, // Song Enhancements { { .widgetName = "Ocarina", .widgetType = WIDGET_SEPARATOR_TEXT }, { "Enable Sun's Song", "gEnhancements.Songs.EnableSunsSong", From 1bebd80b2412be9b8ba2ea19275d28c5627a557d Mon Sep 17 00:00:00 2001 From: mckinlee Date: Thu, 10 Oct 2024 18:49:20 -0400 Subject: [PATCH 50/52] simplify some things Co-Authored-By: Garrett Cox --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 51 ++++++++++--------- mm/2s2h/GameInteractor/GameInteractor.h | 1 + .../ovl_kaleido_scope/z_kaleido_mask.c | 13 +++-- 3 files changed, 36 insertions(+), 29 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index 4d122eb2ee..01e992a659 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -23,7 +23,7 @@ constexpr std::array TransformationMasks = { ITEM_MASK_DEKU, ITEM_MAS // Helper function to check if a mask is a transformation mask bool IsTransformationMask(ItemId mask) { - return std::find(TransformationMasks.begin(), TransformationMasks.end(), mask) != TransformationMasks.end(); + return std::binary_search(TransformationMasks.begin(), TransformationMasks.end(), mask); } // Check if EasyMaskEquip is enabled @@ -225,19 +225,22 @@ void AllocateEasyMaskEquipVtx(GraphicsContext* gfxCtx) { } } -// Apply grayscale to mask icons that shouldn't be equipped -void ApplyMaskIconGrayscale(PauseContext* pauseCtx, GraphicsContext* gfxCtx) { +// Draw the mask item, applying grayscale if it should not be equipped +void RenderMaskItem(PauseContext* pauseCtx, u16 itemId, s16 j) { + GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; OPEN_DISPS(gfxCtx); - for (int i = 0; i < MASK_NUM_SLOTS; ++i) { - const s16 cursorItem = gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS]; - if (cursorItem != ITEM_NONE && !ShouldEquipMask(cursorItem)) { - gDPSetGrayscaleColor(POLY_OPA_DISP++, 109, 109, 109, 255); - gSPGrayscale(POLY_OPA_DISP++, true); - gSPVertex(POLY_OPA_DISP++, reinterpret_cast(&pauseCtx->maskVtx[i * 4]), 4, 0); // Fixed cast - KaleidoScope_DrawTexQuadRGBA32(gfxCtx, gItemIcons[cursorItem], 32, 32, 0); - gSPGrayscale(POLY_OPA_DISP++, false); - } + bool shouldGrayscale = !ShouldEquipMask(itemId); + if (shouldGrayscale) { + gDPSetGrayscaleColor(POLY_OPA_DISP++, 109, 109, 109, 255); + gSPGrayscale(POLY_OPA_DISP++, true); + } + + gSPVertex(POLY_OPA_DISP++, reinterpret_cast(&pauseCtx->maskVtx[j]), 4, 0); + KaleidoScope_DrawTexQuadRGBA32(gfxCtx, gItemIcons[itemId], 32, 32, 0); + + if (shouldGrayscale) { + gSPGrayscale(POLY_OPA_DISP++, false); } CLOSE_DISPS(gfxCtx); @@ -254,12 +257,12 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { if (CHECK_BTN_ALL(pressedButtons, BTN_A)) { s16 cursorItem = pauseCtx->cursorItem[PAUSE_MASK]; if (cursorItem != PAUSE_ITEM_NONE) { - // Check if PersistentBunnyHood is enabled and the mask is Bunny Hood + // Handle Persistent Bunny Hood early exit if (CVarGetInteger("gEnhancements.Masks.PersistentBunnyHood.Enabled", 0) && cursorItem == ITEM_MASK_BUNNY) { - return; // Do nothing if trying to equip Bunny Hood and PersistentBunnyHood is enabled + return; } - // If sPendingMask is already the cursor item, remove the border and reset sPendingMask + // Toggle mask selection if (sPendingMask == cursorItem) { sPendingMask = ITEM_NONE; sIsTransforming = false; @@ -294,11 +297,14 @@ void RegisterEasyMaskEquip() { } }); - // Apply grayscale to mask icons after drawing the mask page - gameInteractor->RegisterGameHookForID( - PAUSE_MASK, [](PauseContext* pauseCtx, u16) { - if (EasyMaskEquip_IsEnabled()) { - ApplyMaskIconGrayscale(pauseCtx, gPlayState->state.gfxCtx); + // Override mask item rendering to apply grayscale if necessary + gameInteractor->RegisterGameHook( + [](GIVanillaBehavior flag, bool* should, va_list args) { + if (flag == VB_DRAW_MASK_ITEM && EasyMaskEquip_IsEnabled()) { + u16* itemId = va_arg(args, u16*); + s16* j = va_arg(args, s16*); + *should = false; + RenderMaskItem(&gPlayState->pauseCtx, *itemId, *j); } }); @@ -306,12 +312,9 @@ void RegisterEasyMaskEquip() { gameInteractor->RegisterGameHookForID( PAUSE_MASK, [](PauseContext* pauseCtx, u16) { if (EasyMaskEquip_IsEnabled() && pauseCtx->pageIndex == PAUSE_MASK) { - GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; - OPEN_DISPS(gfxCtx); - AllocateEasyMaskEquipVtx(gfxCtx); + AllocateEasyMaskEquipVtx(gPlayState->state.gfxCtx); UpdateEasyMaskEquipVtx(pauseCtx); DrawEasyMaskEquipBorder(pauseCtx); - CLOSE_DISPS(gfxCtx); } }); diff --git a/mm/2s2h/GameInteractor/GameInteractor.h b/mm/2s2h/GameInteractor/GameInteractor.h index 17807c9d9d..f471fb9a65 100644 --- a/mm/2s2h/GameInteractor/GameInteractor.h +++ b/mm/2s2h/GameInteractor/GameInteractor.h @@ -66,6 +66,7 @@ typedef enum { VB_THIEF_BIRD_STEAL, VB_PLAY_CREMIA_HUG_CUTSCENE, VB_ALLOW_EQUIP_MASK, + VB_DRAW_MASK_ITEM, } GIVanillaBehavior; typedef enum { diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c index b6ebdcbbdf..b70098ce7c 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_mask.c @@ -275,11 +275,14 @@ void KaleidoScope_DrawMaskSelect(PlayState* play) { pauseCtx->maskVtx[j + 0].v.ob[1] - 32; } } - - gSPVertex(POLY_OPA_DISP++, &pauseCtx->maskVtx[j + 0], 4, 0); - KaleidoScope_DrawTexQuadRGBA32( - play->state.gfxCtx, - gItemIcons[((void)0, gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS])], 32, 32, 0); + u16 itemId = gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS]; + if (GameInteractor_Should(VB_DRAW_MASK_ITEM, true, &itemId, &j)) { + gSPVertex(POLY_OPA_DISP++, &pauseCtx->maskVtx[j + 0], 4, 0); + KaleidoScope_DrawTexQuadRGBA32( + play->state.gfxCtx, + gItemIcons[((void)0, gSaveContext.save.saveInfo.inventory.items[i + ITEM_NUM_SLOTS])], 32, 32, + 0); + } } } } From e7787559fb7a6879cf8fede005c49ba30e3c68a0 Mon Sep 17 00:00:00 2001 From: mckinlee Date: Thu, 10 Oct 2024 18:59:31 -0400 Subject: [PATCH 51/52] remove duplicate header from merge --- mm/src/code/z_player_lib.c | 1 - 1 file changed, 1 deletion(-) diff --git a/mm/src/code/z_player_lib.c b/mm/src/code/z_player_lib.c index b7a7f31ad6..9f624b93d2 100644 --- a/mm/src/code/z_player_lib.c +++ b/mm/src/code/z_player_lib.c @@ -43,7 +43,6 @@ // Assets for other actors #include "overlays/actors/ovl_En_Arrow/z_en_arrow.h" -#include "2s2h/GameInteractor/GameInteractor.h" #include "2s2h/BenPort.h" #include "2s2h/GameInteractor/GameInteractor.h" From 5a35bac09e09fa1fbfd0441df5c856b0bccf99df Mon Sep 17 00:00:00 2001 From: mckinlee Date: Wed, 16 Oct 2024 20:40:30 -0400 Subject: [PATCH 52/52] bunch of fixes --- mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp | 207 +++++++++++++------ 1 file changed, 143 insertions(+), 64 deletions(-) diff --git a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp index 01e992a659..c3c624207b 100644 --- a/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp +++ b/mm/2s2h/Enhancements/Masks/EasyMaskEquip.cpp @@ -16,31 +16,37 @@ static Vtx* easyMaskEquipVtx = nullptr; static s16 sPendingMask = ITEM_NONE; static s16 sLastEquippedMask = ITEM_NONE; static bool sIsTransforming = false; +static bool sShouldUnequipMask = false; // Define transformation masks for easy reference constexpr std::array TransformationMasks = { ITEM_MASK_DEKU, ITEM_MASK_GORON, ITEM_MASK_ZORA, ITEM_MASK_FIERCE_DEITY, ITEM_MASK_GIANT }; -// Helper function to check if a mask is a transformation mask +// Helper function to check if a given mask is a transformation mask bool IsTransformationMask(ItemId mask) { return std::binary_search(TransformationMasks.begin(), TransformationMasks.end(), mask); } -// Check if EasyMaskEquip is enabled +// Check if the Easy Mask Equip feature is enabled bool EasyMaskEquip_IsEnabled() { return CVarGetInteger("gEnhancements.Masks.EasyMaskEquip", 0) && gPlayState->pauseCtx.debugEditor == DEBUG_EDITOR_NONE; } -// Get the equipped mask slot, prioritizing pending masks +// Get the slot index of the currently equipped mask, prioritizing any pending mask s16 GetEquippedMaskSlot() { + if (sShouldUnequipMask) { + // We intend to unequip the mask, so return -1 to indicate no mask + return -1; + } + s16 maskToCheck = (sPendingMask != ITEM_NONE && !sIsTransforming) ? sPendingMask : Player_GetCurMaskItemId(gPlayState); maskToCheck = (maskToCheck == ITEM_NONE || sIsTransforming) ? sLastEquippedMask : maskToCheck; auto& items = gSaveContext.save.saveInfo.inventory.items; - // Search for pending mask first + // Search for the pending mask first if (sPendingMask != ITEM_NONE) { for (s16 i = 0; i < MASK_NUM_SLOTS; ++i) { if (items[i + ITEM_NUM_SLOTS] == sPendingMask) { @@ -49,17 +55,17 @@ s16 GetEquippedMaskSlot() { } } - // Then search for the equipped mask + // Then search for the currently equipped mask for (s16 i = 0; i < MASK_NUM_SLOTS; ++i) { if (items[i + ITEM_NUM_SLOTS] == maskToCheck) { return i; } } - return -1; + return -1; // Mask not found } -// Update vertex positions based on the equipped mask slot +// Update the vertex positions of the mask equip border based on the equipped mask slot void UpdateEasyMaskEquipVtx(PauseContext* pauseCtx) { s16 slot = GetEquippedMaskSlot(); if (slot == -1) @@ -81,10 +87,14 @@ void UpdateEasyMaskEquipVtx(PauseContext* pauseCtx) { } } -// Determine if a mask should be equipped based on various conditions +// Determine if a mask can be equipped based on various conditions bool ShouldEquipMask(s16 cursorItem) { Player* player = GET_PLAYER(gPlayState); + if (CVarGetInteger("gEnhancements.Masks.PersistentBunnyHood.Enabled", 0) && cursorItem == ITEM_MASK_BUNNY) { + return true; // Always allow equipping the Bunny Hood if the persistent option is enabled + } + // Allow all masks to be equipped if the Unrestricted Items cheat is enabled if (CVarGetInteger("gCheats.UnrestrictedItems", 0)) { return true; @@ -94,23 +104,22 @@ bool ShouldEquipMask(s16 cursorItem) { if ((Player_GetEnvironmentalHazard(gPlayState) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && (Player_GetEnvironmentalHazard(gPlayState) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE)) { - // Allow equipping Zora Mask underwater + // Only allow equipping the Zora Mask underwater if (cursorItem == ITEM_MASK_ZORA) { - return true; // Explicitly allow equipping the Zora Mask + return true; } else { - return false; // Prevent equipping any mask other than Zora Mask + return false; } } - // Check if the player is climbing (some of these might be redundant, but it works) + // Prevent equipping transformation masks while climbing if ((player->stateFlags1 & (PLAYER_STATE1_4 | PLAYER_STATE1_4000 | PLAYER_STATE1_40000 | PLAYER_STATE1_200000 | PLAYER_STATE1_2000)) && IsTransformationMask(static_cast(cursorItem))) { - // Player is climbing and trying to equip a transformation mask, do not allow return false; } - // Prevent equipping transformation masks if interacting with an object + // Prevent equipping transformation masks while interacting with objects if (IsTransformationMask(static_cast(cursorItem))) { if ((player->stateFlags2 & PLAYER_STATE2_10) || // Pushing/Pulling (player->stateFlags1 & PLAYER_STATE1_800) || // Carrying @@ -119,61 +128,59 @@ bool ShouldEquipMask(s16 cursorItem) { } } - // Check if the player is interacting with Epona + // Prevent equipping transformation masks while riding Epona if (player->rideActor != NULL && player->rideActor->id == ACTOR_EN_HORSE && IsTransformationMask(static_cast(cursorItem))) { - return false; // Prevent equipping transformation masks while interacting with Epona + return false; } - // Check if a minigame timer is active (prevent equipping masks during minigames) + // Prevent equipping masks during minigames with active timers if (gSaveContext.timerStates[TIMER_ID_MINIGAME_1] != TIMER_STATE_OFF || gSaveContext.timerStates[TIMER_ID_MINIGAME_2] != TIMER_STATE_OFF) { - // If a minigame timer is active, do not equip any mask return false; } - // Prevent equipping transformation masks when swinging a melee weapon + // Prevent equipping transformation masks while swinging a melee weapon if (player->meleeWeaponState != PLAYER_MELEE_WEAPON_STATE_0 && IsTransformationMask(static_cast(cursorItem))) { return false; } - // Check if the mask is Fierce Deity Mask and if the player is in an allowed location + // Prevent equipping Fierce Deity Mask outside allowed locations if the enhancement is not enabled if (cursorItem == ITEM_MASK_FIERCE_DEITY) { if (!CVarGetInteger("gEnhancements.Masks.FierceDeitysAnywhere", 0) && gPlayState->sceneId != SCENE_MITURIN_BS && gPlayState->sceneId != SCENE_HAKUGIN_BS && gPlayState->sceneId != SCENE_SEA_BS && gPlayState->sceneId != SCENE_INISIE_BS && gPlayState->sceneId != SCENE_LAST_BS) { - return false; // Prevent equipping Fierce Deity Mask outside allowed locations + return false; } } - // Check if the player is in the air (not on the ground) and if the mask is a transformation mask + // Prevent equipping transformation masks while in the air (not on the ground) if (!(player->actor.bgCheckFlags & BGCHECKFLAG_GROUND) && (IsTransformationMask(static_cast(cursorItem)) || player->transformation != PLAYER_FORM_HUMAN)) { - return false; // Player is in the air and in a transformation form, cannot equip any mask + return false; } - // Check if the player is interacting with a Deku Flower (some might be redundant because of the above check, but it - // works) + // Prevent equipping any mask while interacting with a Deku Flower if (player->stateFlags3 & (PLAYER_STATE3_200 | PLAYER_STATE3_2000 | PLAYER_STATE3_100)) { - return false; // Prevent equipping any mask while interacting with a Deku Flower + return false; } - // Check if the mask is Giant's Mask and if the player is in the allowed location with enough magic + // Prevent equipping Giant's Mask outside of the allowed scene or without magic if (cursorItem == ITEM_MASK_GIANT) { if (gPlayState->sceneId != SCENE_INISIE_BS || gSaveContext.save.saveInfo.playerData.magic == 0) { - return false; // Prevent equipping Giant's Mask outside SCENE_INISIE_BS or if magic is depleted + return false; } } - // Prevent equipping any mask if Giant's Mask is already equipped, except for Giant's Mask + // Prevent equipping any mask other than Giant's Mask if already wearing the Giant's Mask if (player->currentMask == PLAYER_MASK_GIANT && cursorItem != ITEM_MASK_GIANT) { return false; } - // Prevent equipping Giant's Mask while wearing a transformation mask + // Prevent equipping Giant's Mask while wearing another transformation mask if ((player->transformation != PLAYER_FORM_HUMAN) && cursorItem == ITEM_MASK_GIANT) { - return false; // Prevent equipping Giant's Mask while wearing a transformation mask + return false; } // Prevent equipping transformation masks in first-person mode @@ -181,17 +188,17 @@ bool ShouldEquipMask(s16 cursorItem) { return false; } - // We can add tons more checks here, but I need help to account for all scenarios + // Additional checks can be added here to account for more scenarios - return true; // Allow equipping other masks + return true; // Allow equipping the mask } -// Draw the border around the equipped mask +// Draw the border around the equipped mask in the pause menu void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { s16 slot = GetEquippedMaskSlot(); if (slot == -1 || gSaveContext.save.saveInfo.inventory.items[slot + ITEM_NUM_SLOTS] == ITEM_NONE || Player_GetCurMaskItemId(gPlayState) == sPendingMask) { - return; + return; // No mask equipped or pending } GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; @@ -199,12 +206,13 @@ void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { UpdateEasyMaskEquipVtx(pauseCtx); + // Set up drawing parameters gDPPipeSync(POLY_OPA_DISP++); - gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 255, 255, 255, pauseCtx->alpha); + gDPSetPrimColor(POLY_OPA_DISP++, 0, 0, 230, 190, 255, pauseCtx->alpha); // Lavender color gDPSetCombineLERP(POLY_OPA_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); gDPSetEnvColor(POLY_OPA_DISP++, 0, 0, 0, 0); - gSPVertex(POLY_OPA_DISP++, reinterpret_cast(easyMaskEquipVtx), 4, 0); // Fixed cast + gSPVertex(POLY_OPA_DISP++, reinterpret_cast(easyMaskEquipVtx), 4, 0); gDPLoadTextureBlock(POLY_OPA_DISP++, gEquippedItemOutlineTex, G_IM_FMT_IA, G_IM_SIZ_8b, 32, 32, 0, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); @@ -213,7 +221,7 @@ void DrawEasyMaskEquipBorder(PauseContext* pauseCtx) { CLOSE_DISPS(gfxCtx); } -// Allocate memory for the vertex buffer +// Allocate memory for the vertex buffer used in drawing the equip border void AllocateEasyMaskEquipVtx(GraphicsContext* gfxCtx) { easyMaskEquipVtx = static_cast(GRAPH_ALLOC(gfxCtx, 4 * sizeof(Vtx))); for (int i = 0; i < 4; ++i) { @@ -221,11 +229,11 @@ void AllocateEasyMaskEquipVtx(GraphicsContext* gfxCtx) { easyMaskEquipVtx[i].v.flag = 0; easyMaskEquipVtx[i].v.tc[0] = (i & 1) ? 32 << 5 : 0; easyMaskEquipVtx[i].v.tc[1] = (i & 2) ? 32 << 5 : 0; - std::fill(std::begin(easyMaskEquipVtx[i].v.cn), std::end(easyMaskEquipVtx[i].v.cn), static_cast(255)); + std::fill(std::begin(easyMaskEquipVtx[i].v.cn), std::end(easyMaskEquipVtx[i].v.cn), 255); } } -// Draw the mask item, applying grayscale if it should not be equipped +// Render the mask item in the pause menu, applying grayscale if it cannot be equipped void RenderMaskItem(PauseContext* pauseCtx, u16 itemId, s16 j) { GraphicsContext* gfxCtx = gPlayState->state.gfxCtx; OPEN_DISPS(gfxCtx); @@ -246,26 +254,57 @@ void RenderMaskItem(PauseContext* pauseCtx, u16 itemId, s16 j) { CLOSE_DISPS(gfxCtx); } -// Handle equipping masks based on player input +// Handle equipping masks based on player input in the pause menu void HandleEasyMaskEquip(PauseContext* pauseCtx) { if (pauseCtx->state != PAUSE_STATE_MAIN || pauseCtx->mainState != PAUSE_MAIN_STATE_IDLE) return; - // Directly access the pressed buttons without using a Controller* variable u16 pressedButtons = CONTROLLER1(&gPlayState->state)->press.button; if (CHECK_BTN_ALL(pressedButtons, BTN_A)) { s16 cursorItem = pauseCtx->cursorItem[PAUSE_MASK]; if (cursorItem != PAUSE_ITEM_NONE) { - // Handle Persistent Bunny Hood early exit + // Handle early exit for Persistent Bunny Hood if (CVarGetInteger("gEnhancements.Masks.PersistentBunnyHood.Enabled", 0) && cursorItem == ITEM_MASK_BUNNY) { return; } - // Toggle mask selection - if (sPendingMask == cursorItem) { + Player* player = GET_PLAYER(gPlayState); + bool isTransformation = IsTransformationMask(static_cast(cursorItem)); + bool isCurrentlyTransformed = (player->transformation != PLAYER_FORM_HUMAN); + s16 currentMaskItemId = Player_GetCurMaskItemId(gPlayState); + + if (cursorItem == currentMaskItemId) { + if (sPendingMask == ITEM_NONE && !sShouldUnequipMask) { + // Currently wearing the mask, no pending action + // Initiate unequip action + sShouldUnequipMask = true; + Audio_PlaySfx(NA_SE_SY_CANCEL); // Play the cancel sound + } else if (sShouldUnequipMask) { + // Cancel the unequip action + sShouldUnequipMask = false; + Audio_PlaySfx(NA_SE_SY_DECIDE); // Play the select sound + } else if (sPendingMask != ITEM_NONE) { + // Cancel pending mask equip and keep current mask + sPendingMask = ITEM_NONE; + sIsTransforming = false; + sShouldUnequipMask = false; + Audio_PlaySfx(NA_SE_SY_DECIDE); // Play the select sound + } + return; + } + + if (cursorItem == sPendingMask) { + // Mask is already pending, cancel pending equip sPendingMask = ITEM_NONE; sIsTransforming = false; + Audio_PlaySfx(NA_SE_SY_CANCEL); + + if (currentMaskItemId != ITEM_NONE) { + // Set flag to unequip current mask + sShouldUnequipMask = true; + } + return; } @@ -274,23 +313,37 @@ void HandleEasyMaskEquip(PauseContext* pauseCtx) { return; } - sPendingMask = cursorItem; - sIsTransforming = IsTransformationMask(static_cast(cursorItem)); - UpdateEasyMaskEquipVtx(pauseCtx); - Audio_PlaySfx(NA_SE_SY_DECIDE); + // Determine if we can equip the mask immediately + if (!isTransformation && !isCurrentlyTransformed) { + // Regular mask and player is human, equip immediately + Player_UseItem(gPlayState, player, static_cast(cursorItem)); + sLastEquippedMask = cursorItem; + sPendingMask = ITEM_NONE; + sIsTransforming = false; + sShouldUnequipMask = false; + Audio_PlaySfx(NA_SE_SY_DECIDE); + } else { + // Transformation mask or player is transformed, set as pending + sPendingMask = cursorItem; + sIsTransforming = isTransformation; + sShouldUnequipMask = false; + Audio_PlaySfx(NA_SE_SY_DECIDE); + } } } else if (CHECK_BTN_ALL(pressedButtons, BTN_B)) { + // Cancel any pending mask equip or unequip action sPendingMask = ITEM_NONE; sIsTransforming = false; sLastEquippedMask = ITEM_NONE; + sShouldUnequipMask = false; } } -// Register hooks for the EasyMaskEquip functionality +// Register the Easy Mask Equip functionality with the game interactor void RegisterEasyMaskEquip() { auto gameInteractor = GameInteractor::Instance; - // Handle mask equipping logic during KaleidoScope update + // Handle mask equipping logic during pause menu update gameInteractor->RegisterGameHook([](PauseContext* pauseCtx) { if (EasyMaskEquip_IsEnabled() && pauseCtx->pageIndex == PAUSE_MASK) { HandleEasyMaskEquip(pauseCtx); @@ -318,31 +371,38 @@ void RegisterEasyMaskEquip() { } }); - // Handle mask equipping when KaleidoScope closes + // Handle mask equipping when the pause menu closes gameInteractor->RegisterGameHook([]() { if (!EasyMaskEquip_IsEnabled() || gPlayState->pauseCtx.pageIndex != PAUSE_MASK) return; Player* player = GET_PLAYER(gPlayState); - if (sPendingMask == ITEM_NONE) + + if (sPendingMask == ITEM_NONE) { + if (sShouldUnequipMask) { + // No pending mask to equip, but we intend to unequip the current mask + s16 currentMaskItemId = Player_GetCurMaskItemId(gPlayState); + if (currentMaskItemId != ITEM_NONE) { + // Simulate using the current mask to unequip it + Player_UseItem(gPlayState, player, static_cast(currentMaskItemId)); + sLastEquippedMask = ITEM_NONE; + } + sShouldUnequipMask = false; // Reset the flag + } return; + } + // Equip pending mask bool isTransformation = IsTransformationMask(static_cast(sPendingMask)); bool isCurrentlyTransformed = (player->transformation != PLAYER_FORM_HUMAN); if (!isTransformation) { if (isCurrentlyTransformed) { - // Equip a regular mask while transformed: transform back to human first + // Equip a regular mask after transforming back to human Player_UseItem(gPlayState, player, static_cast(sPendingMask)); sLastEquippedMask = sPendingMask; // Retain sPendingMask to equip after transformation sIsTransforming = true; - } else { - // Equip a regular mask while already human - Player_UseItem(gPlayState, player, static_cast(sPendingMask)); - sLastEquippedMask = sPendingMask; - sPendingMask = ITEM_NONE; - sIsTransforming = false; } } else { // Equip a transformation mask @@ -359,9 +419,9 @@ void RegisterEasyMaskEquip() { return; Player* player = reinterpret_cast(actor); - if (sIsTransforming && player->transformation == PLAYER_FORM_HUMAN && - player->skelAnime.curFrame >= player->skelAnime.endFrame) { + if (sIsTransforming && player->transformation == PLAYER_FORM_HUMAN) { if (sPendingMask != ITEM_NONE) { + // If we have a pending mask, equip it now Player_UseItem(gPlayState, player, static_cast(sPendingMask)); sLastEquippedMask = sPendingMask; sPendingMask = ITEM_NONE; @@ -370,17 +430,36 @@ void RegisterEasyMaskEquip() { } }); - // Disable selected item textbox + // Disable the selected item textbox in the pause menu REGISTER_VB_SHOULD(VB_KALEIDO_DISPLAY_ITEM_TEXT, { if (EasyMaskEquip_IsEnabled()) { *should = false; } }); - // Remove restriction requiring masks be equipped from C or D buttons + // Remove the restriction requiring masks to be equipped from C or D buttons REGISTER_VB_SHOULD(VB_ALLOW_EQUIP_MASK, { if (EasyMaskEquip_IsEnabled()) { *should = false; } }); + + // Prevent the white "equipped" border from being drawn for masks that are currently equipped or pending to be + // equipped + REGISTER_VB_SHOULD(VB_DRAW_ITEM_EQUIPPED_OUTLINE, { + ItemId* itemId = va_arg(args, ItemId*); + if (EasyMaskEquip_IsEnabled()) { + s16 currentMaskItemId = Player_GetCurMaskItemId(gPlayState); + + // Suppress white border for the pending mask (lavender border will be drawn) + if (*itemId == sPendingMask) { + *should = false; + } + // Suppress white border for the currently equipped mask, if not being unequipped + else if (sPendingMask == ITEM_NONE && !sShouldUnequipMask && *itemId == currentMaskItemId) { + *should = false; + } + // Else, allow the white border to be drawn (default behavior) + } + }); }