diff --git a/soh/assets/custom/objects/gameplay_keep/gPlayerAnim_link_rocs_feather_jump b/soh/assets/custom/objects/gameplay_keep/gPlayerAnim_link_rocs_feather_jump
new file mode 100644
index 00000000000..331911d025b
Binary files /dev/null and b/soh/assets/custom/objects/gameplay_keep/gPlayerAnim_link_rocs_feather_jump differ
diff --git a/soh/assets/custom/objects/gameplay_keep/gPlayerAnim_link_rocs_feather_jump_Data b/soh/assets/custom/objects/gameplay_keep/gPlayerAnim_link_rocs_feather_jump_Data
new file mode 100644
index 00000000000..e739fd7b146
Binary files /dev/null and b/soh/assets/custom/objects/gameplay_keep/gPlayerAnim_link_rocs_feather_jump_Data differ
diff --git a/soh/assets/custom/objects/object_rocs_feather/eff_unknown_12.i8 b/soh/assets/custom/objects/object_rocs_feather/eff_unknown_12.i8
new file mode 100644
index 00000000000..e305f366839
Binary files /dev/null and b/soh/assets/custom/objects/object_rocs_feather/eff_unknown_12.i8 differ
diff --git a/soh/assets/custom/objects/object_rocs_feather/eff_unknown_12_i8 b/soh/assets/custom/objects/object_rocs_feather/eff_unknown_12_i8
new file mode 100644
index 00000000000..e305f366839
Binary files /dev/null and b/soh/assets/custom/objects/object_rocs_feather/eff_unknown_12_i8 differ
diff --git a/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL b/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL
new file mode 100644
index 00000000000..2e1d14239bb
--- /dev/null
+++ b/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL_tri_0 b/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL_tri_0
new file mode 100644
index 00000000000..94927832fce
--- /dev/null
+++ b/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL_tri_0
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL_tri_1 b/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL_tri_1
new file mode 100644
index 00000000000..9ab8f972b93
--- /dev/null
+++ b/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL_tri_1
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL_vtx_0 b/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL_vtx_0
new file mode 100644
index 00000000000..6b168144985
--- /dev/null
+++ b/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL_vtx_0
@@ -0,0 +1,64 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL_vtx_1 b/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL_vtx_1
new file mode 100644
index 00000000000..44ab971573e
--- /dev/null
+++ b/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL_vtx_1
@@ -0,0 +1,81 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL_vtx_cull b/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL_vtx_cull
new file mode 100644
index 00000000000..e43cf3bda13
--- /dev/null
+++ b/soh/assets/custom/objects/object_rocs_feather/gGiRocsFeatherDL_vtx_cull
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/soh/assets/custom/objects/object_rocs_feather/mat_gGiRocsFeatherDL_feather_feather b/soh/assets/custom/objects/object_rocs_feather/mat_gGiRocsFeatherDL_feather_feather
new file mode 100644
index 00000000000..e21e92ff897
--- /dev/null
+++ b/soh/assets/custom/objects/object_rocs_feather/mat_gGiRocsFeatherDL_feather_feather
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/soh/assets/custom/objects/object_rocs_feather/mat_gGiRocsFeatherDL_feather_stem b/soh/assets/custom/objects/object_rocs_feather/mat_gGiRocsFeatherDL_feather_stem
new file mode 100644
index 00000000000..b523babf20d
--- /dev/null
+++ b/soh/assets/custom/objects/object_rocs_feather/mat_gGiRocsFeatherDL_feather_stem
@@ -0,0 +1,22 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/soh/assets/custom/objects/object_rocs_feather/model.xml b/soh/assets/custom/objects/object_rocs_feather/model.xml
new file mode 100644
index 00000000000..5d798a1ab9a
--- /dev/null
+++ b/soh/assets/custom/objects/object_rocs_feather/model.xml
@@ -0,0 +1,10 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/soh/assets/custom/textures/icon_item_static/gRocsFeatherTex.rgba32.png b/soh/assets/custom/textures/icon_item_static/gRocsFeatherTex.rgba32.png
new file mode 100644
index 00000000000..b2e4f99da1d
Binary files /dev/null and b/soh/assets/custom/textures/icon_item_static/gRocsFeatherTex.rgba32.png differ
diff --git a/soh/assets/custom/textures/item_name_static/gRocsFeatherItemNameENGTex.ia4.png b/soh/assets/custom/textures/item_name_static/gRocsFeatherItemNameENGTex.ia4.png
new file mode 100644
index 00000000000..bc72f3ced5b
Binary files /dev/null and b/soh/assets/custom/textures/item_name_static/gRocsFeatherItemNameENGTex.ia4.png differ
diff --git a/soh/assets/custom/textures/item_name_static/gRocsFeatherItemNameFRATex.ia4.png b/soh/assets/custom/textures/item_name_static/gRocsFeatherItemNameFRATex.ia4.png
new file mode 100644
index 00000000000..dc42bdc072d
Binary files /dev/null and b/soh/assets/custom/textures/item_name_static/gRocsFeatherItemNameFRATex.ia4.png differ
diff --git a/soh/assets/custom/textures/item_name_static/gRocsFeatherItemNameGERTex.ia4.png b/soh/assets/custom/textures/item_name_static/gRocsFeatherItemNameGERTex.ia4.png
new file mode 100644
index 00000000000..5842acfeb59
Binary files /dev/null and b/soh/assets/custom/textures/item_name_static/gRocsFeatherItemNameGERTex.ia4.png differ
diff --git a/soh/assets/soh_assets.h b/soh/assets/soh_assets.h
index 3c3ddcf5a7e..f973abd132d 100644
--- a/soh/assets/soh_assets.h
+++ b/soh/assets/soh_assets.h
@@ -348,6 +348,9 @@ static const ALIGN_ASSET(2) char gKeyringKeysGanonsCastleMQDL[] = dgKeyringKeysG
#define dgHouseKeyDL "__OTR__objects/object_housekey/gHouseKeyDL"
static const ALIGN_ASSET(2) char gHouseKeyDL[] = dgHouseKeyDL;
+#define dgGiRocsFeatherDL "__OTR__objects/object_rocs_feather/gGiRocsFeatherDL"
+static const ALIGN_ASSET(2) char gGiRocsFeatherDL[] = dgGiRocsFeatherDL;
+
// overlays
#define dgOptionsDividerChangeLangVtx "__OTR__overlays/ovl_file_choose/gOptionsDividerChangeLangVtx"
static const ALIGN_ASSET(2) char gOptionsDividerChangeLangVtx[] = dgOptionsDividerChangeLangVtx;
@@ -431,6 +434,18 @@ static const ALIGN_ASSET(2) char gFileSelLanguageFRATex[] = dgFileSelLanguageFRA
#define dgFileSelLanguageGERTex "__OTR__textures/title_static/gFileSelLanguageGERTex"
static const ALIGN_ASSET(2) char gFileSelLanguageGERTex[] = dgFileSelLanguageGERTex;
+#define dgRocsFeatherTex "__OTR__textures/icon_item_static/gRocsFeatherTex"
+static const ALIGN_ASSET(2) char gRocsFeatherTex[] = dgRocsFeatherTex;
+
+#define dgRocsFeatherItemNameENGTex "__OTR__textures/item_name_static/gRocsFeatherItemNameENGTex"
+static const ALIGN_ASSET(2) char gRocsFeatherItemNameENGTex[] = dgRocsFeatherItemNameENGTex;
+
+#define dgRocsFeatherItemNameGERTex "__OTR__textures/item_name_static/gRocsFeatherItemNameGERTex"
+static const ALIGN_ASSET(2) char gRocsFeatherItemNameGERTex[] = dgRocsFeatherItemNameGERTex;
+
+#define dgRocsFeatherItemNameFRATex "__OTR__textures/item_name_static/gRocsFeatherItemNameFRATex"
+static const ALIGN_ASSET(2) char gRocsFeatherItemNameFRATex[] = dgRocsFeatherItemNameFRATex;
+
#define dgEmptyTexture "__OTR__textures/virtual/gEmptyTexture"
static const ALIGN_ASSET(2) char gEmptyTexture[] = dgEmptyTexture;
@@ -453,6 +468,10 @@ static const ALIGN_ASSET(2) char gLinkAdultGoronTunicSkel[] = dgLinkAdultGoronTu
#define dgLinkAdultZoraTunicSkel "__OTR__objects/object_link_boy_zora/gLinkAdultZoraTunicSkel"
static const ALIGN_ASSET(2) char gLinkAdultZoraTunicSkel[] = dgLinkAdultZoraTunicSkel;
+// Animations
+#define dgPlayerAnim_link_rocs_feather_jump "__OTR__objects/gameplay_keep/gPlayerAnim_link_rocs_feather_jump"
+static const ALIGN_ASSET(2) char gPlayerAnim_link_rocs_feather_jump[] = dgPlayerAnim_link_rocs_feather_jump;
+
// LUS Logo
#define dgShipLogoDL "__OTR__textures/nintendo_rogo_static/gShipLogoDL"
static const ALIGN_ASSET(2) char gShipLogoDL[] = dgShipLogoDL;
diff --git a/soh/include/functions.h b/soh/include/functions.h
index 11dbf8c811a..f9e7650f4a3 100644
--- a/soh/include/functions.h
+++ b/soh/include/functions.h
@@ -2452,11 +2452,13 @@ void Font_LoadOrderedFontNTSC(Font* font);
// #endregion
// #region SOH [General]
-
void Interface_RandoRestoreSwordless(void);
s32 Ship_CalcShouldDrawAndUpdate(PlayState* play, Actor* actor, Vec3f* projectedPos, f32 projectedW, bool* shouldDraw,
bool* shouldUpdate);
+// #region SOH [Rocs Feather]
+void func_80838940(Player* this, LinkAnimationHeader* anim, f32 arg2, PlayState* play, u16 sfxId);
+
// #endregion
#ifdef __cplusplus
diff --git a/soh/include/variables.h b/soh/include/variables.h
index 27ad4584d24..e296017aab3 100644
--- a/soh/include/variables.h
+++ b/soh/include/variables.h
@@ -103,7 +103,7 @@ extern "C"
extern u16 gUpgradeCapacities[8][4];
extern u32 gGsFlagsMasks[4];
extern u32 gGsFlagsShifts[4];
- extern void* gItemIcons[0x82];
+ extern void* gItemIcons[157];
extern u8 gItemAgeReqs[];
extern u8 gSlotAgeReqs[];
extern u8 gItemSlots[56];
diff --git a/soh/include/z64item.h b/soh/include/z64item.h
index b1fa897fe6e..9bbb8bb30f2 100644
--- a/soh/include/z64item.h
+++ b/soh/include/z64item.h
@@ -309,6 +309,7 @@ typedef enum {
/* 0x99 */ ITEM_STICK_UPGRADE_30,
/* 0x9A */ ITEM_NUT_UPGRADE_30,
/* 0x9B */ ITEM_NUT_UPGRADE_40,
+ /* */ ITEM_ROCS_FEATHER,
/* 0xFC */ ITEM_LAST_USED = 0xFC,
/* 0xFE */ ITEM_NONE_FE = 0xFE,
/* 0xFF */ ITEM_NONE = 0xFF
@@ -592,6 +593,7 @@ typedef enum {
/* 0x7A */ GID_SONG_TIME,
/* 0x7B */ GID_SONG_STORM,
/* 0x7C */ GID_TRIFORCE_PIECE,
+ /* 0x7C */ GID_ROCS_FEATHER,
/* */ GID_FISHING_POLE,
/* 0x7C */ GID_MAXIMUM
diff --git a/soh/soh/Enhancements/debugconsole.cpp b/soh/soh/Enhancements/debugconsole.cpp
index ff40f149a02..6a1e619d462 100644
--- a/soh/soh/Enhancements/debugconsole.cpp
+++ b/soh/soh/Enhancements/debugconsole.cpp
@@ -394,7 +394,7 @@ static bool GiveItemHandler(std::shared_ptr Console, const std::v
if (args[1].compare("vanilla") == 0) {
getItemEntry = ItemTableManager::Instance->RetrieveItemEntry(MOD_NONE, std::stoi(args[2]));
} else if (args[1].compare("randomizer") == 0) {
- getItemEntry = ItemTableManager::Instance->RetrieveItemEntry(MOD_RANDOMIZER, std::stoi(args[2]));
+ getItemEntry = Rando::StaticData::RetrieveItem((RandomizerGet)std::stoi(args[2])).GetGIEntry_Copy();
} else {
ERROR_MESSAGE("[SOH] Invalid argument passed, must be 'vanilla' or 'randomizer'");
return 1;
diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp
index 463f900e0d4..7f6e7512283 100644
--- a/soh/soh/Enhancements/debugger/debugSaveEditor.cpp
+++ b/soh/soh/Enhancements/debugger/debugSaveEditor.cpp
@@ -433,7 +433,16 @@ void DrawInventoryTab() {
uint8_t item = gSaveContext.inventory.items[index];
PushStyleButton(Colors::DarkGray);
- if (item != ITEM_NONE) {
+ if (item == ITEM_ROCS_FEATHER) {
+ auto ret = ImGui::ImageButton(
+ "ROCS_FEATHER",
+ Ship::Context::GetInstance()->GetWindow()->GetGui()->GetTextureByName("ROCS_FEATHER"),
+ ImVec2(48.0f, 48.0f), ImVec2(0, 0), ImVec2(1, 1));
+ if (ret) {
+ selectedIndex = index;
+ ImGui::OpenPopup(itemPopupPicker);
+ }
+ } else if (item != ITEM_NONE) {
const ItemMapEntry& slotEntry = itemMapping.find(item)->second;
auto ret = ImGui::ImageButton(
slotEntry.name.c_str(),
@@ -1736,4 +1745,6 @@ void SaveEditorWindow::DrawElement() {
}
void SaveEditorWindow::InitElement() {
+ Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture("ROCS_FEATHER", gRocsFeatherTex,
+ ImVec4(1, 1, 1, 1));
}
diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
index f604c9c7954..a2c30b46de1 100644
--- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
+++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h
@@ -2382,6 +2382,22 @@ typedef enum {
// - `*Color_RGB8`
VB_APPLY_TUNIC_COLOR,
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // #### `args`
+ // - `*int32_t` // ItemID
+ VB_USE_ITEM,
+
+ // #### `result`
+ // ```c
+ // true
+ // ```
+ // #### `args`
+ // - `*int16_t` // pauseCtx->namedItem
+ VB_DRAW_CUSTOM_ITEM_NAME,
+
} GIVanillaBehavior;
#endif
diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp
index c74c0ff23b9..170c726265c 100644
--- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp
+++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp
@@ -1964,6 +1964,16 @@ void StaticData::HintTable_Init_Item() {
CustomMessage("a gold fragment", /*german*/"ein Goldfragment", /*french*/"un fragment d'or")});
// /*spanish*/un fragmento dorado
+ hintTextTable[RHT_ROCS_FEATHER] = HintText(CustomMessage("Roc's Feather", /*german*/"Roc's Feather", /*french*/"Roc's Feather"),
+ // /*spanish*/un fragmento de la Trifuerza
+ {}, {
+ CustomMessage("a feather", /*german*/"a feather", /*french*/"a feather"),
+ // /*spanish*/un trígono del triunfo
+ CustomMessage("a chicken wing", /*german*/"a chicken wing", /*french*/"a chicken wing"),
+ // /*spanish*/un porción de queso
+ CustomMessage("a blue wing", /*german*/"a blue wing", /*french*/"a blue wing")});
+ // /*spanish*/un fragmento dorado
+
hintTextTable[RHT_GOHMA_SOUL] = HintText(CustomMessage("the soul of Gohma", /*german*/"Gohmas Seele", /*french*/"l'Âme de Gohma"),
{
CustomMessage("something webbed", /*german*/"etwas Verwobenes", /*french*/"un truc entoilé")
diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp
index ec90b0619a8..0ddf13ed7ff 100644
--- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp
+++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp
@@ -511,6 +511,14 @@ void GenerateItemPool() {
ctx->PlaceItemInLocation(RC_GANON, RG_TRIFORCE); // Win condition
}
+ if (ctx->GetOption(RSK_ROCS_FEATHER)) {
+ ctx->possibleIceTrapModels.push_back(RG_ROCS_FEATHER);
+ AddItemToMainPool(RG_ROCS_FEATHER);
+ if (ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) {
+ AddItemToPool(PendingJunkPool, RG_ROCS_FEATHER);
+ }
+ }
+
// Fixed item locations
ctx->PlaceItemInLocation(RC_HC_ZELDAS_LETTER, RG_ZELDAS_LETTER);
diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.cpp b/soh/soh/Enhancements/randomizer/3drando/shops.cpp
index 06fe5790f83..0c0ce66abe8 100644
--- a/soh/soh/Enhancements/randomizer/3drando/shops.cpp
+++ b/soh/soh/Enhancements/randomizer/3drando/shops.cpp
@@ -27,7 +27,7 @@ PriceSettingsStruct::PriceSettingsStruct(RandomizerSettingKey _main, RandomizerS
affordable = _affordable;
}
-static std::array, 0xF1> trickNameTable; // Table of trick names for ice traps
+static std::array, RG_MAX> trickNameTable; // Table of trick names for ice traps
bool initTrickNames = false; // Indicates if trick ice trap names have been initialized yet
// Set vanilla shop item locations before potentially shuffling
@@ -887,6 +887,11 @@ void InitTrickNames() {
Text{ "Triforce Shard", "Éclat de Triforce", "Triforce-Fragment" }, // "Triforce Shard"
Text{ "Shiny Rock", "Caillou Brillant", "glänzender Stein" }, // "Shiny Rock"
};
+ trickNameTable[RG_ROCS_FEATHER] = {
+ Text{ "Chicken Wing", "Chicken Wing", "Chicken Wing" }, // "Chicken Wing"
+ Text{ "Roc's Leg", "Roc's Leg", "Roc's Leg" }, // "Roc's Leg"
+ Text{ "Roc's Fapper", "Roc's Fapper", "Roc's Fapper" }, // "Roc's Fapper"
+ };
trickNameTable[RG_GOHMA_SOUL] = {
Text{ "Spider Sense", "Sens de l'Araignée", "Spinnensinn" },
Text{ "Deku Spirit", "Parasite Mojo", "Deku Geist" },
@@ -1159,7 +1164,7 @@ void InitTrickNames() {
}
// Generate a fake name for the ice trap based on the item it's displayed as
-Text GetIceTrapName(uint8_t id) {
+Text GetIceTrapName(uint16_t id) {
// If the trick names table has not been initialized, do so
if (!initTrickNames) {
InitTrickNames();
diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.hpp b/soh/soh/Enhancements/randomizer/3drando/shops.hpp
index 81c4a43582b..799856b6301 100644
--- a/soh/soh/Enhancements/randomizer/3drando/shops.hpp
+++ b/soh/soh/Enhancements/randomizer/3drando/shops.hpp
@@ -27,4 +27,4 @@ extern std::vector GetMinVanillaShopItems(int total_replaced);
extern uint16_t GetRandomPrice(Rando::Location* loc, PriceSettingsStruct priceSettings);
extern uint16_t GetCheapBalancedPrice();
extern int GetShopsanityReplaceAmount();
-extern Text GetIceTrapName(uint8_t id);
+extern Text GetIceTrapName(uint16_t id);
diff --git a/soh/soh/Enhancements/randomizer/RocsFeather.cpp b/soh/soh/Enhancements/randomizer/RocsFeather.cpp
new file mode 100644
index 00000000000..fecb2489e10
--- /dev/null
+++ b/soh/soh/Enhancements/randomizer/RocsFeather.cpp
@@ -0,0 +1,100 @@
+#include
+#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
+#include "soh/ShipInit.hpp"
+#include
+
+extern "C" {
+#include
+#include "functions.h"
+#include "variables.h"
+#include "macros.h"
+#include "objects/gameplay_keep/gameplay_keep.h"
+extern PlayState* gPlayState;
+}
+
+#define MAX_ROCS_USES 1
+
+static uint8_t rocsUseCount = 0;
+static uint8_t groundTimer = 0;
+static f32 effectsScale = 1.0f;
+
+void RegisterRocsFeather() {
+ bool shouldRegister = IS_RANDO && RAND_GET_OPTION(RSK_ROCS_FEATHER);
+
+ COND_HOOK(OnPlayerUpdate, shouldRegister, []() {
+ Player* player = GET_PLAYER(gPlayState);
+
+ // Reset Rocs count when touching the ground for 3+ frames
+ if (player->actor.bgCheckFlags & 1) {
+ if (groundTimer <= 3) {
+ groundTimer++;
+ }
+ } else {
+ groundTimer = 0;
+ }
+
+ if (groundTimer >= 3) {
+ rocsUseCount = 0;
+ }
+ });
+
+ COND_VB_SHOULD(VB_USE_ITEM, shouldRegister, {
+ int32_t* usedItem = va_arg(args, int32_t*);
+ Player* player = GET_PLAYER(gPlayState);
+
+ // Roc's Feather behaviour
+ if (*usedItem == ITEM_ROCS_FEATHER) {
+ *should = false;
+
+ if (rocsUseCount < MAX_ROCS_USES) {
+ rocsUseCount++;
+
+ func_80838940(player, (LinkAnimationHeader*)&gPlayerAnim_link_rocs_feather_jump, 5.8f, gPlayState, 0);
+
+ // Actionvar needed to prevent weird animation morph
+ player->av2.actionVar2 = 1;
+
+ // Move player forward on Roc's use
+ player->linearVelocity = 5.0f;
+ player->actor.world.rot.y = player->yaw = player->actor.shape.rot.y;
+
+ if (gSaveContext.linkAge == LINK_AGE_CHILD) {
+ player->actor.velocity.y = 7.0f;
+ effectsScale = 1.0f;
+ } else {
+ player->actor.velocity.y = 7.5f;
+ effectsScale = 1.5f;
+ }
+
+ Vec3f effectsPos = player->actor.home.pos;
+ effectsPos.y += 3;
+
+ EffectSsGRipple_Spawn(gPlayState, &effectsPos, 200 * effectsScale, 300 * effectsScale, 1);
+ EffectSsGSplash_Spawn(gPlayState, &effectsPos, NULL, NULL, 0, 150 * effectsScale);
+
+ // Remove hopping state when using Roc's after sidehop/backflip to allow grabbing ledges again
+ player->stateFlags2 &= ~(PLAYER_STATE2_HOPPING);
+
+ Player_PlaySfx(&player->actor, NA_SE_PL_SKIP);
+ }
+ }
+ });
+
+ COND_VB_SHOULD(VB_DRAW_CUSTOM_ITEM_NAME, shouldRegister, {
+ u32 namedItem = va_arg(args, u32);
+ if (namedItem == ITEM_ROCS_FEATHER) {
+ *should = true;
+ const char* textureName = gRocsFeatherItemNameENGTex;
+
+ if (gSaveContext.language == LANGUAGE_GER) {
+ textureName = gRocsFeatherItemNameGERTex;
+ } else if (gSaveContext.language == LANGUAGE_FRA) {
+ textureName = gRocsFeatherItemNameFRATex;
+ }
+
+ memcpy(gPlayState->pauseCtx.nameSegment, textureName, strlen(textureName) + 1);
+ }
+ });
+}
+
+static RegisterShipInitFunc registerRocsFeather(RegisterRocsFeather, { "IS_RANDO" });
diff --git a/soh/soh/Enhancements/randomizer/RocsFeatherCycle.c b/soh/soh/Enhancements/randomizer/RocsFeatherCycle.c
new file mode 100644
index 00000000000..31dfb903040
--- /dev/null
+++ b/soh/soh/Enhancements/randomizer/RocsFeatherCycle.c
@@ -0,0 +1,25 @@
+#include "soh/Enhancements/randomizer/RocsFeatherCycle.h"
+#include "functions.h"
+#include "variables.h"
+#include "macros.h"
+
+uint8_t Enhancement_GetNextNayrusItem() {
+ if (INV_CONTENT(ITEM_NAYRUS_LOVE) == ITEM_NAYRUS_LOVE && Flags_GetRandomizerInf(RAND_INF_OBTAINED_ROCS_FEATHER)) {
+ return ITEM_ROCS_FEATHER;
+ }
+ if (INV_CONTENT(ITEM_NAYRUS_LOVE) == ITEM_ROCS_FEATHER && Flags_GetRandomizerInf(RAND_INF_OBTAINED_NAYRUS_LOVE)) {
+ return ITEM_NAYRUS_LOVE;
+ }
+ return ITEM_NONE;
+}
+
+uint8_t Enhancement_GetPrevNayrusItem() {
+ if (INV_CONTENT(ITEM_NAYRUS_LOVE) == ITEM_NAYRUS_LOVE && Flags_GetRandomizerInf(RAND_INF_OBTAINED_ROCS_FEATHER)) {
+ return ITEM_ROCS_FEATHER;
+ }
+ if (INV_CONTENT(ITEM_NAYRUS_LOVE) == ITEM_ROCS_FEATHER && Flags_GetRandomizerInf(RAND_INF_OBTAINED_NAYRUS_LOVE)) {
+ return ITEM_NAYRUS_LOVE;
+ }
+
+ return ITEM_NONE;
+}
diff --git a/soh/soh/Enhancements/randomizer/RocsFeatherCycle.h b/soh/soh/Enhancements/randomizer/RocsFeatherCycle.h
new file mode 100644
index 00000000000..859cb5ab6fa
--- /dev/null
+++ b/soh/soh/Enhancements/randomizer/RocsFeatherCycle.h
@@ -0,0 +1,9 @@
+#ifndef ROCS_FEATHER_H
+#define ROCS_FEATHER_H
+
+#include
+
+uint8_t Enhancement_GetNextNayrusItem();
+uint8_t Enhancement_GetPrevNayrusItem();
+
+#endif
diff --git a/soh/soh/Enhancements/randomizer/draw.cpp b/soh/soh/Enhancements/randomizer/draw.cpp
index 7d266d78bef..dd709a65e91 100644
--- a/soh/soh/Enhancements/randomizer/draw.cpp
+++ b/soh/soh/Enhancements/randomizer/draw.cpp
@@ -489,6 +489,21 @@ extern "C" void Randomizer_DrawMysteryItem(PlayState* play, GetItemEntry* getIte
CLOSE_DISPS(play->state.gfxCtx);
}
+extern "C" void Randomizer_DrawRocsFeather(PlayState* play, GetItemEntry* getItemEntry) {
+ Color_RGB8 color = { 0, 60, 100 };
+
+ OPEN_DISPS(play->state.gfxCtx);
+
+ Gfx_SetupDL_25Xlu(play->state.gfxCtx);
+
+ gSPMatrix(POLY_XLU_DISP++, Matrix_NewMtx(play->state.gfxCtx, (char*)__FILE__, __LINE__),
+ G_MTX_MODELVIEW | G_MTX_LOAD);
+
+ gSPDisplayList(POLY_XLU_DISP++, (Gfx*)gGiRocsFeatherDL);
+
+ CLOSE_DISPS(play->state.gfxCtx);
+}
+
Gfx* GetEmptyDlist(GraphicsContext* gfxCtx) {
Gfx* dListHead;
Gfx* dList;
diff --git a/soh/soh/Enhancements/randomizer/draw.h b/soh/soh/Enhancements/randomizer/draw.h
index 8483a7bf966..ca50cf7e127 100644
--- a/soh/soh/Enhancements/randomizer/draw.h
+++ b/soh/soh/Enhancements/randomizer/draw.h
@@ -27,6 +27,7 @@ void Randomizer_DrawMysteryItem(PlayState* play, GetItemEntry* getItemEntry);
void Randomizer_DrawBombchuBagInLogic(PlayState* play, GetItemEntry* getItemEntry);
void Randomizer_DrawBombchuBag(PlayState* play, GetItemEntry* getItemEntry);
void Randomizer_DrawOverworldKey(PlayState* play, GetItemEntry* getItemEntry);
+void Randomizer_DrawRocsFeather(PlayState* play, GetItemEntry* getItemEntry);
#define GET_ITEM_MYSTERY \
{ \
diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp
index 9aa9b140096..4ab27394398 100644
--- a/soh/soh/Enhancements/randomizer/item_list.cpp
+++ b/soh/soh/Enhancements/randomizer/item_list.cpp
@@ -403,6 +403,8 @@ void Rando::StaticData::InitItemTable() {
itemTable[RG_MAGIC_SINGLE] = Item(RG_MAGIC_SINGLE, Text{ "Magic Meter", "Jauge de Magie", "Magische Kraft" }, ITEMTYPE_ITEM, 0x8A, true, LOGIC_PROGRESSIVE_MAGIC, RHT_MAGIC_SINGLE, RG_MAGIC_SINGLE, OBJECT_GI_MAGICPOT, GID_MAGIC_SMALL, 0xE4, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER);
itemTable[RG_MAGIC_DOUBLE] = Item(RG_MAGIC_DOUBLE, Text{ "Enhanced Magic Meter", "Jauge de Magie améliorée", "Verb. Magische Kraft" }, ITEMTYPE_ITEM, 0x8A, true, LOGIC_PROGRESSIVE_MAGIC, RHT_MAGIC_DOUBLE, RG_MAGIC_DOUBLE, OBJECT_GI_MAGICPOT, GID_MAGIC_LARGE, 0xE8, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER);
itemTable[RG_TRIFORCE_PIECE] = Item(RG_TRIFORCE_PIECE, Text{ "Triforce Piece", "Triforce Piece", "Triforce-Splitter" }, ITEMTYPE_ITEM, 0xDF, true, LOGIC_TRIFORCE_PIECES, RHT_TRIFORCE_PIECE, RG_TRIFORCE_PIECE, OBJECT_GI_BOMB_2, GID_TRIFORCE_PIECE, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER);
+ itemTable[RG_ROCS_FEATHER] = Item(RG_ROCS_FEATHER, Text{ "Roc's Feather", "Roc's Feather", "Roc's Feather" }, ITEMTYPE_ITEM, 0xE0, true, LOGIC_ROCS_FEATHER, RHT_ROCS_FEATHER, RG_ROCS_FEATHER, OBJECT_GI_BOMB_2, GID_ROCS_FEATHER, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER);
+ itemTable[RG_ROCS_FEATHER].SetCustomDrawFunc(Randomizer_DrawRocsFeather);
// clang-format on
diff --git a/soh/soh/Enhancements/randomizer/option_descriptions.cpp b/soh/soh/Enhancements/randomizer/option_descriptions.cpp
index 1f6b71a881a..03d02089d06 100644
--- a/soh/soh/Enhancements/randomizer/option_descriptions.cpp
+++ b/soh/soh/Enhancements/randomizer/option_descriptions.cpp
@@ -762,6 +762,9 @@ void Settings::CreateOptionDescriptions() {
mOptionDescriptions[RSK_SUNLIGHT_ARROWS] =
"Light Arrows can be used to light up the sun switches instead of using the Mirror Shield. "
"Item placement logic will respect this option, so it might be required to use this to progress.";
+ mOptionDescriptions[RSK_ROCS_FEATHER] =
+ "Adds Roc's Feather to the item pool. Roc's Feather is a custom item granting the player a jump on demand. "
+ "The jump can also be used when already in mid-air. Roc's Feather is not considered by logic.";
mOptionDescriptions[RSK_SLINGBOW_BREAK_BEEHIVES] =
"Allows Slingshot and Bow to break beehives when Beehive Shuffle is turned on.";
mOptionDescriptions[RSK_LOGIC_RULES] =
diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp
index de17327f19b..5c8e457de00 100644
--- a/soh/soh/Enhancements/randomizer/randomizer.cpp
+++ b/soh/soh/Enhancements/randomizer/randomizer.cpp
@@ -65,6 +65,7 @@ const std::string Randomizer::triforcePieceMessageTableID = "RandomizerTriforceP
const std::string Randomizer::NaviRandoMessageTableID = "RandomizerNavi";
const std::string Randomizer::IceTrapRandoMessageTableID = "RandomizerIceTrap";
const std::string Randomizer::randoMiscHintsTableID = "RandomizerMiscHints";
+const std::string Randomizer::RocsFeatherMessageTableID = "RandomizerRocsFeather";
static const char* englishRupeeNames[188] = {
"[P]",
@@ -980,7 +981,13 @@ ItemObtainability Randomizer::GetItemObtainabilityFromRandomizerGet(RandomizerGe
case RG_FARORES_WIND:
return INV_CONTENT(ITEM_FARORES_WIND) == ITEM_NONE ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
case RG_NAYRUS_LOVE:
- return INV_CONTENT(ITEM_NAYRUS_LOVE) == ITEM_NONE ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
+ if (!GetRandoSettingValue(RSK_ROCS_FEATHER)) {
+ return INV_CONTENT(ITEM_NAYRUS_LOVE) == ITEM_NONE ? CAN_OBTAIN : CANT_OBTAIN_ALREADY_HAVE;
+ } else {
+ return Flags_GetRandomizerInf(RAND_INF_OBTAINED_NAYRUS_LOVE) ? CANT_OBTAIN_ALREADY_HAVE : CAN_OBTAIN;
+ }
+ case RG_ROCS_FEATHER:
+ return Flags_GetRandomizerInf(RAND_INF_OBTAINED_ROCS_FEATHER) ? CANT_OBTAIN_ALREADY_HAVE : CAN_OBTAIN;
// Bottles
case RG_EMPTY_BOTTLE:
@@ -4904,6 +4911,22 @@ CustomMessage Randomizer::GetTriforcePieceMessage() {
return messageEntry;
}
+void CreateRocsFeatherMessage() {
+ CustomMessage RocsFeatherMessage = {
+ { "You found %cRoc's Feather%w!", "You found %cRoc's Feather%w!", "You found %cRoc's Feather%w!" },
+ };
+ CustomMessageManager* customMessageManager = CustomMessageManager::Instance;
+ customMessageManager->AddCustomMessageTable(Randomizer::RocsFeatherMessageTableID);
+ customMessageManager->CreateMessage(Randomizer::RocsFeatherMessageTableID, 0, RocsFeatherMessage);
+}
+
+CustomMessage Randomizer::GetRocsFeatherMessage() {
+ CustomMessage messageEntry =
+ CustomMessageManager::Instance->RetrieveMessage(Randomizer::RocsFeatherMessageTableID, 0);
+ messageEntry.Format();
+ return messageEntry;
+}
+
void CreateNaviRandoMessages() {
CustomMessage NaviMessages[NUM_NAVI_MESSAGES] = {
@@ -5882,6 +5905,7 @@ void Randomizer::CreateCustomMessages() {
} };
CreateGetItemMessages(getItemMessages);
CreateRupeeMessages();
+ CreateRocsFeatherMessage();
CreateTriforcePieceMessages();
CreateNaviRandoMessages();
CreateFireTempleGoronMessages();
@@ -6318,6 +6342,12 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) {
INV_CONTENT(ITEM_NUT) = ITEM_NUT;
AMMO(ITEM_NUT) = static_cast(CUR_CAPACITY(UPG_NUTS));
break;
+ case RG_ROCS_FEATHER:
+ Flags_SetRandomizerInf(RAND_INF_OBTAINED_ROCS_FEATHER);
+ if (INV_CONTENT(ITEM_NAYRUS_LOVE) == ITEM_NONE) {
+ INV_CONTENT(ITEM_NAYRUS_LOVE) = ITEM_ROCS_FEATHER;
+ }
+ break;
default:
LUSLOG_WARN("Randomizer_Item_Give didn't have behaviour specified for getItemId=%d", item);
assert(false);
diff --git a/soh/soh/Enhancements/randomizer/randomizer.h b/soh/soh/Enhancements/randomizer/randomizer.h
index 54c85307b6f..30e5f2c0dcd 100644
--- a/soh/soh/Enhancements/randomizer/randomizer.h
+++ b/soh/soh/Enhancements/randomizer/randomizer.h
@@ -38,6 +38,7 @@ class Randomizer {
static const std::string NaviRandoMessageTableID;
static const std::string IceTrapRandoMessageTableID;
static const std::string randoMiscHintsTableID;
+ static const std::string RocsFeatherMessageTableID;
static Sprite* GetSeedTexture(uint8_t index);
bool SpoilerFileExists(const char* spoilerFileName);
@@ -75,6 +76,7 @@ class Randomizer {
static CustomMessage GetRupeeMessage(u16 rupeeTextId);
static CustomMessage GetIceTrapMessage();
static CustomMessage GetTriforcePieceMessage();
+ static CustomMessage GetRocsFeatherMessage();
};
#ifdef __cplusplus
diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h
index 02bffe53ed8..dca6faa9ef5 100644
--- a/soh/soh/Enhancements/randomizer/randomizerTypes.h
+++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h
@@ -210,6 +210,7 @@ typedef enum {
LOGIC_OCARINA_C_LEFT_BUTTON,
LOGIC_OCARINA_C_RIGHT_BUTTON,
LOGIC_TRIFORCE_PIECES,
+ LOGIC_ROCS_FEATHER,
LOGIC_CAN_BORROW_MASKS,
LOGIC_BORROW_SKULL_MASK,
LOGIC_BORROW_SPOOKY_MASK,
@@ -4333,6 +4334,10 @@ typedef enum {
RG_BACK_TOWER_KEY,
RG_HYLIA_LAB_KEY,
RG_FISHING_HOLE_KEY,
+
+ // Custom Items
+ RG_ROCS_FEATHER,
+
// Logic Only
RG_DISTANT_SCARECROW,
RG_STICKS,
@@ -5454,6 +5459,7 @@ typedef enum {
RHT_DEKU_STICK_CAPACITY_20,
RHT_DEKU_STICK_CAPACITY_30,
RHT_TRIFORCE_PIECE,
+ RHT_ROCS_FEATHER,
RHT_GOHMA_SOUL,
RHT_KING_DODONGO_SOUL,
RHT_BARINADE_SOUL,
@@ -6238,6 +6244,7 @@ typedef enum {
RSK_SHUFFLE_SONG_FAIRIES,
RSK_LOCK_OVERWORLD_DOORS,
RSK_SHUFFLE_GRASS,
+ RSK_ROCS_FEATHER,
RSK_MAX
} RandomizerSettingKey;
diff --git a/soh/soh/Enhancements/randomizer/randomizer_inf.h b/soh/soh/Enhancements/randomizer/randomizer_inf.h
index 8742805a6f2..b566e8ee5c9 100644
--- a/soh/soh/Enhancements/randomizer/randomizer_inf.h
+++ b/soh/soh/Enhancements/randomizer/randomizer_inf.h
@@ -2016,4 +2016,6 @@ DEFINE_RAND_INF(RAND_INF_DEKU_TREE_QUEEN_GOHMA_GRASS_6)
DEFINE_RAND_INF(RAND_INF_DEKU_TREE_QUEEN_GOHMA_GRASS_7)
DEFINE_RAND_INF(RAND_INF_DEKU_TREE_QUEEN_GOHMA_GRASS_8)
// End Grass
-DEFINE_RAND_INF(RAND_INF_OBTAINED_RUTOS_LETTER)
\ No newline at end of file
+DEFINE_RAND_INF(RAND_INF_OBTAINED_RUTOS_LETTER)
+DEFINE_RAND_INF(RAND_INF_OBTAINED_NAYRUS_LOVE)
+DEFINE_RAND_INF(RAND_INF_OBTAINED_ROCS_FEATHER)
diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp
index cd32cc4b144..49646997ff4 100644
--- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp
+++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp
@@ -136,6 +136,10 @@ std::vector triforcePieces = {
ITEM_TRACKER_ITEM(RG_TRIFORCE_PIECE, 0, DrawItem),
};
+std::vector rocsFeather = {
+ ITEM_TRACKER_ITEM(RG_ROCS_FEATHER, 0, DrawItem),
+};
+
std::vector bossSoulItems = {
ITEM_TRACKER_ITEM(RG_GOHMA_SOUL, 0, DrawItem), ITEM_TRACKER_ITEM(RG_KING_DODONGO_SOUL, 0, DrawItem),
ITEM_TRACKER_ITEM(RG_BARINADE_SOUL, 0, DrawItem), ITEM_TRACKER_ITEM(RG_PHANTOM_GANON_SOUL, 0, DrawItem),
@@ -809,6 +813,16 @@ void DrawItem(ItemTrackerItem item) {
hasItem = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT);
itemName = "Triforce Piece";
break;
+ case ITEM_NAYRUS_LOVE:
+ if (IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_ROCS_FEATHER)) {
+ hasItem = Flags_GetRandomizerInf(RAND_INF_OBTAINED_NAYRUS_LOVE);
+ }
+ break;
+ case RG_ROCS_FEATHER:
+ actualItemId = item.id;
+ hasItem = Flags_GetRandomizerInf(RAND_INF_OBTAINED_ROCS_FEATHER);
+ itemName = "Roc's Feather";
+ break;
case RG_GOHMA_SOUL:
actualItemId = item.id;
hasItem = Flags_GetRandomizerInf(RAND_INF_GOHMA_SOUL);
@@ -1404,6 +1418,9 @@ void UpdateVectors() {
SECTION_DISPLAY_MAIN_WINDOW) {
mainWindowItems.insert(mainWindowItems.end(), dungeonItems.begin(), dungeonItems.end());
}
+ if (IS_RANDO && RAND_GET_OPTION(RSK_ROCS_FEATHER)) {
+ mainWindowItems.insert(mainWindowItems.end(), rocsFeather.begin(), rocsFeather.end());
+ }
// if we're adding greg to the misc window,
// and misc isn't on the main window,
@@ -2120,4 +2137,9 @@ void RegisterItemTrackerWidgets() {
SohGui::mSohMenu->AddSearchWidget({ hookshotIdentWidget, "Randomizer", "Item Tracker", "General Settings" });
}
+void RegisterItemTracker() {
+ COND_HOOK(OnLoadFile, true, [](int32_t fileNum) { shouldUpdateVectors = true; });
+}
+
+static RegisterShipInitFunc registerItemTracker(RegisterItemTracker);
static RegisterMenuInitFunc menuInitFunc(RegisterItemTrackerWidgets);
diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp
index 265066a7eb5..1d451dc31b3 100644
--- a/soh/soh/Enhancements/randomizer/settings.cpp
+++ b/soh/soh/Enhancements/randomizer/settings.cpp
@@ -309,6 +309,7 @@ void Settings::CreateOptions() {
// TODO: Compasses show rewards/woth, maps show dungeon mode
OPT_BOOL(RSK_BLUE_FIRE_ARROWS, "Blue Fire Arrows", CVAR_RANDOMIZER_SETTING("BlueFireArrows"), mOptionDescriptions[RSK_BLUE_FIRE_ARROWS]);
OPT_BOOL(RSK_SUNLIGHT_ARROWS, "Sunlight Arrows", CVAR_RANDOMIZER_SETTING("SunlightArrows"), mOptionDescriptions[RSK_SUNLIGHT_ARROWS]);
+ OPT_BOOL(RSK_ROCS_FEATHER, "Roc's Feather", CVAR_RANDOMIZER_SETTING("RocsFeather"), mOptionDescriptions[RSK_ROCS_FEATHER]);
OPT_U8(RSK_INFINITE_UPGRADES, "Infinite Upgrades", {"Off", "Progressive", "Condensed Progressive"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("InfiniteUpgrades"), mOptionDescriptions[RSK_INFINITE_UPGRADES]);
OPT_BOOL(RSK_SKELETON_KEY, "Skeleton Key", CVAR_RANDOMIZER_SETTING("SkeletonKey"), mOptionDescriptions[RSK_SKELETON_KEY]);
OPT_BOOL(RSK_SLINGBOW_BREAK_BEEHIVES, "Slingshot/Bow Can Break Beehives", CVAR_RANDOMIZER_SETTING("SlingBowBeehives"), mOptionDescriptions[RSK_SLINGBOW_BREAK_BEEHIVES]);
@@ -1423,6 +1424,7 @@ void Settings::CreateOptions() {
&mOptions[RSK_ENABLE_BOMBCHU_DROPS],
&mOptions[RSK_BLUE_FIRE_ARROWS],
&mOptions[RSK_SUNLIGHT_ARROWS],
+ &mOptions[RSK_ROCS_FEATHER],
&mOptions[RSK_INFINITE_UPGRADES],
&mOptions[RSK_SKELETON_KEY],
&mOptions[RSK_SLINGBOW_BREAK_BEEHIVES],
diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp
index 2aadd55d207..77168865112 100644
--- a/soh/soh/OTRGlobals.cpp
+++ b/soh/soh/OTRGlobals.cpp
@@ -2435,6 +2435,8 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) {
messageEntry = Randomizer::GetIceTrapMessage();
} else if (player->getItemEntry.getItemId == RG_TRIFORCE_PIECE) {
messageEntry = Randomizer::GetTriforcePieceMessage();
+ } else if (player->getItemEntry.getItemId == RG_ROCS_FEATHER) {
+ messageEntry = Randomizer::GetRocsFeatherMessage();
} else {
messageEntry = Randomizer_GetCustomGetItemMessage(player);
}
diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp
index 9317f8cbc4e..ead8a06a166 100644
--- a/soh/soh/SaveManager.cpp
+++ b/soh/soh/SaveManager.cpp
@@ -1084,6 +1084,9 @@ void SaveManager::InitFileMaxed() {
gSaveContext.entranceIndex = ENTR_HYRULE_FIELD_PAST_BRIDGE_SPAWN;
gSaveContext.sceneFlags[5].swch = 0x40000000;
+
+ Flags_SetRandomizerInf(RAND_INF_OBTAINED_NAYRUS_LOVE);
+ Flags_SetRandomizerInf(RAND_INF_OBTAINED_ROCS_FEATHER);
}
#if defined(__WIIU__) || defined(__SWITCH__)
diff --git a/soh/soh/SohGui/ImGuiUtils.cpp b/soh/soh/SohGui/ImGuiUtils.cpp
index c80293a53c0..69690ff539d 100644
--- a/soh/soh/SohGui/ImGuiUtils.cpp
+++ b/soh/soh/SohGui/ImGuiUtils.cpp
@@ -133,11 +133,9 @@ std::map gregMapping = {
{ ITEM_RUPEE_GREEN, { ITEM_RUPEE_GREEN, "ITEM_RUPEE_GREEN", "ITEM_RUPEE_GREEN_Faded", gRupeeCounterIconTex } }
};
-std::map triforcePieceMapping = {
- { RG_TRIFORCE_PIECE, { RG_TRIFORCE_PIECE, "RG_TRIFORCE_PIECE", "RG_TRIFORCE_PIECE_Faded", gTriforcePieceTex } }
-};
-
-std::map bossSoulMapping = {
+std::map customItemsMapping = {
+ { RG_TRIFORCE_PIECE, { RG_TRIFORCE_PIECE, "RG_TRIFORCE_PIECE", "RG_TRIFORCE_PIECE_Faded", gTriforcePieceTex } },
+ { RG_ROCS_FEATHER, { RG_ROCS_FEATHER, "RG_ROCS_FEATHER", "RG_ROCS_FEATHER_Faded", gRocsFeatherTex } },
{ RG_GOHMA_SOUL, { RG_GOHMA_SOUL, "RG_GOHMA_SOUL", "RG_GOHMA_SOUL_Faded", gBossSoulTex } },
{ RG_KING_DODONGO_SOUL,
{ RG_KING_DODONGO_SOUL, "RG_KING_DODONGO_SOUL", "RG_KING_DODONGO_SOUL_Faded", gBossSoulTex } },
@@ -216,14 +214,7 @@ void RegisterImGuiItemIcons() {
entry.second.texturePath, gregFadedGreen);
}
- for (const auto& entry : triforcePieceMapping) {
- Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(entry.second.name, entry.second.texturePath,
- ImVec4(1, 1, 1, 1));
- Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(
- entry.second.nameFaded, entry.second.texturePath, ImVec4(1, 1, 1, 0.3f));
- }
-
- for (const auto& entry : bossSoulMapping) {
+ for (const auto& entry : customItemsMapping) {
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(entry.second.name, entry.second.texturePath,
ImVec4(1, 1, 1, 1));
Ship::Context::GetInstance()->GetWindow()->GetGui()->LoadGuiTexture(
diff --git a/soh/soh/SohGui/ImGuiUtils.h b/soh/soh/SohGui/ImGuiUtils.h
index 2206b4e481e..a92a1d73753 100644
--- a/soh/soh/SohGui/ImGuiUtils.h
+++ b/soh/soh/SohGui/ImGuiUtils.h
@@ -36,8 +36,6 @@ typedef struct {
// Maps items ids to info for use in ImGui
extern std::map itemMapping;
-extern std::map gregMapping;
-
typedef struct {
uint32_t id;
std::string name;
diff --git a/soh/src/code/z_inventory.c b/soh/src/code/z_inventory.c
index f2b329ed542..82f087fd099 100644
--- a/soh/src/code/z_inventory.c
+++ b/soh/src/code/z_inventory.c
@@ -3,6 +3,7 @@
#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"
+#include
// Bit Flag array in which gBitFlags[n] is literally (1 << n)
u32 gBitFlags[] = {
@@ -168,6 +169,35 @@ void* gItemIcons[] = {
gOcarinaBtnIconCLeftTex,
gOcarinaBtnIconCRightTex,
gOcarinaBtnIconATex,
+ // Push down array to reach newly added item IDs
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ "",
+ // Start custom items
+ gRocsFeatherTex,
};
// Used to map item IDs to inventory slots
diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c
index 3b1d91f056a..0c880242094 100644
--- a/soh/src/code/z_parameter.c
+++ b/soh/src/code/z_parameter.c
@@ -2463,6 +2463,12 @@ u8 Item_Give(PlayState* play, u8 item) {
}
}
+ return Return_Item(item, MOD_NONE, ITEM_NONE);
+ } else if (item == ITEM_NAYRUS_LOVE && Randomizer_GetSettingValue(RSK_ROCS_FEATHER)) {
+ Flags_SetRandomizerInf(RAND_INF_OBTAINED_NAYRUS_LOVE);
+ if (INV_CONTENT(ITEM_NAYRUS_LOVE) == ITEM_NONE) {
+ INV_CONTENT(ITEM_NAYRUS_LOVE) = ITEM_NAYRUS_LOVE;
+ }
return Return_Item(item, MOD_NONE, ITEM_NONE);
}
returnItem = gSaveContext.inventory.items[slot];
diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c
index 89583e5984f..06fbcbf0148 100644
--- a/soh/src/code/z_play.c
+++ b/soh/src/code/z_play.c
@@ -594,6 +594,10 @@ void Play_Init(GameState* thisx) {
gSlotAgeReqs[SLOT_TRADE_CHILD] = AGE_REQ_CHILD;
}
+ // Handle Rocs Feather requiement
+ gItemAgeReqs[ITEM_ROCS_FEATHER] = AGE_REQ_NONE;
+ gSlotAgeReqs[SLOT_NAYRUS_LOVE] = AGE_REQ_NONE;
+
func_800304DC(play, &play->actorCtx, play->linkActorEntry);
while (!func_800973FC(play, &play->roomCtx)) {
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 2de7113d218..1b5b77c3692 100644
--- a/soh/src/overlays/actors/ovl_player_actor/z_player.c
+++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c
@@ -2570,8 +2570,10 @@ void Player_ProcessItemButtons(Player* this, PlayState* play) {
sHeldItemButtonIsHeldDown = true;
}
} else if (GameInteractor_Should(VB_CHANGE_HELD_ITEM_AND_USE_ITEM, true, item)) {
- this->heldItemButton = i;
- Player_UseItem(play, this, item);
+ if (GameInteractor_Should(VB_USE_ITEM, true, &item)) {
+ this->heldItemButton = i;
+ Player_UseItem(play, this, item);
+ }
}
}
}
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 dad520a0b48..091fad149b4 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
@@ -2,6 +2,7 @@
#include "textures/parameter_static/parameter_static.h"
#include "textures/icon_item_static/icon_item_static.h"
#include "soh/Enhancements/randomizer/ShuffleTradeItems.h"
+#include "soh/Enhancements/randomizer/RocsFeatherCycle.h"
#include "soh/Enhancements/randomizer/randomizerTypes.h"
#include "soh/Enhancements/enhancementTypes.h"
#include "soh/Enhancements/cosmetics/cosmeticsTypes.h"
@@ -374,6 +375,10 @@ void KaleidoScope_HandleItemCycles(PlayState* play) {
KaleidoScope_HandleItemCycleExtras(play, SLOT_TRADE_ADULT,
IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE),
Randomizer_GetPrevAdultTradeItem(), Randomizer_GetNextAdultTradeItem(), true);
+
+ // Handle Nayru's Love/Roc's Feather
+ KaleidoScope_HandleItemCycleExtras(play, SLOT_NAYRUS_LOVE, Randomizer_GetSettingValue(RSK_ROCS_FEATHER),
+ Enhancement_GetPrevNayrusItem(), Enhancement_GetNextNayrusItem(), true);
}
void KaleidoScope_DrawItemCycles(PlayState* play) {
@@ -393,6 +398,10 @@ void KaleidoScope_DrawItemCycles(PlayState* play) {
KaleidoScope_DrawItemCycleExtras(play, SLOT_TRADE_ADULT,
IS_RANDO && Randomizer_GetSettingValue(RSK_SHUFFLE_ADULT_TRADE),
Randomizer_GetPrevAdultTradeItem(), Randomizer_GetNextAdultTradeItem());
+
+ // Draw Nayru's Love/Roc's Feather
+ KaleidoScope_DrawItemCycleExtras(play, SLOT_NAYRUS_LOVE, Randomizer_GetSettingValue(RSK_ROCS_FEATHER),
+ Enhancement_GetPrevNayrusItem(), Enhancement_GetNextNayrusItem());
}
bool IsItemCycling() {
diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c
index 1e53d8180aa..b4cb2d7939c 100644
--- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c
+++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_scope_PAL.c
@@ -22,6 +22,7 @@
#include "soh/ResourceManagerHelpers.h"
#include "soh/SaveManager.h"
#include "soh/Enhancements/kaleido.h"
+#include
static void* sEquipmentFRATexs[] = {
gPauseEquipment00FRATex, gPauseEquipment01Tex, gPauseEquipment02Tex, gPauseEquipment03Tex, gPauseEquipment04Tex,
@@ -2493,7 +2494,10 @@ void KaleidoScope_UpdateNamePanel(PlayState* play) {
osSyncPrintf("J_N=%d point=%d\n", gSaveContext.language, sp2A);
const char* textureName = iconNameTextures[sp2A];
- memcpy(pauseCtx->nameSegment, textureName, strlen(textureName) + 1);
+
+ if (!GameInteractor_Should(VB_DRAW_CUSTOM_ITEM_NAME, false, pauseCtx->namedItem)) {
+ memcpy(pauseCtx->nameSegment, textureName, strlen(textureName) + 1);
+ }
}
pauseCtx->nameDisplayTimer = 0;