diff --git a/BaseClasses.py b/BaseClasses.py index 752b96ae..724410c6 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -1661,7 +1661,7 @@ class Entrance(object): # this is checked first as this often the shortest path follower_region = start_region if follower_region.type not in [RegionType.LightWorld, RegionType.DarkWorld]: - follower_region = start_region.entrances[0].parent_region + follower_region = [i for i in start_region.entrances if i.parent_region.name != 'Menu'][0].parent_region if (follower_region.world.mode[self.player] != 'inverted') == (follower_region.type == RegionType.LightWorld): from OWEdges import OWTileRegions from OverworldShuffle import ow_connections diff --git a/DoorShuffle.py b/DoorShuffle.py index fcd312cd..b55d2f6a 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -1815,10 +1815,7 @@ def remove_pair_type_if_present(door, world, player): def find_inaccessible_regions(world, player): world.inaccessible_regions[player] = [] - if world.mode[player] != 'inverted': - start_regions = ['Links House', 'Sanctuary'] - else: - start_regions = ['Links House', 'Dark Sanctuary Hint'] + start_regions = ['Links House' if not world.is_tile_swapped(0x2c, player) else 'Big Bomb Shop', 'Sanctuary' if world.mode[player] != 'inverted' else 'Dark Sanctuary Hint'] regs = convert_regions(start_regions, world, player) all_regions = [r for r in world.regions if r.player == player and r.type is not RegionType.Dungeon] visited_regions = set() diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 8f92644d..acd6adc6 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -54,33 +54,33 @@ def link_entrances(world, player): for exitname, regionname in mandatory_connections: connect_simple(world, exitname, regionname, player) - if not invFlag: - for exitname, regionname in open_mandatory_connections: - connect_simple(world, exitname, regionname, player) + if not world.is_tile_swapped(0x2c, player): + connect_simple(world, 'Links House S&Q', 'Links House', player) else: - for exitname, regionname in inverted_mandatory_connections: - connect_simple(world, exitname, regionname, player) + connect_simple(world, 'Links House S&Q', 'Big Bomb Shop', player) + + if not invFlag: + connect_simple(world, 'Sanctuary S&Q', 'Sanctuary', player) + else: + connect_simple(world, 'Sanctuary S&Q', 'Dark Sanctuary Hint', player) connect_simple(world, 'Tavern North', 'Tavern', player) + suppress_spoiler = False connect_custom(world, player) + suppress_spoiler = True # if we do not shuffle, set default connections if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: for entrancename, exitname in default_connections + drop_connections + default_item_connections + default_shop_connections: - connect_logical(world, entrancename, exitname, player, False) + connect_logical(world, entrancename, exitname, player, exitname.endswith(' Exit')) for entrancename, exitname in default_connector_connections + dropexit_connections: connect_logical(world, entrancename, exitname, player, True) if invFlag: world.get_entrance('Dark Sanctuary Hint Exit', player).connect(world.get_entrance('Dark Sanctuary Hint', player).parent_region) + if world.is_tile_swapped(0x2c, player): + world.get_entrance('Big Bomb Shop Exit', player).connect(world.get_entrance('Big Bomb Shop', player).parent_region) - if not invFlag: - for entrancename, exitname in open_default_connections: - connect_logical(world, entrancename, exitname, player, exitname.endswith(' Exit')) - else: - for entrancename, exitname in inverted_default_connections: - connect_logical(world, entrancename, exitname, player, exitname.endswith(' Exit')) - ignore_pool = False # dungeon entrance shuffle @@ -215,11 +215,12 @@ def link_entrances(world, player): junk_fill_inaccessible(world, player) # place bomb shop, has limited options - bomb_shop_doors = list(entrance_pool) - if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player): - bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']] - bomb_shop = random.choice(bomb_shop_doors) - connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) + if not world.is_tile_swapped(0x2c, player): + bomb_shop_doors = list(entrance_pool) + if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player): + bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']] + bomb_shop = random.choice(bomb_shop_doors) + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) # place remaining doors connect_doors(world, list(entrance_pool), list(exit_pool), player) @@ -260,11 +261,12 @@ def link_entrances(world, player): place_old_man(world, lw_entrances if not invFlag else dw_entrances, player) # place bomb shop, has limited options - bomb_shop_doors = list(entrance_pool) - if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player): - bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']] - bomb_shop = random.choice(bomb_shop_doors) - connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) + if not world.is_tile_swapped(0x2c, player): + bomb_shop_doors = list(entrance_pool) + if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player): + bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']] + bomb_shop = random.choice(bomb_shop_doors) + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) # shuffle connectors lw_entrances = [e for e in lw_entrances if e in entrance_pool] @@ -317,11 +319,12 @@ def link_entrances(world, player): place_old_man(world, lw_entrances if not invFlag else dw_entrances, player, list(zip(*drop_connections + dropexit_connections))[0]) # place bomb shop, has limited options - bomb_shop_doors = [e for e in entrance_pool if e not in list(zip(*drop_connections + dropexit_connections))[0]] - if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player): - bomb_shop_doors = [e for e in bomb_shop_doors if e not in ['Pyramid Fairy']] - bomb_shop = random.choice(bomb_shop_doors) - connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) + if not world.is_tile_swapped(0x2c, player): + bomb_shop_doors = [e for e in entrance_pool if e not in list(zip(*drop_connections + dropexit_connections))[0]] + if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player): + bomb_shop_doors = [e for e in bomb_shop_doors if e not in ['Pyramid Fairy']] + bomb_shop = random.choice(bomb_shop_doors) + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) # shuffle connectors lw_entrances = [e for e in lw_entrances if e in entrance_pool] @@ -419,11 +422,12 @@ def link_entrances(world, player): connect_caves(world, lw_entrances, dw_entrances, caves, player) # place bomb shop, has limited options - bomb_shop_doors = list(entrance_pool) - if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player): - bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']] - bomb_shop = random.choice(bomb_shop_doors) - connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) + if not world.is_tile_swapped(0x2c, player): + bomb_shop_doors = list(entrance_pool) + if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player): + bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']] + bomb_shop = random.choice(bomb_shop_doors) + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) # place remaining doors connect_doors(world, list(entrance_pool), list(exit_pool), player) @@ -473,11 +477,12 @@ def link_entrances(world, player): connect_caves(world, connector_entrances, [], caves, player) # place bomb shop, has limited options - bomb_shop_doors = list(entrance_pool) - if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player): - bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']] - bomb_shop = random.choice(bomb_shop_doors) - connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) + if not world.is_tile_swapped(0x2c, player): + bomb_shop_doors = list(entrance_pool) + if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player): + bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']] + bomb_shop = random.choice(bomb_shop_doors) + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) # place remaining doors connect_doors(world, list(entrance_pool), list(exit_pool), player) @@ -522,11 +527,12 @@ def link_entrances(world, player): place_old_man(world, pool, player) # place bomb shop, has limited options - bomb_shop_doors = list(entrance_pool) - if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player): - bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']] - bomb_shop = random.choice(bomb_shop_doors) - connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) + if not world.is_tile_swapped(0x2c, player): + bomb_shop_doors = list(entrance_pool) + if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player): + bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']] + bomb_shop = random.choice(bomb_shop_doors) + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) # shuffle connectors pool = [e for e in pool if e in entrance_pool] @@ -605,13 +611,14 @@ def link_entrances(world, player): caves.append('Old Man Cave Exit (West)') # place bomb shop, has limited options - bomb_shop_doors = list(entrance_pool) - if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player): - bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']] - random.shuffle(bomb_shop_doors) - bomb_shop = bomb_shop_doors.pop() - pool.remove(bomb_shop) - connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) + if not world.is_tile_swapped(0x2c, player): + bomb_shop_doors = list(entrance_pool) + if world.logic[player] in ['noglitches', 'minorglitches'] or world.is_tile_swapped(0x1b, player): + bomb_shop_doors = [e for e in entrance_pool if e not in ['Pyramid Fairy']] + random.shuffle(bomb_shop_doors) + bomb_shop = bomb_shop_doors.pop() + pool.remove(bomb_shop) + connect_entrance(world, bomb_shop, 'Big Bomb Shop', player) # shuffle connectors doors = list(entrance_pool) @@ -635,8 +642,8 @@ def link_entrances(world, player): # ensure Houlihan exits where Links House does # TODO: Plando should overrule this if not links_house: - for links_house in world.get_entrance('Links House Exit', player).connected_region.exits: - if links_house.connected_region and links_house.connected_region.name == 'Links House': + for links_house in world.get_entrance('Links House Exit' if not world.is_tile_swapped(0x2c, player) else 'Big Bomb Shop Exit', player).connected_region.exits: + if links_house.connected_region and links_house.connected_region.name == ('Links House' if not world.is_tile_swapped(0x2c, player) else 'Big Bomb Shop'): links_house = links_house.name break connect_exit(world, 'Chris Houlihan Room Exit', links_house, player) @@ -1335,7 +1342,7 @@ def full_shuffle_dungeons(world, Dungeon_Exits, player): def place_links_house(world, player, ignore_list=[]): invFlag = world.mode[player] == 'inverted' if world.mode[player] == 'standard' or not world.shufflelinks[player]: - links_house = 'Links House' if not invFlag else 'Big Bomb Shop' + links_house = 'Links House' if not world.is_tile_swapped(0x2c, player) else 'Big Bomb Shop' else: if invFlag: for dark_sanc in world.get_entrance('Dark Sanctuary Hint Exit', player).connected_region.exits: @@ -1354,7 +1361,11 @@ def place_links_house(world, player, ignore_list=[]): links_house_doors = [e for e in links_house_doors if e not in ignore_list] assert len(links_house_doors), 'No valid candidates to place Links House' links_house = random.choice(links_house_doors) - connect_two_way(world, links_house, 'Links House Exit', player) + if not world.is_tile_swapped(0x2c, player): + connect_two_way(world, links_house, 'Links House Exit', player) + else: + connect_entrance(world, links_house, 'Big Bomb Shop', player) + world.get_entrance('Big Bomb Shop Exit', player).connect(world.get_entrance(links_house, player).parent_region) return links_house @@ -2037,8 +2048,7 @@ Exit_Pool_Base = ['Links House Exit', 'Pyramid'] # these are connections that cannot be shuffled and always exist. They link together separate parts of the world we need to divide into regions -mandatory_connections = [('Links House S&Q', 'Links House'), - ('Old Man S&Q', 'Old Man House'), +mandatory_connections = [('Old Man S&Q', 'Old Man House'), # UW Connections ('Lost Woods Hideout (top to bottom)', 'Lost Woods Hideout (bottom)'), @@ -2069,10 +2079,6 @@ mandatory_connections = [('Links House S&Q', 'Links House'), ('Ganon Drop', 'Bottom of Pyramid') ] -open_mandatory_connections = [('Sanctuary S&Q', 'Sanctuary')] - -inverted_mandatory_connections = [('Sanctuary S&Q', 'Dark Sanctuary Hint')] - # non-shuffled entrance links default_connections = [('Lumberjack House', 'Lumberjack House'), ('Bonk Fairy (Light)', 'Bonk Fairy (Light)'), @@ -2137,7 +2143,8 @@ default_connector_connections = [('Old Man Cave (West)', 'Old Man Cave Exit (Wes ('Hookshot Cave Back Entrance', 'Hookshot Cave Back Exit') ] -default_item_connections = [('Mimic Cave', 'Mimic Cave'), +default_item_connections = [('Links House', 'Links House Exit'), + ('Mimic Cave', 'Mimic Cave'), ('Waterfall of Wishing', 'Waterfall of Wishing'), ('Bonk Rock Cave', 'Bonk Rock Cave'), ('Graveyard Cave', 'Graveyard Cave'), @@ -2161,6 +2168,7 @@ default_item_connections = [('Mimic Cave', 'Mimic Cave'), ('Brewery', 'Brewery'), ('Pyramid Fairy', 'Pyramid Fairy'), ('Dark World Hammer Peg Cave', 'Dark World Hammer Peg Cave'), + ('Big Bomb Shop', 'Big Bomb Shop'), ('Mire Shed', 'Mire Shed'), ('Hype Cave', 'Hype Cave') ] @@ -2196,14 +2204,6 @@ default_dropexit_connections = [('Lost Woods Hideout Stump', 'Lost Woods Hideout #('Pyramid Entrance', 'Pyramid Exit') # this is dynamically added because of Inverted/OW Mixed ] -open_default_connections = [('Links House', 'Links House Exit'), - ('Big Bomb Shop', 'Big Bomb Shop') - ] - -inverted_default_connections = [('Big Bomb Shop', 'Links House Exit'), - ('Links House', 'Big Bomb Shop') - ] - # non shuffled dungeons default_dungeon_connections = [('Desert Palace Entrance (South)', 'Desert Palace Exit (South)'), ('Desert Palace Entrance (West)', 'Desert Palace Exit (West)'), diff --git a/Regions.py b/Regions.py index 0dd69282..47864ca1 100644 --- a/Regions.py +++ b/Regions.py @@ -326,7 +326,7 @@ def create_regions(world, player): create_cave_region(player, 'Dark World Hammer Peg Cave', 'a cave with an item', ['Peg Cave']), create_cave_region(player, 'Archery Game', 'a game of skill'), create_cave_region(player, 'Bonk Fairy (Dark)', 'a fairy fountain'), - create_cave_region(player, 'Big Bomb Shop', 'the bomb shop', ['Big Bomb']), + create_cave_region(player, 'Big Bomb Shop', 'the bomb shop', ['Big Bomb'], ['Big Bomb Shop Exit']), create_cave_region(player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'), create_cave_region(player, 'East Dark World Hint', 'a storyteller'), create_cave_region(player, 'Hype Cave', 'a bounty of five items', ['Hype Cave - Top', 'Hype Cave - Middle Right', 'Hype Cave - Middle Left', diff --git a/Rom.py b/Rom.py index 46e863be..684a2022 100644 --- a/Rom.py +++ b/Rom.py @@ -800,7 +800,10 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): if should_be_bunny(sanc_region, world.mode[player]): rom.write_bytes(0x13fff2, [0x12, 0x00]) - lh_name = 'Links House' + if not world.is_tile_swapped(0x2c, player): + lh_name = 'Links House' + else: + lh_name = 'Big Bomb Shop' links_house = world.get_region(lh_name, player) if should_be_bunny(links_house, world.mode[player]): rom.write_bytes(0x13fff0, [0x04, 0x01]) @@ -2625,10 +2628,24 @@ def set_inverted_mode(world, player, rom, inverted_buffer): if world.is_tile_swapped(0x29, player): rom.write_bytes(snes_to_pc(0x06B2AB), [0xF0, 0xE1, 0x05]) # frog pickup on contact if world.is_tile_swapped(0x2c, player): - if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: - rom.write_byte(0x15B8C, 0x6C) # exit links at bomb shop area - rom.write_byte(0xDBB73 + 0x00, 0x53) # switch bomb shop and links house - rom.write_byte(0xDBB73 + 0x52, 0x01) + rom.write_bytes(snes_to_pc(0x03F484), [0xFD, 0x4B, 0x68]) # place bed in bomb shop + + # spawn in bomb shop + patch_shuffled_bomb_shop(world, rom, player) + rom.write_byte(snes_to_pc(0x02D8D2), 0x1C) + rom.write_bytes(snes_to_pc(0x02D8E0), [0x23, 0x22, 0x23, 0x23, 0x18, 0x18, 0x18, 0x19]) + rom.write_byte(snes_to_pc(0x02D919), 0x18) + rom.write_byte(snes_to_pc(0x02D927), 0x23) + write_int16(rom, snes_to_pc(0x02D934), 0x2398) + rom.write_byte(snes_to_pc(0x02D943), 0x18) + write_int16(rom, snes_to_pc(0x02D950), 0x0087) + write_int16(rom, snes_to_pc(0x02D95E), 0x0081) + rom.write_byte(snes_to_pc(0x02D9A4), 0x53) + + # disable custom exit on links house exit + rom.write_byte(snes_to_pc(0x02E225), 0x1C) + rom.write_byte(snes_to_pc(0x02DAEE), 0x1C) + rom.write_byte(snes_to_pc(0x02DB8C), 0x6C) if world.is_tile_swapped(0x2f, player): rom.write_bytes(snes_to_pc(0x1BC80D), [0xB2, 0x0B, 0x82]) # add warp under rock rom.write_byte(snes_to_pc(0x1BC590), 0x00) # remove secret portal @@ -2661,6 +2678,21 @@ def patch_shuffled_dark_sanc(world, rom, player): rom.write_bytes(0x180262, [unknown_1, unknown_2, 0x00]) +def patch_shuffled_bomb_shop(world, rom, player): + bomb_shop = world.get_region('Big Bomb Shop', player) + bomb_shop_entrance = str([i for i in bomb_shop.entrances if i.parent_region.name != 'Menu'][0].name) + room_id, ow_area, vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x, unknown_1, unknown_2, door_1, door_2 = door_addresses[bomb_shop_entrance][1] + door_index = door_addresses[str(bomb_shop_entrance)][0] + + rom.write_byte(0x180240, 0x02) + rom.write_byte(0x180247, door_index + 1) + write_int16(rom, 0x180264, room_id) + rom.write_byte(0x180266, ow_area) + write_int16s(rom, 0x180267, [vram_loc, scroll_y, scroll_x, link_y, link_x, camera_y, camera_x]) + rom.write_bytes(0x180275, [unknown_1, unknown_2, 0x00]) + write_int16(rom, snes_to_pc(0x02D996), door_1) + + def update_compasses(rom, world, player): layouts = world.dungeon_layouts[player] provided_dungeon = False