"Assignable Tunic and Boots" setting now allows shields also (#5953)

This commit is contained in:
Jordan Longstaff
2025-11-13 16:29:48 -05:00
committed by GitHub
parent 5629d033c2
commit dadc2e5218
8 changed files with 94 additions and 33 deletions

View File

@@ -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;

View File

@@ -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 });

View File

@@ -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));

View File

@@ -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);

View File

@@ -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);

View File

@@ -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)

View File

@@ -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;

View File

@@ -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;
}