diff options
| author | Garrett Cox <garrettjcox@gmail.com> | 2026-07-23 10:28:55 -0500 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2026-07-23 10:28:55 -0500 |
| commit | b690db6fdfe1d06a58ba1c36cdfff8f9e2a8c44d (patch) | |
| tree | efe8b58d9a5d10683722750ca8a1b3071f096dd3 | |
| parent | 4ce76f3de288fde206140b5fe15c0a7a6dab60ae (diff) | |
Shuffle butterflies (#1770)
| -rw-r--r-- | mm/2s2h/Rando/ActorBehavior/ActorBehavior.cpp | 1 | ||||
| -rw-r--r-- | mm/2s2h/Rando/ActorBehavior/ActorBehavior.h | 1 | ||||
| -rw-r--r-- | mm/2s2h/Rando/ActorBehavior/EnButte.cpp | 162 | ||||
| -rw-r--r-- | mm/2s2h/Rando/CheckTracker/CheckTracker.cpp | 1 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Logic/GeneratePools.cpp | 5 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Logic/Regions/North.cpp | 9 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Logic/Regions/South.cpp | 4 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Logic/Regions/TerminaField.cpp | 7 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Logic/Regions/West.cpp | 5 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Menu.cpp | 1 | ||||
| -rw-r--r-- | mm/2s2h/Rando/StaticData/Checks.cpp | 27 | ||||
| -rw-r--r-- | mm/2s2h/Rando/StaticData/Options.cpp | 1 | ||||
| -rw-r--r-- | mm/2s2h/Rando/Types.h | 28 |
13 files changed, 252 insertions, 0 deletions
diff --git a/mm/2s2h/Rando/ActorBehavior/ActorBehavior.cpp b/mm/2s2h/Rando/ActorBehavior/ActorBehavior.cpp index 1eaf0b927..f31d40759 100644 --- a/mm/2s2h/Rando/ActorBehavior/ActorBehavior.cpp +++ b/mm/2s2h/Rando/ActorBehavior/ActorBehavior.cpp @@ -60,6 +60,7 @@ void Rando::ActorBehavior::OnFileLoad() { Rando::ActorBehavior::InitEnBjtBehavior(); Rando::ActorBehavior::InitEnBomBowlManBehavior(); Rando::ActorBehavior::InitEnBoxBehavior(); + Rando::ActorBehavior::InitEnButteBehavior(); Rando::ActorBehavior::InitEnCowBehavior(); Rando::ActorBehavior::InitEnDaiBehavior(); Rando::ActorBehavior::InitEnDnhBehavior(); diff --git a/mm/2s2h/Rando/ActorBehavior/ActorBehavior.h b/mm/2s2h/Rando/ActorBehavior/ActorBehavior.h index 0f9ca1d54..0b46a56d7 100644 --- a/mm/2s2h/Rando/ActorBehavior/ActorBehavior.h +++ b/mm/2s2h/Rando/ActorBehavior/ActorBehavior.h @@ -29,6 +29,7 @@ void InitEnBalBehavior(); void InitEnBjtBehavior(); void InitEnBomBowlManBehavior(); void InitEnBoxBehavior(); +void InitEnButteBehavior(); void InitEnCowBehavior(); void InitEnDaiBehavior(); void InitEnDnhBehavior(); diff --git a/mm/2s2h/Rando/ActorBehavior/EnButte.cpp b/mm/2s2h/Rando/ActorBehavior/EnButte.cpp new file mode 100644 index 000000000..1413a1000 --- /dev/null +++ b/mm/2s2h/Rando/ActorBehavior/EnButte.cpp @@ -0,0 +1,162 @@ +#include "ActorBehavior.h" +#include <libultraship/bridge/consolevariablebridge.h> + +#include "2s2h/CustomItem/CustomItem.h" +#include "2s2h/Rando/Rando.h" +#include "2s2h/ShipInit.hpp" +#include "2s2h/ObjectExtension/ActorListIndex.h" +#include "2s2h/ObjectExtension/ObjectExtension.h" +#include "2s2h/Enhancements/FrameInterpolation/FrameInterpolation.h" +#include "assets/2s2h_assets.h" +#include <spdlog/spdlog.h> + +extern "C" { +#include "variables.h" +#include "overlays/actors/ovl_Obj_Mure/z_obj_mure.h" + +void ObjMure_CulledState(ObjMure*, PlayState*); +void ObjMure_ActiveState(ObjMure*, PlayState*); +void EnButte_Draw(Actor* thisx, PlayState* play); +void EnButte_Update(Actor* thisx, PlayState* play); +} + +typedef enum { + BUTTERFLY_CATCH_IDLE, + BUTTERFLY_CATCH_APPROACHING, +} ButterflyCatchPhase; + +struct ButterflyCatchState { + ButterflyCatchPhase phase = BUTTERFLY_CATCH_IDLE; + // Our own tracked facing, independent of shape.rot.y. Vanilla's own turning (func_8091C6B4, run every + // frame via EnButte_Update) keeps nudging shape.rot.y towards its own ambient-flight heading, so smoothing + // from the live shape.rot.y value fights that every frame and never settles. Smoothing our own value and + // writing it into shape.rot.y afterwards avoids that tug-of-war entirely. + s16 facingYaw = 0; +}; +static ObjectExtension::Register<ButterflyCatchState> ButterflyCatchStateRegister; + +struct ObjMureLastActionFunc { + void* value = nullptr; +}; +static ObjectExtension::Register<ObjMureLastActionFunc> ObjMureLastActionFuncRegister; + +template <typename T> T& GetOrCreateExtension(const void* object) { + T* existing = ObjectExtension::GetInstance().Get<T>(object); + if (existing != nullptr) { + return *existing; + } + ObjectExtension::GetInstance().Set<T>(object, T{}); + return *ObjectExtension::GetInstance().Get<T>(object); +} + +// clang-format off +std::map<std::tuple<s16, u8, u8>, std::tuple<RandoCheckId, u8>> butterflyMap = { + { { SCENE_00KEIKOKU, 0, 171 }, { RC_TERMINA_FIELD_BUTTERFLY_01, 2 } }, + { { SCENE_30GYOSON, 0, 64 }, { RC_GREAT_BAY_COAST_BUTTERFLY_01, 2 } }, + { { SCENE_30GYOSON, 0, 133 }, { RC_GREAT_BAY_COAST_BUTTERFLY_01, 2 } }, // Post-Great Bay Temple clear (4 spawn here, only first 2 are checks) + { { SCENE_10YUKIYAMANOMURA2, 0, 32 }, { RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_01, 4 } }, + { { SCENE_10YUKIYAMANOMURA2, 0, 38 }, { RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_01, 4 } }, // First-time entrance cutscene layer + { { SCENE_10YUKIYAMANOMURA2, 0, 33 }, { RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_05, 5 } }, + { { SCENE_10YUKIYAMANOMURA2, 0, 39 }, { RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_05, 5 } }, // First-time entrance cutscene layer + { { SCENE_KAKUSIANA, 0, 2 }, { RC_TERMINA_FIELD_GOSSIP_STONE_GROTTO_3_BUTTERFLY_01, 2 } }, + { { SCENE_KAKUSIANA, 10, 6 }, { RC_GREAT_BAY_COAST_COW_GROTTO_BUTTERFLY_01, 3 } }, // Adjusted for Termina Field cow grotto below + { { SCENE_KAKUSIANA, 12, 4 }, { RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_BUTTERFLY_01, 4 } }, +}; +// clang-format on + +void DrawButterfly(Actor* actor, PlayState* play) { + EnButte_Draw(actor, play); + + OPEN_DISPS(gPlayState->state.gfxCtx); + RandoCheckId randoCheckId = Rando::ActorBehavior::GetObjectRandoCheckId(actor); + Matrix_Scale(8.0f, 8.0f, 8.0f, MTXMODE_APPLY); + Rando::DrawItem(Rando::ConvertItem(RANDO_SAVE_CHECKS[randoCheckId].randoItemId, randoCheckId), randoCheckId, actor); + CLOSE_DISPS(gPlayState->state.gfxCtx); +} + +// Runs the actual vanilla update (flight AI, wing animation, gravity, collider sync) every frame so a butterfly +// looks and moves exactly like normal until it's lured in, then eases its position toward the stick tip on top. +// The moment it gets close enough it instantly grants the check and goes back to idle, where vanilla's own +// home-seeking behavior (func_8091C794) carries it back home on its own. +void EnButte_UpdateRandoCheck(Actor* thisx, PlayState* play) { + EnButte_Update(thisx, play); + + RandoCheckId randoCheckId = Rando::ActorBehavior::GetObjectRandoCheckId(thisx); + RandoSaveCheck& randoSaveCheck = RANDO_SAVE_CHECKS[randoCheckId]; + if (randoSaveCheck.cycleObtained) { + thisx->draw = EnButte_Draw; + thisx->update = EnButte_Update; + return; + } + + ButterflyCatchState& state = GetOrCreateExtension<ButterflyCatchState>(thisx); + Player* player = GET_PLAYER(play); + bool holdingStick = player->heldItemAction == PLAYER_IA_DEKU_STICK; + + if (state.phase == BUTTERFLY_CATCH_IDLE) { + if (randoSaveCheck.eligible || !holdingStick || thisx->xzDistToPlayer > 240.0f || + fabsf(thisx->playerHeightRel) > 400.0f) { + return; + } + state.phase = BUTTERFLY_CATCH_APPROACHING; + state.facingYaw = thisx->shape.rot.y; + } else if (!holdingStick) { + state.phase = BUTTERFLY_CATCH_IDLE; + return; + } + + Vec3f target = player->meleeWeaponInfo[0].tip; + + f32 dist = Math_Vec3f_DistXYZ(&thisx->world.pos, &target); + f32 step = CLAMP(dist * 0.06f, 1.0f, 2.2f); + Math_Vec3f_StepTo(&thisx->world.pos, &target, step); + + s16 yawTowardsTarget = BINANG_ROT180(Math_Vec3f_Yaw(&thisx->world.pos, &target)); + Math_SmoothStepToS(&state.facingYaw, yawTowardsTarget, 6, 0x7D0, 1); + thisx->shape.rot.y = state.facingYaw; + + if (dist <= 10.0f) { + randoSaveCheck.eligible = true; + state.phase = BUTTERFLY_CATCH_IDLE; + } +} + +void Rando::ActorBehavior::InitEnButteBehavior() { + bool shouldRegister = IS_RANDO && RANDO_SAVE_OPTIONS[RO_SHUFFLE_BUTTERFLIES] != RO_GENERIC_OFF; + + COND_ID_HOOK(OnActorUpdate, ACTOR_OBJ_MURE, shouldRegister, [](Actor* actor) { + ObjMure* objMure = (ObjMure*)actor; + ObjMureLastActionFunc& lastActionFuncState = GetOrCreateExtension<ObjMureLastActionFunc>(actor); + + if (lastActionFuncState.value == (void*)ObjMure_CulledState && + (void*)objMure->actionFunc == (void*)ObjMure_ActiveState) { + auto it = + butterflyMap.find({ gPlayState->sceneId, gPlayState->roomCtx.curRoom.num, GetActorListIndex(actor) }); + if (it != butterflyMap.end()) { + RandoCheckId startingRandoCheckId = std::get<0>(it->second); + u8 butterflyCount = std::get<1>(it->second); + // Adjust for Termina Field cow grotto + if (gPlayState->sceneId == SCENE_KAKUSIANA && gPlayState->roomCtx.curRoom.num == 10 && + gSaveContext.respawn[RESPAWN_MODE_UNK_3].data == 31) { + startingRandoCheckId = + static_cast<RandoCheckId>(startingRandoCheckId + (RC_TERMINA_FIELD_COW_GROTTO_BUTTERFLY_01 - + RC_GREAT_BAY_COAST_COW_GROTTO_BUTTERFLY_01)); + } + for (u8 i = 0; i < butterflyCount; i++) { + RandoCheckId randoCheckId = static_cast<RandoCheckId>(static_cast<int>(startingRandoCheckId) + i); + if (!RANDO_SAVE_CHECKS[randoCheckId].shuffled || RANDO_SAVE_CHECKS[randoCheckId].cycleObtained || + objMure->children[i] == nullptr) { + continue; + } + Actor* butterfly = objMure->children[i]; + SetObjectRandoCheckId(butterfly, randoCheckId); + butterfly->draw = DrawButterfly; + butterfly->update = EnButte_UpdateRandoCheck; + // Clear the "golden butterfly" params bit so vanilla's own stick-attraction never triggers + butterfly->params &= ~1; + } + } + } + lastActionFuncState.value = (void*)objMure->actionFunc; + }); +} diff --git a/mm/2s2h/Rando/CheckTracker/CheckTracker.cpp b/mm/2s2h/Rando/CheckTracker/CheckTracker.cpp index fc7bedf36..050ea90f5 100644 --- a/mm/2s2h/Rando/CheckTracker/CheckTracker.cpp +++ b/mm/2s2h/Rando/CheckTracker/CheckTracker.cpp @@ -120,6 +120,7 @@ std::set<RandoCheckType> checkTypeFilter; std::vector<const char*> checkTypeIconList = { /*RCTYPE_UNKNOWN*/ gItemIconBombersNotebookTex, /*RCTYPE_BARREL*/ gBarrelTrackerIcon, + /*RCTYPE_BUTTERFLY*/ gItemIconDekuStickTex, /*RCTYPE_CHEST*/ gChestTrackerIcon, /*RCTYPE_COW*/ gItemIconRomaniMaskTex, /*RCTYPE_CRATE*/ gCrateTrackerIcon, diff --git a/mm/2s2h/Rando/Logic/GeneratePools.cpp b/mm/2s2h/Rando/Logic/GeneratePools.cpp index ccc38b24d..08dcd6fe5 100644 --- a/mm/2s2h/Rando/Logic/GeneratePools.cpp +++ b/mm/2s2h/Rando/Logic/GeneratePools.cpp @@ -85,6 +85,11 @@ void GeneratePools(RandoSaveInfo& saveInfo, std::vector<RandoCheckId>& checkPool continue; } + if (randoStaticCheck.randoCheckType == RCTYPE_BUTTERFLY && + saveInfo.randoSaveOptions[RO_SHUFFLE_BUTTERFLIES] == RO_GENERIC_NO) { + continue; + } + if (randoStaticCheck.randoCheckType == RCTYPE_FREESTANDING && saveInfo.randoSaveOptions[RO_SHUFFLE_FREESTANDING_ITEMS] == RO_GENERIC_NO) { continue; diff --git a/mm/2s2h/Rando/Logic/Regions/North.cpp b/mm/2s2h/Rando/Logic/Regions/North.cpp index aa827aa76..df559e67d 100644 --- a/mm/2s2h/Rando/Logic/Regions/North.cpp +++ b/mm/2s2h/Rando/Logic/Regions/North.cpp @@ -264,6 +264,15 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_MOUNTAIN_VILLAGE_SPRING_GRASS_28, RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE]), CHECK(RC_MOUNTAIN_VILLAGE_SPRING_GRASS_29, RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE]), CHECK(RC_MOUNTAIN_VILLAGE_SPRING_GRASS_30, RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE]), + CHECK(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_01, RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE] && HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_02, RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE] && HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_03, RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE] && HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_04, RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE] && HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_05, RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE] && HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_06, RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE] && HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_07, RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE] && HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_08, RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE] && HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_09, RANDO_EVENTS[RE_CLEARED_SNOWHEAD_TEMPLE] && HAS_ITEM(ITEM_DEKU_STICK)), CHECK(RC_MOUNTAIN_VILLAGE_LARGE_SNOWBALL_01, CanKillEnemy(ACTOR_OBJ_SNOWBALL)), CHECK(RC_MOUNTAIN_VILLAGE_LARGE_SNOWBALL_02, CanKillEnemy(ACTOR_OBJ_SNOWBALL)), CHECK(RC_MOUNTAIN_VILLAGE_LARGE_SNOWBALL_03, (FIRST_DAY() || SECOND_DAY()) && CanKillEnemy(ACTOR_OBJ_SNOWBALL)), // Goron Elder inside on Final Day diff --git a/mm/2s2h/Rando/Logic/Regions/South.cpp b/mm/2s2h/Rando/Logic/Regions/South.cpp index 7bca9c8b8..323ae3276 100644 --- a/mm/2s2h/Rando/Logic/Regions/South.cpp +++ b/mm/2s2h/Rando/Logic/Regions/South.cpp @@ -70,6 +70,10 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_GRASS_10, true), CHECK(RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_GRASS_11, true), CHECK(RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_GRASS_12, true), + CHECK(RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_BUTTERFLY_01, HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_BUTTERFLY_02, HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_BUTTERFLY_03, HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_BUTTERFLY_04, HAS_ITEM(ITEM_DEKU_STICK)), // TODO: Bean salesman check }, .exits = { // TO FROM diff --git a/mm/2s2h/Rando/Logic/Regions/TerminaField.cpp b/mm/2s2h/Rando/Logic/Regions/TerminaField.cpp index 79276189a..0c1df9df8 100644 --- a/mm/2s2h/Rando/Logic/Regions/TerminaField.cpp +++ b/mm/2s2h/Rando/Logic/Regions/TerminaField.cpp @@ -112,6 +112,9 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_TERMINA_FIELD_COW_GROTTO_GRASS_70, true), CHECK(RC_TERMINA_FIELD_COW_GROTTO_GRASS_71, true), CHECK(RC_TERMINA_FIELD_COW_GROTTO_GRASS_72, true), + CHECK(RC_TERMINA_FIELD_COW_GROTTO_BUTTERFLY_01, HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_TERMINA_FIELD_COW_GROTTO_BUTTERFLY_02, HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_TERMINA_FIELD_COW_GROTTO_BUTTERFLY_03, HAS_ITEM(ITEM_DEKU_STICK)), CHECK(RC_ENEMY_DROP_GIANT_BEE, CAN_USE_PROJECTILE && CanKillEnemy(ACTOR_EN_BEE)), // In a beehive CHECK(RC_TERMINA_FIELD_COW_GROTTO_BEEHIVE, CAN_USE_PROJECTILE), }, @@ -156,6 +159,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_TERMINA_FIELD_GOSSIP_STONE_GROTTO_3_GRASS_03, true), CHECK(RC_TERMINA_FIELD_GOSSIP_STONE_GROTTO_3_GRASS_04, true), CHECK(RC_TERMINA_FIELD_GOSSIP_STONE_GROTTO_3_GRASS_05, true), + CHECK(RC_TERMINA_FIELD_GOSSIP_STONE_GROTTO_3_BUTTERFLY_01, HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_TERMINA_FIELD_GOSSIP_STONE_GROTTO_3_BUTTERFLY_02, HAS_ITEM(ITEM_DEKU_STICK)), CHECK(RC_ENEMY_DROP_GIANT_BEE, CAN_USE_PROJECTILE && CanKillEnemy(ACTOR_EN_BEE)), // In a beehive CHECK(RC_TERMINA_FIELD_GOSSIP_STONE_GROTTO_3_BEEHIVE, CAN_USE_PROJECTILE), }, @@ -515,6 +520,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_ENEMY_DROP_EENO, CanKillEnemy(ACTOR_EN_SNOWMAN) && IS_NIGHT()), // Night only CHECK(RC_ENEMY_DROP_BAD_BAT, CanKillEnemy(ACTOR_EN_BAT)), CHECK(RC_ENEMY_DROP_TAKKURI, CanKillEnemy(ACTOR_EN_THIEFBIRD)), + CHECK(RC_TERMINA_FIELD_BUTTERFLY_01, HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_TERMINA_FIELD_BUTTERFLY_02, HAS_ITEM(ITEM_DEKU_STICK)), }, .exits = { // TO FROM EXIT(ENTRANCE(GROTTOS, 0), ENTRANCE(TERMINA_FIELD, 0), CAN_USE_EXPLOSIVE || CAN_BE_GORON), // Gossip Stone #3 diff --git a/mm/2s2h/Rando/Logic/Regions/West.cpp b/mm/2s2h/Rando/Logic/Regions/West.cpp index a2585095b..bf9d932e2 100644 --- a/mm/2s2h/Rando/Logic/Regions/West.cpp +++ b/mm/2s2h/Rando/Logic/Regions/West.cpp @@ -92,6 +92,9 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_GREAT_BAY_COAST_COW_GROTTO_GRASS_70, true), CHECK(RC_GREAT_BAY_COAST_COW_GROTTO_GRASS_71, true), CHECK(RC_GREAT_BAY_COAST_COW_GROTTO_GRASS_72, true), + CHECK(RC_GREAT_BAY_COAST_COW_GROTTO_BUTTERFLY_01, HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_GREAT_BAY_COAST_COW_GROTTO_BUTTERFLY_02, HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_GREAT_BAY_COAST_COW_GROTTO_BUTTERFLY_03, HAS_ITEM(ITEM_DEKU_STICK)), CHECK(RC_ENEMY_DROP_GIANT_BEE, CAN_USE_PROJECTILE && CanKillEnemy(ACTOR_EN_BEE)), // In a beehive CHECK(RC_GREAT_BAY_COAST_COW_GROTTO_BEEHIVE, CAN_USE_PROJECTILE), }, @@ -132,6 +135,8 @@ static RegisterShipInitFunc initFunc([]() { CHECK(RC_GREAT_BAY_COAST_GRASS_03, true), CHECK(RC_GREAT_BAY_COAST_GRASS_04, true), CHECK(RC_GREAT_BAY_COAST_GRASS_05, true), + CHECK(RC_GREAT_BAY_COAST_BUTTERFLY_01, HAS_ITEM(ITEM_DEKU_STICK)), + CHECK(RC_GREAT_BAY_COAST_BUTTERFLY_02, HAS_ITEM(ITEM_DEKU_STICK)), CHECK(RC_GREAT_BAY_COAST_TREE_02, true), CHECK(RC_GREAT_BAY_COAST_TREE_03, true), CHECK(RC_GREAT_BAY_COAST_TREE_04, true), diff --git a/mm/2s2h/Rando/Menu.cpp b/mm/2s2h/Rando/Menu.cpp index 2b1df7727..22b7fe6fd 100644 --- a/mm/2s2h/Rando/Menu.cpp +++ b/mm/2s2h/Rando/Menu.cpp @@ -541,6 +541,7 @@ static void DrawShufflesTab() { CVarCheckbox("Shuffle Snowball Drops", Rando::StaticData::Options[RO_SHUFFLE_SNOWBALL_DROPS].cvar); CVarCheckbox("Shuffle Grass Drops", Rando::StaticData::Options[RO_SHUFFLE_GRASS_DROPS].cvar); CVarCheckbox("Shuffle Tree Drops", Rando::StaticData::Options[RO_SHUFFLE_TREE_DROPS].cvar); + CVarCheckbox("Shuffle Butterflies", Rando::StaticData::Options[RO_SHUFFLE_BUTTERFLIES].cvar); CVarCheckbox("Shuffle Frogs", Rando::StaticData::Options[RO_SHUFFLE_FROGS].cvar); CVarCheckbox("Shuffle Hive Drops", Rando::StaticData::Options[RO_SHUFFLE_HIVE_DROPS].cvar); CVarCheckbox("Shuffle Freestanding Items", Rando::StaticData::Options[RO_SHUFFLE_FREESTANDING_ITEMS].cvar); diff --git a/mm/2s2h/Rando/StaticData/Checks.cpp b/mm/2s2h/Rando/StaticData/Checks.cpp index 526dbced4..d270063da 100644 --- a/mm/2s2h/Rando/StaticData/Checks.cpp +++ b/mm/2s2h/Rando/StaticData/Checks.cpp @@ -2367,6 +2367,33 @@ std::map<RandoCheckId, RandoStaticCheck> Checks = { RC(RC_ZORA_CAPE_TREE_03, RCTYPE_TREE, SCENE_31MISAKI, FLAG_NONE, 0x0, RI_NONE), RC(RC_ZORA_CAPE_TREE_04, RCTYPE_TREE, SCENE_31MISAKI, FLAG_NONE, 0x0, RI_NONE), RC(RC_ZORA_CAPE_TREE_05, RCTYPE_TREE, SCENE_31MISAKI, FLAG_NONE, 0x0, RI_NONE), + + // Butterflies // + RC(RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_BUTTERFLY_01, RCTYPE_BUTTERFLY, SCENE_KAKUSIANA, FLAG_NONE, 0x0, RI_NONE), + RC(RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_BUTTERFLY_02, RCTYPE_BUTTERFLY, SCENE_KAKUSIANA, FLAG_NONE, 0x0, RI_NONE), + RC(RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_BUTTERFLY_03, RCTYPE_BUTTERFLY, SCENE_KAKUSIANA, FLAG_NONE, 0x0, RI_NONE), + RC(RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_BUTTERFLY_04, RCTYPE_BUTTERFLY, SCENE_KAKUSIANA, FLAG_NONE, 0x0, RI_NONE), + RC(RC_GREAT_BAY_COAST_BUTTERFLY_01, RCTYPE_BUTTERFLY, SCENE_30GYOSON, FLAG_NONE, 0x0, RI_NONE), + RC(RC_GREAT_BAY_COAST_BUTTERFLY_02, RCTYPE_BUTTERFLY, SCENE_30GYOSON, FLAG_NONE, 0x0, RI_NONE), + RC(RC_GREAT_BAY_COAST_COW_GROTTO_BUTTERFLY_01, RCTYPE_BUTTERFLY, SCENE_KAKUSIANA, FLAG_NONE, 0x0, RI_NONE), + RC(RC_GREAT_BAY_COAST_COW_GROTTO_BUTTERFLY_02, RCTYPE_BUTTERFLY, SCENE_KAKUSIANA, FLAG_NONE, 0x0, RI_NONE), + RC(RC_GREAT_BAY_COAST_COW_GROTTO_BUTTERFLY_03, RCTYPE_BUTTERFLY, SCENE_KAKUSIANA, FLAG_NONE, 0x0, RI_NONE), + RC(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_01, RCTYPE_BUTTERFLY, SCENE_10YUKIYAMANOMURA2, FLAG_NONE, 0x0, RI_NONE), + RC(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_02, RCTYPE_BUTTERFLY, SCENE_10YUKIYAMANOMURA2, FLAG_NONE, 0x0, RI_NONE), + RC(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_03, RCTYPE_BUTTERFLY, SCENE_10YUKIYAMANOMURA2, FLAG_NONE, 0x0, RI_NONE), + RC(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_04, RCTYPE_BUTTERFLY, SCENE_10YUKIYAMANOMURA2, FLAG_NONE, 0x0, RI_NONE), + RC(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_05, RCTYPE_BUTTERFLY, SCENE_10YUKIYAMANOMURA2, FLAG_NONE, 0x0, RI_NONE), + RC(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_06, RCTYPE_BUTTERFLY, SCENE_10YUKIYAMANOMURA2, FLAG_NONE, 0x0, RI_NONE), + RC(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_07, RCTYPE_BUTTERFLY, SCENE_10YUKIYAMANOMURA2, FLAG_NONE, 0x0, RI_NONE), + RC(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_08, RCTYPE_BUTTERFLY, SCENE_10YUKIYAMANOMURA2, FLAG_NONE, 0x0, RI_NONE), + RC(RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_09, RCTYPE_BUTTERFLY, SCENE_10YUKIYAMANOMURA2, FLAG_NONE, 0x0, RI_NONE), + RC(RC_TERMINA_FIELD_BUTTERFLY_01, RCTYPE_BUTTERFLY, SCENE_00KEIKOKU, FLAG_NONE, 0x0, RI_NONE), + RC(RC_TERMINA_FIELD_BUTTERFLY_02, RCTYPE_BUTTERFLY, SCENE_00KEIKOKU, FLAG_NONE, 0x0, RI_NONE), + RC(RC_TERMINA_FIELD_COW_GROTTO_BUTTERFLY_01, RCTYPE_BUTTERFLY, SCENE_KAKUSIANA, FLAG_NONE, 0x0, RI_NONE), + RC(RC_TERMINA_FIELD_COW_GROTTO_BUTTERFLY_02, RCTYPE_BUTTERFLY, SCENE_KAKUSIANA, FLAG_NONE, 0x0, RI_NONE), + RC(RC_TERMINA_FIELD_COW_GROTTO_BUTTERFLY_03, RCTYPE_BUTTERFLY, SCENE_KAKUSIANA, FLAG_NONE, 0x0, RI_NONE), + RC(RC_TERMINA_FIELD_GOSSIP_STONE_GROTTO_3_BUTTERFLY_01, RCTYPE_BUTTERFLY, SCENE_KAKUSIANA, FLAG_NONE, 0x0, RI_NONE), + RC(RC_TERMINA_FIELD_GOSSIP_STONE_GROTTO_3_BUTTERFLY_02, RCTYPE_BUTTERFLY, SCENE_KAKUSIANA, FLAG_NONE, 0x0, RI_NONE), }; // clang-format on diff --git a/mm/2s2h/Rando/StaticData/Options.cpp b/mm/2s2h/Rando/StaticData/Options.cpp index 5db59ab6b..a96579934 100644 --- a/mm/2s2h/Rando/StaticData/Options.cpp +++ b/mm/2s2h/Rando/StaticData/Options.cpp @@ -44,6 +44,7 @@ std::map<RandoOptionId, RandoStaticOption> Options = { RO(RO_SHUFFLE_BARREL_DROPS, RO_GENERIC_OFF), RO(RO_SHUFFLE_BOSS_REMAINS, RO_GENERIC_OFF), RO(RO_SHUFFLE_BOSS_SOULS, RO_GENERIC_OFF), + RO(RO_SHUFFLE_BUTTERFLIES, RO_GENERIC_OFF), RO(RO_SHUFFLE_COWS, RO_GENERIC_OFF), RO(RO_SHUFFLE_CRATE_DROPS, RO_GENERIC_OFF), RO(RO_SHUFFLE_ENEMY_DROPS, RO_GENERIC_OFF), diff --git a/mm/2s2h/Rando/Types.h b/mm/2s2h/Rando/Types.h index f85437cfa..5abd92850 100644 --- a/mm/2s2h/Rando/Types.h +++ b/mm/2s2h/Rando/Types.h @@ -4,6 +4,7 @@ typedef enum { RCTYPE_UNKNOWN, RCTYPE_BARREL, + RCTYPE_BUTTERFLY, RCTYPE_CHEST, RCTYPE_COW, RCTYPE_CRATE, @@ -2371,6 +2372,32 @@ typedef enum { RC_ZORA_CAPE_TREE_04, RC_ZORA_CAPE_TREE_05, + RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_BUTTERFLY_01, + RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_BUTTERFLY_02, + RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_BUTTERFLY_03, + RC_DEKU_PALACE_BEAN_SALESMAN_GROTTO_BUTTERFLY_04, + RC_GREAT_BAY_COAST_BUTTERFLY_01, + RC_GREAT_BAY_COAST_BUTTERFLY_02, + RC_GREAT_BAY_COAST_COW_GROTTO_BUTTERFLY_01, + RC_GREAT_BAY_COAST_COW_GROTTO_BUTTERFLY_02, + RC_GREAT_BAY_COAST_COW_GROTTO_BUTTERFLY_03, + RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_01, + RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_02, + RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_03, + RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_04, + RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_05, + RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_06, + RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_07, + RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_08, + RC_MOUNTAIN_VILLAGE_SPRING_BUTTERFLY_09, + RC_TERMINA_FIELD_BUTTERFLY_01, + RC_TERMINA_FIELD_BUTTERFLY_02, + RC_TERMINA_FIELD_COW_GROTTO_BUTTERFLY_01, + RC_TERMINA_FIELD_COW_GROTTO_BUTTERFLY_02, + RC_TERMINA_FIELD_COW_GROTTO_BUTTERFLY_03, + RC_TERMINA_FIELD_GOSSIP_STONE_GROTTO_3_BUTTERFLY_01, + RC_TERMINA_FIELD_GOSSIP_STONE_GROTTO_3_BUTTERFLY_02, + RC_MAX, } RandoCheckId; @@ -2979,6 +3006,7 @@ typedef enum { RO_SHUFFLE_BARREL_DROPS, RO_SHUFFLE_BOSS_REMAINS, RO_SHUFFLE_BOSS_SOULS, + RO_SHUFFLE_BUTTERFLIES, RO_SHUFFLE_COWS, RO_SHUFFLE_CRATE_DROPS, RO_SHUFFLE_ENEMY_DROPS, |
