[Rando] Shuffle Butterfly Fairies (#6430)

This commit is contained in:
A Green Spoon
2026-03-29 16:54:01 +09:00
committed by GitHub
parent e0a1b23525
commit a461d8f6fb
22 changed files with 145 additions and 20 deletions

View File

@@ -2170,6 +2170,14 @@ typedef enum {
// - `*BossVa`
VB_SPAWN_BLUE_WARP,
// #### `result`
// ```c
// this->timer == 4
// ```
// #### `args`
// - `*EnButte`
VB_SPAWN_BUTTERFLY_FAIRY,
// #### `result`
// ```c
// INV_CONTENT(ITEM_ARROW_FIRE) == ITEM_NONE

View File

@@ -2135,6 +2135,30 @@ void StaticData::HintTable_Init_Exclude_Overworld() {
HintText(CustomMessage("They say that a #bush in Zora's Fountain# contains #[[1]]#.",
/*german*/ "",
/*french*/ "Selon moi, un #buisson à la Fontaine Zora# cache #[[1]]#.", { QM_RED, QM_GREEN }));
hintTextTable[RHT_BUTTERFLY_FAIRY_HYRULE_CASTLE] = HintText(CustomMessage("They say that a #butterfly near the castle# reveals #[[1]]#.",
/*german*/ "Man erzählt sich, daß ein #Schmetterling in der Nähe des Schlosses# #[[1]]# enthülle.",
/*french*/ "Selon moi, une #un papillon près du château# révèle #[[1]]#.", {QM_RED, QM_GREEN}));
hintTextTable[RHT_BUTTERFLY_FAIRY_LOST_WOODS] = HintText(CustomMessage("They say that a #butterfly in the woods# reveals #[[1]]#.",
/*german*/ "Man erzählt sich, daß ein #Schmetterling im Wald# #[[1]]# enthülle.",
/*french*/ "Selon moi, une #un papillon dans les bois# révèle #[[1]]#.", {QM_RED, QM_GREEN}));
hintTextTable[RHT_BUTTERFLY_FAIRY_GRAVEYARD] = HintText(CustomMessage("They say that a #butterfly in the graveyard# reveals #[[1]]#.",
/*german*/ "Man erzählt sich, daß ein #Schmetterling auf dem Friedhof# #[[1]]# enthülle.",
/*french*/ "Selon moi, une #un papillon dans le cimetière# révèle #[[1]]#.", {QM_RED, QM_GREEN}));
hintTextTable[RHT_BUTTERFLY_FAIRY_ZORAS_RIVER] = HintText(CustomMessage("They say that a #butterfly near a river# reveals #[[1]]#.",
/*german*/ "Man erzählt sich, daß ein #Schmetterling in der Nähe eines Flusses# #[[1]]# enthülle.",
/*french*/ "Selon moi, une #un papillon près d'une rivière# révèle #[[1]]#.", {QM_RED, QM_GREEN}));
hintTextTable[RHT_BUTTERFLY_FAIRY_ZORAS_FOUNTAIN] = HintText(CustomMessage("They say that a #butterfly on a log# reveals #[[1]]#.",
/*german*/ "Man erzählt sich, daß ein #Schmetterling auf einem Baumstamm# #[[1]]# enthülle.",
/*french*/ "Selon moi, une #un papillon sur une bûche# révèle #[[1]]#.", {QM_RED, QM_GREEN}));
hintTextTable[RHT_BUTTERFLY_FAIRY_LAKE_HYLIA] = HintText(CustomMessage("They say that a #butterfly near a lake# reveals #[[1]]#.",
/*german*/ "Man erzählt sich, daß ein #Schmetterling in der Nähe eines Sees# #[[1]]# enthülle.",
/*french*/ "Selon moi, une #un papillon près d'un lac# révèle #[[1]]#.", {QM_RED, QM_GREEN}));
// clang-format on
}
} // namespace Rando

View File

@@ -211,6 +211,7 @@ void Context::GenerateLocationPool() {
(location.GetRCType() == RCTYPE_STONE_FAIRY && !mOptions[RSK_SHUFFLE_STONE_FAIRIES]) ||
(location.GetRCType() == RCTYPE_BEAN_FAIRY && !mOptions[RSK_SHUFFLE_BEAN_FAIRIES]) ||
(location.GetRCType() == RCTYPE_SONG_FAIRY && !mOptions[RSK_SHUFFLE_SONG_FAIRIES]) ||
(location.GetRCType() == RCTYPE_BUTTERFLY_FAIRY && !mOptions[RSK_SHUFFLE_BUTTERFLY_FAIRIES]) ||
(location.GetRCType() == RCTYPE_TREE && !mOptions[RSK_SHUFFLE_TREES]) ||
(location.GetRCType() == RCTYPE_NLTREE &&
(!mOptions[RSK_SHUFFLE_TREES] || mOptions[RSK_LOGIC_RULES].IsNot(RO_LOGIC_NO_LOGIC))) ||

View File

@@ -11,6 +11,7 @@ extern "C" {
#include "src/overlays/actors/ovl_Obj_Bean/z_obj_bean.h"
#include "src/overlays/actors/ovl_En_Gs/z_en_gs.h"
#include "src/overlays/actors/ovl_Shot_Sun/z_shot_sun.h"
#include "src/overlays/actors/ovl_En_Butte/z_en_butte.h"
}
#define FAIRY_FLAG_TIMED (1 << 8)
@@ -53,7 +54,7 @@ bool ShuffleFairies_FairyExists(CheckIdentity fairyIdentity) {
return false;
}
CheckIdentity ShuffleFairies_GetFairyIdentity(int32_t params) {
CheckIdentity ShuffleFairies_GetFairyIdentity(int32_t params, ActorID id) {
CheckIdentity fairyIdentity;
s16 sceneNum = gPlayState->sceneNum;
fairyIdentity.randomizerInf = RAND_INF_MAX;
@@ -62,8 +63,7 @@ CheckIdentity ShuffleFairies_GetFairyIdentity(int32_t params) {
sceneNum = SCENE_TEMPLE_OF_TIME_EXTERIOR_DAY;
}
Rando::Location* location =
OTRGlobals::Instance->gRandomizer->GetCheckObjectFromActor(ACTOR_EN_ELF, sceneNum, params);
Rando::Location* location = OTRGlobals::Instance->gRandomizer->GetCheckObjectFromActor(id, sceneNum, params);
if (location->GetRandomizerCheck() == RC_UNKNOWN_CHECK) {
LUSLOG_WARN("FairyGetIdentity did not receive a valid RC value (%d).", location->GetRandomizerCheck());
@@ -76,8 +76,8 @@ CheckIdentity ShuffleFairies_GetFairyIdentity(int32_t params) {
return fairyIdentity;
}
static bool SpawnFairy(f32 posX, f32 posY, f32 posZ, int32_t params, FairyType fairyType) {
CheckIdentity fairyIdentity = ShuffleFairies_GetFairyIdentity(params);
static bool SpawnFairy(f32 posX, f32 posY, f32 posZ, int32_t params, FairyType fairyType, ActorID id) {
CheckIdentity fairyIdentity = ShuffleFairies_GetFairyIdentity(params, id);
if (!Flags_GetRandomizerInf(fairyIdentity.randomizerInf)) {
Actor* fairy =
Actor_Spawn(&gPlayState->actorCtx, gPlayState, ACTOR_EN_ELF, posX, posY - 30.0f, posZ, 0, 0, 0, fairyType);
@@ -93,7 +93,9 @@ void RegisterShuffleFairies() {
bool shouldRegisterStone = IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_STONE_FAIRIES);
bool shouldRegisterBean = IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_BEAN_FAIRIES);
bool shouldRegisterSong = IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_SONG_FAIRIES);
bool shouldRegister = shouldRegisterFountain || shouldRegisterStone || shouldRegisterBean || shouldRegisterSong;
bool shouldRegisterButterfly = IS_RANDO && RAND_GET_OPTION(RSK_SHUFFLE_BUTTERFLY_FAIRIES);
bool shouldRegister = shouldRegisterFountain || shouldRegisterStone || shouldRegisterBean || shouldRegisterSong ||
shouldRegisterButterfly;
// Grant item when picking up fairy.
COND_VB_SHOULD(VB_FAIRY_HEAL, shouldRegister, {
@@ -123,7 +125,8 @@ void RegisterShuffleFairies() {
s16 grottoId = (gPlayState->sceneNum == SCENE_FAIRYS_FOUNTAIN) ? Grotto_CurrentGrotto() : 0;
for (s16 index = 0; index < 8; index++) {
int32_t params = (grottoId << 8) | index;
if (SpawnFairy(actor->world.pos.x, actor->world.pos.y, actor->world.pos.z, params, FAIRY_HEAL)) {
if (SpawnFairy(actor->world.pos.x, actor->world.pos.y, actor->world.pos.z, params, FAIRY_HEAL,
ACTOR_EN_ELF)) {
fairySpawned = true;
}
}
@@ -139,7 +142,7 @@ void RegisterShuffleFairies() {
for (s16 index = 0; index < 3; index++) {
int32_t params = ((objBean->dyna.actor.params & 0x3F) << 8) | index;
if (SpawnFairy(objBean->dyna.actor.world.pos.x, objBean->dyna.actor.world.pos.y,
objBean->dyna.actor.world.pos.z, params, FAIRY_HEAL)) {
objBean->dyna.actor.world.pos.z, params, FAIRY_HEAL, ACTOR_EN_ELF)) {
fairySpawned = true;
}
}
@@ -152,7 +155,7 @@ void RegisterShuffleFairies() {
COND_VB_SHOULD(VB_SPAWN_SONG_FAIRY, shouldRegisterSong, {
ShotSun* shotSun = va_arg(args, ShotSun*);
if (SpawnFairy(shotSun->actor.world.pos.x, shotSun->actor.world.pos.y, shotSun->actor.world.pos.z,
TWO_ACTOR_PARAMS(0x1000, (int32_t)shotSun->actor.world.pos.z), FAIRY_HEAL_BIG)) {
TWO_ACTOR_PARAMS(0x1000, (int32_t)shotSun->actor.world.pos.z), FAIRY_HEAL_BIG, ACTOR_EN_ELF)) {
*should = false;
}
});
@@ -185,11 +188,11 @@ void RegisterShuffleFairies() {
// stop spawning the vanilla fairy as well when these fairies exist, otherwise both
// the randomized and the vanilla fairy will spawn. When the randomized fairy is already
// collected, the vanilla code will handle that part automatically.
CheckIdentity fairyIdentity = ShuffleFairies_GetFairyIdentity(params);
CheckIdentity fairyIdentity = ShuffleFairies_GetFairyIdentity(params, ACTOR_EN_ELF);
if (!ShuffleFairies_FairyExists(fairyIdentity)) {
Player* player = GET_PLAYER(gPlayState);
if (SpawnFairy(player->actor.world.pos.x, (player->actor.world.pos.y + 20), player->actor.world.pos.z,
params, fairyType)) {
params, fairyType, ACTOR_EN_ELF)) {
Audio_PlayActorSound2(&gossipStone->actor, NA_SE_EV_BUTTERFRY_TO_FAIRY);
// Set vanilla check for fairy spawned so it doesn't spawn the vanilla fairy afterwards as well.
gossipStone->unk_19D = 0;
@@ -200,6 +203,18 @@ void RegisterShuffleFairies() {
}
}
});
// Spawn a fairy from a butterfly
COND_VB_SHOULD(VB_SPAWN_BUTTERFLY_FAIRY, shouldRegisterButterfly, {
if (*should) {
EnButte* enButte = va_arg(args, EnButte*);
if (SpawnFairy(enButte->actor.focus.pos.x, enButte->actor.focus.pos.y, enButte->actor.focus.pos.z,
TWO_ACTOR_PARAMS(enButte->actor.params, (int32_t)enButte->actor.home.pos.y), FAIRY_HEAL,
ACTOR_EN_BUTTE)) {
*should = false;
}
}
});
}
void Rando::StaticData::RegisterFairyLocations() {
@@ -406,6 +421,17 @@ void Rando::StaticData::RegisterFairyLocations() {
locationTable[RC_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY] = Location::SongFairy(RC_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY, RCQUEST_BOTH, RCAREA_LOST_WOODS, SCENE_GROTTOS, TWO_ACTOR_PARAMS(0x1000, 741), "Deku Scrub Grotto Sun's Song Fairy", RHT_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY));
locationTable[RC_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY] = Location::SongFairy(RC_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_ROYAL_FAMILYS_TOMB, TWO_ACTOR_PARAMS(0x1000, 1476), "Royal Family's Tomb Sun's Song Fairy", RHT_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY));
locationTable[RC_HC_NEAR_WALL_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_HC_NEAR_WALL_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(1, 1476), "Near Wall Butterfly Fairy", RHT_BUTTERFLY_FAIRY_HYRULE_CASTLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_NEAR_WALL_BUTTERFLY_FAIRY));
locationTable[RC_HC_NEAR_STAIRS_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_HC_NEAR_STAIRS_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(1, 1493), "Near Stairs Butterfly Fairy", RHT_BUTTERFLY_FAIRY_HYRULE_CASTLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_NEAR_STAIRS_BUTTERFLY_FAIRY));
locationTable[RC_HC_NEAR_BOULDER_PATH_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_HC_NEAR_BOULDER_PATH_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(1, 1413), "Near Boulder Path Butterfly Fairy", RHT_BUTTERFLY_FAIRY_HYRULE_CASTLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_NEAR_BOULDER_PATH_BUTTERFLY_FAIRY));
locationTable[RC_HC_NEAR_ARCHWAY_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_HC_NEAR_ARCHWAY_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_HYRULE_CASTLE, SCENE_HYRULE_CASTLE, TWO_ACTOR_PARAMS(1, 1478), "Near Archway Butterfly Fairy", RHT_BUTTERFLY_FAIRY_HYRULE_CASTLE, SpoilerCollectionCheck::RandomizerInf(RAND_INF_HC_NEAR_ARCHWAY_BUTTERFLY_FAIRY));
locationTable[RC_LW_MEADOW_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_LW_MEADOW_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_LOST_WOODS, SCENE_LOST_WOODS, TWO_ACTOR_PARAMS(1, 28), "Meadow Butterfly Fairy", RHT_BUTTERFLY_FAIRY_LOST_WOODS, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LW_MEADOW_BUTTERFLY_FAIRY));
locationTable[RC_GY_NEAR_HUT_GRAVE_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_GY_NEAR_HUT_GRAVE_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_GRAVEYARD, SCENE_GRAVEYARD, TWO_ACTOR_PARAMS(1, 137), "Grave Butterfly Fairy", RHT_BUTTERFLY_FAIRY_GRAVEYARD, SpoilerCollectionCheck::RandomizerInf(RAND_INF_GY_NEAR_HUT_GRAVE_BUTTERFLY_FAIRY));
locationTable[RC_ZR_NEAR_ROCK_CIRCLE_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_ZR_NEAR_ROCK_CIRCLE_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(1, 164), "Near Rock Circle Butterfly Fairy", RHT_BUTTERFLY_FAIRY_ZORAS_RIVER, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_NEAR_ROCK_CIRCLE_BUTTERFLY_FAIRY));
locationTable[RC_ZR_WATERFALL_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_ZR_WATERFALL_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_ZORAS_RIVER, SCENE_ZORAS_RIVER, TWO_ACTOR_PARAMS(1, 1010), "Waterfall Butterfly Fairy", RHT_BUTTERFLY_FAIRY_ZORAS_RIVER, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZR_WATERFALL_BUTTERFLY_FAIRY));
locationTable[RC_ZF_LOG_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_ZF_LOG_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_ZORAS_FOUNTAIN, SCENE_ZORAS_FOUNTAIN, TWO_ACTOR_PARAMS(1, 169), "Log Butterfly Fairy", RHT_BUTTERFLY_FAIRY_ZORAS_FOUNTAIN, SpoilerCollectionCheck::RandomizerInf(RAND_INF_ZF_LOG_BUTTERFLY_FAIRY));
locationTable[RC_LH_SCARECROW_BUTTERFLY_FAIRY] = Location::ButterflyFairy(RC_LH_SCARECROW_BUTTERFLY_FAIRY, RCQUEST_BOTH, RCAREA_LAKE_HYLIA, SCENE_LAKE_HYLIA, TWO_ACTOR_PARAMS(1, -1254), "Scarecrow Butterfly Fairy", RHT_BUTTERFLY_FAIRY_LAKE_HYLIA, SpoilerCollectionCheck::RandomizerInf(RAND_INF_LH_SCARECROW_BUTTERFLY_FAIRY));
locationTable[RC_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY] = Location::SongFairy(RC_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY, RCQUEST_VANILLA,RCAREA_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(0x1000, -1896), "After Boulder Room Sun's Song Fairy", RHT_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY));
locationTable[RC_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY] = Location::SongFairy(RC_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY, RCQUEST_VANILLA,RCAREA_SPIRIT_TEMPLE, SCENE_SPIRIT_TEMPLE, TWO_ACTOR_PARAMS(0x1000, -220), "Four Armos Room Sun's Song Fairy", RHT_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY));
locationTable[RC_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY] = Location::SongFairy(RC_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY, RCQUEST_VANILLA,RCAREA_SHADOW_TEMPLE, SCENE_SHADOW_TEMPLE, TWO_ACTOR_PARAMS(0x1000, 54), "Beamos Song of Storms Fairy", RHT_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY, SpoilerCollectionCheck::RandomizerInf(RAND_INF_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY));

