summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJordan Longstaff <JordanLongstaff@users.noreply.github.com>2025-11-13 16:29:48 -0500
committerGitHub <noreply@github.com>2025-11-13 14:29:48 -0700
commitdadc2e521866ef79f04f296fb69c7821dab82ac8 (patch)
treed4582b36def0f2269da4e8999cd14c19146f031a
parent5629d033c274f6335a552e80ba90a17246ffd5cd (diff)
"Assignable Tunic and Boots" setting now allows shields also (#5953)
-rw-r--r--soh/include/z64item.h3
-rw-r--r--soh/soh/Enhancements/AssignableTunicsAndBoots.cpp85
-rw-r--r--soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h1
-rw-r--r--soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp5
-rw-r--r--soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h1
-rw-r--r--soh/soh/SohGui/SohMenuEnhancements.cpp4
-rw-r--r--soh/src/code/z_inventory.c15
-rw-r--r--soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c13
8 files changed, 94 insertions, 33 deletions
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<GameInteractor::OnItemReceive>(itemEntry);
}
+void GameInteractor_ExecuteOnEquipmentDelete(int16_t equipmentType, uint16_t equipValue) {
+ GameInteractor::Instance->ExecuteHooks<GameInteractor::OnEquipmentDelete>(equipmentType, equipValue);
+ GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnEquipmentDelete>(equipmentType, equipValue);
+}
+
void GameInteractor_ExecuteOnSaleEndHooks(GetItemEntry itemEntry) {
GameInteractor::Instance->ExecuteHooks<GameInteractor::OnSaleEnd>(itemEntry);
GameInteractor::Instance->ExecuteHooksForFilter<GameInteractor::OnSaleEnd>(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;
}