Skip to content

Commit

Permalink
Merge 'More consistent handling of boss keys from key rings' (#2149)
Browse files Browse the repository at this point in the history
  • Loading branch information
cjohnson57 committed Feb 10, 2024
2 parents 7b03d98 + b7c735c commit 0254b16
Show file tree
Hide file tree
Showing 8 changed files with 57 additions and 27 deletions.
20 changes: 9 additions & 11 deletions ItemPool.py
Original file line number Diff line number Diff line change
Expand Up @@ -477,12 +477,12 @@ def get_pool_core(world: World) -> tuple[list[str], dict[str, Item]]:
pending_junk_pool.append('Magic Bean Pack')
if (world.settings.gerudo_fortress != "open"
and world.settings.shuffle_hideoutkeys in ('any_dungeon', 'overworld', 'keysanity', 'regional')):
if 'Thieves Hideout' in world.settings.key_rings and world.settings.gerudo_fortress != "fast":
if world.keyring('Thieves Hideout'):
pending_junk_pool.append('Small Key Ring (Thieves Hideout)')
else:
pending_junk_pool.append('Small Key (Thieves Hideout)')
if world.settings.shuffle_tcgkeys in ('any_dungeon', 'overworld', 'keysanity', 'regional'):
if 'Treasure Chest Game' in world.settings.key_rings:
if world.keyring('Treasure Chest Game'):
pending_junk_pool.append('Small Key Ring (Treasure Chest Game)')
else:
pending_junk_pool.append('Small Key (Treasure Chest Game)')
Expand All @@ -491,13 +491,13 @@ def get_pool_core(world: World) -> tuple[list[str], dict[str, Item]]:
if world.settings.shuffle_smallkeys in ('any_dungeon', 'overworld', 'keysanity', 'regional'):
for dungeon in ('Forest Temple', 'Fire Temple', 'Water Temple', 'Shadow Temple', 'Spirit Temple',
'Bottom of the Well', 'Gerudo Training Ground', 'Ganons Castle'):
if dungeon in world.settings.key_rings:
if world.keyring(dungeon):
pending_junk_pool.append(f"Small Key Ring ({dungeon})")
else:
pending_junk_pool.append(f"Small Key ({dungeon})")
if world.settings.shuffle_bosskeys in ('any_dungeon', 'overworld', 'keysanity', 'regional'):
for dungeon in ('Forest Temple', 'Fire Temple', 'Water Temple', 'Shadow Temple', 'Spirit Temple'):
if not world.settings.keyring_give_bk or dungeon not in world.settings.key_rings or world.settings.shuffle_smallkeys == 'vanilla':
if not world.keyring_give_bk(dungeon):
pending_junk_pool.append(f"Boss Key ({dungeon})")
if world.settings.shuffle_ganon_bosskey in ('any_dungeon', 'overworld', 'keysanity', 'regional'):
pending_junk_pool.append('Boss Key (Ganons Castle)')
Expand Down Expand Up @@ -688,13 +688,13 @@ def get_pool_core(world: World) -> tuple[list[str], dict[str, Item]]:
or world.settings.gerudo_fortress == 'fast' and location.name != 'Hideout 1 Torch Jail Gerudo Key'):
item = IGNORE_LOCATION
shuffle_item = False
if shuffle_item and world.settings.gerudo_fortress == 'normal' and 'Thieves Hideout' in world.settings.key_rings:
if shuffle_item and world.keyring('Thieves Hideout'):
item = get_junk_item()[0] if vanilla_items_processed[location.vanilla_item] else 'Small Key Ring (Thieves Hideout)'

# Treasure Chest Game Key Shuffle
elif location.vanilla_item != 'Piece of Heart (Treasure Chest Game)' and location.scene == 0x10:
if world.settings.shuffle_tcgkeys in ('regional', 'overworld', 'any_dungeon', 'keysanity'):
if 'Treasure Chest Game' in world.settings.key_rings and location.vanilla_item == 'Small Key (Treasure Chest Game)':
if world.keyring('Treasure Chest Game') and location.vanilla_item == 'Small Key (Treasure Chest Game)':
item = get_junk_item()[0] if location.name != 'Market Treasure Chest Game Salesman' else 'Small Key Ring (Treasure Chest Game)'
shuffle_item = True
elif world.settings.shuffle_tcgkeys == 'remove':
Expand Down Expand Up @@ -766,9 +766,7 @@ def get_pool_core(world: World) -> tuple[list[str], dict[str, Item]]:

