Fix Guard pots being breakable with hookshot in logic (#6671)

implement ItemUseAllowed
This commit is contained in:
Pepper0ni
2026-06-14 18:15:01 +01:00
committed by GitHub
parent 360f4882d2
commit 041295f058
10 changed files with 325 additions and 39 deletions
@@ -16,6 +16,8 @@
#include "soh/Enhancements/randomizer/3drando/random.hpp"
#include "soh/Enhancements/randomizer/randomizer.h"
#include <vector>
#include <fstream>
#include <spdlog/spdlog.h>
extern "C" {
@@ -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),
});
@@ -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),
@@ -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?
@@ -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),
@@ -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),
+265 -6
View File
@@ -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<uint32_t>(itemName),
static_cast<uint32_t>(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<uint32_t>(itemName));
@@ -1730,6 +1942,53 @@ std::map<uint32_t, uint32_t> 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<RandomizerGet> StaticData::restrictFW = { RG_FARORES_WIND };
std::set<RandomizerGet> StaticData::restrictSpells = { RG_FARORES_WIND, RG_DINS_FIRE, RG_NAYRUS_LOVE };
std::set<RandomizerGet> 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<RandomizerGet> 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<RandomizerGet> 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) {
+2
View File
@@ -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);
@@ -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)
@@ -2,12 +2,14 @@
#include <array>
#include <map>
#include <set>
#include <unordered_map>
#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<RandomizerGet> normalBottles;
static std::vector<RandomizerGet> beanSouls;
static std::vector<RandomizerGet> overworldKeys;
static std::unordered_map<SceneID, std::set<RandomizerGet>> itemRestrictions;
static std::set<RandomizerGet> restrictFW;
static std::set<RandomizerGet> restrictSpells;
static std::set<RandomizerGet> restrictTrade;
static std::set<RandomizerGet> allowMasks;
static std::set<RandomizerGet> allowBottleMaskTrade;
StaticData();
~StaticData();