From 6e6834af22a645b54538a3a4072c29433584266a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= Date: Wed, 14 Jan 2026 04:29:30 +0000 Subject: [PATCH] Rando: Shuffle Crawl (#5032) --- .../vanilla-behavior/GIVanillaBehavior.h | 8 ++++++ .../3drando/hint_list/hint_list_item.cpp | 1 + .../randomizer/3drando/item_pool.cpp | 4 +++ .../Enhancements/randomizer/3drando/shops.cpp | 5 ++++ soh/soh/Enhancements/randomizer/draw.cpp | 19 +++++++++++++- soh/soh/Enhancements/randomizer/draw.h | 1 + .../Enhancements/randomizer/hook_handlers.cpp | 3 +++ soh/soh/Enhancements/randomizer/item_list.cpp | 7 +++-- .../dungeons/bottom_of_the_well.cpp | 26 +++++++++---------- .../location_access/dungeons/deku_tree.cpp | 8 +++--- .../dungeons/spirit_temple.cpp | 20 +++++++------- .../overworld/castle_grounds.cpp | 12 ++++++--- .../overworld/kokiri_forest.cpp | 20 +++++++++----- .../overworld/lon_lon_ranch.cpp | 2 +- soh/soh/Enhancements/randomizer/logic.cpp | 12 +++++++++ .../randomizer/option_descriptions.cpp | 1 + .../Enhancements/randomizer/randomizer.cpp | 6 ++++- .../Enhancements/randomizer/randomizerTypes.h | 5 ++++ .../Enhancements/randomizer/randomizer_inf.h | 1 + soh/soh/Enhancements/randomizer/savefile.cpp | 4 +++ soh/soh/Enhancements/randomizer/settings.cpp | 3 +++ .../actors/ovl_player_actor/z_player.c | 4 +++ 22 files changed, 131 insertions(+), 41 deletions(-) diff --git a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h index 153358a4c..03b256172 100644 --- a/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h +++ b/soh/soh/Enhancements/game-interactor/vanilla-behavior/GIVanillaBehavior.h @@ -326,6 +326,14 @@ typedef enum { // - `*ObjKibako2` VB_CRATE_SETUP_DRAW, + // #### `result` + // ```c + // true + // ``` + // #### `args` + // - None + VB_CRAWL, + // #### `result` // ```c // true diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp index 6a0c8babe..dacd6a28a 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp @@ -2107,6 +2107,7 @@ void StaticData::HintTable_Init_Item() { }, { CustomMessage("a master unlocker", /*german*/ "ein Meisterentsperrer", /*french*/ "un Kit de Déverrouillage") }); // /*spanish*/un desbloqueador maestro + hintTextTable[RHT_CRAWL] = HintText(CustomMessage("the ability to crawl", /*german*/TODO_TRANSLATE, /*french*/"la capacité de ramper")); //RANDOTODO if these are ever used for anything other than name, they want abscure and ambiguous hints hintTextTable[RHT_QUIVER_INF] = HintText(CustomMessage("an infinite Quiver", /*german*/"der unendliche Köcher", /*french*/"un Carquois Infini")); diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index 78f09a8d9..ec5a646d7 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -392,6 +392,10 @@ void GenerateItemPool() { int bronzeScale = ctx->GetOption(RSK_SHUFFLE_SWIM) ? 1 : 0; AddItemToPool(RG_PROGRESSIVE_SCALE, 3 + bronzeScale, 2 + bronzeScale, 2 + bronzeScale, 2 + bronzeScale); + if (ctx->GetOption(RSK_SHUFFLE_CRAWL)) { + AddItemToPool(RG_CRAWL, 2, 1, 1, 1); + } + if (ctx->GetOption(RSK_SHUFFLE_BEEHIVES)) { PlaceItemsForType(RCTYPE_BEEHIVE, true, true); } diff --git a/soh/soh/Enhancements/randomizer/3drando/shops.cpp b/soh/soh/Enhancements/randomizer/3drando/shops.cpp index 01683eec4..1da898fa7 100644 --- a/soh/soh/Enhancements/randomizer/3drando/shops.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/shops.cpp @@ -997,6 +997,11 @@ void InitTrickNames() { Text{ "Stalfos Key" }, Text{ "Nightmare Key" }, Text{ "Graveyard Key" }, Text{ "King's Key" }, Text{ "Hero's Key" }, }; + trickNameTable[RG_CRAWL] = { + // TODO_TRANSLATE + Text{ "Crouch" }, + }; + trickNameTable[RG_OCARINA_A_BUTTON] = { Text{ "Ocarina J Button", "Touche Ha de l'Ocarina", "J-Taste der Okarina" }, Text{ "Ocarina Ayy Button", "Touche Ah de l'Ocarina", "A-Taste der Flöte" }, diff --git a/soh/soh/Enhancements/randomizer/draw.cpp b/soh/soh/Enhancements/randomizer/draw.cpp index 78295f45c..62971a284 100644 --- a/soh/soh/Enhancements/randomizer/draw.cpp +++ b/soh/soh/Enhancements/randomizer/draw.cpp @@ -2,7 +2,6 @@ #include "soh/OTRGlobals.h" #include "soh/cvar_prefixes.h" #include "randomizerTypes.h" -#include #include "soh_assets.h" #include "soh/Enhancements/cosmetics/cosmeticsTypes.h" @@ -35,6 +34,7 @@ extern "C" { #include "overlays/ovl_Boss_Sst/ovl_Boss_Sst.h" #include "objects/object_tw/object_tw.h" #include "objects/object_ganon2/object_ganon2.h" +#include "objects/object_gi_shield_1/object_gi_shield_1.h" extern PlayState* gPlayState; extern SaveContext gSaveContext; } @@ -1136,6 +1136,23 @@ extern "C" void Randomizer_DrawBronzeScale(PlayState* play, GetItemEntry* getIte CLOSE_DISPS(play->state.gfxCtx); } +extern "C" void Randomizer_DrawKneePads(PlayState* play, GetItemEntry* getItemEntry) { + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL_25Opa(play->state.gfxCtx); + Matrix_Translate(-35, -5, 0, MTXMODE_APPLY); + Matrix_Scale(0.4f, 0.8f, 1.2f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gGiDekuShieldDL); + + Gfx_SetupDL_25Opa(play->state.gfxCtx); + Matrix_Translate(35, -7, 4, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gGiDekuShieldDL); + + CLOSE_DISPS(play->state.gfxCtx); +} + extern "C" void Randomizer_DrawFishingPoleGI(PlayState* play, GetItemEntry* getItemEntry) { Vec3f pos; OPEN_DISPS(play->state.gfxCtx); diff --git a/soh/soh/Enhancements/randomizer/draw.h b/soh/soh/Enhancements/randomizer/draw.h index 474835c53..e00678f9b 100644 --- a/soh/soh/Enhancements/randomizer/draw.h +++ b/soh/soh/Enhancements/randomizer/draw.h @@ -22,6 +22,7 @@ void Randomizer_DrawTriforcePiece(PlayState* play, GetItemEntry getItemEntry); void Randomizer_DrawTriforcePieceGI(PlayState* play, GetItemEntry getItemEntry); void Randomizer_DrawOcarinaButton(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawBronzeScale(PlayState* play, GetItemEntry* getItemEntry); +void Randomizer_DrawKneePads(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawFishingPoleGI(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawSkeletonKey(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawMysteryItem(PlayState* play, GetItemEntry* getItemEntry); diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index acf4262bd..57afb717b 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -850,6 +850,9 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l va_copy(args, originalArgs); switch (id) { + case VB_CRAWL: + *should = !RAND_GET_OPTION(RSK_SHUFFLE_CRAWL) || Flags_GetRandomizerInf(RAND_INF_CAN_CRAWL); + break; case VB_ALLOW_ENTRANCE_CS_FOR_EITHER_AGE: { s32 entranceIndex = va_arg(args, s32); diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp index 4ff3e1e04..0b4942974 100644 --- a/soh/soh/Enhancements/randomizer/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/item_list.cpp @@ -369,10 +369,13 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_OCARINA_C_RIGHT_BUTTON] = Item(RG_OCARINA_C_RIGHT_BUTTON, Text{ "Ocarina C Right Button", "Touche C-Droit de l'Ocarina", "C-Rechts-Taste der Okarina" }, ITEMTYPE_ITEM, GI_MAP, true, LOGIC_OCARINA_C_RIGHT_BUTTON, RHT_OCARINA_C_RIGHT_BUTTON, RG_OCARINA_C_RIGHT_BUTTON, OBJECT_GI_MAP, GID_STONE_OF_AGONY, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); itemTable[RG_OCARINA_C_RIGHT_BUTTON].SetCustomDrawFunc(Randomizer_DrawOcarinaButton); - itemTable[RG_BRONZE_SCALE] = Item(RG_BRONZE_SCALE, Text{ "Bronze Scale", "Écaille de Bronze", "Bronzene Schuppe" }, ITEMTYPE_ITEM, GI_SCALE_SILVER, true, LOGIC_PROGRESSIVE_WALLET, RHT_BRONZE_SCALE, RG_BRONZE_SCALE, OBJECT_GI_SCALE, GID_SCALE_SILVER, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_BRONZE_SCALE] = Item(RG_BRONZE_SCALE, Text{ "Bronze Scale", "Écaille de Bronze", "Bronzene Schuppe" }, ITEMTYPE_ITEM, GI_SCALE_SILVER, true, LOGIC_PROGRESSIVE_SCALE, RHT_BRONZE_SCALE, RG_BRONZE_SCALE, OBJECT_GI_SCALE, GID_SCALE_SILVER, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); itemTable[RG_BRONZE_SCALE].SetCustomDrawFunc(Randomizer_DrawBronzeScale); - itemTable[RG_PROGRESSIVE_BOMBCHU_BAG] = Item(RG_PROGRESSIVE_BOMBCHU_BAG, Text{ "Bombchu Bag", "Sac de Missiles Teigneux", "Krabbelminentasche" }, ITEMTYPE_ITEM, RG_PROGRESSIVE_BOMBCHU_BAG, true, LOGIC_BOMBCHUS, RHT_BOMBCHU_BAG, RG_PROGRESSIVE_BOMBCHU_BAG, OBJECT_GI_BOMB_2, GID_BOMBCHU, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_CRAWL] = Item(RG_CRAWL, Text{ "Crawl", "Ramper", "Kriechen" }, ITEMTYPE_ITEM, GI_SHIELD_DEKU, true, LOGIC_NONE, RHT_CRAWL, RG_CRAWL, OBJECT_GI_SHIELD_1, GID_SHIELD_DEKU, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_CRAWL].SetCustomDrawFunc(Randomizer_DrawKneePads); + + itemTable[RG_PROGRESSIVE_BOMBCHU_BAG] = Item(RG_PROGRESSIVE_BOMBCHU_BAG, Text{ "Bombchu Bag", "Sac de Missiles Teigneux", "Krabbelminentasche" }, ITEMTYPE_ITEM, RG_PROGRESSIVE_BOMBCHU_BAG, true, LOGIC_BOMBCHUS, RHT_BOMBCHU_BAG, RG_PROGRESSIVE_BOMBCHU_BAG, OBJECT_GI_BOMB_2, GID_BOMBCHU, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); itemTable[RG_PROGRESSIVE_BOMBCHU_BAG].SetCustomDrawFunc(Randomizer_DrawBombchuBag); itemTable[RG_QUIVER_INF] = Item(RG_QUIVER_INF, Text{ "Infinite Quiver", "Carquois Infini", "Unendlicher Köcher" }, ITEMTYPE_ITEM, RG_QUIVER_INF, true, LOGIC_PROGRESSIVE_BOW, RHT_QUIVER_INF, RG_QUIVER_INF, OBJECT_GI_ARROWCASE, GID_QUIVER_50, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_LESSER, MOD_RANDOMIZER); 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 a66c7e43f..b2d51eeb6 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 @@ -11,8 +11,8 @@ void RegionTable_Init_BottomOfTheWell() { //Exits //Technically involves an fake wall, but passing it lensless is intended in vanilla and it is well telegraphed //Backshot should be implemented here, or new regions should be added - Entrance(RR_BOTW_CORRIDOR, []{return ctx->GetDungeon(Rando::BOTTOM_OF_THE_WELL)->IsVanilla() && logic->IsChild/*CanCrawl*/;}), - Entrance(RR_BOTW_MQ_PERIMETER, []{return ctx->GetDungeon(Rando::BOTTOM_OF_THE_WELL)->IsMQ() && logic->IsChild/*CanCrawl*/;}), + Entrance(RR_BOTW_CORRIDOR, []{return ctx->GetDungeon(Rando::BOTTOM_OF_THE_WELL)->IsVanilla() && logic->CanUse(RG_CRAWL);}), + Entrance(RR_BOTW_MQ_PERIMETER, []{return ctx->GetDungeon(Rando::BOTTOM_OF_THE_WELL)->IsMQ() && logic->CanUse(RG_CRAWL);}), Entrance(RR_KAK_WELL, []{return true;}), }); @@ -20,7 +20,7 @@ void RegionTable_Init_BottomOfTheWell() { areaTable[RR_BOTW_CORRIDOR] = Region("Bottom of the Well Corridor", SCENE_BOTTOM_OF_THE_WELL, {}, {}, { //Exits - Entrance(RR_BOTW_ENTRYWAY, []{return logic->IsChild/*CanCrawl && CanClimb*/;}), + Entrance(RR_BOTW_ENTRYWAY, []{return logic->CanUse(RG_CRAWL) /* && CanClimb*/;}), Entrance(RR_BOTW_PERIMETER, []{return logic->CanPassEnemy(RE_BIG_SKULLTULA);}), }); @@ -46,11 +46,11 @@ void RegionTable_Init_BottomOfTheWell() { Entrance(RR_BOTW_MIDDLE, []{return ctx->GetTrickOption(RT_LENS_BOTW) || logic->CanUse(RG_LENS_OF_TRUTH);}), Entrance(RR_BOTW_PIT_CAGE, []{return ctx->GetTrickOption(RT_LENS_BOTW) || logic->CanUse(RG_LENS_OF_TRUTH);}), Entrance(RR_BOTW_HIDDEN_POTS, []{return ctx->GetTrickOption(RT_LENS_BOTW) || logic->CanUse(RG_LENS_OF_TRUTH);}), - Entrance(RR_BOTW_CORNER_CRAWLSPACE, []{return logic->IsChild/*CanCrawl*/;}), + Entrance(RR_BOTW_CORNER_CRAWLSPACE, []{return logic->CanUse(RG_CRAWL);}), //Climb always needed in case water is lowered out of logic Entrance(RR_BOTW_BEHIND_MOAT, []{return (logic->Get(LOGIC_BOTW_LOWERED_WATER) || logic->HasItem(RG_BRONZE_SCALE) || (logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT))/*CanClimb*/);}), - Entrance(RR_BOTW_NEAR_BOSS_LOWER, []{return logic->Get(LOGIC_BOTW_LOWERED_WATER) && logic->IsChild/*CanCrawl*/;}), + Entrance(RR_BOTW_NEAR_BOSS_LOWER, []{return logic->Get(LOGIC_BOTW_LOWERED_WATER) && logic->CanUse(RG_CRAWL);}), //Falling down into basement requires nothing, but falling down somewhere specific requires lens or lens trick //kinda questionable given several drops are blocked by rocks, but that's how it was handled before and on N64 Entrance(RR_BOTW_B3_OOZE, []{return true;}), @@ -84,7 +84,7 @@ void RegionTable_Init_BottomOfTheWell() { areaTable[RR_BOTW_CORNER_CRAWLSPACE] = Region("Bottom of the Well Corner Crawlspace", SCENE_BOTTOM_OF_THE_WELL, {}, {}, { //Exits - Entrance(RR_BOTW_PERIMETER, []{return logic->IsChild;}), + Entrance(RR_BOTW_PERIMETER, []{return logic->CanUse(RG_CRAWL);}), Entrance(RR_BOTW_HIDDEN_PITS_ROOM, []{return logic->SmallKeys(SCENE_BOTTOM_OF_THE_WELL, 3);}), }); @@ -166,7 +166,7 @@ void RegionTable_Init_BottomOfTheWell() { //Exits //Climb always needed in case the water is lowered out of logic //Adult can ground jump out of the pit without climb but needs a way through the crawlspace - Entrance(RR_BOTW_PERIMETER, []{return logic->IsChild/*CanCrawl*/ && (logic->Get(LOGIC_BOTW_LOWERED_WATER) || logic->HasItem(RG_BRONZE_SCALE))/*&& CanClimb*/;}), + Entrance(RR_BOTW_PERIMETER, []{return logic->CanUse(RG_CRAWL) && (logic->Get(LOGIC_BOTW_LOWERED_WATER) || logic->HasItem(RG_BRONZE_SCALE))/*&& CanClimb*/;}), Entrance(RR_BOTW_NEAR_BOSS_UPPER, []{return true/*CanClimb or (isAdult && CanGroundJump)*/;}), }); @@ -280,14 +280,14 @@ void RegionTable_Init_BottomOfTheWell() { LOCATION(RC_BOTTOM_OF_THE_WELL_MQ_BOMB_RIGHT_HEART, logic->HasExplosives()), }, { //Exits - Entrance(RR_BOTW_ENTRYWAY, []{return logic->IsChild/*CanCrawl() && CanClimb()*/;}), + Entrance(RR_BOTW_ENTRYWAY, []{return logic->CanUse(RG_CRAWL) /* && CanClimb()*/;}), Entrance(RR_BOTW_MQ_MIDDLE, []{return logic->Get(LOGIC_BOTW_MQ_OPENED_GATES);}), Entrance(RR_BOTW_MQ_PIT_CAGE, []{return AnyAgeTime([]{return logic->BlastOrSmash();}) && logic->CanPassEnemy(RE_BIG_SKULLTULA);}), //Climb always needed in case water is lowered out of logic Entrance(RR_BOTW_MQ_BEHIND_MOAT, []{return (logic->Get(LOGIC_BOTW_LOWERED_WATER) || logic->HasItem(RG_BRONZE_SCALE) || (logic->IsAdult && logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT))/*&& CanClimb()*/);}), - Entrance(RR_BOTW_MQ_CORNER_CRAWLSPACE, []{return logic->IsChild/*CanCrawl()*/;}), - Entrance(RR_BOTW_MQ_NEAR_BOSS_LOWER, []{return logic->IsChild/*CanCrawl()*/ && logic->Get(LOGIC_BOTW_LOWERED_WATER);}), + Entrance(RR_BOTW_MQ_CORNER_CRAWLSPACE, []{return logic->CanUse(RG_CRAWL);}), + Entrance(RR_BOTW_MQ_NEAR_BOSS_LOWER, []{return logic->CanUse(RG_CRAWL) && logic->Get(LOGIC_BOTW_LOWERED_WATER);}), Entrance(RR_BOTW_MQ_B3, []{return true;}), }); @@ -365,7 +365,7 @@ void RegionTable_Init_BottomOfTheWell() { }); areaTable[RR_BOTW_MQ_CORNER_CRAWLSPACE] = Region("Bottom of the Well MQ Northeast Crawlspace", SCENE_BOTTOM_OF_THE_WELL, {}, {}, { - Entrance(RR_BOTW_MQ_PERIMETER, []{return logic->IsChild;}), + Entrance(RR_BOTW_MQ_PERIMETER, []{return logic->CanUse(RG_CRAWL);}), Entrance(RR_BOTW_MQ_FLOORMASTER_ROOM, []{return logic->CanUseProjectile();}), }); @@ -379,14 +379,14 @@ void RegionTable_Init_BottomOfTheWell() { EventAccess(LOGIC_BOTW_MQ_OPENED_MIDDLE_HOLE, []{return logic->HasExplosives();}), }, {}, { //Exits - Entrance(RR_BOTW_MQ_FLOORMASTER_ROOM, []{return logic->IsChild && logic->SmallKeys(SCENE_BOTTOM_OF_THE_WELL, 2);}), + Entrance(RR_BOTW_MQ_FLOORMASTER_ROOM, []{return logic->CanUse(RG_CRAWL) && logic->SmallKeys(SCENE_BOTTOM_OF_THE_WELL, 2);}), }); areaTable[RR_BOTW_MQ_NEAR_BOSS_LOWER] = Region("Bottom of the Well MQ Near Boss Lower", SCENE_BOTTOM_OF_THE_WELL, {}, {}, { //Exits //Climb always needed in case the water is lowered out of logic //Adult can ground jump out of the pit without climb but needs a way through the crawlspace - Entrance(RR_BOTW_MQ_PERIMETER, []{return logic->IsChild/*CanCrawl*/ && (logic->Get(LOGIC_BOTW_LOWERED_WATER) || logic->HasItem(RG_BRONZE_SCALE))/*&& CanClimb*/;}), + Entrance(RR_BOTW_MQ_PERIMETER, []{return logic->CanUse(RG_CRAWL) && (logic->Get(LOGIC_BOTW_LOWERED_WATER) || logic->HasItem(RG_BRONZE_SCALE))/*&& CanClimb*/;}), Entrance(RR_BOTW_MQ_NEAR_BOSS_UPPER, []{return true/*CanClimb*/;}), }); 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 eb6ef545b..b2be1d7fc 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/deku_tree.cpp @@ -149,7 +149,7 @@ void RegionTable_Init_DekuTree() { //Exits Entrance(RR_DEKU_TREE_BASEMENT_TORCH_ROOM, []{return true;}), Entrance(RR_DEKU_TREE_BASEMENT_BACK_ROOM, []{return AnyAgeTime([]{return logic->HasFireSourceWithTorch() || logic->CanUse(RG_FAIRY_BOW);}) && AnyAgeTime([]{return logic->BlastOrSmash();});}), - Entrance(RR_DEKU_TREE_BASEMENT_UPPER, []{return AnyAgeTime([]{return logic->HasFireSourceWithTorch() || logic->CanUse(RG_FAIRY_BOW);}) && logic->IsChild;}), + Entrance(RR_DEKU_TREE_BASEMENT_UPPER, []{return AnyAgeTime([]{return logic->HasFireSourceWithTorch() || logic->CanUse(RG_FAIRY_BOW);}) && logic->CanUse(RG_CRAWL);}), }); areaTable[RR_DEKU_TREE_BASEMENT_BACK_ROOM] = Region("Deku Tree Basement Back Room", SCENE_DEKU_TREE, {}, { @@ -168,7 +168,7 @@ void RegionTable_Init_DekuTree() { }, {}, { //Exits Entrance(RR_DEKU_TREE_BASEMENT_LOWER, []{return true;}), - Entrance(RR_DEKU_TREE_BASEMENT_BACK_LOBBY, []{return logic->IsChild;}), + Entrance(RR_DEKU_TREE_BASEMENT_BACK_LOBBY, []{return logic->CanUse(RG_CRAWL);}), Entrance(RR_DEKU_TREE_OUTSIDE_BOSS_ROOM, []{return AnyAgeTime([]{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_DEKU_B1_BOW_WEBS) && logic->IsAdult && logic->CanUse(RG_FAIRY_BOW));}) && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS));}), }); @@ -397,7 +397,7 @@ void RegionTable_Init_DekuTree() { LOCATION(RC_DEKU_TREE_MQ_BASEMENT_GRAVES_GRASS_5, logic->CanCutShrubs()), }, { //Exits - Entrance(RR_DEKU_TREE_MQ_BASEMENT_LEDGE, []{return logic->IsChild && AnyAgeTime([]{return logic->HasFireSourceWithTorch() || logic->CanUse(RG_FAIRY_BOW);});}), + Entrance(RR_DEKU_TREE_MQ_BASEMENT_LEDGE, []{return logic->CanUse(RG_CRAWL) && AnyAgeTime([]{return logic->HasFireSourceWithTorch() || logic->CanUse(RG_FAIRY_BOW);});}), Entrance(RR_DEKU_TREE_MQ_BASEMENT_SOUTHWEST_ROOM, []{return true;}), //Using a bow to get past here as adult is a bit precise on standing position but simple, doing as as child requires a side-hop with the bow out to shoot through the torch and may be trick worthy Entrance(RR_DEKU_TREE_MQ_BASEMENT_BACK_ROOM, []{return AnyAgeTime([]{return logic->HasFireSourceWithTorch() || logic->CanUse(RG_FAIRY_BOW);});}), @@ -425,7 +425,7 @@ void RegionTable_Init_DekuTree() { LOCATION(RC_DEKU_TREE_MQ_BASEMENT_UPPER_GRASS_3, logic->CanCutShrubs()), }, { //Exits - Entrance(RR_DEKU_TREE_MQ_BASEMENT_GRAVE_ROOM, []{return logic->IsChild;}), + Entrance(RR_DEKU_TREE_MQ_BASEMENT_GRAVE_ROOM, []{return logic->CanUse(RG_CRAWL);}), Entrance(RR_DEKU_TREE_MQ_BASEMENT, []{return true;}), //If strength 0 is shuffled, add hovers or block push to the stick check //recoiling to skip swim is possible, but would be a trick 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 75a816306..4f37e3346 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -28,7 +28,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_SIDE_HUB, []{return logic->IsChild/*CanUse(RG_CRAWL)*/;}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_SIDE_HUB, []{return logic->CanUse(RG_CRAWL);}), Entrance(RR_SPIRIT_TEMPLE_ADULT_SIDE_HUB, []{return logic->CanUse(RG_SILVER_GAUNTLETS);}), }); @@ -37,8 +37,8 @@ void RegionTable_Init_SpiritTemple() { EventAccess(LOGIC_NUT_ACCESS, []{return logic->CanBreakSmallCrates();}), }, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_FOYER, []{return logic->IsChild/*CanUse(RG_CRAWL)*/;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_BOXES, []{return logic->IsChild/*CanUse(RG_CRAWL)*/;}), + Entrance(RR_SPIRIT_TEMPLE_FOYER, []{return logic->CanUse(RG_CRAWL);}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_BOXES, []{return logic->CanUse(RG_CRAWL);}), //Implies logic->CanKillEnemy(RE_KEESE) Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_ARMOS);});}), Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_ARMOS);});}), @@ -109,7 +109,7 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_BEFORE_CHILD_CLIMB_SMALL_CRATE_2, logic->CanBreakSmallCrates()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_SIDE_HUB, []{return logic->IsChild/*CanUse(RG_CRAWL)*/;}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_SIDE_HUB, []{return logic->CanUse(RG_CRAWL);}), Entrance(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR_2F, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 1);}), }); @@ -223,7 +223,7 @@ void RegionTable_Init_SpiritTemple() { //they will be able to exit the dungeon through the intended entrance and vice versa //for needing to open the west hand lock to block the intended child route Entrance(RR_DESERT_COLOSSUS, []{return ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && /*str0 &&*/ - logic->IsChild/*CanUse(RG_CRAWL)*/ && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 4) && logic->CanKillEnemy(RE_IRON_KNUCKLE);}), + logic->CanUse(RG_CRAWL) && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 4) && logic->CanKillEnemy(RE_IRON_KNUCKLE);}), }); areaTable[RR_SPIRIT_TEMPLE_INNER_WEST_HAND] = Region("Spirit Temple Inner West Hand", SCENE_SPIRIT_TEMPLE, {}, { @@ -564,7 +564,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_CHILD_SIDE_HUB, []{return logic->IsChild/*logic->CanUse(RG_CRAWL) && logic->HasSoul(RG_NABOORU_SOUL)*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_CHILD_SIDE_HUB, []{return logic->CanUse(RG_CRAWL) /*&& logic->HasSoul(RG_NABOORU_SOUL)*/;}), Entrance(RR_SPIRIT_TEMPLE_MQ_BEHIND_GEYSER, []{return ctx->GetTrickOption(RT_HOVER_BOOST_SIMPLE) && logic->CanUse(RG_HOVER_BOOTS) && (logic->CanUse(RG_MEGATON_HAMMER) || (logic->CanStandingShield() && (logic->CanUseSword() || logic->CanUse(RG_STICKS))));}), Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCKS_HOLE, []{return logic->CanUse(RG_LONGSHOT) && logic->CanUse(RG_BOMBCHU_5);}), }); @@ -582,10 +582,10 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits //Nabooru's legs are technically visible one way collision here, but I'm not sure if this counts - Entrance(RR_SPIRIT_TEMPLE_MQ_FOYER, []{return logic->IsChild/*logic->CanUse(RG_CRAWL)*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FOYER, []{return logic->CanUse(RG_CRAWL);}), Entrance(RR_SPIRIT_TEMPLE_MQ_GIBDO_GRAVES, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_TORCH_SLUG);});}), Entrance(RR_SPIRIT_TEMPLE_MQ_ANUBIS_BRIDGE_CHEST, []{return AnyAgeTime([]{return logic->CanKillEnemy(RE_TORCH_SLUG);});}), - Entrance(RR_SPIRIT_TEMPLE_MQ_1F_CHEST_SWITCH, []{return logic->IsChild/*logic->CanUse(RG_CRAWL)*/ && logic->Get(LOGIC_SPIRIT_MQ_CRAWL_BOULDER);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_1F_CHEST_SWITCH, []{return logic->CanUse(RG_CRAWL) && logic->Get(LOGIC_SPIRIT_MQ_CRAWL_BOULDER);}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_GIBDO_GRAVES] = Region("Spirit Temple MQ Gibdo Graves", SCENE_SPIRIT_TEMPLE, { @@ -667,7 +667,7 @@ void RegionTable_Init_SpiritTemple() { EventAccess(LOGIC_SPIRIT_MQ_CRAWL_BOULDER, []{return logic->CanUse(RG_BOMBCHU_5) || (ctx->GetTrickOption(RT_RUSTED_SWITCHES) && logic->CanUse(RG_MEGATON_HAMMER));}), }, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_CHILD_SIDE_HUB, []{return logic->IsChild/*logic->CanUse(RG_CRAWL)*/ && logic->Get(LOGIC_SPIRIT_MQ_CRAWL_BOULDER);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_CHILD_SIDE_HUB, []{return logic->CanUse(RG_CRAWL) && logic->Get(LOGIC_SPIRIT_MQ_CRAWL_BOULDER);}), //This tracks possible child access, if adult has not entered STATUE_ROOM. Certain Child Access is checked for separately as 7 Keys Entrance(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 1);}), }); @@ -706,7 +706,7 @@ void RegionTable_Init_SpiritTemple() { //This is because with 6 keys it becomes impossible to avoid opening either the west hand lock or the first child side lock //and either direction lets child reach colossus. CanHitSwitch and CanKillEnemy(RE_IRON_KNUCKLE) is implied. //Logic can then allow child back into spirit, putting 1F west in logic with only 6 keys without forwards entry - Entrance(RR_DESERT_COLOSSUS, []{return logic->IsChild/*CanUse(RG_CRAWL)*/ && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && + Entrance(RR_DESERT_COLOSSUS, []{return logic->CanUse(RG_CRAWL) && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 6) && logic->MQSpiritStatueToSunBlock() && (logic->CanUse(RG_BOMBCHU_5) || (ctx->GetTrickOption(RT_RUSTED_SWITCHES) && AnyAgeTime([]{return logic->CanUse(RG_MEGATON_HAMMER);})));}), Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, []{return logic->SmallKeys(SCENE_SPIRIT_TEMPLE, 6);}), 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 6ec8114a0..2afda0fa8 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/castle_grounds.cpp @@ -45,18 +45,24 @@ void RegionTable_Init_CastleGrounds() { }, { //Exits Entrance(RR_CASTLE_GROUNDS, []{return true;}), - Entrance(RR_HC_GARDEN, []{return logic->CanUse(RG_WEIRD_EGG) || (ctx->GetTrickOption(RT_DAMAGE_BOOST_SIMPLE) && logic->HasExplosives() && logic->CanJumpslash());}), - Entrance(RR_HC_GREAT_FAIRY_FOUNTAIN, []{return logic->BlastOrSmash();}), + Entrance(RR_HC_LEDGE, []{return logic->CanUse(RG_WEIRD_EGG) || (ctx->GetTrickOption(RT_DAMAGE_BOOST_SIMPLE) && logic->HasExplosives() && logic->CanJumpslash());}), + Entrance(RR_HC_GREAT_FAIRY_FOUNTAIN, []{return logic->CanUse(RG_CRAWL) && logic->BlastOrSmash();}), Entrance(RR_HC_STORMS_GROTTO, []{return logic->CanOpenStormsGrotto();}), }); + areaTable[RR_HC_LEDGE] = Region("HC Ledge", SCENE_HYRULE_CASTLE, {}, {}, { + //Exits + Entrance(RR_HYRULE_CASTLE_GROUNDS, []{return true;}), + Entrance(RR_HC_GARDEN, []{return logic->CanUse(RG_CRAWL);}), + }); + areaTable[RR_HC_GARDEN] = Region("HC Garden", SCENE_CASTLE_COURTYARD_ZELDA, {}, { //Locations LOCATION(RC_HC_ZELDAS_LETTER, true), LOCATION(RC_SONG_FROM_IMPA, true), }, { //Exits - Entrance(RR_HYRULE_CASTLE_GROUNDS, []{return true;}), + Entrance(RR_HC_LEDGE, []{return true;}), // if this ever gets shuffled leaving garden area should come out crawlspace }); areaTable[RR_HC_GREAT_FAIRY_FOUNTAIN] = Region("HC Great Fairy Fountain", SCENE_GREAT_FAIRYS_FOUNTAIN_SPELLS, {}, { 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 df36eb190..0588ea0c2 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp @@ -11,7 +11,6 @@ void RegionTable_Init_KokiriForest() { EventAccess(LOGIC_SHOWED_MIDO_SWORD_AND_SHIELD, []{return logic->IsChild && logic->CanUse(RG_KOKIRI_SWORD) && logic->CanUse(RG_DEKU_SHIELD);}), }, { //Locations - LOCATION(RC_KF_KOKIRI_SWORD_CHEST, logic->IsChild), 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() && @@ -28,8 +27,6 @@ void RegionTable_Init_KokiriForest() { 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_BOULDER_RUPEE_1, logic->IsChild), - LOCATION(RC_KF_BOULDER_RUPEE_2, 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))), @@ -53,9 +50,6 @@ void RegionTable_Init_KokiriForest() { 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_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_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()), @@ -78,6 +72,7 @@ void RegionTable_Init_KokiriForest() { LOCATION(RC_KF_ADULT_GRASS_20, logic->IsAdult && logic->CanCutShrubs()), }, { //Exits + Entrance(RR_KF_BOULDER_LOOP, []{return logic->CanUse(RG_CRAWL);}), Entrance(RR_KF_LINKS_HOUSE, []{return true;}), Entrance(RR_KF_MIDOS_HOUSE, []{return true;}), Entrance(RR_KF_SARIAS_HOUSE, []{return true;}), @@ -90,6 +85,19 @@ void RegionTable_Init_KokiriForest() { Entrance(RR_KF_STORMS_GROTTO, []{return logic->CanOpenStormsGrotto();}), }); + areaTable[RR_KF_BOULDER_LOOP] = Region("KF Boulder Loop", SCENE_KOKIRI_FOREST, {}, { + //Locations + LOCATION(RC_KF_KOKIRI_SWORD_CHEST, logic->IsChild), + 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()), + }, { + //Exits + Entrance(RR_KOKIRI_FOREST, []{return logic->CanUse(RG_CRAWL);}), + }); + areaTable[RR_KF_OUTSIDE_DEKU_TREE] = Region("KF Outside Deku Tree", SCENE_KOKIRI_FOREST, { //Events EventAccess(LOGIC_STICK_ACCESS, []{return logic->CanGetDekuBabaSticks();}), 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 e40f514fa..daff4ed38 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 @@ -56,7 +56,7 @@ void RegionTable_Init_LonLonRanch() { areaTable[RR_LLR_TOWER] = Region("LLR Tower", SCENE_LON_LON_BUILDINGS, {}, { //Locations - LOCATION(RC_LLR_FREESTANDING_POH, logic->IsChild), + LOCATION(RC_LLR_FREESTANDING_POH, logic->IsChild && logic->HasItem(RG_CRAWL)), LOCATION(RC_LLR_TOWER_LEFT_COW, logic->CanUse(RG_EPONAS_SONG)), LOCATION(RC_LLR_TOWER_RIGHT_COW, logic->CanUse(RG_EPONAS_SONG)), }, { diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 0d3045c98..c27918aec 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -227,6 +227,8 @@ bool Logic::HasItem(RandomizerGet itemName) { return CurrentUpgrade(UPG_SCALE) >= 1; case RG_GOLDEN_SCALE: return CurrentUpgrade(UPG_SCALE) >= 2; + case RG_CRAWL: + return CheckRandoInf(RAND_INF_CAN_CRAWL); case RG_POCKET_EGG: return CheckRandoInf(RAND_INF_ADULT_TRADES_HAS_POCKET_EGG); case RG_COJIRO: @@ -377,6 +379,8 @@ bool Logic::CanUse(RandomizerGet itemName) { // Misc. Items case RG_FISHING_POLE: return HasItem(RG_CHILD_WALLET); // as long as you have enough rubies + case RG_CRAWL: + return IsChild; // Bottle Items case RG_BOTTLE_WITH_BUGS: @@ -1713,6 +1717,9 @@ void Logic::ApplyItemEffect(Item& item, bool state) { case RG_CLAIM_CHECK: SetRandoInf(randoGet - RG_COJIRO + RAND_INF_ADULT_TRADES_HAS_COJIRO, state); break; + case RG_CRAWL: + SetRandoInf(RAND_INF_CAN_CRAWL, state); + break; case RG_PROGRESSIVE_HOOKSHOT: { uint8_t i; for (i = 0; i < 3; i++) { @@ -2576,6 +2583,11 @@ void Logic::Reset(bool resetSaveContext /*= true*/) { SetRandoInf(RAND_INF_CAN_SWIM, true); } + // If we're not shuffling crawl, we start with it + if (ctx->GetOption(RSK_SHUFFLE_CRAWL).Is(false)) { + SetRandoInf(RAND_INF_CAN_CRAWL, true); + } + // If we're not shuffling child's wallet, we start with it if (ctx->GetOption(RSK_SHUFFLE_CHILD_WALLET).Is(false)) { SetRandoInf(RAND_INF_HAS_WALLET, true); diff --git a/soh/soh/Enhancements/randomizer/option_descriptions.cpp b/soh/soh/Enhancements/randomizer/option_descriptions.cpp index be68728a5..c200135a5 100644 --- a/soh/soh/Enhancements/randomizer/option_descriptions.cpp +++ b/soh/soh/Enhancements/randomizer/option_descriptions.cpp @@ -256,6 +256,7 @@ void Settings::CreateOptionDescriptions() { "\n" "If you enter a water entrance without swim you will be respawned on land to prevent infinite death loops.\n" "If you void out in Water Temple you will immediately be kicked out to prevent a softlock."; + mOptionDescriptions[RSK_SHUFFLE_CRAWL] = "Shuffles the ability to use crawlspaces into the item pool."; mOptionDescriptions[RSK_SHUFFLE_WEIRD_EGG] = "Shuffles the Weird Egg from Malon in to the item pool. Enabling " "\"Skip Child Zelda\" disables this feature.\n" "\n" diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index c2e60dd5b..c346cd6cb 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -4945,7 +4945,7 @@ CustomMessage Randomizer::GetGoronMessage(u16 index) { void Randomizer::CreateCustomMessages() { // RANDTODO: Translate into french and german and replace GIMESSAGE_UNTRANSLATED // with GIMESSAGE(getItemID, itemID, english, german, french). - const std::array getItemMessages = { { + const std::array getItemMessages = { { GIMESSAGE(RG_GREG_RUPEE, ITEM_MASK_GORON, "You found %gGreg%w!", "%gGreg%w! Du hast ihn&wirklich gefunden!", "Félicitation! Vous avez trouvé %gGreg%w!"), GIMESSAGE(RG_MASTER_SWORD, ITEM_SWORD_MASTER, "You found the %gMaster Sword%w!", @@ -5290,6 +5290,9 @@ void Randomizer::CreateCustomMessages() { GIMESSAGE(RG_BRONZE_SCALE, ITEM_SCALE_SILVER, "You got the %rBronze Scale%w!&The power of buoyancy is yours!", "Du hast die %rBronzene Schuppe%w&erhalten! Die Fähigkeit zu&Schwimmen ist nun dein!", "Vous obtenez l'%rÉcaille de Bronze%w!&Le pouvoir de la flottabilité est&à vous!"), + GIMESSAGE_NO_GERMAN(RG_CRAWL, ITEM_SCALE_SILVER, // TODO_TRANSLATE + "You got the %rAbility to Crawl%w!&The power of kneecaps is yours!", + "Vous obtenez la %rCapacité à Ramper%w!"), GIMESSAGE(RG_FISHING_POLE, ITEM_FISHING_POLE, "You found a lost %rFishing Pole%w!&Time to hit the pond!", "Du hast eine verlorene %rAngelrute%w&gefunden!&Zeit, im Teich&zu angeln!", "Vous obtenez une %rCanne à pêche%w&perdue!&Il est temps d'aller à %gl'étang%w!"), @@ -5454,6 +5457,7 @@ extern "C" u8 Return_Item_Entry(GetItemEntry itemEntry, u8 returnItem); std::map randomizerGetToRandInf = { { RG_FISHING_POLE, RAND_INF_FISHING_POLE_FOUND }, { RG_BRONZE_SCALE, RAND_INF_CAN_SWIM }, + { RG_CRAWL, RAND_INF_CAN_CRAWL }, { RG_QUIVER_INF, RAND_INF_HAS_INFINITE_QUIVER }, { RG_BOMB_BAG_INF, RAND_INF_HAS_INFINITE_BOMB_BAG }, { RG_BULLET_BAG_INF, RAND_INF_HAS_INFINITE_BULLET_BAG }, diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 693419946..c1e4b5af8 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -530,6 +530,7 @@ typedef enum { RR_NOCTURNE_OF_SHADOW_WARP, RR_PRELUDE_OF_LIGHT_WARP, RR_KOKIRI_FOREST, + RR_KF_BOULDER_LOOP, RR_KF_LINKS_HOUSE, RR_KF_MIDOS_HOUSE, RR_KF_SARIAS_HOUSE, @@ -636,6 +637,7 @@ typedef enum { RR_CASTLE_GROUNDS, RR_CASTLE_GROUNDS_FROM_GANONS_CASTLE, RR_HYRULE_CASTLE_GROUNDS, + RR_HC_LEDGE, RR_HC_GARDEN, RR_HC_GREAT_FAIRY_FOUNTAIN, RR_HC_STORMS_GROTTO, @@ -4603,6 +4605,7 @@ typedef enum { RG_HINT, RG_TYCOON_WALLET, RG_BRONZE_SCALE, + RG_CRAWL, RG_CHILD_WALLET, RG_PROGRESSIVE_BOMBCHU_BAG, RG_QUIVER_INF, @@ -5809,6 +5812,7 @@ typedef enum { RHT_OCARINA_C_LEFT_BUTTON, RHT_OCARINA_C_RIGHT_BUTTON, RHT_BRONZE_SCALE, + RHT_CRAWL, RHT_FISHING_POLE, RHT_SKELETON_KEY, RHT_EPONA, @@ -6405,6 +6409,7 @@ typedef enum { RSK_SHUFFLE_OCARINA, RSK_SHUFFLE_OCARINA_BUTTONS, RSK_SHUFFLE_SWIM, + RSK_SHUFFLE_CRAWL, RSK_STARTING_DEKU_SHIELD, RSK_STARTING_KOKIRI_SWORD, RSK_STARTING_MASTER_SWORD, diff --git a/soh/soh/Enhancements/randomizer/randomizer_inf.h b/soh/soh/Enhancements/randomizer/randomizer_inf.h index f15f7c1d1..b0eae9342 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_inf.h +++ b/soh/soh/Enhancements/randomizer/randomizer_inf.h @@ -1139,6 +1139,7 @@ DEFINE_RAND_INF(RAND_INF_ZF_BUSH_6) DEFINE_RAND_INF(RAND_INF_CAUGHT_LOACH) DEFINE_RAND_INF(RAND_INF_CAN_SWIM) +DEFINE_RAND_INF(RAND_INF_CAN_CRAWL) DEFINE_RAND_INF(RAND_INF_HAS_WALLET) diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index f130f10a3..dc35afdfa 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -313,6 +313,10 @@ extern "C" void Randomizer_InitSaveFile() { Flags_SetRandomizerInf(RAND_INF_CAN_SWIM); } + if (Randomizer_GetSettingValue(RSK_SHUFFLE_CRAWL) == RO_GENERIC_OFF) { + Flags_SetRandomizerInf(RAND_INF_CAN_CRAWL); + } + if (Randomizer_GetSettingValue(RSK_SHUFFLE_CHILD_WALLET) == RO_GENERIC_OFF) { Flags_SetRandomizerInf(RAND_INF_HAS_WALLET); } diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index e78d0b743..8652bb5ef 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -778,6 +778,7 @@ void Settings::CreateOptions() { }); OPT_BOOL(RSK_SHUFFLE_OCARINA_BUTTONS, "Shuffle Ocarina Buttons", CVAR_RANDOMIZER_SETTING("ShuffleOcarinaButtons"), mOptionDescriptions[RSK_SHUFFLE_OCARINA_BUTTONS]); OPT_BOOL(RSK_SHUFFLE_SWIM, "Shuffle Swim", CVAR_RANDOMIZER_SETTING("ShuffleSwim"), mOptionDescriptions[RSK_SHUFFLE_SWIM]); + OPT_BOOL(RSK_SHUFFLE_CRAWL, "Shuffle Crawl", CVAR_RANDOMIZER_SETTING("ShuffleCrawl"), mOptionDescriptions[RSK_SHUFFLE_CRAWL]); OPT_BOOL(RSK_SHUFFLE_WEIRD_EGG, "Shuffle Weird Egg", CVAR_RANDOMIZER_SETTING("ShuffleWeirdEgg"), mOptionDescriptions[RSK_SHUFFLE_WEIRD_EGG]); OPT_BOOL(RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD, "Shuffle Gerudo Membership Card", CVAR_RANDOMIZER_SETTING("ShuffleGerudoToken"), mOptionDescriptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD]); OPT_U8(RSK_SHUFFLE_POTS, "Shuffle Pots", {"Off", "Dungeons", "Overworld", "All Pots"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShufflePots"), mOptionDescriptions[RSK_SHUFFLE_POTS], WIDGET_CVAR_COMBOBOX, RO_SHUFFLE_POTS_OFF); @@ -2388,6 +2389,7 @@ void Settings::CreateOptions() { &mOptions[RSK_SHUFFLE_DEKU_NUT_BAG], &mOptions[RSK_SHUFFLE_OCARINA_BUTTONS], &mOptions[RSK_SHUFFLE_SWIM], + &mOptions[RSK_SHUFFLE_CRAWL], &mOptions[RSK_SHUFFLE_BEAN_SOULS], &mOptions[RSK_ROCS_FEATHER], &mOptions[RSK_BOMBCHU_BAG], @@ -2605,6 +2607,7 @@ void Settings::CreateOptions() { &mOptions[RSK_SHUFFLE_OCARINA], &mOptions[RSK_SHUFFLE_OCARINA_BUTTONS], &mOptions[RSK_SHUFFLE_SWIM], + &mOptions[RSK_SHUFFLE_CRAWL], &mOptions[RSK_SHUFFLE_WEIRD_EGG], &mOptions[RSK_SHUFFLE_GERUDO_MEMBERSHIP_CARD], &mOptions[RSK_SHUFFLE_MERCHANTS], 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 433ae8c2c..025aeb976 100644 --- a/soh/src/overlays/actors/ovl_player_actor/z_player.c +++ b/soh/src/overlays/actors/ovl_player_actor/z_player.c @@ -7633,6 +7633,10 @@ s32 Player_TryEnteringCrawlspace(Player* this, PlayState* play, u32 interactWall s32 i; if (!LINK_IS_ADULT && !(this->stateFlags1 & PLAYER_STATE1_IN_WATER) && (interactWallFlags & 0x30)) { + if (!GameInteractor_Should(VB_CRAWL, true)) { + return false; + } + wallPoly = this->actor.wallPoly; CollisionPoly_GetVerticesByBgId(wallPoly, this->actor.wallBgId, &play->colCtx, wallVertices);