diff --git a/.vscode/tasks.json b/.vscode/tasks.json index bfc010d6419..b3a63ca555d 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -24,7 +24,9 @@ "-T", "v143", "-A", - "x64" + "x64", + "-DBUILD_REMOTE_CONTROL=ON", + "-DCMAKE_PREFIX_PATH=C:/vcpkg/installed/x64-windows" ] }, "group": "build", @@ -45,6 +47,8 @@ "args": [ "--build", "./build/x64", + "--config", + "Release", "--target", "GenerateSohOtr" ] @@ -63,7 +67,10 @@ "windows": { "args": [ "--build", - "./build/x64" + "./build/x64", + "--config", + "Release", + "--parallel" ] }, "group": { diff --git a/CMakeLists.txt b/CMakeLists.txt index f22967641fc..4429cc59943 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ set(CMAKE_C_STANDARD 23 CACHE STRING "The C standard to use") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15" CACHE STRING "Minimum OS X deployment version") -project(Ship VERSION 9.2.1 LANGUAGES C CXX) +project(Ship VERSION 9.2.3 LANGUAGES C CXX) include(CMake/soh-cvars.cmake) include(CMake/lus-cvars.cmake) set(SPDLOG_LEVEL_TRACE 0) diff --git a/soh/include/functions.h b/soh/include/functions.h index 760b77f735e..57c02482fac 100644 --- a/soh/include/functions.h +++ b/soh/include/functions.h @@ -430,6 +430,8 @@ s32 func_8002DF38(PlayState* play, Actor* actor, u8 csMode); s32 Player_SetCsActionWithHaltedActors(PlayState* play, Actor* actor, u8 arg2); void func_8002DF90(DynaPolyActor* dynaActor); void func_8002DFA4(DynaPolyActor* dynaActor, f32 arg1, s16 arg2); +Player* Actor_GetClosestPlayer(PlayState* play, Actor* actor); +Player* Actor_GetClosestPlayerFromPos(PlayState* play, const Vec3f* pos); s32 Player_IsFacingActor(Actor* actor, s16 angle, PlayState* play); s32 Actor_ActorBIsFacingActorA(Actor* actorA, Actor* actorB, s16 angle); s32 Actor_IsFacingPlayer(Actor* actor, s16 angle); @@ -487,6 +489,8 @@ void func_800304DC(PlayState* play, ActorContext* actorCtx, ActorEntry* actorEnt void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx); s32 func_800314D4(PlayState* play, Actor* actorB, Vec3f* arg2, f32 arg3); void func_800315AC(PlayState* play, ActorContext* actorCtx); +void Actor_PlaySfx(Actor* actor); +void Actor_SetSfxUpdatesSuppressed(s32 suppressed); void func_80031A28(PlayState* play, ActorContext* actorCtx); void func_80031B14(PlayState* play, ActorContext* actorCtx); void func_80031C3C(ActorContext* actorCtx, PlayState* play); @@ -988,7 +992,7 @@ void HealthMeter_Init(PlayState* play); void HealthMeter_Update(PlayState* play); void HealthMeter_Draw(PlayState* play); void HealthMeter_HandleCriticalAlarm(PlayState* play); -u32 HealthMeter_IsCritical(void); +u32 HealthMeter_IsCritical(Player* player); void Lights_PointSetInfo(LightInfo* info, s16 x, s16 y, s16 z, u8 r, u8 g, u8 b, s16 radius, s32 type); void Lights_PointNoGlowSetInfo(LightInfo* info, s16 x, s16 y, s16 z, u8 r, u8 g, u8 b, s16 radius); void Lights_PointGlowSetInfo(LightInfo* info, s16 x, s16 y, s16 z, u8 r, u8 g, u8 b, s16 radius); @@ -1066,11 +1070,27 @@ void Interface_SetSceneRestrictions(PlayState* play); void Inventory_SwapAgeEquipment(void); void Interface_InitHorsebackArchery(PlayState* play); void func_800849EC(PlayState* play); +void Interface_LoadItemIconForItem(PlayState* play, u16 button, u8 item); void Interface_LoadItemIcon1(PlayState* play, u16 button); void Interface_LoadItemIcon2(PlayState* play, u16 button); +void Interface_ResetExtraItemData(void); +s16 Interface_GetQuickItemButtonIndex(u8 controllerPort); +s16 Interface_GetQuickItemInventorySlotForPortAndSlot(u8 controllerPort, s16 slotIndex); +s16 Interface_GetQuickItemInventorySlotForPort(u8 controllerPort); +u8 Interface_GetQuickItemForPortAndSlot(u8 controllerPort, s16 slotIndex); +u8 Interface_GetQuickItemForPort(u8 controllerPort); +void Interface_SetQuickItemForPortAndSlot(PlayState* play, u8 controllerPort, s16 slotIndex, u8 item, + s16 inventorySlot); +void Interface_SetQuickItemForPort(PlayState* play, u8 controllerPort, u8 item, s16 inventorySlot); +s16 Interface_GetQuickItemSelectedSlotForPort(u8 controllerPort); +void Interface_SetQuickItemSelectedSlotForPort(u8 controllerPort, s16 slotIndex); +s16 Interface_GetQuickItemUseSlotForPort(u8 controllerPort); +void Interface_SetQuickItemUseSlotForPort(u8 controllerPort, s16 slotIndex); void func_80084BF4(PlayState* play, u16 flag); uint16_t Interface_DrawTextLine(GraphicsContext* gfx, char text[], int16_t x, int16_t y, uint16_t colorR, uint16_t colorG, uint16_t colorB, uint16_t colorA, float textScale, uint8_t textShadow); u8 Item_Give(PlayState* play, u8 item); +void Item_Give_SetPlayer(Player* player); +void Item_Give_ClearPlayer(void); u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry); u8 Item_CheckObtainability(u8 item); void Inventory_DeleteItem(u16 item, u16 invSlot); @@ -1084,12 +1104,21 @@ bool Inventory_HatchPocketCucco(PlayState* play); void Interface_SetDoAction(PlayState* play, u16 action); void Interface_SetNaviCall(PlayState* play, u16 naviCallState); void Interface_LoadActionLabelB(PlayState* play, u16 action); -s32 Health_ChangeBy(PlayState* play, s16 healthChange); +s32 Health_ChangeBy(PlayState* play, s16 healthChange, Player* player); void Rupees_ChangeBy(s16 rupeeChange); void Inventory_ChangeAmmo(s16 item, s16 ammoChange); void Magic_Fill(PlayState* play); void Magic_Reset(PlayState* play); s32 Magic_RequestChange(PlayState* play, s16 amount, s16 type); +s16 Magic_GetLevelForPort(u8 controllerPort); +s16 Magic_GetAmountForPort(u8 controllerPort); +void Magic_InitForPort(u8 controllerPort); +void Magic_ResetExtraData(void); +s16 Magic_GetStateForPlayer(Player* player); +void Magic_SetStateForPlayer(Player* player, s16 state); +void Magic_FillForPlayer(PlayState* play, Player* player); +void Magic_ResetForPlayer(PlayState* play, Player* player); +s32 Magic_RequestChangeForPlayer(PlayState* play, Player* player, s16 amount, s16 type); void Interface_SetSubTimer(s16 seconds); void Interface_SetSubTimerToFinalSecond(PlayState* play); void Interface_SetTimer(s16 arg0); @@ -1242,6 +1271,11 @@ s32 func_8009728C(PlayState* play, RoomContext* roomCtx, s32 roomNum); s32 func_800973FC(PlayState* play, RoomContext* roomCtx); void Room_Draw(PlayState* play, Room* room, u32 flags); void func_80097534(PlayState* play, RoomContext* roomCtx); +s32 OTRRoom_AreaPersistenceEnabled(void); +void OTRRoom_ResetAreaPersistence(void); +s32 OTRRoom_IsRetainedRoom(s32 roomNum); +s32 OTRRoom_GetRetainedRoomCount(void); +Room* OTRRoom_GetRetainedRoom(s32 index); void Sample_Destroy(GameState* thisx); void Sample_Init(GameState* thisx); void Inventory_ChangeEquipment(s16 equipment, u16 value); diff --git a/soh/include/macros.h b/soh/include/macros.h index dd51786f195..06c554cf862 100644 --- a/soh/include/macros.h +++ b/soh/include/macros.h @@ -49,7 +49,8 @@ #define RGBA8(r, g, b, a) ((((r) & 0xFF) << 24) | (((g) & 0xFF) << 16) | (((b) & 0xFF) << 8) | (((a) & 0xFF) << 0)) -#define GET_PLAYER(play) ((Player*)(play)->actorCtx.actorLists[ACTORCAT_PLAYER].head) +#define GET_PLAYER1(play) ((Player*)(play)->actorCtx.actorLists[ACTORCAT_PLAYER].head) +#define GET_PLAYER(play) GET_PLAYER1(play) #define GET_ACTIVE_CAM(play) ((play)->cameraPtrs[(play)->activeCamera]) // Upstream TODO: Camera diff --git a/soh/include/variables.h b/soh/include/variables.h index 69f8ef72ba8..248966dbd19 100644 --- a/soh/include/variables.h +++ b/soh/include/variables.h @@ -243,7 +243,7 @@ extern "C" extern u64 gGfxSPTaskOutputBuffer[0x3000]; // 0x18000 bytes extern u8 gGfxSPTaskYieldBuffer[OS_YIELD_DATA_SIZE]; // 0xC00 bytes extern u8 gGfxSPTaskStack[0x400]; // 0x400 bytes - extern GfxPool gGfxPools[2]; // 0x24820 bytes + extern GfxPool gGfxPools[4]; extern u8* gAudioHeap; extern u8* gSystemHeap; extern GameState* gGameState; diff --git a/soh/include/z64.h b/soh/include/z64.h index 70b877f30ae..f7cc0ff5805 100644 --- a/soh/include/z64.h +++ b/soh/include/z64.h @@ -39,7 +39,7 @@ #endif #define AUDIO_HEAP_SIZE 0x380000 -#define SYSTEM_HEAP_SIZE (1024 * 1024 * 4) +#define SYSTEM_HEAP_SIZE (1024 * 1024 * 8) #ifdef __cplusplus namespace LUS @@ -1454,7 +1454,7 @@ typedef struct PlayState { /* 0x11D4C */ s32 (*grabPlayer)(struct PlayState* play, Player* player); /* 0x11D50 */ s32 (*startPlayerCutscene)(struct PlayState* play, Actor* actor, s32 mode); /* 0x11D54 */ void (*func_11D54)(Player* player, struct PlayState* play); - /* 0x11D58 */ s32 (*damagePlayer)(struct PlayState* play, s32 damage); + /* 0x11D58 */ s32 (*damagePlayer)(struct PlayState* play, Player* player, s32 damage); /* 0x11D5C */ void (*talkWithPlayer)(struct PlayState* play, Actor* actor); /* 0x11D60 */ MtxF viewProjectionMtxF; /* 0x11DA0 */ MtxF billboardMtxF; diff --git a/soh/include/z64player.h b/soh/include/z64player.h index e8c32b09e6f..b46ebfd5099 100644 --- a/soh/include/z64player.h +++ b/soh/include/z64player.h @@ -943,6 +943,16 @@ typedef struct Player { /* 0x0A86 */ s8 unk_A86; /* 0x0A87 */ u8 unk_A87; /* 0x0A88 */ Vec3f unk_A88; // previous body part 0 position + // #region Multiplayer groundwork + // 1-based controller assignment (1-4). Default is 1 for main player. + /* */ u8 controllerPort; + // If false, the actor ignores controller input and behaves as if no buttons are pressed. + /* */ u8 inputEnabled; + // Marks actors spawned as additional local players. + /* */ u8 isSecondPlayer; + // Optional link to the main player actor for follow/sync behaviors. + /* */ struct Player* linkedPlayer; + // #endregion // #region SOH [General] // Upstream TODO: Rename these to be more obviously SoH specific /* */ PendingFlag pendingFlag; @@ -953,6 +963,9 @@ typedef struct Player { /* */ u8 boomerangQuickRecall; // Has the player pressed the boomerang button while it's in the air still? /* */ u8 ivanFloating; /* */ u8 ivanDamageMultiplier; + /* */ u8 bootsAreActive; + /* */ u8 bootsToggleRequest; + /* */ u8 equippedBoots; // #endregion } Player; // size = 0xA94 diff --git a/soh/include/z64save.h b/soh/include/z64save.h index 35f30c26398..aaec8dde47a 100644 --- a/soh/include/z64save.h +++ b/soh/include/z64save.h @@ -264,8 +264,17 @@ typedef struct { /* 0x003F */ u8 ocarinaGameRoundNum; /* 0x0040 */ ItemEquips childEquips; /* 0x004A */ ItemEquips adultEquips; - /* 0x0054 */ u32 unk_54; // this may be incorrect, currently used for alignment - /* 0x0058 */ char unk_58[0x0E]; + /* 0x0054 */ s16 healthCapacity4; // "max_life" for P4 + /* 0x0056 */ s16 health4; // "now_life" for P4 + /* 0x0058 */ s16 healthCapacity2; // "max_life" for P2 + /* 0x005A */ s16 health2; // "now_life" for P2 + /* 0x005C */ s8 magicLevel2; // 0 for no magic/new load, 1 for magic, 2 for double magic (P2) + /* 0x005D */ s8 magic2; // current magic available for use (P2) + /* 0x005E */ u8 isMagicAcquired2; + /* 0x005F */ u8 isDoubleMagicAcquired2; + /* 0x0060 */ s16 healthCapacity3; // "max_life" for P3 + /* 0x0062 */ s16 health3; // "now_life" for P3 + /* 0x0064 */ char unk_64[0x02]; /* 0x0066 */ s16 savedSceneNum; // Upstream TODO: sceneId /* 0x0068 */ ItemEquips equips; /* 0x0074 */ Inventory inventory; diff --git a/soh/soh/Enhancements/BlueFireArrows.cpp b/soh/soh/Enhancements/BlueFireArrows.cpp new file mode 100644 index 00000000000..985767853a8 --- /dev/null +++ b/soh/soh/Enhancements/BlueFireArrows.cpp @@ -0,0 +1,55 @@ +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#include "soh/Enhancements/randomizer/SeedContext.h" +#include "soh/ShipInit.hpp" + +extern "C" { +#include "overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.h" +#include "overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.h" + +extern PlayState* gPlayState; +} + +static void UpdateBlueFireCollidersBgBreakwall(void* actorPtr) { + BgBreakwall* thisx = (BgBreakwall*)actorPtr; + thisx->collider.info.bumper.dmgFlags |= DMG_ARROW_ICE; +} + +static void UpdateBlueFireCollidersBgIceShelter(void* actorPtr) { + BgIceShelter* thisx = (BgIceShelter*)actorPtr; + thisx->cylinder1.base.acFlags |= AC_TYPE_PLAYER; + thisx->cylinder1.info.bumper.dmgFlags |= DMG_ARROW_ICE; + thisx->cylinder2.base.acFlags |= AC_TYPE_PLAYER; + thisx->cylinder2.info.bumper.dmgFlags |= DMG_ARROW_ICE; +} + +static bool CheckAC(Actor* ac) { + return ac != NULL && ac->id == ACTOR_EN_ARROW && ac->child != NULL && ac->child->id == ACTOR_ARROW_ICE; +} + +void RegisterBlueFireArrowsHooks() { + bool shouldRegister = + CVarGetInteger(CVAR_ENHANCEMENT("BlueFireArrows"), 0) || (IS_RANDO && RAND_GET_OPTION(RSK_BLUE_FIRE_ARROWS)); + + COND_ID_HOOK(OnActorInit, ACTOR_BG_BREAKWALL, shouldRegister, UpdateBlueFireCollidersBgBreakwall); + COND_ID_HOOK(OnActorInit, ACTOR_BG_ICE_SHELTER, shouldRegister, UpdateBlueFireCollidersBgIceShelter); + + // fix bug where cylinder2 never checks acFlags + COND_VB_SHOULD(VB_BG_ICE_SHELTER_HIT, shouldRegister, { + BgIceShelter* thisx = va_arg(args, BgIceShelter*); + + if (thisx->cylinder2.base.acFlags & AC_HIT) { + thisx->cylinder2.base.acFlags &= ~AC_HIT; + *should = true; + } + }); + + COND_VB_SHOULD(VB_BG_ICE_SHELTER_MELT, shouldRegister, { + BgIceShelter* thisx = va_arg(args, BgIceShelter*); + + if (CheckAC(thisx->cylinder1.base.ac) || CheckAC(thisx->cylinder2.base.ac)) { + *should = true; + } + }); +} + +static RegisterShipInitFunc initFunc(RegisterBlueFireArrowsHooks, { "IS_RANDO", CVAR_ENHANCEMENT("BlueFireArrows") }); diff --git a/soh/soh/Enhancements/Difficulty/BonkDamage.cpp b/soh/soh/Enhancements/Difficulty/BonkDamage.cpp index 04618c4a3a3..4670e555f49 100644 --- a/soh/soh/Enhancements/Difficulty/BonkDamage.cpp +++ b/soh/soh/Enhancements/Difficulty/BonkDamage.cpp @@ -45,7 +45,7 @@ static void RegisterBonkDamage() { break; } - Health_ChangeBy(gPlayState, -bonkDamage); + Health_ChangeBy(gPlayState, -bonkDamage, GET_PLAYER(gPlayState)); // Set invincibility to make Link flash red as a visual damage indicator. Player* player = GET_PLAYER(gPlayState); player->invincibilityTimer = 28; diff --git a/soh/soh/Enhancements/ExtraModes/EnemyRandomizer.cpp b/soh/soh/Enhancements/ExtraModes/EnemyRandomizer.cpp index e3d311535b0..d2579e1279c 100644 --- a/soh/soh/Enhancements/ExtraModes/EnemyRandomizer.cpp +++ b/soh/soh/Enhancements/ExtraModes/EnemyRandomizer.cpp @@ -392,9 +392,11 @@ uint8_t GetRandomizedEnemy(PlayState* play, int16_t* actorId, s16* posX, s16* po // This should probably be handled on OTR generation in the future when object dependency is fully removed. // Remove bats and Skulltulas from graveyard. // Remove Octorok in Lost Woods. + // Remove signs in Gerudo Fortress as child if (((*actorId == ACTOR_EN_FIREFLY || (*actorId == ACTOR_EN_SW && *params == 0)) && play->sceneNum == SCENE_GRAVEYARD) || - (*actorId == ACTOR_EN_OKUTA && play->sceneNum == SCENE_LOST_WOODS)) { + (*actorId == ACTOR_EN_OKUTA && play->sceneNum == SCENE_LOST_WOODS) || + (*actorId == ACTOR_EN_KANBAN && play->sceneNum == SCENE_GERUDOS_FORTRESS && LINK_IS_CHILD)) { return 0; } diff --git a/soh/soh/Enhancements/ExtraModes/RupeeDash.cpp b/soh/soh/Enhancements/ExtraModes/RupeeDash.cpp index 2fc224152d6..c22441212eb 100644 --- a/soh/soh/Enhancements/ExtraModes/RupeeDash.cpp +++ b/soh/soh/Enhancements/ExtraModes/RupeeDash.cpp @@ -32,7 +32,7 @@ static void UpdateRupeeDash() { uint16_t walletSize = (CUR_UPG_VALUE(UPG_WALLET) + 1) * -1; Rupees_ChangeBy(walletSize); } else { - Health_ChangeBy(gPlayState, -16); + Health_ChangeBy(gPlayState, -16, GET_PLAYER(gPlayState)); } } diff --git a/soh/soh/Enhancements/Lang/Lang.cpp b/soh/soh/Enhancements/Lang/Lang.cpp new file mode 100644 index 00000000000..76efe95a415 --- /dev/null +++ b/soh/soh/Enhancements/Lang/Lang.cpp @@ -0,0 +1,143 @@ +#include "Lang.h" + +#include "soh/SohGui/MenuTypes.h" +#include "soh/SohGui/SohGui.hpp" +#include "soh/SohGui/SohMenu.h" +#include "soh/util.h" + +#include "ship/Context.h" +#include "ship/resource/File.h" +#include "ship/resource/ResourceManager.h" +#include "ship/resource/type/Json.h" +#include "ship/utils/StringHelper.h" + +#include "spdlog/spdlog.h" + +#include + +namespace SohGui { +extern std::shared_ptr mSohMenu; +} + +static bool initialized = false; +static std::map langs; + +#define LANGUAGE_CVAR CVAR_SETTING("Language") +#define DEFAULT_LANGUAGE "en_US" + +std::string Lang::Translate(const char* path) { + if (!initialized) { + SPDLOG_ERROR("Tried to obtain a translation before the translation data is initialized"); + assert(false); + return "ERROR: Language data not initialized yet"; + } + + std::string currentLang = CVarGetString(LANGUAGE_CVAR, DEFAULT_LANGUAGE); + + if (!langs.contains(currentLang)) { + SPDLOG_WARN("Current language ({}) doesn't exist, trying to fall back to default language ({})", + currentLang.c_str(), DEFAULT_LANGUAGE); + + currentLang = DEFAULT_LANGUAGE; + + if (!langs.contains(currentLang)) { + SPDLOG_ERROR("Default language ({}) doesn't exist", DEFAULT_LANGUAGE); + assert(false); + return "ERROR: Language data not found"; + } + + CVarSetString(LANGUAGE_CVAR, DEFAULT_LANGUAGE); + SPDLOG_WARN("Fallback to default language ({}) was succesful", DEFAULT_LANGUAGE); + } + + nlohmann::json currentLangData = langs[currentLang]; + + std::vector segments = SohUtils::StringSplit(std::string(path), "."); + + std::string lastSegment = segments[segments.size() - 1]; + + segments.pop_back(); + + for (const auto& segment : segments) { + if (!currentLangData.contains(segment)) { + SPDLOG_WARN("Current language ({}) doesn't have data for the requested path ({})", currentLang.c_str(), + path); + return std::string(path); + } + + currentLangData = currentLangData[segment]; + } + + if (!currentLangData.contains(lastSegment)) { + SPDLOG_WARN("Current language ({}) doesn't have data for the requested path ({})", currentLang.c_str(), path); + return std::string(path); + } + + if (currentLangData[lastSegment].is_string()) { + return currentLangData[lastSegment].get(); + } + + if (currentLangData[lastSegment].is_array()) { + std::string translatedString = ""; + + for (const auto& item : currentLangData[lastSegment]) { + if (!item.is_string()) { + SPDLOG_WARN("Current language ({}) has an array with a non-string at the requested path ({})", + currentLang.c_str(), path); + return std::string(path); + } + + translatedString += item.get(); + } + + return translatedString; + } + + SPDLOG_WARN("Current language ({}) doesn't have either a string or an array at the requested path ({})", + currentLang.c_str(), path); + return std::string(path); +} + +void Lang::LoadLangs() { + auto initData = std::make_shared(); + initData->Format = RESOURCE_FORMAT_BINARY; + initData->Type = static_cast(Ship::ResourceType::Json); + initData->ResourceVersion = 0; + const static std::string folder = "lang/*"; + auto langFiles = Ship::Context::GetInstance()->GetResourceManager()->GetArchiveManager()->ListFiles(folder); + size_t start = std::string(folder).size() - 1; + for (size_t i = 0; i < langFiles->size(); i++) { + std::string filePath = langFiles->at(i); + auto json = std::static_pointer_cast( + Ship::Context::GetInstance()->GetResourceManager()->LoadResource(filePath, true, initData)); + + std::string fileName = filePath.substr(start, filePath.size() - start - 5); // 5 for length of ".json" + langs.insert_or_assign(fileName, json->Data); + } + initialized = true; +} + +void LanguageCustomWidget(WidgetInfo& info) { + ImGui::Text("Select Language:"); + for (const auto& [id, data] : langs) { + if (ImGui::Button(StringHelper::Sprintf("%s [%s]", data["language_name"].get_ref().c_str(), + id.c_str()) + .c_str())) { + CVarSetString(LANGUAGE_CVAR, id.c_str()); + } + } +} + +void RegisterLangWidgets() { + return; + + // TODO: Improve & enable this when everything is set up + + SohGui::mSohMenu->AddSidebarEntry("Settings", "Language", 1); + WidgetPath path = { "Settings", "Language", SECTION_COLUMN_1 }; + SohGui::mSohMenu->AddWidget(path, "LanguageWidget", WIDGET_CUSTOM) + .CustomFunction(LanguageCustomWidget) + .HideInSearch(true); +} + +static RegisterMenuInitFunc menuInitFunc(RegisterLangWidgets); \ No newline at end of file diff --git a/soh/soh/Enhancements/Lang/Lang.h b/soh/soh/Enhancements/Lang/Lang.h new file mode 100644 index 00000000000..3bf3a886f7d --- /dev/null +++ b/soh/soh/Enhancements/Lang/Lang.h @@ -0,0 +1,8 @@ +#pragma once + +#include + +namespace Lang { +std::string Translate(const char* path); +void LoadLangs(); +} // namespace Lang \ No newline at end of file diff --git a/soh/soh/Enhancements/boss-rush/BossRush.cpp b/soh/soh/Enhancements/boss-rush/BossRush.cpp index 170b175cb4a..499ad161077 100644 --- a/soh/soh/Enhancements/boss-rush/BossRush.cpp +++ b/soh/soh/Enhancements/boss-rush/BossRush.cpp @@ -514,7 +514,7 @@ void BossRush_HandleBlueWarpHeal(PlayState* play) { // This function gets called multiple times per blue warp, so only heal when player isn't at max HP. if (gSaveContext.ship.quest.data.bossRush.options[BR_OPTIONS_HEAL] == BR_CHOICE_HEAL_EVERYBOSS && gSaveContext.health != gSaveContext.healthCapacity) { - Health_ChangeBy(play, 320); + Health_ChangeBy(play, 320, GET_PLAYER(play)); } } @@ -552,7 +552,7 @@ void BossRush_HandleCompleteBoss(PlayState* play) { // Fully heal the player after Ganondorf if (gSaveContext.ship.quest.data.bossRush.options[BR_OPTIONS_HEAL] == BR_CHOICE_HEAL_EVERYBOSS && play->sceneNum == SCENE_GANONDORF_BOSS) { - Health_ChangeBy(play, 320); + Health_ChangeBy(play, 320, GET_PLAYER(play)); } if ((CheckDungeonCount() == 3 && diff --git a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp index 2346d97715f..1d88a6f50b3 100644 --- a/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp +++ b/soh/soh/Enhancements/controls/SohInputEditorWindow.cpp @@ -1634,58 +1634,13 @@ void SohInputEditorWindow::DrawLinkTab() { } } -void SohInputEditorWindow::DrawIvanTab() { - if (CVarGetInteger(CVAR_DEVELOPER_TOOLS("DebugEnabled"), 0)) { - DrawDebugPortTab(1, "Ivan (P2)"); - return; - } - - uint8_t portIndex = 1; - if (ImGui::BeginTabItem(StringHelper::Sprintf("Ivan (P2)###port%d", portIndex).c_str())) { - DrawClearAllButton(portIndex); - DrawSetDefaultsButton(portIndex); - DrawDeviceToggles(portIndex); - - UpdateBitmaskToMappingIds(portIndex); - UpdateStickDirectionToMappingIds(portIndex); - - ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(0.133f, 0.133f, 0.133f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); - ImGui::PushStyleColor(ImGuiCol_HeaderActive, ImVec4(0.0f, 0.0f, 0.0f, 1.0f)); - - if (ImGui::CollapsingHeader("Buttons", NULL, ImGuiTreeNodeFlags_DefaultOpen)) { - DrawButtonLine("A", portIndex, BTN_A, CHIP_COLOR_N64_BLUE); - DrawButtonLine("B", portIndex, BTN_B, CHIP_COLOR_N64_GREEN); - DrawButtonLine("Z", portIndex, BTN_Z); - DrawButtonLine(StringHelper::Sprintf("C %s", ICON_FA_ARROW_UP).c_str(), portIndex, BTN_CUP, - CHIP_COLOR_N64_YELLOW); - DrawButtonLine(StringHelper::Sprintf("C %s", ICON_FA_ARROW_DOWN).c_str(), portIndex, BTN_CDOWN, - CHIP_COLOR_N64_YELLOW); - DrawButtonLine(StringHelper::Sprintf("C %s", ICON_FA_ARROW_LEFT).c_str(), portIndex, BTN_CLEFT, - CHIP_COLOR_N64_YELLOW); - DrawButtonLine(StringHelper::Sprintf("C %s", ICON_FA_ARROW_RIGHT).c_str(), portIndex, BTN_CRIGHT, - CHIP_COLOR_N64_YELLOW); - } - - if (ImGui::CollapsingHeader("D-Pad", NULL, ImGuiTreeNodeFlags_DefaultOpen)) { - DrawButtonLine(StringHelper::Sprintf("%s", ICON_FA_ARROW_UP).c_str(), portIndex, BTN_DUP); - DrawButtonLine(StringHelper::Sprintf("%s", ICON_FA_ARROW_DOWN).c_str(), portIndex, BTN_DDOWN); - DrawButtonLine(StringHelper::Sprintf("%s", ICON_FA_ARROW_LEFT).c_str(), portIndex, BTN_DLEFT); - DrawButtonLine(StringHelper::Sprintf("%s", ICON_FA_ARROW_RIGHT).c_str(), portIndex, BTN_DRIGHT); - } - - if (ImGui::CollapsingHeader("Analog Stick", NULL, ImGuiTreeNodeFlags_DefaultOpen)) { - DrawStickSection(portIndex, Ship::LEFT, 0); - } - - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::PopStyleColor(); - ImGui::EndTabItem(); - } +void SohInputEditorWindow::DrawMultiplayerTabs() { + DrawPlayerPortTab(1, "Player 2 (P2)###port1"); + DrawPlayerPortTab(2, "Player 3 (P3)###port2"); + DrawPlayerPortTab(3, "Player 4 (P4)###port3"); } -void SohInputEditorWindow::DrawDebugPortTab(uint8_t portIndex, std::string customName) { +void SohInputEditorWindow::DrawPlayerPortTab(uint8_t portIndex, std::string customName) { if (ImGui::BeginTabItem(customName == "" ? StringHelper::Sprintf("Port %d###port%d", portIndex + 1, portIndex).c_str() : customName.c_str())) { @@ -1837,11 +1792,7 @@ void SohInputEditorWindow::DrawElement() { ImGui::PushStyleColor(ImGuiCol_TabActive, ImVec4(themeColor.x, themeColor.y, themeColor.z, 0.6f)); ImGui::BeginTabBar("##ControllerConfigPortTabs"); DrawLinkTab(); - DrawIvanTab(); - if (CVarGetInteger(CVAR_DEVELOPER_TOOLS("DebugEnabled"), 0)) { - DrawDebugPortTab(2); - DrawDebugPortTab(3); - } + DrawMultiplayerTabs(); ImGui::EndTabBar(); ImGui::PopStyleColor(3); ImGui::PopFont(); diff --git a/soh/soh/Enhancements/controls/SohInputEditorWindow.h b/soh/soh/Enhancements/controls/SohInputEditorWindow.h index a6815f060aa..9908dfcec99 100644 --- a/soh/soh/Enhancements/controls/SohInputEditorWindow.h +++ b/soh/soh/Enhancements/controls/SohInputEditorWindow.h @@ -87,8 +87,8 @@ class SohInputEditorWindow final : public Ship::GuiWindow { void GetButtonColorsForDeviceType(Ship::PhysicalDeviceType lusIndex, ImVec4& buttonColor, ImVec4& buttonHoveredColor); void DrawLinkTab(); - void DrawIvanTab(); - void DrawDebugPortTab(uint8_t portIndex, std::string customName = ""); + void DrawMultiplayerTabs(); + void DrawPlayerPortTab(uint8_t portIndex, std::string customName = ""); std::set mButtonsBitmasks; std::set mDpadBitmasks; std::set mModifierButtonsBitmasks; diff --git a/soh/soh/Enhancements/custom-message/CustomMessageTypes.h b/soh/soh/Enhancements/custom-message/CustomMessageTypes.h index 4df57e274e7..205e44dc605 100644 --- a/soh/soh/Enhancements/custom-message/CustomMessageTypes.h +++ b/soh/soh/Enhancements/custom-message/CustomMessageTypes.h @@ -219,6 +219,10 @@ typedef enum { TEXT_SHEIK_HAVE_HOOK = 0x7010, TEXT_ALTAR_CHILD = 0x7040, TEXT_CHEST_GAME_PROCEED = 0x704C, + TEXT_BEGGAR_BUY_BLUE_FIRE = 0x70F0, + TEXT_BEGGAR_BUY_FISH = 0x70F1, + TEXT_BEGGAR_BUY_BUGS = 0x70F2, + TEXT_BEGGAR_BUY_FAIRY = 0x70F3, TEXT_GHOST_SHOP_EXPLAINATION = 0x70F4, TEXT_GHOST_SHOP_CARD_HAS_POINTS = 0x70F5, TEXT_GHOST_SHOP_BUY_NORMAL_POE = 0x70F6, diff --git a/soh/soh/Enhancements/customequipment.cpp b/soh/soh/Enhancements/customequipment.cpp index 1ce4c79048e..b799d4a9958 100644 --- a/soh/soh/Enhancements/customequipment.cpp +++ b/soh/soh/Enhancements/customequipment.cpp @@ -1,18 +1,20 @@ #include -#include "src/overlays/actors/ovl_En_Elf/z_en_elf.h" #include "objects/object_link_boy/object_link_boy.h" #include "objects/object_link_child/object_link_child.h" #include "objects/object_custom_equip/object_custom_equip.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/ShipInit.hpp" #include "soh/ResourceManagerHelpers.h" -#include "soh_assets.h" -#include "kaleido.h" #include "soh/cvar_prefixes.h" +extern "C" { +#include "variables.h" +#include "macros.h" + extern SaveContext gSaveContext; extern PlayState* gPlayState; -extern void Overlay_DisplayText(float duration, const char* text); +} + void DummyPlayer_Update(Actor* actor, PlayState* play); static void UpdatePatchCustomEquipmentDlists(); @@ -66,33 +68,17 @@ static void UpdateCustomEquipmentSetModel(Player* player, u8 ModelGroup) { } static void UpdateCustomEquipment() { - if (!GameInteractor::IsSaveLoaded() || gPlayState == nullptr) { - return; - } - - Player* player = GET_PLAYER(gPlayState); - if (player == nullptr || IsDummyPlayer(player)) { + if (!GameInteractor::IsSaveLoaded() || gPlayState == nullptr || GET_PLAYER(gPlayState) == nullptr || + IsDummyPlayer(GET_PLAYER(gPlayState))) { return; } RefreshCustomEquipment(); } -static void PatchCustomEquipment() { - COND_HOOK(OnPlayerSetModels, true, UpdateCustomEquipmentSetModel); - COND_HOOK(OnLinkEquipmentChange, true, UpdateCustomEquipment); - COND_HOOK(OnLinkSkeletonInit, true, UpdateCustomEquipment); - COND_HOOK(OnAssetAltChange, true, UpdateCustomEquipment); -} - -static RegisterShipInitFunc initFunc(PatchCustomEquipment); - static void RefreshCustomEquipment() { - if (!GameInteractor::IsSaveLoaded() || gPlayState == NULL || GET_PLAYER(gPlayState) == nullptr) { - return; - } - - if (IsDummyPlayer(GET_PLAYER(gPlayState))) { + if (!GameInteractor::IsSaveLoaded() || gPlayState == nullptr || GET_PLAYER(gPlayState) == nullptr || + IsDummyPlayer(GET_PLAYER(gPlayState))) { return; } @@ -511,3 +497,12 @@ void UpdatePatchCustomEquipmentDlists() { ApplyCommonEquipmentPatches(); } + +static void PatchCustomEquipment() { + COND_HOOK(OnPlayerSetModels, true, UpdateCustomEquipmentSetModel); + COND_HOOK(OnLinkEquipmentChange, true, UpdateCustomEquipment); + COND_HOOK(OnLinkSkeletonInit, true, UpdateCustomEquipment); + COND_HOOK(OnAssetAltChange, true, UpdateCustomEquipment); +} + +static RegisterShipInitFunc initFunc(PatchCustomEquipment); diff --git a/soh/soh/Enhancements/debugger/SohConsoleWindow.cpp b/soh/soh/Enhancements/debugger/SohConsoleWindow.cpp index 19be376ed7c..a47d60812d1 100644 --- a/soh/soh/Enhancements/debugger/SohConsoleWindow.cpp +++ b/soh/soh/Enhancements/debugger/SohConsoleWindow.cpp @@ -3,14 +3,6 @@ #include "soh/SohGui/UIWidgets.hpp" #include "soh/SohGui/SohGui.hpp" -void SohConsoleWindow::InitElement() { - ConsoleWindow::InitElement(); -} - -void SohConsoleWindow::UpdateElement() { - ConsoleWindow::UpdateElement(); -} - void SohConsoleWindow::DrawElement() { ImGui::BeginDisabled(CVarGetInteger(CVAR_SETTING("DisableChanges"), 0)); UIWidgets::PushStyleInput(THEME_COLOR); diff --git a/soh/soh/Enhancements/debugger/SohConsoleWindow.h b/soh/soh/Enhancements/debugger/SohConsoleWindow.h index b84399ad637..9ae390d146f 100644 --- a/soh/soh/Enhancements/debugger/SohConsoleWindow.h +++ b/soh/soh/Enhancements/debugger/SohConsoleWindow.h @@ -9,8 +9,6 @@ class SohConsoleWindow : public Ship::ConsoleWindow { using ConsoleWindow::ConsoleWindow; protected: - void InitElement() override; - void UpdateElement() override; void DrawElement() override; }; diff --git a/soh/soh/Enhancements/debugger/debugSaveEditor.h b/soh/soh/Enhancements/debugger/debugSaveEditor.h index 2d3c87ba617..6e692963fed 100644 --- a/soh/soh/Enhancements/debugger/debugSaveEditor.h +++ b/soh/soh/Enhancements/debugger/debugSaveEditor.h @@ -1,6 +1,5 @@ #pragma once -#include #include #include #include diff --git a/soh/soh/Enhancements/debugger/valueViewer.cpp b/soh/soh/Enhancements/debugger/valueViewer.cpp index f906f4a96a0..32cd8ddd2fd 100644 --- a/soh/soh/Enhancements/debugger/valueViewer.cpp +++ b/soh/soh/Enhancements/debugger/valueViewer.cpp @@ -5,11 +5,14 @@ #include "soh/ShipInit.hpp" extern "C" { +#include #include #include "variables.h" #include "functions.h" #include "macros.h" #include "soh/cvar_prefixes.h" +#include "overlays/actors/ovl_Door_Warp1/z_door_warp1.h" + extern PlayState* gPlayState; void GfxPrint_SetColor(GfxPrint* printer, u32 r, u32 g, u32 b, u32 a); void GfxPrint_SetPos(GfxPrint* printer, s32 x, s32 y); @@ -20,103 +23,120 @@ s32 GfxPrint_Printf(GfxPrint* printer, const char* fmt, ...); #define CVAR_DEFAULT 0 #define CVAR_VALUE CVarGetInteger(CVAR_NAME, CVAR_DEFAULT) -ImVec4 WHITE = ImVec4(1.0f, 1.0f, 1.0f, 1.0f); +std::map valueViewerSettings; // clang-format off -std::vector valueTable = { - { "Time", "gSaveContext.dayTime", "TIME:", TYPE_U16, false, []() -> void* { return &gSaveContext.dayTime; }, WHITE }, - { "Age", "gSaveContext.linkAge", "AGE:", TYPE_S32, false, []() -> void* { return &gSaveContext.linkAge; }, WHITE }, - { "Health", "gSaveContext.health", "HP:", TYPE_S16, false, []() -> void* { return &gSaveContext.health; }, WHITE }, - { "Navi Timer", "gSaveContext.naviTimer", "NAVI:", TYPE_U16, false, []() -> void* { return &gSaveContext.naviTimer; }, WHITE }, - { "Scene ID", "play->sceneNum", "SCENE:", TYPE_S16, true, []() -> void* { return &gPlayState->sceneNum; }, WHITE }, - { "Room ID", "play->roomCtx.curRoom.num", "ROOM:", TYPE_S8, true, []() -> void* { return &gPlayState->roomCtx.curRoom.num; }, WHITE }, - { "Entrance ID", "gSaveContext.entranceIndex", "ENTR:", TYPE_S32, false, []() -> void* { return &gSaveContext.entranceIndex; }, WHITE }, - { "Cutscene ID", "gSaveContext.cutsceneIndex", "CUTS:", TYPE_S32, false, []() -> void* { return &gSaveContext.cutsceneIndex; }, WHITE }, - { "Link X", "Player->actor.world.pos.x", "X:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.world.pos.x; }, WHITE }, - { "Link Y", "Player->actor.world.pos.y", "Y:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.world.pos.y; }, WHITE }, - { "Link Z", "Player->actor.world.pos.z", "Z:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.world.pos.z; }, WHITE }, - { "Link Yaw", "Player->actor.world.rot.y", "ROT:", TYPE_S16, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.world.rot.y; }, WHITE }, - { "Link Velocity", "Player->linearVelocity", "V:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->linearVelocity; }, WHITE }, - { "Link X Velocity", "Player->actor.velocity.x", "XV:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.velocity.x; }, WHITE }, - { "Link Y Velocity", "Player->actor.velocity.y", "YV:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.velocity.y; }, WHITE }, - { "Link Z Velocity", "Player->actor.velocity.z", "ZV:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.velocity.z; }, WHITE }, - { "Text ID", "play->msgCtx.textId", "TEXTID:", TYPE_U16, true, []() -> void* { return &gPlayState->msgCtx.textId; }, WHITE }, - { "Analog Stick X", "play->state.input->cur.stick_x", "AX:", TYPE_S8, true, []() -> void* { return &gPlayState->state.input->cur.stick_x; }, WHITE }, - { "Analog Stick Y", "play->state.input->cur.stick_y", "AY:", TYPE_S8, true, []() -> void* { return &gPlayState->state.input->cur.stick_y; }, WHITE }, - { "getItemID", "Player->getItemId", "ITEM:", TYPE_S16, true, []() -> void* { return &GET_PLAYER(gPlayState)->getItemId; }, WHITE }, - { "getItemEntry", "Player->getItemEntry", "IE:", TYPE_S16, true, []() -> void* { return &GET_PLAYER(gPlayState)->getItemEntry.itemId; }, WHITE }, +std::array valueTable = {{ + { "Time", "gSaveContext.dayTime", "TIME:", TYPE_U16, false, []() -> void* { return &gSaveContext.dayTime; }}, + { "Age", "gSaveContext.linkAge", "AGE:", TYPE_S32, false, []() -> void* { return &gSaveContext.linkAge; }}, + { "Health", "gSaveContext.health", "HP:", TYPE_S16, false, []() -> void* { return &gSaveContext.health; }}, + { "Navi Timer", "gSaveContext.naviTimer", "NAVI:", TYPE_U16, false, []() -> void* { return &gSaveContext.naviTimer; }}, + { "Scene ID", "play->sceneNum", "SCENE:", TYPE_S16, true, []() -> void* { return &gPlayState->sceneNum; }}, + { "Room ID", "play->roomCtx.curRoom.num", "ROOM:", TYPE_S8, true, []() -> void* { return &gPlayState->roomCtx.curRoom.num; }}, + { "Entrance ID", "gSaveContext.entranceIndex", "ENTR:", TYPE_S32, false, []() -> void* { return &gSaveContext.entranceIndex; }}, + { "Cutscene ID", "gSaveContext.cutsceneIndex", "CUTS:", TYPE_S32, false, []() -> void* { return &gSaveContext.cutsceneIndex; }}, + { "Link X", "Player->actor.world.pos.x", "X:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.world.pos.x; }}, + { "Link Y", "Player->actor.world.pos.y", "Y:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.world.pos.y; }}, + { "Link Z", "Player->actor.world.pos.z", "Z:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.world.pos.z; }}, + { "Link Yaw", "Player->actor.world.rot.y", "ROT:", TYPE_S16, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.world.rot.y; }}, + { "Link Velocity", "Player->linearVelocity", "V:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->linearVelocity; }}, + { "Link X Velocity", "Player->actor.velocity.x", "XV:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.velocity.x; }}, + { "Link Y Velocity", "Player->actor.velocity.y", "YV:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.velocity.y; }}, + { "Link Z Velocity", "Player->actor.velocity.z", "ZV:", TYPE_FLOAT, true, []() -> void* { return &GET_PLAYER(gPlayState)->actor.velocity.z; }}, + { "Text ID", "play->msgCtx.textId", "TEXTID:", TYPE_U16, true, []() -> void* { return &gPlayState->msgCtx.textId; }}, + { "Analog Stick X", "play->state.input->cur.stick_x", "AX:", TYPE_S8, true, []() -> void* { return &gPlayState->state.input->cur.stick_x; }}, + { "Analog Stick Y", "play->state.input->cur.stick_y", "AY:", TYPE_S8, true, []() -> void* { return &gPlayState->state.input->cur.stick_y; }}, + { "getItemID", "Player->getItemId", "ITEM:", TYPE_S16, true, []() -> void* { return &GET_PLAYER(gPlayState)->getItemId; }}, + { "getItemEntry", "Player->getItemEntry", "IE:", TYPE_S16, true, []() -> void* { return &GET_PLAYER(gPlayState)->getItemEntry.itemId; }}, + { "Movement Angle", "Player->yaw", "YAW:", TYPE_S16, true, []() -> void* { return &GET_PLAYER(gPlayState)->yaw; }}, + { "Last Item Pressed", "Player->heldItemButton", "LASTI:", TYPE_S8, true, []() -> void* { return &GET_PLAYER(gPlayState)->heldItemButton; }}, + { "Stick Timer", "Player->unk_860", "STICK:", TYPE_S16, true, []() -> void* { return &GET_PLAYER(gPlayState)->unk_860; }}, + { "Last damage value", "Player->melee[0].info.dmgFlags", "DMG:", TYPE_U32, true, []() -> void* { return &GET_PLAYER(gPlayState)->meleeWeaponQuads[0].info.toucher.dmgFlags; }}, + { "Camera Angle", "play->mainCamera.camDir.y", "CAMY:", TYPE_S16, true, []() -> void* { return &gPlayState->mainCamera.camDir.y; }}, + { "Camera XZ Speed", "play->mainCamera.xzSpeed", "CAMXZV:", TYPE_FLOAT, true, []() -> void* { return &gPlayState->mainCamera.xzSpeed; }}, + { "Frame Counter", "play->state.frames", "FRAM:", TYPE_S32, true, []() -> void* { return &gPlayState->state.frames; }}, + { "Cutscene Pointer", "play->csCtx.segment", "CSP:", TYPE_PTR, true, []() -> void* { return &gPlayState->csCtx.segment; }}, + { "Framerate Divisor", "R_UPDATE_RATE", "FRDV:", TYPE_S16, false, []() -> void* { return &R_UPDATE_RATE; }}, + { "Next HUD mode", "gSaveContext.nextHudMode", "HUD:", TYPE_S16, false, []() -> void* { return &gSaveContext.unk_13E8; }}, + { "Temp B Value", "gSaveContext.buttonStatus[0]", "TEMPB:", TYPE_U8, false, []() -> void* { return &gSaveContext.buttonStatus[0]; }}, + { "Blue Warp Timer", "DoorWarp1->warpTimer", "WARPT:", TYPE_U16, true, []() -> void* { DoorWarp1 *actor = (DoorWarp1 *)Actor_Find(&gPlayState->actorCtx, ACTOR_DOOR_WARP1 ,ACTORCAT_ITEMACTION); if(actor) { return &actor->warpTimer; } else { return nullptr; }}}, /* TODO: Find these (from GZ) - "XZ Units Traveled (Camera based speed variable)" f32 0x801C9018 - "Movement Angle" x16 0x801DBB1C - "Camera Angle" u16 0x801C907C - "Time of Day" x16 0x8011AC8C - "Global Frame Counter" s32 0x801C8DFC - "Lit Deku Stick Timer" u16 0x801DBB40 - "Cutscene Pointer" u32 0x801CAAC8 - "Get Item Value" s8 0x801DB714 "Last RNG Value" x32 0x80105A80 - "Last Item Button Pressed" u8 0x801DB430 - "Last Damage Value" x32 0x801DB7DC - "Temp B Value" u8 0x8011C062 - "Framerate Divisor" u8 0x801C7861 - "Heads Up Display (HUD)" u16 0x8011C068 "Analog Stick Angle" s16 0x803AA698 - "Deku Tree Warp Timer (Reload Room)" u16 0x801F0352 - "Dodongo's Cavern Warp Timer" u16 0x801E30B2 - "Jabu-Jabu Warp Timer" u16 0x802008B2 - "Forest Temple Warp Timer" u16 0x801EC5B2 - "Fire Temple Warp Timer" u16 0x801F3E42 - "Water Temple Warp Timer" u16 0x801F8762 - "Shadow Temple Warp Timer" u16 0x801F48A2 - "Spirit Temple Warp Timer" u16 0x801FD562 - "Deku Tree Warp Timer" u16 0x801F83A2 */ -}; +}}; // clang-format on +void LoadValueConfig() { + auto allConfig = Ship::Context::GetInstance()->GetConfig()->GetNestedJson(); + if (allConfig.find("ValueViewer") == allConfig.end() || !allConfig["ValueViewer"].is_array()) { + allConfig["ValueViewer"] = nlohmann::json::array(); + } + valueViewerSettings = allConfig["ValueViewer"]; +} + +void SaveValueConfig() { + auto allConfig = Ship::Context::GetInstance()->GetConfig()->GetNestedJson(); + allConfig["ValueViewer"] = valueViewerSettings; + Ship::Context::GetInstance()->GetConfig()->SetBlock("ValueViewer", valueViewerSettings); + Ship::Context::GetInstance()->GetConfig()->Save(); +} + extern "C" void ValueViewer_Draw(GfxPrint* printer) { - for (size_t i = 0; i < valueTable.size(); i++) { + for (size_t i = 0; i < VVE_MAX; i++) { ValueTableElement& element = valueTable[i]; - if (!element.isActive || !element.isPrinted || (gPlayState == NULL && element.requiresPlayState)) + if (!valueViewerSettings.contains((ValueViewerEntry)i) || (gPlayState == NULL && element.requiresPlayState)) + continue; + ValueSetting& setting = valueViewerSettings[(ValueViewerEntry)i]; + if (!setting.isPrinted) continue; - GfxPrint_SetColor(printer, element.color.x * 255, element.color.y * 255, element.color.z * 255, - element.color.w * 255); - GfxPrint_SetPos(printer, element.x, element.y); + void* elementValue = element.valueFn(); + if (elementValue == NULL) + continue; + GfxPrint_SetColor(printer, setting.color.x * 255, setting.color.y * 255, setting.color.z * 255, + setting.color.w * 255); + GfxPrint_SetPos(printer, setting.x, setting.y); switch (element.type) { case TYPE_S8: - GfxPrint_Printf(printer, (element.typeFormat ? "%s0x%x" : "%s%d"), element.prefix.c_str(), - *(s8*)element.valueFn()); + GfxPrint_Printf(printer, (setting.typeFormat ? "%s0x%x" : "%s%d"), setting.prefix.c_str(), + *(s8*)elementValue); break; case TYPE_U8: - GfxPrint_Printf(printer, (element.typeFormat ? "%s0x%x" : "%s%u"), element.prefix.c_str(), - *(u8*)element.valueFn()); + GfxPrint_Printf(printer, (setting.typeFormat ? "%s0x%x" : "%s%u"), setting.prefix.c_str(), + *(u8*)elementValue); break; case TYPE_S16: - GfxPrint_Printf(printer, (element.typeFormat ? "%s0x%x" : "%s%d"), element.prefix.c_str(), - *(s16*)element.valueFn()); + GfxPrint_Printf(printer, (setting.typeFormat ? "%s0x%x" : "%s%d"), setting.prefix.c_str(), + *(s16*)elementValue); break; case TYPE_U16: - GfxPrint_Printf(printer, (element.typeFormat ? "%s0x%x" : "%s%u"), element.prefix.c_str(), - *(u16*)element.valueFn()); + GfxPrint_Printf(printer, (setting.typeFormat ? "%s0x%x" : "%s%u"), setting.prefix.c_str(), + *(u16*)elementValue); break; case TYPE_S32: - GfxPrint_Printf(printer, (element.typeFormat ? "%s0x%x" : "%s%d"), element.prefix.c_str(), - *(s32*)element.valueFn()); + GfxPrint_Printf(printer, (setting.typeFormat ? "%s0x%x" : "%s%d"), setting.prefix.c_str(), + *(s32*)elementValue); break; case TYPE_U32: - GfxPrint_Printf(printer, (element.typeFormat ? "%s0x%x" : "%s%u"), element.prefix.c_str(), - *(u32*)element.valueFn()); + GfxPrint_Printf(printer, (setting.typeFormat ? "%s0x%x" : "%s%u"), setting.prefix.c_str(), + *(u32*)elementValue); + break; + case TYPE_PTR: + GfxPrint_Printf(printer, "%s%p", setting.prefix.c_str(), *(void**)elementValue); break; case TYPE_CHAR: - GfxPrint_Printf(printer, "%s%c", element.prefix.c_str(), *(char*)element.valueFn()); + GfxPrint_Printf(printer, "%s%c", setting.prefix.c_str(), *(char*)elementValue); break; case TYPE_STRING: - GfxPrint_Printf(printer, "%s%s", element.prefix.c_str(), (char*)element.valueFn()); + GfxPrint_Printf(printer, "%s%s", setting.prefix.c_str(), (char*)elementValue); break; case TYPE_FLOAT: - GfxPrint_Printf(printer, (element.typeFormat ? "%s%4.1f" : "%s%f"), element.prefix.c_str(), - *(float*)element.valueFn()); + GfxPrint_Printf(printer, (setting.typeFormat ? "%s%4.1f" : "%s%f"), setting.prefix.c_str(), + *(float*)elementValue); break; + default: + SPDLOG_ERROR("ValueViewer_Draw reached `default`, got {}", static_cast(element.type)); + assert(false); } } } @@ -158,14 +178,14 @@ void ValueViewerWindow::DrawElement() { UIWidgets::CVarCheckbox("Enable Printing", CVAR_NAME, UIWidgets::CheckboxOptions().Color(THEME_COLOR)); ImGui::BeginGroup(); - static int selectedElement = -1; + static size_t selectedElement = -1; std::string selectedElementText = (selectedElement == -1) ? "Select a value" : (std::string(valueTable[selectedElement].name) + " (" + std::string(valueTable[selectedElement].path) + ")"); UIWidgets::PushStyleCombobox(THEME_COLOR); if (ImGui::BeginCombo("##valueViewerElement", selectedElementText.c_str())) { for (size_t i = 0; i < valueTable.size(); i++) { - if (valueTable[i].isActive) + if (valueViewerSettings.contains((ValueViewerEntry)i)) continue; bool isSelected = (selectedElement == i); std::string elementText = (std::string(valueTable[i].name) + " (" + std::string(valueTable[i].path) + ")"); @@ -182,64 +202,85 @@ void ValueViewerWindow::DrawElement() { ImGui::SameLine(); UIWidgets::PushStyleButton(THEME_COLOR); if (selectedElement != -1 && ImGui::Button("+")) { - valueTable[selectedElement].isActive = true; + valueViewerSettings.insert( + { (ValueViewerEntry)selectedElement, + { valueTable[selectedElement].prefix, ImVec4(1.0f, 1.0f, 1.0f, 1.0f), false, false, 0, 0 } }); selectedElement = -1; + SaveValueConfig(); } UIWidgets::PopStyleButton(); ImGui::EndGroup(); for (size_t i = 0; i < valueTable.size(); i++) { ValueTableElement& element = valueTable[i]; - if (!element.isActive || (gPlayState == NULL && element.requiresPlayState)) + if (!valueViewerSettings.contains((ValueViewerEntry)i) || (gPlayState == NULL && element.requiresPlayState)) + continue; + void* elementValue = element.valueFn(); + if (elementValue == nullptr) continue; UIWidgets::PushStyleButton(THEME_COLOR); UIWidgets::PushStyleCheckbox(THEME_COLOR); ImGui::AlignTextToFramePadding(); if (ImGui::Button((ICON_FA_TIMES + std::string("##") + std::string(element.name)).c_str())) { - element.isActive = false; - element.isPrinted = false; + valueViewerSettings.erase((ValueViewerEntry)i); + UIWidgets::PopStyleCheckbox(); + UIWidgets::PopStyleButton(); + SaveValueConfig(); + continue; } UIWidgets::PopStyleCheckbox(); UIWidgets::PopStyleButton(); + ValueSetting& setting = valueViewerSettings[(ValueViewerEntry)i]; ImGui::SameLine(); ImGui::Text("%s:", element.name); ImGui::SameLine(); switch (element.type) { case TYPE_S8: - ImGui::Text(element.typeFormat ? "0x%x" : "%d", *(s8*)element.valueFn()); + ImGui::Text(setting.typeFormat ? "0x%x" : "%d", *(s8*)elementValue); break; case TYPE_U8: - ImGui::Text(element.typeFormat ? "0x%x" : "%u", *(u8*)element.valueFn()); + ImGui::Text(setting.typeFormat ? "0x%x" : "%u", *(u8*)elementValue); break; case TYPE_S16: - ImGui::Text(element.typeFormat ? "0x%x" : "%d", *(s16*)element.valueFn()); + ImGui::Text(setting.typeFormat ? "0x%x" : "%d", *(s16*)elementValue); break; case TYPE_U16: - ImGui::Text(element.typeFormat ? "0x%x" : "%u", *(u16*)element.valueFn()); + ImGui::Text(setting.typeFormat ? "0x%x" : "%u", *(u16*)elementValue); break; case TYPE_S32: - ImGui::Text(element.typeFormat ? "0x%x" : "%d", *(s32*)element.valueFn()); + ImGui::Text(setting.typeFormat ? "0x%x" : "%d", *(s32*)elementValue); break; case TYPE_U32: - ImGui::Text(element.typeFormat ? "0x%x" : "%u", *(u32*)element.valueFn()); + ImGui::Text(setting.typeFormat ? "0x%x" : "%u", *(u32*)elementValue); + break; + case TYPE_PTR: + ImGui::Text("%p", *(void**)elementValue); break; case TYPE_CHAR: - ImGui::Text("%c", *(char*)element.valueFn()); + ImGui::Text("%c", *(char*)elementValue); break; case TYPE_STRING: - ImGui::Text("%s", (char*)element.valueFn()); + ImGui::Text("%s", (char*)elementValue); break; case TYPE_FLOAT: - ImGui::Text(element.typeFormat ? "%4.1f" : "%f", *(float*)element.valueFn()); + ImGui::Text(setting.typeFormat ? "%4.1f" : "%f", *(float*)elementValue); break; + default: + SPDLOG_ERROR("ValueViewerWindow::DrawElement reached `default`, got {}", + static_cast(element.type)); + assert(false); } ImGui::SameLine(); UIWidgets::PushStyleCheckbox(THEME_COLOR); if (element.type <= TYPE_U32) { - ImGui::Checkbox(("Hex##" + std::string(element.name)).c_str(), &element.typeFormat); + if (ImGui::Checkbox(("Hex##" + std::string(element.name)).c_str(), &setting.typeFormat)) { + SaveValueConfig(); + } ImGui::SameLine(); } else if (element.type == TYPE_FLOAT) { - ImGui::Checkbox(("Trim##" + std::string(element.name)).c_str(), &element.typeFormat); + if (ImGui::Checkbox(("Trim##" + std::string(element.name)).c_str(), &setting.typeFormat)) { + SaveValueConfig(); + } ImGui::SameLine(); } UIWidgets::PopStyleCheckbox(); @@ -247,20 +288,26 @@ void ValueViewerWindow::DrawElement() { ImGui::BeginGroup(); if (CVarGetInteger(CVAR_DEVELOPER_TOOLS("ValueViewerEnablePrinting"), 0)) { UIWidgets::PushStyleCheckbox(THEME_COLOR); - ImGui::Checkbox(("Print##" + std::string(element.name)).c_str(), &element.isPrinted); + if (ImGui::Checkbox(("Print##" + std::string(element.name)).c_str(), &setting.isPrinted)) { + SaveValueConfig(); + } UIWidgets::PopStyleCheckbox(); - if (element.isPrinted) { + if (setting.isPrinted) { char* prefix = (char*)element.prefix.c_str(); ImGui::SameLine(); ImGui::SetNextItemWidth(80.0f); UIWidgets::PushStyleInput(THEME_COLOR); if (ImGui::InputText(("Prefix##" + std::string(element.name)).c_str(), prefix, 10)) { - element.prefix = prefix; + setting.prefix = prefix; + SaveValueConfig(); } UIWidgets::PopStyleInput(); ImGui::SameLine(); - ImGui::ColorEdit3(("##color" + std::string(element.name)).c_str(), (float*)&element.color, - ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel); + if (ImGui::ColorEdit3(("##color" + std::string(element.name)).c_str(), (float*)&setting.color, + ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel)) { + + SaveValueConfig(); + } ImGui::SameLine(); UIWidgets::PushStyleCheckbox(THEME_COLOR); if (ImGui::Button(("Position##" + std::string(element.name)).c_str())) { @@ -268,8 +315,12 @@ void ValueViewerWindow::DrawElement() { } UIWidgets::PopStyleCheckbox(); if (ImGui::BeginPopup(("Position Picker##" + std::string(element.name)).c_str())) { - ImGui::DragInt("X", (int*)&element.x, 1.0f, 0, 44); - ImGui::DragInt("Y", (int*)&element.y, 1.0f, 0, 29); + if (ImGui::DragInt("X", (int*)&setting.x, 1.0f, 0, 44)) { + SaveValueConfig(); + } + if (ImGui::DragInt("Y", (int*)&setting.y, 1.0f, 0, 29)) { + SaveValueConfig(); + } ImGui::EndPopup(); } } @@ -280,4 +331,9 @@ void ValueViewerWindow::DrawElement() { } void ValueViewerWindow::InitElement() { + static bool loadedConfig = false; + if (!loadedConfig) { + LoadValueConfig(); + loadedConfig = true; + } } diff --git a/soh/soh/Enhancements/debugger/valueViewer.h b/soh/soh/Enhancements/debugger/valueViewer.h index 04d698d2c81..c841f19c630 100644 --- a/soh/soh/Enhancements/debugger/valueViewer.h +++ b/soh/soh/Enhancements/debugger/valueViewer.h @@ -11,6 +11,7 @@ typedef enum { TYPE_U16, TYPE_S32, TYPE_U32, + TYPE_PTR, TYPE_CHAR, TYPE_STRING, TYPE_FLOAT, @@ -25,13 +26,56 @@ typedef struct { ValueType type; bool requiresPlayState; ValueFn valueFn; +} ValueTableElement; + +typedef struct { + std::string prefix; ImVec4 color; - bool isActive; bool isPrinted; bool typeFormat; uint32_t x; uint32_t y; -} ValueTableElement; +} ValueSetting; + +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ImVec4, x, y, z, w) +NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(ValueSetting, prefix, color, isPrinted, typeFormat, x, y) + +typedef enum { + VVE_TIME, + VVE_AGE, + VVE_HEALTH, + VVE_NAVI_TIMER, + VVE_SCENE_ID, + VVE_ROOM_ID, + VVE_ENTRANCE_ID, + VVE_CUTSCENE_ID, + VVE_LINK_X_POS, + VVE_LINK_Y_POS, + VVE_LINK_Z_POS, + VVE_LINK_YAW, + VVE_LINK_VELOCITY, + VVE_LINK_X_VELOCITY, + VVE_LINK_Y_VELOCITY, + VVE_LINK_Z_VELOCITY, + VVE_TEXT_ID, + VVE_ANALOG_STICK_X, + VVE_ANALOG_STICK_Y, + VVE_GETITEMID, + VVE_GETITEMENTRY, + VVE_MOVEMENT_ANGLE, + VVE_LAST_ITEM_PRESSED, + VVE_STICK_TIMER, + VVE_LAST_DAMAGE_VALUE, + VVE_CAMERA_ANGLE, + VVE_CAMERA_SPEED, + VVE_GLOBAL_FRAME_COUNTER, + VVE_CUTSCENE_POINTER, + VVE_FRAMERATE_DIVISOR, + VVE_NEXT_HUD_MOD, + VVE_TEMP_B_VALUE, + VVE_BLUE_WARP_TIMER, + VVE_MAX, +} ValueViewerEntry; class ValueViewerWindow final : public Ship::GuiWindow { public: diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h index 5d3e99c79bf..2cceebc4845 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_HookTable.h @@ -63,6 +63,7 @@ DEFINE_HOOK(OnDialogMessage, ()); DEFINE_HOOK(OnPresentTitleCard, ()); DEFINE_HOOK(OnInterfaceUpdate, ()); DEFINE_HOOK(OnKaleidoscopeUpdate, (int16_t inDungeonScene)); +DEFINE_HOOK(OnMinimapDrawCompassIcons, ()); DEFINE_HOOK(OnPresentFileSelect, ()); DEFINE_HOOK(OnUpdateFileSelectSelection, (uint16_t optionIndex)); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp index 3375d30c78c..b754e9c949c 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.cpp @@ -298,6 +298,10 @@ void GameInteractor_ExecuteOnKaleidoscopeUpdate(int16_t inDungeonScene) { GameInteractor::Instance->ExecuteHooks(inDungeonScene); } +void GameInteractor_ExecuteOnMinimapDrawCompassIcons() { + GameInteractor::Instance->ExecuteHooks(); +} + // MARK: - Main Menu void GameInteractor_ExecuteOnPresentFileSelect() { diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h index e998812f523..c93568712bf 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_Hooks.h @@ -69,6 +69,7 @@ void GameInteractor_ExecuteOnDialogMessage(); void GameInteractor_ExecuteOnPresentTitleCard(); void GameInteractor_ExecuteOnInterfaceUpdate(); void GameInteractor_ExecuteOnKaleidoscopeUpdate(int16_t inDungeonScene); +void GameInteractor_ExecuteOnMinimapDrawCompassIcons(); // MARK: - Main Menu void GameInteractor_ExecuteOnPresentFileSelect(); diff --git a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp index 9ffdb245230..dc669dbb20e 100644 --- a/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp +++ b/soh/soh/Enhancements/game-interactor/GameInteractor_RawAction.cpp @@ -46,10 +46,10 @@ void GameInteractor::RawAction::AddOrRemoveMagic(int8_t amount) { void GameInteractor::RawAction::HealOrDamagePlayer(int16_t hearts) { if (hearts > 0) { - Health_ChangeBy(gPlayState, hearts * FULL_HEART_HEALTH); + Health_ChangeBy(gPlayState, hearts * FULL_HEART_HEALTH, GET_PLAYER(gPlayState)); } else if (hearts < 0) { Player* player = GET_PLAYER(gPlayState); - Health_ChangeBy(gPlayState, hearts * FULL_HEART_HEALTH); + Health_ChangeBy(gPlayState, hearts * FULL_HEART_HEALTH, player); func_80837C0C(gPlayState, player, 0, 0, 0, 0, 0); player->invincibilityTimer = 28; } diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index ae9ea89581a..aba9f1e6833 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -195,14 +195,38 @@ typedef enum { // - `*EnTk` VB_BE_VALID_GRAVEDIGGING_SPOT, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - 'EnHy*' + VB_BEGGAR_GIVE_ITEM, + // #### `result` // ```c // this->collider.base.acFlags & 2 || blueFireArrowHit // ``` // #### `args` - // - None + // - `*BgBreakwall` VB_BG_BREAKWALL_BREAK, + // #### `result` + // ```c + // this->cylinder1.base.acFlags & AC_HIT + // ``` + // #### `args` + // - `*BgIceShelter` + VB_BG_ICE_SHELTER_HIT, + + // #### `result` + // ```c + // (this->cylinder1.base.ac != NULL) && (this->cylinder1.base.ac->id == ACTOR_EN_ICE_HONO) + // ``` + // #### `args` + // - `*BgIceShelter` + VB_BG_ICE_SHELTER_MELT, + // #### `result` // ```c // gSaveContext.bgsFlag @@ -1243,6 +1267,14 @@ typedef enum { // - None VB_HEARTS_INCREASE_WITH_CONTAINERS, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*BgIceTurara` + VB_ICICLE_SETUP_DRAW, + // #### `result` // ```c // (respawnFlag == 1) || (respawnFlag == -1) @@ -1919,6 +1951,22 @@ typedef enum { // - `*EnRd` VB_REDEAD_GIBDO_FREEZE_LINK, + // #### `result` + // ```c + // this->alpha <= 0 + // ``` + // #### `args` + // - `*BgIceShelter` + VB_RED_ICE_DROP_ITEM, + + // #### `result` + // ```c + // !((this->dyna.actor.params >> 6) & 1) && (Flags_GetSwitch(play, this->dyna.actor.params & 0x3F)) + // ``` + // #### `args` + // - `*BgIceShelter` + VB_RED_ICE_MELTED_FLAG, + // #### `result` // #### `result` // ```c @@ -2092,6 +2140,14 @@ typedef enum { // - None VB_SHOW_TITLE_CARD, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - None + VB_SKIP_TALKING, + // #### `result` // ```c // (collectible >= 0) && (collectible <= 0x19 @@ -2162,6 +2218,14 @@ typedef enum { // - `*BossVa` VB_SPAWN_BLUE_WARP, + // #### `result` + // ```c + // this->timer == 4 + // ``` + // #### `args` + // - `*EnButte` + VB_SPAWN_BUTTERFLY_FAIRY, + // #### `result` // ```c // INV_CONTENT(ITEM_ARROW_FIRE) == ITEM_NONE @@ -2223,6 +2287,22 @@ typedef enum { // - None VB_SPEAK, + // #### `result` + // ```c + // this->dyna.actor.params == TURARA_STALACTITE_REGROW + // ``` + // #### `args` + // - `*BgIceTurara` + VB_STALACTITE_DROP_ITEM, + + // #### `result` + // ```c + // this->collider.base.acFlags & AC_HIT + // ``` + // #### `args` + // - `*BgIceTurara` + VB_STALAGMITE_DROP_ITEM, + // #### `result` // ```c // varies, never set should to true @@ -2387,6 +2467,42 @@ typedef enum { // - None VB_WIN_GORON_POT, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - `*EnWonderItem` + VB_WONDER_DROP_ITEM, + + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - 'Vec3f' pos + // - 'f32' rotY + VB_WONDER_HEISHI_ITEM, + + // #### `result` + // ```c + // (gSaveContext.dayTime < 0xB888 || IS_DAY) && ((!IS_RANDO && + // !Flags_GetEventChkInf(EVENTCHKINF_ZELDA_FLED_HYRULE_CASTLE)) || (IS_RANDO && !metZelda)) + // (gSaveContext.dayTime >= 0xB889) || !IS_DAY || (!IS_RANDO && + // Flags_GetEventChkInf(EVENTCHKINF_ZELDA_FLED_HYRULE_CASTLE)) || (IS_RANDO && metZelda) + // ``` + // #### `args` + // - EnHeishi1* + VB_WONDER_HEISHI_PATROLLING, + + // #### `result` + // ```c + // (this->switchFlag >= 0) && Flags_GetSwitch(play, this->switchFlag) + // ``` + // #### `args` + // - `None` + VB_WONDER_SPAWN, + // #### `result` // ```c // true diff --git a/soh/soh/Enhancements/mod_menu.cpp b/soh/soh/Enhancements/mod_menu.cpp index ff9a459baab..cde9dc7b9c7 100644 --- a/soh/soh/Enhancements/mod_menu.cpp +++ b/soh/soh/Enhancements/mod_menu.cpp @@ -352,17 +352,15 @@ void ModMenuWindow::DrawElement() { if (ImGui::BeginChild("Enabled Mods", ImVec2(0, -8))) { DrawMods(true); - - ImGui::EndChild(); } + ImGui::EndChild(); /*ImGui::TableNextColumn(); if (ImGui::BeginChild("Disabled Mods", ImVec2(0, -8))) { DrawMods(false); - - ImGui::EndChild(); - }*/ + } + ImGui::EndChild();*/ ImGui::EndTable(); } diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp index 401f00b561f..c0a7060cd2b 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list.cpp @@ -2237,10 +2237,9 @@ void StaticData::HintTable_Init() { /*french*/ "J'ai entendu dire que Ganondorf aurait caché les #Flèches de Lumière# dans #[[1]]#.", {QM_YELLOW, QM_RED})); - hintTextTable[RHT_BOSS_KEY_HINT] = HintText(CustomMessage("The #boss key# for this door is in #[[1]]#!", + hintTextTable[RHT_BOSS_KEY_HINT] = HintText(CustomMessage("%c@! I can hear Rauru's guidance! He's saying the %ykey for this door%c is in %w[[1]]%c!", /*german*/ TODO_TRANSLATE, - /*french*/ TODO_TRANSLATE, - {QM_GREEN, QM_RED})); + /*french*/ TODO_TRANSLATE)); hintTextTable[RHT_DAMPE_DIARY] = HintText(CustomMessage("Whoever reads this, please enter #[[1]]#. I will let you have my #stretching, shrinking keepsake#.^I'm waiting for you.&--Dampé", /*german*/ "Wer immer dies liest, der möge #[[1]]# nach meinem #langen, kurzen Schatz# suchen.^Ich warte!&Boris", diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp index aa375716f68..550d77b9a62 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp @@ -131,6 +131,10 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß eine #Kiste im Deku-Baum# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans l'Arbre Mojo# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_WONDER_ITEM_DEKU_TREE] = HintText(CustomMessage("They say that a #wonder item in the Deku Tree# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand im Deku-Baum# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans l'Arbre Mojo# cache #[[1]]#.", {QM_RED, QM_GREEN})); + /*-------------------------- | DODONGOS CAVERN | ---------------------------*/ @@ -313,6 +317,10 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß eine #Kiste in Dodongos Höhle# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans la Caverne Dodongo# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_SIGN_DODONGOS_CAVERN] = HintText(CustomMessage("They say that #reading a pedestal in Dodongo's Cavern# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Podests in Dodongos Höhle# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un piédestal dans la Caverne Dodongo# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + /*-------------------------- | JABU JABUS BELLY | ---------------------------*/ @@ -480,6 +488,10 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß eine #Kiste in Jabu-Jabus Bauch# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Ventre de Jabu-Jabu# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_WONDER_ITEM_JABU_JABU] = HintText(CustomMessage("They say that a #wonder item in Jabu Jabu's Belly# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand in Jabu-Jabus Bauch# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans le Ventre de Jabu-Jabu# cache #[[1]]#.", {QM_RED, QM_GREEN})); + /*-------------------------- | FOREST TEMPLE | ---------------------------*/ @@ -922,6 +934,10 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß eine #Kiste im Feuertempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Temple du Feu# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_WONDER_ITEM_FIRE_TEMPLE] = HintText(CustomMessage("They say that a #wonder item in Fire Temple# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand im Feuertempel# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans le Temple du Feu# cache #[[1]]#.", {QM_RED, QM_GREEN})); + /*-------------------------- | WATER TEMPLE | ---------------------------*/ @@ -1073,6 +1089,10 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß eine #Kiste im Wassertempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Temple de l'Eau# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_WONDER_ITEM_WATER_TEMPLE] = HintText(CustomMessage("They say that a #wonder item in Water Temple# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand im Wassertempel# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans le Temple de l'Eau# cache #[[1]]#.", {QM_RED, QM_GREEN})); + /*-------------------------- | SPIRIT TEMPLE | ---------------------------*/ @@ -1322,6 +1342,14 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß eine #Kiste im Geistertempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Temple de l'Esprit# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_SIGN_SPIRIT_TEMPLE] = HintText(CustomMessage("They say that #reading a statue in Spirit Temple# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen einer Statue im Geistertempel# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire une statue dans le Temple de l'Esprit# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_SPIRIT_TEMPLE] = HintText(CustomMessage("They say that a #wonder item in Spirit Temple# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand im Geistertempel# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans le Temple de l'Esprit# cache #[[1]]#.", {QM_RED, QM_GREEN})); + /*-------------------------- | SHADOW TEMPLE | ---------------------------*/ @@ -1589,6 +1617,14 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß eine #Kiste im Schattentempel# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Temple de l'Ombre# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_SIGN_SHADOW_TEMPLE] = HintText(CustomMessage("They say that #reading a sign in Shadow Temple# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes im Schattentempel# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau dans le Temple de l'Ombre# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_SHADOW_TEMPLE] = HintText(CustomMessage("They say that a #wonder item in Shadow Temple# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand im Schattentempel# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans le Temple de l'Ombre# cache #[[1]]#.", {QM_RED, QM_GREEN})); + /*-------------------------- | BOTTOM OF THE WELL | ---------------------------*/ @@ -1735,6 +1771,9 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß etwas #Gras auf dem Grund des Brunnens# #[[1]]# verstecke.", /*french*/ "Selon moi, de l'#herbe dans le Puits# cache #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_BOTTOM_OF_THE_WELL_WONDER_ITEM] = HintText(CustomMessage("They say that a #wonder item in the Bottom of the Well# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand auf dem Grund des Brunnens# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans le Puits# cache #[[1]]#.", {QM_RED, QM_GREEN})); /*-------------------------- | ICE CAVERN | @@ -1826,6 +1865,14 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, #appeler la pluie près de l’entrée d’une grotte gelée# révèle #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #Skulltula tras un ardiente hielo# otorga #[[1]]#. + hintTextTable[RHT_ICE_CAVERN_ICICLE] = HintText(CustomMessage("They say that #breaking an icicle in a frozen cavern# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Zerschlagen eines Eiszapfens in einer gefrorenen Kaverne# #[[1]]# enthülle.", + /*french*/ "Selon moi, #briser un stalactite de glace dans la Caverne Polaire# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_ICE_CAVERN_RED_ICE] = HintText(CustomMessage("They say that #melting red ice in a frozen cavern# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schmelzen von rotem Eis in einer gefrorenen Kaverne# #[[1]]# gäbe.", + /*french*/ "Selon moi, #faire fondre la glace rouge dans la Caverne Polaire# donne #[[1]]#.", {QM_RED, QM_GREEN})); + /*-------------------------- | Gerudo Training Ground | ---------------------------*/ @@ -2016,6 +2063,18 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß eine #Kiste in der Gerudo-Trainingsarena# #[[1]]# enthielte.", /*french*/ "Selon moi, une #caisse dans le Gymnase Gerudo# contient #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_WONDER_ITEM_GERUDO_TRAINING_GROUND] = HintText(CustomMessage("They say that a #wonder item in Gerudo Training Ground# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand in der Gerudo-Trainingsarena# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans le Gymnase Gerudo# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_GERUDO_TRAINING_GROUND_ICICLE] = HintText(CustomMessage("They say that #breaking an icicle in in Gerudo Training Ground# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Zerschlagen eines Eiszapfens in der Gerudo-Trainingsarena# #[[1]]# enthülle.", + /*french*/ "Selon moi, #briser un stalactite de glace dans le Gymnase Gerudo# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_GERUDO_TRAINING_GROUND_RED_ICE] = HintText(CustomMessage("They say that #melting red ice in Gerudo Training Ground# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schmelzen von rotem Eis in der Gerudo-Trainingsarena# #[[1]]# gäbe.", + /*french*/ "Selon moi, #faire fondre la glace rouge dans le Gymnase Gerudo# donne #[[1]]#.", {QM_RED, QM_GREEN})); + /*-------------------------- | GANONS CASTLE | ---------------------------*/ @@ -2225,6 +2284,17 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*german*/ "Man erzählt sich, daß ein #Herz in Ganons Schloß# #[[1]]# verstecke.", /*french*/ "Selon moi, un #coeur dans le Château de Ganon# cache #[[1]]#.", {QM_RED, QM_GREEN})); + hintTextTable[RHT_GANONS_CASTLE_WONDER_ITEM] = HintText(CustomMessage("They say that a #wonder item in Ganon's Castle# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand in Ganons Schloß# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans le Château de Ganon# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_GANONS_CASTLE_ICICLE] = HintText(CustomMessage("They say that #breaking an icicle in Ganon's Castle# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Zerschlagen eines Eiszapfens Ganons Schloß# #[[1]]# enthülle.", + /*french*/ "Selon moi, #briser un stalactite de glace dans le Château de Ganon# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_GANONS_CASTLE_RED_ICE] = HintText(CustomMessage("They say that #melting red ice in a Ganon's Castle# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schmelzen von rotem Eis in Ganons Schloß# #[[1]]# gäbe.", + /*french*/ "Selon moi, #faire fondre la glace rouge dans le Château de Ganon# donne #[[1]]#.", {QM_RED, QM_GREEN})); // clang-format on } } // namespace Rando diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp index 487c761584b..175f8be7c3b 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_overworld.cpp @@ -2135,6 +2135,199 @@ void StaticData::HintTable_Init_Exclude_Overworld() { HintText(CustomMessage("They say that a #bush in Zora's Fountain# contains #[[1]]#.", /*german*/ "", /*french*/ "Selon moi, un #buisson à la Fontaine Zora# cache #[[1]]#.", { QM_RED, QM_GREEN })); + + hintTextTable[RHT_BUTTERFLY_FAIRY_HYRULE_CASTLE] = HintText(CustomMessage("They say that a #butterfly near the castle# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß ein #Schmetterling in der Nähe des Schlosses# #[[1]]# enthülle.", + /*french*/ "Selon moi, une #un papillon près du château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_BUTTERFLY_FAIRY_LOST_WOODS] = HintText(CustomMessage("They say that a #butterfly in the woods# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß ein #Schmetterling im Wald# #[[1]]# enthülle.", + /*french*/ "Selon moi, une #un papillon dans les bois# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_BUTTERFLY_FAIRY_GRAVEYARD] = HintText(CustomMessage("They say that a #butterfly in the graveyard# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß ein #Schmetterling auf dem Friedhof# #[[1]]# enthülle.", + /*french*/ "Selon moi, une #un papillon dans le cimetière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_BUTTERFLY_FAIRY_ZORAS_RIVER] = HintText(CustomMessage("They say that a #butterfly near a river# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß ein #Schmetterling in der Nähe eines Flusses# #[[1]]# enthülle.", + /*french*/ "Selon moi, une #un papillon près d'une rivière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_BUTTERFLY_FAIRY_ZORAS_FOUNTAIN] = HintText(CustomMessage("They say that a #butterfly on a log# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß ein #Schmetterling auf einem Baumstamm# #[[1]]# enthülle.", + /*french*/ "Selon moi, une #un papillon sur une bûche# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_BUTTERFLY_FAIRY_LAKE_HYLIA] = HintText(CustomMessage("They say that a #butterfly near a lake# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß ein #Schmetterling in der Nähe eines Sees# #[[1]]# enthülle.", + /*french*/ "Selon moi, une #un papillon près d'un lac# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_KOKIRI_FOREST] = HintText(CustomMessage("They say that #reading a sign in a forest# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes in einem Wald# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau dans un fôret# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_LINKS_HOUSE] = HintText(CustomMessage("They say that #reading a sign in Link's House# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes in Links Haus# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau dans la Maison de Link# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_DEKU_THEATER] = HintText(CustomMessage("They say that #reading a sign in an underground theater# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes in einem unterirdischen Theater# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau dans un théâtre souterrain# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_HYRULE_FIELD] = HintText(CustomMessage("They say that #reading a sign in an underground theater# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes in der Ebene von Hyrule# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau dans la Plaine d'Hyrule# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_MK_SHOOTING_GALLERY] = HintText(CustomMessage("They say that #reading before shooting in youth# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen vor dem Schießen in der Jugend# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire avant de tirer dans sa jeunesse# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_KAK_SHOOTING_GALLERY] = HintText(CustomMessage("They say that #reading before shooting in maturity# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen vor dem Schießen im Erwachsenenalter# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire avant de tirer dans sa maturité# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_HAPPY_MASK_SHOP] = HintText(CustomMessage("They say that #reading a sign in a mask shop# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes in einem Maskenladen# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau dans un magasin de masques# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_TEMPLE_OF_TIME] = HintText(CustomMessage("They say that #reading a sign in a mask shop# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Altars im Tempel der Zeit# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un autel dans le Temple du Temps# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_HYRULE_CASTLE] = HintText(CustomMessage("They say that #reading a sign near the castle# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes in der Nähe des Schloßes# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau rès du château# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_KAKARIKO_VILLAGE] = HintText(CustomMessage("They say that #reading a sign in Kakariko Village# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes in Kakariko# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau dans le Village de Cocorico# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_GRAVEYARD] = HintText(CustomMessage("They say that #reading in the graveyard# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen auf dem Friedhof# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire dans le cimetière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_DEATH_MOUNTAIN_TRAIL] = HintText(CustomMessage("They say that #reading a sign on Death Mountain Trail# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes auf dem Todesberggipfel-Pfad# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau sur le sentier de la Montagne de la Mort# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_GORON_CITY] = HintText(CustomMessage("They say that #reading a sign in Goron City# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes in Goronia# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau dans le Village Goron# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_DEATH_MOUNTAIN_CRATER] = HintText(CustomMessage("They say that #reading a sign in Death Mountain Crater# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes im Todeskrater# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau sur le Mont du Péril# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_ZORAS_RIVER] = HintText(CustomMessage("They say that #reading a sign near a river# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes in der Nähe eines Flusses# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau près d'une rivière# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_ZORAS_DOMAIN] = HintText(CustomMessage("They say that #reading a sign in Zora's Domain# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes in Zoras Reich# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau dans le Domaine Zora# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_ZORAS_FOUNTAIN] = HintText(CustomMessage("They say that #reading a sign in Zora's Fountain# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes in Zoras Quelle# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau dans la Fontaine Zora# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_LAKE_HYLIA] = HintText(CustomMessage("They say that #reading near a lake# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen in der Nähe eines Sees# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire près d'un lac# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_FISHING_POND] = HintText(CustomMessage("They say that #reading a sign in a fishing pond# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes in einem Fischteich# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau dans un étang de pêche# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_GERUDO_VALLEY] = HintText(CustomMessage("They say that #reading a sign in Gerudo Valley# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes im Gerudotalh# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau dans une vallée# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_GERUDO_FORTRESS] = HintText(CustomMessage("They say that #reading a sign in Gerudo Fortress# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes in der Gerudo-Festung# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau dans la Forteresse Gerudo# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_SIGN_HAUNTED_WASTELAND] = HintText(CustomMessage("They say that #reading a sign in Haunted Wasteland# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Lesen eines Schildes in der Gespensterwüste# #[[1]]# enthülle.", + /*french*/ "Selon moi, #lire un panneau dans le Désert Hanté# révèle #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_KOKIRI_FOREST] = HintText(CustomMessage("They say that a #wonder item in Kokiri Forest# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand im Kokiri-Wald# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans la Fôret Kokiri# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_HYRULE_FIELD] = HintText(CustomMessage("They say that a #wonder item in Hyrule Field# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand in der Ebene von Hyrule# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans la Plaine d'Hyrule# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_MARKET] = HintText(CustomMessage("They say that a #wonder item in the Market# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand auf dem Markt# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans la Place du Marché# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_LON_LON_RANCH] = HintText(CustomMessage("They say that a #wonder item in Lon Lon Ranch# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand auf der Lon Lon-Farm# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans le Ranch Lon Lon# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_HYRULE_CASTLE] = HintText(CustomMessage("They say that a #wonder item in Hyrule Castle# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand im Schloss von Hyrule# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux au Château d'Hyrule# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_CASTLE_COURTYARD_ZELDA] = HintText(CustomMessage("They say that #shooting a window in a castle# reveals #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schießen auf ein Fenster in einem Schloss# #[[1]]# enthülle.", + /*french*/ "Selon moi, #tirer sur une fenêtre dans un château# donne #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_LOST_WOODS] = HintText(CustomMessage("They say that a #wonder item in the woods# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand in den Wäldern# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans des bois# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_SACRED_FOREST_MEADOW] = HintText(CustomMessage("They say that a #wonder item in a forest meadow# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand auf einer Waldlichtung# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans le sanctuaire des bois# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_KAKARIKO_VILLAGE] = HintText(CustomMessage("They say that a #wonder item in Kakariko Village# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand in Kakariko# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans le Village de Cocorico# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_DAMPES_GRAVE] = HintText(CustomMessage("They say that a #wonder item in a gravekeeper's tomb# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand im Grab des Totengräbers# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans la tombe du fossoyeur# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_DEATH_MOUNTAIN_CRATER] = HintText(CustomMessage("They say that a #wonder item in Death Mountain's Crater# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand im Todeskrater# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux sur le Mont du Péril# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_ZORAS_RIVER] = HintText(CustomMessage("They say that a #wonder item in a river# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand in einem Fluss# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans une rivière# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_ZORAS_RIVER] = HintText(CustomMessage("They say that a #wonder item in Zora's Fountain# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand in Zoras Quelle# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans la Fontaine Zora# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_GERUDO_VALLEY] = HintText(CustomMessage("They say that a #wonder item in Gerudo Valley# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand im Gerudotal# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans une vallée# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_GERUDOS_FORTRESS] = HintText(CustomMessage("They say that a #wonder item in Gerudo Fortress# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand in der Gerudo-Festung# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans la Forteresse Gerudo# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_THIEVES_HIDEOUT] = HintText(CustomMessage("They say that a #wonder item in Thieve's Hideout# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand im Versteck der Diebe# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux dans a Cachette des Voleurs# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_WONDER_ITEM_DESERT_COLOSSUS] = HintText(CustomMessage("They say that a #wonder item near the temple of the sand# hides #[[1]]#.", + /*german*/ "Man erzählt sich, daß sich ein #Wunder-Gegenstand nahe des Tempels des Sandes# #[[1]]# verstecke.", + /*french*/ "Selon moi, un #objet merveilleux près du temple du sable# cache #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_BEGGAR_MARKET] = HintText(CustomMessage("They say that #trading with a beggar in the market# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Handeln mit einem Bettler auf dem Markt# #[[1]]# gäbe.", + /*french*/ "Selon moi, #échanger avec un mendiant sur le marché# donne #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_BEGGAR_KAKARIKO_VILLAGE] = HintText(CustomMessage("They say that #trading with a beggar in Kakariko Village# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Handeln mit einem Bettler in Kakariko# #[[1]]# gäbe.", + /*french*/ "Selon moi, #échanger avec un mendiant dans le Village de Cocorico# donne #[[1]]#.", {QM_RED, QM_GREEN})); + + hintTextTable[RHT_RED_ICE_ZORAS_DOMAIN] = HintText(CustomMessage("They say that #melting red ice in Zora's Domain# gives #[[1]]#.", + /*german*/ "Man erzählt sich, daß das #Schmelzen von rotem Eis in Zoras Reich# #[[1]]# gäbe.", + /*french*/ "Selon moi, #faire fondre la glace rouge dans le Domaine Zora# donne #[[1]]#.", {QM_RED, QM_GREEN})); + // clang-format on } } // namespace Rando diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index 4e6101301e9..7f1e886a946 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -447,6 +447,13 @@ void GenerateItemPool() { bool bushesActive = (bool)ctx->GetOption(RSK_SHUFFLE_BUSHES); PlaceItemsForType(RCTYPE_BUSH, bushesActive, false); + // Shuffle Wonder Items + bool overworldWonderItemsActive = ctx->GetOption(RSK_SHUFFLE_WONDER_ITEMS).Is(RO_SHUFFLE_WONDER_ITEMS_OVERWORLD) || + ctx->GetOption(RSK_SHUFFLE_WONDER_ITEMS).Is(RO_SHUFFLE_WONDER_ITEMS_ALL); + bool dungeonWonderItemsActive = ctx->GetOption(RSK_SHUFFLE_WONDER_ITEMS).Is(RO_SHUFFLE_WONDER_ITEMS_DUNGEONS) || + ctx->GetOption(RSK_SHUFFLE_WONDER_ITEMS).Is(RO_SHUFFLE_WONDER_ITEMS_ALL); + PlaceItemsForType(RCTYPE_WONDER_ITEM, overworldWonderItemsActive, dungeonWonderItemsActive); + // Shuffle Crates bool overworldCratesActive = ctx->GetOption(RSK_SHUFFLE_CRATES).Is(RO_SHUFFLE_CRATES_OVERWORLD) || ctx->GetOption(RSK_SHUFFLE_CRATES).Is(RO_SHUFFLE_CRATES_ALL); @@ -490,6 +497,10 @@ void GenerateItemPool() { ctx->PlaceItemInLocation(RC_WASTELAND_BOMBCHU_SALESMAN, RG_BOMBCHU_10, false, true); } + // Shuffle Beggar + bool beggarActive = (bool)ctx->GetOption(RSK_SHUFFLE_BEGGAR); + PlaceItemsForType(RCTYPE_BEGGAR, beggarActive, false); + if (ctx->GetOption(RSK_SHUFFLE_FROG_SONG_RUPEES)) { AddFixedItemToPool(RG_PURPLE_RUPEE, 5); } else { diff --git a/soh/soh/Enhancements/randomizer/SeedContext.cpp b/soh/soh/Enhancements/randomizer/SeedContext.cpp index 089bb5c0ad0..512984117d7 100644 --- a/soh/soh/Enhancements/randomizer/SeedContext.cpp +++ b/soh/soh/Enhancements/randomizer/SeedContext.cpp @@ -196,6 +196,7 @@ void Context::GenerateLocationPool() { !(location.GetRandomizerCheck() == RC_LW_DEKU_SCRUB_GROTTO_FRONT || location.GetRandomizerCheck() == RC_LW_DEKU_SCRUB_NEAR_BRIDGE || location.GetRandomizerCheck() == RC_HF_DEKU_SCRUB_GROTTO)) || + (location.GetRCType() == RCTYPE_BEGGAR && mOptions[RSK_SHUFFLE_BEGGAR].Is(RO_GENERIC_OFF)) || (location.GetRCType() == RCTYPE_ADULT_TRADE && mOptions[RSK_SHUFFLE_ADULT_TRADE].Is(RO_GENERIC_OFF)) || (location.GetRCType() == RCTYPE_COW && mOptions[RSK_SHUFFLE_COWS].Is(RO_GENERIC_OFF)) || (location.GetRandomizerCheck() == RC_LH_HYRULE_LOACH && @@ -211,10 +212,16 @@ void Context::GenerateLocationPool() { (location.GetRCType() == RCTYPE_STONE_FAIRY && !mOptions[RSK_SHUFFLE_STONE_FAIRIES]) || (location.GetRCType() == RCTYPE_BEAN_FAIRY && !mOptions[RSK_SHUFFLE_BEAN_FAIRIES]) || (location.GetRCType() == RCTYPE_SONG_FAIRY && !mOptions[RSK_SHUFFLE_SONG_FAIRIES]) || + (location.GetRCType() == RCTYPE_BUTTERFLY_FAIRY && !mOptions[RSK_SHUFFLE_BUTTERFLY_FAIRIES]) || (location.GetRCType() == RCTYPE_TREE && !mOptions[RSK_SHUFFLE_TREES]) || (location.GetRCType() == RCTYPE_NLTREE && (!mOptions[RSK_SHUFFLE_TREES] || mOptions[RSK_LOGIC_RULES].IsNot(RO_LOGIC_NO_LOGIC))) || (location.GetRCType() == RCTYPE_BUSH && !mOptions[RSK_SHUFFLE_BUSHES]) || + (location.GetRCType() == RCTYPE_ICICLE && !mOptions[RSK_SHUFFLE_ICICLES]) || + (location.GetRCType() == RCTYPE_RED_ICE && !mOptions[RSK_SHUFFLE_RED_ICE]) || + (location.GetRCType() == RCTYPE_SIGN && mOptions[RSK_SHUFFLE_SIGNS].Is(RO_SHUFFLE_SIGNS_OFF)) || + (location.GetRCType() == RCTYPE_WONDER_ITEM && + mOptions[RSK_SHUFFLE_WONDER_ITEMS].Is(RO_SHUFFLE_WONDER_ITEMS_OFF)) || (location.GetRCType() == RCTYPE_FREESTANDING && mOptions[RSK_SHUFFLE_FREESTANDING].Is(RO_SHUFFLE_FREESTANDING_OFF)) || (location.GetRCType() == RCTYPE_BEEHIVE && !mOptions[RSK_SHUFFLE_BEEHIVES])) { @@ -227,12 +234,15 @@ void Context::GenerateLocationPool() { mOptions[RSK_SHUFFLE_FREESTANDING].Is(RO_SHUFFLE_FREESTANDING_DUNGEONS)) || (location.GetRCType() == RCTYPE_POT && mOptions[RSK_SHUFFLE_POTS].Is(RO_SHUFFLE_POTS_DUNGEONS)) || (location.GetRCType() == RCTYPE_GRASS && mOptions[RSK_SHUFFLE_GRASS].Is(RO_SHUFFLE_GRASS_DUNGEONS)) || + (location.GetRCType() == RCTYPE_WONDER_ITEM && + mOptions[RSK_SHUFFLE_WONDER_ITEMS].Is(RO_SHUFFLE_WONDER_ITEMS_DUNGEONS)) || (location.GetRCType() == RCTYPE_CRATE && mOptions[RSK_SHUFFLE_CRATES].Is(RO_SHUFFLE_CRATES_DUNGEONS)) || (location.GetRCType() == RCTYPE_NLCRATE && mOptions[RSK_SHUFFLE_CRATES].Is(RO_SHUFFLE_CRATES_DUNGEONS) && mOptions[RSK_LOGIC_RULES].Is(RO_LOGIC_NO_LOGIC)) || (location.GetRCType() == RCTYPE_SMALL_CRATE && - mOptions[RSK_SHUFFLE_CRATES].Is(RO_SHUFFLE_CRATES_DUNGEONS))) { + mOptions[RSK_SHUFFLE_CRATES].Is(RO_SHUFFLE_CRATES_DUNGEONS)) || + (location.GetRCType() == RCTYPE_SIGN && mOptions[RSK_SHUFFLE_SIGNS].Is(RO_SHUFFLE_SIGNS_DUNGEONS))) { continue; } // If we've gotten past all the conditions where an overworld location should not be @@ -247,13 +257,17 @@ void Context::GenerateLocationPool() { (location.GetRCType() == RCTYPE_POT && mOptions[RSK_SHUFFLE_POTS].Is(RO_SHUFFLE_POTS_OVERWORLD)) || (location.GetRCType() == RCTYPE_GRASS && mOptions[RSK_SHUFFLE_GRASS].Is(RO_SHUFFLE_GRASS_OVERWORLD)) || + (location.GetRCType() == RCTYPE_WONDER_ITEM && + mOptions[RSK_SHUFFLE_WONDER_ITEMS].Is(RO_SHUFFLE_WONDER_ITEMS_OVERWORLD)) || (location.GetRCType() == RCTYPE_CRATE && mOptions[RSK_SHUFFLE_CRATES].Is(RO_SHUFFLE_CRATES_OVERWORLD)) || (location.GetRCType() == RCTYPE_NLCRATE && mOptions[RSK_SHUFFLE_CRATES].Is(RO_SHUFFLE_CRATES_OVERWORLD) && mOptions[RSK_LOGIC_RULES].Is(RO_LOGIC_NO_LOGIC)) || (location.GetRCType() == RCTYPE_SMALL_CRATE && - mOptions[RSK_SHUFFLE_CRATES].Is(RO_SHUFFLE_CRATES_OVERWORLD))) { + mOptions[RSK_SHUFFLE_CRATES].Is(RO_SHUFFLE_CRATES_OVERWORLD)) || + (location.GetRCType() == RCTYPE_SIGN && + mOptions[RSK_SHUFFLE_SIGNS].Is(RO_SHUFFLE_SIGNS_OVERWORLD))) { continue; } // also add to that dungeon's location list. diff --git a/soh/soh/Enhancements/randomizer/ShuffleBeggar.cpp b/soh/soh/Enhancements/randomizer/ShuffleBeggar.cpp new file mode 100644 index 00000000000..f1f48fbcffa --- /dev/null +++ b/soh/soh/Enhancements/randomizer/ShuffleBeggar.cpp @@ -0,0 +1,115 @@ +#include + +extern "C" { +#include "overlays/actors/ovl_En_Hy/z_en_hy.h" +extern PlayState* gPlayState; +} + +CheckIdentity ShuffleBeggar_GetBeggarIdentity(int32_t textId) { + CheckIdentity beggarIdentity; + s16 sceneNum = gPlayState->sceneNum; + + beggarIdentity = OTRGlobals::Instance->gRandomizer->IdentifyBeggar(sceneNum, textId); + + return beggarIdentity; +} + +uint8_t EnHy_RandomizerHoldsItem(int32_t textId) { + CheckIdentity beggarIdentity = ShuffleBeggar_GetBeggarIdentity(textId); + if (beggarIdentity.randomizerCheck == RC_UNKNOWN_CHECK || !IS_RANDO || + Flags_GetRandomizerInf(beggarIdentity.randomizerInf)) { + return false; + } + return true; +} + +void BuildEnHyMessage_BlueFire(uint16_t* textId, bool* loadFromMessageTable) { + if (!EnHy_RandomizerHoldsItem(TEXT_BEGGAR_BUY_BLUE_FIRE)) { + return; + } + + CustomMessage msg = CustomMessage( + "%cBlue Fire%w! I'll trade you %rsomething special%w for it. No returns! I get new " + "special inventory %bevery 7 years or so%w...", + "%cBlaues Feuer%w! Ich tausche es gegen %retwas Besonderes%w. Und nicht feilschen, okay! Ich bekomme neue " + "besondere Ware %balle 7 Jahre oder so%w...", + "%cFeu bleu%w ! Je l'échange contre %rquelque chose de spécial%w. Pas de retour ! Je reçois de nouveaux " + "articles spéciaux %btous les 7 ans environ%w..."); + msg.AutoFormat(); + msg.LoadIntoFont(); + *loadFromMessageTable = false; +} + +void BuildEnHyMessage_Fish(uint16_t* textId, bool* loadFromMessageTable) { + if (!EnHy_RandomizerHoldsItem(TEXT_BEGGAR_BUY_FISH)) { + return; + } + CustomMessage msg = CustomMessage( + "A %pfish%w! I'll trade you %rsomething special%w for it. No returns! I get new " + "special inventory %bevery 7 years or so%w...", + "Ein %pFisch%w! Ich tausche ihn gegen %retwas Besonderes%w. Und nicht feilschen, okay! Ich bekomme neue " + "besondere Ware %balle 7 Jahre oder so%w...", + "Un %ppoisson%w ! Je l'échange contre %rquelque chose de spécial%w. Pas de retour ! Je reçois de nouveaux " + "articles spéciaux %btous les 7 ans environ%w..."); + msg.AutoFormat(); + msg.LoadIntoFont(); + *loadFromMessageTable = false; +} + +void BuildEnHyMessage_Bug(uint16_t* textId, bool* loadFromMessageTable) { + if (!EnHy_RandomizerHoldsItem(TEXT_BEGGAR_BUY_BUGS)) { + return; + } + CustomMessage msg = + CustomMessage("A tiny %gbug%w! I'll trade you %rsomething special%w for it. No returns! I get new " + "special inventory %bevery 7 years or so%w...", + "Ein kleiner %gKäfer%w! Ich tausche ihn gegen %retwas Besonderes%w. Und nicht feilschen, okay! " + "Ich bekomme neue " + "besondere Ware %balle 7 Jahre oder so%w...", + "Un petit %ginsecte%w ! Je l'échange contre %rquelque chose de spécial%w. Pas de retour ! Je " + "reçois de nouveaux " + "articles spéciaux %btous les 7 ans environ%w..."); + msg.AutoFormat(); + msg.LoadIntoFont(); + *loadFromMessageTable = false; +} + +void RegisterShuffleBeggar() { + bool shouldRegister = IS_RANDO && Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_BEGGAR).Get(); + + COND_VB_SHOULD(VB_BEGGAR_GIVE_ITEM, shouldRegister, { + EnHy* enHy = va_arg(args, EnHy*); + int32_t beggarTextId = enHy->actor.textId; + + if (beggarTextId != TEXT_BEGGAR_BUY_FAIRY) { + if (EnHy_RandomizerHoldsItem(beggarTextId)) { + CheckIdentity beggarIdentity = ShuffleBeggar_GetBeggarIdentity(beggarTextId); + Flags_SetRandomizerInf(beggarIdentity.randomizerInf); + *should = false; + } + } + }); + + COND_ID_HOOK(OnOpenText, TEXT_BEGGAR_BUY_BLUE_FIRE, shouldRegister, BuildEnHyMessage_BlueFire); + COND_ID_HOOK(OnOpenText, TEXT_BEGGAR_BUY_FISH, shouldRegister, BuildEnHyMessage_Fish); + COND_ID_HOOK(OnOpenText, TEXT_BEGGAR_BUY_BUGS, shouldRegister, BuildEnHyMessage_Bug); +} + +void Rando::StaticData::RegisterBeggarLocations() { + static bool registered = false; + if (registered) + return; + registered = true; + // clang-format off + // Randomizer Check Randomizer Check Quest RCType Area Actor ID Scene ID Params Short Name Hint Text Key Vanilla Item Spoiler Collection Check + locationTable[RC_MK_BEGGAR_BUGS] = Location::Base(RC_MK_BEGGAR_BUGS, RCQUEST_BOTH, RCTYPE_BEGGAR, RCAREA_MARKET, ACTOR_EN_HY, SCENE_MARKET_DAY, 0x70F2, "Beggar Bugs", RHT_BEGGAR_MARKET, RG_PURPLE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_BEGGAR_BUGS)); + locationTable[RC_MK_BEGGAR_FISH] = Location::Base(RC_MK_BEGGAR_FISH, RCQUEST_BOTH, RCTYPE_BEGGAR, RCAREA_MARKET, ACTOR_EN_HY, SCENE_MARKET_DAY, 0x70F1, "Beggar Fish", RHT_BEGGAR_MARKET, RG_PURPLE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_BEGGAR_FISH)); + locationTable[RC_MK_BEGGAR_BLUE_FIRE] = Location::Base(RC_MK_BEGGAR_BLUE_FIRE, RCQUEST_BOTH, RCTYPE_BEGGAR, RCAREA_MARKET, ACTOR_EN_HY, SCENE_MARKET_DAY, 0x70F0, "Beggar Blue Fire", RHT_BEGGAR_MARKET, RG_HUGE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_BEGGAR_BLUE_FIRE)); + locationTable[RC_KAK_BEGGAR_BUGS] = Location::Base(RC_KAK_BEGGAR_BUGS, RCQUEST_BOTH, RCTYPE_BEGGAR, RCAREA_KAKARIKO_VILLAGE, ACTOR_EN_HY, SCENE_KAKARIKO_VILLAGE, 0x70F2, "Beggar Bugs", RHT_BEGGAR_KAKARIKO_VILLAGE, RG_PURPLE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_BEGGAR_BUGS)); + locationTable[RC_KAK_BEGGAR_FISH] = Location::Base(RC_KAK_BEGGAR_FISH, RCQUEST_BOTH, RCTYPE_BEGGAR, RCAREA_KAKARIKO_VILLAGE, ACTOR_EN_HY, SCENE_KAKARIKO_VILLAGE, 0x70F1, "Beggar Fish", RHT_BEGGAR_KAKARIKO_VILLAGE, RG_PURPLE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_BEGGAR_FISH)); + locationTable[RC_KAK_BEGGAR_BLUE_FIRE] = Location::Base(RC_KAK_BEGGAR_BLUE_FIRE, RCQUEST_BOTH, RCTYPE_BEGGAR, RCAREA_KAKARIKO_VILLAGE, ACTOR_EN_HY, SCENE_KAKARIKO_VILLAGE, 0x70F0, "Beggar Blue Fire", RHT_BEGGAR_KAKARIKO_VILLAGE, RG_HUGE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_BEGGAR_BLUE_FIRE)); + // clang-format on +} + +static RegisterShipInitFunc registerShuffleBeggar(RegisterShuffleBeggar, { "IS_RANDO" }); +static RegisterShipInitFunc registerBeggarLocations(Rando::StaticData::RegisterBeggarLocations); \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/ShuffleFairies.cpp b/soh/soh/Enhancements/randomizer/ShuffleFairies.cpp index 730366949cd..e424d54bc72 100644 --- a/soh/soh/Enhancements/randomizer/ShuffleFairies.cpp +++ b/soh/soh/Enhancements/randomizer/ShuffleFairies.cpp @@ -11,6 +11,7 @@ extern "C" { #include "src/overlays/actors/ovl_Obj_Bean/z_obj_bean.h" #include "src/overlays/actors/ovl_En_Gs/z_en_gs.h" #include "src/overlays/actors/ovl_Shot_Sun/z_shot_sun.h" +#include "src/overlays/actors/ovl_En_Butte/z_en_butte.h" } #define FAIRY_FLAG_TIMED (1 << 8) @@ -53,7 +54,7 @@ bool ShuffleFairies_FairyExists(CheckIdentity fairyIdentity) { return false; } -CheckIdentity ShuffleFairies_GetFairyIdentity(int32_t params) { +CheckIdentity ShuffleFairies_GetFairyIdentity(int32_t params, ActorID id) { CheckIdentity fairyIdentity; s16 sceneNum = gPlayState->sceneNum; fairyIdentity.randomizerInf = RAND_INF_MAX; @@ -62,8 +63,7 @@ CheckIdentity ShuffleFairies_GetFairyIdentity(int32_t params) { sceneNum = SCENE_TEMPLE_OF_TIME_EXTERIOR_DAY; } - Rando::Location* location = - OTRGlobals::Instance->gRandomizer->GetCheckObjectFromActor(ACTOR_EN_ELF, sceneNum, params); + Rando::Location* location = OTRGlobals::Instance->gRandomizer->GetCheckObjectFromActor(id, sceneNum, params); if (location->GetRandomizerCheck() == RC_UNKNOWN_CHECK) { LUSLOG_WARN("FairyGetIdentity did not receive a valid RC value (%d).", location->GetRandomizerCheck()); @@ -76,8 +76,8 @@ CheckIdentity ShuffleFairies_GetFairyIdentity(int32_t params) { return fairyIdentity; } -static bool SpawnFairy(f32 posX, f32 posY, f32 posZ, int32_t params, FairyType fairyType) { - CheckIdentity fairyIdentity = ShuffleFairies_GetFairyIdentity(params); +static bool SpawnFairy(f32 posX, f32 posY, f32 posZ, int32_t params, FairyType fairyType, ActorID id) { + CheckIdentity fairyIdentity = ShuffleFairies_GetFairyIdentity(params, id); if (!Flags_GetRandomizerInf(fairyIdentity.randomizerInf)) { Actor* fairy = Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_ELF, posX, posY - 30.0f, posZ, 0, 0, 0, fairyType); @@ -93,7 +93,9 @@ void RegisterShuffleFairies() { bool shouldRegisterStone = IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_STONE_FAIRIES); bool shouldRegisterBean = IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_BEAN_FAIRIES); bool shouldRegisterSong = IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_SONG_FAIRIES); - bool shouldRegister = shouldRegisterFountain || shouldRegisterStone || shouldRegisterBean || shouldRegisterSong; + bool shouldRegisterButterfly = IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_BUTTERFLY_FAIRIES); + bool shouldRegister = shouldRegisterFountain || shouldRegisterStone || shouldRegisterBean || shouldRegisterSong || + shouldRegisterButterfly; // Grant item when picking up fairy. COND_VB_SHOULD(VB_FAIRY_HEAL, shouldRegister, { @@ -123,7 +125,8 @@ void RegisterShuffleFairies() { s16 grottoId = (gPlayState->sceneNum == SCENE_FAIRYS_FOUNTAIN) ? Grotto_CurrentGrotto() : 0; for (s16 index = 0; index < 8; index++) { int32_t params = (grottoId << 8) | index; - if (SpawnFairy(actor->world.pos.x, actor->world.pos.y, actor->world.pos.z, params, FAIRY_HEAL)) { + if (SpawnFairy(actor->world.pos.x, actor->world.pos.y, actor->world.pos.z, params, FAIRY_HEAL, + ACTOR_EN_ELF)) { fairySpawned = true; } } @@ -139,7 +142,7 @@ void RegisterShuffleFairies() { for (s16 index = 0; index < 3; index++) { int32_t params = ((objBean->dyna.actor.params & 0x3F) << 8) | index; if (SpawnFairy(objBean->dyna.actor.world.pos.x, objBean->dyna.actor.world.pos.y, - objBean->dyna.actor.world.pos.z, params, FAIRY_HEAL)) { + objBean->dyna.actor.world.pos.z, params, FAIRY_HEAL, ACTOR_EN_ELF)) { fairySpawned = true; } } @@ -152,7 +155,7 @@ void RegisterShuffleFairies() { COND_VB_SHOULD(VB_SPAWN_SONG_FAIRY, shouldRegisterSong, { ShotSun* shotSun = va_arg(args, ShotSun*); if (SpawnFairy(shotSun->actor.world.pos.x, shotSun->actor.world.pos.y, shotSun->actor.world.pos.z, - TWO_ACTOR_PARAMS(0x1000, (int32_t)shotSun->actor.world.pos.z), FAIRY_HEAL_BIG)) { + TWO_ACTOR_PARAMS(0x1000, (int32_t)shotSun->actor.world.pos.z), FAIRY_HEAL_BIG, ACTOR_EN_ELF)) { *should = false; } }); @@ -185,11 +188,11 @@ void RegisterShuffleFairies() { // stop spawning the vanilla fairy as well when these fairies exist, otherwise both // the randomized and the vanilla fairy will spawn. When the randomized fairy is already // collected, the vanilla code will handle that part automatically. - CheckIdentity fairyIdentity = ShuffleFairies_GetFairyIdentity(params); + CheckIdentity fairyIdentity = ShuffleFairies_GetFairyIdentity(params, ACTOR_EN_ELF); if (!ShuffleFairies_FairyExists(fairyIdentity)) { Player* player = GET_PLAYER(gPlayState); if (SpawnFairy(player->actor.world.pos.x, (player->actor.world.pos.y + 20), player->actor.world.pos.z, - params, fairyType)) { + params, fairyType, ACTOR_EN_ELF)) { Audio_PlayActorSound2(&gossipStone->actor, NA_SE_EV_BUTTERFRY_TO_FAIRY); // Set vanilla check for fairy spawned so it doesn't spawn the vanilla fairy afterwards as well. gossipStone->unk_19D = 0; @@ -200,6 +203,18 @@ void RegisterShuffleFairies() { } } }); + + // Spawn a fairy from a butterfly + COND_VB_SHOULD(VB_SPAWN_BUTTERFLY_FAIRY, shouldRegisterButterfly, { + if (*should) { + EnButte* enButte = va_arg(args, EnButte*); + if (SpawnFairy(enButte->actor.focus.pos.x, enButte->actor.focus.pos.y, enButte->actor.focus.pos.z, + TWO_ACTOR_PARAMS(enButte->actor.params, (int32_t)enButte->actor.home.pos.y), FAIRY_HEAL, + ACTOR_EN_BUTTE)) { + *should = false; + } + } + }); } void Rando::StaticData::RegisterFairyLocations() { @@ -406,6 +421,17 @@ void Rando::StaticData::RegisterFairyLocations() { locationTable[RC_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY] = Location::SongFairy(RC_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY, RCQUEST_BOTH, RCAREA_LOST_WOODS, SCENE_GROTTOS, TWO_ACTOR_PARAMS(0x1000, 741), "Deku Scrub Grotto Sun's Song Fairy", RHT_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY)); locationTable[RC_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY] = Location::SongFairy(RC_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_ROYAL_FAMILYS_TOMB, TWO_ACTOR_PARAMS(0x1000, 1476), "Royal Family's Tomb Sun's Song Fairy", RHT_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY)); + locationTable[RC_HC_NEAR_WALL_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_HC_NEAR_WALL_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(1, 1476), "Near Wall Butterfly Fairy", RHT_BUTTERFLY_FAIRY_HYRULE_CASTLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_NEAR_WALL_BUTTERFLY_FAIRY)); + locationTable[RC_HC_NEAR_STAIRS_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_HC_NEAR_STAIRS_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(1, 1493), "Near Stairs Butterfly Fairy", RHT_BUTTERFLY_FAIRY_HYRULE_CASTLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_NEAR_STAIRS_BUTTERFLY_FAIRY)); + locationTable[RC_HC_NEAR_BOULDER_PATH_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_HC_NEAR_BOULDER_PATH_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(1, 1413), "Near Boulder Path Butterfly Fairy", RHT_BUTTERFLY_FAIRY_HYRULE_CASTLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_NEAR_BOULDER_PATH_BUTTERFLY_FAIRY)); + locationTable[RC_HC_NEAR_ARCHWAY_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_HC_NEAR_ARCHWAY_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(1, 1478), "Near Archway Butterfly Fairy", RHT_BUTTERFLY_FAIRY_HYRULE_CASTLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_NEAR_ARCHWAY_BUTTERFLY_FAIRY)); + locationTable[RC_LW_MEADOW_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_LW_MEADOW_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_LOST_WOODS, SCENE_LOST_WOODS, TWO_ACTOR_PARAMS(1, 28), "Meadow Butterfly Fairy", RHT_BUTTERFLY_FAIRY_LOST_WOODS, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LW_MEADOW_BUTTERFLY_FAIRY)); + locationTable[RC_GY_NEAR_HUT_GRAVE_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_GY_NEAR_HUT_GRAVE_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_GRAVEYARD, TWO_ACTOR_PARAMS(1, 137), "Grave Butterfly Fairy", RHT_BUTTERFLY_FAIRY_GRAVEYARD, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_NEAR_HUT_GRAVE_BUTTERFLY_FAIRY)); + locationTable[RC_ZR_NEAR_ROCK_CIRCLE_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_ZR_NEAR_ROCK_CIRCLE_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(1, 164), "Near Rock Circle Butterfly Fairy", RHT_BUTTERFLY_FAIRY_ZORAS_RIVER, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_NEAR_ROCK_CIRCLE_BUTTERFLY_FAIRY)); + locationTable[RC_ZR_WATERFALL_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_ZR_WATERFALL_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(1, 1010), "Waterfall Butterfly Fairy", RHT_BUTTERFLY_FAIRY_ZORAS_RIVER, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WATERFALL_BUTTERFLY_FAIRY)); + locationTable[RC_ZF_LOG_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_ZF_LOG_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_ZORAS_FOUNTAIN, SCENE_ZORAS_FOUNTAIN, TWO_ACTOR_PARAMS(1, 169), "Log Butterfly Fairy", RHT_BUTTERFLY_FAIRY_ZORAS_FOUNTAIN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZF_LOG_BUTTERFLY_FAIRY)); + locationTable[RC_LH_SCARECROW_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_LH_SCARECROW_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_LAKE_HYLIA, SCENE_LAKE_HYLIA, TWO_ACTOR_PARAMS(1, -1254), "Scarecrow Butterfly Fairy", RHT_BUTTERFLY_FAIRY_LAKE_HYLIA, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LH_SCARECROW_BUTTERFLY_FAIRY)); + locationTable[RC_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY] = Location::SongFairy(RC_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY, RCQUEST_VANILLA,RCAREA_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(0x1000, -1896), "After Boulder Room Sun's Song Fairy", RHT_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY)); locationTable[RC_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY] = Location::SongFairy(RC_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY, RCQUEST_VANILLA,RCAREA_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(0x1000, -220), "Four Armos Room Sun's Song Fairy", RHT_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY)); locationTable[RC_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY] = Location::SongFairy(RC_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY, RCQUEST_VANILLA,RCAREA_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE, TWO_ACTOR_PARAMS(0x1000, 54), "Beamos Song of Storms Fairy", RHT_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY)); diff --git a/soh/soh/Enhancements/randomizer/ShuffleIcicles.cpp b/soh/soh/Enhancements/randomizer/ShuffleIcicles.cpp new file mode 100644 index 00000000000..ff16fab11c4 --- /dev/null +++ b/soh/soh/Enhancements/randomizer/ShuffleIcicles.cpp @@ -0,0 +1,308 @@ +#include "soh/OTRGlobals.h" +#include "soh/ObjectExtension/ObjectExtension.h" +#include "item_category_adj.h" +#include "particle_cmc.h" + +extern "C" { +#include "functions.h" +#include "overlays/actors/ovl_Bg_Ice_Turara/z_bg_ice_turara.h" +#include "objects/object_ice_objects/object_ice_objects.h" +#include "objects/object_tk/object_tk.h" +extern PlayState* gPlayState; +} + +struct StalactiteDropped {}; +static ObjectExtension::Register StalactiteDroppedRegister; + +extern void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play); + +extern "C" void DrawItemHalo(Actor* icicleActor) { + // If the regrowing stalactite has already dropped, return + if (ObjectExtension::GetInstance().Has(icicleActor)) { + return; + } + + GetItemCategory getItemCategory; + bool cmc = CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeAndTextureMatchContents"), 0); + int requiresStoneAgony = CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeDependsStoneOfAgony"), 0); + int isNotCMC = !cmc || (requiresStoneAgony && !CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)); + const auto icicleIdentity = ObjectExtension::GetInstance().Get(icicleActor); + + GetItemEntry icicleItem = + Rando::Context::GetInstance()->GetFinalGIEntry(icicleIdentity->randomizerCheck, true, GI_NONE); + getItemCategory = Randomizer_AdjustItemCategory(icicleItem); + + if (isNotCMC) { + getItemCategory = ITEM_CATEGORY_MAJOR; + } + Color_RGBA8 primColor = Randomizer_GetParticleCMCColor(getItemCategory, COLOR_PRIMARY); + + // Align halo to center of icicles + // Ice Cavern HP room is slightly different for stalactites + f32 yOffset = (icicleActor->params == 0) ? 135.0f : 45.0f; + f32 xOffset = -23.0f; + f32 zOffset = (icicleActor->params == 0) ? 5.0f + : (gPlayState->sceneNum == SCENE_ICE_CAVERN && gPlayState->roomCtx.curRoom.num == 11) ? 4.0f + : 2.0f; + + // Rotate and draw halo with CMC colors + Matrix_Translate(icicleActor->world.pos.x + xOffset, icicleActor->world.pos.y + yOffset, + icicleActor->world.pos.z + zOffset, MTXMODE_NEW); + Matrix_RotateZ(-M_PI / 2, MTXMODE_APPLY); + Matrix_Scale(0.01f, 0.01f, 0.01f, MTXMODE_APPLY); + OPEN_DISPS(gPlayState->state.gfxCtx); + gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(gPlayState->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetGrayscaleColor(POLY_OPA_DISP++, primColor.r, primColor.g, primColor.b, 175); + gSPGrayscale(POLY_OPA_DISP++, true); + gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gDampeHaloDL); + gSPGrayscale(POLY_OPA_DISP++, false); + CLOSE_DISPS(gPlayState->state.gfxCtx); +} + +uint8_t BgIceTurara_RandomizerHoldsItem(Actor* actor) { + const auto icicleIdentity = ObjectExtension::GetInstance().Get(actor); + if (icicleIdentity == nullptr) { + return false; + } + + RandomizerCheck rc = icicleIdentity->randomizerCheck; + + // Don't pull randomized item if icicle isn't randomized or is already checked + if (!IS_RANDO || Flags_GetRandomizerInf(icicleIdentity->randomizerInf) || + icicleIdentity->randomizerCheck == RC_UNKNOWN_CHECK) { + return false; + } else { + return true; + } +} + +void BgIceTurara_RandomizerSpawnCollectible(void* actor) { + BgIceTurara* icicleActor = (BgIceTurara*)actor; + const auto icicleIdentity = ObjectExtension::GetInstance().Get(&icicleActor->dyna.actor); + + EnItem00* item00 = + (EnItem00*)Item_DropCollectible2(gPlayState, &icicleActor->dyna.actor.world.pos, ITEM00_SOH_DUMMY); + item00->randoInf = icicleIdentity->randomizerInf; + item00->itemEntry = Rando::Context::GetInstance()->GetFinalGIEntry(icicleIdentity->randomizerCheck, true, GI_NONE); + item00->actor.draw = (ActorFunc)EnItem00_DrawRandomizedItem; + item00->actor.velocity.y = 8.0f; + item00->actor.speedXZ = 2.0f; + item00->actor.world.rot.y = static_cast(Rand_CenteredFloat(65536.0f)); +} + +void RegisterShuffleIcicles() { + bool shouldRegister = IS_RANDO && Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_ICICLES).Get(); + + COND_ID_HOOK(OnActorInit, ACTOR_BG_ICE_TURARA, shouldRegister, [](void* actorRef) { + Actor* actor = static_cast(actorRef); + BgIceTurara* icicleActor = static_cast(actorRef); + + auto icicleIdentity = OTRGlobals::Instance->gRandomizer->IdentifyIcicle( + gPlayState->sceneNum, (s16)actor->world.pos.x, (s16)actor->world.pos.z); + ObjectExtension::GetInstance().Set(actor, std::move(icicleIdentity)); + }); + + // Draw halo around icicles + COND_VB_SHOULD(VB_ICICLE_SETUP_DRAW, shouldRegister, { + BgIceTurara* icicleActor = va_arg(args, BgIceTurara*); + if (BgIceTurara_RandomizerHoldsItem(&icicleActor->dyna.actor) && + !ObjectExtension::GetInstance().Has(&icicleActor->dyna.actor)) { + DrawItemHalo(&icicleActor->dyna.actor); + } + }); + + // Drop item for stalagmites + COND_VB_SHOULD(VB_STALAGMITE_DROP_ITEM, shouldRegister, { + BgIceTurara* icicleActor = va_arg(args, BgIceTurara*); + + if (*should) { + if (BgIceTurara_RandomizerHoldsItem(&icicleActor->dyna.actor)) { + BgIceTurara_RandomizerSpawnCollectible(&icicleActor->dyna.actor); + } + } + }); + + // Drop item for stalactites + COND_VB_SHOULD(VB_STALACTITE_DROP_ITEM, shouldRegister, { + BgIceTurara* icicleActor = va_arg(args, BgIceTurara*); + + if (BgIceTurara_RandomizerHoldsItem(&icicleActor->dyna.actor)) { + // Set and check if regrowing stalactite has already dropped + if (*should) { + auto& ext = ObjectExtension::GetInstance(); + if (!ext.Has(&icicleActor->dyna.actor)) { + ext.Set(&icicleActor->dyna.actor, StalactiteDropped{}); + BgIceTurara_RandomizerSpawnCollectible(&icicleActor->dyna.actor); + } + } else { + BgIceTurara_RandomizerSpawnCollectible(&icicleActor->dyna.actor); + } + } + }); + + // Remove the drop indicator when the actor is destroyed + COND_ID_HOOK(OnActorDestroy, ACTOR_BG_ICE_TURARA, shouldRegister, + [](void* actor) { ObjectExtension::GetInstance().Remove(actor); }); +} + +void Rando::StaticData::RegisterIcicleLocations() { + static bool registered = false; + if (registered) + return; + registered = true; + // clang-format off + // Randomizer Check Randomizer Check Quest Area Scene ID Params Short Name Hint Text Key Spoiler Collection Check + locationTable[RC_ICE_CAVERN_ENTRANCE_LEFT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_ENTRANCE_LEFT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(295, 2386), "Entrance Left Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_ENTRANCE_LEFT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_ENTRANCE_MIDDLE_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_ENTRANCE_MIDDLE_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(313, 2446), "Entrance Middle Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_ENTRANCE_MIDDLE_STALAGMITE)); + locationTable[RC_ICE_CAVERN_ENTRANCE_RIGHT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_ENTRANCE_RIGHT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(400, 2443), "Entrance Right Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_ENTRANCE_RIGHT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_ENTRANCE_STALACTITE_1] = Location::Icicle(RC_ICE_CAVERN_ENTRANCE_STALACTITE_1, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(499, 2229), "Entrance Stalactite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_ENTRANCE_STALACTITE_1)); + locationTable[RC_ICE_CAVERN_ENTRANCE_STALACTITE_2] = Location::Icicle(RC_ICE_CAVERN_ENTRANCE_STALACTITE_2, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(536, 2071), "Entrance Stalactite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_ENTRANCE_STALACTITE_2)); + locationTable[RC_ICE_CAVERN_LOBBY_STALACTITE] = Location::Icicle(RC_ICE_CAVERN_LOBBY_STALACTITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-18, 1760), "Lobby Stalactite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_LOBBY_STALACTITE)); + locationTable[RC_ICE_CAVERN_LOBBY_LEFT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_LOBBY_LEFT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-62, 1642), "Lobby Left Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_LOBBY_LEFT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_LOBBY_MIDDLE_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_LOBBY_MIDDLE_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-19, 1676), "Lobby Middle Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_LOBBY_MIDDLE_STALAGMITE)); + locationTable[RC_ICE_CAVERN_LOBBY_RIGHT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_LOBBY_RIGHT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(36, 1643), "Lobby Right Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_LOBBY_RIGHT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_AFTER_LOBBY_STALACTITE] = Location::Icicle(RC_ICE_CAVERN_AFTER_LOBBY_STALACTITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-237, 472), "After Lobby Stalactite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_AFTER_LOBBY_STALACTITE)); + locationTable[RC_ICE_CAVERN_AFTER_LOBBY_LEFT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_AFTER_LOBBY_LEFT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-308, 322), "After Lobby Left Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_AFTER_LOBBY_LEFT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_AFTER_LOBBY_CENTER_LEFT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_AFTER_LOBBY_CENTER_LEFT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-272, 348), "After Lobby Center Left Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_AFTER_LOBBY_CENTER_LEFT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_AFTER_LOBBY_CENTER_RIGHT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_AFTER_LOBBY_CENTER_RIGHT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-226, 371), "After Lobby Center Right Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_AFTER_LOBBY_CENTER_RIGHT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_AFTER_LOBBY_RIGHT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_AFTER_LOBBY_RIGHT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-187, 395), "After Lobby Right Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_AFTER_LOBBY_RIGHT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_SPINNING_BLADE_LEFT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_SPINNING_BLADE_LEFT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(44, -139), "Spinning Blade Left Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_SPINNING_BLADE_LEFT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_SPINNING_BLADE_MIDDLE_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_SPINNING_BLADE_MIDDLE_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(22, -170), "Spinning Blade Middle Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_SPINNING_BLADE_MIDDLE_STALAGMITE)); + locationTable[RC_ICE_CAVERN_SPINNING_BLADE_RIGHT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_SPINNING_BLADE_RIGHT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-9, -175), "Spinning Blade Right Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_SPINNING_BLADE_RIGHT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_1] = Location::Icicle(RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_1, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1264, -890), "Map Hallway Stalactite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MAP_HALLWAY_STALACTITE_1)); + locationTable[RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_2] = Location::Icicle(RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_2, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1141, -946), "Map Hallway Stalactite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MAP_HALLWAY_STALACTITE_2)); + locationTable[RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_3] = Location::Icicle(RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_3, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1229, -1134), "Map Hallway Stalactite 3", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MAP_HALLWAY_STALACTITE_3)); + locationTable[RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_4] = Location::Icicle(RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_4, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1063, -1300), "Map Hallway Stalactite 4", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MAP_HALLWAY_STALACTITE_4)); + locationTable[RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_5] = Location::Icicle(RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_5, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1088, -1505), "Map Hallway Stalactite 5", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MAP_HALLWAY_STALACTITE_5)); + locationTable[RC_ICE_CAVERN_MAP_HALLWAY_LEFT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_MAP_HALLWAY_LEFT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(951, -1217), "Map Hallway Left Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MAP_HALLWAY_LEFT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_MAP_HALLWAY_MIDDLE_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_MAP_HALLWAY_MIDDLE_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1029, -1217), "Map Hallway Middle Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MAP_HALLWAY_MIDDLE_STALAGMITE)); + locationTable[RC_ICE_CAVERN_MAP_HALLWAY_RIGHT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_MAP_HALLWAY_RIGHT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1187, -1217), "Map Hallway Right Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MAP_HALLWAY_RIGHT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_1] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_1, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1026, 296), "Heart Piece Room Center Stalactite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_1)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_2] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_2, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1092, 274), "Heart Piece Room Center Stalactite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_2)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_3] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_3, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1058, 369), "Heart Piece Room Center Stalactite 3", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_3)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_4] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_4, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1148, 347), "Heart Piece Room Center Stalactite 4", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_4)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_5] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_5, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1252, 356), "Heart Piece Room Center Stalactite 5", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_5)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_1] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_1, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1187, 66), "Heart Piece Room Left Stalactite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_1)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_2] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_2, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1347, 110), "Heart Piece Room Left Stalactite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_2)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_1] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_1, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1361, 312), "Heart Piece Room Center Stalagmite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_1)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_2] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_2, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1336, 343), "Heart Piece Room Center Stalagmite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_2)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_3] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_3, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1348, 382), "Heart Piece Room Center Stalagmite 3", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_3)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_4] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_4, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1321, 404), "Heart Piece Room Center Stalagmite 4", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_4)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_5] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_5, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1341, 438), "Heart Piece Room Center Stalagmite 5", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_5)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_6] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_6, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1317, 470), "Heart Piece Room Center Stalagmite 6", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_6)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_1] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_1, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1223, 195), "Heart Piece Room Left Stalagmite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_1)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_2] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_2, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1257, 202), "Heart Piece Room Left Stalagmite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_2)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_3] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_3, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1297, 200), "Heart Piece Room Left Stalagmite 3", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_3)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_1] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_1, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1159, 552), "Heart Piece Room Right Stalagmite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_1)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_2] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_2, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1191, 565), "Heart Piece Room Right Stalagmite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_2)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_3] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_3, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1227, 549), "Heart Piece Room Right Stalagmite 3", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_3)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_4] = Location::Icicle(RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_4, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1267, 559), "Heart Piece Room Right Stalagmite 4", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_4)); + locationTable[RC_ICE_CAVERN_PUSH_BLOCK_HALL_LEFT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_PUSH_BLOCK_HALL_LEFT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-518, -601), "Push Block Hall Left Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_LEFT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_LEFT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_LEFT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-487, -600), "Push Block Hall Center Left Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_LEFT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-451, -604), "Push Block Hall Center Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_STALAGMITE)); + locationTable[RC_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_RIGHT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_RIGHT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-414, -610), "Push Block Hall Center Right Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_RIGHT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_PUSH_BLOCK_HALL_RIGHT_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_PUSH_BLOCK_HALL_RIGHT_STALAGMITE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-373, -605), "Push Block Hall Right Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_RIGHT_STALAGMITE)); + locationTable[RC_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_1] = Location::Icicle(RC_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_1, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-385, -686), "Push Block Hall Stalactite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_1)); + locationTable[RC_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_2] = Location::Icicle(RC_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_2, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-314, -768), "Push Block Hall Stalactite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_2)); + locationTable[RC_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_3] = Location::Icicle(RC_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_3, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-359, -862), "Push Block Hall Stalactite 3", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_3)); + locationTable[RC_ICE_CAVERN_NEAR_END_STALACTITE_1] = Location::Icicle(RC_ICE_CAVERN_NEAR_END_STALACTITE_1, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1231, 170), "Near End Stalactite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_NEAR_END_STALACTITE_1)); + locationTable[RC_ICE_CAVERN_NEAR_END_STALACTITE_2] = Location::Icicle(RC_ICE_CAVERN_NEAR_END_STALACTITE_2, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1328, 215), "Near End Stalactite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_NEAR_END_STALACTITE_2)); + locationTable[RC_ICE_CAVERN_NEAR_END_STALACTITE_3] = Location::Icicle(RC_ICE_CAVERN_NEAR_END_STALACTITE_3, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1412, 246), "Near End Stalactite 3", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_NEAR_END_STALACTITE_3)); + locationTable[RC_ICE_CAVERN_NEAR_END_STALACTITE_4] = Location::Icicle(RC_ICE_CAVERN_NEAR_END_STALACTITE_4, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1571, 422), "Near End Stalactite 4", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_NEAR_END_STALACTITE_4)); + locationTable[RC_ICE_CAVERN_NEAR_END_STALAGMITE_1] = Location::Icicle(RC_ICE_CAVERN_NEAR_END_STALAGMITE_1, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1288, 184), "Near End Stalagmite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_1)); + locationTable[RC_ICE_CAVERN_NEAR_END_STALAGMITE_2] = Location::Icicle(RC_ICE_CAVERN_NEAR_END_STALAGMITE_2, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1367, 206), "Near End Stalagmite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_2)); + locationTable[RC_ICE_CAVERN_NEAR_END_STALAGMITE_3] = Location::Icicle(RC_ICE_CAVERN_NEAR_END_STALAGMITE_3, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1349, 270), "Near End Stalagmite 3", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_3)); + locationTable[RC_ICE_CAVERN_NEAR_END_STALAGMITE_4] = Location::Icicle(RC_ICE_CAVERN_NEAR_END_STALAGMITE_4, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1514, 307), "Near End Stalagmite 4", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_4)); + locationTable[RC_ICE_CAVERN_NEAR_END_STALAGMITE_5] = Location::Icicle(RC_ICE_CAVERN_NEAR_END_STALAGMITE_5, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1507, 443), "Near End Stalagmite 5", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_5)); + locationTable[RC_ICE_CAVERN_NEAR_END_STALAGMITE_6] = Location::Icicle(RC_ICE_CAVERN_NEAR_END_STALAGMITE_6, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1624, 431), "Near End Stalagmite 6", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_6)); + locationTable[RC_ICE_CAVERN_NEAR_END_STALAGMITE_7] = Location::Icicle(RC_ICE_CAVERN_NEAR_END_STALAGMITE_7, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1587, 497), "Near End Stalagmite 7", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_7)); + locationTable[RC_ICE_CAVERN_NEAR_END_STALAGMITE_8] = Location::Icicle(RC_ICE_CAVERN_NEAR_END_STALAGMITE_8, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1595, 600), "Near End Stalagmite 8", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_8)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_1] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_1, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1687, -859), "Water Trial Stalagmite 1", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_1)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_2] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_2, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1706, -884), "Water Trial Stalagmite 2", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_2)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_3] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_3, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1735, -898), "Water Trial Stalagmite 3", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_3)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_4] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_4, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1761, -887), "Water Trial Stalagmite 4", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_4)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_5] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_5, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1787, -871), "Water Trial Stalagmite 5", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_5)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_6] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_6, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1783, -833), "Water Trial Stalagmite 6", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_6)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_7] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_7, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1770, -798), "Water Trial Stalagmite 7", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_7)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_8] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_8, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1737, -787), "Water Trial Stalagmite 8", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_8)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_9] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_9, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1698, -797), "Water Trial Stalagmite 9", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_9)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_10] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_10, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1683, -828), "Water Trial Stalagmite 10", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_10)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_1] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_1, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1966, -1089), "Water Trial Left Stalagmite 1", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_1)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_2] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_2, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(2040, -1015), "Water Trial Left Stalagmite 2", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_2)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_1] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_1, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1980, -590), "Water Trial Right Stalagmite 1", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_1)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_2] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_2, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(2058, -655), "Water Trial Right Stalagmite 2", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_2)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_1] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_1, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1549, -836), "Water Trial Stalactite 1", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_1)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_2] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_2, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1611, -843), "Water Trial Stalactite 2", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_2)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_3] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_3, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1657, -882), "Water Trial Stalactite 3", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_3)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_4] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_4, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1651, -805), "Water Trial Stalactite 4", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_4)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_5] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_5, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1761, -949), "Water Trial Stalactite 5", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_5)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_6] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_6, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1714, -757), "Water Trial Stalactite 6", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_6)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_7] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_7, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1811, -905), "Water Trial Stalactite 7", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_7)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_8] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_8, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1769, -761), "Water Trial Stalactite 8", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_8)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_9] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_9, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1821, -817), "Water Trial Stalactite 9", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_9)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_10] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_10, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1996, -1033), "Water Trial Stalactite 10", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_10)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_11] = Location::Icicle(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_11, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1995, -648), "Water Trial Stalactite 11", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_11)); + locationTable[RC_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_1] = Location::Icicle(RC_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_1, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(287, 2346), "Entrance Stalagmite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_1)); + locationTable[RC_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_2] = Location::Icicle(RC_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_2, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(324, 2280), "Entrance Stalagmite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_2)); + locationTable[RC_ICE_CAVERN_MQ_LOBBY_STALACTITE_1] = Location::Icicle(RC_ICE_CAVERN_MQ_LOBBY_STALACTITE_1, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(268, 980), "Lobby Stalactite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_LOBBY_STALACTITE_1)); + locationTable[RC_ICE_CAVERN_MQ_LOBBY_STALACTITE_2] = Location::Icicle(RC_ICE_CAVERN_MQ_LOBBY_STALACTITE_2, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(204, 910), "Lobby Stalactite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_LOBBY_STALACTITE_2)); + locationTable[RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_1] = Location::Icicle(RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_1, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-351, 423), "After Lobby Stalagmite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_1)); + locationTable[RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_2] = Location::Icicle(RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_2, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-288, 402), "After Lobby Stalagmite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_2)); + locationTable[RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_3] = Location::Icicle(RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_3, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-258, 461), "After Lobby Stalagmite 3", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_3)); + locationTable[RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_4] = Location::Icicle(RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_4, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-212, 398), "After Lobby Stalagmite 4", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_4)); + locationTable[RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_5] = Location::Icicle(RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_5, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-19, 193), "After Lobby Stalagmite 5", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_5)); + locationTable[RC_ICE_CAVERN_MQ_HUB_STALAGMITE_1] = Location::Icicle(RC_ICE_CAVERN_MQ_HUB_STALAGMITE_1, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(607, -170), "Hub Stalagmite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_HUB_STALAGMITE_1)); + locationTable[RC_ICE_CAVERN_MQ_HUB_STALAGMITE_2] = Location::Icicle(RC_ICE_CAVERN_MQ_HUB_STALAGMITE_2, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(598, -213), "Hub Stalagmite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_HUB_STALAGMITE_2)); + locationTable[RC_ICE_CAVERN_MQ_HUB_STALAGMITE_3] = Location::Icicle(RC_ICE_CAVERN_MQ_HUB_STALAGMITE_3, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(597, -256), "Hub Stalagmite 3", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_HUB_STALAGMITE_3)); + locationTable[RC_ICE_CAVERN_MQ_HUB_STALAGMITE_4] = Location::Icicle(RC_ICE_CAVERN_MQ_HUB_STALAGMITE_4, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(614, -292), "Hub Stalagmite 4", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_HUB_STALAGMITE_4)); + locationTable[RC_ICE_CAVERN_MQ_HUB_STALAGMITE_5] = Location::Icicle(RC_ICE_CAVERN_MQ_HUB_STALAGMITE_5, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(640, -312), "Hub Stalagmite 5", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_HUB_STALAGMITE_5)); + locationTable[RC_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_1] = Location::Icicle(RC_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_1, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1170, 97), "Map Room Left Stalagmite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_1)); + locationTable[RC_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_2] = Location::Icicle(RC_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_2, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1149, 64), "Map Room Left Stalagmite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_2)); + locationTable[RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_1] = Location::Icicle(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_1, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1334, 473), "Map Room Center Stalagmite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_1)); + locationTable[RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_2] = Location::Icicle(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_2, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1327, 442), "Map Room Center Stalagmite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_2)); + locationTable[RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_3] = Location::Icicle(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_3, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1321, 410), "Map Room Center Stalagmite 3", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_3)); + locationTable[RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_4] = Location::Icicle(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_4, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1325, 369), "Map Room Center Stalagmite 4", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_4)); + locationTable[RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_5] = Location::Icicle(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_5, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1344, 332), "Map Room Center Stalagmite 5", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_5)); + locationTable[RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_6] = Location::Icicle(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_6, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1375, 291), "Map Room Center Stalagmite 6", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_6)); + locationTable[RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_7] = Location::Icicle(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_7, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1345, 260), "Map Room Center Stalagmite 7", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_7)); + locationTable[RC_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_1] = Location::Icicle(RC_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_1, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(742, -2464), "Compass Left Stalagmite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_1)); + locationTable[RC_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_2] = Location::Icicle(RC_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_2, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(783, -2453), "Compass Left Stalagmite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_2)); + locationTable[RC_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_1] = Location::Icicle(RC_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_1, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(903, -2353), "Compass Right Stalagmite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_1)); + locationTable[RC_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_2] = Location::Icicle(RC_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_2, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(929, -2303), "Compass Right Stalagmite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_2)); + locationTable[RC_ICE_CAVERN_MQ_BEFORE_SCARECROW_STALAGMITE] = Location::Icicle(RC_ICE_CAVERN_MQ_BEFORE_SCARECROW_STALAGMITE, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-261, -840), "Before Scarecrow Stalagmite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_BEFORE_SCARECROW_STALAGMITE)); + locationTable[RC_ICE_CAVERN_MQ_SCARECROW_ROOM_STALACTITE] = Location::Icicle(RC_ICE_CAVERN_MQ_SCARECROW_ROOM_STALACTITE, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-763, -896), "Scarecrow Room Stalactite", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_SCARECROW_ROOM_STALACTITE)); + locationTable[RC_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_1] = Location::Icicle(RC_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_1, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1524, 326), "West Corridor Stalactite 1", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_1)); + locationTable[RC_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_2] = Location::Icicle(RC_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_2, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1583, 609), "West Corridor Stalactite 2", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_2)); + locationTable[RC_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_3] = Location::Icicle(RC_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_3, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1487, 569), "West Corridor Stalactite 3", RHT_ICE_CAVERN_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_3)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_1] = Location::Icicle(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_1, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(-1265, -2067), "Boulder Room Stalagmite 1", RHT_GERUDO_TRAINING_GROUND_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_1)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_2] = Location::Icicle(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_2, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(-1235, -2054), "Boulder Room Stalagmite 2", RHT_GERUDO_TRAINING_GROUND_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_2)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_3] = Location::Icicle(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_3, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(-1205, -2080), "Boulder Room Stalagmite 3", RHT_GERUDO_TRAINING_GROUND_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_3)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_4] = Location::Icicle(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_4, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(-1170, -2080), "Boulder Room Stalagmite 4", RHT_GERUDO_TRAINING_GROUND_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_4)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_5] = Location::Icicle(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_5, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(-1183, -2132), "Boulder Room Stalagmite 5", RHT_GERUDO_TRAINING_GROUND_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_5)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_1] = Location::Icicle(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_1, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(-1261, -1001), "Boulder Room Right Stalactite 1", RHT_GERUDO_TRAINING_GROUND_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_1)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_2] = Location::Icicle(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_2, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(-1247, -1096), "Boulder Room Right Stalactite 2", RHT_GERUDO_TRAINING_GROUND_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_2)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_3] = Location::Icicle(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_3, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(-1182, -1181), "Boulder Room Right Stalactite 3", RHT_GERUDO_TRAINING_GROUND_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_3)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_4] = Location::Icicle(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_4, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(-1255, -1273), "Boulder Room Right Stalactite 4", RHT_GERUDO_TRAINING_GROUND_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_4)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_LEFT_STALACTITE] = Location::Icicle(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_LEFT_STALACTITE, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(-1884, -1024), "Boulder Room Left Stalactite", RHT_GERUDO_TRAINING_GROUND_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_LEFT_STALACTITE)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_1] = Location::Icicle(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_1, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(-1263, -2028), "Boulder Room Top Right Stalactite 1", RHT_GERUDO_TRAINING_GROUND_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_1)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_2] = Location::Icicle(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_2, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(-1216, -2029), "Boulder Room Top Right Stalactite 2", RHT_GERUDO_TRAINING_GROUND_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_2)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_3] = Location::Icicle(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_3, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(-1168, -2028), "Boulder Room Top Right Stalactite 3", RHT_GERUDO_TRAINING_GROUND_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_3)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_1] = Location::Icicle(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_1, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1791, -1141), "Water Trial Left Stalactite 1", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_1)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_2] = Location::Icicle(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_2, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1890, -1068), "Water Trial Left Stalactite 2", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_2)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_3] = Location::Icicle(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_3, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(2002, -1068), "Water Trial Left Stalactite 3", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_3)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_1] = Location::Icicle(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_1, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1864, -568), "Water Trial Right Stalactite 1", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_1)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_2] = Location::Icicle(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_2, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1937, -658), "Water Trial Right Stalactite 2", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_2)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_3] = Location::Icicle(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_3, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(2054, -667), "Water Trial Right Stalactite 3", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_3)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_1] = Location::Icicle(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_1, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1868, -1175), "Water Trial Left Stalagmite 1", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_1)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_2] = Location::Icicle(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_2, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1908, -1151), "Water Trial Left Stalagmite 2", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_2)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_3] = Location::Icicle(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_3, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1960, -1123), "Water Trial Left Stalagmite 3", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_3)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_4] = Location::Icicle(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_4, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(2008, -1115), "Water Trial Left Stalagmite 4", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_4)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_1] = Location::Icicle(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_1, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1916, -511), "Water Trial Right Stalagmite 1", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_1)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_2] = Location::Icicle(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_2, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1936, -551), "Water Trial Right Stalagmite 2", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_2)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_3] = Location::Icicle(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_3, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1984, -567), "Water Trial Right Stalagmite 3", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_3)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_4] = Location::Icicle(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_4, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(2044, -591), "Water Trial Right Stalagmite 4", RHT_GANONS_CASTLE_ICICLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_4)); + // clang-format on +} + +static RegisterShipInitFunc initFunc_ShuffleIcicles(RegisterShuffleIcicles, { "IS_RANDO" }); +static RegisterShipInitFunc registerIcicleLocations(Rando::StaticData::RegisterIcicleLocations); \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/ShuffleRedIce.cpp b/soh/soh/Enhancements/randomizer/ShuffleRedIce.cpp new file mode 100644 index 00000000000..3ce745882db --- /dev/null +++ b/soh/soh/Enhancements/randomizer/ShuffleRedIce.cpp @@ -0,0 +1,206 @@ +#include "soh/OTRGlobals.h" +#include "soh/ObjectExtension/ObjectExtension.h" +#include "item_category_adj.h" +#include "particle_cmc.h" + +extern "C" { +#include "functions.h" +#include "overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.h" +#include "objects/object_ice_objects/object_ice_objects.h" +extern PlayState* gPlayState; +} + +extern void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play); + +uint8_t BgIceShelter_RandomizerHoldsItem(Actor* actor) { + const auto redIceIdentity = ObjectExtension::GetInstance().Get(actor); + if (redIceIdentity == nullptr) { + return false; + } + + RandomizerCheck rc = redIceIdentity->randomizerCheck; + + // Don't pull randomized item if icicle isn't randomized or is already checked + if (!IS_RANDO || Flags_GetRandomizerInf(redIceIdentity->randomizerInf) || + redIceIdentity->randomizerCheck == RC_UNKNOWN_CHECK) { + return false; + } else { + return true; + } +} + +static void BgIceShelter_RandomizerDraw(Actor* actor, Color_RGBA8* primColor, Color_RGBA8* secColor, + Color_RGBA8* envColor) { + Vec3f pos; + s32 type = (actor->params >> 8) & 7; + static Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + + velocity.y = -0.05f; + accel.y = -0.025f; + + // align for King Zora's much bigger red ice + f32 xzScale = (type == RED_ICE_KING_ZORA) ? 30.0f : 15.0f; + f32 yOffset = (type == RED_ICE_KING_ZORA) ? 200.0f : 0.0f; + f32 zOffset = (type == RED_ICE_KING_ZORA) ? 50.0f : 0.0f; + + pos.x = Rand_CenteredFloat(xzScale) + actor->world.pos.x; + pos.y = (Rand_ZeroOne() * 15.0f) + actor->world.pos.y + yOffset; + pos.z = Rand_CenteredFloat(xzScale) + actor->world.pos.z + zOffset; + EffectSsKiraKira_SpawnFocused(gPlayState, &pos, &velocity, &accel, secColor, envColor, 2000, 100); + EffectSsKiraKira_SpawnFocused(gPlayState, &pos, &velocity, &accel, primColor, envColor, 2000, 100); +} + +void BgIceShelter_RandomizerDrawSetup(void* actor) { + GetItemCategory getItemCategory; + Actor* redIceActor = (Actor*)actor; + + // If not a randomized item or too far, don't draw + if (!BgIceShelter_RandomizerHoldsItem(redIceActor) || redIceActor->xzDistToPlayer > 1000.0f) { + return; + } + + bool cmc = CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeAndTextureMatchContents"), 0); + int requiresStoneAgony = CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeDependsStoneOfAgony"), 0); + + int isNotCMC = !cmc || (requiresStoneAgony && !CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)); + + Color_RGBA8 primColor; + Color_RGBA8 secColor; + Color_RGBA8 envColor; + + const auto redIceIdentity = ObjectExtension::GetInstance().Get(redIceActor); + if (redIceIdentity == nullptr) { + return; + } + + GetItemEntry redIceItem = + Rando::Context::GetInstance()->GetFinalGIEntry(redIceIdentity->randomizerCheck, true, GI_NONE); + getItemCategory = Randomizer_AdjustItemCategory(redIceItem); + + if (isNotCMC) { + getItemCategory = ITEM_CATEGORY_MAJOR; + } + primColor = Randomizer_GetParticleCMCColor(getItemCategory, COLOR_PRIMARY); + secColor = Randomizer_GetParticleCMCColor(getItemCategory, COLOR_SECONDARY); + envColor = Randomizer_GetParticleCMCColor(getItemCategory, COLOR_FLARE); + BgIceShelter_RandomizerDraw(redIceActor, &primColor, &secColor, &envColor); +} + +void BgIceShelter_RandomizerSpawnCollectible(Actor* actor) { + const auto redIceIdentity = ObjectExtension::GetInstance().Get(actor); + Player* player = GET_PLAYER(gPlayState); + + // If King Zora, autocollect to avoid spawning issues + if (redIceIdentity->randomizerCheck == RC_ZD_KING_ZORA_RED_ICE) { + Flags_SetRandomizerInf(redIceIdentity->randomizerInf); + } else { + EnItem00* item00 = (EnItem00*)Item_DropCollectible2(gPlayState, &actor->world.pos, ITEM00_SOH_DUMMY); + item00->randoInf = redIceIdentity->randomizerInf; + item00->itemEntry = + Rando::Context::GetInstance()->GetFinalGIEntry(redIceIdentity->randomizerCheck, true, GI_NONE); + item00->actor.draw = (ActorFunc)EnItem00_DrawRandomizedItem; + item00->actor.velocity.y = 8.0f; + // In general, spawn in place, but for checks with objects blocking, spawn out toward player + if ((redIceIdentity->randomizerCheck >= RC_ICE_CAVERN_HEART_PIECE_ROOM_FREESTANDING_RED_ICE && + redIceIdentity->randomizerCheck <= RC_ICE_CAVERN_NEAR_END_RIGHT_RED_ICE) || + redIceIdentity->randomizerCheck == RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_LEFT_RED_ICE || + redIceIdentity->randomizerCheck == RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_RIGHT_RED_ICE || + redIceIdentity->randomizerCheck == RC_GANONS_CASTLE_MQ_WATER_TRIAL_SILVER_RUPEE_RED_ICE) { + item00->actor.speedXZ = 2.0f; + item00->actor.world.rot.y = + Math_Vec3f_Yaw(&item00->actor.world.pos, &player->actor.world.pos) + (s16)Rand_CenteredFloat(16384.0f); + } else { + item00->actor.speedXZ = 0.0f; + item00->actor.world.rot.y = static_cast(Rand_CenteredFloat(65536.0f)); + } + } +} + +void RegisterShuffleRedIce() { + bool shouldRegister = IS_RANDO && Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_RED_ICE).Get(); + + COND_VB_SHOULD(VB_RED_ICE_MELTED_FLAG, shouldRegister, { + BgIceShelter* redIceActor = va_arg(args, BgIceShelter*); + Actor* actor = (Actor*)redIceActor; + + auto redIceIdentity = OTRGlobals::Instance->gRandomizer->IdentifyRedIce( + gPlayState->sceneNum, (s16)actor->world.pos.x, (s16)actor->world.pos.z); + ObjectExtension::GetInstance().Set(actor, std::move(redIceIdentity)); + + if (*should) { + if (BgIceShelter_RandomizerHoldsItem(&redIceActor->dyna.actor)) { + BgIceShelter_RandomizerSpawnCollectible(actor); + } + } + }); + + // Draw particle effect to indicate a randomized item + COND_ID_HOOK(OnActorUpdate, ACTOR_BG_ICE_SHELTER, shouldRegister, BgIceShelter_RandomizerDrawSetup); + + // Collect item for melting red ice + COND_VB_SHOULD(VB_RED_ICE_DROP_ITEM, shouldRegister, { + BgIceShelter* redIceActor = va_arg(args, BgIceShelter*); + + if (*should) { + if (BgIceShelter_RandomizerHoldsItem(&redIceActor->dyna.actor)) { + BgIceShelter_RandomizerSpawnCollectible(&redIceActor->dyna.actor); + } + } + }); +} + +void Rando::StaticData::RegisterRedIceLocations() { + static bool registered = false; + if (registered) + return; + registered = true; + // clang-format off + // Randomizer Check Randomizer Check Quest Area Scene ID Params Short Name Hint Text Key Spoiler Collection Check + locationTable[RC_ZD_KING_ZORA_RED_ICE] = Location::RedIce(RC_ZD_KING_ZORA_RED_ICE, RCQUEST_BOTH, RCAREA_ZORAS_DOMAIN, SCENE_ZORAS_DOMAIN, TWO_ACTOR_PARAMS(628, -1818), "King Zora Red Ice", RHT_RED_ICE_ZORAS_DOMAIN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZD_KING_ZORA_RED_ICE)); + locationTable[RC_ZD_ZORA_SHOP_RED_ICE] = Location::RedIce(RC_ZD_ZORA_SHOP_RED_ICE, RCQUEST_BOTH, RCAREA_ZORAS_DOMAIN, SCENE_ZORAS_DOMAIN, TWO_ACTOR_PARAMS(483, 214), "Zora Shop Red Ice", RHT_RED_ICE_ZORAS_DOMAIN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZD_ZORA_SHOP_RED_ICE)); + locationTable[RC_ICE_CAVERN_ENTRANCE_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_ENTRANCE_RED_ICE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(411, 2332), "Entrance Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_ENTRANCE_RED_ICE)); + locationTable[RC_ICE_CAVERN_LOBBY_LEFT_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_LOBBY_LEFT_RED_ICE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-105, 854), "Lobby Left Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_LOBBY_LEFT_RED_ICE)); + locationTable[RC_ICE_CAVERN_LOBBY_RIGHT_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_LOBBY_RIGHT_RED_ICE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(119, 856), "Lobby Right Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_LOBBY_RIGHT_RED_ICE)); + locationTable[RC_ICE_CAVERN_SPINNING_BLADE_EAST_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_SPINNING_BLADE_EAST_RED_ICE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(651, -232), "Spinning Blade East Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_SPINNING_BLADE_EAST_RED_ICE)); + locationTable[RC_ICE_CAVERN_SPINNING_BLADE_WEST_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_SPINNING_BLADE_WEST_RED_ICE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-134, -415), "Spinning Blade West Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_SPINNING_BLADE_WEST_RED_ICE)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_FREESTANDING_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_HEART_PIECE_ROOM_FREESTANDING_RED_ICE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1261, 68), "Heart Piece Room Freestanding Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_FREESTANDING_RED_ICE)); + locationTable[RC_ICE_CAVERN_HEART_PIECE_ROOM_CHEST_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_HEART_PIECE_ROOM_CHEST_RED_ICE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1201, 643), "Heart Piece Room Chest Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CHEST_RED_ICE)); + locationTable[RC_ICE_CAVERN_MAP_ROOM_POT_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_MAP_ROOM_POT_RED_ICE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(366, -2036), "Map Room Pot Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MAP_ROOM_POT_RED_ICE)); + locationTable[RC_ICE_CAVERN_MAP_ROOM_CHEST_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_MAP_ROOM_CHEST_RED_ICE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(277, -2600), "Map Room Chest Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MAP_ROOM_CHEST_RED_ICE)); + locationTable[RC_ICE_CAVERN_SILVER_RUPEE_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_SILVER_RUPEE_RED_ICE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1126, -1577), "Silver Rupee Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_SILVER_RUPEE_RED_ICE)); + locationTable[RC_ICE_CAVERN_NEAR_END_LEFT_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_NEAR_END_LEFT_RED_ICE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1422, 586), "Near End Left Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_NEAR_END_LEFT_RED_ICE)); + locationTable[RC_ICE_CAVERN_NEAR_END_MIDDLE_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_NEAR_END_MIDDLE_RED_ICE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1459, 625), "Near End Middle Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_NEAR_END_MIDDLE_RED_ICE)); + locationTable[RC_ICE_CAVERN_NEAR_END_RIGHT_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_NEAR_END_RIGHT_RED_ICE, RCQUEST_VANILLA, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1488, 676), "Near End Right Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_NEAR_END_RIGHT_RED_ICE)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_DOOR_RED_ICE] = Location::RedIce(RC_GANONS_CASTLE_WATER_TRIAL_DOOR_RED_ICE, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(2212, -840), "Water Trial Door Red Ice", RHT_GANONS_CASTLE_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_DOOR_RED_ICE)); + locationTable[RC_GANONS_CASTLE_WATER_TRIAL_RUSTED_SWITCH_RED_ICE] = Location::RedIce(RC_GANONS_CASTLE_WATER_TRIAL_RUSTED_SWITCH_RED_ICE, RCQUEST_VANILLA, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(2912, -1420), "Water Trial Rusted Switch Red Ice", RHT_GANONS_CASTLE_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_WATER_TRIAL_RUSTED_SWITCH_RED_ICE)); + locationTable[RC_ICE_CAVERN_MQ_HUB_WEST_LEFT_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_MQ_HUB_WEST_LEFT_RED_ICE, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-142, -377), "Hub West Left Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_HUB_WEST_LEFT_RED_ICE)); + locationTable[RC_ICE_CAVERN_MQ_HUB_WEST_MIDDLE_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_MQ_HUB_WEST_MIDDLE_RED_ICE, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-121, -418), "Hub West Middle Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_HUB_WEST_MIDDLE_RED_ICE)); + locationTable[RC_ICE_CAVERN_MQ_HUB_WEST_RIGHT_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_MQ_HUB_WEST_RIGHT_RED_ICE, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-134, -462), "Hub West Right Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_HUB_WEST_RIGHT_RED_ICE)); + locationTable[RC_ICE_CAVERN_MQ_HUB_LEDGE_LEFT_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_MQ_HUB_LEDGE_LEFT_RED_ICE, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(577, -818), "Hub Ledge Left Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_HUB_LEDGE_LEFT_RED_ICE)); + locationTable[RC_ICE_CAVERN_MQ_HUB_LEDGE_MIDDLE_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_MQ_HUB_LEDGE_MIDDLE_RED_ICE, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(614, -770), "Hub Ledge Middle Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_HUB_LEDGE_MIDDLE_RED_ICE)); + locationTable[RC_ICE_CAVERN_MQ_HUB_LEDGE_RIGHT_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_MQ_HUB_LEDGE_RIGHT_RED_ICE, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(656, -722), "Hub Ledge Right Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_HUB_LEDGE_RIGHT_RED_ICE)); + locationTable[RC_ICE_CAVERN_MQ_COMPASS_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_MQ_COMPASS_RED_ICE, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(376, -2048), "Compass Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_COMPASS_RED_ICE)); + locationTable[RC_ICE_CAVERN_MQ_MAP_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_MQ_MAP_RED_ICE, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(1201, 648), "Map Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_MAP_RED_ICE)); + locationTable[RC_ICE_CAVERN_MQ_SCARECROW_LEFT_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_MQ_SCARECROW_LEFT_RED_ICE, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1335, -159), "Scarecrow Left Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_SCARECROW_LEFT_RED_ICE)); + locationTable[RC_ICE_CAVERN_MQ_SCARECROW_MIDDLE_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_MQ_SCARECROW_MIDDLE_RED_ICE, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1386, -159), "Scarecrow Middle Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_SCARECROW_MIDDLE_RED_ICE)); + locationTable[RC_ICE_CAVERN_MQ_SCARECROW_RIGHT_RED_ICE] = Location::RedIce(RC_ICE_CAVERN_MQ_SCARECROW_RIGHT_RED_ICE, RCQUEST_MQ, RCAREA_ICE_CAVERN, SCENE_ICE_CAVERN, TWO_ACTOR_PARAMS(-1438, -155), "Scarecrow Right Red Ice", RHT_ICE_CAVERN_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ICE_CAVERN_MQ_SCARECROW_RIGHT_RED_ICE)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM_RED_ICE] = Location::RedIce(RC_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM_RED_ICE, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(-864, -2745), "Stalfos Room Red Ice", RHT_GERUDO_TRAINING_GROUND_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM_RED_ICE)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_LEFT_RED_ICE] = Location::RedIce(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_LEFT_RED_ICE, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1754, -1152), "Water Trial First Room Left Red Ice", RHT_GANONS_CASTLE_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_LEFT_RED_ICE)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_RIGHT_RED_ICE] = Location::RedIce(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_RIGHT_RED_ICE, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(1743, -529), "Water Trial First Room Right Red Ice", RHT_GANONS_CASTLE_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_RIGHT_RED_ICE)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_BACK_LEFT_RED_ICE] = Location::RedIce(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_BACK_LEFT_RED_ICE, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(2096, -1011), "Water Trial First Room Back Left Red Ice", RHT_GANONS_CASTLE_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_BACK_LEFT_RED_ICE)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_1] = Location::RedIce(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_1, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(2209, -889), "Water Trial First Room Door Red Ice 1", RHT_GANONS_CASTLE_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_1)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_2] = Location::RedIce(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_2, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(2198, -845), "Water Trial First Room Door Red Ice 2", RHT_GANONS_CASTLE_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_2)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_3] = Location::RedIce(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_3, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(2194, -797), "Water Trial First Room Door Red Ice 3", RHT_GANONS_CASTLE_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_3)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_4] = Location::RedIce(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_4, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(2200, -755), "Water Trial First Room Door Red Ice 4", RHT_GANONS_CASTLE_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_4)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_SILVER_RUPEE_RED_ICE] = Location::RedIce(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SILVER_RUPEE_RED_ICE, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(3370, -729), "Water Trial Silver Rupee Red Ice", RHT_GANONS_CASTLE_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SILVER_RUPEE_RED_ICE)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_1] = Location::RedIce(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_1, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(3370, -776), "Water Trial Second Door Red Ice 1", RHT_GANONS_CASTLE_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_1)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_2] = Location::RedIce(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_2, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(3370, -834), "Water Trial Second Door Red Ice 2", RHT_GANONS_CASTLE_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_2)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_3] = Location::RedIce(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_3, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(3370, -889), "Water Trial Second Door Red Ice 3", RHT_GANONS_CASTLE_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_3)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_4] = Location::RedIce(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_4, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(3370, -950), "Water Trial Second Door Red Ice 4", RHT_GANONS_CASTLE_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_4)); + locationTable[RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_5] = Location::RedIce(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_5, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(2906, -1482), "Water Trial Second Door Red Ice 5", RHT_GANONS_CASTLE_RED_ICE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_5)); + // clang-format on +} + +static RegisterShipInitFunc initFunc_ShuffleRedIce(RegisterShuffleRedIce, { "IS_RANDO" }); +static RegisterShipInitFunc registerRedIceLocations(Rando::StaticData::RegisterRedIceLocations); \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/ShuffleSigns.cpp b/soh/soh/Enhancements/randomizer/ShuffleSigns.cpp new file mode 100644 index 00000000000..6d883228a23 --- /dev/null +++ b/soh/soh/Enhancements/randomizer/ShuffleSigns.cpp @@ -0,0 +1,265 @@ +#include +#include "soh/ObjectExtension/ObjectExtension.h" +#include "item_category_adj.h" +#include "particle_cmc.h" +extern "C" { +extern PlayState* gPlayState; +#include "overlays/actors/ovl_En_Kanban/z_en_kanban.h" +#include "objects/gameplay_keep/gameplay_keep.h" +#include "overlays/actors/ovl_En_Wonder_Talk/z_en_wonder_talk.h" +#include "overlays/actors/ovl_En_Wonder_Talk2/z_en_wonder_talk2.h" +} + +uint8_t Sign_RandomizerHoldsItem(Actor* actor, PlayState* play) { + const auto signIdentity = ObjectExtension::GetInstance().Get(actor); + if (signIdentity == nullptr) { + return false; + } + + RandomizerCheck rc = signIdentity->randomizerCheck; + uint8_t isDungeon = Rando::StaticData::GetLocation(rc)->IsDungeon(); + auto signSetting = RAND_GET_OPTION(RSK_SHUFFLE_SIGNS); + + // Don't pull randomized item if sign isn't randomized or is already checked + if (!IS_RANDO || (signSetting.Is(RO_SHUFFLE_SIGNS_OVERWORLD) && isDungeon) || + (signSetting.Is(RO_SHUFFLE_SIGNS_DUNGEONS) && !isDungeon) || + Flags_GetRandomizerInf(signIdentity->randomizerInf) || signIdentity->randomizerCheck == RC_UNKNOWN_CHECK) { + return false; + } else { + return true; + } +} + +static void Sign_RandomizerDraw(Actor* actor, Color_RGBA8* primColor, Color_RGBA8* secColor, Color_RGBA8* envColor) { + Vec3f pos; + static Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + float yKanbanOffset = LINK_IS_CHILD && actor->id == ACTOR_EN_KANBAN ? 15.0f : 0.0f; + + velocity.y = -0.05f; + accel.y = -0.025f; + + pos.x = Rand_CenteredFloat(10.0f) + actor->world.pos.x; + pos.y = (Rand_ZeroOne() * 10.0f) + actor->world.pos.y + yKanbanOffset; + pos.z = Rand_CenteredFloat(10.0f) + actor->world.pos.z; + EffectSsKiraKira_SpawnFocused(gPlayState, &pos, &velocity, &accel, secColor, envColor, 2000, 100); + EffectSsKiraKira_SpawnFocused(gPlayState, &pos, &velocity, &accel, primColor, envColor, 2000, 100); +} + +void Sign_RandomizerDrawSetup(void* actor) { + GetItemCategory getItemCategory; + Actor* signActor = (Actor*)actor; + + // If not a randomized item or too far, don't draw + if (!Sign_RandomizerHoldsItem(signActor, gPlayState) || signActor->xzDistToPlayer > 1000.0f) { + return; + } + + bool cmc = CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeAndTextureMatchContents"), 0); + int requiresStoneAgony = CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeDependsStoneOfAgony"), 0); + + int isNotCMC = !cmc || (requiresStoneAgony && !CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)); + + Color_RGBA8 primColor; + Color_RGBA8 secColor; + Color_RGBA8 envColor; + + const auto signIdentity = ObjectExtension::GetInstance().Get(signActor); + if (signIdentity == nullptr) { + return; + } + + GetItemEntry signItem = + Rando::Context::GetInstance()->GetFinalGIEntry(signIdentity->randomizerCheck, true, GI_NONE); + getItemCategory = Randomizer_AdjustItemCategory(signItem); + + if (isNotCMC) { + getItemCategory = ITEM_CATEGORY_MAJOR; + } + primColor = Randomizer_GetParticleCMCColor(getItemCategory, COLOR_PRIMARY); + secColor = Randomizer_GetParticleCMCColor(getItemCategory, COLOR_SECONDARY); + envColor = Randomizer_GetParticleCMCColor(getItemCategory, COLOR_FLARE); + Sign_RandomizerDraw(signActor, &primColor, &secColor, &envColor); +} + +void Sign_RandomizerSpawnCollectible(Actor* actor) { + const auto signIdentity = ObjectExtension::GetInstance().Get(actor); + + if (signIdentity == nullptr) { + return; + } + Flags_SetRandomizerInf(signIdentity->randomizerInf); +} + +void Sign_RoyalTombSpawnCollectible(int16_t flagType, int16_t flag) { + if (!Flags_GetRandomizerInf(RAND_INF_GY_ROYAL_TOMB_GRAVE) && + Flags_GetEventChkInf(EVENTCHKINF_DESTROYED_ROYAL_FAMILY_TOMB)) { + Flags_SetRandomizerInf(RAND_INF_GY_ROYAL_TOMB_GRAVE); + } +} + +void RegisterShuffleSigns() { + bool shouldRegister = IS_RANDO && Rando::Context::GetInstance()->GetOption(RSK_SHUFFLE_SIGNS).Get(); + + COND_ID_HOOK(OnActorInit, ACTOR_EN_KANBAN, shouldRegister, [](void* actorRef) { + Actor* actor = static_cast(actorRef); + EnKanban* signActor = static_cast(actorRef); + + auto signIdentity = OTRGlobals::Instance->gRandomizer->IdentifySign( + gPlayState->sceneNum, (s16)actor->world.pos.x, (s16)actor->world.pos.z, actor->id); + ObjectExtension::GetInstance().Set(actor, std::move(signIdentity)); + }); + + COND_ID_HOOK(OnActorInit, ACTOR_EN_A_OBJ, shouldRegister, [](void* actorRef) { + Actor* actor = static_cast(actorRef); + EnAObj* signActor = static_cast(actorRef); + + auto signIdentity = OTRGlobals::Instance->gRandomizer->IdentifySign( + gPlayState->sceneNum, (s16)actor->world.pos.x, (s16)actor->world.pos.z, actor->id); + ObjectExtension::GetInstance().Set(actor, std::move(signIdentity)); + }); + + COND_ID_HOOK(OnActorInit, ACTOR_EN_WONDER_TALK, shouldRegister, [](void* actorRef) { + Actor* actor = static_cast(actorRef); + EnWonderTalk* signActor = static_cast(actorRef); + + auto signIdentity = OTRGlobals::Instance->gRandomizer->IdentifySign( + gPlayState->sceneNum, (s16)actor->world.pos.x, (s16)actor->world.pos.z, actor->id); + ObjectExtension::GetInstance().Set(actor, std::move(signIdentity)); + }); + + COND_ID_HOOK(OnActorInit, ACTOR_EN_WONDER_TALK2, shouldRegister, [](void* actorRef) { + Actor* actor = static_cast(actorRef); + EnWonderTalk2* signActor = static_cast(actorRef); + + auto signIdentity = OTRGlobals::Instance->gRandomizer->IdentifySign( + gPlayState->sceneNum, (s16)actor->world.pos.x, (s16)actor->world.pos.z, actor->id); + ObjectExtension::GetInstance().Set(actor, std::move(signIdentity)); + }); + + // Draw particle effect to indicate a randomized item + COND_ID_HOOK(OnActorUpdate, ACTOR_EN_KANBAN, shouldRegister, Sign_RandomizerDrawSetup); + + COND_ID_HOOK(OnActorUpdate, ACTOR_EN_A_OBJ, shouldRegister, Sign_RandomizerDrawSetup); + + COND_ID_HOOK(OnActorUpdate, ACTOR_EN_WONDER_TALK, shouldRegister, Sign_RandomizerDrawSetup); + + COND_ID_HOOK(OnActorUpdate, ACTOR_EN_WONDER_TALK2, shouldRegister, Sign_RandomizerDrawSetup); + + COND_VB_SHOULD(VB_SKIP_TALKING, shouldRegister, { + Actor* talkActor = GET_PLAYER(gPlayState)->talkActor; + if (talkActor != NULL) { + switch (talkActor->id) { + case ACTOR_EN_KANBAN: + case ACTOR_EN_A_OBJ: + case ACTOR_EN_WONDER_TALK: + case ACTOR_EN_WONDER_TALK2: + if (Sign_RandomizerHoldsItem(talkActor, gPlayState)) { + Sign_RandomizerSpawnCollectible(talkActor); + *should = false; + } + break; + default: + break; + } + } + }); + + // Give Royal Tomb item if destroyed + COND_HOOK(OnFlagSet, shouldRegister, Sign_RoyalTombSpawnCollectible); +} + +void Rando::StaticData::RegisterSignLocations() { + static bool registered = false; + if (registered) + return; + registered = true; + // clang-format off + // Overworld Signs + // Randomizer Check Randomizer Check Quest Area Scene ID Params Short Name Hint Text Key Actor Id Spoiler Collection Check +locationTable[RC_KF_DEKU_TREE_RECTANGLE_SIGN] = Location::Sign(RC_KF_DEKU_TREE_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(871, 311), "Deku Tree Rectangle Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_DEKU_TREE_RECTANGLE_SIGN)); +locationTable[RC_KF_STEPPING_STONES_RECTANGLE_SIGN] = Location::Sign(RC_KF_STEPPING_STONES_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(607, -80), "Stepping Stones Rectangle Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_STEPPING_STONES_RECTANGLE_SIGN)); +locationTable[RC_KF_LINKS_HOUSE_RECTANGLE_SIGN] = Location::Sign(RC_KF_LINKS_HOUSE_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(49, 967), "Link's House Rectangle Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_LINKS_HOUSE_RECTANGLE_SIGN)); +locationTable[RC_KF_FIRST_TRAINING_CENTER_RECTANGLE_SIGN] = Location::Sign(RC_KF_FIRST_TRAINING_CENTER_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-494, 598), "First Training Center Rectangle Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_FIRST_TRAINING_CENTER_RECTANGLE_SIGN)); +locationTable[RC_KF_SECOND_TRAINING_CENTER_RECTANGLE_SIGN] = Location::Sign(RC_KF_SECOND_TRAINING_CENTER_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-538, 718), "Second Training Center Rectangle Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_SECOND_TRAINING_CENTER_RECTANGLE_SIGN)); +locationTable[RC_KF_AFTER_CRAWLSPACE_RECTANGLE_SIGN] = Location::Sign(RC_KF_AFTER_CRAWLSPACE_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-784, 1675), "After Crawlspace Rectangle Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_AFTER_CRAWLSPACE_RECTANGLE_SIGN)); +locationTable[RC_KF_CRAWL_RECTANGLE_RECTANGLE_SIGN] = Location::Sign(RC_KF_CRAWL_RECTANGLE_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-845, 1018), "Crawl Rectangle Rectangle Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_CRAWL_RECTANGLE_RECTANGLE_SIGN)); +locationTable[RC_KF_LOST_WOODS_RECTANGLE_SIGN] = Location::Sign(RC_KF_LOST_WOODS_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-1432, -426), "Lost Woods Rectangle Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_LOST_WOODS_RECTANGLE_SIGN)); +locationTable[RC_KF_HOUSE_OF_TWINS_ARROW_SIGN] = Location::Sign(RC_KF_HOUSE_OF_TWINS_ARROW_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(1089, 473), "House of Twins Arrow Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_HOUSE_OF_TWINS_ARROW_SIGN)); +locationTable[RC_KF_SHOP_ARROW_SIGN] = Location::Sign(RC_KF_SHOP_ARROW_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(728, -195), "Shop Arrow Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_SHOP_ARROW_SIGN)); +locationTable[RC_KF_SARIAS_HOUSE_ARROW_SIGN] = Location::Sign(RC_KF_SARIAS_HOUSE_ARROW_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(436, 601), "Saria's House Arrow Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_SARIAS_HOUSE_ARROW_SIGN)); +locationTable[RC_KF_LOST_WOODS_ARROW_SIGN] = Location::Sign(RC_KF_LOST_WOODS_ARROW_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-170, -1335), "Lost Woods Arrow Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_LOST_WOODS_ARROW_SIGN)); +locationTable[RC_KF_MIDOS_HOUSE_ARROW_SIGN] = Location::Sign(RC_KF_MIDOS_HOUSE_ARROW_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-512, -459), "Mido's House Arrow Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_MIDOS_HOUSE_ARROW_SIGN)); +locationTable[RC_KF_TRAINING_CENTER_ENTRANCE_ARROW_SIGN] = Location::Sign(RC_KF_TRAINING_CENTER_ENTRANCE_ARROW_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-779, 424), "Training Center Entrance Arrow Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_TRAINING_CENTER_ENTRANCE_ARROW_SIGN)); +locationTable[RC_KF_INNER_TRAINING_CENTER_ARROW_SIGN] = Location::Sign(RC_KF_INNER_TRAINING_CENTER_ARROW_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-924, 928), "Inner Training Center Arrow Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_INNER_TRAINING_CENTER_ARROW_SIGN)); +locationTable[RC_KF_KNOW_IT_ALL_BROTHERS_HOUSE_ARROW_SIGN] = Location::Sign(RC_KF_KNOW_IT_ALL_BROTHERS_HOUSE_ARROW_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-1008, 479), "Know-It-All Brothers House Arrow Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_KNOW_IT_ALL_BROTHERS_HOUSE_ARROW_SIGN)); +locationTable[RC_KF_BOULDER_MAZE_RECTANGLE_SIGN] = Location::Sign(RC_KF_BOULDER_MAZE_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-273, 2173), "Boulder Maze Rectangle Sign", RHT_SIGN_KOKIRI_FOREST, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_BOULDER_MAZE_RECTANGLE_SIGN)); +locationTable[RC_KF_LINKS_HOUSE_SIGN] = Location::Sign(RC_KF_LINKS_HOUSE_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_LINKS_HOUSE, TWO_ACTOR_PARAMS(78, 116), "Link's House Sign", RHT_SIGN_LINKS_HOUSE, ACTOR_EN_WONDER_TALK2, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_LINKS_HOUSE_SIGN)); +locationTable[RC_LW_THEATER_RECTANGLE_SIGN] = Location::Sign(RC_LW_THEATER_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_LOST_WOODS, SCENE_GROTTOS, TWO_ACTOR_PARAMS(3898, 1228), "Theater Rectangle Sign", RHT_SIGN_DEKU_THEATER, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LW_THEATER_RECTANGLE_SIGN)); +locationTable[RC_HF_CASTLE_EXIT_ARROW_SIGN] = Location::Sign(RC_HF_CASTLE_EXIT_ARROW_SIGN, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_HYRULE_FIELD, TWO_ACTOR_PARAMS(-130, 2105), "Castle Exit Arrow Sign", RHT_SIGN_HYRULE_FIELD, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_CASTLE_EXIT_ARROW_SIGN)); +locationTable[RC_HF_WOODED_EXIT_ARROW_SIGN] = Location::Sign(RC_HF_WOODED_EXIT_ARROW_SIGN, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_HYRULE_FIELD, TWO_ACTOR_PARAMS(3952, 7232), "Wooded Exit Arrow Sign", RHT_SIGN_HYRULE_FIELD, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_WOODED_EXIT_ARROW_SIGN)); +locationTable[RC_HF_ROCKY_PATH_EXIT_ARROW_SIGN] = Location::Sign(RC_HF_ROCKY_PATH_EXIT_ARROW_SIGN, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_HYRULE_FIELD, TWO_ACTOR_PARAMS(-8780, 7680), "Rocky Path Exit Arrow Sign", RHT_SIGN_HYRULE_FIELD, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_ROCKY_PATH_EXIT_ARROW_SIGN)); +locationTable[RC_HF_FENCED_ARROW_SIGN] = Location::Sign(RC_HF_FENCED_ARROW_SIGN, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_HYRULE_FIELD, TWO_ACTOR_PARAMS(-4935, 14045), "Fenced Arrow Sign", RHT_SIGN_HYRULE_FIELD, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_FENCED_ARROW_SIGN)); +locationTable[RC_HF_CENTER_EXIT_ARROW_SIGN] = Location::Sign(RC_HF_CENTER_EXIT_ARROW_SIGN, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_HYRULE_FIELD, TWO_ACTOR_PARAMS(-1564, 4318), "Center Exit Arrow Sign", RHT_SIGN_HYRULE_FIELD, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_CENTER_EXIT_ARROW_SIGN)); +locationTable[RC_HF_RIVER_EXIT_ARROW_SIGN] = Location::Sign(RC_HF_RIVER_EXIT_ARROW_SIGN, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_HYRULE_FIELD, TWO_ACTOR_PARAMS(5925, 3805), "River Exit Arrow Sign", RHT_SIGN_HYRULE_FIELD, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_RIVER_EXIT_ARROW_SIGN)); +locationTable[RC_HF_STAIRS_EXIT_ARROW_SIGN] = Location::Sign(RC_HF_STAIRS_EXIT_ARROW_SIGN, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_HYRULE_FIELD, TWO_ACTOR_PARAMS(3230, 570), "Stairs Exit Arrow Sign", RHT_SIGN_HYRULE_FIELD, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_STAIRS_EXIT_ARROW_SIGN)); +locationTable[RC_MK_SHOOTING_GALLERY_RECTANGLE_SIGN] = Location::Sign(RC_MK_SHOOTING_GALLERY_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_MARKET, SCENE_SHOOTING_GALLERY, TWO_ACTOR_PARAMS(59, 365), "Shooting Gallery Rectangle Sign", RHT_SIGN_MK_SHOOTING_GALLERY, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_SHOOTING_GALLERY_RECTANGLE_SIGN)); +locationTable[RC_MK_MASK_SHOP_SIGN] = Location::Sign(RC_MK_MASK_SHOP_SIGN, RCQUEST_BOTH, RCAREA_MARKET, SCENE_HAPPY_MASK_SHOP, TWO_ACTOR_PARAMS(-112, 31), "Mask Shop Sign", RHT_SIGN_HAPPY_MASK_SHOP, ACTOR_EN_WONDER_TALK2, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MK_MASK_SHOP_SIGN)); +locationTable[RC_TOT_ALTAR] = Location::Sign(RC_TOT_ALTAR, RCQUEST_BOTH, RCAREA_MARKET, SCENE_TEMPLE_OF_TIME, TWO_ACTOR_PARAMS(0, 1330), "Altar", RHT_SIGN_TEMPLE_OF_TIME, ACTOR_EN_WONDER_TALK, SpoilerCollectionCheck::RandomizerInf(RAND_INF_TOT_ALTAR)); +locationTable[RC_HC_DEAD_END_RECTANGLE_SIGN] = Location::Sign(RC_HC_DEAD_END_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(2351, 2634), "Dead End Rectangle Sign", RHT_SIGN_HYRULE_CASTLE, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_DEAD_END_RECTANGLE_SIGN)); +locationTable[RC_KAK_GUARD_GATE_RECTANGLE_SIGN] = Location::Sign(RC_KAK_GUARD_GATE_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(210, -1275), "Guard Gate Rectangle Sign", RHT_SIGN_KAKARIKO_VILLAGE, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_GUARD_GATE_RECTANGLE_SIGN)); +locationTable[RC_KAK_WELL_RECTANGLE_SIGN] = Location::Sign(RC_KAK_WELL_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(655, 525), "Well Rectangle Sign", RHT_SIGN_KAKARIKO_VILLAGE, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_WELL_RECTANGLE_SIGN)); +locationTable[RC_KAK_SOUTHEAST_EXIT_ARROW_SIGN] = Location::Sign(RC_KAK_SOUTHEAST_EXIT_ARROW_SIGN, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(1165, 1550), "Southeast Exit Arrow Sign", RHT_SIGN_KAKARIKO_VILLAGE, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_SOUTHEAST_EXIT_ARROW_SIGN)); +locationTable[RC_KAK_FRONT_GATE_ARROW_SIGN] = Location::Sign(RC_KAK_FRONT_GATE_ARROW_SIGN, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(-2590, 965), "Front Gate Arrow Sign", RHT_SIGN_KAKARIKO_VILLAGE, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_FRONT_GATE_ARROW_SIGN)); +locationTable[RC_KAK_SHOOTING_GALLERY_RECTANGLE_SIGN] = Location::Sign(RC_KAK_SHOOTING_GALLERY_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_SHOOTING_GALLERY, TWO_ACTOR_PARAMS(59, 365), "Shooting Gallery Rectangle Sign", RHT_SIGN_KAK_SHOOTING_GALLERY, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_SHOOTING_GALLERY_RECTANGLE_SIGN)); +locationTable[RC_GY_ENTRANCE_RECTANGLE_SIGN] = Location::Sign(RC_GY_ENTRANCE_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_GRAVEYARD, TWO_ACTOR_PARAMS(-1110, 430), "Entrance Rectangle Sign", RHT_SIGN_GRAVEYARD, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_ENTRANCE_RECTANGLE_SIGN)); +locationTable[RC_GY_ENTRANCE_PLINTH] = Location::Sign(RC_GY_ENTRANCE_PLINTH, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_GRAVEYARD, TWO_ACTOR_PARAMS(-805, 266), "Entrance Plinth", RHT_SIGN_GRAVEYARD, ACTOR_EN_WONDER_TALK2, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_ENTRANCE_PLINTH)); +locationTable[RC_GY_RIGHT_OF_ROYAL_TOMB_GRAVE] = Location::Sign(RC_GY_RIGHT_OF_ROYAL_TOMB_GRAVE, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_GRAVEYARD, TWO_ACTOR_PARAMS(654, 258), "Right of Royal Tomb Grave", RHT_SIGN_GRAVEYARD, ACTOR_EN_WONDER_TALK, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_RIGHT_OF_ROYAL_TOMB_GRAVE)); +locationTable[RC_GY_LEFT_OF_ROYAL_TOMB_GRAVE] = Location::Sign(RC_GY_LEFT_OF_ROYAL_TOMB_GRAVE, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_GRAVEYARD, TWO_ACTOR_PARAMS(654, -102), "Left of Royal Tomb Grave", RHT_SIGN_GRAVEYARD, ACTOR_EN_WONDER_TALK, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_LEFT_OF_ROYAL_TOMB_GRAVE)); +locationTable[RC_GY_ROYAL_TOMB_GRAVE] = Location::Sign(RC_GY_ROYAL_TOMB_GRAVE, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_GRAVEYARD, TWO_ACTOR_PARAMS(752, 85), "Royal Tomb Grave", RHT_SIGN_GRAVEYARD, ACTOR_EN_WONDER_TALK, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_ROYAL_TOMB_GRAVE)); +locationTable[RC_DMT_ABOVE_DODONGO_RECTANGLE_SIGN] = Location::Sign(RC_DMT_ABOVE_DODONGO_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_TRAIL, SCENE_DEATH_MOUNTAIN_TRAIL, TWO_ACTOR_PARAMS(-1300, -496), "Above Dodongo Rectangle Sign", RHT_SIGN_DEATH_MOUNTAIN_TRAIL, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DMT_ABOVE_DODONGO_RECTANGLE_SIGN)); +locationTable[RC_DMT_ADULT_CENTER_EXIT_ARROW_SIGN] = Location::Sign(RC_DMT_ADULT_CENTER_EXIT_ARROW_SIGN, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_TRAIL, SCENE_DEATH_MOUNTAIN_TRAIL, TWO_ACTOR_PARAMS(-299, -1787), "Adult Center Exit Arrow Sign", RHT_SIGN_DEATH_MOUNTAIN_TRAIL, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DMT_ADULT_CENTER_EXIT_ARROW_SIGN)); +locationTable[RC_DMT_CHILD_CENTER_EXIT_RECTANGLE_SIGN] = Location::Sign(RC_DMT_CHILD_CENTER_EXIT_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_TRAIL, SCENE_DEATH_MOUNTAIN_TRAIL, TWO_ACTOR_PARAMS(-299, -1787), "Child Center Exit Rectangle Sign", RHT_SIGN_DEATH_MOUNTAIN_TRAIL, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DMT_CHILD_CENTER_EXIT_RECTANGLE_SIGN)); +locationTable[RC_DMT_DODONGOS_CAVERN_RECTANGLE_SIGN] = Location::Sign(RC_DMT_DODONGOS_CAVERN_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_TRAIL, SCENE_DEATH_MOUNTAIN_TRAIL, TWO_ACTOR_PARAMS(-1834, -571), "Dodongo's Cavern Rectangle Sign", RHT_SIGN_DEATH_MOUNTAIN_TRAIL, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DMT_DODONGOS_CAVERN_RECTANGLE_SIGN)); +locationTable[RC_DMT_CENTER_TRAIL_RECTANGLE_SIGN] = Location::Sign(RC_DMT_CENTER_TRAIL_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_TRAIL, SCENE_DEATH_MOUNTAIN_TRAIL, TWO_ACTOR_PARAMS(-1260, 540), "Center Trail Rectangle Sign", RHT_SIGN_DEATH_MOUNTAIN_TRAIL, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DMT_CENTER_TRAIL_RECTANGLE_SIGN)); +locationTable[RC_DMT_TO_UPPER_TRAIL_ARROW_SIGN] = Location::Sign(RC_DMT_TO_UPPER_TRAIL_ARROW_SIGN, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_TRAIL, SCENE_DEATH_MOUNTAIN_TRAIL, TWO_ACTOR_PARAMS(-1127, 44), "To Upper Trail Arrow Sign", RHT_SIGN_DEATH_MOUNTAIN_TRAIL, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DMT_TO_UPPER_TRAIL_ARROW_SIGN)); +locationTable[RC_DMT_UPPER_EXIT_ARROW_SIGN] = Location::Sign(RC_DMT_UPPER_EXIT_ARROW_SIGN, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_TRAIL, SCENE_DEATH_MOUNTAIN_TRAIL, TWO_ACTOR_PARAMS(-40, -4410), "Upper Exit Arrow Sign", RHT_SIGN_DEATH_MOUNTAIN_TRAIL, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DMT_UPPER_EXIT_ARROW_SIGN)); +locationTable[RC_DMT_TO_CENTER_EXIT_ARROW_SIGN] = Location::Sign(RC_DMT_TO_CENTER_EXIT_ARROW_SIGN, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_TRAIL, SCENE_DEATH_MOUNTAIN_TRAIL, TWO_ACTOR_PARAMS(-735, 595), "To Center Exit Arrow Sign", RHT_SIGN_DEATH_MOUNTAIN_TRAIL, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DMT_TO_CENTER_EXIT_ARROW_SIGN)); +locationTable[RC_DMT_LOWER_EXIT_ARROW_SIGN] = Location::Sign(RC_DMT_LOWER_EXIT_ARROW_SIGN, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_TRAIL, SCENE_DEATH_MOUNTAIN_TRAIL, TWO_ACTOR_PARAMS(-1300, 2155), "Lower Exit Arrow Sign", RHT_SIGN_DEATH_MOUNTAIN_TRAIL, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DMT_LOWER_EXIT_ARROW_SIGN)); +locationTable[RC_GC_CHILD_ROLLING_GORON_RECTANGLE_SIGN] = Location::Sign(RC_GC_CHILD_ROLLING_GORON_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_GORON_CITY, SCENE_GORON_CITY, TWO_ACTOR_PARAMS(333, -684), "Child Rolling Goron Rectangle Sign", RHT_SIGN_GORON_CITY, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GC_CHILD_ROLLING_GORON_RECTANGLE_SIGN)); +locationTable[RC_DMC_BRIDGE_EXIT_ARROW_SIGN] = Location::Sign(RC_DMC_BRIDGE_EXIT_ARROW_SIGN, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_CRATER, SCENE_DEATH_MOUNTAIN_CRATER, TWO_ACTOR_PARAMS(-1610, 95), "Bridge Exit Arrow Sign", RHT_SIGN_DEATH_MOUNTAIN_CRATER, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DMC_BRIDGE_EXIT_ARROW_SIGN)); +locationTable[RC_ZR_SLEEPLESS_WATERFALL_PLAQUE] = Location::Sign(RC_ZR_SLEEPLESS_WATERFALL_PLAQUE, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(4096, -1401), "Sleepless Waterfall Plaque", RHT_SIGN_ZORAS_RIVER, ACTOR_EN_WONDER_TALK2, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_SLEEPLESS_WATERFALL_PLAQUE)); +locationTable[RC_ZD_SHOP_RECTANGLE_SIGN] = Location::Sign(RC_ZD_SHOP_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_ZORAS_DOMAIN, SCENE_ZORAS_DOMAIN, TWO_ACTOR_PARAMS(477, 318), "Shop Rectangle Sign", RHT_SIGN_ZORAS_DOMAIN, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZD_SHOP_RECTANGLE_SIGN)); +locationTable[RC_ZD_ENTRANCE_RECTANGLE_SIGN] = Location::Sign(RC_ZD_ENTRANCE_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_ZORAS_DOMAIN, SCENE_ZORAS_DOMAIN, TWO_ACTOR_PARAMS(-980, -210), "Entrance Rectangle Sign", RHT_SIGN_ZORAS_DOMAIN, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZD_ENTRANCE_RECTANGLE_SIGN)); +locationTable[RC_ZD_KING_ZORA_PATH_ARROW_SIGN] = Location::Sign(RC_ZD_KING_ZORA_PATH_ARROW_SIGN, RCQUEST_BOTH, RCAREA_ZORAS_DOMAIN, SCENE_ZORAS_DOMAIN, TWO_ACTOR_PARAMS(217, 150), "King Zora Path Arrow Sign", RHT_SIGN_ZORAS_DOMAIN, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZD_KING_ZORA_PATH_ARROW_SIGN)); +locationTable[RC_ZD_NEAR_KING_ZORA_RECTANGLE_SIGN] = Location::Sign(RC_ZD_NEAR_KING_ZORA_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_ZORAS_DOMAIN, SCENE_ZORAS_DOMAIN, TWO_ACTOR_PARAMS(720, -1345), "Near King Zora Rectangle Sign", RHT_SIGN_ZORAS_DOMAIN, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZD_NEAR_KING_ZORA_RECTANGLE_SIGN)); +locationTable[RC_ZD_NEAR_KING_ZORA_ARROW_SIGN] = Location::Sign(RC_ZD_NEAR_KING_ZORA_ARROW_SIGN, RCQUEST_BOTH, RCAREA_ZORAS_DOMAIN, SCENE_ZORAS_DOMAIN, TWO_ACTOR_PARAMS(345, -1572), "Near King Zora Arrow Sign", RHT_SIGN_ZORAS_DOMAIN, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZD_NEAR_KING_ZORA_ARROW_SIGN)); +locationTable[RC_ZF_JABU_JABU_PLATFORM_RECTANGLE_SIGN] = Location::Sign(RC_ZF_JABU_JABU_PLATFORM_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_ZORAS_FOUNTAIN, SCENE_ZORAS_FOUNTAIN, TWO_ACTOR_PARAMS(-1900, 208), "Jabu-Jabu Platform Rectangle Sign", RHT_SIGN_ZORAS_FOUNTAIN, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZF_JABU_JABU_PLATFORM_RECTANGLE_SIGN)); +locationTable[RC_ZF_ENTRANCE_ARROW_SIGN] = Location::Sign(RC_ZF_ENTRANCE_ARROW_SIGN, RCQUEST_BOTH, RCAREA_ZORAS_FOUNTAIN, SCENE_ZORAS_FOUNTAIN, TWO_ACTOR_PARAMS(-2557, 486), "Entrance Arrow Sign", RHT_SIGN_ZORAS_FOUNTAIN, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZF_ENTRANCE_ARROW_SIGN)); +locationTable[RC_LH_LAB_RECTANGLE_SIGN] = Location::Sign(RC_LH_LAB_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_LAKE_HYLIA, SCENE_LAKE_HYLIA, TWO_ACTOR_PARAMS(-2300, 3670), "Lab Rectangle Sign", RHT_SIGN_LAKE_HYLIA, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LH_LAB_RECTANGLE_SIGN)); +locationTable[RC_LH_NORTH_EXIT_ARROW_SIGN] = Location::Sign(RC_LH_NORTH_EXIT_ARROW_SIGN, RCQUEST_BOTH, RCAREA_LAKE_HYLIA, SCENE_LAKE_HYLIA, TWO_ACTOR_PARAMS(-1835, 995), "North Exit Arrow Sign", RHT_SIGN_LAKE_HYLIA, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LH_NORTH_EXIT_ARROW_SIGN)); +locationTable[RC_LH_FISHING_SIGN] = Location::Sign(RC_LH_FISHING_SIGN, RCQUEST_BOTH, RCAREA_LAKE_HYLIA, SCENE_LAKE_HYLIA, TWO_ACTOR_PARAMS(1341, 3779), "Fishing Sign", RHT_SIGN_LAKE_HYLIA, ACTOR_EN_WONDER_TALK2, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LH_FISHING_SIGN)); +locationTable[RC_LH_ISLAND_PEDESTAL] = Location::Sign(RC_LH_ISLAND_PEDESTAL, RCQUEST_BOTH, RCAREA_LAKE_HYLIA, SCENE_LAKE_HYLIA, TWO_ACTOR_PARAMS(-491, 7259), "Island Pedestal", RHT_SIGN_LAKE_HYLIA, ACTOR_EN_WONDER_TALK2, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LH_ISLAND_PEDESTAL)); +locationTable[RC_LH_FISHING_POND_RECTANGLE_SIGN] = Location::Sign(RC_LH_FISHING_POND_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_LAKE_HYLIA, SCENE_FISHING_POND, TWO_ACTOR_PARAMS(53, 982), "Fishing Pond Rectangle Sign", RHT_SIGN_FISHING_POND, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LH_FISHING_POND_RECTANGLE_SIGN)); +locationTable[RC_GV_BRIDGE_RECTANGLE_SIGN] = Location::Sign(RC_GV_BRIDGE_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_GERUDO_VALLEY, SCENE_GERUDO_VALLEY, TWO_ACTOR_PARAMS(359, 254), "Bridge Rectangle Sign", RHT_SIGN_GERUDO_VALLEY, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GV_BRIDGE_RECTANGLE_SIGN)); +locationTable[RC_GV_EAST_EXIT_ARROW_SIGN] = Location::Sign(RC_GV_EAST_EXIT_ARROW_SIGN, RCQUEST_BOTH, RCAREA_GERUDO_VALLEY, SCENE_GERUDO_VALLEY, TWO_ACTOR_PARAMS(2778, 593), "East Exit Arrow Sign", RHT_SIGN_GERUDO_VALLEY, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GV_EAST_EXIT_ARROW_SIGN)); +locationTable[RC_GF_EAST_EXIT_ARROW_SIGN] = Location::Sign(RC_GF_EAST_EXIT_ARROW_SIGN, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(-730, -70), "East Exit Arrow Sign", RHT_SIGN_GERUDO_FORTRESS, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_EAST_EXIT_ARROW_SIGN)); +locationTable[RC_GF_HBA_RECTANGLE_SIGN] = Location::Sign(RC_GF_HBA_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(3635, -360), "HBA Rectangle Sign", RHT_SIGN_GERUDO_FORTRESS, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_HBA_RECTANGLE_SIGN)); +locationTable[RC_GF_GATE_EXIT_RECTANGLE_SIGN] = Location::Sign(RC_GF_GATE_EXIT_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(-1850, -3250), "Gate Exit Rectangle Sign", RHT_SIGN_GERUDO_FORTRESS, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_GATE_EXIT_RECTANGLE_SIGN)); +locationTable[RC_GF_GTG_ENTRANCE_RECTANGLE_SIGN] = Location::Sign(RC_GF_GTG_ENTRANCE_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(14, -1159), "GTG Entrance Rectangle Sign", RHT_SIGN_GERUDO_FORTRESS, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_GTG_ENTRANCE_RECTANGLE_SIGN)); +locationTable[RC_HW_CARPET_SALESMAN_ARROW_SIGN] = Location::Sign(RC_HW_CARPET_SALESMAN_ARROW_SIGN, RCQUEST_BOTH, RCAREA_WASTELAND, SCENE_HAUNTED_WASTELAND, TWO_ACTOR_PARAMS(2104, 2389), "Carpet Salesman Arrow Sign", RHT_SIGN_HAUNTED_WASTELAND, ACTOR_EN_A_OBJ, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HW_CARPET_SALESMAN_ARROW_SIGN)); +locationTable[RC_HW_POE_ALTAR] = Location::Sign(RC_HW_POE_ALTAR, RCQUEST_BOTH, RCAREA_WASTELAND, SCENE_HAUNTED_WASTELAND, TWO_ACTOR_PARAMS(645, -2234), "Poe Altar", RHT_SIGN_HAUNTED_WASTELAND, ACTOR_EN_WONDER_TALK2, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HW_POE_ALTAR)); +// Dungeon Signs +locationTable[RC_DODONGOS_CAVERN_TOP_FLOOR_PEDESTAL] = Location::Sign(RC_DODONGOS_CAVERN_TOP_FLOOR_PEDESTAL, RCQUEST_BOTH, RCAREA_DODONGOS_CAVERN, SCENE_DODONGOS_CAVERN, TWO_ACTOR_PARAMS(578, -929), "Top Floor Pedestal", RHT_SIGN_DODONGOS_CAVERN, ACTOR_EN_WONDER_TALK2, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DODONGOS_CAVERN_TOP_FLOOR_PEDESTAL)); +locationTable[RC_SHADOW_TEMPLE_TRUTHSPINNER_RECTANGLE_SIGN] = Location::Sign(RC_SHADOW_TEMPLE_TRUTHSPINNER_RECTANGLE_SIGN, RCQUEST_VANILLA, RCAREA_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE, TWO_ACTOR_PARAMS(910, -192), "Truthspinner Rectangle Sign", RHT_SIGN_SHADOW_TEMPLE, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SHADOW_TEMPLE_TRUTHSPINNER_RECTANGLE_SIGN)); +locationTable[RC_SHADOW_TEMPLE_FALLING_SPIKES_RECTANGLE_SIGN] = Location::Sign(RC_SHADOW_TEMPLE_FALLING_SPIKES_RECTANGLE_SIGN, RCQUEST_BOTH, RCAREA_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE, TWO_ACTOR_PARAMS(1132, 4103), "Falling Spikes Rectangle Sign", RHT_SIGN_SHADOW_TEMPLE, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SHADOW_TEMPLE_FALLING_SPIKES_RECTANGLE_SIGN)); +locationTable[RC_SPIRIT_TEMPLE_LEFT_SNAKE_STATUE] = Location::Sign(RC_SPIRIT_TEMPLE_LEFT_SNAKE_STATUE, RCQUEST_BOTH, RCAREA_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(-282, 312), "Left Snake Statue", RHT_SIGN_SPIRIT_TEMPLE, ACTOR_EN_WONDER_TALK2, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_TEMPLE_LEFT_SNAKE_STATUE)); +locationTable[RC_SPIRIT_TEMPLE_RIGHT_SNAKE_STATUE] = Location::Sign(RC_SPIRIT_TEMPLE_RIGHT_SNAKE_STATUE, RCQUEST_BOTH, RCAREA_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(302, 310), "Right Snake Statue", RHT_SIGN_SPIRIT_TEMPLE, ACTOR_EN_WONDER_TALK2, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_TEMPLE_RIGHT_SNAKE_STATUE)); +// MQ Dungeon Signs +locationTable[RC_SHADOW_TEMPLE_MQ_LOWER_PIT_RECTANGLE_SIGN] = Location::Sign(RC_SHADOW_TEMPLE_MQ_LOWER_PIT_RECTANGLE_SIGN, RCQUEST_MQ, RCAREA_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE, TWO_ACTOR_PARAMS(2893, 2705), "Lower Pit Rectangle Sign", RHT_SIGN_SHADOW_TEMPLE, ACTOR_EN_KANBAN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SHADOW_TEMPLE_MQ_LOWER_PIT_RECTANGLE_SIGN)); + + // clang-format on +} + +static RegisterShipInitFunc initFunc_ShuffleSigns(RegisterShuffleSigns, { "IS_RANDO" }); +static RegisterShipInitFunc registerSignLocations(Rando::StaticData::RegisterSignLocations); \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/ShuffleWonderItems.cpp b/soh/soh/Enhancements/randomizer/ShuffleWonderItems.cpp new file mode 100644 index 00000000000..b0fddc5026e --- /dev/null +++ b/soh/soh/Enhancements/randomizer/ShuffleWonderItems.cpp @@ -0,0 +1,524 @@ +#include +#include +#include "soh/ResourceManagerHelpers.h" +#include "soh/ObjectExtension/ObjectExtension.h" +#include "soh/ObjectExtension/ActorListIndex.h" +#include "item_category_adj.h" +#include "particle_cmc.h" + +extern "C" { +#include "overlays/actors/ovl_En_Wonder_Item/z_en_wonder_item.h" +#include "overlays/actors/ovl_En_Heishi1/z_en_heishi1.h" +extern PlayState* gPlayState; +extern Vec3f sTagPointsFree[9]; +extern Vec3f sTagPointsOrdered[9]; +} + +extern void EnItem00_DrawRandomizedItem(EnItem00* enItem00, PlayState* play); + +// Many wonder items spawn on top of each other, offset position to make them all distinct +static std::unordered_map sStackedWonderOffsets = { + { RC_COLOSSUS_WONDER_OASIS_TREE_1, { 8.0f, 0.0f, -8.0f } }, + { RC_COLOSSUS_WONDER_OASIS_TREE_2, { 5.0f, 0.0f, -8.0f } }, + + { RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_1, { 6.0f, 15.0f, 6.0f } }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_2, { 12.0f, 0.0f, 0.0f } }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_3, { -4.0f, -2.0f, 25.0f } }, + + { RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_1, { -6.0f, -14.0f, 2.0f } }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_2, { 5.0f, 0.0f, -10.0f } }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_3, { 4.0f, -2.0f, 5.0f } }, + + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_1, { 5.0f, 6.0f, 0.0f } }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_2, { 0.0f, 0.0f, 0.0f } }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_3, { 0.0f, 0.0f, 0.0f } }, + + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_1, { -2.0f, 0.0f, 15.0f } }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_2, { 1.0f, -11.0f, -5.0f } }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_3, { 0.0f, -12.0f, 0.0f } }, + + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_1, { -7.5f, 0.0f, -7.5f } }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_2, { -7.5f, 0.0f, 7.5f } }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_3, { 7.5f, 0.0f, 7.5f } }, + + { RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_1, { -7.5f, 0.0f, -7.5f } }, + { RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_2, { -7.5f, 0.0f, 7.5f } }, + { RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_3, { 7.5f, 0.0f, 7.5f } }, + + { RC_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_HOOKSHOT, { 0.0f, 0.0f, -8.0f } }, + { RC_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_BOW, { 0.0f, 0.0f, 8.0f } }, + + { RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_1, { -8.0f, 0.0f, 0.0f } }, + { RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_2, { 8.0f, 0.0f, 0.0f } }, + + { RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_1, { 0.0f, 0.0f, -8.0f } }, + { RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_2, { 0.0f, 0.0f, 8.0f } }, + + { RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_1, { -5.0f, 1.0f, -5.0f } }, + { RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_2, { 0.0f, -3.0f, -11.0f } }, + { RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_3, { 0.0f, -14.0f, -15.0f } }, + + { RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_1, { 0.0f, 9.0f, 10.0f } }, + { RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_2, { 0.0f, -7.0f, 10.0f } }, + { RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_3, { 0.0f, 0.0f, 10.0f } }, + + { RC_SPIRIT_TEMPLE_MQ_WONDER_CHEST_HAMMER, { 0.0f, 0.0f, -8.0f } }, + { RC_SPIRIT_TEMPLE_MQ_WONDER_CHEST_SLASH, { 0.0f, 0.0f, 8.0f } }, + + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_1, { -22.5f, 0.0f, 0.0f } }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_2, { -7.5f, 0.0f, 0.0f } }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_3, { 7.5f, 0.0f, 0.0f } }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_4, { 22.5f, 0.0f, 0.0f } }, + + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_1, { -22.5f, 0.0f, 0.0f } }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_2, { -7.5f, 0.0f, 0.0f } }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_3, { 7.5f, 0.0f, 0.0f } }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_4, { 22.5f, 0.0f, 0.0f } }, + + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_1, { -22.5f, 0.0f, 0.0f } }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_2, { -7.5f, 0.0f, 0.0f } }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_3, { 7.5f, 0.0f, 0.0f } }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_4, { 22.5f, 0.0f, 0.0f } }, +}; + +static Vec3f GetStackOffset(RandomizerCheck rc) { + auto it = sStackedWonderOffsets.find(rc); + return it != sStackedWonderOffsets.end() ? it->second : Vec3f{ 0.0f, 0.0f, 0.0f }; +} + +void SpawnNTSC10WonderItem() { + if (LINK_IS_ADULT && gPlayState->sceneNum == SCENE_ZORAS_FOUNTAIN) { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_WONDER_ITEM, -667, 320, 1053, 0, 0, 1, 4799); + } else if (LINK_IS_ADULT && gPlayState->sceneNum == SCENE_DEATH_MOUNTAIN_CRATER) { + Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_WONDER_ITEM, 6, 311, -640, 0, 0, 1, 4799); + } +} + +uint8_t EnWonderItem_RandomizerHoldsItem(EnWonderItem* wonderActor, PlayState* play) { + const CheckIdentity* wonderIdentity = ObjectExtension::GetInstance().Get(&wonderActor->actor); + if (wonderIdentity == nullptr) { + Actor* actor = &wonderActor->actor; + s16 actorIndex = GetActorListIndex(actor); + bool isDungeonScene = (play->sceneNum >= SCENE_DEKU_TREE && play->sceneNum <= SCENE_GERUDO_TRAINING_GROUND) || + play->sceneNum == SCENE_INSIDE_GANONS_CASTLE; + // For dungeons, use room Id and actor index. For overworld, use xz coordinates. + auto newIdentity = isDungeonScene ? OTRGlobals::Instance->gRandomizer->IdentifyWonderItem( + play->sceneNum, (s16)play->roomCtx.curRoom.num, actorIndex) + : OTRGlobals::Instance->gRandomizer->IdentifyWonderItem( + play->sceneNum, (s16)actor->world.pos.x, (s16)actor->world.pos.z); + + ObjectExtension::GetInstance().Set(actor, std::move(newIdentity)); + wonderIdentity = ObjectExtension::GetInstance().Get(actor); + if (wonderIdentity == nullptr) { + return false; + } + } + RandomizerCheck rc = wonderIdentity->randomizerCheck; + uint8_t isDungeon = Rando::StaticData::GetLocation(rc)->IsDungeon(); + auto wonderSetting = RAND_GET_OPTION(RSK_SHUFFLE_WONDER_ITEMS); + if (!IS_RANDO || (wonderSetting.Is(RO_SHUFFLE_WONDER_ITEMS_OVERWORLD) && isDungeon) || + (wonderSetting.Is(RO_SHUFFLE_WONDER_ITEMS_DUNGEONS) && !isDungeon) || + Flags_GetRandomizerInf(wonderIdentity->randomizerInf) || wonderIdentity->randomizerCheck == RC_UNKNOWN_CHECK) { + return false; + } else { + return true; + } +} + +static void EnWonderItem_RandomizerDraw(EnWonderItem* wonderActor, Color_RGBA8* primColor, Color_RGBA8* secColor, + Color_RGBA8* envColor, CheckIdentity* wonderIdentity) { + Vec3f pos; + static Vec3f velocity = { 0.0f, 0.0f, 0.0f }; + static Vec3f accel = { 0.0f, 0.0f, 0.0f }; + + velocity.y = -0.05f; + accel.y = -0.025f; + + // Draw particles at tag spots if applicable, otherwise at wonder item actor location + if (wonderActor->wonderMode == WONDERITEM_MULTITAG_ORDERED) { + for (s32 i = 0, mask = 1; i < wonderActor->numTagPoints; i++, mask <<= 1) { + if (!(wonderActor->tagFlags & mask)) { + pos.x = Rand_CenteredFloat(1.0f) + sTagPointsOrdered[i].x; + pos.y = (Rand_ZeroOne() * 25.0f) + sTagPointsOrdered[i].y + 10; + pos.z = Rand_CenteredFloat(1.0f) + sTagPointsOrdered[i].z; + EffectSsKiraKira_SpawnFocused(gPlayState, &pos, &velocity, &accel, secColor, envColor, 2000, 100); + EffectSsKiraKira_SpawnFocused(gPlayState, &pos, &velocity, &accel, primColor, envColor, 2000, 100); + } + } + } else if (wonderActor->wonderMode == WONDERITEM_MULTITAG_FREE) { + for (s32 i = 0, mask = 1; i < wonderActor->numTagPoints; i++, mask <<= 1) { + if (!(wonderActor->tagFlags & mask)) { + pos.x = Rand_CenteredFloat(1.0f) + sTagPointsFree[i].x; + pos.y = (Rand_ZeroOne() * 25.0f) + sTagPointsFree[i].y + 10; + pos.z = Rand_CenteredFloat(1.0f) + sTagPointsFree[i].z; + EffectSsKiraKira_SpawnFocused(gPlayState, &pos, &velocity, &accel, secColor, envColor, 2000, 100); + EffectSsKiraKira_SpawnFocused(gPlayState, &pos, &velocity, &accel, primColor, envColor, 2000, 100); + } + } + } else { + Vec3f offset = GetStackOffset(wonderIdentity->randomizerCheck); + pos.x = Rand_CenteredFloat(1.0f) + wonderActor->actor.world.pos.x + offset.x; + pos.y = (Rand_ZeroOne() * 25.0f) + wonderActor->actor.world.pos.y + 10 + offset.y; + pos.z = Rand_CenteredFloat(1.0f) + wonderActor->actor.world.pos.z + offset.z; + EffectSsKiraKira_SpawnFocused(gPlayState, &pos, &velocity, &accel, secColor, envColor, 2000, 100); + EffectSsKiraKira_SpawnFocused(gPlayState, &pos, &velocity, &accel, primColor, envColor, 2000, 100); + } +} + +void EnWonderItem_RandomizerDrawSetup(void* refActor) { + GetItemCategory getItemCategory; + EnWonderItem* wonderActor = static_cast(refActor); + + // If not a randomized item or too far, don't draw. + // If item is unshuffled or collected, kill wonder actor if switch flag is set. + if (!EnWonderItem_RandomizerHoldsItem(wonderActor, gPlayState)) { + if ((wonderActor->switchFlag >= 0) && Flags_GetSwitch(gPlayState, wonderActor->switchFlag)) { + Actor_Kill(&wonderActor->actor); + } + return; + } else if (wonderActor->actor.xzDistToPlayer > 1000.0f) { + return; + } + + bool cmc = CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeAndTextureMatchContents"), 0); + int requiresStoneAgony = CVarGetInteger(CVAR_ENHANCEMENT("ChestSizeDependsStoneOfAgony"), 0); + + int isNotCMC = !cmc || (requiresStoneAgony && !CHECK_QUEST_ITEM(QUEST_STONE_OF_AGONY)); + + Color_RGBA8 primColor; + Color_RGBA8 secColor; + Color_RGBA8 envColor; + + const auto wonderIdentity = ObjectExtension::GetInstance().Get(refActor); + if (wonderIdentity == nullptr) { + return; + } + + GetItemEntry wonderItem = + Rando::Context::GetInstance()->GetFinalGIEntry(wonderIdentity->randomizerCheck, true, GI_NONE); + getItemCategory = Randomizer_AdjustItemCategory(wonderItem); + + if (isNotCMC) { + getItemCategory = ITEM_CATEGORY_MAJOR; + } + primColor = Randomizer_GetParticleCMCColor(getItemCategory, COLOR_PRIMARY); + secColor = Randomizer_GetParticleCMCColor(getItemCategory, COLOR_SECONDARY); + envColor = Randomizer_GetParticleCMCColor(getItemCategory, COLOR_FLARE); + EnWonderItem_RandomizerDraw(wonderActor, &primColor, &secColor, &envColor, wonderIdentity); +} + +void EnWonderItem_RandomizerSpawnCollectible(EnWonderItem* wonderActor, PlayState* play) { + const auto wonderIdentity = ObjectExtension::GetInstance().Get(&wonderActor->actor); + EnItem00* item00; + Player* player = GET_PLAYER(gPlayState); + + // Hyrule courtyard left window handled separately to replace the bomb the guard throws + if (wonderIdentity == nullptr || wonderIdentity->randomizerCheck == RC_HC_WONDER_COURTYARD_LEFT_WINDOW) { + return; + } + // if activated via tag points, autocollect the check, otherwise spawn the item toward the player + // Water Temple MQ hookshot staircase can easily spawn unreachable items, autocollect + // Jabu MQ holes cow always spawns the item OOB (vanilla bug), autocollect + if (wonderActor->wonderMode == WONDERITEM_MULTITAG_FREE || wonderActor->wonderMode == WONDERITEM_PROXIMITY_DROP || + wonderActor->wonderMode == WONDERITEM_MULTITAG_ORDERED || + (wonderIdentity->randomizerCheck >= RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_1 && + wonderIdentity->randomizerCheck <= RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_3) || + wonderIdentity->randomizerCheck == RC_JABU_JABUS_BELLY_MQ_HOLES_COW) { + Flags_SetRandomizerInf(wonderIdentity->randomizerInf); + } else { + item00 = (EnItem00*)Item_DropCollectible(play, &wonderActor->actor.world.pos, ITEM00_SOH_DUMMY); + item00->randoInf = wonderIdentity->randomizerInf; + item00->itemEntry = + Rando::Context::GetInstance()->GetFinalGIEntry(wonderIdentity->randomizerCheck, true, GI_NONE); + item00->actor.draw = (ActorFunc)EnItem00_DrawRandomizedItem; + if (gPlayState->sceneNum != SCENE_INSIDE_GANONS_CASTLE) { + item00->actor.velocity.y = 3.0f; + item00->actor.speedXZ = 3.0f; + item00->actor.world.rot.y = + Math_Vec3f_Yaw(&item00->actor.world.pos, &player->actor.world.pos) + (s16)Rand_CenteredFloat(16384.0f); + } + } +} + +void WonderHeishi_RandomizerSpawnCollectible(PlayState* play, Vec3f pos, f32 rotY) { + EnItem00* item00; + Player* player = GET_PLAYER(gPlayState); + + item00 = (EnItem00*)Item_DropCollectible(play, &pos, ITEM00_SOH_DUMMY); + item00->randoInf = RAND_INF_HC_WONDER_COURTYARD_LEFT_WINDOW; + item00->itemEntry = + Rando::Context::GetInstance()->GetFinalGIEntry(RC_HC_WONDER_COURTYARD_LEFT_WINDOW, true, GI_NONE); + item00->actor.draw = (ActorFunc)EnItem00_DrawRandomizedItem; + item00->actor.velocity.y = Rand_CenteredFloat(5.0f) + 10.0f; + item00->actor.speedXZ = Rand_CenteredFloat(5.0f) + 10.0f; + item00->actor.world.rot.y = rotY; +} + +void RegisterShuffleWonderItems() { + bool shouldRegister = IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_WONDER_ITEMS); + bool isNtscUs10 = false; + for (uint32_t i = 0; i < ResourceMgr_GetNumGameVersions(); i++) { + if (ResourceMgr_GetGameVersion(i) == OOT_NTSC_US_10) { + isNtscUs10 = true; + } + } + bool shouldRegisterOverworld = RAND_GET_OPTION(RSK_SHUFFLE_WONDER_ITEMS).Is(RO_SHUFFLE_WONDER_ITEMS_ALL) || + RAND_GET_OPTION(RSK_SHUFFLE_WONDER_ITEMS).Is(RO_SHUFFLE_WONDER_ITEMS_OVERWORLD); + bool shouldRegisterNTSC10 = shouldRegister && isNtscUs10 && shouldRegisterOverworld; + + // Draw particle effect in wonder item spot to indicate a randomized item + COND_ID_HOOK(OnActorUpdate, ACTOR_EN_WONDER_ITEM, shouldRegister, EnWonderItem_RandomizerDrawSetup); + + // Spawn missing wonder items for NTSC 1.0 + COND_HOOK(OnSceneSpawnActors, shouldRegisterNTSC10, SpawnNTSC10WonderItem); + + // Prevent or delay actor kill until EnWonderItem_RandomizerDrawSetup in case item isn't yet collected + COND_VB_SHOULD(VB_WONDER_SPAWN, shouldRegister, { *should = false; }); + + // Do not spawn vanilla wonder item, instead spawn the randomized item + COND_VB_SHOULD(VB_WONDER_DROP_ITEM, shouldRegister, { + EnWonderItem* wonderActor = va_arg(args, EnWonderItem*); + if (EnWonderItem_RandomizerHoldsItem(wonderActor, gPlayState)) { + EnWonderItem_RandomizerSpawnCollectible(wonderActor, gPlayState); + *should = false; + } + }); + + // Do not spawn castle courtyard guard bomb, instead spawn the randomized item + COND_VB_SHOULD(VB_WONDER_HEISHI_ITEM, shouldRegisterOverworld, { + if (!Flags_GetRandomizerInf(RAND_INF_HC_WONDER_COURTYARD_LEFT_WINDOW)) { + Vec3f* pos = va_arg(args, Vec3f*); + f32 rotY = (f32)va_arg(args, double); + + WonderHeishi_RandomizerSpawnCollectible(gPlayState, *pos, rotY); + *should = false; + } + }); + + // If courtyard items are uncollected, keep guards patrolling + COND_VB_SHOULD(VB_WONDER_HEISHI_PATROLLING, shouldRegisterOverworld, { + EnHeishi1* guardActor = va_arg(args, EnHeishi1*); + if (!Flags_GetRandomizerInf(RAND_INF_HC_WONDER_COURTYARD_LEFT_WINDOW) || + !Flags_GetRandomizerInf(RAND_INF_HC_WONDER_COURTYARD_RIGHT_WINDOW)) { + *should = guardActor->type != 5; + } + }); +} + +void Rando::StaticData::RegisterWonderItemLocations() { + static bool registered = false; + if (registered) + return; + registered = true; + // clang-format off + // Overworld Wonder Items + // Randomizer Check Randomizer Check Quest Area Scene ID Params Short Name Hint Text Key Vanilla Spoiler Collection Check + locationTable[RC_KF_WONDER_TRAINING_1] = Location::WonderItem(RC_KF_WONDER_TRAINING_1, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-747, 951), "Wonder Training 1", RHT_WONDER_ITEM_KOKIRI_FOREST, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_WONDER_TRAINING_1)); + locationTable[RC_KF_WONDER_TRAINING_2] = Location::WonderItem(RC_KF_WONDER_TRAINING_2, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-677, 899), "Wonder Training 2", RHT_WONDER_ITEM_KOKIRI_FOREST, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_WONDER_TRAINING_2)); + locationTable[RC_KF_WONDER_TRAINING_3] = Location::WonderItem(RC_KF_WONDER_TRAINING_3, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-698, 830), "Wonder Training 3", RHT_WONDER_ITEM_KOKIRI_FOREST, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_WONDER_TRAINING_3)); + locationTable[RC_KF_WONDER_SHOP] = Location::WonderItem(RC_KF_WONDER_SHOP, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_SHOP, TWO_ACTOR_PARAMS(146, -97), "Wonder Shop", RHT_WONDER_ITEM_KOKIRI_FOREST, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_WONDER_SHOP)); + locationTable[RC_KF_WONDER_SIGN] = Location::WonderItem(RC_KF_WONDER_SIGN, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-488, 600), "Wonder Sign", RHT_WONDER_ITEM_KOKIRI_FOREST, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_WONDER_SIGN)); + locationTable[RC_KF_WONDER_PLATFORMS_1] = Location::WonderItem(RC_KF_WONDER_PLATFORMS_1, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(364, 28), "Wonder Platforms 1", RHT_WONDER_ITEM_KOKIRI_FOREST, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_WONDER_PLATFORMS_1)); + locationTable[RC_KF_WONDER_PLATFORMS_2] = Location::WonderItem(RC_KF_WONDER_PLATFORMS_2, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(1074, 178), "Wonder Platforms 2", RHT_WONDER_ITEM_KOKIRI_FOREST, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_WONDER_PLATFORMS_2)); + locationTable[RC_KF_WONDER_CRAWL_GRASS_1] = Location::WonderItem(RC_KF_WONDER_CRAWL_GRASS_1, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-590, 1635), "Wonder Crawl Grass 1", RHT_WONDER_ITEM_KOKIRI_FOREST, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_WONDER_CRAWL_GRASS_1)); + locationTable[RC_KF_WONDER_CRAWL_GRASS_2] = Location::WonderItem(RC_KF_WONDER_CRAWL_GRASS_2, RCQUEST_BOTH, RCAREA_KOKIRI_FOREST, SCENE_KOKIRI_FOREST, TWO_ACTOR_PARAMS(-584, 1762), "Wonder Crawl Grass 2", RHT_WONDER_ITEM_KOKIRI_FOREST, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KF_WONDER_CRAWL_GRASS_2)); + locationTable[RC_HF_WONDER_BRIDGE_1] = Location::WonderItem(RC_HF_WONDER_BRIDGE_1, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_HYRULE_FIELD, TWO_ACTOR_PARAMS(-99, 662), "Wonder Bridge Left", RHT_WONDER_ITEM_HYRULE_FIELD, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_WONDER_BRIDGE_1)); + locationTable[RC_HF_WONDER_BRIDGE_2] = Location::WonderItem(RC_HF_WONDER_BRIDGE_2, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_HYRULE_FIELD, TWO_ACTOR_PARAMS(-1, 665), "Wonder Bridge Center", RHT_WONDER_ITEM_HYRULE_FIELD, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_WONDER_BRIDGE_2)); + locationTable[RC_HF_WONDER_BRIDGE_3] = Location::WonderItem(RC_HF_WONDER_BRIDGE_3, RCQUEST_BOTH, RCAREA_HYRULE_FIELD, SCENE_HYRULE_FIELD, TWO_ACTOR_PARAMS(93, 659), "Wonder Bridge Right", RHT_WONDER_ITEM_HYRULE_FIELD, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HF_WONDER_BRIDGE_3)); + locationTable[RC_MKT_WONDER_DAY_1] = Location::WonderItem(RC_MKT_WONDER_DAY_1, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_DAY, TWO_ACTOR_PARAMS(-523, -340), "Wonder Day Balcony 1", RHT_WONDER_ITEM_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MKT_WONDER_DAY_1)); + locationTable[RC_MKT_WONDER_DAY_2] = Location::WonderItem(RC_MKT_WONDER_DAY_2, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_DAY, TWO_ACTOR_PARAMS(-523, -224), "Wonder Day Balcony 2", RHT_WONDER_ITEM_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MKT_WONDER_DAY_2)); + locationTable[RC_MKT_WONDER_DAY_3] = Location::WonderItem(RC_MKT_WONDER_DAY_3, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_DAY, TWO_ACTOR_PARAMS(-521, -113), "Wonder Day Balcony 3", RHT_WONDER_ITEM_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MKT_WONDER_DAY_3)); + locationTable[RC_MKT_WONDER_DAY_4] = Location::WonderItem(RC_MKT_WONDER_DAY_4, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_DAY, TWO_ACTOR_PARAMS(-521, -2), "Wonder Day Balcony 4", RHT_WONDER_ITEM_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MKT_WONDER_DAY_4)); + locationTable[RC_MKT_WONDER_DAY_5] = Location::WonderItem(RC_MKT_WONDER_DAY_5, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_DAY, TWO_ACTOR_PARAMS(-516, 117), "Wonder Day Balcony 5", RHT_WONDER_ITEM_MARKET, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MKT_WONDER_DAY_5)); + locationTable[RC_MKT_WONDER_NIGHT_1] = Location::WonderItem(RC_MKT_WONDER_NIGHT_1, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_NIGHT, TWO_ACTOR_PARAMS(-524, -246), "Wonder Night Balcony 1", RHT_WONDER_ITEM_MARKET, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MKT_WONDER_NIGHT_1)); + locationTable[RC_MKT_WONDER_NIGHT_2] = Location::WonderItem(RC_MKT_WONDER_NIGHT_2, RCQUEST_BOTH, RCAREA_MARKET, SCENE_MARKET_NIGHT, TWO_ACTOR_PARAMS(-521, 27), "Wonder Night Balcony 2", RHT_WONDER_ITEM_MARKET, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_MKT_WONDER_NIGHT_2)); + locationTable[RC_LLR_WONDER_BIG_FENCE] = Location::WonderItem(RC_LLR_WONDER_BIG_FENCE, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_RANCH, TWO_ACTOR_PARAMS(-443, -311), "Wonder Big Fence", RHT_WONDER_ITEM_LON_LON_RANCH, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_WONDER_BIG_FENCE)); + locationTable[RC_LLR_WONDER_SMALL_FENCE] = Location::WonderItem(RC_LLR_WONDER_SMALL_FENCE, RCQUEST_BOTH, RCAREA_LON_LON_RANCH, SCENE_LON_LON_RANCH, TWO_ACTOR_PARAMS(295, -979), "Wonder Small Fence", RHT_WONDER_ITEM_LON_LON_RANCH, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LLR_WONDER_SMALL_FENCE)); + locationTable[RC_HC_WONDER_LEFT_TORCH] = Location::WonderItem(RC_HC_WONDER_LEFT_TORCH, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(-502, 468), "Wonder Left Torch", RHT_WONDER_ITEM_HYRULE_CASTLE, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_WONDER_LEFT_TORCH)); + locationTable[RC_HC_WONDER_RIGHT_TORCH] = Location::WonderItem(RC_HC_WONDER_RIGHT_TORCH, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(-72, 468), "Wonder Right Torch", RHT_WONDER_ITEM_HYRULE_CASTLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_WONDER_RIGHT_TORCH)); + locationTable[RC_HC_WONDER_MOAT_1] = Location::WonderItem(RC_HC_WONDER_MOAT_1, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(706, -663), "Wonder Moat Past Boxes", RHT_WONDER_ITEM_HYRULE_CASTLE, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_WONDER_MOAT_1)); + locationTable[RC_HC_WONDER_MOAT_2] = Location::WonderItem(RC_HC_WONDER_MOAT_2, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(755, -118), "Wonder Moat Past Gate 1", RHT_WONDER_ITEM_HYRULE_CASTLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_WONDER_MOAT_2)); + locationTable[RC_HC_WONDER_MOAT_3] = Location::WonderItem(RC_HC_WONDER_MOAT_3, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(725, 112), "Wonder Moat Past Gate 2", RHT_WONDER_ITEM_HYRULE_CASTLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_WONDER_MOAT_3)); + locationTable[RC_HC_WONDER_MOAT_4] = Location::WonderItem(RC_HC_WONDER_MOAT_4, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(754, 395), "Wonder Moat Before Gate 1", RHT_WONDER_ITEM_HYRULE_CASTLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_WONDER_MOAT_4)); + locationTable[RC_HC_WONDER_MOAT_5] = Location::WonderItem(RC_HC_WONDER_MOAT_5, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(403, 587), "Wonder Moat Before Gate 2", RHT_WONDER_ITEM_HYRULE_CASTLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_WONDER_MOAT_5)); + locationTable[RC_HC_WONDER_MOAT_6] = Location::WonderItem(RC_HC_WONDER_MOAT_6, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(62, 555), "Wonder Moat Before Gate 3", RHT_WONDER_ITEM_HYRULE_CASTLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_WONDER_MOAT_6)); + locationTable[RC_HC_WONDER_MOAT_7] = Location::WonderItem(RC_HC_WONDER_MOAT_7, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(-256, 598), "Wonder Moat Before Gate 4", RHT_WONDER_ITEM_HYRULE_CASTLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_WONDER_MOAT_7)); + locationTable[RC_HC_WONDER_MOAT_8] = Location::WonderItem(RC_HC_WONDER_MOAT_8, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(-557, 568), "Wonder Moat Before Gate 5", RHT_WONDER_ITEM_HYRULE_CASTLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_WONDER_MOAT_8)); + locationTable[RC_HC_WONDER_MOAT_9] = Location::WonderItem(RC_HC_WONDER_MOAT_9, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(-819, 613), "Wonder Moat Before Gate 6", RHT_WONDER_ITEM_HYRULE_CASTLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_WONDER_MOAT_9)); + locationTable[RC_HC_WONDER_MOAT_10] = Location::WonderItem(RC_HC_WONDER_MOAT_10, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(-1190, 561), "Wonder Moat Before Gate 7", RHT_WONDER_ITEM_HYRULE_CASTLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_WONDER_MOAT_10)); + locationTable[RC_HC_WONDER_COURTYARD_RIGHT_WINDOW] = Location::WonderItem(RC_HC_WONDER_COURTYARD_RIGHT_WINDOW, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_CASTLE_COURTYARD_ZELDA, TWO_ACTOR_PARAMS(0, -481), "Wonder Courtyard Right Window", RHT_WONDER_ITEM_CASTLE_COURTYARD_ZELDA, RG_PURPLE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_WONDER_COURTYARD_RIGHT_WINDOW)); + locationTable[RC_HC_WONDER_COURTYARD_LEFT_WINDOW] = Location::WonderItem(RC_HC_WONDER_COURTYARD_LEFT_WINDOW, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_CASTLE_COURTYARD_ZELDA, TWO_ACTOR_PARAMS(0, 476), "Wonder Courtyard Left Window", RHT_WONDER_ITEM_CASTLE_COURTYARD_ZELDA, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_WONDER_COURTYARD_LEFT_WINDOW)); + locationTable[RC_LW_WONDER_BACK_SKULL_KIDS_GRASS_1] = Location::WonderItem(RC_LW_WONDER_BACK_SKULL_KIDS_GRASS_1, RCQUEST_BOTH, RCAREA_LOST_WOODS, SCENE_LOST_WOODS, TWO_ACTOR_PARAMS(1371, 497), "Wonder Back Skull Kids Grass 1", RHT_WONDER_ITEM_LOST_WOODS, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LW_WONDER_BACK_SKULL_KIDS_GRASS_1)); + locationTable[RC_LW_WONDER_BACK_SKULL_KIDS_GRASS_2] = Location::WonderItem(RC_LW_WONDER_BACK_SKULL_KIDS_GRASS_2, RCQUEST_BOTH, RCAREA_LOST_WOODS, SCENE_LOST_WOODS, TWO_ACTOR_PARAMS(1038, 557), "Wonder Back Skull Kids Grass 2", RHT_WONDER_ITEM_LOST_WOODS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LW_WONDER_BACK_SKULL_KIDS_GRASS_2)); + locationTable[RC_LW_WONDER_FRONT_SKULL_KIDS_GRASS] = Location::WonderItem(RC_LW_WONDER_FRONT_SKULL_KIDS_GRASS, RCQUEST_BOTH, RCAREA_LOST_WOODS, SCENE_LOST_WOODS, TWO_ACTOR_PARAMS(1256, 194), "Wonder Front Skull Kids Grass", RHT_WONDER_ITEM_LOST_WOODS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LW_WONDER_FRONT_SKULL_KIDS_GRASS)); + locationTable[RC_SFM_WONDER_ENTRANCE] = Location::WonderItem(RC_SFM_WONDER_ENTRANCE, RCQUEST_BOTH, RCAREA_SACRED_FOREST_MEADOW, SCENE_SACRED_FOREST_MEADOW, TWO_ACTOR_PARAMS(-351, 2121), "Wonder Entrance Grass", RHT_WONDER_ITEM_SACRED_FOREST_MEADOW, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SFM_WONDER_ENTRANCE)); + locationTable[RC_SFM_WONDER_MAZE_1] = Location::WonderItem(RC_SFM_WONDER_MAZE_1, RCQUEST_BOTH, RCAREA_SACRED_FOREST_MEADOW, SCENE_SACRED_FOREST_MEADOW, TWO_ACTOR_PARAMS(100, 1375), "Wonder First Row Grass", RHT_WONDER_ITEM_SACRED_FOREST_MEADOW, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SFM_WONDER_MAZE_1)); + locationTable[RC_SFM_WONDER_MAZE_2] = Location::WonderItem(RC_SFM_WONDER_MAZE_2, RCQUEST_BOTH, RCAREA_SACRED_FOREST_MEADOW, SCENE_SACRED_FOREST_MEADOW, TWO_ACTOR_PARAMS(100, 1084), "Wonder Second Row Grass 1", RHT_WONDER_ITEM_SACRED_FOREST_MEADOW, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SFM_WONDER_MAZE_2)); + locationTable[RC_SFM_WONDER_MAZE_3] = Location::WonderItem(RC_SFM_WONDER_MAZE_3, RCQUEST_BOTH, RCAREA_SACRED_FOREST_MEADOW, SCENE_SACRED_FOREST_MEADOW, TWO_ACTOR_PARAMS(-204, 1283), "Wonder Second Row Grass 2", RHT_WONDER_ITEM_SACRED_FOREST_MEADOW, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SFM_WONDER_MAZE_3)); + locationTable[RC_SFM_WONDER_MAZE_4] = Location::WonderItem(RC_SFM_WONDER_MAZE_4, RCQUEST_BOTH, RCAREA_SACRED_FOREST_MEADOW, SCENE_SACRED_FOREST_MEADOW, TWO_ACTOR_PARAMS(-199, 177), "Wonder End of Maze Grass 1", RHT_WONDER_ITEM_SACRED_FOREST_MEADOW, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SFM_WONDER_MAZE_4)); + locationTable[RC_SFM_WONDER_MAZE_5] = Location::WonderItem(RC_SFM_WONDER_MAZE_5, RCQUEST_BOTH, RCAREA_SACRED_FOREST_MEADOW, SCENE_SACRED_FOREST_MEADOW, TWO_ACTOR_PARAMS(299, 285), "Wonder End of Maze Grass 2", RHT_WONDER_ITEM_SACRED_FOREST_MEADOW, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SFM_WONDER_MAZE_5)); + locationTable[RC_KAK_WONDER_UNDER_CONSTRUCTION] = Location::WonderItem(RC_KAK_WONDER_UNDER_CONSTRUCTION, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_KAKARIKO_VILLAGE, TWO_ACTOR_PARAMS(148, 1165), "Wonder Under Construction", RHT_WONDER_ITEM_KAKARIKO_VILLAGE, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_WONDER_UNDER_CONSTRUCTION)); + locationTable[RC_KAK_WONDER_ABOVE_COW] = Location::WonderItem(RC_KAK_WONDER_ABOVE_COW, RCQUEST_BOTH, RCAREA_KAKARIKO_VILLAGE, SCENE_IMPAS_HOUSE, TWO_ACTOR_PARAMS(153, -266), "Wonder Above Impa's Cow", RHT_WONDER_ITEM_KAKARIKO_VILLAGE, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_KAK_WONDER_ABOVE_COW)); + locationTable[RC_GY_WONDER_DAMPE_RACE_1] = Location::WonderItem(RC_GY_WONDER_DAMPE_RACE_1, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(391, -2681), "Wonder Dampe's Grave 1", RHT_WONDER_ITEM_DAMPES_GRAVE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_WONDER_DAMPE_RACE_1)); + locationTable[RC_GY_WONDER_DAMPE_RACE_2] = Location::WonderItem(RC_GY_WONDER_DAMPE_RACE_2, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(394, -2588), "Wonder Dampe's Grave 2", RHT_WONDER_ITEM_DAMPES_GRAVE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_WONDER_DAMPE_RACE_2)); + locationTable[RC_GY_WONDER_DAMPE_RACE_3] = Location::WonderItem(RC_GY_WONDER_DAMPE_RACE_3, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(475, -2591), "Wonder Dampe's Grave 3", RHT_WONDER_ITEM_DAMPES_GRAVE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_WONDER_DAMPE_RACE_3)); + locationTable[RC_GY_WONDER_DAMPE_RACE_4] = Location::WonderItem(RC_GY_WONDER_DAMPE_RACE_4, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(562, -2588), "Wonder Dampe's Grave 4", RHT_WONDER_ITEM_DAMPES_GRAVE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_WONDER_DAMPE_RACE_4)); + locationTable[RC_GY_WONDER_DAMPE_RACE_5] = Location::WonderItem(RC_GY_WONDER_DAMPE_RACE_5, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(621, -3219), "Wonder Dampe's Grave 5", RHT_WONDER_ITEM_DAMPES_GRAVE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_WONDER_DAMPE_RACE_5)); + locationTable[RC_GY_WONDER_DAMPE_RACE_6] = Location::WonderItem(RC_GY_WONDER_DAMPE_RACE_6, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(570, -5778), "Wonder Dampe's Grave 6", RHT_WONDER_ITEM_DAMPES_GRAVE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_WONDER_DAMPE_RACE_6)); + locationTable[RC_GY_WONDER_DAMPE_RACE_7] = Location::WonderItem(RC_GY_WONDER_DAMPE_RACE_7, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(353, -5825), "Wonder Dampe's Grave 7", RHT_WONDER_ITEM_DAMPES_GRAVE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_WONDER_DAMPE_RACE_7)); + locationTable[RC_GY_WONDER_DAMPE_RACE_8] = Location::WonderItem(RC_GY_WONDER_DAMPE_RACE_8, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(1666, -5736), "Wonder Dampe's Grave 8", RHT_WONDER_ITEM_DAMPES_GRAVE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_WONDER_DAMPE_RACE_8)); + locationTable[RC_GY_WONDER_DAMPE_RACE_9] = Location::WonderItem(RC_GY_WONDER_DAMPE_RACE_9, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(2549, -4598), "Wonder Dampe's Grave 9", RHT_WONDER_ITEM_DAMPES_GRAVE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_WONDER_DAMPE_RACE_9)); + locationTable[RC_GY_WONDER_DAMPE_RACE_10] = Location::WonderItem(RC_GY_WONDER_DAMPE_RACE_10, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(2547, -4847), "Wonder Dampe's Grave 10", RHT_WONDER_ITEM_DAMPES_GRAVE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_WONDER_DAMPE_RACE_10)); + locationTable[RC_GY_WONDER_DAMPE_RACE_11] = Location::WonderItem(RC_GY_WONDER_DAMPE_RACE_11, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(2726, -3634), "Wonder Dampe's Grave 11", RHT_WONDER_ITEM_DAMPES_GRAVE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_WONDER_DAMPE_RACE_11)); + locationTable[RC_GY_WONDER_DAMPE_RACE_12] = Location::WonderItem(RC_GY_WONDER_DAMPE_RACE_12, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(2728, -3500), "Wonder Dampe's Grave 12", RHT_WONDER_ITEM_DAMPES_GRAVE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_WONDER_DAMPE_RACE_12)); + locationTable[RC_GY_WONDER_DAMPE_RACE_13] = Location::WonderItem(RC_GY_WONDER_DAMPE_RACE_13, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(2633, -3394), "Wonder Dampe's Grave 13", RHT_WONDER_ITEM_DAMPES_GRAVE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_WONDER_DAMPE_RACE_13)); + locationTable[RC_GY_WONDER_DAMPE_RACE_14] = Location::WonderItem(RC_GY_WONDER_DAMPE_RACE_14, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(2377, -3398), "Wonder Dampe's Grave 14", RHT_WONDER_ITEM_DAMPES_GRAVE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_WONDER_DAMPE_RACE_14)); + locationTable[RC_GY_WONDER_DAMPE_RACE_15] = Location::WonderItem(RC_GY_WONDER_DAMPE_RACE_15, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_WINDMILL_AND_DAMPES_GRAVE, TWO_ACTOR_PARAMS(1308, -3125), "Wonder Dampe's Grave 15", RHT_WONDER_ITEM_DAMPES_GRAVE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_WONDER_DAMPE_RACE_15)); + locationTable[RC_DMC_WONDER_BENEATH_BRIDGE_PLATFORM] = Location::WonderItem(RC_DMC_WONDER_BENEATH_BRIDGE_PLATFORM, RCQUEST_BOTH, RCAREA_DEATH_MOUNTAIN_CRATER, SCENE_DEATH_MOUNTAIN_CRATER, TWO_ACTOR_PARAMS(6, -640), "Wonder Beneath Bridge Platform", RHT_WONDER_ITEM_DEATH_MOUNTAIN_CRATER, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DMC_WONDER_BENEATH_BRIDGE_PLATFORM)); + locationTable[RC_ZR_WONDER_NEAR_DOMAIN_1] = Location::WonderItem(RC_ZR_WONDER_NEAR_DOMAIN_1, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(3329, -1499), "Wonder Near Domain 1", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_NEAR_DOMAIN_1)); + locationTable[RC_ZR_WONDER_NEAR_DOMAIN_2] = Location::WonderItem(RC_ZR_WONDER_NEAR_DOMAIN_2, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(3161, -1493), "Wonder Near Domain 2", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_NEAR_DOMAIN_2)); + locationTable[RC_ZR_WONDER_NEAR_DOMAIN_3] = Location::WonderItem(RC_ZR_WONDER_NEAR_DOMAIN_3, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(2987, -1499), "Wonder Near Domain 3", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_NEAR_DOMAIN_3)); + locationTable[RC_ZR_WONDER_NEAR_DOMAIN_4] = Location::WonderItem(RC_ZR_WONDER_NEAR_DOMAIN_4, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(2831, -1421), "Wonder Near Domain 4", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_NEAR_DOMAIN_4)); + locationTable[RC_ZR_WONDER_BEFORE_LADDER_1] = Location::WonderItem(RC_ZR_WONDER_BEFORE_LADDER_1, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(1287, -363), "Wonder Before Ladder 1", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_BEFORE_LADDER_1)); + locationTable[RC_ZR_WONDER_BEFORE_LADDER_2] = Location::WonderItem(RC_ZR_WONDER_BEFORE_LADDER_2, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(1144, -296), "Wonder Before Ladder 2", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_BEFORE_LADDER_2)); + locationTable[RC_ZR_WONDER_BEFORE_LADDER_3] = Location::WonderItem(RC_ZR_WONDER_BEFORE_LADDER_3, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(1021, -224), "Wonder Before Ladder 3", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_BEFORE_LADDER_3)); + locationTable[RC_ZR_WONDER_BEFORE_LADDER_4] = Location::WonderItem(RC_ZR_WONDER_BEFORE_LADDER_4, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(902, -150), "Wonder Before Ladder 4", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_BEFORE_LADDER_4)); + locationTable[RC_ZR_WONDER_BEFORE_LADDER_5] = Location::WonderItem(RC_ZR_WONDER_BEFORE_LADDER_5, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(786, -74), "Wonder Before Ladder 5", RHT_WONDER_ITEM_ZORAS_RIVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_BEFORE_LADDER_5)); + locationTable[RC_ZR_WONDER_BEFORE_LADDER_6] = Location::WonderItem(RC_ZR_WONDER_BEFORE_LADDER_6, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(670, 3), "Wonder Before Ladder 6", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_BEFORE_LADDER_6)); + locationTable[RC_ZR_WONDER_AFTER_LADDER_1] = Location::WonderItem(RC_ZR_WONDER_AFTER_LADDER_1, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(520, 12), "Wonder After Ladder 1", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_AFTER_LADDER_1)); + locationTable[RC_ZR_WONDER_AFTER_LADDER_2] = Location::WonderItem(RC_ZR_WONDER_AFTER_LADDER_2, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(353, 9), "Wonder After Ladder 2", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_AFTER_LADDER_2)); + locationTable[RC_ZR_WONDER_AFTER_LADDER_3] = Location::WonderItem(RC_ZR_WONDER_AFTER_LADDER_3, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(158, 6), "Wonder After Ladder 3", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_AFTER_LADDER_3)); + locationTable[RC_ZR_WONDER_FROG_BRIDGE_1] = Location::WonderItem(RC_ZR_WONDER_FROG_BRIDGE_1, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(1204, -608), "Wonder Frog Bridge 1", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_FROG_BRIDGE_1)); + locationTable[RC_ZR_WONDER_FROG_BRIDGE_2] = Location::WonderItem(RC_ZR_WONDER_FROG_BRIDGE_2, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(1124, -803), "Wonder Frog Bridge 2", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_FROG_BRIDGE_2)); + locationTable[RC_ZR_WONDER_FROG_BRIDGE_3] = Location::WonderItem(RC_ZR_WONDER_FROG_BRIDGE_3, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(1020, -973), "Wonder Frog Bridge 3", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_FROG_BRIDGE_3)); + locationTable[RC_ZR_WONDER_PILLARS_1] = Location::WonderItem(RC_ZR_WONDER_PILLARS_1, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(509, -1223), "Wonder Pillars 1", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_PILLARS_1)); + locationTable[RC_ZR_WONDER_PILLARS_2] = Location::WonderItem(RC_ZR_WONDER_PILLARS_2, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(269, -1018), "Wonder Pillars 2", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_PILLARS_2)); + locationTable[RC_ZR_WONDER_PILLARS_3] = Location::WonderItem(RC_ZR_WONDER_PILLARS_3, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(9, -1158), "Wonder Pillars 3", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_PILLARS_3)); + locationTable[RC_ZR_WONDER_PILLARS_4] = Location::WonderItem(RC_ZR_WONDER_PILLARS_4, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(-145, -938), "Wonder Pillars 4", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_PILLARS_4)); + locationTable[RC_ZR_WONDER_LOWER_LAND_BRIDGE_1] = Location::WonderItem(RC_ZR_WONDER_LOWER_LAND_BRIDGE_1, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(-147, -736), "Wonder Lower Land Bridge 1", RHT_WONDER_ITEM_ZORAS_RIVER, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_LOWER_LAND_BRIDGE_1)); + locationTable[RC_ZR_WONDER_LOWER_LAND_BRIDGE_2] = Location::WonderItem(RC_ZR_WONDER_LOWER_LAND_BRIDGE_2, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(-141, -588), "Wonder Lower Land Bridge 2", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_LOWER_LAND_BRIDGE_2)); + locationTable[RC_ZR_WONDER_LOWER_LAND_BRIDGE_3] = Location::WonderItem(RC_ZR_WONDER_LOWER_LAND_BRIDGE_3, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(-142, -383), "Wonder Lower Land Bridge 3", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_LOWER_LAND_BRIDGE_3)); + locationTable[RC_ZR_WONDER_LOWER_LAND_BRIDGE_4] = Location::WonderItem(RC_ZR_WONDER_LOWER_LAND_BRIDGE_4, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(-141, -218), "Wonder Lower Land Bridge 4", RHT_WONDER_ITEM_ZORAS_RIVER, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_LOWER_LAND_BRIDGE_4)); + locationTable[RC_ZR_WONDER_NEAR_CUCCO_1] = Location::WonderItem(RC_ZR_WONDER_NEAR_CUCCO_1, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(-860, -38), "Wonder Near Cucco 1", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_NEAR_CUCCO_1)); + locationTable[RC_ZR_WONDER_NEAR_CUCCO_2] = Location::WonderItem(RC_ZR_WONDER_NEAR_CUCCO_2, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(-1063, -4), "Wonder Near Cucco 2", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_NEAR_CUCCO_2)); + locationTable[RC_ZR_WONDER_NEAR_CUCCO_3] = Location::WonderItem(RC_ZR_WONDER_NEAR_CUCCO_3, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(-1296, 242), "Wonder Near Cucco 3", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_NEAR_CUCCO_3)); + locationTable[RC_ZR_WONDER_LOWER_RIVER_1] = Location::WonderItem(RC_ZR_WONDER_LOWER_RIVER_1, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(-1287, 457), "Wonder Lower River 1", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_LOWER_RIVER_1)); + locationTable[RC_ZR_WONDER_LOWER_RIVER_2] = Location::WonderItem(RC_ZR_WONDER_LOWER_RIVER_2, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(-1288, 668), "Wonder Lower River 2", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_LOWER_RIVER_2)); + locationTable[RC_ZR_WONDER_LOWER_RIVER_3] = Location::WonderItem(RC_ZR_WONDER_LOWER_RIVER_3, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(-1290, 862), "Wonder Lower River 3", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_LOWER_RIVER_3)); + locationTable[RC_ZR_WONDER_LOWER_RIVER_4] = Location::WonderItem(RC_ZR_WONDER_LOWER_RIVER_4, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(-1288, 1052), "Wonder Lower River 4", RHT_WONDER_ITEM_ZORAS_RIVER, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WONDER_LOWER_RIVER_4)); + locationTable[RC_ZF_WONDER_ROCK] = Location::WonderItem(RC_ZF_WONDER_ROCK, RCQUEST_BOTH, RCAREA_ZORAS_FOUNTAIN, SCENE_ZORAS_FOUNTAIN, TWO_ACTOR_PARAMS(-667, 1053), "Wonder Scarecrow Rock", RHT_WONDER_ITEM_ZORAS_FOUNTAIN, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZF_WONDER_ROCK)); + locationTable[RC_GV_WONDER_LOWER_WATERFALL] = Location::WonderItem(RC_GV_WONDER_LOWER_WATERFALL, RCQUEST_BOTH, RCAREA_GERUDO_VALLEY, SCENE_GERUDO_VALLEY, TWO_ACTOR_PARAMS(-45, -298), "Wonder Lower Waterfall", RHT_WONDER_ITEM_GERUDO_VALLEY, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GV_WONDER_LOWER_WATERFALL)); + locationTable[RC_GV_WONDER_UPPER_WATERFALL] = Location::WonderItem(RC_GV_WONDER_UPPER_WATERFALL, RCQUEST_BOTH, RCAREA_GERUDO_VALLEY, SCENE_GERUDO_VALLEY, TWO_ACTOR_PARAMS(47, -2620), "Wonder Upper Waterfall", RHT_WONDER_ITEM_GERUDO_VALLEY, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GV_WONDER_UPPER_WATERFALL)); + locationTable[RC_GF_WONDER_ENTRANCE_SIGN] = Location::WonderItem(RC_GF_WONDER_ENTRANCE_SIGN, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(-814, -810), "Wonder Entrance Sign", RHT_WONDER_ITEM_GERUDOS_FORTRESS, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_WONDER_ENTRANCE_SIGN)); + locationTable[RC_GF_WONDER_ARCHERY_SIGN] = Location::WonderItem(RC_GF_WONDER_ARCHERY_SIGN, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_GERUDOS_FORTRESS, TWO_ACTOR_PARAMS(1094, -412), "Wonder Archery Sign", RHT_WONDER_ITEM_GERUDOS_FORTRESS, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GF_WONDER_ARCHERY_SIGN)); + locationTable[RC_TH_WONDER_1_TORCH_1] = Location::WonderItem(RC_TH_WONDER_1_TORCH_1, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(-305, -2633), "Wonder 1 Torch Outer Exit Skull", RHT_WONDER_ITEM_THIEVES_HIDEOUT, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_TH_WONDER_1_TORCH_1)); + locationTable[RC_TH_WONDER_1_TORCH_2] = Location::WonderItem(RC_TH_WONDER_1_TORCH_2, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(-307, -2200), "Wonder 1 Torch Inner Exit Skull", RHT_WONDER_ITEM_THIEVES_HIDEOUT, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_TH_WONDER_1_TORCH_2)); + locationTable[RC_TH_WONDER_STEEP_SLOPE_LOWER_EXIT] = Location::WonderItem(RC_TH_WONDER_STEEP_SLOPE_LOWER_EXIT, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(71, -928), "Wonder Steep Slope Lower Exit Skull", RHT_WONDER_ITEM_THIEVES_HIDEOUT, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_TH_WONDER_STEEP_SLOPE_LOWER_EXIT)); + locationTable[RC_TH_WONDER_STEEP_SLOPE_UPPER_EXIT] = Location::WonderItem(RC_TH_WONDER_STEEP_SLOPE_UPPER_EXIT, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(77, -1345), "Wonder Steep Slope Upper Exit Skull", RHT_WONDER_ITEM_THIEVES_HIDEOUT, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_TH_WONDER_STEEP_SLOPE_UPPER_EXIT)); + locationTable[RC_TH_WONDER_DOUBLE_JAIL_LOWER_EXIT] = Location::WonderItem(RC_TH_WONDER_DOUBLE_JAIL_LOWER_EXIT, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(-65, 173), "Wonder Double Jail Lower Exit Skull", RHT_WONDER_ITEM_THIEVES_HIDEOUT, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_TH_WONDER_DOUBLE_JAIL_LOWER_EXIT)); + locationTable[RC_TH_WONDER_DOUBLE_JAIL_UPPER_EXIT] = Location::WonderItem(RC_TH_WONDER_DOUBLE_JAIL_UPPER_EXIT, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(709, 180), "Wonder Double Jail Upper Exit Skull", RHT_WONDER_ITEM_THIEVES_HIDEOUT, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_TH_WONDER_DOUBLE_JAIL_UPPER_EXIT)); + locationTable[RC_TH_WONDER_KITCHEN_SKULL] = Location::WonderItem(RC_TH_WONDER_KITCHEN_SKULL, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(1204, -610), "Wonder Kitchen Skull", RHT_WONDER_ITEM_THIEVES_HIDEOUT, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_TH_WONDER_KITCHEN_SKULL)); + locationTable[RC_TH_WONDER_KITCHEN_SOUP] = Location::WonderItem(RC_TH_WONDER_KITCHEN_SOUP, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(1940, -585), "Wonder Kitchen Soup", RHT_WONDER_ITEM_THIEVES_HIDEOUT, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_TH_WONDER_KITCHEN_SOUP)); + locationTable[RC_TH_WONDER_DEAD_END_SKULL_ENTRANCE] = Location::WonderItem(RC_TH_WONDER_DEAD_END_SKULL_ENTRANCE, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(1666, -2323), "Wonder Dead End Entrance Skull", RHT_WONDER_ITEM_THIEVES_HIDEOUT, RG_ARROWS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_TH_WONDER_DEAD_END_SKULL_ENTRANCE)); + locationTable[RC_TH_WONDER_DEAD_END_SKULL_NEAR_JAIL] = Location::WonderItem(RC_TH_WONDER_DEAD_END_SKULL_NEAR_JAIL, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(1378, -2829), "Wonder Dead End Near Jail Skull", RHT_WONDER_ITEM_THIEVES_HIDEOUT, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_TH_WONDER_DEAD_END_SKULL_NEAR_JAIL)); + locationTable[RC_TH_WONDER_BREAK_ROOM_BOTTOM_SKULL] = Location::WonderItem(RC_TH_WONDER_BREAK_ROOM_BOTTOM_SKULL, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(820, -3511), "Wonder Break Room Bottom Skull", RHT_WONDER_ITEM_THIEVES_HIDEOUT, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_TH_WONDER_BREAK_ROOM_BOTTOM_SKULL)); + locationTable[RC_TH_WONDER_BREAK_ROOM_TOP_SKULL] = Location::WonderItem(RC_TH_WONDER_BREAK_ROOM_TOP_SKULL, RCQUEST_BOTH, RCAREA_GERUDO_FORTRESS, SCENE_THIEVES_HIDEOUT, TWO_ACTOR_PARAMS(38, -3509), "Wonder Break Room Top Skull", RHT_WONDER_ITEM_THIEVES_HIDEOUT, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_TH_WONDER_BREAK_ROOM_TOP_SKULL)); + locationTable[RC_COLOSSUS_WONDER_OASIS_TREE_1] = Location::WonderItem(RC_COLOSSUS_WONDER_OASIS_TREE_1, RCQUEST_BOTH, RCAREA_DESERT_COLOSSUS, SCENE_DESERT_COLOSSUS, TWO_ACTOR_PARAMS(1161, 2383), "Wonder Oasis Tree 1", RHT_WONDER_ITEM_DESERT_COLOSSUS, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_COLOSSUS_WONDER_OASIS_TREE_1)); + locationTable[RC_COLOSSUS_WONDER_OASIS_TREE_2] = Location::WonderItem(RC_COLOSSUS_WONDER_OASIS_TREE_2, RCQUEST_BOTH, RCAREA_DESERT_COLOSSUS, SCENE_DESERT_COLOSSUS, TWO_ACTOR_PARAMS(1113, 2581), "Wonder Oasis Tree 2", RHT_WONDER_ITEM_DESERT_COLOSSUS, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_COLOSSUS_WONDER_OASIS_TREE_2)); + locationTable[RC_COLOSSUS_WONDER_OASIS_CHILD_TREE] = Location::WonderItem(RC_COLOSSUS_WONDER_OASIS_CHILD_TREE, RCQUEST_BOTH, RCAREA_DESERT_COLOSSUS, SCENE_DESERT_COLOSSUS, TWO_ACTOR_PARAMS(456, 2325), "Wonder Oasis Child Tree", RHT_WONDER_ITEM_DESERT_COLOSSUS, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_COLOSSUS_WONDER_OASIS_CHILD_TREE)); + locationTable[RC_COLOSSUS_WONDER_GF_TREE_1] = Location::WonderItem(RC_COLOSSUS_WONDER_GF_TREE_1, RCQUEST_BOTH, RCAREA_DESERT_COLOSSUS, SCENE_DESERT_COLOSSUS, TWO_ACTOR_PARAMS(2406, -1407), "Wonder Great Fairy Tree 1", RHT_WONDER_ITEM_DESERT_COLOSSUS, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_COLOSSUS_WONDER_GF_TREE_1)); + locationTable[RC_COLOSSUS_WONDER_GF_TREE_2] = Location::WonderItem(RC_COLOSSUS_WONDER_GF_TREE_2, RCQUEST_BOTH, RCAREA_DESERT_COLOSSUS, SCENE_DESERT_COLOSSUS, TWO_ACTOR_PARAMS(2098, -1402), "Wonder Great Fairy Tree 2", RHT_WONDER_ITEM_DESERT_COLOSSUS, RG_BLUE_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_COLOSSUS_WONDER_GF_TREE_2)); + // Dungeon Wonder Items + locationTable[RC_SHADOW_TEMPLE_WONDER_THREE_POTS] = Location::WonderItem(RC_SHADOW_TEMPLE_WONDER_THREE_POTS, RCQUEST_VANILLA, RCAREA_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE, TWO_ACTOR_PARAMS(12, 1), "Wonder Three Pots Sign", RHT_WONDER_ITEM_SHADOW_TEMPLE, RG_ARROWS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SHADOW_TEMPLE_WONDER_THREE_POTS)); + locationTable[RC_GERUDO_TRAINING_GROUND_WONDER_BEAMOS_ROOM] = Location::WonderItem(RC_GERUDO_TRAINING_GROUND_WONDER_BEAMOS_ROOM, RCQUEST_VANILLA, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(7, 12), "Wonder Beamos Sign", RHT_WONDER_ITEM_GERUDO_TRAINING_GROUND, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_WONDER_BEAMOS_ROOM)); + locationTable[RC_GERUDO_TRAINING_GROUND_WONDER_EYE_STATUE_ROOM] = Location::WonderItem(RC_GERUDO_TRAINING_GROUND_WONDER_EYE_STATUE_ROOM, RCQUEST_VANILLA, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(4, 2), "Wonder Top of Eye Statue", RHT_WONDER_ITEM_GERUDO_TRAINING_GROUND, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_WONDER_EYE_STATUE_ROOM)); + locationTable[RC_GERUDO_TRAINING_GROUND_WONDER_TORCH_SLUGS_ROOM] = Location::WonderItem(RC_GERUDO_TRAINING_GROUND_WONDER_TORCH_SLUGS_ROOM, RCQUEST_VANILLA, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(5, 16), "Wonder Torch Slug Sign", RHT_WONDER_ITEM_GERUDO_TRAINING_GROUND, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_WONDER_TORCH_SLUGS_ROOM)); + // MQ Dungeon Wonder Items + locationTable[RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_1] = Location::WonderItem(RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_1, RCQUEST_MQ, RCAREA_DEKU_TREE, SCENE_DEKU_TREE, TWO_ACTOR_PARAMS(7, 12), "MQ Wonder Basement Grave 1", RHT_WONDER_ITEM_DEKU_TREE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_1)); + locationTable[RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_2] = Location::WonderItem(RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_2, RCQUEST_MQ, RCAREA_DEKU_TREE, SCENE_DEKU_TREE, TWO_ACTOR_PARAMS(7, 11), "MQ Wonder Basement Grave 2", RHT_WONDER_ITEM_DEKU_TREE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_2)); + locationTable[RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_3] = Location::WonderItem(RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_3, RCQUEST_MQ, RCAREA_DEKU_TREE, SCENE_DEKU_TREE, TWO_ACTOR_PARAMS(7, 10), "MQ Wonder Basement Grave 3", RHT_WONDER_ITEM_DEKU_TREE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_3)); + locationTable[RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_4] = Location::WonderItem(RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_4, RCQUEST_MQ, RCAREA_DEKU_TREE, SCENE_DEKU_TREE, TWO_ACTOR_PARAMS(7, 9), "MQ Wonder Basement Grave 4", RHT_WONDER_ITEM_DEKU_TREE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_4)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_LEFT_COW] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_LEFT_COW, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(0, 13), "MQ Wonder Entrance Cow Left", RHT_WONDER_ITEM_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_LEFT_COW)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_RIGHT_COW] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_RIGHT_COW, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(0, 14), "MQ Wonder Entrance Cow Right", RHT_WONDER_ITEM_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_RIGHT_COW)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_ELEVATOR_COW] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_ELEVATOR_COW, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(1, 19), "MQ Wonder Elevator Cow", RHT_WONDER_ITEM_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_ELEVATOR_COW)); + locationTable[RC_JABU_JABUS_BELLY_MQ_HOLES_COW] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_HOLES_COW, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(2, 13), "MQ Wonder Holes Cow", RHT_WONDER_ITEM_JABU_JABU, RG_ARROWS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_HOLES_COW)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_1] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_1, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(3, 15), "MQ Wonder Basement Right Cow 1", RHT_WONDER_ITEM_JABU_JABU, RG_DEKU_SEEDS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_1)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_2] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_2, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(3, 13), "MQ Wonder Basement Right Cow 2", RHT_WONDER_ITEM_JABU_JABU, RG_DEKU_SEEDS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_2)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_3] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_3, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(3, 14), "MQ Wonder Basement Right Cow 3", RHT_WONDER_ITEM_JABU_JABU, RG_DEKU_SEEDS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_3)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_1] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_1, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(3, 18), "MQ Wonder Basement Left Cow 1", RHT_WONDER_ITEM_JABU_JABU, RG_DEKU_SEEDS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_1)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_2] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_2, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(3, 16), "MQ Wonder Basement Left Cow 2", RHT_WONDER_ITEM_JABU_JABU, RG_DEKU_SEEDS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_2)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_3] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_3, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(3, 17), "MQ Wonder Basement Left Cow 3", RHT_WONDER_ITEM_JABU_JABU, RG_DEKU_SEEDS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_3)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_AFTER_BIG_OCTO] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_AFTER_BIG_OCTO, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(6, 7), "MQ Wonder After Big Octo Cow", RHT_WONDER_ITEM_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_AFTER_BIG_OCTO)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_JIGGLIES_COW] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_JIGGLIES_COW, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(4, 13), "MQ Wonder Jigglies Cow", RHT_WONDER_ITEM_JABU_JABU, RG_DEKU_SEEDS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_JIGGLIES_COW)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_1] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_1, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(11, 21), "MQ Wonder Like Like Right Cow 1", RHT_WONDER_ITEM_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_1)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_2] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_2, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(11, 20), "MQ Wonder Like Like Right Cow 2", RHT_WONDER_ITEM_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_2)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_3] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_3, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(11, 19), "MQ Wonder Like Like Right Cow 3", RHT_WONDER_ITEM_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_3)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_1] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_1, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(11, 24), "MQ Wonder Like Like Left Cow 1", RHT_WONDER_ITEM_JABU_JABU, RG_DEKU_SEEDS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_1)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_2] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_2, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(11, 23), "MQ Wonder Like Like Left Cow 2", RHT_WONDER_ITEM_JABU_JABU, RG_DEKU_SEEDS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_2)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_3] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_3, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(11, 22), "MQ Wonder Like Like Left Cow 3", RHT_WONDER_ITEM_JABU_JABU, RG_DEKU_SEEDS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_3)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_1] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_1, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(11, 29), "MQ Wonder Like Like Grass 1", RHT_WONDER_ITEM_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_1)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_2] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_2, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(11, 28), "MQ Wonder Like Like Grass 2", RHT_WONDER_ITEM_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_2)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_3] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_3, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(11, 27), "MQ Wonder Like Like Grass 3", RHT_WONDER_ITEM_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_3)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_LEFT_COW] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_LEFT_COW, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(5, 10), "MQ Wonder Before Boss Left Cow", RHT_WONDER_ITEM_JABU_JABU, RG_DEKU_SEEDS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_LEFT_COW)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_1] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_1, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(5, 9), "MQ Wonder Before Boss Right Cow 1", RHT_WONDER_ITEM_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_1)); + locationTable[RC_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_2] = Location::WonderItem(RC_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_2, RCQUEST_MQ, RCAREA_JABU_JABUS_BELLY, SCENE_JABU_JABU, TWO_ACTOR_PARAMS(5, 11), "MQ Wonder Before Boss Right Cow 2", RHT_WONDER_ITEM_JABU_JABU, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_2)); + locationTable[RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_1] = Location::WonderItem(RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_1, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, TWO_ACTOR_PARAMS(4, 6), "MQ Wonder Shortcut Room 1", RHT_WONDER_ITEM_FIRE_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_1)); + locationTable[RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_2] = Location::WonderItem(RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_2, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, TWO_ACTOR_PARAMS(4, 7), "MQ Wonder Shortcut Room 2", RHT_WONDER_ITEM_FIRE_TEMPLE, RG_ARROWS_10, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_2)); + locationTable[RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_3] = Location::WonderItem(RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_3, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, TWO_ACTOR_PARAMS(4, 8), "MQ Wonder Shortcut Room 3", RHT_WONDER_ITEM_FIRE_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_3)); + locationTable[RC_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_HOOKSHOT] = Location::WonderItem(RC_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_HOOKSHOT, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, TWO_ACTOR_PARAMS(19, 9), "MQ Wonder Boss Key Room Hookshot", RHT_WONDER_ITEM_FIRE_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_HOOKSHOT)); + locationTable[RC_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_BOW] = Location::WonderItem(RC_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_BOW, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, TWO_ACTOR_PARAMS(19, 10), "MQ Wonder Boss Key Room Bow", RHT_WONDER_ITEM_FIRE_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_BOW)); + locationTable[RC_FIRE_TEMPLE_MQ_WONDER_LIZALFOS_MAZE] = Location::WonderItem(RC_FIRE_TEMPLE_MQ_WONDER_LIZALFOS_MAZE, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, TWO_ACTOR_PARAMS(5, 19), "MQ Wonder Lizalfos Maze", RHT_WONDER_ITEM_FIRE_TEMPLE, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_WONDER_LIZALFOS_MAZE)); + locationTable[RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_1] = Location::WonderItem(RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_1, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, TWO_ACTOR_PARAMS(7, 6), "MQ Wonder East Tower Large Face 1", RHT_WONDER_ITEM_FIRE_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_1)); + locationTable[RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_2] = Location::WonderItem(RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_2, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, TWO_ACTOR_PARAMS(7, 7), "MQ Wonder East Tower Large Face 2", RHT_WONDER_ITEM_FIRE_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_2)); + locationTable[RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_1] = Location::WonderItem(RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_1, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, TWO_ACTOR_PARAMS(7, 4), "MQ Wonder East Tower Small Face 1", RHT_WONDER_ITEM_FIRE_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_1)); + locationTable[RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_2] = Location::WonderItem(RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_2, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, TWO_ACTOR_PARAMS(7, 5), "MQ Wonder East Tower Small Face 2", RHT_WONDER_ITEM_FIRE_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_2)); + locationTable[RC_FIRE_TEMPLE_MQ_WONDER_TORCH_ROOM] = Location::WonderItem(RC_FIRE_TEMPLE_MQ_WONDER_TORCH_ROOM, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, TWO_ACTOR_PARAMS(16, 0), "MQ Wonder Torch Room", RHT_WONDER_ITEM_FIRE_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_WONDER_TORCH_ROOM)); + locationTable[RC_FIRE_TEMPLE_MQ_WONDER_FIRE_MAZE] = Location::WonderItem(RC_FIRE_TEMPLE_MQ_WONDER_FIRE_MAZE, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, TWO_ACTOR_PARAMS(10, 42), "MQ Wonder Fire Maze", RHT_WONDER_ITEM_FIRE_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_WONDER_FIRE_MAZE)); + locationTable[RC_FIRE_TEMPLE_MQ_WONDER_AFTER_FLARE_DANCER] = Location::WonderItem(RC_FIRE_TEMPLE_MQ_WONDER_AFTER_FLARE_DANCER, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, TWO_ACTOR_PARAMS(12, 0), "MQ Wonder After Flare Dancer", RHT_WONDER_ITEM_FIRE_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_WONDER_AFTER_FLARE_DANCER)); + locationTable[RC_FIRE_TEMPLE_MQ_WONDER_STAIRCASE] = Location::WonderItem(RC_FIRE_TEMPLE_MQ_WONDER_STAIRCASE, RCQUEST_MQ, RCAREA_FIRE_TEMPLE, SCENE_FIRE_TEMPLE, TWO_ACTOR_PARAMS(14, 1), "MQ Wonder Hammer Staircase", RHT_WONDER_ITEM_FIRE_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_FIRE_TEMPLE_MQ_WONDER_STAIRCASE)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_LIZALFOS_ROOM] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_LIZALFOS_ROOM, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(19, 5), "MQ Wonder Lizalfos Room", RHT_WONDER_ITEM_WATER_TEMPLE, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_LIZALFOS_ROOM)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_LONGSHOT_ROOM] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_LONGSHOT_ROOM, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(17, 7), "MQ Wonder Longshot Room", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_LONGSHOT_ROOM)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_STALFOS_ROOM] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_STALFOS_ROOM, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(18, 3), "MQ Wonder Stalfos Room", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_STALFOS_ROOM)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_1] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_1, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(5, 4), "MQ Wonder Hookshot Staircase Right 1", RHT_WONDER_ITEM_WATER_TEMPLE, RG_DEKU_NUTS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_1)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_2] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_2, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(5, 3), "MQ Wonder Hookshot Staircase Right 2", RHT_WONDER_ITEM_WATER_TEMPLE, RG_RED_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_2)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_3] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_3, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(5, 2), "MQ Wonder Hookshot Staircase Right 3", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_3)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_1] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_1, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(5, 11), "MQ Wonder Hookshot Staircase Left 1", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_1)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_2] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_2, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(5, 10), "MQ Wonder Hookshot Staircase Left 2", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_2)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_3] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_3, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(5, 9), "MQ Wonder Hookshot Staircase Left 3", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_3)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_AFTER_DARK_LINK] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_AFTER_DARK_LINK, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(7, 0), "MQ Wonder After Dark Link", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_AFTER_DARK_LINK)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_LEFT_EYE] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_LEFT_EYE, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(8, 4), "MQ Wonder Dragon Room Left Eye", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_LEFT_EYE)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_RIGHT_EYE] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_RIGHT_EYE, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(8, 6), "MQ Wonder Dragon Room Right Eye", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_RIGHT_EYE)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_PORTRAIT] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_PORTRAIT, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(8, 5), "MQ Wonder Dragon Room Portrait", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_PORTRAIT)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_TRIPLE_TORCHES] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_TRIPLE_TORCHES, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(3, 3), "MQ Wonder Triple Torches Room", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_TRIPLE_TORCHES)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_1] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_1, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(15, 0), "MQ Wonder Water Sprouts 1", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_1)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_2] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_2, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(15, 1), "MQ Wonder Water Sprouts 2", RHT_WONDER_ITEM_WATER_TEMPLE, RG_ARROWS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_2)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_FREESTANDING_ROOM] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_FREESTANDING_ROOM, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(16, 0), "MQ Wonder Freestanding Room", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_FREESTANDING_ROOM)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_1] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_1, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(11, 1), "MQ Wonder Before Boss 1", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_1)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_2] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_2, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(11, 0), "MQ Wonder Before Boss 2", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_2)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_UNDER_PILLAR_ROOM] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_UNDER_PILLAR_ROOM, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(2, 24), "MQ Wonder Under Pillar Room", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_UNDER_PILLAR_ROOM)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_LIZALFOS_HALLWAY] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_LIZALFOS_HALLWAY, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(20, 2), "MQ Wonder Lizalfos Hallway", RHT_WONDER_ITEM_WATER_TEMPLE, RG_ARROWS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_LIZALFOS_HALLWAY)); + locationTable[RC_WATER_TEMPLE_MQ_WONDER_GS_STORAGE_ROOM] = Location::WonderItem(RC_WATER_TEMPLE_MQ_WONDER_GS_STORAGE_ROOM, RCQUEST_MQ, RCAREA_WATER_TEMPLE, SCENE_WATER_TEMPLE, TWO_ACTOR_PARAMS(10, 1), "MQ Wonder GS Storage Room", RHT_WONDER_ITEM_WATER_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_WATER_TEMPLE_MQ_WONDER_GS_STORAGE_ROOM)); + locationTable[RC_SPIRIT_TEMPLE_MQ_WONDER_CHEST_HAMMER] = Location::WonderItem(RC_SPIRIT_TEMPLE_MQ_WONDER_CHEST_HAMMER, RCQUEST_MQ, RCAREA_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(18, 8), "MQ Wonder Chest Hammer", RHT_WONDER_ITEM_SPIRIT_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_TEMPLE_MQ_WONDER_CHEST_HAMMER)); + locationTable[RC_SPIRIT_TEMPLE_MQ_WONDER_CHEST_SLASH] = Location::WonderItem(RC_SPIRIT_TEMPLE_MQ_WONDER_CHEST_SLASH, RCQUEST_MQ, RCAREA_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(18, 9), "MQ Wonder Chest Sword", RHT_WONDER_ITEM_SPIRIT_TEMPLE, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_TEMPLE_MQ_WONDER_CHEST_SLASH)); + locationTable[RC_SHADOW_TEMPLE_MQ_WONDER_THREE_POTS] = Location::WonderItem(RC_SHADOW_TEMPLE_MQ_WONDER_THREE_POTS, RCQUEST_MQ, RCAREA_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE, TWO_ACTOR_PARAMS(12, 1), "MQ Wonder Three Pots Sign", RHT_WONDER_ITEM_SHADOW_TEMPLE, RG_ARROWS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SHADOW_TEMPLE_MQ_WONDER_THREE_POTS)); + locationTable[RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_1] = Location::WonderItem(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_1, RCQUEST_MQ, RCAREA_BOTTOM_OF_THE_WELL, SCENE_BOTTOM_OF_THE_WELL, TWO_ACTOR_PARAMS(0, 5), "MQ Wonder Main Room West Sign 1", RHT_BOTTOM_OF_THE_WELL_WONDER_ITEM, RG_DEKU_SEEDS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_1)); + locationTable[RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_2] = Location::WonderItem(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_2, RCQUEST_MQ, RCAREA_BOTTOM_OF_THE_WELL, SCENE_BOTTOM_OF_THE_WELL, TWO_ACTOR_PARAMS(0, 6), "MQ Wonder Main Room West Sign 2", RHT_BOTTOM_OF_THE_WELL_WONDER_ITEM, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_2)); + locationTable[RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_3] = Location::WonderItem(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_3, RCQUEST_MQ, RCAREA_BOTTOM_OF_THE_WELL, SCENE_BOTTOM_OF_THE_WELL, TWO_ACTOR_PARAMS(0, 8), "MQ Wonder Main Room West Sign 3", RHT_BOTTOM_OF_THE_WELL_WONDER_ITEM, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_3)); + locationTable[RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_4] = Location::WonderItem(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_4, RCQUEST_MQ, RCAREA_BOTTOM_OF_THE_WELL, SCENE_BOTTOM_OF_THE_WELL, TWO_ACTOR_PARAMS(0, 10), "MQ Wonder Main Room West Sign 4", RHT_BOTTOM_OF_THE_WELL_WONDER_ITEM, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_4)); + locationTable[RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_1] = Location::WonderItem(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_1, RCQUEST_MQ, RCAREA_BOTTOM_OF_THE_WELL, SCENE_BOTTOM_OF_THE_WELL, TWO_ACTOR_PARAMS(0, 12), "MQ Wonder Main Room East Sign 1", RHT_BOTTOM_OF_THE_WELL_WONDER_ITEM, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_1)); + locationTable[RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_2] = Location::WonderItem(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_2, RCQUEST_MQ, RCAREA_BOTTOM_OF_THE_WELL, SCENE_BOTTOM_OF_THE_WELL, TWO_ACTOR_PARAMS(0, 11), "MQ Wonder Main Room East Sign 2", RHT_BOTTOM_OF_THE_WELL_WONDER_ITEM, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_2)); + locationTable[RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_3] = Location::WonderItem(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_3, RCQUEST_MQ, RCAREA_BOTTOM_OF_THE_WELL, SCENE_BOTTOM_OF_THE_WELL, TWO_ACTOR_PARAMS(0, 9), "MQ Wonder Main Room East Sign 3", RHT_BOTTOM_OF_THE_WELL_WONDER_ITEM, RG_DEKU_SEEDS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_3)); + locationTable[RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_4] = Location::WonderItem(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_4, RCQUEST_MQ, RCAREA_BOTTOM_OF_THE_WELL, SCENE_BOTTOM_OF_THE_WELL, TWO_ACTOR_PARAMS(0, 7), "MQ Wonder Main Room East Sign 4", RHT_BOTTOM_OF_THE_WELL_WONDER_ITEM, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_4)); + locationTable[RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_1] = Location::WonderItem(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_1, RCQUEST_MQ, RCAREA_BOTTOM_OF_THE_WELL, SCENE_BOTTOM_OF_THE_WELL, TWO_ACTOR_PARAMS(5, 0), "MQ Wonder East Room Sign 1", RHT_BOTTOM_OF_THE_WELL_WONDER_ITEM, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_1)); + locationTable[RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_2] = Location::WonderItem(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_2, RCQUEST_MQ, RCAREA_BOTTOM_OF_THE_WELL, SCENE_BOTTOM_OF_THE_WELL, TWO_ACTOR_PARAMS(5, 1), "MQ Wonder East Room Sign 2", RHT_BOTTOM_OF_THE_WELL_WONDER_ITEM, RG_DEKU_SEEDS_30, SpoilerCollectionCheck::RandomizerInf(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_2)); + locationTable[RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_3] = Location::WonderItem(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_3, RCQUEST_MQ, RCAREA_BOTTOM_OF_THE_WELL, SCENE_BOTTOM_OF_THE_WELL, TWO_ACTOR_PARAMS(5, 2), "MQ Wonder East Room Sign 3", RHT_BOTTOM_OF_THE_WELL_WONDER_ITEM, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_3)); + locationTable[RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_4] = Location::WonderItem(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_4, RCQUEST_MQ, RCAREA_BOTTOM_OF_THE_WELL, SCENE_BOTTOM_OF_THE_WELL, TWO_ACTOR_PARAMS(5, 3), "MQ Wonder East Room Sign 4", RHT_BOTTOM_OF_THE_WELL_WONDER_ITEM, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_4)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_WONDER_DINOLFOS_ROOM] = Location::WonderItem(RC_GERUDO_TRAINING_GROUND_MQ_WONDER_DINOLFOS_ROOM, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(7, 9), "MQ Wonder Dinolfos Sign", RHT_WONDER_ITEM_GERUDO_TRAINING_GROUND, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_WONDER_DINOLFOS_ROOM)); + locationTable[RC_GERUDO_TRAINING_GROUND_MQ_WONDER_EYE_STATUE] = Location::WonderItem(RC_GERUDO_TRAINING_GROUND_MQ_WONDER_EYE_STATUE, RCQUEST_MQ, RCAREA_GERUDO_TRAINING_GROUND, SCENE_GERUDO_TRAINING_GROUND, TWO_ACTOR_PARAMS(4, 4), "MQ Wonder Top of Eye Statue", RHT_WONDER_ITEM_GERUDO_TRAINING_GROUND, RG_GREEN_RUPEE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GERUDO_TRAINING_GROUND_MQ_WONDER_EYE_STATUE)); + locationTable[RC_GANONS_CASTLE_MQ_WONDER_SHADOW_TRIAL] = Location::WonderItem(RC_GANONS_CASTLE_MQ_WONDER_SHADOW_TRIAL, RCQUEST_MQ, RCAREA_GANONS_CASTLE, SCENE_INSIDE_GANONS_CASTLE, TWO_ACTOR_PARAMS(12, 12), "MQ Wonder Shadow Trial Bombflower", RHT_GANONS_CASTLE_WONDER_ITEM, RG_DEKU_NUTS_5, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GANONS_CASTLE_MQ_WONDER_SHADOW_TRIAL)); + + // clang-format on +} + +static RegisterShipInitFunc registerShuffleWonderItems(RegisterShuffleWonderItems, { "IS_RANDO" }); +static RegisterShipInitFunc registerWonderItemLocations(Rando::StaticData::RegisterWonderItemLocations); \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/entrance.cpp b/soh/soh/Enhancements/randomizer/entrance.cpp index 9af7ddba713..06a72025695 100644 --- a/soh/soh/Enhancements/randomizer/entrance.cpp +++ b/soh/soh/Enhancements/randomizer/entrance.cpp @@ -397,8 +397,8 @@ void SetAllEntrancesData() { { EntranceType::ThievesHideout, RR_TH_KITCHEN_OPPOSITE_CORRIDOR, RR_GF_NEAR_GS, ENTR_GERUDOS_FORTRESS_10 } }, { { EntranceType::ThievesHideout, RR_GF_BELOW_CHEST, RR_TH_BREAK_ROOM, ENTR_THIEVES_HIDEOUT_10 }, { EntranceType::ThievesHideout, RR_TH_BREAK_ROOM, RR_GF_BELOW_CHEST, ENTR_GERUDOS_FORTRESS_11 } }, - { { EntranceType::ThievesHideout, RR_GF_ABOVE_JAIL, RR_TH_BREAK_ROOM_CORRIDOR, ENTR_THIEVES_HIDEOUT_11 }, - { EntranceType::ThievesHideout, RR_TH_BREAK_ROOM_CORRIDOR, RR_GF_ABOVE_JAIL, ENTR_GERUDOS_FORTRESS_12 } }, + { { EntranceType::ThievesHideout, RR_GF_ABOVE_JAIL, RR_TH_BREAK_ROOM_UPPER_CORRIDOR, ENTR_THIEVES_HIDEOUT_11 }, + { EntranceType::ThievesHideout, RR_TH_BREAK_ROOM_UPPER_CORRIDOR, RR_GF_ABOVE_JAIL, ENTR_GERUDOS_FORTRESS_12 } }, { { EntranceType::ThievesHideout, RR_GF_BELOW_GS, RR_TH_DEAD_END_CELL, ENTR_THIEVES_HIDEOUT_12 }, { EntranceType::ThievesHideout, RR_TH_DEAD_END_CELL, RR_GF_BELOW_GS, ENTR_GERUDOS_FORTRESS_13 } }, diff --git a/soh/soh/Enhancements/randomizer/location.cpp b/soh/soh/Enhancements/randomizer/location.cpp index e129a6b264b..15f35f11bcc 100644 --- a/soh/soh/Enhancements/randomizer/location.cpp +++ b/soh/soh/Enhancements/randomizer/location.cpp @@ -590,6 +590,15 @@ Rando::Location Rando::Location::Bush(RandomizerCheck rc, RandomizerCheckQuest q false, collectionCheck }; } +Rando::Location Rando::Location::WonderItem(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_, + SceneID scene_, int32_t actorParams_, std::string&& shortName_, + RandomizerHintTextKey hintKey, RandomizerGet vanillaItem, + SpoilerCollectionCheck collectionCheck) { + return { rc, quest_, RCTYPE_WONDER_ITEM, area_, ACTOR_EN_WONDER_ITEM, + scene_, actorParams_, std::move(shortName_), hintKey, vanillaItem, + false, collectionCheck }; +} + Rando::Location Rando::Location::HintStone(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_, SceneID scene_, int32_t actorParams_, std::string&& shortName_) { return { rc, quest_, RCTYPE_GOSSIP_STONE, area_, ACTOR_EN_GS, @@ -630,6 +639,15 @@ Rando::Location Rando::Location::SongFairy(RandomizerCheck rc, RandomizerCheckQu false, collectionCheck }; } +Rando::Location Rando::Location::ButterflyFairy(RandomizerCheck rc, RandomizerCheckQuest quest_, + RandomizerCheckArea area_, SceneID scene_, int32_t actorParams_, + std::string&& shortName_, RandomizerHintTextKey hintKey, + SpoilerCollectionCheck collectionCheck) { + return { rc, quest_, RCTYPE_BUTTERFLY_FAIRY, area_, ACTOR_EN_BUTTE, + scene_, actorParams_, std::move(shortName_), hintKey, RG_NONE, + false, collectionCheck }; +} + Rando::Location Rando::Location::Grass(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_, SceneID scene_, int32_t actorParams_, std::string&& shortName_, RandomizerHintTextKey hintKey, RandomizerGet vanillaItem, @@ -638,3 +656,27 @@ Rando::Location Rando::Location::Grass(RandomizerCheck rc, RandomizerCheckQuest scene_, actorParams_, std::move(shortName_), hintKey, vanillaItem, false, collectionCheck }; } + +Rando::Location Rando::Location::Sign(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_, + SceneID scene_, int32_t actorParams_, std::string&& shortName_, + RandomizerHintTextKey hintKey, ActorID actorId, + SpoilerCollectionCheck collectionCheck) { + return { rc, quest_, RCTYPE_SIGN, area_, actorId, scene_, actorParams_, std::move(shortName_), + hintKey, RG_NONE, false, collectionCheck }; +} + +Rando::Location Rando::Location::Icicle(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_, + SceneID scene_, int32_t actorParams_, std::string&& shortName_, + RandomizerHintTextKey hintKey, SpoilerCollectionCheck collectionCheck) { + return { rc, quest_, RCTYPE_ICICLE, area_, ACTOR_BG_ICE_TURARA, + scene_, actorParams_, std::move(shortName_), hintKey, RG_NONE, + false, collectionCheck }; +} + +Rando::Location Rando::Location::RedIce(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_, + SceneID scene_, int32_t actorParams_, std::string&& shortName_, + RandomizerHintTextKey hintKey, SpoilerCollectionCheck collectionCheck) { + return { rc, quest_, RCTYPE_RED_ICE, area_, ACTOR_BG_ICE_SHELTER, + scene_, actorParams_, std::move(shortName_), hintKey, RG_NONE, + false, collectionCheck }; +} \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/location.h b/soh/soh/Enhancements/randomizer/location.h index 24b192cc342..c2337b0e3c9 100644 --- a/soh/soh/Enhancements/randomizer/location.h +++ b/soh/soh/Enhancements/randomizer/location.h @@ -257,6 +257,23 @@ class Location { int32_t actorParams_, std::string&& shortName_, RandomizerHintTextKey hintKey, RandomizerGet vanillaItem, SpoilerCollectionCheck collectionCheck); + static Location Sign(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_, SceneID scene_, + int32_t actorParams_, std::string&& shortName_, RandomizerHintTextKey hintKey, + ActorID actorId_, SpoilerCollectionCheck collectionCheck); + + static Location WonderItem(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_, + SceneID scene_, int32_t actorParams_, std::string&& shortName_, + RandomizerHintTextKey hintKey, RandomizerGet vanillaItem, + SpoilerCollectionCheck collectionCheck); + + static Location Icicle(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_, SceneID scene_, + int32_t actorParams_, std::string&& shortName_, RandomizerHintTextKey hintKey, + SpoilerCollectionCheck collectionCheck); + + static Location RedIce(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_, SceneID scene_, + int32_t actorParams_, std::string&& shortName_, RandomizerHintTextKey hintKey, + SpoilerCollectionCheck collectionCheck); + static Location OtherHint(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_, ActorID actorId_, SceneID scene_, std::string&& shortName_, std::string&& spoilerName_); @@ -282,6 +299,10 @@ class Location { SceneID scene_, int32_t actorParams_, std::string&& shortName_, RandomizerHintTextKey hintKey, SpoilerCollectionCheck collectionCheck); + static Location ButterflyFairy(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_, + SceneID scene_, int32_t actorParams_, std::string&& shortName_, + RandomizerHintTextKey hintKey, SpoilerCollectionCheck collectionCheck); + static Location HintStone(RandomizerCheck rc, RandomizerCheckQuest quest_, SceneID scene_, int32_t actorParams_, std::string&& shortName_); diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp index c63ca54a4f7..7fe50d23236 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/bottom_of_the_well.cpp @@ -208,7 +208,7 @@ void RegionTable_Init_BottomOfTheWell() { ENTRANCE(RR_BOTW_HIDDEN_POTS, logic->CanClimbHighLadder()), //It's possible to abuse boulder's limited range of collision detection to detonate the flowers through the boulder with bow, but this is a glitch //the exact range is just past the furthest away plank in the green goo section - ENTRANCE(RR_BOTW_B3_BOMB_FLOWERS, AnyAgeTime([]{return logic->BlastOrSmash() || logic->CanUse(RG_DINS_FIRE) || (ctx->GetTrickOption(RT_BOTW_BASEMENT) && logic->CanUse(RG_STICKS)) || (ctx->GetTrickOption(RT_DISTANT_BOULDER_COLLISION) && logic->CanUse(RG_FAIRY_BOW));})), + ENTRANCE(RR_BOTW_B3_BOMB_FLOWERS, AnyAgeTime([]{return logic->BlastOrSmash() || logic->CanUse(RG_DINS_FIRE) || (ctx->GetTrickOption(RT_BOTW_BASEMENT) && logic->CanUse(RG_STICKS)) || (ctx->GetTrickOption(RT_BOULDER_COLLISION) && logic->CanUse(RG_FAIRY_BOW));})), ENTRANCE(RR_BOTW_B3_BLOCKED_GRASS, AnyAgeTime([]{return logic->BlastOrSmash();})), ENTRANCE(RR_BOTW_B3_CHEST_AREA, AnyAgeTime([]{return logic->BlastOrSmash();})), }); @@ -268,7 +268,7 @@ void RegionTable_Init_BottomOfTheWell() { //Item extension can get a fairy by either shooting the pot through the grate and letting the fairy fly through the wall //This cannot be done if the pot has an item in it, as it cannot be collected this way. (ctx->GetTrickOption(RT_ITEM_EXTENSION) && (ctx->GetOption(RSK_SHUFFLE_POTS).Is(RO_SHUFFLE_POTS_OFF) || ctx->GetOption(RSK_SHUFFLE_POTS).Is(RO_SHUFFLE_POTS_OVERWORLD)) && logic->CanHitEyeTargets()) || - (ctx->GetTrickOption(RT_VISIBLE_COLLISION) && logic->IsChild ? logic->CanHitEyeTargets() : logic->CanUse(RG_FAIRY_SLINGSHOT))), + (ctx->GetTrickOption(RT_BOULDER_COLLISION) && logic->IsChild ? logic->CanHitEyeTargets() : logic->CanUse(RG_FAIRY_SLINGSHOT))), //It is possible to hit the water switch with a pot from RR_BOTW_MQ_MIDDLE, however the hitbox for making it activate is very unintuitive //You have to throw the pot from further back to hit the switch from the front instead of the top, trying to hit the "fingers" directly //This unintuitiveness means it should be a trick. ZL is needed to get a clear path to carry the pot @@ -280,10 +280,18 @@ void RegionTable_Init_BottomOfTheWell() { //Instead of blowing up the boulder, you can aim through the lower left side with sling(either age) or as child with bow //Not even bow extension seems to get adult's bow to work //this would be a trick - LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_OUTER_LOBBY_POT, (AnyAgeTime([]{return logic->BlastOrSmash();}) && logic->CanHitEyeTargets()) || - (ctx->GetTrickOption(RT_VISIBLE_COLLISION) && logic->IsChild ? logic->CanHitEyeTargets() : logic->CanUse(RG_FAIRY_SLINGSHOT))), - LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_BOMB_LEFT_HEART, logic->HasExplosives()), - LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_BOMB_RIGHT_HEART, logic->HasExplosives()), + LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_OUTER_LOBBY_POT, (AnyAgeTime([]{return logic->BlastOrSmash();}) && logic->CanHitEyeTargets()) || + (ctx->GetTrickOption(RT_BOULDER_COLLISION) && logic->IsChild ? logic->CanHitEyeTargets() : logic->CanUse(RG_FAIRY_SLINGSHOT))), + LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_BOMB_LEFT_HEART, logic->HasExplosives()), + LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_BOMB_RIGHT_HEART, logic->HasExplosives()), + LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_1, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_2, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_3, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_4, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_1, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_2, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_3, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_4, logic->CanUse(RG_FAIRY_SLINGSHOT)), }, { //Exits ENTRANCE(RR_BOTW_ENTRYWAY, logic->CanUse(RG_CRAWL) && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT))), @@ -321,6 +329,10 @@ void RegionTable_Init_BottomOfTheWell() { LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_EAST_INNER_ROOM_POT_1, logic->CanBreakPots()), LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_EAST_INNER_ROOM_POT_2, logic->CanBreakPots()), LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_EAST_INNER_ROOM_POT_3, logic->CanBreakPots()), + LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_1, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_2, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_3, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_4, logic->CanUse(RG_FAIRY_SLINGSHOT)), }, { //Exits ENTRANCE(RR_BOTW_MQ_MIDDLE, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp index dfda2371953..3417d10abb9 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp @@ -189,7 +189,10 @@ void RegionTable_Init_DekuTree() { EVENT_ACCESS(LOGIC_NUT_ACCESS, logic->CanGetDekuBabaNuts()), EVENT_ACCESS(LOGIC_DEKU_TREE_PUSHED_BASEMENT_BLOCK, logic->HasItem(RG_POWER_BRACELET)), EVENT_ACCESS(LOGIC_DEKU_TREE_B1_BROKE_WEB, logic->HasFireSource()), - }, {}, { + }, { + //Locations + LOCATION(RC_DEKU_TREE_BEFORE_BOSS_MIDDLE_HEART, !!ctx->GetTrickOption(RT_VOIDOUT_COLLECTION)), + }, { //Exits ENTRANCE(RR_DEKU_TREE_BASEMENT_LOWER, true), ENTRANCE(RR_DEKU_TREE_BASEMENT_BACK_LOBBY, logic->CanUse(RG_CRAWL)), @@ -200,9 +203,10 @@ void RegionTable_Init_DekuTree() { areaTable[RR_DEKU_TREE_OUTSIDE_BOSS_ROOM] = Region("Deku Tree Outside Boss Room", SCENE_DEKU_TREE, {}, { //Locations - LOCATION(RC_DEKU_TREE_BEFORE_BOSS_LEFT_HEART, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG)), - LOCATION(RC_DEKU_TREE_BEFORE_BOSS_MIDDLE_HEART, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG)), - LOCATION(RC_DEKU_TREE_BEFORE_BOSS_RIGHT_HEART, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_DEKU_TREE_BEFORE_BOSS_LEFT_HEART, logic->IsAdult || (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + // Can be grabbed by jumping to it from above, might deserve its own trick + LOCATION(RC_DEKU_TREE_BEFORE_BOSS_MIDDLE_HEART, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || ((logic->CanUse(RG_HOVER_BOOTS) || logic->IsAdult) && ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_DEKU_TREE_BEFORE_BOSS_RIGHT_HEART, logic->IsAdult || (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), LOCATION(RC_DEKU_TREE_BEFORE_BOSS_GRASS_1, logic->CanCutShrubs() && logic->HasFireSourceWithTorch()), LOCATION(RC_DEKU_TREE_BEFORE_BOSS_GRASS_2, logic->CanCutShrubs() && logic->HasFireSourceWithTorch()), LOCATION(RC_DEKU_TREE_BEFORE_BOSS_GRASS_3, logic->CanCutShrubs() && logic->HasFireSourceWithTorch()), @@ -421,6 +425,10 @@ void RegionTable_Init_DekuTree() { LOCATION(RC_DEKU_TREE_MQ_BASEMENT_GRAVES_GRASS_3, logic->CanCutShrubs()), LOCATION(RC_DEKU_TREE_MQ_BASEMENT_GRAVES_GRASS_4, logic->CanCutShrubs()), LOCATION(RC_DEKU_TREE_MQ_BASEMENT_GRAVES_GRASS_5, logic->CanCutShrubs()), + LOCATION(RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_1, logic->CanJumpslashExceptHammer()), + LOCATION(RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_2, logic->CanJumpslashExceptHammer()), + LOCATION(RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_3, logic->CanJumpslashExceptHammer()), + LOCATION(RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_4, logic->CanJumpslashExceptHammer()), }, { //Exits ENTRANCE(RR_DEKU_TREE_MQ_BASEMENT_LEDGE, logic->CanUse(RG_CRAWL) && AnyAgeTime([]{return logic->HasFireSourceWithTorch() || logic->CanUse(RG_FAIRY_BOW);})), @@ -447,10 +455,11 @@ void RegionTable_Init_DekuTree() { EVENT_ACCESS(LOGIC_DEKU_TREE_B1_BROKE_WEB, logic->HasFireSource()), }, { //Locations - LOCATION(RC_DEKU_TREE_MQ_DEKU_SCRUB, logic->CanStunDeku() && logic->HasItem(RG_SPEAK_DEKU) && GetCheckPrice() <= GetWalletCapacity()), - LOCATION(RC_DEKU_TREE_MQ_BASEMENT_UPPER_GRASS_1, logic->CanCutShrubs()), - LOCATION(RC_DEKU_TREE_MQ_BASEMENT_UPPER_GRASS_2, logic->CanCutShrubs()), - LOCATION(RC_DEKU_TREE_MQ_BASEMENT_UPPER_GRASS_3, logic->CanCutShrubs()), + LOCATION(RC_DEKU_TREE_MQ_DEKU_SCRUB, logic->CanStunDeku() && logic->HasItem(RG_SPEAK_DEKU) && GetCheckPrice() <= GetWalletCapacity()), + LOCATION(RC_DEKU_TREE_MQ_BASEMENT_UPPER_GRASS_1, logic->CanCutShrubs()), + LOCATION(RC_DEKU_TREE_MQ_BASEMENT_UPPER_GRASS_2, logic->CanCutShrubs()), + LOCATION(RC_DEKU_TREE_MQ_BASEMENT_UPPER_GRASS_3, logic->CanCutShrubs()), + LOCATION(RC_DEKU_TREE_MQ_BEFORE_BOSS_MIDDLE_HEART, !!ctx->GetTrickOption(RT_VOIDOUT_COLLECTION)), }, { //Exits ENTRANCE(RR_DEKU_TREE_MQ_BASEMENT_GRAVE_ROOM, logic->CanUse(RG_CRAWL)), @@ -461,9 +470,9 @@ void RegionTable_Init_DekuTree() { areaTable[RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM] = Region("Deku Tree MQ Outside Boss Room", SCENE_DEKU_TREE, {}, { //Locations - LOCATION(RC_DEKU_TREE_MQ_BEFORE_BOSS_LEFT_HEART, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG)), - LOCATION(RC_DEKU_TREE_MQ_BEFORE_BOSS_MIDDLE_HEART, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG)), - LOCATION(RC_DEKU_TREE_MQ_BEFORE_BOSS_RIGHT_HEART, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_DEKU_TREE_MQ_BEFORE_BOSS_LEFT_HEART, logic->IsAdult || (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_DEKU_TREE_MQ_BEFORE_BOSS_MIDDLE_HEART, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || ((logic->CanUse(RG_HOVER_BOOTS) || logic->IsAdult) && ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_DEKU_TREE_MQ_BEFORE_BOSS_RIGHT_HEART, logic->IsAdult || (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), LOCATION(RC_DEKU_TREE_MQ_BEFORE_BOSS_GRASS_1, logic->CanCutShrubs()), LOCATION(RC_DEKU_TREE_MQ_BEFORE_BOSS_GRASS_2, logic->CanCutShrubs()), LOCATION(RC_DEKU_TREE_MQ_BEFORE_BOSS_GRASS_3, logic->CanCutShrubs()), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp index a562b6c1d14..6410e262781 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/dodongos_cavern.cpp @@ -242,6 +242,7 @@ void RegionTable_Init_DodongosCavern() { }, { //Locations LOCATION(RC_DODONGOS_CAVERN_END_OF_BRIDGE_CHEST, logic->CanBreakMudWalls() && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_DODONGOS_CAVERN_TOP_FLOOR_PEDESTAL, logic->CanRead()), }, { //Exits ENTRANCE(RR_DODONGOS_CAVERN_LOBBY, true), @@ -328,7 +329,10 @@ void RegionTable_Init_DodongosCavern() { //Events EVENT_ACCESS(LOGIC_DC_MQ_CLEAR_UPPER_LOBBY_ROCKS, logic->BlastOrSmash() || logic->CanUse(RG_DINS_FIRE)), EVENT_ACCESS(LOGIC_DC_EYES_LIT, logic->HasExplosives() || (logic->Get(LOGIC_DC_MQ_CLEAR_UPPER_LOBBY_ROCKS) && logic->HasItem(RG_GORONS_BRACELET) && ((logic->IsAdult && ctx->GetTrickOption(RT_DC_MQ_ADULT_EYES)) || (logic->IsChild && ctx->GetTrickOption(RT_DC_MQ_CHILD_EYES))))), - }, {}, { + }, { + //Locations + LOCATION(RC_DODONGOS_CAVERN_TOP_FLOOR_PEDESTAL, logic->CanRead()), + }, { //Exits ENTRANCE(RR_DODONGOS_CAVERN_MQ_LOBBY, true), ENTRANCE(RR_DODONGOS_CAVERN_MQ_TORCH_PUZZLE_UPPER, logic->Get(LOGIC_DC_MQ_CLEAR_UPPER_LOBBY_ROCKS)), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp index 60f51f75dc6..4f2a05ba578 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/fire_temple.cpp @@ -626,9 +626,11 @@ void RegionTable_Init_FireTemple() { EVENT_ACCESS(LOGIC_FAIRY_ACCESS, logic->CanUse(RG_HOOKSHOT)), }, { //Locations - LOCATION(RC_FIRE_TEMPLE_MQ_BOSS_KEY_CHEST, logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_FIRE_TEMPLE_MQ_LAVA_TORCH_POT_1, logic->HookshotOrBoomerang()), - LOCATION(RC_FIRE_TEMPLE_MQ_LAVA_TORCH_POT_2, logic->HookshotOrBoomerang()), + LOCATION(RC_FIRE_TEMPLE_MQ_BOSS_KEY_CHEST, logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_FIRE_TEMPLE_MQ_LAVA_TORCH_POT_1, logic->HookshotOrBoomerang()), + LOCATION(RC_FIRE_TEMPLE_MQ_LAVA_TORCH_POT_2, logic->HookshotOrBoomerang()), + LOCATION(RC_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_HOOKSHOT, logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_BOW, logic->HookshotOrBoomerang() && logic->CanUse(RG_FAIRY_BOW)), }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_MQ_BIG_LAVA_ROOM, true), @@ -676,7 +678,12 @@ void RegionTable_Init_FireTemple() { ENTRANCE(RR_FIRE_TEMPLE_MQ_SHORTCUT_ROOM_LOWER, true), }); - areaTable[RR_FIRE_TEMPLE_MQ_SHORTCUT_ROOM_LOWER] = Region("Fire Temple MQ Shortcut Room Lower", SCENE_FIRE_TEMPLE, {}, {}, { + areaTable[RR_FIRE_TEMPLE_MQ_SHORTCUT_ROOM_LOWER] = Region("Fire Temple MQ Shortcut Room Lower", SCENE_FIRE_TEMPLE, {}, { + //Locations + LOCATION(RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_1, logic->CanUse(RG_MEGATON_HAMMER)), + LOCATION(RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_2, logic->CanUse(RG_MEGATON_HAMMER)), + LOCATION(RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_3, logic->CanUse(RG_MEGATON_HAMMER)), + }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_MQ_SHORTCUT_ROOM_MID, (logic->HasFireSource() && (logic->IsAdult || (logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_CLIMB)))) || (ctx->GetTrickOption(RT_FIRE_MQ_CLIMB) && logic->CanUse(RG_HOVER_BOOTS) && logic->HasItem(RG_CLIMB))), @@ -697,7 +704,10 @@ void RegionTable_Init_FireTemple() { ENTRANCE(RR_FIRE_TEMPLE_MQ_LOWER_LIZALFOS_MAZE, true), }); - areaTable[RR_FIRE_TEMPLE_MQ_LOWER_LIZALFOS_MAZE] = Region("Fire Temple MQ Lower Lizalfos Maze", SCENE_FIRE_TEMPLE, {}, {}, { + areaTable[RR_FIRE_TEMPLE_MQ_LOWER_LIZALFOS_MAZE] = Region("Fire Temple MQ Lower Lizalfos Maze", SCENE_FIRE_TEMPLE, {}, { + //Locations + LOCATION(RC_FIRE_TEMPLE_MQ_WONDER_LIZALFOS_MAZE, logic->CanUse(RG_HOOKSHOT)), + }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_MQ_SHORTCUT_ROOM_LOWER, true), //Explosives can also reach this room. Chus is relatively simple, they need to detonate on the first horizontal bar up from the floor while horizontally near the switch, but bombs are much harder @@ -797,7 +807,13 @@ void RegionTable_Init_FireTemple() { areaTable[RR_FIRE_TEMPLE_MQ_TORCH_SLUG_CLIMB] = Region("Fire Temple MQ Torch Slug Climb", SCENE_FIRE_TEMPLE, { //Events EVENT_ACCESS(LOGIC_FAIRY_ACCESS, logic->CanUse(RG_HOOKSHOT)), - }, {}, { + }, { + //Locations + LOCATION(RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_1, logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_2, logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_1, logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_2, logic->CanUse(RG_HOOKSHOT)), + }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_MQ_ABOVE_MAZE, true), ENTRANCE(RR_FIRE_TEMPLE_MQ_BURNING_BLOCK, logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_CLIMB)), @@ -842,6 +858,7 @@ void RegionTable_Init_FireTemple() { LOCATION(RC_FIRE_TEMPLE_MQ_LAVA_TORCH_CRATE_4, logic->CanBreakCrates() && logic->FireTimer() >= 16), LOCATION(RC_FIRE_TEMPLE_MQ_LAVA_TORCH_SMALL_CRATE_2, logic->CanBreakSmallCrates() && logic->FireTimer() >= 24), LOCATION(RC_FIRE_TEMPLE_MQ_LAVA_TORCH_SMALL_CRATE_3, logic->CanBreakSmallCrates() && logic->FireTimer() >= 16), + LOCATION(RC_FIRE_TEMPLE_MQ_WONDER_TORCH_ROOM, logic->CanUse(RG_HOOKSHOT) && logic->FireTimer() >= 16), }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_MQ_HIGH_TORCH_ROOM_ABOVE_CAGE, (logic->IsAdult || logic->CanUse(RG_HOOKSHOT)) && logic->FireTimer() >= 24), @@ -945,6 +962,7 @@ void RegionTable_Init_FireTemple() { LOCATION(RC_FIRE_TEMPLE_MQ_PAST_FIRE_MAZE_SOUTH_POT, logic->CanUse(RG_BOOMERANG)), LOCATION(RC_FIRE_TEMPLE_MQ_FIRE_MAZE_NORTHMOST_POT, logic->CanBreakPots()), LOCATION(RC_FIRE_TEMPLE_MQ_FIRE_MAZE_NORTHWEST_POT, logic->CanBreakPots()), + LOCATION(RC_FIRE_TEMPLE_MQ_WONDER_FIRE_MAZE, logic->CanUse(RG_LONGSHOT) && logic->CanUse(RG_BOOMERANG)), }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_MQ_GS_LIZALFOS_ROOM, true), @@ -972,6 +990,7 @@ void RegionTable_Init_FireTemple() { LOCATION(RC_FIRE_TEMPLE_MQ_PAST_FIRE_MAZE_SOUTH_POT, logic->CanBreakPots()), LOCATION(RC_FIRE_TEMPLE_MQ_PAST_FIRE_MAZE_NORTH_POT, logic->CanBreakPots()), LOCATION(RC_FIRE_TEMPLE_MQ_FIRE_MAZE_NORTHWEST_POT, logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_FIRE_TEMPLE_MQ_WONDER_FIRE_MAZE, logic->CanUse(RG_HOOKSHOT)), }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_MQ_3F_FLARE_DANCER, true), @@ -992,7 +1011,10 @@ void RegionTable_Init_FireTemple() { ENTRANCE(RR_FIRE_TEMPLE_MQ_LOCKED_CLIMB, true), }); - areaTable[RR_FIRE_TEMPLE_MQ_LOCKED_CLIMB] = Region("Fire Temple MQ Locked Climb", SCENE_FIRE_TEMPLE, {}, {}, { + areaTable[RR_FIRE_TEMPLE_MQ_LOCKED_CLIMB] = Region("Fire Temple MQ Locked Climb", SCENE_FIRE_TEMPLE, {}, { + //Locations + LOCATION(RC_FIRE_TEMPLE_MQ_WONDER_AFTER_FLARE_DANCER, logic->CanUse(RG_HOOKSHOT)), + }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_MQ_3F_FLARE_DANCER, true), ENTRANCE(RR_FIRE_TEMPLE_MQ_NARROW_STAIRS_ROOM, logic->SmallKeys(SCENE_FIRE_TEMPLE, 4) && logic->HasItem(RG_CLIMB)), @@ -1032,7 +1054,10 @@ void RegionTable_Init_FireTemple() { ENTRANCE(RR_FIRE_TEMPLE_MQ_NARROW_STAIRS_4F, logic->SmallKeys(SCENE_FIRE_TEMPLE, 5)), }); - areaTable[RR_FIRE_TEMPLE_MQ_BASE_OF_COLLAPSING_STAIRS] = Region("Fire Temple MQ Base of Collapsing Stairs", SCENE_FIRE_TEMPLE, {}, {}, { + areaTable[RR_FIRE_TEMPLE_MQ_BASE_OF_COLLAPSING_STAIRS] = Region("Fire Temple MQ Base of Collapsing Stairs", SCENE_FIRE_TEMPLE, {}, { + //Locations + LOCATION(RC_FIRE_TEMPLE_MQ_WONDER_STAIRCASE, logic->Get(LOGIC_FIRE_HIT_STAIRS) && logic->CanUse(RG_HOOKSHOT)), + }, { //Exits ENTRANCE(RR_FIRE_TEMPLE_MQ_TOP_OF_COLLAPSING_STAIRS, logic->Get(LOGIC_FIRE_HIT_STAIRS) && logic->IsAdult), ENTRANCE(RR_FIRE_TEMPLE_MQ_ABOVE_FIRE_MAZE, logic->CanUse(RG_HOOKSHOT)), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp index b34e060d07d..cc4c00db2bd 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp @@ -72,7 +72,7 @@ void RegionTable_Init_GanonsCastle() { }); areaTable[RR_GANONS_CASTLE_FOREST_TRIAL_BEAMOS_ROOM] = Region("Ganon's Castle Forest Trial Beamos Room", SCENE_INSIDE_GANONS_CASTLE, { - EVENT_ACCESS(LOGIC_FOREST_TRIAL_SILVER_RUPEES, logic->IsAdult || logic->CanUse(RG_HOOKSHOT)), // child can get these by voiding after switch + EVENT_ACCESS(LOGIC_FOREST_TRIAL_SILVER_RUPEES, logic->IsAdult || logic->CanUse(RG_HOOKSHOT) || (logic->IsChild && ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), // child can get these by voiding after switch }, {}, { //Exits ENTRANCE(RR_GANONS_CASTLE_FOREST_TRIAL_WOLFOS_ROOM, true), @@ -154,8 +154,34 @@ void RegionTable_Init_GanonsCastle() { EVENT_ACCESS(LOGIC_BLUE_FIRE_ACCESS, logic->CanClearStalagmite() || ctx->GetTrickOption(RT_ICE_STALAGMITE_CLIP)), }, { //Locations - LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_LEFT_CHEST, logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_RIGHT_CHEST, logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_LEFT_CHEST, logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_RIGHT_CHEST, logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_3, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_4, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_5, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_6, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_7, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_8, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_9, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_10, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_1, true), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_2, true), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_3, true), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_4, true), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_5, true), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_6, true), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_7, true), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_8, true), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_9, true), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_10, true), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_11, true), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_DOOR_RED_ICE, logic->BlueFire()), }, { //Exits ENTRANCE(RR_GANONS_CASTLE_MAIN, true), @@ -165,16 +191,25 @@ void RegionTable_Init_GanonsCastle() { areaTable[RR_GANONS_CASTLE_WATER_TRIAL_BLOCK_ROOM] = Region("Ganon's Castle Water Trial Block Room", SCENE_INSIDE_GANONS_CASTLE, { //Events EVENT_ACCESS(LOGIC_FAIRY_ACCESS, logic->CanBreakPots()), - EVENT_ACCESS(LOGIC_WATER_TRIAL_RUSTED_SWITCH, logic->IsAdult && (logic->HasItem(RG_POWER_BRACELET) || logic->CanMiddairGroundJump()) && - (logic->BlueFire() || ctx->GetTrickOption(RT_VISIBLE_COLLISION)) && - logic->CanUse(RG_MEGATON_HAMMER)), }, { //Locations - LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_POT_3, logic->CanBreakPots()), + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_POT_3, logic->CanBreakPots()), }, { //Exits - ENTRANCE(RR_GANONS_CASTLE_WATER_TRIAL_BLUE_FIRE_ROOM, true), - ENTRANCE(RR_GANONS_CASTLE_WATER_TRIAL_BLOCK_ROOM_END, logic->IsAdult || (logic->HasItem(RG_POWER_BRACELET) && logic->CanUse(RG_HOVER_BOOTS)) || logic->CanGroundJump() || ctx->GetTrickOption(RT_SLIDE_JUMP)), + ENTRANCE(RR_GANONS_CASTLE_WATER_TRIAL_BLUE_FIRE_ROOM, true), + ENTRANCE(RR_GANONS_CASTLE_WATER_TRIAL_BLOCK_ROOM_SWITCH, logic->IsAdult && (logic->HasItem(RG_POWER_BRACELET) || logic->CanMiddairGroundJump())), + ENTRANCE(RR_GANONS_CASTLE_WATER_TRIAL_BLOCK_ROOM_END, logic->IsAdult || (logic->HasItem(RG_POWER_BRACELET) && logic->CanUse(RG_HOVER_BOOTS)) || logic->CanGroundJump() || ctx->GetTrickOption(RT_SLIDE_JUMP)), + }); + + areaTable[RR_GANONS_CASTLE_WATER_TRIAL_BLOCK_ROOM_SWITCH] = Region("Ganon's Castle Water Trial Block Room Switch", SCENE_INSIDE_GANONS_CASTLE, { + //Events + EVENT_ACCESS(LOGIC_WATER_TRIAL_RUSTED_SWITCH, (logic->BlueFire() || ctx->GetTrickOption(RT_VISIBLE_COLLISION)) && logic->CanUse(RG_MEGATON_HAMMER)), + }, { + //Locations + LOCATION(RC_GANONS_CASTLE_WATER_TRIAL_RUSTED_SWITCH_RED_ICE, logic->BlueFire()), + }, { + //Exits + ENTRANCE(RR_GANONS_CASTLE_WATER_TRIAL_BLOCK_ROOM, true), }); areaTable[RR_GANONS_CASTLE_WATER_TRIAL_BLOCK_ROOM_END] = Region("Ganon's Castle Water Trial Block Room End", SCENE_INSIDE_GANONS_CASTLE, {}, {}, { @@ -502,8 +537,29 @@ void RegionTable_Init_GanonsCastle() { EVENT_ACCESS(LOGIC_BLUE_FIRE_ACCESS, logic->CanJumpslash() || logic->HasExplosives()), // bow can also hit at right angle }, { //Locations - LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_CHEST, logic->BlueFire() && logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_HEART, logic->BlueFire()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_CHEST, logic->BlueFire() && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_HEART, logic->BlueFire()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_1, true), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_2, true), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_3, true), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_1, true), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_2, true), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_3, true), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_3, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_4, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_3, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_4, logic->CanClearStalagmite()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_LEFT_RED_ICE, logic->BlueFire()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_RIGHT_RED_ICE, logic->BlueFire()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_BACK_LEFT_RED_ICE, logic->BlueFire()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_1, logic->BlueFire()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_2, logic->BlueFire()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_3, logic->BlueFire()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_4, logic->BlueFire()), }, { //Exits ENTRANCE(RR_GANONS_CASTLE_MQ_MAIN, true), @@ -513,16 +569,31 @@ void RegionTable_Init_GanonsCastle() { areaTable[RR_GANONS_CASTLE_MQ_WATER_TRIAL_BLOCK_ROOM] = Region("Ganon's Castle MQ Water Trial Block Room", SCENE_INSIDE_GANONS_CASTLE, { //Events EVENT_ACCESS(LOGIC_WATER_TRIAL_MQ_SILVER_RUPEES, logic->IsAdult && (logic->HasItem(RG_POWER_BRACELET) || logic->CanMiddairGroundJump()) && logic->BlueFire()), - EVENT_ACCESS(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE, (ctx->GetOption(RSK_BLUE_FIRE_ARROWS) && logic->CanUse(RG_ICE_ARROWS)) || (logic->IsAdult || logic->CanUse(RG_HOVER_BOOTS)) && logic->HasItem(RG_POWER_BRACELET) && logic->CanUse(RG_BOTTLE_WITH_BLUE_FIRE)), - }, {}, { + EVENT_ACCESS(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE, (ctx->GetOption(RSK_BLUE_FIRE_ARROWS) && logic->CanUse(RG_ICE_ARROWS)) || ((logic->IsAdult || logic->CanUse(RG_HOVER_BOOTS) || (ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS)/* && logic->CanUse(RG_ROLL)*/)) && logic->HasItem(RG_POWER_BRACELET) && logic->CanUse(RG_BOTTLE_WITH_BLUE_FIRE))), + }, { + //Locations + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SILVER_RUPEE_RED_ICE, logic->IsAdult && (logic->HasItem(RG_POWER_BRACELET) || logic->CanMiddairGroundJump()) && logic->BlueFire()), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_1, logic->Get(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE) && logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_2, logic->Get(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE) && logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_3, logic->Get(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE) && logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_4, logic->Get(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE) && logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_5, logic->Get(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE) && logic->CanUse(RG_BOOMERANG)), + }, { //Exits ENTRANCE(RR_GANONS_CASTLE_MQ_WATER_TRIAL_GEYSER_ROOM, logic->SmallKeys(SCENE_INSIDE_GANONS_CASTLE, 3)), - ENTRANCE(RR_GANONS_CASTLE_MQ_WATER_TRIAL_BLOCK_ROOM_END, logic->Get(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE) && (logic->IsAdult || (logic->CanUse(RG_HOVER_BOOTS) && logic->HasItem(RG_POWER_BRACELET)) || logic->CanGroundJump() || ctx->GetTrickOption(RT_SLIDE_JUMP))), + ENTRANCE(RR_GANONS_CASTLE_MQ_WATER_TRIAL_BLOCK_ROOM_END, logic->Get(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE) && (logic->IsAdult || ((logic->CanUse(RG_HOVER_BOOTS) || (ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS)/* && logic->CanUse(RG_ROLL)*/)) && logic->HasItem(RG_POWER_BRACELET)) || logic->CanGroundJump() || ctx->GetTrickOption(RT_SLIDE_JUMP))), }); areaTable[RR_GANONS_CASTLE_MQ_WATER_TRIAL_BLOCK_ROOM_END] = Region("Ganon's Castle MQ Water Trial Block Room End", SCENE_INSIDE_GANONS_CASTLE, { EVENT_ACCESS(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE, logic->BlueFire()), - }, {}, { + }, { + //Locations + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_1, logic->Get(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE)), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_2, logic->Get(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE)), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_3, logic->Get(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE)), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_4, logic->Get(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE)), + LOCATION(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_5, logic->Get(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE)), + }, { ENTRANCE(RR_GANONS_CASTLE_MQ_WATER_TRIAL_BLOCK_ROOM, logic->Get(LOGIC_WATER_TRIAL_MQ_MELTED_FINAL_DOOR_RED_ICE)), ENTRANCE(RR_GANONS_CASTLE_MQ_WATER_TRIAL_FINAL_ROOM, logic->Get(LOGIC_WATER_TRIAL_MQ_SILVER_RUPEES)), }); @@ -564,7 +635,10 @@ void RegionTable_Init_GanonsCastle() { //Events //A torch run from RR_GANONS_CASTLE_MQ_SHADOW_TRIAL_STARTING_LEDGE is possible but tight, so would be a trick EVENT_ACCESS(LOGIC_SHADOW_TRIAL_FIRST_CHEST, logic->CanDetonateUprightBombFlower()), - }, {}, { + }, { + //Locations + LOCATION(RC_GANONS_CASTLE_MQ_WONDER_SHADOW_TRIAL, (logic->CanDetonateBombFlowers() || (ctx->GetTrickOption(RT_BLUE_FIRE_MUD_WALLS) && logic->CanUse(RG_BOTTLE_WITH_BLUE_FIRE))) && (logic->TakeDamage() || logic->CanUse(RG_NAYRUS_LOVE))), + }, { //Exits ENTRANCE(RR_GANONS_CASTLE_MQ_SHADOW_TRIAL_CHEST_PLATFORM, logic->IsAdult || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS)), ENTRANCE(RR_GANONS_CASTLE_MQ_SHADOW_TRIAL_BEAMOS_TORCH, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp index dd8b6c85a03..e3b232b520f 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp @@ -107,7 +107,10 @@ void RegionTable_Init_GerudoTrainingGround() { ENTRANCE(RR_GERUDO_TRAINING_GROUND_BEHIND_HEAVY_BLOCK, true), }); - areaTable[RR_GERUDO_TRAINING_GROUND_EYE_STATUE_UPPER] = Region("Gerudo Training Ground Eye Statue Upper", SCENE_GERUDO_TRAINING_GROUND, {}, {}, { + areaTable[RR_GERUDO_TRAINING_GROUND_EYE_STATUE_UPPER] = Region("Gerudo Training Ground Eye Statue Upper", SCENE_GERUDO_TRAINING_GROUND, {}, { + //Locations + LOCATION(RC_GERUDO_TRAINING_GROUND_WONDER_EYE_STATUE_ROOM, logic->CanUse(RG_HOVER_BOOTS) || (logic->IsAdult && ctx->GetTrickOption(RT_GTG_STATUE_JUMP))), // Shuffle roll: Jumpslash doesn't require roll, jump only does + }, { //Exits ENTRANCE(RR_GERUDO_TRAINING_GROUND_EYE_STATUE_LOWER, true), ENTRANCE(RR_GERUDO_TRAINING_GROUND_HEAVY_BLOCK_ROOM_UPPER, true), @@ -137,6 +140,7 @@ void RegionTable_Init_GerudoTrainingGround() { //Locations LOCATION(RC_GERUDO_TRAINING_GROUND_HAMMER_ROOM_CLEAR_CHEST, logic->CanAttack() && logic->HasItem(RG_OPEN_CHEST)), LOCATION(RC_GERUDO_TRAINING_GROUND_HAMMER_ROOM_SWITCH_CHEST, (logic->CanUse(RG_MEGATON_HAMMER) || (logic->TakeDamage() && ctx->GetTrickOption(RT_FIRE_RINGS))) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_GERUDO_TRAINING_GROUND_WONDER_TORCH_SLUGS_ROOM, logic->CanUse(RG_FAIRY_BOW)), }, { //Exits ENTRANCE(RR_GERUDO_TRAINING_GROUND_EYE_STATUE_LOWER, logic->CanUse(RG_MEGATON_HAMMER) && logic->CanUse(RG_FAIRY_BOW)), @@ -172,6 +176,7 @@ void RegionTable_Init_GerudoTrainingGround() { LOCATION(RC_GERUDO_TRAINING_GROUND_BEAMOS_CHEST, logic->CanKillEnemy(RE_BEAMOS) && logic->CanKillEnemy(RE_DINOLFOS, ED_CLOSE, true, 2, true) && logic->HasItem(RG_OPEN_CHEST)), LOCATION(RC_GERUDO_TRAINING_GROUND_BEAMOS_SOUTH_HEART, true), LOCATION(RC_GERUDO_TRAINING_GROUND_BEAMOS_EAST_HEART, true), + LOCATION(RC_GERUDO_TRAINING_GROUND_WONDER_BEAMOS_ROOM, logic->CanUse(RG_FAIRY_BOW)), }, { //Exits ENTRANCE(RR_GERUDO_TRAINING_GROUND_LOBBY, true), @@ -240,7 +245,22 @@ void RegionTable_Init_GerudoTrainingGround() { ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM, AnyAgeTime([]{return logic->CanKillEnemy(RE_IRON_KNUCKLE);})), }); - areaTable[RR_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM] = Region("Gerudo Training Ground MQ Left Side", SCENE_GERUDO_TRAINING_GROUND, {}, {}, { + areaTable[RR_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM] = Region("Gerudo Training Ground MQ Left Side", SCENE_GERUDO_TRAINING_GROUND, {}, { + //Locations + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_3, logic->CanClearStalagmite()), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_4, logic->CanClearStalagmite()), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_5, logic->CanClearStalagmite()), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_1, true), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_2, true), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_3, true), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_4, true), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_LEFT_STALACTITE, true), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_1, true), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_2, true), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_3, true), + }, { //Exits ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_SAND_ROOM, true), ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM, AnyAgeTime([]{return logic->CanUse(RG_LONGSHOT) || ctx->GetTrickOption(RT_GTG_MQ_WITHOUT_HOOKSHOT) || (ctx->GetTrickOption(RT_GTG_MQ_WITH_HOOKSHOT) && logic->IsAdult && logic->CanJumpslash() && logic->CanUse(RG_HOOKSHOT));})), @@ -257,9 +277,9 @@ void RegionTable_Init_GerudoTrainingGround() { LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_BEFORE_HEAVY_BLOCK_CHEST, logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2, true) && logic->HasItem(RG_OPEN_CHEST)), }, { //Exits - ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM, true), - ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_BEHIND_BLOCK, logic->Get(LOGIC_GTG_PUSHED_HEAVY_BLOCK)), - ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM_LEDGE, logic->IsAdult && AnyAgeTime([]{return logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2, true);}) && (ctx->GetTrickOption(RT_LENS_GTG_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->BlueFire() && (logic->CanUse(RG_SONG_OF_TIME) || (ctx->GetTrickOption(RT_GTG_FAKE_WALL) && logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS)) || (logic->IsAdult && logic->CanGroundJump()))), + ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM, true), + ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_BEHIND_BLOCK, logic->Get(LOGIC_GTG_PUSHED_HEAVY_BLOCK)), + ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM_ALCOVE, logic->IsAdult && (ctx->GetTrickOption(RT_LENS_GTG_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->BlueFire() && (logic->CanUse(RG_SONG_OF_TIME) || (ctx->GetTrickOption(RT_GTG_FAKE_WALL) && logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS)) || (logic->IsAdult && logic->CanGroundJump()))), }); areaTable[RR_GERUDO_TRAINING_GROUND_MQ_BEHIND_BLOCK] = Region("Gerudo Training Ground MQ Behind Block", SCENE_GERUDO_TRAINING_GROUND, {}, {}, { @@ -275,12 +295,23 @@ void RegionTable_Init_GerudoTrainingGround() { ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_BEHIND_BLOCK, true), }); - areaTable[RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM_LEDGE] = Region("Gerudo Training Ground MQ Statue Room Ledge", SCENE_GERUDO_TRAINING_GROUND, {}, {}, { - //Exits + areaTable[RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM_ALCOVE] = Region("Gerudo Training Ground MQ Stalfos Room Alcove", SCENE_GERUDO_TRAINING_GROUND, {}, { + //Locations + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM_RED_ICE, logic->BlueFire()), + }, { ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM, true), + ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM_LEDGE, logic->Get(LOGIC_GTG_UNLOCKED_DOOR_BEHIND_HEAVY_BLOCK)), + }); + + areaTable[RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM_LEDGE] = Region("Gerudo Training Ground MQ Statue Room Ledge", SCENE_GERUDO_TRAINING_GROUND, {}, { + //Locations + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_WONDER_EYE_STATUE, logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_HOVER_BOOTS) || (logic->IsAdult && ctx->GetTrickOption(RT_GTG_STATUE_JUMP))), // Shuffle roll: Jumpslash doesn't require roll, jump only does + }, { + //Exits + ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM_ALCOVE, true), //implies dropping down to hit the switch. Using swords, especially master, is a bit awkward, may be trick worthy, but is only relevant with other tricks - ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_MAGENTA_FIRE_ROOM, AnyAgeTime([]{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_MASTER_SWORD) || logic->CanUse(RG_BIGGORON_SWORD) || logic->CanUse(RG_STICKS) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG);})), - ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM, true), + ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_MAGENTA_FIRE_ROOM, AnyAgeTime([]{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_MASTER_SWORD) || logic->CanUse(RG_BIGGORON_SWORD) || logic->CanUse(RG_STICKS) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG);})), + ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM, true), }); areaTable[RR_GERUDO_TRAINING_GROUND_MQ_MAGENTA_FIRE_ROOM] = Region("Gerudo Training Ground MQ Magenta Fire Room", SCENE_GERUDO_TRAINING_GROUND, {}, { @@ -400,7 +431,8 @@ void RegionTable_Init_GerudoTrainingGround() { //is logic->CanKillEnemy(RE_DINOLFOS, ED_CLOSE, true, 2, true) && logic->CanKillEnemy(RE_ARMOS, ED_CLOSE, true, 1, true) broken down to exclude sticks, as it takes too many to clear the room //Proper enemy kill room ammo logic is needed to handle this room //some combinations may be impossible without taking damage, keep an eye out for issues here - LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_DINOLFOS_CHEST, (logic->CanUse(RG_MASTER_SWORD) || logic->CanUse(RG_BIGGORON_SWORD) || logic->CanUse(RG_MEGATON_HAMMER) || logic->CanUse(RG_FAIRY_BOW) || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT)))) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_DINOLFOS_CHEST, (logic->CanUse(RG_MASTER_SWORD) || logic->CanUse(RG_BIGGORON_SWORD) || logic->CanUse(RG_MEGATON_HAMMER) || logic->CanUse(RG_FAIRY_BOW) || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT)))) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_WONDER_DINOLFOS_ROOM, logic->CanUse(RG_FAIRY_BOW)), }, { //Exits ENTRANCE(RR_GERUDO_TRAINING_GROUND_MQ_LOBBY, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp index 176ffaee881..0ff80f200b6 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ice_cavern.cpp @@ -18,8 +18,20 @@ void RegionTable_Init_IceCavern() { areaTable[RR_ICE_CAVERN_BEGINNING] = Region("Ice Cavern Beginning", SCENE_ICE_CAVERN, {}, { //Locations - LOCATION(RC_ICE_CAVERN_ENTRANCE_STORMS_FAIRY, logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_ICE_CAVERN_LOBBY_RUPEE, logic->BlueFire()), // can get with rang trick + LOCATION(RC_ICE_CAVERN_ENTRANCE_STORMS_FAIRY, logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_ICE_CAVERN_LOBBY_RUPEE, logic->BlueFire()), // can get with rang trick + LOCATION(RC_ICE_CAVERN_ENTRANCE_LEFT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_ENTRANCE_MIDDLE_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_ENTRANCE_RIGHT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_ENTRANCE_STALACTITE_1, true), + LOCATION(RC_ICE_CAVERN_ENTRANCE_STALACTITE_2, true), + LOCATION(RC_ICE_CAVERN_LOBBY_STALACTITE, true), + LOCATION(RC_ICE_CAVERN_LOBBY_LEFT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_LOBBY_MIDDLE_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_LOBBY_RIGHT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_ENTRANCE_RED_ICE, logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_LOBBY_LEFT_RED_ICE, logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_LOBBY_RIGHT_RED_ICE, logic->BlueFire()), }, { //Exits ENTRANCE(RR_ICE_CAVERN_ENTRYWAY, true), @@ -29,12 +41,22 @@ void RegionTable_Init_IceCavern() { areaTable[RR_ICE_CAVERN_HUB] = Region("Ice Cavern Hub", SCENE_ICE_CAVERN, {}, { //Locations - LOCATION(RC_ICE_CAVERN_GS_SPINNING_SCYTHE_ROOM, logic->HookshotOrBoomerang()), - LOCATION(RC_ICE_CAVERN_HALL_POT_1, logic->CanBreakPots()), - LOCATION(RC_ICE_CAVERN_HALL_POT_2, logic->CanBreakPots()), - LOCATION(RC_ICE_CAVERN_SPINNING_BLADE_POT_1, logic->CanBreakPots()), - LOCATION(RC_ICE_CAVERN_SPINNING_BLADE_POT_2, logic->CanBreakPots()), - LOCATION(RC_ICE_CAVERN_SPINNING_BLADE_POT_3, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_GS_SPINNING_SCYTHE_ROOM, logic->HookshotOrBoomerang()), + LOCATION(RC_ICE_CAVERN_HALL_POT_1, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_HALL_POT_2, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_SPINNING_BLADE_POT_1, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_SPINNING_BLADE_POT_2, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_SPINNING_BLADE_POT_3, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_AFTER_LOBBY_STALACTITE, true), + LOCATION(RC_ICE_CAVERN_AFTER_LOBBY_LEFT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_AFTER_LOBBY_CENTER_LEFT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_AFTER_LOBBY_CENTER_RIGHT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_AFTER_LOBBY_RIGHT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_SPINNING_BLADE_LEFT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_SPINNING_BLADE_MIDDLE_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_SPINNING_BLADE_RIGHT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_SPINNING_BLADE_EAST_RED_ICE, logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_SPINNING_BLADE_WEST_RED_ICE, logic->BlueFire()), }, { //Exits ENTRANCE(RR_ICE_CAVERN_BEGINNING, true), @@ -49,14 +71,24 @@ void RegionTable_Init_IceCavern() { EVENT_ACCESS(LOGIC_BLUE_FIRE_ACCESS, true), }, { //Locations - LOCATION(RC_ICE_CAVERN_MAP_CHEST, logic->BlueFire() && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_ICE_CAVERN_MAP_CHEST, logic->BlueFire() && logic->HasItem(RG_OPEN_CHEST)), // Bow extension is possible, but very precise: X = 403, Z = 2062-3, Rot = -11475, needs a setup and is its own trick - LOCATION(RC_ICE_CAVERN_FROZEN_POT_1, (logic->CanBreakPots() && logic->BlueFire()) || logic->HasExplosives() || - (ctx->GetTrickOption(RT_VISIBLE_COLLISION) && logic->CanJumpslash()) || - (ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->CanUse(RG_HOOKSHOT))), - LOCATION(RC_ICE_CAVERN_MAP_ROOM_LEFT_HEART, true), - LOCATION(RC_ICE_CAVERN_MAP_ROOM_MIDDLE_HEART, true), - LOCATION(RC_ICE_CAVERN_MAP_ROOM_RIGHT_HEART, true), + LOCATION(RC_ICE_CAVERN_FROZEN_POT_1, (logic->CanBreakPots() && logic->BlueFire()) || logic->HasExplosives() || + (ctx->GetTrickOption(RT_VISIBLE_COLLISION) && logic->CanJumpslash()) || + (ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->CanUse(RG_HOOKSHOT))), + LOCATION(RC_ICE_CAVERN_MAP_ROOM_LEFT_HEART, true), + LOCATION(RC_ICE_CAVERN_MAP_ROOM_MIDDLE_HEART, true), + LOCATION(RC_ICE_CAVERN_MAP_ROOM_RIGHT_HEART, true), + LOCATION(RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_1, true), + LOCATION(RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_2, true), + LOCATION(RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_3, true), + LOCATION(RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_4, true), + LOCATION(RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_5, true), + LOCATION(RC_ICE_CAVERN_MAP_HALLWAY_LEFT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MAP_HALLWAY_MIDDLE_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MAP_HALLWAY_RIGHT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MAP_ROOM_POT_RED_ICE, logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_MAP_ROOM_CHEST_RED_ICE, logic->BlueFire()), }, { //Exits ENTRANCE(RR_ICE_CAVERN_HUB, true), @@ -67,9 +99,31 @@ void RegionTable_Init_IceCavern() { EVENT_ACCESS(LOGIC_BLUE_FIRE_ACCESS, true), }, { //Locations - LOCATION(RC_ICE_CAVERN_COMPASS_CHEST, (logic->IsChild || logic->CanClearStalagmite() || ctx->GetTrickOption(RT_ICE_STALAGMITE_CLIP)) && logic->BlueFire() && logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_ICE_CAVERN_FREESTANDING_POH, (logic->CanClearStalagmite() || ctx->GetTrickOption(RT_ICE_STALAGMITE_CLIP)) && logic->BlueFire()), // can skip blue fire with rang trick - LOCATION(RC_ICE_CAVERN_GS_HEART_PIECE_ROOM, logic->HookshotOrBoomerang()), + LOCATION(RC_ICE_CAVERN_COMPASS_CHEST, (logic->IsChild || logic->CanClearStalagmite() || ctx->GetTrickOption(RT_ICE_STALAGMITE_CLIP)) && logic->BlueFire() && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_ICE_CAVERN_FREESTANDING_POH, (logic->CanClearStalagmite() || ctx->GetTrickOption(RT_ICE_STALAGMITE_CLIP)) && logic->BlueFire()), // can skip blue fire with rang trick + LOCATION(RC_ICE_CAVERN_GS_HEART_PIECE_ROOM, logic->HookshotOrBoomerang()), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_1, true), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_2, true), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_3, true), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_4, true), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_5, true), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_1, logic->CanClearStalagmite() || ctx->GetTrickOption(RT_ICE_STALAGMITE_CLIP)), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_2, logic->CanClearStalagmite() || ctx->GetTrickOption(RT_ICE_STALAGMITE_CLIP)), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_3, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_4, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_5, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_6, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_3, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_3, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_4, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_FREESTANDING_RED_ICE, (logic->CanClearStalagmite() || ctx->GetTrickOption(RT_ICE_STALAGMITE_CLIP)) && logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_HEART_PIECE_ROOM_CHEST_RED_ICE, (logic->IsChild || logic->CanClearStalagmite() || ctx->GetTrickOption(RT_ICE_STALAGMITE_CLIP)) && logic->BlueFire()), }, { //Exits ENTRANCE(RR_ICE_CAVERN_HUB, true), @@ -78,15 +132,24 @@ void RegionTable_Init_IceCavern() { areaTable[RR_ICE_CAVERN_BLOCK_ROOM] = Region("Ice Cavern Block Room", SCENE_ICE_CAVERN, {}, { //Locations // trick involves backflip, could be merged into general trick - LOCATION(RC_ICE_CAVERN_GS_PUSH_BLOCK_ROOM, logic->HookshotOrBoomerang() || (ctx->GetTrickOption(RT_ICE_BLOCK_GS) && logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS) && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_SHORT_JUMPSLASH))), - LOCATION(RC_ICE_CAVERN_SLIDING_BLOCK_RUPEE_1, logic->CanUse(RG_BOOMERANG)), - LOCATION(RC_ICE_CAVERN_SLIDING_BLOCK_RUPEE_2, logic->CanUse(RG_BOOMERANG)), - LOCATION(RC_ICE_CAVERN_SLIDING_BLOCK_RUPEE_3, logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_ICE_CAVERN_GS_PUSH_BLOCK_ROOM, logic->HookshotOrBoomerang() || (ctx->GetTrickOption(RT_ICE_BLOCK_GS) && logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS) && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_SHORT_JUMPSLASH))), + LOCATION(RC_ICE_CAVERN_SLIDING_BLOCK_RUPEE_1, logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_ICE_CAVERN_SLIDING_BLOCK_RUPEE_2, logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_ICE_CAVERN_SLIDING_BLOCK_RUPEE_3, logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_ICE_CAVERN_PUSH_BLOCK_HALL_LEFT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_LEFT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_RIGHT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_PUSH_BLOCK_HALL_RIGHT_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_1, true), + LOCATION(RC_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_2, true), + LOCATION(RC_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_3, true), + LOCATION(RC_ICE_CAVERN_SILVER_RUPEE_RED_ICE, (logic->HasItem(RG_POWER_BRACELET) || (logic->IsAdult && (logic->CanGroundJump() || ctx->GetTrickOption(RT_SLIDE_JUMP)))) && logic->BlueFire()), }, { //Exits ENTRANCE(RR_ICE_CAVERN_HUB, logic->CanClearStalagmite() || ctx->GetTrickOption(RT_ICE_STALAGMITE_CLIP)), ENTRANCE(RR_ICE_CAVERN_BLOCK_ROOM_BLUE_FIRE, logic->HasItem(RG_POWER_BRACELET) || (logic->IsAdult && (logic->CanGroundJump() || ctx->GetTrickOption(RT_SLIDE_JUMP)))), - ENTRANCE(RR_ICE_CAVERN_BEFORE_FINAL_ROOM, (logic->HasItem(RG_POWER_BRACELET) || (logic->IsAdult && (logic->CanGroundJump() || ctx->GetTrickOption(RT_SLIDE_JUMP)))) && AnyAgeTime([]{return logic->BlueFire();})), + ENTRANCE(RR_ICE_CAVERN_AFTER_BLOCK_ROOM, (logic->HasItem(RG_POWER_BRACELET) || (logic->IsAdult && (logic->CanGroundJump() || ctx->GetTrickOption(RT_SLIDE_JUMP)))) && AnyAgeTime([]{return logic->BlueFire();})), }); areaTable[RR_ICE_CAVERN_BLOCK_ROOM_BLUE_FIRE] = Region("Ice Cavern Block Room Blue Fire", SCENE_ICE_CAVERN, { @@ -102,16 +165,38 @@ void RegionTable_Init_IceCavern() { ENTRANCE(RR_ICE_CAVERN_BLOCK_ROOM, true), }); + areaTable[RR_ICE_CAVERN_AFTER_BLOCK_ROOM] = Region("Ice Cavern After Block Room", SCENE_ICE_CAVERN, {}, { + //Locations + LOCATION(RC_ICE_CAVERN_GS_PUSH_BLOCK_ROOM, ctx->GetTrickOption(RT_ICE_BLOCK_GS) && logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS) && logic->BlueFire() && logic->HasItem(RG_POWER_BRACELET)), + LOCATION(RC_ICE_CAVERN_NEAR_END_STALACTITE_1, true), + LOCATION(RC_ICE_CAVERN_NEAR_END_STALACTITE_2, true), + LOCATION(RC_ICE_CAVERN_NEAR_END_STALACTITE_3, true), + LOCATION(RC_ICE_CAVERN_NEAR_END_STALACTITE_4, true), + LOCATION(RC_ICE_CAVERN_NEAR_END_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_NEAR_END_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_NEAR_END_STALAGMITE_3, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_NEAR_END_STALAGMITE_4, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_NEAR_END_STALAGMITE_5, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_NEAR_END_STALAGMITE_6, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_NEAR_END_STALAGMITE_7, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_NEAR_END_STALAGMITE_8, logic->CanClearStalagmite()), + }, { + //Exits + ENTRANCE(RR_ICE_CAVERN_BLOCK_ROOM, true), + ENTRANCE(RR_ICE_CAVERN_BEFORE_FINAL_ROOM, AnyAgeTime([]{return logic->BlueFire();})), + }); + // this represents being past the red ice barricade, not just past the silver rupee door areaTable[RR_ICE_CAVERN_BEFORE_FINAL_ROOM] = Region("Ice Cavern Before Final Room", SCENE_ICE_CAVERN, {}, { //Locations - //Assumes RR_ICE_CAVERN_BLOCK_ROOM access - LOCATION(RC_ICE_CAVERN_GS_PUSH_BLOCK_ROOM, ctx->GetTrickOption(RT_ICE_BLOCK_GS) && logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS) && logic->BlueFire() && logic->HasItem(RG_POWER_BRACELET)), - LOCATION(RC_ICE_CAVERN_NEAR_END_POT_1, logic->CanBreakPots() && logic->BlueFire()), - LOCATION(RC_ICE_CAVERN_NEAR_END_POT_2, logic->CanBreakPots() && logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_NEAR_END_POT_1, logic->CanBreakPots() && logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_NEAR_END_POT_2, logic->CanBreakPots() && logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_NEAR_END_LEFT_RED_ICE, logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_NEAR_END_MIDDLE_RED_ICE, logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_NEAR_END_RIGHT_RED_ICE, logic->BlueFire()), }, { //Exits - ENTRANCE(RR_ICE_CAVERN_BLOCK_ROOM, AnyAgeTime([]{return logic->BlueFire();})), + ENTRANCE(RR_ICE_CAVERN_AFTER_BLOCK_ROOM, AnyAgeTime([]{return logic->BlueFire();})), ENTRANCE(RR_ICE_CAVERN_FINAL_ROOM, true), }); @@ -142,7 +227,11 @@ void RegionTable_Init_IceCavern() { areaTable[RR_ICE_CAVERN_MQ_BEGINNING] = Region("Ice Cavern MQ Beginning", SCENE_ICE_CAVERN, {}, { //Locations - LOCATION(RC_ICE_CAVERN_MQ_ENTRANCE_POT, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_MQ_ENTRANCE_POT, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_LOBBY_STALACTITE_1, true), + LOCATION(RC_ICE_CAVERN_MQ_LOBBY_STALACTITE_2, true), }, { //Exits ENTRANCE(RR_ICE_CAVERN_ENTRYWAY, true), @@ -156,12 +245,28 @@ void RegionTable_Init_IceCavern() { EVENT_ACCESS(LOGIC_FAIRY_ACCESS, logic->CanBreakPots()), }, { //Locations - LOCATION(RC_ICE_CAVERN_MQ_FIRST_CRYSTAL_POT_1, logic->CanBreakPots()), - LOCATION(RC_ICE_CAVERN_MQ_FIRST_CRYSTAL_POT_2, logic->CanBreakPots()), - LOCATION(RC_ICE_CAVERN_MQ_EARLY_WOLFOS_POT_1, logic->CanBreakPots()), - LOCATION(RC_ICE_CAVERN_MQ_EARLY_WOLFOS_POT_2, logic->CanBreakPots()), - LOCATION(RC_ICE_CAVERN_MQ_EARLY_WOLFOS_POT_3, logic->CanBreakPots()), - LOCATION(RC_ICE_CAVERN_MQ_EARLY_WOLFOS_POT_4, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_MQ_FIRST_CRYSTAL_POT_1, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_MQ_FIRST_CRYSTAL_POT_2, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_MQ_EARLY_WOLFOS_POT_1, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_MQ_EARLY_WOLFOS_POT_2, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_MQ_EARLY_WOLFOS_POT_3, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_MQ_EARLY_WOLFOS_POT_4, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_3, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_4, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_5, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_HUB_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_HUB_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_HUB_STALAGMITE_3, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_HUB_STALAGMITE_4, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_HUB_STALAGMITE_5, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_HUB_WEST_LEFT_RED_ICE, logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_MQ_HUB_WEST_MIDDLE_RED_ICE, logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_MQ_HUB_WEST_RIGHT_RED_ICE, logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_MQ_HUB_LEDGE_LEFT_RED_ICE, (logic->IsAdult /*|| logic->CanGroundJump()*/) && logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_MQ_HUB_LEDGE_MIDDLE_RED_ICE, (logic->IsAdult /*|| logic->CanGroundJump()*/) && logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_MQ_HUB_LEDGE_RIGHT_RED_ICE, (logic->IsAdult /*|| logic->CanGroundJump()*/) && logic->BlueFire()), }, { //Exits ENTRANCE(RR_ICE_CAVERN_MQ_MAP_ROOM, AnyAgeTime([]{return logic->CanKillEnemy(RE_WHITE_WOLFOS) && logic->CanKillEnemy(RE_FREEZARD);})), @@ -176,7 +281,17 @@ void RegionTable_Init_IceCavern() { EVENT_ACCESS(LOGIC_BLUE_FIRE_ACCESS, logic->IsChild || logic->CanClearStalagmite() || ctx->GetTrickOption(RT_ICE_STALAGMITE_CLIP)), }, { //Locations - LOCATION(RC_ICE_CAVERN_MQ_MAP_CHEST, logic->BlueFire() && AnyAgeTime([]{return logic->CanHitSwitch();}) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_ICE_CAVERN_MQ_MAP_CHEST, logic->BlueFire() && AnyAgeTime([]{return logic->CanHitSwitch();}) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_3, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_4, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_5, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_6, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_7, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_MAP_RED_ICE, logic->BlueFire()), }, {}); areaTable[RR_ICE_CAVERN_MQ_SCARECROW_ROOM] = Region("Ice Cavern MQ Scarecrow Room", SCENE_ICE_CAVERN, { @@ -185,8 +300,13 @@ void RegionTable_Init_IceCavern() { }, { //Locations //Implies being able to kill the skull if you hit the switch - LOCATION(RC_ICE_CAVERN_MQ_GS_ICE_BLOCK, (logic->BlueFire() && logic->HasItem(RG_POWER_BRACELET) && logic->CanKillEnemy(RE_GOLD_SKULLTULA)) || logic->CanHitSwitch(logic->IsAdult ? ED_LONG_JUMPSLASH : ED_BOMB_THROW)), - LOCATION(RC_ICE_CAVERN_MQ_GS_SCARECROW, logic->ReachScarecrow() || (logic->IsAdult && (logic->CanUse(RG_LONGSHOT) || logic->CanGroundJump() || ctx->GetTrickOption(RT_SLIDE_JUMP)))), + LOCATION(RC_ICE_CAVERN_MQ_GS_ICE_BLOCK, (logic->BlueFire() && logic->HasItem(RG_POWER_BRACELET) && logic->CanKillEnemy(RE_GOLD_SKULLTULA)) || logic->CanHitSwitch(logic->IsAdult ? ED_LONG_JUMPSLASH : ED_BOMB_THROW)), + LOCATION(RC_ICE_CAVERN_MQ_GS_SCARECROW, logic->ReachScarecrow() || (logic->IsAdult && (logic->CanUse(RG_LONGSHOT) || logic->CanGroundJump() || ctx->GetTrickOption(RT_SLIDE_JUMP)))), + LOCATION(RC_ICE_CAVERN_MQ_BEFORE_SCARECROW_STALAGMITE, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_SCARECROW_ROOM_STALACTITE, true), + LOCATION(RC_ICE_CAVERN_MQ_SCARECROW_LEFT_RED_ICE, logic->IsChild && ctx->GetOption(RSK_BLUE_FIRE_ARROWS) && logic->CanUse(RG_ICE_ARROWS) && logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_ICE_CAVERN_MQ_SCARECROW_MIDDLE_RED_ICE, logic->IsChild && ctx->GetOption(RSK_BLUE_FIRE_ARROWS) && logic->CanUse(RG_ICE_ARROWS) && logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_ICE_CAVERN_MQ_SCARECROW_RIGHT_RED_ICE, logic->IsChild && ctx->GetOption(RSK_BLUE_FIRE_ARROWS) && logic->CanUse(RG_ICE_ARROWS) && logic->CanUse(RG_BOOMERANG)), }, { //Exits ENTRANCE(RR_ICE_CAVERN_MQ_HUB, logic->BlueFire()), @@ -196,8 +316,14 @@ void RegionTable_Init_IceCavern() { areaTable[RR_ICE_CAVERN_MQ_WEST_CORRIDOR] = Region("Ice Cavern MQ West Corridor", SCENE_ICE_CAVERN, {}, { //Locations - LOCATION(RC_ICE_CAVERN_MQ_PUSH_BLOCK_POT_1, logic->CanBreakPots()), - LOCATION(RC_ICE_CAVERN_MQ_PUSH_BLOCK_POT_2, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_MQ_PUSH_BLOCK_POT_1, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_MQ_PUSH_BLOCK_POT_2, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_1, true), + LOCATION(RC_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_2, true), + LOCATION(RC_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_3, true), + LOCATION(RC_ICE_CAVERN_MQ_SCARECROW_LEFT_RED_ICE, logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_MQ_SCARECROW_MIDDLE_RED_ICE, logic->BlueFire()), + LOCATION(RC_ICE_CAVERN_MQ_SCARECROW_RIGHT_RED_ICE, logic->BlueFire()), }, { //Exits ENTRANCE(RR_ICE_CAVERN_MQ_SCARECROW_ROOM, logic->BlueFire()), @@ -209,14 +335,20 @@ void RegionTable_Init_IceCavern() { EVENT_ACCESS(LOGIC_BLUE_FIRE_ACCESS, true), }, { //Locations - LOCATION(RC_ICE_CAVERN_MQ_COMPASS_CHEST, logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_ICE_CAVERN_MQ_COMPASS_CHEST, logic->HasItem(RG_OPEN_CHEST)), //It is possible for child with master, BGS or sticks, or adult with BGS, to hit this switch through the ice with a crouchstab, but it's precise and unintuitive for a trick - LOCATION(RC_ICE_CAVERN_MQ_FREESTANDING_POH, logic->HasExplosives()), // can get with rang trick + LOCATION(RC_ICE_CAVERN_MQ_FREESTANDING_POH, logic->HasExplosives()), // can get with rang trick //doing RT_ICE_MQ_RED_ICE_GS as child is untested, as I could not perform the trick reliably even as adult - LOCATION(RC_ICE_CAVERN_MQ_GS_RED_ICE, (logic->CanUse(RG_BOTTLE_WITH_BLUE_FIRE) && (logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_ICE_MQ_RED_ICE_GS))) && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)) || - (ctx->GetOption(RSK_BLUE_FIRE_ARROWS) && logic->CanUse(RG_ICE_ARROWS)) || (ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->CanUse(RG_SONG_OF_TIME) && logic->CanUse(RG_HOOKSHOT))), - LOCATION(RC_ICE_CAVERN_MQ_COMPASS_POT_1, logic->CanBreakPots()), - LOCATION(RC_ICE_CAVERN_MQ_COMPASS_POT_2, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_MQ_GS_RED_ICE, (logic->CanUse(RG_BOTTLE_WITH_BLUE_FIRE) && (logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_ICE_MQ_RED_ICE_GS))) && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)) || + (ctx->GetOption(RSK_BLUE_FIRE_ARROWS) && logic->CanUse(RG_ICE_ARROWS)) || (ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->CanUse(RG_SONG_OF_TIME) && logic->CanUse(RG_HOOKSHOT))), + LOCATION(RC_ICE_CAVERN_MQ_COMPASS_POT_1, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_MQ_COMPASS_POT_2, logic->CanBreakPots()), + LOCATION(RC_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_1, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_2, logic->CanClearStalagmite()), + LOCATION(RC_ICE_CAVERN_MQ_COMPASS_RED_ICE, (logic->CanUse(RG_BOTTLE_WITH_BLUE_FIRE) && (logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_ICE_MQ_RED_ICE_GS))) && (logic->CanKillEnemy(RE_GOLD_SKULLTULA) || logic->TakeDamage())) || + (ctx->GetOption(RSK_BLUE_FIRE_ARROWS) && logic->CanUse(RG_ICE_ARROWS))), }, {}); areaTable[RR_ICE_CAVERN_MQ_STALFOS_ROOM] = Region("Ice Cavern MQ Stalfos Room", SCENE_ICE_CAVERN, {}, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp index 3d40a71157d..9c8d9872c72 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/jabujabus_belly.cpp @@ -266,12 +266,14 @@ void RegionTable_Init_JabuJabusBelly() { EVENT_ACCESS(LOGIC_NUT_ACCESS, logic->CanBreakPots()), }, { //Locations - LOCATION(RC_JABU_JABUS_BELLY_MQ_MAP_CHEST, logic->BlastOrSmash() && logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_JABU_JABUS_BELLY_MQ_FIRST_ROOM_SIDE_CHEST, logic->CanUse(RG_FAIRY_SLINGSHOT) && logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_JABU_JABUS_BELLY_MQ_ENTRANCE_POT_1, logic->CanBreakPots()), - LOCATION(RC_JABU_JABUS_BELLY_MQ_ENTRANCE_POT_2, logic->CanBreakPots()), - LOCATION(RC_JABU_JABUS_BELLY_MQ_FIRST_GRASS_1, logic->CanCutShrubs()), - LOCATION(RC_JABU_JABUS_BELLY_MQ_FIRST_GRASS_2, logic->CanCutShrubs()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_MAP_CHEST, logic->BlastOrSmash() && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_FIRST_ROOM_SIDE_CHEST, logic->CanUse(RG_FAIRY_SLINGSHOT) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_ENTRANCE_POT_1, logic->CanBreakPots()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_ENTRANCE_POT_2, logic->CanBreakPots()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_FIRST_GRASS_1, logic->CanCutShrubs()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_FIRST_GRASS_2, logic->CanCutShrubs()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_LEFT_COW, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_RIGHT_COW, logic->CanUse(RG_FAIRY_SLINGSHOT)), }, { //Exits ENTRANCE(RR_JABU_JABUS_BELLY_ENTRYWAY, true), @@ -283,12 +285,13 @@ void RegionTable_Init_JabuJabusBelly() { EVENT_ACCESS(LOGIC_JABU_MQ_LIFT_ROOM_COW, logic->CanUse(RG_FAIRY_SLINGSHOT)), }, { //Locations - LOCATION(RC_JABU_JABUS_BELLY_MQ_SECOND_ROOM_LOWER_CHEST, logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_JABU_JABUS_BELLY_MQ_LIFT_HEART_1, true), - LOCATION(RC_JABU_JABUS_BELLY_MQ_LIFT_HEART_2, true), - LOCATION(RC_JABU_JABUS_BELLY_MQ_LIFT_RUPEE_1, logic->CanUse(RG_IRON_BOOTS)), - LOCATION(RC_JABU_JABUS_BELLY_MQ_LIFT_RUPEE_2, logic->CanUse(RG_IRON_BOOTS)), - LOCATION(RC_JABU_JABUS_BELLY_MQ_LIFT_RUPEE_3, logic->CanUse(RG_IRON_BOOTS)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_SECOND_ROOM_LOWER_CHEST, logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_LIFT_HEART_1, true), + LOCATION(RC_JABU_JABUS_BELLY_MQ_LIFT_HEART_2, true), + LOCATION(RC_JABU_JABUS_BELLY_MQ_LIFT_RUPEE_1, logic->CanUse(RG_IRON_BOOTS)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_LIFT_RUPEE_2, logic->CanUse(RG_IRON_BOOTS)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_LIFT_RUPEE_3, logic->CanUse(RG_IRON_BOOTS)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_ELEVATOR_COW, logic->CanUse(RG_FAIRY_SLINGSHOT) && (logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG))), }, { //Exits ENTRANCE(RR_JABU_JABUS_BELLY_MQ_BEGINNING, true), @@ -317,11 +320,12 @@ void RegionTable_Init_JabuJabusBelly() { areaTable[RR_JABU_JABUS_BELLY_MQ_HOLES_ROOM] = Region("Jabu Jabus Belly MQ Holes Room", SCENE_JABU_JABU, { //Events - EVENT_ACCESS(LOGIC_JABU_MQ_FORKED_ROOM_DOOR, (logic->HasExplosives() || ctx->GetTrickOption(RT_DISTANT_BOULDER_COLLISION)) && logic->CanUse(RG_FAIRY_SLINGSHOT)), + EVENT_ACCESS(LOGIC_JABU_MQ_FORKED_ROOM_DOOR, (logic->HasExplosives() || ctx->GetTrickOption(RT_BOULDER_COLLISION)) && logic->CanUse(RG_FAIRY_SLINGSHOT)), }, { //Locations LOCATION(RC_JABU_JABUS_BELLY_MQ_PIT_GRASS_1, logic->CanCutShrubs() && logic->HasExplosives()), LOCATION(RC_JABU_JABUS_BELLY_MQ_PIT_GRASS_2, logic->CanCutShrubs() && logic->HasExplosives()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_HOLES_COW, (logic->HasExplosives() || ctx->GetTrickOption(RT_BOULDER_COLLISION)) && logic->CanUse(RG_FAIRY_SLINGSHOT)), }, { //Exits ENTRANCE(RR_JABU_JABUS_BELLY_MQ_LIFT_ROOM, true), @@ -329,7 +333,10 @@ void RegionTable_Init_JabuJabusBelly() { ENTRANCE(RR_JABU_JABUS_BELLY_MQ_HOLES_ROOM_PAST_JIGGLY, logic->CanUse(RG_BOOMERANG)), }); - areaTable[RR_JABU_JABUS_BELLY_MQ_HOLES_ROOM_PAST_JIGGLY] = Region("Jabu Jabus Belly MQ Holes Room Past Jiggly", SCENE_JABU_JABU, {}, {}, { + areaTable[RR_JABU_JABUS_BELLY_MQ_HOLES_ROOM_PAST_JIGGLY] = Region("Jabu Jabus Belly MQ Holes Room Past Jiggly", SCENE_JABU_JABU, { + //Events + EVENT_ACCESS(LOGIC_JABU_MQ_FORKED_ROOM_DOOR, ctx->GetTrickOption(RT_BOULDER_COLLISION) && logic->CanUse(RG_FAIRY_SLINGSHOT)), + }, {}, { ENTRANCE(RR_JABU_JABUS_BELLY_MQ_HOLES_ROOM, logic->CanUse(RG_BOOMERANG)), //It is possible to open the door here without crossing the jiggly using RT_DISTANT_BOULDER and good timing ENTRANCE(RR_JABU_JABUS_BELLY_MQ_FORKED_CORRIDOR, logic->Get(LOGIC_JABU_MQ_FORKED_ROOM_DOOR)), @@ -342,6 +349,12 @@ void RegionTable_Init_JabuJabusBelly() { LOCATION(RC_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_1, logic->CanCutShrubs()), LOCATION(RC_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_2, logic->CanCutShrubs()), LOCATION(RC_JABU_JABUS_BELLY_MQ_BASEMENT_GRASS_3, logic->CanCutShrubs()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_1, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_2, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_3, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_1, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_2, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_3, logic->CanUse(RG_FAIRY_SLINGSHOT)), }, { //Exits ENTRANCE(RR_JABU_JABUS_BELLY_MQ_HOLES_ROOM, logic->HasItem(RG_CLIMB)), @@ -432,12 +445,21 @@ void RegionTable_Init_JabuJabusBelly() { areaTable[RR_JABU_JABUS_BELLY_MQ_FORK_NORTH_EAST] = Region("Jabu Jabus Belly MQ Fork North East", SCENE_JABU_JABU, {}, { //Locations //Implies CanKillEnemy(RE_LIKE_LIKE) - LOCATION(RC_JABU_JABUS_BELLY_MQ_FALLING_LIKE_LIKE_ROOM_CHEST, logic->CanUse(RG_FAIRY_SLINGSHOT) && logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_JABU_JABUS_BELLY_MQ_LIKE_LIKES_POT_1, logic->CanBreakPots()), - LOCATION(RC_JABU_JABUS_BELLY_MQ_LIKE_LIKES_POT_2, logic->CanBreakPots()), - LOCATION(RC_JABU_JABUS_BELLY_MQ_FALLING_LIKE_LIKE_GRASS, logic->CanCutShrubs()), - LOCATION(RC_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_1, logic->CanBreakSmallCrates()), - LOCATION(RC_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_2, logic->CanBreakSmallCrates()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_FALLING_LIKE_LIKE_ROOM_CHEST, logic->CanUse(RG_FAIRY_SLINGSHOT) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_LIKE_LIKES_POT_1, logic->CanBreakPots()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_LIKE_LIKES_POT_2, logic->CanBreakPots()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_FALLING_LIKE_LIKE_GRASS, logic->CanCutShrubs()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_1, logic->CanBreakSmallCrates()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_TRIPLE_HALLWAY_SMALL_CRATE_2, logic->CanBreakSmallCrates()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_1, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_2, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_3, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_1, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_2, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_3, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_1, logic->HasExplosives()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_2, logic->HasExplosives()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_3, logic->HasExplosives()), }, { //Exits ENTRANCE(RR_JABU_JABUS_BELLY_MQ_FORKED_CORRIDOR, true), @@ -489,6 +511,7 @@ void RegionTable_Init_JabuJabusBelly() { //Locations LOCATION(RC_JABU_JABUS_BELLY_MQ_AFTER_BIG_OCTO_GRASS_1, logic->CanCutShrubs()), LOCATION(RC_JABU_JABUS_BELLY_MQ_AFTER_BIG_OCTO_GRASS_2, logic->CanCutShrubs()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_AFTER_BIG_OCTO, logic->CanUse(RG_FAIRY_SLINGSHOT)), }, { //Exits ENTRANCE(RR_JABU_JABUS_BELLY_MQ_JIGGLIES_ROOM, true), @@ -501,6 +524,7 @@ void RegionTable_Init_JabuJabusBelly() { LOCATION(RC_JABU_JABUS_BELLY_MQ_JIGGLIES_GRASS, logic->CanCutShrubs()), LOCATION(RC_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_1, logic->CanUse(RG_FAIRY_SLINGSHOT) && logic->CanBreakSmallCrates()), LOCATION(RC_JABU_JABUS_BELLY_MQ_JIGGLIES_SMALL_CRATE_2, logic->CanUse(RG_FAIRY_SLINGSHOT) && logic->CanBreakSmallCrates()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_JIGGLIES_COW, logic->CanUse(RG_FAIRY_SLINGSHOT)), }, { //Exits ENTRANCE(RR_JABU_JABUS_BELLY_MQ_ABOVE_BIGOCTO, true), @@ -538,11 +562,14 @@ void RegionTable_Init_JabuJabusBelly() { EVENT_ACCESS(LOGIC_FAIRY_ACCESS, logic->CanBreakPots()), }, { //Locations - LOCATION(RC_JABU_JABUS_BELLY_MQ_NEAR_BOSS_CHEST, logic->CanUse(RG_FAIRY_SLINGSHOT)), - LOCATION(RC_JABU_JABUS_BELLY_MQ_GS_NEAR_BOSS, (logic->HasItem(RG_CLIMB) && (logic->CanUse(RG_BOOMERANG) || (ctx->GetTrickOption(RT_JABU_NEAR_BOSS_RANGED) && logic->CanUse(RG_HOOKSHOT)))) || (ctx->GetTrickOption(RT_JABU_NEAR_BOSS_RANGED) && logic->CanUse(RG_LONGSHOT))), - LOCATION(RC_JABU_JABUS_BELLY_MQ_BEFORE_BOSS_POT_1, logic->CanBreakPots()), - LOCATION(RC_JABU_JABUS_BELLY_MQ_BEFORE_BOSS_GRASS_1, logic->CanCutShrubs()), - LOCATION(RC_JABU_JABUS_BELLY_MQ_BEFORE_BOSS_GRASS_2, logic->CanCutShrubs()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_NEAR_BOSS_CHEST, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_GS_NEAR_BOSS, (logic->HasItem(RG_CLIMB) && (logic->CanUse(RG_BOOMERANG) || (ctx->GetTrickOption(RT_JABU_NEAR_BOSS_RANGED) && logic->CanUse(RG_HOOKSHOT)))) || (ctx->GetTrickOption(RT_JABU_NEAR_BOSS_RANGED) && logic->CanUse(RG_LONGSHOT))), + LOCATION(RC_JABU_JABUS_BELLY_MQ_BEFORE_BOSS_POT_1, logic->CanBreakPots()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_BEFORE_BOSS_GRASS_1, logic->CanCutShrubs()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_BEFORE_BOSS_GRASS_2, logic->CanCutShrubs()), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_LEFT_COW, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_1, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_2, logic->CanUse(RG_FAIRY_SLINGSHOT)), }, { //Exits ENTRANCE(RR_JABU_JABUS_BELLY_MQ_TO_NEAR_BOSS_ROOM, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp index f5cf0598552..0b6fffc3379 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp @@ -16,7 +16,10 @@ void RegionTable_Init_ShadowTemple() { #pragma region Vanilla - areaTable[RR_SHADOW_TEMPLE_BEGINNING] = Region("Shadow Temple Beginning", SCENE_SHADOW_TEMPLE, {}, {}, { + areaTable[RR_SHADOW_TEMPLE_BEGINNING] = Region("Shadow Temple Beginning", SCENE_SHADOW_TEMPLE, {}, { + //Locations + LOCATION(RC_SHADOW_TEMPLE_TRUTHSPINNER_RECTANGLE_SIGN, logic->CanRead()), + }, { //Exits ENTRANCE(RR_SHADOW_TEMPLE_ENTRYWAY, (ctx->GetTrickOption(RT_LENS_SHADOW) || logic->CanUse(RG_LENS_OF_TRUTH)) && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT))), ENTRANCE(RR_SHADOW_TEMPLE_WHISPERING_WALLS_START, ctx->GetTrickOption(RT_LENS_SHADOW) || logic->CanUse(RG_LENS_OF_TRUTH)), @@ -148,11 +151,12 @@ void RegionTable_Init_ShadowTemple() { // See MQ for comments areaTable[RR_SHADOW_TEMPLE_STONE_UMBRELLA] = Region("Shadow Temple Stone Umbrella", SCENE_SHADOW_TEMPLE, {}, { //Locations - LOCATION(RC_SHADOW_TEMPLE_FALLING_SPIKES_LOWER_CHEST, logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_SHADOW_TEMPLE_GS_FALLING_SPIKES_ROOM, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG) || (logic->IsAdult && logic->CanGroundJumpslash())), - LOCATION(RC_SHADOW_TEMPLE_FALLING_SPIKES_POT_1, logic->CanBreakPots()), - LOCATION(RC_SHADOW_TEMPLE_FALLING_SPIKES_POT_2, logic->CanBreakPots()), - LOCATION(RC_SHADOW_TEMPLE_FALLING_SPIKES_POT_3, logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_SHADOW_TEMPLE_FALLING_SPIKES_LOWER_CHEST, logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_SHADOW_TEMPLE_GS_FALLING_SPIKES_ROOM, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG) || (logic->IsAdult && logic->CanGroundJumpslash())), + LOCATION(RC_SHADOW_TEMPLE_FALLING_SPIKES_POT_1, logic->CanBreakPots()), + LOCATION(RC_SHADOW_TEMPLE_FALLING_SPIKES_POT_2, logic->CanBreakPots()), + LOCATION(RC_SHADOW_TEMPLE_FALLING_SPIKES_POT_3, logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_SHADOW_TEMPLE_FALLING_SPIKES_RECTANGLE_SIGN, logic->CanRead()), }, { //Exits ENTRANCE(RR_SHADOW_TEMPLE_LOWER_HUGE_PIT, !!ctx->GetTrickOption(RT_VISIBLE_COLLISION)), @@ -341,6 +345,7 @@ void RegionTable_Init_ShadowTemple() { areaTable[RR_SHADOW_TEMPLE_THREE_SKULL_JARS] = Region("Shadow Temple Three Skull Jars", SCENE_SHADOW_TEMPLE, {}, { //Locations LOCATION(RC_SHADOW_TEMPLE_GS_TRIPLE_GIANT_POT, logic->HasItem(RG_GORONS_BRACELET) || logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_SHORT_JUMPSLASH)), + LOCATION(RC_SHADOW_TEMPLE_WONDER_THREE_POTS, logic->CanUse(RG_FAIRY_BOW)), }, { //Exits ENTRANCE(RR_SHADOW_TEMPLE_MAZE, true), @@ -527,6 +532,7 @@ void RegionTable_Init_ShadowTemple() { areaTable[RR_SHADOW_TEMPLE_MQ_LOWER_HUGE_PIT] = Region("Shadow Temple MQ Lower Huge Pit", SCENE_SHADOW_TEMPLE, {}, { //Locations LOCATION(RC_SHADOW_TEMPLE_MQ_BEAMOS_SILVER_RUPEES_CHEST, logic->CanUse(RG_LONGSHOT) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_SHADOW_TEMPLE_MQ_LOWER_PIT_RECTANGLE_SIGN, logic->CanRead()), }, { //Exits ENTRANCE(RR_SHADOW_TEMPLE_MQ_B2_TO_B3_CORRIDOR_B3, logic->CanUse(RG_LONGSHOT)), @@ -562,6 +568,7 @@ void RegionTable_Init_ShadowTemple() { LOCATION(RC_SHADOW_TEMPLE_MQ_GS_FALLING_SPIKES_ROOM, ctx->GetTrickOption(RT_SHADOW_UMBRELLA_GS) && logic->CanUse(RG_HOVER_BOOTS) && logic->CanStandingShield() && logic->CanUse(RG_MASTER_SWORD)), LOCATION(RC_SHADOW_TEMPLE_MQ_UPPER_UMBRELLA_NORTH_POT, logic->CanBreakPots()), LOCATION(RC_SHADOW_TEMPLE_MQ_UPPER_UMBRELLA_SOUTH_POT, logic->CanBreakPots()), + LOCATION(RC_SHADOW_TEMPLE_FALLING_SPIKES_RECTANGLE_SIGN, logic->CanRead()), }, { //Exits ENTRANCE(RR_SHADOW_TEMPLE_MQ_STONE_UMBRELLA_ROOM, true), @@ -670,7 +677,7 @@ void RegionTable_Init_ShadowTemple() { //Locations //It's a trick on N64 to kill this and drop down to collect this with normal weapons, as doing so without the statue being dropped voids you to before the boat //hilariously, you can hit this with a pot before you bring down statue, but there's no great way to reset it without crossing. the statue's collision is very inconvenient afterwards - LOCATION(RC_SHADOW_TEMPLE_MQ_GS_AFTER_SHIP, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)), + LOCATION(RC_SHADOW_TEMPLE_MQ_GS_AFTER_SHIP, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG) || (logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOMB_THROW) && ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), LOCATION(RC_SHADOW_TEMPLE_MQ_BEFORE_CHASM_WEST_POT, logic->CanBreakPots()), LOCATION(RC_SHADOW_TEMPLE_MQ_BEFORE_CHASM_EAST_POT, logic->CanBreakPots()), }, { @@ -708,7 +715,7 @@ void RegionTable_Init_ShadowTemple() { areaTable[RR_SHADOW_TEMPLE_MQ_BOSS_DOOR] = Region("Shadow Temple MQ Boss Door", SCENE_SHADOW_TEMPLE, {}, { //Locations //you can drop onto this and the respawn is reasonable - LOCATION(RC_SHADOW_TEMPLE_MQ_GS_NEAR_BOSS, (logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOMB_THROW) || logic->CanUse(RG_MEGATON_HAMMER)) && (ctx->GetTrickOption(RT_LENS_SHADOW_MQ) || logic->CanUse(RG_LENS_OF_TRUTH))), + LOCATION(RC_SHADOW_TEMPLE_MQ_GS_NEAR_BOSS, (logic->HookshotOrBoomerang() || ((logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOMB_THROW) || logic->CanUse(RG_MEGATON_HAMMER)) && ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))) && (ctx->GetTrickOption(RT_LENS_SHADOW_MQ) || logic->CanUse(RG_LENS_OF_TRUTH))), }, { //Exits ENTRANCE(RR_SHADOW_TEMPLE_MQ_PRE_BOSS_ROOM, logic->CanUse(RG_HOVER_BOOTS) && (ctx->GetTrickOption(RT_LENS_SHADOW_MQ) || logic->CanUse(RG_LENS_OF_TRUTH))), @@ -736,7 +743,8 @@ void RegionTable_Init_ShadowTemple() { areaTable[RR_SHADOW_TEMPLE_MQ_THREE_SKULL_JARS] = Region("Shadow Temple MQ Three Skull Jars", SCENE_SHADOW_TEMPLE, {}, { //Locations - LOCATION(RC_SHADOW_TEMPLE_MQ_FREESTANDING_KEY, true), + LOCATION(RC_SHADOW_TEMPLE_MQ_FREESTANDING_KEY, true), + LOCATION(RC_SHADOW_TEMPLE_MQ_WONDER_THREE_POTS, logic->CanUse(RG_FAIRY_BOW)), }, { //Exits ENTRANCE(RR_SHADOW_TEMPLE_MQ_INVISIBLE_MAZE, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index 8baa6abcf11..bf2ad7bd249 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -23,8 +23,10 @@ void RegionTable_Init_SpiritTemple() { EVENT_ACCESS(LOGIC_FORWARDS_SPIRIT_ADULT, logic->IsAdult), }, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_LOBBY_POT_1, logic->CanBreakPots()), - LOCATION(RC_SPIRIT_TEMPLE_LOBBY_POT_2, logic->CanBreakPots()), + LOCATION(RC_SPIRIT_TEMPLE_LOBBY_POT_1, logic->CanBreakPots()), + LOCATION(RC_SPIRIT_TEMPLE_LOBBY_POT_2, logic->CanBreakPots()), + LOCATION(RC_SPIRIT_TEMPLE_LEFT_SNAKE_STATUE, logic->CanRead()), + LOCATION(RC_SPIRIT_TEMPLE_RIGHT_SNAKE_STATUE, logic->CanRead()), }, { //Exits ENTRANCE(RR_SPIRIT_TEMPLE_ENTRYWAY, true), @@ -556,13 +558,16 @@ void RegionTable_Init_SpiritTemple() { }, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_ENTRANCE_FRONT_LEFT_CHEST, logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_SPIRIT_TEMPLE_MQ_ENTRANCE_BACK_LEFT_CHEST, AnyAgeTime([]{return logic->BlastOrSmash();}) && logic->CanHitEyeTargets() && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_SPIRIT_TEMPLE_MQ_ENTRANCE_BACK_LEFT_CHEST, logic->HasItem(RG_OPEN_CHEST) && + ((AnyAgeTime([]{return logic->BlastOrSmash();}) && logic->CanHitEyeTargets()) || (ctx->GetTrickOption(RT_BOULDER_COLLISION) && logic->IsChild ? logic->CanHitEyeTargets() : logic->CanUse(RG_FAIRY_SLINGSHOT)))), LOCATION(RC_SPIRIT_TEMPLE_MQ_ENTRANCE_BACK_RIGHT_CHEST, logic->CanHitSwitch(ED_BOOMERANG) && logic->HasItem(RG_OPEN_CHEST)), LOCATION(RC_SPIRIT_TEMPLE_MQ_ENTRANCE_FRONT_RIGHT_CHEST, logic->Get(LOGIC_SPIRIT_1F_SILVER_RUPEES) && logic->HasItem(RG_OPEN_CHEST)), LOCATION(RC_SPIRIT_TEMPLE_MQ_ENTRANCE_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_MQ_ENTRANCE_POT_2, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_MQ_ENTRANCE_POT_3, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_MQ_ENTRANCE_POT_4, logic->CanBreakPots()), + LOCATION(RC_SPIRIT_TEMPLE_LEFT_SNAKE_STATUE, logic->CanRead()), + LOCATION(RC_SPIRIT_TEMPLE_RIGHT_SNAKE_STATUE, logic->CanRead()), }, { //Exits ENTRANCE(RR_SPIRIT_TEMPLE_ENTRYWAY, true), @@ -983,6 +988,8 @@ void RegionTable_Init_SpiritTemple() { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_CHEST_SWITCH_CHEST, logic->HasItem(RG_OPEN_CHEST)), LOCATION(RC_SPIRIT_TEMPLE_MQ_DINALFOS_ROOM_SUN_FAIRY, logic->CanUse(RG_SUNS_SONG)), + LOCATION(RC_SPIRIT_TEMPLE_MQ_WONDER_CHEST_HAMMER, logic->CanUse(RG_MEGATON_HAMMER)), + LOCATION(RC_SPIRIT_TEMPLE_MQ_WONDER_CHEST_SLASH, logic->CanJumpslashExceptHammer()), }, { //Exits ENTRANCE(RR_SPIRIT_TEMPLE_MQ_BEAMOS_PITS, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp index cfd2b39259e..1c59853c181 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/water_temple.cpp @@ -296,7 +296,7 @@ void RegionTable_Init_WaterTemple() { areaTable[RR_WATER_TEMPLE_BOULDER_CANAL] = Region("Water Temple Boulder Canal", SCENE_WATER_TEMPLE, {}, { //Locations - LOCATION(RC_WATER_TEMPLE_GS_NEAR_BOSS_KEY_CHEST, (logic->IsAdult && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)) || (logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT))), + LOCATION(RC_WATER_TEMPLE_GS_NEAR_BOSS_KEY_CHEST, (logic->IsAdult && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)) || (logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT)) || (((logic->IsAdult && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOMB_THROW)) || (logic->IsChild && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOOMERANG))) && ctx->GetTrickOption(RT_VOIDOUT_COLLECTION) && logic->CanUse(RG_HOVER_BOOTS))), }, { //Exits //making the jump as adult without jumpslash is possible, but hard enough to be a trick @@ -760,7 +760,11 @@ void RegionTable_Init_WaterTemple() { ENTRANCE(RR_WATER_TEMPLE_MQ_BOSS_DOOR_RAMP, true), }); - areaTable[RR_WATER_TEMPLE_MQ_BOSS_DOOR_RAMP] = Region("Water Temple MQ Boss Door Ramp", SCENE_WATER_TEMPLE, {}, {}, { + areaTable[RR_WATER_TEMPLE_MQ_BOSS_DOOR_RAMP] = Region("Water Temple MQ Boss Door Ramp", SCENE_WATER_TEMPLE, {}, { + //Locations + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_1, logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_2, logic->CanUse(RG_HOOKSHOT)), + }, { //Exits ENTRANCE(RR_WATER_TEMPLE_MQ_RISING_TARGET_LEDGE, true), ENTRANCE(RR_WATER_TEMPLE_MQ_BOSS_DOOR, logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_ICE_ARROWS) || logic->CanUse(RG_NAYRUS_LOVE)), @@ -789,7 +793,8 @@ void RegionTable_Init_WaterTemple() { areaTable[RR_WATER_TEMPLE_MQ_SIDE_TOWER_2F] = Region("Water Temple MQ Side Tower 2F", SCENE_WATER_TEMPLE, {}, { //Locations - LOCATION(RC_WATER_TEMPLE_MQ_LONGSHOT_CHEST, logic->CanUse(RG_HOOKSHOT) && ((logic->WaterLevel(WL_MID) && logic->HasItem(RG_OPEN_CHEST)) || logic->CanOpenUnderwaterChest())), + LOCATION(RC_WATER_TEMPLE_MQ_LONGSHOT_CHEST, logic->CanUse(RG_HOOKSHOT) && ((logic->WaterLevel(WL_MID) && logic->HasItem(RG_OPEN_CHEST)) || logic->CanOpenUnderwaterChest())), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_LONGSHOT_ROOM, logic->CanUse(RG_HOOKSHOT) && (logic->WaterLevel(WL_MID) || logic->CanUse(RG_IRON_BOOTS))), }, { //Exits ENTRANCE(RR_WATER_TEMPLE_MQ_SIDE_TOWER_1F, logic->WaterLevel(WL_LOW) || logic->CanUse(RG_IRON_BOOTS)), @@ -810,7 +815,8 @@ void RegionTable_Init_WaterTemple() { areaTable[RR_WATER_TEMPLE_MQ_3_STALFOS_ROOM] = Region("Water Temple MQ 3 Stalfos Room", SCENE_WATER_TEMPLE, {}, { //Locations - LOCATION(RC_WATER_TEMPLE_MQ_MAP_CHEST, logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_WATER_TEMPLE_MQ_MAP_CHEST, logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_STALFOS_ROOM, logic->CanUse(RG_HOOKSHOT)), }, { //Exits ENTRANCE(RR_WATER_TEMPLE_MQ_LOW_EMBLEM, logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 4)), @@ -821,7 +827,8 @@ void RegionTable_Init_WaterTemple() { EVENT_ACCESS(LOGIC_WATER_MQ_SIDE_TOWER_TARGETS, logic->CanKillEnemy(RE_LIZALFOS) && logic->CanKillEnemy(RE_SPIKE)), }, { //Locations - LOCATION(RC_WATER_TEMPLE_MQ_COMPASS_CHEST, logic->Get(LOGIC_WATER_MQ_SIDE_TOWER_TARGETS) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_WATER_TEMPLE_MQ_COMPASS_CHEST, logic->Get(LOGIC_WATER_MQ_SIDE_TOWER_TARGETS) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_LIZALFOS_ROOM, logic->CanUse(RG_HOOKSHOT)), }, { //Exits ENTRANCE(RR_WATER_TEMPLE_MQ_SIDE_TOWER_1F, true), @@ -901,7 +908,8 @@ void RegionTable_Init_WaterTemple() { areaTable[RR_WATER_TEMPLE_MQ_PILLAR_B1_FINAL] = Region("Water Temple MQ Central Pillar B1 Final", SCENE_WATER_TEMPLE, {}, { //Locations - LOCATION(RC_WATER_TEMPLE_MQ_CENTRAL_PILLAR_CHEST, logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_WATER_TEMPLE_MQ_CENTRAL_PILLAR_CHEST, logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_UNDER_PILLAR_ROOM, logic->CanUse(RG_HOOKSHOT)), }, {}); areaTable[RR_WATER_TEMPLE_MQ_STORAGE_ROOM] = Region("Water Temple MQ Storage Room", SCENE_WATER_TEMPLE, {}, { @@ -944,6 +952,7 @@ void RegionTable_Init_WaterTemple() { LOCATION(RC_WATER_TEMPLE_MQ_GS_STORAGE_ROOM_LOWER_CRATE_5, logic->CanBreakCrates()), LOCATION(RC_WATER_TEMPLE_MQ_GS_STORAGE_ROOM_LOWER_CRATE_6, logic->CanBreakCrates()), LOCATION(RC_WATER_TEMPLE_MQ_GS_STORAGE_ROOM_LOWER_SMALL_CRATE, logic->CanBreakSmallCrates()), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_GS_STORAGE_ROOM, logic->CanUse(RG_HOOKSHOT)), }, { //Exits ENTRANCE(RR_WATER_TEMPLE_MQ_MAIN, true), @@ -978,6 +987,7 @@ void RegionTable_Init_WaterTemple() { LOCATION(RC_WATER_TEMPLE_MQ_LIZALFOS_HALLWAY_ROOM_CRATE_3, logic->CanBreakCrates()), LOCATION(RC_WATER_TEMPLE_MQ_LIZALFOS_HALLWAY_ROOM_CRATE_4, logic->CanBreakCrates()), LOCATION(RC_WATER_TEMPLE_MQ_LIZALFOS_HALLWAY_ROOM_CRATE_5, logic->CanBreakCrates()), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_LIZALFOS_HALLWAY, logic->CanUse(RG_HOOKSHOT)), }, { //Exits ENTRANCE(RR_WATER_TEMPLE_MQ_3F_CENTRAL_A, logic->CanUse(RG_HOOKSHOT)), @@ -1024,13 +1034,29 @@ void RegionTable_Init_WaterTemple() { ENTRANCE(RR_WATER_TEMPLE_MQ_2F_CENTRAL_H, logic->WaterLevel(WL_HIGH) && logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_IRON_BOOTS)), }); - areaTable[RR_WATER_TEMPLE_MQ_WATERFALL] = Region("Water Temple Waterfall", SCENE_WATER_TEMPLE, {}, {}, { + areaTable[RR_WATER_TEMPLE_MQ_WATERFALL] = Region("Water Temple Waterfall", SCENE_WATER_TEMPLE, {}, { + //Locations + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_1, logic->CanUse(RG_LONGSHOT)), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_2, logic->CanUse(RG_LONGSHOT)), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_3, logic->CanUse(RG_LONGSHOT)), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_1, logic->CanUse(RG_LONGSHOT)), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_2, logic->CanUse(RG_LONGSHOT)), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_3, logic->CanUse(RG_LONGSHOT)), + }, { //Exits ENTRANCE(RR_WATER_TEMPLE_MQ_OUTSIDE_WATERFALL, logic->SmallKeys(SCENE_WATER_TEMPLE, 1)), ENTRANCE(RR_WATER_TEMPLE_MQ_WATERFALL_TOP, logic->CanUse(RG_LONGSHOT)), }); - areaTable[RR_WATER_TEMPLE_MQ_WATERFALL_TOP] = Region("Water Temple Waterfall Top", SCENE_WATER_TEMPLE, {}, {}, { + areaTable[RR_WATER_TEMPLE_MQ_WATERFALL_TOP] = Region("Water Temple Waterfall Top", SCENE_WATER_TEMPLE, {}, { + //Locations + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_1, logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_2, logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_3, logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_1, logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_2, logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_3, logic->CanUse(RG_HOOKSHOT)), + }, { //Exits ENTRANCE(RR_WATER_TEMPLE_MQ_WATERFALL, logic->CanUse(RG_LONGSHOT) && logic->CanHitSwitch(ED_FAR)), ENTRANCE(RR_WATER_TEMPLE_MQ_STALFOS_PIT, true), @@ -1097,8 +1123,9 @@ void RegionTable_Init_WaterTemple() { EVENT_ACCESS(LOGIC_FAIRY_ACCESS, logic->CanBreakPots()), }, { //Locations - LOCATION(RC_WATER_TEMPLE_MQ_AFTER_DARK_LINK_POT_1, logic->CanBreakPots()), - LOCATION(RC_WATER_TEMPLE_MQ_AFTER_DARK_LINK_POT_2, logic->CanBreakPots()), + LOCATION(RC_WATER_TEMPLE_MQ_AFTER_DARK_LINK_POT_1, logic->CanBreakPots()), + LOCATION(RC_WATER_TEMPLE_MQ_AFTER_DARK_LINK_POT_2, logic->CanBreakPots()), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_AFTER_DARK_LINK, logic->CanUse(RG_HOOKSHOT)), }, { //Exits ENTRANCE(RR_WATER_TEMPLE_MQ_DARK_LINK_ROOM, true), @@ -1138,6 +1165,8 @@ void RegionTable_Init_WaterTemple() { LOCATION(RC_WATER_TEMPLE_MQ_DRAGON_ROOM_SUBMERGED_CRATE_2, logic->CanBreakCrates()), LOCATION(RC_WATER_TEMPLE_MQ_DRAGON_ROOM_SUBMERGED_CRATE_3, logic->CanBreakCrates()), LOCATION(RC_WATER_TEMPLE_MQ_DRAGON_ROOM_SUBMERGED_CRATE_4, logic->CanBreakCrates()), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_LEFT_EYE, logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_RIGHT_EYE, logic->CanUse(RG_HOOKSHOT)), }, { //Exits ENTRANCE(RR_WATER_TEMPLE_MQ_RIVER_POTS, logic->CanUse(RG_LONGSHOT)), @@ -1155,6 +1184,7 @@ void RegionTable_Init_WaterTemple() { LOCATION(RC_WATER_TEMPLE_MQ_DRAGON_ROOM_TORCHES_SMALL_CRATE_1, logic->CanBreakSmallCrates()), LOCATION(RC_WATER_TEMPLE_MQ_DRAGON_ROOM_TORCHES_SMALL_CRATE_2, logic->CanBreakSmallCrates()), LOCATION(RC_WATER_TEMPLE_MQ_DRAGON_ROOM_TORCHES_SMALL_CRATE_3, logic->CanBreakSmallCrates()), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_PORTRAIT, logic->CanUse(RG_HOOKSHOT)), }, { //Exits ENTRANCE(RR_WATER_TEMPLE_MQ_DRAGON_ROOM_TUNNEL, logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 16), @@ -1164,8 +1194,10 @@ void RegionTable_Init_WaterTemple() { areaTable[RR_WATER_TEMPLE_MQ_DRAGON_ROOM_DOOR] = Region("Water Temple MQ Dragon Room Door", SCENE_WATER_TEMPLE, {}, { //Locations - LOCATION(RC_WATER_TEMPLE_MQ_DRAGON_ROOM_DOOR_CRATE_1, logic->CanBreakCrates()), - LOCATION(RC_WATER_TEMPLE_MQ_DRAGON_ROOM_DOOR_CRATE_2, logic->CanBreakCrates()), + LOCATION(RC_WATER_TEMPLE_MQ_DRAGON_ROOM_DOOR_CRATE_1, logic->CanBreakCrates()), + LOCATION(RC_WATER_TEMPLE_MQ_DRAGON_ROOM_DOOR_CRATE_2, logic->CanBreakCrates()), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_LEFT_EYE, logic->CanUse(RG_HOOKSHOT) && logic->WaterTimer() >= 8), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_RIGHT_EYE, logic->CanUse(RG_HOOKSHOT) && logic->WaterTimer() >= 8), }, { //Exits ENTRANCE(RR_WATER_TEMPLE_MQ_RIVER_POTS, logic->CanUse(RG_LONGSHOT)), @@ -1261,6 +1293,8 @@ void RegionTable_Init_WaterTemple() { LOCATION(RC_WATER_TEMPLE_MQ_TRIPLE_TORCH_ROOM_GATE_CRATE_3, logic->CanBreakCrates()), LOCATION(RC_WATER_TEMPLE_MQ_TRIPLE_TORCH_ROOM_GATE_CRATE_1, logic->CanBreakCrates()), LOCATION(RC_WATER_TEMPLE_MQ_TRIPLE_TORCH_ROOM_GATE_CRATE_2, logic->CanBreakCrates()), + // A vanilla bug causes a glitched damage value on hardware and undefined behavior in SOH. Matching the resulting damage triggers with an additional array entry. + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_TRIPLE_TORCHES, logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_BOW)), }, {}); areaTable[RR_WATER_TEMPLE_MQ_SPIKE_MOAT] = Region("Water Temple MQ Spike Moat", SCENE_WATER_TEMPLE, {}, {}, { @@ -1309,9 +1343,9 @@ void RegionTable_Init_WaterTemple() { areaTable[RR_WATER_TEMPLE_MQ_CANAL_ALCOVE] = Region("Water Temple MQ Canal Alcove", SCENE_WATER_TEMPLE, {}, {}, { //Exits - ENTRANCE(RR_WATER_TEMPLE_MQ_3_JETS_ROOM, logic->SmallKeys(SCENE_WATER_TEMPLE, 2)), - ENTRANCE(RR_WATER_TEMPLE_MQ_SCARECROW_CANAL, logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE) || (logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 8)), - ENTRANCE(RR_WATER_TEMPLE_MQ_BEHIND_CANAL, logic->IsAdult && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) && logic->HasItem(RG_BRONZE_SCALE)), + ENTRANCE(RR_WATER_TEMPLE_MQ_3_JETS_ROOM_SWITCH_SIDE, logic->SmallKeys(SCENE_WATER_TEMPLE, 2)), + ENTRANCE(RR_WATER_TEMPLE_MQ_SCARECROW_CANAL, logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE) || (logic->CanUse(RG_IRON_BOOTS) && logic->WaterTimer() >= 8)), + ENTRANCE(RR_WATER_TEMPLE_MQ_BEHIND_CANAL, logic->IsAdult && ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) && logic->HasItem(RG_BRONZE_SCALE)), }); areaTable[RR_WATER_TEMPLE_MQ_BEHIND_CANAL] = Region("Water Temple MQ Behind Canal", SCENE_WATER_TEMPLE, {}, {}, { @@ -1322,23 +1356,38 @@ void RegionTable_Init_WaterTemple() { areaTable[RR_WATER_TEMPLE_MQ_FREESTANDING_ROOM] = Region("Water Temple MQ Freestanding Room", SCENE_WATER_TEMPLE, {}, { //Locations - LOCATION(RC_WATER_TEMPLE_MQ_FREESTANDING_KEY, logic->CanBreakCrates()), - LOCATION(RC_WATER_TEMPLE_MQ_STORAGE_ROOM_B_POT_1, logic->CanBreakPots()), - LOCATION(RC_WATER_TEMPLE_MQ_STORAGE_ROOM_B_POT_2, logic->CanBreakPots()), - LOCATION(RC_WATER_TEMPLE_MQ_STORAGE_ROOM_B_CRATE_1, logic->CanBreakCrates()), - LOCATION(RC_WATER_TEMPLE_MQ_STORAGE_ROOM_B_CRATE_2, logic->CanBreakCrates()), - LOCATION(RC_WATER_TEMPLE_MQ_STORAGE_ROOM_B_CRATE_3, logic->CanBreakCrates()), - LOCATION(RC_WATER_TEMPLE_MQ_STORAGE_ROOM_B_CRATE_4, logic->CanBreakCrates()), - LOCATION(RC_WATER_TEMPLE_MQ_STORAGE_ROOM_B_CRATE_5, logic->CanBreakCrates()), + LOCATION(RC_WATER_TEMPLE_MQ_FREESTANDING_KEY, logic->CanBreakCrates()), + LOCATION(RC_WATER_TEMPLE_MQ_STORAGE_ROOM_B_POT_1, logic->CanBreakPots()), + LOCATION(RC_WATER_TEMPLE_MQ_STORAGE_ROOM_B_POT_2, logic->CanBreakPots()), + LOCATION(RC_WATER_TEMPLE_MQ_STORAGE_ROOM_B_CRATE_1, logic->CanBreakCrates()), + LOCATION(RC_WATER_TEMPLE_MQ_STORAGE_ROOM_B_CRATE_2, logic->CanBreakCrates()), + LOCATION(RC_WATER_TEMPLE_MQ_STORAGE_ROOM_B_CRATE_3, logic->CanBreakCrates()), + LOCATION(RC_WATER_TEMPLE_MQ_STORAGE_ROOM_B_CRATE_4, logic->CanBreakCrates()), + LOCATION(RC_WATER_TEMPLE_MQ_STORAGE_ROOM_B_CRATE_5, logic->CanBreakCrates()), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_FREESTANDING_ROOM, logic->CanUse(RG_HOOKSHOT)), }, { //Exits ENTRANCE(RR_WATER_TEMPLE_MQ_BEHIND_CANAL, AnyAgeTime([]{return logic->CanKillEnemy(RE_STALFOS);})), }); - areaTable[RR_WATER_TEMPLE_MQ_3_JETS_ROOM] = Region("Water Temple MQ 3 Jets Room", SCENE_WATER_TEMPLE, {}, {}, { + areaTable[RR_WATER_TEMPLE_MQ_3_JETS_ROOM_SWITCH_SIDE] = Region("Water Temple MQ 3 Jets Room Switch Side", SCENE_WATER_TEMPLE, {}, { + //Locations + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_1, true), + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_2, logic->CanHitSwitch()), + }, { + //Exits + ENTRANCE(RR_WATER_TEMPLE_MQ_CANAL_ALCOVE, logic->SmallKeys(SCENE_WATER_TEMPLE, 2)), + ENTRANCE(RR_WATER_TEMPLE_MQ_3_JETS_ROOM_NO_SWITCH_SIDE, logic->CanHitSwitch()), + // No Switch Side requires CanHitSwitch(ED_BOOMERANG) to get to this entrance. This is covered by No Switch -> Switch entrance. + ENTRANCE(RR_WATER_TEMPLE_MQ_DODONGO_ROOM, logic->CanHitSwitch(ED_BOOMERANG) && logic->HasFireSource()), + }); + + areaTable[RR_WATER_TEMPLE_MQ_3_JETS_ROOM_NO_SWITCH_SIDE] = Region("Water Temple MQ 3 Jets Room No Switch Side", SCENE_WATER_TEMPLE, {}, { + //Locations + LOCATION(RC_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_2, logic->IsAdult), + }, { //Exits - ENTRANCE(RR_WATER_TEMPLE_MQ_CANAL_ALCOVE, logic->SmallKeys(SCENE_WATER_TEMPLE, 2) && logic->CanHitSwitch(ED_BOOMERANG)), - ENTRANCE(RR_WATER_TEMPLE_MQ_DODONGO_ROOM, logic->CanHitSwitch() && logic->HasFireSource()), + ENTRANCE(RR_WATER_TEMPLE_MQ_3_JETS_ROOM_SWITCH_SIDE, logic->CanHitSwitch(ED_BOOMERANG) || (ctx->GetTrickOption(RT_HOVER_BOOST_SIMPLE) && logic->CanHammerRecoilHover())), }); areaTable[RR_WATER_TEMPLE_MQ_DODONGO_ROOM] = Region("Water Temple MQ Dodongo Room", SCENE_WATER_TEMPLE, {}, { @@ -1352,8 +1401,8 @@ void RegionTable_Init_WaterTemple() { LOCATION(RC_WATER_TEMPLE_MQ_DODONGO_ROOM_LOWER_CRATE_3, logic->CanBreakCrates()), }, { //Exits - ENTRANCE(RR_WATER_TEMPLE_MQ_3_JETS_ROOM, (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS)) && AnyAgeTime([]{return logic->CanKillEnemy(RE_DODONGO, ED_CLOSE, true, 5);})), - ENTRANCE(RR_WATER_TEMPLE_MQ_CRATE_VORTEX_CAGE, (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS)) && AnyAgeTime([]{return logic->CanKillEnemy(RE_DODONGO, ED_CLOSE, true, 5);})), + ENTRANCE(RR_WATER_TEMPLE_MQ_3_JETS_ROOM_NO_SWITCH_SIDE, (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS)) && AnyAgeTime([]{return logic->CanKillEnemy(RE_DODONGO, ED_CLOSE, true, 5);})), + ENTRANCE(RR_WATER_TEMPLE_MQ_CRATE_VORTEX_CAGE, (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS)) && AnyAgeTime([]{return logic->CanKillEnemy(RE_DODONGO, ED_CLOSE, true, 5);})), }); areaTable[RR_WATER_TEMPLE_MQ_CRATE_VORTEX_CAGE] = Region("Water Temple MQ Crate Vortex Cage", SCENE_WATER_TEMPLE, {}, { diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp index 40ba46fcf4b..79d89071997 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp @@ -56,14 +56,19 @@ void RegionTable_Init_CastleGrounds() { EVENT_ACCESS(LOGIC_FAIRY_ACCESS, logic->CanUse(RG_STICKS)), }, { //Locations - LOCATION(RC_HC_NEAR_GUARDS_TREE_1, logic->CanBonkTrees()), - LOCATION(RC_HC_NEAR_GUARDS_TREE_2, logic->CanBonkTrees()), - LOCATION(RC_HC_NEAR_GUARDS_TREE_3, logic->CanBonkTrees()), - LOCATION(RC_HC_NEAR_GUARDS_TREE_4, logic->CanBonkTrees()), - LOCATION(RC_HC_NEAR_GUARDS_TREE_5, logic->CanBonkTrees()), - LOCATION(RC_HC_NEAR_GUARDS_TREE_6, logic->CanBonkTrees()), - LOCATION(RC_HC_NL_TREE_1, false), - LOCATION(RC_HC_NL_TREE_2, false), + LOCATION(RC_HC_NEAR_GUARDS_TREE_1, logic->CanBonkTrees()), + LOCATION(RC_HC_NEAR_GUARDS_TREE_2, logic->CanBonkTrees()), + LOCATION(RC_HC_NEAR_GUARDS_TREE_3, logic->CanBonkTrees()), + LOCATION(RC_HC_NEAR_GUARDS_TREE_4, logic->CanBonkTrees()), + LOCATION(RC_HC_NEAR_GUARDS_TREE_5, logic->CanBonkTrees()), + LOCATION(RC_HC_NEAR_GUARDS_TREE_6, logic->CanBonkTrees()), + LOCATION(RC_HC_NL_TREE_1, false), + LOCATION(RC_HC_NL_TREE_2, false), + LOCATION(RC_HC_NEAR_WALL_BUTTERFLY_FAIRY, logic->CanUse(RG_STICKS)), + LOCATION(RC_HC_NEAR_STAIRS_BUTTERFLY_FAIRY, logic->CanUse(RG_STICKS)), + LOCATION(RC_HC_NEAR_BOULDER_PATH_BUTTERFLY_FAIRY, logic->CanUse(RG_STICKS)), + LOCATION(RC_HC_NEAR_ARCHWAY_BUTTERFLY_FAIRY, logic->CanUse(RG_STICKS)), + LOCATION(RC_HC_DEAD_END_RECTANGLE_SIGN, logic->CanRead()), }, { //Exits ENTRANCE(RR_HC_GATE, true), @@ -88,9 +93,21 @@ void RegionTable_Init_CastleGrounds() { areaTable[RR_HC_MOAT] = Region("Hyrule Castle Grounds", SCENE_HYRULE_CASTLE, {}, { //Locations - LOCATION(RC_HC_GRASS_1, logic->CanCutShrubs()), - LOCATION(RC_HC_GRASS_2, logic->CanCutShrubs()), - LOCATION(RC_HC_GROTTO_TREE, logic->CanBonkTrees()), + LOCATION(RC_HC_GRASS_1, logic->CanCutShrubs()), + LOCATION(RC_HC_GRASS_2, logic->CanCutShrubs()), + LOCATION(RC_HC_GROTTO_TREE, logic->CanBonkTrees()), + LOCATION(RC_HC_WONDER_LEFT_TORCH, logic->CanUse(RG_FAIRY_SLINGSHOT) && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_BOOMERANG) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_HC_WONDER_RIGHT_TORCH, logic->CanUse(RG_FAIRY_SLINGSHOT) && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_BOOMERANG) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_HC_WONDER_MOAT_1, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION)), + LOCATION(RC_HC_WONDER_MOAT_2, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION)), // Requires backflip with Iron Boots + LOCATION(RC_HC_WONDER_MOAT_3, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION)), // Requires backflip with Iron Boots + LOCATION(RC_HC_WONDER_MOAT_4, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION)), + LOCATION(RC_HC_WONDER_MOAT_5, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION)), // Requires backflip with Iron Boots + LOCATION(RC_HC_WONDER_MOAT_6, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION)), // Requires backflip with Iron Boots + LOCATION(RC_HC_WONDER_MOAT_7, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION)), // Requires backflip with Iron Boots + LOCATION(RC_HC_WONDER_MOAT_8, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION)), // Requires backflip with Iron Boots + LOCATION(RC_HC_WONDER_MOAT_9, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION)), // Requires backflip with Iron Boots + LOCATION(RC_HC_WONDER_MOAT_10, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION)), // Requires backflip with Iron Boots }, { //Exits ENTRANCE(RR_HC_GATE, true), @@ -107,8 +124,10 @@ void RegionTable_Init_CastleGrounds() { areaTable[RR_HC_GARDEN] = Region("HC Garden", SCENE_CASTLE_COURTYARD_ZELDA, {}, { //Locations - LOCATION(RC_HC_ZELDAS_LETTER, logic->HasItem(RG_SPEAK_HYLIAN)), - LOCATION(RC_SONG_FROM_IMPA, logic->HasItem(RG_SPEAK_HYLIAN)), + LOCATION(RC_HC_ZELDAS_LETTER, logic->HasItem(RG_SPEAK_HYLIAN)), + LOCATION(RC_SONG_FROM_IMPA, logic->HasItem(RG_SPEAK_HYLIAN)), + LOCATION(RC_HC_WONDER_COURTYARD_RIGHT_WINDOW, logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_HC_WONDER_COURTYARD_LEFT_WINDOW, logic->CanUse(RG_FAIRY_SLINGSHOT)), }, { //Exits ENTRANCE(RR_HC_DRAIN_LEDGE, true), // if this ever gets shuffled leaving garden area should come out crawlspace @@ -161,7 +180,7 @@ void RegionTable_Init_CastleGrounds() { EVENT_ACCESS(LOGIC_BUILD_RAINBOW_BRIDGE, logic->CanBuildRainbowBridge()), }, { //Locations - LOCATION(RC_OGC_GS, logic->CanJumpslashExceptHammer() || logic->CanUseProjectile() || (logic->CanShield() && logic->CanUse(RG_MEGATON_HAMMER)) || logic->CanUse(RG_DINS_FIRE)), + LOCATION(RC_OGC_GS, logic->HookshotOrBoomerang() || ((logic->CanJumpslashExceptHammer() || logic->CanUseProjectile() || (logic->CanShield() && logic->CanUse(RG_MEGATON_HAMMER)) || logic->CanUse(RG_DINS_FIRE)) && ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))) , }, { //Exits ENTRANCE(RR_CASTLE_GROUNDS, logic->AtNight), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_crater.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_crater.cpp index d0194ac67d7..e3880e3e916 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_crater.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_crater.cpp @@ -282,10 +282,11 @@ void RegionTable_Init_DeathMountainCrater() { areaTable[RR_DMC_POTS] = Region("DMC Pots", SCENE_DEATH_MOUNTAIN_CRATER, {}, { // Locations - LOCATION(RC_DMC_NEAR_GC_POT_1, logic->CanBreakPots()), - LOCATION(RC_DMC_NEAR_GC_POT_2, logic->CanBreakPots()), - LOCATION(RC_DMC_NEAR_GC_POT_3, logic->CanBreakPots()), - LOCATION(RC_DMC_NEAR_GC_POT_4, logic->CanBreakPots()), + LOCATION(RC_DMC_NEAR_GC_POT_1, logic->CanBreakPots()), + LOCATION(RC_DMC_NEAR_GC_POT_2, logic->CanBreakPots()), + LOCATION(RC_DMC_NEAR_GC_POT_3, logic->CanBreakPots()), + LOCATION(RC_DMC_NEAR_GC_POT_4, logic->CanBreakPots()), + LOCATION(RC_DMC_BRIDGE_EXIT_ARROW_SIGN, logic->CanRead()), }, { //Exits ENTRANCE(RR_GC_DARUNIAS_CHAMBER, true), @@ -302,19 +303,22 @@ void RegionTable_Init_DeathMountainCrater() { EVENT_ACCESS(LOGIC_FAIRY_ACCESS, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), }, { //Locations - LOCATION(RC_SHEIK_IN_CRATER, logic->IsAdult), - LOCATION(RC_DMC_GS_BEAN_PATCH, logic->CanSpawnSoilSkull(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL) && logic->CanKillEnemy(RE_GOLD_SKULLTULA)), - LOCATION(RC_DMC_NEAR_PLATFORM_RED_RUPEE, logic->IsChild), - LOCATION(RC_DMC_MIDDLE_PLATFORM_RED_RUPEE, logic->IsChild), - LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_1, logic->IsChild), - LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_2, logic->IsChild), - LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_3, logic->IsChild), - LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_4, logic->IsChild), - LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_5, logic->IsChild), - LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_6, logic->IsChild), - LOCATION(RC_DMC_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_DMC_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_DMC_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_SHEIK_IN_CRATER, logic->IsAdult), + LOCATION(RC_DMC_GS_BEAN_PATCH, logic->CanSpawnSoilSkull(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL) && logic->CanKillEnemy(RE_GOLD_SKULLTULA)), + LOCATION(RC_DMC_NEAR_PLATFORM_RED_RUPEE, logic->IsChild), + LOCATION(RC_DMC_MIDDLE_PLATFORM_RED_RUPEE, logic->IsChild), + LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_1, logic->IsChild), + LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_2, logic->IsChild), + LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_3, logic->IsChild), + LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_4, logic->IsChild), + LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_5, logic->IsChild), + LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_6, logic->IsChild), + LOCATION(RC_DMC_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_DMC_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_DMC_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + // RANDOTODO: A number of tricks to reach this: sidehop jumpslash or hookshot + jumpslash from bridge platform, chu+shield damage boost + LOCATION(RC_DMC_WONDER_BENEATH_BRIDGE_PLATFORM, logic->IsAdult && (logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_HOVER_BOOTS))), + }, {}); diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_trail.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_trail.cpp index 78d406403ed..7e5200f12ac 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_trail.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/death_mountain_trail.cpp @@ -10,20 +10,28 @@ void RegionTable_Init_DeathMountainTrail() { EVENT_ACCESS(LOGIC_FAIRY_ACCESS, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS) && (logic->HasExplosives() || logic->HasItem(RG_GORONS_BRACELET))), }, { //Locations - LOCATION(RC_DMT_CHEST, (logic->BlastOrSmash() || (ctx->GetTrickOption(RT_DMT_BOMBABLE) && logic->IsChild && logic->HasItem(RG_GORONS_BRACELET))) && logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_DMT_FREESTANDING_POH, logic->TakeDamage() || logic->CanUse(RG_HOVER_BOOTS) || (logic->IsAdult && CanPlantBean(RR_DEATH_MOUNTAIN_TRAIL, RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL) && (logic->HasExplosives() || logic->HasItem(RG_GORONS_BRACELET)))), - LOCATION(RC_DMT_GS_BEAN_PATCH, logic->CanSpawnSoilSkull(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL) && (logic->HasExplosives() || logic->HasItem(RG_GORONS_BRACELET) || (ctx->GetTrickOption(RT_DMT_SOIL_GS) && (logic->TakeDamage() || logic->CanUse(RG_HOVER_BOOTS)) && logic->CanUse(RG_BOOMERANG)))), - LOCATION(RC_DMT_GS_NEAR_KAK, logic->BlastOrSmash() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_DMT_GS_ABOVE_DODONGOS_CAVERN, logic->IsAdult && logic->CanGetNightTimeGS() && - ((logic->CanUse(RG_MEGATON_HAMMER) || (ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->CanUse(RG_HOOKSHOT)) || (ctx->GetTrickOption(RT_DISTANT_BOULDER_COLLISION) && logic->CanUse(RG_LONGSHOT)) || (ctx->GetTrickOption(RT_DMT_JS_LOWER_GS) && logic->CanJumpslash())) || - ((ctx->GetTrickOption(RT_DMT_BEAN_LOWER_GS) && CanPlantBean(RR_DEATH_MOUNTAIN_TRAIL, RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL)) || (ctx->GetTrickOption(RT_DMT_HOVERS_LOWER_GS) && logic->CanUse(RG_HOVER_BOOTS)) && - (logic->HasExplosives() || logic->CanUse(RG_DINS_FIRE) || ((ctx->GetTrickOption(RT_DISTANT_BOULDER_COLLISION) || ctx->GetTrickOption(RT_ITEM_EXTENSION)) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT))) || logic->CanJumpslash())))), - LOCATION(RC_DMT_BLUE_RUPEE, logic->IsChild && logic->BlastOrSmash()), - LOCATION(RC_DMT_RED_RUPEE, logic->IsChild && logic->BlastOrSmash()), - LOCATION(RC_DMT_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS) && (logic->HasExplosives() || logic->HasItem(RG_GORONS_BRACELET))), - LOCATION(RC_DMT_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS) && (logic->HasExplosives() || logic->HasItem(RG_GORONS_BRACELET))), - LOCATION(RC_DMT_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS) && (logic->HasExplosives() || logic->HasItem(RG_GORONS_BRACELET))), - LOCATION(RC_DMT_FLAG_SUN_FAIRY, logic->CanUse(RG_SUNS_SONG)), + LOCATION(RC_DMT_CHEST, (logic->BlastOrSmash() || (ctx->GetTrickOption(RT_DMT_BOMBABLE) && logic->IsChild && logic->HasItem(RG_GORONS_BRACELET))) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_DMT_FREESTANDING_POH, logic->TakeDamage() || logic->CanUse(RG_HOVER_BOOTS) || (logic->IsAdult && CanPlantBean(RR_DEATH_MOUNTAIN_TRAIL, RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL) && (logic->HasExplosives() || logic->HasItem(RG_GORONS_BRACELET)))), + LOCATION(RC_DMT_GS_BEAN_PATCH, logic->CanSpawnSoilSkull(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL) && (logic->HasExplosives() || logic->HasItem(RG_GORONS_BRACELET) || (ctx->GetTrickOption(RT_DMT_SOIL_GS) && (logic->TakeDamage() || logic->CanUse(RG_HOVER_BOOTS)) && logic->CanUse(RG_BOOMERANG)))), + LOCATION(RC_DMT_GS_NEAR_KAK, logic->BlastOrSmash() && (logic->HasItem(RG_CLIMB) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_BOOMERANG))), + LOCATION(RC_DMT_GS_ABOVE_DODONGOS_CAVERN, logic->IsAdult && logic->CanGetNightTimeGS() && + ((logic->CanUse(RG_MEGATON_HAMMER) || (ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->CanUse(RG_HOOKSHOT)) || (ctx->GetTrickOption(RT_BOULDER_COLLISION) && logic->CanUse(RG_LONGSHOT)) || (ctx->GetTrickOption(RT_DMT_JS_LOWER_GS) && logic->CanJumpslash())) || + ((ctx->GetTrickOption(RT_DMT_BEAN_LOWER_GS) && CanPlantBean(RR_DEATH_MOUNTAIN_TRAIL, RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL)) || (ctx->GetTrickOption(RT_DMT_HOVERS_LOWER_GS) && logic->CanUse(RG_HOVER_BOOTS)) && + (logic->HasExplosives() || logic->CanUse(RG_DINS_FIRE) || ((ctx->GetTrickOption(RT_BOULDER_COLLISION) || ctx->GetTrickOption(RT_ITEM_EXTENSION)) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT))) || logic->CanJumpslash())))), + LOCATION(RC_DMT_BLUE_RUPEE, logic->IsChild && logic->BlastOrSmash()), + LOCATION(RC_DMT_RED_RUPEE, logic->IsChild && logic->BlastOrSmash()), + LOCATION(RC_DMT_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS) && (logic->HasExplosives() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_DMT_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS) && (logic->HasExplosives() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_DMT_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS) && (logic->HasExplosives() || logic->HasItem(RG_GORONS_BRACELET))), + LOCATION(RC_DMT_FLAG_SUN_FAIRY, logic->CanUse(RG_SUNS_SONG)), + LOCATION(RC_DMT_ABOVE_DODONGO_RECTANGLE_SIGN, logic->IsChild && logic->CanRead()), + LOCATION(RC_DMT_ADULT_CENTER_EXIT_ARROW_SIGN, logic->IsAdult && logic->CanRead()), + LOCATION(RC_DMT_CHILD_CENTER_EXIT_RECTANGLE_SIGN, logic->IsChild && logic->CanRead()), + LOCATION(RC_DMT_DODONGOS_CAVERN_RECTANGLE_SIGN, logic->CanRead()), + LOCATION(RC_DMT_CENTER_TRAIL_RECTANGLE_SIGN, logic->CanRead()), + LOCATION(RC_DMT_TO_UPPER_TRAIL_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_DMT_TO_CENTER_EXIT_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_DMT_LOWER_EXIT_ARROW_SIGN, logic->CanRead()), }, { //Exits ENTRANCE(RR_KAK_BEHIND_GATE, true), @@ -36,9 +44,9 @@ void RegionTable_Init_DeathMountainTrail() { areaTable[RR_DEATH_MOUNTAIN_ROCKFALL] = Region("Death Mountain Rockfall", SCENE_DEATH_MOUNTAIN_TRAIL, {}, { //Locations LOCATION(RC_DMT_GS_FALLING_ROCKS_PATH, logic->IsAdult && logic->CanGetNightTimeGS() && - (logic->CanUse(RG_MEGATON_HAMMER) || (ctx->GetTrickOption(RT_DISTANT_BOULDER_COLLISION) && logic->CanUse(RG_LONGSHOT)) || (ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->CanUse(RG_HOOKSHOT)) || + (logic->CanUse(RG_MEGATON_HAMMER) || (ctx->GetTrickOption(RT_BOULDER_COLLISION) && logic->CanUse(RG_LONGSHOT)) || (ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->CanUse(RG_HOOKSHOT)) || (ctx->GetTrickOption(RT_DMT_UPPER_GS) && (logic->CanJumpslash() || logic->CanUse(RG_DINS_FIRE) || logic->HasExplosives() || (ctx->GetTrickOption(RT_ITEM_EXTENSION) && logic->CanUse(RG_FAIRY_SLINGSHOT)) || - (ctx->GetTrickOption(RT_DISTANT_BOULDER_COLLISION) && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_LONGSHOT)))))), + (ctx->GetTrickOption(RT_BOULDER_COLLISION) && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_LONGSHOT)))))), }, { //Exits ENTRANCE(RR_DEATH_MOUNTAIN_TRAIL, true), @@ -60,6 +68,7 @@ void RegionTable_Init_DeathMountainTrail() { LOCATION(RC_DMT_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_DMT_GOSSIP_STONE, true), LOCATION(RC_BIGGORON_HINT, logic->IsAdult && logic->HasItem(RG_SPEAK_GORON)), + LOCATION(RC_DMT_UPPER_EXIT_ARROW_SIGN, logic->CanRead()), }, { //Exits ENTRANCE(RR_DEATH_MOUNTAIN_ROCKFALL, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/desert_colossus.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/desert_colossus.cpp index a60194dd191..e53312b5a61 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/desert_colossus.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/desert_colossus.cpp @@ -10,20 +10,25 @@ void RegionTable_Init_DesertColossus() { EVENT_ACCESS(LOGIC_BUG_ACCESS, logic->HasItem(RG_POWER_BRACELET)), }, { //Locations - LOCATION(RC_COLOSSUS_FREESTANDING_POH, logic->IsAdult && CanPlantBean(RR_DESERT_COLOSSUS, RG_DESERT_COLOSSUS_BEAN_SOUL)), - LOCATION(RC_COLOSSUS_GS_BEAN_PATCH, logic->CanSpawnSoilSkull(RG_DESERT_COLOSSUS_BEAN_SOUL) && logic->CanAttack()), - LOCATION(RC_COLOSSUS_GS_TREE, logic->IsAdult && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), - LOCATION(RC_COLOSSUS_GS_HILL, logic->IsAdult && ((CanPlantBean(RR_DESERT_COLOSSUS, RG_DESERT_COLOSSUS_BEAN_SOUL) && logic->CanAttack()) || logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_COLOSSUS_GS) && logic->CanUse(RG_HOOKSHOT))) && logic->CanGetNightTimeGS()), - LOCATION(RC_COLOSSUS_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DESERT_COLOSSUS_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_COLOSSUS_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DESERT_COLOSSUS_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_COLOSSUS_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DESERT_COLOSSUS_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_COLOSSUS_GOSSIP_STONE_FAIRY, logic->CallGossipFairy()), - LOCATION(RC_COLOSSUS_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_COLOSSUS_GOSSIP_STONE, true), + LOCATION(RC_COLOSSUS_FREESTANDING_POH, logic->IsAdult && CanPlantBean(RR_DESERT_COLOSSUS, RG_DESERT_COLOSSUS_BEAN_SOUL)), + LOCATION(RC_COLOSSUS_GS_BEAN_PATCH, logic->CanSpawnSoilSkull(RG_DESERT_COLOSSUS_BEAN_SOUL) && logic->CanAttack()), + LOCATION(RC_COLOSSUS_GS_TREE, logic->IsAdult && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), + LOCATION(RC_COLOSSUS_GS_HILL, logic->IsAdult && ((CanPlantBean(RR_DESERT_COLOSSUS, RG_DESERT_COLOSSUS_BEAN_SOUL) && logic->CanAttack()) || logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_COLOSSUS_GS) && logic->CanUse(RG_HOOKSHOT))) && logic->CanGetNightTimeGS()), + LOCATION(RC_COLOSSUS_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DESERT_COLOSSUS_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_COLOSSUS_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DESERT_COLOSSUS_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_COLOSSUS_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DESERT_COLOSSUS_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_COLOSSUS_GOSSIP_STONE_FAIRY, logic->CallGossipFairy()), + LOCATION(RC_COLOSSUS_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_COLOSSUS_GOSSIP_STONE, true), + LOCATION(RC_COLOSSUS_WONDER_OASIS_TREE_1, (logic->IsAdult && logic->CanUse(RG_FAIRY_BOW)) || (logic->IsChild && logic->CanUse(RG_FAIRY_SLINGSHOT))), + LOCATION(RC_COLOSSUS_WONDER_OASIS_TREE_2, (logic->IsAdult && logic->CanUse(RG_FAIRY_BOW)) || (logic->IsChild && logic->CanUse(RG_FAIRY_SLINGSHOT))), + LOCATION(RC_COLOSSUS_WONDER_OASIS_CHILD_TREE, logic->IsChild && logic->CanUse(RG_FAIRY_SLINGSHOT)), + LOCATION(RC_COLOSSUS_WONDER_GF_TREE_1, (logic->IsAdult && logic->CanUse(RG_FAIRY_BOW)) || (logic->IsChild && logic->CanUse(RG_FAIRY_SLINGSHOT))), + LOCATION(RC_COLOSSUS_WONDER_GF_TREE_2, (logic->IsAdult && logic->CanUse(RG_FAIRY_BOW)) || (logic->IsChild && logic->CanUse(RG_FAIRY_SLINGSHOT))), }, { //Exits //You can kinda get the fairies without entering the water, but it relies on them cooperating and leevers are jerks. should be a trick - ENTRANCE(RR_DESERT_COLOSSUS_OASIS, logic->CanUse(RG_SONG_OF_STORMS) && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->HasBottle())), + ENTRANCE(RR_DESERT_COLOSSUS_OASIS, logic->CanUse(RG_SONG_OF_STORMS) && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->HasBottle() || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), ENTRANCE(RR_COLOSSUS_GREAT_FAIRY_FOUNTAIN, logic->HasExplosives()), ENTRANCE(RR_SPIRIT_TEMPLE_ENTRYWAY, true), ENTRANCE(RR_WASTELAND_NEAR_COLOSSUS, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp index 9d797c0e0d4..bc2016ef5fd 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_fortress.cpp @@ -9,8 +9,10 @@ void RegionTable_Init_GerudoFortress() { areaTable[RR_GF_OUTSKIRTS] = Region("Gerudo Fortress Outskirts", SCENE_GERUDOS_FORTRESS, {}, { //Locations - LOCATION(RC_GF_OUTSKIRTS_NE_CRATE, (logic->IsChild || logic->CanPassEnemy(RE_GERUDO_GUARD)) && logic->CanBreakCrates()), - LOCATION(RC_GF_OUTSKIRTS_NW_CRATE, (logic->IsChild || logic->CanPassEnemy(RE_GERUDO_GUARD)) && logic->CanBreakCrates()), + LOCATION(RC_GF_OUTSKIRTS_NE_CRATE, (logic->IsChild || logic->CanPassEnemy(RE_GERUDO_GUARD)) && logic->CanBreakCrates()), + LOCATION(RC_GF_OUTSKIRTS_NW_CRATE, (logic->IsChild || logic->CanPassEnemy(RE_GERUDO_GUARD)) && logic->CanBreakCrates()), + LOCATION(RC_GF_EAST_EXIT_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_GF_WONDER_ENTRANCE_SIGN, logic->CanUse(RG_HOOKSHOT)), }, { //Exits ENTRANCE(RR_GV_FORTRESS_SIDE, true), @@ -45,7 +47,10 @@ void RegionTable_Init_GerudoFortress() { areaTable[RR_GF_OUTSIDE_GTG] = Region("GF Outside GTG", SCENE_GERUDOS_FORTRESS, { //Events EVENT_ACCESS(LOGIC_GTG_GATE_OPEN, logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) && logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_GERUDO)), - }, {}, { + }, { + //Locations + LOCATION(RC_GF_GTG_ENTRANCE_RECTANGLE_SIGN, logic->IsAdult && logic->CanRead()), + }, { //Exits ENTRANCE(RR_GF_TO_GTG, logic->Get(LOGIC_GTG_GATE_OPEN) && (logic->IsAdult || ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES))), //Jail @@ -203,13 +208,13 @@ void RegionTable_Init_GerudoFortress() { //there's a trick to reach RR_GF_LONG_ROOF //For some reason, you take fall damage if you backflip onto the fortress but not onto the sand //It's unintuitive to avoid being caught on landing, but that sends you to the same place anyway... - ENTRANCE(RR_GF_OUTSKIRTS, true), - ENTRANCE(RR_GF_NEAR_CHEST, logic->CanUse(RG_LONGSHOT)), - ENTRANCE(RR_GF_BELOW_CHEST, logic->TakeDamage()), - ENTRANCE(RR_GF_JAIL_WINDOW, logic->CanUse(RG_HOOKSHOT)), - ENTRANCE(RR_TH_BREAK_ROOM_CORRIDOR, true), - ENTRANCE(RR_GF_TOWER, ctx->GetTrickOption(RT_GF_ADULT_SKIP_WASTELAND_GATE) && logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS) && logic->CanJumpslash() && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)), - ENTRANCE(RR_GF_OUTSIDE_GATE, ctx->GetTrickOption(RT_GF_ADULT_SKIP_WASTELAND_GATE) && logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS) && logic->CanJumpslash()), + ENTRANCE(RR_GF_OUTSKIRTS, ctx->GetTrickOption(RT_UNINTUITIVE_JUMPS) || logic->TakeDamage()), + ENTRANCE(RR_GF_NEAR_CHEST, logic->CanUse(RG_LONGSHOT)), + ENTRANCE(RR_GF_BELOW_CHEST, logic->TakeDamage()), + ENTRANCE(RR_GF_JAIL_WINDOW, logic->CanUse(RG_HOOKSHOT)), + ENTRANCE(RR_TH_BREAK_ROOM_UPPER_CORRIDOR, true), + ENTRANCE(RR_GF_TOWER, ctx->GetTrickOption(RT_GF_ADULT_SKIP_WASTELAND_GATE) && logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS) && logic->CanJumpslash() && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)), + ENTRANCE(RR_GF_OUTSIDE_GATE, ctx->GetTrickOption(RT_GF_ADULT_SKIP_WASTELAND_GATE) && logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS) && logic->CanJumpslash()), }); areaTable[RR_GF_JAIL_WINDOW] = Region("GF Jail Window", SCENE_GERUDOS_FORTRESS, {}, {}, { @@ -240,6 +245,8 @@ void RegionTable_Init_GerudoFortress() { LOCATION(RC_GF_NORTH_TARGET_CHILD_CRATE, logic->IsChild && logic->BlastOrSmash()), LOCATION(RC_GF_SOUTH_TARGET_EAST_CRATE, logic->CanBreakCrates()), LOCATION(RC_GF_SOUTH_TARGET_WEST_CRATE, logic->CanBreakCrates()), + LOCATION(RC_GF_HBA_RECTANGLE_SIGN, logic->IsAdult && logic->CanRead()), + LOCATION(RC_GF_WONDER_ARCHERY_SIGN, logic->CanUse(RG_HOOKSHOT)), }, { //Exits ENTRANCE(RR_GF_OUTSIDE_GTG, logic->IsChild || logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD)), @@ -248,7 +255,10 @@ void RegionTable_Init_GerudoFortress() { areaTable[RR_GF_OUTSIDE_GATE] = Region("GF Outside Gate", SCENE_GERUDOS_FORTRESS, { //Events EVENT_ACCESS(LOGIC_GF_GATE_OPEN, logic->IsAdult && logic->HasItem(RG_GERUDO_MEMBERSHIP_CARD) && logic->HasItem(RG_SPEAK_GERUDO)), - }, {}, { + }, { + //Locations + LOCATION(RC_GF_GATE_EXIT_RECTANGLE_SIGN, logic->IsAdult && logic->CanRead()), + }, { //Exits ENTRANCE(RR_GF_OUTSKIRTS, logic->Get(LOGIC_GF_GATE_OPEN)), ENTRANCE(RR_WASTELAND_NEAR_FORTRESS, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_valley.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_valley.cpp index d93b877d4ee..713eaa64a4d 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_valley.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_valley.cpp @@ -10,7 +10,9 @@ void RegionTable_Init_GerudoValley() { EVENT_ACCESS(LOGIC_BUG_ACCESS, logic->IsChild && logic->HasItem(RG_POWER_BRACELET)), }, { //Locations - LOCATION(RC_GV_GS_SMALL_BRIDGE, logic->IsChild && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), + LOCATION(RC_GV_GS_SMALL_BRIDGE, logic->IsChild && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), + LOCATION(RC_GV_BRIDGE_RECTANGLE_SIGN, logic->CanRead()), + LOCATION(RC_GV_EAST_EXIT_ARROW_SIGN, logic->CanRead()), }, { //Exits ENTRANCE(RR_HYRULE_FIELD, true), @@ -38,6 +40,8 @@ void RegionTable_Init_GerudoValley() { LOCATION(RC_GV_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_GV_GOSSIP_STONE, true), LOCATION(RC_GV_NEAR_COW_CRATE, logic->IsChild && logic->CanBreakCrates()), + LOCATION(RC_GV_WONDER_LOWER_WATERFALL, logic->IsAdult && (CanPlantBean(RR_GV_UPPER_STREAM, RG_GERUDO_VALLEY_BEAN_SOUL) || logic->CanUse(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_GV_WONDER_UPPER_WATERFALL, logic->IsAdult && CanPlantBean(RR_GV_UPPER_STREAM, RG_GERUDO_VALLEY_BEAN_SOUL)), }, { //Exits ENTRANCE(RR_GV_UPPER_STREAM_WATER, true), @@ -60,6 +64,7 @@ void RegionTable_Init_GerudoValley() { areaTable[RR_GV_WATERFALL_ALCOVE] = Region("GV Waterfall Alcove", SCENE_GERUDO_VALLEY, {}, { //Locations LOCATION(RC_GV_WATERFALL_FREESTANDING_POH, true), + LOCATION(RC_GV_WONDER_UPPER_WATERFALL, logic->IsAdult && (logic->CanUse(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), }, { //Exits ENTRANCE(RR_GV_UPPER_STREAM, logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS)), @@ -86,7 +91,7 @@ void RegionTable_Init_GerudoValley() { areaTable[RR_GV_FORTRESS_SIDE] = Region("GV Fortress Side", SCENE_GERUDO_VALLEY, {}, { //Locations - LOCATION(RC_GV_CHEST, logic->IsAdult && (logic->CanUse(RG_MEGATON_HAMMER) || (ctx->GetTrickOption(RT_DISTANT_BOULDER_COLLISION) && logic->CanUse(RG_LONGSHOT))) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_GV_CHEST, logic->IsAdult && (logic->CanUse(RG_MEGATON_HAMMER) || (ctx->GetTrickOption(RT_BOULDER_COLLISION) && logic->CanUse(RG_LONGSHOT))) && logic->HasItem(RG_OPEN_CHEST)), LOCATION(RC_GV_TRADE_SAW, logic->IsAdult && logic->CanUse(RG_POACHERS_SAW)), LOCATION(RC_GV_GS_BEHIND_TENT, logic->IsAdult && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), LOCATION(RC_GV_GS_PILLAR, logic->IsAdult && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), @@ -112,14 +117,14 @@ void RegionTable_Init_GerudoValley() { areaTable[RR_GV_OCTOROK_GROTTO] = Region("GV Octorok Grotto", SCENE_GROTTOS, {}, { //Locations - LOCATION(RC_GV_OCTOROK_GROTTO_FRONT_LEFT_BLUE_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG)), - LOCATION(RC_GV_OCTOROK_GROTTO_FRONT_RIGHT_BLUE_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG)), - LOCATION(RC_GV_OCTOROK_GROTTO_BACK_BLUE_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG)), - LOCATION(RC_GV_OCTOROK_GROTTO_FRONT_LEFT_GREEN_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG)), - LOCATION(RC_GV_OCTOROK_GROTTO_FRONT_RIGHT_GREEN_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG)), - LOCATION(RC_GV_OCTOROK_GROTTO_BACK_LEFT_GREEN_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG)), - LOCATION(RC_GV_OCTOROK_GROTTO_BACK_RIGHT_GREEN_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG)), - LOCATION(RC_GV_OCTOROK_GROTTO_RED_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_GV_OCTOROK_GROTTO_FRONT_LEFT_BLUE_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || (logic->IsAdult && ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_GV_OCTOROK_GROTTO_FRONT_RIGHT_BLUE_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || (logic->IsAdult && ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_GV_OCTOROK_GROTTO_BACK_BLUE_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || (logic->IsAdult && ctx->GetTrickOption(RT_VOIDOUT_COLLECTION) && logic->CanUse(RG_HOVER_BOOTS))), + LOCATION(RC_GV_OCTOROK_GROTTO_FRONT_LEFT_GREEN_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || (logic->IsAdult && ctx->GetTrickOption(RT_VOIDOUT_COLLECTION) && logic->CanUse(RG_HOVER_BOOTS))), + LOCATION(RC_GV_OCTOROK_GROTTO_FRONT_RIGHT_GREEN_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || (logic->IsAdult && ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_GV_OCTOROK_GROTTO_BACK_LEFT_GREEN_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || (logic->IsAdult && ctx->GetTrickOption(RT_VOIDOUT_COLLECTION) && logic->CanUse(RG_HOVER_BOOTS))), + LOCATION(RC_GV_OCTOROK_GROTTO_BACK_RIGHT_GREEN_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || (logic->IsAdult && ctx->GetTrickOption(RT_VOIDOUT_COLLECTION) && logic->CanUse(RG_HOVER_BOOTS))), + LOCATION(RC_GV_OCTOROK_GROTTO_RED_RUPEE, logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || (logic->IsAdult && ctx->GetTrickOption(RT_VOIDOUT_COLLECTION) && logic->CanUse(RG_HOVER_BOOTS))), }, { //Exits ENTRANCE(RR_GV_GROTTO_LEDGE, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/goron_city.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/goron_city.cpp index e1995b0cec8..8ca66bd5f57 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/goron_city.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/goron_city.cpp @@ -18,24 +18,24 @@ void RegionTable_Init_GoronCity() { (ctx->GetTrickOption(RT_GC_LINK_GORON_DINS) && (logic->CanUse(RG_DINS_FIRE) || (ctx->GetTrickOption(RT_BLUE_FIRE_MUD_WALLS) && logic->CanUse(RG_BOTTLE_WITH_BLUE_FIRE)))))), }, { //Locations - LOCATION(RC_GC_MAZE_LEFT_CHEST, (logic->CanUse(RG_MEGATON_HAMMER) || logic->CanUse(RG_SILVER_GAUNTLETS) || (ctx->GetTrickOption(RT_GC_LEFTMOST) && logic->HasExplosives() && logic->CanUse(RG_HOVER_BOOTS))) && logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_GC_MAZE_CENTER_CHEST, (logic->BlastOrSmash() || logic->CanUse(RG_SILVER_GAUNTLETS)) && logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_GC_MAZE_RIGHT_CHEST, (logic->BlastOrSmash() || logic->CanUse(RG_SILVER_GAUNTLETS)) && logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_GC_POT_FREESTANDING_POH, logic->IsChild && logic->Get(LOGIC_GORON_CITY_CHILD_FIRE) && (logic->CanUse(RG_BOMB_BAG) || (logic->HasItem(RG_GORONS_BRACELET) && ctx->GetTrickOption(RT_GC_POT_STRENGTH)) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_GC_POT)))), - LOCATION(RC_GC_ROLLING_GORON_AS_CHILD, logic->IsChild && logic->HasItem(RG_SPEAK_GORON) && (logic->HasExplosives() || (logic->HasItem(RG_GORONS_BRACELET) && ctx->GetTrickOption(RT_GC_ROLLING_STRENGTH)))), - LOCATION(RC_GC_ROLLING_GORON_AS_ADULT, logic->Get(LOGIC_GORON_CITY_STOP_ROLLING_GORON_AS_ADULT)), - LOCATION(RC_GC_GS_BOULDER_MAZE, logic->IsChild && logic->BlastOrSmash()), - LOCATION(RC_GC_GS_CENTER_PLATFORM, logic->IsAdult && logic->CanAttack()), - LOCATION(RC_GC_MAZE_GOSSIP_STONE_FAIRY, (logic->BlastOrSmash() || logic->CanUse(RG_SILVER_GAUNTLETS)) && logic->CallGossipFairyExceptSuns()), - LOCATION(RC_GC_MAZE_GOSSIP_STONE_FAIRY_BIG, (logic->BlastOrSmash() || logic->CanUse(RG_SILVER_GAUNTLETS)) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_GC_MAZE_GOSSIP_STONE, logic->BlastOrSmash() || logic->CanUse(RG_SILVER_GAUNTLETS)), - LOCATION(RC_GC_LOWER_STAIRCASE_POT_1, logic->CanBreakPots()), - LOCATION(RC_GC_LOWER_STAIRCASE_POT_2, logic->CanBreakPots()), - LOCATION(RC_GC_UPPER_STAIRCASE_POT_1, logic->CanBreakPots()), - LOCATION(RC_GC_UPPER_STAIRCASE_POT_2, logic->CanBreakPots()), - LOCATION(RC_GC_UPPER_STAIRCASE_POT_3, logic->CanBreakPots()), - LOCATION(RC_GC_MAZE_CRATE, logic->BlastOrSmash() || (logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanBreakCrates())), - + LOCATION(RC_GC_MAZE_LEFT_CHEST, (logic->CanUse(RG_MEGATON_HAMMER) || logic->CanUse(RG_SILVER_GAUNTLETS) || (ctx->GetTrickOption(RT_GC_LEFTMOST) && logic->HasExplosives() && logic->CanUse(RG_HOVER_BOOTS))) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_GC_MAZE_CENTER_CHEST, (logic->BlastOrSmash() || logic->CanUse(RG_SILVER_GAUNTLETS)) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_GC_MAZE_RIGHT_CHEST, (logic->BlastOrSmash() || logic->CanUse(RG_SILVER_GAUNTLETS)) && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_GC_POT_FREESTANDING_POH, logic->IsChild && logic->Get(LOGIC_GORON_CITY_CHILD_FIRE) && (logic->CanUse(RG_BOMB_BAG) || (logic->HasItem(RG_GORONS_BRACELET) && ctx->GetTrickOption(RT_GC_POT_STRENGTH)) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_GC_POT)))), + LOCATION(RC_GC_ROLLING_GORON_AS_CHILD, logic->IsChild && logic->HasItem(RG_SPEAK_GORON) && (logic->HasExplosives() || (logic->HasItem(RG_GORONS_BRACELET) && ctx->GetTrickOption(RT_GC_ROLLING_STRENGTH)))), + LOCATION(RC_GC_ROLLING_GORON_AS_ADULT, logic->Get(LOGIC_GORON_CITY_STOP_ROLLING_GORON_AS_ADULT)), + LOCATION(RC_GC_GS_BOULDER_MAZE, logic->IsChild && logic->BlastOrSmash()), + LOCATION(RC_GC_GS_CENTER_PLATFORM, logic->IsAdult && logic->CanAttack()), + LOCATION(RC_GC_MAZE_GOSSIP_STONE_FAIRY, (logic->BlastOrSmash() || logic->CanUse(RG_SILVER_GAUNTLETS)) && logic->CallGossipFairyExceptSuns()), + LOCATION(RC_GC_MAZE_GOSSIP_STONE_FAIRY_BIG, (logic->BlastOrSmash() || logic->CanUse(RG_SILVER_GAUNTLETS)) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_GC_MAZE_GOSSIP_STONE, logic->BlastOrSmash() || logic->CanUse(RG_SILVER_GAUNTLETS)), + LOCATION(RC_GC_LOWER_STAIRCASE_POT_1, logic->CanBreakPots()), + LOCATION(RC_GC_LOWER_STAIRCASE_POT_2, logic->CanBreakPots()), + LOCATION(RC_GC_UPPER_STAIRCASE_POT_1, logic->CanBreakPots()), + LOCATION(RC_GC_UPPER_STAIRCASE_POT_2, logic->CanBreakPots()), + LOCATION(RC_GC_UPPER_STAIRCASE_POT_3, logic->CanBreakPots()), + LOCATION(RC_GC_MAZE_CRATE, logic->BlastOrSmash() || (logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanBreakCrates())), + LOCATION(RC_GC_CHILD_ROLLING_GORON_RECTANGLE_SIGN, logic->IsChild && logic->CanRead()), }, { //Exits ENTRANCE(RR_DEATH_MOUNTAIN_TRAIL, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp index 77ac0aa44d0..a168841b0c2 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp @@ -32,6 +32,12 @@ void RegionTable_Init_Graveyard() { LOCATION(RC_GY_GRASS_11, logic->CanCutShrubs()), LOCATION(RC_GY_GRASS_12, logic->CanCutShrubs()), LOCATION(RC_GRAVEYARD_CRATE, ((logic->IsAdult && CanPlantBean(RR_THE_GRAVEYARD, RG_GRAVEYARD_BEAN_SOUL)) || logic->CanUse(RG_LONGSHOT)) && logic->CanBreakCrates()), + LOCATION(RC_GY_NEAR_HUT_GRAVE_BUTTERFLY_FAIRY, logic->IsChild && logic->AtDay && logic->CanUse(RG_STICKS)), + LOCATION(RC_GY_ENTRANCE_RECTANGLE_SIGN, logic->CanRead()), + LOCATION(RC_GY_ENTRANCE_PLINTH, logic->CanRead()), + LOCATION(RC_GY_RIGHT_OF_ROYAL_TOMB_GRAVE, logic->CanRead()), + LOCATION(RC_GY_LEFT_OF_ROYAL_TOMB_GRAVE, logic->CanRead()), + LOCATION(RC_GY_ROYAL_TOMB_GRAVE, logic->CanRead() || logic->CanUse(RG_ZELDAS_LULLABY)), }, { //Exits ENTRANCE(RR_GRAVEYARD_SHIELD_GRAVE, (logic->IsAdult || logic->AtNight) && logic->HasItem(RG_POWER_BRACELET)), @@ -106,6 +112,21 @@ void RegionTable_Init_Graveyard() { LOCATION(RC_GRAVEYARD_DAMPE_RACE_RUPEE_6, true), LOCATION(RC_GRAVEYARD_DAMPE_RACE_RUPEE_7, true), LOCATION(RC_GRAVEYARD_DAMPE_RACE_RUPEE_8, true), + LOCATION(RC_GY_WONDER_DAMPE_RACE_1, true), + LOCATION(RC_GY_WONDER_DAMPE_RACE_2, true), + LOCATION(RC_GY_WONDER_DAMPE_RACE_3, true), + LOCATION(RC_GY_WONDER_DAMPE_RACE_4, true), + LOCATION(RC_GY_WONDER_DAMPE_RACE_5, true), + LOCATION(RC_GY_WONDER_DAMPE_RACE_6, true), + LOCATION(RC_GY_WONDER_DAMPE_RACE_7, true), + LOCATION(RC_GY_WONDER_DAMPE_RACE_8, true), + LOCATION(RC_GY_WONDER_DAMPE_RACE_9, true), + LOCATION(RC_GY_WONDER_DAMPE_RACE_10, true), + LOCATION(RC_GY_WONDER_DAMPE_RACE_11, true), + LOCATION(RC_GY_WONDER_DAMPE_RACE_12, true), + LOCATION(RC_GY_WONDER_DAMPE_RACE_13, true), + LOCATION(RC_GY_WONDER_DAMPE_RACE_14, true), + LOCATION(RC_GY_WONDER_DAMPE_RACE_15, true), }, { //Exits ENTRANCE(RR_THE_GRAVEYARD, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/haunted_wasteland.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/haunted_wasteland.cpp index a78cb1f53e2..8766b6db663 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/haunted_wasteland.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/haunted_wasteland.cpp @@ -22,16 +22,18 @@ void RegionTable_Init_HauntedWasteland() { EVENT_ACCESS(LOGIC_CARPET_MERCHANT, logic->HasItem(RG_ADULT_WALLET) && GetCheckPrice(RC_WASTELAND_BOMBCHU_SALESMAN) <= GetWalletCapacity() && (logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS))), }, { //Locations - LOCATION(RC_WASTELAND_CHEST, logic->HasFireSource() && logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_WASTELAND_BOMBCHU_SALESMAN, (logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS)) && logic->HasItem(RG_SPEAK_HYLIAN) && GetCheckPrice() <= GetWalletCapacity()), - LOCATION(RC_WASTELAND_GS, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG) || (logic->IsAdult && logic->CanGroundJumpslash())), // need to jumpslash immediately with two handed weapons - LOCATION(RC_WASTELAND_NEAR_GS_POT_1, logic->CanBreakPots()), - LOCATION(RC_WASTELAND_NEAR_GS_POT_2, logic->CanBreakPots()), - LOCATION(RC_WASTELAND_NEAR_GS_POT_3, logic->CanBreakPots()), - LOCATION(RC_WASTELAND_NEAR_GS_POT_4, logic->CanBreakPots()), - LOCATION(RC_HW_AFTER_QUICKSAND_CRATE_1, logic->CanBreakCrates()), - LOCATION(RC_HW_AFTER_QUICKSAND_CRATE_2, logic->CanBreakCrates()), - LOCATION(RC_HW_AFTER_QUICKSAND_CRATE_3, logic->CanBreakCrates()), + LOCATION(RC_WASTELAND_CHEST, logic->HasFireSource() && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_WASTELAND_BOMBCHU_SALESMAN, (logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS)) && logic->HasItem(RG_SPEAK_HYLIAN) && GetCheckPrice() <= GetWalletCapacity()), + LOCATION(RC_WASTELAND_GS, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG) || (logic->IsAdult && logic->CanGroundJumpslash())), // need to jumpslash immediately with two handed weapons + LOCATION(RC_WASTELAND_NEAR_GS_POT_1, logic->CanBreakPots()), + LOCATION(RC_WASTELAND_NEAR_GS_POT_2, logic->CanBreakPots()), + LOCATION(RC_WASTELAND_NEAR_GS_POT_3, logic->CanBreakPots()), + LOCATION(RC_WASTELAND_NEAR_GS_POT_4, logic->CanBreakPots()), + LOCATION(RC_HW_AFTER_QUICKSAND_CRATE_1, logic->CanBreakCrates()), + LOCATION(RC_HW_AFTER_QUICKSAND_CRATE_2, logic->CanBreakCrates()), + LOCATION(RC_HW_AFTER_QUICKSAND_CRATE_3, logic->CanBreakCrates()), + LOCATION(RC_HW_CARPET_SALESMAN_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_HW_POE_ALTAR, logic->CanRead()), }, { //Exits ENTRANCE(RR_WASTELAND_NEAR_COLOSSUS, ctx->GetTrickOption(RT_LENS_HW) || logic->CanUse(RG_LENS_OF_TRUTH)), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/hyrule_field.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/hyrule_field.cpp index 6691f0e94b4..9445f7bf64c 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/hyrule_field.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/hyrule_field.cpp @@ -11,163 +11,173 @@ void RegionTable_Init_HyruleField() { EVENT_ACCESS(LOGIC_BORROW_RIGHT_MASKS, logic->IsChild && logic->Get(LOGIC_BORROW_BUNNY_HOOD) && logic->HasItem(RG_KOKIRI_EMERALD) && logic->HasItem(RG_GORON_RUBY) && logic->HasItem(RG_ZORA_SAPPHIRE) && logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN)), }, { //Locations - LOCATION(RC_HF_OCARINA_OF_TIME_ITEM, logic->IsChild && logic->StoneCount() == 3 && logic->HasItem(RG_BRONZE_SCALE)), - LOCATION(RC_SONG_FROM_OCARINA_OF_TIME, logic->IsChild && logic->StoneCount() == 3 && logic->HasItem(RG_BRONZE_SCALE)), - LOCATION(RC_HF_POND_STORMS_FAIRY, logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_HF_CENTRAL_GRASS_1, logic->CanCutShrubs()), - LOCATION(RC_HF_CENTRAL_GRASS_2, logic->CanCutShrubs()), - LOCATION(RC_HF_CENTRAL_GRASS_3, logic->CanCutShrubs()), - LOCATION(RC_HF_CENTRAL_GRASS_4, logic->CanCutShrubs()), - LOCATION(RC_HF_CENTRAL_GRASS_5, logic->CanCutShrubs()), - LOCATION(RC_HF_CENTRAL_GRASS_6, logic->CanCutShrubs()), - LOCATION(RC_HF_CENTRAL_GRASS_7, logic->CanCutShrubs()), - LOCATION(RC_HF_CENTRAL_GRASS_8, logic->CanCutShrubs()), - LOCATION(RC_HF_CENTRAL_GRASS_9, logic->CanCutShrubs()), - LOCATION(RC_HF_CENTRAL_GRASS_10, logic->CanCutShrubs()), - LOCATION(RC_HF_CENTRAL_GRASS_11, logic->CanCutShrubs()), - LOCATION(RC_HF_CENTRAL_GRASS_12, logic->CanCutShrubs()), - LOCATION(RC_HF_SOUTH_GRASS_1, logic->CanCutShrubs()), - LOCATION(RC_HF_SOUTH_GRASS_2, logic->CanCutShrubs()), - LOCATION(RC_HF_SOUTH_GRASS_3, logic->CanCutShrubs()), - LOCATION(RC_HF_SOUTH_GRASS_4, logic->CanCutShrubs()), - LOCATION(RC_HF_SOUTH_GRASS_5, logic->CanCutShrubs()), - LOCATION(RC_HF_SOUTH_GRASS_6, logic->CanCutShrubs()), - LOCATION(RC_HF_SOUTH_GRASS_7, logic->CanCutShrubs()), - LOCATION(RC_HF_SOUTH_GRASS_8, logic->CanCutShrubs()), - LOCATION(RC_HF_SOUTH_GRASS_9, logic->CanCutShrubs()), - LOCATION(RC_HF_SOUTH_GRASS_10, logic->CanCutShrubs()), - LOCATION(RC_HF_SOUTH_GRASS_11, logic->CanCutShrubs()), - LOCATION(RC_HF_SOUTH_GRASS_12, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_MARKET_GRASS_1, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_MARKET_GRASS_2, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_MARKET_GRASS_3, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_MARKET_GRASS_4, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_MARKET_GRASS_5, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_MARKET_GRASS_6, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_MARKET_GRASS_7, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_MARKET_GRASS_8, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_MARKET_GRASS_9, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_MARKET_GRASS_10, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_MARKET_GRASS_11, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_MARKET_GRASS_12, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_KF_GRASS_1, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_KF_GRASS_2, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_KF_GRASS_3, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_KF_GRASS_4, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_KF_GRASS_5, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_KF_GRASS_6, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_KF_GRASS_7, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_KF_GRASS_8, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_KF_GRASS_9, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_KF_GRASS_10, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_KF_GRASS_11, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_KF_GRASS_12, logic->CanCutShrubs()), - LOCATION(RC_HF_NEAR_LLR_TREE, logic->CanBonkTrees()), - LOCATION(RC_HF_NEAR_LH_TREE, logic->CanBonkTrees()), - LOCATION(RC_HF_CHILD_NEAR_GV_TREE, logic->IsChild && logic->CanBonkTrees()), - LOCATION(RC_HF_ADULT_NEAR_GV_TREE, logic->IsAdult && logic->CanBonkTrees()), - LOCATION(RC_HF_NEAR_ZR_TREE, logic->CanBonkTrees()), - LOCATION(RC_HF_NEAR_KAK_TREE, logic->CanBonkTrees()), - LOCATION(RC_HF_NEAR_KAK_SMALL_TREE, logic->CanBonkTrees()), - LOCATION(RC_HF_NEAR_MARKET_TREE_1, logic->CanBonkTrees()), - LOCATION(RC_HF_NEAR_MARKET_TREE_2, logic->CanBonkTrees()), - LOCATION(RC_HF_NEAR_MARKET_TREE_3, logic->CanBonkTrees()), - LOCATION(RC_HF_NORTHWEST_TREE_1, logic->CanBonkTrees()), - LOCATION(RC_HF_NORTHWEST_TREE_2, logic->CanBonkTrees()), - LOCATION(RC_HF_NORTHWEST_TREE_3, logic->CanBonkTrees()), - LOCATION(RC_HF_NORTHWEST_TREE_4, logic->CanBonkTrees()), - LOCATION(RC_HF_NORTHWEST_TREE_5, logic->CanBonkTrees()), - LOCATION(RC_HF_NORTHWEST_TREE_6, logic->CanBonkTrees()), - LOCATION(RC_HF_EAST_TREE_1, logic->CanBonkTrees()), - LOCATION(RC_HF_EAST_TREE_2, logic->CanBonkTrees()), - LOCATION(RC_HF_EAST_TREE_3, logic->CanBonkTrees()), - LOCATION(RC_HF_EAST_TREE_4, logic->CanBonkTrees()), - LOCATION(RC_HF_EAST_TREE_5, logic->CanBonkTrees()), - LOCATION(RC_HF_EAST_TREE_6, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_1, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_2, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_3, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_4, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_5, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_6, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_7, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_8, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_9, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_10, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_11, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_12, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_13, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_14, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_15, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_16, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_17, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_18, logic->CanBonkTrees()), - LOCATION(RC_HF_SOUTHEAST_TREE_19, logic->CanBonkTrees()), - LOCATION(RC_HF_CHILD_SOUTHEAST_TREE_1, logic->IsChild && logic->CanBonkTrees()), - LOCATION(RC_HF_CHILD_SOUTHEAST_TREE_2, logic->IsChild && logic->CanBonkTrees()), - LOCATION(RC_HF_CHILD_SOUTHEAST_TREE_3, logic->IsChild && logic->CanBonkTrees()), - LOCATION(RC_HF_CHILD_SOUTHEAST_TREE_4, logic->IsChild && logic->CanBonkTrees()), - LOCATION(RC_HF_CHILD_SOUTHEAST_TREE_5, logic->IsChild && logic->CanBonkTrees()), - LOCATION(RC_HF_CHILD_SOUTHEAST_TREE_6, logic->IsChild && logic->CanBonkTrees()), - LOCATION(RC_HF_TEKTITE_GROTTO_TREE, logic->CanBonkTrees()), - LOCATION(RC_HF_BUSH_NEAR_LAKE_1, true), - LOCATION(RC_HF_BUSH_NEAR_LAKE_2, true), - LOCATION(RC_HF_BUSH_NEAR_LAKE_3, true), - LOCATION(RC_HF_BUSH_NEAR_LAKE_4, true), - LOCATION(RC_HF_BUSH_NEAR_LAKE_5, true), - LOCATION(RC_HF_BUSH_NEAR_LAKE_6, true), - LOCATION(RC_HF_BUSH_NEAR_LAKE_7, true), - LOCATION(RC_HF_BUSH_NEAR_LAKE_8, true), - LOCATION(RC_HF_BUSH_NEAR_LAKE_9, true), - LOCATION(RC_HF_BUSH_NEAR_LAKE_10, true), - LOCATION(RC_HF_BUSH_NEAR_LAKE_11, true), - LOCATION(RC_HF_NORTHERN_BUSH_1, true), - LOCATION(RC_HF_NORTHERN_BUSH_2, true), - LOCATION(RC_HF_NORTHERN_BUSH_3, true), - LOCATION(RC_HF_NORTHERN_BUSH_4, true), - LOCATION(RC_HF_NORTHERN_BUSH_5, true), - LOCATION(RC_HF_NORTHERN_BUSH_6, true), - LOCATION(RC_HF_CHILD_NORTHERN_BUSH_1, logic->IsChild), - LOCATION(RC_HF_CHILD_NORTHERN_BUSH_2, logic->IsChild), - LOCATION(RC_HF_CHILD_NORTHERN_BUSH_3, logic->IsChild), - LOCATION(RC_HF_CHILD_NORTHERN_BUSH_4, logic->IsChild), - LOCATION(RC_HF_CHILD_NORTHERN_BUSH_5, logic->IsChild), - LOCATION(RC_HF_CHILD_NORTHERN_BUSH_6, logic->IsChild), - LOCATION(RC_HF_CHILD_NORTHERN_BUSH_7, logic->IsChild), - LOCATION(RC_HF_CHILD_NORTHERN_BUSH_8, logic->IsChild), - LOCATION(RC_HF_CHILD_NORTHERN_BUSH_9, logic->IsChild), - LOCATION(RC_HF_CHILD_NORTHERN_BUSH_10, logic->IsChild), - LOCATION(RC_HF_CHILD_NORTHERN_BUSH_11, logic->IsChild), - LOCATION(RC_HF_BUSH_BY_ROCKY_PATH_1, true), - LOCATION(RC_HF_BUSH_BY_ROCKY_PATH_2, true), - LOCATION(RC_HF_BUSH_BY_ROCKY_PATH_3, true), - LOCATION(RC_HF_BUSH_BY_ROCKY_PATH_4, true), - LOCATION(RC_HF_BUSH_BY_ROCKY_PATH_5, true), - LOCATION(RC_HF_BUSH_BY_ROCKY_PATH_6, true), - LOCATION(RC_HF_SOUTHERN_BUSH_1, true), - LOCATION(RC_HF_SOUTHERN_BUSH_2, true), - LOCATION(RC_HF_SOUTHERN_BUSH_3, true), - LOCATION(RC_HF_SOUTHERN_BUSH_4, true), - LOCATION(RC_HF_SOUTHERN_BUSH_5, true), - LOCATION(RC_HF_SOUTHERN_BUSH_6, true), - LOCATION(RC_HF_SOUTHERN_BUSH_7, true), - LOCATION(RC_HF_SOUTHERN_BUSH_8, true), - LOCATION(RC_HF_SOUTHERN_BUSH_9, true), - LOCATION(RC_HF_SOUTHERN_BUSH_10, true), - LOCATION(RC_HF_SOUTHERN_BUSH_11, true), - LOCATION(RC_HF_SOUTHERN_BUSH_12, true), - LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_1, logic->IsChild), - LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_2, logic->IsChild), - LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_3, logic->IsChild), - LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_4, logic->IsChild), - LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_5, logic->IsChild), - LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_6, logic->IsChild), - LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_7, logic->IsChild), - LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_8, logic->IsChild), - LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_9, logic->IsChild), - LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_10, logic->IsChild), - LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_11, logic->IsChild), - LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_12, logic->IsChild), + LOCATION(RC_HF_OCARINA_OF_TIME_ITEM, logic->IsChild && logic->StoneCount() == 3 && logic->HasItem(RG_BRONZE_SCALE)), + LOCATION(RC_SONG_FROM_OCARINA_OF_TIME, logic->IsChild && logic->StoneCount() == 3 && logic->HasItem(RG_BRONZE_SCALE)), + LOCATION(RC_HF_POND_STORMS_FAIRY, logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_HF_CENTRAL_GRASS_1, logic->CanCutShrubs()), + LOCATION(RC_HF_CENTRAL_GRASS_2, logic->CanCutShrubs()), + LOCATION(RC_HF_CENTRAL_GRASS_3, logic->CanCutShrubs()), + LOCATION(RC_HF_CENTRAL_GRASS_4, logic->CanCutShrubs()), + LOCATION(RC_HF_CENTRAL_GRASS_5, logic->CanCutShrubs()), + LOCATION(RC_HF_CENTRAL_GRASS_6, logic->CanCutShrubs()), + LOCATION(RC_HF_CENTRAL_GRASS_7, logic->CanCutShrubs()), + LOCATION(RC_HF_CENTRAL_GRASS_8, logic->CanCutShrubs()), + LOCATION(RC_HF_CENTRAL_GRASS_9, logic->CanCutShrubs()), + LOCATION(RC_HF_CENTRAL_GRASS_10, logic->CanCutShrubs()), + LOCATION(RC_HF_CENTRAL_GRASS_11, logic->CanCutShrubs()), + LOCATION(RC_HF_CENTRAL_GRASS_12, logic->CanCutShrubs()), + LOCATION(RC_HF_SOUTH_GRASS_1, logic->CanCutShrubs()), + LOCATION(RC_HF_SOUTH_GRASS_2, logic->CanCutShrubs()), + LOCATION(RC_HF_SOUTH_GRASS_3, logic->CanCutShrubs()), + LOCATION(RC_HF_SOUTH_GRASS_4, logic->CanCutShrubs()), + LOCATION(RC_HF_SOUTH_GRASS_5, logic->CanCutShrubs()), + LOCATION(RC_HF_SOUTH_GRASS_6, logic->CanCutShrubs()), + LOCATION(RC_HF_SOUTH_GRASS_7, logic->CanCutShrubs()), + LOCATION(RC_HF_SOUTH_GRASS_8, logic->CanCutShrubs()), + LOCATION(RC_HF_SOUTH_GRASS_9, logic->CanCutShrubs()), + LOCATION(RC_HF_SOUTH_GRASS_10, logic->CanCutShrubs()), + LOCATION(RC_HF_SOUTH_GRASS_11, logic->CanCutShrubs()), + LOCATION(RC_HF_SOUTH_GRASS_12, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_MARKET_GRASS_1, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_MARKET_GRASS_2, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_MARKET_GRASS_3, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_MARKET_GRASS_4, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_MARKET_GRASS_5, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_MARKET_GRASS_6, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_MARKET_GRASS_7, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_MARKET_GRASS_8, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_MARKET_GRASS_9, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_MARKET_GRASS_10, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_MARKET_GRASS_11, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_MARKET_GRASS_12, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_KF_GRASS_1, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_KF_GRASS_2, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_KF_GRASS_3, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_KF_GRASS_4, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_KF_GRASS_5, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_KF_GRASS_6, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_KF_GRASS_7, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_KF_GRASS_8, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_KF_GRASS_9, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_KF_GRASS_10, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_KF_GRASS_11, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_KF_GRASS_12, logic->CanCutShrubs()), + LOCATION(RC_HF_NEAR_LLR_TREE, logic->CanBonkTrees()), + LOCATION(RC_HF_NEAR_LH_TREE, logic->CanBonkTrees()), + LOCATION(RC_HF_CHILD_NEAR_GV_TREE, logic->IsChild && logic->CanBonkTrees()), + LOCATION(RC_HF_ADULT_NEAR_GV_TREE, logic->IsAdult && logic->CanBonkTrees()), + LOCATION(RC_HF_NEAR_ZR_TREE, logic->CanBonkTrees()), + LOCATION(RC_HF_NEAR_KAK_TREE, logic->CanBonkTrees()), + LOCATION(RC_HF_NEAR_KAK_SMALL_TREE, logic->CanBonkTrees()), + LOCATION(RC_HF_NEAR_MARKET_TREE_1, logic->CanBonkTrees()), + LOCATION(RC_HF_NEAR_MARKET_TREE_2, logic->CanBonkTrees()), + LOCATION(RC_HF_NEAR_MARKET_TREE_3, logic->CanBonkTrees()), + LOCATION(RC_HF_NORTHWEST_TREE_1, logic->CanBonkTrees()), + LOCATION(RC_HF_NORTHWEST_TREE_2, logic->CanBonkTrees()), + LOCATION(RC_HF_NORTHWEST_TREE_3, logic->CanBonkTrees()), + LOCATION(RC_HF_NORTHWEST_TREE_4, logic->CanBonkTrees()), + LOCATION(RC_HF_NORTHWEST_TREE_5, logic->CanBonkTrees()), + LOCATION(RC_HF_NORTHWEST_TREE_6, logic->CanBonkTrees()), + LOCATION(RC_HF_EAST_TREE_1, logic->CanBonkTrees()), + LOCATION(RC_HF_EAST_TREE_2, logic->CanBonkTrees()), + LOCATION(RC_HF_EAST_TREE_3, logic->CanBonkTrees()), + LOCATION(RC_HF_EAST_TREE_4, logic->CanBonkTrees()), + LOCATION(RC_HF_EAST_TREE_5, logic->CanBonkTrees()), + LOCATION(RC_HF_EAST_TREE_6, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_1, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_2, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_3, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_4, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_5, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_6, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_7, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_8, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_9, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_10, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_11, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_12, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_13, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_14, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_15, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_16, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_17, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_18, logic->CanBonkTrees()), + LOCATION(RC_HF_SOUTHEAST_TREE_19, logic->CanBonkTrees()), + LOCATION(RC_HF_CHILD_SOUTHEAST_TREE_1, logic->IsChild && logic->CanBonkTrees()), + LOCATION(RC_HF_CHILD_SOUTHEAST_TREE_2, logic->IsChild && logic->CanBonkTrees()), + LOCATION(RC_HF_CHILD_SOUTHEAST_TREE_3, logic->IsChild && logic->CanBonkTrees()), + LOCATION(RC_HF_CHILD_SOUTHEAST_TREE_4, logic->IsChild && logic->CanBonkTrees()), + LOCATION(RC_HF_CHILD_SOUTHEAST_TREE_5, logic->IsChild && logic->CanBonkTrees()), + LOCATION(RC_HF_CHILD_SOUTHEAST_TREE_6, logic->IsChild && logic->CanBonkTrees()), + LOCATION(RC_HF_TEKTITE_GROTTO_TREE, logic->CanBonkTrees()), + LOCATION(RC_HF_BUSH_NEAR_LAKE_1, true), + LOCATION(RC_HF_BUSH_NEAR_LAKE_2, true), + LOCATION(RC_HF_BUSH_NEAR_LAKE_3, true), + LOCATION(RC_HF_BUSH_NEAR_LAKE_4, true), + LOCATION(RC_HF_BUSH_NEAR_LAKE_5, true), + LOCATION(RC_HF_BUSH_NEAR_LAKE_6, true), + LOCATION(RC_HF_BUSH_NEAR_LAKE_7, true), + LOCATION(RC_HF_BUSH_NEAR_LAKE_8, true), + LOCATION(RC_HF_BUSH_NEAR_LAKE_9, true), + LOCATION(RC_HF_BUSH_NEAR_LAKE_10, true), + LOCATION(RC_HF_BUSH_NEAR_LAKE_11, true), + LOCATION(RC_HF_NORTHERN_BUSH_1, true), + LOCATION(RC_HF_NORTHERN_BUSH_2, true), + LOCATION(RC_HF_NORTHERN_BUSH_3, true), + LOCATION(RC_HF_NORTHERN_BUSH_4, true), + LOCATION(RC_HF_NORTHERN_BUSH_5, true), + LOCATION(RC_HF_NORTHERN_BUSH_6, true), + LOCATION(RC_HF_CHILD_NORTHERN_BUSH_1, logic->IsChild), + LOCATION(RC_HF_CHILD_NORTHERN_BUSH_2, logic->IsChild), + LOCATION(RC_HF_CHILD_NORTHERN_BUSH_3, logic->IsChild), + LOCATION(RC_HF_CHILD_NORTHERN_BUSH_4, logic->IsChild), + LOCATION(RC_HF_CHILD_NORTHERN_BUSH_5, logic->IsChild), + LOCATION(RC_HF_CHILD_NORTHERN_BUSH_6, logic->IsChild), + LOCATION(RC_HF_CHILD_NORTHERN_BUSH_7, logic->IsChild), + LOCATION(RC_HF_CHILD_NORTHERN_BUSH_8, logic->IsChild), + LOCATION(RC_HF_CHILD_NORTHERN_BUSH_9, logic->IsChild), + LOCATION(RC_HF_CHILD_NORTHERN_BUSH_10, logic->IsChild), + LOCATION(RC_HF_CHILD_NORTHERN_BUSH_11, logic->IsChild), + LOCATION(RC_HF_BUSH_BY_ROCKY_PATH_1, true), + LOCATION(RC_HF_BUSH_BY_ROCKY_PATH_2, true), + LOCATION(RC_HF_BUSH_BY_ROCKY_PATH_3, true), + LOCATION(RC_HF_BUSH_BY_ROCKY_PATH_4, true), + LOCATION(RC_HF_BUSH_BY_ROCKY_PATH_5, true), + LOCATION(RC_HF_BUSH_BY_ROCKY_PATH_6, true), + LOCATION(RC_HF_SOUTHERN_BUSH_1, true), + LOCATION(RC_HF_SOUTHERN_BUSH_2, true), + LOCATION(RC_HF_SOUTHERN_BUSH_3, true), + LOCATION(RC_HF_SOUTHERN_BUSH_4, true), + LOCATION(RC_HF_SOUTHERN_BUSH_5, true), + LOCATION(RC_HF_SOUTHERN_BUSH_6, true), + LOCATION(RC_HF_SOUTHERN_BUSH_7, true), + LOCATION(RC_HF_SOUTHERN_BUSH_8, true), + LOCATION(RC_HF_SOUTHERN_BUSH_9, true), + LOCATION(RC_HF_SOUTHERN_BUSH_10, true), + LOCATION(RC_HF_SOUTHERN_BUSH_11, true), + LOCATION(RC_HF_SOUTHERN_BUSH_12, true), + LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_1, logic->IsChild), + LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_2, logic->IsChild), + LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_3, logic->IsChild), + LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_4, logic->IsChild), + LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_5, logic->IsChild), + LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_6, logic->IsChild), + LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_7, logic->IsChild), + LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_8, logic->IsChild), + LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_9, logic->IsChild), + LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_10, logic->IsChild), + LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_11, logic->IsChild), + LOCATION(RC_HF_CHILD_SOUTHERN_BUSH_12, logic->IsChild), + LOCATION(RC_HF_CASTLE_EXIT_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_HF_WOODED_EXIT_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_HF_ROCKY_PATH_EXIT_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_HF_FENCED_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_HF_CENTER_EXIT_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_HF_RIVER_EXIT_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_HF_STAIRS_EXIT_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_HF_WONDER_BRIDGE_1, logic->IsChild), + LOCATION(RC_HF_WONDER_BRIDGE_2, logic->IsChild), + LOCATION(RC_HF_WONDER_BRIDGE_3, logic->IsChild), }, { //Exits ENTRANCE(RR_LW_BRIDGE, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp index de64cc2f74f..98105f5ed81 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp @@ -60,6 +60,14 @@ void RegionTable_Init_Kakariko() { LOCATION(RC_KAK_NEAR_BOARDING_HOUSE_CHILD_CRATE, logic->IsChild && logic->CanBreakCrates()), LOCATION(RC_KAK_NEAR_BAZAAR_CHILD_CRATE, logic->IsChild && logic->CanBreakCrates()), LOCATION(RC_KAK_TREE, logic->CanBonkTrees()), + LOCATION(RC_KAK_GUARD_GATE_RECTANGLE_SIGN, logic->IsChild && logic->CanRead()), + LOCATION(RC_KAK_WELL_RECTANGLE_SIGN, logic->IsChild && logic->CanRead()), + LOCATION(RC_KAK_SOUTHEAST_EXIT_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_KAK_FRONT_GATE_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_KAK_WONDER_UNDER_CONSTRUCTION, logic->IsChild), + LOCATION(RC_KAK_BEGGAR_BUGS, logic->IsAdult && logic->CanUse(RG_BOTTLE_WITH_BUGS)), + LOCATION(RC_KAK_BEGGAR_FISH, logic->IsAdult && logic->CanUse(RG_BOTTLE_WITH_FISH)), + LOCATION(RC_KAK_BEGGAR_BLUE_FIRE, logic->IsAdult && logic->CanUse(RG_BOTTLE_WITH_BLUE_FIRE)), }, { //Exits ENTRANCE(RR_HYRULE_FIELD, true), @@ -162,7 +170,8 @@ void RegionTable_Init_Kakariko() { areaTable[RR_KAK_IMPAS_HOUSE] = Region("Kak Impas House", SCENE_IMPAS_HOUSE, {}, { //Locations - LOCATION(RC_KAK_IMPAS_HOUSE_COW, logic->CanUse(RG_EPONAS_SONG)), + LOCATION(RC_KAK_IMPAS_HOUSE_COW, logic->CanUse(RG_EPONAS_SONG)), + LOCATION(RC_KAK_WONDER_ABOVE_COW, true), }, { //Exits ENTRANCE(RR_KAKARIKO_VILLAGE, true), @@ -215,7 +224,8 @@ void RegionTable_Init_Kakariko() { areaTable[RR_KAK_SHOOTING_GALLERY] = Region("Kak Shooting Gallery", SCENE_SHOOTING_GALLERY, {}, { //Locations - LOCATION(RC_KAK_SHOOTING_GALLERY_REWARD, logic->IsAdult && logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_FAIRY_BOW)), + LOCATION(RC_KAK_SHOOTING_GALLERY_REWARD, logic->IsAdult && logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_FAIRY_BOW)), + LOCATION(RC_KAK_SHOOTING_GALLERY_RECTANGLE_SIGN, logic->IsAdult && logic->CanRead()), }, { //Exits ENTRANCE(RR_KAKARIKO_VILLAGE, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp index d0e78f824fc..62b31803f28 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp @@ -11,62 +11,83 @@ void RegionTable_Init_KokiriForest() { EVENT_ACCESS(LOGIC_SHOWED_MIDO_SWORD_AND_SHIELD, logic->IsChild && logic->HasItem(RG_SPEAK_KOKIRI) && logic->CanUse(RG_KOKIRI_SWORD) && logic->CanUse(RG_DEKU_SHIELD)), }, { //Locations - LOCATION(RC_KF_GS_KNOW_IT_ALL_HOUSE, logic->IsChild && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_CLOSE) && logic->CanGetNightTimeGS()), - LOCATION(RC_KF_GS_BEAN_PATCH, logic->CanSpawnSoilSkull(RG_KOKIRI_FOREST_BEAN_SOUL) && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_CLOSE)), - LOCATION(RC_KF_GS_HOUSE_OF_TWINS, logic->IsAdult && logic->CanGetNightTimeGS() && - (logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG) || - (ctx->GetTrickOption(RT_KF_ADULT_GS) && logic->CanUse(RG_HOVER_BOOTS) && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_SHORT_JUMPSLASH)))), - LOCATION(RC_KF_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_KOKIRI_FOREST_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_KF_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_KOKIRI_FOREST_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_KF_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_KOKIRI_FOREST_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_KF_BRIDGE_RUPEE, logic->IsChild), - LOCATION(RC_KF_BEHIND_MIDOS_RUPEE, logic->IsChild), - LOCATION(RC_KF_SOUTH_GRASS_WEST_RUPEE, logic->IsChild), - LOCATION(RC_KF_SOUTH_GRASS_EAST_RUPEE, logic->IsChild), - LOCATION(RC_KF_NORTH_GRASS_WEST_RUPEE, logic->IsChild), - LOCATION(RC_KF_NORTH_GRASS_EAST_RUPEE, logic->IsChild), - LOCATION(RC_KF_BEAN_RUPEE_1, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST, RG_KOKIRI_FOREST_BEAN_SOUL) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_KF_BEAN_RUPEE_2, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST, RG_KOKIRI_FOREST_BEAN_SOUL) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_KF_BEAN_RUPEE_3, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST, RG_KOKIRI_FOREST_BEAN_SOUL) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_KF_BEAN_RUPEE_4, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST, RG_KOKIRI_FOREST_BEAN_SOUL) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_KF_BEAN_RUPEE_5, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST, RG_KOKIRI_FOREST_BEAN_SOUL) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_KF_BEAN_RUPEE_6, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST, RG_KOKIRI_FOREST_BEAN_SOUL) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_KF_BEAN_RED_RUPEE, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST, RG_KOKIRI_FOREST_BEAN_SOUL) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_KF_SARIAS_ROOF_WEST_HEART, logic->IsChild), - LOCATION(RC_KF_SARIAS_ROOF_EAST_HEART, logic->IsChild), - LOCATION(RC_KF_SARIAS_ROOF_NORTH_HEART, logic->IsChild), - LOCATION(RC_KF_CHILD_GRASS_1, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_KF_CHILD_GRASS_2, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_KF_CHILD_GRASS_3, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_KF_CHILD_GRASS_4, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_KF_CHILD_GRASS_5, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_KF_CHILD_GRASS_6, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_KF_CHILD_GRASS_7, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_KF_CHILD_GRASS_8, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_KF_CHILD_GRASS_9, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_KF_CHILD_GRASS_10, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_KF_CHILD_GRASS_11, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_KF_CHILD_GRASS_12, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_1, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_2, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_3, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_4, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_5, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_6, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_7, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_8, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_9, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_10, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_11, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_12, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_13, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_14, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_15, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_16, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_17, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_18, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_19, logic->IsAdult && logic->CanCutShrubs()), - LOCATION(RC_KF_ADULT_GRASS_20, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_GS_KNOW_IT_ALL_HOUSE, logic->IsChild && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_CLOSE) && logic->CanGetNightTimeGS()), + LOCATION(RC_KF_GS_BEAN_PATCH, logic->CanSpawnSoilSkull(RG_KOKIRI_FOREST_BEAN_SOUL) && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_CLOSE)), + LOCATION(RC_KF_GS_HOUSE_OF_TWINS, logic->IsAdult && logic->CanGetNightTimeGS() && + (logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG) || + (ctx->GetTrickOption(RT_KF_ADULT_GS) && logic->CanUse(RG_HOVER_BOOTS) && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_SHORT_JUMPSLASH)))), + LOCATION(RC_KF_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_KOKIRI_FOREST_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_KF_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_KOKIRI_FOREST_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_KF_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_KOKIRI_FOREST_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_KF_BRIDGE_RUPEE, logic->IsChild), + LOCATION(RC_KF_BEHIND_MIDOS_RUPEE, logic->IsChild), + LOCATION(RC_KF_SOUTH_GRASS_WEST_RUPEE, logic->IsChild), + LOCATION(RC_KF_SOUTH_GRASS_EAST_RUPEE, logic->IsChild), + LOCATION(RC_KF_NORTH_GRASS_WEST_RUPEE, logic->IsChild), + LOCATION(RC_KF_NORTH_GRASS_EAST_RUPEE, logic->IsChild), + LOCATION(RC_KF_BEAN_RUPEE_1, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST, RG_KOKIRI_FOREST_BEAN_SOUL) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), + LOCATION(RC_KF_BEAN_RUPEE_2, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST, RG_KOKIRI_FOREST_BEAN_SOUL) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), + LOCATION(RC_KF_BEAN_RUPEE_3, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST, RG_KOKIRI_FOREST_BEAN_SOUL) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), + LOCATION(RC_KF_BEAN_RUPEE_4, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST, RG_KOKIRI_FOREST_BEAN_SOUL) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), + LOCATION(RC_KF_BEAN_RUPEE_5, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST, RG_KOKIRI_FOREST_BEAN_SOUL) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), + LOCATION(RC_KF_BEAN_RUPEE_6, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST, RG_KOKIRI_FOREST_BEAN_SOUL) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), + LOCATION(RC_KF_BEAN_RED_RUPEE, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST, RG_KOKIRI_FOREST_BEAN_SOUL) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), + LOCATION(RC_KF_SARIAS_ROOF_WEST_HEART, logic->IsChild), + LOCATION(RC_KF_SARIAS_ROOF_EAST_HEART, logic->IsChild), + LOCATION(RC_KF_SARIAS_ROOF_NORTH_HEART, logic->IsChild), + LOCATION(RC_KF_CHILD_GRASS_1, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_CHILD_GRASS_2, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_CHILD_GRASS_3, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_CHILD_GRASS_4, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_CHILD_GRASS_5, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_CHILD_GRASS_6, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_CHILD_GRASS_7, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_CHILD_GRASS_8, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_CHILD_GRASS_9, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_CHILD_GRASS_10, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_CHILD_GRASS_11, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_CHILD_GRASS_12, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_1, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_2, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_3, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_4, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_5, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_6, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_7, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_8, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_9, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_10, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_11, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_12, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_13, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_14, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_15, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_16, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_17, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_18, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_19, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_ADULT_GRASS_20, logic->IsAdult && logic->CanCutShrubs()), + LOCATION(RC_KF_DEKU_TREE_RECTANGLE_SIGN, logic->CanRead()), + LOCATION(RC_KF_STEPPING_STONES_RECTANGLE_SIGN, logic->CanRead()), + LOCATION(RC_KF_LINKS_HOUSE_RECTANGLE_SIGN, logic->CanRead()), + LOCATION(RC_KF_FIRST_TRAINING_CENTER_RECTANGLE_SIGN, logic->IsChild && logic->CanRead()), + LOCATION(RC_KF_SECOND_TRAINING_CENTER_RECTANGLE_SIGN, logic->IsChild && logic->CanRead()), + LOCATION(RC_KF_CRAWL_RECTANGLE_RECTANGLE_SIGN, logic->IsChild && logic->CanRead()), + LOCATION(RC_KF_LOST_WOODS_RECTANGLE_SIGN, logic->CanRead()), + LOCATION(RC_KF_HOUSE_OF_TWINS_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_KF_SHOP_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_KF_SARIAS_HOUSE_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_KF_LOST_WOODS_ARROW_SIGN, logic->IsChild && logic->CanRead()), + LOCATION(RC_KF_MIDOS_HOUSE_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_KF_TRAINING_CENTER_ENTRANCE_ARROW_SIGN, logic->IsChild && logic->CanRead()), + LOCATION(RC_KF_INNER_TRAINING_CENTER_ARROW_SIGN, logic->IsChild && logic->CanRead()), + LOCATION(RC_KF_KNOW_IT_ALL_BROTHERS_HOUSE_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_KF_WONDER_TRAINING_1, logic->IsChild), + LOCATION(RC_KF_WONDER_TRAINING_2, logic->IsChild), + LOCATION(RC_KF_WONDER_TRAINING_3, logic->IsChild), + LOCATION(RC_KF_WONDER_SIGN, logic->IsChild && logic->CanJumpslashExceptHammer()), + LOCATION(RC_KF_WONDER_PLATFORMS_1, logic->IsChild), + LOCATION(RC_KF_WONDER_PLATFORMS_2, logic->IsChild), }, { //Exits ENTRANCE(RR_KF_BOULDER_LOOP, logic->CanUse(RG_CRAWL)), @@ -86,12 +107,16 @@ void RegionTable_Init_KokiriForest() { areaTable[RR_KF_BOULDER_LOOP] = Region("KF Boulder Loop", SCENE_KOKIRI_FOREST, {}, { //Locations - LOCATION(RC_KF_KOKIRI_SWORD_CHEST, logic->IsChild && logic->HasItem(RG_OPEN_CHEST)), - LOCATION(RC_KF_BOULDER_RUPEE_1, logic->IsChild), - LOCATION(RC_KF_BOULDER_RUPEE_2, logic->IsChild), - LOCATION(RC_KF_CHILD_GRASS_MAZE_1, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_KF_CHILD_GRASS_MAZE_2, logic->IsChild && logic->CanCutShrubs()), - LOCATION(RC_KF_CHILD_GRASS_MAZE_3, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_KOKIRI_SWORD_CHEST, logic->IsChild && logic->HasItem(RG_OPEN_CHEST)), + LOCATION(RC_KF_BOULDER_RUPEE_1, logic->IsChild), + LOCATION(RC_KF_BOULDER_RUPEE_2, logic->IsChild), + LOCATION(RC_KF_CHILD_GRASS_MAZE_1, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_CHILD_GRASS_MAZE_2, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_CHILD_GRASS_MAZE_3, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_KF_AFTER_CRAWLSPACE_RECTANGLE_SIGN, logic->IsChild && logic->CanRead()), + LOCATION(RC_KF_BOULDER_MAZE_RECTANGLE_SIGN, logic->IsChild && logic->CanRead()), + LOCATION(RC_KF_WONDER_CRAWL_GRASS_1, logic->IsChild), + LOCATION(RC_KF_WONDER_CRAWL_GRASS_2, logic->IsChild), }, { //Exits ENTRANCE(RR_KOKIRI_FOREST, logic->CanUse(RG_CRAWL)), @@ -124,8 +149,9 @@ void RegionTable_Init_KokiriForest() { areaTable[RR_KF_LINKS_HOUSE] = Region("KF Link's House", SCENE_LINKS_HOUSE, {}, { //Locations - LOCATION(RC_KF_LINKS_HOUSE_COW, logic->IsAdult && logic->CanUse(RG_EPONAS_SONG) && logic->Get(LOGIC_LINKS_COW)), - LOCATION(RC_KF_LINKS_HOUSE_POT, logic->HasItem(RG_POWER_BRACELET)), // TODO: CanBreakPots() restricted + LOCATION(RC_KF_LINKS_HOUSE_COW, logic->IsAdult && logic->CanUse(RG_EPONAS_SONG) && logic->Get(LOGIC_LINKS_COW)), + LOCATION(RC_KF_LINKS_HOUSE_POT, logic->HasItem(RG_POWER_BRACELET)), // TODO: CanBreakPots() restricted + LOCATION(RC_KF_LINKS_HOUSE_SIGN, logic->CanRead()), }, { //Exits ENTRANCE(RR_KF_LINKS_PORCH, true), @@ -181,6 +207,7 @@ void RegionTable_Init_KokiriForest() { LOCATION(RC_KF_SHOP_ITEM_6, logic->HasItem(RG_SPEAK_KOKIRI) && GetCheckPrice() <= GetWalletCapacity()), LOCATION(RC_KF_SHOP_ITEM_7, logic->HasItem(RG_SPEAK_KOKIRI) && GetCheckPrice() <= GetWalletCapacity()), LOCATION(RC_KF_SHOP_ITEM_8, logic->HasItem(RG_SPEAK_KOKIRI) && GetCheckPrice() <= GetWalletCapacity()), + LOCATION(RC_KF_WONDER_SHOP, true), }, { //Exits ENTRANCE(RR_KOKIRI_FOREST, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp index cdd10605021..9619fc5697b 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp @@ -79,6 +79,10 @@ void RegionTable_Init_LakeHylia() { LOCATION(RC_LH_CHILD_GRASS_4, logic->IsChild && logic->CanCutShrubs()), LOCATION(RC_LH_WARP_PAD_GRASS_1, logic->CanCutShrubs()), LOCATION(RC_LH_WARP_PAD_GRASS_2, logic->CanCutShrubs()), + LOCATION(RC_LH_SCARECROW_BUTTERFLY_FAIRY, logic->IsChild && logic->CanUse(RG_STICKS)), + LOCATION(RC_LH_LAB_RECTANGLE_SIGN, logic->CanRead()), + LOCATION(RC_LH_NORTH_EXIT_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_LH_ISLAND_PEDESTAL, logic->CanRead()), }, { //Exits ENTRANCE(RR_HF_TO_LAKE_HYLIA, true), @@ -102,7 +106,10 @@ void RegionTable_Init_LakeHylia() { ENTRANCE(RR_WATER_TEMPLE_ENTRYWAY, logic->CanUse(RG_HOOKSHOT) && ((logic->CanUse(RG_IRON_BOOTS) || (ctx->GetTrickOption(RT_LH_WATER_HOOKSHOT) && logic->HasItem(RG_GOLDEN_SCALE))) || (logic->IsAdult && logic->CanUse(RG_LONGSHOT) && logic->HasItem(RG_GOLDEN_SCALE)))), }); - areaTable[RR_LH_FISHING_ISLAND] = Region("LH Fishing Island", SCENE_LAKE_HYLIA, {}, {}, { + areaTable[RR_LH_FISHING_ISLAND] = Region("LH Fishing Island", SCENE_LAKE_HYLIA, {}, { + //Locations + LOCATION(RC_LH_FISHING_SIGN, logic->CanRead()), + }, { //Exits ENTRANCE(RR_LAKE_HYLIA, logic->HasItem(RG_BRONZE_SCALE)), ENTRANCE(RR_LH_FISHING_POND, logic->CanOpenOverworldDoor(RG_FISHING_HOLE_KEY)), @@ -130,43 +137,44 @@ void RegionTable_Init_LakeHylia() { // TODO: should some of these helpers be done via events instead? areaTable[RR_LH_FISHING_POND] = Region("LH Fishing Hole", SCENE_FISHING_POND, {}, { //Locations - LOCATION(RC_LH_CHILD_FISHING, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsChild), - LOCATION(RC_LH_CHILD_FISH_1, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_FISH_2, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_FISH_3, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_FISH_4, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_FISH_5, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_FISH_6, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_FISH_7, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_FISH_8, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_FISH_9, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_FISH_10, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_FISH_11, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_FISH_12, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_FISH_13, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_FISH_14, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_FISH_15, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_LOACH_1, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_CHILD_LOACH_2, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), - LOCATION(RC_LH_ADULT_FISHING, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult), - LOCATION(RC_LH_ADULT_FISH_1, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_ADULT_FISH_2, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_ADULT_FISH_3, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_ADULT_FISH_4, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_ADULT_FISH_5, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_ADULT_FISH_6, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_ADULT_FISH_7, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_ADULT_FISH_8, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_ADULT_FISH_9, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_ADULT_FISH_10, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_ADULT_FISH_11, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_ADULT_FISH_12, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_ADULT_FISH_13, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_ADULT_FISH_14, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_ADULT_FISH_15, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_ADULT_LOACH, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), - LOCATION(RC_LH_HYRULE_LOACH, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN)), - LOCATION(RC_FISHING_POLE_HINT, logic->HasItem(RG_SPEAK_HYLIAN)), + LOCATION(RC_LH_CHILD_FISHING, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsChild), + LOCATION(RC_LH_CHILD_FISH_1, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_FISH_2, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_FISH_3, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_FISH_4, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_FISH_5, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_FISH_6, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_FISH_7, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_FISH_8, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_FISH_9, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_FISH_10, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_FISH_11, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_FISH_12, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_FISH_13, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_FISH_14, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_FISH_15, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_LOACH_1, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_CHILD_LOACH_2, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && (logic->IsChild || !ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT))), + LOCATION(RC_LH_ADULT_FISHING, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult), + LOCATION(RC_LH_ADULT_FISH_1, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_ADULT_FISH_2, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_ADULT_FISH_3, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_ADULT_FISH_4, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_ADULT_FISH_5, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_ADULT_FISH_6, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_ADULT_FISH_7, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_ADULT_FISH_8, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_ADULT_FISH_9, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_ADULT_FISH_10, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_ADULT_FISH_11, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_ADULT_FISH_12, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_ADULT_FISH_13, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_ADULT_FISH_14, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_ADULT_FISH_15, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_ADULT_LOACH, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsAdult && ctx->GetOption(RSK_FISHSANITY_AGE_SPLIT)), + LOCATION(RC_LH_HYRULE_LOACH, logic->CanUse(RG_FISHING_POLE) && logic->HasItem(RG_SPEAK_HYLIAN)), + LOCATION(RC_FISHING_POLE_HINT, logic->HasItem(RG_SPEAK_HYLIAN)), + LOCATION(RC_LH_FISHING_POND_RECTANGLE_SIGN, logic->CanRead()), }, { //Exits ENTRANCE(RR_LH_FISHING_ISLAND, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp index c71745f109c..400b3d98fe1 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp @@ -11,20 +11,22 @@ void RegionTable_Init_LonLonRanch() { EVENT_ACCESS(LOGIC_LINKS_COW, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_EPONAS_SONG) && logic->IsAdult && logic->AtDay), }, { //Locations - LOCATION(RC_SONG_FROM_MALON, logic->IsChild && logic->HasItem(RG_ZELDAS_LETTER) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->HasItem(RG_FAIRY_OCARINA) && logic->AtDay), - LOCATION(RC_LLR_GS_TREE, logic->IsChild && logic->CanBonkTrees() && (logic->HasItem(RG_POWER_BRACELET) || logic->CanKillEnemy(RE_GOLD_SKULLTULA))), - LOCATION(RC_LLR_GS_RAIN_SHED, logic->IsChild && logic->CanGetNightTimeGS() && (logic->HasItem(RG_POWER_BRACELET) || logic->CanKillEnemy(RE_GOLD_SKULLTULA))), - LOCATION(RC_LLR_GS_HOUSE_WINDOW, logic->IsChild && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), - LOCATION(RC_LLR_GS_BACK_WALL, logic->IsChild && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), - LOCATION(RC_LLR_FRONT_POT_1, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_LLR_FRONT_POT_2, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_LLR_FRONT_POT_3, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_LLR_FRONT_POT_4, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_LLR_RAIN_SHED_POT_1, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_LLR_RAIN_SHED_POT_2, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_LLR_RAIN_SHED_POT_3, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_LLR_NEAR_TREE_CRATE, logic->IsChild && logic->CanBreakCrates()), - LOCATION(RC_LLR_TREE, logic->IsChild && logic->CanBonkTrees()), + LOCATION(RC_SONG_FROM_MALON, logic->IsChild && logic->HasItem(RG_ZELDAS_LETTER) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->HasItem(RG_FAIRY_OCARINA) && logic->AtDay), + LOCATION(RC_LLR_GS_TREE, logic->IsChild && logic->CanBonkTrees() && (logic->HasItem(RG_POWER_BRACELET) || logic->CanKillEnemy(RE_GOLD_SKULLTULA))), + LOCATION(RC_LLR_GS_RAIN_SHED, logic->IsChild && logic->CanGetNightTimeGS() && (logic->HasItem(RG_POWER_BRACELET) || logic->CanKillEnemy(RE_GOLD_SKULLTULA))), + LOCATION(RC_LLR_GS_HOUSE_WINDOW, logic->IsChild && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), + LOCATION(RC_LLR_GS_BACK_WALL, logic->IsChild && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), + LOCATION(RC_LLR_FRONT_POT_1, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_LLR_FRONT_POT_2, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_LLR_FRONT_POT_3, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_LLR_FRONT_POT_4, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_LLR_RAIN_SHED_POT_1, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_LLR_RAIN_SHED_POT_2, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_LLR_RAIN_SHED_POT_3, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_LLR_NEAR_TREE_CRATE, logic->IsChild && logic->CanBreakCrates()), + LOCATION(RC_LLR_TREE, logic->IsChild && logic->CanBonkTrees()), + LOCATION(RC_LLR_WONDER_BIG_FENCE, logic->IsAdult && logic->CanUse(RG_EPONAS_SONG) && (logic->HasItem(RG_CHILD_WALLET) || logic->Get(LOGIC_FREED_EPONA))), + LOCATION(RC_LLR_WONDER_SMALL_FENCE, logic->IsAdult && logic->CanUse(RG_EPONAS_SONG) && (logic->HasItem(RG_CHILD_WALLET) || logic->Get(LOGIC_FREED_EPONA))), }, { //Exits ENTRANCE(RR_HYRULE_FIELD, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp index 6df779cf698..4b9e3278fb3 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp @@ -48,6 +48,9 @@ void RegionTable_Init_LostWoods() { LOCATION(RC_LW_GRASS_1, logic->CanCutShrubs()), LOCATION(RC_LW_GRASS_2, logic->CanCutShrubs()), LOCATION(RC_LW_GRASS_3, logic->CanCutShrubs()), + LOCATION(RC_LW_WONDER_BACK_SKULL_KIDS_GRASS_1, logic->IsChild), + LOCATION(RC_LW_WONDER_BACK_SKULL_KIDS_GRASS_2, logic->IsChild), + LOCATION(RC_LW_WONDER_FRONT_SKULL_KIDS_GRASS, logic->IsChild), }, { //Exits ENTRANCE(RR_LW_FOREST_EXIT, true), @@ -93,6 +96,7 @@ void RegionTable_Init_LostWoods() { LOCATION(RC_LW_GRASS_7, logic->CanCutShrubs()), LOCATION(RC_LW_GRASS_8, logic->CanCutShrubs()), LOCATION(RC_LW_GRASS_9, logic->CanCutShrubs()), + LOCATION(RC_LW_MEADOW_BUTTERFLY_FAIRY, logic->IsChild && logic->CanUse(RG_STICKS)), }, { //Exits ENTRANCE(RR_LW_FOREST_EXIT, true), @@ -124,6 +128,7 @@ void RegionTable_Init_LostWoods() { //Locations LOCATION(RC_DEKU_THEATER_SKULL_MASK, logic->CanUse(RG_SKULL_MASK)), LOCATION(RC_DEKU_THEATER_MASK_OF_TRUTH, logic->CanUse(RG_MASK_OF_TRUTH) && logic->HasItem(RG_SPEAK_DEKU)), + LOCATION(RC_LW_THEATER_RECTANGLE_SIGN, logic->CanRead()), }, { //Exits ENTRANCE(RR_LW_BEYOND_MIDO, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp index 9eabf6c290c..558c66539fe 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp @@ -28,6 +28,16 @@ void RegionTable_Init_Market() { LOCATION(RC_MK_SHOOTING_GALLERY_CRATE_1, logic->IsChild /*&& logic->CanRoll()*/), LOCATION(RC_MK_SHOOTING_GALLERY_CRATE_2, logic->IsChild /*&& logic->CanRoll()*/), LOCATION(RC_MARKET_TREE, logic->IsChild && logic->CanBonkTrees()), + LOCATION(RC_MKT_WONDER_DAY_1, logic->IsChild && logic->AtDay), + LOCATION(RC_MKT_WONDER_DAY_2, logic->IsChild && logic->AtDay), + LOCATION(RC_MKT_WONDER_DAY_3, logic->IsChild && logic->AtDay), + LOCATION(RC_MKT_WONDER_DAY_4, logic->IsChild && logic->AtDay), + LOCATION(RC_MKT_WONDER_DAY_5, logic->IsChild && logic->AtDay), + LOCATION(RC_MKT_WONDER_NIGHT_1, logic->IsChild && logic->AtNight), + LOCATION(RC_MKT_WONDER_NIGHT_2, logic->IsChild && logic->AtNight), + LOCATION(RC_MK_BEGGAR_BUGS, logic->IsChild && logic->AtDay && logic->CanUse(RG_BOTTLE_WITH_BUGS)), + LOCATION(RC_MK_BEGGAR_FISH, logic->IsChild && logic->AtDay && logic->CanUse(RG_BOTTLE_WITH_FISH)), + LOCATION(RC_MK_BEGGAR_BLUE_FIRE, logic->IsChild && logic->AtDay && logic->CanUse(RG_BOTTLE_WITH_BLUE_FIRE)), }, { //Exits ENTRANCE(RR_MARKET_ENTRANCE, true), @@ -149,7 +159,8 @@ void RegionTable_Init_Market() { EVENT_ACCESS(LOGIC_BORROW_RIGHT_MASKS, ctx->GetOption(RSK_MASK_QUEST).Is(RO_MASK_QUEST_COMPLETED) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->Get(LOGIC_CAN_BORROW_MASKS)), }, { //Locations - LOCATION(RC_MASK_SHOP_HINT, true), + LOCATION(RC_MASK_SHOP_HINT, true), + LOCATION(RC_MK_MASK_SHOP_SIGN, logic->CanRead()), }, { //Exits ENTRANCE(RR_THE_MARKET, true), @@ -157,7 +168,8 @@ void RegionTable_Init_Market() { areaTable[RR_MARKET_SHOOTING_GALLERY] = Region("Market Shooting Gallery", SCENE_SHOOTING_GALLERY, {}, { //Locations - LOCATION(RC_MARKET_SHOOTING_GALLERY_REWARD, logic->IsChild && logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN)), + LOCATION(RC_MARKET_SHOOTING_GALLERY_REWARD, logic->IsChild && logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN)), + LOCATION(RC_MK_SHOOTING_GALLERY_RECTANGLE_SIGN, logic->IsChild && logic->CanRead()), }, { //Exits ENTRANCE(RR_THE_MARKET, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/sacred_forest_meadow.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/sacred_forest_meadow.cpp index 8d6c7700e39..bececec7e6e 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/sacred_forest_meadow.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/sacred_forest_meadow.cpp @@ -5,7 +5,10 @@ using namespace Rando; void RegionTable_Init_SacredForestMeadow() { // clang-format off - areaTable[RR_SFM_ENTRYWAY] = Region("SFM Entryway", SCENE_SACRED_FOREST_MEADOW, {}, {}, { + areaTable[RR_SFM_ENTRYWAY] = Region("SFM Entryway", SCENE_SACRED_FOREST_MEADOW, {}, { + //Locations + LOCATION(RC_SFM_WONDER_ENTRANCE, true), + }, { //Exits ENTRANCE(RR_LW_BEYOND_MIDO, true), ENTRANCE(RR_SACRED_FOREST_MEADOW, logic->IsAdult || logic->CanKillEnemy(RE_WOLFOS)), @@ -41,6 +44,11 @@ void RegionTable_Init_SacredForestMeadow() { LOCATION(RC_SFM_SARIA_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), LOCATION(RC_SFM_SARIA_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_SFM_SARIA_GOSSIP_STONE, true), + LOCATION(RC_SFM_WONDER_MAZE_1, true), + LOCATION(RC_SFM_WONDER_MAZE_2, true), + LOCATION(RC_SFM_WONDER_MAZE_3, true), + LOCATION(RC_SFM_WONDER_MAZE_4, true), + LOCATION(RC_SFM_WONDER_MAZE_5, true), }, { //Exits ENTRANCE(RR_FOREST_TEMPLE_ENTRYWAY, logic->CanUse(RG_HOOKSHOT)), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/temple_of_time.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/temple_of_time.cpp index 8871ee293a2..3089f75ef48 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/temple_of_time.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/temple_of_time.cpp @@ -34,6 +34,7 @@ void RegionTable_Init_TempleOfTime() { LOCATION(RC_ALTAR_HINT_CHILD, logic->IsChild), LOCATION(RC_ALTAR_HINT_ADULT, logic->IsAdult), LOCATION(RC_TOT_SHEIK_HINT, logic->IsAdult && logic->HasItem(RG_SPEAK_HYLIAN)), + LOCATION(RC_TOT_ALTAR, logic->CanRead()), }, { //Exits ENTRANCE(RR_TOT_ENTRANCE, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/thieves_hideout.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/thieves_hideout.cpp index 74735410357..e9a448c962d 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/thieves_hideout.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/thieves_hideout.cpp @@ -17,6 +17,8 @@ void RegionTable_Init_ThievesHideout() { LOCATION(RC_TH_1_TORCH_CELL_MID_POT, logic->CanBreakPots()), LOCATION(RC_TH_1_TORCH_CELL_LEFT_POT, logic->CanBreakPots()), LOCATION(RC_TH_1_TORCH_CELL_CRATE, logic->CanBreakCrates()), + LOCATION(RC_TH_WONDER_1_TORCH_1, logic->CanUse(RG_FAIRY_BOW)), + LOCATION(RC_TH_WONDER_1_TORCH_2, logic->CanUse(RG_FAIRY_BOW)), LOCATION(RC_TH_FREED_CARPENTERS, logic->Get(LOGIC_TH_RESCUED_ALL_CARPENTERS)), }, { //Exits @@ -30,17 +32,19 @@ void RegionTable_Init_ThievesHideout() { EVENT_ACCESS(LOGIC_TH_RESCUED_ALL_CARPENTERS, logic->SmallKeys(SCENE_THIEVES_HIDEOUT, ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_NORMAL) ? 4 : 1) && logic->Get(LOGIC_TH_COULD_FREE_1_TORCH_CARPENTER) && logic->Get(LOGIC_TH_COULD_FREE_DOUBLE_CELL_CARPENTER) && logic->Get(LOGIC_TH_COULD_FREE_DEAD_END_CARPENTER) && logic->Get(LOGIC_TH_COULD_FREE_SLOPE_CARPENTER)), }, { //Locations - LOCATION(RC_TH_DOUBLE_CELL_CARPENTER, logic->CanKillEnemy(RE_GERUDO_WARRIOR)), - LOCATION(RC_TH_NEAR_DOUBLE_CELL_RIGHT_POT, logic->CanBreakPots()), - LOCATION(RC_TH_NEAR_DOUBLE_CELL_MID_POT, logic->CanBreakPots()), - LOCATION(RC_TH_NEAR_DOUBLE_CELL_LEFT_POT, logic->CanBreakPots()), - LOCATION(RC_TH_RIGHTMOST_JAILED_POT, logic->CanBreakPots()), - LOCATION(RC_TH_RIGHT_MIDDLE_JAILED_POT, logic->CanBreakPots()), - LOCATION(RC_TH_LEFT_MIDDLE_JAILED_POT, logic->CanBreakPots()), - LOCATION(RC_TH_LEFTMOST_JAILED_POT, logic->CanBreakPots()), - LOCATION(RC_TH_DOUBLE_CELL_LEFT_CRATE, logic->CanBreakCrates()), - LOCATION(RC_TH_DOUBLE_CELL_RIGHT_CRATE, logic->CanBreakCrates()), - LOCATION(RC_TH_FREED_CARPENTERS, logic->Get(LOGIC_TH_RESCUED_ALL_CARPENTERS)), + LOCATION(RC_TH_DOUBLE_CELL_CARPENTER, logic->CanKillEnemy(RE_GERUDO_WARRIOR)), + LOCATION(RC_TH_NEAR_DOUBLE_CELL_RIGHT_POT, logic->CanBreakPots()), + LOCATION(RC_TH_NEAR_DOUBLE_CELL_MID_POT, logic->CanBreakPots()), + LOCATION(RC_TH_NEAR_DOUBLE_CELL_LEFT_POT, logic->CanBreakPots()), + LOCATION(RC_TH_RIGHTMOST_JAILED_POT, logic->CanBreakPots()), + LOCATION(RC_TH_RIGHT_MIDDLE_JAILED_POT, logic->CanBreakPots()), + LOCATION(RC_TH_LEFT_MIDDLE_JAILED_POT, logic->CanBreakPots()), + LOCATION(RC_TH_LEFTMOST_JAILED_POT, logic->CanBreakPots()), + LOCATION(RC_TH_DOUBLE_CELL_LEFT_CRATE, logic->CanBreakCrates()), + LOCATION(RC_TH_DOUBLE_CELL_RIGHT_CRATE, logic->CanBreakCrates()), + LOCATION(RC_TH_WONDER_DOUBLE_JAIL_LOWER_EXIT, logic->CanUse(RG_FAIRY_BOW)), + LOCATION(RC_TH_WONDER_DOUBLE_JAIL_UPPER_EXIT, logic->CanUse(RG_FAIRY_BOW)), + LOCATION(RC_TH_FREED_CARPENTERS, logic->Get(LOGIC_TH_RESCUED_ALL_CARPENTERS)), }, { //Exits ENTRANCE(RR_GF_OUTSKIRTS, true), @@ -54,9 +58,11 @@ void RegionTable_Init_ThievesHideout() { EVENT_ACCESS(LOGIC_TH_RESCUED_ALL_CARPENTERS, logic->SmallKeys(SCENE_THIEVES_HIDEOUT, ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_NORMAL) ? 4 : 1) && logic->Get(LOGIC_TH_COULD_FREE_1_TORCH_CARPENTER) && logic->Get(LOGIC_TH_COULD_FREE_DOUBLE_CELL_CARPENTER) && logic->Get(LOGIC_TH_COULD_FREE_DEAD_END_CARPENTER) && logic->Get(LOGIC_TH_COULD_FREE_SLOPE_CARPENTER)), }, { //Locations - LOCATION(RC_TH_DEAD_END_CARPENTER, logic->CanKillEnemy(RE_GERUDO_WARRIOR)), - LOCATION(RC_TH_DEAD_END_CELL_CRATE, logic->CanBreakCrates()), - LOCATION(RC_TH_FREED_CARPENTERS, logic->Get(LOGIC_TH_RESCUED_ALL_CARPENTERS)), + LOCATION(RC_TH_DEAD_END_CARPENTER, logic->CanKillEnemy(RE_GERUDO_WARRIOR)), + LOCATION(RC_TH_DEAD_END_CELL_CRATE, logic->CanBreakCrates()), + LOCATION(RC_TH_WONDER_DEAD_END_SKULL_ENTRANCE, logic->CanUse(RG_FAIRY_BOW)), + LOCATION(RC_TH_WONDER_DEAD_END_SKULL_NEAR_JAIL, logic->CanUse(RG_FAIRY_BOW)), + LOCATION(RC_TH_FREED_CARPENTERS, logic->Get(LOGIC_TH_RESCUED_ALL_CARPENTERS)), }, { //Exits ENTRANCE(RR_GF_BELOW_GS, true), @@ -68,10 +74,12 @@ void RegionTable_Init_ThievesHideout() { EVENT_ACCESS(LOGIC_TH_RESCUED_ALL_CARPENTERS, logic->SmallKeys(SCENE_THIEVES_HIDEOUT, ctx->GetOption(RSK_GERUDO_FORTRESS).Is(RO_GF_CARPENTERS_NORMAL) ? 4 : 1) && logic->Get(LOGIC_TH_COULD_FREE_1_TORCH_CARPENTER) && logic->Get(LOGIC_TH_COULD_FREE_DOUBLE_CELL_CARPENTER) && logic->Get(LOGIC_TH_COULD_FREE_DEAD_END_CARPENTER) && logic->Get(LOGIC_TH_COULD_FREE_SLOPE_CARPENTER)), }, { //Locations - LOCATION(RC_TH_STEEP_SLOPE_CARPENTER, logic->CanKillEnemy(RE_GERUDO_WARRIOR)), - LOCATION(RC_TH_STEEP_SLOPE_RIGHT_POT, logic->CanBreakPots()), - LOCATION(RC_TH_STEEP_SLOPE_LEFT_POT, logic->CanBreakPots()), - LOCATION(RC_TH_FREED_CARPENTERS, logic->Get(LOGIC_TH_RESCUED_ALL_CARPENTERS)), + LOCATION(RC_TH_STEEP_SLOPE_CARPENTER, logic->CanKillEnemy(RE_GERUDO_WARRIOR)), + LOCATION(RC_TH_STEEP_SLOPE_RIGHT_POT, logic->CanBreakPots()), + LOCATION(RC_TH_STEEP_SLOPE_LEFT_POT, logic->CanBreakPots()), + LOCATION(RC_TH_WONDER_STEEP_SLOPE_LOWER_EXIT, logic->CanUse(RG_FAIRY_BOW)), + LOCATION(RC_TH_WONDER_STEEP_SLOPE_UPPER_EXIT, logic->CanUse(RG_FAIRY_BOW)), + LOCATION(RC_TH_FREED_CARPENTERS, logic->Get(LOGIC_TH_RESCUED_ALL_CARPENTERS)), }, { //Exits ENTRANCE(RR_GF_BOTTOM_OF_LOWER_VINES, true), @@ -93,10 +101,12 @@ void RegionTable_Init_ThievesHideout() { areaTable[RR_TH_KITCHEN_MAIN] = Region("Thieves Hideout Kitchen Bottom", SCENE_THIEVES_HIDEOUT, {}, { //Locations - LOCATION(RC_TH_KITCHEN_POT_1, logic->CanBreakPots() && logic->CanPassEnemy(RE_GERUDO_GUARD)), - LOCATION(RC_TH_KITCHEN_POT_2, logic->CanBreakPots() && logic->CanPassEnemy(RE_GERUDO_GUARD)), - LOCATION(RC_TH_KITCHEN_CRATE, logic->CanBreakCrates() && logic->CanPassEnemy(RE_GERUDO_GUARD)), - LOCATION(RC_TH_KITCHEN_SUN_FAIRY, logic->CanPassEnemy(RE_GERUDO_GUARD) && logic->CanUse(RG_SUNS_SONG)), + LOCATION(RC_TH_KITCHEN_POT_1, logic->CanBreakPots() && logic->CanPassEnemy(RE_GERUDO_GUARD)), + LOCATION(RC_TH_KITCHEN_POT_2, logic->CanBreakPots() && logic->CanPassEnemy(RE_GERUDO_GUARD)), + LOCATION(RC_TH_KITCHEN_CRATE, logic->CanBreakCrates() && logic->CanPassEnemy(RE_GERUDO_GUARD)), + LOCATION(RC_TH_KITCHEN_SUN_FAIRY, logic->CanPassEnemy(RE_GERUDO_GUARD) && logic->CanUse(RG_SUNS_SONG)), + LOCATION(RC_TH_WONDER_KITCHEN_SKULL, logic->CanUse(RG_FAIRY_BOW)), + LOCATION(RC_TH_WONDER_KITCHEN_SOUP, (logic->TakeDamage() || logic->IsAdult || logic->CanUse(RG_GORON_TUNIC)) && logic->CanPassEnemy(RE_GERUDO_GUARD)), }, { //Exits ENTRANCE(RR_TH_KITCHEN_CORRIDOR, logic->CanPassEnemy(RE_GERUDO_GUARD)), @@ -142,14 +152,25 @@ void RegionTable_Init_ThievesHideout() { (logic->CanPassEnemy(RE_GERUDO_GUARD) && logic->HasExplosives() && logic->CanUse(RG_BOOMERANG))), }, { //Exits - ENTRANCE(RR_GF_BELOW_CHEST, logic->CanPassEnemy(RE_GERUDO_GUARD)), - //Implies logic->CanPassEnemy(RE_GERUDO_GUARD) - ENTRANCE(RR_TH_BREAK_ROOM_CORRIDOR, logic->CanUse(RG_HOOKSHOT)), + ENTRANCE(RR_GF_BELOW_CHEST, logic->CanPassEnemy(RE_GERUDO_GUARD)), + ENTRANCE(RR_TH_BREAK_ROOM_LOWER_CORRIDOR, logic->CanPassEnemy(RE_GERUDO_GUARD)), }); - areaTable[RR_TH_BREAK_ROOM_CORRIDOR] = Region("Thieves Hideout Break Room", SCENE_THIEVES_HIDEOUT, {}, {}, { + areaTable[RR_TH_BREAK_ROOM_LOWER_CORRIDOR] = Region("Thieves Hideout Break Room", SCENE_THIEVES_HIDEOUT, {}, { + //Locations + LOCATION(RC_TH_WONDER_BREAK_ROOM_BOTTOM_SKULL, logic->CanUse(RG_FAIRY_BOW)), + }, { + //Exits + ENTRANCE(RR_TH_BREAK_ROOM, logic->CanPassEnemy(RE_GERUDO_GUARD)), + ENTRANCE(RR_TH_BREAK_ROOM_UPPER_CORRIDOR, logic->CanUse(RG_HOOKSHOT)), + }); + + areaTable[RR_TH_BREAK_ROOM_UPPER_CORRIDOR] = Region("Thieves Hideout Break Room", SCENE_THIEVES_HIDEOUT, {}, { + //Locations + LOCATION(RC_TH_WONDER_BREAK_ROOM_TOP_SKULL, logic->CanUse(RG_FAIRY_BOW)), + }, { //Exits - ENTRANCE(RR_TH_BREAK_ROOM, logic->CanUse(RG_HOOKSHOT)), + ENTRANCE(RR_TH_BREAK_ROOM_LOWER_CORRIDOR, logic->CanUse(RG_HOOKSHOT)), ENTRANCE(RR_GF_ABOVE_JAIL, true), }); } diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_domain.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_domain.cpp index 87c97f6b31e..c565a6f89e3 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_domain.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_domain.cpp @@ -35,6 +35,13 @@ void RegionTable_Init_ZorasDomain() { LOCATION(RC_ZD_NEAR_SHOP_POT_3, logic->CanBreakPots()), LOCATION(RC_ZD_NEAR_SHOP_POT_4, logic->CanBreakPots()), LOCATION(RC_ZD_NEAR_SHOP_POT_5, logic->CanBreakPots()), + LOCATION(RC_ZD_SHOP_RECTANGLE_SIGN, logic->CanRead()), + LOCATION(RC_ZD_ENTRANCE_RECTANGLE_SIGN, logic->CanRead()), + LOCATION(RC_ZD_KING_ZORA_PATH_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_ZD_NEAR_KING_ZORA_RECTANGLE_SIGN, logic->CanRead()), + LOCATION(RC_ZD_NEAR_KING_ZORA_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_ZD_KING_ZORA_RED_ICE, logic->IsAdult && logic->Get(LOGIC_KING_ZORA_THAWED)), + LOCATION(RC_ZD_ZORA_SHOP_RED_ICE, logic->IsAdult && logic->BlueFire()), }, { //Exits ENTRANCE(RR_ZR_BEHIND_WATERFALL, true), @@ -56,6 +63,7 @@ void RegionTable_Init_ZorasDomain() { }, { //Locations LOCATION(RC_ZD_BEHIND_KING_ZORA_BEEHIVE, logic->IsChild && logic->CanBreakUpperBeehives()), + LOCATION(RC_ZD_KING_ZORA_RED_ICE, logic->IsAdult && logic->Get(LOGIC_KING_ZORA_THAWED)), }, { //Exits ENTRANCE(RR_ZORAS_DOMAIN, logic->Get(LOGIC_DELIVER_RUTOS_LETTER) || ctx->GetOption(RSK_ZORAS_FOUNTAIN).Is(RO_ZF_OPEN) || (ctx->GetOption(RSK_ZORAS_FOUNTAIN).Is(RO_ZF_CLOSED_CHILD) && logic->IsAdult)), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp index 4d861b431f3..7c827315c35 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_fountain.cpp @@ -10,25 +10,29 @@ void RegionTable_Init_ZorasFountain() { EVENT_ACCESS(LOGIC_FAIRY_ACCESS, logic->CallGossipFairyExceptSuns() || (logic->CanUse(RG_STICKS) && logic->AtDay)), }, { //Locations - LOCATION(RC_ZF_GS_TREE, logic->IsChild && logic->CanBonkTrees() && (logic->HasItem(RG_POWER_BRACELET) || logic->CanKillEnemy(RE_GOLD_SKULLTULA))), - LOCATION(RC_ZF_GS_ABOVE_THE_LOG, logic->IsChild && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), - LOCATION(RC_ZF_FAIRY_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), - LOCATION(RC_ZF_FAIRY_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_ZF_JABU_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), - LOCATION(RC_ZF_JABU_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_ZF_FAIRY_GOSSIP_STONE, true), - LOCATION(RC_ZF_JABU_GOSSIP_STONE, true), - LOCATION(RC_ZF_NEAR_JABU_POT_1, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_ZF_NEAR_JABU_POT_2, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_ZF_NEAR_JABU_POT_3, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_ZF_NEAR_JABU_POT_4, logic->IsChild && logic->CanBreakPots()), - LOCATION(RC_ZF_TREE, logic->IsChild && logic->CanBonkTrees()), - LOCATION(RC_ZF_BUSH_1, logic->IsChild), - LOCATION(RC_ZF_BUSH_2, logic->IsChild), - LOCATION(RC_ZF_BUSH_3, logic->IsChild), - LOCATION(RC_ZF_BUSH_4, logic->IsChild), - LOCATION(RC_ZF_BUSH_5, logic->IsChild), - LOCATION(RC_ZF_BUSH_6, logic->IsChild), + LOCATION(RC_ZF_GS_TREE, logic->IsChild && logic->CanBonkTrees() && (logic->HasItem(RG_POWER_BRACELET) || logic->CanKillEnemy(RE_GOLD_SKULLTULA))), + LOCATION(RC_ZF_GS_ABOVE_THE_LOG, logic->IsChild && logic->HookshotOrBoomerang() && logic->CanGetNightTimeGS()), + LOCATION(RC_ZF_FAIRY_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), + LOCATION(RC_ZF_FAIRY_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_ZF_JABU_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), + LOCATION(RC_ZF_JABU_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_ZF_FAIRY_GOSSIP_STONE, true), + LOCATION(RC_ZF_JABU_GOSSIP_STONE, true), + LOCATION(RC_ZF_NEAR_JABU_POT_1, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_ZF_NEAR_JABU_POT_2, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_ZF_NEAR_JABU_POT_3, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_ZF_NEAR_JABU_POT_4, logic->IsChild && logic->CanBreakPots()), + LOCATION(RC_ZF_TREE, logic->IsChild && logic->CanBonkTrees()), + LOCATION(RC_ZF_BUSH_1, logic->IsChild), + LOCATION(RC_ZF_BUSH_2, logic->IsChild), + LOCATION(RC_ZF_BUSH_3, logic->IsChild), + LOCATION(RC_ZF_BUSH_4, logic->IsChild), + LOCATION(RC_ZF_BUSH_5, logic->IsChild), + LOCATION(RC_ZF_BUSH_6, logic->IsChild), + LOCATION(RC_ZF_LOG_BUTTERFLY_FAIRY, logic->IsChild && logic->AtDay && logic->CanUse(RG_STICKS)), + LOCATION(RC_ZF_JABU_JABU_PLATFORM_RECTANGLE_SIGN, logic->IsChild && logic->CanRead()), + LOCATION(RC_ZF_ENTRANCE_ARROW_SIGN, logic->CanRead()), + LOCATION(RC_ZF_WONDER_ROCK, logic->IsAdult && logic->ReachScarecrow()), }, { //Exits ENTRANCE(RR_ZD_BEHIND_KING_ZORA, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_river.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_river.cpp index 6eea347272e..5fbd38a35de 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_river.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_river.cpp @@ -7,20 +7,25 @@ void RegionTable_Init_ZoraRiver() { // clang-format off areaTable[RR_ZR_FRONT] = Region("ZR Front", SCENE_ZORAS_RIVER, {}, { //Locations - LOCATION(RC_ZR_GS_TREE, logic->IsChild && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_CLOSE) && logic->CanBonkTrees()), - LOCATION(RC_ZR_GRASS_1, logic->CanCutShrubs()), - LOCATION(RC_ZR_GRASS_2, logic->CanCutShrubs()), - LOCATION(RC_ZR_GRASS_3, logic->CanCutShrubs()), - LOCATION(RC_ZR_GRASS_4, logic->CanCutShrubs()), - LOCATION(RC_ZR_GRASS_5, logic->CanCutShrubs()), - LOCATION(RC_ZR_GRASS_6, logic->CanCutShrubs()), - LOCATION(RC_ZR_GRASS_7, logic->CanCutShrubs()), - LOCATION(RC_ZR_GRASS_8, logic->CanCutShrubs()), - LOCATION(RC_ZR_GRASS_9, logic->CanCutShrubs()), - LOCATION(RC_ZR_GRASS_10, logic->CanCutShrubs()), - LOCATION(RC_ZR_GRASS_11, logic->CanCutShrubs()), - LOCATION(RC_ZR_GRASS_12, logic->CanCutShrubs()), - LOCATION(RC_ZR_TREE, logic->IsChild && logic->CanBonkTrees()), + LOCATION(RC_ZR_GS_TREE, logic->IsChild && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_CLOSE) && logic->CanBonkTrees()), + LOCATION(RC_ZR_GRASS_1, logic->CanCutShrubs()), + LOCATION(RC_ZR_GRASS_2, logic->CanCutShrubs()), + LOCATION(RC_ZR_GRASS_3, logic->CanCutShrubs()), + LOCATION(RC_ZR_GRASS_4, logic->CanCutShrubs()), + LOCATION(RC_ZR_GRASS_5, logic->CanCutShrubs()), + LOCATION(RC_ZR_GRASS_6, logic->CanCutShrubs()), + LOCATION(RC_ZR_GRASS_7, logic->CanCutShrubs()), + LOCATION(RC_ZR_GRASS_8, logic->CanCutShrubs()), + LOCATION(RC_ZR_GRASS_9, logic->CanCutShrubs()), + LOCATION(RC_ZR_GRASS_10, logic->CanCutShrubs()), + LOCATION(RC_ZR_GRASS_11, logic->CanCutShrubs()), + LOCATION(RC_ZR_GRASS_12, logic->CanCutShrubs()), + LOCATION(RC_ZR_TREE, logic->IsChild && logic->CanBonkTrees()), + // Require backflip with Iron Boots + LOCATION(RC_ZR_WONDER_LOWER_RIVER_1, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_LOWER_RIVER_2, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_LOWER_RIVER_3, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_LOWER_RIVER_4, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), }, { //Exits ENTRANCE(RR_ZORAS_RIVER, logic->IsAdult || logic->BlastOrSmash() || logic->CanUse(RG_HOVER_BOOTS)), @@ -50,12 +55,42 @@ void RegionTable_Init_ZoraRiver() { LOCATION(RC_ZR_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_ZORAS_RIVER_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_ZR_NEAR_DOMAIN_GOSSIP_STONE_FAIRY, logic->CallGossipFairy()), LOCATION(RC_ZR_NEAR_DOMAIN_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_ZR_BENEATH_WATERFALL_LEFT_RUPEE, logic->IsAdult && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_ZR_BENEATH_WATERFALL_MIDDLE_LEFT_RUPEE, logic->IsAdult && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_ZR_BENEATH_WATERFALL_MIDDLE_RIGHT_RUPEE, logic->IsAdult && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_ZR_BENEATH_WATERFALL_RIGHT_RUPEE, logic->IsAdult && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG))), + LOCATION(RC_ZR_BENEATH_WATERFALL_LEFT_RUPEE, logic->IsAdult && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_BENEATH_WATERFALL_MIDDLE_LEFT_RUPEE, logic->IsAdult && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_BENEATH_WATERFALL_MIDDLE_RIGHT_RUPEE, logic->IsAdult && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_BENEATH_WATERFALL_RIGHT_RUPEE, logic->IsAdult && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), LOCATION(RC_ZR_NEAR_DOMAIN_GOSSIP_STONE, true), LOCATION(RC_ZR_NEAR_FREESTANDING_POH_GRASS, logic->CanUse(RG_BOOMERANG)), + LOCATION(RC_ZR_NEAR_ROCK_CIRCLE_BUTTERFLY_FAIRY, logic->IsChild && logic->CanUse(RG_STICKS)), + LOCATION(RC_ZR_WATERFALL_BUTTERFLY_FAIRY, logic->IsChild && logic->CanUse(RG_STICKS)), + LOCATION(RC_ZR_SLEEPLESS_WATERFALL_PLAQUE, logic->CanRead()), + LOCATION(RC_ZR_WONDER_NEAR_DOMAIN_1, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_NEAR_DOMAIN_2, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_NEAR_DOMAIN_3, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_NEAR_DOMAIN_4, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_BEFORE_LADDER_1, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_BEFORE_LADDER_2, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_BEFORE_LADDER_3, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_BEFORE_LADDER_4, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_BEFORE_LADDER_5, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_BEFORE_LADDER_6, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_AFTER_LADDER_1, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_AFTER_LADDER_2, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_AFTER_LADDER_3, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_FROG_BRIDGE_1, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_FROG_BRIDGE_2, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_FROG_BRIDGE_3, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_PILLARS_1, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_PILLARS_2, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_PILLARS_3, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_PILLARS_4, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_LOWER_LAND_BRIDGE_1, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), // Requires backflip with Iron Boots + LOCATION(RC_ZR_WONDER_LOWER_LAND_BRIDGE_2, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_LOWER_LAND_BRIDGE_3, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_LOWER_LAND_BRIDGE_4, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), + LOCATION(RC_ZR_WONDER_NEAR_CUCCO_1, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), // Requires backflip with Iron Boots + LOCATION(RC_ZR_WONDER_NEAR_CUCCO_2, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), // Requires backflip with Iron Boots + LOCATION(RC_ZR_WONDER_NEAR_CUCCO_3, logic->IsChild && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || ctx->GetTrickOption(RT_VOIDOUT_COLLECTION))), }, { //Exits ENTRANCE(RR_ZR_FRONT, logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE) || logic->HasItem(RG_POWER_BRACELET) || logic->BlastOrSmash() || logic->HasItem(RG_HOVER_BOOTS)), diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 18366f47982..e77b2ebdf8c 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -1334,6 +1334,10 @@ bool Logic::CanBonkTrees() { return true; } +bool Logic::CanRead() { + return true; +} + bool Logic::HasExplosives() { return CanUse(RG_BOMB_BAG) || CanUse(RG_BOMBCHU_5); } @@ -2211,7 +2215,16 @@ void Logic::InitSaveContext() { mSaveContext->adultEquips.cButtonSlots[button] = SLOT_NONE; } mSaveContext->adultEquips.equipment = 0; - mSaveContext->unk_54 = 0; + mSaveContext->healthCapacity4 = 0; + mSaveContext->health4 = 0; + mSaveContext->healthCapacity2 = 0; + mSaveContext->health2 = 0; + mSaveContext->magicLevel2 = 0; + mSaveContext->magic2 = MAGIC_NORMAL_METER; + mSaveContext->isMagicAcquired2 = 0; + mSaveContext->isDoubleMagicAcquired2 = 0; + mSaveContext->healthCapacity3 = 0; + mSaveContext->health3 = 0; mSaveContext->savedSceneNum = SCENE_LINKS_HOUSE; // Equipment diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index fd86befc428..496a6190c3e 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -105,6 +105,7 @@ class Logic { bool CanBreakCrates(); bool CanBreakSmallCrates(); bool CanBonkTrees(); + bool CanRead(); bool HasFireSource(); bool HasFireSourceWithTorch(); bool SunlightArrows(); diff --git a/soh/soh/Enhancements/randomizer/option.cpp b/soh/soh/Enhancements/randomizer/option.cpp index 4f6221c656a..4ebdf0178f2 100644 --- a/soh/soh/Enhancements/randomizer/option.cpp +++ b/soh/soh/Enhancements/randomizer/option.cpp @@ -6,6 +6,7 @@ #include "soh/SohGui/SohGui.hpp" #include "soh/SohGui/SohMenu.h" #include "soh/SohGui/UIWidgets.hpp" +#include "soh/Enhancements/Lang/Lang.h" #include namespace SohGui { @@ -357,18 +358,42 @@ RandomizerCheck LocationOption::GetKey() const { return static_cast(key); } +#define RANDO_ENUM_ITEM(enum) { enum, #enum }, + +std::unordered_map trickNames = { +#include "randomizerEnums/RandomizerTrick.h" +}; + +#undef RANDO_ENUM_ITEM + +const static std::string trickPrefix = "randomizer.tricks."; + +static std::string MakeTrickName(RandomizerTrick key) { + const static std::string namePostfix = ".name"; + + std::string trickNamePart = trickNames[key].substr(3); + std::transform(trickNamePart.begin(), trickNamePart.end(), trickNamePart.begin(), ::tolower); + return Lang::Translate((trickPrefix + trickNamePart + namePostfix).c_str()); +} + +static std::string MakeTrickDescription(RandomizerTrick key) { + const static std::string descriptionPostfix = ".description"; + + std::string trickNamePart = trickNames[key].substr(3); + std::transform(trickNamePart.begin(), trickNamePart.end(), trickNamePart.begin(), ::tolower); + return Lang::Translate((trickPrefix + trickNamePart + descriptionPostfix).c_str()); +} + TrickSetting::TrickSetting(RandomizerTrick key_, const RandomizerCheckQuest quest_, const RandomizerArea area_, - std::set tags_, const std::string& name_, const std::string nameTag_, - std::string description_) - : Option(key_, name_, { "Disabled", "Enabled" }, OptionCategory::Setting, "", std::move(description_), - WIDGET_CVAR_CHECKBOX, 0, false, nullptr, IMFLAG_NONE), + std::set tags_, const std::string nameTag_) + : Option(key_, std::move(MakeTrickName(key_)), { "Disabled", "Enabled" }, OptionCategory::Setting, "", + std::move(MakeTrickDescription(key_)), WIDGET_CVAR_CHECKBOX, 0, false, nullptr, IMFLAG_NONE), mQuest(quest_), mArea(area_), mNameTag(nameTag_), mTags(std::move(tags_)) { } TrickSetting TrickSetting::LogicTrick(RandomizerTrick key_, RandomizerCheckQuest quest_, RandomizerArea area_, - std::set tags_, const std::string& name_, const std::string nameTag_, - std::string description_) { - return { key_, quest_, area_, std::move(tags_), name_, nameTag_, std::move(description_) }; + std::set tags_, const std::string nameTag_) { + return { key_, quest_, area_, std::move(tags_), nameTag_ }; } RandomizerTrick TrickSetting::GetKey() const { diff --git a/soh/soh/Enhancements/randomizer/option.h b/soh/soh/Enhancements/randomizer/option.h index 7aad7217257..fff4b265557 100644 --- a/soh/soh/Enhancements/randomizer/option.h +++ b/soh/soh/Enhancements/randomizer/option.h @@ -356,14 +356,11 @@ class TrickSetting : public Option { * @param quest_ MQ, Vanilla, or Both. * @param area_ The area the trick is relevant for. * @param tags_ The set of RandomizerTrickTags for this trick. - * @param name_ The name of the trick. Appears in the menus and spoiler - * @param nameTag_ The 3-8 long name tag of the trick. Appears in the settings and presets file. - * @param description_ A brief description of the trick. + * @param nameTag_ The 3-8 character long name tag of the trick. Appears in the settings and presets file. * @return Option */ static TrickSetting LogicTrick(RandomizerTrick key_, RandomizerCheckQuest quest_, RandomizerArea area_, - std::set tags_, const std::string& name_, const std::string nameTag_, - std::string description_); + std::set tags_, const std::string nameTag_); RandomizerTrick GetKey() const; @@ -400,7 +397,7 @@ class TrickSetting : public Option { private: TrickSetting(RandomizerTrick key_, RandomizerCheckQuest quest_, RandomizerArea area_, std::set tags_, - const std::string& name_, const std::string nameTag_, std::string description_); + const std::string nameTag_); RandomizerCheckQuest mQuest; RandomizerArea mArea; std::string mNameTag; diff --git a/soh/soh/Enhancements/randomizer/option_descriptions.cpp b/soh/soh/Enhancements/randomizer/option_descriptions.cpp index 049ea8d3ee4..d2c18d660bc 100644 --- a/soh/soh/Enhancements/randomizer/option_descriptions.cpp +++ b/soh/soh/Enhancements/randomizer/option_descriptions.cpp @@ -312,6 +312,34 @@ void Settings::CreateOptionDescriptions() { "only shuffle with No Logic."; mOptionDescriptions[RSK_SHUFFLE_BUSHES] = "Bushes in Hyrule Field & Zora's Fountain will contain randomized items when first walked through."; + mOptionDescriptions[RSK_SHUFFLE_ICICLES] = + "Stalagmites and stalactites in Ice Cavern and Ganon's Castle will contain randomized items when broken.\n" + "Icicles will have a halo around them when carrying randomized items."; + mOptionDescriptions[RSK_SHUFFLE_RED_ICE] = + "Red Ice will give randomized items when melted.\n" + "Red Ice will have a particle effect inside it when it holds a randomized item"; + mOptionDescriptions[RSK_SHUFFLE_SIGNS] = "Signs and readable pedestals, plinths, altars, and graves will grant a " + "randomized item the first time they are read. " + "Signs will have a particle effect when they hold a randomized item.\n" + "\n" + "Off - Signs will not be shuffled.\n" + "\n" + "Dungeons - Only shuffle signs that are within dungeons.\n" + "\n" + "Overworld - Only shuffle signs that are outside of dungeons.\n" + "\n" + "All Signs - Shuffle all signs."; + mOptionDescriptions[RSK_SHUFFLE_WONDER_ITEMS] = + "Wonder items will drop a randomized item the first time they're collected. " + "Wonder items will be marked with swirling particles.\n" + "\n" + "Off - Wonder items will not be shuffled.\n" + "\n" + "Dungeons - Only shuffle wonder items that are within dungeons.\n" + "\n" + "Overworld - Only shuffle wonder items that are outside of dungeons.\n" + "\n" + "All Wonder Items - Shuffle all wonder items."; mOptionDescriptions[RSK_SHUFFLE_FISHING_POLE] = "Shuffles the fishing pole into the item pool.\n" "\n" "The fishing pole is required to play the fishing pond minigame."; @@ -473,6 +501,9 @@ void Settings::CreateOptionDescriptions() { "\n" "This setting does not effect the item earned from playing\n" "the Song of Storms and the frog song minigame."; + mOptionDescriptions[RSK_SHUFFLE_BEGGAR] = + "Shuffle the rewards the Beggar gives for selling bugs, fish, and Blue Fire.\n" + "The Beggar will give separate rewards to child and adult."; mOptionDescriptions[RSK_SHUFFLE_ADULT_TRADE] = "Adds all of the adult trade quest items into the pool, each of which " "can be traded for a unique reward.\n" @@ -507,6 +538,7 @@ void Settings::CreateOptionDescriptions() { "Shuffle fairy spots. These are spots where a big fairy is revealed by a song." "\n" "This excludes gossip stones and magic bean locations."; + mOptionDescriptions[RSK_SHUFFLE_BUTTERFLY_FAIRIES] = "Shuffle fairies from butterfly locations."; mOptionDescriptions[RSK_SHUFFLE_GRASS] = "Grass will drop a randomized item the first time they're cut and collected. " "Grass will have a different appearance when they hold a randomized item.\n" diff --git a/soh/soh/Enhancements/randomizer/particle_cmc.cpp b/soh/soh/Enhancements/randomizer/particle_cmc.cpp new file mode 100644 index 00000000000..3aec408bc7d --- /dev/null +++ b/soh/soh/Enhancements/randomizer/particle_cmc.cpp @@ -0,0 +1,47 @@ +#include "particle_cmc.h" + +// Color of the circle for the particles +static Color_RGBA8 mainColors[7] = { + { 255, 255, 255, 255 }, // Junk + { 170, 50, 0, 255 }, // Lesser + { 250, 0, 0, 255 }, // Health + { 255, 255, 0, 255 }, // Boss Key + { 180, 180, 180, 255 }, // Small Key + { 0, 0, 0, 255 }, // Skulltula Token + { 250, 185, 40, 255 }, // Major +}; + +// Secondary color of the circle for the particles +static Color_RGBA8 secColors[7] = { + { 255, 255, 255, 255 }, // Junk + { 250, 75, 0, 255 }, // Lesser + { 0, 0, 255, 255 }, // Health + { 0, 200, 255, 255 }, // Boss Key + { 130, 130, 130, 255 }, // Small Key + { 255, 250, 190, 255 }, // Skulltula Token + { 255, 220, 135, 255 }, // Major +}; + +// Color of the faded flares stretching off the particles +static Color_RGBA8 flareColors[7] = { + { 135, 135, 135, 255 }, // Junk + { 255, 160, 100, 255 }, // Lesser + { 255, 125, 125, 255 }, // Health + { 0, 200, 255, 255 }, // Boss Key + { 100, 100, 100, 255 }, // Small Key + { 255, 255, 255, 255 }, // Skulltula Token + { 250, 220, 180, 255 }, // Major +}; + +Color_RGBA8 Randomizer_GetParticleCMCColor(GetItemCategory colorIndex, ParticleCMCColorType colorType) { + switch (colorType) { + case COLOR_PRIMARY: + return mainColors[colorIndex]; + case COLOR_SECONDARY: + return secColors[colorIndex]; + case COLOR_FLARE: + return flareColors[colorIndex]; + default: + return mainColors[colorIndex]; + } +} \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/particle_cmc.h b/soh/soh/Enhancements/randomizer/particle_cmc.h new file mode 100644 index 00000000000..554adcf2e9f --- /dev/null +++ b/soh/soh/Enhancements/randomizer/particle_cmc.h @@ -0,0 +1,24 @@ +#pragma once + +#include "soh/OTRGlobals.h" + +#ifndef PARTICLE_CMC_H +#define PARTICLE_CMC_H + +typedef enum { + COLOR_PRIMARY, + COLOR_SECONDARY, + COLOR_FLARE, +} ParticleCMCColorType; + +#ifdef __cplusplus +extern "C" { +#endif + +Color_RGBA8 Randomizer_GetParticleCMCColor(GetItemCategory colorIndex, ParticleCMCColorType colorType); + +#ifdef __cplusplus +} +#endif + +#endif \ No newline at end of file diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index aa3a05dff3d..5b2ca35498f 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -2980,6 +2980,528 @@ std::map rcToRandomizerInf = { { RC_ZF_BUSH_4, RAND_INF_ZF_BUSH_4 }, { RC_ZF_BUSH_5, RAND_INF_ZF_BUSH_5 }, { RC_ZF_BUSH_6, RAND_INF_ZF_BUSH_6 }, + { RC_KF_DEKU_TREE_RECTANGLE_SIGN, RAND_INF_KF_DEKU_TREE_RECTANGLE_SIGN }, + { RC_KF_STEPPING_STONES_RECTANGLE_SIGN, RAND_INF_KF_STEPPING_STONES_RECTANGLE_SIGN }, + { RC_KF_LINKS_HOUSE_RECTANGLE_SIGN, RAND_INF_KF_LINKS_HOUSE_RECTANGLE_SIGN }, + { RC_KF_FIRST_TRAINING_CENTER_RECTANGLE_SIGN, RAND_INF_KF_FIRST_TRAINING_CENTER_RECTANGLE_SIGN }, + { RC_KF_SECOND_TRAINING_CENTER_RECTANGLE_SIGN, RAND_INF_KF_SECOND_TRAINING_CENTER_RECTANGLE_SIGN }, + { RC_KF_AFTER_CRAWLSPACE_RECTANGLE_SIGN, RAND_INF_KF_AFTER_CRAWLSPACE_RECTANGLE_SIGN }, + { RC_KF_CRAWL_RECTANGLE_RECTANGLE_SIGN, RAND_INF_KF_CRAWL_RECTANGLE_RECTANGLE_SIGN }, + { RC_KF_LOST_WOODS_RECTANGLE_SIGN, RAND_INF_KF_LOST_WOODS_RECTANGLE_SIGN }, + { RC_KF_HOUSE_OF_TWINS_ARROW_SIGN, RAND_INF_KF_HOUSE_OF_TWINS_ARROW_SIGN }, + { RC_KF_SHOP_ARROW_SIGN, RAND_INF_KF_SHOP_ARROW_SIGN }, + { RC_KF_SARIAS_HOUSE_ARROW_SIGN, RAND_INF_KF_SARIAS_HOUSE_ARROW_SIGN }, + { RC_KF_LOST_WOODS_ARROW_SIGN, RAND_INF_KF_LOST_WOODS_ARROW_SIGN }, + { RC_KF_MIDOS_HOUSE_ARROW_SIGN, RAND_INF_KF_MIDOS_HOUSE_ARROW_SIGN }, + { RC_KF_TRAINING_CENTER_ENTRANCE_ARROW_SIGN, RAND_INF_KF_TRAINING_CENTER_ENTRANCE_ARROW_SIGN }, + { RC_KF_INNER_TRAINING_CENTER_ARROW_SIGN, RAND_INF_KF_INNER_TRAINING_CENTER_ARROW_SIGN }, + { RC_KF_KNOW_IT_ALL_BROTHERS_HOUSE_ARROW_SIGN, RAND_INF_KF_KNOW_IT_ALL_BROTHERS_HOUSE_ARROW_SIGN }, + { RC_KF_BOULDER_MAZE_RECTANGLE_SIGN, RAND_INF_KF_BOULDER_MAZE_RECTANGLE_SIGN }, + { RC_KF_LINKS_HOUSE_SIGN, RAND_INF_KF_LINKS_HOUSE_SIGN }, + { RC_LW_THEATER_RECTANGLE_SIGN, RAND_INF_LW_THEATER_RECTANGLE_SIGN }, + { RC_HF_CASTLE_EXIT_ARROW_SIGN, RAND_INF_HF_CASTLE_EXIT_ARROW_SIGN }, + { RC_HF_WOODED_EXIT_ARROW_SIGN, RAND_INF_HF_WOODED_EXIT_ARROW_SIGN }, + { RC_HF_ROCKY_PATH_EXIT_ARROW_SIGN, RAND_INF_HF_ROCKY_PATH_EXIT_ARROW_SIGN }, + { RC_HF_FENCED_ARROW_SIGN, RAND_INF_HF_FENCED_ARROW_SIGN }, + { RC_HF_CENTER_EXIT_ARROW_SIGN, RAND_INF_HF_CENTER_EXIT_ARROW_SIGN }, + { RC_HF_RIVER_EXIT_ARROW_SIGN, RAND_INF_HF_RIVER_EXIT_ARROW_SIGN }, + { RC_HF_STAIRS_EXIT_ARROW_SIGN, RAND_INF_HF_STAIRS_EXIT_ARROW_SIGN }, + { RC_MK_SHOOTING_GALLERY_RECTANGLE_SIGN, RAND_INF_MK_SHOOTING_GALLERY_RECTANGLE_SIGN }, + { RC_MK_MASK_SHOP_SIGN, RAND_INF_MK_MASK_SHOP_SIGN }, + { RC_TOT_ALTAR, RAND_INF_TOT_ALTAR }, + { RC_HC_DEAD_END_RECTANGLE_SIGN, RAND_INF_HC_DEAD_END_RECTANGLE_SIGN }, + { RC_KAK_GUARD_GATE_RECTANGLE_SIGN, RAND_INF_KAK_GUARD_GATE_RECTANGLE_SIGN }, + { RC_KAK_WELL_RECTANGLE_SIGN, RAND_INF_KAK_WELL_RECTANGLE_SIGN }, + { RC_KAK_SOUTHEAST_EXIT_ARROW_SIGN, RAND_INF_KAK_SOUTHEAST_EXIT_ARROW_SIGN }, + { RC_KAK_FRONT_GATE_ARROW_SIGN, RAND_INF_KAK_FRONT_GATE_ARROW_SIGN }, + { RC_KAK_SHOOTING_GALLERY_RECTANGLE_SIGN, RAND_INF_KAK_SHOOTING_GALLERY_RECTANGLE_SIGN }, + { RC_GY_ENTRANCE_RECTANGLE_SIGN, RAND_INF_GY_ENTRANCE_RECTANGLE_SIGN }, + { RC_GY_ENTRANCE_PLINTH, RAND_INF_GY_ENTRANCE_PLINTH }, + { RC_GY_RIGHT_OF_ROYAL_TOMB_GRAVE, RAND_INF_GY_RIGHT_OF_ROYAL_TOMB_GRAVE }, + { RC_GY_LEFT_OF_ROYAL_TOMB_GRAVE, RAND_INF_GY_LEFT_OF_ROYAL_TOMB_GRAVE }, + { RC_GY_ROYAL_TOMB_GRAVE, RAND_INF_GY_ROYAL_TOMB_GRAVE }, + { RC_DMT_ABOVE_DODONGO_RECTANGLE_SIGN, RAND_INF_DMT_ABOVE_DODONGO_RECTANGLE_SIGN }, + { RC_DMT_ADULT_CENTER_EXIT_ARROW_SIGN, RAND_INF_DMT_ADULT_CENTER_EXIT_ARROW_SIGN }, + { RC_DMT_CHILD_CENTER_EXIT_RECTANGLE_SIGN, RAND_INF_DMT_CHILD_CENTER_EXIT_RECTANGLE_SIGN }, + { RC_DMT_DODONGOS_CAVERN_RECTANGLE_SIGN, RAND_INF_DMT_DODONGOS_CAVERN_RECTANGLE_SIGN }, + { RC_DMT_CENTER_TRAIL_RECTANGLE_SIGN, RAND_INF_DMT_CENTER_TRAIL_RECTANGLE_SIGN }, + { RC_DMT_TO_UPPER_TRAIL_ARROW_SIGN, RAND_INF_DMT_TO_UPPER_TRAIL_ARROW_SIGN }, + { RC_DMT_UPPER_EXIT_ARROW_SIGN, RAND_INF_DMT_UPPER_EXIT_ARROW_SIGN }, + { RC_DMT_TO_CENTER_EXIT_ARROW_SIGN, RAND_INF_DMT_TO_CENTER_EXIT_ARROW_SIGN }, + { RC_DMT_LOWER_EXIT_ARROW_SIGN, RAND_INF_DMT_LOWER_EXIT_ARROW_SIGN }, + { RC_GC_CHILD_ROLLING_GORON_RECTANGLE_SIGN, RAND_INF_GC_CHILD_ROLLING_GORON_RECTANGLE_SIGN }, + { RC_DMC_BRIDGE_EXIT_ARROW_SIGN, RAND_INF_DMC_BRIDGE_EXIT_ARROW_SIGN }, + { RC_ZR_SLEEPLESS_WATERFALL_PLAQUE, RAND_INF_ZR_SLEEPLESS_WATERFALL_PLAQUE }, + { RC_ZD_SHOP_RECTANGLE_SIGN, RAND_INF_ZD_SHOP_RECTANGLE_SIGN }, + { RC_ZD_ENTRANCE_RECTANGLE_SIGN, RAND_INF_ZD_ENTRANCE_RECTANGLE_SIGN }, + { RC_ZD_KING_ZORA_PATH_ARROW_SIGN, RAND_INF_ZD_KING_ZORA_PATH_ARROW_SIGN }, + { RC_ZD_NEAR_KING_ZORA_RECTANGLE_SIGN, RAND_INF_ZD_NEAR_KING_ZORA_RECTANGLE_SIGN }, + { RC_ZD_NEAR_KING_ZORA_ARROW_SIGN, RAND_INF_ZD_NEAR_KING_ZORA_ARROW_SIGN }, + { RC_ZF_JABU_JABU_PLATFORM_RECTANGLE_SIGN, RAND_INF_ZF_JABU_JABU_PLATFORM_RECTANGLE_SIGN }, + { RC_ZF_ENTRANCE_ARROW_SIGN, RAND_INF_ZF_ENTRANCE_ARROW_SIGN }, + { RC_LH_LAB_RECTANGLE_SIGN, RAND_INF_LH_LAB_RECTANGLE_SIGN }, + { RC_LH_NORTH_EXIT_ARROW_SIGN, RAND_INF_LH_NORTH_EXIT_ARROW_SIGN }, + { RC_LH_FISHING_SIGN, RAND_INF_LH_FISHING_SIGN }, + { RC_LH_ISLAND_PEDESTAL, RAND_INF_LH_ISLAND_PEDESTAL }, + { RC_LH_FISHING_POND_RECTANGLE_SIGN, RAND_INF_LH_FISHING_POND_RECTANGLE_SIGN }, + { RC_GV_BRIDGE_RECTANGLE_SIGN, RAND_INF_GV_BRIDGE_RECTANGLE_SIGN }, + { RC_GV_EAST_EXIT_ARROW_SIGN, RAND_INF_GV_EAST_EXIT_ARROW_SIGN }, + { RC_GF_EAST_EXIT_ARROW_SIGN, RAND_INF_GF_EAST_EXIT_ARROW_SIGN }, + { RC_GF_HBA_RECTANGLE_SIGN, RAND_INF_GF_HBA_RECTANGLE_SIGN }, + { RC_GF_GATE_EXIT_RECTANGLE_SIGN, RAND_INF_GF_GATE_EXIT_RECTANGLE_SIGN }, + { RC_GF_GTG_ENTRANCE_RECTANGLE_SIGN, RAND_INF_GF_GTG_ENTRANCE_RECTANGLE_SIGN }, + { RC_HW_CARPET_SALESMAN_ARROW_SIGN, RAND_INF_HW_CARPET_SALESMAN_ARROW_SIGN }, + { RC_HW_POE_ALTAR, RAND_INF_HW_POE_ALTAR }, + { RC_DODONGOS_CAVERN_TOP_FLOOR_PEDESTAL, RAND_INF_DODONGOS_CAVERN_TOP_FLOOR_PEDESTAL }, + { RC_SHADOW_TEMPLE_TRUTHSPINNER_RECTANGLE_SIGN, RAND_INF_SHADOW_TEMPLE_TRUTHSPINNER_RECTANGLE_SIGN }, + { RC_SHADOW_TEMPLE_FALLING_SPIKES_RECTANGLE_SIGN, RAND_INF_SHADOW_TEMPLE_FALLING_SPIKES_RECTANGLE_SIGN }, + { RC_SPIRIT_TEMPLE_LEFT_SNAKE_STATUE, RAND_INF_SPIRIT_TEMPLE_LEFT_SNAKE_STATUE }, + { RC_SPIRIT_TEMPLE_RIGHT_SNAKE_STATUE, RAND_INF_SPIRIT_TEMPLE_RIGHT_SNAKE_STATUE }, + { RC_SHADOW_TEMPLE_MQ_LOWER_PIT_RECTANGLE_SIGN, RAND_INF_SHADOW_TEMPLE_MQ_LOWER_PIT_RECTANGLE_SIGN }, + // Wonder Items + { RC_KF_WONDER_TRAINING_1, RAND_INF_KF_WONDER_TRAINING_1 }, + { RC_KF_WONDER_TRAINING_2, RAND_INF_KF_WONDER_TRAINING_2 }, + { RC_KF_WONDER_TRAINING_3, RAND_INF_KF_WONDER_TRAINING_3 }, + { RC_KF_WONDER_SHOP, RAND_INF_KF_WONDER_SHOP }, + { RC_KF_WONDER_SIGN, RAND_INF_KF_WONDER_SIGN }, + { RC_KF_WONDER_PLATFORMS_1, RAND_INF_KF_WONDER_PLATFORMS_1 }, + { RC_KF_WONDER_PLATFORMS_2, RAND_INF_KF_WONDER_PLATFORMS_2 }, + { RC_KF_WONDER_CRAWL_GRASS_1, RAND_INF_KF_WONDER_CRAWL_GRASS_1 }, + { RC_KF_WONDER_CRAWL_GRASS_2, RAND_INF_KF_WONDER_CRAWL_GRASS_2 }, + { RC_HF_WONDER_BRIDGE_1, RAND_INF_HF_WONDER_BRIDGE_1 }, + { RC_HF_WONDER_BRIDGE_2, RAND_INF_HF_WONDER_BRIDGE_2 }, + { RC_HF_WONDER_BRIDGE_3, RAND_INF_HF_WONDER_BRIDGE_3 }, + { RC_MKT_WONDER_DAY_1, RAND_INF_MKT_WONDER_DAY_1 }, + { RC_MKT_WONDER_DAY_2, RAND_INF_MKT_WONDER_DAY_2 }, + { RC_MKT_WONDER_DAY_3, RAND_INF_MKT_WONDER_DAY_3 }, + { RC_MKT_WONDER_DAY_4, RAND_INF_MKT_WONDER_DAY_4 }, + { RC_MKT_WONDER_DAY_5, RAND_INF_MKT_WONDER_DAY_5 }, + { RC_MKT_WONDER_NIGHT_1, RAND_INF_MKT_WONDER_NIGHT_1 }, + { RC_MKT_WONDER_NIGHT_2, RAND_INF_MKT_WONDER_NIGHT_2 }, + { RC_LLR_WONDER_BIG_FENCE, RAND_INF_LLR_WONDER_BIG_FENCE }, + { RC_LLR_WONDER_SMALL_FENCE, RAND_INF_LLR_WONDER_SMALL_FENCE }, + { RC_HC_WONDER_LEFT_TORCH, RAND_INF_HC_WONDER_LEFT_TORCH }, + { RC_HC_WONDER_RIGHT_TORCH, RAND_INF_HC_WONDER_RIGHT_TORCH }, + { RC_HC_WONDER_MOAT_1, RAND_INF_HC_WONDER_MOAT_1 }, + { RC_HC_WONDER_MOAT_2, RAND_INF_HC_WONDER_MOAT_2 }, + { RC_HC_WONDER_MOAT_3, RAND_INF_HC_WONDER_MOAT_3 }, + { RC_HC_WONDER_MOAT_4, RAND_INF_HC_WONDER_MOAT_4 }, + { RC_HC_WONDER_MOAT_5, RAND_INF_HC_WONDER_MOAT_5 }, + { RC_HC_WONDER_MOAT_6, RAND_INF_HC_WONDER_MOAT_6 }, + { RC_HC_WONDER_MOAT_7, RAND_INF_HC_WONDER_MOAT_7 }, + { RC_HC_WONDER_MOAT_8, RAND_INF_HC_WONDER_MOAT_8 }, + { RC_HC_WONDER_MOAT_9, RAND_INF_HC_WONDER_MOAT_9 }, + { RC_HC_WONDER_MOAT_10, RAND_INF_HC_WONDER_MOAT_10 }, + { RC_HC_WONDER_COURTYARD_RIGHT_WINDOW, RAND_INF_HC_WONDER_COURTYARD_RIGHT_WINDOW }, + { RC_HC_WONDER_COURTYARD_LEFT_WINDOW, RAND_INF_HC_WONDER_COURTYARD_LEFT_WINDOW }, + { RC_LW_WONDER_BACK_SKULL_KIDS_GRASS_1, RAND_INF_LW_WONDER_BACK_SKULL_KIDS_GRASS_1 }, + { RC_LW_WONDER_BACK_SKULL_KIDS_GRASS_2, RAND_INF_LW_WONDER_BACK_SKULL_KIDS_GRASS_2 }, + { RC_LW_WONDER_FRONT_SKULL_KIDS_GRASS, RAND_INF_LW_WONDER_FRONT_SKULL_KIDS_GRASS }, + { RC_SFM_WONDER_ENTRANCE, RAND_INF_SFM_WONDER_ENTRANCE }, + { RC_SFM_WONDER_MAZE_1, RAND_INF_SFM_WONDER_MAZE_1 }, + { RC_SFM_WONDER_MAZE_2, RAND_INF_SFM_WONDER_MAZE_2 }, + { RC_SFM_WONDER_MAZE_3, RAND_INF_SFM_WONDER_MAZE_3 }, + { RC_SFM_WONDER_MAZE_4, RAND_INF_SFM_WONDER_MAZE_4 }, + { RC_SFM_WONDER_MAZE_5, RAND_INF_SFM_WONDER_MAZE_5 }, + { RC_KAK_WONDER_UNDER_CONSTRUCTION, RAND_INF_KAK_WONDER_UNDER_CONSTRUCTION }, + { RC_KAK_WONDER_ABOVE_COW, RAND_INF_KAK_WONDER_ABOVE_COW }, + { RC_GY_WONDER_DAMPE_RACE_1, RAND_INF_GY_WONDER_DAMPE_RACE_1 }, + { RC_GY_WONDER_DAMPE_RACE_2, RAND_INF_GY_WONDER_DAMPE_RACE_2 }, + { RC_GY_WONDER_DAMPE_RACE_3, RAND_INF_GY_WONDER_DAMPE_RACE_3 }, + { RC_GY_WONDER_DAMPE_RACE_4, RAND_INF_GY_WONDER_DAMPE_RACE_4 }, + { RC_GY_WONDER_DAMPE_RACE_5, RAND_INF_GY_WONDER_DAMPE_RACE_5 }, + { RC_GY_WONDER_DAMPE_RACE_6, RAND_INF_GY_WONDER_DAMPE_RACE_6 }, + { RC_GY_WONDER_DAMPE_RACE_7, RAND_INF_GY_WONDER_DAMPE_RACE_7 }, + { RC_GY_WONDER_DAMPE_RACE_8, RAND_INF_GY_WONDER_DAMPE_RACE_8 }, + { RC_GY_WONDER_DAMPE_RACE_9, RAND_INF_GY_WONDER_DAMPE_RACE_9 }, + { RC_GY_WONDER_DAMPE_RACE_10, RAND_INF_GY_WONDER_DAMPE_RACE_10 }, + { RC_GY_WONDER_DAMPE_RACE_11, RAND_INF_GY_WONDER_DAMPE_RACE_11 }, + { RC_GY_WONDER_DAMPE_RACE_12, RAND_INF_GY_WONDER_DAMPE_RACE_12 }, + { RC_GY_WONDER_DAMPE_RACE_13, RAND_INF_GY_WONDER_DAMPE_RACE_13 }, + { RC_GY_WONDER_DAMPE_RACE_14, RAND_INF_GY_WONDER_DAMPE_RACE_14 }, + { RC_GY_WONDER_DAMPE_RACE_15, RAND_INF_GY_WONDER_DAMPE_RACE_15 }, + { RC_DMC_WONDER_BENEATH_BRIDGE_PLATFORM, RAND_INF_DMC_WONDER_BENEATH_BRIDGE_PLATFORM }, + { RC_ZR_WONDER_NEAR_DOMAIN_1, RAND_INF_ZR_WONDER_NEAR_DOMAIN_1 }, + { RC_ZR_WONDER_NEAR_DOMAIN_2, RAND_INF_ZR_WONDER_NEAR_DOMAIN_2 }, + { RC_ZR_WONDER_NEAR_DOMAIN_3, RAND_INF_ZR_WONDER_NEAR_DOMAIN_3 }, + { RC_ZR_WONDER_NEAR_DOMAIN_4, RAND_INF_ZR_WONDER_NEAR_DOMAIN_4 }, + { RC_ZR_WONDER_BEFORE_LADDER_1, RAND_INF_ZR_WONDER_BEFORE_LADDER_1 }, + { RC_ZR_WONDER_BEFORE_LADDER_2, RAND_INF_ZR_WONDER_BEFORE_LADDER_2 }, + { RC_ZR_WONDER_BEFORE_LADDER_3, RAND_INF_ZR_WONDER_BEFORE_LADDER_3 }, + { RC_ZR_WONDER_BEFORE_LADDER_4, RAND_INF_ZR_WONDER_BEFORE_LADDER_4 }, + { RC_ZR_WONDER_BEFORE_LADDER_5, RAND_INF_ZR_WONDER_BEFORE_LADDER_5 }, + { RC_ZR_WONDER_BEFORE_LADDER_6, RAND_INF_ZR_WONDER_BEFORE_LADDER_6 }, + { RC_ZR_WONDER_AFTER_LADDER_1, RAND_INF_ZR_WONDER_AFTER_LADDER_1 }, + { RC_ZR_WONDER_AFTER_LADDER_2, RAND_INF_ZR_WONDER_AFTER_LADDER_2 }, + { RC_ZR_WONDER_AFTER_LADDER_3, RAND_INF_ZR_WONDER_AFTER_LADDER_3 }, + { RC_ZR_WONDER_FROG_BRIDGE_1, RAND_INF_ZR_WONDER_FROG_BRIDGE_1 }, + { RC_ZR_WONDER_FROG_BRIDGE_2, RAND_INF_ZR_WONDER_FROG_BRIDGE_2 }, + { RC_ZR_WONDER_FROG_BRIDGE_3, RAND_INF_ZR_WONDER_FROG_BRIDGE_3 }, + { RC_ZR_WONDER_PILLARS_1, RAND_INF_ZR_WONDER_PILLARS_1 }, + { RC_ZR_WONDER_PILLARS_2, RAND_INF_ZR_WONDER_PILLARS_2 }, + { RC_ZR_WONDER_PILLARS_3, RAND_INF_ZR_WONDER_PILLARS_3 }, + { RC_ZR_WONDER_PILLARS_4, RAND_INF_ZR_WONDER_PILLARS_4 }, + { RC_ZR_WONDER_LOWER_LAND_BRIDGE_1, RAND_INF_ZR_WONDER_LOWER_LAND_BRIDGE_1 }, + { RC_ZR_WONDER_LOWER_LAND_BRIDGE_2, RAND_INF_ZR_WONDER_LOWER_LAND_BRIDGE_2 }, + { RC_ZR_WONDER_LOWER_LAND_BRIDGE_3, RAND_INF_ZR_WONDER_LOWER_LAND_BRIDGE_3 }, + { RC_ZR_WONDER_LOWER_LAND_BRIDGE_4, RAND_INF_ZR_WONDER_LOWER_LAND_BRIDGE_4 }, + { RC_ZR_WONDER_NEAR_CUCCO_1, RAND_INF_ZR_WONDER_NEAR_CUCCO_1 }, + { RC_ZR_WONDER_NEAR_CUCCO_2, RAND_INF_ZR_WONDER_NEAR_CUCCO_2 }, + { RC_ZR_WONDER_NEAR_CUCCO_3, RAND_INF_ZR_WONDER_NEAR_CUCCO_3 }, + { RC_ZR_WONDER_LOWER_RIVER_1, RAND_INF_ZR_WONDER_LOWER_RIVER_1 }, + { RC_ZR_WONDER_LOWER_RIVER_2, RAND_INF_ZR_WONDER_LOWER_RIVER_2 }, + { RC_ZR_WONDER_LOWER_RIVER_3, RAND_INF_ZR_WONDER_LOWER_RIVER_3 }, + { RC_ZR_WONDER_LOWER_RIVER_4, RAND_INF_ZR_WONDER_LOWER_RIVER_4 }, + { RC_ZF_WONDER_ROCK, RAND_INF_ZF_WONDER_ROCK }, + { RC_GV_WONDER_LOWER_WATERFALL, RAND_INF_GV_WONDER_LOWER_WATERFALL }, + { RC_GV_WONDER_UPPER_WATERFALL, RAND_INF_GV_WONDER_UPPER_WATERFALL }, + { RC_GF_WONDER_ENTRANCE_SIGN, RAND_INF_GF_WONDER_ENTRANCE_SIGN }, + { RC_GF_WONDER_ARCHERY_SIGN, RAND_INF_GF_WONDER_ARCHERY_SIGN }, + { RC_TH_WONDER_1_TORCH_1, RAND_INF_TH_WONDER_1_TORCH_1 }, + { RC_TH_WONDER_1_TORCH_2, RAND_INF_TH_WONDER_1_TORCH_2 }, + { RC_TH_WONDER_STEEP_SLOPE_LOWER_EXIT, RAND_INF_TH_WONDER_STEEP_SLOPE_LOWER_EXIT }, + { RC_TH_WONDER_STEEP_SLOPE_UPPER_EXIT, RAND_INF_TH_WONDER_STEEP_SLOPE_UPPER_EXIT }, + { RC_TH_WONDER_DOUBLE_JAIL_LOWER_EXIT, RAND_INF_TH_WONDER_DOUBLE_JAIL_LOWER_EXIT }, + { RC_TH_WONDER_DOUBLE_JAIL_UPPER_EXIT, RAND_INF_TH_WONDER_DOUBLE_JAIL_UPPER_EXIT }, + { RC_TH_WONDER_KITCHEN_SKULL, RAND_INF_TH_WONDER_KITCHEN_SKULL }, + { RC_TH_WONDER_KITCHEN_SOUP, RAND_INF_TH_WONDER_KITCHEN_SOUP }, + { RC_TH_WONDER_DEAD_END_SKULL_ENTRANCE, RAND_INF_TH_WONDER_DEAD_END_SKULL_ENTRANCE }, + { RC_TH_WONDER_DEAD_END_SKULL_NEAR_JAIL, RAND_INF_TH_WONDER_DEAD_END_SKULL_NEAR_JAIL }, + { RC_TH_WONDER_BREAK_ROOM_BOTTOM_SKULL, RAND_INF_TH_WONDER_BREAK_ROOM_BOTTOM_SKULL }, + { RC_TH_WONDER_BREAK_ROOM_TOP_SKULL, RAND_INF_TH_WONDER_BREAK_ROOM_TOP_SKULL }, + { RC_COLOSSUS_WONDER_OASIS_TREE_1, RAND_INF_COLOSSUS_WONDER_OASIS_TREE_1 }, + { RC_COLOSSUS_WONDER_OASIS_TREE_2, RAND_INF_COLOSSUS_WONDER_OASIS_TREE_2 }, + { RC_COLOSSUS_WONDER_OASIS_CHILD_TREE, RAND_INF_COLOSSUS_WONDER_OASIS_CHILD_TREE }, + { RC_COLOSSUS_WONDER_GF_TREE_1, RAND_INF_COLOSSUS_WONDER_GF_TREE_1 }, + { RC_COLOSSUS_WONDER_GF_TREE_2, RAND_INF_COLOSSUS_WONDER_GF_TREE_2 }, + { RC_SHADOW_TEMPLE_WONDER_THREE_POTS, RAND_INF_SHADOW_TEMPLE_WONDER_THREE_POTS }, + { RC_GERUDO_TRAINING_GROUND_WONDER_BEAMOS_ROOM, RAND_INF_GERUDO_TRAINING_GROUND_WONDER_BEAMOS_ROOM }, + { RC_GERUDO_TRAINING_GROUND_WONDER_EYE_STATUE_ROOM, RAND_INF_GERUDO_TRAINING_GROUND_WONDER_EYE_STATUE_ROOM }, + { RC_GERUDO_TRAINING_GROUND_WONDER_TORCH_SLUGS_ROOM, RAND_INF_GERUDO_TRAINING_GROUND_WONDER_TORCH_SLUGS_ROOM }, + { RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_1, RAND_INF_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_1 }, + { RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_2, RAND_INF_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_2 }, + { RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_3, RAND_INF_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_3 }, + { RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_4, RAND_INF_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_4 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_LEFT_COW, RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_LEFT_COW }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_RIGHT_COW, RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_RIGHT_COW }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_ELEVATOR_COW, RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_ELEVATOR_COW }, + { RC_JABU_JABUS_BELLY_MQ_HOLES_COW, RAND_INF_JABU_JABUS_BELLY_MQ_HOLES_COW }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_1, RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_1 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_2, RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_2 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_3, RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_3 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_1, RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_1 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_2, RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_2 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_3, RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_3 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_AFTER_BIG_OCTO, RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_AFTER_BIG_OCTO }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_JIGGLIES_COW, RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_JIGGLIES_COW }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_1, + RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_1 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_2, + RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_2 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_3, + RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_3 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_1, + RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_1 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_2, + RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_2 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_3, + RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_3 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_1, + RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_1 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_2, + RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_2 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_3, + RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_3 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_LEFT_COW, RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_LEFT_COW }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_1, + RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_1 }, + { RC_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_2, + RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_2 }, + { RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_1, RAND_INF_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_1 }, + { RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_2, RAND_INF_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_2 }, + { RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_3, RAND_INF_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_3 }, + { RC_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_HOOKSHOT, RAND_INF_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_HOOKSHOT }, + { RC_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_BOW, RAND_INF_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_BOW }, + { RC_FIRE_TEMPLE_MQ_WONDER_LIZALFOS_MAZE, RAND_INF_FIRE_TEMPLE_MQ_WONDER_LIZALFOS_MAZE }, + { RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_1, RAND_INF_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_1 }, + { RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_2, RAND_INF_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_2 }, + { RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_1, RAND_INF_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_1 }, + { RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_2, RAND_INF_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_2 }, + { RC_FIRE_TEMPLE_MQ_WONDER_TORCH_ROOM, RAND_INF_FIRE_TEMPLE_MQ_WONDER_TORCH_ROOM }, + { RC_FIRE_TEMPLE_MQ_WONDER_FIRE_MAZE, RAND_INF_FIRE_TEMPLE_MQ_WONDER_FIRE_MAZE }, + { RC_FIRE_TEMPLE_MQ_WONDER_AFTER_FLARE_DANCER, RAND_INF_FIRE_TEMPLE_MQ_WONDER_AFTER_FLARE_DANCER }, + { RC_FIRE_TEMPLE_MQ_WONDER_STAIRCASE, RAND_INF_FIRE_TEMPLE_MQ_WONDER_STAIRCASE }, + { RC_WATER_TEMPLE_MQ_WONDER_LIZALFOS_ROOM, RAND_INF_WATER_TEMPLE_MQ_WONDER_LIZALFOS_ROOM }, + { RC_WATER_TEMPLE_MQ_WONDER_LONGSHOT_ROOM, RAND_INF_WATER_TEMPLE_MQ_WONDER_LONGSHOT_ROOM }, + { RC_WATER_TEMPLE_MQ_WONDER_STALFOS_ROOM, RAND_INF_WATER_TEMPLE_MQ_WONDER_STALFOS_ROOM }, + { RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_1, + RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_1 }, + { RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_2, + RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_2 }, + { RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_3, + RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_3 }, + { RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_1, RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_1 }, + { RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_2, RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_2 }, + { RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_3, RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_3 }, + { RC_WATER_TEMPLE_MQ_WONDER_AFTER_DARK_LINK, RAND_INF_WATER_TEMPLE_MQ_WONDER_AFTER_DARK_LINK }, + { RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_LEFT_EYE, RAND_INF_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_LEFT_EYE }, + { RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_RIGHT_EYE, RAND_INF_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_RIGHT_EYE }, + { RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_PORTRAIT, RAND_INF_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_PORTRAIT }, + { RC_WATER_TEMPLE_MQ_WONDER_TRIPLE_TORCHES, RAND_INF_WATER_TEMPLE_MQ_WONDER_TRIPLE_TORCHES }, + { RC_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_1, RAND_INF_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_1 }, + { RC_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_2, RAND_INF_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_2 }, + { RC_WATER_TEMPLE_MQ_WONDER_FREESTANDING_ROOM, RAND_INF_WATER_TEMPLE_MQ_WONDER_FREESTANDING_ROOM }, + { RC_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_1, RAND_INF_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_1 }, + { RC_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_2, RAND_INF_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_2 }, + { RC_WATER_TEMPLE_MQ_WONDER_UNDER_PILLAR_ROOM, RAND_INF_WATER_TEMPLE_MQ_WONDER_UNDER_PILLAR_ROOM }, + { RC_WATER_TEMPLE_MQ_WONDER_LIZALFOS_HALLWAY, RAND_INF_WATER_TEMPLE_MQ_WONDER_LIZALFOS_HALLWAY }, + { RC_WATER_TEMPLE_MQ_WONDER_GS_STORAGE_ROOM, RAND_INF_WATER_TEMPLE_MQ_WONDER_GS_STORAGE_ROOM }, + { RC_SPIRIT_TEMPLE_MQ_WONDER_CHEST_HAMMER, RAND_INF_SPIRIT_TEMPLE_MQ_WONDER_CHEST_HAMMER }, + { RC_SPIRIT_TEMPLE_MQ_WONDER_CHEST_SLASH, RAND_INF_SPIRIT_TEMPLE_MQ_WONDER_CHEST_SLASH }, + { RC_SHADOW_TEMPLE_MQ_WONDER_THREE_POTS, RAND_INF_SHADOW_TEMPLE_MQ_WONDER_THREE_POTS }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_1, RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_1 }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_2, RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_2 }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_3, RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_3 }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_4, RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_4 }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_1, RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_1 }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_2, RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_2 }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_3, RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_3 }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_4, RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_4 }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_1, RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_1 }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_2, RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_2 }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_3, RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_3 }, + { RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_4, RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_4 }, + { RC_GERUDO_TRAINING_GROUND_MQ_WONDER_DINOLFOS_ROOM, RAND_INF_GERUDO_TRAINING_GROUND_MQ_WONDER_DINOLFOS_ROOM }, + { RC_GERUDO_TRAINING_GROUND_MQ_WONDER_EYE_STATUE, RAND_INF_GERUDO_TRAINING_GROUND_MQ_WONDER_EYE_STATUE }, + { RC_GANONS_CASTLE_MQ_WONDER_SHADOW_TRIAL, RAND_INF_GANONS_CASTLE_MQ_WONDER_SHADOW_TRIAL }, + // Beggar + { RC_MK_BEGGAR_BUGS, RAND_INF_MK_BEGGAR_BUGS }, + { RC_MK_BEGGAR_FISH, RAND_INF_MK_BEGGAR_FISH }, + { RC_MK_BEGGAR_BLUE_FIRE, RAND_INF_MK_BEGGAR_BLUE_FIRE }, + { RC_KAK_BEGGAR_BUGS, RAND_INF_KAK_BEGGAR_BUGS }, + { RC_KAK_BEGGAR_FISH, RAND_INF_KAK_BEGGAR_FISH }, + { RC_KAK_BEGGAR_BLUE_FIRE, RAND_INF_KAK_BEGGAR_BLUE_FIRE }, + // Icicles + { RC_ICE_CAVERN_ENTRANCE_LEFT_STALAGMITE, RAND_INF_ICE_CAVERN_ENTRANCE_LEFT_STALAGMITE }, + { RC_ICE_CAVERN_ENTRANCE_MIDDLE_STALAGMITE, RAND_INF_ICE_CAVERN_ENTRANCE_MIDDLE_STALAGMITE }, + { RC_ICE_CAVERN_ENTRANCE_RIGHT_STALAGMITE, RAND_INF_ICE_CAVERN_ENTRANCE_RIGHT_STALAGMITE }, + { RC_ICE_CAVERN_ENTRANCE_STALACTITE_1, RAND_INF_ICE_CAVERN_ENTRANCE_STALACTITE_1 }, + { RC_ICE_CAVERN_ENTRANCE_STALACTITE_2, RAND_INF_ICE_CAVERN_ENTRANCE_STALACTITE_2 }, + { RC_ICE_CAVERN_LOBBY_STALACTITE, RAND_INF_ICE_CAVERN_LOBBY_STALACTITE }, + { RC_ICE_CAVERN_LOBBY_LEFT_STALAGMITE, RAND_INF_ICE_CAVERN_LOBBY_LEFT_STALAGMITE }, + { RC_ICE_CAVERN_LOBBY_MIDDLE_STALAGMITE, RAND_INF_ICE_CAVERN_LOBBY_MIDDLE_STALAGMITE }, + { RC_ICE_CAVERN_LOBBY_RIGHT_STALAGMITE, RAND_INF_ICE_CAVERN_LOBBY_RIGHT_STALAGMITE }, + { RC_ICE_CAVERN_AFTER_LOBBY_STALACTITE, RAND_INF_ICE_CAVERN_AFTER_LOBBY_STALACTITE }, + { RC_ICE_CAVERN_AFTER_LOBBY_LEFT_STALAGMITE, RAND_INF_ICE_CAVERN_AFTER_LOBBY_LEFT_STALAGMITE }, + { RC_ICE_CAVERN_AFTER_LOBBY_CENTER_LEFT_STALAGMITE, RAND_INF_ICE_CAVERN_AFTER_LOBBY_CENTER_LEFT_STALAGMITE }, + { RC_ICE_CAVERN_AFTER_LOBBY_CENTER_RIGHT_STALAGMITE, RAND_INF_ICE_CAVERN_AFTER_LOBBY_CENTER_RIGHT_STALAGMITE }, + { RC_ICE_CAVERN_AFTER_LOBBY_RIGHT_STALAGMITE, RAND_INF_ICE_CAVERN_AFTER_LOBBY_RIGHT_STALAGMITE }, + { RC_ICE_CAVERN_SPINNING_BLADE_LEFT_STALAGMITE, RAND_INF_ICE_CAVERN_SPINNING_BLADE_LEFT_STALAGMITE }, + { RC_ICE_CAVERN_SPINNING_BLADE_MIDDLE_STALAGMITE, RAND_INF_ICE_CAVERN_SPINNING_BLADE_MIDDLE_STALAGMITE }, + { RC_ICE_CAVERN_SPINNING_BLADE_RIGHT_STALAGMITE, RAND_INF_ICE_CAVERN_SPINNING_BLADE_RIGHT_STALAGMITE }, + { RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_1, RAND_INF_ICE_CAVERN_MAP_HALLWAY_STALACTITE_1 }, + { RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_2, RAND_INF_ICE_CAVERN_MAP_HALLWAY_STALACTITE_2 }, + { RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_3, RAND_INF_ICE_CAVERN_MAP_HALLWAY_STALACTITE_3 }, + { RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_4, RAND_INF_ICE_CAVERN_MAP_HALLWAY_STALACTITE_4 }, + { RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_5, RAND_INF_ICE_CAVERN_MAP_HALLWAY_STALACTITE_5 }, + { RC_ICE_CAVERN_MAP_HALLWAY_LEFT_STALAGMITE, RAND_INF_ICE_CAVERN_MAP_HALLWAY_LEFT_STALAGMITE }, + { RC_ICE_CAVERN_MAP_HALLWAY_MIDDLE_STALAGMITE, RAND_INF_ICE_CAVERN_MAP_HALLWAY_MIDDLE_STALAGMITE }, + { RC_ICE_CAVERN_MAP_HALLWAY_RIGHT_STALAGMITE, RAND_INF_ICE_CAVERN_MAP_HALLWAY_RIGHT_STALAGMITE }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_1, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_1 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_2, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_2 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_3, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_3 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_4, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_4 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_5, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_5 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_1, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_1 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_2, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_2 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_1, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_1 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_2, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_2 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_3, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_3 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_4, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_4 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_5, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_5 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_6, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_6 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_1, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_1 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_2, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_2 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_3, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_3 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_1, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_1 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_2, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_2 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_3, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_3 }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_4, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_4 }, + { RC_ICE_CAVERN_PUSH_BLOCK_HALL_LEFT_STALAGMITE, RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_LEFT_STALAGMITE }, + { RC_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_LEFT_STALAGMITE, + RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_LEFT_STALAGMITE }, + { RC_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_STALAGMITE, RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_STALAGMITE }, + { RC_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_RIGHT_STALAGMITE, + RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_RIGHT_STALAGMITE }, + { RC_ICE_CAVERN_PUSH_BLOCK_HALL_RIGHT_STALAGMITE, RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_RIGHT_STALAGMITE }, + { RC_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_1, RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_1 }, + { RC_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_2, RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_2 }, + { RC_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_3, RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_3 }, + { RC_ICE_CAVERN_NEAR_END_STALACTITE_1, RAND_INF_ICE_CAVERN_NEAR_END_STALACTITE_1 }, + { RC_ICE_CAVERN_NEAR_END_STALACTITE_2, RAND_INF_ICE_CAVERN_NEAR_END_STALACTITE_2 }, + { RC_ICE_CAVERN_NEAR_END_STALACTITE_3, RAND_INF_ICE_CAVERN_NEAR_END_STALACTITE_3 }, + { RC_ICE_CAVERN_NEAR_END_STALACTITE_4, RAND_INF_ICE_CAVERN_NEAR_END_STALACTITE_4 }, + { RC_ICE_CAVERN_NEAR_END_STALAGMITE_1, RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_1 }, + { RC_ICE_CAVERN_NEAR_END_STALAGMITE_2, RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_2 }, + { RC_ICE_CAVERN_NEAR_END_STALAGMITE_3, RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_3 }, + { RC_ICE_CAVERN_NEAR_END_STALAGMITE_4, RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_4 }, + { RC_ICE_CAVERN_NEAR_END_STALAGMITE_5, RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_5 }, + { RC_ICE_CAVERN_NEAR_END_STALAGMITE_6, RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_6 }, + { RC_ICE_CAVERN_NEAR_END_STALAGMITE_7, RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_7 }, + { RC_ICE_CAVERN_NEAR_END_STALAGMITE_8, RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_8 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_1, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_1 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_2, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_2 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_3, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_3 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_4, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_4 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_5, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_5 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_6, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_6 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_7, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_7 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_8, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_8 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_9, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_9 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_10, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_10 }, + { RC_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_1, RAND_INF_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_1 }, + { RC_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_2, RAND_INF_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_2 }, + { RC_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_1, RAND_INF_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_1 }, + { RC_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_2, RAND_INF_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_2 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_1, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_1 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_2, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_2 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_3, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_3 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_4, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_4 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_5, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_5 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_6, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_6 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_7, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_7 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_8, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_8 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_9, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_9 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_10, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_10 }, + { RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_11, RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_11 }, + { RC_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_1, RAND_INF_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_1 }, + { RC_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_2, RAND_INF_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_2 }, + { RC_ICE_CAVERN_MQ_LOBBY_STALACTITE_1, RAND_INF_ICE_CAVERN_MQ_LOBBY_STALACTITE_1 }, + { RC_ICE_CAVERN_MQ_LOBBY_STALACTITE_2, RAND_INF_ICE_CAVERN_MQ_LOBBY_STALACTITE_2 }, + { RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_1, RAND_INF_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_1 }, + { RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_2, RAND_INF_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_2 }, + { RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_3, RAND_INF_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_3 }, + { RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_4, RAND_INF_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_4 }, + { RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_5, RAND_INF_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_5 }, + { RC_ICE_CAVERN_MQ_HUB_STALAGMITE_1, RAND_INF_ICE_CAVERN_MQ_HUB_STALAGMITE_1 }, + { RC_ICE_CAVERN_MQ_HUB_STALAGMITE_2, RAND_INF_ICE_CAVERN_MQ_HUB_STALAGMITE_2 }, + { RC_ICE_CAVERN_MQ_HUB_STALAGMITE_3, RAND_INF_ICE_CAVERN_MQ_HUB_STALAGMITE_3 }, + { RC_ICE_CAVERN_MQ_HUB_STALAGMITE_4, RAND_INF_ICE_CAVERN_MQ_HUB_STALAGMITE_4 }, + { RC_ICE_CAVERN_MQ_HUB_STALAGMITE_5, RAND_INF_ICE_CAVERN_MQ_HUB_STALAGMITE_5 }, + { RC_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_1, RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_1 }, + { RC_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_2, RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_2 }, + { RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_1, RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_1 }, + { RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_2, RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_2 }, + { RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_3, RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_3 }, + { RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_4, RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_4 }, + { RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_5, RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_5 }, + { RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_6, RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_6 }, + { RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_7, RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_7 }, + { RC_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_1, RAND_INF_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_1 }, + { RC_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_2, RAND_INF_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_2 }, + { RC_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_1, RAND_INF_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_1 }, + { RC_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_2, RAND_INF_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_2 }, + { RC_ICE_CAVERN_MQ_BEFORE_SCARECROW_STALAGMITE, RAND_INF_ICE_CAVERN_MQ_BEFORE_SCARECROW_STALAGMITE }, + { RC_ICE_CAVERN_MQ_SCARECROW_ROOM_STALACTITE, RAND_INF_ICE_CAVERN_MQ_SCARECROW_ROOM_STALACTITE }, + { RC_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_1, RAND_INF_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_1 }, + { RC_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_2, RAND_INF_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_2 }, + { RC_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_3, RAND_INF_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_3 }, + { RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_1, + RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_1 }, + { RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_2, + RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_2 }, + { RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_3, + RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_3 }, + { RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_4, + RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_4 }, + { RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_5, + RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_5 }, + { RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_1, + RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_1 }, + { RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_2, + RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_2 }, + { RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_3, + RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_3 }, + { RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_4, + RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_4 }, + { RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_LEFT_STALACTITE, + RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_LEFT_STALACTITE }, + { RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_1, + RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_1 }, + { RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_2, + RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_2 }, + { RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_3, + RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_3 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_1, RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_1 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_2, RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_2 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_3, RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_3 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_1, RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_1 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_2, RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_2 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_3, RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_3 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_1, RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_1 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_2, RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_2 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_3, RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_3 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_4, RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_4 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_1, RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_1 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_2, RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_2 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_3, RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_3 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_4, RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_4 }, + // Red Ice + { RC_ZD_KING_ZORA_RED_ICE, RAND_INF_ZD_KING_ZORA_RED_ICE }, + { RC_ZD_ZORA_SHOP_RED_ICE, RAND_INF_ZD_ZORA_SHOP_RED_ICE }, + { RC_ICE_CAVERN_ENTRANCE_RED_ICE, RAND_INF_ICE_CAVERN_ENTRANCE_RED_ICE }, + { RC_ICE_CAVERN_LOBBY_LEFT_RED_ICE, RAND_INF_ICE_CAVERN_LOBBY_LEFT_RED_ICE }, + { RC_ICE_CAVERN_LOBBY_RIGHT_RED_ICE, RAND_INF_ICE_CAVERN_LOBBY_RIGHT_RED_ICE }, + { RC_ICE_CAVERN_SPINNING_BLADE_EAST_RED_ICE, RAND_INF_ICE_CAVERN_SPINNING_BLADE_EAST_RED_ICE }, + { RC_ICE_CAVERN_SPINNING_BLADE_WEST_RED_ICE, RAND_INF_ICE_CAVERN_SPINNING_BLADE_WEST_RED_ICE }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_FREESTANDING_RED_ICE, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_FREESTANDING_RED_ICE }, + { RC_ICE_CAVERN_HEART_PIECE_ROOM_CHEST_RED_ICE, RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CHEST_RED_ICE }, + { RC_ICE_CAVERN_MAP_ROOM_POT_RED_ICE, RAND_INF_ICE_CAVERN_MAP_ROOM_POT_RED_ICE }, + { RC_ICE_CAVERN_MAP_ROOM_CHEST_RED_ICE, RAND_INF_ICE_CAVERN_MAP_ROOM_CHEST_RED_ICE }, + { RC_ICE_CAVERN_SILVER_RUPEE_RED_ICE, RAND_INF_ICE_CAVERN_SILVER_RUPEE_RED_ICE }, + { RC_ICE_CAVERN_NEAR_END_LEFT_RED_ICE, RAND_INF_ICE_CAVERN_NEAR_END_LEFT_RED_ICE }, + { RC_ICE_CAVERN_NEAR_END_MIDDLE_RED_ICE, RAND_INF_ICE_CAVERN_NEAR_END_MIDDLE_RED_ICE }, + { RC_ICE_CAVERN_NEAR_END_RIGHT_RED_ICE, RAND_INF_ICE_CAVERN_NEAR_END_RIGHT_RED_ICE }, + { RC_GANONS_CASTLE_WATER_TRIAL_DOOR_RED_ICE, RAND_INF_GANONS_CASTLE_WATER_TRIAL_DOOR_RED_ICE }, + { RC_GANONS_CASTLE_WATER_TRIAL_RUSTED_SWITCH_RED_ICE, RAND_INF_GANONS_CASTLE_WATER_TRIAL_RUSTED_SWITCH_RED_ICE }, + { RC_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM_RED_ICE, RAND_INF_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM_RED_ICE }, + { RC_ICE_CAVERN_MQ_HUB_WEST_LEFT_RED_ICE, RAND_INF_ICE_CAVERN_MQ_HUB_WEST_LEFT_RED_ICE }, + { RC_ICE_CAVERN_MQ_HUB_WEST_MIDDLE_RED_ICE, RAND_INF_ICE_CAVERN_MQ_HUB_WEST_MIDDLE_RED_ICE }, + { RC_ICE_CAVERN_MQ_HUB_WEST_RIGHT_RED_ICE, RAND_INF_ICE_CAVERN_MQ_HUB_WEST_RIGHT_RED_ICE }, + { RC_ICE_CAVERN_MQ_HUB_LEDGE_LEFT_RED_ICE, RAND_INF_ICE_CAVERN_MQ_HUB_LEDGE_LEFT_RED_ICE }, + { RC_ICE_CAVERN_MQ_HUB_LEDGE_MIDDLE_RED_ICE, RAND_INF_ICE_CAVERN_MQ_HUB_LEDGE_MIDDLE_RED_ICE }, + { RC_ICE_CAVERN_MQ_HUB_LEDGE_RIGHT_RED_ICE, RAND_INF_ICE_CAVERN_MQ_HUB_LEDGE_RIGHT_RED_ICE }, + { RC_ICE_CAVERN_MQ_COMPASS_RED_ICE, RAND_INF_ICE_CAVERN_MQ_COMPASS_RED_ICE }, + { RC_ICE_CAVERN_MQ_MAP_RED_ICE, RAND_INF_ICE_CAVERN_MQ_MAP_RED_ICE }, + { RC_ICE_CAVERN_MQ_SCARECROW_LEFT_RED_ICE, RAND_INF_ICE_CAVERN_MQ_SCARECROW_LEFT_RED_ICE }, + { RC_ICE_CAVERN_MQ_SCARECROW_MIDDLE_RED_ICE, RAND_INF_ICE_CAVERN_MQ_SCARECROW_MIDDLE_RED_ICE }, + { RC_ICE_CAVERN_MQ_SCARECROW_RIGHT_RED_ICE, RAND_INF_ICE_CAVERN_MQ_SCARECROW_RIGHT_RED_ICE }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_LEFT_RED_ICE, + RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_LEFT_RED_ICE }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_RIGHT_RED_ICE, + RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_RIGHT_RED_ICE }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_BACK_LEFT_RED_ICE, + RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_BACK_LEFT_RED_ICE }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_1, + RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_1 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_2, + RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_2 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_3, + RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_3 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_4, + RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_4 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_SILVER_RUPEE_RED_ICE, + RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SILVER_RUPEE_RED_ICE }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_1, + RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_1 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_2, + RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_2 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_3, + RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_3 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_4, + RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_4 }, + { RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_5, + RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_5 }, }; CheckIdentity Randomizer::IdentifyBeehive(s32 sceneNum, s16 xPosition, s32 respawnData) { @@ -3121,6 +3643,16 @@ Rando::Location* Randomizer::GetCheckObjectFromActor(s16 actorId, s16 sceneNum, specialRc = RC_DODONGOS_CAVERN_GOSSIP_STONE; } break; + case SCENE_SHOOTING_GALLERY: + // special case for shooting gallery sign + if (actorId == ACTOR_EN_KANBAN) { + if (LINK_IS_ADULT) { + specialRc = RC_KAK_SHOOTING_GALLERY_RECTANGLE_SIGN; + } else { + specialRc = RC_MK_SHOOTING_GALLERY_RECTANGLE_SIGN; + } + } + break; } if (specialRc != RC_UNKNOWN_CHECK) { @@ -3424,6 +3956,162 @@ CheckIdentity Randomizer::IdentifyTree(s32 sceneNum, s32 posX, s32 posZ) { return treeIdentity; } +CheckIdentity Randomizer::IdentifySign(s32 sceneNum, s32 posX, s32 posZ, s32 id) { + struct CheckIdentity signIdentity; + uint32_t signSceneNum = sceneNum; + Rando::Location* location = nullptr; + + // align child/adult signs + if (sceneNum == SCENE_KAKARIKO_VILLAGE && LINK_IS_ADULT && posX == 1165 && posZ == 1545) { + posZ = 1550; + } else if (sceneNum == SCENE_GRAVEYARD && LINK_IS_ADULT) { + if (id == ACTOR_EN_WONDER_TALK2 && posX == -807 && posZ == 266) { + posX = -805; + } else if (id == ACTOR_EN_WONDER_TALK) { + if (posX == 634 && posZ == 260) { + posX = 654; + posZ = 258; + } else if (posX == 634 && posZ == -100) { + posX = 654; + posZ = -102; + } else if (posX == 753 && posZ == 85) { + posX = 752; + } + } + } else if (sceneNum == SCENE_ZORAS_RIVER && LINK_IS_ADULT && posX == 4097 && posZ == -1399) { + posX = 4096; + posZ = -1401; + } + + signIdentity.randomizerInf = RAND_INF_MAX; + signIdentity.randomizerCheck = RC_UNKNOWN_CHECK; + + s32 actorParams = TWO_ACTOR_PARAMS(posX, posZ); + + switch (id) { + case ACTOR_EN_KANBAN: + location = GetCheckObjectFromActor(ACTOR_EN_KANBAN, signSceneNum, actorParams); + break; + case ACTOR_EN_A_OBJ: + location = GetCheckObjectFromActor(ACTOR_EN_A_OBJ, signSceneNum, actorParams); + break; + case ACTOR_EN_WONDER_TALK2: + location = GetCheckObjectFromActor(ACTOR_EN_WONDER_TALK2, signSceneNum, actorParams); + break; + case ACTOR_EN_WONDER_TALK: + location = GetCheckObjectFromActor(ACTOR_EN_WONDER_TALK, signSceneNum, actorParams); + break; + default: + return signIdentity; + } + + if (location == nullptr || location->GetRandomizerCheck() == RC_UNKNOWN_CHECK) { + LUSLOG_WARN("IdentifySign did not receive a valid RC value (%d).", location->GetRandomizerCheck()); + } else { + signIdentity.randomizerInf = rcToRandomizerInf[location->GetRandomizerCheck()]; + signIdentity.randomizerCheck = location->GetRandomizerCheck(); + } + + return signIdentity; +} + +CheckIdentity Randomizer::IdentifyWonderItem(s32 sceneNum, s32 par1, s32 par2) { + struct CheckIdentity wonderIdentity; + uint32_t wonderSceneNum = sceneNum; + + // align oasis trees in colossus between child/adult + if (sceneNum == SCENE_DESERT_COLOSSUS && LINK_IS_ADULT) { + if (par1 == 1157 && par2 == 2388) { + par1 = 1161; + par2 = 2383; + } else if (par1 == 1114 && par2 == 2580) { + par1 = 1113; + par2 = 2581; + } + } + + wonderIdentity.randomizerInf = RAND_INF_MAX; + wonderIdentity.randomizerCheck = RC_UNKNOWN_CHECK; + + s32 actorParams = TWO_ACTOR_PARAMS(par1, par2); + + Rando::Location* location = GetCheckObjectFromActor(ACTOR_EN_WONDER_ITEM, wonderSceneNum, actorParams); + + if (location->GetRandomizerCheck() == RC_UNKNOWN_CHECK) { + LUSLOG_WARN("IdentifyWonderItem did not receive a valid RC value (%d).", location->GetRandomizerCheck()); + } else { + wonderIdentity.randomizerInf = rcToRandomizerInf[location->GetRandomizerCheck()]; + wonderIdentity.randomizerCheck = location->GetRandomizerCheck(); + } + + return wonderIdentity; +} + +CheckIdentity Randomizer::IdentifyBeggar(s32 sceneNum, s32 textId) { + CheckIdentity beggarIdentity; + beggarIdentity.randomizerInf = RAND_INF_MAX; + beggarIdentity.randomizerCheck = RC_UNKNOWN_CHECK; + + Rando::Location* location = GetCheckObjectFromActor(ACTOR_EN_HY, sceneNum, textId); + if (location->GetRandomizerCheck() == RC_UNKNOWN_CHECK) { + LUSLOG_WARN("IdentifyBeggar did not receive a valid RC value (%d).", location->GetRandomizerCheck()); + } else { + beggarIdentity.randomizerInf = rcToRandomizerInf[location->GetRandomizerCheck()]; + beggarIdentity.randomizerCheck = location->GetRandomizerCheck(); + } + + return beggarIdentity; +} + +CheckIdentity Randomizer::IdentifyIcicle(s32 sceneNum, s32 posX, s32 posZ) { + struct CheckIdentity icicleIdentity; + uint32_t icicleSceneNum = sceneNum; + + icicleIdentity.randomizerInf = RAND_INF_MAX; + icicleIdentity.randomizerCheck = RC_UNKNOWN_CHECK; + + s32 actorParams = TWO_ACTOR_PARAMS(posX, posZ); + + Rando::Location* location = GetCheckObjectFromActor(ACTOR_BG_ICE_TURARA, icicleSceneNum, actorParams); + + if (location->GetRandomizerCheck() == RC_UNKNOWN_CHECK) { + LUSLOG_WARN("IdentifyIcicle did not receive a valid RC value (%d).", location->GetRandomizerCheck()); + assert(false); + } else { + icicleIdentity.randomizerInf = rcToRandomizerInf[location->GetRandomizerCheck()]; + icicleIdentity.randomizerCheck = location->GetRandomizerCheck(); + } + + return icicleIdentity; +} + +CheckIdentity Randomizer::IdentifyRedIce(s32 sceneNum, s32 posX, s32 posZ) { + struct CheckIdentity redIceIdentity; + uint32_t redIceSceneNum = sceneNum; + + // Handle KZ moving + if (sceneNum == SCENE_ZORAS_DOMAIN && LINK_IS_ADULT && posX == 531 && posZ == -1818) { + posX = 628; + } + + redIceIdentity.randomizerInf = RAND_INF_MAX; + redIceIdentity.randomizerCheck = RC_UNKNOWN_CHECK; + + s32 actorParams = TWO_ACTOR_PARAMS(posX, posZ); + + Rando::Location* location = GetCheckObjectFromActor(ACTOR_BG_ICE_SHELTER, redIceSceneNum, actorParams); + + if (location->GetRandomizerCheck() == RC_UNKNOWN_CHECK) { + LUSLOG_WARN("IdentifyRedIce did not receive a valid RC value (%d).", location->GetRandomizerCheck()); + assert(false); + } else { + redIceIdentity.randomizerInf = rcToRandomizerInf[location->GetRandomizerCheck()]; + redIceIdentity.randomizerCheck = location->GetRandomizerCheck(); + } + + return redIceIdentity; +} + u8 Randomizer::GetRandoSettingValue(RandomizerSettingKey randoSettingKey) { return Rando::Context::GetInstance()->GetOption(randoSettingKey).Get(); } diff --git a/soh/soh/Enhancements/randomizer/randomizer.h b/soh/soh/Enhancements/randomizer/randomizer.h index b64a804e4ef..0395caa4a8a 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.h +++ b/soh/soh/Enhancements/randomizer/randomizer.h @@ -43,6 +43,11 @@ class Randomizer { CheckIdentity IdentifyCrate(s32 sceneNum, s32 posX, s32 posZ); CheckIdentity IdentifySmallCrate(s32 sceneNum, s32 posX, s32 posZ); CheckIdentity IdentifyTree(s32 sceneNum, s32 posX, s32 posZ); + CheckIdentity IdentifySign(s32 sceneNum, s32 posX, s32 posZ, s32 id); + CheckIdentity IdentifyWonderItem(s32 sceneNum, s32 par1, s32 par2); + CheckIdentity IdentifyBeggar(s32 sceneNum, s32 textId); + CheckIdentity IdentifyIcicle(s32 sceneNum, s32 posX, s32 posZ); + CheckIdentity IdentifyRedIce(s32 sceneNum, s32 posX, s32 posZ); GetItemEntry GetItemFromKnownCheck(RandomizerCheck randomizerCheck, GetItemID ogItemId, bool checkObtainability = true); GetItemEntry GetItemFromActor(s16 actorId, s16 sceneNum, s16 actorParams, GetItemID ogItemId, diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h index c13d37f1bea..535eba5f226 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerCheck.h @@ -1998,6 +1998,213 @@ RANDO_ENUM_ITEM(RC_BOTTOM_OF_THE_WELL_MQ_BASEMENT_HALLWAY_RIGHT_HEART) RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_HEART) RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_LIGHT_TRIAL_RIGHT_HEART) RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_LIGHT_TRIAL_LEFT_HEART) + +// Overworld Wonder Items +RANDO_ENUM_ITEM(RC_KF_WONDER_TRAINING_1) +RANDO_ENUM_ITEM(RC_KF_WONDER_TRAINING_2) +RANDO_ENUM_ITEM(RC_KF_WONDER_TRAINING_3) +RANDO_ENUM_ITEM(RC_KF_WONDER_SHOP) +RANDO_ENUM_ITEM(RC_KF_WONDER_SIGN) +RANDO_ENUM_ITEM(RC_KF_WONDER_PLATFORMS_1) +RANDO_ENUM_ITEM(RC_KF_WONDER_PLATFORMS_2) +RANDO_ENUM_ITEM(RC_KF_WONDER_CRAWL_GRASS_1) +RANDO_ENUM_ITEM(RC_KF_WONDER_CRAWL_GRASS_2) +RANDO_ENUM_ITEM(RC_HF_WONDER_BRIDGE_1) +RANDO_ENUM_ITEM(RC_HF_WONDER_BRIDGE_2) +RANDO_ENUM_ITEM(RC_HF_WONDER_BRIDGE_3) +RANDO_ENUM_ITEM(RC_MKT_WONDER_DAY_1) +RANDO_ENUM_ITEM(RC_MKT_WONDER_DAY_2) +RANDO_ENUM_ITEM(RC_MKT_WONDER_DAY_3) +RANDO_ENUM_ITEM(RC_MKT_WONDER_DAY_4) +RANDO_ENUM_ITEM(RC_MKT_WONDER_DAY_5) +RANDO_ENUM_ITEM(RC_MKT_WONDER_NIGHT_1) +RANDO_ENUM_ITEM(RC_MKT_WONDER_NIGHT_2) +RANDO_ENUM_ITEM(RC_LLR_WONDER_BIG_FENCE) +RANDO_ENUM_ITEM(RC_LLR_WONDER_SMALL_FENCE) +RANDO_ENUM_ITEM(RC_HC_WONDER_LEFT_TORCH) +RANDO_ENUM_ITEM(RC_HC_WONDER_RIGHT_TORCH) +RANDO_ENUM_ITEM(RC_HC_WONDER_MOAT_1) +RANDO_ENUM_ITEM(RC_HC_WONDER_MOAT_2) +RANDO_ENUM_ITEM(RC_HC_WONDER_MOAT_3) +RANDO_ENUM_ITEM(RC_HC_WONDER_MOAT_4) +RANDO_ENUM_ITEM(RC_HC_WONDER_MOAT_5) +RANDO_ENUM_ITEM(RC_HC_WONDER_MOAT_6) +RANDO_ENUM_ITEM(RC_HC_WONDER_MOAT_7) +RANDO_ENUM_ITEM(RC_HC_WONDER_MOAT_8) +RANDO_ENUM_ITEM(RC_HC_WONDER_MOAT_9) +RANDO_ENUM_ITEM(RC_HC_WONDER_MOAT_10) +RANDO_ENUM_ITEM(RC_HC_WONDER_COURTYARD_RIGHT_WINDOW) +RANDO_ENUM_ITEM(RC_HC_WONDER_COURTYARD_LEFT_WINDOW) +RANDO_ENUM_ITEM(RC_LW_WONDER_BACK_SKULL_KIDS_GRASS_1) +RANDO_ENUM_ITEM(RC_LW_WONDER_BACK_SKULL_KIDS_GRASS_2) +RANDO_ENUM_ITEM(RC_LW_WONDER_FRONT_SKULL_KIDS_GRASS) +RANDO_ENUM_ITEM(RC_SFM_WONDER_ENTRANCE) +RANDO_ENUM_ITEM(RC_SFM_WONDER_MAZE_1) +RANDO_ENUM_ITEM(RC_SFM_WONDER_MAZE_2) +RANDO_ENUM_ITEM(RC_SFM_WONDER_MAZE_3) +RANDO_ENUM_ITEM(RC_SFM_WONDER_MAZE_4) +RANDO_ENUM_ITEM(RC_SFM_WONDER_MAZE_5) +RANDO_ENUM_ITEM(RC_KAK_WONDER_UNDER_CONSTRUCTION) +RANDO_ENUM_ITEM(RC_KAK_WONDER_ABOVE_COW) +RANDO_ENUM_ITEM(RC_GY_WONDER_DAMPE_RACE_1) +RANDO_ENUM_ITEM(RC_GY_WONDER_DAMPE_RACE_2) +RANDO_ENUM_ITEM(RC_GY_WONDER_DAMPE_RACE_3) +RANDO_ENUM_ITEM(RC_GY_WONDER_DAMPE_RACE_4) +RANDO_ENUM_ITEM(RC_GY_WONDER_DAMPE_RACE_5) +RANDO_ENUM_ITEM(RC_GY_WONDER_DAMPE_RACE_6) +RANDO_ENUM_ITEM(RC_GY_WONDER_DAMPE_RACE_7) +RANDO_ENUM_ITEM(RC_GY_WONDER_DAMPE_RACE_8) +RANDO_ENUM_ITEM(RC_GY_WONDER_DAMPE_RACE_9) +RANDO_ENUM_ITEM(RC_GY_WONDER_DAMPE_RACE_10) +RANDO_ENUM_ITEM(RC_GY_WONDER_DAMPE_RACE_11) +RANDO_ENUM_ITEM(RC_GY_WONDER_DAMPE_RACE_12) +RANDO_ENUM_ITEM(RC_GY_WONDER_DAMPE_RACE_13) +RANDO_ENUM_ITEM(RC_GY_WONDER_DAMPE_RACE_14) +RANDO_ENUM_ITEM(RC_GY_WONDER_DAMPE_RACE_15) +RANDO_ENUM_ITEM(RC_DMC_WONDER_BENEATH_BRIDGE_PLATFORM) +RANDO_ENUM_ITEM(RC_ZR_WONDER_NEAR_DOMAIN_1) +RANDO_ENUM_ITEM(RC_ZR_WONDER_NEAR_DOMAIN_2) +RANDO_ENUM_ITEM(RC_ZR_WONDER_NEAR_DOMAIN_3) +RANDO_ENUM_ITEM(RC_ZR_WONDER_NEAR_DOMAIN_4) +RANDO_ENUM_ITEM(RC_ZR_WONDER_BEFORE_LADDER_1) +RANDO_ENUM_ITEM(RC_ZR_WONDER_BEFORE_LADDER_2) +RANDO_ENUM_ITEM(RC_ZR_WONDER_BEFORE_LADDER_3) +RANDO_ENUM_ITEM(RC_ZR_WONDER_BEFORE_LADDER_4) +RANDO_ENUM_ITEM(RC_ZR_WONDER_BEFORE_LADDER_5) +RANDO_ENUM_ITEM(RC_ZR_WONDER_BEFORE_LADDER_6) +RANDO_ENUM_ITEM(RC_ZR_WONDER_AFTER_LADDER_1) +RANDO_ENUM_ITEM(RC_ZR_WONDER_AFTER_LADDER_2) +RANDO_ENUM_ITEM(RC_ZR_WONDER_AFTER_LADDER_3) +RANDO_ENUM_ITEM(RC_ZR_WONDER_FROG_BRIDGE_1) +RANDO_ENUM_ITEM(RC_ZR_WONDER_FROG_BRIDGE_2) +RANDO_ENUM_ITEM(RC_ZR_WONDER_FROG_BRIDGE_3) +RANDO_ENUM_ITEM(RC_ZR_WONDER_PILLARS_1) +RANDO_ENUM_ITEM(RC_ZR_WONDER_PILLARS_2) +RANDO_ENUM_ITEM(RC_ZR_WONDER_PILLARS_3) +RANDO_ENUM_ITEM(RC_ZR_WONDER_PILLARS_4) +RANDO_ENUM_ITEM(RC_ZR_WONDER_LOWER_LAND_BRIDGE_1) +RANDO_ENUM_ITEM(RC_ZR_WONDER_LOWER_LAND_BRIDGE_2) +RANDO_ENUM_ITEM(RC_ZR_WONDER_LOWER_LAND_BRIDGE_3) +RANDO_ENUM_ITEM(RC_ZR_WONDER_LOWER_LAND_BRIDGE_4) +RANDO_ENUM_ITEM(RC_ZR_WONDER_NEAR_CUCCO_1) +RANDO_ENUM_ITEM(RC_ZR_WONDER_NEAR_CUCCO_2) +RANDO_ENUM_ITEM(RC_ZR_WONDER_NEAR_CUCCO_3) +RANDO_ENUM_ITEM(RC_ZR_WONDER_LOWER_RIVER_1) +RANDO_ENUM_ITEM(RC_ZR_WONDER_LOWER_RIVER_2) +RANDO_ENUM_ITEM(RC_ZR_WONDER_LOWER_RIVER_3) +RANDO_ENUM_ITEM(RC_ZR_WONDER_LOWER_RIVER_4) +RANDO_ENUM_ITEM(RC_ZF_WONDER_ROCK) +RANDO_ENUM_ITEM(RC_GV_WONDER_LOWER_WATERFALL) +RANDO_ENUM_ITEM(RC_GV_WONDER_UPPER_WATERFALL) +RANDO_ENUM_ITEM(RC_GF_WONDER_ENTRANCE_SIGN) +RANDO_ENUM_ITEM(RC_GF_WONDER_ARCHERY_SIGN) +RANDO_ENUM_ITEM(RC_TH_WONDER_1_TORCH_1) +RANDO_ENUM_ITEM(RC_TH_WONDER_1_TORCH_2) +RANDO_ENUM_ITEM(RC_TH_WONDER_STEEP_SLOPE_LOWER_EXIT) +RANDO_ENUM_ITEM(RC_TH_WONDER_STEEP_SLOPE_UPPER_EXIT) +RANDO_ENUM_ITEM(RC_TH_WONDER_DOUBLE_JAIL_LOWER_EXIT) +RANDO_ENUM_ITEM(RC_TH_WONDER_DOUBLE_JAIL_UPPER_EXIT) +RANDO_ENUM_ITEM(RC_TH_WONDER_KITCHEN_SKULL) +RANDO_ENUM_ITEM(RC_TH_WONDER_KITCHEN_SOUP) +RANDO_ENUM_ITEM(RC_TH_WONDER_DEAD_END_SKULL_ENTRANCE) +RANDO_ENUM_ITEM(RC_TH_WONDER_DEAD_END_SKULL_NEAR_JAIL) +RANDO_ENUM_ITEM(RC_TH_WONDER_BREAK_ROOM_BOTTOM_SKULL) +RANDO_ENUM_ITEM(RC_TH_WONDER_BREAK_ROOM_TOP_SKULL) +RANDO_ENUM_ITEM(RC_COLOSSUS_WONDER_OASIS_TREE_1) +RANDO_ENUM_ITEM(RC_COLOSSUS_WONDER_OASIS_TREE_2) +RANDO_ENUM_ITEM(RC_COLOSSUS_WONDER_OASIS_CHILD_TREE) +RANDO_ENUM_ITEM(RC_COLOSSUS_WONDER_GF_TREE_1) +RANDO_ENUM_ITEM(RC_COLOSSUS_WONDER_GF_TREE_2) +// Dungeon Wonder Items +RANDO_ENUM_ITEM(RC_SHADOW_TEMPLE_WONDER_THREE_POTS) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_WONDER_BEAMOS_ROOM) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_WONDER_EYE_STATUE_ROOM) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_WONDER_TORCH_SLUGS_ROOM) +// MQ Dungeon Wonder Items +RANDO_ENUM_ITEM(RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_1) +RANDO_ENUM_ITEM(RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_2) +RANDO_ENUM_ITEM(RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_3) +RANDO_ENUM_ITEM(RC_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_4) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_LEFT_COW) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_RIGHT_COW) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_ELEVATOR_COW) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_HOLES_COW) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_1) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_2) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_3) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_1) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_2) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_3) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_AFTER_BIG_OCTO) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_JIGGLIES_COW) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_1) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_2) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_3) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_1) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_2) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_3) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_1) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_2) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_3) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_LEFT_COW) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_1) +RANDO_ENUM_ITEM(RC_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_2) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_1) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_2) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_3) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_HOOKSHOT) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_BOW) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_WONDER_LIZALFOS_MAZE) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_1) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_2) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_1) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_2) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_WONDER_TORCH_ROOM) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_WONDER_FIRE_MAZE) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_WONDER_AFTER_FLARE_DANCER) +RANDO_ENUM_ITEM(RC_FIRE_TEMPLE_MQ_WONDER_STAIRCASE) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_LIZALFOS_ROOM) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_LONGSHOT_ROOM) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_STALFOS_ROOM) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_1) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_2) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_3) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_1) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_2) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_3) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_AFTER_DARK_LINK) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_LEFT_EYE) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_RIGHT_EYE) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_PORTRAIT) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_TRIPLE_TORCHES) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_1) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_2) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_FREESTANDING_ROOM) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_1) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_2) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_UNDER_PILLAR_ROOM) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_LIZALFOS_HALLWAY) +RANDO_ENUM_ITEM(RC_WATER_TEMPLE_MQ_WONDER_GS_STORAGE_ROOM) +RANDO_ENUM_ITEM(RC_SPIRIT_TEMPLE_MQ_WONDER_CHEST_HAMMER) +RANDO_ENUM_ITEM(RC_SPIRIT_TEMPLE_MQ_WONDER_CHEST_SLASH) +RANDO_ENUM_ITEM(RC_SHADOW_TEMPLE_MQ_WONDER_THREE_POTS) +RANDO_ENUM_ITEM(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_1) +RANDO_ENUM_ITEM(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_2) +RANDO_ENUM_ITEM(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_3) +RANDO_ENUM_ITEM(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_4) +RANDO_ENUM_ITEM(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_1) +RANDO_ENUM_ITEM(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_2) +RANDO_ENUM_ITEM(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_3) +RANDO_ENUM_ITEM(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_4) +RANDO_ENUM_ITEM(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_1) +RANDO_ENUM_ITEM(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_2) +RANDO_ENUM_ITEM(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_3) +RANDO_ENUM_ITEM(RC_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_4) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_WONDER_DINOLFOS_ROOM) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_WONDER_EYE_STATUE) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WONDER_SHADOW_TRIAL) +// End Wonder Items + RANDO_ENUM_ITEM(RC_SFM_FAIRY_GROTTO_FAIRY_1) RANDO_ENUM_ITEM(RC_SFM_FAIRY_GROTTO_FAIRY_2) RANDO_ENUM_ITEM(RC_SFM_FAIRY_GROTTO_FAIRY_3) @@ -2191,6 +2398,16 @@ RANDO_ENUM_ITEM(RC_LW_SHORTCUT_STORMS_FAIRY) RANDO_ENUM_ITEM(RC_TH_KITCHEN_SUN_FAIRY) RANDO_ENUM_ITEM(RC_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY) RANDO_ENUM_ITEM(RC_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY) +RANDO_ENUM_ITEM(RC_HC_NEAR_WALL_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RC_HC_NEAR_STAIRS_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RC_HC_NEAR_BOULDER_PATH_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RC_HC_NEAR_ARCHWAY_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RC_LW_MEADOW_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RC_GY_NEAR_HUT_GRAVE_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RC_ZR_NEAR_ROCK_CIRCLE_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RC_ZR_WATERFALL_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RC_ZF_LOG_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RC_LH_SCARECROW_BUTTERFLY_FAIRY) RANDO_ENUM_ITEM(RC_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY) RANDO_ENUM_ITEM(RC_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY) RANDO_ENUM_ITEM(RC_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY) @@ -2563,6 +2780,290 @@ RANDO_ENUM_ITEM(RC_DEKU_TREE_QUEEN_GOHMA_GRASS_7) RANDO_ENUM_ITEM(RC_DEKU_TREE_QUEEN_GOHMA_GRASS_8) // End Grass +// Overworld Signs +RANDO_ENUM_ITEM(RC_KF_DEKU_TREE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_KF_STEPPING_STONES_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_KF_LINKS_HOUSE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_KF_FIRST_TRAINING_CENTER_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_KF_SECOND_TRAINING_CENTER_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_KF_AFTER_CRAWLSPACE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_KF_CRAWL_RECTANGLE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_KF_LOST_WOODS_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_KF_HOUSE_OF_TWINS_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_KF_SHOP_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_KF_SARIAS_HOUSE_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_KF_LOST_WOODS_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_KF_MIDOS_HOUSE_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_KF_TRAINING_CENTER_ENTRANCE_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_KF_INNER_TRAINING_CENTER_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_KF_KNOW_IT_ALL_BROTHERS_HOUSE_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_KF_BOULDER_MAZE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_KF_LINKS_HOUSE_SIGN) +RANDO_ENUM_ITEM(RC_LW_THEATER_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_HF_CASTLE_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_HF_WOODED_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_HF_ROCKY_PATH_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_HF_FENCED_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_HF_CENTER_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_HF_RIVER_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_HF_STAIRS_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_MK_SHOOTING_GALLERY_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_MK_MASK_SHOP_SIGN) +RANDO_ENUM_ITEM(RC_TOT_ALTAR) +RANDO_ENUM_ITEM(RC_HC_DEAD_END_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_KAK_GUARD_GATE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_KAK_WELL_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_KAK_SOUTHEAST_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_KAK_FRONT_GATE_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_KAK_SHOOTING_GALLERY_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_GY_ENTRANCE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_GY_ENTRANCE_PLINTH) +RANDO_ENUM_ITEM(RC_GY_RIGHT_OF_ROYAL_TOMB_GRAVE) +RANDO_ENUM_ITEM(RC_GY_LEFT_OF_ROYAL_TOMB_GRAVE) +RANDO_ENUM_ITEM(RC_GY_ROYAL_TOMB_GRAVE) +RANDO_ENUM_ITEM(RC_DMT_ABOVE_DODONGO_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_DMT_ADULT_CENTER_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_DMT_CHILD_CENTER_EXIT_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_DMT_DODONGOS_CAVERN_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_DMT_CENTER_TRAIL_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_DMT_TO_UPPER_TRAIL_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_DMT_UPPER_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_DMT_TO_CENTER_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_DMT_LOWER_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_GC_CHILD_ROLLING_GORON_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_DMC_BRIDGE_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_ZR_SLEEPLESS_WATERFALL_PLAQUE) +RANDO_ENUM_ITEM(RC_ZD_SHOP_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_ZD_ENTRANCE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_ZD_KING_ZORA_PATH_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_ZD_NEAR_KING_ZORA_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_ZD_NEAR_KING_ZORA_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_ZF_JABU_JABU_PLATFORM_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_ZF_ENTRANCE_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_LH_LAB_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_LH_NORTH_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_LH_FISHING_SIGN) +RANDO_ENUM_ITEM(RC_LH_ISLAND_PEDESTAL) +RANDO_ENUM_ITEM(RC_LH_FISHING_POND_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_GV_BRIDGE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_GV_EAST_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_GF_EAST_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_GF_HBA_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_GF_GATE_EXIT_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_GF_GTG_ENTRANCE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_HW_CARPET_SALESMAN_ARROW_SIGN) +RANDO_ENUM_ITEM(RC_HW_POE_ALTAR) +// Dungeon Signs +RANDO_ENUM_ITEM(RC_DODONGOS_CAVERN_TOP_FLOOR_PEDESTAL) +RANDO_ENUM_ITEM(RC_SHADOW_TEMPLE_TRUTHSPINNER_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_SHADOW_TEMPLE_FALLING_SPIKES_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RC_SPIRIT_TEMPLE_LEFT_SNAKE_STATUE) +RANDO_ENUM_ITEM(RC_SPIRIT_TEMPLE_RIGHT_SNAKE_STATUE) +// MQ Dungeon Signs +RANDO_ENUM_ITEM(RC_SHADOW_TEMPLE_MQ_LOWER_PIT_RECTANGLE_SIGN) +// Beggar +RANDO_ENUM_ITEM(RC_MK_BEGGAR_BUGS) +RANDO_ENUM_ITEM(RC_MK_BEGGAR_FISH) +RANDO_ENUM_ITEM(RC_MK_BEGGAR_BLUE_FIRE) +RANDO_ENUM_ITEM(RC_KAK_BEGGAR_BUGS) +RANDO_ENUM_ITEM(RC_KAK_BEGGAR_FISH) +RANDO_ENUM_ITEM(RC_KAK_BEGGAR_BLUE_FIRE) + +// Vanilla Icicles +RANDO_ENUM_ITEM(RC_ICE_CAVERN_ENTRANCE_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_ENTRANCE_MIDDLE_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_ENTRANCE_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_ENTRANCE_STALACTITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_ENTRANCE_STALACTITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_LOBBY_STALACTITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_LOBBY_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_LOBBY_MIDDLE_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_LOBBY_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_AFTER_LOBBY_STALACTITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_AFTER_LOBBY_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_AFTER_LOBBY_CENTER_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_AFTER_LOBBY_CENTER_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_AFTER_LOBBY_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_SPINNING_BLADE_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_SPINNING_BLADE_MIDDLE_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_SPINNING_BLADE_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_3) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_4) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MAP_HALLWAY_STALACTITE_5) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MAP_HALLWAY_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MAP_HALLWAY_MIDDLE_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MAP_HALLWAY_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_3) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_4) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_5) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_3) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_4) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_5) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_6) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_3) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_3) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_4) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_PUSH_BLOCK_HALL_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_PUSH_BLOCK_HALL_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_3) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_NEAR_END_STALACTITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_NEAR_END_STALACTITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_NEAR_END_STALACTITE_3) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_NEAR_END_STALACTITE_4) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_NEAR_END_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_NEAR_END_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_NEAR_END_STALAGMITE_3) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_NEAR_END_STALAGMITE_4) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_NEAR_END_STALAGMITE_5) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_NEAR_END_STALAGMITE_6) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_NEAR_END_STALAGMITE_7) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_NEAR_END_STALAGMITE_8) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_3) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_4) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_5) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_6) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_7) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_8) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_9) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_10) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_1) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_2) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_3) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_4) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_5) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_6) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_7) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_8) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_9) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_10) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_STALACTITE_11) +// MQ Icicles +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_LOBBY_STALACTITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_LOBBY_STALACTITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_3) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_4) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_5) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_HUB_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_HUB_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_HUB_STALAGMITE_3) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_HUB_STALAGMITE_4) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_HUB_STALAGMITE_5) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_3) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_4) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_5) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_6) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_7) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_BEFORE_SCARECROW_STALAGMITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_SCARECROW_ROOM_STALACTITE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_1) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_2) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_3) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_3) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_4) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_5) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_1) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_2) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_3) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_4) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_LEFT_STALACTITE) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_1) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_2) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_3) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_1) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_2) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_3) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_1) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_2) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_3) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_3) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_4) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_1) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_2) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_3) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_4) +// Red Ice +RANDO_ENUM_ITEM(RC_ZD_KING_ZORA_RED_ICE) +RANDO_ENUM_ITEM(RC_ZD_ZORA_SHOP_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_ENTRANCE_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_LOBBY_LEFT_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_LOBBY_RIGHT_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_SPINNING_BLADE_EAST_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_SPINNING_BLADE_WEST_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_FREESTANDING_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_HEART_PIECE_ROOM_CHEST_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MAP_ROOM_POT_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MAP_ROOM_CHEST_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_SILVER_RUPEE_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_NEAR_END_LEFT_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_NEAR_END_MIDDLE_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_NEAR_END_RIGHT_RED_ICE) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_DOOR_RED_ICE) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_WATER_TRIAL_RUSTED_SWITCH_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_HUB_WEST_LEFT_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_HUB_WEST_MIDDLE_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_HUB_WEST_RIGHT_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_HUB_LEDGE_LEFT_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_HUB_LEDGE_MIDDLE_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_HUB_LEDGE_RIGHT_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_COMPASS_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_MAP_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_SCARECROW_LEFT_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_SCARECROW_MIDDLE_RED_ICE) +RANDO_ENUM_ITEM(RC_ICE_CAVERN_MQ_SCARECROW_RIGHT_RED_ICE) +RANDO_ENUM_ITEM(RC_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM_RED_ICE) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_LEFT_RED_ICE) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_RIGHT_RED_ICE) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_BACK_LEFT_RED_ICE) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_1) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_2) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_3) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_4) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SILVER_RUPEE_RED_ICE) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_1) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_2) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_3) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_4) +RANDO_ENUM_ITEM(RC_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_5) + RANDO_ENUM_ITEM(RC_MAX) RANDO_ENUM_END(RandomizerCheck) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h index a57f7635531..065b6d2f5b8 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerHintTextKey.h @@ -1368,6 +1368,33 @@ RANDO_ENUM_ITEM(RHT_TREE_LON_LON_RANCH) RANDO_ENUM_ITEM(RHT_TREE_KAKARIKO) RANDO_ENUM_ITEM(RHT_BUSH_HYRULE_FIELD) RANDO_ENUM_ITEM(RHT_BUSH_ZORAS_FOUNTAIN) +// Shuffle Wonder Items +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_KOKIRI_FOREST) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_HYRULE_FIELD) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_MARKET) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_LON_LON_RANCH) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_HYRULE_CASTLE) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_CASTLE_COURTYARD_ZELDA) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_LOST_WOODS) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_SACRED_FOREST_MEADOW) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_KAKARIKO_VILLAGE) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_DAMPES_GRAVE) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_DEATH_MOUNTAIN_CRATER) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_ZORAS_RIVER) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_ZORAS_FOUNTAIN) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_GERUDO_VALLEY) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_GERUDOS_FORTRESS) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_THIEVES_HIDEOUT) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_DESERT_COLOSSUS) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_DEKU_TREE) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_JABU_JABU) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_FIRE_TEMPLE) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_WATER_TEMPLE) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_SPIRIT_TEMPLE) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_SHADOW_TEMPLE) +RANDO_ENUM_ITEM(RHT_BOTTOM_OF_THE_WELL_WONDER_ITEM) +RANDO_ENUM_ITEM(RHT_WONDER_ITEM_GERUDO_TRAINING_GROUND) +RANDO_ENUM_ITEM(RHT_GANONS_CASTLE_WONDER_ITEM) // Ganon Line RANDO_ENUM_ITEM(RHT_GANON_JOKE01) RANDO_ENUM_ITEM(RHT_GANON_JOKE02) @@ -1515,6 +1542,12 @@ RANDO_ENUM_ITEM(RHT_LW_SHORTCUT_STORMS_FAIRY) RANDO_ENUM_ITEM(RHT_TH_KITCHEN_SUN_FAIRY) RANDO_ENUM_ITEM(RHT_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY) RANDO_ENUM_ITEM(RHT_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY) +RANDO_ENUM_ITEM(RHT_BUTTERFLY_FAIRY_HYRULE_CASTLE) +RANDO_ENUM_ITEM(RHT_BUTTERFLY_FAIRY_LOST_WOODS) +RANDO_ENUM_ITEM(RHT_BUTTERFLY_FAIRY_GRAVEYARD) +RANDO_ENUM_ITEM(RHT_BUTTERFLY_FAIRY_ZORAS_RIVER) +RANDO_ENUM_ITEM(RHT_BUTTERFLY_FAIRY_ZORAS_FOUNTAIN) +RANDO_ENUM_ITEM(RHT_BUTTERFLY_FAIRY_LAKE_HYLIA) RANDO_ENUM_ITEM(RHT_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY) RANDO_ENUM_ITEM(RHT_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY) RANDO_ENUM_ITEM(RHT_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY) @@ -1547,6 +1580,44 @@ RANDO_ENUM_ITEM(RHT_DEKU_TREE_GRASS) RANDO_ENUM_ITEM(RHT_DODONGOS_CAVERN_GRASS) RANDO_ENUM_ITEM(RHT_BOTTOM_OF_THE_WELL_GRASS) RANDO_ENUM_ITEM(RHT_JABU_JABUS_BELLY_GRASS) +// SIGNS +RANDO_ENUM_ITEM(RHT_SIGN_KOKIRI_FOREST) +RANDO_ENUM_ITEM(RHT_SIGN_LINKS_HOUSE) +RANDO_ENUM_ITEM(RHT_SIGN_DEKU_THEATER) +RANDO_ENUM_ITEM(RHT_SIGN_HYRULE_FIELD) +RANDO_ENUM_ITEM(RHT_SIGN_MK_SHOOTING_GALLERY) +RANDO_ENUM_ITEM(RHT_SIGN_HAPPY_MASK_SHOP) +RANDO_ENUM_ITEM(RHT_SIGN_TEMPLE_OF_TIME) +RANDO_ENUM_ITEM(RHT_SIGN_HYRULE_CASTLE) +RANDO_ENUM_ITEM(RHT_SIGN_KAKARIKO_VILLAGE) +RANDO_ENUM_ITEM(RHT_SIGN_KAK_SHOOTING_GALLERY) +RANDO_ENUM_ITEM(RHT_SIGN_GRAVEYARD) +RANDO_ENUM_ITEM(RHT_SIGN_DEATH_MOUNTAIN_TRAIL) +RANDO_ENUM_ITEM(RHT_SIGN_GORON_CITY) +RANDO_ENUM_ITEM(RHT_SIGN_DEATH_MOUNTAIN_CRATER) +RANDO_ENUM_ITEM(RHT_SIGN_ZORAS_RIVER) +RANDO_ENUM_ITEM(RHT_SIGN_ZORAS_DOMAIN) +RANDO_ENUM_ITEM(RHT_SIGN_ZORAS_FOUNTAIN) +RANDO_ENUM_ITEM(RHT_SIGN_LAKE_HYLIA) +RANDO_ENUM_ITEM(RHT_SIGN_FISHING_POND) +RANDO_ENUM_ITEM(RHT_SIGN_GERUDO_VALLEY) +RANDO_ENUM_ITEM(RHT_SIGN_GERUDO_FORTRESS) +RANDO_ENUM_ITEM(RHT_SIGN_HAUNTED_WASTELAND) +RANDO_ENUM_ITEM(RHT_SIGN_DODONGOS_CAVERN) +RANDO_ENUM_ITEM(RHT_SIGN_SHADOW_TEMPLE) +RANDO_ENUM_ITEM(RHT_SIGN_SPIRIT_TEMPLE) +// BEGGAR +RANDO_ENUM_ITEM(RHT_BEGGAR_MARKET) +RANDO_ENUM_ITEM(RHT_BEGGAR_KAKARIKO_VILLAGE) +// ICICLES +RANDO_ENUM_ITEM(RHT_ICE_CAVERN_ICICLE) +RANDO_ENUM_ITEM(RHT_GERUDO_TRAINING_GROUND_ICICLE) +RANDO_ENUM_ITEM(RHT_GANONS_CASTLE_ICICLE) +// RED ICE +RANDO_ENUM_ITEM(RHT_RED_ICE_ZORAS_DOMAIN) +RANDO_ENUM_ITEM(RHT_GERUDO_TRAINING_GROUND_RED_ICE) +RANDO_ENUM_ITEM(RHT_ICE_CAVERN_RED_ICE) +RANDO_ENUM_ITEM(RHT_GANONS_CASTLE_RED_ICE) // MAX RANDO_ENUM_ITEM(RHT_MAX) RANDO_ENUM_END(RandomizerHintTextKey) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h index d679bb22347..803313f6a07 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerInf.h @@ -1484,6 +1484,212 @@ RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_HEART) RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_LIGHT_TRIAL_RIGHT_HEART) RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_LIGHT_TRIAL_LEFT_HEART) +// Overworld Wonder Items +RANDO_ENUM_ITEM(RAND_INF_KF_WONDER_TRAINING_1) +RANDO_ENUM_ITEM(RAND_INF_KF_WONDER_TRAINING_2) +RANDO_ENUM_ITEM(RAND_INF_KF_WONDER_TRAINING_3) +RANDO_ENUM_ITEM(RAND_INF_KF_WONDER_SHOP) +RANDO_ENUM_ITEM(RAND_INF_KF_WONDER_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_WONDER_PLATFORMS_1) +RANDO_ENUM_ITEM(RAND_INF_KF_WONDER_PLATFORMS_2) +RANDO_ENUM_ITEM(RAND_INF_KF_WONDER_CRAWL_GRASS_1) +RANDO_ENUM_ITEM(RAND_INF_KF_WONDER_CRAWL_GRASS_2) +RANDO_ENUM_ITEM(RAND_INF_HF_WONDER_BRIDGE_1) +RANDO_ENUM_ITEM(RAND_INF_HF_WONDER_BRIDGE_2) +RANDO_ENUM_ITEM(RAND_INF_HF_WONDER_BRIDGE_3) +RANDO_ENUM_ITEM(RAND_INF_MKT_WONDER_DAY_1) +RANDO_ENUM_ITEM(RAND_INF_MKT_WONDER_DAY_2) +RANDO_ENUM_ITEM(RAND_INF_MKT_WONDER_DAY_3) +RANDO_ENUM_ITEM(RAND_INF_MKT_WONDER_DAY_4) +RANDO_ENUM_ITEM(RAND_INF_MKT_WONDER_DAY_5) +RANDO_ENUM_ITEM(RAND_INF_MKT_WONDER_NIGHT_1) +RANDO_ENUM_ITEM(RAND_INF_MKT_WONDER_NIGHT_2) +RANDO_ENUM_ITEM(RAND_INF_LLR_WONDER_BIG_FENCE) +RANDO_ENUM_ITEM(RAND_INF_LLR_WONDER_SMALL_FENCE) +RANDO_ENUM_ITEM(RAND_INF_HC_WONDER_LEFT_TORCH) +RANDO_ENUM_ITEM(RAND_INF_HC_WONDER_RIGHT_TORCH) +RANDO_ENUM_ITEM(RAND_INF_HC_WONDER_MOAT_1) +RANDO_ENUM_ITEM(RAND_INF_HC_WONDER_MOAT_2) +RANDO_ENUM_ITEM(RAND_INF_HC_WONDER_MOAT_3) +RANDO_ENUM_ITEM(RAND_INF_HC_WONDER_MOAT_4) +RANDO_ENUM_ITEM(RAND_INF_HC_WONDER_MOAT_5) +RANDO_ENUM_ITEM(RAND_INF_HC_WONDER_MOAT_6) +RANDO_ENUM_ITEM(RAND_INF_HC_WONDER_MOAT_7) +RANDO_ENUM_ITEM(RAND_INF_HC_WONDER_MOAT_8) +RANDO_ENUM_ITEM(RAND_INF_HC_WONDER_MOAT_9) +RANDO_ENUM_ITEM(RAND_INF_HC_WONDER_MOAT_10) +RANDO_ENUM_ITEM(RAND_INF_HC_WONDER_COURTYARD_RIGHT_WINDOW) +RANDO_ENUM_ITEM(RAND_INF_HC_WONDER_COURTYARD_LEFT_WINDOW) +RANDO_ENUM_ITEM(RAND_INF_LW_WONDER_BACK_SKULL_KIDS_GRASS_1) +RANDO_ENUM_ITEM(RAND_INF_LW_WONDER_BACK_SKULL_KIDS_GRASS_2) +RANDO_ENUM_ITEM(RAND_INF_LW_WONDER_FRONT_SKULL_KIDS_GRASS) +RANDO_ENUM_ITEM(RAND_INF_SFM_WONDER_ENTRANCE) +RANDO_ENUM_ITEM(RAND_INF_SFM_WONDER_MAZE_1) +RANDO_ENUM_ITEM(RAND_INF_SFM_WONDER_MAZE_2) +RANDO_ENUM_ITEM(RAND_INF_SFM_WONDER_MAZE_3) +RANDO_ENUM_ITEM(RAND_INF_SFM_WONDER_MAZE_4) +RANDO_ENUM_ITEM(RAND_INF_SFM_WONDER_MAZE_5) +RANDO_ENUM_ITEM(RAND_INF_KAK_WONDER_UNDER_CONSTRUCTION) +RANDO_ENUM_ITEM(RAND_INF_KAK_WONDER_ABOVE_COW) +RANDO_ENUM_ITEM(RAND_INF_GY_WONDER_DAMPE_RACE_1) +RANDO_ENUM_ITEM(RAND_INF_GY_WONDER_DAMPE_RACE_2) +RANDO_ENUM_ITEM(RAND_INF_GY_WONDER_DAMPE_RACE_3) +RANDO_ENUM_ITEM(RAND_INF_GY_WONDER_DAMPE_RACE_4) +RANDO_ENUM_ITEM(RAND_INF_GY_WONDER_DAMPE_RACE_5) +RANDO_ENUM_ITEM(RAND_INF_GY_WONDER_DAMPE_RACE_6) +RANDO_ENUM_ITEM(RAND_INF_GY_WONDER_DAMPE_RACE_7) +RANDO_ENUM_ITEM(RAND_INF_GY_WONDER_DAMPE_RACE_8) +RANDO_ENUM_ITEM(RAND_INF_GY_WONDER_DAMPE_RACE_9) +RANDO_ENUM_ITEM(RAND_INF_GY_WONDER_DAMPE_RACE_10) +RANDO_ENUM_ITEM(RAND_INF_GY_WONDER_DAMPE_RACE_11) +RANDO_ENUM_ITEM(RAND_INF_GY_WONDER_DAMPE_RACE_12) +RANDO_ENUM_ITEM(RAND_INF_GY_WONDER_DAMPE_RACE_13) +RANDO_ENUM_ITEM(RAND_INF_GY_WONDER_DAMPE_RACE_14) +RANDO_ENUM_ITEM(RAND_INF_GY_WONDER_DAMPE_RACE_15) +RANDO_ENUM_ITEM(RAND_INF_DMC_WONDER_BENEATH_BRIDGE_PLATFORM) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_NEAR_DOMAIN_1) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_NEAR_DOMAIN_2) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_NEAR_DOMAIN_3) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_NEAR_DOMAIN_4) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_BEFORE_LADDER_1) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_BEFORE_LADDER_2) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_BEFORE_LADDER_3) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_BEFORE_LADDER_4) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_BEFORE_LADDER_5) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_BEFORE_LADDER_6) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_AFTER_LADDER_1) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_AFTER_LADDER_2) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_AFTER_LADDER_3) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_FROG_BRIDGE_1) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_FROG_BRIDGE_2) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_FROG_BRIDGE_3) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_PILLARS_1) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_PILLARS_2) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_PILLARS_3) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_PILLARS_4) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_LOWER_LAND_BRIDGE_1) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_LOWER_LAND_BRIDGE_2) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_LOWER_LAND_BRIDGE_3) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_LOWER_LAND_BRIDGE_4) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_NEAR_CUCCO_1) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_NEAR_CUCCO_2) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_NEAR_CUCCO_3) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_LOWER_RIVER_1) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_LOWER_RIVER_2) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_LOWER_RIVER_3) +RANDO_ENUM_ITEM(RAND_INF_ZR_WONDER_LOWER_RIVER_4) +RANDO_ENUM_ITEM(RAND_INF_ZF_WONDER_ROCK) +RANDO_ENUM_ITEM(RAND_INF_GV_WONDER_LOWER_WATERFALL) +RANDO_ENUM_ITEM(RAND_INF_GV_WONDER_UPPER_WATERFALL) +RANDO_ENUM_ITEM(RAND_INF_GF_WONDER_ENTRANCE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_GF_WONDER_ARCHERY_SIGN) +RANDO_ENUM_ITEM(RAND_INF_TH_WONDER_1_TORCH_1) +RANDO_ENUM_ITEM(RAND_INF_TH_WONDER_1_TORCH_2) +RANDO_ENUM_ITEM(RAND_INF_TH_WONDER_STEEP_SLOPE_LOWER_EXIT) +RANDO_ENUM_ITEM(RAND_INF_TH_WONDER_STEEP_SLOPE_UPPER_EXIT) +RANDO_ENUM_ITEM(RAND_INF_TH_WONDER_DOUBLE_JAIL_LOWER_EXIT) +RANDO_ENUM_ITEM(RAND_INF_TH_WONDER_DOUBLE_JAIL_UPPER_EXIT) +RANDO_ENUM_ITEM(RAND_INF_TH_WONDER_KITCHEN_SKULL) +RANDO_ENUM_ITEM(RAND_INF_TH_WONDER_KITCHEN_SOUP) +RANDO_ENUM_ITEM(RAND_INF_TH_WONDER_DEAD_END_SKULL_ENTRANCE) +RANDO_ENUM_ITEM(RAND_INF_TH_WONDER_DEAD_END_SKULL_NEAR_JAIL) +RANDO_ENUM_ITEM(RAND_INF_TH_WONDER_BREAK_ROOM_BOTTOM_SKULL) +RANDO_ENUM_ITEM(RAND_INF_TH_WONDER_BREAK_ROOM_TOP_SKULL) +RANDO_ENUM_ITEM(RAND_INF_COLOSSUS_WONDER_OASIS_TREE_1) +RANDO_ENUM_ITEM(RAND_INF_COLOSSUS_WONDER_OASIS_TREE_2) +RANDO_ENUM_ITEM(RAND_INF_COLOSSUS_WONDER_OASIS_CHILD_TREE) +RANDO_ENUM_ITEM(RAND_INF_COLOSSUS_WONDER_GF_TREE_1) +RANDO_ENUM_ITEM(RAND_INF_COLOSSUS_WONDER_GF_TREE_2) +// Dungeon Wonder Items +RANDO_ENUM_ITEM(RAND_INF_SHADOW_TEMPLE_WONDER_THREE_POTS) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_WONDER_BEAMOS_ROOM) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_WONDER_EYE_STATUE_ROOM) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_WONDER_TORCH_SLUGS_ROOM) +// MQ Dungeon Wonder Items +RANDO_ENUM_ITEM(RAND_INF_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_1) +RANDO_ENUM_ITEM(RAND_INF_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_2) +RANDO_ENUM_ITEM(RAND_INF_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_3) +RANDO_ENUM_ITEM(RAND_INF_DEKU_TREE_MQ_WONDER_BASEMENT_GRAVE_4) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_LEFT_COW) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_ENTRANCE_RIGHT_COW) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_ELEVATOR_COW) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_HOLES_COW) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_1) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_2) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_RIGHT_COW_3) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_1) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_2) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BASEMENT_LEFT_COW_3) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_AFTER_BIG_OCTO) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_JIGGLIES_COW) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_1) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_2) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_RIGHT_3) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_1) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_2) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_COW_LEFT_3) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_1) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_2) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_FALLING_LIKE_LIKES_EXPLOSION_3) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_LEFT_COW) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_1) +RANDO_ENUM_ITEM(RAND_INF_JABU_JABUS_BELLY_MQ_WONDER_BEFORE_BOSS_RIGHT_COW_2) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_1) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_2) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_WONDER_SHORTCUT_ROOM_3) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_HOOKSHOT) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_WONDER_BOSS_KEY_ROOM_BOW) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_WONDER_LIZALFOS_MAZE) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_1) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_LARGE_FACE_2) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_1) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_WONDER_EAST_TOWER_SMALL_FACE_2) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_WONDER_TORCH_ROOM) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_WONDER_FIRE_MAZE) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_WONDER_AFTER_FLARE_DANCER) +RANDO_ENUM_ITEM(RAND_INF_FIRE_TEMPLE_MQ_WONDER_STAIRCASE) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_LIZALFOS_ROOM) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_LONGSHOT_ROOM) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_STALFOS_ROOM) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_1) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_2) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_RIGHT_3) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_1) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_2) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_HOOKSHOT_STAIRCASE_LEFT_3) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_AFTER_DARK_LINK) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_LEFT_EYE) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_RIGHT_EYE) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_DRAGON_ROOM_PORTRAIT) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_TRIPLE_TORCHES) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_1) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_WATER_SPROUTS_2) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_FREESTANDING_ROOM) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_1) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_BEFORE_BOSS_2) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_UNDER_PILLAR_ROOM) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_LIZALFOS_HALLWAY) +RANDO_ENUM_ITEM(RAND_INF_WATER_TEMPLE_MQ_WONDER_GS_STORAGE_ROOM) +RANDO_ENUM_ITEM(RAND_INF_SPIRIT_TEMPLE_MQ_WONDER_CHEST_HAMMER) +RANDO_ENUM_ITEM(RAND_INF_SPIRIT_TEMPLE_MQ_WONDER_CHEST_SLASH) +RANDO_ENUM_ITEM(RAND_INF_SHADOW_TEMPLE_MQ_WONDER_THREE_POTS) +RANDO_ENUM_ITEM(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_1) +RANDO_ENUM_ITEM(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_2) +RANDO_ENUM_ITEM(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_3) +RANDO_ENUM_ITEM(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_LEFT_4) +RANDO_ENUM_ITEM(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_1) +RANDO_ENUM_ITEM(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_2) +RANDO_ENUM_ITEM(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_3) +RANDO_ENUM_ITEM(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_MAIN_ROOM_RIGHT_4) +RANDO_ENUM_ITEM(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_1) +RANDO_ENUM_ITEM(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_2) +RANDO_ENUM_ITEM(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_3) +RANDO_ENUM_ITEM(RAND_INF_BOTTOM_OF_THE_WELL_MQ_WONDER_SIDE_ROOM_4) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_WONDER_DINOLFOS_ROOM) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_WONDER_EYE_STATUE) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WONDER_SHADOW_TRIAL) +// End Wonder Items + RANDO_ENUM_ITEM(RAND_INF_SFM_FAIRY_GROTTO_FAIRY_1) RANDO_ENUM_ITEM(RAND_INF_SFM_FAIRY_GROTTO_FAIRY_2) RANDO_ENUM_ITEM(RAND_INF_SFM_FAIRY_GROTTO_FAIRY_3) @@ -1680,6 +1886,18 @@ RANDO_ENUM_ITEM(RAND_INF_LW_SHORTCUT_STORMS_FAIRY) RANDO_ENUM_ITEM(RAND_INF_TH_KITCHEN_SUN_FAIRY) RANDO_ENUM_ITEM(RAND_INF_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY) RANDO_ENUM_ITEM(RAND_INF_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY) + +RANDO_ENUM_ITEM(RAND_INF_HC_NEAR_WALL_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RAND_INF_HC_NEAR_STAIRS_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RAND_INF_HC_NEAR_BOULDER_PATH_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RAND_INF_HC_NEAR_ARCHWAY_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RAND_INF_LW_MEADOW_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RAND_INF_GY_NEAR_HUT_GRAVE_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RAND_INF_ZR_NEAR_ROCK_CIRCLE_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RAND_INF_ZR_WATERFALL_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RAND_INF_ZF_LOG_BUTTERFLY_FAIRY) +RANDO_ENUM_ITEM(RAND_INF_LH_SCARECROW_BUTTERFLY_FAIRY) + RANDO_ENUM_ITEM(RAND_INF_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY) RANDO_ENUM_ITEM(RAND_INF_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY) RANDO_ENUM_ITEM(RAND_INF_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY) @@ -2129,6 +2347,290 @@ RANDO_ENUM_ITEM(RAND_INF_OBTAINED_NAYRUS_LOVE) RANDO_ENUM_ITEM(RAND_INF_OBTAINED_ROCS_FEATHER) RANDO_ENUM_ITEM(RAND_INF_TALON_SENT_MALON_HOME) +// Overworld Signs +RANDO_ENUM_ITEM(RAND_INF_KF_DEKU_TREE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_STEPPING_STONES_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_LINKS_HOUSE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_FIRST_TRAINING_CENTER_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_SECOND_TRAINING_CENTER_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_AFTER_CRAWLSPACE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_CRAWL_RECTANGLE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_LOST_WOODS_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_HOUSE_OF_TWINS_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_SHOP_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_SARIAS_HOUSE_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_LOST_WOODS_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_MIDOS_HOUSE_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_TRAINING_CENTER_ENTRANCE_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_INNER_TRAINING_CENTER_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_KNOW_IT_ALL_BROTHERS_HOUSE_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_BOULDER_MAZE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KF_LINKS_HOUSE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_LW_THEATER_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_HF_CASTLE_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_HF_WOODED_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_HF_ROCKY_PATH_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_HF_FENCED_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_HF_CENTER_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_HF_RIVER_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_HF_STAIRS_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_MK_SHOOTING_GALLERY_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_MK_MASK_SHOP_SIGN) +RANDO_ENUM_ITEM(RAND_INF_TOT_ALTAR) +RANDO_ENUM_ITEM(RAND_INF_HC_DEAD_END_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KAK_GUARD_GATE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KAK_WELL_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KAK_SOUTHEAST_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KAK_FRONT_GATE_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_KAK_SHOOTING_GALLERY_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_GY_ENTRANCE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_GY_ENTRANCE_PLINTH) +RANDO_ENUM_ITEM(RAND_INF_GY_RIGHT_OF_ROYAL_TOMB_GRAVE) +RANDO_ENUM_ITEM(RAND_INF_GY_LEFT_OF_ROYAL_TOMB_GRAVE) +RANDO_ENUM_ITEM(RAND_INF_GY_ROYAL_TOMB_GRAVE) +RANDO_ENUM_ITEM(RAND_INF_DMT_ABOVE_DODONGO_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_DMT_ADULT_CENTER_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_DMT_CHILD_CENTER_EXIT_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_DMT_DODONGOS_CAVERN_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_DMT_CENTER_TRAIL_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_DMT_TO_UPPER_TRAIL_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_DMT_UPPER_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_DMT_TO_CENTER_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_DMT_LOWER_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_GC_CHILD_ROLLING_GORON_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_DMC_BRIDGE_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_ZR_SLEEPLESS_WATERFALL_PLAQUE) +RANDO_ENUM_ITEM(RAND_INF_ZD_SHOP_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_ZD_ENTRANCE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_ZD_KING_ZORA_PATH_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_ZD_NEAR_KING_ZORA_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_ZD_NEAR_KING_ZORA_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_ZF_JABU_JABU_PLATFORM_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_ZF_ENTRANCE_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_LH_LAB_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_LH_NORTH_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_LH_FISHING_SIGN) +RANDO_ENUM_ITEM(RAND_INF_LH_ISLAND_PEDESTAL) +RANDO_ENUM_ITEM(RAND_INF_LH_FISHING_POND_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_GV_BRIDGE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_GV_EAST_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_GF_EAST_EXIT_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_GF_HBA_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_GF_GATE_EXIT_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_GF_GTG_ENTRANCE_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_HW_CARPET_SALESMAN_ARROW_SIGN) +RANDO_ENUM_ITEM(RAND_INF_HW_POE_ALTAR) +// Dungeon Signs +RANDO_ENUM_ITEM(RAND_INF_DODONGOS_CAVERN_TOP_FLOOR_PEDESTAL) +RANDO_ENUM_ITEM(RAND_INF_SHADOW_TEMPLE_TRUTHSPINNER_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_SHADOW_TEMPLE_FALLING_SPIKES_RECTANGLE_SIGN) +RANDO_ENUM_ITEM(RAND_INF_SPIRIT_TEMPLE_LEFT_SNAKE_STATUE) +RANDO_ENUM_ITEM(RAND_INF_SPIRIT_TEMPLE_RIGHT_SNAKE_STATUE) +// MQ Dungeon Signs +RANDO_ENUM_ITEM(RAND_INF_SHADOW_TEMPLE_MQ_LOWER_PIT_RECTANGLE_SIGN) +// Beggar +RANDO_ENUM_ITEM(RAND_INF_MK_BEGGAR_BUGS) +RANDO_ENUM_ITEM(RAND_INF_MK_BEGGAR_FISH) +RANDO_ENUM_ITEM(RAND_INF_MK_BEGGAR_BLUE_FIRE) +RANDO_ENUM_ITEM(RAND_INF_KAK_BEGGAR_BUGS) +RANDO_ENUM_ITEM(RAND_INF_KAK_BEGGAR_FISH) +RANDO_ENUM_ITEM(RAND_INF_KAK_BEGGAR_BLUE_FIRE) + +// Vanilla Icicles +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_ENTRANCE_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_ENTRANCE_MIDDLE_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_ENTRANCE_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_ENTRANCE_STALACTITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_ENTRANCE_STALACTITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_LOBBY_STALACTITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_LOBBY_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_LOBBY_MIDDLE_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_LOBBY_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_AFTER_LOBBY_STALACTITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_AFTER_LOBBY_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_AFTER_LOBBY_CENTER_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_AFTER_LOBBY_CENTER_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_AFTER_LOBBY_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_SPINNING_BLADE_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_SPINNING_BLADE_MIDDLE_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_SPINNING_BLADE_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MAP_HALLWAY_STALACTITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MAP_HALLWAY_STALACTITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MAP_HALLWAY_STALACTITE_3) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MAP_HALLWAY_STALACTITE_4) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MAP_HALLWAY_STALACTITE_5) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MAP_HALLWAY_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MAP_HALLWAY_MIDDLE_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MAP_HALLWAY_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_3) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_4) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALACTITE_5) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALACTITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_3) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_4) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_5) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CENTER_STALAGMITE_6) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_LEFT_STALAGMITE_3) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_3) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_RIGHT_STALAGMITE_4) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_LEFT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_CENTER_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_RIGHT_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_PUSH_BLOCK_HALL_STALACTITE_3) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_NEAR_END_STALACTITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_NEAR_END_STALACTITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_NEAR_END_STALACTITE_3) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_NEAR_END_STALACTITE_4) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_3) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_4) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_5) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_6) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_7) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_NEAR_END_STALAGMITE_8) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_3) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_4) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_5) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_6) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_7) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_8) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_9) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALAGMITE_10) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_LEFT_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_RIGHT_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_1) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_2) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_3) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_4) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_5) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_6) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_7) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_8) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_9) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_10) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_STALACTITE_11) +// MQ Icicles +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_ENTRANCE_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_LOBBY_STALACTITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_LOBBY_STALACTITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_3) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_4) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_AFTER_LOBBY_STALAGMITE_5) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_HUB_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_HUB_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_HUB_STALAGMITE_3) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_HUB_STALAGMITE_4) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_HUB_STALAGMITE_5) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_LEFT_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_3) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_4) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_5) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_6) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_MAP_ROOM_CENTER_STALAGMITE_7) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_COMPASS_LEFT_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_COMPASS_RIGHT_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_BEFORE_SCARECROW_STALAGMITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_SCARECROW_ROOM_STALACTITE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_1) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_2) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_WEST_CORRIDOR_STALACTITE_3) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_3) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_4) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_STALAGMITE_5) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_1) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_2) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_3) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_RIGHT_STALACTITE_4) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_LEFT_STALACTITE) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_1) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_2) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM_TOP_RIGHT_STALACTITE_3) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_1) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_2) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALACTITE_3) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_1) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_2) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALACTITE_3) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_3) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_LEFT_STALAGMITE_4) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_1) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_2) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_3) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_RIGHT_STALAGMITE_4) +// Red Ice +RANDO_ENUM_ITEM(RAND_INF_ZD_KING_ZORA_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ZD_ZORA_SHOP_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_ENTRANCE_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_LOBBY_LEFT_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_LOBBY_RIGHT_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_SPINNING_BLADE_EAST_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_SPINNING_BLADE_WEST_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_FREESTANDING_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_HEART_PIECE_ROOM_CHEST_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MAP_ROOM_POT_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MAP_ROOM_CHEST_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_SILVER_RUPEE_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_NEAR_END_LEFT_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_NEAR_END_MIDDLE_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_NEAR_END_RIGHT_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_DOOR_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_WATER_TRIAL_RUSTED_SWITCH_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_HUB_WEST_LEFT_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_HUB_WEST_MIDDLE_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_HUB_WEST_RIGHT_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_HUB_LEDGE_LEFT_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_HUB_LEDGE_MIDDLE_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_HUB_LEDGE_RIGHT_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_COMPASS_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_MAP_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_SCARECROW_LEFT_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_SCARECROW_MIDDLE_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_ICE_CAVERN_MQ_SCARECROW_RIGHT_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_LEFT_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_RIGHT_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_BACK_LEFT_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_1) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_2) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_3) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_FIRST_ROOM_DOOR_RED_ICE_4) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SILVER_RUPEE_RED_ICE) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_1) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_2) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_3) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_4) +RANDO_ENUM_ITEM(RAND_INF_GANONS_CASTLE_MQ_WATER_TRIAL_SECOND_DOOR_RED_ICE_5) + RANDO_ENUM_ITEM(RAND_INF_MAX) RANDO_ENUM_END(RandomizerInf) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerMiscEnums.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerMiscEnums.h index 78b032c38c3..3079132fc61 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerMiscEnums.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerMiscEnums.h @@ -119,6 +119,7 @@ RANDO_ENUM_ITEM(RCTYPE_SMALL_CRATE) // Small crates RANDO_ENUM_ITEM(RCTYPE_TREE) // Trees RANDO_ENUM_ITEM(RCTYPE_NLTREE) // NL Trees RANDO_ENUM_ITEM(RCTYPE_BUSH) // Bushes +RANDO_ENUM_ITEM(RCTYPE_WONDER_ITEM) // Wonder Items RANDO_ENUM_ITEM(RCTYPE_DUNGEON_REWARD) // Dungeon rewards (blue warps) RANDO_ENUM_ITEM(RCTYPE_OCARINA) // Ocarina locations RANDO_ENUM_ITEM(RCTYPE_BEEHIVE) // Beehives @@ -128,7 +129,12 @@ RANDO_ENUM_ITEM(RCTYPE_FOUNTAIN_FAIRY) // Fairies in Fountains RANDO_ENUM_ITEM(RCTYPE_STONE_FAIRY) // Fairies from Gossip Stones RANDO_ENUM_ITEM(RCTYPE_BEAN_FAIRY) // Fairies from Beans RANDO_ENUM_ITEM(RCTYPE_SONG_FAIRY) // Fairies from Songs +RANDO_ENUM_ITEM(RCTYPE_BUTTERFLY_FAIRY) // Fairies from Butterflies RANDO_ENUM_ITEM(RCTYPE_GRASS) // Grass +RANDO_ENUM_ITEM(RCTYPE_SIGN) // Signs +RANDO_ENUM_ITEM(RCTYPE_BEGGAR) // Beggar +RANDO_ENUM_ITEM(RCTYPE_ICICLE) // Icicles +RANDO_ENUM_ITEM(RCTYPE_RED_ICE) // Red Ice RANDO_ENUM_END(RandomizerCheckType) RANDO_ENUM_BEGIN(RandomizerCheckQuest) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h index ac0c4dded70..b81225cb23f 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerOptions.h @@ -365,6 +365,14 @@ RANDO_ENUM_ITEM(RO_SHUFFLE_FREESTANDING_OVERWORLD) RANDO_ENUM_ITEM(RO_SHUFFLE_FREESTANDING_ALL) RANDO_ENUM_END(RandoOptionFreestanding) +// Wonder Items settings (off, dungeons, overworld, all) +RANDO_ENUM_BEGIN(RandoOptionWonderItems) +RANDO_ENUM_ITEM(RO_SHUFFLE_WONDER_ITEMS_OFF) +RANDO_ENUM_ITEM(RO_SHUFFLE_WONDER_ITEMS_DUNGEONS) +RANDO_ENUM_ITEM(RO_SHUFFLE_WONDER_ITEMS_OVERWORLD) +RANDO_ENUM_ITEM(RO_SHUFFLE_WONDER_ITEMS_ALL) +RANDO_ENUM_END(RandoOptionWonderItems) + // Shuffle Pots settings (off, dungeons, overworld, all) RANDO_ENUM_BEGIN(RandoOptionShufflePots) RANDO_ENUM_ITEM(RO_SHUFFLE_POTS_OFF) @@ -389,6 +397,14 @@ RANDO_ENUM_ITEM(RO_SHUFFLE_CRATES_OVERWORLD) RANDO_ENUM_ITEM(RO_SHUFFLE_CRATES_ALL) RANDO_ENUM_END(RandoOptionShuffleCrates) +// Shuffle Signs settings (off, dungeons, overworld, all) +RANDO_ENUM_BEGIN(RandoOptionShuffleSigns) +RANDO_ENUM_ITEM(RO_SHUFFLE_SIGNS_OFF) +RANDO_ENUM_ITEM(RO_SHUFFLE_SIGNS_DUNGEONS) +RANDO_ENUM_ITEM(RO_SHUFFLE_SIGNS_OVERWORLD) +RANDO_ENUM_ITEM(RO_SHUFFLE_SIGNS_ALL) +RANDO_ENUM_END(RandoOptionShuffleSigns) + // Link's Pocket Settings (dungeon reward, advancement, anything, nothing) RANDO_ENUM_BEGIN(RandoOptionLinksPocket) RANDO_ENUM_ITEM(RO_LINKS_POCKET_DUNGEON_REWARD) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerRegion.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerRegion.h index 6a60f3512a8..9cf279b1e1e 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerRegion.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerRegion.h @@ -114,7 +114,8 @@ RANDO_ENUM_ITEM(RR_TH_KITCHEN_MAIN) RANDO_ENUM_ITEM(RR_TH_KITCHEN_BY_CORRIDOR) RANDO_ENUM_ITEM(RR_TH_KITCHEN_OPPOSITE_CORRIDOR) RANDO_ENUM_ITEM(RR_TH_BREAK_ROOM) -RANDO_ENUM_ITEM(RR_TH_BREAK_ROOM_CORRIDOR) +RANDO_ENUM_ITEM(RR_TH_BREAK_ROOM_LOWER_CORRIDOR) +RANDO_ENUM_ITEM(RR_TH_BREAK_ROOM_UPPER_CORRIDOR) RANDO_ENUM_ITEM(RR_WASTELAND_NEAR_FORTRESS) RANDO_ENUM_ITEM(RR_HAUNTED_WASTELAND) RANDO_ENUM_ITEM(RR_WASTELAND_NEAR_COLOSSUS) @@ -710,7 +711,8 @@ RANDO_ENUM_ITEM(RR_WATER_TEMPLE_MQ_SCARECROW_CANAL) RANDO_ENUM_ITEM(RR_WATER_TEMPLE_MQ_CANAL_ALCOVE) RANDO_ENUM_ITEM(RR_WATER_TEMPLE_MQ_BEHIND_CANAL) RANDO_ENUM_ITEM(RR_WATER_TEMPLE_MQ_FREESTANDING_ROOM) -RANDO_ENUM_ITEM(RR_WATER_TEMPLE_MQ_3_JETS_ROOM) +RANDO_ENUM_ITEM(RR_WATER_TEMPLE_MQ_3_JETS_ROOM_SWITCH_SIDE) +RANDO_ENUM_ITEM(RR_WATER_TEMPLE_MQ_3_JETS_ROOM_NO_SWITCH_SIDE) RANDO_ENUM_ITEM(RR_WATER_TEMPLE_MQ_DODONGO_ROOM) RANDO_ENUM_ITEM(RR_WATER_TEMPLE_MQ_CRATE_VORTEX_CAGE) @@ -943,6 +945,7 @@ RANDO_ENUM_ITEM(RR_ICE_CAVERN_MAP_ROOM) RANDO_ENUM_ITEM(RR_ICE_CAVERN_COMPASS_ROOM) RANDO_ENUM_ITEM(RR_ICE_CAVERN_BLOCK_ROOM) RANDO_ENUM_ITEM(RR_ICE_CAVERN_BLOCK_ROOM_BLUE_FIRE) +RANDO_ENUM_ITEM(RR_ICE_CAVERN_AFTER_BLOCK_ROOM) RANDO_ENUM_ITEM(RR_ICE_CAVERN_BEFORE_FINAL_ROOM) RANDO_ENUM_ITEM(RR_ICE_CAVERN_FINAL_ROOM) RANDO_ENUM_ITEM(RR_ICE_CAVERN_FINAL_ROOM_UNDERWATER) @@ -985,6 +988,7 @@ RANDO_ENUM_ITEM(RR_GERUDO_TRAINING_GROUND_MQ_BOULDER_ROOM) RANDO_ENUM_ITEM(RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM) RANDO_ENUM_ITEM(RR_GERUDO_TRAINING_GROUND_MQ_BEHIND_BLOCK) RANDO_ENUM_ITEM(RR_GERUDO_TRAINING_GROUND_MQ_ROOM_BEHIND_BLOCK) +RANDO_ENUM_ITEM(RR_GERUDO_TRAINING_GROUND_MQ_STALFOS_ROOM_ALCOVE) RANDO_ENUM_ITEM(RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM_LEDGE) RANDO_ENUM_ITEM(RR_GERUDO_TRAINING_GROUND_MQ_MAGENTA_FIRE_ROOM) RANDO_ENUM_ITEM(RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM) @@ -1014,6 +1018,7 @@ RANDO_ENUM_ITEM(RR_GANONS_CASTLE_FIRE_TRIAL_FINAL_ROOM) RANDO_ENUM_ITEM(RR_GANONS_CASTLE_WATER_TRIAL_BLUE_FIRE_ROOM) RANDO_ENUM_ITEM(RR_GANONS_CASTLE_WATER_TRIAL_BLUE_FIRE_ROOM_END) RANDO_ENUM_ITEM(RR_GANONS_CASTLE_WATER_TRIAL_BLOCK_ROOM) +RANDO_ENUM_ITEM(RR_GANONS_CASTLE_WATER_TRIAL_BLOCK_ROOM_SWITCH) RANDO_ENUM_ITEM(RR_GANONS_CASTLE_WATER_TRIAL_BLOCK_ROOM_END) RANDO_ENUM_ITEM(RR_GANONS_CASTLE_WATER_TRIAL_FINAL_ROOM) RANDO_ENUM_ITEM(RR_GANONS_CASTLE_SHADOW_TRIAL_START) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h index 40ebb218de1..5e1dc80f30b 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerSettingKey.h @@ -160,6 +160,7 @@ RANDO_ENUM_ITEM(RSK_MERCHANT_PRICES_ADULT_WALLET_WEIGHT) RANDO_ENUM_ITEM(RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT) RANDO_ENUM_ITEM(RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT) RANDO_ENUM_ITEM(RSK_MERCHANT_PRICES_AFFORDABLE) +RANDO_ENUM_ITEM(RSK_SHUFFLE_BEGGAR) RANDO_ENUM_ITEM(RSK_BLUE_FIRE_ARROWS) RANDO_ENUM_ITEM(RSK_SUNLIGHT_ARROWS) RANDO_ENUM_ITEM(RSK_SLINGBOW_BREAK_BEEHIVES) @@ -237,13 +238,18 @@ RANDO_ENUM_ITEM(RSK_SKELETON_KEY) RANDO_ENUM_ITEM(RSK_SHUFFLE_DEKU_STICK_BAG) RANDO_ENUM_ITEM(RSK_SHUFFLE_DEKU_NUT_BAG) RANDO_ENUM_ITEM(RSK_SHUFFLE_FREESTANDING) +RANDO_ENUM_ITEM(RSK_SHUFFLE_WONDER_ITEMS) RANDO_ENUM_ITEM(RSK_SHUFFLE_FOUNTAIN_FAIRIES) RANDO_ENUM_ITEM(RSK_SHUFFLE_STONE_FAIRIES) RANDO_ENUM_ITEM(RSK_SHUFFLE_BEAN_FAIRIES) RANDO_ENUM_ITEM(RSK_SHUFFLE_SONG_FAIRIES) +RANDO_ENUM_ITEM(RSK_SHUFFLE_BUTTERFLY_FAIRIES) RANDO_ENUM_ITEM(RSK_LOCK_OVERWORLD_DOORS) RANDO_ENUM_ITEM(RSK_SHUFFLE_GRASS) +RANDO_ENUM_ITEM(RSK_SHUFFLE_SIGNS) RANDO_ENUM_ITEM(RSK_ROCS_FEATHER) +RANDO_ENUM_ITEM(RSK_SHUFFLE_ICICLES) +RANDO_ENUM_ITEM(RSK_SHUFFLE_RED_ICE) RANDO_ENUM_ITEM(RSK_MAX) RANDO_ENUM_END(RandomizerSettingKey) diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerTrick.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerTrick.h index 42ecc2bb92a..31026bec7c7 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerTrick.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerTrick.h @@ -29,9 +29,10 @@ RANDO_ENUM_ITEM(RT_BOMBCHU_BEEHIVES) RANDO_ENUM_ITEM(RT_HOOKSHOT_LADDERS) RANDO_ENUM_ITEM(RT_BLUE_FIRE_MUD_WALLS) RANDO_ENUM_ITEM(RT_OPEN_UNDERWATER_CHEST) -RANDO_ENUM_ITEM(RT_DISTANT_BOULDER_COLLISION) +RANDO_ENUM_ITEM(RT_BOULDER_COLLISION) RANDO_ENUM_ITEM(RT_ITEM_EXTENSION) RANDO_ENUM_ITEM(RT_SLIDE_JUMP) +RANDO_ENUM_ITEM(RT_VOIDOUT_COLLECTION) RANDO_ENUM_ITEM(RT_KF_ADULT_GS) // -- location tricks RANDO_ENUM_ITEM(RT_LW_BRIDGE) RANDO_ENUM_ITEM(RT_LW_MIDO_BACKFLIP) @@ -201,6 +202,7 @@ RANDO_ENUM_ITEM(RT_LENS_GTG) RANDO_ENUM_ITEM(RT_GTG_WITHOUT_HOOKSHOT) RANDO_ENUM_ITEM(RT_GTG_FAKE_WALL) RANDO_ENUM_ITEM(RT_GTG_LAVA_JUMP) +RANDO_ENUM_ITEM(RT_GTG_STATUE_JUMP) RANDO_ENUM_ITEM(RT_LENS_GTG_MQ) RANDO_ENUM_ITEM(RT_GTG_MQ_WITH_HOOKSHOT) RANDO_ENUM_ITEM(RT_GTG_MQ_WITHOUT_HOOKSHOT) diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp index 35aad1f383f..fb9ba56b919 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_objects.cpp @@ -140,6 +140,8 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() { (location.GetRCType() != RCTYPE_MERCHANT || CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleMerchants"), RO_SHUFFLE_MERCHANTS_OFF) != RO_SHUFFLE_MERCHANTS_OFF) && + (location.GetRCType() != RCTYPE_BEGGAR || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleBeggar"), RO_GENERIC_NO)) && (location.GetRCType() != RCTYPE_SONG_LOCATION || (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleSongs"), RO_SONG_SHUFFLE_SONG_LOCATIONS) != RO_SONG_SHUFFLE_SONG_LOCATIONS && @@ -197,6 +199,14 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() { CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleTrees"), RO_GENERIC_NO)) && (location.GetRCType() != RCTYPE_NLTREE || CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleTrees"), RO_GENERIC_NO)) && + (location.GetRCType() != RCTYPE_SIGN || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleSigns"), RO_GENERIC_NO)) && + (location.GetRCType() != RCTYPE_WONDER_ITEM || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleWonderItems"), RO_GENERIC_NO)) && + (location.GetRCType() != RCTYPE_ICICLE || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleIcicles"), RO_GENERIC_NO)) && + (location.GetRCType() != RCTYPE_RED_ICE || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleRedIce"), RO_GENERIC_NO)) && (location.GetRCType() != RCTYPE_FISH || ctx->GetFishsanity()->GetFishLocationIncluded(&location, FSO_SOURCE_CVARS)) && (location.GetRCType() != RCTYPE_ADULT_TRADE || @@ -219,6 +229,8 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() { CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleBeanFairies"), RO_GENERIC_NO)) && (location.GetRCType() != RCTYPE_SONG_FAIRY || CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleFairySpots"), RO_GENERIC_NO)) && + (location.GetRCType() != RCTYPE_BUTTERFLY_FAIRY || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleButterflyFairies"), RO_GENERIC_NO)) && ((location.GetRCType() != RCTYPE_MAP && location.GetRCType() != RCTYPE_COMPASS) || CVarGetInteger(CVAR_RANDOMIZER_SETTING("StartingMapsCompasses"), RO_DUNGEON_ITEM_LOC_OWN_DUNGEON) != RO_DUNGEON_ITEM_LOC_VANILLA) && @@ -230,7 +242,8 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() { RO_DUNGEON_ITEM_LOC_VANILLA) && (location.GetRCType() != RCTYPE_GANON_BOSS_KEY || CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA) != - RO_GANON_BOSS_KEY_VANILLA) && // vanilla ganon boss key + RO_GANON_BOSS_KEY_VANILLA || + CVarGetInteger(CVAR_RANDOMIZER_SETTING("TriforceHunt"), 0)) && (location.GetRandomizerCheck() != RC_TOT_LIGHT_ARROWS_CUTSCENE || (CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleGanonBossKey"), RO_GANON_BOSS_KEY_VANILLA) != RO_GANON_BOSS_KEY_LACS_DUNGEONS && diff --git a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp index 5c681d30762..e42a399bc7d 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_check_tracker.cpp @@ -79,11 +79,19 @@ bool showOverworldCrates; bool showDungeonCrates; bool showTrees; bool showBushes; +bool showOverworldSigns; +bool showDungeonSigns; +bool showOverworldWonderItems; +bool showDungeonWonderItems; +bool showBeggar; +bool showIcicles; +bool showRedIce; bool showFrogSongRupees; bool showFountainFairies; bool showStoneFairies; bool showBeanFairies; bool showSongFairies; +bool showButterflyFairies; bool showStartingMapsCompasses; bool showKeysanity; bool showGerudoFortressKeys; @@ -1361,6 +1369,10 @@ void LoadSettings() { showSongFairies = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SONG_FAIRIES) == RO_GENERIC_YES : false; + showButterflyFairies = + IS_RANDO + ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BUTTERFLY_FAIRIES) == RO_GENERIC_YES + : false; showStartingMapsCompasses = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue( RSK_SHUFFLE_MAPANDCOMPASS) != RO_DUNGEON_ITEM_LOC_VANILLA : false; @@ -1463,8 +1475,49 @@ void LoadSettings() { showDungeonCrates = false; break; } + + switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SIGNS)) { + case RO_SHUFFLE_SIGNS_ALL: + showOverworldSigns = true; + showDungeonSigns = true; + break; + case RO_SHUFFLE_SIGNS_OVERWORLD: + showOverworldSigns = true; + showDungeonSigns = false; + break; + case RO_SHUFFLE_SIGNS_DUNGEONS: + showOverworldSigns = false; + showDungeonSigns = true; + break; + default: + showOverworldSigns = false; + showDungeonSigns = false; + break; + } showTrees = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_TREES); showBushes = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BUSHES); + + switch (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_WONDER_ITEMS)) { + case RO_SHUFFLE_WONDER_ITEMS_ALL: + showOverworldWonderItems = true; + showDungeonWonderItems = true; + break; + case RO_SHUFFLE_WONDER_ITEMS_OVERWORLD: + showOverworldWonderItems = true; + showDungeonWonderItems = false; + break; + case RO_SHUFFLE_WONDER_ITEMS_DUNGEONS: + showOverworldWonderItems = false; + showDungeonWonderItems = true; + break; + default: + showOverworldWonderItems = false; + showDungeonWonderItems = false; + break; + } + showBeggar = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BEGGAR); + showIcicles = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_ICICLES); + showRedIce = OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_RED_ICE); } else { // Vanilla showOverworldTokens = true; showDungeonTokens = true; @@ -1474,8 +1527,15 @@ void LoadSettings() { showDungeonGrass = false; showOverworldCrates = false; showDungeonCrates = false; + showOverworldSigns = false; + showDungeonSigns = false; showTrees = false; showBushes = false; + showOverworldWonderItems = false; + showDungeonWonderItems = false; + showBeggar = false; + showIcicles = false; + showRedIce = false; } fortressFast = false; @@ -1566,6 +1626,7 @@ bool IsCheckShuffled(RandomizerCheck rc) { (showMajorScrubs && (rc == RC_LW_DEKU_SCRUB_NEAR_BRIDGE || // The 3 scrubs that are always randomized rc == RC_HF_DEKU_SCRUB_GROTTO || rc == RC_LW_DEKU_SCRUB_GROTTO_FRONT))) && (loc->GetRCType() != RCTYPE_MERCHANT || showMerchants) && + (loc->GetRCType() != RCTYPE_BEGGAR || showBeggar) && (loc->GetRCType() != RCTYPE_SONG_LOCATION || showSongs) && (loc->GetRCType() != RCTYPE_BEEHIVE || showBeehives) && (loc->GetRCType() != RCTYPE_OCARINA || showOcarinas) && @@ -1593,6 +1654,14 @@ bool IsCheckShuffled(RandomizerCheck rc) { (showTrees && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_LOGIC_RULES) == RO_LOGIC_NO_LOGIC)) && (loc->GetRCType() != RCTYPE_BUSH || showBushes) && (loc->GetRCType() != RCTYPE_COW || showCows) && + (loc->GetRCType() != RCTYPE_SIGN || + (showOverworldSigns && RandomizerCheckObjects::AreaIsOverworld(loc->GetArea())) || + (showDungeonSigns && RandomizerCheckObjects::AreaIsDungeon(loc->GetArea()))) && + (loc->GetRCType() != RCTYPE_WONDER_ITEM || + (showOverworldWonderItems && RandomizerCheckObjects::AreaIsOverworld(loc->GetArea())) || + (showDungeonWonderItems && RandomizerCheckObjects::AreaIsDungeon(loc->GetArea()))) && + (loc->GetRCType() != RCTYPE_ICICLE || showIcicles) && + (loc->GetRCType() != RCTYPE_RED_ICE || showRedIce) && (loc->GetRCType() != RCTYPE_FISH || OTRGlobals::Instance->gRandoContext->GetFishsanity()->GetFishLocationIncluded(loc)) && (loc->GetRCType() != RCTYPE_FREESTANDING || @@ -1611,6 +1680,7 @@ bool IsCheckShuffled(RandomizerCheck rc) { (loc->GetRCType() != RCTYPE_STONE_FAIRY || showStoneFairies) && (loc->GetRCType() != RCTYPE_BEAN_FAIRY || showBeanFairies) && (loc->GetRCType() != RCTYPE_SONG_FAIRY || showSongFairies) && + (loc->GetRCType() != RCTYPE_BUTTERFLY_FAIRY || showButterflyFairies) && (loc->GetRCType() != RCTYPE_SMALL_KEY || showKeysanity) && (loc->GetRCType() != RCTYPE_BOSS_KEY || showBossKeysanity) && (loc->GetRCType() != RCTYPE_GANON_BOSS_KEY || showGanonBossKey) && diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 35a37451b94..3b69fd14ded 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -2,12 +2,14 @@ #include "soh/Enhancements/randomizer/randomizerTypes.h" #include "trial.h" #include "dungeon.h" +#include "3drando/random.hpp" #include "soh/OTRGlobals.h" #include #include +#include namespace Rando { std::shared_ptr Settings::mInstance; @@ -242,11 +244,6 @@ void Settings::CreateOptions() { OPT_U8(RSK_RAINBOW_BRIDGE_REWARD_COUNT, "Bridge Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("RewardCount"), "", WIDGET_CVAR_SLIDER_INT, 9, true); OPT_U8(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT, "Bridge Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("DungeonCount"), "", WIDGET_CVAR_SLIDER_INT, 8, true); OPT_U8(RSK_RAINBOW_BRIDGE_TOKEN_COUNT, "Bridge Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TokenCount"), "", WIDGET_CVAR_SLIDER_INT, 100, true); - OPT_U8(RSK_RAINBOW_BRIDGE_STONE_COUNT, "Bridge Stone Count", {NumOpts(0, 4)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StoneCount"), "", WIDGET_CVAR_SLIDER_INT, 3, true); - OPT_U8(RSK_RAINBOW_BRIDGE_MEDALLION_COUNT, "Bridge Medallion Count", {NumOpts(0, 7)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MedallionCount"), "", WIDGET_CVAR_SLIDER_INT, 6, true); - OPT_U8(RSK_RAINBOW_BRIDGE_REWARD_COUNT, "Bridge Reward Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("RewardCount"), "", WIDGET_CVAR_SLIDER_INT, 9, true); - OPT_U8(RSK_RAINBOW_BRIDGE_DUNGEON_COUNT, "Bridge Dungeon Count", {NumOpts(0, 9)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("DungeonCount"), "", WIDGET_CVAR_SLIDER_INT, 8, true); - OPT_U8(RSK_RAINBOW_BRIDGE_TOKEN_COUNT, "Bridge Token Count", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("TokenCount"), "", WIDGET_CVAR_SLIDER_INT, 100, true); OPT_U8(RSK_BRIDGE_OPTIONS, "Bridge Reward Options", {"Standard Rewards", "Greg as Reward", "Greg as Wildcard"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BridgeRewardOptions"), mOptionDescriptions[RSK_BRIDGE_OPTIONS], WIDGET_CVAR_COMBOBOX, RO_BRIDGE_STANDARD_REWARD, false, nullptr, IMFLAG_NONE); OPT_CALLBACK(RSK_BRIDGE_OPTIONS, { const uint8_t bridgeOpt = CVarGetInteger(CVAR_RANDOMIZER_SETTING("BridgeRewardOptions"), RO_BRIDGE_STANDARD_REWARD); @@ -844,6 +841,9 @@ void Settings::CreateOptions() { OPT_U8(RSK_SHUFFLE_CRATES, "Shuffle Crates", {"Off", "Dungeons", "Overworld", "All Crates"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleCrates"), mOptionDescriptions[RSK_SHUFFLE_CRATES], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_CRATES_OFF); OPT_BOOL(RSK_SHUFFLE_TREES, "Shuffle Trees", CVAR_RANDOMIZER_SETTING("ShuffleTrees"), mOptionDescriptions[RSK_SHUFFLE_TREES]); OPT_BOOL(RSK_SHUFFLE_BUSHES, "Shuffle Bushes", CVAR_RANDOMIZER_SETTING("ShuffleBushes"), mOptionDescriptions[RSK_SHUFFLE_BUSHES]); + OPT_BOOL(RSK_SHUFFLE_ICICLES, "Shuffle Icicles", CVAR_RANDOMIZER_SETTING("ShuffleIcicles"), mOptionDescriptions[RSK_SHUFFLE_ICICLES]); + OPT_BOOL(RSK_SHUFFLE_RED_ICE, "Shuffle Red Ice", CVAR_RANDOMIZER_SETTING("ShuffleRedIce"), mOptionDescriptions[RSK_SHUFFLE_RED_ICE]); + OPT_U8(RSK_SHUFFLE_SIGNS, "Shuffle Signs", {"Off", "Dungeons", "Overworld", "All Signs"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleSigns"), mOptionDescriptions[RSK_SHUFFLE_SIGNS], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_SIGNS_OFF); OPT_BOOL(RSK_SHUFFLE_FISHING_POLE, "Shuffle Fishing Pole", CVAR_RANDOMIZER_SETTING("ShuffleFishingPole"), mOptionDescriptions[RSK_SHUFFLE_FISHING_POLE]); OPT_CALLBACK(RSK_SHUFFLE_FISHING_POLE, { // Disable fishing pole hint if the fishing pole is not shuffled @@ -1011,6 +1011,7 @@ void Settings::CreateOptions() { OPT_U8(RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT, "Merchant Giant Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantGiantWalletWeight"), mOptionDescriptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); OPT_U8(RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT, "Merchant Tycoon Wallet Weight", {NumOpts(0, 100)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("MerchantTycoonWalletWeight"), mOptionDescriptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT], WIDGET_CVAR_SLIDER_INT, 10, true, nullptr, IMFLAG_NONE); OPT_BOOL(RSK_MERCHANT_PRICES_AFFORDABLE, "Merchant Affordable Prices", CVAR_RANDOMIZER_SETTING("MerchantPricesAffordable"), mOptionDescriptions[RSK_MERCHANT_PRICES_AFFORDABLE]); + OPT_BOOL(RSK_SHUFFLE_BEGGAR, "Shuffle Beggar", CVAR_RANDOMIZER_SETTING("ShuffleBeggar"), mOptionDescriptions[RSK_SHUFFLE_BEGGAR]); OPT_BOOL(RSK_SHUFFLE_FROG_SONG_RUPEES, "Shuffle Frog Song Rupees", CVAR_RANDOMIZER_SETTING("ShuffleFrogSongRupees"), mOptionDescriptions[RSK_SHUFFLE_FROG_SONG_RUPEES]); OPT_BOOL(RSK_SHUFFLE_ADULT_TRADE, "Shuffle Adult Trade", CVAR_RANDOMIZER_SETTING("ShuffleAdultTrade"), mOptionDescriptions[RSK_SHUFFLE_ADULT_TRADE]); OPT_U8(RSK_SHUFFLE_CHEST_MINIGAME, "Shuffle Chest Minigame", {"Off", "On (Separate)", "On (Pack)"}); @@ -1041,6 +1042,7 @@ void Settings::CreateOptions() { } }); OPT_U8(RSK_SHUFFLE_FREESTANDING, "Shuffle Freestanding Items", {"Off", "Dungeons", "Overworld", "All Items"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleFreestanding"), mOptionDescriptions[RSK_SHUFFLE_FREESTANDING], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_FREESTANDING_OFF); + OPT_U8(RSK_SHUFFLE_WONDER_ITEMS, "Shuffle Wonder Items", {"Off", "Dungeons", "Overworld", "All Items"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleWonderItems"), mOptionDescriptions[RSK_SHUFFLE_WONDER_ITEMS], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_WONDER_ITEMS_OFF); OPT_U8(RSK_FISHSANITY, "Fishsanity", {"Off", "Shuffle only Hyrule Loach", "Shuffle Fishing Pond", "Shuffle Overworld Fish", "Shuffle Both"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("Fishsanity"), mOptionDescriptions[RSK_FISHSANITY], WIDGET_CVAR_COMBOBOX, RO_FISHSANITY_OFF); OPT_CALLBACK(RSK_FISHSANITY, { // Hide fishing pond settings if we aren't shuffling the fishing pond @@ -1068,6 +1070,7 @@ void Settings::CreateOptions() { OPT_BOOL(RSK_SHUFFLE_STONE_FAIRIES, "Shuffle Gossip Stone Fairies", CVAR_RANDOMIZER_SETTING("ShuffleStoneFairies"), mOptionDescriptions[RSK_SHUFFLE_STONE_FAIRIES]); OPT_BOOL(RSK_SHUFFLE_BEAN_FAIRIES, "Shuffle Bean Fairies", CVAR_RANDOMIZER_SETTING("ShuffleBeanFairies"), mOptionDescriptions[RSK_SHUFFLE_BEAN_FAIRIES]); OPT_BOOL(RSK_SHUFFLE_SONG_FAIRIES, "Shuffle Fairy Spots", CVAR_RANDOMIZER_SETTING("ShuffleFairySpots"), mOptionDescriptions[RSK_SHUFFLE_SONG_FAIRIES]); + OPT_BOOL(RSK_SHUFFLE_BUTTERFLY_FAIRIES, "Shuffle Butterfly Fairies", CVAR_RANDOMIZER_SETTING("ShuffleButterflyFairies"), mOptionDescriptions[RSK_SHUFFLE_BUTTERFLY_FAIRIES]); OPT_U8(RSK_SHUFFLE_MAPANDCOMPASS, "Maps/Compasses", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingMapsCompasses"), mOptionDescriptions[RSK_SHUFFLE_MAPANDCOMPASS], WIDGET_CVAR_COMBOBOX, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON); OPT_U8(RSK_KEYSANITY, "Small Key Shuffle", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("Keysanity"), mOptionDescriptions[RSK_KEYSANITY], WIDGET_CVAR_COMBOBOX, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON); OPT_U8(RSK_GERUDO_KEYS, "Gerudo Fortress Keys", {"Vanilla", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GerudoKeys"), mOptionDescriptions[RSK_GERUDO_KEYS], WIDGET_CVAR_COMBOBOX, RO_GERUDO_KEYS_VANILLA); @@ -1369,34 +1372,7 @@ void Settings::CreateOptions() { mExcludeLocationsOptionsAreas.reserve(RCAREA_INVALID); - // the following are glitches and are currently disabled - // OPT_TRICK(RT_ACUTE_ANGLE_CLIP, RCQUEST_BOTH, RA_NONE, {Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, - // Tricks::Tag::GLITCH}, "Acute angle clip", "Enables locations requiring jumpslash clips through walls which meet - // at an acute angle."); OPT_TRICK(RT_ADVANCED_CLIPS, RCQUEST_BOTH, RA_NONE, {Tricks::Tag::ADVANCED, - // Tricks::Tag::EXPERIMENTAL, Tricks::Tag::GLITCH}, "Advanced clips", "Enables locations requiring clips through - // walls and objects requiring precise jumps or other tricks."); OPT_TRICK(RT_BLANK_A, RCQUEST_BOTH, RA_NONE, - // {Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, Tricks::Tag::GLITCH}, "Blank A", "Enables locations requiring - // blank A button; NOTE: this requires the 'Quick Putaway' restoration."); OPT_TRICK(RT_DOOM_JUMP, RCQUEST_BOTH, - // RA_NONE, {Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, Tricks::Tag::GLITCH}, "Doom Jump", "Enables locations - // requiring doom jumps."); OPT_TRICK(RT_EPG, RCQUEST_BOTH, RA_NONE, {Tricks::Tag::ADVANCED, - // Tricks::Tag::EXPERIMENTAL, Tricks::Tag::GLITCH}, "EPG", "Enables locations requiring use of the Entrance Point - // Glitch."); OPT_TRICK(RT_EQUIP_SWAP, RCQUEST_BOTH, RA_NONE, {Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, - // Tricks::Tag::GLITCH}, "Equip Swap", "Enables locations requiring use of equip swap; NOTE: this may expect the - // 'Allow cursor to be over any slot' enhancement to be turned off."); OPT_TRICK(RT_EQUIP_SWAP_EXPECTS_DINS, - // RCQUEST_BOTH, RA_NONE, {Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, Tricks::Tag::GLITCH}, "Equip Swap - // Require's Din's Fire", "Enables locations requiring use of equip swap once Din's Fire is found."); - // OPT_TRICK(RT_FLAME_STORAGE, RCQUEST_BOTH, RA_NONE, {Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, - // Tricks::Tag::GLITCH}, "Flame Storage", "Enables locations requiring flame storage."); OPT_TRICK(RT_GROUND_CLIP, - // RCQUEST_BOTH, RA_NONE, {Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, Tricks::Tag::GLITCH}, "Ground Clip", - // "Enables locations requiring ground clips."); OPT_TRICK(RT_HESS, RCQUEST_BOTH, RA_NONE, {Tricks::Tag::ADVANCED, - // Tricks::Tag::EXPERIMENTAL, Tricks::Tag::GLITCH}, "HESS", "Enables locations requiring a Hyper Extended Super - // Slide."); OPT_TRICK(RT_HOOKSHOT_CLIP, RCQUEST_BOTH, RA_NONE, {Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, - // Tricks::Tag::GLITCH}, "Hookshot Clip", "Enables locations requiring Hookshot clips."); - // OPT_TRICK(RT_HOOKSHOT_JUMP, RCQUEST_BOTH, RA_NONE, {Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, - // Tricks::Tag::GLITCH}, "Hookshot Jump", "Enables locations requiring Hookshot jumps."); OPT_TRICK(RT_ISG, - // RCQUEST_BOTH, RA_NONE, {Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, Tricks::Tag::GLITCH}, "ISG", "Enables - // locations requiring use of the infinite sword glitch."); - + // RANDOTODO sweep trick descriptions and make sure they match a post-refactor, post shuffles reality /* Common abbreviations in name tags - A: Adult - Blk: Block @@ -1423,857 +1399,291 @@ void Settings::CreateOptions() { Try to keep Name Tags less than 8 chars. */ - OPT_TRICK(RT_VISIBLE_COLLISION, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "Ignore Visible Collision", - "VisCol", - "Allows ignoring visible barriers and objects that do not actually stop Link from walking, climbing or " - "hitting things, without clipping.\n\n" - "This notably allows for walking through Kak's gate backwards, climb into the back of Impa's house as " - "Adult from the coop," - "and hitting Rusted Switches or boulders through Blocks, Ice and Walls.\n\n" - "This trick only applies to case where doing the thing is trivial, instances where it only works from " - "certain angles are not part of this trick," - "niether are any form of clips, including clipping Link's hitbox inside boulders with jumpslash."); - OPT_TRICK(RT_GROTTOS_WITHOUT_AGONY, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, - "Hidden Grottos without Stone of Agony", "NoSoA", - "Allows entering hidden grottos without the Stone of Agony."); - OPT_TRICK( - RT_FEWER_TUNIC_REQUIREMENTS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::INTERMEDIATE }, "Fewer Tunic Requirements", - "FTR", - "Normally the only hot area logic expects you to navigate without a Goron Tunic is Death Mountain Crater\n" - "and you are not expected to navigate underwater sections without a Zora Tunic.\n\n" - "With this trick you are expected to do any underwater area except Central Pillar,\n" - "any hot area except Volvagia and the Block lift room in Fire Temple\n" - "and the health needed to logically navigate Crater is decreased."); - OPT_TRICK(RT_UNINTUITIVE_JUMPS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "Unintuitive Jumps", "UnJmp", - "Many ledges can be overcome with particular jumps which are simple to execute without items.\n" - "This includes jumping from heights to dive deeper without scales,\n" - "though this trick doesn't cover Water Temple's Dragon Room."); - OPT_TRICK(RT_FIRE_RINGS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::INTERMEDIATE }, "Fire Ring", "FlaChst", - "Fire Rings can be run into while Link is invincible from having taken damage," - "letting you interact with some objects inside them such as large chests"); + // the following are glitches and are currently disabled + + // OPT_TRICK(RT_ACUTE_ANGLE_CLIP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, + // Tricks::Tag::GLITCH }); + + // OPT_TRICK(RT_ADVANCED_CLIPS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, + // Tricks::Tag::GLITCH }); + + // OPT_TRICK(RT_BLANK_A, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, + // Tricks::Tag::GLITCH }); + + // OPT_TRICK(RT_DOOM_JUMP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, + // Tricks::Tag::GLITCH }); + + // OPT_TRICK(RT_EPG, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, + // Tricks::Tag::GLITCH}); + + // OPT_TRICK(RT_EQUIP_SWAP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, + // Tricks::Tag::GLITCH }); + + // OPT_TRICK(RT_EQUIP_SWAP_EXPECTS_DINS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, + // Tricks::Tag::GLITCH }); + + // OPT_TRICK(RT_FLAME_STORAGE, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, + // Tricks::Tag::GLITCH }); + + // OPT_TRICK(RT_GROUND_CLIP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, + // Tricks::Tag::GLITCH }); + + // OPT_TRICK(RT_HESS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, + // Tricks::Tag::GLITCH}); + + // OPT_TRICK(RT_HOOKSHOT_CLIP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, + // Tricks::Tag::GLITCH }); + + // OPT_TRICK(RT_HOOKSHOT_JUMP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, + // Tricks::Tag::GLITCH }); + + // OPT_TRICK(RT_ISG, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL, + // Tricks::Tag::GLITCH}); + + OPT_TRICK(RT_VISIBLE_COLLISION, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "VisCol"); + OPT_TRICK(RT_GROTTOS_WITHOUT_AGONY, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "NoSoA"); + OPT_TRICK(RT_FEWER_TUNIC_REQUIREMENTS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::INTERMEDIATE }, "FTR"); + OPT_TRICK(RT_UNINTUITIVE_JUMPS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "UnJmp"); + OPT_TRICK(RT_FIRE_RINGS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::INTERMEDIATE }, "FlaChst"); + // disabled for now, can't check for being able to use bunny hood & bunny hood speedup is currently completely - // decoupled from rando OPT_TRICK(RT_BUNNY_HOOD_JUMPS, RCQUEST_BOTH, RA_NONE, {Tricks::Tag::ADVANCED}, "Bunny Hood - // Jumps", "Allows reaching locations using Bunny Hood's extended jumps."); + // decoupled from rando + + // OPT_TRICK(RT_BUNNY_HOOD_JUMPS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED }); + OPT_TRICK(RT_DAMAGE_BOOST_SIMPLE, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::EXPERIMENTAL }, - "Simple damage boosts", "SDmgBoo", - "Allows damage boosts in order to reach further locations. Can be combined with \"Simple hover boosts\" " - "for reaching far distances."); - OPT_TRICK(RT_HOVER_BOOST_SIMPLE, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::GLITCH }, - "Simple hover boosts", "SHovBoo", - "Allows equipping of hover boots when Link is moving at high speeds to extend distance covered, often " - "after recoil. Can be combined with \"Simple damage boosts\" for greater uses."); - OPT_TRICK(RT_BOMBCHU_BEEHIVES, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "Bombchu Beehives", "ChuBee", - "Allows exploding beehives with Bombchus."); - OPT_TRICK(RT_HOOKSHOT_LADDERS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "Hookshot Ladders", "HSLad", - "You can skip climb for hookshottable ladders can be skipped by hookshotting the top of the ladder from " - "the correct distance and angle.\n" - "This is more difficult for some ladders than others, and a few are not possible.\n" - "Hookshotting climbable walls in the same way is not a trick, as it is trivial to get an angle that " - "correctly ledge grabs."); - OPT_TRICK(RT_BLUE_FIRE_MUD_WALLS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "Blue Fire Beyond Red Ice", - "BluFire", - "Use Blue Fire to break mud walls, detonate bomb flowers, and break floor to King Dodongo.\nDoes not " - "apply to MQ Dead Hand bomb flowers.\nUsing blue fire on bombflower to stop rolling goron also requires " - "\"Stop Link the Goron with Din's Fire\".\nUsing blue fire arrows to break floor in King Dodongo's " - "chamber also requires \"Dodongo\'s Cavern Smash the Boss Lobby Floor\"."); - OPT_TRICK(RT_OPEN_UNDERWATER_CHEST, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, - "Open Underwater Chests", "OpenUC", - "Underwater chests can be opened by wearing iron boots and hookshotting the chest."); - OPT_TRICK(RT_DISTANT_BOULDER_COLLISION, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, - "Distant Boulder Collision", "BolCol", - "From afar boulder collision is disabled, allowing projectiles to pass through them."); - OPT_TRICK(RT_ITEM_EXTENSION, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::INTERMEDIATE }, "Item Extension", "HSExt", - "Slightly extends the range of projectiles such as Hookshot, Bow or Slingshot. Also allows clipping " - "projectile past collision. Used for:\n" - "- Crossing Gerudo Valley with Hookshot\n" - "- Retrieving DMT Gold Skulltula beside bomb flower\n" - "- Hitting switch through wall in Spirit Temple's big mirror room with Bow, Slingshot, or Hookshot\n" - "- Hitting switch through wall in Spirit Trial with Bow or Slingshot\n" - "- Hitting switch through gate in Shadow Temple MQ with Bow or Slingshot"); + "SDmgBoo"); + OPT_TRICK(RT_HOVER_BOOST_SIMPLE, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::ADVANCED, Tricks::Tag::GLITCH }, "SHovBoo"); + OPT_TRICK(RT_BOMBCHU_BEEHIVES, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "ChuBee"); + OPT_TRICK(RT_HOOKSHOT_LADDERS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "HSLad"); + OPT_TRICK(RT_BLUE_FIRE_MUD_WALLS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "BluFire"); + OPT_TRICK(RT_OPEN_UNDERWATER_CHEST, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, "OpenUC"); + OPT_TRICK(RT_BOULDER_COLLISION, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, "BolCol"); + OPT_TRICK(RT_ITEM_EXTENSION, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::INTERMEDIATE }, "HSExt"); OPT_TRICK(RT_BIG_SKULLTULA_PAUSE_LIFT, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, - "Lift Big Skulltulas with Pausing", "SkulPaus", - "Pausing while a big skulltula is bobbing upwards slightly lifts it,\n" - "eventually allowing passage without any items."); - OPT_TRICK(RT_GROUND_JUMP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, "Ground Jump", - "GrdJmp", "Enables requiring ground jumps."); + "SkulPaus"); + OPT_TRICK(RT_GROUND_JUMP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, "GrdJmp"); OPT_TRICK(RT_GROUND_JUMP_HARD, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::INTERMEDIATE, Tricks::Tag::GLITCH }, - "Hard Ground Jumps", "HGrdJmp", - "Enables ground jumps which require some precision outside of setting up jump,\n" - "such as needing extra height with jumpslash or jumping while running with hover boots."); - OPT_TRICK(RT_SLIDE_JUMP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "Sliding Jumps", "SldJmp", - "Running forward while sliding sideways on ice can be used to jump on platforms."); - OPT_TRICK(RT_KF_ADULT_GS, RCQUEST_BOTH, RA_KOKIRI_FOREST, { Tricks::Tag::NOVICE }, - "Adult Kokiri Forest GS with Hover Boots", "KFGSHB", - "Can be obtained without Hookshot by using the Hover Boots off of one of the roots."); - OPT_TRICK(RT_LW_BRIDGE, RCQUEST_BOTH, RA_THE_LOST_WOODS, { Tricks::Tag::EXPERT }, - "Jump onto the Lost Woods Bridge as Adult with Nothing", "LWBrgJmp", - "With very precise movement it's possible for adult to jump onto the bridge without needing Longshot, " - "Hover Boots, or Bean."); - OPT_TRICK(RT_LW_MIDO_BACKFLIP, RCQUEST_BOTH, RA_THE_LOST_WOODS, { Tricks::Tag::NOVICE }, - "Backflip over Mido as Adult", "MidoSkip", - "With a specific position and angle, you can backflip over Mido."); + "HGrdJmp"); + OPT_TRICK(RT_SLIDE_JUMP, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "SldJmp"); + OPT_TRICK(RT_VOIDOUT_COLLECTION, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "VdCl"); + OPT_TRICK(RT_KF_ADULT_GS, RCQUEST_BOTH, RA_KOKIRI_FOREST, { Tricks::Tag::NOVICE }, "KFGSHB"); + OPT_TRICK(RT_LW_BRIDGE, RCQUEST_BOTH, RA_THE_LOST_WOODS, { Tricks::Tag::EXPERT }, "LWBrgJmp"); + OPT_TRICK(RT_LW_MIDO_BACKFLIP, RCQUEST_BOTH, RA_THE_LOST_WOODS, { Tricks::Tag::NOVICE }, "MidoSkip"); OPT_TRICK(RT_LOST_WOOD_NAVI_DIVE, RCQUEST_BOTH, RA_THE_LOST_WOODS, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, - "Lost Woods Navi dive", "LWNaviD", - "You need Deku Sticks or Kokiri Sword to dive with Navi for entering Zora's River."); - OPT_TRICK(RT_LW_GS_BEAN, RCQUEST_BOTH, RA_THE_LOST_WOODS, { Tricks::Tag::INTERMEDIATE }, - "Lost Woods Adult GS without Bean", "LWGSHS", - "You can collect the token with a precise Hookshot use, as long as you can kill the Skulltula somehow " - "first. It can be killed using Longshot, Bow, Bombchus or Din's Fire."); - OPT_TRICK(RT_HC_STORMS_GS, RCQUEST_BOTH, RA_HYRULE_CASTLE, { Tricks::Tag::INTERMEDIATE }, - "Hyrule Castle Storms Grotto GS with Just Boomerang", "HCGrGSRng", - "With precise throws, the Boomerang alone can kill the Skulltula and collect the token, without first " - "needing to blow up the wall."); - OPT_TRICK(RT_HF_BIG_POE_WITHOUT_EPONA, RCQUEST_BOTH, RA_HYRULE_FIELD, { Tricks::Tag::NOVICE }, - "Big Poe without Epona", "PoeDiff", - "Big Poes have a chance of appearing without Epona, you can shoot them quickly with only bow."); - OPT_TRICK(RT_KAK_TOWER_GS, RCQUEST_BOTH, RA_KAKARIKO_VILLAGE, { Tricks::Tag::INTERMEDIATE }, - "Kakariko Tower GS with Jumpslash", "KakGSJS", - "Climb the tower as high as you can without touching the Gold Skulltula, then let go and jumpslash " - "immediately. By jump-slashing from as low on the ladder as possible to still hit the Skulltula, this " - "trick can be done without taking fall damage."); - OPT_TRICK(RT_KAK_CHILD_WINDMILL_POH, RCQUEST_BOTH, RA_KAKARIKO_VILLAGE, { Tricks::Tag::EXTREME }, - "Windmill PoH as Child with Precise Jumpslash", "WndCJS", - "Can jump up to the spinning platform from below as child with a precise jumpslash timed with the " - "platforms rotation."); - OPT_TRICK( - RT_KAK_ROOFTOP_GS, RCQUEST_BOTH, RA_KAKARIKO_VILLAGE, { Tricks::Tag::ADVANCED }, - "Kakariko Rooftop GS with Hover Boots", "KakGSHB", - "Take the Hover Boots from the entrance to Impa's House over to the rooftop of Skulltula House. From there, a " - "precise Hover Boots backwalk with backflip can be used to get onto a hill above the side of the village. And " - "then from there you can Hover onto Impa's rooftop to kill the Skulltula and backflip into the token."); - OPT_TRICK(RT_GY_POH, RCQUEST_BOTH, RA_THE_GRAVEYARD, { Tricks::Tag::INTERMEDIATE }, - "Graveyard Freestanding PoH with Boomerang", "GYPoHRng", - "Using a precise moving setup you can obtain the Piece of Heart by having the Boomerang interact with it " - "along the return path."); - OPT_TRICK( - RT_GY_CHILD_DAMPE_RACE_POH, RCQUEST_BOTH, RA_THE_GRAVEYARD, { Tricks::Tag::NOVICE }, - "Second Dampe Race as Child", "CDmpRace", - "It is possible to complete the second dampe race as child in under a minute, but it is a strict time limit."); - OPT_TRICK(RT_GY_SHADOW_FIRE_ARROWS, RCQUEST_BOTH, RA_THE_GRAVEYARD, { Tricks::Tag::EXPERT }, - "Shadow Temple Entry with Fire Arrows", "FAEntry", - "It is possible to light all of the torches to open the Shadow Temple entrance with just Fire Arrows, " - "but you must be very quick, precise, and strategic with how you take your shots."); - OPT_TRICK(RT_DMT_SHIELDLESS_CLIMB, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::NOVICE }, - "Death Mountain Trail Child Climb Without Shield", "DMTCWoS", - "Child can make it past the eruption to reach DMT Summit without a Hylian Shield or Nayru's Love" - "by backwalking or simply taking damage."); - OPT_TRICK(RT_DMT_SOIL_GS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::INTERMEDIATE }, - "Death Mountain Trail Soil GS without Destroying Boulder", "DMTSoil", - "Bugs will go into the soft soil even while the boulder is still blocking the entrance. Then, using a " - "precise moving setup you can kill the Gold Skulltula and obtain the token by having the Boomerang " - "interact with it along the return path."); - OPT_TRICK(RT_DMT_BOMBABLE, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::INTERMEDIATE }, - "Death Mountain Trail Chest with Strength", "DMTSTR", - "Child Link can blow up the wall using a nearby bomb flower. You must backwalk with the flower and then " - "quickly throw it toward the wall."); - OPT_TRICK(RT_DMT_HOVERS_LOWER_GS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::ADVANCED }, - "Death Mountain Trail Lower Red Rock GS with Hover Boots", "DMTGSHB", - "After killing the Skulltula, the token can be collected without needing to destroy the rock by " - "backflipping down onto it with the Hover Boots. First use the Hover Boots to stand on a nearby fence, " - "and go for the Skulltula Token from there."); - OPT_TRICK(RT_DMT_BEAN_LOWER_GS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::EXPERT }, - "Death Mountain Trail Lower Red Rock GS with Magic Bean", "DMTGSMB", - "After killing the Skulltula, the token can be collected without needing to destroy the rock by jumping " - "down onto it from the bean plant, midflight, with precise timing and positioning."); - OPT_TRICK(RT_DMT_JS_LOWER_GS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::INTERMEDIATE }, - "Death Mountain Trail Lower Red Rock GS with Jumpslash", "DMTGSJS", - "After killing the Skulltula, the token can be collected without needing to destroy the rock by jump " - "slashing from a precise angle."); - OPT_TRICK(RT_DMT_CLIMB_HOVERS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::ADVANCED }, - "Death Mountain Trail Climb with Hover Boots", "DMTBouHB", - "It is possible to use the Hover Boots to bypass needing to destroy the boulders blocking the path to " - "the top of Death Mountain."); - OPT_TRICK( - RT_DMT_UPPER_GS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::NOVICE }, - "Death Mountain Trail Upper Red Rock GS with Backflip", "DMTGSBF", - "After killing the Skulltula, the token can be collected by backflipping into the rock at the correct angle."); + "LWNaviD"); + OPT_TRICK(RT_LW_GS_BEAN, RCQUEST_BOTH, RA_THE_LOST_WOODS, { Tricks::Tag::INTERMEDIATE }, "LWGSHS"); + OPT_TRICK(RT_HC_STORMS_GS, RCQUEST_BOTH, RA_HYRULE_CASTLE, { Tricks::Tag::INTERMEDIATE }, "HCGrGSRng"); + OPT_TRICK(RT_HF_BIG_POE_WITHOUT_EPONA, RCQUEST_BOTH, RA_HYRULE_FIELD, { Tricks::Tag::NOVICE }, "PoeDiff"); + OPT_TRICK(RT_KAK_TOWER_GS, RCQUEST_BOTH, RA_KAKARIKO_VILLAGE, { Tricks::Tag::INTERMEDIATE }, "KakGSJS"); + OPT_TRICK(RT_KAK_CHILD_WINDMILL_POH, RCQUEST_BOTH, RA_KAKARIKO_VILLAGE, { Tricks::Tag::EXTREME }, "WndCJS"); + OPT_TRICK(RT_KAK_ROOFTOP_GS, RCQUEST_BOTH, RA_KAKARIKO_VILLAGE, { Tricks::Tag::ADVANCED }, "KakGSHB"); + OPT_TRICK(RT_GY_POH, RCQUEST_BOTH, RA_THE_GRAVEYARD, { Tricks::Tag::INTERMEDIATE }, "GYPoHRng"); + OPT_TRICK(RT_GY_CHILD_DAMPE_RACE_POH, RCQUEST_BOTH, RA_THE_GRAVEYARD, { Tricks::Tag::NOVICE }, "CDmpRace"); + OPT_TRICK(RT_GY_SHADOW_FIRE_ARROWS, RCQUEST_BOTH, RA_THE_GRAVEYARD, { Tricks::Tag::EXPERT }, "FAEntry"); + OPT_TRICK(RT_DMT_SHIELDLESS_CLIMB, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::NOVICE }, "DMTCWoS"); + OPT_TRICK(RT_DMT_SOIL_GS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::INTERMEDIATE }, "DMTSoil"); + OPT_TRICK(RT_DMT_BOMBABLE, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::INTERMEDIATE }, "DMTSTR"); + OPT_TRICK(RT_DMT_HOVERS_LOWER_GS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::ADVANCED }, "DMTGSHB"); + OPT_TRICK(RT_DMT_BEAN_LOWER_GS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::EXPERT }, "DMTGSMB"); + OPT_TRICK(RT_DMT_JS_LOWER_GS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::INTERMEDIATE }, "DMTGSJS"); + OPT_TRICK(RT_DMT_CLIMB_HOVERS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::ADVANCED }, "DMTBouHB"); + OPT_TRICK(RT_DMT_UPPER_GS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::NOVICE }, "DMTGSBF"); + // disabled for now, only applies when trade quest is not shuffled so there's a timer (currently not considered in - // logic) OPT_TRICK(RT_DMT_BOLERO_BIGGORON, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, {Tricks::Tag::INTERMEDIATE}, - // "Deliver Eye Drops with Bolero of Fire", "Playing a warp song normally causes a trade item to spoil immediately, - // however, it is possible use Bolero to reach Biggoron and still deliver the Eye Drops before they spoil. If you do - // not wear the Goron Tunic, the heat timer inside the crater will override the trade item\'s timer. When you exit - // to Death Mountain Trail you will have one second to show the Eye Drops before they expire. You can get extra time - // to show the Eye Drops if you warp immediately upon receiving them. If you don't have many hearts, you may have to - // reset the heat timer by quickly dipping in and out of Darunia\'s chamber or quickly equipping and unequipping the - // Goron Tunic. This trick does not apply if \"Randomize Warp Song Destinations\" is enabled, or if the settings are - // such that trade items do not need to be delivered within a time limit."); - OPT_TRICK(RT_GC_POT, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::ADVANCED }, - "Goron City Spinning Pot PoH with Bombchu", "GorPotChu", - "A Bombchu can be used to stop the spinning pot, but it can be quite finicky to get it to work."); - OPT_TRICK(RT_GC_POT_STRENGTH, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::INTERMEDIATE }, - "Goron City Spinning Pot PoH with Strength", "GorPotStr", - "Allows for stopping the Goron City Spinning Pot using a Bomb Flower alone, requiring strength in lieu " - "of inventory explosives."); - OPT_TRICK(RT_GC_ROLLING_STRENGTH, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::INTERMEDIATE }, - "Rolling Goron (Hot Rodder Goron) as Child with Strength", "GorStrC", - "Use the Bomb Flower on the stairs or near Medigoron. Timing is tight, especially without backwalking."); - OPT_TRICK(RT_GC_LEFTMOST, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::ADVANCED }, - "Goron City Maze Left Chest with Hover Boots", "GCMazHB", - "A precise backwalk starting from on top of the crate and ending with a precisely-timed backflip can " - "reach this chest without needing either the Hammer or Silver Gauntlets."); - OPT_TRICK(RT_GC_GROTTO, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::ADVANCED }, - "Goron City Grotto with Hookshot While Taking Damage", "GorGroHS", - "It is possible to reach the Goron City Grotto by quickly using the Hookshot while in the midst of " - "taking damage from the lava floor."); - OPT_TRICK(RT_GC_LINK_GORON_DINS, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::NOVICE }, - "Stop Link the Goron with Din\'s Fire", "GorDinA", "The timing is quite awkward."); - OPT_TRICK(RT_DMC_HOVER_BEAN_POH, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_CRATER, { Tricks::Tag::NOVICE }, - "Crater\'s Bean PoH with Hover Boots", "DMCHB", - "Hover from the base of the bridge near Goron City and walk up the very steep slope."); - OPT_TRICK(RT_DMC_BOLERO_JUMP, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_CRATER, { Tricks::Tag::EXTREME }, - "Death Mountain Crater Jump to Bolero", "DMCBolJump", - "As Adult, using a shield to drop a pot while you have the perfect speed and position, the pot can push " - "you that little extra distance you need to jump across the gap in the bridge."); - OPT_TRICK(RT_DMC_BOULDER_JS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_CRATER, { Tricks::Tag::NOVICE }, - "Death Mountain Crater Upper to Lower with Hammer", "DMCHam", - "With the Hammer, you can jumpslash the rock twice in the same jump in order to destroy it before you " - "fall into the lava."); - OPT_TRICK(RT_DMC_BOULDER_SKIP, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_CRATER, { Tricks::Tag::INTERMEDIATE }, - "Death Mountain Crater Upper to Lower Boulder Skip", "DMCULJmp", - "As adult, With careful positioning, you can jump to the ledge where the boulder is, then use repeated " - "ledge grabs to shimmy to a climbable ledge. This trick supersedes \"Death Mountain Crater Upper to " - "Lower with Hammer\"."); - OPT_TRICK(RT_ZR_LOWER, RCQUEST_BOTH, RA_ZORAS_RIVER, { Tricks::Tag::INTERMEDIATE }, - "Zora\'s River Lower Freestanding PoH as Adult with Nothing", "ZRLJmp", - "Adult can reach this PoH with a precise jump, no Hover Boots required."); - OPT_TRICK(RT_ZR_UPPER, RCQUEST_BOTH, RA_ZORAS_RIVER, { Tricks::Tag::INTERMEDIATE }, - "Zora\'s River Upper Freestanding PoH as Adult with Nothing", "ZRUJmp", - "Adult can reach this PoH with a precise jump, no Hover Boots required."); - OPT_TRICK(RT_ZR_HOVERS, RCQUEST_BOTH, RA_ZORAS_RIVER, { Tricks::Tag::NOVICE }, - "Zora\'s Domain Entry with Hover Boots", "ZRZDHB", "Can hover behind the waterfall as adult."); - OPT_TRICK(RT_ZR_CUCCO, RCQUEST_BOTH, RA_ZORAS_RIVER, { Tricks::Tag::NOVICE }, "Zora\'s Domain Entry with Cucco", - "ZRZDCuc", "You can fly behind the waterfall with a Cucco as child."); - OPT_TRICK(RT_ZD_KING_ZORA_SKIP, RCQUEST_BOTH, RA_ZORAS_DOMAIN, { Tricks::Tag::INTERMEDIATE }, - "Skip King Zora as Adult with Nothing", "Mweep", - "With a precise jump as adult, it is possible to get on the fence next to King Zora from the front to " - "access Zora's Fountain."); - OPT_TRICK(RT_ZD_GS, RCQUEST_BOTH, RA_ZORAS_DOMAIN, { Tricks::Tag::INTERMEDIATE }, - "Zora\'s Domain GS with No Additional Items", "ZDGS", - "A precise jumpslash can kill the Skulltula and recoil back onto the top of the frozen waterfall. To " - "kill it, the logic normally guarantees one of Hookshot, Bow, or Magic."); + // logic) + + // OPT_TRICK(RT_DMT_BOLERO_BIGGORON, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_TRAIL, { Tricks::Tag::INTERMEDIATE }); + + OPT_TRICK(RT_GC_POT, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::ADVANCED }, "GorPotChu"); + OPT_TRICK(RT_GC_POT_STRENGTH, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::INTERMEDIATE }, "GorPotStr"); + OPT_TRICK(RT_GC_ROLLING_STRENGTH, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::INTERMEDIATE }, "GorStrC"); + OPT_TRICK(RT_GC_LEFTMOST, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::ADVANCED }, "GCMazHB"); + OPT_TRICK(RT_GC_GROTTO, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::ADVANCED }, "GorGroHS"); + OPT_TRICK(RT_GC_LINK_GORON_DINS, RCQUEST_BOTH, RA_GORON_CITY, { Tricks::Tag::NOVICE }, "GorDinA"); + OPT_TRICK(RT_DMC_HOVER_BEAN_POH, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_CRATER, { Tricks::Tag::NOVICE }, "DMCHB"); + OPT_TRICK(RT_DMC_BOLERO_JUMP, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_CRATER, { Tricks::Tag::EXTREME }, "DMCBolJump"); + OPT_TRICK(RT_DMC_BOULDER_JS, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_CRATER, { Tricks::Tag::NOVICE }, "DMCHam"); + OPT_TRICK(RT_DMC_BOULDER_SKIP, RCQUEST_BOTH, RA_DEATH_MOUNTAIN_CRATER, { Tricks::Tag::INTERMEDIATE }, "DMCULJmp"); + OPT_TRICK(RT_ZR_LOWER, RCQUEST_BOTH, RA_ZORAS_RIVER, { Tricks::Tag::INTERMEDIATE }, "ZRLJmp"); + OPT_TRICK(RT_ZR_UPPER, RCQUEST_BOTH, RA_ZORAS_RIVER, { Tricks::Tag::INTERMEDIATE }, "ZRUJmp"); + OPT_TRICK(RT_ZR_HOVERS, RCQUEST_BOTH, RA_ZORAS_RIVER, { Tricks::Tag::NOVICE }, "ZRZDHB"); + OPT_TRICK(RT_ZR_CUCCO, RCQUEST_BOTH, RA_ZORAS_RIVER, { Tricks::Tag::NOVICE }, "ZRZDCuc"); + OPT_TRICK(RT_ZD_KING_ZORA_SKIP, RCQUEST_BOTH, RA_ZORAS_DOMAIN, { Tricks::Tag::INTERMEDIATE }, "Mweep"); + OPT_TRICK(RT_ZD_GS, RCQUEST_BOTH, RA_ZORAS_DOMAIN, { Tricks::Tag::INTERMEDIATE }, "ZDGS"); OPT_TRICK(RT_ZF_GREAT_FAIRY_WITHOUT_EXPLOSIVES, RCQUEST_BOTH, RA_ZORAS_FOUNTAIN, { Tricks::Tag::NOVICE }, - "Zora\'s Fountain Great Fairy without Explosives", "ZFGFStr2", - "It's possible to use silver gauntlets to pick up the silver rock and hammer to break the rock below it, " - "allowing you to ledge grab the edge of the hole and get past the breakable wall (hammer can't break the " - "wall itself)."); - OPT_TRICK(RT_LH_LAB_WALL_GS, RCQUEST_BOTH, RA_LAKE_HYLIA, { Tricks::Tag::NOVICE }, - "Lake Hylia Lab Wall GS with Jumpslash", "LHGSJS", - "The jumpslash to actually collect the token is somewhat precise."); - OPT_TRICK(RT_LH_LAB_DIVING, RCQUEST_BOTH, RA_LAKE_HYLIA, { Tricks::Tag::NOVICE }, - "Lake Hylia Lab Dive without Gold Scale", "LabHS", - "Remove the Iron Boots in the midst of Hookshotting the underwater crate."); - OPT_TRICK(RT_LH_WATER_HOOKSHOT, RCQUEST_BOTH, RA_LAKE_HYLIA, { Tricks::Tag::INTERMEDIATE }, - "Water Temple Entry without Iron Boots using Hookshot", "WTEntHS", - "When entering Water Temple using Gold Scale instead of Iron Boots, the Longshot is usually used to be " - "able to hit the switch and open the gate. But, by standing in a particular spot, the switch can be hit " - "with only the reach of the Hookshot."); - OPT_TRICK(RT_GV_CRATE_HOVERS, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::INTERMEDIATE }, - "Gerudo Valley Crate PoH as Adult with Hover Boots", "GVPoHHB", - "From the far side of Gerudo Valley, a precise Hover Boots movement and jump-slash recoil can allow " - "adult to reach the ledge with the crate PoH without needing Longshot. You will take fall damage."); - OPT_TRICK(RT_GV_CHILD_TENT, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::NOVICE }, - "Gerudo Valley Enter Carpenter's Tent as Child", "GVTent", - "The loading zone for Carpenter's Tent is accessible to child."); - OPT_TRICK(RT_GV_CHILD_CUCCO_JUMP, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::INTERMEDIATE }, - "Gerudo Valley Jump Fence with Cucco", "GVCUC", - "Using cucco as child, it's possible to jumpslash over the gate."); - OPT_TRICK(RT_GV_HOOKSHOT_BRIDGE, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::ADVANCED }, - "Gerudo Valley Bridge with only Hookshot", "GVHSBrg", - "Using Hookshot Extension and a precise setup, you can cross the broken bridge in Gerudo Valley with " - "only a Hookshot."); - OPT_TRICK(RT_PASS_GUARDS_WITH_NOTHING, RCQUEST_BOTH, RA_GERUDO_FORTRESS, { Tricks::Tag::NOVICE }, - "Sneak Past Moving Gerudo Guards with No Items", "Guards", - "The logic normally guarantees Bow or Hookshot to stun them from a distance," - "but every moving guard can be passed with basic movement and AI manipulation"); - OPT_TRICK(RT_GF_WASTELAND_GATE_SIDEHOP_SKIP, RCQUEST_BOTH, RA_GERUDO_FORTRESS, { Tricks::Tag::NOVICE }, - "Gerudo\'s Fortress Gate Skip with Sidehop", "GFHWC", - "You can sidehop out of bounds from the tower, then sidehop again on the other side of the gate.\n\n" - "This is easiest and most useful for Child, but Adult can also do this to skip the Gerudo Jabber Nut."); + "ZFGFStr2"); + OPT_TRICK(RT_LH_LAB_WALL_GS, RCQUEST_BOTH, RA_LAKE_HYLIA, { Tricks::Tag::NOVICE }, "LHGSJS"); + OPT_TRICK(RT_LH_LAB_DIVING, RCQUEST_BOTH, RA_LAKE_HYLIA, { Tricks::Tag::NOVICE }, "LabHS"); + OPT_TRICK(RT_LH_WATER_HOOKSHOT, RCQUEST_BOTH, RA_LAKE_HYLIA, { Tricks::Tag::INTERMEDIATE }, "WTEntHS"); + OPT_TRICK(RT_GV_CRATE_HOVERS, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::INTERMEDIATE }, "GVPoHHB"); + OPT_TRICK(RT_GV_CHILD_TENT, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::NOVICE }, "GVTent"); + OPT_TRICK(RT_GV_CHILD_CUCCO_JUMP, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::INTERMEDIATE }, "GVCUC"); + OPT_TRICK(RT_GV_HOOKSHOT_BRIDGE, RCQUEST_BOTH, RA_GERUDO_VALLEY, { Tricks::Tag::ADVANCED }, "GVHSBrg"); + OPT_TRICK(RT_PASS_GUARDS_WITH_NOTHING, RCQUEST_BOTH, RA_GERUDO_FORTRESS, { Tricks::Tag::NOVICE }, "Guards"); + OPT_TRICK(RT_GF_WASTELAND_GATE_SIDEHOP_SKIP, RCQUEST_BOTH, RA_GERUDO_FORTRESS, { Tricks::Tag::NOVICE }, "GFHWC"); OPT_TRICK(RT_GF_ADULT_SKIP_WASTELAND_GATE, RCQUEST_BOTH, RA_GERUDO_FORTRESS, { Tricks::Tag::INTERMEDIATE }, - "Gerudo\'s Fortress Skip Wasteland Gate as Adult", "GFHWA", - "As adult a precise jumpslash out of bounds with hoverboots from above the jail can be used to get past " - "the gate.\n\n" - "This can also be used to reach the tower and lower the gate for child without climb."); - OPT_TRICK(RT_GF_WARRIOR_WITH_DIFFICULT_WEAPON, RCQUEST_BOTH, RA_GERUDO_FORTRESS, { Tricks::Tag::NOVICE }, - "Gerudo\'s Fortress Warriors with Difficult Weapons", "GWDiff", - "Warriors can be defeated with Slingshot or Bombchus."); + "GFHWA"); + OPT_TRICK(RT_GF_WARRIOR_WITH_DIFFICULT_WEAPON, RCQUEST_BOTH, RA_GERUDO_FORTRESS, { Tricks::Tag::NOVICE }, "GWDiff"); OPT_TRICK(RT_GF_LEDGE_CLIP_INTO_GTG, RCQUEST_BOTH, RA_GERUDO_FORTRESS, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, - "Ledge Clip into Training Ground", "GTGLdgClp", - "Adult Link can use a ledge clip to enter Gerudo Training Ground without Gerudo Card."); + "GTGLdgClp"); + // disabled for now, can't check for being able to use bunny hood & bunny hood speedup is currently completely - // decoupled from rando OPT_TRICK(RT_HW_BUNNY_CROSSING, RCQUEST_BOTH, RA_HAUNTED_WASTELAND, {Tricks::Tag::NOVICE}, - // "Wasteland Crossing with Bunny Hood", "You can beat the quicksand by using the increased speed of the Bunny Hood. - // Note that jumping to the carpet merchant as child typically requires a fairly precise jumpslash."); - OPT_TRICK(RT_HW_CROSSING, RCQUEST_BOTH, RA_HAUNTED_WASTELAND, { Tricks::Tag::INTERMEDIATE }, - "Wasteland Crossing without Hover Boots or Longshot", "RvrSand", - "You can beat the quicksand by backwalking across it in a specific way. Note that jumping to the carpet " - "merchant as child typically requires a fairly precise jumpslash."); - OPT_TRICK(RT_LENS_HW, RCQUEST_BOTH, RA_HAUNTED_WASTELAND, { Tricks::Tag::INTERMEDIATE }, "Lensless Wasteland", - "HWNoLoT", - "By memorizing the path, you can travel through the Wasteland without using the Lens of Truth to see the " - "Poe. The equivalent trick for going in reverse through the Wasteland is \"Reverse Wasteland\"."); - OPT_TRICK( - RT_HW_REVERSE, RCQUEST_BOTH, RA_HAUNTED_WASTELAND, { Tricks::Tag::INTERMEDIATE }, "Reverse Wasteland", "RevHW", - "By memorizing the path, you can travel through the Wasteland in reverse. Note that jumping to the carpet " - "merchant as child typically requires a fairly precise jumpslash. The equivalent trick for going forward " - "through the Wasteland is \"Lensless Wasteland\". To cross the river of sand with no additional items, be sure " - "to also enable \"Wasteland Crossing without Hover Boots or Longshot\". Unless all overworld entrances are " - "randomized, Child Link will not be expected to do anything at Gerudo's Fortress."); - OPT_TRICK(RT_COLOSSUS_GS, RCQUEST_BOTH, RA_DESERT_COLOSSUS, { Tricks::Tag::NOVICE }, - "Colossus Hill GS with Hookshot", "ColGSHS", - "Somewhat precise. If you kill enough Leevers you can get enough of a break to take some time to aim " - "more carefully."); - OPT_TRICK(RT_DEKU_BASEMENT_GS, RCQUEST_VANILLA, RA_DEKU_TREE, { Tricks::Tag::NOVICE }, - "Deku Tree Basement Vines GS with Jumpslash", "DTGSJS", "Can be defeated by doing a precise jumpslash."); - OPT_TRICK(RT_DEKU_B1_SKIP, RCQUEST_BOTH, RA_DEKU_TREE, { Tricks::Tag::INTERMEDIATE }, - "Deku Tree Basement without Slingshot", "B1Skip", - "A precise jump can be used to skip needing to use the Slingshot to go around B1 of the Deku Tree. If " - "used with the \"Closed Forest\" setting, a Slingshot will not be guaranteed to exist somewhere inside " - "the Forest. This trick applies to both Vanilla and Master Quest."); - OPT_TRICK(RT_DEKU_B1_BOW_WEBS, RCQUEST_VANILLA, RA_DEKU_TREE, { Tricks::Tag::NOVICE }, - "Deku Tree Basement Web to Gohma with Bow", "DTWebBow", - "All spider web walls in the Deku Tree basement can be burnt as adult with just a bow by shooting " - "through torches. This trick only applies to the circular web leading to Gohma; the two vertical webs " - "are always in logic. Backflip onto the chest near the torch at the bottom of the vine wall. With " - "precise positioning you can shoot through the torch to the right edge of the circular web. This allows " - "completion of adult Deku Tree with no fire source."); - OPT_TRICK(RT_DEKU_B1_BACKFLIP_OVER_SPIKED_LOG, RCQUEST_VANILLA, RA_DEKU_TREE, { Tricks::Tag::NOVICE }, - "Deku Tree Basement Backflip over Spiked Log", "DTLogBF", - "Allows backflipping over the spiked log in the Deku Tree basement in Vanilla. Only relevant if " - "\"Shuffle Swim\" is enabled."); - OPT_TRICK(RT_DEKU_MQ_COMPASS_GS, RCQUEST_MQ, RA_DEKU_TREE, { Tricks::Tag::NOVICE }, - "Deku Tree MQ Compass Room GS Boulders with Just Hammer", "DTGSHam", - "Climb to the top of the vines, then let go and jumpslash immediately to destroy the boulders using the " - "Hammer, without needing to spawn a Song of Time block."); - OPT_TRICK(RT_DEKU_MQ_LOG, RCQUEST_MQ, RA_DEKU_TREE, { Tricks::Tag::NOVICE }, - "Deku Tree MQ Roll Under the Spiked Log", "DTLogRol", - "You can get past the spiked log by rolling to briefly shrink your hitbox. As adult, the timing is a bit " - "more precise."); - OPT_TRICK(RT_DC_SCARECROW_GS, RCQUEST_VANILLA, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, - "Dodongo\'s Cavern Scarecrow GS with Armos Statue", "DCArmos", - "You can jump off an Armos Statue to reach the alcove with the Gold Skulltula. It takes quite a long " - "time to pull the statue the entire way. The jump to the alcove can be a bit picky when done as child."); - OPT_TRICK(RT_DC_VINES_GS, RCQUEST_VANILLA, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, - "Dodongo\'s Cavern Vines GS from Below with Longshot", "DCGSLS", - "The vines upon which this Skulltula rests are one-sided collision. You can use the Longshot to get it " - "from below, by shooting it through the vines, bypassing the need to lower the staircase."); - OPT_TRICK(RT_DC_STAIRS_WITH_BOW, RCQUEST_VANILLA, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, - "Dodongo\'s Cavern Stairs with Bow", "DCStaBow", - "The Bow can be used to knock down the stairs with two well-timed shots."); - OPT_TRICK(RT_DC_SLINGSHOT_SKIP, RCQUEST_VANILLA, RA_DODONGOS_CAVERN, { Tricks::Tag::EXPERT }, - "Dodongo\'s Cavern Child Slingshot Skips", "DCSliSkp", - "With precise platforming, child can cross the platforms while the flame circles are there. When " - "enabling this trick, it's recommended that you also enable the Adult variant: \"Dodongo's Cavern Spike " - "Trap Room Jump without Hover Boots\"."); - OPT_TRICK(RT_DC_SCRUB_ROOM, RCQUEST_VANILLA, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, - "Dodongo\'s Cavern Two Scrub Room with Strength", "DCSrbStr", - "With help from a conveniently-positioned block, Adult can quickly carry a Bomb Flower over to destroy " - "the mud wall blocking the room with two Deku Scrubs."); - OPT_TRICK(RT_DC_HAMMER_FLOOR, RCQUEST_BOTH, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, - "Dodongo\'s Cavern Smash the Boss Lobby Floor", "KDHamFl", - "The bombable floor before King Dodongo can be destroyed with Hammer if hit in the very center. This is " - "only relevant with Shuffle Boss Entrances or if Dodongo's Cavern is MQ and either variant of " - "\"Dodongo's Cavern MQ Light the Eyes with Strength\" is on."); - OPT_TRICK(RT_DC_DODONGO_CHU, RCQUEST_BOTH, RA_DODONGOS_CAVERN, { Tricks::Tag::ADVANCED }, - "Dodongo\'s Cavern Dodongo with Only Bombchus", "KDChu", - "With precise timing you can feed King Dodongo a bombchu during a backflip"); - OPT_TRICK(RT_DC_MQ_STAIRS_WITH_ONLY_STRENGTH, RCQUEST_MQ, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, - "Dodongo\'s Cavern MQ Stairs With Only Strength", "DCStaStr", - "Taking a bomb from the back can be used to lower stairs without using stick to drop bomb from wall."); - OPT_TRICK(RT_DC_MQ_CHILD_BOMBS, RCQUEST_MQ, RA_DODONGOS_CAVERN, { Tricks::Tag::ADVANCED }, - "Dodongo\'s Cavern MQ Early Bomb Bag Area as Child", "DCLobyJS", - "With a precise jumpslash from above, you can reach the Bomb Bag area as only child without needing a " - "Slingshot. You will take fall damage."); - OPT_TRICK(RT_DC_MQ_CHILD_EYES, RCQUEST_MQ, RA_DODONGOS_CAVERN, { Tricks::Tag::EXPERT }, - "Dodongo\'s Cavern MQ Light the Eyes with Strength as Child", "DCEyeStrC", - "If you move very quickly, it is possible to use the bomb flower at the top of the room to light the " - "eyes. To perform this trick as child is significantly more difficult than adult. The player is also " - "expected to complete the DC back area without explosives, including getting past the Armos wall to the " - "switch for the boss door."); - OPT_TRICK( - RT_DC_MQ_ADULT_EYES, RCQUEST_MQ, RA_DODONGOS_CAVERN, { Tricks::Tag::ADVANCED }, - "Dodongo\'s Cavern MQ Light the Eyes with Strength as Adult", "DCEyeStrA", - "If you move very quickly, it is possible to use the bomb flower at the top of the room to light the eyes."); - OPT_TRICK( - RT_DC_EYES_CHU, RCQUEST_BOTH, RA_DODONGOS_CAVERN, { Tricks::Tag::ADVANCED }, - "Dodongo\'s Cavern Light the Eyes with Bombchus", "DCEyeChu", - "You can light the dodongo head's eyes with bombchus from the main room, allowing instant access to the end " - "of the dungeon."); - OPT_TRICK(RT_JABU_BOSS_HOVER, RCQUEST_VANILLA, RA_JABU_JABUS_BELLY, { Tricks::Tag::INTERMEDIATE }, - "Jabu Near Boss Room with Hover Boots", "JbuBoxHB", - "A box for the blue switch can be carried over by backwalking with one while the elevator is at its " - "peak. Alternatively, you can skip transporting a box by quickly rolling from the switch and opening the " - "door before it closes. However, the timing for this is very tight."); - OPT_TRICK( - RT_JABU_NEAR_BOSS_RANGED, RCQUEST_BOTH, RA_JABU_JABUS_BELLY, { Tricks::Tag::NOVICE }, - "Jabu Near Boss Ceiling Switch/GS without Boomerang or Explosives", "JbuBosPrj", - "Vanilla Jabu: From near the entrance into the room, you can hit the switch that opens the door to the boss " - "room using a precisely-aimed use of the Slingshot, Bow, or Longshot. As well, if you climb to the top of the " - "vines you can stand on the right edge of the platform and shoot around the glass. From this distance, even " - "the Hookshot can reach the switch. This trick is only relevant if \"Shuffle Boss Entrances\" is enabled. MQ " - "Jabu: A Gold Skulltula Token can be collected with Longshot using the same methods as hitting the switch in " - "Vanilla."); + // decoupled from rando + + // OPT_TRICK(RT_HW_BUNNY_CROSSING, RCQUEST_BOTH, RA_HAUNTED_WASTELAND, { Tricks::Tag::NOVICE }); + + OPT_TRICK(RT_HW_CROSSING, RCQUEST_BOTH, RA_HAUNTED_WASTELAND, { Tricks::Tag::INTERMEDIATE }, "RvrSand"); + OPT_TRICK(RT_LENS_HW, RCQUEST_BOTH, RA_HAUNTED_WASTELAND, { Tricks::Tag::INTERMEDIATE }, "HWNoLoT"); + OPT_TRICK(RT_HW_REVERSE, RCQUEST_BOTH, RA_HAUNTED_WASTELAND, { Tricks::Tag::INTERMEDIATE }, "RevHW"); + OPT_TRICK(RT_COLOSSUS_GS, RCQUEST_BOTH, RA_DESERT_COLOSSUS, { Tricks::Tag::NOVICE }, "ColGSHS"); + OPT_TRICK(RT_DEKU_BASEMENT_GS, RCQUEST_VANILLA, RA_DEKU_TREE, { Tricks::Tag::NOVICE }, "DTGSJS"); + OPT_TRICK(RT_DEKU_B1_SKIP, RCQUEST_BOTH, RA_DEKU_TREE, { Tricks::Tag::INTERMEDIATE }, "B1Skip"); + OPT_TRICK(RT_DEKU_B1_BOW_WEBS, RCQUEST_VANILLA, RA_DEKU_TREE, { Tricks::Tag::NOVICE }, "DTWebBow"); + OPT_TRICK(RT_DEKU_B1_BACKFLIP_OVER_SPIKED_LOG, RCQUEST_VANILLA, RA_DEKU_TREE, { Tricks::Tag::NOVICE }, "DTLogBF"); + OPT_TRICK(RT_DEKU_MQ_COMPASS_GS, RCQUEST_MQ, RA_DEKU_TREE, { Tricks::Tag::NOVICE }, "DTGSHam"); + OPT_TRICK(RT_DEKU_MQ_LOG, RCQUEST_MQ, RA_DEKU_TREE, { Tricks::Tag::NOVICE }, "DTLogRol"); + OPT_TRICK(RT_DC_SCARECROW_GS, RCQUEST_VANILLA, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, "DCArmos"); + OPT_TRICK(RT_DC_VINES_GS, RCQUEST_VANILLA, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, "DCGSLS"); + OPT_TRICK(RT_DC_STAIRS_WITH_BOW, RCQUEST_VANILLA, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, "DCStaBow"); + OPT_TRICK(RT_DC_SLINGSHOT_SKIP, RCQUEST_VANILLA, RA_DODONGOS_CAVERN, { Tricks::Tag::EXPERT }, "DCSliSkp"); + OPT_TRICK(RT_DC_SCRUB_ROOM, RCQUEST_VANILLA, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, "DCSrbStr"); + OPT_TRICK(RT_DC_HAMMER_FLOOR, RCQUEST_BOTH, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, "KDHamFl"); + OPT_TRICK(RT_DC_DODONGO_CHU, RCQUEST_BOTH, RA_DODONGOS_CAVERN, { Tricks::Tag::ADVANCED }, "KDChu"); + OPT_TRICK(RT_DC_MQ_STAIRS_WITH_ONLY_STRENGTH, RCQUEST_MQ, RA_DODONGOS_CAVERN, { Tricks::Tag::NOVICE }, "DCStaStr"); + OPT_TRICK(RT_DC_MQ_CHILD_BOMBS, RCQUEST_MQ, RA_DODONGOS_CAVERN, { Tricks::Tag::ADVANCED }, "DCLobyJS"); + OPT_TRICK(RT_DC_MQ_CHILD_EYES, RCQUEST_MQ, RA_DODONGOS_CAVERN, { Tricks::Tag::EXPERT }, "DCEyeStrC"); + OPT_TRICK(RT_DC_MQ_ADULT_EYES, RCQUEST_MQ, RA_DODONGOS_CAVERN, { Tricks::Tag::ADVANCED }, "DCEyeStrA"); + OPT_TRICK(RT_DC_EYES_CHU, RCQUEST_BOTH, RA_DODONGOS_CAVERN, { Tricks::Tag::ADVANCED }, "DCEyeChu"); + OPT_TRICK(RT_JABU_BOSS_HOVER, RCQUEST_VANILLA, RA_JABU_JABUS_BELLY, { Tricks::Tag::INTERMEDIATE }, "JbuBoxHB"); + OPT_TRICK(RT_JABU_NEAR_BOSS_RANGED, RCQUEST_BOTH, RA_JABU_JABUS_BELLY, { Tricks::Tag::NOVICE }, "JbuBosPrj"); OPT_TRICK(RT_JABU_NEAR_BOSS_EXPLOSIVES, RCQUEST_VANILLA, RA_JABU_JABUS_BELLY, { Tricks::Tag::INTERMEDIATE }, - "Jabu Near Boss Ceiling Switch with Explosives", "JbuBosExp", - "You can hit the switch that opens the door to the boss room using a precisely-aimed Bombchu. Also, " - "using the Hover Boots, adult can throw a Bomb at the switch. This trick is only relevant if \"Shuffle " - "Boss Entrances\" is enabled."); - OPT_TRICK(RT_JABU_B1_CUBE_HOVER, RCQUEST_VANILLA, RA_JABU_JABUS_BELLY, { Tricks::Tag::NOVICE }, - "Jabu B1 Pass Cube with Hover Boots", "JbuJigHB", - "It's possible reach pots past cube with only hover boots."); - OPT_TRICK(RT_LENS_JABU_MQ, RCQUEST_MQ, RA_JABU_JABUS_BELLY, { Tricks::Tag::NOVICE }, - "Jabu MQ without Lens of Truth", "JbuLoT", "Removes the requirements for the Lens of Truth in Jabu MQ."); - OPT_TRICK(RT_JABU_MQ_RANG_JUMP, RCQUEST_MQ, RA_JABU_JABUS_BELLY, { Tricks::Tag::ADVANCED }, - "Jabu MQ Compass Chest with Boomerang", "JbuSwtRng", - "Boomerang can reach the cow switch to spawn the chest by targeting the cow, jumping off of the ledge " - "where the chest spawns, and throwing the Boomerang in midair."); - OPT_TRICK(RT_JABU_MQ_SOT_GS, RCQUEST_MQ, RA_JABU_JABUS_BELLY, { Tricks::Tag::INTERMEDIATE }, - "Jabu MQ Song of Time Block GS with Boomerang", "JbuSoTRng", - "Allow the Boomerang to return to you through the Song of Time block to grab the token."); - OPT_TRICK(RT_JABU_BARINADE_POTS, RCQUEST_BOTH, RA_JABU_JABUS_BELLY, { Tricks::Tag::ADVANCED }, - "Jabu Barinade with Pots", "BariPot", - "Barinade can be damaged with pots, requiring only boomerang to defeat."); - OPT_TRICK(RT_LENS_BOTW, RCQUEST_VANILLA, RA_BOTTOM_OF_THE_WELL, { Tricks::Tag::NOVICE }, - "Bottom of the Well without Lens of Truth", "BWLoT", - "Removes the requirements for the Lens of Truth in Bottom of the Well."); + "JbuBosExp"); + OPT_TRICK(RT_JABU_B1_CUBE_HOVER, RCQUEST_VANILLA, RA_JABU_JABUS_BELLY, { Tricks::Tag::NOVICE }, "JbuJigHB"); + OPT_TRICK(RT_LENS_JABU_MQ, RCQUEST_MQ, RA_JABU_JABUS_BELLY, { Tricks::Tag::NOVICE }, "JbuLoT"); + OPT_TRICK(RT_JABU_MQ_RANG_JUMP, RCQUEST_MQ, RA_JABU_JABUS_BELLY, { Tricks::Tag::ADVANCED }, "JbuSwtRng"); + OPT_TRICK(RT_JABU_MQ_SOT_GS, RCQUEST_MQ, RA_JABU_JABUS_BELLY, { Tricks::Tag::INTERMEDIATE }, "JbuSoTRng"); + OPT_TRICK(RT_JABU_BARINADE_POTS, RCQUEST_BOTH, RA_JABU_JABUS_BELLY, { Tricks::Tag::ADVANCED }, "BariPot"); + OPT_TRICK(RT_LENS_BOTW, RCQUEST_VANILLA, RA_BOTTOM_OF_THE_WELL, { Tricks::Tag::NOVICE }, "BWLoT"); OPT_TRICK(RT_BOTTOM_OF_THE_WELL_NAVI_DIVE, RCQUEST_BOTH, RA_BOTTOM_OF_THE_WELL, - { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, "Bottom of the Well Navi dive", "KakNviD", - "You need Deku Sticks or Kokiri Sword to dive with Navi for entering Bottom of the Well."); - OPT_TRICK(RT_BOTW_CHILD_DEADHAND, RCQUEST_BOTH, RA_BOTTOM_OF_THE_WELL, { Tricks::Tag::NOVICE }, - "Child Dead Hand without Kokiri Sword", "DHDiff", "Requires 9 sticks or 5 jumpslashes."); - OPT_TRICK(RT_BOTW_BASEMENT, RCQUEST_VANILLA, RA_BOTTOM_OF_THE_WELL, { Tricks::Tag::NOVICE }, - "Bottom of the Well Map Chest with Strength & Sticks", "BWBmbFl", - "The chest in the basement can be reached with strength by doing a jumpslash with a lit stick to access " - "the Bomb Flowers."); + { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, "KakNviD"); + OPT_TRICK(RT_BOTW_CHILD_DEADHAND, RCQUEST_BOTH, RA_BOTTOM_OF_THE_WELL, { Tricks::Tag::NOVICE }, "DHDiff"); + OPT_TRICK(RT_BOTW_BASEMENT, RCQUEST_VANILLA, RA_BOTTOM_OF_THE_WELL, { Tricks::Tag::NOVICE }, "BWBmbFl"); // RANDOTODO with doorsanity, this can be relevant in Vanilla - OPT_TRICK(RT_BOTW_PITS, RCQUEST_MQ, RA_BOTTOM_OF_THE_WELL, { Tricks::Tag::NOVICE }, - "Bottom of the Well MQ Jump Over the Pits", "BWPitJmp", - "While the pits in Bottom of the Well don't allow you to jump just by running straight at them, you can " - "still get over them by side-hopping or backflipping across. With explosives, this allows you to access " - "the central areas without Zelda's Lullaby. With Zelda's Lullaby, it allows you to access the west inner " - "room without explosives."); - OPT_TRICK(RT_BOTW_MQ_DEADHAND_KEY, RCQUEST_MQ, RA_BOTTOM_OF_THE_WELL, { Tricks::Tag::NOVICE }, - "Bottom of the Well MQ Dead Hand Freestanding Key with Boomerang", "BWKeyRng", - "Boomerang can fish the item out of the rubble without needing explosives to blow it up."); - OPT_TRICK(RT_FOREST_FIRST_GS, RCQUEST_VANILLA, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, - "Forest Temple First Room GS with Difficult-to-Use Weapons", "FT1stGS", - "Allows killing this Skulltula with Sword or Sticks by jumpslashing it as you let go from the vines. " - "You can avoid taking fall damage by recoiling onto the tree. Also allows killing it as Child with a " - "Bomb throw. It's much more difficult to use a Bomb as child due to Child Link's shorter height."); - OPT_TRICK(RT_FOREST_COURTYARD_EAST_GS, RCQUEST_VANILLA, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, - "Forest Temple East Courtyard GS with Boomerang", "FTGSRng", - "Precise Boomerang throws can allow child to kill the Skulltula and collect the token."); - OPT_TRICK(RT_FOREST_VINES, RCQUEST_BOTH, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, - "Forest Temple East Courtyard Vines with Hookshot", "FTVineHS", - "The vines in Forest Temple leading to where the well drain switch is in the standard form can be barely " - "reached with just the Hookshot. Applies to MQ also."); - OPT_TRICK(RT_FOREST_COURTYARD_LEDGE, RCQUEST_BOTH, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, - "Forest Temple NE Courtyard Ledge with Hover Boots", "FTLdgHB", - "With precise Hover Boots movement you can fall down to this ledge from upper balconies. If done " - "precisely enough, it is not necessary to take fall damage. In MQ, this skips a Longshot requirement. In " - "Vanilla, this can skip a Hookshot requirement in entrance randomizer."); - OPT_TRICK(RT_FOREST_DOORFRAME, RCQUEST_BOTH, RA_FOREST_TEMPLE, { Tricks::Tag::ADVANCED }, - "Forest Temple East Courtyard Door Frame with Hover Boots", "FTDoorHB", - "A precise Hover Boots movement from the upper balconies in this courtyard can be used to get on top of " - "the door frame. Applies to both Vanilla and Master Quest. In Vanilla, from on top the door frame you " - "can summon Pierre, allowing you to access the falling ceiling room early. In Master Quest, this allows " - "you to obtain the GS on the door frame as adult without Hookshot or Song of Time."); - OPT_TRICK(RT_FOREST_OUTSIDE_BACKDOOR, RCQUEST_BOTH, RA_FOREST_TEMPLE, { Tricks::Tag::ADVANCED }, - "Forest Temple Outside Backdoor with Jumpslash", "FTBlkJS", - "A jumpslash recoil can be used to reach the ledge in the block puzzle room that leads to the west " - "courtyard. This skips a potential Hover Boots requirement in Vanilla, and it can sometimes apply in MQ " - "as well. This trick can be performed as both ages."); + OPT_TRICK(RT_BOTW_PITS, RCQUEST_MQ, RA_BOTTOM_OF_THE_WELL, { Tricks::Tag::NOVICE }, "BWPitJmp"); + OPT_TRICK(RT_BOTW_MQ_DEADHAND_KEY, RCQUEST_MQ, RA_BOTTOM_OF_THE_WELL, { Tricks::Tag::NOVICE }, "BWKeyRng"); + OPT_TRICK(RT_FOREST_FIRST_GS, RCQUEST_VANILLA, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FT1stGS"); + OPT_TRICK(RT_FOREST_COURTYARD_EAST_GS, RCQUEST_VANILLA, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FTGSRng"); + OPT_TRICK(RT_FOREST_VINES, RCQUEST_BOTH, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FTVineHS"); + OPT_TRICK(RT_FOREST_COURTYARD_LEDGE, RCQUEST_BOTH, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FTLdgHB"); + OPT_TRICK(RT_FOREST_DOORFRAME, RCQUEST_BOTH, RA_FOREST_TEMPLE, { Tricks::Tag::ADVANCED }, "FTDoorHB"); + OPT_TRICK(RT_FOREST_OUTSIDE_BACKDOOR, RCQUEST_BOTH, RA_FOREST_TEMPLE, { Tricks::Tag::ADVANCED }, "FTBlkJS"); OPT_TRICK(RT_FOREST_COURTYARD_HEARTS_BOOMERANG, RCQUEST_BOTH, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, - "Forest Temple Courtyard Hearts with Boomerang", "FTHrtRng", - "A well aimed boomerang from the water's edge can reach the hearts from ground level. If unable to swim, " - "you can back away from the water while the boomerang is returning so the hearts land on the ground."); - OPT_TRICK(RT_FOREST_WELL_SWIM, RCQUEST_BOTH, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, - "Swim Through Forest Temple Well with Hookshot", "FTSwim", - "Shoot the vines in the well as low and as far to the right as possible, and then immediately swim under " - "the ceiling to the right. This is usually only useful in Master Quest."); - OPT_TRICK(RT_FOREST_MQ_BLOCK_PUZZLE, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, - "Skip Forest Temple MQ Block Puzzle with Bombchu", "FTBlkChu", - "Send the Bombchu straight up the center of the wall directly to the left upon entering the room."); + "FTHrtRng"); + OPT_TRICK(RT_FOREST_WELL_SWIM, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FTSwim"); + OPT_TRICK(RT_FOREST_MQ_BLOCK_PUZZLE, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FTBlkChu"); // Child with hovers cannot do this from the lower floor, and must go to the upper floor which needs goron bracelet. // Adult can do this with hammer and KSword, But child cannot. - OPT_TRICK(RT_FOREST_MQ_JS_HALLWAY_SWITCH, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, - "Forest Temple MQ Twisted Hallway Switch with Jumpslash", "FTTwstJS", - "The switch to twist the hallway can be hit with a jumpslash through the glass block. To get in front " - "of the switch, either use the Hover Boots or hit the shortcut switch at the top of the room and jump " - "from the glass blocks that spawn. Sticks can be used as child, but the Kokiri Sword is too short to " - "reach through the glass."); + OPT_TRICK(RT_FOREST_MQ_JS_HALLWAY_SWITCH, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FTTwstJS"); OPT_TRICK(RT_FOREST_MQ_HOOKSHOT_HALLWAY_SWITCH, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Forest Temple MQ Twisted Hallway Switch with Hookshot", "FTTwstHS", - "There's a very small gap between the glass block and the wall. Through that gap you can hookshot the " - "target on the ceiling."); + "FTTwstHS"); OPT_TRICK(RT_FOREST_MQ_RANG_HALLWAY_SWITCH, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Forest Temple MQ Twisted Hallway Switch with Boomerang", "FTTwstRng", - "The Boomerang can return to Link through walls, allowing child to hit the hallway switch. This can be " - "used to allow adult to pass through later, or in conjunction with \"Forest Temple Outside Backdoor with " - "Jumpslash\"."); - OPT_TRICK(RT_FOREST_MQ_CHILD_DOORFRAME, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, - "Forest Temple MQ Doorframe GS as Child without Boomerang", "FTDoorC", - "If Adult burns the courtyard webbing with Fire Arrows (which is a permanent flag in Ship Rando) " - "then Child can climb up to the balconies and jump to the SoT block from the railing, " - "and from there either roll jump or jump against the wall to reach the doorframe.\n" - "From there, The GS can be killed with a crouchstab, explosives or other ranged weapon " - "and collected by climbing down."); + "FTTwstRng"); + OPT_TRICK(RT_FOREST_MQ_CHILD_DOORFRAME, RCQUEST_MQ, RA_FOREST_TEMPLE, { Tricks::Tag::NOVICE }, "FTDoorC"); // Is also used in MQ logic, but has no practical effect there as of now - OPT_TRICK(RT_FIRE_SOT, RCQUEST_VANILLA, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Fire Temple Song of Time Room GS without Song of Time", "FISoTSkp", - "A precise jump can be used to reach this room."); - OPT_TRICK(RT_FIRE_STRENGTH, RCQUEST_VANILLA, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Fire Temple Climb without Strength", "FIStrSkp", - "A precise jump can be used to skip pushing the block."); - OPT_TRICK(RT_FIRE_SCARECROW, RCQUEST_VANILLA, RA_FIRE_TEMPLE, { Tricks::Tag::EXPERT }, - "Fire Temple East Tower without Scarecrow\'s Song", "PixelShot", - "Also known as \"Pixelshot\". The Longshot can reach the target on the elevator itself, allowing you to " - "skip needing to spawn the scarecrow."); - OPT_TRICK(RT_FIRE_SKIP_FLAME_WALLS, RCQUEST_BOTH, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Fire Temple Skip Flame Walls", "FIRWAL", - "If you move quickly you can sneak past the edge of a flame wall before rises up to block you. To " - "do it without taking damage is more precise. Allows progress without needing either a Small Key or " - "Hover Boots. In MQ if either \"Fire Temple MQ Lower to Upper Lizalfos Maze with Hover Boots\" or " - "\"with Precise Jump\" are enabled, this also allows progress deeper into the dungeon without Hookshot.\n" - "Child can sidehop past fire wall in MQ lobby."); - OPT_TRICK(RT_FIRE_MQ_NEAR_BOSS, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::NOVICE }, - "Fire Temple MQ Chest Near Boss without Breaking Crate", "FICrtTor", - "The hitbox for the torch extends a bit outside of the crate. Shoot a flaming arrow at the side of the " - "crate to light the torch without needing to get over there and break the crate."); - OPT_TRICK(RT_FIRE_MQ_BLOCKED_CHEST, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Fire Temple MQ Big Lava Room Blocked Door without Hookshot", "FIHSSkp", - "There is a gap between the hitboxes of the flame wall in the big lava room. If you know where this gap " - "is located, you can jump through it and skip needing to use the Hookshot. To do this without taking " - "damage is more precise."); - OPT_TRICK( - RT_FIRE_MQ_BK_CHEST, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Fire Temple MQ Boss Key Chest without Bow", "FIBowSkp", - "It is possible to light both of the timed torches to unbar the door to the boss key chest's room with just " - "Din's Fire if you move very quickly between the two torches. It is also possible to unbar the door with just " - "Din's Fire by abusing an oversight in the way the game counts how many torches have been lit."); - OPT_TRICK(RT_FIRE_MQ_CLIMB, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::NOVICE }, - "Fire Temple MQ Climb without Fire Source", "FIFirSkp", - "You can use the Hover Boots to hover around to the climbable wall, skipping the need to use a fire " - "source and spawn a Hookshot target."); - OPT_TRICK(RT_FIRE_MQ_MAZE_SIDE_ROOM, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::NOVICE }, - "Fire Temple MQ Lizalfos Maze Side Room without Box", "FIBoxSkp", - "You can walk from the blue switch to the door and quickly open the door before the bars reclose. This " - "skips needing to reach the upper sections of the maze to get a box to place on the switch."); - OPT_TRICK(RT_FIRE_MQ_MAZE_HOVERS, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::NOVICE }, - "Fire Temple MQ Lower to Upper Lizalfos Maze with Hover Boots", "FIMazHB", - "Use the Hover Boots off of a crate to climb to the upper maze without needing to spawn and use the " - "Hookshot targets."); - OPT_TRICK(RT_FIRE_MQ_MAZE_JUMP, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Fire Temple MQ Lower to Upper Lizalfos Maze with Precise Jump", "FIMazJmp", - "A precise jump off of a crate can be used to climb to the upper maze without needing to spawn and use " - "the Hookshot targets. This trick supersedes both \"Fire Temple MQ Lower to Upper Lizalfos Maze with " - "Hover Boots\" and \"Fire Temple MQ Lizalfos Maze Side Room without Box\"."); - OPT_TRICK(RT_FIRE_MQ_ABOVE_MAZE_GS, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Fire Temple MQ Above Flame Wall Maze GS from Below with Longshot", "FIGSLS", - "The floor of the room that contains this Skulltula is only solid from above. From the maze below, the " - "Longshot can be shot through the ceiling to obtain the token with two fewer small keys than normal."); - OPT_TRICK(RT_WATER_LONGSHOT_TORCH, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, - "Water Temple Torch Longshot", "WTTorLS", - "Stand on the eastern side of the central pillar and longshot the torches on the bottom level. Swim " - "through the corridor and float up to the top level. This allows access to this area and lower water " - "levels without Iron Boots. The majority of the tricks that allow you to skip Iron Boots in the Water " - "Temple are not going to be relevant unless this trick is first enabled."); - OPT_TRICK(RT_WATER_CRACKED_WALL_HOVERS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, - "Water Temple Cracked Wall with Hover Boots", "WTCrkHB", - "With a midair side-hop while wearing the Hover Boots, you can reach the cracked wall without needing to " - "raise the water up to the middle level."); - OPT_TRICK(RT_WATER_CRACKED_WALL, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Water Temple Cracked Wall with No Additional Items", "WTCrkJmp", - "A precise jumpslash (among other methods) will get you to the cracked wall without needing the Hover " - "Boots or to raise the water to the middle level. This trick supersedes \"Water Temple Cracked Wall with " - "Hover Boots\"."); - OPT_TRICK(RT_WATER_BK_REGION, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Water Temple Boss Key Region with Hover Boots", "WTBKHB", - "With precise Hover Boots movement it is possible to reach the boss key chest's region without needing " - "the Longshot. It is not necessary to take damage from the spikes. The Gold Skulltula Token in the " - "following room can also be obtained with just the Hover Boots."); + OPT_TRICK(RT_FIRE_SOT, RCQUEST_VANILLA, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "FISoTSkp"); + OPT_TRICK(RT_FIRE_STRENGTH, RCQUEST_VANILLA, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "FIStrSkp"); + OPT_TRICK(RT_FIRE_SCARECROW, RCQUEST_VANILLA, RA_FIRE_TEMPLE, { Tricks::Tag::EXPERT }, "PixelShot"); + OPT_TRICK(RT_FIRE_SKIP_FLAME_WALLS, RCQUEST_VANILLA, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "FIRWAL"); + OPT_TRICK(RT_FIRE_MQ_NEAR_BOSS, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::NOVICE }, "FICrtTor"); + OPT_TRICK(RT_FIRE_MQ_BLOCKED_CHEST, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "FIHSSkp"); + OPT_TRICK(RT_FIRE_MQ_BK_CHEST, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "FIBowSkp"); + OPT_TRICK(RT_FIRE_MQ_CLIMB, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::NOVICE }, "FIFirSkp"); + OPT_TRICK(RT_FIRE_MQ_MAZE_SIDE_ROOM, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::NOVICE }, "FIBoxSkp"); + OPT_TRICK(RT_FIRE_MQ_MAZE_HOVERS, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::NOVICE }, "FIMazHB"); + OPT_TRICK(RT_FIRE_MQ_MAZE_JUMP, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "FIMazJmp"); + OPT_TRICK(RT_FIRE_MQ_ABOVE_MAZE_GS, RCQUEST_MQ, RA_FIRE_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "FIGSLS"); + OPT_TRICK(RT_WATER_LONGSHOT_TORCH, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTTorLS"); + OPT_TRICK(RT_WATER_CRACKED_WALL_HOVERS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTCrkHB"); + OPT_TRICK(RT_WATER_CRACKED_WALL, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "WTCrkJmp"); + OPT_TRICK(RT_WATER_BK_REGION, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "WTBKHB"); OPT_TRICK(RT_WATER_NORTH_BASEMENT_LEDGE_JUMP, RCQUEST_BOTH, RA_WATER_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Water Temple North Basement Ledge with Precise Jump", "WTBolLdg", - "In the northern basement there's a ledge from where, in Vanilla Water Temple, boulders roll out into " - "the room. Normally to jump directly to this ledge logically requires the Hover Boots, but with precise " - "jump, it can be done without them. This trick applies to both Vanilla and Master Quest."); + "WTBolLdg"); // Also used in MQ logic, but won't be relevent unless a way to enter tower without irons exists (likely a clip + // swim) - OPT_TRICK(RT_WATER_FW_CENTRAL_GS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, - "Water Temple Central Pillar GS with Farore\'s Wind", "WTGSFW", - "If you set Farore's Wind inside the central pillar and then return to that warp point after raising the " - "water to the highest level, you can obtain this Skulltula Token with Hookshot or Boomerang."); - OPT_TRICK( - RT_WATER_IRONS_CENTRAL_GS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, - "Water Temple Central Pillar GS with Iron Boots", "WTGSIB", - "After opening the middle water level door into the central pillar, the door will stay unbarred so long as you " - "do not leave the room, even if you were to raise the water up to the highest level. With the Iron Boots to go " - "through the door after the water has been raised, you can obtain the Skulltula Token with the Hookshot."); - OPT_TRICK(RT_WATER_CENTRAL_BOW, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::ADVANCED }, - "Water Temple Central Bow Target without Longshot or Hover Boots", "WTBowJmp", - "A very precise Bow shot can hit the eye switch from the floor above. Then, you can jump down into the " - "hallway and make through it before the gate closes. It can also be done as child, using the Slingshot " - "instead of the Bow."); - OPT_TRICK( - RT_WATER_HOOKSHOT_FALLING_PLATFORM_GS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, - "Water Temple Falling Platform Room GS with Hookshot", "WTWfalHS", - "If you stand on the very edge of the platform, this Gold Skulltula can be obtained with only the Hookshot."); - OPT_TRICK( - RT_WATER_RANG_FALLING_PLATFORM_GS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Water Temple Falling Platform Room GS with Boomerang", "WTWfalRng", - "If you stand on the very edge of the platform, this Gold Skulltula can be obtained with only the Boomerang."); - OPT_TRICK(RT_WATER_RIVER_GS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Water Temple River GS without Iron Boots", "WTRvrLS", - "Standing on the exposed ground toward the end of the river, a precise Longshot use can obtain the " - "token. The Longshot cannot normally reach far enough to kill the Skulltula, however. You'll first have " - "to find some other way of killing it."); - OPT_TRICK(RT_WATER_DRAGON_JUMP_DIVE, RCQUEST_BOTH, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, - "Water Temple Dragon Statue Jump Dive", "WTDrgJmp", - "If you come into the dragon statue room from the serpent river, you can sidehop down from above and get " - "into the tunnel without needing either Iron Boots or a Scale. This trick applies to both Vanilla and " - "Master Quest. In Vanilla, you must shoot the switch from above with the Bow, and then quickly get " - "through the tunnel before the gate closes."); - OPT_TRICK(RT_WATER_ADULT_DRAGON, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, - "Water Temple Dragon Statue Switch from Above the Water as Adult", "WTDrgA", - "Normally you need both Hookshot and Iron Boots to hit the switch and swim through the tunnel to get to " - "the chest. But by hitting the switch from dry land, using one of Bombchus, Hookshot, or Bow, it is " - "possible to skip one or both of those requirements. After the gate has been opened, besides just using " - "the Iron Boots, a well-timed dive with at least the Silver Scale could be used to swim through the " - "tunnel. If coming from the serpent river, a jump dive can also be used to get into the tunnel."); - OPT_TRICK(RT_WATER_CHILD_DRAGON, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::ADVANCED }, - "Water Temple Dragon Statue Switch from Above the Water as Child", "WTDrgC", - "It is possible for child to hit the switch from dry land using one of Bombchus, Slingshot or Boomerang. " - "Then, to get to the chest, child can dive through the tunnel using at least the Silver Scale. The " - "timing and positioning of this dive needs to be perfect to actually make it under the gate, and it all " - "needs to be done very quickly to be able to get through before the gate closes. Be sure to enable " - "\"Water Temple Dragon Statue Switch from Above the Water as Adult\" for adult's variant of this trick."); - OPT_TRICK(RT_WATER_MQ_CENTRAL_PILLAR, RCQUEST_MQ, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, - "Water Temple MQ Central Pillar with Fire Arrows", "WTCntFA", - "Slanted torches have misleading hitboxes. Whenever you see a slanted torch jutting out of the wall, you " - "can expect most or all of its hitbox is actually on the other side that wall. This can make slanted " - "torches very finicky to light when using arrows. The torches in the central pillar of MQ Water Temple " - "are a particularly egregious example. Logic normally expects Din's Fire and Song of Time."); - OPT_TRICK( - RT_WATER_IRON_BOOTS_LEDGE_GRAB, RCQUEST_BOTH, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, - "Water Temple Ledge Grab While Surfacing with Iron Boots", "IBSrfLG", - "Diving in front of ledge tapping B to swim up faster, then equipping iron boots while surfacing allows you to " - "ledge grab to the higher ground. This can be used to reach ledge to boss door and vanilla compass chest, or " - "MQ storage room"); - OPT_TRICK(RT_WATER_INVISIBLE_HOOKSHOT_TARGET, RCQUEST_BOTH, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, - "Water Temple Invisible Hookshot Target", "WTTarg", - "Invisible hookshot geometry can be used in MQ to get over the gate that blocks you from going to this " - "Skulltula early, skipping a small key as well as needing Hovers or Scarecrow to reach the locked door.\n" - "In vanilla this can be used to get past without bronze scale."); - OPT_TRICK(RT_WATER_MORPHA_WITHOUT_HOOKSHOT, RCQUEST_BOTH, RA_WATER_TEMPLE, { Tricks::Tag::EXTREME }, - "Water Temple Morpha without Hookshot", "MorphDiff", - "It is possible to slash at Morpha without hookshot."); - OPT_TRICK(RT_LENS_SHADOW, RCQUEST_VANILLA, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, - "Shadow Temple Stationary Objects without Lens of Truth", "STStLoT", - "Removes the requirements for the Lens of Truth in Shadow Temple for most areas in the dungeon except " - "for crossing the moving platform in the huge pit room and for fighting Bongo Bongo."); - OPT_TRICK(RT_LENS_SHADOW_PLATFORM, RCQUEST_VANILLA, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, - "Shadow Temple Invisible Moving Platform without Lens of Truth", "STMvLot", - "Removes the requirements for the Lens of Truth in Shadow Temple to cross the invisible moving platform " - "in the huge pit room in either direction."); - OPT_TRICK(RT_LENS_BONGO, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, - "Shadow Temple Bongo Bongo without Lens of Truth", "BNGLoT", - "Bongo Bongo can be defeated without the use of Lens of Truth, as the hands give a pretty good idea of " - "where the eye is."); - OPT_TRICK(RT_SHADOW_UMBRELLA_HOVER, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::EXPERT }, - "Shadow Temple Stone Umbrella Skip", "STUmbSkp", - "A very precise Hover Boots movement from off of the lower chest can get you on top of the falling " - "spikes without needing to pull the block. Applies to both Vanilla and Master Quest."); + OPT_TRICK(RT_WATER_FW_CENTRAL_GS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTGSFW"); + OPT_TRICK(RT_WATER_IRONS_CENTRAL_GS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTGSIB"); + OPT_TRICK(RT_WATER_CENTRAL_BOW, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::ADVANCED }, "WTBowJmp"); + OPT_TRICK(RT_WATER_HOOKSHOT_FALLING_PLATFORM_GS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, + "WTWfalHS"); + OPT_TRICK(RT_WATER_RANG_FALLING_PLATFORM_GS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::INTERMEDIATE }, + "WTWfalRng"); + OPT_TRICK(RT_WATER_RIVER_GS, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "WTRvrLS"); + OPT_TRICK(RT_WATER_DRAGON_JUMP_DIVE, RCQUEST_BOTH, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTDrgJmp"); + OPT_TRICK(RT_WATER_ADULT_DRAGON, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTDrgA"); + OPT_TRICK(RT_WATER_CHILD_DRAGON, RCQUEST_VANILLA, RA_WATER_TEMPLE, { Tricks::Tag::ADVANCED }, "WTDrgC"); + OPT_TRICK(RT_WATER_MQ_CENTRAL_PILLAR, RCQUEST_MQ, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTCntFA"); + OPT_TRICK(RT_WATER_IRON_BOOTS_LEDGE_GRAB, RCQUEST_BOTH, RA_WATER_TEMPLE, + { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, "IBSrfLG"); + OPT_TRICK(RT_WATER_INVISIBLE_HOOKSHOT_TARGET, RCQUEST_BOTH, RA_WATER_TEMPLE, { Tricks::Tag::NOVICE }, "WTTarg"); + OPT_TRICK(RT_WATER_MORPHA_WITHOUT_HOOKSHOT, RCQUEST_BOTH, RA_WATER_TEMPLE, { Tricks::Tag::EXTREME }, "MorphDiff"); + OPT_TRICK(RT_LENS_SHADOW, RCQUEST_VANILLA, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "STStLoT"); + OPT_TRICK(RT_LENS_SHADOW_PLATFORM, RCQUEST_VANILLA, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "STMvLot"); + OPT_TRICK(RT_LENS_BONGO, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "BNGLoT"); + OPT_TRICK(RT_SHADOW_UMBRELLA_HOVER, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::EXPERT }, "STUmbSkp"); OPT_TRICK(RT_SHADOW_UMBRELLA_CLIP, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, - "Shadow Temple Stone Umbrella Clip", "STUmbClp", - "Backflipping as the falling spikes fall clips above without needing any other requirements. " - "Applies to both Vanilla and Master Quest."); - OPT_TRICK(RT_SHADOW_UMBRELLA_GS, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::EXPERT }, - "Shadow Temple Falling Spikes GS with Hover Boots", "STUmbHB", - "After killing the Skulltula, a very precise Hover Boots movement from off of the lower chest can get " - "you on top of the falling spikes without needing to pull the block. From there, another very precise " - "Hover Boots movement can be used to obtain the token without needing the Hookshot. Applies to both " - "Vanilla and Master Quest."); - OPT_TRICK(RT_SHADOW_FREESTANDING_KEY, RCQUEST_VANILLA, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, - "Shadow Temple Freestanding Key with Bombchu", "STPotChu", - "Release the Bombchu with good timing so that it explodes near the bottom of the pot."); - OPT_TRICK(RT_SHADOW_STATUE, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Shadow Temple River Statue with Bombchu", "STStaChu", - "By sending a Bombchu around the edge of the gorge, you can knock down the statue without needing a Bow. " - "Applies in both Vanilla and MQ Shadow."); - OPT_TRICK(RT_SHADOW_BONGO, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Shadow Temple Bongo Bongo without projectiles", "BngNoPrg", - "Using precise sword slashes, Bongo Bongo can be defeated without using projectiles. This is only " - "relevant in conjunction with Shadow Temple dungeon shortcuts or shuffled boss entrances."); - OPT_TRICK(RT_LENS_SHADOW_MQ, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, - "Shadow Temple MQ Stationary Objects without Lens of Truth", "STMQStLoT", - "Removes the requirements for the Lens of Truth in Shadow Temple MQ for most areas in the dungeon. See " - "\"Shadow Temple MQ Invisible Moving Platform without Lens of Truth\", \"Shadow Temple MQ Invisible " - "Blades Silver Rupees without Lens of Truth\", \"Shadow Temple MQ 2nd Dead Hand without Lens of Truth\", " - "and \"Shadow Temple Bongo Bongo without Lens of Truth\" for exceptions."); - OPT_TRICK(RT_LENS_SHADOW_MQ_INVISIBLE_BLADES, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, - "Shadow Temple MQ Invisible Blades Silver Rupees without Lens of Truth", "STBldLoT", - "Removes the requirement for the Lens of Truth or Nayru's Love in Shadow Temple MQ for the Invisible " - "Blades room Silver Rupee collection."); - OPT_TRICK(RT_LENS_SHADOW_MQ_PLATFORM, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, - "Shadow Temple MQ Invisible Moving Platform without Lens of Truth", "STMQMvLot", - "Removes the requirements for the Lens of Truth in Shadow Temple MQ to cross the invisible moving " - "platform in the huge pit room in either direction."); - OPT_TRICK(RT_LENS_SHADOW_MQ_DEADHAND, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, - "Shadow Temple MQ 2nd Dead Hand without Lens of Truth", "STDHLoT", - "Dead Hand spawns in a random spot within the room. Having Lens removes the hassle of having to comb the " - "room looking for his spawn location."); - OPT_TRICK(RT_SHADOW_MQ_GAP, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Shadow Temple MQ Truth Spinner Gap with Longshot", "STTSLS", - "You can Longshot a torch and jump-slash recoil onto the tongue. It works best if you Longshot the right " - "torch from the left side of the room."); - OPT_TRICK( - RT_SHADOW_MQ_INVISIBLE_BLADES, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Shadow Temple MQ Invisible Blades without Song of Time", "STSoTSkp", - "The Like Like can be used to boost you into the Silver Rupee or Recovery Hearts that normally require Song of " - "Time. This cannot be performed on OHKO since the Like Like does not boost you high enough if you die."); - OPT_TRICK(RT_SHADOW_MQ_HUGE_PIT, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Shadow Temple MQ Lower Huge Pit without Fire Source", "STPitJmp", - "Normally a frozen eye switch spawns some platforms that you can use to climb down, but there's actually " - "a small piece of ground that you can stand on that you can just jump down to."); - OPT_TRICK( - RT_SHADOW_MQ_WINDY_WALKWAY, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Shadow Temple MQ Windy Walkway Reverse without Hover Boots", "STWindSkp", - "It is possible to jump from the alcove in the windy hallway to the middle platform. There are two methods: " - "wait out the fan opposite the door and hold forward, or jump to the right to be pushed by the fan there " - "towards the platform ledge. Note that jumps of this distance are inconsistent, but still possible."); - OPT_TRICK(RT_LENS_SPIRIT, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, - "Spirit Temple without Lens of Truth", "SPLoT", - "Removes the requirements for the Lens of Truth in Spirit Temple."); - OPT_TRICK(RT_SPIRIT_CHILD_CHU, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, - "Spirit Temple Child Side Bridge with Bombchu", "SPBrgChu", - "A carefully-timed Bombchu can hit the switch."); - OPT_TRICK(RT_SPIRIT_WEST_LEDGE, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, - "Spirit Temple Statue Room West Ledge Checks with Boomerang", "SPWeRng", - "By carefully walking onto the upper arm of the statue, it's possible to get a good angle on the " - "Gold Skulltula (In Vanilla) and the farthest pot (In MQ) to collect the checks with Boomerang. " - "The nearest pot in MQ can be reached from the forearm and is always in logic."); - OPT_TRICK(RT_SPIRIT_LOWER_ADULT_SWITCH, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::ADVANCED }, - "Spirit Temple Lower Adult Switch with Bombs", "SPSwtBmb", - "A bomb can be used to hit the switch on the ceiling, but it must be thrown from a particular distance " - "away and with precise timing."); - OPT_TRICK( - RT_SPIRIT_STATUE_JUMP, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Spirit Temple Statue Room Jump from Hands to Upper Ledges", "SPHndJmp", - "A precise jump to obtain the following as adult without needing one of Hover Boots, or Hookshot (in Vanilla) " - "or Song of Time (in MQ): - Spirit Temple Statue Room Northeast Chest - Spirit Temple GS Lobby - Spirit Temple " - "MQ Central Chamber Top Left Pot (Left) - Spirit Temple MQ Central Chamber Top Left Pot (Right)"); + "STUmbClp"); + OPT_TRICK(RT_SHADOW_UMBRELLA_GS, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::EXPERT }, "STUmbHB"); + OPT_TRICK(RT_SHADOW_FREESTANDING_KEY, RCQUEST_VANILLA, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "STPotChu"); + OPT_TRICK(RT_SHADOW_STATUE, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "STStaChu"); + OPT_TRICK(RT_SHADOW_BONGO, RCQUEST_BOTH, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "BngNoPrg"); + OPT_TRICK(RT_LENS_SHADOW_MQ, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "STMQStLoT"); + OPT_TRICK(RT_LENS_SHADOW_MQ_INVISIBLE_BLADES, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "STBldLoT"); + OPT_TRICK(RT_LENS_SHADOW_MQ_PLATFORM, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "STMQMvLot"); + OPT_TRICK(RT_LENS_SHADOW_MQ_DEADHAND, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::NOVICE }, "STDHLoT"); + OPT_TRICK(RT_SHADOW_MQ_GAP, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "STTSLS"); + OPT_TRICK(RT_SHADOW_MQ_INVISIBLE_BLADES, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "STSoTSkp"); + OPT_TRICK(RT_SHADOW_MQ_HUGE_PIT, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "STPitJmp"); + OPT_TRICK(RT_SHADOW_MQ_WINDY_WALKWAY, RCQUEST_MQ, RA_SHADOW_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "STWindSkp"); + OPT_TRICK(RT_LENS_SPIRIT, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, "SPLoT"); + OPT_TRICK(RT_SPIRIT_CHILD_CHU, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, "SPBrgChu"); + OPT_TRICK(RT_SPIRIT_WEST_LEDGE, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, "SPWeRng"); + OPT_TRICK(RT_SPIRIT_LOWER_ADULT_SWITCH, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::ADVANCED }, "SPSwtBmb"); + OPT_TRICK(RT_SPIRIT_STATUE_JUMP, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "SPHndJmp"); + // disabled since "Spirit Temple boss shortcuts" (pre-lowers the platform where you break the statues face) isn't a - // setting in ship OPT_TRICK(RT_SPIRIT_PLATFORM_HOOKSHOT, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, - // {Tricks::Tag::INTERMEDIATE}, "Spirit Temple Main Room Hookshot to Boss Platform", "Precise hookshot aiming at the - // platform chains can be used to reach the boss platform from the middle landings. Using a jumpslash immediately - // after reaching a chain makes aiming more lenient. Relevant only when Spirit Temple boss shortcuts are on."); - OPT_TRICK(RT_SPIRIT_MAP_CHEST, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, - "Spirit Temple Map Chest with Bow", "SPMapBow", - "To get a line of sight from the upper torch to the map chest torches, you must pull an Armos statue all " - "the way up the stairs."); - OPT_TRICK(RT_SPIRIT_SUN_CHEST, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::ADVANCED }, - "Spirit Temple Sun Block Room Chest with Bow", "SPSUNBOW", - "Using the blocks in the room as platforms you can get lines of sight to all three torches. The timer on " - "the torches is quite short so you must move quickly in order to light all three.\n" - "A backflip can be used instead to light torches without pushing blocks."); - OPT_TRICK( - RT_SPIRIT_WALL, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Spirit Temple Shifting Wall with No Additional Items", "SPWall", - "Logic normally guarantees a way of dealing with both the Beamos and the Walltula before climbing the wall."); - OPT_TRICK(RT_LENS_SPIRIT_MQ, RCQUEST_MQ, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, - "Spirit Temple MQ without Lens of Truth", "SPMQLoT", - "Removes the requirements for the Lens of Truth in Spirit Temple MQ."); - OPT_TRICK(RT_SPIRIT_MQ_SUN_BLOCK_SOT, RCQUEST_MQ, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Spirit Temple MQ Sun Block Room as Child without Song of Time", "SPBluSkp", - "While adult can easily jump directly to the switch that unbars the door to the sun block room, child " - "Link cannot make the jump without spawning a Song of Time block to jump from. You can skip this by " - "throwing the crate down onto the switch from above, which does unbar the door, however the crate " - "immediately breaks, so you must move quickly to get through the door before it closes back up."); - OPT_TRICK(RT_SPIRIT_MQ_SUN_BLOCK_GS, RCQUEST_MQ, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Spirit Temple MQ Sun Block Room GS with Boomerang", "SPBlkGS", - "Throw the Boomerang in such a way that it curves through the side of the glass block to hit the Gold " - "Skulltula."); - OPT_TRICK(RT_SPIRIT_MQ_LOWER_ADULT, RCQUEST_MQ, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Spirit Temple MQ Lower Adult without Fire Arrows", "SPTorDin", - "By standing in a precise position it is possible to light two of the torches with a single use of " - "Din\'s Fire. This saves enough time to be able to light all three torches with only Din\'s Fire."); - OPT_TRICK(RT_SPIRIT_MQ_FROZEN_EYE, RCQUEST_MQ, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, - "Spirit Temple MQ Frozen Eye Switch without Fire", "SPFEBow", - "You can melt the ice by shooting an arrow through a torch. The only way to find a line of sight for " - "this shot is to first spawn a Song of Time block, and then stand on the very edge of it."); - OPT_TRICK(RT_ICE_STALAGMITE_CLIP, RCQUEST_BOTH, RA_ICE_CAVERN, { Tricks::Tag::NOVICE }, - "Ice Cavern Stalagmite Clips", "StalClp", - "Most stalagmites blocking path in Ice Cavern can be clipped past with basic movement. Also applies to " - "Water Trial."); - OPT_TRICK(RT_ICE_STALAGMITE_HOOKSHOT, RCQUEST_BOTH, RA_ICE_CAVERN, { Tricks::Tag::NOVICE }, - "Ice Cavern Stalagmites with Hookshot", "StalHS", - "Shooting stalagmites with hookshot in the right way also breaks them. Also applies to Water Trial."); - // RANDOTO sweep trick descriptions and make sure they match a post-refactor, post shuffles reality - OPT_TRICK(RT_ICE_BLOCK_GS, RCQUEST_VANILLA, RA_ICE_CAVERN, { Tricks::Tag::INTERMEDIATE }, - "Ice Cavern Block Room GS with Hover Boots", "ICBlkHB", - "The Hover Boots can be used to get in front of the Skulltula to kill it with a jumpslash. Then, the " - "Hover Boots can again be used to obtain the Token, all without Hookshot or Boomerang."); - OPT_TRICK(RT_ICE_MQ_RED_ICE_GS, RCQUEST_MQ, RA_ICE_CAVERN, { Tricks::Tag::INTERMEDIATE }, - "Ice Cavern MQ Red Ice GS without Song of Time", "ICNoSoT", - "If you side-hop into the perfect position, you can briefly stand on the platform with the red ice just " - "long enough to dump some blue fire."); - OPT_TRICK(RT_LENS_GTG, RCQUEST_VANILLA, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::NOVICE }, - "Gerudo Training Ground without Lens of Truth", "GTGLoT", - "Removes the requirements for the Lens of Truth in Gerudo Training Ground."); + // setting in ship + + // OPT_TRICK(RT_SPIRIT_PLATFORM_HOOKSHOT, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }); + + OPT_TRICK(RT_SPIRIT_MAP_CHEST, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, "SPMapBow"); + OPT_TRICK(RT_SPIRIT_SUN_CHEST, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::ADVANCED }, "SPSUNBOW"); + OPT_TRICK(RT_SPIRIT_WALL, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "SPWall"); + OPT_TRICK(RT_LENS_SPIRIT_MQ, RCQUEST_MQ, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, "SPMQLoT"); + OPT_TRICK(RT_SPIRIT_MQ_SUN_BLOCK_SOT, RCQUEST_MQ, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "SPBluSkp"); + OPT_TRICK(RT_SPIRIT_MQ_SUN_BLOCK_GS, RCQUEST_MQ, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "SPBlkGS"); + OPT_TRICK(RT_SPIRIT_MQ_LOWER_ADULT, RCQUEST_MQ, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, "SPTorDin"); + OPT_TRICK(RT_SPIRIT_MQ_FROZEN_EYE, RCQUEST_MQ, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, "SPFEBow"); + OPT_TRICK(RT_ICE_STALAGMITE_CLIP, RCQUEST_BOTH, RA_ICE_CAVERN, { Tricks::Tag::NOVICE }, "StalClp"); + OPT_TRICK(RT_ICE_STALAGMITE_HOOKSHOT, RCQUEST_BOTH, RA_ICE_CAVERN, { Tricks::Tag::NOVICE }, "StalHS"); + OPT_TRICK(RT_ICE_BLOCK_GS, RCQUEST_VANILLA, RA_ICE_CAVERN, { Tricks::Tag::INTERMEDIATE }, "ICBlkHB"); + OPT_TRICK(RT_ICE_MQ_RED_ICE_GS, RCQUEST_MQ, RA_ICE_CAVERN, { Tricks::Tag::INTERMEDIATE }, "ICNoSoT"); + OPT_TRICK(RT_LENS_GTG, RCQUEST_VANILLA, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::NOVICE }, "GTGLoT"); OPT_TRICK(RT_GTG_WITHOUT_HOOKSHOT, RCQUEST_VANILLA, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::INTERMEDIATE }, - "Gerudo Training Ground Left Side Silver Rupees without Hookshot", "GTGNoHS", - "After collecting the rest of the Silver Rupees in the room, you can reach the final Silver Rupee on the " - "ceiling by being pulled up into it after getting grabbed by the Wallmaster. Then, you must also reach " - "the exit of the room without the use of the Hookshot. If you move quickly you can sneak past the edge " - "of a flame wall before it can rise up to block you. To do so without taking damage is more precise."); - OPT_TRICK(RT_GTG_FAKE_WALL, RCQUEST_BOTH, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::NOVICE }, - "Reach Gerudo Training Ground Fake Wall Ledge with Hover Boots", "GTGWallHB", - "A precise Hover Boots use from the top of the chest can allow you to grab the ledge without needing the " - "usual requirements. In Master Quest, this always skips a Song of Time requirement. In Vanilla, this " - "skips a Hookshot requirement, but is only relevant if \"Gerudo Training Ground Left Side Silver Rupees " - "without Hookshot\" is enabled."); - OPT_TRICK(RT_GTG_LAVA_JUMP, RCQUEST_BOTH, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::INTERMEDIATE }, - "Gerudo Training Grounds Itemless Lava Room Jump", "GTGLavaJmp", - "A precise rolling jump can be used to jump between all but the furthest platforms in the lava room."); - OPT_TRICK(RT_LENS_GTG_MQ, RCQUEST_MQ, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::NOVICE }, - "Gerudo Training Ground MQ without Lens of Truth", "GTGMQLoT", - "Removes the requirements for the Lens of Truth in Gerudo Training Ground MQ."); - OPT_TRICK(RT_GTG_MQ_WITH_HOOKSHOT, RCQUEST_MQ, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::NOVICE }, - "Gerudo Training Ground MQ Left Side Silver Rupees with Hookshot", "GTGMQHS", - "The highest Silver Rupee can be obtained by hookshooting the target and then immediately jumpslashing " - "toward the Rupee."); + "GTGNoHS"); + OPT_TRICK(RT_GTG_FAKE_WALL, RCQUEST_BOTH, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::NOVICE }, "GTGWallHB"); + OPT_TRICK(RT_GTG_LAVA_JUMP, RCQUEST_BOTH, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::INTERMEDIATE }, "GTGLavaJmp"); + OPT_TRICK(RT_GTG_STATUE_JUMP, RCQUEST_BOTH, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::INTERMEDIATE }, "GTGStJmp"); + OPT_TRICK(RT_LENS_GTG_MQ, RCQUEST_MQ, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::NOVICE }, "GTGMQLoT"); + OPT_TRICK(RT_GTG_MQ_WITH_HOOKSHOT, RCQUEST_MQ, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::NOVICE }, "GTGMQHS"); OPT_TRICK(RT_GTG_MQ_WITHOUT_HOOKSHOT, RCQUEST_MQ, RA_GERUDO_TRAINING_GROUND, { Tricks::Tag::INTERMEDIATE }, - "Gerudo Training Ground MQ Left Side Silver Rupees without Hookshot", "GTGMQNoHS", - "After collecting the rest of the Silver Rupees in the room, you can reach the final Silver Rupee on the " - "ceiling by being pulled up into it after getting grabbed by the Wallmaster. The Wallmaster will not " - "track you to directly underneath the rupee. You should take the last step to be under the rupee after " - "the Wallmaster has begun its attempt to grab you. Also included with this trick is that fact that the " - "switch that unbars the door to the final chest of GTG can be hit without a projectile, using a precise " - "jumpslash. This trick supersedes \"Gerudo Training Ground MQ Left Side Silver Rupees with Hookshot\"."); - OPT_TRICK(RT_LENS_GANON, RCQUEST_VANILLA, RA_GANONS_CASTLE, { Tricks::Tag::NOVICE }, - "Ganon\'s Castle without Lens of Truth", "GCLoT", - "Removes the requirements for the Lens of Truth in Ganon's Castle."); - OPT_TRICK(RT_GANON_SPIRIT_TRIAL_HOOKSHOT, RCQUEST_VANILLA, RA_GANONS_CASTLE, { Tricks::Tag::NOVICE }, - "Spirit Trial without Hookshot", "GCNoHS", - "The highest rupee can be obtained as adult by performing a precise jump and a well-timed jumpslash " - "off of an Armos."); - OPT_TRICK(RT_LENS_GANON_MQ, RCQUEST_MQ, RA_GANONS_CASTLE, { Tricks::Tag::NOVICE }, - "Ganon\'s Castle MQ without Lens of Truth", "GCMQLoT", - "Removes the requirements for the Lens of Truth in Ganon's Castle MQ."); - OPT_TRICK(RT_GANON_MQ_FIRE_TRIAL, RCQUEST_MQ, RA_GANONS_CASTLE, { Tricks::Tag::ADVANCED }, - "Fire Trial MQ with Hookshot", "GCFTHS", - "It's possible to hook the target at the end of fire trial with just Hookshot, but it requires precise " - "aim and perfect positioning. The main difficulty comes from getting on the very corner of the obelisk " - "without falling into the lava."); - OPT_TRICK(RT_GANON_MQ_SHADOW_TRIAL, RCQUEST_MQ, RA_GANONS_CASTLE, { Tricks::Tag::NOVICE }, - "Shadow Trial MQ Torch with Bow", "GCSTBow", - "You can light the torch in this room without a fire source by shooting an arrow through the lit torch " - "at the beginning of the room. Because the room is so dark and the unlit torch is so far away, it can be " - "difficult to aim the shot correctly."); - OPT_TRICK(RT_GANON_MQ_LIGHT_TRIAL, RCQUEST_MQ, RA_GANONS_CASTLE, { Tricks::Tag::INTERMEDIATE }, - "Light Trial MQ without Hookshot", "GCFirWal", - "If you move quickly you can sneak past the edge of a flame wall before it can rise up to block you. In " - "this case to do it without taking damage is especially precise."); + "GTGMQNoHS"); + OPT_TRICK(RT_LENS_GANON, RCQUEST_VANILLA, RA_GANONS_CASTLE, { Tricks::Tag::NOVICE }, "GCLoT"); + OPT_TRICK(RT_GANON_SPIRIT_TRIAL_HOOKSHOT, RCQUEST_VANILLA, RA_GANONS_CASTLE, { Tricks::Tag::NOVICE }, "GCNoHS"); + OPT_TRICK(RT_LENS_GANON_MQ, RCQUEST_MQ, RA_GANONS_CASTLE, { Tricks::Tag::NOVICE }, "GCMQLoT"); + OPT_TRICK(RT_GANON_MQ_FIRE_TRIAL, RCQUEST_MQ, RA_GANONS_CASTLE, { Tricks::Tag::ADVANCED }, "GCFTHS"); + OPT_TRICK(RT_GANON_MQ_SHADOW_TRIAL, RCQUEST_MQ, RA_GANONS_CASTLE, { Tricks::Tag::NOVICE }, "GCSTBow"); + OPT_TRICK(RT_GANON_MQ_LIGHT_TRIAL, RCQUEST_MQ, RA_GANONS_CASTLE, { Tricks::Tag::INTERMEDIATE }, "GCFirWal"); for (auto trick : mTrickSettings) { if (trick.GetNameTag() != "") { @@ -2446,6 +1856,7 @@ void Settings::CreateOptions() { &mOptions[RSK_FISHSANITY_POND_COUNT], &mOptions[RSK_FISHSANITY_AGE_SPLIT], &mOptions[RSK_SHUFFLE_FREESTANDING], + &mOptions[RSK_SHUFFLE_WONDER_ITEMS], &mOptions[RSK_SHUFFLE_BEEHIVES], &mOptions[RSK_SHUFFLE_COWS], &mOptions[RSK_SHUFFLE_POTS], @@ -2453,6 +1864,9 @@ void Settings::CreateOptions() { &mOptions[RSK_SHUFFLE_CRATES], &mOptions[RSK_SHUFFLE_TREES], &mOptions[RSK_SHUFFLE_BUSHES], + &mOptions[RSK_SHUFFLE_ICICLES], + &mOptions[RSK_SHUFFLE_RED_ICE], + &mOptions[RSK_SHUFFLE_SIGNS], &mOptions[RSK_SHUFFLE_FROG_SONG_RUPEES], &mOptions[RSK_SHUFFLE_ADULT_TRADE], &mOptions[RSK_SHUFFLE_100_GS_REWARD], @@ -2460,6 +1874,7 @@ void Settings::CreateOptions() { &mOptions[RSK_SHUFFLE_STONE_FAIRIES], &mOptions[RSK_SHUFFLE_BEAN_FAIRIES], &mOptions[RSK_SHUFFLE_SONG_FAIRIES], + &mOptions[RSK_SHUFFLE_BUTTERFLY_FAIRIES], }, WidgetContainerType::SECTION); mOptionGroups[RSG_MENU_COLUMN_BASIC_SHUFFLES] = @@ -2501,6 +1916,7 @@ void Settings::CreateOptions() { &mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT], &mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT], &mOptions[RSK_MERCHANT_PRICES_AFFORDABLE], + &mOptions[RSK_SHUFFLE_BEGGAR], }, WidgetContainerType::SECTION); mOptionGroups[RSG_MENU_COLUMN_SHOP_SHUFFLES] = @@ -2733,6 +2149,9 @@ void Settings::CreateOptions() { &mOptions[RSK_SHUFFLE_CRATES], &mOptions[RSK_SHUFFLE_TREES], &mOptions[RSK_SHUFFLE_BUSHES], + &mOptions[RSK_SHUFFLE_ICICLES], + &mOptions[RSK_SHUFFLE_RED_ICE], + &mOptions[RSK_SHUFFLE_SIGNS], &mOptions[RSK_SHUFFLE_KOKIRI_SWORD], &mOptions[RSK_SHUFFLE_OCARINA], &mOptions[RSK_SHUFFLE_OCARINA_BUTTONS], @@ -2755,6 +2174,7 @@ void Settings::CreateOptions() { &mOptions[RSK_MERCHANT_PRICES_GIANT_WALLET_WEIGHT], &mOptions[RSK_MERCHANT_PRICES_TYCOON_WALLET_WEIGHT], &mOptions[RSK_MERCHANT_PRICES_AFFORDABLE], + &mOptions[RSK_SHUFFLE_BEGGAR], &mOptions[RSK_SHUFFLE_FROG_SONG_RUPEES], &mOptions[RSK_SHUFFLE_ADULT_TRADE], &mOptions[RSK_SHUFFLE_CHEST_MINIGAME], @@ -2765,10 +2185,12 @@ void Settings::CreateOptions() { &mOptions[RSK_SHUFFLE_DEKU_STICK_BAG], &mOptions[RSK_SHUFFLE_DEKU_NUT_BAG], &mOptions[RSK_SHUFFLE_FREESTANDING], + &mOptions[RSK_SHUFFLE_WONDER_ITEMS], &mOptions[RSK_SHUFFLE_FOUNTAIN_FAIRIES], &mOptions[RSK_SHUFFLE_STONE_FAIRIES], &mOptions[RSK_SHUFFLE_BEAN_FAIRIES], &mOptions[RSK_SHUFFLE_SONG_FAIRIES], + &mOptions[RSK_SHUFFLE_BUTTERFLY_FAIRIES], }); mOptionGroups[RSG_SHUFFLE_DUNGEON_ITEMS] = OptionGroup("Shuffle Dungeon Items", { @@ -3475,6 +2897,48 @@ void Settings::SetAllToContext() { } } +void Settings::RandomizeAllSettings() { + // Randomize all settings except tricks + for (int i = 0; i < RSK_MAX; i++) { + switch (static_cast(i)) { + case RSK_STARTING_SKULLTULA_TOKEN: + case RSK_STARTING_HEARTS: + case RSK_STARTING_ZELDAS_LULLABY: + case RSK_STARTING_EPONAS_SONG: + case RSK_STARTING_SARIAS_SONG: + case RSK_STARTING_SUNS_SONG: + case RSK_STARTING_SONG_OF_TIME: + case RSK_STARTING_SONG_OF_STORMS: + case RSK_STARTING_MINUET_OF_FOREST: + case RSK_STARTING_BOLERO_OF_FIRE: + case RSK_STARTING_SERENADE_OF_WATER: + case RSK_STARTING_REQUIEM_OF_SPIRIT: + case RSK_STARTING_NOCTURNE_OF_SHADOW: + case RSK_STARTING_PRELUDE_OF_LIGHT: + continue; + default: + break; + } + + auto key = static_cast(i); + Option& option = mOptions[key]; + + if (option.GetOptionCount() == 0) { + continue; + } + + uint8_t randomIndex = Random(0, static_cast(option.GetOptionCount())); + + option.SetContextIndex(randomIndex); + if (!option.GetCVarName().empty()) { + CVarSetInteger(option.GetCVarName().c_str(), randomIndex); + } + option.RunCallback(); + } + + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); +} + std::shared_ptr Settings::GetInstance() { if (mInstance == nullptr) { mInstance = std::make_shared(); diff --git a/soh/soh/Enhancements/randomizer/settings.h b/soh/soh/Enhancements/randomizer/settings.h index e77d9d864fc..ecef3bfa0de 100644 --- a/soh/soh/Enhancements/randomizer/settings.h +++ b/soh/soh/Enhancements/randomizer/settings.h @@ -135,6 +135,13 @@ class Settings { */ void SetAllToContext(); + /** + * @brief Randomizes all randomizer settings (excluding tricks) to random valid values. + * This function iterates through all options and sets them to a random index within + * their valid range. + */ + void RandomizeAllSettings(); + static std::shared_ptr GetInstance(); private: diff --git a/soh/soh/Enhancements/randomizer/static_data.h b/soh/soh/Enhancements/randomizer/static_data.h index ccc895d7bfe..c210385d329 100644 --- a/soh/soh/Enhancements/randomizer/static_data.h +++ b/soh/soh/Enhancements/randomizer/static_data.h @@ -62,6 +62,11 @@ class StaticData { static void RegisterGrassLocations(); static void RegisterCrateLocations(); static void RegisterTreeLocations(); + static void RegisterSignLocations(); + static void RegisterWonderItemLocations(); + static void RegisterBeggarLocations(); + static void RegisterIcicleLocations(); + static void RegisterRedIceLocations(); static void InitHashMaps(); static std::array, 17> randomizerFishingPondFish; static std::unordered_map randomizerGrottoFishMap; diff --git a/soh/soh/Network/Anchor/Anchor.h b/soh/soh/Network/Anchor/Anchor.h index 8476d8c3649..a7277a0a71c 100644 --- a/soh/soh/Network/Anchor/Anchor.h +++ b/soh/soh/Network/Anchor/Anchor.h @@ -29,6 +29,7 @@ typedef struct { bool isSaveLoaded; bool isGameComplete; s16 sceneNum; + s8 curRoomNum; s32 entranceIndex; // Only available in PLAYER_UPDATE packets diff --git a/soh/soh/Network/Anchor/HookHandlers.cpp b/soh/soh/Network/Anchor/HookHandlers.cpp index c620ee30e1c..f6718c4cbc4 100644 --- a/soh/soh/Network/Anchor/HookHandlers.cpp +++ b/soh/soh/Network/Anchor/HookHandlers.cpp @@ -1,6 +1,9 @@ #include "Anchor.h" #include +#include "soh/Enhancements/cosmetics/cosmeticsTypes.h" #include "soh/Enhancements/game-interactor/GameInteractor.h" +#include "soh/frame_interpolation.h" +#include "soh/OTRGlobals.h" extern "C" { #include "variables.h" @@ -28,16 +31,18 @@ extern "C" { #include "src/overlays/actors/ovl_Obj_Hamishi/z_obj_hamishi.h" #include "src/overlays/actors/ovl_Bg_Hidan_Dalm/z_bg_hidan_dalm.h" #include "src/overlays/actors/ovl_Bg_Hidan_Kowarerukabe/z_bg_hidan_kowarerukabe.h" +#include "objects/gameplay_keep/gameplay_keep.h" extern PlayState* gPlayState; +extern MapData* gMapData; void func_8086ED70(BgBombwall* bgBombwall, PlayState* play); void BgBreakwall_Wait(BgBreakwall* bgBreakwall, PlayState* play); void func_80883000(BgHakaZou* bgHakaZou, PlayState* play); void func_808887C4(BgHidanHamstep* bgHidanHamstep, PlayState* play); void func_808896B8(BgHidanHrock* bgHidanHrock, PlayState* play); -void func_8089107C(BgIceShelter* bgIceShelter, PlayState* play); -void func_808911BC(BgIceShelter* bgIceShelter); +void BgIceShelter_Idle(BgIceShelter* bgIceShelter, PlayState* play); +void BgIceShelter_SetupMelt(BgIceShelter* bgIceShelter); void ObjBombiwa_Break(ObjBombiwa* objBombiwa, PlayState* play); void ObjHamishi_Break(ObjHamishi* objHamishi, PlayState* play); void BgJyaBombchuiwa_WaitForExplosion(BgJyaBombchuiwa* bgJyaBombchuiwa, PlayState* play); @@ -48,6 +53,8 @@ void BgYdanSp_FloorWebIdle(BgYdanSp* bgYdanSp, PlayState* play); void BgYdanSp_WallWebIdle(BgYdanSp* bgYdanSp, PlayState* play); void BgYdanSp_BurnWeb(BgYdanSp* bgYdanSp, PlayState* play); void EnDoor_Idle(EnDoor* enDoor, PlayState* play); +float OTRGetDimensionFromLeftEdge(float v); +float OTRGetDimensionFromRightEdge(float v); } void Anchor::RegisterHooks() { @@ -234,8 +241,8 @@ void Anchor::RegisterHooks() { COND_ID_HOOK(ShouldActorUpdate, ACTOR_BG_ICE_SHELTER, isConnected, [&](void* refActor, bool* should) { BgIceShelter* actor = static_cast(refActor); - if (actor->actionFunc == func_8089107C && Flags_GetSwitch(gPlayState, actor->dyna.actor.params & 0x3F)) { - func_808911BC(actor); + if (actor->actionFunc == BgIceShelter_Idle && Flags_GetSwitch(gPlayState, actor->dyna.actor.params & 0x3F)) { + BgIceShelter_SetupMelt(actor); Audio_PlayActorSound2(&actor->dyna.actor, NA_SE_EV_ICE_MELT); } }); @@ -393,4 +400,149 @@ void Anchor::RegisterHooks() { }); // #endregion + + // #region Hooks for visual effects that don't affect gameplay + + struct CompassIcon { + Vec3f pos; + Vec3s rot; + float scale; + Color_RGB8 color; + }; + + COND_HOOK(OnMinimapDrawCompassIcons, isConnected, [&]() { + if (!CVarGetInteger(CVAR_REMOTE_ANCHOR("ShowOtherPlayersOnMinimap"), 1) || + Anchor::Instance->roomState.showLocationsMode == 0) { + return; + } + + std::vector compassIcons; + + bool isInDungeon = gPlayState->sceneNum == SCENE_DEKU_TREE || gPlayState->sceneNum == SCENE_DODONGOS_CAVERN || + gPlayState->sceneNum == SCENE_JABU_JABU || gPlayState->sceneNum == SCENE_FOREST_TEMPLE || + gPlayState->sceneNum == SCENE_FIRE_TEMPLE || gPlayState->sceneNum == SCENE_WATER_TEMPLE || + gPlayState->sceneNum == SCENE_SPIRIT_TEMPLE || gPlayState->sceneNum == SCENE_SHADOW_TEMPLE || + gPlayState->sceneNum == SCENE_BOTTOM_OF_THE_WELL || gPlayState->sceneNum == SCENE_ICE_CAVERN; + std::string teamId = CVarGetString(CVAR_REMOTE_ANCHOR("TeamId"), "default"); + + // When transitioning to a new room via a door, curRoom.num updates immediately but the minimap still shows the + // previous room while fading out + s8 displayedRoomNum = + gPlayState->roomCtx.prevRoom.num >= 0 ? gPlayState->roomCtx.prevRoom.num : gPlayState->roomCtx.curRoom.num; + + for (auto& [clientId, client] : Anchor::Instance->clients) { + // Show compass icons for other players in the current scene. Also require them to be in the current room + // within dungeons. If showLocationsMode isn't all players (2), only show compass icons for players of the + // same team + if (!client.self && client.online && client.player && client.sceneNum == gPlayState->sceneNum && + (!isInDungeon || client.curRoomNum == displayedRoomNum) && + (Anchor::Instance->roomState.showLocationsMode == 2 || client.teamId == teamId)) { + compassIcons.push_back( + CompassIcon{ client.player->actor.world.pos, client.player->actor.shape.rot, 0.3f, client.color }); + } + } + + // The local player's compass icon is always last so it gets drawn above the others + Player* player = GET_PLAYER(gPlayState); + compassIcons.push_back(CompassIcon{ player->actor.world.pos, player->actor.shape.rot, 0.4f, + CVarGetColor24(CVAR_REMOTE_ANCHOR("Color.Value"), { 100, 255, 100 }) }); + + // Adapted internals of Minimap_DrawCompassIcons() + s16 leftMinimapMargin = CVarGetInteger(CVAR_COSMETIC("HUD.Margin.L"), 0); + s16 rightMinimapMargin = CVarGetInteger(CVAR_COSMETIC("HUD.Margin.R"), 0); + s16 bottomMinimapMargin = CVarGetInteger(CVAR_COSMETIC("HUD.Margin.B"), 0); + + s16 xMarginsMinimap; + s16 yMarginsMinimap; + if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.UseMargins"), 0) != 0) { + if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosType"), 0) == ORIGINAL_LOCATION) { + xMarginsMinimap = rightMinimapMargin; + } + yMarginsMinimap = bottomMinimapMargin; + } else { + xMarginsMinimap = 0; + yMarginsMinimap = 0; + } + + s16 mapWidth = isInDungeon ? R_DGN_MINIMAP_X : R_OW_MINIMAP_X; + s16 mapStartPosX = isInDungeon ? 96 : gMapData->owMinimapWidth[R_MAP_INDEX]; + + OPEN_DISPS(gPlayState->state.gfxCtx); + Gfx_SetupDL_42Overlay(gPlayState->state.gfxCtx); + + for (auto& compassIcon : compassIcons) { + gSPMatrix(OVERLAY_DISP++, &gMtxClear, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 255); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_PRIMITIVE, G_CC_PRIMITIVE); + + // The compass offset value is a factor of 10 compared to N64 screen pixels and originates in the center of + // the screen Compute the additional mirror offset value by normalizing the original offset position and + // taking it's distance to the center of the map, duplicating that result and casting back to a factor of 10 + s16 mirrorOffset = + ((mapWidth / 2) - ((R_COMPASS_OFFSET_X / 10) - (mapStartPosX - SCREEN_WIDTH / 2))) * 2 * 10; + + s16 tempX = (s16)compassIcon.pos.x; + s16 tempZ = (s16)compassIcon.pos.z; + tempX /= R_COMPASS_SCALE_X * (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0) ? -1 : 1); + tempZ /= R_COMPASS_SCALE_Y; + + s16 tempXOffset = + R_COMPASS_OFFSET_X + (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0) ? mirrorOffset : 0); + if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosType"), 0) != ORIGINAL_LOCATION) { + if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosType"), 0) == ANCHOR_LEFT) { + if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.UseMargins"), 0) != 0) { + xMarginsMinimap = leftMinimapMargin; + }; + Matrix_Translate( + OTRGetDimensionFromLeftEdge((tempXOffset + (xMarginsMinimap * 10) + tempX + + (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosX"), 0) * 10)) / + 10.0f), + (R_COMPASS_OFFSET_Y + ((yMarginsMinimap * 10) * -1) - tempZ + + ((CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosY"), 0) * 10) * -1)) / + 10.0f, + 0.0f, MTXMODE_NEW); + } else if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosType"), 0) == ANCHOR_RIGHT) { + if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.UseMargins"), 0) != 0) { + xMarginsMinimap = rightMinimapMargin; + }; + Matrix_Translate( + OTRGetDimensionFromRightEdge((tempXOffset + (xMarginsMinimap * 10) + tempX + + (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosX"), 0) * 10)) / + 10.0f), + (R_COMPASS_OFFSET_Y + ((yMarginsMinimap * 10) * -1) - tempZ + + ((CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosY"), 0) * 10) * -1)) / + 10.0f, + 0.0f, MTXMODE_NEW); + } else if (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosType"), 0) == ANCHOR_NONE) { + Matrix_Translate( + (tempXOffset + tempX + (CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosX"), 0) * 10) / 10.0f), + (R_COMPASS_OFFSET_Y + ((yMarginsMinimap * 10) * -1) - tempZ + + ((CVarGetInteger(CVAR_COSMETIC("HUD.Minimap.PosY"), 0) * 10) * -1)) / + 10.0f, + 0.0f, MTXMODE_NEW); + } + } else { + Matrix_Translate(OTRGetDimensionFromRightEdge((tempXOffset + (xMarginsMinimap * 10) + tempX) / 10.0f), + (R_COMPASS_OFFSET_Y + ((yMarginsMinimap * 10) * -1) - tempZ) / 10.0f, 0.0f, + MTXMODE_NEW); + } + Matrix_Scale(compassIcon.scale, compassIcon.scale, compassIcon.scale, MTXMODE_APPLY); + Matrix_RotateX(-1.6f, MTXMODE_APPLY); + s16 rotation = ((0x7FFF - compassIcon.rot.y) / 0x400) * + (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0) ? -1 : 1); + Matrix_RotateY(rotation / 10.0f, MTXMODE_APPLY); + gSPMatrix(OVERLAY_DISP++, MATRIX_NEWMTX(gPlayState->state.gfxCtx), + G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0xFF, compassIcon.color.r, compassIcon.color.g, compassIcon.color.b, + 255); + gSPDisplayList(OVERLAY_DISP++, (Gfx*)gCompassArrowDL); + } + + CLOSE_DISPS(gPlayState->state.gfxCtx); + }); + + // #endregion } diff --git a/soh/soh/Network/Anchor/JsonConversions.hpp b/soh/soh/Network/Anchor/JsonConversions.hpp index 8b28ba40e15..69ad1313607 100644 --- a/soh/soh/Network/Anchor/JsonConversions.hpp +++ b/soh/soh/Network/Anchor/JsonConversions.hpp @@ -62,6 +62,7 @@ inline void from_json(const json& j, AnchorClient& client) { client.isSaveLoaded = j.value("isSaveLoaded", false); client.isGameComplete = j.value("isGameComplete", false); client.sceneNum = j.value("sceneNum", (s16)SCENE_ID_MAX); + client.curRoomNum = j.value("curRoomNum", (s8)-1); client.entranceIndex = j.value("entranceIndex", (s32)0); client.self = j.value("self", false); } diff --git a/soh/soh/Network/Anchor/Menu.cpp b/soh/soh/Network/Anchor/Menu.cpp index 77b544fb3df..4a974532e4f 100644 --- a/soh/soh/Network/Anchor/Menu.cpp +++ b/soh/soh/Network/Anchor/Menu.cpp @@ -139,6 +139,24 @@ void AnchorMainMenu(WidgetInfo& info) { ImGui::SameLine(); UIWidgets::WindowButton("Toggle Anchor Room Window", CVAR_WINDOW("AnchorRoom"), SohGui::mAnchorRoomWindow); + + ImGui::Spacing(); + + bool hideLocations = Anchor::Instance->roomState.showLocationsMode == 0; + ImGui::BeginDisabled(hideLocations); + UIWidgets::CVarCheckbox( + "Show Other Players on Minimap", CVAR_REMOTE_ANCHOR("ShowOtherPlayersOnMinimap"), + UIWidgets::CheckboxOptions() + .Color(THEME_COLOR) + .DefaultValue(true) + .Tooltip(!hideLocations + ? "Other players will appear on the minimap in areas where you have the compass. " + "Visibility is restricted according to the Show Locations mode for the room." + : "Cannot show other players because the room's Show Locations mode is set to None.")); + ImGui::EndDisabled(); + + ImGui::Spacing(); + if (!SohGui::mAnchorRoomWindow->IsVisible()) { SohGui::mAnchorRoomWindow->DrawElement(); } diff --git a/soh/soh/Network/Anchor/Packets/DamagePlayer.cpp b/soh/soh/Network/Anchor/Packets/DamagePlayer.cpp index e97abea4fab..eea1e817596 100644 --- a/soh/soh/Network/Anchor/Packets/DamagePlayer.cpp +++ b/soh/soh/Network/Anchor/Packets/DamagePlayer.cpp @@ -29,7 +29,7 @@ void Anchor::SendPacket_DamagePlayer(u32 clientId, u8 damageEffect, u8 damage) { } void Anchor::HandlePacket_DamagePlayer(nlohmann::json payload) { - uint32_t clientId = payload["clientId"].get(); + uint32_t clientId = payload.at("clientId").get(); if (!clients.contains(clientId) || clients[clientId].player == nullptr) { return; } @@ -44,8 +44,8 @@ void Anchor::HandlePacket_DamagePlayer(nlohmann::json payload) { return; } - u8 damageEffect = payload["damageEffect"].get(); - u8 damage = payload["damage"].get(); + u8 damageEffect = payload.at("damageEffect").get(); + u8 damage = payload.at("damage").get(); self->actor.colChkInfo.damage = damage * 8; // Arbitrary number currently, need to fine tune diff --git a/soh/soh/Network/Anchor/Packets/EntranceDiscovered.cpp b/soh/soh/Network/Anchor/Packets/EntranceDiscovered.cpp index 6669e28a040..85671f0ed92 100644 --- a/soh/soh/Network/Anchor/Packets/EntranceDiscovered.cpp +++ b/soh/soh/Network/Anchor/Packets/EntranceDiscovered.cpp @@ -28,6 +28,6 @@ void Anchor::HandlePacket_EntranceDiscovered(nlohmann::json payload) { return; } - u16 entranceIndex = payload["entranceIndex"].get(); + u16 entranceIndex = payload.at("entranceIndex").get(); Entrance_SetEntranceDiscovered(entranceIndex, 1); } diff --git a/soh/soh/Network/Anchor/Packets/GameComplete.cpp b/soh/soh/Network/Anchor/Packets/GameComplete.cpp index aa05a6e64f2..e8fdaafc3d1 100644 --- a/soh/soh/Network/Anchor/Packets/GameComplete.cpp +++ b/soh/soh/Network/Anchor/Packets/GameComplete.cpp @@ -26,7 +26,7 @@ void Anchor::SendPacket_GameComplete() { } void Anchor::HandlePacket_GameComplete(nlohmann::json payload) { - uint32_t clientId = payload["clientId"].get(); + uint32_t clientId = payload.at("clientId").get(); if (!clients.contains(clientId)) { return; } diff --git a/soh/soh/Network/Anchor/Packets/GiveItem.cpp b/soh/soh/Network/Anchor/Packets/GiveItem.cpp index fbf048d7d25..f957eef0226 100644 --- a/soh/soh/Network/Anchor/Packets/GiveItem.cpp +++ b/soh/soh/Network/Anchor/Packets/GiveItem.cpp @@ -49,10 +49,10 @@ void Anchor::HandlePacket_GiveItem(nlohmann::json payload) { return; } - uint32_t clientId = payload["clientId"].get(); + uint32_t clientId = payload.at("clientId").get(); AnchorClient& client = clients[clientId]; - u16 modId = payload["modId"].get(); - u16 getItemId = payload["getItemId"].get(); + u16 modId = payload.at("modId").get(); + u16 getItemId = payload.at("getItemId").get(); GetItemEntry getItemEntry; if (modId == MOD_NONE) { diff --git a/soh/soh/Network/Anchor/Packets/OcarinaSfx.cpp b/soh/soh/Network/Anchor/Packets/OcarinaSfx.cpp index 72eee3c5c05..c2b958102a1 100644 --- a/soh/soh/Network/Anchor/Packets/OcarinaSfx.cpp +++ b/soh/soh/Network/Anchor/Packets/OcarinaSfx.cpp @@ -1,5 +1,4 @@ #include "soh/Network/Anchor/Anchor.h" -#include "soh/Network/Anchor/JsonConversions.hpp" #include #include @@ -39,10 +38,10 @@ void Anchor::SendPacket_OcarinaSfx(uint8_t note, float modulator, int8_t bend) { } void Anchor::HandlePacket_OcarinaSfx(nlohmann::json payload) { - uint32_t clientId = payload["clientId"].get(); - uint8_t note = payload["note"].get(); - float modulator = payload["modulator"].get(); - int8_t bend = payload["bend"].get(); + uint32_t clientId = payload.at("clientId").get(); + uint8_t note = payload.at("note").get(); + float modulator = payload.at("modulator").get(); + int8_t bend = payload.at("bend").get(); if (!clients.contains(clientId) || !clients[clientId].player) { return; diff --git a/soh/soh/Network/Anchor/Packets/PlayerSfx.cpp b/soh/soh/Network/Anchor/Packets/PlayerSfx.cpp index ce4755adf8d..6dff899eb3e 100644 --- a/soh/soh/Network/Anchor/Packets/PlayerSfx.cpp +++ b/soh/soh/Network/Anchor/Packets/PlayerSfx.cpp @@ -36,8 +36,8 @@ void Anchor::SendPacket_PlayerSfx(u16 sfxId) { } void Anchor::HandlePacket_PlayerSfx(nlohmann::json payload) { - uint32_t clientId = payload["clientId"].get(); - u16 sfxId = payload["sfxId"].get(); + uint32_t clientId = payload.at("clientId").get(); + u16 sfxId = payload.at("sfxId").get(); if (!clients.contains(clientId) || !clients[clientId].player) { return; diff --git a/soh/soh/Network/Anchor/Packets/RequestTeleport.cpp b/soh/soh/Network/Anchor/Packets/RequestTeleport.cpp index 8bcb1a30668..4d9c5726380 100644 --- a/soh/soh/Network/Anchor/Packets/RequestTeleport.cpp +++ b/soh/soh/Network/Anchor/Packets/RequestTeleport.cpp @@ -27,7 +27,7 @@ void Anchor::HandlePacket_RequestTeleport(nlohmann::json payload) { return; } - uint32_t clientId = payload["clientId"].get(); + uint32_t clientId = payload.at("clientId").get(); SendPacket_TeleportTo(clientId); } diff --git a/soh/soh/Network/Anchor/Packets/ServerMessage.cpp b/soh/soh/Network/Anchor/Packets/ServerMessage.cpp index 9c1fdc1326d..e7b1197311c 100644 --- a/soh/soh/Network/Anchor/Packets/ServerMessage.cpp +++ b/soh/soh/Network/Anchor/Packets/ServerMessage.cpp @@ -12,6 +12,6 @@ void Anchor::HandlePacket_ServerMessage(nlohmann::json payload) { Notification::Emit({ .prefix = "Server:", .prefixColor = ImVec4(1.0f, 0.5f, 0.5f, 1.0f), - .message = payload["message"].get(), + .message = payload.at("message").get(), }); } diff --git a/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp b/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp index fb1c70dc9a3..ee48f247a6f 100644 --- a/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp +++ b/soh/soh/Network/Anchor/Packets/SetCheckStatus.cpp @@ -38,9 +38,9 @@ void Anchor::HandlePacket_SetCheckStatus(nlohmann::json payload) { auto randoContext = Rando::Context::GetInstance(); - RandomizerCheck rc = payload["rc"].get(); - RandomizerCheckStatus status = payload["status"].get(); - bool skipped = payload["skipped"].get(); + RandomizerCheck rc = payload.at("rc").get(); + RandomizerCheckStatus status = payload.at("status").get(); + bool skipped = payload.at("skipped").get(); isResultOfHandling = true; diff --git a/soh/soh/Network/Anchor/Packets/SetFlag.cpp b/soh/soh/Network/Anchor/Packets/SetFlag.cpp index 3468bab0167..1cabfcea799 100644 --- a/soh/soh/Network/Anchor/Packets/SetFlag.cpp +++ b/soh/soh/Network/Anchor/Packets/SetFlag.cpp @@ -37,9 +37,9 @@ void Anchor::HandlePacket_SetFlag(nlohmann::json payload) { return; } - s16 sceneNum = payload["sceneNum"].get(); - s16 flagType = payload["flagType"].get(); - s16 flag = payload["flag"].get(); + s16 sceneNum = payload.at("sceneNum").get(); + s16 flagType = payload.at("flagType").get(); + s16 flag = payload.at("flag").get(); if (sceneNum == SCENE_ID_MAX) { auto effect = new GameInteractionEffect::SetFlag(); diff --git a/soh/soh/Network/Anchor/Packets/TeleportTo.cpp b/soh/soh/Network/Anchor/Packets/TeleportTo.cpp index 1d50be44954..c23ce730433 100644 --- a/soh/soh/Network/Anchor/Packets/TeleportTo.cpp +++ b/soh/soh/Network/Anchor/Packets/TeleportTo.cpp @@ -37,9 +37,9 @@ void Anchor::HandlePacket_TeleportTo(nlohmann::json payload) { return; } - s32 entranceIndex = payload["entranceIndex"].get(); - s8 roomIndex = payload["roomIndex"].get(); - PosRot posRot = payload["posRot"].get(); + s32 entranceIndex = payload.at("entranceIndex").get(); + s8 roomIndex = payload.at("roomIndex").get(); + PosRot posRot = payload.at("posRot").get(); gPlayState->nextEntranceIndex = entranceIndex; gPlayState->transitionTrigger = TRANS_TRIGGER_START; diff --git a/soh/soh/Network/Anchor/Packets/UnsetFlag.cpp b/soh/soh/Network/Anchor/Packets/UnsetFlag.cpp index cf2fbf5d401..6943c1a6b42 100644 --- a/soh/soh/Network/Anchor/Packets/UnsetFlag.cpp +++ b/soh/soh/Network/Anchor/Packets/UnsetFlag.cpp @@ -37,9 +37,9 @@ void Anchor::HandlePacket_UnsetFlag(nlohmann::json payload) { return; } - s16 sceneNum = payload["sceneNum"].get(); - s16 flagType = payload["flagType"].get(); - s16 flag = payload["flag"].get(); + s16 sceneNum = payload.at("sceneNum").get(); + s16 flagType = payload.at("flagType").get(); + s16 flag = payload.at("flag").get(); if (sceneNum == SCENE_ID_MAX) { auto effect = new GameInteractionEffect::UnsetFlag(); diff --git a/soh/soh/Network/Anchor/Packets/UpdateBeansCount.cpp b/soh/soh/Network/Anchor/Packets/UpdateBeansCount.cpp index 976c7cc24cf..c536459fbf2 100644 --- a/soh/soh/Network/Anchor/Packets/UpdateBeansCount.cpp +++ b/soh/soh/Network/Anchor/Packets/UpdateBeansCount.cpp @@ -34,6 +34,6 @@ void Anchor::HandlePacket_UpdateBeansCount(nlohmann::json payload) { return; } - AMMO(ITEM_BEAN) = payload["amount"].get(); - BEANS_BOUGHT = payload["amountBought"].get(); + AMMO(ITEM_BEAN) = payload.at("amount").get(); + BEANS_BOUGHT = payload.at("amountBought").get(); } diff --git a/soh/soh/Network/Anchor/Packets/UpdateClientState.cpp b/soh/soh/Network/Anchor/Packets/UpdateClientState.cpp index 3099da84a48..5b7d6e178ad 100644 --- a/soh/soh/Network/Anchor/Packets/UpdateClientState.cpp +++ b/soh/soh/Network/Anchor/Packets/UpdateClientState.cpp @@ -33,12 +33,14 @@ nlohmann::json Anchor::PrepClientState() { payload["isSaveLoaded"] = true; payload["isGameComplete"] = gSaveContext.ship.stats.gameComplete; payload["sceneNum"] = gPlayState->sceneNum; + payload["curRoomNum"] = gPlayState->roomCtx.curRoom.num; payload["entranceIndex"] = gSaveContext.entranceIndex; } else { payload["seed"] = 0; payload["isSaveLoaded"] = false; payload["isGameComplete"] = false; payload["sceneNum"] = SCENE_ID_MAX; + payload["curRoomNum"] = -1; payload["entranceIndex"] = 0x00; } @@ -54,7 +56,7 @@ void Anchor::SendPacket_UpdateClientState() { } void Anchor::HandlePacket_UpdateClientState(nlohmann::json payload) { - uint32_t clientId = payload["clientId"].get(); + uint32_t clientId = payload.at("clientId").get(); if (clients.contains(clientId)) { AnchorClient client = payload["state"].get(); @@ -68,6 +70,7 @@ void Anchor::HandlePacket_UpdateClientState(nlohmann::json payload) { clients[clientId].isSaveLoaded = client.isSaveLoaded; clients[clientId].isGameComplete = client.isGameComplete; clients[clientId].sceneNum = client.sceneNum; + clients[clientId].curRoomNum = client.curRoomNum; clients[clientId].entranceIndex = client.entranceIndex; } } diff --git a/soh/soh/Network/Anchor/Packets/UpdateDungeonItems.cpp b/soh/soh/Network/Anchor/Packets/UpdateDungeonItems.cpp index 9e0a4320097..a994b564c15 100644 --- a/soh/soh/Network/Anchor/Packets/UpdateDungeonItems.cpp +++ b/soh/soh/Network/Anchor/Packets/UpdateDungeonItems.cpp @@ -32,7 +32,7 @@ void Anchor::HandlePacket_UpdateDungeonItems(nlohmann::json payload) { return; } - u16 mapIndex = payload["mapIndex"].get(); - gSaveContext.inventory.dungeonItems[mapIndex] = payload["dungeonItems"].get(); - gSaveContext.inventory.dungeonKeys[mapIndex] = payload["dungeonKeys"].get(); + u16 mapIndex = payload.at("mapIndex").get(); + gSaveContext.inventory.dungeonItems[mapIndex] = payload.at("dungeonItems").get(); + gSaveContext.inventory.dungeonKeys[mapIndex] = payload.at("dungeonKeys").get(); } diff --git a/soh/soh/Network/Anchor/Packets/UpdateTeamState.cpp b/soh/soh/Network/Anchor/Packets/UpdateTeamState.cpp index 9e9169779e0..4fedfcdc586 100644 --- a/soh/soh/Network/Anchor/Packets/UpdateTeamState.cpp +++ b/soh/soh/Network/Anchor/Packets/UpdateTeamState.cpp @@ -2,8 +2,6 @@ #include "soh/Network/Anchor/JsonConversions.hpp" #include #include -#include "soh/Enhancements/randomizer/entrance.h" -#include "soh/Enhancements/randomizer/dungeon.h" #include "soh/OTRGlobals.h" #include "soh/Notification/Notification.h" @@ -125,8 +123,9 @@ void Anchor::HandlePacket_UpdateTeamState(nlohmann::json payload) { } isHandlingUpdateTeamState = true; - // This can happen in between file select and the game starting, so we cant use this check, but we need to ensure we - // be careful to wrap PlayState usage in this check + // This can happen in between file select and the game starting, so we can't use this check, but we need to ensure + // we be careful to wrap PlayState usage in this check + // // if (!IsSaveLoaded()) { // return; // } @@ -224,24 +223,24 @@ void Anchor::HandlePacket_UpdateTeamState(nlohmann::json payload) { auto randoContext = Rando::Context::GetInstance(); for (int i = 0; i < RC_MAX; i++) { + auto itemLocation = payload["state"]["rando"].at("itemLocations").at(i); // randoContext->GetItemLocation(i)->RefPlacedItem() = - // payload["state"]["rando"]["itemLocations"][i]["rgID"].get(); + // itemLocation.at("rgID").get(); OTRGlobals::Instance->gRandoContext->GetItemLocation(i)->SetCheckStatus( - payload["state"]["rando"]["itemLocations"][i][0].get()); - OTRGlobals::Instance->gRandoContext->GetItemLocation(i)->SetIsSkipped( - payload["state"]["rando"]["itemLocations"][i][1].get()); + itemLocation.at(0).get()); + OTRGlobals::Instance->gRandoContext->GetItemLocation(i)->SetIsSkipped(itemLocation.at(1).get()); - // if (payload["state"]["rando"]["itemLocations"][i].contains("fakeRgID")) { + // if (itemLocation.contains("fakeRgID")) { // randoContext->overrides.emplace(static_cast(i), // Rando::ItemOverride(static_cast(i), - // payload["state"]["rando"]["itemLocations"][i]["fakeRgID"].get())); + // itemLocation.at("fakeRgID").get())); // randoContext->GetItemOverride(i).GetTrickName().english = - // payload["state"]["rando"]["itemLocations"][i]["trickName"]["english"].get(); + // itemLocation.at("trickName").at("english").get(); // randoContext->GetItemOverride(i).GetTrickName().french = - // payload["state"]["rando"]["itemLocations"][i]["trickName"]["french"].get(); + // itemLocation.at("trickName").at("french").get(); // } - // if (payload["state"]["rando"]["itemLocations"][i].contains("price")) { - // u16 price = payload["state"]["rando"]["itemLocations"][i]["price"].get(); + // if (itemLocation.contains("price")) { + // u16 price = itemLocation.at("price"].get(); // if (price > 0) { // randoContext->GetItemLocation(i)->SetCustomPrice(price); // } diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 7cfdfc92fd5..4ade9cccc14 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -65,6 +65,8 @@ #include #include "Enhancements/item-tables/ItemTableManager.h" +#include "Enhancements/Lang/Lang.h" +#include "soh/SohGui/SohGui.hpp" #include "soh/SohGui/ImGuiUtils.h" #include "ActorDB.h" #include "SaveManager.h" @@ -886,6 +888,8 @@ void OTRGlobals::Initialize() { loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_BINARY, "Background", static_cast(SOH::ResourceType::SOH_Background), 0); + Lang::LoadLangs(); + gSaveStateMgr = std::make_shared(); gRandoContext->InitStaticData(); gRandoContext = Rando::Context::CreateInstance(); @@ -2166,7 +2170,7 @@ Color_RGB8 GetColorForControllerLED() { color = CVarGetColor24(CVAR_SETTING("LEDPort1Color"), { 255, 255, 255 }); } if (gPlayState && (criticalOverride || source == LED_SOURCE_HEALTH)) { - if (HealthMeter_IsCritical()) { + if (HealthMeter_IsCritical(GET_PLAYER(gPlayState))) { color = { 0xFF, 0, 0 }; } else if (gSaveContext.healthCapacity != 0 && source == LED_SOURCE_HEALTH) { if (gSaveContext.health / (float)gSaveContext.healthCapacity <= 0.4f) { diff --git a/soh/soh/ResourceManagerHelpers.cpp b/soh/soh/ResourceManagerHelpers.cpp index 4d4f9126455..f6ddc23682a 100644 --- a/soh/soh/ResourceManagerHelpers.cpp +++ b/soh/soh/ResourceManagerHelpers.cpp @@ -517,7 +517,47 @@ extern "C" int ResourceMgr_OTRSigCheck(char* imgData) { return 0; } +// Load animation with explicit alt asset path checking. +// When Alt Assets is OFF: use original path directly (O2R or vanilla) +// When Alt Assets is ON: try alt/ prefix first, fall back to regular path if not found or invalid extern "C" AnimationHeaderCommon* ResourceMgr_LoadAnimByName(const char* path) { + bool isAlt = ResourceMgr_IsAltAssetsEnabled(); + + if (isAlt) { + std::string pathStr = std::string(path); + static const std::string sOtr = "__OTR__"; + + if (pathStr.starts_with(sOtr)) { + pathStr = pathStr.substr(sOtr.length()); + } + + // Try alt/ first + pathStr = Ship::IResource::gAltAssetPrefix + pathStr; + AnimationHeaderCommon* animHeader = (AnimationHeaderCommon*)ResourceGetDataByName(pathStr.c_str()); + + // If alt loaded successfully, verify it has valid data + if (animHeader != NULL) { + // Check for valid frame count (> 0) + if (animHeader->frameCount > 0) { + // For Normal animations: check frameData (comes after frameCount in AnimationHeader) + // For Link animations: check segment (comes after frameCount in LinkAnimationHeader) + // We check both to be safe - if either is valid, the animation is usable + AnimationHeader* normalAnim = (AnimationHeader*)animHeader; + LinkAnimationHeader* linkAnim = (LinkAnimationHeader*)animHeader; + + // Valid if Normal animation has frameData OR Link animation has segment + if (normalAnim->frameData != NULL || linkAnim->segment != NULL) { + return animHeader; + } + } + // Alt loaded but is invalid (broken), fall through to original path + } + + // Fall back to original path + return (AnimationHeaderCommon*)ResourceGetDataByName(path); + } + + // Alt OFF: use original path directly return (AnimationHeaderCommon*)ResourceGetDataByName(path); } diff --git a/soh/soh/SaveManager.cpp b/soh/soh/SaveManager.cpp index 89224c0ab57..52d536a5c3f 100644 --- a/soh/soh/SaveManager.cpp +++ b/soh/soh/SaveManager.cpp @@ -29,6 +29,54 @@ extern "C" SaveContext gSaveContext; using namespace std::string_literals; +static void SaveContext_ResetSecondaryPlayerStats(SaveContext& saveContext) { + saveContext.healthCapacity4 = 0; + saveContext.health4 = 0; + saveContext.healthCapacity2 = 0; + saveContext.health2 = 0; + saveContext.magicLevel2 = 0; + saveContext.magic2 = MAGIC_NORMAL_METER; + saveContext.isMagicAcquired2 = 0; + saveContext.isDoubleMagicAcquired2 = 0; + saveContext.healthCapacity3 = 0; + saveContext.health3 = 0; +} + +static void SaveContext_LoadSecondaryPlayerSaveData() { + SaveManager::Instance->LoadData("healthCapacity4", gSaveContext.healthCapacity4, static_cast(0)); + SaveManager::Instance->LoadData("health4", gSaveContext.health4, static_cast(0)); + SaveManager::Instance->LoadData("healthCapacity2", gSaveContext.healthCapacity2, static_cast(0)); + SaveManager::Instance->LoadData("health2", gSaveContext.health2, static_cast(0)); + SaveManager::Instance->LoadData("magicLevel2", gSaveContext.magicLevel2, static_cast(0)); + SaveManager::Instance->LoadData("magic2", gSaveContext.magic2, static_cast(MAGIC_NORMAL_METER)); + SaveManager::Instance->LoadData("isMagicAcquired2", gSaveContext.isMagicAcquired2, static_cast(0)); + SaveManager::Instance->LoadData("isDoubleMagicAcquired2", gSaveContext.isDoubleMagicAcquired2, + static_cast(0)); + SaveManager::Instance->LoadData("healthCapacity3", gSaveContext.healthCapacity3, static_cast(0)); + SaveManager::Instance->LoadData("health3", gSaveContext.health3, static_cast(0)); + + // Legacy versions stored a single u32 at this offset. + u32 legacyUnk54 = 0; + SaveManager::Instance->LoadData("unk_54", legacyUnk54, static_cast(0)); +} + +static void SaveContext_SaveSecondaryPlayerSaveData(const SaveContext* saveContext) { + SaveManager::Instance->SaveData("healthCapacity4", saveContext->healthCapacity4); + SaveManager::Instance->SaveData("health4", saveContext->health4); + SaveManager::Instance->SaveData("healthCapacity2", saveContext->healthCapacity2); + SaveManager::Instance->SaveData("health2", saveContext->health2); + SaveManager::Instance->SaveData("magicLevel2", saveContext->magicLevel2); + SaveManager::Instance->SaveData("magic2", saveContext->magic2); + SaveManager::Instance->SaveData("isMagicAcquired2", saveContext->isMagicAcquired2); + SaveManager::Instance->SaveData("isDoubleMagicAcquired2", saveContext->isDoubleMagicAcquired2); + SaveManager::Instance->SaveData("healthCapacity3", saveContext->healthCapacity3); + SaveManager::Instance->SaveData("health3", saveContext->health3); + + // Keep writing the legacy key so older saves remain readable by intermediate builds. + const u32 legacyUnk54 = 0; + SaveManager::Instance->SaveData("unk_54", legacyUnk54); +} + void SaveManager::WriteSaveFile(const std::filesystem::path& savePath, const uintptr_t addr, void* dramAddr, const size_t size) { std::ofstream saveFile = std::ofstream(savePath, std::fstream::in | std::fstream::out | std::fstream::binary); @@ -693,7 +741,7 @@ void SaveManager::InitFileNormal() { gSaveContext.adultEquips.cButtonSlots[button] = SLOT_NONE; } gSaveContext.adultEquips.equipment = 0; - gSaveContext.unk_54 = 0; + SaveContext_ResetSecondaryPlayerStats(gSaveContext); gSaveContext.savedSceneNum = SCENE_LINKS_HOUSE; // Equipment @@ -868,7 +916,7 @@ void SaveManager::InitFileDebug() { gSaveContext.adultEquips.cButtonSlots[button] = SLOT_NONE; } gSaveContext.adultEquips.equipment = 0; - gSaveContext.unk_54 = 0; + SaveContext_ResetSecondaryPlayerStats(gSaveContext); gSaveContext.savedSceneNum = 0x51; // Equipment @@ -988,7 +1036,7 @@ void SaveManager::InitFileMaxed() { gSaveContext.adultEquips.cButtonSlots[button] = SLOT_NONE; } gSaveContext.adultEquips.equipment = 0; - gSaveContext.unk_54 = 0; + SaveContext_ResetSecondaryPlayerStats(gSaveContext); gSaveContext.savedSceneNum = 0x51; // Equipment @@ -1456,7 +1504,7 @@ void SaveManager::LoadBaseVersion1() { }); SaveManager::Instance->LoadData("equipment", gSaveContext.adultEquips.equipment); }); - SaveManager::Instance->LoadData("unk_54", gSaveContext.unk_54); + SaveContext_LoadSecondaryPlayerSaveData(); SaveManager::Instance->LoadData("savedSceneNum", gSaveContext.savedSceneNum); SaveManager::Instance->LoadStruct("equips", []() { SaveManager::Instance->LoadArray("buttonItems", ARRAY_COUNT(gSaveContext.equips.buttonItems), [](size_t i) { @@ -1595,7 +1643,7 @@ void SaveManager::LoadBaseVersion2() { }); SaveManager::Instance->LoadData("equipment", gSaveContext.adultEquips.equipment); }); - SaveManager::Instance->LoadData("unk_54", gSaveContext.unk_54); + SaveContext_LoadSecondaryPlayerSaveData(); SaveManager::Instance->LoadData("savedSceneNum", gSaveContext.savedSceneNum); SaveManager::Instance->LoadStruct("equips", []() { SaveManager::Instance->LoadArray("buttonItems", ARRAY_COUNT(gSaveContext.equips.buttonItems), [](size_t i) { @@ -1807,7 +1855,7 @@ void SaveManager::LoadBaseVersion3() { }); SaveManager::Instance->LoadData("equipment", gSaveContext.adultEquips.equipment); }); - SaveManager::Instance->LoadData("unk_54", gSaveContext.unk_54); + SaveContext_LoadSecondaryPlayerSaveData(); SaveManager::Instance->LoadData("savedSceneNum", gSaveContext.savedSceneNum); SaveManager::Instance->LoadStruct("equips", []() { SaveManager::Instance->LoadArray("buttonItems", ARRAY_COUNT(gSaveContext.equips.buttonItems), [](size_t i) { @@ -2023,7 +2071,7 @@ void SaveManager::LoadBaseVersion4() { }); SaveManager::Instance->LoadData("equipment", gSaveContext.adultEquips.equipment); }); - SaveManager::Instance->LoadData("unk_54", gSaveContext.unk_54); + SaveContext_LoadSecondaryPlayerSaveData(); SaveManager::Instance->LoadData("savedSceneNum", gSaveContext.savedSceneNum); SaveManager::Instance->LoadStruct("equips", []() { SaveManager::Instance->LoadArray("buttonItems", ARRAY_COUNT(gSaveContext.equips.buttonItems), [](size_t i) { @@ -2193,7 +2241,7 @@ void SaveManager::SaveBase(SaveContext* saveContext, int sectionID, bool fullSav [&](size_t i) { SaveManager::Instance->SaveData("", saveContext->adultEquips.cButtonSlots[i]); }); SaveManager::Instance->SaveData("equipment", saveContext->adultEquips.equipment); }); - SaveManager::Instance->SaveData("unk_54", saveContext->unk_54); + SaveContext_SaveSecondaryPlayerSaveData(saveContext); SaveManager::Instance->SaveData("savedSceneNum", saveContext->savedSceneNum); SaveManager::Instance->SaveStruct("equips", [&]() { SaveManager::Instance->SaveArray("buttonItems", ARRAY_COUNT(saveContext->equips.buttonItems), [&](size_t i) { @@ -2669,7 +2717,7 @@ void CopyV0Save(SaveContext_v0& src, SaveContext& dst) { dst.adultEquips.cButtonSlots[i] = src.adultEquips.cButtonSlots[i]; } dst.adultEquips.equipment = src.adultEquips.equipment; - dst.unk_54 = src.unk_54; + SaveContext_ResetSecondaryPlayerStats(dst); dst.savedSceneNum = src.savedSceneNum; for (size_t i = 0; i < ARRAY_COUNT(src.equips.buttonItems); i++) { dst.equips.buttonItems[i] = src.equips.buttonItems[i]; diff --git a/soh/soh/SohGui/Menu.cpp b/soh/soh/SohGui/Menu.cpp index 0147d75ab38..607c5791732 100644 --- a/soh/soh/SohGui/Menu.cpp +++ b/soh/soh/SohGui/Menu.cpp @@ -208,8 +208,7 @@ uint32_t Menu::DrawSearchResults(std::string& menuSearchText) { info.type == WIDGET_SEPARATOR_TEXT || info.isHidden || info.hideInSearch) { continue; } - const char* tooltip = info.options->tooltip; - std::string widgetStr = std::string(info.name) + std::string(tooltip != NULL ? tooltip : ""); + std::string widgetStr = std::string(info.name) + info.options->tooltip; std::transform(widgetStr.begin(), widgetStr.end(), widgetStr.begin(), ::tolower); widgetStr.erase(std::remove(widgetStr.begin(), widgetStr.end(), ' '), widgetStr.end()); if (widgetStr.find(menuSearchText) != std::string::npos) { diff --git a/soh/soh/SohGui/SohGui.cpp b/soh/soh/SohGui/SohGui.cpp index 625c131175f..04c47b4b7e6 100644 --- a/soh/soh/SohGui/SohGui.cpp +++ b/soh/soh/SohGui/SohGui.cpp @@ -20,14 +20,7 @@ #include #endif #include "include/global.h" -#include "include/z64audio.h" -#include "soh/SaveManager.h" -#include "soh/OTRGlobals.h" -#include "soh/Enhancements/Presets/Presets.h" -#include "soh/resource/type/Skeleton.h" -#include "soh/Enhancements/game-interactor/GameInteractor.h" -#include "soh/Enhancements/cosmetics/authenticGfxPatches.h" #include "soh/Enhancements/debugger/MessageViewer.h" #include "soh/Notification/Notification.h" #include "soh/Enhancements/TimeDisplay/TimeDisplay.h" diff --git a/soh/soh/SohGui/SohMenu.cpp b/soh/soh/SohGui/SohMenu.cpp index 0e8ea5cf886..0bab26bca20 100644 --- a/soh/soh/SohGui/SohMenu.cpp +++ b/soh/soh/SohGui/SohMenu.cpp @@ -86,6 +86,7 @@ SohMenu::SohMenu(const std::string& consoleVariable, const std::string& name) void SohMenu::AddMenuElements() { AddMenuSettings(); + AddMenuLocalMultiplayer(); AddMenuEnhancements(); AddMenuRandomizer(); AddMenuNetwork(); diff --git a/soh/soh/SohGui/SohMenu.h b/soh/soh/SohGui/SohMenu.h index 5b0b07827d5..4051c04e9db 100644 --- a/soh/soh/SohGui/SohMenu.h +++ b/soh/soh/SohGui/SohMenu.h @@ -43,6 +43,7 @@ class SohMenu : public Ship::Menu { void AddMenuElements(); void AddMenuSettings(); void AddMenuEnhancements(); + void AddMenuLocalMultiplayer(); void AddMenuDevTools(); void AddMenuRandomizer(); void AddMenuNetwork(); diff --git a/soh/soh/SohGui/SohMenuEnhancements.cpp b/soh/soh/SohGui/SohMenuEnhancements.cpp index c8e6c34d4af..7d4c68abfd8 100644 --- a/soh/soh/SohGui/SohMenuEnhancements.cpp +++ b/soh/soh/SohGui/SohMenuEnhancements.cpp @@ -309,6 +309,10 @@ void SohMenu::AddMenuEnhancements() { .Options( CheckboxOptions().Tooltip("Resets the Navi timer on scene change. If you have already talked to her, " "she will try and talk to you again, instead of needing a save warp or death.")); + AddWidget(path, "Disable Navi Messages", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DisableNaviMessages")) + .Options(CheckboxOptions().Tooltip( + "Disables Navi call prompts and Navi-triggered message popups.")); AddWidget(path, "Link's Cow in Both Time Periods", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("CowOfTime")) .Options(CheckboxOptions().Tooltip( @@ -538,51 +542,6 @@ void SohMenu::AddMenuEnhancements() { AddSidebarEntry("Enhancements", path.sidebarName, 3); path.column = SECTION_COLUMN_1; - AddWidget(path, "Mods", WIDGET_SEPARATOR_TEXT); - AddWidget(path, "Disable Bomb Billboarding", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("DisableBombBillboarding")) - .RaceDisable(false) - .Options(CheckboxOptions().Tooltip( - "Disables bombs always rotating to face the camera. To be used in conjunction with mods that want to " - "replace bombs with 3D objects.")); - AddWidget(path, "Disable Grotto Fixed Rotation", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("DisableGrottoRotation")) - .RaceDisable(false) - .Options(CheckboxOptions().Tooltip( - "Disables Grottos rotating with the Camera. To be used in conjuction with mods that want to " - "replace grottos with 3D objects.")); - AddWidget(path, "Disable Link's Sword Trail", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("DisableLinkSwordTrail")) - .RaceDisable(false) - .Options(CheckboxOptions().Tooltip("Disables the sword trail effect when swinging Link's sword. Useful when " - "using mods that replace Link's sword model.")); - AddWidget(path, "Disable 2D Pre-Rendered Scenes", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("3DSceneRender")) - .RaceDisable(false) - .Options(CheckboxOptions().Tooltip("Disables 2D pre-rendered backgrounds. Enable this when using a mod that " - "implements 3D backdrops for these areas.\n" - "Requires Scene Change to alter.")); - AddWidget(path, "Disable Fixed Camera", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("DisableFixedCamera")) - .RaceDisable(false) - .PreFunc([](WidgetInfo& info) { - if (CVarGetInteger(CVAR_ENHANCEMENT("3DSceneRender"), 0) == 0) { - CVarSetInteger(CVAR_ENHANCEMENT("DisableFixedCamera"), 0); - info.options->disabled = true; - } else { - info.options->disabled = false; - } - info.options->disabledTooltip = "Requires \"Disable 2D Pre-Rendered Scenes\" to be enabled."; - }) - .Options(CheckboxOptions().Tooltip( - "Disables the fixed camera in maps that use 2D pre-rendered backgrounds. Enable this when using a mod " - "that implements 3D backdrops for these areas.\n" - "Requires Scene Change to alter.")); - AddWidget(path, "Ingame Text Spacing: %d", WIDGET_CVAR_SLIDER_INT) - .CVar(CVAR_ENHANCEMENT("TextSpacing")) - .RaceDisable(false) - .Options(IntSliderOptions().Min(4).Max(6).DefaultValue(6).Tooltip( - "Space between text characters (useful for HD font textures).")); AddWidget(path, "Models & Textures", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Disable LOD", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DisableLOD")) @@ -755,6 +714,16 @@ void SohMenu::AddMenuEnhancements() { AddWidget(path, "Equipment", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Equip Items on Dpad", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DpadEquips")) + .PreFunc([](WidgetInfo& info) { + s32 localMpDisabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0); + s32 localMpPlayers = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2); + bool originalMpItemsEnabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.OriginalItemSystem"), 1) && + !localMpDisabled && (localMpPlayers > 1); + + info.options->disabled = originalMpItemsEnabled; + info.options->disabledTooltip = + "Disabled while \"Use Original Multiplayer Item System\" is active."; + }) .Options(CheckboxOptions().Tooltip( "Equip items and equipment on the D-pad. If used with \"D-pad on Pause Screen\", you must " "hold C-Up to equip instead of navigate.")); @@ -1595,11 +1564,6 @@ void SohMenu::AddMenuEnhancements() { " - Dungeons (MQ): Mirror the world in MQ Dungeons.\n" " - Dungeons Random: Randomly decide to mirror the world in Dungeons.\n" " - Dungeons Random (Seeded): Dungeons are mirrored based on the current randomizer seed/file.")); - AddWidget(path, "Ivan the Fairy (Coop Mode)", WIDGET_CVAR_CHECKBOX) - .CVar(CVAR_ENHANCEMENT("IvanCoopModeEnabled")) - .Options(CheckboxOptions().Tooltip( - "Enables Ivan the Fairy upon the next map change. Player 2 can control Ivan and press the C-Buttons to " - "use items and mess with Player 1!")); AddWidget(path, "Dogs Follow You Everywhere", WIDGET_CVAR_CHECKBOX) .CVar(CVAR_ENHANCEMENT("DogFollowsEverywhere")) .Options(CheckboxOptions().Tooltip("Allows dogs to follow you anywhere you go, even if you leave the Market.")); diff --git a/soh/soh/SohGui/SohMenuRandomizer.cpp b/soh/soh/SohGui/SohMenuRandomizer.cpp index b5b9ece9861..be64ac81587 100644 --- a/soh/soh/SohGui/SohMenuRandomizer.cpp +++ b/soh/soh/SohGui/SohMenuRandomizer.cpp @@ -585,15 +585,23 @@ void SohMenu::AddMenuRandomizer() { .Options(ButtonOptions() .Size(ImVec2(250.f, 0.f)) .DisabledTooltip("Must be on File Select to generate a randomizer seed.")); - AddWidget(path, "Spoiler File", WIDGET_CUSTOM) - .CustomFunction([](WidgetInfo& info) { - JoinRandoGenerationThread(); - if (!CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) { - std::string spoilerfilepath = CVarGetString(CVAR_GENERAL("SpoilerLog"), ""); - ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str()); - } + AddWidget(path, "Randomize All Settings", WIDGET_BUTTON) + .Callback([](WidgetInfo& info) { Rando::Settings::GetInstance()->RandomizeAllSettings(); }) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = CVarGetInteger(CVAR_GENERAL("RandoGenerating"), 0) || + CVarGetInteger(CVAR_GENERAL("OnFileSelectNameEntry"), 0); }) + .Options(ButtonOptions() + .Size(ImVec2(250.f, 0.f)) + .Tooltip("Randomizes all randomizer settings to random valid values (excludes tricks).")) .SameLine(true); + AddWidget(path, "Spoiler File", WIDGET_CUSTOM).CustomFunction([](WidgetInfo& info) { + JoinRandoGenerationThread(); + if (!CVarGetInteger(CVAR_RANDOMIZER_SETTING("DontGenerateSpoiler"), 0)) { + std::string spoilerfilepath = CVarGetString(CVAR_GENERAL("SpoilerLog"), ""); + ImGui::Text("Spoiler File: %s", spoilerfilepath.c_str()); + } + }); // Enhancements AddWidget(path, "Enhancements", WIDGET_SEPARATOR_TEXT); diff --git a/soh/soh/SohGui/SohMenuSettings.cpp b/soh/soh/SohGui/SohMenuSettings.cpp index b806da560c7..f929ea6c344 100644 --- a/soh/soh/SohGui/SohMenuSettings.cpp +++ b/soh/soh/SohGui/SohMenuSettings.cpp @@ -525,9 +525,58 @@ void SohMenu::AddMenuSettings() { .RaceDisable(false) .Options(CheckboxOptions().Tooltip("Prevent notifications from playing a sound.")); - // Mod Menu - path.sidebarName = "Mod Menu"; - AddSidebarEntry("Settings", path.sidebarName, 1); + // Mods + path.sidebarName = "Mods"; + path.column = SECTION_COLUMN_1; + AddSidebarEntry("Settings", path.sidebarName, 2); + + AddWidget(path, "Graphics Mods", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Disable Bomb Billboarding", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DisableBombBillboarding")) + .RaceDisable(false) + .Options(CheckboxOptions().Tooltip( + "Disables bombs always rotating to face the camera. To be used in conjunction with mods that want to " + "replace bombs with 3D objects.")); + AddWidget(path, "Disable Grotto Fixed Rotation", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DisableGrottoRotation")) + .RaceDisable(false) + .Options(CheckboxOptions().Tooltip( + "Disables Grottos rotating with the Camera. To be used in conjuction with mods that want to " + "replace grottos with 3D objects.")); + AddWidget(path, "Disable Link's Sword Trail", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DisableLinkSwordTrail")) + .RaceDisable(false) + .Options(CheckboxOptions().Tooltip("Disables the sword trail effect when swinging Link's sword. Useful when " + "using mods that replace Link's sword model.")); + AddWidget(path, "Disable 2D Pre-Rendered Scenes", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("3DSceneRender")) + .RaceDisable(false) + .Options(CheckboxOptions().Tooltip("Disables 2D pre-rendered backgrounds. Enable this when using a mod that " + "implements 3D backdrops for these areas.\n" + "Requires Scene Change to alter.")); + AddWidget(path, "Disable Fixed Camera", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("DisableFixedCamera")) + .RaceDisable(false) + .PreFunc([](WidgetInfo& info) { + if (CVarGetInteger(CVAR_ENHANCEMENT("3DSceneRender"), 0) == 0) { + CVarSetInteger(CVAR_ENHANCEMENT("DisableFixedCamera"), 0); + info.options->disabled = true; + } else { + info.options->disabled = false; + } + info.options->disabledTooltip = "Requires \"Disable 2D Pre-Rendered Scenes\" to be enabled."; + }) + .Options(CheckboxOptions().Tooltip( + "Disables the fixed camera in maps that use 2D pre-rendered backgrounds. Enable this when using a mod " + "that implements 3D backdrops for these areas.\n" + "Requires Scene Change to alter.")); + AddWidget(path, "Ingame Text Spacing: %d", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("TextSpacing")) + .RaceDisable(false) + .Options(IntSliderOptions().Min(4).Max(6).DefaultValue(6).Tooltip( + "Space between text characters (useful for HD font textures).")); + + AddWidget(path, "Mod Menu", WIDGET_SEPARATOR_TEXT); AddWidget(path, "Popout Mod Menu Window", WIDGET_WINDOW_BUTTON) .CVar(CVAR_WINDOW("ModMenu")) .WindowName("Mod Menu") @@ -535,4 +584,374 @@ void SohMenu::AddMenuSettings() { .Options(WindowButtonOptions().Tooltip("Enables the separate Mod Menu Window.")); } +void SohMenu::AddMenuLocalMultiplayer() { + AddMenuEntry("Local Multiplayer", CVAR_SETTING("Menu.LocalMultiplayerSidebarSection")); + AddSidebarEntry("Local Multiplayer", "General", 1); + + WidgetPath path = { "Local Multiplayer", "General", SECTION_COLUMN_1 }; + + AddWidget(path, "Settings", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "Disable Local Multiplayer (run stock settings)", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.Disable")) + .Options(CheckboxOptions() + .DefaultValue(false) + .Tooltip( + "When enabled, disables local multiplayer and reverts to stock settings (1 player).")); + AddWidget(path, "Local Multiplayer Players: %d", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount")) + .Options(IntSliderOptions() + .Min(1) + .Max(4) + .DefaultValue(2) + .Format("%d") + .Tooltip("Spawns/despawns local controllable players.\n\n" + " - 1: Single-player\n" + " - 2-4: Spawn extra players on controller ports 2-4.")); + AddWidget(path, "Custom Item System", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.OriginalItemSystem")) + .PreFunc([](WidgetInfo& info) { + info.options->disabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0); + info.options->disabledTooltip = "Requires local multiplayer to be enabled."; + }) + .Options(CheckboxOptions() + .DefaultValue(true) + .Tooltip( + "Matches the original mod behavior:\n" + " - Players can use an optional second quick-item slot\n" + " - D-pad Left/Right cycles the selected slot\n" + " - D-pad Up/Down selects slot 1/2 while slot 2 is enabled\n" + " - L uses the selected slot\n\n" + "This mode ignores D-pad equip behavior so item settings won't interfere.")); + AddWidget(path, "Enable Second Item Slot", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.SecondItemSlot")) + .PreFunc([](WidgetInfo& info) { + s32 localMultiplayerDisabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0); + s32 originalItemSystemEnabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.OriginalItemSystem"), 1); + + info.options->disabled = localMultiplayerDisabled || !originalItemSystemEnabled; + info.options->disabledTooltip = localMultiplayerDisabled + ? "Requires local multiplayer to be enabled." + : "Requires custom item system to be enabled."; + }) + .Options(CheckboxOptions() + .DefaultValue(true) + .Tooltip( + "Shows and enables the second quick-item slot.\n\n" + "When disabled:\n" + " - only slot 1 is active\n" + " - L always uses slot 1\n" + " - D-pad Left/Right cycles slot 1")); + AddWidget(path, "Place Second Slot Beside First", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.SecondItemSlotSideBySide")) + .PreFunc([](WidgetInfo& info) { + s32 localMultiplayerDisabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0); + s32 originalItemSystemEnabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.OriginalItemSystem"), 1); + s32 secondSlotEnabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.SecondItemSlot"), 1); + + info.options->disabled = localMultiplayerDisabled || !originalItemSystemEnabled || !secondSlotEnabled; + info.options->disabledTooltip = localMultiplayerDisabled + ? "Requires local multiplayer to be enabled." + : (!originalItemSystemEnabled + ? "Requires custom item system to be enabled." + : "Requires the second item slot to be enabled."); + }) + .Options(CheckboxOptions() + .DefaultValue(true) + .Tooltip( + "Places slot 2 beside slot 1 instead of directly below it.\n\n" + "Shared-screen HUD keeps right-side players mirrored toward the screen center.")); + AddWidget(path, "Independent Slot Usage", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.IndependentSlotUsage")) + .PreFunc([](WidgetInfo& info) { + s32 localMultiplayerDisabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0); + s32 originalItemSystemEnabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.OriginalItemSystem"), 1); + s32 secondSlotEnabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.SecondItemSlot"), 1); + + info.options->disabled = localMultiplayerDisabled || !originalItemSystemEnabled || !secondSlotEnabled; + info.options->disabledTooltip = localMultiplayerDisabled + ? "Requires local multiplayer to be enabled." + : (!originalItemSystemEnabled + ? "Requires custom item system to be enabled." + : "Requires the second item slot to be enabled."); + }) + .Options(CheckboxOptions() + .DefaultValue(false) + .Tooltip( + "Changes how quick slots are used:\n\n" + "When disabled (default):\n" + " - L uses the selected slot\n" + " - D-pad Up/Down selects slot 1/2\n\n" + "When enabled:\n" + " - L always uses slot 1\n" + " - D-pad Down uses slot 2\n" + " - D-pad Up only changes which slot is cycled by Left/Right")); + AddWidget(path, "Use Radial Quick Item Menu", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.RadialQuickItemMenu")) + .PreFunc([](WidgetInfo& info) { + s32 localMultiplayerDisabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0); + s32 originalItemSystemEnabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.OriginalItemSystem"), 1); + + info.options->disabled = localMultiplayerDisabled || !originalItemSystemEnabled; + info.options->disabledTooltip = localMultiplayerDisabled + ? "Requires local multiplayer to be enabled." + : "Requires custom item system to be enabled."; + }) + .Options(CheckboxOptions() + .DefaultValue(true) + .Tooltip( + "Replaces D-pad Left/Right item cycling with a radial quick-swap wheel.\n\n" + "Controls:\n" + " - Hold D-pad Left or Right to open the radial menu\n" + " - Tilt the Control Stick to highlight an item\n" + " - Release D-pad Left/Right to equip the highlighted item\n\n" + "D-pad Up/Down slot controls remain unchanged.")); + AddWidget(path, "Enable Player Pickup and Throw", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCarry")) + .PreFunc([](WidgetInfo& info) { + s32 playerCount = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2); + + info.options->disabled = playerCount <= 1; + info.options->disabledTooltip = "Requires local multiplayer with 2 or more players."; + }) + .Options(CheckboxOptions() + .DefaultValue(true) + .Tooltip("Allows local players to pick up and throw each other.\n" + "Carried players can break free by pressing A.")); + AddWidget(path, "Chaotix Ring Teather", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.ElasticRopeMode")) + .PreFunc([](WidgetInfo& info) { + s32 playerCount = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2); + + info.options->disabled = playerCount <= 1; + info.options->disabledTooltip = "Requires local multiplayer with 2 or more players."; + }) + .Options(CheckboxOptions() + .DefaultValue(false) + .Tooltip("Adds a shared elastic link between local players.\n" + "The farther a player gets from the group, the stronger the pull back.\n" + "In 2P, players are tethered to each other. In 3P/4P, players are tethered to the\n" + "group center.")); + AddWidget(path, "Elastic Rope Strength", WIDGET_CVAR_SLIDER_FLOAT) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.ElasticRopeStrength")) + .PreFunc([](WidgetInfo& info) { + s32 playerCount = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2); + + info.options->disabled = playerCount <= 1; + info.options->disabledTooltip = "Requires local multiplayer with 2 or more players."; + }) + .Options(FloatSliderOptions() + .DefaultValue(1.0f) + .Min(0.25f) + .Max(3.0f) + .Step(0.05f) + .Format("%.2f") + .Tooltip("Adjusts how strongly the rope pulls players together.\n" + "Lower values make the rope looser and allow more separation.\n" + "Higher values tighten the rope and pull harder.")); + AddWidget(path, "Teleport Target Player", WIDGET_CVAR_SLIDER_INT) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.TeleportTargetPlayer")) + .PreFunc([](WidgetInfo& info) { + s32 playerCount = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2); + + info.options->disabled = playerCount <= 1; + info.options->disabledTooltip = "Requires local multiplayer with 2 or more players."; + }) + .Options(IntSliderOptions() + .DefaultValue(1) + .Min(1) + .Max(4) + .Format("Player %d") + .Tooltip("Selects which player all active local players teleport to when the button below is pressed.")); + AddWidget(path, "Teleport All Players To Target", WIDGET_BUTTON) + .PreFunc([](WidgetInfo& info) { + s32 playerCount = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2); + + info.options->disabled = playerCount <= 1; + info.options->disabledTooltip = "Requires local multiplayer with 2 or more players."; + }) + .Callback([](WidgetInfo& info) { + s32 request = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.TeleportAllToSelectedRequest"), 0); + + if (request < 0) { + request = 0; + } + + CVarSetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.TeleportAllToSelectedRequest"), request + 1); + Ship::Context::GetInstance()->GetWindow()->GetGui()->SaveConsoleVariablesNextFrame(); + }) + .Options(ButtonOptions().Tooltip("Teleports every active local player to the selected target player's position.")); + AddWidget(path, "Per-Player Tunic Colors", WIDGET_SEPARATOR_TEXT); + AddWidget(path, "P1 Tunic Color", WIDGET_CVAR_COLOR_PICKER) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.Player1TunicColor")) + .PreFunc([](WidgetInfo& info) { + s32 playerCount = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2); + + info.options->disabled = playerCount <= 1; + info.options->disabledTooltip = "Requires local multiplayer with 2 or more players."; + }) + .Options(ColorPickerOptions() + .DefaultValue({ 30, 105, 27, 255 }) + .UseAlpha(false) + .ShowReset(true) + .ShowRandom(true) + .ShowRainbow(false) + .ShowLock(false) + .Tooltip("Default: green.")); + AddWidget(path, "P2 Tunic Color", WIDGET_CVAR_COLOR_PICKER) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.Player2TunicColor")) + .PreFunc([](WidgetInfo& info) { + s32 playerCount = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2); + + info.options->disabled = playerCount <= 1; + info.options->disabledTooltip = "Requires local multiplayer with 2 or more players."; + }) + .Options(ColorPickerOptions() + .DefaultValue({ 255, 0, 0, 255 }) + .UseAlpha(false) + .ShowReset(true) + .ShowRandom(true) + .ShowRainbow(false) + .ShowLock(false) + .Tooltip("Default: red.")); + AddWidget(path, "P3 Tunic Color", WIDGET_CVAR_COLOR_PICKER) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.Player3TunicColor")) + .PreFunc([](WidgetInfo& info) { + s32 playerCount = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2); + + info.options->disabled = playerCount <= 2; + info.options->disabledTooltip = "Requires local multiplayer with 3 or more players."; + }) + .Options(ColorPickerOptions() + .DefaultValue({ 0, 120, 255, 255 }) + .UseAlpha(false) + .ShowReset(true) + .ShowRandom(true) + .ShowRainbow(false) + .ShowLock(false) + .Tooltip("Default: blue.")); + AddWidget(path, "P4 Tunic Color", WIDGET_CVAR_COLOR_PICKER) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.Player4TunicColor")) + .PreFunc([](WidgetInfo& info) { + s32 playerCount = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2); + + info.options->disabled = playerCount <= 3; + info.options->disabledTooltip = "Requires local multiplayer with 4 players."; + }) + .Options(ColorPickerOptions() + .DefaultValue({ 122, 57, 163, 255 }) + .UseAlpha(false) + .ShowReset(true) + .ShowRandom(true) + .ShowRainbow(false) + .ShowLock(false) + .Tooltip("Default: purple.")); + AddWidget(path, "Allow Player 2-4 to Load Areas", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.SecondaryPlayersLoadAreas")) + .PreFunc([](WidgetInfo& info) { + s32 playerCount = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2); + + info.options->disabled = playerCount <= 1; + info.options->disabledTooltip = "Requires local multiplayer with 2 or more players."; + }) + .Options(CheckboxOptions() + .DefaultValue(true) + .Tooltip("Allows players 2-4 to trigger area loading checks (like invisible exit boundaries).\n" + "Useful for dungeon room transitions and connected area boundaries.")); + AddWidget(path, "Enable Splitscreen", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreen")) + .PreFunc([](WidgetInfo& info) { + s32 playerCount = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2); + + info.options->disabled = playerCount <= 1; + info.options->disabledTooltip = "Requires local multiplayer with 2 or more players."; + }) + .Options(CheckboxOptions() + .DefaultValue(true) + .Tooltip("Renders one camera viewport per active local player.\n" + "Supports 2, 3, and 4 player split-screen layouts.")); + AddWidget(path, "Vertical Split-Screen (2P)", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreenVertical")) + .PreFunc([](WidgetInfo& info) { + s32 playerCount = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2); + bool splitScreenEnabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreen"), 1) != 0; + + info.options->disabled = (playerCount <= 1) || !splitScreenEnabled; + info.options->disabledTooltip = "Requires local multiplayer split-screen to be enabled."; + }) + .Options(CheckboxOptions() + .DefaultValue(true) + .Tooltip("For 2-player split-screen, choose vertical (left/right) layout when enabled.\n" + "When disabled, 2-player uses horizontal (top/bottom) layout.")); + AddWidget(path, "Split-Screen Performance Mode (Reduced Effects)", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreenPerformanceMode")) + .PreFunc([](WidgetInfo& info) { + s32 playerCount = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2); + bool splitScreenEnabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreen"), 1) != 0; + + info.options->disabled = (playerCount <= 1) || !splitScreenEnabled; + info.options->disabledTooltip = "Requires local multiplayer split-screen to be enabled."; + }) + .Options(CheckboxOptions() + .DefaultValue(false) + .Tooltip("Improves split-screen performance by reducing duplicate secondary viewport effects.\n\n" + "Applies to non-primary split viewports:\n" + " - skips previous-room blend pass\n" + " - skips rain and skybox quake pass\n" + " - skips lens flare effects\n\n" + "Recommended for low-end hardware.")); + AddWidget(path, "Split-Screen Aggressive Performance Mode", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreenAggressiveMode")) + .PreFunc([](WidgetInfo& info) { + s32 playerCount = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2); + bool splitScreenEnabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreen"), 1) != 0; + bool perfModeEnabled = CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreenPerformanceMode"), 1) != 0; + + info.options->disabled = (playerCount <= 1) || !splitScreenEnabled || !perfModeEnabled; + info.options->disabledTooltip = + "Requires local multiplayer split-screen and performance mode to be enabled."; + }) + .Options(CheckboxOptions() + .DefaultValue(false) + .Tooltip("Maximum split-screen stability mode for low-end hardware and 3P/4P sessions.\n\n" + "Applies stronger reductions:\n" + " - uses opaque room pass only\n" + " - skips skybox/rain/lightning/lens effects\n" + " - draws only player actors in secondary split viewports\n" + " - disables local multiplayer debug marker rendering\n\n" + "Best for preventing crashes in resource-heavy areas.")); + AddWidget(path, "Local Multiplayer Debug Markers", WIDGET_CVAR_CHECKBOX) + .CVar(CVAR_ENHANCEMENT("LocalMultiplayer.DebugMarkers")) + .Options(CheckboxOptions() + .DefaultValue(false) + .Tooltip("Draw debug markers for local multiplayer positions and midpoint camera.")); +} + } // namespace SohGui diff --git a/soh/soh/SohGui/UIWidgets.cpp b/soh/soh/SohGui/UIWidgets.cpp index ee5c39057e5..b261b9c75fc 100644 --- a/soh/soh/SohGui/UIWidgets.cpp +++ b/soh/soh/SohGui/UIWidgets.cpp @@ -59,6 +59,12 @@ void Tooltip(const char* text) { } } +void Tooltip(std::string text) { + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("%s", WrappedText(text).c_str()); + } +} + void PushStyleMenu(const ImVec4& color) { ImGui::PushStyleColor(ImGuiCol_Header, ImVec4(color.x, color.y, color.z, 0.5f)); ImGui::PushStyleColor(ImGuiCol_HeaderHovered, ImVec4(color.x, color.y, color.z, 1.0f)); @@ -174,9 +180,9 @@ bool Button(const char* label, const ButtonOptions& options) { PopStyleButton(); ImGui::EndDisabled(); if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && - !Ship_IsCStringEmpty(options.disabledTooltip)) { + !options.disabledTooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); - } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !options.tooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); } return dirty; @@ -365,9 +371,9 @@ bool Checkbox(const char* _label, bool* value, const CheckboxOptions& options) { PopStyleCheckbox(); ImGui::EndDisabled(); if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && - !Ship_IsCStringEmpty(options.disabledTooltip)) { + !options.disabledTooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); - } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !options.tooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); } return pressed; @@ -595,9 +601,9 @@ bool SliderInt(const char* label, int32_t* value, const IntSliderOptions& option ImGui::EndDisabled(); ImGui::EndGroup(); if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && - !Ship_IsCStringEmpty(options.disabledTooltip)) { + !options.disabledTooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); - } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !options.tooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); } ImGui::PopID(); @@ -726,9 +732,9 @@ bool SliderFloat(const char* label, float* value, const FloatSliderOptions& opti ImGui::EndDisabled(); ImGui::EndGroup(); if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && - !Ship_IsCStringEmpty(options.disabledTooltip)) { + !options.disabledTooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); - } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !options.tooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); } ImGui::PopID(); @@ -799,13 +805,12 @@ bool InputString(const char* label, std::string* value, const InputOptions& opti PopStyleInput(); ImGui::EndDisabled(); ImGui::EndGroup(); - if (options.hasError && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && - !Ship_IsCStringEmpty(options.errorText)) { + if (options.hasError && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !options.errorText.empty()) { ImGui::SetTooltip("%s", WrappedText(options.errorText).c_str()); } else if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && - !Ship_IsCStringEmpty(options.disabledTooltip)) { + !options.disabledTooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); - } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !options.tooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); } ImGui::PopID(); @@ -854,9 +859,9 @@ bool InputInt(const char* label, int32_t* value, const InputOptions& options) { ImGui::EndDisabled(); ImGui::EndGroup(); if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && - !Ship_IsCStringEmpty(options.disabledTooltip)) { + !options.disabledTooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); - } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !options.tooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); } ImGui::PopID(); @@ -1040,7 +1045,7 @@ bool CVarRadioButton(const char* text, const char* cvarName, int32_t id, const R ImGui::SameLine(); ImGui::Text("%s", text); PopStyleCheckbox(); - if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !options.tooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); } diff --git a/soh/soh/SohGui/UIWidgets.hpp b/soh/soh/SohGui/UIWidgets.hpp index 927b2d27159..6ab02248f79 100644 --- a/soh/soh/SohGui/UIWidgets.hpp +++ b/soh/soh/SohGui/UIWidgets.hpp @@ -36,7 +36,7 @@ std::string WrappedText(const char* text, unsigned int charactersPerLine = 80); std::string WrappedText(const std::string& text, unsigned int charactersPerLine = 80); void PaddedSeparator(bool padTop = true, bool padBottom = true, float extraVerticalTopPadding = 0.0f, float extraVerticalBottomPadding = 0.0f); -void Tooltip(const char* text); +void Tooltip(std::string text); typedef enum ColorPickerModifiers { ColorPickerResetButton = 1, @@ -106,11 +106,11 @@ enum ComponentAlignments { }; struct WidgetOptions { - const char* tooltip = ""; + std::string tooltip = ""; bool disabled = false; - const char* disabledTooltip = ""; + std::string disabledTooltip = ""; - WidgetOptions& Tooltip(const char* tooltip_) { + WidgetOptions& Tooltip(std::string tooltip_) { tooltip = tooltip_; return *this; } @@ -120,7 +120,7 @@ struct WidgetOptions { return *this; } - WidgetOptions& DisabledTooltip(const char* disabledTooltip_) { + WidgetOptions& DisabledTooltip(std::string disabledTooltip_) { disabledTooltip = disabledTooltip_; return *this; } @@ -150,7 +150,7 @@ struct ButtonOptions : WidgetOptions { return *this; } - ButtonOptions& Tooltip(const char* tooltip_) { + ButtonOptions& Tooltip(std::string tooltip_) { WidgetOptions::tooltip = tooltip_; return *this; } @@ -182,7 +182,7 @@ struct ColorPickerOptions : WidgetOptions { return *this; } - ColorPickerOptions& Tooltip(const char* tooltip_) { + ColorPickerOptions& Tooltip(std::string tooltip_) { WidgetOptions::tooltip = tooltip_; return *this; } @@ -240,7 +240,7 @@ struct WindowButtonOptions : WidgetOptions { return *this; } - WindowButtonOptions& Tooltip(const char* tooltip_) { + WindowButtonOptions& Tooltip(std::string tooltip_) { WidgetOptions::tooltip = tooltip_; return *this; } @@ -283,7 +283,7 @@ struct CheckboxOptions : WidgetOptions { return *this; } - CheckboxOptions& Tooltip(const char* tooltip_) { + CheckboxOptions& Tooltip(std::string tooltip_) { WidgetOptions::tooltip = tooltip_; return *this; } @@ -293,7 +293,7 @@ struct CheckboxOptions : WidgetOptions { return *this; } - CheckboxOptions& DisabledTooltip(const char* disabledTooltip_) { + CheckboxOptions& DisabledTooltip(std::string disabledTooltip_) { WidgetOptions::disabledTooltip = disabledTooltip_; return *this; } @@ -332,7 +332,7 @@ struct ComboboxOptions : WidgetOptions { return *this; } - ComboboxOptions& Tooltip(const char* tooltip_) { + ComboboxOptions& Tooltip(std::string tooltip_) { WidgetOptions::tooltip = tooltip_; return *this; } @@ -397,7 +397,7 @@ struct IntSliderOptions : WidgetOptions { return *this; } - IntSliderOptions& Tooltip(const char* tooltip_) { + IntSliderOptions& Tooltip(std::string tooltip_) { WidgetOptions::tooltip = tooltip_; return *this; } @@ -481,7 +481,7 @@ struct FloatSliderOptions : WidgetOptions { return *this; } - FloatSliderOptions& Tooltip(const char* tooltip_) { + FloatSliderOptions& Tooltip(std::string tooltip_) { WidgetOptions::tooltip = tooltip_; return *this; } @@ -544,7 +544,7 @@ struct RadioButtonsOptions : WidgetOptions { return *this; } - RadioButtonsOptions& Tooltip(const char* tooltip_) { + RadioButtonsOptions& Tooltip(std::string tooltip_) { WidgetOptions::tooltip = tooltip_; return *this; } @@ -571,9 +571,9 @@ struct InputOptions : WidgetOptions { bool secret = false; ImGuiInputFlags addedFlags = 0; bool hasError = false; - const char* errorText = ""; + std::string errorText = ""; - InputOptions& Tooltip(const char* tooltip_) { + InputOptions& Tooltip(std::string tooltip_) { WidgetOptions::tooltip = tooltip_; return *this; } @@ -628,7 +628,7 @@ struct InputOptions : WidgetOptions { return *this; } - InputOptions& ErrorText(const char* errorText_) { + InputOptions& ErrorText(std::string errorText_) { errorText = errorText_; return *this; } @@ -754,9 +754,9 @@ bool Combobox(std::string label, T* value, const std::map& combo ImGui::EndDisabled(); ImGui::EndGroup(); if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && - !Ship_IsCStringEmpty(options.disabledTooltip)) { + !options.disabledTooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); - } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !options.tooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); } ImGui::PopID(); @@ -839,9 +839,9 @@ bool Combobox(std::string label, T* value, const std::vector& combo ImGui::EndDisabled(); ImGui::EndGroup(); if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && - !Ship_IsCStringEmpty(options.disabledTooltip)) { + !options.disabledTooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); - } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !options.tooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); } ImGui::PopID(); @@ -925,9 +925,9 @@ bool Combobox(std::string label, T* value, const std::vector& combo ImGui::EndDisabled(); ImGui::EndGroup(); if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && - !Ship_IsCStringEmpty(options.disabledTooltip)) { + !options.disabledTooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); - } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !options.tooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); } ImGui::PopID(); @@ -1011,9 +1011,9 @@ bool Combobox(std::string label, T* value, const char* (&comboArray)[N], const C ImGui::EndDisabled(); ImGui::EndGroup(); if (options.disabled && ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && - !Ship_IsCStringEmpty(options.disabledTooltip)) { + !options.disabledTooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.disabledTooltip).c_str()); - } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !Ship_IsCStringEmpty(options.tooltip)) { + } else if (ImGui::IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled) && !options.tooltip.empty()) { ImGui::SetTooltip("%s", WrappedText(options.tooltip).c_str()); } ImGui::PopID(); diff --git a/soh/soh/resource/importer/AnimationFactory.cpp b/soh/soh/resource/importer/AnimationFactory.cpp index 99507e4f3a8..af8dd8ac382 100644 --- a/soh/soh/resource/importer/AnimationFactory.cpp +++ b/soh/soh/resource/importer/AnimationFactory.cpp @@ -78,15 +78,33 @@ ResourceFactoryBinaryAnimationV0::ReadResource(std::shared_ptr file, } animation->animationData.transformUpdateIndex.copyValues = animation->copyValuesArr.data(); } else if (animType == AnimationType::Link) { + // Initialize segment to nullptr (important for alt asset fallback) + animation->animationData.linkAnimationHeader.segment = nullptr; + // Read the frame count animation->animationData.linkAnimationHeader.common.frameCount = reader->ReadInt16(); // Read the segment pointer (always 32 bit, doesn't adjust for system pointer size) std::string path = reader->ReadString(); - const auto animData = std::static_pointer_cast( + auto animData = std::static_pointer_cast( Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(path.c_str())); - animation->animationData.linkAnimationHeader.segment = animData->GetPointer(); + // If direct load failed and alt assets are enabled, try with alt/ prefix + if (animData == nullptr && Ship::Context::GetInstance()->GetResourceManager()->IsAltAssetsEnabled()) { + std::string altPath = path; + if (altPath.find("__OTR__") == 0) { + altPath = altPath.substr(7); // Strip __OTR__ + } + altPath = "alt/" + altPath; + animData = std::static_pointer_cast( + Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(altPath.c_str())); + } + + if (animData != nullptr) { + animation->animationData.linkAnimationHeader.segment = animData->GetPointer(); + } else { + SPDLOG_WARN("Animation data segment not found: {}", path); + } } else if (animType == AnimationType::Legacy) { SPDLOG_DEBUG("BEYTAH ANIMATION?!"); } diff --git a/soh/soh/util.cpp b/soh/soh/util.cpp index b3bc053f107..7efa4b7d160 100644 --- a/soh/soh/util.cpp +++ b/soh/soh/util.cpp @@ -796,3 +796,20 @@ uint32_t SohUtils::Hash(std::string str) { } return hval; } + +std::vector SohUtils::StringSplit(const std::string& str, const std::string& delimiter) { + std::vector tokens; + size_t pos = str.find(delimiter, 0); + size_t prevpos = 0; + + while (pos != std::string::npos) { + std::string token = str.substr(prevpos, pos - prevpos); + tokens.push_back(token); + prevpos = pos + 1; + pos = str.find(delimiter, prevpos); + } + + tokens.push_back(str.substr(prevpos)); + + return tokens; +} \ No newline at end of file diff --git a/soh/soh/util.h b/soh/soh/util.h index f704076b4d9..2058a344290 100644 --- a/soh/soh/util.h +++ b/soh/soh/util.h @@ -1,5 +1,6 @@ #pragma once #include +#include #include typedef enum FileType { FILE_TYPE_SAVE_VANILLA, FILE_TYPE_SAVE_RANDO, FILE_TYPE_PRESET, FILE_TYPE_SPOILER } FileType; @@ -25,4 +26,6 @@ size_t CopyStringToCharBuffer(char* buffer, const std::string& source, size_t ma bool IsStringEmpty(std::string str); uint32_t Hash(std::string str); + +std::vector StringSplit(const std::string& str, const std::string& delimiter); } // namespace SohUtils diff --git a/soh/soh/z_scene_otr.cpp b/soh/soh/z_scene_otr.cpp index 2ac7b8e3839..23e33203a2e 100644 --- a/soh/soh/z_scene_otr.cpp +++ b/soh/soh/z_scene_otr.cpp @@ -11,8 +11,10 @@ #include "soh/resource/type/Path.h" #include "soh/resource/type/Text.h" #include +#include #include #include +#include #include "soh/resource/type/scenecommand/SetCameraSettings.h" #include "soh/resource/type/scenecommand/SetCutscenes.h" #include "soh/resource/type/scenecommand/SetStartPositionList.h" @@ -39,6 +41,303 @@ extern Ship::IResource* OTRPlay_LoadFile(PlayState* play, const char* fileName); extern "C" s32 Object_Spawn(ObjectContext* objectCtx, s16 objectId); extern "C" RomFile sNaviMsgFiles[]; s32 OTRScene_ExecuteCommands(PlayState* play, SOH::Scene* scene); +bool OTRfunc_800982FC(ObjectContext* objectCtx, s32 bankIndex, s16 objectId); + +#define LOCAL_MP_DISABLED_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Disable") +#define LOCAL_MP_SECONDARY_AREA_LOAD_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.SecondaryPlayersLoadAreas") +#define LOCAL_MP_MAX_RETAINED_ROOMS 4 + +namespace { +typedef struct { + s32 roomNum; + Room roomData; + std::vector objectIds; +} OTRRetainedRoomEntry; + +std::array sRetainedRooms; +s32 sRetainedRoomCount = 0; +s32 sPendingObjectListRoomNum = -1; +std::vector sPendingObjectList; + +bool OTRRoom_AreaPersistenceEnabledInternal() { + return !CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0) && CVarGetInteger(LOCAL_MP_SECONDARY_AREA_LOAD_CVAR, 0); +} + +s16 OTRRoom_NormalizeObjectId(s16 objectId) { + return (objectId < 0) ? -objectId : objectId; +} + +bool OTRRoom_ObjectListContains(const std::vector& objectList, s16 objectId) { + for (s16 listObjectId : objectList) { + if (listObjectId == objectId) { + return true; + } + } + + return false; +} + +s32 OTRRoom_FindRetainedRoomIndexInternal(s32 roomNum) { + for (s32 i = 0; i < sRetainedRoomCount; i++) { + if (sRetainedRooms[i].roomNum == roomNum) { + return i; + } + } + + return -1; +} + +bool OTRRoom_IsRoomOccupiedByAnyPlayerInternal(PlayState* play, s32 roomNum) { + Actor* actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + s32 sanity = 0; + + if (roomNum < 0) { + return false; + } + + while ((actor != NULL) && (sanity < 2000)) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL) && (actor->room == roomNum)) { + return true; + } + + actor = actor->next; + sanity++; + } + + return false; +} + +void OTRRoom_RefreshObjectContextNumInternal(ObjectContext* objectCtx) { + s32 highestUsedSlot = objectCtx->unk_09; + + for (s32 i = objectCtx->unk_09; i < (OBJECT_EXCHANGE_BANK_MAX - 1); i++) { + if (objectCtx->status[i].id != OBJECT_INVALID) { + highestUsedSlot = i + 1; + } + } + + objectCtx->num = highestUsedSlot; +} + +s32 OTRRoom_FindObjectSlotInternal(const ObjectContext* objectCtx, s16 objectId) { + for (s32 i = objectCtx->unk_09; i < (OBJECT_EXCHANGE_BANK_MAX - 1); i++) { + if ((objectCtx->status[i].id != OBJECT_INVALID) && + (OTRRoom_NormalizeObjectId(objectCtx->status[i].id) == objectId)) { + return i; + } + } + + return -1; +} + +s32 OTRRoom_FindFreeObjectSlotInternal(const ObjectContext* objectCtx) { + for (s32 i = objectCtx->unk_09; i < (OBJECT_EXCHANGE_BANK_MAX - 1); i++) { + if (objectCtx->status[i].id == OBJECT_INVALID) { + return i; + } + } + + return -1; +} + +void OTRRoom_MoveRetainedRoomToBackInternal(s32 retainedRoomIndex) { + OTRRetainedRoomEntry movedRoom; + + if ((retainedRoomIndex < 0) || (retainedRoomIndex >= sRetainedRoomCount) || + (retainedRoomIndex == (sRetainedRoomCount - 1))) { + return; + } + + movedRoom = sRetainedRooms[retainedRoomIndex]; + + for (s32 i = retainedRoomIndex; i < (sRetainedRoomCount - 1); i++) { + sRetainedRooms[i] = sRetainedRooms[i + 1]; + } + + sRetainedRooms[sRetainedRoomCount - 1] = movedRoom; +} + +void OTRRoom_RemoveRetainedRoomAtInternal(s32 retainedRoomIndex) { + if ((retainedRoomIndex < 0) || (retainedRoomIndex >= sRetainedRoomCount)) { + return; + } + + for (s32 i = retainedRoomIndex; i < (sRetainedRoomCount - 1); i++) { + sRetainedRooms[i] = sRetainedRooms[i + 1]; + } + + sRetainedRoomCount--; + + if (sRetainedRoomCount >= 0) { + sRetainedRooms[sRetainedRoomCount].roomNum = -1; + sRetainedRooms[sRetainedRoomCount].roomData = {}; + sRetainedRooms[sRetainedRoomCount].roomData.num = -1; + sRetainedRooms[sRetainedRoomCount].objectIds.clear(); + } +} + +void OTRRoom_ClearRetainedStateInternal() { + sRetainedRoomCount = 0; + sPendingObjectListRoomNum = -1; + sPendingObjectList.clear(); + + for (OTRRetainedRoomEntry& retainedRoom : sRetainedRooms) { + retainedRoom.roomNum = -1; + retainedRoom.roomData = {}; + retainedRoom.roomData.num = -1; + retainedRoom.objectIds.clear(); + } +} + +void OTRRoom_SetPendingObjectListInternal(s32 roomNum, const SOH::SetObjectList* cmdObj) { + sPendingObjectListRoomNum = roomNum; + sPendingObjectList.assign(cmdObj->objects.begin(), cmdObj->objects.end()); + + s32 retainedRoomIndex = OTRRoom_FindRetainedRoomIndexInternal(roomNum); + if (retainedRoomIndex >= 0) { + sRetainedRooms[retainedRoomIndex].objectIds = sPendingObjectList; + } +} + +void OTRRoom_EnsureObjectsLoadedForListInternal(PlayState* play, const std::vector& objectList) { + for (s16 objectId : objectList) { + s32 freeSlot; + + if (objectId == OBJECT_INVALID) { + continue; + } + + if (OTRRoom_FindObjectSlotInternal(&play->objectCtx, objectId) >= 0) { + continue; + } + + freeSlot = OTRRoom_FindFreeObjectSlotInternal(&play->objectCtx); + if (freeSlot < 0) { + break; + } + + OTRfunc_800982FC(&play->objectCtx, freeSlot, objectId); + } + + OTRRoom_RefreshObjectContextNumInternal(&play->objectCtx); +} + +void OTRRoom_StoreCurrentRoomDataInternal(PlayState* play) { + s32 roomNum = play->roomCtx.curRoom.num; + s32 retainedRoomIndex; + + if (roomNum < 0) { + return; + } + + retainedRoomIndex = OTRRoom_FindRetainedRoomIndexInternal(roomNum); + if (retainedRoomIndex < 0) { + if (sRetainedRoomCount >= LOCAL_MP_MAX_RETAINED_ROOMS) { + s32 evictionIndex = -1; + + for (s32 i = 0; i < sRetainedRoomCount; i++) { + s32 candidateRoomNum = sRetainedRooms[i].roomNum; + + if ((candidateRoomNum != roomNum) && (candidateRoomNum != play->roomCtx.prevRoom.num) && + !OTRRoom_IsRoomOccupiedByAnyPlayerInternal(play, candidateRoomNum)) { + evictionIndex = i; + break; + } + } + + if (evictionIndex < 0) { + for (s32 i = 0; i < sRetainedRoomCount; i++) { + if (!OTRRoom_IsRoomOccupiedByAnyPlayerInternal(play, sRetainedRooms[i].roomNum)) { + evictionIndex = i; + break; + } + } + } + + if (evictionIndex < 0) { + evictionIndex = 0; + } + + OTRRoom_RemoveRetainedRoomAtInternal(evictionIndex); + } + + retainedRoomIndex = sRetainedRoomCount++; + sRetainedRooms[retainedRoomIndex].roomNum = roomNum; + sRetainedRooms[retainedRoomIndex].objectIds.clear(); + } else { + OTRRoom_MoveRetainedRoomToBackInternal(retainedRoomIndex); + retainedRoomIndex = sRetainedRoomCount - 1; + } + + sRetainedRooms[retainedRoomIndex].roomNum = roomNum; + sRetainedRooms[retainedRoomIndex].roomData = play->roomCtx.curRoom; + + if (sPendingObjectListRoomNum == roomNum) { + sRetainedRooms[retainedRoomIndex].objectIds = sPendingObjectList; + } +} + +void OTRRoom_RebuildObjectContextInternal(PlayState* play) { + std::vector requiredObjects; + + if (!OTRRoom_AreaPersistenceEnabledInternal()) { + return; + } + + for (s32 i = 0; i < sRetainedRoomCount; i++) { + for (s16 objectId : sRetainedRooms[i].objectIds) { + if ((objectId != OBJECT_INVALID) && !OTRRoom_ObjectListContains(requiredObjects, objectId)) { + requiredObjects.push_back(objectId); + } + } + } + + for (s32 i = play->objectCtx.unk_09; i < (OBJECT_EXCHANGE_BANK_MAX - 1); i++) { + s16 objectId = play->objectCtx.status[i].id; + + if ((objectId != OBJECT_INVALID) && + !OTRRoom_ObjectListContains(requiredObjects, OTRRoom_NormalizeObjectId(objectId))) { + play->objectCtx.status[i].id = OBJECT_INVALID; + } + } + + func_80031A28(play, &play->actorCtx); + + OTRRoom_EnsureObjectsLoadedForListInternal(play, requiredObjects); +} +} // namespace + +extern "C" s32 OTRRoom_AreaPersistenceEnabled(void) { + return OTRRoom_AreaPersistenceEnabledInternal(); +} + +extern "C" void OTRRoom_ResetAreaPersistence(void) { + OTRRoom_ClearRetainedStateInternal(); +} + +extern "C" s32 OTRRoom_IsRetainedRoom(s32 roomNum) { + if (!OTRRoom_AreaPersistenceEnabledInternal()) { + return 0; + } + + return OTRRoom_FindRetainedRoomIndexInternal(roomNum) >= 0; +} + +extern "C" s32 OTRRoom_GetRetainedRoomCount(void) { + if (!OTRRoom_AreaPersistenceEnabledInternal()) { + return 0; + } + + return sRetainedRoomCount; +} + +extern "C" Room* OTRRoom_GetRetainedRoom(s32 index) { + if (!OTRRoom_AreaPersistenceEnabledInternal() || (index < 0) || (index >= sRetainedRoomCount)) { + return NULL; + } + + return &sRetainedRooms[index].roomData; +} bool Scene_CommandSpawnList(PlayState* play, SOH::ISceneCommand* cmd) { // SOH::SetStartPositionList* cmdStartPos = std::static_pointer_cast(cmd); @@ -147,37 +446,38 @@ bool Scene_CommandObjectList(PlayState* play, SOH::ISceneCommand* cmd) { // SOH::SetObjectList* cmdObj = static_pointer_cast(cmd); SOH::SetObjectList* cmdObj = (SOH::SetObjectList*)cmd; - s32 i; - s32 j; - s32 k; - ObjectStatus* status2; - // s16* objectEntry = SEGMENTED_TO_VIRTUAL(cmd->objectList.segment); - s16* objectEntry = (s16*)cmdObj->GetRawPointer(); - void* nextPtr; - - k = 0; - i = play->objectCtx.unk_09; - - // Loop until a mismatch in the object lists - // Then clear all object ids past that in the context object list and kill actors for those objects - for (i = play->objectCtx.unk_09, k = 0; i < play->objectCtx.num; i++, k++) { - if (k >= cmdObj->objects.size() || play->objectCtx.status[i].id != cmdObj->objects[k]) { - for (j = i; j < play->objectCtx.num; j++) { - play->objectCtx.status[j].id = OBJECT_INVALID; + if (OTRRoom_AreaPersistenceEnabledInternal() && (play->roomCtx.curRoom.num >= 0)) { + OTRRoom_SetPendingObjectListInternal(play->roomCtx.curRoom.num, cmdObj); + OTRRoom_EnsureObjectsLoadedForListInternal(play, sPendingObjectList); + return false; + } + + { + s32 i; + s32 j; + s32 k; + + // Loop until a mismatch in the object lists + // Then clear all object ids past that in the context object list and kill actors for those objects + for (i = play->objectCtx.unk_09, k = 0; i < play->objectCtx.num; i++, k++) { + if (k >= cmdObj->objects.size() || play->objectCtx.status[i].id != cmdObj->objects[k]) { + for (j = i; j < play->objectCtx.num; j++) { + play->objectCtx.status[j].id = OBJECT_INVALID; + } + func_80031A28(play, &play->actorCtx); + break; } - func_80031A28(play, &play->actorCtx); - break; } - } - // Continuing from the last index, add the remaining object ids from the command object list - for (; k < cmdObj->objects.size(); k++, i++) { - if (i < OBJECT_EXCHANGE_BANK_MAX - 1) { - OTRfunc_800982FC(&play->objectCtx, i, cmdObj->objects[k]); + // Continuing from the last index, add the remaining object ids from the command object list + for (; k < cmdObj->objects.size(); k++, i++) { + if (i < OBJECT_EXCHANGE_BANK_MAX - 1) { + OTRfunc_800982FC(&play->objectCtx, i, cmdObj->objects[k]); + } } - } - play->objectCtx.num = i; + play->objectCtx.num = i; + } return false; } @@ -473,6 +773,11 @@ extern "C" s32 OTRfunc_800973FC(PlayState* play, RoomContext* roomCtx) { OTRScene_ExecuteCommands(play, (SOH::Scene*)roomCtx->roomToLoad); + if (OTRRoom_AreaPersistenceEnabledInternal()) { + OTRRoom_StoreCurrentRoomDataInternal(play); + OTRRoom_RebuildObjectContextInternal(play); + } + Player_SetBootData(play, GET_PLAYER(play)); Actor_SpawnTransitionActors(play, &play->actorCtx); @@ -491,6 +796,29 @@ extern "C" s32 OTRfunc_8009728C(PlayState* play, RoomContext* roomCtx, s32 roomN u32 size; if (roomCtx->status == 0) { + if ((roomNum < 0) || (roomNum >= play->numRooms)) { + return 0; + } + + if (OTRRoom_AreaPersistenceEnabledInternal()) { + s32 retainedRoomIndex = OTRRoom_FindRetainedRoomIndexInternal(roomNum); + + if (retainedRoomIndex >= 0) { + roomCtx->prevRoom = roomCtx->curRoom; + OTRRoom_MoveRetainedRoomToBackInternal(retainedRoomIndex); + roomCtx->curRoom = sRetainedRooms[sRetainedRoomCount - 1].roomData; + roomCtx->status = 0; + roomCtx->roomToLoad = NULL; + + if (roomCtx->curRoom.segment != NULL) { + gSegments[3] = VIRTUAL_TO_PHYSICAL(roomCtx->curRoom.segment); + } + + SPDLOG_INFO("Room Fast Swap - curRoom.num: {0:#x}", roomCtx->curRoom.num); + return 1; + } + } + roomCtx->prevRoom = roomCtx->curRoom; roomCtx->curRoom.num = roomNum; roomCtx->curRoom.segment = NULL; @@ -498,9 +826,6 @@ extern "C" s32 OTRfunc_8009728C(PlayState* play, RoomContext* roomCtx, s32 roomN assert(roomNum < play->numRooms); - if (roomNum >= play->numRooms) - return 0; // UH OH - size = play->roomList[roomNum].vromEnd - play->roomList[roomNum].vromStart; roomCtx->unk_34 = (void*)ALIGN16((uintptr_t)roomCtx->bufPtrs[roomCtx->unk_30] - ((size + 8) * roomCtx->unk_30 + 7)); @@ -514,6 +839,9 @@ extern "C" s32 OTRfunc_8009728C(PlayState* play, RoomContext* roomCtx, s32 roomN roomCtx->status = 1; roomCtx->roomToLoad = roomData.get(); + sPendingObjectListRoomNum = -1; + sPendingObjectList.clear(); + roomCtx->unk_30 ^= 1; SPDLOG_INFO("Room Init - curRoom.num: {0:#x}", roomCtx->curRoom.num); diff --git a/soh/src/buffers/gfxbuffers.c b/soh/src/buffers/gfxbuffers.c index 13206fe2b5a..53e8fae1dbd 100644 --- a/soh/src/buffers/gfxbuffers.c +++ b/soh/src/buffers/gfxbuffers.c @@ -9,5 +9,4 @@ u8 gGfxSPTaskYieldBuffer[OS_YIELD_DATA_SIZE]; // 0x400 bytes u8 gGfxSPTaskStack[0x400]; -// 0x12410 bytes each; 0x24820 bytes total -GfxPool gGfxPools[2]; +GfxPool gGfxPools[4]; diff --git a/soh/src/code/db_camera.c b/soh/src/code/db_camera.c index 591190136d4..953d715a290 100644 --- a/soh/src/code/db_camera.c +++ b/soh/src/code/db_camera.c @@ -520,6 +520,15 @@ void DbCamera_Update(DbCamera* dbCamera, Camera* cam) { s16 spAA; VecSph spA0; + if ((CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) ? 1 + : CVarGetInteger( + CVAR_ENHANCEMENT( + "LocalMultiplayer.PlayerCount"), + 2)) >= + 3) { + return; + } + sp90 = &dbCamera->unk_54; temp_s6 = &cam->playerPosRot; at = &cam->at; diff --git a/soh/src/code/graph.c b/soh/src/code/graph.c index 31dcb211eca..0b210431222 100644 --- a/soh/src/code/graph.c +++ b/soh/src/code/graph.c @@ -104,7 +104,7 @@ void Graph_UCodeFaultClient(Gfx* workBuf) { } void Graph_InitTHGA(GraphicsContext* gfxCtx) { - GfxPool* pool = &gGfxPools[gfxCtx->gfxPoolIdx & 1]; + GfxPool* pool = &gGfxPools[gfxCtx->gfxPoolIdx % ARRAY_COUNT(gGfxPools)]; pool->headMagic = GFXPOOL_HEAD_MAGIC; pool->tailMagic = GFXPOOL_TAIL_MAGIC; @@ -346,7 +346,7 @@ void Graph_Update(GraphicsContext* gfxCtx, GameState* gameState) { problem = false; { - GfxPool* pool = &gGfxPools[gfxCtx->gfxPoolIdx & 1]; + GfxPool* pool = &gGfxPools[gfxCtx->gfxPoolIdx % ARRAY_COUNT(gGfxPools)]; if (pool->headMagic != GFXPOOL_HEAD_MAGIC) { //! @bug (?) : "problem = true;" may be missing diff --git a/soh/src/code/main.c b/soh/src/code/main.c index c9d87c66cac..cdc5afdc847 100644 --- a/soh/src/code/main.c +++ b/soh/src/code/main.c @@ -91,7 +91,7 @@ void Main(void* arg) { SysCfb_Init(0); sysHeap = (uintptr_t)gSystemHeap; fb = SysCfb_GetFbPtr(0); - gSystemHeapSize = 1024 * 1024 * 4; + gSystemHeapSize = SYSTEM_HEAP_SIZE; // "System heap initalization" osSyncPrintf("システムヒープ初期化 %08x-%08x %08x\n", sysHeap, fb, gSystemHeapSize); SystemHeap_Init((void*)sysHeap, gSystemHeapSize); // initializes the system heap diff --git a/soh/src/code/shrink_window.c b/soh/src/code/shrink_window.c index ee4d2e96711..d0b4ce6bd14 100644 --- a/soh/src/code/shrink_window.c +++ b/soh/src/code/shrink_window.c @@ -1,12 +1,20 @@ #include "global.h" +#define LOCAL_MP_PLAYER_COUNT_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount") +#define LOCAL_MP_DISABLED_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Disable") + s32 D_8012CED0 = 0; s32 sShrinkWindowVal = 0; s32 sShrinkWindowCurrentVal = 0; +static s32 ShrinkWindow_ShouldForceDisabled(void) { + return CVarGetInteger(CVAR_ENHANCEMENT("DisableBlackBars"), 0) || + (!CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0) && (CVarGetInteger(LOCAL_MP_PLAYER_COUNT_CVAR, 2) > 1)); +} + void ShrinkWindow_SetVal(s32 value) { - if (CVarGetInteger(CVAR_ENHANCEMENT("DisableBlackBars"), 0)) { + if (ShrinkWindow_ShouldForceDisabled()) { sShrinkWindowVal = 0; return; } @@ -21,7 +29,7 @@ u32 ShrinkWindow_GetVal(void) { } void ShrinkWindow_SetCurrentVal(s32 currentVal) { - if (CVarGetInteger(CVAR_ENHANCEMENT("DisableBlackBars"), 0)) { + if (ShrinkWindow_ShouldForceDisabled()) { sShrinkWindowCurrentVal = 0; return; } diff --git a/soh/src/code/z_actor.c b/soh/src/code/z_actor.c index 54c33e2cfa9..43857e12d8f 100644 --- a/soh/src/code/z_actor.c +++ b/soh/src/code/z_actor.c @@ -90,6 +90,7 @@ f32 iceTrapScale; // For Link's voice pitch SFX modifier static f32 freqMultiplier = 1; +static s32 sActorSfxUpdatesSuppressed = false; void ActorShape_Init(ActorShape* shape, f32 yOffset, ActorShadowFunc shadowDraw, f32 shadowScale) { shape->yOffset = yOffset; @@ -439,9 +440,48 @@ void func_8002C0C0(TargetContext* targetCtx, Actor* actor, PlayState* play) { func_8002BE98(targetCtx, actor->category, play); } +static s32 Actor_IsLocalMultiplayerActive(PlayState* play) { + Actor* playerActor; + s32 activePlayers = 0; + s32 sanity = 0; + s32 configuredPlayers; + + if (play == NULL) { + return false; + } + + configuredPlayers = CVarGetInteger("gEnhancements.LocalMultiplayer.Disable", 0) + ? 1 + : CVarGetInteger("gEnhancements.LocalMultiplayer.PlayerCount", 2); + + if (configuredPlayers > 1) { + return true; + } + + playerActor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + + while ((playerActor != NULL) && (sanity < 2000)) { + if ((playerActor->id == ACTOR_PLAYER) && (playerActor->update != NULL)) { + activePlayers++; + if (activePlayers > 1) { + return true; + } + } + + playerActor = playerActor->next; + sanity++; + } + + return false; +} + void func_8002C124(TargetContext* targetCtx, PlayState* play) { Actor* actor = targetCtx->targetedActor; + if (Actor_IsLocalMultiplayerActive(play)) { + return; + } + OPEN_DISPS(play->state.gfxCtx); if (targetCtx->unk_48 != 0) { @@ -1398,6 +1438,126 @@ f32 Actor_HeightDiff(Actor* actorA, Actor* actorB) { return actorB->world.pos.y - actorA->world.pos.y; } +static Player* Actor_FindClosestActivePlayerToPos(PlayState* play, const Vec3f* pos, Player* fallbackPlayer) { + Actor* playerActor; + Player* closestPlayer = fallbackPlayer; + f32 bestDistSq = FLT_MAX; + s32 sanity = 0; + + if ((play == NULL) || (pos == NULL)) { + return fallbackPlayer; + } + + playerActor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + + while ((playerActor != NULL) && (sanity < 2000)) { + if ((playerActor->id == ACTOR_PLAYER) && (playerActor->update != NULL)) { + Player* player = (Player*)playerActor; + f32 dx = pos->x - player->actor.world.pos.x; + f32 dy = pos->y - player->actor.world.pos.y; + f32 dz = pos->z - player->actor.world.pos.z; + f32 distSq = SQ(dx) + SQ(dy) + SQ(dz); + + if (distSq < bestDistSq) { + bestDistSq = distSq; + closestPlayer = player; + } + } + + playerActor = playerActor->next; + sanity++; + } + + return closestPlayer; +} + +Player* Actor_GetClosestPlayerFromPos(PlayState* play, const Vec3f* pos) { + Player* fallbackPlayer = (play != NULL) ? GET_PLAYER(play) : NULL; + + return Actor_FindClosestActivePlayerToPos(play, pos, fallbackPlayer); +} + +Player* Actor_GetClosestPlayer(PlayState* play, Actor* actor) { + if (actor == NULL) { + return (play != NULL) ? GET_PLAYER(play) : NULL; + } + + return Actor_GetClosestPlayerFromPos(play, &actor->world.pos); +} + +static Player* Actor_FindClosestActivePlayer(PlayState* play, Actor* actor, Player* fallbackPlayer) { + if (actor == NULL) { + return fallbackPlayer; + } + + return Actor_FindClosestActivePlayerToPos(play, &actor->world.pos, fallbackPlayer); +} + +static s32 Actor_ShouldTrackClosestPlayer(const Actor* actor) { + if (actor == NULL) { + return false; + } + + switch (actor->category) { + case ACTORCAT_BG: + case ACTORCAT_ENEMY: + case ACTORCAT_PROP: + case ACTORCAT_ITEMACTION: + case ACTORCAT_MISC: + case ACTORCAT_BOSS: + return true; + default: + return false; + } +} + +static s32 Actor_IsEnemyCategory(const Actor* actor) { + if (actor == NULL) { + return false; + } + + return (actor->category == ACTORCAT_ENEMY) || (actor->category == ACTORCAT_BOSS); +} + +static s32 Actor_IsRoomOccupiedByAnyActivePlayer(PlayState* play, s32 roomNum) { + Actor* playerActor; + s32 sanity = 0; + + if ((play == NULL) || (roomNum < 0)) { + return false; + } + + playerActor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + + while ((playerActor != NULL) && (sanity < 2000)) { + if ((playerActor->id == ACTOR_PLAYER) && (playerActor->update != NULL) && (playerActor->room == roomNum)) { + return true; + } + + playerActor = playerActor->next; + sanity++; + } + + return false; +} + +static s32 Actor_AnyPlayerHasFocusActor(PlayState* play) { + Actor* playerActor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + s32 sanity = 0; + + while ((playerActor != NULL) && (sanity < 2000)) { + if ((playerActor->id == ACTOR_PLAYER) && (playerActor->update != NULL) && + (((Player*)playerActor)->focusActor != NULL)) { + return true; + } + + playerActor = playerActor->next; + sanity++; + } + + return false; +} + f32 Player_GetHeight(Player* player) { f32 offset = (player->stateFlags1 & PLAYER_STATE1_ON_HORSE) ? 32.0f : 0.0f; @@ -1509,7 +1669,7 @@ void func_8002DFA4(DynaPolyActor* dynaActor, f32 arg1, s16 arg2) { * The maximum angle difference that qualifies as "facing" is specified by `maxAngle`. */ s32 Player_IsFacingActor(Actor* actor, s16 maxAngle, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = Actor_FindClosestActivePlayer(play, actor, GET_PLAYER(play)); s16 yawDiff = (s16)(actor->yawTowardsPlayer + 0x8000) - player->actor.shape.rot.y; if (ABS(yawDiff) < maxAngle) { @@ -1882,15 +2042,17 @@ PosRot* Actor_GetWorldPosShapeRot(PosRot* arg0, Actor* actor) { } f32 func_8002EFC0(Actor* actor, Player* player, s16 arg2) { - s16 yawTemp = (s16)(actor->yawTowardsPlayer - 0x8000) - arg2; + f32 distSq = SQ(actor->world.pos.x - player->actor.world.pos.x) + + SQ(actor->world.pos.y - player->actor.world.pos.y) + + SQ(actor->world.pos.z - player->actor.world.pos.z); + s16 yawTemp = (s16)(Actor_WorldYawTowardActor(actor, &player->actor) - 0x8000) - arg2; s16 yawTempAbs = ABS(yawTemp); if (player->focusActor != NULL) { if ((yawTempAbs > 0x4000) || (actor->flags & ACTOR_FLAG_LOCK_ON_DISABLED)) { return FLT_MAX; } else { - f32 ret = - actor->xyzDistToPlayerSq - actor->xyzDistToPlayerSq * 0.8f * ((0x4000 - yawTempAbs) * (1.0f / 0x8000)); + f32 ret = distSq - distSq * 0.8f * ((0x4000 - yawTempAbs) * (1.0f / 0x8000)); return ret; } @@ -1900,7 +2062,7 @@ f32 func_8002EFC0(Actor* actor, Player* player, s16 arg2) { return FLT_MAX; } - return actor->xyzDistToPlayerSq; + return distSq; } typedef struct { @@ -1927,14 +2089,16 @@ s32 func_8002F0C8(Actor* actor, Player* player, s32 flag) { } if (!flag) { - s16 var = (s16)(actor->yawTowardsPlayer - 0x8000) - player->actor.shape.rot.y; + s16 var = (s16)(Actor_WorldYawTowardActor(actor, &player->actor) - 0x8000) - player->actor.shape.rot.y; s16 abs_var = ABS(var); f32 dist; if ((player->focusActor == NULL) && (abs_var > 0x2AAA)) { dist = FLT_MAX; } else { - dist = actor->xyzDistToPlayerSq; + dist = SQ(actor->world.pos.x - player->actor.world.pos.x) + + SQ(actor->world.pos.y - player->actor.world.pos.y) + + SQ(actor->world.pos.z - player->actor.world.pos.z); } return !func_8002F090(actor, D_80115FF8[actor->targetMode].leashScale * dist); @@ -2108,8 +2272,67 @@ s32 GiveItemEntryFromActorWithFixedRange(Actor* actor, PlayState* play, GetItemE return GiveItemEntryFromActor(actor, play, getItemEntry, 50.0f, 10.0f); } +static Player* Actor_FindCarryTargetPlayer(Actor* actor, PlayState* play, f32 xzRange, f32 yRange, + s32* outAbsYawDiff) { + Actor* playerActor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + Player* bestPlayer = NULL; + f32 bestDistSq = FLT_MAX; + f32 maxDistSq = SQ(xzRange); + s32 sanity = 0; + + *outAbsYawDiff = 0; + + while ((playerActor != NULL) && (sanity < 2000)) { + if ((playerActor->id == ACTOR_PLAYER) && (playerActor->update != NULL)) { + Player* player = (Player*)playerActor; + + if (!(player->stateFlags1 & + (PLAYER_STATE1_DEAD | PLAYER_STATE1_CHARGING_SPIN_ATTACK | PLAYER_STATE1_HANGING_OFF_LEDGE | + PLAYER_STATE1_CLIMBING_LEDGE | PLAYER_STATE1_JUMPING | PLAYER_STATE1_FREEFALL | + PLAYER_STATE1_FIRST_PERSON | PLAYER_STATE1_CLIMBING_LADDER)) && + (Player_GetExplosiveHeld(player) < 0) && + !(player->stateFlags1 & (PLAYER_STATE1_CARRYING_ACTOR | PLAYER_STATE1_IN_CUTSCENE))) { + f32 dx = actor->world.pos.x - player->actor.world.pos.x; + f32 dy = actor->world.pos.y - player->actor.world.pos.y; + f32 dz = actor->world.pos.z - player->actor.world.pos.z; + f32 distSq = SQ(dx) + SQ(dz); + + if ((distSq < maxDistSq) && (fabsf(dy) < yRange)) { + s16 yawDiff = Actor_WorldYawTowardActor(actor, &player->actor) - player->actor.shape.rot.y; + s32 absYawDiff = ABS(yawDiff); + + if ((player->getItemDirection < absYawDiff) && (distSq < bestDistSq)) { + bestDistSq = distSq; + bestPlayer = player; + *outAbsYawDiff = absYawDiff; + } + } + } + } + + playerActor = playerActor->next; + sanity++; + } + + return bestPlayer; +} + // If you're doing something for randomizer, you're probably looking for GiveItemEntryFromActor s32 Actor_OfferGetItem(Actor* actor, PlayState* play, s32 getItemId, f32 xzRange, f32 yRange) { + if (getItemId == GI_NONE) { + s32 absYawDiff = 0; + Player* carryPlayer = Actor_FindCarryTargetPlayer(actor, play, xzRange, yRange, &absYawDiff); + + if (carryPlayer != NULL) { + carryPlayer->getItemId = getItemId; + carryPlayer->interactRangeActor = actor; + carryPlayer->getItemDirection = absYawDiff; + return true; + } + + return false; + } + Player* player = GET_PLAYER(play); if (!(player->stateFlags1 & @@ -2210,7 +2433,11 @@ s32 Actor_NotMounted(PlayState* play, Actor* horse) { } void func_8002F698(PlayState* play, Actor* actor, f32 arg2, s16 arg3, f32 arg4, u32 arg5, u32 arg6) { - Player* player = GET_PLAYER(play); + Player* player = (actor != NULL) ? Actor_FindClosestActivePlayer(play, actor, GET_PLAYER(play)) : GET_PLAYER(play); + + if (player == NULL) { + return; + } player->knockbackDamage = arg6; player->knockbackType = arg5; @@ -2573,6 +2800,7 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) { Actor* refActor; Actor* actor; Player* player; + s32 localMultiplayerRetainedUpdatesEnabled; u32* sp80; u32 unkFlag; u32 unkCondition; @@ -2581,6 +2809,8 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) { s32 i; player = GET_PLAYER(play); + localMultiplayerRetainedUpdatesEnabled = + OTRRoom_AreaPersistenceEnabled() && Actor_IsLocalMultiplayerActive(play); sp74 = NULL; unkFlag = 0; @@ -2661,23 +2891,31 @@ void Actor_UpdateAll(PlayState* play, ActorContext* actorCtx) { actor = actor->next; } } else { + Player* distPlayer = player; + + if (Actor_ShouldTrackClosestPlayer(actor)) { + distPlayer = Actor_FindClosestActivePlayer(play, actor, player); + } + Math_Vec3f_Copy(&actor->prevPos, &actor->world.pos); - actor->xzDistToPlayer = Actor_WorldDistXZToActor(actor, &player->actor); - actor->yDistToPlayer = Actor_HeightDiff(actor, &player->actor); + actor->xzDistToPlayer = Actor_WorldDistXZToActor(actor, &distPlayer->actor); + actor->yDistToPlayer = Actor_HeightDiff(actor, &distPlayer->actor); actor->xyzDistToPlayerSq = SQ(actor->xzDistToPlayer) + SQ(actor->yDistToPlayer); - actor->yawTowardsPlayer = Actor_WorldYawTowardActor(actor, &player->actor); + actor->yawTowardsPlayer = Actor_WorldYawTowardActor(actor, &distPlayer->actor); actor->flags &= ~ACTOR_FLAG_SFX_FOR_PLAYER_BODY_HIT; + s32 shouldBypassUpdateCulling = + localMultiplayerRetainedUpdatesEnabled && Actor_IsEnemyCategory(actor) && + (actor->room >= 0) && OTRRoom_IsRetainedRoom(actor->room) && + Actor_IsRoomOccupiedByAnyActivePlayer(play, actor->room); + if ((DECR(actor->freezeTimer) == 0) && - (actor->flags & (ACTOR_FLAG_UPDATE_CULLING_DISABLED | ACTOR_FLAG_INSIDE_CULLING_VOLUME))) { - if (actor == player->focusActor) { - actor->isTargeted = true; - } else { - actor->isTargeted = false; - } + ((actor->flags & (ACTOR_FLAG_UPDATE_CULLING_DISABLED | ACTOR_FLAG_INSIDE_CULLING_VOLUME)) || + shouldBypassUpdateCulling)) { + actor->isTargeted = Actor_IsTargeted(play, actor); - if ((actor->targetPriority != 0) && (player->focusActor == NULL)) { + if ((actor->targetPriority != 0) && !Actor_AnyPlayerHasFocusActor(play)) { actor->targetPriority = 0; } @@ -2829,6 +3067,18 @@ void func_80030ED8(Actor* actor) { } } +void Actor_PlaySfx(Actor* actor) { + if (actor == NULL) { + return; + } + + func_80030ED8(actor); +} + +void Actor_SetSfxUpdatesSuppressed(s32 suppressed) { + sActorSfxUpdatesSuppressed = (suppressed != 0); +} + #define LENS_MASK_WIDTH 64 #define LENS_MASK_HEIGHT 64 // 26 and 6 are for padding between the mask texture and the screen borders @@ -3066,7 +3316,7 @@ void func_800315AC(PlayState* play, ActorContext* actorCtx) { } if ((HREG(64) != 1) || ((HREG(65) != -1) && (HREG(65) != HREG(66))) || (HREG(69) == 0)) { - if (actor->sfx != 0) { + if (!sActorSfxUpdatesSuppressed && (actor->sfx != 0)) { func_80030ED8(actor); } } @@ -3192,7 +3442,7 @@ void func_80031B14(PlayState* play, ActorContext* actorCtx) { actor = actorCtx->actorLists[i].head; while (actor != NULL) { if ((actor->room >= 0) && (actor->room != play->roomCtx.curRoom.num) && - (actor->room != play->roomCtx.prevRoom.num)) { + (actor->room != play->roomCtx.prevRoom.num) && !OTRRoom_IsRetainedRoom(actor->room)) { if (!actor->isDrawn) { actor = Actor_Delete(actorCtx, actor, play); } else { @@ -3449,12 +3699,23 @@ void Actor_SpawnTransitionActors(PlayState* play, ActorContext* actorCtx) { for (i = 0; i < numActors; i++) { if (transitionActor->id >= 0) { - if (((transitionActor->sides[0].room >= 0) && + s32 shouldSpawnTransitionActor = 0; + + if (OTRRoom_AreaPersistenceEnabled()) { + shouldSpawnTransitionActor = + ((transitionActor->sides[0].room >= 0) && OTRRoom_IsRetainedRoom(transitionActor->sides[0].room)) || + ((transitionActor->sides[1].room >= 0) && OTRRoom_IsRetainedRoom(transitionActor->sides[1].room)); + } else { + shouldSpawnTransitionActor = + ((transitionActor->sides[0].room >= 0) && ((transitionActor->sides[0].room == play->roomCtx.curRoom.num) || (transitionActor->sides[0].room == play->roomCtx.prevRoom.num))) || ((transitionActor->sides[1].room >= 0) && ((transitionActor->sides[1].room == play->roomCtx.curRoom.num) || - (transitionActor->sides[1].room == play->roomCtx.prevRoom.num)))) { + (transitionActor->sides[1].room == play->roomCtx.prevRoom.num))); + } + + if (shouldSpawnTransitionActor) { Actor_Spawn(actorCtx, play, (s16)(transitionActor->id & 0x1FFF), transitionActor->pos.x, transitionActor->pos.y, transitionActor->pos.z, 0, transitionActor->rotY, 0, (i << 0xA) + transitionActor->params); @@ -3559,6 +3820,10 @@ void func_800328D4(PlayState* play, ActorContext* actorCtx, Player* player, u32 sp84 = player->focusActor; while (actor != NULL) { + f32 distSqToPlayer = SQ(actor->world.pos.x - player->actor.world.pos.x) + + SQ(actor->world.pos.y - player->actor.world.pos.y) + + SQ(actor->world.pos.z - player->actor.world.pos.z); + if ((actor->update != NULL) && ((Player*)actor != player) && CHECK_FLAG_ALL(actor->flags, ACTOR_FLAG_ATTENTION_ENABLED)) { @@ -3568,15 +3833,16 @@ void func_800328D4(PlayState* play, ActorContext* actorCtx, Player* player, u32 VB_DETECT_BGM_ENEMY, (actorCategory == ACTORCAT_ENEMY) && CHECK_FLAG_ALL(actor->flags, ACTOR_FLAG_ATTENTION_ENABLED | ACTOR_FLAG_HOSTILE) && - (actor->xyzDistToPlayerSq < SQ(500.0f)) && (actor->xyzDistToPlayerSq < sbgmEnemyDistSq), + (distSqToPlayer < SQ(500.0f)) && (distSqToPlayer < sbgmEnemyDistSq), actor, &sbgmEnemyDistSq, (int32_t)actorCategory)) { actorCtx->targetCtx.bgmEnemy = actor; - sbgmEnemyDistSq = actor->xyzDistToPlayerSq; + sbgmEnemyDistSq = distSqToPlayer; } if (actor != sp84) { var = func_8002EFC0(actor, player, D_8015BBFC); - if ((var < D_8015BBF0) && func_8002F090(actor, var) && func_80032880(play, actor) && + if ((var < D_8015BBF0) && func_8002F090(actor, var) && + (player->isSecondPlayer || func_80032880(play, actor)) && (!BgCheck_CameraLineTest1(&play->colCtx, &player->actor.focus.pos, &actor->focus.pos, &sp70, &sp80, 1, 1, 1, 1, &sp7C) || SurfaceType_IsIgnoredByProjectiles(&play->colCtx, sp80, sp7C))) { @@ -4065,26 +4331,47 @@ s16 Actor_TestFloorInDirection(Actor* actor, PlayState* play, f32 distance, s16 * Returns true if the player is targeting the provided actor */ s32 Actor_IsTargeted(PlayState* play, Actor* actor) { - Player* player = GET_PLAYER(play); + Actor* playerActor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + s32 sanity = 0; - if ((player->stateFlags1 & PLAYER_STATE1_HOSTILE_LOCK_ON) && actor->isTargeted) { - return true; - } else { - return false; + while ((playerActor != NULL) && (sanity < 2000)) { + if ((playerActor->id == ACTOR_PLAYER) && (playerActor->update != NULL)) { + Player* player = (Player*)playerActor; + + if ((player->stateFlags1 & PLAYER_STATE1_HOSTILE_LOCK_ON) && (player->focusActor == actor)) { + return true; + } + } + + playerActor = playerActor->next; + sanity++; } + + return false; } /** * Returns true if the player is targeting an actor other than the provided actor */ s32 Actor_OtherIsTargeted(PlayState* play, Actor* actor) { - Player* player = GET_PLAYER(play); + Actor* playerActor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + s32 sanity = 0; - if ((player->stateFlags1 & PLAYER_STATE1_HOSTILE_LOCK_ON) && !actor->isTargeted) { - return true; - } else { - return false; + while ((playerActor != NULL) && (sanity < 2000)) { + if ((playerActor->id == ACTOR_PLAYER) && (playerActor->update != NULL)) { + Player* player = (Player*)playerActor; + + if ((player->stateFlags1 & PLAYER_STATE1_HOSTILE_LOCK_ON) && (player->focusActor != NULL) && + (player->focusActor != actor)) { + return true; + } + } + + playerActor = playerActor->next; + sanity++; } + + return false; } f32 func_80033AEC(Vec3f* arg0, Vec3f* arg1, f32 arg2, f32 arg3, f32 arg4, f32 arg5) { @@ -4716,7 +5003,7 @@ Actor* Actor_FindNearby(PlayState* play, Actor* refActor, s16 actorId, u8 actorC } s32 func_800354B4(PlayState* play, Actor* actor, f32 range, s16 arg3, s16 arg4, s16 arg5) { - Player* player = GET_PLAYER(play); + Player* player = Actor_FindClosestActivePlayer(play, actor, GET_PLAYER(play)); s16 var1; s16 var2; diff --git a/soh/src/code/z_camera.c b/soh/src/code/z_camera.c index 0d30dcb2e60..7c0b349b74c 100644 --- a/soh/src/code/z_camera.c +++ b/soh/src/code/z_camera.c @@ -9,6 +9,35 @@ #include "soh/frame_interpolation.h" #include "soh/Enhancements/controls/Mouse.h" +#define LOCAL_MP_PLAYER_COUNT_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount") +#define LOCAL_MP_DISABLED_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Disable") + +static s32 Camera_IsLocalMultiplayerEnabled(void) { + return !CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0) && (CVarGetInteger(LOCAL_MP_PLAYER_COUNT_CVAR, 2) > 1); +} + +static s32 Camera_IsActorInPlayState(PlayState* play, Actor* actor) { + if ((play == NULL) || (actor == NULL)) { + return false; + } + + for (s32 category = 0; category < ACTORCAT_MAX; category++) { + Actor* iter = play->actorCtx.actorLists[category].head; + s32 sanity = 0; + + while ((iter != NULL) && (sanity < 2000)) { + if (iter == actor) { + return true; + } + + iter = iter->next; + sanity++; + } + } + + return false; +} + s16 Camera_ChangeSettingFlags(Camera* camera, s16 setting, s16 flags); s32 Camera_ChangeModeFlags(Camera* camera, s16 mode, u8 flags); s32 Camera_QRegInit(void); @@ -755,7 +784,15 @@ s32 Camera_CopyPREGToModeValues(Camera* camera) { void Camera_UpdateInterface(s16 flags) { s16 interfaceAlpha; - if ((flags & SHRINKWIN_MASK) != SHRINKWIN_MASK) { + if (Camera_IsLocalMultiplayerEnabled()) { + sCameraShrinkWindowVal = 0; + + if (flags & SHRINKWIN_CURVAL) { + ShrinkWindow_SetCurrentVal(0); + } else { + ShrinkWindow_SetVal(0); + } + } else if ((flags & SHRINKWIN_MASK) != SHRINKWIN_MASK) { switch (flags & SHRINKWINVAL_MASK) { case 0x1000: sCameraShrinkWindowVal = 0x1A; @@ -6588,7 +6625,12 @@ s32 Camera_Special5(Camera* camera) { OLib_Vec3fDiffToVecSphGeo(&sp64, at, eye); OLib_Vec3fDiffToVecSphGeo(&sp5C, at, eyeNext); - Actor_GetWorld(&spA8, camera->target); + + if ((camera->target != NULL) && (camera->target->update != NULL)) { + Actor_GetWorld(&spA8, camera->target); + } else { + camera->target = NULL; + } sCameraInterfaceFlags = spec5->interfaceFlags; @@ -6600,7 +6642,7 @@ s32 Camera_Special5(Camera* camera) { if (anim->animTimer > 0) { anim->animTimer--; } else if (anim->animTimer == 0) { - if (camera->target == NULL || camera->target->update == NULL) { + if (camera->target == NULL) { camera->target = NULL; return true; } @@ -6833,9 +6875,10 @@ s32 Camera_Special9(Camera* camera) { Camera_CopyPREGToModeValues(camera); } - if (spec9->doorParams.doorActor != NULL) { + if ((spec9->doorParams.doorActor != NULL) && Camera_IsActorInPlayState(camera->play, spec9->doorParams.doorActor)) { Actor_GetWorldPosShapeRot(&adjustedPlayerPosRot, spec9->doorParams.doorActor); } else { + spec9->doorParams.doorActor = NULL; adjustedPlayerPosRot = *playerPosRot; adjustedPlayerPosRot.pos.y += playerYOffset + params->yOffset; adjustedPlayerPosRot.rot.x = 0; @@ -7372,8 +7415,13 @@ s32 Camera_UpdateHotRoom(Camera* camera) { s32 Camera_DbgChangeMode(Camera* camera) { s32 changeDir = 0; + s32 localMpPlayerCount = (CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2)); + s32 localMpUsesThirdController = localMpPlayerCount >= 3; - if (!gDbgCamEnabled && camera->play->activeCamera == MAIN_CAM) { + if (!gDbgCamEnabled && camera->play->activeCamera == MAIN_CAM && + CVarGetInteger(CVAR_DEVELOPER_TOOLS("DebugEnabled"), 0) && !localMpUsesThirdController) { if (CHECK_BTN_ALL(D_8015BD7C->state.input[2].press.button, BTN_CUP)) { osSyncPrintf("attention sound URGENCY\n"); Sfx_PlaySfxCentered(NA_SE_SY_ATTENTION_URGENCY); @@ -7632,8 +7680,17 @@ Vec3s Camera_Update(Camera* camera) { sCameraSettings[camera->setting].cameraModes[camera->mode].funcIdx); } + if (gDbgCamEnabled && ((CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2)) >= 3)) { + gDbgCamEnabled = 0; + } + // enable/disable debug cam if (CVarGetInteger(CVAR_DEVELOPER_TOOLS("DebugEnabled"), 0) && + ((CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) + ? 1 + : CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount"), 2)) < 3) && CHECK_BTN_ALL(D_8015BD7C->state.input[2].press.button, BTN_START)) { gDbgCamEnabled ^= 1; if (gDbgCamEnabled) { diff --git a/soh/src/code/z_en_item00.c b/soh/src/code/z_en_item00.c index e8e29a19df1..2772c8d5864 100644 --- a/soh/src/code/z_en_item00.c +++ b/soh/src/code/z_en_item00.c @@ -333,6 +333,38 @@ void EnItem00_SetupAction(EnItem00* this, EnItem00ActionFunc actionFunc) { this->actionFunc = actionFunc; } +static Player* EnItem00_FindClosestPlayer(PlayState* play, Vec3f* pos, f32* distSqOut) { + Actor* actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + Player* closest = NULL; + f32 minDistSq = 1.0e20f; + + while (actor != NULL) { + if (actor->id == ACTOR_PLAYER) { + f32 dx = pos->x - actor->world.pos.x; + f32 dy = pos->y - actor->world.pos.y; + f32 dz = pos->z - actor->world.pos.z; + f32 distSq = dx * dx + dz * dz; + + if ((dy >= -50.0f) && (dy <= 50.0f) && (distSq < minDistSq)) { + closest = (Player*)actor; + minDistSq = distSq; + } + } + + actor = actor->next; + } + + if (distSqOut != NULL) { + *distSqOut = minDistSq; + } + + if (closest != NULL) { + return closest; + } + + return GET_PLAYER(play); +} + void EnItem00_SetObjectDependency(EnItem00* this, PlayState* play, s16 objectIndex) { // Remove object dependency for Enemy Randomizer and Crowd Control to allow Like-likes to // drop equipment correctly in rooms where Like-likes normally don't spawn. @@ -528,10 +560,10 @@ void EnItem00_Init(Actor* thisx, PlayState* play) { Item_Give(play, ITEM_RUPEE_GOLD); break; case ITEM00_HEART: - Item_Give(play, ITEM_HEART); + Health_ChangeBy(play, 0x10, EnItem00_FindClosestPlayer(play, &this->actor.world.pos, NULL)); break; case ITEM00_FLEXIBLE: - Health_ChangeBy(play, 0x70); + Health_ChangeBy(play, 0x70, EnItem00_FindClosestPlayer(play, &this->actor.world.pos, NULL)); break; case ITEM00_BOMBS_A: case ITEM00_BOMBS_B: @@ -723,7 +755,14 @@ void func_8001E304(EnItem00* this, PlayState* play) { } void func_8001E5C8(EnItem00* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player; + + if ((this->actor.parent != NULL) && (this->actor.parent->id == ACTOR_PLAYER)) { + player = (Player*)this->actor.parent; + } else { + player = GET_PLAYER(play); + } + if (this->getItemId != GI_NONE) { if (!Actor_HasParent(&this->actor, play)) { Actor_OfferGetItem(&this->actor, play, this->getItemId, 50.0f, 80.0f); @@ -760,6 +799,8 @@ void EnItem00_Update(Actor* thisx, PlayState* play) { static s16 D_80157D94[1]; s16* params; Actor* dynaActor; + Player* closestPlayer; + f32 minDistSq; s32 getItemId = GI_NONE; s16 sp3A = 0; s16 i; @@ -847,11 +888,18 @@ void EnItem00_Update(Actor* thisx, PlayState* play) { return; } - if (!((this->actor.xzDistToPlayer <= 30.0f) && (this->actor.yDistToPlayer >= -50.0f) && - (this->actor.yDistToPlayer <= 50.0f))) { + closestPlayer = EnItem00_FindClosestPlayer(play, &this->actor.world.pos, &minDistSq); + if (minDistSq > 900.0f) { + closestPlayer = NULL; + } + + if (closestPlayer == NULL) { if (!Actor_HasParent(&this->actor, play)) { return; } + } else if (!Actor_HasParent(&this->actor, play) && (closestPlayer != GET_PLAYER(play))) { + this->actor.parent = &closestPlayer->actor; + closestPlayer->interactRangeActor = &this->actor; } if (play->gameOverCtx.state != GAMEOVER_INACTIVE) { @@ -885,10 +933,10 @@ void EnItem00_Update(Actor* thisx, PlayState* play) { getItemId = GI_NUTS_5; break; case ITEM00_HEART: - Item_Give(play, ITEM_HEART); + Health_ChangeBy(play, 0x10, (closestPlayer != NULL) ? closestPlayer : GET_PLAYER(play)); break; case ITEM00_FLEXIBLE: - Health_ChangeBy(play, 0x70); + Health_ChangeBy(play, 0x70, (closestPlayer != NULL) ? closestPlayer : GET_PLAYER(play)); break; case ITEM00_BOMBS_A: case ITEM00_BOMBS_B: diff --git a/soh/src/code/z_lifemeter.c b/soh/src/code/z_lifemeter.c index cd6de967ea0..8a509236a72 100644 --- a/soh/src/code/z_lifemeter.c +++ b/soh/src/code/z_lifemeter.c @@ -5,6 +5,11 @@ #include "soh/Enhancements/cosmetics/cosmeticsTypes.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" +#define LOCAL_MP_PLAYER_COUNT_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount") +#define LOCAL_MP_DISABLED_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Disable") +#define LOCAL_MP_SPLITSCREEN_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreen") +#define LOCAL_MP_SPLITSCREEN_VERTICAL_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreenVertical") + s16 Top_LM_Margin = 0; s16 Left_LM_Margin = 0; s16 Right_LM_Margin = 0; @@ -314,7 +319,7 @@ s32 func_80078E84(PlayState* play) { interfaceCtx->unk_226 -= 0x10; if (interfaceCtx->unk_226 <= 0) { interfaceCtx->unk_226 = 0; - play->damagePlayer(play, -(gSaveContext.health + 1)); + play->damagePlayer(play, GET_PLAYER(play), -(gSaveContext.health + 1)); return 1; } } @@ -337,6 +342,230 @@ static void* sHeartDDTextures[] = { gDefenseHeartThreeQuarterTex, }; +static s16 HealthMeter_GetLocalMultiplayerPlayerCount(void) { + s16 playerCount; + + if (CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0)) { + return 1; + } + + playerCount = CVarGetInteger(LOCAL_MP_PLAYER_COUNT_CVAR, 2); + if (playerCount < 1) { + playerCount = 1; + } else if (playerCount > 4) { + playerCount = 4; + } + + return playerCount; +} + +static s32 HealthMeter_IsSplitScreenEnabled(void) { + return !CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0) && CVarGetInteger(LOCAL_MP_SPLITSCREEN_CVAR, 1); +} + +static void HealthMeter_GetSplitViewportTopLeft(s16 playerCount, s16 playerIndex, s16* leftX, s16* topY) { + s16 halfWidth = SCREEN_WIDTH / 2; + s16 halfHeight = SCREEN_HEIGHT / 2; + + *leftX = 0; + *topY = 0; + + if (playerCount <= 1) { + return; + } + + if (playerCount == 2) { + if (CVarGetInteger(LOCAL_MP_SPLITSCREEN_VERTICAL_CVAR, 1)) { + *leftX = (playerIndex == 0) ? 0 : halfWidth; + } else { + *topY = (playerIndex == 0) ? 0 : halfHeight; + } + return; + } + + if (playerCount == 3) { + if (playerIndex == 0) { + return; + } + + *topY = halfHeight; + *leftX = (playerIndex == 1) ? 0 : halfWidth; + return; + } + + *topY = (playerIndex < 2) ? 0 : halfHeight; + *leftX = ((playerIndex % 2) == 0) ? 0 : halfWidth; +} + +static void HealthMeter_GetHealthFieldsForPort(u8 controllerPort, s16** health, s16** healthCapacity) { + *health = &gSaveContext.health; + *healthCapacity = &gSaveContext.healthCapacity; + + if (controllerPort == 4) { + *health = &gSaveContext.health4; + *healthCapacity = &gSaveContext.healthCapacity4; + } else if (controllerPort == 3) { + *health = &gSaveContext.health3; + *healthCapacity = &gSaveContext.healthCapacity3; + } else if (controllerPort == 2) { + *health = &gSaveContext.health2; + *healthCapacity = &gSaveContext.healthCapacity2; + } + + if ((controllerPort >= 2) && (**healthCapacity < STARTING_HEALTH)) { + **healthCapacity = gSaveContext.healthCapacity; + if (**healthCapacity < STARTING_HEALTH) { + **healthCapacity = STARTING_HEALTH; + } + + if (**health <= 0) { + **health = **healthCapacity; + } + } +} + +static void HealthMeter_GetHealthFieldsForPlayer(Player* player, s16** health, s16** healthCapacity) { + u8 controllerPort = 1; + + if ((player != NULL) && player->isSecondPlayer && (player->controllerPort >= 2) && (player->controllerPort <= 4)) { + controllerPort = player->controllerPort; + } + + HealthMeter_GetHealthFieldsForPort(controllerPort, health, healthCapacity); +} + +static void Health_DrawAdditionalMeter(PlayState* play, s16 health, s16 healthCapacity, f32 startX, f32 anchorY, + s32 growRight, s32 alignBottom) { + void* heartBgImg; + u32 curColorSet; + f32 offsetX; + f32 offsetY; + s32 heartIndex; + f32 heartCenterX; + f32 heartCenterY; + f32 heartScale; + InterfaceContext* interfaceCtx = &play->interfaceCtx; + GraphicsContext* gfxCtx = play->state.gfxCtx; + Vtx* beatingHeartVtx = interfaceCtx->beatingHeartVtx; + s32 curHeartFraction = health % FULL_HEART_HEALTH; + s16 totalHeartCount = healthCapacity / FULL_HEART_HEALTH; + s16 fullHeartCount = health / FULL_HEART_HEALTH; + s16 totalRows = (totalHeartCount + 9) / 10; + f32 heartsScale = 0.68f; + f32 beatingHeartPulsingSize = interfaceCtx->unk_22A * 0.1f; + s32 curCombineModeSet = 0; + u8* curBgImgLoaded = NULL; + + if (CVarGetInteger(CVAR_COSMETIC("HUD.HeartsCount.PosType"), 0) != ORIGINAL_LOCATION) { + heartsScale = CVarGetFloat(CVAR_COSMETIC("HUD.HeartsCount.Scale"), 0.7f); + } + + if (totalHeartCount <= 0) { + return; + } + + OPEN_DISPS(gfxCtx); + + if (!(health % FULL_HEART_HEALTH)) { + fullHeartCount--; + } + + if (!alignBottom) { + anchorY += 8.0f * heartsScale; + } + + curColorSet = -1; + for (heartIndex = 0; heartIndex < totalHeartCount; heartIndex++) { + s32 row = heartIndex / 10; + s32 col = heartIndex % 10; + + if (heartIndex < fullHeartCount) { + if (curColorSet != 0) { + curColorSet = 0; + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, interfaceCtx->heartsPrimR[0], interfaceCtx->heartsPrimG[0], + interfaceCtx->heartsPrimB[0], interfaceCtx->healthAlpha); + gDPSetEnvColor(OVERLAY_DISP++, interfaceCtx->heartsEnvR[0], interfaceCtx->heartsEnvG[0], + interfaceCtx->heartsEnvB[0], 255); + } + } else if (heartIndex == fullHeartCount) { + if (curColorSet != 1) { + curColorSet = 1; + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, interfaceCtx->beatingHeartPrim[0], + interfaceCtx->beatingHeartPrim[1], interfaceCtx->beatingHeartPrim[2], + interfaceCtx->healthAlpha); + gDPSetEnvColor(OVERLAY_DISP++, interfaceCtx->beatingHeartEnv[0], interfaceCtx->beatingHeartEnv[1], + interfaceCtx->beatingHeartEnv[2], 255); + } + } else { + if (curColorSet != 2) { + curColorSet = 2; + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, interfaceCtx->heartsPrimR[0], interfaceCtx->heartsPrimG[0], + interfaceCtx->heartsPrimB[0], interfaceCtx->healthAlpha); + gDPSetEnvColor(OVERLAY_DISP++, interfaceCtx->heartsEnvR[0], interfaceCtx->heartsEnvG[0], + interfaceCtx->heartsEnvB[0], 255); + } + } + + if (heartIndex < fullHeartCount) { + heartBgImg = gHeartFullTex; + } else if (heartIndex == fullHeartCount) { + heartBgImg = sHeartTextures[curHeartFraction]; + } else { + heartBgImg = gHeartEmptyTex; + } + + if (curBgImgLoaded != heartBgImg) { + curBgImgLoaded = heartBgImg; + gDPLoadTextureBlock(OVERLAY_DISP++, heartBgImg, G_IM_FMT_IA, G_IM_SIZ_8b, 16, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + } + + offsetX = growRight ? (10.0f * col) : (-10.0f * col); + offsetY = alignBottom ? (-10.0f * (totalRows - row - 1)) : (10.0f * row); + heartCenterY = anchorY + offsetY; + heartCenterX = startX + offsetX; + + if (heartIndex != fullHeartCount) { + if (curCombineModeSet != 1) { + curCombineModeSet = 1; + Gfx_SetupDL_39Overlay(gfxCtx); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, + 0, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + } + heartScale = heartsScale; + } else { + if (curCombineModeSet != 2) { + curCombineModeSet = 2; + Gfx_SetupDL_42Overlay(gfxCtx); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, + 0, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + } + + if (CVarGetInteger(CVAR_ENHANCEMENT("NoHUDHeartAnimation"), 0)) { + heartScale = heartsScale; + } else { + heartScale = heartsScale + (heartsScale / 3) - ((heartsScale / 3) * beatingHeartPulsingSize); + } + } + + { + Mtx* matrix = Graph_Alloc(gfxCtx, sizeof(Mtx)); + + Matrix_SetTranslateScaleMtx2(matrix, heartScale, heartScale, heartScale, -130.0f + heartCenterX, + 115.0f - heartCenterY, 0.0f); + gSPMatrix(OVERLAY_DISP++, matrix, G_MTX_MODELVIEW | G_MTX_LOAD); + gSPVertex(OVERLAY_DISP++, beatingHeartVtx, 4, 0); + gSP1Quadrangle(OVERLAY_DISP++, 0, 2, 3, 1, 0); + } + } + + CLOSE_DISPS(gfxCtx); +} + s16 getHealthMeterXOffset() { s16 X_Margins; if (CVarGetInteger(CVAR_COSMETIC("HUD.Hearts.UseMargins"), 0) != 0) @@ -640,6 +869,68 @@ void HealthMeter_Draw(PlayState* play) { FrameInterpolation_RecordCloseChild(); } + { + s16 playerCount = HealthMeter_GetLocalMultiplayerPlayerCount(); + s16* secondaryHealth; + s16* secondaryHealthCapacity; + f32 heartsScale = 0.68f; + f32 p1TopAnchorY; + f32 p1TopCenterY; + f32 p1StartX; + f32 mirroredStartX; + f32 bottomAnchorY; + f32 splitViewportScaleX; + s32 splitScreenEnabled = HealthMeter_IsSplitScreenEnabled() && (playerCount > 1); + + if (CVarGetInteger(CVAR_COSMETIC("HUD.HeartsCount.PosType"), 0) != ORIGINAL_LOCATION) { + heartsScale = CVarGetFloat(CVAR_COSMETIC("HUD.HeartsCount.Scale"), 0.7f); + } + + // Keep secondary heart rows mirrored against player 1's heart baseline and scale. + p1TopAnchorY = getHealthMeterYOffset() + 21.0f - (8.0f * heartsScale); + p1TopCenterY = p1TopAnchorY + (8.0f * heartsScale); + p1StartX = getHealthMeterXOffset(); + // Health meters are translated by (-130 + startX), so mirror in this space uses SCREEN_WIDTH - 60 - x. + mirroredStartX = SCREEN_WIDTH - 60.0f - p1StartX; + bottomAnchorY = SCREEN_HEIGHT - p1TopCenterY; + splitViewportScaleX = + (OTRGetDimensionFromRightEdge(SCREEN_WIDTH) - OTRGetDimensionFromLeftEdge(0.0f)) / SCREEN_WIDTH; + + if (splitScreenEnabled) { + s16 playerIndex; + + for (playerIndex = 1; playerIndex < playerCount; playerIndex++) { + s16 viewportLeftX; + s16 viewportTopY; + f32 viewportLeftHudSpace; + + HealthMeter_GetSplitViewportTopLeft(playerCount, playerIndex, &viewportLeftX, &viewportTopY); + viewportLeftHudSpace = viewportLeftX * splitViewportScaleX; + HealthMeter_GetHealthFieldsForPort(playerIndex + 1, &secondaryHealth, &secondaryHealthCapacity); + Health_DrawAdditionalMeter(play, *secondaryHealth, *secondaryHealthCapacity, + p1StartX + viewportLeftHudSpace, p1TopAnchorY + viewportTopY, true, false); + } + } else { + if (playerCount >= 2) { + HealthMeter_GetHealthFieldsForPort(2, &secondaryHealth, &secondaryHealthCapacity); + Health_DrawAdditionalMeter(play, *secondaryHealth, *secondaryHealthCapacity, mirroredStartX, + p1TopAnchorY, false, false); + } + + if (playerCount >= 3) { + HealthMeter_GetHealthFieldsForPort(3, &secondaryHealth, &secondaryHealthCapacity); + Health_DrawAdditionalMeter(play, *secondaryHealth, *secondaryHealthCapacity, p1StartX, bottomAnchorY, + true, true); + } + + if (playerCount >= 4) { + HealthMeter_GetHealthFieldsForPort(4, &secondaryHealth, &secondaryHealthCapacity); + Health_DrawAdditionalMeter(play, *secondaryHealth, *secondaryHealthCapacity, mirroredStartX, + bottomAnchorY, false, true); + } + } + } + CLOSE_DISPS(gfxCtx); } @@ -652,7 +943,8 @@ void HealthMeter_HandleCriticalAlarm(PlayState* play) { interfaceCtx->unk_22A = 0; interfaceCtx->unk_22C = 0; if (CVarGetInteger(CVAR_AUDIO("LowHpAlarm"), 0) == 0 && !Player_InCsMode(play) && - (play->pauseCtx.state == 0) && (play->pauseCtx.debugState == 0) && HealthMeter_IsCritical() && + (play->pauseCtx.state == 0) && (play->pauseCtx.debugState == 0) && + HealthMeter_IsCritical(GET_PLAYER(play)) && !Play_InCsMode(play)) { Sfx_PlaySfxCentered(NA_SE_SY_HITPOINT_ALARM); } @@ -666,20 +958,24 @@ void HealthMeter_HandleCriticalAlarm(PlayState* play) { } } -u32 HealthMeter_IsCritical(void) { +u32 HealthMeter_IsCritical(Player* player) { + s16* health; + s16* healthCapacity; s32 var; - if (gSaveContext.healthCapacity <= 0x50) { + HealthMeter_GetHealthFieldsForPlayer(player, &health, &healthCapacity); + + if (*healthCapacity <= 0x50) { var = 0x10; - } else if (gSaveContext.healthCapacity <= 0xA0) { + } else if (*healthCapacity <= 0xA0) { var = 0x18; - } else if (gSaveContext.healthCapacity <= 0xF0) { + } else if (*healthCapacity <= 0xF0) { var = 0x20; } else { var = 0x2C; } - if (GameInteractor_Should(VB_HEALTH_METER_BE_CRITICAL, var >= gSaveContext.health && gSaveContext.health > 0)) { + if (GameInteractor_Should(VB_HEALTH_METER_BE_CRITICAL, var >= *health && *health > 0)) { return true; } else { return false; diff --git a/soh/src/code/z_map_exp.c b/soh/src/code/z_map_exp.c index 44b5b43b5d6..6d8222e5a28 100644 --- a/soh/src/code/z_map_exp.c +++ b/soh/src/code/z_map_exp.c @@ -7,6 +7,7 @@ #include #include "soh/OTRGlobals.h" #include "soh/Enhancements/cosmetics/cosmeticsTypes.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" MapData* gMapData; @@ -761,6 +762,10 @@ void Minimap_DrawCompassIcons(PlayState* play) { } CLOSE_DISPS(play->state.gfxCtx); + + if (play->interfaceCtx.minimapAlpha >= 0xAA) { + GameInteractor_ExecuteOnMinimapDrawCompassIcons(); + } } void Minimap_Draw(PlayState* play) { diff --git a/soh/src/code/z_parameter.c b/soh/src/code/z_parameter.c index 4b782d9540c..4d1b6c9d8d1 100644 --- a/soh/src/code/z_parameter.c +++ b/soh/src/code/z_parameter.c @@ -37,6 +37,16 @@ extern MessageTableEntry* sJpnMessageEntryTablePtr; // So, when indexing into it with a item button index, we need to adjust #define BUTTON_STATUS_INDEX(button) ((button) >= 4) ? ((button) + 1) : (button) +#define LOCAL_MP_PLAYER_COUNT_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount") +#define LOCAL_MP_DISABLED_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Disable") +#define LOCAL_MP_SPLITSCREEN_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreen") +#define LOCAL_MP_SPLITSCREEN_VERTICAL_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreenVertical") +#define LOCAL_MP_ORIGINAL_ITEMS_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.OriginalItemSystem") +#define LOCAL_MP_RADIAL_QUICK_ITEM_MENU_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.RadialQuickItemMenu") +#define LOCAL_MP_INDEPENDENT_SLOT_USAGE_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.IndependentSlotUsage") +#define LOCAL_MP_SECOND_ITEM_SLOT_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.SecondItemSlot") +#define LOCAL_MP_SECOND_ITEM_SLOT_SIDE_BY_SIDE_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.SecondItemSlotSideBySide") + s16 Top_HUD_Margin = 0; s16 Left_HUD_Margin = 0; s16 Right_HUD_Margin = 0; @@ -158,6 +168,23 @@ static u16 sHBAScoreDigits[] = { 0, 0, 0, 0 }; static u16 sCUpInvisible = 0; static u16 sCUpTimer = 0; +static u8 sP4QuickItem = ITEM_NONE; +static s16 sP4QuickItemSlot = SLOT_NONE; +static u8 sQuickItemSlot2[4] = { ITEM_NONE, ITEM_NONE, ITEM_NONE, ITEM_NONE }; +static s16 sQuickItemSlot2InventorySlot[4] = { SLOT_NONE, SLOT_NONE, SLOT_NONE, SLOT_NONE }; +static u8 sQuickItemSelectedSlot[4] = { 0, 0, 0, 0 }; +static u8 sQuickItemUseSlot[4] = { 0, 0, 0, 0 }; + +typedef struct { + u8 active; + u8 slotIndex; + u8 itemCount; + u8 selectedIndex; + u8 items[24]; + s16 inventorySlots[24]; +} LocalMPRadialQuickMenuState; + +static LocalMPRadialQuickMenuState sRadialQuickMenuStates[4]; s16 gSpoilingItems[] = { ITEM_ODD_MUSHROOM, ITEM_FROG, ITEM_EYEDROPS }; s16 gSpoilingItemReverts[] = { ITEM_COJIRO, ITEM_PRESCRIPTION, ITEM_PRESCRIPTION }; @@ -165,6 +192,46 @@ s16 gSpoilingItemReverts[] = { ITEM_COJIRO, ITEM_PRESCRIPTION, ITEM_PRESCRIPTION static Color_RGB8 sMagicBorder = { 255, 255, 255 }; static Color_RGB8 sMagicBorder_ori = { 255, 255, 255 }; +static s16 sP2MagicCapacity = 0; +static s16 sP2MagicTarget = 0; +static s16 sP2MagicFillTarget = 0; +static s16 sP2MagicState = MAGIC_STATE_IDLE; +static s16 sP2PrevMagicState = MAGIC_STATE_IDLE; +static s16 sP2MagicBorderRatio = 2; +static s16 sP2MagicBorderStep = 1; +static s16 sP2LensMagicConsumptionTimer = 0; +static Color_RGB8 sMagicBorder2 = { 255, 255, 255 }; + +static s8 sP3MagicLevel = 0; +static s8 sP3Magic = MAGIC_NORMAL_METER; +static u8 sP3IsMagicAcquired = false; +static u8 sP3IsDoubleMagicAcquired = false; +static s16 sP3MagicCapacity = 0; +static s16 sP3MagicTarget = 0; +static s16 sP3MagicFillTarget = 0; +static s16 sP3MagicState = MAGIC_STATE_IDLE; +static s16 sP3PrevMagicState = MAGIC_STATE_IDLE; +static s16 sP3MagicBorderRatio = 2; +static s16 sP3MagicBorderStep = 1; +static s16 sP3LensMagicConsumptionTimer = 0; +static Color_RGB8 sMagicBorder3 = { 255, 255, 255 }; + +static s8 sP4MagicLevel = 0; +static s8 sP4Magic = MAGIC_NORMAL_METER; +static u8 sP4IsMagicAcquired = false; +static u8 sP4IsDoubleMagicAcquired = false; +static s16 sP4MagicCapacity = 0; +static s16 sP4MagicTarget = 0; +static s16 sP4MagicFillTarget = 0; +static s16 sP4MagicState = MAGIC_STATE_IDLE; +static s16 sP4PrevMagicState = MAGIC_STATE_IDLE; +static s16 sP4MagicBorderRatio = 2; +static s16 sP4MagicBorderStep = 1; +static s16 sP4LensMagicConsumptionTimer = 0; +static Color_RGB8 sMagicBorder4 = { 255, 255, 255 }; + +static Player* sItemGivePlayer = NULL; + static s16 sExtraItemBases[] = { ITEM_STICK, ITEM_STICK, ITEM_NUT, ITEM_NUT, ITEM_BOMB, ITEM_BOMB, ITEM_BOMB, ITEM_BOMB, ITEM_BOW, ITEM_BOW, ITEM_BOW, ITEM_SEEDS, ITEM_BOMBCHU, ITEM_BOMBCHU, ITEM_STICK, ITEM_STICK, ITEM_NUT, ITEM_NUT, @@ -195,6 +262,11 @@ static const char* actionsTbl[] = { gNum8DoActionENGTex, }; +void Magic_ResetExtraData(void); +s16 Magic_GetStateForPlayer(Player* player); +void Magic_FillForPlayer(PlayState* play, Player* player); +s32 Magic_RequestChangeForPlayer(PlayState* play, Player* player, s16 amount, s16 type); + // original name: "alpha_change" void Interface_ChangeAlpha(u16 alphaType) { if (alphaType != gSaveContext.unk_13EA) { @@ -1727,263 +1799,969 @@ void func_800849EC(PlayState* play) { Interface_LoadItemIcon1(play, 0); } -void Interface_LoadItemIcon1(PlayState* play, u16 button) { - InterfaceContext* interfaceCtx = &play->interfaceCtx; +static s16 Interface_GetLocalMultiplayerPlayerCount(void) { + s16 playerCount; - osCreateMesgQueue(&interfaceCtx->loadQueue, &interfaceCtx->loadMsg, OS_MESG_BLOCK); - DmaMgr_SendRequest2(&interfaceCtx->dmaRequest_160, interfaceCtx->iconItemSegment + button * 0x1000, - (uintptr_t)_icon_item_staticSegmentRomStart + - (gSaveContext.equips.buttonItems[button] * 0x1000), - 0x1000, 0, &interfaceCtx->loadQueue, OS_MESG_PTR(NULL), __FILE__, __LINE__); - osRecvMesg(&interfaceCtx->loadQueue, NULL, OS_MESG_BLOCK); -} + if (CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0)) { + return 1; + } -void Interface_LoadItemIcon2(PlayState* play, u16 button) { - InterfaceContext* interfaceCtx = &play->interfaceCtx; + playerCount = CVarGetInteger(LOCAL_MP_PLAYER_COUNT_CVAR, 2); + if (playerCount < 1) { + playerCount = 1; + } else if (playerCount > 4) { + playerCount = 4; + } - osCreateMesgQueue(&interfaceCtx->loadQueue, &interfaceCtx->loadMsg, OS_MESG_BLOCK); - DmaMgr_SendRequest2(&interfaceCtx->dmaRequest_180, interfaceCtx->iconItemSegment + button * 0x1000, - (uintptr_t)_icon_item_staticSegmentRomStart + - (gSaveContext.equips.buttonItems[button] * 0x1000), - 0x1000, 0, &interfaceCtx->loadQueue, OS_MESG_PTR(NULL), __FILE__, __LINE__); - osRecvMesg(&interfaceCtx->loadQueue, NULL, OS_MESG_BLOCK); + return playerCount; } -void func_80084BF4(PlayState* play, u16 flag) { - if (flag) { - if ((gSaveContext.equips.buttonItems[0] == ITEM_SLINGSHOT) || - (gSaveContext.equips.buttonItems[0] == ITEM_BOW) || (gSaveContext.equips.buttonItems[0] == ITEM_BOMBCHU) || - (gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE) || - (gSaveContext.buttonStatus[0] == BTN_DISABLED)) { - if ((gSaveContext.equips.buttonItems[0] == ITEM_SLINGSHOT) || - (gSaveContext.equips.buttonItems[0] == ITEM_BOW) || - (gSaveContext.equips.buttonItems[0] == ITEM_BOMBCHU) || - (gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE)) { - gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0]; - Interface_RandoRestoreSwordless(); - Interface_LoadItemIcon1(play, 0); - } - } else if (gSaveContext.equips.buttonItems[0] == ITEM_NONE) { - if ((gSaveContext.equips.buttonItems[0] != ITEM_NONE) || (gSaveContext.infTable[29] == 0)) { - gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0]; - Interface_RandoRestoreSwordless(); - Interface_LoadItemIcon1(play, 0); - } - } - - gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = - gSaveContext.buttonStatus[3] = BTN_ENABLED; - gSaveContext.buttonStatus[5] = gSaveContext.buttonStatus[6] = gSaveContext.buttonStatus[7] = - gSaveContext.buttonStatus[8] = BTN_ENABLED; - Interface_ChangeAlpha(7); - } else { - gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = - gSaveContext.buttonStatus[3] = BTN_ENABLED; - gSaveContext.buttonStatus[5] = gSaveContext.buttonStatus[6] = gSaveContext.buttonStatus[7] = - gSaveContext.buttonStatus[8] = BTN_ENABLED; - func_80083108(play); - } +static s32 Interface_IsSplitScreenEnabled(void) { + return !CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0) && CVarGetInteger(LOCAL_MP_SPLITSCREEN_CVAR, 1); } -// Gameplay stat tracking: Update time the item was acquired -// (special cases for some duplicate items) -void GameplayStats_SetTimestamp(PlayState* play, u8 item) { +static void Interface_GetSplitViewportTopLeft(s16 playerCount, s16 playerIndex, s16* leftX, s16* topY) { + s16 halfWidth = SCREEN_WIDTH / 2; + s16 halfHeight = SCREEN_HEIGHT / 2; - // If we already have a timestamp for this item, do nothing - if (gSaveContext.ship.stats.itemTimestamp[item] != 0) { + *leftX = 0; + *topY = 0; + + if (playerCount <= 1) { return; } - // Use ITEM_KEY_BOSS only for Ganon's boss key - not any other boss keys - if (play != NULL) { - if (item == ITEM_KEY_BOSS && play->sceneNum != SCENE_INSIDE_GANONS_CASTLE && - play->sceneNum != SCENE_GANONS_TOWER) { + + if (playerCount == 2) { + if (CVarGetInteger(LOCAL_MP_SPLITSCREEN_VERTICAL_CVAR, 1)) { + *leftX = (playerIndex == 0) ? 0 : halfWidth; + } else { + *topY = (playerIndex == 0) ? 0 : halfHeight; + } + return; + } + + if (playerCount == 3) { + if (playerIndex == 0) { return; } + + *topY = halfHeight; + *leftX = (playerIndex == 1) ? 0 : halfWidth; + return; } - u32 time = GAMEPLAYSTAT_TOTAL_TIME; + *topY = (playerIndex < 2) ? 0 : halfHeight; + *leftX = ((playerIndex % 2) == 0) ? 0 : halfWidth; +} - // Have items in Link's pocket shown as being obtained at 0.1 seconds - if (time == 0) { - time = 1; +static void Interface_GetSplitViewportBounds(s16 playerCount, s16 playerIndex, Viewport* viewport) { + s16 halfWidth = SCREEN_WIDTH / 2; + s16 halfHeight = SCREEN_HEIGHT / 2; + + viewport->topY = 0; + viewport->bottomY = SCREEN_HEIGHT; + viewport->leftX = 0; + viewport->rightX = SCREEN_WIDTH; + + if (playerCount <= 1) { + return; } - // Count any bottled item as a bottle - if (item >= ITEM_BOTTLE && item <= ITEM_POE) { - if (gSaveContext.ship.stats.itemTimestamp[ITEM_BOTTLE] == 0) { - gSaveContext.ship.stats.itemTimestamp[ITEM_BOTTLE] = time; + if (playerCount == 2) { + if (CVarGetInteger(LOCAL_MP_SPLITSCREEN_VERTICAL_CVAR, 1)) { + viewport->leftX = (playerIndex == 0) ? 0 : halfWidth; + viewport->rightX = (playerIndex == 0) ? halfWidth : SCREEN_WIDTH; + } else { + viewport->topY = (playerIndex == 0) ? 0 : halfHeight; + viewport->bottomY = (playerIndex == 0) ? halfHeight : SCREEN_HEIGHT; } return; } - // Count any bombchu pack as bombchus - if (item == ITEM_BOMBCHU || (item >= ITEM_BOMBCHUS_5 && item <= ITEM_BOMBCHUS_20)) { - if (gSaveContext.ship.stats.itemTimestamp[ITEM_BOMBCHU] == 0) { - gSaveContext.ship.stats.itemTimestamp[ITEM_BOMBCHU] = time; + + if (playerCount == 3) { + if (playerIndex == 0) { + viewport->topY = 0; + viewport->bottomY = halfHeight; + viewport->leftX = 0; + viewport->rightX = SCREEN_WIDTH; + } else if (playerIndex == 1) { + viewport->topY = halfHeight; + viewport->bottomY = SCREEN_HEIGHT; + viewport->leftX = 0; + viewport->rightX = halfWidth; + } else { + viewport->topY = halfHeight; + viewport->bottomY = SCREEN_HEIGHT; + viewport->leftX = halfWidth; + viewport->rightX = SCREEN_WIDTH; } return; } - gSaveContext.ship.stats.itemTimestamp[item] = time; - GameInteractor_ExecuteOnTimestamp(item); + viewport->topY = (playerIndex < 2) ? 0 : halfHeight; + viewport->bottomY = (playerIndex < 2) ? halfHeight : SCREEN_HEIGHT; + viewport->leftX = ((playerIndex % 2) == 0) ? 0 : halfWidth; + viewport->rightX = ((playerIndex % 2) == 0) ? halfWidth : SCREEN_WIDTH; } -u8 Return_Item_Entry(GetItemEntry itemEntry, u8 returnItem) { - GameInteractor_ExecuteOnItemReceiveHooks(itemEntry); - return returnItem; -} +static s16 Interface_NormalizeQuickItemSlotIndex(s16 slotIndex); -// Processes Item_Give returns -u8 Return_Item(u8 itemID, ModIndex modId, ItemID returnItem) { - // ITEM_SOLD_OUT doesn't have an ItemTable entry, so pass custom entry instead - if (itemID == ITEM_SOLD_OUT) { - GetItemEntry gie = { ITEM_SOLD_OUT, 0, 0, 0, 0, 0, 0, 0, 0, false, ITEM_FROM_NPC, ITEM_CATEGORY_LESSER, NULL }; - return Return_Item_Entry(gie, returnItem); - } +static s32 Interface_IsOriginalMultiplayerItemModeEnabled(void) { + return CVarGetInteger(LOCAL_MP_ORIGINAL_ITEMS_CVAR, 1) && + (Interface_GetLocalMultiplayerPlayerCount() > 1); +} - GetItemID getItemID = RetrieveGetItemIDFromItemID(itemID); - if (getItemID != GI_MAX) { - // Vanilla ItemID with an associated GetItemID - return Return_Item_Entry(ItemTable_RetrieveEntry(modId, getItemID), returnItem); - } +static s32 Interface_IsSecondQuickItemSlotEnabled(void) { + return Interface_IsOriginalMultiplayerItemModeEnabled() && + CVarGetInteger(LOCAL_MP_SECOND_ITEM_SLOT_CVAR, 1); +} - RandomizerGet randomizerGet = RetrieveRandomizerGetFromItemID(itemID); - if (randomizerGet != RG_MAX) { - // Vanilla ItemID with an associated RandomizerGet (These are items in extendedVanillaGetItemTable) - return Return_Item_Entry(ItemTable_RetrieveEntry(MOD_RANDOMIZER, randomizerGet), returnItem); - } +static s32 Interface_IsSecondQuickItemSlotSideBySideEnabled(void) { + return Interface_IsSecondQuickItemSlotEnabled() && + CVarGetInteger(LOCAL_MP_SECOND_ITEM_SLOT_SIDE_BY_SIDE_CVAR, 0); +} - // All randomizer items should go through Randomizer_Item_Give, so this should never be reached - // but leaving this here just in case, as it was in the original behavior - assert(false); - return Return_Item_Entry(ItemTable_RetrieveEntry(MOD_RANDOMIZER, itemID), returnItem); +static s32 Interface_IsRadialQuickItemMenuEnabled(void) { + return Interface_IsOriginalMultiplayerItemModeEnabled() && + CVarGetInteger(LOCAL_MP_RADIAL_QUICK_ITEM_MENU_CVAR, 0); } -/** - * @brief Adds the given item to Link's inventory. - * - * NOTE: This function has been edited to be safe to use with a NULL play. - * If you need to add to this function, be sure you check if the play is not - * NULL before doing any operations requiring it. - * - * @param play - * @param item - * @return u8 - */ -u8 Item_Give(PlayState* play, u8 item) { - // prevents getting sticks without the bag in case something got missed - if (IS_RANDO && (item == ITEM_STICK || item == ITEM_STICKS_5 || item == ITEM_STICKS_10) && - Randomizer_GetSettingValue(RSK_SHUFFLE_DEKU_STICK_BAG) && CUR_UPG_VALUE(UPG_STICKS) == 0) { - return item; - } +static s32 Interface_CanCycleToItem(u8 item) { + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + switch (item) { + case ITEM_BOW: + case ITEM_ARROW_FIRE: + case ITEM_ARROW_ICE: + case ITEM_ARROW_LIGHT: + case ITEM_HOOKSHOT: + case ITEM_LONGSHOT: + case ITEM_HAMMER: + case ITEM_BOW_ARROW_FIRE: + case ITEM_BOW_ARROW_ICE: + case ITEM_BOW_ARROW_LIGHT: + return false; - // prevents getting nuts without the bag in case something got missed - if (IS_RANDO && (item == ITEM_NUT || item == ITEM_NUTS_5 || item == ITEM_NUTS_10) && - Randomizer_GetSettingValue(RSK_SHUFFLE_DEKU_NUT_BAG) && CUR_UPG_VALUE(UPG_NUTS) == 0) { - return item; + default: + return true; + } } - lusprintf(__FILE__, __LINE__, 2, "Item Give - item: %#x", item); - static s16 sAmmoRefillCounts[] = { 5, 10, 20, 30, 5, 10, 30, 0, 5, 20, 1, 5, 20, 50, 200, 10 }; - s16 i; - s16 slot; - s16 temp; + switch (item) { + case ITEM_STICK: + case ITEM_SLINGSHOT: + case ITEM_BOOMERANG: + case ITEM_BEAN: + return false; - GetItemID returnItem = ITEM_NONE; + default: + return true; + } +} - // Gameplay stats: Update the time the item was obtained - GameplayStats_SetTimestamp(play, item); +static s32 Interface_IsMultiplayerQuickMenuItemBlocked(u8 item) { + switch (item) { + case ITEM_OCARINA_FAIRY: + case ITEM_OCARINA_TIME: + case ITEM_BEAN: + case ITEM_LENS: + case ITEM_DINS_FIRE: + case ITEM_NAYRUS_LOVE: + case ITEM_FARORES_WIND: + case ITEM_WEIRD_EGG: + case ITEM_CHICKEN: + case ITEM_LETTER_ZELDA: + case ITEM_POCKET_EGG: + case ITEM_POCKET_CUCCO: + case ITEM_COJIRO: + case ITEM_ODD_MUSHROOM: + case ITEM_ODD_POTION: + case ITEM_SAW: + case ITEM_SWORD_BROKEN: + case ITEM_PRESCRIPTION: + case ITEM_FROG: + case ITEM_EYEDROPS: + case ITEM_CLAIM_CHECK: + return true; - slot = SLOT(item); - if (item >= ITEM_STICKS_5) { - slot = SLOT(sExtraItemBases[item - ITEM_STICKS_5]); + default: + return false; } +} - osSyncPrintf(VT_FGCOL(YELLOW)); - osSyncPrintf("item_get_setting=%d pt=%d z=%x\n", item, slot, gSaveContext.inventory.items[slot]); - osSyncPrintf(VT_RST); +static Player* Interface_FindPlayerByPort(PlayState* play, u8 controllerPort) { + Player* player; - if ((item >= ITEM_MEDALLION_FOREST) && (item <= ITEM_MEDALLION_LIGHT)) { - gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_MEDALLION_FOREST + QUEST_MEDALLION_FOREST]; + if ((controllerPort < 1) || (controllerPort > 4)) { + return NULL; + } - osSyncPrintf(VT_FGCOL(YELLOW)); - osSyncPrintf("封印 = %x\n", gSaveContext.inventory.questItems); // "Seals = %x" - osSyncPrintf(VT_RST); + player = GET_PLAYER(play); - if (item == ITEM_MEDALLION_WATER) { - func_8006D0AC(play); + while ((player != NULL) && (player->actor.id == ACTOR_PLAYER)) { + if (!player->isSecondPlayer) { + if (controllerPort == 1) { + return player; + } + } else if (player->controllerPort == controllerPort) { + return player; } - return Return_Item(item, MOD_NONE, ITEM_NONE); - } else if ((item >= ITEM_SONG_MINUET) && (item <= ITEM_SONG_STORMS)) { - gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_SONG_MINUET + QUEST_SONG_MINUET]; + player = (Player*)player->actor.next; + } - osSyncPrintf(VT_FGCOL(YELLOW)); - osSyncPrintf("楽譜 = %x\n", gSaveContext.inventory.questItems); // "Musical scores = %x" - // "Musical scores = %x (%x) (%x)" - osSyncPrintf("楽譜 = %x (%x) (%x)\n", gSaveContext.inventory.questItems, - gBitFlags[item - ITEM_SONG_MINUET + QUEST_SONG_MINUET], gBitFlags[item - ITEM_SONG_MINUET]); - osSyncPrintf(VT_RST); + return NULL; +} - return Return_Item(item, MOD_NONE, ITEM_NONE); - } else if ((item >= ITEM_KOKIRI_EMERALD) && (item <= ITEM_ZORA_SAPPHIRE)) { - gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_KOKIRI_EMERALD + QUEST_KOKIRI_EMERALD]; +static s32 Interface_CanUseRadialQuickMenuForPort(PlayState* play, u8 controllerPort) { + Player* player = Interface_FindPlayerByPort(play, controllerPort); - osSyncPrintf(VT_FGCOL(YELLOW)); - osSyncPrintf("精霊石 = %x\n", gSaveContext.inventory.questItems); // "Spiritual Stones = %x" - osSyncPrintf(VT_RST); + if (controllerPort == 1) { + return player != NULL; + } - return Return_Item(item, MOD_NONE, ITEM_NONE); - } else if ((item == ITEM_STONE_OF_AGONY) || (item == ITEM_GERUDO_CARD)) { - gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_STONE_OF_AGONY + QUEST_STONE_OF_AGONY]; + if ((player == NULL) || !player->isSecondPlayer) { + return false; + } - osSyncPrintf(VT_FGCOL(YELLOW)); - osSyncPrintf("アイテム = %x\n", gSaveContext.inventory.questItems); // "Items = %x" - osSyncPrintf(VT_RST); + return player->inputEnabled; +} - return Return_Item(item, MOD_NONE, ITEM_NONE); - } else if (item == ITEM_SKULL_TOKEN) { - gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_SKULL_TOKEN + QUEST_SKULL_TOKEN]; - gSaveContext.inventory.gsTokens++; +static void Interface_ClearRadialQuickMenuState(LocalMPRadialQuickMenuState* state) { + if (state != NULL) { + memset(state, 0, sizeof(*state)); + } +} - osSyncPrintf(VT_FGCOL(YELLOW)); - // "N Coins = %x(%d)" - osSyncPrintf("Nコイン = %x(%d)\n", gSaveContext.inventory.questItems, gSaveContext.inventory.gsTokens); - osSyncPrintf(VT_RST); +static void Interface_ClearAllRadialQuickMenuStates(void) { + s32 i; - return Return_Item(item, MOD_NONE, ITEM_NONE); - } else if ((item >= ITEM_SWORD_KOKIRI) && (item <= ITEM_SWORD_BGS)) { - gSaveContext.inventory.equipment |= - OWNED_EQUIP_FLAG(EQUIP_TYPE_SWORD, item - ITEM_SWORD_KOKIRI + EQUIP_INV_SWORD_KOKIRI); + for (i = 0; i < ARRAY_COUNT(sRadialQuickMenuStates); i++) { + Interface_ClearRadialQuickMenuState(&sRadialQuickMenuStates[i]); + } +} - // Both Giant's Knife and Biggoron Sword have the same Item ID, so this part handles both of them - if (item == ITEM_SWORD_BGS) { - gSaveContext.swordHealth = 8; +static void Interface_BuildRadialQuickMenuItemsForPort(PlayState* play, u8 controllerPort, u8 slotIndex, + LocalMPRadialQuickMenuState* state) { + s32 i; + u8 currentItem; + s16 currentInventorySlot; + u8 seenItems[ITEM_NONE_FE]; - if (ALL_EQUIP_VALUE(EQUIP_TYPE_SWORD) == - ((1 << EQUIP_INV_SWORD_KOKIRI) | (1 << EQUIP_INV_SWORD_MASTER) | (1 << EQUIP_INV_SWORD_BIGGORON) | - (1 << EQUIP_INV_SWORD_BROKENGIANTKNIFE))) { + (void)play; - gSaveContext.inventory.equipment ^= - OWNED_EQUIP_FLAG_ALT(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_BROKENGIANTKNIFE); + if (state == NULL) { + return; + } - if (gSaveContext.equips.buttonItems[0] == ITEM_SWORD_KNIFE) { - gSaveContext.equips.buttonItems[0] = ITEM_SWORD_BGS; - if (play != NULL) { - Interface_LoadItemIcon1(play, 0); - } - } - } + memset(seenItems, 0, sizeof(seenItems)); - } else if (item == ITEM_SWORD_MASTER) { - gSaveContext.equips.buttonItems[0] = ITEM_SWORD_MASTER; - gSaveContext.equips.equipment &= (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4)); - gSaveContext.equips.equipment |= EQUIP_VALUE_SWORD_MASTER << (EQUIP_TYPE_SWORD * 4); - if (play != NULL) { - Interface_LoadItemIcon1(play, 0); - } + state->itemCount = 0; + state->selectedIndex = 0; + state->slotIndex = Interface_NormalizeQuickItemSlotIndex(slotIndex); + + for (i = 0; i < 24; i++) { + u8 item = gSaveContext.inventory.items[i]; + + if ((item == ITEM_NONE) || (item == ITEM_SOLD_OUT) || (item >= ITEM_NONE_FE)) { + continue; } - return Return_Item(item, MOD_NONE, ITEM_NONE); + if (!Interface_CanCycleToItem(item)) { + continue; + } + + if ((controllerPort > 1) && Interface_IsMultiplayerQuickMenuItemBlocked(item)) { + continue; + } + + if (seenItems[item]) { + continue; + } + + if (state->itemCount >= ARRAY_COUNT(state->items)) { + break; + } + + seenItems[item] = true; + state->items[state->itemCount] = item; + state->inventorySlots[state->itemCount] = i; + state->itemCount++; + } + + if (state->itemCount == 0) { + return; + } + + currentItem = Interface_GetQuickItemForPortAndSlot(controllerPort, state->slotIndex); + currentInventorySlot = Interface_GetQuickItemInventorySlotForPortAndSlot(controllerPort, state->slotIndex); + + for (i = 0; i < state->itemCount; i++) { + if ((state->inventorySlots[i] == currentInventorySlot) && (state->items[i] == currentItem)) { + state->selectedIndex = i; + return; + } + } + + for (i = 0; i < state->itemCount; i++) { + if (state->items[i] == currentItem) { + state->selectedIndex = i; + return; + } + } +} + +static void Interface_UpdateRadialQuickMenuSelectionForPort(PlayState* play, u8 controllerPort, + LocalMPRadialQuickMenuState* state) { + Input* input; + s16 stickX; + s16 stickY; + s32 stickMagnitudeSq; + f32 angle; + u32 scaledIndex; + + if ((state == NULL) || (state->itemCount == 0)) { + return; + } + + if ((controllerPort < 1) || (controllerPort > ARRAY_COUNT(play->state.input))) { + return; + } + + input = &play->state.input[controllerPort - 1]; + stickX = input->cur.stick_x; + stickY = input->cur.stick_y; + stickMagnitudeSq = SQ(stickX) + SQ(stickY); + + if (stickMagnitudeSq < SQ(30)) { + return; + } + + // Use atan2(x, y) so 0 radians points straight up, matching the wheel layout. + angle = Math_FAtan2F(stickX, stickY); + + if (angle < 0.0f) { + angle += (2.0f * M_PI); + } + + scaledIndex = (u32)(((angle / (2.0f * M_PI)) * state->itemCount) + 0.5f); + + if (scaledIndex >= state->itemCount) { + scaledIndex = 0; + } + + if (state->selectedIndex != scaledIndex) { + state->selectedIndex = scaledIndex; + Audio_PlaySoundGeneral(NA_SE_SY_CURSOR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + } +} + +static void Interface_UpdateRadialQuickMenu(PlayState* play) { + s32 playerCount; + s32 port; + + if (!Interface_IsRadialQuickItemMenuEnabled()) { + Interface_ClearAllRadialQuickMenuStates(); + return; + } + + playerCount = Interface_GetLocalMultiplayerPlayerCount(); + if (playerCount <= 1) { + Interface_ClearAllRadialQuickMenuStates(); + return; + } + + if (Player_InCsMode(play) || (play->pauseCtx.state != 0) || (play->transitionTrigger != TRANS_TRIGGER_OFF)) { + Interface_ClearAllRadialQuickMenuStates(); + return; + } + + for (port = 1; port <= ARRAY_COUNT(sRadialQuickMenuStates); port++) { + LocalMPRadialQuickMenuState* state = &sRadialQuickMenuStates[port - 1]; + Input* input; + s32 holdActive; + s32 holdReleased; + u8 slotIndex; + + if ((port > playerCount) || !Interface_CanUseRadialQuickMenuForPort(play, port)) { + Interface_ClearRadialQuickMenuState(state); + continue; + } + + input = &play->state.input[port - 1]; + holdActive = CHECK_BTN_ANY(input->cur.button, BTN_DLEFT | BTN_DRIGHT); + holdReleased = CHECK_BTN_ANY(input->rel.button, BTN_DLEFT | BTN_DRIGHT); + slotIndex = Interface_GetQuickItemSelectedSlotForPort(port); + + if (holdActive) { + if (!state->active || (state->slotIndex != Interface_NormalizeQuickItemSlotIndex(slotIndex))) { + Interface_BuildRadialQuickMenuItemsForPort(play, port, slotIndex, state); + state->active = true; + } + + Interface_UpdateRadialQuickMenuSelectionForPort(play, port, state); + } else if (state->active) { + if ((state->itemCount > 0) && (state->selectedIndex < state->itemCount) && + (holdReleased || !holdActive)) { + u8 selectedItem = state->items[state->selectedIndex]; + s16 selectedInventorySlot = state->inventorySlots[state->selectedIndex]; + u8 currentItem = Interface_GetQuickItemForPortAndSlot(port, state->slotIndex); + s16 currentInventorySlot = Interface_GetQuickItemInventorySlotForPortAndSlot(port, state->slotIndex); + + if ((selectedItem != currentItem) || (selectedInventorySlot != currentInventorySlot)) { + Interface_SetQuickItemForPortAndSlot(play, port, state->slotIndex, selectedItem, + selectedInventorySlot); + Audio_PlaySoundGeneral(NA_SE_SY_DECIDE, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + } + } + + Interface_ClearRadialQuickMenuState(state); + } + } +} + +void Interface_LoadItemIconForItem(PlayState* play, u16 button, u8 item) { + InterfaceContext* interfaceCtx = &play->interfaceCtx; + + if (item >= ITEM_NONE_FE) { + return; + } + + osCreateMesgQueue(&interfaceCtx->loadQueue, &interfaceCtx->loadMsg, OS_MESG_BLOCK); + DmaMgr_SendRequest2(&interfaceCtx->dmaRequest_160, interfaceCtx->iconItemSegment + button * 0x1000, + (uintptr_t)_icon_item_staticSegmentRomStart + (item * 0x1000), 0x1000, 0, + &interfaceCtx->loadQueue, OS_MESG_PTR(NULL), __FILE__, __LINE__); + osRecvMesg(&interfaceCtx->loadQueue, NULL, OS_MESG_BLOCK); +} + +static s16 Interface_GetQuickItemButtonIndexImpl(u8 controllerPort) { + switch (controllerPort) { + case 4: + return 4; + + case 3: + return 2; + + case 2: + return 3; + + case 1: + default: + return 1; + } +} + +s16 Interface_GetQuickItemButtonIndex(u8 controllerPort) { + return Interface_GetQuickItemButtonIndexImpl(controllerPort); +} + +static s16 Interface_NormalizeQuickItemSlotIndex(s16 slotIndex) { + if (!Interface_IsSecondQuickItemSlotEnabled()) { + return 0; + } + + return (slotIndex != 0) ? 1 : 0; +} + +static s16 Interface_GetQuickItemPortArrayIndex(u8 controllerPort) { + if ((controllerPort < 1) || (controllerPort > 4)) { + return -1; + } + + return controllerPort - 1; +} + +static s16 Interface_GetQuickItemControllerPortByButtonIndex(s16 buttonIndex) { + switch (buttonIndex) { + case 1: + return 1; + + case 3: + return 2; + + case 2: + return 3; + + case 4: + return 4; + + default: + return 0; + } +} + +s16 Interface_GetQuickItemInventorySlotForPortAndSlot(u8 controllerPort, s16 slotIndex) { + s16 portIndex = Interface_GetQuickItemPortArrayIndex(controllerPort); + + if (portIndex < 0) { + return SLOT_NONE; + } + + if (Interface_NormalizeQuickItemSlotIndex(slotIndex) == 1) { + return sQuickItemSlot2InventorySlot[portIndex]; + } + + switch (controllerPort) { + case 4: + if (Interface_IsOriginalMultiplayerItemModeEnabled()) { + return sP4QuickItemSlot; + } + return gSaveContext.equips.cButtonSlots[3]; + + case 3: + return gSaveContext.equips.cButtonSlots[1]; + + case 2: + return gSaveContext.equips.cButtonSlots[2]; + + case 1: + default: + return gSaveContext.equips.cButtonSlots[0]; + } +} + +s16 Interface_GetQuickItemInventorySlotForPort(u8 controllerPort) { + return Interface_GetQuickItemInventorySlotForPortAndSlot(controllerPort, 0); +} + +u8 Interface_GetQuickItemForPortAndSlot(u8 controllerPort, s16 slotIndex) { + s16 buttonIndex = Interface_GetQuickItemButtonIndexImpl(controllerPort); + s16 inventorySlot; + s16 portIndex = Interface_GetQuickItemPortArrayIndex(controllerPort); + + if (portIndex < 0) { + return ITEM_NONE; + } + + slotIndex = Interface_NormalizeQuickItemSlotIndex(slotIndex); + + if (slotIndex == 1) { + inventorySlot = sQuickItemSlot2InventorySlot[portIndex]; + + if ((inventorySlot >= 0) && (inventorySlot < 24)) { + return gSaveContext.inventory.items[inventorySlot]; + } + + return sQuickItemSlot2[portIndex]; + } + + if ((controllerPort == 4) && Interface_IsOriginalMultiplayerItemModeEnabled()) { + if ((sP4QuickItemSlot >= 0) && (sP4QuickItemSlot < 24)) { + return gSaveContext.inventory.items[sP4QuickItemSlot]; + } + + return sP4QuickItem; + } + + if ((buttonIndex >= 1) && (buttonIndex < ARRAY_COUNT(gSaveContext.equips.buttonItems))) { + return gSaveContext.equips.buttonItems[buttonIndex]; + } + + return ITEM_NONE; +} + +u8 Interface_GetQuickItemForPort(u8 controllerPort) { + return Interface_GetQuickItemForPortAndSlot(controllerPort, 0); +} + +void Interface_SetQuickItemSelectedSlotForPort(u8 controllerPort, s16 slotIndex) { + s16 portIndex = Interface_GetQuickItemPortArrayIndex(controllerPort); + + if (portIndex < 0) { + return; + } + + sQuickItemSelectedSlot[portIndex] = Interface_NormalizeQuickItemSlotIndex(slotIndex); +} + +s16 Interface_GetQuickItemSelectedSlotForPort(u8 controllerPort) { + s16 portIndex = Interface_GetQuickItemPortArrayIndex(controllerPort); + + if (portIndex < 0) { + return 0; + } + + sQuickItemSelectedSlot[portIndex] = Interface_NormalizeQuickItemSlotIndex(sQuickItemSelectedSlot[portIndex]); + return sQuickItemSelectedSlot[portIndex]; +} + +void Interface_SetQuickItemUseSlotForPort(u8 controllerPort, s16 slotIndex) { + s16 portIndex = Interface_GetQuickItemPortArrayIndex(controllerPort); + + if (portIndex < 0) { + return; + } + + sQuickItemUseSlot[portIndex] = Interface_NormalizeQuickItemSlotIndex(slotIndex); +} + +s16 Interface_GetQuickItemUseSlotForPort(u8 controllerPort) { + s16 portIndex = Interface_GetQuickItemPortArrayIndex(controllerPort); + + if (portIndex < 0) { + return 0; + } + + sQuickItemUseSlot[portIndex] = Interface_NormalizeQuickItemSlotIndex(sQuickItemUseSlot[portIndex]); + return sQuickItemUseSlot[portIndex]; +} + +void Interface_ResetExtraItemData(void) { + s32 i; + + sP4QuickItem = ITEM_NONE; + sP4QuickItemSlot = SLOT_NONE; + + for (i = 0; i < ARRAY_COUNT(sQuickItemSlot2); i++) { + sQuickItemSlot2[i] = ITEM_NONE; + sQuickItemSlot2InventorySlot[i] = SLOT_NONE; + sQuickItemSelectedSlot[i] = 0; + sQuickItemUseSlot[i] = 0; + } + + Interface_ClearAllRadialQuickMenuStates(); + + Magic_ResetExtraData(); +} + +static void Interface_SetQuickItemForPortAndSlotInternal(PlayState* play, u8 controllerPort, s16 slotIndex, u8 item, + s16 inventorySlot) { + s16 buttonIndex = Interface_GetQuickItemButtonIndexImpl(controllerPort); + s16 portIndex = Interface_GetQuickItemPortArrayIndex(controllerPort); + + if (portIndex < 0) { + return; + } + + slotIndex = Interface_NormalizeQuickItemSlotIndex(slotIndex); + + if (slotIndex == 1) { + sQuickItemSlot2[portIndex] = item; + if ((inventorySlot >= 0) && (inventorySlot < 24)) { + sQuickItemSlot2InventorySlot[portIndex] = inventorySlot; + } else if (item == ITEM_NONE) { + sQuickItemSlot2InventorySlot[portIndex] = SLOT_NONE; + } + + return; + } + + if ((controllerPort == 4) && Interface_IsOriginalMultiplayerItemModeEnabled()) { + sP4QuickItem = item; + if ((inventorySlot >= 0) && (inventorySlot < 24)) { + sP4QuickItemSlot = inventorySlot; + } else if (item == ITEM_NONE) { + sP4QuickItemSlot = SLOT_NONE; + } + + if (item < ITEM_NONE_FE) { + Interface_LoadItemIconForItem(play, 4, item); + } + return; + } + + if ((buttonIndex >= 1) && (buttonIndex < ARRAY_COUNT(gSaveContext.equips.buttonItems))) { + gSaveContext.equips.buttonItems[buttonIndex] = item; + if ((inventorySlot >= 0) && (inventorySlot < 24) && (buttonIndex - 1 < ARRAY_COUNT(gSaveContext.equips.cButtonSlots))) { + gSaveContext.equips.cButtonSlots[buttonIndex - 1] = inventorySlot; + } else if ((item == ITEM_NONE) && (buttonIndex - 1 < ARRAY_COUNT(gSaveContext.equips.cButtonSlots))) { + gSaveContext.equips.cButtonSlots[buttonIndex - 1] = SLOT_NONE; + } + Interface_LoadItemIcon1(play, buttonIndex); + } +} + +void Interface_SetQuickItemForPortAndSlot(PlayState* play, u8 controllerPort, s16 slotIndex, u8 item, + s16 inventorySlot) { + if ((controllerPort < 1) || (controllerPort > 4)) { + return; + } + + Interface_SetQuickItemForPortAndSlotInternal(play, controllerPort, slotIndex, item, inventorySlot); +} + +void Interface_SetQuickItemForPort(PlayState* play, u8 controllerPort, u8 item, s16 inventorySlot) { + Interface_SetQuickItemForPortAndSlot(play, controllerPort, 0, item, inventorySlot); +} + +void Interface_LoadItemIcon1(PlayState* play, u16 button) { + InterfaceContext* interfaceCtx = &play->interfaceCtx; + + osCreateMesgQueue(&interfaceCtx->loadQueue, &interfaceCtx->loadMsg, OS_MESG_BLOCK); + DmaMgr_SendRequest2(&interfaceCtx->dmaRequest_160, interfaceCtx->iconItemSegment + button * 0x1000, + (uintptr_t)_icon_item_staticSegmentRomStart + + (gSaveContext.equips.buttonItems[button] * 0x1000), + 0x1000, 0, &interfaceCtx->loadQueue, OS_MESG_PTR(NULL), __FILE__, __LINE__); + osRecvMesg(&interfaceCtx->loadQueue, NULL, OS_MESG_BLOCK); +} + +void Interface_LoadItemIcon2(PlayState* play, u16 button) { + InterfaceContext* interfaceCtx = &play->interfaceCtx; + + osCreateMesgQueue(&interfaceCtx->loadQueue, &interfaceCtx->loadMsg, OS_MESG_BLOCK); + DmaMgr_SendRequest2(&interfaceCtx->dmaRequest_180, interfaceCtx->iconItemSegment + button * 0x1000, + (uintptr_t)_icon_item_staticSegmentRomStart + + (gSaveContext.equips.buttonItems[button] * 0x1000), + 0x1000, 0, &interfaceCtx->loadQueue, OS_MESG_PTR(NULL), __FILE__, __LINE__); + osRecvMesg(&interfaceCtx->loadQueue, NULL, OS_MESG_BLOCK); +} + +void func_80084BF4(PlayState* play, u16 flag) { + if (flag) { + if ((gSaveContext.equips.buttonItems[0] == ITEM_SLINGSHOT) || + (gSaveContext.equips.buttonItems[0] == ITEM_BOW) || (gSaveContext.equips.buttonItems[0] == ITEM_BOMBCHU) || + (gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE) || + (gSaveContext.buttonStatus[0] == BTN_DISABLED)) { + if ((gSaveContext.equips.buttonItems[0] == ITEM_SLINGSHOT) || + (gSaveContext.equips.buttonItems[0] == ITEM_BOW) || + (gSaveContext.equips.buttonItems[0] == ITEM_BOMBCHU) || + (gSaveContext.equips.buttonItems[0] == ITEM_FISHING_POLE)) { + gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0]; + Interface_RandoRestoreSwordless(); + Interface_LoadItemIcon1(play, 0); + } + } else if (gSaveContext.equips.buttonItems[0] == ITEM_NONE) { + if ((gSaveContext.equips.buttonItems[0] != ITEM_NONE) || (gSaveContext.infTable[29] == 0)) { + gSaveContext.equips.buttonItems[0] = gSaveContext.buttonStatus[0]; + Interface_RandoRestoreSwordless(); + Interface_LoadItemIcon1(play, 0); + } + } + + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = BTN_ENABLED; + gSaveContext.buttonStatus[5] = gSaveContext.buttonStatus[6] = gSaveContext.buttonStatus[7] = + gSaveContext.buttonStatus[8] = BTN_ENABLED; + Interface_ChangeAlpha(7); + } else { + gSaveContext.buttonStatus[0] = gSaveContext.buttonStatus[1] = gSaveContext.buttonStatus[2] = + gSaveContext.buttonStatus[3] = BTN_ENABLED; + gSaveContext.buttonStatus[5] = gSaveContext.buttonStatus[6] = gSaveContext.buttonStatus[7] = + gSaveContext.buttonStatus[8] = BTN_ENABLED; + func_80083108(play); + } +} + +// Gameplay stat tracking: Update time the item was acquired +// (special cases for some duplicate items) +void GameplayStats_SetTimestamp(PlayState* play, u8 item) { + + // If we already have a timestamp for this item, do nothing + if (gSaveContext.ship.stats.itemTimestamp[item] != 0) { + return; + } + // Use ITEM_KEY_BOSS only for Ganon's boss key - not any other boss keys + if (play != NULL) { + if (item == ITEM_KEY_BOSS && play->sceneNum != SCENE_INSIDE_GANONS_CASTLE && + play->sceneNum != SCENE_GANONS_TOWER) { + return; + } + } + + u32 time = GAMEPLAYSTAT_TOTAL_TIME; + + // Have items in Link's pocket shown as being obtained at 0.1 seconds + if (time == 0) { + time = 1; + } + + // Count any bottled item as a bottle + if (item >= ITEM_BOTTLE && item <= ITEM_POE) { + if (gSaveContext.ship.stats.itemTimestamp[ITEM_BOTTLE] == 0) { + gSaveContext.ship.stats.itemTimestamp[ITEM_BOTTLE] = time; + } + return; + } + // Count any bombchu pack as bombchus + if (item == ITEM_BOMBCHU || (item >= ITEM_BOMBCHUS_5 && item <= ITEM_BOMBCHUS_20)) { + if (gSaveContext.ship.stats.itemTimestamp[ITEM_BOMBCHU] == 0) { + gSaveContext.ship.stats.itemTimestamp[ITEM_BOMBCHU] = time; + } + return; + } + + gSaveContext.ship.stats.itemTimestamp[item] = time; + GameInteractor_ExecuteOnTimestamp(item); +} + +u8 Return_Item_Entry(GetItemEntry itemEntry, u8 returnItem) { + GameInteractor_ExecuteOnItemReceiveHooks(itemEntry); + return returnItem; +} + +// Processes Item_Give returns +u8 Return_Item(u8 itemID, ModIndex modId, ItemID returnItem) { + // ITEM_SOLD_OUT doesn't have an ItemTable entry, so pass custom entry instead + if (itemID == ITEM_SOLD_OUT) { + GetItemEntry gie = { ITEM_SOLD_OUT, 0, 0, 0, 0, 0, 0, 0, 0, false, ITEM_FROM_NPC, ITEM_CATEGORY_LESSER, NULL }; + return Return_Item_Entry(gie, returnItem); + } + + GetItemID getItemID = RetrieveGetItemIDFromItemID(itemID); + if (getItemID != GI_MAX) { + // Vanilla ItemID with an associated GetItemID + return Return_Item_Entry(ItemTable_RetrieveEntry(modId, getItemID), returnItem); + } + + RandomizerGet randomizerGet = RetrieveRandomizerGetFromItemID(itemID); + if (randomizerGet != RG_MAX) { + // Vanilla ItemID with an associated RandomizerGet (These are items in extendedVanillaGetItemTable) + return Return_Item_Entry(ItemTable_RetrieveEntry(MOD_RANDOMIZER, randomizerGet), returnItem); + } + + // All randomizer items should go through Randomizer_Item_Give, so this should never be reached + // but leaving this here just in case, as it was in the original behavior + assert(false); + return Return_Item_Entry(ItemTable_RetrieveEntry(MOD_RANDOMIZER, itemID), returnItem); +} + +void Item_Give_SetPlayer(Player* player) { + sItemGivePlayer = player; +} + +void Item_Give_ClearPlayer(void) { + sItemGivePlayer = NULL; +} + +static Player* Item_Give_GetPlayer(PlayState* play) { + if (sItemGivePlayer != NULL) { + return sItemGivePlayer; + } + + if (play != NULL) { + return GET_PLAYER(play); + } + + return NULL; +} + +/** + * @brief Adds the given item to Link's inventory. + * + * NOTE: This function has been edited to be safe to use with a NULL play. + * If you need to add to this function, be sure you check if the play is not + * NULL before doing any operations requiring it. + * + * @param play + * @param item + * @return u8 + */ +u8 Item_Give(PlayState* play, u8 item) { + // prevents getting sticks without the bag in case something got missed + if (IS_RANDO && (item == ITEM_STICK || item == ITEM_STICKS_5 || item == ITEM_STICKS_10) && + Randomizer_GetSettingValue(RSK_SHUFFLE_DEKU_STICK_BAG) && CUR_UPG_VALUE(UPG_STICKS) == 0) { + return item; + } + + // prevents getting nuts without the bag in case something got missed + if (IS_RANDO && (item == ITEM_NUT || item == ITEM_NUTS_5 || item == ITEM_NUTS_10) && + Randomizer_GetSettingValue(RSK_SHUFFLE_DEKU_NUT_BAG) && CUR_UPG_VALUE(UPG_NUTS) == 0) { + return item; + } + + lusprintf(__FILE__, __LINE__, 2, "Item Give - item: %#x", item); + static s16 sAmmoRefillCounts[] = { 5, 10, 20, 30, 5, 10, 30, 0, 5, 20, 1, 5, 20, 50, 200, 10 }; + s16 i; + s16 slot; + s16 temp; + + GetItemID returnItem = ITEM_NONE; + + // Gameplay stats: Update the time the item was obtained + GameplayStats_SetTimestamp(play, item); + + slot = SLOT(item); + if (item >= ITEM_STICKS_5) { + slot = SLOT(sExtraItemBases[item - ITEM_STICKS_5]); + } + + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("item_get_setting=%d pt=%d z=%x\n", item, slot, gSaveContext.inventory.items[slot]); + osSyncPrintf(VT_RST); + + if ((item >= ITEM_MEDALLION_FOREST) && (item <= ITEM_MEDALLION_LIGHT)) { + gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_MEDALLION_FOREST + QUEST_MEDALLION_FOREST]; + + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("封印 = %x\n", gSaveContext.inventory.questItems); // "Seals = %x" + osSyncPrintf(VT_RST); + + if (item == ITEM_MEDALLION_WATER) { + func_8006D0AC(play); + } + + return Return_Item(item, MOD_NONE, ITEM_NONE); + } else if ((item >= ITEM_SONG_MINUET) && (item <= ITEM_SONG_STORMS)) { + gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_SONG_MINUET + QUEST_SONG_MINUET]; + + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("楽譜 = %x\n", gSaveContext.inventory.questItems); // "Musical scores = %x" + // "Musical scores = %x (%x) (%x)" + osSyncPrintf("楽譜 = %x (%x) (%x)\n", gSaveContext.inventory.questItems, + gBitFlags[item - ITEM_SONG_MINUET + QUEST_SONG_MINUET], gBitFlags[item - ITEM_SONG_MINUET]); + osSyncPrintf(VT_RST); + + return Return_Item(item, MOD_NONE, ITEM_NONE); + } else if ((item >= ITEM_KOKIRI_EMERALD) && (item <= ITEM_ZORA_SAPPHIRE)) { + gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_KOKIRI_EMERALD + QUEST_KOKIRI_EMERALD]; + + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("精霊石 = %x\n", gSaveContext.inventory.questItems); // "Spiritual Stones = %x" + osSyncPrintf(VT_RST); + + return Return_Item(item, MOD_NONE, ITEM_NONE); + } else if ((item == ITEM_STONE_OF_AGONY) || (item == ITEM_GERUDO_CARD)) { + gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_STONE_OF_AGONY + QUEST_STONE_OF_AGONY]; + + osSyncPrintf(VT_FGCOL(YELLOW)); + osSyncPrintf("アイテム = %x\n", gSaveContext.inventory.questItems); // "Items = %x" + osSyncPrintf(VT_RST); + + return Return_Item(item, MOD_NONE, ITEM_NONE); + } else if (item == ITEM_SKULL_TOKEN) { + gSaveContext.inventory.questItems |= gBitFlags[item - ITEM_SKULL_TOKEN + QUEST_SKULL_TOKEN]; + gSaveContext.inventory.gsTokens++; + + osSyncPrintf(VT_FGCOL(YELLOW)); + // "N Coins = %x(%d)" + osSyncPrintf("Nコイン = %x(%d)\n", gSaveContext.inventory.questItems, gSaveContext.inventory.gsTokens); + osSyncPrintf(VT_RST); + + return Return_Item(item, MOD_NONE, ITEM_NONE); + } else if ((item >= ITEM_SWORD_KOKIRI) && (item <= ITEM_SWORD_BGS)) { + gSaveContext.inventory.equipment |= + OWNED_EQUIP_FLAG(EQUIP_TYPE_SWORD, item - ITEM_SWORD_KOKIRI + EQUIP_INV_SWORD_KOKIRI); + + // Both Giant's Knife and Biggoron Sword have the same Item ID, so this part handles both of them + if (item == ITEM_SWORD_BGS) { + gSaveContext.swordHealth = 8; + + if (ALL_EQUIP_VALUE(EQUIP_TYPE_SWORD) == + ((1 << EQUIP_INV_SWORD_KOKIRI) | (1 << EQUIP_INV_SWORD_MASTER) | (1 << EQUIP_INV_SWORD_BIGGORON) | + (1 << EQUIP_INV_SWORD_BROKENGIANTKNIFE))) { + + gSaveContext.inventory.equipment ^= + OWNED_EQUIP_FLAG_ALT(EQUIP_TYPE_SWORD, EQUIP_INV_SWORD_BROKENGIANTKNIFE); + + if (gSaveContext.equips.buttonItems[0] == ITEM_SWORD_KNIFE) { + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_BGS; + if (play != NULL) { + Interface_LoadItemIcon1(play, 0); + } + } + } + + } else if (item == ITEM_SWORD_MASTER) { + gSaveContext.equips.buttonItems[0] = ITEM_SWORD_MASTER; + gSaveContext.equips.equipment &= (u16) ~(0xF << (EQUIP_TYPE_SWORD * 4)); + gSaveContext.equips.equipment |= EQUIP_VALUE_SWORD_MASTER << (EQUIP_TYPE_SWORD * 4); + if (play != NULL) { + Interface_LoadItemIcon1(play, 0); + } + } + + return Return_Item(item, MOD_NONE, ITEM_NONE); } else if ((item >= ITEM_SHIELD_DEKU) && (item <= ITEM_SHIELD_MIRROR)) { gSaveContext.inventory.equipment |= OWNED_EQUIP_FLAG(EQUIP_TYPE_SHIELD, item - ITEM_SHIELD_DEKU); return Return_Item(item, MOD_NONE, ITEM_NONE); @@ -2329,18 +3107,19 @@ u8 Item_Give(PlayState* play, u8 item) { } else if (item == ITEM_HEART) { osSyncPrintf("回復ハート回復ハート回復ハート\n"); // "Recovery Heart" if (play != NULL) { - Health_ChangeBy(play, FULL_HEART_HEALTH); + Health_ChangeBy(play, FULL_HEART_HEALTH, Item_Give_GetPlayer(play)); } return Return_Item(item, MOD_NONE, item); } else if (item == ITEM_MAGIC_SMALL) { - if (gSaveContext.magicState != MAGIC_STATE_ADD) { + Player* itemPlayer = Item_Give_GetPlayer(play); + if (Magic_GetStateForPlayer(itemPlayer) != MAGIC_STATE_ADD) { if (play != NULL) { - Magic_Fill(play); + Magic_FillForPlayer(play, itemPlayer); } } if (play != NULL) { - Magic_RequestChange(play, 12, MAGIC_ADD); + Magic_RequestChangeForPlayer(play, itemPlayer, 12, MAGIC_ADD); } if (!Flags_GetInfTable(INFTABLE_198)) { @@ -2350,13 +3129,14 @@ u8 Item_Give(PlayState* play, u8 item) { return Return_Item(item, MOD_NONE, item); } else if (item == ITEM_MAGIC_LARGE) { - if (gSaveContext.magicState != MAGIC_STATE_ADD) { + Player* itemPlayer = Item_Give_GetPlayer(play); + if (Magic_GetStateForPlayer(itemPlayer) != MAGIC_STATE_ADD) { if (play != NULL) { - Magic_Fill(play); + Magic_FillForPlayer(play, itemPlayer); } } if (play != NULL) { - Magic_RequestChange(play, 24, MAGIC_ADD); + Magic_RequestChangeForPlayer(play, itemPlayer, 24, MAGIC_ADD); } if (!Flags_GetInfTable(INFTABLE_198)) { @@ -2690,6 +3470,36 @@ s32 Inventory_HasSpecificBottle(u8 bottleItem) { } void Inventory_UpdateBottleItem(PlayState* play, u8 item, u8 button) { + if (Interface_IsOriginalMultiplayerItemModeEnabled()) { + s16 quickControllerPort = Interface_GetQuickItemControllerPortByButtonIndex(button); + + if (quickControllerPort != 0) { + s16 quickSlot = Interface_GetQuickItemUseSlotForPort(quickControllerPort); + s16 inventorySlot = Interface_GetQuickItemInventorySlotForPortAndSlot(quickControllerPort, quickSlot); + + if ((inventorySlot >= 0) && (inventorySlot < 24)) { + // Special case to only empty half of a Lon Lon Milk Bottle. + if ((gSaveContext.inventory.items[inventorySlot] == ITEM_MILK_BOTTLE) && (item == ITEM_BOTTLE)) { + item = ITEM_MILK_HALF; + } + + if (GameInteractor_Should(VB_UPDATE_BOTTLE_ITEM, true, button, item)) { + gSaveContext.inventory.items[inventorySlot] = item; + } + } + + Interface_SetQuickItemForPortAndSlot(play, quickControllerPort, quickSlot, item, inventorySlot); + + if (item < ITEM_NONE_FE) { + Interface_LoadItemIconForItem(play, button, item); + } + + play->pauseCtx.cursorItem[PAUSE_ITEM] = item; + gSaveContext.buttonStatus[BUTTON_STATUS_INDEX(button)] = BTN_ENABLED; + return; + } + } + osSyncPrintf("item_no=%x, c_no=%x, Pt=%x Item_Register=%x\n", item, button, gSaveContext.equips.cButtonSlots[button - 1], gSaveContext.inventory.items[gSaveContext.equips.cButtonSlots[button - 1]]); @@ -2716,18 +3526,58 @@ s32 Inventory_ConsumeFairy(PlayState* play) { s32 bottleSlot = SLOT(ITEM_FAIRY); s16 i; s16 j; + s16 quickControllerPort; + s16 quickSlot; for (i = 0; i < 4; i++) { if (gSaveContext.inventory.items[bottleSlot + i] == ITEM_FAIRY) { + if (Interface_IsOriginalMultiplayerItemModeEnabled()) { + for (quickControllerPort = 1; quickControllerPort <= 4; quickControllerPort++) { + for (quickSlot = 0; quickSlot < 2; quickSlot++) { + if (Interface_GetQuickItemForPortAndSlot(quickControllerPort, quickSlot) == ITEM_FAIRY) { + s16 quickInventorySlot = + Interface_GetQuickItemInventorySlotForPortAndSlot(quickControllerPort, quickSlot); + + Interface_SetQuickItemForPortAndSlot(play, quickControllerPort, quickSlot, ITEM_BOTTLE, + quickInventorySlot); + + if ((quickInventorySlot >= 0) && (quickInventorySlot < 24)) { + bottleSlot = quickInventorySlot; + i = 0; + } + + goto Inventory_ConsumeFairy_ApplyBottle; + } + } + } + } + for (j = 1; j < ARRAY_COUNT(gSaveContext.equips.buttonItems); j++) { - if (gSaveContext.equips.buttonItems[j] == ITEM_FAIRY) { - gSaveContext.equips.buttonItems[j] = ITEM_BOTTLE; - Interface_LoadItemIcon1(play, j); + u8 equippedItem = gSaveContext.equips.buttonItems[j]; + + if (Interface_IsOriginalMultiplayerItemModeEnabled() && (j == 4)) { + equippedItem = sP4QuickItem; + } + + if (equippedItem == ITEM_FAIRY) { i = 0; - bottleSlot = gSaveContext.equips.cButtonSlots[j - 1]; + + if (Interface_IsOriginalMultiplayerItemModeEnabled() && (j == 4)) { + sP4QuickItem = ITEM_BOTTLE; + Interface_LoadItemIconForItem(play, 4, ITEM_BOTTLE); + if ((sP4QuickItemSlot >= 0) && (sP4QuickItemSlot < 24)) { + bottleSlot = sP4QuickItemSlot; + } + } else { + gSaveContext.equips.buttonItems[j] = ITEM_BOTTLE; + Interface_LoadItemIcon1(play, j); + bottleSlot = gSaveContext.equips.cButtonSlots[j - 1]; + } break; } } + +Inventory_ConsumeFairy_ApplyBottle: osSyncPrintf("妖精使用=%d\n", bottleSlot); // "Fairy Usage=%d" gSaveContext.inventory.items[bottleSlot + i] = ITEM_BOTTLE; return 1; @@ -2810,6 +3660,11 @@ void Interface_SetDoAction(PlayState* play, u16 action) { void Interface_SetNaviCall(PlayState* play, u16 naviCallState) { InterfaceContext* interfaceCtx = &play->interfaceCtx; + if (CVarGetInteger(CVAR_ENHANCEMENT("DisableNaviMessages"), 0)) { + interfaceCtx->naviCalling = 0; + return; + } + if (((naviCallState == 0x1D) || (naviCallState == 0x1E)) && !interfaceCtx->naviCalling && (play->csCtx.state == CS_STATE_IDLE)) { if (!CVarGetInteger(CVAR_AUDIO("DisableNaviCallAudio"), 0)) { @@ -2865,13 +3720,47 @@ void Interface_LoadActionLabelB(PlayState* play, u16 action) { interfaceCtx->unk_1FA = 1; } -s32 Health_ChangeBy(PlayState* play, s16 healthChange) { +static void Health_GetTargetValuesForPlayer(Player* player, s16** health, s16** healthCapacity) { + *health = &gSaveContext.health; + *healthCapacity = &gSaveContext.healthCapacity; + + if ((player == NULL) || !player->isSecondPlayer) { + return; + } + + if (player->controllerPort == 4) { + *health = &gSaveContext.health4; + *healthCapacity = &gSaveContext.healthCapacity4; + } else if (player->controllerPort == 3) { + *health = &gSaveContext.health3; + *healthCapacity = &gSaveContext.healthCapacity3; + } else { + *health = &gSaveContext.health2; + *healthCapacity = &gSaveContext.healthCapacity2; + } + + if (**healthCapacity < STARTING_HEALTH) { + **healthCapacity = gSaveContext.healthCapacity; + if (**healthCapacity < STARTING_HEALTH) { + **healthCapacity = STARTING_HEALTH; + } + + if (**health <= 0) { + **health = **healthCapacity; + } + } +} + +s32 Health_ChangeBy(PlayState* play, s16 healthChange, Player* player) { u16 heartCount; u16 healthLevel; + s16* health; + s16* healthCapacity; + + Health_GetTargetValuesForPlayer(player, &health, &healthCapacity); // "***** Fluctuation=%d (now=%d, max=%d) ***" - osSyncPrintf("***** 増減=%d (now=%d, max=%d) ***", healthChange, gSaveContext.health, - gSaveContext.healthCapacity); + osSyncPrintf("***** 増減=%d (now=%d, max=%d) ***", healthChange, *health, *healthCapacity); if (healthChange < 0) { gSaveContext.ship.stats.count[COUNT_DAMAGE_TAKEN] += -healthChange; @@ -2880,7 +3769,7 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) { // If one-hit ko mode is on, any damage kills you and you cannot gain health. if (GameInteractor_OneHitKOActive()) { if (healthChange < 0) { - gSaveContext.health = 0; + *health = 0; } return 0; @@ -2904,13 +3793,13 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) { } } - gSaveContext.health += healthChange; + *health += healthChange; - if (gSaveContext.health > gSaveContext.healthCapacity) { - gSaveContext.health = gSaveContext.healthCapacity; + if (*health > *healthCapacity) { + *health = *healthCapacity; } - heartCount = gSaveContext.health % FULL_HEART_HEALTH; + heartCount = *health % FULL_HEART_HEALTH; healthLevel = heartCount; if (heartCount != 0) { @@ -2924,12 +3813,12 @@ s32 Health_ChangeBy(PlayState* play, s16 healthChange) { } // "Life=%d *** %d ******" - osSyncPrintf(" ライフ=%d *** %d ******\n", gSaveContext.health, healthLevel); + osSyncPrintf(" ライフ=%d *** %d ******\n", *health, healthLevel); GameInteractor_ExecuteOnPlayerHealthChange(healthChange); - if (gSaveContext.health <= 0) { - gSaveContext.health = 0; + if (*health <= 0) { + *health = 0; return 0; } else { return 1; @@ -3022,28 +3911,419 @@ void Inventory_ChangeAmmo(s16 item, s16 ammoChange) { } else if ((item == ITEM_SLINGSHOT) || (item == ITEM_SEEDS)) { AMMO(ITEM_SLINGSHOT) += ammoChange; - if (AMMO(ITEM_SLINGSHOT) >= CUR_CAPACITY(UPG_BULLET_BAG)) { - AMMO(ITEM_SLINGSHOT) = CUR_CAPACITY(UPG_BULLET_BAG); - } else if (AMMO(ITEM_SLINGSHOT) < 0) { - AMMO(ITEM_SLINGSHOT) = 0; - } - } else if (item == ITEM_BOMB) { - AMMO(ITEM_BOMB) += ammoChange; + if (AMMO(ITEM_SLINGSHOT) >= CUR_CAPACITY(UPG_BULLET_BAG)) { + AMMO(ITEM_SLINGSHOT) = CUR_CAPACITY(UPG_BULLET_BAG); + } else if (AMMO(ITEM_SLINGSHOT) < 0) { + AMMO(ITEM_SLINGSHOT) = 0; + } + } else if (item == ITEM_BOMB) { + AMMO(ITEM_BOMB) += ammoChange; + + if (AMMO(ITEM_BOMB) >= CUR_CAPACITY(UPG_BOMB_BAG)) { + AMMO(ITEM_BOMB) = CUR_CAPACITY(UPG_BOMB_BAG); + } else if (AMMO(ITEM_BOMB) < 0) { + AMMO(ITEM_BOMB) = 0; + } + } else if (item == ITEM_BEAN) { + AMMO(ITEM_BEAN) += ammoChange; + } + + osSyncPrintf("合計 = (%d)\n", AMMO(item)); // "Total = (%d)" + + if (ammoChange < 0) { + GameplayStats_UpdateAmmoUsed(item, -ammoChange); + } +} + +static u8 Magic_GetControllerPortForPlayer(Player* player) { + if ((player != NULL) && player->isSecondPlayer) { + if ((player->controllerPort >= 2) && (player->controllerPort <= 4)) { + return player->controllerPort; + } + return 2; + } + + return 1; +} + +static s32 Magic_IsSecondaryPort(u8 controllerPort) { + return (controllerPort >= 2) && (controllerPort <= 4); +} + +static s8* Magic_GetLevelPtrForPort(u8 controllerPort) { + if (controllerPort == 4) { + return &sP4MagicLevel; + } + if (controllerPort == 3) { + return &sP3MagicLevel; + } + if (controllerPort == 2) { + return &gSaveContext.magicLevel2; + } + return &gSaveContext.magicLevel; +} + +static s8* Magic_GetAmountPtrForPort(u8 controllerPort) { + if (controllerPort == 4) { + return &sP4Magic; + } + if (controllerPort == 3) { + return &sP3Magic; + } + if (controllerPort == 2) { + return &gSaveContext.magic2; + } + return &gSaveContext.magic; +} + +static u8* Magic_GetAcquiredPtrForPort(u8 controllerPort) { + if (controllerPort == 4) { + return &sP4IsMagicAcquired; + } + if (controllerPort == 3) { + return &sP3IsMagicAcquired; + } + if (controllerPort == 2) { + return &gSaveContext.isMagicAcquired2; + } + return &gSaveContext.isMagicAcquired; +} + +static u8* Magic_GetDoubleAcquiredPtrForPort(u8 controllerPort) { + if (controllerPort == 4) { + return &sP4IsDoubleMagicAcquired; + } + if (controllerPort == 3) { + return &sP3IsDoubleMagicAcquired; + } + if (controllerPort == 2) { + return &gSaveContext.isDoubleMagicAcquired2; + } + return &gSaveContext.isDoubleMagicAcquired; +} + +static s16* Magic_GetSecondaryCapacityPtr(u8 controllerPort) { + if (controllerPort == 4) { + return &sP4MagicCapacity; + } + if (controllerPort == 3) { + return &sP3MagicCapacity; + } + return &sP2MagicCapacity; +} + +static s16* Magic_GetSecondaryTargetPtr(u8 controllerPort) { + if (controllerPort == 4) { + return &sP4MagicTarget; + } + if (controllerPort == 3) { + return &sP3MagicTarget; + } + return &sP2MagicTarget; +} + +static s16* Magic_GetSecondaryFillTargetPtr(u8 controllerPort) { + if (controllerPort == 4) { + return &sP4MagicFillTarget; + } + if (controllerPort == 3) { + return &sP3MagicFillTarget; + } + return &sP2MagicFillTarget; +} + +static s16* Magic_GetSecondaryStatePtr(u8 controllerPort) { + if (controllerPort == 4) { + return &sP4MagicState; + } + if (controllerPort == 3) { + return &sP3MagicState; + } + return &sP2MagicState; +} + +static s16* Magic_GetSecondaryPrevStatePtr(u8 controllerPort) { + if (controllerPort == 4) { + return &sP4PrevMagicState; + } + if (controllerPort == 3) { + return &sP3PrevMagicState; + } + return &sP2PrevMagicState; +} + +static s16* Magic_GetSecondaryBorderRatioPtr(u8 controllerPort) { + if (controllerPort == 4) { + return &sP4MagicBorderRatio; + } + if (controllerPort == 3) { + return &sP3MagicBorderRatio; + } + return &sP2MagicBorderRatio; +} + +static s16* Magic_GetSecondaryBorderStepPtr(u8 controllerPort) { + if (controllerPort == 4) { + return &sP4MagicBorderStep; + } + if (controllerPort == 3) { + return &sP3MagicBorderStep; + } + return &sP2MagicBorderStep; +} + +static s16* Magic_GetSecondaryLensTimerPtr(u8 controllerPort) { + if (controllerPort == 4) { + return &sP4LensMagicConsumptionTimer; + } + if (controllerPort == 3) { + return &sP3LensMagicConsumptionTimer; + } + return &sP2LensMagicConsumptionTimer; +} + +static Color_RGB8* Magic_GetSecondaryBorderPtr(u8 controllerPort) { + if (controllerPort == 4) { + return &sMagicBorder4; + } + if (controllerPort == 3) { + return &sMagicBorder3; + } + return &sMagicBorder2; +} + +s16 Magic_GetLevelForPort(u8 controllerPort) { + return *Magic_GetLevelPtrForPort(controllerPort); +} + +s16 Magic_GetAmountForPort(u8 controllerPort) { + return *Magic_GetAmountPtrForPort(controllerPort); +} + +void Magic_InitForPort(u8 controllerPort) { + s8* magicLevel; + s8* magic; + u8* isMagicAcquired; + u8* isDoubleMagicAcquired; + + if (!Magic_IsSecondaryPort(controllerPort)) { + return; + } + + magicLevel = Magic_GetLevelPtrForPort(controllerPort); + magic = Magic_GetAmountPtrForPort(controllerPort); + isMagicAcquired = Magic_GetAcquiredPtrForPort(controllerPort); + isDoubleMagicAcquired = Magic_GetDoubleAcquiredPtrForPort(controllerPort); + + if (gSaveContext.isMagicAcquired && !*isMagicAcquired) { + *isMagicAcquired = gSaveContext.isMagicAcquired; + *isDoubleMagicAcquired = gSaveContext.isDoubleMagicAcquired; + *magicLevel = gSaveContext.magicLevel; + *magic = gSaveContext.magic; + } + + if (*isMagicAcquired && (*magicLevel == 0)) { + *magicLevel = *isDoubleMagicAcquired + 1; + } +} + +void Magic_ResetExtraData(void) { + sP2MagicCapacity = 0; + sP2MagicTarget = 0; + sP2MagicFillTarget = 0; + sP2MagicState = MAGIC_STATE_IDLE; + sP2PrevMagicState = MAGIC_STATE_IDLE; + sP2MagicBorderRatio = 2; + sP2MagicBorderStep = 1; + sP2LensMagicConsumptionTimer = 0; + sMagicBorder2 = (Color_RGB8){ 255, 255, 255 }; + + sP3MagicLevel = 0; + sP3Magic = MAGIC_NORMAL_METER; + sP3IsMagicAcquired = false; + sP3IsDoubleMagicAcquired = false; + sP3MagicCapacity = 0; + sP3MagicTarget = 0; + sP3MagicFillTarget = 0; + sP3MagicState = MAGIC_STATE_IDLE; + sP3PrevMagicState = MAGIC_STATE_IDLE; + sP3MagicBorderRatio = 2; + sP3MagicBorderStep = 1; + sP3LensMagicConsumptionTimer = 0; + sMagicBorder3 = (Color_RGB8){ 255, 255, 255 }; + + sP4MagicLevel = 0; + sP4Magic = MAGIC_NORMAL_METER; + sP4IsMagicAcquired = false; + sP4IsDoubleMagicAcquired = false; + sP4MagicCapacity = 0; + sP4MagicTarget = 0; + sP4MagicFillTarget = 0; + sP4MagicState = MAGIC_STATE_IDLE; + sP4PrevMagicState = MAGIC_STATE_IDLE; + sP4MagicBorderRatio = 2; + sP4MagicBorderStep = 1; + sP4LensMagicConsumptionTimer = 0; + sMagicBorder4 = (Color_RGB8){ 255, 255, 255 }; +} + +s16 Magic_GetStateForPlayer(Player* player) { + u8 controllerPort = Magic_GetControllerPortForPlayer(player); + + if (Magic_IsSecondaryPort(controllerPort)) { + return *Magic_GetSecondaryStatePtr(controllerPort); + } + return gSaveContext.magicState; +} + +void Magic_SetStateForPlayer(Player* player, s16 state) { + u8 controllerPort = Magic_GetControllerPortForPlayer(player); + + if (Magic_IsSecondaryPort(controllerPort)) { + *Magic_GetSecondaryStatePtr(controllerPort) = state; + return; + } + + gSaveContext.magicState = state; +} + +void Magic_FillForPlayer(PlayState* play, Player* player) { + u8 controllerPort = Magic_GetControllerPortForPlayer(player); + + if (Magic_IsSecondaryPort(controllerPort)) { + if (*Magic_GetAcquiredPtrForPort(controllerPort)) { + *Magic_GetSecondaryPrevStatePtr(controllerPort) = *Magic_GetSecondaryStatePtr(controllerPort); + *Magic_GetSecondaryFillTargetPtr(controllerPort) = + (*Magic_GetDoubleAcquiredPtrForPort(controllerPort) + 1) * MAGIC_NORMAL_METER; + *Magic_GetSecondaryStatePtr(controllerPort) = MAGIC_STATE_FILL; + } + return; + } + + Magic_Fill(play); +} + +void Magic_ResetForPlayer(PlayState* play, Player* player) { + u8 controllerPort = Magic_GetControllerPortForPlayer(player); + s16* magicState; + + if (Magic_IsSecondaryPort(controllerPort)) { + magicState = Magic_GetSecondaryStatePtr(controllerPort); + + if ((*magicState != MAGIC_STATE_STEP_CAPACITY) && (*magicState != MAGIC_STATE_FILL)) { + if (*magicState == MAGIC_STATE_ADD) { + *Magic_GetSecondaryPrevStatePtr(controllerPort) = *magicState; + } + *magicState = MAGIC_STATE_RESET; + } + return; + } + + Magic_Reset(play); +} + +static s32 Magic_RequestChangeSecondary(PlayState* play, u8 controllerPort, s16 amount, s16 type) { + s8* magic = Magic_GetAmountPtrForPort(controllerPort); + u8* isMagicAcquired = Magic_GetAcquiredPtrForPort(controllerPort); + s16* magicCapacity = Magic_GetSecondaryCapacityPtr(controllerPort); + s16* magicTarget = Magic_GetSecondaryTargetPtr(controllerPort); + s16* magicState = Magic_GetSecondaryStatePtr(controllerPort); + s16* prevMagicState = Magic_GetSecondaryPrevStatePtr(controllerPort); + s16* lensMagicConsumptionTimer = Magic_GetSecondaryLensTimerPtr(controllerPort); + + if (!*isMagicAcquired) { + return false; + } + + if ((type != MAGIC_ADD) && ((*magic - amount) < 0)) { + if (*magicCapacity != 0) { + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + } + return false; + } + + switch (type) { + case MAGIC_CONSUME_NOW: + case MAGIC_CONSUME_NOW_ALT: + if ((*magicState == MAGIC_STATE_IDLE) || (*magicState == MAGIC_STATE_CONSUME_LENS)) { + if (*magicState == MAGIC_STATE_CONSUME_LENS) { + play->actorCtx.lensActive = false; + } + *magicTarget = *magic - amount; + *magicState = MAGIC_STATE_CONSUME_SETUP; + return true; + } + + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + return false; + + case MAGIC_CONSUME_WAIT_NO_PREVIEW: + if ((*magicState == MAGIC_STATE_IDLE) || (*magicState == MAGIC_STATE_CONSUME_LENS)) { + if (*magicState == MAGIC_STATE_CONSUME_LENS) { + play->actorCtx.lensActive = false; + } + *magicTarget = *magic - amount; + *magicState = MAGIC_STATE_METER_FLASH_3; + return true; + } + + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + return false; + + case MAGIC_CONSUME_LENS: + if (*magicState == MAGIC_STATE_IDLE) { + if (*magic != 0) { + *lensMagicConsumptionTimer = 80; + *magicState = MAGIC_STATE_CONSUME_LENS; + return true; + } + return false; + } + + return *magicState == MAGIC_STATE_CONSUME_LENS; + + case MAGIC_CONSUME_WAIT_PREVIEW: + if ((*magicState == MAGIC_STATE_IDLE) || (*magicState == MAGIC_STATE_CONSUME_LENS)) { + if (*magicState == MAGIC_STATE_CONSUME_LENS) { + play->actorCtx.lensActive = false; + } + *magicTarget = *magic - amount; + *magicState = MAGIC_STATE_METER_FLASH_2; + return true; + } + + Audio_PlaySoundGeneral(NA_SE_SY_ERROR, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + return false; + + case MAGIC_ADD: + if (*magic <= *magicCapacity) { + *magicTarget = *magic + amount; - if (AMMO(ITEM_BOMB) >= CUR_CAPACITY(UPG_BOMB_BAG)) { - AMMO(ITEM_BOMB) = CUR_CAPACITY(UPG_BOMB_BAG); - } else if (AMMO(ITEM_BOMB) < 0) { - AMMO(ITEM_BOMB) = 0; - } - } else if (item == ITEM_BEAN) { - AMMO(ITEM_BEAN) += ammoChange; + if (*magicTarget >= *magicCapacity) { + *magicTarget = *magicCapacity; + } + + *magicState = MAGIC_STATE_ADD; + return true; + } + break; } - osSyncPrintf("合計 = (%d)\n", AMMO(item)); // "Total = (%d)" + return false; +} - if (ammoChange < 0) { - GameplayStats_UpdateAmmoUsed(item, -ammoChange); +s32 Magic_RequestChangeForPlayer(PlayState* play, Player* player, s16 amount, s16 type) { + u8 controllerPort = Magic_GetControllerPortForPlayer(player); + + if (Magic_IsSecondaryPort(controllerPort)) { + return Magic_RequestChangeSecondary(play, controllerPort, amount, type); } + + return Magic_RequestChange(play, amount, type); } void Magic_Fill(PlayState* play) { @@ -3407,6 +4687,223 @@ void Interface_UpdateMagicBar(PlayState* play) { } } +static void Magic_UpdateSecondary(PlayState* play, u8 controllerPort) { + static s16 sMagicBorderColors[][3] = { + { 255, 255, 255 }, + { 150, 150, 150 }, + { 255, 255, 150 }, + { 255, 255, 50 }, + }; + static s16 sMagicBorderIndices[] = { 0, 1, 1, 0 }; + MessageContext* msgCtx = &play->msgCtx; + s8* magicLevel = Magic_GetLevelPtrForPort(controllerPort); + s8* magic = Magic_GetAmountPtrForPort(controllerPort); + s16* magicCapacity = Magic_GetSecondaryCapacityPtr(controllerPort); + s16* magicTarget = Magic_GetSecondaryTargetPtr(controllerPort); + s16* magicFillTarget = Magic_GetSecondaryFillTargetPtr(controllerPort); + s16* magicState = Magic_GetSecondaryStatePtr(controllerPort); + s16* prevMagicState = Magic_GetSecondaryPrevStatePtr(controllerPort); + s16* magicBorderRatio = Magic_GetSecondaryBorderRatioPtr(controllerPort); + s16* magicBorderStep = Magic_GetSecondaryBorderStepPtr(controllerPort); + s16* lensMagicConsumptionTimer = Magic_GetSecondaryLensTimerPtr(controllerPort); + Color_RGB8* magicBorder = Magic_GetSecondaryBorderPtr(controllerPort); + s16 borderChangeR; + s16 borderChangeG; + s16 borderChangeB; + s16 temp; + + switch (*magicState) { + case MAGIC_STATE_STEP_CAPACITY: + temp = *magicLevel * MAGIC_NORMAL_METER; + if (temp != *magicCapacity) { + if (*magicCapacity < temp) { + *magicCapacity += 8; + if (*magicCapacity > temp) { + *magicCapacity = temp; + } + } else { + *magicCapacity -= 8; + if (*magicCapacity <= temp) { + *magicCapacity = temp; + } + } + } else { + *magicState = MAGIC_STATE_FILL; + } + break; + + case MAGIC_STATE_FILL: + *magic += 4; + + if (gSaveContext.gameMode == GAMEMODE_NORMAL && gSaveContext.sceneSetupIndex < 4) { + Audio_PlaySoundGeneral(NA_SE_SY_GAUGE_UP - SFX_FLAG, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + } + + if (*magic >= *magicFillTarget) { + *magic = *magicFillTarget; + *magicState = *prevMagicState; + *prevMagicState = MAGIC_STATE_IDLE; + } + break; + + case MAGIC_STATE_CONSUME_SETUP: + *magicBorderRatio = 2; + *magicState = MAGIC_STATE_CONSUME; + break; + + case MAGIC_STATE_CONSUME: + if (!Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MAGIC_METER)) { + *magic -= 2; + + if (*magic <= 0) { + *magic = 0; + *magicState = MAGIC_STATE_METER_FLASH_1; + *magicBorder = (Color_RGB8){ 255, 255, 255 }; + } else if (*magic == *magicTarget) { + *magicState = MAGIC_STATE_METER_FLASH_1; + *magicBorder = (Color_RGB8){ 255, 255, 255 }; + } + } + FALLTHROUGH; + + case MAGIC_STATE_METER_FLASH_1: + case MAGIC_STATE_METER_FLASH_2: + case MAGIC_STATE_METER_FLASH_3: + temp = sMagicBorderIndices[*magicBorderStep]; + borderChangeR = ABS(magicBorder->r - sMagicBorderColors[temp][0]) / *magicBorderRatio; + borderChangeG = ABS(magicBorder->g - sMagicBorderColors[temp][1]) / *magicBorderRatio; + borderChangeB = ABS(magicBorder->b - sMagicBorderColors[temp][2]) / *magicBorderRatio; + + if (magicBorder->r >= sMagicBorderColors[temp][0]) { + magicBorder->r -= borderChangeR; + } else { + magicBorder->r += borderChangeR; + } + + if (magicBorder->g >= sMagicBorderColors[temp][1]) { + magicBorder->g -= borderChangeG; + } else { + magicBorder->g += borderChangeG; + } + + if (magicBorder->b >= sMagicBorderColors[temp][2]) { + magicBorder->b -= borderChangeB; + } else { + magicBorder->b += borderChangeB; + } + + *magicBorderRatio -= 1; + if (*magicBorderRatio == 0) { + magicBorder->r = sMagicBorderColors[temp][0]; + magicBorder->g = sMagicBorderColors[temp][1]; + magicBorder->b = sMagicBorderColors[temp][2]; + *magicBorderRatio = YREG(40 + *magicBorderStep); + *magicBorderStep += 1; + if (*magicBorderStep >= 4) { + *magicBorderStep = 0; + } + } + break; + + case MAGIC_STATE_RESET: + *magicBorder = (Color_RGB8){ 255, 255, 255 }; + *magicState = MAGIC_STATE_IDLE; + break; + + case MAGIC_STATE_CONSUME_LENS: + if ((play->pauseCtx.state == 0) && (play->pauseCtx.debugState == 0) && (msgCtx->msgMode == MSGMODE_NONE) && + (play->gameOverCtx.state == GAMEOVER_INACTIVE) && (play->transitionTrigger == TRANS_TRIGGER_OFF) && + (play->transitionMode == TRANS_MODE_OFF) && !Play_InCsMode(play)) { + bool hasLens = false; + + for (s32 buttonIndex = 1; buttonIndex < ((CVarGetInteger(CVAR_ENHANCEMENT("DpadEquips"), 0) != 0) + ? ARRAY_COUNT(gSaveContext.equips.buttonItems) + : 4); + buttonIndex++) { + if (gSaveContext.equips.buttonItems[buttonIndex] == ITEM_LENS) { + hasLens = true; + break; + } + } + + if ((Interface_GetQuickItemForPortAndSlot(controllerPort, 0) == ITEM_LENS) || + (Interface_GetQuickItemForPortAndSlot(controllerPort, 1) == ITEM_LENS)) { + hasLens = true; + } + + if ((*magic == 0) || + ((Player_GetEnvironmentalHazard(play) >= PLAYER_ENV_HAZARD_UNDERWATER_FLOOR) && + (Player_GetEnvironmentalHazard(play) <= PLAYER_ENV_HAZARD_UNDERWATER_FREE)) || + !hasLens || !play->actorCtx.lensActive) { + play->actorCtx.lensActive = false; + Audio_PlaySoundGeneral(NA_SE_SY_GLASSMODE_OFF, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + *magicState = MAGIC_STATE_IDLE; + *magicBorder = (Color_RGB8){ 255, 255, 255 }; + break; + } + + *lensMagicConsumptionTimer -= 1; + if (*lensMagicConsumptionTimer == 0) { + *magic -= 1; + *lensMagicConsumptionTimer = 80; + } + } + + temp = sMagicBorderIndices[*magicBorderStep]; + borderChangeR = ABS(magicBorder->r - sMagicBorderColors[temp][0]) / *magicBorderRatio; + borderChangeG = ABS(magicBorder->g - sMagicBorderColors[temp][1]) / *magicBorderRatio; + borderChangeB = ABS(magicBorder->b - sMagicBorderColors[temp][2]) / *magicBorderRatio; + + if (magicBorder->r >= sMagicBorderColors[temp][0]) { + magicBorder->r -= borderChangeR; + } else { + magicBorder->r += borderChangeR; + } + + if (magicBorder->g >= sMagicBorderColors[temp][1]) { + magicBorder->g -= borderChangeG; + } else { + magicBorder->g += borderChangeG; + } + + if (magicBorder->b >= sMagicBorderColors[temp][2]) { + magicBorder->b -= borderChangeB; + } else { + magicBorder->b += borderChangeB; + } + + *magicBorderRatio -= 1; + if (*magicBorderRatio == 0) { + magicBorder->r = sMagicBorderColors[temp][0]; + magicBorder->g = sMagicBorderColors[temp][1]; + magicBorder->b = sMagicBorderColors[temp][2]; + *magicBorderRatio = YREG(40 + *magicBorderStep); + *magicBorderStep += 1; + if (*magicBorderStep >= 4) { + *magicBorderStep = 0; + } + } + break; + + case MAGIC_STATE_ADD: + *magic += 4; + Audio_PlaySoundGeneral(NA_SE_SY_GAUGE_UP - SFX_FLAG, &gSfxDefaultPos, 4, &gSfxDefaultFreqAndVolScale, + &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); + if (*magic >= *magicTarget) { + *magic = *magicTarget; + *magicState = *prevMagicState; + *prevMagicState = MAGIC_STATE_IDLE; + } + break; + + default: + *magicState = MAGIC_STATE_IDLE; + break; + } +} + void Interface_DrawLineupTick(PlayState* play) { InterfaceContext* interfaceCtx = &play->interfaceCtx; @@ -3430,6 +4927,9 @@ void Interface_DrawLineupTick(PlayState* play) { CLOSE_DISPS(play->state.gfxCtx); } +static void Interface_DrawMagicMeterForPortAt(PlayState* play, u8 controllerPort, s16 magicMeterX, s16 magicMeterY); +static s16 Interface_GetHeartRowCountForPort(u8 controllerPort); + void Interface_DrawMagicBar(PlayState* play) { InterfaceContext* interfaceCtx = &play->interfaceCtx; s16 magicDrop = R_MAGIC_BAR_LARGE_Y - R_MAGIC_BAR_SMALL_Y + 2; @@ -3536,6 +5036,28 @@ void Interface_DrawMagicBar(PlayState* play) { rMagicFillX = rMagicFillX_original; } + if (Interface_IsOriginalMultiplayerItemModeEnabled() && !Interface_IsSplitScreenEnabled() && + (CVarGetInteger(CVAR_COSMETIC("HUD.MagicBar.PosType"), 0) == ORIGINAL_LOCATION)) { + f32 heartsScale = 0.68f; + f32 topHeartsAnchorY; + f32 heartsBottomY; + s16 heartCount = gSaveContext.healthCapacity / FULL_HEART_HEALTH; + s16 heartRows; + + if (heartCount <= 0) { + heartCount = STARTING_HEALTH / FULL_HEART_HEALTH; + } + heartRows = (heartCount + 9) / 10; + + if (CVarGetInteger(CVAR_COSMETIC("HUD.HeartsCount.PosType"), 0) != ORIGINAL_LOCATION) { + heartsScale = CVarGetFloat(CVAR_COSMETIC("HUD.HeartsCount.Scale"), 0.7f); + } + + topHeartsAnchorY = getHealthMeterYOffset() + 21.0f - (8.0f * heartsScale); + heartsBottomY = topHeartsAnchorY + ((heartRows - 1) * 10.0f) + (16.0f * heartsScale); + magicBarY = (s16)(heartsBottomY + 0.0f); + } + Gfx_SetupDL_39Overlay(play->state.gfxCtx); gDPSetPrimColor(OVERLAY_DISP++, 0, 0, sMagicBorder.r, sMagicBorder.g, sMagicBorder.b, interfaceCtx->magicAlpha); @@ -3564,49 +5086,272 @@ void Interface_DrawMagicBar(PlayState* play) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbar_yellow.r, magicbar_yellow.g, magicbar_yellow.b, interfaceCtx->magicAlpha); - gDPLoadMultiBlock_4b(OVERLAY_DISP++, gMagicMeterFillTex, 0, G_TX_RENDERTILE, G_IM_FMT_I, 16, 16, 0, - G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, - G_TX_NOLOD, G_TX_NOLOD); + gDPLoadMultiBlock_4b(OVERLAY_DISP++, gMagicMeterFillTex, 0, G_TX_RENDERTILE, G_IM_FMT_I, 16, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + + gSPWideTextureRectangle(OVERLAY_DISP++, rMagicFillX << 2, (magicBarY + 3) << 2, + (rMagicFillX + gSaveContext.magic) << 2, (magicBarY + 10) << 2, G_TX_RENDERTILE, 0, + 0, 1 << 10, 1 << 10); + + // Fill the rest of the bar with the normal magic color + gDPPipeSync(OVERLAY_DISP++); + if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MAGIC_METER)) { + // Blue magic + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbar_blue.r, magicbar_blue.g, magicbar_blue.b, + interfaceCtx->magicAlpha); + } else { + // Green magic (default) + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbar_green.r, magicbar_green.g, magicbar_green.b, + interfaceCtx->magicAlpha); + } + + gSPWideTextureRectangle(OVERLAY_DISP++, rMagicFillX << 2, (magicBarY + 3) << 2, + (rMagicFillX + gSaveContext.magicTarget) << 2, (magicBarY + 10) << 2, + G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + } else { + // Fill the whole bar with the normal magic color + if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MAGIC_METER)) { + // Blue magic + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbar_blue.r, magicbar_blue.g, magicbar_blue.b, + interfaceCtx->magicAlpha); + } else { + // Green magic (default) + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbar_green.r, magicbar_green.g, magicbar_green.b, + interfaceCtx->magicAlpha); + } + + gDPLoadMultiBlock_4b(OVERLAY_DISP++, gMagicMeterFillTex, 0, G_TX_RENDERTILE, G_IM_FMT_I, 16, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); + + gSPWideTextureRectangle(OVERLAY_DISP++, rMagicFillX << 2, (magicBarY + 3) << 2, + (rMagicFillX + gSaveContext.magic) << 2, (magicBarY + 10) << 2, G_TX_RENDERTILE, 0, + 0, 1 << 10, 1 << 10); + } + } + + CLOSE_DISPS(play->state.gfxCtx); + + if (!Interface_IsOriginalMultiplayerItemModeEnabled()) { + s16 playerCount = Interface_GetLocalMultiplayerPlayerCount(); + + if (playerCount > 1) { + s16 i; + f32 heartsScale = 0.68f; + f32 topHeartsAnchorY; + f32 topHeartsCenterY; + f32 bottomHeartsAnchorY; + f32 splitViewportScaleX; + s32 splitScreenEnabled = Interface_IsSplitScreenEnabled(); + const s16 leftMagicAnchorX = OTRGetRectDimensionFromLeftEdge(22.0f); + const s16 iconSize = 24; + const s16 magicHeight = 16; + const s16 sharedMagicOffset = 2; + + if (CVarGetInteger(CVAR_COSMETIC("HUD.HeartsCount.PosType"), 0) != ORIGINAL_LOCATION) { + heartsScale = CVarGetFloat(CVAR_COSMETIC("HUD.HeartsCount.Scale"), 0.7f); + } + + splitViewportScaleX = + (OTRGetDimensionFromRightEdge(SCREEN_WIDTH) - OTRGetDimensionFromLeftEdge(0.0f)) / SCREEN_WIDTH; + + topHeartsAnchorY = getHealthMeterYOffset() + 21.0f - (8.0f * heartsScale); + topHeartsCenterY = topHeartsAnchorY + (8.0f * heartsScale); + bottomHeartsAnchorY = SCREEN_HEIGHT - topHeartsCenterY; + + for (i = 1; i < playerCount; i++) { + s16 magicCapacity; + s16 magicWidth; + s16 heartsTopY; + s16 heartsBottomY; + s16 heartRows; + s16 magicX; + s16 magicY; + u8 controllerPort = i + 1; + + if (Magic_GetLevelForPort(controllerPort) == 0) { + continue; + } + + heartRows = Interface_GetHeartRowCountForPort(controllerPort); + magicCapacity = Magic_GetLevelForPort(controllerPort) * MAGIC_NORMAL_METER; + magicWidth = 16 + magicCapacity; + + if (splitScreenEnabled) { + s16 viewportLeftX; + s16 viewportTopY; + s16 viewportLeftHudSpace; + + Interface_GetSplitViewportTopLeft(playerCount, i, &viewportLeftX, &viewportTopY); + viewportLeftHudSpace = + (s16)(OTRGetDimensionFromLeftEdge((f32)viewportLeftX) + (viewportLeftX * splitViewportScaleX)); + + heartsTopY = (s16)(viewportTopY + topHeartsAnchorY); + heartsBottomY = (s16)(heartsTopY + ((heartRows - 1) * 10.0f) + (16.0f * heartsScale)); + magicX = viewportLeftHudSpace + leftMagicAnchorX; + magicY = (s16)(heartsBottomY + 1.0f); + } else { + s16 playerSideAnchorX = (i % 2) ? (SCREEN_WIDTH - iconSize - leftMagicAnchorX) : leftMagicAnchorX; + + heartsTopY = (s16)((i < 2) ? topHeartsAnchorY : bottomHeartsAnchorY); + heartsBottomY = (s16)(heartsTopY + ((heartRows - 1) * 10.0f) + (16.0f * heartsScale)); + + if (i % 2) { + magicX = playerSideAnchorX + iconSize - magicWidth; + } else { + magicX = playerSideAnchorX; + } + + if (i < 2) { + magicY = (s16)(heartsBottomY + sharedMagicOffset); + } else { + magicY = (s16)(heartsTopY - magicHeight + sharedMagicOffset); + } + } + + Interface_DrawMagicMeterForPortAt(play, controllerPort, magicX, magicY); + } + } + } +} + +static s16 Magic_GetMeterCapacityForPort(u8 controllerPort) { + if (Magic_IsSecondaryPort(controllerPort)) { + return *Magic_GetSecondaryCapacityPtr(controllerPort); + } + + return gSaveContext.magicCapacity; +} + +static s16 Magic_GetMeterTargetForPort(u8 controllerPort) { + if (Magic_IsSecondaryPort(controllerPort)) { + return *Magic_GetSecondaryTargetPtr(controllerPort); + } + + return gSaveContext.magicTarget; +} + +static s16 Magic_GetMeterStateForPort(u8 controllerPort) { + if (Magic_IsSecondaryPort(controllerPort)) { + return *Magic_GetSecondaryStatePtr(controllerPort); + } + + return gSaveContext.magicState; +} + +static Color_RGB8 Magic_GetBorderColorForPort(u8 controllerPort) { + if (Magic_IsSecondaryPort(controllerPort)) { + return *Magic_GetSecondaryBorderPtr(controllerPort); + } + + return sMagicBorder; +} + +static void Interface_DrawMagicMeterForPortAt(PlayState* play, u8 controllerPort, s16 magicMeterX, s16 magicMeterY) { + InterfaceContext* interfaceCtx = &play->interfaceCtx; + s16 magicCapacity; + s16 currentMagic; + s16 magicTarget; + s16 magicState; + s16 magicFillX; + Color_RGB8 magicBorder; + Color_RGB8 magicbarYellow = { 250, 250, 0 }; + Color_RGB8 magicbarGreen = { R_MAGIC_FILL_COLOR(0), R_MAGIC_FILL_COLOR(1), R_MAGIC_FILL_COLOR(2) }; + Color_RGB8 magicbarBlue = { 0, 0, 200 }; + + if (Magic_GetLevelForPort(controllerPort) == 0) { + return; + } + + magicCapacity = Magic_GetMeterCapacityForPort(controllerPort); + if (magicCapacity <= 0) { + magicCapacity = Magic_GetLevelForPort(controllerPort) * MAGIC_NORMAL_METER; + } + + currentMagic = Magic_GetAmountForPort(controllerPort); + magicTarget = Magic_GetMeterTargetForPort(controllerPort); + magicState = Magic_GetMeterStateForPort(controllerPort); + magicFillX = magicMeterX + 8; + magicBorder = Magic_GetBorderColorForPort(controllerPort); + + if (CVarGetInteger(CVAR_COSMETIC("Consumable.MagicActive.Changed"), 0)) { + magicbarYellow = CVarGetColor24(CVAR_COSMETIC("Consumable.MagicActive.Value"), magicbarYellow); + } + + if (CVarGetInteger(CVAR_COSMETIC("Consumable.Magic.Changed"), 0)) { + magicbarGreen = CVarGetColor24(CVAR_COSMETIC("Consumable.Magic.Value"), magicbarGreen); + } + + if (CVarGetInteger("gCosmetics.Consumable_MagicInfinite.Changed", 0)) { + magicbarBlue = CVarGetColor24("gCosmetics.Consumable_MagicInfinite.Value", magicbarBlue); + } + + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL_39Overlay(play->state.gfxCtx); + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicBorder.r, magicBorder.g, magicBorder.b, interfaceCtx->magicAlpha); + gDPSetEnvColor(OVERLAY_DISP++, 100, 50, 50, 255); + + OVERLAY_DISP = + Gfx_TextureIA8(OVERLAY_DISP, gMagicMeterEndTex, 8, 16, magicMeterX, magicMeterY, 8, 16, 1 << 10, 1 << 10); + + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gMagicMeterMidTex, 24, 16, magicMeterX + 8, magicMeterY, + magicCapacity, 16, 1 << 10, 1 << 10); + + gDPLoadTextureBlock(OVERLAY_DISP++, gMagicMeterEndTex, G_IM_FMT_IA, G_IM_SIZ_8b, 8, 16, 0, + G_TX_MIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, 3, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSPWideTextureRectangle(OVERLAY_DISP++, (magicMeterX + 8 + magicCapacity) << 2, magicMeterY << 2, + (magicMeterX + 8 + magicCapacity + 8) << 2, (magicMeterY + 16) << 2, G_TX_RENDERTILE, + 256, 0, 1 << 10, 1 << 10); + + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, 0, 0, 0, PRIMITIVE, PRIMITIVE, + ENVIRONMENT, TEXEL0, ENVIRONMENT, 0, 0, 0, PRIMITIVE); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 255); + + if (magicState == MAGIC_STATE_METER_FLASH_2) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbarYellow.r, magicbarYellow.g, magicbarYellow.b, + interfaceCtx->magicAlpha); + + gDPLoadMultiBlock_4b(OVERLAY_DISP++, gMagicMeterFillTex, 0x0000, G_TX_RENDERTILE, G_IM_FMT_I, 16, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); - gSPWideTextureRectangle(OVERLAY_DISP++, rMagicFillX << 2, (magicBarY + 3) << 2, - (rMagicFillX + gSaveContext.magic) << 2, (magicBarY + 10) << 2, G_TX_RENDERTILE, 0, - 0, 1 << 10, 1 << 10); + gSPWideTextureRectangle(OVERLAY_DISP++, magicFillX << 2, (magicMeterY + 3) << 2, + (magicFillX + currentMagic) << 2, (magicMeterY + 10) << 2, G_TX_RENDERTILE, 0, 0, + 1 << 10, 1 << 10); - // Fill the rest of the bar with the normal magic color - gDPPipeSync(OVERLAY_DISP++); - if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MAGIC_METER)) { - // Blue magic - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbar_blue.r, magicbar_blue.g, magicbar_blue.b, - interfaceCtx->magicAlpha); - } else { - // Green magic (default) - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbar_green.r, magicbar_green.g, magicbar_green.b, - interfaceCtx->magicAlpha); - } + gDPPipeSync(OVERLAY_DISP++); + if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MAGIC_METER)) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbarBlue.r, magicbarBlue.g, magicbarBlue.b, + interfaceCtx->magicAlpha); + } else { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbarGreen.r, magicbarGreen.g, magicbarGreen.b, + interfaceCtx->magicAlpha); + } - gSPWideTextureRectangle(OVERLAY_DISP++, rMagicFillX << 2, (magicBarY + 3) << 2, - (rMagicFillX + gSaveContext.magicTarget) << 2, (magicBarY + 10) << 2, - G_TX_RENDERTILE, 0, 0, 1 << 10, 1 << 10); + gSPWideTextureRectangle(OVERLAY_DISP++, magicFillX << 2, (magicMeterY + 3) << 2, + (magicFillX + magicTarget) << 2, (magicMeterY + 10) << 2, G_TX_RENDERTILE, 0, 0, + 1 << 10, 1 << 10); + } else { + if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MAGIC_METER)) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbarBlue.r, magicbarBlue.g, magicbarBlue.b, + interfaceCtx->magicAlpha); } else { - // Fill the whole bar with the normal magic color - if (Flags_GetRandomizerInf(RAND_INF_HAS_INFINITE_MAGIC_METER)) { - // Blue magic - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbar_blue.r, magicbar_blue.g, magicbar_blue.b, - interfaceCtx->magicAlpha); - } else { - // Green magic (default) - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbar_green.r, magicbar_green.g, magicbar_green.b, - interfaceCtx->magicAlpha); - } + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, magicbarGreen.r, magicbarGreen.g, magicbarGreen.b, + interfaceCtx->magicAlpha); + } - gDPLoadMultiBlock_4b(OVERLAY_DISP++, gMagicMeterFillTex, 0, G_TX_RENDERTILE, G_IM_FMT_I, 16, 16, 0, - G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, - G_TX_NOLOD, G_TX_NOLOD); + gDPLoadMultiBlock_4b(OVERLAY_DISP++, gMagicMeterFillTex, 0x0000, G_TX_RENDERTILE, G_IM_FMT_I, 16, 16, 0, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, + G_TX_NOLOD, G_TX_NOLOD); - gSPWideTextureRectangle(OVERLAY_DISP++, rMagicFillX << 2, (magicBarY + 3) << 2, - (rMagicFillX + gSaveContext.magic) << 2, (magicBarY + 10) << 2, G_TX_RENDERTILE, 0, - 0, 1 << 10, 1 << 10); - } + gSPWideTextureRectangle(OVERLAY_DISP++, magicFillX << 2, (magicMeterY + 3) << 2, + (magicFillX + currentMagic) << 2, (magicMeterY + 10) << 2, G_TX_RENDERTILE, 0, 0, + 1 << 10, 1 << 10); } CLOSE_DISPS(play->state.gfxCtx); @@ -3819,6 +5564,7 @@ void Interface_DrawItemButtons(PlayState* play) { s16 dxdy; s16 width; s16 height; + s32 originalQuickItemModeEnabled = Interface_IsOriginalMultiplayerItemModeEnabled(); Color_RGB8 bButtonColor = { 0, 150, 0 }; if (CVarGetInteger(CVAR_COSMETIC("HUD.BButton.Changed"), 0)) { @@ -3900,6 +5646,12 @@ void Interface_DrawItemButtons(PlayState* play) { PosY_BtnB = PosY_BtnB_ori; PosX_BtnB = PosX_BtnB_ori; } + + if (originalQuickItemModeEnabled) { + PosX_BtnB = -9999; + PosY_BtnB = -9999; + } + // Start Button s16 X_Margins_StartBtn; s16 Y_Margins_StartBtn; @@ -4150,30 +5902,32 @@ void Interface_DrawItemButtons(PlayState* play) { OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gButtonBackgroundTex, BBtn_Size, BBtn_Size, PosX_BtnB, PosY_BtnB, BBtnScaled, BBtnScaled, BBtn_factor, BBtn_factor); - // C-Left Button Color & Texture - gDPPipeSync(OVERLAY_DISP++); - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, cLeftButtonColor.r, cLeftButtonColor.g, cLeftButtonColor.b, - interfaceCtx->cLeftAlpha); - gSPWideTextureRectangle(OVERLAY_DISP++, C_Left_BTN_Pos[0] << 2, C_Left_BTN_Pos[1] << 2, - (C_Left_BTN_Pos[0] + R_ITEM_BTN_WIDTH(1)) << 2, - (C_Left_BTN_Pos[1] + R_ITEM_BTN_WIDTH(1)) << 2, G_TX_RENDERTILE, 0, 0, - R_ITEM_BTN_DD(1) << 1, R_ITEM_BTN_DD(1) << 1); - - // C-Down Button Color & Texture - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, cDownButtonColor.r, cDownButtonColor.g, cDownButtonColor.b, - interfaceCtx->cDownAlpha); - gSPWideTextureRectangle(OVERLAY_DISP++, C_Down_BTN_Pos[0] << 2, C_Down_BTN_Pos[1] << 2, - (C_Down_BTN_Pos[0] + R_ITEM_BTN_WIDTH(2)) << 2, - (C_Down_BTN_Pos[1] + R_ITEM_BTN_WIDTH(2)) << 2, G_TX_RENDERTILE, 0, 0, - R_ITEM_BTN_DD(2) << 1, R_ITEM_BTN_DD(2) << 1); - - // C-Right Button Color & Texture - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, cRightButtonColor.r, cRightButtonColor.g, cRightButtonColor.b, - interfaceCtx->cRightAlpha); - gSPWideTextureRectangle(OVERLAY_DISP++, C_Right_BTN_Pos[0] << 2, C_Right_BTN_Pos[1] << 2, - (C_Right_BTN_Pos[0] + R_ITEM_BTN_WIDTH(3)) << 2, - (C_Right_BTN_Pos[1] + R_ITEM_BTN_WIDTH(3)) << 2, G_TX_RENDERTILE, 0, 0, - R_ITEM_BTN_DD(3) << 1, R_ITEM_BTN_DD(3) << 1); + if (!originalQuickItemModeEnabled) { + // C-Left Button Color & Texture + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, cLeftButtonColor.r, cLeftButtonColor.g, cLeftButtonColor.b, + interfaceCtx->cLeftAlpha); + gSPWideTextureRectangle(OVERLAY_DISP++, C_Left_BTN_Pos[0] << 2, C_Left_BTN_Pos[1] << 2, + (C_Left_BTN_Pos[0] + R_ITEM_BTN_WIDTH(1)) << 2, + (C_Left_BTN_Pos[1] + R_ITEM_BTN_WIDTH(1)) << 2, G_TX_RENDERTILE, 0, 0, + R_ITEM_BTN_DD(1) << 1, R_ITEM_BTN_DD(1) << 1); + + // C-Down Button Color & Texture + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, cDownButtonColor.r, cDownButtonColor.g, cDownButtonColor.b, + interfaceCtx->cDownAlpha); + gSPWideTextureRectangle(OVERLAY_DISP++, C_Down_BTN_Pos[0] << 2, C_Down_BTN_Pos[1] << 2, + (C_Down_BTN_Pos[0] + R_ITEM_BTN_WIDTH(2)) << 2, + (C_Down_BTN_Pos[1] + R_ITEM_BTN_WIDTH(2)) << 2, G_TX_RENDERTILE, 0, 0, + R_ITEM_BTN_DD(2) << 1, R_ITEM_BTN_DD(2) << 1); + + // C-Right Button Color & Texture + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, cRightButtonColor.r, cRightButtonColor.g, cRightButtonColor.b, + interfaceCtx->cRightAlpha); + gSPWideTextureRectangle(OVERLAY_DISP++, C_Right_BTN_Pos[0] << 2, C_Right_BTN_Pos[1] << 2, + (C_Right_BTN_Pos[0] + R_ITEM_BTN_WIDTH(3)) << 2, + (C_Right_BTN_Pos[1] + R_ITEM_BTN_WIDTH(3)) << 2, G_TX_RENDERTILE, 0, 0, + R_ITEM_BTN_DD(3) << 1, R_ITEM_BTN_DD(3) << 1); + } if ((pauseCtx->state < 8) || (pauseCtx->state >= 18)) { if ((play->pauseCtx.state != 0) || (play->pauseCtx.debugState != 0)) { @@ -4213,7 +5967,8 @@ void Interface_DrawItemButtons(PlayState* play) { } } - if (interfaceCtx->naviCalling && (play->pauseCtx.state == 0) && (play->pauseCtx.debugState == 0) && + if (!CVarGetInteger(CVAR_ENHANCEMENT("DisableNaviMessages"), 0) && interfaceCtx->naviCalling && + (play->pauseCtx.state == 0) && (play->pauseCtx.debugState == 0) && (play->csCtx.state == CS_STATE_IDLE)) { if (!sCUpInvisible) { // C-Up Button Texture, Color & Label (Navi Text) @@ -4255,11 +6010,12 @@ void Interface_DrawItemButtons(PlayState* play) { } } - gDPPipeSync(OVERLAY_DISP++); + if (!originalQuickItemModeEnabled) { + gDPPipeSync(OVERLAY_DISP++); - // Empty C Button Arrows - for (temp = 1; temp < 4; temp++) { - if (gSaveContext.equips.buttonItems[temp] > 0xF0) { + // Empty C Button Arrows + for (temp = 1; temp < 4; temp++) { + if (gSaveContext.equips.buttonItems[temp] > 0xF0) { s16 X_Margins_CL; s16 X_Margins_CR; s16 X_Margins_CD; @@ -4400,6 +6156,7 @@ void Interface_DrawItemButtons(PlayState* play) { ItemIconPos[temp - 1][1], ItemIconWidthFactor[temp - 1][0], ItemIconWidthFactor[temp - 1][0], ItemIconWidthFactor[temp - 1][1], ItemIconWidthFactor[temp - 1][1]); + } } } @@ -4838,108 +6595,652 @@ void Interface_DrawAmmoCount(PlayState* play, s16 button, s16 alpha) { ItemIconPos[1][0] = OTRGetRectDimensionFromRightEdge(ItemIconPos_ori[1][0]); ItemIconPos[1][1] = ItemIconPos_ori[1][1]; } - // C Button down - if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosType"), 0) != ORIGINAL_LOCATION) { - ItemIconPos[2][1] = CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosY"), 0) + Y_Margins_CD + PosY_adjust; - if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosType"), 0) == ANCHOR_LEFT) { - if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.UseMargins"), 0) != 0) { - X_Margins_CD = Left_HUD_Margin; - }; - ItemIconPos[2][0] = OTRGetDimensionFromLeftEdge(CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosX"), 0) + - X_Margins_CD + PosX_adjust); - } else if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosType"), 0) == ANCHOR_RIGHT) { - if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.UseMargins"), 0) != 0) { - X_Margins_CD = Right_HUD_Margin; - }; - ItemIconPos[2][0] = OTRGetDimensionFromRightEdge(CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosX"), 0) + - X_Margins_CD + PosX_adjust); - } else if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosType"), 0) == ANCHOR_NONE) { - ItemIconPos[2][0] = CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosX"), 0) + PosX_adjust; - } else if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosType"), 0) == HIDDEN) { - ItemIconPos[2][0] = -9999; + // C Button down + if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosType"), 0) != ORIGINAL_LOCATION) { + ItemIconPos[2][1] = CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosY"), 0) + Y_Margins_CD + PosY_adjust; + if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosType"), 0) == ANCHOR_LEFT) { + if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.UseMargins"), 0) != 0) { + X_Margins_CD = Left_HUD_Margin; + }; + ItemIconPos[2][0] = OTRGetDimensionFromLeftEdge(CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosX"), 0) + + X_Margins_CD + PosX_adjust); + } else if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosType"), 0) == ANCHOR_RIGHT) { + if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.UseMargins"), 0) != 0) { + X_Margins_CD = Right_HUD_Margin; + }; + ItemIconPos[2][0] = OTRGetDimensionFromRightEdge(CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosX"), 0) + + X_Margins_CD + PosX_adjust); + } else if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosType"), 0) == ANCHOR_NONE) { + ItemIconPos[2][0] = CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosX"), 0) + PosX_adjust; + } else if (CVarGetInteger(CVAR_COSMETIC("HUD.CDownButton.PosType"), 0) == HIDDEN) { + ItemIconPos[2][0] = -9999; + } + } else { + ItemIconPos[2][0] = OTRGetRectDimensionFromRightEdge(ItemIconPos_ori[2][0]); + ItemIconPos[2][1] = ItemIconPos_ori[2][1]; + } + // C button Right + if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosType"), 0) != ORIGINAL_LOCATION) { + ItemIconPos[3][1] = CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosY"), 0) + Y_Margins_CR + PosY_adjust; + if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosType"), 0) == ANCHOR_LEFT) { + if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.UseMargins"), 0) != 0) { + X_Margins_CR = Left_HUD_Margin; + }; + ItemIconPos[3][0] = OTRGetDimensionFromLeftEdge(CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosX"), 0) + + X_Margins_CR + PosX_adjust); + } else if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosType"), 0) == ANCHOR_RIGHT) { + if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.UseMargins"), 0) != 0) { + X_Margins_CR = Right_HUD_Margin; + }; + ItemIconPos[3][0] = OTRGetDimensionFromRightEdge(CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosX"), 0) + + X_Margins_CR + PosX_adjust); + } else if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosType"), 0) == ANCHOR_NONE) { + ItemIconPos[3][0] = CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosX"), 0) + PosX_adjust; + } else if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosType"), 0) == HIDDEN) { + ItemIconPos[3][0] = -9999; + } + } else { + ItemIconPos[3][0] = OTRGetRectDimensionFromRightEdge(ItemIconPos_ori[3][0]); + ItemIconPos[3][1] = ItemIconPos_ori[3][1]; + } + + OPEN_DISPS(play->state.gfxCtx); + + i = gSaveContext.equips.buttonItems[button]; + + if (GameInteractor_Should(VB_DRAW_AMMO_COUNT, + ((i == ITEM_STICK) || (i == ITEM_NUT) || (i == ITEM_BOMB) || (i == ITEM_BOW) || + ((i >= ITEM_BOW_ARROW_FIRE) && (i <= ITEM_BOW_ARROW_LIGHT)) || (i == ITEM_SLINGSHOT) || + (i == ITEM_BOMBCHU) || (i == ITEM_BEAN)), + &i)) { + if ((i >= ITEM_BOW_ARROW_FIRE) && (i <= ITEM_BOW_ARROW_LIGHT)) { + i = ITEM_BOW; + } + + ammo = AMMO(i); + + gDPPipeSync(OVERLAY_DISP++); + + if ((button == 0) && (gSaveContext.minigameState == 1)) { + ammo = play->interfaceCtx.hbaAmmo; + } else if ((button == 0) && (play->shootingGalleryStatus > 1)) { + ammo = play->shootingGalleryStatus - 1; + } else if ((button == 0) && (play->sceneNum == SCENE_BOMBCHU_BOWLING_ALLEY) && Flags_GetSwitch(play, 0x38)) { + ammo = play->bombchuBowlingStatus; + if (ammo < 0) { + ammo = 0; + } + } else if (((i == ITEM_BOW) && (AMMO(i) == CUR_CAPACITY(UPG_QUIVER))) || + ((i == ITEM_BOMB) && (AMMO(i) == CUR_CAPACITY(UPG_BOMB_BAG))) || + ((i == ITEM_SLINGSHOT) && (AMMO(i) == CUR_CAPACITY(UPG_BULLET_BAG))) || + ((i == ITEM_STICK) && (AMMO(i) == CUR_CAPACITY(UPG_STICKS))) || + ((i == ITEM_NUT) && (AMMO(i) == CUR_CAPACITY(UPG_NUTS))) || ((i == ITEM_BOMBCHU) && (ammo == 50)) || + ((i == ITEM_BEAN) && (ammo == 15)) || GameInteractor_Should(VB_COLOR_AMMO_GREEN, false, i)) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 120, 255, 0, alpha); + } + + if (ammo == 0) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 100, 100, 100, alpha); + } + + for (i = 0; ammo >= 10; i++) { + ammo -= 10; + } + + if (i != 0) { + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, (u8*)_gAmmoDigit0Tex[i], 8, 8, ItemIconPos[button][0], + ItemIconPos[button][1], 8, 8, 1 << 10, 1 << 10); + } + + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, (u8*)_gAmmoDigit0Tex[ammo], 8, 8, ItemIconPos[button][0] + 6, + ItemIconPos[button][1], 8, 8, 1 << 10, 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +static void Interface_DrawItemIconTextureAt(PlayState* play, void* texture, s16 x, s16 y, s16 width, s16 height) { + u16 dsdx = (32 << 10) / width; + u16 dtdy = (32 << 10) / height; + + OPEN_DISPS(play->state.gfxCtx); + + gDPLoadTextureBlock(OVERLAY_DISP++, texture, G_IM_FMT_RGBA, G_IM_SIZ_32b, 32, 32, 0, G_TX_NOMIRROR | G_TX_WRAP, + G_TX_NOMIRROR | G_TX_WRAP, G_TX_NOMASK, G_TX_NOMASK, G_TX_NOLOD, G_TX_NOLOD); + + gSPWideTextureRectangle(OVERLAY_DISP++, x << 2, y << 2, (x + width) << 2, (y + height) << 2, G_TX_RENDERTILE, + 0, 0, dsdx, dtdy); + + CLOSE_DISPS(play->state.gfxCtx); +} + +static void Interface_DrawAmmoCountForItemAt(PlayState* play, s16 item, s16 alpha, s16 x, s16 y) { + s16 tens; + s16 ammo; + + OPEN_DISPS(play->state.gfxCtx); + + if (GameInteractor_Should(VB_DRAW_AMMO_COUNT, + ((item == ITEM_STICK) || (item == ITEM_NUT) || (item == ITEM_BOMB) || + (item == ITEM_BOW) || ((item >= ITEM_BOW_ARROW_FIRE) && (item <= ITEM_BOW_ARROW_LIGHT)) || + (item == ITEM_SLINGSHOT) || (item == ITEM_BOMBCHU) || (item == ITEM_BEAN)), + &item)) { + if ((item >= ITEM_BOW_ARROW_FIRE) && (item <= ITEM_BOW_ARROW_LIGHT)) { + item = ITEM_BOW; + } + + ammo = AMMO(item); + + gDPPipeSync(OVERLAY_DISP++); + + if (((item == ITEM_BOW) && (ammo == CUR_CAPACITY(UPG_QUIVER))) || + ((item == ITEM_BOMB) && (ammo == CUR_CAPACITY(UPG_BOMB_BAG))) || + ((item == ITEM_SLINGSHOT) && (ammo == CUR_CAPACITY(UPG_BULLET_BAG))) || + ((item == ITEM_STICK) && (ammo == CUR_CAPACITY(UPG_STICKS))) || + ((item == ITEM_NUT) && (ammo == CUR_CAPACITY(UPG_NUTS))) || ((item == ITEM_BOMBCHU) && (ammo == 50)) || + ((item == ITEM_BEAN) && (ammo == 15)) || GameInteractor_Should(VB_COLOR_AMMO_GREEN, false, item)) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 120, 255, 0, alpha); + } + + if (ammo == 0) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 100, 100, 100, alpha); + } + + for (tens = 0; ammo >= 10; tens++) { + ammo -= 10; + } + + if (tens != 0) { + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, (u8*)_gAmmoDigit0Tex[tens], 8, 8, x, y, 8, 8, 1 << 10, + 1 << 10); + } + + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, (u8*)_gAmmoDigit0Tex[ammo], 8, 8, x + 6, y, 8, 8, 1 << 10, + 1 << 10); + } + + CLOSE_DISPS(play->state.gfxCtx); +} + +static s16 Interface_GetQuickItemAlpha(InterfaceContext* interfaceCtx, u8 controllerPort, s16 slotIndex) { + (void)slotIndex; + + switch (Interface_GetQuickItemButtonIndexImpl(controllerPort)) { + case 1: + return interfaceCtx->cLeftAlpha; + + case 2: + return interfaceCtx->cDownAlpha; + + case 3: + return interfaceCtx->cRightAlpha; + + case 4: + default: + return interfaceCtx->dpadUpAlpha; + } +} + +static void Interface_DrawQuickItemHudAt(PlayState* play, u8 controllerPort, s16 slotIndex, s16 x, s16 y) { + InterfaceContext* interfaceCtx = &play->interfaceCtx; + u8 item = Interface_GetQuickItemForPortAndSlot(controllerPort, slotIndex); + s16 alpha = Interface_GetQuickItemAlpha(interfaceCtx, controllerPort, slotIndex); + + if ((item == ITEM_NONE) || (item >= ITEM_NONE_FE)) { + return; + } + + OPEN_DISPS(play->state.gfxCtx); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, alpha); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + CLOSE_DISPS(play->state.gfxCtx); + + Interface_DrawItemIconTextureAt(play, gItemIcons[item], x, y, 24, 24); + Interface_DrawAmmoCountForItemAt(play, item, alpha, x + 1, y + 13); +} + +static void Interface_DrawRadialQuickMenuHud(PlayState* play, s16 playerCount) { + InterfaceContext* interfaceCtx = &play->interfaceCtx; + s32 splitScreenEnabled = Interface_IsSplitScreenEnabled(); + const s16 iconSize = 24; + const s16 bgSize = 18; + const s16 bgOffset = (iconSize - bgSize) / 2; + const s16 ringOuterSize = 112; + const s16 ringInnerSize = 84; + const s16 ringRadius = 60; + const s16 ringOuterHalf = ringOuterSize / 2; + const s16 ringInnerHalf = ringInnerSize / 2; + Color_RGB8 wheelColor = { 255, 255, 255 }; + Color_RGB8 centerColor = { 255, 255, 255 }; + Color_RGB8 itemButtonColor = { 255, 160, 0 }; + Color_RGB8 selectedItemButtonColor = { 255, 255, 170 }; + u8 wheelAlpha = interfaceCtx->bAlpha / 3; + u8 centerAlpha = interfaceCtx->bAlpha / 5; + u8 slotAlpha = interfaceCtx->bAlpha / 2; + u8 selectedSlotAlpha = (interfaceCtx->bAlpha * 3) / 4; + s32 i; + + if (wheelAlpha < 35) { + wheelAlpha = 35; + } + + if (centerAlpha < 20) { + centerAlpha = 20; + } + + if (slotAlpha < 60) { + slotAlpha = 60; + } + + if (selectedSlotAlpha < 90) { + selectedSlotAlpha = 90; + } + + if (!Interface_IsRadialQuickItemMenuEnabled()) { + return; + } + + for (i = 0; i < playerCount; i++) { + LocalMPRadialQuickMenuState* state = &sRadialQuickMenuStates[i]; + s16 controllerPort = i + 1; + s16 centerX; + s16 centerY; + s16 j; + Viewport viewport; + + if (!state->active || (state->itemCount == 0)) { + continue; + } + + if (splitScreenEnabled) { + Interface_GetSplitViewportBounds(playerCount, i, &viewport); + centerX = (viewport.leftX + viewport.rightX) / 2; + centerY = (viewport.topY + viewport.bottomY) / 2; + } else { + centerX = SCREEN_WIDTH / 2; + centerY = SCREEN_HEIGHT / 2; + } + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 255); + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, wheelColor.r, wheelColor.g, wheelColor.b, wheelAlpha); + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gButtonBackgroundTex, 32, 32, centerX - ringOuterHalf, + centerY - ringOuterHalf, ringOuterSize, ringOuterSize, + (32 << 10) / ringOuterSize, (32 << 10) / ringOuterSize); + + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, centerColor.r, centerColor.g, centerColor.b, centerAlpha); + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gButtonBackgroundTex, 32, 32, centerX - ringInnerHalf, + centerY - ringInnerHalf, ringInnerSize, ringInnerSize, + (32 << 10) / ringInnerSize, (32 << 10) / ringInnerSize); + CLOSE_DISPS(play->state.gfxCtx); + + Interface_DrawQuickItemHudAt(play, controllerPort, state->slotIndex, centerX - (iconSize / 2), + centerY - (iconSize / 2)); + + for (j = 0; j < state->itemCount; j++) { + u16 angle = (u16)((0x10000 / state->itemCount) * j); + s16 itemX = centerX + (s16)(Math_SinS(angle) * ringRadius) - (iconSize / 2); + s16 itemY = centerY - (s16)(Math_CosS(angle) * ringRadius) - (iconSize / 2); + + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 255); + + if (j == state->selectedIndex) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, selectedItemButtonColor.r, selectedItemButtonColor.g, + selectedItemButtonColor.b, selectedSlotAlpha); + } else { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, itemButtonColor.r, itemButtonColor.g, itemButtonColor.b, + slotAlpha); + } + + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gButtonBackgroundTex, 32, 32, itemX + bgOffset, + itemY + bgOffset, bgSize, bgSize, (32 << 10) / bgSize, + (32 << 10) / bgSize); + CLOSE_DISPS(play->state.gfxCtx); + + Interface_DrawItemIconTextureAt(play, gItemIcons[state->items[j]], itemX, itemY, iconSize, iconSize); + Interface_DrawAmmoCountForItemAt(play, state->items[j], interfaceCtx->bAlpha, itemX + 1, itemY + 13); + } + } +} + +static s16 Interface_GetHealthCapacityForPort(u8 controllerPort) { + s16 healthCapacity = gSaveContext.healthCapacity; + + if (controllerPort == 4) { + healthCapacity = gSaveContext.healthCapacity4; + } else if (controllerPort == 3) { + healthCapacity = gSaveContext.healthCapacity3; + } else if (controllerPort == 2) { + healthCapacity = gSaveContext.healthCapacity2; + } + + if ((controllerPort >= 2) && (healthCapacity < STARTING_HEALTH)) { + healthCapacity = gSaveContext.healthCapacity; + if (healthCapacity < STARTING_HEALTH) { + healthCapacity = STARTING_HEALTH; } - } else { - ItemIconPos[2][0] = OTRGetRectDimensionFromRightEdge(ItemIconPos_ori[2][0]); - ItemIconPos[2][1] = ItemIconPos_ori[2][1]; } - // C button Right - if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosType"), 0) != ORIGINAL_LOCATION) { - ItemIconPos[3][1] = CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosY"), 0) + Y_Margins_CR + PosY_adjust; - if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosType"), 0) == ANCHOR_LEFT) { - if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.UseMargins"), 0) != 0) { - X_Margins_CR = Left_HUD_Margin; - }; - ItemIconPos[3][0] = OTRGetDimensionFromLeftEdge(CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosX"), 0) + - X_Margins_CR + PosX_adjust); - } else if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosType"), 0) == ANCHOR_RIGHT) { - if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.UseMargins"), 0) != 0) { - X_Margins_CR = Right_HUD_Margin; - }; - ItemIconPos[3][0] = OTRGetDimensionFromRightEdge(CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosX"), 0) + - X_Margins_CR + PosX_adjust); - } else if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosType"), 0) == ANCHOR_NONE) { - ItemIconPos[3][0] = CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosX"), 0) + PosX_adjust; - } else if (CVarGetInteger(CVAR_COSMETIC("HUD.CRightButton.PosType"), 0) == HIDDEN) { - ItemIconPos[3][0] = -9999; + + return healthCapacity; +} + +static s16 Interface_GetHeartRowCountForPort(u8 controllerPort) { + s16 heartCount = Interface_GetHealthCapacityForPort(controllerPort) / FULL_HEART_HEALTH; + + if (heartCount <= 0) { + heartCount = STARTING_HEALTH / FULL_HEART_HEALTH; + } + + return (heartCount + 9) / 10; +} + +static void Interface_DrawOriginalMultiplayerItemHud(PlayState* play) { + InterfaceContext* interfaceCtx = &play->interfaceCtx; + s16 playerCount; + s16 playerIconY[4]; + s16 playerItemSlot2X[4]; + s16 playerItemSlot2Y[4]; + s16 playerMagicX[4]; + s16 playerMagicY[4]; + u8 playerDrawMagic[4]; + s16 playerSwordX[4]; + s16 playerItemX[4]; + s16 playerHeartRows[4]; + f32 heartsScale = 0.68f; + f32 topHeartsAnchorY; + f32 topHeartsCenterY; + f32 bottomHeartsAnchorY; + f32 splitViewportScaleX; + s32 splitScreenEnabled; + s32 secondSlotEnabled; + s32 sideBySideQuickSlotLayout; + const f32 topIconGap = 3.0f; + const f32 bottomIconGap = 1.0f; + const f32 sharedTopIconGap = 1.0f; + const f32 sharedBottomIconGap = 0.0f; + const s16 magicHeight = 16; + const s16 magicGap = 3; + const s16 sharedMagicGap = 1; + const s16 sharedMagicOffset = 2; /* small Y offset to nudge shared-screen magic bars closer */ + const s16 iconSize = 24; + const s16 bgSize = 18; /* smaller background circle size */ + const s16 buttonGap = 1; + const s16 buttonStep = bgSize + buttonGap; + const s16 bgOffset = (iconSize - bgSize) / 2; + s16 i; + s16 selectedSlot; + const s16 leftSwordX = OTRGetRectDimensionFromLeftEdge(22.0f); + const s16 leftItemX = leftSwordX + buttonStep; + const s16 rightSwordX = SCREEN_WIDTH - iconSize - leftSwordX; + const s16 rightItemX = SCREEN_WIDTH - iconSize - leftItemX; + Color_RGB8 swordButtonColor = { 0, 150, 0 }; + Color_RGB8 itemButtonColor = { 255, 160, 0 }; + Color_RGB8 selectedItemButtonColor = { 255, 255, 150 }; + + if (!Interface_IsOriginalMultiplayerItemModeEnabled()) { + return; + } + + playerCount = Interface_GetLocalMultiplayerPlayerCount(); + if (playerCount <= 1) { + return; + } + + secondSlotEnabled = Interface_IsSecondQuickItemSlotEnabled(); + sideBySideQuickSlotLayout = Interface_IsSecondQuickItemSlotSideBySideEnabled(); + + if (CVarGetInteger(CVAR_COSMETIC("HUD.HeartsCount.PosType"), 0) != ORIGINAL_LOCATION) { + heartsScale = CVarGetFloat(CVAR_COSMETIC("HUD.HeartsCount.Scale"), 0.7f); + } + + splitScreenEnabled = Interface_IsSplitScreenEnabled(); + splitViewportScaleX = + (OTRGetDimensionFromRightEdge(SCREEN_WIDTH) - OTRGetDimensionFromLeftEdge(0.0f)) / SCREEN_WIDTH; + + topHeartsAnchorY = getHealthMeterYOffset() + 21.0f - (8.0f * heartsScale); + topHeartsCenterY = topHeartsAnchorY + (8.0f * heartsScale); + bottomHeartsAnchorY = SCREEN_HEIGHT - topHeartsCenterY; + + for (i = 0; i < ARRAY_COUNT(playerDrawMagic); i++) { + playerDrawMagic[i] = false; + playerMagicX[i] = 0; + playerMagicY[i] = 0; + } + + if (splitScreenEnabled) { + for (i = 0; i < 4; i++) { + s16 viewportLeftX; + s16 viewportTopY; + s16 viewportLeftHudSpace; + s16 magicCapacity; + s16 magicWidth; + f32 heartsTopY; + f32 heartsBottomY; + + Interface_GetSplitViewportTopLeft(playerCount, i, &viewportLeftX, &viewportTopY); + viewportLeftHudSpace = (s16)(viewportLeftX * splitViewportScaleX); + + playerHeartRows[i] = Interface_GetHeartRowCountForPort(i + 1); + heartsTopY = viewportTopY + topHeartsAnchorY; + heartsBottomY = heartsTopY + ((playerHeartRows[i] - 1) * 10.0f) + (16.0f * heartsScale); + playerIconY[i] = (s16)(heartsBottomY + topIconGap + 0.999f); + playerSwordX[i] = viewportLeftHudSpace + leftSwordX; + playerItemX[i] = viewportLeftHudSpace + leftItemX; + playerItemSlot2X[i] = playerItemX[i] + (sideBySideQuickSlotLayout ? buttonStep : 0); + + if ((i > 0) && (i < playerCount) && (Magic_GetLevelForPort(i + 1) != 0)) { + magicCapacity = Magic_GetMeterCapacityForPort(i + 1); + if (magicCapacity <= 0) { + magicCapacity = Magic_GetLevelForPort(i + 1) * MAGIC_NORMAL_METER; + } + + magicWidth = 16 + magicCapacity; + playerMagicX[i] = playerSwordX[i]; + playerMagicY[i] = (s16)(heartsBottomY + 1.0f); + playerIconY[i] = playerMagicY[i] + magicHeight + magicGap; + playerDrawMagic[i] = magicWidth > 0; + } + + playerItemSlot2Y[i] = playerIconY[i] + (sideBySideQuickSlotLayout ? 0 : buttonStep); } } else { - ItemIconPos[3][0] = OTRGetRectDimensionFromRightEdge(ItemIconPos_ori[3][0]); - ItemIconPos[3][1] = ItemIconPos_ori[3][1]; + const s16 sharedLeftItemX = leftSwordX + buttonStep; + const s16 sharedRightItemX = SCREEN_WIDTH - iconSize - sharedLeftItemX; + + playerSwordX[0] = leftSwordX; + playerSwordX[1] = rightSwordX; + playerSwordX[2] = leftSwordX; + playerSwordX[3] = rightSwordX; + + playerItemX[0] = sharedLeftItemX; + playerItemX[1] = sharedRightItemX; + playerItemX[2] = sharedLeftItemX; + playerItemX[3] = sharedRightItemX; + + for (i = 0; i < 4; i++) { + f32 heartsTopY; + f32 heartsBottomY; + s16 magicCapacity; + s16 magicWidth; + s16 magicX; + s32 rightAligned; + + playerHeartRows[i] = Interface_GetHeartRowCountForPort(i + 1); + + if (i < 2) { + heartsTopY = topHeartsAnchorY; + heartsBottomY = heartsTopY + ((playerHeartRows[i] - 1) * 10.0f) + (16.0f * heartsScale); + playerIconY[i] = (s16)(heartsBottomY + sharedTopIconGap + 0.999f); + + if ((i < playerCount) && (Magic_GetLevelForPort(i + 1) != 0)) { + magicCapacity = Magic_GetMeterCapacityForPort(i + 1); + if (magicCapacity <= 0) { + magicCapacity = Magic_GetLevelForPort(i + 1) * MAGIC_NORMAL_METER; + } + + magicWidth = 16 + magicCapacity; + rightAligned = (i % 2) == 1; + magicX = rightAligned ? (playerSwordX[i] + iconSize - magicWidth) : playerSwordX[i]; + + if (i > 0) { + playerMagicX[i] = magicX; + playerMagicY[i] = (s16)(heartsBottomY + sharedMagicOffset); + playerIconY[i] = playerMagicY[i] + magicHeight + sharedMagicGap; + playerDrawMagic[i] = true; + } else { + /* Player 1 uses the base magic meter draw path; only push icon row below it here. */ + playerIconY[i] = (s16)(heartsBottomY + sharedMagicOffset) + magicHeight + sharedMagicGap; + } + } + } else { + heartsBottomY = bottomHeartsAnchorY + (8.0f * heartsScale); + heartsTopY = bottomHeartsAnchorY - ((playerHeartRows[i] - 1) * 10.0f) - (8.0f * heartsScale); + playerIconY[i] = (s16)(heartsTopY - sharedBottomIconGap - iconSize); + + if ((i < playerCount) && (Magic_GetLevelForPort(i + 1) != 0)) { + magicCapacity = Magic_GetMeterCapacityForPort(i + 1); + if (magicCapacity <= 0) { + magicCapacity = Magic_GetLevelForPort(i + 1) * MAGIC_NORMAL_METER; + } + + magicWidth = 16 + magicCapacity; + rightAligned = (i % 2) == 1; + magicX = rightAligned ? (playerSwordX[i] + iconSize - magicWidth) : playerSwordX[i]; + + playerMagicX[i] = magicX; + playerMagicY[i] = (s16)(heartsTopY - magicHeight + sharedMagicOffset); + playerIconY[i] = playerMagicY[i] - sharedMagicGap - iconSize; + playerDrawMagic[i] = true; + } + } + + if (sideBySideQuickSlotLayout) { + playerItemSlot2X[i] = playerItemX[i] + (((i % 2) == 1) ? -buttonStep : buttonStep); + playerItemSlot2Y[i] = playerIconY[i]; + } else { + playerItemSlot2X[i] = playerItemX[i]; + playerItemSlot2Y[i] = playerIconY[i] + buttonStep; + } + } + } + + if (CVarGetInteger(CVAR_COSMETIC("HUD.BButton.Changed"), 0)) { + swordButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.BButton.Value"), swordButtonColor); + } else if (CVarGetInteger(CVAR_COSMETIC("DefaultColorScheme"), COLORSCHEME_N64) == COLORSCHEME_GAMECUBE) { + swordButtonColor = (Color_RGB8){ 255, 30, 30 }; + } + + if (CVarGetInteger(CVAR_COSMETIC("HUD.CButtons.Changed"), 0)) { + itemButtonColor = CVarGetColor24(CVAR_COSMETIC("HUD.CButtons.Value"), itemButtonColor); } OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 255); - i = gSaveContext.equips.buttonItems[button]; + for (i = 0; i < playerCount; i++) { + s16 controllerPort = i + 1; - if (GameInteractor_Should(VB_DRAW_AMMO_COUNT, - ((i == ITEM_STICK) || (i == ITEM_NUT) || (i == ITEM_BOMB) || (i == ITEM_BOW) || - ((i >= ITEM_BOW_ARROW_FIRE) && (i <= ITEM_BOW_ARROW_LIGHT)) || (i == ITEM_SLINGSHOT) || - (i == ITEM_BOMBCHU) || (i == ITEM_BEAN)), - &i)) { - if ((i >= ITEM_BOW_ARROW_FIRE) && (i <= ITEM_BOW_ARROW_LIGHT)) { - i = ITEM_BOW; - } + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, swordButtonColor.r, swordButtonColor.g, swordButtonColor.b, + interfaceCtx->bAlpha); + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gButtonBackgroundTex, 32, 32, playerSwordX[i] + bgOffset, + playerIconY[i] + bgOffset, bgSize, bgSize, (32 << 10) / bgSize, + (32 << 10) / bgSize); - ammo = AMMO(i); + selectedSlot = secondSlotEnabled ? Interface_GetQuickItemSelectedSlotForPort(controllerPort) : 0; - gDPPipeSync(OVERLAY_DISP++); + if (selectedSlot == 0) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, selectedItemButtonColor.r, selectedItemButtonColor.g, + selectedItemButtonColor.b, Interface_GetQuickItemAlpha(interfaceCtx, controllerPort, 0)); + } else { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, itemButtonColor.r, itemButtonColor.g, itemButtonColor.b, + Interface_GetQuickItemAlpha(interfaceCtx, controllerPort, 0)); + } - if ((button == 0) && (gSaveContext.minigameState == 1)) { - ammo = play->interfaceCtx.hbaAmmo; - } else if ((button == 0) && (play->shootingGalleryStatus > 1)) { - ammo = play->shootingGalleryStatus - 1; - } else if ((button == 0) && (play->sceneNum == SCENE_BOMBCHU_BOWLING_ALLEY) && Flags_GetSwitch(play, 0x38)) { - ammo = play->bombchuBowlingStatus; - if (ammo < 0) { - ammo = 0; + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gButtonBackgroundTex, 32, 32, playerItemX[i] + bgOffset, + playerIconY[i] + bgOffset, bgSize, bgSize, (32 << 10) / bgSize, + (32 << 10) / bgSize); + + if (secondSlotEnabled) { + if (selectedSlot == 1) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, selectedItemButtonColor.r, selectedItemButtonColor.g, + selectedItemButtonColor.b, Interface_GetQuickItemAlpha(interfaceCtx, controllerPort, 1)); + } else { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, itemButtonColor.r, itemButtonColor.g, itemButtonColor.b, + Interface_GetQuickItemAlpha(interfaceCtx, controllerPort, 1)); } - } else if (((i == ITEM_BOW) && (AMMO(i) == CUR_CAPACITY(UPG_QUIVER))) || - ((i == ITEM_BOMB) && (AMMO(i) == CUR_CAPACITY(UPG_BOMB_BAG))) || - ((i == ITEM_SLINGSHOT) && (AMMO(i) == CUR_CAPACITY(UPG_BULLET_BAG))) || - ((i == ITEM_STICK) && (AMMO(i) == CUR_CAPACITY(UPG_STICKS))) || - ((i == ITEM_NUT) && (AMMO(i) == CUR_CAPACITY(UPG_NUTS))) || ((i == ITEM_BOMBCHU) && (ammo == 50)) || - ((i == ITEM_BEAN) && (ammo == 15)) || GameInteractor_Should(VB_COLOR_AMMO_GREEN, false, i)) { - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 120, 255, 0, alpha); - } - if (ammo == 0) { - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 100, 100, 100, alpha); + OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, gButtonBackgroundTex, 32, 32, + playerItemSlot2X[i] + bgOffset, playerItemSlot2Y[i] + bgOffset, bgSize, + bgSize, (32 << 10) / bgSize, (32 << 10) / bgSize); } + } + CLOSE_DISPS(play->state.gfxCtx); - for (i = 0; ammo >= 10; i++) { - ammo -= 10; + /* Ensure icons are drawn without being tinted by the button primitive color */ + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->bAlpha); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + CLOSE_DISPS(play->state.gfxCtx); + + for (i = 1; i < playerCount; i++) { + if (playerDrawMagic[i]) { + Interface_DrawMagicMeterForPortAt(play, i + 1, playerMagicX[i], playerMagicY[i]); } + } - if (i != 0) { - OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, (u8*)_gAmmoDigit0Tex[i], 8, 8, ItemIconPos[button][0], - ItemIconPos[button][1], 8, 8, 1 << 10, 1 << 10); + /* Magic meter drawing changes combine/env state; restore icon-friendly state before item icon draws. */ + OPEN_DISPS(play->state.gfxCtx); + gDPPipeSync(OVERLAY_DISP++); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->bAlpha); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 255); + CLOSE_DISPS(play->state.gfxCtx); + + if ((gSaveContext.equips.buttonItems[0] != ITEM_NONE) && (gSaveContext.equips.buttonItems[0] < ITEM_NONE_FE)) { + Interface_DrawItemIconTextureAt(play, gItemIcons[gSaveContext.equips.buttonItems[0]], playerSwordX[0], + playerIconY[0], 24, 24); + Interface_DrawAmmoCountForItemAt(play, gSaveContext.equips.buttonItems[0], interfaceCtx->bAlpha, + playerSwordX[0] + 1, playerIconY[0] + 13); + + Interface_DrawItemIconTextureAt(play, gItemIcons[gSaveContext.equips.buttonItems[0]], playerSwordX[1], + playerIconY[1], 24, 24); + Interface_DrawAmmoCountForItemAt(play, gSaveContext.equips.buttonItems[0], interfaceCtx->bAlpha, + playerSwordX[1] + 1, playerIconY[1] + 13); + + if (playerCount >= 3) { + Interface_DrawItemIconTextureAt(play, gItemIcons[gSaveContext.equips.buttonItems[0]], playerSwordX[2], + playerIconY[2], 24, 24); + Interface_DrawAmmoCountForItemAt(play, gSaveContext.equips.buttonItems[0], interfaceCtx->bAlpha, + playerSwordX[2] + 1, playerIconY[2] + 13); } - OVERLAY_DISP = Gfx_TextureIA8(OVERLAY_DISP, (u8*)_gAmmoDigit0Tex[ammo], 8, 8, ItemIconPos[button][0] + 6, - ItemIconPos[button][1], 8, 8, 1 << 10, 1 << 10); + if (playerCount >= 4) { + Interface_DrawItemIconTextureAt(play, gItemIcons[gSaveContext.equips.buttonItems[0]], playerSwordX[3], + playerIconY[3], 24, 24); + Interface_DrawAmmoCountForItemAt(play, gSaveContext.equips.buttonItems[0], interfaceCtx->bAlpha, + playerSwordX[3] + 1, playerIconY[3] + 13); + } } - CLOSE_DISPS(play->state.gfxCtx); + for (i = 0; i < playerCount; i++) { + s16 controllerPort = i + 1; + + Interface_DrawQuickItemHudAt(play, controllerPort, 0, playerItemX[i], playerIconY[i]); + + if (secondSlotEnabled) { + Interface_DrawQuickItemHudAt(play, controllerPort, 1, playerItemSlot2X[i], playerItemSlot2Y[i]); + } + } + Interface_DrawRadialQuickMenuHud(play, playerCount); } void Interface_DrawActionButton(PlayState* play, f32 x, f32 y) { @@ -5140,6 +7441,9 @@ void Interface_Draw(PlayState* play) { s16 svar5; s16 timerId; bool fullUi = !CVarGetInteger(CVAR_ENHANCEMENT("MinimalUI"), 0) || !R_MINIMAP_DISABLED || play->pauseCtx.state != 0; + s16 localMultiplayerPlayerCount = Interface_GetLocalMultiplayerPlayerCount(); + bool localMultiplayerEnabled = localMultiplayerPlayerCount > 1; + s32 originalQuickItemModeEnabled = Interface_IsOriginalMultiplayerItemModeEnabled(); // #region SOH [NTSC] s32 languageOffset = gSaveContext.language; @@ -5152,6 +7456,8 @@ void Interface_Draw(PlayState* play) { return; } + Interface_UpdateRadialQuickMenu(play); + OPEN_DISPS(play->state.gfxCtx); gSPSegment(OVERLAY_DISP++, 0x02, interfaceCtx->parameterSegment); @@ -5228,8 +7534,19 @@ void Interface_Draw(PlayState* play) { X_Margins_RC = 0; Y_Margins_RC = 0; } - s16 PosX_RC_ori = OTRGetRectDimensionFromLeftEdge(26 + X_Margins_RC); - s16 PosY_RC_ori = 206 + Y_Margins_RC; + s16 PosX_RC_ori = OTRGetRectDimensionFromLeftEdge(0 + X_Margins_RC); + s16 PosY_RC_ori = (SCREEN_HEIGHT - 16) + Y_Margins_RC; + + if (PosX_RC_ori < 0) { + PosX_RC_ori = 0; + } + + if (PosY_RC_ori < 0) { + PosY_RC_ori = 0; + } else if (PosY_RC_ori > (SCREEN_HEIGHT - 16)) { + PosY_RC_ori = SCREEN_HEIGHT - 16; + } + if (CVarGetInteger(CVAR_COSMETIC("HUD.Rupees.PosType"), 0) != ORIGINAL_LOCATION) { PosY_RC = CVarGetInteger(CVAR_COSMETIC("HUD.Rupees.PosY"), 0) + Y_Margins_RC; if (CVarGetInteger(CVAR_COSMETIC("HUD.Rupees.PosType"), 0) == ANCHOR_LEFT) { @@ -5412,6 +7729,15 @@ void Interface_Draw(PlayState* play) { Minimap_Draw(play); if ((R_PAUSE_MENU_MODE != 2) && (R_PAUSE_MENU_MODE != 3)) { + s32 useSplitTargetViewport = false; + + if (localMultiplayerEnabled && Interface_IsSplitScreenEnabled()) { + Interface_GetSplitViewportBounds(localMultiplayerPlayerCount, 0, &interfaceCtx->viewport); + View_SetViewport(&interfaceCtx->view, &interfaceCtx->viewport); + func_800AB2C4(&interfaceCtx->view); + useSplitTargetViewport = true; + } + if (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0)) { gSPMatrix(OVERLAY_DISP++, interfaceCtx->view.projectionFlippedPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); @@ -5426,6 +7752,10 @@ void Interface_Draw(PlayState* play) { if (CVarGetInteger(CVAR_ENHANCEMENT("EnemyHealthBar"), 0)) { Interface_DrawEnemyHealthBar(&play->actorCtx.targetCtx, play); } + + if (useSplitTargetViewport) { + func_8008A994(interfaceCtx); + } } Gfx_SetupDL_39Overlay(play->state.gfxCtx); @@ -5438,7 +7768,8 @@ void Interface_Draw(PlayState* play) { gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->bAlpha); gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); - if (!(interfaceCtx->unk_1FA)) { + if (!originalQuickItemModeEnabled) { + if (!(interfaceCtx->unk_1FA)) { // B Button Icon & Ammo Count if (gSaveContext.equips.buttonItems[0] != ITEM_NONE) { if (fullUi) { @@ -5458,7 +7789,7 @@ void Interface_Draw(PlayState* play) { Interface_DrawAmmoCount(play, 0, interfaceCtx->bAlpha); } } - } else { + } else { // B Button Do Action Label s16 PosX_adjust; s16 PosY_adjust; @@ -5522,52 +7853,61 @@ void Interface_Draw(PlayState* play) { gSPWideTextureRectangle(OVERLAY_DISP++, BbtnPosX << 2, BbtnPosY << 2, (BbtnPosX + DO_ACTION_TEX_WIDTH()) << 2, (BbtnPosY + DO_ACTION_TEX_HEIGHT()) << 2, G_TX_RENDERTILE, 0, 0, R_B_LABEL_DD, R_B_LABEL_DD); + } } - gDPPipeSync(OVERLAY_DISP++); + if (fullUi && originalQuickItemModeEnabled) { + Interface_DrawOriginalMultiplayerItemHud(play); + } - // C-Left Button Icon & Ammo Count - if (gSaveContext.equips.buttonItems[1] < 0xF0) { - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cLeftAlpha); - gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); - Interface_DrawItemIconTexture(play, gItemIcons[gSaveContext.equips.buttonItems[1]], 1); + if (!originalQuickItemModeEnabled) { gDPPipeSync(OVERLAY_DISP++); - gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, - PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); - Interface_DrawAmmoCount(play, 1, interfaceCtx->cLeftAlpha); - } - gDPPipeSync(OVERLAY_DISP++); + // C-Left Button Icon & Ammo Count + if (gSaveContext.equips.buttonItems[1] < 0xF0) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cLeftAlpha); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + Interface_DrawItemIconTexture(play, gItemIcons[gSaveContext.equips.buttonItems[1]], 1); + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, + PRIMITIVE, 0, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, + 0); + Interface_DrawAmmoCount(play, 1, interfaceCtx->cLeftAlpha); + } - // C-Down Button Icon & Ammo Count - if (gSaveContext.equips.buttonItems[2] < 0xF0) { - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cDownAlpha); - gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); - Interface_DrawItemIconTexture(play, gItemIcons[gSaveContext.equips.buttonItems[2]], 2); gDPPipeSync(OVERLAY_DISP++); - gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, - PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); - Interface_DrawAmmoCount(play, 2, interfaceCtx->cDownAlpha); - } - gDPPipeSync(OVERLAY_DISP++); + // C-Down Button Icon & Ammo Count + if (gSaveContext.equips.buttonItems[2] < 0xF0) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cDownAlpha); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + Interface_DrawItemIconTexture(play, gItemIcons[gSaveContext.equips.buttonItems[2]], 2); + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, + PRIMITIVE, 0, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, + 0); + Interface_DrawAmmoCount(play, 2, interfaceCtx->cDownAlpha); + } - // C-Right Button Icon & Ammo Count - if (gSaveContext.equips.buttonItems[3] < 0xF0) { - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cRightAlpha); - gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); - Interface_DrawItemIconTexture(play, gItemIcons[gSaveContext.equips.buttonItems[3]], 3); gDPPipeSync(OVERLAY_DISP++); - gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, - PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); - Interface_DrawAmmoCount(play, 3, interfaceCtx->cRightAlpha); - } - if (CVarGetInteger(CVAR_ENHANCEMENT("DpadEquips"), 0) != 0) { - // DPad is only greyed-out when all 4 DPad directions are too - uint16_t dpadAlpha = - MAX(MAX(MAX(interfaceCtx->dpadUpAlpha, interfaceCtx->dpadDownAlpha), interfaceCtx->dpadLeftAlpha), - interfaceCtx->dpadRightAlpha); + // C-Right Button Icon & Ammo Count + if (gSaveContext.equips.buttonItems[3] < 0xF0) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->cRightAlpha); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + Interface_DrawItemIconTexture(play, gItemIcons[gSaveContext.equips.buttonItems[3]], 3); + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, + PRIMITIVE, 0, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, + 0); + Interface_DrawAmmoCount(play, 3, interfaceCtx->cRightAlpha); + } + + if (CVarGetInteger(CVAR_ENHANCEMENT("DpadEquips"), 0) != 0) { + // DPad is only greyed-out when all 4 DPad directions are too + uint16_t dpadAlpha = + MAX(MAX(MAX(interfaceCtx->dpadUpAlpha, interfaceCtx->dpadDownAlpha), interfaceCtx->dpadLeftAlpha), + interfaceCtx->dpadRightAlpha); // Draw DPad s16 DpadPosX; @@ -5651,94 +7991,98 @@ void Interface_Draw(PlayState* play) { Interface_DrawAmmoCount(play, 6, interfaceCtx->dpadLeftAlpha); } - // DPad-Right Button Icon & Ammo Count - if (gSaveContext.equips.buttonItems[7] < 0xF0) { - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->dpadRightAlpha); - gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); - Interface_DrawItemIconTexture(play, gItemIcons[gSaveContext.equips.buttonItems[7]], 7); - gDPPipeSync(OVERLAY_DISP++); - gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, - PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); - Interface_DrawAmmoCount(play, 7, interfaceCtx->dpadRightAlpha); + // DPad-Right Button Icon & Ammo Count + if (gSaveContext.equips.buttonItems[7] < 0xF0) { + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->dpadRightAlpha); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATERGBA_PRIM, G_CC_MODULATERGBA_PRIM); + Interface_DrawItemIconTexture(play, gItemIcons[gSaveContext.equips.buttonItems[7]], 7); + gDPPipeSync(OVERLAY_DISP++); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, + PRIMITIVE, 0, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, + PRIMITIVE, 0); + Interface_DrawAmmoCount(play, 7, interfaceCtx->dpadRightAlpha); + } } } - // A Button - Gfx_SetupDL_42Overlay(play->state.gfxCtx); - s16 X_Margins_BtnA; - s16 Y_Margins_BtnA; - if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.UseMargins"), 0) != 0) { - X_Margins_BtnA = Right_HUD_Margin; - Y_Margins_BtnA = (Top_HUD_Margin * -1); - } else { - X_Margins_BtnA = 0; - Y_Margins_BtnA = 0; - } - s16 PosX_BtnA_ori = OTRGetDimensionFromRightEdge(R_A_BTN_X + X_Margins_BtnA); - s16 PosY_BtnA_ori = R_A_BTN_Y + Y_Margins_BtnA; - const f32 rAIconX_ori = OTRGetDimensionFromRightEdge(R_A_ICON_X + X_Margins_BtnA); - const f32 rAIconY_ori = 98.0f - (R_A_ICON_Y + Y_Margins_BtnA); - s16 PosX_BtnA; - s16 PosY_BtnA; - s16 rAIconX; - s16 rAIconY; - if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosType"), 0) != ORIGINAL_LOCATION) { - PosY_BtnA = CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosY"), 0) + Y_Margins_BtnA; - rAIconY = 98.0f - PosY_BtnA; - if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosType"), 0) == ANCHOR_LEFT) { - if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.UseMargins"), 0) != 0) { - X_Margins_BtnA = Left_HUD_Margin; - }; - PosX_BtnA = - OTRGetDimensionFromLeftEdge(CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosX"), 0) + X_Margins_BtnA); - rAIconX = - OTRGetDimensionFromLeftEdge(CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosX"), 0) + X_Margins_BtnA); - } else if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosType"), 0) == ANCHOR_RIGHT) { - if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.UseMargins"), 0) != 0) { - X_Margins_BtnA = Right_HUD_Margin; - }; - PosX_BtnA = - OTRGetDimensionFromRightEdge(CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosX"), 0) + X_Margins_BtnA); - rAIconX = - OTRGetDimensionFromRightEdge(CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosX"), 0) + X_Margins_BtnA); - } else if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosType"), 0) == ANCHOR_NONE) { - PosX_BtnA = CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosX"), 0); - rAIconX = CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosX"), 0); - } else if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosType"), 0) == HIDDEN) { - PosX_BtnA = -9999; - rAIconX = -9999; + if (!localMultiplayerEnabled) { + // A Button + Gfx_SetupDL_42Overlay(play->state.gfxCtx); + s16 X_Margins_BtnA; + s16 Y_Margins_BtnA; + if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.UseMargins"), 0) != 0) { + X_Margins_BtnA = Right_HUD_Margin; + Y_Margins_BtnA = (Top_HUD_Margin * -1); + } else { + X_Margins_BtnA = 0; + Y_Margins_BtnA = 0; + } + s16 PosX_BtnA_ori = OTRGetDimensionFromRightEdge(R_A_BTN_X + X_Margins_BtnA); + s16 PosY_BtnA_ori = R_A_BTN_Y + Y_Margins_BtnA; + const f32 rAIconX_ori = OTRGetDimensionFromRightEdge(R_A_ICON_X + X_Margins_BtnA); + const f32 rAIconY_ori = 98.0f - (R_A_ICON_Y + Y_Margins_BtnA); + s16 PosX_BtnA; + s16 PosY_BtnA; + s16 rAIconX; + s16 rAIconY; + if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosType"), 0) != ORIGINAL_LOCATION) { + PosY_BtnA = CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosY"), 0) + Y_Margins_BtnA; + rAIconY = 98.0f - PosY_BtnA; + if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosType"), 0) == ANCHOR_LEFT) { + if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.UseMargins"), 0) != 0) { + X_Margins_BtnA = Left_HUD_Margin; + }; + PosX_BtnA = OTRGetDimensionFromLeftEdge(CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosX"), 0) + + X_Margins_BtnA); + rAIconX = OTRGetDimensionFromLeftEdge(CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosX"), 0) + + X_Margins_BtnA); + } else if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosType"), 0) == ANCHOR_RIGHT) { + if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.UseMargins"), 0) != 0) { + X_Margins_BtnA = Right_HUD_Margin; + }; + PosX_BtnA = OTRGetDimensionFromRightEdge(CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosX"), 0) + + X_Margins_BtnA); + rAIconX = OTRGetDimensionFromRightEdge(CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosX"), 0) + + X_Margins_BtnA); + } else if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosType"), 0) == ANCHOR_NONE) { + PosX_BtnA = CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosX"), 0); + rAIconX = CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosX"), 0); + } else if (CVarGetInteger(CVAR_COSMETIC("HUD.AButton.PosType"), 0) == HIDDEN) { + PosX_BtnA = -9999; + rAIconX = -9999; + } + } else { + PosY_BtnA = PosY_BtnA_ori; + PosX_BtnA = PosX_BtnA_ori; + rAIconY = rAIconY_ori; + rAIconX = rAIconX_ori; } - } else { - PosY_BtnA = PosY_BtnA_ori; - PosX_BtnA = PosX_BtnA_ori; - rAIconY = rAIconY_ori; - rAIconX = rAIconX_ori; - } - gSPClearGeometryMode(OVERLAY_DISP++, G_CULL_BOTH); - gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, aButtonColor.r, aButtonColor.g, aButtonColor.b, interfaceCtx->aAlpha); - if (fullUi) { - Interface_DrawActionButton(play, PosX_BtnA, PosY_BtnA); - } - gDPPipeSync(OVERLAY_DISP++); - gSPSetGeometryMode(OVERLAY_DISP++, G_CULL_BACK); - gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, - PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); - gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->aAlpha); - gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 0); - Matrix_Translate(-138.0f + rAIconX, rAIconY, WREG(46 + languageOffset) / 10.0f, MTXMODE_NEW); - Matrix_Scale(1.0f, 1.0f, 1.0f, MTXMODE_APPLY); - Matrix_RotateX(interfaceCtx->unk_1F4 / 10000.0f, MTXMODE_APPLY); - gSPMatrix(OVERLAY_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_MODELVIEW | G_MTX_LOAD); - gSPVertex(OVERLAY_DISP++, &interfaceCtx->actionVtx[4], 4, 0); - - if ((interfaceCtx->unk_1EC < 2) || (interfaceCtx->unk_1EC == 3)) { - Interface_DrawActionLabel(play->state.gfxCtx, interfaceCtx->doActionSegment[0]); - } else { - Interface_DrawActionLabel(play->state.gfxCtx, interfaceCtx->doActionSegment[1]); - } + gSPClearGeometryMode(OVERLAY_DISP++, G_CULL_BOTH); + gDPSetCombineMode(OVERLAY_DISP++, G_CC_MODULATEIA_PRIM, G_CC_MODULATEIA_PRIM); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, aButtonColor.r, aButtonColor.g, aButtonColor.b, interfaceCtx->aAlpha); + if (fullUi) { + Interface_DrawActionButton(play, PosX_BtnA, PosY_BtnA); + } + gDPPipeSync(OVERLAY_DISP++); + gSPSetGeometryMode(OVERLAY_DISP++, G_CULL_BACK); + gDPSetCombineLERP(OVERLAY_DISP++, PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0, + PRIMITIVE, ENVIRONMENT, TEXEL0, ENVIRONMENT, TEXEL0, 0, PRIMITIVE, 0); + gDPSetPrimColor(OVERLAY_DISP++, 0, 0, 255, 255, 255, interfaceCtx->aAlpha); + gDPSetEnvColor(OVERLAY_DISP++, 0, 0, 0, 0); + Matrix_Translate(-138.0f + rAIconX, rAIconY, WREG(46 + languageOffset) / 10.0f, MTXMODE_NEW); + Matrix_Scale(1.0f, 1.0f, 1.0f, MTXMODE_APPLY); + Matrix_RotateX(interfaceCtx->unk_1F4 / 10000.0f, MTXMODE_APPLY); + gSPMatrix(OVERLAY_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_MODELVIEW | G_MTX_LOAD); + gSPVertex(OVERLAY_DISP++, &interfaceCtx->actionVtx[4], 4, 0); - gDPPipeSync(OVERLAY_DISP++); + if ((interfaceCtx->unk_1EC < 2) || (interfaceCtx->unk_1EC == 3)) { + Interface_DrawActionLabel(play->state.gfxCtx, interfaceCtx->doActionSegment[0]); + } else { + Interface_DrawActionLabel(play->state.gfxCtx, interfaceCtx->doActionSegment[1]); + } + + gDPPipeSync(OVERLAY_DISP++); + } func_8008A994(interfaceCtx); svar3 = 16; @@ -6044,7 +8388,7 @@ void Interface_Draw(PlayState* play) { gSaveContext.timerState = TIMER_STATE_STOP; if (sEnvHazardActive) { gSaveContext.health = 0; - play->damagePlayer(play, -(gSaveContext.health + 2)); + play->damagePlayer(play, GET_PLAYER(play), -(gSaveContext.health + 2)); } sEnvHazardActive = false; } else if (gSaveContext.timerSeconds > 60) { @@ -6510,23 +8854,30 @@ void Interface_Update(PlayState* play) { bool isPal = ResourceMgr_GetGameRegion(0) == GAME_REGION_PAL; - if (CHECK_BTN_ALL(debugInput->press.button, BTN_DLEFT)) { - gSaveContext.language = LANGUAGE_ENG; - CVarSetInteger(CVAR_SETTING("Languages"), LANGUAGE_ENG); - osSyncPrintf("J_N=%x J_N=%x\n", gSaveContext.language, &gSaveContext.language); - } else if (CHECK_BTN_ALL(debugInput->press.button, BTN_DUP) && sGerMessageEntryTablePtr != NULL) { - gSaveContext.language = LANGUAGE_GER; - CVarSetInteger(CVAR_SETTING("Languages"), LANGUAGE_GER); - osSyncPrintf("J_N=%x J_N=%x\n", gSaveContext.language, &gSaveContext.language); - } else if (CHECK_BTN_ALL(debugInput->press.button, BTN_DRIGHT) && sFraMessageEntryTablePtr != NULL) { - gSaveContext.language = LANGUAGE_FRA; - CVarSetInteger(CVAR_SETTING("Languages"), LANGUAGE_FRA); - osSyncPrintf("J_N=%x J_N=%x\n", gSaveContext.language, &gSaveContext.language); - } else if (CHECK_BTN_ALL(debugInput->press.button, BTN_DDOWN) && sJpnMessageEntryTablePtr != NULL) { - // Add this in to have an equivalent ntsc language debugging feature - gSaveContext.language = LANGUAGE_JPN; - CVarSetInteger(CVAR_SETTING("Languages"), LANGUAGE_JPN); - osSyncPrintf("J_N=%x J_N=%x\n", gSaveContext.language, &gSaveContext.language); + if ((CVarGetInteger(CVAR_ENHANCEMENT("LocalMultiplayer.Disable"), 0) ? 1 + : CVarGetInteger( + CVAR_ENHANCEMENT( + "LocalMultiplayer.PlayerCount"), + 2)) < + 3) { + if (CHECK_BTN_ALL(debugInput->press.button, BTN_DLEFT)) { + gSaveContext.language = LANGUAGE_ENG; + CVarSetInteger(CVAR_SETTING("Languages"), LANGUAGE_ENG); + osSyncPrintf("J_N=%x J_N=%x\n", gSaveContext.language, &gSaveContext.language); + } else if (CHECK_BTN_ALL(debugInput->press.button, BTN_DUP) && sGerMessageEntryTablePtr != NULL) { + gSaveContext.language = LANGUAGE_GER; + CVarSetInteger(CVAR_SETTING("Languages"), LANGUAGE_GER); + osSyncPrintf("J_N=%x J_N=%x\n", gSaveContext.language, &gSaveContext.language); + } else if (CHECK_BTN_ALL(debugInput->press.button, BTN_DRIGHT) && sFraMessageEntryTablePtr != NULL) { + gSaveContext.language = LANGUAGE_FRA; + CVarSetInteger(CVAR_SETTING("Languages"), LANGUAGE_FRA); + osSyncPrintf("J_N=%x J_N=%x\n", gSaveContext.language, &gSaveContext.language); + } else if (CHECK_BTN_ALL(debugInput->press.button, BTN_DDOWN) && sJpnMessageEntryTablePtr != NULL) { + // Add this in to have an equivalent ntsc language debugging feature + gSaveContext.language = LANGUAGE_JPN; + CVarSetInteger(CVAR_SETTING("Languages"), LANGUAGE_JPN); + osSyncPrintf("J_N=%x J_N=%x\n", gSaveContext.language, &gSaveContext.language); + } } if ((play->pauseCtx.state == 0) && (play->pauseCtx.debugState == 0)) { @@ -6796,6 +9147,26 @@ void Interface_Update(PlayState* play) { } Interface_UpdateMagicBar(play); + + if (Interface_GetLocalMultiplayerPlayerCount() > 1) { + s16 localMpCount = Interface_GetLocalMultiplayerPlayerCount(); + + for (u8 controllerPort = 2; controllerPort <= localMpCount; controllerPort++) { + s16* magicState = Magic_GetSecondaryStatePtr(controllerPort); + s16* magicCapacity = Magic_GetSecondaryCapacityPtr(controllerPort); + + Magic_InitForPort(controllerPort); + + if ((Magic_GetLevelForPort(controllerPort) != 0) && (*magicCapacity == 0) && + (*magicState == MAGIC_STATE_IDLE)) { + *magicState = MAGIC_STATE_STEP_CAPACITY; + } + + if ((Magic_GetLevelForPort(controllerPort) != 0) || (*magicState != MAGIC_STATE_IDLE)) { + Magic_UpdateSecondary(play, controllerPort); + } + } + } } if (gSaveContext.timerState == TIMER_STATE_OFF) { diff --git a/soh/src/code/z_play.c b/soh/src/code/z_play.c index f73d076de42..fb5a511eef7 100644 --- a/soh/src/code/z_play.c +++ b/soh/src/code/z_play.c @@ -38,6 +38,1245 @@ s16 firstInit = 0; s16 gEnPartnerId; void Play_SpawnScene(PlayState* play, s32 sceneId, s32 spawn); +void Actor_Draw(PlayState* play, Actor* actor); + +#define LOCAL_MP_PLAYER_COUNT_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount") +#define LOCAL_MP_DEBUG_MARKERS_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.DebugMarkers") +#define LOCAL_MP_DISABLED_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Disable") +#define LOCAL_MP_SPLITSCREEN_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreen") +#define LOCAL_MP_SPLITSCREEN_VERTICAL_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreenVertical") +#define LOCAL_MP_SPLITSCREEN_PERF_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreenPerformanceMode") +#define LOCAL_MP_SPLITSCREEN_AGGRESSIVE_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.SplitScreenAggressiveMode") +#define LOCAL_MP_TELEPORT_TARGET_PORT_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.TeleportTargetPlayer") +#define LOCAL_MP_TELEPORT_REQUEST_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.TeleportAllToSelectedRequest") + +typedef struct LocalMPCameraState { + Vec3f midpoint; + f32 maxRadius; + f32 heightSpan; + f32 forwardX; + f32 forwardZ; + s32 playerCount; + u8 hasForward; + u8 initialized; + Vec3f smoothedAt; + f32 smoothedDist; + f32 smoothedEyeLift; + s16 smoothedYaw; +} LocalMPCameraState; + +static LocalMPCameraState sLocalMPCameraState; +static s16 sLocalMPSplitCamIds[3] = { SUBCAM_NONE, SUBCAM_NONE, SUBCAM_NONE }; +static u16 sMergedStartPrevCur = 0; +static s32 sLocalMPLastTeleportRequest = 0; + +static void LocalMP_DrawDebugMarkers(PlayState* play); + +static s32 LocalMP_GetDesiredPlayerCount(void) { + if (CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0)) { + return 1; + } + + s32 desiredPlayers = CVarGetInteger(LOCAL_MP_PLAYER_COUNT_CVAR, 2); + + if (desiredPlayers < 1) { + desiredPlayers = 1; + } else if (desiredPlayers > 4) { + desiredPlayers = 4; + } + + return desiredPlayers; +} + +static Player* LocalMP_FindPlayerByPort(PlayState* play, s32 controllerPort) { + Actor* actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* player = (Player*)actor; + + if (player->controllerPort == controllerPort) { + return player; + } + } + + actor = actor->next; + } + + return NULL; +} + +static void LocalMP_HandleTeleportAllToSelected(PlayState* play) { + s32 request = CVarGetInteger(LOCAL_MP_TELEPORT_REQUEST_CVAR, 0); + s32 targetPort; + Player* targetPlayer; + Vec3f targetPos; + s16 targetYaw; + s32 targetRoom; + Actor* actor; + + if (request == sLocalMPLastTeleportRequest) { + return; + } + + if ((play->transitionTrigger != TRANS_TRIGGER_OFF) || (play->roomCtx.status != 0) || Play_InCsMode(play) || + (play->pauseCtx.state != 0) || (play->pauseCtx.debugState != 0)) { + return; + } + + if (LocalMP_GetDesiredPlayerCount() <= 1) { + return; + } + + targetPort = CVarGetInteger(LOCAL_MP_TELEPORT_TARGET_PORT_CVAR, 1); + + if (targetPort < 1) { + targetPort = 1; + } else if (targetPort > 4) { + targetPort = 4; + } + + targetPlayer = LocalMP_FindPlayerByPort(play, targetPort); + if ((targetPlayer == NULL) || (targetPlayer->actor.update == NULL)) { + targetPlayer = LocalMP_FindPlayerByPort(play, 1); + } + + if ((targetPlayer == NULL) || (targetPlayer->actor.update == NULL)) { + return; + } + + targetPos = targetPlayer->actor.world.pos; + targetYaw = targetPlayer->actor.shape.rot.y; + targetRoom = targetPlayer->actor.room; + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* player = (Player*)actor; + + actor->world.pos = targetPos; + actor->home.pos = targetPos; + actor->prevPos = targetPos; + actor->shape.rot.y = targetYaw; + actor->world.rot.y = targetYaw; + actor->velocity.x = 0.0f; + actor->velocity.y = 0.0f; + actor->velocity.z = 0.0f; + actor->speedXZ = 0.0f; + actor->room = targetRoom; + player->yaw = targetYaw; + player->linearVelocity = 0.0f; + player->pushedSpeed = 0.0f; + player->fallStartHeight = targetPos.y; + } + + actor = actor->next; + } + + sLocalMPLastTeleportRequest = request; +} + +static void LocalMP_ReorderPlayerList(PlayState* play, Player* mainPlayer) { + Actor* head; + Actor* prev; + Actor* next; + + if (mainPlayer == NULL) { + return; + } + + head = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + + if ((head == NULL) || (head == &mainPlayer->actor)) { + return; + } + + prev = mainPlayer->actor.prev; + next = mainPlayer->actor.next; + + if (prev != NULL) { + prev->next = next; + } + + if (next != NULL) { + next->prev = prev; + } + + mainPlayer->actor.prev = NULL; + mainPlayer->actor.next = head; + head->prev = &mainPlayer->actor; + play->actorCtx.actorLists[ACTORCAT_PLAYER].head = &mainPlayer->actor; +} + +static Player* LocalMP_SpawnPlayer(PlayState* play, Player* mainPlayer, s32 controllerPort) { + static Vec3f sSpawnOffsets[] = { + { 40.0f, 0.0f, 0.0f }, + { -40.0f, 0.0f, 40.0f }, + { 0.0f, 0.0f, -40.0f }, + }; + Vec3f spawnOffset; + Actor* actor; + Player* player; + + if ((mainPlayer == NULL) || (controllerPort < 2) || (controllerPort > 4)) { + return NULL; + } + + spawnOffset = sSpawnOffsets[controllerPort - 2]; + + actor = Actor_Spawn(&play->actorCtx, play, ACTOR_PLAYER, mainPlayer->actor.world.pos.x + spawnOffset.x, + mainPlayer->actor.world.pos.y + spawnOffset.y, mainPlayer->actor.world.pos.z + spawnOffset.z, + mainPlayer->actor.shape.rot.x, mainPlayer->actor.shape.rot.y, mainPlayer->actor.shape.rot.z, + mainPlayer->actor.params | 0x8000); + + if (actor == NULL) { + return NULL; + } + + player = (Player*)actor; + player->controllerPort = controllerPort; + player->inputEnabled = true; + player->isSecondPlayer = true; + player->linkedPlayer = mainPlayer; + Magic_InitForPort(controllerPort); + player->actor.room = mainPlayer->actor.room; + player->yaw = mainPlayer->yaw; + + if (controllerPort == 2) { + player->currentTunic = PLAYER_TUNIC_GORON; + } else if (controllerPort == 3) { + player->currentTunic = PLAYER_TUNIC_ZORA; + } else { + player->currentTunic = PLAYER_TUNIC_KOKIRI; + } + + return player; +} + +static void LocalMP_EnsurePlayers(PlayState* play) { + Player* mainPlayer; + bool seenPorts[5] = { false, false, false, false, false }; + Actor* actor; + s32 desiredPlayers; + s32 controllerPort; + + desiredPlayers = LocalMP_GetDesiredPlayerCount(); + + // Keep the player list stable while rooms/transitions are in-flight. + if ((play->transitionTrigger != TRANS_TRIGGER_OFF) || (play->roomCtx.status != 0) || Play_InCsMode(play)) { + return; + } + + if (desiredPlayers <= 1) { + Actor* nextActor; + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + nextActor = actor->next; + + if ((actor->id == ACTOR_PLAYER) && (actor != play->actorCtx.actorLists[ACTORCAT_PLAYER].head)) { + Player* player = (Player*)actor; + if ((player->controllerPort >= 2) && (player->controllerPort <= 4)) { + Actor_Kill(actor); + } + } + + actor = nextActor; + } + + sLocalMPCameraState.playerCount = 1; + sLocalMPCameraState.initialized = false; + return; + } + + mainPlayer = LocalMP_FindPlayerByPort(play, 1); + if (mainPlayer == NULL) { + mainPlayer = GET_PLAYER(play); + } + + if (mainPlayer == NULL) { + return; + } + + mainPlayer->controllerPort = 1; + mainPlayer->inputEnabled = true; + mainPlayer->isSecondPlayer = false; + mainPlayer->linkedPlayer = NULL; + seenPorts[1] = true; + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + Actor* nextActor = actor->next; + + if ((actor->id == ACTOR_PLAYER) && (actor != &mainPlayer->actor)) { + Player* player = (Player*)actor; + controllerPort = player->controllerPort; + + if ((controllerPort < 2) || (controllerPort > desiredPlayers) || (controllerPort > 4) || + seenPorts[controllerPort]) { + Actor_Kill(actor); + } else { + seenPorts[controllerPort] = true; + player->isSecondPlayer = true; + player->inputEnabled = true; + player->linkedPlayer = mainPlayer; + player->actor.room = mainPlayer->actor.room; + } + } + + actor = nextActor; + } + + for (controllerPort = 2; controllerPort <= desiredPlayers; controllerPort++) { + if (!seenPorts[controllerPort]) { + Player* spawned = LocalMP_SpawnPlayer(play, mainPlayer, controllerPort); + + if (spawned != NULL) { + seenPorts[controllerPort] = true; + } + } + } + + LocalMP_ReorderPlayerList(play, mainPlayer); +} + +static s32 LocalMP_UpdateMidpointState(PlayState* play) { + Vec3f sum = { 0.0f, 0.0f, 0.0f }; + Actor* actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + f32 forwardX = 0.0f; + f32 forwardZ = 0.0f; + f32 forwardMag; + f32 minY = 32767.0f; + f32 maxY = -32767.0f; + s32 count = 0; + + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* player = (Player*)actor; + + if ((player->controllerPort >= 1) && (player->controllerPort <= 4)) { + sum.x += actor->world.pos.x; + sum.y += actor->world.pos.y; + sum.z += actor->world.pos.z; + forwardX += Math_SinS(actor->shape.rot.y); + forwardZ += Math_CosS(actor->shape.rot.y); + + if (actor->world.pos.y < minY) { + minY = actor->world.pos.y; + } + if (actor->world.pos.y > maxY) { + maxY = actor->world.pos.y; + } + + count++; + } + } + + actor = actor->next; + } + + sLocalMPCameraState.playerCount = count; + + if (count < 2) { + sLocalMPCameraState.maxRadius = 0.0f; + sLocalMPCameraState.heightSpan = 0.0f; + sLocalMPCameraState.hasForward = false; + return false; + } + + sLocalMPCameraState.midpoint.x = sum.x / count; + sLocalMPCameraState.midpoint.y = sum.y / count; + sLocalMPCameraState.midpoint.z = sum.z / count; + sLocalMPCameraState.maxRadius = 0.0f; + sLocalMPCameraState.heightSpan = maxY - minY; + forwardMag = sqrtf(SQ(forwardX) + SQ(forwardZ)); + + if (forwardMag > 0.1f) { + sLocalMPCameraState.forwardX = forwardX / forwardMag; + sLocalMPCameraState.forwardZ = forwardZ / forwardMag; + sLocalMPCameraState.hasForward = true; + } else { + sLocalMPCameraState.forwardX = 0.0f; + sLocalMPCameraState.forwardZ = 0.0f; + sLocalMPCameraState.hasForward = false; + } + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* player = (Player*)actor; + + if ((player->controllerPort >= 1) && (player->controllerPort <= 4)) { + f32 dx = actor->world.pos.x - sLocalMPCameraState.midpoint.x; + f32 dz = actor->world.pos.z - sLocalMPCameraState.midpoint.z; + f32 radius = sqrtf(SQ(dx) + SQ(dz)); + + if (radius > sLocalMPCameraState.maxRadius) { + sLocalMPCameraState.maxRadius = radius; + } + } + } + + actor = actor->next; + } + + return true; +} + +static bool LocalMP_IsSplitScreenEnabled(void) { + return !CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0) && CVarGetInteger(LOCAL_MP_SPLITSCREEN_CVAR, 1); +} + +static bool LocalMP_IsVerticalSplitScreenEnabled(void) { + return LocalMP_IsSplitScreenEnabled() && CVarGetInteger(LOCAL_MP_SPLITSCREEN_VERTICAL_CVAR, 1); +} + +static bool LocalMP_IsSplitScreenPerformanceModeEnabled(void) { + return LocalMP_IsSplitScreenEnabled() && CVarGetInteger(LOCAL_MP_SPLITSCREEN_PERF_CVAR, 1); +} + +static bool LocalMP_IsSplitScreenAggressiveModeEnabled(void) { + return LocalMP_IsSplitScreenPerformanceModeEnabled() && CVarGetInteger(LOCAL_MP_SPLITSCREEN_AGGRESSIVE_CVAR, 0); +} + +static s32 LocalMP_CollectPlayersByPort(PlayState* play, Player* players[4]) { + Player* portPlayers[5] = { NULL, NULL, NULL, NULL, NULL }; + Actor* actor; + s32 port; + s32 count = 0; + + memset(players, 0, sizeof(Player*) * 4); + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* player = (Player*)actor; + port = player->controllerPort; + + if ((port >= 1) && (port <= 4) && (portPlayers[port] == NULL)) { + portPlayers[port] = player; + } + } + + actor = actor->next; + } + + if ((portPlayers[1] == NULL) && (GET_PLAYER(play) != NULL) && (GET_PLAYER(play)->actor.update != NULL)) { + portPlayers[1] = GET_PLAYER(play); + } + + for (port = 1; port <= 4; port++) { + if (portPlayers[port] != NULL) { + players[count++] = portPlayers[port]; + } + } + + return count; +} + +static void LocalMP_ClearSplitScreenCameras(PlayState* play) { + s32 i; + + for (i = 0; i < ARRAY_COUNT(sLocalMPSplitCamIds); i++) { + s16 camId = sLocalMPSplitCamIds[i]; + + if ((camId >= SUBCAM_FIRST) && (camId < NUM_CAMS) && (play->cameraPtrs[camId] != NULL)) { + Play_ClearCamera(play, camId); + } + + sLocalMPSplitCamIds[i] = SUBCAM_NONE; + } +} + +// Returns true for camera settings driven by P1's item/animation state that should not be forced +// onto split-screen cameras (those cameras track their own players independently). +static bool LocalMP_IsCameraSettingPlayerAnimation(s16 setting) { + return (setting == CAM_SET_TURN_AROUND) || // bottle use, first-person item animations + (setting == CAM_SET_SLOW_CHEST_CS); // big-chest opening cutscene +} + +static void LocalMP_SyncSplitCamera(Camera* splitCam, Camera* mainCam, Player* player) { + if ((splitCam == NULL) || (mainCam == NULL) || (player == NULL)) { + return; + } + + if (splitCam->player != player) { + Camera_InitPlayerSettings(splitCam, player); + } + + if ((mainCam->camDataIdx >= 0) && (splitCam->camDataIdx != mainCam->camDataIdx)) { + Camera_ChangeDataIdx(splitCam, mainCam->camDataIdx); + } else if ((splitCam->setting != mainCam->setting) && + !LocalMP_IsCameraSettingPlayerAnimation(mainCam->setting)) { + // Don't propagate player-item-animation camera modes; each player's camera should stay + // in its own movement mode while another player is using an item. + Camera_ChangeSetting(splitCam, mainCam->setting); + } +} + +static void LocalMP_GetSplitViewport(s32 playerCount, s32 playerIndex, Viewport* viewport) { + s32 halfWidth = SCREEN_WIDTH / 2; + s32 halfHeight = SCREEN_HEIGHT / 2; + + if (playerCount <= 1) { + viewport->topY = 0; + viewport->bottomY = SCREEN_HEIGHT; + viewport->leftX = 0; + viewport->rightX = SCREEN_WIDTH; + return; + } + + if (playerCount == 2) { + if (LocalMP_IsVerticalSplitScreenEnabled()) { + viewport->topY = 0; + viewport->bottomY = SCREEN_HEIGHT; + viewport->leftX = (playerIndex == 0) ? 0 : halfWidth; + viewport->rightX = (playerIndex == 0) ? halfWidth : SCREEN_WIDTH; + } else { + viewport->topY = (playerIndex == 0) ? 0 : halfHeight; + viewport->bottomY = (playerIndex == 0) ? halfHeight : SCREEN_HEIGHT; + viewport->leftX = 0; + viewport->rightX = SCREEN_WIDTH; + } + return; + } + + if (playerCount == 3) { + if (playerIndex == 0) { + viewport->topY = 0; + viewport->bottomY = halfHeight; + viewport->leftX = 0; + viewport->rightX = SCREEN_WIDTH; + } else if (playerIndex == 1) { + viewport->topY = halfHeight; + viewport->bottomY = SCREEN_HEIGHT; + viewport->leftX = 0; + viewport->rightX = halfWidth; + } else { + viewport->topY = halfHeight; + viewport->bottomY = SCREEN_HEIGHT; + viewport->leftX = halfWidth; + viewport->rightX = SCREEN_WIDTH; + } + return; + } + + viewport->topY = (playerIndex < 2) ? 0 : halfHeight; + viewport->bottomY = (playerIndex < 2) ? halfHeight : SCREEN_HEIGHT; + viewport->leftX = ((playerIndex % 2) == 0) ? 0 : halfWidth; + viewport->rightX = ((playerIndex % 2) == 0) ? halfWidth : SCREEN_WIDTH; +} + +static bool LocalMP_IsSplitScreenRenderAllowed(PlayState* play) { + if (!LocalMP_IsSplitScreenEnabled()) { + return false; + } + + if ((play->pauseCtx.state != 0) || (play->pauseCtx.debugState != 0)) { + return false; + } + + if (Play_InCsMode(play)) { + return false; + } + + if ((play->transitionTrigger != TRANS_TRIGGER_OFF) || (play->transitionMode != TRANS_MODE_OFF)) { + return false; + } + + if ((R_PAUSE_MENU_MODE != 0) || (gTrnsnUnkState != 0)) { + return false; + } + + if (gDbgCamEnabled) { + return false; + } + + return true; +} + +static void LocalMP_EnsureSplitScreenCameras(PlayState* play) { + Player* players[4]; + Camera* mainCam; + s32 playerCount; + s32 i; + + if (!LocalMP_IsSplitScreenRenderAllowed(play)) { + LocalMP_ClearSplitScreenCameras(play); + return; + } + + playerCount = LocalMP_CollectPlayersByPort(play, players); + if (playerCount <= 1) { + LocalMP_ClearSplitScreenCameras(play); + return; + } + + if (playerCount > 4) { + playerCount = 4; + } + + mainCam = play->cameraPtrs[MAIN_CAM]; + if (mainCam == NULL) { + LocalMP_ClearSplitScreenCameras(play); + return; + } + + for (i = 1; i < playerCount; i++) { + s32 slot = i - 1; + s16 camId = sLocalMPSplitCamIds[slot]; + Camera* splitCam; + bool createdCamera = false; + + if ((camId < SUBCAM_FIRST) || (camId >= NUM_CAMS) || (play->cameraPtrs[camId] == NULL)) { + camId = Play_CreateSubCamera(play); + sLocalMPSplitCamIds[slot] = camId; + createdCamera = true; + } + + if ((camId < SUBCAM_FIRST) || (camId >= NUM_CAMS) || (play->cameraPtrs[camId] == NULL)) { + sLocalMPSplitCamIds[slot] = SUBCAM_NONE; + continue; + } + + if (createdCamera) { + Play_CopyCamera(play, camId, MAIN_CAM); + } + + splitCam = play->cameraPtrs[camId]; + + LocalMP_SyncSplitCamera(splitCam, mainCam, players[i]); + + Camera_ChangeStatus(splitCam, CAM_STAT_ACTIVE); + } + + for (i = playerCount - 1; i < ARRAY_COUNT(sLocalMPSplitCamIds); i++) { + s16 camId = sLocalMPSplitCamIds[i]; + + if ((camId >= SUBCAM_FIRST) && (camId < NUM_CAMS) && (play->cameraPtrs[camId] != NULL)) { + Play_ClearCamera(play, camId); + } + + sLocalMPSplitCamIds[i] = SUBCAM_NONE; + } +} + +static void LocalMP_SetViewForCamera(PlayState* play, Camera* camera, Viewport* viewport) { + if ((play->sceneNum == SCENE_HYRULE_FIELD) && (camera->fov < 59.0f)) { + View_SetScale(&play->view, 0.79f); + } else { + View_SetScale(&play->view, 1.0f); + } + + play->view.fovy = camera->fov; + func_800AA358(&play->view, &camera->eye, &camera->at, &camera->up); + View_SetViewport(&play->view, viewport); + func_800AA460(&play->view, play->view.fovy, play->view.zNear, play->lightCtx.fogFar); + func_800AAA50(&play->view, 15); + // Keep OVERLAY-space world effects (hookshot reticle, lock-on indicators) in the same split viewport. + func_800AB560(&play->view); +} + +static f32 LocalMP_DotVec3f(const Vec3f* a, const Vec3f* b) { + return (a->x * b->x) + (a->y * b->y) + (a->z * b->z); +} + +static void LocalMP_WorldToAudioSpace(const Camera* camera, const Vec3f* worldPos, Vec3f* outAudioPos) { + Vec3f forward; + Vec3f right; + Vec3f up; + Vec3f delta; + f32 mag; + + if ((camera == NULL) || (worldPos == NULL) || (outAudioPos == NULL)) { + if (outAudioPos != NULL) { + outAudioPos->x = 0.0f; + outAudioPos->y = 0.0f; + outAudioPos->z = 0.0f; + } + return; + } + + forward.x = camera->at.x - camera->eye.x; + forward.y = camera->at.y - camera->eye.y; + forward.z = camera->at.z - camera->eye.z; + + mag = sqrtf(SQ(forward.x) + SQ(forward.y) + SQ(forward.z)); + if (mag < 0.001f) { + forward.x = 0.0f; + forward.y = 0.0f; + forward.z = 1.0f; + mag = 1.0f; + } + + forward.x /= mag; + forward.y /= mag; + forward.z /= mag; + + right.x = (forward.y * camera->up.z) - (forward.z * camera->up.y); + right.y = (forward.z * camera->up.x) - (forward.x * camera->up.z); + right.z = (forward.x * camera->up.y) - (forward.y * camera->up.x); + + mag = sqrtf(SQ(right.x) + SQ(right.y) + SQ(right.z)); + if (mag < 0.001f) { + Vec3f fallbackUp = { 0.0f, 1.0f, 0.0f }; + + if (fabsf(forward.y) > 0.99f) { + fallbackUp.x = 1.0f; + fallbackUp.y = 0.0f; + fallbackUp.z = 0.0f; + } + + right.x = (forward.y * fallbackUp.z) - (forward.z * fallbackUp.y); + right.y = (forward.z * fallbackUp.x) - (forward.x * fallbackUp.z); + right.z = (forward.x * fallbackUp.y) - (forward.y * fallbackUp.x); + mag = sqrtf(SQ(right.x) + SQ(right.y) + SQ(right.z)); + } + + if (mag < 0.001f) { + right.x = 1.0f; + right.y = 0.0f; + right.z = 0.0f; + mag = 1.0f; + } + + right.x /= mag; + right.y /= mag; + right.z /= mag; + + up.x = (right.y * forward.z) - (right.z * forward.y); + up.y = (right.z * forward.x) - (right.x * forward.z); + up.z = (right.x * forward.y) - (right.y * forward.x); + + mag = sqrtf(SQ(up.x) + SQ(up.y) + SQ(up.z)); + if (mag < 0.001f) { + up.x = camera->up.x; + up.y = camera->up.y; + up.z = camera->up.z; + mag = sqrtf(SQ(up.x) + SQ(up.y) + SQ(up.z)); + } + + if (mag < 0.001f) { + up.x = 0.0f; + up.y = 1.0f; + up.z = 0.0f; + mag = 1.0f; + } + + up.x /= mag; + up.y /= mag; + up.z /= mag; + + delta.x = worldPos->x - camera->eye.x; + delta.y = worldPos->y - camera->eye.y; + delta.z = worldPos->z - camera->eye.z; + + outAudioPos->x = LocalMP_DotVec3f(&delta, &right); + outAudioPos->y = LocalMP_DotVec3f(&delta, &up); + outAudioPos->z = LocalMP_DotVec3f(&delta, &forward); +} + +static s32 LocalMP_GetClosestPlayerSlotForPos(Player* players[4], s32 playerCount, const Vec3f* worldPos) { + s32 bestSlot = 0; + f32 bestDistSq = FLT_MAX; + s32 i; + + if ((players == NULL) || (worldPos == NULL) || (playerCount <= 0)) { + return 0; + } + + if (playerCount > 4) { + playerCount = 4; + } + + for (i = 0; i < playerCount; i++) { + if (players[i] != NULL) { + f32 dx = worldPos->x - players[i]->actor.world.pos.x; + f32 dy = worldPos->y - players[i]->actor.world.pos.y; + f32 dz = worldPos->z - players[i]->actor.world.pos.z; + f32 distSq = SQ(dx) + SQ(dy) + SQ(dz); + + if (distSq < bestDistSq) { + bestDistSq = distSq; + bestSlot = i; + } + } + } + + return bestSlot; +} + +static void LocalMP_ProcessActorAudio(PlayState* play, Player* players[4], Camera* cameras[4], s32 playerCount) { + ActorListEntry* actorListEntry = &play->actorCtx.actorLists[0]; + s32 i; + + if ((play == NULL) || (players == NULL) || (cameras == NULL) || (playerCount <= 0)) { + return; + } + + if (playerCount > 4) { + playerCount = 4; + } + + for (i = 0; i < ARRAY_COUNT(play->actorCtx.actorLists); i++, actorListEntry++) { + Actor* actor = actorListEntry->head; + s32 sanity = 0; + + while ((actor != NULL) && (sanity < 2000)) { + if ((actor->update != NULL) && (actor->sfx != 0)) { + s32 playerSlot = LocalMP_GetClosestPlayerSlotForPos(players, playerCount, &actor->world.pos); + Camera* listenerCamera = cameras[playerSlot]; + + if (listenerCamera == NULL) { + listenerCamera = cameras[0]; + } + + LocalMP_WorldToAudioSpace(listenerCamera, &actor->world.pos, &actor->projectedPos); + Actor_PlaySfx(actor); + } + + actor = actor->next; + sanity++; + } + } +} + +static void LocalMP_DrawPlayerActorsOnly(PlayState* play) { + Actor* actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + + while (actor != NULL) { + Actor* next = actor->next; + + if ((actor->update != NULL) && (actor->draw != NULL)) { + Actor_Draw(play, actor); + } + + actor = next; + } +} + +static bool LocalMP_DrawSplitScreen(PlayState* play) { + GraphicsContext* __gfxCtx = play->state.gfxCtx; + Player* players[4]; + Camera* cameras[4]; + View mainView = play->view; + MtxF mainViewProjection = play->viewProjectionMtxF; + MtxF mainBillboardMtxF = play->billboardMtxF; + Mtx* mainBillboardMtx = play->billboardMtx; + Lights* lights; + Vec3f sunPos; + s32 playerCount; + s32 i; + bool performanceMode = LocalMP_IsSplitScreenPerformanceModeEnabled(); + bool aggressiveMode = LocalMP_IsSplitScreenAggressiveModeEnabled(); + + if (!LocalMP_IsSplitScreenRenderAllowed(play)) { + return false; + } + + playerCount = LocalMP_CollectPlayersByPort(play, players); + if (playerCount <= 1) { + return false; + } + + if (playerCount > 4) { + playerCount = 4; + } + + cameras[0] = play->cameraPtrs[MAIN_CAM]; + if (cameras[0] == NULL) { + return false; + } + + for (i = 1; i < playerCount; i++) { + s16 camId = sLocalMPSplitCamIds[i - 1]; + + if ((camId < SUBCAM_FIRST) || (camId >= NUM_CAMS) || (play->cameraPtrs[camId] == NULL)) { + return false; + } + + cameras[i] = play->cameraPtrs[camId]; + LocalMP_SyncSplitCamera(cameras[i], cameras[0], players[i]); + } + + if (!aggressiveMode && ((HREG(80) != 10) || (HREG(83) != 0))) { + if (play->skyboxId && (play->skyboxId != SKYBOX_UNSET_1D) && !play->envCtx.skyboxDisabled) { + if ((play->skyboxId == SKYBOX_NORMAL_SKY) || (play->skyboxId == SKYBOX_CUTSCENE_MAP)) { + Environment_UpdateSkybox(play, play->skyboxId, &play->envCtx, &play->skyboxCtx); + SkyboxDraw_Draw(&play->skyboxCtx, play->state.gfxCtx, play->skyboxId, play->envCtx.skyboxBlend, + play->view.eye.x, play->view.eye.y, play->view.eye.z); + } else if (play->skyboxCtx.unk_140 == 0) { + SkyboxDraw_Draw(&play->skyboxCtx, play->state.gfxCtx, play->skyboxId, 0, play->view.eye.x, + play->view.eye.y, play->view.eye.z); + } + } + } + + if (!aggressiveMode && ((HREG(80) != 10) || (HREG(90) & 2))) { + if (!play->envCtx.sunMoonDisabled) { + Environment_DrawSunAndMoon(play); + } + } + + if (!aggressiveMode && ((HREG(80) != 10) || (HREG(90) & 1))) { + Environment_DrawSkyboxFilters(play); + } + + if (!aggressiveMode && ((HREG(80) != 10) || (HREG(90) & 4))) { + Environment_UpdateLightningStrike(play); + Environment_DrawLightning(play, 0); + } + + if (!aggressiveMode && ((HREG(80) != 10) || (HREG(90) & 8))) { + lights = LightContext_NewLights(&play->lightCtx, play->state.gfxCtx); + Lights_BindAll(lights, play->lightCtx.listHead, NULL); + Lights_Draw(lights, play->state.gfxCtx); + } + + Actor_SetSfxUpdatesSuppressed(true); + + for (i = 0; i < playerCount; i++) { + Viewport viewport; + Camera* camera = cameras[i]; + bool skipSecondaryViewportEffects = performanceMode && (i > 0); + bool skipViewportEffects = skipSecondaryViewportEffects || aggressiveMode; + bool drawPlayersOnly = aggressiveMode && (i > 0); + + // Keep frame interpolation history isolated per viewport so higher-FPS interpolation + // does not cross-match matrices between split-screen players. + FrameInterpolation_RecordOpenChild("LocalMPSplitViewport", i); + + LocalMP_GetSplitViewport(playerCount, i, &viewport); + LocalMP_SetViewForCamera(play, camera, &viewport); + + if ((R_PAUSE_MENU_MODE <= 1) && CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0)) { + gSPSetExtraGeometryMode(POLY_OPA_DISP++, G_EX_INVERT_CULLING); + gSPSetExtraGeometryMode(POLY_XLU_DISP++, G_EX_INVERT_CULLING); + gSPMatrix(POLY_OPA_DISP++, play->view.projectionFlippedPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPMatrix(POLY_XLU_DISP++, play->view.projectionFlippedPtr, G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_PROJECTION); + gSPMatrix(POLY_OPA_DISP++, play->view.viewingPtr, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + gSPMatrix(POLY_XLU_DISP++, play->view.viewingPtr, G_MTX_NOPUSH | G_MTX_MUL | G_MTX_PROJECTION); + } + + Matrix_MtxToMtxF(&play->view.viewing, &play->billboardMtxF); + Matrix_MtxToMtxF(&play->view.projection, &play->viewProjectionMtxF); + Matrix_Mult(&play->viewProjectionMtxF, MTXMODE_NEW); + Matrix_Mult(&play->billboardMtxF, MTXMODE_APPLY); + Matrix_Get(&play->viewProjectionMtxF); + play->billboardMtxF.mf[0][3] = play->billboardMtxF.mf[1][3] = play->billboardMtxF.mf[2][3] = + play->billboardMtxF.mf[3][0] = play->billboardMtxF.mf[3][1] = play->billboardMtxF.mf[3][2] = 0.0f; + Matrix_Transpose(&play->billboardMtxF); + play->billboardMtx = + Matrix_MtxFToMtx(MATRIX_CHECKFLOATS(&play->billboardMtxF), Graph_Alloc(play->state.gfxCtx, sizeof(Mtx))); + + gSPSegment(POLY_OPA_DISP++, 0x01, play->billboardMtx); + + if (i == 0) { + mainView = play->view; + mainViewProjection = play->viewProjectionMtxF; + mainBillboardMtxF = play->billboardMtxF; + mainBillboardMtx = play->billboardMtx; + } + + if ((HREG(80) != 10) || (HREG(84) != 0)) { + if (VREG(94) == 0) { + s32 roomDrawFlags; + + if (HREG(80) != 10) { + roomDrawFlags = 3; + } else { + roomDrawFlags = HREG(84); + } + + if (aggressiveMode) { + roomDrawFlags &= 1; + } + + Scene_Draw(play); + if (OTRRoom_AreaPersistenceEnabled()) { + s32 retainedRoomCount = OTRRoom_GetRetainedRoomCount(); + + if (retainedRoomCount > 0) { + s32 retainedRoomIndex; + + for (retainedRoomIndex = 0; retainedRoomIndex < retainedRoomCount; retainedRoomIndex++) { + Room* retainedRoom = OTRRoom_GetRetainedRoom(retainedRoomIndex); + + if ((retainedRoom != NULL) && (retainedRoom->num >= 0)) { + Room_Draw(play, retainedRoom, roomDrawFlags & 3); + } + } + } else { + Room_Draw(play, &play->roomCtx.curRoom, roomDrawFlags & 3); + if (!skipViewportEffects) { + Room_Draw(play, &play->roomCtx.prevRoom, roomDrawFlags & 3); + } + } + } else { + Room_Draw(play, &play->roomCtx.curRoom, roomDrawFlags & 3); + if (!skipViewportEffects) { + Room_Draw(play, &play->roomCtx.prevRoom, roomDrawFlags & 3); + } + } + } + } + + if ((HREG(80) != 10) || (HREG(83) != 0)) { + if (!skipViewportEffects && (play->skyboxCtx.unk_140 != 0) && + (camera->setting != CAM_SET_PREREND_FIXED)) { + Vec3f quakeOffset; + + Camera_GetSkyboxOffset(&quakeOffset, camera); + SkyboxDraw_Draw(&play->skyboxCtx, play->state.gfxCtx, play->skyboxId, 0, play->view.eye.x + quakeOffset.x, + play->view.eye.y + quakeOffset.y, play->view.eye.z + quakeOffset.z); + } + } + + if (!skipViewportEffects && (play->envCtx.unk_EE[1] != 0)) { + Environment_DrawRain(play, &play->view, play->state.gfxCtx); + } + + if ((HREG(80) != 10) || (HREG(84) != 0)) { + Environment_FillScreen(play->state.gfxCtx, 0, 0, 0, play->unk_11E18, FILL_SCREEN_OPA); + } + + if ((HREG(80) != 10) || (HREG(85) != 0)) { + if (drawPlayersOnly) { + LocalMP_DrawPlayerActorsOnly(play); + } else { + func_800315AC(play, &play->actorCtx); + } + } + + if (!skipViewportEffects && ((HREG(80) != 10) || (HREG(86) != 0))) { + if (!play->envCtx.sunMoonDisabled) { + sunPos.x = play->view.eye.x + play->envCtx.sunPos.x; + sunPos.y = play->view.eye.y + play->envCtx.sunPos.y; + sunPos.z = play->view.eye.z + play->envCtx.sunPos.z; + Environment_DrawSunLensFlare(play, &play->envCtx, &play->view, play->state.gfxCtx, sunPos, 0); + } + Environment_DrawCustomLensFlare(play); + } + + FrameInterpolation_RecordCloseChild(); + } + + Actor_SetSfxUpdatesSuppressed(false); + LocalMP_ProcessActorAudio(play, players, cameras, playerCount); + + play->view = mainView; + play->viewProjectionMtxF = mainViewProjection; + play->billboardMtxF = mainBillboardMtxF; + play->billboardMtx = mainBillboardMtx; + play->view.unk_124 = 0; + + SET_FULLSCREEN_VIEWPORT(&play->view); + func_800AA460(&play->view, play->view.fovy, play->view.zNear, play->lightCtx.fogFar); + func_800AAA50(&play->view, 15); + func_800AB560(&play->view); + + if ((HREG(80) != 10) || (HREG(87) != 0)) { + if (MREG(64) != 0) { + Environment_FillScreen(play->state.gfxCtx, MREG(65), MREG(66), MREG(67), MREG(68), + FILL_SCREEN_OPA | FILL_SCREEN_XLU); + } + + switch (play->envCtx.fillScreen) { + case 1: + Environment_FillScreen(play->state.gfxCtx, play->envCtx.screenFillColor[0], play->envCtx.screenFillColor[1], + play->envCtx.screenFillColor[2], play->envCtx.screenFillColor[3], + FILL_SCREEN_OPA | FILL_SCREEN_XLU); + break; + default: + break; + } + } + + if ((HREG(80) != 10) || (HREG(88) != 0)) { + if (play->envCtx.sandstormState != SANDSTORM_OFF) { + Environment_DrawSandstorm(play, play->envCtx.sandstormState); + } + } + + if (!aggressiveMode) { + LocalMP_DrawDebugMarkers(play); + } + + if ((HREG(80) != 10) || (HREG(93) != 0)) { + DebugDisplay_DrawObjects(play); + } + + play->viewProjectionMtxF = mainViewProjection; + play->billboardMtxF = mainBillboardMtxF; + play->billboardMtx = mainBillboardMtx; + + return true; +} + +static void LocalMP_FinishSplitScreenCameras(PlayState* play) { + s32 i; + + for (i = 0; i < ARRAY_COUNT(sLocalMPSplitCamIds); i++) { + s16 camId = sLocalMPSplitCamIds[i]; + + if ((camId >= SUBCAM_FIRST) && (camId < NUM_CAMS) && (play->cameraPtrs[camId] != NULL)) { + Camera_Finish(play->cameraPtrs[camId]); + } + } +} + +static void LocalMP_UpdateSharedCamera(PlayState* play) { + static s16 sSharedCamLastScene = -1; + Camera* camera; + Player* mainPlayer; + f32 forwardLookAhead; + f32 eyeLift; + f32 yawStepScale; + Vec3f at; + Vec3f eye; + f32 cameraDist; + f32 atHeight; + s16 yaw; + + if (sSharedCamLastScene != play->sceneNum) { + sSharedCamLastScene = play->sceneNum; + sLocalMPCameraState.initialized = false; + } + + if (LocalMP_IsSplitScreenEnabled()) { + sLocalMPCameraState.initialized = false; + return; + } + + if (LocalMP_GetDesiredPlayerCount() <= 1) { + sLocalMPCameraState.initialized = false; + return; + } + + if ((play->pauseCtx.state != 0) || (play->pauseCtx.debugState != 0) || Play_InCsMode(play)) { + return; + } + + if ((play->transitionTrigger != TRANS_TRIGGER_OFF) || (play->transitionMode != TRANS_MODE_OFF) || + (play->roomCtx.status != 0)) { + sLocalMPCameraState.initialized = false; + return; + } + + camera = play->cameraPtrs[MAIN_CAM]; + if ((camera == NULL) || (play->activeCamera != MAIN_CAM) || (camera->setting == CAM_SET_PREREND_FIXED)) { + sLocalMPCameraState.initialized = false; + return; + } + + // Keep mode-specific camera behavior (like hookshot) from being overridden. + if (camera->mode == CAM_MODE_HOOKSHOT) { + sLocalMPCameraState.initialized = false; + return; + } + + if (!LocalMP_UpdateMidpointState(play)) { + sLocalMPCameraState.initialized = false; + return; + } + + mainPlayer = LocalMP_FindPlayerByPort(play, 1); + if (mainPlayer == NULL) { + mainPlayer = GET_PLAYER(play); + } + + cameraDist = 180.0f + (sLocalMPCameraState.maxRadius * 1.8f); + cameraDist = CLAMP(cameraDist, 190.0f, 560.0f); + + atHeight = 26.0f + (sLocalMPCameraState.heightSpan * 0.35f); + eyeLift = 84.0f + (sLocalMPCameraState.heightSpan * 0.2f) + (sLocalMPCameraState.maxRadius * 0.25f); + eyeLift = CLAMP(eyeLift, 84.0f, 190.0f); + + if (sLocalMPCameraState.hasForward) { + yaw = Math_Atan2S(sLocalMPCameraState.forwardX, sLocalMPCameraState.forwardZ); + } else if (mainPlayer != NULL) { + yaw = mainPlayer->actor.shape.rot.y; + } else { + yaw = camera->inputDir.y; + } + + forwardLookAhead = sLocalMPCameraState.hasForward ? CLAMP(sLocalMPCameraState.maxRadius * 0.25f, 8.0f, 40.0f) + : 0.0f; + + at.x = sLocalMPCameraState.midpoint.x + (sLocalMPCameraState.forwardX * forwardLookAhead); + at.y = sLocalMPCameraState.midpoint.y + atHeight; + at.z = sLocalMPCameraState.midpoint.z + (sLocalMPCameraState.forwardZ * forwardLookAhead); + + if (!sLocalMPCameraState.initialized) { + sLocalMPCameraState.smoothedAt = at; + sLocalMPCameraState.smoothedDist = cameraDist; + sLocalMPCameraState.smoothedEyeLift = eyeLift; + sLocalMPCameraState.smoothedYaw = yaw; + sLocalMPCameraState.initialized = true; + + // Scene loads/mode handoffs can produce large one-frame jumps. + FrameInterpolation_DontInterpolateCamera(); + } else { + Math_SmoothStepToF(&sLocalMPCameraState.smoothedAt.x, at.x, 0.18f, 24.0f, 0.1f); + Math_SmoothStepToF(&sLocalMPCameraState.smoothedAt.y, at.y, 0.2f, 12.0f, 0.1f); + Math_SmoothStepToF(&sLocalMPCameraState.smoothedAt.z, at.z, 0.18f, 24.0f, 0.1f); + Math_SmoothStepToF(&sLocalMPCameraState.smoothedDist, cameraDist, 0.12f, 14.0f, 0.1f); + Math_SmoothStepToF(&sLocalMPCameraState.smoothedEyeLift, eyeLift, 0.12f, 8.0f, 0.1f); + + yawStepScale = CLAMP(220.0f + (sLocalMPCameraState.maxRadius * 3.0f), 220.0f, 900.0f); + Math_ScaledStepToS(&sLocalMPCameraState.smoothedYaw, yaw, yawStepScale); + } + + at = sLocalMPCameraState.smoothedAt; + + eye.x = at.x - Math_SinS(sLocalMPCameraState.smoothedYaw) * sLocalMPCameraState.smoothedDist; + eye.y = at.y + sLocalMPCameraState.smoothedEyeLift; + eye.z = at.z - Math_CosS(sLocalMPCameraState.smoothedYaw) * sLocalMPCameraState.smoothedDist; + + Play_CameraSetAtEye(play, MAIN_CAM, &at, &eye); +} + +static void LocalMP_DrawDebugMarkers(PlayState* play) { + static Color_RGBA8 sPortColors[] = { + { 255, 255, 255, 200 }, + { 255, 96, 96, 200 }, + { 96, 128, 255, 200 }, + { 255, 220, 64, 200 }, + }; + Actor* actor; + + if (!CVarGetInteger(LOCAL_MP_DEBUG_MARKERS_CVAR, 0)) { + return; + } + + if (LocalMP_GetDesiredPlayerCount() <= 1) { + return; + } + + if (!LocalMP_UpdateMidpointState(play)) { + return; + } + + DebugDisplay_AddObject(sLocalMPCameraState.midpoint.x, sLocalMPCameraState.midpoint.y + 10.0f, + sLocalMPCameraState.midpoint.z, 0, 0, 0, 8.0f, 8.0f, 8.0f, 255, 80, 80, 210, 2, + play->state.gfxCtx); + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* player = (Player*)actor; + + if ((player->controllerPort >= 1) && (player->controllerPort <= 4)) { + Color_RGBA8 color = sPortColors[player->controllerPort - 1]; + + DebugDisplay_AddObject(actor->world.pos.x, actor->world.pos.y + 70.0f, actor->world.pos.z, 0, 0, 0, + 3.0f, 3.0f, 3.0f, color.r, color.g, color.b, color.a, 0, play->state.gfxCtx); + } + } + + actor = actor->next; + } +} // This macro prints the number "1" with a file and line number if R_ENABLE_PLAY_LOGS is enabled. // For example, it can be used to trace the play state execution at a high level. @@ -202,6 +1441,7 @@ void Play_Destroy(GameState* thisx) { Player* player = GET_PLAYER(play); GameInteractor_ExecuteOnPlayDestroy(); + LocalMP_ClearSplitScreenCameras(play); play->state.gfxCtx->callback = NULL; play->state.gfxCtx->callbackParam = 0; @@ -684,11 +1924,11 @@ void Play_Init(GameState* thisx) { #endif if (CVarGetInteger(CVAR_ENHANCEMENT("IvanCoopModeEnabled"), 0)) { - Actor_Spawn(&play->actorCtx, play, gEnPartnerId, GET_PLAYER(play)->actor.world.pos.x, - GET_PLAYER(play)->actor.world.pos.y + Player_GetHeight(GET_PLAYER(play)) + 5.0f, - GET_PLAYER(play)->actor.world.pos.z, 0, 0, 0, 1); + CVarSetInteger(CVAR_ENHANCEMENT("IvanCoopModeEnabled"), 0); } + sLocalMPLastTeleportRequest = CVarGetInteger(LOCAL_MP_TELEPORT_REQUEST_CVAR, 0); + // nextEntranceIndex was not initialized, so the previous value was carried over during soft resets. gPlayState->nextEntranceIndex = gSaveContext.entranceIndex; } @@ -1215,6 +2455,8 @@ void Play_Update(PlayState* play) { PLAY_LOG(3637); if (!play->unk_11DE9) { + LocalMP_EnsurePlayers(play); + LocalMP_HandleTeleportAllToSelected(play); Actor_UpdateAll(play, &play->actorCtx); } @@ -1308,6 +2550,7 @@ void Play_Update(PlayState* play) { PLAY_LOG(3801); GameInteractor_ExecuteOnCameraState(play); + LocalMP_EnsureSplitScreenCameras(play); if (!isPaused || gDbgCamEnabled) { s32 i; @@ -1324,6 +2567,7 @@ void Play_Update(PlayState* play) { } Camera_Update(play->cameraPtrs[play->nextCamera]); + LocalMP_UpdateSharedCamera(play); PLAY_LOG(3814); } @@ -1501,6 +2745,11 @@ void Play_Draw(PlayState* play) { goto Play_Draw_DrawOverlayElements; } + if (LocalMP_DrawSplitScreen(play)) { + GameInteractor_ExecuteOnPlayDrawEnd(); + goto Play_Draw_DrawOverlayElements; + } + if ((HREG(80) != 10) || (HREG(83) != 0)) { if (play->skyboxId && (play->skyboxId != SKYBOX_UNSET_1D) && !play->envCtx.skyboxDisabled) { if ((play->skyboxId == SKYBOX_NORMAL_SKY) || (play->skyboxId == SKYBOX_CUTSCENE_MAP)) { @@ -1545,8 +2794,27 @@ void Play_Draw(PlayState* play) { roomDrawFlags = HREG(84); } Scene_Draw(play); - Room_Draw(play, &play->roomCtx.curRoom, roomDrawFlags & 3); - Room_Draw(play, &play->roomCtx.prevRoom, roomDrawFlags & 3); + if (OTRRoom_AreaPersistenceEnabled()) { + s32 retainedRoomCount = OTRRoom_GetRetainedRoomCount(); + + if (retainedRoomCount > 0) { + s32 retainedRoomIndex; + + for (retainedRoomIndex = 0; retainedRoomIndex < retainedRoomCount; retainedRoomIndex++) { + Room* retainedRoom = OTRRoom_GetRetainedRoom(retainedRoomIndex); + + if ((retainedRoom != NULL) && (retainedRoom->num >= 0)) { + Room_Draw(play, retainedRoom, roomDrawFlags & 3); + } + } + } else { + Room_Draw(play, &play->roomCtx.curRoom, roomDrawFlags & 3); + Room_Draw(play, &play->roomCtx.prevRoom, roomDrawFlags & 3); + } + } else { + Room_Draw(play, &play->roomCtx.curRoom, roomDrawFlags & 3); + Room_Draw(play, &play->roomCtx.prevRoom, roomDrawFlags & 3); + } } } @@ -1605,6 +2873,8 @@ void Play_Draw(PlayState* play) { } } + LocalMP_DrawDebugMarkers(play); + if ((HREG(80) != 10) || (HREG(93) != 0)) { DebugDisplay_DrawObjects(play); } @@ -1677,6 +2947,7 @@ void Play_Draw(PlayState* play) { } Camera_Finish(GET_ACTIVE_CAM(play)); + LocalMP_FinishSplitScreenCameras(play); CLOSE_DISPS(gfxCtx); @@ -1705,6 +2976,27 @@ void Play_Main(GameState* thisx) { D_8012D1F8 = &play->state.input[0]; + /* Route START from any controller into input[0] because pause/menu code + * only checks pad 1. Also synthesize a one-frame START press from cur + * button rising edges for input backends that miss press for START. */ + { + u16 mergedStartCur = 0; + u16 mergedStartPress = 0; + + for (s32 _i = 0; _i < ARRAY_COUNT(play->state.input); _i++) { + mergedStartCur |= play->state.input[_i].cur.button & BTN_START; + mergedStartPress |= play->state.input[_i].press.button & BTN_START; + } + + if ((mergedStartCur & BTN_START) && !(sMergedStartPrevCur & BTN_START)) { + mergedStartPress |= BTN_START; + } + + play->state.input[0].cur.button |= mergedStartCur & BTN_START; + play->state.input[0].press.button |= mergedStartPress & BTN_START; + sMergedStartPrevCur = mergedStartCur & BTN_START; + } + DebugDisplay_Init(); PLAY_LOG(4556); diff --git a/soh/src/code/z_player_lib.c b/soh/src/code/z_player_lib.c index 34750ed2cc7..deb8dc5b997 100644 --- a/soh/src/code/z_player_lib.c +++ b/soh/src/code/z_player_lib.c @@ -13,6 +13,13 @@ #include +#define LOCAL_MP_PLAYER_COUNT_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount") +#define LOCAL_MP_DISABLED_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Disable") +#define LOCAL_MP_TUNIC_COLOR_P1_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Player1TunicColor.Value") +#define LOCAL_MP_TUNIC_COLOR_P2_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Player2TunicColor.Value") +#define LOCAL_MP_TUNIC_COLOR_P3_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Player3TunicColor.Value") +#define LOCAL_MP_TUNIC_COLOR_P4_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Player4TunicColor.Value") + typedef struct { /* 0x00 */ u8 flag; /* 0x02 */ u16 textId; @@ -505,7 +512,8 @@ s32 Player_InBlockingCsMode(PlayState* play, Player* this) { return (this->stateFlags1 & (PLAYER_STATE1_DEAD | PLAYER_STATE1_IN_CUTSCENE)) || (this->csAction != 0) || (play->transitionTrigger == TRANS_TRIGGER_START) || (this->stateFlags1 & PLAYER_STATE1_LOADING) || (this->stateFlags3 & PLAYER_STATE3_FLYING_WITH_HOOKSHOT) || - ((gSaveContext.magicState != MAGIC_STATE_IDLE) && (Player_ActionToMagicSpell(this, this->itemAction) >= 0)); + ((Magic_GetStateForPlayer(this) != MAGIC_STATE_IDLE) && + (Player_ActionToMagicSpell(this, this->itemAction) >= 0)); } s32 Player_InCsMode(PlayState* play) { @@ -676,10 +684,30 @@ void func_8008EC70(Player* this) { } void Player_SetEquipmentData(PlayState* play, Player* this) { + s32 equippedBoots; + if (this->csAction != 0x56) { this->currentShield = SHIELD_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD)); this->currentTunic = TUNIC_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC)); - this->currentBoots = BOOTS_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS)); + equippedBoots = BOOTS_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS)); + + if (this->equippedBoots != equippedBoots) { + this->equippedBoots = equippedBoots; + this->bootsAreActive = false; + } + + if ((equippedBoots == PLAYER_BOOTS_IRON) || (equippedBoots == PLAYER_BOOTS_HOVER)) { + if (this->bootsToggleRequest) { + this->bootsAreActive ^= 1; + } + + this->currentBoots = this->bootsAreActive ? equippedBoots : PLAYER_BOOTS_KOKIRI; + } else { + this->bootsAreActive = false; + this->currentBoots = equippedBoots; + } + + this->bootsToggleRequest = false; this->currentSwordItemId = B_BTN_ITEM; Player_SetModelGroup(this, Player_ActionToModelGroup(this, this->heldItemAction)); Player_SetBootData(play, this); @@ -1031,6 +1059,57 @@ Gfx* sBootDListGroups[][2] = { { gLinkAdultLeftHoverBootDL, gLinkAdultRightHoverBootDL }, // PLAYER_BOOTS_HOVER }; +static bool LocalMP_UsePerPlayerTunicColors(void) { + return !CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0) && (CVarGetInteger(LOCAL_MP_PLAYER_COUNT_CVAR, 2) > 1); +} + +static const char* LocalMP_GetPlayerTunicColorCVar(s32 controllerPort) { + switch (controllerPort) { + case 1: + return LOCAL_MP_TUNIC_COLOR_P1_CVAR; + case 2: + return LOCAL_MP_TUNIC_COLOR_P2_CVAR; + case 3: + return LOCAL_MP_TUNIC_COLOR_P3_CVAR; + case 4: + return LOCAL_MP_TUNIC_COLOR_P4_CVAR; + default: + return NULL; + } +} + +static Color_RGB8 LocalMP_GetDefaultPlayerTunicColor(s32 controllerPort) { + Color_RGB8 color; + + switch (controllerPort) { + case 1: + color.r = 30; + color.g = 105; + color.b = 27; + break; + case 2: + color.r = 255; + color.g = 0; + color.b = 0; + break; + case 3: + color.r = 0; + color.g = 120; + color.b = 255; + break; + case 4: + color.r = 122; + color.g = 57; + color.b = 163; + break; + default: + color = sTunicColors[PLAYER_TUNIC_KOKIRI]; + break; + } + + return color; +} + void Player_DrawImpl(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dListCount, s32 lod, s32 tunic, s32 boots, s32 face, OverrideLimbDrawOpa overrideLimbDraw, PostLimbDrawOpa postLimbDraw, void* data) { Color_RGB8* color; @@ -1077,6 +1156,20 @@ void Player_DrawImpl(PlayState* play, void** skeleton, Vec3s* jointTable, s32 dL color = &sTemp; } + if ((postLimbDraw != NULL) && (data != NULL) && LocalMP_UsePerPlayerTunicColors()) { + Player* player = (Player*)data; + const char* playerColorCVar; + + if ((player->controllerPort >= 1) && (player->controllerPort <= 4)) { + playerColorCVar = LocalMP_GetPlayerTunicColorCVar(player->controllerPort); + + if (playerColorCVar != NULL) { + sTemp = CVarGetColor24(playerColorCVar, LocalMP_GetDefaultPlayerTunicColor(player->controllerPort)); + color = &sTemp; + } + } + } + if (GameInteractor_Should(VB_APPLY_TUNIC_COLOR, true, data, color)) { gDPSetEnvColor(POLY_OPA_DISP++, color->r, color->g, color->b, 0); } diff --git a/soh/src/code/z_rcp.c b/soh/src/code/z_rcp.c index d358d4f44b6..87108154f3f 100644 --- a/soh/src/code/z_rcp.c +++ b/soh/src/code/z_rcp.c @@ -2,6 +2,14 @@ #include #include "soh/OTRGlobals.h" +#define LOCAL_MP_PLAYER_COUNT_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount") +#define LOCAL_MP_DISABLED_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Disable") + +static s32 Rcp_IsLocalMultiplayerEnabled(void) { + return !CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0) && + (CVarGetInteger(LOCAL_MP_PLAYER_COUNT_CVAR, 2) > 1); +} + Gfx sSetupDL[SETUPDL_MAX][6] = { { /* SETUPDL_0 */ @@ -1600,6 +1608,10 @@ void Gfx_SetupFrame(GraphicsContext* gfxCtx, u8 r, u8 g, u8 b) { if ((R_PAUSE_MENU_MODE < 2) && (gTrnsnUnkState < 2)) { s32 letterboxSize = ShrinkWindow_GetCurrentVal(); // Upstream TODO: Letterbox + if (Rcp_IsLocalMultiplayerEnabled()) { + letterboxSize = 0; + } + if (HREG(80) == 16) { if (HREG(95) != 16) { HREG(81) = 3; diff --git a/soh/src/code/z_room.c b/soh/src/code/z_room.c index 317ee0775a5..5b646b96fa5 100644 --- a/soh/src/code/z_room.c +++ b/soh/src/code/z_room.c @@ -515,6 +515,7 @@ void func_80096F6C(PlayState* play, Room* room, u32 flags) { void func_80096FD4(PlayState* play, Room* room) { room->num = -1; room->segment = NULL; + OTRRoom_ResetAreaPersistence(); } u32 func_80096FE8(PlayState* play, RoomContext* roomCtx) { diff --git a/soh/src/code/z_skelanime.c b/soh/src/code/z_skelanime.c index 4335df0c8f4..3449c2ed91a 100644 --- a/soh/src/code/z_skelanime.c +++ b/soh/src/code/z_skelanime.c @@ -902,6 +902,10 @@ void AnimationContext_SetLoadFrame(PlayState* play, LinkAnimationHeader* animati if (frame < 0) { frame = 0; } + // SOH [Alt Assets] Check if animData is null (can happen if animation data segment failed to load) + if (animData == NULL) { + return; + } memcpy(ram, (uintptr_t)animData + (((sizeof(Vec3s) * limbCount + 2) * frame)), sizeof(Vec3s) * limbCount + 2); } } diff --git a/soh/src/code/z_sound_source.c b/soh/src/code/z_sound_source.c index 6e89130d7d5..4aa12a16265 100644 --- a/soh/src/code/z_sound_source.c +++ b/soh/src/code/z_sound_source.c @@ -1,5 +1,176 @@ #include "global.h" +static f32 SoundSource_DotVec3f(const Vec3f* a, const Vec3f* b) { + return (a->x * b->x) + (a->y * b->y) + (a->z * b->z); +} + +static void SoundSource_WorldToAudioSpace(const Camera* camera, const Vec3f* worldPos, Vec3f* outAudioPos) { + Vec3f forward; + Vec3f right; + Vec3f up; + Vec3f delta; + f32 mag; + + if ((camera == NULL) || (worldPos == NULL) || (outAudioPos == NULL)) { + if (outAudioPos != NULL) { + outAudioPos->x = 0.0f; + outAudioPos->y = 0.0f; + outAudioPos->z = 0.0f; + } + return; + } + + forward.x = camera->at.x - camera->eye.x; + forward.y = camera->at.y - camera->eye.y; + forward.z = camera->at.z - camera->eye.z; + + mag = sqrtf(SQ(forward.x) + SQ(forward.y) + SQ(forward.z)); + if (mag < 0.001f) { + forward.x = 0.0f; + forward.y = 0.0f; + forward.z = 1.0f; + mag = 1.0f; + } + + forward.x /= mag; + forward.y /= mag; + forward.z /= mag; + + right.x = (forward.y * camera->up.z) - (forward.z * camera->up.y); + right.y = (forward.z * camera->up.x) - (forward.x * camera->up.z); + right.z = (forward.x * camera->up.y) - (forward.y * camera->up.x); + + mag = sqrtf(SQ(right.x) + SQ(right.y) + SQ(right.z)); + if (mag < 0.001f) { + Vec3f fallbackUp = { 0.0f, 1.0f, 0.0f }; + + if (fabsf(forward.y) > 0.99f) { + fallbackUp.x = 1.0f; + fallbackUp.y = 0.0f; + fallbackUp.z = 0.0f; + } + + right.x = (forward.y * fallbackUp.z) - (forward.z * fallbackUp.y); + right.y = (forward.z * fallbackUp.x) - (forward.x * fallbackUp.z); + right.z = (forward.x * fallbackUp.y) - (forward.y * fallbackUp.x); + mag = sqrtf(SQ(right.x) + SQ(right.y) + SQ(right.z)); + } + + if (mag < 0.001f) { + right.x = 1.0f; + right.y = 0.0f; + right.z = 0.0f; + mag = 1.0f; + } + + right.x /= mag; + right.y /= mag; + right.z /= mag; + + up.x = (right.y * forward.z) - (right.z * forward.y); + up.y = (right.z * forward.x) - (right.x * forward.z); + up.z = (right.x * forward.y) - (right.y * forward.x); + + mag = sqrtf(SQ(up.x) + SQ(up.y) + SQ(up.z)); + if (mag < 0.001f) { + up.x = camera->up.x; + up.y = camera->up.y; + up.z = camera->up.z; + mag = sqrtf(SQ(up.x) + SQ(up.y) + SQ(up.z)); + } + + if (mag < 0.001f) { + up.x = 0.0f; + up.y = 1.0f; + up.z = 0.0f; + mag = 1.0f; + } + + up.x /= mag; + up.y /= mag; + up.z /= mag; + + delta.x = worldPos->x - camera->eye.x; + delta.y = worldPos->y - camera->eye.y; + delta.z = worldPos->z - camera->eye.z; + + outAudioPos->x = SoundSource_DotVec3f(&delta, &right); + outAudioPos->y = SoundSource_DotVec3f(&delta, &up); + outAudioPos->z = SoundSource_DotVec3f(&delta, &forward); +} + +static Player* SoundSource_FindClosestActivePlayer(PlayState* play, const Vec3f* worldPos) { + Actor* playerActor; + Player* closestPlayer = GET_PLAYER(play); + f32 bestDistSq = FLT_MAX; + s32 sanity = 0; + + if ((play == NULL) || (worldPos == NULL)) { + return closestPlayer; + } + + playerActor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + + while ((playerActor != NULL) && (sanity < 2000)) { + if ((playerActor->id == ACTOR_PLAYER) && (playerActor->update != NULL)) { + Player* player = (Player*)playerActor; + f32 dx = worldPos->x - player->actor.world.pos.x; + f32 dy = worldPos->y - player->actor.world.pos.y; + f32 dz = worldPos->z - player->actor.world.pos.z; + f32 distSq = SQ(dx) + SQ(dy) + SQ(dz); + + if (distSq < bestDistSq) { + bestDistSq = distSq; + closestPlayer = player; + } + } + + playerActor = playerActor->next; + sanity++; + } + + return closestPlayer; +} + +static Camera* SoundSource_FindCameraForPlayer(PlayState* play, Player* player) { + s32 i; + + if ((play == NULL) || (player == NULL)) { + return NULL; + } + + for (i = 0; i < NUM_CAMS; i++) { + Camera* camera = play->cameraPtrs[i]; + + if ((camera != NULL) && (camera->player == player) && (camera->status == CAM_STAT_ACTIVE)) { + return camera; + } + } + + for (i = 0; i < NUM_CAMS; i++) { + Camera* camera = play->cameraPtrs[i]; + + if ((camera != NULL) && (camera->player == player)) { + return camera; + } + } + + return play->cameraPtrs[MAIN_CAM]; +} + +static void SoundSource_UpdateProjectedPos(PlayState* play, SoundSource* source) { + Camera* camera; + Player* closestPlayer; + + if ((play == NULL) || (source == NULL)) { + return; + } + + closestPlayer = SoundSource_FindClosestActivePlayer(play, &source->worldPos); + camera = SoundSource_FindCameraForPlayer(play, closestPlayer); + SoundSource_WorldToAudioSpace(camera, &source->worldPos, &source->projectedPos); +} + void SoundSource_InitAll(PlayState* play) { SoundSource* sources = &play->soundSources[0]; s32 i; @@ -18,7 +189,7 @@ void SoundSource_UpdateAll(PlayState* play) { if (DECR(source->countdown) == 0) { Audio_StopSfxByPos(&source->projectedPos); } else { - SkinMatrix_Vec3fMtxFMultXYZ(&play->viewProjectionMtxF, &source->worldPos, &source->projectedPos); + SoundSource_UpdateProjectedPos(play, source); } } @@ -57,7 +228,7 @@ void SoundSource_PlaySfxAtFixedWorldPos(PlayState* play, Vec3f* worldPos, s32 du source->worldPos = *worldPos; source->countdown = duration; - SkinMatrix_Vec3fMtxFMultXYZ(&play->viewProjectionMtxF, &source->worldPos, &source->projectedPos); + SoundSource_UpdateProjectedPos(play, source); Audio_PlaySoundGeneral(sfxId, &source->projectedPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); } diff --git a/soh/src/code/z_sram.c b/soh/src/code/z_sram.c index 7abc221a4aa..2d24269c316 100644 --- a/soh/src/code/z_sram.c +++ b/soh/src/code/z_sram.c @@ -22,6 +22,7 @@ void BossRush_InitSave(void); */ void Sram_InitNewSave(void) { Save_InitFile(false); + Interface_ResetExtraItemData(); } /** @@ -35,6 +36,7 @@ void Sram_InitNewSave(void) { */ void Sram_InitDebugSave(void) { Save_InitFile(true); + Interface_ResetExtraItemData(); } void Sram_InitBossRushSave(void) { @@ -77,6 +79,7 @@ void Sram_OpenSave() { u8* ptr; Save_LoadFile(); + Interface_ResetExtraItemData(); switch (gSaveContext.savedSceneNum) { case SCENE_DEKU_TREE: @@ -155,6 +158,32 @@ void Sram_OpenSave() { CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) ? gSaveContext.healthCapacity : STARTING_HEALTH; } + { + s16* secondaryHealth[] = { &gSaveContext.health2, &gSaveContext.health3, &gSaveContext.health4 }; + s16* secondaryHealthCapacity[] = { &gSaveContext.healthCapacity2, &gSaveContext.healthCapacity3, + &gSaveContext.healthCapacity4 }; + + for (i = 0; i < ARRAY_COUNT(secondaryHealth); i++) { + if (*secondaryHealthCapacity[i] < STARTING_HEALTH) { + *secondaryHealthCapacity[i] = gSaveContext.healthCapacity; + + if (*secondaryHealthCapacity[i] < STARTING_HEALTH) { + *secondaryHealthCapacity[i] = STARTING_HEALTH; + } + } + + if (*secondaryHealth[i] < STARTING_HEALTH) { + *secondaryHealth[i] = CVarGetInteger(CVAR_ENHANCEMENT("FullHealthSpawn"), 0) + ? *secondaryHealthCapacity[i] + : STARTING_HEALTH; + } + + if (*secondaryHealth[i] > *secondaryHealthCapacity[i]) { + *secondaryHealth[i] = *secondaryHealthCapacity[i]; + } + } + } + if (gSaveContext.scarecrowLongSongSet) { osSyncPrintf(VT_FGCOL(BLUE)); osSyncPrintf("\n====================================================================\n"); diff --git a/soh/src/code/z_view.c b/soh/src/code/z_view.c index 2dd05af7253..8c1ff140fdd 100644 --- a/soh/src/code/z_view.c +++ b/soh/src/code/z_view.c @@ -7,6 +7,14 @@ #include "soh/frame_interpolation.h" +#define LOCAL_MP_PLAYER_COUNT_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount") +#define LOCAL_MP_DISABLED_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Disable") + +static s32 View_IsLocalMultiplayerEnabled(void) { + return !CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0) && + (CVarGetInteger(LOCAL_MP_PLAYER_COUNT_CVAR, 2) > 1); +} + vu32 D_8012ABF0 = true; void View_ViewportToVp(Vp* dest, Viewport* src) { @@ -155,6 +163,10 @@ void func_800AA550(View* view) { varY = ShrinkWindow_GetCurrentVal(); + if (View_IsLocalMultiplayerEnabled()) { + varY = 0; + } + varX = -1; // The following is optimized to varX = 0 but affects codegen if (varX < 0) { @@ -288,6 +300,52 @@ static float sqr(float a) { return a * a; } +#define VIEW_INTERP_HISTORY_COUNT 8 + +typedef struct { + s32 valid; + s32 hasView; + Viewport viewport; + View oldView; +} ViewInterpolationHistory; + +static ViewInterpolationHistory sViewInterpolationHistory[VIEW_INTERP_HISTORY_COUNT]; + +static s32 View_ViewportMatches(Viewport* a, Viewport* b) { + return (a->topY == b->topY) && (a->bottomY == b->bottomY) && (a->leftX == b->leftX) && + (a->rightX == b->rightX); +} + +static s32 View_GetInterpolationHistorySlot(View* view) { + s32 i; + s32 freeSlot = -1; + + for (i = 0; i < VIEW_INTERP_HISTORY_COUNT; i++) { + if (sViewInterpolationHistory[i].valid) { + if (View_ViewportMatches(&sViewInterpolationHistory[i].viewport, &view->viewport)) { + return i; + } + } else if (freeSlot < 0) { + freeSlot = i; + } + } + + if (freeSlot < 0) { + freeSlot = 0; + } + + sViewInterpolationHistory[freeSlot].valid = true; + sViewInterpolationHistory[freeSlot].hasView = false; + sViewInterpolationHistory[freeSlot].viewport = view->viewport; + + return freeSlot; +} + +static s32 View_IsFullScreenViewport(Viewport* viewport) { + return (viewport->topY == 0) && (viewport->bottomY == SCREEN_HEIGHT) && (viewport->leftX == 0) && + (viewport->rightX == SCREEN_WIDTH); +} + s32 func_800AAA9C(View* view) { f32 aspect; s32 width; @@ -336,69 +394,96 @@ s32 func_800AAA9C(View* view) { guLookAtF(viewingF.mf, view->eye.x, view->eye.y, view->eye.z, view->lookAt.x, view->lookAt.y, view->lookAt.z, view->up.x, view->up.y, view->up.z); - // Some heuristics to identify instant camera movements and skip interpolation in that case - - static View old_view; + // Some heuristics to identify instant camera movements and skip interpolation in that case. + // Split-screen draws multiple cameras per frame, so keep prior-view history per viewport. + s32 historySlot = View_GetInterpolationHistorySlot(view); + ViewInterpolationHistory* viewHistory = &sViewInterpolationHistory[historySlot]; + View* old_view = &viewHistory->oldView; + s32 hasOldView = viewHistory->hasView; float dirx = view->eye.x - view->lookAt.x; float diry = view->eye.y - view->lookAt.y; float dirz = view->eye.z - view->lookAt.z; float dir_dist = sqrtf(sqr(dirx) + sqr(diry) + sqr(dirz)); + + if (dir_dist <= 0.0f) { + dir_dist = 1.0f; + } + dirx /= dir_dist; diry /= dir_dist; dirz /= dir_dist; - float odirx = old_view.eye.x - old_view.lookAt.x; - float odiry = old_view.eye.y - old_view.lookAt.y; - float odirz = old_view.eye.z - old_view.lookAt.z; - float odir_dist = sqrtf(sqr(odirx) + sqr(odiry) + sqr(odirz)); - odirx /= odir_dist; - odiry /= odir_dist; - odirz /= odir_dist; - - float eye_dist = sqrtf(sqr(view->eye.x - old_view.eye.x) + sqr(view->eye.y - old_view.eye.y) + - sqr(view->eye.z - old_view.eye.z)); - float look_dist = sqrtf(sqr(view->lookAt.x - old_view.lookAt.x) + sqr(view->lookAt.y - old_view.lookAt.y) + - sqr(view->lookAt.z - old_view.lookAt.z)); - float up_dist = - sqrtf(sqr(view->up.x - old_view.up.x) + sqr(view->up.y - old_view.up.y) + sqr(view->up.z - old_view.up.z)); - float d_dist = sqrtf(sqr(dirx - odirx) + sqr(diry - odiry) + sqr(dirz - odirz)); + float odirx = dirx; + float odiry = diry; + float odirz = dirz; + float eye_dist = 0.0f; + float look_dist = 0.0f; + float up_dist = 0.0f; + float d_dist = 0.0f; bool dont_interpolate = false; - if (up_dist < 0.01 && d_dist < 0.01) { - if (eye_dist + look_dist > 300) { - dont_interpolate = true; - } - } else { - if (eye_dist >= 400) { - dont_interpolate = true; - } - if (look_dist >= 100) { - dont_interpolate = true; - } - if (up_dist >= 1.50f) { - dont_interpolate = true; - } - if (d_dist >= 1.414f && look_dist >= 15) { - dont_interpolate = true; - } - if (d_dist >= 1.414f && up_dist >= 0.31f && look_dist >= 1 && eye_dist >= 300) { - dont_interpolate = true; - } - if (d_dist >= 0.5f && up_dist >= 0.31f && look_dist >= 3 && eye_dist >= 170) { - dont_interpolate = true; - } - if (look_dist >= 52 && eye_dist >= 52) { - dont_interpolate = true; + if (hasOldView) { + float odir_dist; + + odirx = old_view->eye.x - old_view->lookAt.x; + odiry = old_view->eye.y - old_view->lookAt.y; + odirz = old_view->eye.z - old_view->lookAt.z; + odir_dist = sqrtf(sqr(odirx) + sqr(odiry) + sqr(odirz)); + + if (odir_dist <= 0.0f) { + odir_dist = 1.0f; } - if (look_dist >= 30 && eye_dist >= 90) { - dont_interpolate = true; + + odirx /= odir_dist; + odiry /= odir_dist; + odirz /= odir_dist; + + eye_dist = sqrtf(sqr(view->eye.x - old_view->eye.x) + sqr(view->eye.y - old_view->eye.y) + + sqr(view->eye.z - old_view->eye.z)); + look_dist = sqrtf(sqr(view->lookAt.x - old_view->lookAt.x) + sqr(view->lookAt.y - old_view->lookAt.y) + + sqr(view->lookAt.z - old_view->lookAt.z)); + up_dist = + sqrtf(sqr(view->up.x - old_view->up.x) + sqr(view->up.y - old_view->up.y) + sqr(view->up.z - old_view->up.z)); + d_dist = sqrtf(sqr(dirx - odirx) + sqr(diry - odiry) + sqr(dirz - odirz)); + + if (up_dist < 0.01 && d_dist < 0.01) { + if (eye_dist + look_dist > 300) { + dont_interpolate = true; + } + } else { + if (eye_dist >= 400) { + dont_interpolate = true; + } + if (look_dist >= 100) { + dont_interpolate = true; + } + if (up_dist >= 1.50f) { + dont_interpolate = true; + } + if (d_dist >= 1.414f && look_dist >= 15) { + dont_interpolate = true; + } + if (d_dist >= 1.414f && up_dist >= 0.31f && look_dist >= 1 && eye_dist >= 300) { + dont_interpolate = true; + } + if (d_dist >= 0.5f && up_dist >= 0.31f && look_dist >= 3 && eye_dist >= 170) { + dont_interpolate = true; + } + if (look_dist >= 52 && eye_dist >= 52) { + dont_interpolate = true; + } + if (look_dist >= 30 && eye_dist >= 90) { + dont_interpolate = true; + } } } - // Ignore camera heuristics when paused as the camera moves a lot in Kaleido, allowing it to be interpolate - if (dont_interpolate && R_PAUSE_MENU_MODE == 0) { + // Ignore camera heuristics when paused as the camera moves a lot in Kaleido, allowing it to be interpolate. + // In split-screen, avoid forcing camera epoch resets per viewport because that can desync interpolation. + if (dont_interpolate && (R_PAUSE_MENU_MODE == 0) && + (!View_IsLocalMultiplayerEnabled() || View_IsFullScreenViewport(&view->viewport))) { FrameInterpolation_DontInterpolateCamera(); } @@ -460,7 +545,8 @@ s32 func_800AAA9C(View* view) { view->eye.z, view->lookAt.x, view->lookAt.y, view->lookAt.z, view->up.x, view->up.y, view->up.z, eye_dist, look_dist, up_dist, d_dist, dont_interpolate);*/ - old_view = *view; + viewHistory->oldView = *view; + viewHistory->hasView = true; if (QREG(88) & 2) { s32 i; diff --git a/soh/src/overlays/actors/ovl_Arrow_Fire/z_arrow_fire.c b/soh/src/overlays/actors/ovl_Arrow_Fire/z_arrow_fire.c index 9f3efde5122..1354b9f4377 100644 --- a/soh/src/overlays/actors/ovl_Arrow_Fire/z_arrow_fire.c +++ b/soh/src/overlays/actors/ovl_Arrow_Fire/z_arrow_fire.c @@ -41,6 +41,42 @@ void ArrowFire_SetupAction(ArrowFire* this, ArrowFireActionFunc actionFunc) { this->actionFunc = actionFunc; } +static Player* ArrowFire_FindPlayerByPort(PlayState* play, u8 controllerPort) { + Actor* actor; + + if (controllerPort == 1) { + return GET_PLAYER(play); + } + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* candidate = (Player*)actor; + + if (candidate->isSecondPlayer && (candidate->controllerPort == controllerPort)) { + return candidate; + } + } + + actor = actor->next; + } + + return GET_PLAYER(play); +} + +static Player* ArrowFire_GetShootingPlayer(PlayState* play, Actor* arrowActor) { + if ((arrowActor != NULL) && (arrowActor->parent != NULL) && (arrowActor->parent->id == ACTOR_PLAYER) && + (arrowActor->parent->update != NULL)) { + return (Player*)arrowActor->parent; + } + + if (arrowActor != NULL) { + return ArrowFire_FindPlayerByPort(play, arrowActor->home.rot.z); + } + + return GET_PLAYER(play); +} + void ArrowFire_Init(Actor* thisx, PlayState* play) { ArrowFire* this = (ArrowFire*)thisx; @@ -55,7 +91,8 @@ void ArrowFire_Init(Actor* thisx, PlayState* play) { } void ArrowFire_Destroy(Actor* thisx, PlayState* play) { - Magic_Reset(play); + ArrowFire* this = (ArrowFire*)thisx; + Magic_ResetForPlayer(play, ArrowFire_GetShootingPlayer(play, this->actor.parent)); LOG_STRING("消滅"); // "Disappearance" } diff --git a/soh/src/overlays/actors/ovl_Arrow_Ice/z_arrow_ice.c b/soh/src/overlays/actors/ovl_Arrow_Ice/z_arrow_ice.c index 53f8107a168..df8b2e14ab5 100644 --- a/soh/src/overlays/actors/ovl_Arrow_Ice/z_arrow_ice.c +++ b/soh/src/overlays/actors/ovl_Arrow_Ice/z_arrow_ice.c @@ -42,6 +42,42 @@ void ArrowIce_SetupAction(ArrowIce* this, ArrowIceActionFunc actionFunc) { this->actionFunc = actionFunc; } +static Player* ArrowIce_FindPlayerByPort(PlayState* play, u8 controllerPort) { + Actor* actor; + + if (controllerPort == 1) { + return GET_PLAYER(play); + } + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* candidate = (Player*)actor; + + if (candidate->isSecondPlayer && (candidate->controllerPort == controllerPort)) { + return candidate; + } + } + + actor = actor->next; + } + + return GET_PLAYER(play); +} + +static Player* ArrowIce_GetShootingPlayer(PlayState* play, Actor* arrowActor) { + if ((arrowActor != NULL) && (arrowActor->parent != NULL) && (arrowActor->parent->id == ACTOR_PLAYER) && + (arrowActor->parent->update != NULL)) { + return (Player*)arrowActor->parent; + } + + if (arrowActor != NULL) { + return ArrowIce_FindPlayerByPort(play, arrowActor->home.rot.z); + } + + return GET_PLAYER(play); +} + void ArrowIce_Init(Actor* thisx, PlayState* play) { ArrowIce* this = (ArrowIce*)thisx; @@ -56,7 +92,8 @@ void ArrowIce_Init(Actor* thisx, PlayState* play) { } void ArrowIce_Destroy(Actor* thisx, PlayState* play) { - Magic_Reset(play); + ArrowIce* this = (ArrowIce*)thisx; + Magic_ResetForPlayer(play, ArrowIce_GetShootingPlayer(play, this->actor.parent)); LOG_STRING("消滅"); // "Disappearance" } diff --git a/soh/src/overlays/actors/ovl_Arrow_Light/z_arrow_light.c b/soh/src/overlays/actors/ovl_Arrow_Light/z_arrow_light.c index ee2cfc95347..20fa6af3979 100644 --- a/soh/src/overlays/actors/ovl_Arrow_Light/z_arrow_light.c +++ b/soh/src/overlays/actors/ovl_Arrow_Light/z_arrow_light.c @@ -42,6 +42,42 @@ void ArrowLight_SetupAction(ArrowLight* this, ArrowLightActionFunc actionFunc) { this->actionFunc = actionFunc; } +static Player* ArrowLight_FindPlayerByPort(PlayState* play, u8 controllerPort) { + Actor* actor; + + if (controllerPort == 1) { + return GET_PLAYER(play); + } + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* candidate = (Player*)actor; + + if (candidate->isSecondPlayer && (candidate->controllerPort == controllerPort)) { + return candidate; + } + } + + actor = actor->next; + } + + return GET_PLAYER(play); +} + +static Player* ArrowLight_GetShootingPlayer(PlayState* play, Actor* arrowActor) { + if ((arrowActor != NULL) && (arrowActor->parent != NULL) && (arrowActor->parent->id == ACTOR_PLAYER) && + (arrowActor->parent->update != NULL)) { + return (Player*)arrowActor->parent; + } + + if (arrowActor != NULL) { + return ArrowLight_FindPlayerByPort(play, arrowActor->home.rot.z); + } + + return GET_PLAYER(play); +} + void ArrowLight_Init(Actor* thisx, PlayState* play) { ArrowLight* this = (ArrowLight*)thisx; @@ -56,7 +92,8 @@ void ArrowLight_Init(Actor* thisx, PlayState* play) { } void ArrowLight_Destroy(Actor* thisx, PlayState* play) { - Magic_Reset(play); + ArrowLight* this = (ArrowLight*)thisx; + Magic_ResetForPlayer(play, ArrowLight_GetShootingPlayer(play, this->actor.parent)); LOG_STRING("消滅"); // "Disappearance" } diff --git a/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.c b/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.c index 3f9b424abca..988a0b6740d 100644 --- a/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.c +++ b/soh/src/overlays/actors/ovl_Bg_Breakwall/z_bg_breakwall.c @@ -8,8 +8,6 @@ #include "scenes/dungeons/ddan/ddan_scene.h" #include "objects/object_bwall/object_bwall.h" #include "objects/object_kingdodongo/object_kingdodongo.h" -#include "soh/OTRGlobals.h" -#include "soh/Enhancements/game-interactor/GameInteractor.h" #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS ACTOR_FLAG_UPDATE_CULLING_DISABLED @@ -62,27 +60,6 @@ static ColliderQuadInit sQuadInit = { { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, }; -// Replacement quad used for "Blue Fire Arrows" enhancement -static ColliderQuadInit sIceArrowQuadInit = { - { - COLTYPE_NONE, - AT_NONE, - AC_ON | AC_TYPE_PLAYER | AC_TYPE_OTHER, - OC1_NONE, - OC2_TYPE_2, - COLSHAPE_QUAD, - }, - { - ELEMTYPE_UNK0, - { 0x00000048, 0x00, 0x00 }, - { 0x00001048, 0x00, 0x00 }, - TOUCH_NONE, - BUMP_ON, - OCELEM_NONE, - }, - { { { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f }, { 0.0f, 0.0f, 0.0f } } }, -}; - static BombableWallInfo sBombableWallInfo[] = { { &object_bwall_Col_000118, object_bwall_DL_000040, 0 }, { &object_bwall_Col_000118, object_bwall_DL_000040, 0 }, @@ -97,8 +74,6 @@ static InitChainEntry sInitChain[] = { ICHAIN_F32(uncullZoneDownward, 400, ICHAIN_STOP), }; -bool blueFireArrowsEnabledOnMudwallLoad = false; - void BgBreakwall_SetupAction(BgBreakwall* this, BgBreakwallActionFunc actionFunc) { this->actionFunc = actionFunc; } @@ -108,10 +83,6 @@ void BgBreakwall_Init(Actor* thisx, PlayState* play) { s32 pad; s32 wallType = ((this->dyna.actor.params >> 13) & 3) & 0xFF; - // Initialize this with the mud wall, so it can't be affected by toggling while the actor is loaded - blueFireArrowsEnabledOnMudwallLoad = CVarGetInteger(CVAR_ENHANCEMENT("BlueFireArrows"), 0) || - (IS_RANDO && Randomizer_GetSettingValue(RSK_BLUE_FIRE_ARROWS)); - Actor_ProcessInitChain(&this->dyna.actor, sInitChain); DynaPolyActor_Init(&this->dyna, DPM_UNK); this->bombableWallDList = sBombableWallInfo[wallType].dList; @@ -129,14 +100,8 @@ void BgBreakwall_Init(Actor* thisx, PlayState* play) { ActorShape_Init(&this->dyna.actor.shape, 0.0f, NULL, 0.0f); - // If "Blue Fire Arrows" are enabled, set up this collider for them - if (blueFireArrowsEnabledOnMudwallLoad) { - Collider_InitQuad(play, &this->collider); - Collider_SetQuad(play, &this->collider, &this->dyna.actor, &sIceArrowQuadInit); - } else { - Collider_InitQuad(play, &this->collider); - Collider_SetQuad(play, &this->collider, &this->dyna.actor, &sQuadInit); - } + Collider_InitQuad(play, &this->collider); + Collider_SetQuad(play, &this->collider, &this->dyna.actor, &sQuadInit); } else { this->dyna.actor.world.pos.y -= 40.0f; } @@ -263,20 +228,7 @@ void BgBreakwall_WaitForObject(BgBreakwall* this, PlayState* play) { * despawn itself. */ void BgBreakwall_Wait(BgBreakwall* this, PlayState* play) { - bool blueFireArrowHit = false; - // If "Blue Fire Arrows" enabled, check this collider for a hit - if (blueFireArrowsEnabledOnMudwallLoad) { - if (this->collider.base.acFlags & AC_HIT) { - if ((this->collider.base.ac != NULL) && (this->collider.base.ac->id == ACTOR_EN_ARROW)) { - - if (this->collider.base.ac->child != NULL && this->collider.base.ac->child->id == ACTOR_ARROW_ICE) { - blueFireArrowHit = true; - } - } - } - } - - if (GameInteractor_Should(VB_BG_BREAKWALL_BREAK, this->collider.base.acFlags & 2 || blueFireArrowHit)) { + if (GameInteractor_Should(VB_BG_BREAKWALL_BREAK, this->collider.base.acFlags & AC_HIT, this)) { Vec3f effectPos; s32 wallType = ((this->dyna.actor.params >> 13) & 3) & 0xFF; diff --git a/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.c b/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.c index eb267edcb60..0ce95602a2b 100644 --- a/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.c +++ b/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.c @@ -1,6 +1,6 @@ #include "z_bg_ice_shelter.h" #include "objects/object_ice_objects/object_ice_objects.h" -#include "soh/OTRGlobals.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS 0 @@ -9,14 +9,11 @@ void BgIceShelter_Destroy(Actor* thisx, PlayState* play); void BgIceShelter_Update(Actor* thisx, PlayState* play); void BgIceShelter_Draw(Actor* thisx, PlayState* play); -void func_80891064(BgIceShelter* this); -void func_808911BC(BgIceShelter* this); +void BgIceShelter_SetupIdle(BgIceShelter* this); +void BgIceShelter_SetupMelt(BgIceShelter* this); -void func_8089107C(BgIceShelter* this, PlayState* play); -void func_808911D4(BgIceShelter* this, PlayState* play); - -// For "Blue Fire Arrows" enhancement -void MeltOnIceArrowHit(BgIceShelter* this, ColliderCylinder cylinder, s16 type, PlayState* play); +void BgIceShelter_Idle(BgIceShelter* this, PlayState* play); +void BgIceShelter_Melt(BgIceShelter* this, PlayState* play); const ActorInit Bg_Ice_Shelter_InitVars = { ACTOR_BG_ICE_SHELTER, @@ -31,10 +28,10 @@ const ActorInit Bg_Ice_Shelter_InitVars = { NULL, }; -static f32 sScales[] = { 0.1f, 0.06f, 0.1f, 0.1f, 0.25f }; +static f32 sRedIceScales[] = { 0.1f, 0.06f, 0.1f, 0.1f, 0.25f }; -static Color_RGBA8 sDustPrimColor = { 250, 250, 250, 255 }; -static Color_RGBA8 sDustEnvColor = { 180, 180, 180, 255 }; +static Color_RGBA8 sSteamPrimColor = { 250, 250, 250, 255 }; +static Color_RGBA8 sSteamEnvColor = { 180, 180, 180, 255 }; static ColliderCylinderInit sCylinder1Init = { { @@ -76,52 +73,24 @@ static ColliderCylinderInit sCylinder2Init = { { 0, 0, 0, { 0, 0, 0 } }, }; -// This cylinder only used for "Blue Fire Arrows" enhancement -static ColliderCylinderInit sIceArrowCylinderInit = { - { - COLTYPE_NONE, - AT_NONE, - AC_ON | AC_TYPE_OTHER | AC_TYPE_PLAYER, - OC1_ON | OC1_TYPE_ALL, - OC2_TYPE_2, - COLSHAPE_CYLINDER, - }, - { - ELEMTYPE_UNK0, - { 0x00000000, 0x00, 0x00 }, - { 0xFFCFFFFF, 0x00, 0x00 }, - TOUCH_NONE, - BUMP_ON, - OCELEM_ON, - }, - { 0, 0, 0, { 0, 0, 0 } }, -}; - -bool blueFireArrowsEnabledOnRedIceLoad = false; - -void func_80890740(BgIceShelter* this, PlayState* play) { +/** + * Initializes either one or both cylinder colliders, depending on the actor's type. + */ +void BgIceShelter_InitColliders(BgIceShelter* this, PlayState* play) { static s16 cylinderRadii[] = { 47, 33, 44, 41, 100 }; static s16 cylinderHeights[] = { 80, 54, 90, 60, 200 }; s32 pad; s32 type = (this->dyna.actor.params >> 8) & 7; - // Initialize this with the red ice, so it can't be affected by toggling while the actor is loaded - blueFireArrowsEnabledOnRedIceLoad = CVarGetInteger(CVAR_ENHANCEMENT("BlueFireArrows"), 0) || - (IS_RANDO && Randomizer_GetSettingValue(RSK_BLUE_FIRE_ARROWS)); - Collider_InitCylinder(play, &this->cylinder1); - // If "Blue Fire Arrows" is enabled, set up a collider on the red ice that responds to them - if (blueFireArrowsEnabledOnRedIceLoad) { - Collider_SetCylinder(play, &this->cylinder1, &this->dyna.actor, &sIceArrowCylinderInit); - } else { - Collider_SetCylinder(play, &this->cylinder1, &this->dyna.actor, &sCylinder1Init); - } + Collider_SetCylinder(play, &this->cylinder1, &this->dyna.actor, &sCylinder1Init); Collider_UpdateCylinder(&this->dyna.actor, &this->cylinder1); this->cylinder1.dim.radius = cylinderRadii[type]; this->cylinder1.dim.height = cylinderHeights[type]; - if (type == 0 || type == 1 || type == 4) { + // The wall and platform types use DynaPoly for collision, so they don't need the second collider + if (type == RED_ICE_LARGE || type == RED_ICE_SMALL || type == RED_ICE_KING_ZORA) { Collider_InitCylinder(play, &this->cylinder2); Collider_SetCylinder(play, &this->cylinder2, &this->dyna.actor, &sCylinder2Init); Collider_UpdateCylinder(&this->dyna.actor, &this->cylinder2); @@ -129,13 +98,13 @@ void func_80890740(BgIceShelter* this, PlayState* play) { this->cylinder2.dim.height = cylinderHeights[type]; } - if (type == 4) { + if (type == RED_ICE_KING_ZORA) { this->cylinder1.dim.pos.z += 30; this->cylinder2.dim.pos.z += 30; } } -void func_80890874(BgIceShelter* this, PlayState* play, CollisionHeader* collision, s32 moveFlag) { +void BgIceShelter_InitDynaPoly(BgIceShelter* this, PlayState* play, CollisionHeader* collision, s32 moveFlag) { s32 pad; CollisionHeader* colHeader = NULL; s32 pad2; @@ -151,7 +120,7 @@ void func_80890874(BgIceShelter* this, PlayState* play, CollisionHeader* collisi } } -void func_808908FC(Vec3f* dest, Vec3f* src, s16 angle) { +void BgIceShelter_RotateY(Vec3f* dest, Vec3f* src, s16 angle) { f32 sin = Math_SinS(angle); f32 cos = Math_CosS(angle); @@ -173,38 +142,42 @@ void BgIceShelter_Init(Actor* thisx, PlayState* play) { Actor_ProcessInitChain(&this->dyna.actor, sInitChain); - if (type == 4) { + if (type == RED_ICE_KING_ZORA) { this->dyna.actor.world.rot.x += 0xBB8; this->dyna.actor.world.pos.y -= 45.0f; this->dyna.actor.shape.rot.x = this->dyna.actor.world.rot.x; this->dyna.actor.world.pos.z -= 38.0f; } - if (type == 4) { + if (type == RED_ICE_KING_ZORA) { Math_Vec3f_Copy(&this->dyna.actor.scale, &kzIceScale); } else { - Actor_SetScale(&this->dyna.actor, sScales[type]); + Actor_SetScale(&this->dyna.actor, sRedIceScales[type]); } + // Only 2 types use DynaPoly switch (type) { - case 2: - func_80890874(this, play, &gRedIcePlatformCol, 0); + case RED_ICE_PLATFORM: + BgIceShelter_InitDynaPoly(this, play, &gRedIcePlatformCol, 0); break; - case 3: - func_80890874(this, play, &gRedIceWallCol, 0); + case RED_ICE_WALL: + BgIceShelter_InitDynaPoly(this, play, &gRedIceWallCol, 0); break; } - func_80890740(this, play); + // All types use at least one collider + BgIceShelter_InitColliders(this, play); this->dyna.actor.colChkInfo.mass = MASS_IMMOVABLE; - if (!((this->dyna.actor.params >> 6) & 1) && (Flags_GetSwitch(play, this->dyna.actor.params & 0x3F))) { + if (GameInteractor_Should( + VB_RED_ICE_MELTED_FLAG, + !((this->dyna.actor.params >> 6) & 1) && (Flags_GetSwitch(play, this->dyna.actor.params & 0x3F)), this)) { Actor_Kill(&this->dyna.actor); return; } - func_80891064(this); + BgIceShelter_SetupIdle(this); osSyncPrintf("(ice shelter)(arg_data 0x%04x)\n", this->dyna.actor.params); } @@ -213,14 +186,14 @@ void BgIceShelter_Destroy(Actor* thisx, PlayState* play) { BgIceShelter* this = (BgIceShelter*)thisx; switch ((this->dyna.actor.params >> 8) & 7) { - case 2: - case 3: + case RED_ICE_PLATFORM: + case RED_ICE_WALL: DynaPoly_DeleteBgActor(play, &play->colCtx.dyna, this->dyna.bgId); break; - case 0: - case 1: - case 4: + case RED_ICE_LARGE: + case RED_ICE_SMALL: + case RED_ICE_KING_ZORA: Collider_DestroyCylinder(play, &this->cylinder2); break; } @@ -228,131 +201,153 @@ void BgIceShelter_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyCylinder(play, &this->cylinder1); } -static s16 D_80891794[] = { 0x0000, 0x4000, 0x2000, 0x6000, 0x1000, 0x5000, 0x3000, 0x7000 }; -static s16 D_808917A4[] = { 0x0000, 0x003C, 0x0018, 0x0054, 0x0030, 0x000C, 0x0048, 0x0024 }; - -void func_80890B8C(BgIceShelter* this, PlayState* play, f32 chance, f32 scale) { +/** + * Angles used to spawn steam particles in a circle. + */ +static s16 sSteamCircleAngles[] = { 0x0000, 0x4000, 0x2000, 0x6000, 0x1000, 0x5000, 0x3000, 0x7000 }; + +/** + * Positions used to spawn steam particles in a straight line. + */ +static s16 sSteamLinePositions[] = { 0x0000, 0x003C, 0x0018, 0x0054, 0x0030, 0x000C, 0x0048, 0x0024 }; + +/** + * Spawns steam particle effects in a circle around the ice block. + * + * On each frame the function is called, two particles have a chance to appear, at the same distance and opposite + * sides from the center. + */ +void BgIceShelter_SpawnSteamAround(BgIceShelter* this, PlayState* play, f32 particleSpawningChance, + f32 steamEffectScale) { f32 cos; f32 sin; - f32 xzOffset; + f32 distance; Vec3f* icePos; s16 angle; - s16 frames; + s16 frameCounter; s32 i; s32 pad[2]; - Vec3f dustPos; - Vec3f dustVel; - Vec3f dustAccel; + Vec3f steamPos; + Vec3f steamVel; + Vec3f steamAccel; - frames = (s16)play->state.frames & 7; + frameCounter = (s16)play->state.frames & 7; for (i = 0; i < 2; i++) { - if (chance < Rand_ZeroOne()) { + if (particleSpawningChance < Rand_ZeroOne()) { continue; } - xzOffset = 42.0f * scale; + // The steamEffectScale is used here to make the particles appear at the edges of the red ice. + distance = 42.0f * steamEffectScale; icePos = &this->dyna.actor.world.pos; - angle = D_80891794[frames] + (i * 0x8000); + angle = sSteamCircleAngles[frameCounter] + (i * 0x8000); sin = Math_SinS(angle); cos = Math_CosS(angle); - dustPos.x = (xzOffset * sin) + icePos->x; - dustPos.y = (16.0f * scale) + icePos->y; - dustPos.z = (xzOffset * cos) + icePos->z; + steamPos.x = (distance * sin) + icePos->x; + steamPos.y = (16.0f * steamEffectScale) + icePos->y; + steamPos.z = (distance * cos) + icePos->z; - dustVel.x = ((Rand_ZeroOne() * 3.0f) - 1.0f) * sin; - dustVel.y = 0.0f; - dustVel.z = ((Rand_ZeroOne() * 3.0f) - 1.0f) * cos; + steamVel.x = ((Rand_ZeroOne() * 3.0f) - 1.0f) * sin; + steamVel.y = 0.0f; + steamVel.z = ((Rand_ZeroOne() * 3.0f) - 1.0f) * cos; - dustAccel.x = 0.07f * sin; - dustAccel.y = 0.8f; - dustAccel.z = 0.07f * cos; + steamAccel.x = 0.07f * sin; + steamAccel.y = 0.8f; + steamAccel.z = 0.07f * cos; - func_8002829C(play, &dustPos, &dustVel, &dustAccel, &sDustPrimColor, &sDustEnvColor, 450.0f * scale, - (s16)((Rand_ZeroOne() * 40.0f) + 40.0f) * scale); + func_8002829C(play, &steamPos, &steamVel, &steamAccel, &sSteamPrimColor, &sSteamEnvColor, + 450.0f * steamEffectScale, (s16)((Rand_ZeroOne() * 40.0f) + 40.0f) * steamEffectScale); } } -void func_80890E00(BgIceShelter* this, PlayState* play, f32 chance, f32 arg3) { - static f32 D_808917B4[] = { -1.0f, 1.0f }; +/** + * Spawns steam particle effects in a straight line. Only used for the ice wall type. + * + * On each frame the function is called, two particles have a chance to appear, at the same distance and opposite + * sides from the midpoint. + * + * The last argument is unused because only one red ice type can call this function, so the scale isn't needed. + */ +void BgIceShelter_SpawnSteamAlong(BgIceShelter* this, PlayState* play, f32 particleSpawningChance, f32 unusedArg) { + static f32 signs[] = { -1.0f, 1.0f }; Vec3f* icePos; - s16 frames; + s16 frameCounter; s32 pad[2]; - Vec3f dustPos; - Vec3f dustVel; - Vec3f dustAccel; + Vec3f steamPos; + Vec3f steamVel; + Vec3f steamAccel; Vec3f posOffset; s32 i; - frames = (s16)play->state.frames & 7; + frameCounter = (s16)play->state.frames & 7; for (i = 0; i < 2; i++) { icePos = &this->dyna.actor.world.pos; - if (chance < Rand_ZeroOne()) { + if (particleSpawningChance < Rand_ZeroOne()) { continue; } - posOffset.x = (D_808917A4[frames] + ((Rand_ZeroOne() * 12.0f) - 6.0f)) * D_808917B4[i]; + posOffset.x = (sSteamLinePositions[frameCounter] + ((Rand_ZeroOne() * 12.0f) - 6.0f)) * signs[i]; posOffset.y = 15.0f; posOffset.z = ((84.0f - posOffset.x) * 0.2f) + (Rand_ZeroOne() * 20.0f); - func_808908FC(&dustPos, &posOffset, this->dyna.actor.world.rot.y); - Math_Vec3f_Sum(&dustPos, icePos, &dustPos); + // Convert the position offset from relative to the ice wall to absolute. + BgIceShelter_RotateY(&steamPos, &posOffset, this->dyna.actor.world.rot.y); + Math_Vec3f_Sum(&steamPos, icePos, &steamPos); - dustVel.x = (Rand_ZeroOne() * 3.0f) - 1.5f; - dustVel.y = 0.0f; - dustVel.z = (Rand_ZeroOne() * 3.0f) - 1.5f; + steamVel.x = (Rand_ZeroOne() * 3.0f) - 1.5f; + steamVel.y = 0.0f; + steamVel.z = (Rand_ZeroOne() * 3.0f) - 1.5f; - dustAccel.x = (Rand_ZeroOne() * 0.14f) - 0.07f; - dustAccel.y = 0.8f; - dustAccel.z = (Rand_ZeroOne() * 0.14f) - 0.07f; + steamAccel.x = (Rand_ZeroOne() * 0.14f) - 0.07f; + steamAccel.y = 0.8f; + steamAccel.z = (Rand_ZeroOne() * 0.14f) - 0.07f; - func_8002829C(play, &dustPos, &dustVel, &dustAccel, &sDustPrimColor, &sDustEnvColor, 450, + func_8002829C(play, &steamPos, &steamVel, &steamAccel, &sSteamPrimColor, &sSteamEnvColor, 450, (Rand_ZeroOne() * 40.0f) + 40.0f); } } -void func_80891064(BgIceShelter* this) { - this->actionFunc = func_8089107C; +void BgIceShelter_SetupIdle(BgIceShelter* this) { + this->actionFunc = BgIceShelter_Idle; this->alpha = 255; } -void func_8089107C(BgIceShelter* this, PlayState* play) { +void BgIceShelter_Idle(BgIceShelter* this, PlayState* play) { s32 pad; s16 type = (this->dyna.actor.params >> 8) & 7; - if (type == 4) { + // Freeze King Zora + if (type == RED_ICE_KING_ZORA) { if (this->dyna.actor.parent != NULL) { this->dyna.actor.parent->freezeTimer = 10000; } } - // If we have "Blue Fire Arrows" enabled, check both cylinders for a hit - if (blueFireArrowsEnabledOnRedIceLoad) { - MeltOnIceArrowHit(this, this->cylinder1, type, play); - MeltOnIceArrowHit(this, this->cylinder2, type, play); - } - // Default blue fire check - if (this->cylinder1.base.acFlags & AC_HIT) { + + if (GameInteractor_Should(VB_BG_ICE_SHELTER_HIT, this->cylinder1.base.acFlags & AC_HIT, this)) { this->cylinder1.base.acFlags &= ~AC_HIT; - if ((this->cylinder1.base.ac != NULL) && (this->cylinder1.base.ac->id == ACTOR_EN_ICE_HONO)) { - if (type == 4) { + if (GameInteractor_Should( + VB_BG_ICE_SHELTER_MELT, + (this->cylinder1.base.ac != NULL) && (this->cylinder1.base.ac->id == ACTOR_EN_ICE_HONO), this)) { + if (type == RED_ICE_KING_ZORA) { if (this->dyna.actor.parent != NULL) { this->dyna.actor.parent->freezeTimer = 50; } } - func_808911BC(this); + BgIceShelter_SetupMelt(this); Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_ICE_MELT); } } switch (type) { - case 0: - case 1: - case 4: + case RED_ICE_LARGE: + case RED_ICE_SMALL: + case RED_ICE_KING_ZORA: CollisionCheck_SetOC(play, &play->colChkCtx, &this->cylinder1.base); CollisionCheck_SetAC(play, &play->colChkCtx, &this->cylinder2.base); break; @@ -361,54 +356,50 @@ void func_8089107C(BgIceShelter* this, PlayState* play) { CollisionCheck_SetAC(play, &play->colChkCtx, &this->cylinder1.base); } -// For "Blue Fire Arrows" enhancement: If hit by an Ice Arrow, melt the red ice (copied from the default blue fire -// function above). -void MeltOnIceArrowHit(BgIceShelter* this, ColliderCylinder cylinder, s16 type, PlayState* play) { - if (cylinder.base.acFlags & AC_HIT) { - cylinder.base.acFlags &= ~AC_HIT; - if ((cylinder.base.ac != NULL) && (cylinder.base.ac->id == ACTOR_EN_ARROW)) { - if (cylinder.base.ac->child != NULL && cylinder.base.ac->child->id == ACTOR_ARROW_ICE) { - if (type == 4) { - if (this->dyna.actor.parent != NULL) { - this->dyna.actor.parent->freezeTimer = 50; - } - } - func_808911BC(this); - Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_ICE_MELT); - } - } - } -} - -void func_808911BC(BgIceShelter* this) { - this->actionFunc = func_808911D4; +void BgIceShelter_SetupMelt(BgIceShelter* this) { + this->actionFunc = BgIceShelter_Melt; this->alpha = 255; } -static f32 D_808917BC[] = { -0.0015f, -0.0009f, -0.0016f, -0.0016f, -0.00375f }; -static f32 D_808917D0[] = { 1.0f, 0.6f, 1.2f, 1.0f, 1.8f }; - -static void (*sEffSpawnFuncs[])(BgIceShelter* this, PlayState* play, f32 chance, f32 scale) = { - func_80890B8C, func_80890B8C, func_80890B8C, func_80890E00, func_80890B8C, +/** + * Values added to the ice block's height every frame while it's melting. + */ +static f32 sMeltingRates[] = { -0.0015f, -0.0009f, -0.0016f, -0.0016f, -0.00375f }; + +/** + * Values used to scale and position the steam effects so they match the ice block's size. + */ +static f32 sSteamEffectScales[] = { 1.0f, 0.6f, 1.2f, 1.0f, 1.8f }; + +/** + * Functions used to spawn steam effects at the base of the red ice. + */ +static void (*sSteamSpawnFuncs[])(BgIceShelter* this, PlayState* play, f32 particleSpawningChance, + f32 steamEffectScale) = { + BgIceShelter_SpawnSteamAround, BgIceShelter_SpawnSteamAround, BgIceShelter_SpawnSteamAround, + BgIceShelter_SpawnSteamAlong, BgIceShelter_SpawnSteamAround, }; -void func_808911D4(BgIceShelter* this, PlayState* play) { +/** + * Progressively reduces the height and opacity of the red ice, while spawning steam effects at its base. + */ +void BgIceShelter_Melt(BgIceShelter* this, PlayState* play) { s32 pad; s32 type = (this->dyna.actor.params >> 8) & 7; - f32 phi_f0; + f32 particleSpawningChance; this->alpha -= 5; this->alpha = CLAMP(this->alpha, 0, 255); - this->dyna.actor.scale.y += D_808917BC[type]; + this->dyna.actor.scale.y += sMeltingRates[type]; this->dyna.actor.scale.y = CLAMP_MIN(this->dyna.actor.scale.y, 0.0001f); if (this->alpha > 80) { switch (type) { - case 0: - case 1: - case 4: + case RED_ICE_LARGE: + case RED_ICE_SMALL: + case RED_ICE_KING_ZORA: CollisionCheck_SetOC(play, &play->colChkCtx, &this->cylinder1.base); CollisionCheck_SetAC(play, &play->colChkCtx, &this->cylinder2.base); break; @@ -416,16 +407,16 @@ void func_808911D4(BgIceShelter* this, PlayState* play) { } if (this->alpha > 180) { - phi_f0 = 1.0f; + particleSpawningChance = 1.0f; } else if (this->alpha > 60) { - phi_f0 = 0.5f; + particleSpawningChance = 0.5f; } else { - phi_f0 = 0.0f; + particleSpawningChance = 0.0f; } - sEffSpawnFuncs[type](this, play, phi_f0, D_808917D0[type]); + sSteamSpawnFuncs[type](this, play, particleSpawningChance, sSteamEffectScales[type]); - if (this->alpha <= 0) { + if (GameInteractor_Should(VB_RED_ICE_DROP_ITEM, this->alpha <= 0, this)) { if (!((this->dyna.actor.params >> 6) & 1)) { Flags_SetSwitch(play, this->dyna.actor.params & 0x3F); } @@ -455,10 +446,10 @@ void BgIceShelter_Draw(Actor* thisx, PlayState* play2) { gSPMatrix(POLY_XLU_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); switch ((this->dyna.actor.params >> 8) & 7) { - case 0: - case 1: - case 2: - case 4: + case RED_ICE_LARGE: + case RED_ICE_SMALL: + case RED_ICE_PLATFORM: + case RED_ICE_KING_ZORA: func_8002ED80(&this->dyna.actor, play, 0); break; } @@ -471,9 +462,9 @@ void BgIceShelter_Draw(Actor* thisx, PlayState* play2) { } switch ((this->dyna.actor.params >> 8) & 7) { - case 0: - case 1: - case 4: + case RED_ICE_LARGE: + case RED_ICE_SMALL: + case RED_ICE_KING_ZORA: gSPSegment(POLY_XLU_DISP++, 0x08, Gfx_TwoTexScrollEx(play->state.gfxCtx, 0, -play->gameplayFrames & 0x7F, -play->gameplayFrames & 0x7F, 0x20, 0x20, 1, -play->gameplayFrames & 0x7F, @@ -481,7 +472,7 @@ void BgIceShelter_Draw(Actor* thisx, PlayState* play2) { gSPDisplayList(POLY_XLU_DISP++, gRedIceBlockDL); break; - case 2: + case RED_ICE_PLATFORM: gSPSegment(POLY_XLU_DISP++, 0x08, Gfx_TwoTexScrollEx(play->state.gfxCtx, 0, 0, play->gameplayFrames & 0xFF, 0x40, 0x40, 1, 0, -play->gameplayFrames & 0xFF, 0x40, 0x40, 0, 1, 0, -1)); @@ -492,7 +483,7 @@ void BgIceShelter_Draw(Actor* thisx, PlayState* play2) { gSPDisplayList(POLY_XLU_DISP++, gRedIcePlatformDL); break; - case 3: + case RED_ICE_WALL: gSPDisplayList(POLY_XLU_DISP++, gRedIceWallDL); break; } diff --git a/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.h b/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.h index df6976c4227..4eca07050ca 100644 --- a/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.h +++ b/soh/src/overlays/actors/ovl_Bg_Ice_Shelter/z_bg_ice_shelter.h @@ -8,6 +8,14 @@ struct BgIceShelter; typedef void (*BgIceShelterActionFunc)(struct BgIceShelter*, PlayState*); +typedef enum RedIceType { + /* 0 */ RED_ICE_LARGE, // Large red ice block + /* 1 */ RED_ICE_SMALL, // Small red ice block + /* 2 */ RED_ICE_PLATFORM, // Complex structure that can be climbed and walked on. Unused in vanilla OoT, used in MQ to cover the Ice Cavern Map chest + /* 3 */ RED_ICE_WALL, // Vertical ice sheets blocking corridors + /* 4 */ RED_ICE_KING_ZORA // Giant red ice block covering King Zora +} RedIceType; + typedef struct BgIceShelter { /* 0x0000 */ DynaPolyActor dyna; /* 0x0164 */ BgIceShelterActionFunc actionFunc; diff --git a/soh/src/overlays/actors/ovl_Bg_Ice_Turara/z_bg_ice_turara.c b/soh/src/overlays/actors/ovl_Bg_Ice_Turara/z_bg_ice_turara.c index db45bca9a6c..efcbe4d4308 100644 --- a/soh/src/overlays/actors/ovl_Bg_Ice_Turara/z_bg_ice_turara.c +++ b/soh/src/overlays/actors/ovl_Bg_Ice_Turara/z_bg_ice_turara.c @@ -6,6 +6,7 @@ #include "z_bg_ice_turara.h" #include "objects/object_ice_objects/object_ice_objects.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS 0 @@ -114,7 +115,7 @@ void BgIceTurara_Break(BgIceTurara* this, PlayState* play, f32 arg2) { } void BgIceTurara_Stalagmite(BgIceTurara* this, PlayState* play) { - if (this->collider.base.acFlags & AC_HIT) { + if (GameInteractor_Should(VB_STALAGMITE_DROP_ITEM, this->collider.base.acFlags & AC_HIT, this)) { BgIceTurara_Break(this, play, 50.0f); Actor_Kill(&this->dyna.actor); return; @@ -165,7 +166,7 @@ void BgIceTurara_Fall(BgIceTurara* this, PlayState* play) { this->dyna.actor.world.pos.y = this->dyna.actor.floorHeight; } BgIceTurara_Break(this, play, 40.0f); - if (this->dyna.actor.params == TURARA_STALACTITE_REGROW) { + if (GameInteractor_Should(VB_STALACTITE_DROP_ITEM, this->dyna.actor.params == TURARA_STALACTITE_REGROW, this)) { this->dyna.actor.world.pos.y = this->dyna.actor.home.pos.y + 120.0f; func_8003EC50(play, &play->colCtx.dyna, this->dyna.bgId); this->actionFunc = BgIceTurara_Regrow; @@ -198,4 +199,5 @@ void BgIceTurara_Update(Actor* thisx, PlayState* play) { void BgIceTurara_Draw(Actor* thisx, PlayState* play) { Gfx_DrawDListOpa(play, object_ice_objects_DL_0023D0); + if (GameInteractor_Should(VB_ICICLE_SETUP_DRAW, true, thisx)) {} } diff --git a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c index a13eb154774..2a3def01bf5 100644 --- a/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c +++ b/soh/src/overlays/actors/ovl_Boss_Mo/z_boss_mo.c @@ -821,7 +821,7 @@ void BossMo_Tentacle(BossMo* this, PlayState* play) { Interface_ChangeAlpha(0xB); } if ((this->timers[0] % 8) == 0) { - play->damagePlayer(play, -1); + play->damagePlayer(play, GET_PLAYER(play), -1); } Math_ApproachF(&this->waterLevelMod, -5.0f, 0.1f, 0.4f); sp1B4 = this->tentRot[15].x; diff --git a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c index aa68a5866b1..d48813285f0 100644 --- a/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c +++ b/soh/src/overlays/actors/ovl_Boss_Sst/z_boss_sst.c @@ -1896,7 +1896,7 @@ void BossSst_HandCrush(BossSst* this, PlayState* play) { Player_PlaySfx(&player->actor, NA_SE_VO_LI_DAMAGE_S); } - play->damagePlayer(play, -8); + play->damagePlayer(play, GET_PLAYER(play), -8); } if (Animation_OnFrame(&this->skelAnime, 0.0f)) { Audio_PlayActorSound2(&this->actor, NA_SE_EN_SHADEST_CATCH); diff --git a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c index 416715f395c..8de9cd43b85 100644 --- a/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c +++ b/soh/src/overlays/actors/ovl_Boss_Tw/z_boss_tw.c @@ -4814,7 +4814,7 @@ void BossTw_UpdateEffects(PlayState* play) { } if (!(play->gameplayFrames & 1)) { - play->damagePlayer(play, -1); + play->damagePlayer(play, GET_PLAYER(play), -1); } } else { Math_ApproachF(&eff->workf[EFF_SCALE], 0.042f, 1.0f, 0.002f); diff --git a/soh/src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.c b/soh/src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.c index 29eccfdd7a3..28ee961baec 100644 --- a/soh/src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.c +++ b/soh/src/overlays/actors/ovl_Door_Gerudo/z_door_gerudo.c @@ -16,7 +16,7 @@ void DoorGerudo_Update(Actor* thisx, PlayState* play); void DoorGerudo_Draw(Actor* thisx, PlayState* play); void func_8099485C(DoorGerudo* this, PlayState* play); -s32 func_80994750(DoorGerudo* this, PlayState* play); +s32 func_80994750(DoorGerudo* this, PlayState* play, Player** outPlayer); void func_8099496C(DoorGerudo* this, PlayState* play); void func_809949C8(DoorGerudo* this, PlayState* play); @@ -61,8 +61,7 @@ void DoorGerudo_Destroy(Actor* thisx, PlayState* play) { DynaPoly_DeleteBgActor(play, &play->colCtx.dyna, this->dyna.bgId); } -f32 func_809946BC(PlayState* play, DoorGerudo* this, f32 arg2, f32 arg3, f32 arg4) { - Player* player = GET_PLAYER(play); +f32 func_809946BC(DoorGerudo* this, Player* player, f32 arg2, f32 arg3, f32 arg4) { Vec3f playerPos; Vec3f sp1C; @@ -78,24 +77,44 @@ f32 func_809946BC(PlayState* play, DoorGerudo* this, f32 arg2, f32 arg3, f32 arg } } -s32 func_80994750(DoorGerudo* this, PlayState* play) { - Player* player = GET_PLAYER(play); - f32 temp_f0; - s16 rotYDiff; +s32 func_80994750(DoorGerudo* this, PlayState* play, Player** outPlayer) { + Actor* actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + f32 bestAbsRelZ = 1000000000.0f; + s32 bestDirection = 0; + s32 sanity = 0; + + *outPlayer = NULL; if (!Player_InCsMode(play)) { - temp_f0 = func_809946BC(play, this, 0.0f, 20.0f, 15.0f); - if (fabsf(temp_f0) < 40.0f) { - rotYDiff = player->actor.shape.rot.y - this->dyna.actor.shape.rot.y; - if (temp_f0 > 0.0f) { - rotYDiff = 0x8000 - rotYDiff; - } - if (ABS(rotYDiff) < 0x2000) { - return (temp_f0 >= 0.0f) ? 1.0f : -1.0f; + while ((actor != NULL) && (sanity < 2000)) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* player = (Player*)actor; + f32 relZ = func_809946BC(this, player, 0.0f, 20.0f, 15.0f); + + if (fabsf(relZ) < 40.0f) { + s16 rotYDiff = player->actor.shape.rot.y - this->dyna.actor.shape.rot.y; + + if (relZ > 0.0f) { + rotYDiff = 0x8000 - rotYDiff; + } + if (ABS(rotYDiff) < 0x2000) { + f32 absRelZ = fabsf(relZ); + + if (absRelZ < bestAbsRelZ) { + bestAbsRelZ = absRelZ; + bestDirection = (relZ >= 0.0f) ? 1 : -1; + *outPlayer = player; + } + } + } } + + actor = actor->next; + sanity++; } } - return 0; + + return bestDirection; } void func_8099485C(DoorGerudo* this, PlayState* play) { @@ -106,10 +125,10 @@ void func_8099485C(DoorGerudo* this, PlayState* play) { Audio_PlayActorSound2(&this->dyna.actor, NA_SE_EV_CHAIN_KEY_UNLOCK); GameInteractor_ExecuteOnDungeonKeyUsedHooks(gSaveContext.mapIndex); } else { - s32 direction = func_80994750(this, play); + Player* player = NULL; + s32 direction = func_80994750(this, play, &player); - if (direction != 0) { - Player* player = GET_PLAYER(play); + if ((direction != 0) && (player != NULL)) { if (gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] <= 0) { player->naviTextId = -0x203; diff --git a/soh/src/overlays/actors/ovl_Door_Killer/z_door_killer.c b/soh/src/overlays/actors/ovl_Door_Killer/z_door_killer.c index 6f1e4fa712f..5ca5a9f8a61 100644 --- a/soh/src/overlays/actors/ovl_Door_Killer/z_door_killer.c +++ b/soh/src/overlays/actors/ovl_Door_Killer/z_door_killer.c @@ -432,12 +432,6 @@ void DoorKiller_WaitBeforeWobble(DoorKiller* this, PlayState* play) { } void DoorKiller_Wait(DoorKiller* this, PlayState* play) { - Player* player = GET_PLAYER(play); - Vec3f playerPosRelToDoor; - s16 angleToFacingPlayer; - - Actor_WorldToActorCoords(&this->actor, &playerPosRelToDoor, &player->actor.world.pos); - // playerIsOpening is set by player if (this->playerIsOpening) { this->actionFunc = DoorKiller_WaitBeforeWobble; @@ -461,18 +455,44 @@ void DoorKiller_Wait(DoorKiller* this, PlayState* play) { DoorKiller_SpawnRubble(&this->actor, play); this->actionFunc = DoorKiller_Die; SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 20, NA_SE_EN_KDOOR_BREAK); - } else if (!Player_InCsMode(play) && (fabsf(playerPosRelToDoor.y) < 20.0f) && - (fabsf(playerPosRelToDoor.x) < 20.0f) && (playerPosRelToDoor.z < 50.0f) && - (playerPosRelToDoor.z > 0.0f)) { - // Set player properties to make the door openable if within range - angleToFacingPlayer = player->actor.shape.rot.y - this->actor.shape.rot.y; - if (playerPosRelToDoor.z > 0.0f) { - angleToFacingPlayer = 0x8000 - angleToFacingPlayer; + } else if (!Player_InCsMode(play)) { + Actor* actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + Player* bestPlayer = NULL; + s16 bestDirection = 1; + f32 bestDistSq = 1000000000.0f; + s32 sanity = 0; + + while ((actor != NULL) && (sanity < 2000)) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* candidate = (Player*)actor; + Vec3f relPos; + s16 angleToFacingPlayer; + + Actor_WorldToActorCoords(&this->actor, &relPos, &candidate->actor.world.pos); + if ((fabsf(relPos.y) < 20.0f) && (fabsf(relPos.x) < 20.0f) && (relPos.z < 50.0f) && (relPos.z > 0.0f)) { + angleToFacingPlayer = candidate->actor.shape.rot.y - this->actor.shape.rot.y; + angleToFacingPlayer = 0x8000 - angleToFacingPlayer; + + if (ABS(angleToFacingPlayer) < 0x3000) { + f32 distSq = SQ(relPos.x) + SQ(relPos.z); + + if (distSq < bestDistSq) { + bestDistSq = distSq; + bestPlayer = candidate; + bestDirection = (relPos.z >= 0.0f) ? 1 : -1; + } + } + } + } + + actor = actor->next; + sanity++; } - if (ABS(angleToFacingPlayer) < 0x3000) { - player->doorType = PLAYER_DOORTYPE_FAKE; - player->doorDirection = (playerPosRelToDoor.z >= 0.0f) ? 1.0f : -1.0f; - player->doorActor = &this->actor; + + if (bestPlayer != NULL) { + bestPlayer->doorType = PLAYER_DOORTYPE_FAKE; + bestPlayer->doorDirection = bestDirection; + bestPlayer->doorActor = &this->actor; } } diff --git a/soh/src/overlays/actors/ovl_Door_Shutter/z_door_shutter.c b/soh/src/overlays/actors/ovl_Door_Shutter/z_door_shutter.c index 307aee785d3..3b0e4235ae8 100644 --- a/soh/src/overlays/actors/ovl_Door_Shutter/z_door_shutter.c +++ b/soh/src/overlays/actors/ovl_Door_Shutter/z_door_shutter.c @@ -26,6 +26,8 @@ #include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS ACTOR_FLAG_UPDATE_CULLING_DISABLED +#define LOCAL_MP_DISABLED_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Disable") +#define LOCAL_MP_SECONDARY_AREA_LOAD_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.SecondaryPlayersLoadAreas") void DoorShutter_Init(Actor* thisx, PlayState* play); void DoorShutter_Destroy(Actor* thisx, PlayState* play); @@ -325,11 +327,10 @@ void DoorShutter_SetupType(DoorShutter* this, PlayState* play) { } } -f32 func_80996840(PlayState* play, DoorShutter* this, f32 arg2, f32 arg3, f32 arg4) { +f32 func_80996840(DoorShutter* this, Player* player, f32 arg2, f32 arg3, f32 arg4) { s32 pad; Vec3f sp28; Vec3f sp1C; - Player* player = GET_PLAYER(play); sp28.x = player->actor.world.pos.x; sp28.y = player->actor.world.pos.y + arg2; @@ -342,25 +343,84 @@ f32 func_80996840(PlayState* play, DoorShutter* this, f32 arg2, f32 arg3, f32 ar } } -s32 func_809968D4(DoorShutter* this, PlayState* play) { - Player* player = GET_PLAYER(play); +s32 func_809968D4(DoorShutter* this, PlayState* play, Player** outPlayer) { + Actor* actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + f32 bestAbsRelZ = 1000000000.0f; + s32 bestDirection = 0; + s32 sanity = 0; + + *outPlayer = NULL; if (!Player_InCsMode(play)) { ShutterInfo* temp_v1 = &sShutterInfo[this->unk_16C]; - f32 temp_f2 = func_80996840(play, this, (this->unk_16C != 3) ? 0.0f : 80.0f, temp_v1->e, temp_v1->f); - if (fabsf(temp_f2) < 50.0f) { - s16 phi_v0 = player->actor.shape.rot.y - this->dyna.actor.shape.rot.y; + while ((actor != NULL) && (sanity < 2000)) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* player = (Player*)actor; + f32 relZ = func_80996840(this, player, (this->unk_16C != 3) ? 0.0f : 80.0f, temp_v1->e, temp_v1->f); - if (temp_f2 > 0.0f) { - phi_v0 = 0x8000 - phi_v0; + if (fabsf(relZ) < 50.0f) { + s16 rotYDiff = player->actor.shape.rot.y - this->dyna.actor.shape.rot.y; + + if (relZ > 0.0f) { + rotYDiff = 0x8000 - rotYDiff; + } + if (ABS(rotYDiff) < 0x3000) { + f32 absRelZ = fabsf(relZ); + + if (absRelZ < bestAbsRelZ) { + bestAbsRelZ = absRelZ; + bestDirection = (relZ >= 0.0f) ? 1 : -1; + *outPlayer = player; + } + } + } } - if (ABS(phi_v0) < 0x3000) { - return (temp_f2 >= 0.0f) ? 1.0f : -1.0f; + + actor = actor->next; + sanity++; + } + } + + return bestDirection; +} + +static s32 DoorShutter_SecondaryPlayersCanTrigger(PlayState* play) { + return !CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0) && CVarGetInteger(LOCAL_MP_SECONDARY_AREA_LOAD_CVAR, 0); +} + +static Player* DoorShutter_GetTriggerPlayer(PlayState* play, DoorShutter* this) { + Player* mainPlayer = GET_PLAYER(play); + Player* triggerPlayer = NULL; + Actor* actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + s32 sanity = 0; + + if (mainPlayer == NULL) { + return NULL; + } + + if (!DoorShutter_SecondaryPlayersCanTrigger(play)) { + return mainPlayer; + } + + while ((actor != NULL) && (sanity < 2000)) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* player = (Player*)actor; + + if ((player->doorType == PLAYER_DOORTYPE_SLIDING) && (player->doorActor == &this->dyna.actor)) { + return player; } } + + actor = actor->next; + sanity++; + } + + if ((func_809968D4(this, play, &triggerPlayer) != 0) && (triggerPlayer != NULL)) { + return triggerPlayer; } - return 0.0f; + + return mainPlayer; } void func_80996A54(DoorShutter* this, PlayState* play) { @@ -372,10 +432,12 @@ void func_80996A54(DoorShutter* this, PlayState* play) { OnePointCutscene_Attention(play, &GET_PLAYER(play)->actor); this->unk_16F = -100; } - } else if (func_809968D4(this, play) != 0) { - Player* player = GET_PLAYER(play); + } else { + Player* player = NULL; - player->naviTextId = -0x202; + if (func_809968D4(this, play, &player) != 0 && player != NULL) { + player->naviTextId = -0x202; + } } } @@ -397,10 +459,10 @@ void func_80996B0C(DoorShutter* this, PlayState* play) { } } } else { - s32 doorDirection = func_809968D4(this, play); + Player* player = NULL; + s32 doorDirection = func_809968D4(this, play, &player); - if (doorDirection != 0) { - Player* player = GET_PLAYER(play); + if ((doorDirection != 0) && (player != NULL)) { if (this->unk_16E != 0) { if (this->doorType == SHUTTER_BOSS) { @@ -423,17 +485,27 @@ void func_80996B0C(DoorShutter* this, PlayState* play) { void func_80996C60(DoorShutter* this, PlayState* play) { if (this->dyna.actor.category == ACTORCAT_DOOR) { - Player* player = GET_PLAYER(play); + Player* mainPlayer = GET_PLAYER(play); + Player* player = DoorShutter_GetTriggerPlayer(play, this); + Actor* doorCamActor = &this->dyna.actor; s32 sp38 = this->unk_16C; s32 sp34 = 0xF; + if (player == NULL) { + return; + } + + if (player != mainPlayer) { + doorCamActor = NULL; + } + if (DoorShutter_SetupDoor(this, play)) { sp34 = 0x20; } DoorShutter_SetupAction(this, func_80997004); this->unk_16C = sp38; this->unk_170 = 0.0f; - Camera_ChangeDoorCam(play->cameraPtrs[MAIN_CAM], &this->dyna.actor, player->cv.slidingDoorBgCamIndex, 0.0f, 12, + Camera_ChangeDoorCam(play->cameraPtrs[MAIN_CAM], doorCamActor, player->cv.slidingDoorBgCamIndex, 0.0f, 12, sp34, 10); } } @@ -491,10 +563,13 @@ void func_80996EE8(DoorShutter* this, PlayState* play) { OnePointCutscene_Attention(play, &this->dyna.actor); this->unk_16F = -100; } - } else if (func_809968D4(this, play)) { - Player* player = GET_PLAYER(play); - // Jabu navi text for switch doors is different - player->naviTextId = (play->sceneNum == SCENE_JABU_JABU) ? -0x20B : -0x202; + } else { + Player* player = NULL; + + if (func_809968D4(this, play, &player) && (player != NULL)) { + // Jabu navi text for switch doors is different + player->naviTextId = (play->sceneNum == SCENE_JABU_JABU) ? -0x20B : -0x202; + } } } } @@ -551,9 +626,13 @@ void func_80997150(DoorShutter* this, PlayState* play) { } void func_80997220(DoorShutter* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = DoorShutter_GetTriggerPlayer(play, this); s8 room = this->dyna.actor.room; + if (player == NULL) { + return; + } + if (this->dyna.actor.room >= 0) { Vec3f vec; diff --git a/soh/src/overlays/actors/ovl_Elf_Msg/z_elf_msg.c b/soh/src/overlays/actors/ovl_Elf_Msg/z_elf_msg.c index 86dc36a7f5c..d29d2bd091f 100644 --- a/soh/src/overlays/actors/ovl_Elf_Msg/z_elf_msg.c +++ b/soh/src/overlays/actors/ovl_Elf_Msg/z_elf_msg.c @@ -118,18 +118,38 @@ s32 ElfMsg_GetMessageId(ElfMsg* this) { } } +static void ElfMsg_SetNaviForPlayer(ElfMsg* this, Player* player) { + EnElf* navi; + + if ((player->naviActor == NULL) || (player->naviActor->update == NULL)) { + return; + } + + if (!GameInteractor_Should(VB_NAVI_TALK, true, this)) { + return; + } + + navi = (EnElf*)player->naviActor; + player->naviTextId = ElfMsg_GetMessageId(this); + navi->elfMsg = this; +} + void ElfMsg_CallNaviCuboid(ElfMsg* this, PlayState* play) { - Player* player = GET_PLAYER(play); - EnElf* navi = (EnElf*)player->naviActor; - - if ((fabsf(player->actor.world.pos.x - this->actor.world.pos.x) < (100.0f * this->actor.scale.x)) && - (this->actor.world.pos.y <= player->actor.world.pos.y) && - ((player->actor.world.pos.y - this->actor.world.pos.y) < (100.0f * this->actor.scale.y)) && - (fabsf(player->actor.world.pos.z - this->actor.world.pos.z) < (100.0f * this->actor.scale.z))) { - if (GameInteractor_Should(VB_NAVI_TALK, true, this)) { - player->naviTextId = ElfMsg_GetMessageId(this); - navi->elfMsg = this; + Actor* actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* player = (Player*)actor; + + if ((fabsf(player->actor.world.pos.x - this->actor.world.pos.x) < (100.0f * this->actor.scale.x)) && + (this->actor.world.pos.y <= player->actor.world.pos.y) && + ((player->actor.world.pos.y - this->actor.world.pos.y) < (100.0f * this->actor.scale.y)) && + (fabsf(player->actor.world.pos.z - this->actor.world.pos.z) < (100.0f * this->actor.scale.z))) { + ElfMsg_SetNaviForPlayer(this, player); + } } + + actor = actor->next; } } @@ -138,20 +158,20 @@ s32 ElfMsg_WithinXZDistance(Vec3f* pos1, Vec3f* pos2, f32 distance) { } void ElfMsg_CallNaviCylinder(ElfMsg* this, PlayState* play) { - Player* player = GET_PLAYER(play); - EnElf* navi = (EnElf*)player->naviActor; + Actor* actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; - // This fixes a crash when using a grotto exit when you never properly entered - if (navi == NULL) - return; + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* player = (Player*)actor; - if (ElfMsg_WithinXZDistance(&player->actor.world.pos, &this->actor.world.pos, this->actor.scale.x * 100.0f) && - (this->actor.world.pos.y <= player->actor.world.pos.y) && - ((player->actor.world.pos.y - this->actor.world.pos.y) < (100.0f * this->actor.scale.y))) { - if (GameInteractor_Should(VB_NAVI_TALK, true, this)) { - player->naviTextId = ElfMsg_GetMessageId(this); - navi->elfMsg = this; + if (ElfMsg_WithinXZDistance(&player->actor.world.pos, &this->actor.world.pos, this->actor.scale.x * 100.0f) && + (this->actor.world.pos.y <= player->actor.world.pos.y) && + ((player->actor.world.pos.y - this->actor.world.pos.y) < (100.0f * this->actor.scale.y))) { + ElfMsg_SetNaviForPlayer(this, player); + } } + + actor = actor->next; } } diff --git a/soh/src/overlays/actors/ovl_En_Arrow/z_en_arrow.c b/soh/src/overlays/actors/ovl_En_Arrow/z_en_arrow.c index a0c210cb8ca..dcd55f3c2a0 100644 --- a/soh/src/overlays/actors/ovl_En_Arrow/z_en_arrow.c +++ b/soh/src/overlays/actors/ovl_En_Arrow/z_en_arrow.c @@ -59,6 +59,37 @@ static InitChainEntry sInitChain[] = { ICHAIN_F32(minVelocityY, -150, ICHAIN_STOP), }; +static Player* EnArrow_FindPlayerByPort(PlayState* play, u8 controllerPort) { + Actor* actor; + + if (controllerPort == 1) { + return GET_PLAYER(play); + } + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* candidate = (Player*)actor; + + if (candidate->isSecondPlayer && (candidate->controllerPort == controllerPort)) { + return candidate; + } + } + + actor = actor->next; + } + + return GET_PLAYER(play); +} + +static Player* EnArrow_GetShooter(PlayState* play, EnArrow* this) { + if ((this->actor.parent != NULL) && (this->actor.parent->id == ACTOR_PLAYER) && (this->actor.parent->update != NULL)) { + return (Player*)this->actor.parent; + } + + return EnArrow_FindPlayerByPort(play, this->actor.home.rot.z); +} + void EnArrow_SetupAction(EnArrow* this, EnArrowActionFunc actionFunc) { this->actionFunc = actionFunc; } @@ -207,7 +238,13 @@ void EnArrow_Destroy(Actor* thisx, PlayState* play) { } void EnArrow_Shoot(EnArrow* this, PlayState* play) { - Player* player = GET_PLAYER(play); + if ((this->actor.parent != NULL) && (this->actor.parent->id == ACTOR_PLAYER) && (this->actor.parent->update != NULL)) { + Player* shooter = (Player*)this->actor.parent; + + this->actor.home.rot.z = shooter->isSecondPlayer ? shooter->controllerPort : 1; + } + + Player* player = EnArrow_GetShooter(play, this); if (this->actor.parent == NULL) { if ((this->actor.params != ARROW_NUT) && (player->unk_A73 == 0)) { @@ -442,7 +479,7 @@ void func_809B4640(EnArrow* this, PlayState* play) { void EnArrow_Update(Actor* thisx, PlayState* play) { s32 pad; EnArrow* this = (EnArrow*)thisx; - Player* player = GET_PLAYER(play); + Player* player = EnArrow_GetShooter(play, this); if (this->isCsNut || ((this->actor.params >= ARROW_NORMAL_LIT) && (player->unk_A73 != 0)) || !Player_InBlockingCsMode(play, player)) { diff --git a/soh/src/overlays/actors/ovl_En_Box/z_en_box.c b/soh/src/overlays/actors/ovl_En_Box/z_en_box.c index fb4f09d2b5a..475edffd2ac 100644 --- a/soh/src/overlays/actors/ovl_En_Box/z_en_box.c +++ b/soh/src/overlays/actors/ovl_En_Box/z_en_box.c @@ -92,6 +92,38 @@ void EnBox_SetupAction(EnBox* this, EnBoxActionFunc actionFunc) { this->actionFunc = actionFunc; } +static Player* EnBox_FindInteractPlayer(PlayState* play, EnBox* this) { + Actor* actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + Player* bestPlayer = NULL; + f32 bestDistSq = 1000000000.0f; + s32 sanity = 0; + + while ((actor != NULL) && (sanity < 2000)) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* player = (Player*)actor; + Vec3f relPos; + s16 yawToChest; + + Actor_WorldToActorCoords(&this->dyna.actor, &relPos, &player->actor.world.pos); + yawToChest = Math_Vec3f_Yaw(&player->actor.world.pos, &this->dyna.actor.world.pos); + if ((relPos.z > -50.0f) && (relPos.z < 0.0f) && (fabsf(relPos.y) < 10.0f) && (fabsf(relPos.x) < 20.0f) && + (ABS(player->actor.shape.rot.y - yawToChest) < 0x3000)) { + f32 distSq = SQ(relPos.x) + SQ(relPos.z); + + if (distSq < bestDistSq) { + bestDistSq = distSq; + bestPlayer = player; + } + } + } + + actor = actor->next; + sanity++; + } + + return bestPlayer; +} + void EnBox_ClipToGround(EnBox* this, PlayState* play) { f32 newY; CollisionPoly* poly; @@ -414,7 +446,6 @@ void EnBox_WaitOpen(EnBox* this, PlayState* play) { AnimationHeader* anim; s32 linkAge; s32 pad; - Vec3f sp4C; Player* player; this->alpha = 255; @@ -443,11 +474,11 @@ void EnBox_WaitOpen(EnBox* this, PlayState* play) { osSyncPrintf("Actor_Environment_Tbox_On() %d\n", this->dyna.actor.params & 0x1F); Flags_SetTreasure(play, this->dyna.actor.params & 0x1F); } else { - player = GET_PLAYER(play); - Actor_WorldToActorCoords(&this->dyna.actor, &sp4C, &player->actor.world.pos); - if (sp4C.z > -50.0f && sp4C.z < 0.0f && fabsf(sp4C.y) < 10.0f && fabsf(sp4C.x) < 20.0f && - Player_IsFacingActor(&this->dyna.actor, 0x3000, play)) { - Actor_OfferGetItemNearby(&this->dyna.actor, play, -(this->dyna.actor.params >> 5 & 0x7F)); + player = EnBox_FindInteractPlayer(play, this); + if (player != NULL) { + player->getItemId = -(this->dyna.actor.params >> 5 & 0x7F); + player->interactRangeActor = &this->dyna.actor; + player->getItemDirection = ABS(this->dyna.actor.yawTowardsPlayer - player->actor.shape.rot.y); } if (Flags_GetTreasure(play, this->dyna.actor.params & 0x1F)) { EnBox_SetupAction(this, EnBox_Open); diff --git a/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c b/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c index 6717b9788f8..7179b794d00 100644 --- a/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c +++ b/soh/src/overlays/actors/ovl_En_Bubble/z_en_bubble.c @@ -118,7 +118,7 @@ u32 func_809CBCEC(EnBubble* this) { void EnBubble_DamagePlayer(EnBubble* this, PlayState* play) { s32 damage = -this->colliderSphere.elements[0].info.toucher.damage; - play->damagePlayer(play, damage); + play->damagePlayer(play, GET_PLAYER(play), damage); func_8002F7A0(play, &this->actor, 6.0f, this->actor.yawTowardsPlayer, 6.0f); } diff --git a/soh/src/overlays/actors/ovl_En_Butte/z_en_butte.c b/soh/src/overlays/actors/ovl_En_Butte/z_en_butte.c index 4421865d2cb..f7a171bd03d 100644 --- a/soh/src/overlays/actors/ovl_En_Butte/z_en_butte.c +++ b/soh/src/overlays/actors/ovl_En_Butte/z_en_butte.c @@ -9,6 +9,7 @@ #include "objects/gameplay_keep/gameplay_keep.h" #include "objects/gameplay_field_keep/gameplay_field_keep.h" #include "soh/ResourceManagerHelpers.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS 0 @@ -363,7 +364,7 @@ void EnButte_TransformIntoFairy(EnButte* this, PlayState* play) { if (this->timer == 5) { SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 60, NA_SE_EV_BUTTERFRY_TO_FAIRY); - } else if (this->timer == 4) { + } else if (GameInteractor_Should(VB_SPAWN_BUTTERFLY_FAIRY, this->timer == 4, this)) { Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ELF, this->actor.focus.pos.x, this->actor.focus.pos.y, this->actor.focus.pos.z, 0, this->actor.shape.rot.y, 0, FAIRY_HEAL_TIMED); this->drawSkelAnime = false; diff --git a/soh/src/overlays/actors/ovl_En_Bx/z_en_bx.c b/soh/src/overlays/actors/ovl_En_Bx/z_en_bx.c index b7e515e3ddc..4e49bedafb9 100644 --- a/soh/src/overlays/actors/ovl_En_Bx/z_en_bx.c +++ b/soh/src/overlays/actors/ovl_En_Bx/z_en_bx.c @@ -152,7 +152,7 @@ void EnBx_Update(Actor* thisx, PlayState* play) { player->invincibilityTimer = 0; } else { player->invincibilityTimer = 0; - play->damagePlayer(play, -4); + play->damagePlayer(play, GET_PLAYER(play), -4); } } func_8002F71C(play, &this->actor, 6.0f, tmp32, 6.0f); diff --git a/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c index b84103a4a0b..0f80c246b32 100644 --- a/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c +++ b/soh/src/overlays/actors/ovl_En_Dekubaba/z_en_dekubaba.c @@ -32,6 +32,10 @@ void EnDekubaba_DeadStickDrop(EnDekubaba* this, PlayState* play); static Vec3f sZeroVec = { 0.0f, 0.0f, 0.0f }; +static Player* EnDekubaba_GetClosestPlayer(PlayState* play, EnDekubaba* this) { + return Actor_GetClosestPlayerFromPos(play, (this != NULL) ? &this->actor.home.pos : NULL); +} + const ActorInit En_Dekubaba_InitVars = { ACTOR_EN_DEKUBABA, ACTORCAT_ENEMY, @@ -488,7 +492,7 @@ void EnDekubaba_Wait(EnDekubaba* this, PlayState* play) { } void EnDekubaba_Grow(EnDekubaba* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = EnDekubaba_GetClosestPlayer(play, this); f32 headDistHorizontal; f32 headDistVertical; f32 headShiftX; @@ -628,7 +632,7 @@ void EnDekubaba_UpdateHeadPosition(EnDekubaba* this) { } void EnDekubaba_DecideLunge(EnDekubaba* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = EnDekubaba_GetClosestPlayer(play, this); SkelAnime_Update(&this->skelAnime); if (Animation_OnFrame(&this->skelAnime, 0.0f) || Animation_OnFrame(&this->skelAnime, 12.0f)) { @@ -668,7 +672,8 @@ void EnDekubaba_DecideLunge(EnDekubaba* this, PlayState* play) { if (240.0f * this->size < Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos)) { EnDekubaba_SetupRetract(this); - } else if ((this->timer == 0) || (this->actor.xzDistToPlayer < 80.0f * this->size)) { + } else if ((this->timer == 0) || + (Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos) < 80.0f * this->size)) { EnDekubaba_SetupPrepareLunge(this); } } @@ -733,7 +738,7 @@ void EnDekubaba_Lunge(EnDekubaba* this, PlayState* play) { } void EnDekubaba_PrepareLunge(EnDekubaba* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = EnDekubaba_GetClosestPlayer(play, this); if (this->timer != 0) { this->timer--; diff --git a/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c index df30c191b11..4154d35d299 100644 --- a/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c +++ b/soh/src/overlays/actors/ovl_En_Dodongo/z_en_dodongo.c @@ -530,7 +530,7 @@ void EnDodongo_SwallowBomb(EnDodongo* this, PlayState* play) { void EnDodongo_Walk(EnDodongo* this, PlayState* play) { s32 pad; f32 playbackSpeed; - Player* player = GET_PLAYER(play); + Player* player = Actor_GetClosestPlayerFromPos(play, &this->actor.home.pos); s16 yawDiff = (s16)(this->actor.yawTowardsPlayer - this->actor.shape.rot.y); yawDiff = ABS(yawDiff); diff --git a/soh/src/overlays/actors/ovl_En_Door/z_en_door.c b/soh/src/overlays/actors/ovl_En_Door/z_en_door.c index 26835509464..7dfbdae1497 100644 --- a/soh/src/overlays/actors/ovl_En_Door/z_en_door.c +++ b/soh/src/overlays/actors/ovl_En_Door/z_en_door.c @@ -81,6 +81,46 @@ static Gfx* D_809FCEE4[5][2] = { { gFieldDoorLeftDL, gFieldDoorRightDL }, }; +static Player* EnDoor_FindInteractPlayer(EnDoor* this, PlayState* play, s16* outDoorDirection) { + Actor* actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + Player* bestPlayer = NULL; + f32 bestDistSq = 1000000000.0f; + s32 sanity = 0; + + *outDoorDirection = 1; + + while ((actor != NULL) && (sanity < 2000)) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* player = (Player*)actor; + Vec3f relPos; + s16 rotYDiff; + + Actor_WorldToActorCoords(&this->actor, &relPos, &player->actor.world.pos); + if ((fabsf(relPos.y) < 20.0f) && (fabsf(relPos.x) < 20.0f) && (fabsf(relPos.z) < 50.0f)) { + rotYDiff = player->actor.shape.rot.y - this->actor.shape.rot.y; + if (relPos.z > 0.0f) { + rotYDiff = 0x8000 - rotYDiff; + } + + if (ABS(rotYDiff) < 0x3000) { + f32 distSq = SQ(relPos.x) + SQ(relPos.z); + + if (distSq < bestDistSq) { + bestDistSq = distSq; + bestPlayer = player; + *outDoorDirection = (relPos.z >= 0.0f) ? 1 : -1; + } + } + } + } + + actor = actor->next; + sanity++; + } + + return bestPlayer; +} + void EnDoor_Init(Actor* thisx, PlayState* play2) { PlayState* play = play2; EnDoor* this = (EnDoor*)thisx; @@ -193,12 +233,11 @@ void EnDoor_SetupType(EnDoor* this, PlayState* play) { void EnDoor_Idle(EnDoor* this, PlayState* play) { Player* player = GET_PLAYER(play); + Player* interactPlayer; s32 doorType; - Vec3f playerPosRelToDoor; - s16 phi_v0; + s16 doorDirection; doorType = this->actor.params >> 7 & 7; - Actor_WorldToActorCoords(&this->actor, &playerPosRelToDoor, &player->actor.world.pos); if (this->playerIsOpening != 0) { this->actionFunc = EnDoor_Open; Animation_PlayOnceSetSpeed(&this->skelAnime, D_809FCECC[this->animStyle], @@ -212,28 +251,21 @@ void EnDoor_Idle(EnDoor* this, PlayState* play) { GameInteractor_ExecuteOnDungeonKeyUsedHooks(gSaveContext.mapIndex); } } else if (!Player_InCsMode(play)) { - if (fabsf(playerPosRelToDoor.y) < 20.0f && fabsf(playerPosRelToDoor.x) < 20.0f && - fabsf(playerPosRelToDoor.z) < 50.0f) { - phi_v0 = player->actor.shape.rot.y - this->actor.shape.rot.y; - if (playerPosRelToDoor.z > 0.0f) { - phi_v0 = 0x8000 - phi_v0; - } - if (ABS(phi_v0) < 0x3000) { - if (this->lockTimer != 0) { - if (GameInteractor_Should(VB_NOT_HAVE_SMALL_KEY, - gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] <= 0, this)) { - Player* player2 = GET_PLAYER(play); - - player2->naviTextId = -0x203; - return; - } else { - player->doorTimer = 10; - } + interactPlayer = EnDoor_FindInteractPlayer(this, play, &doorDirection); + + if (interactPlayer != NULL) { + if (this->lockTimer != 0) { + if (GameInteractor_Should(VB_NOT_HAVE_SMALL_KEY, + gSaveContext.inventory.dungeonKeys[gSaveContext.mapIndex] <= 0, this)) { + interactPlayer->naviTextId = -0x203; + return; + } else { + interactPlayer->doorTimer = 10; } - player->doorType = (doorType == DOOR_AJAR) ? PLAYER_DOORTYPE_AJAR : PLAYER_DOORTYPE_HANDLE; - player->doorDirection = (playerPosRelToDoor.z >= 0.0f) ? 1.0f : -1.0f; - player->doorActor = &this->actor; } + interactPlayer->doorType = (doorType == DOOR_AJAR) ? PLAYER_DOORTYPE_AJAR : PLAYER_DOORTYPE_HANDLE; + interactPlayer->doorDirection = doorDirection; + interactPlayer->doorActor = &this->actor; } else if (doorType == DOOR_AJAR && this->actor.xzDistToPlayer > DOOR_AJAR_OPEN_RANGE) { this->actionFunc = EnDoor_AjarOpen; } diff --git a/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c index 27586062bfa..16b561f2692 100644 --- a/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c +++ b/soh/src/overlays/actors/ovl_En_Elf/z_en_elf.c @@ -84,6 +84,80 @@ static Color_RGBAf sOuterColors[] = { { 255.0f, 50.0f, 100.0f, 255.0f }, }; +static Color_RGBAf sPlayerNaviInnerColors[] = { + { 180.0f, 255.0f, 180.0f, 255.0f }, + { 255.0f, 180.0f, 180.0f, 255.0f }, + { 180.0f, 220.0f, 255.0f, 255.0f }, + { 230.0f, 190.0f, 255.0f, 255.0f }, +}; + +static Color_RGBAf sPlayerNaviOuterColors[] = { + { 0.0f, 120.0f, 0.0f, 255.0f }, + { 255.0f, 0.0f, 0.0f, 255.0f }, + { 0.0f, 120.0f, 255.0f, 255.0f }, + { 122.0f, 57.0f, 163.0f, 255.0f }, +}; + +static Player* EnElf_GetPlayer(EnElf* this, PlayState* play) { + Actor* actor; + + if ((this->actor.parent != NULL) && (this->actor.parent->id == ACTOR_PLAYER) && + (this->actor.parent->update != NULL)) { + return (Player*)this->actor.parent; + } + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (((Player*)actor)->naviActor == &this->actor)) { + return (Player*)actor; + } + + actor = actor->next; + } + + return GET_PLAYER(play); +} + +static Player* EnElf_FindUnassignedNaviPlayer(PlayState* play) { + Actor* actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (((Player*)actor)->naviActor == NULL)) { + return (Player*)actor; + } + + actor = actor->next; + } + + return GET_PLAYER(play); +} + +static u8 EnElf_GetPlayerControllerPort(Player* player) { + if ((player == NULL) || !player->isSecondPlayer || (player->controllerPort < 1) || (player->controllerPort > 4)) { + return 1; + } + + return player->controllerPort; +} + +static void EnElf_GetPlayerNaviColors(Player* player, Color_RGBAf* innerColor, Color_RGBAf* outerColor) { + u8 controllerPort = EnElf_GetPlayerControllerPort(player); + + *innerColor = sPlayerNaviInnerColors[controllerPort - 1]; + *outerColor = sPlayerNaviOuterColors[controllerPort - 1]; +} + +static Actor* EnElf_GetHoverActor(EnElf* this, PlayState* play) { + Player* player = EnElf_GetPlayer(this, play); + Actor* hoverActor = player->focusActor; + + if ((hoverActor == &this->actor) || (hoverActor == player->naviActor) || (hoverActor == &player->actor)) { + return NULL; + } + + return hoverActor; +} + typedef struct { u8 r, g, b; } FairyColorFlags; @@ -213,7 +287,7 @@ s32 func_80A01F90(Vec3f* this, Vec3f* arg1, f32 arg2) { } void func_80A01FE0(EnElf* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = EnElf_GetPlayer(this, play); if (!func_80A01F90(&this->actor.world.pos, &player->actor.world.pos, 30.0f)) { this->unk_2B8 = 0.5f; @@ -233,7 +307,7 @@ void func_80A01FE0(EnElf* this, PlayState* play) { } void func_80A020A4(EnElf* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = EnElf_GetPlayer(this, play); if (func_80A01F90(&this->actor.world.pos, &player->actor.world.pos, 50.0f)) { if (this->unk_2C0 > 0) { @@ -249,12 +323,14 @@ void func_80A020A4(EnElf* this, PlayState* play) { } void func_80A0214C(EnElf* this, PlayState* play) { + Player* player = EnElf_GetPlayer(this, play); f32 xzDistToPlayer; if (this->unk_2C0 > 0) { this->unk_2C0--; } else { - xzDistToPlayer = this->actor.xzDistToPlayer; + xzDistToPlayer = sqrtf(SQ(this->actor.world.pos.x - player->actor.world.pos.x) + + SQ(this->actor.world.pos.z - player->actor.world.pos.z)); if (xzDistToPlayer < 50.0f) { if (Rand_ZeroOne() < 0.2f) { this->unk_2A8 = 2; @@ -318,10 +394,23 @@ f32 EnElf_GetColorValue(s32 colorFlag) { void EnElf_Init(Actor* thisx, PlayState* play) { EnElf* this = (EnElf*)thisx; s32 pad; - Player* player = GET_PLAYER(play); + Player* player; s32 colorConfig; s32 i; + if (thisx->params == FAIRY_NAVI) { + if ((thisx->parent != NULL) && (thisx->parent->id == ACTOR_PLAYER) && (thisx->parent->update != NULL)) { + player = (Player*)thisx->parent; + } else { + player = EnElf_FindUnassignedNaviPlayer(play); + thisx->parent = &player->actor; + } + + player->naviActor = thisx; + } else { + player = GET_PLAYER(play); + } + Actor_ProcessInitChain(thisx, sInitChain); SkelAnime_Init(play, &this->skelAnime, &gFairySkel, &gFairyAnim, this->jointTable, this->morphTable, 15); ActorShape_Init(&thisx->shape, 0.0f, NULL, 15.0f); @@ -425,6 +514,10 @@ void EnElf_Init(Actor* thisx, PlayState* play) { this->innerColor = sInnerColors[-colorConfig]; this->outerColor = sOuterColors[-colorConfig]; } + + if (thisx->params == FAIRY_NAVI) { + EnElf_GetPlayerNaviColors(player, &this->innerColor, &this->outerColor); + } } void func_80A0299C(EnElf* this, s32 arg1) { @@ -439,6 +532,14 @@ void func_80A029A8(EnElf* this, s16 increment) { void EnElf_Destroy(Actor* thisx, PlayState* play) { s32 pad; EnElf* this = (EnElf*)thisx; + Player* player; + + if (thisx->params == FAIRY_NAVI) { + player = EnElf_GetPlayer(this, play); + if ((player != NULL) && (player->naviActor == thisx)) { + player->naviActor = NULL; + } + } LightContext_RemoveLight(play, &play->lightCtx, this->lightNodeGlow); LightContext_RemoveLight(play, &play->lightCtx, this->lightNodeNoGlow); @@ -468,7 +569,7 @@ void func_80A02AA4(EnElf* this, PlayState* play) { } void func_80A02B38(EnElf* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = EnElf_GetPlayer(this, play); this->unk_2AA = (this->unk_2AC * 2) & 0xFFFF; this->unk_28C.x = Math_SinS(this->unk_2AC) * this->unk_2B8; @@ -544,7 +645,7 @@ void func_80A02F2C(EnElf* this, Vec3f* targetPos) { void func_80A03018(EnElf* this, PlayState* play) { s32 pad[2]; - Player* player = GET_PLAYER(play); + Player* player = EnElf_GetPlayer(this, play); s16 targetYaw; Vec3f* unk_28C = &this->unk_28C; @@ -604,9 +705,9 @@ void func_80A03148(EnElf* this, Vec3f* arg1, f32 arg2, f32 arg3, f32 arg4) { } void func_80A0329C(EnElf* this, PlayState* play) { - Player* refActor = GET_PLAYER(play); + Player* refActor = EnElf_GetPlayer(this, play); s32 pad; - Player* player = GET_PLAYER(play); + Player* player = EnElf_GetPlayer(this, play); f32 heightDiff; SkelAnime_Update(&this->skelAnime); @@ -636,10 +737,10 @@ void func_80A0329C(EnElf* this, PlayState* play) { if ((heightDiff > 0.0f) && (heightDiff < 60.0f)) { if (!func_80A01F90(&this->actor.world.pos, &refActor->actor.world.pos, 10.0f)) { if (GameInteractor_Should(VB_FAIRY_HEAL, true, this)) { - Health_ChangeBy(play, 128); + Health_ChangeBy(play, 128, refActor); } if (this->fairyFlags & FAIRY_FLAG_BIG) { - Magic_Fill(play); + Magic_FillForPlayer(play, refActor); } this->unk_2B8 = 50.0f; this->unk_2AC = refActor->actor.shape.rot.y; @@ -824,7 +925,7 @@ void EnElf_UpdateLights(EnElf* this, PlayState* play) { } if (this->fairyFlags & 0x20) { - player = GET_PLAYER(play); + player = EnElf_GetPlayer(this, play); Lights_PointNoGlowSetInfo(&this->lightInfoNoGlow, player->actor.world.pos.x, (s16)(player->actor.world.pos.y) + 60.0f, player->actor.world.pos.z, 255, 255, 255, 200); @@ -844,7 +945,7 @@ void EnElf_UpdateLights(EnElf* this, PlayState* play) { void func_80A03CF8(EnElf* this, PlayState* play) { Vec3f nextPos; Vec3f prevPos; - Player* player = GET_PLAYER(play); + Player* player = EnElf_GetPlayer(this, play); Actor* arrowPointedActor; f32 xScale; f32 distFromLinksHead; @@ -940,9 +1041,15 @@ void func_80A03CF8(EnElf* this, PlayState* play) { break; default: func_80A029A8(this, 1); - nextPos = play->actorCtx.targetCtx.naviRefPos; - nextPos.y += (1500.0f * this->actor.scale.y); - arrowPointedActor = play->actorCtx.targetCtx.arrowPointedActor; + arrowPointedActor = EnElf_GetHoverActor(this, play); + + if (arrowPointedActor != NULL) { + nextPos = arrowPointedActor->focus.pos; + nextPos.y += (1500.0f * this->actor.scale.y); + } else { + nextPos = player->bodyPartsPos[8]; + nextPos.y += (1500.0f * this->actor.scale.y); + } if (arrowPointedActor != NULL) { func_80A03148(this, &nextPos, 0.0f, 20.0f, 0.2f); @@ -1005,53 +1112,33 @@ void EnElf_ChangeColor(Color_RGBAf* dest, Color_RGBAf* newColor, Color_RGBAf* cu } void func_80A04414(EnElf* this, PlayState* play) { - Actor* arrowPointedActor = play->actorCtx.targetCtx.arrowPointedActor; - Player* player = GET_PLAYER(play); - f32 transitionRate; + Actor* arrowPointedActor = EnElf_GetHoverActor(this, play); + Player* player = EnElf_GetPlayer(this, play); + Color_RGBAf innerColor; + Color_RGBAf outerColor; u16 targetSound; - if (play->actorCtx.targetCtx.unk_40 != 0.0f) { - this->unk_2C6 = 0; - this->unk_29C = 1.0f; - - if (this->unk_2C7 == 0) { - Audio_PlayActorSound2(&this->actor, NA_SE_EV_FAIRY_DASH); - } - - } else { - if (this->unk_2C6 == 0) { - if ((arrowPointedActor == NULL) || - (Math_Vec3f_DistXYZ(&this->actor.world.pos, &play->actorCtx.targetCtx.naviRefPos) < 50.0f)) { - this->unk_2C6 = 1; - } - } else if (this->unk_29C != 0.0f) { - if (Math_StepToF(&this->unk_29C, 0.0f, 0.25f) != 0) { - this->innerColor = play->actorCtx.targetCtx.naviInner; - this->outerColor = play->actorCtx.targetCtx.naviOuter; - } else { - transitionRate = 0.25f / this->unk_29C; - EnElf_ChangeColor(&this->innerColor, &play->actorCtx.targetCtx.naviInner, &this->innerColor, - transitionRate); - EnElf_ChangeColor(&this->outerColor, &play->actorCtx.targetCtx.naviOuter, &this->outerColor, - transitionRate); - } - } - } + EnElf_GetPlayerNaviColors(player, &innerColor, &outerColor); + this->innerColor = innerColor; + this->outerColor = outerColor; if (this->fairyFlags & 1) { - if ((arrowPointedActor == NULL) || (player->focusActor == NULL)) { + if ((arrowPointedActor == NULL) || (player->focusActor == NULL) || (player->focusActor == &this->actor)) { this->fairyFlags ^= 1; } } else { - if ((arrowPointedActor != NULL) && (player->focusActor != NULL)) { + if ((arrowPointedActor != NULL) && (player->focusActor != NULL) && (player->focusActor != &this->actor)) { if (arrowPointedActor->category == ACTORCAT_NPC) { targetSound = NA_SE_VO_NAVY_HELLO; + } else if ((arrowPointedActor->category == ACTORCAT_ENEMY) || + (arrowPointedActor->category == ACTORCAT_BOSS)) { + targetSound = NA_SE_VO_NAVY_ENEMY; } else { targetSound = - (arrowPointedActor->category == ACTORCAT_ENEMY) ? NA_SE_VO_NAVY_ENEMY : NA_SE_VO_NAVY_HEAR; + NA_SE_VO_NAVY_HEAR; } - if (this->unk_2C7 == 0) { + if ((this->unk_2C7 == 0) && !player->isSecondPlayer) { Audio_PlayActorSound2(&this->actor, targetSound); } @@ -1063,7 +1150,7 @@ void func_80A04414(EnElf* this, PlayState* play) { void func_80A0461C(EnElf* this, PlayState* play) { s32 temp; Actor* arrowPointedActor; - Player* player = GET_PLAYER(play); + Player* player = EnElf_GetPlayer(this, play); if (play->csCtx.state != CS_STATE_IDLE) { if (play->csCtx.npcActions[8] != NULL) { @@ -1087,7 +1174,7 @@ void func_80A0461C(EnElf* this, PlayState* play) { } } else { - arrowPointedActor = play->actorCtx.targetCtx.arrowPointedActor; + arrowPointedActor = EnElf_GetHoverActor(this, play); if ((player->stateFlags1 & PLAYER_STATE1_GETTING_ITEM) || ((YREG(15) & 0x10) && Play_CheckViewpoint(play, 2))) { temp = 12; @@ -1104,7 +1191,7 @@ void func_80A0461C(EnElf* this, PlayState* play) { this->unk_2C0--; temp = 0; } else { - if (this->unk_2C7 == 0) { + if ((this->unk_2C7 == 0) && !player->isSecondPlayer) { Audio_PlayActorSound2(&this->actor, NA_SE_EV_NAVY_VANISH); } temp = 7; @@ -1148,7 +1235,7 @@ void func_80A0461C(EnElf* this, PlayState* play) { case 0: if (!(player->stateFlags2 & PLAYER_STATE2_NAVI_ACTIVE)) { temp = 7; - if (this->unk_2C7 == 0) { + if ((this->unk_2C7 == 0) && !player->isSecondPlayer) { Audio_PlayActorSound2(&this->actor, NA_SE_EV_NAVY_VANISH); } } @@ -1158,7 +1245,7 @@ void func_80A0461C(EnElf* this, PlayState* play) { func_80A0299C(this, 0x32); this->unk_2C0 = 42; temp = 11; - if (this->unk_2C7 == 0) { + if ((this->unk_2C7 == 0) && !player->isSecondPlayer) { Audio_PlayActorSound2(&this->actor, NA_SE_EV_FAIRY_DASH); } } @@ -1218,14 +1305,17 @@ void func_80A04D90(EnElf* this, PlayState* play) { // move to talk to player void func_80A04DE4(EnElf* this, PlayState* play) { Vec3f headCopy; - Player* player = GET_PLAYER(play); + Player* player = EnElf_GetPlayer(this, play); + Actor* hoverActor; Vec3f naviRefPos; if (this->fairyFlags & 0x10) { - naviRefPos = play->actorCtx.targetCtx.naviRefPos; + hoverActor = EnElf_GetHoverActor(this, play); - if ((player->focusActor == NULL) || (&player->actor == player->focusActor) || - (&this->actor == player->focusActor)) { + if (hoverActor != NULL) { + naviRefPos = hoverActor->focus.pos; + naviRefPos.y += 1500.0f * this->actor.scale.y; + } else { naviRefPos.x = player->bodyPartsPos[7].x + (Math_SinS(player->actor.shape.rot.y) * 20.0f); naviRefPos.y = player->bodyPartsPos[7].y + 5.0f; naviRefPos.z = player->bodyPartsPos[7].z + (Math_CosS(player->actor.shape.rot.y) * 20.0f); @@ -1250,7 +1340,7 @@ void func_80A04DE4(EnElf* this, PlayState* play) { // move after talking to player void func_80A04F94(EnElf* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = EnElf_GetPlayer(this, play); Math_SmoothStepToS(&this->actor.shape.rot.y, this->unk_2BC, 5, 0x1000, 0x400); this->timer++; @@ -1373,25 +1463,31 @@ void func_80A052F4(Actor* thisx, PlayState* play) { void func_80A053F0(Actor* thisx, PlayState* play) { u8 unk2C7; s32 pad; - Player* player = GET_PLAYER(play); + Player* player = EnElf_GetPlayer((EnElf*)thisx, play); EnElf* this = (EnElf*)thisx; + s32 disableNaviMessages = CVarGetInteger(CVAR_ENHANCEMENT("DisableNaviMessages"), 0); - if (player->naviTextId == 0) { - if (player->focusActor == NULL) { - if (((gSaveContext.naviTimer >= 600) && (gSaveContext.naviTimer <= 3000)) || (nREG(89) != 0)) { - player->naviTextId = ElfMessage_GetCUpText(play); + if (!disableNaviMessages) { + if (player->naviTextId == 0) { + if (player->focusActor == NULL) { + if (((gSaveContext.naviTimer >= 600) && (gSaveContext.naviTimer <= 3000)) || (nREG(89) != 0)) { + player->naviTextId = ElfMessage_GetCUpText(play); - if (player->naviTextId == 0x15F) { - player->naviTextId = 0; + if (player->naviTextId == 0x15F) { + player->naviTextId = 0; + } } } + } else if (player->naviTextId < 0) { + // trigger dialog instantly for negative message IDs + thisx->flags |= ACTOR_FLAG_TALK_OFFER_AUTO_ACCEPTED; } - } else if (player->naviTextId < 0) { - // trigger dialog instantly for negative message IDs - thisx->flags |= ACTOR_FLAG_TALK_OFFER_AUTO_ACCEPTED; + } else { + player->naviTextId = 0; + thisx->flags &= ~ACTOR_FLAG_TALK_OFFER_AUTO_ACCEPTED; } - if (Actor_ProcessTalkRequest(thisx, play)) { + if (!disableNaviMessages && Actor_ProcessTalkRequest(thisx, play)) { func_800F4524(&gSfxDefaultPos, NA_SE_VO_SK_LAUGH, 0x20); thisx->focus.pos = thisx->world.pos; @@ -1504,7 +1600,7 @@ void EnElf_Draw(Actor* thisx, PlayState* play) { EnElf* this = (EnElf*)thisx; s32 pad1; Gfx* dListHead; - Player* player = GET_PLAYER(play); + Player* player = EnElf_GetPlayer(this, play); if ((this->unk_2A8 != 8) && !(this->fairyFlags & 8)) { if (!(player->stateFlags1 & PLAYER_STATE1_FIRST_PERSON) || (kREG(90) < this->actor.projectedPos.z)) { diff --git a/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c index caea853340b..7a693de0f27 100644 --- a/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c +++ b/soh/src/overlays/actors/ovl_En_Floormas/z_en_floormas.c @@ -823,7 +823,7 @@ void EnFloormas_GrabLink(EnFloormas* this, PlayState* play) { } else { Player_PlaySfx(&player->actor, NA_SE_VO_LI_DAMAGE_S); } - play->damagePlayer(play, -8); + play->damagePlayer(play, player, -8); } } diff --git a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c index 9576c7a82fa..26fc99acab3 100644 --- a/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c +++ b/soh/src/overlays/actors/ovl_En_GirlA/z_en_girla.c @@ -972,7 +972,7 @@ void EnGirlA_ItemGive_Health(PlayState* play, EnGirlA* this) { GetItemEntry entry = ItemTable_Retrieve(this->getItemId); gSaveContext.ship.pendingSale = entry.itemId; gSaveContext.ship.pendingSaleMod = entry.modIndex; - Health_ChangeBy(play, this->itemCount); + Health_ChangeBy(play, this->itemCount, GET_PLAYER(play)); Rupees_ChangeBy(-this->basePrice); } diff --git a/soh/src/overlays/actors/ovl_En_Go/z_en_go.c b/soh/src/overlays/actors/ovl_En_Go/z_en_go.c index 949af288952..09be6c01e2e 100644 --- a/soh/src/overlays/actors/ovl_En_Go/z_en_go.c +++ b/soh/src/overlays/actors/ovl_En_Go/z_en_go.c @@ -715,7 +715,7 @@ void EnGo_StopRolling(EnGo* this, PlayState* play) { if (DECR(this->unk_20E) == 0) { if (this->collider.base.ocFlags2 & OC2_HIT_PLAYER) { this->collider.base.ocFlags2 &= ~OC2_HIT_PLAYER; - play->damagePlayer(play, -4); + play->damagePlayer(play, GET_PLAYER(play), -4); func_8002F71C(play, &this->actor, 4.0f, this->actor.yawTowardsPlayer, 6.0f); this->unk_20E = 0x10; } diff --git a/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c b/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c index ed409b115da..92f6eb9831f 100644 --- a/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c +++ b/soh/src/overlays/actors/ovl_En_Go2/z_en_go2.c @@ -911,7 +911,7 @@ s32 func_80A44AB0(EnGo2* this, PlayState* play) { arg2 = this->actionFunc == EnGo2_ContinueRolling ? 1.5f : this->actor.speedXZ * 1.5f; - play->damagePlayer(play, -4); + play->damagePlayer(play, player, -4); func_8002F71C(play, &this->actor, arg2, this->actor.yawTowardsPlayer, 6.0f); Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); this->collider.base.ocFlags1 &= ~0x8; diff --git a/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.c b/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.c index 3ca58303350..9107ed6b48c 100644 --- a/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.c +++ b/soh/src/overlays/actors/ovl_En_Heishi1/z_en_heishi1.c @@ -8,6 +8,7 @@ #include "objects/object_sd/object_sd.h" #include "vt.h" #include "soh/ResourceManagerHelpers.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS ACTOR_FLAG_UPDATE_CULLING_DISABLED @@ -124,15 +125,21 @@ void EnHeishi1_Init(Actor* thisx, PlayState* play) { (Flags_GetEventChkInf(EVENTCHKINF_LEARNED_ZELDAS_LULLABY)); if (this->type != 5) { - if ((gSaveContext.dayTime < 0xB888 || IS_DAY) && - ((!IS_RANDO && !Flags_GetEventChkInf(EVENTCHKINF_ZELDA_FLED_HYRULE_CASTLE)) || (IS_RANDO && !metZelda))) { + if (GameInteractor_Should(VB_WONDER_HEISHI_PATROLLING, + (gSaveContext.dayTime < 0xB888 || IS_DAY) && + ((!IS_RANDO && !Flags_GetEventChkInf(EVENTCHKINF_ZELDA_FLED_HYRULE_CASTLE)) || + (IS_RANDO && !metZelda)), + this)) { this->actionFunc = EnHeishi1_SetupWalk; } else { Actor_Kill(&this->actor); } } else { - if ((gSaveContext.dayTime >= 0xB889) || !IS_DAY || - (!IS_RANDO && Flags_GetEventChkInf(EVENTCHKINF_ZELDA_FLED_HYRULE_CASTLE)) || (IS_RANDO && metZelda)) { + if (GameInteractor_Should(VB_WONDER_HEISHI_PATROLLING, + (gSaveContext.dayTime >= 0xB889) || !IS_DAY || + (!IS_RANDO && Flags_GetEventChkInf(EVENTCHKINF_ZELDA_FLED_HYRULE_CASTLE)) || + (IS_RANDO && metZelda), + this)) { this->actionFunc = EnHeishi1_SetupWaitNight; } else { Actor_Kill(&this->actor); diff --git a/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c index b6c1b866bd8..e1662198a70 100644 --- a/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c +++ b/soh/src/overlays/actors/ovl_En_Heishi2/z_en_heishi2.c @@ -679,10 +679,12 @@ void func_80A5455C(EnHeishi2* this, PlayState* play) { pos.y = Rand_CenteredFloat(20.0f) + (this->unk_274.y - 40.0f); pos.z = Rand_CenteredFloat(20.0f) + (this->unk_274.z - 20.0f); rotY = Rand_CenteredFloat(7000.0f) + this->actor.yawTowardsPlayer; - bomb = (EnBom*)Actor_Spawn(&play->actorCtx, play, ACTOR_EN_BOM, pos.x, pos.y, pos.z, 0, rotY, 0, 0); - if (bomb != NULL) { - bomb->actor.speedXZ = Rand_CenteredFloat(5.0f) + 10.0f; - bomb->actor.velocity.y = Rand_CenteredFloat(5.0f) + 10.0f; + if (GameInteractor_Should(VB_WONDER_HEISHI_ITEM, true, &pos, rotY)) { + bomb = (EnBom*)Actor_Spawn(&play->actorCtx, play, ACTOR_EN_BOM, pos.x, pos.y, pos.z, 0, rotY, 0, 0); + if (bomb != NULL) { + bomb->actor.speedXZ = Rand_CenteredFloat(5.0f) + 10.0f; + bomb->actor.velocity.y = Rand_CenteredFloat(5.0f) + 10.0f; + } } // "This is down!" diff --git a/soh/src/overlays/actors/ovl_En_Holl/z_en_holl.c b/soh/src/overlays/actors/ovl_En_Holl/z_en_holl.c index 86030a52a93..7a72c4aac6e 100644 --- a/soh/src/overlays/actors/ovl_En_Holl/z_en_holl.c +++ b/soh/src/overlays/actors/ovl_En_Holl/z_en_holl.c @@ -2,6 +2,9 @@ #define FLAGS ACTOR_FLAG_UPDATE_CULLING_DISABLED +#define LOCAL_MP_DISABLED_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Disable") +#define LOCAL_MP_SECONDARY_AREA_LOAD_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.SecondaryPlayersLoadAreas") + // Horizontal Plane parameters #define PLANE_Y_MIN -50.0f @@ -46,6 +49,56 @@ static InitChainEntry sInitChain[] = { ICHAIN_F32(uncullZoneDownward, 400, ICHAIN_STOP), }; +static s32 EnHoll_SecondaryPlayersCanTrigger(PlayState* play) { + return !CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0) && CVarGetInteger(LOCAL_MP_SECONDARY_AREA_LOAD_CVAR, 0); +} + +static Player* EnHoll_GetTriggerPlayer(PlayState* play, EnHoll* this) { + Player* mainPlayer = GET_PLAYER(play); + Player* bestPlayer; + Actor* actor; + f32 dx; + f32 dy; + f32 dz; + f32 distSq; + f32 bestDistSq; + + if (mainPlayer == NULL) { + return NULL; + } + + if (!EnHoll_SecondaryPlayersCanTrigger(play)) { + return mainPlayer; + } + + bestPlayer = mainPlayer; + dx = mainPlayer->actor.world.pos.x - this->actor.world.pos.x; + dy = mainPlayer->actor.world.pos.y - this->actor.world.pos.y; + dz = mainPlayer->actor.world.pos.z - this->actor.world.pos.z; + bestDistSq = SQ(dx) + SQ(dy) + SQ(dz); + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* player = (Player*)actor; + + dx = player->actor.world.pos.x - this->actor.world.pos.x; + dy = player->actor.world.pos.y - this->actor.world.pos.y; + dz = player->actor.world.pos.z - this->actor.world.pos.z; + distSq = SQ(dx) + SQ(dy) + SQ(dz); + + if (distSq < bestDistSq) { + bestDistSq = distSq; + bestPlayer = player; + } + } + + actor = actor->next; + } + + return bestPlayer; +} + /** * These are all absolute distances in the relative z direction. That is, moving * towards or away from the "face" of the loading plane regardless of orientation. @@ -119,12 +172,16 @@ void EnHoll_SwapRooms(PlayState* play) { // Horizontal Planes void func_80A58DD4(EnHoll* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = EnHoll_GetTriggerPlayer(play, this); s32 phi_t0 = ((play->sceneNum == SCENE_SPIRIT_TEMPLE) ? 1 : 0) & 0xFFFFFFFF; Vec3f vec; f32 absZ; s32 transitionActorIdx; + if (player == NULL) { + return; + } + Actor_WorldToActorCoords(&this->actor, &vec, &player->actor.world.pos); this->side = (vec.z < 0.0f) ? 0 : 1; absZ = fabsf(vec.z); @@ -155,13 +212,17 @@ void func_80A58DD4(EnHoll* this, PlayState* play) { // Horizontal Planes void func_80A59014(EnHoll* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = EnHoll_GetTriggerPlayer(play, this); s32 useViewEye = gDbgCamEnabled || play->csCtx.state != CS_STATE_IDLE; Vec3f vec; s32 temp; f32 planeHalfWidth; f32 absZ; + if ((player == NULL) && !useViewEye) { + return; + } + Actor_WorldToActorCoords(&this->actor, &vec, (useViewEye) ? &play->view.eye : &player->actor.world.pos); planeHalfWidth = (((this->actor.params >> 6) & 7) == 6) ? PLANE_HALFWIDTH : PLANE_HALFWIDTH_2; @@ -186,11 +247,21 @@ void func_80A59014(EnHoll* this, PlayState* play) { // Vertical Planes void func_80A591C0(EnHoll* this, PlayState* play) { - Player* player = GET_PLAYER(play); - f32 absY = fabsf(this->actor.yDistToPlayer); + Player* player = EnHoll_GetTriggerPlayer(play, this); + f32 absY; + f32 yDist; + f32 xzDist; s32 transitionActorIdx; - if (this->actor.xzDistToPlayer < 500.0f && absY < 700.0f) { + if (player == NULL) { + return; + } + + yDist = player->actor.world.pos.y - this->actor.world.pos.y; + xzDist = Math_Vec3f_DistXZ(&this->actor.world.pos, &player->actor.world.pos); + absY = fabsf(yDist); + + if (xzDist < 500.0f && absY < 700.0f) { transitionActorIdx = (u16)this->actor.params >> 0xA; if (absY < 95.0f) { play->unk_11E18 = 0xFF; @@ -218,11 +289,22 @@ void func_80A591C0(EnHoll* this, PlayState* play) { // Vertical Planes void func_80A593A4(EnHoll* this, PlayState* play) { + Player* player = EnHoll_GetTriggerPlayer(play, this); + f32 xzDist; + f32 yDist; f32 absY; s32 side; s32 transitionActorIdx; - if ((this->actor.xzDistToPlayer < 120.0f) && (absY = fabsf(this->actor.yDistToPlayer), absY < 200.0f)) { + if (player == NULL) { + return; + } + + xzDist = Math_Vec3f_DistXZ(&this->actor.world.pos, &player->actor.world.pos); + yDist = player->actor.world.pos.y - this->actor.world.pos.y; + absY = fabsf(yDist); + + if ((xzDist < 120.0f) && (absY < 200.0f)) { if (absY < 50.0f) { play->unk_11E18 = 0xFF; } else { @@ -230,7 +312,7 @@ void func_80A593A4(EnHoll* this, PlayState* play) { } if (absY > 50.0f) { transitionActorIdx = (u16)this->actor.params >> 0xA; - side = (0.0f < this->actor.yDistToPlayer) ? 0 : 1; + side = (0.0f < yDist) ? 0 : 1; this->actor.room = play->transiActorCtx.list[transitionActorIdx].sides[side].room; if (this->actor.room != play->roomCtx.curRoom.num && func_8009728C(play, &play->roomCtx, this->actor.room) != 0) { @@ -246,15 +328,25 @@ void func_80A593A4(EnHoll* this, PlayState* play) { // Vertical Planes void func_80A59520(EnHoll* this, PlayState* play) { + Player* player = EnHoll_GetTriggerPlayer(play, this); + f32 xzDist; + f32 yDist; f32 absY; s8 side; s32 transitionActorIdx; - if (this->actor.xzDistToPlayer < 120.0f) { - absY = fabsf(this->actor.yDistToPlayer); + if (player == NULL) { + return; + } + + xzDist = Math_Vec3f_DistXZ(&this->actor.world.pos, &player->actor.world.pos); + yDist = player->actor.world.pos.y - this->actor.world.pos.y; + + if (xzDist < 120.0f) { + absY = fabsf(yDist); if (absY < 200.0f && absY > 50.0f) { transitionActorIdx = (u16)this->actor.params >> 0xA; - side = (0.0f < this->actor.yDistToPlayer) ? 0 : 1; + side = (0.0f < yDist) ? 0 : 1; this->actor.room = play->transiActorCtx.list[transitionActorIdx].sides[side].room; if (this->actor.room != play->roomCtx.curRoom.num && func_8009728C(play, &play->roomCtx, this->actor.room) != 0) { @@ -266,7 +358,7 @@ void func_80A59520(EnHoll* this, PlayState* play) { // Horizontal Planes void func_80A59618(EnHoll* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player; Vec3f vec; f32 absZ; s32 side; @@ -278,6 +370,11 @@ void func_80A59618(EnHoll* this, PlayState* play) { this->unk_14F = 0; } } else { + player = EnHoll_GetTriggerPlayer(play, this); + if (player == NULL) { + return; + } + Actor_WorldToActorCoords(&this->actor, &vec, &player->actor.world.pos); absZ = fabsf(vec.z); if (PLANE_Y_MIN < vec.y && vec.y < PLANE_Y_MAX && fabsf(vec.x) < PLANE_HALFWIDTH_2 && absZ < 100.0f) { diff --git a/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c b/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c index bcd1f362d0e..3d03a129cc4 100644 --- a/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c +++ b/soh/src/overlays/actors/ovl_En_Hy/z_en_hy.c @@ -610,7 +610,9 @@ s16 func_80A70058(PlayState* play, Actor* thisx) { case 0x70F1: case 0x70F2: case 0x70F3: - Rupees_ChangeBy(beggarRewards[this->actor.textId - 0x70F0]); + if (GameInteractor_Should(VB_BEGGAR_GIVE_ITEM, true, this)) { + Rupees_ChangeBy(beggarRewards[this->actor.textId - 0x70F0]); + } Animation_ChangeByInfo(&this->skelAnime, sAnimationInfo, ENHY_ANIM_17); Player_UpdateBottleHeld(play, GET_PLAYER(play), ITEM_BOTTLE, PLAYER_IA_BOTTLE); break; diff --git a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c index c4dbe3f3af0..7b03b7cdc36 100644 --- a/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c +++ b/soh/src/overlays/actors/ovl_En_Ik/z_en_ik.c @@ -785,7 +785,7 @@ void func_80A75FA0(Actor* thisx, PlayState* play) { player->invincibilityTimer = 0; } else { player->invincibilityTimer = 0; - play->damagePlayer(play, -64); + play->damagePlayer(play, player, -64); this->unk_2FE = 0; } } diff --git a/soh/src/overlays/actors/ovl_En_Ishi/z_en_ishi.c b/soh/src/overlays/actors/ovl_En_Ishi/z_en_ishi.c index c88e69fdde8..346979bfd78 100644 --- a/soh/src/overlays/actors/ovl_En_Ishi/z_en_ishi.c +++ b/soh/src/overlays/actors/ovl_En_Ishi/z_en_ishi.c @@ -371,18 +371,39 @@ void EnIshi_Wait(EnIshi* this, PlayState* play) { sFragmentSpawnFuncs[type](this, play); sDustSpawnFuncs[type](this, play); Actor_Kill(&this->actor); - } else if (this->actor.xzDistToPlayer < 600.0f) { - Collider_UpdateCylinder(&this->actor, &this->collider); - this->collider.base.acFlags &= ~AC_HIT; - CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base); - if (this->actor.xzDistToPlayer < 400.0f) { - CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); - if (this->actor.xzDistToPlayer < 90.0f) { - // GI_NONE in these cases allows the player to lift the actor - if (type == ROCK_LARGE) { - Actor_OfferGetItem(&this->actor, play, GI_NONE, 80.0f, 20.0f); - } else { - Actor_OfferGetItem(&this->actor, play, GI_NONE, 50.0f, 10.0f); + } else { + Actor* playerActor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + f32 minDistSq = 1.0e20f; + s32 sanity = 0; + + while ((playerActor != NULL) && (sanity < 2000)) { + if ((playerActor->id == ACTOR_PLAYER) && (playerActor->update != NULL)) { + f32 dx = this->actor.world.pos.x - playerActor->world.pos.x; + f32 dz = this->actor.world.pos.z - playerActor->world.pos.z; + f32 distSq = SQ(dx) + SQ(dz); + + if (distSq < minDistSq) { + minDistSq = distSq; + } + } + + playerActor = playerActor->next; + sanity++; + } + + if (minDistSq < SQ(600.0f)) { + Collider_UpdateCylinder(&this->actor, &this->collider); + this->collider.base.acFlags &= ~AC_HIT; + CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base); + if (minDistSq < SQ(400.0f)) { + CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); + if (minDistSq < SQ(90.0f)) { + // GI_NONE in these cases allows the player to lift the actor + if (type == ROCK_LARGE) { + Actor_OfferGetItem(&this->actor, play, GI_NONE, 80.0f, 20.0f); + } else { + Actor_OfferGetItem(&this->actor, play, GI_NONE, 50.0f, 10.0f); + } } } } diff --git a/soh/src/overlays/actors/ovl_En_Karebaba/z_en_karebaba.c b/soh/src/overlays/actors/ovl_En_Karebaba/z_en_karebaba.c index 0b1e6de23ed..3429145235f 100644 --- a/soh/src/overlays/actors/ovl_En_Karebaba/z_en_karebaba.c +++ b/soh/src/overlays/actors/ovl_En_Karebaba/z_en_karebaba.c @@ -256,7 +256,7 @@ void EnKarebaba_Awaken(EnKarebaba* this, PlayState* play) { } void EnKarebaba_Upright(EnKarebaba* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = Actor_GetClosestPlayerFromPos(play, &this->actor.home.pos); SkelAnime_Update(&this->skelAnime); diff --git a/soh/src/overlays/actors/ovl_En_Kusa/z_en_kusa.c b/soh/src/overlays/actors/ovl_En_Kusa/z_en_kusa.c index 818c7b4b038..a340b76d0fb 100644 --- a/soh/src/overlays/actors/ovl_En_Kusa/z_en_kusa.c +++ b/soh/src/overlays/actors/ovl_En_Kusa/z_en_kusa.c @@ -331,17 +331,35 @@ void EnKusa_Main(EnKusa* this, PlayState* play) { EnKusa_SetupCut(this); this->actor.flags |= ACTOR_FLAG_GRASS_DESTROYED; } else { - if (!(this->collider.base.ocFlags1 & OC1_TYPE_PLAYER) && (this->actor.xzDistToPlayer > 12.0f)) { + Actor* plActor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + f32 minDistSq = 1.0e20f; + s32 sanity = 0; + + while ((plActor != NULL) && (sanity < 2000)) { + if ((plActor->id == ACTOR_PLAYER) && (plActor->update != NULL)) { + f32 dx = this->actor.world.pos.x - plActor->world.pos.x; + f32 dz = this->actor.world.pos.z - plActor->world.pos.z; + f32 distSq = (dx * dx) + (dz * dz); + + if (distSq < minDistSq) { + minDistSq = distSq; + } + } + plActor = plActor->next; + sanity++; + } + + if (!(this->collider.base.ocFlags1 & OC1_TYPE_PLAYER) && (minDistSq > 144.0f)) { this->collider.base.ocFlags1 |= OC1_TYPE_PLAYER; } - - if (this->actor.xzDistToPlayer < 600.0f) { + + if (minDistSq < 360000.0f) { Collider_UpdateCylinder(&this->actor, &this->collider); CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base); - if (this->actor.xzDistToPlayer < 400.0f) { + if (minDistSq < 160000.0f) { CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); - if (this->actor.xzDistToPlayer < 100.0f) { + if (minDistSq < 10000.0f) { Actor_OfferCarry(&this->actor, play); } } diff --git a/soh/src/overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.c b/soh/src/overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.c index 4f838f5bbca..c354e840415 100644 --- a/soh/src/overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.c +++ b/soh/src/overlays/actors/ovl_En_M_Thunder/z_en_m_thunder.c @@ -60,10 +60,40 @@ void func_80A9EFE0(EnMThunder* this, EnMThunderActionFunc actionFunc) { this->actionFunc = actionFunc; } +static Player* EnMThunder_FindClosestPlayer(EnMThunder* this, PlayState* play) { + Actor* playerActor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + Player* closestPlayer = GET_PLAYER(play); + f32 currentMinDistSq = 9999999.9f; + + while (playerActor != NULL) { + if (playerActor->id == ACTOR_PLAYER) { + f32 distSq = Math_Vec3f_DistXZ(&this->actor.world.pos, &playerActor->world.pos); + + distSq *= distSq; + if (distSq < currentMinDistSq) { + currentMinDistSq = distSq; + closestPlayer = (Player*)playerActor; + } + } + + playerActor = playerActor->next; + } + + return closestPlayer; +} + +static Player* EnMThunder_GetPlayer(EnMThunder* this, PlayState* play) { + if ((this->actor.parent != NULL) && (this->actor.parent->id == ACTOR_PLAYER) && (this->actor.parent->update != NULL)) { + return (Player*)this->actor.parent; + } + + return GET_PLAYER(play); +} + void EnMThunder_Init(Actor* thisx, PlayState* play2) { PlayState* play = play2; EnMThunder* this = (EnMThunder*)thisx; - Player* player = GET_PLAYER(play); + Player* player = EnMThunder_FindClosestPlayer(this, play); Collider_InitCylinder(play, &this->collider); Collider_SetCylinder(play, &this->collider, &this->actor, &D_80AA0420); @@ -76,6 +106,7 @@ void EnMThunder_Init(Actor* thisx, PlayState* play2) { this->collider.dim.yShift = -20; this->unk_1C4 = 8; this->unk_1B4 = 0.0f; + this->actor.parent = &player->actor; this->actor.world.pos = player->bodyPartsPos[0]; this->unk_1AC = 0.0f; this->unk_1BC = 0.0f; @@ -85,9 +116,10 @@ void EnMThunder_Init(Actor* thisx, PlayState* play2) { this->unk_1CA = 0; if (player->stateFlags2 & PLAYER_STATE2_SPIN_ATTACKING) { - if (!gSaveContext.isMagicAcquired || (gSaveContext.magicState != MAGIC_STATE_IDLE) || + if (!Magic_GetLevelForPort(player->isSecondPlayer ? player->controllerPort : 1) || + (Magic_GetStateForPlayer(player) != MAGIC_STATE_IDLE) || (((this->actor.params & 0xFF00) >> 8) && - !(Magic_RequestChange(play, (this->actor.params & 0xFF00) >> 8, MAGIC_CONSUME_NOW)))) { + !(Magic_RequestChangeForPlayer(play, player, (this->actor.params & 0xFF00) >> 8, MAGIC_CONSUME_NOW)))) { Audio_PlaySoundGeneral(NA_SE_IT_ROLLING_CUT, &player->actor.projectedPos, 4, &gSfxDefaultFreqAndVolScale, &gSfxDefaultFreqAndVolScale, &gSfxDefaultReverb); Audio_PlaySoundGeneral(NA_SE_IT_SWORD_SWING_HARD, &player->actor.projectedPos, 4, @@ -116,7 +148,7 @@ void EnMThunder_Destroy(Actor* thisx, PlayState* play) { EnMThunder* this = (EnMThunder*)thisx; if (this->unk_1CA != 0) { - Magic_Reset(play); + Magic_ResetForPlayer(play, EnMThunder_GetPlayer(this, play)); } Collider_DestroyCylinder(play, &this->collider); @@ -129,7 +161,7 @@ void func_80A9F314(PlayState* play, f32 arg1) { } void func_80A9F350(EnMThunder* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = EnMThunder_GetPlayer(this, play); if (player->stateFlags2 & PLAYER_STATE2_SPIN_ATTACKING) { if (player->meleeWeaponAnimation >= 0x18) { @@ -149,7 +181,7 @@ void func_80A9F350(EnMThunder* this, PlayState* play) { } void func_80A9F408(EnMThunder* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = EnMThunder_GetPlayer(this, play); Actor* child = this->actor.child; this->unk_1B8 = player->unk_858; @@ -158,9 +190,10 @@ void func_80A9F408(EnMThunder* this, PlayState* play) { if (this->unk_1CA == 0) { if (player->unk_858 >= 0.1f) { - if ((gSaveContext.magicState != MAGIC_STATE_IDLE) || + if ((Magic_GetStateForPlayer(player) != MAGIC_STATE_IDLE) || (((this->actor.params & 0xFF00) >> 8) && - !(Magic_RequestChange(play, (this->actor.params & 0xFF00) >> 8, MAGIC_CONSUME_WAIT_PREVIEW)))) { + !(Magic_RequestChangeForPlayer(play, player, (this->actor.params & 0xFF00) >> 8, + MAGIC_CONSUME_WAIT_PREVIEW)))) { func_80A9F350(this, play); func_80A9EFE0(this, func_80A9F350); this->unk_1C8 = 0; @@ -194,7 +227,7 @@ void func_80A9F408(EnMThunder* this, PlayState* play) { } else { player->stateFlags2 &= ~PLAYER_STATE2_SPIN_ATTACKING; if ((this->actor.params & 0xFF00) >> 8) { - gSaveContext.magicState = MAGIC_STATE_CONSUME_SETUP; + Magic_SetStateForPlayer(player, MAGIC_STATE_CONSUME_SETUP); } if (player->unk_858 < 0.85f) { this->collider.info.toucher.dmgFlags = D_80AA044C[this->unk_1C7]; @@ -271,7 +304,7 @@ void func_80A9F938(EnMThunder* this, PlayState* play) { } void func_80A9F9B4(EnMThunder* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = EnMThunder_GetPlayer(this, play); if (Math_StepToF(&this->unk_1AC, 0.0f, 1 / 16.0f)) { Actor_Kill(&this->actor); @@ -320,7 +353,7 @@ void EnMThunder_Draw(Actor* thisx, PlayState* play2) { static f32 D_80AA046C[] = { 0.1f, 0.15f, 0.2f, 0.25f, 0.3f, 0.25f, 0.2f, 0.15f }; PlayState* play = play2; EnMThunder* this = (EnMThunder*)thisx; - Player* player = GET_PLAYER(play); + Player* player = EnMThunder_GetPlayer(this, play); f32 phi_f14; s32 phi_t1; diff --git a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c index 78f75dabd5a..31c307d2176 100644 --- a/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c +++ b/soh/src/overlays/actors/ovl_En_Mb/z_en_mb.c @@ -863,7 +863,7 @@ void EnMb_ClubAttack(EnMb* this, PlayState* play) { player->invincibilityTimer = 0; } else { player->invincibilityTimer = 0; - play->damagePlayer(play, -8); + play->damagePlayer(play, GET_PLAYER(play), -8); } } @@ -947,7 +947,7 @@ void EnMb_SpearPatrolPrepareAndCharge(EnMb* this, PlayState* play) { player->invincibilityTimer = 0; } else { player->invincibilityTimer = 0; - play->damagePlayer(play, -8); + play->damagePlayer(play, GET_PLAYER(play), -8); } } if (!(this->attackCollider.base.atFlags & AT_BOUNCED)) { @@ -1016,7 +1016,7 @@ void EnMb_SpearPatrolImmediateCharge(EnMb* this, PlayState* play) { player->invincibilityTimer = 0; } else { player->invincibilityTimer = 0; - play->damagePlayer(play, -8); + play->damagePlayer(play, GET_PLAYER(play), -8); } } if (!(this->attackCollider.base.atFlags & AT_BOUNCED)) { @@ -1150,7 +1150,7 @@ void EnMb_SpearGuardWalk(EnMb* this, PlayState* play) { s32 beforeCurFrame; s32 pad1; s32 pad2; - Player* player = GET_PLAYER(play); + Player* player = Actor_GetClosestPlayerFromPos(play, &this->actor.home.pos); s16 relYawTowardsPlayer = this->actor.yawTowardsPlayer - this->actor.shape.rot.y; s16 yawTowardsHome; f32 playSpeedAbs; @@ -1263,7 +1263,7 @@ void EnMb_SpearPatrolWalkTowardsWaypoint(EnMb* this, PlayState* play) { } void EnMb_ClubWaitPlayerNear(EnMb* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = Actor_GetClosestPlayerFromPos(play, &this->actor.home.pos); s32 pad; s16 relYawFromPlayer = this->actor.world.rot.y - this->actor.yawTowardsPlayer; diff --git a/soh/src/overlays/actors/ovl_En_Part/z_en_part.c b/soh/src/overlays/actors/ovl_En_Part/z_en_part.c index 599aa39c152..7a990901023 100644 --- a/soh/src/overlays/actors/ovl_En_Part/z_en_part.c +++ b/soh/src/overlays/actors/ovl_En_Part/z_en_part.c @@ -190,7 +190,7 @@ void func_80ACE5C8(EnPart* this, PlayState* play) { player->invincibilityTimer = 0; } else { player->invincibilityTimer = 0; - play->damagePlayer(play, -8); + play->damagePlayer(play, GET_PLAYER(play), -8); } } func_8002F71C(play, this->actor.parent, (650.0f - this->actor.parent->xzDistToPlayer) * 0.04f + 4.0f, diff --git a/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c index 0957398887b..119ad137d37 100644 --- a/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c +++ b/soh/src/overlays/actors/ovl_En_Peehat/z_en_peehat.c @@ -503,7 +503,7 @@ void EnPeehat_Ground_SetStateSeekPlayer(EnPeehat* this) { } void EnPeehat_Ground_StateSeekPlayer(EnPeehat* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = Actor_GetClosestPlayerFromPos(play, &this->actor.home.pos); Math_SmoothStepToF(&this->actor.speedXZ, 3.0f, 1.0f, 0.25f, 0.0f); Math_SmoothStepToF(&this->actor.world.pos.y, this->actor.floorHeight + 80.0f, 1.0f, 3.0f, 0.0f); @@ -664,7 +664,7 @@ void EnPeehat_Ground_SetStateHover(EnPeehat* this) { void EnPeehat_Ground_StateHover(EnPeehat* this, PlayState* play) { f32 cos; - Player* player = GET_PLAYER(play); + Player* player = Actor_GetClosestPlayerFromPos(play, &this->actor.home.pos); // hover but don't gain altitude if (this->actor.world.pos.y - this->actor.floorHeight > 75.0f) { @@ -713,7 +713,7 @@ void EnPeehat_Ground_StateReturnHome(EnPeehat* this, PlayState* play) { s16 yRot; Player* player; - player = GET_PLAYER(play); + player = Actor_GetClosestPlayerFromPos(play, &this->actor.home.pos); if (this->actor.world.pos.y - this->actor.floorHeight > 75.0f) { this->actor.world.pos.y -= 1.0f; } else { diff --git a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c index 062be3268d7..c2dd12fab63 100644 --- a/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c +++ b/soh/src/overlays/actors/ovl_En_Rd/z_en_rd.c @@ -502,7 +502,7 @@ void func_80AE3454(EnRd* this, PlayState* play) { case 1: Animation_PlayLoop(&this->skelAnime, &gGibdoRedeadGrabAttackAnim); this->unk_304++; - play->damagePlayer(play, -8); + play->damagePlayer(play, GET_PLAYER(play), -8); func_800AA000(this->actor.xzDistToPlayer, 0xFF, 1, 0xC); this->unk_319 = 20; case 0: @@ -536,7 +536,7 @@ void func_80AE3454(EnRd* this, PlayState* play) { this->unk_319--; if (this->unk_319 == 0) { - play->damagePlayer(play, -8); + play->damagePlayer(play, GET_PLAYER(play), -8); func_800AA000(this->actor.xzDistToPlayer, 0xF0, 1, 0xC); this->unk_319 = 20; Player_PlaySfx(&player->actor, NA_SE_VO_LI_DAMAGE_S + player->ageProperties->unk_92); diff --git a/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c index b7612dd9179..9acc95f2214 100644 --- a/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c +++ b/soh/src/overlays/actors/ovl_En_Skb/z_en_skb.c @@ -263,7 +263,7 @@ void EnSkb_Advance(EnSkb* this, PlayState* play) { s32 thisKeyFrame; s32 prevKeyFrame; f32 playSpeed; - Player* player = GET_PLAYER(play); + Player* player = Actor_GetClosestPlayerFromPos(play, &this->actor.home.pos); if ((this->unk_283 != 0) && ((play->gameplayFrames & 0xF) == 0)) { this->unk_288 = Rand_CenteredFloat(50000.0f); diff --git a/soh/src/overlays/actors/ovl_En_Ssh/z_en_ssh.c b/soh/src/overlays/actors/ovl_En_Ssh/z_en_ssh.c index 37c9df3d30b..bb51291a75c 100644 --- a/soh/src/overlays/actors/ovl_En_Ssh/z_en_ssh.c +++ b/soh/src/overlays/actors/ovl_En_Ssh/z_en_ssh.c @@ -456,14 +456,20 @@ void EnSsh_CheckBodyStickHit(EnSsh* this, PlayState* play) { s32 EnSsh_CheckHitPlayer(EnSsh* this, PlayState* play) { s32 i; s32 hit = false; + Player* hitPlayer = NULL; if ((this->hitCount == 0) && (this->spinTimer == 0)) { return false; } for (i = 0; i < 3; i++) { if (this->colCylinder[i + 3].base.ocFlags2 & OC2_HIT_PLAYER) { + Actor* hitActor = this->colCylinder[i + 3].base.oc; + this->colCylinder[i + 3].base.ocFlags2 &= ~OC2_HIT_PLAYER; hit = true; + if ((hitActor != NULL) && (hitActor->id == ACTOR_PLAYER)) { + hitPlayer = (Player*)hitActor; + } } } if (!hit) { @@ -475,7 +481,11 @@ s32 EnSsh_CheckHitPlayer(EnSsh* this, PlayState* play) { } Audio_PlayActorSound2(&this->actor, NA_SE_EN_STALTU_ROLL); Audio_PlayActorSound2(&this->actor, NA_SE_VO_ST_ATTACK); - play->damagePlayer(play, -8); + if (hitPlayer != NULL) { + play->damagePlayer(play, hitPlayer, -8); + } else { + play->damagePlayer(play, GET_PLAYER(play), -8); + } func_8002F71C(play, &this->actor, 4.0f, this->actor.yawTowardsPlayer, 6.0f); this->hitCount--; return true; diff --git a/soh/src/overlays/actors/ovl_En_St/z_en_st.c b/soh/src/overlays/actors/ovl_En_St/z_en_st.c index 99f4617bb9a..304b1bbd7cb 100644 --- a/soh/src/overlays/actors/ovl_En_St/z_en_st.c +++ b/soh/src/overlays/actors/ovl_En_St/z_en_st.c @@ -401,7 +401,7 @@ s32 EnSt_CheckHitLink(EnSt* this, PlayState* play) { } this->gaveDamageSpinTimer = 30; - play->damagePlayer(play, -8); + play->damagePlayer(play, GET_PLAYER(play), -8); Audio_PlayActorSound2(&player->actor, NA_SE_PL_BODY_HIT); func_8002F71C(play, &this->actor, 4.0f, this->actor.yawTowardsPlayer, 6.0f); return true; diff --git a/soh/src/overlays/actors/ovl_En_Trap/z_en_trap.c b/soh/src/overlays/actors/ovl_En_Trap/z_en_trap.c index 7ef4c6a4c98..efb59c0ec7c 100644 --- a/soh/src/overlays/actors/ovl_En_Trap/z_en_trap.c +++ b/soh/src/overlays/actors/ovl_En_Trap/z_en_trap.c @@ -174,7 +174,7 @@ void EnTrap_Update(Actor* thisx, PlayState* play) { } else { angleToKnockPlayer = thisx->yawTowardsPlayer; } - play->damagePlayer(play, -4); + play->damagePlayer(play, GET_PLAYER(play), -4); func_8002F7A0(play, thisx, 6.0f, angleToKnockPlayer, 6.0f); this->playerDmgTimer = 15; } diff --git a/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.c b/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.c index 77cedb32638..37ff2830216 100644 --- a/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.c +++ b/soh/src/overlays/actors/ovl_En_Wallmas/z_en_wallmas.c @@ -383,7 +383,7 @@ void EnWallmas_JumpToCeiling(EnWallmas* this, PlayState* play) { } void EnWallmas_ReturnToCeiling(EnWallmas* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = Actor_GetClosestPlayerFromPos(play, &this->actor.home.pos); SkelAnime_Update(&this->skelAnime); if (this->skelAnime.curFrame > 20.0f) { this->timer += 9; @@ -506,7 +506,7 @@ void EnWallmas_TakePlayer(EnWallmas* this, PlayState* play) { } void EnWallmas_WaitForProximity(EnWallmas* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = Actor_GetClosestPlayerFromPos(play, &this->actor.home.pos); if (this->actor.params == WMT_SHADOWTAG || Math_Vec3f_DistXZ(&this->actor.home.pos, &player->actor.world.pos) < 200.0f) { EnWallmas_TimerInit(this, play); diff --git a/soh/src/overlays/actors/ovl_En_Wonder_Item/z_en_wonder_item.c b/soh/src/overlays/actors/ovl_En_Wonder_Item/z_en_wonder_item.c index d7c0ddf702a..4003ab624f7 100644 --- a/soh/src/overlays/actors/ovl_En_Wonder_Item/z_en_wonder_item.c +++ b/soh/src/overlays/actors/ovl_En_Wonder_Item/z_en_wonder_item.c @@ -6,6 +6,7 @@ #include "z_en_wonder_item.h" #include "vt.h" +#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h" #define FLAGS 0 @@ -54,8 +55,8 @@ const ActorInit En_Wonder_Item_InitVars = { NULL, }; -static Vec3f sTagPointsFree[9]; -static Vec3f sTagPointsOrdered[9]; +Vec3f sTagPointsFree[9]; // SOH [Randomizer] remove static to use in ShuffleWonderItems +Vec3f sTagPointsOrdered[9]; // SOH [Randomizer] remove static to use in ShuffleWonderItems void EnWonderItem_Destroy(Actor* thisx, PlayState* play) { s32 pad; @@ -75,24 +76,26 @@ void EnWonderItem_DropCollectible(EnWonderItem* this, PlayState* play, s32 autoC s32 i; s32 randomDrop; - Sfx_PlaySfxCentered(NA_SE_SY_GET_ITEM); + if (GameInteractor_Should(VB_WONDER_DROP_ITEM, true, this)) { + Sfx_PlaySfxCentered(NA_SE_SY_GET_ITEM); - if (this->dropCount == 0) { - this->dropCount++; - } - for (i = this->dropCount; i > 0; i--) { - if (this->itemDrop < WONDERITEM_DROP_RANDOM) { - if ((this->itemDrop == WONDERITEM_DROP_FLEXIBLE) || !autoCollect) { - Item_DropCollectible(play, &this->actor.world.pos, dropTable[this->itemDrop]); - } else { - Item_DropCollectible(play, &this->actor.world.pos, dropTable[this->itemDrop] | 0x8000); - } - } else { - randomDrop = this->itemDrop - WONDERITEM_DROP_RANDOM; - if (!autoCollect) { - Item_DropCollectibleRandom(play, NULL, &this->actor.world.pos, randomDrop); + if (this->dropCount == 0) { + this->dropCount++; + } + for (i = this->dropCount; i > 0; i--) { + if (this->itemDrop < WONDERITEM_DROP_RANDOM) { + if ((this->itemDrop == WONDERITEM_DROP_FLEXIBLE) || !autoCollect) { + Item_DropCollectible(play, &this->actor.world.pos, dropTable[this->itemDrop]); + } else { + Item_DropCollectible(play, &this->actor.world.pos, dropTable[this->itemDrop] | 0x8000); + } } else { - Item_DropCollectibleRandom(play, NULL, &this->actor.world.pos, randomDrop | 0x8000); + randomDrop = this->itemDrop - WONDERITEM_DROP_RANDOM; + if (!autoCollect) { + Item_DropCollectibleRandom(play, NULL, &this->actor.world.pos, randomDrop); + } else { + Item_DropCollectibleRandom(play, NULL, &this->actor.world.pos, randomDrop | 0x8000); + } } } } @@ -104,9 +107,10 @@ void EnWonderItem_DropCollectible(EnWonderItem* this, PlayState* play, s32 autoC void EnWonderItem_Init(Actor* thisx, PlayState* play) { static u32 collisionTypes[] = { - 0x00000702 /* sword slash */, 0x0001F820 /* arrow */, 0x00000040 /* hammer */, 0x00000008 /* bomb */, - 0x00000004 /* slingshot */, 0x00000010 /* boomerang */, 0x00000080 /* hookshot */, - }; + 0x00000702 /* sword slash */, 0x0001F820 /* arrow */, 0x00000040 /* hammer */, + 0x00000008 /* bomb */, 0x00000004 /* slingshot */, 0x00000010 /* boomerang */, + 0x00000080 /* hookshot */, 0x0001F820 | 0x00000080 /* arrow or hookshot */ + }; // SOH [Port] add final array entry to avoid OOB behavior on Water MQ Torches room wonder item s32 pad; s16 colTypeIndex; EnWonderItem* this = (EnWonderItem*)thisx; @@ -125,7 +129,7 @@ void EnWonderItem_Init(Actor* thisx, PlayState* play) { this->switchFlag = -1; } this->actor.targetMode = 1; - if ((this->switchFlag >= 0) && Flags_GetSwitch(play, this->switchFlag)) { + if (GameInteractor_Should(VB_WONDER_SPAWN, (this->switchFlag >= 0) && Flags_GetSwitch(play, this->switchFlag))) { osSyncPrintf(VT_FGCOL(GREEN) "☆☆☆☆☆ You are Shock! ☆☆☆☆☆ %d\n" VT_RST, this->switchFlag); Actor_Kill(&this->actor); return; @@ -312,7 +316,7 @@ void EnWonderItem_BombSoldier(EnWonderItem* this, PlayState* play) { // "Careless soldier spawned" osSyncPrintf(VT_FGCOL(YELLOW) "☆☆☆☆☆ うっかり兵セット完了 ☆☆☆☆☆ \n" VT_RST); } - if (this->switchFlag >= 0) { + if (GameInteractor_Should(VB_WONDER_DROP_ITEM, this->switchFlag >= 0, this)) { Flags_SetSwitch(play, this->switchFlag); } Actor_Kill(&this->actor); diff --git a/soh/src/overlays/actors/ovl_Magic_Dark/z_magic_dark.c b/soh/src/overlays/actors/ovl_Magic_Dark/z_magic_dark.c index 526932d19af..12f9f79c1bf 100644 --- a/soh/src/overlays/actors/ovl_Magic_Dark/z_magic_dark.c +++ b/soh/src/overlays/actors/ovl_Magic_Dark/z_magic_dark.c @@ -36,9 +36,40 @@ const ActorInit Magic_Dark_InitVars = { // unused static Color_RGBA8 D_80B88B10[] = { { 50, 100, 150, 200 }, { 255, 200, 150, 100 } }; +static Player* MagicDark_FindPlayerByPort(PlayState* play, u8 controllerPort) { + Actor* actor; + + if (controllerPort == 1) { + return GET_PLAYER(play); + } + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* candidate = (Player*)actor; + + if (candidate->isSecondPlayer && (candidate->controllerPort == controllerPort)) { + return candidate; + } + } + + actor = actor->next; + } + + return GET_PLAYER(play); +} + +static Player* MagicDark_GetCastingPlayer(MagicDark* this, PlayState* play) { + if ((this->actor.parent != NULL) && (this->actor.parent->id == ACTOR_PLAYER) && (this->actor.parent->update != NULL)) { + return (Player*)this->actor.parent; + } + + return MagicDark_FindPlayerByPort(play, this->actor.home.rot.z); +} + void MagicDark_Init(Actor* thisx, PlayState* play) { MagicDark* this = (MagicDark*)thisx; - Player* player = GET_PLAYER(play); + Player* player = MagicDark_GetCastingPlayer(this, play); if (!LINK_IS_ADULT) { this->scale = 0.4f; @@ -64,15 +95,17 @@ void MagicDark_Init(Actor* thisx, PlayState* play) { } void MagicDark_Destroy(Actor* thisx, PlayState* play) { + MagicDark* this = (MagicDark*)thisx; + if (gSaveContext.nayrusLoveTimer == 0) { - Magic_Reset(play); + Magic_ResetForPlayer(play, MagicDark_GetCastingPlayer(this, play)); } } void MagicDark_DiamondUpdate(Actor* thisx, PlayState* play) { MagicDark* this = (MagicDark*)thisx; u8 phi_a0; - Player* player = GET_PLAYER(play); + Player* player = MagicDark_GetCastingPlayer(this, play); s16 pad; s16 nayrusLoveTimer = gSaveContext.nayrusLoveTimer; s32 msgMode = play->msgCtx.msgMode; @@ -169,7 +202,7 @@ void MagicDark_DimLighting(PlayState* play, f32 intensity) { void MagicDark_OrbUpdate(Actor* thisx, PlayState* play) { MagicDark* this = (MagicDark*)thisx; s32 pad; - Player* player = GET_PLAYER(play); + Player* player = MagicDark_GetCastingPlayer(this, play); func_8002F974(&this->actor, NA_SE_PL_MAGIC_SOUL_BALL - SFX_FLAG); if (this->timer < 35) { @@ -212,7 +245,7 @@ void MagicDark_DiamondDraw(Actor* thisx, PlayState* play) { Gfx_SetupDL_25Xlu(play->state.gfxCtx); { - Player* player = GET_PLAYER(play); + Player* player = MagicDark_GetCastingPlayer(this, play); f32 heightDiff; this->actor.world.pos.x = player->bodyPartsPos[0].x; @@ -243,7 +276,7 @@ void MagicDark_DiamondDraw(Actor* thisx, PlayState* play) { void MagicDark_OrbDraw(Actor* thisx, PlayState* play) { MagicDark* this = (MagicDark*)thisx; Vec3f pos; - Player* player = GET_PLAYER(play); + Player* player = MagicDark_GetCastingPlayer(this, play); s32 pad; f32 sp6C = play->state.frames & 0x1F; diff --git a/soh/src/overlays/actors/ovl_Magic_Fire/z_magic_fire.c b/soh/src/overlays/actors/ovl_Magic_Fire/z_magic_fire.c index 18fbd3f5646..efc80f1eb84 100644 --- a/soh/src/overlays/actors/ovl_Magic_Fire/z_magic_fire.c +++ b/soh/src/overlays/actors/ovl_Magic_Fire/z_magic_fire.c @@ -76,6 +76,37 @@ static u8 sVertexIndices[] = { 14, 20, 21, 23, 28, 30, 33, 34, 40, 41, 43, 48, 50, 55, 57, 62, 64, 65, 73, 74, }; +static Player* MagicFire_FindPlayerByPort(PlayState* play, u8 controllerPort) { + Actor* actor; + + if (controllerPort == 1) { + return GET_PLAYER(play); + } + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* candidate = (Player*)actor; + + if (candidate->isSecondPlayer && (candidate->controllerPort == controllerPort)) { + return candidate; + } + } + + actor = actor->next; + } + + return GET_PLAYER(play); +} + +static Player* MagicFire_GetCastingPlayer(MagicFire* this, PlayState* play) { + if ((this->actor.parent != NULL) && (this->actor.parent->id == ACTOR_PLAYER) && (this->actor.parent->update != NULL)) { + return (Player*)this->actor.parent; + } + + return MagicFire_FindPlayerByPort(play, this->actor.home.rot.z); +} + void MagicFire_Init(Actor* thisx, PlayState* play) { MagicFire* this = (MagicFire*)thisx; @@ -94,12 +125,13 @@ void MagicFire_Init(Actor* thisx, PlayState* play) { } void MagicFire_Destroy(Actor* thisx, PlayState* play) { - Magic_Reset(play); + MagicFire* this = (MagicFire*)thisx; + Magic_ResetForPlayer(play, MagicFire_GetCastingPlayer(this, play)); } void MagicFire_UpdateBeforeCast(Actor* thisx, PlayState* play) { MagicFire* this = (MagicFire*)thisx; - Player* player = GET_PLAYER(play); + Player* player = MagicFire_GetCastingPlayer(this, play); if ((play->msgCtx.msgMode == MSGMODE_OCARINA_CORRECT_PLAYBACK) || (play->msgCtx.msgMode == MSGMODE_SONG_PLAYED)) { Actor_Kill(&this->actor); @@ -116,7 +148,7 @@ void MagicFire_UpdateBeforeCast(Actor* thisx, PlayState* play) { void MagicFire_Update(Actor* thisx, PlayState* play) { MagicFire* this = (MagicFire*)thisx; - Player* player = GET_PLAYER(play); + Player* player = MagicFire_GetCastingPlayer(this, play); s32 pad; this->actor.world.pos = player->actor.world.pos; diff --git a/soh/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.c b/soh/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.c index fe23ea1a2ea..1a89a942193 100644 --- a/soh/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.c +++ b/soh/src/overlays/actors/ovl_Magic_Wind/z_magic_wind.c @@ -43,9 +43,40 @@ void MagicWind_SetupAction(MagicWind* this, MagicWindFunc actionFunc) { this->actionFunc = actionFunc; } +static Player* MagicWind_FindPlayerByPort(PlayState* play, u8 controllerPort) { + Actor* actor; + + if (controllerPort == 1) { + return GET_PLAYER(play); + } + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* candidate = (Player*)actor; + + if (candidate->isSecondPlayer && (candidate->controllerPort == controllerPort)) { + return candidate; + } + } + + actor = actor->next; + } + + return GET_PLAYER(play); +} + +static Player* MagicWind_GetCastingPlayer(MagicWind* this, PlayState* play) { + if ((this->actor.parent != NULL) && (this->actor.parent->id == ACTOR_PLAYER) && (this->actor.parent->update != NULL)) { + return (Player*)this->actor.parent; + } + + return MagicWind_FindPlayerByPort(play, this->actor.home.rot.z); +} + void MagicWind_Init(Actor* thisx, PlayState* play) { MagicWind* this = (MagicWind*)thisx; - Player* player = GET_PLAYER(play); + Player* player = MagicWind_GetCastingPlayer(this, play); if (SkelCurve_Init(play, &this->skelCurve, &sSkel, &sAnim) == 0) { // "Magic_Wind_Actor_ct (): Construct failed" @@ -71,7 +102,7 @@ void MagicWind_Init(Actor* thisx, PlayState* play) { void MagicWind_Destroy(Actor* thisx, PlayState* play) { MagicWind* this = (MagicWind*)thisx; SkelCurve_Destroy(play, &this->skelCurve); - Magic_Reset(play); + Magic_ResetForPlayer(play, MagicWind_GetCastingPlayer(this, play)); // "wipe out" LOG_STRING("消滅"); } @@ -87,7 +118,7 @@ void MagicWind_UpdateAlpha(f32 alpha) { } void MagicWind_WaitForTimer(MagicWind* this, PlayState* play) { - Player* player = GET_PLAYER(play); + Player* player = MagicWind_GetCastingPlayer(this, play); if (this->timer > 0) { this->timer--; diff --git a/soh/src/overlays/actors/ovl_Obj_Kibako/z_obj_kibako.c b/soh/src/overlays/actors/ovl_Obj_Kibako/z_obj_kibako.c index 0a53b34e775..211a47bd554 100644 --- a/soh/src/overlays/actors/ovl_Obj_Kibako/z_obj_kibako.c +++ b/soh/src/overlays/actors/ovl_Obj_Kibako/z_obj_kibako.c @@ -200,19 +200,38 @@ void ObjKibako_Idle(ObjKibako* this, PlayState* play) { ObjKibako_SpawnCollectible(this, play); Actor_Kill(&this->actor); } else { + Actor* playerActor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + f32 minDistSq = 1.0e20f; + s32 sanity = 0; + + while ((playerActor != NULL) && (sanity < 2000)) { + if ((playerActor->id == ACTOR_PLAYER) && (playerActor->update != NULL)) { + f32 dx = this->actor.world.pos.x - playerActor->world.pos.x; + f32 dz = this->actor.world.pos.z - playerActor->world.pos.z; + f32 distSq = SQ(dx) + SQ(dz); + + if (distSq < minDistSq) { + minDistSq = distSq; + } + } + + playerActor = playerActor->next; + sanity++; + } + Actor_MoveXZGravity(&this->actor); Actor_UpdateBgCheckInfo(play, &this->actor, 19.0f, 20.0f, 0.0f, 5); - if (!(this->collider.base.ocFlags1 & OC1_TYPE_PLAYER) && (this->actor.xzDistToPlayer > 28.0f)) { + if (!(this->collider.base.ocFlags1 & OC1_TYPE_PLAYER) && (minDistSq > SQ(28.0f))) { this->collider.base.ocFlags1 |= OC1_TYPE_PLAYER; } - if (this->actor.xzDistToPlayer < 600.0f) { + if (minDistSq < SQ(600.0f)) { Collider_UpdateCylinder(&this->actor, &this->collider); CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base); - if (this->actor.xzDistToPlayer < 180.0f) { + if (minDistSq < SQ(180.0f)) { CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); } } - if (this->actor.xzDistToPlayer < 100.0f) { + if (minDistSq < SQ(100.0f)) { Actor_OfferCarry(&this->actor, play); } } diff --git a/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.c b/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.c index 643b7bca3d4..cfa76cfe428 100644 --- a/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.c +++ b/soh/src/overlays/actors/ovl_Obj_Tsubo/z_obj_tsubo.c @@ -259,16 +259,40 @@ void ObjTsubo_Idle(ObjTsubo* this, PlayState* play) { SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 20, NA_SE_EV_POT_BROKEN); Actor_Kill(&this->actor); } else { - if (this->actor.xzDistToPlayer < 600.0f) { + Actor* playerActor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + Player* nearestPlayer = NULL; + f32 minDistSq = 1.0e20f; + s16 yawTowardsNearestPlayer = 0; + s32 sanity = 0; + + while ((playerActor != NULL) && (sanity < 2000)) { + if ((playerActor->id == ACTOR_PLAYER) && (playerActor->update != NULL)) { + Player* player = (Player*)playerActor; + f32 dx = this->actor.world.pos.x - player->actor.world.pos.x; + f32 dz = this->actor.world.pos.z - player->actor.world.pos.z; + f32 distSq = SQ(dx) + SQ(dz); + + if (distSq < minDistSq) { + minDistSq = distSq; + nearestPlayer = player; + yawTowardsNearestPlayer = Math_Vec3f_Yaw(&this->actor.world.pos, &player->actor.world.pos); + } + } + + playerActor = playerActor->next; + sanity++; + } + + if (minDistSq < SQ(600.0f)) { Collider_UpdateCylinder(&this->actor, &this->collider); this->collider.base.acFlags &= ~AC_HIT; CollisionCheck_SetAC(play, &play->colChkCtx, &this->collider.base); - if (this->actor.xzDistToPlayer < 150.0f) { + if (minDistSq < SQ(150.0f)) { CollisionCheck_SetOC(play, &play->colChkCtx, &this->collider.base); } } - if (this->actor.xzDistToPlayer < 100.0f) { - temp_v0 = this->actor.yawTowardsPlayer - GET_PLAYER(play)->actor.world.rot.y; + if ((nearestPlayer != NULL) && (minDistSq < SQ(100.0f))) { + temp_v0 = yawTowardsNearestPlayer - nearestPlayer->actor.world.rot.y; phi_v1 = ABS(temp_v0); if (phi_v1 >= 0x5556) { // GI_NONE in this case allows the player to lift the actor 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 130644968db..143dc3851a6 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -19,6 +19,7 @@ #include "overlays/effects/ovl_Effect_Ss_Fhg_Flash/z_eff_ss_fhg_flash.h" #include "overlays/misc/ovl_kaleido_scope/z_kaleido_scope.h" #include "objects/gameplay_keep/gameplay_keep.h" +#include "objects/object_link_boy/object_link_boy.h" #include "objects/object_link_child/object_link_child.h" #include #include "soh/Enhancements/item-tables/ItemTableTypes.h" @@ -41,6 +42,33 @@ // This is called "adjusted" for now. #define PLAYER_ANIM_ADJUSTED_SPEED (2.0f / 3.0f) +#define LOCAL_MP_PLAYER_COUNT_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCount") +#define LOCAL_MP_DISABLED_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.Disable") +#define LOCAL_MP_ORIGINAL_ITEMS_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.OriginalItemSystem") +#define LOCAL_MP_RADIAL_QUICK_ITEM_MENU_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.RadialQuickItemMenu") +#define LOCAL_MP_INDEPENDENT_SLOT_USAGE_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.IndependentSlotUsage") +#define LOCAL_MP_SECOND_ITEM_SLOT_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.SecondItemSlot") +#define LOCAL_MP_PLAYER_CARRY_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.PlayerCarry") +#define LOCAL_MP_ELASTIC_ROPE_MODE_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.ElasticRopeMode") +#define LOCAL_MP_ELASTIC_ROPE_STRENGTH_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.ElasticRopeStrength") +#define LOCAL_MP_SECONDARY_AREA_LOAD_CVAR CVAR_ENHANCEMENT("LocalMultiplayer.SecondaryPlayersLoadAreas") + +static s16* Player_GetHealthPtr(Player* this) { + if ((this != NULL) && this->isSecondPlayer) { + if (this->controllerPort == 4) { + return &gSaveContext.health4; + } + + if (this->controllerPort == 3) { + return &gSaveContext.health3; + } + + return &gSaveContext.health2; + } + + return &gSaveContext.health; +} + typedef enum { /* 0x00 */ KNOB_ANIM_ADULT_L, /* 0x01 */ KNOB_ANIM_CHILD_L, @@ -250,8 +278,8 @@ s32 Player_StartFishing(PlayState* play); s32 func_80852F38(PlayState* play, Player* this); s32 Player_TryCsAction(PlayState* play, Actor* actor, s32 csAction); void func_80853080(Player* this, PlayState* play); -s32 Player_InflictDamage(PlayState* play, s32 damage); -s32 Player_InflictDamageModified(PlayState* play, s32 damage, u8 modified); +s32 Player_InflictDamage(PlayState* play, Player* this, s32 damage); +s32 Player_InflictDamageModified(PlayState* play, Player* this, s32 damage, u8 modified); void Player_StartTalking(PlayState* play, Actor* actor); void Player_Action_80840450(Player* this, PlayState* play); @@ -291,6 +319,7 @@ void Player_Action_80846120(Player* this, PlayState* play); void Player_Action_80846260(Player* this, PlayState* play); void Player_Action_80846358(Player* this, PlayState* play); void Player_Action_80846408(Player* this, PlayState* play); +void Player_Action_CarriedByPlayer(Player* this, PlayState* play); void Player_Action_808464B0(Player* this, PlayState* play); void Player_Action_80846578(Player* this, PlayState* play); void Player_Action_8084B1D8(Player* this, PlayState* play); @@ -2058,6 +2087,52 @@ void Player_AnimReplaceNormalPlayLoopAdjusted(PlayState* play, Player* this, Lin Player_AnimReplacePlayLoopAdjusted(play, this, anim, 0x1C); } +static Camera* Player_GetCameraForPlayer(PlayState* play, Player* this) { + s32 camId; + + if ((play == NULL) || (this == NULL)) { + return NULL; + } + + for (camId = SUBCAM_FIRST; camId < NUM_CAMS; camId++) { + Camera* cam = play->cameraPtrs[camId]; + + if ((cam != NULL) && (cam->player == this)) { + return cam; + } + } + + if ((play->cameraPtrs[MAIN_CAM] != NULL) && (play->cameraPtrs[MAIN_CAM]->player == this)) { + return play->cameraPtrs[MAIN_CAM]; + } + + return NULL; +} + +static Camera* Player_GetCameraForPlayerOrMain(PlayState* play, Player* this) { + Camera* cam = Player_GetCameraForPlayer(play, this); + + if (cam != NULL) { + return cam; + } + + return Play_GetCamera(play, MAIN_CAM); +} + +static s16 Player_GetInputDirYawForPlayer(PlayState* play, Player* this) { + Camera* cam; + + cam = Player_GetCameraForPlayer(play, this); + if (cam == NULL) { + cam = GET_ACTIVE_CAM(play); + if (cam == NULL) { + return 0; + } + } + + return Camera_GetInputDirYaw(cam); +} + void Player_ProcessControlStick(PlayState* play, Player* this) { s8 spinAngle; s8 direction; @@ -2067,7 +2142,7 @@ void Player_ProcessControlStick(PlayState* play, Player* this) { func_80077D10(&sControlStickMagnitude, &sControlStickAngle, sControlInput); - sControlStickWorldYaw = Camera_GetInputDirYaw(GET_ACTIVE_CAM(play)) + sControlStickAngle; + sControlStickWorldYaw = Player_GetInputDirYawForPlayer(play, this) + sControlStickAngle; this->controlStickDataIndex = (this->controlStickDataIndex + 1) % 4; @@ -2478,8 +2553,275 @@ s32 Player_ItemIsItemAction(s32 item1, s32 itemAction) { } } +static s32 Player_GetLocalMultiplayerPlayerCount(void) { + s32 playerCount; + + if (CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0)) { + return 1; + } + + playerCount = CVarGetInteger(LOCAL_MP_PLAYER_COUNT_CVAR, 2); + if (playerCount < 1) { + playerCount = 1; + } else if (playerCount > 4) { + playerCount = 4; + } + + return playerCount; +} + +static s32 Player_IsOriginalMultiplayerItemModeEnabled(void) { + return CVarGetInteger(LOCAL_MP_ORIGINAL_ITEMS_CVAR, 1) && (Player_GetLocalMultiplayerPlayerCount() > 1); +} + +static s32 Player_IsCarryThrowSystemEnabled(void) { + return !CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0) && + (Player_GetLocalMultiplayerPlayerCount() > 1) && + CVarGetInteger(LOCAL_MP_PLAYER_CARRY_CVAR, 1); +} + +static s32 Player_ShouldSuppressSecondaryInputDuringCutscene(PlayState* play, Player* this) { + return this->isSecondPlayer && (Player_GetLocalMultiplayerPlayerCount() > 1) && + (play->csCtx.state != CS_STATE_IDLE); +} + +// Returns non-zero if the given quick item is restricted by the current scene. +// Default to not restricted to preserve pre-existing behavior when no special +// restrictions are required. +static s32 Player_IsItemRestrictedByScene(PlayState* play, s32 item, s32 index) { + (void)play; + (void)item; + (void)index; + return 0; +} + +// Returns non-zero if the given quick item is restricted by the player's state. +// Default to not restricted. +static s32 Player_IsItemRestrictedByState(Player* this, s32 item, s32 index) { + (void)this; + (void)item; + (void)index; + return 0; +} + +static s32 Player_IsIndependentQuickSlotUsageEnabled(void) { + return Player_IsOriginalMultiplayerItemModeEnabled() && + CVarGetInteger(LOCAL_MP_SECOND_ITEM_SLOT_CVAR, 1) && + CVarGetInteger(LOCAL_MP_INDEPENDENT_SLOT_USAGE_CVAR, 0); +} + +static s32 Player_IsSecondQuickItemSlotEnabled(void) { + return Player_IsOriginalMultiplayerItemModeEnabled() && + CVarGetInteger(LOCAL_MP_SECOND_ITEM_SLOT_CVAR, 1); +} + +static s32 Player_IsRadialQuickItemMenuEnabled(void) { + return Player_IsOriginalMultiplayerItemModeEnabled() && + CVarGetInteger(LOCAL_MP_RADIAL_QUICK_ITEM_MENU_CVAR, 0); +} + +static s32 Player_GetQuickItemOnButtonForPlayer(PlayState* play, Player* this, s32 index, s32 slotIndex) { + u8 controllerPort; + s32 quickButtonIndex; + s32 item; + + if ((index < 0) || (index >= 5)) { + return ITEM_NONE; + } else if (play->bombchuBowlingStatus != 0) { + return (play->bombchuBowlingStatus > 0) ? ITEM_BOMBCHU : ITEM_NONE; + } + + if ((slotIndex < 0) || (slotIndex > 1)) { + slotIndex = 0; + } + + if (index == 0) { + return B_BTN_ITEM; + } + + controllerPort = this->isSecondPlayer ? this->controllerPort : 1; + quickButtonIndex = Interface_GetQuickItemButtonIndex(controllerPort); + if (index != quickButtonIndex) { + return ITEM_NONE; + } + + item = Interface_GetQuickItemForPortAndSlot(controllerPort, slotIndex); + if (Player_IsItemRestrictedByScene(play, item, index) || Player_IsItemRestrictedByState(this, item, index)) { + return ITEM_NONE; + } + + return item; +} + +static s32 Player_CanCycleToItem(u8 item) { + if (LINK_AGE_IN_YEARS == YEARS_CHILD) { + switch (item) { + case ITEM_BOW: + case ITEM_ARROW_FIRE: + case ITEM_ARROW_ICE: + case ITEM_ARROW_LIGHT: + case ITEM_HOOKSHOT: + case ITEM_LONGSHOT: + case ITEM_HAMMER: + case ITEM_BOW_ARROW_FIRE: + case ITEM_BOW_ARROW_ICE: + case ITEM_BOW_ARROW_LIGHT: + return false; + default: + return true; + } + } + + switch (item) { + case ITEM_STICK: + case ITEM_SLINGSHOT: + case ITEM_BOOMERANG: + case ITEM_BEAN: + return false; + default: + return true; + } +} + +static s32 Player_IsMultiplayerQuickMenuItemBlocked(u8 item) { + switch (item) { + case ITEM_OCARINA_FAIRY: + case ITEM_OCARINA_TIME: + case ITEM_BEAN: + case ITEM_LENS: + case ITEM_DINS_FIRE: + case ITEM_NAYRUS_LOVE: + case ITEM_FARORES_WIND: + case ITEM_WEIRD_EGG: + case ITEM_CHICKEN: + case ITEM_LETTER_ZELDA: + case ITEM_POCKET_EGG: + case ITEM_POCKET_CUCCO: + case ITEM_COJIRO: + case ITEM_ODD_MUSHROOM: + case ITEM_ODD_POTION: + case ITEM_SAW: + case ITEM_SWORD_BROKEN: + case ITEM_PRESCRIPTION: + case ITEM_FROG: + case ITEM_EYEDROPS: + case ITEM_CLAIM_CHECK: + return true; + + default: + return false; + } +} + +static void Player_HandleOriginalMultiplayerItemCycle(Player* this, PlayState* play, Input* input) { + s32 controllerPort; + s32 secondSlotEnabled; + s32 selectedSlot; + s32 independentSlotUsage; + s32 direction; + s32 currentSlot; + s32 i; + s32 startSlot; + u8 currentItem; + + if (!Player_IsOriginalMultiplayerItemModeEnabled()) { + return; + } + + if (Player_InCsMode(play) || (play->pauseCtx.state != 0) || (play->transitionTrigger != TRANS_TRIGGER_OFF)) { + return; + } + + controllerPort = this->isSecondPlayer ? this->controllerPort : 1; + secondSlotEnabled = Player_IsSecondQuickItemSlotEnabled(); + selectedSlot = secondSlotEnabled ? Interface_GetQuickItemSelectedSlotForPort(controllerPort) : 0; + independentSlotUsage = Player_IsIndependentQuickSlotUsageEnabled(); + direction = 0; + + if (this->isSecondPlayer) { + if (!(this->inputEnabled && (this->controllerPort > 0) && (this->controllerPort <= 4))) { + return; + } + } + + if (secondSlotEnabled) { + if (independentSlotUsage) { + if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) { + selectedSlot = (selectedSlot == 0) ? 1 : 0; + Interface_SetQuickItemSelectedSlotForPort(controllerPort, selectedSlot); + Sfx_PlaySfxCentered(NA_SE_SY_CURSOR); + } + } else { + if (CHECK_BTN_ALL(input->press.button, BTN_DUP)) { + Interface_SetQuickItemSelectedSlotForPort(controllerPort, 0); + selectedSlot = 0; + Sfx_PlaySfxCentered(NA_SE_SY_CURSOR); + } else if (CHECK_BTN_ALL(input->press.button, BTN_DDOWN)) { + Interface_SetQuickItemSelectedSlotForPort(controllerPort, 1); + selectedSlot = 1; + Sfx_PlaySfxCentered(NA_SE_SY_CURSOR); + } + } + } + + if (Player_IsRadialQuickItemMenuEnabled()) { + return; + } + + if (CHECK_BTN_ALL(input->press.button, BTN_DLEFT)) { + direction = -1; + } else if (CHECK_BTN_ALL(input->press.button, BTN_DRIGHT)) { + direction = 1; + } + + if (direction == 0) { + return; + } + + currentItem = Interface_GetQuickItemForPortAndSlot(controllerPort, selectedSlot); + currentSlot = Interface_GetQuickItemInventorySlotForPortAndSlot(controllerPort, selectedSlot); + + if ((currentSlot < 0) || (currentSlot >= 24) || (gSaveContext.inventory.items[currentSlot] != currentItem)) { + currentSlot = -1; + for (i = 0; i < 24; i++) { + if (gSaveContext.inventory.items[i] == currentItem) { + currentSlot = i; + break; + } + } + } + + startSlot = (currentSlot == -1) ? 0 : currentSlot; + for (i = 1; i <= 24; i++) { + s32 checkSlot = startSlot + (direction * i); + u8 item; + + while (checkSlot < 0) { + checkSlot += 24; + } + while (checkSlot >= 24) { + checkSlot -= 24; + } + + item = gSaveContext.inventory.items[checkSlot]; + if ((item != ITEM_NONE) && (item != ITEM_SOLD_OUT) && Player_CanCycleToItem(item) && + (!this->isSecondPlayer || !Player_IsMultiplayerQuickMenuItemBlocked(item))) { + Interface_SetQuickItemForPortAndSlot(play, controllerPort, selectedSlot, item, checkSlot); + Sfx_PlaySfxCentered(NA_SE_SY_CURSOR); + break; + } + } +} + s32 Player_GetItemOnButton(PlayState* play, s32 index) { - if (index >= ((CVarGetInteger(CVAR_ENHANCEMENT("DpadEquips"), 0) != 0) ? 8 : 4)) { + s32 dpadEquipsEnabled = CVarGetInteger(CVAR_ENHANCEMENT("DpadEquips"), 0) && + !Player_IsOriginalMultiplayerItemModeEnabled(); + + if (index < 0) { + return ITEM_NONE; + } + + if (index >= (dpadEquipsEnabled ? 8 : 4)) { return ITEM_NONE; } else if (play->bombchuBowlingStatus != 0) { return (play->bombchuBowlingStatus > 0) ? ITEM_BOMBCHU : ITEM_NONE; @@ -2513,42 +2855,154 @@ s32 Player_GetItemOnButton(PlayState* play, s32 index) { */ void Player_ProcessItemButtons(Player* this, PlayState* play) { s32 maskItemAction; + s32 controllerPort; + s32 quickButtonIndex; + s32 buttonItem0; + s32 quickItemSlot1; + s32 quickItemSlot2; + s32 quickItemModeEnabled; + s32 secondSlotEnabled; + s32 independentSlotUsage; + s32 quickSelectedSlot; + s32 quickUseSlotForL; + s32 lPressed; + s32 downPressed; + s32 maxItemButtons; + s32 buttonIndex; s32 item; s32 i; + quickItemModeEnabled = Player_IsOriginalMultiplayerItemModeEnabled(); + controllerPort = this->isSecondPlayer ? this->controllerPort : 1; + quickButtonIndex = Interface_GetQuickItemButtonIndex(controllerPort); + buttonItem0 = Player_GetItemOnButton(play, 0); + secondSlotEnabled = Player_IsSecondQuickItemSlotEnabled(); + quickItemSlot1 = quickItemModeEnabled ? Player_GetQuickItemOnButtonForPlayer(play, this, quickButtonIndex, 0) + : ITEM_NONE; + quickItemSlot2 = (quickItemModeEnabled && secondSlotEnabled) + ? Player_GetQuickItemOnButtonForPlayer(play, this, quickButtonIndex, 1) + : ITEM_NONE; + independentSlotUsage = Player_IsIndependentQuickSlotUsageEnabled(); + maxItemButtons = ARRAY_COUNT(sItemButtons); + if (this->currentMask != PLAYER_MASK_NONE && !CVarGetInteger(CVAR_ENHANCEMENT("PersistentMasks"), 0)) { maskItemAction = this->currentMask - 1 + PLAYER_IA_MASK_KEATON; - bool hasOnDpad = false; - if (CVarGetInteger(CVAR_ENHANCEMENT("DpadEquips"), 0) != 0) { - for (int buttonIndex = 0; buttonIndex < 4; buttonIndex++) { - hasOnDpad |= Player_ItemIsItemAction(DPAD_ITEM(buttonIndex), maskItemAction); + if (quickItemModeEnabled) { + if (!Player_ItemIsItemAction(quickItemSlot1, maskItemAction) && + !Player_ItemIsItemAction(quickItemSlot2, maskItemAction)) { + this->currentMask = PLAYER_MASK_NONE; + } + } else { + bool hasOnDpad = false; + if (CVarGetInteger(CVAR_ENHANCEMENT("DpadEquips"), 0) != 0) { + for (int dpadButtonIndex = 0; dpadButtonIndex < 4; dpadButtonIndex++) { + hasOnDpad |= Player_ItemIsItemAction(DPAD_ITEM(dpadButtonIndex), maskItemAction); + } + } + if (!Player_ItemIsItemAction(C_BTN_ITEM(0), maskItemAction) && + !Player_ItemIsItemAction(C_BTN_ITEM(1), maskItemAction) && + !Player_ItemIsItemAction(C_BTN_ITEM(2), maskItemAction) && !hasOnDpad) { + this->currentMask = PLAYER_MASK_NONE; } - } - if (!Player_ItemIsItemAction(C_BTN_ITEM(0), maskItemAction) && - !Player_ItemIsItemAction(C_BTN_ITEM(1), maskItemAction) && - !Player_ItemIsItemAction(C_BTN_ITEM(2), maskItemAction) && !hasOnDpad) { - this->currentMask = PLAYER_MASK_NONE; } } if (!(this->stateFlags1 & (PLAYER_STATE1_CARRYING_ACTOR | PLAYER_STATE1_IN_CUTSCENE)) && !func_8008F128(this)) { if (this->itemAction >= PLAYER_IA_FISHING_POLE) { - bool hasOnDpad = false; - if (CVarGetInteger(CVAR_ENHANCEMENT("DpadEquips"), 0) != 0) { - for (int buttonIndex = 0; buttonIndex < 4; buttonIndex++) { - hasOnDpad |= Player_ItemIsInUse(this, DPAD_ITEM(buttonIndex)); + if (quickItemModeEnabled) { + if (!Player_ItemIsInUse(this, buttonItem0) && !Player_ItemIsInUse(this, quickItemSlot1) && + !Player_ItemIsInUse(this, quickItemSlot2)) { + Player_UseItem(play, this, ITEM_NONE); + return; + } + } else { + bool hasOnDpad = false; + if (CVarGetInteger(CVAR_ENHANCEMENT("DpadEquips"), 0) != 0) { + for (int dpadButtonIndex = 0; dpadButtonIndex < 4; dpadButtonIndex++) { + hasOnDpad |= Player_ItemIsInUse(this, DPAD_ITEM(dpadButtonIndex)); + } + } + if (!Player_ItemIsInUse(this, B_BTN_ITEM) && !Player_ItemIsInUse(this, C_BTN_ITEM(0)) && + !Player_ItemIsInUse(this, C_BTN_ITEM(1)) && !Player_ItemIsInUse(this, C_BTN_ITEM(2)) && + !hasOnDpad) { + Player_UseItem(play, this, ITEM_NONE); + return; } } - if (!Player_ItemIsInUse(this, B_BTN_ITEM) && !Player_ItemIsInUse(this, C_BTN_ITEM(0)) && - !Player_ItemIsInUse(this, C_BTN_ITEM(1)) && !Player_ItemIsInUse(this, C_BTN_ITEM(2)) && !hasOnDpad) { - Player_UseItem(play, this, ITEM_NONE); - return; + } + + if (quickItemModeEnabled) { + s32 mappedSlot; + + quickSelectedSlot = secondSlotEnabled ? Interface_GetQuickItemSelectedSlotForPort(controllerPort) : 0; + quickUseSlotForL = independentSlotUsage ? 0 : quickSelectedSlot; + lPressed = CHECK_BTN_ALL(sControlInput->press.button, BTN_L); + downPressed = secondSlotEnabled && independentSlotUsage && CHECK_BTN_ALL(sControlInput->press.button, BTN_DDOWN); + + i = -1; + mappedSlot = independentSlotUsage ? 0 : quickSelectedSlot; + + if (lPressed || downPressed) { + i = quickButtonIndex; + mappedSlot = lPressed ? quickUseSlotForL : 1; + item = Player_GetQuickItemOnButtonForPlayer(play, this, i, mappedSlot); + Interface_SetQuickItemUseSlotForPort(controllerPort, mappedSlot); + } else { + for (buttonIndex = 0; buttonIndex < 4; buttonIndex++) { + if (CHECK_BTN_ALL(sControlInput->press.button, sItemButtons[buttonIndex])) { + i = buttonIndex; + break; + } + } + + item = Player_GetQuickItemOnButtonForPlayer(play, this, i, mappedSlot); + + if (i == quickButtonIndex) { + Interface_SetQuickItemUseSlotForPort(controllerPort, mappedSlot); + } } + + if (item >= ITEM_NONE_FE) { + lPressed = CHECK_BTN_ALL(sControlInput->cur.button, BTN_L); + downPressed = secondSlotEnabled && independentSlotUsage && CHECK_BTN_ALL(sControlInput->cur.button, BTN_DDOWN); + + if (lPressed || downPressed) { + i = quickButtonIndex; + mappedSlot = lPressed ? quickUseSlotForL : 1; + } else { + i = -1; + for (buttonIndex = 0; buttonIndex < 4; buttonIndex++) { + if (CHECK_BTN_ALL(sControlInput->cur.button, sItemButtons[buttonIndex])) { + i = buttonIndex; + break; + } + } + + mappedSlot = independentSlotUsage ? 0 : quickSelectedSlot; + } + + item = Player_GetQuickItemOnButtonForPlayer(play, this, i, mappedSlot); + + if (i == quickButtonIndex) { + Interface_SetQuickItemUseSlotForPort(controllerPort, mappedSlot); + } + + if ((item < ITEM_NONE_FE) && (Player_ItemToItemAction(item) == this->heldItemAction)) { + sHeldItemButtonIsHeldDown = true; + } + } else if (GameInteractor_Should(VB_CHANGE_HELD_ITEM_AND_USE_ITEM, true, item)) { + this->heldItemButton = i; + Player_UseItem(play, this, item); + } + + return; } - for (i = 0; i < ARRAY_COUNT(sItemButtons); i++) { - if (CHECK_BTN_ALL(sControlInput->press.button, sItemButtons[i])) { + i = -1; + for (buttonIndex = 0; buttonIndex < maxItemButtons; buttonIndex++) { + if (CHECK_BTN_ALL(sControlInput->press.button, sItemButtons[buttonIndex])) { + i = buttonIndex; break; } } @@ -2556,8 +3010,10 @@ void Player_ProcessItemButtons(Player* this, PlayState* play) { item = Player_GetItemOnButton(play, i); if (item >= ITEM_NONE_FE) { - for (i = 0; i < ARRAY_COUNT(sItemButtons); i++) { - if (CHECK_BTN_ALL(sControlInput->cur.button, sItemButtons[i])) { + i = -1; + for (buttonIndex = 0; buttonIndex < maxItemButtons; buttonIndex++) { + if (CHECK_BTN_ALL(sControlInput->cur.button, sItemButtons[buttonIndex])) { + i = buttonIndex; break; } } @@ -2567,9 +3023,12 @@ void Player_ProcessItemButtons(Player* this, PlayState* play) { if ((item < ITEM_NONE_FE) && (Player_ItemToItemAction(item) == this->heldItemAction)) { sHeldItemButtonIsHeldDown = true; } - } else if (GameInteractor_Should(VB_CHANGE_HELD_ITEM_AND_USE_ITEM, true, item)) { - this->heldItemButton = i; - Player_UseItem(play, this, item); + } else { + if (GameInteractor_Should(VB_CHANGE_HELD_ITEM_AND_USE_ITEM, true, item)) { + buttonIndex = i; + this->heldItemButton = buttonIndex; + Player_UseItem(play, this, item); + } } } } @@ -2630,7 +3089,7 @@ void Player_UpdateItems(Player* this, PlayState* play) { (CVarGetInteger(CVAR_ENHANCEMENT("QuickPutaway"), 0) || !(this->stateFlags1 & PLAYER_STATE1_START_CHANGING_HELD_ITEM)) && ((this->heldItemAction == this->itemAction) || (this->stateFlags1 & PLAYER_STATE1_SHIELDING)) && - (gSaveContext.health != 0) && (play->csCtx.state == CS_STATE_IDLE) && (this->csAction == 0) && + (*Player_GetHealthPtr(this) != 0) && (play->csCtx.state == CS_STATE_IDLE) && (this->csAction == 0) && (play->shootingGalleryStatus == 0) && (play->activeCamera == MAIN_CAM) && (play->transitionTrigger != TRANS_TRIGGER_START) && (gSaveContext.timerState != TIMER_STATE_STOP)) { Player_ProcessItemButtons(this, play); @@ -2675,7 +3134,7 @@ s32 func_8083442C(Player* this, PlayState* play) { s32 magicArrowType; if ((this->heldItemAction >= PLAYER_IA_BOW_FIRE) && (this->heldItemAction <= PLAYER_IA_BOW_0E) && - (gSaveContext.magicState != MAGIC_STATE_IDLE)) { + (Magic_GetStateForPlayer(this) != MAGIC_STATE_IDLE)) { Sfx_PlaySfxCentered(NA_SE_SY_ERROR); } else { Player_SetUpperActionFunc(this, func_808351D4); @@ -2693,7 +3152,8 @@ s32 func_8083442C(Player* this, PlayState* play) { if ((magicArrowType >= 0) && (magicArrowType <= 2)) { if (GameInteractor_Should(VB_PLAYER_ARROW_MAGIC_CONSUMPTION, true, this, magicArrowType, &arrowType)) { - if (!Magic_RequestChange(play, sMagicArrowCosts[magicArrowType], MAGIC_CONSUME_NOW)) { + if (!Magic_RequestChangeForPlayer(play, this, sMagicArrowCosts[magicArrowType], + MAGIC_CONSUME_NOW)) { arrowType = ARROW_NORMAL; } } @@ -3413,6 +3873,20 @@ void func_80835EA4(PlayState* play, s32 arg1) { Camera_SetCameraData(Play_GetCamera(play, 0), 4, NULL, NULL, arg1, 0, 0); } +static void Player_SetTurnAroundCameraForPlayer(PlayState* play, Player* this, s32 arg1) { + Camera* playerCam = Player_GetCameraForPlayerOrMain(play, this); + + if (playerCam == NULL) { + return; + } + + if (func_800C0CB8(play)) { + Camera_ChangeSetting(playerCam, CAM_SET_TURN_AROUND); + } + + Camera_SetCameraData(playerCam, 4, NULL, NULL, arg1, 0, 0); +} + void Player_DestroyHookshot(Player* this) { if (Player_HoldsHookshot(this)) { Actor* heldActor = this->heldActor; @@ -3453,7 +3927,7 @@ void Player_UseItem(PlayState* play, Player* this, s32 item) { Sfx_PlaySfxCentered(NA_SE_SY_ERROR); } else if (itemAction == PLAYER_IA_LENS_OF_TRUTH) { // Handle Lens of Truth - if (Magic_RequestChange(play, 0, MAGIC_CONSUME_LENS)) { + if (Magic_RequestChangeForPlayer(play, this, 0, MAGIC_CONSUME_LENS)) { if (play->actorCtx.lensActive) { Actor_DisableLens(play); } else { @@ -3473,8 +3947,10 @@ void Player_UseItem(PlayState* play, Player* this, s32 item) { } else if ((temp = Player_ActionToMagicSpell(this, itemAction)) >= 0) { // Handle magic spells if (((itemAction == PLAYER_IA_FARORES_WIND) && (gSaveContext.respawn[RESPAWN_MODE_TOP].data > 0)) || - ((gSaveContext.magicCapacity != 0) && (gSaveContext.magicState == MAGIC_STATE_IDLE) && - (gSaveContext.magic >= sMagicSpellCosts[temp]))) { + ((Magic_GetLevelForPort(this->isSecondPlayer ? this->controllerPort : 1) != 0) && + (Magic_GetStateForPlayer(this) == MAGIC_STATE_IDLE) && + (Magic_GetAmountForPort(this->isSecondPlayer ? this->controllerPort : 1) >= + sMagicSpellCosts[temp]))) { this->itemAction = itemAction; this->unk_6AD = 4; } else { @@ -3674,7 +4150,8 @@ void Player_UpdateShapeYaw(Player* this, PlayState* play) { if (!(this->stateFlags2 & (PLAYER_STATE2_DISABLE_ROTATION_Z_TARGET | PLAYER_STATE2_DISABLE_ROTATION_ALWAYS))) { if ((this->focusActor != NULL) && - ((play->actorCtx.targetCtx.unk_4B != 0) || (this->actor.category != ACTORCAT_PLAYER))) { + ((play->actorCtx.targetCtx.unk_4B != 0) || (this->actor.category != ACTORCAT_PLAYER) || + this->isSecondPlayer)) { Math_ScaledStepToS(&this->actor.shape.rot.y, Math_Vec3f_Yaw(&this->actor.world.pos, &this->focusActor->focus.pos), 4000); } else if ((this->stateFlags1 & PLAYER_STATE1_PARALLEL) && @@ -3833,9 +4310,14 @@ void Player_UpdateZTargeting(Player* this, PlayState* play) { CHECK_BTN_ALL(sControlInput->press.button, BTN_Z))) { if (this->actor.category == ACTORCAT_PLAYER) { - // The next lock-on actor defaults to the actor Navi is hovering over. - // This may change to the arrow hover actor below. - nextLockOnActor = play->actorCtx.targetCtx.arrowPointedActor; + if (this->isSecondPlayer) { + // Compute lock-on candidates from this local player's own position and facing. + func_80032AF0(play, &play->actorCtx, &nextLockOnActor, this); + } else { + // The next lock-on actor defaults to the actor Navi is hovering over. + // This may change to the arrow hover actor below. + nextLockOnActor = play->actorCtx.targetCtx.arrowPointedActor; + } } else { // Dark Link will always lock onto the player. nextLockOnActor = &GET_PLAYER(play)->actor; @@ -3852,7 +4334,8 @@ void Player_UpdateZTargeting(Player* this, PlayState* play) { // Navi hovers over the current lock-on actor, so `nextLockOnActor` and `focusActor` // will be the same if already locked on. // In this case, `nextLockOnActor` will be the arrow hover actor instead. - if ((nextLockOnActor == this->focusActor) && (this->actor.category == ACTORCAT_PLAYER)) { + if (!this->isSecondPlayer && (nextLockOnActor == this->focusActor) && + (this->actor.category == ACTORCAT_PLAYER)) { nextLockOnActor = play->actorCtx.targetCtx.unk_94; } @@ -3875,7 +4358,10 @@ void Player_UpdateZTargeting(Player* this, PlayState* play) { this->stateFlags1 &= ~PLAYER_STATE1_LOCK_ON_FORCED_TO_RELEASE; } else { // Lock-on was not started above. Set Parallel Mode. - if (!(this->stateFlags1 & (PLAYER_STATE1_PARALLEL | PLAYER_STATE1_LOCK_ON_FORCED_TO_RELEASE))) { + if ((this->focusActor != NULL) && !usingHoldTargeting) { + Player_ReleaseLockOn(this); + } else if (!(this->stateFlags1 & + (PLAYER_STATE1_PARALLEL | PLAYER_STATE1_LOCK_ON_FORCED_TO_RELEASE))) { Player_SetParallel(this); } } @@ -4034,7 +4520,7 @@ s32 Player_GetMovementSpeedAndYaw(Player* this, f32* outSpeedTarget, s16* outYaw *outYawTarget = this->actor.shape.rot.y; if (this->focusActor != NULL) { - if ((play->actorCtx.targetCtx.unk_4B != 0) && + if (((play->actorCtx.targetCtx.unk_4B != 0) || this->isSecondPlayer) && !(this->stateFlags2 & PLAYER_STATE2_DISABLE_ROTATION_ALWAYS)) { *outYawTarget = Math_Vec3f_Yaw(&this->actor.world.pos, &this->focusActor->focus.pos); return false; @@ -4045,7 +4531,7 @@ s32 Player_GetMovementSpeedAndYaw(Player* this, f32* outSpeedTarget, s16* outYaw return false; } else { - *outYawTarget += Camera_GetInputDirYaw(GET_ACTIVE_CAM(play)); + *outYawTarget += Player_GetInputDirYawForPlayer(play, this); return true; } } @@ -4510,7 +4996,7 @@ s32 func_80837B18_modified(PlayState* play, Player* this, s32 damage, u8 modifie modifiedDamage *= (1 << CVarGetInteger(CVAR_ENHANCEMENT("DamageMult"), 0)); } - return Health_ChangeBy(play, modifiedDamage); + return Health_ChangeBy(play, modifiedDamage, this); } s32 func_80837B18(PlayState* play, Player* this, s32 damage) { @@ -4735,8 +5221,8 @@ s32 func_808382DC(Player* this, PlayState* play) { if (this->unk_A86 != 0) { if (!Player_InBlockingCsMode(play, this)) { - Player_InflictDamageModified(play, -16 * (1 << CVarGetInteger(CVAR_ENHANCEMENT("VoidDamageMult"), 0)), - false); + Player_InflictDamageModified(play, this, + -16 * (1 << CVarGetInteger(CVAR_ENHANCEMENT("VoidDamageMult"), 0)), false); this->unk_A86 = 0; } } else { @@ -5118,15 +5604,20 @@ s32 Player_HandleExitsAndVoids(PlayState* play, Player* this, CollisionPoly* pol s32 sp34; f32 speedXZ; s32 yaw; + s32 secondaryCanLoadAreas = + this->isSecondPlayer && (Player_GetLocalMultiplayerPlayerCount() > 1) && + !CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0) && CVarGetInteger(LOCAL_MP_SECONDARY_AREA_LOAD_CVAR, 0); + s32 triggerExit; + s32 triggerVoidSurface; - if (this->actor.category == ACTORCAT_PLAYER) { + if ((this->actor.category == ACTORCAT_PLAYER) && (!this->isSecondPlayer || secondaryCanLoadAreas)) { exitIndex = 0; + triggerExit = (poly != NULL) && (exitIndex = SurfaceType_GetSceneExitIndex(&play->colCtx, poly, bgId), exitIndex != 0); + triggerVoidSurface = !this->isSecondPlayer && func_8083816C(sFloorType) && (this->floorProperty == 12); if (!(this->stateFlags1 & PLAYER_STATE1_DEAD) && (play->transitionTrigger == TRANS_TRIGGER_OFF) && (this->csAction == 0) && !(this->stateFlags1 & PLAYER_STATE1_LOADING) && - (((poly != NULL) && - (exitIndex = SurfaceType_GetSceneExitIndex(&play->colCtx, poly, bgId), exitIndex != 0)) || - (func_8083816C(sFloorType) && (this->floorProperty == 12)))) { + (triggerExit || triggerVoidSurface)) { sp34 = this->unk_A84 - (s32)this->actor.world.pos.y; @@ -5277,10 +5768,16 @@ void Player_GetRelativePosition(Player* this, Vec3f* base, Vec3f* offset, Vec3f* Actor* Player_SpawnFairy(PlayState* play, Player* this, Vec3f* arg2, Vec3f* arg3, s32 type) { Vec3f pos; + Actor* fairy; Player_GetRelativePosition(this, arg2, arg3, &pos); + fairy = Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ELF, pos.x, pos.y, pos.z, 0, 0, 0, type); + + if ((fairy != NULL) && (type == FAIRY_NAVI)) { + fairy->parent = &this->actor; + } - return Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ELF, pos.x, pos.y, pos.z, 0, 0, 0, type); + return fairy; } f32 func_808396F4(PlayState* play, Player* this, Vec3f* arg2, Vec3f* arg3, CollisionPoly** arg4, s32* arg5) { @@ -5556,6 +6053,384 @@ int func_8083A0D4(Player* this) { return (this->interactRangeActor != NULL) && (this->heldActor == NULL); } +static Player* Player_GetCarrier(Player* this) { + Actor* parent = this->actor.parent; + + if ((parent != NULL) && (parent->id == ACTOR_PLAYER) && (((Player*)parent)->heldActor == &this->actor)) { + return (Player*)parent; + } + + return NULL; +} + +static s32 Player_IsElasticRopeModeEnabled(void) { + return !CVarGetInteger(LOCAL_MP_DISABLED_CVAR, 0) && + (Player_GetLocalMultiplayerPlayerCount() > 1) && + CVarGetInteger(LOCAL_MP_ELASTIC_ROPE_MODE_CVAR, 0); +} + +static s32 Player_CanElasticRopeAffect(Player* this) { + if ((this == NULL) || (this->actor.id != ACTOR_PLAYER) || (this->actor.update == NULL)) { + return false; + } + + if ((Player_GetCarrier(this) != NULL) || (this->heldActor != NULL) || (this->actor.parent != NULL)) { + return false; + } + + if (this->stateFlags1 & + (PLAYER_STATE1_DEAD | PLAYER_STATE1_LOADING | PLAYER_STATE1_ON_HORSE | PLAYER_STATE1_IN_CUTSCENE | + PLAYER_STATE1_TALKING | PLAYER_STATE1_CARRYING_ACTOR | PLAYER_STATE1_FIRST_PERSON)) { + return false; + } + + if (this->stateFlags2 & + (PLAYER_STATE2_PAUSE_MOST_UPDATING | PLAYER_STATE2_UNDERWATER | PLAYER_STATE2_GRABBED_BY_ENEMY | + PLAYER_STATE2_FROZEN)) { + return false; + } + + return true; +} + +static s32 Player_CanElasticRopeRender(Player* this) { + if ((this == NULL) || (this->actor.id != ACTOR_PLAYER) || (this->actor.update == NULL)) { + return false; + } + + if ((Player_GetCarrier(this) != NULL) || (this->heldActor != NULL) || (this->actor.parent != NULL)) { + return false; + } + + if (this->stateFlags1 & (PLAYER_STATE1_DEAD | PLAYER_STATE1_LOADING)) { + return false; + } + + if (this->stateFlags2 & PLAYER_STATE2_PAUSE_MOST_UPDATING) { + return false; + } + + return true; +} + +static f32 Player_GetElasticRopeStrength(void) { + f32 ropeStrength = CVarGetFloat(LOCAL_MP_ELASTIC_ROPE_STRENGTH_CVAR, 1.0f); + + return CLAMP(ropeStrength, 0.25f, 3.0f); +} + +static s32 Player_GetElasticRopeTarget(Player* this, PlayState* play, Vec3f* outTarget, Player** outOtherPlayer, + s32* outParticipantCount, s32 canAffectPredicate(Player*)) { + Actor* actor; + Player* otherPlayer = NULL; + Vec3f center = { 0.0f, 0.0f, 0.0f }; + s32 participantCount = 0; + + if ((play == NULL) || (this == NULL) || !canAffectPredicate(this)) { + return false; + } + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + if ((actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* player = (Player*)actor; + + if (canAffectPredicate(player)) { + participantCount++; + center.x += actor->world.pos.x; + center.y += actor->world.pos.y; + center.z += actor->world.pos.z; + + if (player != this) { + otherPlayer = player; + } + } + } + + actor = actor->next; + } + + if (outOtherPlayer != NULL) { + *outOtherPlayer = otherPlayer; + } + + if (outParticipantCount != NULL) { + *outParticipantCount = participantCount; + } + + if ((participantCount <= 1) || (outTarget == NULL)) { + return false; + } + + if ((participantCount == 2) && (otherPlayer != NULL)) { + *outTarget = otherPlayer->actor.world.pos; + } else { + center.x /= participantCount; + center.y /= participantCount; + center.z /= participantCount; + *outTarget = center; + } + + return true; +} + +static void Player_DrawElasticRopeChainSegment(PlayState* play, Vec3f* fromPos, Vec3f* toPos, f32 ropeStrength) { + Vec3f ropeVec; + f32 xzDist; + f32 ropeDist; + f32 ropeThickness; + + if ((play == NULL) || (fromPos == NULL) || (toPos == NULL)) { + return; + } + + ropeVec.x = toPos->x - fromPos->x; + ropeVec.y = toPos->y - fromPos->y; + ropeVec.z = toPos->z - fromPos->z; + + xzDist = sqrtf(SQ(ropeVec.x) + SQ(ropeVec.z)); + ropeDist = sqrtf(SQ(ropeVec.y) + SQ(xzDist)); + if (ropeDist <= 1.0f) { + return; + } + + ropeThickness = CLAMP(0.010f + (ropeStrength * 0.0025f), 0.008f, 0.018f); + + OPEN_DISPS(play->state.gfxCtx); + + Matrix_Push(); + Matrix_Translate(fromPos->x, fromPos->y, fromPos->z, MTXMODE_NEW); + Matrix_RotateY(Math_FAtan2F(ropeVec.x, ropeVec.z), MTXMODE_APPLY); + Matrix_RotateX(Math_FAtan2F(-ropeVec.y, xzDist), MTXMODE_APPLY); + Matrix_Scale(ropeThickness, ropeThickness, ropeDist * 0.01f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_NOPUSH | G_MTX_LOAD | G_MTX_MODELVIEW); + gSPDisplayList(POLY_OPA_DISP++, gLinkAdultHookshotChainDL); + Matrix_Pop(); + + CLOSE_DISPS(play->state.gfxCtx); +} + +static void Player_DrawElasticRope(Player* this, PlayState* play) { + const f32 ropeAnchorYOffset = 22.0f; + Player* otherPlayer = NULL; + Vec3f targetPos; + Vec3f ropeStart; + Vec3f ropeEnd; + f32 ropeStrength; + s32 participantCount = 0; + + if ((play == NULL) || !Player_IsElasticRopeModeEnabled() || !Player_CanElasticRopeRender(this)) { + return; + } + + if (!Player_GetElasticRopeTarget(this, play, &targetPos, &otherPlayer, &participantCount, + Player_CanElasticRopeRender)) { + return; + } + + if ((participantCount == 2) && (otherPlayer != NULL) && (this->controllerPort > otherPlayer->controllerPort)) { + return; + } + + ropeStrength = Player_GetElasticRopeStrength(); + ropeStart = this->actor.world.pos; + ropeEnd = targetPos; + ropeStart.y += ropeAnchorYOffset; + ropeEnd.y += ropeAnchorYOffset; + + Gfx_SetupDL_25Opa(play->state.gfxCtx); + Player_DrawElasticRopeChainSegment(play, &ropeStart, &ropeEnd, ropeStrength); +} + +static void Player_ApplyElasticRopePull(Player* this, PlayState* play) { + Vec3f targetPos; + f32 dx; + f32 dy; + f32 dz; + f32 distSq; + f32 dist; + const f32 baseRopeSlackDist = 160.0f; + const f32 baseRopeHardLimit = 360.0f; + const f32 pullLinearScale = 0.018f; + const f32 pullQuadraticScale = 0.00020f; + const f32 maxBasePull = 6.0f; + f32 ropeStrength; + f32 distanceAllowanceScale; + f32 ropeSlackDist; + f32 ropeHardLimit; + + if ((play == NULL) || !Player_IsElasticRopeModeEnabled() || !Player_CanElasticRopeAffect(this)) { + return; + } + + if (!Player_GetElasticRopeTarget(this, play, &targetPos, NULL, NULL, Player_CanElasticRopeAffect)) { + return; + } + + ropeStrength = Player_GetElasticRopeStrength(); + distanceAllowanceScale = 1.0f / ropeStrength; + ropeSlackDist = baseRopeSlackDist * distanceAllowanceScale; + ropeHardLimit = baseRopeHardLimit * distanceAllowanceScale; + + dx = targetPos.x - this->actor.world.pos.x; + dy = targetPos.y - this->actor.world.pos.y; + dz = targetPos.z - this->actor.world.pos.z; + distSq = SQ(dx) + SQ(dy) + SQ(dz); + + if (distSq < 1.0f) { + return; + } + + dist = sqrtf(distSq); + + if (dist > ropeSlackDist) { + f32 stretch = dist - ropeSlackDist; + f32 pullStrength = (stretch * pullLinearScale) + (SQ(stretch) * pullQuadraticScale); + f32 invDist = 1.0f / dist; + + pullStrength *= ropeStrength; + pullStrength = CLAMP(pullStrength, 0.0f, maxBasePull * ropeStrength); + this->actor.velocity.x += dx * invDist * pullStrength; + this->actor.velocity.y += dy * invDist * pullStrength; + this->actor.velocity.z += dz * invDist * pullStrength; + } + + if (dist > ropeHardLimit) { + f32 invDist = 1.0f / dist; + f32 overflow = dist - ropeHardLimit; + f32 awayX = -dx * invDist; + f32 awayY = -dy * invDist; + f32 awayZ = -dz * invDist; + f32 outwardVelocity = + (this->actor.velocity.x * awayX) + (this->actor.velocity.y * awayY) + (this->actor.velocity.z * awayZ); + f32 stopFactor = 1.0f + CLAMP(overflow * (0.02f * ropeStrength), 0.0f, 2.5f); + f32 inwardSnap = CLAMP(overflow * (0.05f * ropeStrength), 0.0f, 8.0f * ropeStrength); + + if (outwardVelocity > 0.0f) { + this->actor.velocity.x -= awayX * outwardVelocity * stopFactor; + this->actor.velocity.y -= awayY * outwardVelocity * stopFactor; + this->actor.velocity.z -= awayZ * outwardVelocity * stopFactor; + } + + this->actor.velocity.x += dx * invDist * inwardSnap; + this->actor.velocity.y += dy * invDist * inwardSnap; + this->actor.velocity.z += dz * invDist * inwardSnap; + } +} + +static s32 Player_CanPickupPlayer(Player* this, Player* target, s32 requireStillInput) { + s16 yawDiff; + + if (!Player_IsCarryThrowSystemEnabled()) { + return false; + } + + if ((target == NULL) || (target == this) || (target->actor.update == NULL)) { + return false; + } + + if ((Player_GetCarrier(this) != NULL) || (Player_GetCarrier(target) != NULL) || (this->actor.parent != NULL) || + (target->actor.parent != NULL) || (this->heldActor != NULL) || (target->heldActor != NULL)) { + return false; + } + + if ((this->stateFlags1 & + (PLAYER_STATE1_DEAD | PLAYER_STATE1_CHARGING_SPIN_ATTACK | PLAYER_STATE1_HANGING_OFF_LEDGE | + PLAYER_STATE1_CLIMBING_LEDGE | PLAYER_STATE1_JUMPING | PLAYER_STATE1_FREEFALL | + PLAYER_STATE1_FIRST_PERSON | PLAYER_STATE1_CLIMBING_LADDER | PLAYER_STATE1_TALKING | + PLAYER_STATE1_IN_CUTSCENE | PLAYER_STATE1_CARRYING_ACTOR)) || + (target->stateFlags1 & + (PLAYER_STATE1_DEAD | PLAYER_STATE1_CHARGING_SPIN_ATTACK | PLAYER_STATE1_HANGING_OFF_LEDGE | + PLAYER_STATE1_CLIMBING_LEDGE | PLAYER_STATE1_JUMPING | PLAYER_STATE1_FREEFALL | + PLAYER_STATE1_FIRST_PERSON | PLAYER_STATE1_CLIMBING_LADDER | PLAYER_STATE1_TALKING | + PLAYER_STATE1_IN_CUTSCENE | PLAYER_STATE1_CARRYING_ACTOR))) { + return false; + } + + if ((this->stateFlags2 & PLAYER_STATE2_UNDERWATER) || (target->stateFlags2 & PLAYER_STATE2_UNDERWATER) || + (Player_GetExplosiveHeld(this) >= 0) || (Player_GetExplosiveHeld(target) >= 0)) { + return false; + } + + if (!(this->actor.bgCheckFlags & 1) || !(target->actor.bgCheckFlags & 1) || + (fabsf(this->linearVelocity) >= 0.1f) || (fabsf(target->linearVelocity) >= 0.1f) || + (SQ(this->actor.world.pos.x - target->actor.world.pos.x) + + SQ(this->actor.world.pos.z - target->actor.world.pos.z) >= SQ(50.0f)) || + (fabsf(this->actor.world.pos.y - target->actor.world.pos.y) >= 10.0f)) { + return false; + } + + yawDiff = Math_Vec3f_Yaw(&this->actor.world.pos, &target->actor.world.pos) - this->actor.shape.rot.y; + if (ABS(yawDiff) >= 0x3000) { + return false; + } + + if (requireStillInput && + ((sControlInput == NULL) || (ABS(sControlInput->rel.stick_x) >= 10) || (ABS(sControlInput->rel.stick_y) >= 10))) { + return false; + } + + return true; +} + +static void Player_OfferCarryToOtherPlayers(Player* this, PlayState* play) { + Actor* actor; + Player* closestPlayer = NULL; + s16 closestYawDiff = 0; + f32 closestDistSq = SQ(50.0f); + + if (!Player_IsCarryThrowSystemEnabled()) { + return; + } + + if (Player_InCsMode(play) || (play->pauseCtx.state != 0) || (play->transitionTrigger != TRANS_TRIGGER_OFF) || + (Player_GetCarrier(this) != NULL) || (this->heldActor != NULL) || + (this->stateFlags1 & PLAYER_STATE1_CARRYING_ACTOR) || (this->stateFlags2 & PLAYER_STATE2_UNDERWATER)) { + return; + } + + actor = play->actorCtx.actorLists[ACTORCAT_PLAYER].head; + while (actor != NULL) { + if ((actor != &this->actor) && (actor->id == ACTOR_PLAYER) && (actor->update != NULL)) { + Player* other = (Player*)actor; + f32 distSq; + + if (Player_CanPickupPlayer(other, this, false)) { + f32 dx = other->actor.world.pos.x - this->actor.world.pos.x; + f32 dz = other->actor.world.pos.z - this->actor.world.pos.z; + + distSq = (dx * dx) + (dz * dz); + if (distSq < closestDistSq) { + closestPlayer = other; + closestYawDiff = Math_Vec3f_Yaw(&other->actor.world.pos, &this->actor.world.pos) - + other->actor.shape.rot.y; + closestDistSq = distSq; + } + } + } + + actor = actor->next; + } + + if (closestPlayer != NULL) { + Actor* currentInteractRangeActor = closestPlayer->interactRangeActor; + f32 currentDistSq = FLT_MAX; + + if ((currentInteractRangeActor != NULL) && (currentInteractRangeActor->update != NULL)) { + f32 dx = closestPlayer->actor.world.pos.x - currentInteractRangeActor->world.pos.x; + f32 dz = closestPlayer->actor.world.pos.z - currentInteractRangeActor->world.pos.z; + + currentDistSq = (dx * dx) + (dz * dz); + } + + if ((currentInteractRangeActor == NULL) || (currentDistSq > closestDistSq)) { + closestPlayer->getItemId = GI_NONE; + closestPlayer->interactRangeActor = &this->actor; + closestPlayer->getItemDirection = ABS(closestYawDiff); + } + } +} + void func_8083A0F4(PlayState* play, Player* this) { if (func_8083A0D4(this)) { Actor* interactRangeActor = this->interactRangeActor; @@ -5606,7 +6481,6 @@ void func_8083A0F4(PlayState* play, Player* this) { void Player_SetupTalk(PlayState* play, Player* this) { Player_SetupActionPreserveAnimMovement(play, this, Player_Action_Talk, 0); - this->stateFlags1 |= PLAYER_STATE1_TALKING | PLAYER_STATE1_IN_CUTSCENE; if (this->actor.textId != 0) { @@ -5851,6 +6725,7 @@ void func_8083AA10(Player* this, PlayState* play) { s32 func_8083AD4C(PlayState* play, Player* this) { s32 camMode; + Camera* playerCam; if (this->unk_6AD == 2) { if (func_8002DD6C(this)) { @@ -5875,7 +6750,12 @@ s32 func_8083AD4C(PlayState* play, Player* this) { camMode = CAM_MODE_FIRSTPERSON; } - return Camera_ChangeMode(Play_GetCamera(play, 0), camMode); + playerCam = Player_GetCameraForPlayerOrMain(play, this); + if (playerCam == NULL) { + return false; + } + + return Camera_ChangeMode(playerCam, camMode); } /** @@ -5931,7 +6811,7 @@ void func_8083AF44(PlayState* play, Player* this, s32 magicSpell) { //! When `MAGIC_STATE_CONSUME_SETUP` is set in `Player_Action_808507F4`, magic will eventually be //! consumed to a stale target value. If that stale target value is higher than the current //! magic value, it will be consumed to zero. - Magic_RequestChange(play, sMagicSpellCosts[magicSpell], MAGIC_CONSUME_WAIT_PREVIEW); + Magic_RequestChangeForPlayer(play, this, sMagicSpellCosts[magicSpell], MAGIC_CONSUME_WAIT_PREVIEW); u8 isFastFarores = CVarGetInteger(CVAR_ENHANCEMENT("FastFarores"), 0) && this->itemAction == PLAYER_IA_FARORES_WIND; @@ -6041,8 +6921,13 @@ s32 Player_ActionHandler_13(Player* this, PlayState* play) { func_8083AE40(this, giEntry.objectId); } - this->stateFlags1 |= - PLAYER_STATE1_TALKING | PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE; + // Only set cutscene flags in single-player mode; in MP, skip to prevent UI/split-screen disable + if (Player_GetLocalMultiplayerPlayerCount() <= 1) { + this->stateFlags1 |= + PLAYER_STATE1_TALKING | PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE; + } else { + this->stateFlags1 |= PLAYER_STATE1_TALKING; + } if (sp2C >= 0) { sp2C = sp2C + 1; @@ -6071,11 +6956,11 @@ s32 Player_ActionHandler_13(Player* this, PlayState* play) { } else if (sp2C == EXCH_ITEM_LETTER_RUTO) { this->av1.actionVar1 = 1; this->actor.textId = 0x4005; - func_80835EA4(play, 1); + Player_SetTurnAroundCameraForPlayer(play, this, 1); } else { this->av1.actionVar1 = 2; this->actor.textId = 0xCF; - func_80835EA4(play, 4); + Player_SetTurnAroundCameraForPlayer(play, this, 4); } this->actor.flags |= ACTOR_FLAG_TALK; @@ -6098,15 +6983,15 @@ s32 Player_ActionHandler_13(Player* this, PlayState* play) { if (sp2C == 0xC) { Player_SetupActionPreserveItemAction(play, this, Player_Action_8084EED8, 0); Player_AnimPlayOnceAdjusted(play, this, &gPlayerAnim_link_bottle_bug_out); - func_80835EA4(play, 3); + Player_SetTurnAroundCameraForPlayer(play, this, 3); } else if ((sp2C > 0) && (sp2C < 4)) { Player_SetupActionPreserveItemAction(play, this, Player_Action_8084EFC0, 0); Player_AnimPlayOnceAdjusted(play, this, &gPlayerAnim_link_bottle_fish_out); - func_80835EA4(play, (sp2C == 1) ? 1 : 5); + Player_SetTurnAroundCameraForPlayer(play, this, (sp2C == 1) ? 1 : 5); } else { Player_SetupActionPreserveItemAction(play, this, Player_Action_8084EAC0, 0); Player_AnimChangeOnceMorphAdjusted(play, this, &gPlayerAnim_link_bottle_drink_demo_start); - func_80835EA4(play, 2); + Player_SetTurnAroundCameraForPlayer(play, this, 2); } } else { Player_SetupActionPreserveItemAction(play, this, Player_Action_8084E3C4, 0); @@ -6134,7 +7019,10 @@ s32 Player_ActionHandler_13(Player* this, PlayState* play) { return 0; } - this->stateFlags1 |= PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE; + // Only set cutscene flags in single-player mode; in MP, skip to prevent UI/split-screen disable + if (Player_GetLocalMultiplayerPlayerCount() <= 1) { + this->stateFlags1 |= PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE; + } } func_80832224(this); @@ -6227,7 +7115,9 @@ s32 Player_ActionHandler_Talk(Player* this, PlayState* play) { // text will be used. This is especially important to prevent unwanted behavior with regards to mask // trading. this->currentMask = sSavedCurrentMask; - Player_StartTalking(play, talkOfferActor); + if (GameInteractor_Should(VB_SKIP_TALKING, true)) { + Player_StartTalking(play, talkOfferActor); + } return true; } } @@ -7286,10 +8176,14 @@ void func_8083E4C4(PlayState* play, Player* this, GetItemEntry* giEntry) { Item_DropCollectible(play, &this->actor.world.pos, dropType | 0x8000); if ((dropType != 4) && (dropType != 8) && (dropType != 9) && (dropType != 0xA) && (dropType != 0) && (dropType != 1) && (dropType != 2) && (dropType != 0x14) && (dropType != 0x13)) { + Item_Give_SetPlayer(this); Item_Give(play, giEntry->itemId); + Item_Give_ClearPlayer(); } } else { + Item_Give_SetPlayer(this); Item_Give(play, giEntry->itemId); + Item_Give_ClearPlayer(); } Sfx_PlaySfxCentered((this->getItemId < 0 || this->getItemEntry.getItemId < 0) ? NA_SE_SY_GET_BOXITEM : NA_SE_SY_GET_ITEM); @@ -7458,6 +8352,11 @@ s32 Player_ActionHandler_2(Player* this, PlayState* play) { return 0; } + if ((interactedActor->id == ACTOR_PLAYER) && + !Player_CanPickupPlayer(this, (Player*)interactedActor, true)) { + return 0; + } + Player_SetupWaitForPutAway(play, this, func_8083A0F4); } @@ -7640,6 +8539,13 @@ s32 Player_TryEnteringCrawlspace(Player* this, PlayState* play, u32 interactWall } wallPoly = this->actor.wallPoly; + + // In local multiplayer, shared wall-touch state can be stale across players. + // Guard against null wall data before dereferencing crawlspace geometry. + if (wallPoly == NULL) { + return false; + } + CollisionPoly_GetVerticesByBgId(wallPoly, this->actor.wallBgId, &play->colCtx, wallVertices); // Determines min and max vertices for x & z (edges of the crawlspace hole) @@ -7780,7 +8686,9 @@ s32 Player_TryLeavingCrawlspace(Player* this, PlayState* play) { this->actor.shape.rot.y = this->actor.wallYaw + 0x8000; Player_AnimPlayOnce(play, this, &gPlayerAnim_link_child_tunnel_end); Player_StartAnimMovement(play, this, 0x9D); - OnePointCutscene_Init(play, 9601, 999, NULL, MAIN_CAM); + if (!this->isSecondPlayer) { + OnePointCutscene_Init(play, 9601, 999, NULL, MAIN_CAM); + } } else { // Leaving a crawlspace backwards this->actor.shape.rot.y = this->actor.wallYaw; @@ -7788,7 +8696,9 @@ s32 Player_TryLeavingCrawlspace(Player* this, PlayState* play) { Animation_GetLastFrame(&gPlayerAnim_link_child_tunnel_start), 0.0f, ANIMMODE_ONCE, 0.0f); Player_StartAnimMovement(play, this, 0x9D); - OnePointCutscene_Init(play, 9602, 999, NULL, MAIN_CAM); + if (!this->isSecondPlayer) { + OnePointCutscene_Init(play, 9602, 999, NULL, MAIN_CAM); + } } } @@ -8029,7 +8939,7 @@ s32 func_80840058(Player* this, f32* arg1, s16* arg2, PlayState* play) { func_8083DC54(this, play); if ((*arg1 != 0.0f) || (ABS(this->unk_87C) > 400)) { - s16 temp1 = *arg2 - (u16)Camera_GetInputDirYaw(GET_ACTIVE_CAM(play)); + s16 temp1 = *arg2 - (u16)Player_GetInputDirYawForPlayer(play, this); u16 temp2 = (ABS(temp1) - 0x2000); if ((temp2 < 0x4000) || (this->unk_87C != 0)) { @@ -8264,7 +9174,7 @@ void Player_ChooseNextIdleAnim(PlayState* play, Player* this) { s32 commonType; if ((this->focusActor != NULL) || - (!(heathIsCritical = HealthMeter_IsCritical()) && ((this->idleType = (this->idleType + 1) & 1) != 0))) { + (!(heathIsCritical = HealthMeter_IsCritical(this)) && ((this->idleType = (this->idleType + 1) & 1) != 0))) { this->stateFlags2 &= ~PLAYER_STATE2_IDLE_FIDGET; anim = Player_GetIdleAnim(this); } else { @@ -9294,7 +10204,7 @@ void Player_Action_80843188(Player* this, PlayState* play) { (CVarGetInteger(CVAR_SETTING("Controls.InvertShieldAimingYAxis"), 1) ? 1 : -1); sp50 = sControlInput->rel.stick_x * (CVarGetInteger(CVAR_ENHANCEMENT("MirroredWorld"), 0) ? 120 : -120) * (CVarGetInteger(CVAR_SETTING("Controls.InvertShieldAimingXAxis"), 0) ? -1 : 1); - sp4E = this->actor.shape.rot.y - Camera_GetInputDirYaw(GET_ACTIVE_CAM(play)); + sp4E = this->actor.shape.rot.y - Player_GetInputDirYawForPlayer(play, this); GameInteractor_ExecuteOnPlayerShieldControl(&sp50, &sp54); sp40 = Math_CosS(sp4E); @@ -9508,10 +10418,14 @@ void func_80843AE8(PlayState* play, Player* this) { LinkAnimation_Change(play, &this->skelAnime, &gPlayerAnim_link_derth_rebirth, 1.0f, 99.0f, Animation_GetLastFrame(&gPlayerAnim_link_derth_rebirth), ANIMMODE_ONCE, 0.0f); } - gSaveContext.healthAccumulator = MAX_HEALTH; + if (this->isSecondPlayer) { + Health_ChangeBy(play, MAX_HEALTH, this); + } else { + gSaveContext.healthAccumulator = MAX_HEALTH; + } this->av2.actionVar2 = -1; } - } else if (gSaveContext.healthAccumulator == 0) { + } else if (this->isSecondPlayer || (gSaveContext.healthAccumulator == 0)) { this->stateFlags1 &= ~PLAYER_STATE1_DEAD; if (this->stateFlags1 & PLAYER_STATE1_IN_WATER) { func_80838F18(play, this); @@ -9612,7 +10526,8 @@ s32 func_80843E64(PlayState* play, Player* this) { impactInfo = &D_80854600[impactIndex]; if (Player_InflictDamageModified( - play, impactInfo->damage * (1 << CVarGetInteger(CVAR_ENHANCEMENT("FallDamageMult"), 0)), false)) { + play, this, impactInfo->damage * (1 << CVarGetInteger(CVAR_ENHANCEMENT("FallDamageMult"), 0)), + false)) { return -1; } @@ -9653,6 +10568,30 @@ void func_8084409C(PlayState* play, Player* this, f32 speedXZ, f32 velocityY) { heldActor->speedXZ = speedXZ; heldActor->velocity.y = velocityY; func_80834644(play, this); + + if (Player_IsCarryThrowSystemEnabled() && (heldActor->id == ACTOR_PLAYER)) { + Player* thrownPlayer = (Player*)heldActor; + + func_80837B9C(thrownPlayer, play); + thrownPlayer->yaw = thrownPlayer->actor.shape.rot.y = thrownPlayer->actor.world.rot.y; + + { + f32 forwardMult = 1.25f; + f32 upMult = 0.8f; + f32 newSpeed = speedXZ * forwardMult; + + thrownPlayer->linearVelocity = newSpeed; + thrownPlayer->actor.speedXZ = newSpeed; + thrownPlayer->actor.velocity.x = Math_SinS(thrownPlayer->actor.world.rot.y) * newSpeed; + thrownPlayer->actor.velocity.z = Math_CosS(thrownPlayer->actor.world.rot.y) * newSpeed; + thrownPlayer->actor.velocity.y = velocityY * upMult; + thrownPlayer->actor.gravity = -1.4f; + } + + thrownPlayer->actor.bgCheckFlags &= ~1; + thrownPlayer->hoverBootsTimer = 0; + } + Player_PlaySfx(this, NA_SE_PL_THROW); Player_PlayVoiceSfx(this, NA_SE_VO_LI_SWORD_N); } @@ -10427,12 +11366,46 @@ void Player_Action_80846050(Player* this, PlayState* play) { if (LinkAnimation_OnFrame(&this->skelAnime, 4.0f)) { Actor* interactRangeActor = this->interactRangeActor; + if ((interactRangeActor != NULL) && (interactRangeActor->id == ACTOR_PLAYER)) { + Player* targetPlayer = (Player*)interactRangeActor; + + // This runs during the pickup windup, where this player already has CARRYING_ACTOR set. + // Avoid Player_CanPickupPlayer here because that flag would reject valid pickups. + // Instead, only reject if the target is no longer a valid carry target at grab frame. + if ((targetPlayer->actor.update == NULL) || (targetPlayer == this) || + (Player_GetCarrier(targetPlayer) != NULL) || (targetPlayer->actor.parent != NULL) || + (targetPlayer->heldActor != NULL) || + (targetPlayer->stateFlags1 & (PLAYER_STATE1_DEAD | PLAYER_STATE1_TALKING | PLAYER_STATE1_IN_CUTSCENE)) || + (targetPlayer->stateFlags2 & PLAYER_STATE2_UNDERWATER)) { + this->stateFlags1 &= ~PLAYER_STATE1_CARRYING_ACTOR; + this->interactRangeActor = NULL; + this->getItemId = GI_NONE; + func_80839F90(this, play); + return; + } + } + if (!func_80835644(play, this, interactRangeActor)) { this->heldActor = interactRangeActor; this->actor.child = interactRangeActor; interactRangeActor->parent = &this->actor; interactRangeActor->bgCheckFlags &= 0xFF00; this->unk_3BC.y = interactRangeActor->shape.rot.y - this->actor.shape.rot.y; + + if (Player_IsCarryThrowSystemEnabled() && (interactRangeActor->id == ACTOR_PLAYER)) { + Player* carriedPlayer = (Player*)interactRangeActor; + + Player_SetupAction(play, carriedPlayer, Player_Action_CarriedByPlayer, 1); + Player_AnimPlayLoop(play, carriedPlayer, Player_GetIdleAnim(carriedPlayer)); + carriedPlayer->interactRangeActor = NULL; + carriedPlayer->getItemId = GI_NONE; + carriedPlayer->linearVelocity = 0.0f; + carriedPlayer->actor.speedXZ = 0.0f; + carriedPlayer->actor.velocity.x = 0.0f; + carriedPlayer->actor.velocity.y = 0.0f; + carriedPlayer->actor.velocity.z = 0.0f; + carriedPlayer->actor.gravity = 0.0f; + } } return; } @@ -10531,6 +11504,40 @@ void Player_Action_80846358(Player* this, PlayState* play) { } } +void Player_Action_CarriedByPlayer(Player* this, PlayState* play) { + Player* carrier = Player_GetCarrier(this); + + if (carrier == NULL) { + if ((this->actor.speedXZ > 0.0f) || (this->actor.velocity.y > 0.0f)) { + func_80837B9C(this, play); + this->linearVelocity = this->actor.speedXZ; + this->yaw = this->actor.shape.rot.y = this->actor.world.rot.y; + this->actor.bgCheckFlags &= ~1; + return; + } + + this->actor.minVelocityY = -20.0f; + this->actor.gravity = REG(68) / 100.0f; + func_80839F90(this, play); + return; + } + + LinkAnimation_Update(play, &this->skelAnime); + this->linearVelocity = 0.0f; + this->actor.speedXZ = 0.0f; + this->actor.velocity.x = 0.0f; + this->actor.velocity.y = 0.0f; + this->actor.velocity.z = 0.0f; + this->actor.gravity = 0.0f; + this->interactRangeActor = NULL; + this->getItemId = GI_NONE; + this->actor.bgCheckFlags &= 0xFF00; + + if (!Player_IsCarryThrowSystemEnabled() || CHECK_BTN_ALL(sControlInput->press.button, BTN_A)) { + func_80834644(play, carrier); + } +} + void Player_Action_80846408(Player* this, PlayState* play) { if (LinkAnimation_Update(play, &this->skelAnime)) { Player_AnimPlayLoop(play, this, &gPlayerAnim_link_normal_nocarry_free_wait); @@ -10728,9 +11735,14 @@ void Player_StartMode_WarpSong(PlayState* play, Player* this) { Actor* Player_SpawnMagicSpell(PlayState* play, Player* this, s32 spell) { static s16 sMagicSpellActorIds[] = { ACTOR_MAGIC_WIND, ACTOR_MAGIC_DARK, ACTOR_MAGIC_FIRE }; + Actor* spellActor = Actor_Spawn(&play->actorCtx, play, sMagicSpellActorIds[spell], this->actor.world.pos.x, + this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); - return Actor_Spawn(&play->actorCtx, play, sMagicSpellActorIds[spell], this->actor.world.pos.x, - this->actor.world.pos.y, this->actor.world.pos.z, 0, 0, 0, 0); + if (spellActor != NULL) { + spellActor->home.rot.z = this->isSecondPlayer ? this->controllerPort : 1; + } + + return spellActor; } void Player_StartMode_FaroresWind(PlayState* play, Player* this) { @@ -10826,8 +11838,21 @@ void Player_Init(Actor* thisx, PlayState* play2) { this->ageProperties = &sAgeProperties[gSaveContext.linkAge]; this->itemAction = this->heldItemAction = -1; this->heldItemId = ITEM_NONE; + this->controllerPort = 1; + this->inputEnabled = true; + this->isSecondPlayer = false; + this->linkedPlayer = NULL; + + if ((thisx->params & 0x8000) != 0) { + this->isSecondPlayer = true; + this->controllerPort = 2; + thisx->params &= ~0x8000; + } Player_UseItem(play, this, ITEM_NONE); + this->bootsAreActive = false; + this->bootsToggleRequest = false; + this->equippedBoots = BOOTS_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS)); Player_SetEquipmentData(play, this); this->prevBoots = this->currentBoots; // keep masks thru loading zones @@ -10918,7 +11943,7 @@ void Player_Init(Actor* thisx, PlayState* play2) { } if (gSaveContext.nayrusLoveTimer != 0) { - gSaveContext.magicState = MAGIC_STATE_METER_FLASH_1; + Magic_SetStateForPlayer(this, MAGIC_STATE_METER_FLASH_1); Player_SpawnMagicSpell(play, this, 1); this->stateFlags3 &= ~PLAYER_STATE3_RESTORE_NAYRUS_LOVE; } @@ -10996,7 +12021,8 @@ static f32 D_80854784[] = { 120.0f, 240.0f, 360.0f }; * - Navi C-up icon for hints */ void Player_UpdateInterface(PlayState* play, Player* this) { - if ((Message_GetState(&play->msgCtx) == TEXT_STATE_NONE) && (this->actor.category == ACTORCAT_PLAYER)) { + if ((Message_GetState(&play->msgCtx) == TEXT_STATE_NONE) && (this->actor.category == ACTORCAT_PLAYER) && + !this->isSecondPlayer) { Actor* heldActor = this->heldActor; Actor* interactRangeActor = this->interactRangeActor; s32 sp24; @@ -11268,7 +12294,7 @@ void Player_ProcessSceneCollision(PlayState* play, Player* this) { } } - if (this->actor.category == ACTORCAT_PLAYER) { + if ((this->actor.category == ACTORCAT_PLAYER) && !this->isSecondPlayer) { Audio_SetCodeReverb(SurfaceType_GetEcho(&play->colCtx, floorPoly, this->actor.floorBgId)); if (this->actor.floorBgId == BGCHECK_SCENE) { @@ -11523,16 +12549,22 @@ void Player_UpdateCamAndSeqModes(PlayState* play, Player* this) { s32 pad; Actor* focusActor; s32 camMode; + Camera* playerCam; if (this->actor.category == ACTORCAT_PLAYER) { + playerCam = Player_GetCameraForPlayer(play, this); + if (playerCam == NULL) { + return; + } + seqMode = SEQ_MODE_DEFAULT; if (this->csAction != 0) { - Camera_ChangeMode(Play_GetCamera(play, 0), CAM_MODE_NORMAL); + Camera_ChangeMode(playerCam, CAM_MODE_NORMAL); } else if (!(this->stateFlags1 & PLAYER_STATE1_FIRST_PERSON)) { if ((this->actor.parent != NULL) && (this->stateFlags3 & PLAYER_STATE3_FLYING_WITH_HOOKSHOT)) { camMode = CAM_MODE_HOOKSHOT; - Camera_SetParam(Play_GetCamera(play, 0), 8, this->actor.parent); + Camera_SetParam(playerCam, 8, this->actor.parent); } else if (Player_Action_8084377C == this->actionFunc) { camMode = CAM_MODE_STILL; } else if (this->stateFlags2 & PLAYER_STATE2_GRABBING_DYNAPOLY) { @@ -11549,7 +12581,7 @@ void Player_UpdateCamAndSeqModes(PlayState* play, Player* this) { } else { camMode = CAM_MODE_BATTLE; } - Camera_SetParam(Play_GetCamera(play, 0), 8, focusActor); + Camera_SetParam(playerCam, 8, focusActor); } else if (this->stateFlags1 & PLAYER_STATE1_CHARGING_SPIN_ATTACK) { camMode = CAM_MODE_CHARGE; } else if (this->stateFlags1 & PLAYER_STATE1_BOOMERANG_THROWN) { @@ -11557,7 +12589,7 @@ void Player_UpdateCamAndSeqModes(PlayState* play, Player* this) { if (CVarGetInteger(CVAR_ENHANCEMENT("BoomerangFirstPerson"), 0)) { // Avoid camera jumps by switching to normal cam to exit the first person camera, // before following the boomerang - if (Play_GetCamera(play, 0)->mode == CAM_MODE_FIRSTPERSON) { + if (playerCam->mode == CAM_MODE_FIRSTPERSON) { camMode = CAM_MODE_NORMAL; } else { camMode = CAM_MODE_FOLLOWBOOMERANG; @@ -11566,7 +12598,7 @@ void Player_UpdateCamAndSeqModes(PlayState* play, Player* this) { } else { camMode = CAM_MODE_FOLLOWBOOMERANG; } - Camera_SetParam(Play_GetCamera(play, 0), 8, this->boomerangActor); + Camera_SetParam(playerCam, 8, this->boomerangActor); } else if (this->stateFlags1 & (PLAYER_STATE1_HANGING_OFF_LEDGE | PLAYER_STATE1_CLIMBING_LEDGE)) { if (Player_FriendlyLockOnOrParallel(this)) { camMode = CAM_MODE_HANGZ; @@ -11602,18 +12634,18 @@ void Player_UpdateCamAndSeqModes(PlayState* play, Player* this) { } } - Camera_ChangeMode(Play_GetCamera(play, 0), camMode); + Camera_ChangeMode(playerCam, camMode); } else { // First person mode seqMode = SEQ_MODE_STILL; } - if (play->actorCtx.targetCtx.bgmEnemy != NULL) { + if (!this->isSecondPlayer && (play->actorCtx.targetCtx.bgmEnemy != NULL)) { seqMode = SEQ_MODE_ENEMY; Audio_SetBgmEnemyVolume(sqrtf(play->actorCtx.targetCtx.bgmEnemy->xyzDistToPlayerSq)); } - if (play->sceneNum != SCENE_FISHING_POND) { + if (!this->isSecondPlayer && (play->sceneNum != SCENE_FISHING_POND)) { Audio_SetSequenceMode(seqMode); } } @@ -11737,7 +12769,7 @@ void Player_UpdateBodyBurn(PlayState* play, Player* this) { } if ((dmgCooldown & play->gameplayFrames) == 0) { - Player_InflictDamage(play, -1); + Player_InflictDamage(play, this, -1); } } else { this->bodyIsBurning = false; @@ -11852,6 +12884,21 @@ void Player_UpdateCommon(Player* this, PlayState* play, Input* input) { sControlInput = input; + if (this->isSecondPlayer) { + s32 expectedShield = SHIELD_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_SHIELD)); + s32 expectedTunic = TUNIC_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_TUNIC)); + s32 expectedBoots = BOOTS_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS)); + + if ((this->currentShield != expectedShield) || (this->currentTunic != expectedTunic) || + (this->equippedBoots != expectedBoots) || this->bootsToggleRequest) { + Player_SetEquipmentData(play, this); + } + } + + // Boot physics tuning lives in global regs. Re-apply for each player every update + // so one player's quick boot toggle cannot leak movement behavior to another player. + Player_SetBootData(play, this); + if (this->unk_A86 < 0) { this->unk_A86++; if (this->unk_A86 == 0) { @@ -11903,8 +12950,8 @@ void Player_UpdateCommon(Player* this, PlayState* play, Input* input) { } if ((this->stateFlags3 & PLAYER_STATE3_RESTORE_NAYRUS_LOVE) && (gSaveContext.nayrusLoveTimer != 0) && - (gSaveContext.magicState == MAGIC_STATE_IDLE)) { - gSaveContext.magicState = MAGIC_STATE_METER_FLASH_1; + (Magic_GetStateForPlayer(this) == MAGIC_STATE_IDLE)) { + Magic_SetStateForPlayer(this, MAGIC_STATE_METER_FLASH_1); Player_SpawnMagicSpell(play, this, 1); this->stateFlags3 &= ~PLAYER_STATE3_RESTORE_NAYRUS_LOVE; } @@ -12015,6 +13062,8 @@ void Player_UpdateCommon(Player* this, PlayState* play, Input* input) { this->actor.velocity.z += this->pushedSpeed * Math_CosS(this->pushedYaw); } + Player_ApplyElasticRopePull(this, play); + Actor_UpdatePos(&this->actor); Player_ProcessSceneCollision(play, this); } else { @@ -12075,7 +13124,7 @@ void Player_UpdateCommon(Player* this, PlayState* play, Input* input) { if (!Player_InBlockingCsMode(play, this) && !(this->stateFlags2 & PLAYER_STATE2_CRAWLING)) { func_8083D53C(play, this); - if ((this->actor.category == ACTORCAT_PLAYER) && (gSaveContext.health == 0)) { + if ((this->actor.category == ACTORCAT_PLAYER) && (*Player_GetHealthPtr(this) == 0)) { if (this->stateFlags1 & (PLAYER_STATE1_HANGING_OFF_LEDGE | PLAYER_STATE1_CLIMBING_LEDGE | PLAYER_STATE1_CLIMBING_LADDER)) { func_80832440(play, this); @@ -12097,7 +13146,7 @@ void Player_UpdateCommon(Player* this, PlayState* play, Input* input) { } } - if ((play->csCtx.state != CS_STATE_IDLE) && (this->csAction != 6) && + if ((play->csCtx.state != CS_STATE_IDLE) && !this->isSecondPlayer && (this->csAction != 6) && !(this->stateFlags1 & PLAYER_STATE1_ON_HORSE) && !(this->stateFlags2 & PLAYER_STATE2_GRABBED_BY_ENEMY) && (this->actor.category == ACTORCAT_PLAYER)) { CsCmdActorCue* linkActionCsCmd = play->csCtx.linkAction; @@ -12122,6 +13171,16 @@ void Player_UpdateCommon(Player* this, PlayState* play, Input* input) { } } else { this->prevCsAction = 0; + + // Recover from stale cutscene lock for the main player after cutscene exit. + if (!this->isSecondPlayer && (play->csCtx.state == CS_STATE_IDLE) && + (this->stateFlags1 & PLAYER_STATE1_IN_CUTSCENE) && + !(this->stateFlags1 & (PLAYER_STATE1_LOADING | PLAYER_STATE1_DEAD | PLAYER_STATE1_IN_ITEM_CS | + PLAYER_STATE1_GETTING_ITEM | PLAYER_STATE1_TALKING)) && + (this->actionFunc != Player_Action_CsAction) && (this->actionFunc != Player_Action_8084F608) && + (this->actionFunc != Player_Action_8084F698) && (this->actionFunc != Player_Action_8084F710)) { + this->stateFlags1 &= ~PLAYER_STATE1_IN_CUTSCENE; + } } func_8083D6EC(play, this); @@ -12220,7 +13279,8 @@ void Player_UpdateCommon(Player* this, PlayState* play, Input* input) { if (!(this->stateFlags2 & PLAYER_STATE2_FROZEN)) { if (!(this->stateFlags1 & (PLAYER_STATE1_DEAD | PLAYER_STATE1_HANGING_OFF_LEDGE | - PLAYER_STATE1_CLIMBING_LEDGE | PLAYER_STATE1_ON_HORSE))) { + PLAYER_STATE1_CLIMBING_LEDGE | PLAYER_STATE1_ON_HORSE)) && + (Player_GetCarrier(this) == NULL)) { CollisionCheck_SetOC(play, &play->colChkCtx, &this->cylinder.base); } @@ -12240,7 +13300,8 @@ void Player_UpdateCommon(Player* this, PlayState* play, Input* input) { Math_Vec3f_Copy(&this->actor.home.pos, &this->actor.world.pos); Math_Vec3f_Copy(&this->unk_A88, &this->bodyPartsPos[PLAYER_BODYPART_WAIST]); - if (this->stateFlags1 & (PLAYER_STATE1_DEAD | PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE)) { + if ((Player_GetCarrier(this) != NULL) || + (this->stateFlags1 & (PLAYER_STATE1_DEAD | PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE))) { this->actor.colChkInfo.mass = MASS_IMMOVABLE; } else { this->actor.colChkInfo.mass = 50; @@ -12259,6 +13320,16 @@ void Player_UpdateCommon(Player* this, PlayState* play, Input* input) { static Vec3f D_80854838 = { 0.0f, 0.0f, -30.0f }; +static Input* Player_GetAssignedInput(PlayState* play, Player* this) { + s32 controllerPort = this->controllerPort; + + if ((controllerPort < 1) || (controllerPort > ARRAY_COUNT(play->state.input))) { + controllerPort = 1; + } + + return &play->state.input[controllerPort - 1]; +} + s32 Player_UpdateNoclip(Player* this, PlayState* play); void Player_Update(Actor* thisx, PlayState* play) { @@ -12266,7 +13337,9 @@ void Player_Update(Actor* thisx, PlayState* play) { Player* this = (Player*)thisx; s32 dogParams; s32 pad; + s32 suppressSecondaryInputDuringCutscene; Input sp44; + Input* assignedInput = Player_GetAssignedInput(play, this); Actor* dog; if (Player_UpdateNoclip(this, play)) { @@ -12297,27 +13370,61 @@ void Player_Update(Actor* thisx, PlayState* play) { Player_DetachHeldActor(play, this); } - if (this->stateFlags1 & (PLAYER_STATE1_INPUT_DISABLED | PLAYER_STATE1_IN_CUTSCENE)) { + suppressSecondaryInputDuringCutscene = Player_ShouldSuppressSecondaryInputDuringCutscene(play, this); + if (suppressSecondaryInputDuringCutscene) { + this->csAction = 0; + this->prevCsAction = 0; + this->stateFlags1 &= ~PLAYER_STATE1_IN_CUTSCENE; + Player_ZeroSpeedXZ(this); + } + + if (suppressSecondaryInputDuringCutscene || + (this->stateFlags1 & (PLAYER_STATE1_INPUT_DISABLED | PLAYER_STATE1_IN_CUTSCENE)) || !this->inputEnabled) { memset(&sp44, 0, sizeof(sp44)); } else { - sp44 = play->state.input[0]; + sp44 = *assignedInput; if (this->textboxBtnCooldownTimer != 0) { sp44.cur.button &= ~(BTN_A | BTN_B | BTN_CUP); sp44.press.button &= ~(BTN_A | BTN_B | BTN_CUP); } } + Player_HandleOriginalMultiplayerItemCycle(this, play, &sp44); + if (CVarGetFloat(CVAR_CHEAT("SpeedModifier.Value"), 1.0f) != 1.0f && CVarGetInteger(CVAR_CHEAT("SpeedModifier.SpeedToggle"), 0)) { const s32 mod1Mask = CVarGetInteger(CVAR_CHEAT("SpeedModifier.Btn"), BTN_CUSTOM_MODIFIER1); - if (mod1Mask != 0 && CHECK_BTN_ALL(sControlInput->cur.button, mod1Mask) && - CHECK_BTN_ANY(sControlInput->press.button, mod1Mask)) { + if (mod1Mask != 0 && CHECK_BTN_ALL(sp44.cur.button, mod1Mask) && CHECK_BTN_ANY(sp44.press.button, mod1Mask)) { gWalkSpeedToggle = !gWalkSpeedToggle; } } + if (!Player_InCsMode(play) && (play->msgCtx.msgMode == MSGMODE_NONE) && + !(this->stateFlags1 & PLAYER_STATE1_TALKING) && CHECK_BTN_ALL(sp44.press.button, BTN_A)) { + s32 equippedBoots = BOOTS_EQUIP_TO_PLAYER(CUR_EQUIP_VALUE(EQUIP_TYPE_BOOTS)); + s32 canToggle = false; + + if ((equippedBoots == PLAYER_BOOTS_IRON) || (equippedBoots == PLAYER_BOOTS_HOVER)) { + if ((equippedBoots == PLAYER_BOOTS_IRON) && (this->stateFlags1 & PLAYER_STATE1_IN_WATER)) { + canToggle = true; + } else if ((this->actor.bgCheckFlags & 1) && (this->linearVelocity < 0.1f) && + (ABS(sp44.rel.stick_x) < 10) && (ABS(sp44.rel.stick_y) < 10)) { + canToggle = true; + } + } + + if (canToggle) { + this->bootsToggleRequest = true; + Player_SetEquipmentData(play, this); + sp44.cur.button &= ~BTN_A; + sp44.press.button &= ~BTN_A; + sp44.rel.button &= ~BTN_A; + } + } + Player_UpdateCommon(this, play, &sp44); + Player_OfferCarryToOtherPlayers(this, play); } MREG(52) = this->actor.world.pos.x; @@ -12596,6 +13703,8 @@ void Player_Draw(Actor* thisx, PlayState* play2) { POLY_OPA_DISP = Play_SetFog(play, POLY_OPA_DISP); } + Player_DrawElasticRope(this, play); + if (this->stateFlags2 & PLAYER_STATE2_FROZEN) { f32 scale = (this->av1.actionVar1 >> 1) * 22.0f; @@ -12627,7 +13736,7 @@ void Player_Destroy(Actor* thisx, PlayState* play) { Collider_DestroyQuad(play, &this->meleeWeaponQuads[1]); Collider_DestroyQuad(play, &this->shieldQuad); - Magic_Reset(play); + Magic_ResetForPlayer(play, this); gSaveContext.linkAge = play->linkAgeOnLoad; @@ -14146,7 +15255,9 @@ s32 func_8084DFF4(PlayState* play, Player* this) { gSaveContext.bgsFlag = true; gSaveContext.swordHealth = 8; } + Item_Give_SetPlayer(this); Item_Give(play, giEntry.itemId); + Item_Give_ClearPlayer(); } else { Randomizer_Item_Give(play, giEntry); } @@ -14497,28 +15608,40 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { rand = 3; } - if ((rand < 0) && (gSaveContext.health <= FULL_HEART_HEALTH)) { + if ((rand < 0) && (*Player_GetHealthPtr(this) <= FULL_HEART_HEALTH)) { rand = 3; } if (rand < 0) { - Health_ChangeBy(play, -FULL_HEART_HEALTH); + Health_ChangeBy(play, -FULL_HEART_HEALTH, this); } else { - gSaveContext.healthAccumulator = rand * FULL_HEART_HEALTH; + if (this->isSecondPlayer) { + Health_ChangeBy(play, rand * FULL_HEART_HEALTH, this); + } else { + gSaveContext.healthAccumulator = rand * FULL_HEART_HEALTH; + } } } else { s32 sp28 = D_808549FC[this->itemAction - PLAYER_IA_BOTTLE_POTION_RED]; if (sp28 & 1) { - gSaveContext.healthAccumulator = MAX_HEALTH; + if (this->isSecondPlayer) { + Health_ChangeBy(play, MAX_HEALTH, this); + } else { + gSaveContext.healthAccumulator = MAX_HEALTH; + } } if (sp28 & 2) { - Magic_Fill(play); + Magic_FillForPlayer(play, this); } if (sp28 & 4) { - gSaveContext.healthAccumulator = 0x50; + if (this->isSecondPlayer) { + Health_ChangeBy(play, 0x50, this); + } else { + gSaveContext.healthAccumulator = 0x50; + } } } @@ -14526,10 +15649,11 @@ void Player_Action_8084EAC0(Player* this, PlayState* play) { this->av2.actionVar2 = 1; } else { func_8083C0E8(this, play); - func_8005B1A4(Play_GetCamera(play, 0)); + func_8005B1A4(Player_GetCameraForPlayerOrMain(play, this)); } } else if (this->av2.actionVar2 == 1) { - if ((gSaveContext.healthAccumulator == 0) && (gSaveContext.magicState != MAGIC_STATE_FILL)) { + if ((this->isSecondPlayer || (gSaveContext.healthAccumulator == 0)) && + (Magic_GetStateForPlayer(this) != MAGIC_STATE_FILL)) { Player_AnimChangeOnceMorphAdjusted(play, this, &gPlayerAnim_link_bottle_drink_demo_end); this->av2.actionVar2 = 2; Player_UpdateBottleHeld(play, this, ITEM_BOTTLE, PLAYER_IA_BOTTLE); @@ -14573,18 +15697,21 @@ void Player_Action_SwingBottle(Player* this, PlayState* play) { if (LinkAnimation_Update(play, &this->skelAnime)) { if (this->av1.bottleCatchType != BOTTLE_CATCH_NONE) { if (!this->av2.startedTextbox) { - if (CVarGetInteger(CVAR_ENHANCEMENT("FastBottles"), 0)) { + BottleCatchInfo* catchInfo = &sBottleCatchInfo[this->av1.bottleCatchType - 1]; + s32 shouldSkipBottleCutscene = CVarGetInteger(CVAR_ENHANCEMENT("FastBottles"), 0) || + (Player_GetLocalMultiplayerPlayerCount() > 1); + + if (shouldSkipBottleCutscene) { this->av1.bottleCatchType = BOTTLE_CATCH_NONE; } else { - // 1 is subtracted because `sBottleCatchInfo` does not have an entry for `BOTTLE_CATCH_NONE` - Message_StartTextbox(play, sBottleCatchInfo[this->av1.bottleCatchType - 1].textId, &this->actor); + Message_StartTextbox(play, catchInfo->textId, &this->actor); } Audio_PlayFanfare(NA_BGM_ITEM_GET | 0x900); - GameInteractor_ExecuteOnPlayerBottleUpdate((sBottleCatchInfo[this->av1.bottleCatchType - 1].itemId)); + GameInteractor_ExecuteOnPlayerBottleUpdate(catchInfo->itemId); this->av2.startedTextbox = true; } else if (Message_GetState(&play->msgCtx) == TEXT_STATE_CLOSING) { this->av1.bottleCatchType = BOTTLE_CATCH_NONE; - func_8005B1A4(Play_GetCamera(play, 0)); + func_8005B1A4(Player_GetCameraForPlayerOrMain(play, this)); } } else { func_8083C0E8(this, play); @@ -14612,19 +15739,22 @@ void Player_Action_SwingBottle(Player* this, PlayState* play) { if (GameInteractor_Should(VB_BOTTLE_ACTOR, i < ARRAY_COUNT(sBottleCatchInfo), this->interactRangeActor)) { + s32 shouldSkipBottleCutscene = CVarGetInteger(CVAR_ENHANCEMENT("FastBottles"), 0) || + (Player_GetLocalMultiplayerPlayerCount() > 1); + // 1 is added because `sBottleCatchInfo` does not have an entry for `BOTTLE_CATCH_NONE` this->av1.bottleCatchType = i + 1; this->av2.startedTextbox = false; - if (!CVarGetInteger(CVAR_ENHANCEMENT("FastBottles"), 0)) { + if (!shouldSkipBottleCutscene) { this->stateFlags1 |= PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE; } this->interactRangeActor->parent = &this->actor; Player_UpdateBottleHeld(play, this, catchInfo->itemId, ABS(catchInfo->itemAction)); - if (!CVarGetInteger(CVAR_ENHANCEMENT("FastBottles"), 0)) { + if (!shouldSkipBottleCutscene) { Player_AnimPlayOnceAdjusted(play, this, swingEntry->catchAnimation); - func_80835EA4(play, 4); + Player_SetTurnAroundCameraForPlayer(play, this, 4); } } } @@ -14644,7 +15774,7 @@ static Vec3f D_80854A1C = { 0.0f, 0.0f, 5.0f }; void Player_Action_8084EED8(Player* this, PlayState* play) { if (LinkAnimation_Update(play, &this->skelAnime)) { func_8083C0E8(this, play); - func_8005B1A4(Play_GetCamera(play, 0)); + func_8005B1A4(Player_GetCameraForPlayerOrMain(play, this)); return; } @@ -14654,7 +15784,11 @@ void Player_Action_8084EED8(Player* this, PlayState* play) { Player_PlaySfx(this, NA_SE_EV_BOTTLE_CAP_OPEN); Player_PlaySfx(this, NA_SE_EV_FIATY_HEAL - SFX_FLAG); } else if (LinkAnimation_OnFrame(&this->skelAnime, 47.0f)) { - gSaveContext.healthAccumulator = MAX_HEALTH; + if (this->isSecondPlayer) { + Health_ChangeBy(play, MAX_HEALTH, this); + } else { + gSaveContext.healthAccumulator = MAX_HEALTH; + } } } @@ -14676,7 +15810,7 @@ void Player_Action_8084EFC0(Player* this, PlayState* play) { if (LinkAnimation_Update(play, &this->skelAnime)) { func_8083C0E8(this, play); - func_8005B1A4(Play_GetCamera(play, 0)); + func_8005B1A4(Player_GetCameraForPlayerOrMain(play, this)); return; } @@ -14728,6 +15862,15 @@ void Player_Action_ExchangeItem(Player* this, PlayState* play) { if (this->av2.actionVar2 == 0) { s32 pad; + // In multiplayer with no talk actor, skip the global textbox so it does not cover + // other players' viewports and does not trigger Play_InCsMode via Player_SetupTalk. + if (Player_GetLocalMultiplayerPlayerCount() > 1 && this->talkActor == NULL) { + // Skip textbox; pretend it already closed so the action finishes immediately. + this->av2.actionVar2 = -1; + func_8005B1A4(Player_GetCameraForPlayerOrMain(play, this)); + return; + } + Message_StartTextbox(play, this->actor.textId, &this->actor); if ((this->itemAction == PLAYER_IA_CHICKEN) || (this->itemAction == PLAYER_IA_POCKET_CUCCO)) { @@ -14746,7 +15889,7 @@ void Player_Action_ExchangeItem(Player* this, PlayState* play) { func_8083C0E8(this, play); } - func_8005B1A4(Play_GetCamera(play, 0)); + func_8005B1A4(Player_GetCameraForPlayerOrMain(play, this)); } } } else if (this->av2.actionVar2 >= 0) { @@ -14963,7 +16106,7 @@ void Player_Action_8084FB10(Player* this, PlayState* play) { } if ((play->gameplayFrames % 4) == 0) { - Player_InflictDamage(play, -1); + Player_InflictDamage(play, this, -1); } } else { if (LinkAnimation_Update(play, &this->skelAnime)) { @@ -15008,7 +16151,7 @@ void Player_Action_8084FBF4(Player* this, PlayState* play) { * @return true if Noclip is disabled, false if enabled */ s32 Player_UpdateNoclip(Player* this, PlayState* play) { - sControlInput = &play->state.input[0]; + sControlInput = Player_GetAssignedInput(play, this); s32 mask = CVarGetInteger("gDeveloperTools.NoClipBtn", BTN_L | BTN_DRIGHT); if (CVarGetInteger(CVAR_DEVELOPER_TOOLS("DebugEnabled"), 0) && @@ -15043,7 +16186,7 @@ s32 Player_UpdateNoclip(Player* this, PlayState* play) { s16 angle; s16 temp; - angle = temp = Camera_GetInputDirYaw(GET_ACTIVE_CAM(play)); + angle = temp = Player_GetInputDirYawForPlayer(play, this); if (CHECK_BTN_ALL(sControlInput->cur.button, BTN_DDOWN)) { angle = temp + 0x8000; @@ -15334,7 +16477,7 @@ void Player_Action_808507F4(Player* this, PlayState* play) { if (LinkAnimation_Update(play, &this->skelAnime)) { if (this->av1.actionVar1 < 0) { if ((this->itemAction == PLAYER_IA_NAYRUS_LOVE) || isFastFarores || - (gSaveContext.magicState == MAGIC_STATE_IDLE)) { + (Magic_GetStateForPlayer(this) == MAGIC_STATE_IDLE)) { func_80839FFC(this, play); func_8005B1A4(Play_GetCamera(play, 0)); } @@ -15346,10 +16489,10 @@ void Player_Action_808507F4(Player* this, PlayState* play) { if (Player_SpawnMagicSpell(play, this, this->av1.actionVar1) != NULL) { this->stateFlags1 |= PLAYER_STATE1_IN_ITEM_CS | PLAYER_STATE1_IN_CUTSCENE; if ((this->av1.actionVar1 != 0) || (gSaveContext.respawn[RESPAWN_MODE_TOP].data <= 0)) { - gSaveContext.magicState = MAGIC_STATE_CONSUME_SETUP; + Magic_SetStateForPlayer(this, MAGIC_STATE_CONSUME_SETUP); } } else { - Magic_Reset(play); + Magic_ResetForPlayer(play, this); } } else { LinkAnimation_PlayLoopSetSpeed(play, &this->skelAnime, D_80854A64[this->av1.actionVar1], @@ -16615,13 +17758,11 @@ void func_80853080(Player* this, PlayState* play) { this->yaw = this->actor.shape.rot.y; } -s32 Player_InflictDamage(PlayState* play, s32 damage) { - return Player_InflictDamageModified(play, damage, true); +s32 Player_InflictDamage(PlayState* play, Player* this, s32 damage) { + return Player_InflictDamageModified(play, this, damage, true); } -s32 Player_InflictDamageModified(PlayState* play, s32 damage, u8 modified) { - Player* this = GET_PLAYER(play); - +s32 Player_InflictDamageModified(PlayState* play, Player* this, s32 damage, u8 modified) { if (!Player_InBlockingCsMode(play, this) && !func_80837B18_modified(play, this, damage, modified)) { this->stateFlags2 &= ~PLAYER_STATE2_GRABBED_BY_ENEMY; return 1; diff --git a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c index b79da892f62..df0f2427f71 100644 --- a/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c +++ b/soh/src/overlays/misc/ovl_kaleido_scope/z_kaleido_debug.c @@ -383,13 +383,13 @@ void KaleidoScope_DrawDebugEditor(PlayState* play) { case 2: if (CHECK_BTN_ALL(input->press.button, BTN_CLEFT)) { - Health_ChangeBy(play, -4); + Health_ChangeBy(play, -4, GET_PLAYER(play)); } else if (CHECK_BTN_ALL(input->press.button, BTN_CRIGHT)) { - Health_ChangeBy(play, 4); + Health_ChangeBy(play, 4, GET_PLAYER(play)); } else if (CHECK_BTN_ALL(input->press.button, BTN_CUP)) { - Health_ChangeBy(play, -FULL_HEART_HEALTH); + Health_ChangeBy(play, -FULL_HEART_HEALTH, GET_PLAYER(play)); } else if (CHECK_BTN_ALL(input->press.button, BTN_CDOWN)) { - Health_ChangeBy(play, FULL_HEART_HEALTH); + Health_ChangeBy(play, FULL_HEART_HEALTH, GET_PLAYER(play)); } break;