diff --git a/mm/2s2h/BenGui/BenMenu.cpp b/mm/2s2h/BenGui/BenMenu.cpp index 5850badab8..a73fea958d 100644 --- a/mm/2s2h/BenGui/BenMenu.cpp +++ b/mm/2s2h/BenGui/BenMenu.cpp @@ -962,6 +962,10 @@ void BenMenu::AddEnhancements() { AddWidget(path, "Dpad Equips", WIDGET_CVAR_CHECKBOX) .CVar("gEnhancements.Dpad.DpadEquips") .Options(CheckboxOptions().Tooltip("Allows you to equip items to your D-pad.")); + AddWidget(path, "Unequip Items", WIDGET_CVAR_CHECKBOX) + .CVar("gEnhancements.Equipment.ItemUnequip") + .Options(CheckboxOptions().Tooltip("In the pause menu, press the same C-button or D-pad button an item is " + "equipped to in order to unequip it.")); AddWidget(path, "Fast Magic Arrow Equip Animation", WIDGET_CVAR_CHECKBOX) .CVar("gEnhancements.Equipment.MagicArrowEquipSpeed") .Options(CheckboxOptions().Tooltip("Removes the animation for equipping Magic Arrows.")); diff --git a/mm/2s2h/Enhancements/Equipment/ItemUnequip.cpp b/mm/2s2h/Enhancements/Equipment/ItemUnequip.cpp new file mode 100644 index 0000000000..1038c8ef29 --- /dev/null +++ b/mm/2s2h/Enhancements/Equipment/ItemUnequip.cpp @@ -0,0 +1,141 @@ +#include +#include "2s2h/GameInteractor/GameInteractor.h" +#include "2s2h/ShipInit.hpp" +#include "2s2h_assets.h" + +extern "C" { +#include "z64.h" +#include "functions.h" +#include "macros.h" +#include "variables.h" +#include "overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_scope.h" +} + +#define CVAR_DPAD_NAME "gEnhancements.Dpad.DpadEquips" +#define CVAR_DPAD CVarGetInteger(CVAR_DPAD_NAME, 0) +#define CVAR_NAME "gEnhancements.Equipment.ItemUnequip" +#define CVAR CVarGetInteger(CVAR_NAME, 0) + +void RegisterDpadPageSwitchPrevention() { + COND_VB_SHOULD(VB_KALEIDO_SWITCH_PAGE_WITH_DPAD, CVAR_DPAD, { + u16 button = va_arg(args, int); + Input* input = &gPlayState->state.input[0]; + + if (CHECK_BTN_ALL(input->cur.button, button)) { + PauseContext* pauseCtx = &gPlayState->pauseCtx; + + // Prevent page switching with D-pad when on item or mask page + if ((pauseCtx->pageIndex == PAUSE_ITEM || pauseCtx->pageIndex == PAUSE_MASK) && + pauseCtx->mainState <= PAUSE_MAIN_STATE_IDLE_CURSOR_ON_SONG) { + *should = false; + } + } + }); +} + +void RegisterItemUnequip() { + COND_VB_SHOULD(VB_KALEIDO_EQUIP_ITEM_TO_BUTTON, CVAR, { + u16 cursorSlot = va_arg(args, int); + u16 cursorItem = va_arg(args, int); + + PauseContext* pauseCtx = &gPlayState->pauseCtx; + s32 targetSlot = -1; + bool isDpad = false; + + // Determine which button was pressed based on equipTargetCBtn + switch (pauseCtx->equipTargetCBtn) { + case PAUSE_EQUIP_C_LEFT: + targetSlot = EQUIP_SLOT_C_LEFT; + break; + case PAUSE_EQUIP_C_DOWN: + targetSlot = EQUIP_SLOT_C_DOWN; + break; + case PAUSE_EQUIP_C_RIGHT: + targetSlot = EQUIP_SLOT_C_RIGHT; + break; + case PAUSE_EQUIP_D_RIGHT: + targetSlot = EQUIP_SLOT_D_RIGHT; + isDpad = true; + break; + case PAUSE_EQUIP_D_LEFT: + targetSlot = EQUIP_SLOT_D_LEFT; + isDpad = true; + break; + case PAUSE_EQUIP_D_DOWN: + targetSlot = EQUIP_SLOT_D_DOWN; + isDpad = true; + break; + case PAUSE_EQUIP_D_UP: + targetSlot = EQUIP_SLOT_D_UP; + isDpad = true; + break; + default: + return; + } + + u8 equippedItem; + u8 equippedSlot; + bool shouldUnequip = false; + bool isMask = cursorSlot >= ITEM_NUM_SLOTS; + + // C-buttons vs D-pad + if (!isDpad) { + equippedItem = BUTTON_ITEM_EQUIP(0, targetSlot); + equippedSlot = C_SLOT_EQUIP(0, targetSlot); + } else { + equippedItem = DPAD_BUTTON_ITEM_EQUIP(0, targetSlot); + equippedSlot = DPAD_SLOT_EQUIP(0, targetSlot); + } + + // Check if we should unequip + if (equippedItem == cursorItem) { + if (isMask) { + // For masks, check the slot matches (cursorSlot is already offset by ITEM_NUM_SLOTS) + if (equippedSlot == cursorSlot) { + shouldUnequip = true; + } + } + // For bottles, we need to check the slot too (since there are multiple bottle items) + else if (cursorItem >= ITEM_BOTTLE && cursorItem <= ITEM_OBABA_DRINK) { + if (equippedSlot == cursorSlot) { + shouldUnequip = true; + } + } else { + shouldUnequip = true; + } + } + // Handle magic arrows (bow variants) + else if (cursorItem == ITEM_ARROW_FIRE && equippedItem == ITEM_BOW_FIRE) { + shouldUnequip = true; + } else if (cursorItem == ITEM_ARROW_ICE && equippedItem == ITEM_BOW_ICE) { + shouldUnequip = true; + } else if (cursorItem == ITEM_ARROW_LIGHT && equippedItem == ITEM_BOW_LIGHT) { + shouldUnequip = true; + } else if (cursorItem == ITEM_BOW && + (equippedItem == ITEM_BOW_FIRE || equippedItem == ITEM_BOW_ICE || equippedItem == ITEM_BOW_LIGHT)) { + shouldUnequip = true; + } + + if (shouldUnequip) { + if (!isDpad) { + // C-buttons + BUTTON_ITEM_EQUIP(0, targetSlot) = ITEM_NONE; + C_SLOT_EQUIP(0, targetSlot) = SLOT_NONE; + Interface_LoadItemIconImpl(gPlayState, targetSlot); + } else { + // D-pad + DPAD_BUTTON_ITEM_EQUIP(0, targetSlot) = ITEM_NONE; + DPAD_SLOT_EQUIP(0, targetSlot) = SLOT_NONE; + // Manually clear D-pad icon + gPlayState->interfaceCtx.iconItemSegment[DPAD_BUTTON(targetSlot) + EQUIP_SLOT_MAX] = + (char*)gEmptyTexture; + } + + Audio_PlaySfx(NA_SE_SY_DECIDE); + *should = false; + } + }); +} + +static RegisterShipInitFunc initFunc(RegisterItemUnequip, { CVAR_NAME }); +static RegisterShipInitFunc initDpadPageSwitch(RegisterDpadPageSwitchPrevention, { CVAR_DPAD_NAME }); \ No newline at end of file diff --git a/mm/2s2h/GameInteractor/GameInteractor_VanillaBehavior.h b/mm/2s2h/GameInteractor/GameInteractor_VanillaBehavior.h index 0d84558fa3..9da425a4c2 100644 --- a/mm/2s2h/GameInteractor/GameInteractor_VanillaBehavior.h +++ b/mm/2s2h/GameInteractor/GameInteractor_VanillaBehavior.h @@ -1101,6 +1101,23 @@ typedef enum { // - `u16` (item under cursor) VB_KALEIDO_DISPLAY_ITEM_TEXT, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `u16` (cursor slot) + // - `u16` (cursor item) + VB_KALEIDO_EQUIP_ITEM_TO_BUTTON, + + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `u16` (button - BTN_DLEFT or BTN_DRIGHT) + VB_KALEIDO_SWITCH_PAGE_WITH_DPAD, + // #### `result` // ```c // true diff --git a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_item.c b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_item.c index 3a80dfda80..e997224bfa 100644 --- a/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_item.c +++ b/mm/src/overlays/kaleido_scope/ovl_kaleido_scope/z_kaleido_item.c @@ -733,6 +733,9 @@ void KaleidoScope_UpdateItemCursor(PlayState* play) { } } // #endregion + if (!GameInteractor_Should(VB_KALEIDO_EQUIP_ITEM_TO_BUTTON, true, cursorSlot, cursorItem)) { + return; + } // Equip item to the C buttons pauseCtx->equipTargetItem = cursorItem; 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 757678732c..56e2856797 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 @@ -661,6 +661,11 @@ void KaleidoScope_UpdateMaskCursor(PlayState* play) { } // #endregion + if (!GameInteractor_Should(VB_KALEIDO_EQUIP_ITEM_TO_BUTTON, true, cursorSlot + ITEM_NUM_SLOTS, + cursorItem)) { + return; + } + // Equip item to the C buttons pauseCtx->equipTargetItem = cursorItem; pauseCtx->equipTargetSlot = cursorSlot + ITEM_NUM_SLOTS; 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 4209fc257b..e2581adfc5 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 @@ -545,6 +545,9 @@ void KaleidoScope_HandlePageToggles(PlayState* play, Input* input) { } if (CHECK_BTN_ALL(input->cur.button, BTN_DRIGHT) || CHECK_BTN_ALL(input->press.button, BTN_R)) { + if (!GameInteractor_Should(VB_KALEIDO_SWITCH_PAGE_WITH_DPAD, true, BTN_DRIGHT)) { + return; + } // Switch the page to the right regardless of where the cursor is if (interfaceCtx->aButtonDoActionDelayed == DO_ACTION_DECIDE) { Interface_SetAButtonDoAction(play, DO_ACTION_INFO); @@ -554,6 +557,9 @@ void KaleidoScope_HandlePageToggles(PlayState* play, Input* input) { } if (CHECK_BTN_ALL(input->cur.button, BTN_DLEFT) || CHECK_BTN_ALL(input->press.button, BTN_Z)) { + if (!GameInteractor_Should(VB_KALEIDO_SWITCH_PAGE_WITH_DPAD, true, BTN_DLEFT)) { + return; + } // Switch the page to the left regardless of where the cursor is if (interfaceCtx->aButtonDoActionDelayed == DO_ACTION_DECIDE) { Interface_SetAButtonDoAction(play, DO_ACTION_INFO);