From 4d60f4c4107b5ce47cdca0a1aa0b8894a2a6c049 Mon Sep 17 00:00:00 2001 From: Demur Rumed Date: Sat, 12 Apr 2025 02:02:02 +0000 Subject: [PATCH 01/29] refactor dungeon logic: spirit --- .../dungeons/spirit_temple.cpp | 410 ++++++++++++++---- soh/soh/Enhancements/randomizer/logic.cpp | 4 +- soh/soh/Enhancements/randomizer/logic.h | 4 +- .../Enhancements/randomizer/randomizerTypes.h | 46 +- 4 files changed, 378 insertions(+), 86 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index e7f9c0ee6..0974aeb2a 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -23,26 +23,80 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD, []{return logic->IsChild;}), - Entrance(RR_SPIRIT_TEMPLE_EARLY_ADULT, []{return logic->CanUse(RG_SILVER_GAUNTLETS);}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return logic->CanUse(RG_SILVER_GAUNTLETS);}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD] = Region("Child Spirit Temple", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_SPIRIT_TEMPLE_CHILD_LOBBY] = Region("Child Spirit Temple", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events EventAccess(&logic->NutCrate, []{return true;}), + }, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_CHILD_STALFOS] = Region("Child Spirit Temple Stalfos", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->SpiritChildStalfosBridge, []{return logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_FAIRY_BOW) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU));}), + }, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE, []{return (logic->SpiritChildStalfosBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false)) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE] = Region("Child Spirit Temple Stalfos Across Bridge", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->SpiritChildStalfosBridge, []{return logic->CanHitSwitch();}), }, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_CHILD_BRIDGE_CHEST, (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU))) && (logic->HasExplosives() || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT))))), - LOCATION(RC_SPIRIT_TEMPLE_CHILD_EARLY_TORCHES_CHEST, (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU))) && (logic->HasExplosives() || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT)))) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_DINS_FIRE))), - LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU))) && (logic->HasExplosives() || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT))))), - LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_1, (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU))) && (logic->HasExplosives() || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT))))), - LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_2, (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU))) && (logic->HasExplosives() || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT))))), - LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_3, (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU))) && (logic->HasExplosives() || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT))))), - LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_4, (logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU))) && (logic->HasExplosives() || ((logic->CanUse(RG_NUTS) || logic->CanUse(RG_BOOMERANG)) && (logic->CanUse(RG_STICKS) || logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_FAIRY_SLINGSHOT))))), + LOCATION(RC_SPIRIT_TEMPLE_CHILD_BRIDGE_CHEST, true), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS, []{return logic->CanUse(RG_HOVER_BOOTS) || (logic->SpiritChildStalfosBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false));}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_ANUBIS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_CHILD_ANUBIS] = Region("Child Spirit Temple Anubis", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_1, true), + LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_2, true), + LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_3, true), + LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_4, true), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE, []{return logic->CanHitSwitch();}), + }); + + areaTable[RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE] = Region("Child Spirit Temple Torches", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->SpiritChildTorchesBridge, []{return true;}), + }, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_CHILD_EARLY_TORCHES_CHEST, logic->HasFireSourceWithTorch()), + LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOOMERANG)), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES, []{return logic->SpiritChildTorchesBridge;}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_ANUBIS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_CHILD_TORCHES] = Region("Child Spirit Temple Torches", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE, []{return logic->SpiritChildTorchesBridge;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB] = Region("Child Spirit Temple Before Climb", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations LOCATION(RC_SPIRIT_TEMPLE_BEFORE_CHILD_CLIMB_SMALL_CRATE_1, logic->CanBreakSmallCrates()), LOCATION(RC_SPIRIT_TEMPLE_BEFORE_CHILD_CLIMB_SMALL_CRATE_2, logic->CanBreakSmallCrates()), }, { //Exits + Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return logic->IsChild;}), Entrance(RR_SPIRIT_TEMPLE_CHILD_CLIMB, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 1);}), }); @@ -57,95 +111,256 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_POT_1, logic->CanBreakPots()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER, []{return logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), }); - areaTable[RR_SPIRIT_TEMPLE_EARLY_ADULT] = Region("Early Adult Spirit Temple", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_ADULT_LOBBY] = Region("Adult Spirit Temple Lobby", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_ADULT_SAND_PIT, []{return logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanUse(RG_BOMB_BAG) && logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH));}), + Entrance(RR_SPIRIT_TEMPLE_ADULT_BOULDERS, []{return logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanUse(RG_BOMB_BAG) && logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH));}), + Entrance(RR_SPIRIT_TEMPLE_ADULT_CLIMB, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_ADULT_SAND_PIT] = Region("Adult Spirit Temple Sand Pit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_COMPASS_CHEST, logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_ZELDAS_LULLABY)), - LOCATION(RC_SPIRIT_TEMPLE_EARLY_ADULT_RIGHT_CHEST, (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanUse(RG_BOMB_BAG) && logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH))) && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslashExceptHammer())), - LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_LEFT_CHEST, logic->SmallKeys(RR_SPIRIT_TEMPLE, 3)), - LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_RIGHT_CHEST, logic->SmallKeys(RR_SPIRIT_TEMPLE, 3)), - LOCATION(RC_SPIRIT_TEMPLE_GS_BOULDER_ROOM, logic->CanUse(RG_SONG_OF_TIME) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanUse(RG_BOMB_BAG) && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH)))), - LOCATION(RC_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY, logic->CanUse(RG_SUNS_SONG) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanUse(RG_BOMB_BAG) && logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH))) && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslash())), + LOCATION(RC_SPIRIT_TEMPLE_COMPASS_CHEST, logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_ZELDAS_LULLABY)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 1);}), + Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER] = Region("Spirit Temple Central Chamber", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_ADULT_BOULDERS] = Region("Adult Spirit Temple Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_GS_BOULDER_ROOM, logic->CanUse(RG_SONG_OF_TIME) && logic->CanKillEnemy(RE_GOLD_SKULLTULA)), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_ADULT_PAST_BOULDERS, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslashExceptHammer();}), + }); + + areaTable[RR_SPIRIT_TEMPLE_ADULT_PAST_BOULDERS] = Region("Adult Spirit Temple Past Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_EARLY_ADULT_RIGHT_CHEST, true), + LOCATION(RC_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY, logic->CanUse(RG_SUNS_SONG)), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_ADULT_BOULDERS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_ADULT_CLIMB] = Region("Adult Spirit Temple Climb", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_LEFT_CHEST, true), + LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_RIGHT_CHEST, true), + }, { + Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3);}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM] = Region("Spirit Temple Statue Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, ((logic->HasExplosives() || logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF))) && - (logic->CanUse(RG_DINS_FIRE) || ((logic->CanUse(RG_FIRE_ARROWS) || ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST)) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_STICKS) ))) || + (logic->HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_STICKS)))) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->HasExplosives() && logic->CanUse(RG_STICKS)) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && (logic->CanUse(RG_FIRE_ARROWS) || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW))) && logic->CanUse(RG_SILVER_GAUNTLETS))), - LOCATION(RC_SPIRIT_TEMPLE_SUN_BLOCK_ROOM_CHEST, ((logic->HasExplosives() || logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF))) && - (logic->CanUse(RG_DINS_FIRE) || ((logic->CanUse(RG_FIRE_ARROWS) || ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST)) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_STICKS) ))) || - (logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->HasExplosives() && logic->CanUse(RG_STICKS)) || - (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && (logic->CanUse(RG_FIRE_ARROWS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))) && logic->CanUse(RG_SILVER_GAUNTLETS))), - LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_HAND_CHEST, logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanUse(RG_ZELDAS_LULLABY)), - LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_NORTHEAST_CHEST, logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanUse(RG_ZELDAS_LULLABY) && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))), - LOCATION(RC_SPIRIT_TEMPLE_GS_HALL_AFTER_SUN_BLOCK_ROOM, (logic->HasExplosives() && logic->CanUse(RG_BOOMERANG) && logic->CanUse(RG_HOOKSHOT)) || - (logic->CanUse(RG_BOOMERANG) && logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->HasExplosives()) || - (logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_SILVER_GAUNTLETS) && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && - logic->CanUse(RG_BOOMERANG) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF))))), LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, ((logic->HasExplosives() || logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF))) && ctx->GetTrickOption(RT_SPIRIT_LOBBY_GS) && logic->CanUse(RG_BOOMERANG) && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))) || (ctx->GetTrickOption(RT_SPIRIT_LOBBY_GS) && logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->HasExplosives() && logic->CanUse(RG_BOOMERANG)) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanUse(RG_SILVER_GAUNTLETS) && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)))), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_1, logic->CanBreakPots()), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_2, logic->CanBreakPots()), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_3, logic->CanBreakPots()), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_4, logic->CanBreakPots()), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_5, logic->CanBreakPots()), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_6, logic->CanBreakPots()), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_CHILD_CLIMB, []{return true;}), + // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled + Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT);}), + //explicit adult check here is a precaution against possible child logic leaking, child with a hookshot can do this + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE] = Region("Spirit Temple Stairs to Block Puzzle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BLOCK_PUZZLE, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_BLOCK_PUZZLE] = Region("Spirit Temple Block Puzzle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_SUN_BLOCK_ROOM_CHEST, ((logic->HasExplosives() || logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF))) && + (logic->CanUse(RG_DINS_FIRE) || ((logic->CanUse(RG_FIRE_ARROWS) || ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST)) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_STICKS) ))) || + (logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->HasExplosives() && logic->CanUse(RG_STICKS)) || + (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && (logic->CanUse(RG_FIRE_ARROWS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))) && logic->CanUse(RG_SILVER_GAUNTLETS))), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND] = Region("Spirit Temple West Stairs to Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_GS_HALL_AFTER_SUN_BLOCK_ROOM, (logic->HasExplosives() && logic->CanUse(RG_BOOMERANG) && logic->CanUse(RG_HOOKSHOT)) || + (logic->CanUse(RG_BOOMERANG) && logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->HasExplosives()) || + (logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_SILVER_GAUNTLETS) && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && + logic->CanUse(RG_BOOMERANG) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF))))), LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_1, logic->CanBreakPots() && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF)))), LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_2, logic->CanBreakPots() && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF)))), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_1, logic->CanBreakPots() && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF)))), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_2, logic->CanBreakPots() && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF)))), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_3, logic->CanBreakPots() && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF)))), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_4, logic->CanBreakPots() && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF)))), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_5, logic->CanBreakPots() && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF)))), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_6, logic->CanBreakPots() && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF)))), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_OUTDOOR_HANDS, []{return logic->CanJumpslashExceptHammer() || logic->HasExplosives();}), - Entrance(RR_SPIRIT_TEMPLE_BEYOND_CENTRAL_LOCKED_DOOR, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && logic->CanUse(RG_SILVER_GAUNTLETS);}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_CLIMB, []{return true;}), - // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled - Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_BLOCK_PUZZLE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), }); - areaTable[RR_SPIRIT_TEMPLE_OUTDOOR_HANDS] = Region("Spirit Temple Outdoor Hands", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE] = Region("Spirit Temple East Stairs to Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple Exit to Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanUse(RG_LONGSHOT) && logic->HasExplosives()) || logic->SmallKeys(RR_SPIRIT_TEMPLE, 5)), - LOCATION(RC_SPIRIT_TEMPLE_MIRROR_SHIELD_CHEST, logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && logic->CanUse(RG_SILVER_GAUNTLETS) && logic->HasExplosives()), + LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, true), }, { //Exits - Entrance(RR_DESERT_COLOSSUS, []{return (logic->IsChild && logic->SmallKeys(RR_SPIRIT_TEMPLE, 5)) || (logic->CanUse(RG_SILVER_GAUNTLETS) && ((logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->HasExplosives()) || logic->SmallKeys(RR_SPIRIT_TEMPLE, 5)));}), + Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return true;}), + Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_BEYOND_CENTRAL_LOCKED_DOOR] = Region("Spirit Temple Beyond Central Locked Door", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST] = Region("Spirit Temple Statue Room East", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_HAND_CHEST, logic->CanUse(RG_ZELDAS_LULLABY)), + LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_NORTHEAST_CHEST, logic->CanUse(RG_ZELDAS_LULLABY) && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_ADULT_CLIMB, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}), + Entrance(RR_SPIRIT_TEMPLE_SHORTCUT, []{return logic->CanUse(RG_MEGATON_HAMMER) && (ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP) || logic->CanUse(RG_HOVER_BOOTS) || (logic->CanUse(RG_ZELDAS_LULLABY) && logic->CanUse(RG_HOOKSHOT)));}), + }); + + areaTable[RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS] = Region("Spirit Temple Stairs to Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + LOCATION(RC_SPIRIT_TEMPLE_BEAMOS_HALL_POT_1, logic->CanBreakPots()), + }, { + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}), + Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_BEAMOS_PITS] = Region("Spirit Temple Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_NEAR_FOUR_ARMOS_CHEST, (logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))) && logic->HasExplosives()), - LOCATION(RC_SPIRIT_TEMPLE_HALLWAY_LEFT_INVISIBLE_CHEST, (ctx->GetTrickOption(RT_LENS_SPIRIT) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->HasExplosives()), - LOCATION(RC_SPIRIT_TEMPLE_HALLWAY_RIGHT_INVISIBLE_CHEST, (ctx->GetTrickOption(RT_LENS_SPIRIT) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->HasExplosives()), - LOCATION(RC_SPIRIT_TEMPLE_BEAMOS_HALL_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY, logic->HasExplosives() && logic->CanUse(RG_SUNS_SONG)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_BEYOND_FINAL_LOCKED_DOOR, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && (ctx->GetTrickOption(RT_SPIRIT_WALL) || logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_BOMBCHU_5) || ((logic->CanUse(RG_BOMB_BAG) || logic->CanUse(RG_NUTS) || logic->CanUse(RG_DINS_FIRE)) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_MEGATON_HAMMER))));}), + Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS, []{return logic->CanKillEnemy(RE_BEAMOS);}), + Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return logic->CanKillEnemy(RE_BEAMOS);}), + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && (ctx->GetTrickOption(RT_SPIRIT_WALL) || logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_BOMBCHU_5) || ((logic->CanUse(RG_BOMB_BAG) || logic->CanUse(RG_NUTS) || logic->CanUse(RG_DINS_FIRE)) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_MEGATON_HAMMER))));}), }); - areaTable[RR_SPIRIT_TEMPLE_BEYOND_FINAL_LOCKED_DOOR] = Region("Spirit Temple Beyond Final Locked Door", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_FOUR_ARMOS] = Region("Spirit Temple Four Armos", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS_SIDE_ROOM, []{return logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), + Entrance(RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_FOUR_ARMOS_SIDE_ROOM] = Region("Spirit Temple Four Armos Side Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_BOSS_KEY_CHEST, logic->CanUse(RG_ZELDAS_LULLABY) && ((logic->TakeDamage() && ctx->GetTrickOption(RT_FLAMING_CHESTS)) || (logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_HOOKSHOT)))), - LOCATION(RC_SPIRIT_TEMPLE_TOPMOST_CHEST, (logic->CanUse(RG_MIRROR_SHIELD) && logic->CanAttack()) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))), - LOCATION(RC_SPIRIT_TEMPLE_ADULT_CLIMB_LEFT_HEART, logic->CanUse(RG_HOOKSHOT)), - LOCATION(RC_SPIRIT_TEMPLE_ADULT_CLIMB_RIGHT_HEART, logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_SPIRIT_TEMPLE_NEAR_FOUR_ARMOS_CHEST, true), }, { //Exits + Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND] = Region("Spirit Temple East Stairs to Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_HALLWAY_LEFT_INVISIBLE_CHEST, ctx->GetTrickOption(RT_LENS_SPIRIT) || logic->CanUse(RG_LENS_OF_TRUTH)), + LOCATION(RC_SPIRIT_TEMPLE_HALLWAY_RIGHT_INVISIBLE_CHEST, ctx->GetTrickOption(RT_LENS_SPIRIT) || logic->CanUse(RG_LENS_OF_TRUTH)), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE] = Region("Spirit Temple East Stairs to Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND] = Region("Spirit Temple Exit to Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MIRROR_SHIELD_HAND, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MIRROR_SHIELD_HAND] = Region("Spirit Temple Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_MIRROR_SHIELD_CHEST, true), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND, []{return logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_DESERT_COLOSSUS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_BIG_WALL] = Region("Spirit Temple Big Wall", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_ADULT_CLIMB_LEFT_HEART, logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_SPIRIT_TEMPLE_ADULT_CLIMB_RIGHT_HEART, logic->CanUse(RG_HOOKSHOT)), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_4F_CENTRAL] = Region("Spirit Temple 4F Central", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_KEY_ROOM, []{return logic->CanUse(RG_ZELDAS_LULLABY);}), + Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_BOSS_KEY_ROOM] = Region("Spirit Temple Boss Key Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_BOSS_KEY_CHEST, (logic->TakeDamage() && ctx->GetTrickOption(RT_FLAMING_CHESTS)) || (logic->CanHitEyeTargets() && logic->CanUse(RG_HOOKSHOT))), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM] = Region("Spirit Temple Big Mirror Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE, []{return logic->CanJumpslash() || logic->HasExplosives();}), + }); + + areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE] = Region("Spirit Temple Big Mirror Cave", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_TOPMOST_CHEST, logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return logic->CanUse(RG_MIRROR_SHIELD) && logic->HasExplosives() && logic->CanUse(RG_HOOKSHOT);}), }); + areaTable[RR_SPIRIT_TEMPLE_SHORTCUT] = Region("Spirit Temple Shortcut", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanUse(RG_MEGATON_HAMMER);}), + }); + areaTable[RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD] = Region("Spirit Temple Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits - Entrance(RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), }); #pragma endregion @@ -166,6 +381,7 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_1F_EAST, []{return false;}), Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH, []{return logic->CanUse(RG_LONGSHOT) && logic->CanUse(RG_BOMBCHU_5);}), }); @@ -190,7 +406,6 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH, []{return logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets();}), - Entrance(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets() && logic->CanKillEnemy(RE_GIBDO);}), }); // Room to store the 2 pots in to handle glitch logic going backwards around the loop later @@ -198,7 +413,9 @@ void RegionTable_Init_SpiritTemple() { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_2, logic->CanBreakPots()), - }, {}); + }, { + Entrance(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return logic->CanKillEnemy(RE_GIBDO);}), + }); areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple Turntable Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events @@ -226,6 +443,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits //Stalfos room blocks you in with fire until you kill the stalfos, which won't spawn from behind the fire + Entrance(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return false;}), Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_SOUTH, []{return true;}), }); @@ -296,11 +514,17 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), //We can use Here instead of Shared here because adult will never need to rely on child access to reach this room, and adult access is Certain Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, []{return Here(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_SONG_OF_TIME));});}), - Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || logic->CanUse(RG_SONG_OF_TIME);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return logic->IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || logic->CanUse(RG_SONG_OF_TIME);}), //explicit adult check here is a precaution against possible child logic leaking, child with a hookshot can do this Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}), }); + areaTable[RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS] = Region("Spirit Temple MQ Flamethrower Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true;}), + }); + areaTable[RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM] = Region("Spirit Temple MQ Sun Block Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations //We don't need Shared here because If we are checking as child, universe 2 adult access needs nothing so it always passes, and if we are checking as adult, it is Certain Access @@ -310,15 +534,27 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_2, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanBreakPots();})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return true;}), //This door causes the Universes to merge as it requires 7 keys for both ages + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND] = Region("Spirit Temple MQ West Stairs to Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE] = Region("Spirit Temple MQ East Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple MQ Exit to Silver Gauntlets Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple MQ Silver Gauntlets Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -327,8 +563,8 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits //If it is ever relevent for 1 age to spawn the mirror shield chest for the other can longshot across, it needs an eventAccess - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return true;}), - Entrance(RR_DESERT_COLOSSUS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return true;}), + Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH] = Region("Spirit Temple MQ Block Room South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -361,13 +597,13 @@ void RegionTable_Init_SpiritTemple() { //This exit handles that possibility as cleanly as possible without quantum logic, but will not survive glitch logic //logic->CanKillEnemy(RE_FLOORMASTER) is implied Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && - logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && - logic->CanJumpslash() && - (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && - logic->CanKillEnemy(RE_IRON_KNUCKLE) && - logic->CanUse(RG_LONGSHOT);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->CanUse(RG_HOOKSHOT);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F, []{return logic->CanUse(RG_FIRE_ARROWS) || (ctx->GetTrickOption(RT_SPIRIT_MQ_LOWER_ADULT) && logic->CanUse(RG_DINS_FIRE));}), + logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && + logic->CanJumpslash() && + (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && + logic->CanKillEnemy(RE_IRON_KNUCKLE) && + logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F, []{return logic->CanUse(RG_FIRE_ARROWS) || (ctx->GetTrickOption(RT_SPIRIT_MQ_LOWER_ADULT) && logic->CanUse(RG_DINS_FIRE));}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F] = Region("Spirit Temple MQ Three Suns Room 2F", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { @@ -427,15 +663,27 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM, []{return true;}), }); + areaTable[RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER] = Region("Spirit Temple MQ Fire Wall Stairs Lower", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_UPPER, []{return logic->CanUse(RG_HOOKSHOT);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_UPPER] = Region("Spirit Temple MQ Fire Wall Stairs Upper", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER, []{return logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM, []{return true;}), + }); + areaTable[RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM] = Region("Spirit Temple MQ Four Beamos Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_BEAMOS_ROOM_CHEST, logic->CanKillEnemy(RE_BEAMOS)), LOCATION(RC_SPIRIT_TEMPLE_MQ_BEAMOS_SMALL_CRATE, logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_SONG_OF_TIME) && logic->CanBreakSmallCrates()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->CanUse(RG_HOOKSHOT);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_UPPER, []{return logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6);}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM] = Region("Spirit Temple MQ SoT Sun Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -457,8 +705,13 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE] = Region("Spirit Temple MQ East Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_MIRROR_SHIELD_HAND, []{return Here(RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_EXIT_TO_MIRROR_SHIELD_HAND, []{return Here(RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_EXIT_TO_MIRROR_SHIELD_HAND] = Region("Spirit Temple MQ Exit To Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_MIRROR_SHIELD_HAND, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_MIRROR_SHIELD_HAND] = Region("Spirit Temple MQ Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -533,6 +786,7 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_MIRROR_PUZZLE_INVISIBLE_CHEST, ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)), }, { //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return true;}), // TODO hammer switch logic //If it's ever relevant to longshot into head from lobby, this needs to be an event access Entrance(RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->CanUse(RG_MIRROR_SHIELD);}) && logic->CanUse(RG_HOOKSHOT);}), Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->CanUse(RG_MIRROR_SHIELD);});}), diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 9fd460d93..009418198 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2500,6 +2500,7 @@ void Logic::Reset() { MQGTGMazeSwitch = false; GTGPlatformSilverRupees = false; MQJabuHolesRoomDoor = false; + JabuRutoIn1F = false; JabuWestTentacle = false; JabuEastTentacle = false; JabuNorthTentacle = false; @@ -2516,7 +2517,8 @@ void Logic::Reset() { MQSpiritMapRoomEnemies = false; MQSpirit3SunsEnemies = false; Spirit1FSilverRupees = false; - JabuRutoIn1F = false; + SpiritChildStalfosBridge = false; + SpiritChildTorchesBridge = false; StopPerformanceTimer(PT_LOGIC_RESET); } diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 4f966f5de..f9f4ace67 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -162,6 +162,7 @@ class Logic { bool MQGTGRightSideSwitch = false; bool GTGPlatformSilverRupees = false; bool MQJabuHolesRoomDoor = false; + bool JabuRutoIn1F = false; bool JabuWestTentacle = false; bool JabuEastTentacle = false; bool JabuNorthTentacle = false; @@ -179,7 +180,8 @@ class Logic { bool MQSpiritTimeTravelChest = false; bool MQSpirit3SunsEnemies = false; bool Spirit1FSilverRupees = false; - bool JabuRutoIn1F = false; + bool SpiritChildStalfosBridge = false; + bool SpiritChildTorchesBridge = false; /* --- END OF HELPERS AND LOCATION ACCESS --- */ diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index ce074bf4c..8d728514f 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -844,13 +844,41 @@ typedef enum { RR_WATER_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_LOBBY, - RR_SPIRIT_TEMPLE_CHILD, + RR_SPIRIT_TEMPLE_CHILD_LOBBY, + RR_SPIRIT_TEMPLE_CHILD_STALFOS, + RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE, + RR_SPIRIT_TEMPLE_CHILD_ANUBIS, + RR_SPIRIT_TEMPLE_CHILD_TORCHES, + RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE, + RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB, RR_SPIRIT_TEMPLE_CHILD_CLIMB, - RR_SPIRIT_TEMPLE_EARLY_ADULT, - RR_SPIRIT_TEMPLE_CENTRAL_CHAMBER, - RR_SPIRIT_TEMPLE_OUTDOOR_HANDS, - RR_SPIRIT_TEMPLE_BEYOND_CENTRAL_LOCKED_DOOR, - RR_SPIRIT_TEMPLE_BEYOND_FINAL_LOCKED_DOOR, + RR_SPIRIT_TEMPLE_ADULT_LOBBY, + RR_SPIRIT_TEMPLE_ADULT_SAND_PIT, + RR_SPIRIT_TEMPLE_ADULT_BOULDERS, + RR_SPIRIT_TEMPLE_ADULT_PAST_BOULDERS, + RR_SPIRIT_TEMPLE_ADULT_CLIMB, + RR_SPIRIT_TEMPLE_STATUE_ROOM, + RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, + RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE, + RR_SPIRIT_TEMPLE_BLOCK_PUZZLE, + RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND, + RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE, + RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND, + RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND, + RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS, + RR_SPIRIT_TEMPLE_BEAMOS_PITS, + RR_SPIRIT_TEMPLE_FOUR_ARMOS, + RR_SPIRIT_TEMPLE_FOUR_ARMOS_SIDE_ROOM, + RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND, + RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, + RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND, + RR_SPIRIT_TEMPLE_MIRROR_SHIELD_HAND, + RR_SPIRIT_TEMPLE_BIG_WALL, + RR_SPIRIT_TEMPLE_4F_CENTRAL, + RR_SPIRIT_TEMPLE_BOSS_KEY_ROOM, + RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, + RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE, + RR_SPIRIT_TEMPLE_SHORTCUT, RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, RR_SPIRIT_TEMPLE_MQ_LOBBY, @@ -864,8 +892,11 @@ typedef enum { RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, + RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, + RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND, RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, + RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND, RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH, RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, @@ -876,10 +907,13 @@ typedef enum { RR_SPIRIT_TEMPLE_MQ_LEEVER_ROOM, RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM, RR_SPIRIT_TEMPLE_MQ_AFTER_SYMPHONY_ROOM, + RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER, + RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_UPPER, RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM, RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND, RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE, + RR_SPIRIT_TEMPLE_MQ_EXIT_TO_MIRROR_SHIELD_HAND, RR_SPIRIT_TEMPLE_MQ_MIRROR_SHIELD_HAND, RR_SPIRIT_TEMPLE_MQ_3F_GIBDO_ROOM, RR_SPIRIT_TEMPLE_MQ_BIG_WALL, From 403d905b7ef9a5f66c1da750b0724cf5d259674d Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sat, 12 Apr 2025 20:37:13 +0100 Subject: [PATCH 02/29] initial SharedSpirit implementation --- .../randomizer/location_access.cpp | 12 ++- .../Enhancements/randomizer/location_access.h | 74 ++++++++++++------- .../dungeons/spirit_temple.cpp | 11 +-- 3 files changed, 59 insertions(+), 38 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index 6cf2aa2ab..1a8c13802 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -210,12 +210,20 @@ bool Here(const RandomizerRegion region, ConditionFn condition) { return areaTable[region].Here(condition); } +bool SpiritExplosiveLogic(){ + return logic->HasExplosives() ? 1 : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 : 3; +} + +bool SpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) { + return areaTable[region].SpiritShared(condition, []{return logic->HasExplosives();}, []{return true;}, 5, 3, SpiritExplosiveLogic(), anyAge); +} + bool MQSpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) { - return areaTable[region].MQSpiritShared(condition, false, anyAge); + return areaTable[region].SpiritShared(condition, []{return true;}, []{return true;}, 7, 0, 0, anyAge); } bool MQSpiritSharedBrokenWallRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) { - return areaTable[region].MQSpiritShared(condition, true, anyAge); + return areaTable[region].SpiritShared(condition, []{return true;}, []{return true;}, 7, 7, 6, anyAge); } bool BeanPlanted(const RandomizerRegion region) { diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 828c31a08..8bbe4d8fb 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -8,6 +8,7 @@ #include "soh/Enhancements/randomizer/randomizerTypes.h" #include "soh/Enhancements/randomizer/context.h" #include "soh/Enhancements/randomizer/logic.h" +#include "soh/Enhancements/randomizer/dungeon.h" typedef bool (*ConditionFn)(); @@ -232,6 +233,7 @@ class Region { } /* + RANDOTODO edit this * This logic covers checks that exist in the shared areas of MQ spirit from a glitchless standpoint. * This room has Quantum logic that I am currently handling with this function, however this is NOT suitable for glitch logic as it relies on specific ages @@ -282,42 +284,57 @@ class Region { * - If Child and Adult can get the check (ignoring actual adult access to the location), and the location is either not 6 key locked or we have 6 keys, we can get the check with the overlap */ - bool MQSpiritShared(ConditionFn condition, bool IsBrokenWall, bool anyAge = false) { - // if we have Certain Access as child, we can check anyAge and if true, resolve a condition with Here as if - // adult is here it's also Certain Access - if (logic->SmallKeys(RR_SPIRIT_TEMPLE, 7)) { + + bool SpiritShared(ConditionFn condition, ConditionFn childAccess, ConditionFn adultAccess, uint8_t adultKeys, uint8_t childKeys, uint8_t eitherKeys, bool anyAge = false){ + //If we have all of the keys, we know that access is Certain Access + if (ctx->GetDungeon(Rando::SPIRIT_TEMPLE)->IsMQ() ? logic->SmallKeys(RR_SPIRIT_TEMPLE, 7) : logic->SmallKeys(RR_SPIRIT_TEMPLE, 5)) { if (anyAge) { return Here(condition); } return condition(); - // else, if we are here as adult, we have Certain Access from that and don't need special handling for - // checking adult - } else if (Adult() && logic->IsAdult) { - return condition(); - // if we do not have Certain Access, we need to check the overlap by seeing if we are both here as child and - // meet the adult universe's access condition We only need to do it as child, as only child access matters - // for this check, as adult access is assumed based on keys - } else if (Child() && logic->IsChild && (!IsBrokenWall || logic->SmallKeys(RR_SPIRIT_TEMPLE, 6))) { - bool result = false; - // store current age variables - bool pastAdult = logic->IsAdult; - bool pastChild = logic->IsChild; + // otherwise, we have to check the current age and... + } else if (Child() && logic->IsChild) { + bool result = condition(); + //if we have enough keys to have Certain Access, we just run the condition + if (logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys)){ + return result; + //otherwise we need to check both ages if we have enough keys that either can get there + } else if (result && logic->SmallKeys(RR_SPIRIT_TEMPLE, eitherKeys) && adultAccess) { + // store current age variables + bool pastAdult = logic->IsAdult; + bool pastChild = logic->IsChild; - // First check if the check is possible as child - logic->IsChild = true; - logic->IsAdult = false; - result = condition(); - // If so, check again as adult. both have to be true for result to be true - if (result) { logic->IsChild = false; logic->IsAdult = true; - result = condition(); - } - // set back age variables - logic->IsChild = pastChild; - logic->IsAdult = pastAdult; - return result; + result = condition(); + + logic->IsChild = pastChild; + logic->IsAdult = pastAdult; + + return result; + } + } else if (Adult() && logic->IsAdult) { + bool result = condition(); + //if we have enough keys to have Certain Access, we just run the condition + if (logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys)){ + return result; + //otherwise we need to check both ages + } else if (result && logic->SmallKeys(RR_SPIRIT_TEMPLE, eitherKeys) && childAccess) { + // store current age variables + bool pastAdult = logic->IsAdult; + bool pastChild = logic->IsChild; + + logic->IsChild = true; + logic->IsAdult = false; + + result = condition(); + + logic->IsChild = pastChild; + logic->IsAdult = pastAdult; + + return result; + } } return false; } @@ -329,6 +346,7 @@ extern std::vector grottoEvents; bool Here(const RandomizerRegion region, ConditionFn condition); // RANDOTODO make a less stupid way to check own at either age than self referencing with this +bool SpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge = false); 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); diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index 0974aeb2a..6322e7e3d 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -160,14 +160,9 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM] = Region("Spirit Temple Statue Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, ((logic->HasExplosives() || logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF))) && - (logic->HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_STICKS)))) || - (logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->HasExplosives() && logic->CanUse(RG_STICKS)) || - (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && (logic->CanUse(RG_FIRE_ARROWS) || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW))) && logic->CanUse(RG_SILVER_GAUNTLETS))), - LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, ((logic->HasExplosives() || logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF))) && - ctx->GetTrickOption(RT_SPIRIT_LOBBY_GS) && logic->CanUse(RG_BOOMERANG) && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))) || - (ctx->GetTrickOption(RT_SPIRIT_LOBBY_GS) && logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->HasExplosives() && logic->CanUse(RG_BOOMERANG)) || - (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanUse(RG_SILVER_GAUNTLETS) && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)))), + LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));})), + LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, + (ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP) || logic->CanUse(RG_HOVER_BOOTS)) ? ED_CLOSE : ctx->GetTrickOption(RT_SPIRIT_LOBBY_GS) ? ED_BOOMERANG : ED_HOOKSHOT);})), LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_2, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_3, logic->CanBreakPots()), From fe6270b3c1347fe17ac9349526b307302982bb73 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sat, 12 Apr 2025 23:30:23 +0100 Subject: [PATCH 03/29] change comment and fix an oversight --- .../Enhancements/randomizer/location_access.h | 67 +++++-------------- 1 file changed, 18 insertions(+), 49 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 8bbe4d8fb..57595e06e 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -233,56 +233,25 @@ class Region { } /* - RANDOTODO edit this - * This logic covers checks that exist in the shared areas of MQ spirit from a glitchless standpoint. - * This room has Quantum logic that I am currently handling with this function, however this is NOT suitable for - glitch logic as it relies on specific ages - * In this chunk there are 3 possibilities for passing a check, but first I have to talk about parallel universes. + * This logic covers checks that exist in the shared areas of Spirit from a glitchless standpoint. + * This code will fail if any glitch allows Adult to go in the Child spirit door first or vice versa as it relies on specific ages - * In MQ Spirit key logic, we mostly care about 2 possibilities for how the player can spend keys, creating 2 - Parralel universes - * In the first universe, the player did not enter spirit as adult until after climbing as child, thus child spends - keys linearly, only needing 2 to reach statue room. - * In the second universe, the player went in as adult, possibly out of logic, and started wasting the keys to lock - child out. - * These Universes converge when the player has 7 keys (meaning adult can no longer lock child out) and adult is - known to be able to reach Statue room. This creates "Certain Access", which is tracked seperatly for each age. - * Child Certain Access is simple, if we have 7 keys and child access, it's Certain Access. - * Adult Certain Access is also simple, adult is not key locked, so if they make it to a location, it's Certain - Access. - * Things get complicated when we handle the overlap of the 2 universes, - * though an important detail is that if we have Certain Access as either age, we don't need to checked the overlap - because overlap logic is strictly stricter than either Certain Access. + * There are 4 possibilities for passing a check, but first I have to talk about parallel universes. - * In order to track the first universe, the logic allows technical child access with the minimum number of keys, - and then checks in this function for if we have 7 keys to determine if that is Certain or not. - * This is for technical reasons, as areas with no access at all will simply not be checked. - * Normally we would need to do similar shenanigans to track the second universe, however adult must have go through - statue room to waste keys, - * so can go back there and get new keys for Child to use if they do, and the navigation logic for shared MQ spirit - from Statue Room is very simple for Adult. - * Additionally, we don't need to know if adult can actually reach spirit temple or climb to statue room, because if - the player can't do that, then universe 2 can't happen anyway, - * and if the player does so out of logic, they can do it again, as the only consumable used sets a permanent flag. - - * The Adult Navigation logic is as such: - * - Broken Wall room is 6 key locked, because if the player tries to spend 6 keys in a way that would block adults - access, they would have to give child access instead. - * - The child side hammer switch for the time travelling chest is 7 key locked for adult - * - Reaching gauntlets hand is 7 key locked - * - Going back into big block room is complex, but the only check there is child only so not a concern - * - Everything else is possible with basic adult movement, or is impossible for child to reach glitchlessly - * Anything 7 key locked does not need to be checked as shared, as all child access is Certain and because of this - workaround we don't need to fake Adult access, meaning that is also Certain. - * All of this combined means that when checking if adult can reach a location in universe 2, we only have to ask if - it is a 6 key locked location or not. - - * Knowing all of this this, we can confirm things are logical in 3 different ways: - * - If we have Adult Access, we know it is Certain Access, so they can get checks alone. - * - If we have 7 keys, child has Certain Access as we know they cannot be locked out, so can get checks alone, - otherwise we check the logical overlap - * - If Child and Adult can get the check (ignoring actual adult access to the location), and the location is either - not 6 key locked or we have 6 keys, we can get the check with the overlap + * In the first universe, the player enters spirit as child, and spends as many keys as they can to lock adult out + * In the second, they enter as adult and spend as many keys as they can to lock child out. + + * When an Age can no longer be kept out by the other age, that age is said to have Certain Access to a region + * If both ages have access to a region with a certain number of keys, but there is no Certain Access, + * then a check is only in logic if both ages can collect the check independently + + * If an age has Certain Access then that age can collect checks alone, + * and there is no reason to check the other age untile the universes converge. + + * The universes converge when the player has all the keys, giving both ages Certain Access. + + * We must check for these universes manually as we allow technical access with minimum keys for + * technical reasons as otherwise the code will never run */ bool SpiritShared(ConditionFn condition, ConditionFn childAccess, ConditionFn adultAccess, uint8_t adultKeys, uint8_t childKeys, uint8_t eitherKeys, bool anyAge = false){ @@ -317,7 +286,7 @@ class Region { } else if (Adult() && logic->IsAdult) { bool result = condition(); //if we have enough keys to have Certain Access, we just run the condition - if (logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys)){ + if (logic->SmallKeys(RR_SPIRIT_TEMPLE, adultKeys)){ return result; //otherwise we need to check both ages } else if (result && logic->SmallKeys(RR_SPIRIT_TEMPLE, eitherKeys) && childAccess) { From 2c95403c0f894eefb507663c422c0309030ee526 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sat, 12 Apr 2025 23:31:39 +0100 Subject: [PATCH 04/29] clang --- .../randomizer/location_access.cpp | 11 ++++--- .../Enhancements/randomizer/location_access.h | 33 ++++++++++--------- 2 files changed, 25 insertions(+), 19 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index 1a8c13802..7ff1a6db5 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -210,20 +210,23 @@ bool Here(const RandomizerRegion region, ConditionFn condition) { return areaTable[region].Here(condition); } -bool SpiritExplosiveLogic(){ +bool SpiritExplosiveLogic() { return logic->HasExplosives() ? 1 : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 : 3; } bool SpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) { - return areaTable[region].SpiritShared(condition, []{return logic->HasExplosives();}, []{return true;}, 5, 3, SpiritExplosiveLogic(), anyAge); + return areaTable[region].SpiritShared( + condition, [] { return logic->HasExplosives(); }, [] { return true; }, 5, 3, SpiritExplosiveLogic(), anyAge); } bool MQSpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) { - return areaTable[region].SpiritShared(condition, []{return true;}, []{return true;}, 7, 0, 0, anyAge); + return areaTable[region].SpiritShared( + condition, [] { return true; }, [] { return true; }, 7, 0, 0, anyAge); } bool MQSpiritSharedBrokenWallRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) { - return areaTable[region].SpiritShared(condition, []{return true;}, []{return true;}, 7, 7, 6, anyAge); + return areaTable[region].SpiritShared( + condition, [] { return true; }, [] { return true; }, 7, 7, 6, anyAge); } bool BeanPlanted(const RandomizerRegion region) { diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 57595e06e..31f043756 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -234,40 +234,43 @@ class Region { /* * This logic covers checks that exist in the shared areas of Spirit from a glitchless standpoint. - * This code will fail if any glitch allows Adult to go in the Child spirit door first or vice versa as it relies on specific ages + * This code will fail if any glitch allows Adult to go in the Child spirit door first or vice versa as it relies on + specific ages * There are 4 possibilities for passing a check, but first I have to talk about parallel universes. * In the first universe, the player enters spirit as child, and spends as many keys as they can to lock adult out * In the second, they enter as adult and spend as many keys as they can to lock child out. - + * When an Age can no longer be kept out by the other age, that age is said to have Certain Access to a region * If both ages have access to a region with a certain number of keys, but there is no Certain Access, * then a check is only in logic if both ages can collect the check independently - + * If an age has Certain Access then that age can collect checks alone, * and there is no reason to check the other age untile the universes converge. - + * The universes converge when the player has all the keys, giving both ages Certain Access. - + * We must check for these universes manually as we allow technical access with minimum keys for * technical reasons as otherwise the code will never run */ - bool SpiritShared(ConditionFn condition, ConditionFn childAccess, ConditionFn adultAccess, uint8_t adultKeys, uint8_t childKeys, uint8_t eitherKeys, bool anyAge = false){ - //If we have all of the keys, we know that access is Certain Access - if (ctx->GetDungeon(Rando::SPIRIT_TEMPLE)->IsMQ() ? logic->SmallKeys(RR_SPIRIT_TEMPLE, 7) : logic->SmallKeys(RR_SPIRIT_TEMPLE, 5)) { + bool SpiritShared(ConditionFn condition, ConditionFn childAccess, ConditionFn adultAccess, uint8_t adultKeys, + uint8_t childKeys, uint8_t eitherKeys, bool anyAge = false) { + // If we have all of the keys, we know that access is Certain Access + if (ctx->GetDungeon(Rando::SPIRIT_TEMPLE)->IsMQ() ? logic->SmallKeys(RR_SPIRIT_TEMPLE, 7) + : logic->SmallKeys(RR_SPIRIT_TEMPLE, 5)) { if (anyAge) { return Here(condition); } return condition(); - // otherwise, we have to check the current age and... + // otherwise, we have to check the current age and... } else if (Child() && logic->IsChild) { bool result = condition(); - //if we have enough keys to have Certain Access, we just run the condition - if (logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys)){ + // if we have enough keys to have Certain Access, we just run the condition + if (logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys)) { return result; - //otherwise we need to check both ages if we have enough keys that either can get there + // otherwise we need to check both ages if we have enough keys that either can get there } else if (result && logic->SmallKeys(RR_SPIRIT_TEMPLE, eitherKeys) && adultAccess) { // store current age variables bool pastAdult = logic->IsAdult; @@ -285,10 +288,10 @@ class Region { } } else if (Adult() && logic->IsAdult) { bool result = condition(); - //if we have enough keys to have Certain Access, we just run the condition - if (logic->SmallKeys(RR_SPIRIT_TEMPLE, adultKeys)){ + // if we have enough keys to have Certain Access, we just run the condition + if (logic->SmallKeys(RR_SPIRIT_TEMPLE, adultKeys)) { return result; - //otherwise we need to check both ages + // otherwise we need to check both ages } else if (result && logic->SmallKeys(RR_SPIRIT_TEMPLE, eitherKeys) && childAccess) { // store current age variables bool pastAdult = logic->IsAdult; From f84af0ffc5cf1dff6ac02e1094aea0b1fd8f34f4 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 13 Apr 2025 00:26:48 +0100 Subject: [PATCH 05/29] remove redundent region argument, do sun block room --- .../randomizer/location_access.cpp | 16 ++++++++-- .../Enhancements/randomizer/location_access.h | 4 ++- .../dungeons/spirit_temple.cpp | 30 +++++++++---------- soh/soh/Enhancements/randomizer/logic.cpp | 1 + soh/soh/Enhancements/randomizer/logic.h | 1 + 5 files changed, 33 insertions(+), 19 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index 7ff1a6db5..a12e2f5b6 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -213,12 +213,22 @@ bool Here(const RandomizerRegion region, ConditionFn condition) { bool SpiritExplosiveLogic() { return logic->HasExplosives() ? 1 : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 : 3; } - -bool SpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) { - return areaTable[region].SpiritShared( +// RANDOTODO basically every condition here will need climb or longshot. +bool SpiritSharedStatueRoom(ConditionFn condition, bool anyAge) { + return areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM].SpiritShared( condition, [] { return logic->HasExplosives(); }, [] { return true; }, 5, 3, SpiritExplosiveLogic(), anyAge); } +bool SpiritSharedSunBlockRoom(ConditionFn condition, bool anyAge) { + return areaTable[RR_SPIRIT_TEMPLE_BLOCK_PUZZLE].SpiritShared( + condition, [] { return logic->HasExplosives(); }, [] { return true; }, 5, 3, SpiritExplosiveLogic(), anyAge); +} + +bool SpiritSharedBrokenWallRoom(ConditionFn condition, bool anyAge) { + return areaTable[RR_SPIRIT_TEMPLE_CHILD_CLIMB].SpiritShared( + condition, [] { return true; }, [] { return true; }, 5, 3, 1, anyAge); +} + bool MQSpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) { return areaTable[region].SpiritShared( condition, [] { return true; }, [] { return true; }, 7, 0, 0, anyAge); diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 31f043756..d08a5da21 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -318,7 +318,9 @@ extern std::vector grottoEvents; bool Here(const RandomizerRegion region, ConditionFn condition); // RANDOTODO make a less stupid way to check own at either age than self referencing with this -bool SpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge = false); +bool SpiritSharedBrokenWallRoom(ConditionFn condition, bool anyAge = false); +bool SpiritSharedStatueRoom(ConditionFn condition, bool anyAge = false); +bool SpiritSharedSunBlockRoom(ConditionFn condition, bool anyAge = false); 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); diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index 6322e7e3d..4f4cf736f 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -102,17 +102,14 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_CHILD_CLIMB] = Region("Child Spirit Temple Climb", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_NORTH_CHEST, logic->HasProjectile(HasProjectileAge::Both) || ((logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF))) && logic->CanUse(RG_SILVER_GAUNTLETS) && logic->HasProjectile(HasProjectileAge::Adult)) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->IsChild && logic->HasProjectile(HasProjectileAge::Child))), - LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_EAST_CHEST, logic->HasProjectile(HasProjectileAge::Both) || ((logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF))) && logic->CanUse(RG_SILVER_GAUNTLETS) && logic->HasProjectile(HasProjectileAge::Adult)) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->IsChild && logic->HasProjectile(HasProjectileAge::Child))), - LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, logic->HasProjectile(HasProjectileAge::Both) || logic->CanUse(RG_DINS_FIRE) || - (logic->TakeDamage() && (logic->CanJumpslashExceptHammer() || logic->HasProjectile(HasProjectileAge::Child))) || - (logic->IsChild && logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->HasProjectile(HasProjectileAge::Child)) || - ((logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF))) && logic->CanUse(RG_SILVER_GAUNTLETS) && (logic->HasProjectile(HasProjectileAge::Adult) || (logic->TakeDamage() && logic->CanJumpslashExceptHammer())))), + LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_NORTH_CHEST, SpiritSharedBrokenWallRoom([]{return logic->CanHitSwitch(ED_BOMB_THROW);})), + LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_EAST_CHEST, SpiritSharedBrokenWallRoom([]{return logic->CanHitSwitch(ED_BOMB_THROW);})), + LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritSharedBrokenWallRoom([]{return logic->CanKillEnemy(RE_GOLD_SKULLTULA, logic->TakeDamage() ? ED_SHORT_JUMPSLASH : ED_BOMB_THROW);})), LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_POT_1, logic->CanBreakPots()), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), }); areaTable[RR_SPIRIT_TEMPLE_ADULT_LOBBY] = Region("Adult Spirit Temple Lobby", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -160,9 +157,9 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM] = Region("Spirit Temple Statue Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));})), - LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, - (ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP) || logic->CanUse(RG_HOVER_BOOTS)) ? ED_CLOSE : ctx->GetTrickOption(RT_SPIRIT_LOBBY_GS) ? ED_BOOMERANG : ED_HOOKSHOT);})), + LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritSharedStatueRoom([]{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));})), + LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritSharedStatueRoom([]{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, + (ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP) || logic->CanUse(RG_HOVER_BOOTS)) ? ED_CLOSE : ctx->GetTrickOption(RT_SPIRIT_LOBBY_GS) ? ED_BOOMERANG : ED_HOOKSHOT);})), LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_2, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_3, logic->CanBreakPots()), @@ -185,12 +182,15 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_BLOCK_PUZZLE, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_BLOCK_PUZZLE] = Region("Spirit Temple Block Puzzle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_BLOCK_PUZZLE] = Region("Spirit Temple Block Puzzle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->SpiritSunBlockTorch, []{return SpiritSharedSunBlockRoom([]{return logic->IsAdult || logic->CanKillEnemy(RE_BEAMOS);});}), + }, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_SUN_BLOCK_ROOM_CHEST, ((logic->HasExplosives() || logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF))) && - (logic->CanUse(RG_DINS_FIRE) || ((logic->CanUse(RG_FIRE_ARROWS) || ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST)) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_STICKS) ))) || - (logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->HasExplosives() && logic->CanUse(RG_STICKS)) || - (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && (logic->CanUse(RG_FIRE_ARROWS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))) && logic->CanUse(RG_SILVER_GAUNTLETS))), + //Child can push blocks to get to the chest without killing the beamos, but it's likely a trick for similar reasons to armos push, and is not relevant without doorsanity + LOCATION(RC_SPIRIT_TEMPLE_SUN_BLOCK_ROOM_CHEST, SpiritSharedSunBlockRoom([]{return (logic->HasFireSource() || + (logic->SpiritSunBlockTorch && (logic->CanUse(RG_STICKS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))))) && + (logic->IsAdult || logic->CanKillEnemy(RE_BEAMOS) || logic->CanUse(RG_HOOKSHOT));})), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE, []{return true;}), diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 009418198..8fae17a65 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2519,6 +2519,7 @@ void Logic::Reset() { Spirit1FSilverRupees = false; SpiritChildStalfosBridge = false; SpiritChildTorchesBridge = false; + SpiritSunBlockTorch = false; StopPerformanceTimer(PT_LOGIC_RESET); } diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index f9f4ace67..f48ca3df8 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -182,6 +182,7 @@ class Logic { bool Spirit1FSilverRupees = false; bool SpiritChildStalfosBridge = false; bool SpiritChildTorchesBridge = false; + bool SpiritSunBlockTorch = false; /* --- END OF HELPERS AND LOCATION ACCESS --- */ From 6f1e7fd2e354f3e83829eb34bd1b9ca1485d1881 Mon Sep 17 00:00:00 2001 From: Demur Rumed Date: Sat, 12 Apr 2025 16:42:15 +0000 Subject: [PATCH 06/29] spirit feedback --- .../Enhancements/randomizer/location_access.h | 5 +- .../dungeons/spirit_temple.cpp | 52 ++++++++++++------- soh/soh/Enhancements/randomizer/logic.cpp | 4 ++ soh/soh/Enhancements/randomizer/logic.h | 3 ++ .../Enhancements/randomizer/randomizerTypes.h | 2 +- 5 files changed, 43 insertions(+), 23 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 828c31a08..10f390368 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -295,10 +295,9 @@ class Region { } else if (Adult() && logic->IsAdult) { return condition(); // if we do not have Certain Access, we need to check the overlap by seeing if we are both here as child and - // meet the adult universe's access condition We only need to do it as child, as only child access matters + // meet the adult universe's access condition. We only need to do it as child, as only child access matters // for this check, as adult access is assumed based on keys } else if (Child() && logic->IsChild && (!IsBrokenWall || logic->SmallKeys(RR_SPIRIT_TEMPLE, 6))) { - bool result = false; // store current age variables bool pastAdult = logic->IsAdult; bool pastChild = logic->IsChild; @@ -306,7 +305,7 @@ class Region { // First check if the check is possible as child logic->IsChild = true; logic->IsAdult = false; - result = condition(); + bool result = condition(); // If so, check again as adult. both have to be true for result to be true if (result) { logic->IsChild = false; diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index 0974aeb2a..ad1ba4c10 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -68,7 +68,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE, []{return logic->CanHitSwitch();}), + Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE, []{return logic->CanHitSwitch() || logic->CanKillEnemy(RE_ANUBIS);}), }); areaTable[RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE] = Region("Child Spirit Temple Torches", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { @@ -76,15 +76,20 @@ void RegionTable_Init_SpiritTemple() { EventAccess(&logic->SpiritChildTorchesBridge, []{return true;}), }, { //Locations + // these assume SpiritChildTorchesBridge, silver rupee shuffle & shuffle climb will want to adjust LOCATION(RC_SPIRIT_TEMPLE_CHILD_EARLY_TORCHES_CHEST, logic->HasFireSourceWithTorch()), - LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOOMERANG)), + // possible to collect without lowering fence, should be a trick + LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOMB_THROW)), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES, []{return logic->SpiritChildTorchesBridge;}), Entrance(RR_SPIRIT_TEMPLE_CHILD_ANUBIS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_TORCHES] = Region("Child Spirit Temple Torches", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_CHILD_TORCHES] = Region("Child Spirit Temple Torches", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOOMERANG)), + }, { //Exits Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE, []{return logic->SpiritChildTorchesBridge;}), @@ -112,14 +117,17 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), }); - areaTable[RR_SPIRIT_TEMPLE_ADULT_LOBBY] = Region("Adult Spirit Temple Lobby", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_ADULT_LOBBY] = Region("Adult Spirit Temple Lobby", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->SpiritAdultLobbySwitch, []{return logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanUse(RG_BOMB_BAG) && logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH));}), + }, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_ADULT_SAND_PIT, []{return logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanUse(RG_BOMB_BAG) && logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH));}), - Entrance(RR_SPIRIT_TEMPLE_ADULT_BOULDERS, []{return logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanUse(RG_BOMB_BAG) && logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH));}), + Entrance(RR_SPIRIT_TEMPLE_ADULT_SAND_PIT, []{return logic->SpiritAdultLobbySwitch;}), + Entrance(RR_SPIRIT_TEMPLE_ADULT_BOULDERS, []{return logic->SpiritAdultLobbySwitch;}), Entrance(RR_SPIRIT_TEMPLE_ADULT_CLIMB, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3);}), }); @@ -137,7 +145,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_ADULT_PAST_BOULDERS, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslashExceptHammer();}), + Entrance(RR_SPIRIT_TEMPLE_ADULT_PAST_BOULDERS, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslash() || logic->CanUse(RG_LONGSHOT);}), }); areaTable[RR_SPIRIT_TEMPLE_ADULT_PAST_BOULDERS] = Region("Adult Spirit Temple Past Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -264,7 +272,7 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS, []{return logic->CanKillEnemy(RE_BEAMOS);}), Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return logic->CanKillEnemy(RE_BEAMOS);}), - Entrance(RR_SPIRIT_TEMPLE_BIG_WALL, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && (ctx->GetTrickOption(RT_SPIRIT_WALL) || logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_BOMBCHU_5) || ((logic->CanUse(RG_BOMB_BAG) || logic->CanUse(RG_NUTS) || logic->CanUse(RG_DINS_FIRE)) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_MEGATON_HAMMER))));}), + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), }); areaTable[RR_SPIRIT_TEMPLE_FOUR_ARMOS] = Region("Spirit Temple Four Armos", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -320,7 +328,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return ctx->GetTrickOption(RT_SPIRIT_WALL) || logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_BOMBCHU_5) || ((logic->CanUse(RG_BOMB_BAG) || logic->CanUse(RG_NUTS) || logic->CanUse(RG_DINS_FIRE)) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_MEGATON_HAMMER)));}), }); areaTable[RR_SPIRIT_TEMPLE_4F_CENTRAL] = Region("Spirit Temple 4F Central", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -402,19 +410,22 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_1F_RUSTED_SWITCH, []{return logic->IsChild && logic->MQSpiritCrawlBoulder;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_SOUTH] = Region("Spirit Temple MQ 1F Gibdo Room South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_SOUTH] = Region("Spirit Temple MQ 1F Gibdo Room South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->MQSpiritGibdosCleared, []{return logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets() && logic->CanKillEnemy(RE_GIBDO, ED_CLOSE, true, 3);}), + }, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH, []{return logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets();}), }); - // Room to store the 2 pots in to handle glitch logic going backwards around the loop later areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH] = Region("Spirit Temple MQ Gibdo Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_GIBDO_POT_2, logic->CanBreakPots()), }, { - Entrance(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return logic->CanKillEnemy(RE_GIBDO);}), + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return logic->MQSpiritGibdosCleared;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple Turntable Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { @@ -548,7 +559,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE] = Region("Spirit Temple MQ East Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return Here(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple MQ Exit to Silver Gauntlets Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -564,7 +575,7 @@ void RegionTable_Init_SpiritTemple() { //Exits //If it is ever relevent for 1 age to spawn the mirror shield chest for the other can longshot across, it needs an eventAccess Entrance(RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return true;}), - Entrance(RR_DESERT_COLOSSUS, []{return true;}), + Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH] = Region("Spirit Temple MQ Block Room South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -706,7 +717,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE] = Region("Spirit Temple MQ East Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_EXIT_TO_MIRROR_SHIELD_HAND, []{return Here(RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_EXIT_TO_MIRROR_SHIELD_HAND, []{return Here(RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_EXIT_TO_MIRROR_SHIELD_HAND] = Region("Spirit Temple MQ Exit To Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -765,7 +776,10 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM] = Region("Spirit Temple MQ Big Mirror Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM] = Region("Spirit Temple MQ Big Mirror Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->MQSpiritOpenedBigMirrorCave, []{return logic->CanUse(RG_MEGATON_HAMMER);}), + }, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_BIG_MIRROR_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_MQ_BIG_MIRROR_POT_2, logic->CanBreakPots()), @@ -778,7 +792,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return logic->CanUse(RG_MEGATON_HAMMER);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->MQSpiritOpenedBigMirrorCave;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE] = Region("Spirit Temple MQ Big Mirror Cave", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -786,7 +800,7 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_MIRROR_PUZZLE_INVISIBLE_CHEST, ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return true;}), // TODO hammer switch logic + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return logic->MQSpiritOpenedBigMirrorCave;}), //If it's ever relevant to longshot into head from lobby, this needs to be an event access Entrance(RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->CanUse(RG_MIRROR_SHIELD);}) && logic->CanUse(RG_HOOKSHOT);}), Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->CanUse(RG_MIRROR_SHIELD);});}), diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 009418198..51898cd26 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2513,12 +2513,16 @@ void Logic::Reset() { MQWaterB1Switch = false; // MQWaterPillarSoTBlock = false; MQWaterOpenedPillarB1 = false; + MQSpiritGibdosCleared = false; MQSpiritCrawlBoulder = false; MQSpiritMapRoomEnemies = false; + MQSpiritTimeTravelChest = false; MQSpirit3SunsEnemies = false; + MQSpiritOpenedBigMirrorCave = false; Spirit1FSilverRupees = false; SpiritChildStalfosBridge = false; SpiritChildTorchesBridge = false; + SpiritAdultLobbySwitch = false; StopPerformanceTimer(PT_LOGIC_RESET); } diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index f9f4ace67..4cc475e59 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -175,13 +175,16 @@ class Logic { bool MQWaterB1Switch = false; // bool MQWaterPillarSoTBlock = false; should be irrelevant. SHOULD. bool MQWaterOpenedPillarB1 = false; + bool MQSpiritGibdosCleared = false; bool MQSpiritCrawlBoulder = false; bool MQSpiritMapRoomEnemies = false; bool MQSpiritTimeTravelChest = false; bool MQSpirit3SunsEnemies = false; + bool MQSpiritOpenedBigMirrorCave = false; bool Spirit1FSilverRupees = false; bool SpiritChildStalfosBridge = false; bool SpiritChildTorchesBridge = false; + bool SpiritAdultLobbySwitch = false; /* --- END OF HELPERS AND LOCATION ACCESS --- */ diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 8d728514f..d8d0910b8 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -883,8 +883,8 @@ typedef enum { RR_SPIRIT_TEMPLE_MQ_LOBBY, RR_SPIRIT_TEMPLE_MQ_1F_WEST, - RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH, RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_SOUTH, + RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH, RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_NORTH, RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_SOUTH, From 54a033b88d35d351cd5c48ae18756bbf6dfe0a9c Mon Sep 17 00:00:00 2001 From: Demur Rumed Date: Mon, 14 Apr 2025 01:56:29 +0000 Subject: [PATCH 07/29] split big wall region --- .../dungeons/spirit_temple.cpp | 30 +++++++++++++------ soh/soh/Enhancements/randomizer/logic.cpp | 2 +- .../Enhancements/randomizer/randomizerTypes.h | 6 ++-- 3 files changed, 26 insertions(+), 12 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index ad1ba4c10..eaa8cfa3b 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -272,7 +272,7 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS, []{return logic->CanKillEnemy(RE_BEAMOS);}), Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return logic->CanKillEnemy(RE_BEAMOS);}), - Entrance(RR_SPIRIT_TEMPLE_BIG_WALL, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_LOWER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), }); areaTable[RR_SPIRIT_TEMPLE_FOUR_ARMOS] = Region("Spirit Temple Four Armos", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -321,19 +321,25 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_BIG_WALL] = Region("Spirit Temple Big Wall", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_BIG_WALL_LOWER] = Region("Spirit Temple Big Wall Lower", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return ctx->GetTrickOption(RT_SPIRIT_WALL) || logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanAvoidEnemy(RE_BEAMOS, true, 2) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_MEGATON_HAMMER)));}), + }); + + areaTable[RR_SPIRIT_TEMPLE_BIG_WALL_UPPER] = Region("Spirit Temple Big Wall Upper", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_ADULT_CLIMB_LEFT_HEART, logic->CanUse(RG_HOOKSHOT)), LOCATION(RC_SPIRIT_TEMPLE_ADULT_CLIMB_RIGHT_HEART, logic->CanUse(RG_HOOKSHOT)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return ctx->GetTrickOption(RT_SPIRIT_WALL) || logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_BOMBCHU_5) || ((logic->CanUse(RG_BOMB_BAG) || logic->CanUse(RG_NUTS) || logic->CanUse(RG_DINS_FIRE)) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_MEGATON_HAMMER)));}), + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_LOWER, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_4F_CENTRAL] = Region("Spirit Temple 4F Central", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_BIG_WALL, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_BOSS_KEY_ROOM, []{return logic->CanUse(RG_ZELDAS_LULLABY);}), Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return true;}), }); @@ -694,7 +700,7 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_UPPER, []{return logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME);}), Entrance(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_LOWER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6);}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM] = Region("Spirit Temple MQ SoT Sun Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -743,7 +749,7 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_WALL] = Region("Spirit Temple MQ Big Wall", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_WALL_LOWER] = Region("Spirit Temple MQ Big Wall Lower", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_LONG_CLIMB_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_MQ_LONG_CLIMB_POT_2, logic->CanBreakPots()), @@ -751,8 +757,14 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM, []{return true;}), //technically we only need to avoid them, but the sheer height and the moving walls makes getting to the top after only stunning them very difficult/impossible + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER, []{return logic->CanKillEnemy(RE_KEESE);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER] = Region("Spirit Temple MQ Big Wall Upper", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_LOWER, []{return true;}), //The silver rupees are irrelevant without silver shuffle - Entrance(RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL, []{return logic->CanKillEnemy(RE_KEESE);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL] = Region("Spirit Temple MQ 4F Central", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -761,7 +773,7 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_BEFORE_MIRROR_POT_2, logic->CanBreakPots()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_MQ_NINE_CHAIRS_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return logic->CanUse(RG_ZELDAS_LULLABY);}), }); diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 51898cd26..a30cb4d93 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -889,7 +889,7 @@ bool Logic::CanAvoidEnemy(RandomizerEnemy enemy, bool grounded, uint8_t quantity case RE_PURPLE_LEEVER: return true; case RE_BEAMOS: - return !grounded || CanUse(RG_NUTS) || + return !grounded || CanUse(RG_NUTS) || CanUse(RG_DINS_FIRE) || (quantity == 1 && (CanUse(RG_FAIRY_BOW) || CanUse(RG_FAIRY_SLINGSHOT))); case RE_MAD_SCRUB: return !grounded || CanUse(RG_NUTS); diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index d8d0910b8..6353c12f2 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -873,7 +873,8 @@ typedef enum { RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND, RR_SPIRIT_TEMPLE_MIRROR_SHIELD_HAND, - RR_SPIRIT_TEMPLE_BIG_WALL, + RR_SPIRIT_TEMPLE_BIG_WALL_LOWER, + RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, RR_SPIRIT_TEMPLE_4F_CENTRAL, RR_SPIRIT_TEMPLE_BOSS_KEY_ROOM, RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, @@ -916,7 +917,8 @@ typedef enum { RR_SPIRIT_TEMPLE_MQ_EXIT_TO_MIRROR_SHIELD_HAND, RR_SPIRIT_TEMPLE_MQ_MIRROR_SHIELD_HAND, RR_SPIRIT_TEMPLE_MQ_3F_GIBDO_ROOM, - RR_SPIRIT_TEMPLE_MQ_BIG_WALL, + RR_SPIRIT_TEMPLE_MQ_BIG_WALL_LOWER, + RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER, RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL, RR_SPIRIT_TEMPLE_MQ_NINE_CHAIRS_ROOM, RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, From 89a3a8667d25f23f5967727a4648b392d46c7e09 Mon Sep 17 00:00:00 2001 From: Demur Rumed Date: Mon, 14 Apr 2025 12:52:31 +0000 Subject: [PATCH 08/29] HERE --- .../randomizer/location_access/dungeons/spirit_temple.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index eaa8cfa3b..571aa6341 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -162,6 +162,7 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_LEFT_CHEST, true), LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_RIGHT_CHEST, true), }, { + //Exits Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3);}), Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}), }); @@ -227,7 +228,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE] = Region("Spirit Temple East Stairs to Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), - Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);}), + Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return Here(RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), }); areaTable[RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple Exit to Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -302,7 +303,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE] = Region("Spirit Temple East Stairs to Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);}), + Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND, []{return Here(RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), }); areaTable[RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND] = Region("Spirit Temple Exit to Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { From bee29ee27db638b7b70af947fddad2d49dca5315 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Mon, 14 Apr 2025 14:16:49 +0100 Subject: [PATCH 09/29] fix key order --- soh/soh/Enhancements/randomizer/location_access.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index d08a5da21..23d454010 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -255,8 +255,8 @@ class Region { * technical reasons as otherwise the code will never run */ - bool SpiritShared(ConditionFn condition, ConditionFn childAccess, ConditionFn adultAccess, uint8_t adultKeys, - uint8_t childKeys, uint8_t eitherKeys, bool anyAge = false) { + bool SpiritShared(ConditionFn condition, ConditionFn childAccess, ConditionFn adultAccess, uint8_t childKeys, + uint8_t adultKeys, uint8_t eitherKeys, bool anyAge = false) { // If we have all of the keys, we know that access is Certain Access if (ctx->GetDungeon(Rando::SPIRIT_TEMPLE)->IsMQ() ? logic->SmallKeys(RR_SPIRIT_TEMPLE, 7) : logic->SmallKeys(RR_SPIRIT_TEMPLE, 5)) { From 01800f74c6304977136d187bca0e04a2ac112ec8 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Tue, 22 Apr 2025 02:34:59 +0100 Subject: [PATCH 10/29] WIP --- .../randomizer/location_access.cpp | 138 ++++- .../Enhancements/randomizer/location_access.h | 110 +--- .../dungeons/spirit_temple.cpp | 538 +++++++++++------- soh/soh/Enhancements/randomizer/logic.cpp | 87 ++- soh/soh/Enhancements/randomizer/logic.h | 19 +- .../Enhancements/randomizer/randomizerTypes.h | 79 +-- soh/soh/Enhancements/randomizer/settings.cpp | 10 +- 7 files changed, 617 insertions(+), 364 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index a12e2f5b6..63147c2da 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -210,33 +210,129 @@ bool Here(const RandomizerRegion region, ConditionFn condition) { return areaTable[region].Here(condition); } -bool SpiritExplosiveLogic() { - return logic->HasExplosives() ? 1 : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 : 3; +uint8_t SpiritExplosiveLogic() { + return logic->SpiritBrokenWallToStatue() ? 1 : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 : 3; } // RANDOTODO basically every condition here will need climb or longshot. -bool SpiritSharedStatueRoom(ConditionFn condition, bool anyAge) { - return areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM].SpiritShared( - condition, [] { return logic->HasExplosives(); }, [] { return true; }, 5, 3, SpiritExplosiveLogic(), anyAge); -} -bool SpiritSharedSunBlockRoom(ConditionFn condition, bool anyAge) { - return areaTable[RR_SPIRIT_TEMPLE_BLOCK_PUZZLE].SpiritShared( - condition, [] { return logic->HasExplosives(); }, [] { return true; }, 5, 3, SpiritExplosiveLogic(), anyAge); -} +// clang-format off +std::map Region::spiritLogicData = { + {RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, SpiritLogicData(5, 5, 3, []{return true;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_BROKEN_WALL, SpiritLogicData(5, 5, 3, []{return true /*logic->CanClimbHigh()*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_2F_MIRROR, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();} , []{return true/*logic->CanClimbHigh()*/;} , []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue();} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_GS_LEDGE, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue() && logic->SpiritWestToSkull();} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic()) && logic->SpiritWestToSkull() + /* && logic->CanClimbHigh() && str0*/;} , []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue();} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true;})}, + {RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && str0*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*(logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0*/;})}, + {RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && str0*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*(logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0*/;})}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();} , []{return true/*logic->CanClimbHigh() && str0*/;} , []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, + {RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue() && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;} , []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));})}, + {RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, SpiritLogicData(7, 6, 7, []{return true;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, SpiritLogicData(7, 6, 7, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;} , []{return true/*logic->Climb*/;} , []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;} , []{return true;} , []{return true;})}, + {RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock()/* && logic->CanClimbHigh()*/;} , []{return logic->MQSpiritStatueToSunBlock()/* && logic->Climb*/;} , []{return logic->MQSpiritStatueToSunBlock()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();}) + /* && logic->CanClimbHigh()*/;} , []{return false;} , []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();});})}, +}; +// clang-format on -bool SpiritSharedBrokenWallRoom(ConditionFn condition, bool anyAge) { - return areaTable[RR_SPIRIT_TEMPLE_CHILD_CLIMB].SpiritShared( - condition, [] { return true; }, [] { return true; }, 5, 3, 1, anyAge); -} -bool MQSpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) { - return areaTable[region].SpiritShared( - condition, [] { return true; }, [] { return true; }, 7, 0, 0, anyAge); -} +/* + * This logic covers checks that exist in the shared areas of Spirit from a glitchless standpoint. + * This code will fail if any glitch allows Adult to go in the Child spirit door first or vice versa as it relies on + specific ages -bool MQSpiritSharedBrokenWallRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge) { - return areaTable[region].SpiritShared( - condition, [] { return true; }, [] { return true; }, 7, 7, 6, anyAge); + * There are 4 possibilities for passing a check, but first I have to talk about parallel universes. + + * In the first universe, the player enters spirit as child, and spends as many keys as they can to lock adult out + * In the second, they enter as adult and spend as many keys as they can to lock child out. + + * When an Age can no longer be kept out by the other age, that age is said to have Certain Access to a region + * If both ages have access to a region with a certain number of keys, but there is no Certain Access, + * then a check is only in logic if both ages can collect the check independently + + * If an age has Certain Access then that age can collect checks alone, + * and there is no reason to check the other age untile the universes converge. + + * The universes converge when the player has all the keys, giving both ages Certain Access. + + * We must check for these universes manually as we allow technical access with minimum keys for + * technical reasons as otherwise the code will never run + */ + + +bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, RandomizerRegion otherRegion, ConditionFn otherCondition){ + SpiritLogicData curRegionData = Region::spiritLogicData[region]; + uint8_t childKeys = logic->ReverseSpiritChild ? curRegionData.childReverseKeys : curRegionData.childKeys; + // If we have enough keys that an age cannot be kept out, we have Certain Access + // otherwise if we have entered in reverse and can reach from the face, we have Certain Access + bool ChildCertainAccess = (logic->ReverseSpiritChild && curRegionData.reverseAccess) || logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys); + bool AdultCertainAccess = (logic->ReverseSpiritAdult && curRegionData.reverseAccess) || logic->SmallKeys(RR_SPIRIT_TEMPLE, curRegionData.adultKeys); + // If both ages have certain access, we can test with Either age + if (ChildCertainAccess && AdultCertainAccess) { + if (anyAge) { + return areaTable[region].Here(condition); + } + return condition(); + // otherwise, we have to check the current age and... + } else if (areaTable[region].Child() && logic->IsChild) { + bool result = condition(); + // If we have Certain Access, we just run the condition. + if (ChildCertainAccess) { + return result; + // Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Adult too + } else if (result) { + SpiritLogicData otherRegionData = Region::spiritLogicData[otherRegion]; + // store current age variables + bool pastAdult = logic->IsAdult; + bool pastChild = logic->IsChild; + + logic->IsChild = false; + logic->IsAdult = true; + + // If Adult can get there and get the check, we can get the check in logic + // If reverse spirit is also possible, we need to make sure Adult can get it via reverse entry too + result = (curRegionData.adultAccess && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) || + (otherRegion != RR_NONE && + (otherRegionData.adultAccess && (!logic->IsReverseAccessPossible() || otherRegionData.reverseAccess) && otherCondition())); + + logic->IsChild = pastChild; + logic->IsAdult = pastAdult; + + return result; + } + } else if (areaTable[region].Adult() && logic->IsAdult) { + bool result = condition(); + // if we have enough keys to have Certain Access, we just run the condition + // Alternatively, if we have entered in reverse and can reach from the face, we have Certain Access + if (AdultCertainAccess) { + return result; + // Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Child too + } else if (result){ + SpiritLogicData otherRegionData = Region::spiritLogicData[otherRegion]; + // store current age variables + bool pastAdult = logic->IsAdult; + bool pastChild = logic->IsChild; + + logic->IsChild = true; + logic->IsAdult = false; + + // If Child can get there and get the check, we can get the check in logic + // If reverse spirit is also possible, we need to make sure Child can get it via reverse entry too + result = (curRegionData.childAccess && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) || + (otherRegion != RR_NONE && + (otherRegionData.childAccess && (!logic->IsReverseAccessPossible() || otherRegionData.reverseAccess) && otherCondition())); + + logic->IsChild = pastChild; + logic->IsAdult = pastAdult; + + return result; + } + } + return false; } bool BeanPlanted(const RandomizerRegion region) { diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 23d454010..f1e943454 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -111,6 +111,25 @@ class Entrance; enum class EntranceType; } // namespace Rando +struct SpiritLogicData { + uint8_t childKeys; //the number of keys that guarantees Child can reach this region + //The number of keys that guarantees Child can reach this region if they have reverse access + //This changes for MQ broken wall room as the first child lock can only be opened by Child + //guaranteeing access with 6 keys + uint8_t childReverseKeys; + uint8_t adultKeys; //the number of keys that guarantees Adult can reach this region + //The area access condition to reach this region as Child, from the first lock, + //including the minimum number of keys for ambiguous access + // 1 key is always assumed to be required + ConditionFn childAccess; + //The area access condition to reach this region as Adult, from the first lock + //including the minimum number of keys for ambiguous access + //1 key is always assumed to be required on vanilla + ConditionFn adultAccess; + //The area access condition to reach this region from the boss door, + ConditionFn reverseAccess; +}; + class Region { public: Region(); @@ -231,85 +250,11 @@ class Region { "Adult Night: " + std::to_string(adultNight); } - - /* - * This logic covers checks that exist in the shared areas of Spirit from a glitchless standpoint. - * This code will fail if any glitch allows Adult to go in the Child spirit door first or vice versa as it relies on - specific ages - - * There are 4 possibilities for passing a check, but first I have to talk about parallel universes. - - * In the first universe, the player enters spirit as child, and spends as many keys as they can to lock adult out - * In the second, they enter as adult and spend as many keys as they can to lock child out. - - * When an Age can no longer be kept out by the other age, that age is said to have Certain Access to a region - * If both ages have access to a region with a certain number of keys, but there is no Certain Access, - * then a check is only in logic if both ages can collect the check independently - - * If an age has Certain Access then that age can collect checks alone, - * and there is no reason to check the other age untile the universes converge. - - * The universes converge when the player has all the keys, giving both ages Certain Access. - - * We must check for these universes manually as we allow technical access with minimum keys for - * technical reasons as otherwise the code will never run - */ - - bool SpiritShared(ConditionFn condition, ConditionFn childAccess, ConditionFn adultAccess, uint8_t childKeys, - uint8_t adultKeys, uint8_t eitherKeys, bool anyAge = false) { - // If we have all of the keys, we know that access is Certain Access - if (ctx->GetDungeon(Rando::SPIRIT_TEMPLE)->IsMQ() ? logic->SmallKeys(RR_SPIRIT_TEMPLE, 7) - : logic->SmallKeys(RR_SPIRIT_TEMPLE, 5)) { - if (anyAge) { - return Here(condition); - } - return condition(); - // otherwise, we have to check the current age and... - } else if (Child() && logic->IsChild) { - bool result = condition(); - // if we have enough keys to have Certain Access, we just run the condition - if (logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys)) { - return result; - // otherwise we need to check both ages if we have enough keys that either can get there - } else if (result && logic->SmallKeys(RR_SPIRIT_TEMPLE, eitherKeys) && adultAccess) { - // store current age variables - bool pastAdult = logic->IsAdult; - bool pastChild = logic->IsChild; - - logic->IsChild = false; - logic->IsAdult = true; - - result = condition(); - - logic->IsChild = pastChild; - logic->IsAdult = pastAdult; - - return result; - } - } else if (Adult() && logic->IsAdult) { - bool result = condition(); - // if we have enough keys to have Certain Access, we just run the condition - if (logic->SmallKeys(RR_SPIRIT_TEMPLE, adultKeys)) { - return result; - // otherwise we need to check both ages - } else if (result && logic->SmallKeys(RR_SPIRIT_TEMPLE, eitherKeys) && childAccess) { - // store current age variables - bool pastAdult = logic->IsAdult; - bool pastChild = logic->IsChild; - - logic->IsChild = true; - logic->IsAdult = false; - - result = condition(); - - logic->IsChild = pastChild; - logic->IsAdult = pastAdult; - - return result; - } - } - return false; - } + + static std::map spiritLogicData; + + bool SpiritShared(ConditionFn condition, ConditionFn childAccess, ConditionFn adultAccess, ConditionFn ReverseAccess, + uint8_t childKeys, uint8_t adultKeys, uint8_t eitherKeys, bool anyAge); }; extern std::array areaTable; @@ -318,11 +263,8 @@ extern std::vector grottoEvents; bool Here(const RandomizerRegion region, ConditionFn condition); // RANDOTODO make a less stupid way to check own at either age than self referencing with this -bool SpiritSharedBrokenWallRoom(ConditionFn condition, bool anyAge = false); -bool SpiritSharedStatueRoom(ConditionFn condition, bool anyAge = false); -bool SpiritSharedSunBlockRoom(ConditionFn condition, bool anyAge = false); -bool MQSpiritSharedStatueRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge = false); -bool MQSpiritSharedBrokenWallRoom(const RandomizerRegion region, ConditionFn condition, bool anyAge = false); +bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge = false, + RandomizerRegion otherRegion = RR_NONE, ConditionFn otherCondition = []{return false;}); bool CanPlantBean(const RandomizerRegion region); bool BothAges(const RandomizerRegion region); bool ChildCanAccess(const RandomizerRegion region); diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index dd7de996a..8c2f49d58 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -22,44 +22,45 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_LOBBY_POT_2, logic->CanBreakPots()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return logic->IsChild;}), - Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return logic->CanUse(RG_SILVER_GAUNTLETS);}), + Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return logic->CanUse(RG_SILVER_GAUNTLETS);}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_LOBBY] = Region("Child Spirit Temple", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_SPIRIT_TEMPLE_1F_WEST] = Region("Spirit Temple 1F West", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events EventAccess(&logic->NutCrate, []{return true;}), }, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->IsChild;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB, []{return logic->IsChild;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);}), + Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_1F_BOXES, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH, []{return Here(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);});}), + Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH, []{return Here(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);});}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_STALFOS] = Region("Child Spirit Temple Stalfos", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH] = Region("Spirit Temple Switch Bridge South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->SpiritChildStalfosBridge, []{return logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_FAIRY_BOW) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU));}), + //RANDOTODO a version od CanHitSwitch that takes WallOrFloor + EventAccess(&logic->SpiritChildSwitchBridge, []{return logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_FAIRY_BOW) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU));}), }, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE, []{return (logic->SpiritChildStalfosBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false)) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH, []{return (logic->SpiritChildSwitchBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false)) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT);}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE] = Region("Child Spirit Temple Stalfos Across Bridge", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH] = Region("Spirit Temple Switch Bridge North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->SpiritChildStalfosBridge, []{return logic->CanHitSwitch();}), + EventAccess(&logic->SpiritChildSwitchBridge, []{return logic->CanHitSwitch();}), }, { //Locations LOCATION(RC_SPIRIT_TEMPLE_CHILD_BRIDGE_CHEST, true), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS, []{return logic->CanUse(RG_HOVER_BOOTS) || (logic->SpiritChildStalfosBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false));}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_ANUBIS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH, []{return logic->CanUse(RG_HOVER_BOOTS) || (logic->SpiritChildSwitchBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false));}), + Entrance(RR_SPIRIT_TEMPLE_1F_ANUBIS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_ANUBIS] = Region("Child Spirit Temple Anubis", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_1F_ANUBIS] = Region("Spirit Temple 1F Anubis", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_1, true), LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_2, true), @@ -67,214 +68,297 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_ANUBIS_POT_4, true), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE, []{return logic->CanHitSwitch() || logic->CanKillEnemy(RE_ANUBIS);}), + Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH, []{return Here(RR_SPIRIT_TEMPLE_1F_ANUBIS, []{return logic->CanHitSwitch() || logic->CanKillEnemy(RE_ANUBIS);});}), + Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH, []{return Here(RR_SPIRIT_TEMPLE_1F_ANUBIS, []{return logic->CanHitSwitch() || logic->CanKillEnemy(RE_ANUBIS);});}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE] = Region("Child Spirit Temple Torches", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH] = Region("Spirit Temple Rupee Bridge North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->SpiritChildTorchesBridge, []{return true;}), + EventAccess(&logic->SpiritRupeeBridge, []{return true;}), }, { //Locations - // these assume SpiritChildTorchesBridge, silver rupee shuffle & shuffle climb will want to adjust + // these assume SpiritRupeeBridge, silver rupee shuffle & shuffle climb will want to adjust LOCATION(RC_SPIRIT_TEMPLE_CHILD_EARLY_TORCHES_CHEST, logic->HasFireSourceWithTorch()), // possible to collect without lowering fence, should be a trick LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOMB_THROW)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES, []{return logic->SpiritChildTorchesBridge;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_ANUBIS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH, []{return logic->SpiritRupeeBridge;}), + Entrance(RR_SPIRIT_TEMPLE_1F_ANUBIS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_TORCHES] = Region("Child Spirit Temple Torches", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH] = Region("Spirit Temple Rupee Bridge South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOOMERANG)), + LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE, []{return logic->SpiritChildTorchesBridge;}), + Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH, []{return logic->SpiritRupeeBridge;}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB] = Region("Child Spirit Temple Before Climb", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_1F_BOXES] = Region("Child Spirit Temple Before Climb", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_BEFORE_CHILD_CLIMB_SMALL_CRATE_1, logic->CanBreakSmallCrates()), LOCATION(RC_SPIRIT_TEMPLE_BEFORE_CHILD_CLIMB_SMALL_CRATE_2, logic->CanBreakSmallCrates()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_LOBBY, []{return logic->IsChild;}), - Entrance(RR_SPIRIT_TEMPLE_CHILD_CLIMB, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 1);}), + Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 1);}), }); - areaTable[RR_SPIRIT_TEMPLE_CHILD_CLIMB] = Region("Child Spirit Temple Climb", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE] = Region("Spirit Temple Child Climb Base", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_NORTH_CHEST, SpiritSharedBrokenWallRoom([]{return logic->CanHitSwitch(ED_BOMB_THROW);})), - LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_EAST_CHEST, SpiritSharedBrokenWallRoom([]{return logic->CanHitSwitch(ED_BOMB_THROW);})), - LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritSharedBrokenWallRoom([]{return logic->CanKillEnemy(RE_GOLD_SKULLTULA, logic->TakeDamage() ? ED_SHORT_JUMPSLASH : ED_BOMB_THROW);})), - LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_POT_1, logic->CanBreakPots()), + LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG);})), + LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_POT_1, SpiritShared(RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, []{return logic->CanBreakPots();})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), + Entrance(RR_SPIRIT_TEMPLE_1F_BOXES, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_ADULT_LOBBY] = Region("Adult Spirit Temple Lobby", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { - //Events - EventAccess(&logic->SpiritAdultLobbySwitch, []{return logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanUse(RG_BOMB_BAG) && logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH));}), - }, {}, { + areaTable[RR_SPIRIT_TEMPLE_BROKEN_WALL] = Region("Spirit Temple Broken Wall", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_NORTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->CanHitSwitch(ED_BOMB_THROW);})), + LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_EAST_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->CanHitSwitch(ED_BOMB_THROW);})), + LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA, logic->TakeDamage() ? ED_SHORT_JUMPSLASH : ED_BOMB_THROW);})), + }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_ADULT_SAND_PIT, []{return logic->SpiritAdultLobbySwitch;}), - Entrance(RR_SPIRIT_TEMPLE_ADULT_BOULDERS, []{return logic->SpiritAdultLobbySwitch;}), - Entrance(RR_SPIRIT_TEMPLE_ADULT_CLIMB, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3);}), + Entrance(RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, []{return true;}), + ///*CanClimbHigh() &&*/ (HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS))) + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->SpiritBrokenWallToStatue();}), }); - areaTable[RR_SPIRIT_TEMPLE_ADULT_SAND_PIT] = Region("Adult Spirit Temple Sand Pit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_1F_EAST] = Region("Adult Spirit Temple Lobby", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SAND_PIT, []{return Here(RR_SPIRIT_TEMPLE_1F_EAST, []{return logic->CanHitSwitch(logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH) ? ED_BOMB_THROW : ED_BOOMERANG);});}), + Entrance(RR_SPIRIT_TEMPLE_BOULDERS, []{return Here(RR_SPIRIT_TEMPLE_1F_EAST, []{return logic->CanHitSwitch(logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH) ? ED_BOMB_THROW : ED_BOOMERANG);});}), + Entrance(RR_SPIRIT_TEMPLE_EAST_CLIMB_BASE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 1);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_SAND_PIT] = Region("Spirit Temple Sand Pit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_COMPASS_CHEST, logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_ZELDAS_LULLABY)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_ADULT_BOULDERS] = Region("Adult Spirit Temple Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_ABOVE_BOULDERS] = Region("Spirit Temple Above Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + //Jump slash is possible as child, but pretty tight. Jumpslash as late as you can + //A damage boost off the boulder is also possible, but you need + EventAccess(&logic->SpiritBouldersSilvers, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslash() || logic->CanUse(RG_LONGSHOT);}), + }, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BOULDERS, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_BOULDERS] = Region("Spirit Temple Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_GS_BOULDER_ROOM, logic->CanUse(RG_SONG_OF_TIME) && logic->CanKillEnemy(RE_GOLD_SKULLTULA)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_ADULT_PAST_BOULDERS, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslash() || logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_ABOVE_BOULDERS, []{return true;}), + //Jump slash is possible as child, but pretty tight. Jumpslash as late as you can + //A damage boost off the boulder is also possible, but you need + Entrance(RR_SPIRIT_TEMPLE_PAST_BOULDERS, []{return logic->SpiritBouldersSilvers;}), }); - areaTable[RR_SPIRIT_TEMPLE_ADULT_PAST_BOULDERS] = Region("Adult Spirit Temple Past Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_PAST_BOULDERS] = Region("Spirit Temple Past Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_EARLY_ADULT_RIGHT_CHEST, true), LOCATION(RC_SPIRIT_TEMPLE_BOULDER_ROOM_SUN_FAIRY, logic->CanUse(RG_SUNS_SONG)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_ADULT_BOULDERS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BOULDERS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_ADULT_CLIMB] = Region("Adult Spirit Temple Climb", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_EAST_CLIMB_BASE] = Region("Spirit Temple East Climb Base", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_2F_MIRROR] = Region("Spirit Temple 2F Mirror", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_LEFT_CHEST, true), - LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_RIGHT_CHEST, true), + LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_LEFT_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;})), + LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_RIGHT_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_ADULT_LOBBY, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3);}), + Entrance(RR_SPIRIT_TEMPLE_EAST_CLIMB_BASE, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}), }); + areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST] = Region("Spirit Temple Statue Rooom West", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + //Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM access + LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));})), + //Current and N64 logic doesn't need scarecrow, you can hit the skull with normal hookshot from a specific spot on the forearm + //Child can get this with hook by backflipping onto the upper arm and standing in a precise place + LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE) ? ED_BOOMERANG : ED_HOOKSHOT);}, false, + RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA);})), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return true;}), + //(IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SCARECROW) + Entrance(RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->SpiritWestToSkull();}), + // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered && + (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), + Entrance(RR_SPIRIT_TEMPLE_EMPTY_STAIRS, []{return true;}), + //!QUANTUM LOGIC! + //When child enters spirit in reverse, has 4 keys, and dungeon entrance shuffle is off, + //Child cannot lock themselves out of desert colossus access as if they save the west hand lock for last + //they will be able to exit the dungeon through the intended entrance and vice versa + //for needing to open the west hand lock to block the intended child route + Entrance(RR_DESERT_COLOSSUS, []{return ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && logic->ReverseSpiritChild && + logic->IsChild && logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && logic->CanKillEnemy(RE_IRON_KNUCKLE);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_GS_LEDGE] = Region("Spirit Temple GS ledge", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritShared(RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA);})), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return true;}) + }); + areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM] = Region("Spirit Temple Statue Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritSharedStatueRoom([]{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));})), - LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritSharedStatueRoom([]{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, - (ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP) || logic->CanUse(RG_HOVER_BOOTS)) ? ED_CLOSE : ctx->GetTrickOption(RT_SPIRIT_LOBBY_GS) ? ED_BOOMERANG : ED_HOOKSHOT);})), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_1, logic->CanBreakPots()), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_2, logic->CanBreakPots()), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_3, logic->CanBreakPots()), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_4, logic->CanBreakPots()), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_5, logic->CanBreakPots()), - LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_6, logic->CanBreakPots()), + LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasFireSource();}, false, + RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));})), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_1, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_2, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_3, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_4, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_5, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_6, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHILD_CLIMB, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return true;}), // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled - Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered && + (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), //explicit adult check here is a precaution against possible child logic leaking, child with a hookshot can do this - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}), - Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_SHORTCUT, []{return logic->SpiritStatueRoomSouthDoor;}), }); - areaTable[RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE] = Region("Spirit Temple Stairs to Block Puzzle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_EMPTY_STAIRS] = Region("Spirit Temple Empty Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BLOCK_PUZZLE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_BLOCK_PUZZLE] = Region("Spirit Temple Block Puzzle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { - //Events - EventAccess(&logic->SpiritSunBlockTorch, []{return SpiritSharedSunBlockRoom([]{return logic->IsAdult || logic->CanKillEnemy(RE_BEAMOS);});}), - }, { + areaTable[RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM] = Region("Spirit Temple Sun Block Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - //Child can push blocks to get to the chest without killing the beamos, but it's likely a trick for similar reasons to armos push, and is not relevant without doorsanity - LOCATION(RC_SPIRIT_TEMPLE_SUN_BLOCK_ROOM_CHEST, SpiritSharedSunBlockRoom([]{return (logic->HasFireSource() || - (logic->SpiritSunBlockTorch && (logic->CanUse(RG_STICKS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))))) && - (logic->IsAdult || logic->CanKillEnemy(RE_BEAMOS) || logic->CanUse(RG_HOOKSHOT));})), + //the blocks can be used to get all the silver rupees and the chest itemless + LOCATION(RC_SPIRIT_TEMPLE_SUN_BLOCK_ROOM_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, + []{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW));})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EMPTY_STAIRS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND] = Region("Spirit Temple West Stairs to Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS] = Region("Spirit Temple Skulltula Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_GS_HALL_AFTER_SUN_BLOCK_ROOM, (logic->HasExplosives() && logic->CanUse(RG_BOOMERANG) && logic->CanUse(RG_HOOKSHOT)) || - (logic->CanUse(RG_BOOMERANG) && logic->SmallKeys(RR_SPIRIT_TEMPLE, 5) && logic->HasExplosives()) || - (logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_SILVER_GAUNTLETS) && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && - logic->CanUse(RG_BOOMERANG) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF))))), - LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_1, logic->CanBreakPots() && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF)))), - LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_2, logic->CanBreakPots() && (logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) || (logic->SmallKeys(RR_SPIRIT_TEMPLE, 2) && ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF)))), + LOCATION(RC_SPIRIT_TEMPLE_GS_HALL_AFTER_SUN_BLOCK_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG);})), + LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_1, SpiritShared(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_AFTER_SUN_BLOCK_POT_2, SpiritShared(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return logic->CanBreakPots();})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_BLOCK_PUZZLE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_WEST_THRONE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), }); - areaTable[RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE] = Region("Spirit Temple East Stairs to Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_WEST_THRONE] = Region("Spirit Temple West Throne", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), - Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return Here(RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + Entrance(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_WEST_HAND_EXIT, []{return Here(RR_SPIRIT_TEMPLE_WEST_THRONE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), }); - areaTable[RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple Exit to Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_WEST_HAND_EXIT] = Region("Spirit Temple West Hand Exit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_WEST_THRONE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_WEST_HAND, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_WEST_HAND] = Region("Spirit Temple West Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, true), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return true;}), - Entrance(RR_DESERT_COLOSSUS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_WEST_HAND_EXIT, []{return true;}), + Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST] = Region("Spirit Temple Statue Room East", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_HAND_CHEST, logic->CanUse(RG_ZELDAS_LULLABY)), - LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_NORTHEAST_CHEST, logic->CanUse(RG_ZELDAS_LULLABY) && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS) || ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))), + //Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST access. WARNING: ZL spawning chests here is a temp flag + LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_HAND_CHEST, logic->CanUse(RG_ZELDAS_LULLABY)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_ADULT_CLIMB, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}), - Entrance(RR_SPIRIT_TEMPLE_SHORTCUT, []{return logic->CanUse(RG_MEGATON_HAMMER) && (ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP) || logic->CanUse(RG_HOVER_BOOTS) || (logic->CanUse(RG_ZELDAS_LULLABY) && logic->CanUse(RG_HOOKSHOT)));}), + Entrance(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + //(IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); + Entrance(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return logic->SpiritEastToSwitch();}), + Entrance(RR_SPIRIT_TEMPLE_POT_STAIRS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}), + //!QUANTUM LOGIC! + //With 3 keys, you cannot lock adult out of longshotting to the west hand as you would have to + //open the west hand door and then adult could climb through sun block room + //This requires that adult can complete both routes + //Implies CanKillEnemy(RE_IRON_KNUCKLE) + Entrance(RR_SPIRIT_TEMPLE_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanUse(RG_LONGSHOT) && logic->CanKillEnemy(RE_BEAMOS);}), + //Similarly, we can gurantee jumping into desert colossus the same way + //except this does not need the longshot as we don't care which hand we jump down from + Entrance(RR_DESERT_COLOSSUS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanKillEnemy(RE_BEAMOS);}), }); - areaTable[RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS] = Region("Spirit Temple Stairs to Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH] = Region("Spirit Temple Shortcut Switch", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->SpiritStatueRoomSouthDoor, []{return SpiritShared(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return logic->CanUse(RG_MEGATON_HAMMER);});}), + }, { + //Locations + //Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST access + LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_NORTHEAST_CHEST, logic->CanUse(RG_ZELDAS_LULLABY)), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_SHORTCUT] = Region("Spirit Temple Shortcut", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //If child can ever use silver gauntlets and adult items, there needs to be an event here to account for child entering in reverse + //opening the way for adult entering via the front. + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanUse(RG_MEGATON_HAMMER);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_POT_STAIRS] = Region("Spirit Temple Stairs to Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { LOCATION(RC_SPIRIT_TEMPLE_BEAMOS_HALL_POT_1, logic->CanBreakPots()), }, { Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}), Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_BEAMOS_PITS] = Region("Spirit Temple Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { - //Locations - LOCATION(RC_SPIRIT_TEMPLE_NEAR_FOUR_ARMOS_CHEST, (logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))) && logic->HasExplosives()), - LOCATION(RC_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY, logic->HasExplosives() && logic->CanUse(RG_SUNS_SONG)), - }, { + areaTable[RR_SPIRIT_TEMPLE_BEAMOS_PITS] = Region("Spirit Temple Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS, []{return logic->CanKillEnemy(RE_BEAMOS);}), - Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return logic->CanKillEnemy(RE_BEAMOS);}), - Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_LOWER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + //Implies killing the anubis with the fire ring, doing so itemless requires voiding out, which can lock hardcore + OHKO seeds + Entrance(RR_SPIRIT_TEMPLE_POT_STAIRS, []{return logic->CanKillEnemy(RE_BEAMOS);}), + Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return logic->CanKillEnemy(RE_BEAMOS);}), + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_LOWER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), }); - areaTable[RR_SPIRIT_TEMPLE_FOUR_ARMOS] = Region("Spirit Temple Four Armos", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_FOUR_ARMOS] = Region("Spirit Temple Four Armos", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_NEAR_FOUR_ARMOS_CHEST, (logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))) && logic->HasExplosives()), + LOCATION(RC_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY, logic->HasExplosives() && logic->CanUse(RG_SUNS_SONG)), + }, { Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS_SIDE_ROOM, []{return logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), - Entrance(RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_CHEST_STAIRS, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_FOUR_ARMOS_SIDE_ROOM] = Region("Spirit Temple Four Armos Side Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -285,46 +369,47 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND] = Region("Spirit Temple East Stairs to Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_CHEST_STAIRS] = Region("Spirit Temple Chest Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_HALLWAY_LEFT_INVISIBLE_CHEST, ctx->GetTrickOption(RT_LENS_SPIRIT) || logic->CanUse(RG_LENS_OF_TRUTH)), LOCATION(RC_SPIRIT_TEMPLE_HALLWAY_RIGHT_INVISIBLE_CHEST, ctx->GetTrickOption(RT_LENS_SPIRIT) || logic->CanUse(RG_LENS_OF_TRUTH)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EAST_THRONE, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE] = Region("Spirit Temple East Stairs to Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_EAST_THRONE] = Region("Spirit Temple East Throne", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND, []{return Here(RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + Entrance(RR_SPIRIT_TEMPLE_CHEST_STAIRS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EAST_HAND_EXIT, []{return Here(RR_SPIRIT_TEMPLE_EAST_THRONE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), }); - areaTable[RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND] = Region("Spirit Temple Exit to Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_EAST_HAND_EXIT] = Region("Spirit Temple East hand Exit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MIRROR_SHIELD_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EAST_THRONE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EAST_HAND, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MIRROR_SHIELD_HAND] = Region("Spirit Temple Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_EAST_HAND] = Region("Spirit Temple East Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MIRROR_SHIELD_CHEST, true), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND, []{return logic->CanUse(RG_LONGSHOT);}), - Entrance(RR_DESERT_COLOSSUS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EAST_HAND_EXIT, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_WEST_HAND, []{return logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_BIG_WALL_LOWER] = Region("Spirit Temple Big Wall Lower", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return ctx->GetTrickOption(RT_SPIRIT_WALL) || logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_BOMBCHU_5) || (logic->CanAvoidEnemy(RE_BEAMOS, true, 2) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_MEGATON_HAMMER)));}), + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return ctx->GetTrickOption(RT_SPIRIT_WALL) || (logic->CanAvoidEnemy(RE_BEAMOS, true, 2) && logic->CanPassEnemy(RE_WALLTULA));}), }); areaTable[RR_SPIRIT_TEMPLE_BIG_WALL_UPPER] = Region("Spirit Temple Big Wall Upper", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations + //Grabbing these with rang is possible, but requires a blind shot aimed high LOCATION(RC_SPIRIT_TEMPLE_ADULT_CLIMB_LEFT_HEART, logic->CanUse(RG_HOOKSHOT)), LOCATION(RC_SPIRIT_TEMPLE_ADULT_CLIMB_RIGHT_HEART, logic->CanUse(RG_HOOKSHOT)), }, { @@ -336,41 +421,69 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_4F_CENTRAL] = Region("Spirit Temple 4F Central", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_KEY_ROOM, []{return logic->CanUse(RG_ZELDAS_LULLABY);}), + Entrance(RR_SPIRIT_TEMPLE_FAKE_DOORS_ROOM, []{return logic->CanUse(RG_ZELDAS_LULLABY);}), Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_BOSS_KEY_ROOM] = Region("Spirit Temple Boss Key Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_FAKE_DOORS_ROOM] = Region("Spirit Temple Fake Doors Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_BOSS_KEY_CHEST, (logic->TakeDamage() && ctx->GetTrickOption(RT_FLAMING_CHESTS)) || (logic->CanHitEyeTargets() && logic->CanUse(RG_HOOKSHOT))), + LOCATION(RC_SPIRIT_TEMPLE_BOSS_KEY_CHEST, (logic->TakeDamage() && ctx->GetTrickOption(RT_FLAMING_CHESTS)) || + (Here(RR_SPIRIT_TEMPLE_FAKE_DOORS_ROOM, []{return logic->CanHitEyeTargets() && logic->CanAvoidEnemy(RE_TORCH_SLUG, true, 4);}) + && logic->CanUse(RG_HOOKSHOT))), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM] = Region("Spirit Temple Big Mirror Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM] = Region("Spirit Temple Big Mirror Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->Spirit4FSwitch, []{return logic->CanJumpslash() || logic->HasExplosives();}), + //Needs the mirror in the cave to be a perm flag and event for doorsanity + EventAccess(&logic->SpiritPlatformLowered, []{return (Here(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return logic->CanJumpslash() || logic->HasExplosives();}) || logic->CanUse(RG_MIRROR_SHIELD)) || + (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), + + }, {}, { Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE, []{return logic->CanJumpslash() || logic->HasExplosives();}), + //Rang can hit the switch on the way back but that's a trick + Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE, []{return logic->Spirit4FSwitch;}), + //Assumes RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE access + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return (logic->CanUse(RG_MIRROR_SHIELD) && logic->HasExplosives() && logic->Spirit4FSwitch) || + (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), }); - areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE] = Region("Spirit Temple Big Mirror Cave", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE] = Region("Spirit Temple Big Mirror Cave", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->Spirit4FSwitch, []{return logic->HasExplosives();}), + }, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_TOPMOST_CHEST, logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))), + LOCATION(RC_SPIRIT_TEMPLE_TOPMOST_CHEST, (logic->IsAdult && logic->CanUse(RG_MIRROR_SHIELD)) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return logic->CanUse(RG_MIRROR_SHIELD) && logic->HasExplosives() && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_SHORTCUT] = Region("Spirit Temple Shortcut", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanUse(RG_MEGATON_HAMMER);}), + //Assumes a SpiritPlatformLowered check on entry + areaTable[RR_SPIRIT_TEMPLE_PLATFORM] = Region("Spirit Temple Lowered Platform", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_HEAD, []{return Here(RR_SPIRIT_TEMPLE_STATUE_HEAD, []{return logic->CanUse(RG_MIRROR_SHIELD) && logic->HasExplosives() && logic->Spirit4FSwitch;}) + && logic->CanUse(RG_HOOKSHOT);}), }); - areaTable[RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD] = Region("Spirit Temple Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_STATUE_HEAD] = Region("Spirit Temple Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + //WARNING these events are not glitchproofed and assume you need all keys to reach from the front + EventAccess(&logic->ReverseSpiritChild, []{return logic->IsChild;}), + EventAccess(&logic->ReverseSpiritAdult, []{return logic->IsAdult;}), + }, {}, { // Exits - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT));}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), }); #pragma endregion @@ -432,7 +545,6 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple Turntable Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - //For non-fairy pot items, you can also get them with rang without killing the stalfos EventAccess(&logic->FairyPot, []{return Here(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return logic->CanKillEnemy(RE_STALFOS);});}), }, { //Locations @@ -470,7 +582,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits //The bridge is a temp flag, so not a way to cross south to north in logic - Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_NORTH, []{return logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_NORTH, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}), Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return true;}), }); @@ -487,7 +599,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE] = Region("Spirit Temple MQ Under Like Like", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_LIKE_LIKE_POT, MQSpiritSharedBrokenWallRoom(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_LIKE_LIKE_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->CanBreakPots();})), }, { //Exits //This covers adult access only, as child arrives here from the other side of this door @@ -498,10 +610,9 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM] = Region("Spirit Temple MQ Broken Wall Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations //Implies CanKillEnemy(RE_LIKE_LIKE) - LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_NORTH_CHEST, MQSpiritSharedBrokenWallRoom(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->CanKillEnemy(RE_BEAMOS);})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_NORTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->CanKillEnemy(RE_BEAMOS);})), //Sunlights only temp spawn this chest, which is unintuitive/a bug. - //chest is only reachable as adult glitchlessly, so we can skip the shared in favour of IsAdult as adult access is always Certain - LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_SOUTH_CHEST, logic->IsAdult && (logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))) && logic->CanUse(RG_HOOKSHOT)), + LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_SOUTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return (logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))) && logic->CanUse(RG_HOOKSHOT);})), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->CanHitSwitch();}), @@ -509,28 +620,48 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 2);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM] = Region("Spirit Temple MQ Statue Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST] = Region("Spirit Temple MQ Statue Room West", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->SpiritStatueRoomSouthDoor, []{return SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_SONG_OF_TIME);});}), + }, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_MQ_COMPASS_CHEST, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanHitEyeTargets();})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_EAST_POT, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return ((logic->IsAdult || logic->CanJumpslash()) && logic->CanUse(RG_BOOMERANG)) || ((logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))) && logic->CanBreakPots());})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_WEST_POT, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) && logic->CanBreakPots());})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_CENTER_EAST_POT, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_WEST_POT, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_EASTMOST_POT, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_CRATE_1, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakCrates();})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_CRATE_2, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakCrates();})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_SMALL_CRATE, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakSmallCrates();})), - + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_EAST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return ((logic->IsAdult || logic->CanJumpslash()) && logic->CanUse(RG_BOOMERANG)) || + ((logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))) && logic->CanBreakPots());})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_WEST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return ((logic->IsAdult || logic->CanJumpslash()) && logic->CanUse(RG_BOOMERANG) && ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE)) || + (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) && logic->CanBreakPots());})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_SMALL_CRATE, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return logic->CanBreakSmallCrates();})), }, { //Exits - //we check possible adult access directly in MQSpiritSharedBrokenWallRoom, so this exit only covers Certain Access - Entrance(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), - //We can use Here instead of Shared here because adult will never need to rely on child access to reach this room, and adult access is Certain - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, []{return Here(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_SONG_OF_TIME));});}), - Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return logic->IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || logic->CanUse(RG_SONG_OF_TIME);}), + //Minimal entry, real key logic handled in Shared functions + Entrance(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6);}), + //IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME) + Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return logic->MQSpiritStatueToSunBlock();}), //explicit adult check here is a precaution against possible child logic leaking, child with a hookshot can do this Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}), }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM] = Region("Spirit Temple MQ Statue Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->SpiritStatueRoomSouthDoor, []{return SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->HasFireSource();});}), + }, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_MQ_COMPASS_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanHitEyeTargets();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_CENTER_EAST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_WEST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_EASTMOST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_CRATE_1, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakCrates();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_CRATE_2, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakCrates();})), + //the drop sometimes flies off the block when the crate is blown up, but not always, so I added a rang requirement + //a trick to reload for it is plausible + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_SMALL_CRATE, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return (logic->CanUse(RG_SONG_OF_TIME) && logic->CanBreakSmallCrates()) || + (logic->CanUse(RG_BOOMERANG) && logic->HasExplosives());})), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return true/*logic->CanClimb()*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, []{return logic->SpiritStatueRoomSouthDoor;}), + //explicit adult check here is a precaution against possible child logic leaking, child with a hookshot can do this + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_SCARECROW);}), + }); areaTable[RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS] = Region("Spirit Temple MQ Flamethrower Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits @@ -541,43 +672,43 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM] = Region("Spirit Temple MQ Sun Block Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations //We don't need Shared here because If we are checking as child, universe 2 adult access needs nothing so it always passes, and if we are checking as adult, it is Certain Access - LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM_CHEST, true), - LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_SUN_BLOCK_ROOM, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanUse(RG_HOOKSHOT) || (ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_GS) && logic->CanUse(RG_BOOMERANG));})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_1, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanBreakPots();})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_2, MQSpiritSharedStatueRoom(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM_CHEST, SpiritMQSharedSunBlockRoom([]{return true/*str0*/;})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_SUN_BLOCK_ROOM, SpiritMQSharedSunBlockRoom([]{return logic->CanUse(RG_HOOKSHOT) || (ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_GS) && logic->CanUse(RG_BOOMERANG));})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_1, SpiritMQSharedSunBlockRoom([]{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_2, SpiritMQSharedSunBlockRoom([]{return logic->CanBreakPots();})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return true/*str0*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS, []{return true/*str0*/;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS] = Region("Spirit Temple MQ Skulltula Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true;}), //This door causes the Universes to merge as it requires 7 keys for both ages - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_THRONE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND] = Region("Spirit Temple MQ West Stairs to Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_THRONE] = Region("Spirit Temple MQ West Throne", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT, []{return Here(RR_SPIRIT_TEMPLE_MQ_WEST_THRONE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE] = Region("Spirit Temple MQ East Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT] = Region("Spirit Temple MQ West Hand Exit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return Here(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_THRONE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple MQ Exit to Silver Gauntlets Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { - //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return true;}), - }); - - areaTable[RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND] = Region("Spirit Temple MQ Silver Gauntlets Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_HAND] = Region("Spirit Temple MQ West Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, true), }, { //Exits //If it is ever relevent for 1 age to spawn the mirror shield chest for the other can longshot across, it needs an eventAccess - Entrance(RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND, []{return true;}), - Entrance(RR_DESERT_COLOSSUS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT, []{return true;}), + Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH] = Region("Spirit Temple MQ Block Room South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -590,8 +721,8 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH] = Region("Spirit Temple MQ Block Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - //Does not need to be shared as it's hard child locked, because adult pushing the block is a permanent flag that blocks the eye target and cannot be undone - LOCATION(RC_SPIRIT_TEMPLE_MQ_SILVER_BLOCK_HALLWAY_CHEST, logic->IsChild && logic->SmallKeys(RR_SPIRIT_TEMPLE, 7) && logic->CanHitEyeTargets()), + //Dhard child locked because adult pushing the block is a permanent flag that blocks the eye target and cannot be undone + LOCATION(RC_SPIRIT_TEMPLE_MQ_SILVER_BLOCK_HALLWAY_CHEST, SpiritMQSharedBigBlock([]{return logic->IsChild && logic->CanHitEyeTargets();})), }, { //Exits //if going to RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH from here is ever relevant, there needs to be an event to handle the block @@ -609,7 +740,7 @@ void RegionTable_Init_SpiritTemple() { //We only need 4 keys, access to Shield hand and longshot to reach Gauntlets hand, as if we waste the 5th key we have given ourselves Gauntlets hand access through child climb //This exit handles that possibility as cleanly as possible without quantum logic, but will not survive glitch logic //logic->CanKillEnemy(RE_FLOORMASTER) is implied - Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && logic->CanJumpslash() && (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && @@ -647,11 +778,11 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return logic->CanUse(RG_MEGATON_HAMMER);}), Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_1F, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_LEEVER_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SAND_PIT, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_LEEVER_ROOM] = Region("Spirit Temple MQ Leever Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_SAND_PIT] = Region("Spirit Temple MQ Sand Pit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_LEEVER_ROOM_CHEST, logic->CanKillEnemy(RE_PURPLE_LEEVER) && logic->CanUse(RG_HOOKSHOT)), LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_LEEVER_ROOM, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)), @@ -732,7 +863,7 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MIRROR_SHIELD_CHEST, true), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, []{return logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND, []{return logic->CanUse(RG_LONGSHOT);}), Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE, []{return true;}), Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); @@ -810,11 +941,16 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return logic->MQSpiritOpenedBigMirrorCave;}), //If it's ever relevant to longshot into head from lobby, this needs to be an event access - Entrance(RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->CanUse(RG_MIRROR_SHIELD);}) && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->CanUse(RG_MIRROR_SHIELD);}) && logic->CanUse(RG_HOOKSHOT);}), Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->CanUse(RG_MIRROR_SHIELD);});}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD] = Region("Spirit Temple MQ Inside Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD] = Region("Spirit Temple MQ Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + //WARNING these events are not glitchproofed and assume you need all keys to reach from the front + EventAccess(&logic->ReverseSpiritChild, []{return logic->IsChild;}), + EventAccess(&logic->ReverseSpiritAdult, []{return logic->IsAdult;}), + }, {}, { // Exits Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), @@ -825,9 +961,9 @@ void RegionTable_Init_SpiritTemple() { // Boss Room areaTable[RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY] = Region("Spirit Temple Boss Entryway", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits - Entrance(RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsVanilla() && false;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsMQ() && false;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsVanilla() && false;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD, []{return ctx->GetDungeon(SPIRIT_TEMPLE)->IsMQ() && false;}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ROOM, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_BOSS_ROOM] = Region("Spirit Temple Boss Room", "Spirit Temple", {}, NO_DAY_NIGHT_CYCLE, { diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index acae5c2d4..d6e06bf79 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -539,14 +539,11 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal killed = killed || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_STICKS); [[fallthrough]]; case ED_BOMB_THROW: - killed = killed || CanUse(RG_BOMB_BAG); + killed = killed || CanUse(RG_BOMB_BAG) || CanUse(RG_DINS_FIRE); [[fallthrough]]; case ED_BOOMERANG: - // RANDOTODO test dins and chu range in a practical example - killed = killed || CanUse(RG_DINS_FIRE); - [[fallthrough]]; case ED_HOOKSHOT: - // RANDOTODO test dins 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: @@ -576,15 +573,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 + // RANDOTODO test chu range in a practical example killed = killed || (!inWater && CanUse(RG_BOMB_BAG)); [[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: @@ -629,7 +626,7 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal [[fallthrough]]; case ED_BOOMERANG: case ED_HOOKSHOT: - // RANDOTODO test dins and chu range in a practical example + // RANDOTODO test chu range in a practical example killed = killed || (wallOrFloor && CanUse(RG_BOMBCHU_5)); [[fallthrough]]; case ED_LONGSHOT: @@ -710,12 +707,11 @@ 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)); [[fallthrough]]; case ED_BOOMERANG: 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: @@ -789,6 +785,33 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal case RE_OCTOROK: return CanReflectNuts() || HookshotOrBoomerang() || CanUse(RG_FAIRY_BOW) || CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_BOMB_BAG) || (wallOrFloor && CanUse(RG_BOMBCHU_5)); + case RE_WALLTULA: + switch (distance) { + case ED_CLOSE: + case ED_SHORT_JUMPSLASH: + killed = CanUse(RG_KOKIRI_SWORD); + [[fallthrough]]; + case ED_MASTER_SWORD_JUMPSLASH: + killed = killed || CanUse(RG_MASTER_SWORD); + [[fallthrough]]; + case ED_LONG_JUMPSLASH: + killed = killed || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_STICKS); + [[fallthrough]]; + case ED_BOMB_THROW: + killed = killed || (!inWater && CanUse(RG_BOMB_BAG)) || CanUse(RG_DINS_FIRE); + [[fallthrough]]; + case ED_BOOMERANG: + case ED_HOOKSHOT: + killed = killed || CanUse(RG_HOOKSHOT) || CanUse(RG_BOMBCHU_5) || CanUse(RG_MEGATON_HAMMER); + [[fallthrough]]; + case ED_LONGSHOT: + killed = killed || CanUse(RG_LONGSHOT); + [[fallthrough]]; + case ED_FAR: + killed = killed || CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_FAIRY_BOW); + break; + } + return killed; default: SPDLOG_ERROR("CanKillEnemy reached `default`."); assert(false); @@ -841,6 +864,7 @@ bool Logic::CanPassEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal return CanUse(RG_HOOKSHOT) || CanUse(RG_SUNS_SONG); case RE_IRON_KNUCKLE: case RE_BIG_OCTO: + case RE_WALLTULA: //consistent with RT_SPIRIT_WALL return false; case RE_GREEN_BUBBLE: return TakeDamage() || CanUse(RG_NUTS) || CanUse(RG_BOOMERANG) || CanUse(RG_HOOKSHOT); @@ -887,6 +911,7 @@ bool Logic::CanAvoidEnemy(RandomizerEnemy enemy, bool grounded, uint8_t quantity case RE_WALLMASTER: case RE_ANUBIS: case RE_PURPLE_LEEVER: + case RE_WALLTULA: return true; case RE_BEAMOS: return !grounded || CanUse(RG_NUTS) || CanUse(RG_DINS_FIRE) || @@ -899,6 +924,8 @@ bool Logic::CanAvoidEnemy(RandomizerEnemy enemy, bool grounded, uint8_t quantity case RE_BLUE_BUBBLE: // RANDOTODO Trick to use shield hylian shield as child to stun these guys return !grounded || CanUse(RG_NUTS) || HookshotOrBoomerang() || CanStandingShield(); + case RE_TORCH_SLUG: + return !grounded || CanUse(RG_NUTS) || CanUse(RG_HOOKSHOT) || CanUse(RG_DINS_FIRE); default: SPDLOG_ERROR("CanPassEnemy reached `default`."); assert(false); @@ -2331,6 +2358,32 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) { inLogic[logicVal] = value; } +bool Logic::IsReverseAccessPossible(){ + return ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES) && + (ctx->GetOption(RSK_DECOUPLED_ENTRANCES) || + ctx->GetOption(RSK_MIX_BOSS_ENTRANCES)); +} + +bool Logic::SpiritBrokenWallToStatue() { + return /*CanClimbHigh() &&*/ (HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS))); +} + +bool Logic::SpiritEastToSwitch() { + return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); +} + +bool Logic::SpiritWestToSkull() { + return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SCARECROW); +} + +bool Logic::MQSpiritStatueToSunBlock() { + return IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME); +} + +bool Logic::MQSpiritStatueSouthDoor() { + return HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && CanUse(RG_FAIRY_BOW) && CanUse(RG_SONG_OF_TIME)/* && CanClimb()*/); +} + void Logic::Reset() { NewSaveContext(); StartPerformanceTimer(PT_LOGIC_RESET); @@ -2520,11 +2573,15 @@ void Logic::Reset() { MQSpirit3SunsEnemies = false; MQSpiritOpenedBigMirrorCave = false; Spirit1FSilverRupees = false; - SpiritChildStalfosBridge = false; - SpiritChildTorchesBridge = false; - SpiritSunBlockTorch = false; - SpiritAdultLobbySwitch = false; + SpiritChildSwitchBridge = false; + SpiritRupeeBridge = false; + SpiritBouldersSilvers = false; + SpiritPlatformLowered = false; + Spirit4FSwitch = false; + ReverseSpiritChild = false; + ReverseSpiritAdult = false; StopPerformanceTimer(PT_LOGIC_RESET); } + } // namespace Rando diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 207109f4c..2a03262ee 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -26,6 +26,7 @@ enum class GlitchDifficulty { HERO, }; + class Logic { public: bool noVariable = false; @@ -182,10 +183,14 @@ class Logic { bool MQSpirit3SunsEnemies = false; bool MQSpiritOpenedBigMirrorCave = false; bool Spirit1FSilverRupees = false; - bool SpiritChildStalfosBridge = false; - bool SpiritChildTorchesBridge = false; - bool SpiritSunBlockTorch = false; - bool SpiritAdultLobbySwitch = false; + bool SpiritChildSwitchBridge = false; + bool SpiritRupeeBridge = false; + bool SpiritBouldersSilvers = false; + bool SpiritStatueRoomSouthDoor = false; + bool SpiritPlatformLowered = false; + bool Spirit4FSwitch = false; + bool ReverseSpiritChild = false; + bool ReverseSpiritAdult = true; /* --- END OF HELPERS AND LOCATION ACCESS --- */ @@ -294,6 +299,12 @@ class Logic { static std::map RandoGetToDungeonScene; static std::map RandoGetToEquipFlag; static std::map RandoGetToRandInf; + bool Logic::IsReverseAccessPossible(); + bool Logic::SpiritBrokenWallToStatue(); + bool Logic::SpiritEastToSwitch(); + bool Logic::SpiritWestToSkull(); + bool Logic::MQSpiritStatueToSunBlock(); + bool Logic::MQSpiritStatueSouthDoor(); private: std::shared_ptr ctx; diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 6353c12f2..7d669730d 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -8,6 +8,8 @@ #define TWO_ACTOR_PARAMS(a, b) ((((a)&0xFFFF) << 16) | ((b)&0xFFFF)) +typedef bool (*ConditionFn)(); + // This should probably go in a less rando-specific location // but the best location will probably be in the modding engine // which doesn't exist yet. @@ -844,43 +846,50 @@ typedef enum { RR_WATER_TEMPLE_BOSS_ROOM, RR_SPIRIT_TEMPLE_LOBBY, - RR_SPIRIT_TEMPLE_CHILD_LOBBY, - RR_SPIRIT_TEMPLE_CHILD_STALFOS, - RR_SPIRIT_TEMPLE_CHILD_STALFOS_ACROSS_BRIDGE, - RR_SPIRIT_TEMPLE_CHILD_ANUBIS, - RR_SPIRIT_TEMPLE_CHILD_TORCHES, - RR_SPIRIT_TEMPLE_CHILD_TORCHES_ACROSS_BRIDGE, - RR_SPIRIT_TEMPLE_CHILD_BEFORE_CLIMB, - RR_SPIRIT_TEMPLE_CHILD_CLIMB, - RR_SPIRIT_TEMPLE_ADULT_LOBBY, - RR_SPIRIT_TEMPLE_ADULT_SAND_PIT, - RR_SPIRIT_TEMPLE_ADULT_BOULDERS, - RR_SPIRIT_TEMPLE_ADULT_PAST_BOULDERS, - RR_SPIRIT_TEMPLE_ADULT_CLIMB, + RR_SPIRIT_TEMPLE_1F_WEST, + RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH, + RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH, + RR_SPIRIT_TEMPLE_1F_ANUBIS, + RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH, + RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH, + RR_SPIRIT_TEMPLE_1F_BOXES, + RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, + RR_SPIRIT_TEMPLE_BROKEN_WALL, + RR_SPIRIT_TEMPLE_1F_EAST, + RR_SPIRIT_TEMPLE_SAND_PIT, + RR_SPIRIT_TEMPLE_BOULDERS, + RR_SPIRIT_TEMPLE_ABOVE_BOULDERS, + RR_SPIRIT_TEMPLE_PAST_BOULDERS, + RR_SPIRIT_TEMPLE_EAST_CLIMB_BASE, + RR_SPIRIT_TEMPLE_2F_MIRROR, RR_SPIRIT_TEMPLE_STATUE_ROOM, + RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, + RR_SPIRIT_TEMPLE_GS_LEDGE, RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, - RR_SPIRIT_TEMPLE_STAIRS_TO_BLOCK_PUZZLE, - RR_SPIRIT_TEMPLE_BLOCK_PUZZLE, - RR_SPIRIT_TEMPLE_WEST_STAIRS_TO_HAND, - RR_SPIRIT_TEMPLE_WEST_IRON_KNUCKLE, - RR_SPIRIT_TEMPLE_EXIT_TO_SILVER_GAUNTLETS_HAND, - RR_SPIRIT_TEMPLE_SILVER_GAUNTLETS_HAND, - RR_SPIRIT_TEMPLE_STAIRS_TO_BEAMOS_PITS, + RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, + RR_SPIRIT_TEMPLE_SHORTCUT, + RR_SPIRIT_TEMPLE_EMPTY_STAIRS, + RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, + RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, + RR_SPIRIT_TEMPLE_WEST_THRONE, + RR_SPIRIT_TEMPLE_WEST_HAND_EXIT, + RR_SPIRIT_TEMPLE_WEST_HAND, + RR_SPIRIT_TEMPLE_POT_STAIRS, RR_SPIRIT_TEMPLE_BEAMOS_PITS, RR_SPIRIT_TEMPLE_FOUR_ARMOS, RR_SPIRIT_TEMPLE_FOUR_ARMOS_SIDE_ROOM, - RR_SPIRIT_TEMPLE_EAST_STAIRS_TO_HAND, - RR_SPIRIT_TEMPLE_EAST_IRON_KNUCKLE, - RR_SPIRIT_TEMPLE_EXIT_TO_MIRROR_SHIELD_HAND, - RR_SPIRIT_TEMPLE_MIRROR_SHIELD_HAND, + RR_SPIRIT_TEMPLE_CHEST_STAIRS, + RR_SPIRIT_TEMPLE_EAST_THRONE, + RR_SPIRIT_TEMPLE_EAST_HAND_EXIT, + RR_SPIRIT_TEMPLE_EAST_HAND, RR_SPIRIT_TEMPLE_BIG_WALL_LOWER, RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, RR_SPIRIT_TEMPLE_4F_CENTRAL, - RR_SPIRIT_TEMPLE_BOSS_KEY_ROOM, + RR_SPIRIT_TEMPLE_FAKE_DOORS_ROOM, RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE, - RR_SPIRIT_TEMPLE_SHORTCUT, - RR_SPIRIT_TEMPLE_INSIDE_STATUE_HEAD, + RR_SPIRIT_TEMPLE_PLATFORM, + RR_SPIRIT_TEMPLE_STATUE_HEAD, RR_SPIRIT_TEMPLE_MQ_LOBBY, RR_SPIRIT_TEMPLE_MQ_1F_WEST, @@ -893,19 +902,20 @@ typedef enum { RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, + RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, - RR_SPIRIT_TEMPLE_MQ_WEST_STAIRS_TO_HAND, - RR_SPIRIT_TEMPLE_MQ_WEST_IRON_KNUCKLE, - RR_SPIRIT_TEMPLE_MQ_EXIT_TO_SILVER_GAUNTLETS_HAND, - RR_SPIRIT_TEMPLE_MQ_SILVER_GAUNTLETS_HAND, + RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS, + RR_SPIRIT_TEMPLE_MQ_WEST_THRONE, + RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT, + RR_SPIRIT_TEMPLE_MQ_WEST_HAND, RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH, RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F, RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_1F, RR_SPIRIT_TEMPLE_MQ_1F_EAST, - RR_SPIRIT_TEMPLE_MQ_LEEVER_ROOM, + RR_SPIRIT_TEMPLE_MQ_SAND_PIT, RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM, RR_SPIRIT_TEMPLE_MQ_AFTER_SYMPHONY_ROOM, RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER, @@ -923,7 +933,7 @@ typedef enum { RR_SPIRIT_TEMPLE_MQ_NINE_CHAIRS_ROOM, RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, - RR_SPIRIT_TEMPLE_MQ_INSIDE_STATUE_HEAD, + RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, RR_SPIRIT_TEMPLE_BOSS_ROOM, @@ -3665,7 +3675,7 @@ typedef enum { RT_SHADOW_MQ_WINDY_WALKWAY, RT_LENS_SPIRIT, RT_SPIRIT_CHILD_CHU, - RT_SPIRIT_LOBBY_GS, + RT_SPIRIT_WEST_LEDGE, RT_SPIRIT_LOWER_ADULT_SWITCH, RT_SPIRIT_LOBBY_JUMP, RT_SPIRIT_PLATFORM_HOOKSHOT, @@ -6481,6 +6491,7 @@ typedef enum { RE_BARI, RE_SHABOM, RE_OCTOROK, + RE_WALLTULA, } RandomizerEnemy; // RANDOTODO compare child long jumpslash range with adult short diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index c5d0c23d8..f36ffec9a 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -1006,11 +1006,11 @@ void Settings::CreateOptions() { "Removes the requirements for the Lens of Truth in Spirit Temple."); OPT_TRICK(RT_SPIRIT_CHILD_CHU, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, "Spirit Temple Child Side Bridge with Bombchu", "A carefully-timed Bombchu can hit the switch."); - OPT_TRICK(RT_SPIRIT_LOBBY_GS, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, - "Spirit Temple Main Room GS with Boomerang", - "Standing on the highest part of the arm of the statue, a precise Boomerang throw can kill and obtain " - "this Gold Skulltula. You must throw the Boomerang slightly off to the side so that it curves into the " - "Skulltula, as aiming directly at it will clank off of the wall in front."); + OPT_TRICK(RT_SPIRIT_WEST_LEDGE, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::NOVICE }, + "Spirit Temple Statue Room West Ledge Checks with Boomerang", + "By carefully walking onto the upper arm of the statue, it's possible to get a good angle on the " + "Gold Skulltula (In Vanilla) and the farthest pot (In MQ) to collect the checks with Boomerang. " + "The nearest pot in MQ can be reached from the forearm and is always in logic."); OPT_TRICK(RT_SPIRIT_LOWER_ADULT_SWITCH, RCQUEST_VANILLA, RA_SPIRIT_TEMPLE, { Tricks::Tag::ADVANCED }, "Spirit Temple Lower Adult Switch with Bombs", "A bomb can be used to hit the switch on the ceiling, but it must be thrown from a particular distance " From 5b42fb1e4e745f60147efdf6cbf4fe2f38fc0149 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 23 Apr 2025 22:44:24 +0100 Subject: [PATCH 11/29] v0 complete --- .../randomizer/location_access.cpp | 220 +++++--- .../Enhancements/randomizer/location_access.h | 6 +- .../dungeons/spirit_temple.cpp | 524 ++++++++++++------ soh/soh/Enhancements/randomizer/logic.cpp | 23 +- soh/soh/Enhancements/randomizer/logic.h | 21 +- .../Enhancements/randomizer/randomizerTypes.h | 33 +- 6 files changed, 537 insertions(+), 290 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index 63147c2da..b2493bf8c 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -213,126 +213,176 @@ bool Here(const RandomizerRegion region, ConditionFn condition) { uint8_t SpiritExplosiveLogic() { return logic->SpiritBrokenWallToStatue() ? 1 : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 : 3; } -// RANDOTODO basically every condition here will need climb or longshot. - -// clang-format off -std::map Region::spiritLogicData = { - {RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, SpiritLogicData(5, 5, 3, []{return true;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, - {RR_SPIRIT_TEMPLE_BROKEN_WALL, SpiritLogicData(5, 5, 3, []{return true /*logic->CanClimbHigh()*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, - {RR_SPIRIT_TEMPLE_2F_MIRROR, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();} , []{return true/*logic->CanClimbHigh()*/;} , []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue();} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, - {RR_SPIRIT_TEMPLE_GS_LEDGE, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue() && logic->SpiritWestToSkull();} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic()) && logic->SpiritWestToSkull() - /* && logic->CanClimbHigh() && str0*/;} , []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue();} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true;})}, - {RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && str0*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*(logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0*/;})}, - {RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && str0*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*(logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0*/;})}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();} , []{return true/*logic->CanClimbHigh() && str0*/;} , []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, - {RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue() && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;} , []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));})}, - {RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, SpiritLogicData(7, 6, 7, []{return true;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, - {RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, SpiritLogicData(7, 6, 7, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;} , []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, - {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;} , []{return true/*logic->Climb*/;} , []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, - {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;} , []{return true;} , []{return true;})}, - {RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock()/* && logic->CanClimbHigh()*/;} , []{return logic->MQSpiritStatueToSunBlock()/* && logic->Climb*/;} , []{return logic->MQSpiritStatueToSunBlock()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, - {RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && - areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();}) - /* && logic->CanClimbHigh()*/;} , []{return false;} , []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();});})}, -}; -// clang-format on - /* - * This logic covers checks that exist in the shared areas of Spirit from a glitchless standpoint. + * This logic covers checks that exist in the shared areas of Spirit * This code will fail if any glitch allows Adult to go in the Child spirit door first or vice versa as it relies on specific ages - * There are 4 possibilities for passing a check, but first I have to talk about parallel universes. + * In order to pass a check, we must either determine that Access is certain, + or that it is always possible to get a check somehow. + + * But first I have to talk about parallel universes. * In the first universe, the player enters spirit as child, and spends as many keys as they can to lock adult out * In the second, they enter as adult and spend as many keys as they can to lock child out. - * When an Age can no longer be kept out by the other age, that age is said to have Certain Access to a region + * Additionally, if it is possible to enter spirit in reverse, there are 2 more universes: + * In the third universe, adult enters in reverse, and wastes all the keys so noone can enter through the front + * In the forth, child manages to do the same, and lock people out of the front + * All access from the boss door in shared areas is Certain + + * While other universes exist, such as both ages entering in reverse or + child using their key, getting stuck, then coming back to do the dungeon as adult, these + are all sub-possibilities of these 4 universes + + * As we do not know which universe we are in until the player chooses one in-game, + we must be able to collect the check in both universes + + * When an Age can no longer be kept out by conflicting universes, that age is said to have Certain Access to a region * If both ages have access to a region with a certain number of keys, but there is no Certain Access, - * then a check is only in logic if both ages can collect the check independently + * then a check is only in logic if all possible universes can collect the check independently - * If an age has Certain Access then that age can collect checks alone, - * and there is no reason to check the other age untile the universes converge. - - * The universes converge when the player has all the keys, giving both ages Certain Access. + * The universes converge when the player has all the keys, giving both ages Certain Access everywhere. * We must check for these universes manually as we allow technical access with minimum keys for - * technical reasons as otherwise the code will never run - */ + * technical reasons as otherwise the logic code will never run + + * The first and 3rd column list how many keys are needed for each age to have Certain Access + * the second column is child keys in case there's Child reverse access, due to an edge case in MQ spirit logic + * where the broken wall room can be reached with 6 keys if you can hit switches and have reverse Child access + + * The first condition is the combined conditions needed to move from the 1F child lock to the area being checks + * the second condition is the same for adult 1F lock, and the third is the access from the boss door. +*/ +// clang-format off +std::map Region::spiritLogicData = { + //Vanilla + {RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, SpiritLogicData(5, 5, 3, []{return true;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_BROKEN_WALL, SpiritLogicData(5, 5, 3, []{return true /*logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_2F_MIRROR, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();}, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_INNER_WEST_HAND, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_GS_LEDGE, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic()) && logic->SpiritWestToSkull() + /* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true;})}, + //Assumes SpiritSunBlockSouthLedge() for all access + {RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*(logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0*/;})}, + {RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*(logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0*/;})}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, + {RR_SPIRIT_TEMPLE_INNER_EAST_HAND, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, + {RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue() && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));})}, + //MQ /*&& logic->CanClimbHigh()*/ + {RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, SpiritLogicData(7, 6, 7, []{return true;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, SpiritLogicData(7, 6, 7, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->Climb*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;})}, + {RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;})}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true;}, []{return true;})}, + {RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock()/* && logic->CanClimbHigh()*/;} , []{return logic->MQSpiritStatueToSunBlock()/* && logic->Climb*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();}) + /* && logic->CanClimbHigh()*/;}, []{return true;}, []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();});})}, +}; +// clang-format on -bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, RandomizerRegion otherRegion, ConditionFn otherCondition){ +/* + * Spirit Shared can take up to 3 regions, this is because checks can exist in many regions at the same time + and the logic needs to be able to check the access logic from those regions to check the other universes properly. + + *anyAge is equivalent to a self referencing Here, used for events and any check where that is relevent. +*/ + +bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, + RandomizerRegion otherRegion, ConditionFn otherCondition, + RandomizerRegion thirdRegion, ConditionFn thirdCondition){ SpiritLogicData curRegionData = Region::spiritLogicData[region]; - uint8_t childKeys = logic->ReverseSpiritChild ? curRegionData.childReverseKeys : curRegionData.childKeys; + bool result = false; + + // store current age variables + bool pastAdult = logic->IsAdult; + bool pastChild = logic->IsChild; + // If child enters in reverse, then they have access to Certain Access to Broken Wall room in 6 keys, + // the ability to hit switches and the ability to climb because only child can reach the initial child lock + // without opening the Statue room to Broken Wall Room lock first + logic->IsChild = true; + logic->IsAdult = false; + uint8_t childKeys = (logic->ReverseSpiritChild && logic->CanHitSwitch()/* && CanClimbHigh()*/) ? curRegionData.childReverseKeys : curRegionData.childKeys; + // If we have enough keys that an age cannot be kept out, we have Certain Access // otherwise if we have entered in reverse and can reach from the face, we have Certain Access - bool ChildCertainAccess = (logic->ReverseSpiritChild && curRegionData.reverseAccess) || logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys); - bool AdultCertainAccess = (logic->ReverseSpiritAdult && curRegionData.reverseAccess) || logic->SmallKeys(RR_SPIRIT_TEMPLE, curRegionData.adultKeys); - // If both ages have certain access, we can test with Either age - if (ChildCertainAccess && AdultCertainAccess) { - if (anyAge) { - return areaTable[region].Here(condition); - } - return condition(); - // otherwise, we have to check the current age and... - } else if (areaTable[region].Child() && logic->IsChild) { - bool result = condition(); - // If we have Certain Access, we just run the condition. - if (ChildCertainAccess) { - return result; - // Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Adult too - } else if (result) { - SpiritLogicData otherRegionData = Region::spiritLogicData[otherRegion]; - // store current age variables - bool pastAdult = logic->IsAdult; - bool pastChild = logic->IsChild; + bool ChildCertainAccess = (logic->ReverseSpiritChild && curRegionData.reverseAccess()) || logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys); + //Switch back to adult to check adult access + logic->IsChild = false; + logic->IsAdult = true; + + bool AdultCertainAccess = (logic->ReverseSpiritAdult && curRegionData.reverseAccess()) || logic->SmallKeys(RR_SPIRIT_TEMPLE, curRegionData.adultKeys); + // If we are AnyAge and have any CeratinAccess, then we can check those ages + //we don't need to check ambiguity here as if this fails, then 1 of the ages has failed + if (anyAge && (ChildCertainAccess || AdultCertainAccess)){ + // set age access to the Certain Access + logic->IsChild = ChildCertainAccess; + logic->IsAdult = AdultCertainAccess; + + // check condition as well as having at least child or adult access + result = condition(); + + // otherwise, we have to check the current age and... + } else if (areaTable[region].Child() && pastChild) { + //Switch to Child + logic->IsChild = true; + logic->IsAdult = false; + + result = condition(); + // If we have Certain Access, we just run the condition. + // Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Adult + // and if needed, in reverse + if (!ChildCertainAccess && result && (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess())) { + //Switch to Adult logic->IsChild = false; logic->IsAdult = true; - // If Adult can get there and get the check, we can get the check in logic // If reverse spirit is also possible, we need to make sure Adult can get it via reverse entry too - result = (curRegionData.adultAccess && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) || + result = (curRegionData.adultAccess() && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) || (otherRegion != RR_NONE && - (otherRegionData.adultAccess && (!logic->IsReverseAccessPossible() || otherRegionData.reverseAccess) && otherCondition())); - - logic->IsChild = pastChild; - logic->IsAdult = pastAdult; - - return result; + (Region::spiritLogicData[otherRegion].adultAccess() && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && + otherCondition())) || + (thirdRegion != RR_NONE && + (Region::spiritLogicData[thirdRegion].adultAccess() && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && + thirdCondition())); } - } else if (areaTable[region].Adult() && logic->IsAdult) { - bool result = condition(); + } else if (areaTable[region].Adult() && pastAdult) { + result = condition(); // if we have enough keys to have Certain Access, we just run the condition // Alternatively, if we have entered in reverse and can reach from the face, we have Certain Access - if (AdultCertainAccess) { - return result; - // Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Child too - } else if (result){ - SpiritLogicData otherRegionData = Region::spiritLogicData[otherRegion]; - // store current age variables - bool pastAdult = logic->IsAdult; - bool pastChild = logic->IsChild; - + // Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Child + // and if needed, in reverse + if (!AdultCertainAccess && result && (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess)){ + //Switch to Child logic->IsChild = true; logic->IsAdult = false; // If Child can get there and get the check, we can get the check in logic // If reverse spirit is also possible, we need to make sure Child can get it via reverse entry too - result = (curRegionData.childAccess && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) || + result = (curRegionData.childAccess() && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess()) && condition()) || (otherRegion != RR_NONE && - (otherRegionData.childAccess && (!logic->IsReverseAccessPossible() || otherRegionData.reverseAccess) && otherCondition())); - - logic->IsChild = pastChild; - logic->IsAdult = pastAdult; - - return result; + (Region::spiritLogicData[otherRegion].childAccess() && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && + otherCondition())) || + (thirdRegion != RR_NONE && + (Region::spiritLogicData[thirdRegion].childAccess() && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && + thirdCondition())); } } - return false; + // set back age variables + logic->IsChild = pastChild; + logic->IsAdult = pastAdult; + return result; } bool BeanPlanted(const RandomizerRegion region) { diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index f1e943454..4cc3e02ef 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -252,9 +252,6 @@ class Region { } static std::map spiritLogicData; - - bool SpiritShared(ConditionFn condition, ConditionFn childAccess, ConditionFn adultAccess, ConditionFn ReverseAccess, - uint8_t childKeys, uint8_t adultKeys, uint8_t eitherKeys, bool anyAge); }; extern std::array areaTable; @@ -264,7 +261,8 @@ bool Here(const RandomizerRegion region, ConditionFn condition); // RANDOTODO make a less stupid way to check own at either age than self referencing with this bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge = false, - RandomizerRegion otherRegion = RR_NONE, ConditionFn otherCondition = []{return false;}); + RandomizerRegion otherRegion = RR_NONE, ConditionFn otherCondition = []{return false;}, + RandomizerRegion thirdRegion = RR_NONE, ConditionFn thirdCondition = []{return false;}); bool CanPlantBean(const RandomizerRegion region); bool BothAges(const RandomizerRegion region); bool ChildCanAccess(const RandomizerRegion region); diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index 8c2f49d58..8d2250291 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -23,7 +23,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->IsChild/*CanUse(RG_CRAWL)*/;}), Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return logic->CanUse(RG_SILVER_GAUNTLETS);}), }); @@ -32,15 +32,15 @@ void RegionTable_Init_SpiritTemple() { EventAccess(&logic->NutCrate, []{return true;}), }, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->IsChild;}), - Entrance(RR_SPIRIT_TEMPLE_1F_BOXES, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->IsChild/*CanUse(RG_CRAWL)*/;}), + Entrance(RR_SPIRIT_TEMPLE_1F_BOXES, []{return logic->IsChild/*CanUse(RG_CRAWL)*/;}), Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH, []{return Here(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);});}), Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH, []{return Here(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);});}), }); areaTable[RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH] = Region("Spirit Temple Switch Bridge South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - //RANDOTODO a version od CanHitSwitch that takes WallOrFloor + //RANDOTODO a version of CanHitSwitch that takes WallOrFloor EventAccess(&logic->SpiritChildSwitchBridge, []{return logic->CanUse(RG_BOOMERANG) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_FAIRY_BOW) || (logic->CanUse(RG_BOMBCHU_5) && ctx->GetTrickOption(RT_SPIRIT_CHILD_CHU));}), }, {}, { //Exits @@ -74,13 +74,13 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH] = Region("Spirit Temple Rupee Bridge North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->SpiritRupeeBridge, []{return true;}), + EventAccess(&logic->SpiritRupeeBridge, []{return true/*CanClimb()*/;}), }, { //Locations // these assume SpiritRupeeBridge, silver rupee shuffle & shuffle climb will want to adjust LOCATION(RC_SPIRIT_TEMPLE_CHILD_EARLY_TORCHES_CHEST, logic->HasFireSourceWithTorch()), // possible to collect without lowering fence, should be a trick - LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOMB_THROW)), + LOCATION(RC_SPIRIT_TEMPLE_GS_METAL_FENCE, logic->SpiritRupeeBridge && logic->CanKillEnemy(RE_GOLD_SKULLTULA, ED_BOMB_THROW)), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH, []{return logic->SpiritRupeeBridge;}), @@ -102,7 +102,7 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_BEFORE_CHILD_CLIMB_SMALL_CRATE_2, logic->CanBreakSmallCrates()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->IsChild/*CanUse(RG_CRAWL)*/;}), Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 1);}), }); @@ -113,7 +113,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_1F_BOXES, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), - Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return true/*CanClimbHigh()*/;}), }); areaTable[RR_SPIRIT_TEMPLE_BROKEN_WALL] = Region("Spirit Temple Broken Wall", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -160,7 +160,7 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_GS_BOULDER_ROOM, logic->CanUse(RG_SONG_OF_TIME) && logic->CanKillEnemy(RE_GOLD_SKULLTULA)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_ABOVE_BOULDERS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_ABOVE_BOULDERS, []{return true/*CanClimb()*/;}), //Jump slash is possible as child, but pretty tight. Jumpslash as late as you can //A damage boost off the boulder is also possible, but you need Entrance(RR_SPIRIT_TEMPLE_PAST_BOULDERS, []{return logic->SpiritBouldersSilvers;}), @@ -178,56 +178,71 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_EAST_CLIMB_BASE] = Region("Spirit Temple East Climb Base", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), - Entrance(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true/*CanClimbHigh()*/;}), }); areaTable[RR_SPIRIT_TEMPLE_2F_MIRROR] = Region("Spirit Temple 2F Mirror", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_LEFT_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;})), - LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_RIGHT_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;})), + LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_LEFT_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true/*str0 || SunlightArrows()*/;})), + LOCATION(RC_SPIRIT_TEMPLE_FIRST_MIRROR_RIGHT_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true/*str0 || SunlightArrows()*/;})), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_EAST_CLIMB_BASE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true/*str0 || SunlightArrows()*/;}), }); areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST] = Region("Spirit Temple Statue Rooom West", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations //Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM access - LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));})), - //Current and N64 logic doesn't need scarecrow, you can hit the skull with normal hookshot from a specific spot on the forearm - //Child can get this with hook by backflipping onto the upper arm and standing in a precise place - LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE) ? ED_BOOMERANG : ED_HOOKSHOT);}, false, - RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA);})), + LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));}, false, + RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasFireSource();})), + LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_LONGSHOT);}, false, + RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE) ? ED_BOOMERANG : ED_HOOKSHOT);}, + RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA);})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return true;}), - //(IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SCARECROW) - Entrance(RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->SpiritWestToSkull();}), + Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SCARECROW);}), // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled - Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered && - (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), - Entrance(RR_SPIRIT_TEMPLE_EMPTY_STAIRS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered && + (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), + Entrance(RR_SPIRIT_TEMPLE_EMPTY_STAIRS, []{return true/*str0*/;}), //!QUANTUM LOGIC! //When child enters spirit in reverse, has 4 keys, and dungeon entrance shuffle is off, //Child cannot lock themselves out of desert colossus access as if they save the west hand lock for last //they will be able to exit the dungeon through the intended entrance and vice versa //for needing to open the west hand lock to block the intended child route - Entrance(RR_DESERT_COLOSSUS, []{return ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && logic->ReverseSpiritChild && - logic->IsChild && logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && logic->CanKillEnemy(RE_IRON_KNUCKLE);}), + Entrance(RR_DESERT_COLOSSUS, []{return ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && logic->ReverseSpiritChild && /*str0 &&*/ + logic->IsChild/*CanUse(RG_CRAWL)*/ && logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && logic->CanKillEnemy(RE_IRON_KNUCKLE);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_INNER_WEST_HAND] = Region("Spirit Temple Inner West Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + //You can hit the skull with normal hookshot from a specific spot on the forearm, which is in logic on N64 and 3DS, pretty questionable though + LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritShared(RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE) ? ED_BOOMERANG : ED_HOOKSHOT);}, false, + RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA);}, + RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_LONGSHOT);})), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP);}) }); areaTable[RR_SPIRIT_TEMPLE_GS_LEDGE] = Region("Spirit Temple GS ledge", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritShared(RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA);})), + LOCATION(RC_SPIRIT_TEMPLE_GS_LOBBY, SpiritShared(RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA);}, false, + RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE) ? ED_BOOMERANG : ED_HOOKSHOT);}, + RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_LONGSHOT);})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return true;}) + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return true;}) }); areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM] = Region("Spirit Temple Statue Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasFireSource();}, false, + LOCATION(RC_SPIRIT_TEMPLE_MAP_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->HasFireSource();}, false, RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_MAP_CHEST) && logic->CanUse(RG_FAIRY_BOW));})), LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_1, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})), LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_2, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})), @@ -237,7 +252,7 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_CENTRAL_CHAMBER_POT_6, SpiritShared(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->CanBreakPots();})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return true/*CanClimb()*/;}), // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered && (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), @@ -252,15 +267,29 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM] = Region("Spirit Temple Sun Block Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM] = Region("Spirit Temple Sun Block Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_EMPTY_STAIRS, []{return true/*str0 || SunlightArrows*/;}), + //The blocks can be used to get onto this ledge itemless + //true/*str0 || IsAdult || CanKillEnemy(RE_BEAMOS) || BunnyHovers() || + //((HasFireSource() || (SpiritSunBlockTorch && (logic->CanUse(STICKS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))))) && CanUse(RG_HOOKSHOT))*/ + Entrance(RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, []{return logic->SpiritSunBlockSouthLedge();}), + Entrance(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return true/*str0 || SunlightArrows*/;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE] = Region("Spirit Temple Sun Block South ledge", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + //Assumes RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM access + EventAccess(&logic->SpiritSunBlockTorch, []{return SpiritShared(RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, []{return true;}, true);}), + }, { //Locations - //the blocks can be used to get all the silver rupees and the chest itemless - LOCATION(RC_SPIRIT_TEMPLE_SUN_BLOCK_ROOM_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, - []{return logic->HasFireSourceWithTorch() || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW));})), + //Spawning the chest to get here is accounted for in movement logic, so we only need to confirm it can be spawned here + LOCATION(RC_SPIRIT_TEMPLE_SUN_BLOCK_ROOM_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, + []{return logic->HasFireSource() || + (logic->SpiritSunBlockTorch && (logic->CanUse(RG_STICKS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))));})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_EMPTY_STAIRS, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS] = Region("Spirit Temple Skulltula Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -283,10 +312,10 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_WEST_HAND_EXIT] = Region("Spirit Temple West Hand Exit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_WEST_THRONE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_WEST_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_WEST_HAND] = Region("Spirit Temple West Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_OUTER_WEST_HAND] = Region("Spirit Temple West Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, true), }, { @@ -295,11 +324,7 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST] = Region("Spirit Temple Statue Room East", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { - //Locations - //Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST access. WARNING: ZL spawning chests here is a temp flag - LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_HAND_CHEST, logic->CanUse(RG_ZELDAS_LULLABY)), - }, { + areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST] = Region("Spirit Temple Statue Room East", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), @@ -307,14 +332,40 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return logic->SpiritEastToSwitch();}), Entrance(RR_SPIRIT_TEMPLE_POT_STAIRS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}), //!QUANTUM LOGIC! - //With 3 keys, you cannot lock adult out of longshotting to the west hand as you would have to - //open the west hand door and then adult could climb through sun block room + //With 3 keys, you cannot lock adult out of leaving spirit onto the hands and jumping down, as you would have to + //open the west hand door and then adult could climb through sun block room to jump down from there //This requires that adult can complete both routes //Implies CanKillEnemy(RE_IRON_KNUCKLE) - Entrance(RR_SPIRIT_TEMPLE_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanUse(RG_LONGSHOT) && logic->CanKillEnemy(RE_BEAMOS);}), - //Similarly, we can gurantee jumping into desert colossus the same way - //except this does not need the longshot as we don't care which hand we jump down from - Entrance(RR_DESERT_COLOSSUS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanKillEnemy(RE_BEAMOS);}), + Entrance(RR_DESERT_COLOSSUS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanKillEnemy(RE_BEAMOS)/* && CanClimb() && str0*/;}), + //!QUANTUM LOGIC! + //Continuing from above, if you also have the Longshot, then you can always reach the outer west hand, as you can longshot from east hand + Entrance(RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanUse(RG_LONGSHOT) && logic->CanKillEnemy(RE_BEAMOS)/* && CanClimb() && str0*/;}), + //!QUANTUM LOGIC! + //A variant of the above, if dungeon entrance randomiser is off, Adult entered spirit in reverse, and they have str 2 to get there from the front, + //it is always possible for them to reach 1F_EAST with only 2 keys. This is because you can only waste 1 key (on the first child side lock) + //before you either allow you to climb down through 2F mirror room, or give yourself access to a hand to jump down from. + //If for whatever reason you can reach east hand but not west hand, this becomes possible with 3 keys instead. + //If for whatever reason you can reach west hand but not east hand, this becomes possible with 4 keys instead. + Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && + logic->CanUse(RG_SILVER_GAUNTLETS) && + ((logic->CanKillEnemy(RE_BEAMOS) && logic->SmallKeys(RR_SPIRIT_TEMPLE, /*CanClimb() && str0 ?*/ 2 /*: 3*/)) || + (/*CanClimb() && str0 && */logic->CanKillEnemy(RE_IRON_KNUCKLE) && logic->SmallKeys(RR_SPIRIT_TEMPLE, 4)));}), + //RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered && + (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), + }); + + areaTable[RR_SPIRIT_TEMPLE_INNER_EAST_HAND] = Region("Spirit Temple Inner East Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + //Assumes RR_SPIRIT_TEMPLE_INNER_WEST_HAND access via RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST and RR_SPIRIT_TEMPLE_STATUE_ROOM. + LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_HAND_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_INNER_EAST_HAND, []{return logic->CanUse(RG_ZELDAS_LULLABY)/* && CanClimb()*/;})), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->CanUse(RG_HOVER_BOOTS);}), + //Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST access via RR_SPIRIT_TEMPLE_STATUE_ROOM + Entrance(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || + (logic->CanUse(RG_ZELDAS_LULLABY) && logic->CanUse(RG_HOOKSHOT));}), }); areaTable[RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH] = Region("Spirit Temple Shortcut Switch", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { @@ -322,21 +373,21 @@ void RegionTable_Init_SpiritTemple() { EventAccess(&logic->SpiritStatueRoomSouthDoor, []{return SpiritShared(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return logic->CanUse(RG_MEGATON_HAMMER);});}), }, { //Locations - //Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST access - LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_NORTHEAST_CHEST, logic->CanUse(RG_ZELDAS_LULLABY)), + //Assumes RR_SPIRIT_TEMPLE_INNER_EAST_HAND access + LOCATION(RC_SPIRIT_TEMPLE_STATUE_ROOM_NORTHEAST_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return logic->CanUse(RG_ZELDAS_LULLABY);})), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_SHORTCUT] = Region("Spirit Temple Shortcut", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { - //If child can ever use silver gauntlets and adult items, there needs to be an event here to account for child entering in reverse + //If child can ever use silver gauntlets, there needs to be an event here to account for child entering in reverse //opening the way for adult entering via the front. Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->CanUse(RG_SILVER_GAUNTLETS) && logic->CanUse(RG_MEGATON_HAMMER);}), }); - areaTable[RR_SPIRIT_TEMPLE_POT_STAIRS] = Region("Spirit Temple Stairs to Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_POT_STAIRS] = Region("Spirit Temple Pot Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { LOCATION(RC_SPIRIT_TEMPLE_BEAMOS_HALL_POT_1, logic->CanBreakPots()), }, { Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}), @@ -346,18 +397,18 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_BEAMOS_PITS] = Region("Spirit Temple Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits //Implies killing the anubis with the fire ring, doing so itemless requires voiding out, which can lock hardcore + OHKO seeds - Entrance(RR_SPIRIT_TEMPLE_POT_STAIRS, []{return logic->CanKillEnemy(RE_BEAMOS);}), - Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return logic->CanKillEnemy(RE_BEAMOS);}), - Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_LOWER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_POT_STAIRS, []{return logic->CanKillEnemy(RE_BEAMOS);}), + Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return logic->CanKillEnemy(RE_BEAMOS);}), + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_BASE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), }); areaTable[RR_SPIRIT_TEMPLE_FOUR_ARMOS] = Region("Spirit Temple Four Armos", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_NEAR_FOUR_ARMOS_CHEST, (logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))) && logic->HasExplosives()), + LOCATION(RC_SPIRIT_TEMPLE_NEAR_FOUR_ARMOS_CHEST, (logic->CanUse(RG_MIRROR_SHIELD) || logic->SunlightArrows()) && logic->HasExplosives()), LOCATION(RC_SPIRIT_TEMPLE_ARMOS_ROOM_SUN_FAIRY, logic->HasExplosives() && logic->CanUse(RG_SUNS_SONG)), }, { Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS_SIDE_ROOM, []{return logic->CanUse(RG_MIRROR_SHIELD) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), + Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS_SIDE_ROOM, []{return logic->CanUse(RG_MIRROR_SHIELD) || logic->SunlightArrows();}), Entrance(RR_SPIRIT_TEMPLE_CHEST_STAIRS, []{return true;}), }); @@ -387,24 +438,25 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_EAST_HAND_EXIT] = Region("Spirit Temple East hand Exit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_EAST_THRONE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_EAST_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EAST_THRONE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_OUTER_EAST_HAND, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_EAST_HAND] = Region("Spirit Temple East Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_OUTER_EAST_HAND] = Region("Spirit Temple Outer East Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MIRROR_SHIELD_CHEST, true), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_EAST_HAND_EXIT, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_WEST_HAND, []{return logic->CanUse(RG_LONGSHOT);}), - Entrance(RR_DESERT_COLOSSUS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_EAST_HAND_EXIT, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, []{return logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_BIG_WALL_LOWER] = Region("Spirit Temple Big Wall Lower", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_BIG_WALL_BASE] = Region("Spirit Temple Big Wall Base", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return ctx->GetTrickOption(RT_SPIRIT_WALL) || (logic->CanAvoidEnemy(RE_BEAMOS, true, 2) && logic->CanPassEnemy(RE_WALLTULA));}), + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return (ctx->GetTrickOption(RT_SPIRIT_WALL) || (logic->CanAvoidEnemy(RE_BEAMOS, true, 2) && logic->CanPassEnemy(RE_WALLTULA))) + /*&& CanClimbHigh()*/;}), }); areaTable[RR_SPIRIT_TEMPLE_BIG_WALL_UPPER] = Region("Spirit Temple Big Wall Upper", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -414,8 +466,8 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_ADULT_CLIMB_RIGHT_HEART, logic->CanUse(RG_HOOKSHOT)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_LOWER, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_BASE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_4F_CENTRAL] = Region("Spirit Temple 4F Central", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -439,27 +491,27 @@ void RegionTable_Init_SpiritTemple() { //Events EventAccess(&logic->Spirit4FSwitch, []{return logic->CanJumpslash() || logic->HasExplosives();}), //Needs the mirror in the cave to be a perm flag and event for doorsanity - EventAccess(&logic->SpiritPlatformLowered, []{return (Here(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return logic->CanJumpslash() || logic->HasExplosives();}) || logic->CanUse(RG_MIRROR_SHIELD)) || - (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), + EventAccess(&logic->SpiritPlatformLowered, []{return (logic->SpiritPushed4FMirrors && logic->CanUse(RG_MIRROR_SHIELD)) || logic->SunlightArrows();}), }, {}, { Entrance(RR_SPIRIT_TEMPLE_4F_CENTRAL, []{return true;}), //Rang can hit the switch on the way back but that's a trick Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE, []{return logic->Spirit4FSwitch;}), //Assumes RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE access - Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return (logic->CanUse(RG_MIRROR_SHIELD) && logic->HasExplosives() && logic->Spirit4FSwitch) || - (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered;}), }); areaTable[RR_SPIRIT_TEMPLE_BIG_MIRROR_CAVE] = Region("Spirit Temple Big Mirror Cave", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->Spirit4FSwitch, []{return logic->HasExplosives();}), + EventAccess(&logic->Spirit4FSwitch, []{return logic->HasExplosives();}), + //WARNING: not currently a permanent flag, needs to be one for doorsanity + EventAccess(&logic->SpiritPushed4FMirrors, []{return logic->HasExplosives()/*&& str0*/;}), }, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_TOPMOST_CHEST, (logic->IsAdult && logic->CanUse(RG_MIRROR_SHIELD)) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))), + LOCATION(RC_SPIRIT_TEMPLE_TOPMOST_CHEST, (logic->IsAdult && logic->CanUse(RG_MIRROR_SHIELD)) || logic->SunlightArrows()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_BIG_MIRROR_ROOM, []{return true;}), }); //Assumes a SpiritPlatformLowered check on entry @@ -467,9 +519,10 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_STATUE_HEAD, []{return Here(RR_SPIRIT_TEMPLE_STATUE_HEAD, []{return logic->CanUse(RG_MIRROR_SHIELD) && logic->HasExplosives() && logic->Spirit4FSwitch;}) - && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_INNER_EAST_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_HEAD, []{return logic->SpiritPushed4FMirrors && logic->CanUse(RG_HOOKSHOT);}), }); areaTable[RR_SPIRIT_TEMPLE_STATUE_HEAD] = Region("Spirit Temple Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { @@ -480,8 +533,8 @@ void RegionTable_Init_SpiritTemple() { }, {}, { // Exits Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanUse(RG_HOVER_BOOTS);}), - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_INNER_EAST_HAND, []{return logic->CanUse(RG_HOVER_BOOTS);}), Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT));}), Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), }); @@ -503,7 +556,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_ENTRYWAY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return logic->IsChild;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return logic->IsChild/*logic->CanUse(RG_CRAWL) && logic->HasSoul(RG_NABOORU_SOUL)*/;}), Entrance(RR_SPIRIT_TEMPLE_MQ_1F_EAST, []{return false;}), Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH, []{return logic->CanUse(RG_LONGSHOT) && logic->CanUse(RG_BOMBCHU_5);}), }); @@ -520,9 +573,11 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_RIGHT_HEART, logic->CanHitEyeTargets()), }, { //Exits + //Nabooru's legs are technically visible one way collision here, but I'm not sure if this counts + Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return logic->IsChild/*logic->CanUse(RG_CRAWL)*/;}), Entrance(RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_SOUTH, []{return Here(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return logic->CanKillEnemy(RE_TORCH_SLUG);});}), Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_SOUTH, []{return Here(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return logic->CanKillEnemy(RE_TORCH_SLUG);});}), - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_1F_RUSTED_SWITCH, []{return logic->IsChild && logic->MQSpiritCrawlBoulder;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_1F_RUSTED_SWITCH, []{return logic->IsChild/*logic->CanUse(RG_CRAWL)*/ && logic->MQSpiritCrawlBoulder;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_SOUTH] = Region("Spirit Temple MQ 1F Gibdo Room South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { @@ -531,7 +586,7 @@ void RegionTable_Init_SpiritTemple() { }, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH, []{return logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets();}), + Entrance(RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH, []{return logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets()/* && str0*/;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH] = Region("Spirit Temple MQ Gibdo Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -543,7 +598,7 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return logic->MQSpiritGibdosCleared;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple Turntable Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM] = Region("Spirit Temple MQ Turntable Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events EventAccess(&logic->FairyPot, []{return Here(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return logic->CanKillEnemy(RE_STALFOS);});}), }, { @@ -561,7 +616,8 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_NORTH] = Region("Spirit Temple MQ Map Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->MQSpiritMapRoomEnemies, []{return logic->CanKillEnemy(RE_ANUBIS) && logic->CanKillEnemy(RE_KEESE);}), + EventAccess(&logic->MQSpiritMapRoomEnemies, []{return /*(*/logic->CanKillEnemy(RE_ANUBIS) && logic->CanKillEnemy(RE_KEESE)/*) && (str0 || CanUse(RG_HOVER_BOOTS) || CanUse(RG_HOOKSHOT)) || + logic->CanKillEnemy(RE_ANUBIS, ED_BOOMERANG) && logic->CanKillEnemy(RE_KEESE, ED_FAR)*/;}), }, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_MAP_ROOM_ENEMY_CHEST, logic->MQSpiritMapRoomEnemies), @@ -569,7 +625,7 @@ void RegionTable_Init_SpiritTemple() { //Exits //Stalfos room blocks you in with fire until you kill the stalfos, which won't spawn from behind the fire Entrance(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return false;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_SOUTH, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_SOUTH, []{return true/*str0 || CanUse(RG_HOVER_BOOTS) || CanUse(RG_HOOKSHOT)*/;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_SOUTH] = Region("Spirit Temple MQ Map Room South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { @@ -592,7 +648,7 @@ void RegionTable_Init_SpiritTemple() { EventAccess(&logic->MQSpiritCrawlBoulder, []{return logic->CanUse(RG_BOMBCHU_5) || (ctx->GetTrickOption(RT_RUSTED_SWITCHES) && logic->CanUse(RG_MEGATON_HAMMER));}), }, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return logic->IsChild && logic->MQSpiritCrawlBoulder;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return logic->IsChild/*logic->CanUse(RG_CRAWL)*/ && logic->MQSpiritCrawlBoulder;}), //This tracks possible child access, if adult has not entered STATUE_ROOM. Certain Child Access is checked for separately as 7 Keys Entrance(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 1);}), }); @@ -602,9 +658,9 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_LIKE_LIKE_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->CanBreakPots();})), }, { //Exits - //This covers adult access only, as child arrives here from the other side of this door + //This covers adult and reverse access only, as child going forwards arrives here from the other side of this door Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_1F_RUSTED_SWITCH, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->CanHitSwitch();}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->CanHitSwitch()/* && CanClimbHigh()*/;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM] = Region("Spirit Temple MQ Broken Wall Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -612,37 +668,81 @@ void RegionTable_Init_SpiritTemple() { //Implies CanKillEnemy(RE_LIKE_LIKE) LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_NORTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->CanKillEnemy(RE_BEAMOS);})), //Sunlights only temp spawn this chest, which is unintuitive/a bug. - LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_SOUTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return (logic->HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS))) && logic->CanUse(RG_HOOKSHOT);})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_SOUTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return (logic->HasExplosives() || logic->SunlightArrows()) && logic->CanUse(RG_HOOKSHOT);})), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->CanHitSwitch();}), - //This exit only governs child possible access, adult access starts on the other side so never checks this + //This exit only governs child forwards access, adult and reverse access starts on the other side so never checks this Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 2);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST] = Region("Spirit Temple MQ Statue Room West", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { - //Events - EventAccess(&logic->SpiritStatueRoomSouthDoor, []{return SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_SONG_OF_TIME);});}), - }, { + areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST] = Region("Spirit Temple MQ Statue Room West", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_EAST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return ((logic->IsAdult || logic->CanJumpslash()) && logic->CanUse(RG_BOOMERANG)) || - ((logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP))) && logic->CanBreakPots());})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_WEST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return ((logic->IsAdult || logic->CanJumpslash()) && logic->CanUse(RG_BOOMERANG) && ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE)) || - (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME) || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) && logic->CanBreakPots());})), LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_SMALL_CRATE, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return logic->CanBreakSmallCrates();})), }, { //Exits - //Minimal entry, real key logic handled in Shared functions - Entrance(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6);}), - //IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME) - Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return logic->MQSpiritStatueToSunBlock();}), + //!QUANTUM LOGIC! + //If we entered in reverse and dungeon entrances are off, we only need 6 keys, access to Gauntlets Hand and the ability to crawl to reach colossus + //This is because with 6 keys it becomes impossible to avoid opening either the west hand lock or the first child side lock + //and either direction lets child reach colossus. CanHitSwitch and CanKillEnemy(RE_IRON_KNUCKLE) is implied. + //Logic can then allow child back into spirit, putting 1F west in logic with only 6 keys without forwards entry + Entrance(RR_DESERT_COLOSSUS, []{return logic->IsChild/*CanUse(RG_CRAWL)*/ && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && + logic->ReverseSpiritChild && logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && /*(str0 || SunlightArrows())&&*/ + (logic->CanUse(RG_BOMBCHU_5) || (ctx->GetTrickOption(RT_RUSTED_SWITCHES) && logic->CanUse(RG_MEGATON_HAMMER)));}), + //!QUANTUM LOGIC! + //If we have 6 keys and Child reverse spirit entry, we can gurantee broken wall room access for Child + //as long as we can hit a switch and because Adult cannot reach the initial child lock without + //first opening the Statue Room to Broken Wall Room lock. The details of this are handled in SpiritShared. + //if adult can ever cross crawlspaces this becomes more complicated. + Entrance(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, []{return logic->IsAdult || logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS);}), + //(IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME)) && str0 + Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return logic->MQSpiritStatueToSunBlock();}), //explicit adult check here is a precaution against possible child logic leaking, child with a hookshot can do this - Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_POT_LEDGE] = Region("Spirit Temple MQ Pot Ledge", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + //This event does not need handling in SpiritShared as it only affects navigation, adult access here is always Certain, and Child has no way through that Adult does not. + EventAccess(&logic->SpiritStatueRoomSouthDoor, []{return ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_SONG_OF_TIME);}), + }, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_EAST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE , []{return logic->CanBreakPots();}, false, + RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND , []{return logic->CanUse(RG_BOOMERANG);})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_WEST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE , []{return logic->CanBreakPots();}, false, + RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND , []{return ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE) && logic->CanUse(RG_BOOMERANG);})), + }, { + //Exits + //This is pretty tight to reach the SoT block, but you can just go via the hand... + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND] = Region("Spirit Temple MQ Inner West Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_EAST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, []{return logic->CanUse(RG_BOOMERANG);}, false, + RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_WEST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, []{return logic->CanUse(RG_BOOMERANG) && ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE);}, false, + RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->CanBreakPots();})), + }, { + //Exits + //Land on the SoT block + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP);}), + // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled + Entrance(RR_SPIRIT_TEMPLE_MQ_PLATFORM, []{return logic->SpiritPlatformLowered && + (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM] = Region("Spirit Temple MQ Statue Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->SpiritStatueRoomSouthDoor, []{return SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->HasFireSource();});}), + //This event does not need handling in SpiritShared as it only affects navigation, Adult access here is always Certain, and Child has no way through that adult does not. + EventAccess(&logic->SpiritStatueRoomSouthDoor, []{return logic->HasFireSource();}), + //Assuming all higher areas filter down to here for this despite there being many good angles to use FAs + EventAccess(&logic->MQSpiritStatueRoomTorches, []{return logic->CanUse(RG_FIRE_ARROWS) || (ctx->GetTrickOption(RT_SPIRIT_MQ_LOWER_ADULT) && logic->CanUse(RG_DINS_FIRE));}), }, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_COMPASS_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanHitEyeTargets();})), @@ -651,15 +751,16 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_2F_EASTMOST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakPots();})), LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_CRATE_1, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakCrates();})), LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_CRATE_2, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return logic->CanBreakCrates();})), - //the drop sometimes flies off the block when the crate is blown up, but not always, so I added a rang requirement - //a trick to reload for it is plausible + //The drop sometimes flies off the block when the crate is blown up, but not always, so I added a rang requirement + //A trick to reload for it is plausible LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_SMALL_CRATE, SpiritShared(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return (logic->CanUse(RG_SONG_OF_TIME) && logic->CanBreakSmallCrates()) || - (logic->CanUse(RG_BOOMERANG) && logic->HasExplosives());})), + (logic->CanUse(RG_BOOMERANG) && logic->HasExplosives());})), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return true/*logic->CanClimb()*/;}), Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, []{return logic->SpiritStatueRoomSouthDoor;}), - //explicit adult check here is a precaution against possible child logic leaking, child with a hookshot can do this + //explicit adult check here is a precaution against possible Child logic leaking, Child with a hookshot can do this + //It's possible to climb the legs of the statue to line up a longshot hit, but you can just go via West Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_SCARECROW);}), }); @@ -672,14 +773,14 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM] = Region("Spirit Temple MQ Sun Block Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations //We don't need Shared here because If we are checking as child, universe 2 adult access needs nothing so it always passes, and if we are checking as adult, it is Certain Access - LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM_CHEST, SpiritMQSharedSunBlockRoom([]{return true/*str0*/;})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_SUN_BLOCK_ROOM, SpiritMQSharedSunBlockRoom([]{return logic->CanUse(RG_HOOKSHOT) || (ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_GS) && logic->CanUse(RG_BOOMERANG));})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_1, SpiritMQSharedSunBlockRoom([]{return logic->CanBreakPots();})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_2, SpiritMQSharedSunBlockRoom([]{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true/*str0*/;})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_SUN_BLOCK_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanUse(RG_HOOKSHOT) || (ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_GS) && logic->CanUse(RG_BOOMERANG));})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_1, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanBreakPots();})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_2, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanBreakPots();})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return true/*str0*/;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS, []{return true/*str0*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return true/*str0 || SunlightArrows()*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS , []{return true/*str0 || SunlightArrows()*/;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS] = Region("Spirit Temple MQ Skulltula Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -697,11 +798,11 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT] = Region("Spirit Temple MQ West Hand Exit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_THRONE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_THRONE , []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_HAND] = Region("Spirit Temple MQ West Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND] = Region("Spirit Temple MQ Outer West Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, true), }, { @@ -716,44 +817,77 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return true;}), //The block here is unusual in that it is a permanent flag, but reset anyway as child. This is because there's a check that would be blocked off by pushing them otherwise //It may be worth considering making this always temp in future so adult doesn't have the same issue - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, []{return logic->IsChild ? logic->CanUse(RG_SILVER_GAUNTLETS) : Here(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return logic->CanUse(RG_SILVER_GAUNTLETS);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, []{return logic->IsChild ? logic->CanUse(RG_SILVER_GAUNTLETS) : Here(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH, []{return logic->CanUse(RG_SILVER_GAUNTLETS);});}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH] = Region("Spirit Temple MQ Block Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - //Dhard child locked because adult pushing the block is a permanent flag that blocks the eye target and cannot be undone - LOCATION(RC_SPIRIT_TEMPLE_MQ_SILVER_BLOCK_HALLWAY_CHEST, SpiritMQSharedBigBlock([]{return logic->IsChild && logic->CanHitEyeTargets();})), + //Hard child locked because pushing the block is a permanent flag that blocks the eye target and cannot be undone + //but it resets as Child as a rando QoL feature + LOCATION(RC_SPIRIT_TEMPLE_MQ_SILVER_BLOCK_HALLWAY_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, []{return logic->IsChild && logic->CanHitEyeTargets();})), }, { //Exits //if going to RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH from here is ever relevant, there needs to be an event to handle the block Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST] = Region("Spirit Temple MQ Statue Room East", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { - //Locations - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_ROOM_LULLABY_CHEST, logic->CanUse(RG_HOOKSHOT) & logic->CanUse(RG_ZELDAS_LULLABY) && (logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS))), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_ROOM_INVISIBLE_CHEST, (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS))), - }, { + areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST] = Region("Spirit Temple MQ Statue Room East", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_EAST_HAND, []{return logic->IsAdult || logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_CHEST_LEDGE, []{return logic->CanUse(RG_HOVER_BOOTS) || + ((ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->CanUse(RG_LONGSHOT));}), + Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F, []{return logic->MQSpiritStatueRoomTorches;}), //!QUANTUM LOGIC! - //We only need 4 keys, access to Shield hand and longshot to reach Gauntlets hand, as if we waste the 5th key we have given ourselves Gauntlets hand access through child climb - //This exit handles that possibility as cleanly as possible without quantum logic, but will not survive glitch logic + //We only need 4 keys and the ability to reach both hands for adult to logically be able to drop down onto Desert Colossus + //This is because there are only 3 keys that can be wasted without opening up either this lock to East hand, or the West Hand lock through Sun Block Room + //and both directions allow you to drop onto colossus //logic->CanKillEnemy(RE_FLOORMASTER) is implied - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && + Entrance(RR_DESERT_COLOSSUS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && - logic->CanJumpslash() && + logic->CanJumpslash() && /*(str0 || SunlightArrows) &&*/ (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->CanKillEnemy(RE_IRON_KNUCKLE) && - logic->CanUse(RG_LONGSHOT);}), + logic->CanUse(RG_HOOKSHOT);}), + //!QUANTUM LOGIC! + //Continuing from above, if we also have a longshot, we can go from the East hand to the West hand, meaning we always have access to East Hand + Entrance(RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && + logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && + logic->CanJumpslash() && /*(str0 || SunlightArrows) &&*/ + (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && + logic->CanKillEnemy(RE_IRON_KNUCKLE) && + logic->CanUse(RG_LONGSHOT);}), Entrance(RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F, []{return logic->CanUse(RG_FIRE_ARROWS) || (ctx->GetTrickOption(RT_SPIRIT_MQ_LOWER_ADULT) && logic->CanUse(RG_DINS_FIRE));}), + // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled + Entrance(RR_SPIRIT_TEMPLE_MQ_PLATFORM, []{return logic->SpiritPlatformLowered && + (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_INNER_EAST_HAND] = Region("Spirit Temple MQ Inner East Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_ROOM_LULLABY_CHEST, logic->CanUse(RG_ZELDAS_LULLABY) && logic->CanBreakCrates()), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_CHEST_LEDGE, []{return ((logic->IsAdult || ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE)) && (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->CanUse(RG_HOOKSHOT)) + || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP));}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_CHEST_LEDGE] = Region("Spirit Temple MQ Chest Ledge", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_ROOM_INVISIBLE_CHEST, ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)), + }, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_EAST_HAND, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F] = Region("Spirit Temple MQ Three Suns Room 2F", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events //implies logic->CanKillEnemy(RE_WALLMASTER). If we have lights, we can kill stalfos and wallmasters with bow - EventAccess(&logic->MQSpirit3SunsEnemies, []{return (logic->CanUse(RG_MIRROR_SHIELD) && logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2)) || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && logic->CanUse(RG_LIGHT_ARROWS));}), + EventAccess(&logic->MQSpirit3SunsEnemies, []{return (logic->CanUse(RG_MIRROR_SHIELD) && logic->CanKillEnemy(RE_STALFOS, ED_CLOSE, true, 2)) || logic->SunlightArrows();}), }, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return true;}), @@ -762,7 +896,8 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_1F] = Region("Spirit Temple MQ Three Suns Room 1F", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F, []{return logic->MQSpirit3SunsEnemies;}), + //It is possible to clip through here with longshot by aiming high enough + Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F, []{return logic->MQSpirit3SunsEnemies/* && CanClimbHigh()*/;}), Entrance(RR_SPIRIT_TEMPLE_MQ_1F_EAST, []{return true;}), }); @@ -776,10 +911,10 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_EARLY_ADULT_POT_2, logic->CanBreakPots()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return logic->CanUse(RG_MEGATON_HAMMER);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_1F, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_SAND_PIT, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return logic->CanUse(RG_MEGATON_HAMMER);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_1F, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SAND_PIT, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_UPPER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_SAND_PIT] = Region("Spirit Temple MQ Sand Pit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -791,11 +926,22 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_1F_EAST, []{return logic->CanUse(RG_ZELDAS_LULLABY);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM] = Region("Spirit Temple MQ Symphony Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_UPPER] = Region("Spirit Temple MQ Symphony Room Upper", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->MQSpiritSymphonyRoomDoor, []{return logic->CanUse(RG_MEGATON_HAMMER) && logic->CanUse(RG_SONG_OF_TIME) && logic->CanUse(RG_EPONAS_SONG) && + logic->CanUse(RG_SUNS_SONG) && logic->CanUse(RG_SONG_OF_STORMS) && logic->CanUse(RG_ZELDAS_LULLABY);}), + }, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_1F_EAST, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), //Implies CanPassEnemy(RE_MOBLIN_CHIEF) - Entrance(RR_SPIRIT_TEMPLE_MQ_AFTER_SYMPHONY_ROOM, []{return logic->CanUse(RG_MEGATON_HAMMER) && logic->CanUse(RG_SONG_OF_TIME) && logic->CanUse(RG_EPONAS_SONG) && logic->CanUse(RG_SUNS_SONG) && logic->CanUse(RG_SONG_OF_STORMS) && logic->CanUse(RG_ZELDAS_LULLABY);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_LOWER, []{return true;}), + }); + + areaTable[RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_LOWER] = Region("Spirit Temple MQ Symphony Room Lower", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_UPPER, []{return true/*CanClimb()*/;}), + //Implies CanPassEnemy(RE_MOBLIN_CHIEF) + Entrance(RR_SPIRIT_TEMPLE_MQ_AFTER_SYMPHONY_ROOM, []{return logic->MQSpiritSymphonyRoomDoor;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_AFTER_SYMPHONY_ROOM] = Region("Spirit Temple MQ After Symphony Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -804,7 +950,7 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_SYMPHONY_ROOM, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_UPPER, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER] = Region("Spirit Temple MQ Fire Wall Stairs Lower", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -816,17 +962,17 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_UPPER] = Region("Spirit Temple MQ Fire Wall Stairs Upper", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER, []{return logic->CanUse(RG_HOOKSHOT);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BEAMOS_PITS, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM] = Region("Spirit Temple MQ Four Beamos Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_BEAMOS_PITS] = Region("Spirit Temple MQ Beamos Pits", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_BEAMOS_ROOM_CHEST, logic->CanKillEnemy(RE_BEAMOS)), - LOCATION(RC_SPIRIT_TEMPLE_MQ_BEAMOS_SMALL_CRATE, logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_SONG_OF_TIME) && logic->CanBreakSmallCrates()), + LOCATION(RC_SPIRIT_TEMPLE_MQ_BEAMOS_SMALL_CRATE, logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && logic->CanBreakSmallCrates()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_UPPER, []{return logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_UPPER, []{return logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME)/* && str0*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME)/* && str0*/;}), Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_LOWER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6);}), }); @@ -836,36 +982,36 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_DINALFOS_ROOM_SUN_FAIRY, logic->CanUse(RG_SUNS_SONG)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND, []{return logic->CanJumpslash();}), - Entrance(RR_SPIRIT_TEMPLE_MQ_3F_GIBDO_ROOM, []{return Here(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return (logic->IsAdult || logic->CanUse(RG_SONG_OF_TIME)) && logic->CanUse(RG_MIRROR_SHIELD);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BEAMOS_PITS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FLOORMASTER_STAIRS, []{return logic->CanJumpslash();}), + Entrance(RR_SPIRIT_TEMPLE_MQ_3F_GIBDO_ROOM, []{return Here(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return (logic->IsAdult || logic->CanUse(RG_SONG_OF_TIME)) && logic->CanUse(RG_MIRROR_SHIELD);});}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND] = Region("Spirit Temple MQ East Stairs to Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_FLOORMASTER_STAIRS] = Region("Spirit Temple MQ Floormaster Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE, []{return (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && Here(RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND, []{return logic->CanKillEnemy(RE_FLOORMASTER);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_THRONE, []{return (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && Here(RR_SPIRIT_TEMPLE_MQ_FLOORMASTER_STAIRS, []{return logic->CanKillEnemy(RE_FLOORMASTER);});}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE] = Region("Spirit Temple MQ East Iron Knuckle", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_EAST_THRONE] = Region("Spirit Temple MQ East Throne", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_EXIT_TO_MIRROR_SHIELD_HAND, []{return Here(RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_FLOORMASTER_STAIRS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_HAND_EXIT, []{return Here(RR_SPIRIT_TEMPLE_MQ_EAST_THRONE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_EXIT_TO_MIRROR_SHIELD_HAND] = Region("Spirit Temple MQ Exit To Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { - Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_MIRROR_SHIELD_HAND, []{return true;}), + areaTable[RR_SPIRIT_TEMPLE_MQ_EAST_HAND_EXIT] = Region("Spirit Temple MQ East Hand Exit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_THRONE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_OUTER_EAST_HAND, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_MIRROR_SHIELD_HAND] = Region("Spirit Temple MQ Mirror Shield Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_OUTER_EAST_HAND] = Region("Spirit Temple MQ Outer East Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MIRROR_SHIELD_CHEST, true), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND, []{return logic->CanUse(RG_LONGSHOT);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE, []{return true;}), - Entrance(RR_DESERT_COLOSSUS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND, []{return logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_EAST_THRONE, []{return true;}), + Entrance(RR_DESERT_COLOSSUS, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_3F_GIBDO_ROOM] = Region("Spirit Temple MQ 3F Gibdo Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -882,16 +1028,19 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_LONG_CLIMB_POT_2, logic->CanBreakPots()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BEAMOS_PITS, []{return true;}), //technically we only need to avoid them, but the sheer height and the moving walls makes getting to the top after only stunning them very difficult/impossible - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER, []{return logic->CanKillEnemy(RE_KEESE);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER, []{return /*(*/logic->CanKillEnemy(RE_KEESE)/*|| CanUse(RG_SKULL_MASK)) && CanClimbHigh()*/;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER] = Region("Spirit Temple MQ Big Wall Upper", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER] = Region("Spirit Temple MQ Big Wall Upper", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + //Getting some of these with just climbing downwards is theoretically possible but definitly a trick + EventAccess(&logic->MQSpiritBigWallSilvers, []{return /*(*/logic->CanKillEnemy(RE_KEESE)/*|| CanUse(RG_SKULL_MASK)) && CanClimbHigh()*/;}), + }, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_LOWER, []{return true;}), - //The silver rupees are irrelevant without silver shuffle - Entrance(RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL, []{return logic->MQSpiritBigWallSilvers;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL] = Region("Spirit Temple MQ 4F Central", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -900,12 +1049,12 @@ void RegionTable_Init_SpiritTemple() { LOCATION(RC_SPIRIT_TEMPLE_MQ_BEFORE_MIRROR_POT_2, logic->CanBreakPots()), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_NINE_CHAIRS_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return logic->CanUse(RG_ZELDAS_LULLABY);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_NINE_THRONES_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return logic->CanUse(RG_ZELDAS_LULLABY);}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_NINE_CHAIRS_ROOM] = Region("Spirit Temple MQ Nine Chairs Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_NINE_THRONES_ROOM] = Region("Spirit Temple MQ Nine Thrones Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations //These skulls rely on the iron knuckle existing without a trick to shoot through the chairs LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_NINE_THRONES_ROOM_WEST, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_BOOMERANG)), @@ -917,7 +1066,8 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM] = Region("Spirit Temple MQ Big Mirror Room", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->MQSpiritOpenedBigMirrorCave, []{return logic->CanUse(RG_MEGATON_HAMMER);}), + //Needs the mirror in the cave to be a perm flag and event for doorsanity + EventAccess(&logic->SpiritPlatformLowered, []{return (logic->SpiritPushed4FMirrors && logic->CanUse(RG_MIRROR_SHIELD)) || logic->SunlightArrows();}), }, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_BIG_MIRROR_POT_1, logic->CanBreakPots()), @@ -931,18 +1081,31 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->MQSpiritOpenedBigMirrorCave;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return logic->CanUse(RG_MEGATON_HAMMER);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_PLATFORM, []{return logic->SpiritPlatformLowered;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE] = Region("Spirit Temple MQ Big Mirror Cave", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE] = Region("Spirit Temple MQ Big Mirror Cave", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { + //Events + //WARNING: Not currently a permanent flag, needs to be one for doorsanity + EventAccess(&logic->SpiritPushed4FMirrors, []{return ((logic->IsAdult && logic->CanUse(RG_MIRROR_SHIELD)) || logic->SunlightArrows())/* && str0*/;}), + }, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_MIRROR_PUZZLE_INVISIBLE_CHEST, ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return logic->MQSpiritOpenedBigMirrorCave;}), - //If it's ever relevant to longshot into head from lobby, this needs to be an event access - Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->CanUse(RG_MIRROR_SHIELD);}) && logic->CanUse(RG_HOOKSHOT);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return Here(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, []{return logic->CanUse(RG_MIRROR_SHIELD);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, []{return true;}), + }); + + //Assumes SpiritPlatformLowered is checked on entry + areaTable[RR_SPIRIT_TEMPLE_MQ_PLATFORM] = Region("Spirit Temple MQ Platform", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD, []{return logic->SpiritPushed4FMirrors && logic->CanUse(RG_MIRROR_SHIELD) && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_EAST_HAND, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD] = Region("Spirit Temple MQ Statue Head", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { @@ -952,8 +1115,11 @@ void RegionTable_Init_SpiritTemple() { EventAccess(&logic->ReverseSpiritAdult, []{return logic->IsAdult;}), }, {}, { // Exits - Entrance(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, []{return logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_EAST_HAND, []{return logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_PLATFORM, []{return logic->SpiritPlatformLowered && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT));}), + Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), }); #pragma endregion diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index d6e06bf79..ba94392cf 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -1286,6 +1286,10 @@ bool Logic::HasFireSourceWithTorch() { return HasFireSource() || CanUse(RG_STICKS); } +bool Logic::SunlightArrows() { + return ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS); +} + // Is this best off signaling what you have already traded, or what step you are currently on? bool Logic::TradeQuestStep(RandomizerGet rg) { if (ctx->GetOption(RSK_SHUFFLE_ADULT_TRADE)) { @@ -2376,8 +2380,19 @@ bool Logic::SpiritWestToSkull() { return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SCARECROW); } +bool Logic::SpiritSunBlockSouthLedge() { + return true/*str0 || IsAdult || CanKillEnemy(RE_BEAMOS) || BunnyHovers() || + (CanUse(RG_HOOKSHOT) && (HasFireSource() || + (SpiritSunBlockTorch && (logic->CanUse(STICKS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))))))*/; +} + +//Combines crossing the ledge directly and the jump from the hand +bool Logic::MQSpiritWestToPots() { + return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SONG_OF_TIME); +} + bool Logic::MQSpiritStatueToSunBlock() { - return IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME); + return (IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME))/* && str0*/; } bool Logic::MQSpiritStatueSouthDoor() { @@ -2570,18 +2585,20 @@ void Logic::Reset() { MQSpiritCrawlBoulder = false; MQSpiritMapRoomEnemies = false; MQSpiritTimeTravelChest = false; + MQSpiritStatueRoomTorches = false; MQSpirit3SunsEnemies = false; - MQSpiritOpenedBigMirrorCave = false; + MQSpiritSymphonyRoomDoor = false; + MQSpiritBigWallSilvers = false; Spirit1FSilverRupees = false; SpiritChildSwitchBridge = false; SpiritRupeeBridge = false; SpiritBouldersSilvers = false; SpiritPlatformLowered = false; Spirit4FSwitch = false; + SpiritPushed4FMirrors = false; ReverseSpiritChild = false; ReverseSpiritAdult = false; StopPerformanceTimer(PT_LOGIC_RESET); } - } // namespace Rando diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 2a03262ee..54fbe2912 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -180,15 +180,19 @@ class Logic { bool MQSpiritCrawlBoulder = false; bool MQSpiritMapRoomEnemies = false; bool MQSpiritTimeTravelChest = false; + bool MQSpiritStatueRoomTorches = false; bool MQSpirit3SunsEnemies = false; - bool MQSpiritOpenedBigMirrorCave = false; + bool MQSpiritSymphonyRoomDoor = false; + bool MQSpiritBigWallSilvers = false; bool Spirit1FSilverRupees = false; bool SpiritChildSwitchBridge = false; bool SpiritRupeeBridge = false; + bool SpiritSunBlockTorch = false; bool SpiritBouldersSilvers = false; bool SpiritStatueRoomSouthDoor = false; bool SpiritPlatformLowered = false; bool Spirit4FSwitch = false; + bool SpiritPushed4FMirrors = false; bool ReverseSpiritChild = false; bool ReverseSpiritAdult = true; @@ -258,6 +262,7 @@ class Logic { bool CanBreakSmallCrates(); bool HasFireSource(); bool HasFireSourceWithTorch(); + bool SunlightArrows(); bool TradeQuestStep(RandomizerGet rg); bool CanFinishGerudoFortress(); bool CanStandingShield(); @@ -299,12 +304,14 @@ class Logic { static std::map RandoGetToDungeonScene; static std::map RandoGetToEquipFlag; static std::map RandoGetToRandInf; - bool Logic::IsReverseAccessPossible(); - bool Logic::SpiritBrokenWallToStatue(); - bool Logic::SpiritEastToSwitch(); - bool Logic::SpiritWestToSkull(); - bool Logic::MQSpiritStatueToSunBlock(); - bool Logic::MQSpiritStatueSouthDoor(); + bool IsReverseAccessPossible(); + bool SpiritBrokenWallToStatue(); + bool SpiritEastToSwitch(); + bool SpiritWestToSkull(); + bool SpiritSunBlockSouthLedge(); + bool MQSpiritWestToPots(); + bool MQSpiritStatueToSunBlock(); + bool MQSpiritStatueSouthDoor(); private: std::shared_ptr ctx; diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 7d669730d..0c0dad508 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -864,16 +864,19 @@ typedef enum { RR_SPIRIT_TEMPLE_2F_MIRROR, RR_SPIRIT_TEMPLE_STATUE_ROOM, RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, + RR_SPIRIT_TEMPLE_INNER_WEST_HAND, RR_SPIRIT_TEMPLE_GS_LEDGE, RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, + RR_SPIRIT_TEMPLE_INNER_EAST_HAND, RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, RR_SPIRIT_TEMPLE_SHORTCUT, RR_SPIRIT_TEMPLE_EMPTY_STAIRS, RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, + RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, RR_SPIRIT_TEMPLE_WEST_THRONE, RR_SPIRIT_TEMPLE_WEST_HAND_EXIT, - RR_SPIRIT_TEMPLE_WEST_HAND, + RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, RR_SPIRIT_TEMPLE_POT_STAIRS, RR_SPIRIT_TEMPLE_BEAMOS_PITS, RR_SPIRIT_TEMPLE_FOUR_ARMOS, @@ -881,8 +884,8 @@ typedef enum { RR_SPIRIT_TEMPLE_CHEST_STAIRS, RR_SPIRIT_TEMPLE_EAST_THRONE, RR_SPIRIT_TEMPLE_EAST_HAND_EXIT, - RR_SPIRIT_TEMPLE_EAST_HAND, - RR_SPIRIT_TEMPLE_BIG_WALL_LOWER, + RR_SPIRIT_TEMPLE_OUTER_EAST_HAND, + RR_SPIRIT_TEMPLE_BIG_WALL_BASE, RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, RR_SPIRIT_TEMPLE_4F_CENTRAL, RR_SPIRIT_TEMPLE_FAKE_DOORS_ROOM, @@ -901,38 +904,44 @@ typedef enum { RR_SPIRIT_TEMPLE_MQ_WEST_1F_RUSTED_SWITCH, RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, - RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, + RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, + RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, + RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS, RR_SPIRIT_TEMPLE_MQ_WEST_THRONE, RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT, - RR_SPIRIT_TEMPLE_MQ_WEST_HAND, + RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND, RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH, RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, + RR_SPIRIT_TEMPLE_MQ_INNER_EAST_HAND, + RR_SPIRIT_TEMPLE_MQ_CHEST_LEDGE, RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F, RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_1F, RR_SPIRIT_TEMPLE_MQ_1F_EAST, RR_SPIRIT_TEMPLE_MQ_SAND_PIT, - RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM, + RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_UPPER, + RR_SPIRIT_TEMPLE_MQ_SYMPHONY_ROOM_LOWER, RR_SPIRIT_TEMPLE_MQ_AFTER_SYMPHONY_ROOM, RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER, RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_UPPER, - RR_SPIRIT_TEMPLE_MQ_FOUR_BEAMOS_ROOM, + RR_SPIRIT_TEMPLE_MQ_BEAMOS_PITS, RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, - RR_SPIRIT_TEMPLE_MQ_EAST_STAIRS_TO_HAND, - RR_SPIRIT_TEMPLE_MQ_EAST_IRON_KNUCKLE, - RR_SPIRIT_TEMPLE_MQ_EXIT_TO_MIRROR_SHIELD_HAND, - RR_SPIRIT_TEMPLE_MQ_MIRROR_SHIELD_HAND, + RR_SPIRIT_TEMPLE_MQ_FLOORMASTER_STAIRS, + RR_SPIRIT_TEMPLE_MQ_EAST_THRONE, + RR_SPIRIT_TEMPLE_MQ_EAST_HAND_EXIT, + RR_SPIRIT_TEMPLE_MQ_OUTER_EAST_HAND, RR_SPIRIT_TEMPLE_MQ_3F_GIBDO_ROOM, RR_SPIRIT_TEMPLE_MQ_BIG_WALL_LOWER, RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER, RR_SPIRIT_TEMPLE_MQ_4F_CENTRAL, - RR_SPIRIT_TEMPLE_MQ_NINE_CHAIRS_ROOM, + RR_SPIRIT_TEMPLE_MQ_NINE_THRONES_ROOM, RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_ROOM, RR_SPIRIT_TEMPLE_MQ_BIG_MIRROR_CAVE, + RR_SPIRIT_TEMPLE_MQ_PLATFORM, RR_SPIRIT_TEMPLE_MQ_STATUE_HEAD, RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, From 9b2d092a5b00036f355c44a6d8b18d85073d7951 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Thu, 24 Apr 2025 21:50:53 +0100 Subject: [PATCH 12/29] format --- .../randomizer/location_access.cpp | 74 ++++++++++--------- .../Enhancements/randomizer/location_access.h | 35 ++++----- soh/soh/Enhancements/randomizer/logic.cpp | 25 ++++--- soh/soh/Enhancements/randomizer/logic.h | 1 - 4 files changed, 73 insertions(+), 62 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index b2493bf8c..d9f1d0947 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -219,7 +219,7 @@ uint8_t SpiritExplosiveLogic() { * This code will fail if any glitch allows Adult to go in the Child spirit door first or vice versa as it relies on specific ages - * In order to pass a check, we must either determine that Access is certain, + * In order to pass a check, we must either determine that Access is certain, or that it is always possible to get a check somehow. * But first I have to talk about parallel universes. @@ -231,15 +231,16 @@ uint8_t SpiritExplosiveLogic() { * In the third universe, adult enters in reverse, and wastes all the keys so noone can enter through the front * In the forth, child manages to do the same, and lock people out of the front * All access from the boss door in shared areas is Certain - - * While other universes exist, such as both ages entering in reverse or - child using their key, getting stuck, then coming back to do the dungeon as adult, these + + * While other universes exist, such as both ages entering in reverse or + child using their key, getting stuck, then coming back to do the dungeon as adult, these are all sub-possibilities of these 4 universes * As we do not know which universe we are in until the player chooses one in-game, we must be able to collect the check in both universes - * When an Age can no longer be kept out by conflicting universes, that age is said to have Certain Access to a region + * When an Age can no longer be kept out by conflicting universes, that age is said to have Certain Access to a + region * If both ages have access to a region with a certain number of keys, but there is no Certain Access, * then a check is only in logic if all possible universes can collect the check independently @@ -247,11 +248,11 @@ uint8_t SpiritExplosiveLogic() { * We must check for these universes manually as we allow technical access with minimum keys for * technical reasons as otherwise the logic code will never run - + * The first and 3rd column list how many keys are needed for each age to have Certain Access * the second column is child keys in case there's Child reverse access, due to an edge case in MQ spirit logic * where the broken wall room can be reached with 6 keys if you can hit switches and have reverse Child access - + * The first condition is the combined conditions needed to move from the 1F child lock to the area being checks * the second condition is the same for adult 1F lock, and the third is the access from the boss door. */ @@ -294,9 +295,8 @@ std::map Region::spiritLogicData = { *anyAge is equivalent to a self referencing Here, used for events and any check where that is relevent. */ -bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, - RandomizerRegion otherRegion, ConditionFn otherCondition, - RandomizerRegion thirdRegion, ConditionFn thirdCondition){ +bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, RandomizerRegion otherRegion, + ConditionFn otherCondition, RandomizerRegion thirdRegion, ConditionFn thirdCondition) { SpiritLogicData curRegionData = Region::spiritLogicData[region]; bool result = false; @@ -308,20 +308,24 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, // without opening the Statue room to Broken Wall Room lock first logic->IsChild = true; logic->IsAdult = false; - uint8_t childKeys = (logic->ReverseSpiritChild && logic->CanHitSwitch()/* && CanClimbHigh()*/) ? curRegionData.childReverseKeys : curRegionData.childKeys; + uint8_t childKeys = (logic->ReverseSpiritChild && logic->CanHitSwitch() /* && CanClimbHigh()*/) + ? curRegionData.childReverseKeys + : curRegionData.childKeys; // If we have enough keys that an age cannot be kept out, we have Certain Access // otherwise if we have entered in reverse and can reach from the face, we have Certain Access - bool ChildCertainAccess = (logic->ReverseSpiritChild && curRegionData.reverseAccess()) || logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys); + bool ChildCertainAccess = + (logic->ReverseSpiritChild && curRegionData.reverseAccess()) || logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys); - //Switch back to adult to check adult access + // Switch back to adult to check adult access logic->IsChild = false; logic->IsAdult = true; - bool AdultCertainAccess = (logic->ReverseSpiritAdult && curRegionData.reverseAccess()) || logic->SmallKeys(RR_SPIRIT_TEMPLE, curRegionData.adultKeys); + bool AdultCertainAccess = (logic->ReverseSpiritAdult && curRegionData.reverseAccess()) || + logic->SmallKeys(RR_SPIRIT_TEMPLE, curRegionData.adultKeys); // If we are AnyAge and have any CeratinAccess, then we can check those ages - //we don't need to check ambiguity here as if this fails, then 1 of the ages has failed - if (anyAge && (ChildCertainAccess || AdultCertainAccess)){ + // we don't need to check ambiguity here as if this fails, then 1 of the ages has failed + if (anyAge && (ChildCertainAccess || AdultCertainAccess)) { // set age access to the Certain Access logic->IsChild = ChildCertainAccess; logic->IsAdult = AdultCertainAccess; @@ -331,7 +335,7 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, // otherwise, we have to check the current age and... } else if (areaTable[region].Child() && pastChild) { - //Switch to Child + // Switch to Child logic->IsChild = true; logic->IsAdult = false; @@ -339,19 +343,21 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, // If we have Certain Access, we just run the condition. // Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Adult // and if needed, in reverse - if (!ChildCertainAccess && result && (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess())) { - //Switch to Adult + if (!ChildCertainAccess && result && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess())) { + // Switch to Adult logic->IsChild = false; logic->IsAdult = true; // If Adult can get there and get the check, we can get the check in logic // If reverse spirit is also possible, we need to make sure Adult can get it via reverse entry too - result = (curRegionData.adultAccess() && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) || + result = (curRegionData.adultAccess() && + (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) || (otherRegion != RR_NONE && - (Region::spiritLogicData[otherRegion].adultAccess() && + (Region::spiritLogicData[otherRegion].adultAccess() && (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && otherCondition())) || (thirdRegion != RR_NONE && - (Region::spiritLogicData[thirdRegion].adultAccess() && + (Region::spiritLogicData[thirdRegion].adultAccess() && (!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && thirdCondition())); } @@ -361,22 +367,24 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, // Alternatively, if we have entered in reverse and can reach from the face, we have Certain Access // Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Child // and if needed, in reverse - if (!AdultCertainAccess && result && (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess)){ - //Switch to Child + if (!AdultCertainAccess && result && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess)) { + // Switch to Child logic->IsChild = true; logic->IsAdult = false; // If Child can get there and get the check, we can get the check in logic // If reverse spirit is also possible, we need to make sure Child can get it via reverse entry too - result = (curRegionData.childAccess() && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess()) && condition()) || - (otherRegion != RR_NONE && - (Region::spiritLogicData[otherRegion].childAccess() && - (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && - otherCondition())) || - (thirdRegion != RR_NONE && - (Region::spiritLogicData[thirdRegion].childAccess() && - (!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && - thirdCondition())); + result = (curRegionData.childAccess() && + (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess()) && condition()) || + (otherRegion != RR_NONE && + (Region::spiritLogicData[otherRegion].childAccess() && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && + otherCondition())) || + (thirdRegion != RR_NONE && + (Region::spiritLogicData[thirdRegion].childAccess() && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && + thirdCondition())); } } // set back age variables diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 4cc3e02ef..07554edee 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -112,21 +112,21 @@ enum class EntranceType; } // namespace Rando struct SpiritLogicData { - uint8_t childKeys; //the number of keys that guarantees Child can reach this region - //The number of keys that guarantees Child can reach this region if they have reverse access - //This changes for MQ broken wall room as the first child lock can only be opened by Child - //guaranteeing access with 6 keys - uint8_t childReverseKeys; - uint8_t adultKeys; //the number of keys that guarantees Adult can reach this region - //The area access condition to reach this region as Child, from the first lock, - //including the minimum number of keys for ambiguous access - // 1 key is always assumed to be required + uint8_t childKeys; // the number of keys that guarantees Child can reach this region + // The number of keys that guarantees Child can reach this region if they have reverse access + // This changes for MQ broken wall room as the first child lock can only be opened by Child + // guaranteeing access with 6 keys + uint8_t childReverseKeys; + uint8_t adultKeys; // the number of keys that guarantees Adult can reach this region + // The area access condition to reach this region as Child, from the first lock, + // including the minimum number of keys for ambiguous access + // 1 key is always assumed to be required ConditionFn childAccess; - //The area access condition to reach this region as Adult, from the first lock - //including the minimum number of keys for ambiguous access - //1 key is always assumed to be required on vanilla + // The area access condition to reach this region as Adult, from the first lock + // including the minimum number of keys for ambiguous access + // 1 key is always assumed to be required on vanilla ConditionFn adultAccess; - //The area access condition to reach this region from the boss door, + // The area access condition to reach this region from the boss door, ConditionFn reverseAccess; }; @@ -250,7 +250,7 @@ class Region { "Adult Night: " + std::to_string(adultNight); } - + static std::map spiritLogicData; }; @@ -260,9 +260,10 @@ extern std::vector grottoEvents; bool Here(const RandomizerRegion region, ConditionFn condition); // RANDOTODO make a less stupid way to check own at either age than self referencing with this -bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge = false, - RandomizerRegion otherRegion = RR_NONE, ConditionFn otherCondition = []{return false;}, - RandomizerRegion thirdRegion = RR_NONE, ConditionFn thirdCondition = []{return false;}); +bool SpiritShared( + RandomizerRegion region, ConditionFn condition, bool anyAge = false, RandomizerRegion otherRegion = RR_NONE, + ConditionFn otherCondition = [] { return false; }, RandomizerRegion thirdRegion = RR_NONE, + ConditionFn thirdCondition = [] { return false; }); bool CanPlantBean(const RandomizerRegion region); bool BothAges(const RandomizerRegion region); bool ChildCanAccess(const RandomizerRegion region); diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index ba94392cf..f5ab2a62f 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -864,7 +864,7 @@ bool Logic::CanPassEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal return CanUse(RG_HOOKSHOT) || CanUse(RG_SUNS_SONG); case RE_IRON_KNUCKLE: case RE_BIG_OCTO: - case RE_WALLTULA: //consistent with RT_SPIRIT_WALL + case RE_WALLTULA: // consistent with RT_SPIRIT_WALL return false; case RE_GREEN_BUBBLE: return TakeDamage() || CanUse(RG_NUTS) || CanUse(RG_BOOMERANG) || CanUse(RG_HOOKSHOT); @@ -2362,10 +2362,9 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) { inLogic[logicVal] = value; } -bool Logic::IsReverseAccessPossible(){ +bool Logic::IsReverseAccessPossible() { return ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES) && - (ctx->GetOption(RSK_DECOUPLED_ENTRANCES) || - ctx->GetOption(RSK_MIX_BOSS_ENTRANCES)); + (ctx->GetOption(RSK_DECOUPLED_ENTRANCES) || ctx->GetOption(RSK_MIX_BOSS_ENTRANCES)); } bool Logic::SpiritBrokenWallToStatue() { @@ -2373,7 +2372,8 @@ bool Logic::SpiritBrokenWallToStatue() { } bool Logic::SpiritEastToSwitch() { - return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); + return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || + (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); } bool Logic::SpiritWestToSkull() { @@ -2381,22 +2381,25 @@ bool Logic::SpiritWestToSkull() { } bool Logic::SpiritSunBlockSouthLedge() { - return true/*str0 || IsAdult || CanKillEnemy(RE_BEAMOS) || BunnyHovers() || - (CanUse(RG_HOOKSHOT) && (HasFireSource() || - (SpiritSunBlockTorch && (logic->CanUse(STICKS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))))))*/; + return true /*str0 || IsAdult || CanKillEnemy(RE_BEAMOS) || BunnyHovers() || + (CanUse(RG_HOOKSHOT) && (HasFireSource() || + (SpiritSunBlockTorch && (logic->CanUse(STICKS) || + (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))))))*/ + ; } -//Combines crossing the ledge directly and the jump from the hand +// Combines crossing the ledge directly and the jump from the hand bool Logic::MQSpiritWestToPots() { return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SONG_OF_TIME); } bool Logic::MQSpiritStatueToSunBlock() { - return (IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME))/* && str0*/; + return (IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME)) /* && str0*/; } bool Logic::MQSpiritStatueSouthDoor() { - return HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && CanUse(RG_FAIRY_BOW) && CanUse(RG_SONG_OF_TIME)/* && CanClimb()*/); + return HasFireSource() || (ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && CanUse(RG_FAIRY_BOW) && + CanUse(RG_SONG_OF_TIME) /* && CanClimb()*/); } void Logic::Reset() { diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 54fbe2912..eb019abf2 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -26,7 +26,6 @@ enum class GlitchDifficulty { HERO, }; - class Logic { public: bool noVariable = false; From 6c1a82a8d56e01f185bfe7710a15fa423b5bf02b Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Thu, 24 Apr 2025 22:31:20 +0100 Subject: [PATCH 13/29] fix ReverseSpiritAdult initialising as true --- soh/soh/Enhancements/randomizer/logic.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index eb019abf2..5f09cd97c 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -193,7 +193,7 @@ class Logic { bool Spirit4FSwitch = false; bool SpiritPushed4FMirrors = false; bool ReverseSpiritChild = false; - bool ReverseSpiritAdult = true; + bool ReverseSpiritAdult = false; /* --- END OF HELPERS AND LOCATION ACCESS --- */ From 2b793ed2d893202eea88a10597149032081e13b9 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sat, 26 Apr 2025 12:24:30 +0100 Subject: [PATCH 14/29] fix logic issues --- .../randomizer/location_access.cpp | 88 +++++++++++-------- .../Enhancements/randomizer/location_access.h | 3 +- .../dungeons/spirit_temple.cpp | 18 ++-- 3 files changed, 61 insertions(+), 48 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index d9f1d0947..04eca70cd 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -210,10 +210,6 @@ bool Here(const RandomizerRegion region, ConditionFn condition) { return areaTable[region].Here(condition); } -uint8_t SpiritExplosiveLogic() { - return logic->SpiritBrokenWallToStatue() ? 1 : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 : 3; -} - /* * This logic covers checks that exist in the shared areas of Spirit * This code will fail if any glitch allows Adult to go in the Child spirit door first or vice versa as it relies on @@ -257,37 +253,62 @@ uint8_t SpiritExplosiveLogic() { * the second condition is the same for adult 1F lock, and the third is the access from the boss door. */ + +bool SpiritExplosiveKeyLogic() { + return logic->SmallKeys(RR_SPIRIT_TEMPLE, + logic->SpiritBrokenWallToStatue() ? 1 : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 : 3); +} + // clang-format off std::map Region::spiritLogicData = { //Vanilla - {RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, SpiritLogicData(5, 5, 3, []{return true;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, - {RR_SPIRIT_TEMPLE_BROKEN_WALL, SpiritLogicData(5, 5, 3, []{return true /*logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, - {RR_SPIRIT_TEMPLE_2F_MIRROR, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();}, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, - {RR_SPIRIT_TEMPLE_INNER_WEST_HAND, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, - {RR_SPIRIT_TEMPLE_GS_LEDGE, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic()) && logic->SpiritWestToSkull() - /* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true;})}, + {RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, SpiritLogicData(5, 5, 5, []{return true;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_BROKEN_WALL, SpiritLogicData(5, 5, 5, []{return true /*logic->CanClimbHigh()*/;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_2F_MIRROR, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();}, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, SpiritLogicData(5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_INNER_WEST_HAND, SpiritLogicData(5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_GS_LEDGE, SpiritLogicData(5, 5, 3, []{return SpiritExplosiveKeyLogic() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh()*/;}, []{return logic->SpiritWestToSkull()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM, SpiritLogicData(5, 5, 3, []{return SpiritExplosiveKeyLogic();}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true;})}, //Assumes SpiritSunBlockSouthLedge() for all access - {RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*(logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0*/;})}, - {RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, SpiritLogicData(5, 5, 3, []{return logic->SpiritBrokenWallToStatue()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, SpiritExplosiveLogic())/* && logic->CanClimbHigh() && str0*/;}, []{return true/*(logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0*/;})}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, - {RR_SPIRIT_TEMPLE_INNER_EAST_HAND, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, - {RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue() && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));})}, + {RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, SpiritLogicData(5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;})}, + {RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, SpiritLogicData(5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;})}, + {RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, SpiritLogicData(5, 5, 3, []{return SpiritExplosiveKeyLogic() && logic->CanKillEnemy(RE_IRON_KNUCKLE) //For the purpose of shared, adult needs to get to west side via BOTH possible routes for it to count //Only using HasItem here is intended so this check can pass as child if adult can do their part. This works because this edge case assumes that you can only waste keys on adult side with adult + /*&& logic->CanClimbHigh() && str0*/;}, []{return logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT)/* && logic->CanClimbHigh() && str0*/;}, []{return logic->CanKillEnemy(RE_BEAMOS) && logic->HasItem(RG_LONGSHOT)/* && logic->CanClimb() && str0*/;})}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, + {RR_SPIRIT_TEMPLE_INNER_EAST_HAND, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, + {RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && SpiritExplosiveKeyLogic() && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));})}, //MQ /*&& logic->CanClimbHigh()*/ - {RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, SpiritLogicData(7, 6, 7, []{return true;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, - {RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, SpiritLogicData(7, 6, 7, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, - {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->Climb*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, - {RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;})}, - {RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;})}, - {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true;}, []{return true;})}, - {RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock()/* && logic->CanClimbHigh()*/;} , []{return logic->MQSpiritStatueToSunBlock()/* && logic->Climb*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, SpiritLogicData(7, 6, 7, []{return true;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, SpiritLogicData(7, 6, 7, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->Climb*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, + {RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;})}, + {RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;})}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true;}, []{return true;})}, + {RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && logic->Climb*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, {RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();}) - /* && logic->CanClimbHigh()*/;}, []{return true;}, []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();});})}, + /* && logic->CanClimbHigh()*/;}, []{return true;}, []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();});})}, }; // clang-format on +bool SpiritCertainAccess(RandomizerRegion region){ + SpiritLogicData& curRegionData = Region::spiritLogicData[region]; + uint8_t keys = curRegionData.adultKeys; + bool reverseAccess = logic->ReverseSpiritAdult; + if (logic->IsChild){ + // If child enters in reverse, then they have access to Certain Access to Broken Wall room in 6 keys, + // the ability to hit switches and the ability to climb because only child can reach the initial child lock + // without opening the Statue room to Broken Wall Room lock first + keys = (logic->ReverseSpiritChild && logic->CanHitSwitch() /* && CanClimbHigh()*/) + ? curRegionData.childReverseKeys + : curRegionData.childKeys; + reverseAccess = logic->ReverseSpiritChild; + } + // If we have enough keys that an age cannot be kept out, we have Certain Access + // otherwise if we have entered in reverse and can reach from the face, we have Certain Access + return logic->SmallKeys(RR_SPIRIT_TEMPLE, keys) || (reverseAccess && curRegionData.reverseAccess()); +} + /* * Spirit Shared can take up to 3 regions, this is because checks can exist in many regions at the same time and the logic needs to be able to check the access logic from those regions to check the other universes properly. @@ -297,32 +318,23 @@ std::map Region::spiritLogicData = { bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, RandomizerRegion otherRegion, ConditionFn otherCondition, RandomizerRegion thirdRegion, ConditionFn thirdCondition) { - SpiritLogicData curRegionData = Region::spiritLogicData[region]; + SpiritLogicData& curRegionData = Region::spiritLogicData[region]; bool result = false; // store current age variables bool pastAdult = logic->IsAdult; bool pastChild = logic->IsChild; - // If child enters in reverse, then they have access to Certain Access to Broken Wall room in 6 keys, - // the ability to hit switches and the ability to climb because only child can reach the initial child lock - // without opening the Statue room to Broken Wall Room lock first + logic->IsChild = true; logic->IsAdult = false; - uint8_t childKeys = (logic->ReverseSpiritChild && logic->CanHitSwitch() /* && CanClimbHigh()*/) - ? curRegionData.childReverseKeys - : curRegionData.childKeys; - // If we have enough keys that an age cannot be kept out, we have Certain Access - // otherwise if we have entered in reverse and can reach from the face, we have Certain Access - bool ChildCertainAccess = - (logic->ReverseSpiritChild && curRegionData.reverseAccess()) || logic->SmallKeys(RR_SPIRIT_TEMPLE, childKeys); + bool ChildCertainAccess = SpiritCertainAccess(region); // Switch back to adult to check adult access logic->IsChild = false; logic->IsAdult = true; - bool AdultCertainAccess = (logic->ReverseSpiritAdult && curRegionData.reverseAccess()) || - logic->SmallKeys(RR_SPIRIT_TEMPLE, curRegionData.adultKeys); + bool AdultCertainAccess = SpiritCertainAccess(region); // If we are AnyAge and have any CeratinAccess, then we can check those ages // we don't need to check ambiguity here as if this fails, then 1 of the ages has failed if (anyAge && (ChildCertainAccess || AdultCertainAccess)) { diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 07554edee..ffee32e2f 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -120,7 +120,7 @@ struct SpiritLogicData { uint8_t adultKeys; // the number of keys that guarantees Adult can reach this region // The area access condition to reach this region as Child, from the first lock, // including the minimum number of keys for ambiguous access - // 1 key is always assumed to be required + // 1 key is always assumed to be required ConditionFn childAccess; // The area access condition to reach this region as Adult, from the first lock // including the minimum number of keys for ambiguous access @@ -264,6 +264,7 @@ bool SpiritShared( RandomizerRegion region, ConditionFn condition, bool anyAge = false, RandomizerRegion otherRegion = RR_NONE, ConditionFn otherCondition = [] { return false; }, RandomizerRegion thirdRegion = RR_NONE, ConditionFn thirdCondition = [] { return false; }); +bool SpiritCertainAccess(RandomizerRegion region); bool CanPlantBean(const RandomizerRegion region); bool BothAges(const RandomizerRegion region); bool ChildCanAccess(const RandomizerRegion region); diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index 8d2250291..8eeefb041 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -300,24 +300,24 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_WEST_THRONE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_WEST_THRONE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3);}), }); areaTable[RR_SPIRIT_TEMPLE_WEST_THRONE] = Region("Spirit Temple West Throne", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), + Entrance(RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3);}), Entrance(RR_SPIRIT_TEMPLE_WEST_HAND_EXIT, []{return Here(RR_SPIRIT_TEMPLE_WEST_THRONE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), }); areaTable[RR_SPIRIT_TEMPLE_WEST_HAND_EXIT] = Region("Spirit Temple West Hand Exit", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_WEST_THRONE, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_WEST_THRONE, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_OUTER_WEST_HAND] = Region("Spirit Temple West Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, true), + LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, []{return true;})), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_WEST_HAND_EXIT, []{return true;}), @@ -328,6 +328,7 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_INNER_EAST_HAND, []{return true;}), //(IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); Entrance(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return logic->SpiritEastToSwitch();}), Entrance(RR_SPIRIT_TEMPLE_POT_STAIRS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}), @@ -338,9 +339,6 @@ void RegionTable_Init_SpiritTemple() { //Implies CanKillEnemy(RE_IRON_KNUCKLE) Entrance(RR_DESERT_COLOSSUS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanKillEnemy(RE_BEAMOS)/* && CanClimb() && str0*/;}), //!QUANTUM LOGIC! - //Continuing from above, if you also have the Longshot, then you can always reach the outer west hand, as you can longshot from east hand - Entrance(RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanUse(RG_LONGSHOT) && logic->CanKillEnemy(RE_BEAMOS)/* && CanClimb() && str0*/;}), - //!QUANTUM LOGIC! //A variant of the above, if dungeon entrance randomiser is off, Adult entered spirit in reverse, and they have str 2 to get there from the front, //it is always possible for them to reach 1F_EAST with only 2 keys. This is because you can only waste 1 key (on the first child side lock) //before you either allow you to climb down through 2F mirror room, or give yourself access to a hand to jump down from. @@ -378,6 +376,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_INNER_EAST_HAND, []{return true;}), }); areaTable[RR_SPIRIT_TEMPLE_SHORTCUT] = Region("Spirit Temple Shortcut", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -455,7 +454,8 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_BIG_WALL_BASE] = Region("Spirit Temple Big Wall Base", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_BEAMOS_PITS, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return (ctx->GetTrickOption(RT_SPIRIT_WALL) || (logic->CanAvoidEnemy(RE_BEAMOS, true, 2) && logic->CanPassEnemy(RE_WALLTULA))) + Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_UPPER, []{return (ctx->GetTrickOption(RT_SPIRIT_WALL) || + (logic->CanAvoidEnemy(RE_BEAMOS, true, 2) && logic->CanPassEnemy(RE_WALLTULA, ED_BOOMERANG))) /*&& CanClimbHigh()*/;}), }); From 0353c0ae0af5a5b62765089fa34be0a3a10a2ed2 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sat, 26 Apr 2025 12:26:46 +0100 Subject: [PATCH 15/29] clanged --- .../Enhancements/randomizer/location_access.cpp | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index 04eca70cd..d274e9766 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -253,10 +253,10 @@ bool Here(const RandomizerRegion region, ConditionFn condition) { * the second condition is the same for adult 1F lock, and the third is the access from the boss door. */ - bool SpiritExplosiveKeyLogic() { - return logic->SmallKeys(RR_SPIRIT_TEMPLE, - logic->SpiritBrokenWallToStatue() ? 1 : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 : 3); + return logic->SmallKeys(RR_SPIRIT_TEMPLE, logic->SpiritBrokenWallToStatue() ? 1 + : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 + : 3); } // clang-format off @@ -291,17 +291,17 @@ std::map Region::spiritLogicData = { }; // clang-format on -bool SpiritCertainAccess(RandomizerRegion region){ +bool SpiritCertainAccess(RandomizerRegion region) { SpiritLogicData& curRegionData = Region::spiritLogicData[region]; uint8_t keys = curRegionData.adultKeys; bool reverseAccess = logic->ReverseSpiritAdult; - if (logic->IsChild){ + if (logic->IsChild) { // If child enters in reverse, then they have access to Certain Access to Broken Wall room in 6 keys, // the ability to hit switches and the ability to climb because only child can reach the initial child lock // without opening the Statue room to Broken Wall Room lock first keys = (logic->ReverseSpiritChild && logic->CanHitSwitch() /* && CanClimbHigh()*/) - ? curRegionData.childReverseKeys - : curRegionData.childKeys; + ? curRegionData.childReverseKeys + : curRegionData.childKeys; reverseAccess = logic->ReverseSpiritChild; } // If we have enough keys that an age cannot be kept out, we have Certain Access From cad21e9547fea2a243523387f2fe5387eea3564a Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sat, 26 Apr 2025 12:30:40 +0100 Subject: [PATCH 16/29] forgot rang in walltula kill logic --- soh/soh/Enhancements/randomizer/logic.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index f5ab2a62f..e67b5dadf 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -801,6 +801,8 @@ bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance, bool wal killed = killed || (!inWater && CanUse(RG_BOMB_BAG)) || CanUse(RG_DINS_FIRE); [[fallthrough]]; case ED_BOOMERANG: + killed = killed || CanUse(RG_BOOMERANG); + [[fallthrough]]; case ED_HOOKSHOT: killed = killed || CanUse(RG_HOOKSHOT) || CanUse(RG_BOMBCHU_5) || CanUse(RG_MEGATON_HAMMER); [[fallthrough]]; From 264e4e3db3a0c1aa1ed5968f2bd3c76a537f6166 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sat, 26 Apr 2025 19:25:56 +0100 Subject: [PATCH 17/29] why constructor no work --- soh/soh/Enhancements/randomizer/location_access.h | 5 +++++ .../randomizer/location_access/dungeons/spirit_temple.cpp | 5 ++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index ffee32e2f..6afafb9c8 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -128,6 +128,11 @@ struct SpiritLogicData { ConditionFn adultAccess; // The area access condition to reach this region from the boss door, ConditionFn reverseAccess; + + SpiritLogicData(uint8_t childKeys_, uint8_t childReverseKeys_, uint8_t adultKeys_, + ConditionFn childAccess_, ConditionFn adultAccess_, ConditionFn reverseAccess_) + : childKeys(childKeys_), childReverseKeys(childReverseKeys_), adultKeys(adultKeys_), + childAccess(childAccess_), adultAccess(adultAccess_), reverseAccess(reverseAccess_) {} }; class Region { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index 8eeefb041..b32439c98 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -161,8 +161,6 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_ABOVE_BOULDERS, []{return true/*CanClimb()*/;}), - //Jump slash is possible as child, but pretty tight. Jumpslash as late as you can - //A damage boost off the boulder is also possible, but you need Entrance(RR_SPIRIT_TEMPLE_PAST_BOULDERS, []{return logic->SpiritBouldersSilvers;}), }); @@ -286,7 +284,8 @@ void RegionTable_Init_SpiritTemple() { //Spawning the chest to get here is accounted for in movement logic, so we only need to confirm it can be spawned here LOCATION(RC_SPIRIT_TEMPLE_SUN_BLOCK_ROOM_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, []{return logic->HasFireSource() || - (logic->SpiritSunBlockTorch && (logic->CanUse(RG_STICKS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW))));})), + (logic->SpiritSunBlockTorch && //Possible without str0 by timing a shot during a backflip from the ledge, but it's probably a separate trick + (logic->CanUse(RG_STICKS) || (ctx->GetTrickOption(RT_SPIRIT_SUN_CHEST) && logic->CanUse(RG_FAIRY_BOW)/* && str0*/)));})), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, []{return true;}), From 9cc6e063e752ecae571eed09e06177f5e0c565e3 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sat, 26 Apr 2025 19:37:25 +0100 Subject: [PATCH 18/29] try without the explicit call --- .../randomizer/location_access.cpp | 46 +++++++++---------- .../Enhancements/randomizer/location_access.h | 5 -- 2 files changed, 23 insertions(+), 28 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index d274e9766..a622b6058 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -262,32 +262,32 @@ bool SpiritExplosiveKeyLogic() { // clang-format off std::map Region::spiritLogicData = { //Vanilla - {RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, SpiritLogicData(5, 5, 5, []{return true;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, - {RR_SPIRIT_TEMPLE_BROKEN_WALL, SpiritLogicData(5, 5, 5, []{return true /*logic->CanClimbHigh()*/;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, - {RR_SPIRIT_TEMPLE_2F_MIRROR, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();}, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, SpiritLogicData(5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, - {RR_SPIRIT_TEMPLE_INNER_WEST_HAND, SpiritLogicData(5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, - {RR_SPIRIT_TEMPLE_GS_LEDGE, SpiritLogicData(5, 5, 3, []{return SpiritExplosiveKeyLogic() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh()*/;}, []{return logic->SpiritWestToSkull()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM, SpiritLogicData(5, 5, 3, []{return SpiritExplosiveKeyLogic();}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true;})}, + {RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, {5, 5, 5, []{return true;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_BROKEN_WALL, {5, 5, 5, []{return true /*logic->CanClimbHigh()*/;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_2F_MIRROR, {5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();}, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, {5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_INNER_WEST_HAND, {5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_GS_LEDGE, {5, 5, 3, []{return SpiritExplosiveKeyLogic() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh()*/;}, []{return logic->SpiritWestToSkull()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM, {5, 5, 3, []{return SpiritExplosiveKeyLogic();}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true;}}}, //Assumes SpiritSunBlockSouthLedge() for all access - {RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, SpiritLogicData(5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;})}, - {RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, SpiritLogicData(5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;})}, - {RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, SpiritLogicData(5, 5, 3, []{return SpiritExplosiveKeyLogic() && logic->CanKillEnemy(RE_IRON_KNUCKLE) //For the purpose of shared, adult needs to get to west side via BOTH possible routes for it to count //Only using HasItem here is intended so this check can pass as child if adult can do their part. This works because this edge case assumes that you can only waste keys on adult side with adult - /*&& logic->CanClimbHigh() && str0*/;}, []{return logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT)/* && logic->CanClimbHigh() && str0*/;}, []{return logic->CanKillEnemy(RE_BEAMOS) && logic->HasItem(RG_LONGSHOT)/* && logic->CanClimb() && str0*/;})}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, - {RR_SPIRIT_TEMPLE_INNER_EAST_HAND, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);})}, - {RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, SpiritLogicData(5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && SpiritExplosiveKeyLogic() && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));})}, + {RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, {5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, + {RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, {5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, + {RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, {5, 5, 3, []{return SpiritExplosiveKeyLogic() && logic->CanKillEnemy(RE_IRON_KNUCKLE) //For the purpose of shared, adult needs to get to west side via BOTH possible routes for it to count //Only using HasItem here is intended so this check can pass as child if adult can do their part. This works because this edge case assumes that you can only waste keys on adult side with adult + /*&& logic->CanClimbHigh() && str0*/;}, []{return logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT)/* && logic->CanClimbHigh() && str0*/;}, []{return logic->CanKillEnemy(RE_BEAMOS) && logic->HasItem(RG_LONGSHOT)/* && logic->CanClimb() && str0*/;}}}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, {5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, + {RR_SPIRIT_TEMPLE_INNER_EAST_HAND, {5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, + {RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, {5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && SpiritExplosiveKeyLogic() && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));}}}, //MQ /*&& logic->CanClimbHigh()*/ - {RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, SpiritLogicData(7, 6, 7, []{return true;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, - {RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, SpiritLogicData(7, 6, 7, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, - {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->Climb*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;})}, - {RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;})}, - {RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;})}, - {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true;}, []{return true;})}, - {RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && logic->Climb*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;})}, - {RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, SpiritLogicData(7, 7, 0, []{return logic->CanHitSwitch() && + {RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, {7, 6, 7, []{return true;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, {7, 6, 7, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, {7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->Climb*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, {7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, {7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, {7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true;}, []{return true;}}}, + {RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, {7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && logic->Climb*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, {7, 7, 0, []{return logic->CanHitSwitch() && areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();}) - /* && logic->CanClimbHigh()*/;}, []{return true;}, []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();});})}, + /* && logic->CanClimbHigh()*/;}, []{return true;}, []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();});}}}, }; // clang-format on diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 6afafb9c8..ffee32e2f 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -128,11 +128,6 @@ struct SpiritLogicData { ConditionFn adultAccess; // The area access condition to reach this region from the boss door, ConditionFn reverseAccess; - - SpiritLogicData(uint8_t childKeys_, uint8_t childReverseKeys_, uint8_t adultKeys_, - ConditionFn childAccess_, ConditionFn adultAccess_, ConditionFn reverseAccess_) - : childKeys(childKeys_), childReverseKeys(childReverseKeys_), adultKeys(adultKeys_), - childAccess(childAccess_), adultAccess(adultAccess_), reverseAccess(reverseAccess_) {} }; class Region { From 92b01444f0cae4f1fbf6926e34ba69cc4d314c3b Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 27 Apr 2025 00:03:33 +0100 Subject: [PATCH 19/29] MQ fixes --- .../randomizer/location_access.cpp | 2 ++ .../dungeons/spirit_temple.cpp | 20 ++++++++++--------- soh/soh/Enhancements/randomizer/logic.cpp | 16 +++++++++++++++ soh/soh/Enhancements/randomizer/logic.h | 2 ++ 4 files changed, 31 insertions(+), 9 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index a622b6058..bcc0bce00 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -285,6 +285,8 @@ std::map Region::spiritLogicData = { {RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, {7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;}}}, {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, {7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true;}, []{return true;}}}, {RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, {7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && logic->Climb*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND, {7, 7, 4, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock() //For the purpose of shared, adult needs to get to west side via BOTH possible routes for it to count //Only using HasItem here for adult items so child can pass this check + /* && logic->CanClimbHigh() && str0*/;}, []{return logic->MQSpirit4KeyWestHand();}, []{return logic->CouldMQSpirit4KeyWestHand();}}}, {RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, {7, 7, 0, []{return logic->CanHitSwitch() && areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();}) /* && logic->CanClimbHigh()*/;}, []{return true;}, []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();});}}}, diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index b32439c98..4b82d3251 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -320,7 +320,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_WEST_HAND_EXIT, []{return true;}), - Entrance(RR_DESERT_COLOSSUS, []{return true;}), + Entrance(RR_DESERT_COLOSSUS, []{return SpiritCertainAccess(RR_SPIRIT_TEMPLE_OUTER_WEST_HAND);}), }); areaTable[RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST] = Region("Spirit Temple Statue Room East", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -773,7 +773,9 @@ void RegionTable_Init_SpiritTemple() { //Locations //We don't need Shared here because If we are checking as child, universe 2 adult access needs nothing so it always passes, and if we are checking as adult, it is Certain Access LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true/*str0*/;})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_SUN_BLOCK_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanUse(RG_HOOKSHOT) || (ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_GS) && logic->CanUse(RG_BOOMERANG));})), + // RT_SPIRIT_MQ_SUN_BLOCK_GS should probably be expanded to cover all ground based methods when str0 is added, as it can be hit with longshot because the skull hitbox is larger than the model + LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_SUN_BLOCK_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return (logic->CanUse(RG_HOOKSHOT)/* && (str0 || SunlightArrows())*/) || + (ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_GS) && logic->CanUse(RG_BOOMERANG));})), LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_1, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanBreakPots();})), LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_2, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanBreakPots();})), }, { @@ -785,8 +787,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_SKULLTULA_STAIRS] = Region("Spirit Temple MQ Skulltula Stairs", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true;}), - //This door causes the Universes to merge as it requires 7 keys for both ages - Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_THRONE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_THRONE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_WEST_THRONE] = Region("Spirit Temple MQ West Throne", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -803,12 +804,12 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND] = Region("Spirit Temple MQ Outer West Hand", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, true), + LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND, []{return true;})), }, { //Exits //If it is ever relevent for 1 age to spawn the mirror shield chest for the other can longshot across, it needs an eventAccess Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_HAND_EXIT, []{return true;}), - Entrance(RR_DESERT_COLOSSUS, []{return true;}), + Entrance(RR_DESERT_COLOSSUS, []{return SpiritCertainAccess(RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND);}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_SOUTH] = Region("Spirit Temple MQ Block Room South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { @@ -850,12 +851,13 @@ void RegionTable_Init_SpiritTemple() { logic->CanUse(RG_HOOKSHOT);}), //!QUANTUM LOGIC! //Continuing from above, if we also have a longshot, we can go from the East hand to the West hand, meaning we always have access to East Hand - Entrance(RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && + /* logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && - logic->CanJumpslash() && /*(str0 || SunlightArrows) &&*/ + logic->CanJumpslash() && /*(str0 || SunlightArrows) && (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->CanKillEnemy(RE_IRON_KNUCKLE) && - logic->CanUse(RG_LONGSHOT);}), + logic->CanUse(RG_LONGSHOT) */ + Entrance(RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && logic->MQSpirit4KeyWestHand();}), Entrance(RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled Entrance(RR_SPIRIT_TEMPLE_MQ_PLATFORM, []{return logic->SpiritPlatformLowered && diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 4b4858244..ddc64f966 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2404,6 +2404,22 @@ bool Logic::MQSpiritStatueSouthDoor() { CanUse(RG_SONG_OF_TIME) /* && CanClimb()*/); } +bool Logic::MQSpirit4KeyWestHand() { + return CanAvoidEnemy(RE_BEAMOS, true, 4) && CanUse(RG_SONG_OF_TIME) && + CanJumpslash() && /*(str0 || SunlightArrows) &&*/ + (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || CanUse(RG_LENS_OF_TRUTH)) && CanKillEnemy(RE_IRON_KNUCKLE) && + CanUse(RG_LONGSHOT); +} +// This version of the function handles reaching there as child, based on what adult could do if they existed +bool Logic::CouldMQSpirit4KeyWestHand() { + return CanAvoidEnemy(RE_BEAMOS, true, 4) && CanUse(RG_SONG_OF_TIME) && HasItem(RG_MASTER_SWORD) || + HasItem(RG_BIGGORON_SWORD) || + HasItem(RG_MEGATON_HAMMER) && + /*(str0 || SunlightArrows) &&*/ + (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || CanUse(RG_LENS_OF_TRUTH)) && CanKillEnemy(RE_IRON_KNUCKLE) && + HasItem(RG_LONGSHOT); +} + void Logic::Reset() { NewSaveContext(); StartPerformanceTimer(PT_LOGIC_RESET); diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 5f09cd97c..09601da63 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -311,6 +311,8 @@ class Logic { bool MQSpiritWestToPots(); bool MQSpiritStatueToSunBlock(); bool MQSpiritStatueSouthDoor(); + bool MQSpirit4KeyWestHand(); + bool CouldMQSpirit4KeyWestHand(); private: std::shared_ptr ctx; From b18d774fcdc0e7f6d65d1465bac21303130f5c13 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 27 Apr 2025 11:00:04 +0100 Subject: [PATCH 20/29] fix warning --- .../randomizer/location_access/dungeons/spirit_temple.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index 4b82d3251..6016074a4 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -581,7 +581,8 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_SOUTH] = Region("Spirit Temple MQ 1F Gibdo Room South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->MQSpiritGibdosCleared, []{return logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets() && logic->CanKillEnemy(RE_GIBDO, ED_CLOSE, true, 3);}), + EventAccess(&logic->MQSpiritGibdosCleared, []{return logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets() + && logic->CanKillEnemy(RE_GIBDO, ED_CLOSE, true, 3) /* && str0*/;}), }, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return true;}), @@ -853,7 +854,7 @@ void RegionTable_Init_SpiritTemple() { //Continuing from above, if we also have a longshot, we can go from the East hand to the West hand, meaning we always have access to East Hand /* logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && - logic->CanJumpslash() && /*(str0 || SunlightArrows) && + logic->CanJumpslash() && (str0 || SunlightArrows) && (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->CanKillEnemy(RE_IRON_KNUCKLE) && logic->CanUse(RG_LONGSHOT) */ From 2635476919cd53052f09682b866fcc97b2f6169e Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 27 Apr 2025 20:15:30 +0100 Subject: [PATCH 21/29] fix more errors and add bunny hood logic to comments --- .../dungeons/spirit_temple.cpp | 54 +++++++++++-------- soh/soh/Enhancements/randomizer/logic.cpp | 7 ++- 2 files changed, 38 insertions(+), 23 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index 6016074a4..080c8550e 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -45,7 +45,8 @@ void RegionTable_Init_SpiritTemple() { }, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH, []{return (logic->SpiritChildSwitchBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false)) || logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH, []{return (logic->SpiritChildSwitchBridge && logic->CanPassEnemy(RE_GREEN_BUBBLE, ED_CLOSE, false)) || + logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT)/* || (logic->IsAdult && CanBunnyJump())*/;}), }); areaTable[RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_NORTH] = Region("Spirit Temple Switch Bridge North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { @@ -147,8 +148,9 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_ABOVE_BOULDERS] = Region("Spirit Temple Above Boulders", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events //Jump slash is possible as child, but pretty tight. Jumpslash as late as you can - //A damage boost off the boulder is also possible, but you need - EventAccess(&logic->SpiritBouldersSilvers, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslash() || logic->CanUse(RG_LONGSHOT);}), + //A damage boost off the boulder is also possible, but you need to land on the middle of the boulder + //to get enough distance to reach the rupee + EventAccess(&logic->SpiritBouldersSilvers, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslash() || logic->CanUse(RG_LONGSHOT)/* || CanBunnyHop()*/;}), }, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return true;}), @@ -223,7 +225,7 @@ void RegionTable_Init_SpiritTemple() { RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_LONGSHOT);})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT)/* || CanBunnyJump()*/;}), Entrance(RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP);}) }); @@ -234,7 +236,7 @@ void RegionTable_Init_SpiritTemple() { RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_LONGSHOT);})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT)/* || CanBunnyJump()*/;}), Entrance(RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return true;}) }); @@ -359,7 +361,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->CanUse(RG_HOVER_BOOTS)/* || CanBunnyJump()*/;}), //Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST access via RR_SPIRIT_TEMPLE_STATUE_ROOM Entrance(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || (logic->CanUse(RG_ZELDAS_LULLABY) && logic->CanUse(RG_HOOKSHOT));}), @@ -430,7 +432,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_EAST_THRONE] = Region("Spirit Temple East Throne", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_SPIRIT_TEMPLE_CHEST_STAIRS, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_CHEST_STAIRS, []{return Here(RR_SPIRIT_TEMPLE_EAST_THRONE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), Entrance(RR_SPIRIT_TEMPLE_EAST_HAND_EXIT, []{return Here(RR_SPIRIT_TEMPLE_EAST_THRONE, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE);});}), }); @@ -532,9 +534,11 @@ void RegionTable_Init_SpiritTemple() { }, {}, { // Exits Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), + //CanBunnyJump with a jumpslash can reach either hand and with good timing the platform as child. the latter is definitely a trick, the former may not be + //If this interaction with the hands is added, SpiritSharedLogic needs updating for it Entrance(RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return logic->CanUse(RG_HOVER_BOOTS);}), Entrance(RR_SPIRIT_TEMPLE_INNER_EAST_HAND, []{return logic->CanUse(RG_HOVER_BOOTS);}), - Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT));}), + Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT)/* || (IsAdult && CanBunnyJump())*/);}), Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), }); @@ -581,12 +585,16 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_SOUTH] = Region("Spirit Temple MQ 1F Gibdo Room South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->MQSpiritGibdosCleared, []{return logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets() + EventAccess(&logic->MQSpiritGibdosCleared, []{return /*str0 && */ + (logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets()) || + logic->CanUse(RG_HOVER_BOOTS)/* || (IsAdult && CanBunnyJump())*/ && logic->CanKillEnemy(RE_GIBDO, ED_CLOSE, true, 3) /* && str0*/;}), }, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH, []{return logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets()/* && str0*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH, []{return /*str0 && */ + (logic->CanUse(RG_BOMBCHU_5) && logic->CanHitEyeTargets()) || + logic->CanUse(RG_HOVER_BOOTS)/* || (IsAdult && CanBunnyJump())*/;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_1F_GIBDO_ROOM_NORTH] = Region("Spirit Temple MQ Gibdo Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, { @@ -616,7 +624,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_NORTH] = Region("Spirit Temple MQ Map Room North", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { //Events - EventAccess(&logic->MQSpiritMapRoomEnemies, []{return /*(*/logic->CanKillEnemy(RE_ANUBIS) && logic->CanKillEnemy(RE_KEESE)/*) && (str0 || CanUse(RG_HOVER_BOOTS) || CanUse(RG_HOOKSHOT)) || + EventAccess(&logic->MQSpiritMapRoomEnemies, []{return /*(*/logic->CanKillEnemy(RE_ANUBIS) && logic->CanKillEnemy(RE_KEESE)/*) && (str0 || CanUse(RG_HOVER_BOOTS) || CanUse(RG_HOOKSHOT) || (logic->IsAdult && CanBunnyJump())) || logic->CanKillEnemy(RE_ANUBIS, ED_BOOMERANG) && logic->CanKillEnemy(RE_KEESE, ED_FAR)*/;}), }, { //Locations @@ -625,7 +633,7 @@ void RegionTable_Init_SpiritTemple() { //Exits //Stalfos room blocks you in with fire until you kill the stalfos, which won't spawn from behind the fire Entrance(RR_SPIRIT_TEMPLE_MQ_TURNTABLE_ROOM, []{return false;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_SOUTH, []{return true/*str0 || CanUse(RG_HOVER_BOOTS) || CanUse(RG_HOOKSHOT)*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_SOUTH, []{return true/*str0 || CanUse(RG_HOVER_BOOTS) || CanUse(RG_HOOKSHOT) || (logic->IsAdult && CanBunnyJump())*/;}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_SOUTH] = Region("Spirit Temple MQ Map Room South", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, { @@ -638,7 +646,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits //The bridge is a temp flag, so not a way to cross south to north in logic - Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_NORTH, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_NORTH, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS)/* || (logic->IsAdult && CanBunnyJump())*/;}), Entrance(RR_SPIRIT_TEMPLE_MQ_1F_WEST, []{return true;}), }); @@ -687,17 +695,17 @@ void RegionTable_Init_SpiritTemple() { //and either direction lets child reach colossus. CanHitSwitch and CanKillEnemy(RE_IRON_KNUCKLE) is implied. //Logic can then allow child back into spirit, putting 1F west in logic with only 6 keys without forwards entry Entrance(RR_DESERT_COLOSSUS, []{return logic->IsChild/*CanUse(RG_CRAWL)*/ && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && - logic->ReverseSpiritChild && logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && /*(str0 || SunlightArrows())&&*/ - (logic->CanUse(RG_BOMBCHU_5) || (ctx->GetTrickOption(RT_RUSTED_SWITCHES) && logic->CanUse(RG_MEGATON_HAMMER)));}), + logic->ReverseSpiritChild && logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->MQSpiritStatueToSunBlock() && + (logic->CanUse(RG_BOMBCHU_5) || (ctx->GetTrickOption(RT_RUSTED_SWITCHES) && Here(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return logic->CanUse(RG_MEGATON_HAMMER);})));}), //!QUANTUM LOGIC! //If we have 6 keys and Child reverse spirit entry, we can gurantee broken wall room access for Child - //as long as we can hit a switch and because Adult cannot reach the initial child lock without + //as long as we can hit a switch and climb because Adult cannot reach the initial child lock without //first opening the Statue Room to Broken Wall Room lock. The details of this are handled in SpiritShared. //if adult can ever cross crawlspaces this becomes more complicated. Entrance(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6);}), Entrance(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, []{return logic->IsAdult || logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS);}), - //(IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME)) && str0 + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, []{return logic->IsAdult || logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS)/* || CanBunnyJump()*/;}), + //(IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME) || CanBunnyJump()) && str0 Entrance(RR_SPIRIT_TEMPLE_MQ_FLAMETHROWER_STAIRS, []{return logic->MQSpiritStatueToSunBlock();}), //explicit adult check here is a precaution against possible child logic leaking, child with a hookshot can do this Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}), @@ -774,7 +782,7 @@ void RegionTable_Init_SpiritTemple() { //Locations //We don't need Shared here because If we are checking as child, universe 2 adult access needs nothing so it always passes, and if we are checking as adult, it is Certain Access LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true/*str0*/;})), - // RT_SPIRIT_MQ_SUN_BLOCK_GS should probably be expanded to cover all ground based methods when str0 is added, as it can be hit with longshot because the skull hitbox is larger than the model + //RT_SPIRIT_MQ_SUN_BLOCK_GS should probably be expanded to cover all ground based methods when str0 is added, as it can be hit with longshot because the skull hitbox is larger than the model LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_SUN_BLOCK_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return (logic->CanUse(RG_HOOKSHOT)/* && (str0 || SunlightArrows())*/) || (ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_GS) && logic->CanUse(RG_BOOMERANG));})), LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCKS_POT_1, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return logic->CanBreakPots();})), @@ -835,7 +843,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST] = Region("Spirit Temple MQ Statue Room East", "Spirit Temple", {RA_SPIRIT_TEMPLE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_EAST_HAND, []{return logic->IsAdult || logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_EAST_HAND, []{return logic->IsAdult || logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS)/* || CanBunnyJump()*/;}), Entrance(RR_SPIRIT_TEMPLE_MQ_CHEST_LEDGE, []{return logic->CanUse(RG_HOVER_BOOTS) || ((ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->CanUse(RG_LONGSHOT));}), Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F, []{return logic->MQSpiritStatueRoomTorches;}), @@ -882,7 +890,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_LONGSHOT)/* || CanBunnyJump()*/;}), Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_EAST_HAND, []{return true;}), }); @@ -1118,9 +1126,11 @@ void RegionTable_Init_SpiritTemple() { }, {}, { // Exits Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), + //CanBunnyJump with a jumpslash can reach either hand and with good timing the platform as child. the latter is definitely a trick, the former may not be + //If this interaction with the hands is added, SpiritSharedLogic needs updating for it Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, []{return logic->CanUse(RG_HOVER_BOOTS);}), Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_EAST_HAND, []{return logic->CanUse(RG_HOVER_BOOTS);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_PLATFORM, []{return logic->SpiritPlatformLowered && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT));}), + Entrance(RR_SPIRIT_TEMPLE_MQ_PLATFORM, []{return logic->SpiritPlatformLowered && (logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT)/* || (IsAdult && CanBunnyJump())*/);}), Entrance(RR_SPIRIT_TEMPLE_BOSS_ENTRYWAY, []{return logic->HasItem(RG_SPIRIT_TEMPLE_BOSS_KEY);}), }); diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index ddc64f966..65f89cad6 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2383,6 +2383,8 @@ bool Logic::SpiritWestToSkull() { } bool Logic::SpiritSunBlockSouthLedge() { + // It's also possible to do a backwalk hover + backflip if you equip hovers as you start the backwalk to accelerate + // faster return true /*str0 || IsAdult || CanKillEnemy(RE_BEAMOS) || BunnyHovers() || (CanUse(RG_HOOKSHOT) && (HasFireSource() || (SpiritSunBlockTorch && (logic->CanUse(STICKS) || @@ -2396,7 +2398,8 @@ bool Logic::MQSpiritWestToPots() { } bool Logic::MQSpiritStatueToSunBlock() { - return (IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME)) /* && str0*/; + return (IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || + CanUse(RG_SONG_OF_TIME) /* || CanBunnyJump()*/) /* && str0*/; } bool Logic::MQSpiritStatueSouthDoor() { @@ -2613,7 +2616,9 @@ void Logic::Reset() { Spirit1FSilverRupees = false; SpiritChildSwitchBridge = false; SpiritRupeeBridge = false; + SpiritSunBlockTorch = false; SpiritBouldersSilvers = false; + SpiritStatueRoomSouthDoor = false; SpiritPlatformLowered = false; Spirit4FSwitch = false; SpiritPushed4FMirrors = false; From 458722d15d38e422aa8f45d7db47c5b20bc490ad Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 22 Jun 2025 14:43:03 +0100 Subject: [PATCH 22/29] clear forwards vanilla testing --- .../randomizer/location_access.cpp | 53 ++++++++++--------- .../Enhancements/randomizer/location_access.h | 4 +- .../dungeons/spirit_temple.cpp | 25 +++++---- soh/soh/Enhancements/randomizer/logic.cpp | 13 ++--- soh/soh/Enhancements/randomizer/logic.h | 1 - .../Enhancements/randomizer/randomizerTypes.h | 2 +- soh/soh/Enhancements/randomizer/settings.cpp | 4 +- 7 files changed, 54 insertions(+), 48 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index 98484d888..69128e091 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -600,30 +600,24 @@ bool Here(const RandomizerRegion region, ConditionFn condition) { * the second condition is the same for adult 1F lock, and the third is the access from the boss door. */ -bool SpiritExplosiveKeyLogic() { - return logic->SmallKeys(RR_SPIRIT_TEMPLE, logic->SpiritBrokenWallToStatue() ? 1 - : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 - : 3); -} - // clang-format off std::map Region::spiritLogicData = { //Vanilla - {RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, {5, 5, 5, []{return true;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, - {RR_SPIRIT_TEMPLE_BROKEN_WALL, {5, 5, 5, []{return true /*logic->CanClimbHigh()*/;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, - {RR_SPIRIT_TEMPLE_2F_MIRROR, {5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();}, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, {5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, - {RR_SPIRIT_TEMPLE_INNER_WEST_HAND, {5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, - {RR_SPIRIT_TEMPLE_GS_LEDGE, {5, 5, 3, []{return SpiritExplosiveKeyLogic() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh()*/;}, []{return logic->SpiritWestToSkull()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM, {5, 5, 3, []{return SpiritExplosiveKeyLogic();}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true;}}}, + {RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, {5, 5, 9, []{return true;}, []{return true/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_BROKEN_WALL, {5, 5, 9, []{return true /*logic->CanClimbHigh()*/;}, []{return true/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_2F_MIRROR, {5, 5, 9, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();}, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, {5, 5, 9, []{return true/*logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_INNER_WEST_HAND, {5, 5, 9, []{return true/*logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_GS_LEDGE, {5, 5, 9, []{return true && logic->SpiritWestToSkull()/* && logic->CanClimbHigh()*/;}, []{return logic->SpiritWestToSkull()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM, {5, 5, 9, []{return true;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true;}}}, //Assumes SpiritSunBlockSouthLedge() for all access - {RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, {5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, - {RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, {5, 5, 3, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, - {RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, {5, 5, 3, []{return SpiritExplosiveKeyLogic() && logic->CanKillEnemy(RE_IRON_KNUCKLE) //For the purpose of shared, adult needs to get to west side via BOTH possible routes for it to count //Only using HasItem here is intended so this check can pass as child if adult can do their part. This works because this edge case assumes that you can only waste keys on adult side with adult - /*&& logic->CanClimbHigh() && str0*/;}, []{return logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT)/* && logic->CanClimbHigh() && str0*/;}, []{return logic->CanKillEnemy(RE_BEAMOS) && logic->HasItem(RG_LONGSHOT)/* && logic->CanClimb() && str0*/;}}}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, {5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, - {RR_SPIRIT_TEMPLE_INNER_EAST_HAND, {5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, - {RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, {5, 5, 3, []{return logic->CanUse(RG_HOOKSHOT) && SpiritExplosiveKeyLogic() && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));}}}, + {RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, {5, 5, 9, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, + {RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, {5, 5, 9, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, + {RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, {5, 5, 3, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE)/*&& logic->CanClimbHigh() && str0*/;}, //For the purpose of shared, adult needs to get to west side via BOTH possible routes for it to count //Only using HasItem here is intended so this check can pass as child if adult can do their part. This works because this edge case assumes that you can only waste keys on adult side with adult + []{return logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT)/* && logic->CanClimbHigh() && str0*/;}, []{return logic->CanKillEnemy(RE_BEAMOS) && logic->HasItem(RG_LONGSHOT)/* && logic->CanClimb() && str0*/;}}}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, {5, 5, 9, []{return logic->CanUse(RG_HOOKSHOT)/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, + {RR_SPIRIT_TEMPLE_INNER_EAST_HAND, {5, 5, 9, []{return logic->CanUse(RG_HOOKSHOT)/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, + {RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, {5, 5, 9, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));}}}, //MQ /*&& logic->CanClimbHigh()*/ {RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, {7, 6, 7, []{return true;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, {RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, {7, 6, 7, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, @@ -643,7 +637,7 @@ std::map Region::spiritLogicData = { bool SpiritCertainAccess(RandomizerRegion region) { SpiritLogicData& curRegionData = Region::spiritLogicData[region]; uint8_t keys = curRegionData.adultKeys; - bool reverseAccess = logic->ReverseSpiritAdult; + bool canReach = false; if (logic->IsChild) { // If child enters in reverse, then they have access to Certain Access to Broken Wall room in 6 keys, // the ability to hit switches and the ability to climb because only child can reach the initial child lock @@ -651,18 +645,25 @@ bool SpiritCertainAccess(RandomizerRegion region) { keys = (logic->ReverseSpiritChild && logic->CanHitSwitch() /* && CanClimbHigh()*/) ? curRegionData.childReverseKeys : curRegionData.childKeys; - reverseAccess = logic->ReverseSpiritChild; + canReach = (areaTable[region].Child() || curRegionData.childAccess()) && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess()); + } else { + canReach = (areaTable[region].Adult() || curRegionData.adultAccess()) && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess()); } + //keys set to 9 means it's the bombchu edge case. + if (keys == 9){ + keys = ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 : 3; + } + // If we have enough keys that an age cannot be kept out, we have Certain Access // otherwise if we have entered in reverse and can reach from the face, we have Certain Access - return logic->SmallKeys(RR_SPIRIT_TEMPLE, keys) || (reverseAccess && curRegionData.reverseAccess()); + return canReach && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys); } /* - * Spirit Shared can take up to 3 regions, this is because checks can exist in many regions at the same time + Spirit Shared can take up to 3 regions, this is because checks can exist in many regions at the same time and the logic needs to be able to check the access logic from those regions to check the other universes properly. - *anyAge is equivalent to a self referencing Here, used for events and any check where that is relevent. + anyAge is equivalent to a self referencing Here, used for events and any check where that is relevent. */ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, RandomizerRegion otherRegion, @@ -684,7 +685,7 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, R logic->IsAdult = true; bool AdultCertainAccess = SpiritCertainAccess(region); - // If we are AnyAge and have any CeratinAccess, then we can check those ages + // If we are AnyAge and have any CertainAccess, then we can check those ages // we don't need to check ambiguity here as if this fails, then 1 of the ages has failed if (anyAge && (ChildCertainAccess || AdultCertainAccess)) { // set age access to the Certain Access diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 98e492a0e..75f1157db 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -121,7 +121,9 @@ struct SpiritLogicData { // This changes for MQ broken wall room as the first child lock can only be opened by Child // guaranteeing access with 6 keys uint8_t childReverseKeys; - uint8_t adultKeys; // the number of keys that guarantees Adult can reach this region + // the number of keys that guarantees Adult can reach this region + // if it is 9, that means the bombchu edge case is to be checked. + uint8_t adultKeys; // The area access condition to reach this region as Child, from the first lock, // including the minimum number of keys for ambiguous access // 1 key is always assumed to be required diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index 5128856b7..a151d4aa6 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -34,8 +34,9 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return logic->IsChild/*CanUse(RG_CRAWL)*/;}), Entrance(RR_SPIRIT_TEMPLE_1F_BOXES, []{return logic->IsChild/*CanUse(RG_CRAWL)*/;}), - Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH, []{return Here(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);});}), - Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH, []{return Here(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->CanKillEnemy(RE_ARMOS) && logic->CanKillEnemy(RE_KEESE);});}), + //Implies logic->CanKillEnemy(RE_KEESE) + Entrance(RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH, []{return Here(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->CanKillEnemy(RE_ARMOS);});}), + Entrance(RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_SOUTH, []{return Here(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->CanKillEnemy(RE_ARMOS);});}), }); areaTable[RR_SPIRIT_TEMPLE_SWITCH_BRIDGE_SOUTH] = Region("Spirit Temple Switch Bridge South", SCENE_SPIRIT_TEMPLE, { @@ -129,7 +130,7 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->SpiritBrokenWallToStatue();}), }); - areaTable[RR_SPIRIT_TEMPLE_1F_EAST] = Region("Adult Spirit Temple Lobby", SCENE_SPIRIT_TEMPLE, {}, {}, { + areaTable[RR_SPIRIT_TEMPLE_1F_EAST] = Region("Spirit Temple 1F East", SCENE_SPIRIT_TEMPLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_LOBBY, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_SAND_PIT, []{return Here(RR_SPIRIT_TEMPLE_1F_EAST, []{return logic->CanHitSwitch(logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOWER_ADULT_SWITCH) ? ED_BOMB_THROW : ED_BOOMERANG);});}), @@ -265,7 +266,6 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_SUN_BLOCK_ROOM] = Region("Spirit Temple Sun Block Room", SCENE_SPIRIT_TEMPLE, {}, {}, { //Exits Entrance(RR_SPIRIT_TEMPLE_EMPTY_STAIRS, []{return true/*str0 || SunlightArrows*/;}), @@ -329,7 +329,7 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_2F_MIRROR, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_INNER_EAST_HAND, []{return true;}), - //(IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); + //(IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || CanUse(RG_HOVER_BOOTS) || (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); Entrance(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return logic->SpiritEastToSwitch();}), Entrance(RR_SPIRIT_TEMPLE_POT_STAIRS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}), //!QUANTUM LOGIC! @@ -339,6 +339,11 @@ void RegionTable_Init_SpiritTemple() { //Implies CanKillEnemy(RE_IRON_KNUCKLE) Entrance(RR_DESERT_COLOSSUS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanKillEnemy(RE_BEAMOS)/* && CanClimb() && str0*/;}), //!QUANTUM LOGIC! + //Continuing from above, if we have the longshot, we can guarantee access to the outer west hand as you can longshot from the east hand to the west + //Implies CanKillEnemy(RE_IRON_KNUCKLE) + Entrance(RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanKillEnemy(RE_BEAMOS) && /*CanClimb() && str0 &&*/ + logic->CanUse(RG_LONGSHOT);}), + //!QUANTUM LOGIC! //A variant of the above, if dungeon entrance randomiser is off, Adult entered spirit in reverse, and they have str 2 to get there from the front, //it is always possible for them to reach 1F_EAST with only 2 keys. This is because you can only waste 1 key (on the first child side lock) //before you either allow you to climb down through 2F mirror room, or give yourself access to a hand to jump down from. @@ -362,7 +367,7 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->CanUse(RG_HOVER_BOOTS)/* || CanBunnyJump()*/;}), //Assumes RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST access via RR_SPIRIT_TEMPLE_STATUE_ROOM - Entrance(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || + Entrance(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || (logic->CanUse(RG_ZELDAS_LULLABY) && logic->CanUse(RG_HOOKSHOT));}), }); @@ -522,14 +527,13 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_INNER_EAST_HAND, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_STATUE_HEAD, []{return logic->SpiritPushed4FMirrors && logic->CanUse(RG_HOOKSHOT);}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_HEAD, []{return logic->SpiritPushed4FMirrors && logic->CanUse(RG_MIRROR_SHIELD) && logic->CanUse(RG_HOOKSHOT);}), }); areaTable[RR_SPIRIT_TEMPLE_STATUE_HEAD] = Region("Spirit Temple Statue Head", SCENE_SPIRIT_TEMPLE, { //Events //WARNING these events are not glitchproofed and assume you need all keys to reach from the front EventAccess(&logic->ReverseSpiritChild, []{return logic->IsChild;}), - EventAccess(&logic->ReverseSpiritAdult, []{return logic->IsAdult;}), }, {}, { // Exits Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), @@ -738,7 +742,7 @@ void RegionTable_Init_SpiritTemple() { //Land on the SoT block Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), - Entrance(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP);}), // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled Entrance(RR_SPIRIT_TEMPLE_MQ_PLATFORM, []{return logic->SpiritPlatformLowered && (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), @@ -880,7 +884,7 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_EAST, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_HOOKSHOT);}), Entrance(RR_SPIRIT_TEMPLE_MQ_CHEST_LEDGE, []{return ((logic->IsAdult || ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE)) && (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->CanUse(RG_HOOKSHOT)) - || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP));}), + || (logic->IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP));}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_CHEST_LEDGE] = Region("Spirit Temple MQ Chest Ledge", SCENE_SPIRIT_TEMPLE, {}, { @@ -1121,7 +1125,6 @@ void RegionTable_Init_SpiritTemple() { //Events //WARNING these events are not glitchproofed and assume you need all keys to reach from the front EventAccess(&logic->ReverseSpiritChild, []{return logic->IsChild;}), - EventAccess(&logic->ReverseSpiritAdult, []{return logic->IsAdult;}), }, {}, { // Exits Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 24ca3f5e4..80371cbf2 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -926,7 +926,7 @@ bool Logic::CanAvoidEnemy(RandomizerEnemy enemy, bool grounded, uint8_t quantity case RE_TORCH_SLUG: return !grounded || CanUse(RG_NUTS) || CanUse(RG_HOOKSHOT) || CanUse(RG_DINS_FIRE); default: - SPDLOG_ERROR("CanPassEnemy reached `default`."); + SPDLOG_ERROR("CanAvoidEnemy reached `default`."); assert(false); return false; } @@ -2364,8 +2364,10 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) { } bool Logic::IsReverseAccessPossible() { + //If we ever allow dungeon entrances to connect to boss rooms directly in dungeon chains, or for 1 boss door to lead to another dungeons boss door, add RSK_MIX_DUNGEON_ENTRANCES to the final condition return ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES) && - (ctx->GetOption(RSK_DECOUPLED_ENTRANCES) || ctx->GetOption(RSK_MIX_BOSS_ENTRANCES)); + (ctx->GetOption(RSK_DECOUPLED_ENTRANCES) || + (ctx->GetOption(RSK_MIX_BOSS_ENTRANCES) && (ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES) || ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES)))); } bool Logic::SpiritBrokenWallToStatue() { @@ -2373,12 +2375,12 @@ bool Logic::SpiritBrokenWallToStatue() { } bool Logic::SpiritEastToSwitch() { - return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || + return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || CanUse(RG_HOVER_BOOTS) || (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); } bool Logic::SpiritWestToSkull() { - return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SCARECROW); + return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SCARECROW); } bool Logic::SpiritSunBlockSouthLedge() { @@ -2393,7 +2395,7 @@ bool Logic::SpiritSunBlockSouthLedge() { // Combines crossing the ledge directly and the jump from the hand bool Logic::MQSpiritWestToPots() { - return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_LOBBY_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SONG_OF_TIME); + return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SONG_OF_TIME); } bool Logic::MQSpiritStatueToSunBlock() { @@ -2628,7 +2630,6 @@ void Logic::Reset(bool resetSaveContext /*= true*/) { Spirit4FSwitch = false; SpiritPushed4FMirrors = false; ReverseSpiritChild = false; - ReverseSpiritAdult = false; CalculatingAvailableChecks = false; diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 82ca8acda..4e230a233 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -187,7 +187,6 @@ class Logic { bool Spirit4FSwitch = false; bool SpiritPushed4FMirrors = false; bool ReverseSpiritChild = false; - bool ReverseSpiritAdult = false; /* --- END OF HELPERS AND LOCATION ACCESS --- */ diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 3ac6b488a..bdc47519e 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -3703,7 +3703,7 @@ typedef enum { RT_SPIRIT_CHILD_CHU, RT_SPIRIT_WEST_LEDGE, RT_SPIRIT_LOWER_ADULT_SWITCH, - RT_SPIRIT_LOBBY_JUMP, + RT_SPIRIT_STATUE_JUMP, RT_SPIRIT_PLATFORM_HOOKSHOT, RT_SPIRIT_MAP_CHEST, RT_SPIRIT_SUN_CHEST, diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 111c7a18d..f9b29d553 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -1023,8 +1023,8 @@ void Settings::CreateOptions() { "A bomb can be used to hit the switch on the ceiling, but it must be thrown from a particular distance " "away and with precise timing."); OPT_TRICK( - RT_SPIRIT_LOBBY_JUMP, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, - "Spirit Temple Main Room Jump from Hands to Upper Ledges", + RT_SPIRIT_STATUE_JUMP, RCQUEST_BOTH, RA_SPIRIT_TEMPLE, { Tricks::Tag::INTERMEDIATE }, + "Spirit Temple Statue Room Jump from Hands to Upper Ledges", "A precise jump to obtain the following as adult without needing one of Hover Boots, or Hookshot (in Vanilla) " "or Song of Time (in MQ): - Spirit Temple Statue Room Northeast Chest - Spirit Temple GS Lobby - Spirit Temple " "MQ Central Chamber Top Left Pot (Left) - Spirit Temple MQ Central Chamber Top Left Pot (Right)"); From e263acd4a60533ae3c2cd188f56f7b117cc24a1d Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 25 Jun 2025 17:34:20 +0100 Subject: [PATCH 23/29] finish vanilla --- .../randomizer/location_access.cpp | 119 +++++++++++------- .../Enhancements/randomizer/location_access.h | 8 +- .../dungeons/spirit_temple.cpp | 94 +++++++------- soh/soh/Enhancements/randomizer/logic.cpp | 5 +- soh/soh/Enhancements/randomizer/logic.h | 5 +- .../Enhancements/randomizer/randomizerTypes.h | 4 +- soh/soh/Enhancements/randomizer/settings.cpp | 9 +- 7 files changed, 140 insertions(+), 104 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index 69128e091..abf32e49b 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -600,63 +600,89 @@ bool Here(const RandomizerRegion region, ConditionFn condition) { * the second condition is the same for adult 1F lock, and the third is the access from the boss door. */ +bool SpiritExplosiveKeyLogic() { + return logic->SmallKeys(RR_SPIRIT_TEMPLE, logic->HasExplosives() ? 1 + : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 + : 3); +} + +//!QUANTUM LOGIC! +//With 3 keys, you cannot lock adult out of leaving spirit onto the hands and jumping down, as you would have to +//open the west hand door and then adult could climb through sun block room to jump down from there +//This requires that adult can complete both routes +//If we have the longshot, we can also guarantee access to the outer west hand as you can longshot from the east hand to the west +//Implies CanKillEnemy(RE_IRON_KNUCKLE) +bool OuterWestHandLogic(){ + return logic->HasExplosives()/* && logic->CanClimbHigh() && str0*/ && logic->SmallKeys(RR_SPIRIT_TEMPLE, logic->HasItem(RG_LONGSHOT) ? 3 : 5); +} + // clang-format off std::map Region::spiritLogicData = { - //Vanilla - {RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, {5, 5, 9, []{return true;}, []{return true/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, - {RR_SPIRIT_TEMPLE_BROKEN_WALL, {5, 5, 9, []{return true /*logic->CanClimbHigh()*/;}, []{return true/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, - {RR_SPIRIT_TEMPLE_2F_MIRROR, {5, 5, 9, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritBrokenWallToStatue();}, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, {5, 5, 9, []{return true/*logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, - {RR_SPIRIT_TEMPLE_INNER_WEST_HAND, {5, 5, 9, []{return true/*logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, - {RR_SPIRIT_TEMPLE_GS_LEDGE, {5, 5, 9, []{return true && logic->SpiritWestToSkull()/* && logic->CanClimbHigh()*/;}, []{return logic->SpiritWestToSkull()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM, {5, 5, 9, []{return true;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true;}}}, + //Vanilla Child uses ExplosiveKeyLogic here because they need to exist for shared adult checks + {RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, {5, 0, 3, 0, []{return true;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_SUN_ON_FLOOR, {5, 0, 3, 0, []{return true/*logic->CanClimbHigh()*/;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_2F_MIRROR, {5, 0, 3, 0, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritSunOnFloorToStatue();}, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_INNER_WEST_HAND, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_GS_LEDGE, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh()*/;}, []{return SpiritExplosiveKeyLogic() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic();}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true;}}}, //Assumes SpiritSunBlockSouthLedge() for all access - {RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, {5, 5, 9, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, - {RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, {5, 5, 9, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, - {RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, {5, 5, 3, []{return logic->CanKillEnemy(RE_IRON_KNUCKLE)/*&& logic->CanClimbHigh() && str0*/;}, //For the purpose of shared, adult needs to get to west side via BOTH possible routes for it to count //Only using HasItem here is intended so this check can pass as child if adult can do their part. This works because this edge case assumes that you can only waste keys on adult side with adult - []{return logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT)/* && logic->CanClimbHigh() && str0*/;}, []{return logic->CanKillEnemy(RE_BEAMOS) && logic->HasItem(RG_LONGSHOT)/* && logic->CanClimb() && str0*/;}}}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, {5, 5, 9, []{return logic->CanUse(RG_HOOKSHOT)/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, - {RR_SPIRIT_TEMPLE_INNER_EAST_HAND, {5, 5, 9, []{return logic->CanUse(RG_HOOKSHOT)/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, - {RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, {5, 5, 9, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));}}}, + {RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, + {RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, + {RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, {5, 5, 3, 3, []{return OuterWestHandLogic();}, []{return OuterWestHandLogic();}, []{return OuterWestHandLogic();}}}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic() && logic->CanUse(RG_HOOKSHOT)/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, + {RR_SPIRIT_TEMPLE_INNER_EAST_HAND, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic() && logic->CanUse(RG_HOOKSHOT)/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, + {RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic() && logic->CanUse(RG_HOOKSHOT) && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));}}}, //MQ /*&& logic->CanClimbHigh()*/ - {RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, {7, 6, 7, []{return true;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, - {RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, {7, 6, 7, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, - {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, {7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->Climb*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, - {RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, {7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;}}}, - {RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, {7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;}}}, - {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, {7, 7, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true;}, []{return true;}}}, - {RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, {7, 7, 0, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && logic->Climb*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, - {RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND, {7, 7, 4, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock() //For the purpose of shared, adult needs to get to west side via BOTH possible routes for it to count //Only using HasItem here for adult items so child can pass this check - /* && logic->CanClimbHigh() && str0*/;}, []{return logic->MQSpirit4KeyWestHand();}, []{return logic->CouldMQSpirit4KeyWestHand();}}}, - {RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, {7, 7, 0, []{return logic->CanHitSwitch() && - areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();}) - /* && logic->CanClimbHigh()*/;}, []{return true;}, []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();});}}}, + {RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, {7, 9, 7, 7, []{return true;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, {7, 9, 7, 7, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, {7, 0, 0, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->Climb*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, {7, 0, 0, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, {7, 0, 0, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, {7, 0, 0, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true;}, []{return true;}}}, + {RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, {7, 0, 0, 0, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && logic->Climb*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + //RANDOTODO FIX + {RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND, {7, 7, 4, 4, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock() //For the purpose of shared, adult needs to get to west side via BOTH possible routes for it to count //Only using HasItem here for adult items so child can pass this check + /* && logic->CanClimbHigh() && str0*/;}, []{return logic->MQSpirit4KeyWestHand();}, []{return logic->CouldMQSpirit4KeyWestHand();}}}, + {RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, {7, 0, 0, 0, []{return logic->CanHitSwitch() && + areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();}) + /* && logic->CanClimbHigh()*/;}, []{return true;}, []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();});}}}, }; // clang-format on bool SpiritCertainAccess(RandomizerRegion region) { SpiritLogicData& curRegionData = Region::spiritLogicData[region]; - uint8_t keys = curRegionData.adultKeys; - bool canReach = false; if (logic->IsChild) { + uint8_t keys = curRegionData.childKeys; + uint8_t revKeys = curRegionData.childRevKeys; + bool knownFrontAccess = logic->ForwardsSpiritChild || !logic->IsReverseAccessPossible(); + // revKeys set to 9 means it's the sun on floor room in MQ // If child enters in reverse, then they have access to Certain Access to Broken Wall room in 6 keys, // the ability to hit switches and the ability to climb because only child can reach the initial child lock // without opening the Statue room to Broken Wall Room lock first - keys = (logic->ReverseSpiritChild && logic->CanHitSwitch() /* && CanClimbHigh()*/) - ? curRegionData.childReverseKeys - : curRegionData.childKeys; - canReach = (areaTable[region].Child() || curRegionData.childAccess()) && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess()); - } else { - canReach = (areaTable[region].Adult() || curRegionData.adultAccess()) && (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess()); - } - //keys set to 9 means it's the bombchu edge case. - if (keys == 9){ - keys = ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 : 3; - } + if (revKeys == 9){ + revKeys = (logic->ReverseSpiritChild && logic->CanHitSwitch() /* && CanClimbHigh()*/) ? 6 : 7; + } - // If we have enough keys that an age cannot be kept out, we have Certain Access - // otherwise if we have entered in reverse and can reach from the face, we have Certain Access - return canReach && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys); + // If we have enough keys that an age cannot be kept out, we have Certain Access + // otherwise if we have entered in reverse and can reach from the face, we have Certain Access + return ((knownFrontAccess && curRegionData.childAccess()) && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys)) || + ((logic->ReverseSpiritChild && curRegionData.reverseAccess()) && logic->SmallKeys(RR_SPIRIT_TEMPLE, revKeys)) || + (curRegionData.childAccess() && curRegionData.reverseAccess() && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys > revKeys ? keys : revKeys)); + } else { + uint8_t keys = curRegionData.adultKeys; + uint8_t revKeys = curRegionData.adultRevKeys; + bool knownFrontAccess = logic->ForwardsSpiritAdult || !logic->IsReverseAccessPossible(); + auto test = logic->IsReverseAccessPossible(); + auto test2 = (knownFrontAccess && curRegionData.adultAccess()); + auto test3 = (logic->ReverseSpiritAdult && curRegionData.reverseAccess()); + auto test4 = curRegionData.adultAccess() && curRegionData.reverseAccess(); + // If we have enough keys that an age cannot be kept out, we have Certain Access + // otherwise if we have entered in reverse and can reach from the face, we have Certain Access + return ((knownFrontAccess && curRegionData.adultAccess()) && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys)) || + ((logic->ReverseSpiritAdult && curRegionData.reverseAccess()) && logic->SmallKeys(RR_SPIRIT_TEMPLE, revKeys)) || + (curRegionData.adultAccess() && curRegionData.reverseAccess() && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys > revKeys ? keys : revKeys)); + } } /* @@ -705,11 +731,11 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, R // If we have Certain Access, we just run the condition. // Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Adult // and if needed, in reverse - if (!ChildCertainAccess && result && - (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess())) { + if (!ChildCertainAccess && result) { // Switch to Adult logic->IsChild = false; logic->IsAdult = true; + // If Adult can get there and get the check, we can get the check in logic // If reverse spirit is also possible, we need to make sure Adult can get it via reverse entry too result = (curRegionData.adultAccess() && @@ -729,8 +755,7 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, R // Alternatively, if we have entered in reverse and can reach from the face, we have Certain Access // Otherwise, if we have the keys to know either age can reach, we need to see if we could reach as Child // and if needed, in reverse - if (!AdultCertainAccess && result && - (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess)) { + if (!AdultCertainAccess && result) { // Switch to Child logic->IsChild = true; logic->IsAdult = false; diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 75f1157db..0e864c228 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -118,12 +118,14 @@ enum class EntranceType; struct SpiritLogicData { uint8_t childKeys; // the number of keys that guarantees Child can reach this region // The number of keys that guarantees Child can reach this region if they have reverse access - // This changes for MQ broken wall room as the first child lock can only be opened by Child - // guaranteeing access with 6 keys - uint8_t childReverseKeys; + // 9 means MQ broken wall room, as the first child lock can only be opened by Child + // without opening the lock to Statue room, guaranteeing access with 6 keys if you can hit a switch + uint8_t childRevKeys; // the number of keys that guarantees Adult can reach this region // if it is 9, that means the bombchu edge case is to be checked. uint8_t adultKeys; + // the number of keys that guarantees Adult can reach this region with reverse entry + uint8_t adultRevKeys; // The area access condition to reach this region as Child, from the first lock, // including the minimum number of keys for ambiguous access // 1 key is always assumed to be required diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index a151d4aa6..e870e78d6 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -16,7 +16,12 @@ void RegionTable_Init_SpiritTemple() { #pragma region Vanilla - areaTable[RR_SPIRIT_TEMPLE_LOBBY] = Region("Spirit Temple Lobby", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_LOBBY] = Region("Spirit Temple Lobby", SCENE_SPIRIT_TEMPLE, { + // Events + //WARNING these events assume you need less or equal keys for forwards entry and reverse + EventAccess(&logic->ForwardsSpiritChild, []{return logic->IsChild;}), + EventAccess(&logic->ForwardsSpiritAdult, []{return logic->IsAdult;}), + }, { //Locations LOCATION(RC_SPIRIT_TEMPLE_LOBBY_POT_1, logic->CanBreakPots()), LOCATION(RC_SPIRIT_TEMPLE_LOBBY_POT_2, logic->CanBreakPots()), @@ -105,7 +110,7 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_1F_WEST, []{return logic->IsChild/*CanUse(RG_CRAWL)*/;}), - Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 1);}), + Entrance(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 1);}), }); areaTable[RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE] = Region("Spirit Temple Child Climb Base", SCENE_SPIRIT_TEMPLE, {}, { @@ -115,19 +120,19 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits Entrance(RR_SPIRIT_TEMPLE_1F_BOXES, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), - Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return true/*CanClimbHigh()*/;}), + Entrance(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR, []{return true/*CanClimbHigh()*/;}), }); - areaTable[RR_SPIRIT_TEMPLE_BROKEN_WALL] = Region("Spirit Temple Broken Wall", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_SUN_ON_FLOOR] = Region("Spirit Temple Broken Wall", SCENE_SPIRIT_TEMPLE, {}, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_NORTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->CanHitSwitch(ED_BOMB_THROW);})), - LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_EAST_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->CanHitSwitch(ED_BOMB_THROW);})), - LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA, logic->TakeDamage() ? ED_SHORT_JUMPSLASH : ED_BOMB_THROW);})), + LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_NORTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR, []{return logic->CanHitSwitch(ED_BOMB_THROW);})), + LOCATION(RC_SPIRIT_TEMPLE_CHILD_CLIMB_EAST_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR, []{return logic->CanHitSwitch(ED_BOMB_THROW);})), + LOCATION(RC_SPIRIT_TEMPLE_GS_SUN_ON_FLOOR_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA, logic->TakeDamage() ? ED_SHORT_JUMPSLASH : ED_BOMB_THROW);})), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, []{return true;}), ///*CanClimbHigh() &&*/ (HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS))) - Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->SpiritBrokenWallToStatue();}), + Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return logic->SpiritSunOnFloorToStatue();}), }); areaTable[RR_SPIRIT_TEMPLE_1F_EAST] = Region("Spirit Temple 1F East", SCENE_SPIRIT_TEMPLE, {}, {}, { @@ -202,7 +207,7 @@ void RegionTable_Init_SpiritTemple() { RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanKillEnemy(RE_GOLD_SKULLTULA);})), }, { //Exits - Entrance(RR_SPIRIT_TEMPLE_BROKEN_WALL, []{return true;}), + Entrance(RR_SPIRIT_TEMPLE_SUN_ON_FLOOR, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_INNER_WEST_HAND, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_GS_LEDGE, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SCARECROW);}), // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled @@ -214,7 +219,7 @@ void RegionTable_Init_SpiritTemple() { //Child cannot lock themselves out of desert colossus access as if they save the west hand lock for last //they will be able to exit the dungeon through the intended entrance and vice versa //for needing to open the west hand lock to block the intended child route - Entrance(RR_DESERT_COLOSSUS, []{return ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && logic->ReverseSpiritChild && /*str0 &&*/ + Entrance(RR_DESERT_COLOSSUS, []{return ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && /*str0 &&*/ logic->IsChild/*CanUse(RG_CRAWL)*/ && logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && logic->CanKillEnemy(RE_IRON_KNUCKLE);}), }); @@ -258,7 +263,17 @@ void RegionTable_Init_SpiritTemple() { //explicit adult check here is a precaution against possible child logic leaking, child with a hookshot can do this Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, []{return logic->IsAdult && logic->CanUse(RG_HOOKSHOT);}), Entrance(RR_SPIRIT_TEMPLE_SHORTCUT, []{return logic->SpiritStatueRoomSouthDoor;}), - }); + //!QUANTUM LOGIC! + //If dungeon entrance randomiser is off, Adult entered spirit in reverse, and they have str 2 to get there from the front and Explosives, + //it is always possible for them to reach 1F_EAST with only 2 keys. This is because you can only waste 1 key (on the first child side lock) + //before you either allow you to climb down through 2F mirror room, or give yourself access to a hand to jump down from. + //If for whatever reason you can reach east hand but not west hand, this becomes possible with 3 keys instead. + //If you do not have explosives to kill Beamos, but do have a way to defeat Iron Knuckles, this becomes possible with 4 keys instead. + Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && + logic->CanUse(RG_SILVER_GAUNTLETS) && logic->IsAdult && + ((logic->CanKillEnemy(RE_BEAMOS) && logic->SmallKeys(RR_SPIRIT_TEMPLE, /*CanClimb() && str0 ?*/ 2 /*: 3*/)) || + (/*CanClimb() && str0 && */logic->CanKillEnemy(RE_IRON_KNUCKLE) && logic->SmallKeys(RR_SPIRIT_TEMPLE, 4)));}), + }); areaTable[RR_SPIRIT_TEMPLE_EMPTY_STAIRS] = Region("Spirit Temple Empty Stairs", SCENE_SPIRIT_TEMPLE, {}, {}, { //Exits @@ -315,7 +330,7 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, []{return true;}), }); - areaTable[RR_SPIRIT_TEMPLE_OUTER_WEST_HAND] = Region("Spirit Temple West Hand", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_OUTER_WEST_HAND] = Region("Spirit Temple Outer West Hand", SCENE_SPIRIT_TEMPLE, {}, { //Locations LOCATION(RC_SPIRIT_TEMPLE_SILVER_GAUNTLETS_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, []{return true;})), }, { @@ -332,27 +347,6 @@ void RegionTable_Init_SpiritTemple() { //(IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || CanUse(RG_HOVER_BOOTS) || (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); Entrance(RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, []{return logic->SpiritEastToSwitch();}), Entrance(RR_SPIRIT_TEMPLE_POT_STAIRS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4);}), - //!QUANTUM LOGIC! - //With 3 keys, you cannot lock adult out of leaving spirit onto the hands and jumping down, as you would have to - //open the west hand door and then adult could climb through sun block room to jump down from there - //This requires that adult can complete both routes - //Implies CanKillEnemy(RE_IRON_KNUCKLE) - Entrance(RR_DESERT_COLOSSUS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanKillEnemy(RE_BEAMOS)/* && CanClimb() && str0*/;}), - //!QUANTUM LOGIC! - //Continuing from above, if we have the longshot, we can guarantee access to the outer west hand as you can longshot from the east hand to the west - //Implies CanKillEnemy(RE_IRON_KNUCKLE) - Entrance(RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 3) && logic->CanKillEnemy(RE_BEAMOS) && /*CanClimb() && str0 &&*/ - logic->CanUse(RG_LONGSHOT);}), - //!QUANTUM LOGIC! - //A variant of the above, if dungeon entrance randomiser is off, Adult entered spirit in reverse, and they have str 2 to get there from the front, - //it is always possible for them to reach 1F_EAST with only 2 keys. This is because you can only waste 1 key (on the first child side lock) - //before you either allow you to climb down through 2F mirror room, or give yourself access to a hand to jump down from. - //If for whatever reason you can reach east hand but not west hand, this becomes possible with 3 keys instead. - //If for whatever reason you can reach west hand but not east hand, this becomes possible with 4 keys instead. - Entrance(RR_SPIRIT_TEMPLE_1F_EAST, []{return ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && - logic->CanUse(RG_SILVER_GAUNTLETS) && - ((logic->CanKillEnemy(RE_BEAMOS) && logic->SmallKeys(RR_SPIRIT_TEMPLE, /*CanClimb() && str0 ?*/ 2 /*: 3*/)) || - (/*CanClimb() && str0 && */logic->CanKillEnemy(RE_IRON_KNUCKLE) && logic->SmallKeys(RR_SPIRIT_TEMPLE, 4)));}), //RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled Entrance(RR_SPIRIT_TEMPLE_PLATFORM, []{return logic->SpiritPlatformLowered && (logic->CanUse(RG_LONGSHOT) || (ctx->GetTrickOption(RT_SPIRIT_PLATFORM_HOOKSHOT) && logic->CanUse(RG_HOOKSHOT)));}), @@ -534,6 +528,7 @@ void RegionTable_Init_SpiritTemple() { //Events //WARNING these events are not glitchproofed and assume you need all keys to reach from the front EventAccess(&logic->ReverseSpiritChild, []{return logic->IsChild;}), + EventAccess(&logic->ReverseSpiritAdult, []{return logic->IsAdult;}), }, {}, { // Exits Entrance(RR_SPIRIT_TEMPLE_STATUE_ROOM, []{return true;}), @@ -549,7 +544,12 @@ void RegionTable_Init_SpiritTemple() { #pragma region MQ - areaTable[RR_SPIRIT_TEMPLE_MQ_LOBBY] = Region("Spirit Temple MQ Lobby", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_LOBBY] = Region("Spirit Temple MQ Lobby", SCENE_SPIRIT_TEMPLE, { + // Events + //WARNING these events assume you need less or equal keys for forwards entry and reverse + EventAccess(&logic->ForwardsSpiritChild, []{return logic->IsChild;}), + EventAccess(&logic->ForwardsSpiritAdult, []{return logic->IsAdult;}), + }, { //Locations LOCATION(RC_SPIRIT_TEMPLE_MQ_ENTRANCE_FRONT_LEFT_CHEST, true), LOCATION(RC_SPIRIT_TEMPLE_MQ_ENTRANCE_BACK_LEFT_CHEST, Here(RR_SPIRIT_TEMPLE_MQ_LOBBY, []{return logic->BlastOrSmash();}) && logic->CanHitEyeTargets()), @@ -671,15 +671,15 @@ void RegionTable_Init_SpiritTemple() { //Exits //This covers adult and reverse access only, as child going forwards arrives here from the other side of this door Entrance(RR_SPIRIT_TEMPLE_MQ_WEST_1F_RUSTED_SWITCH, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 7);}), - Entrance(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->CanHitSwitch()/* && CanClimbHigh()*/;}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, []{return logic->CanHitSwitch()/* && CanClimbHigh()*/;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM] = Region("Spirit Temple MQ Broken Wall Room", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR] = Region("Spirit Temple MQ Broken Wall Room", SCENE_SPIRIT_TEMPLE, {}, { //Locations //Implies CanKillEnemy(RE_LIKE_LIKE) - LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_NORTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->CanKillEnemy(RE_BEAMOS);})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_NORTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, []{return logic->CanKillEnemy(RE_BEAMOS);})), //Sunlights only temp spawn this chest, which is unintuitive/a bug. - LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_SOUTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return (logic->HasExplosives() || logic->SunlightArrows()) && logic->CanUse(RG_HOOKSHOT);})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_SOUTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, []{return (logic->HasExplosives() || logic->SunlightArrows()) && logic->CanUse(RG_HOOKSHOT);})), }, { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, []{return logic->CanHitSwitch();}), @@ -693,19 +693,20 @@ void RegionTable_Init_SpiritTemple() { }, { //Exits //!QUANTUM LOGIC! - //If we entered in reverse and dungeon entrances are off, we only need 6 keys, access to Gauntlets Hand and the ability to crawl to reach colossus + //If we entered in reverse and dungeon entrances are off, we only need 6 keys, access to Gauntlets Hand, and the ability to + // crawl through the boulder filled tunnel to reach colossus. //This is because with 6 keys it becomes impossible to avoid opening either the west hand lock or the first child side lock //and either direction lets child reach colossus. CanHitSwitch and CanKillEnemy(RE_IRON_KNUCKLE) is implied. //Logic can then allow child back into spirit, putting 1F west in logic with only 6 keys without forwards entry Entrance(RR_DESERT_COLOSSUS, []{return logic->IsChild/*CanUse(RG_CRAWL)*/ && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && - logic->ReverseSpiritChild && logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->MQSpiritStatueToSunBlock() && + logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->MQSpiritStatueToSunBlock() && (logic->CanUse(RG_BOMBCHU_5) || (ctx->GetTrickOption(RT_RUSTED_SWITCHES) && Here(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return logic->CanUse(RG_MEGATON_HAMMER);})));}), //!QUANTUM LOGIC! - //If we have 6 keys and Child reverse spirit entry, we can gurantee broken wall room access for Child + //If we have 6 keys and Child reverse spirit entry, we can guarantee broken wall room access for Child //as long as we can hit a switch and climb because Adult cannot reach the initial child lock without //first opening the Statue Room to Broken Wall Room lock. The details of this are handled in SpiritShared. //if adult can ever cross crawlspaces this becomes more complicated. - Entrance(RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6);}), + Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6);}), Entrance(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME);}), Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, []{return logic->IsAdult || logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS)/* || CanBunnyJump()*/;}), //(IsAdult || ctx->GetTrickOption(RT_SPIRIT_MQ_SUN_BLOCK_SOT) || CanUse(RG_SONG_OF_TIME) || CanBunnyJump()) && str0 @@ -720,10 +721,10 @@ void RegionTable_Init_SpiritTemple() { EventAccess(&logic->SpiritStatueRoomSouthDoor, []{return ctx->GetTrickOption(RT_SPIRIT_MQ_FROZEN_EYE) && logic->CanUse(RG_FAIRY_BOW) && logic->CanUse(RG_SONG_OF_TIME);}), }, { //Locations - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_EAST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE , []{return logic->CanBreakPots();}, false, - RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND , []{return logic->CanUse(RG_BOOMERANG);})), - LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_WEST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE , []{return logic->CanBreakPots();}, false, - RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND , []{return ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE) && logic->CanUse(RG_BOOMERANG);})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_EAST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->CanBreakPots();}, false, + RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, []{return logic->CanUse(RG_BOOMERANG);})), + LOCATION(RC_SPIRIT_TEMPLE_MQ_STATUE_3F_WEST_POT, SpiritShared(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->CanBreakPots();}, false, + RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, []{return ctx->GetTrickOption(RT_SPIRIT_WEST_LEDGE) && logic->CanUse(RG_BOOMERANG);})), }, { //Exits //This is pretty tight to reach the SoT block, but you can just go via the hand... @@ -1125,6 +1126,7 @@ void RegionTable_Init_SpiritTemple() { //Events //WARNING these events are not glitchproofed and assume you need all keys to reach from the front EventAccess(&logic->ReverseSpiritChild, []{return logic->IsChild;}), + EventAccess(&logic->ReverseSpiritAdult, []{return logic->IsAdult;}), }, {}, { // Exits Entrance(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, []{return true;}), diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 80371cbf2..e5d398028 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2365,12 +2365,12 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) { bool Logic::IsReverseAccessPossible() { //If we ever allow dungeon entrances to connect to boss rooms directly in dungeon chains, or for 1 boss door to lead to another dungeons boss door, add RSK_MIX_DUNGEON_ENTRANCES to the final condition - return ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES) && + return !ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES).Is(RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) && (ctx->GetOption(RSK_DECOUPLED_ENTRANCES) || (ctx->GetOption(RSK_MIX_BOSS_ENTRANCES) && (ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES) || ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES)))); } -bool Logic::SpiritBrokenWallToStatue() { +bool Logic::SpiritSunOnFloorToStatue() { return /*CanClimbHigh() &&*/ (HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS))); } @@ -2630,6 +2630,7 @@ void Logic::Reset(bool resetSaveContext /*= true*/) { Spirit4FSwitch = false; SpiritPushed4FMirrors = false; ReverseSpiritChild = false; + ReverseSpiritAdult = false; CalculatingAvailableChecks = false; diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 4e230a233..8b62d8274 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -187,6 +187,9 @@ class Logic { bool Spirit4FSwitch = false; bool SpiritPushed4FMirrors = false; bool ReverseSpiritChild = false; + bool ReverseSpiritAdult = false; + bool ForwardsSpiritChild = false; + bool ForwardsSpiritAdult = false; /* --- END OF HELPERS AND LOCATION ACCESS --- */ @@ -300,7 +303,7 @@ class Logic { static std::map RandoGetToEquipFlag; static std::map RandoGetToRandInf; bool IsReverseAccessPossible(); - bool SpiritBrokenWallToStatue(); + bool SpiritSunOnFloorToStatue(); bool SpiritEastToSwitch(); bool SpiritWestToSkull(); bool SpiritSunBlockSouthLedge(); diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index bdc47519e..772ef3437 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -869,7 +869,7 @@ typedef enum { RR_SPIRIT_TEMPLE_RUPEE_BRIDGE_NORTH, RR_SPIRIT_TEMPLE_1F_BOXES, RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, - RR_SPIRIT_TEMPLE_BROKEN_WALL, + RR_SPIRIT_TEMPLE_SUN_ON_FLOOR, RR_SPIRIT_TEMPLE_1F_EAST, RR_SPIRIT_TEMPLE_SAND_PIT, RR_SPIRIT_TEMPLE_BOULDERS, @@ -918,7 +918,7 @@ typedef enum { RR_SPIRIT_TEMPLE_MQ_MAP_ROOM_SOUTH, RR_SPIRIT_TEMPLE_MQ_WEST_1F_RUSTED_SWITCH, RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, - RR_SPIRIT_TEMPLE_MQ_BROKEN_WALL_ROOM, + RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index f9b29d553..0f58e27b4 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -379,9 +379,12 @@ void Settings::CreateOptions() { "Allows the following possible without Tunics:\n- Enter Water Temple. The area below the center pillar " "still requires Zora Tunic. Applies to MQ also.\n- Enter Fire Temple. Volvagia still requires Goron " "Tunic. Applies to MQ also, and includes child access to first floor with dungeon shuffle."); - OPT_TRICK(RT_RUSTED_SWITCHES, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, - "Hammer Rusted Switches Through Walls", - "Applies to:\n- Fire Temple Highest Goron Chest.\n- MQ Fire Temple Lizalfos Maze.\n- MQ Spirit Trial."); + OPT_TRICK(RT_RUSTED_SWITCHES, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE },"Hammer Through Collision", + "Applies to:\n" + "- Hitting Fire Temple Highest Goron Chest's Rusted Switch in the SoT Block without Song of Time.\n" + "- Hitting MQ Fire Temple Lizalfos Maze's Rusted Switch in the wall.\n" + "- Having Adult hammer the rock in the west side crawlspace of MQ Spirit so child can get through without bombchus." + "- MQ Spirit Trial's Rusted Switch between the thrones without hitting the eye target to drop an Iron Knuckle.\n"); OPT_TRICK(RT_FLAMING_CHESTS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::INTERMEDIATE }, "Flaming Chests", "The chests encircled in flames in Gerudo Training Ground and in Spirit Temple can be opened by running " "into the flames while Link is invincible after taking damage."); From 076b138a80201647baa7e50125ad3437182e7f76 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 29 Jun 2025 14:37:34 +0100 Subject: [PATCH 24/29] finish implementation and cleanup --- .../randomizer/location_access.cpp | 156 ++++++++---------- .../Enhancements/randomizer/location_access.h | 2 +- .../dungeons/spirit_temple.cpp | 43 ++--- soh/soh/Enhancements/randomizer/logic.cpp | 75 +++++++-- soh/soh/Enhancements/randomizer/logic.h | 7 +- soh/soh/Enhancements/randomizer/settings.cpp | 8 +- 6 files changed, 153 insertions(+), 138 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index abf32e49b..f6f87efea 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -573,14 +573,15 @@ bool Here(const RandomizerRegion region, ConditionFn condition) { * Additionally, if it is possible to enter spirit in reverse, there are 2 more universes: * In the third universe, adult enters in reverse, and wastes all the keys so noone can enter through the front * In the forth, child manages to do the same, and lock people out of the front - * All access from the boss door in shared areas is Certain + * However all access from the boss door in Statue Room and adjacent areas is Certain, so this is not usually + relevant * While other universes exist, such as both ages entering in reverse or child using their key, getting stuck, then coming back to do the dungeon as adult, these are all sub-possibilities of these 4 universes * As we do not know which universe we are in until the player chooses one in-game, - we must be able to collect the check in both universes + we must be able to collect the check in all universes * When an Age can no longer be kept out by conflicting universes, that age is said to have Certain Access to a region @@ -592,61 +593,46 @@ bool Here(const RandomizerRegion region, ConditionFn condition) { * We must check for these universes manually as we allow technical access with minimum keys for * technical reasons as otherwise the logic code will never run - * The first and 3rd column list how many keys are needed for each age to have Certain Access - * the second column is child keys in case there's Child reverse access, due to an edge case in MQ spirit logic - * where the broken wall room can be reached with 6 keys if you can hit switches and have reverse Child access + * The 1st and 3rd column list how many keys are needed for each age to have Certain Access from the front + * the 2nd and 4th column list how many keys are needed for each age to have Certain Access from the boss door + * Sometimes, we may check for a higher number of keys in the condition, this happens in cases where the number of + keys + * for Certain Access depends on a certain condition, the listed number is the lowest possible to make sure the + condition is checked. - * The first condition is the combined conditions needed to move from the 1F child lock to the area being checks - * the second condition is the same for adult 1F lock, and the third is the access from the boss door. + * The 1st condition is the combined conditions needed to move from the 1F child lock to the area being checks + * the 2nd condition is the same for adult 1F lock, and the 3rd is the access from the boss door. */ -bool SpiritExplosiveKeyLogic() { - return logic->SmallKeys(RR_SPIRIT_TEMPLE, logic->HasExplosives() ? 1 - : ctx->GetOption(RSK_BOMBCHU_BAG) && logic->BombchuRefill() ? 2 - : 3); -} - -//!QUANTUM LOGIC! -//With 3 keys, you cannot lock adult out of leaving spirit onto the hands and jumping down, as you would have to -//open the west hand door and then adult could climb through sun block room to jump down from there -//This requires that adult can complete both routes -//If we have the longshot, we can also guarantee access to the outer west hand as you can longshot from the east hand to the west -//Implies CanKillEnemy(RE_IRON_KNUCKLE) -bool OuterWestHandLogic(){ - return logic->HasExplosives()/* && logic->CanClimbHigh() && str0*/ && logic->SmallKeys(RR_SPIRIT_TEMPLE, logic->HasItem(RG_LONGSHOT) ? 3 : 5); -} - // clang-format off std::map Region::spiritLogicData = { //Vanilla Child uses ExplosiveKeyLogic here because they need to exist for shared adult checks - {RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, {5, 0, 3, 0, []{return true;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, - {RR_SPIRIT_TEMPLE_SUN_ON_FLOOR, {5, 0, 3, 0, []{return true/*logic->CanClimbHigh()*/;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, - {RR_SPIRIT_TEMPLE_2F_MIRROR, {5, 0, 3, 0, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritSunOnFloorToStatue();}, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, - {RR_SPIRIT_TEMPLE_INNER_WEST_HAND, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, - {RR_SPIRIT_TEMPLE_GS_LEDGE, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh()*/;}, []{return SpiritExplosiveKeyLogic() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic();}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true;}}}, + {RR_SPIRIT_TEMPLE_WEST_CLIMB_BASE, {5, 0, 3, 0, []{return true;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_SUN_ON_FLOOR, {5, 0, 3, 0, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_2F_MIRROR, {5, 0, 3, 0, []{return logic->CanUse(RG_HOOKSHOT) && logic->SpiritSunOnFloorToStatue();}, []{return true/*logic->CanClimbHigh()*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_WEST, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_INNER_WEST_HAND, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh()*/;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_GS_LEDGE, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh()*/;}, []{return logic->SpiritExplosiveKeyLogic() && logic->SpiritWestToSkull()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritWestToSkull()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic();}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true;}}}, //Assumes SpiritSunBlockSouthLedge() for all access - {RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, - {RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, - {RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, {5, 5, 3, 3, []{return OuterWestHandLogic();}, []{return OuterWestHandLogic();}, []{return OuterWestHandLogic();}}}, - {RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic() && logic->CanUse(RG_HOOKSHOT)/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, - {RR_SPIRIT_TEMPLE_INNER_EAST_HAND, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic() && logic->CanUse(RG_HOOKSHOT)/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, - {RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, {5, 0, 3, 0, []{return SpiritExplosiveKeyLogic() && logic->CanUse(RG_HOOKSHOT) && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));}}}, - //MQ /*&& logic->CanClimbHigh()*/ - {RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, {7, 9, 7, 7, []{return true;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, - {RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, {7, 9, 7, 7, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, - {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, {7, 0, 0, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->Climb*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, - {RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, {7, 0, 0, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;}}}, - {RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, {7, 0, 0, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;}}}, - {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, {7, 0, 0, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true;}, []{return true;}}}, - {RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, {7, 0, 0, 0, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && logic->Climb*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, - //RANDOTODO FIX - {RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND, {7, 7, 4, 4, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock() //For the purpose of shared, adult needs to get to west side via BOTH possible routes for it to count //Only using HasItem here for adult items so child can pass this check - /* && logic->CanClimbHigh() && str0*/;}, []{return logic->MQSpirit4KeyWestHand();}, []{return logic->CouldMQSpirit4KeyWestHand();}}}, + {RR_SPIRIT_TEMPLE_SUN_BLOCK_SOUTH_LEDGE, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, + {RR_SPIRIT_TEMPLE_SKULLTULA_STAIRS, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritExplosiveKeyLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return true/*((logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)) && str0) || (logic->CanKillEnemy(RE_BEAMOS) && logic->CanUse(RG_LONGSHOT))*/;}}}, + {RR_SPIRIT_TEMPLE_OUTER_WEST_HAND, {5, 5, 3, 3, []{return logic->OuterWestHandLogic();}, []{return logic->OuterWestHandLogic();}, []{return logic->OuterWestHandLogic();}}}, + {RR_SPIRIT_TEMPLE_STATUE_ROOM_EAST, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic() && logic->CanUse(RG_HOOKSHOT)/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, + {RR_SPIRIT_TEMPLE_INNER_EAST_HAND, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic() && logic->CanUse(RG_HOOKSHOT)/* && logic->CanClimbHigh()*/;}, []{return true/*logic->CanClimbHigh() && str0*/;}, []{return logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS);}}}, + {RR_SPIRIT_TEMPLE_SHORTCUT_SWITCH, {5, 0, 3, 0, []{return logic->SpiritExplosiveKeyLogic() && logic->CanUse(RG_HOOKSHOT) && logic->SpiritEastToSwitch();}, []{return logic->SpiritEastToSwitch()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->SpiritEastToSwitch() && (logic->CanUse(RG_HOOKSHOT) || logic->CanUse(RG_HOVER_BOOTS));}}}, + //MQ /*&& logic->CanClimbHigh()*/ + {RR_SPIRIT_TEMPLE_MQ_UNDER_LIKE_LIKE, {7, 6, 7, 7, []{return logic->StatueRoomMQKeyLogic();}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->CanHitSwitch()/* && logic->Climb*/;}, []{return logic->StatueRoomMQKeyLogic() && logic->CanHitSwitch()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, {7, 6, 7, 7, []{return logic->StatueRoomMQKeyLogic() && logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6)/* && logic->Climb*/;}, []{return logic->StatueRoomMQKeyLogic()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, {7, 0, 0, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true/*logic->Climb*/;}, []{return true/*logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS)*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, {7, 0, 0, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, {7, 0, 0, 0, []{return logic->CanHitSwitch() && logic->MQSpiritWestToPots()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritWestToPots()/* && logic->Climb*/;}, []{return /*logic->CanUse(RG_HOVER_BOOTS) || (logic->CanClimb() && */logic->MQSpiritWestToPots()/*)*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM, {7, 0, 0, 0, []{return logic->CanHitSwitch()/* && logic->CanClimbHigh()*/;}, []{return true;}, []{return true;}}}, + {RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, {7, 0, 0, 0, []{return logic->CanHitSwitch() && logic->MQSpiritStatueToSunBlock()/* && logic->CanClimbHigh()*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && logic->Climb*/;}, []{return logic->MQSpiritStatueToSunBlock()/* && (logic->CanClimb() || logic->CanUse(RG_HOVER_BOOTS))*/;}}}, + {RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND, {7, 7, 4, 4, []{return logic->CanHitSwitch() && logic->OuterWestHandMQLogic()/* && logic->CanClimbHigh() && str0*/;}, []{return logic->OuterWestHandMQLogic();}, []{return logic->OuterWestHandMQLogic();}}}, {RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH, {7, 0, 0, 0, []{return logic->CanHitSwitch() && areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();}) - /* && logic->CanClimbHigh()*/;}, []{return true;}, []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();});}}}, + /* && logic->CanClimbHigh()*/;}, []{return true;}, []{return areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_BLOCK_ROOM_NORTH].Here([]{return logic->MQSpiritStatueSouthDoor();});}}}, }; // clang-format on @@ -655,33 +641,25 @@ bool SpiritCertainAccess(RandomizerRegion region) { if (logic->IsChild) { uint8_t keys = curRegionData.childKeys; uint8_t revKeys = curRegionData.childRevKeys; - bool knownFrontAccess = logic->ForwardsSpiritChild || !logic->IsReverseAccessPossible(); - // revKeys set to 9 means it's the sun on floor room in MQ - // If child enters in reverse, then they have access to Certain Access to Broken Wall room in 6 keys, - // the ability to hit switches and the ability to climb because only child can reach the initial child lock - // without opening the Statue room to Broken Wall Room lock first - if (revKeys == 9){ - revKeys = (logic->ReverseSpiritChild && logic->CanHitSwitch() /* && CanClimbHigh()*/) ? 6 : 7; - } - + bool knownFrontAccess = logic->ForwardsSpiritChild || !logic->IsAdultReverseAccessPossible(); // If we have enough keys that an age cannot be kept out, we have Certain Access // otherwise if we have entered in reverse and can reach from the face, we have Certain Access - return ((knownFrontAccess && curRegionData.childAccess()) && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys)) || - ((logic->ReverseSpiritChild && curRegionData.reverseAccess()) && logic->SmallKeys(RR_SPIRIT_TEMPLE, revKeys)) || - (curRegionData.childAccess() && curRegionData.reverseAccess() && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys > revKeys ? keys : revKeys)); + return ((knownFrontAccess && curRegionData.childAccess()) && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys)) || + ((logic->ReverseSpiritChild && curRegionData.reverseAccess()) && + logic->SmallKeys(RR_SPIRIT_TEMPLE, revKeys)) || + (curRegionData.childAccess() && curRegionData.reverseAccess() && + logic->SmallKeys(RR_SPIRIT_TEMPLE, keys > revKeys ? keys : revKeys)); } else { uint8_t keys = curRegionData.adultKeys; uint8_t revKeys = curRegionData.adultRevKeys; - bool knownFrontAccess = logic->ForwardsSpiritAdult || !logic->IsReverseAccessPossible(); - auto test = logic->IsReverseAccessPossible(); - auto test2 = (knownFrontAccess && curRegionData.adultAccess()); - auto test3 = (logic->ReverseSpiritAdult && curRegionData.reverseAccess()); - auto test4 = curRegionData.adultAccess() && curRegionData.reverseAccess(); + bool knownFrontAccess = logic->ForwardsSpiritAdult || !logic->IsAdultReverseAccessPossible(); // If we have enough keys that an age cannot be kept out, we have Certain Access // otherwise if we have entered in reverse and can reach from the face, we have Certain Access - return ((knownFrontAccess && curRegionData.adultAccess()) && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys)) || - ((logic->ReverseSpiritAdult && curRegionData.reverseAccess()) && logic->SmallKeys(RR_SPIRIT_TEMPLE, revKeys)) || - (curRegionData.adultAccess() && curRegionData.reverseAccess() && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys > revKeys ? keys : revKeys)); + return ((knownFrontAccess && curRegionData.adultAccess()) && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys)) || + ((logic->ReverseSpiritAdult && curRegionData.reverseAccess()) && + logic->SmallKeys(RR_SPIRIT_TEMPLE, revKeys)) || + (curRegionData.adultAccess() && curRegionData.reverseAccess() && + logic->SmallKeys(RR_SPIRIT_TEMPLE, keys > revKeys ? keys : revKeys)); } } @@ -738,16 +716,17 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, R // If Adult can get there and get the check, we can get the check in logic // If reverse spirit is also possible, we need to make sure Adult can get it via reverse entry too - result = (curRegionData.adultAccess() && - (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) || - (otherRegion != RR_NONE && - (Region::spiritLogicData[otherRegion].adultAccess() && - (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && - otherCondition())) || - (thirdRegion != RR_NONE && - (Region::spiritLogicData[thirdRegion].adultAccess() && - (!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && - thirdCondition())); + result = + (curRegionData.adultAccess() && + (!logic->IsAdultReverseAccessPossible() || curRegionData.reverseAccess) && condition()) || + (otherRegion != RR_NONE && + (Region::spiritLogicData[otherRegion].adultAccess() && + (!logic->IsAdultReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && + otherCondition())) || + (thirdRegion != RR_NONE && + (Region::spiritLogicData[thirdRegion].adultAccess() && + (!logic->IsAdultReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && + thirdCondition())); } } else if (areaTable[region].Adult() && pastAdult) { result = condition(); @@ -762,16 +741,17 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, R // If Child can get there and get the check, we can get the check in logic // If reverse spirit is also possible, we need to make sure Child can get it via reverse entry too - result = (curRegionData.childAccess() && - (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess()) && condition()) || - (otherRegion != RR_NONE && - (Region::spiritLogicData[otherRegion].childAccess() && - (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && - otherCondition())) || - (thirdRegion != RR_NONE && - (Region::spiritLogicData[thirdRegion].childAccess() && - (!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && - thirdCondition())); + result = + (curRegionData.childAccess() && + (!logic->IsAdultReverseAccessPossible() || curRegionData.reverseAccess()) && condition()) || + (otherRegion != RR_NONE && + (Region::spiritLogicData[otherRegion].childAccess() && + (!logic->IsAdultReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && + otherCondition())) || + (thirdRegion != RR_NONE && + (Region::spiritLogicData[thirdRegion].childAccess() && + (!logic->IsAdultReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && + thirdCondition())); } } // set back age variables diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 0e864c228..3fc597b99 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -123,7 +123,7 @@ struct SpiritLogicData { uint8_t childRevKeys; // the number of keys that guarantees Adult can reach this region // if it is 9, that means the bombchu edge case is to be checked. - uint8_t adultKeys; + uint8_t adultKeys; // the number of keys that guarantees Adult can reach this region with reverse entry uint8_t adultRevKeys; // The area access condition to reach this region as Child, from the first lock, diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp index e870e78d6..3909b8cca 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -153,7 +153,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_ABOVE_BOULDERS] = Region("Spirit Temple Above Boulders", SCENE_SPIRIT_TEMPLE, { //Events - //Jump slash is possible as child, but pretty tight. Jumpslash as late as you can + //Jumpslash is possible as child, but pretty tight. Jumpslash as late as you can //A damage boost off the boulder is also possible, but you need to land on the middle of the boulder //to get enough distance to reach the rupee EventAccess(&logic->SpiritBouldersSilvers, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanJumpslash() || logic->CanUse(RG_LONGSHOT)/* || CanBunnyHop()*/;}), @@ -394,7 +394,6 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_BEAMOS_PITS] = Region("Spirit Temple Beamos Pits", SCENE_SPIRIT_TEMPLE, {}, {}, { //Exits - //Implies killing the anubis with the fire ring, doing so itemless requires voiding out, which can lock hardcore + OHKO seeds Entrance(RR_SPIRIT_TEMPLE_POT_STAIRS, []{return logic->CanKillEnemy(RE_BEAMOS);}), Entrance(RR_SPIRIT_TEMPLE_FOUR_ARMOS, []{return logic->CanKillEnemy(RE_BEAMOS);}), Entrance(RR_SPIRIT_TEMPLE_BIG_WALL_BASE, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), @@ -674,7 +673,7 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, []{return logic->CanHitSwitch()/* && CanClimbHigh()*/;}), }); - areaTable[RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR] = Region("Spirit Temple MQ Broken Wall Room", SCENE_SPIRIT_TEMPLE, {}, { + areaTable[RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR] = Region("Spirit Temple MQ Sun on Floor Room", SCENE_SPIRIT_TEMPLE, {}, { //Locations //Implies CanKillEnemy(RE_LIKE_LIKE) LOCATION(RC_SPIRIT_TEMPLE_MQ_CHILD_CLIMB_NORTH_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, []{return logic->CanKillEnemy(RE_BEAMOS);})), @@ -694,18 +693,13 @@ void RegionTable_Init_SpiritTemple() { //Exits //!QUANTUM LOGIC! //If we entered in reverse and dungeon entrances are off, we only need 6 keys, access to Gauntlets Hand, and the ability to - // crawl through the boulder filled tunnel to reach colossus. + //crawl through the boulder filled tunnel to reach colossus. //This is because with 6 keys it becomes impossible to avoid opening either the west hand lock or the first child side lock //and either direction lets child reach colossus. CanHitSwitch and CanKillEnemy(RE_IRON_KNUCKLE) is implied. //Logic can then allow child back into spirit, putting 1F west in logic with only 6 keys without forwards entry Entrance(RR_DESERT_COLOSSUS, []{return logic->IsChild/*CanUse(RG_CRAWL)*/ && ctx->GetOption(RSK_SHUFFLE_DUNGEON_ENTRANCES).Is(RO_DUNGEON_ENTRANCE_SHUFFLE_OFF) && logic->SmallKeys(RR_SPIRIT_TEMPLE, 6) && logic->MQSpiritStatueToSunBlock() && (logic->CanUse(RG_BOMBCHU_5) || (ctx->GetTrickOption(RT_RUSTED_SWITCHES) && Here(RR_SPIRIT_TEMPLE_MQ_STATUE_ROOM_WEST, []{return logic->CanUse(RG_MEGATON_HAMMER);})));}), - //!QUANTUM LOGIC! - //If we have 6 keys and Child reverse spirit entry, we can guarantee broken wall room access for Child - //as long as we can hit a switch and climb because Adult cannot reach the initial child lock without - //first opening the Statue Room to Broken Wall Room lock. The details of this are handled in SpiritShared. - //if adult can ever cross crawlspaces this becomes more complicated. Entrance(RR_SPIRIT_TEMPLE_MQ_SUN_ON_FLOOR, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 6);}), Entrance(RR_SPIRIT_TEMPLE_MQ_POT_LEDGE, []{return logic->CanUse(RG_HOVER_BOOTS) || logic->CanUse(RG_SONG_OF_TIME);}), Entrance(RR_SPIRIT_TEMPLE_MQ_INNER_WEST_HAND, []{return logic->IsAdult || logic->CanJumpslash() || logic->CanUse(RG_HOVER_BOOTS)/* || CanBunnyJump()*/;}), @@ -784,7 +778,6 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM] = Region("Spirit Temple MQ Sun Block Room", SCENE_SPIRIT_TEMPLE, {}, { //Locations - //We don't need Shared here because If we are checking as child, universe 2 adult access needs nothing so it always passes, and if we are checking as adult, it is Certain Access LOCATION(RC_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM_CHEST, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return true/*str0*/;})), //RT_SPIRIT_MQ_SUN_BLOCK_GS should probably be expanded to cover all ground based methods when str0 is added, as it can be hit with longshot because the skull hitbox is larger than the model LOCATION(RC_SPIRIT_TEMPLE_MQ_GS_SUN_BLOCK_ROOM, SpiritShared(RR_SPIRIT_TEMPLE_MQ_SUN_BLOCK_ROOM, []{return (logic->CanUse(RG_HOOKSHOT)/* && (str0 || SunlightArrows())*/) || @@ -851,25 +844,17 @@ void RegionTable_Init_SpiritTemple() { Entrance(RR_SPIRIT_TEMPLE_MQ_CHEST_LEDGE, []{return logic->CanUse(RG_HOVER_BOOTS) || ((ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->CanUse(RG_LONGSHOT));}), Entrance(RR_SPIRIT_TEMPLE_MQ_THREE_SUNS_ROOM_2F, []{return logic->MQSpiritStatueRoomTorches;}), - //!QUANTUM LOGIC! - //We only need 4 keys and the ability to reach both hands for adult to logically be able to drop down onto Desert Colossus - //This is because there are only 3 keys that can be wasted without opening up either this lock to East hand, or the West Hand lock through Sun Block Room - //and both directions allow you to drop onto colossus - //logic->CanKillEnemy(RE_FLOORMASTER) is implied - Entrance(RR_DESERT_COLOSSUS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && - logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && - logic->CanJumpslash() && /*(str0 || SunlightArrows) &&*/ + /* logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && + logic->CanJumpslash() && (str0 || SunlightArrows) && (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && logic->CanKillEnemy(RE_IRON_KNUCKLE) && - logic->CanUse(RG_HOOKSHOT);}), - //!QUANTUM LOGIC! - //Continuing from above, if we also have a longshot, we can go from the East hand to the West hand, meaning we always have access to East Hand - /* - logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && - logic->CanJumpslash() && (str0 || SunlightArrows) && - (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && - logic->CanKillEnemy(RE_IRON_KNUCKLE) && - logic->CanUse(RG_LONGSHOT) */ + logic->CanUse(RG_HOOKSHOT)*/ + Entrance(RR_DESERT_COLOSSUS, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && logic->MQSpirit4KeyColossus();}), + /* logic->CanAvoidEnemy(RE_BEAMOS, true, 4) && logic->CanUse(RG_SONG_OF_TIME) && + logic->CanJumpslash() && (str0 || SunlightArrows) && + (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || logic->CanUse(RG_LENS_OF_TRUTH)) && + logic->CanKillEnemy(RE_IRON_KNUCKLE) && + logic->CanUse(RG_LONGSHOT) */ Entrance(RR_SPIRIT_TEMPLE_MQ_OUTER_WEST_HAND, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 4) && logic->MQSpirit4KeyWestHand();}), Entrance(RR_SPIRIT_TEMPLE_MQ_FIRE_WALL_STAIRS_LOWER, []{return logic->SmallKeys(RR_SPIRIT_TEMPLE, 5);}), // RT_SPIRIT_PLATFORM_HOOKSHOT is currently disabled @@ -998,7 +983,7 @@ void RegionTable_Init_SpiritTemple() { //Exits Entrance(RR_SPIRIT_TEMPLE_MQ_BEAMOS_PITS, []{return true;}), Entrance(RR_SPIRIT_TEMPLE_MQ_FLOORMASTER_STAIRS, []{return logic->CanJumpslash();}), - Entrance(RR_SPIRIT_TEMPLE_MQ_3F_GIBDO_ROOM, []{return Here(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return (logic->IsAdult || logic->CanUse(RG_SONG_OF_TIME)) && logic->CanUse(RG_MIRROR_SHIELD);});}), + Entrance(RR_SPIRIT_TEMPLE_MQ_3F_GIBDO_ROOM, []{return Here(RR_SPIRIT_TEMPLE_MQ_SOT_SUN_ROOM, []{return ((logic->IsAdult || logic->CanUse(RG_SONG_OF_TIME)) && logic->CanUse(RG_MIRROR_SHIELD)) || logic->SunlightArrows();});}), }); areaTable[RR_SPIRIT_TEMPLE_MQ_FLOORMASTER_STAIRS] = Region("Spirit Temple MQ Floormaster Stairs", SCENE_SPIRIT_TEMPLE, {}, {}, { @@ -1049,7 +1034,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_MQ_BIG_WALL_UPPER] = Region("Spirit Temple MQ Big Wall Upper", SCENE_SPIRIT_TEMPLE, { //Events - //Getting some of these with just climbing downwards is theoretically possible but definitly a trick + //Getting some of these with just climbing downwards is theoretically possible but definitely a trick EventAccess(&logic->MQSpiritBigWallSilvers, []{return /*(*/logic->CanKillEnemy(RE_KEESE)/*|| CanUse(RG_SKULL_MASK)) && CanClimbHigh()*/;}), }, {}, { //Exits diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index e5d398028..0e3ab4cf3 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2363,20 +2363,25 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) { inLogic[logicVal] = value; } -bool Logic::IsReverseAccessPossible() { - //If we ever allow dungeon entrances to connect to boss rooms directly in dungeon chains, or for 1 boss door to lead to another dungeons boss door, add RSK_MIX_DUNGEON_ENTRANCES to the final condition +bool Logic::IsAdultReverseAccessPossible() { + // If we ever allow dungeon entrances to connect to boss rooms directly in dungeon chains, or for 1 boss door to + // lead to another dungeons boss door, add RSK_MIX_DUNGEON_ENTRANCES to the final condition + // RANDOTODO Check for Age-Locked Boss entrances + Ganon's tower when it is shuffled return !ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES).Is(RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) && - (ctx->GetOption(RSK_DECOUPLED_ENTRANCES) || - (ctx->GetOption(RSK_MIX_BOSS_ENTRANCES) && (ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES) || ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES)))); + ((ctx->GetOption(RSK_DECOUPLED_ENTRANCES) && + ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES).Is(RO_BOSS_ROOM_ENTRANCE_SHUFFLE_FULL)) || + (ctx->GetOption(RSK_MIX_BOSS_ENTRANCES) && + (ctx->GetOption(RSK_MIX_OVERWORLD_ENTRANCES) || ctx->GetOption(RSK_MIX_INTERIOR_ENTRANCES)))); } bool Logic::SpiritSunOnFloorToStatue() { return /*CanClimbHigh() &&*/ (HasExplosives() || (ctx->GetOption(RSK_SUNLIGHT_ARROWS) && CanUse(RG_LIGHT_ARROWS))); } -bool Logic::SpiritEastToSwitch() { - return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || CanUse(RG_HOVER_BOOTS) || - (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); +bool Logic::SpiritExplosiveKeyLogic() { + return SmallKeys(RR_SPIRIT_TEMPLE, HasExplosives() ? 1 + : ctx->GetOption(RSK_BOMBCHU_BAG) && BombchuRefill() ? 2 + : 3); } bool Logic::SpiritWestToSkull() { @@ -2393,6 +2398,12 @@ bool Logic::SpiritSunBlockSouthLedge() { ; } +bool Logic::SpiritEastToSwitch() { + return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || CanUse(RG_HOVER_BOOTS) || + (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); +} + + // Combines crossing the ledge directly and the jump from the hand bool Logic::MQSpiritWestToPots() { return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SONG_OF_TIME); @@ -2408,20 +2419,52 @@ bool Logic::MQSpiritStatueSouthDoor() { CanUse(RG_SONG_OF_TIME) /* && CanClimb()*/); } -bool Logic::MQSpirit4KeyWestHand() { +bool Logic::MQSpirit4KeyColossus() { + // !QUANTUM LOGIC! + // We only need 4 keys and the ability to reach both hands for adult to logically be able to drop down onto Desert Colossus + // This is because there are only 3 keys that can be wasted without opening up either this lock to East hand, or the West Hand lock through Sun Block Room + // and both directions allow you to drop onto colossus + // logic->CanKillEnemy(RE_FLOORMASTER) is implied return CanAvoidEnemy(RE_BEAMOS, true, 4) && CanUse(RG_SONG_OF_TIME) && CanJumpslash() && /*(str0 || SunlightArrows) &&*/ (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || CanUse(RG_LENS_OF_TRUTH)) && CanKillEnemy(RE_IRON_KNUCKLE) && - CanUse(RG_LONGSHOT); + CanUse(RG_HOOKSHOT); } -// This version of the function handles reaching there as child, based on what adult could do if they existed + +bool Logic::MQSpirit4KeyWestHand() { + // !QUANTUM LOGIC! + // Continuing from MQSpirit4KeyColossus, if we also have a longshot, we can go from the East hand to the West hand, meaning we always have access to East Hand + return CanUse(RG_LONGSHOT) && MQSpirit4KeyColossus(); +} +// This version of the function handles Shared Access for child, based on what adult could do if they existed bool Logic::CouldMQSpirit4KeyWestHand() { - return CanAvoidEnemy(RE_BEAMOS, true, 4) && CanUse(RG_SONG_OF_TIME) && HasItem(RG_MASTER_SWORD) || - HasItem(RG_BIGGORON_SWORD) || - HasItem(RG_MEGATON_HAMMER) && - /*(str0 || SunlightArrows) &&*/ - (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || CanUse(RG_LENS_OF_TRUTH)) && CanKillEnemy(RE_IRON_KNUCKLE) && - HasItem(RG_LONGSHOT); + return CanAvoidEnemy(RE_BEAMOS, true, 4) && CanUse(RG_SONG_OF_TIME) && + (HasItem(RG_MASTER_SWORD) || HasItem(RG_BIGGORON_SWORD) || HasItem(RG_MEGATON_HAMMER)) && + /*(str0 || SunlightArrows) &&*/ + (ctx->GetTrickOption(RT_LENS_SPIRIT_MQ) || CanUse(RG_LENS_OF_TRUTH)) && HasItem(RG_LONGSHOT); +} + +// !QUANTUM LOGIC! +// With 3 keys, you cannot lock adult out of leaving spirit onto the hands and jumping down, as you would have to +// open the west hand door and then adult could climb through sun block room to jump down from there +// This requires that adult can complete both routes +// If we have the longshot, we can also guarantee access to the outer west hand as you can longshot from the east hand +// to the west Implies CanKillEnemy(RE_IRON_KNUCKLE) +bool Logic::OuterWestHandLogic() { + return HasExplosives() /* && CanClimbHigh() && str0*/ && SmallKeys(RR_SPIRIT_TEMPLE, HasItem(RG_LONGSHOT) ? 3 : 5); +} + +bool Logic::OuterWestHandMQLogic() { + return MQSpiritStatueToSunBlock() && SmallKeys(RR_SPIRIT_TEMPLE, CouldMQSpirit4KeyWestHand() ? 4 : 7); +} + +bool Logic::StatueRoomMQKeyLogic() { + // !QUANTUM LOGIC! + // If child enters in reverse, then they have access to Certain Access to Broken Wall room in 6 keys, + // the ability to hit switches and the ability to climb because only child can reach the initial child lock + // without opening the Statue room to Broken Wall Room lock first + // if adult can ever cross crawlspaces this becomes more complicated. + return SmallKeys(RR_SPIRIT_TEMPLE, IsChild && ReverseSpiritChild && CanHitSwitch() /* && CanClimbHigh()*/ ? 6 : 7); } void Logic::Reset(bool resetSaveContext /*= true*/) { diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 8b62d8274..3dafa02ee 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -302,7 +302,7 @@ class Logic { static std::map RandoGetToDungeonScene; static std::map RandoGetToEquipFlag; static std::map RandoGetToRandInf; - bool IsReverseAccessPossible(); + bool IsAdultReverseAccessPossible(); bool SpiritSunOnFloorToStatue(); bool SpiritEastToSwitch(); bool SpiritWestToSkull(); @@ -310,8 +310,13 @@ class Logic { bool MQSpiritWestToPots(); bool MQSpiritStatueToSunBlock(); bool MQSpiritStatueSouthDoor(); + bool MQSpirit4KeyColossus(); bool MQSpirit4KeyWestHand(); bool CouldMQSpirit4KeyWestHand(); + bool OuterWestHandLogic(); + bool OuterWestHandMQLogic(); + bool SpiritExplosiveKeyLogic(); + bool StatueRoomMQKeyLogic(); private: std::shared_ptr ctx; diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index 0f58e27b4..676524506 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -379,12 +379,14 @@ void Settings::CreateOptions() { "Allows the following possible without Tunics:\n- Enter Water Temple. The area below the center pillar " "still requires Zora Tunic. Applies to MQ also.\n- Enter Fire Temple. Volvagia still requires Goron " "Tunic. Applies to MQ also, and includes child access to first floor with dungeon shuffle."); - OPT_TRICK(RT_RUSTED_SWITCHES, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE },"Hammer Through Collision", + OPT_TRICK(RT_RUSTED_SWITCHES, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE }, "Hammer Through Collision", "Applies to:\n" "- Hitting Fire Temple Highest Goron Chest's Rusted Switch in the SoT Block without Song of Time.\n" "- Hitting MQ Fire Temple Lizalfos Maze's Rusted Switch in the wall.\n" - "- Having Adult hammer the rock in the west side crawlspace of MQ Spirit so child can get through without bombchus." - "- MQ Spirit Trial's Rusted Switch between the thrones without hitting the eye target to drop an Iron Knuckle.\n"); + "- Having Adult hammer the rock in the west side crawlspace of MQ Spirit so child can get through " + "without bombchus." + "- MQ Spirit Trial's Rusted Switch between the thrones without hitting the eye target to drop an Iron " + "Knuckle.\n"); OPT_TRICK(RT_FLAMING_CHESTS, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::INTERMEDIATE }, "Flaming Chests", "The chests encircled in flames in Gerudo Training Ground and in Spirit Temple can be opened by running " "into the flames while Link is invincible after taking damage."); From 754de9a619d5122d16c292f4b3d18d2f534604b4 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 29 Jun 2025 14:40:27 +0100 Subject: [PATCH 25/29] submodules --- OTRExporter | 2 +- ZAPDTR | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OTRExporter b/OTRExporter index 461ab19a3..41052efcd 160000 --- a/OTRExporter +++ b/OTRExporter @@ -1 +1 @@ -Subproject commit 461ab19a36cde807591543397e136cae19aa6e7c +Subproject commit 41052efcdf8df8e67517cc93da8975fcd4e14af9 diff --git a/ZAPDTR b/ZAPDTR index 684f21a47..2aeababbf 160000 --- a/ZAPDTR +++ b/ZAPDTR @@ -1 +1 @@ -Subproject commit 684f21a475dcfeee89938ae1f4afc42768a3e7ef +Subproject commit 2aeababbfb81b00d34673406453e8e8e2deaa27b From e24ccdc6dd79418a3d7e2d5d1ea0757851948bf0 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 29 Jun 2025 14:41:54 +0100 Subject: [PATCH 26/29] more submodules --- OTRExporter | 2 +- ZAPDTR | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/OTRExporter b/OTRExporter index 41052efcd..461ab19a3 160000 --- a/OTRExporter +++ b/OTRExporter @@ -1 +1 @@ -Subproject commit 41052efcdf8df8e67517cc93da8975fcd4e14af9 +Subproject commit 461ab19a36cde807591543397e136cae19aa6e7c diff --git a/ZAPDTR b/ZAPDTR index 2aeababbf..684f21a47 160000 --- a/ZAPDTR +++ b/ZAPDTR @@ -1 +1 @@ -Subproject commit 2aeababbfb81b00d34673406453e8e8e2deaa27b +Subproject commit 684f21a475dcfeee89938ae1f4afc42768a3e7ef From d17bd0668352f832ff52b94817527414f25f74e9 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Sun, 29 Jun 2025 22:36:55 +0100 Subject: [PATCH 27/29] remove bombchu edge case --- soh/soh/Enhancements/randomizer/location_access.h | 12 +++++------- soh/soh/Enhancements/randomizer/logic.cpp | 14 ++++++-------- 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index eeb0c9e4a..4e4cf1215 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -116,15 +116,13 @@ enum class EntranceType; } // namespace Rando struct SpiritLogicData { - uint8_t childKeys; // the number of keys that guarantees Child can reach this region - // The number of keys that guarantees Child can reach this region if they have reverse access - // 9 means MQ broken wall room, as the first child lock can only be opened by Child - // without opening the lock to Statue room, guaranteeing access with 6 keys if you can hit a switch + // the minimum number of keys that guarantees Child can reach this region + uint8_t childKeys; + // The minimum number of keys that guarantees Child can reach this region if they have reverse access uint8_t childRevKeys; - // the number of keys that guarantees Adult can reach this region - // if it is 9, that means the bombchu edge case is to be checked. + // the minimum number of keys that guarantees Adult can reach this region uint8_t adultKeys; - // the number of keys that guarantees Adult can reach this region with reverse entry + // the minimum number of keys that guarantees Adult can reach this region with reverse entry uint8_t adultRevKeys; // The area access condition to reach this region as Child, from the first lock, // including the minimum number of keys for ambiguous access diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 36d9610f3..a8b8cb52a 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2386,9 +2386,7 @@ bool Logic::SpiritSunOnFloorToStatue() { } bool Logic::SpiritExplosiveKeyLogic() { - return SmallKeys(RR_SPIRIT_TEMPLE, HasExplosives() ? 1 - : ctx->GetOption(RSK_BOMBCHU_BAG) && BombchuRefill() ? 2 - : 3); + return SmallKeys(RR_SPIRIT_TEMPLE, HasExplosives() ? 1 : 2); } bool Logic::SpiritWestToSkull() { @@ -2410,7 +2408,6 @@ bool Logic::SpiritEastToSwitch() { (CanUse(RG_ZELDAS_LULLABY) && CanUse(RG_HOOKSHOT)); } - // Combines crossing the ledge directly and the jump from the hand bool Logic::MQSpiritWestToPots() { return (IsAdult && ctx->GetTrickOption(RT_SPIRIT_STATUE_JUMP)) || CanUse(RG_HOVER_BOOTS) || CanUse(RG_SONG_OF_TIME); @@ -2428,9 +2425,9 @@ bool Logic::MQSpiritStatueSouthDoor() { bool Logic::MQSpirit4KeyColossus() { // !QUANTUM LOGIC! - // We only need 4 keys and the ability to reach both hands for adult to logically be able to drop down onto Desert Colossus - // This is because there are only 3 keys that can be wasted without opening up either this lock to East hand, or the West Hand lock through Sun Block Room - // and both directions allow you to drop onto colossus + // We only need 4 keys and the ability to reach both hands for adult to logically be able to drop down onto Desert + // Colossus This is because there are only 3 keys that can be wasted without opening up either this lock to East + // hand, or the West Hand lock through Sun Block Room and both directions allow you to drop onto colossus // logic->CanKillEnemy(RE_FLOORMASTER) is implied return CanAvoidEnemy(RE_BEAMOS, true, 4) && CanUse(RG_SONG_OF_TIME) && CanJumpslash() && /*(str0 || SunlightArrows) &&*/ @@ -2440,7 +2437,8 @@ bool Logic::MQSpirit4KeyColossus() { bool Logic::MQSpirit4KeyWestHand() { // !QUANTUM LOGIC! - // Continuing from MQSpirit4KeyColossus, if we also have a longshot, we can go from the East hand to the West hand, meaning we always have access to East Hand + // Continuing from MQSpirit4KeyColossus, if we also have a longshot, we can go from the East hand to the West hand, + // meaning we always have access to East Hand return CanUse(RG_LONGSHOT) && MQSpirit4KeyColossus(); } // This version of the function handles Shared Access for child, based on what adult could do if they existed From 8d02e97184a940461ac8aa55d2b8a9fe4a7e5b66 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Mon, 30 Jun 2025 11:34:43 +0100 Subject: [PATCH 28/29] post merge fixes --- soh/soh/Enhancements/randomizer/location_access.h | 1 + 1 file changed, 1 insertion(+) diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 45e6c7600..61d41a9e0 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -239,6 +239,7 @@ class Region { void ResetVariables(); void printAgeTimeAccess(); + static std::map spiritLogicData; }; extern std::array areaTable; From 5f1948b5d782a3d0c0093b820857f4ba5f28de9f Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Thu, 7 Aug 2025 13:23:38 +0100 Subject: [PATCH 29/29] cleanup --- .../randomizer/location_access.cpp | 112 ++---------------- .../Enhancements/randomizer/location_access.h | 1 - soh/soh/Enhancements/randomizer/logic.cpp | 4 +- soh/soh/Enhancements/randomizer/logic.h | 2 +- 4 files changed, 14 insertions(+), 105 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access.cpp b/soh/soh/Enhancements/randomizer/location_access.cpp index 6097c87a1..aa3f270cc 100644 --- a/soh/soh/Enhancements/randomizer/location_access.cpp +++ b/soh/soh/Enhancements/randomizer/location_access.cpp @@ -563,96 +563,6 @@ void Region::ResetVariables() { } } -/* - * This logic covers checks that exist in the shared areas of MQ spirit from a glitchless standpoint. - * This room has Quantum logic that I am currently handling with this function, however this is NOT suitable for - glitch logic as it relies on specific ages - * In this chunk there are 3 possibilities for passing a check, but first I have to talk about parallel universes. - - * In MQ Spirit key logic, we mostly care about 2 possibilities for how the player can spend keys, creating 2 - Parralel universes - * In the first universe, the player did not enter spirit as adult until after climbing as child, thus child spends - keys linearly, only needing 2 to reach statue room. - * In the second universe, the player went in as adult, possibly out of logic, and started wasting the keys to lock - child out. - * These Universes converge when the player has 7 keys (meaning adult can no longer lock child out) and adult is - known to be able to reach Statue room. This creates "Certain Access", which is tracked seperatly for each age. - * Child Certain Access is simple, if we have 7 keys and child access, it's Certain Access. - * Adult Certain Access is also simple, adult is not key locked, so if they make it to a location, it's Certain - Access. - * Things get complicated when we handle the overlap of the 2 universes, - * though an important detail is that if we have Certain Access as either age, we don't need to checked the overlap - because overlap logic is strictly stricter than either Certain Access. - - * In order to track the first universe, the logic allows technical child access with the minimum number of keys, - and then checks in this function for if we have 7 keys to determine if that is Certain or not. - * This is for technical reasons, as areas with no access at all will simply not be checked. - * Normally we would need to do similar shenanigans to track the second universe, however adult must have go through - statue room to waste keys, - * so can go back there and get new keys for Child to use if they do, and the navigation logic for shared MQ spirit - from Statue Room is very simple for Adult. - * Additionally, we don't need to know if adult can actually reach spirit temple or climb to statue room, because if - the player can't do that, then universe 2 can't happen anyway, - * and if the player does so out of logic, they can do it again, as the only consumable used sets a permanent flag. - - * The Adult Navigation logic is as such: - * - Broken Wall room is 6 key locked, because if the player tries to spend 6 keys in a way that would block adults - access, they would have to give child access instead. - * - The child side hammer switch for the time travelling chest is 7 key locked for adult - * - Reaching gauntlets hand is 7 key locked - * - Going back into big block room is complex, but the only check there is child only so not a concern - * - Everything else is possible with basic adult movement, or is impossible for child to reach glitchlessly - * Anything 7 key locked does not need to be checked as shared, as all child access is Certain and because of this - workaround we don't need to fake Adult access, meaning that is also Certain. - * All of this combined means that when checking if adult can reach a location in universe 2, we only have to ask if - it is a 6 key locked location or not. - - * Knowing all of this this, we can confirm things are logical in 3 different ways: - * - If we have Adult Access, we know it is Certain Access, so they can get checks alone. - * - If we have 7 keys, child has Certain Access as we know they cannot be locked out, so can get checks alone, - otherwise we check the logical overlap - * - If Child and Adult can get the check (ignoring actual adult access to the location), and the location is either - not 6 key locked or we have 6 keys, we can get the check with the overlap - */ -bool Region::MQSpiritShared(ConditionFn condition, bool IsBrokenWall, bool anyAge) { - // if we have Certain Access as child, we can check anyAge and if true, resolve a condition with Here as if - // adult is here it's also Certain Access - if (logic->SmallKeys(RR_SPIRIT_TEMPLE, 7)) { - if (anyAge) { - return Here(condition); - } - return condition(); - // else, if we are here as adult, we have Certain Access from that and don't need special handling for - // checking adult - } else if (Adult() && logic->IsAdult) { - return condition(); - // if we do not have Certain Access, we need to check the overlap by seeing if we are both here as child and - // meet the adult universe's access condition We only need to do it as child, as only child access matters - // for this check, as adult access is assumed based on keys - } else if (Child() && logic->IsChild && (!IsBrokenWall || logic->SmallKeys(RR_SPIRIT_TEMPLE, 6))) { - bool result = false; - // store current age variables - bool pastAdult = logic->IsAdult; - bool pastChild = logic->IsChild; - - // First check if the check is possible as child - logic->IsChild = true; - logic->IsAdult = false; - result = condition(); - // If so, check again as adult. both have to be true for result to be true - if (result) { - logic->IsChild = false; - logic->IsAdult = true; - result = condition(); - } - - // set back age variables - logic->IsChild = pastChild; - logic->IsAdult = pastAdult; - return result; - } - return false; -} void Region::printAgeTimeAccess() { auto message = "Child Day: " + std::to_string(childDay) + @@ -699,14 +609,14 @@ bool Here(const RandomizerRegion region, ConditionFn condition) { * As we do not know which universe we are in until the player chooses one in-game, we must be able to collect the check in all universes - * When an Age can no longer be kept out by conflicting universes, that age is said to have Certain Access to a + * When an Age can no longer be kept out in any universe, that age is said to have Certain Access to a region - * If both ages have access to a region with a certain number of keys, but there is no Certain Access, + * If both ages have potential access to a region with a certain number of keys, but there is no Certain Access, * then a check is only in logic if all possible universes can collect the check independently * The universes converge when the player has all the keys, giving both ages Certain Access everywhere. - * We must check for these universes manually as we allow technical access with minimum keys for + * We must check for these universes manually as we set access vairables to true with minimum keys for * technical reasons as otherwise the logic code will never run * The 1st and 3rd column list how many keys are needed for each age to have Certain Access from the front @@ -757,7 +667,7 @@ bool SpiritCertainAccess(RandomizerRegion region) { if (logic->IsChild) { uint8_t keys = curRegionData.childKeys; uint8_t revKeys = curRegionData.childRevKeys; - bool knownFrontAccess = logic->ForwardsSpiritChild || !logic->IsAdultReverseAccessPossible(); + bool knownFrontAccess = logic->ForwardsSpiritChild || !logic->IsReverseAccessPossible(); // If we have enough keys that an age cannot be kept out, we have Certain Access // otherwise if we have entered in reverse and can reach from the face, we have Certain Access return ((knownFrontAccess && curRegionData.childAccess()) && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys)) || @@ -768,7 +678,7 @@ bool SpiritCertainAccess(RandomizerRegion region) { } else { uint8_t keys = curRegionData.adultKeys; uint8_t revKeys = curRegionData.adultRevKeys; - bool knownFrontAccess = logic->ForwardsSpiritAdult || !logic->IsAdultReverseAccessPossible(); + bool knownFrontAccess = logic->ForwardsSpiritAdult || !logic->IsReverseAccessPossible(); // If we have enough keys that an age cannot be kept out, we have Certain Access // otherwise if we have entered in reverse and can reach from the face, we have Certain Access return ((knownFrontAccess && curRegionData.adultAccess()) && logic->SmallKeys(RR_SPIRIT_TEMPLE, keys)) || @@ -834,14 +744,14 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, R // If reverse spirit is also possible, we need to make sure Adult can get it via reverse entry too result = (curRegionData.adultAccess() && - (!logic->IsAdultReverseAccessPossible() || curRegionData.reverseAccess) && condition()) || + (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess) && condition()) || (otherRegion != RR_NONE && (Region::spiritLogicData[otherRegion].adultAccess() && - (!logic->IsAdultReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && otherCondition())) || (thirdRegion != RR_NONE && (Region::spiritLogicData[thirdRegion].adultAccess() && - (!logic->IsAdultReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && thirdCondition())); } } else if (areaTable[region].Adult() && pastAdult) { @@ -859,14 +769,14 @@ bool SpiritShared(RandomizerRegion region, ConditionFn condition, bool anyAge, R // If reverse spirit is also possible, we need to make sure Child can get it via reverse entry too result = (curRegionData.childAccess() && - (!logic->IsAdultReverseAccessPossible() || curRegionData.reverseAccess()) && condition()) || + (!logic->IsReverseAccessPossible() || curRegionData.reverseAccess()) && condition()) || (otherRegion != RR_NONE && (Region::spiritLogicData[otherRegion].childAccess() && - (!logic->IsAdultReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[otherRegion].reverseAccess()) && otherCondition())) || (thirdRegion != RR_NONE && (Region::spiritLogicData[thirdRegion].childAccess() && - (!logic->IsAdultReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && + (!logic->IsReverseAccessPossible() || Region::spiritLogicData[thirdRegion].reverseAccess()) && thirdCondition())); } } diff --git a/soh/soh/Enhancements/randomizer/location_access.h b/soh/soh/Enhancements/randomizer/location_access.h index 61d41a9e0..8eac0b6df 100644 --- a/soh/soh/Enhancements/randomizer/location_access.h +++ b/soh/soh/Enhancements/randomizer/location_access.h @@ -234,7 +234,6 @@ class Region { bool CanPlantBeanCheck() const; bool AllAccountedFor() const; - bool MQSpiritShared(ConditionFn condition, bool IsBrokenWall, bool anyAge = false); void ResetVariables(); diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 7c730d801..96d59c490 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -2365,10 +2365,10 @@ void Logic::SetInLogic(LogicVal logicVal, bool value) { inLogic[logicVal] = value; } -bool Logic::IsAdultReverseAccessPossible() { +bool Logic::IsReverseAccessPossible() { // If we ever allow dungeon entrances to connect to boss rooms directly in dungeon chains, or for 1 boss door to // lead to another dungeons boss door, add RSK_MIX_DUNGEON_ENTRANCES to the final condition - // RANDOTODO Check for Age-Locked Boss entrances + Ganon's tower when it is shuffled + // RANDOTODO Check for Age-Locked Boss entrances + decoupled + Ganon's tower when it is shuffled return !ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES).Is(RO_BOSS_ROOM_ENTRANCE_SHUFFLE_OFF) && ((ctx->GetOption(RSK_DECOUPLED_ENTRANCES) && ctx->GetOption(RSK_SHUFFLE_BOSS_ENTRANCES).Is(RO_BOSS_ROOM_ENTRANCE_SHUFFLE_FULL)) || diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index b48522bfd..80ca1b5a5 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -308,7 +308,7 @@ class Logic { static std::map RandoGetToDungeonScene; static std::map RandoGetToEquipFlag; static std::map RandoGetToRandInf; - bool IsAdultReverseAccessPossible(); + bool IsReverseAccessPossible(); bool SpiritSunOnFloorToStatue(); bool SpiritEastToSwitch(); bool SpiritWestToSkull();