feat: skull woods er options

This commit is contained in:
aerinon
2024-05-28 14:52:24 -06:00
parent a5023a18a6
commit 28872c8d27
14 changed files with 411 additions and 83 deletions

View File

@@ -23,6 +23,7 @@ class EntrancePool(object):
self.decoupled_exits = []
self.original_entrances = set()
self.original_exits = set()
self.same_world_restricted = {}
self.world = world
self.player = player
@@ -90,6 +91,10 @@ def link_entrances_new(world, player):
if mode not in modes:
raise RuntimeError(f'Shuffle mode {mode} is not yet supported')
mode_cfg = copy.deepcopy(modes[mode])
if world.linked_drops[player] != 'unset':
mode_cfg['keep_drops_together'] = 'on' if world.linked_drops[player] == 'linked' else 'off'
avail_pool.swapped = mode_cfg['undefined'] == 'swap'
if avail_pool.is_standard():
do_standard_connections(avail_pool)
@@ -97,11 +102,7 @@ def link_entrances_new(world, player):
for pool_name, pool in pool_list.items():
special_shuffle = pool['special'] if 'special' in pool else None
if special_shuffle == 'drops':
holes, targets = find_entrances_and_targets_drops(avail_pool, pool['entrances'])
if avail_pool.swapped:
connect_swapped(holes, targets, avail_pool)
else:
connect_random(holes, targets, avail_pool)
handle_skull_woods_drops(avail_pool, pool['entrances'], mode_cfg)
elif special_shuffle == 'fixed_shuffle':
do_fixed_shuffle(avail_pool, pool['entrances'])
elif special_shuffle == 'same_world':
@@ -124,12 +125,7 @@ def link_entrances_new(world, player):
elif special_shuffle == 'vanilla':
do_vanilla_connect(pool, avail_pool)
elif special_shuffle == 'skull':
entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances'])
if avail_pool.swapped:
connect_swapped(entrances, exits, avail_pool, True)
else:
connect_random(entrances, exits, avail_pool, True)
avail_pool.skull_handled = True
handle_skull_woods_entrances(avail_pool, pool['entrances'])
else:
entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances'])
do_main_shuffle(entrances, exits, avail_pool, mode_cfg)
@@ -239,12 +235,13 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
# mandatory exits
rem_entrances, rem_exits = set(), set()
if not cross_world:
determine_dungeon_restrictions(avail)
mand_exits = figure_out_must_exits_same_world(entrances, exits, avail)
must_exit_lw, must_exit_dw, lw_entrances, dw_entrances, multi_exit_caves, hyrule_forced = mand_exits
if hyrule_forced:
do_mandatory_connections(avail, lw_entrances, hyrule_forced, must_exit_lw)
else:
do_mandatory_connections(avail, lw_entrances, multi_exit_caves, must_exit_lw)
must_exit_lw, must_exit_dw, lw_entrances, dw_entrances, multi_exit_caves = mand_exits
lw_candidates = filter_restricted_caves(multi_exit_caves, 'LightWorld', avail)
other_candidates = [x for x in multi_exit_caves if x not in lw_candidates] # remember those not passed in
do_mandatory_connections(avail, lw_entrances, lw_candidates, must_exit_lw)
multi_exit_caves = other_candidates + lw_candidates # rebuild list from the lw_candidates and those not passed
# remove old man house as connector - not valid for dw must_exit if it is a spawn point
if not avail.inverted:
new_mec = []
@@ -254,7 +251,10 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
else:
new_mec.append(cave_option)
multi_exit_caves = new_mec
do_mandatory_connections(avail, dw_entrances, multi_exit_caves, must_exit_dw)
dw_candidates = filter_restricted_caves(multi_exit_caves, 'DarkWorld', avail)
other_candidates = [x for x in multi_exit_caves if x not in dw_candidates] # remember those not passed in
do_mandatory_connections(avail, dw_entrances, dw_candidates, must_exit_dw)
multi_exit_caves = other_candidates + dw_candidates # rebuild list from the dw_candidates and those not passed
rem_entrances.update(lw_entrances)
rem_entrances.update(dw_entrances)
else:
@@ -351,6 +351,10 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
if bonk_fairy_exception(x):
lw_entrances.append(x) if x in LW_Entrances else dw_entrances.append(x)
do_same_world_connectors(lw_entrances, dw_entrances, multi_exit_caves, avail)
if avail.world.doorShuffle[avail.player] != 'vanilla':
determine_dungeon_restrictions(avail)
possibles = figure_out_possible_exits(rem_exits)
do_same_world_possible_connectors(lw_entrances, dw_entrances, possibles, avail)
unused_entrances.update(lw_entrances)
unused_entrances.update(dw_entrances)
else:
@@ -436,12 +440,16 @@ def do_holes_and_linked_drops(entrances, exits, avail, cross_world, keep_togethe
if not keep_together:
targets = [avail.one_way_map[x] for x in holes_to_shuffle]
connect_random(holes_to_shuffle, targets, avail)
if avail.swapped:
connect_swapped(holes_to_shuffle, targets, avail)
else:
connect_random(holes_to_shuffle, targets, avail)
remove_from_list(entrances, holes_to_shuffle)
remove_from_list(exits, targets)
return # we're done here
hole_entrances, hole_targets = [], []
leftover_hole_entrances, leftover_hole_targets = [], []
for hole in drop_map:
if hole in avail.original_entrances and hole in linked_drop_map:
linked_entrance = linked_drop_map[hole]
@@ -451,40 +459,39 @@ def do_holes_and_linked_drops(entrances, exits, avail, cross_world, keep_togethe
target_drop = avail.one_way_map[hole]
if target_exit in exits and target_drop in exits:
hole_targets.append((target_exit, target_drop))
else:
if hole in avail.original_entrances and hole in entrances:
leftover_hole_entrances.append(hole)
if drop_map[hole] in exits:
leftover_hole_targets.append(drop_map[hole])
random.shuffle(hole_entrances)
if not cross_world and 'Sanctuary Grave' in holes_to_shuffle:
hc = avail.world.get_entrance('Hyrule Castle Exit (South)', avail.player)
is_hc_in_dw = avail.world.mode[avail.player] == 'inverted'
if hc.connected_region:
is_hc_in_dw = hc.connected_region.type == RegionType.DarkWorld
chosen_entrance = None
if is_hc_in_dw:
if avail.swapped:
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances and e[0] != 'Sanctuary')
if not cross_world:
if 'Sanctuary Grave' in holes_to_shuffle:
hc = avail.world.get_entrance('Hyrule Castle Exit (South)', avail.player)
is_hc_in_dw = avail.world.mode[avail.player] == 'inverted'
if hc.connected_region:
is_hc_in_dw = hc.connected_region.type == RegionType.DarkWorld
chosen_entrance = None
if is_hc_in_dw:
if avail.swapped:
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances and e[0] != 'Sanctuary')
if not chosen_entrance:
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances)
if not chosen_entrance:
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances)
if not chosen_entrance:
if avail.swapped:
chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances and e[0] != 'Sanctuary')
if not chosen_entrance:
chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances)
if avail.swapped:
chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances and e[0] != 'Sanctuary')
if not chosen_entrance:
chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances)
if chosen_entrance:
hole_entrances.remove(chosen_entrance)
sanc_interior = next(target for target in hole_targets if target[0] == 'Sanctuary Exit')
hole_targets.remove(sanc_interior)
connect_two_way(chosen_entrance[0], sanc_interior[0], avail) # two-way exit
connect_entrance(chosen_entrance[1], sanc_interior[1], avail) # hole
remove_from_list(entrances, [chosen_entrance[0], chosen_entrance[1]])
remove_from_list(exits, [sanc_interior[0], sanc_interior[1]])
if avail.swapped and drop_map[chosen_entrance[1]] != sanc_interior[1]:
swap_ent, swap_ext = connect_swap(chosen_entrance[0], sanc_interior[0], avail)
swap_drop, swap_tgt = connect_swap(chosen_entrance[1], sanc_interior[1], avail)
hole_entrances.remove((swap_ent, swap_drop))
hole_targets.remove((swap_ext, swap_tgt))
remove_from_list(entrances, [swap_ent, swap_drop])
remove_from_list(exits, [swap_ext, swap_tgt])
if chosen_entrance:
connect_hole_via_interior(chosen_entrance, 'Sanctuary Exit', hole_entrances, hole_targets, entrances, exits, avail)
if 'Skull Woods First Section Hole (North)' in holes_to_shuffle:
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances)
connect_hole_via_interior(chosen_entrance, 'Skull Woods First Section Exit', hole_entrances, hole_targets, entrances, exits, avail)
if 'Skull Woods Second Section Hole' in holes_to_shuffle:
chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances)
connect_hole_via_interior(chosen_entrance, 'Skull Woods Second Section Exit (East)', hole_entrances, hole_targets, entrances, exits, avail)
random.shuffle(hole_targets)
while len(hole_entrances):
@@ -506,6 +513,31 @@ def do_holes_and_linked_drops(entrances, exits, avail, cross_world, keep_togethe
remove_from_list(entrances, [swap_ent, swap_drop])
remove_from_list(exits, [swap_ext, swap_tgt])
if leftover_hole_entrances and leftover_hole_targets:
remove_from_list(entrances, leftover_hole_entrances)
remove_from_list(exits, leftover_hole_targets)
if avail.swapped:
connect_swapped(leftover_hole_entrances, leftover_hole_targets, avail)
else:
connect_random(leftover_hole_entrances, leftover_hole_targets, avail)
def connect_hole_via_interior(chosen_entrance, interior, hole_entrances, hole_targets, entrances, exits, avail):
hole_entrances.remove(chosen_entrance)
interior = next(target for target in hole_targets if target[0] == interior)
hole_targets.remove(interior)
connect_two_way(chosen_entrance[0], interior[0], avail)
connect_entrance(chosen_entrance[1], interior[1], avail)
remove_from_list(entrances, [chosen_entrance[0], chosen_entrance[1]])
remove_from_list(exits, [interior[0], interior[1]])
if avail.swapped and drop_map[chosen_entrance[1]] != interior[1]:
swap_ent, swap_ext = connect_swap(chosen_entrance[0], interior[0], avail)
swap_drop, swap_tgt = connect_swap(chosen_entrance[1], interior[1], avail)
hole_entrances.remove((swap_ent, swap_drop))
hole_targets.remove((swap_ext, swap_tgt))
remove_from_list(entrances, [swap_ent, swap_drop])
remove_from_list(exits, [swap_ext, swap_tgt])
def do_links_house(entrances, exits, avail, cross_world):
lh_exit = 'Links House Exit'
@@ -628,38 +660,77 @@ def figure_out_true_exits(exits, avail):
return multi_exit_caves
# todo: figure out hyrule forced better
def figure_out_possible_exits(exits):
possible_multi_exit_caves = []
for item in doors_possible_connectors:
if item in exits:
remove_from_list(exits, item)
possible_multi_exit_caves.append(item)
return possible_multi_exit_caves
def determine_dungeon_restrictions(avail):
check_for_hc = (avail.is_standard() or avail.world.doorShuffle[avail.player] != 'vanilla')
for check in dungeon_restriction_checks:
dungeon_exits, drop_regions = check
if check_for_hc and any('Hyrule Castle' in x for x in dungeon_exits):
avail.same_world_restricted.update({x: 'LightWorld' for x in dungeon_exits})
else:
restriction = None
for x in dungeon_exits:
ent = avail.world.get_entrance(x, avail.player)
if ent.connected_region:
if ent.connected_region.type == RegionType.LightWorld:
restriction = 'LightWorld'
elif ent.connected_region.type == RegionType.DarkWorld:
restriction = 'DarkWorld'
# Holes only restrict
for x in drop_regions:
region = avail.world.get_region(x, avail.player)
ent = next((ent for ent in region.entrances if ent.parent_region and ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]), None)
if ent:
if ent.parent_region.type == RegionType.LightWorld and not avail.inverted:
restriction = 'LightWorld'
elif ent.parent_region.type == RegionType.DarkWorld and avail.inverted:
restriction = 'DarkWorld'
if restriction:
avail.same_world_restricted.update({x: restriction for x in dungeon_exits})
def figure_out_must_exits_same_world(entrances, exits, avail):
lw_entrances, dw_entrances = [], []
hyrule_forced = None
check_for_hc = (avail.is_standard() or avail.world.doorShuffle[avail.player] != 'vanilla')
for x in entrances:
lw_entrances.append(x) if x in LW_Entrances else dw_entrances.append(x)
multi_exit_caves = figure_out_connectors(exits)
if check_for_hc:
for option in multi_exit_caves:
if any(x in option for x in ['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (East)',
'Hyrule Castle Exit (West)']):
hyrule_forced = [option]
if hyrule_forced:
remove_from_list(multi_exit_caves, hyrule_forced)
if not avail.inverted and not avail.skull_handled:
skull_connector = [x for x in ['Skull Woods Second Section Exit (West)', 'Skull Woods Second Section Exit (East)'] if x in exits]
multi_exit_caves.append(skull_connector)
must_exit_lw, must_exit_dw, unfiltered_lw, unfiltered_dw = must_exits_helper(avail, lw_entrances, dw_entrances)
return must_exit_lw, must_exit_dw, lw_entrances, dw_entrances, multi_exit_caves, hyrule_forced
return must_exit_lw, must_exit_dw, lw_entrances, dw_entrances, multi_exit_caves
def must_exits_helper(avail, lw_entrances, dw_entrances):
must_exit_lw_orig = (Inverted_LW_Must_Exit if avail.inverted else LW_Must_Exit).copy()
must_exit_dw_orig = (Inverted_DW_Must_Exit if avail.inverted else DW_Must_Exit).copy()
if not avail.inverted and not avail.skull_handled:
must_exit_dw_orig.append(('Skull Woods Second Section Door (West)', 'Skull Woods Final Section'))
must_exit_dw_orig.append('Skull Woods Second Section Door (West)')
must_exit_lw = must_exit_filter(avail, must_exit_lw_orig, lw_entrances)
must_exit_dw = must_exit_filter(avail, must_exit_dw_orig, dw_entrances)
return must_exit_lw, must_exit_dw, flatten(must_exit_lw_orig), flatten(must_exit_dw_orig)
def filter_restricted_caves(multi_exit_caves, restriction, avail):
candidates = []
for cave in multi_exit_caves:
if all(x not in avail.same_world_restricted or avail.same_world_restricted[x] == restriction for x in cave):
candidates.append(cave)
return candidates
def flatten(list_to_flatten):
ret = []
for item in list_to_flatten:
@@ -672,11 +743,14 @@ def flatten(list_to_flatten):
def figure_out_must_exits_cross_world(entrances, exits, avail):
multi_exit_caves = figure_out_connectors(exits)
if not avail.skull_handled:
skull_connector = [x for x in ['Skull Woods Second Section Exit (West)', 'Skull Woods Second Section Exit (East)'] if x in exits]
multi_exit_caves.append(skull_connector)
must_exit_lw = (Inverted_LW_Must_Exit if avail.inverted else LW_Must_Exit).copy()
must_exit_dw = (Inverted_DW_Must_Exit if avail.inverted else DW_Must_Exit).copy()
if not avail.inverted and not avail.skull_handled:
must_exit_dw.append(('Skull Woods Second Section Door (West)', 'Skull Woods Final Section'))
must_exit_dw.append('Skull Woods Second Section Door (West)')
must_exit = must_exit_filter(avail, must_exit_lw + must_exit_dw, entrances)
return must_exit, multi_exit_caves
@@ -687,7 +761,7 @@ def do_same_world_connectors(lw_entrances, dw_entrances, caves, avail):
random.shuffle(dw_entrances)
random.shuffle(caves)
while caves:
# connect highest exit count caves first, prevent issue where we have 2 or 3 exits across worlds left to fill
# connect highest-exit-count caves first, prevent issue where we have 2 or 3 exits across worlds left to fill
cave_candidate = (None, 0)
for i, cave in enumerate(caves):
if isinstance(cave, str):
@@ -696,12 +770,19 @@ def do_same_world_connectors(lw_entrances, dw_entrances, caves, avail):
cave_candidate = (i, len(cave))
cave = caves.pop(cave_candidate[0])
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
if isinstance(cave, str):
cave = (cave,)
target, restriction = None, None
if any(x in avail.same_world_restricted for x in cave):
restriction = next(avail.same_world_restricted[x] for x in cave if x in avail.same_world_restricted)
target = lw_entrances if restriction == 'LightWorld' else dw_entrances
if target is None:
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
# check if we can still fit the cave into our target group
if len(target) < len(cave):
if restriction:
raise Exception('Not enough entrances for restricted cave, algorithm needs revision (main)')
# need to use other set
target = lw_entrances if target is dw_entrances else dw_entrances
@@ -715,6 +796,18 @@ def do_same_world_connectors(lw_entrances, dw_entrances, caves, avail):
connect_two_way(target.pop(), ext, avail)
def do_same_world_possible_connectors(lw_entrances, dw_entrances, possibles, avail):
random.shuffle(possibles)
while possibles:
possible = possibles.pop()
target = None
if possible in avail.same_world_restricted:
target = lw_entrances if avail.same_world_restricted[possible] == 'LightWorld' else dw_entrances
if target is None:
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
connect_two_way(target.pop(), possible, avail)
determine_dungeon_restrictions(avail)
def do_cross_world_connectors(entrances, caves, avail):
random.shuffle(entrances)
random.shuffle(caves)
@@ -762,6 +855,37 @@ def do_cross_world_connectors(entrances, caves, avail):
break
def handle_skull_woods_drops(avail, pool, mode_cfg):
skull_woods = avail.world.skullwoods[avail.player]
if skull_woods in ['restricted', 'loose']:
for drop in pool:
target = drop_map[drop]
connect_entrance(drop, target, avail)
elif skull_woods == 'original':
holes, targets = find_entrances_and_targets_drops(avail, pool)
if avail.swapped:
connect_swapped(holes, targets, avail)
else:
connect_random(holes, targets, avail)
elif skull_woods == 'followlinked':
keep_together = mode_cfg['keep_drops_together'] == 'on' if 'keep_drops_together' in mode_cfg else True
if keep_together:
for drop in ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)']:
target = drop_map[drop]
connect_entrance(drop, target, avail)
def handle_skull_woods_entrances(avail, pool):
skull_woods = avail.world.skullwoods[avail.player]
if skull_woods in ['restricted', 'original']:
entrances, exits = find_entrances_and_exits(avail, pool)
if avail.swapped:
connect_swapped(entrances, exits, avail, True)
else:
connect_random(entrances, exits, avail, True)
avail.skull_handled = True
def do_fixed_shuffle(avail, entrance_list):
max_size = 0
options = {}
@@ -803,8 +927,6 @@ def do_same_world_shuffle(avail, pool_def):
multi_exit = pool_def['connectors']
# complete_entrance_set = set()
lw_entrances, dw_entrances, multi_exits_caves, other_exits = [], [], [], []
hyrule_forced = None
check_for_hc = avail.is_standard() or avail.world.doorShuffle[avail.player] != 'vanilla'
single_entrances, single_exits = find_entrances_and_exits(avail, single_exit)
other_exits.extend(single_exits)
@@ -814,12 +936,7 @@ def do_same_world_shuffle(avail, pool_def):
for option in multi_exit:
multi_entrances, multi_exits = find_entrances_and_exits(avail, option)
# complete_entrance_set.update(multi_entrances)
if check_for_hc and any(x in multi_entrances for x in ['Hyrule Castle Entrance (South)',
'Hyrule Castle Entrance (East)',
'Hyrule Castle Entrance (West)']):
hyrule_forced = [multi_exits]
else:
multi_exits_caves.append(multi_exits)
multi_exits_caves.append(multi_exits)
for x in multi_entrances:
(dw_entrances, lw_entrances)[x in LW_Entrances].append(x)
@@ -828,11 +945,16 @@ def do_same_world_shuffle(avail, pool_def):
must_exit_lw = must_exit_filter(avail, must_exit_lw, lw_entrances)
must_exit_dw = must_exit_filter(avail, must_exit_dw, dw_entrances)
if hyrule_forced:
do_mandatory_connections(avail, lw_entrances, hyrule_forced, must_exit_lw)
else:
do_mandatory_connections(avail, lw_entrances, multi_exits_caves, must_exit_lw)
do_mandatory_connections(avail, dw_entrances, multi_exits_caves, must_exit_dw)
determine_dungeon_restrictions(avail)
lw_candidates = filter_restricted_caves(multi_exits_caves, 'LightWorld', avail)
other_candidates = [x for x in multi_exits_caves if x not in lw_candidates] # remember those not passed in
do_mandatory_connections(avail, lw_entrances, lw_candidates, must_exit_lw)
multi_exits_caves = other_candidates + lw_candidates # rebuild list from the lw_candidates and those not passed
dw_candidates = filter_restricted_caves(multi_exits_caves, 'DarkWorld', avail)
other_candidates = [x for x in multi_exits_caves if x not in dw_candidates] # remember those not passed in
do_mandatory_connections(avail, dw_entrances, dw_candidates, must_exit_dw)
multi_exits_caves = other_candidates + dw_candidates # rebuild list from the dw_candidates and those not passed
# connect caves
random.shuffle(lw_entrances)
@@ -845,8 +967,15 @@ def do_same_world_shuffle(avail, pool_def):
cave_candidate = (i, len(cave))
cave = multi_exits_caves.pop(cave_candidate[0])
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
target, restriction = None, None
if any(x in avail.same_world_restricted for x in cave):
restriction = next(avail.same_world_restricted[x] for x in cave if x in avail.same_world_restricted)
target = lw_entrances if restriction == 'LightWorld' else dw_entrances
if target is None:
target = lw_entrances if random.randint(0, 1) == 0 else dw_entrances
if len(target) < len(cave): # swap because we ran out of entrances in that world
if restriction:
raise Exception('Not enough entrances for restricted cave, algorithm needs revision (dungeonsfull)')
target = lw_entrances if target is dw_entrances else dw_entrances
for ext in cave:
@@ -1386,6 +1515,12 @@ modes = {
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'Skull Woods Second Section Door (West)']
},
'skull_layout': {
'special': 'vanilla',
'condition': '',
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'Skull Woods Second Section Door (West)']
},
'single_entrance_dungeon': {
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace', 'Ganons Tower']
@@ -1419,13 +1554,15 @@ modes = {
'sanc_flag': 'light_world', # always light world flag
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
'Agahnims Tower', 'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace',
'Ganons Tower'],
'Ganons Tower', 'Desert Palace Entrance (North)', 'Dark Death Mountain Ledge (East)'],
'connectors': [['Hyrule Castle Entrance (South)', 'Hyrule Castle Entrance (East)',
'Hyrule Castle Entrance (West)'],
['Desert Palace Entrance (South)', 'Desert Palace Entrance (East)',
'Desert Palace Entrance (West)', 'Desert Palace Entrance (North)'],
'Desert Palace Entrance (West)'],
['Turtle Rock', 'Turtle Rock Isolated Ledge Entrance',
'Dark Death Mountain Ledge (West)', 'Dark Death Mountain Ledge (East)']]
'Dark Death Mountain Ledge (West)'],
['Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)',
'Skull Woods First Section Door']]
},
}
},
@@ -1611,6 +1748,12 @@ modes = {
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'Skull Woods Second Section Door (West)']
},
'skull_layout': {
'special': 'vanilla',
'condition': '',
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'Skull Woods Second Section Door (West)']
},
'single_entrance_dungeon': {
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace', 'Ganons Tower']
@@ -1700,6 +1843,12 @@ modes = {
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'Skull Woods Second Section Door (West)']
},
'skull_layout': {
'special': 'vanilla',
'condition': '',
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
'Skull Woods Second Section Door (West)']
},
'single_entrance_dungeon': {
'entrances': ['Eastern Palace', 'Tower of Hera', 'Thieves Town', 'Skull Woods Final Section',
'Palace of Darkness', 'Ice Palace', 'Misery Mire', 'Swamp Palace', 'Ganons Tower']
@@ -1801,7 +1950,10 @@ linked_drop_map = {
'Lumberjack Tree Tree': 'Lumberjack Tree Cave',
'Sanctuary Grave': 'Sanctuary',
'Pyramid Hole': 'Pyramid Entrance',
'Inverted Pyramid Hole': 'Inverted Pyramid Entrance'
'Inverted Pyramid Hole': 'Inverted Pyramid Entrance',
'Skull Woods First Section Hole (North)': 'Skull Woods First Section Door',
'Skull Woods Second Section Hole': 'Skull Woods Second Section Door (East)',
}
entrance_map = {
@@ -2066,6 +2218,19 @@ Connector_Exit_Set = {
'Turtle Rock Isolated Ledge Exit', 'Turtle Rock Ledge Exit (West)'
}
dungeon_restriction_checks = [
(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Sanctuary Exit'], ['Sewer Drop']),
(['Desert Palace Exit (South)', 'Desert Palace Exit (East)', 'Desert Palace Exit (West)', 'Desert Palace Exit (North)'], []),
(['Turtle Rock Exit (Front)', 'Turtle Rock Isolated Ledge Exit', 'Turtle Rock Ledge Exit (West)', 'Turtle Rock Ledge Exit (East)'], []),
(['Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)', 'Skull Woods Final Section Exit'],
['Skull Pinball', 'Skull Left Drop', 'Skull Pot Circle', 'Skull Back Drop'])
]
doors_possible_connectors = [
'Sanctuary Exit', 'Desert Palace Exit (North)', 'Skull Woods First Section Exit',
'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)', 'Skull Woods Final Section Exit'
]
# Entrances that cannot be used to access a must_exit entrance - symmetrical to allow reverse lookups
Must_Exit_Invalid_Connections = defaultdict(set, {
'Dark Death Mountain Ledge (East)': {'Dark Death Mountain Ledge (West)', 'Mimic Cave'},