feat: swapped ER
This commit is contained in:
@@ -112,7 +112,7 @@ class World(object):
|
|||||||
set_player_attr('can_access_trock_big_chest', None)
|
set_player_attr('can_access_trock_big_chest', None)
|
||||||
set_player_attr('can_access_trock_middle', None)
|
set_player_attr('can_access_trock_middle', None)
|
||||||
set_player_attr('fix_fake_world', logic[player] not in ['owglitches', 'nologic']
|
set_player_attr('fix_fake_world', logic[player] not in ['owglitches', 'nologic']
|
||||||
or shuffle[player] in ['lean', 'crossed', 'insanity'])
|
or shuffle[player] in ['lean', 'swapped', 'crossed', 'insanity'])
|
||||||
set_player_attr('mapshuffle', False)
|
set_player_attr('mapshuffle', False)
|
||||||
set_player_attr('compassshuffle', False)
|
set_player_attr('compassshuffle', False)
|
||||||
set_player_attr('keyshuffle', 'none')
|
set_player_attr('keyshuffle', 'none')
|
||||||
@@ -2970,7 +2970,7 @@ class Pot(object):
|
|||||||
# byte 0: DDDE EEEE (DR, ER)
|
# byte 0: DDDE EEEE (DR, ER)
|
||||||
dr_mode = {"basic": 1, "crossed": 2, "vanilla": 0, "partitioned": 3, 'paired': 4}
|
dr_mode = {"basic": 1, "crossed": 2, "vanilla": 0, "partitioned": 3, 'paired': 4}
|
||||||
er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4, "insanity": 5, 'lite': 8,
|
er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4, "insanity": 5, 'lite': 8,
|
||||||
'lean': 9, "dungeonsfull": 7, "dungeonssimple": 6}
|
'lean': 9, "dungeonsfull": 7, "dungeonssimple": 6, 'swapped': 10}
|
||||||
|
|
||||||
# byte 1: LLLW WSS? (logic, mode, sword)
|
# byte 1: LLLW WSS? (logic, mode, sword)
|
||||||
logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4}
|
logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4}
|
||||||
|
|||||||
2
Doors.py
2
Doors.py
@@ -1506,7 +1506,7 @@ def create_doors(world, player):
|
|||||||
|
|
||||||
# static portal flags
|
# static portal flags
|
||||||
world.get_door('Sanctuary S', player).dead_end(allowPassage=True)
|
world.get_door('Sanctuary S', player).dead_end(allowPassage=True)
|
||||||
if world.mode[player] == 'open' and world.shuffle[player] not in ['crossed', 'insanity']:
|
if world.mode[player] == 'open' and world.shuffle[player] not in ['lean', 'swapped', 'crossed', 'insanity']:
|
||||||
world.get_door('Sanctuary S', player).lw_restricted = True
|
world.get_door('Sanctuary S', player).lw_restricted = True
|
||||||
world.get_door('Eastern Hint Tile Blocked Path SE', player).passage = False
|
world.get_door('Eastern Hint Tile Blocked Path SE', player).passage = False
|
||||||
world.get_door('TR Big Chest Entrance SE', player).passage = False
|
world.get_door('TR Big Chest Entrance SE', player).passage = False
|
||||||
|
|||||||
@@ -1360,7 +1360,7 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player, dunge
|
|||||||
for name, builder in dungeon_map.items():
|
for name, builder in dungeon_map.items():
|
||||||
calc_allowance_and_dead_ends(builder, connections_tuple, world, player)
|
calc_allowance_and_dead_ends(builder, connections_tuple, world, player)
|
||||||
|
|
||||||
if world.mode[player] == 'open' and world.shuffle[player] not in ['crossed', 'insanity']:
|
if world.mode[player] == 'open' and world.shuffle[player] not in ['lean', 'swapped', 'crossed', 'insanity']:
|
||||||
sanc = find_sector('Sanctuary', candidate_sectors)
|
sanc = find_sector('Sanctuary', candidate_sectors)
|
||||||
if sanc: # only run if sanc if a candidate
|
if sanc: # only run if sanc if a candidate
|
||||||
lw_builders = []
|
lw_builders = []
|
||||||
|
|||||||
@@ -809,7 +809,7 @@ def balance_prices(world, player):
|
|||||||
|
|
||||||
|
|
||||||
def check_hints(world, player):
|
def check_hints(world, player):
|
||||||
if world.shuffle[player] in ['simple', 'restricted', 'full', 'crossed', 'insanity']:
|
if world.shuffle[player] in ['simple', 'restricted', 'full', 'lite', 'lean', 'swapped', 'crossed', 'insanity']:
|
||||||
for shop, location_list in shop_to_location_table.items():
|
for shop, location_list in shop_to_location_table.items():
|
||||||
if shop in ['Capacity Upgrade', 'Paradox Shop', 'Potion Shop']:
|
if shop in ['Capacity Upgrade', 'Paradox Shop', 'Potion Shop']:
|
||||||
continue # near the queen, near potions, and near 7 chests are fine
|
continue # near the queen, near potions, and near 7 chests are fine
|
||||||
|
|||||||
2
Main.py
2
Main.py
@@ -241,7 +241,7 @@ def main(args, seed=None, fish=None):
|
|||||||
|
|
||||||
for player in range(1, world.players + 1):
|
for player in range(1, world.players + 1):
|
||||||
create_dynamic_exits(world, player)
|
create_dynamic_exits(world, player)
|
||||||
if world.experimental[player] or world.shuffle[player] in ['lite', 'lean'] or world.shuffletavern[player] or (world.customizer and world.customizer.get_entrances()):
|
if world.experimental[player] or world.shuffle[player] in ['lite', 'lean', 'swapped'] or world.shuffletavern[player] or (world.customizer and world.customizer.get_entrances()):
|
||||||
link_entrances_new(world, player)
|
link_entrances_new(world, player)
|
||||||
else:
|
else:
|
||||||
link_entrances(world, player)
|
link_entrances(world, player)
|
||||||
|
|||||||
10
Rom.py
10
Rom.py
@@ -1358,7 +1358,7 @@ def patch_rom(world, rom, player, team, is_mystery=False):
|
|||||||
rom.write_bytes(0x02F539, [0xEA, 0xEA, 0xEA, 0xEA, 0xEA] if world.powder_patch_required[player] else [0xAD, 0xBF, 0x0A, 0xF0, 0x4F])
|
rom.write_bytes(0x02F539, [0xEA, 0xEA, 0xEA, 0xEA, 0xEA] if world.powder_patch_required[player] else [0xAD, 0xBF, 0x0A, 0xF0, 0x4F])
|
||||||
|
|
||||||
# allow smith into multi-entrance caves in appropriate shuffles
|
# allow smith into multi-entrance caves in appropriate shuffles
|
||||||
if world.shuffle[player] in ['restricted', 'full', 'lite', 'lean', 'crossed', 'insanity'] or (world.shuffle[player] == 'simple' and world.mode[player] == 'inverted'):
|
if world.shuffle[player] in ['restricted', 'full', 'lite', 'lean', 'swapped', 'crossed', 'insanity'] or (world.shuffle[player] == 'simple' and world.mode[player] == 'inverted'):
|
||||||
rom.write_byte(0x18004C, 0x01)
|
rom.write_byte(0x18004C, 0x01)
|
||||||
|
|
||||||
# set correct flag for hera basement item
|
# set correct flag for hera basement item
|
||||||
@@ -1885,7 +1885,7 @@ def write_strings(rom, world, player, team):
|
|||||||
break
|
break
|
||||||
# Now we write inconvenient locations for most shuffles and finish taking care of the less chaotic ones.
|
# Now we write inconvenient locations for most shuffles and finish taking care of the less chaotic ones.
|
||||||
entrances_to_hint.update(InconvenientOtherEntrances)
|
entrances_to_hint.update(InconvenientOtherEntrances)
|
||||||
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
|
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'lite', 'lean', 'swapped']:
|
||||||
hint_count = 0
|
hint_count = 0
|
||||||
elif world.shuffle[player] in ['simple', 'restricted']:
|
elif world.shuffle[player] in ['simple', 'restricted']:
|
||||||
hint_count = 2
|
hint_count = 2
|
||||||
@@ -1935,7 +1935,7 @@ def write_strings(rom, world, player, team):
|
|||||||
entrances_to_hint.update({'Inverted Pyramid Entrance': 'The extra castle passage'})
|
entrances_to_hint.update({'Inverted Pyramid Entrance': 'The extra castle passage'})
|
||||||
else:
|
else:
|
||||||
entrances_to_hint.update({'Pyramid Entrance': 'The pyramid ledge'})
|
entrances_to_hint.update({'Pyramid Entrance': 'The pyramid ledge'})
|
||||||
hint_count = 4 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 0
|
hint_count = 4 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'swapped'] else 0
|
||||||
hint_count -= 2 if world.shuffle[player] not in ['simple', 'restricted'] else 0
|
hint_count -= 2 if world.shuffle[player] not in ['simple', 'restricted'] else 0
|
||||||
for entrance in all_entrances:
|
for entrance in all_entrances:
|
||||||
if entrance.name in entrances_to_hint:
|
if entrance.name in entrances_to_hint:
|
||||||
@@ -1954,7 +1954,7 @@ def write_strings(rom, world, player, team):
|
|||||||
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
|
if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']:
|
||||||
locations_to_hint.extend(InconvenientVanillaLocations)
|
locations_to_hint.extend(InconvenientVanillaLocations)
|
||||||
random.shuffle(locations_to_hint)
|
random.shuffle(locations_to_hint)
|
||||||
hint_count = 3 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 5
|
hint_count = 3 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'swapped'] else 5
|
||||||
hint_count -= 2 if world.doorShuffle[player] not in ['vanilla', 'basic'] else 0
|
hint_count -= 2 if world.doorShuffle[player] not in ['vanilla', 'basic'] else 0
|
||||||
del locations_to_hint[hint_count:]
|
del locations_to_hint[hint_count:]
|
||||||
for location in locations_to_hint:
|
for location in locations_to_hint:
|
||||||
@@ -2017,7 +2017,7 @@ def write_strings(rom, world, player, team):
|
|||||||
if world.bigkeyshuffle[player]:
|
if world.bigkeyshuffle[player]:
|
||||||
items_to_hint.extend(BigKeys)
|
items_to_hint.extend(BigKeys)
|
||||||
random.shuffle(items_to_hint)
|
random.shuffle(items_to_hint)
|
||||||
hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 8
|
hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'swapped'] else 8
|
||||||
hint_count += 2 if world.doorShuffle[player] not in ['vanilla', 'basic'] else 0
|
hint_count += 2 if world.doorShuffle[player] not in ['vanilla', 'basic'] else 0
|
||||||
while hint_count > 0 and len(items_to_hint) > 0:
|
while hint_count > 0 and len(items_to_hint) > 0:
|
||||||
this_item = items_to_hint.pop(0)
|
this_item = items_to_hint.pop(0)
|
||||||
|
|||||||
@@ -49,6 +49,9 @@ def main(args=None):
|
|||||||
test("Shopsanity", "--shuffle vanilla --shopsanity")
|
test("Shopsanity", "--shuffle vanilla --shopsanity")
|
||||||
test("Simple ", "--shuffle simple")
|
test("Simple ", "--shuffle simple")
|
||||||
test("Full ", "--shuffle full")
|
test("Full ", "--shuffle full")
|
||||||
|
test("Lite ", "--shuffle lite")
|
||||||
|
test("Lean ", "--shuffle lean")
|
||||||
|
test("Swapped ", "--shuffle swapped")
|
||||||
test("Crossed ", "--shuffle crossed")
|
test("Crossed ", "--shuffle crossed")
|
||||||
test("Insanity ", "--shuffle insanity")
|
test("Insanity ", "--shuffle insanity")
|
||||||
test("OWG ", "--logic owglitches")
|
test("OWG ", "--logic owglitches")
|
||||||
|
|||||||
@@ -72,6 +72,9 @@
|
|||||||
simple: 2
|
simple: 2
|
||||||
restricted: 2
|
restricted: 2
|
||||||
full: 2
|
full: 2
|
||||||
|
lite: 2
|
||||||
|
lean: 2
|
||||||
|
swapped: 2
|
||||||
crossed: 3
|
crossed: 3
|
||||||
insanity: 1
|
insanity: 1
|
||||||
open_pyramid:
|
open_pyramid:
|
||||||
|
|||||||
@@ -60,6 +60,9 @@ entrance_shuffle:
|
|||||||
simple: 1
|
simple: 1
|
||||||
restricted: 1
|
restricted: 1
|
||||||
full: 1
|
full: 1
|
||||||
|
lite: 1
|
||||||
|
lean: 1
|
||||||
|
swapped: 1
|
||||||
crossed: 1
|
crossed: 1
|
||||||
insanity: 1
|
insanity: 1
|
||||||
shufflelinks:
|
shufflelinks:
|
||||||
|
|||||||
@@ -167,6 +167,7 @@
|
|||||||
"simple",
|
"simple",
|
||||||
"restricted",
|
"restricted",
|
||||||
"full",
|
"full",
|
||||||
|
"swapped",
|
||||||
"crossed",
|
"crossed",
|
||||||
"insanity",
|
"insanity",
|
||||||
"dungeonsfull",
|
"dungeonsfull",
|
||||||
|
|||||||
@@ -206,6 +206,7 @@
|
|||||||
" connect remaining entrances.",
|
" connect remaining entrances.",
|
||||||
"Crossed: Mix cave and dungeon entrances freely while allowing",
|
"Crossed: Mix cave and dungeon entrances freely while allowing",
|
||||||
" caves to cross between worlds.",
|
" caves to cross between worlds.",
|
||||||
|
"Swapped: Same as Crossed, but entrances switch places in pairs.",
|
||||||
"Insanity: Decouple entrances and exits from each other and",
|
"Insanity: Decouple entrances and exits from each other and",
|
||||||
" shuffle them freely. Caves that used to be single",
|
" shuffle them freely. Caves that used to be single",
|
||||||
" entrance will still exit to the same location from",
|
" entrance will still exit to the same location from",
|
||||||
|
|||||||
@@ -156,6 +156,7 @@
|
|||||||
"randomizer.entrance.entranceshuffle.simple": "Simple",
|
"randomizer.entrance.entranceshuffle.simple": "Simple",
|
||||||
"randomizer.entrance.entranceshuffle.restricted": "Restricted",
|
"randomizer.entrance.entranceshuffle.restricted": "Restricted",
|
||||||
"randomizer.entrance.entranceshuffle.full": "Full",
|
"randomizer.entrance.entranceshuffle.full": "Full",
|
||||||
|
"randomizer.entrance.entranceshuffle.swapped": "Swapped",
|
||||||
"randomizer.entrance.entranceshuffle.crossed": "Crossed",
|
"randomizer.entrance.entranceshuffle.crossed": "Crossed",
|
||||||
"randomizer.entrance.entranceshuffle.insanity": "Insanity",
|
"randomizer.entrance.entranceshuffle.insanity": "Insanity",
|
||||||
"randomizer.entrance.entranceshuffle.dungeonsfull": "Dungeons + Full",
|
"randomizer.entrance.entranceshuffle.dungeonsfull": "Dungeons + Full",
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
"full",
|
"full",
|
||||||
"lite",
|
"lite",
|
||||||
"lean",
|
"lean",
|
||||||
|
"swapped",
|
||||||
"crossed",
|
"crossed",
|
||||||
"insanity",
|
"insanity",
|
||||||
"dungeonsfull",
|
"dungeonsfull",
|
||||||
|
|||||||
@@ -11,6 +11,7 @@ class EntrancePool(object):
|
|||||||
self.exits = set()
|
self.exits = set()
|
||||||
self.inverted = False
|
self.inverted = False
|
||||||
self.coupled = True
|
self.coupled = True
|
||||||
|
self.swapped = False
|
||||||
self.default_map = {}
|
self.default_map = {}
|
||||||
self.one_way_map = {}
|
self.one_way_map = {}
|
||||||
self.skull_handled = False
|
self.skull_handled = False
|
||||||
@@ -92,6 +93,7 @@ def link_entrances_new(world, player):
|
|||||||
if mode not in modes:
|
if mode not in modes:
|
||||||
raise RuntimeError(f'Shuffle mode {mode} is not yet supported')
|
raise RuntimeError(f'Shuffle mode {mode} is not yet supported')
|
||||||
mode_cfg = copy.deepcopy(modes[mode])
|
mode_cfg = copy.deepcopy(modes[mode])
|
||||||
|
avail_pool.swapped = mode_cfg['undefined'] == 'swap'
|
||||||
if avail_pool.is_standard():
|
if avail_pool.is_standard():
|
||||||
do_standard_connections(avail_pool)
|
do_standard_connections(avail_pool)
|
||||||
pool_list = mode_cfg['pools'] if 'pools' in mode_cfg else {}
|
pool_list = mode_cfg['pools'] if 'pools' in mode_cfg else {}
|
||||||
@@ -99,7 +101,10 @@ def link_entrances_new(world, player):
|
|||||||
special_shuffle = pool['special'] if 'special' in pool else None
|
special_shuffle = pool['special'] if 'special' in pool else None
|
||||||
if special_shuffle == 'drops':
|
if special_shuffle == 'drops':
|
||||||
holes, targets = find_entrances_and_targets_drops(avail_pool, pool['entrances'])
|
holes, targets = find_entrances_and_targets_drops(avail_pool, pool['entrances'])
|
||||||
connect_random(holes, targets, avail_pool)
|
if avail_pool.swapped:
|
||||||
|
connect_swapped(holes, targets, avail_pool)
|
||||||
|
else:
|
||||||
|
connect_random(holes, targets, avail_pool)
|
||||||
elif special_shuffle == 'fixed_shuffle':
|
elif special_shuffle == 'fixed_shuffle':
|
||||||
do_fixed_shuffle(avail_pool, pool['entrances'])
|
do_fixed_shuffle(avail_pool, pool['entrances'])
|
||||||
elif special_shuffle == 'same_world':
|
elif special_shuffle == 'same_world':
|
||||||
@@ -123,7 +128,10 @@ def link_entrances_new(world, player):
|
|||||||
do_vanilla_connect(pool, avail_pool)
|
do_vanilla_connect(pool, avail_pool)
|
||||||
elif special_shuffle == 'skull':
|
elif special_shuffle == 'skull':
|
||||||
entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances'])
|
entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances'])
|
||||||
connect_random(entrances, exits, avail_pool, True)
|
if avail_pool.swapped:
|
||||||
|
connect_swapped(entrances, exits, avail_pool, True)
|
||||||
|
else:
|
||||||
|
connect_random(entrances, exits, avail_pool, True)
|
||||||
avail_pool.skull_handled = True
|
avail_pool.skull_handled = True
|
||||||
else:
|
else:
|
||||||
entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances'])
|
entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances'])
|
||||||
@@ -131,7 +139,7 @@ def link_entrances_new(world, player):
|
|||||||
undefined_behavior = mode_cfg['undefined']
|
undefined_behavior = mode_cfg['undefined']
|
||||||
if undefined_behavior == 'vanilla':
|
if undefined_behavior == 'vanilla':
|
||||||
do_vanilla_connections(avail_pool)
|
do_vanilla_connections(avail_pool)
|
||||||
elif undefined_behavior == 'shuffle':
|
elif undefined_behavior in {'shuffle', 'swap'}:
|
||||||
do_main_shuffle(set(avail_pool.entrances), set(avail_pool.exits), avail_pool, mode_cfg)
|
do_main_shuffle(set(avail_pool.entrances), set(avail_pool.exits), avail_pool, mode_cfg)
|
||||||
|
|
||||||
# afterward
|
# afterward
|
||||||
@@ -186,6 +194,10 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
|
|||||||
if not avail.coupled:
|
if not avail.coupled:
|
||||||
avail.decoupled_entrances.remove('Agahnims Tower')
|
avail.decoupled_entrances.remove('Agahnims Tower')
|
||||||
avail.decoupled_exits.remove('Ganons Tower Exit')
|
avail.decoupled_exits.remove('Ganons Tower Exit')
|
||||||
|
if avail.swapped:
|
||||||
|
connect_swap('Agahnims Tower', 'Ganons Tower Exit', avail)
|
||||||
|
entrances.remove('Ganons Tower')
|
||||||
|
exits.remove('Agahnims Tower Exit')
|
||||||
elif 'Ganons Tower' in entrances:
|
elif 'Ganons Tower' in entrances:
|
||||||
connect_two_way('Ganons Tower', 'Ganons Tower Exit', avail)
|
connect_two_way('Ganons Tower', 'Ganons Tower Exit', avail)
|
||||||
entrances.remove('Ganons Tower')
|
entrances.remove('Ganons Tower')
|
||||||
@@ -207,7 +219,13 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
|
|||||||
|
|
||||||
# inverted sanc
|
# inverted sanc
|
||||||
if avail.inverted and 'Dark Sanctuary Hint' in exits:
|
if avail.inverted and 'Dark Sanctuary Hint' in exits:
|
||||||
choices = [e for e in Inverted_Dark_Sanctuary_Doors if e in entrances]
|
forbidden = set()
|
||||||
|
if avail.swapped:
|
||||||
|
forbidden.add('Dark Sanctuary Hint')
|
||||||
|
forbidden.update(Forbidden_Swap_Entrances)
|
||||||
|
if not avail.inverted:
|
||||||
|
forbidden.append('Links House')
|
||||||
|
choices = [e for e in Inverted_Dark_Sanctuary_Doors if e in entrances and e not in forbidden]
|
||||||
choice = random.choice(choices)
|
choice = random.choice(choices)
|
||||||
entrances.remove(choice)
|
entrances.remove(choice)
|
||||||
exits.remove('Dark Sanctuary Hint')
|
exits.remove('Dark Sanctuary Hint')
|
||||||
@@ -216,6 +234,10 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
|
|||||||
ext.connect(avail.world.get_entrance(choice, avail.player).parent_region)
|
ext.connect(avail.world.get_entrance(choice, avail.player).parent_region)
|
||||||
if not avail.coupled:
|
if not avail.coupled:
|
||||||
avail.decoupled_entrances.remove(choice)
|
avail.decoupled_entrances.remove(choice)
|
||||||
|
if avail.swapped and choice != 'Dark Sanctuary Hint':
|
||||||
|
swap_ent, swap_ext = connect_swap(choice, 'Dark Sanctuary Hint', avail)
|
||||||
|
entrances.remove(swap_ent)
|
||||||
|
exits.remove(swap_ext)
|
||||||
|
|
||||||
# mandatory exits
|
# mandatory exits
|
||||||
rem_entrances, rem_exits = set(), set()
|
rem_entrances, rem_exits = set(), set()
|
||||||
@@ -241,12 +263,19 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
|
|||||||
else:
|
else:
|
||||||
# cross world mandantory
|
# cross world mandantory
|
||||||
entrance_list = list(entrances)
|
entrance_list = list(entrances)
|
||||||
|
if avail.swapped:
|
||||||
|
forbidden = [e for e in Forbidden_Swap_Entrances if e in entrance_list]
|
||||||
|
entrance_list = [e for e in entrance_list if e not in forbidden]
|
||||||
must_exit, multi_exit_caves = figure_out_must_exits_cross_world(entrances, exits, avail)
|
must_exit, multi_exit_caves = figure_out_must_exits_cross_world(entrances, exits, avail)
|
||||||
do_mandatory_connections(avail, entrance_list, multi_exit_caves, must_exit)
|
do_mandatory_connections(avail, entrance_list, multi_exit_caves, must_exit)
|
||||||
rem_entrances.update(entrance_list)
|
rem_entrances.update(entrance_list)
|
||||||
|
if avail.swapped:
|
||||||
|
rem_entrances.update(forbidden)
|
||||||
|
|
||||||
rem_exits.update([x for item in multi_exit_caves for x in item])
|
rem_exits.update([x for item in multi_exit_caves for x in item])
|
||||||
rem_exits.update(exits)
|
rem_exits.update(exits)
|
||||||
|
if avail.swapped:
|
||||||
|
rem_exits = [x for x in rem_exits if x in avail.exits]
|
||||||
|
|
||||||
# old man cave
|
# old man cave
|
||||||
do_old_man_cave_exit(rem_entrances, rem_exits, avail, cross_world)
|
do_old_man_cave_exit(rem_entrances, rem_exits, avail, cross_world)
|
||||||
@@ -254,9 +283,15 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
|
|||||||
# blacksmith
|
# blacksmith
|
||||||
if 'Blacksmiths Hut' in rem_exits:
|
if 'Blacksmiths Hut' in rem_exits:
|
||||||
blacksmith_options = [x for x in Blacksmith_Options if x in rem_entrances]
|
blacksmith_options = [x for x in Blacksmith_Options if x in rem_entrances]
|
||||||
|
if avail.swapped:
|
||||||
|
blacksmith_options = [e for e in blacksmith_options if e not in Forbidden_Swap_Entrances]
|
||||||
blacksmith_choice = random.choice(blacksmith_options)
|
blacksmith_choice = random.choice(blacksmith_options)
|
||||||
connect_entrance(blacksmith_choice, 'Blacksmiths Hut', avail)
|
connect_entrance(blacksmith_choice, 'Blacksmiths Hut', avail)
|
||||||
rem_entrances.remove(blacksmith_choice)
|
rem_entrances.remove(blacksmith_choice)
|
||||||
|
if avail.swapped and blacksmith_choice != 'Blacksmiths Hut':
|
||||||
|
swap_ent, swap_ext = connect_swap(blacksmith_choice, 'Blacksmiths Hut', avail)
|
||||||
|
rem_entrances.remove(swap_ent)
|
||||||
|
rem_exits.remove(swap_ext)
|
||||||
if not avail.coupled:
|
if not avail.coupled:
|
||||||
avail.decoupled_exits.remove('Blacksmiths Hut')
|
avail.decoupled_exits.remove('Blacksmiths Hut')
|
||||||
rem_exits.remove('Blacksmiths Hut')
|
rem_exits.remove('Blacksmiths Hut')
|
||||||
@@ -266,9 +301,15 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
|
|||||||
if bomb_shop in rem_exits:
|
if bomb_shop in rem_exits:
|
||||||
bomb_shop_options = Inverted_Bomb_Shop_Options if avail.inverted else Bomb_Shop_Options
|
bomb_shop_options = Inverted_Bomb_Shop_Options if avail.inverted else Bomb_Shop_Options
|
||||||
bomb_shop_options = [x for x in bomb_shop_options if x in rem_entrances]
|
bomb_shop_options = [x for x in bomb_shop_options if x in rem_entrances]
|
||||||
|
if avail.swapped and len(bomb_shop_options) > 1:
|
||||||
|
bomb_shop_options = [x for x in bomb_shop_options if x != 'Big Bomb Shop']
|
||||||
bomb_shop_choice = random.choice(bomb_shop_options)
|
bomb_shop_choice = random.choice(bomb_shop_options)
|
||||||
connect_entrance(bomb_shop_choice, bomb_shop, avail)
|
connect_entrance(bomb_shop_choice, bomb_shop, avail)
|
||||||
rem_entrances.remove(bomb_shop_choice)
|
rem_entrances.remove(bomb_shop_choice)
|
||||||
|
if avail.swapped and bomb_shop_choice != 'Big Bomb Shop':
|
||||||
|
swap_ent, swap_ext = connect_swap(bomb_shop_choice, bomb_shop, avail)
|
||||||
|
rem_exits.remove(swap_ext)
|
||||||
|
rem_entrances.remove(swap_ent)
|
||||||
if not avail.coupled:
|
if not avail.coupled:
|
||||||
avail.decoupled_exits.remove(bomb_shop)
|
avail.decoupled_exits.remove(bomb_shop)
|
||||||
rem_exits.remove(bomb_shop)
|
rem_exits.remove(bomb_shop)
|
||||||
@@ -326,12 +367,17 @@ def do_main_shuffle(entrances, exits, avail, mode_def):
|
|||||||
rem_entrances = list(unused_entrances)
|
rem_entrances = list(unused_entrances)
|
||||||
rem_entrances.sort()
|
rem_entrances.sort()
|
||||||
rem_exits = list(rem_exits if avail.coupled else avail.decoupled_exits)
|
rem_exits = list(rem_exits if avail.coupled else avail.decoupled_exits)
|
||||||
|
if avail.swapped:
|
||||||
|
rem_exits = [x for x in rem_exits if x in avail.exits]
|
||||||
rem_exits.sort()
|
rem_exits.sort()
|
||||||
random.shuffle(rem_entrances)
|
random.shuffle(rem_entrances)
|
||||||
random.shuffle(rem_exits)
|
random.shuffle(rem_exits)
|
||||||
placing = min(len(rem_entrances), len(rem_exits))
|
placing = min(len(rem_entrances), len(rem_exits))
|
||||||
for door, target in zip(rem_entrances, rem_exits):
|
if avail.swapped:
|
||||||
connect_entrance(door, target, avail)
|
connect_swapped(rem_entrances, rem_exits, avail)
|
||||||
|
else:
|
||||||
|
for door, target in zip(rem_entrances, rem_exits):
|
||||||
|
connect_entrance(door, target, avail)
|
||||||
rem_entrances[:] = rem_entrances[placing:]
|
rem_entrances[:] = rem_entrances[placing:]
|
||||||
rem_exits[:] = rem_exits[placing:]
|
rem_exits[:] = rem_exits[placing:]
|
||||||
if rem_entrances or rem_exits:
|
if rem_entrances or rem_exits:
|
||||||
@@ -344,6 +390,8 @@ def do_old_man_cave_exit(entrances, exits, avail, cross_world):
|
|||||||
if avail.inverted and cross_world:
|
if avail.inverted and cross_world:
|
||||||
om_cave_options = Inverted_Old_Man_Entrances + Old_Man_Entrances
|
om_cave_options = Inverted_Old_Man_Entrances + Old_Man_Entrances
|
||||||
om_cave_options = [x for x in om_cave_options if x in entrances]
|
om_cave_options = [x for x in om_cave_options if x in entrances]
|
||||||
|
if avail.swapped:
|
||||||
|
om_cave_options = [e for e in om_cave_options if e not in Forbidden_Swap_Entrances]
|
||||||
om_cave_choice = random.choice(om_cave_options)
|
om_cave_choice = random.choice(om_cave_options)
|
||||||
if not avail.coupled:
|
if not avail.coupled:
|
||||||
connect_exit('Old Man Cave Exit (East)', om_cave_choice, avail)
|
connect_exit('Old Man Cave Exit (East)', om_cave_choice, avail)
|
||||||
@@ -351,6 +399,10 @@ def do_old_man_cave_exit(entrances, exits, avail, cross_world):
|
|||||||
else:
|
else:
|
||||||
connect_two_way(om_cave_choice, 'Old Man Cave Exit (East)', avail)
|
connect_two_way(om_cave_choice, 'Old Man Cave Exit (East)', avail)
|
||||||
entrances.remove(om_cave_choice)
|
entrances.remove(om_cave_choice)
|
||||||
|
if avail.swapped and om_cave_choice != 'Old Man Cave (East)':
|
||||||
|
swap_ent, swap_ext = connect_swap(om_cave_choice, 'Old Man Cave Exit (East)', avail)
|
||||||
|
entrances.remove(swap_ent)
|
||||||
|
exits.remove(swap_ext)
|
||||||
exits.remove('Old Man Cave Exit (East)')
|
exits.remove('Old Man Cave Exit (East)')
|
||||||
|
|
||||||
|
|
||||||
@@ -405,32 +457,74 @@ def do_holes_and_linked_drops(entrances, exits, avail, cross_world, keep_togethe
|
|||||||
|
|
||||||
random.shuffle(hole_entrances)
|
random.shuffle(hole_entrances)
|
||||||
if not cross_world and 'Sanctuary Grave' in holes_to_shuffle:
|
if not cross_world and 'Sanctuary Grave' in holes_to_shuffle:
|
||||||
lw_entrance = next(entrance for entrance in hole_entrances if entrance[0] in LW_Entrances)
|
hc = avail.world.get_entrance('Hyrule Castle Exit (South)', avail.player)
|
||||||
hole_entrances.remove(lw_entrance)
|
is_hc_in_dw = avail.world.mode[avail.player] == 'inverted'
|
||||||
sanc_interior = next(target for target in hole_targets if target[0] == 'Sanctuary Exit')
|
if hc.connected_region:
|
||||||
hole_targets.remove(sanc_interior)
|
is_hc_in_dw = hc.connected_region.type == RegionType.DarkWorld
|
||||||
connect_two_way(lw_entrance[0], sanc_interior[0], avail) # two-way exit
|
chosen_entrance = None
|
||||||
connect_entrance(lw_entrance[1], sanc_interior[1], avail) # hole
|
if is_hc_in_dw:
|
||||||
remove_from_list(entrances, [lw_entrance[0], lw_entrance[1]])
|
if avail.swapped:
|
||||||
remove_from_list(exits, [sanc_interior[0], sanc_interior[1]])
|
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:
|
||||||
|
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])
|
||||||
|
|
||||||
random.shuffle(hole_targets)
|
random.shuffle(hole_targets)
|
||||||
for entrance, drop in hole_entrances:
|
while len(hole_entrances):
|
||||||
ext, target = hole_targets.pop()
|
entrance, drop = hole_entrances.pop()
|
||||||
|
if avail.swapped and len(hole_targets) > 1:
|
||||||
|
ext, target = next((x, t) for x, t in hole_targets if x != entrance_map[entrance])
|
||||||
|
hole_targets.remove((ext, target))
|
||||||
|
else:
|
||||||
|
ext, target = hole_targets.pop()
|
||||||
connect_two_way(entrance, ext, avail)
|
connect_two_way(entrance, ext, avail)
|
||||||
connect_entrance(drop, target, avail)
|
connect_entrance(drop, target, avail)
|
||||||
remove_from_list(entrances, [entrance, drop])
|
remove_from_list(entrances, [entrance, drop])
|
||||||
remove_from_list(exits, [ext, target])
|
remove_from_list(exits, [ext, target])
|
||||||
|
if avail.swapped and drop_map[drop] != target:
|
||||||
|
swap_ent, swap_ext = connect_swap(entrance, ext, avail)
|
||||||
|
swap_drop, swap_tgt = connect_swap(drop, target, 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):
|
def do_links_house(entrances, exits, avail, cross_world):
|
||||||
lh_exit = 'Links House Exit'
|
lh_exit = 'Links House Exit'
|
||||||
if lh_exit in exits:
|
if lh_exit in exits:
|
||||||
|
links_house_vanilla = 'Big Bomb Shop' if avail.inverted else 'Links House'
|
||||||
if not avail.world.shufflelinks[avail.player]:
|
if not avail.world.shufflelinks[avail.player]:
|
||||||
links_house = 'Big Bomb Shop' if avail.inverted else 'Links House'
|
links_house = links_house_vanilla
|
||||||
else:
|
else:
|
||||||
forbidden = list((Isolated_LH_Doors_Inv + Inverted_Dark_Sanctuary_Doors)
|
forbidden = list((Isolated_LH_Doors_Inv + Inverted_Dark_Sanctuary_Doors)
|
||||||
if avail.inverted else Isolated_LH_Doors_Open)
|
if avail.inverted else Isolated_LH_Doors_Open)
|
||||||
|
if not avail.inverted:
|
||||||
|
if avail.world.doorShuffle[avail.player] != 'vanilla' and avail.world.intensity[avail.player] > 2:
|
||||||
|
forbidden.append('Hyrule Castle Entrance (South)')
|
||||||
|
if avail.swapped:
|
||||||
|
forbidden.append(links_house_vanilla)
|
||||||
|
forbidden.extend(Forbidden_Swap_Entrances)
|
||||||
shuffle_mode = avail.world.shuffle[avail.player]
|
shuffle_mode = avail.world.shuffle[avail.player]
|
||||||
# simple shuffle -
|
# simple shuffle -
|
||||||
if shuffle_mode == 'simple':
|
if shuffle_mode == 'simple':
|
||||||
@@ -471,6 +565,11 @@ def do_links_house(entrances, exits, avail, cross_world):
|
|||||||
avail.decoupled_entrances.remove(links_house)
|
avail.decoupled_entrances.remove(links_house)
|
||||||
avail.decoupled_exits.remove('Links House Exit')
|
avail.decoupled_exits.remove('Links House Exit')
|
||||||
avail.decoupled_exits.remove('Chris Houlihan Room Exit')
|
avail.decoupled_exits.remove('Chris Houlihan Room Exit')
|
||||||
|
if avail.swapped and links_house != links_house_vanilla:
|
||||||
|
swap_ent, swap_ext = connect_swap(links_house, lh_exit, avail)
|
||||||
|
entrances.remove(swap_ent)
|
||||||
|
exits.remove(swap_ext)
|
||||||
|
|
||||||
# links on dm
|
# links on dm
|
||||||
dm_spots = LH_DM_Connector_List.union(LH_DM_Exit_Forbidden)
|
dm_spots = LH_DM_Connector_List.union(LH_DM_Exit_Forbidden)
|
||||||
if links_house in dm_spots:
|
if links_house in dm_spots:
|
||||||
@@ -491,20 +590,20 @@ def do_links_house(entrances, exits, avail, cross_world):
|
|||||||
possible_exits.sort()
|
possible_exits.sort()
|
||||||
chosen_dm_escape = random.choice(possible_dm_exits)
|
chosen_dm_escape = random.choice(possible_dm_exits)
|
||||||
chosen_landing = random.choice(possible_exits)
|
chosen_landing = random.choice(possible_exits)
|
||||||
|
chosen_exit_start = chosen_cave.pop(0)
|
||||||
|
chosen_exit_end = chosen_cave.pop()
|
||||||
if avail.coupled:
|
if avail.coupled:
|
||||||
connect_two_way(chosen_dm_escape, chosen_cave.pop(0), avail)
|
connect_two_way(chosen_dm_escape, chosen_exit_start, avail)
|
||||||
connect_two_way(chosen_landing, chosen_cave.pop(), avail)
|
connect_two_way(chosen_landing, chosen_exit_end, avail)
|
||||||
entrances.remove(chosen_dm_escape)
|
entrances.remove(chosen_dm_escape)
|
||||||
entrances.remove(chosen_landing)
|
entrances.remove(chosen_landing)
|
||||||
else:
|
else:
|
||||||
chosen_cave_first = chosen_cave.pop(0)
|
connect_entrance(chosen_dm_escape, chosen_exit_start, avail)
|
||||||
connect_entrance(chosen_dm_escape, chosen_cave_first, avail)
|
connect_exit(chosen_exit_end, chosen_landing, avail)
|
||||||
connect_exit(chosen_cave.pop(), chosen_landing, avail)
|
|
||||||
entrances.remove(chosen_dm_escape)
|
entrances.remove(chosen_dm_escape)
|
||||||
avail.decoupled_exits.remove(chosen_cave_first)
|
avail.decoupled_exits.remove(chosen_exit_start)
|
||||||
avail.decoupled_entrances.remove(chosen_landing)
|
avail.decoupled_entrances.remove(chosen_landing)
|
||||||
# chosen cave has already been removed from exits
|
exits.add(chosen_exit_start) # this needs to be added back in
|
||||||
exits.add(chosen_cave_first) # this needs to be added back in
|
|
||||||
if len(chosen_cave):
|
if len(chosen_cave):
|
||||||
exits.update([x for x in chosen_cave])
|
exits.update([x for x in chosen_cave])
|
||||||
exits.update([x for item in multi_exit_caves for x in item])
|
exits.update([x for item in multi_exit_caves for x in item])
|
||||||
@@ -626,21 +725,44 @@ def do_cross_world_connectors(entrances, caves, avail):
|
|||||||
cave_candidate = (None, 0)
|
cave_candidate = (None, 0)
|
||||||
for i, cave in enumerate(caves):
|
for i, cave in enumerate(caves):
|
||||||
if isinstance(cave, str):
|
if isinstance(cave, str):
|
||||||
cave = (cave,)
|
cave = [cave]
|
||||||
if len(cave) > cave_candidate[1]:
|
if len(cave) > cave_candidate[1]:
|
||||||
cave_candidate = (i, len(cave))
|
cave_candidate = (i, len(cave))
|
||||||
cave = caves.pop(cave_candidate[0])
|
cave = caves.pop(cave_candidate[0])
|
||||||
|
|
||||||
if isinstance(cave, str):
|
if isinstance(cave, str):
|
||||||
cave = (cave,)
|
cave = [cave]
|
||||||
|
|
||||||
for ext in cave:
|
while len(cave):
|
||||||
|
ext = cave.pop()
|
||||||
if not avail.coupled:
|
if not avail.coupled:
|
||||||
choice = random.choice(avail.decoupled_entrances)
|
choice = random.choice(avail.decoupled_entrances)
|
||||||
connect_exit(ext, choice, avail)
|
connect_exit(ext, choice, avail)
|
||||||
avail.decoupled_entrances.remove(choice)
|
avail.decoupled_entrances.remove(choice)
|
||||||
else:
|
else:
|
||||||
connect_two_way(entrances.pop(), ext, avail)
|
if avail.swapped and len(entrances) > 1:
|
||||||
|
chosen_entrance = next(e for e in entrances if combine_map[e] != ext)
|
||||||
|
entrances.remove(chosen_entrance)
|
||||||
|
else:
|
||||||
|
chosen_entrance = entrances.pop()
|
||||||
|
connect_two_way(chosen_entrance, ext, avail)
|
||||||
|
if avail.swapped:
|
||||||
|
swap_ent, swap_ext = connect_swap(chosen_entrance, ext, avail)
|
||||||
|
if swap_ent:
|
||||||
|
entrances.remove(swap_ent)
|
||||||
|
if chosen_entrance not in single_entrance_map:
|
||||||
|
if swap_ext in cave:
|
||||||
|
cave.remove(swap_ext)
|
||||||
|
else:
|
||||||
|
for c in caves:
|
||||||
|
if swap_ext == c:
|
||||||
|
caves.remove(swap_ext)
|
||||||
|
break
|
||||||
|
if not isinstance(c, str) and swap_ext in c:
|
||||||
|
c.remove(swap_ext)
|
||||||
|
if len(c) == 0:
|
||||||
|
caves.remove(c)
|
||||||
|
break
|
||||||
|
|
||||||
|
|
||||||
def do_fixed_shuffle(avail, entrance_list):
|
def do_fixed_shuffle(avail, entrance_list):
|
||||||
@@ -841,7 +963,12 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit):
|
|||||||
invalid_connections[entrance] = set()
|
invalid_connections[entrance] = set()
|
||||||
if entrance in must_exit:
|
if entrance in must_exit:
|
||||||
must_exit.remove(entrance)
|
must_exit.remove(entrance)
|
||||||
entrances.append(entrance)
|
if entrance not in entrances:
|
||||||
|
entrances.append(entrance)
|
||||||
|
if avail.swapped:
|
||||||
|
swap_forbidden = [e for e in entrances if combine_map[e] in must_exit]
|
||||||
|
for e in swap_forbidden:
|
||||||
|
entrances.remove(e)
|
||||||
entrances.sort() # sort these for consistency
|
entrances.sort() # sort these for consistency
|
||||||
random.shuffle(entrances)
|
random.shuffle(entrances)
|
||||||
random.shuffle(cave_options)
|
random.shuffle(cave_options)
|
||||||
@@ -854,6 +981,19 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit):
|
|||||||
invalid_connections[ext] = invalid_connections[ext].union({'Agahnims Tower', 'Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)'})
|
invalid_connections[ext] = invalid_connections[ext].union({'Agahnims Tower', 'Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)'})
|
||||||
break
|
break
|
||||||
|
|
||||||
|
def connect_cave_swap(entrance, exit, current_cave):
|
||||||
|
swap_entrance, swap_exit = connect_swap(entrance, exit, avail)
|
||||||
|
if swap_entrance and entrance not in single_entrance_map:
|
||||||
|
for option in cave_options:
|
||||||
|
if swap_exit in option and option == current_cave:
|
||||||
|
x=0
|
||||||
|
if swap_exit in option and option != current_cave:
|
||||||
|
option.remove(swap_exit)
|
||||||
|
if len(option) == 0:
|
||||||
|
cave_options.remove(option)
|
||||||
|
break
|
||||||
|
return swap_entrance, swap_exit
|
||||||
|
|
||||||
used_caves = []
|
used_caves = []
|
||||||
required_entrances = 0 # Number of entrances reserved for used_caves
|
required_entrances = 0 # Number of entrances reserved for used_caves
|
||||||
while must_exit:
|
while must_exit:
|
||||||
@@ -861,9 +1001,10 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit):
|
|||||||
# find multi exit cave
|
# find multi exit cave
|
||||||
candidates = []
|
candidates = []
|
||||||
for candidate in cave_options:
|
for candidate in cave_options:
|
||||||
if not isinstance(candidate, str) and (candidate in used_caves
|
if not isinstance(candidate, str) and len(candidate) > 1 and (candidate in used_caves
|
||||||
or len(candidate) < len(entrances) - required_entrances):
|
or len(candidate) < len(entrances) - required_entrances):
|
||||||
candidates.append(candidate)
|
if not avail.swapped or (combine_map[exit] not in candidate and not any(e for e in must_exit if combine_map[e] in candidate)): #maybe someday allow these, but we need to disallow mutual locks in Swapped
|
||||||
|
candidates.append(candidate)
|
||||||
cave = random.choice(candidates)
|
cave = random.choice(candidates)
|
||||||
if cave is None:
|
if cave is None:
|
||||||
raise RuntimeError('No more caves left. Should not happen!')
|
raise RuntimeError('No more caves left. Should not happen!')
|
||||||
@@ -871,13 +1012,23 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit):
|
|||||||
# all caves are sorted so that the last exit is always reachable
|
# all caves are sorted so that the last exit is always reachable
|
||||||
rnd_cave = list(cave)
|
rnd_cave = list(cave)
|
||||||
shuffle_connector_exits(rnd_cave) # should be the same as unbiasing some entrances...
|
shuffle_connector_exits(rnd_cave) # should be the same as unbiasing some entrances...
|
||||||
entrances.remove(exit)
|
if avail.swapped and exit in swap_forbidden:
|
||||||
|
swap_forbidden.remove(exit)
|
||||||
|
else:
|
||||||
|
entrances.remove(exit)
|
||||||
connect_two_way(exit, rnd_cave[-1], avail)
|
connect_two_way(exit, rnd_cave[-1], avail)
|
||||||
|
if avail.swapped:
|
||||||
|
swap_ent, _ = connect_cave_swap(exit, rnd_cave[-1], cave)
|
||||||
|
entrances.remove(swap_ent)
|
||||||
if len(cave) == 2:
|
if len(cave) == 2:
|
||||||
entrance = next(e for e in entrances[::-1] if e not in invalid_connections[exit]
|
entrance = next(e for e in entrances[::-1] if e not in invalid_connections[exit]
|
||||||
and e not in invalid_cave_connections[tuple(cave)] and e not in must_exit)
|
and e not in invalid_cave_connections[tuple(cave)] and e not in must_exit
|
||||||
|
and (not avail.swapped or rnd_cave[0] != combine_map[e]))
|
||||||
entrances.remove(entrance)
|
entrances.remove(entrance)
|
||||||
connect_two_way(entrance, rnd_cave[0], avail)
|
connect_two_way(entrance, rnd_cave[0], avail)
|
||||||
|
if avail.swapped and combine_map[entrance] != rnd_cave[0]:
|
||||||
|
swap_ent, _ = connect_cave_swap(entrance, rnd_cave[0], cave)
|
||||||
|
entrances.remove(swap_ent)
|
||||||
if cave in used_caves:
|
if cave in used_caves:
|
||||||
required_entrances -= 2
|
required_entrances -= 2
|
||||||
used_caves.remove(cave)
|
used_caves.remove(cave)
|
||||||
@@ -887,10 +1038,18 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit):
|
|||||||
elif cave[-1] == 'Spectacle Rock Cave Exit': # Spectacle rock only has one exit
|
elif cave[-1] == 'Spectacle Rock Cave Exit': # Spectacle rock only has one exit
|
||||||
cave_entrances = []
|
cave_entrances = []
|
||||||
for cave_exit in rnd_cave[:-1]:
|
for cave_exit in rnd_cave[:-1]:
|
||||||
entrance = next(e for e in entrances[::-1] if e not in invalid_connections[exit] and e not in must_exit)
|
if avail.swapped and cave_exit not in avail.exits:
|
||||||
cave_entrances.append(entrance)
|
entrance = avail.world.get_entrance(cave_exit, avail.player).parent_region.entrances[0].name
|
||||||
entrances.remove(entrance)
|
cave_entrances.append(entrance)
|
||||||
connect_two_way(entrance, cave_exit, avail)
|
else:
|
||||||
|
entrance = next(e for e in entrances[::-1] if e not in invalid_connections[exit] and e not in must_exit
|
||||||
|
and (not avail.swapped or cave_exit != combine_map[e]))
|
||||||
|
cave_entrances.append(entrance)
|
||||||
|
entrances.remove(entrance)
|
||||||
|
connect_two_way(entrance, cave_exit, avail)
|
||||||
|
if avail.swapped and combine_map[entrance] != cave_exit:
|
||||||
|
swap_ent, _ = connect_cave_swap(entrance, cave_exit, cave)
|
||||||
|
entrances.remove(swap_ent)
|
||||||
if entrance not in invalid_connections:
|
if entrance not in invalid_connections:
|
||||||
invalid_connections[exit] = set()
|
invalid_connections[exit] = set()
|
||||||
if all(entrance in invalid_connections for entrance in cave_entrances):
|
if all(entrance in invalid_connections for entrance in cave_entrances):
|
||||||
@@ -911,11 +1070,20 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit):
|
|||||||
for cave in used_caves:
|
for cave in used_caves:
|
||||||
if cave in cave_options: # check if we placed multiple entrances from this 3 or 4 exit
|
if cave in cave_options: # check if we placed multiple entrances from this 3 or 4 exit
|
||||||
for cave_exit in cave:
|
for cave_exit in cave:
|
||||||
entrance = next(e for e in entrances[::-1] if e not in invalid_cave_connections[tuple(cave)])
|
if avail.swapped and cave_exit not in avail.exits:
|
||||||
invalid_cave_connections[tuple(cave)] = set()
|
continue
|
||||||
entrances.remove(entrance)
|
else:
|
||||||
connect_two_way(entrance, cave_exit, avail)
|
entrance = next(e for e in entrances[::-1] if e not in invalid_cave_connections[tuple(cave)]
|
||||||
|
and (not avail.swapped or cave_exit != combine_map[e]))
|
||||||
|
invalid_cave_connections[tuple(cave)] = set()
|
||||||
|
entrances.remove(entrance)
|
||||||
|
connect_two_way(entrance, cave_exit, avail)
|
||||||
|
if avail.swapped and combine_map[entrance] != cave_exit:
|
||||||
|
swap_ent, _ = connect_cave_swap(entrance, cave_exit, cave)
|
||||||
|
entrances.remove(swap_ent)
|
||||||
cave_options.remove(cave)
|
cave_options.remove(cave)
|
||||||
|
if avail.swapped:
|
||||||
|
entrances.extend(swap_forbidden)
|
||||||
|
|
||||||
|
|
||||||
def do_mandatory_connections_decoupled(avail, cave_options, must_exit):
|
def do_mandatory_connections_decoupled(avail, cave_options, must_exit):
|
||||||
@@ -1033,6 +1201,47 @@ def inverted_substitution(avail_pool, collection, is_entrance, is_set=False):
|
|||||||
pass
|
pass
|
||||||
|
|
||||||
|
|
||||||
|
def connect_swapped(entrancelist, targetlist, avail, two_way=False):
|
||||||
|
random.shuffle(entrancelist)
|
||||||
|
sorted_targets = list()
|
||||||
|
for ent in entrancelist:
|
||||||
|
if ent in combine_map:
|
||||||
|
if combine_map[ent] not in targetlist:
|
||||||
|
logging.getLogger('').error(f'{combine_map[ent]} not in target list, cannot swap entrance')
|
||||||
|
raise Exception(f'{combine_map[ent]} not in target list, cannot swap entrance')
|
||||||
|
sorted_targets.append(combine_map[ent])
|
||||||
|
if len(sorted_targets):
|
||||||
|
targetlist = list(sorted_targets)
|
||||||
|
else:
|
||||||
|
targetlist = list(targetlist)
|
||||||
|
indexlist = list(range(len(targetlist)))
|
||||||
|
random.shuffle(indexlist)
|
||||||
|
|
||||||
|
while len(indexlist) > 1:
|
||||||
|
index1 = indexlist.pop()
|
||||||
|
index2 = indexlist.pop()
|
||||||
|
targetlist[index1], targetlist[index2] = targetlist[index2], targetlist[index1]
|
||||||
|
|
||||||
|
for exit, target in zip(entrancelist, targetlist):
|
||||||
|
if two_way:
|
||||||
|
connect_two_way(exit, target, avail)
|
||||||
|
else:
|
||||||
|
connect_entrance(exit, target, avail)
|
||||||
|
|
||||||
|
|
||||||
|
def connect_swap(entrance, exit, avail):
|
||||||
|
swap_exit = combine_map[entrance]
|
||||||
|
if swap_exit != exit:
|
||||||
|
swap_entrance = next(e for e, x in combine_map.items() if x == exit)
|
||||||
|
if swap_entrance in ['Pyramid Entrance', 'Pyramid Hole'] and avail.inverted:
|
||||||
|
swap_entrance = 'Inverted ' + swap_entrance
|
||||||
|
if entrance in entrance_map:
|
||||||
|
connect_two_way(swap_entrance, swap_exit, avail)
|
||||||
|
else:
|
||||||
|
connect_entrance(swap_entrance, swap_exit, avail)
|
||||||
|
return swap_entrance, swap_exit
|
||||||
|
return None, None
|
||||||
|
|
||||||
def connect_random(exitlist, targetlist, avail, two_way=False):
|
def connect_random(exitlist, targetlist, avail, two_way=False):
|
||||||
targetlist = list(targetlist)
|
targetlist = list(targetlist)
|
||||||
random.shuffle(targetlist)
|
random.shuffle(targetlist)
|
||||||
@@ -1479,6 +1688,23 @@ modes = {
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
'swapped': {
|
||||||
|
'undefined': 'swap',
|
||||||
|
'keep_drops_together': 'on',
|
||||||
|
'cross_world': 'on',
|
||||||
|
'pools': {
|
||||||
|
'skull_drops': {
|
||||||
|
'special': 'drops',
|
||||||
|
'entrances': ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)',
|
||||||
|
'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole']
|
||||||
|
},
|
||||||
|
'skull_doors': {
|
||||||
|
'special': 'skull',
|
||||||
|
'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)',
|
||||||
|
'Skull Woods Second Section Door (West)']
|
||||||
|
},
|
||||||
|
}
|
||||||
|
},
|
||||||
'crossed': {
|
'crossed': {
|
||||||
'undefined': 'shuffle',
|
'undefined': 'shuffle',
|
||||||
'keep_drops_together': 'on',
|
'keep_drops_together': 'on',
|
||||||
@@ -1641,6 +1867,8 @@ single_entrance_map = {
|
|||||||
'Blinds Hideout': 'Blinds Hideout', 'Waterfall of Wishing': 'Waterfall of Wishing'
|
'Blinds Hideout': 'Blinds Hideout', 'Waterfall of Wishing': 'Waterfall of Wishing'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
combine_map = {**entrance_map, **single_entrance_map, **drop_map}
|
||||||
|
|
||||||
default_dw = {
|
default_dw = {
|
||||||
'Thieves Town Exit', 'Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)',
|
'Thieves Town Exit', 'Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)',
|
||||||
'Skull Woods Second Section Exit (West)', 'Skull Woods Final Section Exit', 'Ice Palace Exit', 'Misery Mire Exit',
|
'Skull Woods Second Section Exit (West)', 'Skull Woods Final Section Exit', 'Ice Palace Exit', 'Misery Mire Exit',
|
||||||
@@ -1882,6 +2110,8 @@ Inverted_Bomb_Shop_Options = [
|
|||||||
'Agahnims Tower', 'Ganons Tower', 'Dark Sanctuary Hint', 'Big Bomb Shop', 'Links House'] + Blacksmith_Options
|
'Agahnims Tower', 'Ganons Tower', 'Dark Sanctuary Hint', 'Big Bomb Shop', 'Links House'] + Blacksmith_Options
|
||||||
|
|
||||||
|
|
||||||
|
Forbidden_Swap_Entrances = {'Old Man Cave (East)', 'Blacksmiths Hut', 'Big Bomb Shop'}
|
||||||
|
|
||||||
# these are connections that cannot be shuffled and always exist.
|
# these are connections that cannot be shuffled and always exist.
|
||||||
# They link together separate parts of the world we need to divide into regions
|
# They link together separate parts of the world we need to divide into regions
|
||||||
mandatory_connections = [('Links House S&Q', 'Links House'),
|
mandatory_connections = [('Links House S&Q', 'Links House'),
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
meta:
|
meta:
|
||||||
seed: 398
|
seed: 394
|
||||||
settings:
|
settings:
|
||||||
1:
|
1:
|
||||||
# mode: standard
|
# mode: standard
|
||||||
@@ -7,10 +7,11 @@ settings:
|
|||||||
# boss_shuffle: random
|
# boss_shuffle: random
|
||||||
# dropshuffle: underworld
|
# dropshuffle: underworld
|
||||||
# enemy_shuffle: shuffled
|
# enemy_shuffle: shuffled
|
||||||
# door_shuffle: crossed
|
door_shuffle: partitioned
|
||||||
# intensity: 3
|
intensity: 3
|
||||||
shuffle: insanity
|
shuffle: lite
|
||||||
experimental: on
|
experimental: on
|
||||||
|
shopsanity: on
|
||||||
|
|
||||||
# dungeon_counters: 'on'
|
# dungeon_counters: 'on'
|
||||||
#entrances:
|
#entrances:
|
||||||
@@ -19,23 +20,28 @@ settings:
|
|||||||
# Hyrule Castle Secret Entrance Drop: Lumberjack Tree (top)
|
# Hyrule Castle Secret Entrance Drop: Lumberjack Tree (top)
|
||||||
# two-way:
|
# two-way:
|
||||||
# Hyrule Castle Entrance (South): Links House Exit
|
# Hyrule Castle Entrance (South): Links House Exit
|
||||||
#doors:
|
doors:
|
||||||
# 1:
|
1:
|
||||||
# lobbies:
|
# lobbies:
|
||||||
# Hyrule Castle South: GT Lobby S
|
# Hyrule Castle South: GT Lobby S
|
||||||
# doors:
|
doors:
|
||||||
# GT Lobby Left Down Stairs: GT Quad Pot Up Stairs
|
TR Eye Bridge SW: Ice Compass Room NE
|
||||||
# GT Lobby Right Down Stairs: GT Lobby Up Stairs
|
TR Lazy Eyes SE: Mire Conveyor Barrier NW
|
||||||
# GT Beam Dash ES: Swamp Big Key Ledge WN
|
Hera Basement Cage Up Stairs: Hera Lobby Down Stairs
|
||||||
|
Hera Lobby Key Stairs:
|
||||||
|
dest: Hera Boss Down Stairs
|
||||||
|
type: Key Door
|
||||||
|
Hera Tile Room Up Stairs: Hera 5F Down Stairs
|
||||||
|
|
||||||
|
|
||||||
#bosses:
|
#bosses:
|
||||||
# 1:
|
# 1:
|
||||||
# Ganons Tower (middle): Trinexx
|
# Ganons Tower (middle): Trinexx
|
||||||
|
|
||||||
|
|
||||||
#placements:
|
placements:
|
||||||
# 1:
|
1:
|
||||||
# Lumberjack Tree: Lamp#2
|
Tower of Hera - Basement Cage: Small Key (Tower of Hera)
|
||||||
# Link's House: Lamp
|
# Link's House: Lamp
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user