Skip to content
96 changes: 96 additions & 0 deletions soh/soh/Enhancements/Graphics/ChildHoldsHylianShield.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#include "soh/ShipInit.hpp"
#include <spdlog/spdlog.h>

extern "C" {
#include "macros.h"
#include "variables.h"
#include "functions.h"
#include "soh/ResourceManagerHelpers.h"
#include "objects/object_link_boy/object_link_boy.h"
#include "objects/object_link_child/object_link_child.h"

extern SaveContext gSaveContext;
extern PlayState* gPlayState;
}

static constexpr int32_t CVAR_SCALEADULTEQUIPMENTASCHILD_DEFAULT = 0;
#define CVAR_SCALEADULTEQUIPMENTASCHILD_NAME CVAR_ENHANCEMENT("ScaleAdultEquipmentAsChild")
#define CVAR_SCALEADULTEQUIPMENTASCHILD_VALUE \
CVarGetInteger(CVAR_SCALEADULTEQUIPMENTASCHILD_NAME, CVAR_SCALEADULTEQUIPMENTASCHILD_DEFAULT)

static constexpr int32_t CVAR_CHILDHOLDSHYLIANSHIELD_DEFAULT = 0;
#define CVAR_CHILDHOLDSHYLIANSHIELD_NAME CVAR_CHEAT("ChildHoldsHylianShield")
#define CVAR_CHILDHOLDSHYLIANSHIELD_VALUE \
CVarGetInteger(CVAR_CHILDHOLDSHYLIANSHIELD_NAME, CVAR_CHILDHOLDSHYLIANSHIELD_DEFAULT)

static void ResetPatchChildHylianShield() {
ResourceMgr_UnpatchGfxByName(gLinkAdultHylianShieldSwordAndSheathNearDL, "childHylianShield1");
ResourceMgr_UnpatchGfxByName(gLinkAdultHylianShieldSwordAndSheathNearDL, "childHylianShield2");
ResourceMgr_UnpatchGfxByName(gLinkAdultHylianShieldSwordAndSheathNearDL, "childHylianShield3");
}

static void UpdatePatchChildHylianShield() {
ResetPatchChildHylianShield();

if (CVAR_SCALEADULTEQUIPMENTASCHILD_VALUE && LINK_IS_CHILD) {
if (gSaveContext.equips.buttonItems[0] == ITEM_SWORD_KOKIRI ||
gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE) {
ResourceMgr_PatchGfxByName(gLinkAdultHylianShieldSwordAndSheathNearDL, "childHylianShield1", 82,
gsSPDisplayListOTRFilePath(gLinkChildSwordAndSheathNearDL));
ResourceMgr_PatchGfxByName(gLinkAdultHylianShieldSwordAndSheathNearDL, "childHylianShield2", 83,
gsSPEndDisplayList());
}
if (gSaveContext.equips.buttonItems[0] == ITEM_NONE || gSaveContext.equips.buttonItems[0] == ITEM_STICK) {
ResourceMgr_PatchGfxByName(gLinkAdultHylianShieldSwordAndSheathNearDL, "childHylianShield3", 82,
gsSPEndDisplayList());
}
}
}

static void RegisterChildHoldsHylianShieldGraphics() {
ResetPatchChildHylianShield();

COND_HOOK(OnLoadGame, CVAR_SCALEADULTEQUIPMENTASCHILD_VALUE, [](int32_t fileNum) {
if (gPlayState == nullptr) {
return;
}
Player* player = GET_PLAYER(gPlayState);
Player_SetModels(player, Player_ActionToModelGroup(player, player->heldItemAction));
});

COND_HOOK(OnPlayerUpdate, CVAR_SCALEADULTEQUIPMENTASCHILD_VALUE, []() {
static uint16_t lastItemOnB = gSaveContext.equips.buttonItems[0];
if (lastItemOnB != gSaveContext.equips.buttonItems[0]) {
UpdatePatchChildHylianShield();
lastItemOnB = gSaveContext.equips.buttonItems[0];
}
});

COND_HOOK(OnSceneInit, CVAR_SCALEADULTEQUIPMENTASCHILD_VALUE,
[](int16_t sceneNum) { UpdatePatchChildHylianShield(); });
}