# Boss Key
if location.vanilla_item == dungeon.item_name("Boss Key"):
if (world.settings.shuffle_smallkeys != 'vanilla'
and dungeon.name in world.settings.key_rings and world.settings.keyring_give_bk
and dungeon.name in ('Forest Temple', 'Fire Temple', 'Water Temple', 'Shadow Temple', 'Spirit Temple')):
if world.keyring_give_bk(dungeon.name):
item = get_junk_item()[0]
shuffle_item = True
else:
Expand All @@ -788,9 +786,9 @@ def get_pool_core(world: World) -> tuple[list[str], dict[str, Item]]:
dungeon_collection = dungeon.small_keys
if shuffle_setting == 'vanilla':
shuffle_item = False
elif dungeon.name in world.settings.key_rings and not vanilla_items_processed[location.vanilla_item]:
elif world.keyring(dungeon.name) and not vanilla_items_processed[location.vanilla_item]:
item = dungeon.item_name("Small Key Ring")
elif dungeon.name in world.settings.key_rings:
elif world.keyring(dungeon.name):
item = get_junk_item()[0]
shuffle_item = True
# Silver Rupee
Expand Down
21 changes: 19 additions & 2 deletions Patches.py
Original file line number Diff line number Diff line change
Expand Up @@ -2972,8 +2972,25 @@ def get_door_to_unlock(rom: Rom, actor_id: int, actor: int, scene: int) -> list[
return [0x00D4 + scene * 0x1C + 0x04 + flag_byte, flag_bits]

# Return Boss Doors that should be unlocked
if (world.settings.shuffle_bosskeys == 'remove' and scene != 0x0A) or (world.settings.shuffle_ganon_bosskey == 'remove' and scene == 0x0A) or (world.settings.shuffle_pots and scene == 0x0A and switch_flag == 0x15):
if actor_id == 0x002E and door_type == 0x05:
if actor_id == 0x002E and door_type == 0x05:
dungeons = {
0x00: 'Deku Tree',
0x01: 'Dodongos Cavern',
0x02: 'Jabu Jabus Belly',
0x03: 'Forest Temple',
0x04: 'Fire Temple',
0x05: 'Water Temple',
0x06: 'Spirit Temple',
0x07: 'Shadow Temple',
0x0A: 'Ganons Castle',
}
if scene in dungeons and world.keyring_give_bk(dungeons[scene]):
setting = world.settings.shuffle_smallkeys
elif scene == 0x0A:
setting = world.settings.shuffle_ganon_bosskey
else:
setting = world.settings.shuffle_bosskeys
if setting == 'remove' or (world.settings.shuffle_pots and scene == 0x0A and switch_flag == 0x15):
return [0x00D4 + scene * 0x1C + 0x04 + flag_byte, flag_bits]

return get_actor_list(rom, get_door_to_unlock)
Expand Down
7 changes: 0 additions & 7 deletions Plandomizer.py
Original file line number Diff line number Diff line change
Expand Up @@ -1102,13 +1102,6 @@ def configure_effective_starting_items(self, worlds: list[World], world: World)
self.skipped_locations.append(loc)
if loc.item is not None and world.id == loc.item.world.id:
add_starting_item_with_ammo(items, loc.item.name)
# With small keysy, key rings, and key rings give boss key, but boss keysy
# is not on, boss keys are still required in the game to open boss doors.
for dungeon in world.dungeons:
if (dungeon.name in world.settings.key_rings and dungeon.name != 'Ganons Castle'
and dungeon.shuffle_smallkeys == 'remove' and dungeon.shuffle_bosskeys != 'remove'
and world.settings.keyring_give_bk and len(dungeon.boss_key) > 0):
items[dungeon.boss_key[0].name] = StarterRecord(1)

effective_adult_trade_item_index = -1
effective_child_trade_item_index = -1
Expand Down
3 changes: 3 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,9 @@ issue. You should always Hard Reset to avoid this issue entirely.
* Fix an issue in the Co-op hint distribution which caused seed generation failures for some settings.
* Fix bug which causes `Maps and Compasses Give Information` to fail when logic is set to `Glitched`.

#### Other Changes
* Removing small keys for a dungeon with key rings and `Key Rings give Boss Keys` enabled will now open the boss door instead of giving the player the boss key.

### 8.0

#### New Features
Expand Down
2 changes: 1 addition & 1 deletion SaveContext.py
Original file line number Diff line number Diff line change
Expand Up @@ -404,7 +404,7 @@ def give_item(self, world: World, item: str, count: int = 1) -> None:
'total_keys.tcg': 6,
},
}[dungeon]
if world.settings.keyring_give_bk:
if world.keyring_give_bk(dungeon):
bk_names = {
"Forest Temple": 'dungeon_items.forest.boss_key',
"Fire Temple": 'dungeon_items.fire.boss_key',
Expand Down
8 changes: 4 additions & 4 deletions State.py
Original file line number Diff line number Diff line change
Expand Up @@ -151,9 +151,9 @@ def guarantee_hint(self) -> bool:
def collect(self, item: Item) -> None:
if item.solver_id is None:
raise Exception(f"Item '{item.name}' lacks a `solver_id` and can not be used in `State.collect()`.")
if 'Small Key Ring' in item.name and self.world.settings.keyring_give_bk:
if 'Small Key Ring' in item.name:
dungeon_name = item.name[:-1].split(' (', 1)[1]
if dungeon_name in ('Forest Temple', 'Fire Temple', 'Water Temple', 'Shadow Temple', 'Spirit Temple'):
if self.world.keyring_give_bk(dungeon_name):
bk = f'Boss Key ({dungeon_name})'
self.solv_items[ItemInfo.solver_ids[escape_name(bk)]] = 1
if item.alias and item.alias_id is not None:
Expand All @@ -165,9 +165,9 @@ def collect(self, item: Item) -> None:
def remove(self, item: Item) -> None:
if item.solver_id is None:
raise Exception(f"Item '{item.name}' lacks a `solver_id` and can not be used in `State.remove()`.")
if 'Small Key Ring' in item.name and self.world.settings.keyring_give_bk:
if 'Small Key Ring' in item.name:
dungeon_name = item.name[:-1].split(' (', 1)[1]
if dungeon_name in ('Forest Temple', 'Fire Temple', 'Water Temple', 'Shadow Temple', 'Spirit Temple'):
if self.world.keyring_give_bk(dungeon_name):
bk = f'Boss Key ({dungeon_name})'
self.solv_items[ItemInfo.solver_ids[escape_name(bk)]] = 0
if item.alias and item.alias_id is not None and self.solv_items[item.alias_id] > 0:
Expand Down
21 changes: 20 additions & 1 deletion World.py
Original file line number Diff line number Diff line change
Expand Up @@ -456,7 +456,7 @@ def resolve_random_settings(self) -> None:
elif self.settings.dungeon_shortcuts_choice == 'all':
self.settings.dungeon_shortcuts = dungeons

# Determine area with keyring
# Determine areas with keyrings
areas = ['Thieves Hideout', 'Treasure Chest Game', 'Forest Temple', 'Fire Temple', 'Water Temple', 'Shadow Temple', 'Spirit Temple', 'Bottom of the Well', 'Gerudo Training Ground', 'Ganons Castle']
if self.settings.key_rings_choice == 'random':
self.settings.key_rings = random.sample(areas, random.randint(0, len(areas)))
Expand Down Expand Up @@ -1159,6 +1159,25 @@ def region_has_shortcuts(self, region_name: str) -> bool:
dungeon_name = HintArea.at(region).dungeon_name
return dungeon_name in self.settings.dungeon_shortcuts

# Returns whether the given dungeon has a keyring.
def keyring(self, dungeon_name: str) -> bool:
if dungeon_name in ('Forest Temple', 'Fire Temple', 'Water Temple', 'Shadow Temple', 'Spirit Temple', 'Bottom of the Well', 'Gerudo Training Ground', 'Ganons Castle'):
return dungeon_name in self.settings.key_rings and self.settings.shuffle_smallkeys != 'vanilla'
elif dungeon_name == 'Thieves Hideout':
return dungeon_name in self.settings.key_rings and self.settings.gerudo_fortress != 'fast' and self.settings.shuffle_hideoutkeys != 'vanilla'
elif dungeon_name == 'Treasure Chest Game':
return dungeon_name in self.settings.key_rings and self.settings.shuffle_tcgkeys != 'vanilla'
else:
raise ValueError(f'Attempted to check keyring for unknown dungeon {dungeon_name!r}')

# Returns whether the given dungeon has a keyring that includes the boss key.
def keyring_give_bk(self, dungeon_name: str) -> bool:
return (
dungeon_name in ('Forest Temple', 'Fire Temple', 'Water Temple', 'Shadow Temple', 'Spirit Temple')
and self.settings.keyring_give_bk
and self.keyring(dungeon_name)
)

# Function to run exactly once after placing items in drop locations for each world
# Sets all Drop locations to a unique name in order to avoid name issues and to identify locations in the spoiler
def set_drop_location_names(self):
Expand Down
2 changes: 1 addition & 1 deletion version.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
__version__ = '8.0.15'
__version__ = '8.0.16'

# This is a supplemental version number for branches based off of main dev.
supplementary_version = 0
Expand Down

0 comments on commit 0254b16

Please sign in to comment.