diff --git a/BaseClasses.py b/BaseClasses.py index 81b47e79..289b681b 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -61,6 +61,7 @@ class World(object): self.lock_aga_door_in_escape = False self.save_and_quit_from_boss = True self.accessibility = accessibility.copy() + self.initial_overworld_flags = {} self.fix_skullwoods_exit = {} self.fix_palaceofdarkness_exit = {} self.fix_trock_exit = {} @@ -118,6 +119,7 @@ class World(object): set_player_attr('ganon_at_pyramid', True) set_player_attr('ganonstower_vanilla', True) set_player_attr('sewer_light_cone', self.mode[player] == 'standard') + set_player_attr('initial_overworld_flags', [0] * 0x80) set_player_attr('fix_trock_doors', self.shuffle[player] != 'vanilla' or ((self.mode[player] == 'inverted') != 0x05 in self.owswaps[player][0])) set_player_attr('fix_skullwoods_exit', self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'] or self.doorShuffle[player] not in ['vanilla']) set_player_attr('fix_palaceofdarkness_exit', self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple']) @@ -304,6 +306,12 @@ class World(object): def is_tile_swapped(self, owid, player): return (self.mode[player] == 'inverted') != (owid in self.owswaps[player][0] and self.owMixed[player]) + def is_atgt_swapped(self, player): + return (0x03 in self.owswaps[player][0]) == (0x1b in self.owswaps[player][0]) == (self.mode[player] != 'inverted') + + def is_bombshop_start(self, player): + return self.is_tile_swapped(0x2c, player) and (self.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull'] or not self.shufflelinks[player]) + def check_for_door(self, doorname, player): if isinstance(doorname, Door): return doorname @@ -1439,10 +1447,10 @@ class CollectionState(object): return self.has('Fire Rod', player) or self.has('Lamp', player) def can_flute(self, player): - if any(map(lambda i: i.name == 'Ocarina', self.world.precollected_items)): + if any(map(lambda i: i.name in ['Ocarina', 'Ocarina (Activated)'], self.world.precollected_items)): return True lw = self.world.get_region('Kakariko Area', player) - return self.has('Ocarina', player) and lw.can_reach(self) and self.is_not_bunny(lw, player) + return self.has('Ocarina Activated', player) or (self.has('Ocarina', player) and lw.can_reach(self) and self.is_not_bunny(lw, player)) def can_melt_things(self, player): return self.has('Fire Rod', player) or (self.has('Bombos', player) and self.can_use_medallions(player)) @@ -1867,7 +1875,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 @@ -3046,7 +3054,9 @@ class Spoiler(object): if self.world.custom and p in self.world.customitemarray: self.metadata['triforcegoal'][p], self.metadata['triforcepool'][p] = set_default_triforce(self.metadata['goal'][p], self.world.customitemarray[p]["triforcepiecesgoal"], self.world.customitemarray[p]["triforcepieces"]) else: - self.metadata['triforcegoal'][p], self.metadata['triforcepool'][p] = set_default_triforce(self.metadata['goal'][p], self.world.treasure_hunt_count[p], self.world.treasure_hunt_total[p]) + custom_goal = self.world.treasure_hunt_count[p] if isinstance(self.world.treasure_hunt_count, dict) else self.world.treasure_hunt_count + custom_total = self.world.treasure_hunt_total[p] if isinstance(self.world.treasure_hunt_total, dict) else self.world.treasure_hunt_total + self.metadata['triforcegoal'][p], self.metadata['triforcepool'][p] = set_default_triforce(self.metadata['goal'][p], custom_goal, custom_total) def parse_data(self): self.medallions = OrderedDict() @@ -3254,6 +3264,18 @@ class Spoiler(object): if self.overworlds: outfile.write('\n\nOverworld:\n\n') + + # flute shuffle + for player in range(1, self.world.players + 1): + if ('flute', player) in self.maps: + outfile.write('Flute Spots:\n') + break + for player in range(1, self.world.players + 1): + if ('flute', player) in self.maps: + if self.world.players > 1: + outfile.write(str('(Player ' + str(player) + ')\n')) # player name + outfile.write(self.maps[('flute', player)]['text'] + '\n\n') + # overworld tile swaps for player in range(1, self.world.players + 1): if ('swaps', player) in self.maps: diff --git a/CHANGELOG.md b/CHANGELOG.md index 6bb81edf..0d41ef2a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,47 @@ # Changelog +### 0.2.6.0 +- New text engine font! +- Fixed invisible Witch item bug +- Added 'O' to ROM Header for autotrackers +- Fixed generation error with Shopsanity + OWR Layout +- Fixed OWR validation error with Insanity ER + OWR Layout +- Fixed issue with TR Peg Puzzle not spawning a valid portal + +### 0.2.5.3 +- Changed AT/GT Swap to favor vanilla, only swapping if GT entrance is the only choice in starting world +- Fixed issue with Links House not swapping in OW Mixed +- Added Flute Spots to spoiler log +- Fixed issue with Light Hype Fairy excluded from bombable door list + +### 0.2.5.1 +- Fixed missing rule for Inverted VoO Portal access + +### 0.2.5.0 +- Many updates to Inverted OW Terrain: + - Links House start now spawns in Big Bomb Shop + - Old Man Cave/Bumper Cave returned to vanilla + - Mountain Cave S+Q now spawns in his usual Cave + - Flute Spot 1 is moved to Top of Dark Death Mountain + - Ladder is removed from West Dark Death Mountain + - When finding Flute, it comes pre-activated (will hear SFX on collecting) + - Spiral and Mimic Cave are now bridged together + - TR Peg Puzzle is restored but instead reveals a ladder + - Houlihan now exits same place as Big Bomb Shop does (OWR branch always had this) + - Ice Palace has been re-sealed to vanilla, portal moved to outer edge of moat + - Glitched modes will now use vanilla terrain except where necessary +- Fixed errors with OW Layout Shuffle +- Fixed issue with incorrect Mirror bonking +- Fixed issue with old man follower death to Pyramid +- Fixed Hera boss music not playing when boss not defeated +- ~~Merged DR v0.5.1.7 - TT boss trap door fix/Applied Glitched flag~~ + +### 0.2.4.0 +- Added Guaranteed OWR Reachability +- Fixed incorrect parity calc for Whirlpool Shuffle +- Fixed error with generating seeds with GUI +- CLI fixes for triforce piece arguments + ### 0.2.3.6 - Added Trinity goal (8/10 default TF pieces) - Many improvements to TFH pool allocation diff --git a/DoorShuffle.py b/DoorShuffle.py index fcd312cd..45ecbc7b 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_bombshop_start(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() @@ -1836,7 +1833,7 @@ def find_inaccessible_regions(world, player): if connect and connect not in queue and connect not in visited_regions: if connect.type is not RegionType.Dungeon or connect.name.endswith(' Portal'): queue.append(connect) - world.inaccessible_regions[player].extend([r.name for r in all_regions if r not in visited_regions and valid_inaccessible_region(r)]) + world.inaccessible_regions[player].extend([r.name for r in all_regions if r not in visited_regions and valid_inaccessible_region(world, r, player)]) if world.is_tile_swapped(0x1b, player): ledge = world.get_region('Hyrule Castle Ledge', player) if any(x for x in ledge.exits if x.connected_region and x.connected_region.name == 'Agahnims Tower Portal'): @@ -1853,10 +1850,8 @@ def find_accessible_entrances(world, player, builder): if world.mode[player] == 'standard' and builder.name == 'Hyrule Castle': start_regions = ['Hyrule Castle Courtyard'] - elif 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_bombshop_start(player) else 'Big Bomb Shop', 'Sanctuary' if world.mode[player] != 'inverted' else 'Dark Sanctuary Hint'] if world.is_tile_swapped(0x1b, player): start_regions.append('Hyrule Castle Ledge') regs = convert_regions(start_regions, world, player) @@ -1890,8 +1885,8 @@ def find_accessible_entrances(world, player, builder): return visited_entrances -def valid_inaccessible_region(r): - return r.type is not RegionType.Cave or (len(r.exits) > 0 and r.name not in ['Links House', 'Chris Houlihan Room']) +def valid_inaccessible_region(world, r, player): + return r.type is not RegionType.Cave or (len(r.exits) > 0 and r.name not in ['Links House' if not world.is_bombshop_start(player) else 'Big Bomb Shop', 'Chris Houlihan Room']) def add_inaccessible_doors(world, player): diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 5e6c772f..46217d2e 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -47,11 +47,6 @@ def link_entrances(world, player): dropexit_connections.append(tuple(('Inverted Pyramid Entrance', 'Pyramid Exit'))) connect_simple(world, 'Other World S&Q', 'Hyrule Castle Ledge', player) - if not world.is_tile_swapped(0x03, player): - connect_simple(world, 'Old Man S&Q', 'Old Man House', player) - else: - connect_simple(world, 'Old Man S&Q', 'West Dark Death Mountain (Bottom)', player) - unbias_some_entrances(Dungeon_Exits, Cave_Exits, Old_Man_House, Cave_Three_Exits) Cave_Exits.extend(Cave_Exits_Directional) @@ -59,47 +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_bombshop_start(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_bombshop_start(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')) - - # inverted entrance mods - ignore_pool = True - for owid in swapped_connections.keys(): - if world.is_tile_swapped(owid, player): - for (entrancename, exitname) in swapped_connections[owid]: - try: - connect_two_way(world, entrancename, exitname, player) - except RuntimeError: - connect_entrance(world, entrancename, exitname, player) - - if world.is_tile_swapped(0x03, player) and not world.is_tile_swapped(0x0a, player): - connect_entrance(world, 'Death Mountain Return Cave (West)', 'Dark Death Mountain Healer Fairy', player) - elif world.is_tile_swapped(0x0a, player) and not world.is_tile_swapped(0x03, player): - connect_two_way(world, 'Bumper Cave (Top)', 'Death Mountain Return Cave Exit (West)', player) ignore_pool = False # dungeon entrance shuffle @@ -109,11 +90,11 @@ def link_entrances(world, player): for entrancename, exitname in default_skulldrop_connections: connect_logical(world, entrancename, exitname, player, False) - if not invFlag: - for entrancename, exitname in open_default_dungeon_connections: + if world.is_atgt_swapped(player): + for entrancename, exitname in inverted_default_dungeon_connections: connect_logical(world, entrancename, exitname, player, True) else: - for entrancename, exitname in inverted_default_dungeon_connections: + for entrancename, exitname in open_default_dungeon_connections: connect_logical(world, entrancename, exitname, player, True) elif world.shuffle[player] == 'dungeonssimple': suppress_spoiler = False @@ -129,7 +110,8 @@ def link_entrances(world, player): scramble_holes(world, player) # list modification - lw_wdm_entrances = ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', + lw_wdm_entrances = ['Old Man Cave (West)', 'Death Mountain Return Cave (West)', + 'Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)'] lw_edm_entrances = ['Paradox Cave (Bottom)', 'Paradox Cave (Middle)', 'Paradox Cave (Top)', 'Spiral Cave', @@ -139,13 +121,9 @@ def link_entrances(world, player): caves = list(Cave_Exits) three_exit_caves = list(Cave_Three_Exits) - Two_Door_Caves_Directional = list() + Two_Door_Caves_Directional = [('Bumper Cave (Bottom)', 'Bumper Cave (Top)')] Two_Door_Caves = [('Elder House (East)', 'Elder House (West)'), ('Superbunny Cave (Bottom)', 'Superbunny Cave (Top)')] - if not world.is_tile_swapped(0x0a, player): - Two_Door_Caves_Directional.append(tuple({'Bumper Cave (Bottom)', 'Bumper Cave (Top)'})) - else: - Two_Door_Caves_Directional.append(tuple({'Old Man Cave (West)', 'Death Mountain Return Cave (West)'})) if not world.is_tile_swapped(0x05, player): Two_Door_Caves_Directional.append(tuple({'Hookshot Cave', 'Hookshot Cave Back Entrance'})) else: @@ -179,43 +157,25 @@ def link_entrances(world, player): caves.extend(list(Old_Man_House)) caves.extend(list(three_exit_caves)) - if (not world.is_tile_swapped(0x18, player)) or (not world.is_tile_swapped(0x03, player)): # ability to activate flute in LW - candidates = [e for e in lw_wdm_entrances if e != 'Old Man House (Bottom)'] + candidates = [e for e in lw_wdm_entrances if e != 'Old Man House (Bottom)'] + random.shuffle(candidates) + old_man_exit = candidates.pop() + lw_wdm_entrances.remove(old_man_exit) + connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) + + if 0x03 in world.owswaps[player][0] == 0x05 in world.owswaps[player][0]: # if WDM and EDM are in same world + candidates = lw_wdm_entrances + lw_edm_entrances random.shuffle(candidates) - old_man_exit = candidates.pop() - lw_wdm_entrances.remove(old_man_exit) - connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) - - if not world.is_tile_swapped(0x0a, player): - lw_wdm_entrances.extend(['Old Man Cave (West)', 'Death Mountain Return Cave (West)']) - else: - lw_wdm_entrances.extend(['Bumper Cave (Bottom)', 'Bumper Cave (Top)']) - - if 0x03 in world.owswaps[player][0] == 0x05 in world.owswaps[player][0]: # if WDM and EDM are in same world - candidates = lw_wdm_entrances + lw_edm_entrances - random.shuffle(candidates) - old_man_entrance = candidates.pop() + old_man_entrance = candidates.pop() + if old_man_entrance in lw_wdm_entrances: lw_wdm_entrances.remove(old_man_entrance) - if old_man_entrance in lw_wdm_entrances: - lw_wdm_entrances.remove(old_man_entrance) - elif old_man_entrance in lw_edm_entrances: - lw_edm_entrances.remove(old_man_entrance) - else: - random.shuffle(lw_wdm_entrances) - old_man_entrance = lw_wdm_entrances.pop() - connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) + elif old_man_entrance in lw_edm_entrances: + lw_edm_entrances.remove(old_man_entrance) else: - # force connection to DM - random.shuffle(ddm_entrances) - old_man_exit = ddm_entrances.pop() - connect_two_way(world, old_man_exit, 'Old Man Cave Exit (East)', player) - - # place old man, bumper cave bottom to DDM entrances not in east bottom - if not world.is_tile_swapped(0x0a, player): - connect_two_way(world, 'Old Man Cave (West)', 'Old Man Cave Exit (West)', player) - else: - connect_two_way(world, 'Bumper Cave (Bottom)', 'Old Man Cave Exit (West)', player) - + random.shuffle(lw_wdm_entrances) + old_man_entrance = lw_wdm_entrances.pop() + connect_two_way(world, old_man_entrance, 'Old Man Cave Exit (West)', player) + # connect remaining LW DM entrances if 0x03 in world.owswaps[player][0] == 0x05 in world.owswaps[player][0]: # if WDM and EDM are in same world connect_caves(world, lw_wdm_entrances + lw_edm_entrances, [], caves, player) @@ -255,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_bombshop_start(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) @@ -300,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_bombshop_start(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] @@ -326,7 +288,7 @@ def link_entrances(world, player): caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player) else: caves.append('Ganons Tower Exit') @@ -357,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_bombshop_start(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] @@ -398,7 +361,7 @@ def link_entrances(world, player): lw_dungeons.append(tuple(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Hyrule Castle Exit (South)'))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player) else: dw_dungeons.append('Ganons Tower Exit') @@ -459,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_bombshop_start(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) @@ -485,7 +449,7 @@ def link_entrances(world, player): Dungeon_Exits.append(tuple(('Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)', 'Hyrule Castle Exit (South)'))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player) else: Dungeon_Exits.append('Ganons Tower Exit') @@ -513,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_bombshop_start(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) @@ -534,7 +499,7 @@ def link_entrances(world, player): caves.append(tuple(random.sample(['Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)'], 3))) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player) else: caves.append('Ganons Tower Exit') @@ -562,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_bombshop_start(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] @@ -601,7 +567,7 @@ def link_entrances(world, player): caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player) connect_two_way(world, 'Pyramid Entrance' if not world.is_tile_swapped(0x1b, player) else 'Inverted Pyramid Entrance', 'Pyramid Exit', player) connect_entrance(world, 'Pyramid Hole' if not world.is_tile_swapped(0x1b, player) else 'Inverted Pyramid Hole', 'Pyramid', player) else: @@ -645,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_bombshop_start(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) @@ -675,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_bombshop_start(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_bombshop_start(player) else 'Big Bomb Shop'): links_house = links_house.name break connect_exit(world, 'Chris Houlihan Room Exit', links_house, player) @@ -695,7 +662,7 @@ def link_entrances(world, player): world.ganon_at_pyramid[player] = False # check for Ganon's Tower location - if world.get_entrance('Ganons Tower' if not invFlag else 'Agahnims Tower', player).connected_region.name != 'Ganons Tower Portal' if not invFlag else 'GT Lobby': + if world.get_entrance('Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', player).connected_region.name != 'Ganons Tower Portal' if not invFlag else 'GT Lobby': world.ganonstower_vanilla[player] = False @@ -1268,7 +1235,7 @@ def full_shuffle_dungeons(world, Dungeon_Exits, player): connect_two_way(world, 'Hyrule Castle Entrance (South)', 'Hyrule Castle Exit (South)', player) if not world.shuffle_ganon: - connect_two_way(world, 'Ganons Tower' if not invFlag else 'Agahnims Tower', 'Ganons Tower Exit', player) + connect_two_way(world, 'Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', 'Ganons Tower Exit', player) else: dungeon_exits.append('Ganons Tower Exit') @@ -1375,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_bombshop_start(player) else 'Big Bomb Shop' else: if invFlag: for dark_sanc in world.get_entrance('Dark Sanctuary Hint Exit', player).connected_region.exits: @@ -1394,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_bombshop_start(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 @@ -2077,7 +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'), +mandatory_connections = [('Old Man S&Q', 'Old Man House'), # UW Connections ('Lost Woods Hideout (top to bottom)', 'Lost Woods Hideout (bottom)'), @@ -2108,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)'), @@ -2176,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'), @@ -2200,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') ] @@ -2235,28 +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 ] -swapped_connections = { - 0x03: [ - ('Old Man Cave (East)', 'Death Mountain Return Cave Exit (West)'), - #('Death Mountain Return Cave (East)', 'Death Mountain Return Cave Exit (East)'), - ('Dark Death Mountain Fairy', 'Old Man Cave Exit (East)') - ], - 0x0a: [ - ('Old Man Cave (West)', 'Bumper Cave Exit (Bottom)'), - ('Death Mountain Return Cave (West)', 'Bumper Cave Exit (Top)'), - ('Bumper Cave (Bottom)', 'Old Man Cave Exit (West)'), - ('Bumper Cave (Top)', 'Dark Death Mountain Healer Fairy') - ] -} - -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)'), @@ -2349,177 +2296,175 @@ one_way_ledges = { indirect_connections = { 'Turtle Rock Ledge': 'Turtle Rock', - 'Big Bomb Shop': 'Pyramid Fairy', + #'Big Bomb Shop': 'Pyramid Fairy', #'East Dark World': 'Pyramid Fairy', 'Pyramid Area': 'Pyramid Fairy', # HC Ledge/Courtyard #'Dark Desert': 'Pyramid Fairy', - 'Misery Mire Area': 'Pyramid Fairy', # Desert/Checkerboard Ledge + #'Misery Mire Area': 'Pyramid Fairy', # Desert/Checkerboard Ledge #'West Dark World': 'Pyramid Fairy', - 'Dark Chapel Area': 'Pyramid Fairy', # Bonk Rocks - 'Dark Graveyard North': 'Pyramid Fairy', # Graveyard Ledge/Kings Tomb + #'Dark Chapel Area': 'Pyramid Fairy', # Bonk Rocks + #'Dark Graveyard North': 'Pyramid Fairy', # Graveyard Ledge/Kings Tomb #'South Dark World': 'Pyramid Fairy', - 'Dig Game Ledge': 'Pyramid Fairy', # Brother House Left - 'Stumpy Approach Area': 'Pyramid Fairy', # Cave 45 + #'Dig Game Ledge': 'Pyramid Fairy', # Brother House Left + #'Stumpy Approach Area': 'Pyramid Fairy', # Cave 45 # Inverted Cases #'Light World': 'Pyramid Fairy', - 'Lost Woods West Area': 'Pyramid Fairy', # Skull Woods Back - 'East Death Mountain (Top East)': 'Pyramid Fairy', # Floating Island - 'Blacksmith Area': 'Pyramid Fairy', # Hammerpegs - 'Forgotten Forest Area': 'Pyramid Fairy', # Shield Shop - 'Desert Area': 'Pyramid Fairy', # Mire Area + #'Lost Woods West Area': 'Pyramid Fairy', # Skull Woods Back + #'East Death Mountain (Top East)': 'Pyramid Fairy', # Floating Island + #'Blacksmith Area': 'Pyramid Fairy', # Hammerpegs + #'Forgotten Forest Area': 'Pyramid Fairy', # Shield Shop + #'Desert Area': 'Pyramid Fairy', # Mire Area 'Old Man Cave': 'Old Man S&Q' } # format: # Key=Name -# addr = (door_index, exitdata) # multiexit +# addr = (door_index, exitdata, ow_flag) # multiexit # | ([addr], None) # holes # exitdata = (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) # ToDo somehow merge this with creation of the locations - -# ToDo somehow merge this with creation of the locations -door_addresses = {'Links House': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0x0ae8, 0x08b8, 0x0b07, 0x08bf, 0x06, 0xfe, 0x0816, 0x0000)), - 'Desert Palace Entrance (South)': (0x08, (0x0084, 0x30, 0x0314, 0x0c56, 0x00a6, 0x0ca8, 0x0128, 0x0cc3, 0x0133, 0x0a, 0xfa, 0x0000, 0x0000)), - 'Desert Palace Entrance (West)': (0x0A, (0x0083, 0x30, 0x0280, 0x0c46, 0x0003, 0x0c98, 0x0088, 0x0cb3, 0x0090, 0x0a, 0xfd, 0x0000, 0x0000)), - 'Desert Palace Entrance (North)': (0x0B, (0x0063, 0x30, 0x0016, 0x0c00, 0x00a2, 0x0c28, 0x0128, 0x0c6d, 0x012f, 0x00, 0x0e, 0x0000, 0x0000)), - 'Desert Palace Entrance (East)': (0x09, (0x0085, 0x30, 0x02a8, 0x0c4a, 0x0142, 0x0c98, 0x01c8, 0x0cb7, 0x01cf, 0x06, 0xfe, 0x0000, 0x0000)), - 'Eastern Palace': (0x07, (0x00c9, 0x1e, 0x005a, 0x0600, 0x0ed6, 0x0618, 0x0f50, 0x066d, 0x0f5b, 0x00, 0xfa, 0x0000, 0x0000)), - 'Tower of Hera': (0x32, (0x0077, 0x03, 0x0050, 0x0014, 0x087c, 0x0068, 0x08f0, 0x0083, 0x08fb, 0x0a, 0xf4, 0x0000, 0x0000)), - 'Hyrule Castle Entrance (South)': (0x03, (0x0061, 0x1b, 0x0530, 0x0692, 0x0784, 0x06cc, 0x07f8, 0x06ff, 0x0803, 0x0e, 0xfa, 0x0000, 0x87be)), - 'Hyrule Castle Entrance (West)': (0x02, (0x0060, 0x1b, 0x0016, 0x0600, 0x06ae, 0x0604, 0x0728, 0x066d, 0x0733, 0x00, 0x02, 0x0000, 0x8124)), - 'Hyrule Castle Entrance (East)': (0x04, (0x0062, 0x1b, 0x004a, 0x0600, 0x0856, 0x0604, 0x08c8, 0x066d, 0x08d3, 0x00, 0xfa, 0x0000, 0x8158)), - 'Inverted Pyramid Entrance': (0x35, (0x0010, 0x1b, 0x000e, 0x0600, 0x0676, 0x0604, 0x06e8, 0x066d, 0x06f3, 0x00, 0x0a, 0x0000, 0x811c)), - 'Agahnims Tower': (0x23, (0x00e0, 0x1b, 0x0032, 0x0600, 0x0784, 0x0634, 0x07f8, 0x066d, 0x0803, 0x00, 0x0a, 0x0000, 0x82be)), - 'Thieves Town': (0x33, (0x00db, 0x58, 0x0b2e, 0x075a, 0x0176, 0x07a8, 0x01f8, 0x07c7, 0x0203, 0x06, 0xfa, 0x0000, 0x0000)), - 'Skull Woods First Section Door': (0x29, (0x0058, 0x40, 0x0f4c, 0x01f6, 0x0262, 0x0248, 0x02e8, 0x0263, 0x02ef, 0x0a, 0xfe, 0x0000, 0x0000)), - 'Skull Woods Second Section Door (East)': (0x28, (0x0057, 0x40, 0x0eb8, 0x01e6, 0x01c2, 0x0238, 0x0248, 0x0253, 0x024f, 0x0a, 0xfe, 0x0000, 0x0000)), - 'Skull Woods Second Section Door (West)': (0x27, (0x0056, 0x40, 0x0c8e, 0x01a6, 0x0062, 0x01f8, 0x00e8, 0x0213, 0x00ef, 0x0a, 0x0e, 0x0000, 0x0000)), - 'Skull Woods Final Section': (0x2A, (0x0059, 0x40, 0x0282, 0x0066, 0x0016, 0x00b8, 0x0098, 0x00d3, 0x00a3, 0x0a, 0xfa, 0x0000, 0x0000)), - 'Ice Palace': (0x2C, (0x000e, 0x75, 0x0bc6, 0x0d6a, 0x0c3e, 0x0db8, 0x0cb8, 0x0dd7, 0x0cc3, 0x06, 0xf2, 0x0000, 0x0000)), - 'Misery Mire': (0x26, (0x0098, 0x70, 0x0414, 0x0c79, 0x00a6, 0x0cc7, 0x0128, 0x0ce6, 0x0133, 0x07, 0xfa, 0x0000, 0x0000)), - 'Palace of Darkness': (0x25, (0x004a, 0x5e, 0x005a, 0x0600, 0x0ed6, 0x0628, 0x0f50, 0x066d, 0x0f5b, 0x00, 0xfa, 0x0000, 0x0000)), - 'Swamp Palace': (0x24, (0x0028, 0x7b, 0x049e, 0x0e8c, 0x06f2, 0x0ed8, 0x0778, 0x0ef9, 0x077f, 0x04, 0xfe, 0x0000, 0x0000)), - 'Turtle Rock': (0x34, (0x00d6, 0x47, 0x0712, 0x00da, 0x0e96, 0x0128, 0x0f08, 0x0147, 0x0f13, 0x06, 0xfa, 0x0000, 0x0000)), - 'Dark Death Mountain Ledge (West)': (0x14, (0x0023, 0x45, 0x07ca, 0x0103, 0x0c46, 0x0157, 0x0cb8, 0x0172, 0x0cc3, 0x0b, 0x0a, 0x0000, 0x0000)), - 'Dark Death Mountain Ledge (East)': (0x18, (0x0024, 0x45, 0x07e0, 0x0103, 0x0d00, 0x0157, 0x0d78, 0x0172, 0x0d7d, 0x0b, 0x00, 0x0000, 0x0000)), - 'Turtle Rock Isolated Ledge Entrance': (0x17, (0x00d5, 0x45, 0x0ad4, 0x0164, 0x0ca6, 0x01b8, 0x0d18, 0x01d3, 0x0d23, 0x0a, 0xfa, 0x0000, 0x0000)), - 'Hyrule Castle Secret Entrance Stairs': (0x31, (0x0055, 0x1b, 0x044a, 0x067a, 0x0854, 0x06c8, 0x08c8, 0x06e7, 0x08d3, 0x06, 0xfa, 0x0000, 0x0000)), - 'Kakariko Well Cave': (0x38, (0x002f, 0x18, 0x0386, 0x0665, 0x0032, 0x06b7, 0x00b8, 0x06d2, 0x00bf, 0x0b, 0xfe, 0x0000, 0x0000)), - 'Bat Cave Cave': (0x10, (0x00e3, 0x22, 0x0412, 0x087a, 0x048e, 0x08c8, 0x0508, 0x08e7, 0x0513, 0x06, 0x02, 0x0000, 0x0000)), - 'Elder House (East)': (0x0D, (0x00f3, 0x18, 0x02c4, 0x064a, 0x0222, 0x0698, 0x02a8, 0x06b7, 0x02af, 0x06, 0xfe, 0x05d4, 0x0000)), - 'Elder House (West)': (0x0C, (0x00f2, 0x18, 0x02bc, 0x064c, 0x01e2, 0x0698, 0x0268, 0x06b9, 0x026f, 0x04, 0xfe, 0x05cc, 0x0000)), - 'North Fairy Cave': (0x37, (0x0008, 0x15, 0x0088, 0x0400, 0x0a36, 0x0448, 0x0aa8, 0x046f, 0x0ab3, 0x00, 0x0a, 0x0000, 0x0000)), - 'Lost Woods Hideout Stump': (0x2B, (0x00e1, 0x00, 0x0f4e, 0x01f6, 0x0262, 0x0248, 0x02e8, 0x0263, 0x02ef, 0x0a, 0x0e, 0x0000, 0x0000)), - 'Lumberjack Tree Cave': (0x11, (0x00e2, 0x02, 0x0118, 0x0015, 0x04c6, 0x0067, 0x0548, 0x0082, 0x0553, 0x0b, 0xfa, 0x0000, 0x0000)), - 'Two Brothers House (East)': (0x0F, (0x00f5, 0x29, 0x0880, 0x0b07, 0x0200, 0x0b58, 0x0238, 0x0b74, 0x028d, 0x09, 0x00, 0x0b86, 0x0000)), - 'Two Brothers House (West)': (0x0E, (0x00f4, 0x28, 0x08a0, 0x0b06, 0x0100, 0x0b58, 0x01b8, 0x0b73, 0x018d, 0x0a, 0x00, 0x0bb6, 0x0000)), - 'Sanctuary': (0x01, (0x0012, 0x13, 0x001c, 0x0400, 0x06de, 0x0414, 0x0758, 0x046d, 0x0763, 0x00, 0x02, 0x0000, 0x01aa)), - 'Old Man Cave (West)': (0x05, (0x00f0, 0x0a, 0x03a0, 0x0264, 0x0500, 0x02b8, 0x05a8, 0x02d3, 0x058d, 0x0a, 0x00, 0x0000, 0x0000)), - 'Old Man Cave (East)': (0x06, (0x00f1, 0x03, 0x1402, 0x0294, 0x0604, 0x02e8, 0x0678, 0x0303, 0x0683, 0x0a, 0xfc, 0x0000, 0x0000)), - 'Old Man House (Bottom)': (0x2F, (0x00e4, 0x03, 0x181a, 0x031e, 0x06b4, 0x03a7, 0x0728, 0x038d, 0x0733, 0x00, 0x0c, 0x0000, 0x0000)), - 'Old Man House (Top)': (0x30, (0x00e5, 0x03, 0x10c6, 0x0224, 0x0814, 0x0278, 0x0888, 0x0293, 0x0893, 0x0a, 0x0c, 0x0000, 0x0000)), - 'Death Mountain Return Cave (East)': (0x2E, (0x00e7, 0x03, 0x0d82, 0x01c4, 0x0600, 0x0218, 0x0648, 0x0233, 0x067f, 0x0a, 0x00, 0x0000, 0x0000)), - 'Death Mountain Return Cave (West)': (0x2D, (0x00e6, 0x0a, 0x00a0, 0x0205, 0x0500, 0x0257, 0x05b8, 0x0272, 0x058d, 0x0b, 0x00, 0x0000, 0x0000)), - 'Spectacle Rock Cave Peak': (0x22, (0x00ea, 0x03, 0x092c, 0x0133, 0x0754, 0x0187, 0x07c8, 0x01a2, 0x07d3, 0x0b, 0xfc, 0x0000, 0x0000)), - 'Spectacle Rock Cave': (0x21, (0x00fa, 0x03, 0x0eac, 0x01e3, 0x0754, 0x0237, 0x07c8, 0x0252, 0x07d3, 0x0b, 0xfc, 0x0000, 0x0000)), - 'Spectacle Rock Cave (Bottom)': (0x20, (0x00f9, 0x03, 0x0d9c, 0x01c3, 0x06d4, 0x0217, 0x0748, 0x0232, 0x0753, 0x0b, 0xfc, 0x0000, 0x0000)), - 'Paradox Cave (Bottom)': (0x1D, (0x00ff, 0x05, 0x0ee0, 0x01e3, 0x0d00, 0x0237, 0x0da8, 0x0252, 0x0d7d, 0x0b, 0x00, 0x0000, 0x0000)), - 'Paradox Cave (Middle)': (0x1E, (0x00ef, 0x05, 0x17e0, 0x0304, 0x0d00, 0x0358, 0x0dc8, 0x0373, 0x0d7d, 0x0a, 0x00, 0x0000, 0x0000)), - 'Paradox Cave (Top)': (0x1F, (0x00df, 0x05, 0x0460, 0x0093, 0x0d00, 0x00e7, 0x0db8, 0x0102, 0x0d7d, 0x0b, 0x00, 0x0000, 0x0000)), - 'Fairy Ascension Cave (Bottom)': (0x19, (0x00fd, 0x05, 0x0dd4, 0x01c4, 0x0ca6, 0x0218, 0x0d18, 0x0233, 0x0d23, 0x0a, 0xfa, 0x0000, 0x0000)), - 'Fairy Ascension Cave (Top)': (0x1A, (0x00ed, 0x05, 0x0ad4, 0x0163, 0x0ca6, 0x01b7, 0x0d18, 0x01d2, 0x0d23, 0x0b, 0xfa, 0x0000, 0x0000)), - 'Spiral Cave': (0x1C, (0x00ee, 0x05, 0x07c8, 0x0108, 0x0c46, 0x0158, 0x0cb8, 0x0177, 0x0cc3, 0x06, 0xfa, 0x0000, 0x0000)), - 'Spiral Cave (Bottom)': (0x1B, (0x00fe, 0x05, 0x0cca, 0x01a3, 0x0c56, 0x01f7, 0x0cc8, 0x0212, 0x0cd3, 0x0b, 0xfa, 0x0000, 0x0000)), - 'Bumper Cave (Bottom)': (0x15, (0x00fb, 0x4a, 0x03a0, 0x0263, 0x0500, 0x02b7, 0x05a8, 0x02d2, 0x058d, 0x0b, 0x00, 0x0000, 0x0000)), - 'Bumper Cave (Top)': (0x16, (0x00eb, 0x4a, 0x00a0, 0x020a, 0x0500, 0x0258, 0x05b8, 0x0277, 0x058d, 0x06, 0x00, 0x0000, 0x0000)), - 'Superbunny Cave (Top)': (0x13, (0x00e8, 0x45, 0x0460, 0x0093, 0x0d00, 0x00e7, 0x0db8, 0x0102, 0x0d7d, 0x0b, 0x00, 0x0000, 0x0000)), - 'Superbunny Cave (Bottom)': (0x12, (0x00f8, 0x45, 0x0ee0, 0x01e4, 0x0d00, 0x0238, 0x0d78, 0x0253, 0x0d7d, 0x0a, 0x00, 0x0000, 0x0000)), - 'Hookshot Cave': (0x39, (0x003c, 0x45, 0x04da, 0x00a3, 0x0cd6, 0x0107, 0x0d48, 0x0112, 0x0d53, 0x0b, 0xfa, 0x0000, 0x0000)), - 'Hookshot Cave Back Entrance': (0x3A, (0x002c, 0x45, 0x004c, 0x0000, 0x0c56, 0x0038, 0x0cc8, 0x006f, 0x0cd3, 0x00, 0x0a, 0x0000, 0x0000)), - 'Ganons Tower': (0x36, (0x000c, 0x43, 0x0052, 0x0000, 0x0884, 0x0028, 0x08f8, 0x006f, 0x0903, 0x00, 0xfc, 0x0000, 0x0000)), - 'Pyramid Entrance': (0x35, (0x0010, 0x5b, 0x0b0e, 0x075a, 0x0674, 0x07a8, 0x06e8, 0x07c7, 0x06f3, 0x06, 0xfa, 0x0000, 0x0000)), +door_addresses = {'Links House': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0x0ae8, 0x08b8, 0x0b07, 0x08bf, 0x06, 0xfe, 0x0816, 0x0000), 0x00), + 'Desert Palace Entrance (South)': (0x08, (0x0084, 0x30, 0x0314, 0x0c56, 0x00a6, 0x0ca8, 0x0128, 0x0cc3, 0x0133, 0x0a, 0xfa, 0x0000, 0x0000), 0x00), + 'Desert Palace Entrance (West)': (0x0A, (0x0083, 0x30, 0x0280, 0x0c46, 0x0003, 0x0c98, 0x0088, 0x0cb3, 0x0090, 0x0a, 0xfd, 0x0000, 0x0000), 0x00), + 'Desert Palace Entrance (North)': (0x0B, (0x0063, 0x30, 0x0016, 0x0c00, 0x00a2, 0x0c28, 0x0128, 0x0c6d, 0x012f, 0x00, 0x0e, 0x0000, 0x0000), 0x00), + 'Desert Palace Entrance (East)': (0x09, (0x0085, 0x30, 0x02a8, 0x0c4a, 0x0142, 0x0c98, 0x01c8, 0x0cb7, 0x01cf, 0x06, 0xfe, 0x0000, 0x0000), 0x00), + 'Eastern Palace': (0x07, (0x00c9, 0x1e, 0x005a, 0x0600, 0x0ed6, 0x0618, 0x0f50, 0x066d, 0x0f5b, 0x00, 0xfa, 0x0000, 0x0000), 0x00), + 'Tower of Hera': (0x32, (0x0077, 0x03, 0x0050, 0x0014, 0x087c, 0x0068, 0x08f0, 0x0083, 0x08fb, 0x0a, 0xf4, 0x0000, 0x0000), 0x00), + 'Hyrule Castle Entrance (South)': (0x03, (0x0061, 0x1b, 0x0530, 0x0692, 0x0784, 0x06cc, 0x07f8, 0x06ff, 0x0803, 0x0e, 0xfa, 0x0000, 0x87be), 0x00), + 'Hyrule Castle Entrance (West)': (0x02, (0x0060, 0x1b, 0x0016, 0x0600, 0x06ae, 0x0604, 0x0728, 0x066d, 0x0733, 0x00, 0x02, 0x0000, 0x8124), 0x00), + 'Hyrule Castle Entrance (East)': (0x04, (0x0062, 0x1b, 0x004a, 0x0600, 0x0856, 0x0604, 0x08c8, 0x066d, 0x08d3, 0x00, 0xfa, 0x0000, 0x8158), 0x00), + 'Inverted Pyramid Entrance': (0x35, (0x0010, 0x1b, 0x000e, 0x0600, 0x0676, 0x0604, 0x06e8, 0x066d, 0x06f3, 0x00, 0x0a, 0x0000, 0x811c), 0x00), + 'Agahnims Tower': (0x23, (0x00e0, 0x1b, 0x0032, 0x0600, 0x0784, 0x0634, 0x07f8, 0x066d, 0x0803, 0x00, 0x0a, 0x0000, 0x82be), 0x40), + 'Thieves Town': (0x33, (0x00db, 0x58, 0x0b2e, 0x075a, 0x0176, 0x07a8, 0x01f8, 0x07c7, 0x0203, 0x06, 0xfa, 0x0000, 0x0000), 0x20), + 'Skull Woods First Section Door': (0x29, (0x0058, 0x40, 0x0f4c, 0x01f6, 0x0262, 0x0248, 0x02e8, 0x0263, 0x02ef, 0x0a, 0xfe, 0x0000, 0x0000), 0x00), + 'Skull Woods Second Section Door (East)': (0x28, (0x0057, 0x40, 0x0eb8, 0x01e6, 0x01c2, 0x0238, 0x0248, 0x0253, 0x024f, 0x0a, 0xfe, 0x0000, 0x0000), 0x00), + 'Skull Woods Second Section Door (West)': (0x27, (0x0056, 0x40, 0x0c8e, 0x01a6, 0x0062, 0x01f8, 0x00e8, 0x0213, 0x00ef, 0x0a, 0x0e, 0x0000, 0x0000), 0x00), + 'Skull Woods Final Section': (0x2A, (0x0059, 0x40, 0x0282, 0x0066, 0x0016, 0x00b8, 0x0098, 0x00d3, 0x00a3, 0x0a, 0xfa, 0x0000, 0x0000), 0x20), + 'Ice Palace': (0x2C, (0x000e, 0x75, 0x0bc6, 0x0d6a, 0x0c3e, 0x0db8, 0x0cb8, 0x0dd7, 0x0cc3, 0x06, 0xf2, 0x0000, 0x0000), 0x00), + 'Misery Mire': (0x26, (0x0098, 0x70, 0x0414, 0x0c79, 0x00a6, 0x0cc7, 0x0128, 0x0ce6, 0x0133, 0x07, 0xfa, 0x0000, 0x0000), 0x20), + 'Palace of Darkness': (0x25, (0x004a, 0x5e, 0x005a, 0x0600, 0x0ed6, 0x0628, 0x0f50, 0x066d, 0x0f5b, 0x00, 0xfa, 0x0000, 0x0000), 0x20), + 'Swamp Palace': (0x24, (0x0028, 0x7b, 0x049e, 0x0e8c, 0x06f2, 0x0ed8, 0x0778, 0x0ef9, 0x077f, 0x04, 0xfe, 0x0000, 0x0000), 0x00), + 'Turtle Rock': (0x34, (0x00d6, 0x47, 0x0712, 0x00da, 0x0e96, 0x0128, 0x0f08, 0x0147, 0x0f13, 0x06, 0xfa, 0x0000, 0x0000), 0x20), + 'Dark Death Mountain Ledge (West)': (0x14, (0x0023, 0x45, 0x07ca, 0x0103, 0x0c46, 0x0157, 0x0cb8, 0x0172, 0x0cc3, 0x0b, 0x0a, 0x0000, 0x0000), 0x00), + 'Dark Death Mountain Ledge (East)': (0x18, (0x0024, 0x45, 0x07e0, 0x0103, 0x0d00, 0x0157, 0x0d78, 0x0172, 0x0d7d, 0x0b, 0x00, 0x0000, 0x0000), 0x00), + 'Turtle Rock Isolated Ledge Entrance': (0x17, (0x00d5, 0x45, 0x0ad4, 0x0164, 0x0ca6, 0x01b8, 0x0d18, 0x01d3, 0x0d23, 0x0a, 0xfa, 0x0000, 0x0000), 0x00), + 'Hyrule Castle Secret Entrance Stairs': (0x31, (0x0055, 0x1b, 0x044a, 0x067a, 0x0854, 0x06c8, 0x08c8, 0x06e7, 0x08d3, 0x06, 0xfa, 0x0000, 0x0000), 0x00), + 'Kakariko Well Cave': (0x38, (0x002f, 0x18, 0x0386, 0x0665, 0x0032, 0x06b7, 0x00b8, 0x06d2, 0x00bf, 0x0b, 0xfe, 0x0000, 0x0000), 0x00), + 'Bat Cave Cave': (0x10, (0x00e3, 0x22, 0x0412, 0x087a, 0x048e, 0x08c8, 0x0508, 0x08e7, 0x0513, 0x06, 0x02, 0x0000, 0x0000), 0x00), + 'Elder House (East)': (0x0D, (0x00f3, 0x18, 0x02c4, 0x064a, 0x0222, 0x0698, 0x02a8, 0x06b7, 0x02af, 0x06, 0xfe, 0x05d4, 0x0000), 0x00), + 'Elder House (West)': (0x0C, (0x00f2, 0x18, 0x02bc, 0x064c, 0x01e2, 0x0698, 0x0268, 0x06b9, 0x026f, 0x04, 0xfe, 0x05cc, 0x0000), 0x00), + 'North Fairy Cave': (0x37, (0x0008, 0x15, 0x0088, 0x0400, 0x0a36, 0x0448, 0x0aa8, 0x046f, 0x0ab3, 0x00, 0x0a, 0x0000, 0x0000), 0x00), + 'Lost Woods Hideout Stump': (0x2B, (0x00e1, 0x00, 0x0f4e, 0x01f6, 0x0262, 0x0248, 0x02e8, 0x0263, 0x02ef, 0x0a, 0x0e, 0x0000, 0x0000), 0x00), + 'Lumberjack Tree Cave': (0x11, (0x00e2, 0x02, 0x0118, 0x0015, 0x04c6, 0x0067, 0x0548, 0x0082, 0x0553, 0x0b, 0xfa, 0x0000, 0x0000), 0x00), + 'Two Brothers House (East)': (0x0F, (0x00f5, 0x29, 0x0880, 0x0b07, 0x0200, 0x0b58, 0x0238, 0x0b74, 0x028d, 0x09, 0x00, 0x0b86, 0x0000), 0x00), + 'Two Brothers House (West)': (0x0E, (0x00f4, 0x28, 0x08a0, 0x0b06, 0x0100, 0x0b58, 0x01b8, 0x0b73, 0x018d, 0x0a, 0x00, 0x0bb6, 0x0000), 0x00), + 'Sanctuary': (0x01, (0x0012, 0x13, 0x001c, 0x0400, 0x06de, 0x0414, 0x0758, 0x046d, 0x0763, 0x00, 0x02, 0x0000, 0x01aa), 0x00), + 'Old Man Cave (West)': (0x05, (0x00f0, 0x0a, 0x03a0, 0x0264, 0x0500, 0x02b8, 0x05a8, 0x02d3, 0x058d, 0x0a, 0x00, 0x0000, 0x0000), 0x00), + 'Old Man Cave (East)': (0x06, (0x00f1, 0x03, 0x1402, 0x0294, 0x0604, 0x02e8, 0x0678, 0x0303, 0x0683, 0x0a, 0xfc, 0x0000, 0x0000), 0x00), + 'Old Man House (Bottom)': (0x2F, (0x00e4, 0x03, 0x181a, 0x031e, 0x06b4, 0x03a7, 0x0728, 0x038d, 0x0733, 0x00, 0x0c, 0x0000, 0x0000), 0x00), + 'Old Man House (Top)': (0x30, (0x00e5, 0x03, 0x10c6, 0x0224, 0x0814, 0x0278, 0x0888, 0x0293, 0x0893, 0x0a, 0x0c, 0x0000, 0x0000), 0x00), + 'Death Mountain Return Cave (East)': (0x2E, (0x00e7, 0x03, 0x0d82, 0x01c4, 0x0600, 0x0218, 0x0648, 0x0233, 0x067f, 0x0a, 0x00, 0x0000, 0x0000), 0x00), + 'Death Mountain Return Cave (West)': (0x2D, (0x00e6, 0x0a, 0x00a0, 0x0205, 0x0500, 0x0257, 0x05b8, 0x0272, 0x058d, 0x0b, 0x00, 0x0000, 0x0000), 0x00), + 'Spectacle Rock Cave Peak': (0x22, (0x00ea, 0x03, 0x092c, 0x0133, 0x0754, 0x0187, 0x07c8, 0x01a2, 0x07d3, 0x0b, 0xfc, 0x0000, 0x0000), 0x00), + 'Spectacle Rock Cave': (0x21, (0x00fa, 0x03, 0x0eac, 0x01e3, 0x0754, 0x0237, 0x07c8, 0x0252, 0x07d3, 0x0b, 0xfc, 0x0000, 0x0000), 0x00), + 'Spectacle Rock Cave (Bottom)': (0x20, (0x00f9, 0x03, 0x0d9c, 0x01c3, 0x06d4, 0x0217, 0x0748, 0x0232, 0x0753, 0x0b, 0xfc, 0x0000, 0x0000), 0x00), + 'Paradox Cave (Bottom)': (0x1D, (0x00ff, 0x05, 0x0ee0, 0x01e3, 0x0d00, 0x0237, 0x0da8, 0x0252, 0x0d7d, 0x0b, 0x00, 0x0000, 0x0000), 0x00), + 'Paradox Cave (Middle)': (0x1E, (0x00ef, 0x05, 0x17e0, 0x0304, 0x0d00, 0x0358, 0x0dc8, 0x0373, 0x0d7d, 0x0a, 0x00, 0x0000, 0x0000), 0x00), + 'Paradox Cave (Top)': (0x1F, (0x00df, 0x05, 0x0460, 0x0093, 0x0d00, 0x00e7, 0x0db8, 0x0102, 0x0d7d, 0x0b, 0x00, 0x0000, 0x0000), 0x00), + 'Fairy Ascension Cave (Bottom)': (0x19, (0x00fd, 0x05, 0x0dd4, 0x01c4, 0x0ca6, 0x0218, 0x0d18, 0x0233, 0x0d23, 0x0a, 0xfa, 0x0000, 0x0000), 0x00), + 'Fairy Ascension Cave (Top)': (0x1A, (0x00ed, 0x05, 0x0ad4, 0x0163, 0x0ca6, 0x01b7, 0x0d18, 0x01d2, 0x0d23, 0x0b, 0xfa, 0x0000, 0x0000), 0x00), + 'Spiral Cave': (0x1C, (0x00ee, 0x05, 0x07c8, 0x0108, 0x0c46, 0x0158, 0x0cb8, 0x0177, 0x0cc3, 0x06, 0xfa, 0x0000, 0x0000), 0x00), + 'Spiral Cave (Bottom)': (0x1B, (0x00fe, 0x05, 0x0cca, 0x01a3, 0x0c56, 0x01f7, 0x0cc8, 0x0212, 0x0cd3, 0x0b, 0xfa, 0x0000, 0x0000), 0x00), + 'Bumper Cave (Bottom)': (0x15, (0x00fb, 0x4a, 0x03a0, 0x0263, 0x0500, 0x02b7, 0x05a8, 0x02d2, 0x058d, 0x0b, 0x00, 0x0000, 0x0000), 0x00), + 'Bumper Cave (Top)': (0x16, (0x00eb, 0x4a, 0x00a0, 0x020a, 0x0500, 0x0258, 0x05b8, 0x0277, 0x058d, 0x06, 0x00, 0x0000, 0x0000), 0x00), + 'Superbunny Cave (Top)': (0x13, (0x00e8, 0x45, 0x0460, 0x0093, 0x0d00, 0x00e7, 0x0db8, 0x0102, 0x0d7d, 0x0b, 0x00, 0x0000, 0x0000), 0x00), + 'Superbunny Cave (Bottom)': (0x12, (0x00f8, 0x45, 0x0ee0, 0x01e4, 0x0d00, 0x0238, 0x0d78, 0x0253, 0x0d7d, 0x0a, 0x00, 0x0000, 0x0000), 0x00), + 'Hookshot Cave': (0x39, (0x003c, 0x45, 0x04da, 0x00a3, 0x0cd6, 0x0107, 0x0d48, 0x0112, 0x0d53, 0x0b, 0xfa, 0x0000, 0x0000), 0x20), + 'Hookshot Cave Back Entrance': (0x3A, (0x002c, 0x45, 0x004c, 0x0000, 0x0c56, 0x0038, 0x0cc8, 0x006f, 0x0cd3, 0x00, 0x0a, 0x0000, 0x0000), 0x00), + 'Ganons Tower': (0x36, (0x000c, 0x43, 0x0052, 0x0000, 0x0884, 0x0028, 0x08f8, 0x006f, 0x0903, 0x00, 0xfc, 0x0000, 0x0000), 0x20), + 'Pyramid Entrance': (0x35, (0x0010, 0x5b, 0x0b0e, 0x075a, 0x0674, 0x07a8, 0x06e8, 0x07c7, 0x06f3, 0x06, 0xfa, 0x0000, 0x0000), 0x00), 'Skull Woods First Section Hole (West)': ([0xDB84D, 0xDB84E], None), 'Skull Woods First Section Hole (East)': ([0xDB84F, 0xDB850], None), 'Skull Woods First Section Hole (North)': ([0xDB84C], None), 'Skull Woods Second Section Hole': ([0xDB851, 0xDB852], None), 'Pyramid Hole': ([0xDB854, 0xDB855, 0xDB856], None), 'Inverted Pyramid Hole': ([0xDB854, 0xDB855, 0xDB856, 0x180340], None), - 'Waterfall of Wishing': (0x5B, (0x0114, 0x0f, 0x0080, 0x0200, 0x0e00, 0x0207, 0x0e60, 0x026f, 0x0e7d, 0x00, 0x00, 0x0000, 0x0000)), - 'Dam': (0x4D, (0x010b, 0x3b, 0x04a0, 0x0e8a, 0x06fa, 0x0ed8, 0x0778, 0x0ef7, 0x077f, 0x06, 0xfa, 0x0000, 0x0000)), - 'Blinds Hideout': (0x60, (0x0119, 0x18, 0x02b2, 0x064a, 0x0186, 0x0697, 0x0208, 0x06b7, 0x0213, 0x06, 0xfa, 0x0000, 0x0000)), + 'Waterfall of Wishing': (0x5B, (0x0114, 0x0f, 0x0080, 0x0200, 0x0e00, 0x0207, 0x0e60, 0x026f, 0x0e7d, 0x00, 0x00, 0x0000, 0x0000), 0x00), + 'Dam': (0x4D, (0x010b, 0x3b, 0x04a0, 0x0e8a, 0x06fa, 0x0ed8, 0x0778, 0x0ef7, 0x077f, 0x06, 0xfa, 0x0000, 0x0000), 0x00), + 'Blinds Hideout': (0x60, (0x0119, 0x18, 0x02b2, 0x064a, 0x0186, 0x0697, 0x0208, 0x06b7, 0x0213, 0x06, 0xfa, 0x0000, 0x0000), 0x00), 'Hyrule Castle Secret Entrance Drop': ([0xDB858], None), - 'Bonk Fairy (Light)': (0x76, (0x0126, 0x2b, 0x00a0, 0x0a0a, 0x0700, 0x0a67, 0x0788, 0x0a77, 0x0785, 0x06, 0xfa, 0x0000, 0x0000)), - 'Lake Hylia Fairy': (0x5D, (0x0115, 0x2e, 0x0016, 0x0a00, 0x0cb6, 0x0a37, 0x0d28, 0x0a6d, 0x0d33, 0x00, 0x00, 0x0000, 0x0000)), - 'Light Hype Fairy': (0x6B, (0x0115, 0x34, 0x00a0, 0x0c04, 0x0900, 0x0c58, 0x0988, 0x0c73, 0x0985, 0x0a, 0xf6, 0x0000, 0x0000)), - 'Desert Fairy': (0x71, (0x0115, 0x3a, 0x0000, 0x0e00, 0x0400, 0x0e26, 0x0468, 0x0e6d, 0x0485, 0x00, 0x00, 0x0000, 0x0000)), - 'Kings Grave': (0x5A, (0x0113, 0x14, 0x0320, 0x0456, 0x0900, 0x04a6, 0x0998, 0x04c3, 0x097d, 0x0a, 0xf6, 0x0000, 0x0000)), - 'Tavern North': (0x42, (0x0103, 0x18, 0x1440, 0x08a7, 0x0206, 0x08f9, 0x0288, 0x0914, 0x0293, 0xf7, 0x09, 0xFFFF, 0x0000)), # do not use, buggy - 'Chicken House': (0x4A, (0x0108, 0x18, 0x1120, 0x0837, 0x0106, 0x0888, 0x0188, 0x08a4, 0x0193, 0x07, 0xf9, 0x1530, 0x0000)), - 'Aginahs Cave': (0x70, (0x010a, 0x30, 0x0656, 0x0cc6, 0x02aa, 0x0d18, 0x0328, 0x0d33, 0x032f, 0x08, 0xf8, 0x0000, 0x0000)), - 'Sahasrahlas Hut': (0x44, (0x0105, 0x1e, 0x0610, 0x06d4, 0x0c76, 0x0727, 0x0cf0, 0x0743, 0x0cfb, 0x0a, 0xf6, 0x0000, 0x0000)), - 'Cave Shop (Lake Hylia)': (0x57, (0x0112, 0x35, 0x0022, 0x0c00, 0x0b1a, 0x0c26, 0x0b98, 0x0c6d, 0x0b9f, 0x00, 0x00, 0x0000, 0x0000)), - 'Capacity Upgrade': (0x5C, (0x0115, 0x35, 0x0a46, 0x0d36, 0x0c2a, 0x0d88, 0x0ca8, 0x0da3, 0x0caf, 0x0a, 0xf6, 0x0000, 0x0000)), + 'Bonk Fairy (Light)': (0x76, (0x0126, 0x2b, 0x00a0, 0x0a0a, 0x0700, 0x0a67, 0x0788, 0x0a77, 0x0785, 0x06, 0xfa, 0x0000, 0x0000), 0x20), + 'Lake Hylia Fairy': (0x5D, (0x0115, 0x2e, 0x0016, 0x0a00, 0x0cb6, 0x0a37, 0x0d28, 0x0a6d, 0x0d33, 0x00, 0x00, 0x0000, 0x0000), 0x00), + 'Light Hype Fairy': (0x6B, (0x0115, 0x34, 0x00a0, 0x0c04, 0x0900, 0x0c58, 0x0988, 0x0c73, 0x0985, 0x0a, 0xf6, 0x0000, 0x0000), 0x02), + 'Desert Fairy': (0x71, (0x0115, 0x3a, 0x0000, 0x0e00, 0x0400, 0x0e26, 0x0468, 0x0e6d, 0x0485, 0x00, 0x00, 0x0000, 0x0000), 0x00), + 'Kings Grave': (0x5A, (0x0113, 0x14, 0x0320, 0x0456, 0x0900, 0x04a6, 0x0998, 0x04c3, 0x097d, 0x0a, 0xf6, 0x0000, 0x0000), 0x20), + 'Tavern North': (0x42, (0x0103, 0x18, 0x1440, 0x08a7, 0x0206, 0x08f9, 0x0288, 0x0914, 0x0293, 0xf7, 0x09, 0xFFFF, 0x0000), 0x00), # do not use, buggy + 'Chicken House': (0x4A, (0x0108, 0x18, 0x1120, 0x0837, 0x0106, 0x0888, 0x0188, 0x08a4, 0x0193, 0x07, 0xf9, 0x1530, 0x0000), 0x00), + 'Aginahs Cave': (0x70, (0x010a, 0x30, 0x0656, 0x0cc6, 0x02aa, 0x0d18, 0x0328, 0x0d33, 0x032f, 0x08, 0xf8, 0x0000, 0x0000), 0x00), + 'Sahasrahlas Hut': (0x44, (0x0105, 0x1e, 0x0610, 0x06d4, 0x0c76, 0x0727, 0x0cf0, 0x0743, 0x0cfb, 0x0a, 0xf6, 0x0000, 0x0000), 0x00), + 'Cave Shop (Lake Hylia)': (0x57, (0x0112, 0x35, 0x0022, 0x0c00, 0x0b1a, 0x0c26, 0x0b98, 0x0c6d, 0x0b9f, 0x00, 0x00, 0x0000, 0x0000), 0x00), + 'Capacity Upgrade': (0x5C, (0x0115, 0x35, 0x0a46, 0x0d36, 0x0c2a, 0x0d88, 0x0ca8, 0x0da3, 0x0caf, 0x0a, 0xf6, 0x0000, 0x0000), 0x00), 'Kakariko Well Drop': ([0xDB85C, 0xDB85D], None), - 'Blacksmiths Hut': (0x63, (0x0121, 0x22, 0x010c, 0x081a, 0x0466, 0x0868, 0x04d8, 0x0887, 0x04e3, 0x06, 0xfa, 0x041A, 0x0000)), + 'Blacksmiths Hut': (0x63, (0x0121, 0x22, 0x010c, 0x081a, 0x0466, 0x0868, 0x04d8, 0x0887, 0x04e3, 0x06, 0xfa, 0x041A, 0x0000), 0x00), 'Bat Cave Drop': ([0xDB859, 0xDB85A], None), - 'Sick Kids House': (0x3F, (0x0102, 0x18, 0x10be, 0x0826, 0x01f6, 0x0877, 0x0278, 0x0893, 0x0283, 0x08, 0xf8, 0x14CE, 0x0000)), + 'Sick Kids House': (0x3F, (0x0102, 0x18, 0x10be, 0x0826, 0x01f6, 0x0877, 0x0278, 0x0893, 0x0283, 0x08, 0xf8, 0x14CE, 0x0000), 0x00), 'North Fairy Cave Drop': ([0xDB857], None), - 'Lost Woods Gamble': (0x3B, (0x0100, 0x00, 0x004e, 0x0000, 0x0272, 0x0008, 0x02f0, 0x006f, 0x02f7, 0x00, 0x00, 0x0000, 0x0000)), - 'Fortune Teller (Light)': (0x64, (0x0122, 0x11, 0x060e, 0x04b4, 0x027d, 0x0508, 0x02f8, 0x0523, 0x0302, 0x0a, 0xf6, 0x0000, 0x0000)), - 'Snitch Lady (East)': (0x3D, (0x0101, 0x18, 0x0ad8, 0x074a, 0x02c6, 0x0798, 0x0348, 0x07b7, 0x0353, 0x06, 0xfa, 0x0DE8, 0x0000)), - 'Snitch Lady (West)': (0x3E, (0x0101, 0x18, 0x0788, 0x0706, 0x0046, 0x0758, 0x00c8, 0x0773, 0x00d3, 0x08, 0xf8, 0x0B98, 0x0000)), - 'Bush Covered House': (0x43, (0x0103, 0x18, 0x1156, 0x081a, 0x02b6, 0x0868, 0x0338, 0x0887, 0x0343, 0x06, 0xfa, 0x1466, 0x0000)), - 'Tavern (Front)': (0x41, (0x0103, 0x18, 0x1842, 0x0916, 0x0206, 0x0967, 0x0288, 0x0983, 0x0293, 0x08, 0xf8, 0x1C50, 0x0000)), - 'Light World Bomb Hut': (0x49, (0x0107, 0x18, 0x1800, 0x0916, 0x0000, 0x0967, 0x0068, 0x0983, 0x008d, 0x08, 0xf8, 0x9C0C, 0x0000)), - 'Kakariko Shop': (0x45, (0x011f, 0x18, 0x16a8, 0x08e7, 0x0136, 0x0937, 0x01b8, 0x0954, 0x01c3, 0x07, 0xf9, 0x1AB6, 0x0000)), + 'Lost Woods Gamble': (0x3B, (0x0100, 0x00, 0x004e, 0x0000, 0x0272, 0x0008, 0x02f0, 0x006f, 0x02f7, 0x00, 0x00, 0x0000, 0x0000), 0x00), + 'Fortune Teller (Light)': (0x64, (0x0122, 0x11, 0x060e, 0x04b4, 0x027d, 0x0508, 0x02f8, 0x0523, 0x0302, 0x0a, 0xf6, 0x0000, 0x0000), 0x00), + 'Snitch Lady (East)': (0x3D, (0x0101, 0x18, 0x0ad8, 0x074a, 0x02c6, 0x0798, 0x0348, 0x07b7, 0x0353, 0x06, 0xfa, 0x0DE8, 0x0000), 0x00), + 'Snitch Lady (West)': (0x3E, (0x0101, 0x18, 0x0788, 0x0706, 0x0046, 0x0758, 0x00c8, 0x0773, 0x00d3, 0x08, 0xf8, 0x0B98, 0x0000), 0x00), + 'Bush Covered House': (0x43, (0x0103, 0x18, 0x1156, 0x081a, 0x02b6, 0x0868, 0x0338, 0x0887, 0x0343, 0x06, 0xfa, 0x1466, 0x0000), 0x00), + 'Tavern (Front)': (0x41, (0x0103, 0x18, 0x1842, 0x0916, 0x0206, 0x0967, 0x0288, 0x0983, 0x0293, 0x08, 0xf8, 0x1C50, 0x0000), 0x00), + 'Light World Bomb Hut': (0x49, (0x0107, 0x18, 0x1800, 0x0916, 0x0000, 0x0967, 0x0068, 0x0983, 0x008d, 0x08, 0xf8, 0x9C0C, 0x0000), 0x02), + 'Kakariko Shop': (0x45, (0x011f, 0x18, 0x16a8, 0x08e7, 0x0136, 0x0937, 0x01b8, 0x0954, 0x01c3, 0x07, 0xf9, 0x1AB6, 0x0000), 0x00), 'Lost Woods Hideout Drop': ([0xDB853], None), 'Lumberjack Tree Tree': ([0xDB85B], None), - 'Cave 45': (0x50, (0x011b, 0x32, 0x0680, 0x0cc9, 0x0400, 0x0d16, 0x0438, 0x0d36, 0x0485, 0x07, 0xf9, 0x0000, 0x0000)), - 'Graveyard Cave': (0x51, (0x011b, 0x14, 0x0016, 0x0400, 0x08a2, 0x0446, 0x0918, 0x046d, 0x091f, 0x00, 0x00, 0x0000, 0x0000)), - 'Checkerboard Cave': (0x7D, (0x0126, 0x30, 0x00c8, 0x0c0a, 0x024a, 0x0c67, 0x02c8, 0x0c77, 0x02cf, 0x06, 0xfa, 0x0000, 0x0000)), - 'Mini Moldorm Cave': (0x7C, (0x0123, 0x35, 0x1480, 0x0e96, 0x0a00, 0x0ee8, 0x0a68, 0x0f03, 0x0a85, 0x08, 0xf8, 0x0000, 0x0000)), - 'Long Fairy Cave': (0x54, (0x011e, 0x2f, 0x06a0, 0x0aca, 0x0f00, 0x0b18, 0x0fa8, 0x0b37, 0x0f85, 0x06, 0xfa, 0x0000, 0x0000)), - 'Good Bee Cave': (0x6A, (0x0120, 0x37, 0x0084, 0x0c00, 0x0e26, 0x0c36, 0x0e98, 0x0c6f, 0x0ea3, 0x00, 0x00, 0x0000, 0x0000)), - '20 Rupee Cave': (0x7A, (0x0125, 0x37, 0x0200, 0x0c23, 0x0e00, 0x0c86, 0x0e68, 0x0c92, 0x0e7d, 0x0d, 0xf3, 0x0000, 0x0000)), - '50 Rupee Cave': (0x78, (0x0124, 0x3a, 0x0790, 0x0eea, 0x047a, 0x0f47, 0x04f8, 0x0f57, 0x04ff, 0x06, 0xfa, 0x0000, 0x0000)), - 'Ice Rod Cave': (0x7F, (0x0120, 0x37, 0x0080, 0x0c00, 0x0e00, 0x0c37, 0x0e48, 0x0c6f, 0x0e7d, 0x00, 0x00, 0x0000, 0x0000)), - 'Bonk Rock Cave': (0x79, (0x0124, 0x13, 0x0280, 0x044a, 0x0600, 0x04a7, 0x0638, 0x04b7, 0x067d, 0x06, 0xfa, 0x0000, 0x0000)), - 'Library': (0x48, (0x0107, 0x29, 0x0100, 0x0a14, 0x0200, 0x0a67, 0x0278, 0x0a83, 0x0285, 0x0a, 0xf6, 0x040E, 0x0000)), - 'Potion Shop': (0x4B, (0x0109, 0x16, 0x070a, 0x04e6, 0x0c56, 0x0538, 0x0cc8, 0x0553, 0x0cd3, 0x08, 0xf8, 0x0A98, 0x0000)), + 'Cave 45': (0x50, (0x011b, 0x32, 0x0680, 0x0cc9, 0x0400, 0x0d16, 0x0438, 0x0d36, 0x0485, 0x07, 0xf9, 0x0000, 0x0000), 0x00), + 'Graveyard Cave': (0x51, (0x011b, 0x14, 0x0016, 0x0400, 0x08a2, 0x0446, 0x0918, 0x046d, 0x091f, 0x00, 0x00, 0x0000, 0x0000), 0x00), + 'Checkerboard Cave': (0x7D, (0x0126, 0x30, 0x00c8, 0x0c0a, 0x024a, 0x0c67, 0x02c8, 0x0c77, 0x02cf, 0x06, 0xfa, 0x0000, 0x0000), 0x20), + 'Mini Moldorm Cave': (0x7C, (0x0123, 0x35, 0x1480, 0x0e96, 0x0a00, 0x0ee8, 0x0a68, 0x0f03, 0x0a85, 0x08, 0xf8, 0x0000, 0x0000), 0x02), + 'Long Fairy Cave': (0x54, (0x011e, 0x2f, 0x06a0, 0x0aca, 0x0f00, 0x0b18, 0x0fa8, 0x0b37, 0x0f85, 0x06, 0xfa, 0x0000, 0x0000), 0x00), + 'Good Bee Cave': (0x6A, (0x0120, 0x37, 0x0084, 0x0c00, 0x0e26, 0x0c36, 0x0e98, 0x0c6f, 0x0ea3, 0x00, 0x00, 0x0000, 0x0000), 0x00), + '20 Rupee Cave': (0x7A, (0x0125, 0x37, 0x0200, 0x0c23, 0x0e00, 0x0c86, 0x0e68, 0x0c92, 0x0e7d, 0x0d, 0xf3, 0x0000, 0x0000), 0x20), + '50 Rupee Cave': (0x78, (0x0124, 0x3a, 0x0790, 0x0eea, 0x047a, 0x0f47, 0x04f8, 0x0f57, 0x04ff, 0x06, 0xfa, 0x0000, 0x0000), 0x20), + 'Ice Rod Cave': (0x7F, (0x0120, 0x37, 0x0080, 0x0c00, 0x0e00, 0x0c37, 0x0e48, 0x0c6f, 0x0e7d, 0x00, 0x00, 0x0000, 0x0000), 0x02), + 'Bonk Rock Cave': (0x79, (0x0124, 0x13, 0x0280, 0x044a, 0x0600, 0x04a7, 0x0638, 0x04b7, 0x067d, 0x06, 0xfa, 0x0000, 0x0000), 0x20), + 'Library': (0x48, (0x0107, 0x29, 0x0100, 0x0a14, 0x0200, 0x0a67, 0x0278, 0x0a83, 0x0285, 0x0a, 0xf6, 0x040E, 0x0000), 0x00), + 'Potion Shop': (0x4B, (0x0109, 0x16, 0x070a, 0x04e6, 0x0c56, 0x0538, 0x0cc8, 0x0553, 0x0cd3, 0x08, 0xf8, 0x0A98, 0x0000), 0x00), 'Sanctuary Grave': ([0xDB85E], None), - 'Hookshot Fairy': (0x4F, (0x010c, 0x05, 0x0ee0, 0x01e3, 0x0d00, 0x0236, 0x0d78, 0x0252, 0x0d7d, 0x0b, 0xf5, 0x0000, 0x0000)), - 'Pyramid Fairy': (0x62, (0x0116, 0x5b, 0x0b1e, 0x0754, 0x06fa, 0x07a7, 0x0778, 0x07c3, 0x077f, 0x0a, 0xf6, 0x0000, 0x0000)), - 'East Dark World Hint': (0x68, (0x010e, 0x6f, 0x06a0, 0x0aca, 0x0f00, 0x0b18, 0x0fa8, 0x0b37, 0x0f85, 0x06, 0xfa, 0x0000, 0x0000)), - 'Palace of Darkness Hint': (0x67, (0x011a, 0x5e, 0x0c24, 0x0794, 0x0d12, 0x07e8, 0x0d90, 0x0803, 0x0d97, 0x0a, 0xf6, 0x0000, 0x0000)), - 'Dark Lake Hylia Fairy': (0x6C, (0x0115, 0x6e, 0x0016, 0x0a00, 0x0cb6, 0x0a36, 0x0d28, 0x0a6d, 0x0d33, 0x00, 0x00, 0x0000, 0x0000)), - 'Dark Lake Hylia Ledge Fairy': (0x80, (0x0115, 0x77, 0x0080, 0x0c00, 0x0e00, 0x0c37, 0x0e48, 0x0c6f, 0x0e7d, 0x00, 0x00, 0x0000, 0x0000)), - 'Dark Lake Hylia Ledge Spike Cave': (0x7B, (0x0125, 0x77, 0x0200, 0x0c27, 0x0e00, 0x0c86, 0x0e68, 0x0c96, 0x0e7d, 0x09, 0xf7, 0x0000, 0x0000)), - 'Dark Lake Hylia Ledge Hint': (0x69, (0x010e, 0x77, 0x0084, 0x0c00, 0x0e26, 0x0c36, 0x0e98, 0x0c6f, 0x0ea3, 0x00, 0x00, 0x0000, 0x0000)), - 'Hype Cave': (0x3C, (0x011e, 0x74, 0x00a0, 0x0c0a, 0x0900, 0x0c58, 0x0988, 0x0c77, 0x097d, 0x06, 0xfa, 0x0000, 0x0000)), - 'Bonk Fairy (Dark)': (0x77, (0x0126, 0x6b, 0x00a0, 0x0a05, 0x0700, 0x0a66, 0x0788, 0x0a72, 0x0785, 0x0b, 0xf5, 0x0000, 0x0000)), - 'Brewery': (0x47, (0x0106, 0x58, 0x16a8, 0x08e4, 0x013e, 0x0938, 0x01b8, 0x0953, 0x01c3, 0x0a, 0xf6, 0x1AB6, 0x0000)), - 'C-Shaped House': (0x53, (0x011c, 0x58, 0x09d8, 0x0744, 0x02ce, 0x0797, 0x0348, 0x07b3, 0x0353, 0x0a, 0xf6, 0x0DE8, 0x0000)), - 'Chest Game': (0x46, (0x0106, 0x58, 0x078a, 0x0705, 0x004e, 0x0758, 0x00c8, 0x0774, 0x00d3, 0x09, 0xf7, 0x0B98, 0x0000)), - 'Dark World Hammer Peg Cave': (0x7E, (0x0127, 0x62, 0x0894, 0x091e, 0x0492, 0x09a6, 0x0508, 0x098b, 0x050f, 0x00, 0x00, 0x0000, 0x0000)), - 'Red Shield Shop': (0x74, (0x0110, 0x5a, 0x079a, 0x06e8, 0x04d6, 0x0738, 0x0548, 0x0755, 0x0553, 0x08, 0xf8, 0x0AA8, 0x0000)), - 'Dark Sanctuary Hint': (0x59, (0x0112, 0x53, 0x001e, 0x0400, 0x06e2, 0x0446, 0x0758, 0x046d, 0x075f, 0x00, 0x00, 0x0000, 0x0000)), - 'Fortune Teller (Dark)': (0x65, (0x0122, 0x51, 0x0610, 0x04b4, 0x027e, 0x0507, 0x02f8, 0x0523, 0x0303, 0x0a, 0xf6, 0x091E, 0x0000)), - 'Dark World Shop': (0x5F, (0x010f, 0x58, 0x1058, 0x0814, 0x02be, 0x0868, 0x0338, 0x0883, 0x0343, 0x0a, 0xf6, 0x0000, 0x0000)), - 'Dark World Lumberjack Shop': (0x56, (0x010f, 0x42, 0x041c, 0x0074, 0x04e2, 0x00c7, 0x0558, 0x00e3, 0x055f, 0x0a, 0xf6, 0x0000, 0x0000)), - 'Dark World Potion Shop': (0x6E, (0x010f, 0x56, 0x080e, 0x04f4, 0x0c66, 0x0548, 0x0cd8, 0x0563, 0x0ce3, 0x0a, 0xf6, 0x0000, 0x0000)), - 'Archery Game': (0x58, (0x0111, 0x69, 0x069e, 0x0ac4, 0x02ea, 0x0b18, 0x0368, 0x0b33, 0x036f, 0x0a, 0xf6, 0x09AC, 0x0000)), - 'Mire Shed': (0x5E, (0x010d, 0x70, 0x0384, 0x0c69, 0x001e, 0x0cb6, 0x0098, 0x0cd6, 0x00a3, 0x07, 0xf9, 0x0000, 0x0000)), - 'Dark Desert Hint': (0x61, (0x0114, 0x70, 0x0654, 0x0cc5, 0x02aa, 0x0d16, 0x0328, 0x0d32, 0x032f, 0x09, 0xf7, 0x0000, 0x0000)), - 'Dark Desert Fairy': (0x55, (0x0115, 0x70, 0x03a8, 0x0c6a, 0x013a, 0x0cb7, 0x01b8, 0x0cd7, 0x01bf, 0x06, 0xfa, 0x0000, 0x0000)), - 'Spike Cave': (0x40, (0x0117, 0x43, 0x0ed4, 0x01e4, 0x08aa, 0x0236, 0x0928, 0x0253, 0x092f, 0x0a, 0xf6, 0x0000, 0x0000)), - 'Cave Shop (Dark Death Mountain)': (0x6D, (0x0112, 0x45, 0x0ee0, 0x01e3, 0x0d00, 0x0236, 0x0daa, 0x0252, 0x0d7d, 0x0b, 0xf5, 0x0000, 0x0000)), - 'Dark Death Mountain Fairy': (0x6F, (0x0115, 0x43, 0x1400, 0x0294, 0x0600, 0x02e8, 0x0678, 0x0303, 0x0685, 0x0a, 0xf6, 0x0000, 0x0000)), - 'Mimic Cave': (0x4E, (0x010c, 0x05, 0x07e0, 0x0103, 0x0d00, 0x0156, 0x0d78, 0x0172, 0x0d7d, 0x0b, 0xf5, 0x0000, 0x0000)), - 'Big Bomb Shop': (0x52, (0x011c, 0x6c, 0x0506, 0x0a9a, 0x0832, 0x0ae7, 0x08b8, 0x0b07, 0x08bf, 0x06, 0xfa, 0x0816, 0x0000)), - 'Dark Lake Hylia Shop': (0x73, (0x010f, 0x75, 0x0380, 0x0c6a, 0x0a00, 0x0cb8, 0x0a58, 0x0cd7, 0x0a85, 0x06, 0xfa, 0x0000, 0x0000)), - 'Lumberjack House': (0x75, (0x011f, 0x02, 0x049c, 0x0088, 0x04e6, 0x00d8, 0x0558, 0x00f7, 0x0563, 0x08, 0xf8, 0x07AA, 0x0000)), - 'Lake Hylia Fortune Teller': (0x72, (0x0122, 0x35, 0x0380, 0x0c6a, 0x0a00, 0x0cb8, 0x0a58, 0x0cd7, 0x0a85, 0x06, 0xfa, 0x0000, 0x0000)), - 'Kakariko Gamble Game': (0x66, (0x0118, 0x29, 0x069e, 0x0ac4, 0x02ea, 0x0b18, 0x0368, 0x0b33, 0x036f, 0x0a, 0xf6, 0x09AC, 0x0000))} + 'Hookshot Fairy': (0x4F, (0x010c, 0x05, 0x0ee0, 0x01e3, 0x0d00, 0x0236, 0x0d78, 0x0252, 0x0d7d, 0x0b, 0xf5, 0x0000, 0x0000), 0x00), + 'Pyramid Fairy': (0x62, (0x0116, 0x5b, 0x0b1e, 0x0754, 0x06fa, 0x07a7, 0x0778, 0x07c3, 0x077f, 0x0a, 0xf6, 0x0000, 0x0000), 0x02), + 'East Dark World Hint': (0x68, (0x010e, 0x6f, 0x06a0, 0x0aca, 0x0f00, 0x0b18, 0x0fa8, 0x0b37, 0x0f85, 0x06, 0xfa, 0x0000, 0x0000), 0x00), + 'Palace of Darkness Hint': (0x67, (0x011a, 0x5e, 0x0c24, 0x0794, 0x0d12, 0x07e8, 0x0d90, 0x0803, 0x0d97, 0x0a, 0xf6, 0x0000, 0x0000), 0x00), + 'Dark Lake Hylia Fairy': (0x6C, (0x0115, 0x6e, 0x0016, 0x0a00, 0x0cb6, 0x0a36, 0x0d28, 0x0a6d, 0x0d33, 0x00, 0x00, 0x0000, 0x0000), 0x00), + 'Dark Lake Hylia Ledge Fairy': (0x80, (0x0115, 0x77, 0x0080, 0x0c00, 0x0e00, 0x0c37, 0x0e48, 0x0c6f, 0x0e7d, 0x00, 0x00, 0x0000, 0x0000), 0x02), + 'Dark Lake Hylia Ledge Spike Cave': (0x7B, (0x0125, 0x77, 0x0200, 0x0c27, 0x0e00, 0x0c86, 0x0e68, 0x0c96, 0x0e7d, 0x09, 0xf7, 0x0000, 0x0000), 0x20), + 'Dark Lake Hylia Ledge Hint': (0x69, (0x010e, 0x77, 0x0084, 0x0c00, 0x0e26, 0x0c36, 0x0e98, 0x0c6f, 0x0ea3, 0x00, 0x00, 0x0000, 0x0000), 0x00), + 'Hype Cave': (0x3C, (0x011e, 0x74, 0x00a0, 0x0c0a, 0x0900, 0x0c58, 0x0988, 0x0c77, 0x097d, 0x06, 0xfa, 0x0000, 0x0000), 0x02), + 'Bonk Fairy (Dark)': (0x77, (0x0126, 0x6b, 0x00a0, 0x0a05, 0x0700, 0x0a66, 0x0788, 0x0a72, 0x0785, 0x0b, 0xf5, 0x0000, 0x0000), 0x20), + 'Brewery': (0x47, (0x0106, 0x58, 0x16a8, 0x08e4, 0x013e, 0x0938, 0x01b8, 0x0953, 0x01c3, 0x0a, 0xf6, 0x1AB6, 0x0000), 0x02), + 'C-Shaped House': (0x53, (0x011c, 0x58, 0x09d8, 0x0744, 0x02ce, 0x0797, 0x0348, 0x07b3, 0x0353, 0x0a, 0xf6, 0x0DE8, 0x0000), 0x00), + 'Chest Game': (0x46, (0x0106, 0x58, 0x078a, 0x0705, 0x004e, 0x0758, 0x00c8, 0x0774, 0x00d3, 0x09, 0xf7, 0x0B98, 0x0000), 0x00), + 'Dark World Hammer Peg Cave': (0x7E, (0x0127, 0x62, 0x0894, 0x091e, 0x0492, 0x09a6, 0x0508, 0x098b, 0x050f, 0x00, 0x00, 0x0000, 0x0000), 0x20), + 'Red Shield Shop': (0x74, (0x0110, 0x5a, 0x079a, 0x06e8, 0x04d6, 0x0738, 0x0548, 0x0755, 0x0553, 0x08, 0xf8, 0x0AA8, 0x0000), 0x00), + 'Dark Sanctuary Hint': (0x59, (0x0112, 0x53, 0x001e, 0x0400, 0x06e2, 0x0446, 0x0758, 0x046d, 0x075f, 0x00, 0x00, 0x0000, 0x0000), 0x00), + 'Fortune Teller (Dark)': (0x65, (0x0122, 0x51, 0x0610, 0x04b4, 0x027e, 0x0507, 0x02f8, 0x0523, 0x0303, 0x0a, 0xf6, 0x091E, 0x0000), 0x00), + 'Dark World Shop': (0x5F, (0x010f, 0x58, 0x1058, 0x0814, 0x02be, 0x0868, 0x0338, 0x0883, 0x0343, 0x0a, 0xf6, 0x0000, 0x0000), 0x00), + 'Dark World Lumberjack Shop': (0x56, (0x010f, 0x42, 0x041c, 0x0074, 0x04e2, 0x00c7, 0x0558, 0x00e3, 0x055f, 0x0a, 0xf6, 0x0000, 0x0000), 0x00), + 'Dark World Potion Shop': (0x6E, (0x010f, 0x56, 0x080e, 0x04f4, 0x0c66, 0x0548, 0x0cd8, 0x0563, 0x0ce3, 0x0a, 0xf6, 0x0000, 0x0000), 0x00), + 'Archery Game': (0x58, (0x0111, 0x69, 0x069e, 0x0ac4, 0x02ea, 0x0b18, 0x0368, 0x0b33, 0x036f, 0x0a, 0xf6, 0x09AC, 0x0000), 0x00), + 'Mire Shed': (0x5E, (0x010d, 0x70, 0x0384, 0x0c69, 0x001e, 0x0cb6, 0x0098, 0x0cd6, 0x00a3, 0x07, 0xf9, 0x0000, 0x0000), 0x00), + 'Dark Desert Hint': (0x61, (0x0114, 0x70, 0x0654, 0x0cc5, 0x02aa, 0x0d16, 0x0328, 0x0d32, 0x032f, 0x09, 0xf7, 0x0000, 0x0000), 0x00), + 'Dark Desert Fairy': (0x55, (0x0115, 0x70, 0x03a8, 0x0c6a, 0x013a, 0x0cb7, 0x01b8, 0x0cd7, 0x01bf, 0x06, 0xfa, 0x0000, 0x0000), 0x00), + 'Spike Cave': (0x40, (0x0117, 0x43, 0x0ed4, 0x01e4, 0x08aa, 0x0236, 0x0928, 0x0253, 0x092f, 0x0a, 0xf6, 0x0000, 0x0000), 0x00), + 'Cave Shop (Dark Death Mountain)': (0x6D, (0x0112, 0x45, 0x0ee0, 0x01e3, 0x0d00, 0x0236, 0x0daa, 0x0252, 0x0d7d, 0x0b, 0xf5, 0x0000, 0x0000), 0x00), + 'Dark Death Mountain Fairy': (0x6F, (0x0115, 0x43, 0x1400, 0x0294, 0x0600, 0x02e8, 0x0678, 0x0303, 0x0685, 0x0a, 0xf6, 0x0000, 0x0000), 0x00), + 'Mimic Cave': (0x4E, (0x010c, 0x05, 0x07e0, 0x0103, 0x0d00, 0x0156, 0x0d78, 0x0172, 0x0d7d, 0x0b, 0xf5, 0x0000, 0x0000), 0x00), + 'Big Bomb Shop': (0x52, (0x011c, 0x6c, 0x0506, 0x0a9a, 0x0832, 0x0ae7, 0x08b8, 0x0b07, 0x08bf, 0x06, 0xfa, 0x0816, 0x0000), 0x00), + 'Dark Lake Hylia Shop': (0x73, (0x010f, 0x75, 0x0380, 0x0c6a, 0x0a00, 0x0cb8, 0x0a58, 0x0cd7, 0x0a85, 0x06, 0xfa, 0x0000, 0x0000), 0x00), + 'Lumberjack House': (0x75, (0x011f, 0x02, 0x049c, 0x0088, 0x04e6, 0x00d8, 0x0558, 0x00f7, 0x0563, 0x08, 0xf8, 0x07AA, 0x0000), 0x00), + 'Lake Hylia Fortune Teller': (0x72, (0x0122, 0x35, 0x0380, 0x0c6a, 0x0a00, 0x0cb8, 0x0a58, 0x0cd7, 0x0a85, 0x06, 0xfa, 0x0000, 0x0000), 0x00), + 'Kakariko Gamble Game': (0x66, (0x0118, 0x29, 0x069e, 0x0ac4, 0x02ea, 0x0b18, 0x0368, 0x0b33, 0x036f, 0x0a, 0xf6, 0x09AC, 0x0000), 0x00)} # format: # Key=Name diff --git a/ItemList.py b/ItemList.py index b1856f63..bbddf1d6 100644 --- a/ItemList.py +++ b/ItemList.py @@ -272,7 +272,7 @@ def generate_itempool(world, player): (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_total, treasure_hunt_icon, lamps_needed_for_dark_rooms) = make_custom_item_pool(world.progressive, world.shuffle[player], world.difficulty[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.customitemarray[player]) world.rupoor_cost = min(world.customitemarray[player]["rupoorcost"], 9999) else: - (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.doorShuffle[player], world.logic[player]) + (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) = get_pool_core(world.progressive, world.shuffle[player], world.difficulty[player], world.treasure_hunt_total[player], world.timer, world.goal[player], world.mode[player], world.swords[player], world.retro[player], world.bombbag[player], world.doorShuffle[player], world.logic[player], world.is_tile_swapped(0x18, player)) if player in world.pool_adjustment.keys(): amt = world.pool_adjustment[player] @@ -797,7 +797,7 @@ rupee_chart = {'Rupee (1)': 1, 'Rupees (5)': 5, 'Rupees (20)': 20, 'Rupees (50)' 'Rupees (100)': 100, 'Rupees (300)': 300} -def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic): +def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic, flute_activated): pool = [] placed_items = {} precollected_items = [] @@ -809,6 +809,10 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, pool.extend(alwaysitems) + if flute_activated: + pool.remove('Ocarina') + pool.append('Ocarina (Activated)') + def place_item(loc, item): assert loc not in placed_items placed_items[loc] = item @@ -1086,7 +1090,7 @@ def test(): for retro in [True, False]: for door_shuffle in ['basic', 'crossed', 'vanilla']: for bombbag in [True, False]: - out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic) + out = get_pool_core(progressive, shuffle, difficulty, 30, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic, False) count = len(out[0]) + len(out[1]) correct_count = total_items_to_place diff --git a/Items.py b/Items.py index f10d2aca..1f5bae03 100644 --- a/Items.py +++ b/Items.py @@ -27,23 +27,24 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Progressive Bow': (True, False, None, 0x64, 150, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), 'Progressive Bow (Alt)': (True, False, None, 0x65, 150, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), 'Book of Mudora': (True, False, None, 0x1D, 150, 'This is a\nparadox?!', 'and the story book', 'the scholarly kid', 'moon runes for sale', 'drugs for literacy', 'book-worm boy can read again', 'the Book'), - 'Hammer': (True, False, None, 0x09, 250, 'stop\nhammer time!', 'and m c hammer', 'hammer-smashing kid', 'm c hammer for sale', 'stop... hammer time', 'stop, hammer time', 'the hammer'), + 'Hammer': (True, False, None, 0x09, 250, 'stop\nhammer time!', 'and m c hammer', 'hammer-smashing kid', 'm c hammer for sale', 'stop... hammer time', 'stop, hammer time', 'the Hammer'), 'Hookshot': (True, False, None, 0x0A, 250, 'BOING!!!\nBOING!!!\nBOING!!!', 'and the tickle beam', 'tickle-monster kid', 'tickle beam for sale', 'witch and tickle boy', 'beam boy tickles again', 'the Hookshot'), 'Magic Mirror': (True, False, None, 0x1A, 250, 'Isn\'t your\nreflection so\npretty?', 'the face reflector', 'the narcissistic kid', 'your face for sale', 'trades looking-glass', 'narcissistic boy is happy again', 'the Mirror'), 'Ocarina': (True, False, None, 0x14, 250, 'Save the duck\nand fly to\nfreedom!', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the Flute'), + 'Ocarina (Activated)': (True, False, None, 0x4A, 250, 'Save the duck\nand fly to\nfreedom!', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the Flute'), 'Pegasus Boots': (True, False, None, 0x4B, 250, 'Gotta go fast!', 'and the sprint shoes', 'the running-man kid', 'sprint shoe for sale', 'shrooms for speed', 'gotta-go-fast boy runs again', 'the Boots'), - 'Power Glove': (True, False, None, 0x1B, 100, 'Now you can\nlift weak\nstuff!', 'and the grey mittens', 'body-building kid', 'lift glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'the glove'), - 'Cape': (True, False, None, 0x19, 50, 'Wear this to\nbecome\ninvisible!', 'the camouflage cape', 'red riding-hood kid', 'red hood for sale', 'hood from a hood', 'dapper boy hides again', 'the cape'), - 'Mushroom': (True, False, None, 0x29, 50, 'I\'m a fun guy!\n\nI\'m a funghi!', 'and the legal drugs', 'the drug-dealing kid', 'legal drugs for sale', 'shroom swap', 'shroom boy sells drugs again', 'the mushroom'), - 'Shovel': (True, False, None, 0x13, 50, 'Can\n You\n Dig it?', 'and the spade', 'archaeologist kid', 'dirt spade for sale', 'can you dig it', 'shovel boy digs again', 'the shovel'), - 'Lamp': (True, False, None, 0x12, 150, 'Baby, baby,\nbaby.\nLight my way!', 'and the flashlight', 'light-shining kid', 'flashlight for sale', 'fungus for illumination', 'illuminated boy can see again', 'the lamp'), - 'Magic Powder': (True, False, None, 0x0D, 50, 'you can turn\nanti-faeries\ninto faeries', 'and the magic sack', 'the sack-holding kid', 'magic sack for sale', 'the witch and assistant', 'magic boy plays marbles again', 'the powder'), - 'Moon Pearl': (True, False, None, 0x1F, 200, ' Bunny Link\n be\n gone!', 'and the jaw breaker', 'fortune-telling kid', 'lunar orb for sale', 'shrooms for moon rock', 'moon boy plays ball again', 'the moon pearl'), - 'Cane of Somaria': (True, False, None, 0x15, 250, 'I make blocks\nto hold down\nswitches!', 'and the red blocks', 'the block-making kid', 'block stick for sale', 'block stick for trade', 'cane boy makes blocks again', 'the red cane'), - 'Fire Rod': (True, False, None, 0x07, 250, 'I\'m the hot\nrod. I make\nthings burn!', 'and the flamethrower', 'fire-starting kid', 'rage rod for sale', 'fungus for rage-rod', 'firestarter boy burns again', 'the fire rod'), + 'Power Glove': (True, False, None, 0x1B, 100, 'Now you can\nlift weak\nstuff!', 'and the grey mittens', 'body-building kid', 'lift glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'the Glove'), + 'Cape': (True, False, None, 0x19, 50, 'Wear this to\nbecome\ninvisible!', 'the camouflage cape', 'red riding-hood kid', 'red hood for sale', 'hood from a hood', 'dapper boy hides again', 'the Cape'), + 'Mushroom': (True, False, None, 0x29, 50, 'I\'m a fun guy!\n\nI\'m a funghi!', 'and the legal drugs', 'the drug-dealing kid', 'legal drugs for sale', 'shroom swap', 'shroom boy sells drugs again', 'the Mushroom'), + 'Shovel': (True, False, None, 0x13, 50, 'Can\n You\n Dig it?', 'and the spade', 'archaeologist kid', 'dirt spade for sale', 'can you dig it', 'shovel boy digs again', 'the Shovel'), + 'Lamp': (True, False, None, 0x12, 150, 'Baby, baby,\nbaby.\nLight my way!', 'and the flashlight', 'light-shining kid', 'flashlight for sale', 'fungus for illumination', 'illuminated boy can see again', 'the Lamp'), + 'Magic Powder': (True, False, None, 0x0D, 50, 'you can turn\nanti-faeries\ninto faeries', 'and the magic sack', 'the sack-holding kid', 'magic sack for sale', 'the witch and assistant', 'magic boy plays marbles again', 'the Powder'), + 'Moon Pearl': (True, False, None, 0x1F, 200, ' Bunny Link\n be\n gone!', 'and the jaw breaker', 'fortune-telling kid', 'lunar orb for sale', 'shrooms for moon rock', 'moon boy plays ball again', 'the Moon Pearl'), + 'Cane of Somaria': (True, False, None, 0x15, 250, 'I make blocks\nto hold down\nswitches!', 'and the red blocks', 'the block-making kid', 'block stick for sale', 'block stick for trade', 'cane boy makes blocks again', 'the Red Cane'), + 'Fire Rod': (True, False, None, 0x07, 250, 'I\'m the hot\nrod. I make\nthings burn!', 'and the flamethrower', 'fire-starting kid', 'rage rod for sale', 'fungus for rage-rod', 'firestarter boy burns again', 'the Fire Rod'), 'Flippers': (True, False, None, 0x1E, 250, 'fancy a swim?', 'and the toewebs', 'the swimming kid', 'finger webs for sale', 'shrooms let you swim', 'swimming boy swims again', 'the flippers'), - 'Ice Rod': (True, False, None, 0x08, 250, 'I\'m the cold\nrod. I make\nthings freeze!', 'and the freeze ray', 'the ice-bending kid', 'freeze ray for sale', 'fungus for ice-rod', 'ice-cube boy freezes again', 'the ice rod'), - 'Titans Mitts': (True, False, None, 0x1C, 200, 'Now you can\nlift heavy\nstuff!', 'and the golden glove', 'body-building kid', 'carry glove for sale', 'fungus for bling-gloves', 'body-building boy has gold again', 'the mitts'), + 'Ice Rod': (True, False, None, 0x08, 250, 'I\'m the cold\nrod. I make\nthings freeze!', 'and the freeze ray', 'the ice-bending kid', 'freeze ray for sale', 'fungus for ice-rod', 'ice-cube boy freezes again', 'the Ice Rod'), + 'Titans Mitts': (True, False, None, 0x1C, 200, 'Now you can\nlift heavy\nstuff!', 'and the golden glove', 'body-building kid', 'carry glove for sale', 'fungus for bling-gloves', 'body-building boy has gold again', 'the Mitts'), 'Bombos': (True, False, None, 0x0F, 100, 'Burn, baby,\nburn! Fear my\nring of fire!', 'and the swirly coin', 'coin-collecting kid', 'swirly coin for sale', 'shrooms for swirly-coin', 'medallion boy melts room again', 'Bombos'), 'Ether': (True, False, None, 0x10, 100, 'This magic\ncoin freezes\neverything!', 'and the bolt coin', 'coin-collecting kid', 'bolt coin for sale', 'shrooms for bolt-coin', 'medallion boy sees floor again', 'Ether'), 'Quake': (True, False, None, 0x11, 100, 'Maxing out the\nRichter scale\nis what I do!', 'and the wavy coin', 'coin-collecting kid', 'wavy coin for sale', 'shrooms for wavy-coin', 'medallion boy shakes dirt again', 'Quake'), @@ -56,11 +57,11 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Bottle (Good Bee)': (True, False, None, 0x48, 60, 'I will sting your foes a whole lot!', 'and the sparkle sting', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has beetor again', 'a Bottle'), 'Master Sword': (True, False, 'Sword', 0x50, 100, 'I beat barries and pigs alike', 'and the master sword', 'sword-wielding kid', 'glow sword for sale', 'fungus for blue slasher', 'sword boy fights again', 'the Master Sword'), 'Tempered Sword': (True, False, 'Sword', 0x02, 150, 'I stole the\nblacksmith\'s\njob!', 'the tempered sword', 'sword-wielding kid', 'flame sword for sale', 'fungus for red slasher', 'sword boy fights again', 'the Tempered Sword'), - 'Fighter Sword': (True, False, 'Sword', 0x49, 50, 'A pathetic\nsword rests\nhere!', 'the tiny sword', 'sword-wielding kid', 'tiny sword for sale', 'fungus for tiny slasher', 'sword boy fights again', 'the small sword'), + 'Fighter Sword': (True, False, 'Sword', 0x49, 50, 'A pathetic\nsword rests\nhere!', 'the tiny sword', 'sword-wielding kid', 'tiny sword for sale', 'fungus for tiny slasher', 'sword boy fights again', 'the Fighter Sword'), 'Golden Sword': (True, False, 'Sword', 0x03, 200, 'The butter\nsword rests\nhere!', 'and the butter sword', 'sword-wielding kid', 'butter for sale', 'cap churned to butter', 'sword boy fights again', 'the Golden Sword'), - 'Progressive Sword': (True, False, 'Sword', 0x5E, 150, 'a better copy\nof your sword\nfor your time', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a sword'), - 'Progressive Glove': (True, False, None, 0x61, 150, 'a way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a glove'), - 'Silver Arrows': (True, False, None, 0x58, 100, 'Do you fancy\nsilver tipped\narrows?', 'and the ganonsbane', 'ganon-killing kid', 'ganon doom for sale', 'fungus for pork', 'archer boy shines again', 'the silver arrows'), + 'Progressive Sword': (True, False, 'Sword', 0x5E, 150, 'a better copy\nof your sword\nfor your time', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a Sword'), + 'Progressive Glove': (True, False, None, 0x61, 150, 'a way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a Glove'), + 'Silver Arrows': (True, False, None, 0x58, 100, 'Do you fancy\nsilver tipped\narrows?', 'and the ganonsbane', 'ganon-killing kid', 'ganon doom for sale', 'fungus for pork', 'archer boy shines again', 'the Silver Arrows'), 'Green Pendant': (True, False, 'Crystal', [0x04, 0x38, 0x62, 0x00, 0x69, 0x01], 999, None, None, None, None, None, None, None), 'Blue Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x02], 999, None, None, None, None, None, None, None), 'Red Pendant': (True, False, 'Crystal', [0x01, 0x32, 0x60, 0x00, 0x69, 0x03], 999, None, None, None, None, None, None, None), @@ -83,17 +84,17 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Bombs (10)': (False, False, None, 0x31, 50, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'), 'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), 'Bomb Upgrade (+5)': (False, False, None, 0x51, 100, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), - 'Blue Mail': (False, True, None, 0x22, 50, 'Now you\'re a\nblue elf!', 'and the banana hat', 'the protected kid', 'banana hat for sale', 'the clothing store', 'tailor boy banana hatted again', 'the blue mail'), - 'Red Mail': (False, True, None, 0x23, 100, 'Now you\'re a\nred elf!', 'and the eggplant hat', 'well-protected kid', 'purple hat for sale', 'the nice clothing store', 'tailor boy fears nothing again', 'the red mail'), + 'Blue Mail': (False, True, None, 0x22, 50, 'Now you\'re a\nblue elf!', 'and the banana hat', 'the protected kid', 'banana hat for sale', 'the clothing store', 'tailor boy banana hatted again', 'the Blue Mail'), + 'Red Mail': (False, True, None, 0x23, 100, 'Now you\'re a\nred elf!', 'and the eggplant hat', 'well-protected kid', 'purple hat for sale', 'the nice clothing store', 'tailor boy fears nothing again', 'the Red Mail'), 'Progressive Armor': (False, True, None, 0x60, 50, 'time for a\nchange of\nclothes?', 'and the unknown hat', 'the protected kid', 'new hat for sale', 'the clothing store', 'tailor boy has threads again', 'some armor'), - 'Blue Boomerang': (True, False, None, 0x0C, 50, 'No matter what\nyou do, blue\nreturns to you', 'and the bluemarang', 'the bat-throwing kid', 'bent stick for sale', 'fungus for puma-stick', 'throwing boy plays fetch again', 'the blue boomerang'), - 'Red Boomerang': (True, False, None, 0x2A, 50, 'No matter what\nyou do, red\nreturns to you', 'and the badmarang', 'the bat-throwing kid', 'air foil for sale', 'fungus for return-stick', 'magical boy plays fetch again', 'the red boomerang'), - 'Blue Shield': (False, True, None, 0x04, 50, 'Now you can\ndefend against\npebbles!', 'and the stone blocker', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'the blue shield'), - 'Red Shield': (False, True, None, 0x05, 500, 'Now you can\ndefend against\nfireballs!', 'and the shot blocker', 'shield-wielding kid', 'fire shield for sale', 'fungus for fire shield', 'shield boy defends again', 'the red shield'), - 'Mirror Shield': (True, False, None, 0x06, 200, 'Now you can\ndefend against\nlasers!', 'and the laser blocker', 'shield-wielding kid', 'face shield for sale', 'fungus for face shield', 'shield boy defends again', 'the mirror shield'), + 'Blue Boomerang': (True, False, None, 0x0C, 50, 'No matter what\nyou do, blue\nreturns to you', 'and the bluemarang', 'the bat-throwing kid', 'bent stick for sale', 'fungus for puma-stick', 'throwing boy plays fetch again', 'the Blue Boomerang'), + 'Red Boomerang': (True, False, None, 0x2A, 50, 'No matter what\nyou do, red\nreturns to you', 'and the badmarang', 'the bat-throwing kid', 'air foil for sale', 'fungus for return-stick', 'magical boy plays fetch again', 'the Red Boomerang'), + 'Blue Shield': (False, True, None, 0x04, 50, 'Now you can\ndefend against\npebbles!', 'and the stone blocker', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'the Blue Shield'), + 'Red Shield': (False, True, None, 0x05, 500, 'Now you can\ndefend against\nfireballs!', 'and the shot blocker', 'shield-wielding kid', 'fire shield for sale', 'fungus for fire shield', 'shield boy defends again', 'the Red Shield'), + 'Mirror Shield': (True, False, None, 0x06, 200, 'Now you can\ndefend against\nlasers!', 'and the laser blocker', 'shield-wielding kid', 'face shield for sale', 'fungus for face shield', 'shield boy defends again', 'the Mirror Shield'), 'Progressive Shield': (True, False, None, 0x5F, 50, 'have a better\nblocker in\nfront of you', 'and the new shield', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a shield'), - 'Bug Catching Net': (True, False, None, 0x21, 50, 'Let\'s catch\nsome bees and\nfaeries!', 'and the bee catcher', 'the bug-catching kid', 'stick web for sale', 'fungus for butterflies', 'wrong boy catches bees again', 'the bug net'), - 'Cane of Byrna': (True, False, None, 0x18, 50, 'Use this to\nbecome\ninvincible!', 'and the bad cane', 'the spark-making kid', 'spark stick for sale', 'spark-stick for trade', 'cane boy encircles again', 'the blue cane'), + 'Bug Catching Net': (True, False, None, 0x21, 50, 'Let\'s catch\nsome bees and\nfaeries!', 'and the bee catcher', 'the bug-catching kid', 'stick web for sale', 'fungus for butterflies', 'wrong boy catches bees again', 'the Bug Net'), + 'Cane of Byrna': (True, False, None, 0x18, 50, 'Use this to\nbecome\ninvincible!', 'and the bad cane', 'the spark-making kid', 'spark stick for sale', 'spark-stick for trade', 'cane boy encircles again', 'the Blue Cane'), 'Boss Heart Container': (False, False, None, 0x3E, 40, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'), 'Sanctuary Heart Container': (False, False, None, 0x3F, 50, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'), 'Piece of Heart': (False, False, None, 0x17, 10, 'Just a little\npiece of love!', 'and the broken heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart piece'), @@ -109,8 +110,8 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Green Clock': (False, True, None, 0x5D, 200, 'a lot of time', 'the emerald clock', 'the emerald-time kid', 'green time for sale', 'for emerald time', 'moment boy adjusts time again', 'a red clock'), 'Single RNG': (False, True, None, 0x62, 300, 'something you don\'t yet have', None, None, None, None, 'unknown boy somethings again', 'a new mystery'), 'Multi RNG': (False, True, None, 0x63, 100, 'something you may already have', None, None, None, None, 'unknown boy somethings again', 'a total mystery'), - 'Magic Upgrade (1/2)': (True, False, None, 0x4E, 50, 'Your magic\npower has been\ndoubled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'half magic'), # can be required to beat mothula in an open seed in very very rare circumstance - 'Magic Upgrade (1/4)': (True, False, None, 0x4F, 100, 'Your magic\npower has been\nquadrupled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'quarter magic'), # can be required to beat mothula in an open seed in very very rare circumstance + 'Magic Upgrade (1/2)': (True, False, None, 0x4E, 50, 'Your magic\npower has been\ndoubled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'Half magic'), # can be required to beat mothula in an open seed in very very rare circumstance + 'Magic Upgrade (1/4)': (True, False, None, 0x4F, 100, 'Your magic\npower has been\nquadrupled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'Quarter magic'), # can be required to beat mothula in an open seed in very very rare circumstance 'Small Key (Eastern Palace)': (False, False, 'SmallKey', 0xA2, 40, 'A small key to Armos Knights', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Eastern Palace'), 'Big Key (Eastern Palace)': (False, False, 'BigKey', 0x9D, 60, 'A big key to Armos Knights', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Eastern Palace'), 'Compass (Eastern Palace)': (False, True, 'Compass', 0x8D, 10, 'Now you can find the Armos Knights!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Eastern Palace'), @@ -161,7 +162,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Map (Turtle Rock)': (False, True, 'Map', 0x73, 20, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Turtle Rock'), 'Small Key (Ganons Tower)': (False, False, 'SmallKey', 0xAD, 40, 'A small key to the evil tower', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key to Ganon\'s Tower'), 'Big Key (Ganons Tower)': (False, False, 'BigKey', 0x92, 60, 'A big key to the evil tower', 'and the big key', 'the big-unlock kid', 'big key for sale', 'face key fungus', 'key boy opens chest again', 'a big key to Ganon\'s Tower'), - 'Compass (Ganons Tower)': (False, True, 'Compass', 0x82, 10, 'Now you can find Agahnim!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a comapss to Ganon\'s Tower'), + 'Compass (Ganons Tower)': (False, True, 'Compass', 0x82, 10, 'Now you can find Agahnim!', 'and the compass', 'the magnetic kid', 'compass for sale', 'magnetic fungus', 'compass boy finds boss again', 'a compass to Ganon\'s Tower'), 'Map (Ganons Tower)': (False, True, 'Map', 0x72, 10, 'A tightly folded map rests here', 'and the map', 'cartography kid', 'map for sale', 'a map to shrooms', 'map boy navigates again', 'a map to Ganon\'s Tower'), 'Small Key (Universal)': (False, True, None, 0xAF, 100, 'A small key for any door', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a small key'), 'Nothing': (False, False, None, 0x5A, 1, 'Some Hot Air', 'and the Nothing', 'the zen kid', 'outright theft', 'shroom theft', 'empty boy is bored again', 'nothing'), diff --git a/Main.py b/Main.py index 624f8107..23ef43d4 100644 --- a/Main.py +++ b/Main.py @@ -30,7 +30,7 @@ from Fill import sell_potions, sell_keys, balance_multiworld_progression, balanc from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops from Utils import output_path, parse_player_names -__version__ = '0.5.1.6-u' +__version__ = '0.5.1.7-u' from source.classes.BabelFish import BabelFish diff --git a/OWEdges.py b/OWEdges.py index fc307d8c..95c546ad 100644 --- a/OWEdges.py +++ b/OWEdges.py @@ -703,6 +703,7 @@ OWTileRegions = bidict({ 'East Death Mountain (Top East)': 0x05, 'Spiral Cave Ledge': 0x05, 'Mimic Cave Ledge': 0x05, + 'Spiral Mimic Ledge Extend': 0x05, 'Fairy Ascension Ledge': 0x05, 'Fairy Ascension Plateau': 0x05, 'East Death Mountain (Bottom Left)': 0x05, @@ -819,6 +820,7 @@ OWTileRegions = bidict({ 'Lake Hylia Central Island': 0x35, 'Lake Hylia Island': 0x35, 'Lake Hylia Water': 0x35, + 'Lake Hylia Water D': 0x35, 'Ice Cave Area': 0x37, @@ -991,7 +993,7 @@ OWTileGroups = { 0x42 ] ), - ("West Mountain", "Regular", "None"): ( + ("Mountain Entry", "Entrance", "None"): ( [ 0x03 ], @@ -1183,14 +1185,14 @@ OWTileGroups = { 0x6b ] ), - # ("Links", "Regular", "None"): ( - # [ - # 0x2c - # ], - # [ - # 0x6c - # ] - # ), + ("Links", "Regular", "None"): ( + [ + 0x2c + ], + [ + 0x6c + ] + ), ("Tree Line", "Regular", "None"): ( [ 0x2e @@ -1402,6 +1404,7 @@ OWExitTypes = { 'East Death Mountain Mimic Ledge Drop', 'Spiral Ledge Drop', 'Mimic Ledge Drop', + 'Spiral Mimic Ledge Drop', 'Fairy Ascension Ledge Drop', 'Fairy Ascension Plateau Ledge Drop', 'TR Pegs Ledge Drop', @@ -1514,6 +1517,10 @@ OWExitTypes = { 'Fairy Ascension Rocks (North)', 'DM Broken Bridge (West)', 'DM Broken Bridge (East)', + 'Spiral Mimic Bridge (West)', + 'Spiral Mimic Bridge (East)', + 'Spiral Ledge Approach', + 'Mimic Ledge Approach', 'Fairy Ascension Rocks (South)', 'Floating Island Bridge (West)', 'TR Pegs Ledge Entry', @@ -1578,6 +1585,7 @@ OWExitTypes = { 'Lake Hylia West Pier', 'Lake Hylia Northeast Water Drop', 'Lake Hylia East Pier', + 'Lake Hylia Water D Entry', 'Desert Pass Ladder (South)', 'Desert Pass Rocks (North)', 'Desert Pass Rocks (South)', @@ -1590,9 +1598,7 @@ OWExitTypes = { 'Skull Woods Forgotten Bush (West)', 'Skull Woods Forgotten Bush (East)', 'GT Entry Approach', - 'Dark Death Mountain Ladder (North)', 'GT Entry Leave', - 'Dark Death Mountain Ladder (South)', 'Bumper Cave Entrance Rock', 'Skull Woods Pass Bush Row (West)', 'Skull Woods Pass Bush Row (East)', @@ -1635,8 +1641,6 @@ OWExitTypes = { 'Ice Lake Northeast Pier', 'Ice Lake Northeast Pier Hop', 'Ice Lake Moat Water Entry', - 'Ice Palace Approach', - 'Ice Palace Leave', 'Bomber Corner Water Drop', 'Bomber Corner Pier' ], @@ -1756,6 +1760,7 @@ OWExitTypes = { 'Ice Lake Southeast Mirror Spot', 'Ice Lake Northeast Mirror Spot', 'Ice Palace Mirror Spot', + 'Ice Lake Moat Mirror Spot', 'Shopping Mall Mirror Spot', 'Swamp Nook Mirror Spot', 'Swamp Nook Southeast Mirror Spot', diff --git a/OverworldGlitchRules.py b/OverworldGlitchRules.py index 40c1efb9..cfe15841 100644 --- a/OverworldGlitchRules.py +++ b/OverworldGlitchRules.py @@ -150,6 +150,7 @@ def get_boots_clip_exits_lw(inverted = False): yield ('WDM DMD To River Bend Clip', 'East Death Mountain (Bottom Left)', 'River Bend Area') yield ('EDM DMD To River Bend Clip', 'East Death Mountain (Bottom)', 'River Bend Area') yield ('TR Pegs Ledge Clip', 'Death Mountain TR Pegs', 'Death Mountain TR Pegs Ledge') + yield ('TR Pegs Ledge Descent Clip', 'Death Mountain TR Pegs Ledge', 'Death Mountain TR Pegs') yield ('TR Pegs To EDM Clip', 'Death Mountain TR Pegs', 'East Death Mountain (Top East)') yield ('Zora DMD Clip', 'Death Mountain TR Pegs', 'Zora Waterfall Area') yield ('Mountain Entry To Ledge Clip', 'Mountain Entry Area', 'Mountain Entry Ledge') diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 0dd9223a..ed5e914b 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -5,7 +5,7 @@ from BaseClasses import OWEdge, WorldType, RegionType, Direction, Terrain, PolSl from Regions import mark_dark_world_regions, mark_light_world_regions from OWEdges import OWTileRegions, OWTileGroups, OWEdgeGroups, OWExitTypes, OpenStd, parallel_links, IsParallel -__version__ = '0.2.3.6-u' +__version__ = '0.2.6.0-u' def link_overworld(world, player): # setup mandatory connections @@ -67,8 +67,12 @@ def link_overworld(world, player): # move both sets if parallel == IsParallel.Yes and not (all(edge in orig_swaps for edge in map(getParallel, forward_set)) and all(edge in orig_swaps for edge in map(getParallel, back_set))): raise Exception('Cannot move a parallel edge without the other') - new_groups[(OpenStd.Open, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)][0].append(forward_set) - new_groups[(OpenStd.Open, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)][1].append(back_set) + new_mode = OpenStd.Open + if tuple((OpenStd.Open, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)) not in new_groups.keys(): + # when Links House tile is swapped, the DW edges need to get put into existing Standard group + new_mode = OpenStd.Standard + new_groups[(new_mode, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)][0].append(forward_set) + new_groups[(new_mode, WorldType((int(wrld) + 1) % 2), dir, terrain, parallel, count)][1].append(back_set) for edge in forward_set: swaps.remove(edge) for edge in back_set: @@ -416,6 +420,24 @@ def link_overworld(world, player): world.owflutespots[player] = new_spots connect_flutes(new_spots) + # update spoiler + s = list(map(lambda x: ' ' if x not in new_spots else 'F', [i for i in range(0x40)])) + text_output = tile_swap_spoiler_table.replace('s', '%s') % ( s[0x02], s[0x07], + s[0x00], s[0x03], s[0x05], + s[0x00], s[0x02],s[0x03], s[0x05], s[0x07], s[0x0a], s[0x0f], + s[0x0a], s[0x0f], + s[0x10],s[0x11],s[0x12],s[0x13],s[0x14],s[0x15],s[0x16],s[0x17], s[0x10],s[0x11],s[0x12],s[0x13],s[0x14],s[0x15],s[0x16],s[0x17], + s[0x18], s[0x1a],s[0x1b], s[0x1d],s[0x1e], + s[0x22], s[0x25], s[0x1a], s[0x1d], + s[0x28],s[0x29],s[0x2a],s[0x2b],s[0x2c],s[0x2d],s[0x2e],s[0x2f], s[0x18], s[0x1b], s[0x1e], + s[0x30], s[0x32],s[0x33],s[0x34],s[0x35], s[0x37], s[0x22], s[0x25], + s[0x3a],s[0x3b],s[0x3c], s[0x3f], + s[0x28],s[0x29],s[0x2a],s[0x2b],s[0x2c],s[0x2d],s[0x2e],s[0x2f], + s[0x32],s[0x33],s[0x34], s[0x37], + s[0x30], s[0x35], + s[0x3a],s[0x3b],s[0x3c], s[0x3f]) + world.spoiler.set_map('flute', text_output, new_spots, player) + def connect_custom(world, connected_edges, player): if hasattr(world, 'custom_overworld') and world.custom_overworld[player]: for edgename1, edgename2 in world.custom_overworld[player]: @@ -502,7 +524,7 @@ def shuffle_tiles(world, groups, result_list, player): exist_dw_regions.extend(dw_regions) # check whirlpool parity - valid_whirlpool_parity = world.owCrossed[player] not in ['none', 'grouped'] or len(OrderedDict.fromkeys(new_results[0] + [0x0f, 0x12, 0x15, 0x33, 0x35, 0x3f, 0x55, 0x7f])) % 2 == 0 + valid_whirlpool_parity = world.owCrossed[player] not in ['none', 'grouped'] or len([o for o in new_results[0] if o in [0x0f, 0x12, 0x15, 0x33, 0x35, 0x3f, 0x55, 0x7f]]) % 2 == 0 (exist_owids, exist_lw_regions, exist_dw_regions) = result_list exist_owids.extend(new_results[0]) @@ -817,7 +839,7 @@ def can_reach_smith(world, player): region = world.get_region(region_name, player) for exit in region.exits: if not found and exit.connected_region is not None: - if any(map(lambda i: i.name == 'Ocarina', world.precollected_items)) and exit.spot_type == 'Flute': + if any(map(lambda i: i.name in ['Ocarina', 'Ocarina (Activated)'], world.precollected_items)) and exit.spot_type == 'Flute': fluteregion = exit.connected_region for flutespot in fluteregion.exits: if flutespot.connected_region and flutespot.connected_region.name not in explored_regions: @@ -841,7 +863,11 @@ def can_reach_smith(world, player): found = False explored_regions = list() - explore_region('Links House') + if not world.is_bombshop_start(player): + start_region = 'Links House' + else: + start_region = 'Big Bomb Shop' + explore_region(start_region) if not found: if not invFlag: explore_region('Sanctuary') @@ -869,7 +895,7 @@ def build_sectors(world, player): if (any(r in unique_regions for r in explored_regions)): for s in range(len(sectors)): if (any(r in sectors[s] for r in explored_regions)): - sectors[s] = list(list(sectors[s]) + list(explored_regions)) + sectors[s] = list(dict.fromkeys(list(sectors[s]) + list(explored_regions))) break else: sectors.append(explored_regions) @@ -895,7 +921,7 @@ def build_sectors(world, player): if (any(r in unique_regions for r in explored_regions)): for s2 in range(len(sectors2)): if (any(r in sectors2[s2] for r in explored_regions)): - sectors2[s2] = list(sectors2[s2] + explored_regions) + sectors2[s2] = list(dict.fromkeys(sectors2[s2] + explored_regions)) break else: sectors2.append(explored_regions) @@ -918,7 +944,7 @@ def build_accessible_region_list(world, start_region, player, build_copy_world=F region = base_world.get_region(region_name, player) for exit in region.exits: if exit.connected_region is not None: - if any(map(lambda i: i.name == 'Ocarina', base_world.precollected_items)) and exit.spot_type == 'Flute': + if any(map(lambda i: i.name in ['Ocarina', 'Ocarina (Activated)'], base_world.precollected_items)) and exit.spot_type == 'Flute': fluteregion = exit.connected_region for flutespot in fluteregion.exits: if flutespot.connected_region and flutespot.connected_region.name not in explored_regions: @@ -948,17 +974,152 @@ def build_accessible_region_list(world, start_region, player, build_copy_world=F return explored_regions def validate_layout(world, player): - sectors = [[r for l in s for r in l] for s in world.owsectors[player]] - for sector in sectors: - entrances_present = False - for region_name in sector: - region = world.get_region(region_name, player) - if any(x.spot_type == 'Entrance' for x in region.exits): - entrances_present = True - break - if not entrances_present and not all(r in isolated_regions for r in sector): - return False + if world.accessibility[player] == 'beatable': + return True + + entrance_connectors = { + 'East Death Mountain (Bottom)': ['East Death Mountain (Top East)'], + 'Kakariko Suburb Area': ['Maze Race Ledge'], + 'Maze Race Ledge': ['Kakariko Suburb Area'], + 'Desert Area': ['Desert Ledge', 'Desert Palace Mouth'], + 'East Dark Death Mountain (Top)': ['Dark Death Mountain Floating Island'], + 'East Dark Death Mountain (Bottom)': ['East Dark Death Mountain (Top)'], + 'Turtle Rock Area': ['Dark Death Mountain Ledge', + 'Dark Death Mountain Isolated Ledge'], + 'Dark Death Mountain Ledge': ['Turtle Rock Area'], + 'Dark Death Mountain Isolated Ledge': ['Turtle Rock Area'] + } + sane_connectors = { + # guaranteed dungeon access + 'Skull Woods Forest': ['Skull Woods Forest (West)'], + 'Skull Woods Forest (West)': ['Skull Woods Forest'], + # guaranteed dropdown access + 'Graveyard Area': ['Sanctuary Area'], + 'Pyramid Area': ['Pyramid Exit Ledge'] + } + if not world.is_tile_swapped(0x0a, player): + if not world.is_tile_swapped(0x03, player): + entrance_connectors['Mountain Entry Entrance'] = ['West Death Mountain (Bottom)'] + entrance_connectors['Mountain Entry Ledge'] = ['West Death Mountain (Bottom)'] + entrance_connectors['West Death Mountain (Bottom)'] = ['Mountain Entry Ledge'] + else: + entrance_connectors['Mountain Entry Entrance'] = ['West Dark Death Mountain (Bottom)'] + entrance_connectors['Bumper Cave Entrance'] = ['Bumper Cave Ledge'] + else: + if not world.is_tile_swapped(0x03, player): + entrance_connectors['Bumper Cave Entrance'] = ['West Death Mountain (Bottom)'] + entrance_connectors['Bumper Cave Ledge'] = ['West Death Mountain (Bottom)'] + entrance_connectors['West Death Mountain (Bottom)'] = ['Bumper Cave Ledge'] + else: + entrance_connectors['Bumper Cave Entrance'] = ['West Dark Death Mountain (Bottom)'] + entrance_connectors['Mountain Entry Entrance'] = ['Mountain Entry Ledge'] + + from Main import copy_world + from Utils import stack_size3a + from EntranceShuffle import default_dungeon_connections, default_connector_connections, default_item_connections, default_shop_connections, default_drop_connections, default_dropexit_connections + + dungeon_entrances = list(zip(*default_dungeon_connections + [('Ganons Tower', '')]))[0] + connector_entrances = list(zip(*default_connector_connections))[0] + item_entrances = list(zip(*default_item_connections))[0] + shop_entrances = list(zip(*default_shop_connections))[0] + drop_entrances = list(zip(*default_drop_connections + default_dropexit_connections))[0] + + def explore_region(region_name, region=None): + if stack_size3a() > 500: + raise GenerationException(f'Infinite loop detected for "{region_name}" located at \'validate_layout\'') + + explored_regions.append(region_name) + if not region: + region = base_world.get_region(region_name, player) + for exit in region.exits: + if exit.connected_region is not None and exit.connected_region.name not in explored_regions \ + and exit.connected_region.type in [RegionType.LightWorld, RegionType.DarkWorld]: + explore_region(exit.connected_region.name, exit.connected_region) + if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'simple'] \ + and region_name in entrance_connectors: + for dest_region in entrance_connectors[region_name]: + if dest_region not in explored_regions: + explore_region(dest_region) + if world.shuffle[player] not in ['insanity'] and region_name in sane_connectors: + for dest_region in sane_connectors[region_name]: + if dest_region not in explored_regions: + explore_region(dest_region) + + for p in range(1, world.players + 1): + world.key_logic[p] = {} + base_world = copy_world(world) + world.key_logic = {} + explored_regions = list() + + if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull'] or not world.shufflelinks[player]: + if not world.is_bombshop_start(player): + start_region = 'Links House Area' + else: + start_region = 'Big Bomb Shop Area' + explore_region(start_region) + + if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'lite', 'lean'] and world.mode == 'inverted': + start_region = 'Dark Chapel Area' + explore_region(start_region) + + if not world.is_tile_swapped(0x30, player): + start_region = 'Desert Palace Teleporter Ledge' + else: + start_region = 'Misery Mire Teleporter Ledge' + explore_region(start_region) + + if not world.is_tile_swapped(0x1b, player): + start_region = 'Pyramid Area' + else: + start_region = 'Hyrule Castle Ledge' + explore_region(start_region) + + unreachable_regions = OrderedDict() + unreachable_count = -1 + while unreachable_count != len(unreachable_regions): + # find unreachable regions + unreachable_regions = {} + flat_sectors = [[r for l in s for r in l] for s in world.owsectors[player]] + for sector in flat_sectors: + for region_name in sector: + if region_name not in explored_regions and region_name not in isolated_regions: + region = base_world.get_region(region_name, player) + unreachable_regions[region_name] = region + + # loop thru unreachable regions to check if some can be excluded + unreachable_count = len(unreachable_regions) + for region_name in reversed(unreachable_regions): + # check if can be accessed flute + if unreachable_regions[region_name].type == RegionType.LightWorld: + owid = OWTileRegions[region_name] + if owid < 0x80 and any(f[1] == owid and region_name in f[0] for f in flute_data.values()): + if world.owFluteShuffle[player] != 'vanilla' or owid % 0x40 in [0x03, 0x16, 0x18, 0x2c, 0x2f, 0x3b, 0x3f]: + unreachable_regions.pop(region_name) + explore_region(region_name) + break + # check if entrances in region could be used to access region + if world.shuffle[player] != 'vanilla': + for entrance in [e for e in unreachable_regions[region_name].exits if e.spot_type == 'Entrance']: + if (entrance.name == 'Links House' and (world.mode == 'inverted' or not world.shufflelinks[player] or world.shuffle[player] in ['dungeonssimple', 'dungeonsfull', 'lite', 'lean'])) \ + or (entrance.name == 'Big Bomb Shop' and (world.mode != 'inverted' or not world.shufflelinks[player] or world.shuffle[player] in ['dungeonssimple', 'dungeonsfull', 'lite', 'lean'])) \ + or (entrance.name == 'Ganons Tower' and (world.mode != 'inverted' and not world.shuffle_ganon[player])) \ + or (entrance.name in ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)'] and world.shuffle[player] not in ['insanity']) \ + or entrance.name == 'Tavern North': + continue # these are fixed entrances and cannot be used for gaining access to region + if entrance.name not in drop_entrances \ + and ((entrance.name in dungeon_entrances and world.shuffle[player] not in ['dungeonssimple', 'simple', 'restricted']) \ + or (entrance.name in connector_entrances and world.shuffle[player] not in ['dungeonssimple', 'dungeonsfull', 'simple']) \ + or (entrance.name in item_entrances + (tuple() if world.shopsanity[player] else shop_entrances) and world.shuffle[player] not in ['dungeonssimple', 'dungeonsfull', 'lite', 'lean'])): + unreachable_regions.pop(region_name) + explore_region(region_name) + break + if unreachable_count != len(unreachable_regions): + break + + if len(unreachable_regions): + return False + return True test_connections = [ @@ -1069,6 +1230,7 @@ mandatory_connections = [# Intra-tile OW Connections ('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'), ('Lake Hylia West Pier', 'Lake Hylia Area'), ('Lake Hylia East Pier', 'Lake Hylia Northeast Bank'), + ('Lake Hylia Water D Entry', 'Lake Hylia Water'), #flippers ('Desert Pass Ledge Drop', 'Desert Pass Area'), ('Desert Pass Rocks (North)', 'Desert Pass Southeast'), #glove ('Desert Pass Rocks (South)', 'Desert Pass Area'), #glove @@ -1246,8 +1408,6 @@ ow_connections = { ], [ ('Spectacle Rock Leave', 'West Death Mountain (Top)'), ('Spectacle Rock Approach', 'Spectacle Rock Ledge'), - ('Dark Death Mountain Ladder (North)', 'West Dark Death Mountain (Bottom)'), - ('Dark Death Mountain Ladder (South)', 'West Dark Death Mountain (Top)'), ('West Dark Death Mountain (Top) Mirror Spot', 'West Dark Death Mountain (Top)'), ('Bubble Boy Mirror Spot', 'West Dark Death Mountain (Bottom)'), ('West Dark Death Mountain (Bottom) Mirror Spot', 'West Dark Death Mountain (Bottom)'), @@ -1268,6 +1428,11 @@ ow_connections = { ('Floating Island Bridge (East)', 'Death Mountain Floating Island'), ('East Death Mountain Mimic Ledge Drop', 'Mimic Cave Ledge'), ('Mimic Ledge Drop', 'East Death Mountain (Bottom)'), + ('Spiral Mimic Bridge (West)', 'Spiral Mimic Ledge Extend'), + ('Spiral Mimic Bridge (East)', 'Spiral Mimic Ledge Extend'), + ('Spiral Ledge Approach', 'Spiral Cave Ledge'), + ('Mimic Ledge Approach', 'Mimic Cave Ledge'), + ('Spiral Mimic Ledge Drop', 'Fairy Ascension Ledge'), ('East Dark Death Mountain (Top West) Mirror Spot', 'East Dark Death Mountain (Top)'), ('East Dark Death Mountain (Top East) Mirror Spot', 'East Dark Death Mountain (Top)'), ('TR Ledge (West) Mirror Spot', 'Dark Death Mountain Ledge'), @@ -1527,14 +1692,13 @@ ow_connections = { ('Lake Hylia Teleporter', 'Ice Palace Area') ], [ ('Lake Hylia Island Pier', 'Lake Hylia Island'), - ('Ice Palace Approach', 'Ice Palace Area'), - ('Ice Palace Leave', 'Ice Lake Moat'), ('Ice Lake Mirror Spot', 'Ice Lake Area'), ('Ice Lake Southwest Mirror Spot', 'Ice Lake Ledge (West)'), ('Ice Lake Southeast Mirror Spot', 'Ice Lake Ledge (East)'), ('Ice Lake Northeast Mirror Spot', 'Ice Lake Northeast Bank'), ('Ice Palace Mirror Spot', 'Ice Palace Area'), - ('Ice Palace Teleporter', 'Lake Hylia Central Island') + ('Ice Lake Moat Mirror Spot', 'Ice Lake Moat'), + ('Ice Palace Teleporter', 'Lake Hylia Water D') ]), 0x37: ([ ('Ice Cave Mirror Spot', 'Ice Cave Area') @@ -1719,14 +1883,26 @@ default_connections = [#('Lost Woods NW', 'Master Sword Meadow SC'), isolated_regions = [ 'Death Mountain Floating Island', - 'Mimic Cave Ledge' + 'Mimic Cave Ledge', + 'Spiral Mimic Ledge Extend', + 'Mountain Entry Ledge', + 'Maze Race Prize', + 'Maze Race Ledge', + 'Desert Ledge', + 'Desert Palace Entrance (North) Spot', + 'Desert Palace Mouth', + 'Dark Death Mountain Floating Island', + 'Dark Death Mountain Ledge', + 'Dark Death Mountain Isolated Ledge', + 'Bumper Cave Ledge', + 'Pyramid Exit Ledge' ] flute_data = { - #Slot LW Region DW Region OWID VRAM BG Y BG X Link Y Link X Cam Y Cam X Unk1 Unk2 IconY IconX AltY AltX + #Slot LW Region DW Region OWID VRAM BG Y BG X Link Y Link X Cam Y Cam X Unk1 Unk2 IconY IconX AltY AltX AltVRAM AltBGY AltBGX AltCamY AltCamX AltUnk1 AltUnk2 AltIconY AltIconX 0x09: (['Lost Woods East Area', 'Skull Woods Forest'], 0x00, 0x1042, 0x022e, 0x0202, 0x0290, 0x0288, 0x029b, 0x028f, 0xfff2, 0x000e, 0x0290, 0x0288, 0x0290, 0x0290), 0x02: (['Lumberjack Area', 'Dark Lumberjack Area'], 0x02, 0x059c, 0x00d6, 0x04e6, 0x0138, 0x0558, 0x0143, 0x0563, 0xfffa, 0xfffa, 0x0138, 0x0550), - 0x0b: (['West Death Mountain (Bottom)', 'West Dark Death Mountain (Bottom)'], 0x03, 0x1600, 0x02ca, 0x060e, 0x0328, 0x0678, 0x0337, 0x0683, 0xfff6, 0xfff2, 0x035b, 0x0680), + 0x0b: (['West Death Mountain (Bottom)', 'West Dark Death Mountain (Top)'], 0x03, 0x1600, 0x02ca, 0x060e, 0x0328, 0x0678, 0x0337, 0x0683, 0xfff6, 0xfff2, 0x035b, 0x0680, 0x0118, 0x0860, 0x05c0, 0x00b8, 0x07ec, 0x0127, 0x086b, 0xfff8, 0x0004, 0x0148, 0x0850), 0x0e: (['East Death Mountain (Bottom)', 'East Dark Death Mountain (Bottom)'], 0x05, 0x1860, 0x031e, 0x0d00, 0x0388, 0x0da8, 0x038d, 0x0d7d, 0x0000, 0x0000, 0x0388, 0x0da8), 0x07: (['Death Mountain TR Pegs', 'Turtle Rock Area'], 0x07, 0x0804, 0x0102, 0x0e1a, 0x0160, 0x0e90, 0x016f, 0x0e97, 0xfffe, 0x0006, 0x0160, 0x0f20), 0x0a: (['Mountain Entry Area', 'Bumper Cave Area'], 0x0a, 0x0180, 0x0220, 0x0406, 0x0280, 0x0488, 0x028f, 0x0493, 0x0000, 0xfffa, 0x0280, 0x0488), diff --git a/RELEASENOTES.md b/RELEASENOTES.md index eaa45e2b..50cf5282 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -15,6 +15,13 @@ CLI: ```--bombbag``` # Bug Fixes and Notes. +* 0.5.1.7 + * Baserom update + * Fix for Inverted Mode: Dark Lake Hylia shop defaults to selling a blue potion + * Fix for Ijwu's enemizer: Boss door in Thieves' Town no longer closes after the maiden hint if Blind is shuffled to Theives' Town in boss shuffle + crossed mode + * No logic now sets the AllowAccidentalMajorGlitches flag in the rom appropriately + * Houlihan room now exits wherever Link's House is shuffled to + * Rom fixes from Catobat and Codemann8. Thanks! * 0.5.1.6 * Rules fixes for TT (Boss and Cell) can now have TT Big Key if not otherwise required (boss shuffle + crossed dungeon) * BUg fix for money balancing diff --git a/Regions.py b/Regions.py index f1d372cf..98548c9d 100644 --- a/Regions.py +++ b/Regions.py @@ -17,8 +17,9 @@ def create_regions(world, player): create_lw_region(player, 'West Death Mountain (Bottom)', None, ['Old Man Cave (East)', 'Old Man House (Bottom)', 'Old Man House (Top)', 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', 'West Dark Death Mountain (Bottom) Mirror Spot', 'West Death Mountain Teleporter', 'West Death Mountain ES']), create_lw_region(player, 'East Death Mountain (Top West)', None, ['DM Hammer Bridge (West)', 'East Dark Death Mountain (Top West) Mirror Spot', 'East Death Mountain WN']), create_lw_region(player, 'East Death Mountain (Top East)', None, ['DM Hammer Bridge (East)', 'Floating Island Bridge (East)', 'East Death Mountain Spiral Ledge Drop', 'East Death Mountain Fairy Ledge Drop', 'East Death Mountain Mimic Ledge Drop', 'Paradox Cave (Top)', 'East Dark Death Mountain (Top East) Mirror Spot', 'East Death Mountain EN']), - create_lw_region(player, 'Spiral Cave Ledge', None, ['Spiral Ledge Drop', 'Spiral Cave', 'TR Ledge (West) Mirror Spot']), - create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Ledge Drop', 'Mimic Cave', 'TR Ledge (East) Mirror Spot']), + create_lw_region(player, 'Spiral Cave Ledge', None, ['Spiral Ledge Drop', 'Spiral Mimic Bridge (West)', 'Spiral Cave', 'TR Ledge (West) Mirror Spot']), + create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Ledge Drop', 'Spiral Mimic Bridge (East)', 'Mimic Cave', 'TR Ledge (East) Mirror Spot']), + create_lw_region(player, 'Spiral Mimic Ledge Extend', None, ['Spiral Ledge Approach', 'Mimic Ledge Approach', 'Spiral Mimic Ledge Drop']), create_lw_region(player, 'Fairy Ascension Ledge', None, ['Fairy Ascension Ledge Drop', 'Fairy Ascension Cave (Top)', 'TR Isolated Mirror Spot']), create_lw_region(player, 'Fairy Ascension Plateau', None, ['Fairy Ascension Rocks (North)', 'Fairy Ascension Plateau Ledge Drop', 'Fairy Ascension Cave (Bottom)', 'East Dark Death Mountain (Bottom Plateau) Mirror Spot']), create_lw_region(player, 'East Death Mountain (Bottom Left)', None, ['DM Broken Bridge (West)', 'East Dark Death Mountain (Bottom Left) Mirror Spot', 'East Death Mountain WS']), @@ -111,6 +112,7 @@ def create_regions(world, player): create_lw_region(player, 'Lake Hylia Central Island', None, ['Lake Hylia Central Water Drop', 'Capacity Upgrade', 'Ice Palace Mirror Spot', 'Lake Hylia Teleporter']), create_lw_region(player, 'Lake Hylia Island', ['Lake Hylia Island'], ['Lake Hylia Island Water Drop']), create_lw_region(player, 'Lake Hylia Water', None, ['Lake Hylia Central Island Pier', 'Lake Hylia Island Pier', 'Lake Hylia West Pier', 'Lake Hylia East Pier', 'Lake Hylia NC', 'Lake Hylia EC', 'Lake Hylia Whirlpool'], Terrain.Water), + create_lw_region(player, 'Lake Hylia Water D', None, ['Lake Hylia Water D Entry', 'Ice Lake Moat Mirror Spot'], Terrain.Water), create_lw_region(player, 'Ice Cave Area', None, ['Ice Rod Cave', 'Good Bee Cave', '20 Rupee Cave', 'Shopping Mall Mirror Spot', 'Ice Cave SE', 'Ice Cave SW']), create_lw_region(player, 'Desert Pass Area', ['Middle Aged Man'], ['Desert Pass Ladder (South)', 'Middle Aged Man', 'Desert Fairy', '50 Rupee Cave', 'Swamp Nook Mirror Spot', 'Desert Pass WS', 'Desert Pass EC', 'Desert Pass Rocks (North)']), create_lw_region(player, 'Middle Aged Man', ['Purple Chest'], None), @@ -129,9 +131,9 @@ def create_regions(world, player): create_dw_region(player, 'Skull Woods Forgotten Path (Southwest)', None, ['Skull Woods Forgotten Bush (West)', 'Lost Woods Southwest Mirror Spot', 'Skull Woods SW']), create_dw_region(player, 'Skull Woods Forgotten Path (Northeast)', None, ['Skull Woods Forgotten Bush (East)', 'Lost Woods East (Forgotten) Mirror Spot', 'Lost Woods West (Forgotten) Mirror Spot', 'Skull Woods EN']), create_dw_region(player, 'Dark Lumberjack Area', None, ['Dark World Lumberjack Shop', 'Lumberjack Mirror Spot', 'Dark Lumberjack WN', 'Dark Lumberjack SW']), - create_dw_region(player, 'West Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (West)', 'GT Entry Approach', 'Dark Death Mountain Ladder (North)', 'West Death Mountain (Top) Mirror Spot', 'West Dark Death Mountain EN']), + create_dw_region(player, 'West Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (West)', 'GT Entry Approach', 'West Death Mountain (Top) Mirror Spot', 'West Dark Death Mountain EN']), create_dw_region(player, 'GT Approach', None, ['GT Entry Leave', 'Ganons Tower']), - create_dw_region(player, 'West Dark Death Mountain (Bottom)', None, ['Dark Death Mountain Ladder (South)', 'Spike Cave', 'Dark Death Mountain Fairy', 'Dark Death Mountain Teleporter (West)', 'Spectacle Rock Mirror Spot', 'West Dark Death Mountain ES']), + create_dw_region(player, 'West Dark Death Mountain (Bottom)', None, ['Spike Cave', 'Dark Death Mountain Fairy', 'Dark Death Mountain Teleporter (West)', 'Spectacle Rock Mirror Spot', 'West Dark Death Mountain ES']), create_dw_region(player, 'East Dark Death Mountain (Top)', None, ['Dark Death Mountain Drop (East)', 'Superbunny Cave (Top)', 'Hookshot Cave', 'East Death Mountain (Top West) Mirror Spot', 'East Death Mountain (Top East) Mirror Spot', 'East Dark Death Mountain WN', 'East Dark Death Mountain EN']), create_dw_region(player, 'East Dark Death Mountain (Bottom)', None, ['Superbunny Cave (Bottom)', 'Cave Shop (Dark Death Mountain)', 'Dark Death Mountain Teleporter (East)', 'Fairy Ascension Mirror Spot']), create_dw_region(player, 'East Dark Death Mountain (Bottom Left)', None, ['Death Mountain Bridge Mirror Spot', 'East Dark Death Mountain WS']), @@ -211,8 +213,8 @@ def create_regions(world, player): create_dw_region(player, 'Ice Lake Ledge (West)', None, ['Ice Lake Southwest Water Drop', 'South Shore Mirror Spot', 'Ice Lake WS']), create_dw_region(player, 'Ice Lake Ledge (East)', None, ['Ice Lake Southeast Water Drop', 'South Shore East Mirror Spot', 'Ice Lake ES']), create_dw_region(player, 'Ice Lake Water', None, ['Ice Lake Northeast Pier', 'Ice Lake Moat Bomb Jump', 'Lake Hylia Island Mirror Spot', 'Ice Lake NC', 'Ice Lake EC'], Terrain.Water), - create_dw_region(player, 'Ice Lake Moat', None, ['Ice Lake Moat Water Entry', 'Ice Lake Northeast Pier Hop', 'Ice Palace Approach', 'Lake Hylia Water Mirror Spot']), - create_dw_region(player, 'Ice Palace Area', None, ['Ice Palace Leave', 'Ice Palace', 'Ice Palace Teleporter', 'Lake Hylia Central Island Mirror Spot']), + create_dw_region(player, 'Ice Lake Moat', None, ['Ice Lake Moat Water Entry', 'Ice Lake Northeast Pier Hop', 'Lake Hylia Water Mirror Spot']), + create_dw_region(player, 'Ice Palace Area', None, ['Ice Palace', 'Ice Palace Teleporter', 'Lake Hylia Central Island Mirror Spot']), create_dw_region(player, 'Shopping Mall Area', None, ['Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Spike Cave', 'Ice Cave Mirror Spot', 'Shopping Mall SW', 'Shopping Mall SE']), create_dw_region(player, 'Swamp Nook Area', None, ['Desert Pass Ledge Mirror Spot', 'Desert Pass Mirror Spot', 'Swamp Nook EC', 'Swamp Nook ES']), create_dw_region(player, 'Swamp Area', None, ['Swamp Palace', 'Dam Mirror Spot', 'Swamp WC', 'Swamp WS', 'Swamp NC', 'Swamp EC']), @@ -326,7 +328,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', @@ -1125,8 +1127,8 @@ def create_shops(world, player): if (0x35 not in world.owswaps[player][0] and region_name == 'Dark Lake Hylia Shop') \ or (0x35 in world.owswaps[player][0] and region_name == 'Cave Shop (Lake Hylia)'): locked = True - custom = True inventory = [('Blue Potion', 160), ('Blue Shield', 50), ('Bombs (10)', 50)] + custom = True region = world.get_region(region_name, player) shop = Shop(region, room_id, type, shopkeeper, custom, locked, sram) region.shop = shop @@ -1240,8 +1242,8 @@ key_drop_data = { 'Swamp Palace - Waterway Pot Key': [0x140009, 0x14000a, 'in Swamp Palace', 'Small Key (Swamp Palace)'], 'Skull Woods - West Lobby Pot Key': [0x14002d, 0x14002e, 'in Skull Woods', 'Small Key (Skull Woods)'], 'Skull Woods - Spike Corner Key Drop': [0x14001b, 0x14001c, 'near Mothula', 'Small Key (Skull Woods)'], - "Thieves' Town - Hallway Pot Key": [0x14005d, 0x14005e, "in Thieves' Town", 'Small Key (Thieves Town)'], - "Thieves' Town - Spike Switch Pot Key": [0x14004e, 0x14004f, "in Thieves' Town", 'Small Key (Thieves Town)'], + "Thieves' Town - Hallway Pot Key": [0x14005d, 0x14005e, "in Thieves Town", 'Small Key (Thieves Town)'], + "Thieves' Town - Spike Switch Pot Key": [0x14004e, 0x14004f, "in Thieves Town", 'Small Key (Thieves Town)'], 'Ice Palace - Jelly Key Drop': [0x140003, 0x140004, 'in Ice Palace', 'Small Key (Ice Palace)'], 'Ice Palace - Conveyor Key Drop': [0x140021, 0x140022, 'in Ice Palace', 'Small Key (Ice Palace)'], 'Ice Palace - Hammer Block Key Drop': [0x140024, 0x140025, 'in Ice Palace', 'Small Key (Ice Palace)'], @@ -1304,10 +1306,10 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), 'Kakariko Well - Middle': (0xea94, 0x186268, False, 'in a well'), 'Kakariko Well - Right': (0xea97, 0x18626b, False, 'in a well'), 'Kakariko Well - Bottom': (0xea9a, 0x18626e, False, 'in a well'), - 'Blacksmith': (0x18002a, 0x186366, False, 'with the smith'), - 'Magic Bat': (0x180015, 0x18635e, False, 'with the bat'), + 'Blacksmith': (0x18002a, 0x186366, False, 'with the Smith'), + 'Magic Bat': (0x180015, 0x18635e, False, 'with the Bat'), 'Sick Kid': (0x339cf, 0x186367, False, 'with the sick'), - 'Hobo': (0x33e7d, 0x186368, False, 'with the hobo'), + 'Hobo': (0x33e7d, 0x186368, False, 'with the Hobo'), 'Lost Woods Hideout': (0x180000, 0x186348, False, 'near a thief'), 'Lumberjack Tree': (0x180001, 0x186349, False, 'in a hole'), 'Cave 45': (0x180003, 0x18634b, False, 'alone in a cave'), @@ -1337,7 +1339,7 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), 'Eastern Palace - Big Key Chest': (0xe9b9, 0x18618d, False, 'in Eastern Palace'), 'Eastern Palace - Map Chest': (0xe9f5, 0x1861c9, False, 'in Eastern Palace'), 'Eastern Palace - Boss': (0x180150, 0x18633e, False, 'with the Armos'), - 'Master Sword Pedestal': (0x289b0, 0x186369, False, 'at the pedestal'), + 'Master Sword Pedestal': (0x289b0, 0x186369, False, 'at the Pedestal'), 'Hyrule Castle - Boomerang Chest': (0xe974, 0x186148, False, 'in Hyrule Castle'), 'Hyrule Castle - Map Chest': (0xeb0c, 0x1862e0, False, 'in Hyrule Castle'), "Hyrule Castle - Zelda's Chest": (0xeb09, 0x1862dd, False, 'in Hyrule Castle'), @@ -1348,7 +1350,7 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), 'Sanctuary': (0xea79, 0x18624d, False, 'in Sanctuary'), 'Castle Tower - Room 03': (0xeab5, 0x186289, False, 'in Castle Tower'), 'Castle Tower - Dark Maze': (0xeab2, 0x186286, False, 'in Castle Tower'), - 'Old Man': (0xf69fa, 0x186364, False, 'with the old man'), + 'Old Man': (0xf69fa, 0x186364, False, 'with the Old Man'), 'Spectacle Rock Cave': (0x180002, 0x18634a, False, 'alone in a cave'), 'Paradox Cave Lower - Far Left': (0xeb2a, 0x1862fe, False, 'in a cave with seven chests'), 'Paradox Cave Lower - Left': (0xeb2d, 0x186301, False, 'in a cave with seven chests'), @@ -1357,7 +1359,7 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), 'Paradox Cave Lower - Middle': (0xeb36, 0x18630a, False, 'in a cave with seven chests'), 'Paradox Cave Upper - Left': (0xeb39, 0x18630d, False, 'in a cave with seven chests'), 'Paradox Cave Upper - Right': (0xeb3c, 0x186310, False, 'in a cave with seven chests'), - 'Spiral Cave': (0xe9bf, 0x186193, False, 'in spiral cave'), + 'Spiral Cave': (0xe9bf, 0x186193, False, 'in Spiral Cave'), 'Ether Tablet': (0x180016, 0x18633b, False, 'at a monolith'), 'Spectacle Rock': (0x180140, 0x18634f, False, 'atop a rock'), 'Tower of Hera - Basement Cage': (0x180162, 0x18633a, False, 'in Tower of Hera'), @@ -1366,9 +1368,9 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), 'Tower of Hera - Compass Chest': (0xe9fb, 0x1861cf, False, 'in Tower of Hera'), 'Tower of Hera - Big Chest': (0xe9f8, 0x1861cc, False, 'in Tower of Hera'), 'Tower of Hera - Boss': (0x180152, 0x186340, False, 'with Moldorm'), - 'Pyramid': (0x180147, 0x186356, False, 'on the pyramid'), + 'Pyramid': (0x180147, 0x186356, False, 'on the Pyramid'), 'Catfish': (0xee185, 0x186361, False, 'with a catfish'), - 'Stumpy': (0x330c7, 0x18636a, False, 'with tree boy'), + 'Stumpy': (0x330c7, 0x18636a, False, 'with Tree Boy'), 'Digging Game': (0x180148, 0x186357, False, 'underground'), 'Bombos Tablet': (0x180017, 0x18633c, False, 'at a monolith'), 'Hype Cave - Top': (0xeb1e, 0x1862f2, False, 'near a bat-like man'), @@ -1404,13 +1406,13 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), 'Swamp Palace - Flooded Room - Right': (0xeaac, 0x186280, False, 'in Swamp Palace'), 'Swamp Palace - Waterfall Room': (0xeaaf, 0x186283, False, 'in Swamp Palace'), 'Swamp Palace - Boss': (0x180154, 0x186342, False, 'with Arrghus'), - "Thieves' Town - Big Key Chest": (0xea04, 0x1861d8, False, "in Thieves' Town"), - "Thieves' Town - Map Chest": (0xea01, 0x1861d5, False, "in Thieves' Town"), - "Thieves' Town - Compass Chest": (0xea07, 0x1861db, False, "in Thieves' Town"), - "Thieves' Town - Ambush Chest": (0xea0a, 0x1861de, False, "in Thieves' Town"), - "Thieves' Town - Attic": (0xea0d, 0x1861e1, False, "in Thieves' Town"), - "Thieves' Town - Big Chest": (0xea10, 0x1861e4, False, "in Thieves' Town"), - "Thieves' Town - Blind's Cell": (0xea13, 0x1861e7, False, "in Thieves' Town"), + "Thieves' Town - Big Key Chest": (0xea04, 0x1861d8, False, "in Thieves Town"), + "Thieves' Town - Map Chest": (0xea01, 0x1861d5, False, "in Thieves Town"), + "Thieves' Town - Compass Chest": (0xea07, 0x1861db, False, "in Thieves Town"), + "Thieves' Town - Ambush Chest": (0xea0a, 0x1861de, False, "in Thieves Town"), + "Thieves' Town - Attic": (0xea0d, 0x1861e1, False, "in Thieves Town"), + "Thieves' Town - Big Chest": (0xea10, 0x1861e4, False, "in Thieves Town"), + "Thieves' Town - Blind's Cell": (0xea13, 0x1861e7, False, "in Thieves Town"), "Thieves' Town - Boss": (0x180156, 0x186344, False, 'with Blind'), 'Skull Woods - Compass Chest': (0xe992, 0x186166, False, 'in Skull Woods'), 'Skull Woods - Map Chest': (0xe99b, 0x18616f, False, 'in Skull Woods'), @@ -1513,7 +1515,7 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), 'Tower of Hera - Prize': ([0x120A5, 0x53F0A, 0x53F0B, 0x18005A, 0x18007A, 0xC706], None, True, 'Tower of Hera'), 'Palace of Darkness - Prize': ([0x120A1, 0x53F00, 0x53F01, 0x180056, 0x18007D, 0xC702], None, True, 'Palace of Darkness'), 'Swamp Palace - Prize': ([0x120A0, 0x53F6C, 0x53F6D, 0x180055, 0x180071, 0xC701], None, True, 'Swamp Palace'), - 'Thieves\' Town - Prize': ([0x120A6, 0x53F36, 0x53F37, 0x18005B, 0x180077, 0xC707], None, True, 'Thieves\' Town'), + 'Thieves\' Town - Prize': ([0x120A6, 0x53F36, 0x53F37, 0x18005B, 0x180077, 0xC707], None, True, 'Thieves Town'), 'Skull Woods - Prize': ([0x120A3, 0x53F12, 0x53F13, 0x180058, 0x18007B, 0xC704], None, True, 'Skull Woods'), 'Ice Palace - Prize': ([0x120A4, 0x53F5A, 0x53F5B, 0x180059, 0x180073, 0xC705], None, True, 'Ice Palace'), 'Misery Mire - Prize': ([0x120A2, 0x53F48, 0x53F49, 0x180057, 0x180075, 0xC703], None, True, 'Misery Mire'), diff --git a/Rom.py b/Rom.py index 3aeb31d1..dd304e6d 100644 --- a/Rom.py +++ b/Rom.py @@ -33,7 +33,7 @@ from source.classes.SFX import randomize_sfx JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '9bd966f2eabdfdaecf54c59d3921f470' +RANDOMIZERBASEHASH = '4fe27f1d5b874373e48233b7dffe4a6a' class JsonRom(object): @@ -349,6 +349,7 @@ def patch_enemizer(world, player, rom, local_rom, enemizercli, random_sprite_on_ rom.write_bytes(0xEA081, [0x5c, 0x00, 0x80, 0xb7, 0xc9, 0x6, 0xf0, 0x24, 0xad, 0x3, 0x4, 0x29, 0x20, 0xf0, 0x1d]) rom.write_byte(0x200101, 0) # Do not close boss room door on entry. + rom.write_byte(0x1B0101, 0) # Do not close boss room door on entry. (for Ijwu's enemizer) if random_sprite_on_hit: _populate_sprite_table() @@ -643,21 +644,33 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): offset = 0x40 write_int16(rom, snes_to_pc(0x02E849 + (o * 2)), data[1] + offset) # owid - write_int16(rom, snes_to_pc(0x02E86B + (o * 2)), data[2]) #vram - write_int16(rom, snes_to_pc(0x02E88D + (o * 2)), data[3]) # BG scroll Y - write_int16(rom, snes_to_pc(0x02E8AF + (o * 2)), data[4]) # BG scroll X write_int16(rom, snes_to_pc(0x02E8D1 + (o * 2)), data[13] if offset > 0 and len(data) > 13 else data[5]) # link Y write_int16(rom, snes_to_pc(0x02E8F3 + (o * 2)), data[14] if offset > 0 and len(data) > 13 else data[6]) # link X - write_int16(rom, snes_to_pc(0x02E915 + (o * 2)), data[7]) # cam Y - write_int16(rom, snes_to_pc(0x02E937 + (o * 2)), data[8]) # cam X - write_int16(rom, snes_to_pc(0x02E959 + (o * 2)), data[9]) # unknown 1 - write_int16(rom, snes_to_pc(0x02E97B + (o * 2)), data[10]) # unknown 2 - - # flute menu blips - rom.write_byte(snes_to_pc(0x0AB783 + o), data[12] & 0xff) # X low byte - rom.write_byte(snes_to_pc(0x0AB78B + o), data[12] // 0x100) # X high byte - rom.write_byte(snes_to_pc(0x0AB793 + o), data[11] & 0xff) # Y low byte - rom.write_byte(snes_to_pc(0x0AB79B + o), data[11] // 0x100) # Y high byte + + if offset == 0 or len(data) <= 15: + write_int16(rom, snes_to_pc(0x02E86B + (o * 2)), data[2]) # vram + write_int16(rom, snes_to_pc(0x02E88D + (o * 2)), data[3]) # BG scroll Y + write_int16(rom, snes_to_pc(0x02E8AF + (o * 2)), data[4]) # BG scroll X + write_int16(rom, snes_to_pc(0x02E915 + (o * 2)), data[7]) # cam Y + write_int16(rom, snes_to_pc(0x02E937 + (o * 2)), data[8]) # cam X + write_int16(rom, snes_to_pc(0x02E959 + (o * 2)), data[9]) # unknown 1 + write_int16(rom, snes_to_pc(0x02E97B + (o * 2)), data[10]) # unknown 2 + rom.write_byte(snes_to_pc(0x0AB783 + o), data[12] & 0xff) # flute menu blip - X low byte + rom.write_byte(snes_to_pc(0x0AB78B + o), data[12] // 0x100) # flute menu blip - X high byte + rom.write_byte(snes_to_pc(0x0AB793 + o), data[11] & 0xff) # flute menu blip - Y low byte + rom.write_byte(snes_to_pc(0x0AB79B + o), data[11] // 0x100) # flute menu blip - Y high byte + else: # use alternate flute data + write_int16(rom, snes_to_pc(0x02E86B + (o * 2)), data[15]) # vram + write_int16(rom, snes_to_pc(0x02E88D + (o * 2)), data[16]) # BG scroll Y + write_int16(rom, snes_to_pc(0x02E8AF + (o * 2)), data[17]) # BG scroll X + write_int16(rom, snes_to_pc(0x02E915 + (o * 2)), data[18]) # cam Y + write_int16(rom, snes_to_pc(0x02E937 + (o * 2)), data[19]) # cam X + write_int16(rom, snes_to_pc(0x02E959 + (o * 2)), data[20]) # unknown 1 + write_int16(rom, snes_to_pc(0x02E97B + (o * 2)), data[21]) # unknown 2 + rom.write_byte(snes_to_pc(0x0AB783 + o), data[23] & 0xff) # flute menu blip - X low byte + rom.write_byte(snes_to_pc(0x0AB78B + o), data[23] // 0x100) # flute menu blip - X high byte + rom.write_byte(snes_to_pc(0x0AB793 + o), data[22] & 0xff) # flute menu blip - Y low byte + rom.write_byte(snes_to_pc(0x0AB79B + o), data[22] // 0x100) # flute menu blip - Y high byte # patch whirlpools if world.owWhirlpoolShuffle[player]: @@ -814,10 +827,13 @@ 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_bombshop_start(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]) + rom.write_bytes(0x13fff0, [0x04 if lh_name == 'Links House' else 0x1C, 0x01]) old_man_house = world.get_region('Old Man House', player) if should_be_bunny(old_man_house, world.mode[player]): @@ -876,7 +892,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x138006, 1) # swap in non-ER Lobby Shuffle Inverted - but only then - if world.mode[player] == 'inverted' and world.intensity[player] >= 3 and world.doorShuffle[player] != 'vanilla' and world.shuffle[player] == 'vanilla': + if world.is_atgt_swapped(player) and world.intensity[player] >= 3 and world.doorShuffle[player] != 'vanilla' and world.shuffle[player] == 'vanilla': aga_portal = world.get_portal('Agahnims Tower', player) gt_portal = world.get_portal('Ganons Tower', player) aga_portal.exit_offset, gt_portal.exit_offset = gt_portal.exit_offset, aga_portal.exit_offset @@ -1385,10 +1401,17 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): gametype |= 0x01 # enemizer rom.write_byte(0x180211, gametype) # Game type + warningflags = 0x00 # none + if world.logic[player] in ['owglitches', 'nologic']: + warningflags |= 0x20 + if world.logic[player] in ['minorglitches', 'owglitches', 'nologic']: + warningflags |= 0x40 + rom.write_byte(0x180212, warningflags) # Warning flags + # assorted fixes rom.write_byte(0x1800A2, 0x01 if world.fix_fake_world else 0x00) # remain in real dark world when dying in dark world dungeon before killing aga1 rom.write_byte(0x180169, 0x01 if world.lock_aga_door_in_escape else 0x00) # Lock or unlock aga tower door during escape sequence. - if world.mode[player] == 'inverted': + if world.is_atgt_swapped(player): rom.write_byte(0x180169, 0x02) # lock aga/ganon tower door with crystals in inverted rom.write_byte(0x180171, 0x01 if world.ganon_at_pyramid[player] else 0x00) # Enable respawning on pyramid after ganon death rom.write_byte(0x180173, 0x01) # Bob is enabled @@ -1405,6 +1428,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_bytes(0xE9A5, [0x7E, 0x00, 0x24]) # disable below ganon chest rom.write_byte(0x18008B, 0x01 if world.open_pyramid[player] or world.goal[player] == 'trinity' else 0x00) # pre-open Pyramid Hole rom.write_byte(0x18008C, 0x01 if world.crystals_needed_for_gt[player] == 0 else 0x00) # GT pre-opened if crystal requirement is 0 + rom.write_byte(0x18008F, 0x01 if world.is_atgt_swapped(player) else 0x00) # AT/GT swapped rom.write_byte(0xF5D73, 0xF0) # bees are catchable rom.write_byte(0xF5F10, 0xF0) # bees are catchable rom.write_byte(0x180086, 0x00 if world.aga_randomness else 0x01) # set blue ball and ganon warp randomness @@ -1413,6 +1437,10 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x180174, 0x01 if world.fix_fake_world[player] else 0x00) rom.write_byte(0x18017E, 0x01) # Fairy fountains only trade in bottles + # starting overworld flags + assert len(world.initial_overworld_flags[player]) == 0x80 and all([i < 0x100 for i in world.initial_overworld_flags[player]]) + rom.write_bytes(0x183280, world.initial_overworld_flags[player]) + # Starting equipment if world.pseudoboots[player]: rom.write_byte(0x18008E, 0x01) @@ -1582,9 +1610,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x180043, equip[0x359]) assert equip[:0x340] == [0] * 0x340 - rom.write_bytes(0x183000, equip[0x340:]) + rom.write_bytes(0x183340, equip[0x340:]) rom.write_bytes(0x271A6, equip[0x340:0x340+60]) - + rom.write_byte(0x18004A, 0x00 if world.mode[player] != 'inverted' else 0x01) # Inverted mode rom.write_byte(0x18005D, 0x00) # Hammer always breaks barrier rom.write_byte(0x03A943, 0xD0 if world.mode[player] != 'inverted' else 0xF0) # Mirror: Normal (D0=Dark to Light, F0=light to dark, 42 = both) @@ -1730,6 +1758,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x1800A3, 0x01) # enable correct world setting behaviour after agahnim kills rom.write_byte(0x1800A4, 0x01 if world.logic[player] != 'nologic' else 0x00) # enable POD EG fix rom.write_byte(0x180042, 0x01 if world.save_and_quit_from_boss else 0x00) # Allow Save and Quit after boss kill + rom.write_byte(0x180358, 0x01 if world.logic[player] == 'nologic' else 0x00) # remove shield from uncle rom.write_bytes(0x6D253, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E]) @@ -1825,7 +1854,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): from Main import __version__ seedstring = f'{world.seed:09}' if isinstance(world.seed, int) else world.seed # todo: change to DR when Enemizer is okay with DR - rom.name = bytearray(f'ER{__version__.split("-")[0].replace(".","")[0:3]}_{team+1}_{player}_{seedstring}\0', 'utf8')[:21] + rom.name = bytearray(f'ER{__version__.split("-")[0].replace(".","")[0:3]}_{team+1}_{player}_{seedstring}O\0', 'utf8')[:21] rom.name.extend([0] * (21 - len(rom.name))) rom.write_bytes(0x7FC0, rom.name) @@ -2309,7 +2338,7 @@ def write_strings(rom, world, player, team): entrances_to_hint = {} entrances_to_hint.update(InconvenientDungeonEntrances) if world.shuffle_ganon: - if world.mode[player] == 'inverted': + if world.is_atgt_swapped(player): entrances_to_hint.update({'Agahnims Tower': 'The sealed castle door'}) else: entrances_to_hint.update({'Ganons Tower': 'Ganon\'s Tower'}) @@ -2342,7 +2371,7 @@ def write_strings(rom, world, player, team): if world.shuffle[player] not in ['simple', 'restricted', 'restricted_legacy']: entrances_to_hint.update(ConnectorEntrances) entrances_to_hint.update(DungeonEntrances) - if world.mode[player] == 'inverted': + if world.is_atgt_swapped(player): entrances_to_hint.update({'Ganons Tower': 'The dark mountain tower'}) else: entrances_to_hint.update({'Agahnims Tower': 'The sealed castle door'}) @@ -2355,14 +2384,10 @@ def write_strings(rom, world, player, team): elif world.shopsanity[player]: entrances_to_hint.update(ShopEntrances) if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull']: - if world.is_tile_swapped(0x2c, player): - entrances_to_hint.update({'Links House': 'The hero\'s old residence'}) - if world.shufflelinks[player]: - entrances_to_hint.update({'Big Bomb Shop': 'The old bomb shop'}) - else: + if not world.is_bombshop_start(player): entrances_to_hint.update({'Big Bomb Shop': 'The old bomb shop'}) - if world.shufflelinks[player]: - entrances_to_hint.update({'Links House': 'The hero\'s old residence'}) + else: + entrances_to_hint.update({'Links House': 'The hero\'s old residence'}) entrances_to_hint.update({'Dark Sanctuary Hint': 'The dark sanctuary cave'}) if world.shuffle[player] in ['insanity', 'madness_legacy', 'insanity_legacy']: entrances_to_hint.update(InsanityEntrances) @@ -2438,13 +2463,19 @@ def write_strings(rom, world, player, team): # Lastly we write hints to show where certain interesting items are. It is done the way it is to re-use the silver code and also to give one hint per each type of item regardless of how many exist. This supports many settings well. items_to_hint = RelevantItems.copy() + flute_item = 'Ocarina' + if world.is_tile_swapped(0x18, player): + items_to_hint.remove(flute_item) + flute_item = 'Ocarina (Activated)' + items_to_hint.append(flute_item) if world.owShuffle[player] != 'vanilla' or world.owMixed[player]: # Adding a guaranteed hint for the Flute in overworld shuffle. - this_location = world.find_items_not_key_only('Ocarina', player) + this_location = world.find_items_not_key_only(flute_item, player) if this_location: this_hint = this_location[0].item.hint_text + ' can be found ' + hint_text(this_location[0]) + '.' + this_hint = this_hint.capitalize() tt[hint_locations.pop(0)] = this_hint - items_to_hint.remove('Ocarina') + items_to_hint.remove(flute_item) if world.keyshuffle[player]: items_to_hint.extend(SmallKeys) if world.bigkeyshuffle[player]: @@ -2459,6 +2490,7 @@ def write_strings(rom, world, player, team): random.shuffle(this_location) if this_location: this_hint = this_location[0].item.hint_text + ' can be found ' + hint_text(this_location[0]) + '.' + this_hint = this_hint.capitalize() tt[hint_locations.pop(0)] = this_hint hint_count -= 1 @@ -2615,8 +2647,8 @@ def write_strings(rom, world, player, team): # inverted spawn menu changes if world.mode[player] == 'inverted': - tt['menu_start_2'] = "{MENU}\n{SPEED0}\n≥@'s house\n Dark Chapel\n{CHOICE3}" - tt['menu_start_3'] = "{MENU}\n{SPEED0}\n≥@'s house\n Dark Chapel\n Mountain Cave\n{CHOICE2}" + tt['menu_start_2'] = "{MENU}\n{SPEED0}\n≥@'s House\n Dark Chapel\n{CHOICE3}" + tt['menu_start_3'] = "{MENU}\n{SPEED0}\n≥@'s House\n Dark Chapel\n Mountain Cave\n{CHOICE2}" tt['intro_main'] = CompressedTextMapper.convert( "{INTRO}\n Episode III\n{PAUSE3}\n A Link to\n the Past\n" + "{PAUSE3}\nInverted\n Randomizer\n{PAUSE3}\nAfter mostly disregarding what happened in the first two games.\n" @@ -2697,38 +2729,15 @@ def set_inverted_mode(world, player, rom, inverted_buffer): write_int16(rom, snes_to_pc(0x02D998), 0x0000) write_int16(rom, snes_to_pc(0x02D9A6), 0x005A) rom.write_byte(snes_to_pc(0x02D9B3), 0x12) + + # switch AT and GT + if world.shuffle[player] == 'vanilla' and world.is_atgt_swapped(player): + rom.write_byte(0xDBB73 + 0x23, 0x37) + rom.write_byte(0xDBB73 + 0x36, 0x24) + if world.doorShuffle[player] == 'vanilla' or world.intensity[player] < 3: + write_int16(rom, 0x15AEE + 2*0x38, 0x00E0) + write_int16(rom, 0x15AEE + 2*0x25, 0x000C) - if world.shuffle[player] == 'vanilla': - rom.write_byte(0xDBB73 + 0x23, 0x37) # switch AT and GT - rom.write_byte(0xDBB73 + 0x36, 0x24) - if world.doorShuffle[player] == 'vanilla' or world.intensity[player] < 3: - write_int16(rom, 0x15AEE + 2*0x38, 0x00E0) - write_int16(rom, 0x15AEE + 2*0x25, 0x000C) - - if world.is_tile_swapped(0x03, player): - if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull'] \ - or (world.shuffle[player] == 'simple' and world.is_tile_swapped(0x05, player)): - rom.write_bytes(snes_to_pc(0x308350), [0x00, 0x00, 0x01]) # mountain cave starts on OW - - write_int16(rom, snes_to_pc(0x02D8DE), 0x00F1) # change mountain cave spawn point to just outside old man cave - rom.write_bytes(snes_to_pc(0x02D910), [0x1F, 0x1E, 0x1F, 0x1F, 0x03, 0x02, 0x03, 0x03]) - write_int16(rom, snes_to_pc(0x02D924), 0x0300) - write_int16(rom, snes_to_pc(0x02D932), 0x1F10) - write_int16(rom, snes_to_pc(0x02D940), 0x1FC0) - write_int16(rom, snes_to_pc(0x02D94E), 0x0378) - write_int16(rom, snes_to_pc(0x02D95C), 0x0187) - write_int16(rom, snes_to_pc(0x02D96A), 0x017F) - rom.write_byte(snes_to_pc(0x02D972), 0x06) - rom.write_byte(snes_to_pc(0x02D979), 0x00) - rom.write_byte(snes_to_pc(0x02D980), 0xFF) - rom.write_byte(snes_to_pc(0x02D987), 0x00) - rom.write_byte(snes_to_pc(0x02D98E), 0x22) - rom.write_byte(snes_to_pc(0x02D995), 0x12) - write_int16(rom, snes_to_pc(0x02D9A2), 0x0000) - write_int16(rom, snes_to_pc(0x02D9B0), 0x0007) - rom.write_byte(snes_to_pc(0x02D9B8), 0x12) - - rom.write_bytes(0x180247, [0x00, 0x5A, 0x00, 0x00, 0x00, 0x00, 0x00]) # indicates the overworld door being used for the single entrance spawn point if world.is_tile_swapped(0x05, player): rom.write_bytes(snes_to_pc(0x1BC655), [0x4A, 0x1D, 0x82]) # add warp under rock rom.write_byte(snes_to_pc(0x1BC428), 0x00) # remove secret portal @@ -2737,6 +2746,8 @@ def set_inverted_mode(world, player, rom, inverted_buffer): rom.write_bytes(snes_to_pc(0x1BD1DD), [0xA4, 0x06, 0x82, 0x9E, 0x06, 0x82, 0xFF, 0xFF]) # add warps under rocks rom.write_byte(0x180089, 0x01) # open TR after exit rom.write_bytes(0x0086E, [0x5C, 0x00, 0xA0, 0xA1]) # TR tail + rom.write_bytes(snes_to_pc(0x04E7A3), [0x20, 0x06]) # Top peg moved down, for peg puzzle + rom.write_byte(snes_to_pc(0x04E7E4), 0x10) # Remove TR portal overlay if world.shuffle[player] in ['vanilla']: world.fix_trock_doors[player] = True if world.is_tile_swapped(0x10, player): @@ -2850,10 +2861,25 @@ 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) + if world.is_bombshop_start(player): + 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 @@ -2865,7 +2891,8 @@ def set_inverted_mode(world, player, rom, inverted_buffer): rom.write_bytes(snes_to_pc(0x1BD1D8), [0xA8, 0x02, 0x82, 0xFF, 0xFF]) # add warp under rock rom.write_byte(snes_to_pc(0x1BC5B1), 0x00) # remove secret portal if world.is_tile_swapped(0x35, player): - rom.write_bytes(snes_to_pc(0x1BC85A), [0x50, 0x0F, 0x82]) # add warp under rock + #rom.write_bytes(snes_to_pc(0x1BC85A), [0x50, 0x0F, 0x82]) # add warp under rock + rom.write_bytes(snes_to_pc(0x1BC85A), [0x52, 0x13, 0x82]) # add warp under rock rom.write_byte(snes_to_pc(0x1BC5C7), 0x00) # remove secret portal # apply inverted map changes @@ -2877,6 +2904,7 @@ def patch_shuffled_dark_sanc(world, rom, player): dark_sanc_entrance = str([i for i in dark_sanc.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[dark_sanc_entrance][1] door_index = door_addresses[str(dark_sanc_entrance)][0] + world.initial_overworld_flags[player][ow_area] |= door_addresses[dark_sanc_entrance][2] rom.write_byte(0x180241, 0x01) rom.write_byte(0x180248, door_index + 1) @@ -2886,6 +2914,22 @@ 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] + world.initial_overworld_flags[player][ow_area] |= door_addresses[bomb_shop_entrance][2] + + 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 diff --git a/Rules.py b/Rules.py index afecc0fc..f5b9ddbf 100644 --- a/Rules.py +++ b/Rules.py @@ -610,7 +610,7 @@ def global_rules(world, player): def bomb_rules(world, player): bonkable_doors = ['Two Brothers House Exit (West)', 'Two Brothers House Exit (East)'] # Technically this is incorrectly defined, but functionally the same as what is intended. - bombable_doors = ['Ice Rod Cave', 'Light World Bomb Hut', 'Light World Death Mountain Shop', 'Mini Moldorm Cave', + bombable_doors = ['Ice Rod Cave', 'Light World Bomb Hut', 'Light World Death Mountain Shop', 'Light Hype Fairy', 'Mini Moldorm Cave', 'Hookshot Cave Back to Middle', 'Hookshot Cave Front to Middle', 'Hookshot Cave Middle to Front','Hookshot Cave Middle to Back', 'Dark Lake Hylia Ledge Fairy', 'Hype Cave', 'Brewery'] for entrance in bonkable_doors: @@ -838,6 +838,7 @@ def default_rules(world, player): set_rule(world.get_entrance('Lake Hylia Northeast Water Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Lake Hylia Central Water Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Lake Hylia Island Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Lake Hylia Water D Entry', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Ice Cave SW', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Octoballoon Water Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Octoballoon Waterfall Water Drop', player), lambda state: state.has('Flippers', player)) @@ -872,12 +873,12 @@ def default_rules(world, player): def ow_rules(world, player): - if world.mode[player] != 'inverted': + if world.is_atgt_swapped(player): + set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) + else: set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has_sword(player, 2) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle set_rule(world.get_entrance('GT Entry Approach', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) set_rule(world.get_entrance('GT Entry Leave', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player) or state.world.shuffle[player] in ('restricted', 'full', 'lite', 'lean', 'crossed', 'insanity')) - else: - set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) if not world.is_tile_swapped(0x00, player): set_rule(world.get_entrance('Lost Woods East Mirror Spot', player), lambda state: state.has_Mirror(player)) @@ -906,7 +907,9 @@ def ow_rules(world, player): set_rule(world.get_entrance('West Dark Death Mountain (Top) Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Bubble Boy Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('West Dark Death Mountain (Bottom) Mirror Spot', player), lambda state: state.has_Mirror(player)) - + set_rule(world.get_entrance('Spectacle Rock Approach', player), lambda state: world.logic[player] in ['noglitches', 'minorglitches'] and state.has_Pearl(player)) + set_rule(world.get_entrance('Spectacle Rock Leave', player), lambda state: world.logic[player] in ['noglitches', 'minorglitches'] and state.has_Pearl(player)) + if not world.is_tile_swapped(0x05, player): set_rule(world.get_entrance('East Death Mountain (Top West) Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('East Death Mountain (Top East) Mirror Spot', player), lambda state: state.has_Mirror(player)) @@ -935,8 +938,11 @@ def ow_rules(world, player): else: set_rule(world.get_entrance('Turtle Rock Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Turtle Rock Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) - set_rule(world.get_entrance('Turtle Rock Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_heavy_rocks(player) and state.has_Pearl(player)) - + set_rule(world.get_entrance('Turtle Rock Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('TR Pegs Ledge Drop', player), lambda state: False) + set_rule(world.get_entrance('TR Pegs Ledge Leave', player), lambda state: state.has('Hammer', player) and state.can_lift_heavy_rocks(player) and state.has_Pearl(player)) + set_rule(world.get_entrance('Turtle Rock Tail Ledge Drop', player), lambda state: world.logic[player] in ['noglitches', 'minorglitches']) + if not world.is_tile_swapped(0x0a, player): set_rule(world.get_entrance('Mountain Entry Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Mountain Entry Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) @@ -962,7 +968,8 @@ def ow_rules(world, player): set_rule(world.get_entrance('Skull Woods Pass East Top Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Skull Woods Pass Portal Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Skull Woods Pass East Bottom Mirror Spot', player), lambda state: state.has_Mirror(player)) - + set_rule(world.get_entrance('West Dark World Teleporter', player), lambda state: state.can_lift_rocks(player)) + if not world.is_tile_swapped(0x11, player): set_rule(world.get_entrance('Kakariko Fortune Mirror Spot', player), lambda state: state.has_Mirror(player)) else: @@ -984,8 +991,8 @@ def ow_rules(world, player): set_rule(world.get_entrance('Graveyard Ledge Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player)) set_rule(world.get_entrance('Kings Grave Mirror Spot', player), lambda state: state.has_Pearl(player) and state.has_Mirror(player)) else: - set_rule(world.get_entrance('Graveyard Ladder (Top)', player), lambda state: state.has_Pearl(player)) - set_rule(world.get_entrance('Graveyard Ladder (Bottom)', player), lambda state: state.has_Pearl(player)) + set_rule(world.get_entrance('Graveyard Ladder (Top)', player), lambda state: world.logic[player] in ['noglitches', 'minorglitches'] and state.has_Pearl(player)) + set_rule(world.get_entrance('Graveyard Ladder (Bottom)', player), lambda state: world.logic[player] in ['noglitches', 'minorglitches'] and state.has_Pearl(player)) set_rule(world.get_entrance('Dark Graveyard Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Dark Graveyard Ledge Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Dark Graveyard Grave Mirror Spot', player), lambda state: state.has_Mirror(player)) @@ -1147,14 +1154,18 @@ def ow_rules(world, player): set_rule(world.get_entrance('Misery Mire Blocked Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Misery Mire Main Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Misery Mire Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) - + set_rule(world.get_entrance('Checkerboard Ledge Approach', player), lambda state: world.logic[player] in ['noglitches', 'minorglitches']) + set_rule(world.get_entrance('Checkerboard Ledge Leave', player), lambda state: world.logic[player] in ['noglitches', 'minorglitches']) + if not world.is_tile_swapped(0x32, player): set_rule(world.get_entrance('Cave 45 Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Flute Boy Entry Mirror Spot', player), lambda state: state.has_Mirror(player)) else: set_rule(world.get_entrance('Stumpy Approach Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Stumpy Bush Entry Mirror Spot', player), lambda state: state.has_Mirror(player)) - + set_rule(world.get_entrance('Cave 45 Inverted Approach', player), lambda state: world.logic[player] in ['noglitches', 'minorglitches']) + set_rule(world.get_entrance('Cave 45 Inverted Leave', player), lambda state: world.logic[player] in ['noglitches', 'minorglitches']) + if not world.is_tile_swapped(0x33, player): set_rule(world.get_entrance('C Whirlpool Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('C Whirlpool Outer Mirror Spot', player), lambda state: state.has_Mirror(player)) @@ -1187,7 +1198,8 @@ def ow_rules(world, player): set_rule(world.get_entrance('Ice Lake Northeast Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Ice Palace Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Ice Palace Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) - + set_rule(world.get_entrance('Lake Hylia Island Pier', player), lambda state: world.logic[player] in ['noglitches', 'minorglitches']) + if not world.is_tile_swapped(0x37, player): set_rule(world.get_entrance('Ice Cave Mirror Spot', player), lambda state: state.has_Mirror(player)) else: @@ -1200,7 +1212,9 @@ def ow_rules(world, player): set_rule(world.get_entrance('Swamp Nook Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Swamp Nook Southeast Mirror Spot', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Swamp Nook Pegs Mirror Spot', player), lambda state: state.has_Mirror(player)) - + set_rule(world.get_entrance('Desert Pass Ladder (South)', player), lambda state: world.logic[player] in ['noglitches', 'minorglitches']) + set_rule(world.get_entrance('Desert Pass Ladder (North)', player), lambda state: world.logic[player] in ['noglitches', 'minorglitches']) + if not world.is_tile_swapped(0x3b, player): set_rule(world.get_entrance('Dam Mirror Spot', player), lambda state: state.has_Mirror(player)) else: @@ -1530,8 +1544,8 @@ def swordless_rules(world, player): set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player)) set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player)) set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has('Hammer', player)) # need to damage ganon to get tiles to drop - - if world.mode[player] != 'inverted': + + if not world.is_atgt_swapped(player): set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player) or state.has('Beat Agahnim 1', player)) # barrier gets removed after killing agahnim, relevant for entrance shuffle set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!) diff --git a/Text.py b/Text.py index c84a202c..5257dc40 100644 --- a/Text.py +++ b/Text.py @@ -627,8 +627,8 @@ class MultiByteCoreTextMapper(object): } @classmethod - def convert(cls, text, pause=True, wrap=14): - text = text.upper() + def convert(cls, text, pause=True, wrap=19): + #text = text.upper() lines = text.split('\n') outbuf = bytearray() lineindex = 0 @@ -655,7 +655,7 @@ class MultiByteCoreTextMapper(object): pending_space = False while words: word = words.pop(0) - # sanity check: if the word we have is more than 14 characters, we take as much as we can still fit and push the rest back for later + # sanity check: if the word we have is more than 19 characters, we take as much as we can still fit and push the rest back for later if cls.wordlen(word) > wrap: (word_first, word_rest) = cls.splitword(word, linespace) words.insert(0, word_rest) @@ -736,7 +736,7 @@ class CompressedTextMapper(object): } @classmethod - def convert(cls, text, pause=True, max_bytes_expanded=0x800, wrap=14): + def convert(cls, text, pause=True, max_bytes_expanded=0x800, wrap=19): inbuf = MultiByteCoreTextMapper.convert(text, pause, wrap) # Links name will need 8 bytes in the target buffer @@ -772,20 +772,24 @@ class CompressedTextMapper(object): class CharTextMapper(object): number_offset = None alpha_offset = 0 + alpha_lower_offset = 0 char_map = {} @classmethod def map_char(cls, char): if cls.number_offset is not None: if 0x30 <= ord(char) <= 0x39: return ord(char) + cls.number_offset + if 0x41 <= ord(char) <= 0x5A: + return ord(char) + 0x20 + cls.alpha_offset if 0x61 <= ord(char) <= 0x7A: - return ord(char) + cls.alpha_offset + return ord(char) + cls.alpha_lower_offset return cls.char_map.get(char, cls.char_map[' ']) @classmethod def convert(cls, text): buf = bytearray() - for char in text.lower(): + #for char in text.lower(): + for char in text: buf.append(cls.map_char(char)) return buf @@ -1240,6 +1244,7 @@ class RawMBTextMapper(CharTextMapper): "月": 0xFE, "姫": 0xFF} alpha_offset = 0x49 + alpha_lower_offset = -0x31 number_offset = 0x70 @classmethod @@ -1251,7 +1256,8 @@ class RawMBTextMapper(CharTextMapper): @classmethod def convert(cls, text): buf = bytearray() - for char in text.lower(): + #for char in text.lower(): + for char in text: res = cls.map_char(char) if isinstance(res, int): buf.extend([0x00, res]) @@ -1267,16 +1273,19 @@ class GoldCreditMapper(CharTextMapper): '-': 0x36, '.': 0x37,} alpha_offset = -0x47 + alpha_lower_offset = -0x47 class GreenCreditMapper(CharTextMapper): char_map = {' ': 0x9F, '·': 0x52} alpha_offset = -0x29 + alpha_lower_offset = -0x29 class RedCreditMapper(CharTextMapper): char_map = {' ': 0x9F} alpha_offset = -0x61 + alpha_lower_offset = -0x61 class LargeCreditTopMapper(CharTextMapper): char_map = {' ': 0x9F, @@ -1296,6 +1305,7 @@ class LargeCreditTopMapper(CharTextMapper): '◢': 0xAA, '◣': 0xAB,} alpha_offset = -0x04 + alpha_lower_offset = -0x04 number_offset = 0x23 @@ -1317,6 +1327,7 @@ class LargeCreditBottomMapper(CharTextMapper): '◢': 0xCA, '◣': 0xCB,} alpha_offset = 0x22 + alpha_lower_offset = 0x22 number_offset = 0x49 class TextTable(object): @@ -1903,7 +1914,7 @@ class TextTable(object): text['pond_of_wishing_good_luck'] = CompressedTextMapper.convert("\n is good luck") text['pond_of_wishing_meh_luck'] = CompressedTextMapper.convert("\n is meh luck") # Repurposed to no items in Randomizer - text['pond_of_wishing_bad_luck'] = CompressedTextMapper.convert("Why you come in here and pretend like you have something this fountain wants? Come back with bottles!") + text['pond_of_wishing_bad_luck'] = CompressedTextMapper.convert("Why come in here and pretend like you have something this fountain wants? Come back with bottles!") text['pond_of_wishing_fortune'] = CompressedTextMapper.convert("by the way, your fortune,") text['item_get_14_heart'] = CompressedTextMapper.convert("3 more to go\n ¼\nYay!") text['item_get_24_heart'] = CompressedTextMapper.convert("2 more to go\n ½\nWhee!") @@ -1955,16 +1966,16 @@ class TextTable(object): text['game_chest_lost_woods'] = CompressedTextMapper.convert("Pay 100 rupees open 1 chest. Are you lucky?\nSo, Play game?\n ≥ play\n never!\n{CHOICE}") text['kakariko_flophouse_man_no_flippers'] = CompressedTextMapper.convert("I really hate mowing my yard.\nI moved my house and everyone else's to avoid it.\n{PAGEBREAK}\nI hope you don't mind.") text['kakariko_flophouse_man'] = CompressedTextMapper.convert("I really hate mowing my yard.\nI moved my house and everyone else's to avoid it.\n{PAGEBREAK}\nI hope you don't mind.") - text['menu_start_2'] = CompressedTextMapper.convert("{MENU}\n{SPEED0}\n≥@'s house\n Sanctuary\n{CHOICE3}", False) - text['menu_start_3'] = CompressedTextMapper.convert("{MENU}\n{SPEED0}\n≥@'s house\n Sanctuary\n Mountain Cave\n{CHOICE2}", False) - text['menu_pause'] = CompressedTextMapper.convert("{SPEED0}\n≥continue\n save and quit\n{CHOICE3}", False) + text['menu_start_2'] = CompressedTextMapper.convert("{MENU}\n{SPEED0}\n≥@'s House\n Sanctuary\n{CHOICE3}", False) + text['menu_start_3'] = CompressedTextMapper.convert("{MENU}\n{SPEED0}\n≥@'s House\n Sanctuary\n Mountain Cave\n{CHOICE2}", False) + text['menu_pause'] = CompressedTextMapper.convert("{SPEED0}\n≥Continue\n Save and Quit\n{CHOICE3}", False) text['game_digging_choice'] = CompressedTextMapper.convert("Have 80 Rupees? Want to play digging game?\n ≥yes\n no\n{CHOICE}") text['game_digging_start'] = CompressedTextMapper.convert("Okay, use the shovel with Y!") text['game_digging_no_cash'] = CompressedTextMapper.convert("Shovel rental is 80 rupees.\nI have all day") text['game_digging_end_time'] = CompressedTextMapper.convert("Time's up!\nTime for you to go.") text['game_digging_come_back_later'] = CompressedTextMapper.convert("Come back later, I have to bury things.") text['game_digging_no_follower'] = CompressedTextMapper.convert("Something is following you. I don't like.") - text['menu_start_4'] = CompressedTextMapper.convert("{MENU}\n{SPEED0}\n≥@'s house\n Mountain Cave\n{CHOICE3}", False) + text['menu_start_4'] = CompressedTextMapper.convert("{MENU}\n{SPEED0}\n≥@'s House\n Mountain Cave\n{CHOICE3}", False) # Start of new text data text['ganon_fall_in_alt'] = CompressedTextMapper.convert("You think you\nare ready to\nface me?\n\nI will not die\n\nunless you\ncomplete your\ngoals. Dingus!") text['ganon_phase_3_alt'] = CompressedTextMapper.convert("Got wax in\nyour ears?\nI cannot die!") diff --git a/asm/drhooks.asm b/asm/drhooks.asm index 1d3b485b..a5815d98 100644 --- a/asm/drhooks.asm +++ b/asm/drhooks.asm @@ -76,6 +76,9 @@ nop : jsl OverridePaletteHeader org $02817e ; Bank02.asm : 414 (LDA $02811E, X) jsl FixAnimatedTiles +org $0aef43 ; UnderworldMap_RecoverGFX +jsl FixCloseDungeonMap + org $028a06 ; Bank02.asm : 1941 Dungeon_ResetTorchBackgroundAndPlayer JSL FixWallmasterLamp @@ -186,9 +189,9 @@ Main_ShowTextMessage: ; Conditionally disable UW music changes in Door Rando org $028ADB ; <- Bank02.asm:2088-2095 (LDX.b #$14 : LDA $A0 ...) -JSL.l Underworld_DoorDown_Entry : CPX #$10 -db $B0, $21 ; BCS $028B04 -BRA + : NOP #6 : + +JSL.l Underworld_DoorDown_Entry : CPX #$FF +BEQ + : db $80, $1C ; BRA $028B04 +NOP #6 : + org $02C3F2 ; <- Bank02.asm:10521 Unused call Underworld_DoorDown_Call: diff --git a/asm/gfx.asm b/asm/gfx.asm index b22fba62..94cb8848 100644 --- a/asm/gfx.asm +++ b/asm/gfx.asm @@ -45,6 +45,16 @@ FixAnimatedTiles: + LDA $02802E, X ; what we wrote over RTL +FixCloseDungeonMap: + LDA.l DRMode : CMP #$02 : BNE .vanilla + LDA $040C : BMI .vanilla + LSR : TAX + LDA.l DungeonTilesets,x + RTL + .vanilla + LDA $7EC20E + RTL + FixWallmasterLamp: ORA $0458 STY $1C : STA $1D : RTL ; what we wrote over diff --git a/data/base2current.bps b/data/base2current.bps index dd3449c3..8372df77 100644 Binary files a/data/base2current.bps and b/data/base2current.bps differ diff --git a/mystery_example.yml b/mystery_example.yml index 45f07fac..ca85b725 100644 --- a/mystery_example.yml +++ b/mystery_example.yml @@ -157,6 +157,13 @@ easy: 1 hard: 1 expert: 0 + pseudoboots: + on: 1 + off: 0 + startinventory: + "Pegasus Boots": + on: 0 + off: 1 rom: quickswap: on: 1