diff --git a/soh/soh/Enhancements/randomizer/SeedContext.cpp b/soh/soh/Enhancements/randomizer/SeedContext.cpp index 9ac56f7770..ea696e5a16 100644 --- a/soh/soh/Enhancements/randomizer/SeedContext.cpp +++ b/soh/soh/Enhancements/randomizer/SeedContext.cpp @@ -16,6 +16,8 @@ #include "soh/Enhancements/randomizer/3drando/random.hpp" #include "soh/Enhancements/randomizer/randomizer.h" +#include + #include #include extern "C" { diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp index 641dff6e25..dd40ac1ece 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/kakariko.cpp @@ -196,7 +196,7 @@ void RegionTable_Init_Kakariko() { }, { //Locations LOCATION(RC_KAK_WINDMILL_FREESTANDING_POH, logic->CanUse(RG_BOOMERANG)), - LOCATION(RC_SONG_FROM_WINDMILL, logic->IsAdult && logic->HasItem(RG_FAIRY_OCARINA)), + LOCATION(RC_SONG_FROM_WINDMILL, logic->IsAdult && logic->CanUse(RG_FAIRY_OCARINA)), }, { //Exits ENTRANCE(RR_KAKARIKO_VILLAGE, true), @@ -228,7 +228,7 @@ void RegionTable_Init_Kakariko() { areaTable[RR_KAK_SHOOTING_GALLERY] = Region("Kak Shooting Gallery", SCENE_SHOOTING_GALLERY, {}, { //Locations - LOCATION(RC_KAK_SHOOTING_GALLERY_REWARD, logic->IsAdult && logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->CanUse(RG_FAIRY_BOW)), + LOCATION(RC_KAK_SHOOTING_GALLERY_REWARD, logic->IsAdult && logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->HasItem(RG_FAIRY_BOW)), LOCATION(RC_KAK_SHOOTING_GALLERY_RECTANGLE_SIGN, logic->IsAdult && logic->CanRead()), }, { //Exits @@ -247,7 +247,7 @@ void RegionTable_Init_Kakariko() { LOCATION(RC_KAK_POTION_SHOP_ITEM_8, logic->IsAdult && logic->HasItem(RG_SPEAK_HYLIAN) && GetCheckPrice() <= GetWalletCapacity()), }, { //Exits - ENTRANCE(RR_KAKARIKO_VILLAGE, true), + ENTRANCE(RR_KAKARIKO_VILLAGE, true), ENTRANCE(RR_KAK_BEHIND_POTION_SHOP, logic->IsAdult), }); diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp index 5d65b3afd1..180c4ae9ef 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/kokiri_forest.cpp @@ -197,7 +197,7 @@ void RegionTable_Init_KokiriForest() { areaTable[RR_KF_LINKS_HOUSE] = Region("KF Link's House", SCENE_LINKS_HOUSE, {}, { //Locations LOCATION(RC_KF_LINKS_HOUSE_COW, logic->IsAdult && logic->CanUse(RG_EPONAS_SONG) && logic->Get(LOGIC_LINKS_COW)), - LOCATION(RC_KF_LINKS_HOUSE_POT, logic->HasItem(RG_POWER_BRACELET)), // TODO: CanBreakPots() restricted + LOCATION(RC_KF_LINKS_HOUSE_POT, logic->CanBreakPots()), LOCATION(RC_KF_LINKS_HOUSE_SIGN, logic->CanRead()), }, { //Exits @@ -229,8 +229,8 @@ void RegionTable_Init_KokiriForest() { areaTable[RR_KF_HOUSE_OF_TWINS] = Region("KF House of Twins", SCENE_TWINS_HOUSE, {}, { //Locations - LOCATION(RC_KF_TWINS_HOUSE_POT_1, logic->HasItem(RG_POWER_BRACELET)), // TODO: CanBreakPots() restricted - LOCATION(RC_KF_TWINS_HOUSE_POT_2, logic->HasItem(RG_POWER_BRACELET)), // TODO: CanBreakPots() restricted + LOCATION(RC_KF_TWINS_HOUSE_POT_1, logic->CanBreakPots()), + LOCATION(RC_KF_TWINS_HOUSE_POT_2, logic->CanBreakPots()), }, { //Exits ENTRANCE(RR_KOKIRI_FOREST, true), @@ -238,8 +238,8 @@ void RegionTable_Init_KokiriForest() { areaTable[RR_KF_KNOW_IT_ALL_HOUSE] = Region("KF Know It All House", SCENE_KNOW_IT_ALL_BROS_HOUSE, {}, { // Locations - LOCATION(RC_KF_BROTHERS_HOUSE_POT_1, logic->HasItem(RG_POWER_BRACELET)), // TODO: CanBreakPots() restricted - LOCATION(RC_KF_BROTHERS_HOUSE_POT_2, logic->HasItem(RG_POWER_BRACELET)), // TODO: CanBreakPots() restricted + LOCATION(RC_KF_BROTHERS_HOUSE_POT_1, logic->CanBreakPots()), + LOCATION(RC_KF_BROTHERS_HOUSE_POT_2, logic->CanBreakPots()), }, { //Exits ENTRANCE(RR_KOKIRI_FOREST, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp index 1abe5fc54b..322592272d 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lake_hylia.cpp @@ -123,16 +123,30 @@ void RegionTable_Init_LakeHylia() { areaTable[RR_LH_LAB] = Region("LH Lab", SCENE_LAKESIDE_LABORATORY, {}, { //Locations - LOCATION(RC_LH_LAB_DIVE, (logic->HasItem(RG_GOLDEN_SCALE) || (ctx->GetTrickOption(RT_LH_LAB_DIVING) && logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_BRONZE_SCALE))) && logic->HasItem(RG_SPEAK_HYLIAN)), + LOCATION(RC_LH_LAB_DIVE, logic->HasItem(RG_GOLDEN_SCALE) && logic->HasItem(RG_SPEAK_HYLIAN)), LOCATION(RC_LH_TRADE_FROG, logic->IsAdult && logic->CanUse(RG_EYEBALL_FROG)), - LOCATION(RC_LH_GS_LAB_CRATE, logic->CanUse(RG_IRON_BOOTS) && logic->CanUse(RG_HOOKSHOT) && logic->CanBreakCrates()), - LOCATION(RC_LH_LAB_FRONT_RUPEE, logic->CanUse(RG_IRON_BOOTS) || logic->HasItem(RG_GOLDEN_SCALE)), - LOCATION(RC_LH_LAB_LEFT_RUPEE, logic->CanUse(RG_IRON_BOOTS) || logic->HasItem(RG_GOLDEN_SCALE)), - LOCATION(RC_LH_LAB_RIGHT_RUPEE, logic->CanUse(RG_IRON_BOOTS) || logic->HasItem(RG_GOLDEN_SCALE)), - LOCATION(RC_LH_LAB_CRATE, logic->CanUse(RG_IRON_BOOTS) && logic->CanBreakCrates()), + LOCATION(RC_LH_LAB_FRONT_RUPEE, logic->HasItem(RG_GOLDEN_SCALE)), + LOCATION(RC_LH_LAB_LEFT_RUPEE, logic->HasItem(RG_GOLDEN_SCALE)), + LOCATION(RC_LH_LAB_RIGHT_RUPEE, logic->HasItem(RG_GOLDEN_SCALE)), }, { //Exits - ENTRANCE(RR_LAKE_HYLIA, true), + ENTRANCE(RR_LAKE_HYLIA, true), + ENTRANCE(RR_LH_LAB_UNDERWATER, logic->CanUse(RG_IRON_BOOTS)), + }); + + //Assumes checking logic->CanUse(RG_IRON_BOOTS) on access + areaTable[RR_LH_LAB_UNDERWATER] = Region("LH Lab Underwater", SCENE_LAKESIDE_LABORATORY, {}, { + //Locations + //Assumes RR_LH_LAB access + LOCATION(RC_LH_LAB_DIVE, (ctx->GetTrickOption(RT_LH_LAB_DIVING) && logic->CanUse(RG_HOOKSHOT) && logic->HasItem(RG_BRONZE_SCALE)) && logic->HasItem(RG_SPEAK_HYLIAN)), + LOCATION(RC_LH_GS_LAB_CRATE, logic->CanUse(RG_HOOKSHOT) && logic->CanBreakCrates()), + LOCATION(RC_LH_LAB_FRONT_RUPEE, true), + LOCATION(RC_LH_LAB_LEFT_RUPEE, true), + LOCATION(RC_LH_LAB_RIGHT_RUPEE, true), + LOCATION(RC_LH_LAB_CRATE, logic->CanBreakCrates()), + }, { + //Exits + ENTRANCE(RR_LH_LAB, logic->HasItem(RG_BRONZE_SCALE)), }); // TODO: should some of these helpers be done via events instead? diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp index 400b3d98fe..9bbc38859c 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/lon_lon_ranch.cpp @@ -39,9 +39,9 @@ void RegionTable_Init_LonLonRanch() { areaTable[RR_LLR_TALONS_HOUSE] = Region("LLR Talons House", SCENE_LON_LON_BUILDINGS, {}, { //Locations LOCATION(RC_LLR_TALONS_CHICKENS, logic->HasItem(RG_CHILD_WALLET) && logic->HasItem(RG_SPEAK_HYLIAN) && logic->IsChild && logic->AtDay && logic->HasItem(RG_ZELDAS_LETTER) && logic->HasItem(RG_POWER_BRACELET)), - LOCATION(RC_LLR_TALONS_HOUSE_POT_1, logic->HasItem(RG_POWER_BRACELET) || logic->CanUseSword()), // TODO: CanBreakPots() restricted - LOCATION(RC_LLR_TALONS_HOUSE_POT_2, logic->HasItem(RG_POWER_BRACELET) || logic->CanUseSword()), // TODO: CanBreakPots() restricted - LOCATION(RC_LLR_TALONS_HOUSE_POT_3, logic->HasItem(RG_POWER_BRACELET) || logic->CanUseSword()), // TODO: CanBreakPots() restricted + LOCATION(RC_LLR_TALONS_HOUSE_POT_1, logic->CanBreakPots()), + LOCATION(RC_LLR_TALONS_HOUSE_POT_2, logic->CanBreakPots()), + LOCATION(RC_LLR_TALONS_HOUSE_POT_3, logic->CanBreakPots()), }, { //Exits ENTRANCE(RR_LON_LON_RANCH, true), diff --git a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp index 558c66539f..5b3935c74a 100644 --- a/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp +++ b/soh/soh/Enhancements/randomizer/location_access/overworld/market.cpp @@ -15,18 +15,18 @@ void RegionTable_Init_Market() { areaTable[RR_THE_MARKET] = Region("Market", SCENE_MARKET_DAY, {}, { //Locations //RANDOTODO add item avalibility to regions to remove need to hardcode logic in limited item use situations - LOCATION(RC_MARKET_GRASS_1, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), - LOCATION(RC_MARKET_GRASS_2, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), - LOCATION(RC_MARKET_GRASS_3, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), - LOCATION(RC_MARKET_GRASS_4, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), - LOCATION(RC_MARKET_GRASS_5, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), - LOCATION(RC_MARKET_GRASS_6, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), - LOCATION(RC_MARKET_GRASS_7, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), - LOCATION(RC_MARKET_GRASS_8, logic->IsChild && (logic->CanUseSword() || logic->HasItem(RG_GORONS_BRACELET))), - LOCATION(RC_MK_NEAR_BAZAAR_CRATE_1, logic->IsChild /*&& logic->CanRoll()*/), - LOCATION(RC_MK_NEAR_BAZAAR_CRATE_2, logic->IsChild /*&& logic->CanRoll()*/), - LOCATION(RC_MK_SHOOTING_GALLERY_CRATE_1, logic->IsChild /*&& logic->CanRoll()*/), - LOCATION(RC_MK_SHOOTING_GALLERY_CRATE_2, logic->IsChild /*&& logic->CanRoll()*/), + LOCATION(RC_MARKET_GRASS_1, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_MARKET_GRASS_2, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_MARKET_GRASS_3, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_MARKET_GRASS_4, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_MARKET_GRASS_5, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_MARKET_GRASS_6, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_MARKET_GRASS_7, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_MARKET_GRASS_8, logic->IsChild && logic->CanCutShrubs()), + LOCATION(RC_MK_NEAR_BAZAAR_CRATE_1, logic->IsChild && logic->CanBreakCrates()), + LOCATION(RC_MK_NEAR_BAZAAR_CRATE_2, logic->IsChild && logic->CanBreakCrates()), + LOCATION(RC_MK_SHOOTING_GALLERY_CRATE_1, logic->IsChild && logic->CanBreakCrates()), + LOCATION(RC_MK_SHOOTING_GALLERY_CRATE_2, logic->IsChild && logic->CanBreakCrates()), LOCATION(RC_MARKET_TREE, logic->IsChild && logic->CanBonkTrees()), LOCATION(RC_MKT_WONDER_DAY_1, logic->IsChild && logic->AtDay), LOCATION(RC_MKT_WONDER_DAY_2, logic->IsChild && logic->AtDay), @@ -247,9 +247,9 @@ void RegionTable_Init_Market() { areaTable[RR_MARKET_MAN_IN_GREEN_HOUSE] = Region("Market Man in Green House", SCENE_BACK_ALLEY_HOUSE, {}, { // Locations - LOCATION(RC_MK_BACK_ALLEY_HOUSE_POT_1, logic->HasItem(RG_POWER_BRACELET)), // TODO: CanBreakPots() restricted - LOCATION(RC_MK_BACK_ALLEY_HOUSE_POT_2, logic->HasItem(RG_POWER_BRACELET)), // TODO: CanBreakPots() restricted - LOCATION(RC_MK_BACK_ALLEY_HOUSE_POT_3, logic->HasItem(RG_POWER_BRACELET)), // TODO: CanBreakPots() restricted + LOCATION(RC_MK_BACK_ALLEY_HOUSE_POT_1, logic->CanBreakPots()), + LOCATION(RC_MK_BACK_ALLEY_HOUSE_POT_2, logic->CanBreakPots()), + LOCATION(RC_MK_BACK_ALLEY_HOUSE_POT_3, logic->CanBreakPots()), }, { //Exits ENTRANCE(RR_MARKET_BACK_ALLEY, true), diff --git a/soh/soh/Enhancements/randomizer/logic.cpp b/soh/soh/Enhancements/randomizer/logic.cpp index a8ccec4fe9..9621fcdf35 100644 --- a/soh/soh/Enhancements/randomizer/logic.cpp +++ b/soh/soh/Enhancements/randomizer/logic.cpp @@ -15,6 +15,7 @@ #include "soh/resource/type/scenecommand/SetTransitionActorList.h" #include "src/overlays/actors/ovl_En_Door/z_en_door.h" #include "src/overlays/actors/ovl_Door_Shutter/z_door_shutter.h" +#include "location_access.h" namespace Rando { @@ -299,12 +300,219 @@ bool Logic::HasItem(RandomizerGet itemName) { return false; } +/* based on sRestrictionFlags in z_parameter.c */ +bool Logic::ItemUseAllowed(RandomizerGet itemName) { + switch (itemName) { + case RG_KOKIRI_SWORD: + case RG_MASTER_SWORD: + case RG_GIANTS_KNIFE: + case RG_BIGGORON_SWORD: + return BAllowed(); + case RG_DEKU_SHIELD: + case RG_HYLIAN_SHIELD: + case RG_MIRROR_SHIELD: + case RG_GORON_TUNIC: + case RG_ZORA_TUNIC: + case RG_IRON_BOOTS: + case RG_HOVER_BOOTS: + case RG_MAGIC_SINGLE: + case RG_SILVER_GAUNTLETS: + case RG_GOLDEN_GAUNTLETS: + case RG_ZELDAS_LULLABY: + case RG_EPONAS_SONG: + case RG_PRELUDE_OF_LIGHT: + case RG_SARIAS_SONG: + case RG_SONG_OF_TIME: + case RG_BOLERO_OF_FIRE: + case RG_REQUIEM_OF_SPIRIT: + case RG_SONG_OF_STORMS: + case RG_MINUET_OF_FOREST: + case RG_SERENADE_OF_WATER: + case RG_NOCTURNE_OF_SHADOW: + case RG_CRAWL: + return true; + default: + break; + } + + // hacky fix for underwater sections TODO this properly with a flag in regions + if (CurrentRegionKey == RR_LH_LAB_UNDERWATER) { + return itemName == RG_HOOKSHOT || itemName == RG_LONGSHOT; + } + + switch (RegionTable(CurrentRegionKey)->scene) { + case SCENE_DEKU_TREE: + case SCENE_DODONGOS_CAVERN: + case SCENE_JABU_JABU: + case SCENE_FOREST_TEMPLE: + case SCENE_FIRE_TEMPLE: + case SCENE_WATER_TEMPLE: + case SCENE_SPIRIT_TEMPLE: + case SCENE_SHADOW_TEMPLE: + case SCENE_BOTTOM_OF_THE_WELL: + case SCENE_ICE_CAVERN: + case SCENE_ID_MAX: + return true; + case SCENE_HYRULE_FIELD: + case SCENE_GANONS_TOWER: + case SCENE_GERUDO_TRAINING_GROUND: + case SCENE_THIEVES_HIDEOUT: + case SCENE_INSIDE_GANONS_CASTLE: + case SCENE_GREAT_FAIRYS_FOUNTAIN_MAGIC: + case SCENE_FAIRYS_FOUNTAIN: + case SCENE_GREAT_FAIRYS_FOUNTAIN_SPELLS: + case SCENE_GROTTOS: + case SCENE_GRAVE_WITH_FAIRYS_FOUNTAIN: + case SCENE_REDEAD_GRAVE: + case SCENE_ROYAL_FAMILYS_TOMB: + case SCENE_KAKARIKO_VILLAGE: + case SCENE_GRAVEYARD: + case SCENE_ZORAS_RIVER: + case SCENE_KOKIRI_FOREST: + case SCENE_SACRED_FOREST_MEADOW: + case SCENE_LAKE_HYLIA: + case SCENE_ZORAS_DOMAIN: + case SCENE_ZORAS_FOUNTAIN: + case SCENE_GERUDO_VALLEY: + case SCENE_LOST_WOODS: + case SCENE_DESERT_COLOSSUS: + case SCENE_GERUDOS_FORTRESS: + case SCENE_HAUNTED_WASTELAND: + case SCENE_HYRULE_CASTLE: + case SCENE_DEATH_MOUNTAIN_TRAIL: + case SCENE_DEATH_MOUNTAIN_CRATER: + case SCENE_GORON_CITY: + case SCENE_LON_LON_RANCH: + case SCENE_OUTSIDE_GANONS_CASTLE: + return !(itemName == RG_FARORES_WIND); + case SCENE_GANONS_TOWER_COLLAPSE_INTERIOR: + case SCENE_INSIDE_GANONS_CASTLE_COLLAPSE: + case SCENE_GANONS_TOWER_COLLAPSE_EXTERIOR: + return !(itemName == RG_FARORES_WIND || itemName == RG_FAIRY_OCARINA || itemName == RG_OCARINA_OF_TIME); + case SCENE_CASTLE_COURTYARD_ZELDA: + return !(StaticData::restrictSpells.contains(itemName) || itemName == RG_FAIRY_OCARINA || + itemName == RG_OCARINA_OF_TIME); + case SCENE_DEKU_TREE_BOSS: + case SCENE_DODONGOS_CAVERN_BOSS: + case SCENE_JABU_JABU_BOSS: + case SCENE_FOREST_TEMPLE_BOSS: + case SCENE_FIRE_TEMPLE_BOSS: + case SCENE_WATER_TEMPLE_BOSS: + case SCENE_SPIRIT_TEMPLE_BOSS: + case SCENE_SHADOW_TEMPLE_BOSS: + case SCENE_GANONDORF_BOSS: + case SCENE_GANON_BOSS: + return !(StaticData::restrictTrade.contains(itemName) || itemName == RG_FARORES_WIND || + itemName == RG_FAIRY_OCARINA || itemName == RG_OCARINA_OF_TIME); + case SCENE_WINDMILL_AND_DAMPES_GRAVE: + return !(StaticData::restrictSpells.contains(itemName)); + case SCENE_MARKET_GUARD_HOUSE: + return !(StaticData::restrictSpells.contains(itemName) || itemName == RG_HOOKSHOT || + itemName == RG_LONGSHOT); + case SCENE_MARKET_ENTRANCE_DAY: // test + case SCENE_MARKET_ENTRANCE_NIGHT: + case SCENE_MARKET_ENTRANCE_RUINS: + case SCENE_BACK_ALLEY_DAY: + case SCENE_BACK_ALLEY_NIGHT: + case SCENE_MARKET_DAY: + case SCENE_MARKET_NIGHT: + case SCENE_MARKET_RUINS: + case SCENE_TEMPLE_OF_TIME_EXTERIOR_DAY: + case SCENE_TEMPLE_OF_TIME_EXTERIOR_NIGHT: + case SCENE_TEMPLE_OF_TIME_EXTERIOR_RUINS: + case SCENE_KNOW_IT_ALL_BROS_HOUSE: + case SCENE_TWINS_HOUSE: + case SCENE_MIDOS_HOUSE: + case SCENE_SARIAS_HOUSE: + case SCENE_KAKARIKO_CENTER_GUEST_HOUSE: + case SCENE_BACK_ALLEY_HOUSE: + case SCENE_BAZAAR: + case SCENE_KOKIRI_SHOP: + case SCENE_GORON_SHOP: + case SCENE_ZORA_SHOP: + case SCENE_POTION_SHOP_KAKARIKO: + case SCENE_BOMBCHU_SHOP: + case SCENE_HAPPY_MASK_SHOP: + case SCENE_LINKS_HOUSE: + case SCENE_DOG_LADY_HOUSE: + case SCENE_STABLE: + case SCENE_IMPAS_HOUSE: + case SCENE_LAKESIDE_LABORATORY: + case SCENE_CARPENTERS_TENT: + case SCENE_GRAVEKEEPERS_HUT: + case SCENE_TEMPLE_OF_TIME: + case SCENE_LON_LON_BUILDINGS: + case SCENE_HOUSE_OF_SKULLTULA: + return StaticData::allowBottleMaskTrade.contains(itemName) || itemName == RG_FAIRY_OCARINA || + itemName == RG_OCARINA_OF_TIME; + case SCENE_TREASURE_BOX_SHOP: + return StaticData::allowBottleMaskTrade.contains(itemName) || itemName == RG_LENS_OF_TRUTH; + case SCENE_POTION_SHOP_GRANNY: + return StaticData::allowBottleMaskTrade.contains(itemName); + case SCENE_SHOOTING_GALLERY: + case SCENE_CASTLE_COURTYARD_GUARDS_DAY: + case SCENE_CASTLE_COURTYARD_GUARDS_NIGHT: + case SCENE_BOMBCHU_BOWLING_ALLEY: + return StaticData::allowMasks.contains(itemName); + case SCENE_FISHING_POND: + return itemName == RG_FISHING_POLE; + default: + SPDLOG_INFO("ItemUseAllowed reached `default` with item {} in Scene {}.", static_cast(itemName), + static_cast(RegionTable(CurrentRegionKey)->scene)); + return true; + } +} + +bool Logic::BAllowed() { + // hacky fix for underwater sections TODO this properly with a flag in regions + if (CurrentRegionKey == RR_LH_LAB_UNDERWATER) { + return false; + } + + switch (RegionTable(CurrentRegionKey)->scene) { + case SCENE_TREASURE_BOX_SHOP: + case SCENE_KNOW_IT_ALL_BROS_HOUSE: + case SCENE_TWINS_HOUSE: + case SCENE_MIDOS_HOUSE: + case SCENE_SARIAS_HOUSE: + case SCENE_KAKARIKO_CENTER_GUEST_HOUSE: + case SCENE_BACK_ALLEY_HOUSE: + case SCENE_BAZAAR: + case SCENE_KOKIRI_SHOP: + case SCENE_GORON_SHOP: + case SCENE_ZORA_SHOP: + case SCENE_POTION_SHOP_KAKARIKO: + case SCENE_BOMBCHU_SHOP: + case SCENE_HAPPY_MASK_SHOP: + case SCENE_LINKS_HOUSE: + case SCENE_DOG_LADY_HOUSE: + case SCENE_STABLE: + case SCENE_IMPAS_HOUSE: + case SCENE_LAKESIDE_LABORATORY: + case SCENE_CARPENTERS_TENT: + case SCENE_GRAVEKEEPERS_HUT: + case SCENE_SHOOTING_GALLERY: + case SCENE_BOMBCHU_BOWLING_ALLEY: + case SCENE_POTION_SHOP_GRANNY: + case SCENE_CASTLE_COURTYARD_GUARDS_DAY: + case SCENE_CASTLE_COURTYARD_GUARDS_NIGHT: + case SCENE_FISHING_POND: + return false; + default: + return true; + } +} + // Can the passed in item be used? // RANDOTODO catch magic items explicitly and add an assert on miss. bool Logic::CanUse(RandomizerGet itemName) { if (!HasItem(itemName)) return false; + if (!ItemUseAllowed(itemName)) { + return false; + } + switch (itemName) { // Magic items case RG_MAGIC_SINGLE: @@ -392,28 +600,28 @@ bool Logic::CanUse(RandomizerGet itemName) { case RG_ZELDAS_LULLABY: case RG_EPONAS_SONG: case RG_PRELUDE_OF_LIGHT: - return HasItem(RG_FAIRY_OCARINA) && HasItem(RG_OCARINA_C_LEFT_BUTTON) && + return CanUse(RG_FAIRY_OCARINA) && HasItem(RG_OCARINA_C_LEFT_BUTTON) && HasItem(RG_OCARINA_C_RIGHT_BUTTON) && HasItem(RG_OCARINA_C_UP_BUTTON); case RG_SARIAS_SONG: - return HasItem(RG_FAIRY_OCARINA) && HasItem(RG_OCARINA_C_LEFT_BUTTON) && + return CanUse(RG_FAIRY_OCARINA) && HasItem(RG_OCARINA_C_LEFT_BUTTON) && HasItem(RG_OCARINA_C_RIGHT_BUTTON) && HasItem(RG_OCARINA_C_DOWN_BUTTON); case RG_SUNS_SONG: - return HasItem(RG_FAIRY_OCARINA) && HasItem(RG_OCARINA_C_RIGHT_BUTTON) && HasItem(RG_OCARINA_C_UP_BUTTON) && + return CanUse(RG_FAIRY_OCARINA) && HasItem(RG_OCARINA_C_RIGHT_BUTTON) && HasItem(RG_OCARINA_C_UP_BUTTON) && HasItem(RG_OCARINA_C_DOWN_BUTTON); case RG_SONG_OF_TIME: case RG_BOLERO_OF_FIRE: case RG_REQUIEM_OF_SPIRIT: - return HasItem(RG_FAIRY_OCARINA) && HasItem(RG_OCARINA_A_BUTTON) && HasItem(RG_OCARINA_C_RIGHT_BUTTON) && + return CanUse(RG_FAIRY_OCARINA) && HasItem(RG_OCARINA_A_BUTTON) && HasItem(RG_OCARINA_C_RIGHT_BUTTON) && HasItem(RG_OCARINA_C_DOWN_BUTTON); case RG_SONG_OF_STORMS: return HasItem(RG_FAIRY_OCARINA) && HasItem(RG_OCARINA_A_BUTTON) && HasItem(RG_OCARINA_C_UP_BUTTON) && HasItem(RG_OCARINA_C_DOWN_BUTTON); case RG_MINUET_OF_FOREST: - return HasItem(RG_FAIRY_OCARINA) && HasItem(RG_OCARINA_A_BUTTON) && HasItem(RG_OCARINA_C_LEFT_BUTTON) && + return CanUse(RG_FAIRY_OCARINA) && HasItem(RG_OCARINA_A_BUTTON) && HasItem(RG_OCARINA_C_LEFT_BUTTON) && HasItem(RG_OCARINA_C_RIGHT_BUTTON) && HasItem(RG_OCARINA_C_UP_BUTTON); case RG_SERENADE_OF_WATER: case RG_NOCTURNE_OF_SHADOW: - return HasItem(RG_FAIRY_OCARINA) && HasItem(RG_OCARINA_A_BUTTON) && HasItem(RG_OCARINA_C_LEFT_BUTTON) && + return CanUse(RG_FAIRY_OCARINA) && HasItem(RG_OCARINA_A_BUTTON) && HasItem(RG_OCARINA_C_LEFT_BUTTON) && HasItem(RG_OCARINA_C_RIGHT_BUTTON) && HasItem(RG_OCARINA_C_DOWN_BUTTON); // Misc. Items @@ -432,6 +640,10 @@ bool Logic::CanUse(RandomizerGet itemName) { case RG_BOTTLE_WITH_FAIRY: return Get(LOGIC_FAIRY_ACCESS); + case RG_FAIRY_OCARINA: + case RG_OCARINA_OF_TIME: + return true; + default: SPDLOG_INFO("CanUse reached `default` for {}. using HasItem is a minor Optimisation.", static_cast(itemName)); @@ -1730,6 +1942,53 @@ std::map BottleRandomizerGetToItemID = { uint32_t HookshotLookup[3] = { ITEM_NONE, ITEM_HOOKSHOT, ITEM_LONGSHOT }; uint32_t OcarinaLookup[3] = { ITEM_NONE, ITEM_OCARINA_FAIRY, ITEM_OCARINA_TIME }; +std::set StaticData::restrictFW = { RG_FARORES_WIND }; + +std::set StaticData::restrictSpells = { RG_FARORES_WIND, RG_DINS_FIRE, RG_NAYRUS_LOVE }; + +std::set StaticData::restrictTrade = { + RG_POCKET_EGG, RG_COJIRO, RG_ODD_MUSHROOM, RG_ODD_POTION, RG_POACHERS_SAW, + RG_BROKEN_SWORD, RG_PRESCRIPTION, RG_EYEBALL_FROG, RG_EYEDROPS, RG_CLAIM_CHECK, +}; + +std::set StaticData::allowMasks = { + RG_KEATON_MASK, RG_SKULL_MASK, RG_SPOOKY_MASK, RG_BUNNY_HOOD, RG_GORON_MASK, + RG_ZORA_MASK, RG_GERUDO_MASK, RG_MASK_OF_TRUTH, RG_WEIRD_EGG, RG_ZELDAS_LETTER, +}; + +std::set StaticData::allowBottleMaskTrade = { RG_KEATON_MASK, + RG_SKULL_MASK, + RG_SPOOKY_MASK, + RG_BUNNY_HOOD, + RG_GORON_MASK, + RG_ZORA_MASK, + RG_GERUDO_MASK, + RG_MASK_OF_TRUTH, + RG_WEIRD_EGG, + RG_ZELDAS_LETTER, + RG_POCKET_EGG, + RG_COJIRO, + RG_ODD_MUSHROOM, + RG_ODD_POTION, + RG_POACHERS_SAW, + RG_BROKEN_SWORD, + RG_PRESCRIPTION, + RG_EYEBALL_FROG, + RG_EYEDROPS, + RG_CLAIM_CHECK, + RG_EMPTY_BOTTLE, + RG_BOTTLE_WITH_MILK, + RG_BOTTLE_WITH_RED_POTION, + RG_BOTTLE_WITH_GREEN_POTION, + RG_BOTTLE_WITH_BLUE_POTION, + RG_BOTTLE_WITH_FAIRY, + RG_BOTTLE_WITH_FISH, + RG_BOTTLE_WITH_BLUE_FIRE, + RG_BOTTLE_WITH_BUGS, + RG_BOTTLE_WITH_POE, + RG_RUTOS_LETTER, + RG_BOTTLE_WITH_BIG_POE }; + void Logic::ApplyItemEffect(Item& item, bool state) { auto randoGet = item.GetRandomizerGet(); if (item.GetGIEntry()->objectId == OBJECT_GI_STICK) { diff --git a/soh/soh/Enhancements/randomizer/logic.h b/soh/soh/Enhancements/randomizer/logic.h index 91d019e76b..5764cae5e3 100644 --- a/soh/soh/Enhancements/randomizer/logic.h +++ b/soh/soh/Enhancements/randomizer/logic.h @@ -43,6 +43,8 @@ class Logic { bool CanUse(RandomizerGet itemName); bool HasProjectile(HasProjectileAge age); bool HasItem(RandomizerGet itemName); + bool ItemUseAllowed(RandomizerGet itemName); + bool BAllowed(); bool HasBossSoul(RandomizerGet itemName); bool CanOpenOverworldDoor(RandomizerGet itemName); bool SmallKeys(s16 scene, uint8_t requiredAmount); diff --git a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerRegion.h b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerRegion.h index b2ad74ca67..bde9ade881 100644 --- a/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerRegion.h +++ b/soh/soh/Enhancements/randomizer/randomizerEnums/RandomizerRegion.h @@ -70,6 +70,7 @@ RANDO_ENUM_ITEM(RR_LH_FROM_WATER_TEMPLE) RANDO_ENUM_ITEM(RR_LH_FISHING_ISLAND) RANDO_ENUM_ITEM(RR_LH_OWL_FLIGHT) RANDO_ENUM_ITEM(RR_LH_LAB) +RANDO_ENUM_ITEM(RR_LH_LAB_UNDERWATER) RANDO_ENUM_ITEM(RR_LH_FISHING_POND) RANDO_ENUM_ITEM(RR_LH_GROTTO) RANDO_ENUM_ITEM(RR_GERUDO_VALLEY) diff --git a/soh/soh/Enhancements/randomizer/static_data.h b/soh/soh/Enhancements/randomizer/static_data.h index 241f6794a0..eefe268194 100644 --- a/soh/soh/Enhancements/randomizer/static_data.h +++ b/soh/soh/Enhancements/randomizer/static_data.h @@ -2,12 +2,14 @@ #include #include +#include #include #include "randomizerTypes.h" #include "item.h" #include "location.h" namespace Rando { + /** * @brief Singleton for storing and accessing static Randomizer-related data * @@ -93,6 +95,12 @@ class StaticData { static std::vector normalBottles; static std::vector beanSouls; static std::vector overworldKeys; + static std::unordered_map> itemRestrictions; + static std::set restrictFW; + static std::set restrictSpells; + static std::set restrictTrade; + static std::set allowMasks; + static std::set allowBottleMaskTrade; StaticData(); ~StaticData();