summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPhilip Dubé <159546+serprex@users.noreply.github.com>2026-08-17 17:24:03 +0000
committerGitHub <noreply@github.com>2026-08-17 17:24:03 +0000
commitbd8a5825f94bf226680ce009ce5a50948aa48a78 (patch)
tree99d308c0604fa356f899d1f382519b0b6c548e22
parent172b373fb5eb02a0412a256cba2197e54138c69e (diff)
Hookify masks / timeless equipment (#7088)
-rw-r--r--soh/soh/Enhancements/AdultMasks.cpp37
-rw-r--r--soh/soh/Enhancements/AdultMasks.h15
-rw-r--r--soh/soh/Enhancements/BunnyHood.cpp69
-rw-r--r--soh/soh/Enhancements/BunnyHood.h13
-rw-r--r--soh/soh/Enhancements/Cheats/TimelessEquipment.cpp15
-rw-r--r--soh/soh/Enhancements/PersistentMasks.cpp30
-rw-r--r--soh/soh/Enhancements/SpeedModifiers.cpp25
-rw-r--r--soh/soh/Enhancements/TimeSavers/MarketSneak.cpp56
-rw-r--r--soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h75
-rw-r--r--soh/soh/Enhancements/gameplaystats.cpp5
-rw-r--r--soh/soh/Enhancements/gameplaystats.h2
-rw-r--r--soh/soh/SohGui/SohMenuEnhancements.cpp6
-rw-r--r--soh/src/code/z_face_reaction.c9
-rw-r--r--soh/src/code/z_message_PAL.c4
-rw-r--r--soh/src/code/z_parameter.c15
-rw-r--r--soh/src/code/z_play.c20
-rw-r--r--soh/src/code/z_player_lib.c2
-rw-r--r--soh/src/code/z_sram.c4
-rw-r--r--soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c38
-rw-r--r--soh/src/overlays/actors/ovl_player_actor/z_player.c21
-rw-r--r--soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_equipment.c25
-rw-r--r--soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_item.c17
-rw-r--r--soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h21
23 files changed, 366 insertions, 158 deletions
diff --git a/soh/soh/Enhancements/AdultMasks.cpp b/soh/soh/Enhancements/AdultMasks.cpp
new file mode 100644
index 000000000..60d031422
--- /dev/null
+++ b/soh/soh/Enhancements/AdultMasks.cpp
@@ -0,0 +1,37 @@
+#include "soh/Enhancements/AdultMasks.h"
+#include "soh/Enhancements/game-interactor/GameInteractor.h"
+#include "soh/ShipInit.hpp"
+
+extern "C" {
+#include "z64.h"
+#include "macros.h"
+#include "variables.h"
+}
+
+bool Ship_MasksEquippableAsAdult() {
+ return CVarGetInteger(CVAR_ADULT_MASKS_NAME, 0) || CVarGetInteger(CVAR_CHEAT("TimelessEquipment"), 0);
+}
+
+static bool IsMask(int item) {
+ return item >= ITEM_MASK_KEATON && item <= ITEM_MASK_TRUTH;
+}
+
+static void RegisterAdultMasks() {
+ bool adultMasks = CVarGetInteger(CVAR_ADULT_MASKS_NAME, 0);
+
+ // Masks drop their child-only age requirement
+ COND_VB_SHOULD(VB_ITEM_MEETS_AGE_REQ, adultMasks, {
+ if (IsMask(va_arg(args, int))) {
+ *should = true;
+ }
+ });
+
+ // Same for the child trade slot, but only while it holds a mask
+ COND_VB_SHOULD(VB_SLOT_MEETS_AGE_REQ, adultMasks, {
+ if (va_arg(args, int) == SLOT_TRADE_CHILD && IsMask(INV_CONTENT(ITEM_TRADE_CHILD))) {
+ *should = true;
+ }
+ });
+}
+
+static RegisterShipInitFunc initFunc(RegisterAdultMasks, { CVAR_ADULT_MASKS_NAME });
diff --git a/soh/soh/Enhancements/AdultMasks.h b/soh/soh/Enhancements/AdultMasks.h
new file mode 100644
index 000000000..1417da283
--- /dev/null
+++ b/soh/soh/Enhancements/AdultMasks.h
@@ -0,0 +1,15 @@
+#pragma once
+
+#include <stdbool.h>
+
+#include "soh/cvar_prefixes.h"
+
+#define CVAR_ADULT_MASKS_NAME CVAR_ENHANCEMENT("AdultMasks")
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+bool Ship_MasksEquippableAsAdult();
+#ifdef __cplusplus
+};
+#endif
diff --git a/soh/soh/Enhancements/BunnyHood.cpp b/soh/soh/Enhancements/BunnyHood.cpp
new file mode 100644
index 000000000..175c8424c
--- /dev/null
+++ b/soh/soh/Enhancements/BunnyHood.cpp
@@ -0,0 +1,69 @@
+#include "soh/Enhancements/game-interactor/GameInteractor.h"
+#include "soh/ShipInit.hpp"
+#include "soh/Enhancements/BunnyHood.h"
+#include "soh/Enhancements/gameplaystats.h"
+
+extern "C" {
+#include "z64.h"
+#include "functions.h"
+#include "macros.h"
+#include "variables.h"
+extern PlayState* gPlayState;
+}
+
+BunnyHoodMode Ship_GetBunnyHoodMode() {
+ return (BunnyHoodMode)CVarGetInteger(CVAR_BUNNY_HOOD_NAME, BUNNY_HOOD_VANILLA);
+}
+
+float Ship_GetBunnyHoodRunFactor(Player* player) {
+ if (Ship_GetBunnyHoodMode() != BUNNY_HOOD_VANILLA && player->currentMask == PLAYER_MASK_BUNNY) {
+ return 1.5f;
+ }
+ return 1.0f;
+}
+
+float Ship_GetBunnyHoodJumpFactor(Player* player) {
+ if (Ship_GetBunnyHoodMode() == BUNNY_HOOD_FAST_AND_JUMP && player->currentMask == PLAYER_MASK_BUNNY) {
+ return 1.5f;
+ }
+ return 1.0f;
+}
+
+static void RegisterBunnyHood() {
+ bool bunnyHoodActive = Ship_GetBunnyHoodMode() != BUNNY_HOOD_VANILLA;
+
+ // Wearing the hood shouldn't lock you out of NPC interactions, so NPCs ignore it
+ COND_VB_SHOULD(VB_NPC_REACT_TO_MASK, bunnyHoodActive, {
+ u8 currentMask = va_arg(args, int);
+ if (currentMask == PLAYER_MASK_BUNNY) {
+ *should = false;
+ }
+ });
+
+ // Masks stay usable where trade items are restricted, so the hood can be equipped there
+ COND_VB_SHOULD(VB_DISABLE_TRADE_ITEM_BUTTON, bunnyHoodActive, {
+ u8 item = va_arg(args, int);
+ if (item >= ITEM_MASK_KEATON && item <= ITEM_MASK_TRUTH) {
+ *should = false;
+ }
+ });
+
+ // Same speed boost as running, for the "Move in first person" setting
+ COND_VB_SHOULD(VB_PLAYER_MODIFY_FIRST_PERSON_SPEED, bunnyHoodActive, {
+ Player* player = va_arg(args, Player*);
+ f32* movementSpeed = va_arg(args, f32*);
+ *movementSpeed *= Ship_GetBunnyHoodRunFactor(player);
+ });
+
+ // Gameplay stat: time spent wearing the hood
+ COND_HOOK(OnPlayerUpdate, bunnyHoodActive, []() {
+ if (gSaveContext.ship.stats.gameComplete || (IS_BOSS_RUSH && gSaveContext.ship.quest.data.bossRush.isPaused)) {
+ return;
+ }
+ if (GET_PLAYER(gPlayState)->currentMask == PLAYER_MASK_BUNNY) {
+ gSaveContext.ship.stats.count[COUNT_TIME_BUNNY_HOOD]++;
+ }
+ });
+}
+
+static RegisterShipInitFunc initFunc(RegisterBunnyHood, { CVAR_BUNNY_HOOD_NAME });
diff --git a/soh/soh/Enhancements/BunnyHood.h b/soh/soh/Enhancements/BunnyHood.h
new file mode 100644
index 000000000..52724c43f
--- /dev/null
+++ b/soh/soh/Enhancements/BunnyHood.h
@@ -0,0 +1,13 @@
+#pragma once
+
+#include "soh/Enhancements/enhancementTypes.h"
+#include "soh/cvar_prefixes.h"
+
+typedef struct Player Player;
+
+#define CVAR_BUNNY_HOOD_NAME CVAR_ENHANCEMENT("MMBunnyHood")
+
+BunnyHoodMode Ship_GetBunnyHoodMode();
+// Speed multipliers the effect applies to the player, 1.0f when it doesn't.
+float Ship_GetBunnyHoodRunFactor(Player* player);
+float Ship_GetBunnyHoodJumpFactor(Player* player);
diff --git a/soh/soh/Enhancements/Cheats/TimelessEquipment.cpp b/soh/soh/Enhancements/Cheats/TimelessEquipment.cpp
new file mode 100644
index 000000000..0897b7b97
--- /dev/null
+++ b/soh/soh/Enhancements/Cheats/TimelessEquipment.cpp
@@ -0,0 +1,15 @@
+#include "soh/Enhancements/game-interactor/GameInteractor.h"
+#include "soh/ShipInit.hpp"
+#include "soh/cvar_prefixes.h"
+
+#define CVAR_TIMELESS_EQUIPMENT_NAME CVAR_CHEAT("TimelessEquipment")
+
+static void RegisterTimelessEquipment() {
+ bool timelessEquipment = CVarGetInteger(CVAR_TIMELESS_EQUIPMENT_NAME, 0);
+
+ COND_VB_SHOULD(VB_PLAYER_MEETS_AGE_REQ, timelessEquipment, { *should = true; });
+ COND_VB_SHOULD(VB_ITEM_MEETS_AGE_REQ, timelessEquipment, { *should = true; });
+ COND_VB_SHOULD(VB_SLOT_MEETS_AGE_REQ, timelessEquipment, { *should = true; });
+}
+
+static RegisterShipInitFunc initFunc(RegisterTimelessEquipment, { CVAR_TIMELESS_EQUIPMENT_NAME });
diff --git a/soh/soh/Enhancements/PersistentMasks.cpp b/soh/soh/Enhancements/PersistentMasks.cpp
new file mode 100644
index 000000000..8baa1dfba
--- /dev/null
+++ b/soh/soh/Enhancements/PersistentMasks.cpp
@@ -0,0 +1,30 @@
+#include "soh/Enhancements/AdultMasks.h"
+#include "soh/Enhancements/game-interactor/GameInteractor.h"
+#include "soh/ShipInit.hpp"
+
+extern "C" {
+#include "z64.h"
+#include "macros.h"
+#include "variables.h"
+}
+
+#define CVAR_PERSISTENT_MASKS_NAME CVAR_ENHANCEMENT("PersistentMasks")
+
+static void RegisterPersistentMasks() {
+ bool persistentMasks = CVarGetInteger(CVAR_PERSISTENT_MASKS_NAME, 0);
+
+ // A mask normally comes off the moment it leaves the buttons, keep it on instead
+ COND_VB_SHOULD(VB_PLAYER_UNEQUIP_MASK_WITHOUT_BUTTON, persistentMasks, { *should = false; });
+
+ // Put the remembered mask back on whenever the player actor is built, so it
+ // survives loading zones, deaths and save loads
+ COND_ID_HOOK(OnActorInit, ACTOR_PLAYER, persistentMasks, [](void* actorPtr) {
+ // Forget the mask if it's gone from the inventory, or if adult can't wear it
+ if (INV_CONTENT(ITEM_TRADE_CHILD) == ITEM_SOLD_OUT || (LINK_IS_ADULT && !Ship_MasksEquippableAsAdult())) {
+ gSaveContext.ship.maskMemory = PLAYER_MASK_NONE;
+ }
+ ((Player*)actorPtr)->currentMask = gSaveContext.ship.maskMemory;
+ });
+}
+
+static RegisterShipInitFunc initFunc(RegisterPersistentMasks, { CVAR_PERSISTENT_MASKS_NAME });
diff --git a/soh/soh/Enhancements/SpeedModifiers.cpp b/soh/soh/Enhancements/SpeedModifiers.cpp
index 92f897453..9193485e9 100644
--- a/soh/soh/Enhancements/SpeedModifiers.cpp
+++ b/soh/soh/Enhancements/SpeedModifiers.cpp
@@ -1,7 +1,7 @@
#include "soh/Enhancements/game-interactor/GameInteractor.h"
#include "soh/ShipInit.hpp"
#include "soh/OTRGlobals.h"
-#include "soh/Enhancements/enhancementTypes.h"
+#include "soh/Enhancements/BunnyHood.h"
extern "C" {
#include "z64.h"
@@ -12,7 +12,6 @@ extern PlayState* gPlayState;
}
#define CVAR_SPEED_MODIFIER_VALUE_NAME CVAR_CHEAT("SpeedModifier.Value")
-#define CVAR_BUNNY_HOOD_NAME CVAR_ENHANCEMENT("MMBunnyHood")
static f32 GetSpeedModifierFactor(bool inputAvailable) {
f32 value = CVarGetFloat(CVAR_SPEED_MODIFIER_VALUE_NAME, 1.0f);
@@ -42,29 +41,13 @@ static f32 GetSpeedModifierJumpFactor() {
return GetSpeedModifierFactor(true);
}
-static f32 GetBunnyHoodRunFactor(Player* player) {
- if (CVarGetInteger(CVAR_BUNNY_HOOD_NAME, BUNNY_HOOD_VANILLA) != BUNNY_HOOD_VANILLA &&
- player->currentMask == PLAYER_MASK_BUNNY) {
- return 1.5f;
- }
- return 1.0f;
-}
-
-static f32 GetBunnyHoodJumpFactor(Player* player) {
- if (CVarGetInteger(CVAR_BUNNY_HOOD_NAME, BUNNY_HOOD_VANILLA) == BUNNY_HOOD_FAST_AND_JUMP &&
- player->currentMask == PLAYER_MASK_BUNNY) {
- return 1.5f;
- }
- return 1.0f;
-}
-
static bool ShouldAmplifyJump(Player* player) {
- return GetBunnyHoodJumpFactor(player) != 1.0f || GetSpeedModifierJumpFactor() != 1.0f;
+ return Ship_GetBunnyHoodJumpFactor(player) != 1.0f || GetSpeedModifierJumpFactor() != 1.0f;
}
static void RegisterSpeedModifiers() {
bool speedModifierActive = CVarGetFloat(CVAR_SPEED_MODIFIER_VALUE_NAME, 1.0f) != 1.0f;
- bool bunnyHoodActive = CVarGetInteger(CVAR_BUNNY_HOOD_NAME, BUNNY_HOOD_VANILLA) != BUNNY_HOOD_VANILLA;
+ bool bunnyHoodActive = Ship_GetBunnyHoodMode() != BUNNY_HOOD_VANILLA;
// Airborne (jump) velocity. z_player clamps linearVelocity to the vanilla run speed limit when this returns true;
// skip that clamp so the amplified running velocity carries into the jump.
@@ -90,7 +73,7 @@ static void RegisterSpeedModifiers() {
COND_VB_SHOULD(VB_PLAYER_MODIFY_RUN_SPEED, speedModifierActive || bunnyHoodActive, {
Player* player = va_arg(args, Player*);
f32* speedTarget = va_arg(args, f32*);
- *speedTarget *= GetBunnyHoodRunFactor(player) * GetSpeedModifierFactor(true);
+ *speedTarget *= Ship_GetBunnyHoodRunFactor(player) * GetSpeedModifierFactor(true);
});
// Swim speed multiplied in place. Called per speed z_player scales; bunny hood does not apply underwater.
diff --git a/soh/soh/Enhancements/TimeSavers/MarketSneak.cpp b/soh/soh/Enhancements/TimeSavers/MarketSneak.cpp
index b1567cb4c..ed1bd2b97 100644
--- a/soh/soh/Enhancements/TimeSavers/MarketSneak.cpp
+++ b/soh/soh/Enhancements/TimeSavers/MarketSneak.cpp
@@ -1,12 +1,21 @@
#include <soh/OTRGlobals.h>
+#include "soh/Enhancements/game-interactor/GameInteractor.h"
+#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
+#include "soh/ShipInit.hpp"
#include "soh/Enhancements/randomizer/randomizer.h"
+#include "soh/Enhancements/randomizer/randomizer_entrance.h"
extern "C" {
+#include <functions.h>
#include <variables.h>
+#include "overlays/actors/ovl_En_Heishi4/z_en_heishi4.h"
extern PlayState* gPlayState;
-}
-// RANDOTODO: Port the rest of the behavior for this enhancement here.
+// the guard's idle action func, restored once the sneak prompt is answered
+void func_80A56614(EnHeishi4* heishi, PlayState* play);
+
+u8 Randomizer_GetSettingValue(RandomizerSettingKey randoSettingKey);
+}
void BuildNightGuardMessage(uint16_t* textId, bool* loadFromMessageTable) {
// Other guards should not have their text overridden
@@ -22,9 +31,50 @@ void BuildNightGuardMessage(uint16_t* textId, bool* loadFromMessageTable) {
*loadFromMessageTable = false;
}
+// Answers the choice from BuildNightGuardMessage, letting child Link exit from the Market
+// entrance to Hyrule Field at night.
+extern "C" void EnHeishi4_MarketSneak(EnHeishi4* heishi, PlayState* play) {
+ if (Message_GetState(&play->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(play)) {
+ switch (play->msgCtx.choiceIndex) {
+ case 0: // yes
+ if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_ENTRANCES) != RO_GENERIC_OFF) {
+ play->nextEntranceIndex =
+ Entrance_OverrideNextIndex(ENTR_HYRULE_FIELD_ON_BRIDGE_SPAWN); // Market Entrance -> HF
+ } else {
+ play->nextEntranceIndex = ENTR_HYRULE_FIELD_PAST_BRIDGE_SPAWN; // HF Near bridge (OoT cutscene
+ // entrance) to not fall in the water
+ }
+ play->transitionTrigger = TRANS_TRIGGER_START;
+ play->transitionType = TRANS_TYPE_CIRCLE(TCA_STARBURST, TCC_WHITE, TCS_FAST);
+ gSaveContext.nextTransitionType = TRANS_TYPE_CIRCLE(TCA_STARBURST, TCC_WHITE, TCS_FAST);
+ heishi->actionFunc = func_80A56614;
+ break;
+ case 1: // no
+ heishi->actionFunc = func_80A56614;
+ break;
+ }
+ }
+}
+
void MarketSneak_Register() {
COND_ID_HOOK(OnOpenText, TEXT_MARKET_GUARD_NIGHT, CVarGetInteger(CVAR_ENHANCEMENT("MarketSneak"), 0),
BuildNightGuardMessage);
+
+ COND_VB_SHOULD(VB_MARKET_NIGHT_GUARD_SET_ACTION_AFTER_TALK, CVarGetInteger(CVAR_ENHANCEMENT("MarketSneak"), 0), {
+ EnHeishi4* heishi = va_arg(args, EnHeishi4*);
+ PlayState* play = va_arg(args, PlayState*);
+ Player* player = GET_PLAYER(play);
+
+ // Only allow sneaking when not wearing a mask as that triggers different dialogue. A mask NPCs
+ // don't react to (MM Bunny hood) is fine in that case.
+ if (player->currentMask != PLAYER_MASK_NONE &&
+ GameInteractor_Should(VB_NPC_REACT_TO_MASK, true, player->currentMask)) {
+ return;
+ }
+
+ heishi->actionFunc = EnHeishi4_MarketSneak;
+ *should = false;
+ });
}
-static RegisterShipInitFunc initFunc(MarketSneak_Register, { CVAR_ENHANCEMENT("MarketSneak") }); \ No newline at end of file
+static RegisterShipInitFunc initFunc(MarketSneak_Register, { CVAR_ENHANCEMENT("MarketSneak") });
diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
index 895ab4ae4..f4347d94a 100644
--- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
+++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
@@ -3755,4 +3755,79 @@ typedef enum {
// #### `args`
// - `*EnBox`
VB_CHEST_CONSIDER_CHEST_OPEN,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // Whether NPCs give their mask reaction text for the mask being worn.
+ // #### `args`
+ // - `u8 currentMask`
+ VB_NPC_REACT_TO_MASK,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // Whether a trade item button gets greyed out while trade items are restricted.
+ // #### `args`
+ // - `u8 item`
+ VB_DISABLE_TRADE_ITEM_BUTTON,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // Movement speed for the "Move in first person" setting, multiplied in place.
+ // #### `args`
+ // - `*Player`
+ // - `f32*` movementSpeed
+ VB_PLAYER_MODIFY_FIRST_PERSON_SPEED,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // Whether the market night guard goes back to idle once talked to, rather than
+ // a hook taking over his action func.
+ // #### `args`
+ // - `*EnHeishi4`
+ // - `*PlayState`
+ VB_MARKET_NIGHT_GUARD_SET_ACTION_AFTER_TALK,
+
+ // #### `result`
+ // ```c
+ // ageReq == AGE_REQ_NONE || ageReq == gSaveContext.linkAge
+ // ```
+ // Whether the player is the right age for something age gated.
+ // #### `args`
+ // - `u8 ageReq` an `AGE_REQ_*`, which for adult and child is the matching `LINK_AGE_*`
+ VB_PLAYER_MEETS_AGE_REQ,
+
+ // #### `result`
+ // ```c
+ // gItemAgeReqs[itemIndex] == AGE_REQ_NONE || gItemAgeReqs[itemIndex] == gSaveContext.linkAge
+ // ```
+ // Whether the player is the right age to hold an item.
+ // #### `args`
+ // - `u8 itemIndex`
+ VB_ITEM_MEETS_AGE_REQ,
+
+ // #### `result`
+ // ```c
+ // gSlotAgeReqs[slotIndex] == AGE_REQ_NONE || gSlotAgeReqs[slotIndex] == gSaveContext.linkAge
+ // ```
+ // Whether the player is the right age to use an inventory slot.
+ // #### `args`
+ // - `u8 slotIndex`
+ VB_SLOT_MEETS_AGE_REQ,
+
+ // #### `result`
+ // ```c
+ // this->currentMask != PLAYER_MASK_NONE
+ // ```
+ // Whether a worn mask comes off once it's no longer on a button.
+ // #### `args`
+ // - `*Player`
+ VB_PLAYER_UNEQUIP_MASK_WITHOUT_BUTTON,
} GIVanillaBehavior;
diff --git a/soh/soh/Enhancements/gameplaystats.cpp b/soh/soh/Enhancements/gameplaystats.cpp
index de5da8ab1..9602775b1 100644
--- a/soh/soh/Enhancements/gameplaystats.cpp
+++ b/soh/soh/Enhancements/gameplaystats.cpp
@@ -10,7 +10,7 @@
#include <string>
#include <spdlog/common.h>
-#include "soh/Enhancements/enhancementTypes.h"
+#include "soh/Enhancements/BunnyHood.h"
#include "soh/OTRGlobals.h"
extern "C" {
@@ -562,8 +562,7 @@ void DrawGameplayStatsCountsTab() {
GameplayStatsRow("Sword Swings:", formatIntGameplayStat(gSaveContext.ship.stats.count[COUNT_SWORD_SWINGS]));
GameplayStatsRow("Steps Taken:", formatIntGameplayStat(gSaveContext.ship.stats.count[COUNT_STEPS]));
// If using MM Bunny Hood enhancement, show how long it's been equipped (not counting pause time)
- if (CVarGetInteger(CVAR_ENHANCEMENT("MMBunnyHood"), BUNNY_HOOD_VANILLA) != BUNNY_HOOD_VANILLA ||
- gSaveContext.ship.stats.count[COUNT_TIME_BUNNY_HOOD] > 0) {
+ if (Ship_GetBunnyHoodMode() != BUNNY_HOOD_VANILLA || gSaveContext.ship.stats.count[COUNT_TIME_BUNNY_HOOD] > 0) {
GameplayStatsRow("Bunny Hood Time:",
formatTimestampGameplayStat(gSaveContext.ship.stats.count[COUNT_TIME_BUNNY_HOOD] / 2));
}
diff --git a/soh/soh/Enhancements/gameplaystats.h b/soh/soh/Enhancements/gameplaystats.h
index e1fa3b1d7..2a07775ca 100644
--- a/soh/soh/Enhancements/gameplaystats.h
+++ b/soh/soh/Enhancements/gameplaystats.h
@@ -231,7 +231,7 @@ typedef enum {
COUNT_SWORD_SWINGS, // z_player.c
COUNT_SIDEHOPS, // z_player.c
COUNT_BACKFLIPS, // z_player.c
- COUNT_TIME_BUNNY_HOOD, // z_play.c
+ COUNT_TIME_BUNNY_HOOD, // BunnyHood.cpp
COUNT_MAX
diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp
index a11e2679d..d9c600a43 100644
--- a/soh/soh/SohGui/SohMenuEnhancements.cpp
+++ b/soh/soh/SohGui/SohMenuEnhancements.cpp
@@ -1,6 +1,8 @@
#include "SohMenu.h"
#include <soh/Enhancements/enhancementTypes.h>
#include "soh/Enhancements/SwitchAge.h"
+#include "soh/Enhancements/AdultMasks.h"
+#include "soh/Enhancements/BunnyHood.h"
#include <soh/Enhancements/game-interactor/GameInteractor.h>
#include <soh/OTRGlobals.h>
#include <soh/Enhancements/cosmetics/authenticGfxPatches.h>
@@ -906,7 +908,7 @@ void SohMenu::AddMenuEnhancements() {
AddWidget(path, "Masks", WIDGET_SEPARATOR_TEXT);
AddWidget(path, "Bunny Hood Effect", WIDGET_CVAR_COMBOBOX)
- .CVar(CVAR_ENHANCEMENT("MMBunnyHood"))
+ .CVar(CVAR_BUNNY_HOOD_NAME)
.Options(ComboboxOptions()
.ComboMap(bunnyHoodEffectMap)
.Tooltip("Wearing the Bunny Hood grants a speed and jump boost like in Majora's Mask.\n"
@@ -914,7 +916,7 @@ void SohMenu::AddMenuEnhancements() {
"The effects of either option are not accounted for in Randomizer logic.\n"
"Also disables NPC's reactions to wearing the Bunny Hood."));
AddWidget(path, "Masks Equippable as Adult", WIDGET_CVAR_CHECKBOX)
- .CVar(CVAR_ENHANCEMENT("AdultMasks"))
+ .CVar(CVAR_ADULT_MASKS_NAME)
.Options(CheckboxOptions().Tooltip("Allows masks to be equipped normally from the pause menu as adult."));
AddWidget(path, "Persistent Masks", WIDGET_CVAR_CHECKBOX)
.CVar(CVAR_ENHANCEMENT("PersistentMasks"))
diff --git a/soh/src/code/z_face_reaction.c b/soh/src/code/z_face_reaction.c
index 0ea94b119..b6f1c6949 100644
--- a/soh/src/code/z_face_reaction.c
+++ b/soh/src/code/z_face_reaction.c
@@ -1,5 +1,5 @@
#include "global.h"
-#include "soh/Enhancements/enhancementTypes.h"
+#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
u16 sReactionTextIds[][PLAYER_MASK_MAX] = {
{ 0x0000, 0x7124, 0x7127, 0x7126, 0x7125, 0x7127, 0x7124, 0x7125, 0x7127 },
@@ -67,10 +67,9 @@ u16 sReactionTextIds[][PLAYER_MASK_MAX] = {
u16 Text_GetFaceReaction(PlayState* play, u32 reactionSet) {
u8 currentMask = Player_GetMask(play);
- if (CVarGetInteger(CVAR_ENHANCEMENT("MMBunnyHood"), BUNNY_HOOD_VANILLA) != BUNNY_HOOD_VANILLA &&
- currentMask == PLAYER_MASK_BUNNY) {
+ if (!GameInteractor_Should(VB_NPC_REACT_TO_MASK, true, currentMask)) {
return 0;
- } else {
- return sReactionTextIds[reactionSet][currentMask];
}
+
+ return sReactionTextIds[reactionSet][currentMask];
}
diff --git a/soh/src/code/z_message_PAL.c b/soh/src/code/z_message_PAL.c
index c6c2803b1..66a7baae2 100644
--- a/soh/src/code/z_message_PAL.c
+++ b/soh/src/code/z_message_PAL.c
@@ -2782,10 +2782,10 @@ void Message_OpenText(PlayState* play, u16 textId) {
// font->msgOffset), font->msgLength, __FILE__, __LINE__);
} else if (CVarGetInteger(CVAR_ENHANCEMENT("AskToEquip"), 0) &&
- (((LINK_IS_ADULT || CVarGetInteger(CVAR_CHEAT("TimelessEquipment"), 0)) &&
+ ((GameInteractor_Should(VB_PLAYER_MEETS_AGE_REQ, LINK_IS_ADULT, LINK_AGE_ADULT) &&
// 0C = Biggoron, 4B = Giant's, 4E = Mirror Shield, 50-51 = Tunics
(textId == 0x0C || textId == 0x4B || textId == 0x4E || textId == 0x50 || textId == 0x51)) ||
- ((!LINK_IS_ADULT || CVarGetInteger(CVAR_CHEAT("TimelessEquipment"), 0)) &&
+ (GameInteractor_Should(VB_PLAYER_MEETS_AGE_REQ, !LINK_IS_ADULT, LINK_AGE_CHILD) &&
// 4C = Deku Shield, A4 = Kokiri Sword
(textId == 0x4C || textId == 0xA4)) ||
// 4D == Hylian Shield
diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c
index 894a212a0..69fd6ded3 100644
--- a/soh/src/code/z_parameter.c
+++ b/soh/src/code/z_parameter.c
@@ -1143,13 +1143,14 @@ void func_80083108(PlayState* play) {
if (interfaceCtx->restrictions.tradeItems != 0) {
for (i = 1; i < ARRAY_COUNT(gSaveContext.equips.buttonItems); i++) {
- if ((CVarGetInteger(CVAR_ENHANCEMENT("MMBunnyHood"), BUNNY_HOOD_VANILLA) !=
- BUNNY_HOOD_VANILLA) &&
- (gSaveContext.equips.buttonItems[i] >= ITEM_MASK_KEATON) &&
- (gSaveContext.equips.buttonItems[i] <= ITEM_MASK_TRUTH)) {
- gSaveContext.buttonStatus[BUTTON_STATUS_INDEX(i)] = BTN_ENABLED;
- } else if ((gSaveContext.equips.buttonItems[i] >= ITEM_WEIRD_EGG) &&
- (gSaveContext.equips.buttonItems[i] <= ITEM_CLAIM_CHECK)) {
+ if ((gSaveContext.equips.buttonItems[i] >= ITEM_WEIRD_EGG) &&
+ (gSaveContext.equips.buttonItems[i] <= ITEM_CLAIM_CHECK)) {
+ if (!GameInteractor_Should(VB_DISABLE_TRADE_ITEM_BUTTON, true,
+ gSaveContext.equips.buttonItems[i])) {
+ gSaveContext.buttonStatus[BUTTON_STATUS_INDEX(i)] = BTN_ENABLED;
+ continue;
+ }
+
if (gSaveContext.buttonStatus[BUTTON_STATUS_INDEX(i)] == BTN_ENABLED) {
sp28 = 1;
}
diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c
index 4a92668c4..37fc299da 100644
--- a/soh/src/code/z_play.c
+++ b/soh/src/code/z_play.c
@@ -559,21 +559,6 @@ void Play_Init(GameState* thisx) {
Fault_AddClient(&D_801614B8, ZeldaArena_Display, NULL, NULL);
- // In order to keep masks equipped on first load, we need to pre-set the age reqs for the item and slot
- if (CVarGetInteger(CVAR_ENHANCEMENT("AdultMasks"), 0) || CVarGetInteger(CVAR_CHEAT("TimelessEquipment"), 0)) {
- for (int i = ITEM_MASK_KEATON; i <= ITEM_MASK_TRUTH; i += 1) {
- gItemAgeReqs[i] = AGE_REQ_NONE;
- }
- if (INV_CONTENT(ITEM_TRADE_CHILD) >= ITEM_MASK_KEATON && INV_CONTENT(ITEM_TRADE_CHILD) <= ITEM_MASK_TRUTH) {
- gSlotAgeReqs[SLOT_TRADE_CHILD] = AGE_REQ_NONE;
- }
- } else {
- for (int i = ITEM_MASK_KEATON; i <= ITEM_MASK_TRUTH; i += 1) {
- gItemAgeReqs[i] = AGE_REQ_CHILD;
- }
- gSlotAgeReqs[SLOT_TRADE_CHILD] = AGE_REQ_CHILD;
- }
-
// Handle Rocs Feather requirement
gItemAgeReqs[ITEM_ROCS_FEATHER] = AGE_REQ_NONE;
gSlotAgeReqs[SLOT_NAYRUS_LOVE] = AGE_REQ_NONE;
@@ -1150,11 +1135,6 @@ void Play_Update(PlayState* play) {
gSaveContext.ship.stats.playTimer++;
gSaveContext.ship.stats.sceneTimer++;
gSaveContext.ship.stats.roomTimer++;
-
- if (CVarGetInteger(CVAR_ENHANCEMENT("MMBunnyHood"), BUNNY_HOOD_VANILLA) != BUNNY_HOOD_VANILLA &&
- Player_GetMask(play) == PLAYER_MASK_BUNNY) {
- gSaveContext.ship.stats.count[COUNT_TIME_BUNNY_HOOD]++;
- }
}
if (play->actorCtx.freezeFlashTimer && (play->actorCtx.freezeFlashTimer-- < 5)) {
diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c
index c9d5ba5d8..62b003c98 100644
--- a/soh/src/code/z_player_lib.c
+++ b/soh/src/code/z_player_lib.c
@@ -782,7 +782,7 @@ s32 Player_GetStrength(void) {
return PLAYER_STR_NONE;
}
- if (CVarGetInteger(CVAR_CHEAT("TimelessEquipment"), 0) || LINK_IS_ADULT) {
+ if (GameInteractor_Should(VB_PLAYER_MEETS_AGE_REQ, LINK_IS_ADULT, LINK_AGE_ADULT)) {
return strengthUpgrade;
} else if (strengthUpgrade != 0) {
return PLAYER_STR_BRACELET;
diff --git a/soh/src/code/z_sram.c b/soh/src/code/z_sram.c
index ac93b9358..97c0a6612 100644
--- a/soh/src/code/z_sram.c
+++ b/soh/src/code/z_sram.c
@@ -143,10 +143,6 @@ void Sram_OpenSave() {
break;
}
- if (!CVarGetInteger(CVAR_ENHANCEMENT("PersistentMasks"), 0)) {
- gSaveContext.ship.maskMemory = PLAYER_MASK_NONE;
- }
-
osSyncPrintf("scene_no = %d\n", gSaveContext.entranceIndex);
osSyncPrintf(VT_RST);
diff --git a/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c b/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c
index 47848e10c..556e7d26a 100644
--- a/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c
+++ b/soh/src/overlays/actors/ovl_En_Heishi4/z_en_heishi4.c
@@ -1,7 +1,7 @@
#include "z_en_heishi4.h"
#include "objects/object_sd/object_sd.h"
#include "vt.h"
-#include "soh/OTRGlobals.h"
+#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS (ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_FRIENDLY)
@@ -21,7 +21,6 @@ void func_80A56900(EnHeishi4* this, PlayState* play);
void func_80A56994(EnHeishi4* this, PlayState* play);
void func_80A56A50(EnHeishi4* this, PlayState* play);
void func_80A56ACC(EnHeishi4* this, PlayState* play);
-void EnHeishi4_MarketSneak(EnHeishi4* this, PlayState* play);
const ActorInit En_Heishi4_InitVars = {
ACTOR_EN_HEISHI4,
@@ -333,14 +332,7 @@ void func_80A56B40(EnHeishi4* this, PlayState* play) {
return;
}
if (this->type == HEISHI4_AT_MARKET_NIGHT) {
- Player* player = GET_PLAYER(play);
- // Only allow sneaking when not wearing a mask as that triggers different dialogue. MM Bunny hood disables
- // these interactions, so bunny hood is fine in that case.
- if (CVarGetInteger(CVAR_ENHANCEMENT("MarketSneak"), 0) &&
- (player->currentMask == PLAYER_MASK_NONE ||
- (player->currentMask == PLAYER_MASK_BUNNY && CVarGetInteger(CVAR_ENHANCEMENT("MMBunnyHood"), 0)))) {
- this->actionFunc = EnHeishi4_MarketSneak;
- } else {
+ if (GameInteractor_Should(VB_MARKET_NIGHT_GUARD_SET_ACTION_AFTER_TALK, true, this, play)) {
this->actionFunc = func_80A56614;
return;
}
@@ -349,32 +341,6 @@ void func_80A56B40(EnHeishi4* this, PlayState* play) {
Actor_OfferTalkNearColChkInfoCylinder(&this->actor, play);
}
-/*Function that allows child Link to exit from Market entrance to Hyrule Field
-at night.
-*/
-void EnHeishi4_MarketSneak(EnHeishi4* this, PlayState* play) {
- if (Message_GetState(&play->msgCtx) == TEXT_STATE_CHOICE && Message_ShouldAdvance(play)) {
- switch (play->msgCtx.choiceIndex) {
- case 0: // yes
- if (IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_OVERWORLD_ENTRANCES) != RO_GENERIC_OFF) {
- play->nextEntranceIndex =
- Entrance_OverrideNextIndex(ENTR_HYRULE_FIELD_ON_BRIDGE_SPAWN); // Market Entrance -> HF
- } else {
- play->nextEntranceIndex = ENTR_HYRULE_FIELD_PAST_BRIDGE_SPAWN; // HF Near bridge (OoT cutscene
- // entrance) to not fall in the water
- }
- play->transitionTrigger = TRANS_TRIGGER_START;
- play->transitionType = TRANS_TYPE_CIRCLE(TCA_STARBURST, TCC_WHITE, TCS_FAST);
- gSaveContext.nextTransitionType = TRANS_TYPE_CIRCLE(TCA_STARBURST, TCC_WHITE, TCS_FAST);
- this->actionFunc = func_80A56614;
- break;
- case 1: // no
- this->actionFunc = func_80A56614;
- break;
- }
- }
-}
-
void EnHeishi4_Update(Actor* thisx, PlayState* play) {
EnHeishi4* this = (EnHeishi4*)thisx;
s32 pad;
diff --git a/soh/src/overlays/actors/ovl_player_actor/z_player.c b/soh/src/overlays/actors/ovl_player_actor/z_player.c
index f7c75fdf0..fe36b2f4b 100644
--- a/soh/src/overlays/actors/ovl_player_actor/z_player.c
+++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c
@@ -2497,7 +2497,7 @@ void Player_ProcessItemButtons(Player* this, PlayState* play) {
s32 item;
s32 i;
- if (this->currentMask != PLAYER_MASK_NONE && !CVarGetInteger(CVAR_ENHANCEMENT("PersistentMasks"), 0)) {
+ if (GameInteractor_Should(VB_PLAYER_UNEQUIP_MASK_WITHOUT_BUTTON, this->currentMask != PLAYER_MASK_NONE, this)) {
maskItemAction = this->currentMask - 1 + PLAYER_IA_MASK_KEATON;
bool hasOnDpad = false;
@@ -5557,10 +5557,6 @@ void func_8083A0F4(PlayState* play, Player* this) {
this->interactRangeActor->parent = &this->actor;
Player_SetupAction(play, this, Player_Action_WaitForCutscene, 0);
this->stateFlags1 |= PLAYER_STATE1_IN_CUTSCENE;
- if (!CVarGetInteger(CVAR_ENHANCEMENT("PersistentMasks"), 0) ||
- !CVarGetInteger(CVAR_ENHANCEMENT("AdultMasks"), 0)) {
- gSaveContext.ship.maskMemory = PLAYER_MASK_NONE;
- }
} else {
LinkAnimationHeader* anim;
@@ -9507,9 +9503,6 @@ void func_80843AE8(PlayState* play, Player* this) {
OnePointCutscene_Init(play, 9908, 125, &this->actor, CAM_ID_MAIN);
} else if (play->gameOverCtx.state == GAMEOVER_DEATH_WAIT_GROUND) {
play->gameOverCtx.state = GAMEOVER_DEATH_DELAY_MENU;
- if (!CVarGetInteger(CVAR_ENHANCEMENT("PersistentMasks"), 0)) {
- gSaveContext.ship.maskMemory = PLAYER_MASK_NONE;
- }
}
}
@@ -10820,13 +10813,6 @@ void Player_Init(Actor* thisx, PlayState* play2) {
Player_UseItem(play, this, ITEM_NONE);
Player_SetEquipmentData(play, this);
this->prevBoots = this->currentBoots;
- // keep masks thru loading zones
- if (CVarGetInteger(CVAR_ENHANCEMENT("PersistentMasks"), 0)) {
- if (INV_CONTENT(ITEM_TRADE_CHILD) == ITEM_SOLD_OUT) {
- gSaveContext.ship.maskMemory = PLAYER_MASK_NONE;
- }
- this->currentMask = gSaveContext.ship.maskMemory;
- }
Player_InitCommon(this, play, gPlayerSkelHeaders[((void)0, gSaveContext.linkAge)]);
// `giObjectSegment` is used for both "get item" objects and title cards. The maximum size for
// get item objects is 0x2000 (see the assert in func_8083AE40), and the maximum size for
@@ -12674,10 +12660,7 @@ s16 func_8084ABD8(PlayState* play, Player* this, s32 arg2, s16 arg3) {
if (CVarGetInteger(CVAR_SETTING("MoveInFirstPerson"), 0) &&
CVarGetInteger(CVAR_SETTING("Controls.RightStickAim"), 0)) {
f32 movementSpeed = LINK_IS_ADULT ? 9.0f : 8.25f;
- if (CVarGetInteger(CVAR_ENHANCEMENT("MMBunnyHood"), BUNNY_HOOD_VANILLA) != BUNNY_HOOD_VANILLA &&
- this->currentMask == PLAYER_MASK_BUNNY) {
- movementSpeed *= 1.5f;
- }
+ GameInteractor_Should(VB_PLAYER_MODIFY_FIRST_PERSON_SPEED, true, this, &movementSpeed);
f32 relX = (sControlInput->rel.stick_x / 10 * -invertXAxisMulti);
f32 relY = (sControlInput->rel.stick_y / 10);
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 f0e7626f2..a8144cc75 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
@@ -773,29 +773,27 @@ void KaleidoScope_DrawEquipment(PlayState* play) {
for (rowStart = 0, j = 0, temp = 0, i = 0; i < 4; i++, rowStart += 4, j += 16) {
gSPVertex(POLY_OPA_DISP++, &pauseCtx->equipVtx[j], 16, 0);
- bool drawGreyItems = !CVarGetInteger(CVAR_CHEAT("TimelessEquipment"), 0);
if (LINK_AGE_IN_YEARS == YEARS_CHILD) {
point = CUR_UPG_VALUE(sChildUpgrades[i]);
if ((point != 0) && (CUR_UPG_VALUE(sChildUpgrades[i]) != 0)) {
+ int upgradeItem = sChildUpgradeItemBases[i] + point - 1;
// Grey Out the Gauntlets as Child
// Grey Out Strength Upgrades when Disabled and the Toggle Strength Option is on
- if ((drawGreyItems &&
- ((sChildUpgradeItemBases[i] + CUR_UPG_VALUE(sChildUpgrades[i]) - 1) == ITEM_GAUNTLETS_SILVER ||
- (sChildUpgradeItemBases[i] + CUR_UPG_VALUE(sChildUpgrades[i]) - 1) == ITEM_GAUNTLETS_GOLD)) ||
+ if (!CHECK_AGE_REQ_ITEM(upgradeItem) ||
(CVarGetInteger(CVAR_ENHANCEMENT("ToggleStrength"), 0) &&
CVarGetInteger(CVAR_ENHANCEMENT("StrengthDisabled"), 0) && sChildUpgrades[i] == UPG_STRENGTH)) {
gDPSetGrayscaleColor(POLY_OPA_DISP++, 109, 109, 109, 255);
gSPGrayscale(POLY_OPA_DISP++, true);
}
- KaleidoScope_DrawQuadTextureRGBA32(play->state.gfxCtx,
- gItemIcons[sChildUpgradeItemBases[i] + point - 1], 32, 32, 0);
+ KaleidoScope_DrawQuadTextureRGBA32(play->state.gfxCtx, gItemIcons[upgradeItem], 32, 32, 0);
gSPGrayscale(POLY_OPA_DISP++, false);
}
} else {
if ((i == 0) &&
(CUR_UPG_VALUE(sAdultUpgrades[i]) ==
0)) { // If the player doesn't have the bow, load the current slingshot ammo upgrade instead.
- if (drawGreyItems) {
+ // Check the base item, the upgrade level can be 0 here
+ if (!CHECK_AGE_REQ_ITEM(sChildUpgradeItemBases[i])) {
gDPSetGrayscaleColor(POLY_OPA_DISP++, 109, 109, 109, 255); // Grey Out Slingshot Bullet Bags
gSPGrayscale(POLY_OPA_DISP++, true);
}
@@ -804,19 +802,18 @@ void KaleidoScope_DrawEquipment(PlayState* play) {
32, 32, 0);
gSPGrayscale(POLY_OPA_DISP++, false);
} else if (CUR_UPG_VALUE(sAdultUpgrades[i]) != 0) {
- // Grey Out the Goron Bracelet when Not Randomized and Toggle Strength Option is off
+ int upgradeItem = sAdultUpgradeItemBases[i] + CUR_UPG_VALUE(sAdultUpgrades[i]) - 1;
+ // Grey Out upgrades the wrong age, ie the Goron Bracelet as Adult,
+ // unless rando or the Toggle Strength Option still has a use for it
// Grey Out Strength Upgrades when Disabled and the Toggle Strength Option is on
- if ((drawGreyItems &&
- (((sAdultUpgradeItemBases[i] + CUR_UPG_VALUE(sAdultUpgrades[i]) - 1) == ITEM_BRACELET &&
- !(IS_RANDO) && !CVarGetInteger(CVAR_ENHANCEMENT("ToggleStrength"), 0)))) ||
+ if ((!CHECK_AGE_REQ_ITEM(upgradeItem) && !(IS_RANDO) &&
+ !CVarGetInteger(CVAR_ENHANCEMENT("ToggleStrength"), 0)) ||
(CVarGetInteger(CVAR_ENHANCEMENT("ToggleStrength"), 0) &&
CVarGetInteger(CVAR_ENHANCEMENT("StrengthDisabled"), 0) && sAdultUpgrades[i] == UPG_STRENGTH)) {
gDPSetGrayscaleColor(POLY_OPA_DISP++, 109, 109, 109, 255);
gSPGrayscale(POLY_OPA_DISP++, true);
}
- KaleidoScope_DrawQuadTextureRGBA32(
- play->state.gfxCtx, gItemIcons[sAdultUpgradeItemBases[i] + CUR_UPG_VALUE(sAdultUpgrades[i]) - 1],
- 32, 32, 0);
+ KaleidoScope_DrawQuadTextureRGBA32(play->state.gfxCtx, gItemIcons[upgradeItem], 32, 32, 0);
gSPGrayscale(POLY_OPA_DISP++, false);
}
}
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 0c9881678..073e10c54 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
@@ -355,23 +355,6 @@ void KaleidoScope_HandleItemCycles(PlayState* play) {
: INV_CONTENT(ITEM_TRADE_CHILD) + 1),
true);
- // the slot age requirement for the child trade slot has to be updated
- // in case it currently holds a mask
- // to allow adult link to wear it if the setting is enabled
- gSlotAgeReqs[SLOT_TRADE_CHILD] =
- (CVarGetInteger(CVAR_ENHANCEMENT("AdultMasks"), 0) || CVarGetInteger(CVAR_CHEAT("TimelessEquipment"), 0)) &&
- INV_CONTENT(ITEM_TRADE_CHILD) >= ITEM_MASK_KEATON && INV_CONTENT(ITEM_TRADE_CHILD) <= ITEM_MASK_TRUTH
- ? AGE_REQ_NONE
- : AGE_REQ_CHILD;
-
- // also update the age requirements for the masks itself
- for (int i = ITEM_MASK_KEATON; i <= ITEM_MASK_TRUTH; i += 1) {
- gItemAgeReqs[i] =
- CVarGetInteger(CVAR_ENHANCEMENT("AdultMasks"), 0) || CVarGetInteger(CVAR_CHEAT("TimelessEquipment"), 0)
- ? AGE_REQ_NONE
- : AGE_REQ_CHILD;
- }
-
// handle the adult trade select
KaleidoScope_HandleItemCycleExtras(play, SLOT_TRADE_ADULT,
IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE),
diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h
index 6c5fe7ba8..95493ab09 100644
--- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h
+++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h
@@ -3,6 +3,7 @@
#include <libultraship/libultra.h>
#include "global.h"
+#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
extern u8 gAmmoItems[];
extern s16 D_8082AAEC[];
@@ -20,9 +21,23 @@ extern u8 gAreaGsFlags[];
#define AGE_REQ_CHILD LINK_AGE_CHILD
#define AGE_REQ_NONE 9
-#define CHECK_AGE_REQ_EQUIP(i, j) (CVarGetInteger(CVAR_CHEAT("TimelessEquipment"), 0) || (gEquipAgeReqs[i][j] == AGE_REQ_NONE) || (gEquipAgeReqs[i][j] == ((void)0, gSaveContext.linkAge)))
-#define CHECK_AGE_REQ_SLOT(slotIndex) (CVarGetInteger(CVAR_CHEAT("TimelessEquipment"), 0) || (gSlotAgeReqs[slotIndex] == AGE_REQ_NONE) || gSlotAgeReqs[slotIndex] == ((void)0, gSaveContext.linkAge))
-#define CHECK_AGE_REQ_ITEM(itemIndex) (CVarGetInteger(CVAR_CHEAT("TimelessEquipment"), 0) || (gItemAgeReqs[itemIndex] == AGE_REQ_NONE) || (gItemAgeReqs[itemIndex] == gSaveContext.linkAge))
+#define CHECK_AGE_REQ_EQUIP(i, j) \
+ GameInteractor_Should(VB_PLAYER_MEETS_AGE_REQ, \
+ (gEquipAgeReqs[i][j] == AGE_REQ_NONE) || \
+ (gEquipAgeReqs[i][j] == ((void)0, gSaveContext.linkAge)), \
+ gEquipAgeReqs[i][j])
+
+#define CHECK_AGE_REQ_SLOT(slotIndex) \
+ GameInteractor_Should(VB_SLOT_MEETS_AGE_REQ, \
+ (gSlotAgeReqs[slotIndex] == AGE_REQ_NONE) || \
+ (gSlotAgeReqs[slotIndex] == ((void)0, gSaveContext.linkAge)), \
+ slotIndex)
+
+#define CHECK_AGE_REQ_ITEM(itemIndex) \
+ GameInteractor_Should(VB_ITEM_MEETS_AGE_REQ, \
+ (gItemAgeReqs[itemIndex] == AGE_REQ_NONE) || \
+ (gItemAgeReqs[itemIndex] == gSaveContext.linkAge), \
+ itemIndex)
void KaleidoScope_DrawQuestStatus(PlayState* play, GraphicsContext* gfxCtx);
s32 KaleidoScope_UpdateQuestStatusPoint(PauseContext* pauseCtx, s32 point);