diff --git a/soh/include/z64item.h b/soh/include/z64item.h index 214d82711..b1fa897fe 100644 --- a/soh/include/z64item.h +++ b/soh/include/z64item.h @@ -146,6 +146,9 @@ typedef enum { /* 0x1B */ SLOT_BOOTS_KOKIRI, /* 0x1C */ SLOT_BOOTS_IRON, /* 0x1D */ SLOT_BOOTS_HOVER, + /* 0x1E */ SLOT_SHIELD_DEKU, + /* 0x1F */ SLOT_SHIELD_HYLIAN, + /* 0x20 */ SLOT_SHIELD_MIRROR, /* 0xFF */ SLOT_NONE = 0xFF } InventorySlot; diff --git a/soh/soh/Enhancements/AssignableTunicsAndBoots.cpp b/soh/soh/Enhancements/AssignableTunicsAndBoots.cpp index 1ca5a7c19..535cd1400 100644 --- a/soh/soh/Enhancements/AssignableTunicsAndBoots.cpp +++ b/soh/soh/Enhancements/AssignableTunicsAndBoots.cpp @@ -14,7 +14,7 @@ extern PlayState* gPlayState; static u16 sItemButtons[] = { BTN_B, BTN_CLEFT, BTN_CDOWN, BTN_CRIGHT, BTN_DUP, BTN_DDOWN, BTN_DLEFT, BTN_DRIGHT }; -void UseTunicBoots(Player* player, PlayState* play, Input* input) { +static void UseTunicBoots(Player* player, PlayState* play, Input* input) { // Boots and tunics equip despite state if (player->stateFlags1 & (PLAYER_STATE1_INPUT_DISABLED | PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE | PLAYER_STATE1_TALKING | PLAYER_STATE1_DEAD) || @@ -30,7 +30,7 @@ void UseTunicBoots(Player* player, PlayState* play, Input* input) { } } - if (item >= ITEM_TUNIC_KOKIRI && item <= ITEM_BOOTS_HOVER) { + if (item >= ITEM_SHIELD_DEKU && item <= ITEM_BOOTS_HOVER) { if (item >= ITEM_BOOTS_KOKIRI) { u16 bootsValue = item - ITEM_BOOTS_KOKIRI + 1; if (CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS) == bootsValue) { @@ -41,7 +41,7 @@ void UseTunicBoots(Player* player, PlayState* play, Input* input) { Player_SetEquipmentData(play, player); func_808328EC(player, CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS) == EQUIP_VALUE_BOOTS_IRON ? NA_SE_PL_WALK_HEAVYBOOTS : NA_SE_PL_CHANGE_ARMS); - } else { + } else if (item >= ITEM_TUNIC_KOKIRI) { u16 tunicValue = item - ITEM_TUNIC_KOKIRI + 1; if (CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC) == tunicValue) { Inventory_ChangeEquipment(EQUIP_TYPE_TUNIC, EQUIP_VALUE_TUNIC_KOKIRI); @@ -50,45 +50,93 @@ void UseTunicBoots(Player* player, PlayState* play, Input* input) { } Player_SetEquipmentData(play, player); func_808328EC(player, NA_SE_PL_CHANGE_ARMS); + } else { + u16 shieldValue = item - ITEM_SHIELD_DEKU + 1; + if (CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD) != shieldValue) { + Inventory_ChangeEquipment(EQUIP_TYPE_SHIELD, shieldValue); + Player_SetEquipmentData(play, player); + func_808328EC(player, NA_SE_PL_CHANGE_ARMS); + } } } } -void ClearAssignedTunicsBoots(int32_t unused = 0) { +static void ClearAssignedTunicsBoots(int32_t unused = 0) { for (int32_t buttonIndex = 0; buttonIndex < 8; buttonIndex++) { int32_t item = gSaveContext.equips.buttonItems[buttonIndex]; - if (item >= ITEM_TUNIC_KOKIRI && item <= ITEM_BOOTS_HOVER) { + if (item >= ITEM_SHIELD_DEKU && item <= ITEM_BOOTS_HOVER) { gSaveContext.equips.buttonItems[buttonIndex] = ITEM_NONE; } } } +static void ClearDeletedAssignedEquipment(int16_t equipmentType, uint16_t equipValue) { + ItemID itemToRemove = ITEM_NONE; + + if (equipmentType == EQUIP_TYPE_TUNIC) { + switch (equipValue) { + case EQUIP_VALUE_TUNIC_KOKIRI: + break; + case EQUIP_VALUE_TUNIC_GORON: + itemToRemove = ITEM_TUNIC_GORON; + break; + case EQUIP_VALUE_TUNIC_ZORA: + itemToRemove = ITEM_TUNIC_ZORA; + break; + } + } else if (equipmentType == EQUIP_TYPE_SHIELD) { + switch (equipValue) { + case EQUIP_VALUE_SHIELD_DEKU: + itemToRemove = ITEM_SHIELD_DEKU; + break; + case EQUIP_VALUE_SHIELD_HYLIAN: + itemToRemove = ITEM_SHIELD_HYLIAN; + break; + case EQUIP_VALUE_SHIELD_MIRROR: + itemToRemove = ITEM_SHIELD_MIRROR; + break; + } + } + + if (itemToRemove == ITEM_NONE) { + return; + } + + for (int i = 1; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) { + if (gSaveContext.equips.buttonItems[i] == itemToRemove) { + gSaveContext.equips.buttonItems[i] = ITEM_NONE; + gSaveContext.equips.cButtonSlots[i - 1] = SLOT_NONE; + } + } +} + #define CVAR_TUNICBOOTS_NAME CVAR_ENHANCEMENT("AssignableTunicsAndBoots") #define CVAR_TUNICBOOTS_DEFAULT 0 #define CVAR_TUNICBOOTS_VALUE CVarGetInteger(CVAR_TUNICBOOTS_NAME, CVAR_TUNICBOOTS_DEFAULT) +#define CVAR_TUNICBOOTS_SET (CVAR_TUNICBOOTS_VALUE != CVAR_TUNICBOOTS_DEFAULT) -void RegisterAssignableTunicsBoots() { - // make sure we don't change our held/equipped item when changing tunics/boots - COND_VB_SHOULD(VB_CHANGE_HELD_ITEM_AND_USE_ITEM, CVAR_TUNICBOOTS_VALUE != CVAR_TUNICBOOTS_DEFAULT, { +static void RegisterAssignableTunicsBoots() { + // make sure we don't change our held/equipped item when changing shield/tunic/boots + COND_VB_SHOULD(VB_CHANGE_HELD_ITEM_AND_USE_ITEM, CVAR_TUNICBOOTS_SET, { int32_t item = va_arg(args, int32_t); - if (item >= ITEM_TUNIC_KOKIRI && item <= ITEM_BOOTS_HOVER) { + if (item >= ITEM_SHIELD_DEKU && item <= ITEM_BOOTS_HOVER) { *should = false; } }); // make sure we don't crash because tunics/boots don't have assoicated item actions - COND_VB_SHOULD(VB_ITEM_ACTION_BE_NONE, CVAR_TUNICBOOTS_VALUE != CVAR_TUNICBOOTS_DEFAULT, { + COND_VB_SHOULD(VB_ITEM_ACTION_BE_NONE, CVAR_TUNICBOOTS_SET, { int32_t item = va_arg(args, int32_t); - if (item >= ITEM_TUNIC_KOKIRI && item <= ITEM_BOOTS_HOVER) { + if (item >= ITEM_SHIELD_DEKU && item <= ITEM_BOOTS_HOVER) { *should = true; } }); - // don't throw items when the pressed button is a tunic or boots - COND_VB_SHOULD(VB_THROW_OR_PUT_DOWN_HELD_ITEM, CVAR_TUNICBOOTS_VALUE != CVAR_TUNICBOOTS_DEFAULT, { + // don't throw items when the pressed button is a shield, tunic or boots + COND_VB_SHOULD(VB_THROW_OR_PUT_DOWN_HELD_ITEM, CVAR_TUNICBOOTS_SET, { // if the vanilla condition doesn't want us to throw/put down the item, early return if (!*should) { return; @@ -104,13 +152,13 @@ void RegisterAssignableTunicsBoots() { } } - if (item >= ITEM_TUNIC_KOKIRI && item <= ITEM_BOOTS_HOVER) { + if (item >= ITEM_SHIELD_DEKU && item <= ITEM_BOOTS_HOVER) { *should = false; } }); // do something when the player presses a button to use the tunics/boots - COND_VB_SHOULD(VB_EXECUTE_PLAYER_ACTION_FUNC, CVAR_TUNICBOOTS_VALUE != CVAR_TUNICBOOTS_DEFAULT, { + COND_VB_SHOULD(VB_EXECUTE_PLAYER_ACTION_FUNC, CVAR_TUNICBOOTS_SET, { // if the vanilla condition doesn't want us to run the actionFunc, don't do any of this if (!*should) { return; @@ -133,13 +181,16 @@ void RegisterAssignableTunicsBoots() { UseTunicBoots(player, gPlayState, input); }); + // remove assigned equipment when it gets deleted + COND_HOOK(OnEquipmentDelete, CVAR_TUNICBOOTS_SET, ClearDeletedAssignedEquipment); + // clear out assigned tunics/boots when the enhancement is toggled off - if (GameInteractor::IsSaveLoaded(true) && CVAR_TUNICBOOTS_VALUE == CVAR_TUNICBOOTS_DEFAULT) { + if (GameInteractor::IsSaveLoaded(true) && !CVAR_TUNICBOOTS_SET) { ClearAssignedTunicsBoots(); } // clear out assigned tunics/boots when loading a save with enhancement turned off - COND_HOOK(OnLoadGame, CVAR_TUNICBOOTS_VALUE == CVAR_TUNICBOOTS_DEFAULT, ClearAssignedTunicsBoots); + COND_HOOK(OnLoadGame, !CVAR_TUNICBOOTS_SET, ClearAssignedTunicsBoots); } static RegisterShipInitFunc initFunc(RegisterAssignableTunicsBoots, { CVAR_TUNICBOOTS_NAME }); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index 0f694660c..8ca003329 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -12,6 +12,7 @@ DEFINE_HOOK(OnExitGame, (int32_t fileNum)); DEFINE_HOOK(OnGameStateMainStart, ()); DEFINE_HOOK(OnGameFrameUpdate, ()); DEFINE_HOOK(OnItemReceive, (GetItemEntry itemEntry)); +DEFINE_HOOK(OnEquipmentDelete, (int16_t equipmentType, uint16_t equipValue)); DEFINE_HOOK(OnSaleEnd, (GetItemEntry itemEntry)); DEFINE_HOOK(OnTransitionEnd, (int16_t sceneNum)); DEFINE_HOOK(OnSceneInit, (int16_t sceneNum)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 7d5a243d2..ed2b288c1 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -34,6 +34,11 @@ void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry) { GameInteractor::Instance->ExecuteHooksForFilter(itemEntry); } +void GameInteractor_ExecuteOnEquipmentDelete(int16_t equipmentType, uint16_t equipValue) { + GameInteractor::Instance->ExecuteHooks(equipmentType, equipValue); + GameInteractor::Instance->ExecuteHooksForFilter(equipmentType, equipValue); +} + void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry) { GameInteractor::Instance->ExecuteHooks(itemEntry); GameInteractor::Instance->ExecuteHooksForFilter(itemEntry); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index 4c3a600e7..b114ac84a 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -15,6 +15,7 @@ void GameInteractor_ExecuteOnExitGame(int32_t fileNum); void GameInteractor_ExecuteOnGameStateMainStart(); void GameInteractor_ExecuteOnGameFrameUpdate(); void GameInteractor_ExecuteOnItemReceiveHooks(GetItemEntry itemEntry); +void GameInteractor_ExecuteOnEquipmentDelete(int16_t equipmentType, uint16_t equipValue); void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry); void GameInteractor_ExecuteOnTransitionEndHooks(int16_t sceneNum); void GameInteractor_ExecuteOnSceneInit(int16_t sceneNum); diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index e81478df9..4df596405 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -732,9 +732,9 @@ 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, "Assignable Tunics and Boots", WIDGET_CVAR_CHECKBOX) + AddWidget(path, "Assignable Shields, Tunics and Boots", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("AssignableTunicsAndBoots")) - .Options(CheckboxOptions().Tooltip("Allows equipping the Tunics and Boots to C-Buttons/D-pad.")); + .Options(CheckboxOptions().Tooltip("Allows equipping Shields, Tunics and Boots to C-Buttons/D-pad.")); // TODO: Revist strength toggle, it's currently separate but should probably be locked behind the // Equipment toggle settings or be absorbed by it completely. AddWidget(path, "Equipment Toggle", WIDGET_CVAR_CHECKBOX) diff --git a/soh/src/code/z_inventory.c b/soh/src/code/z_inventory.c index 8a51c6263..f2b329ed5 100644 --- a/soh/src/code/z_inventory.c +++ b/soh/src/code/z_inventory.c @@ -1,4 +1,5 @@ #include "global.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #include "textures/icon_item_static/icon_item_static.h" #include "textures/icon_item_24_static/icon_item_24_static.h" #include "textures/parameter_static/parameter_static.h" @@ -204,20 +205,10 @@ u8 Inventory_DeleteEquipment(PlayState* play, s16 equipment) { if (equipment == EQUIP_TYPE_TUNIC) { gSaveContext.equips.equipment |= EQUIP_VALUE_TUNIC_KOKIRI << (EQUIP_TYPE_TUNIC * 4); - // non-vanilla: remove goron and zora tunics from item buttons if assignable tunics is on - if (CVarGetInteger(CVAR_ENHANCEMENT("AssignableTunicsAndBoots"), 0) && - equipValue != EQUIP_VALUE_TUNIC_KOKIRI) { - ItemID item = (equipValue == EQUIP_VALUE_TUNIC_GORON ? ITEM_TUNIC_GORON : ITEM_TUNIC_ZORA); - for (int i = 1; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) { - if (gSaveContext.equips.buttonItems[i] == item) { - gSaveContext.equips.buttonItems[i] = ITEM_NONE; - gSaveContext.equips.cButtonSlots[i - 1] = SLOT_NONE; - } - } - } - // end non-vanilla } + GameInteractor_ExecuteOnEquipmentDelete(equipment, equipValue); + if (equipment == EQUIP_TYPE_SWORD) { gSaveContext.equips.buttonItems[0] = ITEM_NONE; gSaveContext.infTable[29] = 1; 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 cec05af72..59dfffb1f 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 @@ -642,8 +642,8 @@ void KaleidoScope_DrawEquipment(PlayState* play) { pauseCtx->unk_1E4 = 7; sEquipTimer = 10; } else if (CVarGetInteger(CVAR_ENHANCEMENT("AssignableTunicsAndBoots"), 0) != 0) { - // Only allow assigning tunic and boots to c-buttons - if (pauseCtx->cursorY[PAUSE_EQUIP] > 1) { + // Only allow assigning shield, tunic and boots to c-buttons + if (pauseCtx->cursorY[PAUSE_EQUIP] > 0) { if (CHECK_OWNED_EQUIP(pauseCtx->cursorY[PAUSE_EQUIP], pauseCtx->cursorX[PAUSE_EQUIP] - 1)) { u16 slot = 0; switch (cursorItem) { @@ -665,6 +665,15 @@ void KaleidoScope_DrawEquipment(PlayState* play) { case ITEM_BOOTS_HOVER: slot = SLOT_BOOTS_HOVER; break; + case ITEM_SHIELD_DEKU: + slot = SLOT_SHIELD_DEKU; + break; + case ITEM_SHIELD_HYLIAN: + slot = SLOT_SHIELD_HYLIAN; + break; + case ITEM_SHIELD_MIRROR: + slot = SLOT_SHIELD_MIRROR; + break; default: break; }