Fix pseudo regions

This commit is contained in:
Pepe20129 2025-05-26 20:07:52 +02:00
commit 8ff130cc5b
8 changed files with 62 additions and 33 deletions

View file

@ -222,22 +222,7 @@ std::set<RandomizerArea> CalculateAreas(SceneID scene) {
} }
} }
Region::Region() = default; bool GetTimePassFromScene(SceneID scene) {
Region::Region(std::string regionName_, SceneID scene_, std::set<RandomizerArea> areas,
std::vector<EventAccess> events_, std::vector<LocationAccess> locations_,
std::list<Rando::Entrance> exits_)
: regionName(std::move(regionName_)), scene(scene_), areas(areas), events(std::move(events_)),
locations(std::move(locations_)), exits(std::move(exits_)) {
}
Region::Region(std::string regionName_, SceneID scene_, std::vector<EventAccess> events_,
std::vector<LocationAccess> locations_, std::list<Rando::Entrance> exits_)
: regionName(std::move(regionName_)), scene(scene_), areas(CalculateAreas(scene_)), events(std::move(events_)),
locations(std::move(locations_)), exits(std::move(exits_)) {
}
Region::~Region() = default;
bool Region::TimePass() {
switch (scene) { switch (scene) {
case SCENE_DEKU_TREE: case SCENE_DEKU_TREE:
case SCENE_DODONGOS_CAVERN: case SCENE_DODONGOS_CAVERN:
@ -365,6 +350,25 @@ bool Region::TimePass() {
} }
} }
Region::Region() = default;
Region::Region(std::string regionName_, SceneID scene_, bool timePass_, std::set<RandomizerArea> areas,
std::vector<EventAccess> events_, std::vector<LocationAccess> locations_,
std::list<Rando::Entrance> exits_)
: regionName(std::move(regionName_)), scene(scene_), timePass(timePass_), areas(areas), events(std::move(events_)),
locations(std::move(locations_)), exits(std::move(exits_)) {
}
Region::Region(std::string regionName_, SceneID scene_, std::vector<EventAccess> events_,
std::vector<LocationAccess> locations_, std::list<Rando::Entrance> exits_)
: regionName(std::move(regionName_)), scene(scene_), timePass(GetTimePassFromScene(scene_)), areas(CalculateAreas(scene_)), events(std::move(events_)),
locations(std::move(locations_)), exits(std::move(exits_)) {
}
Region::~Region() = default;
bool Region::TimePass() {
return timePass;
}
void Region::ApplyTimePass() { void Region::ApplyTimePass() {
if (TimePass()) { if (TimePass()) {
StartPerformanceTimer(PT_TOD_ACCESS); StartPerformanceTimer(PT_TOD_ACCESS);
@ -606,10 +610,10 @@ void RegionTable_Init() {
}; };
// Clear the array from any previous playthrough attempts. This is important so that // Clear the array from any previous playthrough attempts. This is important so that
// locations which appear in both MQ and Vanilla dungeons don't get set in both areas. // locations which appear in both MQ and Vanilla dungeons don't get set in both areas.
areaTable.fill(Region("Invalid Region", SCENE_ID_MAX, {}, {}, {}, {})); areaTable.fill(Region("Invalid Region", SCENE_ID_MAX, {}, {}, {}));
// clang-format off // clang-format off
areaTable[RR_ROOT] = Region("Root", SCENE_ID_MAX, {RA_LINKS_POCKET}, { areaTable[RR_ROOT] = Region("Root", SCENE_ID_MAX, TIME_DOESNT_PASS, {RA_LINKS_POCKET}, {
//Events //Events
EventAccess(&logic->KakarikoVillageGateOpen, []{return ctx->GetOption(RSK_KAK_GATE).Is(RO_KAK_GATE_OPEN);}), EventAccess(&logic->KakarikoVillageGateOpen, []{return ctx->GetOption(RSK_KAK_GATE).Is(RO_KAK_GATE_OPEN);}),
//The big poes bottle softlock safety check does not account for the guard house lock if the guard house is not shuffled, so the key is needed before we can safely allow bottle use in logic //The big poes bottle softlock safety check does not account for the guard house lock if the guard house is not shuffled, so the key is needed before we can safely allow bottle use in logic
@ -625,7 +629,7 @@ void RegionTable_Init() {
Entrance(RR_ROOT_EXITS, []{return true;}), Entrance(RR_ROOT_EXITS, []{return true;}),
}); });
areaTable[RR_ROOT_EXITS] = Region("Root Exits", SCENE_ID_MAX, {RA_LINKS_POCKET}, {}, {}, { areaTable[RR_ROOT_EXITS] = Region("Root Exits", SCENE_ID_MAX, TIME_DOESNT_PASS, {RA_LINKS_POCKET}, {}, {}, {
//Exits //Exits
Entrance(RR_CHILD_SPAWN, []{return logic->IsChild;}), Entrance(RR_CHILD_SPAWN, []{return logic->IsChild;}),
Entrance(RR_ADULT_SPAWN, []{return logic->IsAdult;}), Entrance(RR_ADULT_SPAWN, []{return logic->IsAdult;}),
@ -637,42 +641,42 @@ void RegionTable_Init() {
Entrance(RR_PRELUDE_OF_LIGHT_WARP, []{return logic->CanUse(RG_PRELUDE_OF_LIGHT) && logic->CanLeaveForest();}), Entrance(RR_PRELUDE_OF_LIGHT_WARP, []{return logic->CanUse(RG_PRELUDE_OF_LIGHT) && logic->CanLeaveForest();}),
}); });
areaTable[RR_CHILD_SPAWN] = Region("Child Spawn", SCENE_ID_MAX, {RA_LINKS_POCKET}, {}, {}, { areaTable[RR_CHILD_SPAWN] = Region("Child Spawn", SCENE_ID_MAX, TIME_DOESNT_PASS, {RA_LINKS_POCKET}, {}, {}, {
//Exits //Exits
Entrance(RR_KF_LINKS_HOUSE, []{return true;}), Entrance(RR_KF_LINKS_HOUSE, []{return true;}),
}); });
areaTable[RR_ADULT_SPAWN] = Region("Adult Spawn", SCENE_ID_MAX, {RA_LINKS_POCKET}, {}, {}, { areaTable[RR_ADULT_SPAWN] = Region("Adult Spawn", SCENE_ID_MAX, TIME_DOESNT_PASS, {RA_LINKS_POCKET}, {}, {}, {
//Exits //Exits
Entrance(RR_TEMPLE_OF_TIME, []{return true;}), Entrance(RR_TEMPLE_OF_TIME, []{return true;}),
}); });
areaTable[RR_MINUET_OF_FOREST_WARP] = Region("Minuet of Forest Warp", SCENE_ID_MAX, {RA_LINKS_POCKET}, {}, {}, { areaTable[RR_MINUET_OF_FOREST_WARP] = Region("Minuet of Forest Warp", SCENE_ID_MAX, TIME_DOESNT_PASS, {RA_LINKS_POCKET}, {}, {}, {
//Exits //Exits
Entrance(RR_SACRED_FOREST_MEADOW, []{return true;}), Entrance(RR_SACRED_FOREST_MEADOW, []{return true;}),
}); });
areaTable[RR_BOLERO_OF_FIRE_WARP] = Region("Bolero of Fire Warp", SCENE_ID_MAX, {RA_LINKS_POCKET}, {}, {}, { areaTable[RR_BOLERO_OF_FIRE_WARP] = Region("Bolero of Fire Warp", SCENE_ID_MAX, TIME_DOESNT_PASS, {RA_LINKS_POCKET}, {}, {}, {
//Exits //Exits
Entrance(RR_DMC_CENTRAL_LOCAL, []{return true;}), Entrance(RR_DMC_CENTRAL_LOCAL, []{return true;}),
}); });
areaTable[RR_SERENADE_OF_WATER_WARP] = Region("Serenade of Water Warp", SCENE_ID_MAX, {RA_LINKS_POCKET}, {}, {}, { areaTable[RR_SERENADE_OF_WATER_WARP] = Region("Serenade of Water Warp", SCENE_ID_MAX, TIME_DOESNT_PASS, {RA_LINKS_POCKET}, {}, {}, {
//Exits //Exits
Entrance(RR_LAKE_HYLIA, []{return true;}), Entrance(RR_LAKE_HYLIA, []{return true;}),
}); });
areaTable[RR_REQUIEM_OF_SPIRIT_WARP] = Region("Requiem of Spirit Warp", SCENE_ID_MAX, {RA_LINKS_POCKET}, {}, {}, { areaTable[RR_REQUIEM_OF_SPIRIT_WARP] = Region("Requiem of Spirit Warp", SCENE_ID_MAX, TIME_DOESNT_PASS, {RA_LINKS_POCKET}, {}, {}, {
//Exits //Exits
Entrance(RR_DESERT_COLOSSUS, []{return true;}), Entrance(RR_DESERT_COLOSSUS, []{return true;}),
}); });
areaTable[RR_NOCTURNE_OF_SHADOW_WARP] = Region("Nocturne of Shadow Warp", SCENE_ID_MAX, {RA_LINKS_POCKET}, {}, {}, { areaTable[RR_NOCTURNE_OF_SHADOW_WARP] = Region("Nocturne of Shadow Warp", SCENE_ID_MAX, TIME_DOESNT_PASS, {RA_LINKS_POCKET}, {}, {}, {
//Exits //Exits
Entrance(RR_GRAVEYARD_WARP_PAD_REGION, []{return true;}), Entrance(RR_GRAVEYARD_WARP_PAD_REGION, []{return true;}),
}); });
areaTable[RR_PRELUDE_OF_LIGHT_WARP] = Region("Prelude of Light Warp", SCENE_ID_MAX, {RA_LINKS_POCKET}, {}, {}, { areaTable[RR_PRELUDE_OF_LIGHT_WARP] = Region("Prelude of Light Warp", SCENE_ID_MAX, TIME_DOESNT_PASS, {RA_LINKS_POCKET}, {}, {}, {
//Exits //Exits
Entrance(RR_TEMPLE_OF_TIME, []{return true;}), Entrance(RR_TEMPLE_OF_TIME, []{return true;}),
}); });

View file

@ -9,6 +9,9 @@
#include "soh/Enhancements/randomizer/context.h" #include "soh/Enhancements/randomizer/context.h"
#include "soh/Enhancements/randomizer/logic.h" #include "soh/Enhancements/randomizer/logic.h"
#define TIME_PASSES true
#define TIME_DOESNT_PASS false
typedef bool (*ConditionFn)(); typedef bool (*ConditionFn)();
// I hate this but every alternative I can think of right now is worse // I hate this but every alternative I can think of right now is worse
@ -113,7 +116,7 @@ enum class EntranceType;
class Region { class Region {
public: public:
Region(); Region();
Region(std::string regionName_, SceneID scene_, std::set<RandomizerArea> areas, std::vector<EventAccess> events_, Region(std::string regionName_, SceneID scene_, bool timePass, std::set<RandomizerArea> areas, std::vector<EventAccess> events_,
std::vector<LocationAccess> locations_, std::list<Rando::Entrance> exits_); std::vector<LocationAccess> locations_, std::list<Rando::Entrance> exits_);
Region(std::string regionName_, SceneID scene_, std::vector<EventAccess> events_, Region(std::string regionName_, SceneID scene_, std::vector<EventAccess> events_,
std::vector<LocationAccess> locations_, std::list<Rando::Entrance> exits_); std::vector<LocationAccess> locations_, std::list<Rando::Entrance> exits_);
@ -121,6 +124,7 @@ class Region {
std::string regionName; std::string regionName;
SceneID scene; SceneID scene;
bool timePass;
std::set<RandomizerArea> areas; std::set<RandomizerArea> areas;
std::vector<EventAccess> events; std::vector<EventAccess> events;
std::vector<LocationAccess> locations; std::vector<LocationAccess> locations;

View file

@ -9,7 +9,7 @@ void RegionTable_Init_CastleGrounds() {
//{RA_HYRULE_CASTLE} and {RA_OUTSIDE_GANONS_CASTLE}, but a setting to merge the latter 2 into the former may be preferred //{RA_HYRULE_CASTLE} and {RA_OUTSIDE_GANONS_CASTLE}, but a setting to merge the latter 2 into the former may be preferred
// //
//Temporarily uses SCENE_OUTSIDE_GANONS_CASTLE to avoid self connection between ages //Temporarily uses SCENE_OUTSIDE_GANONS_CASTLE to avoid self connection between ages
areaTable[RR_CASTLE_GROUNDS] = Region("Castle Grounds", SCENE_OUTSIDE_GANONS_CASTLE, {RA_CASTLE_GROUNDS}, {}, {}, { areaTable[RR_CASTLE_GROUNDS] = Region("Castle Grounds", SCENE_OUTSIDE_GANONS_CASTLE, TIME_DOESNT_PASS, {RA_CASTLE_GROUNDS}, {}, {}, {
//Exits //Exits
Entrance(RR_THE_MARKET, []{return true;}), Entrance(RR_THE_MARKET, []{return true;}),
Entrance(RR_HYRULE_CASTLE_GROUNDS, []{return logic->IsChild;}), Entrance(RR_HYRULE_CASTLE_GROUNDS, []{return logic->IsChild;}),

View file

@ -84,14 +84,26 @@ void RegionTable_Init_LakeHylia() {
}, { }, {
//Exits //Exits
Entrance(RR_HYRULE_FIELD, []{return true;}), Entrance(RR_HYRULE_FIELD, []{return true;}),
Entrance(RR_ZORAS_DOMAIN, []{return logic->IsChild && (logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS));}), Entrance(RR_LH_FROM_SHORTCUT, []{return true;}),
Entrance(RR_LH_OWL_FLIGHT, []{return logic->IsChild;}), Entrance(RR_LH_OWL_FLIGHT, []{return logic->IsChild;}),
Entrance(RR_LH_FISHING_ISLAND, []{return ((logic->IsChild || logic->WaterTempleClear) && logic->HasItem(RG_BRONZE_SCALE)) || (logic->IsAdult && (logic->CanUse(RG_SCARECROW) || CanPlantBean(RR_LAKE_HYLIA)));}), Entrance(RR_LH_FISHING_ISLAND, []{return ((logic->IsChild || logic->WaterTempleClear) && logic->HasItem(RG_BRONZE_SCALE)) || (logic->IsAdult && (logic->CanUse(RG_SCARECROW) || CanPlantBean(RR_LAKE_HYLIA)));}),
Entrance(RR_LH_LAB, []{return logic->CanOpenOverworldDoor(RG_HYLIA_LAB_KEY);}), Entrance(RR_LH_LAB, []{return logic->CanOpenOverworldDoor(RG_HYLIA_LAB_KEY);}),
Entrance(RR_WATER_TEMPLE_ENTRYWAY, []{return logic->CanUse(RG_HOOKSHOT) && ((logic->CanUse(RG_IRON_BOOTS) || (ctx->GetTrickOption(RT_LH_WATER_HOOKSHOT) && logic->HasItem(RG_GOLDEN_SCALE))) || (logic->IsAdult && logic->CanUse(RG_LONGSHOT) && logic->HasItem(RG_GOLDEN_SCALE)));}), Entrance(RR_LH_FROM_WATER_TEMPLE, []{return true;}),
Entrance(RR_LH_GROTTO, []{return true;}), Entrance(RR_LH_GROTTO, []{return true;}),
}); });
areaTable[RR_LH_FROM_SHORTCUT] = Region("LH From Shortcut", SCENE_LAKE_HYLIA, TIME_DOESNT_PASS, {RA_LAKE_HYLIA}, {}, {}, {
//Exits
Entrance(RR_LAKE_HYLIA, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS);}),
Entrance(RR_ZORAS_DOMAIN, []{return logic->IsChild && (logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS));}),
});
areaTable[RR_LH_FROM_WATER_TEMPLE] = Region("LH From Water Temple", SCENE_LAKE_HYLIA, TIME_DOESNT_PASS, {RA_LAKE_HYLIA}, {}, {}, {
//Exits
Entrance(RR_LAKE_HYLIA, []{return logic->HasItem(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS);}),
Entrance(RR_WATER_TEMPLE_ENTRYWAY, []{return logic->CanUse(RG_HOOKSHOT) && ((logic->CanUse(RG_IRON_BOOTS) || (ctx->GetTrickOption(RT_LH_WATER_HOOKSHOT) && logic->HasItem(RG_GOLDEN_SCALE))) || (logic->IsAdult && logic->CanUse(RG_LONGSHOT) && logic->HasItem(RG_GOLDEN_SCALE)));}),
});
areaTable[RR_LH_FISHING_ISLAND] = Region("LH Fishing Island", SCENE_LAKE_HYLIA, {}, {}, { areaTable[RR_LH_FISHING_ISLAND] = Region("LH Fishing Island", SCENE_LAKE_HYLIA, {}, {}, {
//Exits //Exits
Entrance(RR_LAKE_HYLIA, []{return logic->HasItem(RG_BRONZE_SCALE);}), Entrance(RR_LAKE_HYLIA, []{return logic->HasItem(RG_BRONZE_SCALE);}),

View file

@ -57,7 +57,7 @@ void RegionTable_Init_LostWoods() {
Entrance(RR_LW_FOREST_EXIT, []{return true;}), Entrance(RR_LW_FOREST_EXIT, []{return true;}),
Entrance(RR_GC_WOODS_WARP, []{return true;}), Entrance(RR_GC_WOODS_WARP, []{return true;}),
Entrance(RR_LW_BRIDGE, []{return logic->CanLeaveForest() && ((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->CanLeaveForest() && ((logic->IsAdult && (CanPlantBean(RR_THE_LOST_WOODS) || ctx->GetTrickOption(RT_LW_BRIDGE))) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT));}),
Entrance(RR_ZORAS_RIVER, []{return logic->CanLeaveForest() && (logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS));}), Entrance(RR_ZR_FROM_SHORTCUT, []{return logic->CanLeaveForest() && (logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS));}),
Entrance(RR_LW_BEYOND_MIDO, []{return logic->IsChild || logic->CanUse(RG_SARIAS_SONG) || ctx->GetTrickOption(RT_LW_MIDO_BACKFLIP);}), 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();});}), Entrance(RR_LW_NEAR_SHORTCUTS_GROTTO, []{return Here(RR_THE_LOST_WOODS, []{return logic->BlastOrSmash();});}),
}); });

View file

@ -38,7 +38,7 @@ void RegionTable_Init_ZorasDomain() {
}, { }, {
//Exits //Exits
Entrance(RR_ZR_BEHIND_WATERFALL, []{return true;}), Entrance(RR_ZR_BEHIND_WATERFALL, []{return true;}),
Entrance(RR_LAKE_HYLIA, []{return logic->IsChild && (logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS));}), Entrance(RR_LH_FROM_SHORTCUT, []{return logic->IsChild && (logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS));}),
Entrance(RR_ZD_BEHIND_KING_ZORA, []{return logic->DeliverLetter || ctx->GetOption(RSK_ZORAS_FOUNTAIN).Is(RO_ZF_OPEN) || (ctx->GetOption(RSK_ZORAS_FOUNTAIN).Is(RO_ZF_CLOSED_CHILD) && logic->IsAdult) || (ctx->GetTrickOption(RT_ZD_KING_ZORA_SKIP) && logic->IsAdult);}), Entrance(RR_ZD_BEHIND_KING_ZORA, []{return logic->DeliverLetter || ctx->GetOption(RSK_ZORAS_FOUNTAIN).Is(RO_ZF_OPEN) || (ctx->GetOption(RSK_ZORAS_FOUNTAIN).Is(RO_ZF_CLOSED_CHILD) && logic->IsAdult) || (ctx->GetTrickOption(RT_ZD_KING_ZORA_SKIP) && logic->IsAdult);}),
Entrance(RR_ZD_SHOP, []{return logic->IsChild || logic->BlueFire();}), Entrance(RR_ZD_SHOP, []{return logic->IsChild || logic->BlueFire();}),
Entrance(RR_ZORAS_DOMAIN_ISLAND, []{return true;}), Entrance(RR_ZORAS_DOMAIN_ISLAND, []{return true;}),

View file

@ -71,6 +71,12 @@ void RegionTable_Init_ZoraRiver() {
Entrance(RR_ZR_BEHIND_WATERFALL, []{return ctx->GetOption(RSK_SLEEPING_WATERFALL).Is(RO_WATERFALL_OPEN) || Here(RR_ZORAS_RIVER, []{return logic->CanUse(RG_ZELDAS_LULLABY);}) || (logic->IsChild && ctx->GetTrickOption(RT_ZR_CUCCO)) || (logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS) && ctx->GetTrickOption(RT_ZR_HOVERS));}), Entrance(RR_ZR_BEHIND_WATERFALL, []{return ctx->GetOption(RSK_SLEEPING_WATERFALL).Is(RO_WATERFALL_OPEN) || Here(RR_ZORAS_RIVER, []{return logic->CanUse(RG_ZELDAS_LULLABY);}) || (logic->IsChild && ctx->GetTrickOption(RT_ZR_CUCCO)) || (logic->IsAdult && logic->CanUse(RG_HOVER_BOOTS) && ctx->GetTrickOption(RT_ZR_HOVERS));}),
}); });
areaTable[RR_ZR_FROM_SHORTCUT] = Region("ZR From Shortcut", SCENE_ZORAS_RIVER, TIME_DOESNT_PASS, {RA_ZORAS_RIVER}, {}, {}, {
//Exits
Entrance(RR_ZORAS_RIVER, []{return logic->HasItem(RG_BRONZE_SCALE);}),
Entrance(RR_THE_LOST_WOODS, []{return logic->HasItem(RG_SILVER_SCALE) || logic->CanUse(RG_IRON_BOOTS);}),
});
areaTable[RR_ZR_BEHIND_WATERFALL] = Region("ZR Behind Waterfall", SCENE_ZORAS_RIVER, {}, {}, { areaTable[RR_ZR_BEHIND_WATERFALL] = Region("ZR Behind Waterfall", SCENE_ZORAS_RIVER, {}, {}, {
//Exits //Exits
Entrance(RR_ZORAS_RIVER, []{return true;}), Entrance(RR_ZORAS_RIVER, []{return true;}),

View file

@ -394,6 +394,8 @@ typedef enum {
RR_HF_NEAR_KAK_GROTTO, RR_HF_NEAR_KAK_GROTTO,
RR_HF_TEKTITE_GROTTO, RR_HF_TEKTITE_GROTTO,
RR_LAKE_HYLIA, RR_LAKE_HYLIA,
RR_LH_FROM_SHORTCUT,
RR_LH_FROM_WATER_TEMPLE,
RR_LH_FISHING_ISLAND, RR_LH_FISHING_ISLAND,
RR_LH_OWL_FLIGHT, RR_LH_OWL_FLIGHT,
RR_LH_LAB, RR_LH_LAB,
@ -499,6 +501,7 @@ typedef enum {
RR_DMC_DISTANT_PLATFORM, RR_DMC_DISTANT_PLATFORM,
RR_ZR_FRONT, RR_ZR_FRONT,
RR_ZORAS_RIVER, RR_ZORAS_RIVER,
RR_ZR_FROM_SHORTCUT,
RR_ZR_BEHIND_WATERFALL, RR_ZR_BEHIND_WATERFALL,
RR_ZR_OPEN_GROTTO, RR_ZR_OPEN_GROTTO,
RR_ZR_FAIRY_GROTTO, RR_ZR_FAIRY_GROTTO,