From d41273f34be3acee2f4d6046ad885b9dbc3e8ae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= Date: Wed, 31 Dec 2025 01:27:37 +0000 Subject: [PATCH] Shuffle Bean Souls (#5833) --- .../3drando/hint_list/hint_list_item.cpp | 2 + .../randomizer/3drando/item_pool.cpp | 22 +++- soh/soh/Enhancements/randomizer/draw.cpp | 12 ++ soh/soh/Enhancements/randomizer/draw.h | 1 + .../Enhancements/randomizer/hook_handlers.cpp | 68 ++++++++++ soh/soh/Enhancements/randomizer/item_list.cpp | 21 +++ .../randomizer/location_access.cpp | 43 ++++--- .../Enhancements/randomizer/location_access.h | 4 +- .../overworld/death_mountain_crater.cpp | 16 +-- .../overworld/death_mountain_trail.cpp | 16 +-- .../overworld/desert_colossus.cpp | 12 +- .../overworld/gerudo_valley.cpp | 12 +- .../location_access/overworld/graveyard.cpp | 12 +- .../overworld/kokiri_forest.cpp | 24 ++-- .../location_access/overworld/lake_hylia.cpp | 14 +- .../location_access/overworld/lost_woods.cpp | 24 ++-- .../location_access/overworld/zoras_river.cpp | 8 +- soh/soh/Enhancements/randomizer/logic.cpp | 59 ++++++++- soh/soh/Enhancements/randomizer/logic.h | 2 +- .../randomizer/option_descriptions.cpp | 4 + .../Enhancements/randomizer/randomizer.cpp | 45 ++++++- .../Enhancements/randomizer/randomizerTypes.h | 15 +++ .../Enhancements/randomizer/randomizer_inf.h | 11 ++ .../randomizer/randomizer_item_tracker.cpp | 120 ++++++++++++++++++ soh/soh/Enhancements/randomizer/savefile.cpp | 35 +++++ soh/soh/Enhancements/randomizer/settings.cpp | 10 +- soh/soh/OTRGlobals.cpp | 18 ++- 27 files changed, 523 insertions(+), 107 deletions(-) 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 c74c0ff23..4962aeac2 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_item.cpp @@ -1964,6 +1964,8 @@ void StaticData::HintTable_Init_Item() { CustomMessage("a gold fragment", /*german*/"ein Goldfragment", /*french*/"un fragment d'or")}); // /*spanish*/un fragmento dorado + hintTextTable[RHT_BEAN_SOUL] = HintText(CustomMessage("a bean soul", /*german*/"eine bohnenseele", /*french*/"une âme de haricot")); + hintTextTable[RHT_GOHMA_SOUL] = HintText(CustomMessage("the soul of Gohma", /*german*/"Gohmas Seele", /*french*/"l'Âme de Gohma"), { CustomMessage("something webbed", /*german*/"etwas Verwobenes", /*french*/"un truc entoilé") diff --git a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp index 8dc10b94f..e9260e02f 100644 --- a/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/item_pool.cpp @@ -662,15 +662,16 @@ void GenerateItemPool() { } } - if (ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_BEANS_ONLY) || - ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL)) { + // if beans unshuffled, put on bean guy, otherwise if not starting with beans, add to pool + if (ctx->GetOption(RSK_SHUFFLE_MERCHANTS).IsNot(RO_SHUFFLE_MERCHANTS_BEANS_ONLY) && + ctx->GetOption(RSK_SHUFFLE_MERCHANTS).IsNot(RO_SHUFFLE_MERCHANTS_ALL)) { + ctx->PlaceItemInLocation(RC_ZR_MAGIC_BEAN_SALESMAN, RG_MAGIC_BEAN, false, true); + } else if (!ctx->GetOption(RSK_STARTING_BEANS)) { AddItemToMainPool(RG_MAGIC_BEAN_PACK); if (ctx->GetOption(RSK_ITEM_POOL).Is(RO_ITEM_POOL_PLENTIFUL)) { AddItemToPool(PendingJunkPool, RG_MAGIC_BEAN_PACK); } ctx->possibleIceTrapModels.push_back(RG_MAGIC_BEAN_PACK); - } else { - ctx->PlaceItemInLocation(RC_ZR_MAGIC_BEAN_SALESMAN, RG_MAGIC_BEAN, false, true); } if (ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL_BUT_BEANS) || @@ -752,6 +753,19 @@ void GenerateItemPool() { ctx->PlaceItemInLocation(RC_KAK_100_GOLD_SKULLTULA_REWARD, RG_HUGE_RUPEE, false, true); } + if (ctx->GetOption(RSK_SHUFFLE_BEAN_SOULS)) { + AddItemToMainPool(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL); + AddItemToMainPool(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL); + AddItemToMainPool(RG_DESERT_COLOSSUS_BEAN_SOUL); + AddItemToMainPool(RG_GERUDO_VALLEY_BEAN_SOUL); + AddItemToMainPool(RG_GRAVEYARD_BEAN_SOUL); + AddItemToMainPool(RG_KOKIRI_FOREST_BEAN_SOUL); + AddItemToMainPool(RG_LAKE_HYLIA_BEAN_SOUL); + AddItemToMainPool(RG_LOST_WOODS_BRIDGE_BEAN_SOUL); + AddItemToMainPool(RG_LOST_WOODS_BEAN_SOUL); + AddItemToMainPool(RG_ZORAS_RIVER_BEAN_SOUL); + } + if (ctx->GetOption(RSK_SHUFFLE_BOSS_SOULS)) { AddItemToMainPool(RG_GOHMA_SOUL); AddItemToMainPool(RG_KING_DODONGO_SOUL); diff --git a/soh/soh/Enhancements/randomizer/draw.cpp b/soh/soh/Enhancements/randomizer/draw.cpp index 7d266d78b..7774125bc 100644 --- a/soh/soh/Enhancements/randomizer/draw.cpp +++ b/soh/soh/Enhancements/randomizer/draw.cpp @@ -28,6 +28,7 @@ extern "C" { #include "objects/object_bv/object_bv.h" #include "objects/object_gnd/object_gnd.h" #include "objects/object_fd/object_fd.h" +#include "objects/object_mamenoki/object_mamenoki.h" #include "objects/object_mo/object_mo.h" #include "objects/object_sst/object_sst.h" #include "overlays/actors/ovl_Boss_Goma/z_boss_goma.h" @@ -929,6 +930,17 @@ extern "C" void DrawGanon(PlayState* play) { CLOSE_DISPS(play->state.gfxCtx); } +extern "C" void Randomizer_DrawBeanSprout(PlayState* play, GetItemEntry* getItemEntry) { + OPEN_DISPS(play->state.gfxCtx); + + Gfx_SetupDL_25Opa(play->state.gfxCtx); + Matrix_Scale(0.3f, 0.3f, 0.3f, MTXMODE_APPLY); + gSPMatrix(POLY_OPA_DISP++, MATRIX_NEWMTX(play->state.gfxCtx), G_MTX_MODELVIEW | G_MTX_LOAD); + gSPDisplayList(POLY_OPA_DISP++, (Gfx*)gMagicBeanSeedlingDL); + + CLOSE_DISPS(play->state.gfxCtx); +} + extern "C" void Randomizer_DrawBossSoul(PlayState* play, GetItemEntry* getItemEntry) { s16 slot; if (getItemEntry->getItemId != RG_ICE_TRAP) { diff --git a/soh/soh/Enhancements/randomizer/draw.h b/soh/soh/Enhancements/randomizer/draw.h index 8483a7bf9..3830fdceb 100644 --- a/soh/soh/Enhancements/randomizer/draw.h +++ b/soh/soh/Enhancements/randomizer/draw.h @@ -14,6 +14,7 @@ void Randomizer_DrawMap(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawCompass(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawKeyRing(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawBossKey(PlayState* play, GetItemEntry* getItemEntry); +void Randomizer_DrawBeanSprout(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawBossSoul(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawDoubleDefense(PlayState* play, GetItemEntry* getItemEntry); void Randomizer_DrawMasterSword(PlayState* play, GetItemEntry* getItemEntry); diff --git a/soh/soh/Enhancements/randomizer/hook_handlers.cpp b/soh/soh/Enhancements/randomizer/hook_handlers.cpp index faedf2fd7..7b4cd7645 100644 --- a/soh/soh/Enhancements/randomizer/hook_handlers.cpp +++ b/soh/soh/Enhancements/randomizer/hook_handlers.cpp @@ -55,11 +55,13 @@ extern "C" { #include "src/overlays/actors/ovl_En_Xc/z_en_xc.h" #include "src/overlays/actors/ovl_Fishing/z_fishing.h" #include "src/overlays/actors/ovl_En_Mk/z_en_mk.h" +#include "src/overlays/actors/ovl_Obj_Bean/z_obj_bean.h" #include "draw.h" extern SaveContext gSaveContext; extern PlayState* gPlayState; extern void func_8084DFAC(PlayState* play, Player* player); +extern void func_80B8FE00(ObjBean*); // trigger planting extern void Player_SetupActionPreserveAnimMovement(PlayState* play, Player* player, PlayerActionFunc actionFunc, s32 flags); extern s32 Player_SetupWaitForPutAway(PlayState* play, Player* player, AfterPutAwayFunc func); @@ -933,6 +935,8 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l RAND_GET_OPTION(RSK_SHUFFLE_MERCHANTS) == RO_SHUFFLE_MERCHANTS_ALL) { *should = gSaveContext.rupees >= OTRGlobals::Instance->gRandoContext->GetItemLocation(RC_ZR_MAGIC_BEAN_SALESMAN)->GetPrice(); + } else if (RAND_GET_OPTION(RSK_SKIP_PLANTING_BEANS)) { + *should = gSaveContext.rupees >= 60; } break; } @@ -1268,6 +1272,27 @@ void RandomizerOnVanillaBehaviorHandler(GIVanillaBehavior id, bool* should, va_l Flags_SetRandomizerInf(RAND_INF_MERCHANTS_MAGIC_BEAN_SALESMAN); enMs->actionFunc = (EnMsActionFunc)EnMs_Wait; *should = false; + } else if (RAND_GET_OPTION(RSK_SKIP_PLANTING_BEANS)) { + Rupees_ChangeBy(-60); + Item_Give(NULL, ITEM_BEAN); + BEANS_BOUGHT = 10; + AMMO(ITEM_BEAN) = 0; + gSaveContext.sceneFlags[SCENE_DEATH_MOUNTAIN_CRATER].swch |= (1 << 3); + gSaveContext.sceneFlags[SCENE_DEATH_MOUNTAIN_TRAIL].swch |= (1 << 6); + gSaveContext.sceneFlags[SCENE_DESERT_COLOSSUS].swch |= (1 << 24); + gSaveContext.sceneFlags[SCENE_GERUDO_VALLEY].swch |= (1 << 3); + gSaveContext.sceneFlags[SCENE_GRAVEYARD].swch |= (1 << 3); + gSaveContext.sceneFlags[SCENE_KOKIRI_FOREST].swch |= (1 << 9); + gSaveContext.sceneFlags[SCENE_LAKE_HYLIA].swch |= (1 << 1); + gSaveContext.sceneFlags[SCENE_LOST_WOODS].swch |= (1 << 4) | (1 << 18); + gSaveContext.sceneFlags[SCENE_ZORAS_RIVER].swch |= (1 << 3); + ObjBean* bean = (ObjBean*)Actor_Find(&gPlayState->actorCtx, ACTOR_OBJ_BEAN, ACTORCAT_BG); + if (bean != nullptr) { + Flags_SetSwitch(gPlayState, bean->dyna.actor.params & 0x3F); + func_80B8FE00(bean); + } + enMs->actionFunc = (EnMsActionFunc)EnMs_Wait; + *should = false; } break; } @@ -2249,6 +2274,49 @@ void RandomizerOnActorInitHandler(void* actorRef) { return; } + if (RAND_GET_OPTION(RSK_SHUFFLE_BEAN_SOULS)) { + if (actor->id == ACTOR_OBJ_BEAN) { + RandomizerInf currentBeanSoulRandInf = RAND_INF_MAX; + switch (gPlayState->sceneNum) { + case SCENE_DEATH_MOUNTAIN_CRATER: + currentBeanSoulRandInf = RAND_INF_DEATH_MOUNTAIN_CRATER_BEAN_SOUL; + break; + case SCENE_DEATH_MOUNTAIN_TRAIL: + currentBeanSoulRandInf = RAND_INF_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL; + break; + case SCENE_DESERT_COLOSSUS: + currentBeanSoulRandInf = RAND_INF_DESERT_COLOSSUS_BEAN_SOUL; + break; + case SCENE_GERUDO_VALLEY: + currentBeanSoulRandInf = RAND_INF_GERUDO_VALLEY_BEAN_SOUL; + break; + case SCENE_GRAVEYARD: + currentBeanSoulRandInf = RAND_INF_GRAVEYARD_BEAN_SOUL; + break; + case SCENE_KOKIRI_FOREST: + currentBeanSoulRandInf = RAND_INF_KOKIRI_FOREST_BEAN_SOUL; + break; + case SCENE_LAKE_HYLIA: + currentBeanSoulRandInf = RAND_INF_LAKE_HYLIA_BEAN_SOUL; + break; + case SCENE_LOST_WOODS: + if ((actor->params & 0x3F) == 4) { + currentBeanSoulRandInf = RAND_INF_LOST_WOODS_BRIDGE_BEAN_SOUL; + } else { + currentBeanSoulRandInf = RAND_INF_LOST_WOODS_BEAN_SOUL; + } + break; + case SCENE_ZORAS_RIVER: + currentBeanSoulRandInf = RAND_INF_ZORAS_RIVER_BEAN_SOUL; + break; + } + if (currentBeanSoulRandInf != RAND_INF_MAX && !Flags_GetRandomizerInf(currentBeanSoulRandInf)) { + Actor_Kill(actor); + return; + } + } + } + // If child is in the adult shooting gallery or adult in the child shooting gallery, then despawn the shooting // gallery man if (actor->id == ACTOR_EN_SYATEKI_MAN && RAND_GET_OPTION(RSK_SHUFFLE_INTERIOR_ENTRANCES) && diff --git a/soh/soh/Enhancements/randomizer/item_list.cpp b/soh/soh/Enhancements/randomizer/item_list.cpp index 861923312..10c2eea91 100644 --- a/soh/soh/Enhancements/randomizer/item_list.cpp +++ b/soh/soh/Enhancements/randomizer/item_list.cpp @@ -316,6 +316,27 @@ void Rando::StaticData::InitItemTable() { itemTable[RG_BUY_RED_POTION_40] = Item(RG_BUY_RED_POTION_40, Text{ "Buy Red Potion [40]", "Acheter: Potion Rouge [40]", "Rotes Elixier kaufen [40]" }, ITEMTYPE_SHOP, GI_POTION_RED, false, LOGIC_NONE, RHT_BOTTLE_WITH_RED_POTION, ITEM_POTION_RED, OBJECT_GI_LIQUID, GID_POTION_RED, 0x43, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_JUNK, MOD_NONE, false, 40); itemTable[RG_BUY_RED_POTION_50] = Item(RG_BUY_RED_POTION_50, Text{ "Buy Red Potion [50]", "Acheter: Potion Rouge [50]", "Rotes Elixier kaufen [50]" }, ITEMTYPE_SHOP, GI_POTION_RED, false, LOGIC_NONE, RHT_BOTTLE_WITH_RED_POTION, ITEM_POTION_RED, OBJECT_GI_LIQUID, GID_POTION_RED, 0x43, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_JUNK, MOD_NONE, false, 50); // Misc. + itemTable[RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL] = Item(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL, Text{ "Death Mountain Crater Bean Soul" }, ITEMTYPE_ITEM, 0xE0, true, LOGIC_NONE, RHT_BEAN_SOUL, RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL, OBJECT_GI_BEAN, GID_BEAN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL].SetCustomDrawFunc(Randomizer_DrawBeanSprout); + itemTable[RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL] = Item(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL, Text{ "Death Mountain Trail Bean Soul" }, ITEMTYPE_ITEM, 0xE0, true, LOGIC_NONE, RHT_BEAN_SOUL, RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL, OBJECT_GI_BEAN, GID_BEAN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL].SetCustomDrawFunc(Randomizer_DrawBeanSprout); + itemTable[RG_DESERT_COLOSSUS_BEAN_SOUL] = Item(RG_DESERT_COLOSSUS_BEAN_SOUL, Text{ "Desert Colossus Bean Soul" }, ITEMTYPE_ITEM, 0xE0, true, LOGIC_NONE, RHT_BEAN_SOUL, RG_DESERT_COLOSSUS_BEAN_SOUL, OBJECT_GI_BEAN, GID_BEAN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_DESERT_COLOSSUS_BEAN_SOUL].SetCustomDrawFunc(Randomizer_DrawBeanSprout); + itemTable[RG_GERUDO_VALLEY_BEAN_SOUL] = Item(RG_GERUDO_VALLEY_BEAN_SOUL, Text{ "Gerudo Valley Bean Soul" }, ITEMTYPE_ITEM, 0xE0, true, LOGIC_NONE, RHT_BEAN_SOUL, RG_GERUDO_VALLEY_BEAN_SOUL, OBJECT_GI_BEAN, GID_BEAN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_GERUDO_VALLEY_BEAN_SOUL].SetCustomDrawFunc(Randomizer_DrawBeanSprout); + itemTable[RG_GRAVEYARD_BEAN_SOUL] = Item(RG_GRAVEYARD_BEAN_SOUL, Text{ "Graveyard Bean Soul" }, ITEMTYPE_ITEM, 0xE0, true, LOGIC_NONE, RHT_BEAN_SOUL, RG_GRAVEYARD_BEAN_SOUL, OBJECT_GI_BEAN, GID_BEAN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_GRAVEYARD_BEAN_SOUL].SetCustomDrawFunc(Randomizer_DrawBeanSprout); + itemTable[RG_KOKIRI_FOREST_BEAN_SOUL] = Item(RG_KOKIRI_FOREST_BEAN_SOUL, Text{ "Kokiri Forest Bean Soul" }, ITEMTYPE_ITEM, 0xE0, true, LOGIC_NONE, RHT_BEAN_SOUL, RG_KOKIRI_FOREST_BEAN_SOUL, OBJECT_GI_BEAN, GID_BEAN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_KOKIRI_FOREST_BEAN_SOUL].SetCustomDrawFunc(Randomizer_DrawBeanSprout); + itemTable[RG_LAKE_HYLIA_BEAN_SOUL] = Item(RG_LAKE_HYLIA_BEAN_SOUL, Text{ "Lake Hylia Bean Soul" }, ITEMTYPE_ITEM, 0xE0, true, LOGIC_NONE, RHT_BEAN_SOUL, RG_LAKE_HYLIA_BEAN_SOUL, OBJECT_GI_BEAN, GID_BEAN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_LAKE_HYLIA_BEAN_SOUL].SetCustomDrawFunc(Randomizer_DrawBeanSprout); + itemTable[RG_LOST_WOODS_BRIDGE_BEAN_SOUL] = Item(RG_LOST_WOODS_BRIDGE_BEAN_SOUL, Text{ "Lost Woods Bridge Bean Soul" }, ITEMTYPE_ITEM, 0xE0, true, LOGIC_NONE, RHT_BEAN_SOUL, RG_LOST_WOODS_BRIDGE_BEAN_SOUL, OBJECT_GI_BEAN, GID_BEAN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_LOST_WOODS_BRIDGE_BEAN_SOUL].SetCustomDrawFunc(Randomizer_DrawBeanSprout); + itemTable[RG_LOST_WOODS_BEAN_SOUL] = Item(RG_LOST_WOODS_BEAN_SOUL, Text{ "Lost Woods Bean Soul" }, ITEMTYPE_ITEM, 0xE0, true, LOGIC_NONE, RHT_BEAN_SOUL, RG_LOST_WOODS_BEAN_SOUL, OBJECT_GI_BEAN, GID_BEAN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_LOST_WOODS_BEAN_SOUL].SetCustomDrawFunc(Randomizer_DrawBeanSprout); + itemTable[RG_ZORAS_RIVER_BEAN_SOUL] = Item(RG_ZORAS_RIVER_BEAN_SOUL, Text{ "Zora's River Bean Soul" }, ITEMTYPE_ITEM, 0xE0, true, LOGIC_NONE, RHT_BEAN_SOUL, RG_ZORAS_RIVER_BEAN_SOUL, OBJECT_GI_BEAN, GID_SKULL_TOKEN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); + itemTable[RG_ZORAS_RIVER_BEAN_SOUL].SetCustomDrawFunc(Randomizer_DrawBeanSprout); + itemTable[RG_GOHMA_SOUL] = Item(RG_GOHMA_SOUL, Text{ "Gohma's Soul", "Âme de Gohma", "Gohmas Seele" }, ITEMTYPE_ITEM, 0xE0, true, LOGIC_CAN_SUMMON_GOHMA, RHT_GOHMA_SOUL, RG_GOHMA_SOUL, OBJECT_GI_SUTARU, GID_SKULL_TOKEN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); itemTable[RG_GOHMA_SOUL].SetCustomDrawFunc(Randomizer_DrawBossSoul); itemTable[RG_KING_DODONGO_SOUL] = Item(RG_KING_DODONGO_SOUL, Text{ "King Dodongo's Soul", "Âme du Roi Dodongo", "König Dodongos Seele" }, ITEMTYPE_ITEM, 0xE1, true, LOGIC_CAN_SUMMON_KINGDODONGO, RHT_KING_DODONGO_SOUL, RG_KING_DODONGO_SOUL, OBJECT_GI_SUTARU, GID_SKULL_TOKEN, TEXT_RANDOMIZER_CUSTOM_ITEM, 0x80, CHEST_ANIM_LONG, ITEM_CATEGORY_MAJOR, MOD_RANDOMIZER); diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index e3430c5a9..7cab9fdb7 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -505,8 +505,11 @@ Rando::Entrance* Region::GetExit(RandomizerRegion exitToReturn) { return nullptr; } -bool Region::CanPlantBeanCheck() const { - return Rando::Context::GetInstance()->GetLogic()->GetAmmo(ITEM_BEAN) > 0 && BothAgesCheck(); +bool Region::CanPlantBeanCheck(RandomizerGet bean) const { + auto ctx = Rando::Context::GetInstance(); + auto logic = ctx->GetLogic(); + return logic->HasItem(bean) && logic->GetAmmo(ITEM_BEAN) > 0 && + (ctx->GetOption(RSK_SKIP_PLANTING_BEANS) || BothAgesCheck()); } bool Region::AllAccountedFor() const { @@ -675,49 +678,55 @@ bool MQSpiritSharedBrokenWallRoom(const RandomizerRegion region, ConditionFn con return areaTable[region].MQSpiritShared(condition, true, anyAge); } -bool BeanPlanted(const RandomizerRegion region) { +bool BeanPlanted(const RandomizerGet bean) { + auto logic = Rando::Context::GetInstance()->GetLogic(); + // flag irrelevant if plant won't spawn + if (!logic->HasItem(bean)) { + return false; + } + // swchFlag found using the Actor Viewer to get the Obj_Bean parameters & 0x3F // not tested with multiple OTRs, but can be automated similarly to GetDungeonSmallKeyDoors SceneID sceneID; uint8_t swchFlag; - switch (region) { - case RR_ZORAS_RIVER: + switch (bean) { + case RG_ZORAS_RIVER_BEAN_SOUL: sceneID = SceneID::SCENE_ZORAS_RIVER; swchFlag = 3; break; - case RR_THE_GRAVEYARD: + case RG_GRAVEYARD_BEAN_SOUL: sceneID = SceneID::SCENE_GRAVEYARD; swchFlag = 3; break; - case RR_KOKIRI_FOREST: + case RG_KOKIRI_FOREST_BEAN_SOUL: sceneID = SceneID::SCENE_KOKIRI_FOREST; swchFlag = 9; break; - case RR_THE_LOST_WOODS: + case RG_LOST_WOODS_BRIDGE_BEAN_SOUL: sceneID = SceneID::SCENE_LOST_WOODS; swchFlag = 4; break; - case RR_LW_BEYOND_MIDO: + case RG_LOST_WOODS_BEAN_SOUL: sceneID = SceneID::SCENE_LOST_WOODS; swchFlag = 18; break; - case RR_DEATH_MOUNTAIN_TRAIL: + case RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL: sceneID = SceneID::SCENE_DEATH_MOUNTAIN_TRAIL; swchFlag = 6; break; - case RR_LAKE_HYLIA: + case RG_LAKE_HYLIA_BEAN_SOUL: sceneID = SceneID::SCENE_LAKE_HYLIA; swchFlag = 1; break; - case RR_GERUDO_VALLEY: + case RG_GERUDO_VALLEY_BEAN_SOUL: sceneID = SceneID::SCENE_GERUDO_VALLEY; swchFlag = 3; break; - case RR_DMC_CENTRAL_LOCAL: + case RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL: sceneID = SceneID::SCENE_DEATH_MOUNTAIN_CRATER; swchFlag = 3; break; - case RR_DESERT_COLOSSUS: + case RG_DESERT_COLOSSUS_BEAN_SOUL: sceneID = SceneID::SCENE_DESERT_COLOSSUS; swchFlag = 24; break; @@ -733,7 +742,7 @@ bool BeanPlanted(const RandomizerRegion region) { if (gPlayState != nullptr && gPlayState->sceneNum == sceneID) { swch = gPlayState->actorCtx.flags.swch; } else if (sceneID != SCENE_ID_MAX) { - swch = Rando::Context::GetInstance()->GetLogic()->GetSaveContext()->sceneFlags[sceneID].swch; + swch = logic->GetSaveContext()->sceneFlags[sceneID].swch; } else { swch = 0; } @@ -741,8 +750,8 @@ bool BeanPlanted(const RandomizerRegion region) { return swch >> swchFlag & 1; } -bool CanPlantBean(const RandomizerRegion region) { - return areaTable[region].CanPlantBeanCheck() || BeanPlanted(region); +bool CanPlantBean(const RandomizerRegion region, const RandomizerGet bean) { + return areaTable[region].CanPlantBeanCheck(bean) || BeanPlanted(bean); } bool BothAges(const RandomizerRegion region) { diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 26f540aa2..fcb1f6131 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -207,7 +207,7 @@ class Region { return hereVal; } - bool CanPlantBeanCheck() const; + bool CanPlantBeanCheck(RandomizerGet bean) const; bool AllAccountedFor() const; bool MQSpiritShared(ConditionFn condition, bool IsBrokenWall, bool anyAge = false); @@ -224,7 +224,7 @@ bool Here(const RandomizerRegion region, condition); // RANDOTODO make a less stupid way to check own at either age than self referencing with this bool MQSpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge = false); bool MQSpiritSharedBrokenWallRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge = false); -bool CanPlantBean(const RandomizerRegion region); +bool CanPlantBean(const RandomizerRegion region, RandomizerGet bean); bool BothAges(const RandomizerRegion region); bool ChildCanAccess(const RandomizerRegion region); bool AdultCanAccess(const RandomizerRegion region); 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 61b345911..907d209ef 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 @@ -66,7 +66,7 @@ void RegionTable_Init_DeathMountainCrater() { areaTable[RR_DMC_CENTRAL_NEARBY] = Region("DMC Central Nearby", SCENE_DEATH_MOUNTAIN_CRATER, {}, { //Locations - LOCATION(RC_DMC_VOLCANO_FREESTANDING_POH, logic->IsAdult && logic->Hearts() >= 3 && (CanPlantBean(RR_DMC_CENTRAL_LOCAL) || (ctx->GetTrickOption(RT_DMC_HOVER_BEAN_POH) && logic->CanUse(RG_HOVER_BOOTS)))), + LOCATION(RC_DMC_VOLCANO_FREESTANDING_POH, logic->IsAdult && logic->Hearts() >= 3 && (CanPlantBean(RR_DMC_CENTRAL_LOCAL, RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL) || (ctx->GetTrickOption(RT_DMC_HOVER_BEAN_POH) && logic->CanUse(RG_HOVER_BOOTS)))), LOCATION(RC_SHEIK_IN_CRATER, logic->IsAdult && (logic->FireTimer() >= 8 || logic->Hearts() >= 3)), }, { //Exits @@ -75,10 +75,10 @@ void RegionTable_Init_DeathMountainCrater() { areaTable[RR_DMC_CENTRAL_LOCAL] = Region("DMC Central Local", SCENE_DEATH_MOUNTAIN_CRATER, { //Events - EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS) && (logic->FireTimer() >= 8 || logic->Hearts() >= 3);}), + EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS) && (logic->FireTimer() >= 8 || logic->Hearts() >= 3);}), }, { //Locations - LOCATION(RC_DMC_GS_BEAN_PATCH, (logic->FireTimer() >= 8 || logic->Hearts() >= 3) && logic->CanSpawnSoilSkull() && logic->CanAttack()), + LOCATION(RC_DMC_GS_BEAN_PATCH, (logic->FireTimer() >= 8 || logic->Hearts() >= 3) && logic->CanSpawnSoilSkull(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL) && logic->CanAttack()), LOCATION(RC_DMC_NEAR_PLATFORM_RED_RUPEE, logic->IsChild), LOCATION(RC_DMC_MIDDLE_PLATFORM_RED_RUPEE, logic->IsChild && (logic->FireTimer() >= 8 || logic->Hearts() >= 3)), LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_1, logic->IsChild && (logic->FireTimer() >= 8 || logic->Hearts() >= 3)), @@ -87,14 +87,14 @@ void RegionTable_Init_DeathMountainCrater() { LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_4, logic->IsChild && (logic->FireTimer() >= 8 || logic->Hearts() >= 3)), LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_5, logic->IsChild && (logic->FireTimer() >= 8 || logic->Hearts() >= 3)), LOCATION(RC_DMC_MIDDLE_PLATFORM_BLUE_RUPEE_6, logic->IsChild && (logic->FireTimer() >= 8 || logic->Hearts() >= 3)), - LOCATION(RC_DMC_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS) && (logic->FireTimer() >= 8 || logic->Hearts() >= 3)), - LOCATION(RC_DMC_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS) && (logic->FireTimer() >= 8 || logic->Hearts() >= 3)), - LOCATION(RC_DMC_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS) && (logic->FireTimer() >= 8 || logic->Hearts() >= 3)), + 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) && (logic->FireTimer() >= 8 || logic->Hearts() >= 3)), + 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) && (logic->FireTimer() >= 8 || logic->Hearts() >= 3)), + 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) && (logic->FireTimer() >= 8 || logic->Hearts() >= 3)), }, { //Exits Entrance(RR_DMC_CENTRAL_NEARBY, []{return true;}), - Entrance(RR_DMC_LOWER_NEARBY, []{return (logic->IsAdult && CanPlantBean(RR_DMC_CENTRAL_LOCAL)) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT);}), - Entrance(RR_DMC_UPPER_NEARBY, []{return logic->IsAdult && CanPlantBean(RR_DMC_CENTRAL_LOCAL);}), + Entrance(RR_DMC_LOWER_NEARBY, []{return (logic->IsAdult && CanPlantBean(RR_DMC_CENTRAL_LOCAL, RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL)) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_DMC_UPPER_NEARBY, []{return logic->IsAdult && CanPlantBean(RR_DMC_CENTRAL_LOCAL, RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL);}), Entrance(RR_FIRE_TEMPLE_ENTRYWAY, []{return (logic->IsChild && logic->Hearts() >= 3 && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).IsNot(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF)) || (logic->IsAdult && logic->FireTimer() >= 24);}), Entrance(RR_DMC_DISTANT_PLATFORM, []{return logic->FireTimer() >= 48 && logic->CanUse(RG_DISTANT_SCARECROW);}), }); 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 e3d262b17..1691053ab 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 @@ -7,25 +7,25 @@ void RegionTable_Init_DeathMountainTrail() { // clang-format off areaTable[RR_DEATH_MOUNTAIN_TRAIL] = Region("Death Mountain", SCENE_DEATH_MOUNTAIN_TRAIL, { //Events - EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS) && (logic->HasExplosives() || logic->HasItem(RG_GORONS_BRACELET));}), + EventAccess(LOGIC_FAIRY_ACCESS, []{return 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))), - LOCATION(RC_DMT_FREESTANDING_POH, logic->TakeDamage() || logic->CanUse(RG_HOVER_BOOTS) || (logic->IsAdult && CanPlantBean(RR_DEATH_MOUNTAIN_TRAIL) && (logic->HasExplosives() || logic->HasItem(RG_GORONS_BRACELET)))), - LOCATION(RC_DMT_GS_BEAN_PATCH, logic->CanSpawnSoilSkull() && (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_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()), - LOCATION(RC_DMT_GS_ABOVE_DODONGOS_CAVERN, logic->IsAdult && logic->AtNight && (logic->CanUse(RG_MEGATON_HAMMER) || (ctx->GetTrickOption(RT_HOOKSHOT_EXTENSION) && logic->CanUse(RG_HOOKSHOT)) || (ctx->GetTrickOption(RT_DMT_BEAN_LOWER_GS) && CanPlantBean(RR_DEATH_MOUNTAIN_TRAIL)) || (ctx->GetTrickOption(RT_DMT_HOVERS_LOWER_GS) && logic->CanUse(RG_HOVER_BOOTS)) || (ctx->GetTrickOption(RT_DMT_JS_LOWER_GS) && logic->CanJumpslash())) && logic->CanGetNightTimeGS()), + LOCATION(RC_DMT_GS_ABOVE_DODONGOS_CAVERN, logic->IsAdult && logic->AtNight && (logic->CanUse(RG_MEGATON_HAMMER) || (ctx->GetTrickOption(RT_HOOKSHOT_EXTENSION) && logic->CanUse(RG_HOOKSHOT)) || (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)) || (ctx->GetTrickOption(RT_DMT_JS_LOWER_GS) && logic->CanJumpslash())) && logic->CanGetNightTimeGS()), 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->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->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->CanUse(RG_SONG_OF_STORMS) && (logic->HasExplosives() || logic->HasItem(RG_GORONS_BRACELET))), + 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)), }, { //Exits Entrance(RR_KAK_BEHIND_GATE, []{return true;}), Entrance(RR_GORON_CITY, []{return true;}), - Entrance(RR_DEATH_MOUNTAIN_SUMMIT, []{return Here(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->BlastOrSmash();}) || (logic->IsAdult && ((CanPlantBean(RR_DEATH_MOUNTAIN_TRAIL) && logic->HasItem(RG_GORONS_BRACELET)) || (logic->CanUse(RG_HOVER_BOOTS) && ctx->GetTrickOption(RT_DMT_CLIMB_HOVERS))));}), + Entrance(RR_DEATH_MOUNTAIN_SUMMIT, []{return Here(RR_DEATH_MOUNTAIN_TRAIL, []{return logic->BlastOrSmash();}) || (logic->IsAdult && ((CanPlantBean(RR_DEATH_MOUNTAIN_TRAIL, RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL) && logic->HasItem(RG_GORONS_BRACELET)) || (logic->CanUse(RG_HOVER_BOOTS) && ctx->GetTrickOption(RT_DMT_CLIMB_HOVERS))));}), Entrance(RR_DODONGOS_CAVERN_ENTRYWAY, []{return logic->HasExplosives() || logic->HasItem(RG_GORONS_BRACELET) || logic->IsAdult;}), Entrance(RR_DMT_STORMS_GROTTO, []{return logic->CanOpenStormsGrotto();}), }); 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 5ffb8ac8d..f948fe22d 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/desert_colossus.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/desert_colossus.cpp @@ -10,13 +10,13 @@ void RegionTable_Init_DesertColossus() { EventAccess(LOGIC_BUG_ACCESS, []{return true;}), }, { //Locations - LOCATION(RC_COLOSSUS_FREESTANDING_POH, logic->IsAdult && CanPlantBean(RR_DESERT_COLOSSUS)), - LOCATION(RC_COLOSSUS_GS_BEAN_PATCH, logic->CanSpawnSoilSkull() && logic->CanAttack()), + 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) && 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->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_COLOSSUS_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_COLOSSUS_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), + 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), 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 f83aa8e99..7ed1ae42b 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_valley.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/gerudo_valley.cpp @@ -23,15 +23,15 @@ void RegionTable_Init_GerudoValley() { areaTable[RR_GV_UPPER_STREAM] = Region("GV Upper Stream", SCENE_GERUDO_VALLEY, { //Events - EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->CallGossipFairy() || (logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS));}), + EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->CallGossipFairy() || (logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_GERUDO_VALLEY_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS));}), }, { //Locations - LOCATION(RC_GV_WATERFALL_FREESTANDING_POH, logic->IsChild || logic->HasItem(RG_BRONZE_SCALE)),//can use cucco as child - LOCATION(RC_GV_GS_BEAN_PATCH, logic->CanSpawnSoilSkull() && logic->CanAttack()), + LOCATION(RC_GV_WATERFALL_FREESTANDING_POH, logic->IsChild || logic->HasItem(RG_BRONZE_SCALE) || CanPlantBean(RR_GV_UPPER_STREAM, RG_GERUDO_VALLEY_BEAN_SOUL)),//can use cucco as child + LOCATION(RC_GV_GS_BEAN_PATCH, logic->CanSpawnSoilSkull(RG_GERUDO_VALLEY_BEAN_SOUL) && logic->CanAttack()), LOCATION(RC_GV_COW, logic->IsChild && logic->CanUse(RG_EPONAS_SONG)), - LOCATION(RC_GV_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_GV_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_GV_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_GV_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_GERUDO_VALLEY_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_GV_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_GERUDO_VALLEY_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_GV_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_GERUDO_VALLEY_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_GV_GOSSIP_STONE_FAIRY, logic->CallGossipFairy()), LOCATION(RC_GV_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_GV_GOSSIP_STONE, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp index ff1b90467..3c1eb42e2 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/graveyard.cpp @@ -12,13 +12,13 @@ void RegionTable_Init_Graveyard() { EventAccess(LOGIC_BORROW_BUNNY_HOOD, []{return logic->IsChild && logic->AtDay && logic->Get(LOGIC_BORROW_SPOOKY_MASK) && logic->HasItem(RG_CHILD_WALLET);}), }, { //Locations - LOCATION(RC_GRAVEYARD_FREESTANDING_POH, (((logic->IsAdult && CanPlantBean(RR_THE_GRAVEYARD)) || logic->CanUse(RG_LONGSHOT)) && logic->CanBreakCrates()) || (ctx->GetTrickOption(RT_GY_POH) && logic->CanUse(RG_BOOMERANG))), + LOCATION(RC_GRAVEYARD_FREESTANDING_POH, (((logic->IsAdult && CanPlantBean(RR_THE_GRAVEYARD, RG_GRAVEYARD_BEAN_SOUL)) || logic->CanUse(RG_LONGSHOT)) && logic->CanBreakCrates()) || (ctx->GetTrickOption(RT_GY_POH) && logic->CanUse(RG_BOOMERANG))), LOCATION(RC_GRAVEYARD_DAMPE_GRAVEDIGGING_TOUR, logic->HasItem(RG_CHILD_WALLET) && logic->IsChild && logic->AtNight), //TODO: This needs to change LOCATION(RC_GRAVEYARD_GS_WALL, logic->IsChild && logic->HookshotOrBoomerang() && logic->AtNight && logic->CanGetNightTimeGS()), - LOCATION(RC_GRAVEYARD_GS_BEAN_PATCH, logic->CanSpawnSoilSkull() && logic->CanAttack()), - LOCATION(RC_GRAVEYARD_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_GRAVEYARD_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_GRAVEYARD_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_GRAVEYARD_GS_BEAN_PATCH, logic->CanSpawnSoilSkull(RG_GRAVEYARD_BEAN_SOUL) && logic->CanAttack()), + LOCATION(RC_GRAVEYARD_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_GRAVEYARD_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_GRAVEYARD_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_GRAVEYARD_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_GRAVEYARD_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_GRAVEYARD_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_GY_GRASS_1, logic->CanCutShrubs()), LOCATION(RC_GY_GRASS_2, logic->CanCutShrubs()), LOCATION(RC_GY_GRASS_3, logic->CanCutShrubs()), @@ -31,7 +31,7 @@ void RegionTable_Init_Graveyard() { LOCATION(RC_GY_GRASS_10, logic->CanCutShrubs()), LOCATION(RC_GY_GRASS_11, logic->CanCutShrubs()), LOCATION(RC_GY_GRASS_12, logic->CanCutShrubs()), - LOCATION(RC_GRAVEYARD_CRATE, ((logic->IsAdult && CanPlantBean(RR_THE_GRAVEYARD)) || logic->CanUse(RG_LONGSHOT)) && logic->CanBreakCrates()), + LOCATION(RC_GRAVEYARD_CRATE, ((logic->IsAdult && CanPlantBean(RR_THE_GRAVEYARD, RG_GRAVEYARD_BEAN_SOUL)) || logic->CanUse(RG_LONGSHOT)) && logic->CanBreakCrates()), }, { //Exits Entrance(RR_GRAVEYARD_SHIELD_GRAVE, []{return logic->IsAdult || logic->AtNight;}), 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 4e73fa2bc..c0fac1702 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp @@ -7,17 +7,17 @@ void RegionTable_Init_KokiriForest() { // clang-format off areaTable[RR_KOKIRI_FOREST] = Region("Kokiri Forest", SCENE_KOKIRI_FOREST, { //Events - EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->CallGossipFairyExceptSuns() || (logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS));}), + EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->CallGossipFairyExceptSuns() || (logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_KOKIRI_FOREST_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS));}), 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->CanAttack() && logic->CanGetNightTimeGS()), - LOCATION(RC_KF_GS_BEAN_PATCH, logic->CanSpawnSoilSkull() && logic->CanAttack()), + LOCATION(RC_KF_GS_BEAN_PATCH, logic->CanSpawnSoilSkull(RG_KOKIRI_FOREST_BEAN_SOUL) && logic->CanAttack()), LOCATION(RC_KF_GS_HOUSE_OF_TWINS, logic->IsAdult && (logic->HookshotOrBoomerang() || (ctx->GetTrickOption(RT_KF_ADULT_GS) && logic->CanUse(RG_HOVER_BOOTS))) && logic->CanGetNightTimeGS()), - LOCATION(RC_KF_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_KF_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_KF_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), + 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_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), LOCATION(RC_KF_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_KF_BRIDGE_RUPEE, logic->IsChild), @@ -28,13 +28,13 @@ void RegionTable_Init_KokiriForest() { 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) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_KF_BEAN_RUPEE_2, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_KF_BEAN_RUPEE_3, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_KF_BEAN_RUPEE_4, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_KF_BEAN_RUPEE_5, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_KF_BEAN_RUPEE_6, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), - LOCATION(RC_KF_BEAN_RED_RUPEE, logic->IsAdult && (CanPlantBean(RR_KOKIRI_FOREST) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_BOOMERANG))), + 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), 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 76ca529e7..ad114cbe9 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp @@ -7,7 +7,7 @@ void RegionTable_Init_LakeHylia() { // clang-format off areaTable[RR_LAKE_HYLIA] = Region("Lake Hylia", SCENE_LAKE_HYLIA, { //Events - EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->CallGossipFairy() || logic->CanUse(RG_STICKS) || (logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS));}), + EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->CallGossipFairy() || logic->CanUse(RG_STICKS) || (logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_LAKE_HYLIA_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS));}), EventAccess(LOGIC_BUG_ACCESS, []{return logic->IsChild && logic->CanCutShrubs();}), EventAccess(LOGIC_CHILD_SCARECROW, []{return logic->IsChild && logic->HasItem(RG_FAIRY_OCARINA) && logic->OcarinaButtons() >= 2;}), EventAccess(LOGIC_ADULT_SCARECROW, []{return logic->IsAdult && logic->HasItem(RG_FAIRY_OCARINA) && logic->OcarinaButtons() >= 2;}), @@ -15,17 +15,17 @@ void RegionTable_Init_LakeHylia() { //Locations LOCATION(RC_LH_UNDERWATER_ITEM, logic->IsChild && logic->HasItem(RG_SILVER_SCALE)), LOCATION(RC_LH_SUN, logic->IsAdult && ((logic->Get(LOGIC_WATER_TEMPLE_CLEAR) && logic->HasItem(RG_BRONZE_SCALE)) || logic->CanUse(RG_DISTANT_SCARECROW)) && logic->CanUse(RG_FAIRY_BOW)), - LOCATION(RC_LH_FREESTANDING_POH, logic->IsAdult && (logic->CanUse(RG_SCARECROW) || CanPlantBean(RR_LAKE_HYLIA))), - LOCATION(RC_LH_GS_BEAN_PATCH, logic->CanSpawnSoilSkull() && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)), + LOCATION(RC_LH_FREESTANDING_POH, logic->IsAdult && (logic->CanUse(RG_SCARECROW) || CanPlantBean(RR_LAKE_HYLIA, RG_LAKE_HYLIA_BEAN_SOUL)) && logic->CanAvoidEnemy(RE_GUAY, false)), + LOCATION(RC_LH_GS_BEAN_PATCH, logic->CanSpawnSoilSkull(RG_LAKE_HYLIA_BEAN_SOUL) && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)), LOCATION(RC_LH_GS_LAB_WALL, logic->IsChild && (logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG) || (ctx->GetTrickOption(RT_LH_LAB_WALL_GS) && logic->CanJumpslashExceptHammer())) && logic->CanGetNightTimeGS()), LOCATION(RC_LH_GS_SMALL_ISLAND, logic->IsChild && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA) && logic->CanGetNightTimeGS() && logic->HasItem(RG_BRONZE_SCALE)), LOCATION(RC_LH_GS_TREE, logic->IsAdult && logic->CanUse(RG_LONGSHOT) && logic->CanGetNightTimeGS()), LOCATION(RC_LH_FRONT_RUPEE, logic->IsChild && logic->HasItem(RG_BRONZE_SCALE)), LOCATION(RC_LH_MIDDLE_RUPEE, logic->IsChild && (logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS))), LOCATION(RC_LH_BACK_RUPEE, logic->IsChild && (logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS))), - LOCATION(RC_LH_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_LH_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_LH_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_LH_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_LAKE_HYLIA_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_LH_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_LAKE_HYLIA_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_LH_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_LAKE_HYLIA_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_LH_LAB_GOSSIP_STONE_FAIRY, logic->CallGossipFairy()), LOCATION(RC_LH_LAB_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), //You can walk along the edge of the lake to get these without swimming, the fairy is created going backwards, which is convenient here @@ -84,7 +84,7 @@ void RegionTable_Init_LakeHylia() { Entrance(RR_HYRULE_FIELD, []{return true;}), Entrance(RR_LH_FROM_SHORTCUT, []{return true;}), Entrance(RR_LH_OWL_FLIGHT, []{return logic->IsChild;}), - Entrance(RR_LH_FISHING_ISLAND, []{return ((logic->IsChild || logic->Get(LOGIC_WATER_TEMPLE_CLEAR)) && logic->HasItem(RG_BRONZE_SCALE)) || (logic->IsAdult && (logic->CanUse(RG_SCARECROW) || CanPlantBean(RR_LAKE_HYLIA)));}), + Entrance(RR_LH_FISHING_ISLAND, []{return ((logic->IsChild || logic->Get(LOGIC_WATER_TEMPLE_CLEAR)) && logic->HasItem(RG_BRONZE_SCALE)) || (logic->IsAdult && (logic->CanUse(RG_SCARECROW) || CanPlantBean(RR_LAKE_HYLIA, RG_LAKE_HYLIA_BEAN_SOUL)));}), Entrance(RR_LH_LAB, []{return logic->CanOpenOverworldDoor(RG_HYLIA_LAB_KEY);}), Entrance(RR_LH_FROM_WATER_TEMPLE, []{return true;}), Entrance(RR_LH_GROTTO, []{return 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 a14fed0d7..5aaa5ccd2 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lost_woods.cpp @@ -12,7 +12,7 @@ void RegionTable_Init_LostWoods() { areaTable[RR_THE_LOST_WOODS] = Region("Lost Woods", SCENE_LOST_WOODS, { //Events - EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->CallGossipFairyExceptSuns() || (logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS));}), + EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->CallGossipFairyExceptSuns() || (logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_LOST_WOODS_BRIDGE_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS));}), EventAccess(LOGIC_BUG_ACCESS, []{return logic->IsChild && logic->CanCutShrubs();}), EventAccess(LOGIC_BORROW_SPOOKY_MASK, []{return logic->IsChild && logic->Get(LOGIC_BORROW_SKULL_MASK) && logic->CanUse(RG_SARIAS_SONG) && logic->HasItem(RG_CHILD_WALLET);}), }, { @@ -32,7 +32,7 @@ void RegionTable_Init_LostWoods() { LOCATION(RC_LW_OCARINA_MEMORY_GAME, logic->IsChild && logic->HasItem(RG_FAIRY_OCARINA) && logic->OcarinaButtons() >= 5), LOCATION(RC_LW_TARGET_IN_WOODS, logic->IsChild && logic->CanUse(RG_FAIRY_SLINGSHOT)), LOCATION(RC_LW_DEKU_SCRUB_NEAR_BRIDGE, logic->IsChild && logic->CanStunDeku() && GetCheckPrice() <= GetWalletCapacity()), - LOCATION(RC_LW_GS_BEAN_PATCH_NEAR_BRIDGE, logic->CanSpawnSoilSkull() && logic->CanAttack()), + LOCATION(RC_LW_GS_BEAN_PATCH_NEAR_BRIDGE, logic->CanSpawnSoilSkull(RG_LOST_WOODS_BRIDGE_BEAN_SOUL) && logic->CanAttack()), //RANDOTODO handle collecting some of these as you leave the shortcut from the other side LOCATION(RC_LW_SHORTCUT_RUPEE_1, logic->IsChild && (logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS))), LOCATION(RC_LW_SHORTCUT_RUPEE_2, logic->IsChild && (logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS))), @@ -42,9 +42,9 @@ void RegionTable_Init_LostWoods() { LOCATION(RC_LW_SHORTCUT_RUPEE_6, logic->IsChild && (logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS))), LOCATION(RC_LW_SHORTCUT_RUPEE_7, logic->IsChild && (logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS))), LOCATION(RC_LW_SHORTCUT_RUPEE_8, logic->IsChild && (logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS))), - LOCATION(RC_LW_BEAN_SPROUT_NEAR_BRIDGE_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_LW_BEAN_SPROUT_NEAR_BRIDGE_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_LW_BEAN_SPROUT_NEAR_BRIDGE_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_LW_BEAN_SPROUT_NEAR_BRIDGE_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_LOST_WOODS_BRIDGE_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_LW_BEAN_SPROUT_NEAR_BRIDGE_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_LOST_WOODS_BRIDGE_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_LW_BEAN_SPROUT_NEAR_BRIDGE_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_LOST_WOODS_BRIDGE_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_LW_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), LOCATION(RC_LW_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_LW_SHORTCUT_STORMS_FAIRY, logic->CanUse(RG_SONG_OF_STORMS)), @@ -56,7 +56,7 @@ void RegionTable_Init_LostWoods() { //Exits Entrance(RR_LW_FOREST_EXIT, []{return true;}), Entrance(RR_GC_WOODS_WARP, []{return true;}), - Entrance(RR_LW_BRIDGE, []{return (logic->IsAdult && (CanPlantBean(RR_THE_LOST_WOODS) || ctx->GetTrickOption(RT_LW_BRIDGE))) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_LW_BRIDGE, []{return (logic->IsAdult && (CanPlantBean(RR_THE_LOST_WOODS, RG_LOST_WOODS_BRIDGE_BEAN_SOUL) || ctx->GetTrickOption(RT_LW_BRIDGE))) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT);}), Entrance(RR_ZR_FROM_SHORTCUT, []{return logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS) || (ctx->GetTrickOption(RT_LOST_WOOD_NAVI_DIVE) && logic->IsChild && logic->HasItem(RG_BRONZE_SCALE) && logic->CanJumpslash());}), Entrance(RR_LW_BEYOND_MIDO, []{return logic->IsChild || logic->CanUse(RG_SARIAS_SONG) || ctx->GetTrickOption(RT_LW_MIDO_BACKFLIP);}), Entrance(RR_LW_NEAR_SHORTCUTS_GROTTO, []{return Here(RR_THE_LOST_WOODS, []{return logic->BlastOrSmash();});}), @@ -64,17 +64,17 @@ void RegionTable_Init_LostWoods() { areaTable[RR_LW_BEYOND_MIDO] = Region("LW Beyond Mido", SCENE_LOST_WOODS, { //Events - EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->CanUse(RG_STICKS);}), + EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->CanUse(RG_STICKS) || (logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_LOST_WOODS_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS));}), }, { //Locations LOCATION(RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_RIGHT, logic->IsChild && logic->CanStunDeku() && GetCheckPrice() <= GetWalletCapacity()), LOCATION(RC_LW_DEKU_SCRUB_NEAR_DEKU_THEATER_LEFT, logic->IsChild && logic->CanStunDeku() && GetCheckPrice() <= GetWalletCapacity()), - LOCATION(RC_LW_GS_ABOVE_THEATER, logic->IsAdult && ((CanPlantBean(RR_LW_BEYOND_MIDO) && logic->CanAttack()) || (ctx->GetTrickOption(RT_LW_GS_BEAN) && logic->CanUse(RG_HOOKSHOT) && (logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOMBCHU_5) || logic->CanUse(RG_DINS_FIRE)))) && logic->CanGetNightTimeGS()), - LOCATION(RC_LW_GS_BEAN_PATCH_NEAR_THEATER, logic->CanSpawnSoilSkull() && (logic->CanAttack() || (ctx->GetOption(RSK_SHUFFLE_SCRUBS).Is(RO_SCRUBS_OFF) && logic->CanReflectNuts()))), + LOCATION(RC_LW_GS_ABOVE_THEATER, logic->IsAdult && ((CanPlantBean(RR_LW_BEYOND_MIDO, RG_LOST_WOODS_BEAN_SOUL) && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)) || (ctx->GetTrickOption(RT_LW_GS_BEAN) && logic->CanUse(RG_HOOKSHOT) && (logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOMBCHU_5) || logic->CanUse(RG_DINS_FIRE)))) && logic->CanGetNightTimeGS()), + LOCATION(RC_LW_GS_BEAN_PATCH_NEAR_THEATER, logic->CanSpawnSoilSkull(RG_LOST_WOODS_BEAN_SOUL) && (logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA) || (ctx->GetOption(RSK_SHUFFLE_SCRUBS).Is(RO_SCRUBS_OFF) && logic->CanReflectNuts()))), LOCATION(RC_LW_BOULDER_RUPEE, logic->BlastOrSmash()), - LOCATION(RC_LW_BEAN_SPROUT_NEAR_THEATER_FAIRY_1, logic->IsChild && logic->HasItem(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_LW_BEAN_SPROUT_NEAR_THEATER_FAIRY_2, logic->IsChild && logic->HasItem(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_LW_BEAN_SPROUT_NEAR_THEATER_FAIRY_3, logic->IsChild && logic->HasItem(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_LW_BEAN_SPROUT_NEAR_THEATER_FAIRY_1, logic->IsChild && logic->HasItem(RG_MAGIC_BEAN) && logic->HasItem(RG_LOST_WOODS_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_LW_BEAN_SPROUT_NEAR_THEATER_FAIRY_2, logic->IsChild && logic->HasItem(RG_MAGIC_BEAN) && logic->HasItem(RG_LOST_WOODS_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_LW_BEAN_SPROUT_NEAR_THEATER_FAIRY_3, logic->IsChild && logic->HasItem(RG_MAGIC_BEAN) && logic->HasItem(RG_LOST_WOODS_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_LW_GRASS_4, logic->CanCutShrubs()), LOCATION(RC_LW_GRASS_5, logic->CanCutShrubs()), LOCATION(RC_LW_GRASS_6, logic->CanCutShrubs()), 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 642f51117..a3df2f78e 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_river.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/zoras_river.cpp @@ -29,7 +29,7 @@ void RegionTable_Init_ZoraRiver() { areaTable[RR_ZORAS_RIVER] = Region("Zora River", SCENE_ZORAS_RIVER, { //Events - EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->CallGossipFairy() || (logic->IsChild && logic->CanUse(RG_STICKS)) || (logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS));}), + EventAccess(LOGIC_FAIRY_ACCESS, []{return logic->CallGossipFairy() || (logic->IsChild && logic->CanUse(RG_STICKS)) || (logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_ZORAS_RIVER_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS));}), }, { //Locations LOCATION(RC_ZR_MAGIC_BEAN_SALESMAN, logic->IsChild && GetCheckPrice() <= GetWalletCapacity()/* && CanUse(SPEAK_HYLIAN)*/), @@ -45,9 +45,9 @@ void RegionTable_Init_ZoraRiver() { LOCATION(RC_ZR_GS_LADDER, logic->IsChild && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_SHORT_JUMPSLASH) && logic->CanGetNightTimeGS()), LOCATION(RC_ZR_GS_NEAR_RAISED_GROTTOS, logic->IsAdult && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_LONGSHOT) && logic->CanGetNightTimeGS()), LOCATION(RC_ZR_GS_ABOVE_BRIDGE, logic->IsAdult && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_HOOKSHOT) && logic->CanGetNightTimeGS()), - LOCATION(RC_ZR_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_ZR_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), - LOCATION(RC_ZR_BEAN_SPROUT_FAIRY_3, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_ZR_BEAN_SPROUT_FAIRY_1, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_ZORAS_RIVER_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + LOCATION(RC_ZR_BEAN_SPROUT_FAIRY_2, logic->IsChild && logic->CanUse(RG_MAGIC_BEAN) && logic->HasItem(RG_ZORAS_RIVER_BEAN_SOUL) && logic->CanUse(RG_SONG_OF_STORMS)), + 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))), diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index bb835c440..06f1e16ec 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -132,6 +132,17 @@ bool Logic::HasItem(RandomizerGet itemName) { case RG_OCARINA_C_RIGHT_BUTTON: case RG_OCARINA_C_DOWN_BUTTON: case RG_OCARINA_C_UP_BUTTON: + // Bean Souls + case RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL: + case RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL: + case RG_DESERT_COLOSSUS_BEAN_SOUL: + case RG_GERUDO_VALLEY_BEAN_SOUL: + case RG_GRAVEYARD_BEAN_SOUL: + case RG_KOKIRI_FOREST_BEAN_SOUL: + case RG_LAKE_HYLIA_BEAN_SOUL: + case RG_LOST_WOODS_BRIDGE_BEAN_SOUL: + case RG_LOST_WOODS_BEAN_SOUL: + case RG_ZORAS_RIVER_BEAN_SOUL: // Boss Souls case RG_GOHMA_SOUL: case RG_KING_DODONGO_SOUL: @@ -571,6 +582,7 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal return CanJumpslash() || HasExplosives() || CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_FAIRY_BOW); case RE_KEESE: case RE_FIRE_KEESE: + case RE_GUAY: switch (distance) { case ED_CLOSE: case ED_SHORT_JUMPSLASH: @@ -583,15 +595,15 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal killed = killed || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_STICKS); [[fallthrough]]; case ED_BOMB_THROW: - // RANDOTODO test dins and chu range in a practical example - killed = killed || (!inWater && CanUse(RG_BOMB_BAG)); + // RANDOTODO test chu range in a practical example + killed = killed || (!inWater && CanUse(RG_BOMB_BAG)) || (enemy == RE_GUAY && CanUse(RG_DINS_FIRE)); [[fallthrough]]; case ED_BOOMERANG: - // RANDOTODO test dins and chu range in a practical example + // RANDOTODO test chu range in a practical example killed = killed || CanUse(RG_BOOMERANG); [[fallthrough]]; case ED_HOOKSHOT: - // RANDOTODO test dins, bomb and chu range in a practical example + // RANDOTODO test chu range in a practical example killed = killed || CanUse(RG_HOOKSHOT) || (wallOrFloor && CanUse(RG_BOMBCHU_5)); [[fallthrough]]; case ED_LONGSHOT: @@ -912,6 +924,7 @@ bool Logic::CanAvoidEnemy(RandomizerEnemy enemy, bool grounded, uint8_t quantity return !grounded || CanUse(RG_NUTS); case RE_KEESE: case RE_FIRE_KEESE: + case RE_GUAY: return CanUse(RG_NUTS); case RE_BLUE_BUBBLE: // RANDOTODO Trick to use shield hylian shield as child to stun these guys @@ -956,6 +969,7 @@ bool Logic::CanGetEnemyDrop(RandomizerEnemy enemy, EnemyDistance distance, bool break; case RE_KEESE: case RE_FIRE_KEESE: + case RE_GUAY: return true; default: return aboveLink || (distance <= ED_BOOMERANG && CanUse(RG_BOOMERANG)); @@ -1173,8 +1187,8 @@ bool Logic::BlastOrSmash() { return HasExplosives() || CanUse(RG_MEGATON_HAMMER); } -bool Logic::CanSpawnSoilSkull() { - return IsChild && CanUse(RG_BOTTLE_WITH_BUGS); +bool Logic::CanSpawnSoilSkull(RandomizerGet bean) { + return IsChild && CanUse(RG_BOTTLE_WITH_BUGS) && HasItem(bean); } bool Logic::CanReflectNuts() { @@ -1402,6 +1416,16 @@ std::map Logic::RandoGetToRandInf = { { RG_ZELDAS_LETTER, RAND_INF_ZELDAS_LETTER }, { RG_WEIRD_EGG, RAND_INF_WEIRD_EGG }, { RG_RUTOS_LETTER, RAND_INF_OBTAINED_RUTOS_LETTER }, + { RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL, RAND_INF_DEATH_MOUNTAIN_CRATER_BEAN_SOUL }, + { RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL, RAND_INF_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL }, + { RG_DESERT_COLOSSUS_BEAN_SOUL, RAND_INF_DESERT_COLOSSUS_BEAN_SOUL }, + { RG_GERUDO_VALLEY_BEAN_SOUL, RAND_INF_GERUDO_VALLEY_BEAN_SOUL }, + { RG_GRAVEYARD_BEAN_SOUL, RAND_INF_GRAVEYARD_BEAN_SOUL }, + { RG_KOKIRI_FOREST_BEAN_SOUL, RAND_INF_KOKIRI_FOREST_BEAN_SOUL }, + { RG_LAKE_HYLIA_BEAN_SOUL, RAND_INF_LAKE_HYLIA_BEAN_SOUL }, + { RG_LOST_WOODS_BRIDGE_BEAN_SOUL, RAND_INF_LOST_WOODS_BRIDGE_BEAN_SOUL }, + { RG_LOST_WOODS_BEAN_SOUL, RAND_INF_LOST_WOODS_BEAN_SOUL }, + { RG_ZORAS_RIVER_BEAN_SOUL, RAND_INF_ZORAS_RIVER_BEAN_SOUL }, { RG_GOHMA_SOUL, RAND_INF_GOHMA_SOUL }, { RG_KING_DODONGO_SOUL, RAND_INF_KING_DODONGO_SOUL }, { RG_BARINADE_SOUL, RAND_INF_BARINADE_SOUL }, @@ -1768,6 +1792,16 @@ void Logic::ApplyItemEffect(Item& item, bool state) { case RG_RUTOS_LETTER: SetRandoInf(RAND_INF_OBTAINED_RUTOS_LETTER, state); break; + case RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL: + case RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL: + case RG_DESERT_COLOSSUS_BEAN_SOUL: + case RG_GERUDO_VALLEY_BEAN_SOUL: + case RG_GRAVEYARD_BEAN_SOUL: + case RG_KOKIRI_FOREST_BEAN_SOUL: + case RG_LAKE_HYLIA_BEAN_SOUL: + case RG_LOST_WOODS_BRIDGE_BEAN_SOUL: + case RG_LOST_WOODS_BEAN_SOUL: + case RG_ZORAS_RIVER_BEAN_SOUL: case RG_GOHMA_SOUL: case RG_KING_DODONGO_SOUL: case RG_BARINADE_SOUL: @@ -2319,6 +2353,19 @@ void Logic::Reset(bool resetSaveContext /*= true*/) { SetRandoInf(RAND_INF_FISHING_POLE_FOUND, true); } + if (ctx->GetOption(RSK_SHUFFLE_BEAN_SOULS).Is(false)) { + SetRandoInf(RAND_INF_DEATH_MOUNTAIN_CRATER_BEAN_SOUL, true); + SetRandoInf(RAND_INF_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL, true); + SetRandoInf(RAND_INF_DESERT_COLOSSUS_BEAN_SOUL, true); + SetRandoInf(RAND_INF_GERUDO_VALLEY_BEAN_SOUL, true); + SetRandoInf(RAND_INF_GRAVEYARD_BEAN_SOUL, true); + SetRandoInf(RAND_INF_KOKIRI_FOREST_BEAN_SOUL, true); + SetRandoInf(RAND_INF_LAKE_HYLIA_BEAN_SOUL, true); + SetRandoInf(RAND_INF_LOST_WOODS_BRIDGE_BEAN_SOUL, true); + SetRandoInf(RAND_INF_LOST_WOODS_BEAN_SOUL, true); + SetRandoInf(RAND_INF_ZORAS_RIVER_BEAN_SOUL, true); + } + // If not keysanity, start with 1 logical key to account for automatically unlocking the basement door in // vanilla FiT if (!IsFireLoopLocked() && ctx->GetDungeon(Rando::FIRE_TEMPLE)->IsVanilla()) { diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 9873e90aa..859f41f16 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -76,7 +76,7 @@ class Logic { bool BlueFire(); bool HasExplosives(); bool BlastOrSmash(); - bool CanSpawnSoilSkull(); + bool CanSpawnSoilSkull(RandomizerGet bean); bool CanReflectNuts(); bool CanCutShrubs(); bool CanStunDeku(); diff --git a/soh/soh/Enhancements/randomizer/option_descriptions.cpp b/soh/soh/Enhancements/randomizer/option_descriptions.cpp index 67513935b..1dd94f4b2 100644 --- a/soh/soh/Enhancements/randomizer/option_descriptions.cpp +++ b/soh/soh/Enhancements/randomizer/option_descriptions.cpp @@ -627,6 +627,8 @@ void Settings::CreateOptionDescriptions() { "Start with the ability to summon Pierre the Scarecrow. Pulling out an Ocarina in the usual locations will " "automatically summon him.\n" "With \"Shuffle Ocarina Buttons\" enabled, you'll need at least two Ocarina buttons to summon him."; + mOptionDescriptions[RSK_SKIP_PLANTING_BEANS] = "Beans will be planted once you find beans.\n" + "If bean souls are shuffled, you must find soul still."; mOptionDescriptions[RSK_ITEM_POOL] = "Sets how many major items appear in the item pool.\n" "\n" "Plentiful - Extra major items are added to the pool.\n" @@ -776,6 +778,8 @@ void Settings::CreateOptionDescriptions() { "location is reachable. When disabled, only " "required items and locations to beat the game " "will be guaranteed reachable."; + mOptionDescriptions[RSK_SHUFFLE_BEAN_SOULS] = + "Shuffle 10 bean souls which must be found to spawn corresponding soil / plant."; mOptionDescriptions[RSK_SHUFFLE_BOSS_SOULS] = "Shuffles 8 boss souls (one for each blue warp dungeon). A boss will not appear until you collect its " "respective soul." diff --git a/soh/soh/Enhancements/randomizer/randomizer.cpp b/soh/soh/Enhancements/randomizer/randomizer.cpp index dd6c78a79..0c5086b52 100644 --- a/soh/soh/Enhancements/randomizer/randomizer.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer.cpp @@ -5579,7 +5579,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!", @@ -5855,6 +5855,27 @@ void Randomizer::CreateCustomMessages() { "Du erhältst die %rKindergeldbörse%w!&Jetzt kannst Du bis&zu %y99 Rubine%w mit Dir führen!", "Vous obtenez la %rPetite Bourse%w!&Elle peut contenir jusqu'à %y99 rubis%w!"), + GIMESSAGE(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL, ITEM_BEAN, "You found the&%gDeath Mountain Crater Bean Soul%w!", + TODO_TRANSLATE, TODO_TRANSLATE), + GIMESSAGE(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL, ITEM_BEAN, "You found the&%gDeath Mountain Trail Bean Soul%w!", + TODO_TRANSLATE, TODO_TRANSLATE), + GIMESSAGE(RG_DESERT_COLOSSUS_BEAN_SOUL, ITEM_BEAN, "You found the&%gDesert Colossus Bean Soul%w!", + TODO_TRANSLATE, TODO_TRANSLATE), + GIMESSAGE(RG_GERUDO_VALLEY_BEAN_SOUL, ITEM_BEAN, "You found the&%gGerudo Valley Bean Soul%w!", TODO_TRANSLATE, + TODO_TRANSLATE), + GIMESSAGE(RG_GRAVEYARD_BEAN_SOUL, ITEM_BEAN, "You found the&%gGraveyard Bean Soul%w!", TODO_TRANSLATE, + TODO_TRANSLATE), + GIMESSAGE(RG_KOKIRI_FOREST_BEAN_SOUL, ITEM_BEAN, "You found the&%gKokiri Forest Bean Soul%w!", TODO_TRANSLATE, + TODO_TRANSLATE), + GIMESSAGE(RG_LAKE_HYLIA_BEAN_SOUL, ITEM_BEAN, "You found the&%gLake Hylia Bean Soul%w!", TODO_TRANSLATE, + TODO_TRANSLATE), + GIMESSAGE(RG_LOST_WOODS_BRIDGE_BEAN_SOUL, ITEM_BEAN, "You found the&%gLost Wood's Bridge Bean Soul%w!", + TODO_TRANSLATE, TODO_TRANSLATE), + GIMESSAGE(RG_LOST_WOODS_BEAN_SOUL, ITEM_BEAN, "You found the&%gLost Wood's Theatre Bean Soul%w!", + TODO_TRANSLATE, TODO_TRANSLATE), + GIMESSAGE(RG_ZORAS_RIVER_BEAN_SOUL, ITEM_BEAN, "You found the&%gZora's River Bean Soul%w!", TODO_TRANSLATE, + TODO_TRANSLATE), + GIMESSAGE(RG_GOHMA_SOUL, ITEM_BIG_POE, "You found the soul for %gGohma%w!", "Du hast die Seele von&%gGohma%w gefunden!", "Vous obtenez l'âme de %gGohma%w!"), GIMESSAGE(RG_KING_DODONGO_SOUL, ITEM_BIG_POE, "You found the soul for %rKing&Dodongo%w!", @@ -6086,6 +6107,16 @@ std::map randomizerGetToRandInf = { { RG_OCARINA_C_DOWN_BUTTON, RAND_INF_HAS_OCARINA_C_DOWN }, { RG_OCARINA_C_LEFT_BUTTON, RAND_INF_HAS_OCARINA_C_LEFT }, { RG_OCARINA_C_RIGHT_BUTTON, RAND_INF_HAS_OCARINA_C_RIGHT }, + { RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL, RAND_INF_DEATH_MOUNTAIN_CRATER_BEAN_SOUL }, + { RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL, RAND_INF_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL }, + { RG_DESERT_COLOSSUS_BEAN_SOUL, RAND_INF_DESERT_COLOSSUS_BEAN_SOUL }, + { RG_GERUDO_VALLEY_BEAN_SOUL, RAND_INF_GERUDO_VALLEY_BEAN_SOUL }, + { RG_GRAVEYARD_BEAN_SOUL, RAND_INF_GRAVEYARD_BEAN_SOUL }, + { RG_KOKIRI_FOREST_BEAN_SOUL, RAND_INF_KOKIRI_FOREST_BEAN_SOUL }, + { RG_LAKE_HYLIA_BEAN_SOUL, RAND_INF_LAKE_HYLIA_BEAN_SOUL }, + { RG_LOST_WOODS_BRIDGE_BEAN_SOUL, RAND_INF_LOST_WOODS_BRIDGE_BEAN_SOUL }, + { RG_LOST_WOODS_BEAN_SOUL, RAND_INF_LOST_WOODS_BEAN_SOUL }, + { RG_ZORAS_RIVER_BEAN_SOUL, RAND_INF_ZORAS_RIVER_BEAN_SOUL }, { RG_GOHMA_SOUL, RAND_INF_GOHMA_SOUL }, { RG_KING_DODONGO_SOUL, RAND_INF_KING_DODONGO_SOUL }, { RG_BARINADE_SOUL, RAND_INF_BARINADE_SOUL }, @@ -6321,6 +6352,18 @@ extern "C" u16 Randomizer_Item_Give(PlayState* play, GetItemEntry giEntry) { if (INV_CONTENT(ITEM_BEAN) == ITEM_NONE) { INV_CONTENT(ITEM_BEAN) = ITEM_BEAN; AMMO(ITEM_BEAN) = 10; + if (OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SKIP_PLANTING_BEANS)) { + gSaveContext.sceneFlags[SCENE_DEATH_MOUNTAIN_CRATER].swch |= (1 << 3); + gSaveContext.sceneFlags[SCENE_DEATH_MOUNTAIN_TRAIL].swch |= (1 << 6); + gSaveContext.sceneFlags[SCENE_DESERT_COLOSSUS].swch |= (1 << 24); + gSaveContext.sceneFlags[SCENE_GERUDO_VALLEY].swch |= (1 << 3); + gSaveContext.sceneFlags[SCENE_GRAVEYARD].swch |= (1 << 3); + gSaveContext.sceneFlags[SCENE_KOKIRI_FOREST].swch |= (1 << 9); + gSaveContext.sceneFlags[SCENE_LAKE_HYLIA].swch |= (1 << 1); + gSaveContext.sceneFlags[SCENE_LOST_WOODS].swch |= (1 << 4) | (1 << 18); + gSaveContext.sceneFlags[SCENE_ZORAS_RIVER].swch |= (1 << 3); + AMMO(ITEM_BEAN) = 0; + } } break; case RG_DOUBLE_DEFENSE: diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 6dc0ffdd9..9cde3a7eb 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -4367,6 +4367,16 @@ typedef enum { RG_BUY_RED_POTION_50, RG_TRIFORCE, RG_TRIFORCE_PIECE, + RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL, + RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL, + RG_DESERT_COLOSSUS_BEAN_SOUL, + RG_GERUDO_VALLEY_BEAN_SOUL, + RG_GRAVEYARD_BEAN_SOUL, + RG_KOKIRI_FOREST_BEAN_SOUL, + RG_LAKE_HYLIA_BEAN_SOUL, + RG_LOST_WOODS_BRIDGE_BEAN_SOUL, + RG_LOST_WOODS_BEAN_SOUL, + RG_ZORAS_RIVER_BEAN_SOUL, RG_GOHMA_SOUL, RG_KING_DODONGO_SOUL, RG_BARINADE_SOUL, @@ -5570,6 +5580,7 @@ typedef enum { RHT_DEKU_STICK_CAPACITY_20, RHT_DEKU_STICK_CAPACITY_30, RHT_TRIFORCE_PIECE, + RHT_BEAN_SOUL, RHT_GOHMA_SOUL, RHT_KING_DODONGO_SOUL, RHT_BARINADE_SOUL, @@ -6258,12 +6269,14 @@ typedef enum { RSK_SKIP_CHILD_ZELDA, RSK_STARTING_STICKS, RSK_STARTING_NUTS, + RSK_STARTING_BEANS, RSK_FULL_WALLETS, RSK_SHUFFLE_CHEST_MINIGAME, RSK_BIG_POE_COUNT, RSK_SKIP_EPONA_RACE, RSK_COMPLETE_MASK_QUEST, RSK_SKIP_SCARECROWS_SONG, + RSK_SKIP_PLANTING_BEANS, RSK_SKULLS_SUNS_SONG, RSK_SHUFFLE_ADULT_TRADE, RSK_SHUFFLE_MERCHANTS, @@ -6341,6 +6354,7 @@ typedef enum { RSK_TRIFORCE_HUNT, RSK_TRIFORCE_HUNT_PIECES_TOTAL, RSK_TRIFORCE_HUNT_PIECES_REQUIRED, + RSK_SHUFFLE_BEAN_SOULS, RSK_SHUFFLE_BOSS_SOULS, RSK_FISHSANITY, RSK_FISHSANITY_POND_COUNT, @@ -6891,6 +6905,7 @@ typedef enum { RE_GOHMA_LARVA, RE_KEESE, RE_FIRE_KEESE, + RE_GUAY, RE_MAD_SCRUB, RE_BLUE_BUBBLE, RE_POE, diff --git a/soh/soh/Enhancements/randomizer/randomizer_inf.h b/soh/soh/Enhancements/randomizer/randomizer_inf.h index ad928e855..b2d8c21ea 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_inf.h +++ b/soh/soh/Enhancements/randomizer/randomizer_inf.h @@ -182,6 +182,17 @@ DEFINE_RAND_INF(RAND_INF_ADULT_LOACH) DEFINE_RAND_INF(RAND_INF_10_BIG_POES) DEFINE_RAND_INF(RAND_INF_GRANT_GANONS_BOSSKEY) +DEFINE_RAND_INF(RAND_INF_DEATH_MOUNTAIN_CRATER_BEAN_SOUL) +DEFINE_RAND_INF(RAND_INF_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL) +DEFINE_RAND_INF(RAND_INF_DESERT_COLOSSUS_BEAN_SOUL) +DEFINE_RAND_INF(RAND_INF_GERUDO_VALLEY_BEAN_SOUL) +DEFINE_RAND_INF(RAND_INF_GRAVEYARD_BEAN_SOUL) +DEFINE_RAND_INF(RAND_INF_KOKIRI_FOREST_BEAN_SOUL) +DEFINE_RAND_INF(RAND_INF_LAKE_HYLIA_BEAN_SOUL) +DEFINE_RAND_INF(RAND_INF_LOST_WOODS_BRIDGE_BEAN_SOUL) +DEFINE_RAND_INF(RAND_INF_LOST_WOODS_BEAN_SOUL) +DEFINE_RAND_INF(RAND_INF_ZORAS_RIVER_BEAN_SOUL) + DEFINE_RAND_INF(RAND_INF_GOHMA_SOUL) DEFINE_RAND_INF(RAND_INF_KING_DODONGO_SOUL) DEFINE_RAND_INF(RAND_INF_BARINADE_SOUL) diff --git a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp index cd32cc4b1..16a11cdbc 100644 --- a/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp +++ b/soh/soh/Enhancements/randomizer/randomizer_item_tracker.cpp @@ -55,6 +55,7 @@ static WidgetInfo triforcePieceCount; static WidgetInfo dungeonItemTracking; static WidgetInfo gregTracking; static WidgetInfo triforcePieceTracking; +static WidgetInfo beanSoulsTracking; static WidgetInfo bossSoulsTracking; static WidgetInfo ocarinaButtonTracking; static WidgetInfo overworldKeysTracking; @@ -136,6 +137,19 @@ std::vector triforcePieces = { ITEM_TRACKER_ITEM(RG_TRIFORCE_PIECE, 0, DrawItem), }; +std::vector beanSoulItems = { + ITEM_TRACKER_ITEM_CUSTOM(RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL, ITEM_BEAN, ITEM_BEAN, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL, ITEM_BEAN, ITEM_BEAN, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_DESERT_COLOSSUS_BEAN_SOUL, ITEM_BEAN, ITEM_BEAN, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_GERUDO_VALLEY_BEAN_SOUL, ITEM_BEAN, ITEM_BEAN, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_GRAVEYARD_BEAN_SOUL, ITEM_BEAN, ITEM_BEAN, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_KOKIRI_FOREST_BEAN_SOUL, ITEM_BEAN, ITEM_BEAN, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_LAKE_HYLIA_BEAN_SOUL, ITEM_BEAN, ITEM_BEAN, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_LOST_WOODS_BRIDGE_BEAN_SOUL, ITEM_BEAN, ITEM_BEAN, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_LOST_WOODS_BEAN_SOUL, ITEM_BEAN, ITEM_BEAN, 0, DrawItem), + ITEM_TRACKER_ITEM_CUSTOM(RG_ZORAS_RIVER_BEAN_SOUL, ITEM_BEAN, ITEM_BEAN, 0, DrawItem), +}; + std::vector bossSoulItems = { ITEM_TRACKER_ITEM(RG_GOHMA_SOUL, 0, DrawItem), ITEM_TRACKER_ITEM(RG_KING_DODONGO_SOUL, 0, DrawItem), ITEM_TRACKER_ITEM(RG_BARINADE_SOUL, 0, DrawItem), ITEM_TRACKER_ITEM(RG_PHANTOM_GANON_SOUL, 0, DrawItem), @@ -246,6 +260,19 @@ std::map itemTrackerDungeonShortNames = { { SCENE_THIEVES_HIDEOUT, "HIDE" }, }; +std::map itemTrackerBeanShortNames = { + { RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL, "DMC" }, + { RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL, "DMT" }, + { RG_DESERT_COLOSSUS_BEAN_SOUL, "DC" }, + { RG_GERUDO_VALLEY_BEAN_SOUL, "GV" }, + { RG_GRAVEYARD_BEAN_SOUL, "GY" }, + { RG_KOKIRI_FOREST_BEAN_SOUL, "KF" }, + { RG_LAKE_HYLIA_BEAN_SOUL, "LA" }, + { RG_LOST_WOODS_BRIDGE_BEAN_SOUL, "LWB" }, + { RG_LOST_WOODS_BEAN_SOUL, "LWT" }, + { RG_ZORAS_RIVER_BEAN_SOUL, "ZR" }, +}; + std::map itemTrackerBossShortNames = { { RG_GOHMA_SOUL, "GOHMA" }, { RG_KING_DODONGO_SOUL, "KD" }, { RG_BARINADE_SOUL, "BARI" }, { RG_PHANTOM_GANON_SOUL, "PG" }, { RG_VOLVAGIA_SOUL, "VOLV" }, { RG_MORPHA_SOUL, "MORPH" }, @@ -809,6 +836,56 @@ void DrawItem(ItemTrackerItem item) { hasItem = IS_RANDO && OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_TRIFORCE_HUNT); itemName = "Triforce Piece"; break; + case RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_DEATH_MOUNTAIN_CRATER_BEAN_SOUL); + itemName = "Death Mountain Crater Bean Soul"; + break; + case RG_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL); + itemName = "Death Mountain Trail Bean Soul"; + break; + case RG_DESERT_COLOSSUS_BEAN_SOUL: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_DESERT_COLOSSUS_BEAN_SOUL); + itemName = "Desert Colossus Bean Soul"; + break; + case RG_GERUDO_VALLEY_BEAN_SOUL: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_GERUDO_VALLEY_BEAN_SOUL); + itemName = "Gerudo Valley Bean Soul"; + break; + case RG_GRAVEYARD_BEAN_SOUL: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_GRAVEYARD_BEAN_SOUL); + itemName = "Graveyard Bean Soul"; + break; + case RG_KOKIRI_FOREST_BEAN_SOUL: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_KOKIRI_FOREST_BEAN_SOUL); + itemName = "Kokiri Forest Bean Soul"; + break; + case RG_LAKE_HYLIA_BEAN_SOUL: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_LAKE_HYLIA_BEAN_SOUL); + itemName = "Lake Hylia Bean Soul"; + break; + case RG_LOST_WOODS_BRIDGE_BEAN_SOUL: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_LOST_WOODS_BRIDGE_BEAN_SOUL); + itemName = "Lost Woods Bridge Bean Soul"; + break; + case RG_LOST_WOODS_BEAN_SOUL: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_LOST_WOODS_BEAN_SOUL); + itemName = "Lost Woods Theatre Bean Soul"; + break; + case RG_ZORAS_RIVER_BEAN_SOUL: + actualItemId = item.id; + hasItem = Flags_GetRandomizerInf(RAND_INF_ZORAS_RIVER_BEAN_SOUL); + itemName = "Zora's River Bean Soul"; + break; case RG_GOHMA_SOUL: actualItemId = item.id; hasItem = Flags_GetRandomizerInf(RAND_INF_GOHMA_SOUL); @@ -1025,6 +1102,16 @@ void DrawItem(ItemTrackerItem item) { DrawItemCount(item, false); + if (item.id >= RG_DEATH_MOUNTAIN_CRATER_BEAN_SOUL && item.id <= RG_ZORAS_RIVER_BEAN_SOUL) { + ImVec2 p = ImGui::GetCursorScreenPos(); + std::string beanName = itemTrackerBeanShortNames[item.id]; + ImGui::SetCursorScreenPos( + ImVec2(p.x + (iconSize / 2) - (ImGui::CalcTextSize(beanName.c_str()).x / 2), p.y - (iconSize + 13))); + ImGui::PushStyleColor(ImGuiCol_Text, IM_COL_WHITE); + ImGui::Text("%s", beanName.c_str()); + ImGui::PopStyleColor(); + } + if (item.id >= RG_GOHMA_SOUL && item.id <= RG_GANON_SOUL) { ImVec2 p = ImGui::GetCursorScreenPos(); std::string bossName = itemTrackerBossShortNames[item.id]; @@ -1478,6 +1565,19 @@ void UpdateVectors() { mainWindowItems.insert(mainWindowItems.end(), fishingPoleItems.begin(), fishingPoleItems.end()); } + // If we're adding bean souls to the main window... + if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.BeanSouls"), SECTION_DISPLAY_HIDDEN) == + SECTION_DISPLAY_MAIN_WINDOW) { + //...add empty items on the main window to get the souls on their own row. (Too many to sit with Greg/Triforce + // pieces) + while (mainWindowItems.size() % 6) { + mainWindowItems.push_back(ITEM_TRACKER_ITEM(ITEM_NONE, 0, DrawItem)); + } + + // Add bean souls + mainWindowItems.insert(mainWindowItems.end(), beanSoulItems.begin(), beanSoulItems.end()); + } + // If we're adding boss souls to the main window... if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.BossSouls"), SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_MAIN_WINDOW) { @@ -1674,6 +1774,13 @@ void ItemTrackerWindow::DrawElement() { EndFloatingWindows(); } + if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.BeanSouls"), SECTION_DISPLAY_HIDDEN) == + SECTION_DISPLAY_SEPARATE) { + BeginFloatingWindows("Bean Soul Tracker"); + DrawItemsInRows(beanSoulItems); + EndFloatingWindows(); + } + if (CVarGetInteger(CVAR_TRACKER_ITEM("DisplayType.BossSouls"), SECTION_DISPLAY_HIDDEN) == SECTION_DISPLAY_SEPARATE) { BeginFloatingWindows("Boss Soul Tracker"); @@ -1914,6 +2021,7 @@ void ItemTrackerSettingsWindow::DrawElement() { } SohGui::mSohMenu->MenuDrawItem(gregTracking, 250, THEME_COLOR); SohGui::mSohMenu->MenuDrawItem(triforcePieceTracking, 250, THEME_COLOR); + SohGui::mSohMenu->MenuDrawItem(beanSoulsTracking, 250, THEME_COLOR); SohGui::mSohMenu->MenuDrawItem(bossSoulsTracking, 250, THEME_COLOR); SohGui::mSohMenu->MenuDrawItem(ocarinaButtonTracking, 250, THEME_COLOR); SohGui::mSohMenu->MenuDrawItem(overworldKeysTracking, 250, THEME_COLOR); @@ -2028,6 +2136,18 @@ void RegisterItemTrackerWidgets() { ; SohGui::mSohMenu->AddSearchWidget({ gregTracking, "Randomizer", "Item Tracker", "General Settings", "icon" }); + beanSoulsTracking = { .name = "Bean Souls", .type = WidgetType::WIDGET_CVAR_COMBOBOX }; + beanSoulsTracking.CVar(CVAR_TRACKER_ITEM("DisplayType.BeanSouls")) + .Options(ComboboxOptions() + .DefaultIndex(SECTION_DISPLAY_HIDDEN) + .ComponentAlignment(ComponentAlignments::Right) + .LabelPosition(LabelPositions::Far) + .Color(THEME_COLOR) + .ComboMap(displayTypes)) + .Callback([](WidgetInfo& info) { shouldUpdateVectors = true; }); + ; + SohGui::mSohMenu->AddSearchWidget({ beanSoulsTracking, "Randomizer", "Item Tracker", "General Settings", "icon" }); + bossSoulsTracking = { .name = "Boss Souls", .type = WidgetType::WIDGET_CVAR_COMBOBOX }; bossSoulsTracking.CVar(CVAR_TRACKER_ITEM("DisplayType.BossSouls")) .Options(ComboboxOptions() diff --git a/soh/soh/Enhancements/randomizer/savefile.cpp b/soh/soh/Enhancements/randomizer/savefile.cpp index c8c3ea431..0bcfa1038 100644 --- a/soh/soh/Enhancements/randomizer/savefile.cpp +++ b/soh/soh/Enhancements/randomizer/savefile.cpp @@ -266,6 +266,41 @@ extern "C" void Randomizer_InitSaveFile() { // Go away Ruto (Water Temple first cutscene). gSaveContext.sceneFlags[SCENE_WATER_TEMPLE].swch |= (1 << 0x10); + if (Randomizer_GetSettingValue(RSK_STARTING_BEANS)) { + INV_CONTENT(ITEM_BEAN) = ITEM_BEAN; + if (Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_BEANS_ONLY && + Randomizer_GetSettingValue(RSK_SHUFFLE_MERCHANTS) != RO_SHUFFLE_MERCHANTS_ALL) { + BEANS_BOUGHT = 10; + } + if (Randomizer_GetSettingValue(RSK_SKIP_PLANTING_BEANS)) { + AMMO(ITEM_BEAN) = 0; + gSaveContext.sceneFlags[SCENE_DEATH_MOUNTAIN_CRATER].swch |= (1 << 3); + gSaveContext.sceneFlags[SCENE_DEATH_MOUNTAIN_TRAIL].swch |= (1 << 6); + gSaveContext.sceneFlags[SCENE_DESERT_COLOSSUS].swch |= (1 << 24); + gSaveContext.sceneFlags[SCENE_GERUDO_VALLEY].swch |= (1 << 3); + gSaveContext.sceneFlags[SCENE_GRAVEYARD].swch |= (1 << 3); + gSaveContext.sceneFlags[SCENE_KOKIRI_FOREST].swch |= (1 << 9); + gSaveContext.sceneFlags[SCENE_LAKE_HYLIA].swch |= (1 << 1); + gSaveContext.sceneFlags[SCENE_LOST_WOODS].swch |= (1 << 4) | (1 << 18); + gSaveContext.sceneFlags[SCENE_ZORAS_RIVER].swch |= (1 << 3); + } else { + AMMO(ITEM_BEAN) = 10; + } + } + + if (Randomizer_GetSettingValue(RSK_SHUFFLE_BEAN_SOULS) == RO_GENERIC_OFF) { + Flags_SetRandomizerInf(RAND_INF_DEATH_MOUNTAIN_CRATER_BEAN_SOUL); + Flags_SetRandomizerInf(RAND_INF_DEATH_MOUNTAIN_TRAIL_BEAN_SOUL); + Flags_SetRandomizerInf(RAND_INF_DESERT_COLOSSUS_BEAN_SOUL); + Flags_SetRandomizerInf(RAND_INF_GERUDO_VALLEY_BEAN_SOUL); + Flags_SetRandomizerInf(RAND_INF_GRAVEYARD_BEAN_SOUL); + Flags_SetRandomizerInf(RAND_INF_KOKIRI_FOREST_BEAN_SOUL); + Flags_SetRandomizerInf(RAND_INF_LAKE_HYLIA_BEAN_SOUL); + Flags_SetRandomizerInf(RAND_INF_LOST_WOODS_BRIDGE_BEAN_SOUL); + Flags_SetRandomizerInf(RAND_INF_LOST_WOODS_BEAN_SOUL); + Flags_SetRandomizerInf(RAND_INF_ZORAS_RIVER_BEAN_SOUL); + } + if (Randomizer_GetSettingValue(RSK_SHUFFLE_OCARINA_BUTTONS) == RO_GENERIC_OFF) { Flags_SetRandomizerInf(RAND_INF_HAS_OCARINA_A); Flags_SetRandomizerInf(RAND_INF_HAS_OCARINA_C_LEFT); diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 1266cb651..95463f20d 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -237,6 +237,7 @@ void Settings::CreateOptions() { 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)"}); OPT_BOOL(RSK_SHUFFLE_100_GS_REWARD, "Shuffle 100 GS Reward", CVAR_RANDOMIZER_SETTING("Shuffle100GSReward"), mOptionDescriptions[RSK_SHUFFLE_100_GS_REWARD], IMFLAG_SEPARATOR_BOTTOM, WidgetType::Checkbox, RO_GENERIC_OFF); + OPT_BOOL(RSK_SHUFFLE_BEAN_SOULS, "Shuffle Bean Souls", CVAR_RANDOMIZER_SETTING("ShuffleBeanSouls"), mOptionDescriptions[RSK_SHUFFLE_BEAN_SOULS], IMFLAG_SEPARATOR_BOTTOM, WidgetType::Checkbox, RO_GENERIC_OFF); OPT_U8(RSK_SHUFFLE_BOSS_SOULS, "Shuffle Boss Souls", {"Off", "On", "On + Ganon"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("ShuffleBossSouls"), mOptionDescriptions[RSK_SHUFFLE_BOSS_SOULS], WidgetType::Combobox); OPT_BOOL(RSK_SHUFFLE_DEKU_STICK_BAG, "Shuffle Deku Stick Bag", CVAR_RANDOMIZER_SETTING("ShuffleDekuStickBag"), mOptionDescriptions[RSK_SHUFFLE_DEKU_STICK_BAG], IMFLAG_SEPARATOR_BOTTOM, WidgetType::Checkbox, RO_GENERIC_OFF); OPT_BOOL(RSK_SHUFFLE_DEKU_NUT_BAG, "Shuffle Deku Nut Bag", CVAR_RANDOMIZER_SETTING("ShuffleDekuNutBag"), mOptionDescriptions[RSK_SHUFFLE_DEKU_NUT_BAG], IMFLAG_SEPARATOR_BOTTOM, WidgetType::Checkbox, RO_GENERIC_OFF); @@ -276,6 +277,7 @@ void Settings::CreateOptions() { OPT_BOOL(RSK_SKIP_CHILD_ZELDA, "Skip Child Zelda", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipChildZelda"), mOptionDescriptions[RSK_SKIP_CHILD_ZELDA], WidgetType::Checkbox, RO_GENERIC_DONT_SKIP); OPT_BOOL(RSK_SKIP_EPONA_RACE, "Skip Epona Race", {"Don't Skip", "Skip"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("SkipEponaRace"), mOptionDescriptions[RSK_SKIP_EPONA_RACE], WidgetType::Checkbox, RO_GENERIC_DONT_SKIP); OPT_BOOL(RSK_SKIP_SCARECROWS_SONG, "Skip Scarecrow's Song", CVAR_RANDOMIZER_SETTING("SkipScarecrowsSong"), mOptionDescriptions[RSK_SKIP_SCARECROWS_SONG]); + OPT_BOOL(RSK_SKIP_PLANTING_BEANS, "Skip Planting Beans", CVAR_RANDOMIZER_SETTING("SkipPlantingBeans"), mOptionDescriptions[RSK_SKIP_PLANTING_BEANS]); OPT_U8(RSK_BIG_POE_COUNT, "Big Poe Target Count", {NumOpts(0, 10)}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("BigPoeTargetCount"), mOptionDescriptions[RSK_BIG_POE_COUNT], WidgetType::Slider, 10); OPT_BOOL(RSK_COMPLETE_MASK_QUEST, "Complete Mask Quest", CVAR_RANDOMIZER_SETTING("CompleteMaskQuest"), mOptionDescriptions[RSK_COMPLETE_MASK_QUEST]); OPT_U8(RSK_GOSSIP_STONE_HINTS, "Gossip Stone Hints", {"No Hints", "Need Nothing", "Mask of Truth", "Stone of Agony"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GossipStoneHints"), mOptionDescriptions[RSK_GOSSIP_STONE_HINTS], WidgetType::Combobox, RO_GOSSIP_STONES_NEED_NOTHING, false, IMFLAG_NONE); @@ -322,6 +324,7 @@ void Settings::CreateOptions() { OPT_BOOL(RSK_STARTING_MASTER_SWORD, "Start with Master Sword", CVAR_RANDOMIZER_SETTING("StartingMasterSword")); OPT_BOOL(RSK_STARTING_STICKS, "Start with Stick Ammo", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingSticks"), "", WidgetType::Checkbox, RO_GENERIC_OFF); OPT_BOOL(RSK_STARTING_NUTS, "Start with Nut Ammo", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingNuts"), "", WidgetType::Checkbox, RO_GENERIC_OFF); + OPT_BOOL(RSK_STARTING_BEANS, "Start with Magic Beans", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingBeans"), "", WidgetType::Checkbox, RO_GENERIC_OFF); OPT_BOOL(RSK_FULL_WALLETS, "Full Wallets", {"No", "Yes"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("FullWallets"), mOptionDescriptions[RSK_FULL_WALLETS], WidgetType::Checkbox, RO_GENERIC_OFF); OPT_BOOL(RSK_STARTING_ZELDAS_LULLABY, "Start with Zelda's Lullaby", CVAR_RANDOMIZER_SETTING("StartingZeldasLullaby"), "", IMFLAG_NONE); OPT_BOOL(RSK_STARTING_EPONAS_SONG, "Start with Epona's Song", CVAR_RANDOMIZER_SETTING("StartingEponasSong"), "", IMFLAG_NONE); @@ -1343,6 +1346,7 @@ void Settings::CreateOptions() { &mOptions[RSK_SHUFFLE_FROG_SONG_RUPEES], &mOptions[RSK_SHUFFLE_ADULT_TRADE], &mOptions[RSK_SHUFFLE_100_GS_REWARD], + &mOptions[RSK_SHUFFLE_BEAN_SOULS], &mOptions[RSK_SHUFFLE_BOSS_SOULS], &mOptions[RSK_SHUFFLE_FOUNTAIN_FAIRIES], &mOptions[RSK_SHUFFLE_STONE_FAIRIES], @@ -1389,7 +1393,7 @@ void Settings::CreateOptions() { mOptionGroups[RSG_TIMESAVERS_IMGUI] = OptionGroup::SubGroup( "Timesavers", { &mOptions[RSK_BIG_POE_COUNT], &mOptions[RSK_SKIP_CHILD_ZELDA], &mOptions[RSK_SKIP_EPONA_RACE], - &mOptions[RSK_COMPLETE_MASK_QUEST], &mOptions[RSK_SKIP_SCARECROWS_SONG] }, + &mOptions[RSK_COMPLETE_MASK_QUEST], &mOptions[RSK_SKIP_SCARECROWS_SONG], &mOptions[RSK_SKIP_PLANTING_BEANS] }, WidgetContainerType::COLUMN); mOptionGroups[RSG_ITEM_POOL_HINTS_IMGUI] = OptionGroup::SubGroup("", { @@ -1463,6 +1467,7 @@ void Settings::CreateOptions() { &mOptions[RSK_STARTING_OCARINA], &mOptions[RSK_STARTING_STICKS], &mOptions[RSK_STARTING_NUTS], + &mOptions[RSK_STARTING_BEANS], &mOptions[RSK_STARTING_SKULLTULA_TOKEN], &mOptions[RSK_STARTING_HEARTS], }, @@ -1608,6 +1613,7 @@ void Settings::CreateOptions() { &mOptions[RSK_SHUFFLE_ADULT_TRADE], &mOptions[RSK_SHUFFLE_CHEST_MINIGAME], &mOptions[RSK_SHUFFLE_100_GS_REWARD], + &mOptions[RSK_SHUFFLE_BEAN_SOULS], &mOptions[RSK_SHUFFLE_BOSS_SOULS], &mOptions[RSK_SHUFFLE_DEKU_STICK_BAG], &mOptions[RSK_SHUFFLE_DEKU_NUT_BAG], @@ -1664,6 +1670,7 @@ void Settings::CreateOptions() { mOptionGroups[RSG_STARTING_OTHER] = OptionGroup::SubGroup("Other", { &mOptions[RSK_STARTING_STICKS], &mOptions[RSK_STARTING_NUTS], + &mOptions[RSK_STARTING_BEANS], &mOptions[RSK_FULL_WALLETS], &mOptions[RSK_STARTING_SKULLTULA_TOKEN], &mOptions[RSK_STARTING_HEARTS], @@ -1679,6 +1686,7 @@ void Settings::CreateOptions() { &mOptions[RSK_SKIP_CHILD_ZELDA], &mOptions[RSK_SKIP_EPONA_RACE], &mOptions[RSK_SKIP_SCARECROWS_SONG], + &mOptions[RSK_SKIP_PLANTING_BEANS], &mOptions[RSK_BIG_POE_COUNT], &mOptions[RSK_COMPLETE_MASK_QUEST], }); diff --git a/soh/soh/OTRGlobals.cpp b/soh/soh/OTRGlobals.cpp index 2aadd55d2..9167d5e9c 100644 --- a/soh/soh/OTRGlobals.cpp +++ b/soh/soh/OTRGlobals.cpp @@ -2549,12 +2549,18 @@ extern "C" int CustomMessage_RetrieveIfExists(PlayState* play) { u16 naviTextId = Random(0, NUM_NAVI_MESSAGES); messageEntry = CustomMessageManager::Instance->RetrieveMessage(Randomizer::NaviRandoMessageTableID, naviTextId, MF_FORMATTED); - } else if (textId == TEXT_BEAN_SALESMAN_BUY_FOR_10 && - (ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_BEANS_ONLY) || - ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL))) { - messageEntry = OTRGlobals::Instance->gRandomizer->GetMerchantMessage( - RC_ZR_MAGIC_BEAN_SALESMAN, TEXT_BEAN_SALESMAN_BUY_FOR_10, TEXT_NONE, - Randomizer_GetSettingValue(RSK_MERCHANT_TEXT_HINT) == RO_GENERIC_OFF); + } else if (textId == TEXT_BEAN_SALESMAN_BUY_FOR_10) { + if (ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_BEANS_ONLY) || + ctx->GetOption(RSK_SHUFFLE_MERCHANTS).Is(RO_SHUFFLE_MERCHANTS_ALL)) { + messageEntry = OTRGlobals::Instance->gRandomizer->GetMerchantMessage( + RC_ZR_MAGIC_BEAN_SALESMAN, TEXT_BEAN_SALESMAN_BUY_FOR_10, TEXT_NONE, + Randomizer_GetSettingValue(RSK_MERCHANT_TEXT_HINT) == RO_GENERIC_OFF); + } else if (ctx->GetOption(RSK_SKIP_PLANTING_BEANS)) { + // TODO_TRANSLATE Translate into french and german + messageEntry = + CustomMessage("For #60 rupees# I'll plant beans across Hyrule, deal?\x1B#Yes&No#", { QM_YELLOW }); + messageEntry.AutoFormat(); + } } else if (textId == TEXT_BEAN_SALESMAN_BUY_FOR_100) { messageEntry = CustomMessageManager::Instance->RetrieveMessage( Randomizer::merchantMessageTableID, TEXT_BEAN_SALESMAN_BUY_FOR_100, MF_AUTO_FORMAT);