static void RegisterChildHoldsHylianShieldReflect() {
COND_VB_SHOULD(VB_REFLECT_NUTSBALL, CVAR_CHILDHOLDSHYLIANSHIELD_VALUE, {
Player* player = GET_PLAYER(gPlayState);

if (LINK_IS_CHILD && (player->currentShield == PLAYER_SHIELD_HYLIAN)) {
*should = true;
}
});

COND_VB_SHOULD(VB_REFLECT_OCTOROK_PROJECTILE, CVAR_CHILDHOLDSHYLIANSHIELD_VALUE, {
Player* player = GET_PLAYER(gPlayState);

if (LINK_IS_CHILD && (player->currentShield == PLAYER_SHIELD_HYLIAN)) {
*should = true;
}
});
}

static RegisterShipInitFunc initFunc_Graphics(RegisterChildHoldsHylianShieldGraphics,
{ CVAR_SCALEADULTEQUIPMENTASCHILD_NAME });

static RegisterShipInitFunc initFunc_Reflect(RegisterChildHoldsHylianShieldReflect,
{ CVAR_CHILDHOLDSHYLIANSHIELD_NAME });
Original file line number Diff line number Diff line change
Expand Up @@ -1777,6 +1777,24 @@ typedef enum {
// - `*EnRd`
VB_REDEAD_GIBDO_FREEZE_LINK,

// #### `result`
// ```c
// (player->currentShield == PLAYER_SHIELD_DEKU) || ((player->currentShield == PLAYER_SHIELD_HYLIAN) &&
// LINK_IS_ADULT)
// ```
// #### `args`
// - `*EnNutsball`
VB_REFLECT_NUTSBALL,

// #### `result`
// ```c
// (player->currentShield == PLAYER_SHIELD_DEKU) || ((player->currentShield == PLAYER_SHIELD_HYLIAN) &&
// LINK_IS_ADULT)
// ```
// #### `args`
// - `*EnOkuta`
VB_REFLECT_OCTOROK_PROJECTILE,

// #### `result`
// #### `result`
// ```c
Expand Down
4 changes: 4 additions & 0 deletions soh/soh/SohGui/SohMenuEnhancements.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1676,6 +1676,10 @@ void SohMenu::AddMenuEnhancements() {
.CVar(CVAR_CHEAT("TimelessEquipment"))
.Options(CheckboxOptions().Tooltip("Allows any item to be equipped, regardless of age.\n"
"Also allows child to use adult strength upgrades."));
AddWidget(path, "Hold Hylian Shield as Child Link", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_CHEAT("ChildHoldsHylianShield"))
.Options(CheckboxOptions().Tooltip(
"Allows Child Link to hold the Hylian Shield the same way as the rest of the shields."));
AddWidget(path, "Unrestricted Items", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_CHEAT("NoRestrictItems"))
.Options(CheckboxOptions().Tooltip("Allows you to use any item at any location"));
Expand Down
38 changes: 32 additions & 6 deletions soh/src/code/z_player_lib.c
Original file line number Diff line number Diff line change
Expand Up @@ -525,7 +525,12 @@ s32 Player_CheckHostileLockOn(Player* this) {
}

s32 Player_IsChildWithHylianShield(Player* this) {
return gSaveContext.linkAge != 0 && (this->currentShield == PLAYER_SHIELD_HYLIAN);
if (CVarGetInteger(CVAR_CHEAT("ChildHoldsHylianShield"), 0)) {
return false; // Skip vanilla check for making child Link have the Hylian Shield on his back, allowing for it to
// be used in hand
} else {
return gSaveContext.linkAge != 0 && (this->currentShield == PLAYER_SHIELD_HYLIAN);
}
}

s32 Player_ActionToModelGroup(Player* this, s32 actionParam) {
Expand All @@ -546,8 +551,11 @@ void Player_SetModelsForHoldingShield(Player* this) {
!Player_HoldsTwoHandedWeapon(this)) &&
!Player_IsChildWithHylianShield(this)) {
this->rightHandType = PLAYER_MODELTYPE_RH_SHIELD;
if (LINK_IS_CHILD && (CVarGetInteger(CVAR_ENHANCEMENT("EquipmentAlwaysVisible"), 0)) &&
(this->currentShield == PLAYER_SHIELD_MIRROR)) {
if (LINK_IS_CHILD && CVarGetInteger(CVAR_CHEAT("ChildHoldsHylianShield"), 0) &&
this->currentShield == PLAYER_SHIELD_HYLIAN) {
this->rightHandDLists = &sPlayerDListGroups[PLAYER_MODELTYPE_RH_SHIELD][LINK_AGE_ADULT];
} else if (LINK_IS_CHILD && (CVarGetInteger(CVAR_ENHANCEMENT("EquipmentAlwaysVisible"), 0)) &&
(this->currentShield == PLAYER_SHIELD_MIRROR)) {
this->rightHandDLists = &sPlayerDListGroups[PLAYER_MODELTYPE_RH_SHIELD][0];
} else if (LINK_IS_ADULT && (CVarGetInteger(CVAR_ENHANCEMENT("EquipmentAlwaysVisible"), 0)) &&
(this->currentShield == PLAYER_SHIELD_DEKU)) {
Expand Down Expand Up @@ -598,8 +606,10 @@ void Player_SetModels(Player* this, s32 modelGroup) {
this->rightHandType = gPlayerModelTypes[modelGroup][PLAYER_MODELGROUPENTRY_RIGHT_HAND];
this->rightHandDLists = &sPlayerDListGroups[this->rightHandType][gSaveContext.linkAge];

this->rightHandType = gPlayerModelTypes[modelGroup][PLAYER_MODELGROUPENTRY_RIGHT_HAND];
this->rightHandDLists = &sPlayerDListGroups[this->rightHandType][gSaveContext.linkAge];
if (LINK_IS_CHILD && CVarGetInteger(CVAR_CHEAT("ChildHoldsHylianShield"), 0) &&
this->rightHandType == PLAYER_MODELTYPE_RH_SHIELD && this->currentShield == PLAYER_SHIELD_HYLIAN) {
this->rightHandDLists = &sPlayerDListGroups[this->rightHandType][LINK_AGE_ADULT];
}

if (CVarGetInteger(CVAR_ENHANCEMENT("EquipmentAlwaysVisible"), 0)) {
if (LINK_IS_CHILD &&
Expand All @@ -622,6 +632,16 @@ void Player_SetModels(Player* this, s32 modelGroup) {
this->sheathType = gPlayerModelTypes[modelGroup][PLAYER_MODELGROUPENTRY_SHEATH];
this->sheathDLists = &sPlayerDListGroups[this->sheathType][gSaveContext.linkAge];

if (CVarGetInteger(CVAR_ENHANCEMENT("ScaleAdultEquipmentAsChild"), 0) &&
!CVarGetInteger(CVAR_CHEAT("ChildHoldsHylianShield"), 0)) {
if (LINK_IS_CHILD && this->sheathType == PLAYER_MODELTYPE_SHEATH_18 &&
this->currentShield == PLAYER_SHIELD_HYLIAN &&
(gSaveContext.equips.buttonItems[0] != ITEM_SWORD_MASTER &&
gSaveContext.equips.buttonItems[0] != ITEM_SWORD_BGS)) {
this->sheathDLists = &sPlayerDListGroups[this->sheathType][LINK_AGE_ADULT];
}
}

if (CVarGetInteger(CVAR_ENHANCEMENT("EquipmentAlwaysVisible"), 0)) {
if (LINK_IS_CHILD && (this->currentShield == PLAYER_SHIELD_HYLIAN &&
((gSaveContext.equips.buttonItems[0] == ITEM_SWORD_MASTER) ||
Expand All @@ -636,7 +656,9 @@ void Player_SetModels(Player* this, s32 modelGroup) {
} else if (LINK_IS_ADULT && (this->currentShield == PLAYER_SHIELD_DEKU &&
gSaveContext.equips.buttonItems[0] != ITEM_SWORD_MASTER) ||
(gSaveContext.equips.buttonItems[0] == ITEM_SWORD_MASTER &&
this->sheathType == PLAYER_MODELTYPE_SHEATH_18 && this->currentShield == PLAYER_SHIELD_DEKU)) {
this->sheathType == PLAYER_MODELTYPE_SHEATH_18 && this->currentShield == PLAYER_SHIELD_DEKU) ||
(this->sheathType == PLAYER_MODELTYPE_SHEATH_17 &&
gSaveContext.equips.buttonItems[0] == ITEM_SWORD_KOKIRI)) {
this->sheathDLists = &sPlayerDListGroups[this->sheathType][1];
} else if (LINK_IS_CHILD && this->sheathType == PLAYER_MODELTYPE_SHEATH_17 &&
((gSaveContext.equips.buttonItems[0] == ITEM_SWORD_MASTER) ||
Expand Down Expand Up @@ -1263,6 +1285,10 @@ s32 Player_OverrideLimbDrawGameplayCommon(PlayState* play, s32 limbIndex, Gfx**
(sRightHandType == PLAYER_MODELTYPE_RH_BOW_SLINGSHOT && Player_HoldsBow(this))) {
Matrix_Scale(0.8, 0.8, 0.8, MTXMODE_APPLY);
}
if (CVarGetInteger(CVAR_CHEAT("ChildHoldsHylianShield"), 0) &&
this->currentShield == PLAYER_SHIELD_HYLIAN && sRightHandType == PLAYER_MODELTYPE_RH_SHIELD) {
Matrix_Scale(0.8, 0.8, 0.8, MTXMODE_APPLY);
}
}
if (limbIndex == PLAYER_LIMB_SHEATH) {
if ((this->currentShield == PLAYER_SHIELD_MIRROR ||
Expand Down
7 changes: 5 additions & 2 deletions soh/src/overlays/actors/ovl_En_Nutsball/z_en_nutsball.c
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
#include "objects/object_shopnuts/object_shopnuts.h"
#include "objects/object_dns/object_dns.h"
#include "objects/object_dnk/object_dnk.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"

#define FLAGS ACTOR_FLAG_UPDATE_CULLING_DISABLED

Expand Down Expand Up @@ -122,8 +123,10 @@ void func_80ABBBA8(EnNutsball* this, PlayState* play) {
(this->collider.base.acFlags & AC_HIT) || (this->collider.base.ocFlags1 & OC1_HIT)) {
// Checking if the player is using a shield that reflects projectiles
// And if so, reflects the projectile on impact
if ((player->currentShield == PLAYER_SHIELD_DEKU) ||
((player->currentShield == PLAYER_SHIELD_HYLIAN) && LINK_IS_ADULT)) {
if (GameInteractor_Should(VB_REFLECT_NUTSBALL,
(player->currentShield == PLAYER_SHIELD_DEKU) ||
((player->currentShield == PLAYER_SHIELD_HYLIAN) && LINK_IS_ADULT),
this)) {
if ((this->collider.base.atFlags & AT_HIT) && (this->collider.base.atFlags & AT_TYPE_ENEMY) &&
(this->collider.base.atFlags & AT_BOUNCED)) {
this->collider.base.atFlags &= ~AT_TYPE_ENEMY & ~AT_BOUNCED & ~AT_HIT;
Expand Down
6 changes: 4 additions & 2 deletions soh/src/overlays/actors/ovl_En_Okuta/z_en_okuta.c
Original file line number Diff line number Diff line change
Expand Up @@ -493,8 +493,10 @@ void EnOkuta_ProjectileFly(EnOkuta* this, PlayState* play) {
if ((this->actor.bgCheckFlags & 8) || (this->actor.bgCheckFlags & 1) || (this->collider.base.atFlags & AT_HIT) ||
this->collider.base.acFlags & AC_HIT || this->collider.base.ocFlags1 & OC1_HIT ||
this->actor.floorHeight == BGCHECK_Y_MIN) {
if ((player->currentShield == PLAYER_SHIELD_DEKU ||
(player->currentShield == PLAYER_SHIELD_HYLIAN && LINK_IS_ADULT)) &&
if (GameInteractor_Should(VB_REFLECT_OCTOROK_PROJECTILE,
(player->currentShield == PLAYER_SHIELD_DEKU) ||
((player->currentShield == PLAYER_SHIELD_HYLIAN) && LINK_IS_ADULT),
this) &&
this->collider.base.atFlags & AT_HIT && this->collider.base.atFlags & AT_TYPE_ENEMY &&
this->collider.base.atFlags & AT_BOUNCED) {
this->collider.base.atFlags &= ~(AT_HIT | AT_BOUNCED | AT_TYPE_ENEMY);
Expand Down
Loading