From 8cc02fd23565ceebce03f8bed7f9b8084efd85b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Philip=20Dub=C3=A9?= Date: Wed, 31 Dec 2025 02:54:41 +0000 Subject: [PATCH] Add missing extension trick to Shadow MQ, fix 2 logic mixups in GTG (#5983) Also allow Giant's Knife to logically hit these switches Ganon's Castle diamond switch can be hit with bomb by setting bomb down instead of shield dropping --- .../dungeons/ganons_castle.cpp | 2 +- .../dungeons/gerudo_training_ground.cpp | 2 +- .../dungeons/shadow_temple.cpp | 6 ++-- .../dungeons/spirit_temple.cpp | 2 +- .../location_access/overworld/goron_city.cpp | 7 ++-- soh/soh/Enhancements/randomizer/logic.cpp | 34 +++++++++++-------- .../Enhancements/randomizer/randomizerTypes.h | 1 + soh/soh/Enhancements/randomizer/settings.cpp | 3 +- 8 files changed, 34 insertions(+), 23 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp index 859172230..aa64b85de 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/ganons_castle.cpp @@ -214,7 +214,7 @@ void RegionTable_Init_GanonsCastle() { areaTable[RR_GANONS_CASTLE_SPIRIT_TRIAL_BEFORE_SWITCH] = Region("Ganon's Castle Spirit Trial Before Switch", SCENE_INSIDE_GANONS_CASTLE, {}, { //Locations - LOCATION(RC_GANONS_CASTLE_SPIRIT_TRIAL_CRYSTAL_SWITCH_CHEST, logic->CanJumpslashExceptHammer() || logic->HasExplosives()), + LOCATION(RC_GANONS_CASTLE_SPIRIT_TRIAL_CRYSTAL_SWITCH_CHEST, logic->CanJumpslash() || logic->HasExplosives() || logic->CanUse(RG_GIANTS_KNIFE)), }, { //Exits Entrance(RR_GANONS_CASTLE_SPIRIT_TRIAL_BEAMOS_ROOM, []{return true;}), diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp index d8d7cb64e..d70844bea 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/gerudo_training_ground.cpp @@ -272,7 +272,7 @@ void RegionTable_Init_GerudoTrainingGround() { //implies logic->CanKillEnemy(RE_SPIKE) LOCATION(RC_GERUDO_TRAINING_GROUND_MQ_HEAVY_BLOCK_CHEST, logic->CanKillEnemy(RE_FREEZARD)), }, { - Entrance(RR_GERUDO_TRAINING_GROUND_MQ_ROOM_BEHIND_BLOCK, []{return true;}), + Entrance(RR_GERUDO_TRAINING_GROUND_MQ_BEHIND_BLOCK, []{return true;}), }); areaTable[RR_GERUDO_TRAINING_GROUND_MQ_STATUE_ROOM_LEDGE] = Region("Gerudo Training Ground MQ Statue Room Ledge", SCENE_GERUDO_TRAINING_GROUND, {}, {}, { diff --git a/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp b/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp index ae24f1a78..02a79384e 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/shadow_temple.cpp @@ -514,7 +514,7 @@ void RegionTable_Init_ShadowTemple() { Entrance(RR_SHADOW_TEMPLE_MQ_B2_TO_B3_CORRIDOR_B3, []{return logic->CanUse(RG_LONGSHOT);}), Entrance(RR_SHADOW_TEMPLE_MQ_UPPER_HUGE_PIT, []{return logic->Get(LOGIC_SHADOW_MQ_PIT_STAIRS);}), Entrance(RR_SHADOW_TEMPLE_MQ_LOWER_HUGE_PIT_DOOR_LEDGE, []{return logic->CanUse(RG_HOVER_BOOTS) && (ctx->GetTrickOption(RT_LENS_SHADOW_MQ_PLATFORM) || logic->CanUse(RG_LENS_OF_TRUTH));}), - Entrance(RR_SHADOW_TEMPLE_MQ_STONE_UMBRELLA_ROOM, []{return Here(RR_SHADOW_TEMPLE_MQ_LOWER_HUGE_PIT, []{return logic->CanJumpslash() || logic->HasExplosives();});}), + Entrance(RR_SHADOW_TEMPLE_MQ_STONE_UMBRELLA_ROOM, []{return Here(RR_SHADOW_TEMPLE_MQ_LOWER_HUGE_PIT, []{return logic->CanJumpslash() || logic->HasExplosives() || logic->CanUse(RG_GIANTS_KNIFE) || (ctx->GetTrickOption(RT_HOOKSHOT_EXTENSION) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT)));});}), }); areaTable[RR_SHADOW_TEMPLE_MQ_LOWER_HUGE_PIT_DOOR_LEDGE] = Region("Shadow Temple MQ Upper Huge Pit Door Ledge", SCENE_SHADOW_TEMPLE, {}, {}, { @@ -669,8 +669,8 @@ void RegionTable_Init_ShadowTemple() { //Locations LOCATION(RC_SHADOW_TEMPLE_MQ_AFTER_CHASM_WEST_POT, logic->CanBreakPots()), LOCATION(RC_SHADOW_TEMPLE_MQ_AFTER_CHASM_EAST_POT, logic->CanBreakPots()), - LOCATION(RC_SHADOW_TEMPLE_MQ_AFTER_SHIP_UPPER_LEFT_HEART, logic->CanUse(RG_SONG_OF_TIME) && logic->CanHitEyeTargets() && logic->CanUse(RG_LONGSHOT)), - LOCATION(RC_SHADOW_TEMPLE_MQ_AFTER_SHIP_UPPER_RIGHT_HEART, logic->CanUse(RG_SONG_OF_TIME) && logic->CanHitEyeTargets() && logic->CanUse(RG_LONGSHOT)), + LOCATION(RC_SHADOW_TEMPLE_MQ_AFTER_SHIP_UPPER_LEFT_HEART, logic->Get(LOGIC_SHADOW_MQ_EYE_SWITCH_ACROSS_CHASM) && logic->CanUse(RG_LONGSHOT)), + LOCATION(RC_SHADOW_TEMPLE_MQ_AFTER_SHIP_UPPER_RIGHT_HEART, logic->Get(LOGIC_SHADOW_MQ_EYE_SWITCH_ACROSS_CHASM) && logic->CanUse(RG_LONGSHOT)), //There's invisible floor collision that makes aiming for the heart with rang harder than it should be, so it's a trick. LOCATION(RC_SHADOW_TEMPLE_MQ_AFTER_SHIP_LOWER_HEART, logic->IsAdult), }, { 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 2127ab254..6d2c9f4d0 100644 --- a/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/dungeons/spirit_temple.cpp @@ -133,7 +133,7 @@ void RegionTable_Init_SpiritTemple() { areaTable[RR_SPIRIT_TEMPLE_BEYOND_FINAL_LOCKED_DOOR] = Region("Spirit Temple Beyond Final Locked Door", SCENE_SPIRIT_TEMPLE, {}, { //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->CanJumpslash() || logic->HasExplosives() || (ctx->GetTrickOption(RT_HOOKSHOT_EXTENSION) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_HOOKSHOT))))) || + LOCATION(RC_SPIRIT_TEMPLE_TOPMOST_CHEST, (logic->CanUse(RG_MIRROR_SHIELD) && (logic->CanJumpslash() || logic->HasExplosives() || logic->CanUse(RG_GIANTS_KNIFE) || (ctx->GetTrickOption(RT_HOOKSHOT_EXTENSION) && (logic->CanUse(RG_FAIRY_BOW) || logic->CanUse(RG_FAIRY_SLINGSHOT) || logic->CanUse(RG_HOOKSHOT))))) || (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)), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/goron_city.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/goron_city.cpp index 7edb4392a..9641da2fa 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/goron_city.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/goron_city.cpp @@ -26,7 +26,6 @@ void RegionTable_Init_GoronCity() { LOCATION(RC_GC_ROLLING_GORON_AS_ADULT, logic->Get(LOGIC_GORON_CITY_STOP_ROLLING_GORON_AS_ADULT)), LOCATION(RC_GC_GS_BOULDER_MAZE, logic->IsChild && logic->BlastOrSmash()), LOCATION(RC_GC_GS_CENTER_PLATFORM, logic->IsAdult && logic->CanAttack()), - LOCATION(RC_GC_MEDIGORON, logic->IsAdult && (logic->CanBreakMudWalls() || logic->HasItem(RG_GORONS_BRACELET)) && GetCheckPrice() <= GetWalletCapacity()), LOCATION(RC_GC_MAZE_GOSSIP_STONE_FAIRY, (logic->BlastOrSmash() || logic->CanUse(RG_SILVER_GAUNTLETS)) && logic->CallGossipFairyExceptSuns()), LOCATION(RC_GC_MAZE_GOSSIP_STONE_FAIRY_BIG, (logic->BlastOrSmash() || logic->CanUse(RG_SILVER_GAUNTLETS)) && logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_GC_MAZE_GOSSIP_STONE, logic->BlastOrSmash() || logic->CanUse(RG_SILVER_GAUNTLETS)), @@ -47,8 +46,12 @@ void RegionTable_Init_GoronCity() { Entrance(RR_GC_GROTTO_PLATFORM, []{return logic->IsAdult && ((logic->CanUse(RG_SONG_OF_TIME) && ((logic->EffectiveHealth() > 2) || logic->CanUse(RG_GORON_TUNIC) || logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_NAYRUS_LOVE))) || (logic->EffectiveHealth() > 1 && logic->CanUse(RG_GORON_TUNIC) && logic->CanUse(RG_HOOKSHOT)) || (logic->CanUse(RG_NAYRUS_LOVE) && logic->CanUse(RG_HOOKSHOT)) || (logic->EffectiveHealth() > 2 && logic->CanUse(RG_HOOKSHOT) && ctx->GetTrickOption(RT_GC_GROTTO)));}), }); - areaTable[RR_GC_MEDIGORON] = Region("GC Medigoron", SCENE_GORON_CITY, {}, { + areaTable[RR_GC_MEDIGORON] = Region("GC Medigoron", SCENE_GORON_CITY, { + //Events + EventAccess(LOGIC_MEDIGORON, []{return logic->HasItem(RG_ADULT_WALLET) && GetCheckPrice(RC_GC_MEDIGORON) <= GetWalletCapacity();}), + }, { //Locations + LOCATION(RC_GC_MEDIGORON, logic->IsAdult && GetCheckPrice() <= GetWalletCapacity()), LOCATION(RC_GC_MEDIGORON_GOSSIP_STONE_FAIRY, logic->CallGossipFairyExceptSuns()), LOCATION(RC_GC_MEDIGORON_GOSSIP_STONE_FAIRY_BIG, logic->CanUse(RG_SONG_OF_STORMS)), LOCATION(RC_GC_MEDIGORON_GOSSIP_STONE, true), diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index 06f1e16ec..136acb3dc 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -78,10 +78,13 @@ bool Logic::HasItem(RandomizerGet itemName) { case RG_HYLIAN_SHIELD: case RG_MIRROR_SHIELD: case RG_MASTER_SWORD: - case RG_BIGGORON_SWORD: case RG_IRON_BOOTS: case RG_HOVER_BOOTS: return CheckEquipment(RandoGetToEquipFlag.at(itemName)); + case RG_GIANTS_KNIFE: + return CheckEquipment(RandoGetToEquipFlag.at(itemName)) || Get(LOGIC_MEDIGORON); + case RG_BIGGORON_SWORD: + return CheckEquipment(RandoGetToEquipFlag.at(itemName)) && mSaveContext->bgsFlag; case RG_GORONS_BRACELET: return CurrentUpgrade(UPG_STRENGTH); case RG_SILVER_GAUNTLETS: @@ -307,6 +310,7 @@ bool Logic::CanUse(RandomizerGet itemName) { return IsAdult; // || MirrorShieldAsChild; case RG_MASTER_SWORD: return IsAdult; // || MasterSwordAsChild; + case RG_GIANTS_KNIFE: case RG_BIGGORON_SWORD: return IsAdult; // || BiggoronSwordAsChild; case RG_SILVER_GAUNTLETS: @@ -1093,7 +1097,7 @@ bool Logic::CanJumpslash() { } bool Logic::CanClearStalagmite() { - return CanJumpslash() || HasExplosives() || + return CanJumpslash() || HasExplosives() || CanUse(RG_GIANTS_KNIFE) || (ctx->GetTrickOption(RT_ICE_STALAGMITE_HOOKSHOT) && CanUse(RG_HOOKSHOT)); } @@ -1102,7 +1106,7 @@ bool Logic::CanHitSwitch(EnemyDistance distance, bool inWater) { switch (distance) { case ED_CLOSE: case ED_SHORT_JUMPSLASH: - hit = CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MEGATON_HAMMER); + hit = CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MEGATON_HAMMER) || CanUse(RG_GIANTS_KNIFE); [[fallthrough]]; case ED_MASTER_SWORD_JUMPSLASH: hit = hit || CanUse(RG_MASTER_SWORD); @@ -1197,7 +1201,8 @@ bool Logic::CanReflectNuts() { bool Logic::CanCutShrubs() { return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_BOOMERANG) || HasExplosives() || CanUse(RG_MASTER_SWORD) || - CanUse(RG_MEGATON_HAMMER) || CanUse(RG_BIGGORON_SWORD) || HasItem(RG_GORONS_BRACELET); + CanUse(RG_MEGATON_HAMMER) || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_GIANTS_KNIFE) || + HasItem(RG_GORONS_BRACELET); } bool Logic::CanStunDeku() { @@ -1403,13 +1408,14 @@ bool Logic::SmallKeys(s16 scene, uint8_t requiredAmount) { } std::map Logic::RandoGetToEquipFlag = { - { RG_KOKIRI_SWORD, EQUIP_FLAG_SWORD_KOKIRI }, { RG_MASTER_SWORD, EQUIP_FLAG_SWORD_MASTER }, - { RG_BIGGORON_SWORD, EQUIP_FLAG_SWORD_BGS }, { RG_DEKU_SHIELD, EQUIP_FLAG_SHIELD_DEKU }, - { RG_HYLIAN_SHIELD, EQUIP_FLAG_SHIELD_HYLIAN }, { RG_MIRROR_SHIELD, EQUIP_FLAG_SHIELD_MIRROR }, - { RG_GORON_TUNIC, EQUIP_FLAG_TUNIC_GORON }, { RG_ZORA_TUNIC, EQUIP_FLAG_TUNIC_ZORA }, - { RG_BUY_DEKU_SHIELD, EQUIP_FLAG_SHIELD_DEKU }, { RG_BUY_HYLIAN_SHIELD, EQUIP_FLAG_SHIELD_HYLIAN }, - { RG_BUY_GORON_TUNIC, EQUIP_FLAG_TUNIC_GORON }, { RG_BUY_ZORA_TUNIC, EQUIP_FLAG_TUNIC_ZORA }, - { RG_IRON_BOOTS, EQUIP_FLAG_BOOTS_IRON }, { RG_HOVER_BOOTS, EQUIP_FLAG_BOOTS_HOVER } + { RG_KOKIRI_SWORD, EQUIP_FLAG_SWORD_KOKIRI }, { RG_MASTER_SWORD, EQUIP_FLAG_SWORD_MASTER }, + { RG_GIANTS_KNIFE, EQUIP_FLAG_SWORD_BGS }, { RG_BIGGORON_SWORD, EQUIP_FLAG_SWORD_BGS }, + { RG_DEKU_SHIELD, EQUIP_FLAG_SHIELD_DEKU }, { RG_HYLIAN_SHIELD, EQUIP_FLAG_SHIELD_HYLIAN }, + { RG_MIRROR_SHIELD, EQUIP_FLAG_SHIELD_MIRROR }, { RG_GORON_TUNIC, EQUIP_FLAG_TUNIC_GORON }, + { RG_ZORA_TUNIC, EQUIP_FLAG_TUNIC_ZORA }, { RG_BUY_DEKU_SHIELD, EQUIP_FLAG_SHIELD_DEKU }, + { RG_BUY_HYLIAN_SHIELD, EQUIP_FLAG_SHIELD_HYLIAN }, { RG_BUY_GORON_TUNIC, EQUIP_FLAG_TUNIC_GORON }, + { RG_BUY_ZORA_TUNIC, EQUIP_FLAG_TUNIC_ZORA }, { RG_IRON_BOOTS, EQUIP_FLAG_BOOTS_IRON }, + { RG_HOVER_BOOTS, EQUIP_FLAG_BOOTS_HOVER } }; std::map Logic::RandoGetToRandInf = { @@ -1858,18 +1864,18 @@ void Logic::ApplyItemEffect(Item& item, bool state) { } break; case ITEMTYPE_EQUIP: { RandomizerGet itemRG = item.GetRandomizerGet(); - if (itemRG == RG_GIANTS_KNIFE || itemRG == RG_DEKU_SHIELD || itemRG == RG_HYLIAN_SHIELD) { + if (itemRG == RG_DEKU_SHIELD || itemRG == RG_HYLIAN_SHIELD) { return; } uint32_t equipId = RandoGetToEquipFlag.find(itemRG)->second; if (!state) { mSaveContext->inventory.equipment &= ~equipId; - if (equipId == EQUIP_FLAG_SWORD_BGS) { + if (equipId == EQUIP_FLAG_SWORD_BGS && itemRG != RG_GIANTS_KNIFE) { mSaveContext->bgsFlag = false; } } else { mSaveContext->inventory.equipment |= equipId; - if (equipId == EQUIP_FLAG_SWORD_BGS) { + if (equipId == EQUIP_FLAG_SWORD_BGS && itemRG != RG_GIANTS_KNIFE) { mSaveContext->bgsFlag = true; } } diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 1880a64ba..d01ab357c 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -234,6 +234,7 @@ typedef enum { LOGIC_CHILD_SCARECROW, LOGIC_ADULT_SCARECROW, LOGIC_CARPET_MERCHANT, + LOGIC_MEDIGORON, LOGIC_COULD_PLAY_BOWLING, LOGIC_BIG_POE_KILL, LOGIC_BUILD_RAINBOW_BRIDGE, diff --git a/soh/soh/Enhancements/randomizer/settings.cpp b/soh/soh/Enhancements/randomizer/settings.cpp index ce365146e..0a8fda379 100644 --- a/soh/soh/Enhancements/randomizer/settings.cpp +++ b/soh/soh/Enhancements/randomizer/settings.cpp @@ -424,7 +424,8 @@ void Settings::CreateOptions() { "- Crossing Gerudo Valley with Hookshot\n" "- Retrieving DMT Gold Skulltula beside bomb flower\n" "- Hitting switch through wall in Spirit Temple's big mirror room with Bow, Slingshot, or Hookshot\n" - "- Hitting switch through wall in Spirit Trial with Bow or Slingshot"); + "- Hitting switch through wall in Spirit Trial with Bow or Slingshot\n" + "- Hitting switch through gate in Shadow Temple MQ with Bow or Slingshot"); OPT_TRICK(RT_BIG_SKULLTULA_PAUSE_LIFT, RCQUEST_BOTH, RA_NONE, { Tricks::Tag::NOVICE, Tricks::Tag::GLITCH }, "Lift Big Skulltulas with Pausing", "Pausing while a big skulltula is bobbing upwards slightly lifts it,\n"