View File

@@ -630,6 +630,15 @@ Rando::Location Rando::Location::SongFairy(RandomizerCheck rc, RandomizerCheckQu
false, collectionCheck };
}
Rando::Location Rando::Location::ButterflyFairy(RandomizerCheck rc, RandomizerCheckQuest quest_,
RandomizerCheckArea area_, SceneID scene_, int32_t actorParams_,
std::string&& shortName_, RandomizerHintTextKey hintKey,
SpoilerCollectionCheck collectionCheck) {
return { rc, quest_, RCTYPE_BUTTERFLY_FAIRY, area_, ACTOR_EN_BUTTE,
scene_, actorParams_, std::move(shortName_), hintKey, RG_NONE,
false, collectionCheck };
}
Rando::Location Rando::Location::Grass(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_,
SceneID scene_, int32_t actorParams_, std::string&& shortName_,
RandomizerHintTextKey hintKey, RandomizerGet vanillaItem,

View File

@@ -282,6 +282,10 @@ class Location {
SceneID scene_, int32_t actorParams_, std::string&& shortName_,
RandomizerHintTextKey hintKey, SpoilerCollectionCheck collectionCheck);
static Location ButterflyFairy(RandomizerCheck rc, RandomizerCheckQuest quest_, RandomizerCheckArea area_,
SceneID scene_, int32_t actorParams_, std::string&& shortName_,
RandomizerHintTextKey hintKey, SpoilerCollectionCheck collectionCheck);
static Location HintStone(RandomizerCheck rc, RandomizerCheckQuest quest_, SceneID scene_, int32_t actorParams_,
std::string&& shortName_);

View File

@@ -56,14 +56,18 @@ void RegionTable_Init_CastleGrounds() {
EVENT_ACCESS(LOGIC_FAIRY_ACCESS, logic->CanUse(RG_STICKS)),
}, {
//Locations
LOCATION(RC_HC_NEAR_GUARDS_TREE_1, logic->CanBonkTrees()),
LOCATION(RC_HC_NEAR_GUARDS_TREE_2, logic->CanBonkTrees()),
LOCATION(RC_HC_NEAR_GUARDS_TREE_3, logic->CanBonkTrees()),
LOCATION(RC_HC_NEAR_GUARDS_TREE_4, logic->CanBonkTrees()),
LOCATION(RC_HC_NEAR_GUARDS_TREE_5, logic->CanBonkTrees()),
LOCATION(RC_HC_NEAR_GUARDS_TREE_6, logic->CanBonkTrees()),
LOCATION(RC_HC_NL_TREE_1, false),
LOCATION(RC_HC_NL_TREE_2, false),
LOCATION(RC_HC_NEAR_GUARDS_TREE_1, logic->CanBonkTrees()),
LOCATION(RC_HC_NEAR_GUARDS_TREE_2, logic->CanBonkTrees()),
LOCATION(RC_HC_NEAR_GUARDS_TREE_3, logic->CanBonkTrees()),
LOCATION(RC_HC_NEAR_GUARDS_TREE_4, logic->CanBonkTrees()),
LOCATION(RC_HC_NEAR_GUARDS_TREE_5, logic->CanBonkTrees()),
LOCATION(RC_HC_NEAR_GUARDS_TREE_6, logic->CanBonkTrees()),
LOCATION(RC_HC_NL_TREE_1, false),
LOCATION(RC_HC_NL_TREE_2, false),
LOCATION(RC_HC_NEAR_WALL_BUTTERFLY_FAIRY, logic->CanUse(RG_STICKS)),
LOCATION(RC_HC_NEAR_STAIRS_BUTTERFLY_FAIRY, logic->CanUse(RG_STICKS)),
LOCATION(RC_HC_NEAR_BOULDER_PATH_BUTTERFLY_FAIRY, logic->CanUse(RG_STICKS)),
LOCATION(RC_HC_NEAR_ARCHWAY_BUTTERFLY_FAIRY, logic->CanUse(RG_STICKS)),
}, {
//Exits
ENTRANCE(RR_HC_GATE, true),

View File

@@ -32,6 +32,7 @@ void RegionTable_Init_Graveyard() {
LOCATION(RC_GY_GRASS_11, logic->CanCutShrubs()),
LOCATION(RC_GY_GRASS_12, logic->CanCutShrubs()),
LOCATION(RC_GRAVEYARD_CRATE, ((logic->IsAdult && CanPlantBean(RR_THE_GRAVEYARD, RG_GRAVEYARD_BEAN_SOUL)) || logic->CanUse(RG_LONGSHOT)) && logic->CanBreakCrates()),
LOCATION(RC_GY_NEAR_HUT_GRAVE_BUTTERFLY_FAIRY, logic->IsChild && logic->AtDay && logic->CanUse(RG_STICKS)),
}, {
//Exits
ENTRANCE(RR_GRAVEYARD_SHIELD_GRAVE, (logic->IsAdult || logic->AtNight) && logic->HasItem(RG_POWER_BRACELET)),

View File

@@ -79,6 +79,7 @@ void RegionTable_Init_LakeHylia() {
LOCATION(RC_LH_CHILD_GRASS_4, logic->IsChild && logic->CanCutShrubs()),
LOCATION(RC_LH_WARP_PAD_GRASS_1, logic->CanCutShrubs()),
LOCATION(RC_LH_WARP_PAD_GRASS_2, logic->CanCutShrubs()),
LOCATION(RC_LH_SCARECROW_BUTTERFLY_FAIRY, logic->IsChild && logic->CanUse(RG_STICKS)),
}, {
//Exits
ENTRANCE(RR_HF_TO_LAKE_HYLIA, true),

View File

@@ -93,6 +93,7 @@ void RegionTable_Init_LostWoods() {
LOCATION(RC_LW_GRASS_7, logic->CanCutShrubs()),
LOCATION(RC_LW_GRASS_8, logic->CanCutShrubs()),
LOCATION(RC_LW_GRASS_9, logic->CanCutShrubs()),
LOCATION(RC_LW_MEADOW_BUTTERFLY_FAIRY, logic->IsChild && logic->CanUse(RG_STICKS)),
}, {
//Exits
ENTRANCE(RR_LW_FOREST_EXIT, true),

View File

@@ -29,6 +29,7 @@ void RegionTable_Init_ZorasFountain() {
LOCATION(RC_ZF_BUSH_4, logic->IsChild),
LOCATION(RC_ZF_BUSH_5, logic->IsChild),
LOCATION(RC_ZF_BUSH_6, logic->IsChild),
LOCATION(RC_ZF_LOG_BUTTERFLY_FAIRY, logic->IsChild && logic->AtDay && logic->CanUse(RG_STICKS)),
}, {
//Exits
ENTRANCE(RR_ZD_BEHIND_KING_ZORA, true),

View File

@@ -56,6 +56,8 @@ void RegionTable_Init_ZoraRiver() {
LOCATION(RC_ZR_BENEATH_WATERFALL_RIGHT_RUPEE, logic->IsAdult && (logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS) || logic->CanUse(RG_BOOMERANG))),
LOCATION(RC_ZR_NEAR_DOMAIN_GOSSIP_STONE, true),
LOCATION(RC_ZR_NEAR_FREESTANDING_POH_GRASS, logic->CanUse(RG_BOOMERANG)),
LOCATION(RC_ZR_NEAR_ROCK_CIRCLE_BUTTERFLY_FAIRY, logic->IsChild && logic->CanUse(RG_STICKS)),
LOCATION(RC_ZR_WATERFALL_BUTTERFLY_FAIRY, logic->IsChild && logic->CanUse(RG_STICKS)),
}, {
//Exits
ENTRANCE(RR_ZR_FRONT, logic->IsAdult || logic->HasItem(RG_BRONZE_SCALE) || logic->HasItem(RG_POWER_BRACELET) || logic->BlastOrSmash() || logic->HasItem(RG_HOVER_BOOTS)),

View File

@@ -507,6 +507,7 @@ void Settings::CreateOptionDescriptions() {
"Shuffle fairy spots. These are spots where a big fairy is revealed by a song."
"\n"
"This excludes gossip stones and magic bean locations.";
mOptionDescriptions[RSK_SHUFFLE_BUTTERFLY_FAIRIES] = "Shuffle fairies from butterfly locations.";
mOptionDescriptions[RSK_SHUFFLE_GRASS] =
"Grass will drop a randomized item the first time they're cut and collected. "
"Grass will have a different appearance when they hold a randomized item.\n"

View File

@@ -2191,6 +2191,16 @@ RANDO_ENUM_ITEM(RC_LW_SHORTCUT_STORMS_FAIRY)
RANDO_ENUM_ITEM(RC_TH_KITCHEN_SUN_FAIRY)
RANDO_ENUM_ITEM(RC_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY)
RANDO_ENUM_ITEM(RC_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY)
RANDO_ENUM_ITEM(RC_HC_NEAR_WALL_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RC_HC_NEAR_STAIRS_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RC_HC_NEAR_BOULDER_PATH_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RC_HC_NEAR_ARCHWAY_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RC_LW_MEADOW_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RC_GY_NEAR_HUT_GRAVE_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RC_ZR_NEAR_ROCK_CIRCLE_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RC_ZR_WATERFALL_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RC_ZF_LOG_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RC_LH_SCARECROW_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RC_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY)
RANDO_ENUM_ITEM(RC_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY)
RANDO_ENUM_ITEM(RC_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY)

View File

@@ -1515,6 +1515,12 @@ RANDO_ENUM_ITEM(RHT_LW_SHORTCUT_STORMS_FAIRY)
RANDO_ENUM_ITEM(RHT_TH_KITCHEN_SUN_FAIRY)
RANDO_ENUM_ITEM(RHT_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY)
RANDO_ENUM_ITEM(RHT_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY)
RANDO_ENUM_ITEM(RHT_BUTTERFLY_FAIRY_HYRULE_CASTLE)
RANDO_ENUM_ITEM(RHT_BUTTERFLY_FAIRY_LOST_WOODS)
RANDO_ENUM_ITEM(RHT_BUTTERFLY_FAIRY_GRAVEYARD)
RANDO_ENUM_ITEM(RHT_BUTTERFLY_FAIRY_ZORAS_RIVER)
RANDO_ENUM_ITEM(RHT_BUTTERFLY_FAIRY_ZORAS_FOUNTAIN)
RANDO_ENUM_ITEM(RHT_BUTTERFLY_FAIRY_LAKE_HYLIA)
RANDO_ENUM_ITEM(RHT_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY)
RANDO_ENUM_ITEM(RHT_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY)
RANDO_ENUM_ITEM(RHT_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY)

View File

@@ -1680,6 +1680,18 @@ RANDO_ENUM_ITEM(RAND_INF_LW_SHORTCUT_STORMS_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_TH_KITCHEN_SUN_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_LW_DEKU_SCRUB_GROTTO_SUN_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_GRAVEYARD_ROYAL_FAMILYS_TOMB_SUN_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_HC_NEAR_WALL_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_HC_NEAR_STAIRS_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_HC_NEAR_BOULDER_PATH_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_HC_NEAR_ARCHWAY_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_LW_MEADOW_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_GY_NEAR_HUT_GRAVE_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_ZR_NEAR_ROCK_CIRCLE_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_ZR_WATERFALL_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_ZF_LOG_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_LH_SCARECROW_BUTTERFLY_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY)
RANDO_ENUM_ITEM(RAND_INF_SHADOW_TEMPLE_BEAMOS_STORM_FAIRY)

View File

@@ -128,6 +128,7 @@ RANDO_ENUM_ITEM(RCTYPE_FOUNTAIN_FAIRY) // Fairies in Fountains
RANDO_ENUM_ITEM(RCTYPE_STONE_FAIRY) // Fairies from Gossip Stones
RANDO_ENUM_ITEM(RCTYPE_BEAN_FAIRY) // Fairies from Beans
RANDO_ENUM_ITEM(RCTYPE_SONG_FAIRY) // Fairies from Songs
RANDO_ENUM_ITEM(RCTYPE_BUTTERFLY_FAIRY) // Fairies from Butterflies
RANDO_ENUM_ITEM(RCTYPE_GRASS) // Grass
RANDO_ENUM_END(RandomizerCheckType)

View File

@@ -241,6 +241,7 @@ RANDO_ENUM_ITEM(RSK_SHUFFLE_FOUNTAIN_FAIRIES)
RANDO_ENUM_ITEM(RSK_SHUFFLE_STONE_FAIRIES)
RANDO_ENUM_ITEM(RSK_SHUFFLE_BEAN_FAIRIES)
RANDO_ENUM_ITEM(RSK_SHUFFLE_SONG_FAIRIES)
RANDO_ENUM_ITEM(RSK_SHUFFLE_BUTTERFLY_FAIRIES)
RANDO_ENUM_ITEM(RSK_LOCK_OVERWORLD_DOORS)
RANDO_ENUM_ITEM(RSK_SHUFFLE_GRASS)
RANDO_ENUM_ITEM(RSK_ROCS_FEATHER)

View File

@@ -219,6 +219,8 @@ void RandomizerCheckObjects::UpdateImGuiVisibility() {
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleBeanFairies"), RO_GENERIC_NO)) &&
(location.GetRCType() != RCTYPE_SONG_FAIRY ||
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleFairySpots"), RO_GENERIC_NO)) &&
(location.GetRCType() != RCTYPE_BUTTERFLY_FAIRY ||
CVarGetInteger(CVAR_RANDOMIZER_SETTING("ShuffleButterflyFairies"), RO_GENERIC_NO)) &&
((location.GetRCType() != RCTYPE_MAP && location.GetRCType() != RCTYPE_COMPASS) ||
CVarGetInteger(CVAR_RANDOMIZER_SETTING("StartingMapsCompasses"), RO_DUNGEON_ITEM_LOC_OWN_DUNGEON) !=
RO_DUNGEON_ITEM_LOC_VANILLA) &&

View File

@@ -84,6 +84,7 @@ bool showFountainFairies;
bool showStoneFairies;
bool showBeanFairies;
bool showSongFairies;
bool showButterflyFairies;
bool showStartingMapsCompasses;
bool showKeysanity;
bool showGerudoFortressKeys;
@@ -1361,6 +1362,10 @@ void LoadSettings() {
showSongFairies =
IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_SONG_FAIRIES) == RO_GENERIC_YES
: false;
showButterflyFairies =
IS_RANDO
? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(RSK_SHUFFLE_BUTTERFLY_FAIRIES) == RO_GENERIC_YES
: false;
showStartingMapsCompasses = IS_RANDO ? OTRGlobals::Instance->gRandomizer->GetRandoSettingValue(
RSK_SHUFFLE_MAPANDCOMPASS) != RO_DUNGEON_ITEM_LOC_VANILLA
: false;
@@ -1611,6 +1616,7 @@ bool IsCheckShuffled(RandomizerCheck rc) {
(loc->GetRCType() != RCTYPE_STONE_FAIRY || showStoneFairies) &&
(loc->GetRCType() != RCTYPE_BEAN_FAIRY || showBeanFairies) &&
(loc->GetRCType() != RCTYPE_SONG_FAIRY || showSongFairies) &&
(loc->GetRCType() != RCTYPE_BUTTERFLY_FAIRY || showButterflyFairies) &&
(loc->GetRCType() != RCTYPE_SMALL_KEY || showKeysanity) &&
(loc->GetRCType() != RCTYPE_BOSS_KEY || showBossKeysanity) &&
(loc->GetRCType() != RCTYPE_GANON_BOSS_KEY || showGanonBossKey) &&

View File

@@ -1070,6 +1070,7 @@ void Settings::CreateOptions() {
OPT_BOOL(RSK_SHUFFLE_STONE_FAIRIES, "Shuffle Gossip Stone Fairies", CVAR_RANDOMIZER_SETTING("ShuffleStoneFairies"), mOptionDescriptions[RSK_SHUFFLE_STONE_FAIRIES]);
OPT_BOOL(RSK_SHUFFLE_BEAN_FAIRIES, "Shuffle Bean Fairies", CVAR_RANDOMIZER_SETTING("ShuffleBeanFairies"), mOptionDescriptions[RSK_SHUFFLE_BEAN_FAIRIES]);
OPT_BOOL(RSK_SHUFFLE_SONG_FAIRIES, "Shuffle Fairy Spots", CVAR_RANDOMIZER_SETTING("ShuffleFairySpots"), mOptionDescriptions[RSK_SHUFFLE_SONG_FAIRIES]);
OPT_BOOL(RSK_SHUFFLE_BUTTERFLY_FAIRIES, "Shuffle Butterfly Fairies", CVAR_RANDOMIZER_SETTING("ShuffleButterflyFairies"), mOptionDescriptions[RSK_SHUFFLE_BUTTERFLY_FAIRIES]);
OPT_U8(RSK_SHUFFLE_MAPANDCOMPASS, "Maps/Compasses", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("StartingMapsCompasses"), mOptionDescriptions[RSK_SHUFFLE_MAPANDCOMPASS], WIDGET_CVAR_COMBOBOX, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON);
OPT_U8(RSK_KEYSANITY, "Small Key Shuffle", {"Start With", "Vanilla", "Own Dungeon", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("Keysanity"), mOptionDescriptions[RSK_KEYSANITY], WIDGET_CVAR_COMBOBOX, RO_DUNGEON_ITEM_LOC_OWN_DUNGEON);
OPT_U8(RSK_GERUDO_KEYS, "Gerudo Fortress Keys", {"Vanilla", "Any Dungeon", "Overworld", "Anywhere"}, OptionCategory::Setting, CVAR_RANDOMIZER_SETTING("GerudoKeys"), mOptionDescriptions[RSK_GERUDO_KEYS], WIDGET_CVAR_COMBOBOX, RO_GERUDO_KEYS_VANILLA);
@@ -1867,6 +1868,7 @@ void Settings::CreateOptions() {
&mOptions[RSK_SHUFFLE_STONE_FAIRIES],
&mOptions[RSK_SHUFFLE_BEAN_FAIRIES],
&mOptions[RSK_SHUFFLE_SONG_FAIRIES],
&mOptions[RSK_SHUFFLE_BUTTERFLY_FAIRIES],
},
WidgetContainerType::SECTION);
mOptionGroups[RSG_MENU_COLUMN_BASIC_SHUFFLES] =
@@ -2176,6 +2178,7 @@ void Settings::CreateOptions() {
&mOptions[RSK_SHUFFLE_STONE_FAIRIES],
&mOptions[RSK_SHUFFLE_BEAN_FAIRIES],
&mOptions[RSK_SHUFFLE_SONG_FAIRIES],
&mOptions[RSK_SHUFFLE_BUTTERFLY_FAIRIES],
});
mOptionGroups[RSG_SHUFFLE_DUNGEON_ITEMS] =
OptionGroup("Shuffle Dungeon Items", {

View File

@@ -9,6 +9,7 @@
#include "objects/gameplay_keep/gameplay_keep.h"
#include "objects/gameplay_field_keep/gameplay_field_keep.h"
#include "soh/ResourceManagerHelpers.h"
#include "soh/Enhancements/game-interactor/GameInteractor_Hooks.h"
#define FLAGS 0
@@ -363,7 +364,7 @@ void EnButte_TransformIntoFairy(EnButte* this, PlayState* play) {
if (this->timer == 5) {
SoundSource_PlaySfxAtFixedWorldPos(play, &this->actor.world.pos, 60, NA_SE_EV_BUTTERFRY_TO_FAIRY);
} else if (this->timer == 4) {
} else if (GameInteractor_Should(VB_SPAWN_BUTTERFLY_FAIRY, this->timer == 4, this)) {
Actor_Spawn(&play->actorCtx, play, ACTOR_EN_ELF, this->actor.focus.pos.x, this->actor.focus.pos.y,
this->actor.focus.pos.z, 0, this->actor.shape.rot.y, 0, FAIRY_HEAL_TIMED);
this->drawSkelAnime = false;