From 43b04a32cdd4d524b7574ad835d000d0066716b8 Mon Sep 17 00:00:00 2001 From: Pepper0ni <93387759+Pepper0ni@users.noreply.github.com> Date: Wed, 16 Oct 2024 22:16:58 +0100 Subject: [PATCH] rewrite MQ deku tree logic (#4418) --- .../hint_list/hint_list_exclude_dungeon.cpp | 2 +- .../location_access/locacc_deku_tree.cpp | 188 +++++++++++++----- soh/soh/Enhancements/randomizer/dungeon.cpp | 2 +- .../Enhancements/randomizer/location_list.cpp | 2 +- soh/soh/Enhancements/randomizer/logic.cpp | 64 +++++- soh/soh/Enhancements/randomizer/logic.h | 6 +- .../Enhancements/randomizer/randomizerTypes.h | 16 +- 7 files changed, 222 insertions(+), 58 deletions(-) diff --git a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp index c2117c4eb..20e646307 100644 --- a/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/hint_list/hint_list_exclude_dungeon.cpp @@ -102,7 +102,7 @@ void StaticData::HintTable_Init_Exclude_Dungeon() { /*french*/ "Selon moi, une #Skulltula dans une boîte dans l'Arbre Mojo# a #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #Skulltula bajo una caja# del Árbol Deku otorga #[[1]]#. - hintTextTable[RHT_DEKU_TREE_MQ_GS_COMPASS_ROOM] = HintText(CustomMessage("They say that a #wall of rock protects a spider# within the Deku Tree holding #[[1]]#.", + hintTextTable[RHT_DEKU_TREE_MQ_GS_PAST_BOULDER_VINES] = HintText(CustomMessage("They say that a #wall of rock protects a spider# within the Deku Tree holding #[[1]]#.", /*german*/ "Man erzählt sich, daß eine #von einer Steinwand geschützte Spinne# innerhalb des Deku-Baums #[[1]]# hielte.", /*french*/ "Selon moi, une #Skulltula derrière des rochers dans l'Arbre Mojo# a #[[1]]#.", {QM_RED, QM_GREEN})); // /*spanish*/ Según dicen, una #Skulltula protegida por una pared rocosa# del Árbol Deku otorga #[[1]]#. diff --git a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_deku_tree.cpp b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_deku_tree.cpp index 7bb07ba57..ab642b402 100644 --- a/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_deku_tree.cpp +++ b/soh/soh/Enhancements/randomizer/3drando/location_access/locacc_deku_tree.cpp @@ -10,9 +10,9 @@ void RegionTable_Init_DekuTree() { ---------------------------*/ areaTable[RR_DEKU_TREE_ENTRYWAY] = Region("Deku Tree Entryway", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, { //Exits - Entrance(RR_DEKU_TREE_LOBBY, {[]{return ctx->GetDungeon(DEKU_TREE)->IsVanilla();}}), - Entrance(RR_DEKU_TREE_MQ_LOBBY, {[]{return ctx->GetDungeon(DEKU_TREE)->IsMQ();}}), - Entrance(RR_KF_OUTSIDE_DEKU_TREE, {[]{return true;}}), + Entrance(RR_DEKU_TREE_LOBBY, {[]{return ctx->GetDungeon(DEKU_TREE)->IsVanilla();}}), + Entrance(RR_DEKU_TREE_MQ_1F, {[]{return ctx->GetDungeon(DEKU_TREE)->IsMQ();}}), + Entrance(RR_KF_OUTSIDE_DEKU_TREE, {[]{return true;}}), }); /*-------------------------- @@ -155,88 +155,186 @@ void RegionTable_Init_DekuTree() { | MASTER QUEST DUNGEON | ---------------------------*/ if (ctx->GetDungeon(DEKU_TREE)->IsMQ()) { - areaTable[RR_DEKU_TREE_MQ_LOBBY] = Region("Deku Tree MQ Lobby", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, { + areaTable[RR_DEKU_TREE_MQ_1F] = Region("Deku Tree MQ 1F", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->DekuBabaSticks, {[]{return logic->CanKillEnemy(RE_WITHERED_DEKU_BABA);}}), + EventAccess(&logic->BrokeDeku1FWeb, {[]{return logic->HasFireSource();}}), + }, {}, { + //Exits + Entrance(RR_DEKU_TREE_ENTRYWAY, {[]{return true;}}), + //may need canAvoid logic with enemy shuffle + Entrance(RR_DEKU_TREE_MQ_2F, {[]{return true;}}), + //Swim is not required because you can jump with enough momentum to hit land. + //You even avoid fall damage if you hit the shallow water, though it's obscure knowledge so may be a trick + //if it is, then we need a landing room with (IsAdult || CanUse(RG_BRONZE_SCALE) || TakeDamage() || that trick) to reach basement + Entrance(RR_DEKU_TREE_MQ_BASEMENT, {[]{return logic->BrokeDeku1FWeb;}}), + //is it possible to recoil from here to the ledge with a trick? + }); + + areaTable[RR_DEKU_TREE_MQ_2F] = Region("Deku Tree MQ 2F", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_DEKU_TREE_MQ_MAP_CHEST, true), + LOCATION(RC_DEKU_TREE_MQ_GS_LOBBY, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA)), + }, { + //Exits + Entrance(RR_DEKU_TREE_MQ_1F, {[]{return true;}}), + //Will need canAvoid logic with enemy shuffle + Entrance(RR_DEKU_TREE_MQ_3F, {[]{return true;}}), + Entrance(RR_DEKU_TREE_MQ_EYE_TARGET_ROOM, {[]{return Here(RR_DEKU_TREE_MQ_2F, []{return logic->HasFireSource();});}}), + }); + + areaTable[RR_DEKU_TREE_MQ_3F] = Region("Deku Tree MQ 3F", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->DekuBabaSticks, {[]{return logic->CanGetDekuBabaSticks();}}), + EventAccess(&logic->DekuBabaNuts, {[]{return logic->CanGetDekuBabaNuts();}}), + EventAccess(&logic->BrokeDeku1FWeb, {[]{return true;}}), + }, { + //Locations + //Implies CanKillEnemy(RE_GOHMA_LARVA) + LOCATION(RC_DEKU_TREE_MQ_SLINGSHOT_CHEST, logic->CanKillEnemy(RE_DEKU_BABA)), + LOCATION(RC_DEKU_TREE_MQ_SLINGSHOT_ROOM_BACK_CHEST, logic->HasFireSourceWithTorch() || (logic->IsAdult && logic->CanUse(RG_FAIRY_BOW))), + }, { + //Exits + Entrance(RR_DEKU_TREE_MQ_2F, {[]{return true;}}), + //Assumes RR_DEKU_TREE_MQ_2F access + Entrance(RR_DEKU_TREE_MQ_EYE_TARGET_ROOM, {[]{return Here(RR_DEKU_TREE_MQ_3F, []{return logic->CanUse(RG_STICKS) || logic->CanUse(RG_FAIRY_BOW);});}}), + Entrance(RR_DEKU_TREE_MQ_BASEMENT, {[]{return true;}}), + }); + + areaTable[RR_DEKU_TREE_MQ_EYE_TARGET_ROOM] = Region("Deku Tree MQ Eye Target Room", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + Entrance(RR_DEKU_TREE_MQ_COMPASS_ROOM, {[]{return Here(RR_DEKU_TREE_MQ_EYE_TARGET_ROOM, []{return logic->CanHitEyeTargets();});}}), + Entrance(RR_DEKU_TREE_MQ_2F, {[]{return true;}}), + }); + + areaTable[RR_DEKU_TREE_MQ_COMPASS_ROOM] = Region("Deku Tree MQ Compass Room", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_DEKU_TREE_MQ_COMPASS_CHEST, true), + }, { + //Exits + Entrance(RR_DEKU_TREE_MQ_EYE_TARGET_ROOM, {[]{return true;}}), + Entrance(RR_DEKU_TREE_MQ_PAST_BOULDER_VINES, {[]{return Here(RR_DEKU_TREE_MQ_COMPASS_ROOM, []{return logic->CanUse(RG_BOMBCHU_5) || + (logic->CanUse(RG_BOMB_BAG) && (logic->CanUse(RG_SONG_OF_TIME) || logic->IsAdult || logic->CanUse(RG_HOVER_BOOTS))) || + (logic->CanUse(RG_MEGATON_HAMMER) && (logic->CanUse(RG_SONG_OF_TIME) || ctx->GetTrickOption(RT_DEKU_MQ_COMPASS_GS)));});}}), + }); + + areaTable[RR_DEKU_TREE_MQ_PAST_BOULDER_VINES] = Region("Deku Tree MQ Past Boulder Vines", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, { + //Locations + LOCATION(RC_DEKU_TREE_MQ_GS_PAST_BOULDER_VINES, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_RANG_OR_HOOKSHOT)), + }, { + //Exits + Entrance(RR_DEKU_TREE_MQ_COMPASS_ROOM, {[]{return logic->BlastOrSmash();}}), + }); + + areaTable[RR_DEKU_TREE_MQ_BASEMENT] = Region("Deku Tree MQ Basement", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, { //Events EventAccess(&logic->DekuBabaSticks, {[]{return logic->CanGetDekuBabaSticks();}}), EventAccess(&logic->DekuBabaNuts, {[]{return logic->CanGetDekuBabaNuts();}}), }, { //Locations - LOCATION(RC_DEKU_TREE_MQ_MAP_CHEST, true), - LOCATION(RC_DEKU_TREE_MQ_SLINGSHOT_CHEST, logic->CanAttack()), - LOCATION(RC_DEKU_TREE_MQ_SLINGSHOT_ROOM_BACK_CHEST, logic->HasFireSourceWithTorch() || (logic->IsAdult && logic->CanUse(RG_FAIRY_BOW))), - LOCATION(RC_DEKU_TREE_MQ_BASEMENT_CHEST, logic->HasFireSourceWithTorch() || (logic->IsAdult && logic->CanUse(RG_FAIRY_BOW))), - LOCATION(RC_DEKU_TREE_MQ_GS_LOBBY, logic->CanAttack()), + LOCATION(RC_DEKU_TREE_MQ_BASEMENT_CHEST, logic->HasFireSourceWithTorch() || logic->CanUse(RG_FAIRY_BOW)), }, { //Exits - Entrance(RR_DEKU_TREE_ENTRYWAY, {[]{return true;}}), - Entrance(RR_DEKU_TREE_MQ_COMPASS_ROOM, {[]{return Here(RR_DEKU_TREE_MQ_LOBBY, []{return (logic->IsChild && logic->CanUse(RG_FAIRY_SLINGSHOT)) || (logic->IsAdult && logic->CanUse(RG_FAIRY_BOW));}) && - Here(RR_DEKU_TREE_MQ_LOBBY, []{return logic->HasFireSourceWithTorch() || (logic->IsAdult && logic->CanUse(RG_FAIRY_BOW));});}}), - Entrance(RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_FRONT, {[]{return Here(RR_DEKU_TREE_MQ_LOBBY, []{return (logic->IsChild && logic->CanUse(RG_FAIRY_SLINGSHOT)) || (logic->IsAdult && logic->CanUse(RG_FAIRY_BOW));}) && - Here(RR_DEKU_TREE_MQ_LOBBY, []{return logic->HasFireSourceWithTorch();});}}), - Entrance(RR_DEKU_TREE_MQ_BASEMENT_LEDGE, {[]{return ctx->GetTrickOption(RT_DEKU_B1_SKIP) || Here(RR_DEKU_TREE_MQ_LOBBY, []{return logic->IsAdult;});}}), + Entrance(RR_DEKU_TREE_MQ_1F, {[]{return true;}}), + Entrance(RR_DEKU_TREE_MQ_BASEMENT_SOUTHEAST_ROOM, {[]{return Here(RR_DEKU_TREE_MQ_BASEMENT, []{return logic->CanHitEyeTargets();});}}), + //includes RR_DEKU_TREE_MQ_BASEMENT_SOUTHEAST_ROOM Access, other fire sources clear directly from there + Entrance(RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_FRONT, {[]{return Here(RR_DEKU_TREE_MQ_BASEMENT, []{return logic->CanHitEyeTargets();}) && logic->ClearedMQDekuSERoom && + Here(RR_DEKU_TREE_MQ_BASEMENT, []{return logic->CanUse(RG_STICKS);});}}), + Entrance(RR_DEKU_TREE_MQ_BASEMENT_LEDGE, {[]{return ctx->GetTrickOption(RT_DEKU_B1_SKIP) || logic->PushedDekuBasementBlock || logic->IsAdult || logic->CanUse(RG_HOVER_BOOTS);}}), }); - areaTable[RR_DEKU_TREE_MQ_COMPASS_ROOM] = Region("Deku Tree MQ Compass Room", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, { - //Locations - LOCATION(RC_DEKU_TREE_MQ_COMPASS_CHEST, true), - LOCATION(RC_DEKU_TREE_MQ_GS_COMPASS_ROOM, logic->HookshotOrBoomerang() && - Here(RR_DEKU_TREE_MQ_COMPASS_ROOM, []{return logic->CanUse(RG_BOMBCHU_5) || - (logic->CanUse(RG_BOMB_BAG) && (logic->CanUse(RG_SONG_OF_TIME) || logic->IsAdult)) || - (logic->IsAdult && logic->CanUse(RG_MEGATON_HAMMER) && (logic->CanUse(RG_SONG_OF_TIME) || ctx->GetTrickOption(RT_DEKU_MQ_COMPASS_GS)));})), - }, { + areaTable[RR_DEKU_TREE_MQ_BASEMENT_SOUTHEAST_ROOM] = Region("Deku Tree MQ Southeast Room", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, { + //Events + //Implies CanKillEnemy(RE_GOHMA_LARVA) + EventAccess(&logic->ClearedMQDekuSERoom, {[]{return logic->CanKillEnemy(RE_MAD_SCRUB);}}), + }, {}, { //Exits - Entrance(RR_DEKU_TREE_MQ_LOBBY, {[]{return true;}}), + Entrance(RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_FRONT, {[]{return logic->HasFireSource();}}), + Entrance(RR_DEKU_TREE_MQ_BASEMENT, {[]{return logic->ClearedMQDekuSERoom;}}), }); - areaTable[RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_FRONT] = Region("Deku Tree MQ Basement Water Room Front", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_FRONT] = Region("Deku Tree MQ Basement Water Room Front", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, { + //Events + //It's possible to get this with bow if you have move while in first person and one-point skips on, noticeably harder and jankier as child, but that's a trick + EventAccess(&logic->MQDekuWaterRoomTorches, {[]{return logic->CanUse(RG_FIRE_ARROWS) || (logic->IsChild && logic->CanUse(RG_STICKS) && logic->CanShield());}}), + }, { //Locations LOCATION(RC_DEKU_TREE_MQ_BEFORE_SPINNING_LOG_CHEST, true), }, { //Exits - Entrance(RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_BACK, {[]{return ctx->GetTrickOption(RT_DEKU_MQ_LOG) || (logic->IsChild && (logic->CanUse(RG_DEKU_SHIELD) || logic->HasItem(RG_HYLIAN_SHIELD))) || - (logic->IsAdult && (logic->CanUse(RG_LONGSHOT) || (logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_IRON_BOOTS))));}}), - Entrance(RR_DEKU_TREE_MQ_LOBBY, {[]{return true;}}), + Entrance(RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_BACK, {[]{return ctx->GetTrickOption(RT_DEKU_MQ_LOG) || (logic->IsChild && logic->CanShield()) || + logic->CanUse(RG_LONGSHOT) || (logic->CanUse(RG_HOOKSHOT) && logic->CanUse(RG_IRON_BOOTS));}}), + Entrance(RR_DEKU_TREE_MQ_BASEMENT_SOUTHEAST_ROOM, {[]{return true;}}), }); - areaTable[RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_BACK] = Region("Deku Tree MQ Basement Water Room Back", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_BACK] = Region("Deku Tree MQ Basement Water Room Back", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->DekuBabaSticks, {[]{return logic->CanKillEnemy(RE_WITHERED_DEKU_BABA);}}), + EventAccess(&logic->MQDekuWaterRoomTorches, {[]{return logic->HasFireSource();}}), + }, { //Locations - LOCATION(RC_DEKU_TREE_MQ_AFTER_SPINNING_LOG_CHEST, logic->CanUse(RG_SONG_OF_TIME)), + //it blocks the chest while stunned unless you stun it from afar while it's slightly off the ground + LOCATION(RC_DEKU_TREE_MQ_AFTER_SPINNING_LOG_CHEST, logic->CanUse(RG_SONG_OF_TIME) && logic->CanPassEnemy(RE_BIG_SKULLTULA)), }, { //Exits - Entrance(RR_DEKU_TREE_MQ_BASEMENT_BACK_ROOM, {[]{return Here(RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_BACK, []{return (logic->IsChild && logic->CanUse(RG_STICKS)) || logic->CanUse(RG_DINS_FIRE) || - Here(RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_FRONT, []{return logic->IsAdult && logic->CanUse(RG_FIRE_ARROWS);});}) && - Here(RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_BACK, []{return logic->CanUse(RG_KOKIRI_SWORD) || logic->CanUse(RG_MASTER_SWORD) || logic->CanUse(RG_BIGGORON_SWORD) || - logic->CanUseProjectile() || (logic->CanUse(RG_NUTS) && logic->CanUse(RG_STICKS));});}}), - Entrance(RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_FRONT, {[]{return true;}}), + Entrance(RR_DEKU_TREE_MQ_BASEMENT_SOUTHWEST_ROOM, {[]{return logic->MQDekuWaterRoomTorches && logic->CanPassEnemy(RE_BIG_SKULLTULA, logic->CanUse(RG_SONG_OF_TIME) ? ED_CLOSE : ED_HAMMER_JUMPSLASH);}}), + Entrance(RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_FRONT, {[]{return ctx->GetTrickOption(RT_DEKU_MQ_LOG) || (logic->IsChild && logic->CanShield()) || + logic->CanUse(RG_LONGSHOT) || logic->CanUse(RG_BRONZE_SCALE) || + (logic->CanUse(RG_IRON_BOOTS) && (logic->IsAdult || logic->CanUse(RG_HOOKSHOT)));}}), + }); + + areaTable[RR_DEKU_TREE_MQ_BASEMENT_SOUTHWEST_ROOM] = Region("Deku Tree MQ Basement Southwest Room", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, { + //Exits + //both imply CanKillEnemy(RE_GOHMA_LARVA) + Entrance(RR_DEKU_TREE_MQ_BASEMENT_GRAVE_ROOM, {[]{return Here(RR_DEKU_TREE_MQ_BASEMENT_SOUTHWEST_ROOM, []{return logic->CanKillEnemy(RE_MAD_SCRUB) && logic->CanKillEnemy(RE_KEESE);});}}), + Entrance(RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_BACK, {[]{return Here(RR_DEKU_TREE_MQ_BASEMENT_SOUTHWEST_ROOM, []{return logic->CanKillEnemy(RE_MAD_SCRUB) && logic->CanKillEnemy(RE_KEESE);});}}), + }); + + areaTable[RR_DEKU_TREE_MQ_BASEMENT_GRAVE_ROOM] = Region("Deku Tree MQ Basement Grave Room", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->DekuBabaSticks, {[]{return logic->CanGetDekuBabaSticks();}}), + EventAccess(&logic->DekuBabaNuts, {[]{return logic->CanGetDekuBabaNuts();}}) + }, { + //Locations + LOCATION(RC_DEKU_TREE_MQ_GS_BASEMENT_GRAVES_ROOM, logic->CanUse(RG_LONGSHOT) || (logic->CanUse(RG_SONG_OF_TIME) && logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_RANG_OR_HOOKSHOT))), + }, { + //Exits + Entrance(RR_DEKU_TREE_MQ_BASEMENT_LEDGE, {[]{return logic->IsChild && Here(RR_DEKU_TREE_MQ_BASEMENT_GRAVE_ROOM, []{return logic->HasFireSourceWithTorch() || logic->CanUse(RG_FAIRY_BOW);});}}), + Entrance(RR_DEKU_TREE_MQ_BASEMENT_SOUTHWEST_ROOM, {[]{return true;}}), + //Using a bow to get past here as adult is a bit precise on standing position but simple, doing as as child requires a side-hop with the bow out to shoot through the torch and may be trick worthy + Entrance(RR_DEKU_TREE_MQ_BASEMENT_BACK_ROOM, {[]{return Here(RR_DEKU_TREE_MQ_BASEMENT_GRAVE_ROOM, []{return logic->HasFireSourceWithTorch() || logic->CanUse(RG_FAIRY_BOW);});}}), }); areaTable[RR_DEKU_TREE_MQ_BASEMENT_BACK_ROOM] = Region("Deku Tree MQ Basement Back Room", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, { //Locations - LOCATION(RC_DEKU_TREE_MQ_GS_BASEMENT_GRAVES_ROOM, (logic->IsAdult && logic->CanUse(RG_LONGSHOT)) || (logic->CanUse(RG_SONG_OF_TIME) && logic->HookshotOrBoomerang())), - LOCATION(RC_DEKU_TREE_MQ_GS_BASEMENT_BACK_ROOM, logic->HasFireSourceWithTorch() && logic->HookshotOrBoomerang()), + LOCATION(RC_DEKU_TREE_MQ_GS_BASEMENT_BACK_ROOM, logic->CanGetEnemyDrop(RE_GOLD_SKULLTULA, ED_RANG_OR_HOOKSHOT)), }, { //Exits - Entrance(RR_DEKU_TREE_MQ_BASEMENT_LEDGE, {[]{return logic->IsChild;}}), - Entrance(RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_BACK, {[]{return (logic->IsChild && logic->CanUse(RG_KOKIRI_SWORD)) || logic->CanUseProjectile() || (logic->CanUse(RG_NUTS) && (logic->IsChild && logic->CanUse(RG_STICKS)));}}), + Entrance(RR_DEKU_TREE_MQ_BASEMENT_GRAVE_ROOM, {[]{return true;}}), }); - areaTable[RR_DEKU_TREE_MQ_BASEMENT_LEDGE] = Region("Deku Tree MQ Basement Ledge", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, { + areaTable[RR_DEKU_TREE_MQ_BASEMENT_LEDGE] = Region("Deku Tree MQ Basement Ledge", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, { + //Events + EventAccess(&logic->PushedDekuBasementBlock, {[]{return true;}}), + }, { //Locations LOCATION(RC_DEKU_TREE_MQ_DEKU_SCRUB, logic->CanStunDeku()), }, { //Exits Entrance(RR_DEKU_TREE_MQ_BASEMENT_BACK_ROOM, {[]{return logic->IsChild;}}), - Entrance(RR_DEKU_TREE_MQ_LOBBY, {[]{return true;}}), - Entrance(RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM, - { [] { return Here(RR_DEKU_TREE_MQ_BASEMENT_LEDGE, [] { return logic->HasFireSourceWithTorch(); }); } }), + Entrance(RR_DEKU_TREE_MQ_BASEMENT, {[]{return true;}}), + //If strength 0 is shuffled, add hovers or block push to the stick check + //recoiling to skip swim is possible, but would be a trick + Entrance(RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM, {[]{return Here(RR_DEKU_TREE_MQ_BASEMENT_LEDGE, []{return logic->HasFireSource() || (/*logic->PushedDekuBasementBlock && */logic->CanUse(RG_STICKS));}) + && (logic->CanUse(RG_BRONZE_SCALE) || logic->CanUse(RG_IRON_BOOTS));}}), }); areaTable[RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM] = Region("Deku Tree MQ Outside Boss Room", "Deku Tree", {RA_DEKU_TREE}, NO_DAY_NIGHT_CYCLE, {}, {}, { // Exits - Entrance(RR_DEKU_TREE_MQ_BASEMENT_LEDGE, {[]{ return true; }}), - Entrance(RR_DEKU_TREE_BOSS_ENTRYWAY, {[]{return (logic->HasItem(RG_BRONZE_SCALE) || Here(RR_DEKU_TREE_MQ_BASEMENT_LEDGE, []{return logic->CanUse(RG_IRON_BOOTS);})) && Here(RR_DEKU_TREE_MQ_BASEMENT_LEDGE, [] { return logic->CanReflectNuts(); }); } }), + Entrance(RR_DEKU_TREE_MQ_BASEMENT_LEDGE, {[]{return logic->CanUse(RG_BRONZE_SCALE);}}), + Entrance(RR_DEKU_TREE_BOSS_ENTRYWAY, {[]{return Here(RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM, []{return logic->CanReflectNuts();});}}), }); } diff --git a/soh/soh/Enhancements/randomizer/dungeon.cpp b/soh/soh/Enhancements/randomizer/dungeon.cpp index 8484288a5..cbbb39b28 100644 --- a/soh/soh/Enhancements/randomizer/dungeon.cpp +++ b/soh/soh/Enhancements/randomizer/dungeon.cpp @@ -187,7 +187,7 @@ Dungeons::Dungeons() { RC_DEKU_TREE_MQ_AFTER_SPINNING_LOG_CHEST, RC_DEKU_TREE_MQ_DEKU_SCRUB, RC_DEKU_TREE_MQ_GS_LOBBY, - RC_DEKU_TREE_MQ_GS_COMPASS_ROOM, + RC_DEKU_TREE_MQ_GS_PAST_BOULDER_VINES, RC_DEKU_TREE_MQ_GS_BASEMENT_GRAVES_ROOM, RC_DEKU_TREE_MQ_GS_BASEMENT_BACK_ROOM, }, diff --git a/soh/soh/Enhancements/randomizer/location_list.cpp b/soh/soh/Enhancements/randomizer/location_list.cpp index 9b3c43999..d5bea5cf6 100644 --- a/soh/soh/Enhancements/randomizer/location_list.cpp +++ b/soh/soh/Enhancements/randomizer/location_list.cpp @@ -649,7 +649,7 @@ void Rando::StaticData::InitLocationTable() { // locationTable[RC_DEKU_TREE_GS_BASEMENT_VINES] = Location::GSToken(RC_DEKU_TREE_GS_BASEMENT_VINES, RCQUEST_VANILLA, SCENE_DEKU_TREE, 8196, 0x04, "GS Basement Vines", RHT_DEKU_TREE_GS_BASEMENT_VINES); locationTable[RC_DEKU_TREE_GS_COMPASS_ROOM] = Location::GSToken(RC_DEKU_TREE_GS_COMPASS_ROOM, RCQUEST_VANILLA, SCENE_DEKU_TREE, 8200, 0x08, "GS Compass Room", RHT_DEKU_TREE_GS_COMPASS_ROOM); locationTable[RC_DEKU_TREE_MQ_GS_LOBBY] = Location::GSToken(RC_DEKU_TREE_MQ_GS_LOBBY, RCQUEST_MQ, SCENE_DEKU_TREE, 8194, 0x02, "MQ GS Lobby", RHT_DEKU_TREE_MQ_GS_LOBBY); - locationTable[RC_DEKU_TREE_MQ_GS_COMPASS_ROOM] = Location::GSToken(RC_DEKU_TREE_MQ_GS_COMPASS_ROOM, RCQUEST_MQ, SCENE_DEKU_TREE, 8200, 0x08, "MQ GS Compass Room", RHT_DEKU_TREE_MQ_GS_COMPASS_ROOM); + locationTable[RC_DEKU_TREE_MQ_GS_PAST_BOULDER_VINES] = Location::GSToken(RC_DEKU_TREE_MQ_GS_PAST_BOULDER_VINES, RCQUEST_MQ, SCENE_DEKU_TREE, 8200, 0x08, "MQ GS Past Boulder Vines", RHT_DEKU_TREE_MQ_GS_PAST_BOULDER_VINES); locationTable[RC_DEKU_TREE_MQ_GS_BASEMENT_GRAVES_ROOM] = Location::GSToken(RC_DEKU_TREE_MQ_GS_BASEMENT_GRAVES_ROOM, RCQUEST_MQ, SCENE_DEKU_TREE, 8196, 0x04, "MQ GS Basement Graves Room", RHT_DEKU_TREE_MQ_GS_BASEMENT_GRAVES_ROOM); locationTable[RC_DEKU_TREE_MQ_GS_BASEMENT_BACK_ROOM] = Location::GSToken(RC_DEKU_TREE_MQ_GS_BASEMENT_BACK_ROOM, RCQUEST_MQ, SCENE_DEKU_TREE, 8193, 0x01, "MQ GS Basement Back Room", RHT_DEKU_TREE_MQ_GS_BASEMENT_BACK_ROOM); // Dodongo's Cavern diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index cc0d80a24..7d2bb308e 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -421,13 +421,35 @@ namespace Rando { } bool Logic::CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance) { + bool killed = false; switch(enemy) { case RE_GOLD_SKULLTULA: case RE_GOHMA_LARVA: case RE_MAD_SCRUB: + case RE_DEKU_BABA: return CanAttack(); case RE_BIG_SKULLTULA: - return CanDamage() || CanUse(RG_HOOKSHOT); + switch (distance){ + case ED_CLOSE: + //hammer jumpslash cannot damage these, but hammer swing can + killed = killed || CanUse(RG_MEGATON_HAMMER); + [[fallthrough]]; + case ED_HAMMER_JUMPSLASH: + case ED_MASTER_SWORD_JUMPSLASH: + killed = killed || CanJumpslashExceptHammer(); + [[fallthrough]]; + case ED_RANG_OR_HOOKSHOT: + //RANDOTODO test dins, bomb and chu range in a practical example, might need a wall var to handle chus + killed = killed || CanUse(RG_HOOKSHOT) || HasExplosives() || CanUse(RG_DINS_FIRE); + [[fallthrough]]; + case ED_LONGSHOT: + killed = killed || CanUse(RG_LONGSHOT); + [[fallthrough]]; + case ED_FAR: + killed = CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_FAIRY_BOW); + break; + } + return killed; case RE_DODONGO: case RE_LIZALFOS: return CanJumpslash() || HasExplosives() || CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_FAIRY_BOW); @@ -435,12 +457,14 @@ namespace Rando { case RE_FIRE_KEESE: return CanJumpslash() || HasExplosives() || CanUse(RG_FAIRY_SLINGSHOT) || CanUse(RG_FAIRY_BOW) || CanUse(RG_HOOKSHOT) || CanUse(RG_BOOMERANG); case RE_BLUE_BUBBLE: - //RANDOTODO Trick to use shield hylian shield to stun these guys + //RANDOTODO Trick to use shield hylian shield as child to stun these guys //RANDOTODO check hammer damage return BlastOrSmash() || CanUse(RG_FAIRY_BOW) || ((CanJumpslashExceptHammer() || CanUse(RG_FAIRY_SLINGSHOT)) && (CanUse(RG_NUTS) || HookshotOrBoomerang() || CanStandingShield())); case RE_DEAD_HAND: //RANDOTODO change Dead Hand trick to be sticks Dead Hand return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || (CanUse(RG_STICKS) && ctx->GetTrickOption(RT_BOTW_CHILD_DEADHAND)); + case RE_WITHERED_DEKU_BABA: + return CanUse(RG_KOKIRI_SWORD) || CanUse(RG_MASTER_SWORD) || CanUse(RG_BIGGORON_SWORD) || CanUse(RG_BOOMERANG); default: SPDLOG_ERROR("CanKillEnemy reached `default`."); assert(false); @@ -448,8 +472,10 @@ namespace Rando { } } - bool Logic::CanPassEnemy(RandomizerEnemy enemy) { - if (CanKillEnemy(enemy)){ +//It is rare for Pass Enemy to need distance, this only happens when the enemy blocks a platform and you can't reach it before it blocks you +//an example is the Big Skulltula in water room of MQ deku, which is out of sword swing height but blocks off the whole SoT block + bool Logic::CanPassEnemy(RandomizerEnemy enemy, EnemyDistance distance) { + if (CanKillEnemy(enemy, distance)){ return true; } switch(enemy) { @@ -462,8 +488,11 @@ namespace Rando { case RE_FIRE_KEESE: case RE_BLUE_BUBBLE: case RE_DEAD_HAND: + case RE_DEKU_BABA: + case RE_WITHERED_DEKU_BABA: return true; case RE_BIG_SKULLTULA: + //hammer jumpslash can pass, but only on flat land where you can kill with hammer swing return CanUse(RG_NUTS) || CanUse(RG_BOOMERANG); default: SPDLOG_ERROR("CanPassEnemy reached `default`."); @@ -483,13 +512,15 @@ namespace Rando { case RE_DODONGO: //RANDOTODO do dodongos block the way in tight corridors? case RE_BIG_SKULLTULA: case RE_DEAD_HAND: + case RE_DEKU_BABA: + case RE_WITHERED_DEKU_BABA: return true; case RE_MAD_SCRUB: case RE_KEESE: case RE_FIRE_KEESE: return CanUse(RG_NUTS); case RE_BLUE_BUBBLE: - //RANDOTODO Trick to use shield hylian shield to stun these guys + //RANDOTODO Trick to use shield hylian shield as child to stun these guys return CanUse(RG_NUTS) || HookshotOrBoomerang() || CanStandingShield(); default: SPDLOG_ERROR("CanPassEnemy reached `default`."); @@ -644,9 +675,26 @@ namespace Rando { return CallGossipFairyExceptSuns() || CanUse(RG_SUNS_SONG); } + //the number returned by this is in half heart hits taken. + //RANDOTODO work in OoT side health instead for greater applicability (16 per heart) uint8_t Logic::EffectiveHealth(){ + /* Multiplier will be: + 0 for half daamge + 1 for normal damage + 2 for double damage + 3 for quad damage + 4 for 8* damage + 5 for 16* damage + 10 for OHKO. + This is the number of shifts to apply, not a real multiplier + */ uint8_t Multiplier = (ctx->GetOption(RSK_DAMAGE_MULTIPLIER).Value() < 6) ? ctx->GetOption(RSK_DAMAGE_MULTIPLIER).Value() : 10; - return ((Hearts() << (2 + HasItem(RG_DOUBLE_DEFENSE))) >> Multiplier) + ((Hearts() << (2 + HasItem(RG_DOUBLE_DEFENSE))) % (1 << Multiplier) > 0); + //(Hearts() << (2 + HasItem(RG_DOUBLE_DEFENSE))) is quarter hearts after DD + //>> Multiplier halves on normal and does nothing on half, meaning we're working with half hearts on normal damage + return ((Hearts() << (2 + HasItem(RG_DOUBLE_DEFENSE))) >> Multiplier) + + //As 1 is a quarter heart, (1 << Multiplier) is effectivly half-hearts of unmodified damage + //Adds an extra hit if the damage is not exact lethal + ((Hearts() << (2 + HasItem(RG_DOUBLE_DEFENSE))) % (1 << Multiplier) > 0); } uint8_t Logic::Hearts(){ @@ -1791,6 +1839,10 @@ namespace Rando { LoweredWaterInsideBotw = false; OpenedWestRoomMQBotw = false; OpenedMiddleHoleMQBotw = false; + BrokeDeku1FWeb = false; + ClearedMQDekuSERoom = false; + MQDekuWaterRoomTorches = false; + PushedDekuBasementBlock = false; StopPerformanceTimer(PT_LOGIC_RESET); } diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index dc3ce31af..0e8a98875 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -136,6 +136,10 @@ class Logic { bool LoweredWaterInsideBotw = false; bool OpenedWestRoomMQBotw = false; bool OpenedMiddleHoleMQBotw = false; + bool BrokeDeku1FWeb = false; + bool ClearedMQDekuSERoom = false; + bool MQDekuWaterRoomTorches = false; + bool PushedDekuBasementBlock = false; /* --- END OF HELPERS AND LOCATION ACCESS --- */ @@ -150,7 +154,7 @@ class Logic { bool CanDoGlitch(GlitchType glitch); bool CanEquipSwap(RandomizerGet itemName); bool CanKillEnemy(RandomizerEnemy enemy, EnemyDistance distance = ED_CLOSE); - bool CanPassEnemy(RandomizerEnemy enemy); + bool CanPassEnemy(RandomizerEnemy enemy, EnemyDistance distance = ED_CLOSE); bool CanAvoidEnemy(RandomizerEnemy enemy); bool CanGetEnemyDrop(RandomizerEnemy enemy, EnemyDistance distance = ED_CLOSE, bool aboveLink = false); bool CanBreakMudWalls(); diff --git a/soh/soh/Enhancements/randomizer/randomizerTypes.h b/soh/soh/Enhancements/randomizer/randomizerTypes.h index 0cd02f4e7..ee3ae9d52 100644 --- a/soh/soh/Enhancements/randomizer/randomizerTypes.h +++ b/soh/soh/Enhancements/randomizer/randomizerTypes.h @@ -501,10 +501,18 @@ typedef enum { RR_DEKU_TREE_BASEMENT_UPPER, RR_DEKU_TREE_OUTSIDE_BOSS_ROOM, - RR_DEKU_TREE_MQ_LOBBY, + RR_DEKU_TREE_MQ_1F, + RR_DEKU_TREE_MQ_2F, + RR_DEKU_TREE_MQ_3F, + RR_DEKU_TREE_MQ_EYE_TARGET_ROOM, RR_DEKU_TREE_MQ_COMPASS_ROOM, + RR_DEKU_TREE_MQ_PAST_BOULDER_VINES, + RR_DEKU_TREE_MQ_BASEMENT, + RR_DEKU_TREE_MQ_BASEMENT_SOUTHEAST_ROOM, RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_FRONT, RR_DEKU_TREE_MQ_BASEMENT_WATER_ROOM_BACK, + RR_DEKU_TREE_MQ_BASEMENT_SOUTHWEST_ROOM, + RR_DEKU_TREE_MQ_BASEMENT_GRAVE_ROOM, RR_DEKU_TREE_MQ_BASEMENT_BACK_ROOM, RR_DEKU_TREE_MQ_BASEMENT_LEDGE, RR_DEKU_TREE_MQ_OUTSIDE_BOSS_ROOM, @@ -1207,7 +1215,7 @@ typedef enum { RC_DEKU_TREE_MQ_AFTER_SPINNING_LOG_CHEST, RC_DEKU_TREE_MQ_DEKU_SCRUB, RC_DEKU_TREE_MQ_GS_LOBBY, - RC_DEKU_TREE_MQ_GS_COMPASS_ROOM, + RC_DEKU_TREE_MQ_GS_PAST_BOULDER_VINES, RC_DEKU_TREE_MQ_GS_BASEMENT_GRAVES_ROOM, RC_DEKU_TREE_MQ_GS_BASEMENT_BACK_ROOM, RC_DEKU_TREE_QUEEN_GOHMA_HEART, @@ -2806,7 +2814,7 @@ typedef enum { RHT_DEKU_TREE_MQ_AFTER_SPINNING_LOG_CHEST, RHT_DEKU_TREE_MQ_DEKU_SCRUB, RHT_DEKU_TREE_MQ_GS_LOBBY, - RHT_DEKU_TREE_MQ_GS_COMPASS_ROOM, + RHT_DEKU_TREE_MQ_GS_PAST_BOULDER_VINES, RHT_DEKU_TREE_MQ_GS_BASEMENT_GRAVES_ROOM, RHT_DEKU_TREE_MQ_GS_BASEMENT_BACK_ROOM, RHT_DEKU_TREE_QUEEN_GOHMA_HEART, @@ -4409,6 +4417,8 @@ typedef enum { RE_MAD_SCRUB, RE_BLUE_BUBBLE, RE_DEAD_HAND, + RE_DEKU_BABA, + RE_WITHERED_DEKU_BABA, } RandomizerEnemy; typedef enum {