From c688923272f875b20183a3e6bee3429e04f008cc Mon Sep 17 00:00:00 2001 From: OtherBlue <93625085+OtherBlue@users.noreply.github.com> Date: Tue, 30 Dec 2025 21:36:33 -0300 Subject: [PATCH] [Enhancement] Unequip C-items (#6043) --- soh/soh/Enhancements/ItemUnequip.cpp | 83 +++++++++++++++++++ .../vanilla-behavior/GIVanillaBehavior.h | 10 +++ soh/soh/SohGui/SohMenuEnhancements.cpp | 4 + .../ovl_kaleido_scope/z_kaleido_equipment.c | 9 +- .../misc/ovl_kaleido_scope/z_kaleido_item.c | 8 +- 5 files changed, 108 insertions(+), 6 deletions(-) create mode 100644 soh/soh/Enhancements/ItemUnequip.cpp diff --git a/soh/soh/Enhancements/ItemUnequip.cpp b/soh/soh/Enhancements/ItemUnequip.cpp new file mode 100644 index 000000000..338a1b36b --- /dev/null +++ b/soh/soh/Enhancements/ItemUnequip.cpp @@ -0,0 +1,83 @@ +#include +#include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/ShipInit.hpp" + +extern "C" { +#include "z64.h" +#include "functions.h" +#include "macros.h" +#include "variables.h" +#include "overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h" +extern SaveContext gSaveContext; +} + +#define CVAR_ITEM_UNEQUIP_NAME CVAR_ENHANCEMENT("ItemUnequip") +#define CVAR_ITEM_UNEQUIP_DEFAULT 0 +#define CVAR_ITEM_UNEQUIP_VALUE CVarGetInteger(CVAR_ITEM_UNEQUIP_NAME, CVAR_ITEM_UNEQUIP_DEFAULT) + +void RegisterItemUnequip() { + COND_VB_SHOULD(VB_EQUIP_ITEM_TO_C_BUTTON, CVAR_ITEM_UNEQUIP_VALUE, { + PlayState* play = va_arg(args, PlayState*); + u16 cursorSlot = va_arg(args, int); + u16 cursorItem = va_arg(args, int); + + Input* input = &play->state.input[0]; + + int targetButton = -1; + + if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { + targetButton = 1; + } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN)) { + targetButton = 2; + } else if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { + targetButton = 3; + } else if (CVarGetInteger(CVAR_ENHANCEMENT("DpadEquips"), 0)) { + if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) { + targetButton = 4; + } else if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { + targetButton = 5; + } else if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT)) { + targetButton = 6; + } else if (CHECK_BTN_ALL(input->press.button, BTN_DRIGHT)) { + targetButton = 7; + } + } + + if (targetButton == -1) { + return; + } + + u8 equippedItem = gSaveContext.equips.buttonItems[targetButton]; + u8 equippedSlot = gSaveContext.equips.cButtonSlots[targetButton - 1]; + bool shouldUnequip = false; + + if (equippedItem == cursorItem) { + if (cursorItem >= ITEM_BOTTLE && cursorItem <= ITEM_POE) { + if (equippedSlot == cursorSlot) { + shouldUnequip = true; + } + } else { + shouldUnequip = true; + } + } else if (cursorItem == ITEM_ARROW_FIRE && equippedItem == ITEM_BOW_ARROW_FIRE) { + shouldUnequip = true; + } else if (cursorItem == ITEM_ARROW_ICE && equippedItem == ITEM_BOW_ARROW_ICE) { + shouldUnequip = true; + } else if (cursorItem == ITEM_ARROW_LIGHT && equippedItem == ITEM_BOW_ARROW_LIGHT) { + shouldUnequip = true; + } + + if (shouldUnequip) { + gSaveContext.equips.buttonItems[targetButton] = ITEM_NONE; + gSaveContext.equips.cButtonSlots[targetButton - 1] = SLOT_NONE; + Interface_LoadItemIcon1(play, targetButton); + + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + + *should = false; + } + }); +} + +static RegisterShipInitFunc initFunc(RegisterItemUnequip, { CVAR_ITEM_UNEQUIP_NAME }); diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 11756cb29..101b13641 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -2414,6 +2414,16 @@ typedef enum { // - `*Color_RGB8` VB_APPLY_TUNIC_COLOR, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*PlayState` + // - `uint16_t` (cursorSlot - promoted from `u16`) + // - `uint16_t` (cursorItem - promoted from `u16`) + VB_EQUIP_ITEM_TO_C_BUTTON, + } GIVanillaBehavior; #endif diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index c32b11755..d6557cb47 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -730,6 +730,10 @@ void SohMenu::AddMenuEnhancements() { .Options(CheckboxOptions().Tooltip( "Equip items and equipment on the D-pad. If used with \"D-pad on Pause Screen\", you must " "hold C-Up to equip instead of navigate.")); + AddWidget(path, "Unequip C-Items on Re-press", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("ItemUnequip")) + .Options(CheckboxOptions().Tooltip("Allows unequipping items from C-Buttons/D-pad by hovering over an equipped " + "item and pressing the button it's equipped to.")); AddWidget(path, "Assignable Shields, Tunics and Boots", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("AssignableTunicsAndBoots")) .Options(CheckboxOptions().Tooltip("Allows equipping Shields, Tunics and Boots to C-Buttons/D-pad.")); diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c index 59dfffb1f..f5adb0b44 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c @@ -3,6 +3,7 @@ #include "textures/parameter_static/parameter_static.h" #include "soh/Enhancements/cosmetics/cosmeticsTypes.h" #include "soh/Enhancements/enhancementTypes.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" static u8 sChildUpgrades[] = { UPG_BULLET_BAG, UPG_BOMB_BAG, UPG_STRENGTH, UPG_SCALE }; static u8 sAdultUpgrades[] = { UPG_QUIVER, UPG_BOMB_BAG, UPG_STRENGTH, UPG_SCALE }; @@ -677,9 +678,11 @@ void KaleidoScope_DrawEquipment(PlayState* play) { default: break; } - KaleidoScope_SetupItemEquip(play, cursorItem, slot, - pauseCtx->equipVtx[cursorSlot * 4].v.ob[0] * 10, - pauseCtx->equipVtx[cursorSlot * 4].v.ob[1] * 10); + if (GameInteractor_Should(VB_EQUIP_ITEM_TO_C_BUTTON, true, play, slot, cursorItem)) { + KaleidoScope_SetupItemEquip(play, cursorItem, slot, + pauseCtx->equipVtx[cursorSlot * 4].v.ob[0] * 10, + pauseCtx->equipVtx[cursorSlot * 4].v.ob[1] * 10); + } } else { Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c index dad520a0b..28e5e3ed8 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c @@ -692,9 +692,11 @@ void KaleidoScope_DrawItemSelect(PlayState* play) { if (CHECK_BTN_ANY(input->press.button, buttonsToCheck)) { if (CHECK_AGE_REQ_SLOT(cursorSlot) && (cursorItem != ITEM_SOLD_OUT) && (cursorItem != ITEM_NONE)) { - KaleidoScope_SetupItemEquip(play, cursorItem, cursorSlot, - pauseCtx->itemVtx[index].v.ob[0] * 10, - pauseCtx->itemVtx[index].v.ob[1] * 10); + if (GameInteractor_Should(VB_EQUIP_ITEM_TO_C_BUTTON, true, play, cursorSlot, cursorItem)) { + KaleidoScope_SetupItemEquip(play, cursorItem, cursorSlot, + pauseCtx->itemVtx[index].v.ob[0] * 10, + pauseCtx->itemVtx[index].v.ob[1] * 10); + } } else { Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb);