Merge branch 'OverworldShuffleDev' into OverworldShuffle

This commit is contained in:
codemann8
2023-04-03 08:33:55 -05:00
31 changed files with 681 additions and 388 deletions

View File

@@ -1299,11 +1299,7 @@ class CollectionState(object):
def can_flute(self, player): def can_flute(self, player):
if self.world.mode[player] == 'standard' and not self.has('Zelda Delivered', player): if self.world.mode[player] == 'standard' and not self.has('Zelda Delivered', player):
return False # can't flute in rain state return False # can't flute in rain state
if any(map(lambda i: i.name in ['Ocarina', 'Ocarina (Activated)'], self.world.precollected_items)): return self.has('Ocarina (Activated)', player)
return True
lw = self.world.get_region('Kakariko Area', player)
return self.has('Ocarina (Activated)', player) or (self.has('Ocarina', player) and lw.can_reach(self)
and self.is_not_bunny(lw, player))
def can_melt_things(self, player): def can_melt_things(self, player):
return self.has('Fire Rod', player) or (self.has('Bombos', player) and self.has_sword(player)) return self.has('Fire Rod', player) or (self.has('Bombos', player) and self.has_sword(player))
@@ -2876,6 +2872,7 @@ class Spoiler(object):
'dungeon_counters': self.world.dungeon_counters, 'dungeon_counters': self.world.dungeon_counters,
'item_pool': self.world.difficulty, 'item_pool': self.world.difficulty,
'item_functionality': self.world.difficulty_adjustments, 'item_functionality': self.world.difficulty_adjustments,
'beemizer': self.world.beemizer,
'gt_crystals': self.world.crystals_needed_for_gt, 'gt_crystals': self.world.crystals_needed_for_gt,
'ganon_crystals': self.world.crystals_needed_for_ganon, 'ganon_crystals': self.world.crystals_needed_for_ganon,
'open_pyramid': self.world.open_pyramid, 'open_pyramid': self.world.open_pyramid,
@@ -3055,25 +3052,33 @@ class Spoiler(object):
if self.world.players > 1: if self.world.players > 1:
outfile.write('\nPlayer %d: %s\n' % (player, self.world.get_player_names(player))) outfile.write('\nPlayer %d: %s\n' % (player, self.world.get_player_names(player)))
outfile.write('Settings Code:'.ljust(line_width) + '%s\n' % self.metadata["code"][player]) outfile.write('Settings Code:'.ljust(line_width) + '%s\n' % self.metadata["code"][player])
outfile.write('Logic:'.ljust(line_width) + '%s\n' % self.metadata['logic'][player]) outfile.write('\n')
outfile.write('Mode:'.ljust(line_width) + '%s\n' % self.metadata['mode'][player]) outfile.write('Mode:'.ljust(line_width) + '%s\n' % self.metadata['mode'][player])
outfile.write('Swords:'.ljust(line_width) + '%s\n' % self.metadata['weapons'][player]) outfile.write('Logic:'.ljust(line_width) + '%s\n' % self.metadata['logic'][player])
outfile.write('Goal:'.ljust(line_width) + '%s\n' % self.metadata['goal'][player]) outfile.write('Goal:'.ljust(line_width) + '%s\n' % self.metadata['goal'][player])
if self.metadata['goal'][player] in ['triforcehunt', 'trinity', 'ganonhunt']: if self.metadata['goal'][player] in ['triforcehunt', 'trinity', 'ganonhunt']:
outfile.write('Triforce Pieces Required:'.ljust(line_width) + '%s\n' % self.metadata['triforcegoal'][player]) outfile.write('Triforce Pieces Required:'.ljust(line_width) + '%s\n' % self.metadata['triforcegoal'][player])
outfile.write('Triforce Pieces Total:'.ljust(line_width) + '%s\n' % self.metadata['triforcepool'][player]) outfile.write('Triforce Pieces Total:'.ljust(line_width) + '%s\n' % self.metadata['triforcepool'][player])
outfile.write('Crystals Required for GT:'.ljust(line_width) + '%s\n' % str(self.world.crystals_gt_orig[player])) outfile.write('Crystals Required for GT:'.ljust(line_width) + '%s\n' % str(self.world.crystals_gt_orig[player]))
outfile.write('Crystals Required for Ganon:'.ljust(line_width) + '%s\n' % str(self.world.crystals_ganon_orig[player])) outfile.write('Crystals Required for Ganon:'.ljust(line_width) + '%s\n' % str(self.world.crystals_ganon_orig[player]))
outfile.write('Swords:'.ljust(line_width) + '%s\n' % self.metadata['weapons'][player])
outfile.write('\n')
outfile.write('Accessibility:'.ljust(line_width) + '%s\n' % self.metadata['accessibility'][player]) outfile.write('Accessibility:'.ljust(line_width) + '%s\n' % self.metadata['accessibility'][player])
outfile.write('Restricted Boss Items:'.ljust(line_width) + '%s\n' % self.metadata['restricted_boss_items'][player]) outfile.write('Restricted Boss Items:'.ljust(line_width) + '%s\n' % self.metadata['restricted_boss_items'][player])
outfile.write('Difficulty:'.ljust(line_width) + '%s\n' % self.metadata['item_pool'][player])
outfile.write('Item Functionality:'.ljust(line_width) + '%s\n' % self.metadata['item_functionality'][player]) outfile.write('Item Functionality:'.ljust(line_width) + '%s\n' % self.metadata['item_functionality'][player])
outfile.write('Difficulty:'.ljust(line_width) + '%s\n' % self.metadata['item_pool'][player])
outfile.write('Flute Mode:'.ljust(line_width) + '%s\n' % self.metadata['flute_mode'][player]) outfile.write('Flute Mode:'.ljust(line_width) + '%s\n' % self.metadata['flute_mode'][player])
outfile.write('Bow Mode:'.ljust(line_width) + '%s\n' % self.metadata['bow_mode'][player]) outfile.write('Bow Mode:'.ljust(line_width) + '%s\n' % self.metadata['bow_mode'][player])
outfile.write('Take Any Caves:'.ljust(line_width) + '%s\n' % self.metadata['take_any'][player]) outfile.write('Beemizer:'.ljust(line_width) + '%s\n' % self.metadata['beemizer'][player])
outfile.write('Shopsanity:'.ljust(line_width) + '%s\n' % yn(self.metadata['shopsanity'][player]))
outfile.write('Bombbag:'.ljust(line_width) + '%s\n' % yn(self.metadata['bombbag'][player])) outfile.write('Bombbag:'.ljust(line_width) + '%s\n' % yn(self.metadata['bombbag'][player]))
outfile.write('Pseudoboots:'.ljust(line_width) + '%s\n' % yn(self.metadata['pseudoboots'][player])) outfile.write('\n')
outfile.write('Shopsanity:'.ljust(line_width) + '%s\n' % yn(self.metadata['shopsanity'][player]))
outfile.write('Bonk Drops:'.ljust(line_width) + '%s\n' % yn(self.metadata['bonk_drops'][player]))
outfile.write('Pottery Mode:'.ljust(line_width) + '%s\n' % self.metadata['pottery'][player])
outfile.write('Pot Shuffle (Legacy):'.ljust(line_width) + '%s\n' % yn(self.metadata['potshuffle'][player]))
outfile.write('Enemy Drop Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['dropshuffle'][player]))
outfile.write('Take Any Caves:'.ljust(line_width) + '%s\n' % self.metadata['take_any'][player])
outfile.write('\n')
outfile.write('Overworld Layout Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['ow_shuffle'][player]) outfile.write('Overworld Layout Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['ow_shuffle'][player])
if self.metadata['ow_shuffle'][player] != 'vanilla': if self.metadata['ow_shuffle'][player] != 'vanilla':
outfile.write('Free Terrain:'.ljust(line_width) + '%s\n' % yn(self.metadata['ow_terrain'][player])) outfile.write('Free Terrain:'.ljust(line_width) + '%s\n' % yn(self.metadata['ow_terrain'][player]))
@@ -3083,35 +3088,37 @@ class Spoiler(object):
outfile.write('OW Tile Flip (Mixed):'.ljust(line_width) + '%s\n' % yn(self.metadata['ow_mixed'][player])) outfile.write('OW Tile Flip (Mixed):'.ljust(line_width) + '%s\n' % yn(self.metadata['ow_mixed'][player]))
outfile.write('Whirlpool Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['ow_whirlpool'][player])) outfile.write('Whirlpool Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['ow_whirlpool'][player]))
outfile.write('Flute Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['ow_fluteshuffle'][player]) outfile.write('Flute Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['ow_fluteshuffle'][player])
outfile.write('Bonk Drops:'.ljust(line_width) + '%s\n' % yn(self.metadata['bonk_drops'][player])) outfile.write('\n')
outfile.write('Entrance Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['shuffle'][player]) outfile.write('Entrance Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['shuffle'][player])
if self.metadata['shuffle'][player] != 'vanilla': if self.metadata['shuffle'][player] != 'vanilla':
outfile.write('Shuffle GT/Ganon:'.ljust(line_width) + '%s\n' % yn(self.metadata['shuffleganon'][player])) outfile.write('Shuffle GT/Ganon:'.ljust(line_width) + '%s\n' % yn(self.metadata['shuffleganon'][player]))
outfile.write('Shuffle Links:'.ljust(line_width) + '%s\n' % yn(self.metadata['shufflelinks'][player])) outfile.write('Shuffle Links:'.ljust(line_width) + '%s\n' % yn(self.metadata['shufflelinks'][player]))
outfile.write('Shuffle Tavern:'.ljust(line_width) + '%s\n' % yn(self.metadata['shuffletavern'][player])) outfile.write('Shuffle Tavern:'.ljust(line_width) + '%s\n' % yn(self.metadata['shuffletavern'][player]))
outfile.write('Pyramid Hole Pre-opened:'.ljust(line_width) + '%s\n' % self.metadata['open_pyramid'][player])
if self.metadata['shuffle'][player] != 'vanilla' or self.metadata['ow_mixed'][player]: if self.metadata['shuffle'][player] != 'vanilla' or self.metadata['ow_mixed'][player]:
outfile.write('Overworld Map:'.ljust(line_width) + '%s\n' % self.metadata['overworld_map'][player]) outfile.write('Overworld Map:'.ljust(line_width) + '%s\n' % self.metadata['overworld_map'][player])
outfile.write('Pyramid Hole Pre-opened:'.ljust(line_width) + '%s\n' % self.metadata['open_pyramid'][player]) outfile.write('\n')
outfile.write('Map Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['mapshuffle'][player]))
outfile.write('Compass Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['compassshuffle'][player]))
outfile.write('Small Key Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['keyshuffle'][player])
outfile.write('Big Key Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['bigkeyshuffle'][player]))
outfile.write('Key Logic Algorithm:'.ljust(line_width) + '%s\n' % self.metadata['key_logic'][player])
outfile.write('\n')
outfile.write('Door Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['door_shuffle'][player]) outfile.write('Door Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['door_shuffle'][player])
if self.metadata['door_shuffle'][player] != 'vanilla': if self.metadata['door_shuffle'][player] != 'vanilla':
outfile.write('Intensity:'.ljust(line_width) + '%s\n' % self.metadata['intensity'][player]) outfile.write('Intensity:'.ljust(line_width) + '%s\n' % self.metadata['intensity'][player])
outfile.write('Door Type Mode:'.ljust(line_width) + '%s\n' % self.metadata['door_type_mode'][player]) outfile.write('Door Type Mode:'.ljust(line_width) + '%s\n' % self.metadata['door_type_mode'][player])
outfile.write('Trap Door Mode:'.ljust(line_width) + '%s\n' % self.metadata['trap_door_mode'][player]) outfile.write('Trap Door Mode:'.ljust(line_width) + '%s\n' % self.metadata['trap_door_mode'][player])
outfile.write('Key Logic Algorithm:'.ljust(line_width) + '%s\n' % self.metadata['key_logic'][player])
outfile.write('Decouple Doors:'.ljust(line_width) + '%s\n' % yn(self.metadata['decoupledoors'][player])) outfile.write('Decouple Doors:'.ljust(line_width) + '%s\n' % yn(self.metadata['decoupledoors'][player]))
outfile.write('Experimental:'.ljust(line_width) + '%s\n' % yn(self.metadata['experimental'][player])) outfile.write('Experimental:'.ljust(line_width) + '%s\n' % yn(self.metadata['experimental'][player]))
outfile.write('Dungeon Counters:'.ljust(line_width) + '%s\n' % self.metadata['dungeon_counters'][player]) outfile.write('Dungeon Counters:'.ljust(line_width) + '%s\n' % self.metadata['dungeon_counters'][player])
outfile.write('Enemy Drop Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['dropshuffle'][player])) outfile.write('\n')
outfile.write('Pottery Mode:'.ljust(line_width) + '%s\n' % self.metadata['pottery'][player])
outfile.write('Pot Shuffle (Legacy):'.ljust(line_width) + '%s\n' % yn(self.metadata['potshuffle'][player]))
outfile.write('Map Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['mapshuffle'][player]))
outfile.write('Compass Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['compassshuffle'][player]))
outfile.write('Small Key Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['keyshuffle'][player])
outfile.write('Big Key Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['bigkeyshuffle'][player]))
outfile.write('Boss Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['boss_shuffle'][player]) outfile.write('Boss Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['boss_shuffle'][player])
outfile.write('Enemy Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['enemy_shuffle'][player]) outfile.write('Enemy Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['enemy_shuffle'][player])
outfile.write('Enemy Health:'.ljust(line_width) + '%s\n' % self.metadata['enemy_health'][player]) outfile.write('Enemy Health:'.ljust(line_width) + '%s\n' % self.metadata['enemy_health'][player])
outfile.write('Enemy Damage:'.ljust(line_width) + '%s\n' % self.metadata['enemy_damage'][player]) outfile.write('Enemy Damage:'.ljust(line_width) + '%s\n' % self.metadata['enemy_damage'][player])
outfile.write('\n')
outfile.write('Pseudoboots:'.ljust(line_width) + '%s\n' % yn(self.metadata['pseudoboots'][player]))
outfile.write('Hints:'.ljust(line_width) + '%s\n' % yn(self.metadata['hints'][player])) outfile.write('Hints:'.ljust(line_width) + '%s\n' % yn(self.metadata['hints'][player]))
outfile.write('Race:'.ljust(line_width) + '%s\n' % yn(self.world.settings.world_rep['meta']['race'])) outfile.write('Race:'.ljust(line_width) + '%s\n' % yn(self.world.settings.world_rep['meta']['race']))

View File

@@ -1,5 +1,11 @@
# Changelog # Changelog
## 0.3.0.5
- Major reorganization to GUI Options
- Corrected various fake world behavior in glitched modes
- Fixed various issues with glitched mode logic
- Various minor logic fixes
## 0.3.0.4 ## 0.3.0.4
- \~Merged in DR v1.2.0.14~ - \~Merged in DR v1.2.0.14~
- Fixed issue with enemy drops on OW enemies - Fixed issue with enemy drops on OW enemies

8
Gui.py
View File

@@ -144,14 +144,14 @@ def guiMain(args=None):
self.pages["randomizer"].pages["entrance"] = entrando_page(self.pages["randomizer"].notebook) self.pages["randomizer"].pages["entrance"] = entrando_page(self.pages["randomizer"].notebook)
self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["entrance"], text="Entrances") self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["entrance"], text="Entrances")
# Dungeons
self.pages["randomizer"].pages["dungeon"] = dungeon_page(self.pages["randomizer"].notebook)
self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["dungeon"], text="Dungeons")
# Enemizer # Enemizer
self.pages["randomizer"].pages["enemizer"],self.settings = enemizer_page(self.pages["randomizer"].notebook,self.settings) self.pages["randomizer"].pages["enemizer"],self.settings = enemizer_page(self.pages["randomizer"].notebook,self.settings)
self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["enemizer"], text="Enemizer") self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["enemizer"], text="Enemizer")
# Dungeon Shuffle
self.pages["randomizer"].pages["dungeon"] = dungeon_page(self.pages["randomizer"].notebook)
self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["dungeon"], text="Dungeon Shuffle")
# Multiworld # Multiworld
# self.pages["randomizer"].pages["multiworld"],self.settings = multiworld_page(self.pages["randomizer"].notebook,self.settings) # self.pages["randomizer"].pages["multiworld"],self.settings = multiworld_page(self.pages["randomizer"].notebook,self.settings)
# self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["multiworld"], text="Multiworld") # self.pages["randomizer"].notebook.add(self.pages["randomizer"].pages["multiworld"], text="Multiworld")

View File

@@ -212,6 +212,20 @@ def generate_itempool(world, player):
loc.locked = True loc.locked = True
loc.forced_item = loc.item loc.forced_item = loc.item
if not world.is_tile_swapped(0x18, player):
region = world.get_region('Kakariko Area',player)
loc = Location(player, "Flute Activation", parent=region)
region.locations.append(loc)
world.dynamic_locations.append(loc)
world.clear_location_cache()
world.push_item(loc, ItemFactory('Ocarina (Activated)', player), False)
loc.event = True
loc.locked = True
loc.forced_item = loc.item
world.get_location('Ganon', player).event = True world.get_location('Ganon', player).event = True
world.get_location('Ganon', player).locked = True world.get_location('Ganon', player).locked = True
world.push_item(world.get_location('Agahnim 1', player), ItemFactory('Beat Agahnim 1', player), False) world.push_item(world.get_location('Agahnim 1', player), ItemFactory('Beat Agahnim 1', player), False)
@@ -1433,6 +1447,9 @@ def make_customizer_pool(world, player):
place_item('Master Sword Pedestal', 'Triforce') place_item('Master Sword Pedestal', 'Triforce')
guaranteed_items = alwaysitems + ['Magic Mirror', 'Moon Pearl'] guaranteed_items = alwaysitems + ['Magic Mirror', 'Moon Pearl']
if world.is_tile_swapped(0x18, player) or world.flute_mode[player] == 'active':
guaranteed_items.remove('Ocarina')
guaranteed_items.append('Ocarina (Activated)')
missing_items = [] missing_items = []
if world.shopsanity[player]: if world.shopsanity[player]:
guaranteed_items.extend(['Blue Potion', 'Green Potion', 'Red Potion']) guaranteed_items.extend(['Blue Potion', 'Green Potion', 'Red Potion'])

View File

@@ -785,7 +785,7 @@ def create_playthrough(world):
# get locations containing progress items # get locations containing progress items
prog_locations = [location for location in world.get_filled_locations() if location.item.advancement prog_locations = [location for location in world.get_filled_locations() if location.item.advancement
or world.goal[location.player] == 'completionist'] or world.goal[location.player] == 'completionist']
optional_locations = ['Trench 1 Switch', 'Trench 2 Switch', 'Ice Block Drop', 'Skull Star Tile'] optional_locations = ['Trench 1 Switch', 'Trench 2 Switch', 'Ice Block Drop', 'Skull Star Tile', 'Flute Activation']
optional_locations.extend(['Hyrule Castle Courtyard Tree Pull', 'Mountain Entry Area Tree Pull']) # adding pre-aga tree pulls optional_locations.extend(['Hyrule Castle Courtyard Tree Pull', 'Mountain Entry Area Tree Pull']) # adding pre-aga tree pulls
optional_locations.extend(['Lumberjack Area Crab Drop', 'South Pass Area Crab Drop']) # adding pre-aga bush crabs optional_locations.extend(['Lumberjack Area Crab Drop', 'South Pass Area Crab Drop']) # adding pre-aga bush crabs
state_cache = [None] state_cache = [None]

View File

@@ -1182,6 +1182,7 @@ OWTileRegions = bidict({
'East Dark Death Mountain (Top)': 0x45, 'East Dark Death Mountain (Top)': 0x45,
'East Dark Death Mountain (Bottom Left)': 0x45, 'East Dark Death Mountain (Bottom Left)': 0x45,
'East Dark Death Mountain (Bottom)': 0x45, 'East Dark Death Mountain (Bottom)': 0x45,
'East Dark Death Mountain (Bushes)': 0x45,
'Dark Death Mountain Ledge': 0x45, 'Dark Death Mountain Ledge': 0x45,
'Dark Death Mountain Isolated Ledge': 0x45, 'Dark Death Mountain Isolated Ledge': 0x45,
'Dark Death Mountain Floating Island': 0x45, 'Dark Death Mountain Floating Island': 0x45,

View File

@@ -512,5 +512,5 @@ mirror_clips = [
mirror_offsets = [ mirror_offsets = [
(['DM Offset Mirror', 'DDM Offset Mirror'], ['West Death Mountain (Bottom)', 'West Dark Death Mountain (Bottom)'], ['Hyrule Castle Courtyard Northeast', 'Pyramid Crack'], ['Pyramid Area', 'Hyrule Castle Courtyard']), (['DM Offset Mirror', 'DDM Offset Mirror'], ['West Death Mountain (Bottom)', 'West Dark Death Mountain (Bottom)'], ['Hyrule Castle Courtyard Northeast', 'Pyramid Crack'], ['Pyramid Area', 'Hyrule Castle Courtyard']),
(['DM To HC Ledge Offset Mirror', 'DDM To HC Ledge Offset Mirror'], ['West Death Mountain (Bottom)', 'West Dark Death Mountain (Bottom)'], ['Hyrule Castle Ledge', None], ['Pyramid Area', None]) (['DM To HC Ledge Offset Mirror', 'DDM To Pyramid Offset Mirror'], ['West Death Mountain (Bottom)', 'West Dark Death Mountain (Bottom)'], ['Hyrule Castle Ledge', 'Pyramid Area'], ['Pyramid Area', 'Hyrule Castle Area'])
] ]

View File

@@ -7,7 +7,7 @@ from OWEdges import OWTileRegions, OWEdgeGroups, OWEdgeGroupsTerrain, OWExitType
from OverworldGlitchRules import create_owg_connections from OverworldGlitchRules import create_owg_connections
from Utils import bidict from Utils import bidict
version_number = '0.3.0.4' version_number = '0.3.0.5'
# branch indicator is intentionally different across branches # branch indicator is intentionally different across branches
version_branch = '' version_branch = ''
@@ -973,27 +973,26 @@ def can_reach_smith(world, player):
region = world.get_region(region_name, player) region = world.get_region(region_name, player)
for exit in region.exits: for exit in region.exits:
if not found and exit.connected_region is not None: if not found and exit.connected_region is not None:
if any(map(lambda i: i.name in ['Ocarina', 'Ocarina (Activated)'], world.precollected_items)) and exit.spot_type == 'Flute': if exit.spot_type == 'Flute':
fluteregion = exit.connected_region if any(map(lambda i: i.name == 'Ocarina (Activated)', world.precollected_items)):
for flutespot in fluteregion.exits: for flutespot in exit.connected_region.exits:
if flutespot.connected_region and flutespot.connected_region.name not in explored_regions: if flutespot.connected_region and flutespot.connected_region.name not in explored_regions:
explore_region(flutespot.connected_region.name, flutespot.connected_region) explore_region(flutespot.connected_region.name, flutespot.connected_region)
elif exit.connected_region.name not in explored_regions \
and exit.connected_region.type in [RegionType.LightWorld, RegionType.DarkWorld] \
and exit.access_rule(blank_state):
explore_region(exit.connected_region.name, exit.connected_region)
elif exit.name == 'Sanctuary S':
sanc_region = exit.connected_region
if len(sanc_region.exits) and sanc_region.exits[0].name == 'Sanctuary Exit':
explore_region(sanc_region.exits[0].connected_region.name, sanc_region.exits[0].connected_region)
elif exit.connected_region.name == 'Blacksmiths Hut' and exit.access_rule(blank_state): elif exit.connected_region.name == 'Blacksmiths Hut' and exit.access_rule(blank_state):
found = True found = True
return
elif exit.connected_region.name not in explored_regions:
if (region.type == RegionType.Dungeon and exit.connected_region.name.endswith(' Portal')) \
or (exit.connected_region.type in [RegionType.LightWorld, RegionType.DarkWorld] \
and exit.access_rule(blank_state)):
explore_region(exit.connected_region.name, exit.connected_region)
blank_state = CollectionState(world) blank_state = CollectionState(world)
if world.mode[player] == 'standard': if world.mode[player] == 'standard':
blank_state.collect(ItemFactory('Zelda Delivered', player), True) blank_state.collect(ItemFactory('Zelda Delivered', player), True)
if world.logic[player] in ['noglitches', 'minorglitches'] and world.get_region('Frog Prison', player).type == (RegionType.DarkWorld if not invFlag else RegionType.LightWorld): if world.logic[player] in ['noglitches', 'minorglitches'] and not world.is_tile_swapped(0x29, player):
blank_state.collect(ItemFactory('Titans Mitts', player), True) blank_state.collect(ItemFactory('Titans Mitts', player), True)
blank_state.collect(ItemFactory('Moon Pearl', player), True)
found = False found = False
explored_regions = list() explored_regions = list()
@@ -1004,6 +1003,10 @@ def can_reach_smith(world, player):
explore_region(start_region) explore_region(start_region)
if not found: if not found:
if not invFlag: if not invFlag:
if world.intensity[player] >= 3 and world.doorShuffle[player] != 'vanilla':
sanc_mirror = world.get_entrance('Sanctuary Mirror Route', player)
explore_region(sanc_mirror.connected_region.name, sanc_mirror.connected_region)
else:
explore_region('Sanctuary') explore_region('Sanctuary')
else: else:
explore_region('Dark Sanctuary Hint') explore_region('Dark Sanctuary Hint')
@@ -1089,7 +1092,7 @@ def build_accessible_region_list(world, start_region, player, build_copy_world=F
region = base_world.get_region(region_name, player) region = base_world.get_region(region_name, player)
for exit in region.exits: for exit in region.exits:
if exit.connected_region is not None: if exit.connected_region is not None:
if any(map(lambda i: i.name in ['Ocarina', 'Ocarina (Activated)'], base_world.precollected_items)) and exit.spot_type == 'Flute': if any(map(lambda i: i.name == 'Ocarina (Activated)', base_world.precollected_items)) and exit.spot_type == 'Flute':
fluteregion = exit.connected_region fluteregion = exit.connected_region
for flutespot in fluteregion.exits: for flutespot in fluteregion.exits:
if flutespot.connected_region and flutespot.connected_region.name not in explored_regions: if flutespot.connected_region and flutespot.connected_region.name not in explored_regions:
@@ -1448,11 +1451,6 @@ mandatory_connections = [('Old Man S&Q', 'Old Man House'),
('Bomber Corner Pier', 'Bomber Corner Area'), ('Bomber Corner Pier', 'Bomber Corner Area'),
# OWG In-Bounds Connections # OWG In-Bounds Connections
('Stone Bridge EC Cliff Water Drop', 'Stone Bridge Water'), #fake flipper
('Tree Line WC Cliff Water Drop', 'Tree Line Water'), #fake flipper,
('Hammer Bridge EC Cliff Water Drop', 'Hammer Bridge Water'), #fake flipper
('Dark Tree Line WC Cliff Water Drop', 'Dark Tree Line Water'), #fake flipper
('Ice Lake Northeast Pier Hop', 'Ice Lake Northeast Bank'), ('Ice Lake Northeast Pier Hop', 'Ice Lake Northeast Bank'),
('Ice Lake Moat Bomb Jump', 'Ice Lake Moat') ('Ice Lake Moat Bomb Jump', 'Ice Lake Moat')
] ]
@@ -1552,12 +1550,20 @@ ow_connections = {
('Stone Bridge East Ledge Drop', 'Stone Bridge North Area'), # OWG ('Stone Bridge East Ledge Drop', 'Stone Bridge North Area'), # OWG
('Hammer Bridge North Ledge Drop', 'Hammer Bridge North Area'), # OWG ('Hammer Bridge North Ledge Drop', 'Hammer Bridge North Area'), # OWG
('Stone Bridge Cliff Ledge Drop', 'Stone Bridge South Area'), # OWG ('Stone Bridge Cliff Ledge Drop', 'Stone Bridge South Area'), # OWG
('Hammer Bridge South Cliff Ledge Drop', 'Hammer Bridge South Area') # OWG ('Hammer Bridge South Cliff Ledge Drop', 'Hammer Bridge South Area'), # OWG
('Stone Bridge EC Cliff Water Drop', 'Stone Bridge Water'), # fake flipper
('Hammer Bridge EC Cliff Water Drop', 'Hammer Bridge Water'), # fake flipper
('Tree Line WC Cliff Water Drop', 'Tree Line Water'), # fake flipper
('Dark Tree Line WC Cliff Water Drop', 'Dark Tree Line Water') # fake flipper
], [ ], [
('Stone Bridge East Ledge Drop', 'Hammer Bridge North Area'), # OWG ('Stone Bridge East Ledge Drop', 'Hammer Bridge North Area'), # OWG
('Hammer Bridge North Ledge Drop', 'Stone Bridge North Area'), # OWG ('Hammer Bridge North Ledge Drop', 'Stone Bridge North Area'), # OWG
('Stone Bridge Cliff Ledge Drop', 'Hammer Bridge South Area'), # OWG ('Stone Bridge Cliff Ledge Drop', 'Hammer Bridge South Area'), # OWG
('Hammer Bridge South Cliff Ledge Drop', 'Stone Bridge South Area') # OWG ('Hammer Bridge South Cliff Ledge Drop', 'Stone Bridge South Area'), # OWG
('Stone Bridge EC Cliff Water Drop', 'Hammer Bridge Water'), # fake flipper
('Hammer Bridge EC Cliff Water Drop', 'Stone Bridge Water'), # fake flipper
('Tree Line WC Cliff Water Drop', 'Dark Tree Line Water'), # fake flipper
('Dark Tree Line WC Cliff Water Drop', 'Tree Line Water') # fake flipper
]), ]),
0x2e: ([ 0x2e: ([
('Tree Line Ledge Drop', 'Tree Line Area'), # OWG ('Tree Line Ledge Drop', 'Tree Line Area'), # OWG

11
Rom.py
View File

@@ -38,7 +38,7 @@ from source.dungeon.RoomList import Room0127
JAP10HASH = '03a63945398191337e896e5771f77173' JAP10HASH = '03a63945398191337e896e5771f77173'
RANDOMIZERBASEHASH = '0a3d1d4bbec659013be5ed876c2658bd' RANDOMIZERBASEHASH = '92c16c60f26218c9aec838ce204c0b1e'
class JsonRom(object): class JsonRom(object):
@@ -936,6 +936,10 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
if should_be_bunny(old_man_house, world.mode[player]): if should_be_bunny(old_man_house, world.mode[player]):
rom.write_bytes(0x13fff4, [0xe4, 0x00]) rom.write_bytes(0x13fff4, [0xe4, 0x00])
old_man_cave = world.get_entrance('Old Man Cave Exit (East)', player)
if old_man_cave.connected_region.type == RegionType.DarkWorld:
rom.write_byte(0x13fff6, 0x40)
# patch doors # patch doors
if world.doorShuffle[player] not in ['vanilla', 'basic']: if world.doorShuffle[player] not in ['vanilla', 'basic']:
rom.write_byte(0x138002, 2) rom.write_byte(0x138002, 2)
@@ -1364,7 +1368,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False):
rom.write_byte(0x180212, warningflags) # Warning flags rom.write_byte(0x180212, warningflags) # Warning flags
# assorted fixes # assorted fixes
rom.write_byte(0x1800A2, 0x01 if world.fix_fake_world else 0x00) # remain in real dark world when dying in dark world dungeon before killing aga1 rom.write_byte(0x1800A2, 0x01 if world.fix_fake_world[player] else 0x00) # remain in real dark world when dying in dark world dungeon before killing aga1
rom.write_byte(0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence. rom.write_byte(0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence.
if world.is_atgt_swapped(player): if world.is_atgt_swapped(player):
rom.write_byte(0x180169, 0x02) # lock aga/ganon tower door with crystals in inverted rom.write_byte(0x180169, 0x02) # lock aga/ganon tower door with crystals in inverted
@@ -2308,10 +2312,9 @@ def write_strings(rom, world, player, team):
# of how many exist. This supports many settings well. # of how many exist. This supports many settings well.
items_to_hint = RelevantItems.copy() items_to_hint = RelevantItems.copy()
flute_item = 'Ocarina' flute_item = 'Ocarina'
if world.is_tile_swapped(0x18, player): if world.is_tile_swapped(0x18, player) or world.flute_mode[player] == 'active':
items_to_hint.remove(flute_item) items_to_hint.remove(flute_item)
flute_item = 'Ocarina (Activated)' flute_item = 'Ocarina (Activated)'
items_to_hint.append(flute_item)
if world.owShuffle[player] != 'vanilla' or world.owMixed[player]: if world.owShuffle[player] != 'vanilla' or world.owMixed[player]:
# Adding a guaranteed hint for the Flute in overworld shuffle. # Adding a guaranteed hint for the Flute in overworld shuffle.
this_location = world.find_items_not_key_only(flute_item, player) this_location = world.find_items_not_key_only(flute_item, player)

View File

@@ -70,6 +70,13 @@ def set_rules(world, player):
elif world.goal[player] == 'completionist': elif world.goal[player] == 'completionist':
add_rule(world.get_location('Ganon', player), lambda state: state.everything(player)) add_rule(world.get_location('Ganon', player), lambda state: state.everything(player))
if not world.is_tile_swapped(0x18, player):
if not world.is_copied_world:
# Commented out below, this would be needed for rando implementations where Inverted requires flute activation in bunny territory
# kak_region = self.world.get_region('Kakariko Area', player)
# add_rule(world.get_location('Flute Activation', player), lambda state: state.has('Ocarina', player) and state.is_not_bunny(kak_region, player))
add_rule(world.get_location('Flute Activation', player), lambda state: state.has('Ocarina', player))
# if swamp and dam have not been moved we require mirror for swamp palace # if swamp and dam have not been moved we require mirror for swamp palace
if not world.swamp_patch_required[player]: if not world.swamp_patch_required[player]:
add_rule(world.get_entrance('Swamp Lobby Moat', player), lambda state: state.has_Mirror(player)) add_rule(world.get_entrance('Swamp Lobby Moat', player), lambda state: state.has_Mirror(player))
@@ -1214,6 +1221,7 @@ def ow_bunny_rules(world, player):
add_bunny_rule(world.get_entrance('Lake Hylia Northeast Water Drop', player), player) add_bunny_rule(world.get_entrance('Lake Hylia Northeast Water Drop', player), player)
add_bunny_rule(world.get_entrance('Lake Hylia Central Water Drop', player), player) add_bunny_rule(world.get_entrance('Lake Hylia Central Water Drop', player), player)
add_bunny_rule(world.get_entrance('Lake Hylia Island Water Drop', player), player) add_bunny_rule(world.get_entrance('Lake Hylia Island Water Drop', player), player)
add_bunny_rule(world.get_entrance('Lake Hylia Water D Leave', player), player)
add_bunny_rule(world.get_entrance('Ice Cave SW', player), player) add_bunny_rule(world.get_entrance('Ice Cave SW', player), player)
add_bunny_rule(world.get_entrance('Octoballoon Water Drop', player), player) add_bunny_rule(world.get_entrance('Octoballoon Water Drop', player), player)
add_bunny_rule(world.get_entrance('Octoballoon Waterfall Water Drop', player), player) add_bunny_rule(world.get_entrance('Octoballoon Waterfall Water Drop', player), player)
@@ -1368,9 +1376,9 @@ def add_conditional_lamps(world, player):
is_dark = False is_dark = False
if not world.sewer_light_cone[player]: if not world.sewer_light_cone[player]:
is_dark = True is_dark = True
elif world.doorShuffle[player] != 'crossed' and not info['sewer']: elif world.doorShuffle[player] not in ['partitioned', 'crossed'] and not info['sewer']:
is_dark = True is_dark = True
elif world.doorShuffle[player] == 'crossed': elif world.doorShuffle[player] in ['partitioned', 'crossed']:
sewer_builder = world.dungeon_layouts[player]['Hyrule Castle'] sewer_builder = world.dungeon_layouts[player]['Hyrule Castle']
is_dark = region not in sewer_builder.master_sector.region_set() is_dark = region not in sewer_builder.master_sector.region_set()
if is_dark: if is_dark:

View File

@@ -860,16 +860,17 @@ OWNewDestination:
{ {
tya : sta $4202 : lda #16 : sta $4203 ;wait 8 cycles tya : sta $4202 : lda #16 : sta $4203 ;wait 8 cycles
rep #$20 : txa : nop : !add $4216 : tax ;a = offset to dest record rep #$20 : txa : nop : !add $4216 : tax ;a = offset to dest record
lda.w $0006,x : sta $06 ;set coord
lda.w $0008,x : sta $04 ;save dest OW slot/ID lda.w $0008,x : sta $04 ;save dest OW slot/ID
lda.w $000a,x : sta $84 ;VRAM ldy $20 : lda $418 : dec #2 : bpl + : ldy $22 : + sty $06
;;22 e0 e2 61c 61e - X ;;22 e0 e2 61c 61e - X
;;20 e6 e8 618 61a - Y ;;20 e6 e8 618 61a - Y
;keep current position if within incoming gap ;keep current position if within incoming gap
lda.w $0000,x : and #$01ff : pha : lda.w $0002,x : and #$01ff : pha lda.w $0000,x : and #$01ff : pha : lda.w $0002,x : and #$01ff : pha
ldy $20 : lda $418 : dec #2 : bpl + : ldy $22 LDA.l OWMode : AND.w #$0007 : BEQ .noLayoutShuffle ;temporary fix until VRAM issues are solved
+ tya : and #$01ff : cmp 3,s : !blt .adjustMainAxis lda.w $0006,x : sta $06 ;set coord
lda.w $000a,x : sta $84 ;VRAM
tya : and #$01ff : cmp 3,s : !blt .adjustMainAxis
dec : cmp 1,s : !bge .adjustMainAxis dec : cmp 1,s : !bge .adjustMainAxis
inc : pha : lda $06 : and #$fe00 : !add 1,s : sta $06 : pla inc : pha : lda $06 : and #$fe00 : !add 1,s : sta $06 : pla
@@ -885,6 +886,7 @@ OWNewDestination:
LDA $84 : SEC : SBC #$0400 : AND #$0F00 : ASL : XBA : STA $88 ; vram LDA $84 : SEC : SBC #$0400 : AND #$0F00 : ASL : XBA : STA $88 ; vram
LDA $84 : SEC : SBC #$0010 : AND #$003E : LSR : STA $86 LDA $84 : SEC : SBC #$0010 : AND #$003E : LSR : STA $86
.noLayoutShuffle
LDA.w $000F,X : AND.w #$00FF : STA.w $06FC ; position to walk to after transition (if non-zero) LDA.w $000F,X : AND.w #$00FF : STA.w $06FC ; position to walk to after transition (if non-zero)
LDY.w #$0000 LDY.w #$0000

Binary file not shown.

View File

@@ -1,4 +1,22 @@
{ {
"startHeader": {
"usestartinventory": {
"type": "checkbox",
"config": {
"padx": [10,0],
"pady": [10,10]
}
}
},
"customHeader": {
"usecustompool": {
"type": "checkbox",
"config": {
"padx": [10,0],
"pady": [10,10]
}
}
},
"itemList1": { "itemList1": {
"bow": { "bow": {
"type": "textbox", "type": "textbox",

View File

@@ -57,21 +57,7 @@
"randomizer.dungeon.smallkeyshuffle.wild": "Randomized", "randomizer.dungeon.smallkeyshuffle.wild": "Randomized",
"randomizer.dungeon.smallkeyshuffle.universal": "Universal", "randomizer.dungeon.smallkeyshuffle.universal": "Universal",
"randomizer.dungeon.bigkeyshuffle": "Big Keys", "randomizer.dungeon.bigkeyshuffle": "Big Keys",
"randomizer.dungeon.keydropshuffle": "Key Drop Shuffle (Legacy)",
"randomizer.dungeon.decoupledoors": "Decouple Doors", "randomizer.dungeon.decoupledoors": "Decouple Doors",
"randomizer.dungeon.dropshuffle": "Shuffle Enemy Key Drops",
"randomizer.dungeon.potshuffle": "Pot Shuffle (Legacy)",
"randomizer.dungeon.pottery": "Pottery",
"randomizer.dungeon.pottery.none": "None",
"randomizer.dungeon.pottery.keys": "Key Pots",
"randomizer.dungeon.pottery.cave": "Cave Pots",
"randomizer.dungeon.pottery.cavekeys": "Cave+Key Pots",
"randomizer.dungeon.pottery.reduced": "Reduced Dungeon Pots (Dynamic)",
"randomizer.dungeon.pottery.clustered": "Clustered Dungeon Pots (Dynamic)",
"randomizer.dungeon.pottery.nonempty": "Excludes Empty Pots",
"randomizer.dungeon.pottery.dungeon": "Dungeon Pots",
"randomizer.dungeon.pottery.lottery": "Lottery (All Pots and Large Blocks)",
"randomizer.dungeon.colorizepots": "Colorize Randomized Pots",
"randomizer.dungeon.dungeondoorshuffle": "Dungeon Door Shuffle", "randomizer.dungeon.dungeondoorshuffle": "Dungeon Door Shuffle",
"randomizer.dungeon.dungeondoorshuffle.vanilla": "Vanilla", "randomizer.dungeon.dungeondoorshuffle.vanilla": "Vanilla",
@@ -123,7 +109,7 @@
"randomizer.enemizer.enemyshuffle.none": "None", "randomizer.enemizer.enemyshuffle.none": "None",
"randomizer.enemizer.enemyshuffle.shuffled": "Shuffled", "randomizer.enemizer.enemyshuffle.shuffled": "Shuffled",
"randomizer.enemizer.enemyshuffle.random": "Random", "randomizer.enemizer.enemyshuffle.random": "Random",
"randomizer.enemizer.enemyshuffle.legacy": "Random (including Thieves)", "randomizer.enemizer.enemyshuffle.legacy": "Random (50/50 Thieves)",
"randomizer.enemizer.bossshuffle": "Boss Shuffle", "randomizer.enemizer.bossshuffle": "Boss Shuffle",
"randomizer.enemizer.bossshuffle.none": "None", "randomizer.enemizer.bossshuffle.none": "None",
@@ -168,8 +154,6 @@
"randomizer.overworld.whirlpool": "Whirlpool Shuffle", "randomizer.overworld.whirlpool": "Whirlpool Shuffle",
"randomizer.overworld.bonk_drops": "Bonk Drops",
"randomizer.overworld.overworldflute": "Flute Shuffle", "randomizer.overworld.overworldflute": "Flute Shuffle",
"randomizer.overworld.overworldflute.vanilla": "Vanilla", "randomizer.overworld.overworldflute.vanilla": "Vanilla",
"randomizer.overworld.overworldflute.balanced": "Balanced", "randomizer.overworld.overworldflute.balanced": "Balanced",
@@ -200,11 +184,6 @@
"randomizer.entrance.entranceshuffle.dungeonsfull": "Dungeons + Full", "randomizer.entrance.entranceshuffle.dungeonsfull": "Dungeons + Full",
"randomizer.entrance.entranceshuffle.dungeonssimple": "Dungeons + Simple", "randomizer.entrance.entranceshuffle.dungeonssimple": "Dungeons + Simple",
"randomizer.entrance.take_any": "Take Any Caves",
"randomizer.entrance.take_any.none": "None",
"randomizer.entrance.take_any.random": "Random",
"randomizer.entrance.take_any.fixed": "Fixed",
"randomizer.gameoptions.nobgm": "Disable Music & MSU-1", "randomizer.gameoptions.nobgm": "Disable Music & MSU-1",
"randomizer.gameoptions.quickswap": "L/R Quickswapping", "randomizer.gameoptions.quickswap": "L/R Quickswapping",
"randomizer.gameoptions.reduce_flashing": "Reduce Flashing", "randomizer.gameoptions.reduce_flashing": "Reduce Flashing",
@@ -253,9 +232,6 @@
"randomizer.generation.createrom": "Create Patched ROM", "randomizer.generation.createrom": "Create Patched ROM",
"randomizer.generation.calcplaythrough": "Calculate Playthrough", "randomizer.generation.calcplaythrough": "Calculate Playthrough",
"randomizer.generation.print_custom_yaml": "Print Customizer File", "randomizer.generation.print_custom_yaml": "Print Customizer File",
"randomizer.generation.usestartinventory": "Use Starting Inventory",
"randomizer.generation.usecustompool": "Use Custom Item Pool",
"randomizer.generation.race": "Generate \"Race\" ROM",
"randomizer.generation.saveonexit": "Save Settings on Exit", "randomizer.generation.saveonexit": "Save Settings on Exit",
"randomizer.generation.saveonexit.ask": "Ask Me", "randomizer.generation.saveonexit.ask": "Ask Me",
@@ -267,10 +243,10 @@
"randomizer.generation.rom.dialog.romfiles": "Rom Files", "randomizer.generation.rom.dialog.romfiles": "Rom Files",
"randomizer.generation.rom.dialog.allfiles": "All Files", "randomizer.generation.rom.dialog.allfiles": "All Files",
"randomizer.item.hints": "Include Helpful Hints", "randomizer.item.hints": "Hints",
"randomizer.item.race": "Generate \"Race\" ROM",
"randomizer.item.retro": "Retro mode", "randomizer.item.retro": "Retro mode",
"randomizer.item.pseudoboots": "Start with Pseudo Boots", "randomizer.item.pseudoboots": "Pseudoboots",
"randomizer.item.bombbag": "Bombbag",
"randomizer.item.worldstate": "World State", "randomizer.item.worldstate": "World State",
"randomizer.item.worldstate.standard": "Standard", "randomizer.item.worldstate.standard": "Standard",
@@ -322,25 +298,67 @@
"randomizer.item.weapons.swordless": "Swordless", "randomizer.item.weapons.swordless": "Swordless",
"randomizer.item.weapons.vanilla": "Vanilla", "randomizer.item.weapons.vanilla": "Vanilla",
"randomizer.item.beemizer": "Beemizer", "randomizer.item.sortingalgo": "Item Fill",
"randomizer.item.beemizer.0": "No Bee Traps", "randomizer.item.sortingalgo.balanced": "Balanced",
"randomizer.item.beemizer.1": "25% Bee Traps", "randomizer.item.sortingalgo.vanilla_fill": "Vanilla Fill",
"randomizer.item.beemizer.2": "40% Traps, 20% Refills", "randomizer.item.sortingalgo.major_only": "Major Location Restriction",
"randomizer.item.beemizer.3": "50% Traps, 50% Refills", "randomizer.item.sortingalgo.dungeon_only": "Dungeon Restriction",
"randomizer.item.beemizer.4": "100% Bee Traps", "randomizer.item.sortingalgo.district": "District Restriction",
"randomizer.item.itempool": "Item Pool", "randomizer.item.accessibility": "Accessibility",
"randomizer.item.itempool.normal": "Normal", "randomizer.item.accessibility.items": "100% Inventory",
"randomizer.item.itempool.hard": "Hard", "randomizer.item.accessibility.locations": "100% Locations",
"randomizer.item.itempool.expert": "Expert", "randomizer.item.accessibility.none": "Beatable",
"randomizer.item.shopsanity": "Shopsanity", "randomizer.item.restrict_boss_items": "Forbidden Boss Items",
"randomizer.item.restrict_boss_items.none": "None",
"randomizer.item.restrict_boss_items.mapcompass": "Map & Compass",
"randomizer.item.restrict_boss_items.dungeon": "Map & Compass & Keys",
"randomizer.item.itemfunction": "Item Functionality", "randomizer.item.itemfunction": "Item Functionality",
"randomizer.item.itemfunction.normal": "Normal", "randomizer.item.itemfunction.normal": "Normal",
"randomizer.item.itemfunction.hard": "Hard", "randomizer.item.itemfunction.hard": "Hard",
"randomizer.item.itemfunction.expert": "Expert", "randomizer.item.itemfunction.expert": "Expert",
"randomizer.item.timer": "Timer Setting",
"randomizer.item.timer.none": "No Timer",
"randomizer.item.timer.display": "Stopwatch",
"randomizer.item.timer.timed": "Timed",
"randomizer.item.timer.timed-ohko": "Timed OHKO",
"randomizer.item.timer.ohko": "OHKO",
"randomizer.item.timer.timed-countdown": "Timed Countdown",
"randomizer.item.shopsanity": "Shopsanity",
"randomizer.item.bonk_drops": "Bonk Drops",
"randomizer.item.pottery": "Pottery",
"randomizer.item.pottery.none": "None",
"randomizer.item.pottery.keys": "Key Pots",
"randomizer.item.pottery.cave": "Cave Pots",
"randomizer.item.pottery.cavekeys": "Cave+Key Pots",
"randomizer.item.pottery.reduced": "Reduced Dungeon Pots (Dynamic)",
"randomizer.item.pottery.clustered": "Clustered Dungeon Pots (Dynamic)",
"randomizer.item.pottery.nonempty": "Excludes Empty Pots",
"randomizer.item.pottery.dungeon": "Dungeon Pots",
"randomizer.item.pottery.lottery": "Lottery (All Pots and Large Blocks)",
"randomizer.item.colorizepots": "Colorize Randomized Pots",
"randomizer.item.potshuffle": "Pot Shuffle (Legacy)",
"randomizer.item.dropshuffle": "Shuffle Enemy Key Drops",
"randomizer.item.keydropshuffle": "Key Drop Shuffle (Legacy)",
"randomizer.item.take_any": "Take Any Caves",
"randomizer.item.take_any.none": "None",
"randomizer.item.take_any.random": "Random",
"randomizer.item.take_any.fixed": "Fixed",
"randomizer.item.itempool": "Item Pool",
"randomizer.item.itempool.normal": "Normal",
"randomizer.item.itempool.hard": "Hard",
"randomizer.item.itempool.expert": "Expert",
"randomizer.item.flute_mode": "Flute Mode", "randomizer.item.flute_mode": "Flute Mode",
"randomizer.item.flute_mode.normal": "Normal", "randomizer.item.flute_mode.normal": "Normal",
"randomizer.item.flute_mode.active": "Pre-Activated", "randomizer.item.flute_mode.active": "Pre-Activated",
@@ -351,30 +369,17 @@
"randomizer.item.bow_mode.retro": "Retro (Progressive)", "randomizer.item.bow_mode.retro": "Retro (Progressive)",
"randomizer.item.bow_mode.retro_silvers": "Retro + Silvers", "randomizer.item.bow_mode.retro_silvers": "Retro + Silvers",
"randomizer.item.timer": "Timer Setting", "randomizer.item.beemizer": "Beemizer",
"randomizer.item.timer.none": "No Timer", "randomizer.item.beemizer.0": "No Bee Traps",
"randomizer.item.timer.display": "Stopwatch", "randomizer.item.beemizer.1": "25% Bee Traps",
"randomizer.item.timer.timed": "Timed", "randomizer.item.beemizer.2": "40% Traps, 20% Refills",
"randomizer.item.timer.timed-ohko": "Timed OHKO", "randomizer.item.beemizer.3": "50% Traps, 50% Refills",
"randomizer.item.timer.ohko": "OHKO", "randomizer.item.beemizer.4": "100% Bee Traps",
"randomizer.item.timer.timed-countdown": "Timed Countdown",
"randomizer.item.accessibility": "Accessibility", "randomizer.item.bombbag": "Bombbag",
"randomizer.item.accessibility.items": "100% Inventory",
"randomizer.item.accessibility.locations": "100% Locations",
"randomizer.item.accessibility.none": "Beatable",
"randomizer.item.sortingalgo": "Item Sorting", "startinventory.usestartinventory": "Use Starting Inventory",
"randomizer.item.sortingalgo.balanced": "Balanced", "custom.usecustompool": "Use Custom Item Pool",
"randomizer.item.sortingalgo.vanilla_fill": "Vanilla Fill",
"randomizer.item.sortingalgo.major_only": "Major Location Restriction",
"randomizer.item.sortingalgo.dungeon_only": "Dungeon Restriction",
"randomizer.item.sortingalgo.district": "District Restriction",
"randomizer.item.restrict_boss_items": "Forbidden Boss Items",
"randomizer.item.restrict_boss_items.none": "None",
"randomizer.item.restrict_boss_items.mapcompass": "Map & Compass",
"randomizer.item.restrict_boss_items.dungeon": "Map & Compass & Keys",
"bottom.content.worlds": "Worlds", "bottom.content.worlds": "Worlds",
"bottom.content.names": "Player names", "bottom.content.names": "Player names",

View File

@@ -1,5 +1,13 @@
{ {
"keysanity": { "keysanity": {
"smallkeyshuffle": {
"type": "selectbox",
"options": [
"none",
"wild",
"universal"
]
},
"mapshuffle": { "type": "checkbox" }, "mapshuffle": { "type": "checkbox" },
"compassshuffle": { "type": "checkbox" }, "compassshuffle": { "type": "checkbox" },
"bigkeyshuffle": { "type": "checkbox" } "bigkeyshuffle": { "type": "checkbox" }

View File

@@ -1,12 +1,17 @@
{ {
"widgets": { "widgets": {
"smallkeyshuffle": { "key_logic_algorithm": {
"type": "selectbox", "type": "selectbox",
"default": "default",
"options": [ "options": [
"none", "default",
"wild", "partial",
"universal" "strict"
] ],
"config": {
"padx": [20,0],
"pady": [0,20]
}
}, },
"dungeondoorshuffle": { "dungeondoorshuffle": {
"type": "selectbox", "type": "selectbox",
@@ -28,7 +33,8 @@
"random" "random"
], ],
"config": { "config": {
"width": 45 "width": 45,
"padx": [20,0]
} }
}, },
"door_type_mode": { "door_type_mode": {
@@ -41,7 +47,8 @@
"chaos" "chaos"
], ],
"config": { "config": {
"width": 45 "width": 45,
"padx": [20,0]
} }
}, },
"trap_door_mode": { "trap_door_mode": {
@@ -54,41 +61,17 @@
"oneway" "oneway"
], ],
"config": { "config": {
"width": 30 "width": 30,
"padx": [20,0]
} }
}, },
"key_logic_algorithm": { "decoupledoors": {
"type": "selectbox", "type": "checkbox",
"default": "default",
"options": [
"default",
"partial",
"strict"
]
},
"decoupledoors": { "type": "checkbox" },
"keydropshuffle": { "type": "checkbox" },
"pottery": {
"type": "selectbox",
"default": "none",
"options": [
"none",
"keys",
"cave",
"cavekeys",
"reduced",
"clustered",
"nonempty",
"dungeon",
"lottery"
],
"config": { "config": {
"width": 35 "padx": [20,0],
"pady": [0,20]
} }
}, },
"colorizepots": { "type": "checkbox" },
"dropshuffle": { "type": "checkbox" },
"potshuffle": { "type": "checkbox" },
"experimental": { "type": "checkbox" }, "experimental": { "type": "checkbox" },
"dungeon_counters": { "dungeon_counters": {
"type": "selectbox", "type": "selectbox",

View File

@@ -1,38 +1,5 @@
{ {
"widgets": { "widgets": {
"openpyramid": {
"type": "selectbox",
"options": [
"auto",
"yes",
"no"
],
"config": {
"width": 10
}
},
"take_any": {
"type": "selectbox",
"options": [
"none",
"random",
"fixed"
]
},
"shuffleganon": { "type": "checkbox" },
"shufflelinks": { "type": "checkbox" },
"shuffletavern": { "type": "checkbox" },
"overworld_map": {
"type": "selectbox",
"options": [
"default",
"compass",
"map"
],
"config": {
"width": 45
}
},
"entranceshuffle": { "entranceshuffle": {
"type": "selectbox", "type": "selectbox",
"options": [ "options": [
@@ -47,6 +14,47 @@
"dungeonsfull", "dungeonsfull",
"dungeonssimple" "dungeonssimple"
] ]
},
"shuffleganon": {
"type": "checkbox",
"config": {
"padx": [20,0]
}
},
"shufflelinks": {
"type": "checkbox",
"config": {
"padx": [20,0]
}
},
"shuffletavern": {
"type": "checkbox",
"config": {
"padx": [20,0]
}
},
"openpyramid": {
"type": "selectbox",
"options": [
"auto",
"yes",
"no"
],
"config": {
"width": 6,
"pady": [20,0]
}
},
"overworld_map": {
"type": "selectbox",
"options": [
"default",
"compass",
"map"
],
"config": {
"width": 45
}
} }
} }
} }

View File

@@ -4,9 +4,6 @@
"bps": { "type": "checkbox" }, "bps": { "type": "checkbox" },
"createspoiler": { "type": "checkbox" }, "createspoiler": { "type": "checkbox" },
"calcplaythrough": { "type": "checkbox" }, "calcplaythrough": { "type": "checkbox" },
"print_custom_yaml": { "type": "checkbox" }, "print_custom_yaml": { "type": "checkbox" }
"usestartinventory": { "type": "checkbox" },
"usecustompool": { "type": "checkbox" },
"race": { "type": "checkbox" }
} }
} }

View File

@@ -1,12 +1,8 @@
{ {
"checkboxes": { "checkboxes": {
"retro": { "type": "checkbox" }, "hints": { "type": "checkbox" },
"bombbag": { "type": "checkbox" }, "pseudoboots": { "type": "checkbox" },
"shopsanity": { "type": "checkbox" }, "race": { "type": "checkbox" }
"hints": {
"type": "checkbox"
},
"pseudoboots": { "type": "checkbox" }
}, },
"leftItemFrame": { "leftItemFrame": {
"worldstate": { "worldstate": {
@@ -17,7 +13,10 @@
"open", "open",
"inverted", "inverted",
"retro" "retro"
] ],
"config": {
"command": "worldstate"
}
}, },
"logiclevel": { "logiclevel": {
"type": "selectbox", "type": "selectbox",
@@ -63,16 +62,38 @@
"swordless", "swordless",
"vanilla" "vanilla"
] ]
},
"beemizer": {
"type": "selectbox",
"options": [
"0", "1", "2", "3", "4"
]
} }
}, },
"rightItemFrame": { "rightItemFrame": {
"itempool": { "sortingalgo": {
"type": "selectbox",
"default": "balanced",
"options": [
"balanced",
"vanilla_fill",
"major_only",
"dungeon_only",
"district"
]
},
"accessibility": {
"type": "selectbox",
"options": [
"items",
"locations",
"none"
]
},
"restrict_boss_items": {
"type": "selectbox",
"default": "none",
"options": [
"none",
"mapcompass",
"dungeon"
]
},
"itemfunction": {
"type": "selectbox", "type": "selectbox",
"options": [ "options": [
"normal", "normal",
@@ -80,7 +101,78 @@
"expert" "expert"
] ]
}, },
"itemfunction": { "timer": {
"type": "selectbox",
"options": [
"none",
"display",
"timed",
"timed-ohko",
"ohko",
"timed-countdown"
]
}
},
"leftPoolHeader": {
"shopsanity": {
"type": "checkbox"
},
"bonk_drops": {
"type": "checkbox",
"default": false
}
},
"leftPoolFrame": {
"pottery": {
"type": "selectbox",
"default": "none",
"options": [
"none",
"keys",
"cave",
"cavekeys",
"reduced",
"clustered",
"nonempty",
"dungeon",
"lottery"
],
"config": {
"width": 35
}
},
"colorizepots": {
"type": "checkbox",
"config": {
"padx": [50,0]
}
},
"potshuffle": {
"type": "checkbox",
"config": {
"padx": [50,0]
}
},
"dropshuffle": {
"type": "checkbox"
},
"keydropshuffle": {
"type": "checkbox",
"config": {
"command": "keydropshuffle"
}
},
"take_any": {
"type": "selectbox",
"options": [
"none",
"random",
"fixed"
]
}
},
"rightPoolFrame": {
"itempool": {
"type": "selectbox", "type": "selectbox",
"options": [ "options": [
"normal", "normal",
@@ -104,44 +196,17 @@
"retro_silvers" "retro_silvers"
] ]
}, },
"timer": { "beemizer": {
"type": "selectbox", "type": "selectbox",
"options": [ "options": [
"none", "0", "1", "2", "3", "4"
"display",
"timed",
"timed-ohko",
"ohko",
"timed-countdown"
] ]
}, },
"accessibility": { "bombbag": {
"type": "selectbox", "type": "checkbox",
"options": [ "config": {
"items", "padx": [64,0]
"locations", }
"none"
]
},
"sortingalgo": {
"type": "selectbox",
"default": "balanced",
"options": [
"balanced",
"vanilla_fill",
"major_only",
"dungeon_only",
"district"
]
},
"restrict_boss_items": {
"type": "selectbox",
"default": "none",
"options": [
"none",
"mapcompass",
"dungeon"
]
} }
} }
} }

View File

@@ -1,10 +1,5 @@
{ {
"topOverworldFrame": { "topOverworldFrame": {},
"bonk_drops": {
"type": "checkbox",
"default": false
}
},
"leftOverworldFrame": { "leftOverworldFrame": {
"overworldshuffle": { "overworldshuffle": {
"type": "selectbox", "type": "selectbox",
@@ -28,11 +23,17 @@
}, },
"mixed": { "mixed": {
"type": "checkbox", "type": "checkbox",
"default": false "default": false,
"config": {
"padx": [79,0]
}
}, },
"whirlpool": { "whirlpool": {
"type": "checkbox", "type": "checkbox",
"default": false "default": false,
"config": {
"padx": [79,0]
}
}, },
"overworldflute": { "overworldflute": {
"type": "selectbox", "type": "selectbox",
@@ -41,17 +42,26 @@
"vanilla", "vanilla",
"balanced", "balanced",
"random" "random"
] ],
"config": {
"pady": [20,0]
}
} }
}, },
"rightOverworldFrame": { "rightOverworldFrame": {
"terrain": { "terrain": {
"type": "checkbox", "type": "checkbox",
"default": false "default": false,
"config": {
"pady": [3,0]
}
}, },
"keepsimilar": { "keepsimilar": {
"type": "checkbox", "type": "checkbox",
"default": false "default": false,
"config": {
"pady": [6,0]
}
} }
} }
} }

View File

@@ -56,25 +56,36 @@ SETTINGSTOPROCESS = {
"randomizer": { "randomizer": {
"item": { "item": {
"hints": "hints", "hints": "hints",
"retro": "retro",
"bombbag": "bombbag",
"shopsanity": "shopsanity",
"pseudoboots": "pseudoboots", "pseudoboots": "pseudoboots",
"race": "race",
"worldstate": "mode", "worldstate": "mode",
"logiclevel": "logic", "logiclevel": "logic",
"goal": "goal", "goal": "goal",
"crystals_gt": "crystals_gt", "crystals_gt": "crystals_gt",
"crystals_ganon": "crystals_ganon", "crystals_ganon": "crystals_ganon",
"weapons": "swords", "weapons": "swords",
"itempool": "difficulty",
"sortingalgo": "algorithm",
"accessibility": "accessibility",
"restrict_boss_items": "restrict_boss_items",
"itemfunction": "item_functionality", "itemfunction": "item_functionality",
"timer": "timer",
"shopsanity": "shopsanity",
"bonk_drops": "bonk_drops",
"pottery": "pottery",
"colorizepots": "colorizepots",
"potshuffle": "shufflepots",
"dropshuffle": "dropshuffle",
"keydropshuffle": "keydropshuffle",
"take_any": "take_any",
"itempool": "difficulty",
"flute_mode": "flute_mode", "flute_mode": "flute_mode",
"bow_mode": "bow_mode", "bow_mode": "bow_mode",
"timer": "timer",
"accessibility": "accessibility",
"sortingalgo": "algorithm",
"beemizer": "beemizer", "beemizer": "beemizer",
"restrict_boss_items": "restrict_boss_items" "bombbag": "bombbag"
}, },
"overworld": { "overworld": {
"overworldshuffle": "ow_shuffle", "overworldshuffle": "ow_shuffle",
@@ -83,17 +94,31 @@ SETTINGSTOPROCESS = {
"keepsimilar": "ow_keepsimilar", "keepsimilar": "ow_keepsimilar",
"mixed": "ow_mixed", "mixed": "ow_mixed",
"whirlpool": "ow_whirlpool", "whirlpool": "ow_whirlpool",
"bonk_drops": "bonk_drops",
"overworldflute": "ow_fluteshuffle" "overworldflute": "ow_fluteshuffle"
}, },
"entrance": { "entrance": {
"openpyramid": "openpyramid", "entranceshuffle": "shuffle",
"shuffleganon": "shuffleganon", "shuffleganon": "shuffleganon",
"shufflelinks": "shufflelinks", "shufflelinks": "shufflelinks",
"shuffletavern": "shuffletavern", "shuffletavern": "shuffletavern",
"entranceshuffle": "shuffle", "openpyramid": "openpyramid",
"overworld_map": "overworld_map", "overworld_map": "overworld_map",
"take_any": "take_any", },
"dungeon": {
"smallkeyshuffle": "keyshuffle",
"mapshuffle": "mapshuffle",
"compassshuffle": "compassshuffle",
"bigkeyshuffle": "bigkeyshuffle",
"key_logic_algorithm": "key_logic_algorithm",
"dungeondoorshuffle": "door_shuffle",
"dungeonintensity": "intensity",
"door_type_mode": "door_type_mode",
"trap_door_mode": "trap_door_mode",
"decoupledoors": "decoupledoors",
"experimental": "experimental",
"dungeon_counters": "dungeon_counters",
"mixed_travel": "mixed_travel",
"standardize_palettes": "standardize_palettes"
}, },
"enemizer": { "enemizer": {
"enemyshuffle": "shuffleenemies", "enemyshuffle": "shuffleenemies",
@@ -101,27 +126,6 @@ SETTINGSTOPROCESS = {
"enemydamage": "enemy_damage", "enemydamage": "enemy_damage",
"enemyhealth": "enemy_health" "enemyhealth": "enemy_health"
}, },
"dungeon": {
"mapshuffle": "mapshuffle",
"compassshuffle": "compassshuffle",
"smallkeyshuffle": "keyshuffle",
"bigkeyshuffle": "bigkeyshuffle",
"dungeondoorshuffle": "door_shuffle",
"dungeonintensity": "intensity",
"door_type_mode": "door_type_mode",
"trap_door_mode": "trap_door_mode",
"key_logic_algorithm": "key_logic_algorithm",
"decoupledoors": "decoupledoors",
"keydropshuffle": "keydropshuffle",
"dropshuffle": "dropshuffle",
"pottery": "pottery",
"colorizepots": "colorizepots",
"potshuffle": "shufflepots",
"experimental": "experimental",
"dungeon_counters": "dungeon_counters",
"mixed_travel": "mixed_travel",
"standardize_palettes": "standardize_palettes"
},
"gameoptions": { "gameoptions": {
"nobgm": "disablemusic", "nobgm": "disablemusic",
"quickswap": "quickswap", "quickswap": "quickswap",
@@ -141,12 +145,15 @@ SETTINGSTOPROCESS = {
"createrom": "create_rom", "createrom": "create_rom",
"calcplaythrough": "calc_playthrough", "calcplaythrough": "calc_playthrough",
"print_custom_yaml": "print_custom_yaml", "print_custom_yaml": "print_custom_yaml",
"usestartinventory": "usestartinventory",
"usecustompool": "custom",
"race": "race",
"saveonexit": "saveonexit" "saveonexit": "saveonexit"
} }
}, },
"startinventory": {
"usestartinventory": "usestartinventory"
},
"custom": {
"usecustompool": "custom"
},
"bottom": { "bottom": {
"content": { "content": {
"names": "names", "names": "names",

View File

@@ -203,13 +203,19 @@ def create_guiargs(parent):
# Cycle through each page # Cycle through each page
for mainpage in options: for mainpage in options:
subpage = None
_, v = next(iter(options[mainpage].items()))
if isinstance(v, str):
subpage = ""
# Cycle through each subpage (in case of Item Randomizer) # Cycle through each subpage (in case of Item Randomizer)
for subpage in options[mainpage]: for subpage in (options[mainpage] if subpage is None else [subpage]):
# Cycle through each widget # Cycle through each widget
for widget in options[mainpage][subpage]: for widget in (options[mainpage][subpage] if subpage != "" else options[mainpage]):
# Get the value and set it # Get the value and set it
arg = options[mainpage][subpage][widget] arg = options[mainpage][subpage][widget] if subpage != "" else options[mainpage][widget]
setattr(guiargs, arg, parent.pages[mainpage].pages[subpage].widgets[widget].storageVar.get()) page = parent.pages[mainpage].pages[subpage] if subpage != "" else parent.pages[mainpage]
pagewidgets = page.content.customWidgets if mainpage == "custom" else page.content.startingWidgets if mainpage == "startinventory" else page.widgets
setattr(guiargs, arg, pagewidgets[widget].storageVar.get())
# Get EnemizerCLI setting # Get EnemizerCLI setting
guiargs.enemizercli = parent.pages["randomizer"].pages["enemizer"].widgets["enemizercli"].storageVar.get() guiargs.enemizercli = parent.pages["randomizer"].pages["enemizer"].widgets["enemizercli"].storageVar.get()
@@ -226,7 +232,7 @@ def create_guiargs(parent):
guiargs.customizer = customizer_value guiargs.customizer = customizer_value
# Get if we're using the Custom Item Pool # Get if we're using the Custom Item Pool
guiargs.custom = bool(parent.pages["randomizer"].pages["generation"].widgets["usecustompool"].storageVar.get()) guiargs.custom = bool(parent.pages["custom"].content.customWidgets["usecustompool"].storageVar.get())
# Get Seed ID # Get Seed ID
guiargs.seed = None guiargs.seed = None
@@ -285,7 +291,7 @@ def create_guiargs(parent):
guiargs.dropshuffle = 1 guiargs.dropshuffle = 1
guiargs.pottery = 'keys' if guiargs.pottery == 'none' else guiargs.pottery guiargs.pottery = 'keys' if guiargs.pottery == 'none' else guiargs.pottery
if guiargs.retro or guiargs.mode == 'retro': if (hasattr(guiargs, 'retro') and guiargs.retro) or guiargs.mode == 'retro':
if guiargs.bow_mode == 'progressive': if guiargs.bow_mode == 'progressive':
guiargs.bow_mode = 'retro' guiargs.bow_mode = 'retro'
elif guiargs.bow_mode == 'silvers': elif guiargs.bow_mode == 'silvers':

View File

@@ -1,4 +1,4 @@
from tkinter import ttk, Frame, N, E, W, LEFT, X, VERTICAL, Y from tkinter import ttk, Frame, N, E, W, LEFT, TOP, X, VERTICAL, Y
import source.gui.widgets as widgets import source.gui.widgets as widgets
import json import json
import os import os
@@ -11,10 +11,10 @@ def custom_page(top,parent):
# Create uniform list columns # Create uniform list columns
def create_list_frame(parent, framename): def create_list_frame(parent, framename):
parent.frames[framename] = Frame(parent) self.frames[framename] = Frame(parent)
parent.frames[framename].pack(side=LEFT, padx=(0,0), anchor=N) self.frames[framename].pack(side=LEFT, padx=(0,0), anchor=N)
parent.frames[framename].thisRow = 0 self.frames[framename].thisRow = 0
parent.frames[framename].thisCol = 0 self.frames[framename].thisCol = 0
# Create a vertical rule to help with splitting columns visually # Create a vertical rule to help with splitting columns visually
def create_vertical_rule(num=1): def create_vertical_rule(num=1):
@@ -34,6 +34,8 @@ def custom_page(top,parent):
# Custom Item Pool option sections # Custom Item Pool option sections
self.frames = {} self.frames = {}
self.frames["customHeader"] = Frame(self)
self.frames["customHeader"].pack(side=TOP, anchor=W)
# Create 5 columns with 2 vertical rules in between each # Create 5 columns with 2 vertical rules in between each
create_list_frame(self, "itemList1") create_list_frame(self, "itemList1")
create_vertical_rule(2) create_vertical_rule(2)
@@ -50,9 +52,14 @@ def custom_page(top,parent):
with open(os.path.join("resources", "app", "gui", "custom", "overview", "widgets.json")) as widgetDefns: with open(os.path.join("resources", "app", "gui", "custom", "overview", "widgets.json")) as widgetDefns:
myDict = json.load(widgetDefns) myDict = json.load(widgetDefns)
for framename,theseWidgets in myDict.items(): for framename,theseWidgets in myDict.items():
if framename in self.frames:
dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename]) dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename])
for key in dictWidgets: for key in dictWidgets:
self.customWidgets[key] = dictWidgets[key] self.customWidgets[key] = dictWidgets[key]
if framename == "customHeader":
packAttrs = {"anchor":W}
packAttrs = widgets.add_padding_from_config(packAttrs, theseWidgets[key])
self.customWidgets[key].pack(packAttrs)
# Load Custom Item Pool settings from settings file # Load Custom Item Pool settings from settings file
for key in CONST.CUSTOMITEMS: for key in CONST.CUSTOMITEMS:

View File

@@ -23,34 +23,41 @@ def loadcliargs(gui, args, settings=None):
# Cycle through each page # Cycle through each page
for mainpage in options: for mainpage in options:
subpage = None
_, v = next(iter(options[mainpage].items()))
if isinstance(v, str):
subpage = ""
# Cycle through each subpage (in case of Item Randomizer) # Cycle through each subpage (in case of Item Randomizer)
for subpage in options[mainpage]: for subpage in (options[mainpage] if subpage is None else [subpage]):
# Cycle through each widget # Cycle through each widget
for widget in options[mainpage][subpage]: for widget in (options[mainpage][subpage] if subpage != "" else options[mainpage]):
if widget in gui.pages[mainpage].pages[subpage].widgets: page = gui.pages[mainpage].pages[subpage] if subpage != "" else gui.pages[mainpage]
pagewidgets = page.content.customWidgets if mainpage == "custom" else page.content.startingWidgets if mainpage == "startinventory" else page.widgets
if widget in pagewidgets:
thisType = "" thisType = ""
# Get the value and set it # Get the value and set it
arg = options[mainpage][subpage][widget] arg = options[mainpage][subpage][widget] if subpage != "" else options[mainpage][widget]
if args[arg] == None: if args[arg] == None:
args[arg] = "" args[arg] = ""
label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) label_ref = mainpage + ('.' + subpage if subpage != "" else '') + '.' + widget
if hasattr(gui.pages[mainpage].pages[subpage].widgets[widget],"type"): label = fish.translate("gui","gui", label_ref)
thisType = gui.pages[mainpage].pages[subpage].widgets[widget].type if hasattr(pagewidgets[widget],"type"):
thisType = pagewidgets[widget].type
if thisType == "checkbox": if thisType == "checkbox":
gui.pages[mainpage].pages[subpage].widgets[widget].checkbox.configure(text=label) pagewidgets[widget].checkbox.configure(text=label)
elif thisType == "selectbox": elif thisType == "selectbox":
theseOptions = gui.pages[mainpage].pages[subpage].widgets[widget].selectbox.options theseOptions = pagewidgets[widget].selectbox.options
gui.pages[mainpage].pages[subpage].widgets[widget].label.configure(text=label) pagewidgets[widget].label.configure(text=label)
i = 0 i = 0
for value in theseOptions["values"]: for value in theseOptions["values"]:
gui.pages[mainpage].pages[subpage].widgets[widget].selectbox.options["labels"][i] = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget + '.' + str(value)) pagewidgets[widget].selectbox.options["labels"][i] = fish.translate("gui","gui", label_ref + '.' + str(value))
i += 1 i += 1
for i in range(0, len(theseOptions["values"])): for i in range(0, len(theseOptions["values"])):
gui.pages[mainpage].pages[subpage].widgets[widget].selectbox["menu"].entryconfigure(i, label=theseOptions["labels"][i]) pagewidgets[widget].selectbox["menu"].entryconfigure(i, label=theseOptions["labels"][i])
gui.pages[mainpage].pages[subpage].widgets[widget].selectbox.options = theseOptions pagewidgets[widget].selectbox.options = theseOptions
elif thisType == "spinbox": elif thisType == "spinbox":
gui.pages[mainpage].pages[subpage].widgets[widget].label.configure(text=label) pagewidgets[widget].label.configure(text=label)
gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(args[arg]) pagewidgets[widget].storageVar.set(args[arg])
# If we're on the Game Options page and it's not about Hints # If we're on the Game Options page and it's not about Hints
if subpage == "gameoptions" and widget not in ["hints", "collection_rate"]: if subpage == "gameoptions" and widget not in ["hints", "collection_rate"]:
# Check if we've got settings # Check if we've got settings

View File

@@ -1,4 +1,4 @@
from tkinter import ttk, Frame, Label, E, W, LEFT, RIGHT from tkinter import ttk, Frame, Label, E, W, LEFT, RIGHT, TOP
import source.gui.widgets as widgets import source.gui.widgets as widgets
import json import json
import os import os
@@ -16,8 +16,8 @@ def dungeon_page(parent):
self.frames["keysanity"].pack(anchor=W) self.frames["keysanity"].pack(anchor=W)
## Dungeon Item Shuffle ## Dungeon Item Shuffle
mscbLabel = Label(self.frames["keysanity"], text="Shuffle: ") mscbLabel = Label(self.frames["keysanity"], text="Dungeon Items: ")
mscbLabel.pack(side=LEFT) mscbLabel.pack(side=TOP, anchor=W)
# Load Dungeon Shuffle option widgets as defined by JSON file # Load Dungeon Shuffle option widgets as defined by JSON file
# Defns include frame name, widget type, widget options, widget placement attributes # Defns include frame name, widget type, widget options, widget placement attributes
@@ -28,7 +28,9 @@ def dungeon_page(parent):
dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["keysanity"]) dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["keysanity"])
for key in dictWidgets: for key in dictWidgets:
self.widgets[key] = dictWidgets[key] self.widgets[key] = dictWidgets[key]
self.widgets[key].pack(side=LEFT) packAttrs = {"side":LEFT}
packAttrs = widgets.add_padding_from_config(packAttrs, myDict[key])
self.widgets[key].pack(packAttrs)
# These get split left & right # These get split left & right
self.frames["widgets"] = Frame(self) self.frames["widgets"] = Frame(self)
@@ -39,6 +41,8 @@ def dungeon_page(parent):
dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["widgets"]) dictWidgets = widgets.make_widgets_from_dict(self, myDict, self.frames["widgets"])
for key in dictWidgets: for key in dictWidgets:
self.widgets[key] = dictWidgets[key] self.widgets[key] = dictWidgets[key]
self.widgets[key].pack(anchor=W) packAttrs = {"anchor":W}
packAttrs = widgets.add_padding_from_config(packAttrs, myDict[key])
self.widgets[key].pack(packAttrs)
return self return self

View File

@@ -26,9 +26,8 @@ def entrando_page(parent):
dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename]) dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename])
for key in dictWidgets: for key in dictWidgets:
self.widgets[key] = dictWidgets[key] self.widgets[key] = dictWidgets[key]
packAttrs = {"anchor":E} packAttrs = {"anchor":W}
if self.widgets[key].type == "checkbox" or key in ["openpyramid", "take_any"]: packAttrs = widgets.add_padding_from_config(packAttrs, theseWidgets[key])
packAttrs["anchor"] = W
self.widgets[key].pack(packAttrs) self.widgets[key].pack(packAttrs)
return self return self

View File

@@ -1,4 +1,4 @@
from tkinter import ttk, Frame, E, W, LEFT, RIGHT, Label from tkinter import ttk, font, Frame, E, W, NW, TOP, LEFT, RIGHT, Y, Label
import source.gui.widgets as widgets import source.gui.widgets as widgets
import json import json
import os import os
@@ -17,14 +17,40 @@ def item_page(parent):
self.frames["checkboxes"] = Frame(self) self.frames["checkboxes"] = Frame(self)
self.frames["checkboxes"].pack(anchor=W) self.frames["checkboxes"].pack(anchor=W)
various_options = Label(self.frames["checkboxes"], text="") various_options = Label(self.frames["checkboxes"], text="Options: ")
various_options.pack(side=LEFT) various_options.pack(side=LEFT)
self.frames["leftItemFrame"] = Frame(self) self.frames["mainFrame"] = Frame(self)
self.frames["rightItemFrame"] = Frame(self) self.frames["mainFrame"].pack(side=TOP, pady=(20,0))
self.frames["poolFrame"] = Frame(self)
self.frames["poolFrame"].pack(fill=Y)
self.frames["leftItemFrame"] = Frame(self.frames["mainFrame"])
self.frames["leftItemFrame"].pack(side=LEFT) self.frames["leftItemFrame"].pack(side=LEFT)
self.frames["rightItemFrame"] = Frame(self.frames["mainFrame"])
self.frames["rightItemFrame"].pack(side=RIGHT) self.frames["rightItemFrame"].pack(side=RIGHT)
self.frames["leftPoolContainer"] = Frame(self.frames["poolFrame"])
self.frames["leftPoolContainer"].pack(side=LEFT, padx=(0,20))
base_font = font.nametofont('TkTextFont').actual()
underline_font = f'"{base_font["family"]}" {base_font["size"]} underline'
various_options = Label(self.frames["leftPoolContainer"], text="Pool Expansions", font=underline_font)
various_options.pack(side=TOP, pady=(20,0))
self.frames["leftPoolHeader"] = Frame(self.frames["leftPoolContainer"])
self.frames["leftPoolHeader"].pack(side=TOP, anchor=W)
self.frames["leftPoolFrame"] = Frame(self.frames["leftPoolContainer"])
self.frames["leftPoolFrame"].pack(side=LEFT, fill=Y)
self.frames["rightPoolFrame"] = Frame(self.frames["poolFrame"])
self.frames["rightPoolFrame"].pack(side=RIGHT)
various_options = Label(self.frames["rightPoolFrame"], text="Pool Modifications", font=underline_font)
various_options.pack(side=TOP, pady=(20,0))
# Load Item Randomizer option widgets as defined by JSON file # Load Item Randomizer option widgets as defined by JSON file
# Defns include frame name, widget type, widget options, widget placement attributes # Defns include frame name, widget type, widget options, widget placement attributes
# Checkboxes go West # Checkboxes go West
@@ -36,8 +62,15 @@ def item_page(parent):
for key in dictWidgets: for key in dictWidgets:
self.widgets[key] = dictWidgets[key] self.widgets[key] = dictWidgets[key]
packAttrs = {"anchor":E} packAttrs = {"anchor":E}
if self.widgets[key].type == "checkbox": if self.widgets[key].type == "checkbox" or framename == "leftPoolFrame":
packAttrs["anchor"] = W
if framename == "checkboxes":
packAttrs["side"] = LEFT packAttrs["side"] = LEFT
packAttrs["padx"] = (10,0)
elif framename == "leftPoolHeader":
packAttrs["side"] = LEFT
packAttrs["padx"] = (0,20)
packAttrs = widgets.add_padding_from_config(packAttrs, theseWidgets[key])
self.widgets[key].pack(packAttrs) self.widgets[key].pack(packAttrs)
return self return self

View File

@@ -15,33 +15,24 @@ def overworld_page(parent):
# Load Overworld Shuffle option widgets as defined by JSON file # Load Overworld Shuffle option widgets as defined by JSON file
# Defns include frame name, widget type, widget options, widget placement attributes # Defns include frame name, widget type, widget options, widget placement attributes
self.frames["topOverworldFrame"] = Frame(self)
self.frames["leftOverworldFrame"] = Frame(self) self.frames["leftOverworldFrame"] = Frame(self)
self.frames["rightOverworldFrame"] = Frame(self) self.frames["rightOverworldFrame"] = Frame(self)
self.frames["topOverworldFrame"].pack(side=TOP, anchor=NW)
self.frames["leftOverworldFrame"].pack(side=LEFT, anchor=NW, fill=Y) self.frames["leftOverworldFrame"].pack(side=LEFT, anchor=NW, fill=Y)
self.frames["rightOverworldFrame"].pack(anchor=NW, fill=Y) self.frames["rightOverworldFrame"].pack(anchor=NW, fill=Y)
shuffleLabel = Label(self.frames["topOverworldFrame"], text="Shuffle: ")
shuffleLabel.pack(side=LEFT)
with open(os.path.join("resources","app","gui","randomize","overworld","widgets.json")) as overworldWidgets: with open(os.path.join("resources","app","gui","randomize","overworld","widgets.json")) as overworldWidgets:
myDict = json.load(overworldWidgets) myDict = json.load(overworldWidgets)
for framename,theseWidgets in myDict.items(): for framename,theseWidgets in myDict.items():
if not theseWidgets:
continue
dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename]) dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename])
for key in dictWidgets: for key in dictWidgets:
self.widgets[key] = dictWidgets[key] self.widgets[key] = dictWidgets[key]
packAttrs = {"anchor":E} packAttrs = {"anchor":W}
if key == "terrain": if self.widgets[key].type != "checkbox":
packAttrs = {"anchor":W, "pady":(3,0)} packAttrs["anchor"] = E
elif key == "keepsimilar": packAttrs = widgets.add_padding_from_config(packAttrs, theseWidgets[key])
packAttrs = {"anchor":W, "pady":(6,0)}
elif key == "overworldflute":
packAttrs["pady"] = (20,0)
elif key in ["mixed", "whirlpool"]:
packAttrs = {"anchor":W, "padx":(79,0)}
self.widgets[key].pack(packAttrs) self.widgets[key].pack(packAttrs)
return self return self

View File

@@ -1,4 +1,4 @@
from tkinter import ttk, Frame, N, E, W, LEFT, X, VERTICAL, Y from tkinter import ttk, Frame, N, E, W, LEFT, TOP, X, VERTICAL, Y
import source.gui.widgets as widgets import source.gui.widgets as widgets
import json import json
import os import os
@@ -11,10 +11,10 @@ def startinventory_page(top,parent):
# Create uniform list columns # Create uniform list columns
def create_list_frame(parent, framename): def create_list_frame(parent, framename):
parent.frames[framename] = Frame(parent) self.frames[framename] = Frame(parent)
parent.frames[framename].pack(side=LEFT, padx=(0,0), anchor=N) self.frames[framename].pack(side=LEFT, padx=(0,0), anchor=N)
parent.frames[framename].thisRow = 0 self.frames[framename].thisRow = 0
parent.frames[framename].thisCol = 0 self.frames[framename].thisCol = 0
# Create a vertical rule to help with splitting columns visually # Create a vertical rule to help with splitting columns visually
def create_vertical_rule(num=1): def create_vertical_rule(num=1):
@@ -34,6 +34,8 @@ def startinventory_page(top,parent):
# Starting Inventory option sections # Starting Inventory option sections
self.frames = {} self.frames = {}
self.frames["startHeader"] = Frame(self)
self.frames["startHeader"].pack(side=TOP, anchor=W)
# Create 5 columns with 2 vertical rules in between each # Create 5 columns with 2 vertical rules in between each
create_list_frame(self,"itemList1") create_list_frame(self,"itemList1")
create_vertical_rule(2) create_vertical_rule(2)
@@ -55,9 +57,14 @@ def startinventory_page(top,parent):
if key in myDict[thisList]: if key in myDict[thisList]:
del myDict[thisList][key] del myDict[thisList][key]
for framename,theseWidgets in myDict.items(): for framename,theseWidgets in myDict.items():
if framename in self.frames:
dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename]) dictWidgets = widgets.make_widgets_from_dict(self, theseWidgets, self.frames[framename])
for key in dictWidgets: for key in dictWidgets:
self.startingWidgets[key] = dictWidgets[key] self.startingWidgets[key] = dictWidgets[key]
if framename == "startHeader":
packAttrs = {"anchor":W}
packAttrs = widgets.add_padding_from_config(packAttrs, theseWidgets[key])
self.startingWidgets[key].pack(packAttrs)
# Load Custom Starting Inventory settings from settings file, ignoring ones to be excluded # Load Custom Starting Inventory settings from settings file, ignoring ones to be excluded
for key in CONST.CUSTOMITEMS: for key in CONST.CUSTOMITEMS:

View File

@@ -1,4 +1,4 @@
from tkinter import Checkbutton, Entry, Frame, IntVar, Label, OptionMenu, Spinbox, StringVar, LEFT, RIGHT, X from tkinter import messagebox, Checkbutton, Entry, Frame, IntVar, Label, OptionMenu, Spinbox, StringVar, LEFT, RIGHT, X
from source.classes.Empty import Empty from source.classes.Empty import Empty
# Override Spinbox to include mousewheel support for changing value # Override Spinbox to include mousewheel support for changing value
@@ -16,7 +16,7 @@ class mySpinbox(Spinbox):
self.invoke('buttonup') self.invoke('buttonup')
# Make a Checkbutton with a label # Make a Checkbutton with a label
def make_checkbox(self, parent, label, storageVar, manager, managerAttrs): def make_checkbox(self, parent, label, storageVar, manager, managerAttrs, config):
self = Frame(parent) self = Frame(parent)
self.storageVar = storageVar self.storageVar = storageVar
if managerAttrs is not None and "default" in managerAttrs: if managerAttrs is not None and "default" in managerAttrs:
@@ -25,7 +25,10 @@ def make_checkbox(self, parent, label, storageVar, manager, managerAttrs):
elif managerAttrs["default"] == "false" or managerAttrs["default"] == False: elif managerAttrs["default"] == "false" or managerAttrs["default"] == False:
self.storageVar.set(False) self.storageVar.set(False)
del managerAttrs["default"] del managerAttrs["default"]
self.checkbox = Checkbutton(self, text=label, variable=self.storageVar) options = {"text":label, "variable":self.storageVar}
if config and "command" in config:
options.update({"command":lambda m=config["command"]: widget_command(self, m)})
self.checkbox = Checkbutton(self, options)
if managerAttrs is not None: if managerAttrs is not None:
self.checkbox.pack(managerAttrs) self.checkbox.pack(managerAttrs)
else: else:
@@ -43,6 +46,10 @@ def make_selectbox(self, parent, label, options, storageVar, manager, managerAtt
self.labelVar = StringVar() self.labelVar = StringVar()
self.storageVar = storageVar self.storageVar = storageVar
if config and "command" in config:
self.command = config["command"]
self.selectbox = OptionMenu(self, self.labelVar, *labels, command=lambda m: widget_command(self, self.command))
else:
self.selectbox = OptionMenu(self, self.labelVar, *labels) self.selectbox = OptionMenu(self, self.labelVar, *labels)
self.selectbox.options = {} self.selectbox.options = {}
@@ -96,7 +103,7 @@ def make_selectbox(self, parent, label, options, storageVar, manager, managerAtt
else: else:
self.label.pack(side=LEFT) self.label.pack(side=LEFT)
self.selectbox.config(width=config['width'] if config and config['width'] else 20) self.selectbox.config(width=config['width'] if config and 'width' in config else 20)
idx = 0 idx = 0
default = self.selectbox.options["values"][idx] default = self.selectbox.options["values"][idx]
if managerAttrs is not None and "default" in managerAttrs: if managerAttrs is not None and "default" in managerAttrs:
@@ -181,7 +188,7 @@ def make_widget(self, type, parent, label, storageVar=None, manager=None, manage
if type == "checkbox": if type == "checkbox":
if thisStorageVar is None: if thisStorageVar is None:
thisStorageVar = IntVar() thisStorageVar = IntVar()
widget = make_checkbox(self, parent, label, thisStorageVar, manager, managerAttrs) widget = make_checkbox(self, parent, label, thisStorageVar, manager, managerAttrs, config)
elif type == "selectbox": elif type == "selectbox":
if thisStorageVar is None: if thisStorageVar is None:
thisStorageVar = StringVar() thisStorageVar = StringVar()
@@ -221,3 +228,51 @@ def make_widgets_from_dict(self, defns, parent):
for key,defn in defns.items(): for key,defn in defns.items():
widgets[key] = make_widget_from_dict(self, defn, parent) widgets[key] = make_widget_from_dict(self, defn, parent)
return widgets return widgets
# Add padding to widget
def add_padding_from_config(packAttrs, defn):
if "config" in defn:
config = defn["config"]
if 'padx' in config:
packAttrs["padx"] = config['padx']
if 'pady' in config:
packAttrs["pady"] = config['pady']
return packAttrs
# Callback when a widget issues a command
def widget_command(widget, command=""):
root = widget.winfo_toplevel()
text_output = ""
if command == "worldstate":
if widget.storageVar.get() == 'retro':
temp_widget = root.pages["randomizer"].pages["dungeon"].widgets["smallkeyshuffle"]
text_output += f'\n {temp_widget.label.cget("text")}'
temp_widget.storageVar.set('universal')
temp_widget = root.pages["randomizer"].pages["item"].widgets["bow_mode"]
text_output += f'\n {temp_widget.label.cget("text")}'
if temp_widget.storageVar.get() == 'progressive':
temp_widget.storageVar.set('retro')
elif temp_widget.storageVar.get() == 'silvers':
temp_widget.storageVar.set('retro_silvers')
temp_widget = root.pages["randomizer"].pages["item"].widgets["take_any"]
text_output += f'\n {temp_widget.label.cget("text")}'
if temp_widget.storageVar.get() == 'none':
temp_widget.storageVar.set('random')
widget.storageVar.set('open')
messagebox.showinfo('', f'The following settings were changed:{text_output}')
elif command == "keydropshuffle":
if widget.storageVar.get() > 0:
temp_widget = root.pages["randomizer"].pages["item"].widgets["pottery"]
text_output += f'\n {temp_widget.label.cget("text")}'
if temp_widget.storageVar.get() == 'none':
temp_widget.storageVar.set('keys')
temp_widget = root.pages["randomizer"].pages["item"].widgets["dropshuffle"]
temp_widget.storageVar.set(1)
text_output += f'\n {temp_widget.checkbox.cget("text")}'
widget.storageVar.set(0)
messagebox.showinfo('', f'The following settings were changed:{text_output}')

View File

@@ -528,9 +528,14 @@ def do_links_house(entrances, exits, avail, cross_world):
# can't have links house on eddm in restricted because Inverted Aga Tower isn't available # can't have links house on eddm in restricted because Inverted Aga Tower isn't available
# todo: inverted full may have the same problem if both links house and a mandatory connector is chosen # todo: inverted full may have the same problem if both links house and a mandatory connector is chosen
# from the 3 inverted options # from the 3 inverted options
if avail.world.shuffle[avail.player] in ['restricted'] and avail.world.is_tile_swapped(0x03, avail.player): if avail.world.shuffle[avail.player] in ['restricted', 'lite', 'lean'] and avail.world.is_tile_swapped(0x03, avail.player):
avail.links_on_mountain = True avail.links_on_mountain = True
forbidden.extend(['Spike Cave', 'Dark Death Mountain Fairy']) forbidden.extend(['Spike Cave', 'Dark Death Mountain Fairy'])
if avail.world.shuffle[avail.player] in ['lite', 'lean']:
if avail.world.is_tile_swapped(0x05, avail.player):
avail.links_on_mountain = True
forbidden.extend(['Cave Shop (Dark Death Mountain)'])
else: else:
avail.links_on_mountain = True avail.links_on_mountain = True
@@ -558,6 +563,17 @@ def do_links_house(entrances, exits, avail, cross_world):
if links_house in dm_spots and avail.world.owShuffle[avail.player] == 'vanilla': if links_house in dm_spots and avail.world.owShuffle[avail.player] == 'vanilla':
if avail.links_on_mountain: if avail.links_on_mountain:
return # connector is fine return # connector is fine
if avail.world.shuffle[avail.player] in ['lite', 'lean']:
rem_exits = [e for e in avail.exits if e in Connector_Exit_Set and e not in Dungeon_Exit_Set]
multi_exit_caves = figure_out_connectors(rem_exits)
if cross_world:
possible_dm_exits = [e for e in avail.entrances if e not in entrances and e in LH_DM_Connector_List]
possible_exits = [e for e in avail.entrances if e not in entrances and e not in dm_spots]
else:
world_list = LW_Entrances if not avail.inverted else DW_Entrances
possible_dm_exits = [e for e in avail.entrances if e not in entrances and e in LH_DM_Connector_List and e in world_list]
possible_exits = [e for e in avail.entrances if e not in entrances and e not in dm_spots and e in world_list]
else:
multi_exit_caves = figure_out_connectors(exits) multi_exit_caves = figure_out_connectors(exits)
entrance_pool = entrances if avail.coupled else avail.decoupled_entrances entrance_pool = entrances if avail.coupled else avail.decoupled_entrances
if cross_world: if cross_world:
@@ -2036,6 +2052,23 @@ Connector_Exit_Set = {
'Turtle Rock Isolated Ledge Exit', 'Turtle Rock Ledge Exit (West)' 'Turtle Rock Isolated Ledge Exit', 'Turtle Rock Ledge Exit (West)'
} }
Dungeon_Exit_Set = {
'Eastern Palace Exit',
'Tower of Hera Exit',
'Agahnims Tower Exit',
'Palace of Darkness Exit',
'Swamp Palace Exit',
'Skull Woods Final Section Exit',
'Thieves Town Exit',
'Ice Palace Exit',
'Misery Mire Exit',
'Ganons Tower Exit',
'Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)',
'Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)',
'Desert Palace Exit (South)', 'Desert Palace Exit (East)', 'Desert Palace Exit (West)',
'Turtle Rock Exit (Front)', 'Turtle Rock Isolated Ledge Exit', 'Turtle Rock Ledge Exit (West)'
}
# Entrances that cannot be used to access a must_exit entrance - symmetrical to allow reverse lookups # Entrances that cannot be used to access a must_exit entrance - symmetrical to allow reverse lookups
Must_Exit_Invalid_Connections = defaultdict(set) Must_Exit_Invalid_Connections = defaultdict(set)