diff --git a/BaseClasses.py b/BaseClasses.py index cfa4e48d..ab4f4158 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -10,7 +10,6 @@ try: except ImportError: from enum import IntFlag as FastEnum - from source.classes.BabelFish import BabelFish from EntranceShuffle import door_addresses, indirect_connections from Utils import int16_as_bytes @@ -53,11 +52,8 @@ class World(object): self._location_cache = {} self.required_locations = [] self.shuffle_bonk_prizes = False - self.light_world_light_cone = False - self.dark_world_light_cone = False self.clock_mode = 'none' self.rupoor_cost = 10 - self.aga_randomness = True self.lock_aga_door_in_escape = False self.save_and_quit_from_boss = True self.accessibility = accessibility.copy() @@ -90,11 +86,13 @@ class World(object): self._portal_cache = {} self.sanc_portal = {} self.fish = BabelFish() - self.pot_contents = {} + self.data_tables = {} + self.damage_table = {} for player in range(1, players + 1): def set_player_attr(attr, val): self.__dict__.setdefault(attr, {})[player] = val + set_player_attr('_region_cache', {}) set_player_attr('player_names', []) set_player_attr('remote_items', False) @@ -113,8 +111,8 @@ class World(object): set_player_attr('can_access_trock_front', None) set_player_attr('can_access_trock_big_chest', None) set_player_attr('can_access_trock_middle', None) - set_player_attr('fix_fake_world', logic[player] not in ['owglitches', 'nologic'] - or shuffle[player] in ['lean', 'crossed', 'insanity']) + set_player_attr('fix_fake_world', logic[player] not in ['owglitches', 'hybridglitches', 'nologic'] + or shuffle[player] in ['lean', 'swapped', 'crossed', 'insanity']) set_player_attr('mapshuffle', False) set_player_attr('compassshuffle', False) set_player_attr('keyshuffle', 'none') @@ -128,6 +126,7 @@ class World(object): set_player_attr('enemy_shuffle', 'none') set_player_attr('enemy_health', 'default') set_player_attr('enemy_damage', 'default') + set_player_attr('any_enemy_logic', 'allow_all') set_player_attr('beemizer', 0) set_player_attr('escape_assist', []) set_player_attr('crystals_needed_for_ganon', 7) @@ -150,6 +149,7 @@ class World(object): set_player_attr('door_type_mode', 'original') set_player_attr('trap_door_mode', 'optional') set_player_attr('key_logic_algorithm', 'partial') + set_player_attr('aga_randomness', True) set_player_attr('shopsanity', False) set_player_attr('mixed_travel', 'prevent') @@ -158,6 +158,7 @@ class World(object): set_player_attr('exp_cache', defaultdict(dict)) set_player_attr('enabled_entrances', {}) + set_player_attr('data_tables', None) def finish_init(self): for player in range(1, self.players + 1): @@ -250,6 +251,9 @@ class World(object): return dungeon raise RuntimeError('No such dungeon %s for player %d' % (dungeonname, player)) + def get_dungeons(self, player): + return [d for d in self.dungeons if d.player == player] + def get_door(self, doorname, player): if isinstance(doorname, Door): return doorname @@ -404,7 +408,7 @@ class World(object): def push_precollected(self, item): item.world = self if ((item.smallkey and self.keyshuffle[item.player] != 'none') - or (item.bigkey and self.bigkeyshuffle[item.player])): + or (item.bigkey and self.bigkeyshuffle[item.player])): item.advancement = True self.precollected_items.append(item) self.state.collect(item, True) @@ -418,7 +422,7 @@ class World(object): item.location = location item.world = self if location.player != item.player and location.type == LocationType.Pot: - self.pot_contents[location.player].multiworld_count += 1 + self.data_tables[location.player].pot_secret_table.multiworld_count += 1 if collect: self.state.collect(item, location.event, location) @@ -508,7 +512,7 @@ class World(object): if not sphere: # ran out of places and did not finish yet, quit if log_error: - missing_locations = ", ".join([x.name for x in prog_locations]) + missing_locations = ", ".join([f'{x.name} (#{x.player})' for x in prog_locations]) logging.getLogger('').error(f'Cannot reach the following locations: {missing_locations}') return False @@ -543,9 +547,45 @@ class CollectionState(object): self.opened_doors = {player: set() for player in range(1, parent.players + 1)} self.dungeons_to_check = {player: defaultdict(dict) for player in range(1, parent.players + 1)} self.dungeon_limits = None - self.placing_item = None + self.placing_items = None # self.trace = None + def can_reach_from(self, spot, start, player=None): + old_state = self.copy() + # old_state.path = {old_state.world.get_region(start, player)} + old_state.stale[player] = False + old_state.reachable_regions[player] = dict() + old_state.blocked_connections[player] = dict() + rrp = old_state.reachable_regions[player] + bc = old_state.blocked_connections[player] + + # init on first call - this can't be done on construction since the regions don't exist yet + start = self.world.get_region(start, player) + if start in self.reachable_regions[player]: + rrp[start] = self.reachable_regions[player][start] + for conn in start.exits: + bc[conn] = self.blocked_connections[player][conn] + else: + rrp[start] = CrystalBarrier.Orange + for conn in start.exits: + bc[conn] = CrystalBarrier.Orange + + queue = deque(old_state.blocked_connections[player].items()) + + old_state.traverse_world(queue, rrp, bc, player) + if old_state.world.key_logic_algorithm[player] == 'default': + unresolved_events = [x for y in old_state.reachable_regions[player] for x in y.locations + if x.event and x.item and (x.item.smallkey or x.item.bigkey or x.item.advancement) + and x not in old_state.locations_checked and x.can_reach(old_state)] + unresolved_events = old_state._do_not_flood_the_keys(unresolved_events) + if len(unresolved_events) == 0: + old_state.check_key_doors_in_dungeons(rrp, player) + + if self.world.get_region(spot, player) in rrp: + return True + else: + return False + def update_reachable_regions(self, player): self.stale[player] = False rrp = self.reachable_regions[player] @@ -757,7 +797,7 @@ class CollectionState(object): bc_ = self.blocked_connections[player] for block, crystal in bc_.items(): if (block, crystal) not in terminal_queue and self.possibly_connected_to_dungeon(block.connected_region, player): - terminal_queue.append((block, crystal)) + terminal_queue.append((block, crystal)) self.traverse_world(terminal_queue, rrp_, bc_, player) self.dungeon_limits = None @@ -780,7 +820,7 @@ class CollectionState(object): missing_bc = {} for blocked, crystal in common_bc.items(): if (blocked not in bc and blocked.parent_region in rrp - and self.should_visit(blocked.connected_region, rrp, crystal, player)): + and self.should_visit(blocked.connected_region, rrp, crystal, player)): missing_bc[blocked] = crystal for k in missing_bc: bc[k] = missing_bc[k] @@ -829,15 +869,15 @@ class CollectionState(object): return door_candidates door_candidates, skip = [], set() if (state.world.accessibility[player] != 'locations' and remaining_keys == 0 and dungeon_name != 'Universal' - and state.placing_item and state.placing_item.name == small_key_name): + and state.placing_items and any(i.name == small_key_name and i.player == player for i in state.placing_items)): key_logic = state.world.key_logic[player][dungeon_name] for door, paired in key_logic.sm_doors.items(): if door.name in key_logic.door_rules: rule = key_logic.door_rules[door.name] key = KeyRuleType.AllowSmall if (key in rule.new_rules and key_total >= rule.new_rules[key] and door.name not in skip - and door.name in state.reached_doors[player] and door.name not in state.opened_doors[player] - and rule.small_location.item is None): + and door.name in state.reached_doors[player] and door.name not in state.opened_doors[player] + and rule.small_location.item is None): if paired: door_candidates.append((door.name, paired.name)) skip.add(paired.name) @@ -874,7 +914,7 @@ class CollectionState(object): player: defaultdict(dict, {name: copy.copy(checklist) for name, checklist in self.dungeons_to_check[player].items()}) for player in range(1, self.world.players + 1)} - ret.placing_item = self.placing_item + ret.placing_items = self.placing_items return ret def apply_dungeon_exploration(self, rrp, player, dungeon_name, checklist): @@ -961,8 +1001,8 @@ class CollectionState(object): 'Mirror Shield', 'Progressive Shield', 'Bug Catching Net', 'Cane of Byrna', 'Ocarina (Activated)', 'Boss Heart Container', 'Sanctuary Heart Container', 'Piece of Heart', 'Magic Upgrade (1/2)', 'Magic Upgrade (1/4)'] - or item_name.startswith(('Bottle', 'Small Key', 'Big Key')) - or (self.world.restrict_boss_items[player] != 'none' and item_name.startswith(('Map', 'Compass')))) + or item_name.startswith(('Bottle', 'Small Key', 'Big Key')) + or (self.world.restrict_boss_items[player] != 'none' and item_name.startswith(('Map', 'Compass')))) def can_reach(self, spot, resolution_hint=None, player=None): try: @@ -1009,7 +1049,6 @@ class CollectionState(object): self.collect(event.item, True, event) new_locations = True - def can_reach_blue(self, region, player): return region in self.reachable_regions[player] and self.reachable_regions[player][region] in [CrystalBarrier.Blue, CrystalBarrier.Either] @@ -1022,7 +1061,7 @@ class CollectionState(object): if event.name in flooded_keys.keys(): flood_location = self.world.get_location(flooded_keys[event.name], event.player) if (flood_location.item and flood_location not in self.locations_checked - and self.location_can_be_flooded(flood_location)): + and self.location_can_be_flooded(flood_location)): adjusted_checks.remove(event) if len(adjusted_checks) < len(reachable_events): return adjusted_checks @@ -1097,6 +1136,12 @@ class CollectionState(object): def can_lift_rocks(self, player): return self.has('Power Glove', player) or self.has('Titans Mitts', player) + def can_bomb_clip(self, region, player: int) -> bool: + return self.is_not_bunny(region, player) and self.has('Pegasus Boots', player) and self.can_use_bombs(player) + + def can_dash_clip(self, region, player: int) -> bool: + return self.is_not_bunny(region, player) and self.has('Pegasus Boots', player) + def has_bottle(self, player): return self.bottle_count(player) > 0 @@ -1111,16 +1156,16 @@ class CollectionState(object): # Warning: This only considers items that are marked as advancement items diff = self.world.difficulty_requirements[player] return ( - min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit) - + self.item_count('Sanctuary Heart Container', player) - + min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4 - + 3 # starting hearts + min(self.item_count('Boss Heart Container', player), diff.boss_heart_container_limit) + + self.item_count('Sanctuary Heart Container', player) + + min(self.item_count('Piece of Heart', player), diff.heart_piece_limit) // 4 + + 3 # starting hearts ) def can_lift_heavy_rocks(self, player): return self.has('Titans Mitts', player) - def can_extend_magic(self, player, smallmagic=16, fullrefill=False): #This reflects the total magic Link has, not the total extra he has. + def can_extend_magic(self, player, smallmagic=16, fullrefill=False): # This reflects the total magic Link has, not the total extra he has. basemagic = 8 if self.has('Magic Upgrade (1/4)', player): basemagic = 32 @@ -1161,16 +1206,16 @@ class CollectionState(object): def can_hit_crystal_through_barrier(self, player): return (self.can_use_bombs(player) - or self.can_shoot_arrows(player) - or self.has('Blue Boomerang', player) - or self.has('Red Boomerang', player) - or self.has('Fire Rod', player) - or self.has('Ice Rod', player) - or self.has('Cane of Somaria', player)) + or self.can_shoot_arrows(player) + or self.has('Blue Boomerang', player) + or self.has('Red Boomerang', player) + or self.has('Fire Rod', player) + or self.has('Ice Rod', player) + or self.has('Cane of Somaria', player)) def can_shoot_arrows(self, player): if self.world.bow_mode[player] in ['retro', 'retro_silvers']: - #todo: Non-progressive silvers grant wooden arrows, but progressive bows do not. Always require shop arrows to be safe + # todo: Non-progressive silvers grant wooden arrows, but progressive bows do not. Always require shop arrows to be safe return self.has('Bow', player) and (self.can_buy_unlimited('Single Arrow', player) or self.has('Single Arrow', player)) return self.has('Bow', player) @@ -1209,15 +1254,16 @@ class CollectionState(object): def can_flute(self, player): if self.world.mode[player] == 'standard' and not self.has('Zelda Delivered', player): return False # can't flute in rain state - lw = self.world.get_region('Light World', player) + lw = self.world.get_region('Kakariko Village', player) return self.has('Ocarina (Activated)', player) or (self.has('Ocarina', player) and lw.can_reach(self) - and self.is_not_bunny(lw, player)) + 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.has_sword(player)) def can_avoid_lasers(self, player): - return self.has('Mirror Shield', player) or self.has('Cane of Byrna', player) or self.has('Cape', player) + return (self.has('Mirror Shield', player) or self.has('Cape', player) + or (self.has('Cane of Byrna', player) and self.world.difficulty_adjustments[player] not in ['hard', 'expert'])) def is_not_bunny(self, region, player): if self.has_Pearl(player): @@ -1266,6 +1312,9 @@ class CollectionState(object): def can_superbunny_mirror_with_sword(self, player): return self.has_Mirror(player) and self.has_sword(player) + def can_bunny_pocket(self, player): + return self.has_Boots(player) and (self.has_Mirror(player) or self.has_bottle(player)) + def collect(self, item, event=False, location=None): if location: self.locations_checked.add(location) @@ -1399,13 +1448,20 @@ class CollectionState(object): def __getattr__(self, item): if item.startswith('can_reach_'): return self.can_reach(item[10]) - #elif item.startswith('has_'): + # elif item.startswith('has_'): # return self.has(item[4]) if item == '__len__': return raise RuntimeError('Cannot parse %s.' % item) + +@unique +class Terrain(Enum): + Land = 0 + Water = 1 + + @unique class RegionType(Enum): Menu = 0 @@ -1431,9 +1487,10 @@ class Region(object): self.dungeon = None self.shop = None self.world = None - self.is_light_world = False # will be set aftermaking connections. + self.is_light_world = False # will be set aftermaking connections. self.is_dark_world = False self.spot_type = 'Region' + self.terrain = None self.hint_text = hint self.recursion_count = 0 self.player = player @@ -1463,6 +1520,9 @@ class Region(object): return self.dungeon and self.dungeon.is_dungeon_item(item) and item.player == self.player return True + def is_outdoors(self): + return self.type in {RegionType.LightWorld, RegionType.DarkWorld} + def __str__(self): return str(self.__unicode__()) @@ -1482,6 +1542,7 @@ class Entrance(object): self.recursion_count = 0 self.vanilla = None self.access_rule = lambda state: True + self.verbose_rule = None self.player = player self.door = None self.hide_path = False @@ -1742,7 +1803,7 @@ class Door(object): self.edge_id = None self.edge_width = None - #portal items + # portal items self.portalAble = False self.roomLayout = 0x22 # free scroll- both directions self.entranceFlag = False @@ -2088,17 +2149,17 @@ class Portal(object): return self.door.roomIndex def relative_coords(self): - y_rel = (self.door.roomIndex & 0xf0) >> 3 #todo: fix the shift!!!! + y_rel = (self.door.roomIndex & 0xf0) >> 3 # todo: fix the shift!!!! x_rel = (self.door.roomIndex & 0x0f) * 2 quad = self.door.quadrant if quad == 0: - return [y_rel, y_rel, y_rel, y_rel+1, x_rel, x_rel, x_rel, x_rel+1] + return [y_rel, y_rel, y_rel, y_rel + 1, x_rel, x_rel, x_rel, x_rel + 1] elif quad == 1: - return [y_rel, y_rel, y_rel, y_rel+1, x_rel+1, x_rel, x_rel+1, x_rel+1] + return [y_rel, y_rel, y_rel, y_rel + 1, x_rel + 1, x_rel, x_rel + 1, x_rel + 1] elif quad == 2: - return [y_rel+1, y_rel, y_rel+1, y_rel+1, x_rel, x_rel, x_rel, x_rel+1] + return [y_rel + 1, y_rel, y_rel + 1, y_rel + 1, x_rel, x_rel, x_rel, x_rel + 1] else: - return [y_rel+1, y_rel, y_rel+1, y_rel+1, x_rel+1, x_rel, x_rel+1, x_rel+1] + return [y_rel + 1, y_rel, y_rel + 1, y_rel + 1, x_rel + 1, x_rel, x_rel + 1, x_rel + 1] def scroll_x(self): x_rel = (self.door.roomIndex & 0x0f) * 2 @@ -2107,7 +2168,7 @@ class Portal(object): elif self.door.doorIndex == 1: return [0x80, x_rel] else: - return [0x00, x_rel+1] + return [0x00, x_rel + 1] def scroll_y(self): y_rel = ((self.door.roomIndex & 0xf0) >> 3) + 1 @@ -2127,7 +2188,7 @@ class Portal(object): elif self.door.doorIndex == 1: return [0xf8, x_rel] else: - return [0x78, x_rel+1] + return [0x78, x_rel + 1] # def camera_y(self): # return [0x87, 0x01] @@ -2186,20 +2247,22 @@ class Boss(object): def can_defeat(self, state): return self.defeat_rule(state, self.player) + class Location(object): - def __init__(self, player, name='', address=None, crystal=False, hint_text=None, parent=None, forced_item=None, player_address=None): + def __init__(self, player, name='', address=None, crystal=False, hint_text=None, parent=None, forced_item=None, + player_address=None, note=None): self.name = name self.parent_region = parent if forced_item is not None: - from Items import ItemFactory - self.forced_item = ItemFactory([forced_item], player)[0] - self.item = self.forced_item - self.item.location = self - self.event = True + from Items import ItemFactory + self.forced_item = ItemFactory([forced_item], player)[0] + self.item = self.forced_item + self.item.location = self + self.event = True else: - self.forced_item = None - self.item = None - self.event = False + self.forced_item = None + self.item = None + self.event = False self.crystal = crystal self.address = address self.player_address = player_address @@ -2211,11 +2274,14 @@ class Location(object): self.real = not crystal self.always_allow = lambda item, state: False self.access_rule = lambda state: True + self.verbose_rule = None self.item_rule = lambda item: True self.player = player self.skip = False self.type = LocationType.Normal if not crystal else LocationType.Prize self.pot = None + self.drop = None + self.note = note def can_fill(self, state, item, check_access=True): if not self.valid_multiworld(state, item): @@ -2224,7 +2290,7 @@ class Location(object): def valid_multiworld(self, state, item): if self.type == LocationType.Pot and self.player != item.player: - return state.world.pot_contents[self.player].multiworld_count < 256 + return state.world.data_tables[self.player].pot_secret_table.multiworld_count < 256 return True def can_reach(self, state): @@ -2246,6 +2312,8 @@ class Location(object): name += f' @ {self.parent_region.dungeon.name}' if world and world.players > 1: name += f' ({world.get_player_names(self.player)})' + if self.note: + name += f' ({self.note})' return name def __str__(self): @@ -2342,12 +2410,14 @@ class Item(object): class Crystal(Item): pass + @unique class ShopType(Enum): Shop = 0 TakeAny = 1 UpgradeShop = 2 + class Shop(object): def __init__(self, region, room_id, type, shopkeeper_config, custom, locked, sram_address): self.region = region @@ -2371,7 +2441,7 @@ class Shop(object): entrances = self.region.entrances config = self.item_count if len(entrances) == 1 and entrances[0].name in door_addresses: - door_id = door_addresses[entrances[0].name][0]+1 + door_id = door_addresses[entrances[0].name][0] + 1 else: door_id = 0 config |= 0x40 # ignore door id @@ -2379,7 +2449,7 @@ class Shop(object): config |= 0x80 if self.type == ShopType.UpgradeShop: config |= 0x10 # Alt. VRAM - return [0x00]+int16_as_bytes(self.room_id)+[door_id, 0x00, config, self.shopkeeper_config, 0x00] + return [0x00] + int16_as_bytes(self.room_id) + [door_id, 0x00, config, self.shopkeeper_config, 0x00] def has_unlimited(self, item): for inv in self.inventory: @@ -2493,6 +2563,7 @@ class Spoiler(object): 'enemy_shuffle': self.world.enemy_shuffle, 'enemy_health': self.world.enemy_health, 'enemy_damage': self.world.enemy_damage, + 'any_enemy_logic': self.world.any_enemy_logic, 'players': self.world.players, 'teams': self.world.teams, 'experimental': self.world.experimental, @@ -2620,7 +2691,7 @@ class Spoiler(object): out['Special'] = self.medallions out['Bottles'] = self.bottles if self.hashes: - out['Hashes'] = {f"{self.world.player_names[player][team]} (Team {team+1})": hash for (player, team), hash in self.hashes.items()} + out['Hashes'] = {f"{self.world.player_names[player][team]} (Team {team + 1})": hash for (player, team), hash in self.hashes.items()} if self.shops: out['Shops'] = self.shops out['playthrough'] = self.playthrough @@ -2693,7 +2764,7 @@ class Spoiler(object): outfile.write(f"Spiral Stairs can self-loop: {yn(self.metadata['door_self_loops'][player])}\n") outfile.write(f"Experimental: {yn(self.metadata['experimental'][player])}\n") outfile.write(f"Dungeon Counters: {self.metadata['dungeon_counters'][player]}\n") - outfile.write(f"Drop Shuffle: {yn(self.metadata['dropshuffle'][player])}\n") + outfile.write(f"Drop Shuffle: {self.metadata['dropshuffle'][player]}\n") outfile.write(f"Pottery Mode: {self.metadata['pottery'][player]}\n") outfile.write(f"Pot Shuffle (Legacy): {yn(self.metadata['potshuffle'][player])}\n") outfile.write('Map shuffle: %s\n' % ('Yes' if self.metadata['mapshuffle'][player] else 'No')) @@ -2704,12 +2775,14 @@ class Spoiler(object): outfile.write('Enemy shuffle: %s\n' % self.metadata['enemy_shuffle'][player]) outfile.write('Enemy health: %s\n' % self.metadata['enemy_health'][player]) outfile.write('Enemy damage: %s\n' % self.metadata['enemy_damage'][player]) + if self.metadata['enemy_shuffle'][player] != 'none': + outfile.write(f"Enemy logic: {self.metadata['any_enemy_logic'][player]}\n") outfile.write(f"Hints: {yn(self.metadata['hints'][player])}\n") outfile.write('Race: %s\n' % ('Yes' if self.world.settings.world_rep['meta']['race'] else 'No')) if self.startinventory: outfile.write('Starting Inventory:'.ljust(line_width)) - outfile.write('\n'.ljust(line_width+1).join(self.startinventory) + '\n') + outfile.write('\n'.ljust(line_width + 1).join(self.startinventory) + '\n') def hashes_to_file(self, filename): with open(filename, 'r') as infile: @@ -2729,7 +2802,7 @@ class Spoiler(object): if len(self.hashes) > 0: for team in range(self.world.teams): player_name = self.world.player_names[player][team] - label = f"Hash - {player_name} (Team {team+1}): " if self.world.teams > 1 else 'Hash: ' + label = f"Hash - {player_name} (Team {team + 1}): " if self.world.teams > 1 else 'Hash: ' idx = insert(contents, idx, f'{label}{self.hashes[player, team]}\n') if self.world.players > 1: insert(contents, idx, '\n') # return value ignored here, if you want to add more lines @@ -2760,16 +2833,16 @@ class Spoiler(object): if self.entrances: # entrances: To/From overworld; Checking w/ & w/out "Exit" and translating accordingly outfile.write('\nEntrances:\n\n') - outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_names(entry["player"])}: ' if self.world.players > 1 else '', self.world.fish.translate("meta","entrances",entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', self.world.fish.translate("meta","entrances",entry['exit'])) for entry in self.entrances.values()])) + outfile.write('\n'.join(['%s%s %s %s' % (f'{self.world.get_player_names(entry["player"])}: ' if self.world.players > 1 else '', self.world.fish.translate("meta", "entrances", entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', self.world.fish.translate("meta", "entrances", entry['exit'])) for entry in self.entrances.values()])) if self.doors: outfile.write('\n\nDoors:\n\n') outfile.write('\n'.join( ['%s%s %s %s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', - self.world.fish.translate("meta","doors",entry['entrance']), + self.world.fish.translate("meta", "doors", entry['entrance']), '<=>' if entry['direction'] == 'both' else '<=' if entry['direction'] == 'exit' else '=>', - self.world.fish.translate("meta","doors",entry['exit']), - '({0})'.format(entry['dname']) if self.world.doorShuffle[entry['player']] == 'crossed' else '') for + self.world.fish.translate("meta", "doors", entry['exit']), + '({0})'.format(entry['dname']) if self.world.doorShuffle[entry['player']] != 'basic' else '') for entry in self.doors.values()])) if self.lobbies: outfile.write('\n\nDungeon Lobbies:\n\n') @@ -2781,7 +2854,7 @@ class Spoiler(object): # doorNames: For some reason these come in combined, somehow need to split on the thing to translate # doorTypes: Small Key, Bombable, Bonkable outfile.write('\n\nDoor Types:\n\n') - outfile.write('\n'.join(['%s%s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', self.world.fish.translate("meta","doors",entry['doorNames']), self.world.fish.translate("meta","doorTypes",entry['type'])) for entry in self.doorTypes.values()])) + outfile.write('\n'.join(['%s%s %s' % ('Player {0}: '.format(entry['player']) if self.world.players > 1 else '', self.world.fish.translate("meta", "doors", entry['doorNames']), self.world.fish.translate("meta", "doorTypes", entry['type'])) for entry in self.doorTypes.values()])) # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name # items: Item names @@ -2791,7 +2864,7 @@ class Spoiler(object): # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name # items: Item names outfile.write('\n\nShops:\n\n') - outfile.write('\n'.join("{} [{}]\n {}".format(self.world.fish.translate("meta","locations",shop['location']), shop['type'], "\n ".join(self.world.fish.translate("meta","items",item) for item in [shop.get('item_0', None), shop.get('item_1', None), shop.get('item_2', None)] if item)) for shop in self.shops)) + outfile.write('\n'.join("{} [{}]\n {}".format(self.world.fish.translate("meta", "locations", shop['location']), shop['type'], "\n ".join(self.world.fish.translate("meta", "items", item) for item in [shop.get('item_0', None), shop.get('item_1', None), shop.get('item_2', None)] if item)) for shop in self.shops)) for player in range(1, self.world.players + 1): if self.world.boss_shuffle[player] != 'none': @@ -2799,12 +2872,28 @@ class Spoiler(object): outfile.write(f'\n\nBosses ({self.world.get_player_names(player)}):\n\n') outfile.write('\n'.join([f'{x}: {y}' for x, y in bossmap.items() if y not in ['Agahnim', 'Agahnim 2', 'Ganon']])) + def extras(self, filename): + # todo: conditional on enemy shuffle mode + with open(filename, 'a') as outfile: + outfile.write('\n\nOverworld Enemies:\n\n') + for player in range(1, self.world.players + 1): + player_tag = ' ' + self.world.get_player_names(player) if self.world.players > 1 else '' + for area, sprite_list in self.world.data_tables[player].ow_enemy_table.items(): + for idx, sprite in enumerate(sprite_list): + outfile.write(f'{hex(area)} Enemy #{idx + 1}{player_tag}: {str(sprite)}\n') + outfile.write('\n\nUnderworld Enemies:\n\n') + for player in range(1, self.world.players + 1): + player_tag = ' ' + self.world.get_player_names(player) if self.world.players > 1 else '' + for area, sprite_list in self.world.data_tables[player].uw_enemy_table.room_map.items(): + for idx, sprite in enumerate(sprite_list): + outfile.write(f'{hex(area)} Enemy #{idx + 1}{player_tag}: {str(sprite)}\n') + def playthrough_to_file(self, filename): with open(filename, 'a') as outfile: # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name # items: Item names outfile.write('\n\nPlaythrough:\n\n') - outfile.write('\n'.join(['%s: {\n%s\n}' % (sphere_nr, '\n'.join([' %s: %s' % (self.world.fish.translate("meta","locations",location), self.world.fish.translate("meta","items",item)) for (location, item) in sphere.items()] if sphere_nr != '0' else [f' {item}' for item in sphere])) for (sphere_nr, sphere) in self.playthrough.items()])) + outfile.write('\n'.join(['%s: {\n%s\n}' % (sphere_nr, '\n'.join([' %s: %s' % (self.world.fish.translate("meta", "locations", location), self.world.fish.translate("meta", "items", item)) for (location, item) in sphere.items()] if sphere_nr != '0' else [f' {item}' for item in sphere])) for (sphere_nr, sphere) in self.playthrough.items()])) if self.unreachables: # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name # items: Item names @@ -2822,10 +2911,10 @@ class Spoiler(object): path_lines = [] for region, exit in path: if exit is not None: - path_lines.append("{} -> {}".format(self.world.fish.translate("meta","rooms",region), self.world.fish.translate("meta","entrances",exit))) + path_lines.append("{} -> {}".format(self.world.fish.translate("meta", "rooms", region), self.world.fish.translate("meta", "entrances", exit))) else: - path_lines.append(self.world.fish.translate("meta","rooms",region)) - path_listings.append("{}\n {}".format(self.world.fish.translate("meta","locations",location), "\n => ".join(path_lines))) + path_lines.append(self.world.fish.translate("meta", "rooms", region)) + path_listings.append("{}\n {}".format(self.world.fish.translate("meta", "locations", location), "\n => ".join(path_lines))) outfile.write('\n'.join(path_listings)) @@ -2857,6 +2946,7 @@ dungeon_keys = { 'Universal': 'Small Key (Universal)' } + class PotItem(FastEnum): Nothing = 0x0 OneRupee = 0x1 @@ -2933,12 +3023,12 @@ class Pot(object): # byte 0: DDDE EEEE (DR, ER) dr_mode = {"basic": 1, "crossed": 2, "vanilla": 0, "partitioned": 3, 'paired': 4} er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4, "insanity": 5, 'lite': 8, - 'lean': 9, "dungeonsfull": 7, "dungeonssimple": 6} + 'lean': 9, "dungeonsfull": 7, "dungeonssimple": 6, 'swapped': 10} # byte 1: LLLW WSS? (logic, mode, sword) -logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4} +logic_mode = {"noglitches": 0, "minorglitches": 1, "nologic": 2, "owglitches": 3, "majorglitches": 4, "hybridglitches": 5} world_mode = {"open": 0, "standard": 1, "inverted": 2} -sword_mode = {"random": 0, "assured": 1, "swordless": 2, "vanilla": 3} +sword_mode = {"random": 0, "assured": 1, "swordless": 2, "vanilla": 3} # byte 2: GGGD DFFH (goal, diff, item_func, hints) goal_mode = {'ganon': 0, 'pedestal': 1, 'dungeons': 2, 'triforcehunt': 3, 'crystals': 4, 'trinity': 5, @@ -2953,6 +3043,7 @@ mixed_travel_mode = {"prevent": 0, "allow": 1, "force": 2} # new byte 4: TDDD PPPP (tavern shuffle, drop, pottery) # dropshuffle reserves 2 bits, pottery needs 4) +drop_shuffle_mode = {'none': 0, 'keys': 1, 'underworld': 2} pottery_mode = {'none': 0, 'keys': 2, 'lottery': 3, 'dungeon': 4, 'cave': 5, 'cavekeys': 6, 'reduced': 7, 'clustered': 8, 'nonempty': 9} @@ -2977,7 +3068,6 @@ rb_mode = {"none": 0, "mapcompass": 1, "dungeon": 2} algo_mode = {"balanced": 0, "equitable": 1, "vanilla_fill": 2, "dungeon_only": 3, "district": 4, 'major_only': 5} boss_mode = {"none": 0, "simple": 1, "full": 2, "chaos": 3, 'random': 3, 'unique': 4} - # byte 10: settings_version # byte 11: FBBB TTSS (flute_mode, bow_mode, take_any, small_key_mode) flute_mode = {'normal': 0, 'active': 1} @@ -3015,7 +3105,7 @@ class Settings(object): | (0x8 if w.standardize_palettes[p] == "original" else 0) | (0 if w.intensity[p] == "random" else w.intensity[p]), - (0x80 if w.shuffletavern[p] else 0) | (0x10 if w.dropshuffle[p] else 0) | (pottery_mode[w.pottery[p]]), + (0x80 if w.shuffletavern[p] else 0) | (drop_shuffle_mode[w.dropshuffle[p]] << 4) | (pottery_mode[w.pottery[p]]), (0x80 if w.door_self_loops[p] else 0) | ((8 if w.crystals_gt_orig[p] == "random" else int(w.crystals_gt_orig[p])) << 3) @@ -3040,7 +3130,7 @@ class Settings(object): ((0x80 if w.pseudoboots[p] else 0) | overworld_map_mode[w.overworld_map[p]] << 5 | trap_door_mode[w.trap_door_mode[p]] << 3 | key_logic_algo[w.key_logic_algorithm[p]]), - ]) + ]) return base64.b64encode(code, "+-".encode()).decode() @staticmethod @@ -3074,7 +3164,7 @@ class Settings(object): args.intensity[p] = "random" if intensity == 0 else intensity args.shuffletavern[p] = True if settings[4] & 0x80 else False - args.dropshuffle[p] = True if settings[4] & 0x10 else False + args.dropshuffle[p] = r(drop_shuffle_mode)[(settings[4] & 0x70) >> 4] args.pottery[p] = r(pottery_mode)[settings[4] & 0x0F] args.door_self_loops[p] = True if settings[5] & 0x80 else False diff --git a/Bosses.py b/Bosses.py index 7052bb5c..f60489a2 100644 --- a/Bosses.py +++ b/Bosses.py @@ -2,13 +2,15 @@ import logging import RaceRandom as random from BaseClasses import Boss, FillError +from source.enemizer.Bossmizer import boss_adjust -def BossFactory(boss, player): +def BossFactory(boss, player, on_ice=False): if boss is None: return None if boss in boss_table: - enemizer_name, defeat_rule = boss_table[boss] + enemizer_name, normal_defeat_rule, ice_defeat_rule = boss_table[boss] + defeat_rule = ice_defeat_rule if on_ice else normal_defeat_rule return Boss(boss, enemizer_name, defeat_rule, player) logging.getLogger('').error('Unknown Boss: %s', boss) @@ -41,16 +43,21 @@ def MoldormDefeatRule(state, player): def HelmasaurKingDefeatRule(state, player): return (state.has('Hammer', player) or state.can_use_bombs(player)) and (state.has_sword(player) or state.can_shoot_arrows(player)) + +def IceHelmasaurKingDefeatRule(state, player): + return state.can_use_bombs(player) and (state.has_sword(player) or state.can_shoot_arrows(player)) + + def ArrghusDefeatRule(state, player): if not state.has('Hookshot', player): return False - # TODO: ideally we would have a check for bow and silvers, which combined with the - # hookshot is enough. This is not coded yet because the silvers that only work in pyramid feature - # makes this complicated if state.has_blunt_weapon(player): return True - return ((state.has('Fire Rod', player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, 12))) or #assuming mostly gitting two puff with one shot + if state.can_shoot_arrows(player) and state.has('Silver Arrows', player) and state.world.difficulty_adjustments[player] not in ['hard', 'expert']: + return True + + return ((state.has('Fire Rod', player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, 12))) or # assuming mostly getting two puffs with one shot (state.has('Ice Rod', player) and state.can_use_bombs(player) and (state.can_shoot_arrows(player) or state.can_extend_magic(player, 16)))) @@ -64,9 +71,27 @@ def MothulaDefeatRule(state, player): (state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16)) ) + def BlindDefeatRule(state, player): return state.has_blunt_weapon(player) or state.has('Cane of Somaria', player) or state.has('Cane of Byrna', player) + +def IceBlindDefeatRule(state, player): + return ( + ( + # weapon + state.has_beam_sword(player) or + state.has('Cane of Somaria', player) or + (state.has('Cane of Byrna', player) and state.can_extend_magic(player, 16)) + ) and + ( + # protection + state.has('Red Shield', player) or + (state.has('Cane of Byrna', player) and state.world.difficulty_adjustments[player] not in ['hard', 'expert']) + ) + ) + + def KholdstareDefeatRule(state, player): return ( ( @@ -90,9 +115,39 @@ def KholdstareDefeatRule(state, player): ) ) + +def IceKholdstareDefeatRule(state, player): + return ( + ( + state.has('Fire Rod', player) or + ( + state.has('Bombos', player) and + # FIXME: the following only actually works for the vanilla location for swordless + (state.has_sword(player) or state.world.swords[player] == 'swordless') + ) + ) and + ( + state.has_beam_sword(player) or + (state.has('Fire Rod', player) and state.can_extend_magic(player, 20)) or + # FIXME: this actually only works for the vanilla location for swordless + ( + state.has('Fire Rod', player) and + state.has('Bombos', player) and + (state.has_sword(player) or state.world.swords[player] == 'swordless') and + state.can_extend_magic(player, 16) + ) + ) + ) + + def VitreousDefeatRule(state, player): return (state.can_shoot_arrows(player) and state.can_use_bombs(player)) or state.has_blunt_weapon(player) + +def IceVitreousDefeatRule(state, player): + return (state.can_shoot_arrows(player) and state.can_use_bombs(player)) or state.has_beam_sword(player) + + def TrinexxDefeatRule(state, player): if not (state.has('Fire Rod', player) and state.has('Ice Rod', player)): return False @@ -102,24 +157,36 @@ def TrinexxDefeatRule(state, player): (state.has('Master Sword', player) and state.can_extend_magic(player, 16)) or (state.has_sword(player) and state.can_extend_magic(player, 32))) + +def IceTrinexxDefeatRule(state, player): + if not (state.has('Fire Rod', player) and state.has('Ice Rod', player) and state.has_Boots(player)): + return False + return (state.has('Golden Sword', player) or + (state.has('Tempered Sword', player) and state.can_extend_magic(player, 16)) or + ((state.has('Hammer', player) or + state.has('Master Sword', player)) and state.can_extend_magic(player, 32))) # rod spam rule + + def AgahnimDefeatRule(state, player): return state.has_sword(player) or state.has('Hammer', player) or state.has('Bug Catching Net', player) + boss_table = { - 'Armos Knights': ('Armos', ArmosKnightsDefeatRule), - 'Lanmolas': ('Lanmola', LanmolasDefeatRule), - 'Moldorm': ('Moldorm', MoldormDefeatRule), - 'Helmasaur King': ('Helmasaur', HelmasaurKingDefeatRule), - 'Arrghus': ('Arrghus', ArrghusDefeatRule), - 'Mothula': ('Mothula', MothulaDefeatRule), - 'Blind': ('Blind', BlindDefeatRule), - 'Kholdstare': ('Kholdstare', KholdstareDefeatRule), - 'Vitreous': ('Vitreous', VitreousDefeatRule), - 'Trinexx': ('Trinexx', TrinexxDefeatRule), - 'Agahnim': ('Agahnim', AgahnimDefeatRule), - 'Agahnim2': ('Agahnim2', AgahnimDefeatRule) + 'Armos Knights': ('Armos', ArmosKnightsDefeatRule, ArmosKnightsDefeatRule), + 'Lanmolas': ('Lanmola', LanmolasDefeatRule, LanmolasDefeatRule), + 'Moldorm': ('Moldorm', MoldormDefeatRule, MoldormDefeatRule), + 'Helmasaur King': ('Helmasaur', HelmasaurKingDefeatRule, IceHelmasaurKingDefeatRule), + 'Arrghus': ('Arrghus', ArrghusDefeatRule, ArrghusDefeatRule), + 'Mothula': ('Mothula', MothulaDefeatRule, MothulaDefeatRule), + 'Blind': ('Blind', BlindDefeatRule, IceBlindDefeatRule), + 'Kholdstare': ('Kholdstare', KholdstareDefeatRule, IceKholdstareDefeatRule), + 'Vitreous': ('Vitreous', VitreousDefeatRule, IceVitreousDefeatRule), + 'Trinexx': ('Trinexx', TrinexxDefeatRule, IceTrinexxDefeatRule), + 'Agahnim': ('Agahnim', AgahnimDefeatRule, AgahnimDefeatRule), + 'Agahnim2': ('Agahnim2', AgahnimDefeatRule, AgahnimDefeatRule) } + def can_place_boss(world, player, boss, dungeon_name, level=None): if world.swords[player] in ['swordless'] and boss == 'Kholdstare' and dungeon_name != 'Ice Palace': return False @@ -132,6 +199,11 @@ def can_place_boss(world, player, boss, dungeon_name, level=None): if boss in ["Blind"]: return False + # no Trinexx on Ice in doors without doing some health modelling + if world.doorShuffle[player] != 'vanilla' and boss == 'Trinexx': + if dungeon_name == 'Ganons Tower' and level == 'bottom': + return False + if dungeon_name == 'Tower of Hera' and boss in ["Armos Knights", "Arrghus", "Blind", "Trinexx", "Lanmolas"]: return False @@ -151,6 +223,7 @@ def place_bosses(world, player): ['Tower of Hera', None], ['Skull Woods', None], ['Ganons Tower', 'middle'], + ['Ganons Tower', 'bottom'], ['Eastern Palace', None], ['Desert Palace', None], ['Palace of Darkness', None], @@ -159,7 +232,6 @@ def place_bosses(world, player): ['Ice Palace', None], ['Misery Mire', None], ['Turtle Rock', None], - ['Ganons Tower', 'bottom'], ] all_bosses = sorted(boss_table.keys()) #s orted to be deterministic on older pythons @@ -238,6 +310,7 @@ def place_bosses(world, player): raise FillError('Could not place boss for location %s' % loc_text) place_boss(boss, level, loc, loc_text, world, player) + boss_adjust(world, player) def place_boss(boss, level, loc, loc_text, world, player): @@ -246,4 +319,4 @@ def place_boss(boss, level, loc, loc_text, world, player): loc = [x.name for x in world.dungeons if x.player == player and level in x.bosses.keys()][0] loc_text = loc + ' (' + level + ')' logging.getLogger('').debug('Placing boss %s at %s', boss, loc_text) - world.get_dungeon(loc, player).bosses[level] = BossFactory(boss, player) + world.get_dungeon(loc, player).bosses[level] = BossFactory(boss, player, level == 'bottom') diff --git a/CLI.py b/CLI.py index 1cd4488a..1066b845 100644 --- a/CLI.py +++ b/CLI.py @@ -107,7 +107,7 @@ def parse_cli(argv, no_defaults=False): ret.keyshuffle = 'wild' if ret.keydropshuffle: - ret.dropshuffle = True + ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' else ret.dropshuffle ret.pottery = 'keys' if ret.pottery == 'none' else ret.pottery if ret.retro or ret.mode == 'retro': @@ -141,7 +141,7 @@ def parse_cli(argv, no_defaults=False): 'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle', 'mixed_travel', 'standardize_palettes', 'code', 'reduce_flashing', 'shuffle_sfx', 'msu_resume', 'collection_rate', 'colorizepots', 'decoupledoors', 'door_type_mode', - 'trap_door_mode', 'key_logic_algorithm', 'door_self_loops']: + 'trap_door_mode', 'key_logic_algorithm', 'door_self_loops', 'any_enemy_logic', 'aga_randomness']: value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name) if player == 1: setattr(ret, name, {1: value}) @@ -199,11 +199,11 @@ def parse_settings(): "shufflebosses": "none", "enemy_damage": "default", "enemy_health": "default", - "enemizercli": os.path.join(".", "EnemizerCLI", "EnemizerCLI.Core"), + 'any_enemy_logic': 'allow_all', "shopsanity": False, 'keydropshuffle': False, - 'dropshuffle': False, + 'dropshuffle': 'none', 'pottery': 'none', 'colorizepots': True, 'shufflepots': False, @@ -223,6 +223,7 @@ def parse_settings(): 'dungeon_counters': 'default', 'mixed_travel': 'prevent', 'standardize_palettes': 'standardize', + 'aga_randomness': True, "triforce_pool": 0, "triforce_goal": 0, @@ -351,9 +352,6 @@ def parse_settings(): "notes": "" } - if sys.platform.lower().find("windows"): - settings["enemizercli"] += ".exe" - # read saved settings file if it exists and set these settings_path = os.path.join(".", "resources", "user", "settings.json") settings = apply_settings_file(settings, settings_path) diff --git a/DoorShuffle.py b/DoorShuffle.py index b8c78f9f..b75e4df4 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -7,7 +7,7 @@ from typing import DefaultDict, Dict, List from itertools import chain from BaseClasses import RegionType, Region, Door, DoorType, Sector, CrystalBarrier, DungeonInfo, dungeon_keys -from BaseClasses import PotFlags, LocationType, Direction +from BaseClasses import PotFlags, LocationType, Direction, KeyRuleType from Doors import reset_portals from Dungeons import dungeon_regions, region_starts, standard_starts, split_region_starts from Dungeons import dungeon_bigs, dungeon_hints @@ -215,6 +215,7 @@ def valid_connection(region, std_flag, world, player): return region and (region.type == RegionType.Dungeon or region.name in world.inaccessible_regions[player] or (std_flag and region.name == 'Hyrule Castle Ledge')) + def vanilla_key_logic(world, player): builders = [] world.dungeon_layouts[player] = {} @@ -260,8 +261,25 @@ def vanilla_key_logic(world, player): world.key_logic[player][builder.name] = key_layout.key_logic world.key_layout[player][builder.name] = key_layout log_key_logic(builder.name, key_layout.key_logic) - # if world.shuffle[player] == 'vanilla' and world.accessibility[player] == 'items' and not world.retro[player] and not world.keydropshuffle[player]: - # validate_vanilla_key_logic(world, player) + # special adjustments for vanilla + if world.keyshuffle[player] != 'universal': + if world.mode[player] != 'standard' and world.dropshuffle[player] == 'none' : + # adjust hc doors + def adjust_hc_door(door_rule): + if door_rule.new_rules[KeyRuleType.WorstCase] == 3: + door_rule.new_rules[KeyRuleType.WorstCase] = 2 + door_rule.small_key_num = 2 + + rules = world.key_logic[player]['Hyrule Castle'].door_rules + adjust_hc_door(rules['Sewers Secret Room Key Door S']) + adjust_hc_door(rules['Hyrule Dungeon Map Room Key Door S']) + adjust_hc_door(rules['Sewers Dark Cross Key Door N']) + # adjust pod front door + pod_front = world.key_logic[player]['Palace of Darkness'].door_rules['PoD Middle Cage N'] + if pod_front.new_rules[KeyRuleType.WorstCase] == 6: + pod_front.new_rules[KeyRuleType.WorstCase] = 1 + pod_front.small_key_num = 1 + # gt logic? I'm unsure it needs adjusting def validate_vanilla_reservation(dungeon, world, player): @@ -461,7 +479,7 @@ def choose_portals(world, player): if portal_region.type == RegionType.LightWorld: world.get_portal(portal, player).light_world = True if name in world.inaccessible_regions[player] or (hc_flag and portal != 'Hyrule Castle South'): - name_key = 'Desert Ledge' if name == 'Desert Palace Entrance (North) Spot' else name + name_key = 'Desert Ledge' if name == 'Desert Ledge Keep' else name region_map[name_key].append(portal) inaccessible_portals.append(portal) else: @@ -614,7 +632,7 @@ def analyze_portals(world, player): if portal_region.type == RegionType.LightWorld: world.get_portal(portal, player).light_world = True if name in world.inaccessible_regions[player]: - name_key = 'Desert Ledge' if name == 'Desert Palace Entrance (North) Spot' else name + name_key = 'Desert Ledge' if name == 'Desert Ledge Keep' else name region_map[name_key].append(portal) inaccessible_portals.append(portal) else: @@ -885,10 +903,10 @@ def main_dungeon_pool(dungeon_pool, world, player): all_dungeon_items_cnt = len(list(y for x in world.dungeons if x.player == player for y in x.all_items)) target_items = 34 if world.keyshuffle[player] == 'universal': - target_items += 1 if world.dropshuffle[player] else 0 # the hc big key + target_items += 1 if world.dropshuffle[player] != 'none' else 0 # the hc big key else: target_items += 29 # small keys in chests - if world.dropshuffle[player]: + if world.dropshuffle[player] != 'none': target_items += 14 # 13 dropped smalls + 1 big if world.pottery[player] not in ['none', 'cave']: target_items += 19 # 19 pot keys @@ -1296,10 +1314,10 @@ def cross_dungeon(world, player): all_dungeon_items_cnt = len(list(y for x in world.dungeons if x.player == player for y in x.all_items)) target_items = 34 if world.keyshuffle[player] == 'universal': - target_items += 1 if world.dropshuffle[player] else 0 # the hc big key + target_items += 1 if world.dropshuffle[player] != 'none' else 0 # the hc big key else: target_items += 29 # small keys in chests - if world.dropshuffle[player]: + if world.dropshuffle[player] != 'none': target_items += 14 # 13 dropped smalls + 1 big if world.pottery[player] not in ['none', 'cave']: target_items += 19 # 19 pot keys @@ -1385,7 +1403,7 @@ def assign_cross_keys(dungeon_builders, world, player): start = time.process_time() if world.keyshuffle[player] == 'universal': remaining = 29 - if world.dropshuffle[player]: + if world.dropshuffle[player] != 'none': remaining += 13 if world.pottery[player] not in ['none', 'cave']: remaining += 19 @@ -2424,15 +2442,15 @@ def change_door_to_trap(d, world, player): trap_door_exceptions = { 'PoD Mimics 2 SW', 'TR Twin Pokeys NW', 'Thieves Blocked Entry SW', 'Hyrule Dungeon Armory Interior Key Door N', 'Desert Compass Key Door WN', 'TR Tile Room SE', 'Mire Cross SW', 'Tower Circle of Pots ES', - 'Eastern Single Eyegore ES', 'Eastern Duo Eyegores SE', 'Swamp Push Statue S', + 'PoD Mimics 1 SW', 'Eastern Single Eyegore ES', 'Eastern Duo Eyegores SE', 'Swamp Push Statue S', 'Skull 2 East Lobby WS', 'GT Hope Room WN', 'Eastern Courtyard Ledge S', 'Ice Lobby SE', 'GT Speed Torch WN', 'Ice Switch Room ES', 'Ice Switch Room NE', 'Skull Torch Room WS', 'GT Speed Torch NE', 'GT Speed Torch WS', 'GT Torch Cross WN', 'Mire Tile Room SW', 'Mire Tile Room ES', 'TR Torches WN', 'PoD Lobby N', 'PoD Middle Cage S', - 'Ice Bomb Jump NW', 'GT Hidden Spikes SE', 'Ice Tall Hint EN', 'GT Conveyor Cross EN', 'Eastern Pot Switch WN', + 'Ice Bomb Jump NW', 'GT Hidden Spikes SE', 'Ice Tall Hint EN', 'Ice Tall Hint SE', 'Eastern Pot Switch WN', 'Thieves Conveyor Maze WN', 'Thieves Conveyor Maze SW', 'Eastern Dark Square Key Door WN', 'Eastern Lobby NW', 'Eastern Lobby NE', 'Ice Cross Bottom SE', 'Desert Back Lobby S', 'Desert West S', 'Desert West Lobby ES', 'Mire Hidden Shooters SE', 'Mire Hidden Shooters ES', 'Mire Hidden Shooters WS', - 'Tower Dark Pits EN', 'Tower Dark Maze ES', 'TR Tongue Pull WS', + 'Tower Dark Pits EN', 'Tower Dark Maze ES', 'TR Tongue Pull WS', 'GT Conveyor Cross EN', } @@ -3351,7 +3369,7 @@ def find_accessible_entrances(world, player, builder): hc_std = True start_regions = ['Hyrule Castle Courtyard'] elif world.mode[player] != 'inverted': - start_regions = ['Links House', 'Sanctuary', 'East Dark World'] + start_regions = ['Links House', 'Sanctuary', 'Pyramid Area'] else: start_regions = ['Links House', 'Dark Sanctuary Hint', 'Hyrule Castle Ledge'] regs = convert_regions(start_regions, world, player) @@ -3373,7 +3391,7 @@ def find_accessible_entrances(world, player, builder): if connect not in queue and connect not in visited_regions: queue.append(connect) for ext in next_region.exits: - if hc_std and ext.name in ['Hyrule Castle Main Gate (North)', 'Castle Gate Teleporter', 'Hyrule Castle Ledge Drop']: # just skip it + if hc_std and ext.name in ['Hyrule Castle Main Gate (North)', 'Castle Gate Teleporter (Inner)', 'Hyrule Castle Ledge Drop']: # just skip it continue connect = ext.connected_region if connect is None or ext.door and ext.door.blocked: @@ -3571,6 +3589,7 @@ class DROptions(Flag): Hide_Total = 0x100 DarkWorld_Spawns = 0x200 BigKeyDoor_Shuffle = 0x400 + EnemyDropIndicator = 0x800 # if on, enemy drop indicator show, else it doesn't # DATA GOES DOWN HERE @@ -3643,8 +3662,10 @@ logical_connections = [ ('PoD Map Balcony to Ranged Crystal', 'PoD Map Balcony - Ranged Crystal'), ('PoD Map Balcony Ranged Crystal Exit', 'PoD Map Balcony'), ('PoD Basement Ledge Drop Down', 'PoD Stalfos Basement'), - ('PoD Falling Bridge Path N', 'PoD Falling Bridge Ledge'), - ('PoD Falling Bridge Path S', 'PoD Falling Bridge'), + ('PoD Falling Bridge Path N', 'PoD Falling Bridge Mid'), + ('PoD Falling Bridge Path S', 'PoD Falling Bridge Mid'), + ('PoD Falling Bridge Mid Path S', 'PoD Falling Bridge'), + ('PoD Falling Bridge Mid Path N', 'PoD Falling Bridge Ledge'), ('PoD Bow Statue Left to Right Barrier - Orange', 'PoD Bow Statue Right'), ('PoD Bow Statue Left to Right Bypass', 'PoD Bow Statue Right'), ('PoD Bow Statue Left to Crystal', 'PoD Bow Statue Left - Crystal'), @@ -3732,11 +3753,14 @@ logical_connections = [ ('Ice Cross Bottom Push Block Left', 'Ice Floor Switch'), ('Ice Cross Right Push Block Top', 'Ice Bomb Drop'), + ('Ice Bomb Drop Path', 'Ice Bomb Drop - Top'), ('Ice Conveyor to Crystal', 'Ice Conveyor - Crystal'), ('Ice Conveyor Crystal Exit', 'Ice Conveyor'), ('Ice Big Key Push Block', 'Ice Dead End'), ('Ice Bomb Jump Ledge Orange Barrier', 'Ice Bomb Jump Catwalk'), ('Ice Bomb Jump Catwalk Orange Barrier', 'Ice Bomb Jump Ledge'), + ('Ice Right H Path', 'Ice Hammer Block'), + ('Ice Hammer Block Path', 'Ice Right H'), ('Ice Hookshot Ledge Path', 'Ice Hookshot Balcony'), ('Ice Hookshot Balcony Path', 'Ice Hookshot Ledge'), ('Ice Crystal Right Orange Barrier', 'Ice Crystal Left'), @@ -4107,6 +4131,7 @@ interior_doors = [ ('PoD Lobby NE', 'PoD Middle Cage SE'), ('PoD Warp Hint SE', 'PoD Jelly Hall NE'), ('PoD Jelly Hall NW', 'PoD Mimics 1 SW'), + ('PoD Map Balcony ES', 'PoD Fairy Pool WS'), ('PoD Falling Bridge EN', 'PoD Compass Room WN'), ('PoD Compass Room SE', 'PoD Harmless Hellway NE'), ('PoD Mimics 2 NW', 'PoD Bow Statue SW'), diff --git a/Doors.py b/Doors.py index 4020caff..4c9611fc 100644 --- a/Doors.py +++ b/Doors.py @@ -250,7 +250,7 @@ def create_doors(world, player): create_door(player, 'Desert Tiles 2 NE', Intr).dir(No, 0x43, Right, High).small_key().pos(1), create_door(player, 'Desert Wall Slide SE', Intr).dir(So, 0x43, Right, High).small_key().pos(1), create_door(player, 'Desert Wall Slide NW', Nrml).dir(No, 0x43, Left, High).big_key().pos(0).no_entrance(), - create_door(player, 'Desert Boss SW', Nrml).dir(So, 0x33, Left, High).no_exit().trap(0x4).pos(0), # .portal(Z, 0x00), problem with enemizer + create_door(player, 'Desert Boss SW', Nrml).dir(So, 0x33, Left, High).no_exit().trap(0x4).pos(0).portal(Z, 0x00), # Hera create_door(player, 'Hera Lobby S', Nrml).dir(So, 0x77, Mid, Low).pos(0).portal(Z, 0x22, 1), @@ -410,6 +410,8 @@ def create_doors(world, player): create_door(player, 'PoD Map Balcony Drop Down', Lgcl), create_door(player, 'PoD Map Balcony to Ranged Crystal', Lgcl), create_door(player, 'PoD Map Balcony Ranged Crystal Exit', Lgcl), + create_door(player, 'PoD Map Balcony ES', Intr).dir(Ea, 0x2b, Bot, High).pos(0), + create_door(player, 'PoD Fairy Pool WS', Intr).dir(We, 0x2b, Bot, High).pos(0), create_door(player, 'PoD Map Balcony WS', Nrml).dir(We, 0x2b, Bot, High).pos(1), create_door(player, 'PoD Map Balcony South Stairs', StrS).dir(So, 0x2b, Left, High), create_door(player, 'PoD Conveyor North Stairs', StrS).dir(No, 0x3b, Left, High), @@ -425,6 +427,8 @@ def create_doors(world, player): create_door(player, 'PoD Falling Bridge EN', Intr).dir(Ea, 0x1a, Top, High).pos(4), create_door(player, 'PoD Falling Bridge Path N', Lgcl), create_door(player, 'PoD Falling Bridge Path S', Lgcl), + create_door(player, 'PoD Falling Bridge Mid Path S', Lgcl), + create_door(player, 'PoD Falling Bridge Mid Path N', Lgcl), create_door(player, 'PoD Big Chest Balcony W', Nrml).dir(We, 0x1a, Mid, High).pos(2), create_door(player, 'PoD Dark Maze EN', Nrml).dir(Ea, 0x19, Top, High).small_key().pos(1), create_door(player, 'PoD Dark Maze E', Nrml).dir(Ea, 0x19, Mid, High).pos(0), @@ -736,6 +740,7 @@ def create_doors(world, player): create_door(player, 'Ice Cross Top Push Block Right', Lgcl), # dynamic create_door(player, 'Ice Cross Bottom SE', Nrml).dir(So, 0x1e, Right, High).pos(3).portal(X, 0x00), create_door(player, 'Ice Cross Right ES', Nrml).dir(Ea, 0x1e, Bot, High).trap(0x4).pos(0), + create_door(player, 'Ice Bomb Drop Path', Lgcl), create_door(player, 'Ice Bomb Drop Hole', Hole), create_door(player, 'Ice Compass Room NE', Nrml).dir(No, 0x2e, Right, High).pos(0), create_door(player, 'Ice Pengator Switch WS', Nrml).dir(We, 0x1f, Bot, High).trap(0x4).pos(0), @@ -767,6 +772,8 @@ def create_doors(world, player): create_door(player, 'Ice Spike Room WS', Nrml).dir(We, 0x5f, Bot, High).small_key().pos(0), create_door(player, 'Ice Spike Room Down Stairs', Sprl).dir(Dn, 0x5f, 3, HTH).ss(Z, 0x11, 0x48, True, True), create_door(player, 'Ice Spike Room Up Stairs', Sprl).dir(Up, 0x5f, 4, HTH).ss(Z, 0x1a, 0xa4, True, True), + create_door(player, 'Ice Right H Path', Lgcl), + create_door(player, 'Ice Hammer Block Path', Lgcl), create_door(player, 'Ice Hammer Block Down Stairs', Sprl).dir(Dn, 0x3f, 0, HTH).ss(Z, 0x11, 0xb8, True, True).kill(), create_door(player, 'Ice Hammer Block ES', Intr).dir(Ea, 0x3f, Bot, High).pos(0), create_door(player, 'Ice Tongue Pull WS', Intr).dir(We, 0x3f, Bot, High).pos(0), @@ -1067,7 +1074,7 @@ def create_doors(world, player): create_door(player, 'TR Final Abyss Balcony Path', Lgcl), create_door(player, 'TR Final Abyss Ledge Path', Lgcl), create_door(player, 'TR Final Abyss NW', Nrml).dir(No, 0xb4, Left, High).big_key().pos(0), - create_door(player, 'TR Boss SW', Nrml).dir(So, 0xa4, Left, High).no_exit().trap(0x4).pos(0), # .portal(Z, 0x00), -enemizer doesn't work + create_door(player, 'TR Boss SW', Nrml).dir(So, 0xa4, Left, High).no_exit().trap(0x4).pos(0).portal(Z, 0x00), create_door(player, 'GT Lobby S', Nrml).dir(So, 0x0c, Mid, High).pos(0).portal(Z, 0x22), create_door(player, 'GT Lobby Left Down Stairs', Sprl).dir(Dn, 0x0c, 1, HTL).ss(A, 0x0f, 0x80), @@ -1499,14 +1506,14 @@ def create_doors(world, player): # static portal flags world.get_door('Sanctuary S', player).dead_end(allowPassage=True) - if world.mode[player] == 'open' and world.shuffle[player] not in ['crossed', 'insanity']: + if world.mode[player] == 'open' and world.shuffle[player] not in ['lean', 'swapped', 'crossed', 'insanity']: world.get_door('Sanctuary S', player).lw_restricted = True world.get_door('Eastern Hint Tile Blocked Path SE', player).passage = False world.get_door('TR Big Chest Entrance SE', player).passage = False world.get_door('Sewers Secret Room Key Door S', player).dungeonLink = 'Hyrule Castle' world.get_door('Desert Cannonball S', player).dead_end() - # world.get_door('Desert Boss SW', player).dead_end() - # world.get_door('Desert Boss SW', player).dungeonLink = 'Desert Palace' + world.get_door('Desert Boss SW', player).dead_end() + world.get_door('Desert Boss SW', player).dungeonLink = 'Desert Palace' world.get_door('Skull 1 Lobby S', player).dungeonLink = 'Skull Woods' world.get_door('Skull Map Room SE', player).dungeonLink = 'Skull Woods' world.get_door('Skull Spike Corner SW', player).dungeonLink = 'Skull Woods' @@ -1516,8 +1523,8 @@ def create_doors(world, player): world.get_door('Mire Right Bridge SE', player).dead_end(allowPassage=True) world.get_door('TR Roller Room SW', player).dead_end() world.get_door('TR Tile Room SE', player).dead_end() - # world.get_door('TR Boss SW', player).dead_end() - # world.get_door('TR Boss SW', player).dungeonLink = 'Turtle Rock' + world.get_door('TR Boss SW', player).dead_end() + world.get_door('TR Boss SW', player).dungeonLink = 'Turtle Rock' world.get_door('GT Petting Zoo SE', player).dead_end() world.get_door('GT DMs Room SW', player).dead_end() world.get_door("GT Bob\'s Room SE", player).passage = False diff --git a/DungeonGenerator.py b/DungeonGenerator.py index d96b654a..5f34fbf2 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -1360,7 +1360,7 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player, dunge for name, builder in dungeon_map.items(): calc_allowance_and_dead_ends(builder, connections_tuple, world, player) - if world.mode[player] == 'open' and world.shuffle[player] not in ['crossed', 'insanity']: + if world.mode[player] == 'open' and world.shuffle[player] not in ['lean', 'swapped', 'crossed', 'insanity']: sanc = find_sector('Sanctuary', candidate_sectors) if sanc: # only run if sanc if a candidate lw_builders = [] @@ -1538,8 +1538,8 @@ def calc_allowance_and_dead_ends(builder, connections_tuple, world, player): if entrance in connections.keys(): enabling_region = connections[entrance] check_list = list(potentials[enabling_region]) - if enabling_region.name in ['Desert Ledge', 'Desert Palace Entrance (North) Spot']: - alternate = 'Desert Palace Entrance (North) Spot' if enabling_region.name == 'Desert Ledge' else 'Desert Ledge' + if enabling_region.name in ['Desert Ledge', 'Desert Ledge Keep']: + alternate = 'Desert Ledge Keep' if enabling_region.name == 'Desert Ledge' else 'Desert Ledge' if world.get_region(alternate, player) in potentials: check_list.extend(potentials[world.get_region(alternate, player)]) connecting_entrances = [x for x in check_list if x != entrance and x not in dead_entrances and x not in drop_entrances_allowance] diff --git a/Dungeons.py b/Dungeons.py index 3835ef62..3284617e 100644 --- a/Dungeons.py +++ b/Dungeons.py @@ -98,10 +98,11 @@ pod_regions = [ 'PoD Lobby', 'PoD Left Cage', 'PoD Middle Cage', 'PoD Shooter Room', 'PoD Pit Room', 'PoD Pit Room Blocked', 'PoD Arena Main', 'PoD Arena Main - Ranged Crystal', 'PoD Arena North', 'PoD Arena Bridge', 'PoD Arena Bridge - Ranged Crystal', 'PoD Arena Landing', 'PoD Arena Right', 'PoD Arena Right - Ranged Crystal', 'PoD Arena Ledge', 'PoD Arena Ledge - Ranged Crystal', 'PoD Sexy Statue', - 'PoD Map Balcony', 'PoD Map Balcony - Ranged Crystal', 'PoD Conveyor', 'PoD Mimics 1', 'PoD Jelly Hall', 'PoD Warp Hint', 'PoD Warp Room', - 'PoD Stalfos Basement', 'PoD Basement Ledge', 'PoD Big Key Landing', 'PoD Falling Bridge', - 'PoD Falling Bridge Ledge', 'PoD Dark Maze', 'PoD Big Chest Balcony', 'PoD Compass Room', 'PoD Dark Basement', - 'PoD Harmless Hellway', 'PoD Mimics 2', 'PoD Bow Statue Left', 'PoD Bow Statue Left - Crystal', 'PoD Bow Statue Right', 'PoD Bow Statue Right - Ranged Crystal', + 'PoD Map Balcony', 'PoD Map Balcony - Ranged Crystal', 'PoD Fairy Pool', 'PoD Conveyor', 'PoD Mimics 1', + 'PoD Jelly Hall', 'PoD Warp Hint', 'PoD Warp Room', 'PoD Stalfos Basement', 'PoD Basement Ledge', + 'PoD Big Key Landing', 'PoD Falling Bridge', 'PoD Falling Bridge Mid', 'PoD Falling Bridge Ledge', 'PoD Dark Maze', + 'PoD Big Chest Balcony', 'PoD Compass Room', 'PoD Dark Basement', 'PoD Harmless Hellway', 'PoD Mimics 2', + 'PoD Bow Statue Left', 'PoD Bow Statue Left - Crystal', 'PoD Bow Statue Right', 'PoD Bow Statue Right - Ranged Crystal', 'PoD Dark Pegs Landing', 'PoD Dark Pegs Right', 'PoD Dark Pegs Middle', 'PoD Dark Pegs Left', 'PoD Dark Pegs Landing - Ranged Crystal', 'PoD Dark Pegs Middle - Ranged Crystal', 'PoD Dark Pegs Left - Ranged Crystal', 'PoD Lonely Turtle', 'PoD Turtle Party', 'PoD Dark Alley', 'PoD Callback', 'PoD Boss', 'Palace of Darkness Portal' @@ -144,7 +145,8 @@ thieves_regions = [ ice_regions = [ 'Ice Lobby', 'Ice Jelly Key', 'Ice Floor Switch', 'Ice Cross Left', 'Ice Cross Bottom', 'Ice Cross Right', 'Ice Cross Top', 'Ice Compass Room', 'Ice Pengator Switch', 'Ice Dead End', 'Ice Big Key', 'Ice Bomb Drop', - 'Ice Stalfos Hint', 'Ice Conveyor', 'Ice Conveyor - Crystal', 'Ice Bomb Jump Ledge', 'Ice Bomb Jump Catwalk', 'Ice Narrow Corridor', + 'Ice Stalfos Hint', 'Ice Conveyor', 'Ice Conveyor - Crystal', 'Ice Bomb Jump Ledge', 'Ice Bomb Jump Catwalk', + 'Ice Narrow Corridor', 'Ice Right H', 'Ice Bomb Drop - Top', 'Ice Pengator Trap', 'Ice Spike Cross', 'Ice Firebar', 'Ice Falling Square', 'Ice Spike Room', 'Ice Hammer Block', 'Ice Tongue Pull', 'Ice Freezors', 'Ice Freezors Ledge', 'Ice Tall Hint', 'Ice Hookshot Ledge', 'Ice Hookshot Balcony', 'Ice Spikeball', 'Ice Lonely Freezor', 'Iced T', 'Ice Catwalk', 'Ice Many Pots', diff --git a/ER_hint_reference.txt b/ER_hint_reference.txt index 018dfb3a..2752961a 100644 --- a/ER_hint_reference.txt +++ b/ER_hint_reference.txt @@ -188,7 +188,7 @@ Dark World Shop: The hammer sealed building Red Shield Shop: The fenced in building Mire Shed: The western hut in the mire East Dark World Hint: The dark cave near the eastmost portal -Dark Desert Hint: The cave east of the mire +Mire Hint: The cave east of the mire Spike Cave: The ledge cave on west dark DM Palace of Darkness Hint: The building south of Kiki Dark Lake Hylia Ledge Spike Cave: The rock SE dark Lake Hylia @@ -200,7 +200,7 @@ Hype Cave: The cave south of the old bomb shop Brewery: The Village of Outcasts building with no door Dark Lake Hylia Ledge Hint: The open cave SE dark Lake Hylia Chest Game: The westmost building in the Village of Outcasts -Dark Desert Fairy: The eastern hut in the mire +Mire Fairy: The eastern hut in the mire Dark Lake Hylia Ledge Fairy: The sealed cave SE dark Lake Hylia Fortune Teller (Dark): The building NE the Village of Outcasts Sanctuary: Sanctuary diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 3a44b677..84caaaf1 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -20,13 +20,6 @@ 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) - else: - for exitname, regionname in inverted_mandatory_connections: - connect_simple(world, exitname, regionname, player) - connect_custom(world, player) # if we do not shuffle, set default connections @@ -836,6 +829,7 @@ def link_entrances(world, player): random.shuffle(hole_targets) random.shuffle(exit_pool) + # fill up holes for hole in hole_entrances: connect_entrance(world, hole, hole_targets.pop(), player) @@ -851,6 +845,7 @@ def link_entrances(world, player): caves.append(('Hyrule Castle Exit (South)', 'Hyrule Castle Exit (West)', 'Hyrule Castle Exit (East)')) if not invFlag: exit_pool.append('Hyrule Castle Entrance (South)') + random.shuffle(doors) # place links house if world.mode[player] == 'standard' or not world.shufflelinks[player]: @@ -1104,15 +1099,16 @@ def connect_random(world, exitlist, targetlist, player, two_way=False): def connect_mandatory_exits(world, entrances, caves, must_be_exits, player): # Keeps track of entrances that cannot be used to access each exit / cave - if world.mode[player] == 'inverted': + inverted = world.mode[player] == 'inverted' + if inverted: invalid_connections = Inverted_Must_Exit_Invalid_Connections.copy() else: invalid_connections = Must_Exit_Invalid_Connections.copy() invalid_cave_connections = defaultdict(set) - if world.logic[player] in ['owglitches', 'nologic']: + if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']: import OverworldGlitchRules - for entrance in OverworldGlitchRules.get_non_mandatory_exits(world.mode[player] == 'inverted'): + for entrance in OverworldGlitchRules.inverted_non_mandatory_exits if inverted else OverworldGlitchRules.open_non_mandatory_exits: invalid_connections[entrance] = set() if entrance in must_be_exits: must_be_exits.remove(entrance) @@ -1123,7 +1119,7 @@ def connect_mandatory_exits(world, entrances, caves, must_be_exits, player): random.shuffle(caves) # Handle inverted Aga Tower - if it depends on connections, then so does Hyrule Castle Ledge - if world.mode[player] == 'inverted': + if inverted: for entrance in invalid_connections: if world.get_entrance(entrance, player).connected_region == world.get_region('Agahnims Tower Portal', player): for exit in invalid_connections[entrance]: @@ -1576,7 +1572,7 @@ DW_Single_Cave_Doors = ['Bonk Fairy (Dark)', 'Red Shield Shop', 'Mire Shed', 'East Dark World Hint', - 'Dark Desert Hint', + 'Mire Hint', 'Spike Cave', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', @@ -1589,7 +1585,7 @@ DW_Single_Cave_Doors = ['Bonk Fairy (Dark)', 'Brewery', 'Dark Lake Hylia Ledge Hint', 'Chest Game', - 'Dark Desert Fairy', + 'Mire Fairy', 'Dark Lake Hylia Ledge Fairy', 'Fortune Teller (Dark)', 'Hammer Peg Cave'] @@ -1654,8 +1650,8 @@ Bomb_Shop_Single_Cave_Doors = ['Waterfall of Wishing', 'Dark Potion Shop', 'Archery Game', 'Mire Shed', - 'Dark Desert Hint', - 'Dark Desert Fairy', + 'Mire Hint', + 'Mire Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Dark Death Mountain Fairy', @@ -1720,8 +1716,8 @@ Single_Cave_Targets = ['Blinds Hideout', 'Dark Lumberjack Shop', 'Archery Game', 'Mire Shed', - 'Dark Desert Hint', - 'Dark Desert Healer Fairy', + 'Mire Hint', + 'Mire Healer Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Dark Death Mountain Healer Fairy', @@ -1864,7 +1860,7 @@ Inverted_DW_Single_Cave_Doors = ['Bonk Fairy (Dark)', 'Red Shield Shop', 'Mire Shed', 'East Dark World Hint', - 'Dark Desert Hint', + 'Mire Hint', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', 'Dark Death Mountain Shop', @@ -1876,7 +1872,7 @@ Inverted_DW_Single_Cave_Doors = ['Bonk Fairy (Dark)', 'Brewery', 'Dark Lake Hylia Ledge Hint', 'Chest Game', - 'Dark Desert Fairy', + 'Mire Fairy', 'Dark Lake Hylia Ledge Fairy', 'Fortune Teller (Dark)', 'Hammer Peg Cave'] @@ -1911,8 +1907,8 @@ Inverted_Bomb_Shop_Single_Cave_Doors = ['Waterfall of Wishing', 'Dark Potion Shop', 'Archery Game', 'Mire Shed', - 'Dark Desert Hint', - 'Dark Desert Fairy', + 'Mire Hint', + 'Mire Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Bumper Cave (Top)', @@ -2007,8 +2003,8 @@ Inverted_Single_Cave_Targets = ['Blinds Hideout', 'Dark Lumberjack Shop', 'Archery Game', 'Mire Shed', - 'Dark Desert Hint', - 'Dark Desert Healer Fairy', + 'Mire Hint', + 'Mire Healer Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Dark Death Mountain Healer Fairy', @@ -2064,16 +2060,18 @@ Inverted_Must_Exit_Invalid_Connections = defaultdict(set, { # 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'), - - # underworld +mandatory_connections = [# underworld ('Lost Woods Hideout (top to bottom)', 'Lost Woods Hideout (bottom)'), ('Lumberjack Tree (top to bottom)', 'Lumberjack Tree (bottom)'), ('Death Mountain Return Cave E', 'Death Mountain Return Cave (right)'), ('Death Mountain Return Cave W', 'Death Mountain Return Cave (left)'), - ('Old Man Cave Dropdown', 'Old Man Cave'), - ('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'), - ('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (Bottom)'), + ('Old Man Cave Dropdown', 'Old Man Cave (West)'), + ('Old Man Cave W', 'Old Man Cave (West)'), + ('Old Man Cave E', 'Old Man Cave (East)'), + ('Spectacle Rock Cave Drop', 'Spectacle Rock Cave Pool'), + ('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Pool'), + ('Spectacle Rock Cave West Edge', 'Spectacle Rock Cave (Bottom)'), + ('Spectacle Rock Cave East Edge', 'Spectacle Rock Cave Pool'), ('Old Man House Front to Back', 'Old Man House Back'), ('Old Man House Back to Front', 'Old Man House'), ('Spiral Cave (top to bottom)', 'Spiral Cave (Bottom)'), @@ -2091,222 +2089,36 @@ mandatory_connections = [('Links House S&Q', 'Links House'), ('Kakariko Well (top to back)', 'Kakariko Well (back)'), ('Blinds Hideout N', 'Blinds Hideout (Top)'), ('Bat Cave Door', 'Bat Cave (left)'), + ('Good Bee Cave Front to Back', 'Good Bee Cave (back)'), + ('Good Bee Cave Back to Front', 'Good Bee Cave'), + ('Capacity Upgrade East', 'Capacity Fairy Pool'), + ('Capacity Fairy Pool West', 'Capacity Upgrade'), + ('Bonk Fairy (Dark) Pool', 'Bonk Fairy Pool'), + ('Bonk Fairy (Light) Pool', 'Bonk Fairy Pool'), ('Hookshot Cave Front to Middle', 'Hookshot Cave (Middle)'), ('Hookshot Cave Middle to Front', 'Hookshot Cave (Front)'), ('Hookshot Cave Middle to Back', 'Hookshot Cave (Back)'), ('Hookshot Cave Back to Middle', 'Hookshot Cave (Middle)'), + ('Hookshot Cave Back to Fairy', 'Hookshot Cave (Fairy Pool)'), + ('Hookshot Cave Fairy to Back', 'Hookshot Cave (Back)'), ('Hookshot Cave Bonk Path', 'Hookshot Cave (Bonk Islands)'), ('Hookshot Cave Hook Path', 'Hookshot Cave (Hook Islands)'), ('Superbunny Cave Climb', 'Superbunny Cave (Top)'), ('Bumper Cave Bottom to Top', 'Bumper Cave (top)'), ('Bumper Cave Top To Bottom', 'Bumper Cave (bottom)'), - ('Ganon Drop', 'Bottom of Pyramid'), - - # water entry - ('Waterfall Fairy Access', 'Zora Waterfall Entryway'), - ('Zora Waterfall Water Drop', 'Lake Hylia Water'), - ('Light World Water Drop', 'Lake Hylia Water'), - ('Potion Shop Water Drop', 'Lake Hylia Water'), - ('Northeast Light World Water Drop', 'Lake Hylia Water'), - ('Lake Hylia Central Island Water Drop', 'Lake Hylia Water'), - - ('West Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Northeast Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Catfish Water Drop', 'Dark Lake Hylia Water'), - ('East Dark World Water Drop', 'Dark Lake Hylia Water'), - ('South Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Southeast Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Ice Palace Leave Water Drop', 'Dark Lake Hylia Water'), - - # water exit - ('Light World Pier', 'Light World'), # there are several piers in-game, only one needs to be modeled - ('Potion Shop Pier', 'Potion Shop Area'), - ('Hobo Pier', 'Hobo Bridge'), - ('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'), - ('Lake Hylia Whirlpool', 'Northeast Light World'), - - ('Northeast Dark World Pier', 'Northeast Dark World'), - ('East Dark World Pier', 'East Dark World'), - ('Southeast Dark World Pier', 'Southeast Dark World'), - - # terrain - ('Master Sword Meadow', 'Master Sword Meadow'), - ('DM Hammer Bridge (West)', 'East Death Mountain (Top)'), - ('DM Hammer Bridge (East)', 'West Death Mountain (Top)'), - ('DM Broken Bridge (West)', 'East Death Mountain (Bottom)'), - ('DM Broken Bridge (East)', 'West Death Mountain (Bottom)'), - ('Fairy Ascension Rocks', 'Fairy Ascension Plateau'), - ('Death Mountain Entrance Rock', 'Death Mountain Entrance'), - ('Zoras Domain', 'Zoras Domain'), - ('Kings Grave Rocks (Outer)', 'Kings Grave Area'), - ('Kings Grave Rocks (Inner)', 'Light World'), - ('Potion Shop Rock (South)', 'Northeast Light World'), - ('Potion Shop Rock (North)', 'Potion Shop Area'), - ('Kakariko Southwest Bush (North)', 'Bomb Hut Area'), - ('Kakariko Southwest Bush (South)', 'Light World'), - ('Kakariko Yard Bush (North)', 'Light World'), - ('Kakariko Yard Bush (South)', 'Bush Covered Lawn'), - ('Hyrule Castle Courtyard Bush (North)', 'Hyrule Castle Courtyard'), - ('Hyrule Castle Courtyard Bush (South)', 'Hyrule Castle Secret Entrance Area'), - ('Hyrule Castle Main Gate', 'Hyrule Castle Courtyard'), - ('Hyrule Castle Main Gate (North)', 'Light World'), - ('Wooden Bridge Bush (North)', 'Light World'), - ('Wooden Bridge Bush (South)', 'Potion Shop Area'), - ('Bat Cave Ledge Peg', 'Bat Cave Ledge'), - ('Bat Cave Ledge Peg (East)', 'Light World'), - ('Desert Statue Move', 'Desert Palace Stairs'), - ('Desert Ledge Rocks (Outer)', 'Desert Palace Entrance (North) Spot'), - ('Desert Ledge Rocks (Inner)', 'Desert Ledge'), - - ('Skull Woods Forest', 'Skull Woods Forest'), - ('East Dark Death Mountain Bushes', 'East Dark Death Mountain (Bushes)'), - ('Bumper Cave Entrance Rock', 'Bumper Cave Entrance'), - ('Dark Witch Rock (North)', 'Northeast Dark World'), - ('Dark Witch Rock (South)', 'Catfish Area'), - ('Grassy Lawn Pegs (Top)', 'West Dark World'), - ('Grassy Lawn Pegs (Bottom)', 'Dark Grassy Lawn'), - ('West Dark World Gap', 'West Dark World'), - ('Broken Bridge Pass (Top)', 'East Dark World'), - ('Broken Bridge Pass (Bottom)', 'Northeast Dark World'), - ('Dark Graveyard Bush (South)', 'Dark Graveyard North'), - ('Dark Graveyard Bush (North)', 'West Dark World'), - ('Peg Area Rocks (Left)', 'Hammer Peg Area'), - ('Peg Area Rocks (Right)', 'West Dark World'), - ('Village of Outcasts Heavy Rock', 'West Dark World'), - ('Hammer Bridge Pegs (North)', 'South Dark World'), - ('Hammer Bridge Pegs (South)', 'East Dark World'), - ('Ice Island To East Pier', 'East Dark World'), - - # ledge drops - ('Spectacle Rock Drop', 'West Death Mountain (Top)'), - ('Death Mountain Drop', 'West Death Mountain (Bottom)'), - ('Spiral Cave Ledge Access', 'Spiral Cave Ledge'), - ('Fairy Ascension Ledge Access', 'Fairy Ascension Ledge'), - ('East Death Mountain Drop', 'East Death Mountain (Bottom)'), - ('Spiral Cave Ledge Drop', 'East Death Mountain (Bottom)'), - ('Fairy Ascension Ledge Drop', 'Fairy Ascension Plateau'), - ('Fairy Ascension Drop', 'East Death Mountain (Bottom)'), - ('Death Mountain Entrance Drop', 'Light World'), - ('Death Mountain Return Ledge Drop', 'Light World'), - ('Graveyard Ledge Drop', 'Light World'), - ('Hyrule Castle Ledge Courtyard Drop', 'Hyrule Castle Courtyard'), - ('Hyrule Castle Ledge Drop', 'Light World'), - ('Maze Race Ledge Drop', 'Light World'), - ('Desert Ledge Drop', 'Light World'), - ('Desert Palace Mouth Drop', 'Light World'), - ('Checkerboard Ledge Drop', 'Light World'), - ('Desert Teleporter Drop', 'Light World'), - ('Cave 45 Ledge Drop', 'Light World'), - - ('Dark Death Mountain Drop (West)', 'West Dark Death Mountain (Bottom)'), - ('Dark Death Mountain Drop (East)', 'East Dark Death Mountain (Bottom)'), - ('Floating Island Drop', 'Dark Death Mountain (Top)'), - ('Turtle Rock Drop', 'Dark Death Mountain (Top)'), - ('Bumper Cave Entrance Drop', 'West Dark World'), - ('Bumper Cave Ledge Drop', 'West Dark World'), - ('Pyramid Drop', 'East Dark World'), - ('Village of Outcasts Drop', 'South Dark World'), - ('Dark Desert Drop', 'Dark Desert') + ('Ganon Drop', 'Bottom of Pyramid') ] -open_mandatory_connections = [('Sanctuary S&Q', 'Sanctuary'), - ('Old Man S&Q', 'Old Man House'), - ('Other World S&Q', 'East Dark World'), - - # flute - ('Flute Spot 1', 'West Death Mountain (Bottom)'), - ('Flute Spot 2', 'Potion Shop Area'), - ('Flute Spot 3', 'Light World'), - ('Flute Spot 4', 'Light World'), - ('Flute Spot 5', 'Light World'), - ('Flute Spot 6', 'Desert Teleporter Ledge'), - ('Flute Spot 7', 'Light World'), - ('Flute Spot 8', 'Light World'), - ('LW Flute', 'Flute Sky'), - ('NWLW Flute', 'Flute Sky'), - ('ZLW Flute', 'Flute Sky'), - ('DM Flute', 'Flute Sky'), - ('EDM Flute', 'Flute Sky'), - - # portals - ('Death Mountain Teleporter', 'West Dark Death Mountain (Bottom)'), - ('East Death Mountain Teleporter', 'East Dark Death Mountain (Bottom)'), - ('Turtle Rock Teleporter', 'Turtle Rock (Top)'), - ('Kakariko Teleporter', 'West Dark World'), - ('Castle Gate Teleporter', 'East Dark World'), - ('East Hyrule Teleporter', 'East Dark World'), - ('South Hyrule Teleporter', 'South Dark World'), - ('Desert Teleporter', 'Dark Desert'), - ('Lake Hylia Teleporter', 'Dark Lake Hylia Central Island') - ] - -inverted_mandatory_connections = [('Sanctuary S&Q', 'Dark Sanctuary Hint'), - ('Old Man S&Q', 'West Dark Death Mountain (Bottom)'), - ('Other World S&Q', 'Hyrule Castle Ledge'), - - # flute - ('Flute Spot 1', 'West Dark Death Mountain (Bottom)'), - ('Flute Spot 2', 'Northeast Dark World'), - ('Flute Spot 3', 'West Dark World'), - ('Flute Spot 4', 'South Dark World'), - ('Flute Spot 5', 'East Dark World'), - ('Flute Spot 6', 'Dark Desert Ledge'), - ('Flute Spot 7', 'South Dark World'), - ('Flute Spot 8', 'Southeast Dark World'), - ('DDM Flute', 'Flute Sky'), - ('NEDW Flute', 'Flute Sky'), - ('WDW Flute', 'Flute Sky'), - ('SDW Flute', 'Flute Sky'), - ('EDW Flute', 'Flute Sky'), - ('DD Flute', 'Flute Sky'), - ('DLHL Flute', 'Flute Sky'), - ('EDDM Flute', 'Flute Sky'), - ('Dark Grassy Lawn Flute', 'Flute Sky'), - ('Hammer Peg Area Flute', 'Flute Sky'), - - # modified terrain - ('Spectacle Rock Approach', 'Spectacle Rock'), - ('Spectacle Rock Leave', 'West Death Mountain (Top)'), - ('Floating Island Bridge (West)', 'East Death Mountain (Top)'), - ('Floating Island Bridge (East)', 'Death Mountain Floating Island'), - ('Graveyard Ladder (Top)', 'Light World'), - ('Graveyard Ladder (Bottom)', 'Graveyard Ledge'), - ('Mimic Cave Ledge Access', 'Mimic Cave Ledge'), - ('Mimic Cave Ledge Drop', 'East Death Mountain (Bottom)'), - ('Checkerboard Ledge Approach', 'Desert Checkerboard Ledge'), - ('Checkerboard Ledge Leave', 'Light World'), - ('Cave 45 Approach', 'Cave 45 Ledge'), - ('Cave 45 Leave', 'Light World'), - ('Lake Hylia Island Pier', 'Lake Hylia Island'), - ('Bombos Tablet Ladder (Top)', 'Light World'), - ('Bombos Tablet Ladder (Bottom)', 'Bombos Tablet Ledge'), - ('Dark Death Mountain Ladder (Top)', 'West Dark Death Mountain (Bottom)'), - ('Dark Death Mountain Ladder (Bottom)', 'Dark Death Mountain (Top)'), - ('Turtle Rock Tail Drop', 'Turtle Rock (Top)'), - ('Ice Palace Approach', 'Dark Lake Hylia Central Island'), - - # portals - ('Dark Death Mountain Teleporter (West)', 'West Death Mountain (Bottom)'), - ('East Dark Death Mountain Teleporter (Bottom)', 'East Death Mountain (Bottom)'), - ('East Dark Death Mountain Teleporter (Top)', 'East Death Mountain (Top)'), - ('West Dark World Teleporter', 'Light World'), - ('Post Aga Teleporter', 'Light World'), - ('East Dark World Teleporter', 'Light World'), - ('South Dark World Teleporter', 'Light World'), - ('Dark Desert Teleporter', 'Light World'), - ('Dark Lake Hylia Teleporter', 'Lake Hylia Central Island') - ] - # non-shuffled entrance links default_connections = [('Lost Woods Gamble', 'Lost Woods Gamble'), ('Lost Woods Hideout Drop', 'Lost Woods Hideout (top)'), ('Lost Woods Hideout Stump', 'Lost Woods Hideout (bottom)'), - ('Lost Woods Hideout Exit', 'Light World'), + ('Lost Woods Hideout Exit', 'Lost Woods East Area'), ('Lumberjack House', 'Lumberjack House'), ('Lumberjack Tree Tree', 'Lumberjack Tree (top)'), ('Lumberjack Tree Cave', 'Lumberjack Tree (bottom)'), - ('Lumberjack Tree Exit', 'Light World'), + ('Lumberjack Tree Exit', 'Lumberjack Area'), ('Death Mountain Return Cave (East)', 'Death Mountain Return Cave (right)'), ('Death Mountain Return Cave Exit (East)', 'West Death Mountain (Bottom)'), ('Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Peak)'), @@ -2334,27 +2146,27 @@ default_connections = [('Lost Woods Gamble', 'Lost Woods Gamble'), ('Paradox Cave (Top)', 'Paradox Cave'), ('Paradox Cave Exit (Bottom)', 'East Death Mountain (Bottom)'), ('Paradox Cave Exit (Middle)', 'East Death Mountain (Bottom)'), - ('Paradox Cave Exit (Top)', 'East Death Mountain (Top)'), + ('Paradox Cave Exit (Top)', 'East Death Mountain (Top East)'), ('Waterfall of Wishing', 'Waterfall of Wishing'), ('Fortune Teller (Light)', 'Fortune Teller (Light)'), ('Bonk Rock Cave', 'Bonk Rock Cave'), ('Sanctuary', 'Sanctuary Portal'), - ('Sanctuary Exit', 'Light World'), + ('Sanctuary Exit', 'Sanctuary Area'), ('Sanctuary Grave', 'Sewer Drop'), ('Graveyard Cave', 'Graveyard Cave'), ('Kings Grave', 'Kings Grave'), ('North Fairy Cave Drop', 'North Fairy Cave'), ('North Fairy Cave', 'North Fairy Cave'), - ('North Fairy Cave Exit', 'Light World'), + ('North Fairy Cave Exit', 'River Bend Area'), ('Potion Shop', 'Potion Shop'), ('Kakariko Well Drop', 'Kakariko Well (top)'), ('Kakariko Well Cave', 'Kakariko Well (bottom)'), - ('Kakariko Well Exit', 'Light World'), + ('Kakariko Well Exit', 'Kakariko Village'), ('Blinds Hideout', 'Blinds Hideout'), ('Elder House (West)', 'Elder House'), ('Elder House (East)', 'Elder House'), - ('Elder House Exit (West)', 'Light World'), - ('Elder House Exit (East)', 'Light World'), + ('Elder House Exit (West)', 'Kakariko Village'), + ('Elder House Exit (East)', 'Kakariko Village'), ('Snitch Lady (West)', 'Snitch Lady (West)'), ('Snitch Lady (East)', 'Snitch Lady (East)'), ('Bush Covered House', 'Bush Covered House'), @@ -2366,16 +2178,16 @@ default_connections = [('Lost Woods Gamble', 'Lost Woods Gamble'), ('Tavern (Front)', 'Tavern (Front)'), ('Hyrule Castle Secret Entrance Drop', 'Hyrule Castle Secret Entrance'), ('Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Secret Entrance'), - ('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Secret Entrance Area'), + ('Hyrule Castle Secret Entrance Exit', 'Hyrule Castle Courtyard Northeast'), ('Sahasrahlas Hut', 'Sahasrahlas Hut'), ('Blacksmiths Hut', 'Blacksmiths Hut'), ('Bat Cave Drop', 'Bat Cave (right)'), ('Bat Cave Cave', 'Bat Cave (left)'), - ('Bat Cave Exit', 'Light World'), + ('Bat Cave Exit', 'Blacksmith Area'), ('Two Brothers House (West)', 'Two Brothers House'), ('Two Brothers House Exit (West)', 'Maze Race Ledge'), ('Two Brothers House (East)', 'Two Brothers House'), - ('Two Brothers House Exit (East)', 'Light World'), + ('Two Brothers House Exit (East)', 'Kakariko Suburb Area'), ('Library', 'Library'), ('Kakariko Gamble Game', 'Kakariko Gamble Game'), ('Bonk Fairy (Light)', 'Bonk Fairy (Light)'), @@ -2401,9 +2213,9 @@ default_connections = [('Lost Woods Gamble', 'Lost Woods Gamble'), ('Hookshot Cave Back Exit', 'Dark Death Mountain Floating Island'), ('Hookshot Cave Back Entrance', 'Hookshot Cave (Back)'), ('Hookshot Cave', 'Hookshot Cave (Front)'), - ('Hookshot Cave Front Exit', 'Dark Death Mountain (Top)'), + ('Hookshot Cave Front Exit', 'East Dark Death Mountain (Top)'), ('Superbunny Cave (Top)', 'Superbunny Cave (Top)'), - ('Superbunny Cave Exit (Top)', 'Dark Death Mountain (Top)'), + ('Superbunny Cave Exit (Top)', 'East Dark Death Mountain (Top)'), ('Superbunny Cave (Bottom)', 'Superbunny Cave (Bottom)'), ('Superbunny Cave Exit (Bottom)', 'East Dark Death Mountain (Bottom)'), ('Dark Death Mountain Shop', 'Dark Death Mountain Shop'), @@ -2423,8 +2235,8 @@ default_connections = [('Lost Woods Gamble', 'Lost Woods Gamble'), ('Dark Lake Hylia Fairy', 'Dark Lake Hylia Healer Fairy'), ('East Dark World Hint', 'East Dark World Hint'), ('Mire Shed', 'Mire Shed'), - ('Dark Desert Fairy', 'Dark Desert Healer Fairy'), - ('Dark Desert Hint', 'Dark Desert Hint'), + ('Mire Fairy', 'Mire Healer Fairy'), + ('Mire Hint', 'Mire Hint'), ('Hype Cave', 'Hype Cave'), ('Dark Lake Hylia Shop', 'Dark Lake Hylia Shop'), ('Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Healer Fairy'), @@ -2433,18 +2245,18 @@ default_connections = [('Lost Woods Gamble', 'Lost Woods Gamble'), ] open_default_connections = [('Links House', 'Links House'), - ('Links House Exit', 'Light World'), + ('Links House Exit', 'Links House Area'), ('Big Bomb Shop', 'Big Bomb Shop'), ('Old Man Cave (West)', 'Old Man Cave Ledge'), - ('Old Man Cave (East)', 'Old Man Cave'), - ('Old Man Cave Exit (West)', 'Light World'), + ('Old Man Cave (East)', 'Old Man Cave (East)'), + ('Old Man Cave Exit (West)', 'Mountain Pass Entry'), ('Old Man Cave Exit (East)', 'West Death Mountain (Bottom)'), ('Death Mountain Return Cave (West)', 'Death Mountain Return Cave (left)'), - ('Death Mountain Return Cave Exit (West)', 'Death Mountain Return Ledge'), + ('Death Mountain Return Cave Exit (West)', 'Mountain Pass Ledge'), ('Bumper Cave (Bottom)', 'Bumper Cave (bottom)'), ('Bumper Cave (Top)', 'Bumper Cave (top)'), ('Bumper Cave Exit (Top)', 'Bumper Cave Ledge'), - ('Bumper Cave Exit (Bottom)', 'West Dark World'), + ('Bumper Cave Exit (Bottom)', 'Bumper Cave Entry'), ('Dark Death Mountain Fairy', 'Dark Death Mountain Healer Fairy'), ('Pyramid Hole', 'Pyramid'), ('Pyramid Entrance', 'Bottom of Pyramid'), @@ -2452,20 +2264,20 @@ open_default_connections = [('Links House', 'Links House'), ] inverted_default_connections = [('Links House', 'Big Bomb Shop'), - ('Links House Exit', 'South Dark World'), + ('Links House Exit', 'Big Bomb Shop Area'), ('Big Bomb Shop', 'Links House'), - ('Dark Sanctuary Hint Exit', 'West Dark World'), + ('Dark Sanctuary Hint Exit', 'Dark Chapel Area'), ('Old Man Cave (West)', 'Bumper Cave (bottom)'), ('Old Man Cave (East)', 'Death Mountain Return Cave (left)'), - ('Old Man Cave Exit (West)', 'West Dark World'), + ('Old Man Cave Exit (West)', 'Bumper Cave Entry'), ('Old Man Cave Exit (East)', 'West Dark Death Mountain (Bottom)'), ('Death Mountain Return Cave (West)', 'Bumper Cave (top)'), ('Death Mountain Return Cave Exit (West)', 'West Death Mountain (Bottom)'), ('Bumper Cave (Bottom)', 'Old Man Cave Ledge'), ('Bumper Cave (Top)', 'Dark Death Mountain Healer Fairy'), - ('Bumper Cave Exit (Top)', 'Death Mountain Return Ledge'), - ('Bumper Cave Exit (Bottom)', 'Light World'), - ('Dark Death Mountain Fairy', 'Old Man Cave'), + ('Bumper Cave Exit (Top)', 'Mountain Pass Ledge'), + ('Bumper Cave Exit (Bottom)', 'Mountain Pass Entry'), + ('Dark Death Mountain Fairy', 'Old Man Cave (East)'), ('Inverted Pyramid Hole', 'Pyramid'), ('Inverted Pyramid Entrance', 'Bottom of Pyramid'), ('Pyramid Exit', 'Hyrule Castle Courtyard') @@ -2482,19 +2294,19 @@ default_dungeon_connections = [('Hyrule Castle Entrance (South)', 'Hyrule Castle ('Desert Palace Entrance (West)', 'Desert West Portal'), ('Desert Palace Entrance (North)', 'Desert Back Portal'), ('Desert Palace Entrance (East)', 'Desert East Portal'), - ('Desert Palace Exit (South)', 'Desert Palace Stairs'), + ('Desert Palace Exit (South)', 'Desert Stairs'), ('Desert Palace Exit (West)', 'Desert Ledge'), - ('Desert Palace Exit (East)', 'Desert Palace Mouth'), - ('Desert Palace Exit (North)', 'Desert Palace Entrance (North) Spot'), + ('Desert Palace Exit (East)', 'Desert Mouth'), + ('Desert Palace Exit (North)', 'Desert Ledge Keep'), ('Eastern Palace', 'Eastern Portal'), - ('Eastern Palace Exit', 'Light World'), + ('Eastern Palace Exit', 'Eastern Palace Area'), ('Tower of Hera', 'Hera Portal'), ('Tower of Hera Exit', 'West Death Mountain (Top)'), ('Palace of Darkness', 'Palace of Darkness Portal'), - ('Palace of Darkness Exit', 'East Dark World'), + ('Palace of Darkness Exit', 'Palace of Darkness Area'), ('Swamp Palace', 'Swamp Portal'), # requires additional patch for flooding moat if moved - ('Swamp Palace Exit', 'South Dark World'), + ('Swamp Palace Exit', 'Swamp Area'), ('Skull Woods First Section Hole (East)', 'Skull Pinball'), ('Skull Woods First Section Hole (West)', 'Skull Left Drop'), ('Skull Woods First Section Hole (North)', 'Skull Pot Circle'), @@ -2508,13 +2320,13 @@ default_dungeon_connections = [('Hyrule Castle Entrance (South)', 'Hyrule Castle ('Skull Woods Final Section', 'Skull 3 Portal'), ('Skull Woods Final Section Exit', 'Skull Woods Forest (West)'), ('Thieves Town', 'Thieves Town Portal'), - ('Thieves Town Exit', 'West Dark World'), + ('Thieves Town Exit', 'Village of Outcasts'), ('Ice Palace', 'Ice Portal'), - ('Ice Palace Exit', 'Dark Lake Hylia Central Island'), + ('Ice Palace Exit', 'Ice Palace Area'), ('Misery Mire', 'Mire Portal'), - ('Misery Mire Exit', 'Dark Desert'), + ('Misery Mire Exit', 'Mire Area'), ('Turtle Rock', 'Turtle Rock Main Portal'), - ('Turtle Rock Exit (Front)', 'Dark Death Mountain (Top)'), + ('Turtle Rock Exit (Front)', 'Turtle Rock Area'), ('Dark Death Mountain Ledge (West)', 'Turtle Rock Lazy Eyes Portal'), ('Dark Death Mountain Ledge (East)', 'Turtle Rock Chest Portal'), ('Turtle Rock Ledge Exit (West)', 'Dark Death Mountain Ledge'), @@ -2526,24 +2338,24 @@ default_dungeon_connections = [('Hyrule Castle Entrance (South)', 'Hyrule Castle open_default_dungeon_connections = [('Agahnims Tower', 'Agahnims Tower Portal'), ('Agahnims Tower Exit', 'Hyrule Castle Ledge'), ('Ganons Tower', 'Ganons Tower Portal'), - ('Ganons Tower Exit', 'Dark Death Mountain (Top)') + ('Ganons Tower Exit', 'West Dark Death Mountain (Top)') ] inverted_default_dungeon_connections = [('Agahnims Tower', 'Ganons Tower Portal'), - ('Agahnims Tower Exit', 'Dark Death Mountain (Top)'), + ('Agahnims Tower Exit', 'West Dark Death Mountain (Top)'), ('Ganons Tower', 'Agahnims Tower Portal'), ('Ganons Tower Exit', 'Hyrule Castle Ledge') ] indirect_connections = { - 'Turtle Rock (Top)': 'Turtle Rock', - 'East Dark World': 'Pyramid Fairy', + 'Turtle Rock Ledge': 'Turtle Rock', + 'Pyramid Area': 'Pyramid Fairy', 'Big Bomb Shop': 'Pyramid Fairy', - 'Dark Desert': 'Pyramid Fairy', - 'West Dark World': 'Pyramid Fairy', - 'South Dark World': 'Pyramid Fairy', - 'Light World': 'Pyramid Fairy', - 'Old Man Cave': 'Old Man S&Q' + 'Mire Area': 'Pyramid Fairy', + #'West Dark World': 'Pyramid Fairy', + 'Big Bomb Shop Area': 'Pyramid Fairy', + #'Light World': 'Pyramid Fairy', + 'Old Man Cave (East)': 'Old Man S&Q' } # format: # Key=Name @@ -2683,8 +2495,8 @@ door_addresses = {'Links House': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0 'Dark 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)), + 'Mire Hint': (0x61, (0x0114, 0x70, 0x0654, 0x0cc5, 0x02aa, 0x0d16, 0x0328, 0x0d32, 0x032f, 0x09, 0xf7, 0x0000, 0x0000)), + 'Mire 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)), 'Dark Death Mountain Shop': (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)), @@ -2770,7 +2582,7 @@ exit_ids = {'Links House Exit': (0x01, 0x00), 'Desert Healer Fairy': 0x5E, 'Dark Lake Hylia Healer Fairy': 0x5E, 'Dark Lake Hylia Ledge Healer Fairy': 0x5E, - 'Dark Desert Healer Fairy': 0x5E, + 'Mire Healer Fairy': 0x5E, 'Dark Death Mountain Healer Fairy': 0x5E, 'Fortune Teller (Light)': 0x65, 'Lake Hylia Fortune Teller': 0x65, @@ -2825,7 +2637,7 @@ exit_ids = {'Links House Exit': (0x01, 0x00), 'Fortune Teller (Dark)': 0x66, 'Archery Game': 0x59, 'Mire Shed': 0x5F, - 'Dark Desert Hint': 0x62, + 'Mire Hint': 0x62, 'Spike Cave': 0x41, 'Mimic Cave': 0x4F, 'Kakariko Well (top)': 0x80, @@ -2961,8 +2773,8 @@ ow_prize_table = {'Links House': (0x8b1, 0xb2d), 'Dark Potion Shop': (0xc80, 0x4c0), 'Archery Game': (0x2f0, 0xaf0), 'Mire Shed': (0x060, 0xc90), - 'Dark Desert Hint': (0x2e0, 0xd00), - 'Dark Desert Fairy': (0x1c0, 0xc90), + 'Mire Hint': (0x2e0, 0xd00), + 'Mire Fairy': (0x1c0, 0xc90), 'Spike Cave': (0x860, 0x180), 'Dark Death Mountain Shop': (0xd80, 0x180), 'Dark Death Mountain Fairy': (0x620, 0x2c0), diff --git a/Fill.py b/Fill.py index f6582a16..6ac6b3bb 100644 --- a/Fill.py +++ b/Fill.py @@ -3,13 +3,14 @@ import collections import itertools import logging import math +from collections import Counter from contextlib import suppress from BaseClasses import CollectionState, FillError, LocationType from Items import ItemFactory from Regions import shop_to_location_table, retro_shops from source.item.FillUtil import filter_locations, classify_major_items, replace_trash_item, vanilla_fallback -from source.item.FillUtil import filter_pot_locations, valid_pot_items +from source.item.FillUtil import filter_special_locations, valid_pot_items def get_dungeon_item_pool(world): @@ -71,13 +72,13 @@ def fill_dungeons_restrictive(world, shuffled_locations): def fill_restrictive(world, base_state, locations, itempool, key_pool=None, single_player_placement=False, vanilla=False): - def sweep_from_pool(placing_item=None): + def sweep_from_pool(placing_items=None): new_state = base_state.copy() for item in itempool: new_state.collect(item, True) - new_state.placing_item = placing_item + new_state.placing_items = placing_items new_state.sweep_for_events() - new_state.placing_item = None + new_state.placing_items = None return new_state unplaced_items = [] @@ -94,7 +95,7 @@ def fill_restrictive(world, base_state, locations, itempool, key_pool=None, sing while any(player_items.values()) and locations: items_to_place = [[itempool.remove(items[-1]), items.pop()][-1] for items in player_items.values() if items] - maximum_exploration_state = sweep_from_pool(placing_item=items_to_place[0]) + maximum_exploration_state = sweep_from_pool(placing_items=items_to_place) has_beaten_game = world.has_beaten_game(maximum_exploration_state) for item_to_place in items_to_place: @@ -174,6 +175,9 @@ def valid_key_placement(item, location, key_pool, collection_state, world): if dungeon: if dungeon.name not in item.name and (dungeon.name != 'Hyrule Castle' or 'Escape' not in item.name): return True + # Small key and big key in Swamp and Hera are placed without logic + if world.logic[item.player] == 'hybridglitches' and dungeon.name in ['Tower of Hera', 'Swamp Palace'] and dungeon.name in item.name: + return True key_logic = world.key_logic[item.player][dungeon.name] unplaced_keys = len([x for x in key_pool if x.name == key_logic.small_key_name and x.player == item.player]) prize_loc = None @@ -280,9 +284,10 @@ def recovery_placement(item_to_place, locations, world, state, base_state, itemp return spot_to_fill return None # explicitly fail these cases - elif world.algorithm in ['dungeon_only', 'major_only']: + elif world.algorithm in ['dungeon_only', 'major_only', 'district']: raise FillError(f'Rare placement for {world.algorithm} detected. {item_to_place} unable to be placed.' f' Try a different seed') + # I don't think any algorithm uses fallback placement anymore, vanilla is special. Others simply fail. else: other_locations = [x for x in locations if x not in attempted] for location in other_locations: @@ -382,24 +387,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None # get items to distribute classify_major_items(world) - # handle pot shuffle - pots_used = False - pot_item_pool = collections.defaultdict(list) - for item in world.itempool: - if item.name in ['Chicken', 'Big Magic']: # can only fill these in that players world - pot_item_pool[item.player].append(item) - for player, pot_pool in pot_item_pool.items(): - if pot_pool: - for pot_item in pot_pool: - world.itempool.remove(pot_item) - pot_locations = [location for location in fill_locations - if location.type == LocationType.Pot and location.player == player] - pot_locations = filter_pot_locations(pot_locations, world) - fast_fill_helper(world, pot_pool, pot_locations) - pots_used = True - if pots_used: - fill_locations = world.get_unfilled_locations() - random.shuffle(fill_locations) + # handle pot/drop shuffle random.shuffle(world.itempool) config_sort(world) @@ -413,7 +401,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None # fill in gtower locations with trash first for player in range(1, world.players + 1): if (not gftower_trash or not world.ganonstower_vanilla[player] - or world.logic[player] in ['owglitches', 'nologic']): + or world.logic[player] in ['owglitches', 'hybridglitches', 'nologic']): continue gt_count, total_count = calc_trash_locations(world, player) scale_factor = .75 * (world.crystals_needed_for_gt[player] / 7) @@ -488,6 +476,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None if world.players > 1: fast_fill_pot_for_multiworld(world, restitempool, fill_locations) + # todo: fast fill drops? if world.algorithm == 'vanilla_fill': fast_vanilla_fill(world, restitempool, fill_locations) else: @@ -497,7 +486,7 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None unfilled = [location.name for location in fill_locations] if unplaced or unfilled: logging.warning('Unplaced items: %s - Unfilled Locations: %s', unplaced, unfilled) - ensure_good_pots(world) + ensure_good_items(world) def config_sort(world): @@ -527,7 +516,7 @@ def calc_trash_locations(world, player): return gt_count, total_count -def ensure_good_pots(world, write_skips=False): +def ensure_good_items(world, write_skips=False): for loc in world.get_locations(): if loc.item is None: loc.item = ItemFactory('Nothing', loc.player) @@ -535,25 +524,18 @@ def ensure_good_pots(world, write_skips=False): if (loc.item.name in {'Arrows (5)', 'Nothing'} and (loc.type != LocationType.Pot or loc.item.player != loc.player)): loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.item.player) - # can be placed here by multiworld balancing or shop balancing - # change it to something normal for the player it got swapped to - elif (loc.item.name in {'Chicken', 'Big Magic'} - and (loc.type != LocationType.Pot or loc.item.player != loc.player)): - if loc.type == LocationType.Pot: - loc.item.player = loc.player - else: - loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.player) # do the arrow retro check if world.bow_mode[loc.item.player].startswith('retro') and loc.item.name in {'Arrows (5)', 'Arrows (10)'}: loc.item = ItemFactory('Rupees (5)', loc.item.player) # don't write out all pots to spoiler + # todo: skip uninteresting enemy drops if write_skips: if loc.type == LocationType.Pot and loc.item.name in valid_pot_items: loc.skip = True invalid_location_replacement = {'Arrows (5)': 'Arrows (10)', 'Nothing': 'Rupees (5)', - 'Chicken': 'Rupees (5)', 'Big Magic': 'Small Magic'} + 'Chicken': 'Rupees (5)', 'Big Magic': 'Small Magic', 'Fairy': 'Small Heart'} def fast_fill_helper(world, item_pool, fill_locations): @@ -588,7 +570,7 @@ def fast_fill_pot_for_multiworld(world, item_pool, fill_locations): if loc.type == LocationType.Pot: pot_fill_locations[loc.player].append(loc) for player in range(1, world.players+1): - flex = 256 - world.pot_contents[player].multiworld_count + flex = 256 - world.data_tables[player].pot_secret_table.multiworld_count fill_count = len(pot_fill_locations[player]) - flex if fill_count > 0: fill_spots = random.sample(pot_fill_locations[player], fill_count) @@ -725,24 +707,44 @@ def balance_multiworld_progression(world): checked_locations = set() unchecked_locations = set(world.get_locations()) + total_locations_count = Counter(location.player for location in world.get_locations() if not location.locked and not location.forced_item) + reachable_locations_count = {} for player in range(1, world.players + 1): reachable_locations_count[player] = 0 + sphere_num = 1 + moved_item_count = 0 def get_sphere_locations(sphere_state, locations): sphere_state.sweep_for_events(key_only=True, locations=locations) return {loc for loc in locations if sphere_state.can_reach(loc) and sphere_state.not_flooding_a_key(sphere_state.world, loc)} + def item_percentage(player, num): + return num / total_locations_count[player] + while True: sphere_locations = get_sphere_locations(state, unchecked_locations) for location in sphere_locations: unchecked_locations.remove(location) - reachable_locations_count[location.player] += 1 + if not location.locked and not location.forced_item: + reachable_locations_count[location.player] += 1 + + logging.debug(f'Sphere {sphere_num}') + logging.debug(f'Reachable locations: {reachable_locations_count}') + debug_percentages = { + player: round(item_percentage(player, num), 2) + for player, num in reachable_locations_count.items() + } + logging.debug(f'Reachable percentages: {debug_percentages}\n') + sphere_num += 1 if checked_locations: - threshold = max(reachable_locations_count.values()) - 20 + max_percentage = max(map(lambda p: item_percentage(p, reachable_locations_count[p]), reachable_locations_count)) + threshold_percentages = {player: max_percentage * .8 for player in range(1, world.players + 1)} + logging.debug(f'Thresholds: {threshold_percentages}') - balancing_players = {player for player, reachables in reachable_locations_count.items() if reachables < threshold} + balancing_players = {player for player, reachables in reachable_locations_count.items() + if item_percentage(player, reachables) < threshold_percentages[player]} if balancing_players: balancing_state = state.copy() balancing_unchecked_locations = unchecked_locations.copy() @@ -760,7 +762,8 @@ def balance_multiworld_progression(world): for location in balancing_sphere: balancing_unchecked_locations.remove(location) balancing_reachables[location.player] += 1 - if world.has_beaten_game(balancing_state) or all(reachables >= threshold for reachables in balancing_reachables.values()): + if world.has_beaten_game(balancing_state) or all(item_percentage(player, reachables) >= threshold_percentages[player] + for player, reachables in balancing_reachables.items()): break elif not balancing_sphere: raise RuntimeError('Not all required items reachable. Something went terribly wrong here.') @@ -787,7 +790,8 @@ def balance_multiworld_progression(world): items_to_replace.append(testing) else: reduced_sphere = get_sphere_locations(reducing_state, locations_to_test) - if reachable_locations_count[player] + len(reduced_sphere) < threshold: + p = item_percentage(player, reachable_locations_count[player] + len(reduced_sphere)) + if p < threshold_percentages[player]: items_to_replace.append(testing) replaced_items = False @@ -812,6 +816,7 @@ def balance_multiworld_progression(world): new_location.event, old_location.event = True, False logging.debug(f"Progression balancing moved {new_location.item} to {new_location}, " f"displacing {old_location.item} into {old_location}") + moved_item_count += 1 state.collect(new_location.item, True, new_location) replaced_items = True break @@ -819,6 +824,7 @@ def balance_multiworld_progression(world): logging.warning(f"Could not Progression Balance {old_location.item}") if replaced_items: + logging.debug(f'Moved {moved_item_count} items so far\n') unlocked = {fresh for player in balancing_players for fresh in unlocked_locations[player]} for location in get_sphere_locations(state, unlocked): unchecked_locations.remove(location) @@ -833,7 +839,8 @@ def balance_multiworld_progression(world): if world.has_beaten_game(state): break elif not sphere_locations: - raise RuntimeError('Not all required items reachable. Something went terribly wrong here.') + logging.warning('Progression Balancing ran out of paths.') + break def check_shop_swap(l): @@ -925,7 +932,7 @@ def balance_money_progression(world): checked_locations = [] for player in range(1, world.players+1): kiki_payable = state.prog_items[('Moon Pearl', player)] > 0 or world.mode[player] == 'inverted' - if kiki_payable and world.get_region('East Dark World', player) in state.reachable_regions[player]: + if kiki_payable and world.get_region('Palace of Darkness Area', player) in state.reachable_regions[player]: if not kiki_paid[player]: kiki_check[player] = True sphere_costs[player] += 110 @@ -991,6 +998,7 @@ def balance_money_progression(world): logger.debug(f'Money balancing needed: Player {target_player} short {difference}') else: difference = 0 + target_player = next(p for p in solvent) while difference > 0: swap_targets = [x for x in unchecked_locations if x not in sphere_locations and x.item.name.startswith('Rupees') and x.item.player == target_player] if len(swap_targets) == 0: diff --git a/InitialSram.py b/InitialSram.py index 67c7dc83..b697823e 100644 --- a/InitialSram.py +++ b/InitialSram.py @@ -53,18 +53,17 @@ class InitialSram: def pre_open_pyramid_hole(self): self._or_value(OVERWORLD_DATA+0x5B, 0x20) + def pre_open_tr_bomb_doors(self): + self._or_value(ROOM_DATA+0x47, 0x80) + self._or_value(ROOM_DATA+0x01AB, 0x80) + def set_starting_equipment(self, world: object, player: int): equip = [0] * (0x340 + 0x4F) equip[0x36C] = 0x18 equip[0x36D] = 0x18 equip[0x379] = 0x68 - if world.bombbag[player]: - starting_max_bombs = 0 - else: - starting_max_bombs = 10 - starting_max_arrows = 30 - starting_bomb_cap_upgrades = 0 - starting_arrow_cap_upgrades = 0 + starting_bomb_cap_upgrades = 10 if not world.bombbag[player] else 0 + starting_arrow_cap_upgrades = 30 starting_bombs = 0 starting_arrows = 0 @@ -204,8 +203,8 @@ class InitialSram: equip[0x370] = min(starting_bomb_cap_upgrades, 50) equip[0x371] = min(starting_arrow_cap_upgrades, 70) - equip[0x343] = min(starting_bombs, (equip[0x370] + starting_max_bombs)) - equip[0x377] = min(starting_arrows, (equip[0x371] + starting_max_arrows)) + equip[0x343] = min(starting_bombs, equip[0x370]) + equip[0x377] = min(starting_arrows, equip[0x371]) # Assertion and copy equip to initial_sram_bytes assert equip[:0x340] == [0] * 0x340 diff --git a/ItemList.py b/ItemList.py index 9ca15555..7b8172d5 100644 --- a/ItemList.py +++ b/ItemList.py @@ -10,6 +10,7 @@ from Fill import FillError, fill_restrictive, get_dungeon_item_pool, track_dunge from PotShuffle import vanilla_pots from Items import ItemFactory +from source.dungeon.EnemyList import add_drop_contents from source.item.FillUtil import trash_items, pot_items import source.classes.constants as CONST @@ -198,7 +199,7 @@ def generate_itempool(world, player): world.push_item(world.get_location('Ganon', player), ItemFactory('Triforce', player), False) if world.goal[player] in ['triforcehunt', 'trinity']: - region = world.get_region('Light World', player) + region = world.get_region('Hyrule Castle Courtyard', player) loc = Location(player, "Murahdahla", parent=region) region.locations.append(loc) world.dynamic_locations.append(loc) @@ -286,6 +287,11 @@ def generate_itempool(world, player): for _ in range(0, amt): pool.append('Rupees (20)') + if world.logic[player] == 'hybridglitches' and world.pottery[player] not in ['none', 'cave']: + # In HMG force swamp smalls in pots to allow getting out of swamp palace + placed_items['Swamp Palace - Trench 1 Pot Key'] = 'Small Key (Swamp Palace)' + placed_items['Swamp Palace - Pot Row Pot Key'] = 'Small Key (Swamp Palace)' + start_inventory = list(world.precollected_items) for item in precollected_items: world.push_precollected(ItemFactory(item, player)) @@ -300,6 +306,8 @@ def generate_itempool(world, player): if not found_sword and world.swords[player] != 'swordless': found_sword = True possible_weapons.append(item) + if world.algorithm == 'vanilla_fill': # skip other possibilities + continue if (item in ['Progressive Bow', 'Bow'] and not found_bow and not world.bow_mode[player].startswith('retro')): found_bow = True @@ -393,9 +401,15 @@ def generate_itempool(world, player): if tr_medallion == 'Random': tr_medallion = None if not mm_medallion: - mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] + if world.algorithm == 'vanilla_fill': + mm_medallion = 'Ether' + else: + mm_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] if not tr_medallion: - tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] + if world.algorithm == 'vanilla_fill': + tr_medallion = 'Quake' + else: + tr_medallion = ['Ether', 'Quake', 'Bombos'][random.randint(0, 2)] world.required_medallions[player] = (mm_medallion, tr_medallion) # shuffle bottle refills @@ -412,7 +426,7 @@ def generate_itempool(world, player): if world.take_any[player] != 'none': set_up_take_anys(world, player, skip_pool_adjustments) if world.keyshuffle[player] == 'universal': - if world.dropshuffle[player] and not skip_pool_adjustments: + if world.dropshuffle[player] != 'none' and not skip_pool_adjustments: world.itempool += [ItemFactory('Small Key (Universal)', player)] * 13 if world.pottery[player] not in ['none', 'cave'] and not skip_pool_adjustments: world.itempool += [ItemFactory('Small Key (Universal)', player)] * 19 @@ -422,6 +436,9 @@ def generate_itempool(world, player): if world.pottery[player] not in ['none', 'keys'] and not skip_pool_adjustments: add_pot_contents(world, player) + if world.dropshuffle[player] == 'underworld' and not skip_pool_adjustments: + add_drop_contents(world, player) + # modfiy based on start inventory, if any modify_pool_for_start_inventory(start_inventory, world, player) @@ -453,15 +470,17 @@ def generate_itempool(world, player): world.itempool.append(ItemFactory(item_name, player)) + + take_any_locations = [ 'Snitch Lady (East)', 'Snitch Lady (West)', 'Bush Covered House', 'Light World Bomb Hut', 'Fortune Teller (Light)', 'Lake Hylia Fortune Teller', 'Lumberjack House', 'Bonk Fairy (Light)', 'Bonk Fairy (Dark)', 'Lake Hylia Healer Fairy', 'Light Hype Fairy', 'Desert Healer Fairy', - 'Dark Lake Hylia Healer Fairy', 'Dark Lake Hylia Ledge Healer Fairy', 'Dark Desert Healer Fairy', + 'Dark Lake Hylia Healer Fairy', 'Dark Lake Hylia Ledge Healer Fairy', 'Mire Healer Fairy', 'Dark Death Mountain Healer Fairy', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave', 'Kakariko Gamble Game', '50 Rupee Cave', 'Lost Woods Gamble', 'Hookshot Fairy', 'Palace of Darkness Hint', 'East Dark World Hint', 'Archery Game', 'Dark Lake Hylia Ledge Hint', - 'Dark Lake Hylia Ledge Spike Cave', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Dark Desert Hint'] + 'Dark Lake Hylia Ledge Spike Cave', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Mire Hint'] fixed_take_anys = [ 'Desert Healer Fairy', 'Light Hype Fairy', 'Dark Death Mountain Healer Fairy', @@ -503,7 +522,7 @@ def set_up_take_anys(world, player, skip_adjustments=False): else: if world.shopsanity[player] and not skip_adjustments: world.itempool.append(ItemFactory('Rupees (300)', player)) - old_man_take_any.shop.add_inventory(0, 'Rupees (300)', 0, 0, create_location=world.shopsanity[player]) + old_man_take_any.shop.add_inventory(0, 'Rupees (300)', 0, 0, create_location=True) take_any_type = ShopType.Shop if world.shopsanity[player] else ShopType.TakeAny for num in range(4): @@ -512,13 +531,15 @@ def set_up_take_anys(world, player, skip_adjustments=False): world.dynamic_regions.append(take_any) target, room_id = random.choice([(0x58, 0x0112), (0x60, 0x010F), (0x46, 0x011F)]) reg = regions.pop() - entrance = world.get_region(reg, player).entrances[0] + entrance = next((ent for ent in world.get_region(reg, player).entrances if ent.parent_region.is_outdoors()), None) + if entrance is None: + raise Exception(f'No outside entrance found for {reg}') connect_entrance(world, entrance, take_any, player) entrance.target = target take_any.shop = Shop(take_any, room_id, take_any_type, 0xE3, True, not world.shopsanity[player], 33 + num*2) world.shops[player].append(take_any.shop) take_any.shop.add_inventory(0, 'Blue Potion', 0, 0, create_location=world.shopsanity[player]) - take_any.shop.add_inventory(1, 'Boss Heart Container', 0, 0, create_location=world.shopsanity[player]) + take_any.shop.add_inventory(1, 'Boss Heart Container', 0, 0, create_location=True) if world.shopsanity[player] and not skip_adjustments: world.itempool.append(ItemFactory('Blue Potion', player)) world.itempool.append(ItemFactory('Boss Heart Container', player)) @@ -627,6 +648,7 @@ def set_up_shops(world, player): else: cap_shop = world.get_region('Capacity Upgrade', player).shop cap_shop.inventory[0] = cap_shop.inventory[1] # remove bomb capacity upgrades in bombbag + cap_shop.inventory[1] = None def customize_shops(world, player): @@ -800,7 +822,7 @@ def balance_prices(world, player): def check_hints(world, player): - if world.shuffle[player] in ['simple', 'restricted', 'full', 'crossed', 'insanity']: + if world.shuffle[player] in ['simple', 'restricted', 'full', 'lite', 'lean', 'swapped', 'crossed', 'insanity']: for shop, location_list in shop_to_location_table.items(): if shop in ['Capacity Upgrade', 'Paradox Shop', 'Potion Shop']: continue # near the queen, near potions, and near 7 chests are fine @@ -864,7 +886,7 @@ def get_pool_core(world, player, progressive, shuffle, difficulty, treasure_hunt return random.choice([True, False]) if progressive == 'random' else progressive == 'on' # provide boots to boots glitch dependent modes - if logic in ['owglitches', 'nologic']: + if logic in ['owglitches', 'hybridglitches', 'nologic']: precollected_items.append('Pegasus Boots') pool.remove('Pegasus Boots') pool.extend(['Rupees (20)']) @@ -1157,20 +1179,19 @@ def make_custom_item_pool(world, player, progressive, shuffle, difficulty, timer pool.extend(['Nothing'] * nothings) start_inventory = [x for x in world.precollected_items if x.player == player] - if not start_inventory: - if world.logic[player] in ['owglitches', 'nologic']: - precollected_items.append('Pegasus Boots') - if 'Pegasus Boots' in pool: - pool.remove('Pegasus Boots') - pool.append('Rupees (20)') - if world.swords[player] == 'assured': - precollected_items.append('Progressive Sword') - if 'Progressive Sword' in pool: - pool.remove('Progressive Sword') - pool.append('Rupees (50)') - elif 'Fighter Sword' in pool: - pool.remove('Fighter Sword') - pool.append('Rupees (50)') + if world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] and all(x.name != 'Pegasus Boots' for x in start_inventory): + precollected_items.append('Pegasus Boots') + if 'Pegasus Boots' in pool: + pool.remove('Pegasus Boots') + pool.append('Rupees (20)') + if world.swords[player] == 'assured' and all(' Sword' not in x.name for x in start_inventory): + precollected_items.append('Progressive Sword') + if 'Progressive Sword' in pool: + pool.remove('Progressive Sword') + pool.append('Rupees (50)') + elif 'Fighter Sword' in pool: + pool.remove('Fighter Sword') + pool.append('Rupees (50)') return (pool, placed_items, precollected_items, clock_mode, treasure_hunt_count, treasure_hunt_icon, lamps_needed_for_dark_rooms) @@ -1300,7 +1321,7 @@ def make_customizer_pool(world, player): sphere_0 = world.customizer.get_start_inventory() no_start_inventory = not sphere_0 or not sphere_0[player] init_equip = [] if no_start_inventory else sphere_0[player] - if (world.logic[player] in ['owglitches', 'nologic'] + if (world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] and (no_start_inventory or all(x != 'Pegasus Boots' for x in init_equip))): precollected_items.append('Pegasus Boots') if 'Pegasus Boots' in pool: diff --git a/Items.py b/Items.py index 76370548..1bbda012 100644 --- a/Items.py +++ b/Items.py @@ -1,5 +1,3 @@ -import logging - from BaseClasses import Item @@ -62,19 +60,19 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche '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, 0x08], 999, None, None, None, None, None, None, None), - 'Blue Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x02, 0x09], 999, None, None, None, None, None, None, None), - 'Red Pendant': (True, False, 'Crystal', [0x01, 0x32, 0x60, 0x00, 0x69, 0x03, 0x0a], 999, None, None, None, None, None, None, None), + 'Green Pendant': (True, False, 'Crystal', [0x04, 0x38, 0x62, 0x00, 0x69, 0x37, 0x08], 999, None, None, None, None, None, None, None), + 'Red Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x38, 0x09], 999, None, None, None, None, None, None, None), + 'Blue Pendant': (True, False, 'Crystal', [0x01, 0x32, 0x60, 0x00, 0x69, 0x39, 0x0a], 999, None, None, None, None, None, None, None), 'Triforce': (True, False, None, 0x6A, 777, '\n YOU WIN!', 'and the triforce', 'victorious kid', 'victory for sale', 'fungus for the win', 'greedy boy wins game again', 'the Triforce'), 'Power Star': (True, False, None, 0x6B, 100, 'A small victory', 'and the power star', 'star-struck kid', 'star for sale', 'see stars with shroom', 'mario powers up again', 'a Power Star'), 'Triforce Piece': (True, False, None, 0x6C, 100, 'A small victory', 'and the thirdforce', 'triangular kid', 'triangle for sale', 'fungus for triangle', 'wise boy has triangle again', 'a Triforce piece'), - 'Crystal 1': (True, False, 'Crystal', [0x02, 0x34, 0x64, 0x40, 0x7F, 0x06, 0x01], 999, None, None, None, None, None, None, None), - 'Crystal 2': (True, False, 'Crystal', [0x10, 0x34, 0x64, 0x40, 0x79, 0x06, 0x02], 999, None, None, None, None, None, None, None), - 'Crystal 3': (True, False, 'Crystal', [0x40, 0x34, 0x64, 0x40, 0x6C, 0x06, 0x03], 999, None, None, None, None, None, None, None), - 'Crystal 4': (True, False, 'Crystal', [0x20, 0x34, 0x64, 0x40, 0x6D, 0x06, 0x04], 999, None, None, None, None, None, None, None), - 'Crystal 5': (True, False, 'Crystal', [0x04, 0x32, 0x64, 0x40, 0x6E, 0x06, 0x05], 999, None, None, None, None, None, None, None), - 'Crystal 6': (True, False, 'Crystal', [0x01, 0x32, 0x64, 0x40, 0x6F, 0x06, 0x06], 999, None, None, None, None, None, None, None), - 'Crystal 7': (True, False, 'Crystal', [0x08, 0x34, 0x64, 0x40, 0x7C, 0x06, 0x07], 999, None, None, None, None, None, None, None), + 'Crystal 1': (True, False, 'Crystal', [0x02, 0x34, 0x64, 0x40, 0x7F, 0x20, 0x01], 999, None, None, None, None, None, None, None), + 'Crystal 2': (True, False, 'Crystal', [0x10, 0x34, 0x64, 0x40, 0x79, 0x20, 0x02], 999, None, None, None, None, None, None, None), + 'Crystal 3': (True, False, 'Crystal', [0x40, 0x34, 0x64, 0x40, 0x6C, 0x20, 0x03], 999, None, None, None, None, None, None, None), + 'Crystal 4': (True, False, 'Crystal', [0x20, 0x34, 0x64, 0x40, 0x6D, 0x20, 0x04], 999, None, None, None, None, None, None, None), + 'Crystal 5': (True, False, 'Crystal', [0x04, 0x32, 0x64, 0x40, 0x6E, 0x20, 0x05], 999, None, None, None, None, None, None, None), + 'Crystal 6': (True, False, 'Crystal', [0x01, 0x32, 0x64, 0x40, 0x6F, 0x20, 0x06], 999, None, None, None, None, None, None, None), + 'Crystal 7': (True, False, 'Crystal', [0x08, 0x34, 0x64, 0x40, 0x7C, 0x20, 0x07], 999, None, None, None, None, None, None, None), 'Single Arrow': (False, False, None, 0x43, 3, 'A lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'), 'Arrows (10)': (False, False, None, 0x44, 30, 'This will give\nyou ten shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'ten arrows'), 'Arrow Upgrade (+10)': (False, False, None, 0x54, 100, 'Increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), @@ -82,8 +80,8 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Single Bomb': (False, False, None, 0x27, 5, 'I make things\ngo BOOM! But\njust once.', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again', 'a bomb'), 'Arrows (5)': (False, False, None, 0x5A, 15, 'This will give\nyou five shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'five arrows'), 'Small Magic': (False, False, None, 0x45, 5, 'A bit of magic', 'and the bit of magic', 'bit-o-magic kid', 'magic bit for sale', 'fungus for magic', 'magic boy conjures again', 'a bit of magic'), - 'Big Magic': (False, False, None, 0x5A, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'), - 'Chicken': (False, False, None, 0x5A, 5, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'), + 'Big Magic': (False, False, None, 0xD4, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'), + 'Chicken': (False, False, None, 0xD3, 5, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'), 'Bombs (3)': (False, False, None, 0x28, 15, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'), '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'), @@ -170,12 +168,13 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche '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'), - 'Bee Trap': (False, False, None, 0xB0, 50, 'We will sting your face a whole lot!', 'and the sting buddies', 'the beekeeper kid', 'insects for sale', 'shroom pollenation', 'bottle boy has mad bees again', 'friendship'), + 'Bee Trap': (False, False, None, 0xD0, 50, 'We will sting your face a whole lot!', 'and the sting buddies', 'the beekeeper kid', 'insects for sale', 'shroom pollenation', 'bottle boy has mad bees again', 'friendship'), 'Red Potion': (False, False, None, 0x2E, 150, 'Hearty red goop!', 'and the red goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has red goo again', 'a red potion'), 'Green Potion': (False, False, None, 0x2F, 60, 'Refreshing green goop!', 'and the green goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has green goo again', 'a green potion'), 'Blue Potion': (False, False, None, 0x30, 160, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a blue potion'), 'Bee': (False, False, None, 0x0E, 10, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has mad bee again', 'a bee'), 'Small Heart': (False, False, None, 0x42, 10, 'Just a little\npiece of love!', 'and the heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart'), + 'Fairy': (False, False, None, 0xD2, 50, 'Just a pixie!', 'and the pixie', 'the pixie kid', 'pixie for sale', 'pixie fungus', 'bottle boy has pixie again', 'a pixie'), 'Beat Agahnim 1': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Beat Agahnim 2': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Get Frog': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), diff --git a/KeyDoorShuffle.py b/KeyDoorShuffle.py index 6883fb94..5331ad4c 100644 --- a/KeyDoorShuffle.py +++ b/KeyDoorShuffle.py @@ -1430,7 +1430,7 @@ def prize_relevance_sig2(start_regions, d_name, dungeon_entrance, is_atgt_swappe def validate_bk_layout(proposal, builder, start_regions, world, player): bk_special = check_bk_special(builder.master_sector.regions, world, player) - if world.bigkeyshuffle[player] and (world.dropshuffle[player] or not bk_special): + if world.bigkeyshuffle[player] and (world.dropshuffle[player] != 'none' or not bk_special): return True flat_proposal = flatten_pair_list(proposal) state = ExplorationState(dungeon=builder.name) @@ -2106,6 +2106,22 @@ def validate_key_placement(key_layout, world, player): if i.player == player and i.name == smallkey_name: keys_outside += 1 + if world.logic[player] == 'hybridglitches': + # Swamp keylogic + if smallkey_name.endswith('(Swamp Palace)'): + swamp_entrance = world.get_location('Swamp Palace - Entrance', player) + # Swamp small not vanilla + if swamp_entrance.item is None or (swamp_entrance.item.name != smallkey_name or swamp_entrance.item.player != player): + mire_keylayout = world.key_layout[player]['Misery Mire'] + mire_smallkey_name = dungeon_keys[mire_keylayout.sector.name] + # Check if any mire keys are in swamp (excluding entrance), if none then add one to keys_outside + mire_keys_in_swamp = sum([1 if x.item.name == mire_smallkey_name else 0 for x in key_layout.item_locations if x.item is not None and x != swamp_entrance]) + if mire_keys_in_swamp == 0: + keys_outside +=1 + # Mire keylogic + if smallkey_name.endswith('(Tower of Hera)'): + # TODO: Make sure that mire medallion isn't in hera basement, or if it it, the small key is available downstairs + big_key_outside = True for code, counter in key_layout.key_counters.items(): if len(counter.child_doors) == 0: continue diff --git a/Main.py b/Main.py index 0fd377c6..83af5048 100644 --- a/Main.py +++ b/Main.py @@ -15,26 +15,30 @@ from KeyDoorShuffle import validate_key_placement from OverworldGlitchRules import create_owg_connections from PotShuffle import shuffle_pots, shuffle_pot_switches from Regions import create_regions, create_shops, mark_light_dark_world_regions, create_dungeon_regions, adjust_locations -from OverworldShuffle import create_dynamic_exits +from OverworldShuffle import link_overworld, create_dynamic_exits from EntranceShuffle import link_entrances -from Rom import patch_rom, patch_race_rom, patch_enemizer, apply_rom_settings, LocalRom, JsonRom, get_hash_string +from Rom import patch_rom, patch_race_rom, apply_rom_settings, LocalRom, JsonRom, get_hash_string from Doors import create_doors from DoorShuffle import link_doors, connect_portal, link_doors_prep from RoomData import create_rooms from Rules import set_rules from Dungeons import create_dungeons -from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dungeons_restrictive, ensure_good_pots +from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dungeons_restrictive, ensure_good_items from Fill import dungeon_tracking from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items +from UnderworldGlitchRules import create_hybridmajor_connections, create_hybridmajor_connectors from Utils import output_path, parse_player_names from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config from source.overworld.EntranceShuffle2 import link_entrances_new from source.tools.BPS import create_bps_from_data from source.classes.CustomSettings import CustomSettings +from source.enemizer.DamageTables import DamageTable +from source.enemizer.Enemizer import randomize_enemies +from source.rom.DataTables import init_data_tables -version_number = '1.2.0.23' +version_number = '1.4.1.3' version_branch = '-u' __version__ = f'{version_number}{version_branch}' @@ -75,6 +79,9 @@ def main(args, seed=None, fish=None): seed = customized.determine_seed(seed) seeded = True customized.adjust_args(args) + for i in zip(args.logic.values(), args.door_shuffle.values()): + if i[0] == 'hybridglitches' and i[1] != 'vanilla': + raise RuntimeError(BabelFish().translate("cli","cli","hybridglitches.door.shuffle")) world = World(args.multi, args.shuffle, args.door_shuffle, args.logic, args.mode, args.swords, args.difficulty, args.item_functionality, args.timer, args.progressive, args.goal, args.algorithm, args.accessibility, args.shuffleganon, args.custom, args.customitemarray, args.hints) @@ -109,6 +116,7 @@ def main(args, seed=None, fish=None): world.enemy_shuffle = args.shuffleenemies.copy() world.enemy_health = args.enemy_health.copy() world.enemy_damage = args.enemy_damage.copy() + world.any_enemy_logic = args.any_enemy_logic.copy() world.beemizer = args.beemizer.copy() world.intensity = {player: random.randint(1, 3) if args.intensity[player] == 'random' else int(args.intensity[player]) for player in range(1, world.players + 1)} world.door_type_mode = args.door_type_mode.copy() @@ -133,6 +141,7 @@ def main(args, seed=None, fish=None): world.restrict_boss_items = args.restrict_boss_items.copy() world.collection_rate = args.collection_rate.copy() world.colorizepots = args.colorizepots.copy() + world.aga_randomness = args.aga_randomness.copy() world.treasure_hunt_count = {} world.treasure_hunt_total = {} @@ -200,22 +209,28 @@ def main(args, seed=None, fish=None): for player in range(1, world.players + 1): create_regions(world, player) - if world.logic[player] in ('owglitches', 'nologic'): + if world.logic[player] in ('owglitches', 'hybridglitches', 'nologic'): create_owg_connections(world, player) create_dungeon_regions(world, player) create_shops(world, player) create_doors(world, player) create_rooms(world, player) create_dungeons(world, player) - adjust_locations(world, player) + world.damage_table[player] = DamageTable() + world.data_tables[player] = init_data_tables(world, player) place_bosses(world, player) + randomize_enemies(world, player) + if world.logic[player] in ('nologic', 'hybridglitches'): + create_hybridmajor_connections(world, player) + adjust_locations(world, player) if world.customizer and world.customizer.get_start_inventory(): for p, inv_list in world.customizer.get_start_inventory().items(): - for inv_item in inv_list: - item = ItemFactory(inv_item.strip(), p) - if item: - world.push_precollected(item) + if inv_list: + for inv_item in inv_list: + item = ItemFactory(inv_item.strip(), p) + if item: + world.push_precollected(item) if args.print_custom_yaml: world.settings.record_info(world) @@ -231,11 +246,14 @@ def main(args, seed=None, fish=None): logger.info(world.fish.translate("cli","cli","shuffling.world")) for player in range(1, world.players + 1): + link_overworld(world, player) create_dynamic_exits(world, player) - if world.experimental[player] or world.shuffle[player] in ['lite', 'lean'] or world.shuffletavern[player] or (world.customizer and world.customizer.get_entrances()): + if world.experimental[player] or world.shuffle[player] in ['lite', 'lean', 'swapped'] or world.shuffletavern[player] or (world.customizer and world.customizer.get_entrances()): link_entrances_new(world, player) else: link_entrances(world, player) + if world.logic[player] in ('nologic', 'hybridglitches'): + create_hybridmajor_connectors(world, player) logger.info(world.fish.translate("cli", "cli", "shuffling.prep")) for player in range(1, world.players + 1): @@ -322,7 +340,7 @@ def main(args, seed=None, fish=None): customize_shops(world, player) if args.algorithm in ['balanced', 'equitable']: balance_money_progression(world) - ensure_good_pots(world, True) + ensure_good_items(world, True) if args.print_custom_yaml: world.settings.record_item_placements(world) @@ -330,34 +348,13 @@ def main(args, seed=None, fish=None): rom_names = [] jsonout = {} - enemized = False if not args.suppress_rom or args.bps: logger.info(world.fish.translate("cli","cli","patching.rom")) for team in range(world.teams): for player in range(1, world.players + 1): - sprite_random_on_hit = type(args.sprite[player]) is str and args.sprite[player].lower() == 'randomonhit' - use_enemizer = (world.boss_shuffle[player] != 'none' or world.enemy_shuffle[player] != 'none' - or world.enemy_health[player] != 'default' or world.enemy_damage[player] != 'default' - or sprite_random_on_hit) + rom = JsonRom() if args.jsonout else LocalRom(args.rom) - rom = JsonRom() if args.jsonout or use_enemizer else LocalRom(args.rom) - - if use_enemizer and (args.enemizercli or not args.jsonout): - local_rom = LocalRom(args.rom) # update base2current.json (side effect) - if args.rom and not(os.path.isfile(args.rom)): - raise RuntimeError("Could not find valid base rom for enemizing at expected path %s." % args.rom) - if os.path.exists(args.enemizercli): - patch_enemizer(world, player, rom, local_rom, args.enemizercli, sprite_random_on_hit) - enemized = True - if not args.jsonout: - rom = LocalRom.fromJsonRom(rom, args.rom, 0x400000) - else: - enemizerMsg = world.fish.translate("cli","cli","enemizer.not.found") + ': ' + args.enemizercli + "\n" - enemizerMsg += world.fish.translate("cli","cli","enemizer.nothing.applied") - logging.warning(enemizerMsg) - raise EnemizerError(enemizerMsg) - - patch_rom(world, rom, player, team, enemized, bool(args.mystery)) + patch_rom(world, rom, player, team, bool(args.mystery)) if args.race: patch_race_rom(rom) @@ -409,6 +406,8 @@ def main(args, seed=None, fish=None): if args.create_spoiler and not args.jsonout: logger.info(world.fish.translate("cli", "cli", "patching.spoiler")) world.spoiler.to_file(output_path(f'{outfilebase}_Spoiler.txt')) + if args.loglevel == 'debug': + world.spoiler.extras(output_path(f'{outfilebase}_Spoiler.txt')) if not args.skip_playthrough: logger.info(world.fish.translate("cli","cli","calc.playthrough")) @@ -432,7 +431,6 @@ def main(args, seed=None, fish=None): logger.info(world.fish.translate("cli","cli","made.rom") % (YES if (args.create_rom) else NO)) logger.info(world.fish.translate("cli","cli","made.playthrough") % (YES if (args.calc_playthrough) else NO)) logger.info(world.fish.translate("cli","cli","made.spoiler") % (YES if (not args.jsonout and args.create_spoiler) else NO)) - logger.info(world.fish.translate("cli","cli","used.enemizer") % (YES if enemized else NO)) logger.info(world.fish.translate("cli","cli","seed") + ": %s", world.seed) logger.info(world.fish.translate("cli","cli","total.time"), time.perf_counter() - start) @@ -459,8 +457,6 @@ def copy_world(world): ret.treasure_hunt_count = world.treasure_hunt_count.copy() ret.treasure_hunt_icon = world.treasure_hunt_icon.copy() ret.sewer_light_cone = world.sewer_light_cone.copy() - ret.light_world_light_cone = world.light_world_light_cone - ret.dark_world_light_cone = world.dark_world_light_cone ret.seed = world.seed ret.can_access_trock_eyebridge = world.can_access_trock_eyebridge.copy() ret.can_access_trock_front = world.can_access_trock_front.copy() @@ -476,19 +472,26 @@ def copy_world(world): ret.bigkeyshuffle = world.bigkeyshuffle.copy() ret.bombbag = world.bombbag.copy() ret.flute_mode = world.flute_mode.copy() + ret.bow_mode = world.bow_mode.copy() ret.crystals_needed_for_ganon = world.crystals_needed_for_ganon.copy() ret.crystals_needed_for_gt = world.crystals_needed_for_gt.copy() ret.crystals_ganon_orig = world.crystals_ganon_orig.copy() ret.crystals_gt_orig = world.crystals_gt_orig.copy() ret.open_pyramid = world.open_pyramid.copy() + ret.take_any = world.take_any.copy() ret.boss_shuffle = world.boss_shuffle.copy() ret.enemy_shuffle = world.enemy_shuffle.copy() ret.enemy_health = world.enemy_health.copy() ret.enemy_damage = world.enemy_damage.copy() + ret.any_enemy_logic = world.any_enemy_logic.copy() ret.beemizer = world.beemizer.copy() ret.intensity = world.intensity.copy() ret.decoupledoors = world.decoupledoors.copy() ret.door_self_loops = world.door_self_loops.copy() + ret.door_type_mode = world.door_type_mode.copy() + ret.trap_door_mode = world.trap_door_mode.copy() + ret.key_logic_algorithm = world.key_logic_algorithm.copy() + ret.aga_randomness = world.aga_randomness.copy() ret.experimental = world.experimental.copy() ret.shopsanity = world.shopsanity.copy() ret.dropshuffle = world.dropshuffle.copy() @@ -497,6 +500,8 @@ def copy_world(world): ret.mixed_travel = world.mixed_travel.copy() ret.standardize_palettes = world.standardize_palettes.copy() ret.restrict_boss_items = world.restrict_boss_items.copy() + ret.damage_table = world.damage_table + ret.data_tables = world.data_tables # can be changed... for player in range(1, world.players + 1): create_regions(ret, player) @@ -505,8 +510,11 @@ def copy_world(world): create_shops(ret, player) create_rooms(ret, player) create_dungeons(ret, player) - if world.logic[player] in ('owglitches', 'nologic'): + if world.logic[player] in ('owglitches', 'hybridglitches', 'nologic'): create_owg_connections(ret, player) + if world.logic[player] in ('nologic', 'hybridglitches'): + create_hybridmajor_connections(ret, player) + # there are region references here they must be migrated to preserve integrity # ret.exp_cache = world.exp_cache.copy() @@ -587,6 +595,8 @@ def copy_world(world): ret.sanc_portal = world.sanc_portal for player in range(1, world.players + 1): + if world.logic[player] in ('nologic', 'hybridglitches'): + create_hybridmajor_connectors(ret, player) set_rules(ret, player) return ret diff --git a/MultiClient.py b/MultiClient.py index 646c60cf..a4c3b95f 100644 --- a/MultiClient.py +++ b/MultiClient.py @@ -8,10 +8,12 @@ import shlex import urllib.parse import websockets -from BaseClasses import PotItem, PotFlags +from BaseClasses import PotItem, PotFlags, LocationType import Items import Regions import PotShuffle +import source.dungeon.EnemyList as EnemyList +import source.rom.DataTables as DataTables class ReceivedItem: @@ -67,6 +69,7 @@ class Context: self.lookup_id_to_name = {} self.pottery_locations_enabled = None + self.uw_sprite_locations_enabled = None def color_code(*args): codes = {'reset': 0, 'bold': 1, 'underline': 4, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, @@ -99,6 +102,7 @@ ITEM_SRAM_SIZE = 0x250 SHOP_SRAM_LEN = 0x29 # 41 tracked items POT_LOCATION_TABLE = 0x142A60 +UW_SPRITE_LOCATION_TABLE = 0x142CB0 RECV_PROGRESS_ADDR = SAVEDATA_START + 0x4D0 # 2 bytes RECV_ITEM_ADDR = SAVEDATA_START + 0x4D2 # 1 byte @@ -835,9 +839,11 @@ def filter_location(ctx, location): tracking_data = ctx.pottery_locations_enabled tile_pots = tracking_data[tile_idx] | (tracking_data[tile_idx+1] << 8) return (mask & tile_pots) == 0 - if (not ctx.key_drop_mode and location in PotShuffle.key_drop_data - and PotShuffle.key_drop_data[location][0] == 'Drop'): - return True + if location in location_table_sprite_items: + tile_idx, mask = location_table_sprite_items[location] + tracking_data = ctx.uw_sprite_locations_enabled + tile_sprites = tracking_data[tile_idx] | (tracking_data[tile_idx+1] << 8) + return (mask & tile_sprites) == 0 if not ctx.shop_mode and location in Regions.flat_normal_shops: return True if not ctx.retro_mode and location in Regions.flat_retro_shops: @@ -848,13 +854,6 @@ def filter_location(ctx, location): def init_lookups(ctx): ctx.lookup_id_to_name = {x: y for x, y in Regions.lookup_id_to_name.items()} ctx.lookup_name_to_id = {x: y for x, y in Regions.lookup_name_to_id.items()} - for location, datum in PotShuffle.key_drop_data.items(): - type = datum[0] - if type == 'Drop': - location_id, super_tile, sprite_index = datum[1] - location_table_sprite_items[location] = (2 * super_tile, 0x8000 >> sprite_index) - ctx.lookup_name_to_id[location] = location_id - ctx.lookup_id_to_name[location_id] = location for super_tile, pot_list in PotShuffle.vanilla_pots.items(): for pot_index, pot in enumerate(pot_list): if pot.item != PotItem.Hole: @@ -868,6 +867,25 @@ def init_lookups(ctx): location_id = Regions.pot_address(pot_index, super_tile) ctx.lookup_name_to_id[loc_name] = location_id ctx.lookup_id_to_name[location_id] = loc_name + uw_table = DataTables.get_uw_enemy_table() + key_drop_data = {(v[1][1], v[1][2]): k for k, v in PotShuffle.key_drop_data.items() if v[0] == 'Drop'} + for super_tile, enemy_list in uw_table.room_map.items(): + index_adj = 0 + for index, sprite in enumerate(enemy_list): + if sprite.sub_type == 0x07: # overlord + index_adj += 1 + continue + if (super_tile, index) in key_drop_data: + loc_name = key_drop_data[(super_tile, index)] + location_id = PotShuffle.key_drop_data[loc_name][1][0] + else: + loc_name = f'{sprite.region} Enemy #{index+1}' + location_id = EnemyList.drop_address(index, super_tile) + if index < index_adj: + logging.info(f'Problem at {hex(super_tile)} {loc_name}') + location_table_sprite_items[loc_name] = (2 * super_tile, 0x8000 >> (index-index_adj)) + ctx.lookup_name_to_id[loc_name] = location_id + ctx.lookup_id_to_name[location_id] = loc_name async def track_locations(ctx : Context, roomid, roomdata): @@ -989,6 +1007,7 @@ async def game_watcher(ctx : Context): if not ctx.rom: rom = await snes_read(ctx, ROMNAME_START, ROMNAME_SIZE) + logging.info(f'Rom name: {rom} @ {hex(ROMNAME_START)}') if rom is None or rom == bytes([0] * ROMNAME_SIZE): continue @@ -1005,6 +1024,9 @@ async def game_watcher(ctx : Context): if ctx.pottery_locations_enabled is None: ctx.pottery_locations_enabled = await snes_read(ctx, POT_LOCATION_TABLE, 0x250) + if ctx.uw_sprite_locations_enabled is None: + ctx.uw_sprite_locations_enabled = await snes_read(ctx, UW_SPRITE_LOCATION_TABLE, 0x250) + gamemode = await snes_read(ctx, WRAM_START + 0x10, 1) if gamemode is None or gamemode[0] not in INGAME_MODES: continue diff --git a/MultiServer.py b/MultiServer.py index a9f9e9e2..c92f9989 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -16,6 +16,8 @@ import Items import Regions import PotShuffle from MultiClient import ReceivedItem, get_item_name_from_id, get_location_name_from_address +import source.dungeon.EnemyList as EnemyList +import source.rom.DataTables as DataTables class Client: def __init__(self, socket): @@ -355,12 +357,6 @@ async def console(ctx : Context): def init_lookups(ctx): ctx.lookup_id_to_name = {x: y for x, y in Regions.lookup_id_to_name.items()} ctx.lookup_name_to_id = {x: y for x, y in Regions.lookup_name_to_id.items()} - for location, datum in PotShuffle.key_drop_data.items(): - type = datum[0] - if type == 'Drop': - location_id = datum[1][0] - ctx.lookup_name_to_id[location] = location_id - ctx.lookup_id_to_name[location_id] = location for super_tile, pot_list in PotShuffle.vanilla_pots.items(): for pot_index, pot in enumerate(pot_list): if pot.item != PotItem.Hole: @@ -373,6 +369,18 @@ def init_lookups(ctx): location_id = Regions.pot_address(pot_index, super_tile) ctx.lookup_name_to_id[loc_name] = location_id ctx.lookup_id_to_name[location_id] = loc_name + uw_table = DataTables.get_uw_enemy_table() + key_drop_data = {(v[1][1], v[1][2]): k for k, v in PotShuffle.key_drop_data.items() if v[0] == 'Drop'} + for super_tile, enemy_list in uw_table.room_map.items(): + for index, sprite in enumerate(enemy_list): + if (super_tile, index) in key_drop_data: + loc_name = key_drop_data[(super_tile, index)] + location_id = PotShuffle.key_drop_data[loc_name][1][0] + else: + loc_name = f'{sprite.region} Enemy #{index+1}' + location_id = EnemyList.drop_address(index, super_tile) + ctx.lookup_name_to_id[loc_name] = location_id + ctx.lookup_id_to_name[location_id] = loc_name async def main(): diff --git a/Mystery.py b/Mystery.py index 786bb774..170dd8bc 100644 --- a/Mystery.py +++ b/Mystery.py @@ -33,7 +33,6 @@ def main(): parser.add_argument('--suppress_meta', action='store_true') parser.add_argument('--bps', action='store_true') parser.add_argument('--rom') - parser.add_argument('--enemizercli') parser.add_argument('--outputpath') parser.add_argument('--loglevel', default='info', choices=['debug', 'info', 'warning', 'error', 'critical']) for player in range(1, multiargs.multi + 1): @@ -76,8 +75,6 @@ def main(): if args.rom: erargs.rom = args.rom - if args.enemizercli: - erargs.enemizercli = args.enemizercli mw_settings = {'algorithm': False} diff --git a/OverworldGlitchRules.py b/OverworldGlitchRules.py index 890e8420..aab2ddda 100644 --- a/OverworldGlitchRules.py +++ b/OverworldGlitchRules.py @@ -4,262 +4,280 @@ Helper functions to deliver entrance/exit/region sets to OWG rules. from BaseClasses import Entrance +# Cave regions that superbunny can get through - but only with a sword. +sword_required_superbunny_mirror_regions = ["Spiral Cave (Top)"] -def get_sword_required_superbunny_mirror_regions(): - """ - Cave regions that superbunny can get through - but only with a sword. - """ - yield 'Spiral Cave (Top)' +# Cave regions that superbunny can get through - but only with boots. +boots_required_superbunny_mirror_regions = ["Two Brothers House"] -def get_boots_required_superbunny_mirror_regions(): - """ - Cave regions that superbunny can get through - but only with boots. - """ - yield 'Two Brothers House' +# Cave locations that superbunny can access - but only with boots. +boots_required_superbunny_mirror_locations = [ + "Sahasrahla's Hut - Left", + "Sahasrahla's Hut - Middle", + "Sahasrahla's Hut - Right", +] -def get_boots_required_superbunny_mirror_locations(): - """ - Cave locations that superbunny can access - but only with boots. - """ - yield 'Sahasrahla\'s Hut - Left' - yield 'Sahasrahla\'s Hut - Middle' - yield 'Sahasrahla\'s Hut - Right' +# Entrances that can't be superbunny-mirrored into. +invalid_mirror_bunny_entrances = [ + "Hype Cave", + "Bonk Fairy (Dark)", + "Thieves Town", + "Hammer Peg Cave", + "Brewery", + "Hookshot Cave", + "Dark Lake Hylia Ledge Fairy", + "Dark Lake Hylia Ledge Spike Cave", + "Palace of Darkness", + "Misery Mire", + "Turtle Rock", + "Bonk Rock Cave", + "Bonk Fairy (Light)", + "50 Rupee Cave", + "20 Rupee Cave", + "Checkerboard Cave", + "Light Hype Fairy", + "Waterfall of Wishing", + "Light World Bomb Hut", + "Mini Moldorm Cave", + "Ice Rod Cave", + "Sanctuary Grave", + "Kings Grave", + "Sanctuary Grave", + "Hyrule Castle Secret Entrance Drop", + "Skull Woods Second Section Hole", + "Skull Woods First Section Hole (North)", +] + +# Interior locations that can be accessed with superbunny state. +superbunny_accessible_locations = [ + "Waterfall of Wishing - Left", + "Waterfall of Wishing - Right", + "King's Tomb", + "Floodgate", + "Floodgate Chest", + "Cave 45", + "Bonk Rock Cave", + "Brewery", + "C-Shaped House", + "Chest Game", + "Mire Shed - Left", + "Mire Shed - Right", + "Secret Passage", + "Ice Rod Cave", + "Pyramid Fairy - Left", + "Pyramid Fairy - Right", + "Superbunny Cave - Top", + "Superbunny Cave - Bottom", + "Blind's Hideout - Left", + "Blind's Hideout - Right", + "Blind's Hideout - Far Left", + "Blind's Hideout - Far Right", + "Kakariko Well - Left", + "Kakariko Well - Middle", + "Kakariko Well - Right", + "Kakariko Well - Bottom", + "Kakariko Tavern", + "Library", + "Spiral Cave", +] + boots_required_superbunny_mirror_locations -def get_invalid_mirror_bunny_entrances(): - """ - Entrances that can't be superbunny-mirrored into. - """ - yield 'Skull Woods Final Section' - yield 'Hype Cave' - yield 'Bonk Fairy (Dark)' - yield 'Thieves Town' - yield 'Hammer Peg Cave' - yield 'Brewery' - yield 'Hookshot Cave' - yield 'Dark Lake Hylia Ledge Fairy' - yield 'Dark Lake Hylia Ledge Spike Cave' - yield 'Palace of Darkness' - yield 'Misery Mire' - yield 'Turtle Rock' - yield 'Bonk Rock Cave' - yield 'Bonk Fairy (Light)' - yield '50 Rupee Cave' - yield '20 Rupee Cave' - yield 'Checkerboard Cave' - yield 'Light Hype Fairy' - yield 'Waterfall of Wishing' - yield 'Light World Bomb Hut' - yield 'Mini Moldorm Cave' - yield 'Ice Rod Cave' - yield 'Sanctuary Grave' - yield 'Kings Grave' - yield 'Sanctuary Grave' - yield 'Hyrule Castle Secret Entrance Drop' - yield 'Skull Woods Second Section Hole' - yield 'Skull Woods First Section Hole (North)' +# Entrances that can be reached with full equipment using overworld glitches and don't need to be an exit. +# The following are still be mandatory exits: +# Open: +# Turtle Rock Isolated Ledge Entrance +# Skull Woods Second Section Door (West) (or Skull Woods Final Section) +# Inverted: +# Two Brothers House (West) +# Desert Palace Entrance (East) -def get_superbunny_accessible_locations(): - """ - Interior locations that can be accessed with superbunny state. - """ +non_mandatory_exits = [ + "Bumper Cave (Top)", + "Death Mountain Return Cave (West)", + "Hookshot Cave Back Entrance", +] - yield 'Waterfall of Wishing - Left' - yield 'Waterfall of Wishing - Right' - yield 'King\'s Tomb' - yield 'Floodgate' - yield 'Floodgate Chest' - yield 'Cave 45' - yield 'Bonk Rock Cave' - yield 'Brewery' - yield 'C-Shaped House' - yield 'Chest Game' - yield 'Mire Shed - Left' - yield 'Mire Shed - Right' - yield 'Secret Passage' - yield 'Ice Rod Cave' - yield 'Pyramid Fairy - Left' - yield 'Pyramid Fairy - Right' - yield 'Superbunny Cave - Top' - yield 'Superbunny Cave - Bottom' - yield 'Blind\'s Hideout - Left' - yield 'Blind\'s Hideout - Right' - yield 'Blind\'s Hideout - Far Left' - yield 'Blind\'s Hideout - Far Right' - yield 'Kakariko Well - Left' - yield 'Kakariko Well - Middle' - yield 'Kakariko Well - Right' - yield 'Kakariko Well - Bottom' - yield 'Kakariko Tavern' - yield 'Library' - yield 'Spiral Cave' - for location in get_boots_required_superbunny_mirror_locations(): - yield location +inverted_non_mandatory_exits = [ + "Desert Palace Entrance (North)", + "Desert Palace Entrance (West)", + "Agahnims Tower", + "Hyrule Castle Entrance (West)", + "Hyrule Castle Entrance (East)", +] + non_mandatory_exits + +open_non_mandatory_exits = [ + "Dark Death Mountain Ledge (West)", + "Dark Death Mountain Ledge (East)", + "Mimic Cave", + "Desert Palace Entrance (East)", +] + non_mandatory_exits -def get_non_mandatory_exits(inverted): - """ - Entrances that can be reached with full equipment using overworld glitches and don't need to be an exit. - The following are still be mandatory exits: +# Special Light World region exits that require boots clips. +boots_clip_exits_lw = [ + ('Lumberjack DMA Clip', 'Lumberjack Area', 'West Death Mountain (Bottom)'), + ('Spectacle Rock Clip', 'West Death Mountain (Top)', 'Spectacle Rock Ledge'), + ('Hera Ascent Clip', 'West Death Mountain (Bottom)', 'West Death Mountain (Top)'), + ('Death Mountain Glitched Bridge Clip', 'West Death Mountain (Bottom)', 'East Death Mountain (Top East)'), + ('Sanctuary DMD Clip', 'West Death Mountain (Bottom)', 'Sanctuary Area'), + ('Graveyard Ledge Clip', 'West Death Mountain (Bottom)', 'Graveyard Ledge'), + ('Kings Grave Clip', 'West Death Mountain (Bottom)', 'Kings Grave Area'), + ('Floating Island Clip', 'East Death Mountain (Top East)', 'Death Mountain Floating Island'), + ('Zora DMD Clip', 'Death Mountain TR Pegs Area', 'Zoras Domain'), + ('TR Pegs Ledge Clip', 'Death Mountain TR Pegs Area', 'Death Mountain TR Pegs Ledge'), + ('Mountain Pass Ledge Clip', 'Mountain Pass Area', 'Mountain Pass Ledge'), + ('Mountain Pass Entry Clip', 'Kakariko Pond Area', 'Mountain Pass Entry'), + ('Bat Cave River Clip', 'Blacksmith Area', 'Blacksmith Ledge'), + ('Desert Keep Clip', 'Maze Race Area', 'Desert Ledge Keep'), + ('Desert Ledge Clip', 'Maze Race Area', 'Desert Ledge'), + ('Maze Race Prize Clip', 'Maze Race Area', 'Maze Race Prize'), + ('Stone Bridge To Cliff Clip', 'Stone Bridge South Area', 'Central Cliffs'), + ('Hobo Clip', 'Stone Bridge South Area', 'Stone Bridge Water'), + ('Bombos Tablet Clip', 'Desert Area', 'Bombos Tablet Ledge'), + ('Desert Teleporter Clip', 'Desert Area', 'Desert Teleporter Ledge'), + ('Cave 45 Clip', 'Flute Boy Approach Area', 'Cave 45 Ledge'), + ('Desert Northern Cliffs Clip', 'Flute Boy Approach Area', 'Desert Northern Cliffs') +] - Open: - Turtle Rock Isolated Ledge Entrance - Skull Woods Second Section Door (West) (or Skull Woods Final Section) - - Inverted: - Two Brothers House (West) - Desert Palace Entrance (East) - """ - - yield 'Bumper Cave (Top)' - yield 'Death Mountain Return Cave (West)' - yield 'Hookshot Cave Back Entrance' - - if inverted: - yield 'Desert Palace Entrance (North)' - yield 'Desert Palace Entrance (West)' - yield 'Agahnims Tower' - yield 'Hyrule Castle Entrance (West)' - yield 'Hyrule Castle Entrance (East)' - else: - yield 'Dark Death Mountain Ledge (West)' - yield 'Dark Death Mountain Ledge (East)' - yield 'Mimic Cave' - yield 'Desert Palace Entrance (East)' +# Special Dark World region exits that require boots clips. +boots_clip_exits_dw = [ + ('Dark World DMA Clip', 'Dark Lumberjack Area', 'West Dark Death Mountain (Bottom)'), + ('Dark Death Mountain Descent', 'West Dark Death Mountain (Bottom)', 'Dark Chapel Area'), + ('Ganons Tower Ascent', 'West Dark Death Mountain (Bottom)', 'GT Stairs'), # This only gets you to the GT entrance + ('Dark Death Mountain Glitched Bridge', 'West Dark Death Mountain (Bottom)', 'East Dark Death Mountain (Top)'), + ('DW Floating Island Clip', 'East Dark Death Mountain (Bottom)', 'Dark Death Mountain Floating Island'), + ('Turtle Rock (Top) Clip', 'Turtle Rock Area', 'Turtle Rock Ledge'), + ('Catfish DMD', 'Turtle Rock Area', 'Catfish Area'), + ('Bumper Cave Ledge Clip', 'Bumper Cave Area', 'Bumper Cave Ledge'), + ('Bumper Cave Entry Clip', 'Outcast Pond Area', 'Bumper Cave Entry'), + ('Broken Bridge Hammer Rock Skip Clip', 'Qirn Jump East Bank', 'Broken Bridge Area'), + ('Dark Witch Rock Skip Clip', 'Dark Witch Area', 'Dark Witch Northeast'), + ('Hammer Pegs River Clip', 'Dark Dunes Area', 'Hammer Pegs Area'), + ('Hammer Bridge To Cliff Clip', 'Hammer Bridge South Area', 'Dark Central Cliffs'), + ('Mire Cliffs Clip', 'Stumpy Approach Area', 'Mire Northern Cliffs'), + ('Dark Lake Hylia Ledge Clip', 'Darkness Nook Area', 'Shopping Mall Area'), + ('Mire Teleporter Clip', 'Mire Area', 'Mire Teleporter Ledge') +] -def get_boots_clip_exits_lw(inverted = False): - """ - Special Light World region exits that require boots clips. - """ - - yield ('Bat Cave River Clip Spot', 'Light World', 'Bat Cave Ledge') - yield ('Light World DMA Clip Spot', 'Light World', 'West Death Mountain (Bottom)') - yield ('Hera Ascent', 'West Death Mountain (Bottom)', 'West Death Mountain (Top)') - yield ('Death Mountain Return Ledge Clip Spot', 'Light World', 'Death Mountain Return Ledge') - yield ('Death Mountain Entrance Clip Spot', 'Light World', 'Death Mountain Entrance') - yield ('Death Mountain Glitched Bridge', 'West Death Mountain (Bottom)', 'East Death Mountain (Top)') - yield ('Zora Descent Clip Spot', 'East Death Mountain (Top)', 'Zoras Domain') - yield ('Desert Northern Cliffs', 'Light World', 'Desert Northern Cliffs') - yield ('Desert Ledge Dropdown', 'Desert Northern Cliffs', 'Desert Ledge') - yield ('Desert Palace Entrance Dropdown', 'Desert Northern Cliffs', 'Desert Palace Entrance (North) Spot') - yield ('Lake Hylia Island Clip Spot', 'Light World', 'Lake Hylia Island') - yield ('Death Mountain Descent', 'West Death Mountain (Bottom)', 'Light World') - yield ('Kings Grave Clip Spot', 'West Death Mountain (Bottom)', 'Kings Grave Area') - - if not inverted: - yield ('Graveyard Ledge Clip Spot', 'West Death Mountain (Bottom)', 'Graveyard Ledge') - yield ('Desert Ledge (Northeast) Dropdown', 'Desert Northern Cliffs', 'Desert Checkerboard Ledge') - yield ('Spectacle Rock Clip Spot', 'West Death Mountain (Top)', 'Spectacle Rock') - yield ('Bombos Tablet Clip Spot', 'Light World', 'Bombos Tablet Ledge') - yield ('Floating Island Clip Spot', 'East Death Mountain (Top)', 'Death Mountain Floating Island') - yield ('Cave 45 Clip Spot', 'Light World', 'Cave 45 Ledge') +# Dark World drop-down ledges that require glitched speed. +glitched_speed_drops_dw = [ + ('Dark Death Mountain Ledge Clip', 'East Dark Death Mountain (Top)', 'Dark Death Mountain Ledge') +] -def get_boots_clip_exits_dw(inverted): - """ - Special Dark World region exits that require boots clips. - """ +# Out of bounds transitions using the mirror +mirror_clip_spots_dw = [ + ('Bunny DMD Mirror Spot', 'West Dark Death Mountain (Bottom)', 'Qirn Jump Area'), + ( + 'Dark Death Mountain Bunny Mirror To East Jump', + 'West Dark Death Mountain (Bottom)', + 'East Dark Death Mountain (Bottom)', + ), + ('Desert East Mirror Clip', 'Mire Area', 'Desert Mouth'), +] - yield ('Dark World DMA Clip Spot', 'West Dark World', 'West Dark Death Mountain (Bottom)') - yield ('Bumper Cave Ledge Clip Spot', 'West Dark World', 'Bumper Cave Ledge') - yield ('Bumper Cave Entrance Clip Spot', 'West Dark World', 'Bumper Cave Entrance') - yield ('Catfish Descent', 'Dark Death Mountain (Top)', 'Catfish Area') - yield ('Hammer Pegs River Clip Spot', 'East Dark World', 'Hammer Peg Area') - yield ('Dark Lake Hylia Ledge Clip Spot', 'East Dark World', 'Southeast Dark World') - yield ('Dark Desert Cliffs Clip Spot', 'South Dark World', 'Dark Desert') - yield ('DW Floating Island Clip Spot', 'East Dark Death Mountain (Bottom)', 'Dark Death Mountain Floating Island') +# Mirror shenanigans placing a mirror portal with a broken camera +mirror_offset_spots_dw = [('Dark Death Mountain Offset Mirror', 'West Dark Death Mountain (Bottom)', 'Pyramid Area')] - if not inverted: - yield ('Dark Death Mountain Descent', 'West Dark Death Mountain (Bottom)', 'West Dark World') - yield ('Ganons Tower Ascent', 'West Dark Death Mountain (Bottom)', 'Dark Death Mountain (Top)') # This only gets you to the GT entrance - yield ('Dark Death Mountain Glitched Bridge', 'West Dark Death Mountain (Bottom)', 'Dark Death Mountain (Top)') - yield ('Turtle Rock (Top) Clip Spot', 'Dark Death Mountain (Top)', 'Turtle Rock (Top)') - else: - yield ('Dark Desert Teleporter Clip Spot', 'Dark Desert', 'Dark Desert Ledge') - - -def get_glitched_speed_drops_dw(inverted = False): - """ - Dark World drop-down ledges that require glitched speed. - """ - yield ('Dark Death Mountain Ledge Clip Spot', 'Dark Death Mountain (Top)', 'Dark Death Mountain Ledge') - - -def get_mirror_clip_spots_dw(): - """ - Out of bounds transitions using the mirror - """ - yield ('Dark Death Mountain Bunny Descent Mirror Spot', 'West Dark Death Mountain (Bottom)', 'West Dark World') - yield ('Dark Death Mountain Bunny Mirror To East Jump', 'West Dark Death Mountain (Bottom)', 'East Dark Death Mountain (Bottom)') - yield ('Desert East Mirror Clip', 'Dark Desert', 'Desert Palace Mouth') - - -def get_mirror_offset_spots_dw(): - """ - Mirror shenanigans placing a mirror portal with a broken camera - """ - yield ('Dark Death Mountain Offset Mirror', 'West Dark Death Mountain (Bottom)', 'East Dark World') - - -def get_mirror_offset_spots_lw(player): - """ - Mirror shenanigans placing a mirror portal with a broken camera - """ - yield ('Death Mountain Offset Mirror', 'West Death Mountain (Bottom)', 'Light World') - yield ('Death Mountain Uncle Offset Mirror', 'West Death Mountain (Bottom)', 'Hyrule Castle Secret Entrance Area') - yield ('Death Mountain Castle Ledge Offset Mirror', 'West Death Mountain (Bottom)', 'Hyrule Castle Ledge') +# Mirror shenanigans placing a mirror portal with a broken camera +mirror_offset_spots_lw = [ + ('Death Mountain Offset Mirror', 'West Death Mountain (Bottom)', 'Hyrule Castle Area'), + ('Death Mountain Uncle Offset Mirror', 'West Death Mountain (Bottom)', 'Hyrule Castle Courtyard Northeast'), + ('Death Mountain Castle Ledge Offset Mirror', 'West Death Mountain (Bottom)', 'Hyrule Castle Ledge'), +] def create_owg_connections(world, player): """ Add OWG transitions to player's world without logic """ - create_no_logic_connections(player, world, get_boots_clip_exits_lw(world.mode[player] == 'inverted')) - create_no_logic_connections(player, world, get_boots_clip_exits_dw(world.mode[player] == 'inverted')) - - # Glitched speed drops. - create_no_logic_connections(player, world, get_glitched_speed_drops_dw(world.mode[player] == 'inverted')) - - # Mirror clip spots. - if world.mode[player] != 'inverted': - create_no_logic_connections(player, world, get_mirror_clip_spots_dw()) - create_no_logic_connections(player, world, get_mirror_offset_spots_dw()) + if world.mode[player] == "inverted": + connections = ( + boots_clip_exits_dw + + boots_clip_exits_lw + + glitched_speed_drops_dw + + mirror_offset_spots_lw + ) else: - create_no_logic_connections(player, world, get_mirror_offset_spots_lw(player)) + connections = ( + boots_clip_exits_dw + + boots_clip_exits_lw + + glitched_speed_drops_dw + + mirror_clip_spots_dw + + mirror_offset_spots_dw + ) + create_no_logic_connections(player, world, connections) def overworld_glitches_rules(world, player): - # Boots-accessible locations. - set_owg_rules(player, world, get_boots_clip_exits_lw(world.mode[player] == 'inverted'), lambda state: state.can_boots_clip_lw(player)) - set_owg_rules(player, world, get_boots_clip_exits_dw(world.mode[player] == 'inverted'), lambda state: state.can_boots_clip_dw(player)) + inverted = world.mode[player] == "inverted" + # Boots-accessible locations. + set_owg_rules( + player, + world, + boots_clip_exits_lw, + lambda state: state.can_boots_clip_lw(player), + ) + set_owg_rules( + player, + world, + boots_clip_exits_dw, + lambda state: state.can_boots_clip_dw(player), + ) # Glitched speed drops. - set_owg_rules(player, world, get_glitched_speed_drops_dw(world.mode[player] == 'inverted'), lambda state: state.can_get_glitched_speed_dw(player)) - # Dark Death Mountain Ledge Clip Spot also accessible with mirror. - if world.mode[player] != 'inverted': - add_alternate_rule(world.get_entrance('Dark Death Mountain Ledge Clip Spot', player), lambda state: state.has_Mirror(player)) + set_owg_rules( + player, + world, + glitched_speed_drops_dw, + lambda state: state.can_get_glitched_speed_dw(player), + ) + # Dark Death Mountain Ledge Clip also accessible with mirror. + if not inverted: + add_alternate_rule( + world.get_entrance('Dark Death Mountain Ledge Clip', player), lambda state: state.has_Mirror(player) + ) # Mirror clip spots. - if world.mode[player] != 'inverted': - set_owg_rules(player, world, get_mirror_clip_spots_dw(), lambda state: state.has_Mirror(player)) - set_owg_rules(player, world, get_mirror_offset_spots_dw(), lambda state: state.has_Mirror(player) and state.can_boots_clip_lw(player)) + if inverted: + set_owg_rules( + player, + world, + mirror_offset_spots_lw, + lambda state: state.has_Mirror(player) and state.can_boots_clip_dw(player), + ) else: - set_owg_rules(player, world, get_mirror_offset_spots_lw(player), lambda state: state.has_Mirror(player) and state.can_boots_clip_dw(player)) - + set_owg_rules(player, world, mirror_clip_spots_dw, lambda state: state.has_Mirror(player)) + set_owg_rules( + player, + world, + mirror_offset_spots_dw, + lambda state: state.has_Mirror(player) and state.can_boots_clip_lw(player), + ) + # Regions that require the boots and some other stuff. - if world.mode[player] != 'inverted': - world.get_entrance('Turtle Rock Teleporter', player).access_rule = lambda state: (state.can_boots_clip_lw(player) or state.can_lift_heavy_rocks(player)) and state.has('Hammer', player) - add_alternate_rule(world.get_entrance('Waterfall Fairy Access', player), lambda state: state.has_Pearl(player) or state.has_Boots(player)) # assumes access to Waterwalk ability (boots case) + if not inverted: + add_alternate_rule( + world.get_entrance('Zora Waterfall Approach', player), + lambda state: state.has_Pearl(player) or state.has_Boots(player), + ) # assumes access to Waterwalk ability (boots case) else: - add_alternate_rule(world.get_entrance('Waterfall Fairy Access', player), lambda state: state.has_Pearl(player)) + add_alternate_rule( + world.get_entrance('Zora Waterfall Approach', player), + lambda state: state.has_Pearl(player) + ) - world.get_entrance('Dark Desert Teleporter', player).access_rule = lambda state: (state.can_flute(player) or state.can_boots_clip_dw(player)) and state.can_lift_heavy_rocks(player) - add_alternate_rule(world.get_entrance('Dark Witch Rock (North)', player), lambda state: state.can_boots_clip_dw(player)) - add_alternate_rule(world.get_entrance('Broken Bridge Pass (Top)', player), lambda state: state.can_boots_clip_dw(player)) - add_alternate_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.can_boots_clip_lw(player)) # assumes access to Waterwalk ability + add_alternate_rule( + world.get_location("Zora's Ledge", player), lambda state: state.can_boots_clip_lw(player) + ) # assumes access to Waterwalk ability + # Bunny pocket + if not inverted: + add_alternate_rule(world.get_entrance("Skull Woods Final Section", player), lambda state: state.can_bunny_pocket(player) and state.has("Fire Rod", player)) + add_alternate_rule(world.get_entrance("Dark World Shop", player), lambda state: state.can_bunny_pocket(player) and state.has("Hammer", player)) + def add_alternate_rule(entrance, rule): old_rule = entrance.access_rule diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 3793f272..303aeed2 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -1,4 +1,41 @@ -from BaseClasses import RegionType, Entrance +from BaseClasses import RegionType, Terrain, Entrance +from Utils import bidict +import logging + +def link_overworld(world, player): + # setup mandatory connections + for exitname, regionname in mandatory_connections: + connect_simple(world, exitname, regionname, player) + + # apply tile logical connections + if not world.mode[player] == 'inverted': + for exitname, regionname in open_mandatory_connections: + connect_simple(world, exitname, regionname, player) + else: + for exitname, regionname in inverted_mandatory_connections: + connect_simple(world, exitname, regionname, player) + + for forward_edge, back_edge in default_connections: + connect_two_way(world, forward_edge, back_edge, player) + + +def connect_simple(world, exitname, regionname, player): + world.get_entrance(exitname, player).connect(world.get_region(regionname, player)) + +def connect_two_way(world, edgename1, edgename2, player): + edge1 = world.get_entrance(edgename1, player) + edge2 = world.get_entrance(edgename2, player) + edge1.connect(edge2.parent_region) + edge2.connect(edge1.parent_region) + +def create_flute_exits(world, player): + for region in (r for r in world.regions if r.player == player and r.terrain == Terrain.Land and r.name not in ['Zoras Domain', 'Master Sword Meadow', 'Hobo Bridge']): + if region.type == (RegionType.LightWorld if world.mode[player] != 'inverted' else RegionType.DarkWorld): + exitname = 'Flute From ' + region.name + exit = Entrance(region.player, exitname, region) + exit.spot_type = 'Flute' + exit.connect(world.get_region('Flute Sky', player)) + region.exits.append(exit) def get_mirror_exit_name(from_region, to_region): if from_region in mirror_connections and to_region in mirror_connections[from_region]: @@ -21,8 +58,7 @@ def create_mirror_exits(world, player): exit = Entrance(region.player, exitname, region) exit.spot_type = 'Mirror' to_region = world.get_region(region_dest_name, player) - # if region.terrain == Terrain.Water or to_region.terrain == Terrain.Water: - if region.name == 'Dark Lake Hylia Water': # TODO: Uncomment line above when Terrain type is modeled + if region.terrain == Terrain.Water or to_region.terrain == Terrain.Water: exit.access_rule = lambda state: state.has('Flippers', player) and state.has_Pearl(player) and state.has_Mirror(player) else: exit.access_rule = lambda state: state.has_Mirror(player) @@ -32,80 +68,997 @@ def create_mirror_exits(world, player): mirror_exits.add(exitname) def create_dynamic_exits(world, player): + create_flute_exits(world, player) create_mirror_exits(world, player) world.initialize_regions() -mirror_connections = { - 'Skull Woods Forest (West)': ['Light World'], +# 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'), - 'West Dark Death Mountain (Bottom)': ['Spectacle Rock'], - 'Dark Death Mountain (Top)': ['East Death Mountain (Top)'], + # Intra-tile OW Connections + ('Lost Woods Bush (West)', 'Lost Woods East Area'), #pearl + ('Lost Woods Bush (East)', 'Lost Woods West Area'), #pearl + ('West Death Mountain Drop', 'West Death Mountain (Bottom)'), + ('Spectacle Rock Ledge Drop', 'West Death Mountain (Top)'), + ('DM Hammer Bridge (West)', 'East Death Mountain (Top East)'), #hammer + ('DM Hammer Bridge (East)', 'East Death Mountain (Top West)'), #hammer + ('EDM To Spiral Ledge Drop', 'Spiral Cave Ledge'), + ('EDM To Fairy Ledge Drop', 'Fairy Ascension Ledge'), + ('EDM Ledge Drop', 'East Death Mountain (Bottom)'), + ('Spiral Ledge Drop', 'East Death Mountain (Bottom)'), + ('Fairy Ascension Ledge Drop', 'Fairy Ascension Plateau'), + ('Fairy Ascension Plateau Ledge Drop', 'East Death Mountain (Bottom)'), + ('Fairy Ascension Rocks (Inner)', 'East Death Mountain (Bottom)'), #mitts + ('Fairy Ascension Rocks (Outer)', 'Fairy Ascension Plateau'), #mitts + ('DM Broken Bridge (West)', 'East Death Mountain (Bottom)'), #hookshot + ('DM Broken Bridge (East)', 'East Death Mountain (Bottom Left)'), #hookshot + ('TR Pegs Ledge Entry', 'Death Mountain TR Pegs Ledge'), #mitts + ('TR Pegs Ledge Leave', 'Death Mountain TR Pegs Area'), #mitts + ('TR Pegs Ledge Drop', 'Death Mountain TR Pegs Area'), + ('Mountain Pass Rock (Outer)', 'Mountain Pass Entry'), #glove + ('Mountain Pass Rock (Inner)', 'Mountain Pass Area'), #glove + ('Mountain Pass Entry Ledge Drop', 'Mountain Pass Area'), + ('Mountain Pass Ledge Drop', 'Mountain Pass Area'), + ('Zora Waterfall Landing', 'Zora Waterfall Area'), + ('Zora Waterfall Water Drop', 'Zora Waterfall Water'), #flippers + ('Zora Waterfall Water Entry', 'Zora Waterfall Water'), #flippers + ('Zora Waterfall Approach', 'Zora Waterfall Entryway'), #flippers + ('Lost Woods Pass Hammer (North)', 'Lost Woods Pass Portal Area'), #hammer + ('Lost Woods Pass Hammer (South)', 'Lost Woods Pass East Top Area'), #hammer + ('Lost Woods Pass Rock (North)', 'Lost Woods Pass East Bottom Area'), #mitts + ('Lost Woods Pass Rock (South)', 'Lost Woods Pass Portal Area'), #mitts + ('Bonk Rock Ledge Drop', 'Sanctuary Area'), + ('Graveyard Ledge Drop', 'Graveyard Area'), + ('Kings Grave Rocks (Outer)', 'Kings Grave Area'), #mitts + ('Kings Grave Rocks (Inner)', 'Graveyard Area'), #mitts + ('River Bend Water Drop', 'River Bend Water'), #flippers + ('River Bend East Water Drop', 'River Bend Water'), #flippers + ('River Bend West Pier', 'River Bend Area'), + ('River Bend East Pier', 'River Bend East Bank'), + ('Potion Shop Water Drop', 'Potion Shop Water'), #flippers + ('Potion Shop Northeast Water Drop', 'Potion Shop Water'), #flippers + ('Potion Shop Rock (South)', 'Potion Shop Northeast'), #glove + ('Potion Shop Rock (North)', 'Potion Shop Area'), #glove + ('Zora Approach Water Drop', 'Zora Approach Water'), #flippers + ('Zora Approach Rocks (West)', 'Zora Approach Ledge'), #mitts/boots + ('Zora Approach Rocks (East)', 'Zora Approach Area'), #mitts/boots + ('Zora Approach Bottom Ledge Drop', 'Zora Approach Ledge'), + ('Zora Approach Ledge Drop', 'Zora Approach Area'), + ('Kakariko Southwest Bush (North)', 'Kakariko Southwest'), #pearl + ('Kakariko Southwest Bush (South)', 'Kakariko Village'), #pearl + ('Kakariko Yard Bush (South)', 'Kakariko Bush Yard'), #pearl + ('Kakariko Yard Bush (North)', 'Kakariko Village'), #pearl + ('Hyrule Castle Southwest Bush (North)', 'Hyrule Castle Southwest'), #pearl + ('Hyrule Castle Southwest Bush (South)', 'Hyrule Castle Area'), #pearl + ('Hyrule Castle Courtyard Bush (North)', 'Hyrule Castle Courtyard'), #pearl + ('Hyrule Castle Courtyard Bush (South)', 'Hyrule Castle Courtyard Northeast'), #pearl + ('Hyrule Castle Main Gate (South)', 'Hyrule Castle Courtyard'), #aga+mirror + ('Hyrule Castle Main Gate (North)', 'Hyrule Castle Area'), #aga+mirror + ('Hyrule Castle Ledge Drop', 'Hyrule Castle Area'), + ('Hyrule Castle Ledge Courtyard Drop', 'Hyrule Castle Courtyard'), + ('Hyrule Castle East Rock (Inner)', 'Hyrule Castle East Entry'), #glove + ('Hyrule Castle East Rock (Outer)', 'Hyrule Castle Area'), #glove + ('Wooden Bridge Bush (South)', 'Wooden Bridge Northeast'), #pearl + ('Wooden Bridge Bush (North)', 'Wooden Bridge Area'), #pearl + ('Wooden Bridge Water Drop', 'Wooden Bridge Water'), #flippers + ('Wooden Bridge Northeast Water Drop', 'Wooden Bridge Water'), #flippers + ('Blacksmith Ledge Peg (West)', 'Blacksmith Ledge'), #hammer + ('Blacksmith Ledge Peg (East)', 'Blacksmith Area'), #hammer + ('Maze Race Game', 'Maze Race Prize'), #pearl + ('Maze Race Ledge Drop', 'Maze Race Area'), + ('Stone Bridge (Southbound)', 'Stone Bridge South Area'), + ('Stone Bridge (Northbound)', 'Stone Bridge North Area'), + ('Desert Statue Move', 'Desert Stairs'), #book + ('Desert Ledge Drop', 'Desert Area'), + ('Desert Ledge Rocks (Outer)', 'Desert Ledge Keep'), #glove + ('Desert Ledge Rocks (Inner)', 'Desert Ledge'), #glove + ('Checkerboard Ledge Drop', 'Desert Area'), + ('Desert Mouth Drop', 'Desert Area'), + ('Desert Teleporter Drop', 'Desert Area'), + ('Bombos Tablet Drop', 'Desert Area'), + ('Flute Boy Bush (North)', 'Flute Boy Approach Area'), #pearl + ('Flute Boy Bush (South)', 'Flute Boy Bush Entry'), #pearl + ('Cave 45 Ledge Drop', 'Flute Boy Approach Area'), + ('C Whirlpool Water Entry', 'C Whirlpool Water'), #flippers + ('C Whirlpool Landing', 'C Whirlpool Area'), + ('C Whirlpool Rock (Bottom)', 'C Whirlpool Outer Area'), #glove + ('C Whirlpool Rock (Top)', 'C Whirlpool Area'), #glove + ('C Whirlpool Pegs (Outer)', 'C Whirlpool Portal Area'), #hammer + ('C Whirlpool Pegs (Inner)', 'C Whirlpool Area'), #hammer + ('Statues Water Entry', 'Statues Water'), #flippers + ('Statues Landing', 'Statues Area'), + ('Lake Hylia Water Drop', 'Lake Hylia Water'), #flippers + ('Lake Hylia South Water Drop', 'Lake Hylia Water'), #flippers + ('Lake Hylia Northeast Water Drop', 'Lake Hylia Water'), #flippers + ('Lake Hylia Central Water Drop', 'Lake Hylia Water'), #flippers + ('Lake Hylia Island Water Drop', 'Lake Hylia Water'), #flippers + ('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'), + ('Lake Hylia West Pier', 'Lake Hylia Northwest Bank'), + ('Lake Hylia East Pier', 'Lake Hylia Northeast Bank'), + ('Lake Hylia Water D Approach', 'Lake Hylia Water D'), + ('Lake Hylia Water D Leave', 'Lake Hylia Water'), #flippers + ('Ice Cave Water Drop', 'Ice Cave Water'), #flippers + ('Ice Cave Pier', 'Ice Cave Area'), + ('Desert Pass Ledge Drop', 'Desert Pass Area'), + ('Desert Pass Rocks (North)', 'Desert Pass Southeast'), #glove + ('Desert Pass Rocks (South)', 'Desert Pass Area'), #glove + ('Octoballoon Water Drop', 'Octoballoon Water'), #flippers + ('Octoballoon Waterfall Water Drop', 'Octoballoon Water'), #flippers + ('Octoballoon Pier', 'Octoballoon Area'), + + ('Skull Woods Rock (West)', 'Skull Woods Forest'), #glove + ('Skull Woods Rock (East)', 'Skull Woods Portal Entry'), #glove + ('Skull Woods Forgotten Bush (West)', 'Skull Woods Forgotten Path (Northeast)'), #pearl + ('Skull Woods Forgotten Bush (East)', 'Skull Woods Forgotten Path (Southwest)'), #pearl + ('West Dark Death Mountain Drop', 'West Dark Death Mountain (Bottom)'), + ('GT Approach', 'GT Stairs'), + ('GT Leave', 'West Dark Death Mountain (Top)'), + ('Floating Island Drop', 'East Dark Death Mountain (Top)'), + ('East Dark Death Mountain Drop', 'East Dark Death Mountain (Bottom)'), + ('East Dark Death Mountain Bushes', 'East Dark Death Mountain (Bushes)'), + ('Turtle Rock Ledge Drop', 'Turtle Rock Area'), + ('Bumper Cave Rock (Outer)', 'Bumper Cave Entry'), #glove + ('Bumper Cave Rock (Inner)', 'Bumper Cave Area'), #glove + ('Bumper Cave Ledge Drop', 'Bumper Cave Area'), + ('Bumper Cave Entry Drop', 'Bumper Cave Area'), + ('Skull Woods Pass Bush Row (West)', 'Skull Woods Pass East Top Area'), #pearl + ('Skull Woods Pass Bush Row (East)', 'Skull Woods Pass West Area'), #pearl + ('Skull Woods Pass Bush (North)', 'Skull Woods Pass Portal Area'), #pearl + ('Skull Woods Pass Bush (South)', 'Skull Woods Pass East Top Area'), #pearl + ('Skull Woods Pass Rock (North)', 'Skull Woods Pass East Bottom Area'), #mitts + ('Skull Woods Pass Rock (South)', 'Skull Woods Pass Portal Area'), #mitts + ('Dark Graveyard Bush (South)', 'Dark Graveyard North'), #pearl + ('Dark Graveyard Bush (North)', 'Dark Graveyard Area'), #pearl + ('Qirn Jump Water Drop', 'Qirn Jump Water'), #flippers + ('Qirn Jump East Water Drop', 'Qirn Jump Water'), #flippers + ('Qirn Jump Pier', 'Qirn Jump East Bank'), + ('Dark Witch Water Drop', 'Dark Witch Water'), #flippers + ('Dark Witch Northeast Water Drop', 'Dark Witch Water'), #flippers + ('Dark Witch Rock (North)', 'Dark Witch Area'), #glove + ('Dark Witch Rock (South)', 'Dark Witch Northeast'), #glove + ('Catfish Approach Water Drop', 'Catfish Approach Water'), #flippers + ('Catfish Approach Rocks (West)', 'Catfish Approach Ledge'), #mitts/boots + ('Catfish Approach Rocks (East)', 'Catfish Approach Area'), #mitts/boots + ('Catfish Approach Bottom Ledge Drop', 'Catfish Approach Ledge'), + ('Catfish Approach Ledge Drop', 'Catfish Approach Area'), + ('Bush Yard Pegs (Outer)', 'Village of Outcasts Bush Yard'), #hammer + ('Bush Yard Pegs (Inner)', 'Village of Outcasts'), #hammer + ('Shield Shop Fence Drop (Outer)', 'Shield Shop Fence'), + ('Shield Shop Fence Drop (Inner)', 'Shield Shop Area'), + ('Pyramid Exit Ledge Drop', 'Pyramid Area'), + ('Broken Bridge Hammer Rock (South)', 'Broken Bridge Northeast'), #hammer/glove + ('Broken Bridge Hammer Rock (North)', 'Broken Bridge Area'), #hammer/glove + ('Broken Bridge Hookshot Gap', 'Broken Bridge West'), #hookshot + ('Broken Bridge Water Drop', 'Broken Bridge Water'), #flippers + ('Broken Bridge Northeast Water Drop', 'Broken Bridge Water'), #flippers + ('Broken Bridge West Water Drop', 'Broken Bridge Water'), #flippers + ('Peg Area Rocks (West)', 'Hammer Pegs Area'), #mitts + ('Peg Area Rocks (East)', 'Hammer Pegs Entry'), #mitts + ('Dig Game To Ledge Drop', 'Dig Game Ledge'), #mitts + ('Dig Game Ledge Drop', 'Dig Game Area'), + ('Frog Ledge Drop', 'Archery Game Area'), + ('Frog Rock (Inner)', 'Frog Area'), #mitts + ('Frog Rock (Outer)', 'Frog Prison'), #mitts + ('Archery Game Rock (North)', 'Archery Game Area'), #mitts + ('Archery Game Rock (South)', 'Frog Area'), #mitts + ('Hammer Bridge Pegs (North)', 'Hammer Bridge South Area'), #hammer + ('Hammer Bridge Pegs (South)', 'Hammer Bridge North Area'), #hammer + ('Hammer Bridge Water Drop', 'Hammer Bridge Water'), #flippers + ('Hammer Bridge Pier', 'Hammer Bridge North Area'), + ('Mire Teleporter Ledge Drop', 'Mire Area'), + ('Stumpy Approach Bush (North)', 'Stumpy Approach Area'), #pearl + ('Stumpy Approach Bush (South)', 'Stumpy Approach Bush Entry'), #pearl + ('Dark C Whirlpool Water Entry', 'Dark C Whirlpool Water'), #flippers + ('Dark C Whirlpool Landing', 'Dark C Whirlpool Area'), + ('Dark C Whirlpool Rock (Bottom)', 'Dark C Whirlpool Outer Area'), #glove + ('Dark C Whirlpool Rock (Top)', 'Dark C Whirlpool Area'), #glove + ('Dark C Whirlpool Pegs (Outer)', 'Dark C Whirlpool Portal Area'), #hammer + ('Dark C Whirlpool Pegs (Inner)', 'Dark C Whirlpool Area'), #hammer + ('Hype Cave Water Entry', 'Hype Cave Water'), #flippers + ('Hype Cave Landing', 'Hype Cave Area'), + ('Ice Lake Water Drop', 'Ice Lake Water'), #flippers + ('Ice Lake Northeast Water Drop', 'Ice Lake Water'), #flippers + ('Ice Lake Southwest Water Drop', 'Ice Lake Water'), #flippers + ('Ice Lake Southeast Water Drop', 'Ice Lake Water'), #flippers + ('Ice Lake Iceberg Water Entry', 'Ice Lake Water'), #flippers + ('Ice Lake Northeast Pier', 'Ice Lake Northeast Bank'), + ('Shopping Mall Water Drop', 'Shopping Mall Water'), #flippers + ('Shopping Mall Pier', 'Shopping Mall Area'), + ('Bomber Corner Water Drop', 'Bomber Corner Water'), #flippers + ('Bomber Corner Waterfall Water Drop', 'Bomber Corner Water'), #flippers + ('Bomber Corner Pier', 'Bomber Corner Area'), + + # OWG In-Bounds Connections + ('Ice Lake Northeast Pier Hop', 'Ice Lake Northeast Bank'), + ('Ice Lake Iceberg Bomb Jump', 'Ice Lake Iceberg'), + + # OWG Connections + ('Lake Hylia Island FAWT Ledge Drop', 'Lake Hylia Island'), + ('Stone Bridge EC Cliff Water Drop', 'Stone Bridge Water'), #fake flipper + ('C Whirlpool Portal Cliff Ledge Drop', 'C Whirlpool Portal Area'), + ('Checkerboard Cliff Ledge Drop', 'Desert Checkerboard Ledge'), + ('Cave 45 Cliff Ledge Drop', 'Cave 45 Ledge'), + + ('Ice Lake Iceberg FAWT Ledge Drop', 'Ice Lake Iceberg'), + ('Hammer Bridge EC Cliff Water Drop', 'Hammer Bridge Water'), #fake flipper + ('Dark C Whirlpool Portal Cliff Ledge Drop', 'Dark C Whirlpool Portal Area'), + ('Mire Cliff Ledge Drop', 'Mire Area'), + ('Stumpy Approach Cliff Ledge Drop', 'Stumpy Approach Area') +] + +open_mandatory_connections = [ + ('Sanctuary S&Q', 'Sanctuary'), + ('Old Man S&Q', 'Old Man House'), + ('Other World S&Q', 'Pyramid Area'), + + # flute + ('Flute Spot 1', 'West Death Mountain (Bottom)'), + ('Flute Spot 2', 'Potion Shop Area'), + ('Flute Spot 3', 'Kakariko Village'), + ('Flute Spot 4', 'Links House Area'), + ('Flute Spot 5', 'Eastern Nook Area'), + ('Flute Spot 6', 'Desert Teleporter Ledge'), + ('Flute Spot 7', 'Dam Area'), + ('Flute Spot 8', 'Octoballoon Area'), + + # portals + ('West Death Mountain Teleporter', 'West Dark Death Mountain (Bottom)'), + ('East Death Mountain Teleporter', 'East Dark Death Mountain (Bottom)'), + ('TR Pegs Teleporter', 'Turtle Rock Ledge'), + ('Kakariko Teleporter', 'Skull Woods Pass Portal Area'), + ('Castle Gate Teleporter', 'Pyramid Area'), + ('Castle Gate Teleporter (Inner)', 'Pyramid Area'), + ('East Hyrule Teleporter', 'Darkness Nook Area'), + ('South Hyrule Teleporter', 'Dark C Whirlpool Portal Area'), + ('Desert Teleporter', 'Mire Teleporter Ledge'), + ('Lake Hylia Teleporter', 'Ice Palace Area'), + + # OWG connections + ('Mirror To Bombos Tablet Ledge', 'Bombos Tablet Ledge') +] + +inverted_mandatory_connections = [ + ('Sanctuary S&Q', 'Dark Sanctuary Hint'), + ('Old Man S&Q', 'West Dark Death Mountain (Bottom)'), + ('Other World S&Q', 'Hyrule Castle Ledge'), + + # flute + ('Flute Spot 1', 'West Dark Death Mountain (Bottom)'), + ('Flute Spot 2', 'Dark Witch Area'), + ('Flute Spot 3', 'Village of Outcasts'), + ('Flute Spot 4', 'Big Bomb Shop Area'), + ('Flute Spot 5', 'Darkness Nook Area'), + ('Flute Spot 6', 'Mire Teleporter Ledge'), + ('Flute Spot 7', 'Swamp Area'), + ('Flute Spot 8', 'Bomber Corner Area'), + + # modified terrain + ('Spectacle Rock Approach', 'Spectacle Rock Ledge'), + ('Spectacle Rock Leave', 'West Death Mountain (Top)'), + ('Floating Island Bridge (West)', 'East Death Mountain (Top East)'), + ('Floating Island Bridge (East)', 'Death Mountain Floating Island'), + ('Graveyard Ladder (Top)', 'Graveyard Area'), + ('Graveyard Ladder (Bottom)', 'Graveyard Ledge'), + ('EDM To Mimic Ledge Drop', 'Mimic Cave Ledge'), + ('Mimic Ledge Drop', 'East Death Mountain (Bottom)'), + ('Checkerboard Ledge Approach', 'Desert Checkerboard Ledge'), + ('Checkerboard Ledge Leave', 'Desert Area'), + ('Cave 45 Approach', 'Cave 45 Ledge'), + ('Cave 45 Leave', 'Flute Boy Approach Area'), + ('Lake Hylia Island Pier', 'Lake Hylia Island'), + ('Desert Pass Ladder (North)', 'Desert Pass Area'), + ('Desert Pass Ladder (South)', 'Desert Pass Ledge'), + ('Dark Death Mountain Ladder (Top)', 'West Dark Death Mountain (Bottom)'), + ('Dark Death Mountain Ladder (Bottom)', 'West Dark Death Mountain (Top)'), + ('Turtle Rock Tail Ledge Drop', 'Turtle Rock Ledge'), + ('Ice Palace Approach', 'Ice Palace Area'), + ('Ice Palace Leave', 'Ice Lake Iceberg'), + + # portals + ('Dark Death Mountain Teleporter (West)', 'West Death Mountain (Bottom)'), + ('East Dark Death Mountain Teleporter', 'East Death Mountain (Bottom)'), + ('Turtle Rock Teleporter', 'Death Mountain TR Pegs Ledge'), + ('West Dark World Teleporter', 'Lost Woods Pass Portal Area'), + ('Post Aga Teleporter', 'Hyrule Castle Area'), + ('East Dark World Teleporter', 'Eastern Nook Area'), + ('South Dark World Teleporter', 'C Whirlpool Portal Area'), + ('Mire Teleporter', 'Desert Teleporter Ledge'), + ('Ice Lake Teleporter', 'Lake Hylia Central Island') +] + +# non shuffled overworld +default_connections = [ + ('Lost Woods NW', 'Master Sword Meadow SC'), + ('Lost Woods SW', 'Lost Woods Pass NW'), + ('Lost Woods SC', 'Lost Woods Pass NE'), + ('Lost Woods SE', 'Kakariko Fortune NE'), + ('Lost Woods EN', 'Lumberjack WN'), + ('Lumberjack SW', 'Mountain Pass NW'), + ('Mountain Pass SE', 'Kakariko Pond NE'), + ('Zora Waterfall NE', 'Zoras Domain SW'), + ('Lost Woods Pass SW', 'Kakariko NW'), + ('Lost Woods Pass SE', 'Kakariko NC'), + ('Kakariko Fortune SC', 'Kakariko NE'), + ('Kakariko Fortune EN', 'Kakariko Pond WN'), + ('Kakariko Fortune ES', 'Kakariko Pond WS'), + ('Kakariko Pond SW', 'Forgotten Forest NW'), + ('Kakariko Pond SE', 'Forgotten Forest NE'), + ('Kakariko Pond EN', 'Sanctuary WN'), + ('Kakariko Pond ES', 'Sanctuary WS'), + ('Forgotten Forest ES', 'Hyrule Castle WN'), + ('Sanctuary EC', 'Graveyard WC'), + ('Graveyard EC', 'River Bend WC'), + ('River Bend SW', 'Wooden Bridge NW'), + ('River Bend SC', 'Wooden Bridge NC'), + ('River Bend SE', 'Wooden Bridge NE'), + ('River Bend EN', 'Potion Shop WN'), + ('River Bend EC', 'Potion Shop WC'), + ('River Bend ES', 'Potion Shop WS'), + ('Potion Shop EN', 'Zora Approach WN'), + ('Potion Shop EC', 'Zora Approach WC'), + ('Zora Approach NE', 'Zora Waterfall SE'), + ('Kakariko SE', 'Kakariko Suburb NE'), + ('Kakariko ES', 'Blacksmith WS'), + ('Hyrule Castle SW', 'Central Bonk Rocks NW'), + ('Hyrule Castle SE', 'Links House NE'), + ('Hyrule Castle ES', 'Sand Dunes WN'), + ('Wooden Bridge SW', 'Sand Dunes NW'), + ('Sand Dunes SC', 'Stone Bridge NC'), + ('Eastern Palace SW', 'Tree Line NW'), + ('Eastern Palace SE', 'Eastern Nook NE'), + ('Maze Race ES', 'Kakariko Suburb WS'), + ('Kakariko Suburb ES', 'Flute Boy WS'), + ('Flute Boy SW', 'Flute Boy Pass NW'), + ('Flute Boy SC', 'Flute Boy Pass NC'), + ('Flute Boy Pass EC', 'C Whirlpool WC'), + ('C Whirlpool NW', 'Central Bonk Rocks SW'), + ('C Whirlpool SC', 'Dam NC'), + ('C Whirlpool EN', 'Statues WN'), + ('C Whirlpool EC', 'Statues WC'), + ('C Whirlpool ES', 'Statues WS'), + ('Central Bonk Rocks EN', 'Links House WN'), + ('Central Bonk Rocks EC', 'Links House WC'), + ('Central Bonk Rocks ES', 'Links House WS'), + ('Links House SC', 'Statues NC'), + ('Links House ES', 'Stone Bridge WS'), + ('Stone Bridge SC', 'Lake Hylia NW'), + ('Stone Bridge EN', 'Tree Line WN'), + ('Stone Bridge EC', 'Tree Line WC'), + ('Stone Bridge WC', 'Hobo EC'), + ('Tree Line SC', 'Lake Hylia NC'), + ('Tree Line SE', 'Lake Hylia NE'), + ('Desert EC', 'Desert Pass WC'), + ('Desert ES', 'Desert Pass WS'), + ('Desert Pass EC', 'Dam WC'), + ('Desert Pass ES', 'Dam WS'), + ('Dam EC', 'South Pass WC'), + ('Statues SC', 'South Pass NC'), + ('South Pass ES', 'Lake Hylia WS'), + ('Lake Hylia EC', 'Octoballoon WC'), + ('Lake Hylia ES', 'Octoballoon WS'), + ('Octoballoon NW', 'Ice Cave SW'), + ('Octoballoon NE', 'Ice Cave SE'), + ('West Death Mountain EN', 'East Death Mountain WN'), + ('West Death Mountain ES', 'East Death Mountain WS'), + ('East Death Mountain EN', 'Death Mountain TR Pegs WN'), + + ('Skull Woods SW', 'Skull Woods Pass NW'), + ('Skull Woods SC', 'Skull Woods Pass NE'), + ('Skull Woods SE', 'Dark Fortune NE'), + ('Skull Woods EN', 'Dark Lumberjack WN'), + ('Dark Lumberjack SW', 'Bumper Cave NW'), + ('Bumper Cave SE', 'Outcast Pond NE'), + ('Skull Woods Pass SW', 'Village of Outcasts NW'), + ('Skull Woods Pass SE', 'Village of Outcasts NC'), + ('Dark Fortune SC', 'Village of Outcasts NE'), + ('Dark Fortune EN', 'Outcast Pond WN'), + ('Dark Fortune ES', 'Outcast Pond WS'), + ('Outcast Pond SW', 'Shield Shop NW'), + ('Outcast Pond SE', 'Shield Shop NE'), + ('Outcast Pond EN', 'Dark Chapel WN'), + ('Outcast Pond ES', 'Dark Chapel WS'), + ('Dark Chapel EC', 'Dark Graveyard WC'), + ('Dark Graveyard EC', 'Qirn Jump WC'), + ('Qirn Jump SW', 'Broken Bridge NW'), + ('Qirn Jump SC', 'Broken Bridge NC'), + ('Qirn Jump SE', 'Broken Bridge NE'), + ('Qirn Jump EN', 'Dark Witch WN'), + ('Qirn Jump EC', 'Dark Witch WC'), + ('Qirn Jump ES', 'Dark Witch WS'), + ('Dark Witch EN', 'Catfish Approach WN'), + ('Dark Witch EC', 'Catfish Approach WC'), + ('Catfish Approach NE', 'Catfish SE'), + ('Village of Outcasts SE', 'Frog NE'), + ('Village of Outcasts ES', 'Hammer Pegs WS'), + ('Pyramid SW', 'Dark Bonk Rocks NW'), + ('Pyramid SE', 'Big Bomb Shop NE'), + ('Pyramid ES', 'Dark Dunes WN'), + ('Broken Bridge SW', 'Dark Dunes NW'), + ('Dark Dunes SC', 'Hammer Bridge NC'), + ('Palace of Darkness SW', 'Dark Tree Line NW'), + ('Palace of Darkness SE', 'Palace of Darkness Nook NE'), + ('Dig Game EC', 'Frog WC'), + ('Dig Game ES', 'Frog WS'), + ('Frog ES', 'Stumpy WS'), + ('Stumpy SW', 'Stumpy Approach NW'), + ('Stumpy SC', 'Stumpy Approach NC'), + ('Stumpy Approach EC', 'Dark C Whirlpool WC'), + ('Dark C Whirlpool NW', 'Dark Bonk Rocks SW'), + ('Dark C Whirlpool SC', 'Swamp NC'), + ('Dark C Whirlpool EN', 'Hype Cave WN'), + ('Dark C Whirlpool EC', 'Hype Cave WC'), + ('Dark C Whirlpool ES', 'Hype Cave WS'), + ('Dark Bonk Rocks EN', 'Big Bomb Shop WN'), + ('Dark Bonk Rocks EC', 'Big Bomb Shop WC'), + ('Dark Bonk Rocks ES', 'Big Bomb Shop WS'), + ('Big Bomb Shop SC', 'Hype Cave NC'), + ('Big Bomb Shop ES', 'Hammer Bridge WS'), + ('Hammer Bridge SC', 'Ice Lake NW'), + ('Hammer Bridge EN', 'Dark Tree Line WN'), + ('Hammer Bridge EC', 'Dark Tree Line WC'), + ('Dark Tree Line SC', 'Ice Lake NC'), + ('Dark Tree Line SE', 'Ice Lake NE'), + ('Swamp Nook EC', 'Swamp WC'), + ('Swamp Nook ES', 'Swamp WS'), + ('Swamp EC', 'Dark South Pass WC'), + ('Hype Cave SC', 'Dark South Pass NC'), + ('Dark South Pass ES', 'Ice Lake WS'), + ('Ice Lake EC', 'Bomber Corner WC'), + ('Ice Lake ES', 'Bomber Corner WS'), + ('Bomber Corner NW', 'Shopping Mall SW'), + ('Bomber Corner NE', 'Shopping Mall SE'), + ('West Dark Death Mountain EN', 'East Dark Death Mountain WN'), + ('West Dark Death Mountain ES', 'East Dark Death Mountain WS'), + ('East Dark Death Mountain EN', 'Turtle Rock WN'), + + # whirlpool connections + ('C Whirlpool', 'River Bend Whirlpool'), + ('Lake Hylia Whirlpool', 'Zora Whirlpool'), + ('Kakariko Pond Whirlpool', 'Octoballoon Whirlpool'), + ('Qirn Jump Whirlpool', 'Bomber Corner Whirlpool') +] + +mirror_connections = { + 'Skull Woods Forest': ['Lost Woods East Area'], + 'Skull Woods Portal Entry': ['Lost Woods West Area'], + 'Skull Woods Forest (West)': ['Lost Woods West Area'], + 'Skull Woods Forgotten Path (Southwest)': ['Lost Woods West Area'], + 'Skull Woods Forgotten Path (Northeast)': ['Lost Woods East Area', 'Lost Woods West Area'], + + 'Dark Lumberjack Area': ['Lumberjack Area'], + + 'West Dark Death Mountain (Top)': ['West Death Mountain (Top)'], + 'West Dark Death Mountain (Bottom)': ['Spectacle Rock Ledge'], 'Dark Death Mountain Floating Island': ['Death Mountain Floating Island'], + 'East Dark Death Mountain (Top)': ['East Death Mountain (Top West)', 'East Death Mountain (Top East)'], 'Dark Death Mountain Ledge': ['Spiral Cave Ledge', 'Mimic Cave Ledge'], 'Dark Death Mountain Isolated Ledge': ['Fairy Ascension Ledge'], 'East Dark Death Mountain (Bushes)': ['Fairy Ascension Plateau'], - 'East Dark Death Mountain (Bottom)': ['East Death Mountain (Bottom)'], - + 'East Dark Death Mountain (Bottom Left)': ['East Death Mountain (Bottom Left)'], + + 'Turtle Rock Area': ['Death Mountain TR Pegs Area'], + + 'Bumper Cave Area': ['Mountain Pass Area'], + 'Bumper Cave Entry': ['Mountain Pass Entry'], + 'Bumper Cave Ledge': ['Mountain Pass Ledge'], + + 'Catfish Area': ['Zora Waterfall Area'], + + 'Skull Woods Pass West Area': ['Lost Woods Pass West Area'], + 'Skull Woods Pass East Top Area': ['Lost Woods Pass East Top Area'], + 'Skull Woods Pass Portal Area': ['Lost Woods Pass Portal Area'], + 'Skull Woods Pass East Bottom Area': ['Lost Woods Pass East Bottom Area'], + + 'Dark Fortune Area': ['Kakariko Fortune Area'], + + 'Outcast Pond Area': ['Kakariko Pond Area'], + + 'Dark Chapel Area': ['Sanctuary Area', 'Bonk Rock Ledge'], + + 'Dark Graveyard Area': ['Graveyard Area'], 'Dark Graveyard North': ['Graveyard Ledge', 'Kings Grave Area'], - 'Bumper Cave Ledge': ['Death Mountain Return Ledge'], - 'Bumper Cave Entrance': ['Death Mountain Entrance'], + 'Qirn Jump Area': ['River Bend Area'], + 'Qirn Jump East Bank': ['River Bend East Bank'], - 'Northeast Dark World': ['Potion Shop Area'], + 'Dark Witch Area': ['Potion Shop Area'], + 'Dark Witch Northeast': ['Potion Shop Northeast'], - 'Dark Grassy Lawn': ['Bush Covered Lawn'], + 'Catfish Approach Area': ['Zora Approach Area'], + 'Catfish Approach Ledge': ['Zora Approach Ledge'], - 'Hammer Peg Area': ['Bat Cave Ledge'], + 'Village of Outcasts': ['Kakariko Village'], + 'Village of Outcasts Bush Yard': ['Kakariko Village'], - 'East Dark World': ['Hyrule Castle Ledge', 'Hyrule Castle Courtyard'], + 'Shield Shop Area': ['Forgotten Forest Area'], + 'Shield Shop Fence': ['Forgotten Forest Area'], - 'Dark Desert': ['Desert Ledge', 'Desert Checkerboard Ledge', 'Desert Palace Stairs', 'Desert Palace Entrance (North) Spot'], + 'Pyramid Area': ['Hyrule Castle Ledge', 'Hyrule Castle Courtyard', 'Hyrule Castle Area', 'Hyrule Castle East Entry'], + 'Pyramid Exit Ledge': ['Hyrule Castle Courtyard'], + 'Pyramid Pass': ['Hyrule Castle Area'], - 'South Dark World': ['Maze Race Ledge', 'Cave 45 Ledge', 'Bombos Tablet Ledge'], + 'Broken Bridge Area': ['Wooden Bridge Area'], + 'Broken Bridge Northeast': ['Wooden Bridge Area'], + 'Broken Bridge West': ['Wooden Bridge Area'], - 'Dark Lake Hylia Water': ['Lake Hylia Island'], - 'Dark Lake Hylia Central Island': ['Lake Hylia Central Island'], + 'Palace of Darkness Area': ['Eastern Palace Area'], - 'Southeast Dark World': ['Light World'], + 'Hammer Pegs Area': ['Blacksmith Area', 'Blacksmith Ledge'], + 'Hammer Pegs Entry': ['Blacksmith Area'], + + 'Dark Dunes Area': ['Sand Dunes Area'], + + 'Dig Game Area': ['Maze Race Ledge'], + 'Dig Game Ledge': ['Maze Race Ledge'], + + 'Frog Area': ['Kakariko Suburb Area'], + 'Archery Game Area': ['Kakariko Suburb Area'], + + 'Stumpy Area': ['Flute Boy Area'], + 'Stumpy Pass': ['Flute Boy Pass'], + + 'Dark Bonk Rocks Area': ['Central Bonk Rocks Area'], + + 'Big Bomb Shop Area': ['Links House Area'], + + 'Hammer Bridge North Area': ['Stone Bridge North Area'], + 'Hammer Bridge South Area': ['Stone Bridge South Area'], + 'Hammer Bridge Water': ['Stone Bridge Water'], + + 'Dark Tree Line Area': ['Tree Line Area'], + + 'Darkness Nook Area': ['Eastern Nook Area'], + + 'Mire Area': ['Desert Area', 'Desert Ledge', 'Desert Checkerboard Ledge', 'Desert Stairs', 'Desert Ledge Keep'], + + 'Stumpy Approach Area': ['Cave 45 Ledge'], + 'Stumpy Approach Bush Entry': ['Flute Boy Bush Entry'], + + 'Dark C Whirlpool Area': ['C Whirlpool Area'], + 'Dark C Whirlpool Outer Area': ['C Whirlpool Outer Area'], + + 'Hype Cave Area': ['Statues Area'], + + 'Ice Lake Northwest Bank': ['Lake Hylia Northwest Bank'], + 'Ice Lake Northeast Bank': ['Lake Hylia Northeast Bank'], + 'Ice Lake Southwest Ledge': ['Lake Hylia South Shore'], + 'Ice Lake Southeast Ledge': ['Lake Hylia South Shore'], + 'Ice Lake Water': ['Lake Hylia Island'], + 'Ice Palace Area': ['Lake Hylia Central Island'], + 'Ice Lake Iceberg': ['Lake Hylia Water', 'Lake Hylia Water D'], #first one needs flippers + + 'Shopping Mall Area': ['Ice Cave Area'], + + 'Swamp Nook Area': ['Desert Pass Area', 'Desert Pass Ledge'], + + 'Swamp Area': ['Dam Area'], + + 'Dark South Pass Area': ['South Pass Area'], + + 'Bomber Corner Area': ['Octoballoon Area'], - 'Light World': ['Skull Woods Forest (West)', 'West Dark World', 'Hammer Peg Area', 'East Dark World', 'South Dark World', 'Dark Desert', 'Southeast Dark World'], + 'Lost Woods West Area': ['Skull Woods Forest (West)', 'Skull Woods Forgotten Path (Southwest)', 'Skull Woods Portal Entry'], + #'Lost Woods West Area': ['Skull Woods Forgotten Path (Northeast)'], # technically yes, but we dont need it + 'Lost Woods East Area': ['Skull Woods Forgotten Path (Northeast)', 'Skull Woods Forest'], - 'West Death Mountain (Top)': ['Dark Death Mountain (Top)'], + 'Lumberjack Area': ['Dark Lumberjack Area'], + + 'West Death Mountain (Top)': ['West Dark Death Mountain (Top)'], + 'Spectacle Rock Ledge': ['West Dark Death Mountain (Bottom)'], 'West Death Mountain (Bottom)': ['West Dark Death Mountain (Bottom)'], - 'East Death Mountain (Top)': ['Dark Death Mountain (Top)'], - 'Death Mountain Floating Island': ['Dark Death Mountain Floating Island'], + 'East Death Mountain (Top West)': ['East Dark Death Mountain (Top)'], + 'East Death Mountain (Top East)': ['East Dark Death Mountain (Top)'], 'Spiral Cave Ledge': ['Dark Death Mountain Ledge'], 'Mimic Cave Ledge': ['Dark Death Mountain Ledge'], 'Fairy Ascension Ledge': ['Dark Death Mountain Isolated Ledge'], + 'Fairy Ascension Plateau': ['East Dark Death Mountain (Bottom)'], + 'East Death Mountain (Bottom Left)': ['East Dark Death Mountain (Bottom Left)'], 'East Death Mountain (Bottom)': ['East Dark Death Mountain (Bottom)'], + 'Death Mountain Floating Island': ['Dark Death Mountain Floating Island'], - 'Death Mountain Return Ledge': ['Bumper Cave Ledge'], - 'Death Mountain Entrance': ['Bumper Cave Entrance'], + 'Death Mountain TR Pegs Area': ['Turtle Rock Area'], + 'Death Mountain TR Pegs Ledge': ['Turtle Rock Ledge'], - 'Northeast Light World': ['Catfish Area'], + 'Mountain Pass Area': ['Bumper Cave Area'], + 'Mountain Pass Entry': ['Bumper Cave Entry'], + 'Mountain Pass Ledge': ['Bumper Cave Ledge'], - 'Graveyard Ledge': ['West Dark World'], - 'Kings Grave Area': ['West Dark World'], + 'Zora Waterfall Area': ['Catfish Area'], - 'Potion Shop Area': ['Northeast Dark World'], + 'Lost Woods Pass West Area': ['Skull Woods Pass West Area'], + 'Lost Woods Pass East Top Area': ['Skull Woods Pass East Top Area'], + 'Lost Woods Pass Portal Area': ['Skull Woods Pass Portal Area'], + 'Lost Woods Pass East Bottom Area': ['Skull Woods Pass East Bottom Area'], - 'Bush Covered Lawn': ['Dark Grassy Lawn'], - 'Bomb Hut Area': ['West Dark World'], + 'Kakariko Fortune Area': ['Dark Fortune Area'], - 'Hyrule Castle Secret Entrance Area': ['East Dark World'], + 'Kakariko Pond Area': ['Outcast Pond Area'], - 'Maze Race Ledge': ['South Dark World'], + 'Sanctuary Area': ['Dark Chapel Area'], + 'Bonk Rock Ledge': ['Dark Chapel Area'], - 'Cave 45 Ledge': ['South Dark World'], + 'Graveyard Area': ['Dark Graveyard Area'], + 'Graveyard Ledge': ['Dark Graveyard Area'], + 'Kings Grave Area': ['Dark Graveyard Area'], - 'Desert Palace Stairs': ['Dark Desert'], - 'Desert Ledge': ['Dark Desert'], - 'Desert Palace Entrance (North) Spot': ['Dark Desert'], - 'Desert Checkerboard Ledge': ['Dark Desert'], + 'River Bend Area': ['Qirn Jump Area'], + 'River Bend East Bank': ['Qirn Jump East Bank'], - 'Lake Hylia Central Island': ['Dark Lake Hylia Central Island'] -} \ No newline at end of file + 'Potion Shop Area': ['Dark Witch Area'], + 'Potion Shop Northeast': ['Dark Witch Northeast'], + + 'Zora Approach Area': ['Catfish Approach Area'], + 'Zora Approach Ledge': ['Catfish Approach Ledge'], + + 'Kakariko Village': ['Village of Outcasts'], + 'Kakariko Southwest': ['Village of Outcasts'], + 'Kakariko Bush Yard': ['Village of Outcasts Bush Yard'], + + 'Forgotten Forest Area': ['Shield Shop Area'], + + 'Hyrule Castle Area': ['Pyramid Area', 'Pyramid Pass'], + 'Hyrule Castle Southwest': ['Pyramid Pass'], + 'Hyrule Castle Courtyard': ['Pyramid Area'], + 'Hyrule Castle Courtyard Northeast': ['Pyramid Area'], + 'Hyrule Castle Ledge': ['Pyramid Area'], + 'Hyrule Castle East Entry': ['Pyramid Area'], + + 'Wooden Bridge Area': ['Broken Bridge Area', 'Broken Bridge West'], + 'Wooden Bridge Northeast': ['Broken Bridge Northeast'], + + 'Eastern Palace Area': ['Palace of Darkness Area'], + + 'Blacksmith Area': ['Hammer Pegs Area', 'Hammer Pegs Entry'], + + 'Sand Dunes Area': ['Dark Dunes Area'], + + 'Maze Race Area': ['Dig Game Area'], + 'Maze Race Ledge': ['Dig Game Ledge'], + + 'Kakariko Suburb Area': ['Frog Area', 'Frog Prison', 'Archery Game Area'], + + 'Flute Boy Area': ['Stumpy Area'], + 'Flute Boy Pass': ['Stumpy Pass'], + + 'Central Bonk Rocks Area': ['Dark Bonk Rocks Area'], + + 'Links House Area': ['Big Bomb Shop Area'], + + 'Stone Bridge North Area': ['Hammer Bridge North Area'], + 'Stone Bridge South Area': ['Hammer Bridge South Area'], + 'Stone Bridge Water': ['Hammer Bridge Water'], + + 'Tree Line Area': ['Dark Tree Line Area'], + + 'Eastern Nook Area': ['Darkness Nook Area'], + + 'Desert Area': ['Mire Area'], + 'Desert Ledge': ['Mire Area'], + 'Desert Ledge Keep': ['Mire Area'], + 'Desert Checkerboard Ledge': ['Mire Area'], + 'Desert Stairs': ['Mire Area'], + + 'Flute Boy Approach Area': ['Stumpy Approach Area'], + 'Cave 45 Ledge': ['Stumpy Approach Area'], + 'Flute Boy Bush Entry': ['Stumpy Approach Bush Entry'], + + 'C Whirlpool Area': ['Dark C Whirlpool Area'], + 'C Whirlpool Outer Area': ['Dark C Whirlpool Outer Area'], + + 'Statues Area': ['Hype Cave Area'], + + 'Lake Hylia Northwest Bank': ['Ice Lake Northwest Bank'], + 'Lake Hylia South Shore': ['Ice Lake Southwest Ledge', 'Ice Lake Southeast Ledge'], + 'Lake Hylia Northeast Bank': ['Ice Lake Northeast Bank'], + 'Lake Hylia Central Island': ['Ice Palace Area'], + 'Lake Hylia Water D': ['Ice Lake Iceberg'], + + 'Ice Cave Area': ['Shopping Mall Area'], + + 'Desert Pass Area': ['Swamp Nook Area'], + 'Desert Pass Southeast': ['Swamp Nook Area'], + 'Desert Pass Ledge': ['Swamp Nook Area'], + + 'Dam Area': ['Swamp Area'], + + 'South Pass Area': ['Dark South Pass Area'], + + 'Octoballoon Area': ['Bomber Corner Area'] +} + +OWTileRegions = bidict({ + 'Lost Woods West Area': 0x00, + 'Lost Woods East Area': 0x00, + + 'Lumberjack Area': 0x02, + + 'West Death Mountain (Top)': 0x03, + 'Spectacle Rock Ledge': 0x03, + 'West Death Mountain (Bottom)': 0x03, + + 'East Death Mountain (Top West)': 0x05, + 'East Death Mountain (Top East)': 0x05, + 'Spiral Cave Ledge': 0x05, + 'Mimic Cave Ledge': 0x05, + 'Fairy Ascension Ledge': 0x05, + 'Fairy Ascension Plateau': 0x05, + 'East Death Mountain (Bottom Left)': 0x05, + 'East Death Mountain (Bottom)': 0x05, + 'Death Mountain Floating Island': 0x05, + + 'Death Mountain TR Pegs Area': 0x07, + 'Death Mountain TR Pegs Ledge': 0x07, + + 'Mountain Pass Area': 0x0a, + 'Mountain Pass Entry': 0x0a, + 'Mountain Pass Ledge': 0x0a, + + 'Zora Waterfall Area': 0x0f, + 'Zora Waterfall Water': 0x0f, + 'Zora Waterfall Entryway': 0x0f, + + 'Lost Woods Pass West Area': 0x10, + 'Lost Woods Pass East Top Area': 0x10, + 'Lost Woods Pass Portal Area': 0x10, + 'Lost Woods Pass East Bottom Area': 0x10, + + 'Kakariko Fortune Area': 0x11, + + 'Kakariko Pond Area': 0x12, + + 'Sanctuary Area': 0x13, + 'Bonk Rock Ledge': 0x13, + + 'Graveyard Area': 0x14, + 'Graveyard Ledge': 0x14, + 'Kings Grave Area': 0x14, + + 'River Bend Area': 0x15, + 'River Bend East Bank': 0x15, + 'River Bend Water': 0x15, + + 'Potion Shop Area': 0x16, + 'Potion Shop Northeast': 0x16, + 'Potion Shop Water': 0x16, + + 'Zora Approach Area': 0x17, + 'Zora Approach Ledge': 0x17, + 'Zora Approach Water': 0x17, + + 'Kakariko Village': 0x18, + 'Kakariko Southwest': 0x18, + 'Kakariko Bush Yard': 0x18, + + 'Forgotten Forest Area': 0x1a, + + 'Hyrule Castle Area': 0x1b, + 'Hyrule Castle Southwest': 0x1b, + 'Hyrule Castle Courtyard': 0x1b, + 'Hyrule Castle Courtyard Northeast': 0x1b, + 'Hyrule Castle Ledge': 0x1b, + 'Hyrule Castle East Entry': 0x1b, + 'Hyrule Castle Water': 0x1b, + + 'Wooden Bridge Area': 0x1d, + 'Wooden Bridge Northeast': 0x1d, + 'Wooden Bridge Water': 0x1d, + + 'Eastern Palace Area': 0x1e, + + 'Blacksmith Area': 0x22, + 'Blacksmith Ledge': 0x22, + + 'Sand Dunes Area': 0x25, + + 'Maze Race Area': 0x28, + 'Maze Race Ledge': 0x28, + 'Maze Race Prize': 0x28, + + 'Kakariko Suburb Area': 0x29, + + 'Flute Boy Area': 0x2a, + 'Flute Boy Pass': 0x2a, + + 'Central Bonk Rocks Area': 0x2b, + + 'Links House Area': 0x2c, + + 'Stone Bridge North Area': 0x2d, + 'Stone Bridge South Area': 0x2d, + 'Stone Bridge Water': 0x2d, + + 'Tree Line Area': 0x2e, + 'Tree Line Water': 0x2e, + + 'Eastern Nook Area': 0x2f, + + 'Desert Area': 0x30, + 'Desert Ledge': 0x30, + 'Desert Ledge Keep': 0x30, + 'Desert Checkerboard Ledge': 0x30, + 'Desert Stairs': 0x30, + 'Desert Mouth': 0x30, + 'Desert Teleporter Ledge': 0x30, + 'Bombos Tablet Ledge': 0x30, + + 'Flute Boy Approach Area': 0x32, + 'Flute Boy Bush Entry': 0x32, + 'Cave 45 Ledge': 0x32, + + 'C Whirlpool Area': 0x33, + 'C Whirlpool Portal Area': 0x33, + 'C Whirlpool Water': 0x33, + 'C Whirlpool Outer Area': 0x33, + + 'Statues Area': 0x34, + 'Statues Water': 0x34, + + 'Lake Hylia Northwest Bank': 0x35, + 'Lake Hylia Northeast Bank': 0x35, + 'Lake Hylia South Shore': 0x35, + 'Lake Hylia Central Island': 0x35, + 'Lake Hylia Island': 0x35, + 'Lake Hylia Water': 0x35, + 'Lake Hylia Water D': 0x35, + + 'Ice Cave Area': 0x37, + 'Ice Cave Water': 0x37, + + 'Desert Pass Area': 0x3a, + 'Desert Pass Southeast': 0x3a, + 'Desert Pass Ledge': 0x3a, + + 'Dam Area': 0x3b, + + 'South Pass Area': 0x3c, + + 'Octoballoon Area': 0x3f, + 'Octoballoon Water': 0x3f, + 'Octoballoon Water Ledge': 0x3f, + + 'Skull Woods Forest': 0x40, + 'Skull Woods Portal Entry': 0x40, + 'Skull Woods Forest (West)': 0x40, + 'Skull Woods Forgotten Path (Southwest)': 0x40, + 'Skull Woods Forgotten Path (Northeast)': 0x40, + + 'Dark Lumberjack Area': 0x42, + + 'West Dark Death Mountain (Top)': 0x43, + 'GT Stairs': 0x43, + 'West Dark Death Mountain (Bottom)': 0x43, + + 'East Dark Death Mountain (Top)': 0x45, + 'East Dark Death Mountain (Bottom Left)': 0x45, + 'East Dark Death Mountain (Bottom)': 0x45, + 'East Dark Death Mountain (Bushes)': 0x45, + 'Dark Death Mountain Ledge': 0x45, + 'Dark Death Mountain Isolated Ledge': 0x45, + 'Dark Death Mountain Floating Island': 0x45, + + 'Turtle Rock Area': 0x47, + 'Turtle Rock Ledge': 0x47, + + 'Bumper Cave Area': 0x4a, + 'Bumper Cave Entry': 0x4a, + 'Bumper Cave Ledge': 0x4a, + + 'Catfish Area': 0x4f, + + 'Skull Woods Pass West Area': 0x50, + 'Skull Woods Pass East Top Area': 0x50, + 'Skull Woods Pass Portal Area': 0x50, + 'Skull Woods Pass East Bottom Area': 0x50, + + 'Dark Fortune Area': 0x51, + + 'Outcast Pond Area': 0x52, + + 'Dark Chapel Area': 0x53, + + 'Dark Graveyard Area': 0x54, + 'Dark Graveyard North': 0x54, + + 'Qirn Jump Area': 0x55, + 'Qirn Jump East Bank': 0x55, + 'Qirn Jump Water': 0x55, + + 'Dark Witch Area': 0x56, + 'Dark Witch Northeast': 0x56, + 'Dark Witch Water': 0x56, + + 'Catfish Approach Area': 0x57, + 'Catfish Approach Ledge': 0x57, + 'Catfish Approach Water': 0x57, + + 'Village of Outcasts': 0x58, + 'Village of Outcasts Bush Yard': 0x58, + + 'Shield Shop Area': 0x5a, + 'Shield Shop Fence': 0x5a, + + 'Pyramid Area': 0x5b, + 'Pyramid Exit Ledge': 0x5b, + 'Pyramid Pass': 0x5b, + 'Pyramid Water': 0x5b, + + 'Broken Bridge Area': 0x5d, + 'Broken Bridge Northeast': 0x5d, + 'Broken Bridge West': 0x5d, + 'Broken Bridge Water': 0x5d, + + 'Palace of Darkness Area': 0x5e, + + 'Hammer Pegs Area': 0x62, + 'Hammer Pegs Entry': 0x62, + + 'Dark Dunes Area': 0x65, + + 'Dig Game Area': 0x68, + 'Dig Game Ledge': 0x68, + + 'Frog Area': 0x69, + 'Frog Prison': 0x69, + 'Archery Game Area': 0x69, + + 'Stumpy Area': 0x6a, + 'Stumpy Pass': 0x6a, + + 'Dark Bonk Rocks Area': 0x6b, + + 'Big Bomb Shop Area': 0x6c, + + 'Hammer Bridge North Area': 0x6d, + 'Hammer Bridge South Area': 0x6d, + 'Hammer Bridge Water': 0x6d, + + 'Dark Tree Line Area': 0x6e, + 'Dark Tree Line Water': 0x6e, + + 'Darkness Nook Area': 0x6f, + + 'Mire Area': 0x70, + 'Mire Teleporter Ledge': 0x70, + + 'Stumpy Approach Area': 0x72, + 'Stumpy Approach Bush Entry': 0x72, + + 'Dark C Whirlpool Area': 0x73, + 'Dark C Whirlpool Portal Area': 0x73, + 'Dark C Whirlpool Water': 0x73, + 'Dark C Whirlpool Outer Area': 0x73, + + 'Hype Cave Area': 0x74, + 'Hype Cave Water': 0x74, + + 'Ice Lake Northwest Bank': 0x75, + 'Ice Lake Northeast Bank': 0x75, + 'Ice Lake Southwest Ledge': 0x75, + 'Ice Lake Southeast Ledge': 0x75, + 'Ice Lake Water': 0x75, + 'Ice Lake Iceberg': 0x75, + 'Ice Palace Area': 0x75, + + 'Shopping Mall Area': 0x77, + 'Shopping Mall Water': 0x77, + + 'Swamp Nook Area': 0x7a, + + 'Swamp Area': 0x7b, + + 'Dark South Pass Area': 0x7c, + + 'Bomber Corner Area': 0x7f, + 'Bomber Corner Water': 0x7f, + 'Bomber Corner Water Ledge': 0x7f, + + 'Master Sword Meadow': 0x80, + 'Hobo Bridge': 0x80, + + 'Zoras Domain': 0x81 +}) \ No newline at end of file diff --git a/Plando.py b/Plando.py index c32c75cd..1af62926 100755 --- a/Plando.py +++ b/Plando.py @@ -9,6 +9,7 @@ import sys from BaseClasses import World from Regions import create_regions +from OverworldShuffle import link_overworld from EntranceShuffle import link_entrances, connect_entrance, connect_two_way, connect_exit from Rom import patch_rom, LocalRom, write_string_to_rom, apply_rom_settings, get_sprite_from_name from Rules import set_rules @@ -42,6 +43,7 @@ def main(args): create_regions(world, 1) create_dungeons(world, 1) + link_overworld(world, 1) link_entrances(world, 1) logger.info('Calculating Access Rules.') @@ -122,12 +124,6 @@ def fill_world(world, plando, text_patches): elif line.startswith('!light_cone_sewers'): _, sewerstr = line.split(':', 1) world.sewer_light_cone = {1: sewerstr.strip().lower() == 'true'} - elif line.startswith('!light_cone_lw'): - _, lwconestr = line.split(':', 1) - world.light_world_light_cone = lwconestr.strip().lower() == 'true' - elif line.startswith('!light_cone_dw'): - _, dwconestr = line.split(':', 1) - world.dark_world_light_cone = dwconestr.strip().lower() == 'true' elif line.startswith('!fix_trock_doors'): _, trdstr = line.split(':', 1) world.fix_trock_doors = {1: trdstr.strip().lower() == 'true'} diff --git a/PotShuffle.py b/PotShuffle.py index b90d7863..c7164565 100644 --- a/PotShuffle.py +++ b/PotShuffle.py @@ -750,7 +750,7 @@ vanilla_pots = { Pot(88, 14, PotItem.SmallMagic, 'Bumper Cave (bottom)', obj=RoomObject(0x0AADED, [0xB3, 0x73, 0xFA])), Pot(92, 14, PotItem.Heart, 'Bumper Cave (bottom)', obj=RoomObject(0x0AADF0, [0xBB, 0x73, 0xFA])), Pot(96, 14, PotItem.SmallMagic, 'Bumper Cave (bottom)', obj=RoomObject(0x0AADF3, [0xC3, 0x73, 0xFA]))], - 0xF1: [Pot(64, 5, PotItem.Heart, 'Old Man Cave', obj=RoomObject(0x0AA6B2, [0x83, 0x2B, 0xFA]))], + 0xF1: [Pot(64, 5, PotItem.Heart, 'Old Man Cave (East)', obj=RoomObject(0x0AA6B2, [0x83, 0x2B, 0xFA]))], 0xF3: [Pot(0x28, 0x14, PotItem.Nothing, 'Elder House', obj=RoomObject(0x0AA76F, [0x53, 0xA3, 0xFA])), Pot(0x2C, 0x14, PotItem.Nothing, 'Elder House', obj=RoomObject(0x0AA772, [0x5B, 0xA3, 0xFA])), Pot(0x30, 0x14, PotItem.Nothing, 'Elder House', obj=RoomObject(0x0AA775, [0x63, 0xA3, 0xFA]))], @@ -788,12 +788,12 @@ vanilla_pots = { 0x108: [Pot(166, 19, PotItem.Chicken, 'Chicken House', obj=RoomObject(0x03EFA9, [0x4F, 0x9F, 0xFA]))], 0x10C: [Pot(88, 14, PotItem.Heart, 'Hookshot Fairy', obj=RoomObject(0x03F329, [0xB3, 0x73, 0xFA]))], # note: these addresses got moved thanks to waterfall fairy edit - 0x114: [Pot(92, 4, PotItem.Heart, 'Dark Desert Hint', obj=RoomObject(0x03F79A, [0xBB, 0x23, 0xFA])), - Pot(96, 4, PotItem.Heart, 'Dark Desert Hint', obj=RoomObject(0x03F79D, [0xC3, 0x23, 0xFA])), - Pot(92, 5, PotItem.Bomb, 'Dark Desert Hint', obj=RoomObject(0x03F7A0, [0xBB, 0x2B, 0xFA])), - Pot(96, 5, PotItem.Bomb, 'Dark Desert Hint', obj=RoomObject(0x03F7A3, [0xC3, 0x2B, 0xFA])), - Pot(92, 10, PotItem.FiveArrows, 'Dark Desert Hint', obj=RoomObject(0x03F7A6, [0xBB, 0x53, 0xFA])), - Pot(96, 10, PotItem.Heart, 'Dark Desert Hint', obj=RoomObject(0x03F7A9, [0xC3, 0x53, 0xFA]))], + 0x114: [Pot(92, 4, PotItem.Heart, 'Mire Hint', obj=RoomObject(0x03F79A, [0xBB, 0x23, 0xFA])), + Pot(96, 4, PotItem.Heart, 'Mire Hint', obj=RoomObject(0x03F79D, [0xC3, 0x23, 0xFA])), + Pot(92, 5, PotItem.Bomb, 'Mire Hint', obj=RoomObject(0x03F7A0, [0xBB, 0x2B, 0xFA])), + Pot(96, 5, PotItem.Bomb, 'Mire Hint', obj=RoomObject(0x03F7A3, [0xC3, 0x2B, 0xFA])), + Pot(92, 10, PotItem.FiveArrows, 'Mire Hint', obj=RoomObject(0x03F7A6, [0xBB, 0x53, 0xFA])), + Pot(96, 10, PotItem.Heart, 'Mire Hint', obj=RoomObject(0x03F7A9, [0xC3, 0x53, 0xFA]))], 0x117: [Pot(138, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCB2, [0x17, 0x1F, 0xFA])), # 0x38A -> 38A Pot(142, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCB8, [0x1F, 0x1F, 0xFA])), Pot(166, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCC1, [0x4F, 0x1F, 0xFA])), @@ -919,14 +919,14 @@ def shuffle_pots(world, player): new_pot_contents.room_map[super_tile] = new_pots - world.pot_contents[player] = new_pot_contents + world.data_tables[player].pot_secret_table = new_pot_contents def shuffle_pot_switches(world, player): import RaceRandom as random for super_tile in vanilla_pots: - new_pots = world.pot_contents[player].room_map[super_tile] + new_pots = world.data_tables[player].pot_secret_table.room_map[super_tile] # sort in the order Hole, Switch, Key, Other, Nothing sort_order = {PotItem.Hole: 4, PotItem.Switch: 3, PotItem.Key: 2, PotItem.Nothing: 0} old_pots = sorted(new_pots, key=lambda pot: sort_order.get(pot.item, 1), reverse=True) @@ -1016,16 +1016,16 @@ key_drop_data = { 'Swamp Palace - Trench 2 Pot Key': ['Pot', 0x35, 'in a pot in Swamp Palace', 'Small Key (Swamp Palace)'], 'Swamp Palace - Waterway Pot Key': ['Pot', 0x16, 'in a pot in Swamp Palace', 'Small Key (Swamp Palace)'], 'Skull Woods - West Lobby Pot Key': ['Pot', 0x56, 'in a pot in Skull Woods', 'Small Key (Skull Woods)'], - 'Skull Woods - Spike Corner Key Drop': ['Drop', (0x09DD74, 0x39, 1), 'dropped near Mothula', 'Small Key (Skull Woods)'], + 'Skull Woods - Spike Corner Key Drop': ['Drop', (0x09DD74, 0x39, 2), 'dropped near Mothula', 'Small Key (Skull Woods)'], "Thieves' Town - Hallway Pot Key": ['Pot', 0xBC, "in a pot in Thieves Town", 'Small Key (Thieves Town)'], "Thieves' Town - Spike Switch Pot Key": ['Pot', 0xAB, "in a pot in Thieves Town", 'Small Key (Thieves Town)'], 'Ice Palace - Jelly Key Drop': ['Drop', (0x09DA21, 0xE, 3), 'dropped in Ice Palace', 'Small Key (Ice Palace)'], - 'Ice Palace - Conveyor Key Drop': ['Drop', (0x09DE08, 0x3E, 8), 'dropped in Ice Palace', 'Small Key (Ice Palace)'], + 'Ice Palace - Conveyor Key Drop': ['Drop', (0x09DE08, 0x3E, 9), 'dropped in Ice Palace', 'Small Key (Ice Palace)'], 'Ice Palace - Hammer Block Key Drop': ['Pot', 0x3F, 'under a block in Ice Palace', 'Small Key (Ice Palace)'], 'Ice Palace - Many Pots Pot Key': ['Pot', 0x9F, 'in a pot in Ice Palace', 'Small Key (Ice Palace)'], 'Misery Mire - Spikes Pot Key': ['Pot', 0xB3, 'in a pot in Misery Mire', 'Small Key (Misery Mire)'], 'Misery Mire - Fishbone Pot Key': ['Pot', 0xA1, 'in a pot in forgotten Mire', 'Small Key (Misery Mire)'], - 'Misery Mire - Conveyor Crystal Key Drop': ['Drop', (0x09E7FB, 0xC1, 9), 'dropped in Misery Mire', 'Small Key (Misery Mire)'], + 'Misery Mire - Conveyor Crystal Key Drop': ['Drop', (0x09E7FB, 0xC1, 10), 'dropped in Misery Mire', 'Small Key (Misery Mire)'], 'Turtle Rock - Pokey 1 Key Drop': ['Drop', (0x09E70D, 0xB6, 5), 'dropped in Turtle Rock', 'Small Key (Turtle Rock)'], 'Turtle Rock - Pokey 2 Key Drop': ['Drop', (0x09DA5D, 0x13, 6), 'dropped in Turtle Rock', 'Small Key (Turtle Rock)'], 'Ganons Tower - Conveyor Cross Pot Key': ['Pot', 0x8B, "in a pot in Ganon's Tower", 'Small Key (Ganons Tower)'], @@ -1034,18 +1034,21 @@ key_drop_data = { 'Ganons Tower - Mini Helmasaur Key Drop': ['Drop', (0x09DDC4, 0x3D, 2), "dropped atop Ganon's Tower", 'Small Key (Ganons Tower)'] } +key_drop_special = {(data[1][1], data[1][2]): name for name, data in key_drop_data.items() if data[0] == 'Drop'} + class PotSecretTable(object): def __init__(self): self.room_map = defaultdict(list) self.multiworld_count = 0 - def write_pot_data_to_rom(self, rom, colorize): - pointer_address = 0x140000 # pots currently in bank 28 - pointer_offset = 0x128 * 2 - empty_address = (pointer_address + pointer_offset) + def write_pot_data_to_rom(self, rom, colorize, data_tables): + pointer_address = snes_to_pc(0x09D87E) # pots currently in bank 9 + data_bank_address = snes_to_pc(0x09DACE) + # pointer_offset = 0x128 * 2 + empty_address = data_bank_address empty_pointer = pc_to_snes(empty_address) & 0xFFFF - data_pointer = pointer_address + pointer_offset + 2 + data_pointer = data_bank_address + 2 for room in range(0, 0x128): if room in self.room_map: list_idx = 0 @@ -1056,9 +1059,13 @@ class PotSecretTable(object): rom.write_bytes(data_pointer + list_idx * 3, pot.pot_data()) if pot.location is not None and not pot.location.forced_item: collection_rate_mask |= 1 << (15 - list_idx) - if colorize and pot.obj_ref: - pot.obj_ref.change_type(Shuffled_Pot) - pot.obj_ref.write_to_rom(rom) + if colorize: + if room in data_tables.room_list: + room_object = data_tables.room_list[room] + room_object.find_all_pots()[list_idx].change_type(Shuffled_Pot) + elif pot.obj_ref: + pot.obj_ref.change_type(Shuffled_Pot) + pot.obj_ref.write_to_rom(rom) list_idx += 1 rom.write_bytes(data_pointer + list_idx * 3, [0xFF, 0xFF]) rom.write_bytes(snes_to_pc(0x28AA60) + room * 2, int16_as_bytes(collection_rate_mask)) diff --git a/README.md b/README.md index 24e79807..cac13120 100644 --- a/README.md +++ b/README.md @@ -37,6 +37,7 @@ See https://alttpr.com/ for more details on the normal randomizer. 7. [Standard Changes](#standard-changes) 8. [Game Options](#game-options) 9. [Generation Setup & Miscellaneous](#generation-setup--miscellaneous) + 10. [Glitched Logic](#glitched-logic) ## Setup and Installation @@ -597,4 +598,34 @@ Can be used to set a seed number to generate. Using the same seed with same sett Use to batch generate multiple seeds with same settings. If a seed number is provided, it will be used for the first seed, then used to derive the next seed (i.e. generating 10 seeds with the same seed number given will produce the same 10 (different) roms each time). +## Glitched Logic +Overworld glitches, Hybrid Major Glitches (HMG) and No Logic are currently supported. + +### Overworld Glitches +_Support added by qadan and compiling_ + +Overworld Glitches logic includes (but is not limited to) the following: +* Overworld teleports and clips to reach various items/entrances +* Use of superbunny to obtain items and/or bonk open entrances +* Use of mirror to access Desert Palace East Entrance +* Use of bunny pocket to access the Back of Skull Woods and VOO Hammer house entrances + + +### Hybrid Major Glitches +_Support added by Muffins (ported from work by Espeon)_. + +**Not currently compatible with Door Shuffle** + +Hybrid Major Glitches logic includes the following: +* All Overworld Glitches logic +* Kikiskip to access PoD wihtout MP or DW access +* IP Lobby clip to skip fire requirement +* Traversal between TT -> Desert +* Traversal between Spec rock upper -> Spec rock mid +* Traversal between Paradox lower -> Paradox mid + upper +* Traversal between Mire -> Hera -> Swamp +* Stealing SK from Mire to open SP +* Using the Mire big key to open Hera doors and big chest + +All traversals mentioned are considered connectors in entrance shuffle diff --git a/RELEASENOTES.md b/RELEASENOTES.md index 9f04d779..c668042b 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,5 +1,37 @@ # New Features +FastROM changes have been included now. + +## Enemizer Features + +Please see this document for extensive details. [Enemizer in DR](https://docs.google.com/document/d/1iwY7Gy50DR3SsdXVaLFIbx4xRBqo9a-e1_jAl5LMCX8/edit?usp=sharing) + +Key points: +* Enemizer no longer uses a third party program. It is now built-in. +* New option under Shuffle Enemy Drops: Underworld. Any underworld enemy can drop items. +* New option under Enemizer tab: Enemy Logic + +Please read the entire document above for extensive details about enemizer and enemy drop shuffle systems. + +Enemizer main changes: +* Several sprites added to the pool. Most notable is how enemies behave on shallow water. They work now. +* Clearing rooms, spawnable chests, and enemy keys drops can now have enemies with specific logic in the room. This logic is controlled by the new Enemy Logic option +* New system for banning enemies that cause issue is place. If you see an enemy in a place that would cause issue, please report it and it can be banned to never happen again. Initial bans can be found [in the code](source/enemizer/enemy_deny.yaml) for the curious +* Thieves are always unkillable, but banned from the entire underworld. We can selectively ban them from problematic places in the overworld, and if someone wants to figure out where they could be safe in the underworld, I'll allow them there once the major problems have been banned. +* THe old "random" and "legacy" options have been discarded for enemy shuffle. Tile room patterns are currently shuffled with enemies. + +Underworld drops: + +* A flashing blue square added to help locate enemies that have remaining drops on the supertile. (Dungeons and caves without a compass get this for free.) +* Flying enemies, spawned enemies, and enemies with special death routines will not drop items. +* Pikits do not drop their item if they have eaten a shield. +* Hovers in swamp waterway do no drop items due to a layer issue that's not been solved. +* Enemies that are over pits require boomerang or hookshot to collect the item +* Enemies behind rails require the boomerang (hookshot can sequence break in certain cases) +* Enemies that spawn on walls do not drop items. (Keese normally don't, but in enemizer these can be valid drops otherwise. The document has a visual guide.) + +(Older notes below) + One major change with this update is that big key doors and certain trap doors are no longer guaranteed to be vanilla in Dungeon Door Shuffle modes even if you choose not to shuffle those types. A newer algorithm for putting dungeons together has been written and it will remove big key doors and trap doors when necessary to ensure progress can be made. Please note that retro features are now independently customizable as referenced below. Selecting Retro mode or World State: Retro will change Bow Mode to Retro (Progressive). Take Anys to Random, and Small Keys to Universal. @@ -109,6 +141,139 @@ These are now independent of retro mode and have three options: None, Random, an # Bug Fixes and Notes +* 1.4.1.3v + * Enemizer: Raven/Murderdactyls using the correct damage table + * Enemzier: Boss drops only center when boss shuffle is on +* 1.4.1.2v + * Expert/Hard Item Pool: Capacity fairy no longer gives out free crystal + * Vanilla door + Universal Keys: Generation fixed + * Boss Shuffle: Generation fixed (thanks Codemann for easy solution) + * Vanilla ER: No need for ability to check prizes on keysanity menu + * Swapped ER: Possible generation issue fixed (thanks Codemann) + * Enemizer: Roller ban + * Performance: Faster text boxes. Thanks Kan! +* 1.4.1.1v + * Logic: Moon pearl logic respects blocked doors +* 1.4.1.0v + * World Model Refactor: The overworld has been split up by screen, brings OR and DR a bit closer together in the model sense. A few OWG clips have been rewritten to fit into this new logic better. + * Logic: New logic for some bosses on ice + * Helmasaur on Ice: Bombs for mask, sword or arrows for 2nd phase + * Blind on Ice: Beam sword, Somaria, or Byrna plus magic extension for damage. Red shield or Byrna for protection. + * Kholdstare on Ice: Three options (after cracking the shell) + * Beam sword + * Fire Rod with 1.5 magic extensions + * Fire Rod & Bombos & any Sword & 1 Magic Extension + * Vitreous on Ice: Arrows and Bombs or a Beam Sword + * Trinexx on Ice: Boots always required for dodging. Damage options: + * Gold sword + * Tempered sword with magic extension + * Hammer or Master sword with 3 magic extensions (Rod spam for elemental heads, non-ideal weapon for last phase) + * Trinexx on Ice forbidden in doors seeds until we can model some health requirements. Low health Trinexx still isn't realistically feasible (bascially playing OHKO) + * Logic: Added silver arrows as Arrghus damage option when item functionality is not set to hard or expert + * Logic: Byrna not in logic for laser bridge when item functionality is set to hard or expert + * Enemizer Damage Rework: + * Shuffled: Actually shuffles the damage groups in the table instead of picking random numbers and reducing for mails from there. Enemies will still be assigned to a damage group randomly. + * There will always be at least one group which does no damage. The thief will always be in that group. Ganon always has his own group. + * Glitched modes: Aga 1 should be vulnerable in rain state for glitched modes + * Generation: Trinexx and Lanmolas room allowed as lobbies in intensity 3 (works with enemizer now) + * Enemy AI: Terrorpin AI code removed. May help with unusual enemy behavior? +* 1.4.0.1v + * Key logic: Vanilla key logic fixes. Statically set some HC logic and PoD front door + * Generation: Fix a broken tile pattern + * Inverted: Castle warp should not appear after defeating Aga 1 + * Murahdahla: Should not disappear after Aga 1. May fix other subtle issues. + * Shopsanity: Buying multiple of an item in the potion shop should no longer increase item count. +* 1.4.0.0v + * Initial support for HMG (Thanks Muffins!) + * Generation: fix for bunny walk logic taking up too much memory + * Key Logic: Partial is now the new default + * Enemizer: enemy bans +* 1.3.0.9v + * ER: New Swapped ER mode borrowed from OWR + * ER: fixed a generation error where TR chooses all "must-exits" + * Ganonhunt: playthrough no longer collects crystals + * Vanilla Fill: Uncle weapon is always a sword, medallions for Mire/TR will be vanilla + * Customizer: support shufflebosses/shuffleenemies as well as boss_shuffle/enemy_shuffle + * Enemizer: enemy bans +* 1.3.0.8v + * Enemizer: Red Mimics correctly banned from challenge rooms in appropriate logic setting + * No Logic Standard ER: Rain doors aren't blocked if no logic is enabled. + * Trinexx: attempt to fix early start + * MW Progression Balancing: Change to be percentage based instead of raw count. (80% threshold) + * Take anys: Good Bee cave chosen as take any should no longer prevent generation + * Money balancing: Fixed generation issue + * Enemizer: various enemy bans +* 1.3.0.7v + * Fix for Mimic Cave enemy drops + * Fix for Spectacle Rock Cave enemy drops (the mini-moldorms) + * Fix for multiworld lamps with incorrect graphics + * No longer shuffles fairy bonks (from trees) as part of Enemizer +* 1.3.0.6v + * Flute can't be activated in rain state (except glitched modes) (Thanks codemann!) + * Enemizer + * Arrghus at Lanmo 2 no longer prevents pot pickups + * Trinexx at Lanmo 2 requires the Cape go backwards to face him + * Lift-able Blocks require a sprite slot (should help reduce problems) + * Fixed logic issues: + * Self-locking key not allowed in Sanctuary in standard (typo fixed) + * More advanced bunny-walking logic in dungeons (multiple paths considered) + * ER: Minor fix for Link's House on DM in Insanity (escape cave should not be re-used) + * MSU: GTBK song fix for DR (Thanks codemann!) + * District Algorithm: Fails if no available location outside chosen districts + * Various enemy bans + * More Gibos near kiki and Old Man + * Bumper/AntiFairy obstacles + * Damaging roller + * Statue + Pots don't mix + * Statues on Skull Big Key Chest tile + * Toppo in challenge rooms + * Misc others +* 1.3.0.5v + * Hud/Map Counter: Collecting a keys for this dungeon of a bonk torch no longer increments the counter twice and immediately updates the hud. + * Enemizer: Hera basement item counting twice fixed by banning wallmasters on the tile. + * Enemizer: Statues banned offscreen for pull switches + * Enemizer: Several sprite producing enemies have been limited on crowded tiles. Offenders: Hinox, Sluggula, Bomb Guard, Beamos, Gibo, Wall Cannons, Probe using Guards. Others do not spam as many projectiles. + * Enemizer: More enemy bans (mostly Wizzrobes near walls where they won't spawn, couple missed firebar spots) +* 1.3.0.4v + * Enemizer: The bunny beam near Lanmo 2 and the 4 fairies near Ice Armos are not shuffled anymore. This is due to how bosses shuffle works and since it cannot be guaranteed to work within the current system, they are vanilla. (Vitreous still overwrites the fairies and Arrghus only lets two spawn, etc.) + * Dropshuffle: Pokey 1 has been fixed to drop his item + * Mystery/Customizer: true/false and on/off in yaml files should behave the same. + * More enemy bans as have been reported +* 1.3.0.3v + * Faeries now part of the enemy shuffle pool. Take note, this will increase enemy drop locations to include fairy pools both in dungeons and in caves. + * Enemy drop indicator (blue square) now works in caves based on entrance used + * Fixes: + * Collection rate counter is properly hidden in mystery seeds + * Sprite limit lowered where possible to allow for lifting of pots + * Hovers in Swamp Waterway properly do not drop items anymore + * Lots more bans (thanks to jsd in particular but also thanks to all the reports) + * Minor issue with customizer/mystery files not allowing "true" for booleans +* 1.3.0.2v + * Fix for multiworld received keys not counting correctly + * Fix for multiworld lamps incorrect graphics + * Fix for collection rate decreasing on item "pickup" + * Fix for pendants as prizes counting as items + * Fix for castle barrier gfx in rain state + * Enemizer fixes and bans: + * Fixed a generation issue where ChainChomp placement would cause a failure. (Invincible enemies banned in Sprial Cave for early game traversal for now) + * Skull Pot Prison should not be blocked by "impassable" enemies + * Bumpers banned in Ice Hookshot room + * Fixed issue in GT Spike Crystal room + * Fixed blockage issues in TT Ambush and Compass rooms + * Forbid Bumper in Fairy Ascension cave; needed to clip into wall weirdly to pass. + * Enemy Drop bans + * Forbid Stals in many places where they cannot be woken up. Behind rails and on top of blocks, for example. + * A couple minor wizzrobes bans because of despawns. + * Enemies over pits and on conveyors near pits have been issued standard bans for falling enemies. Mimics join the ranks here as they don't work well on pits or on conveyors. + * Mimics banned where conveyors touch walls and could clip out unintentionally +* 1.3.0.1v + * Fixed bugs with item duping and disappearing drops + * Fixed multiworld crash + * Fixed assured sword missing when using start inventory (via GUI/CLI) + * Forbid extra statues in Swamp Push Statue room + * Forbid bumpers on OW water + * Forbid Stal on pits + * Text fix on sprite author (thanks Synack) * 1.2.0.23u * Generation: fix for bunny walk logic taking up too much memory * Key Logic: Partial is now the new default diff --git a/Regions.py b/Regions.py index 7546e012..6cb44847 100644 --- a/Regions.py +++ b/Regions.py @@ -1,116 +1,244 @@ import collections from Items import ItemFactory -from BaseClasses import Region, Location, Entrance, RegionType, Shop, ShopType, LocationType, PotItem, PotFlags +from BaseClasses import Region, Location, Entrance, RegionType, Terrain, Shop, ShopType, LocationType, PotItem, PotFlags from PotShuffle import key_drop_data, vanilla_pots, choose_pots, PotSecretTable +from source.dungeon.EnemyList import setup_enemy_locations, enemy_names + def create_regions(world, player): world.regions += [ create_menu_region(player, 'Menu', None, ['Links House S&Q', 'Sanctuary S&Q', 'Old Man S&Q', 'Other World S&Q']), create_menu_region(player, 'Flute Sky', None, ['Flute Spot 1', 'Flute Spot 2', 'Flute Spot 3', 'Flute Spot 4', - 'Flute Spot 5', 'Flute Spot 6', 'Flute Spot 7', 'Flute Spot 8']), - create_lw_region(player, 'Light World', ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest'], - ['Lost Woods Gamble', 'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump', 'Lumberjack Tree Tree', 'Lumberjack Tree Cave', 'Lumberjack House', - 'Fortune Teller (Light)', 'Bonk Rock Cave', 'Sanctuary', 'Sanctuary Grave', 'North Fairy Cave Drop', 'North Fairy Cave', - 'Kakariko Well Drop', 'Kakariko Well Cave', 'Blinds Hideout', 'Elder House (West)', 'Elder House (East)', - 'Snitch Lady (West)', 'Snitch Lady (East)', 'Chicken House', 'Sick Kids House', 'Kakariko Shop', 'Tavern North', 'Tavern (Front)', - 'Hyrule Castle Secret Entrance Drop', 'Sahasrahlas Hut', 'Eastern Palace', 'Blacksmiths Hut', 'Bat Cave Cave', - 'Library', 'Two Brothers House (East)', 'Kakariko Gamble Game', 'Bonk Fairy (Light)', 'Links House', 'Lake Hylia Fairy', 'Long Fairy Cave', - 'Aginahs Cave', 'Light Hype Fairy', 'Lake Hylia Fortune Teller', 'Lake Hylia Shop', 'Mini Moldorm Cave', - 'Ice Rod Cave', 'Good Bee Cave', '20 Rupee Cave', 'Desert Fairy', '50 Rupee Cave', 'Dam', - - 'Master Sword Meadow', 'Death Mountain Entrance Rock', 'Graveyard Ladder (Bottom)', 'Kings Grave Rocks (Outer)', - 'Wooden Bridge Bush (South)', 'Kakariko Southwest Bush (North)', 'Kakariko Yard Bush (South)', 'Hyrule Castle Main Gate', 'Bat Cave Ledge Peg', - 'Light World Water Drop', 'Desert Statue Move', 'Checkerboard Ledge Approach', 'Cave 45 Approach', 'Bombos Tablet Ladder (Bottom)', - - 'Kakariko Teleporter', 'Castle Gate Teleporter', 'East Hyrule Teleporter', 'South Hyrule Teleporter', 'LW Flute']), - create_lw_region(player, 'West Death Mountain (Top)', ['Ether Tablet'], ['Tower of Hera', 'Death Mountain Drop', 'Spectacle Rock Approach', - 'DM Hammer Bridge (West)']), + 'Flute Spot 5', 'Flute Spot 6', 'Flute Spot 7', 'Flute Spot 8']), + create_lw_region(player, 'Master Sword Meadow', ['Master Sword Pedestal'], ['Master Sword Meadow SC']), + create_lw_region(player, 'Lost Woods West Area', None, ['Lost Woods Bush (West)', 'Lost Woods NW', 'Lost Woods SW', 'Lost Woods SC']), + create_lw_region(player, 'Lost Woods East Area', ['Mushroom'], ['Lost Woods Gamble', 'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump', 'Lost Woods Bush (East)', 'Lost Woods SE', 'Lost Woods EN']), + create_lw_region(player, 'Lumberjack Area', None, ['Lumberjack Tree Tree', 'Lumberjack Tree Cave', 'Lumberjack House', 'Lumberjack WN', 'Lumberjack SW']), + create_lw_region(player, 'West Death Mountain (Top)', ['Ether Tablet'], ['Tower of Hera', 'Spectacle Rock Approach', 'West Death Mountain Drop', 'West Death Mountain EN']), + create_lw_region(player, 'Spectacle Rock Ledge', ['Spectacle Rock'], ['Spectacle Rock Leave', 'Spectacle Rock Ledge Drop']), 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)', - 'DM Broken Bridge (West)', 'Death Mountain Teleporter', 'DM Flute']), - create_lw_region(player, 'Spectacle Rock', ['Spectacle Rock'], ['Spectacle Rock Drop', 'Spectacle Rock Leave']), - create_lw_region(player, 'East Death Mountain (Top)', None, ['Paradox Cave (Top)', 'DM Hammer Bridge (East)', 'Floating Island Bridge (East)', - 'Spiral Cave Ledge Access', 'Fairy Ascension Ledge Access', 'Mimic Cave Ledge Access', 'East Death Mountain Drop', - 'Turtle Rock Teleporter']), + 'Death Mountain Return Cave (East)', 'Spectacle Rock Cave', 'Spectacle Rock Cave Peak', 'Spectacle Rock Cave (Bottom)', + 'West Death Mountain Teleporter', 'West Death Mountain ES']), + create_lw_region(player, 'East Death Mountain (Top West)', None, ['DM Hammer Bridge (West)', 'East Death Mountain WN']), + create_lw_region(player, 'East Death Mountain (Top East)', None, ['Paradox Cave (Top)', 'DM Hammer Bridge (East)', 'Floating Island Bridge (East)', + 'EDM To Spiral Ledge Drop', 'EDM To Fairy Ledge Drop', 'EDM To Mimic Ledge Drop', 'EDM Ledge Drop', 'East Death Mountain EN']), create_lw_region(player, 'Death Mountain Floating Island', ['Floating Island'], ['Floating Island Bridge (West)']), - create_lw_region(player, 'Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Cave Ledge Drop']), - create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Cave', 'Mimic Cave Ledge Drop']), + create_lw_region(player, 'Spiral Cave Ledge', None, ['Spiral Cave', 'Spiral Ledge Drop']), + create_lw_region(player, 'Mimic Cave Ledge', None, ['Mimic Cave', 'Mimic Ledge Drop']), create_lw_region(player, 'Fairy Ascension Ledge', None, ['Fairy Ascension Cave (Top)', 'Fairy Ascension Ledge Drop']), + create_lw_region(player, 'Fairy Ascension Plateau', None, ['Fairy Ascension Cave (Bottom)', 'Fairy Ascension Rocks (Inner)', 'Fairy Ascension Plateau Ledge Drop']), + create_lw_region(player, 'East Death Mountain (Bottom Left)', None, ['DM Broken Bridge (West)', 'East Death Mountain WS']), create_lw_region(player, 'East Death Mountain (Bottom)', None, ['Spiral Cave (Bottom)', 'Hookshot Fairy', 'Paradox Cave (Bottom)', 'Paradox Cave (Middle)', - 'DM Broken Bridge (East)', 'Fairy Ascension Rocks', 'East Death Mountain Teleporter', 'EDM Flute']), - create_lw_region(player, 'Fairy Ascension Plateau', None, ['Fairy Ascension Cave (Bottom)', 'Fairy Ascension Drop']), - create_lw_region(player, 'Death Mountain Return Ledge', None, ['Death Mountain Return Cave (West)', 'Death Mountain Return Ledge Drop'], 'a ledge in the foothills'), - create_lw_region(player, 'Death Mountain Entrance', None, ['Old Man Cave (West)', 'Death Mountain Entrance Drop']), - create_lw_region(player, 'Northeast Light World', None, ['Zoras Domain', 'Potion Shop Rock (North)', 'Northeast Light World Water Drop']), - create_lw_region(player, 'Zora Waterfall Entryway', None, ['Waterfall of Wishing', 'Zora Waterfall Water Drop', 'ZLW Flute']), + 'DM Broken Bridge (East)', 'Fairy Ascension Rocks (Outer)', 'East Death Mountain Teleporter']), + create_lw_region(player, 'Death Mountain TR Pegs Area', None, ['TR Pegs Ledge Entry', 'Death Mountain TR Pegs WN']), + create_lw_region(player, 'Death Mountain TR Pegs Ledge', None, ['TR Pegs Ledge Leave', 'TR Pegs Ledge Drop', 'TR Pegs Teleporter']), + create_lw_region(player, 'Mountain Pass Area', None, ['Mountain Pass Rock (Outer)', 'Mountain Pass NW', 'Mountain Pass SE']), + create_lw_region(player, 'Mountain Pass Ledge', None, ['Death Mountain Return Cave (West)', 'Mountain Pass Ledge Drop'], 'a ledge in the foothills'), + create_lw_region(player, 'Mountain Pass Entry', None, ['Old Man Cave (West)', 'Mountain Pass Rock (Inner)', 'Mountain Pass Entry Ledge Drop']), + create_lw_region(player, 'Zora Waterfall Area', None, ['Zora Waterfall Water Entry', 'Zora Waterfall SE', 'Zora Waterfall NE']), + create_lw_region(player, 'Zora Waterfall Water', None, ['Zora Waterfall Approach', 'Zora Waterfall Landing', 'Zora Whirlpool'], 'Light World', Terrain.Water), + create_lw_region(player, 'Zora Waterfall Entryway', None, ['Waterfall of Wishing', 'Zora Waterfall Water Drop']), + create_lw_region(player, 'Zoras Domain', ['King Zora', 'Zora\'s Ledge'], ['Zoras Domain SW']), + create_lw_region(player, 'Lost Woods Pass West Area', None, ['Lost Woods Pass NW', 'Lost Woods Pass SW']), + create_lw_region(player, 'Lost Woods Pass East Top Area', None, ['Lost Woods Pass Hammer (North)', 'Lost Woods Pass NE']), + create_lw_region(player, 'Lost Woods Pass Portal Area', None, ['Lost Woods Pass Hammer (South)', 'Lost Woods Pass Rock (North)', 'Kakariko Teleporter']), + create_lw_region(player, 'Lost Woods Pass East Bottom Area', None, ['Lost Woods Pass Rock (South)', 'Lost Woods Pass SE']), + create_lw_region(player, 'Kakariko Fortune Area', None, ['Fortune Teller (Light)', 'Kakariko Fortune NE', 'Kakariko Fortune EN', 'Kakariko Fortune ES', 'Kakariko Fortune SC']), + create_lw_region(player, 'Kakariko Pond Area', None, ['Kakariko Pond Whirlpool', 'Kakariko Pond NE', 'Kakariko Pond WN', 'Kakariko Pond WS', + 'Kakariko Pond SW', 'Kakariko Pond SE', 'Kakariko Pond EN', 'Kakariko Pond ES']), + create_lw_region(player, 'Sanctuary Area', None, ['Sanctuary', 'Sanctuary WS', 'Sanctuary EC']), + create_lw_region(player, 'Bonk Rock Ledge', None, ['Bonk Rock Cave', 'Bonk Rock Ledge Drop', 'Sanctuary WN']), + create_lw_region(player, 'Graveyard Area', None, ['Sanctuary Grave', 'Kings Grave Rocks (Outer)', 'Graveyard Ladder (Bottom)', 'Graveyard WC', 'Graveyard EC']), create_lw_region(player, 'Graveyard Ledge', None, ['Graveyard Cave', 'Graveyard Ledge Drop', 'Graveyard Ladder (Top)']), create_lw_region(player, 'Kings Grave Area', None, ['Kings Grave', 'Kings Grave Rocks (Inner)']), - create_lw_region(player, 'Potion Shop Area', None, ['Potion Shop', 'Wooden Bridge Bush (North)', 'Potion Shop Rock (South)', 'Potion Shop Water Drop', 'NWLW Flute']), - create_lw_region(player, 'Bush Covered Lawn', None, ['Bush Covered House', 'Kakariko Yard Bush (North)']), - create_lw_region(player, 'Bomb Hut Area', None, ['Light World Bomb Hut', 'Kakariko Southwest Bush (South)']), - create_lw_region(player, 'Hyrule Castle Courtyard', None, ['Hyrule Castle Entrance (South)', 'Inverted Pyramid Entrance', 'Hyrule Castle Courtyard Bush (South)', 'Hyrule Castle Main Gate (North)']), - create_lw_region(player, 'Hyrule Castle Secret Entrance Area', None, ['Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Courtyard Bush (North)']), + create_lw_region(player, 'River Bend Area', None, ['North Fairy Cave Drop', 'North Fairy Cave', 'River Bend Water Drop', 'River Bend WC', 'River Bend SW']), + create_lw_region(player, 'River Bend East Bank', None, ['River Bend East Water Drop', 'River Bend SE', 'River Bend EC', 'River Bend ES']), + create_lw_region(player, 'River Bend Water', None, ['River Bend West Pier', 'River Bend East Pier', 'River Bend Whirlpool', 'River Bend EN', 'River Bend SC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Potion Shop Area', None, ['Potion Shop', 'Potion Shop Water Drop', 'Potion Shop Rock (South)', 'Potion Shop WC', 'Potion Shop WS']), + create_lw_region(player, 'Potion Shop Northeast', None, ['Potion Shop Northeast Water Drop', 'Potion Shop Rock (North)', 'Potion Shop EC']), + create_lw_region(player, 'Potion Shop Water', None, ['Potion Shop WN', 'Potion Shop EN'], 'Light World', Terrain.Water), + create_lw_region(player, 'Zora Approach Area', None, ['Zora Approach Rocks (West)', 'Zora Approach Bottom Ledge Drop', 'Zora Approach Water Drop', 'Zora Approach WC']), + create_lw_region(player, 'Zora Approach Ledge', None, ['Zora Approach Rocks (East)', 'Zora Approach Ledge Drop', 'Zora Approach NE']), + create_lw_region(player, 'Zora Approach Water', None, ['Zora Approach WN'], 'Light World', Terrain.Water), + create_lw_region(player, 'Kakariko Village', ['Bottle Merchant'], ['Kakariko Well Drop', 'Kakariko Well Cave', 'Blinds Hideout', 'Elder House (West)', 'Elder House (East)', + 'Snitch Lady (West)', 'Snitch Lady (East)', 'Chicken House', 'Sick Kids House', 'Kakariko Shop', 'Tavern (Front)', 'Tavern North', + 'Kakariko Southwest Bush (North)', 'Kakariko Yard Bush (South)', 'Kakariko NW', 'Kakariko NC', 'Kakariko NE', 'Kakariko ES', 'Kakariko SE']), + create_lw_region(player, 'Kakariko Southwest', None, ['Light World Bomb Hut', 'Kakariko Southwest Bush (South)']), + create_lw_region(player, 'Kakariko Bush Yard', None, ['Bush Covered House', 'Kakariko Yard Bush (North)']), + create_lw_region(player, 'Forgotten Forest Area', None, ['Forgotten Forest NW', 'Forgotten Forest NE', 'Forgotten Forest ES']), + create_lw_region(player, 'Hyrule Castle Area', None, ['Hyrule Castle Secret Entrance Drop', 'Hyrule Castle East Rock (Inner)', 'Hyrule Castle Southwest Bush (North)', + 'Hyrule Castle Main Gate (South)', 'Castle Gate Teleporter', 'Hyrule Castle WN', 'Hyrule Castle SE']), + create_lw_region(player, 'Hyrule Castle Southwest', None, ['Hyrule Castle Southwest Bush (South)', 'Hyrule Castle SW']), + create_lw_region(player, 'Hyrule Castle Courtyard', None, ['Hyrule Castle Entrance (South)', 'Inverted Pyramid Entrance', 'Hyrule Castle Courtyard Bush (South)', 'Hyrule Castle Main Gate (North)', 'Castle Gate Teleporter (Inner)']), + create_lw_region(player, 'Hyrule Castle Courtyard Northeast', None, ['Hyrule Castle Secret Entrance Stairs', 'Hyrule Castle Courtyard Bush (North)']), create_lw_region(player, 'Hyrule Castle Ledge', None, ['Hyrule Castle Entrance (West)', 'Agahnims Tower', 'Hyrule Castle Entrance (East)', 'Inverted Pyramid Hole', - 'Hyrule Castle Ledge Courtyard Drop', 'Hyrule Castle Ledge Drop'], 'the castle rampart'), - create_lw_region(player, 'Bat Cave Ledge', None, ['Bat Cave Drop', 'Bat Cave Ledge Peg (East)']), - create_lw_region(player, 'Maze Race Ledge', ['Maze Race'], ['Two Brothers House (West)', 'Maze Race Ledge Drop'], 'a race against time'), - create_lw_region(player, 'Desert Palace Stairs', None, ['Desert Palace Entrance (South)']), - create_lw_region(player, 'Desert Palace Mouth', None, ['Desert Palace Entrance (East)', 'Desert Palace Mouth Drop'], 'a sandy vista'), + 'Hyrule Castle Ledge Courtyard Drop', 'Hyrule Castle Ledge Drop'], 'the castle rampart'), + create_lw_region(player, 'Hyrule Castle East Entry', None, ['Hyrule Castle East Rock (Outer)', 'Hyrule Castle ES']), + create_lw_region(player, 'Hyrule Castle Water', None, [], 'Light World', Terrain.Water), + create_lw_region(player, 'Wooden Bridge Area', None, ['Wooden Bridge Bush (South)', 'Wooden Bridge Water Drop', 'Wooden Bridge NW', 'Wooden Bridge SW']), + create_lw_region(player, 'Wooden Bridge Northeast', None, ['Wooden Bridge Bush (North)', 'Wooden Bridge Northeast Water Drop', 'Wooden Bridge NE']), + create_lw_region(player, 'Wooden Bridge Water', None, ['Wooden Bridge NC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Eastern Palace Area', None, ['Sahasrahlas Hut', 'Eastern Palace', 'Eastern Palace SW', 'Eastern Palace SE']), + create_lw_region(player, 'Blacksmith Area', None, ['Blacksmiths Hut', 'Bat Cave Cave', 'Blacksmith Ledge Peg (West)', 'Blacksmith WS']), + create_lw_region(player, 'Blacksmith Ledge', None, ['Bat Cave Drop', 'Blacksmith Ledge Peg (East)']), + create_lw_region(player, 'Sand Dunes Area', None, ['Sand Dunes NW', 'Sand Dunes WN', 'Sand Dunes SC']), + create_lw_region(player, 'Maze Race Area', None, ['Maze Race ES']), + create_lw_region(player, 'Maze Race Ledge', None, ['Two Brothers House (West)', 'Maze Race Game'], 'a race against time'), + create_lw_region(player, 'Maze Race Prize', ['Maze Race'], ['Maze Race Ledge Drop']), # this is a separate region to make OWG item get possible without allowing the Entrance access + create_lw_region(player, 'Kakariko Suburb Area', None, ['Library', 'Two Brothers House (East)', 'Kakariko Gamble Game', 'Kakariko Suburb NE', 'Kakariko Suburb WS', 'Kakariko Suburb ES']), + create_lw_region(player, 'Flute Boy Area', ['Flute Spot'], ['Flute Boy SC']), + create_lw_region(player, 'Flute Boy Pass', None, ['Flute Boy WS', 'Flute Boy SW']), + create_lw_region(player, 'Central Bonk Rocks Area', None, ['Bonk Fairy (Light)', 'Central Bonk Rocks NW', 'Central Bonk Rocks SW', + 'Central Bonk Rocks EN', 'Central Bonk Rocks EC', 'Central Bonk Rocks ES']), + create_lw_region(player, 'Links House Area', None, ['Links House', 'Links House NE', 'Links House WN', 'Links House WC', 'Links House WS', 'Links House SC', 'Links House ES']), + create_lw_region(player, 'Stone Bridge North Area', None, ['Stone Bridge (Southbound)', 'Stone Bridge NC', 'Stone Bridge EN']), + create_lw_region(player, 'Stone Bridge South Area', None, ['Stone Bridge (Northbound)', 'Stone Bridge WS', 'Stone Bridge SC']), + create_lw_region(player, 'Stone Bridge Water', None, ['Stone Bridge WC', 'Stone Bridge EC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Hobo Bridge', ['Hobo'], ['Hobo EC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Central Cliffs', None, ['Lake Hylia Island FAWT Ledge Drop', 'Stone Bridge EC Cliff Water Drop', 'C Whirlpool Portal Cliff Ledge Drop']), + create_lw_region(player, 'Tree Line Area', None, ['Lake Hylia Fairy', 'Tree Line WN', 'Tree Line NW', 'Tree Line SE']), + create_lw_region(player, 'Tree Line Water', None, ['Tree Line WC', 'Tree Line SC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Eastern Nook Area', None, ['Long Fairy Cave', 'East Hyrule Teleporter', 'Eastern Nook NE']), + create_lw_region(player, 'Desert Area', None, ['Aginahs Cave', 'Desert Statue Move', 'Checkerboard Ledge Approach', 'Desert ES']), create_lw_region(player, 'Desert Ledge', ['Desert Ledge'], ['Desert Palace Entrance (West)', 'Desert Ledge Rocks (Outer)', 'Desert Ledge Drop'], 'the desert ledge'), - create_lw_region(player, 'Desert Palace Entrance (North) Spot', None, ['Desert Palace Entrance (North)', 'Desert Ledge Rocks (Inner)'], 'the desert ledge'), + create_lw_region(player, 'Desert Ledge Keep', None, ['Desert Palace Entrance (North)', 'Desert Ledge Rocks (Inner)'], 'the desert ledge'), create_lw_region(player, 'Desert Checkerboard Ledge', None, ['Checkerboard Cave', 'Checkerboard Ledge Drop', 'Checkerboard Ledge Leave']), + create_lw_region(player, 'Desert Stairs', None, ['Desert Palace Entrance (South)']), + create_lw_region(player, 'Desert Mouth', None, ['Desert Palace Entrance (East)', 'Desert Mouth Drop'], 'a sandy vista'), create_lw_region(player, 'Desert Teleporter Ledge', None, ['Desert Teleporter Drop', 'Desert Teleporter']), - create_lw_region(player, 'Bombos Tablet Ledge', ['Bombos Tablet'], ['Bombos Tablet Ladder (Top)']), - create_lw_region(player, 'Desert Northern Cliffs'), + create_lw_region(player, 'Desert Northern Cliffs', None, ['Checkerboard Cliff Ledge Drop', 'Cave 45 Cliff Ledge Drop']), + create_lw_region(player, 'Bombos Tablet Ledge', ['Bombos Tablet'], ['Bombos Tablet Drop', 'Desert EC']), + create_lw_region(player, 'Flute Boy Approach Area', None, ['Flute Boy Bush (South)', 'Cave 45 Approach', 'Flute Boy Pass NW', 'Flute Boy Pass EC']), + create_lw_region(player, 'Flute Boy Bush Entry', None, ['Flute Boy Bush (North)', 'Flute Boy Pass NC']), create_lw_region(player, 'Cave 45 Ledge', None, ['Cave 45', 'Cave 45 Ledge Drop', 'Cave 45 Leave']), - create_lw_region(player, 'Lake Hylia Water', None, ['Light World Pier', 'Potion Shop Pier', 'Hobo Pier', 'Lake Hylia Island Pier', 'Lake Hylia Central Island Pier', 'Lake Hylia Whirlpool', 'Waterfall Fairy Access']), - create_lw_region(player, 'Lake Hylia Island', ['Lake Hylia Island']), - create_lw_region(player, 'Lake Hylia Central Island', None, ['Capacity Upgrade', 'Lake Hylia Central Island Water Drop', 'Lake Hylia Teleporter']), - create_lw_region(player, 'Master Sword Meadow', ['Master Sword Pedestal']), - create_lw_region(player, 'Hobo Bridge', ['Hobo']), - create_lw_region(player, 'Zoras Domain', ['King Zora', 'Zora\'s Ledge']), + create_lw_region(player, 'C Whirlpool Area', None, ['C Whirlpool Rock (Bottom)', 'C Whirlpool Pegs (Outer)', 'C Whirlpool Water Entry', 'C Whirlpool EN', 'C Whirlpool ES', 'C Whirlpool SC']), + create_lw_region(player, 'C Whirlpool Portal Area', None, ['C Whirlpool Pegs (Inner)', 'South Hyrule Teleporter']), + create_lw_region(player, 'C Whirlpool Water', None, ['C Whirlpool Landing', 'C Whirlpool', 'C Whirlpool EC'], 'Light World', Terrain.Water), + create_lw_region(player, 'C Whirlpool Outer Area', None, ['C Whirlpool Rock (Top)', 'C Whirlpool WC', 'C Whirlpool NW']), + create_lw_region(player, 'Statues Area', None, ['Light Hype Fairy', 'Statues Water Entry', 'Statues NC', 'Statues WN', 'Statues WS', 'Statues SC']), + create_lw_region(player, 'Statues Water', None, ['Statues Landing', 'Statues WC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Lake Hylia Northwest Bank', None, ['Lake Hylia Fortune Teller', 'Lake Hylia Shop', 'Lake Hylia Water Drop', 'Lake Hylia NW']), + create_lw_region(player, 'Lake Hylia Northeast Bank', None, ['Lake Hylia Northeast Water Drop', 'Lake Hylia NE']), + create_lw_region(player, 'Lake Hylia South Shore', None, ['Mini Moldorm Cave', 'Lake Hylia South Water Drop', 'Lake Hylia WS', 'Lake Hylia ES']), + create_lw_region(player, 'Lake Hylia Central Island', None, ['Capacity Upgrade', 'Lake Hylia Central Water Drop', '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 Water D Approach', 'Lake Hylia Whirlpool', 'Lake Hylia NC', 'Lake Hylia EC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Lake Hylia Water D', None, ['Lake Hylia Water D Leave']), + create_lw_region(player, 'Ice Cave Area', None, ['Ice Rod Cave', 'Good Bee Cave', '20 Rupee Cave', 'Ice Cave Water Drop', 'Ice Cave SE']), + create_lw_region(player, 'Ice Cave Water', None, ['Ice Cave Pier', 'Ice Cave SW'], 'Light World', Terrain.Water), + create_lw_region(player, 'Desert Pass Area', ['Purple Chest'], ['Desert Fairy', '50 Rupee Cave', 'Desert Pass Ladder (South)', 'Desert Pass Rocks (North)', 'Desert Pass WS', 'Desert Pass EC']), + create_lw_region(player, 'Desert Pass Southeast', None, ['Desert Pass Rocks (South)', 'Desert Pass ES']), + create_lw_region(player, 'Desert Pass Ledge', None, ['Desert Pass Ladder (North)', 'Desert Pass Ledge Drop', 'Desert Pass WC']), + create_lw_region(player, 'Dam Area', ['Sunken Treasure'], ['Dam', 'Dam WC', 'Dam WS', 'Dam NC', 'Dam EC']), + create_lw_region(player, 'South Pass Area', None, ['South Pass WC', 'South Pass NC', 'South Pass ES']), + create_lw_region(player, 'Octoballoon Area', None, ['Octoballoon Water Drop', 'Octoballoon WS', 'Octoballoon NE']), + create_lw_region(player, 'Octoballoon Water', None, ['Octoballoon Pier', 'Octoballoon Whirlpool', 'Octoballoon WC'], 'Light World', Terrain.Water), + create_lw_region(player, 'Octoballoon Water Ledge', None, ['Octoballoon Waterfall Water Drop', 'Octoballoon NW'], 'Light World', Terrain.Water), + create_dw_region(player, 'Skull Woods Forest', None, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', + 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Rock (East)', 'Skull Woods SE']), + create_dw_region(player, 'Skull Woods Portal Entry', None, ['Skull Woods Rock (West)', 'Skull Woods SC']), + create_dw_region(player, 'Skull Woods Forest (West)', None, ['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section'], 'a deep, dark forest'), + create_dw_region(player, 'Skull Woods Forgotten Path (Southwest)', None, ['Skull Woods Forgotten Bush (West)', 'Skull Woods SW']), + create_dw_region(player, 'Skull Woods Forgotten Path (Northeast)', None, ['Skull Woods Forgotten Bush (East)', 'Skull Woods EN']), + create_dw_region(player, 'Dark Lumberjack Area', None, ['Dark Lumberjack Shop', 'Dark Lumberjack WN', 'Dark Lumberjack SW']), + create_dw_region(player, 'West Dark Death Mountain (Top)', None, ['GT Approach', 'Dark Death Mountain Ladder (Top)', 'West Dark Death Mountain Drop', 'West Dark Death Mountain EN']), + create_dw_region(player, 'GT Stairs', None, ['Ganons Tower', 'GT Leave']), create_dw_region(player, 'West Dark Death Mountain (Bottom)', None, ['Spike Cave', 'Dark Death Mountain Fairy', 'Dark Death Mountain Ladder (Bottom)', - 'Dark Death Mountain Teleporter (West)', 'DDM Flute']), - create_dw_region(player, 'Dark Death Mountain (Top)', None, ['Ganons Tower', 'Hookshot Cave', 'Superbunny Cave (Top)', 'Turtle Rock', 'Dark Death Mountain Drop (West)', - 'Dark Death Mountain Drop (East)', 'Dark Death Mountain Ladder (Top)', 'Turtle Rock Tail Drop']), - create_dw_region(player, 'Dark Death Mountain Floating Island', None, ['Hookshot Cave Back Entrance', 'Floating Island Drop'], 'a dark island'), + 'Dark Death Mountain Teleporter (West)', 'West Dark Death Mountain ES']), + create_dw_region(player, 'East Dark Death Mountain (Top)', None, ['Superbunny Cave (Top)', 'Hookshot Cave', 'East Dark Death Mountain Drop', 'East Dark Death Mountain WN', 'East Dark Death Mountain EN']), + create_dw_region(player, 'Dark Death Mountain Floating Island', None, ['Hookshot Cave Back Entrance', 'Floating Island Drop'], 'a dark floating island'), create_dw_region(player, 'Dark Death Mountain Ledge', None, ['Dark Death Mountain Ledge (West)', 'Dark Death Mountain Ledge (East)'], 'a dark ledge'), create_dw_region(player, 'Dark Death Mountain Isolated Ledge', None, ['Turtle Rock Isolated Ledge Entrance'], 'a dark vista'), - create_dw_region(player, 'East Dark Death Mountain (Bottom)', None, ['East Dark Death Mountain Bushes', 'Superbunny Cave (Bottom)', 'Dark Death Mountain Shop', - 'East Dark Death Mountain Teleporter (Bottom)', 'EDDM Flute']), + create_dw_region(player, 'East Dark Death Mountain (Bottom)', None, ['Superbunny Cave (Bottom)', 'Dark Death Mountain Shop', 'East Dark Death Mountain Bushes', 'East Dark Death Mountain Teleporter']), create_dw_region(player, 'East Dark Death Mountain (Bushes)', None, []), - create_dw_region(player, 'Turtle Rock (Top)', None, ['Turtle Rock Drop', 'East Dark Death Mountain Teleporter (Top)']), - create_dw_region(player, 'West Dark World', ['Frog'], ['Dark Lumberjack Shop', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Chest Game', 'Thieves Town', - 'C-Shaped House', 'Brewery', 'Red Shield Shop', 'Skull Woods Forest', 'Bumper Cave Entrance Rock', - 'West Dark World Water Drop', 'Grassy Lawn Pegs (Bottom)', 'Peg Area Rocks (Left)', 'Village of Outcasts Drop', - 'West Dark World Teleporter', 'WDW Flute', 'Dark Graveyard Bush (South)']), - create_dw_region(player, 'Dark Graveyard North', None, ['Dark Graveyard Bush (North)']), - create_dw_region(player, 'Skull Woods Forest', None, ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (North)', - 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)']), - create_dw_region(player, 'Skull Woods Forest (West)', None, ['Skull Woods Second Section Hole', 'Skull Woods Second Section Door (West)', 'Skull Woods Final Section'], 'a deep, dark forest'), + create_dw_region(player, 'East Dark Death Mountain (Bottom Left)', None, ['East Dark Death Mountain WS']), + create_dw_region(player, 'Turtle Rock Area', None, ['Turtle Rock', 'Turtle Rock Tail Ledge Drop', 'Turtle Rock WN']), + create_dw_region(player, 'Turtle Rock Ledge', None, ['Turtle Rock Ledge Drop', 'Turtle Rock Teleporter']), + create_dw_region(player, 'Bumper Cave Area', None, ['Bumper Cave Rock (Outer)', 'Bumper Cave NW', 'Bumper Cave SE']), create_dw_region(player, 'Bumper Cave Ledge', ['Bumper Cave Ledge'], ['Bumper Cave (Top)', 'Bumper Cave Ledge Drop'], 'a ledge with an item'), - create_dw_region(player, 'Bumper Cave Entrance', None, ['Bumper Cave (Bottom)', 'Bumper Cave Entrance Drop']), - create_dw_region(player, 'Dark Grassy Lawn', None, ['Dark World Shop', 'Grassy Lawn Pegs (Top)', 'Dark Grassy Lawn Flute']), - create_dw_region(player, 'Hammer Peg Area', ['Dark Blacksmith Ruins'], ['Hammer Peg Cave', 'Peg Area Rocks (Right)', 'Hammer Peg Area Flute']), - create_dw_region(player, 'Northeast Dark World', None, ['Dark Potion Shop', 'Northeast Dark World Water Drop', 'Dark Witch Rock (South)', 'West Dark World Gap', - 'Broken Bridge Pass (Top)', 'NEDW Flute']), - create_dw_region(player, 'Catfish Area', ['Catfish'], ['Dark Witch Rock (North)', 'Catfish Water Drop']), - create_dw_region(player, 'East Dark World', ['Pyramid'], ['Pyramid Hole', 'Pyramid Fairy', 'Palace of Darkness Hint', 'Palace of Darkness', 'Dark Lake Hylia Fairy', - 'East Dark World Hint', 'Broken Bridge Pass (Bottom)', 'East Dark World Water Drop', 'Hammer Bridge Pegs (North)', - 'East Dark World Teleporter', 'EDW Flute']), - create_dw_region(player, 'Pyramid Exit Ledge', None, ['Pyramid Entrance', 'Pyramid Drop']), - create_dw_region(player, 'Dark Desert', None, ['Mire Shed', 'Misery Mire', 'Dark Desert Fairy', 'Dark Desert Hint', 'DD Flute']), - create_dw_region(player, 'Dark Desert Ledge', None, ['Dark Desert Drop', 'Dark Desert Teleporter']), - create_dw_region(player, 'South Dark World', ['Stumpy', 'Digging Game'], ['Archery Game', 'Bonk Fairy (Dark)', 'Big Bomb Shop', 'Hype Cave', 'Dark Lake Hylia Shop', 'Swamp Palace', - 'Village of Outcasts Heavy Rock', 'Hammer Bridge Pegs (South)', 'South Dark World Water Drop', 'Post Aga Teleporter', - 'South Dark World Teleporter', 'SDW Flute']), - create_dw_region(player, 'Dark Lake Hylia Water', None, ['Northeast Dark World Pier', 'East Dark World Pier', 'Southeast Dark World Pier', 'Ice Palace Approach']), - create_dw_region(player, 'Dark Lake Hylia Central Island', None, ['Ice Palace', 'Ice Palace Leave Water Drop', 'Ice Island To East Pier', - 'Dark Lake Hylia Teleporter']), - create_dw_region(player, 'Southeast Dark World', None, ['Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Spike Cave', - 'Southeast Dark World Water Drop', 'DLHL Flute']), + create_dw_region(player, 'Bumper Cave Entry', None, ['Bumper Cave (Bottom)', 'Bumper Cave Rock (Inner)', 'Bumper Cave Entry Drop']), + create_dw_region(player, 'Catfish Area', ['Catfish'], ['Catfish SE']), + create_dw_region(player, 'Skull Woods Pass West Area', None, ['Skull Woods Pass Bush Row (West)', 'Skull Woods Pass NW', 'Skull Woods Pass SW']), + create_dw_region(player, 'Skull Woods Pass East Top Area', None, ['Skull Woods Pass Bush Row (East)', 'Skull Woods Pass Bush (North)', 'Skull Woods Pass NE']), + create_dw_region(player, 'Skull Woods Pass Portal Area', None, ['Skull Woods Pass Bush (South)', 'Skull Woods Pass Rock (North)', 'West Dark World Teleporter']), + create_dw_region(player, 'Skull Woods Pass East Bottom Area', None, ['Skull Woods Pass Rock (South)', 'Skull Woods Pass SE']), + create_dw_region(player, 'Dark Fortune Area', None, ['Fortune Teller (Dark)', 'Dark Fortune NE', 'Dark Fortune EN', 'Dark Fortune ES', 'Dark Fortune SC']), + create_dw_region(player, 'Outcast Pond Area', None, ['Outcast Pond NE', 'Outcast Pond WN', 'Outcast Pond WS', 'Outcast Pond SW', 'Outcast Pond SE', 'Outcast Pond EN', 'Outcast Pond ES']), + create_dw_region(player, 'Dark Chapel Area', None, ['Dark Sanctuary Hint', 'Dark Chapel WN', 'Dark Chapel WS', 'Dark Chapel EC']), + create_dw_region(player, 'Dark Graveyard Area', None, ['Dark Graveyard Bush (South)', 'Dark Graveyard WC', 'Dark Graveyard EC']), + create_dw_region(player, 'Dark Graveyard North', None, ['Dark Graveyard Bush (North)']), + create_dw_region(player, 'Qirn Jump Area', None, ['Qirn Jump Water Drop', 'Qirn Jump WC', 'Qirn Jump SW']), + create_dw_region(player, 'Qirn Jump East Bank', None, ['Qirn Jump East Water Drop', 'Qirn Jump SE', 'Qirn Jump EC', 'Qirn Jump ES']), + create_dw_region(player, 'Qirn Jump Water', None, ['Qirn Jump Pier', 'Qirn Jump Whirlpool', 'Qirn Jump EN', 'Qirn Jump SC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Dark Witch Area', None, ['Dark Potion Shop', 'Dark Witch Water Drop', 'Dark Witch Rock (South)', 'Dark Witch WC', 'Dark Witch WS']), + create_dw_region(player, 'Dark Witch Northeast', None, ['Dark Witch Northeast Water Drop', 'Dark Witch Rock (North)', 'Dark Witch EC']), + create_dw_region(player, 'Dark Witch Water', None, ['Dark Witch WN', 'Dark Witch EN'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Catfish Approach Area', None, ['Catfish Approach Rocks (West)', 'Catfish Approach Bottom Ledge Drop', 'Catfish Approach Water Drop', 'Catfish Approach WC']), + create_dw_region(player, 'Catfish Approach Ledge', None, ['Catfish Approach Rocks (East)', 'Catfish Approach Ledge Drop', 'Catfish Approach NE']), + create_dw_region(player, 'Catfish Approach Water', None, ['Catfish Approach WN'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Village of Outcasts', None, ['Chest Game', 'Thieves Town', 'C-Shaped House', 'Brewery', 'Bush Yard Pegs (Outer)', + 'Village of Outcasts NW', 'Village of Outcasts NC', 'Village of Outcasts NE', 'Village of Outcasts ES', 'Village of Outcasts SE']), + create_dw_region(player, 'Village of Outcasts Bush Yard', None, ['Dark World Shop', 'Bush Yard Pegs (Inner)']), + create_dw_region(player, 'Shield Shop Area', None, ['Shield Shop Fence Drop (Outer)', 'Shield Shop NW', 'Shield Shop NE']), + create_dw_region(player, 'Shield Shop Fence', None, ['Red Shield Shop', 'Shield Shop Fence Drop (Inner)']), + create_dw_region(player, 'Pyramid Area', ['Pyramid'], ['Pyramid Hole', 'Pyramid Fairy', 'Pyramid ES']), + create_dw_region(player, 'Pyramid Exit Ledge', None, ['Pyramid Entrance', 'Pyramid Exit Ledge Drop']), + create_dw_region(player, 'Pyramid Pass', None, ['Post Aga Teleporter', 'Pyramid SW', 'Pyramid SE']), + create_dw_region(player, 'Pyramid Water', None, [], 'Dark World', Terrain.Water), + create_dw_region(player, 'Broken Bridge Area', None, ['Broken Bridge Hammer Rock (South)', 'Broken Bridge Water Drop', 'Broken Bridge SW']), + create_dw_region(player, 'Broken Bridge Northeast', None, ['Broken Bridge Hammer Rock (North)', 'Broken Bridge Hookshot Gap', 'Broken Bridge Northeast Water Drop', 'Broken Bridge NE']), + create_dw_region(player, 'Broken Bridge West', None, ['Broken Bridge West Water Drop', 'Broken Bridge NW']), + create_dw_region(player, 'Broken Bridge Water', None, ['Broken Bridge NC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Palace of Darkness Area', None, ['Palace of Darkness Hint', 'Palace of Darkness', 'Palace of Darkness SW', 'Palace of Darkness SE']), + create_dw_region(player, 'Hammer Pegs Area', ['Dark Blacksmith Ruins'], ['Hammer Peg Cave', 'Peg Area Rocks (East)']), + create_dw_region(player, 'Hammer Pegs Entry', None, ['Peg Area Rocks (West)', 'Hammer Pegs WS']), + create_dw_region(player, 'Dark Dunes Area', None, ['Dark Dunes NW', 'Dark Dunes WN', 'Dark Dunes SC']), + create_dw_region(player, 'Dig Game Area', ['Digging Game'], ['Dig Game To Ledge Drop', 'Dig Game ES']), + create_dw_region(player, 'Dig Game Ledge', None, ['Dig Game Ledge Drop', 'Dig Game EC']), + create_dw_region(player, 'Frog Area', None, ['Frog Ledge Drop', 'Frog Rock (Outer)', 'Archery Game Rock (North)', 'Frog NE']), + create_dw_region(player, 'Frog Prison', ['Frog'], ['Frog Rock (Inner)']), + create_dw_region(player, 'Archery Game Area', None, ['Archery Game', 'Archery Game Rock (South)', 'Frog WC', 'Frog WS', 'Frog ES']), + create_dw_region(player, 'Stumpy Area', ['Stumpy'], ['Stumpy SC']), + create_dw_region(player, 'Stumpy Pass', None, ['Stumpy WS', 'Stumpy SW']), + create_dw_region(player, 'Dark Bonk Rocks Area', None, ['Bonk Fairy (Dark)', 'Dark Bonk Rocks NW', 'Dark Bonk Rocks SW', 'Dark Bonk Rocks EN', 'Dark Bonk Rocks EC', 'Dark Bonk Rocks ES']), + create_dw_region(player, 'Big Bomb Shop Area', None, ['Big Bomb Shop', 'Big Bomb Shop NE', 'Big Bomb Shop WN', 'Big Bomb Shop WC', 'Big Bomb Shop WS', 'Big Bomb Shop SC', 'Big Bomb Shop ES']), + create_dw_region(player, 'Hammer Bridge North Area', None, ['Hammer Bridge Pegs (North)', 'Hammer Bridge Water Drop', 'Hammer Bridge NC', 'Hammer Bridge EN']), + create_dw_region(player, 'Hammer Bridge South Area', None, ['Hammer Bridge Pegs (South)', 'Hammer Bridge WS', 'Hammer Bridge SC']), + create_dw_region(player, 'Hammer Bridge Water', None, ['Hammer Bridge Pier', 'Hammer Bridge EC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Dark Central Cliffs', None, ['Ice Lake Iceberg FAWT Ledge Drop', 'Hammer Bridge EC Cliff Water Drop', 'Dark C Whirlpool Portal Cliff Ledge Drop']), + create_dw_region(player, 'Dark Tree Line Area', None, ['Dark Lake Hylia Fairy', 'Dark Tree Line WN', 'Dark Tree Line NW', 'Dark Tree Line SE']), + create_dw_region(player, 'Dark Tree Line Water', None, ['Dark Tree Line WC', 'Dark Tree Line SC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Darkness Nook Area', None, ['East Dark World Hint', 'East Dark World Teleporter', 'Palace of Darkness Nook NE']), + create_dw_region(player, 'Mire Area', None, ['Mire Shed', 'Misery Mire', 'Mire Fairy', 'Mire Hint']), + create_dw_region(player, 'Mire Teleporter Ledge', None, ['Mire Teleporter Ledge Drop', 'Mire Teleporter']), + create_dw_region(player, 'Mire Northern Cliffs', None, ['Mire Cliff Ledge Drop', 'Stumpy Approach Cliff Ledge Drop', 'Mirror To Bombos Tablet Ledge']), + create_dw_region(player, 'Stumpy Approach Area', None, ['Stumpy Approach Bush (South)', 'Stumpy Approach NW', 'Stumpy Approach EC']), + create_dw_region(player, 'Stumpy Approach Bush Entry', None, ['Stumpy Approach Bush (North)', 'Stumpy Approach NC']), + create_dw_region(player, 'Dark C Whirlpool Area', None, ['Dark C Whirlpool Rock (Bottom)', 'Dark C Whirlpool Pegs (Outer)', 'Dark C Whirlpool Water Entry', + 'Dark C Whirlpool EN', 'Dark C Whirlpool ES', 'Dark C Whirlpool SC']), + create_dw_region(player, 'Dark C Whirlpool Portal Area', None, ['Dark C Whirlpool Pegs (Inner)', 'South Dark World Teleporter']), + create_dw_region(player, 'Dark C Whirlpool Water', None, ['Dark C Whirlpool Landing', 'Dark C Whirlpool EC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Dark C Whirlpool Outer Area', None, ['Dark C Whirlpool Rock (Top)', 'Dark C Whirlpool WC', 'Dark C Whirlpool NW']), + create_dw_region(player, 'Hype Cave Area', None, ['Hype Cave', 'Hype Cave Water Entry', 'Hype Cave NC', 'Hype Cave WN', 'Hype Cave WS', 'Hype Cave SC']), + create_dw_region(player, 'Hype Cave Water', None, ['Hype Cave Landing', 'Hype Cave WC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Ice Lake Northwest Bank', None, ['Dark Lake Hylia Shop', 'Ice Lake Water Drop', 'Ice Lake NW']), + create_dw_region(player, 'Ice Lake Northeast Bank', None, ['Ice Lake Northeast Water Drop', 'Ice Lake Iceberg Bomb Jump', 'Ice Lake NE']), + create_dw_region(player, 'Ice Lake Southwest Ledge', None, ['Ice Lake Southwest Water Drop', 'Ice Lake WS']), + create_dw_region(player, 'Ice Lake Southeast Ledge', None, ['Ice Lake Southeast Water Drop', 'Ice Lake ES']), + create_dw_region(player, 'Ice Lake Water', None, ['Ice Lake Northeast Pier', 'Ice Lake NC', 'Ice Lake EC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Ice Lake Iceberg', None, ['Ice Palace Approach', 'Ice Lake Iceberg Water Entry', 'Ice Lake Northeast Pier Hop', 'Ice Lake Teleporter']), + create_dw_region(player, 'Ice Palace Area', None, ['Ice Palace', 'Ice Palace Leave']), + create_dw_region(player, 'Shopping Mall Area', None, ['Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Spike Cave', 'Shopping Mall Water Drop', 'Shopping Mall SE']), + create_dw_region(player, 'Shopping Mall Water', None, ['Shopping Mall Pier', 'Shopping Mall SW'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Swamp Nook Area', None, ['Swamp Nook EC', 'Swamp Nook ES']), + create_dw_region(player, 'Swamp Area', None, ['Swamp Palace', 'Swamp WC', 'Swamp WS', 'Swamp NC', 'Swamp EC']), + create_dw_region(player, 'Dark South Pass Area', None, ['Dark South Pass WC', 'Dark South Pass NC', 'Dark South Pass ES']), + create_dw_region(player, 'Bomber Corner Area', None, ['Bomber Corner Water Drop', 'Bomber Corner WS', 'Bomber Corner NE']), + create_dw_region(player, 'Bomber Corner Water', None, ['Bomber Corner Pier', 'Bomber Corner Whirlpool', 'Bomber Corner WC'], 'Dark World', Terrain.Water), + create_dw_region(player, 'Bomber Corner Water Ledge', None, ['Bomber Corner Waterfall Water Drop', 'Bomber Corner NW'], 'Dark World', Terrain.Water), create_cave_region(player, 'Lost Woods Gamble', 'a game of chance'), create_cave_region(player, 'Lost Woods Hideout (top)', 'a drop\'s exit', ['Lost Woods Hideout'], ['Lost Woods Hideout (top to bottom)']), @@ -118,14 +246,16 @@ def create_regions(world, player): create_cave_region(player, 'Lumberjack Tree (top)', 'a drop\'s exit', ['Lumberjack Tree'], ['Lumberjack Tree (top to bottom)']), create_cave_region(player, 'Lumberjack Tree (bottom)', 'a drop\'s exit', None, ['Lumberjack Tree Exit']), create_cave_region(player, 'Lumberjack House', 'a boring house'), - create_cave_region(player, 'Old Man Cave', 'a connector', ['Old Man'], ['Old Man Cave Exit (East)']), + create_cave_region(player, 'Old Man Cave (West)', 'a connector', ['Old Man'], ['Old Man Cave E']), + create_cave_region(player, 'Old Man Cave (East)', 'a connector', None, ['Old Man Cave Exit (East)', 'Old Man Cave W']), create_cave_region(player, 'Old Man Cave Ledge', 'a connector', None, ['Old Man Cave Exit (West)', 'Old Man Cave Dropdown']), create_cave_region(player, 'Old Man House', 'a connector', None, ['Old Man House Exit (Bottom)', 'Old Man House Front to Back']), create_cave_region(player, 'Old Man House Back', 'a connector', None, ['Old Man House Exit (Top)', 'Old Man House Back to Front']), create_cave_region(player, 'Death Mountain Return Cave (left)', 'a connector', None, ['Death Mountain Return Cave Exit (West)', 'Death Mountain Return Cave E']), create_cave_region(player, 'Death Mountain Return Cave (right)', 'a connector', None, ['Death Mountain Return Cave Exit (East)', 'Death Mountain Return Cave W']), create_cave_region(player, 'Spectacle Rock Cave (Top)', 'a connector', ['Spectacle Rock Cave'], ['Spectacle Rock Cave Drop', 'Spectacle Rock Cave Exit (Top)']), - create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit']), + create_cave_region(player, 'Spectacle Rock Cave (Bottom)', 'a connector', None, ['Spectacle Rock Cave Exit', 'Spectacle Rock Cave East Edge']), + create_cave_region(player, 'Spectacle Rock Cave Pool', 'a connector', None, ['Spectacle Rock Cave West Edge']), create_cave_region(player, 'Spectacle Rock Cave (Peak)', 'a connector', None, ['Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Exit (Peak)']), create_cave_region(player, 'Spiral Cave (Top)', 'a connector', ['Spiral Cave'], ['Spiral Cave (top to bottom)', 'Spiral Cave Exit (Top)']), create_cave_region(player, 'Spiral Cave (Bottom)', 'a connector', None, ['Spiral Cave Exit']), @@ -174,7 +304,8 @@ def create_regions(world, player): create_cave_region(player, 'Two Brothers House', 'a connector', None, ['Two Brothers House Exit (East)', 'Two Brothers House Exit (West)']), create_cave_region(player, 'Library', 'the library', ['Library']), create_cave_region(player, 'Kakariko Gamble Game', 'a game of chance'), - create_cave_region(player, 'Bonk Fairy (Light)', 'a fairy fountain'), + create_cave_region(player, 'Bonk Fairy (Light)', 'a fairy fountain', None, ['Bonk Fairy (Light) Pool']), + create_cave_region(player, 'Bonk Fairy Pool', 'a fairy fountain'), create_cave_region(player, 'Links House', 'your house', ['Link\'s House'], ['Links House Exit']), create_cave_region(player, 'Chris Houlihan Room', 'I AM ERROR', None, ['Chris Houlihan Room Exit']), create_cave_region(player, 'Lake Hylia Healer Fairy', 'a fairy fountain'), @@ -185,11 +316,13 @@ def create_regions(world, player): create_cave_region(player, 'Light Hype Fairy', 'a fairy fountain'), create_cave_region(player, 'Lake Hylia Fortune Teller', 'a fortune teller'), create_cave_region(player, 'Lake Hylia Shop', 'a common shop', ['Lake Hylia Shop - Left', 'Lake Hylia Shop - Middle', 'Lake Hylia Shop - Right']), - create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade - Left', 'Capacity Upgrade - Right']), + create_cave_region(player, 'Capacity Upgrade', 'the queen of fairies', ['Capacity Upgrade - Left', 'Capacity Upgrade - Right'], ['Capacity Upgrade East']), + create_cave_region(player, 'Capacity Fairy Pool', 'near the queen of fairies', None, ['Capacity Fairy Pool West']), create_cave_region(player, 'Mini Moldorm Cave', 'a bounty of five items', ['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right', 'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Generous Guy']), create_cave_region(player, 'Ice Rod Cave', 'a cave with a chest', ['Ice Rod Cave']), - create_cave_region(player, 'Good Bee Cave', 'a cold bee'), + create_cave_region(player, 'Good Bee Cave', 'a cold bee', None, ['Good Bee Cave Front to Back']), + create_cave_region(player, 'Good Bee Cave (back)', 'a cold bee', None, ['Good Bee Cave Back to Front']), create_cave_region(player, '20 Rupee Cave', 'a cave with some cash'), create_cave_region(player, 'Desert Healer Fairy', 'a fairy fountain'), create_cave_region(player, '50 Rupee Cave', 'a cave with some cash'), @@ -203,7 +336,8 @@ def create_regions(world, player): create_cave_region(player, 'Hookshot Cave (Bonk Islands)', 'a connector', ['Hookshot Cave - Bottom Right']), create_cave_region(player, 'Hookshot Cave (Hook Islands)', 'a connector', ['Hookshot Cave - Top Right', 'Hookshot Cave - Top Left', 'Hookshot Cave - Bottom Left']), create_cave_region(player, 'Hookshot Cave (Middle)', 'a connector', None, ['Hookshot Cave Middle to Back', 'Hookshot Cave Middle to Front']), - create_cave_region(player, 'Hookshot Cave (Back)', 'a connector', None, ['Hookshot Cave Back to Middle', 'Hookshot Cave Back Exit']), + create_cave_region(player, 'Hookshot Cave (Back)', 'a connector', None, ['Hookshot Cave Back to Middle', 'Hookshot Cave Back to Fairy', 'Hookshot Cave Back Exit']), + create_cave_region(player, 'Hookshot Cave (Fairy Pool)', 'a connector', None, ['Hookshot Cave Fairy to Back']), create_cave_region(player, 'Superbunny Cave (Top)', 'a connector', ['Superbunny Cave - Top', 'Superbunny Cave - Bottom'], ['Superbunny Cave Exit (Top)']), create_cave_region(player, 'Superbunny Cave (Bottom)', 'a connector', None, ['Superbunny Cave Climb', 'Superbunny Cave Exit (Bottom)']), create_cave_region(player, 'Dark Death Mountain Shop', 'a common shop', ['Dark Death Mountain Shop - Left', 'Dark Death Mountain Shop - Middle', 'Dark Death Mountain Shop - Right']), @@ -223,13 +357,13 @@ def create_regions(world, player): create_cave_region(player, 'Palace of Darkness Hint', 'a storyteller'), create_cave_region(player, '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, 'Bonk Fairy (Dark)', 'a fairy fountain', None, ['Bonk Fairy (Dark) Pool']), create_cave_region(player, 'Big Bomb Shop', 'the bomb shop'), 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, 'Mire Shed', 'a cave with two chests', ['Mire Shed - Left', 'Mire Shed - Right']), - create_cave_region(player, 'Dark Desert Healer Fairy', 'a fairy fountain'), - create_cave_region(player, 'Dark Desert Hint', 'a storyteller'), + create_cave_region(player, 'Mire Healer Fairy', 'a fairy fountain'), + create_cave_region(player, 'Mire 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', 'Hype Cave - Bottom', 'Hype Cave - Generous Guy']), create_cave_region(player, 'Dark Lake Hylia Shop', 'a common shop', ['Dark Lake Hylia Shop - Left', 'Dark Lake Hylia Shop - Middle', 'Dark Lake Hylia Shop - Right']), @@ -444,8 +578,9 @@ def create_dungeon_regions(world, player): create_dungeon_region(player, 'PoD Arena Ledge', 'Palace of Darkness', ['Palace of Darkness - The Arena - Ledge'], ['PoD Arena Ledge ES', 'PoD Arena Ledge to Ranged Crystal']), create_dungeon_region(player, 'PoD Arena Ledge - Ranged Crystal', 'Palace of Darkness', None, ['PoD Arena Ledge Ranged Crystal Exit']), create_dungeon_region(player, 'PoD Sexy Statue', 'Palace of Darkness', None, ['PoD Sexy Statue W', 'PoD Sexy Statue NW']), - create_dungeon_region(player, 'PoD Map Balcony', 'Palace of Darkness', ['Palace of Darkness - Map Chest'], ['PoD Map Balcony to Ranged Crystal', 'PoD Map Balcony WS', 'PoD Map Balcony South Stairs', 'PoD Map Balcony Drop Down']), + create_dungeon_region(player, 'PoD Map Balcony', 'Palace of Darkness', ['Palace of Darkness - Map Chest'], ['PoD Map Balcony to Ranged Crystal', 'PoD Map Balcony WS', 'PoD Map Balcony South Stairs', 'PoD Map Balcony Drop Down', 'PoD Map Balcony ES']), create_dungeon_region(player, 'PoD Map Balcony - Ranged Crystal', 'Palace of Darkness', None, ['PoD Map Balcony Ranged Crystal Exit']), + create_dungeon_region(player, 'PoD Fairy Pool', 'Palace of Darkness', None, ['PoD Fairy Pool WS']), create_dungeon_region(player, 'PoD Conveyor', 'Palace of Darkness', None, ['PoD Conveyor North Stairs', 'PoD Conveyor SW']), create_dungeon_region(player, 'PoD Mimics 1', 'Palace of Darkness', None, ['PoD Mimics 1 NW', 'PoD Mimics 1 SW']), create_dungeon_region(player, 'PoD Jelly Hall', 'Palace of Darkness', None, ['PoD Jelly Hall NW', 'PoD Jelly Hall NE']), @@ -455,6 +590,7 @@ def create_dungeon_regions(world, player): create_dungeon_region(player, 'PoD Basement Ledge', 'Palace of Darkness', None, ['PoD Basement Ledge Drop Down', 'PoD Basement Ledge Up Stairs']), create_dungeon_region(player, 'PoD Big Key Landing', 'Palace of Darkness', ['Palace of Darkness - Big Key Chest'], ['PoD Big Key Landing Down Stairs', 'PoD Big Key Landing Hole']), create_dungeon_region(player, 'PoD Falling Bridge Ledge', 'Palace of Darkness', None, ['PoD Falling Bridge WN', 'PoD Falling Bridge EN', 'PoD Falling Bridge Path S']), + create_dungeon_region(player, 'PoD Falling Bridge Mid', 'Palace of Darkness', None, ['PoD Falling Bridge Mid Path S', 'PoD Falling Bridge Mid Path N']), create_dungeon_region(player, 'PoD Falling Bridge', 'Palace of Darkness', None, ['PoD Falling Bridge SW', 'PoD Falling Bridge Path N']), create_dungeon_region(player, 'PoD Dark Maze', 'Palace of Darkness', ['Palace of Darkness - Dark Maze - Top', 'Palace of Darkness - Dark Maze - Bottom'], ['PoD Dark Maze EN', 'PoD Dark Maze E']), create_dungeon_region(player, 'PoD Big Chest Balcony', 'Palace of Darkness', ['Palace of Darkness - Big Chest'], ['PoD Big Chest Balcony W']), @@ -607,7 +743,8 @@ def create_dungeon_regions(world, player): create_dungeon_region(player, 'Ice Pengator Switch', 'Ice Palace', None, ['Ice Pengator Switch WS', 'Ice Pengator Switch ES']), create_dungeon_region(player, 'Ice Dead End', 'Ice Palace', None, ['Ice Dead End WS']), create_dungeon_region(player, 'Ice Big Key', 'Ice Palace', ['Ice Palace - Big Key Chest'], ['Ice Big Key Push Block', 'Ice Big Key Down Ladder']), - create_dungeon_region(player, 'Ice Bomb Drop', 'Ice Palace', None, ['Ice Bomb Drop SE', 'Ice Bomb Drop Hole']), + create_dungeon_region(player, 'Ice Bomb Drop', 'Ice Palace', None, ['Ice Bomb Drop SE', 'Ice Bomb Drop Path']), + create_dungeon_region(player, 'Ice Bomb Drop - Top', 'Ice Palace', None, ['Ice Bomb Drop Hole']), create_dungeon_region(player, 'Ice Stalfos Hint', 'Ice Palace', None, ['Ice Stalfos Hint SE']), create_dungeon_region(player, 'Ice Conveyor', 'Ice Palace', ['Ice Palace - Conveyor Key Drop'], ['Ice Conveyor NE', 'Ice Conveyor to Crystal', 'Ice Conveyor SW']), create_dungeon_region(player, 'Ice Conveyor - Crystal', 'Ice Palace', None, ['Ice Conveyor Crystal Exit']), @@ -619,7 +756,8 @@ def create_dungeon_regions(world, player): create_dungeon_region(player, 'Ice Firebar', 'Ice Palace', None, ['Ice Firebar ES', 'Ice Firebar Down Ladder']), create_dungeon_region(player, 'Ice Falling Square', 'Ice Palace', None, ['Ice Falling Square SE', 'Ice Falling Square Hole']), create_dungeon_region(player, 'Ice Spike Room', 'Ice Palace', ['Ice Palace - Spike Room'], ['Ice Spike Room WS', 'Ice Spike Room Up Stairs', 'Ice Spike Room Down Stairs']), - create_dungeon_region(player, 'Ice Hammer Block', 'Ice Palace', ['Ice Palace - Hammer Block Key Drop', 'Ice Palace - Map Chest'], ['Ice Hammer Block Down Stairs', 'Ice Hammer Block ES']), + create_dungeon_region(player, 'Ice Right H', 'Ice Palace', None, ['Ice Hammer Block Down Stairs', 'Ice Hammer Block ES', 'Ice Right H Path']), + create_dungeon_region(player, 'Ice Hammer Block', 'Ice Palace', ['Ice Palace - Hammer Block Key Drop', 'Ice Palace - Map Chest'], ['Ice Hammer Block Path']), create_dungeon_region(player, 'Ice Tongue Pull', 'Ice Palace', None, ['Ice Tongue Pull Up Ladder', 'Ice Tongue Pull WS']), create_dungeon_region(player, 'Ice Freezors', 'Ice Palace', ['Ice Palace - Freezor Chest'], ['Ice Freezors Up Ladder', 'Ice Freezors Hole', 'Ice Freezors Bomb Hole']), create_dungeon_region(player, 'Ice Freezors Ledge', 'Ice Palace', None, ['Ice Freezors Ledge ES', 'Ice Freezors Ledge Hole']), @@ -934,12 +1072,16 @@ def create_menu_region(player, name, locations=None, exits=None): return _create_region(player, name, RegionType.Menu, 'Menu', locations, exits) -def create_lw_region(player, name, locations=None, exits=None, hint='Light World'): - return _create_region(player, name, RegionType.LightWorld, hint, locations, exits) +def create_lw_region(player, name, locations=None, exits=None, hint='Light World', terrain=Terrain.Land): + region = _create_region(player, name, RegionType.LightWorld, hint, locations, exits) + region.terrain = terrain + return region -def create_dw_region(player, name, locations=None, exits=None, hint='Dark World'): - return _create_region(player, name, RegionType.DarkWorld, hint, locations, exits) +def create_dw_region(player, name, locations=None, exits=None, hint='Dark World', terrain=Terrain.Land): + region = _create_region(player, name, RegionType.DarkWorld, hint, locations, exits) + region.terrain = terrain + return region def create_cave_region(player, name, hint='Hyrule', locations=None, exits=None): @@ -976,7 +1118,7 @@ def mark_light_dark_world_regions(world, player): current = queue.popleft() current.is_light_world = True for exit in current.exits: - if exit.connected_region is None or exit.connected_region.type == RegionType.DarkWorld: # todo: remove none check + if exit.connected_region is None or exit.connected_region.type == RegionType.DarkWorld: # Don't venture into the dark world continue if exit.connected_region not in seen: @@ -1021,7 +1163,7 @@ def create_shops(world, player): def adjust_locations(world, player): # handle pots - world.pot_contents[player] = PotSecretTable() + world.data_tables[player].pot_secret_table = PotSecretTable() for location, datum in key_drop_data.items(): loc = world.get_location(location, player) drop_location = 'Drop' == datum[0] @@ -1029,6 +1171,10 @@ def adjust_locations(world, player): loc.type = LocationType.Drop snes_address, room, sprite_idx = datum[1] loc.address = snes_address + sprite = world.data_tables[player].uw_enemy_table.room_map[room][sprite_idx] + sprite.location = loc + if world.enemy_shuffle[player] != 'none': + loc.note = enemy_names[sprite.kind] else: loc.type = LocationType.Pot pot, pot_index = next((p, i) for i, p in enumerate(vanilla_pots[datum[1]]) if p.item == PotItem.Key) @@ -1036,13 +1182,12 @@ def adjust_locations(world, player): loc.address = pot_address(pot_index, datum[1]) loc.pot = pot pot.location = loc - if (not world.dropshuffle[player] and drop_location)\ - or (not drop_location and world.pottery[player] in ['none', 'cave']): + if ((world.dropshuffle[player] == 'none' and drop_location) + or (not drop_location and world.pottery[player] in ['none', 'cave'])): loc.skip = True else: key_item = loc.item key_item.location = None - loc.forced_item = None loc.item = None loc.event = False @@ -1060,7 +1205,7 @@ def adjust_locations(world, player): pot = world.get_location(loc, player).pot else: pot = pot_orig.copy() - world.pot_contents[player].room_map[super_tile].append(pot) + world.data_tables[player].pot_secret_table.room_map[super_tile].append(pot) if valid_pot_location(pot, world.pot_pool[player], world, player): create_pot_location(pot, pot_index, super_tile, world, player) @@ -1073,8 +1218,9 @@ def adjust_locations(world, player): loc.type = LocationType.Shop # player address? it is in the shop table index += 1 + setup_enemy_locations(world, player) # unreal events: - for l in ['Ganon', 'Agahnim 1', 'Agahnim 2', 'Dark Blacksmith Ruins', 'Frog', 'Missing Smith', 'Floodgate', + for l in ['Ganon', 'Agahnim 1', 'Agahnim 2', 'Frog', 'Missing Smith', 'Dark Blacksmith Ruins', 'Floodgate', 'Trench 1 Switch', 'Trench 2 Switch', 'Swamp Drain', 'Attic Cracked Floor', 'Suspicious Maiden', 'Revealing Light', 'Ice Block Drop', 'Zelda Pickup', 'Zelda Drop Off', 'Skull Star Tile']: location = world.get_location_unsafe(l, player) @@ -1192,222 +1338,224 @@ flooded_keys_reverse = { 'Swamp Palace - Trench 1 Pot Key': 'Trench 1 Switch', 'Swamp Palace - Trench 2 Pot Key': 'Trench 2 Switch' } -location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), - 'Bottle Merchant': (0x2eb18, 0x186339, False, 'with a merchant'), - 'Flute Spot': (0x18014a, 0x18633d, False, 'underground'), - 'Sunken Treasure': (0x180145, 0x186354, False, 'underwater'), - 'Purple Chest': (0x33d68, 0x186359, False, 'from a box'), - "Blind's Hideout - Top": (0xeb0f, 0x1862e3, False, 'in a basement'), - "Blind's Hideout - Left": (0xeb12, 0x1862e6, False, 'in a basement'), - "Blind's Hideout - Right": (0xeb15, 0x1862e9, False, 'in a basement'), - "Blind's Hideout - Far Left": (0xeb18, 0x1862ec, False, 'in a basement'), - "Blind's Hideout - Far Right": (0xeb1b, 0x1862ef, False, 'in a basement'), - "Link's Uncle": (0x2df45, 0x18635f, False, 'with your uncle'), - 'Secret Passage': (0xe971, 0x186145, False, 'near your uncle'), - 'King Zora': (0xee1c3, 0x186360, False, 'at a high price'), - "Zora's Ledge": (0x180149, 0x186358, False, 'near Zora'), - 'Waterfall Fairy - Left': (0xe9b0, 0x186184, False, 'near a fairy'), - 'Waterfall Fairy - Right': (0xe9d1, 0x1861a5, False, 'near a fairy'), - "King's Tomb": (0xe97a, 0x18614e, False, 'alone in a cave'), - 'Floodgate Chest': (0xe98c, 0x186160, False, 'in the dam'), - "Link's House": (0xe9bc, 0x186190, False, 'in your home'), - 'Kakariko Tavern': (0xe9ce, 0x1861a2, False, 'in the bar'), - 'Chicken House': (0xe9e9, 0x1861bd, False, 'near poultry'), - "Aginah's Cave": (0xe9f2, 0x1861c6, False, 'with Aginah'), - "Sahasrahla's Hut - Left": (0xea82, 0x186256, False, 'near the elder'), - "Sahasrahla's Hut - Middle": (0xea85, 0x186259, False, 'near the elder'), - "Sahasrahla's Hut - Right": (0xea88, 0x18625c, False, 'near the elder'), - 'Sahasrahla': (0x2f1fc, 0x186365, False, 'with the elder'), - 'Kakariko Well - Top': (0xea8e, 0x186262, False, 'in a well'), - 'Kakariko Well - Left': (0xea91, 0x186265, False, 'in a well'), - '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'), - 'Sick Kid': (0x339cf, 0x186367, False, 'with the sick'), - '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'), - 'Graveyard Cave': (0x180004, 0x18634c, False, 'alone in a cave'), - 'Checkerboard Cave': (0x180005, 0x18634d, False, 'alone in a cave'), - 'Mini Moldorm Cave - Far Left': (0xeb42, 0x186316, False, 'near Moldorms'), - 'Mini Moldorm Cave - Left': (0xeb45, 0x186319, False, 'near Moldorms'), - 'Mini Moldorm Cave - Right': (0xeb48, 0x18631c, False, 'near Moldorms'), - 'Mini Moldorm Cave - Far Right': (0xeb4b, 0x18631f, False, 'near Moldorms'), - 'Mini Moldorm Cave - Generous Guy': (0x180010, 0x18635a, False, 'near Moldorms'), - 'Ice Rod Cave': (0xeb4e, 0x186322, False, 'in a frozen cave'), - 'Bonk Rock Cave': (0xeb3f, 0x186313, False, 'alone in a cave'), - 'Library': (0x180012, 0x18635c, False, 'near books'), - 'Potion Shop': (0x180014, 0x18635d, False, 'near potions'), - 'Lake Hylia Island': (0x180144, 0x186353, False, 'on an island'), - 'Maze Race': (0x180142, 0x186351, False, 'at the race'), - 'Desert Ledge': (0x180143, 0x186352, False, 'in the desert'), - 'Desert Palace - Big Chest': (0xe98f, 0x186163, False, 'in Desert Palace'), - 'Desert Palace - Torch': (0x180160, 0x186362, False, 'in Desert Palace'), - 'Desert Palace - Map Chest': (0xe9b6, 0x18618a, False, 'in Desert Palace'), - 'Desert Palace - Compass Chest': (0xe9cb, 0x18619f, False, 'in Desert Palace'), - 'Desert Palace - Big Key Chest': (0xe9c2, 0x186196, False, 'in Desert Palace'), - 'Desert Palace - Boss': (0x180151, 0x18633f, False, 'with Lanmolas'), - 'Eastern Palace - Compass Chest': (0xe977, 0x18614b, False, 'in Eastern Palace'), - 'Eastern Palace - Big Chest': (0xe97d, 0x186151, False, 'in Eastern Palace'), - 'Eastern Palace - Cannonball Chest': (0xe9b3, 0x186187, False, 'in Eastern Palace'), - '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'), - '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'), - 'Sewers - Dark Cross': (0xe96e, 0x186142, False, 'in the sewers'), - 'Sewers - Secret Room - Left': (0xeb5d, 0x186331, False, 'in the sewers'), - 'Sewers - Secret Room - Middle': (0xeb60, 0x186334, False, 'in the sewers'), - 'Sewers - Secret Room - Right': (0xeb63, 0x186337, False, 'in the sewers'), - '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'), - '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'), - 'Paradox Cave Lower - Right': (0xeb30, 0x186304, False, 'in a cave with seven chests'), - 'Paradox Cave Lower - Far Right': (0xeb33, 0x186307, False, 'in a cave with seven chests'), - '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'), - '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'), - 'Tower of Hera - Map Chest': (0xe9ad, 0x186181, False, 'in Tower of Hera'), - 'Tower of Hera - Big Key Chest': (0xe9e6, 0x1861ba, False, 'in Tower of Hera'), - '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'), - 'Catfish': (0xee185, 0x186361, False, 'with a catfish'), - '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'), - 'Hype Cave - Middle Right': (0xeb21, 0x1862f5, False, 'near a bat-like man'), - 'Hype Cave - Middle Left': (0xeb24, 0x1862f8, False, 'near a bat-like man'), - 'Hype Cave - Bottom': (0xeb27, 0x1862fb, False, 'near a bat-like man'), - 'Hype Cave - Generous Guy': (0x180011, 0x18635b, False, 'with a bat-like man'), - 'Peg Cave': (0x180006, 0x18634e, False, 'alone in a cave'), - 'Pyramid Fairy - Left': (0xe980, 0x186154, False, 'near a fairy'), - 'Pyramid Fairy - Right': (0xe983, 0x186157, False, 'near a fairy'), - 'Brewery': (0xe9ec, 0x1861c0, False, 'alone in a home'), - 'C-Shaped House': (0xe9ef, 0x1861c3, False, 'alone in a home'), - 'Chest Game': (0xeda8, 0x18636b, False, 'as a prize'), - 'Bumper Cave Ledge': (0x180146, 0x186355, False, 'on a ledge'), - 'Mire Shed - Left': (0xea73, 0x186247, False, 'near sparks'), - 'Mire Shed - Right': (0xea76, 0x18624a, False, 'near sparks'), - 'Superbunny Cave - Top': (0xea7c, 0x186250, False, 'in a connection'), - 'Superbunny Cave - Bottom': (0xea7f, 0x186253, False, 'in a connection'), - 'Spike Cave': (0xea8b, 0x18625f, False, 'beyond spikes'), - 'Hookshot Cave - Top Right': (0xeb51, 0x186325, False, 'across pits'), - 'Hookshot Cave - Top Left': (0xeb54, 0x186328, False, 'across pits'), - 'Hookshot Cave - Bottom Right': (0xeb5a, 0x18632e, False, 'across pits'), - 'Hookshot Cave - Bottom Left': (0xeb57, 0x18632b, False, 'across pits'), - 'Floating Island': (0x180141, 0x186350, False, 'on an island'), - 'Mimic Cave': (0xe9c5, 0x186199, False, 'in a cave of mimicry'), - 'Swamp Palace - Entrance': (0xea9d, 0x186271, False, 'in Swamp Palace'), - 'Swamp Palace - Map Chest': (0xe986, 0x18615a, False, 'in Swamp Palace'), - 'Swamp Palace - Big Chest': (0xe989, 0x18615d, False, 'in Swamp Palace'), - 'Swamp Palace - Compass Chest': (0xeaa0, 0x186274, False, 'in Swamp Palace'), - 'Swamp Palace - Big Key Chest': (0xeaa6, 0x18627a, False, 'in Swamp Palace'), - 'Swamp Palace - West Chest': (0xeaa3, 0x186277, False, 'in Swamp Palace'), - 'Swamp Palace - Flooded Room - Left': (0xeaa9, 0x18627d, False, 'in Swamp Palace'), - '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 - 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'), - 'Skull Woods - Big Chest': (0xe998, 0x18616c, False, 'in Skull Woods'), - 'Skull Woods - Pot Prison': (0xe9a1, 0x186175, False, 'in Skull Woods'), - 'Skull Woods - Pinball Room': (0xe9c8, 0x18619c, False, 'in Skull Woods'), - 'Skull Woods - Big Key Chest': (0xe99e, 0x186172, False, 'in Skull Woods'), - 'Skull Woods - Bridge Room': (0xe9fe, 0x1861d2, False, 'near Mothula'), - 'Skull Woods - Boss': (0x180155, 0x186343, False, 'with Mothula'), - 'Ice Palace - Compass Chest': (0xe9d4, 0x1861a8, False, 'in Ice Palace'), - 'Ice Palace - Freezor Chest': (0xe995, 0x186169, False, 'in Ice Palace'), - 'Ice Palace - Big Chest': (0xe9aa, 0x18617e, False, 'in Ice Palace'), - 'Ice Palace - Iced T Room': (0xe9e3, 0x1861b7, False, 'in Ice Palace'), - 'Ice Palace - Spike Room': (0xe9e0, 0x1861b4, False, 'in Ice Palace'), - 'Ice Palace - Big Key Chest': (0xe9a4, 0x186178, False, 'in Ice Palace'), - 'Ice Palace - Map Chest': (0xe9dd, 0x1861b1, False, 'in Ice Palace'), - 'Ice Palace - Boss': (0x180157, 0x186345, False, 'with Kholdstare'), - 'Misery Mire - Big Chest': (0xea67, 0x18623b, False, 'in Misery Mire'), - 'Misery Mire - Map Chest': (0xea6a, 0x18623e, False, 'in Misery Mire'), - 'Misery Mire - Main Lobby': (0xea5e, 0x186232, False, 'in Misery Mire'), - 'Misery Mire - Bridge Chest': (0xea61, 0x186235, False, 'in Misery Mire'), - 'Misery Mire - Spike Chest': (0xe9da, 0x1861ae, False, 'in Misery Mire'), - 'Misery Mire - Compass Chest': (0xea64, 0x186238, False, 'in Misery Mire'), - 'Misery Mire - Big Key Chest': (0xea6d, 0x186241, False, 'in Misery Mire'), - 'Misery Mire - Boss': (0x180158, 0x186346, False, 'with Vitreous'), - 'Turtle Rock - Compass Chest': (0xea22, 0x1861f6, False, 'in Turtle Rock'), - 'Turtle Rock - Roller Room - Left': (0xea1c, 0x1861f0, False, 'in Turtle Rock'), - 'Turtle Rock - Roller Room - Right': (0xea1f, 0x1861f3, False, 'in Turtle Rock'), - 'Turtle Rock - Chain Chomps': (0xea16, 0x1861ea, False, 'in Turtle Rock'), - 'Turtle Rock - Big Key Chest': (0xea25, 0x1861f9, False, 'in Turtle Rock'), - 'Turtle Rock - Big Chest': (0xea19, 0x1861ed, False, 'in Turtle Rock'), - 'Turtle Rock - Crystaroller Room': (0xea34, 0x186208, False, 'in Turtle Rock'), - 'Turtle Rock - Eye Bridge - Bottom Left': (0xea31, 0x186205, False, 'in Turtle Rock'), - 'Turtle Rock - Eye Bridge - Bottom Right': (0xea2e, 0x186202, False, 'in Turtle Rock'), - 'Turtle Rock - Eye Bridge - Top Left': (0xea2b, 0x1861ff, False, 'in Turtle Rock'), - 'Turtle Rock - Eye Bridge - Top Right': (0xea28, 0x1861fc, False, 'in Turtle Rock'), - 'Turtle Rock - Boss': (0x180159, 0x186347, False, 'with Trinexx'), - 'Palace of Darkness - Shooter Room': (0xea5b, 0x18622f, False, 'in Palace of Darkness'), - 'Palace of Darkness - The Arena - Bridge': (0xea3d, 0x186211, False, 'in Palace of Darkness'), - 'Palace of Darkness - Stalfos Basement': (0xea49, 0x18621d, False, 'in Palace of Darkness'), - 'Palace of Darkness - Big Key Chest': (0xea37, 0x18620b, False, 'in Palace of Darkness'), - 'Palace of Darkness - The Arena - Ledge': (0xea3a, 0x18620e, False, 'in Palace of Darkness'), - 'Palace of Darkness - Map Chest': (0xea52, 0x186226, False, 'in Palace of Darkness'), - 'Palace of Darkness - Compass Chest': (0xea43, 0x186217, False, 'in Palace of Darkness'), - 'Palace of Darkness - Dark Basement - Left': (0xea4c, 0x186220, False, 'in Palace of Darkness'), - 'Palace of Darkness - Dark Basement - Right': (0xea4f, 0x186223, False, 'in Palace of Darkness'), - 'Palace of Darkness - Dark Maze - Top': (0xea55, 0x186229, False, 'in Palace of Darkness'), - 'Palace of Darkness - Dark Maze - Bottom': (0xea58, 0x18622c, False, 'in Palace of Darkness'), - 'Palace of Darkness - Big Chest': (0xea40, 0x186214, False, 'in Palace of Darkness'), - 'Palace of Darkness - Harmless Hellway': (0xea46, 0x18621a, False, 'in Palace of Darkness'), - 'Palace of Darkness - Boss': (0x180153, 0x186341, False, 'with Helmasaur King'), - "Ganons Tower - Bob's Torch": (0x180161, 0x186363, False, "in Ganon's Tower"), - 'Ganons Tower - Hope Room - Left': (0xead9, 0x1862ad, False, "in Ganon's Tower"), - 'Ganons Tower - Hope Room - Right': (0xeadc, 0x1862b0, False, "in Ganon's Tower"), - 'Ganons Tower - Tile Room': (0xeae2, 0x1862b6, False, "in Ganon's Tower"), - 'Ganons Tower - Compass Room - Top Left': (0xeae5, 0x1862b9, False, "in Ganon's Tower"), - 'Ganons Tower - Compass Room - Top Right': (0xeae8, 0x1862bc, False, "in Ganon's Tower"), - 'Ganons Tower - Compass Room - Bottom Left': (0xeaeb, 0x1862bf, False, "in Ganon's Tower"), - 'Ganons Tower - Compass Room - Bottom Right': (0xeaee, 0x1862c2, False, "in Ganon's Tower"), - 'Ganons Tower - DMs Room - Top Left': (0xeab8, 0x18628c, False, "in Ganon's Tower"), - 'Ganons Tower - DMs Room - Top Right': (0xeabb, 0x18628f, False, "in Ganon's Tower"), - 'Ganons Tower - DMs Room - Bottom Left': (0xeabe, 0x186292, False, "in Ganon's Tower"), - 'Ganons Tower - DMs Room - Bottom Right': (0xeac1, 0x186295, False, "in Ganon's Tower"), - 'Ganons Tower - Map Chest': (0xead3, 0x1862a7, False, "in Ganon's Tower"), - 'Ganons Tower - Firesnake Room': (0xead0, 0x1862a4, False, "in Ganon's Tower"), - 'Ganons Tower - Randomizer Room - Top Left': (0xeac4, 0x186298, False, "in Ganon's Tower"), - 'Ganons Tower - Randomizer Room - Top Right': (0xeac7, 0x18629b, False, "in Ganon's Tower"), - 'Ganons Tower - Randomizer Room - Bottom Left': (0xeaca, 0x18629e, False, "in Ganon's Tower"), - 'Ganons Tower - Randomizer Room - Bottom Right': (0xeacd, 0x1862a1, False, "in Ganon's Tower"), - "Ganons Tower - Bob's Chest": (0xeadf, 0x1862b3, False, "in Ganon's Tower"), - 'Ganons Tower - Big Chest': (0xead6, 0x1862aa, False, "in Ganon's Tower"), - 'Ganons Tower - Big Key Room - Left': (0xeaf4, 0x1862c8, False, "in Ganon's Tower"), - 'Ganons Tower - Big Key Room - Right': (0xeaf7, 0x1862cb, False, "in Ganon's Tower"), - 'Ganons Tower - Big Key Chest': (0xeaf1, 0x1862c5, False, "in Ganon's Tower"), - 'Ganons Tower - Mini Helmasaur Room - Left': (0xeafd, 0x1862d1, False, "atop Ganon's Tower"), - 'Ganons Tower - Mini Helmasaur Room - Right': (0xeb00, 0x1862d4, False, "atop Ganon's Tower"), - 'Ganons Tower - Pre-Moldorm Chest': (0xeb03, 0x1862d7, False, "atop Ganon's Tower"), - 'Ganons Tower - Validation Chest': (0xeb06, 0x1862da, False, "atop Ganon's Tower"), + + +location_table = {'Mushroom': (0x180013, 0x186df8, False, 'in the woods'), + 'Bottle Merchant': (0x2eb18, 0x186df9, False, 'with a merchant'), + 'Flute Spot': (0x18014a, 0x186dfd, False, 'underground'), + 'Sunken Treasure': (0x180145, 0x186e14, False, 'underwater'), + 'Purple Chest': (0x33d68, 0x186e19, False, 'from a box'), + "Blind's Hideout - Top": (0xeb0f, 0x186da3, False, 'in a basement'), + "Blind's Hideout - Left": (0xeb12, 0x186da6, False, 'in a basement'), + "Blind's Hideout - Right": (0xeb15, 0x186da9, False, 'in a basement'), + "Blind's Hideout - Far Left": (0xeb18, 0x186dac, False, 'in a basement'), + "Blind's Hideout - Far Right": (0xeb1b, 0x186daf, False, 'in a basement'), + "Link's Uncle": (0x2df45, 0x186e1f, False, 'with your uncle'), + 'Secret Passage': (0xe971, 0x186c05, False, 'near your uncle'), + 'King Zora': (0xee1c3, 0x186e20, False, 'at a high price'), + "Zora's Ledge": (0x180149, 0x186e18, False, 'near Zora'), + 'Waterfall Fairy - Left': (0xe9b0, 0x186c44, False, 'near a fairy'), + 'Waterfall Fairy - Right': (0xe9d1, 0x186c65, False, 'near a fairy'), + "King's Tomb": (0xe97a, 0x186c0e, False, 'alone in a cave'), + 'Floodgate Chest': (0xe98c, 0x186c20, False, 'in the dam'), + "Link's House": (0xe9bc, 0x186c50, False, 'in your home'), + 'Kakariko Tavern': (0xe9ce, 0x186c62, False, 'in the bar'), + 'Chicken House': (0xe9e9, 0x186c7d, False, 'near poultry'), + "Aginah's Cave": (0xe9f2, 0x186c86, False, 'with Aginah'), + "Sahasrahla's Hut - Left": (0xea82, 0x186d16, False, 'near the elder'), + "Sahasrahla's Hut - Middle": (0xea85, 0x186d19, False, 'near the elder'), + "Sahasrahla's Hut - Right": (0xea88, 0x186d1c, False, 'near the elder'), + 'Sahasrahla': (0x2f1fc, 0x186e25, False, 'with the elder'), + 'Kakariko Well - Top': (0xea8e, 0x186d22, False, 'in a well'), + 'Kakariko Well - Left': (0xea91, 0x186d25, False, 'in a well'), + 'Kakariko Well - Middle': (0xea94, 0x186d28, False, 'in a well'), + 'Kakariko Well - Right': (0xea97, 0x186d2b, False, 'in a well'), + 'Kakariko Well - Bottom': (0xea9a, 0x186d2e, False, 'in a well'), + 'Blacksmith': (0x18002a, 0x186e26, False, 'with the smith'), + 'Magic Bat': (0x180015, 0x186e1e, False, 'with the bat'), + 'Sick Kid': (0x339cf, 0x186e27, False, 'with the sick'), + 'Hobo': (0x33e7d, 0x186e28, False, 'with the hobo'), + 'Lost Woods Hideout': (0x180000, 0x186e08, False, 'near a thief'), + 'Lumberjack Tree': (0x180001, 0x186e09, False, 'in a hole'), + 'Cave 45': (0x180003, 0x186e0b, False, 'alone in a cave'), + 'Graveyard Cave': (0x180004, 0x186e0c, False, 'alone in a cave'), + 'Checkerboard Cave': (0x180005, 0x186e0d, False, 'alone in a cave'), + 'Mini Moldorm Cave - Far Left': (0xeb42, 0x186dd6, False, 'near Moldorms'), + 'Mini Moldorm Cave - Left': (0xeb45, 0x186dd9, False, 'near Moldorms'), + 'Mini Moldorm Cave - Right': (0xeb48, 0x186ddc, False, 'near Moldorms'), + 'Mini Moldorm Cave - Far Right': (0xeb4b, 0x186ddf, False, 'near Moldorms'), + 'Mini Moldorm Cave - Generous Guy': (0x180010, 0x186e1a, False, 'near Moldorms'), + 'Ice Rod Cave': (0xeb4e, 0x186de2, False, 'in a frozen cave'), + 'Bonk Rock Cave': (0xeb3f, 0x186dd3, False, 'alone in a cave'), + 'Library': (0x180012, 0x186e1c, False, 'near books'), + 'Potion Shop': (0x180014, 0x186e1d, False, 'near potions'), + 'Lake Hylia Island': (0x180144, 0x186e13, False, 'on an island'), + 'Maze Race': (0x180142, 0x186e11, False, 'at the race'), + 'Desert Ledge': (0x180143, 0x186e12, False, 'in the desert'), + 'Desert Palace - Big Chest': (0xe98f, 0x186c23, False, 'in Desert Palace'), + 'Desert Palace - Torch': (0x180160, 0x186e22, False, 'in Desert Palace'), + 'Desert Palace - Map Chest': (0xe9b6, 0x186c4a, False, 'in Desert Palace'), + 'Desert Palace - Compass Chest': (0xe9cb, 0x186c5f, False, 'in Desert Palace'), + 'Desert Palace - Big Key Chest': (0xe9c2, 0x186c56, False, 'in Desert Palace'), + 'Desert Palace - Boss': (0x180151, 0x186dff, False, 'with Lanmolas'), + 'Eastern Palace - Compass Chest': (0xe977, 0x186c0b, False, 'in Eastern Palace'), + 'Eastern Palace - Big Chest': (0xe97d, 0x186c11, False, 'in Eastern Palace'), + 'Eastern Palace - Cannonball Chest': (0xe9b3, 0x186c47, False, 'in Eastern Palace'), + 'Eastern Palace - Big Key Chest': (0xe9b9, 0x186c4d, False, 'in Eastern Palace'), + 'Eastern Palace - Map Chest': (0xe9f5, 0x186c89, False, 'in Eastern Palace'), + 'Eastern Palace - Boss': (0x180150, 0x186dfe, False, 'with the Armos'), + 'Master Sword Pedestal': (0x289b0, 0x186e29, False, 'at the pedestal'), + 'Hyrule Castle - Boomerang Chest': (0xe974, 0x186c08, False, 'in Hyrule Castle'), + 'Hyrule Castle - Map Chest': (0xeb0c, 0x186da0, False, 'in Hyrule Castle'), + "Hyrule Castle - Zelda's Chest": (0xeb09, 0x186d9d, False, 'in Hyrule Castle'), + 'Sewers - Dark Cross': (0xe96e, 0x186c02, False, 'in the sewers'), + 'Sewers - Secret Room - Left': (0xeb5d, 0x186df1, False, 'in the sewers'), + 'Sewers - Secret Room - Middle': (0xeb60, 0x186df4, False, 'in the sewers'), + 'Sewers - Secret Room - Right': (0xeb63, 0x186df7, False, 'in the sewers'), + 'Sanctuary': (0xea79, 0x186d0d, False, 'in Sanctuary'), + 'Castle Tower - Room 03': (0xeab5, 0x186d49, False, 'in Castle Tower'), + 'Castle Tower - Dark Maze': (0xeab2, 0x186d46, False, 'in Castle Tower'), + 'Old Man': (0xf69fa, 0x186e24, False, 'with the old man'), + 'Spectacle Rock Cave': (0x180002, 0x186e0a, False, 'alone in a cave'), + 'Paradox Cave Lower - Far Left': (0xeb2a, 0x186dbe, False, 'in a cave with seven chests'), + 'Paradox Cave Lower - Left': (0xeb2d, 0x186dc1, False, 'in a cave with seven chests'), + 'Paradox Cave Lower - Right': (0xeb30, 0x186dc4, False, 'in a cave with seven chests'), + 'Paradox Cave Lower - Far Right': (0xeb33, 0x186dc7, False, 'in a cave with seven chests'), + 'Paradox Cave Lower - Middle': (0xeb36, 0x186dca, False, 'in a cave with seven chests'), + 'Paradox Cave Upper - Left': (0xeb39, 0x186dcd, False, 'in a cave with seven chests'), + 'Paradox Cave Upper - Right': (0xeb3c, 0x186dd0, False, 'in a cave with seven chests'), + 'Spiral Cave': (0xe9bf, 0x186c53, False, 'in Spiral Cave'), + 'Ether Tablet': (0x180016, 0x186dfb, False, 'at a monolith'), + 'Spectacle Rock': (0x180140, 0x186e0f, False, 'atop a rock'), + 'Tower of Hera - Basement Cage': (0x180162, 0x186dfa, False, 'in Tower of Hera'), + 'Tower of Hera - Map Chest': (0xe9ad, 0x186c41, False, 'in Tower of Hera'), + 'Tower of Hera - Big Key Chest': (0xe9e6, 0x186c7a, False, 'in Tower of Hera'), + 'Tower of Hera - Compass Chest': (0xe9fb, 0x186c8f, False, 'in Tower of Hera'), + 'Tower of Hera - Big Chest': (0xe9f8, 0x186c8c, False, 'in Tower of Hera'), + 'Tower of Hera - Boss': (0x180152, 0x186e00, False, 'with Moldorm'), + 'Pyramid': (0x180147, 0x186e16, False, 'on the Pyramid'), + 'Catfish': (0xee185, 0x186e21, False, 'with a catfish'), + 'Stumpy': (0x330c7, 0x186e2a, False, 'with tree boy'), + 'Digging Game': (0x180148, 0x186e17, False, 'underground'), + 'Bombos Tablet': (0x180017, 0x186dfc, False, 'at a monolith'), + 'Hype Cave - Top': (0xeb1e, 0x186db2, False, 'near a bat-like man'), + 'Hype Cave - Middle Right': (0xeb21, 0x186db5, False, 'near a bat-like man'), + 'Hype Cave - Middle Left': (0xeb24, 0x186db8, False, 'near a bat-like man'), + 'Hype Cave - Bottom': (0xeb27, 0x186dbb, False, 'near a bat-like man'), + 'Hype Cave - Generous Guy': (0x180011, 0x186e1b, False, 'with a bat-like man'), + 'Peg Cave': (0x180006, 0x186e0e, False, 'alone in a cave'), + 'Pyramid Fairy - Left': (0xe980, 0x186c14, False, 'near a fairy'), + 'Pyramid Fairy - Right': (0xe983, 0x186c17, False, 'near a fairy'), + 'Brewery': (0xe9ec, 0x186c80, False, 'alone in a home'), + 'C-Shaped House': (0xe9ef, 0x186c83, False, 'alone in a home'), + 'Chest Game': (0xeda8, 0x186e2b, False, 'as a prize'), + 'Bumper Cave Ledge': (0x180146, 0x186e15, False, 'on a ledge'), + 'Mire Shed - Left': (0xea73, 0x186d07, False, 'near sparks'), + 'Mire Shed - Right': (0xea76, 0x186d0a, False, 'near sparks'), + 'Superbunny Cave - Top': (0xea7c, 0x186d10, False, 'in a connection'), + 'Superbunny Cave - Bottom': (0xea7f, 0x186d13, False, 'in a connection'), + 'Spike Cave': (0xea8b, 0x186d1f, False, 'beyond spikes'), + 'Hookshot Cave - Top Right': (0xeb51, 0x186de5, False, 'across pits'), + 'Hookshot Cave - Top Left': (0xeb54, 0x186de8, False, 'across pits'), + 'Hookshot Cave - Bottom Right': (0xeb5a, 0x186dee, False, 'across pits'), + 'Hookshot Cave - Bottom Left': (0xeb57, 0x186deb, False, 'across pits'), + 'Floating Island': (0x180141, 0x186e10, False, 'on an island'), + 'Mimic Cave': (0xe9c5, 0x186c59, False, 'in a cave of mimicry'), + 'Swamp Palace - Entrance': (0xea9d, 0x186d31, False, 'in Swamp Palace'), + 'Swamp Palace - Map Chest': (0xe986, 0x186c1a, False, 'in Swamp Palace'), + 'Swamp Palace - Big Chest': (0xe989, 0x186c1d, False, 'in Swamp Palace'), + 'Swamp Palace - Compass Chest': (0xeaa0, 0x186d34, False, 'in Swamp Palace'), + 'Swamp Palace - Big Key Chest': (0xeaa6, 0x186d3a, False, 'in Swamp Palace'), + 'Swamp Palace - West Chest': (0xeaa3, 0x186d37, False, 'in Swamp Palace'), + 'Swamp Palace - Flooded Room - Left': (0xeaa9, 0x186d3d, False, 'in Swamp Palace'), + 'Swamp Palace - Flooded Room - Right': (0xeaac, 0x186d40, False, 'in Swamp Palace'), + 'Swamp Palace - Waterfall Room': (0xeaaf, 0x186d43, False, 'in Swamp Palace'), + 'Swamp Palace - Boss': (0x180154, 0x186e02, False, 'with Arrghus'), + "Thieves' Town - Big Key Chest": (0xea04, 0x186c98, False, "in Thieves Town"), + "Thieves' Town - Map Chest": (0xea01, 0x186c95, False, "in Thieves Town"), + "Thieves' Town - Compass Chest": (0xea07, 0x186c9b, False, "in Thieves Town"), + "Thieves' Town - Ambush Chest": (0xea0a, 0x186c9e, False, "in Thieves Town"), + "Thieves' Town - Attic": (0xea0d, 0x186ca1, False, "in Thieves Town"), + "Thieves' Town - Big Chest": (0xea10, 0x186ca4, False, "in Thieves Town"), + "Thieves' Town - Blind's Cell": (0xea13, 0x186ca7, False, "in Thieves Town"), + "Thieves' Town - Boss": (0x180156, 0x186e04, False, 'with Blind'), + 'Skull Woods - Compass Chest': (0xe992, 0x186c26, False, 'in Skull Woods'), + 'Skull Woods - Map Chest': (0xe99b, 0x186c2f, False, 'in Skull Woods'), + 'Skull Woods - Big Chest': (0xe998, 0x186c2c, False, 'in Skull Woods'), + 'Skull Woods - Pot Prison': (0xe9a1, 0x186c35, False, 'in Skull Woods'), + 'Skull Woods - Pinball Room': (0xe9c8, 0x186c5c, False, 'in Skull Woods'), + 'Skull Woods - Big Key Chest': (0xe99e, 0x186c32, False, 'in Skull Woods'), + 'Skull Woods - Bridge Room': (0xe9fe, 0x186c92, False, 'near Mothula'), + 'Skull Woods - Boss': (0x180155, 0x186e03, False, 'with Mothula'), + 'Ice Palace - Compass Chest': (0xe9d4, 0x186c68, False, 'in Ice Palace'), + 'Ice Palace - Freezor Chest': (0xe995, 0x186c29, False, 'in Ice Palace'), + 'Ice Palace - Big Chest': (0xe9aa, 0x186c3e, False, 'in Ice Palace'), + 'Ice Palace - Iced T Room': (0xe9e3, 0x186c77, False, 'in Ice Palace'), + 'Ice Palace - Spike Room': (0xe9e0, 0x186c74, False, 'in Ice Palace'), + 'Ice Palace - Big Key Chest': (0xe9a4, 0x186c38, False, 'in Ice Palace'), + 'Ice Palace - Map Chest': (0xe9dd, 0x186c71, False, 'in Ice Palace'), + 'Ice Palace - Boss': (0x180157, 0x186e05, False, 'with Kholdstare'), + 'Misery Mire - Big Chest': (0xea67, 0x186cfb, False, 'in Misery Mire'), + 'Misery Mire - Map Chest': (0xea6a, 0x186cfe, False, 'in Misery Mire'), + 'Misery Mire - Main Lobby': (0xea5e, 0x186cf2, False, 'in Misery Mire'), + 'Misery Mire - Bridge Chest': (0xea61, 0x186cf5, False, 'in Misery Mire'), + 'Misery Mire - Spike Chest': (0xe9da, 0x186c6e, False, 'in Misery Mire'), + 'Misery Mire - Compass Chest': (0xea64, 0x186cf8, False, 'in Misery Mire'), + 'Misery Mire - Big Key Chest': (0xea6d, 0x186d01, False, 'in Misery Mire'), + 'Misery Mire - Boss': (0x180158, 0x186e06, False, 'with Vitreous'), + 'Turtle Rock - Compass Chest': (0xea22, 0x186cb6, False, 'in Turtle Rock'), + 'Turtle Rock - Roller Room - Left': (0xea1c, 0x186cb0, False, 'in Turtle Rock'), + 'Turtle Rock - Roller Room - Right': (0xea1f, 0x186cb3, False, 'in Turtle Rock'), + 'Turtle Rock - Chain Chomps': (0xea16, 0x186caa, False, 'in Turtle Rock'), + 'Turtle Rock - Big Key Chest': (0xea25, 0x186cb9, False, 'in Turtle Rock'), + 'Turtle Rock - Big Chest': (0xea19, 0x186cad, False, 'in Turtle Rock'), + 'Turtle Rock - Crystaroller Room': (0xea34, 0x186cc8, False, 'in Turtle Rock'), + 'Turtle Rock - Eye Bridge - Bottom Left': (0xea31, 0x186cc5, False, 'in Turtle Rock'), + 'Turtle Rock - Eye Bridge - Bottom Right': (0xea2e, 0x186cc2, False, 'in Turtle Rock'), + 'Turtle Rock - Eye Bridge - Top Left': (0xea2b, 0x186cbf, False, 'in Turtle Rock'), + 'Turtle Rock - Eye Bridge - Top Right': (0xea28, 0x186cbc, False, 'in Turtle Rock'), + 'Turtle Rock - Boss': (0x180159, 0x186e07, False, 'with Trinexx'), + 'Palace of Darkness - Shooter Room': (0xea5b, 0x186cef, False, 'in Palace of Darkness'), + 'Palace of Darkness - The Arena - Bridge': (0xea3d, 0x186cd1, False, 'in Palace of Darkness'), + 'Palace of Darkness - Stalfos Basement': (0xea49, 0x186cdd, False, 'in Palace of Darkness'), + 'Palace of Darkness - Big Key Chest': (0xea37, 0x186ccb, False, 'in Palace of Darkness'), + 'Palace of Darkness - The Arena - Ledge': (0xea3a, 0x186cce, False, 'in Palace of Darkness'), + 'Palace of Darkness - Map Chest': (0xea52, 0x186ce6, False, 'in Palace of Darkness'), + 'Palace of Darkness - Compass Chest': (0xea43, 0x186cd7, False, 'in Palace of Darkness'), + 'Palace of Darkness - Dark Basement - Left': (0xea4c, 0x186ce0, False, 'in Palace of Darkness'), + 'Palace of Darkness - Dark Basement - Right': (0xea4f, 0x186ce3, False, 'in Palace of Darkness'), + 'Palace of Darkness - Dark Maze - Top': (0xea55, 0x186ce9, False, 'in Palace of Darkness'), + 'Palace of Darkness - Dark Maze - Bottom': (0xea58, 0x186cec, False, 'in Palace of Darkness'), + 'Palace of Darkness - Big Chest': (0xea40, 0x186cd4, False, 'in Palace of Darkness'), + 'Palace of Darkness - Harmless Hellway': (0xea46, 0x186cda, False, 'in Palace of Darkness'), + 'Palace of Darkness - Boss': (0x180153, 0x186e01, False, 'with Helmasaur King'), + "Ganons Tower - Bob's Torch": (0x180161, 0x186e23, False, "in Ganon's Tower"), + 'Ganons Tower - Hope Room - Left': (0xead9, 0x186d6d, False, "in Ganon's Tower"), + 'Ganons Tower - Hope Room - Right': (0xeadc, 0x186d70, False, "in Ganon's Tower"), + 'Ganons Tower - Tile Room': (0xeae2, 0x186d76, False, "in Ganon's Tower"), + 'Ganons Tower - Compass Room - Top Left': (0xeae5, 0x186d79, False, "in Ganon's Tower"), + 'Ganons Tower - Compass Room - Top Right': (0xeae8, 0x186d7c, False, "in Ganon's Tower"), + 'Ganons Tower - Compass Room - Bottom Left': (0xeaeb, 0x186d7f, False, "in Ganon's Tower"), + 'Ganons Tower - Compass Room - Bottom Right': (0xeaee, 0x186d82, False, "in Ganon's Tower"), + 'Ganons Tower - DMs Room - Top Left': (0xeab8, 0x186d4c, False, "in Ganon's Tower"), + 'Ganons Tower - DMs Room - Top Right': (0xeabb, 0x186d4f, False, "in Ganon's Tower"), + 'Ganons Tower - DMs Room - Bottom Left': (0xeabe, 0x186d52, False, "in Ganon's Tower"), + 'Ganons Tower - DMs Room - Bottom Right': (0xeac1, 0x186d55, False, "in Ganon's Tower"), + 'Ganons Tower - Map Chest': (0xead3, 0x186d67, False, "in Ganon's Tower"), + 'Ganons Tower - Firesnake Room': (0xead0, 0x186d64, False, "in Ganon's Tower"), + 'Ganons Tower - Randomizer Room - Top Left': (0xeac4, 0x186d58, False, "in Ganon's Tower"), + 'Ganons Tower - Randomizer Room - Top Right': (0xeac7, 0x186d5b, False, "in Ganon's Tower"), + 'Ganons Tower - Randomizer Room - Bottom Left': (0xeaca, 0x186d5e, False, "in Ganon's Tower"), + 'Ganons Tower - Randomizer Room - Bottom Right': (0xeacd, 0x186d61, False, "in Ganon's Tower"), + "Ganons Tower - Bob's Chest": (0xeadf, 0x186d73, False, "in Ganon's Tower"), + 'Ganons Tower - Big Chest': (0xead6, 0x186d6a, False, "in Ganon's Tower"), + 'Ganons Tower - Big Key Room - Left': (0xeaf4, 0x186d88, False, "in Ganon's Tower"), + 'Ganons Tower - Big Key Room - Right': (0xeaf7, 0x186d8b, False, "in Ganon's Tower"), + 'Ganons Tower - Big Key Chest': (0xeaf1, 0x186d85, False, "in Ganon's Tower"), + 'Ganons Tower - Mini Helmasaur Room - Left': (0xeafd, 0x186d91, False, "atop Ganon's Tower"), + 'Ganons Tower - Mini Helmasaur Room - Right': (0xeb00, 0x186d94, False, "atop Ganon's Tower"), + 'Ganons Tower - Pre-Moldorm Chest': (0xeb03, 0x186d97, False, "atop Ganon's Tower"), + 'Ganons Tower - Validation Chest': (0xeb06, 0x186d9a, False, "atop Ganon's Tower"), 'Ganon': (None, None, False, 'from me'), 'Agahnim 1': (None, None, False, 'from Ganon\'s wizardry form'), 'Agahnim 2': (None, None, False, 'from Ganon\'s wizardry form'), @@ -1468,7 +1616,6 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), 'Potion Shop - Middle': (None, None, False, 'for sale near potions'), 'Potion Shop - Right': (None, None, False, 'for sale near potions'), } - lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int} lookup_id_to_name.update(shop_table_by_location_id) lookup_name_to_id = {name: data[0] for name, data in location_table.items() if type(data[0]) == int} diff --git a/Rom.py b/Rom.py index 11bf35ce..ea11e06b 100644 --- a/Rom.py +++ b/Rom.py @@ -5,6 +5,8 @@ import json import hashlib import logging import os + +import Items import RaceRandom as random import struct import sys @@ -33,11 +35,14 @@ from InitialSram import InitialSram from source.classes.SFX import randomize_sfx from source.item.FillUtil import valid_pot_items -from source.dungeon.RoomList import Room0127 +from source.dungeon.EnemyList import EnemySprite, setup_enemy_dungeon_tables +from source.dungeon.RoomObject import DoorObject +from source.enemizer.Bossmizer import boss_writes +from source.enemizer.Enemizer import write_enemy_shuffle_settings JAP10HASH = '03a63945398191337e896e5771f77173' -RANDOMIZERBASEHASH = '7ba119d84e56617f8da16669f8b519c1' +RANDOMIZERBASEHASH = 'd63715be60a548e080dc84ae588307e5' class JsonRom(object): @@ -215,182 +220,6 @@ def read_rom(stream): has_smc_header = True return buffer, has_smc_header -def patch_enemizer(world, player, rom, local_rom, enemizercli, random_sprite_on_hit): - baserom_path = os.path.abspath(local_rom.file) - unheadered_path = None - if local_rom.has_smc_header: - headered_path = baserom_path - unheadered_path = baserom_path = os.path.abspath(output_path('unheadered_rom.sfc')) - with open(headered_path, 'rb') as headered: - with open(baserom_path, 'wb') as unheadered: - unheadered.write(headered.read()[0x200:]) - basepatch_path = os.path.abspath(local_path(os.path.join("data","base2current.json"))) - enemizer_basepatch_path = os.path.join(os.path.dirname(enemizercli), "enemizerBasePatch.json") - randopatch_path = os.path.abspath(output_path('enemizer_randopatch.json')) - options_path = os.path.abspath(output_path('enemizer_options.json')) - enemizer_output_path = os.path.abspath(output_path('enemizer_output.json')) - - # write options file for enemizer - options = { - 'RandomizeEnemies': world.enemy_shuffle[player] != 'none', - 'RandomizeEnemiesType': 3, - 'RandomizeBushEnemyChance': world.enemy_shuffle[player] in ['random', 'legacy'], - 'RandomizeEnemyHealthRange': world.enemy_health[player] != 'default', - 'RandomizeEnemyHealthType': {'default': 0, 'easy': 0, 'normal': 1, 'hard': 2, 'expert': 3}[world.enemy_health[player]], - 'OHKO': False, - 'RandomizeEnemyDamage': world.enemy_damage[player] != 'default', - 'AllowEnemyZeroDamage': True, - 'ShuffleEnemyDamageGroups': world.enemy_damage[player] != 'default', - 'EnemyDamageChaosMode': world.enemy_damage[player] == 'random', - 'EasyModeEscape': False, - 'EnemiesAbsorbable': False, - 'AbsorbableSpawnRate': 10, - 'AbsorbableTypes': { - 'FullMagic': True, 'SmallMagic': True, 'Bomb_1': True, 'BlueRupee': True, 'Heart': True, 'BigKey': True, 'Key': True, - 'Fairy': True, 'Arrow_10': True, 'Arrow_5': True, 'Bomb_8': True, 'Bomb_4': True, 'GreenRupee': True, 'RedRupee': True - }, - 'BossMadness': False, - 'RandomizeBosses': True, - 'RandomizeBossesType': 0, - 'RandomizeBossHealth': False, - 'RandomizeBossHealthMinAmount': 0, - 'RandomizeBossHealthMaxAmount': 300, - 'RandomizeBossDamage': False, - 'RandomizeBossDamageMinAmount': 0, - 'RandomizeBossDamageMaxAmount': 200, - 'RandomizeBossBehavior': False, - 'RandomizeDungeonPalettes': False, - 'SetBlackoutMode': False, - 'RandomizeOverworldPalettes': False, - 'RandomizeSpritePalettes': False, - 'SetAdvancedSpritePalettes': False, - 'PukeMode': False, - 'NegativeMode': False, - 'GrayscaleMode': False, - 'GenerateSpoilers': False, - 'RandomizeLinkSpritePalette': False, - 'RandomizePots': False, - 'ShuffleMusic': False, - 'BootlegMagic': True, - 'CustomBosses': False, - 'AndyMode': False, - 'HeartBeepSpeed': 0, - 'AlternateGfx': False, - 'ShieldGraphics': "shield_gfx/normal.gfx", - 'SwordGraphics': "sword_gfx/normal.gfx", - 'BeeMizer': False, - 'BeesLevel': 0, - 'RandomizeTileTrapPattern': world.enemy_shuffle[player] in ['random', 'legacy'], - 'RandomizeTileTrapFloorTile': False, - 'AllowKillableThief': bool(random.randint(0, 1)) if world.enemy_shuffle[player] == 'legacy' else world.enemy_shuffle[player] != 'none', - 'RandomizeSpriteOnHit': random_sprite_on_hit, - 'DebugMode': False, - 'DebugForceEnemy': False, - 'DebugForceEnemyId': 0, - 'DebugForceBoss': False, - 'DebugForceBossId': 0, - 'DebugOpenShutterDoors': False, - 'DebugForceEnemyDamageZero': False, - 'DebugShowRoomIdInRupeeCounter': False, - 'UseManualBosses': True, - 'ManualBosses': { - 'EasternPalace': world.get_dungeon("Eastern Palace", player).boss.enemizer_name, - 'DesertPalace': world.get_dungeon("Desert Palace", player).boss.enemizer_name, - 'TowerOfHera': world.get_dungeon("Tower of Hera", player).boss.enemizer_name, - 'AgahnimsTower': 'Agahnim', - 'PalaceOfDarkness': world.get_dungeon("Palace of Darkness", player).boss.enemizer_name, - 'SwampPalace': world.get_dungeon("Swamp Palace", player).boss.enemizer_name, - 'SkullWoods': world.get_dungeon("Skull Woods", player).boss.enemizer_name, - 'ThievesTown': world.get_dungeon("Thieves Town", player).boss.enemizer_name, - 'IcePalace': world.get_dungeon("Ice Palace", player).boss.enemizer_name, - 'MiseryMire': world.get_dungeon("Misery Mire", player).boss.enemizer_name, - 'TurtleRock': world.get_dungeon("Turtle Rock", player).boss.enemizer_name, - 'GanonsTower1': [x for x in world.dungeons if x.player == player and 'bottom' in x.bosses.keys()][0].bosses['bottom'].enemizer_name, - 'GanonsTower2': [x for x in world.dungeons if x.player == player and 'middle' in x.bosses.keys()][0].bosses['middle'].enemizer_name, - 'GanonsTower3': [x for x in world.dungeons if x.player == player and 'top' in x.bosses.keys()][0].bosses['top'].enemizer_name, - 'GanonsTower4': 'Agahnim2', - 'Ganon': 'Ganon', - } - } - - rom.write_to_file(randopatch_path) - - with open(options_path, 'w') as f: - json.dump(options, f) - - try: - subprocess.run([os.path.abspath(enemizercli), - '--rom', baserom_path, - '--seed', str(world.rom_seeds[player]), - '--base', basepatch_path, - '--randomizer', randopatch_path, - '--enemizer', options_path, - '--output', enemizer_output_path], - cwd=os.path.dirname(enemizercli), - check=True, - capture_output=True) - except subprocess.CalledProcessError as e: - from Main import EnemizerError - enemizerMsg = world.fish.translate("cli","cli","Enemizer returned exit code: ") + str(e.returncode) + "\n" - enemizerMsg += world.fish.translate("cli","cli","enemizer.nothing.applied") - logging.error(f'Enemizer error output: {e.stderr.decode("utf-8")}\n') - raise EnemizerError(enemizerMsg) - - with open(enemizer_basepatch_path, 'r') as f: - for patch in json.load(f): - rom.write_bytes(patch["address"], patch["patchData"]) - - with open(enemizer_output_path, 'r') as f: - for patch in json.load(f): - rom.write_bytes(patch["address"], patch["patchData"]) - - if world.get_dungeon("Thieves Town", player).boss.enemizer_name == "Blind": - rom.write_byte(0x04DE81, 0x6) # maiden spawn - # restore blind spawn code - necessary because the old enemizer clobbers this stuff - # this line could be commented out if ijwu's enemizer is used exclusively - # if keeping this line, note the jump to the dr_baserom's enemizer section - 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) - else: - rom.write_byte(0x04DE83, 0xB3) # maiden is now something else - - - if random_sprite_on_hit: - _populate_sprite_table() - sprites = list(_sprite_table.values()) - if sprites: - while len(sprites) < 32: - sprites.extend(sprites) - random.shuffle(sprites) - - for i, path in enumerate(sprites[:32]): - sprite = Sprite(path) - rom.write_bytes(0x300000 + (i * 0x8000), sprite.sprite) - rom.write_bytes(0x307000 + (i * 0x8000), sprite.palette) - rom.write_bytes(0x307078 + (i * 0x8000), sprite.glove_palette) - - if local_rom.has_smc_header: - try: - os.remove(unheadered_path) - except OSError: - pass - - try: - os.remove(randopatch_path) - except OSError: - pass - - try: - os.remove(options_path) - except OSError: - pass - - try: - os.remove(enemizer_output_path) - except OSError: - pass _sprite_table = {} def _populate_sprite_table(): @@ -585,7 +414,7 @@ def handle_native_dungeon(location, itemid): return itemid -def patch_rom(world, rom, player, team, enemized, is_mystery=False): +def patch_rom(world, rom, player, team, is_mystery=False): random.seed(world.rom_seeds[player]) # progressive bow silver arrow hint hack @@ -616,18 +445,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): location.pot.indicator = standing_item_flag location.pot.standing_item_code = code continue - elif location.type == LocationType.Drop: - if location.item.player != player: - code = 0xF9 - else: - code = 0xF8 - sprite_pointer = snes_to_pc(location.address) - rom.write_byte(sprite_pointer, handle_native_dungeon(location, itemid)) - if code == 0xF9: - rom.write_byte(sprite_pointer+1, location.item.player) - else: - rom.write_byte(sprite_pointer+1, 0) - rom.write_byte(sprite_pointer+2, code) + elif location.type == LocationType.Drop: # handled in the sprite table routine continue if location.address is None or (type(location.address) is int and location.address >= 0x400000): continue @@ -733,6 +551,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): if ((world.collection_rate[player] or world.goal[player] == 'completionist') and world.goal[player] not in ['triforcehunt', 'trinity', 'ganonhunt']): dr_flags |= DROptions.Debug + rom.write_byte(snes_to_pc(0x308039), 1) if world.doorShuffle[player] not in ['vanilla', 'basic'] and world.logic[player] != 'nologic'\ and world.mixed_travel[player] == 'prevent': # PoD Falling Bridge or Hammjump @@ -756,6 +575,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): dr_flags |= DROptions.Fix_EG if world.door_type_mode[player] in ['big', 'all', 'chaos']: dr_flags |= DROptions.BigKeyDoor_Shuffle + if world.dropshuffle[player] in ['underworld']: + dr_flags |= DROptions.EnemyDropIndicator my_locations = world.get_filled_locations(player) valid_locations = [l for l in my_locations if ((l.type == LocationType.Pot and not l.forced_item) @@ -765,7 +586,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): valid_loc_by_dungeon = valid_dungeon_locations(valid_locations) # fix hc big key problems (map and compass too) - if (world.doorShuffle[player] not in ['vanilla', 'basic'] or world.dropshuffle[player] + if (world.doorShuffle[player] not in ['vanilla', 'basic'] or world.dropshuffle[player] != 'none' or world.pottery[player] not in ['none', 'cave']): rom.write_byte(0x151f1, 2) rom.write_byte(0x15270, 2) @@ -799,17 +620,11 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): for name, layout in world.key_layout[player].items(): offset = compass_data[name][4]//2 if world.keyshuffle[player] == 'universal': - rom.write_byte(0x13f030+offset, layout.max_chests + layout.max_drops) + rom.write_byte(0x187010+offset, layout.max_chests + layout.max_drops) else: rom.write_byte(0x13f020+offset, layout.max_chests + layout.max_drops) # not currently used - rom.write_byte(0x13f030+offset, layout.max_chests) + rom.write_byte(0x187010+offset, layout.max_chests) builder = world.dungeon_layouts[player][name] - valid_cnt = len(valid_loc_by_dungeon[name]) - if valid_cnt > 256: - logging.getLogger('').warning(f'{name} exceeds 256 in locations ({valid_cnt})') - rom.write_byte(0x13f080+offset, valid_cnt % 10) - rom.write_byte(0x13f090+offset, valid_cnt // 10) - rom.write_byte(0x13f0a0+offset, valid_cnt) bk_status = 1 if builder.bk_required else 0 bk_status = 2 if builder.bk_provided else bk_status rom.write_byte(0x13f040+offset*2, bk_status) @@ -895,9 +710,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): credits_total = len(valid_locations) - if world.dropshuffle[player] or world.pottery[player] != 'none': + if world.dropshuffle[player] != 'none' or world.pottery[player] != 'none': rom.write_byte(0x142A50, 1) # StandingItemsOn - multiClientFlags = ((0x1 if world.dropshuffle[player] else 0) + multiClientFlags = ((0x1 if world.dropshuffle[player] != 'none' else 0) | (0x2 if world.shopsanity[player] else 0) | (0x4 if world.take_any[player] != 'none' else 0) | (0x8 if world.pottery[player] != 'none' else 0) @@ -905,13 +720,13 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x142A51, multiClientFlags) # StandingItemCounterMask rom.write_byte(0x142A55, ((0x1 if world.pottery[player] not in ['none', 'cave'] else 0) - | (0x2 if world.dropshuffle[player] else 0))) + | (0x2 if world.dropshuffle[player] != 'none' else 0))) if world.pottery[player] not in ['none', 'keys']: # Cuccos should not prevent kill rooms from opening rom.write_byte(snes_to_pc(0x0DB457), 0x40) rom.write_byte(snes_to_pc(0x28AA56), 0 if world.pottery[player] == 'none' else 1) - write_int16(rom, 0x187010, credits_total) # dynamic credits + write_int16(rom, 0x180196, credits_total) # dynamic credits if credits_total != 216: # collection rate address (hi): cr_address = 0x238055 @@ -921,12 +736,12 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): last_top, last_bot = credits_digit(credits_total % 10) if credits_total >= 1000: thousands_top, thousands_bot = credits_digit((credits_total // 1000) % 10) - rom.write_byte(cr_pc, 0xa2) # slash + rom.write_byte(cr_pc, 0xDB) # slash rom.write_byte(cr_pc+1, thousands_top) - rom.write_byte(cr_pc+0x1e, 0xc2) # slash + rom.write_byte(cr_pc+0x1e, 0xEE) # slash rom.write_byte(cr_pc+0x1f, thousands_bot) # modify stat config - stat_address = 0x23B969 + stat_address = 0x2397B2 # 0x23B969 - old stat_pc = snes_to_pc(stat_address) rom.write_byte(stat_pc, 0xa9) # change to pos 21 (from b1) rom.write_byte(stat_pc+2, 0xc0) # change to 12 bits (from a0) @@ -987,8 +802,6 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): # set light cones rom.write_byte(0x180038, 0x01 if world.sewer_light_cone[player] else 0x00) - rom.write_byte(0x180039, 0x01 if world.light_world_light_cone else 0x00) - rom.write_byte(0x18003A, 0x01 if world.dark_world_light_cone else 0x00) GREEN_TWENTY_RUPEES = 0x47 TRIFORCE_PIECE = ItemFactory('Triforce Piece', player).code @@ -1283,11 +1096,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_bytes(0x180213, [0x00, 0x01]) # Not a Tournament Seed gametype = 0x04 # item - if (world.shuffle[player] != 'vanilla' or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] - or world.pottery[player] != 'none'): + if (world.shuffle[player] != 'vanilla' or world.doorShuffle[player] != 'vanilla' + or world.dropshuffle[player] != 'none' or world.pottery[player] != 'none'): gametype |= 0x02 # entrance/door - if enemized: - gametype |= 0x01 # enemizer rom.write_byte(0x180211, gametype) # Game type # assorted fixes @@ -1309,7 +1120,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.initial_sram.pre_open_ganons_tower() 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 + rom.write_byte(0x180086, 0x00 if world.aga_randomness[player] else 0x01) # set blue ball and ganon warp randomness rom.write_byte(0x1800A0, 0x01) # return to light world on s+q without mirror rom.write_byte(0x1800A1, 0x01) # enable overworld screen transition draining for water level inside swamp rom.write_byte(0x180174, 0x01 if world.fix_fake_world[player] else 0x00) @@ -1319,8 +1130,6 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): if world.pseudoboots[player]: rom.write_byte(0x18008E, 0x01) rom.initial_sram.set_starting_equipment(world, player) - rom.write_byte(0x180034, 10 if not world.bombbag[player] else 0) # starting max bombs - rom.write_byte(0x180035, 30) # starting max arrows rom.write_byte(0x18004A, 0x00 if world.mode[player] != 'inverted' else 0x01) # Inverted mode rom.write_byte(0x18005D, 0x00) # Hammer always breaks barrier @@ -1336,26 +1145,28 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): (0x04 if 'magic' in world.escape_assist[player] else 0x00))) # Escape assist if world.goal[player] in ['pedestal', 'triforcehunt']: - rom.write_byte(0x18003E, 0x01) # make ganon invincible + rom.write_byte(0x1801A8, 0x01) # make ganon invincible elif world.goal[player] in ['dungeons']: - rom.write_byte(0x18003E, 0x02) # make ganon invincible until all dungeons are beat + rom.write_byte(0x1801A8, 0x02) # make ganon invincible until all dungeons are beat elif world.goal[player] in ['crystals', 'trinity']: - rom.write_byte(0x18003E, 0x04) # make ganon invincible until all crystals + rom.write_byte(0x1801A8, 0x04) # make ganon invincible until all crystals elif world.goal[player] in ['ganonhunt']: - rom.write_byte(0x18003E, 0x05) # make ganon invincible until all triforce pieces collected + rom.write_byte(0x1801A8, 0x05) # make ganon invincible until all triforce pieces collected elif world.goal[player] in ['completionist']: - rom.write_byte(0x18003E, 0x0a) # make ganon invincible until everything is collected + rom.write_byte(0x1801A8, 0x0B) # make ganon invincible until everything is collected else: - rom.write_byte(0x18003E, 0x03) # make ganon invincible until all crystals and aga 2 are collected + rom.write_byte(0x1801A8, 0x03) # make ganon invincible until all crystals and aga 2 are collected - rom.write_byte(0x18005E, world.crystals_needed_for_gt[player]) - rom.write_byte(0x18005F, world.crystals_needed_for_ganon[player]) + rom.write_byte(0x18019A, world.crystals_needed_for_gt[player]) + rom.write_byte(0x1801A6, world.crystals_needed_for_ganon[player]) + rom.write_byte(0x1801A2, 0x00) # ped requirement is vanilla, set to 0x1 for special requirements # block HC upstairs doors in rain state in standard mode - prevent_rain = world.mode[player] == "standard" and world.shuffle[player] != 'vanilla' + prevent_rain = world.mode[player] == 'standard' and world.shuffle[player] != 'vanilla' and world.logic[player] != 'nologic' rom.write_byte(0x18008A, 0x01 if prevent_rain else 0x00) # block sanc door in rain state and the dungeon is not vanilla - rom.write_byte(0x13f0fa, 0x01 if world.mode[player] == "standard" and world.doorShuffle[player] != 'vanilla' else 0x00) + block_sanc = world.mode[player] == 'standard' and world.doorShuffle[player] != 'vanilla' and world.logic[player] != 'nologic' + rom.write_byte(0x13f0fa, 0x01 if block_sanc else 0x00) if prevent_rain: portals = [world.get_portal('Hyrule Castle East', player), world.get_portal('Hyrule Castle West', player)] @@ -1388,7 +1199,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x18003C, 0x00) elif world.dungeon_counters[player] == 'on': compass_mode = 0x02 # always on - elif (world.compassshuffle[player] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] + elif (world.compassshuffle[player] or world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none' or world.dungeon_counters[player] == 'pickup' or world.pottery[player] not in ['none', 'cave']): compass_mode = 0x01 # show on pickup if world.shuffle[player] != 'vanilla' and world.overworld_map[player] != 'default': @@ -1434,17 +1245,19 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): # Bitfield - enable free items to show up in menu # - # ----dcba + # ---edcba + # e - Bosses # d - Compass # c - Map # b - Big Key # a - Small Key # - enable_menu_map_check = world.overworld_map[player] != 'default' and world.shuffle[player] != 'none' + enable_menu_map_check = world.overworld_map[player] != 'default' and world.shuffle[player] != 'vanilla' rom.write_byte(0x180045, ((0x01 if world.keyshuffle[player] == 'wild' else 0x00) | (0x02 if world.bigkeyshuffle[player] else 0x00) | (0x04 if world.mapshuffle[player] or enable_menu_map_check else 0x00) - | (0x08 if world.compassshuffle[player] else 0x00))) # free roaming items in menu + | (0x08 if world.compassshuffle[player] else 0x00) # free roaming items in menu + | (0x10 if world.logic[player] == 'nologic' else 0))) # boss icon # Map reveals reveal_bytes = { @@ -1476,9 +1289,9 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_byte(0x180175, 0x01 if world.bow_mode[player].startswith('retro') else 0x00) # rupee bow rom.write_byte(0x180176, 0x0A if world.bow_mode[player].startswith('retro') else 0x00) # wood arrow cost rom.write_byte(0x180178, 0x32 if world.bow_mode[player].startswith('retro') else 0x00) # silver arrow cost - rom.write_byte(0x301FC, 0xDA if world.bow_mode[player].startswith('retro') else 0xE1) # rupees replace arrows under pots - if enemized: - rom.write_byte(0x1B152e, 0xDA if world.bow_mode[player].startswith('retro') else 0xE1) + # rupees replace arrows under pots for original and enemizer code + rom.write_byte(0x301FC, 0xDA if world.bow_mode[player].startswith('retro') else 0xE1) + rom.write_byte(snes_to_pc(0x36837D), 0xDA if world.bow_mode[player].startswith('retro') else 0xE1) rom.write_byte(0x30052, 0xDB if world.bow_mode[player].startswith('retro') else 0xE2) # replace arrows in fish prize from bottle merchant rom.write_bytes(0xECB4E, [0xA9, 0x00, 0xEA, 0xEA] if world.bow_mode[player].startswith('retro') else [0xAF, 0x77, 0xF3, 0x7E]) # Thief steals rupees instead of arrows rom.write_bytes(0xF0D96, [0xA9, 0x00, 0xEA, 0xEA] if world.bow_mode[player].startswith('retro') else [0xAF, 0x77, 0xF3, 0x7E]) # Pikit steals rupees instead of arrows @@ -1489,7 +1302,9 @@ 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] in ['owglitches', 'nologic']) else 0x00) + glitches_enabled = world.logic[player] in ['owglitches', 'hybridglitches', 'nologic'] + rom.write_byte(0x180358, 0x01 if glitches_enabled else 0x00) + rom.write_byte(0x18008B, 0x01 if glitches_enabled else 0x00) # remove shield from uncle rom.write_bytes(0x6D253, [0x00, 0x00, 0xf6, 0xff, 0x00, 0x0E]) @@ -1547,7 +1362,7 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): rom.write_bytes(0x02F539, [0xEA, 0xEA, 0xEA, 0xEA, 0xEA] if world.powder_patch_required[player] else [0xAD, 0xBF, 0x0A, 0xF0, 0x4F]) # allow smith into multi-entrance caves in appropriate shuffles - if world.shuffle[player] in ['restricted', 'full', 'lite', 'lean', 'crossed', 'insanity'] or (world.shuffle[player] == 'simple' and world.mode[player] == 'inverted'): + if world.shuffle[player] in ['restricted', 'full', 'lite', 'lean', 'swapped', 'crossed', 'insanity'] or (world.shuffle[player] == 'simple' and world.mode[player] == 'inverted'): rom.write_byte(0x18004C, 0x01) # set correct flag for hera basement item @@ -1558,10 +1373,11 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): if item_dungeon == 'Escape': item_dungeon = 'Hyrule Castle' is_small_key_this_dungeon = hera_basement.parent_region.dungeon.name == item_dungeon + # hera small key is 11th in list, 10th sprite because of overlord if is_small_key_this_dungeon: - rom.write_byte(0x4E3BB, 0xE4) + world.data_tables[player].uw_enemy_table.room_map[0x87][11].kind = EnemySprite.SmallKey else: - rom.write_byte(0x4E3BB, 0xEB) + world.data_tables[player].uw_enemy_table.room_map[0x87][11].kind = EnemySprite.HeartPiece # fix trock doors for reverse entrances if world.fix_trock_doors[player]: @@ -1569,27 +1385,32 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): world.get_room(0x23, player).change(0, DoorKind.CaveEntrance) if world.get_door('TR Eye Bridge SW', player).entranceFlag: world.get_room(0xd5, player).change(0, DoorKind.CaveEntrance) - # do this unconditionally - gets overwritten by RoomData in doorShufflemodes - rom.write_byte(0xFED31, 0x0E) # preopen bombable exit - rom.write_byte(0xFEE41, 0x0E) # preopen bombable exit + # do this conditionally - don't mess with doors + if world.doorShuffle[player] == 'vanilla': + rom.initial_sram.pre_open_tr_bomb_doors() # preopen bombable exits - if (world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] + if world.boss_shuffle[player] != 'none' or world.doorShuffle[player] != 'vanilla': + rom.write_byte(snes_to_pc(0x30835A), 1) # fix Prize On The Eyes + + if world.boss_shuffle[player] != 'none': + boss_writes(world, player, rom) + write_enemy_shuffle_settings(world, player, rom) + + if (world.doorShuffle[player] != 'vanilla' or world.dropshuffle[player] != 'none' or world.pottery[player] != 'none'): for room in world.rooms: if room.player == player and room.modified: - rom.write_bytes(room.address(), room.rom_data()) + if room.index in world.data_tables[player].room_list: + t = [DoorObject(x[0], x[1]) for x in room.doorList] + world.data_tables[player].room_list[room.index].doors = t + else: + rom.write_bytes(room.address(), room.rom_data()) - if world.pottery[player] not in ['none']: - rom.write_bytes(snes_to_pc(0x1F8375), int32_as_bytes(0x2B8000)) - # make hammer pegs use different tiles - Room0127.write_to_rom(snes_to_pc(0x2B8000), rom) - - if world.pot_contents[player]: - colorize_pots = (world.pottery[player] != 'vanilla' + if world.data_tables[player]: + colorize_pots = (world.pottery[player] != 'vanilla', 'lottery' and (world.colorizepots[player] or world.pottery[player] in ['reduced', 'clustered'])) - if world.pot_contents[player].size() > 0x2800: - raise Exception('Pot table is too big for current area') - world.pot_contents[player].write_pot_data_to_rom(rom, colorize_pots) + setup_enemy_dungeon_tables(world, player) + world.data_tables[player].write_to_rom(rom, colorize_pots, world.enemy_shuffle[player] == 'random') write_enemizer_tweaks(rom, world, player) write_strings(rom, world, player, team) @@ -1597,14 +1418,13 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): # write initial sram rom.write_initial_sram() - rom.write_byte(0x18636C, 1 if world.remote_items[player] else 0) + rom.write_byte(0x187E30, 1 if world.remote_items[player] else 0) # set rom name # 21 bytes 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'DR{__version__.split("-")[0].replace(".","")[0:3]}_{team+1}_{player}_{seedstring}\0', 'utf8')[:21] rom.name.extend([0] * (21 - len(rom.name))) rom.write_bytes(0x7FC0, rom.name) @@ -1657,7 +1477,7 @@ def write_custom_shops(rom, world, player): if item is None: break if world.shopsanity[player] or shop.type == ShopType.TakeAny: - rom.write_byte(0x186560 + shop.sram_address + index, 1) + rom.write_byte(0x186E40 + shop.sram_address + index, 1) if world.shopsanity[player] and shop.region.name in shop_to_location_table: loc_item = world.get_location(shop_to_location_table[shop.region.name][index], player).item elif world.shopsanity[player] and shop.region.name in retro_shops: @@ -1666,8 +1486,8 @@ def write_custom_shops(rom, world, player): loc_item = ItemFactory(item['item'], player) if (not world.shopsanity[player] and shop.region.name == 'Capacity Upgrade' and world.difficulty[player] != 'normal'): - # really should be 5A instead of B0 -- surprise!!! - item_id, price, replace, replace_price, item_max = 0xB0, [0, 0], 0xFF, [0, 0], 1 + # it's a BeeTrap -- surprise!!! + item_id, price, replace, replace_price, item_max = Items.item_table['Bee Trap'][3], [0, 0], 0xFF, [0, 0], 1 else: item_id = loc_item.code price = int16_as_bytes(item['price']) @@ -1689,6 +1509,7 @@ def write_enemizer_tweaks(rom, world, player): rom.write_byte(snes_to_pc(0x1DF6D8), 0) # lets enemies walk on water instead of clipping into infinity? rom.write_byte(snes_to_pc(0x0DB6B3), 0x82) # hovers don't need water necessarily? + def hud_format_text(text): output = bytes() for char in text.lower(): @@ -1759,17 +1580,7 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr # set heart color if color == 'random': color = random.choice(['red', 'blue', 'green', 'yellow']) - rom.write_byte(0x6FA1E, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color]) - rom.write_byte(0x6FA20, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color]) - rom.write_byte(0x6FA22, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color]) - rom.write_byte(0x6FA24, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color]) - rom.write_byte(0x6FA26, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color]) - rom.write_byte(0x6FA28, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color]) - rom.write_byte(0x6FA2A, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color]) - rom.write_byte(0x6FA2C, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color]) - rom.write_byte(0x6FA2E, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color]) - rom.write_byte(0x6FA30, {'red': 0x24, 'blue': 0x2C, 'green': 0x3C, 'yellow': 0x28}[color]) - rom.write_byte(0x65561, {'red': 0x05, 'blue': 0x0D, 'green': 0x19, 'yellow': 0x09}[color]) + rom.write_byte(0x187020, {'red': 0, 'blue': 1, 'green': 2, 'yellow': 3}[color]) # write link sprite if required if sprite is not None: @@ -1789,8 +1600,8 @@ def apply_rom_settings(rom, beep, color, quickswap, fastmenu, disable_music, spr "J": (0x66, 0x8C), "K": (0x67, 0x8D), "L": (0x68, 0x8E), "M": (0x69, 0x8F), "N": (0x6A, 0x90), "O": (0x6B, 0x91), "P": (0x6C, 0x92), "Q": (0x6D, 0x93), "R": (0x6E, 0x94), "S": (0x6F, 0x95), "T": (0x70, 0x96), "U": (0x71, 0x97), "V": (0x72, 0x98), "W": (0x73, 0x99), "X": (0x74, 0x9A), - "Y": (0x75, 0x9B), "Z": (0x76, 0x9C), "'": (0x77, 0x9d), ".": (0xA0, 0xC0), "/": (0xA2, 0xC2), - ":": (0xA3, 0xC3), "_": (0xA6, 0xC6)} + "Y": (0x75, 0x9B), "Z": (0x76, 0x9C), "'": (0xD9, 0xEC), ".": (0xDC, 0xEF), "/": (0xDB, 0xEE), + ":": (0xDD, 0xF0), "_": (0xDE, 0xF1)} return char_map[char] if char in char_map else (0x9F, 0x9F) character_bytes = map(convert_char_to_credits, padded_author) @@ -2018,6 +1829,16 @@ def write_string_to_rom(rom, target, string): def write_strings(rom, world, player, team): tt = TextTable() tt.removeUnwantedText() + if world.shuffle[player] != 'vanilla': + tt['houlihan_room'] = CompressedTextMapper.convert( + " Crosskeys\n" + " Tournament\n" + " Winners\n{HARP}\n" + " ~~~2022~~~\n Schulzer\n\n" + " ~~~2021~~~\n Goomba\n\n" + " ~~~2020~~~\n Linlinlin\n\n" + " ~~~2019~~~\n Kohrek\n" + ) # Let's keep this guy's text accurate to the shuffle setting. if world.shuffle[player] in ['vanilla', 'dungeonsfull', 'dungeonssimple']: @@ -2068,7 +1889,7 @@ def write_strings(rom, world, player, team): break # Now we write inconvenient locations for most shuffles and finish taking care of the less chaotic ones. entrances_to_hint.update(InconvenientOtherEntrances) - if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: + if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'lite', 'lean', 'swapped']: hint_count = 0 elif world.shuffle[player] in ['simple', 'restricted']: hint_count = 2 @@ -2118,7 +1939,7 @@ def write_strings(rom, world, player, team): entrances_to_hint.update({'Inverted Pyramid Entrance': 'The extra castle passage'}) else: entrances_to_hint.update({'Pyramid Entrance': 'The pyramid ledge'}) - hint_count = 4 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 0 + hint_count = 4 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'swapped'] else 0 hint_count -= 2 if world.shuffle[player] not in ['simple', 'restricted'] else 0 for entrance in all_entrances: if entrance.name in entrances_to_hint: @@ -2137,7 +1958,7 @@ def write_strings(rom, world, player, team): if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: locations_to_hint.extend(InconvenientVanillaLocations) random.shuffle(locations_to_hint) - hint_count = 3 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 5 + hint_count = 3 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'swapped'] else 5 hint_count -= 2 if world.doorShuffle[player] not in ['vanilla', 'basic'] else 0 del locations_to_hint[hint_count:] for location in locations_to_hint: @@ -2200,7 +2021,7 @@ def write_strings(rom, world, player, team): if world.bigkeyshuffle[player]: items_to_hint.extend(BigKeys) random.shuffle(items_to_hint) - hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull'] else 8 + hint_count = 5 if world.shuffle[player] not in ['vanilla', 'dungeonssimple', 'dungeonsfull', 'swapped'] else 8 hint_count += 2 if world.doorShuffle[player] not in ['vanilla', 'basic'] else 0 while hint_count > 0 and len(items_to_hint) > 0: this_item = items_to_hint.pop(0) @@ -2625,7 +2446,7 @@ def set_inverted_mode(world, player, rom): 0x190F, 0x9D04, 0x9D04]) write_int16s(rom, snes_to_pc(0x1bb810), [0x00BE, 0x00C0, 0x013E]) write_int16s(rom, snes_to_pc(0x1bb836), [0x001B, 0x001B, 0x001B]) - write_int16(rom, snes_to_pc(0x308300), 0x0140) # new pyramid hole entrance + write_int16(rom, snes_to_pc(0x308300), 0x0140) # new pyramid hole entrance write_int16(rom, snes_to_pc(0x308320), 0x001B) if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: rom.write_byte(snes_to_pc(0x308340), 0x7B) @@ -2666,7 +2487,8 @@ def set_inverted_mode(world, player, rom): write_int16(rom, 0xDBA71 + 2 * 0x35, 0x06A4) if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: rom.write_byte(0xDBB73 + 0x35, 0x36) - rom.write_byte(snes_to_pc(0x09D436), 0xF3) # remove castle gate warp + # rom.write_byte(snes_to_pc(0x09D436), 0xF3) # remove castle gate warp + del world.data_tables[player].ow_enemy_table[0xab][5] if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull']: write_int16(rom, 0x15AEE + 2 * 0x37, 0x0010) # pyramid exit to new hc area rom.write_byte(0x15B8C + 0x37, 0x1B) @@ -2716,9 +2538,11 @@ def update_compasses(rom, dungeon_locations, world, player): for name, builder in layouts.items(): dungeon_id = compass_data[name][4] dungeon_count = len(dungeon_locations[name]) - if dungeon_count > 255: - logging.getLogger('').warning(f'{name} has more locations than 255. Need 16-bit compass counts') - rom.write_byte(0x187000 + dungeon_id//2, dungeon_count % 256) + rom.write_bytes(0x187040 + dungeon_id, int16_as_bytes(dungeon_count)) + # total tiles + rom.write_bytes(0x187060 + dungeon_id, int16_as_bytes(((dungeon_count // 100) % 10) + 0x2490)) + rom.write_bytes(0x187080 + dungeon_id, int16_as_bytes(((dungeon_count // 10) % 10) + 0x2490)) + rom.write_bytes(0x1870A0 + dungeon_id, int16_as_bytes((dungeon_count % 10) + 0x2490)) if builder.bk_provided: if provided_dungeon: logging.getLogger('').warning('Multiple dungeons have forced BKs! Compass code might need updating?') @@ -2840,12 +2664,12 @@ OtherEntrances = {'Lake Hylia Fairy': 'A cave NE of Lake Hylia', 'Dark Lake Hylia Fairy': 'The cave NE dark Lake Hylia', 'Dark Death Mountain Fairy': 'The SW cave on dark DM', 'East Dark World Hint': 'The dark cave near the eastmost portal', - 'Dark Desert Hint': 'The cave east of the mire', + 'Mire Hint': 'The cave east of the mire', 'Palace of Darkness Hint': 'The building south of Kiki', 'Dark Lake Hylia Ledge Spike Cave': 'The rock SE dark Lake Hylia', 'Archery Game': 'The old archery game', 'Dark Lake Hylia Ledge Hint': 'The open cave SE dark Lake Hylia', - 'Dark Desert Fairy': 'The eastern hut in the mire', + 'Mire Fairy': 'The eastern hut in the mire', 'Dark Lake Hylia Ledge Fairy': 'The sealed cave SE dark Lake Hylia', 'Fortune Teller (Dark)': 'The building NE the Village of Outcasts', 'Dark Sanctuary Hint': 'The dark sanctuary cave' diff --git a/Rules.py b/Rules.py index 942eef33..41705faf 100644 --- a/Rules.py +++ b/Rules.py @@ -8,6 +8,12 @@ from BaseClasses import PotFlags from Dungeons import dungeon_table from RoomData import DoorKind from OverworldGlitchRules import overworld_glitches_rules +from UnderworldGlitchRules import underworld_glitches_rules + +from source.logic.Rule import RuleFactory +from source.dungeon.EnemyList import EnemySprite, Sprite +from source.enemizer.EnemyLogic import special_rules_check, special_rules_for_region, defeat_rule_single +from source.enemizer.EnemyLogic import defeat_rule_multiple, and_rule as and_rule_new, or_rule as or_rule_new def set_rules(world, player): @@ -25,6 +31,22 @@ def set_rules(world, player): if world.swords[player] == 'swordless': swordless_rules(world, player) + if world.logic[player] == 'noglitches': + no_glitches_rules(world, player) + elif world.logic[player] == 'minorglitches': + logging.getLogger('').info('Minor Glitches may be buggy still. No guarantee for proper logic checks.') + no_glitches_rules(world, player) + fake_flipper_rules(world, player) + elif world.logic[player] in ['owglitches', 'hybridglitches']: + logging.getLogger('').info('There is a chance OWG has bugged edge case rulesets, especially in inverted. Definitely file a report on GitHub if you see anything strange.') + # Initially setting no_glitches_rules to set the baseline rules for some + # entrances. The overworld_glitches_rules set is primarily additive. + no_glitches_rules(world, player) + fake_flipper_rules(world, player) + overworld_glitches_rules(world, player) + else: + raise NotImplementedError('Not implemented yet') + ow_bunny_rules(world, player) if world.mode[player] == 'standard': @@ -34,22 +56,8 @@ def set_rules(world, player): bomb_rules(world, player) pot_rules(world, player) - - if world.logic[player] == 'noglitches': - no_glitches_rules(world, player) - elif world.logic[player] == 'minorglitches': - logging.getLogger('').info('Minor Glitches may be buggy still. No guarantee for proper logic checks.') - no_glitches_rules(world, player) - fake_flipper_rules(world, player) - elif world.logic[player] == 'owglitches': - logging.getLogger('').info('There is a chance OWG has bugged edge case rulesets, especially in inverted. Definitely file a report on GitHub if you see anything strange.') - # Initially setting no_glitches_rules to set the baseline rules for some - # entrances. The overworld_glitches_rules set is primarily additive. - no_glitches_rules(world, player) - fake_flipper_rules(world, player) - overworld_glitches_rules(world, player) - else: - raise NotImplementedError('Not implemented yet') + drop_rules(world, player) + challenge_room_rules(world, player) if world.goal[player] == 'dungeons': # require all dungeons to beat ganon @@ -66,7 +74,7 @@ def set_rules(world, player): if world.mode[player] != 'inverted': set_big_bomb_rules(world, player) - if world.logic[player] == 'owglitches' and world.shuffle[player] != 'insanity': + if world.logic[player] in ['owglitches', 'hybridglitches'] and world.shuffle[player] != 'insanity': path_to_courtyard = mirrorless_path_to_castle_courtyard(world, player) add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.world.get_entrance('Dark Death Mountain Offset Mirror', player).can_reach(state) and all(rule(state) for rule in path_to_courtyard), 'or') else: @@ -76,9 +84,16 @@ def set_rules(world, player): if not world.swamp_patch_required[player]: add_rule(world.get_entrance('Swamp Lobby Moat', player), lambda state: state.has_Mirror(player)) + if world.logic[player] in ['owglitches', 'hybridglitches']: + overworld_glitches_rules(world, player) + set_bunny_rules(world, player, world.mode[player] == 'inverted') - if world.mode[player] != 'inverted' and world.logic[player] == 'owglitches': + # These rules go here because the overwrite/add to some of the above rules + if world.logic[player] == 'hybridglitches': + underworld_glitches_rules(world, player) + + if world.mode[player] != 'inverted' and world.logic[player] in ['owglitches', 'hybridglitches']: add_rule(world.get_entrance('Ganons Tower', player), lambda state: state.world.get_entrance('Ganons Tower Ascent', player).can_reach(state), 'or') @@ -115,6 +130,14 @@ def set_always_allow(spot, rule): spot.always_allow = rule +def add_rule_new(spot, rule, combine='and'): + if combine == 'and': + spot.verbose_rule = and_rule_new(*[spot.verbose_rule, rule]) + else: + spot.verbose_rule = or_rule_new(*[spot.verbose_rule, rule]) + add_rule(spot, rule.rule_lambda, combine) + + def add_rule(spot, rule, combine='and'): old_rule = spot.access_rule if combine == 'or': @@ -173,6 +196,10 @@ def global_rules(world, player): for exit in world.get_region('Menu', player).exits: exit.hide_path = True + # s&q regions. link's house entrance is set to true so the filler knows the chest inside can always be reached + set_rule(world.get_entrance('Old Man S&Q', player), lambda state: state.can_reach('Old Man', 'Location', player)) + set_rule(world.get_entrance('Other World S&Q', player), lambda state: state.has_Mirror(player) and state.has('Beat Agahnim 1', player)) + # flute rules set_rule(world.get_entrance('Flute Spot 1', player), lambda state: state.can_flute(player)) set_rule(world.get_entrance('Flute Spot 2', player), lambda state: state.can_flute(player)) @@ -183,31 +210,26 @@ def global_rules(world, player): set_rule(world.get_entrance('Flute Spot 7', player), lambda state: state.can_flute(player)) set_rule(world.get_entrance('Flute Spot 8', player), lambda state: state.can_flute(player)) - # s&q regions. link's house entrance is set to true so the filler knows the chest inside can always be reached - set_rule(world.get_entrance('Old Man S&Q', player), lambda state: state.can_reach('Old Man', 'Location', player)) - set_rule(world.get_entrance('Other World S&Q', player), lambda state: state.has_Mirror(player) and state.has('Beat Agahnim 1', player)) - # overworld location rules set_rule(world.get_location('Master Sword Pedestal', player), lambda state: state.has('Red Pendant', player) and state.has('Blue Pendant', player) and state.has('Green Pendant', player)) - set_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player)) - set_rule(world.get_location('Flute Spot', player), lambda state: state.has('Shovel', player)) set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player)) + set_rule(world.get_location('Zora\'s Ledge', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_location('Missing Smith', player), lambda state: state.has('Get Frog', player) and state.can_reach('Blacksmiths Hut', 'Region', player)) # Can't S&Q with smith + set_rule(world.get_location('Flute Spot', player), lambda state: state.has('Shovel', player)) set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has_beam_sword(player)) - - set_rule(world.get_location('Missing Smith', player), lambda state: state.has('Get Frog', player) and state.can_reach('Blacksmiths Hut', 'Region', player)) # Can't S&Q with smith - set_rule(world.get_location('Dark Blacksmith Ruins', player), lambda state: state.has('Return Smith', player)) set_rule(world.get_location('Purple Chest', player), lambda state: state.has('Pick Up Purple Chest', player)) # Can S&Q with chest + set_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player)) + set_rule(world.get_location('Dark Blacksmith Ruins', player), lambda state: state.has('Return Smith', player)) - # underworld rules + # underworld location rules + set_rule(world.get_entrance('Old Man Cave Exit (West)', player), lambda state: False) # drop cannot be climbed up set_rule(world.get_location('Mimic Cave', player), lambda state: state.has('Hammer', player)) - set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: state.has_Mirror(player)) # can erase block - overridden in noglitches set_rule(world.get_location('Potion Shop', player), lambda state: state.has('Mushroom', player) and state.can_reach('Potion Shop Area', 'Region', player)) set_rule(world.get_location('Sick Kid', player), lambda state: state.has_bottle(player)) - set_rule(world.get_location('Magic Bat', player), lambda state: state.has('Magic Powder', player)) - set_rule(world.get_location('Blacksmith', player), lambda state: state.has('Return Smith', player)) - set_rule(world.get_location('Library', player), lambda state: state.has_Boots(player)) set_rule(world.get_location('Sahasrahla', player), lambda state: state.has('Green Pendant', player)) + set_rule(world.get_location('Blacksmith', player), lambda state: state.has('Return Smith', player)) + set_rule(world.get_location('Magic Bat', player), lambda state: state.has('Magic Powder', player)) + set_rule(world.get_location('Library', player), lambda state: state.has_Boots(player)) set_rule(world.get_location('Spike Cave', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and ((state.has('Cape', player) and state.can_extend_magic(player, 16, True)) or @@ -215,6 +237,9 @@ def global_rules(world, player): (state.can_extend_magic(player, 12, True) or (state.world.can_take_damage and (state.has_Boots(player) or state.has_hearts(player, 4)))))) ) + + # underworld rules + set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: state.has_Mirror(player)) # can erase block - overridden in noglitches set_rule(world.get_entrance('Hookshot Cave Bonk Path', player), lambda state: state.has('Hookshot', player) or state.has('Pegasus Boots', player)) set_rule(world.get_entrance('Hookshot Cave Hook Path', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('Bumper Cave Bottom to Top', player), lambda state: state.has('Cape', player)) @@ -225,37 +250,93 @@ def global_rules(world, player): set_rule(world.get_entrance('DM Hammer Bridge (East)', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('DM Broken Bridge (West)', player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('DM Broken Bridge (East)', player), lambda state: state.has('Hookshot', player)) - set_rule(world.get_entrance('Fairy Ascension Rocks', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Death Mountain Entrance Rock', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Fairy Ascension Rocks (Inner)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Fairy Ascension Rocks (Outer)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('TR Pegs Ledge Entry', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('TR Pegs Ledge Leave', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Mountain Pass Rock (Outer)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Mountain Pass Rock (Inner)', player), lambda state: state.can_lift_rocks(player)) # can be fake flippered into, but is in weird state inside that might prevent you from doing things. - set_rule(world.get_entrance('Waterfall Fairy Access', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Zora Waterfall Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Zora Waterfall Water Entry', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Zora Waterfall Approach', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Lost Woods Pass Hammer (North)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Lost Woods Pass Hammer (South)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Lost Woods Pass Rock (North)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Lost Woods Pass Rock (South)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Kakariko Pond Whirlpool', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Kings Grave Rocks (Outer)', player), lambda state: state.can_lift_heavy_rocks(player)) set_rule(world.get_entrance('Kings Grave Rocks (Inner)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('River Bend Water Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Potion Shop Rock (North)', player), lambda state: state.can_lift_rocks(player)) set_rule(world.get_entrance('Potion Shop Rock (South)', player), lambda state: state.can_lift_rocks(player)) - set_rule(world.get_entrance('Bat Cave Ledge Peg', player), lambda state: state.has('Hammer', player)) - set_rule(world.get_entrance('Bat Cave Ledge Peg (East)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Zora Approach Rocks (West)', player), lambda state: state.can_lift_heavy_rocks(player) or state.has_Boots(player)) + set_rule(world.get_entrance('Zora Approach Rocks (East)', player), lambda state: state.can_lift_heavy_rocks(player) or state.has_Boots(player)) + set_rule(world.get_entrance('Hyrule Castle East Rock (Inner)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Hyrule Castle East Rock (Outer)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Wooden Bridge Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Wooden Bridge Northeast Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Blacksmith Ledge Peg (West)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Blacksmith Ledge Peg (East)', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('Desert Statue Move', player), lambda state: state.has('Book of Mudora', player)) set_rule(world.get_entrance('Desert Ledge Rocks (Outer)', player), lambda state: state.can_lift_rocks(player)) set_rule(world.get_entrance('Desert Ledge Rocks (Inner)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('C Whirlpool Rock (Bottom)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('C Whirlpool Rock (Top)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('C Whirlpool Pegs (Outer)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('C Whirlpool Pegs (Inner)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Lake Hylia Water Drop', player), lambda state: state.has('Flippers', 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 Leave', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Ice Cave Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Desert Pass Rocks (North)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Desert Pass Rocks (South)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Octoballoon Waterfall Water Drop', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('Bumper Cave Entrance Rock', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Skull Woods Rock (West)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Skull Woods Rock (East)', player), lambda state: state.can_lift_rocks(player)) + # this more like an ohko rule - dependent on bird being present too - so enemizer could turn this off? + set_rule(world.get_entrance('Bumper Cave Ledge Drop', player), lambda state: state.has('Cape', player) or state.has('Cane of Byrna', player) or state.has_sword(player)) + set_rule(world.get_entrance('Bumper Cave Rock (Outer)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Bumper Cave Rock (Inner)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Skull Woods Pass Rock (North)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Skull Woods Pass Rock (South)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Qirn Jump Water Drop', player), lambda state: state.has('Flippers', player)) set_rule(world.get_entrance('Dark Witch Rock (North)', player), lambda state: state.can_lift_rocks(player)) set_rule(world.get_entrance('Dark Witch Rock (South)', player), lambda state: state.can_lift_rocks(player)) - set_rule(world.get_entrance('Grassy Lawn Pegs (Bottom)', player), lambda state: state.has('Hammer', player)) - set_rule(world.get_entrance('Grassy Lawn Pegs (Top)', player), lambda state: state.has('Hammer', player)) - set_rule(world.get_entrance('Broken Bridge Pass (Bottom)', player), lambda state: state.can_lift_rocks(player) or state.has('Hammer', player) or state.has('Flippers', player)) - set_rule(world.get_entrance('Broken Bridge Pass (Top)', player), lambda state: state.can_lift_rocks(player) or state.has('Hammer', player)) - set_rule(world.get_entrance('West Dark World Gap', player), lambda state: state.has('Hookshot', player)) - set_rule(world.get_entrance('Peg Area Rocks (Left)', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Peg Area Rocks (Right)', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Village of Outcasts Heavy Rock', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Catfish Approach Rocks (West)', player), lambda state: state.can_lift_heavy_rocks(player) or state.has_Boots(player)) + set_rule(world.get_entrance('Catfish Approach Rocks (East)', player), lambda state: state.can_lift_heavy_rocks(player) or state.has_Boots(player)) + set_rule(world.get_entrance('Bush Yard Pegs (Outer)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Bush Yard Pegs (Inner)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Broken Bridge Hammer Rock (South)', player), lambda state: state.can_lift_rocks(player) or state.has('Hammer', player)) + set_rule(world.get_entrance('Broken Bridge Hammer Rock (North)', player), lambda state: state.can_lift_rocks(player) or state.has('Hammer', player)) + set_rule(world.get_entrance('Broken Bridge Hookshot Gap', player), lambda state: state.has('Hookshot', player)) + set_rule(world.get_entrance('Broken Bridge Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Broken Bridge Northeast Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Broken Bridge West Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Peg Area Rocks (West)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Peg Area Rocks (East)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Dig Game To Ledge Drop', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Frog Rock (Inner)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Frog Rock (Outer)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Archery Game Rock (North)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Archery Game Rock (South)', player), lambda state: state.can_lift_heavy_rocks(player)) set_rule(world.get_entrance('Hammer Bridge Pegs (North)', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('Hammer Bridge Pegs (South)', player), lambda state: state.has('Hammer', player)) - - # this more like an ohko rule - dependent on bird being present too - so enemizer could turn this off? - set_rule(world.get_entrance('Bumper Cave Ledge Drop', player), lambda state: state.has_Pearl(player) and - (state.has('Cape', player) or state.has('Cane of Byrna', player) or state.has_sword(player))) + set_rule(world.get_entrance('Hammer Bridge Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Dark C Whirlpool Rock (Bottom)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Dark C Whirlpool Rock (Top)', player), lambda state: state.can_lift_rocks(player)) + set_rule(world.get_entrance('Dark C Whirlpool Pegs (Outer)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Dark C Whirlpool Pegs (Inner)', player), lambda state: state.has('Hammer', player)) + set_rule(world.get_entrance('Ice Lake Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Ice Lake Northeast Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Ice Lake Southwest Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Ice Lake Iceberg Water Entry', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Ice Lake Iceberg Bomb Jump', player), lambda state: state.can_use_bombs(player)) + set_rule(world.get_entrance('Shopping Mall Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Bomber Corner Waterfall Water Drop', player), lambda state: state.has('Flippers', player)) # entrance rules # Caution: If king's grave is relaxed at all to account for reaching it via a two way cave's exit in insanity mode, then the bomb shop logic will need to be updated (that would involve create a small ledge-like Region for it) @@ -275,11 +356,14 @@ def global_rules(world, player): set_rule(world.get_entrance('Skull Woods Final Section', player), lambda state: state.has('Fire Rod', player)) set_rule(world.get_entrance('Misery Mire', player), lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player)) # sword required to cast magic (!) - set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_sword(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword required to cast magic (!) + set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_sword(player) and state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock Ledge', 'Region', player)) # sword required to cast magic (!) if not world.is_atgt_swapped(player): set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has_beam_sword(player)) - set_rule(world.get_entrance('Ganons Tower' if not world.is_atgt_swapped(player) else 'Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) + set_rule(world.get_entrance('GT Approach', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) + set_rule(world.get_entrance('GT Leave', 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_crystals(world.crystals_needed_for_gt[player], player)) # Start of door rando rules # TODO: Do these need to flag off when door rando is off? - some of them, yes @@ -289,13 +373,9 @@ def global_rules(world, player): # Eastern Palace # Eyegore room needs a bow - set_rule(world.get_entrance('Eastern Duo Eyegores NE', player), lambda state: state.can_shoot_arrows(player)) - set_rule(world.get_entrance('Eastern Single Eyegore NE', player), lambda state: state.can_shoot_arrows(player)) + # set_rule(world.get_entrance('Eastern Duo Eyegores NE', player), lambda state: state.can_shoot_arrows(player)) + # set_rule(world.get_entrance('Eastern Single Eyegore NE', player), lambda state: state.can_shoot_arrows(player)) set_rule(world.get_entrance('Eastern Map Balcony Hook Path', player), lambda state: state.has('Hookshot', player)) - if is_trapped('Eastern Single Eyegore ES'): - set_rule(world.get_entrance('Eastern Single Eyegore ES', player), lambda state: state.can_shoot_arrows(player)) - if is_trapped('Eastern Duo Eyegores SE'): - set_rule(world.get_entrance('Eastern Duo Eyegores SE', player), lambda state: state.can_shoot_arrows(player)) # Boss rules. Same as below but no BK or arrow requirement. set_defeat_dungeon_boss_rule(world.get_location('Eastern Palace - Prize', player)) @@ -314,24 +394,12 @@ def global_rules(world, player): set_defeat_dungeon_boss_rule(world.get_location('Tower of Hera - Prize', player)) # Castle Tower - set_rule(world.get_entrance('Tower Gold Knights SW', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('Tower Gold Knights EN', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('Tower Dark Archers WN', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('Tower Red Spears WN', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('Tower Red Guards EN', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('Tower Red Guards SW', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('Tower Circle of Pots NW', player), lambda state: state.can_kill_most_things(player)) - if is_trapped('Tower Circle of Pots ES'): - set_rule(world.get_entrance('Tower Circle of Pots ES', player), - lambda state: state.can_kill_most_things(player)) set_rule(world.get_entrance('Tower Altar NW', player), lambda state: state.has_sword(player)) set_defeat_dungeon_boss_rule(world.get_location('Agahnim 1', player)) set_rule(world.get_entrance('PoD Arena Landing Bonk Path', player), lambda state: state.has_Boots(player)) - set_rule(world.get_entrance('PoD Mimics 1 NW', player), lambda state: state.can_shoot_arrows(player)) - set_rule(world.get_entrance('PoD Mimics 2 NW', player), lambda state: state.can_shoot_arrows(player)) - if is_trapped('PoD Mimics 2 SW'): - set_rule(world.get_entrance('PoD Mimics 2 SW', player), lambda state: state.can_shoot_arrows(player)) + # set_rule(world.get_entrance('PoD Mimics 1 NW', player), lambda state: state.can_shoot_arrows(player)) + # set_rule(world.get_entrance('PoD Mimics 2 NW', player), lambda state: state.can_shoot_arrows(player)) set_rule(world.get_entrance('PoD Bow Statue Down Ladder', player), lambda state: state.can_shoot_arrows(player)) set_rule(world.get_entrance('PoD Map Balcony Drop Down', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('PoD Dark Pegs Landing to Right', player), lambda state: state.has('Hammer', player)) @@ -422,6 +490,7 @@ def global_rules(world, player): if is_trapped('Ice Lobby SE'): set_rule(world.get_entrance('Ice Lobby SE', player), lambda state: state.can_melt_things(player)) set_rule(world.get_entrance('Ice Hammer Block ES', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) + set_rule(world.get_entrance('Ice Right H Path', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) set_rule(world.get_location('Ice Palace - Hammer Block Key Drop', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) set_rule(world.get_location('Ice Palace - Map Chest', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) set_rule(world.get_entrance('Ice Antechamber Hole', player), lambda state: state.can_lift_rocks(player) and state.has('Hammer', player)) @@ -467,9 +536,10 @@ def global_rules(world, player): set_rule(world.get_entrance('Mire Tile Room ES', player), lambda state: state.has_fire_source(player)) set_rule(world.get_entrance('Mire Attic Hint Hole', player), lambda state: state.has_fire_source(player)) set_rule(world.get_entrance('Mire Dark Shooters SW', player), lambda state: state.has('Cane of Somaria', player)) - if is_trapped('Mire Dark Shooters SE'): - set_rule(world.get_entrance('Mire Dark Shooters SE', player), - lambda state: state.has('Cane of Somaria', player)) + # Not: somaria doesn't work here, so this cannot be opened if trapped + # if is_trapped('Mire Dark Shooters SE'): + # set_rule(world.get_entrance('Mire Dark Shooters SE', player), + # lambda state: state.has('Cane of Somaria', player)) set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Misery Mire - Prize', player)) @@ -498,10 +568,10 @@ def global_rules(world, player): set_rule(location, lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('TR Final Abyss Balcony Path', player), lambda state: state.has('Cane of Somaria', player)) set_rule(world.get_entrance('TR Final Abyss Ledge Path', player), lambda state: state.has('Cane of Somaria', player)) - set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) - set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) - set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) - set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right', player), lambda state: state.has('Cane of Byrna', player) or state.has('Cape', player) or state.has('Mirror Shield', player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Left', player), lambda state: state.can_avoid_lasers(player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Bottom Right', player), lambda state: state.can_avoid_lasers(player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Left', player), lambda state: state.can_avoid_lasers(player)) + set_rule(world.get_location('Turtle Rock - Eye Bridge - Top Right', player), lambda state: state.can_avoid_lasers(player)) set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Boss', player)) set_defeat_dungeon_boss_rule(world.get_location('Turtle Rock - Prize', player)) @@ -539,21 +609,12 @@ def global_rules(world, player): set_rule(world.get_entrance('GT Mimics 2 NE', player), lambda state: state.can_shoot_arrows(player)) # consider access to refill room - interior doors would need a change set_rule(world.get_entrance('GT Cannonball Bridge SE', player), lambda state: state.has_Boots(player)) - set_rule(world.get_entrance('GT Gauntlet 1 WN', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Gauntlet 2 EN', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Gauntlet 2 SW', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Gauntlet 3 NW', player), lambda state: state.can_kill_most_things(player)) - if not world.get_door('GT Gauntlet 3 SW', player).entranceFlag: - set_rule(world.get_entrance('GT Gauntlet 3 SW', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Gauntlet 4 NW', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Gauntlet 4 SW', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Gauntlet 5 NW', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Gauntlet 5 WS', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Wizzrobes 1 SW', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Wizzrobes 2 SE', player), lambda state: state.can_kill_most_things(player)) - set_rule(world.get_entrance('GT Wizzrobes 2 NE', player), lambda state: state.can_kill_most_things(player)) set_rule(world.get_entrance('GT Lanmolas 2 ES', player), lambda state: world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].can_defeat(state)) set_rule(world.get_entrance('GT Lanmolas 2 NW', player), lambda state: world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].can_defeat(state)) + # Need cape to safely get past trinexx backwards in this room, makes magic usage tighter + # Could not guarantee safety with byrna, not sure why + if world.get_region('GT Lanmolas 2', player).dungeon.bosses['middle'].name == 'Trinexx': + add_rule(world.get_entrance('GT Quad Pot SW', player), lambda state: state.has('Cape', player)) set_rule(world.get_entrance('GT Torch Cross ES', player), lambda state: state.has_fire_source(player)) if is_trapped('GT Torch Cross WN'): set_rule(world.get_entrance('GT Torch Cross WN', player), lambda state: state.has_fire_source(player)) @@ -653,6 +714,7 @@ def global_rules(world, player): set_rule(world.get_entrance('Ice Bomb Jump Ledge Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Ice Bomb Jump Ledge', player), player)) set_rule(world.get_entrance('Ice Bomb Jump Catwalk Orange Barrier', player), lambda state: state.can_reach_orange(world.get_region('Ice Bomb Jump Catwalk', player), player)) + set_rule(world.get_entrance('Ice Bomb Drop Path', player), lambda state: state.can_hit_crystal(player)) set_rule(world.get_entrance('Ice Conveyor to Crystal', player), lambda state: state.can_hit_crystal(player)) set_rule(world.get_entrance('Ice Refill to Crystal', player), lambda state: state.can_hit_crystal(player) or state.can_reach_blue(world.get_region('Ice Refill', player), player)) @@ -739,6 +801,9 @@ def global_rules(world, player): set_rule(world.get_entrance('GT Crystal Circles to Ranged Crystal', player), lambda state: state.can_hit_crystal_through_barrier(player) or state.has_blunt_weapon(player) or state.has('Cane of Byrna', player)) # or state.has_beam_sword(player) add_key_logic_rules(world, player) + + if world.logic[player] == 'hybridglitches': + add_hmg_key_logic_rules(world, player) # End of door rando rules. if world.restrict_boss_items[player] != 'none': @@ -758,9 +823,11 @@ def global_rules(world, player): add_mc_rule('Agahnim 1') add_mc_rule('Agahnim 2') - add_rule(world.get_location('Sunken Treasure', player), lambda state: state.has('Open Floodgate', player)) - set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player) + set_rule(world.get_location('Ganon', player), lambda state: state.has_beam_sword(player) and state.has_fire_source(player) and (state.has('Tempered Sword', player) or state.has('Golden Sword', player) or (state.has('Silver Arrows', player) and state.can_shoot_arrows(player)) or state.has('Lamp', player) or state.can_extend_magic(player, 12))) # need to light torch a sufficient amount of times + if world.goal[player] != 'ganonhunt': + add_rule(world.get_location('Ganon', player), lambda state: state.has_crystals(world.crystals_needed_for_ganon[player], player)) + set_rule(world.get_entrance('Ganon Drop', player), lambda state: state.has_beam_sword(player)) # need to damage ganon to get tiles to drop @@ -769,7 +836,9 @@ 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', 'Paradox Shop', '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', + 'Hookshot Cave Middle to Back', 'Hookshot Cave Back to Fairy', 'Hookshot Cave Fairy to Back', + 'Good Bee Cave Front to Back', 'Good Bee Cave Back to Front', 'Capacity Upgrade East', + 'Capacity Fairy Pool West', 'Dark Lake Hylia Ledge Fairy', 'Hype Cave', 'Brewery', 'Paradox Cave Chest Area NE', 'Blinds Hideout N', 'Kakariko Well (top to back)', 'Light Hype Fairy'] for entrance in bonkable_doors: @@ -783,82 +852,16 @@ def bomb_rules(world, player): bombable_items = ['Chicken House', 'Aginah\'s Cave', 'Graveyard Cave', 'Hype Cave - Top', 'Hype Cave - Middle Right', 'Hype Cave - Middle Left', 'Hype Cave - Bottom'] for location in bonkable_items: - add_rule(world.get_location(location, player), lambda state: state.can_use_bombs(player) or state.has_Boots(player)) + add_rule(world.get_location(location, player), lambda state: state.can_use_bombs(player) or state.has_Boots(player)) + add_bunny_rule(world.get_location(location, player), player) for location in bombable_items: - add_rule(world.get_location(location, player), lambda state: state.can_use_bombs(player)) - - cave_kill_locations = ['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Left', 'Mini Moldorm Cave - Right', 'Mini Moldorm Cave - Generous Guy', 'Spiral Cave'] - for location in cave_kill_locations: - add_rule(world.get_location(location, player), lambda state: state.can_kill_most_things(player) or state.can_use_bombs(player)) - add_rule(world.get_entrance('Spiral Cave (top to bottom)', player), lambda state: state.can_kill_most_things(player) or state.can_use_bombs(player)) + add_rule(world.get_location(location, player), lambda state: state.can_use_bombs(player)) + add_bunny_rule(world.get_location(location, player), player) paradox_switch_chests = ['Paradox Cave Lower - Far Left', 'Paradox Cave Lower - Left', 'Paradox Cave Lower - Right', 'Paradox Cave Lower - Far Right', 'Paradox Cave Lower - Middle'] for location in paradox_switch_chests: add_rule(world.get_location(location, player), lambda state: state.can_hit_crystal_through_barrier(player)) - - # Dungeon bomb logic - easy_kill_rooms = [ # Door, bool-bombable - ('Hyrule Dungeon Armory S', True), # One green guard - ('Hyrule Dungeon Armory ES', True), # One green guard - ('Hyrule Dungeon Armory Boomerang WS', True), # One blue guard - ('Desert Compass NE', True), # Three popos - ('Desert Four Statues NW', True), # Four popos - ('Desert Four Statues ES', True), # Four popos - ('Hera Beetles WS', False), # Three blue beetles and only two pots, and bombs don't work. - ('Thieves Basement Block WN', True), # One blue and one red zazak and one Stalfos. Two pots. Need to kill the third enemy somehow. - ('Ice Pengator Trap NE', False), # Five pengators. Bomb-doable? - ('TR Twin Pokeys EN', False), # Two pokeys - ('TR Twin Pokeys SW', False), # Two pokeys - ('GT Petting Zoo SE', False), # Dont make anyone do this room with bombs and/or pots. - ('GT DMs Room SW', False) # Four red stalfos - ] - conditional_kill_traps = [ - ('Hyrule Dungeon Armory Interior Key Door N', True), - ('Desert Compass Key Door WN', True), - ('Thieves Blocked Entry SW', True), - ('TR Tongue Pull WS', True), - ('TR Twin Pokeys NW', False), - ] - for killdoor, bombable in easy_kill_rooms: - if bombable: - add_rule(world.get_entrance(killdoor, player), lambda state: (state.can_use_bombs(player) or state.can_kill_most_things(player))) - else: - add_rule(world.get_entrance(killdoor, player), lambda state: state.can_kill_most_things(player)) - for kill_door, bombable in conditional_kill_traps: - if world.get_entrance(kill_door, player).door.trapped: - if bombable: - add_rule(world.get_entrance(kill_door, player), - lambda state: (state.can_use_bombs(player) or state.can_kill_most_things(player))) - else: - add_rule(world.get_entrance(kill_door, player), lambda state: state.can_kill_most_things(player)) - add_rule(world.get_entrance('Ice Stalfos Hint SE', player), lambda state: state.can_use_bombs(player)) # Need bombs for big stalfos knights - add_rule(world.get_entrance('Mire Cross ES', player), lambda state: state.can_kill_most_things(player)) # 4 Sluggulas. Bombs don't work // or (state.can_use_bombs(player) and state.has('Magic Powder'), player) - if world.get_entrance('Mire Cross SW', player).door.trapped: - add_rule(world.get_entrance('Mire Cross SW', player), lambda state: state.can_kill_most_things(player)) - - enemy_kill_drops = [ # Location, bool-bombable - ('Hyrule Castle - Map Guard Key Drop', True), - ('Hyrule Castle - Boomerang Guard Key Drop', True), - ('Hyrule Castle - Key Rat Key Drop', True), - # ('Hyrule Castle - Big Key Drop', True), # Pots are available - # ('Eastern Palace - Dark Eyegore Key Drop', True), # Pots are available - ('Castle Tower - Dark Archer Key Drop', True), - # ('Castle Tower - Circle of Pots Key Drop', True), # Pots are available - # ('Skull Woods - Spike Corner Key Drop', True), # Pots are available - ('Ice Palace - Jelly Key Drop', True), - ('Ice Palace - Conveyor Key Drop', True), - ('Misery Mire - Conveyor Crystal Key Drop', True), - ('Turtle Rock - Pokey 1 Key Drop', True), - ('Turtle Rock - Pokey 2 Key Drop', True), - # ('Ganons Tower - Mini Helmasaur Key Drop', True) # Pots are available - ('Castle Tower - Room 03', True), # Two spring soliders - ('Ice Palace - Compass Chest', True) # Pengators - ] - for location, bombable in enemy_kill_drops: - if bombable: - add_rule(world.get_location(location, player), lambda state: state.can_use_bombs(player) or state.can_kill_most_things(player)) - else: - add_rule(world.get_location(location, player), lambda state: state.can_kill_most_things(player)) + add_bunny_rule(world.get_location(location, player), player) add_rule(world.get_location('Attic Cracked Floor', player), lambda state: state.can_use_bombs(player)) bombable_floors = ['PoD Pit Room Bomb Hole', 'Ice Bomb Drop Hole', 'Ice Freezors Bomb Hole', 'GT Bob\'s Room Hole'] @@ -896,6 +899,49 @@ def bomb_rules(world, player): add_rule(door.entrance, lambda state: state.can_use_bombs(player)) +def challenge_room_rules(world, player): + room_map = world.data_tables[player].uw_enemy_table.room_map + stats = world.data_tables[player].enemy_stats + for region, data in std_kill_rooms.items(): + entrances, trap_ables, room_id, enemy_list = data + rule = get_challenge_rule(world, player, room_map, stats, room_id, enemy_list, region) + for ent in entrances: + entrance = world.get_entrance(ent, player) + if not entrance.door or not entrance.door.entranceFlag: + add_rule_new(entrance, rule) + for ent in trap_ables: + entrance = world.get_entrance(ent, player) + if entrance.door.trapped and not entrance.door.entranceFlag: + add_rule_new(entrance, rule) + for region, data in kill_chests.items(): + locations, room_id, enemy_list = data + rule = get_challenge_rule(world, player, room_map, stats, room_id, enemy_list, region) + for loc in locations: + add_rule_new(world.get_location(loc, player), rule) + + +def get_challenge_rule(world, player, room_map, stats, room_id, enemy_list, region): + sprite_list = room_map[room_id] + sprite_region_pairs = [] + for idx, sprite in enumerate(sprite_list): + if idx in enemy_list: + if not stats[sprite.kind].ignore_for_kill_room: + sprite_region_pairs.append((sprite, world.get_region(sprite.region, player))) + if region == 'Eastern Stalfos Spawn': + stalfos_spawn_exception(sprite_region_pairs, stats, world, player) + if sprite_region_pairs: + return defeat_rule_multiple(world, player, sprite_region_pairs) + return RuleFactory.static_rule(True) + + +def stalfos_spawn_exception(sprite_region_pairs, stats, world, player): + if stats[EnemySprite.Stalfos].health * 4 > 40: + for x in range(0, 4): + sprite_region_pairs.append((Sprite(0x00a8, EnemySprite.Stalfos, 0, 0, 0, 0, 'Eastern Stalfos Spawn'), + world.get_region('Eastern Stalfos Spawn', player))) + return sprite_region_pairs + + def pot_rules(world, player): if world.pottery[player] != 'none': blocks = [l for l in world.get_locations() if l.type == LocationType.Pot and l.pot.flags & PotFlags.Block] @@ -911,7 +957,7 @@ def pot_rules(world, player): (state.has('Cane of Byrna', player) and (state.can_extend_magic(player, 12, True) or (state.world.can_take_damage and (state.has_Boots(player) or state.has_hearts(player, 4))))))) - for l in world.get_region('Dark Desert Hint', player).locations: + for l in world.get_region('Mire Hint', player).locations: if l.type == LocationType.Pot: add_rule(l, lambda state: state.can_use_bombs(player)) for l in world.get_region('Palace of Darkness Hint', player).locations: @@ -941,34 +987,53 @@ def pot_rules(world, player): add_rule(l, lambda state: state.can_hit_crystal(player)) +def drop_rules(world, player): + data_tables = world.data_tables[player] + for super_tile, enemy_list in data_tables.uw_enemy_table.room_map.items(): + for enemy in enemy_list: + if enemy.location: + rule = defeat_rule_single(world, player, enemy, enemy.location.parent_region) + if enemy.location.parent_region.name in special_rules_check: + rule = special_rules_for_region(world, player, enemy.location.parent_region.name, + enemy.location, rule) + if rule.rule_lambda is None: + raise Exception(f'Bad rule for enemy drop. Need to inspect this case: {hex(enemy.kind)}') + add_rule_new(enemy.location, rule) + + def ow_inverted_rules(world, player): if world.mode[player] != 'inverted': set_rule(world.get_entrance('East Death Mountain Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Turtle Rock Teleporter', player), lambda state: state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) + set_rule(world.get_entrance('TR Pegs Teleporter', player), lambda state: state.has('Hammer', player)) set_rule(world.get_entrance('Kakariko Teleporter', player), lambda state: ((state.has('Hammer', player) and state.can_lift_rocks(player)) or state.can_lift_heavy_rocks(player)) and state.has_Pearl(player)) # bunny cannot lift bushes set_rule(world.get_entrance('Castle Gate Teleporter', player), lambda state: state.has('Beat Agahnim 1', player)) + set_rule(world.get_entrance('Castle Gate Teleporter (Inner)', player), lambda state: state.has('Beat Agahnim 1', player)) set_rule(world.get_entrance('East Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer set_rule(world.get_entrance('South Hyrule Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer set_rule(world.get_entrance('Desert Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) set_rule(world.get_entrance('Lake Hylia Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Hyrule Castle Main Gate', player), lambda state: state.has_Mirror(player)) set_rule(world.get_entrance('Hyrule Castle Main Gate (North)', player), lambda state: state.has_Mirror(player)) + set_rule(world.get_entrance('Hyrule Castle Main Gate (South)', player), lambda state: state.has_Mirror(player)) set_rule(world.get_location('Frog', player), lambda state: state.can_lift_heavy_rocks(player) and state.has_Pearl(player)) - set_rule(world.get_entrance('Pyramid Hole', player), lambda state: world.is_pyramid_open(player) or state.has('Beat Agahnim 2', player)) + set_rule(world.get_entrance('Pyramid Hole', player), lambda state: world.is_pyramid_open(player) or world.goal[player] == 'trinity' or state.has('Beat Agahnim 2', player)) + set_rule(world.get_entrance('Mirror To Bombos Tablet Ledge', player), lambda state: state.has_Mirror(player)) # OWG else: - set_rule(world.get_entrance('East Dark Death Mountain Teleporter (Top)', player), lambda state: state.can_lift_heavy_rocks(player) and state.has('Hammer', player) and state.has_Pearl(player)) # bunny cannot use hammer - set_rule(world.get_entrance('East Dark Death Mountain Teleporter (Bottom)', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Turtle Rock Teleporter', player), lambda state: state.can_lift_heavy_rocks(player) and state.has('Hammer', player) and state.has_Pearl(player)) # bunny cannot use hammer + set_rule(world.get_entrance('East Dark Death Mountain Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) set_rule(world.get_entrance('West Dark World Teleporter', player), lambda state: ((state.has('Hammer', player) and state.can_lift_rocks(player)) or state.can_lift_heavy_rocks(player)) and state.has_Pearl(player)) set_rule(world.get_entrance('Post Aga Teleporter', player), lambda state: state.has('Beat Agahnim 1', player)) set_rule(world.get_entrance('East Dark World Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer set_rule(world.get_entrance('South Dark World Teleporter', player), lambda state: state.has('Hammer', player) and state.can_lift_rocks(player) and state.has_Pearl(player)) # bunny cannot use hammer - set_rule(world.get_entrance('Dark Desert Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) - set_rule(world.get_entrance('Dark Lake Hylia Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Mire Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) + set_rule(world.get_entrance('Ice Lake Teleporter', player), lambda state: state.can_lift_heavy_rocks(player)) + + set_rule(world.get_entrance('TR Pegs Ledge Drop', player), lambda state: state.has('Hammer', player)) # inverted 1.0 + set_rule(world.get_entrance('Pyramid Exit Ledge Drop', player), lambda state: state.has('Hammer', player)) # inverted 1.0 set_rule(world.get_location('Frog', player), lambda state: state.can_lift_heavy_rocks(player) and (state.has_Pearl(player) or state.has('Beat Agahnim 1', player)) - or (state.can_reach('Light World', 'Region', player) and state.has_Mirror(player))) # Need LW access using Mirror or Portal + or (state.can_reach('Kakariko Suburb Area', 'Region', player) and state.has_Mirror(player))) # Need LW access using Mirror or Portal set_rule(world.get_entrance('Inverted Pyramid Hole', player), lambda state: world.is_pyramid_open(player) or state.has('Beat Agahnim 2', player)) @@ -978,10 +1043,12 @@ def ow_bunny_rules(world, player): add_bunny_rule(world.get_location('Zora\'s Ledge', player), player) add_bunny_rule(world.get_location('Maze Race', player), player) add_bunny_rule(world.get_location('Flute Spot', player), player) + add_bunny_rule(world.get_location('Catfish', player), player) # entrances add_bunny_rule(world.get_entrance('Lost Woods Hideout Drop', player), player) add_bunny_rule(world.get_entrance('Lumberjack Tree Tree', player), player) + add_bunny_rule(world.get_entrance('Waterfall of Wishing', player), player) add_bunny_rule(world.get_entrance('Bonk Rock Cave', player), player) add_bunny_rule(world.get_entrance('Sanctuary Grave', player), player) add_bunny_rule(world.get_entrance('Kings Grave', player), player) @@ -1005,48 +1072,144 @@ def ow_bunny_rules(world, player): add_bunny_rule(world.get_entrance('Dark Lake Hylia Ledge Spike Cave', player), player) # terrain + add_bunny_rule(world.get_entrance('Lost Woods Bush (West)', player), player) + add_bunny_rule(world.get_entrance('Lost Woods Bush (East)', player), player) add_bunny_rule(world.get_entrance('DM Hammer Bridge (West)', player), player) add_bunny_rule(world.get_entrance('DM Hammer Bridge (East)', player), player) add_bunny_rule(world.get_entrance('DM Broken Bridge (West)', player), player) add_bunny_rule(world.get_entrance('DM Broken Bridge (East)', player), player) - add_bunny_rule(world.get_entrance('Fairy Ascension Rocks', player), player) - add_bunny_rule(world.get_entrance('Death Mountain Entrance Rock', player), player) - add_bunny_rule(world.get_entrance('Waterfall Fairy Access', player), player) - add_bunny_rule(world.get_entrance('Graveyard Ladder (Top)', player), player) - add_bunny_rule(world.get_entrance('Graveyard Ladder (Bottom)', player), player) + add_bunny_rule(world.get_entrance('Fairy Ascension Rocks (Inner)', player), player) + add_bunny_rule(world.get_entrance('Fairy Ascension Rocks (Outer)', player), player) + add_bunny_rule(world.get_entrance('TR Pegs Ledge Entry', player), player) + add_bunny_rule(world.get_entrance('TR Pegs Ledge Leave', player), player) + add_bunny_rule(world.get_entrance('TR Pegs Ledge Drop', player), player) # inverted 1.0 + add_bunny_rule(world.get_entrance('Mountain Pass Rock (Outer)', player), player) + add_bunny_rule(world.get_entrance('Mountain Pass Rock (Inner)', player), player) + add_bunny_rule(world.get_entrance('Zora Waterfall Water Drop', player), player) + add_bunny_rule(world.get_entrance('Zora Waterfall Water Entry', player), player) + add_bunny_rule(world.get_entrance('Zora Waterfall Approach', player), player) + add_bunny_rule(world.get_entrance('Lost Woods Pass Hammer (North)', player), player) + add_bunny_rule(world.get_entrance('Lost Woods Pass Hammer (South)', player), player) + add_bunny_rule(world.get_entrance('Lost Woods Pass Rock (North)', player), player) + add_bunny_rule(world.get_entrance('Lost Woods Pass Rock (South)', player), player) + add_bunny_rule(world.get_entrance('Kakariko Pond Whirlpool', player), player) add_bunny_rule(world.get_entrance('Kings Grave Rocks (Outer)', player), player) add_bunny_rule(world.get_entrance('Kings Grave Rocks (Inner)', player), player) + add_bunny_rule(world.get_entrance('Graveyard Ladder (Top)', player), player) + add_bunny_rule(world.get_entrance('Graveyard Ladder (Bottom)', player), player) + add_bunny_rule(world.get_entrance('River Bend Water Drop', player), player) + add_bunny_rule(world.get_entrance('River Bend East Water Drop', player), player) + add_bunny_rule(world.get_entrance('Potion Shop Water Drop', player), player) + add_bunny_rule(world.get_entrance('Potion Shop Northeast Water Drop', player), player) add_bunny_rule(world.get_entrance('Potion Shop Rock (North)', player), player) add_bunny_rule(world.get_entrance('Potion Shop Rock (South)', player), player) - add_bunny_rule(world.get_entrance('Kakariko Yard Bush (North)', player), player) - add_bunny_rule(world.get_entrance('Kakariko Yard Bush (South)', player), player) + add_bunny_rule(world.get_entrance('Zora Approach Water Drop', player), player) + add_bunny_rule(world.get_entrance('Zora Approach Rocks (West)', player), player) + add_bunny_rule(world.get_entrance('Zora Approach Rocks (East)', player), player) add_bunny_rule(world.get_entrance('Kakariko Southwest Bush (North)', player), player) add_bunny_rule(world.get_entrance('Kakariko Southwest Bush (South)', player), player) + add_bunny_rule(world.get_entrance('Kakariko Yard Bush (North)', player), player) + add_bunny_rule(world.get_entrance('Kakariko Yard Bush (South)', player), player) + add_bunny_rule(world.get_entrance('Hyrule Castle Southwest Bush (North)', player), player) + add_bunny_rule(world.get_entrance('Hyrule Castle Southwest Bush (South)', player), player) add_bunny_rule(world.get_entrance('Hyrule Castle Courtyard Bush (North)', player), player) add_bunny_rule(world.get_entrance('Hyrule Castle Courtyard Bush (South)', player), player) + add_bunny_rule(world.get_entrance('Hyrule Castle East Rock (Inner)', player), player) + add_bunny_rule(world.get_entrance('Hyrule Castle East Rock (Outer)', player), player) add_bunny_rule(world.get_entrance('Wooden Bridge Bush (North)', player), player) add_bunny_rule(world.get_entrance('Wooden Bridge Bush (South)', player), player) - add_bunny_rule(world.get_entrance('Bat Cave Ledge Peg', player), player) - add_bunny_rule(world.get_entrance('Bat Cave Ledge Peg (East)', player), player) + add_bunny_rule(world.get_entrance('Wooden Bridge Water Drop', player), player) + add_bunny_rule(world.get_entrance('Wooden Bridge Northeast Water Drop', player), player) + add_bunny_rule(world.get_entrance('Blacksmith Ledge Peg (West)', player), player) + add_bunny_rule(world.get_entrance('Blacksmith Ledge Peg (East)', player), player) + add_bunny_rule(world.get_entrance('Maze Race Game', player), player) add_bunny_rule(world.get_entrance('Desert Ledge Rocks (Outer)', player), player) add_bunny_rule(world.get_entrance('Desert Ledge Rocks (Inner)', player), player) + add_bunny_rule(world.get_entrance('Flute Boy Bush (North)', player), player) + add_bunny_rule(world.get_entrance('Flute Boy Bush (South)', player), player) + add_bunny_rule(world.get_entrance('C Whirlpool Water Entry', player), player) + add_bunny_rule(world.get_entrance('C Whirlpool Rock (Bottom)', player), player) + add_bunny_rule(world.get_entrance('C Whirlpool Rock (Top)', player), player) + add_bunny_rule(world.get_entrance('C Whirlpool Pegs (Outer)', player), player) + add_bunny_rule(world.get_entrance('C Whirlpool Pegs (Inner)', player), player) + add_bunny_rule(world.get_entrance('Statues Water Entry', player), player) + add_bunny_rule(world.get_entrance('Lake Hylia Water Drop', player), player) + add_bunny_rule(world.get_entrance('Lake Hylia South Water Drop', player), player) + add_bunny_rule(world.get_entrance('Lake Hylia Northeast Water Drop', player), player) + add_bunny_rule(world.get_entrance('Lake Hylia Central Water Drop', player), player) + add_bunny_rule(world.get_entrance('Lake Hylia Island Water Drop', player), player) + add_bunny_rule(world.get_entrance('Lake Hylia Water D Leave', player), player) + add_bunny_rule(world.get_entrance('Ice Cave Water Drop', player), player) + add_bunny_rule(world.get_entrance('Desert Pass Rocks (North)', player), player) + add_bunny_rule(world.get_entrance('Desert Pass Rocks (South)', player), player) + add_bunny_rule(world.get_entrance('Octoballoon Water Drop', player), player) + add_bunny_rule(world.get_entrance('Octoballoon Waterfall Water Drop', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Rock (West)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Rock (East)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Forgotten Bush (West)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Forgotten Bush (East)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Second Section Hole', player), player) add_bunny_rule(world.get_entrance('East Dark Death Mountain Bushes', player), player) - add_bunny_rule(world.get_entrance('Bumper Cave Entrance Rock', player), player) + add_bunny_rule(world.get_entrance('Bumper Cave Ledge Drop', player), player) + add_bunny_rule(world.get_entrance('Bumper Cave Rock (Outer)', player), player) + add_bunny_rule(world.get_entrance('Bumper Cave Rock (Inner)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Pass Bush Row (West)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Pass Bush Row (East)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Pass Bush (North)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Pass Bush (South)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Pass Rock (North)', player), player) + add_bunny_rule(world.get_entrance('Skull Woods Pass Rock (South)', player), player) add_bunny_rule(world.get_entrance('Dark Graveyard Bush (South)', player), player) add_bunny_rule(world.get_entrance('Dark Graveyard Bush (North)', player), player) + add_bunny_rule(world.get_entrance('Qirn Jump Water Drop', player), player) + add_bunny_rule(world.get_entrance('Qirn Jump East Water Drop', player), player) + add_bunny_rule(world.get_entrance('Dark Witch Water Drop', player), player) + add_bunny_rule(world.get_entrance('Dark Witch Northeast Water Drop', player), player) add_bunny_rule(world.get_entrance('Dark Witch Rock (North)', player), player) add_bunny_rule(world.get_entrance('Dark Witch Rock (South)', player), player) - add_bunny_rule(world.get_entrance('Grassy Lawn Pegs (Bottom)', player), player) - add_bunny_rule(world.get_entrance('Grassy Lawn Pegs (Top)', player), player) - add_bunny_rule(world.get_entrance('Broken Bridge Pass (Bottom)', player), player) - add_bunny_rule(world.get_entrance('Broken Bridge Pass (Top)', player), player) - add_bunny_rule(world.get_entrance('West Dark World Gap', player), player) - add_bunny_rule(world.get_entrance('Peg Area Rocks (Left)', player), player) - add_bunny_rule(world.get_entrance('Peg Area Rocks (Right)', player), player) - add_bunny_rule(world.get_entrance('Village of Outcasts Heavy Rock', player), player) + add_bunny_rule(world.get_entrance('Catfish Approach Water Drop', player), player) + add_bunny_rule(world.get_entrance('Catfish Approach Rocks (West)', player), player) + add_bunny_rule(world.get_entrance('Catfish Approach Rocks (East)', player), player) + add_bunny_rule(world.get_entrance('Bush Yard Pegs (Outer)', player), player) + add_bunny_rule(world.get_entrance('Bush Yard Pegs (Inner)', player), player) + add_bunny_rule(world.get_entrance('Broken Bridge Hammer Rock (South)', player), player) + add_bunny_rule(world.get_entrance('Broken Bridge Hammer Rock (North)', player), player) + add_bunny_rule(world.get_entrance('Broken Bridge Hookshot Gap', player), player) + add_bunny_rule(world.get_entrance('Broken Bridge Water Drop', player), player) + add_bunny_rule(world.get_entrance('Broken Bridge Northeast Water Drop', player), player) + add_bunny_rule(world.get_entrance('Broken Bridge West Water Drop', player), player) + add_bunny_rule(world.get_entrance('Peg Area Rocks (West)', player), player) + add_bunny_rule(world.get_entrance('Peg Area Rocks (East)', player), player) + add_bunny_rule(world.get_entrance('Dig Game To Ledge Drop', player), player) + add_bunny_rule(world.get_entrance('Frog Rock (Inner)', player), player) + add_bunny_rule(world.get_entrance('Frog Rock (Outer)', player), player) + add_bunny_rule(world.get_entrance('Archery Game Rock (North)', player), player) + add_bunny_rule(world.get_entrance('Archery Game Rock (South)', player), player) add_bunny_rule(world.get_entrance('Hammer Bridge Pegs (North)', player), player) add_bunny_rule(world.get_entrance('Hammer Bridge Pegs (South)', player), player) + add_bunny_rule(world.get_entrance('Hammer Bridge Water Drop', player), player) + add_bunny_rule(world.get_entrance('Stumpy Approach Bush (North)', player), player) + add_bunny_rule(world.get_entrance('Stumpy Approach Bush (South)', player), player) + add_bunny_rule(world.get_entrance('Dark C Whirlpool Water Entry', player), player) + add_bunny_rule(world.get_entrance('Dark C Whirlpool Rock (Bottom)', player), player) + add_bunny_rule(world.get_entrance('Dark C Whirlpool Rock (Top)', player), player) + add_bunny_rule(world.get_entrance('Dark C Whirlpool Pegs (Outer)', player), player) + add_bunny_rule(world.get_entrance('Dark C Whirlpool Pegs (Inner)', player), player) + add_bunny_rule(world.get_entrance('Hype Cave Water Entry', player), player) + add_bunny_rule(world.get_entrance('Ice Lake Water Drop', player), player) + add_bunny_rule(world.get_entrance('Ice Lake Northeast Water Drop', player), player) + add_bunny_rule(world.get_entrance('Ice Lake Southwest Water Drop', player), player) + add_bunny_rule(world.get_entrance('Ice Lake Southeast Water Drop', player), player) + add_bunny_rule(world.get_entrance('Ice Lake Iceberg Water Entry', player), player) + add_bunny_rule(world.get_entrance('Ice Lake Iceberg Bomb Jump', player), player) + add_bunny_rule(world.get_entrance('Shopping Mall Water Drop', player), player) + add_bunny_rule(world.get_entrance('Bomber Corner Water Drop', player), player) + add_bunny_rule(world.get_entrance('Bomber Corner Waterfall Water Drop', player), player) + + # OWG rules + add_bunny_rule(world.get_entrance('Stone Bridge EC Cliff Water Drop', player), player) + add_bunny_rule(world.get_entrance('Hammer Bridge EC Cliff Water Drop', player), player) if not world.is_atgt_swapped(player): add_bunny_rule(world.get_entrance('Agahnims Tower', player), player) @@ -1057,31 +1220,23 @@ def ow_bunny_rules(world, player): def no_glitches_rules(world, player): - set_rule(world.get_entrance('Light World Water Drop', player), lambda state: state.has('Flippers', player)) # can be fake flippered to - set_rule(world.get_entrance('Zora Waterfall Water Drop', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('Potion Shop Water Drop', player), lambda state: state.has('Flippers', player)) # can be fake flippered to - set_rule(world.get_entrance('Northeast Light World Water Drop', player), lambda state: state.has('Flippers', player)) # can be fake flippered to - set_rule(world.get_entrance('Lake Hylia Central Island Water Drop', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('West Dark World Water Drop', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('South Dark World Water Drop', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('East Dark World Water Drop', player), lambda state: state.has('Flippers', player)) - set_rule(world.get_entrance('Northeast Dark World Water Drop', player), lambda state: state.has('Flippers', player)) # can be fake flippered to - set_rule(world.get_entrance('Southeast Dark World Water Drop', player), lambda state: state.has('Flippers', player)) # can be fake flippered to - set_rule(world.get_entrance('Catfish Water Drop', player), lambda state: state.has('Flippers', player)) # can be fake flippered to - set_rule(world.get_entrance('Ice Palace Leave Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('River Bend East Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Potion Shop Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Potion Shop Northeast Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Zora Approach Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('C Whirlpool Water Entry', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Statues Water Entry', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Lake Hylia South Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Octoballoon Water Drop', player), lambda state: state.has('Flippers', player)) - add_bunny_rule(world.get_entrance('Light World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Zora Waterfall Water Drop', player), player) - add_bunny_rule(world.get_entrance('Potion Shop Water Drop', player), player) - add_bunny_rule(world.get_entrance('Northeast Light World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Lake Hylia Central Island Water Drop', player), player) - add_bunny_rule(world.get_entrance('West Dark World Water Drop', player), player) - add_bunny_rule(world.get_entrance('South Dark World Water Drop', player), player) - add_bunny_rule(world.get_entrance('East Dark World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Northeast Dark World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Southeast Dark World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Catfish Water Drop', player), player) - add_bunny_rule(world.get_entrance('Ice Palace Leave Water Drop', player), player) + set_rule(world.get_entrance('Qirn Jump East Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Dark Witch Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Dark Witch Northeast Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Catfish Approach Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Dark C Whirlpool Water Entry', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Hype Cave Water Entry', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Ice Lake Southeast Water Drop', player), lambda state: state.has('Flippers', player)) + set_rule(world.get_entrance('Bomber Corner Water Drop', player), lambda state: state.has('Flippers', player)) # todo: move some dungeon rules to no glicthes logic - see these for examples # add_rule(world.get_entrance('Ganons Tower (Hookshot Room)', player), lambda state: state.has('Hookshot', player) or state.has_Boots(player)) @@ -1090,24 +1245,29 @@ def no_glitches_rules(world, player): # for location in DMs_room_chests: # add_rule(world.get_location(location, player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('Paradox Cave Push Block Reverse', player), lambda state: False) # no glitches does not require block override + set_rule(world.get_entrance('Ice Lake Northeast Pier Hop', player), lambda state: False) forbid_bomb_jump_requirements(world, player) add_conditional_lamps(world, player) def fake_flipper_rules(world, player): - set_rule(world.get_entrance('Light World Water Drop', player), lambda state: True) + set_rule(world.get_entrance('River Bend East Water Drop', player), lambda state: True) set_rule(world.get_entrance('Potion Shop Water Drop', player), lambda state: True) - set_rule(world.get_entrance('Northeast Light World Water Drop', player), lambda state: True) - set_rule(world.get_entrance('Northeast Dark World Water Drop', player), lambda state: True) - set_rule(world.get_entrance('Southeast Dark World Water Drop', player), lambda state: True) - set_rule(world.get_entrance('Catfish Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Potion Shop Northeast Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Zora Approach Water Drop', player), lambda state: True) + set_rule(world.get_entrance('C Whirlpool Water Entry', player), lambda state: True) + set_rule(world.get_entrance('Statues Water Entry', player), lambda state: True) + set_rule(world.get_entrance('Lake Hylia South Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Octoballoon Water Drop', player), lambda state: True) - add_bunny_rule(world.get_entrance('Light World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Potion Shop Water Drop', player), player) - add_bunny_rule(world.get_entrance('Northeast Light World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Northeast Dark World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Southeast Dark World Water Drop', player), player) - add_bunny_rule(world.get_entrance('Catfish Water Drop', player), player) + set_rule(world.get_entrance('Qirn Jump East Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Dark Witch Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Dark Witch Northeast Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Catfish Approach Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Dark C Whirlpool Water Entry', player), lambda state: True) + set_rule(world.get_entrance('Hype Cave Water Entry', player), lambda state: True) + set_rule(world.get_entrance('Ice Lake Southeast Water Drop', player), lambda state: True) + set_rule(world.get_entrance('Bomber Corner Water Drop', player), lambda state: True) def forbid_bomb_jump_requirements(world, player): @@ -1115,7 +1275,8 @@ def forbid_bomb_jump_requirements(world, player): for location in DMs_room_chests: add_rule(world.get_location(location, player), lambda state: state.has('Hookshot', player)) set_rule(world.get_entrance('Paradox Cave Bomb Jump', player), lambda state: False) - set_rule(world.get_entrance('Ice Island To East Pier', player), lambda state: False) + set_rule(world.get_entrance('Ice Lake Iceberg Bomb Jump', player), lambda state: False) + # Light cones in standard depend on which world we actually are in, not which one the location would normally be @@ -1137,8 +1298,7 @@ def add_conditional_lamps(world, player): spot = world.get_location(spot, player) else: spot = world.get_entrance(spot, player) - if (not world.dark_world_light_cone and check_is_dark_world(world.get_region(region, player))) or (not world.light_world_light_cone and not check_is_dark_world(world.get_region(region, player))): - add_lamp_requirement(spot, player) + add_lamp_requirement(spot, player) dark_rooms = { 'TR Dark Ride': {'sewer': False, 'entrances': ['TR Dark Ride Up Stairs', 'TR Dark Ride SW', 'TR Dark Ride Path'], 'locations': []}, @@ -1176,7 +1336,8 @@ def add_conditional_lamps(world, player): 'Sewers Water': {'sewer': True, 'entrances': ['Sewers Water S', 'Sewers Water W'], 'locations': []}, 'Sewers Dark Aquabats': {'sewer': True, 'entrances': ['Sewers Dark Aquabats N', 'Sewers Dark Aquabats ES'], 'locations': []}, 'Sewers Key Rat': {'sewer': True, 'entrances': ['Sewers Key Rat S', 'Sewers Key Rat NE'], 'locations': ['Hyrule Castle - Key Rat Key Drop']}, - 'Old Man Cave': {'sewer': False, 'entrances': ['Old Man Cave Exit (East)']}, + 'Old Man Cave (East)': {'sewer': False, 'entrances': ['Old Man Cave Exit (East)', 'Old Man Cave W']}, + 'Old Man Cave (West)': {'sewer': False, 'entrances': ['Old Man Cave E']}, 'Old Man House Back': {'sewer': False, 'entrances': ['Old Man House Back to Front', 'Old Man House Exit (Top)']}, 'Death Mountain Return Cave (left)': {'sewer': False, 'entrances': ['Death Mountain Return Cave E', 'Death Mountain Return Cave Exit (West)']}, 'Death Mountain Return Cave (right)': {'sewer': False, 'entrances': ['Death Mountain Return Cave Exit (East)', 'Death Mountain Return Cave W']}, @@ -1224,56 +1385,99 @@ def swordless_rules(world, 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 - set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock (Top)', 'Region', player)) # sword not required to use medallion for opening in swordless (!) + set_rule(world.get_entrance('Turtle Rock', player), lambda state: state.has_turtle_rock_medallion(player) and state.can_reach('Turtle Rock Ledge', 'Region', player)) # sword not required to use medallion for opening in swordless (!) 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 (!) if world.mode[player] != 'inverted': set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player)) -# todo: new traps std_kill_rooms = { - 'Hyrule Dungeon Armory Main': ['Hyrule Dungeon Armory S', 'Hyrule Dungeon Armory ES'], # One green guard - 'Hyrule Dungeon Armory Boomerang': ['Hyrule Dungeon Armory Boomerang WS'], # One blue guard - 'Eastern Stalfos Spawn': ['Eastern Stalfos Spawn ES', 'Eastern Stalfos Spawn NW'], # Can use pots - 'Desert Compass Room': ['Desert Compass NE'], # Three popos - 'Desert Four Statues': ['Desert Four Statues NW', 'Desert Four Statues ES'], # Four popos - 'Hera Beetles': ['Hera Beetles WS'], # Three blue beetles and only two pots, and bombs don't work. - 'Tower Gold Knights': ['Tower Gold Knights SW', 'Tower Gold Knights EN'], # Two ball and chain - 'Tower Dark Archers': ['Tower Dark Archers WN'], # Not a kill room - 'Tower Red Spears': ['Tower Red Spears WN'], # Two spear soldiers - 'Tower Red Guards': ['Tower Red Guards EN', 'Tower Red Guards SW'], # Two usain bolts - 'Tower Circle of Pots': ['Tower Circle of Pots NW'], # Two spear soldiers. Plenty of pots. - 'PoD Turtle Party': ['PoD Turtle Party ES', 'PoD Turtle Party NW'], # Lots of turtles. - 'Thieves Basement Block': ['Thieves Basement Block WN'], # One blue and one red zazak and one Stalfos. Two pots. Need weapon. - 'Ice Stalfos Hint': ['Ice Stalfos Hint SE'], # Need bombs for big stalfos knights - 'Ice Pengator Trap': ['Ice Pengator Trap NE'], # Five pengators. Bomb-doable? - 'Mire 2': ['Mire 2 NE'], # Wizzrobes. Bombs dont work. - 'Mire Cross': ['Mire Cross ES'], # 4 Sluggulas. Bombs don't work - 'TR Twin Pokeys': ['TR Twin Pokeys EN', 'TR Twin Pokeys SW'], # Two pokeys - 'GT Petting Zoo': ['GT Petting Zoo SE'], # Dont make anyone do this room with bombs. - 'GT DMs Room': ['GT DMs Room SW'], # Four red stalfos - 'GT Gauntlet 1': ['GT Gauntlet 1 WN'], # Stalfos/zazaks - 'GT Gauntlet 2': ['GT Gauntlet 2 EN', 'GT Gauntlet 2 SW'], # Red stalfos - 'GT Gauntlet 3': ['GT Gauntlet 3 NW', 'GT Gauntlet 3 SW'], # Blue zazaks - 'GT Gauntlet 4': ['GT Gauntlet 4 NW', 'GT Gauntlet 4 SW'], # Red zazaks - 'GT Gauntlet 5': ['GT Gauntlet 5 NW', 'GT Gauntlet 5 WS'], # Stalfos and zazak - 'GT Wizzrobes 1': ['GT Wizzrobes 1 SW'], # Wizzrobes. Bombs don't work - 'GT Wizzrobes 2': ['GT Wizzrobes 2 SE', 'GT Wizzrobes 2 NE'] # Wizzrobes. Bombs don't work -} # all trap rooms? + 'Hyrule Dungeon Armory Main': # One green guard + (['Hyrule Dungeon Armory S', 'Hyrule Dungeon Armory ES'], ['Hyrule Dungeon Armory Interior Key Door N'], + 0x71, [0]), + 'Hyrule Dungeon Armory Boomerang': # One blue guard + (['Hyrule Dungeon Armory Boomerang WS'], [], 0x71, [1]), + 'Eastern Stalfos Spawn': # Can use pots up to a point see stalfos_spawn_exception + (['Eastern Stalfos Spawn ES', 'Eastern Stalfos Spawn NW'], [], 0xa8, []), + 'Eastern Single Eyegore': + (['Eastern Single Eyegore NE'], ['Eastern Single Eyegore ES'], 0xd8, [8, 9, 10]), + 'Eastern Duo Eyegores': + (['Eastern Duo Eyegores NE'], ['Eastern Duo Eyegores SE'], 0xd8, [0, 1, 2, 3, 4, 5, 6, 7]), + 'Desert Compass Room': # Three popos (beamos) + (['Desert Compass NE'], ['Desert Compass Key Door WN'], 0x085, [2, 3, 4, 5]), + 'Desert Four Statues': # Four popos (beamos) + (['Desert Four Statues NW', 'Desert Four Statues ES'], [], 0x53, [5, 6, 8, 9, 10]), + 'Hera Beetles': # Three blue beetles and only two pots, and bombs don't work. + (['Hera Beetles WS'], [], 0x31, [7, 8, 10]), + 'Tower Gold Knights': # Two ball and chain + (['Tower Gold Knights SW', 'Tower Gold Knights EN'], [], 0xe0, [0, 1]), + 'Tower Dark Archers': # Backwards kill room + (['Tower Dark Archers WN'], [], 0xc0, [0, 1, 3]), + 'Tower Red Spears': # Two spear soldiers + (['Tower Red Spears WN'], [], 0xb0, [1, 2, 3, 4]), + 'Tower Red Guards': # Two usain bolts + (['Tower Red Guards EN', 'Tower Red Guards SW'], [], 0xb0, [0, 5]), + 'Tower Circle of Pots': # Two spear soldiers. Plenty of pots. + (['Tower Circle of Pots NW'], ['Tower Circle of Pots ES'], 0xb0, [7, 8, 9, 10]), + 'PoD Mimics 1': + (['PoD Mimics 1 NW'], ['PoD Mimics 1 SW'], 0x4b, [0, 3, 4]), + 'PoD Mimics 2': + (['PoD Mimics 2 NW'], ['PoD Mimics 2 SW'], 0x1b, [3, 4, 5]), + 'PoD Turtle Party': # Lots of turtles. + (['PoD Turtle Party ES', 'PoD Turtle Party NW'], [], 0x0b, [4, 5, 6, 7, 8, 9]), + 'Thieves Basement Block': # One blue and one red zazak and one Stalfos. Two pots. Need weapon. + (['Thieves Basement Block WN'], ['Thieves Blocked Entry SW'], 0x45, [1, 2, 3]), + 'Ice Jelly Key': + (['Ice Jelly Key ES'], [], 0x0e, [1, 2, 3]), + 'Ice Stalfos Hint': # Need bombs for big stalfos knights + (['Ice Stalfos Hint SE'], [], 0x3e, [1, 2]), + 'Ice Pengator Trap': # Five pengators. Bomb-doable? + (['Ice Pengator Trap NE'], [], 0x6e, [0, 1, 2, 3, 4]), + 'Mire 2': # Wizzrobes. Bombs dont work. + (['Mire 2 NE'], [], 0xd2, [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]), + 'Mire Cross': # 4 Sluggulas. Bombs don't work + (['Mire Cross ES'], ['Mire Cross SW'], 0xb2, [5, 6, 7, 10, 11]), + 'TR Twin Pokeys': # Two pokeys + (['TR Twin Pokeys EN', 'TR Twin Pokeys SW'], ['TR Twin Pokeys NW'], 0x24, [3, 4, 5, 6]), + 'TR Tongue Pull': # Kill zols for money + (['TR Tongue Pull NE'], ['TR Tongue Pull WS'], 0x04, [9, 13, 14]), + 'GT Petting Zoo': # Don't make anyone do this room with bombs. + (['GT Petting Zoo SE'], [], 0x7d, [4, 5, 6, 7, 8, 10]), + 'GT DMs Room': # Four red stalfos + (['GT DMs Room SW'], [], 0x7b, [2, 3, 4, 5, 8, 9, 10]), + 'GT Gauntlet 1': # Stalfos/zazaks + (['GT Gauntlet 1 WN'], [], 0x5d, [3, 4, 5, 6]), + 'GT Gauntlet 2': # Red stalfos + (['GT Gauntlet 2 EN', 'GT Gauntlet 2 SW'], [], 0x5d, [0, 1, 2, 7]), + 'GT Gauntlet 3': # Blue zazaks + (['GT Gauntlet 3 NW', 'GT Gauntlet 3 SW'], [], 0x5d, [8, 9, 10, 11, 12]), + 'GT Gauntlet 4': # Red zazaks + (['GT Gauntlet 4 NW', 'GT Gauntlet 4 SW'], [], 0x6d, [0, 1, 2, 3]), + 'GT Gauntlet 5': # Stalfos and zazak + (['GT Gauntlet 5 NW', 'GT Gauntlet 5 WS'], [], 0x6d, [4, 5, 6, 7, 8]), + 'GT Wizzrobes 1': # Wizzrobes. Bombs don't work + (['GT Wizzrobes 1 SW'], [], 0xa5, [2, 3, 7]), + 'GT Wizzrobes 2': # Wizzrobes. Bombs don't work + (['GT Wizzrobes 2 SE', 'GT Wizzrobes 2 NE'], [], 0xa5, [0, 1, 4, 5, 6]), + 'Spiral Cave (Top)': # for traversal in enemizer at low health + (['Spiral Cave (top to bottom)'], [], 0xEE, [0, 1, 2, 3, 4]), +} # all trap rooms? (Desert Trap Room, Thieves Trap Room currently subtile only) -std_kill_doors_if_trapped = { - 'Hyrule Dungeon Armory Main': 'Hyrule Dungeon Armory Interior Key Door N', - # 'Eastern Single Eyegore ES', # arrow rule is sufficient - # 'Eastern Duo Eyegores S', # arrow rule is sufficient - 'TR Twin Pokeys': 'TR Twin Pokeys NW', - 'Thieves Basement Block': 'Thieves Blocked Entry SW', - 'Desert Compass Room': 'Desert Compass Key Door WN', - 'Mire Cross': 'Mire Cross SW', - 'Tower Circle of Pots': 'Tower Circle of Pots ES', - # 'Ice Lobby S' # can melt rule is sufficient +kill_chests = { + 'Tower Room 03': (['Castle Tower - Room 03'], 0xe0, [2, 3]), + 'Ice Compass Room': (['Ice Palace - Compass Chest'], 0x2e, [0, 1, 2, 3, 4, 5]), + 'Swamp Entrance': (['Swamp Palace - Entrance'], 0x28, [0, 1, 2, 3, 4]), + 'GT Tile Room': (['Ganons Tower - Tile Room'], 0x8d, [1, 2, 3, 4]), + 'Mini Moldorm Cave': + (['Mini Moldorm Cave - Far Left', 'Mini Moldorm Cave - Far Right', 'Mini Moldorm Cave - Left', + 'Mini Moldorm Cave - Right', 'Mini Moldorm Cave - Generous Guy'], 0x123, [0, 1, 2, 3]), + 'Mimic Cave': + (['Mimic Cave'], 0x10c, [4, 5, 6, 7]), } + + def add_connection(parent_name, target_name, entrance_name, world, player): parent = world.get_region(parent_name, player) target = world.get_region(target_name, player) @@ -1294,8 +1498,6 @@ def standard_rules(world, player): entrance = world.get_portal(portal_name, player).door.entrance set_rule(entrance, lambda state: state.has('Zelda Delivered', player)) set_rule(world.get_entrance('Sanctuary Exit', player), lambda state: state.has('Zelda Delivered', player)) - set_rule(world.get_entrance('Hyrule Castle Ledge Drop', player), lambda state: state.has('Zelda Delivered', player)) - set_rule(world.get_entrance('Hyrule Castle Main Gate (North)', player), lambda state: state.has('Zelda Delivered', player)) # zelda should be saved before agahnim is in play add_rule(world.get_location('Agahnim 1', player), lambda state: state.has('Zelda Delivered', player)) @@ -1326,12 +1528,12 @@ def standard_rules(world, player): for loc in region.locations: add_rule(loc, lambda state: standard_escape_rule(state)) if region.name in std_kill_rooms: - for ent in std_kill_rooms[region.name]: + for ent in std_kill_rooms[region.name][0]: add_rule(world.get_entrance(ent, player), lambda state: standard_escape_rule(state)) - if region.name in std_kill_doors_if_trapped: - ent = world.get_entrance(std_kill_doors_if_trapped[region.name], player) - if ent.door.trapped: - add_rule(ent, lambda state: standard_escape_rule(state)) + for ent in std_kill_rooms[region.name][1]: + entrance = world.get_entrance(ent, player) + if entrance.door.trapped: + add_rule(entrance, lambda state: standard_escape_rule(state)) set_rule(world.get_location('Zelda Pickup', player), lambda state: state.has('Big Key (Escape)', player)) set_rule(world.get_entrance('Hyrule Castle Tapestry Backwards', player), lambda state: state.has('Zelda Herself', player)) @@ -1345,24 +1547,8 @@ def standard_rules(world, player): set_rule(world.get_location('Zelda Drop Off', player), lambda state: state.has('Zelda Herself', player) and check_rule_list(state, rule_list)) - for location in ['Mushroom', 'Bottle Merchant', 'Flute Spot', 'Sunken Treasure', 'Purple Chest']: - add_rule(world.get_location(location, player), lambda state: state.has('Zelda Delivered', player)) - - for entrance in ['Blinds Hideout', 'Kings Grave Rocks (Outer)', 'Dam', 'Tavern North', 'Chicken House', - 'Aginahs Cave', 'Sahasrahlas Hut', 'Kakariko Well Drop', 'Kakariko Well Cave', 'Blacksmiths Hut', - 'Bat Cave Ledge Peg', 'Bat Cave Cave', 'Sick Kids House', 'Wooden Bridge Bush (South)', - 'Lost Woods Hideout Drop', 'Lost Woods Hideout Stump', 'Lumberjack Tree Tree', - 'Lumberjack Tree Cave', 'Mini Moldorm Cave', 'Ice Rod Cave', 'Light World Water Drop', - 'Bonk Rock Cave', 'Library', 'Potion Shop', 'Two Brothers House (East)', 'Desert Statue Move', - 'Eastern Palace', 'Master Sword Meadow', 'Sanctuary', 'Sanctuary Grave', - 'Death Mountain Entrance Rock', 'Light World Water Drop', 'East Hyrule Teleporter', - 'South Hyrule Teleporter', 'Kakariko Teleporter', 'Elder House (East)', 'Elder House (West)', - 'North Fairy Cave', 'North Fairy Cave Drop', 'Lost Woods Gamble', 'Snitch Lady (East)', - 'Snitch Lady (West)', 'Tavern (Front)', 'Kakariko Yard Bush (South)', 'Kakariko Southwest Bush (North)', - 'Kakariko Shop', 'Long Fairy Cave', 'Good Bee Cave', '20 Rupee Cave', 'Lake Hylia Shop', - 'Waterfall of Wishing', 'Hyrule Castle Main Gate', '50 Rupee Cave', 'Bonk Fairy (Light)', - 'Fortune Teller (Light)', 'Lake Hylia Fairy', 'Light Hype Fairy', 'Desert Fairy', - 'Lumberjack House', 'Lake Hylia Fortune Teller', 'Kakariko Gamble Game', 'Castle Gate Teleporter']: + for entrance in ['Links House SC', 'Links House ES', 'Central Bonk Rocks SW', 'Hyrule Castle WN', 'Hyrule Castle ES', + 'Bonk Fairy (Light)', 'Hyrule Castle Main Gate (South)', 'Hyrule Castle Main Gate (North)', 'Hyrule Castle Ledge Drop']: add_rule(world.get_entrance(entrance, player), lambda state: state.has('Zelda Delivered', player)) # don't allow bombs to get past here before zelda is rescued @@ -1447,8 +1633,8 @@ def set_big_bomb_rules(world, player): 'Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Hint', 'Mire Shed', - 'Dark Desert Hint', - 'Dark Desert Fairy', + 'Mire Hint', + 'Mire Fairy', 'Misery Mire'] Northern_DW_entrances = ['Brewery', 'C-Shaped House', @@ -1513,7 +1699,7 @@ def set_big_bomb_rules(world, player): 'Desert Palace Entrance (South)', 'Checkerboard Cave'] - set_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('East Dark World', 'Region', player) and state.can_reach('Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player)) + set_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_reach('Pyramid Area', 'Region', player) and state.can_reach('Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player)) # crossing peg bridge starting from the southern dark world def cross_peg_bridge(state): @@ -1594,7 +1780,7 @@ def set_big_bomb_rules(world, player): # 1. Have mire access, Mirror to reach locations, return via mirror spot, move to center of desert, mirror again and then basic routes # 2. flute then basic routes # -> (Mire access and M) or Flute) and BR - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: ((state.can_reach('Dark Desert', 'Region', player) and state.has_Mirror(player)) or state.can_flute(player)) and basic_routes(state)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: ((state.can_reach('Mire Area', 'Region', player) and state.has_Mirror(player)) or state.can_flute(player)) and basic_routes(state)) elif bombshop_entrance.name == 'Old Man Cave (West)': # 1. Lift rock then basic_routes # 2. flute then basic_routes @@ -1604,12 +1790,12 @@ def set_big_bomb_rules(world, player): # 1. flute then basic routes # 2. (has west dark world access) use existing mirror spot (required Pearl), mirror again off ledge # -> (Flute or (M and P and West Dark World access) and BR - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or (state.can_reach('West Dark World', 'Region', player) and state.has_Pearl(player) and state.has_Mirror(player))) and basic_routes(state)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or (state.can_reach('Village of Outcasts Area', 'Region', player) and state.has_Pearl(player) and state.has_Mirror(player))) and basic_routes(state)) elif bombshop_entrance.name in Mirror_from_SDW_entrances: # 1. flute then basic routes # 2. (has South dark world access) use existing mirror spot, mirror again off ledge # -> (Flute or (M and South Dark World access) and BR - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or (state.can_reach('South Dark World', 'Region', player) and state.has_Mirror(player))) and basic_routes(state)) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: (state.can_flute(player) or (state.can_reach('Big Bomb Shop Area', 'Region', player) and state.has_Mirror(player))) and basic_routes(state)) elif bombshop_entrance.name == 'Dark Potion Shop': # 1. walk down by lifting rock: needs gloves and pearl` # 2. walk down by hammering peg: needs hammer and pearl @@ -1736,8 +1922,8 @@ def set_inverted_big_bomb_rules(world, player): 'Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Hint', 'Mire Shed', - 'Dark Desert Hint', - 'Dark Desert Fairy', + 'Mire Hint', + 'Mire Fairy', 'Misery Mire', 'Red Shield Shop'] LW_bush_entrances = ['Bush Covered House', @@ -1748,7 +1934,7 @@ def set_inverted_big_bomb_rules(world, player): 'Spectacle Rock Cave (Bottom)'] set_rule(world.get_entrance('Pyramid Fairy', player), - lambda state: state.can_reach('East Dark World', 'Region', player) and state.can_reach('Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player)) + lambda state: state.can_reach('Pyramid Area', 'Region', player) and state.can_reach('Big Bomb Shop', 'Region', player) and state.has('Crystal 5', player) and state.has('Crystal 6', player)) # Key for below abbreviations: # P = pearl @@ -1768,25 +1954,25 @@ def set_inverted_big_bomb_rules(world, player): elif bombshop_entrance.name in Northern_DW_entrances: # You can just fly with the Flute, you can take a long walk with Mitts and Hammer, # or you can leave a Mirror portal nearby and then walk to the castle to Mirror again. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has_Mirror(player) and state.can_reach('Light World', 'Region', player))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has_Mirror(player) and state.can_reach('Hyrule Castle Area', 'Region', player))) elif bombshop_entrance.name in Southern_DW_entrances: # This is the same as north DW without the Mitts rock present. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Hammer', player) or state.can_flute(player) or (state.has_Mirror(player) and state.can_reach('Light World', 'Region', player))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has('Hammer', player) or state.can_flute(player) or (state.has_Mirror(player) and state.can_reach('Hyrule Castle Area', 'Region', player))) elif bombshop_entrance.name in Isolated_DW_entrances: # There's just no way to escape these places with the bomb and no Flute. add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player)) elif bombshop_entrance.name in LW_walkable_entrances: # You can fly with the flute, or leave a mirror portal and walk through the light world - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) or (state.has_Mirror(player) and state.can_reach('Light World', 'Region', player))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute(player) or (state.has_Mirror(player) and state.can_reach('Hyrule Castle Area', 'Region', player))) elif bombshop_entrance.name in LW_bush_entrances: # These entrances are behind bushes in LW so you need either Pearl or the tools to solve NDW bomb shop locations. add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and (state.can_flute(player) or state.has_Pearl(player) or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)))) elif bombshop_entrance.name == 'Dark World Shop': # This is mostly the same as NDW but the Mirror path requires the Pearl, or using the Hammer - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has_Mirror(player) and state.can_reach('Light World', 'Region', player) and (state.has_Pearl(player) or state.has('Hammer', player)))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has_Mirror(player) and state.can_reach('Hyrule Castle Area', 'Region', player) and (state.has_Pearl(player) or state.has('Hammer', player)))) elif bombshop_entrance.name == 'Bumper Cave (Bottom)': # This is mostly the same as NDW but the Mirror path requires being able to lift a rock. - add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has_Mirror(player) and state.can_lift_rocks(player) and state.can_reach('Light World', 'Region', player))) + add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.can_flute or (state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.has_Mirror(player) and state.can_lift_rocks(player) and state.can_reach('Hyrule Castle Area', 'Region', player))) elif bombshop_entrance.name == 'Old Man Cave (West)': # The three paths back are Mirror and DW walk, Mirror and Flute, or LW walk and then Mirror. add_rule(world.get_entrance('Pyramid Fairy', player), lambda state: state.has_Mirror(player) and ((state.can_lift_heavy_rocks(player) and state.has('Hammer', player)) or (state.can_lift_rocks(player) and state.has_Pearl(player)) or state.can_flute(player))) @@ -1824,6 +2010,8 @@ def set_inverted_big_bomb_rules(world, player): def set_bunny_rules(world, player, inverted): # regions for the exits of multi-entrace caves/drops that bunny cannot pass # Note spiral cave may be technically passible, but it would be too absurd to require since OHKO mode is a thing. + all_single_exit_dungeons = ['Eastern Palace', 'Tower of Hera', 'Castle Tower', 'Palace of Darkness', 'Swamp Palace', 'Thieves Town', 'Ice Palace', 'Misery Mire', 'Ganons Tower'] + hmg_single_exit_dungeons = [d for d in all_single_exit_dungeons if d not in ['Tower of Hera', 'Misery Mire', 'Thieves Town']] bunny_impassable_caves = ['Bumper Cave (top)', 'Bumper Cave (bottom)', 'Two Brothers House', 'Hookshot Cave (Middle)', 'Pyramid', 'Spiral Cave (Top)', 'Fairy Ascension Cave (Drop)'] bunny_accessible_locations = ['Link\'s Uncle', 'Sahasrahla', 'Sick Kid', 'Lost Woods Hideout', 'Lumberjack Tree', @@ -1870,9 +2058,9 @@ def set_bunny_rules(world, player, inverted): def get_rule_to_add(region, location=None, connecting_entrance=None): # In OWG, a location can potentially be superbunny-mirror accessible or # bunny revival accessible. - if world.logic[player] == 'owglitches': + if world.logic[player] in ['owglitches', 'hybridglitches']: if region.type != RegionType.Dungeon \ - and (location is None or location.name not in OverworldGlitchRules.get_superbunny_accessible_locations()) \ + and (location is None or location.name not in OverworldGlitchRules.superbunny_accessible_locations) \ and not is_link(region): return lambda state: state.has_Pearl(player) else: @@ -1890,26 +2078,32 @@ def set_bunny_rules(world, player, inverted): # for each such entrance a new option is added that consist of: # a) being able to reach it, and # b) being able to access all entrances from there to `region` - queue = deque([(region, [], {region})]) + queue = deque([(region, [], {region}, [region])]) seen_sets = set([frozenset({region})]) while queue: - (current, path, seen) = queue.popleft() + (current, path, seen, region_path) = queue.popleft() for entrance in current.entrances: + if entrance.door and entrance.door.blocked: + continue new_region = entrance.parent_region new_seen = seen.union({new_region}) if new_region.type in (RegionType.Cave, RegionType.Dungeon) and new_seen in seen_sets: continue new_path = path + [entrance.access_rule] + new_region_path = region_path + [new_region] seen_sets.add(frozenset(new_seen)) if not is_link(new_region): - if world.logic[player] == 'owglitches': + if world.logic[player] in ['owglitches', 'hybridglitches']: if region.type == RegionType.Dungeon and new_region.type != RegionType.Dungeon: - if entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances(): + if entrance.name in OverworldGlitchRules.invalid_mirror_bunny_entrances: continue + # todo - Is this a bunny pocketable entrance? + # Is there an entrance reachable to arm bunny pocket? For now, assume there is if entrance.name in drop_dungeon_entrances: lobby = entrance.connected_region else: - lobby = next(exit.connected_region for exit in current.exits if exit.connected_region.type == RegionType.Dungeon) + portal_regions = [world.get_region(reg, player) for reg in region.dungeon.regions if reg.endswith('Portal')] + lobby = next(reg.connected_region for portal_reg in portal_regions for reg in portal_reg.exits if reg.name.startswith('Enter ')) if lobby.name in bunny_revivable_entrances: possible_options.append(path_to_access_rule(new_path, entrance)) elif lobby.name in superbunny_revivable_entrances: @@ -1918,27 +2112,29 @@ def set_bunny_rules(world, player, inverted): possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_sword(player)], entrance)) continue elif region.type == RegionType.Cave and new_region.type != RegionType.Cave: - if entrance.name in OverworldGlitchRules.get_invalid_mirror_bunny_entrances(): + if entrance.name in OverworldGlitchRules.invalid_mirror_bunny_entrances: continue - if region.name in OverworldGlitchRules.get_sword_required_superbunny_mirror_regions(): + # todo - Is this a bunny pocketable entrance? + # Is there an entrance reachable to arm bunny pocket? For now, assume there is + if region.name in OverworldGlitchRules.sword_required_superbunny_mirror_regions: possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_sword(player)], entrance)) - elif region.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_regions(): + elif region.name in OverworldGlitchRules.boots_required_superbunny_mirror_regions: possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_Boots(player)], entrance)) - elif location and location.name in OverworldGlitchRules.get_superbunny_accessible_locations(): - if location.name in OverworldGlitchRules.get_boots_required_superbunny_mirror_locations(): + elif location and location.name in OverworldGlitchRules.superbunny_accessible_locations: + if location.name in OverworldGlitchRules.boots_required_superbunny_mirror_locations: possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player) and state.has_Boots(player)], entrance)) elif region.name == 'Kakariko Well (top)': possible_options.append(path_to_access_rule(new_path, entrance)) else: possible_options.append(path_to_access_rule(new_path + [lambda state: state.has_Mirror(player)], entrance)) continue - elif region.name == 'Superbunny Cave (Top)' and new_region.name == 'Superbunny Cave (Bottom)' and location and location.name in OverworldGlitchRules.get_superbunny_accessible_locations(): + elif region.name == 'Superbunny Cave (Top)' and new_region.name == 'Superbunny Cave (Bottom)' and location and location.name in OverworldGlitchRules.superbunny_accessible_locations: possible_options.append(path_to_access_rule(new_path, entrance)) else: continue if is_bunny(new_region): # todo: if not owg or hmg and entrance is in bunny_impassible_doors, then skip this nonsense? - queue.append((new_region, new_path, new_seen)) + queue.append((new_region, new_path, new_seen, new_region_path)) else: # we have reached pure light world, so we have a new possible option possible_options.append(path_to_access_rule(new_path, entrance)) @@ -1959,7 +2155,7 @@ def set_bunny_rules(world, player, inverted): for ent_name in bunny_impassible_doors: bunny_exit = world.get_entrance(ent_name, player) - if is_bunny(bunny_exit.parent_region): + if bunny_exit.connected_region and is_bunny(bunny_exit.parent_region): add_rule(bunny_exit, get_rule_to_add(bunny_exit.parent_region)) for ent_name in bunny_impassible_if_trapped: @@ -2064,8 +2260,9 @@ bunny_impassible_doors = { 'Thieves Hellway Blue Barrier', 'Thieves Hellway Crystal Blue Barrier', 'Thieves Attic ES', 'Thieves Basement Block Path', 'Thieves Blocked Entry Path', 'Thieves Conveyor Bridge Block Path', 'Thieves Conveyor Block Path', 'Ice Lobby WS', 'Ice Cross Left Push Block', 'Ice Cross Bottom Push Block Left', - 'Ice Bomb Drop Hole', 'Ice Pengator Switch WS', 'Ice Pengator Switch ES', 'Ice Big Key Push Block', 'Ice Stalfos Hint SE', 'Ice Bomb Jump EN', - 'Ice Pengator Trap NE', 'Ice Hammer Block ES', 'Ice Tongue Pull WS', 'Ice Freezors Bomb Hole', 'Ice Tall Hint WS', + 'Ice Bomb Drop Hole', 'Ice Pengator Switch WS', 'Ice Pengator Switch ES', 'Ice Big Key Push Block', + 'Ice Stalfos Hint SE', 'Ice Bomb Jump EN', 'Ice Pengator Trap NE', 'Ice Hammer Block ES', 'Ice Right H Path', + 'Ice Bomb Drop Path', 'Ice Tongue Pull WS', 'Ice Freezors Bomb Hole', 'Ice Tall Hint WS', 'Ice Hookshot Ledge Path', 'Ice Hookshot Balcony Path', 'Ice Many Pots SW', 'Ice Many Pots WS', 'Ice Crystal Right Blue Hole', 'Ice Crystal Left Blue Barrier', 'Ice Big Chest Landing Push Blocks', 'Ice Backwards Room Hole', 'Ice Switch Room SE', 'Ice Antechamber NE', 'Ice Antechamber Hole', 'Mire Lobby Gap', @@ -2097,17 +2294,25 @@ bunny_impassible_doors = { 'GT Validation Block Path' } + +# these should generally match trap_door_exceptions unless the switch is in the open/push block bunny_impassible_if_trapped = { 'Hyrule Dungeon Armory Interior Key Door N', 'Eastern Pot Switch WN', 'Eastern Lobby NW', - 'Eastern Lobby NE', 'Desert Compass Key Door WN', 'Tower Circle of Pots ES', 'PoD Mimics 2 SW', - 'PoD Middle Cage S', 'Swamp Push Statue S', 'Skull 2 East Lobby WS', 'Skull Torch Room WS', - 'Thieves Conveyor Maze WN', 'Thieves Conveyor Maze SW', 'Thieves Blocked Entry SW', 'Ice Bomb Jump NW', - 'Ice Tall Hint EN', 'Ice Switch Room ES', 'Ice Switch Room NE', 'Mire Cross SW', - 'Mire Tile Room SW', 'Mire Tile Room ES', 'TR Twin Pokeys NW', 'TR Torches WN', 'GT Hope Room WN', + 'Eastern Lobby NE', 'Eastern Courtyard Ledge S', 'Desert Compass Key Door WN', 'Tower Circle of Pots ES', + 'PoD Mimics 1 SW', 'PoD Mimics 2 SW', 'PoD Middle Cage S', 'PoD Lobby N', 'Swamp Push Statue S', + 'Skull 2 East Lobby WS', 'Skull Torch Room WS', 'Thieves Conveyor Maze WN', 'Thieves Conveyor Maze SW', + 'Thieves Blocked Entry SW', 'Ice Bomb Jump NW', + 'Ice Tall Hint EN', 'Ice Tall Hint SE', 'Ice Switch Room ES', 'Ice Switch Room NE', 'Mire Cross SW', + 'Mire Tile Room SW', 'Mire Tile Room ES', 'TR Tongue Pull WS', 'TR Twin Pokeys NW', 'TR Torches WN', 'GT Hope Room WN', 'GT Speed Torch NE', 'GT Speed Torch WS', 'GT Torch Cross WN', 'GT Hidden Spikes SE', 'GT Conveyor Cross EN', 'GT Speed Torch WN', 'Ice Lobby SE' } +def add_hmg_key_logic_rules(world, player): + for toh_loc in world.key_logic[player]['Tower of Hera'].bk_restricted: + set_always_allow(world.get_location(toh_loc.name, player), allow_big_key_in_big_chest('Big Key (Tower of Hera)', player)) + set_always_allow(world.get_location('Swamp Palace - Entrance', player), allow_big_key_in_big_chest('Big Key (Swamp Palace)', player)) + def add_key_logic_rules(world, player): key_logic = world.key_logic[player] diff --git a/TestSuite.py b/TestSuite.py index ede5ae53..d8a1f3e1 100644 --- a/TestSuite.py +++ b/TestSuite.py @@ -49,6 +49,9 @@ def main(args=None): test("Shopsanity", "--shuffle vanilla --shopsanity") test("Simple ", "--shuffle simple") test("Full ", "--shuffle full") + test("Lite ", "--shuffle lite") + test("Lean ", "--shuffle lean") + test("Swapped ", "--shuffle swapped") test("Crossed ", "--shuffle crossed") test("Insanity ", "--shuffle insanity") test("OWG ", "--logic owglitches") diff --git a/Text.py b/Text.py index 796723d3..4d95f516 100644 --- a/Text.py +++ b/Text.py @@ -478,7 +478,7 @@ class Credits(object): ], 'pedestal': [ SceneSmallCreditLine(19, 'and the master sword'), - SceneSmallAltCreditLine(21, 'sleeps again...'), + SceneSmallAltCreditLine(21, 'sleeps again···'), SceneLargeCreditLine(23, 'Forever!'), ], } @@ -826,6 +826,7 @@ class CharTextMapper(object): class RawMBTextMapper(CharTextMapper): char_map = {' ': 0xFF, + '≥': 0x99, # Cursor '『': 0xC4, '』': 0xC5, '?': 0xC6, @@ -834,22 +835,15 @@ class RawMBTextMapper(CharTextMapper): '-': 0xC9, "🡄": 0xCA, "🡆": 0xCB, - '…': 0xCC, + '…': 0x9F, '.': 0xCD, '~': 0xCE, '~': 0xCE, + ':': 0xEA, '@': [0x6A], # Links name (only works if compressed) - '>': [0x00, 0xD2, 0x00, 0xD3], # Link's face - "'": 0xD8, + '>': [0x00, 0x9B, 0x00, 0x9C], # Link's face + "'": 0x9D, '’': 0xD8, - '%': 0xDD, # Hylian Bird - '^': 0xDE, # Hylian Ankh - '=': 0xDF, # Hylian Wavy Lines - '↑': 0xE0, - '↓': 0xE1, - '→': 0xE2, - '←': 0xE3, - '≥': 0xE4, # Cursor '¼': [0x00, 0xE5, 0x00, 0xE7], # ¼ heart '½': [0x00, 0xE6, 0x00, 0xE7], # ½ heart '¾': [0x00, 0xE8, 0x00, 0xE9], # ¾ heart @@ -1034,22 +1028,29 @@ class RawMBTextMapper(CharTextMapper): "司": 0x0D, "書": 0x0E, "戻": 0x0F, - "様": 0x10, - "子": 0x11, - "湖": 0x12, - "達": 0x13, - "彼": 0x14, - "女": 0x15, - "言": 0x16, - "祭": 0x17, - "早": 0x18, - "雨": 0x19, - "剣": 0x1A, - "盾": 0x1B, - "解": 0x1C, - "抜": 0x1D, - "者": 0x1E, - "味": 0x1F, + "%": 0x10, # Hylian Bird + "^": 0x11, # Hylian Ankh + "=": 0x12, # Hylian Wavy Lines + "↑": 0x13, + "↓": 0x14, + "→": 0x15, + "←": 0x16, + #"様": 0x10, + #"子": 0x11, + #"湖": 0x12, + #"達": 0x13, + #"彼": 0x14, + #"女": 0x15, + #"言": 0x16, + #"祭": 0x17, + #"早": 0x18, + #"雨": 0x19, + #"剣": 0x1A, + #"盾": 0x1B, + #"解": 0x1C, + #"抜": 0x1D, + #"者": 0x1E, + #"味": 0x1F, "方": 0x20, "無": 0x21, "事": 0x22, @@ -1275,7 +1276,7 @@ class RawMBTextMapper(CharTextMapper): "月": 0xFE, "姫": 0xFF} alpha_offset = 0x49 - alpha_lower_offset = -0x31 + alpha_lower_offset = 0x6F number_offset = 0x70 @classmethod @@ -1298,7 +1299,7 @@ class RawMBTextMapper(CharTextMapper): class GoldCreditMapper(CharTextMapper): char_map = {' ': 0x9F, - ',': 0x34, + ',': 0x34, # apostrophe/comma top "'": 0x35, '-': 0x36, '.': 0x37,} @@ -1319,21 +1320,23 @@ class RedCreditMapper(CharTextMapper): class LargeCreditTopMapper(CharTextMapper): char_map = {' ': 0x9F, - "'": 0x77, - '!': 0x78, - '.': 0xA0, - '#': 0xA1, - '/': 0xA2, - ':': 0xA3, - ',': 0xA4, - '?': 0xA5, - '=': 0xA6, - '"': 0xA7, - '-': 0xA8, - '·': 0xA9, - '•': 0xA9, - '◢': 0xAA, - '◣': 0xAB,} + "'": 0xD9, + '"': 0xDA, + '/': 0xDB, + '.': 0xDC, + ':': 0xDD, + '_': 0xDE, + '·': 0xDF, + '•': 0xDF, + '…': 0xE0, + '#': 0xE1, + '@': 0xE2, + '>': 0xE3, + '?': 0xE4, + '!': 0xE5, + '~': 0xE6, + ',': 0xE7, + '-': 0xE8,} alpha_offset = -0x04 alpha_lower_offset = -0x04 number_offset = 0x23 @@ -1341,21 +1344,23 @@ class LargeCreditTopMapper(CharTextMapper): class LargeCreditBottomMapper(CharTextMapper): char_map = {' ': 0x9F, - "'": 0x9D, - '!': 0x9E, - '.': 0xC0, - '#': 0xC1, - '/': 0xC2, - ':': 0xC3, - ',': 0xC4, - '?': 0xC5, - '=': 0xC6, - '"': 0xC7, - '-': 0xC8, - '·': 0xC9, - '•': 0xC9, - '◢': 0xCA, - '◣': 0xCB,} + "'": 0xEC, + '"': 0xED, + '/': 0xEE, + '.': 0xEF, + ':': 0xF0, + '_': 0xF1, + '·': 0xF2, + '•': 0xF2, + '…': 0xF3, + '#': 0xF4, + '@': 0xF5, + '>': 0xF6, + '?': 0xF7, + '!': 0xF8, + '~': 0xF9, + ',': 0xFA, + '-': 0xFB,} alpha_offset = 0x22 alpha_lower_offset = 0x22 number_offset = 0x49 @@ -1568,16 +1573,16 @@ class TextTable(object): def setDefaultText(self): text = self._text - text['set_cursor'] = bytearray([0xFB, 0xFC, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0xE4, 0xFE, 0x68]) - text['set_cursor2'] = bytearray([0xFB, 0xFC, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0xE4, 0xFE, 0x68]) + text['set_cursor'] = bytearray([0xFB, 0xFC, 0x00, 0xF9, 0xFF, 0xFF, 0xFF, 0xF8, 0xFF, 0xFF, 0x99, 0xFE, 0x68]) + text['set_cursor2'] = bytearray([0xFB, 0xFC, 0x00, 0xF8, 0xFF, 0xFF, 0xFF, 0xF9, 0xFF, 0xFF, 0x99, 0xFE, 0x68]) text['game_over_menu'] = CompressedTextMapper.convert("{SPEED0}\nSave-Continue\nSave-Quit\nContinue", False) text['var_test'] = CompressedTextMapper.convert("0= ᚋ, 1= ᚌ\n2= ᚍ, 3= ᚎ", False) text['follower_no_enter'] = CompressedTextMapper.convert("Can't you take me some place nice.") - text['choice_1_3'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xE4, 0xF8, 0xFF, 0xF9, 0xFF, 0xFE, 0x71]) - text['choice_2_3'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0xE4, 0xF9, 0xFF, 0xFE, 0x71]) - text['choice_3_3'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0xE4, 0xFE, 0x71]) - text['choice_1_2'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xE4, 0xF8, 0xFF, 0xFE, 0x72]) - text['choice_2_2'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0xE4, 0xFE, 0x72]) + text['choice_1_3'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0x99, 0xF8, 0xFF, 0xF9, 0xFF, 0xFE, 0x71]) + text['choice_2_3'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0x99, 0xF9, 0xFF, 0xFE, 0x71]) + text['choice_3_3'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0xFF, 0xF9, 0x99, 0xFE, 0x71]) + text['choice_1_2'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0x99, 0xF8, 0xFF, 0xFE, 0x72]) + text['choice_2_2'] = bytearray([0xFB, 0xFC, 0x00, 0xF7, 0xFF, 0xF8, 0x99, 0xFE, 0x72]) text['uncle_leaving_text'] = CompressedTextMapper.convert("I'm just going out for a pack of smokes.") text['uncle_dying_sewer'] = CompressedTextMapper.convert("I've fallen and I can't get up, take this.") text['tutorial_guard_1'] = CompressedTextMapper.convert("Only adults should travel at night.") @@ -1640,7 +1645,7 @@ class TextTable(object): text['sign_outside_magic_shop'] = CompressedTextMapper.convert("Welcome to the Magic Shoppe") # 40 text['sign_death_mountain_cave_back'] = CompressedTextMapper.convert("Cave away from sky cabbages") - text['sign_east_of_links_house'] = CompressedTextMapper.convert("↓ Lake Hylia\n\n Also, a shop") + text['sign_east_of_links_house'] = CompressedTextMapper.convert("↓Lake Hylia\n\n Also, a shop") text['sign_south_of_lumberjacks'] = CompressedTextMapper.convert("← Kakariko\n Village") text['sign_east_of_desert'] = CompressedTextMapper.convert("← Desert\n\n It's hot.") text['sign_east_of_sanctuary'] = CompressedTextMapper.convert("↑→ Potions!\n\nWish waterfall") @@ -1772,7 +1777,10 @@ class TextTable(object): text['telepathic_tile_misery_mire'] = CompressedTextMapper.convert("{NOBORDER}\nLighting 4 torches will open your way forward!") text['hylian_text_2'] = CompressedTextMapper.convert("%%^= %==%\n ^ =%^=\n==%= ^^%^") text['desert_entry_translated'] = CompressedTextMapper.convert("Kneel before this stone, and magic will move around you.") - text['telepathic_tile_under_ganon'] = CompressedTextMapper.convert("Doors Async League winners\n{HARP}\n ~~~2022~~~\nAndy\n\n ~~~2021~~~\nprdwong") + text['telepathic_tile_under_ganon'] = CompressedTextMapper.convert("Doors Async League winners\n{HARP}\n" + " ~~~2023~~~\nEriror\n\n" + " ~~~2022~~~\nAndy\n\n" + " ~~~2021~~~\nprdwong") text['telepathic_tile_palace_of_darkness'] = CompressedTextMapper.convert("{NOBORDER}\nThis is a funny looking Enemizer") # C0 text['telepathic_tile_desert_bonk_torch_room'] = CompressedTextMapper.convert("{NOBORDER}\nThings can be knocked down, if you fancy yourself a dashing dude.") @@ -1782,7 +1790,13 @@ class TextTable(object): text['telepathic_tile_ice_entrance'] = CompressedTextMapper.convert("{NOBORDER}\nYou can use Fire Rod or Bombos to pass.") text['telepathic_tile_ice_stalfos_knights_room'] = CompressedTextMapper.convert("{NOBORDER}\nKnock 'em down and then bomb them dead.") text['telepathic_tile_tower_of_hera_entrance'] = CompressedTextMapper.convert("{NOBORDER}\nThis is a bad place, with a guy who will make you fall…\n\n\na lot.") - text['houlihan_room'] = CompressedTextMapper.convert("Randomizer tournament winners\n{HARP}\n ~~~2021~~~\nDaaanty\n\n ~~~2019~~~\nJet082\n\n ~~~2018~~~\nAndy\n\n ~~~2017~~~\nA: ajneb174\nS: ajneb174") + text['houlihan_room'] = CompressedTextMapper.convert("Randomizer tournament winners\n{HARP}\n" + " ~~~2023~~~\nnGanonsGoneWild\n\n" + " ~~~2022~~~\nObscure\n\n" + " ~~~2021~~~\nDaaanty\n\n" + " ~~~2019~~~\nJet082\n\n" + " ~~~2018~~~\nAndy\n\n" + " ~~~2017~~~\nA: ajneb174\nS: ajneb174") text['caught_a_bee'] = CompressedTextMapper.convert("Caught a Bee\n ≥ Keep\n Release\n{CHOICE}") text['caught_a_fairy'] = CompressedTextMapper.convert("Caught Fairy!\n ≥ Keep\n Release\n{CHOICE}") text['no_empty_bottles'] = CompressedTextMapper.convert("Whoa, bucko!\nNo empty bottles.") @@ -2010,7 +2024,10 @@ class TextTable(object): text['ganon_fall_in_alt'] = CompressedTextMapper.convert("You think you are ready to face me?\n\nI will not die unless you complete your goals. Dingus!") text['ganon_phase_3_alt'] = CompressedTextMapper.convert("Got wax in your ears? I cannot die!") # 190 - text['sign_east_death_mountain_bridge'] = CompressedTextMapper.convert("Glitched\ntournament\nwinners\n{HARP}\n~~~HMG 2021~~~\nKrithel\n\n~~~OWG 2019~~~\nGlan\n\n~~~OWG 2018~~~\nChristosOwen\nthe numpty") + text['sign_east_death_mountain_bridge'] = CompressedTextMapper.convert("Glitched\ntournament\nwinners\n{HARP}\n" + "~~~HMG 2021~~~\nKrithel\n\n" + "~~~OWG 2019~~~\nGlan\n\n" + "~~~OWG 2018~~~\nChristosOwen\nthe numpty") text['fish_money'] = CompressedTextMapper.convert("It's a secret to everyone.") text['sign_ganons_tower'] = CompressedTextMapper.convert("You need all 7 crystals to enter.") text['sign_ganon'] = CompressedTextMapper.convert("You need all 7 crystals to beat Ganon.") diff --git a/UnderworldGlitchRules.py b/UnderworldGlitchRules.py new file mode 100644 index 00000000..c01e242f --- /dev/null +++ b/UnderworldGlitchRules.py @@ -0,0 +1,278 @@ +from BaseClasses import Entrance +import Rules +from OverworldGlitchRules import create_no_logic_connections + +kikiskip_spots = [("Kiki Skip", "Spectacle Rock Cave (Bottom)", "Palace of Darkness Portal")] + +mireheraswamp_spots = [ + ("Mire to Hera Clip", "Mire Torches Top", "Hera Portal"), + ("Hera to Swamp Clip", "Mire Torches Top", "Swamp Portal"), +] + +icepalace_spots = [("Ice Lobby Clip", "Ice Portal", "Ice Bomb Drop")] + +thievesdesert_spots = [ + ("Thieves to Desert Clip", "Thieves Attic", "Desert West Portal"), + ("Thieves to Desert Clip", "Thieves Attic", "Desert South Portal"), + ("Thieves to Desert Clip", "Thieves Attic", "Desert East Portal"), +] + +specrock_spots = [("Spec Rock Clip", "Spectacle Rock Cave (Peak)", "Spectacle Rock Cave (Top)")] + +paradox_spots = [("Paradox Front Teleport", "Paradox Cave Front", "Paradox Cave Chest Area")] + + +# We need to make connectors at a separate time from the connections, because of how dungeons are linked to regions +kikiskip_connectors = [("Kiki Skip", "Spectacle Rock Cave (Bottom)", "Palace of Darkness Exit")] + + +mireheraswamp_connectors = [ + ("Mire to Hera Clip", "Mire Torches Top", "Tower of Hera Exit"), + ("Mire to Hera Clip", "Mire Torches Top", "Swamp Palace Exit"), +] + + +thievesdesert_connectors = [ + ("Thieves to Desert Clip", "Thieves Attic", "Desert Palace Exit (West)"), + ("Thieves to Desert Clip", "Thieves Attic", "Desert Palace Exit (South)"), + ("Thieves to Desert Clip", "Thieves Attic", "Desert Palace Exit (East)"), +] + +specrock_connectors = [ + ("Spec Rock Clip", "Spectacle Rock Cave (Peak)", "Spectacle Rock Cave Exit (Top)"), + ("Spec Rock Clip", "Spectacle Rock Cave (Peak)", "Spectacle Rock Cave Exit"), +] + + +# Create connections between dungeons/locations +def create_hybridmajor_connections(world, player): + for spots in [ + kikiskip_spots, + mireheraswamp_spots, + icepalace_spots, + thievesdesert_spots, + specrock_spots, + paradox_spots, + ]: + create_no_logic_connections(player, world, spots) + + +# Turn dungeons into connectors +def create_hybridmajor_connectors(world, player): + for connectors in [ + kikiskip_connectors, + mireheraswamp_connectors, + thievesdesert_connectors, + specrock_connectors, + ]: + new_connectors = [(connector[0], connector[1], world.get_entrance(connector[2], player).connected_region) for connector in connectors] + create_no_logic_connections(player, world, new_connectors) + + +# For some entrances, we need to fake having pearl, because we're in fake DW/LW. +# This creates a copy of the input state that has Moon Pearl. +def fake_pearl_state(state, player): + if state.has("Moon Pearl", player): + return state + fake_state = state.copy() + fake_state.prog_items["Moon Pearl", player] += 1 + return fake_state + + +# Sets the rules on where we can actually go using this clip. +# Behavior differs based on what type of ER shuffle we're playing. +def dungeon_reentry_rules(world, player, clip: Entrance, dungeon_region: str, dungeon_exit: str): + fix_dungeon_exits = world.fix_palaceofdarkness_exit[player] + fix_fake_worlds = world.fix_fake_world[player] + + dungeon_entrance = [r for r in world.get_region(dungeon_region, player).entrances if r.name != clip.name][0] + if not fix_dungeon_exits: # vanilla, simple, restricted, dungeonssimple; should never have fake worlds fix + # Dungeons are only shuffled among themselves. We need to check SW, MM, and AT because they can't be reentered trivially. + + # entrance doesn't exist until you fire rod it from the other side + if dungeon_entrance.name == "Skull Woods Final Section": + Rules.set_rule(clip, lambda state: False) + + elif dungeon_entrance.name == "Misery Mire": + if world.swords[player] == "swordless": + Rules.add_rule(clip, lambda state: state.has_misery_mire_medallion(player)) + else: + Rules.add_rule(clip, lambda state: state.has_sword(player) and state.has_misery_mire_medallion(player)) + + elif dungeon_entrance.name == "Agahnims Tower": + Rules.add_rule( + clip, + lambda state: state.has("Cape", player) + or state.has_beam_sword(player) + or state.has("Beat Agahnim 1", player), + ) + + # Then we set a restriction on exiting the dungeon, so you can't leave unless you got in normally. + Rules.add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state)) + elif not fix_fake_worlds: # full, dungeonsfull; fixed dungeon exits, but no fake worlds fix + # Entry requires the entrance's requirements plus a fake pearl, but you don't gain logical access to the surrounding region. + Rules.add_rule(clip, lambda state: dungeon_entrance.access_rule(fake_pearl_state(state, player))) + # exiting restriction + Rules.add_rule(world.get_entrance(dungeon_exit, player), lambda state: dungeon_entrance.can_reach(state)) + + # Otherwise, the shuffle type is lean, lite, crossed, or insanity; all of these do not need additional rules on where we can go, + # since the clip links directly to the exterior region. + + +def underworld_glitches_rules(world, player): + # Ice Palace Entrance Clip, needs bombs or cane of somaria to exit bomb drop room + Rules.add_rule( + world.get_entrance("Ice Bomb Drop SE", player), + lambda state: state.can_dash_clip(world.get_region("Ice Lobby", player), player) + and (state.can_use_bombs(player) or state.has("Cane of Somaria", player)), + combine="or", + ) + + # Kiki Skip + kks = world.get_entrance("Kiki Skip", player) + Rules.set_rule(kks, lambda state: state.can_bomb_clip(kks.parent_region, player)) + dungeon_reentry_rules(world, player, kks, "Palace of Darkness Portal", "Palace of Darkness Exit") + + # Mire -> Hera -> Swamp + def mire_clip(state): + return state.can_reach("Mire Torches Top", "Region", player) and state.can_dash_clip( + world.get_region("Mire Torches Top", player), player + ) + + def hera_clip(state): + return state.can_reach("Hera 4F", "Region", player) and state.can_dash_clip( + world.get_region("Hera 4F", player), player + ) + + Rules.add_rule( + world.get_entrance("Hera Startile Corner NW", player), + lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), + combine="or", + ) + Rules.add_rule( + world.get_location("Tower of Hera - Big Chest", player), + lambda state: mire_clip(state) and state.has("Big Key (Misery Mire)", player), + combine="or", + ) + + mire_to_hera = world.get_entrance("Mire to Hera Clip", player) + mire_to_swamp = world.get_entrance("Hera to Swamp Clip", player) + Rules.set_rule(mire_to_hera, mire_clip) + Rules.set_rule(mire_to_swamp, lambda state: mire_clip(state) and state.has("Flippers", player)) + + # Using the entrances for various ER types. Hera -> Swamp never matters because you can only logically traverse with the mire keys + dungeon_reentry_rules(world, player, mire_to_hera, "Hera Lobby", "Tower of Hera Exit") + dungeon_reentry_rules(world, player, mire_to_swamp, "Swamp Lobby", "Swamp Palace Exit") + # We need to set _all_ swamp doors to be openable with mire keys, otherwise the small key can't be behind them - 6 keys because of Pots + # Flippers required for all of these doors to prevent locks when flooding + for door in [ + "Swamp Trench 1 Approach ES", + "Swamp Hammer Switch SW", + "Swamp Entrance Down Stairs", + "Swamp Pot Row WS", + "Swamp Trench 1 Key Ledge NW", + "Swamp Hub WN", + "Swamp Hub North Ledge N", + "Swamp Crystal Switch EN", + "Swamp Push Statue S", + "Swamp Waterway NW", + "Swamp T SW", + ]: + Rules.add_rule( + world.get_entrance(door, player), + lambda state: mire_clip(state) + and state.has("Small Key (Misery Mire)", player, count=6) + and state.has("Flippers", player), + combine="or", + ) + # Rules.add_rule(world.get_entrance(door, player), lambda state: mire_clip(state) and state.has('Flippers', player), combine="or") + + Rules.add_rule( + world.get_location("Trench 1 Switch", player), lambda state: mire_clip(state) or hera_clip(state), combine="or" + ) + + # Build the rule for SP moat. + # We need to be able to s+q to old man, then go to either Mire or Hera at either Hera or GT. + # First we require a certain type of entrance shuffle, then build the rule from its pieces. + if not world.swamp_patch_required[player]: + if world.shuffle[player] in [ + "vanilla", + "dungeonssimple", + "dungeonsfull", + "dungeonscrossed", + ]: + rule_map = { + "Mire Portal": (lambda state: state.can_reach("Mire Torches Top", "Entrance", player)), + "Hera Portal": (lambda state: state.can_reach("Hera Startile Corner NW", "Entrance", player)), + } + inverted = world.mode[player] == "inverted" + + def hera_rule(state): + return (state.has("Moon Pearl", player) or not inverted) and rule_map.get( + world.get_entrance("Tower of Hera", player).connected_region.name, lambda state: False + )(state) + + def gt_rule(state): + return (state.has("Moon Pearl", player) or inverted) and rule_map.get( + world.get_entrance(("Ganons Tower"), player).connected_region.name, lambda state: False + )(state) + + def mirrorless_moat_rule(state): + return ( + state.can_reach("Old Man S&Q", "Entrance", player) + and state.has("Flippers", player) + and mire_clip(state) + and (hera_rule(state) or gt_rule(state)) + ) + + Rules.add_rule( + world.get_entrance("Swamp Lobby Moat", player), lambda state: mirrorless_moat_rule(state), combine="or" + ) + + # Thieves -> Desert + Rules.add_rule( + world.get_entrance("Thieves to Desert Clip", player), + lambda state: state.can_dash_clip(world.get_region("Thieves Attic", player), player), + ) + dungeon_reentry_rules( + world, + player, + world.get_entrance("Thieves to Desert Clip", player), + "Desert West Portal", + "Desert Palace Exit (West)", + ) + dungeon_reentry_rules( + world, + player, + world.get_entrance("Thieves to Desert Clip", player), + "Desert South Portal", + "Desert Palace Exit (South)", + ) + dungeon_reentry_rules( + world, + player, + world.get_entrance("Thieves to Desert Clip", player), + "Desert East Portal", + "Desert Palace Exit (East)", + ) + + # Collecting left chests in Paradox Cave using a dash clip -> dash citrus, 1f right, teleport up + paradox_left_chests = ["Paradox Cave Lower - Far Left", "Paradox Cave Lower - Left", "Paradox Cave Lower - Middle"] + for location in paradox_left_chests: + Rules.add_rule( + world.get_location(location, player), + lambda state: state.can_dash_clip(world.get_location(location, player).parent_region, player), + "or", + ) + + # Collecting right chests in Paradox Cave using a dash clip on left side -> dash citrus, 1f right, teleport up, then hitting the switch + paradox_right_chests = ["Paradox Cave Lower - Right", "Paradox Cave Lower - Far Right"] + for location in paradox_right_chests: + Rules.add_rule( + world.get_location(location, player), + lambda state: ( + state.can_dash_clip(world.get_location(location, player).parent_region, player) + and state.can_hit_crystal(player) + ), + "or", + ) diff --git a/Utils.py b/Utils.py index d0bbb5fc..50ab1b84 100644 --- a/Utils.py +++ b/Utils.py @@ -6,6 +6,12 @@ import sys import xml.etree.ElementTree as ET from collections import defaultdict from math import factorial +import fileinput + +import urllib.request +import urllib.parse +import yaml +from pathlib import Path def int16_as_bytes(value): @@ -13,6 +19,11 @@ def int16_as_bytes(value): return [value & 0xFF, (value >> 8) & 0xFF] +def int24_as_bytes(value): + value = value & 0xFFFFFF + return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF] + + def int32_as_bytes(value): value = value & 0xFFFFFFFF return [value & 0xFF, (value >> 8) & 0xFF, (value >> 16) & 0xFF, (value >> 24) & 0xFF] @@ -696,10 +707,68 @@ def check_pots(): print(f'{pot.room}#{i+1} secret: {hex(secret_vram)} tile: {hex(tile_vram)}') +def find_and_replace(): + for data_line in fileinput.input('scratch.txt', inplace=True): + if '=' in data_line: + one, two = data_line.split(' = ') + number = int(two.strip()) + print(data_line.replace(two, hex(number))) + + +def load_yaml(path_list): + path = os.path.join(*path_list) + if os.path.exists(Path(path)): + with open(path, "r", encoding="utf-8") as f: + return yaml.load(f, Loader=yaml.SafeLoader) + elif urllib.parse.urlparse(path).scheme in ['http', 'https']: + return yaml.load(urllib.request.urlopen(path), Loader=yaml.FullLoader) + + +yaml_cache = {} + + +def load_cached_yaml(path_list): + path = os.path.join(*path_list) + if path in yaml_cache: + return yaml_cache[path] + else: + if os.path.exists(Path(path)): + with open(path, "r", encoding="utf-8") as f: + data = yaml.load(f, Loader=yaml.SafeLoader) + yaml_cache[path] = data + return data + elif urllib.parse.urlparse(path).scheme in ['http', 'https']: + data = yaml.load(urllib.request.urlopen(path), Loader=yaml.FullLoader) + yaml_cache[path] = data + return data + + +class bidict(dict): + def __init__(self, *args, **kwargs): + super(bidict, self).__init__(*args, **kwargs) + self.inverse = {} + for key, value in self.items(): + self.inverse.setdefault(value,[]).append(key) + + def __setitem__(self, key, value): + if key in self: + self.inverse[self[key]].remove(key) + super(bidict, self).__setitem__(key, value) + self.inverse.setdefault(value,[]).append(key) + + def __delitem__(self, key): + value = self[key] + self.inverse.setdefault(value,[]).remove(key) + if value in self.inverse and not self.inverse[value]: + del self.inverse[value] + super(bidict, self).__delitem__(key) + + if __name__ == '__main__': # make_new_base2current() # read_entrance_data(old_rom=sys.argv[1]) # room_palette_data(old_rom=sys.argv[1]) # extract_data_from_us_rom(sys.argv[1]) # extract_data_from_jp_rom(sys.argv[1]) - check_pots() + # check_pots() + find_and_replace() diff --git a/data/base2current.bps b/data/base2current.bps index 14084390..22fd6589 100644 Binary files a/data/base2current.bps and b/data/base2current.bps differ diff --git a/docs/SuperTrueIceRodHunt.yaml b/docs/SuperTrueIceRodHunt.yaml index 3b644bf9..d44e94c5 100644 --- a/docs/SuperTrueIceRodHunt.yaml +++ b/docs/SuperTrueIceRodHunt.yaml @@ -4,7 +4,7 @@ meta: settings: 1: door_shuffle: vanilla - dropshuffle: true + dropshuffle: keys experimental: true goal: ganon hints: false diff --git a/docs/customizer_example.yaml b/docs/customizer_example.yaml index acdf5188..508cca8b 100644 --- a/docs/customizer_example.yaml +++ b/docs/customizer_example.yaml @@ -7,7 +7,7 @@ meta: settings: 1: door_shuffle: basic - dropshuffle: true + dropshuffle: keys experimental: true goal: ganon hints: true diff --git a/docs/presets/async_doors_league/S4_Main.yaml b/docs/presets/async_doors_league/S4_Main.yaml new file mode 100644 index 00000000..6e27f1e5 --- /dev/null +++ b/docs/presets/async_doors_league/S4_Main.yaml @@ -0,0 +1,31 @@ +meta: + players: 1 + race: true +settings: + 1: + shopsanity: true + pottery: keys + dropshuffle: keys + keysanity: true + accessibility: locations + + goal: crystals + crystals_gt: random + + shuffle: crossed + shufflelinks: true + shuffletavern: true + + door_shuffle: crossed + intensity: 3 + door_type_mode: big + key_logic_algorithm: partial + + experimental: true + dungeon_counters: 'on' + + pseudoboots: true + hints: true + msu_resume: true + collection_rate: true + quickswap: true diff --git a/docs/presets/async_doors_league/S3_BombBag.yaml b/docs/presets/async_doors_league/prior/S3_BombBag.yaml similarity index 100% rename from docs/presets/async_doors_league/S3_BombBag.yaml rename to docs/presets/async_doors_league/prior/S3_BombBag.yaml diff --git a/docs/presets/async_doors_league/S3_Main.yaml b/docs/presets/async_doors_league/prior/S3_Main.yaml similarity index 94% rename from docs/presets/async_doors_league/S3_Main.yaml rename to docs/presets/async_doors_league/prior/S3_Main.yaml index 564d19d4..83d3affe 100644 --- a/docs/presets/async_doors_league/S3_Main.yaml +++ b/docs/presets/async_doors_league/prior/S3_Main.yaml @@ -14,7 +14,7 @@ settings: intensity: 3 door_type_mode: big pottery: keys - dropshuffle: true + dropshuffle: keys experimental: true dungeon_counters: 'on' hints: true diff --git a/docs/presets/async_doors_league/S3_PotteryLottery.yaml b/docs/presets/async_doors_league/prior/S3_PotteryLottery.yaml similarity index 100% rename from docs/presets/async_doors_league/S3_PotteryLottery.yaml rename to docs/presets/async_doors_league/prior/S3_PotteryLottery.yaml diff --git a/docs/presets/async_doors_league/S3_Standard.yaml b/docs/presets/async_doors_league/prior/S3_Standard.yaml similarity index 100% rename from docs/presets/async_doors_league/S3_Standard.yaml rename to docs/presets/async_doors_league/prior/S3_Standard.yaml diff --git a/mystery_example.yml b/mystery_example.yml index bd8cbbe2..b7ba403e 100644 --- a/mystery_example.yml +++ b/mystery_example.yml @@ -35,8 +35,9 @@ on: 1 off: 1 dropshuffle: - on: 1 - off: 1 + none: 4 + keys: 1 + underworld: 1 pottery: none: 8 keys: 1 @@ -71,6 +72,9 @@ simple: 2 restricted: 2 full: 2 + lite: 2 + lean: 2 + swapped: 2 crossed: 3 insanity: 1 open_pyramid: @@ -165,6 +169,10 @@ none: 3 shuffled: 2 random: 1 + any_enemy_logic: + none: 1 + allow_drops: 2 + allow_all: 3 hints: on: 1 off: 1 diff --git a/mystery_testsuite.yml b/mystery_testsuite.yml index c7250d2b..d3a83234 100644 --- a/mystery_testsuite.yml +++ b/mystery_testsuite.yml @@ -35,8 +35,9 @@ door_self_loops: on: 1 off: 1 dropshuffle: - on: 1 - off: 1 + none: 10 # fewer locations + keys: 1 + underworld: 1 pottery: none: 10 # fewer locations keys: 1 @@ -59,6 +60,9 @@ entrance_shuffle: simple: 1 restricted: 1 full: 1 + lite: 1 + lean: 1 + swapped: 1 crossed: 1 insanity: 1 shufflelinks: @@ -149,6 +153,7 @@ enemy_shuffle: # shouldn't affect generation shuffled: 1 random: 1 legacy: 0 +any_enemy_logic: allow_all # more interesting logic hints: on: 1 off: 1 diff --git a/resources/app/cli/args.json b/resources/app/cli/args.json index 0d6db740..6aedcad8 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -23,6 +23,7 @@ "noglitches", "minorglitches", "owglitches", + "hybridglitches", "nologic" ] }, @@ -95,8 +96,11 @@ "type": "bool" }, "dropshuffle" : { - "action": "store_true", - "type": "bool" + "choices" : [ + "none", + "keys", + "underworld" + ] }, "pottery" : { "choices" : [ @@ -164,6 +168,7 @@ "simple", "restricted", "full", + "swapped", "crossed", "insanity", "dungeonsfull", @@ -463,9 +468,6 @@ "bps": { "action": "store_true" }, - "enemizercli": { - "setting": "enemizercli" - }, "shufflebosses": { "choices": [ "none", @@ -478,9 +480,7 @@ "shuffleenemies": { "choices": [ "none", - "shuffled", - "random", - "legacy" + "shuffled" ] }, "enemy_health": { @@ -499,6 +499,13 @@ "random" ] }, + "any_enemy_logic": { + "choices": [ + "none", + "allow_drops", + "allow_all" + ] + }, "remote_items": { "action": "store_true", "type": "bool" @@ -509,6 +516,10 @@ "action": "store_true", "type": "bool" }, + "aga_randomness": { + "action": "store_false", + "type": "bool" + }, "saveonexit": { "choices": [ "ask", diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index 10790d99..1f0defb9 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -55,7 +55,8 @@ "building.collection.spheres": "Building up collection spheres", "building.calculating.spheres": "Calculated sphere %i, containing %i of %i progress items.", "building.final.spheres": "Calculated final sphere %i, containing %i of %i progress items.", - "old.python.version": "Door Rando may have issues with python versions earlier than 3.7. Detected version: %s" + "old.python.version": "Door Rando may have issues with python versions earlier than 3.7. Detected version: %s", + "hybridglitches.door.shuffle": "Hybrid Major Glitches is not currently compatible with Door Shuffle." }, "help": { "lang": [ "App Language, if available, defaults to English" ], @@ -63,11 +64,13 @@ "bps": [ "Output BPS patches instead of ROMs"], "logic": [ "Select Enforcement of Item Requirements. (default: %(default)s)", - "No Glitches: No Glitch knowledge required.", - "Minor Glitches: May require Fake Flippers, Bunny Revival", - " and Dark Room Navigation.", - "No Logic: Distribute items without regard for", - " item requirements." + "No Glitches: No Glitch knowledge required.", + "Minor Glitches: May require Fake Flippers, Bunny Revival", + " and Dark Room Navigation.", + "Overworld Glitches: May require overworld clips and teleports.", + "Hybrid Major Glitches: May require underworld clips.", + "No Logic: Distribute items without regard for", + " item requirements." ], "mode": [ "Select game mode. (default: %(default)s)", @@ -206,6 +209,7 @@ " connect remaining entrances.", "Crossed: Mix cave and dungeon entrances freely while allowing", " caves to cross between worlds.", + "Swapped: Same as Crossed, but entrances switch places in pairs.", "Insanity: Decouple entrances and exits from each other and", " shuffle them freely. Caves that used to be single", " entrance will still exit to the same location from", @@ -295,7 +299,11 @@ "keyshuffle": [ "Small Keys are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ], "bigkeyshuffle": [ "Big Keys are no longer restricted to their dungeons, but can be anywhere. (default: %(default)s)" ], "shopsanity": ["Shop contents are shuffle in the main item pool and other items can take their place. (default: %(default)s)"], - "dropshuffle": [ "Keys dropped by enemies are shuffled and other items can take their place. (default: %(default)s)"], + "dropshuffle": [ "Controls how enemies drop items (default: %(default)s)", + "None: Enemies drops prize packs or keys as normal", + "Keys: Enemies that drop keys can drop other items and the keys are shuffled into the pool", + "Underworld: All killable enemies in the underworld can drop items" + ], "pottery": [ "Controls how items under pots are shuffled and if other items can take their place:", "None: No pots are changed", "Keys: Key pots are included in the location pool and other items can take their place", @@ -333,6 +341,12 @@ ], "pseudoboots": [ " Players starts with pseudo boots that allow dashing but no item checks (default: %(default)s"], "bombbag": ["Start with 0 bomb capacity. Two capacity upgrades (+10) are added to the pool (default: %(default)s)" ], + "any_enemy_logic": [ + "How to handle potential traversal between dungeon in Crossed door shuffle", + "None: Enemies that required unusual weaponry are not allowed to logical protect doors, chests, or key drops.", + "Allow_Drops: Drops are okay for those enemies, and a correct weapon is logically required", + "Allow_All: All enemies allowed anywhere. (Logic will be adapted to the placement)" + ], "startinventory": [ "Specifies a list of items that will be in your starting inventory (separated by commas). (default: %(default)s)" ], "usestartinventory": [ "Toggle usage of Starting Inventory." ], "customizer": ["Path to a customizer file."], diff --git a/resources/app/gui/lang/en.json b/resources/app/gui/lang/en.json index 6a30113d..c14dee25 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -132,8 +132,10 @@ "randomizer.enemizer.enemyhealth.hard": "Hard", "randomizer.enemizer.enemyhealth.expert": "Expert", - "randomizer.enemizer.enemizercli": "EnemizerCLI path: ", - "randomizer.enemizer.enemizercli.online": "(get online)", + "randomizer.enemizer.enemylogic": "Enemy Logic", + "randomizer.enemizer.enemylogic.none": "Forbid special enemies", + "randomizer.enemizer.enemylogic.allow_drops": "Item drops may have special enemies", + "randomizer.enemizer.enemylogic.allow_all": "Allow special enemies anywhere", "randomizer.entrance.openpyramid": "Pre-open Pyramid Hole", "randomizer.entrance.openpyramid.auto": "Auto", @@ -154,6 +156,7 @@ "randomizer.entrance.entranceshuffle.simple": "Simple", "randomizer.entrance.entranceshuffle.restricted": "Restricted", "randomizer.entrance.entranceshuffle.full": "Full", + "randomizer.entrance.entranceshuffle.swapped": "Swapped", "randomizer.entrance.entranceshuffle.crossed": "Crossed", "randomizer.entrance.entranceshuffle.insanity": "Insanity", "randomizer.entrance.entranceshuffle.dungeonsfull": "Dungeons + Full", @@ -234,6 +237,7 @@ "randomizer.item.logiclevel.noglitches": "No Glitches", "randomizer.item.logiclevel.minorglitches": "Minor Glitches", "randomizer.item.logiclevel.owglitches": "Overworld Glitches", + "randomizer.item.logiclevel.hybridglitches": "Hybrid Major Glitches", "randomizer.item.logiclevel.nologic": "No Logic", "randomizer.item.goal": "Goal", @@ -322,7 +326,10 @@ "randomizer.item.colorizepots": "Colorize Randomized Pots", "randomizer.item.potshuffle": "Pot Shuffle (Legacy)", - "randomizer.item.dropshuffle": "Shuffle Enemy Key Drops", + "randomizer.item.dropshuffle": "Shuffle Enemy Drops", + "randomizer.item.dropshuffle.none": "None", + "randomizer.item.dropshuffle.keys": "Small Key Enemies", + "randomizer.item.dropshuffle.underworld": "Underworld Enemies", "randomizer.item.keydropshuffle": "Enable Key Drop Shuffle (Legacy)", "randomizer.item.take_any": "Take Any Caves", diff --git a/resources/app/gui/randomize/enemizer/widgets.json b/resources/app/gui/randomize/enemizer/widgets.json index 7fa097a2..cf03c320 100644 --- a/resources/app/gui/randomize/enemizer/widgets.json +++ b/resources/app/gui/randomize/enemizer/widgets.json @@ -4,9 +4,7 @@ "type": "selectbox", "options": [ "none", - "shuffled", - "random", - "legacy" + "shuffled" ] }, "bossshuffle": { @@ -39,5 +37,19 @@ "expert" ] } + }, + "bottomEnemizerFrame": { + "enemylogic": { + "type": "selectbox", + "default": "allow_all", + "options": [ + "none", + "allow_drops", + "allow_all" + ], + "config": { + "width": 32 + } + } } } diff --git a/resources/app/gui/randomize/entrando/widgets.json b/resources/app/gui/randomize/entrando/widgets.json index a3104bf1..c325ea26 100644 --- a/resources/app/gui/randomize/entrando/widgets.json +++ b/resources/app/gui/randomize/entrando/widgets.json @@ -9,6 +9,7 @@ "full", "lite", "lean", + "swapped", "crossed", "insanity", "dungeonsfull", diff --git a/resources/app/gui/randomize/item/widgets.json b/resources/app/gui/randomize/item/widgets.json index 9857c45e..0a895d3c 100644 --- a/resources/app/gui/randomize/item/widgets.json +++ b/resources/app/gui/randomize/item/widgets.json @@ -20,6 +20,7 @@ "noglitches", "minorglitches", "owglitches", + "hybridglitches", "nologic" ] }, @@ -158,12 +159,17 @@ } }, "dropshuffle": { - "type": "checkbox" - } + "type": "selectbox", + "default": "none", + "options": [ + "none", + "keys", + "underworld" + ]} }, "leftPoolFrame2": { "keydropshuffle": { - "type": "button", + "type": "checkbox", "config": { "command": "keydropshuffle" } diff --git a/source/classes/CustomSettings.py b/source/classes/CustomSettings.py index 07bbbbf7..14084f0b 100644 --- a/source/classes/CustomSettings.py +++ b/source/classes/CustomSettings.py @@ -94,7 +94,8 @@ class CustomSettings(object): args.pottery[p] = get_setting(settings['pottery'], args.pottery[p]) if get_setting(settings['keydropshuffle'], args.keydropshuffle[p]): - args.dropshuffle[p] = True + if args.dropshuffle[p] == 'none': + args.dropshuffle[p] = 'keys' if args.pottery[p] == 'none': args.pottery[p] = 'keys' @@ -133,10 +134,11 @@ class CustomSettings(object): args.mapshuffle[p] = True args.compassshuffle[p] = True - args.shufflebosses[p] = get_setting(settings['boss_shuffle'], args.shufflebosses[p]) - args.shuffleenemies[p] = get_setting(settings['enemy_shuffle'], args.shuffleenemies[p]) + args.shufflebosses[p] = get_setting(settings['boss_shuffle'], get_setting(settings['shufflebosses'], args.shufflebosses[p])) + args.shuffleenemies[p] = get_setting(settings['enemy_shuffle'], get_setting(settings['shuffleenemies'], args.shuffleenemies[p])) args.enemy_health[p] = get_setting(settings['enemy_health'], args.enemy_health[p]) args.enemy_damage[p] = get_setting(settings['enemy_damage'], args.enemy_damage[p]) + args.any_enemy_logic[p] = get_setting(settings['any_enemy_logic'], args.any_enemy_logic[p]) args.shufflepots[p] = get_setting(settings['shufflepots'], args.shufflepots[p]) args.bombbag[p] = get_setting(settings['bombbag'], args.bombbag[p]) args.shufflelinks[p] = get_setting(settings['shufflelinks'], args.shufflelinks[p]) @@ -153,6 +155,7 @@ class CustomSettings(object): args.triforce_min_difference[p] = get_setting(settings['triforce_min_difference'], args.triforce_min_difference[p]) args.triforce_max_difference[p] = get_setting(settings['triforce_max_difference'], args.triforce_max_difference[p]) args.beemizer[p] = get_setting(settings['beemizer'], args.beemizer[p]) + args.aga_randomness[p] = get_setting(settings['aga_randomness'], args.aga_randomness[p]) # mystery usage args.usestartinventory[p] = get_setting(settings['usestartinventory'], args.usestartinventory[p]) @@ -216,6 +219,11 @@ class CustomSettings(object): return self.file_source['drops'] return None + def get_enemies(self): + if 'enemies' in self.file_source: + return self.file_source['enemies'] + return None + def create_from_world(self, world, settings): self.player_range = range(1, world.players + 1) settings_dict, meta_dict = {}, {} @@ -262,10 +270,11 @@ class CustomSettings(object): settings_dict[p]['keyshuffle'] = world.keyshuffle[p] settings_dict[p]['mapshuffle'] = world.mapshuffle[p] settings_dict[p]['compassshuffle'] = world.compassshuffle[p] - settings_dict[p]['shufflebosses'] = world.boss_shuffle[p] - settings_dict[p]['shuffleenemies'] = world.enemy_shuffle[p] + settings_dict[p]['boss_shuffle'] = world.boss_shuffle[p] + settings_dict[p]['enemy_shuffle'] = world.enemy_shuffle[p] settings_dict[p]['enemy_health'] = world.enemy_health[p] settings_dict[p]['enemy_damage'] = world.enemy_damage[p] + settings_dict[p]['any_enemy_logic'] = world.any_enemy_logic[p] settings_dict[p]['shufflepots'] = world.potshuffle[p] settings_dict[p]['bombbag'] = world.bombbag[p] settings_dict[p]['shufflelinks'] = world.shufflelinks[p] @@ -275,6 +284,7 @@ class CustomSettings(object): settings_dict[p]['triforce_goal'] = world.treasure_hunt_count[p] settings_dict[p]['triforce_pool'] = world.treasure_hunt_total[p] settings_dict[p]['beemizer'] = world.beemizer[p] + settings_dict[p]['aga_randomness'] = world.aga_randomness[p] # rom adjust stuff # settings_dict[p]['sprite'] = world.sprite[p] diff --git a/source/classes/constants.py b/source/classes/constants.py index 1ca3c08e..15797e03 100644 --- a/source/classes/constants.py +++ b/source/classes/constants.py @@ -116,7 +116,8 @@ SETTINGSTOPROCESS = { "enemyshuffle": "shuffleenemies", "bossshuffle": "shufflebosses", "enemydamage": "enemy_damage", - "enemyhealth": "enemy_health" + "enemyhealth": "enemy_health", + "enemylogic": "any_enemy_logic" }, "gameoptions": { "nobgm": "disablemusic", diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py new file mode 100644 index 00000000..21ca04b6 --- /dev/null +++ b/source/dungeon/EnemyList.py @@ -0,0 +1,2672 @@ +from collections import defaultdict, deque +import typing + +import yaml +from yaml.representer import Representer + +try: + from fast_enum import FastEnum +except ImportError: + from enum import IntFlag as FastEnum + +import RaceRandom as random +from BaseClasses import Location, LocationType, RegionType +from EntranceShuffle import door_addresses +from Items import ItemFactory +from PotShuffle import key_drop_special +from Utils import snes_to_pc, pc_to_snes, int16_as_bytes + + +class EnemyStats: + def __init__(self, sprite, static, drop_flag=False, prize_pack: typing.Union[tuple, int] = 0, + sub_type=0, health=None, ignore=False, dmg=None, dmask=0): + self.sprite = sprite + self.sub_type = sub_type + self.static = static + self.health = health + self.damage = dmg # this is a damage group, notably 0-9 (9 is reserved for ganon) + self.dmask = dmask # mask that is written out with the above damage class, same byte + self.drop_flag = drop_flag + self.prize_pack = prize_pack + + # todo: write this bit for cucco (False), antifairy (True) + # rotating firebars (False) rollers (False) + self.ignore_for_kill_room = ignore + + # health special cases: + # Octorok light/dark world + # check the 0000 000x of the x coordinate + # Hardhat Beetle - (000b bit of x coordinate?) (blue if set, red otherwise) + # Tektite - starting position? - (000r bit of x coordinate) (red if set, blue otherwise) + # RatCricket light/dark world + # Keese light/dark world + # Rope light/dark world + # Raven light/dark world + + +class EnemySprite(FastEnum): + Raven = 0x00 + Vulture = 0x01 + CorrectPullSwitch = 0x04 + WrongPullSwitch = 0x06 + Octorok = 0x08 + Moldorm = 0x09 + Octorok4Way = 0x0a + Cucco = 0x0b + Buzzblob = 0x0d + Snapdragon = 0x0e + + Octoballoon = 0x0f + OctoballoonBaby = 0x10 + Hinox = 0x11 + Moblin = 0x12 + MiniHelmasaur = 0x13 + ThievesTownGrate = 0x14 + AntiFairy = 0x15 + Wiseman = 0x16 + Hoarder = 0x17 + MiniMoldorm = 0x18 + Poe = 0x19 + Smithy = 0x1a + Arrow = 0x1b + Statue = 0x1c + FluteQuest = 0x1d + CrystalSwitch = 0x1e + SickKid = 0x1f + Sluggula = 0x20 + WaterSwitch = 0x21 + Ropa = 0x22 + RedBari = 0x23 + BlueBari = 0x24 + TalkingTree = 0x25 + HardhatBeetle = 0x26 + Deadrock = 0x27 + DarkWorldHintNpc = 0x28 + AdultNpc = 0x29 + SweepingLady = 0x2a + Hobo = 0x2b + Lumberjacks = 0x2c + TelepathicTile = 0x2d + FluteKid = 0x2e + RaceGameLady = 0x2f + RaceGameGuy = 0x30 + FortuneTeller = 0x31 + ArgueBros = 0x32 + RupeePull = 0x33 + YoungSnitch = 0x34 + Innkeeper = 0x35 + Witch = 0x36 + Waterfall = 0x37 + EyeStatue = 0x38 + Locksmith = 0x39 + MagicBat = 0x3a + BonkItem = 0x3b + KidInKak = 0x3c + OldSnitch = 0x3d + Hoarder2 = 0x3e + TutorialGuard = 0x3f + + LightningGate = 0x40 + BlueGuard = 0x41 + GreenGuard = 0x42 + RedSpearGuard = 0x43 + BluesainBolt = 0x44 + UsainBolt = 0x45 + BlueArcher = 0x46 + GreenBushGuard = 0x47 + RedJavelinGuard = 0x48 + RedBushGuard = 0x49 + BombGuard = 0x4a + GreenKnifeGuard = 0x4b + Geldman = 0x4c + Toppo = 0x4d + Popo = 0x4e + Popo2 = 0x4f + + Cannonball = 0x50 + ArmosStatue = 0x51 + KingZora = 0x52 + ArmosKnight = 0x53 + Lanmolas = 0x54 + FireballZora = 0x55 + Zora = 0x56 + DesertStatue = 0x57 + Crab = 0x58 + LostWoodsBird = 0x59 + LostWoodsSquirrel = 0x5a + SparkCW = 0x5b + SparkCCW = 0x5c + RollerVerticalUp = 0x5d + RollerVerticalDown = 0x5e + RollerHorizontalLeft = 0x5f + RollerHorizontalRight = 0x60 + Beamos = 0x61 + MasterSword = 0x62 + DebirandoPit = 0x63 + Debirando = 0x64 + ArcheryNpc = 0x65 + WallCannonVertLeft = 0x66 + WallCannonVertRight = 0x67 + WallCannonHorzTop = 0x68 + WallCannonHorzBottom = 0x69 + BallNChain = 0x6a + CannonTrooper = 0x6b + CricketRat = 0x6d + Snake = 0x6e + Keese = 0x6f + + Leever = 0x71 + FairyPondTrigger = 0x72 + UnclePriest = 0x73 + RunningNpc = 0x74 + BottleMerchant = 0x75 + Zelda = 0x76 + Grandma = 0x78 + Bee = 0x79 + Agahnim = 0x7a + FloatingSkull = 0x7c + BigSpike = 0x7d + FirebarCW = 0x7e + FirebarCCW = 0x7f + Firesnake = 0x80 + Hover = 0x81 + AntiFairyCircle = 0x82 + GreenEyegoreMimic = 0x83 + RedEyegoreMimic = 0x84 + YellowStalfos = 0x85 # falling stalfos that shoots head + Kodongo = 0x86 + KodongoFire = 0x87 + Mothula = 0x88 + SpikeBlock = 0x8a + Gibdo = 0x8b + Arrghus = 0x8c + Arrghi = 0x8d + Terrorpin = 0x8e + Blob = 0x8f + Wallmaster = 0x90 + StalfosKnight = 0x91 + HelmasaurKing = 0x92 + Bumper = 0x93 + Pirogusu = 0x94 + LaserEyeLeft = 0x95 + LaserEyeRight = 0x96 + LaserEyeTop = 0x97 + LaserEyeBottom = 0x98 + Pengator = 0x99 + Kyameron = 0x9a + Wizzrobe = 0x9b + Zoro = 0x9c # babasu horizontal? + Babasu = 0x9d # babasu vertical? + GroveOstritch = 0x9e + GroveRabbit = 0x9f + GroveBird = 0xa0 + Freezor = 0xa1 + Kholdstare = 0xa2 + KholdstareShell = 0xa3 + FallingIce = 0xa4 + BlueZazak = 0xa5 + RedZazak = 0xa6 + Stalfos = 0xa7 + GreenZirro = 0xa8 + BlueZirro = 0xa9 + Pikit = 0xaa + CrystalMaiden = 0xab + Apple = 0xac + OldMan = 0xad + PipeDown = 0xae + PipeUp = 0xaf + PipeRight = 0xb0 + PipeLeft = 0xb1 + GoodBee = 0xb2 + PedestalPlaque = 0xb3 + PurpleChest = 0xb4 + BombShopGuy = 0xb5 + Kiki = 0xb6 + BlindMaiden = 0xb7 + BullyPinkBall = 0xb9 + + Whirlpool = 0xba + Shopkeeper = 0xbb + Drunkard = 0xbc + Vitreous = 0xbd + # ... (spawnables) + Catfish = 0xc0 + CutsceneAgahnim = 0xc1 + Boulder = 0xc2 + Gibo = 0xc3 # patrick! + Thief = 0xc4 + Medusa = 0xc5 + FourWayShooter = 0xc6 + Pokey = 0xc7 + BigFairy = 0xc8 + Tektite = 0xc9 # firebat? + Chainchomp = 0xca + TrinexxRockHead = 0xcb + TrinexxFireHead = 0xcc + TrinexxIceHead = 0xcd + Blind = 0xce + Swamola = 0xcf + Lynel = 0xd0 + BunnyBeam = 0xd1 + FloppingFish = 0xd2 + Stal = 0xd3 # alive skull rock? + Landmine = 0xd4 + DiggingGameNPC = 0xd5 + Ganon = 0xd6 + + SmallHeart = 0xd8 + BlueRupee = 0xda + RedRupee = 0xdb + BombRefill1 = 0xdc + BombRefill4 = 0xdd + BombRefill8 = 0xde + + LargeMagic = 0xe0 + Faerie = 0xe3 + SmallKey = 0xe4 + Mushroom = 0xe7 + FakeMasterSword = 0xe8 + MagicShopAssistant = 0xe9 + HeartPiece = 0xeb + SomariaPlatform = 0xed + CastleMantle = 0xee + GreenMimic = 0xef + RedMimic = 0xf0 + MedallionTablet = 0xf2 + PositionTarget = 0xf3 + Boulders = 0xf4 + + +class SpriteType(FastEnum): + Normal = 0x00 + Overlord = 0x07 + + +def init_enemy_stats(): + stats = { + EnemySprite.Raven: EnemyStats(EnemySprite.Raven, False, False, (6, 2), health=(4, 8), dmg=(1, 8), dmask=0x80), + EnemySprite.Vulture: EnemyStats(EnemySprite.Vulture, False, False, 6, health=6, dmg=3, dmask=0x80), + EnemySprite.CorrectPullSwitch: EnemyStats(EnemySprite.CorrectPullSwitch, True, ignore=True, dmg=2), + EnemySprite.WrongPullSwitch: EnemyStats(EnemySprite.WrongPullSwitch, True, ignore=True, dmg=2), + EnemySprite.Octorok: EnemyStats(EnemySprite.Octorok, False, True, 2, health=(2, 4), dmg=(3, 5)), + EnemySprite.Moldorm: EnemyStats(EnemySprite.Moldorm, True, False, dmg=3, dmask=0x10), + EnemySprite.Octorok4Way: EnemyStats(EnemySprite.Octorok4Way, False, True, 2, health=(2, 4), dmg=(3, 5)), + EnemySprite.Cucco: EnemyStats(EnemySprite.Cucco, False, False, ignore=True, dmg=1), + EnemySprite.Buzzblob: EnemyStats(EnemySprite.Buzzblob, False, True, 3, health=3, dmg=1), + EnemySprite.Snapdragon: EnemyStats(EnemySprite.Snapdragon, False, True, 7, health=12, dmg=8), + + EnemySprite.Octoballoon: EnemyStats(EnemySprite.Octoballoon, False, False, 0, health=2, dmg=1), + EnemySprite.OctoballoonBaby: EnemyStats(EnemySprite.OctoballoonBaby, False, dmg=1), + EnemySprite.Hinox: EnemyStats(EnemySprite.Hinox, False, True, 4, health=20, dmg=8), + EnemySprite.Moblin: EnemyStats(EnemySprite.Moblin, False, True, 1, health=4, dmg=5), + EnemySprite.MiniHelmasaur: EnemyStats(EnemySprite.MiniHelmasaur, False, True, 7, health=4, dmg=3), + EnemySprite.ThievesTownGrate: EnemyStats(EnemySprite.ThievesTownGrate, True, dmg=0, dmask=0x40), + EnemySprite.AntiFairy: EnemyStats(EnemySprite.AntiFairy, False, False, dmg=4), + EnemySprite.Wiseman: EnemyStats(EnemySprite.Wiseman, True, dmg=0), + EnemySprite.Hoarder: EnemyStats(EnemySprite.Hoarder, False, True, health=2, dmg=2), + EnemySprite.MiniMoldorm: EnemyStats(EnemySprite.MiniMoldorm, False, True, 2, health=3, dmg=3), + EnemySprite.Poe: EnemyStats(EnemySprite.Poe, False, False, 6, health=8, dmg=5, dmask=0x80), + EnemySprite.Smithy: EnemyStats(EnemySprite.Smithy, True, dmg=0), + EnemySprite.Arrow: EnemyStats(EnemySprite.Arrow, True, ignore=True, dmg=1), + EnemySprite.Statue: EnemyStats(EnemySprite.Statue, False, ignore=True, dmg=0), + EnemySprite.FluteQuest: EnemyStats(EnemySprite.FluteQuest, True, dmg=0, dmask=0x40), + EnemySprite.CrystalSwitch: EnemyStats(EnemySprite.CrystalSwitch, True, ignore=True, dmg=0), + EnemySprite.SickKid: EnemyStats(EnemySprite.SickKid, True, dmg=0), + EnemySprite.Sluggula: EnemyStats(EnemySprite.Sluggula, False, True, 4, health=8, dmg=6), + EnemySprite.WaterSwitch: EnemyStats(EnemySprite.WaterSwitch, True, dmg=0), + EnemySprite.Ropa: EnemyStats(EnemySprite.Ropa, False, True, 2, health=8, dmg=5), + EnemySprite.RedBari: EnemyStats(EnemySprite.RedBari, False, True, 6, 2, health=2, dmg=3), + EnemySprite.BlueBari: EnemyStats(EnemySprite.BlueBari, False, True, 6, 2, health=2, dmg=1), + EnemySprite.TalkingTree: EnemyStats(EnemySprite.TalkingTree, True, dmg=0), + EnemySprite.HardhatBeetle: EnemyStats(EnemySprite.HardhatBeetle, False, True, (2, 6), health=32, dmg=0), + EnemySprite.Deadrock: EnemyStats(EnemySprite.Deadrock, False, True, dmg=3, health=255), + EnemySprite.DarkWorldHintNpc: EnemyStats(EnemySprite.DarkWorldHintNpc, True, dmg=0), + EnemySprite.AdultNpc: EnemyStats(EnemySprite.AdultNpc, True, dmg=0), + EnemySprite.SweepingLady: EnemyStats(EnemySprite.SweepingLady, True, dmg=0), + EnemySprite.Hobo: EnemyStats(EnemySprite.Hobo, True, dmg=0), + EnemySprite.Lumberjacks: EnemyStats(EnemySprite.Lumberjacks, True, dmg=0), + EnemySprite.TelepathicTile: EnemyStats(EnemySprite.TelepathicTile, True, dmg=0), + EnemySprite.FluteKid: EnemyStats(EnemySprite.FluteKid, True, dmg=0), + EnemySprite.RaceGameLady: EnemyStats(EnemySprite.RaceGameLady, True, dmg=0), + + EnemySprite.FortuneTeller: EnemyStats(EnemySprite.FortuneTeller, True, dmg=0), + EnemySprite.ArgueBros: EnemyStats(EnemySprite.ArgueBros, True, dmg=0), + EnemySprite.RupeePull: EnemyStats(EnemySprite.RupeePull, True, dmg=0), + EnemySprite.YoungSnitch: EnemyStats(EnemySprite.YoungSnitch, True, dmg=0), + EnemySprite.Innkeeper: EnemyStats(EnemySprite.Innkeeper, True, dmg=0), + EnemySprite.Witch: EnemyStats(EnemySprite.Witch, True, dmg=0), + EnemySprite.Waterfall: EnemyStats(EnemySprite.Waterfall, True, ignore=True, dmg=0, dmask=0x40), + EnemySprite.EyeStatue: EnemyStats(EnemySprite.EyeStatue, True, dmg=0), + EnemySprite.Locksmith: EnemyStats(EnemySprite.Locksmith, True, dmg=0), + EnemySprite.MagicBat: EnemyStats(EnemySprite.MagicBat, True, dmg=0), + EnemySprite.BonkItem: EnemyStats(EnemySprite.BonkItem, True, dmg=0), + EnemySprite.KidInKak: EnemyStats(EnemySprite.KidInKak, True, dmg=0), + EnemySprite.OldSnitch: EnemyStats(EnemySprite.OldSnitch, True, dmg=0), + EnemySprite.Hoarder2: EnemyStats(EnemySprite.Hoarder2, False, True, health=2, dmg=2), + EnemySprite.TutorialGuard: EnemyStats(EnemySprite.TutorialGuard, True, dmg=2), + + EnemySprite.LightningGate: EnemyStats(EnemySprite.LightningGate, True, dmg=0), + EnemySprite.BlueGuard: EnemyStats(EnemySprite.BlueGuard, False, True, 1, health=6, dmg=1), + EnemySprite.GreenGuard: EnemyStats(EnemySprite.GreenGuard, False, True, 1, health=4, dmg=1), + EnemySprite.RedSpearGuard: EnemyStats(EnemySprite.RedSpearGuard, False, True, 1, health=8, dmg=3), + EnemySprite.BluesainBolt: EnemyStats(EnemySprite.BluesainBolt, False, True, 7, health=6, dmg=1), + EnemySprite.UsainBolt: EnemyStats(EnemySprite.UsainBolt, False, True, 1, health=8, dmg=3), + EnemySprite.BlueArcher: EnemyStats(EnemySprite.BlueArcher, False, True, 5, health=6, dmg=1), + EnemySprite.GreenBushGuard: EnemyStats(EnemySprite.GreenBushGuard, False, True, 5, health=4, dmg=1), + EnemySprite.RedJavelinGuard: EnemyStats(EnemySprite.RedJavelinGuard, False, True, 3, health=8, dmg=3), + EnemySprite.RedBushGuard: EnemyStats(EnemySprite.RedBushGuard, False, True, 7, health=8, dmg=3), + EnemySprite.BombGuard: EnemyStats(EnemySprite.BombGuard, False, True, 4, health=8, dmg=3), + EnemySprite.GreenKnifeGuard: EnemyStats(EnemySprite.GreenKnifeGuard, False, True, 1, health=4, dmg=1), + EnemySprite.Geldman: EnemyStats(EnemySprite.Geldman, False, True, 2, health=4, dmg=3), + EnemySprite.Toppo: EnemyStats(EnemySprite.Toppo, False, False, 1, health=2, dmg=1), + EnemySprite.Popo: EnemyStats(EnemySprite.Popo, False, True, 2, health=2, dmg=1), + EnemySprite.Popo2: EnemyStats(EnemySprite.Popo2, False, True, 2, health=2, dmg=1), + + EnemySprite.Cannonball: EnemyStats(EnemySprite.Cannonball, True, health=255, dmg=1), + EnemySprite.ArmosStatue: EnemyStats(EnemySprite.ArmosStatue, False, True, 5, health=8), + EnemySprite.ArmosKnight: EnemyStats(EnemySprite.ArmosKnight, True, False, dmg=1, dmask=0x10), + EnemySprite.Lanmolas: EnemyStats(EnemySprite.Lanmolas, True, False, dmg=4, dmask=0x10), + EnemySprite.FireballZora: EnemyStats(EnemySprite.FireballZora, False, False, 4, health=8, dmg=1), + EnemySprite.Zora: EnemyStats(EnemySprite.Zora, False, True, 4, health=8, dmg=1), + EnemySprite.DesertStatue: EnemyStats(EnemySprite.DesertStatue, True, dmg=2), + EnemySprite.Crab: EnemyStats(EnemySprite.Crab, False, True, 1, health=2, dmg=5), + EnemySprite.LostWoodsBird: EnemyStats(EnemySprite.LostWoodsBird, True, dmg=0), + EnemySprite.LostWoodsSquirrel: EnemyStats(EnemySprite.LostWoodsSquirrel, True, dmg=0), + EnemySprite.SparkCW: EnemyStats(EnemySprite.SparkCW, False, False, ignore=True, dmg=4), + EnemySprite.SparkCCW: EnemyStats(EnemySprite.SparkCCW, False, False, ignore=True, dmg=4), + EnemySprite.RollerVerticalUp: EnemyStats(EnemySprite.RollerVerticalUp, False, False, dmg=8), + EnemySprite.RollerVerticalDown: EnemyStats(EnemySprite.RollerVerticalDown, False, False, dmg=8), + EnemySprite.RollerHorizontalLeft: EnemyStats(EnemySprite.RollerHorizontalLeft, False, False, dmg=8), + EnemySprite.RollerHorizontalRight: EnemyStats(EnemySprite.RollerHorizontalRight, False, False, dmg=8), + EnemySprite.Beamos: EnemyStats(EnemySprite.Beamos, False, False, ignore=True, dmg=4), + EnemySprite.MasterSword: EnemyStats(EnemySprite.MasterSword, True, dmg=0), + EnemySprite.DebirandoPit: EnemyStats(EnemySprite.DebirandoPit, False, True, 2, health=4, ignore=True, dmg=4), + EnemySprite.Debirando: EnemyStats(EnemySprite.Debirando, False, True, 2, health=4, dmg=3), + EnemySprite.ArcheryNpc: EnemyStats(EnemySprite.ArcheryNpc, True, dmg=2), + EnemySprite.WallCannonVertLeft: EnemyStats(EnemySprite.WallCannonVertLeft, True, dmg=2), + EnemySprite.WallCannonVertRight: EnemyStats(EnemySprite.WallCannonVertRight, True, dmg=2), + EnemySprite.WallCannonHorzTop: EnemyStats(EnemySprite.WallCannonHorzTop, True, dmg=2), + EnemySprite.WallCannonHorzBottom: EnemyStats(EnemySprite.WallCannonHorzBottom, True, dmg=2), + EnemySprite.BallNChain: EnemyStats(EnemySprite.BallNChain, False, True, 2, health=16, dmg=3), + EnemySprite.CannonTrooper: EnemyStats(EnemySprite.CannonTrooper, False, True, 1, health=3, dmg=1), + EnemySprite.CricketRat: EnemyStats(EnemySprite.CricketRat, False, True, 2, health=(2, 8), dmg=(0, 5)), + EnemySprite.Snake: EnemyStats(EnemySprite.Snake, False, True, (1, 7), health=(4, 8), dmg=(1, 5)), + EnemySprite.Keese: EnemyStats(EnemySprite.Keese, False, False, (0, 7), health=(1, 4), ignore=True, + dmg=(0, 5), dmask=0x80), + + # skip helmafireball for damage + EnemySprite.Leever: EnemyStats(EnemySprite.Leever, False, True, 1, health=4, dmg=1), + EnemySprite.FairyPondTrigger: EnemyStats(EnemySprite.FairyPondTrigger, True, dmg=0), + EnemySprite.UnclePriest: EnemyStats(EnemySprite.UnclePriest, True, dmg=0), + EnemySprite.RunningNpc: EnemyStats(EnemySprite.RunningNpc, True, dmg=0), + EnemySprite.BottleMerchant: EnemyStats(EnemySprite.BottleMerchant, True, dmg=0, dmask=0x40), + EnemySprite.Zelda: EnemyStats(EnemySprite.Zelda, True, dmg=0), + EnemySprite.Grandma: EnemyStats(EnemySprite.Grandma, True, dmg=0), + EnemySprite.Bee: EnemyStats(EnemySprite.Bee, True, dmg=0), + EnemySprite.Agahnim: EnemyStats(EnemySprite.Agahnim, True, dmg=4, dmask=0x10), + EnemySprite.FloatingSkull: EnemyStats(EnemySprite.FloatingSkull, False, True, 7, health=24, dmg=6), + EnemySprite.BigSpike: EnemyStats(EnemySprite.BigSpike, False, False, ignore=True, dmg=4), + EnemySprite.FirebarCW: EnemyStats(EnemySprite.FirebarCW, False, False, ignore=True, dmg=4), + EnemySprite.FirebarCCW: EnemyStats(EnemySprite.FirebarCCW, False, False, ignore=True, dmg=4), + EnemySprite.Firesnake: EnemyStats(EnemySprite.Firesnake, False, False, ignore=True, dmg=4), + EnemySprite.Hover: EnemyStats(EnemySprite.Hover, False, True, 2, health=4, dmg=3), + EnemySprite.AntiFairyCircle: EnemyStats(EnemySprite.AntiFairyCircle, False, False, ignore=True, dmg=4), + EnemySprite.GreenEyegoreMimic: EnemyStats(EnemySprite.GreenEyegoreMimic, False, True, 5, health=16, dmg=4), + EnemySprite.RedEyegoreMimic: EnemyStats(EnemySprite.RedEyegoreMimic, False, True, 5, health=8, dmg=4), + EnemySprite.YellowStalfos: EnemyStats(EnemySprite.YellowStalfos, True, health=8, ignore=True, dmg=1), + EnemySprite.Kodongo: EnemyStats(EnemySprite.Kodongo, False, True, 6, health=1, dmg=4), + EnemySprite.KodongoFire: EnemyStats(EnemySprite.KodongoFire, True, health=255, dmg=4), + EnemySprite.Mothula: EnemyStats(EnemySprite.Mothula, True, dmg=5, dmask=0x10), + EnemySprite.SpikeBlock: EnemyStats(EnemySprite.SpikeBlock, False, False, ignore=True, dmg=4), + EnemySprite.Gibdo: EnemyStats(EnemySprite.Gibdo, False, True, 3, health=32, dmg=5), + EnemySprite.Arrghus: EnemyStats(EnemySprite.Arrghus, True, ignore=True, dmg=5, dmask=0x10), + EnemySprite.Arrghi: EnemyStats(EnemySprite.Arrghi, True, dmg=5, dmask=0x10), + EnemySprite.Terrorpin: EnemyStats(EnemySprite.Terrorpin, False, True, 2, health=8, dmg=3), + EnemySprite.Blob: EnemyStats(EnemySprite.Blob, False, True, 1, health=4, dmg=5), + EnemySprite.Wallmaster: EnemyStats(EnemySprite.Wallmaster, False, dmg=0), + EnemySprite.StalfosKnight: EnemyStats(EnemySprite.StalfosKnight, False, True, 4, health=64, dmg=5), + EnemySprite.HelmasaurKing: EnemyStats(EnemySprite.HelmasaurKing, True, dmg=5, dmask=0x10), + EnemySprite.Bumper: EnemyStats(EnemySprite.Bumper, False, ignore=True, dmg=5), + EnemySprite.Pirogusu: EnemyStats(EnemySprite.Pirogusu, True, dmg=5), + EnemySprite.LaserEyeLeft: EnemyStats(EnemySprite.LaserEyeLeft, True, ignore=True, dmg=6), + EnemySprite.LaserEyeRight: EnemyStats(EnemySprite.LaserEyeRight, True, ignore=True, dmg=6), + EnemySprite.LaserEyeTop: EnemyStats(EnemySprite.LaserEyeTop, True, ignore=True, dmg=6), + EnemySprite.LaserEyeBottom: EnemyStats(EnemySprite.LaserEyeBottom, True, ignore=True, dmg=6), + EnemySprite.Pengator: EnemyStats(EnemySprite.Pengator, False, True, 3, health=16, dmg=5), + EnemySprite.Kyameron: EnemyStats(EnemySprite.Kyameron, False, False, health=4, ignore=True, dmg=3), + EnemySprite.Wizzrobe: EnemyStats(EnemySprite.Wizzrobe, False, True, 1, health=2, dmg=6), + EnemySprite.Zoro: EnemyStats(EnemySprite.Zoro, False, True, health=4, dmg=5), + EnemySprite.Babasu: EnemyStats(EnemySprite.Babasu, False, True, 0, health=4, dmg=5), + EnemySprite.GroveOstritch: EnemyStats(EnemySprite.GroveOstritch, True, ignore=True, dmg=3), + EnemySprite.GroveRabbit: EnemyStats(EnemySprite.GroveRabbit, True, ignore=True, dmg=3), + EnemySprite.GroveBird: EnemyStats(EnemySprite.GroveBird, True, ignore=True, dmg=3), + EnemySprite.Freezor: EnemyStats(EnemySprite.Freezor, True, False, 0, health=16, dmg=6), + EnemySprite.Kholdstare: EnemyStats(EnemySprite.Kholdstare, True, dmg=7, dmask=0x10), + EnemySprite.KholdstareShell: EnemyStats(EnemySprite.KholdstareShell, True, dmg=5, dmask=0x10), + EnemySprite.FallingIce: EnemyStats(EnemySprite.FallingIce, True, dmg=5, dmask=0x10), + EnemySprite.BlueZazak: EnemyStats(EnemySprite.BlueZazak, False, True, 6, health=4, dmg=5), + EnemySprite.RedZazak: EnemyStats(EnemySprite.RedZazak, False, True, 6, health=8, dmg=5), + EnemySprite.Stalfos: EnemyStats(EnemySprite.Stalfos, False, True, 6, health=4, dmg=1), + EnemySprite.GreenZirro: EnemyStats(EnemySprite.GreenZirro, False, False, 1, health=4, dmg=5, dmask=0x80), + EnemySprite.BlueZirro: EnemyStats(EnemySprite.BlueZirro, False, False, 7, health=8, dmg=3, dmask=0x80), + EnemySprite.Pikit: EnemyStats(EnemySprite.Pikit, False, True, 2, health=12, dmg=5), + + EnemySprite.OldMan: EnemyStats(EnemySprite.OldMan, True, dmg=0), + EnemySprite.PipeDown: EnemyStats(EnemySprite.PipeDown, True, dmg=0), + EnemySprite.PipeUp: EnemyStats(EnemySprite.PipeUp, True, dmg=0), + EnemySprite.PipeRight: EnemyStats(EnemySprite.PipeRight, True, dmg=0), + EnemySprite.PipeLeft: EnemyStats(EnemySprite.PipeLeft, True, dmg=0), + EnemySprite.GoodBee: EnemyStats(EnemySprite.GoodBee, True, ignore=True, dmg=0), + EnemySprite.PedestalPlaque: EnemyStats(EnemySprite.PedestalPlaque, True, dmg=0), + EnemySprite.BombShopGuy: EnemyStats(EnemySprite.BombShopGuy, True, dmg=0), + EnemySprite.BlindMaiden: EnemyStats(EnemySprite.BlindMaiden, True, dmg=0), + + EnemySprite.Whirlpool: EnemyStats(EnemySprite.Whirlpool, True, dmg=0), + EnemySprite.Shopkeeper: EnemyStats(EnemySprite.Shopkeeper, True, dmg=0), + EnemySprite.Drunkard: EnemyStats(EnemySprite.Drunkard, True, dmg=0), + EnemySprite.Vitreous: EnemyStats(EnemySprite.Vitreous, True, dmg=7, dmask=0x10), + EnemySprite.Catfish: EnemyStats(EnemySprite.Catfish, True, dmg=5), + EnemySprite.CutsceneAgahnim: EnemyStats(EnemySprite.CutsceneAgahnim, True, dmg=5), + EnemySprite.Boulder: EnemyStats(EnemySprite.Boulder, True, dmg=4), + EnemySprite.Gibo: EnemyStats(EnemySprite.Gibo, False, True, 0, health=8, dmg=3), # patrick! + # could drop if killable thieves is on + EnemySprite.Thief: EnemyStats(EnemySprite.Thief, False, False, health=None, dmg=2), + EnemySprite.Medusa: EnemyStats(EnemySprite.Medusa, True, ignore=True, dmg=0, dmask=0x10), + EnemySprite.FourWayShooter: EnemyStats(EnemySprite.FourWayShooter, True, ignore=True, dmg=0), + EnemySprite.Pokey: EnemyStats(EnemySprite.Pokey, False, True, 7, health=32, dmg=6), + EnemySprite.BigFairy: EnemyStats(EnemySprite.BigFairy, True, dmg=0), + EnemySprite.Tektite: EnemyStats(EnemySprite.Tektite, False, True, 2, health=12, dmg=(3, 5)), + EnemySprite.Chainchomp: EnemyStats(EnemySprite.Chainchomp, False, False, dmg=7), + EnemySprite.TrinexxRockHead: EnemyStats(EnemySprite.TrinexxRockHead, True, dmg=7, dmask=0x10), + EnemySprite.TrinexxFireHead: EnemyStats(EnemySprite.TrinexxFireHead, True, dmg=7, dmask=0x10), + EnemySprite.TrinexxIceHead: EnemyStats(EnemySprite.TrinexxIceHead, True, dmg=7, dmask=0x10), + EnemySprite.Blind: EnemyStats(EnemySprite.Blind, True, dmg=5, dmask=0x10), + EnemySprite.Swamola: EnemyStats(EnemySprite.Swamola, False, False, 0, health=16, dmg=7), + EnemySprite.Lynel: EnemyStats(EnemySprite.Lynel, False, True, 7, health=24, dmg=6), + # medallions can kill bunny beams, but we don't need that in logic per se + EnemySprite.BunnyBeam: EnemyStats(EnemySprite.BunnyBeam, False, False, ignore=True, dmg=0, dmask=0x10), + EnemySprite.FloppingFish: EnemyStats(EnemySprite.FloppingFish, False, dmg=0), + EnemySprite.Stal: EnemyStats(EnemySprite.Stal, False, True, 1, health=4, dmg=3), + EnemySprite.Ganon: EnemyStats(EnemySprite.Ganon, True, dmg=9, dmask=0x10), + EnemySprite.SmallHeart: EnemyStats(EnemySprite.SmallHeart, True, ignore=True, dmg=0), + EnemySprite.BlueRupee: EnemyStats(EnemySprite.BlueRupee, True, ignore=True, dmg=0), + EnemySprite.RedRupee: EnemyStats(EnemySprite.RedRupee, True, ignore=True, dmg=0), + EnemySprite.BombRefill1: EnemyStats(EnemySprite.BombRefill1, True, ignore=True, dmg=0), + EnemySprite.BombRefill4: EnemyStats(EnemySprite.BombRefill4, True, ignore=True, dmg=0), + EnemySprite.Faerie: EnemyStats(EnemySprite.Faerie, False, False, ignore=True, dmg=0, dmask=0x10), + EnemySprite.SmallKey: EnemyStats(EnemySprite.SmallKey, True, ignore=True, dmg=0), + EnemySprite.FakeMasterSword: EnemyStats(EnemySprite.FakeMasterSword, False, False, ignore=True, dmg=0), + EnemySprite.MagicShopAssistant: EnemyStats(EnemySprite.MagicShopAssistant, True, ignore=True, dmg=0), + EnemySprite.HeartPiece: EnemyStats(EnemySprite.HeartPiece, True, ignore=True, dmg=0), + EnemySprite.CastleMantle: EnemyStats(EnemySprite.CastleMantle, True, dmg=0), + EnemySprite.GreenMimic: EnemyStats(EnemySprite.GreenMimic, False, True, 5, health=16, dmg=4), + EnemySprite.RedMimic: EnemyStats(EnemySprite.RedMimic, False, True, 5, health=8, dmg=4), + } + return stats + + +def handle_native_dungeon(location, itemid): + # Keys in their native dungeon should use the original item code for keys + if location.parent_region.dungeon: + if location.parent_region.dungeon.name == location.item.dungeon: + if location.item.bigkey: + return 0x32 + if location.item.smallkey: + return 0x24 + if location.item.map: + return 0x33 + if location.item.compass: + return 0x25 + return itemid + + +class Sprite(object): + def __init__(self, super_tile, kind, sub_type, layer, tile_x, tile_y, + region=None, drops_item=False, drop_item_kind=None): + self.super_tile = super_tile + self.kind = kind + self.sub_type = sub_type + self.layer = layer + self.tile_x = tile_x + self.tile_y = tile_y + self.region = region + self.drops_item = drops_item + self.drop_item_kind = drop_item_kind + + self.location = None + self.original_address = None + self.static = False # don't randomize me + self.water = False # water types can spawn here + self.embedded = False # sprite starts on impassible terrian + self.never_drop = False + + def copy(self): + sprite = Sprite(self.super_tile, self.kind, self.sub_type, self.layer, self.tile_x, self.tile_y, self.region, + self.drops_item, self.drop_item_kind) + sprite.original_address = self.original_address + sprite.static = self.static + sprite.water = self.water + sprite.embedded = self.embedded + sprite.never_drop = self.never_drop + return sprite + + def sprite_data(self): + data = [(self.layer << 7) | ((self.sub_type & 0x18) << 2) | self.tile_y, + ((self.sub_type & 7) << 5) | self.tile_x, self.kind] + if self.location is not None: + item_id = self.location.item.code if self.location.item is not None else 0x5A + code = 0xF9 if self.location.item.player != self.location.player else 0xF8 + if code == 0xF8: + item_id = handle_native_dungeon(self.location, item_id) + data.append(item_id) + data.append(0 if code == 0xF8 else self.location.item.player) + data.append(code) + return data + + def sprite_data_ow(self): + return [self.tile_y, self.tile_x, self.kind] + + def __str__(self): + return enemy_names[self.kind] if self.sub_type != 0x7 else overlord_names[self.kind] + + +# map of super_tile to list of Sprite objects: +vanilla_sprites = {} + + +def create_sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, region=None, + drops_item=False, drop_item_kind=None, fix=False, water=False, embed=False, never_drop=False): + if super_tile not in vanilla_sprites: + vanilla_sprites[super_tile] = [] + sprite = Sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, region, drops_item, drop_item_kind) + if fix: + sprite.static = True + if water: + sprite.water = True + if embed: + sprite.embedded = True + if never_drop: + sprite.never_drop = True + vanilla_sprites[super_tile].append(sprite) + + +def init_vanilla_sprites(): + if vanilla_sprites: + return + create_sprite(0x0000, EnemySprite.Ganon, 0x00, 0, 0x17, 0x05, 'Pyramid') + create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x12, 0x05, 'Sewers Yet More Rats') + create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x15, 0x06, 'Sewers Yet More Rats') + create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x0f, 0x08, 'Sewers Yet More Rats') + create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x10, 0x08, 'Sewers Yet More Rats') + create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x18, 0x09, 'Sewers Yet More Rats') + create_sprite(0x0002, 0x06, SpriteType.Overlord, 1, 0x0f, 0x17) + create_sprite(0x0002, 0x06, SpriteType.Overlord, 1, 0x09, 0x18) + create_sprite(0x0002, 0x06, SpriteType.Overlord, 1, 0x0b, 0x18) + create_sprite(0x0002, 0x06, SpriteType.Overlord, 1, 0x0a, 0x19) + create_sprite(0x0002, 0x06, SpriteType.Overlord, 1, 0x0c, 0x19) + create_sprite(0x0002, 0x06, SpriteType.Overlord, 1, 0x09, 0x1a) + create_sprite(0x0002, 0x06, SpriteType.Overlord, 1, 0x0b, 0x1b) + create_sprite(0x0002, EnemySprite.WrongPullSwitch, 0x00, 1, 0x0a, 0x17) + create_sprite(0x0002, EnemySprite.CorrectPullSwitch, 0x00, 1, 0x15, 0x17) + create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x0d, 0x1a, 'Sewers Pull Switch') + create_sprite(0x0002, EnemySprite.CricketRat, 0x00, 1, 0x12, 0x1a, 'Sewers Pull Switch') + create_sprite(0x0004, EnemySprite.CrystalSwitch, 0x00, 0, 0x09, 0x04) + create_sprite(0x0004, EnemySprite.RollerVerticalUp, 0x00, 0, 0x14, 0x04, 'TR Rupees') + create_sprite(0x0004, EnemySprite.RollerHorizontalRight, 0x00, 0, 0x1b, 0x04, 'TR Rupees') + create_sprite(0x0004, EnemySprite.RollerHorizontalLeft, 0x00, 0, 0x05, 0x07, 'TR Crystaroller Middle') + create_sprite(0x0004, EnemySprite.RollerHorizontalLeft, 0x00, 0, 0x15, 0x09, 'TR Rupees') + create_sprite(0x0004, 0x16, SpriteType.Overlord, 0, 0x07, 0x12) + create_sprite(0x0004, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x15, 0x15) + create_sprite(0x0004, EnemySprite.WrongPullSwitch, 0x00, 0, 0x1a, 0x15) + create_sprite(0x0004, 0x1a, SpriteType.Overlord, 0, 0x18, 0x15) + create_sprite(0x0004, EnemySprite.Blob, 0x00, 0, 0x1c, 0x15, 'TR Tongue Pull') + create_sprite(0x0004, 0x1a, SpriteType.Overlord, 0, 0x16, 0x17) + create_sprite(0x0004, 0x1a, SpriteType.Overlord, 0, 0x1a, 0x17) + create_sprite(0x0004, 0x1a, SpriteType.Overlord, 0, 0x18, 0x18) + create_sprite(0x0004, EnemySprite.Blob, 0x00, 0, 0x1a, 0x1a, 'TR Tongue Pull') + create_sprite(0x0004, EnemySprite.Blob, 0x00, 0, 0x15, 0x1b, 'TR Tongue Pull') + create_sprite(0x0004, EnemySprite.Pokey, 0x00, 0, 0x07, 0x18, 'TR Dash Room') + create_sprite(0x0006, EnemySprite.Arrghus, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0006, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07) + create_sprite(0x0007, EnemySprite.Moldorm, 0x00, 0, 0x9, 0x09) + create_sprite(0x0008, EnemySprite.BigFairy, 0x00, 0, 0x07, 0x16) + create_sprite(0x0009, EnemySprite.Medusa, 0x00, 0, 0x07, 0x08) + create_sprite(0x0009, EnemySprite.Medusa, 0x00, 0, 0x08, 0x08) + create_sprite(0x0009, EnemySprite.AntiFairy, 0x00, 0, 0x17, 0x0b, 'PoD Warp Room') + create_sprite(0x000a, EnemySprite.Terrorpin, 0x00, 0, 0x17, 0x08, 'PoD Stalfos Basement') + create_sprite(0x000a, EnemySprite.Terrorpin, 0x00, 0, 0x17, 0x09, 'PoD Stalfos Basement') + create_sprite(0x000a, 0x05, SpriteType.Overlord, 0, 0x0d, 0x09) + create_sprite(0x000a, 0x05, SpriteType.Overlord, 0, 0x11, 0x09) + create_sprite(0x000a, 0x17, SpriteType.Overlord, 0, 0x13, 0x0b) + create_sprite(0x000a, 0x05, SpriteType.Overlord, 0, 0x0d, 0x0e) + create_sprite(0x000a, 0x05, SpriteType.Overlord, 0, 0x11, 0x0e) + create_sprite(0x000b, EnemySprite.CrystalSwitch, 0x00, 0, 0x1c, 0x04) + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x07, 0x08, 'PoD Lonely Turtle') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x16, 0x0b, 'PoD Dark Pegs Middle') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x1b, 0x0b, 'PoD Dark Pegs Right') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x05, 0x16, 'PoD Turtle Party') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x0a, 0x16, 'PoD Turtle Party') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x07, 0x19, 'PoD Turtle Party') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x08, 0x19, 'PoD Turtle Party') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x06, 0x1b, 'PoD Turtle Party') + create_sprite(0x000b, EnemySprite.Terrorpin, 0x00, 0, 0x09, 0x1b, 'PoD Turtle Party') + create_sprite(0x000d, EnemySprite.Agahnim, 0x00, 0, 0x07, 0x15) + create_sprite(0x000e, EnemySprite.Freezor, 0x00, 0, 0x16, 0x12, 'Ice Lobby') + create_sprite(0x000e, EnemySprite.BlueBari, 0x00, 0, 0x05, 0x16, 'Ice Jelly Key') + create_sprite(0x000e, EnemySprite.BlueBari, 0x00, 0, 0x05, 0x18, 'Ice Jelly Key') + create_sprite(0x000e, EnemySprite.BlueBari, 0x00, 0, 0x05, 0x1a, 'Ice Jelly Key', True, 0xe4) + create_sprite(0x0011, EnemySprite.CricketRat, 0x00, 0, 0x17, 0x0a, 'Sewers Rat Path') + create_sprite(0x0011, EnemySprite.CricketRat, 0x00, 0, 0x18, 0x0a, 'Sewers Rat Path') + create_sprite(0x0011, EnemySprite.Keese, 0x00, 0, 0x17, 0x0c, 'Sewers Rat Path') + create_sprite(0x0011, EnemySprite.Keese, 0x00, 0, 0x18, 0x0c, 'Sewers Rat Path') + create_sprite(0x0011, EnemySprite.CricketRat, 0x00, 0, 0x1c, 0x11, 'Sewers Rat Path') + create_sprite(0x0011, EnemySprite.CricketRat, 0x00, 0, 0x1c, 0x12, 'Sewers Rat Path') + create_sprite(0x0011, EnemySprite.CricketRat, 0x00, 0, 0x1a, 0x16, 'Sewers Rat Path') + create_sprite(0x0011, EnemySprite.CricketRat, 0x00, 0, 0x1b, 0x16, 'Sewers Rat Path') + create_sprite(0x0012, EnemySprite.UnclePriest, 0x00, 0, 0x0f, 0x07) + create_sprite(0x0012, EnemySprite.Zelda, 0x00, 0, 0x10, 0x06) + create_sprite(0x0013, EnemySprite.CrystalSwitch, 0x00, 0, 0x14, 0x11) + create_sprite(0x0013, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x04, 'TR Pokey 2 Top') + create_sprite(0x0013, EnemySprite.AntiFairy, 0x00, 0, 0x1a, 0x04, 'TR Pokey 2 Top') + create_sprite(0x0013, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x05, 'TR Pokey 2 Top') + create_sprite(0x0013, EnemySprite.AntiFairy, 0x00, 0, 0x1a, 0x05, 'TR Pokey 2 Top') + create_sprite(0x0013, EnemySprite.FloatingSkull, 0x00, 0, 0x1b, 0x16, 'TR Pokey 2 Bottom') + create_sprite(0x0013, EnemySprite.Pokey, 0x00, 0, 0x16, 0x18, 'TR Pokey 2 Bottom', True, 0xe4) + create_sprite(0x0013, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x18) + create_sprite(0x0013, EnemySprite.FloatingSkull, 0x00, 0, 0x14, 0x1a, 'TR Pokey 2 Bottom') + create_sprite(0x0013, EnemySprite.BunnyBeam, 0x00, 0, 0x1b, 0x1b, 'TR Pokey 2 Bottom') + create_sprite(0x0014, EnemySprite.PipeRight, 0x00, 1, 0x0c, 0x04) + create_sprite(0x0014, EnemySprite.PipeUp, 0x00, 1, 0x0f, 0x0a) + create_sprite(0x0014, EnemySprite.PipeDown, 0x00, 1, 0x19, 0x0a) + create_sprite(0x0014, EnemySprite.PipeDown, 0x00, 1, 0x03, 0x0d) + create_sprite(0x0014, EnemySprite.PipeDown, 0x00, 1, 0x1b, 0x0d) + create_sprite(0x0014, EnemySprite.PipeDown, 0x00, 1, 0x0f, 0x13) + create_sprite(0x0014, EnemySprite.PipeRight, 0x00, 1, 0x08, 0x18) + create_sprite(0x0014, EnemySprite.PipeLeft, 0x00, 1, 0x17, 0x18) + create_sprite(0x0014, EnemySprite.PipeRight, 0x00, 1, 0x0c, 0x1b) + create_sprite(0x0014, EnemySprite.PipeLeft, 0x00, 1, 0x13, 0x1b) + create_sprite(0x0015, EnemySprite.PipeDown, 0x00, 1, 0x04, 0x0c) + create_sprite(0x0015, EnemySprite.PipeDown, 0x00, 1, 0x11, 0x11) + create_sprite(0x0015, EnemySprite.PipeUp, 0x00, 1, 0x04, 0x17) + create_sprite(0x0015, EnemySprite.PipeLeft, 0x00, 1, 0x16, 0x1b) + create_sprite(0x0015, EnemySprite.Blob, 0x00, 1, 0x0a, 0x09, 'TR Pipe Pit') + create_sprite(0x0015, EnemySprite.Blob, 0x00, 1, 0x15, 0x09, 'TR Pipe Pit') + create_sprite(0x0015, EnemySprite.AntiFairy, 0x00, 1, 0x09, 0x0a, 'TR Pipe Pit') + create_sprite(0x0015, EnemySprite.Pokey, 0x00, 1, 0x18, 0x16, 'TR Pipe Pit') + create_sprite(0x0015, EnemySprite.AntiFairy, 0x00, 1, 0x08, 0x17, 'TR Pipe Pit') + create_sprite(0x0015, EnemySprite.AntiFairy, 0x00, 1, 0x17, 0x17, 'TR Pipe Pit') + create_sprite(0x0016, EnemySprite.Blob, 0x00, 0, 0x15, 0x07, 'Swamp C') + create_sprite(0x0016, EnemySprite.Blob, 0x00, 0, 0x15, 0x08, 'Swamp C') + create_sprite(0x0016, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x09, 'Swamp C') + create_sprite(0x0016, EnemySprite.Blob, 0x00, 0, 0x10, 0x0a, 'Swamp I') + create_sprite(0x0016, EnemySprite.Hover, 0x00, 1, 0x0c, 0x18, 'Swamp Waterway', fix=True, never_drop=True) + create_sprite(0x0016, EnemySprite.Hover, 0x00, 1, 0x07, 0x1b, 'Swamp Waterway', fix=True, never_drop=True) + create_sprite(0x0016, EnemySprite.Hover, 0x00, 1, 0x14, 0x1b, 'Swamp Waterway', fix=True, never_drop=True) + create_sprite(0x0017, EnemySprite.Bumper, 0x00, 0, 0x07, 0x0b, 'Hera 5F') + create_sprite(0x0017, EnemySprite.Bumper, 0x00, 0, 0x10, 0x0e, 'Hera 5F') + create_sprite(0x0017, EnemySprite.Bumper, 0x00, 0, 0x07, 0x16, 'Hera 5F') + create_sprite(0x0017, EnemySprite.HardhatBeetle, 0x00, 0, 0x15, 0x07, 'Hera 5F') + create_sprite(0x0017, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x09, 'Hera 5F') + create_sprite(0x0017, EnemySprite.FirebarCW, 0x00, 0, 0x06, 0x11, 'Hera 5F') + create_sprite(0x0017, EnemySprite.HardhatBeetle, 0x00, 0, 0x12, 0x11, 'Hera 5F') + create_sprite(0x0017, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x17, 'Hera 5F') + create_sprite(0x0017, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x17, 'Hera 5F') + create_sprite(0x0019, EnemySprite.Kodongo, 0x00, 0, 0x16, 0x0a, 'PoD Dark Maze') + create_sprite(0x0019, EnemySprite.Kodongo, 0x00, 0, 0x1a, 0x0e, 'PoD Dark Maze') + create_sprite(0x0019, EnemySprite.Kodongo, 0x00, 0, 0x16, 0x10, 'PoD Dark Maze') + create_sprite(0x0019, EnemySprite.Kodongo, 0x00, 0, 0x18, 0x16, 'PoD Dark Maze') + create_sprite(0x001a, EnemySprite.MiniHelmasaur, 0x00, 0, 0x08, 0x06, 'PoD Falling Bridge Mid') + create_sprite(0x001a, EnemySprite.Terrorpin, 0x00, 0, 0x16, 0x06, 'PoD Compass Room') + create_sprite(0x001a, EnemySprite.Terrorpin, 0x00, 0, 0x19, 0x06, 'PoD Compass Room') + create_sprite(0x001a, EnemySprite.Terrorpin, 0x00, 0, 0x16, 0x0a, 'PoD Compass Room') + create_sprite(0x001a, EnemySprite.Terrorpin, 0x00, 0, 0x19, 0x0a, 'PoD Compass Room') + create_sprite(0x001a, EnemySprite.MiniHelmasaur, 0x00, 0, 0x07, 0x10, 'PoD Falling Bridge Mid') + create_sprite(0x001a, EnemySprite.SpikeBlock, 0x00, 0, 0x16, 0x15, 'PoD Harmless Hellway') + create_sprite(0x001a, EnemySprite.SpikeBlock, 0x00, 0, 0x16, 0x17, 'PoD Harmless Hellway') + create_sprite(0x001a, EnemySprite.Statue, 0x00, 0, 0x15, 0x19, 'PoD Harmless Hellway') + create_sprite(0x001a, EnemySprite.SpikeBlock, 0x00, 0, 0x16, 0x19, 'PoD Harmless Hellway') + create_sprite(0x001a, 0x0b, SpriteType.Overlord, 0, 0x07, 0x1a) + create_sprite(0x001b, EnemySprite.CrystalSwitch, 0x00, 0, 0x07, 0x04) + create_sprite(0x001b, EnemySprite.EyeStatue, 0x00, 0, 0x10, 0x04) + create_sprite(0x001b, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x0c, 'PoD Bow Statue Left') + create_sprite(0x001b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x07, 0x14, 'PoD Mimics 2') + create_sprite(0x001b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x03, 0x1c, 'PoD Mimics 2') + create_sprite(0x001b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x1c, 'PoD Mimics 2') + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x05) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x05) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x0a, 0x05) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x0a, 0x08) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x08) + create_sprite(0x001c, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x08) + create_sprite(0x001c, 0x19, SpriteType.Overlord, 0, 0x07, 0x08) + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x07, 0x07, 'GT Fairy Abyss', fix=True) + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07, 'GT Fairy Abyss', fix=True) + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x07, 0x08, 'GT Fairy Abyss', fix=True) + create_sprite(0x001c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x08, 'GT Fairy Abyss', fix=True) + create_sprite(0x001e, EnemySprite.CrystalSwitch, 0x00, 0, 0x1a, 0x09) + create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x16, 0x05, 'Ice Bomb Drop - Top') + create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x19, 0x05, 'Ice Bomb Drop - Top') + create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x16, 0x0a, 'Ice Bomb Drop') + create_sprite(0x001e, EnemySprite.RedBari, 0x00, 0, 0x19, 0x0a, 'Ice Bomb Drop') + create_sprite(0x001e, EnemySprite.Blob, 0x00, 0, 0x08, 0x18, 'Ice Floor Switch') + create_sprite(0x001e, EnemySprite.Blob, 0x00, 0, 0x05, 0x1c, 'Ice Floor Switch') + create_sprite(0x001f, EnemySprite.Pengator, 0x00, 0, 0x04, 0x15, 'Ice Pengator Switch') + create_sprite(0x001f, EnemySprite.Pengator, 0x00, 0, 0x09, 0x15, 'Ice Pengator Switch') + create_sprite(0x001f, EnemySprite.AntiFairy, 0x00, 0, 0x06, 0x16, 'Ice Pengator Switch') + create_sprite(0x001f, EnemySprite.BunnyBeam, 0x00, 0, 0x07, 0x17, 'Ice Pengator Switch') # , fix=True) + create_sprite(0x001f, EnemySprite.Pengator, 0x00, 0, 0x0a, 0x17, 'Ice Pengator Switch') + create_sprite(0x001f, EnemySprite.Pengator, 0x00, 0, 0x0a, 0x19, 'Ice Pengator Switch') + create_sprite(0x001f, EnemySprite.Pengator, 0x00, 0, 0x04, 0x1b, 'Ice Pengator Switch') + create_sprite(0x001f, EnemySprite.Pengator, 0x00, 0, 0x09, 0x1b, 'Ice Pengator Switch') + create_sprite(0x0020, EnemySprite.Agahnim, 0x00, 0, 0x07, 0x15) + create_sprite(0x0021, EnemySprite.CricketRat, 0x00, 0, 0x05, 0x06, 'Sewers Key Rat', True, 0xe4) + create_sprite(0x0021, EnemySprite.Keese, 0x00, 0, 0x17, 0x06, 'Sewers Key Rat') + create_sprite(0x0021, EnemySprite.Keese, 0x00, 0, 0x18, 0x06, 'Sewers Key Rat') + create_sprite(0x0021, EnemySprite.CricketRat, 0x00, 0, 0x11, 0x09, 'Sewers Key Rat') + create_sprite(0x0021, EnemySprite.CricketRat, 0x00, 0, 0x0d, 0x0a, 'Sewers Key Rat') + create_sprite(0x0021, EnemySprite.CricketRat, 0x00, 0, 0x07, 0x14, 'Sewers Dark Aquabats') + create_sprite(0x0021, EnemySprite.Keese, 0x00, 0, 0x0d, 0x14, 'Sewers Dark Aquabats') + create_sprite(0x0021, EnemySprite.Keese, 0x00, 0, 0x12, 0x14, 'Sewers Dark Aquabats') + create_sprite(0x0021, EnemySprite.CricketRat, 0x00, 0, 0x0d, 0x18, 'Sewers Dark Aquabats') + create_sprite(0x0021, EnemySprite.CricketRat, 0x00, 0, 0x0a, 0x1c, 'Sewers Dark Aquabats') + create_sprite(0x0021, EnemySprite.CricketRat, 0x00, 0, 0x13, 0x1c, 'Sewers Dark Aquabats') + create_sprite(0x0022, EnemySprite.CricketRat, 0x00, 0, 0x06, 0x14, 'Sewers Water') + create_sprite(0x0022, EnemySprite.CricketRat, 0x00, 0, 0x08, 0x14, 'Sewers Water') + create_sprite(0x0022, EnemySprite.CricketRat, 0x00, 0, 0x11, 0x14, 'Sewers Water') + create_sprite(0x0022, EnemySprite.CricketRat, 0x00, 0, 0x12, 0x14, 'Sewers Water') + create_sprite(0x0022, EnemySprite.CricketRat, 0x00, 0, 0x11, 0x15, 'Sewers Water') + create_sprite(0x0022, EnemySprite.CricketRat, 0x00, 0, 0x12, 0x15, 'Sewers Water') + create_sprite(0x0022, EnemySprite.CricketRat, 0x00, 0, 0x09, 0x18, 'Sewers Water') + create_sprite(0x0023, EnemySprite.LaserEyeTop, 0x00, 0, 0x15, 0x14) + create_sprite(0x0023, EnemySprite.LaserEyeTop, 0x00, 0, 0x16, 0x14) + create_sprite(0x0023, EnemySprite.LaserEyeTop, 0x00, 0, 0x17, 0x14) + create_sprite(0x0023, EnemySprite.LaserEyeTop, 0x00, 0, 0x18, 0x14) + create_sprite(0x0023, EnemySprite.LaserEyeTop, 0x00, 0, 0x19, 0x14) + create_sprite(0x0024, EnemySprite.Medusa, 0x00, 0, 0x13, 0x04) + create_sprite(0x0024, EnemySprite.Medusa, 0x00, 0, 0x1c, 0x04) + create_sprite(0x0024, EnemySprite.RollerHorizontalRight, 0x00, 0, 0x1b, 0x06, 'TR Dodgers') + create_sprite(0x0024, EnemySprite.Pokey, 0x00, 0, 0x05, 0x08, 'TR Twin Pokeys') + create_sprite(0x0024, EnemySprite.Medusa, 0x00, 0, 0x07, 0x08, 'TR Twin Pokeys') + create_sprite(0x0024, EnemySprite.Pokey, 0x00, 0, 0x0a, 0x08, 'TR Twin Pokeys') + create_sprite(0x0024, EnemySprite.BunnyBeam, 0x00, 0, 0x0c, 0x0c, 'TR Twin Pokeys') # , fix=True) + create_sprite(0x0026, EnemySprite.Medusa, 0x00, 0, 0x03, 0x04) + create_sprite(0x0026, EnemySprite.RedBari, 0x00, 0, 0x1a, 0x05, 'Swamp Right Elbow') + create_sprite(0x0026, EnemySprite.RedBari, 0x00, 0, 0x05, 0x06, 'Swamp Shooters') + create_sprite(0x0026, EnemySprite.Stalfos, 0x00, 0, 0x09, 0x06, 'Swamp Shooters') + create_sprite(0x0026, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x09, 'Swamp Shooters') + create_sprite(0x0026, EnemySprite.Medusa, 0x00, 0, 0x0c, 0x0c) + create_sprite(0x0026, EnemySprite.Statue, 0x00, 0, 0x06, 0x17, fix=True) + create_sprite(0x0026, EnemySprite.FourWayShooter, 0x00, 0, 0x19, 0x17) + create_sprite(0x0026, EnemySprite.RedBari, 0x00, 0, 0x07, 0x18, 'Swamp Push Statue') + create_sprite(0x0026, EnemySprite.Kyameron, 0x00, 0, 0x15, 0x18, 'Swamp Push Statue', water=True) + create_sprite(0x0026, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x19, 'Swamp Push Statue') + create_sprite(0x0026, EnemySprite.Firesnake, 0x00, 0, 0x1c, 0x1a, 'Swamp Push Statue') + create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x17, 0x09, 'Hera 4F') + create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x18, 0x13, 'Hera 4F') + create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x1b, 0x13, 'Hera 4F') + create_sprite(0x0027, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x1a, 'Hera 4F') + create_sprite(0x0027, EnemySprite.SparkCW, 0x00, 0, 0x0f, 0x06, 'Hera Big Chest Landing') + create_sprite(0x0027, EnemySprite.Kodongo, 0x00, 0, 0x05, 0x0e, 'Hera 4F') + create_sprite(0x0027, EnemySprite.Kodongo, 0x00, 0, 0x04, 0x16, 'Hera 4F') + create_sprite(0x0028, EnemySprite.Kyameron, 0x00, 0, 0x0a, 0x06, 'Swamp Entrance', water=True) + create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x08, 0x08, 'Swamp Entrance', water=True) + create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x0b, 0x0a, 'Swamp Entrance', water=True) + create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x07, 0x0d, 'Swamp Entrance', water=True) + create_sprite(0x0028, EnemySprite.SpikeBlock, 0x00, 0, 0x08, 0x10, 'Swamp Entrance') + create_sprite(0x0029, EnemySprite.Mothula, 0x00, 0, 0x08, 0x06) + create_sprite(0x0029, 0x07, SpriteType.Overlord, 0, 0x07, 0x16) + create_sprite(0x002a, EnemySprite.CrystalSwitch, 0x00, 0, 0x10, 0x17, 'PoD Arena Main') + create_sprite(0x002a, EnemySprite.Bumper, 0x00, 0, 0x0f, 0x0f, 'PoD Arena Main') + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x0d, 0x08, 'PoD Arena North') + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x07, 0x0c, 'PoD Arena Main') + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x10, 0x0c, 'PoD Arena Main') + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x0d, 0x0f, 'PoD Arena Main') + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x11, 'PoD Arena Main') + create_sprite(0x002a, EnemySprite.HardhatBeetle, 0x00, 0, 0x0f, 0x13, 'PoD Arena Main') + create_sprite(0x002b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0a, 0x11) + create_sprite(0x002b, EnemySprite.Statue, 0x00, 0, 0x0a, 0x0a, fix=True) + create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x07, 0x17, 'PoD Map Balcony') + create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x16, 0x17, 'PoD Fairy Pool') + create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x18, 0x18, 'PoD Fairy Pool') + create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x05, 0x1a, 'PoD Map Balcony') + create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x1a, 'PoD Map Balcony') + create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x17, 0x1a, 'PoD Fairy Pool') + create_sprite(0x002c, EnemySprite.BigFairy, 0x00, 0, 0x17, 0x05) + create_sprite(0x002c, EnemySprite.Faerie, 0x00, 0, 0x09, 0x04, 'Hookshot Cave (Fairy Pool)') + create_sprite(0x002c, EnemySprite.Faerie, 0x00, 0, 0x06, 0x05, 'Hookshot Cave (Fairy Pool)') + create_sprite(0x002c, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07, 'Hookshot Cave (Fairy Pool)') + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x14, 0x06, 'Ice Compass Room') + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x1c, 0x06, 'Ice Compass Room') + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x16, 0x08, 'Ice Compass Room') + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x19, 0x08, 'Ice Compass Room') + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x14, 0x0b, 'Ice Compass Room') + create_sprite(0x002e, EnemySprite.Pengator, 0x00, 0, 0x1b, 0x0b, 'Ice Compass Room') + create_sprite(0x0030, EnemySprite.CutsceneAgahnim, 0x00, 0, 0x07, 0x05) + create_sprite(0x0031, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x1a) + create_sprite(0x0031, EnemySprite.CrystalSwitch, 0x00, 0, 0x16, 0x0b) + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x15, 0x05, 'Hera Startile Wide') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x06, 'Hera Startile Wide') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x09, 'Hera Startile Wide') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x0c, 'Hera Startile Wide') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x15, 'Hera Startile Corner') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x15, 'Hera Beetles') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x16, 'Hera Beetles') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x18, 'Hera Startile Corner') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x19, 'Hera Beetles') + create_sprite(0x0031, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x1c, 'Hera Startile Corner') + create_sprite(0x0032, EnemySprite.Keese, 0x00, 0, 0x0b, 0x0d, 'Sewers Dark Cross') + create_sprite(0x0032, EnemySprite.Snake, 0x00, 0, 0x0f, 0x0d, 'Sewers Dark Cross') + create_sprite(0x0032, EnemySprite.Keese, 0x00, 0, 0x13, 0x0d, 'Sewers Dark Cross') + create_sprite(0x0032, EnemySprite.Snake, 0x00, 0, 0x10, 0x0e, 'Sewers Dark Cross') + create_sprite(0x0032, EnemySprite.Snake, 0x00, 0, 0x12, 0x0f, 'Sewers Dark Cross') + create_sprite(0x0033, EnemySprite.Lanmolas, 0x00, 0, 0x06, 0x07) + create_sprite(0x0033, EnemySprite.Lanmolas, 0x00, 0, 0x09, 0x07) + create_sprite(0x0033, EnemySprite.Lanmolas, 0x00, 0, 0x07, 0x09) + create_sprite(0x0034, EnemySprite.Hover, 0x00, 0, 0x0f, 0x0b, 'Swamp West Shallows', water=True) + create_sprite(0x0034, EnemySprite.Hover, 0x00, 0, 0x10, 0x12, 'Swamp West Shallows', water=True) + create_sprite(0x0034, EnemySprite.Kyameron, 0x00, 0, 0x0f, 0x15, 'Swamp West Shallows', water=True) + create_sprite(0x0034, EnemySprite.Firesnake, 0x00, 0, 0x19, 0x17, 'Swamp West Shallows') + create_sprite(0x0034, EnemySprite.Blob, 0x00, 0, 0x03, 0x18, 'Swamp West Block Path') + create_sprite(0x0034, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x18, 'Swamp West Shallows') + create_sprite(0x0034, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x1a, 'Swamp West Shallows') + create_sprite(0x0035, EnemySprite.CrystalSwitch, 0x00, 0, 0x16, 0x06) + create_sprite(0x0035, EnemySprite.WaterSwitch, 0x00, 0, 0x14, 0x05) + create_sprite(0x0035, EnemySprite.RedBari, 0x00, 0, 0x18, 0x05, 'Swamp Crystal Switch Outer') + create_sprite(0x0035, EnemySprite.SpikeBlock, 0x00, 0, 0x13, 0x09, 'Swamp Crystal Switch Outer') + create_sprite(0x0035, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x0b, 'Swamp Crystal Switch Outer') + create_sprite(0x0035, EnemySprite.Blob, 0x00, 0, 0x07, 0x14, 'Swamp Trench 2 Departure') + create_sprite(0x0035, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x18, 'Swamp Trench 2 Pots') + create_sprite(0x0035, EnemySprite.Firesnake, 0x00, 0, 0x16, 0x19, 'Swamp Trench 2 Pots') + create_sprite(0x0035, EnemySprite.FourWayShooter, 0x00, 0, 0x17, 0x1a) + create_sprite(0x0035, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1b, 'Swamp Trench 2 Pots') + create_sprite(0x0035, EnemySprite.Stalfos, 0x00, 0, 0x1b, 0x1c, 'Swamp Trench 2 Pots') + create_sprite(0x0036, 0x12, SpriteType.Overlord, 0, 0x17, 0x02) + create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x0b, 0x0a, 'Swamp Hub', water=True) + create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x14, 0x0a, 'Swamp Hub', water=True) + create_sprite(0x0036, EnemySprite.Medusa, 0x00, 0, 0x15, 0x0b, 'Swamp Hub', water=True) + create_sprite(0x0036, 0x10, SpriteType.Overlord, 0, 0x01, 0x0d) + create_sprite(0x0036, EnemySprite.Kyameron, 0x00, 0, 0x14, 0x13, 'Swamp Hub', water=True) + create_sprite(0x0036, 0x11, SpriteType.Overlord, 0, 0x1e, 0x13) + create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x09, 0x14, 'Swamp Hub', water=True) + create_sprite(0x0036, EnemySprite.Hover, 0x00, 0, 0x12, 0x17, 'Swamp Hub', water=True) + create_sprite(0x0036, 0x13, SpriteType.Overlord, 0, 0x0a, 0x1e) + create_sprite(0x0036, 0x13, SpriteType.Overlord, 0, 0x14, 0x1e) + create_sprite(0x0037, EnemySprite.WaterSwitch, 0x00, 0, 0x0b, 0x04) + create_sprite(0x0037, EnemySprite.Stalfos, 0x00, 0, 0x05, 0x06, 'Swamp Hammer Switch') + create_sprite(0x0037, EnemySprite.Blob, 0x00, 0, 0x17, 0x08, 'Swamp Map Ledge') + create_sprite(0x0037, EnemySprite.Blob, 0x00, 0, 0x1a, 0x08, 'Swamp Map Ledge') + create_sprite(0x0037, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x09, 'Swamp Hammer Switch') + create_sprite(0x0037, EnemySprite.Firesnake, 0x00, 0, 0x15, 0x14, 'Swamp Trench 1 Approach') + create_sprite(0x0037, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x17, 'Swamp Trench 1 Approach') + create_sprite(0x0037, EnemySprite.BlueBari, 0x00, 0, 0x13, 0x19, 'Swamp Trench 1 Approach') + create_sprite(0x0037, EnemySprite.FourWayShooter, 0x00, 0, 0x17, 0x1a) + create_sprite(0x0037, EnemySprite.RedBari, 0x00, 0, 0x15, 0x1c, 'Swamp Trench 1 Approach') + create_sprite(0x0038, EnemySprite.Hover, 0x00, 0, 0x0c, 0x06, 'Swamp Pot Row', water=True) + create_sprite(0x0038, EnemySprite.Hover, 0x00, 0, 0x07, 0x0a, 'Swamp Pot Row', water=True) + create_sprite(0x0038, EnemySprite.Kyameron, 0x00, 0, 0x0c, 0x0c, 'Swamp Pot Row', water=True) + create_sprite(0x0038, EnemySprite.Medusa, 0x00, 0, 0x0c, 0x10, 'Swamp Pot Row', water=True) + create_sprite(0x0038, EnemySprite.Kyameron, 0x00, 0, 0x06, 0x14, 'Swamp Pot Row', water=True) + create_sprite(0x0038, EnemySprite.Kyameron, 0x00, 0, 0x0c, 0x18, 'Swamp Pot Row', water=True) + create_sprite(0x0038, EnemySprite.Hover, 0x00, 0, 0x07, 0x1a, 'Swamp Pot Row', water=True) + create_sprite(0x0039, EnemySprite.MiniMoldorm, 0x00, 0, 0x04, 0x18, 'Skull Spike Corner') + create_sprite(0x0039, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) + create_sprite(0x0039, EnemySprite.Gibdo, 0x00, 0, 0x05, 0x15, 'Skull Spike Corner', True, 0xe4) + create_sprite(0x0039, EnemySprite.MiniHelmasaur, 0x00, 0, 0x09, 0x15, 'Skull Spike Corner') + create_sprite(0x0039, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x16, 'Skull Final Drop') + create_sprite(0x0039, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x18, 'Skull Spike Corner') + create_sprite(0x0039, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x1a, 'Skull Final Drop') + create_sprite(0x003a, EnemySprite.Terrorpin, 0x00, 0, 0x0e, 0x11, 'PoD Pit Room') + create_sprite(0x003a, EnemySprite.Terrorpin, 0x00, 0, 0x11, 0x11, 'PoD Pit Room') + create_sprite(0x003a, EnemySprite.Medusa, 0x00, 0, 0x04, 0x14, 'PoD Pit Room') + create_sprite(0x003a, EnemySprite.BlueBari, 0x00, 0, 0x0a, 0x14, 'PoD Pit Room') + create_sprite(0x003a, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x14, 'PoD Pit Room') + create_sprite(0x003a, EnemySprite.Medusa, 0x00, 0, 0x1b, 0x14, 'PoD Pit Room') + create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x06, 'PoD Conveyor') + create_sprite(0x003b, EnemySprite.RedBari, 0x00, 0, 0x07, 0x09, 'PoD Conveyor') + create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x0d, 'PoD Conveyor') + create_sprite(0x003b, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x0f, 'PoD Conveyor') + create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x13, 'PoD Conveyor') + create_sprite(0x003b, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x16, 'PoD Conveyor') + create_sprite(0x003b, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x1a, 'PoD Conveyor') + create_sprite(0x003c, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x08, 'Hookshot Cave (Hook Islands)') + create_sprite(0x003c, EnemySprite.BlueBari, 0x00, 0, 0x0a, 0x14, 'Hookshot Cave (Hook Islands)') + create_sprite(0x003c, EnemySprite.BlueBari, 0x00, 0, 0x12, 0x14, 'Hookshot Cave (Bonk Islands)') + create_sprite(0x003d, EnemySprite.CrystalSwitch, 0x00, 0, 0x05, 0x17) + create_sprite(0x003d, EnemySprite.CrystalSwitch, 0x00, 0, 0x0a, 0x19) + create_sprite(0x003d, EnemySprite.MiniHelmasaur, 0x00, 0, 0x17, 0x07, 'GT Mini Helmasaur Room', True, 0xe4) + create_sprite(0x003d, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x07, 'GT Mini Helmasaur Room') + create_sprite(0x003d, EnemySprite.Medusa, 0x00, 0, 0x15, 0x08, 'GT Mini Helmasaur Room') + create_sprite(0x003d, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x08, 'GT Mini Helmasaur Room') + create_sprite(0x003d, EnemySprite.SpikeBlock, 0x00, 0, 0x04, 0x0a, 'GT Bomb Conveyor') + create_sprite(0x003d, EnemySprite.BigSpike, 0x00, 0, 0x03, 0x0b, 'GT Bomb Conveyor') + create_sprite(0x003d, 0x0a, SpriteType.Overlord, 0, 0x1b, 0x15) + create_sprite(0x003d, EnemySprite.SparkCCW, 0x00, 0, 0x13, 0x16, 'GT Falling Torches') + create_sprite(0x003d, EnemySprite.SparkCW, 0x00, 0, 0x1c, 0x16, 'GT Falling Torches') + create_sprite(0x003d, EnemySprite.SparkCW, 0x00, 0, 0x09, 0x16, 'GT Crystal Inner Circle') + create_sprite(0x003d, EnemySprite.BunnyBeam, 0x00, 0, 0x07, 0x17, 'GT Crystal Inner Circle') + create_sprite(0x003d, EnemySprite.AntiFairy, 0x00, 0, 0x08, 0x17, 'GT Crystal Inner Circle') + create_sprite(0x003e, EnemySprite.CrystalSwitch, 0x00, 0, 0x06, 0x15) + create_sprite(0x003e, EnemySprite.StalfosKnight, 0x00, 0, 0x19, 0x04, 'Ice Stalfos Hint') + create_sprite(0x003e, EnemySprite.StalfosKnight, 0x00, 0, 0x16, 0x0b, 'Ice Stalfos Hint') + create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x05, 0x12, 'Ice Conveyor', fix=True, embed=True) + create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x0e, 0x12, 'Ice Conveyor', fix=True, embed=True) + create_sprite(0x003e, 0x07, SpriteType.Overlord, 0, 0x10, 0x12) + create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x12, 0x12, 'Ice Conveyor', fix=True, embed=True) + create_sprite(0x003e, EnemySprite.Babasu, 0x00, 0, 0x15, 0x12, 'Ice Conveyor', fix=True, embed=True) + create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x16, 'Ice Conveyor') + create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x11, 0x18, 'Ice Conveyor', True, 0xe4) + create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x19, 'Ice Conveyor') + create_sprite(0x003e, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x1a, 'Ice Conveyor') + create_sprite(0x003f, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x04, 0x15) + create_sprite(0x003f, EnemySprite.StalfosKnight, 0x00, 0, 0x0c, 0x16, 'Ice Right H') + create_sprite(0x003f, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x13, 0x15) + create_sprite(0x003f, EnemySprite.StalfosKnight, 0x00, 0, 0x04, 0x17, 'Ice Hammer Block') + create_sprite(0x003f, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x18, 'Ice Hammer Block') + create_sprite(0x0040, EnemySprite.BlueGuard, 0x00, 1, 0x09, 0x08, 'Tower Catwalk') + create_sprite(0x0040, EnemySprite.BlueGuard, 0x1b, 1, 0x09, 0x0f, 'Tower Catwalk') + create_sprite(0x0040, EnemySprite.Statue, 0x00, 1, 0x18, 0x15, 'Tower Push Statue', fix=True) + create_sprite(0x0040, EnemySprite.RedSpearGuard, 0x00, 1, 0x1b, 0x18, 'Tower Push Statue') + create_sprite(0x0040, EnemySprite.BlueArcher, 0x00, 1, 0x17, 0x1a, 'Tower Push Statue') + create_sprite(0x0040, EnemySprite.BlueArcher, 0x00, 1, 0x19, 0x1a, 'Tower Push Statue') + create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x11, 0x0a, 'Sewers Behind Tapestry') + create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x1b, 0x0b, 'Sewers Behind Tapestry') + create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x0f, 0x0d, 'Sewers Behind Tapestry') + create_sprite(0x0041, EnemySprite.CricketRat, 0x00, 0, 0x06, 0x15, 'Sewers Behind Tapestry') + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x12, 0x06, 'Sewers Rope Room') + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x13, 0x06, 'Sewers Rope Room') + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x14, 0x06, 'Sewers Rope Room') + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x12, 0x07, 'Sewers Rope Room') + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x13, 0x07, 'Sewers Rope Room') + create_sprite(0x0042, EnemySprite.Snake, 0x00, 0, 0x14, 0x07, 'Sewers Rope Room') + create_sprite(0x0043, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x0c, 0x06, 'Desert Wall Slide') + create_sprite(0x0043, 0x14, SpriteType.Overlord, 0, 0x17, 0x18) + create_sprite(0x0044, EnemySprite.Bumper, 0x00, 0, 0x09, 0x06, 'Thieves Trap') + create_sprite(0x0044, EnemySprite.Bumper, 0x00, 0, 0x05, 0x08, 'Thieves Trap') + create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x04, 'Thieves Trap') + create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x03, 0x08, 'Thieves Trap') + create_sprite(0x0044, EnemySprite.Blob, 0x00, 0, 0x17, 0x08, 'Thieves Conveyor Bridge') + create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x0c, 'Thieves Trap') + create_sprite(0x0044, EnemySprite.RedBari, 0x00, 0, 0x17, 0x0f, 'Thieves Conveyor Bridge') + create_sprite(0x0044, 0x0a, SpriteType.Overlord, 0, 0x0b, 0x15) + create_sprite(0x0044, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x16, 'Thieves Conveyor Bridge') + create_sprite(0x0045, EnemySprite.BlindMaiden, 0x00, 0, 0x19, 0x06) + create_sprite(0x0045, EnemySprite.RedZazak, 0x00, 0, 0x06, 0x06, 'Thieves Basement Block') + create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x04, 0x0b, 'Thieves Basement Block') + create_sprite(0x0045, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x0b, 'Thieves Basement Block') + create_sprite(0x0045, EnemySprite.BunnyBeam, 0x00, 0, 0x17, 0x0b, "Thieves Blind's Cell Interior") # , fix=True) + create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x18, 0x0c, "Thieves Blind's Cell Interior") + create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x1a, 0x0c, "Thieves Blind's Cell Interior") + create_sprite(0x0045, EnemySprite.BlueZazak, 0x00, 0, 0x18, 0x11, "Thieves Blind's Cell Interior") + create_sprite(0x0045, EnemySprite.Blob, 0x00, 0, 0x16, 0x18, "Thieves Blind's Cell") + create_sprite(0x0045, EnemySprite.RedZazak, 0x00, 0, 0x19, 0x1b, "Thieves Blind's Cell") + create_sprite(0x0045, EnemySprite.RedZazak, 0x00, 0, 0x07, 0x1c, 'Thieves Lonely Zazak') + create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x16, 0x05, 'Swamp Donut Top', water=True) + create_sprite(0x0046, 0x11, SpriteType.Overlord, 0, 0x1b, 0x06) + create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x09, 0x1a, 'Swamp Donut Bottom', water=True) + create_sprite(0x0046, 0x11, SpriteType.Overlord, 0, 0x1b, 0x1a) + create_sprite(0x0046, EnemySprite.Hover, 0x00, 0, 0x11, 0x1b, 'Swamp Donut Bottom', water=True) + create_sprite(0x0049, EnemySprite.MiniMoldorm, 0x00, 0, 0x0b, 0x05, 'Skull Vines') + create_sprite(0x0049, EnemySprite.MiniMoldorm, 0x00, 0, 0x04, 0x0b, 'Skull Vines') + create_sprite(0x0049, EnemySprite.MiniMoldorm, 0x00, 0, 0x09, 0x0c, 'Skull Vines') + create_sprite(0x0049, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x06, 'Skull Vines') + create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x07, 0x08, 'Skull Vines') + create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x0b, 'Skull Torch Room') + create_sprite(0x0049, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f, 'Skull Torch Room') + create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x10, 'Skull Torch Room') + create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x16, 0x14, 'Skull Torch Room') + create_sprite(0x0049, EnemySprite.BlueBari, 0x00, 0, 0x09, 0x16, 'Skull Star Pits') + create_sprite(0x0049, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x17, 'Skull Star Pits') + create_sprite(0x0049, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x18, 'Skull Star Pits') + create_sprite(0x0049, EnemySprite.Gibdo, 0x00, 0, 0x1a, 0x18, 'Skull Torch Room') + create_sprite(0x004a, EnemySprite.Statue, 0x00, 0, 0x14, 0x07, 'PoD Middle Cage') + create_sprite(0x004a, EnemySprite.MiniHelmasaur, 0x00, 0, 0x08, 0x08, 'PoD Left Cage') + create_sprite(0x004a, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x08, 'PoD Middle Cage') + create_sprite(0x004b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x07, 0x04, 'PoD Mimics 1') + create_sprite(0x004b, EnemySprite.AntiFairy, 0x00, 0, 0x17, 0x05, 'PoD Warp Hint') + create_sprite(0x004b, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x06, 'PoD Warp Hint') + create_sprite(0x004b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x04, 0x08, 'PoD Mimics 1') + create_sprite(0x004b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0b, 0x08, 'PoD Mimics 1') + create_sprite(0x004b, EnemySprite.BlueBari, 0x00, 0, 0x0f, 0x18, 'PoD Jelly Hall') + create_sprite(0x004b, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x19, 'PoD Jelly Hall') + create_sprite(0x004b, EnemySprite.BlueBari, 0x00, 0, 0x12, 0x19, 'PoD Jelly Hall') + create_sprite(0x004c, EnemySprite.Bumper, 0x00, 0, 0x15, 0x11, 'GT Frozen Over') + create_sprite(0x004c, EnemySprite.Bumper, 0x00, 0, 0x19, 0x12, 'GT Frozen Over') + create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x15, 0x05, 'GT Frozen Over') + create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x1a, 0x05, 'GT Frozen Over') + create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x17, 0x06, 'GT Frozen Over') + create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x0a, 'GT Frozen Over') + create_sprite(0x004c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x14, 0x15, 'GT Frozen Over') + create_sprite(0x004c, EnemySprite.SpikeBlock, 0x00, 0, 0x13, 0x18, 'GT Frozen Over') + create_sprite(0x004d, EnemySprite.Moldorm, 0x00, 0, 0x09, 0x09) + create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x14, 0x08, 'Ice Narrow Corridor') + create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x16, 0x08, 'Ice Narrow Corridor') + create_sprite(0x004e, EnemySprite.Blob, 0x00, 0, 0x18, 0x08, 'Ice Narrow Corridor') + create_sprite(0x004e, EnemySprite.FirebarCW, 0x00, 0, 0x07, 0x09, 'Ice Bomb Jump Catwalk') + create_sprite(0x004f, EnemySprite.Faerie, 0x00, 0, 0x17, 0x06, 'Ice Fairy') + create_sprite(0x004f, EnemySprite.Faerie, 0x00, 0, 0x14, 0x08, 'Ice Fairy') + create_sprite(0x004f, EnemySprite.Faerie, 0x00, 0, 0x1a, 0x08, 'Ice Fairy') + create_sprite(0x0050, EnemySprite.GreenGuard, 0x00, 1, 0x17, 0x0e, 'Hyrule Castle West Hall') + create_sprite(0x0050, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x18, 0x10, 'Hyrule Castle West Hall') + create_sprite(0x0050, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x17, 0x12, 'Hyrule Castle West Hall') + create_sprite(0x0051, EnemySprite.CastleMantle, 0x00, 0, 0x0e, 0x02, 'Hyrule Castle Throne Room') + create_sprite(0x0051, EnemySprite.BlueGuard, 0x01, 1, 0x09, 0x17, 'Hyrule Castle Throne Room') + create_sprite(0x0051, EnemySprite.BlueGuard, 0x02, 1, 0x16, 0x17, 'Hyrule Castle Throne Room') + create_sprite(0x0052, EnemySprite.GreenGuard, 0x00, 1, 0x07, 0x0d, 'Hyrule Castle East Hall') + create_sprite(0x0052, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x08, 0x0f, 'Hyrule Castle East Hall') + create_sprite(0x0052, EnemySprite.GreenKnifeGuard, 0x00, 1, 0x07, 0x12, 'Hyrule Castle East Hall') + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x17, 0x07, 'Desert Beamos Hall') + create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x1c, 0x09, 'Desert Beamos Hall') + create_sprite(0x0053, EnemySprite.Popo2, 0x00, 0, 0x17, 0x0c, 'Desert Beamos Hall') + create_sprite(0x0053, EnemySprite.Popo2, 0x00, 0, 0x1a, 0x0c, 'Desert Beamos Hall') + create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x13, 0x0e, 'Desert Beamos Hall') + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x05, 0x15, 'Desert Four Statues') + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x0b, 0x16, 'Desert Four Statues') + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x1a, 0x17, 'Desert Beamos Hall') + create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x07, 0x19, 'Desert Four Statues') + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x04, 0x1a, 'Desert Four Statues') + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x0b, 0x1a, 'Desert Four Statues') + create_sprite(0x0053, EnemySprite.Beamos, 0x00, 0, 0x1b, 0x1a, 'Desert Beamos Hall') + create_sprite(0x0053, EnemySprite.Popo, 0x00, 0, 0x1a, 0x1b, 'Desert Beamos Hall') + create_sprite(0x0054, EnemySprite.Kyameron, 0x00, 0, 0x0e, 0x05, 'Swamp Attic', water=True) + create_sprite(0x0054, EnemySprite.Hover, 0x00, 0, 0x0c, 0x0b, 'Swamp Attic', water=True) + create_sprite(0x0054, EnemySprite.Medusa, 0x00, 0, 0x0b, 0x0e, 'Swamp Attic', water=True) + create_sprite(0x0054, EnemySprite.FirebarCW, 0x00, 0, 0x0f, 0x0e, 'Swamp Attic') + create_sprite(0x0054, EnemySprite.Hover, 0x00, 0, 0x10, 0x0f, 'Swamp Attic', water=True) + create_sprite(0x0054, EnemySprite.Kyameron, 0x00, 0, 0x12, 0x14, 'Swamp Attic', water=True) + create_sprite(0x0054, EnemySprite.Hover, 0x00, 0, 0x0f, 0x15, 'Swamp Attic', water=True) + create_sprite(0x0054, EnemySprite.Kyameron, 0x00, 0, 0x0c, 0x17, 'Swamp Attic', water=True) + create_sprite(0x0055, EnemySprite.UnclePriest, 0x00, 0, 0x0e, 0x08, 'Hyrule Castle Secret Entrance') + create_sprite(0x0055, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x14, 0x15, 'Hyrule Castle Secret Entrance') + create_sprite(0x0055, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x0d, 0x16, 'Hyrule Castle Secret Entrance') + create_sprite(0x0056, 0x0a, SpriteType.Overlord, 0, 0x0b, 0x05, 'Skull X Room') + create_sprite(0x0056, EnemySprite.Bumper, 0x00, 0, 0x07, 0x19, 'Skull 2 West Lobby') + create_sprite(0x0056, EnemySprite.Bumper, 0x00, 0, 0x17, 0x19, 'Skull Small Hall') + create_sprite(0x0056, EnemySprite.MiniHelmasaur, 0x00, 0, 0x07, 0x04, 'Skull X Room') + create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x05, 'Skull Back Drop') + create_sprite(0x0056, EnemySprite.MiniHelmasaur, 0x00, 0, 0x03, 0x06, 'Skull X Room') + create_sprite(0x0056, EnemySprite.MiniHelmasaur, 0x00, 0, 0x0c, 0x06, 'Skull X Room') + create_sprite(0x0056, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) + create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x11, 'Skull Back Drop') + create_sprite(0x0056, EnemySprite.SpikeBlock, 0x00, 0, 0x18, 0x12, 'Skull Back Drop') + create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x03, 0x1b, 'Skull 2 West Lobby') + create_sprite(0x0056, EnemySprite.Firesnake, 0x00, 0, 0x13, 0x1c, 'Skull Small Hall') + create_sprite(0x0056, EnemySprite.HardhatBeetle, 0x00, 0, 0x19, 0x1c, 'Skull Small Hall') + create_sprite(0x0057, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x04, 'Skull Big Key') # , fix=True) + create_sprite(0x0057, EnemySprite.RedBari, 0x00, 0, 0x0c, 0x04, 'Skull Big Key') + create_sprite(0x0057, EnemySprite.SpikeBlock, 0x00, 0, 0x08, 0x05, 'Skull Big Key') + create_sprite(0x0057, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x07, 'Skull Big Key') + create_sprite(0x0057, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x0c, 'Skull Big Key') + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x0c, 0x0c, 'Skull Big Key') + create_sprite(0x0057, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x05, 0x14, 'Skull 2 East Lobby') + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x0a, 0x14, 'Skull 2 East Lobby') + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x14, 'Skull Pot Prison') + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x19, 0x14, 'Skull Pot Prison') + create_sprite(0x0057, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x15, 'Skull Pot Prison') + create_sprite(0x0057, EnemySprite.Gibdo, 0x00, 0, 0x13, 0x17, 'Skull Pot Prison') + create_sprite(0x0057, EnemySprite.BlueBari, 0x00, 0, 0x07, 0x18, 'Skull 2 East Lobby') + create_sprite(0x0057, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x18, 'Skull 2 East Lobby') + create_sprite(0x0057, EnemySprite.Statue, 0x00, 0, 0x0b, 0x18, 'Skull 2 East Lobby', fix=True) + create_sprite(0x0058, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x14, 'Skull Big Chest') + create_sprite(0x0058, EnemySprite.MiniMoldorm, 0x00, 0, 0x06, 0x16, 'Skull Big Chest') + create_sprite(0x0058, EnemySprite.Bumper, 0x00, 0, 0x16, 0x16, 'Skull Map Room') + create_sprite(0x0058, EnemySprite.MiniHelmasaur, 0x00, 0, 0x14, 0x04, 'Skull Pot Circle') + create_sprite(0x0058, EnemySprite.SparkCW, 0x00, 0, 0x16, 0x06, 'Skull Pot Circle') + create_sprite(0x0058, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x08, 0x0a) + create_sprite(0x0058, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x0b, 'Skull Pot Circle') + create_sprite(0x0058, EnemySprite.HardhatBeetle, 0x00, 0, 0x16, 0x19, 'Skull Map Room') + create_sprite(0x0058, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x1a, 'Skull 1 Lobby') + create_sprite(0x0059, EnemySprite.MiniMoldorm, 0x00, 0, 0x07, 0x10, 'Skull 3 Lobby') + create_sprite(0x0059, EnemySprite.MiniMoldorm, 0x00, 0, 0x08, 0x16, 'Skull 3 Lobby') + create_sprite(0x0059, EnemySprite.Bumper, 0x00, 1, 0x14, 0x0f, 'Skull East Bridge') + create_sprite(0x0059, EnemySprite.Bumper, 0x00, 1, 0x1a, 0x0f, 'Skull East Bridge') + create_sprite(0x0059, EnemySprite.SpikeBlock, 0x00, 1, 0x1a, 0x0a, 'Skull East Bridge') + create_sprite(0x0059, EnemySprite.Firesnake, 0x00, 0, 0x08, 0x0b, 'Skull 3 Lobby') + create_sprite(0x0059, EnemySprite.SpikeBlock, 0x00, 1, 0x15, 0x0d, 'Skull East Bridge') + create_sprite(0x0059, EnemySprite.SparkCW, 0x00, 1, 0x05, 0x0e, 'Skull 3 Lobby') + create_sprite(0x0059, EnemySprite.BunnyBeam, 0x00, 1, 0x1a, 0x13, 'Skull East Bridge') + create_sprite(0x0059, EnemySprite.Gibdo, 0x00, 0, 0x17, 0x14, 'Skull East Bridge') + create_sprite(0x0059, EnemySprite.Gibdo, 0x00, 1, 0x15, 0x15, 'Skull East Bridge') + create_sprite(0x0059, EnemySprite.Gibdo, 0x00, 1, 0x1a, 0x15, 'Skull East Bridge') + create_sprite(0x005a, EnemySprite.HelmasaurKing, 0x00, 0, 0x07, 0x06) + create_sprite(0x005b, EnemySprite.CrystalSwitch, 0x00, 1, 0x17, 0x0c) + create_sprite(0x005b, EnemySprite.CrystalSwitch, 0x00, 1, 0x18, 0x13) + create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x17, 0x15, 'GT Hidden Spikes') + create_sprite(0x005b, EnemySprite.GreenEyegoreMimic, 0x00, 1, 0x16, 0x08, 'GT Hidden Spikes') + create_sprite(0x005b, EnemySprite.RedEyegoreMimic, 0x00, 1, 0x19, 0x08, 'GT Hidden Spikes') + create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x14, 0x0e, 'GT Hidden Spikes') + create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x1b, 0x10, 'GT Hidden Spikes') + create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x17, 0x11, 'GT Hidden Spikes') + create_sprite(0x005b, EnemySprite.SpikeBlock, 0x00, 1, 0x14, 0x12, 'GT Hidden Spikes') + create_sprite(0x005c, EnemySprite.WallCannonHorzTop, 0x00, 0, 0x0b, 0x02, embed=True) + create_sprite(0x005c, EnemySprite.WallCannonHorzBottom, 0x00, 0, 0x05, 0x0e, embed=True) + create_sprite(0x005c, EnemySprite.WallCannonHorzBottom, 0x00, 0, 0x0e, 0x0e, embed=True) + create_sprite(0x005c, EnemySprite.Faerie, 0x00, 0, 0x17, 0x18, 'GT Refill') + create_sprite(0x005c, EnemySprite.Faerie, 0x00, 0, 0x18, 0x18, 'GT Refill') + create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x05, 'GT Gauntlet 2') + create_sprite(0x005d, EnemySprite.Beamos, 0x00, 0, 0x08, 0x06, 'GT Gauntlet 2') + create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x08, 'GT Gauntlet 2') + create_sprite(0x005d, EnemySprite.RedZazak, 0x00, 0, 0x15, 0x08, 'GT Gauntlet 1') + create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x08, 'GT Gauntlet 1') + create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x19, 0x08, 'GT Gauntlet 1') + create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x1b, 0x08, 'GT Gauntlet 1') + create_sprite(0x005d, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x0b, 'GT Gauntlet 2') + create_sprite(0x005d, EnemySprite.Beamos, 0x00, 0, 0x04, 0x15, 'GT Gauntlet 3') + create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x0b, 0x15, 'GT Gauntlet 3') + create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x04, 0x1a, 'GT Gauntlet 3') + create_sprite(0x005d, EnemySprite.BlueZazak, 0x00, 0, 0x08, 0x1a, 'GT Gauntlet 3') + create_sprite(0x005d, EnemySprite.Beamos, 0x00, 0, 0x0b, 0x1a, 'GT Gauntlet 3') + create_sprite(0x005e, 0x0a, SpriteType.Overlord, 0, 0x1b, 0x05) + create_sprite(0x005e, EnemySprite.Medusa, 0x00, 0, 0x1c, 0x05, 'Ice Falling Square') + create_sprite(0x005e, EnemySprite.Medusa, 0x00, 0, 0x13, 0x0b, 'Ice Falling Square') + create_sprite(0x005e, EnemySprite.BigSpike, 0x00, 0, 0x17, 0x14, 'Ice Spike Cross') + create_sprite(0x005e, EnemySprite.FirebarCW, 0x00, 0, 0x08, 0x18, 'Ice Firebar') + create_sprite(0x005f, EnemySprite.BlueBari, 0x00, 0, 0x04, 0x18, 'Ice Spike Room') + create_sprite(0x005f, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x18, 'Ice Spike Room') + create_sprite(0x005f, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x1b, 'Ice Spike Room') + create_sprite(0x0060, EnemySprite.BlueGuard, 0x13, 0, 0x13, 0x08, 'Hyrule Castle West Lobby') + create_sprite(0x0061, EnemySprite.GreenGuard, 0x01, 0, 0x0c, 0x0e, 'Hyrule Castle Lobby') + create_sprite(0x0061, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x0d, 0x12, 'Hyrule Castle Lobby') + create_sprite(0x0061, EnemySprite.GreenKnifeGuard, 0x00, 0, 0x12, 0x12, 'Hyrule Castle Lobby') + create_sprite(0x0062, EnemySprite.BlueGuard, 0x13, 0, 0x0c, 0x08, 'Hyrule Castle East Lobby') + create_sprite(0x0062, EnemySprite.GreenGuard, 0x00, 1, 0x0a, 0x0d, 'Hyrule Castle East Lobby') + create_sprite(0x0062, EnemySprite.GreenGuard, 0x00, 1, 0x11, 0x0e, 'Hyrule Castle East Lobby') + create_sprite(0x0063, 0x14, SpriteType.Overlord, 0, 0x07, 0x08) + create_sprite(0x0063, EnemySprite.Beamos, 0x00, 0, 0x07, 0x18, 'Desert Back Lobby') + create_sprite(0x0064, EnemySprite.Keese, 0x00, 0, 0x05, 0x12, 'Thieves Attic Hint', embed=True) + create_sprite(0x0064, EnemySprite.WrongPullSwitch, 0x00, 0, 0x0b, 0x13) + create_sprite(0x0064, EnemySprite.Keese, 0x00, 0, 0x05, 0x13, 'Thieves Attic Hint') + create_sprite(0x0064, EnemySprite.BunnyBeam, 0x00, 0, 0x03, 0x16, 'Thieves Attic Hint') # , fix=True) + create_sprite(0x0064, EnemySprite.CricketRat, 0x00, 0, 0x17, 0x17, 'Thieves Cricket Hall Left') + create_sprite(0x0064, EnemySprite.CricketRat, 0x00, 0, 0x19, 0x19, 'Thieves Cricket Hall Left') + create_sprite(0x0064, EnemySprite.CricketRat, 0x00, 0, 0x05, 0x1a, 'Thieves Attic') + create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x09, 0x15) + create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x07, 0x17) + create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x09, 0x17) + create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x0b, 0x17) + create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x09, 0x19) + create_sprite(0x0064, 0x06, SpriteType.Overlord, 0, 0x0c, 0x1b) + create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x13, 0x15, 'Thieves Attic Window') + create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x09, 0x17, 'Thieves Cricket Hall Right') + create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x06, 0x18, 'Thieves Cricket Hall Right') + create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x16, 0x19, 'Thieves Attic Window') + create_sprite(0x0065, EnemySprite.CricketRat, 0x00, 0, 0x16, 0x1c, 'Thieves Attic Window') + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x0b, 0x05, 'Swamp Refill') + create_sprite(0x0066, 0x10, SpriteType.Overlord, 1, 0x04, 0x06) + create_sprite(0x0066, EnemySprite.BlueBari, 0x00, 0, 0x16, 0x06, 'Swamp Behind Waterfall') + create_sprite(0x0066, EnemySprite.BlueBari, 0x00, 0, 0x1a, 0x07, 'Swamp Behind Waterfall') + create_sprite(0x0066, EnemySprite.Waterfall, 0x00, 1, 0x17, 0x14, 'Swamp Waterfall Room') + create_sprite(0x0066, 0x10, SpriteType.Overlord, 1, 0x01, 0x16) + create_sprite(0x0066, EnemySprite.Kyameron, 0x00, 1, 0x0f, 0x16, 'Swamp Waterfall Room', water=True) + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x13, 0x16, 'Swamp Waterfall Room', water=True) + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x0b, 0x18, 'Swamp Waterfall Room', water=True) + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x0d, 0x19, 'Swamp Waterfall Room', water=True) + create_sprite(0x0066, 0x11, SpriteType.Overlord, 1, 0x1e, 0x19) + create_sprite(0x0066, EnemySprite.Hover, 0x00, 1, 0x17, 0x1b, 'Swamp Waterfall Room', water=True) + create_sprite(0x0067, EnemySprite.Bumper, 0x00, 0, 0x07, 0x0c, 'Skull Left Drop') + create_sprite(0x0067, EnemySprite.BlueBari, 0x00, 0, 0x04, 0x06, 'Skull Left Drop') + create_sprite(0x0067, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x06, 'Skull Left Drop') + create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x0c, 'Skull Left Drop') + create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x0f, 'Skull Compass Room') + create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x13, 'Skull Left Drop') + create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x13, 'Skull Left Drop') + create_sprite(0x0067, EnemySprite.FirebarCW, 0x00, 0, 0x18, 0x14, 'Skull Compass Room') + create_sprite(0x0067, EnemySprite.FirebarCCW, 0x00, 0, 0x07, 0x17, 'Skull Left Drop') + create_sprite(0x0067, EnemySprite.HardhatBeetle, 0x00, 0, 0x18, 0x1a, 'Skull Compass Room') + create_sprite(0x0068, EnemySprite.Bumper, 0x00, 0, 0x0e, 0x07, 'Skull Pinball') + create_sprite(0x0068, EnemySprite.Bumper, 0x00, 0, 0x11, 0x07, 'Skull Pinball') + create_sprite(0x0068, EnemySprite.Bumper, 0x00, 0, 0x0c, 0x0b, 'Skull Pinball') + create_sprite(0x0068, EnemySprite.Bumper, 0x00, 0, 0x13, 0x0b, 'Skull Pinball') + create_sprite(0x0068, EnemySprite.Gibdo, 0x00, 0, 0x14, 0x08, 'Skull Pinball') + create_sprite(0x0068, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) + create_sprite(0x0068, EnemySprite.Gibdo, 0x00, 0, 0x0e, 0x12, 'Skull Pinball') + create_sprite(0x0068, EnemySprite.Gibdo, 0x00, 0, 0x12, 0x12, 'Skull Pinball') + create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x17, 0x0a, 'PoD Dark Alley') + create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x18, 0x0a, 'PoD Dark Alley') + create_sprite(0x006a, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x0b, 'PoD Dark Basement') + create_sprite(0x006a, EnemySprite.AntiFairy, 0x00, 0, 0x1c, 0x0b, 'PoD Dark Basement') + create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x17, 0x0e, 'PoD Dark Alley') + create_sprite(0x006a, EnemySprite.Terrorpin, 0x00, 0, 0x18, 0x0e, 'PoD Dark Alley') + create_sprite(0x006b, EnemySprite.CrystalSwitch, 0x00, 0, 0x07, 0x04) + create_sprite(0x006b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0b, 0x04) + create_sprite(0x006b, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0a, 0x06, 'GT Crystal Paths') + create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x06, 0x09, 'GT Crystal Paths') + create_sprite(0x006b, EnemySprite.AntiFairy, 0x00, 0, 0x0c, 0x0a, 'GT Crystal Paths') + create_sprite(0x006b, EnemySprite.Statue, 0x00, 0, 0x06, 0x15, 'GT Mimics 1') + create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x03, 0x18, 'GT Mimics 1') + create_sprite(0x006b, EnemySprite.SpikeBlock, 0x00, 0, 0x04, 0x18, 'GT Mimics 1') + create_sprite(0x006b, EnemySprite.SpikeBlock, 0x00, 0, 0x04, 0x1b, 'GT Mimics 1') + create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x0c, 0x1b, 'GT Mimics 1') + create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x17, 0x15, 'GT Mimics 2') + create_sprite(0x006b, EnemySprite.Beamos, 0x00, 0, 0x1b, 0x15, 'GT Mimics 2') + create_sprite(0x006b, EnemySprite.Beamos, 0x00, 0, 0x14, 0x1b, 'GT Mimics 2') + create_sprite(0x006b, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x18, 0x1b, 'GT Mimics 2') + create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x06, 0x07, 'GT Lanmolas 2') + create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x09, 0x07, 'GT Lanmolas 2') + create_sprite(0x006c, EnemySprite.Lanmolas, 0x00, 0, 0x07, 0x09, 'GT Lanmolas 2') + create_sprite(0x006c, EnemySprite.BunnyBeam, 0x00, 0, 0x17, 0x18, 'GT Beam Dash', fix=True) + create_sprite(0x006c, EnemySprite.Medusa, 0x00, 0, 0x03, 0x1c, 'GT Lanmolas 2') + create_sprite(0x006d, EnemySprite.RedZazak, 0x00, 0, 0x05, 0x06, 'GT Gauntlet 4') + create_sprite(0x006d, EnemySprite.Beamos, 0x00, 0, 0x0b, 0x06, 'GT Gauntlet 4') + create_sprite(0x006d, EnemySprite.Beamos, 0x00, 0, 0x04, 0x09, 'GT Gauntlet 4') + create_sprite(0x006d, EnemySprite.RedZazak, 0x00, 0, 0x0a, 0x0b, 'GT Gauntlet 4') + create_sprite(0x006d, EnemySprite.Medusa, 0x00, 0, 0x04, 0x15, 'GT Gauntlet 5') + create_sprite(0x006d, EnemySprite.Beamos, 0x00, 0, 0x0b, 0x15, 'GT Gauntlet 5') + create_sprite(0x006d, EnemySprite.Stalfos, 0x00, 0, 0x05, 0x18, 'GT Gauntlet 5') + create_sprite(0x006d, EnemySprite.RedZazak, 0x00, 0, 0x0a, 0x18, 'GT Gauntlet 5') + create_sprite(0x006d, EnemySprite.SparkCCW, 0x00, 0, 0x06, 0x1a, 'GT Gauntlet 5') + create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x08, 'Ice Pengator Trap') + create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x09, 'Ice Pengator Trap') + create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x0a, 'Ice Pengator Trap') + create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x0b, 'Ice Pengator Trap') + create_sprite(0x006e, EnemySprite.Pengator, 0x00, 0, 0x13, 0x0c, 'Ice Pengator Trap') + create_sprite(0x0071, EnemySprite.GreenGuard, 0x00, 1, 0x06, 0x18, 'Hyrule Dungeon Armory Main') + create_sprite(0x0071, EnemySprite.BlueGuard, 0x15, 1, 0x1a, 0x18, 'Hyrule Dungeon Armory Boomerang', True, 0xe4) + create_sprite(0x0072, EnemySprite.BlueGuard, 0x05, 0, 0x11, 0x06, 'Hyrule Dungeon Map Room', True, 0xe4) + create_sprite(0x0072, EnemySprite.BlueGuard, 0x01, 1, 0x0a, 0x19, 'Hyrule Dungeon North Abyss') + create_sprite(0x0073, EnemySprite.Debirando, 0x00, 0, 0x18, 0x18, 'Desert Sandworm Corner') + create_sprite(0x0073, EnemySprite.Beamos, 0x00, 0, 0x17, 0x09, 'Desert Bonk Torch') + create_sprite(0x0073, EnemySprite.Leever, 0x00, 0, 0x15, 0x15, 'Desert Sandworm Corner') + create_sprite(0x0073, EnemySprite.Leever, 0x00, 0, 0x1b, 0x18, 'Desert Sandworm Corner') + create_sprite(0x0073, EnemySprite.Beamos, 0x00, 0, 0x07, 0x19, 'Desert Circle of Pots') + create_sprite(0x0073, EnemySprite.Leever, 0x00, 0, 0x16, 0x1b, 'Desert Sandworm Corner') + create_sprite(0x0073, EnemySprite.BonkItem, 0x00, 0, 0x14, 0x06) + create_sprite(0x0074, EnemySprite.Debirando, 0x00, 0, 0x08, 0x18, 'Desert North Hall') + create_sprite(0x0074, EnemySprite.Debirando, 0x00, 0, 0x17, 0x18, 'Desert North Hall') + create_sprite(0x0074, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x05, 'Desert Map Room') + create_sprite(0x0074, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x13, 0x05, 'Desert Map Room') + create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x0c, 0x0a, 'Desert Map Room') + create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x13, 0x0a, 'Desert Map Room') + create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x0e, 0x1b, 'Desert Dead End') + create_sprite(0x0074, EnemySprite.Leever, 0x00, 0, 0x12, 0x1b, 'Desert Dead End') + create_sprite(0x0075, EnemySprite.Debirando, 0x00, 0, 0x08, 0x07, 'Desert Trap Room') + create_sprite(0x0075, EnemySprite.Debirando, 0x00, 0, 0x04, 0x1b, 'Desert Arrow Pot Corner') + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x06, 0x05, 'Desert Trap Room') + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x0a, 0x05, 'Desert Trap Room') + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x06, 0x0a, 'Desert Trap Room') + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x0a, 0x0a, 'Desert Trap Room') + create_sprite(0x0075, EnemySprite.WallCannonVertLeft, 0x00, 0, 0x11, 0x0b, embed=True) + create_sprite(0x0075, EnemySprite.WallCannonVertRight, 0x00, 0, 0x1e, 0x0b, embed=True) + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x07, 0x19, 'Desert Arrow Pot Corner') + create_sprite(0x0075, EnemySprite.Leever, 0x00, 0, 0x09, 0x19, 'Desert Arrow Pot Corner') + create_sprite(0x0076, EnemySprite.WaterSwitch, 0x00, 0, 0x19, 0x03) + create_sprite(0x0076, EnemySprite.Hover, 0x00, 0, 0x07, 0x0a, 'Swamp Basement Shallows', water=True) + create_sprite(0x0076, EnemySprite.Kyameron, 0x00, 0, 0x07, 0x0f, 'Swamp Basement Shallows', water=True) + create_sprite(0x0076, EnemySprite.Hover, 0x00, 0, 0x08, 0x11, 'Swamp Basement Shallows', water=True) + create_sprite(0x0076, EnemySprite.Blob, 0x00, 0, 0x1b, 0x19, 'Swamp Flooded Room') + create_sprite(0x0076, 0x13, SpriteType.Overlord, 0, 0x08, 0x1c) + create_sprite(0x0076, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x1c, 'Swamp Flooded Room') + create_sprite(0x0077, EnemySprite.MiniMoldorm, 0x00, 1, 0x0b, 0x09, 'Hera Back') + create_sprite(0x0077, EnemySprite.CrystalSwitch, 0x00, 1, 0x10, 0x18) + create_sprite(0x0077, EnemySprite.CrystalSwitch, 0x00, 1, 0x09, 0x1a) + create_sprite(0x0077, EnemySprite.CrystalSwitch, 0x00, 1, 0x16, 0x1a) + create_sprite(0x0077, EnemySprite.Kodongo, 0x00, 1, 0x07, 0x0a, 'Hera Back') + create_sprite(0x0077, EnemySprite.Kodongo, 0x00, 1, 0x17, 0x0a, 'Hera Back') + create_sprite(0x007b, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x07, 'GT Conveyor Star Pits') + create_sprite(0x007b, EnemySprite.BlueBari, 0x00, 0, 0x16, 0x09, 'GT Conveyor Star Pits') + create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x04, 0x15, 'GT DMs Room') + create_sprite(0x007b, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x15, 'GT DMs Room') + create_sprite(0x007b, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x17, 'GT DMs Room') + create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x09, 0x17, 'GT DMs Room') + create_sprite(0x007b, EnemySprite.Statue, 0x00, 0, 0x13, 0x18, 'GT Hidden Star') + create_sprite(0x007b, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x18, 'GT Hidden Star') + create_sprite(0x007b, EnemySprite.Stalfos, 0x00, 0, 0x09, 0x19, 'GT DMs Room') + create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x05, 0x1a, 'GT DMs Room') + create_sprite(0x007b, EnemySprite.FourWayShooter, 0x00, 0, 0x0b, 0x1b, 'GT DMs Room') + create_sprite(0x007c, EnemySprite.MiniMoldorm, 0x00, 0, 0x19, 0x1c, 'GT Randomizer Room') + create_sprite(0x007c, EnemySprite.FirebarCCW, 0x00, 0, 0x06, 0x0c, 'GT Falling Bridge') + create_sprite(0x007c, EnemySprite.SpikeBlock, 0x00, 0, 0x07, 0x10, 'GT Falling Bridge') + create_sprite(0x007c, EnemySprite.FirebarCW, 0x00, 0, 0x09, 0x14, 'GT Falling Bridge') + create_sprite(0x007c, EnemySprite.HardhatBeetle, 0x00, 0, 0x0b, 0x18, 'GT Falling Bridge') + create_sprite(0x007c, EnemySprite.BlueBari, 0x00, 0, 0x17, 0x18, 'GT Randomizer Room') + create_sprite(0x007c, 0x0b, SpriteType.Overlord, 0, 0x07, 0x1a) + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x06, 'GT Firesnake Room') + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x08, 'GT Firesnake Room') + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x0a, 'GT Firesnake Room') + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x11, 0x0c, 'GT Firesnake Room') + create_sprite(0x007d, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x16, 'GT Petting Zoo') + create_sprite(0x007d, EnemySprite.FourWayShooter, 0x00, 0, 0x18, 0x17, 'GT Petting Zoo') + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x1c, 0x19, 'GT Petting Zoo') + create_sprite(0x007d, EnemySprite.MiniHelmasaur, 0x00, 0, 0x14, 0x1a, 'GT Petting Zoo') + create_sprite(0x007d, EnemySprite.RedBari, 0x00, 0, 0x17, 0x1a, 'GT Petting Zoo') + create_sprite(0x007d, EnemySprite.Firesnake, 0x00, 0, 0x0a, 0x1c, 'GT Warp Maze - Main Rails') + create_sprite(0x007d, EnemySprite.HardhatBeetle, 0x00, 0, 0x1b, 0x1c, 'GT Petting Zoo') + create_sprite(0x007e, EnemySprite.Bumper, 0x00, 0, 0x17, 0x11, 'Ice Tall Hint') + create_sprite(0x007e, EnemySprite.FirebarCCW, 0x00, 0, 0x18, 0x0e, 'Ice Tall Hint') + create_sprite(0x007e, EnemySprite.Pengator, 0x00, 0, 0x14, 0x0f, 'Ice Tall Hint') + create_sprite(0x007e, EnemySprite.Freezor, 0x00, 0, 0x07, 0x12, 'Ice Freezors') + create_sprite(0x007e, EnemySprite.Freezor, 0x00, 0, 0x0a, 0x12, 'Ice Freezors') + create_sprite(0x007e, EnemySprite.Pengator, 0x00, 0, 0x1b, 0x16, 'Ice Tall Hint') + create_sprite(0x007e, EnemySprite.FirebarCCW, 0x00, 0, 0x17, 0x17, 'Ice Tall Hint') + create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x06, 0x07, 'Ice Hookshot Ledge') + create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x08, 0x07, 'Ice Hookshot Ledge') + create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x0a, 0x08, 'Ice Hookshot Ledge') + create_sprite(0x007f, EnemySprite.RedBari, 0x00, 0, 0x07, 0x09, 'Ice Hookshot Ledge') + create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x0b, 0x14, 'Ice Spikeball') + create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x03, 0x17, 'Ice Spikeball') + create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x0b, 0x19, 'Ice Spikeball') + create_sprite(0x007f, EnemySprite.BigSpike, 0x00, 0, 0x03, 0x1b, 'Ice Spikeball') + create_sprite(0x0080, EnemySprite.Zelda, 0x00, 0, 0x16, 0x03, 'Hyrule Dungeon Cell') + create_sprite(0x0080, EnemySprite.GreenGuard, 0x00, 0, 0x07, 0x09, 'Hyrule Dungeon Cellblock') + create_sprite(0x0080, EnemySprite.BallNChain, 0x00, 0, 0x1a, 0x09, 'Hyrule Dungeon Cellblock', True, 0xe5) + create_sprite(0x0081, EnemySprite.GreenGuard, 0x1b, 1, 0x0b, 0x0b, 'Hyrule Dungeon Guardroom') + create_sprite(0x0081, EnemySprite.GreenGuard, 0x03, 1, 0x0e, 0x0b, 'Hyrule Dungeon Guardroom') + create_sprite(0x0082, EnemySprite.BlueGuard, 0x1b, 1, 0x09, 0x05, 'Hyrule Dungeon South Abyss') + create_sprite(0x0082, EnemySprite.BlueGuard, 0x03, 1, 0x10, 0x06, 'Hyrule Dungeon South Abyss') + create_sprite(0x0082, EnemySprite.BlueGuard, 0x03, 1, 0x15, 0x11, 'Hyrule Dungeon South Abyss') + create_sprite(0x0083, EnemySprite.DebirandoPit, 0x00, 0, 0x1b, 0x08, 'Desert West Wing') + create_sprite(0x0083, EnemySprite.DebirandoPit, 0x00, 0, 0x14, 0x10, 'Desert West Wing') + create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x14, 0x05, 'Desert West Wing') + create_sprite(0x0083, EnemySprite.Faerie, 0x00, 0, 0x07, 0x06, 'Desert Fairy Fountain') + create_sprite(0x0083, EnemySprite.Faerie, 0x00, 0, 0x08, 0x08, 'Desert Fairy Fountain') + create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x1b, 0x0b, 'Desert West Wing') + create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x17, 0x10, 'Desert West Wing') + create_sprite(0x0083, EnemySprite.Beamos, 0x00, 0, 0x08, 0x17, 'Desert West Lobby') + create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x18, 0x18, 'Desert West Wing') + create_sprite(0x0083, EnemySprite.Leever, 0x00, 0, 0x14, 0x1b, 'Desert West Wing') + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x03, 0x05, 'Desert Left Alcove') + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x1b, 0x05, 'Desert Right Alcove') + create_sprite(0x0084, EnemySprite.Beamos, 0x00, 0, 0x0f, 0x07, 'Desert Main Lobby') + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x09, 0x12, 'Desert Main Lobby') + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x15, 0x12, 'Desert Main Lobby') + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x09, 0x1b, 'Desert Main Lobby') + create_sprite(0x0084, EnemySprite.Leever, 0x00, 0, 0x15, 0x1b, 'Desert Main Lobby') + create_sprite(0x0085, EnemySprite.DebirandoPit, 0x00, 0, 0x07, 0x0e, 'Desert East Wing') + create_sprite(0x0085, EnemySprite.Debirando, 0x00, 0, 0x09, 0x1b, 'Desert East Wing') + create_sprite(0x0085, EnemySprite.Popo2, 0x00, 0, 0x14, 0x05, 'Desert Compass Room') + create_sprite(0x0085, EnemySprite.Popo2, 0x00, 0, 0x1b, 0x05, 'Desert Compass Room') + create_sprite(0x0085, EnemySprite.Popo2, 0x00, 0, 0x16, 0x08, 'Desert Compass Room') + create_sprite(0x0085, EnemySprite.Beamos, 0x00, 0, 0x18, 0x0a, 'Desert Compass Room') + create_sprite(0x0085, EnemySprite.Leever, 0x00, 0, 0x03, 0x0e, 'Desert East Wing') + create_sprite(0x0085, EnemySprite.Leever, 0x00, 0, 0x0c, 0x15, 'Desert East Wing') + create_sprite(0x0085, EnemySprite.Beamos, 0x00, 0, 0x18, 0x18, 'Desert East Lobby') + create_sprite(0x0085, EnemySprite.Leever, 0x00, 0, 0x07, 0x1c, 'Desert East Wing') + create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x14, 0x05, 'Hera Tridorm') + create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x1a, 0x07, 'Hera Tridorm') + create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x13, 0x0b, 'Hera Tridorm') + create_sprite(0x0087, EnemySprite.MiniMoldorm, 0x00, 0, 0x06, 0x19, 'Hera Basement Cage') + create_sprite(0x0087, 0x14, SpriteType.Overlord, 0, 0x07, 0x08) + create_sprite(0x0087, EnemySprite.CrystalSwitch, 0x00, 0, 0x17, 0x04) + create_sprite(0x0087, EnemySprite.CrystalSwitch, 0x00, 0, 0x03, 0x0c) + create_sprite(0x0087, EnemySprite.CrystalSwitch, 0x00, 0, 0x04, 0x15) + create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x17, 'Hera Basement Cage') + create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x19, 0x18, 'Hera Torches') + create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x19, 'Hera Basement Cage') + create_sprite(0x0087, EnemySprite.SmallKey, 0x00, 0, 0x08, 0x1a, 'Hera Basement Cage') + create_sprite(0x0087, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x1c, 'Hera Torches') + create_sprite(0x0089, EnemySprite.Faerie, 0x00, 0, 0x10, 0x0a, 'Eastern Fairies') + create_sprite(0x0089, EnemySprite.Faerie, 0x00, 0, 0x0f, 0x0b, 'Eastern Fairies') + create_sprite(0x008b, EnemySprite.Bumper, 0x00, 0, 0x15, 0x07, 'GT Conveyor Cross') + create_sprite(0x008b, EnemySprite.CrystalSwitch, 0x00, 0, 0x04, 0x18, 'GT Hookshot South Platform') + create_sprite(0x008b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0b, 0x18, 'GT Hookshot South Platform') + create_sprite(0x008b, EnemySprite.BlueBari, 0x00, 0, 0x1a, 0x04, 'GT Conveyor Cross') + create_sprite(0x008b, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x12, 'GT Hookshot Mid Platform') # todo: boots may be sufficient - special rule? + create_sprite(0x008b, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x18, 'GT Hookshot South Platform') + create_sprite(0x008b, EnemySprite.FirebarCW, 0x00, 0, 0x18, 0x18, 'GT Map Room') + create_sprite(0x008b, EnemySprite.FirebarCCW, 0x00, 0, 0x18, 0x18, 'GT Map Room') + create_sprite(0x008c, EnemySprite.WrongPullSwitch, 0x00, 0, 0x1a, 0x03, 'GT Hope Room') + create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x18, 0x05) + create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x15, 0x06) + create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x1a, 0x06) + create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x15, 0x0a) + create_sprite(0x008c, 0x1a, SpriteType.Overlord, 0, 0x1a, 0x0a) + create_sprite(0x008c, EnemySprite.SparkCW, 0x00, 0, 0x08, 0x08, 'GT Bob\'s Torch') + create_sprite(0x008c, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x08, 'GT Hope Room') + create_sprite(0x008c, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x09, 'GT Bob\'s Torch') + create_sprite(0x008c, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x0b, 'GT Bob\'s Torch') + create_sprite(0x008c, EnemySprite.Firesnake, 0x00, 0, 0x05, 0x17, 'GT Big Chest') + create_sprite(0x008c, EnemySprite.SparkCW, 0x00, 0, 0x16, 0x17, 'GT Bob\'s Room') + create_sprite(0x008c, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x18, 'GT Bob\'s Room') + create_sprite(0x008c, EnemySprite.Firesnake, 0x00, 0, 0x0b, 0x1b, 'GT Big Chest') + create_sprite(0x008c, EnemySprite.AntiFairy, 0x00, 0, 0x1a, 0x1c, 'GT Bob\'s Room') + create_sprite(0x008c, EnemySprite.BonkItem, 0x00, 0, 0x09, 0x07) + create_sprite(0x008d, 0x14, SpriteType.Overlord, 0, 0x07, 0x08) + create_sprite(0x008d, EnemySprite.FourWayShooter, 0x00, 0, 0x07, 0x04, 'GT Tile Room') + create_sprite(0x008d, EnemySprite.AntiFairy, 0x00, 0, 0x09, 0x08, 'GT Tile Room') + create_sprite(0x008d, EnemySprite.BunnyBeam, 0x00, 0, 0x08, 0x09, 'GT Tile Room') + create_sprite(0x008d, EnemySprite.FourWayShooter, 0x00, 0, 0x09, 0x0c, 'GT Tile Room') + create_sprite(0x008d, EnemySprite.Gibdo, 0x00, 0, 0x13, 0x0d, 'GT Speed Torch Upper') + create_sprite(0x008d, 0x09, SpriteType.Overlord, 0, 0x0f, 0x0f) + create_sprite(0x008d, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x10, 'GT Speed Torch') + create_sprite(0x008d, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x14, 'GT Speed Torch') + create_sprite(0x008d, EnemySprite.FirebarCW, 0x00, 0, 0x07, 0x18, 'GT Pots n Blocks') + create_sprite(0x008d, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1b, 'GT Speed Torch') + create_sprite(0x008d, EnemySprite.Medusa, 0x00, 0, 0x13, 0x1c, 'GT Speed Torch') + create_sprite(0x008d, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1c, 'GT Speed Torch') + create_sprite(0x008e, EnemySprite.Freezor, 0x00, 0, 0x1b, 0x02, 'Ice Lonely Freezor') + create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x18, 0x05, 'Ice Lonely Freezor') + create_sprite(0x008e, EnemySprite.BunnyBeam, 0x00, 0, 0x14, 0x06, 'Ice Lonely Freezor') # , fix=True) + create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x1b, 0x08, 'Ice Lonely Freezor') + create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x14, 0x09, 'Ice Lonely Freezor') + create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x16, 0x0a, 'Ice Lonely Freezor') + create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x14, 0x0b, 'Ice Lonely Freezor') + create_sprite(0x008e, EnemySprite.Blob, 0x00, 0, 0x18, 0x0b, 'Ice Lonely Freezor') + create_sprite(0x0090, EnemySprite.Vitreous, 0x00, 0, 0x07, 0x05) + create_sprite(0x0091, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x04, 'Mire Falling Foes') + create_sprite(0x0091, EnemySprite.SpikeBlock, 0x00, 0, 0x1b, 0x0e, 'Mire Falling Foes') + create_sprite(0x0091, 0x08, SpriteType.Overlord, 0, 0x17, 0x0f) + create_sprite(0x0091, EnemySprite.Medusa, 0x00, 0, 0x17, 0x12, 'Mire Falling Foes') + create_sprite(0x0091, EnemySprite.BunnyBeam, 0x00, 0, 0x18, 0x12, 'Mire Falling Foes') + create_sprite(0x0091, EnemySprite.AntiFairy, 0x00, 0, 0x19, 0x12, 'Mire Falling Foes') + create_sprite(0x0091, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x18, 'Mire Falling Foes') + create_sprite(0x0092, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x09, 'Mire Tall Dark and Roomy') + create_sprite(0x0092, EnemySprite.CrystalSwitch, 0x00, 0, 0x03, 0x0c) + create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x04, 'Mire Tall Dark and Roomy') + create_sprite(0x0092, EnemySprite.Medusa, 0x00, 0, 0x0b, 0x05, 'Mire Shooter Rupees') + create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x09, 0x08, 'Mire Shooter Rupees') + create_sprite(0x0092, EnemySprite.Medusa, 0x00, 0, 0x17, 0x09, 'Mire Tall Dark and Roomy') + create_sprite(0x0092, EnemySprite.FourWayShooter, 0x00, 0, 0x15, 0x0f, 'Mire Tall Dark and Roomy') + create_sprite(0x0092, 0x16, SpriteType.Overlord, 0, 0x07, 0x12) + create_sprite(0x0092, EnemySprite.SpikeBlock, 0x00, 0, 0x19, 0x12, 'Mire Tall Dark and Roomy') + create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x14, 'Mire Crystal Right') + create_sprite(0x0092, EnemySprite.Stalfos, 0x00, 0, 0x0a, 0x16, 'Mire Crystal Mid') + create_sprite(0x0092, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x1b, 'Mire Crystal Right') + create_sprite(0x0093, EnemySprite.Medusa, 0x00, 0, 0x09, 0x09) + create_sprite(0x0093, EnemySprite.Medusa, 0x00, 0, 0x16, 0x09) + create_sprite(0x0093, EnemySprite.Medusa, 0x00, 0, 0x0c, 0x0c) + create_sprite(0x0093, EnemySprite.Medusa, 0x00, 0, 0x13, 0x0c) + create_sprite(0x0093, EnemySprite.Blob, 0x00, 0, 0x17, 0x0c, 'Mire Dark Shooters') + create_sprite(0x0093, EnemySprite.Stalfos, 0x00, 0, 0x04, 0x15, 'Mire Block X') + create_sprite(0x0093, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x1c, 'Mire Block X') + create_sprite(0x0093, EnemySprite.AntiFairy, 0x00, 0, 0x04, 0x1c, 'Mire Block X') + create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x16, 0x0c, 'GT Conveyor Bridge') + create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x17, 0x0c, 'GT Conveyor Bridge') + create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x18, 0x0c, 'GT Conveyor Bridge') + create_sprite(0x0095, EnemySprite.RedSpearGuard, 0x00, 0, 0x19, 0x0c, 'GT Conveyor Bridge') + create_sprite(0x0095, 0x0b, SpriteType.Overlord, 0, 0x17, 0x1a) + create_sprite(0x0096, EnemySprite.FirebarCW, 0x00, 0, 0x08, 0x0b, 'GT Torch Cross') + create_sprite(0x0096, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x15, embed=True) + create_sprite(0x0096, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x17, embed=True) + create_sprite(0x0096, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x19, embed=True) + create_sprite(0x0096, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x1b, embed=True) + create_sprite(0x0097, 0x15, SpriteType.Overlord, 0, 0x0f, 0x0f) + create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x10, 0x13, 'Mire Lobby') + create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x09, 0x14, 'Mire Lobby') + create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x0c, 0x14, 'Mire Lobby') + create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x0f, 0x14, 'Mire Lobby') + create_sprite(0x0098, EnemySprite.Blob, 0x00, 0, 0x08, 0x17, 'Mire Lobby') + create_sprite(0x0099, EnemySprite.AntiFairy, 0x00, 0, 0x15, 0x06, 'Eastern Rupees') + create_sprite(0x0099, EnemySprite.AntiFairy, 0x00, 0, 0x1a, 0x08, 'Eastern Rupees') + create_sprite(0x0099, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0e, 0x17, 'Eastern Darkness') + create_sprite(0x0099, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x11, 0x17, 'Eastern Darkness', True, 0xe4) + create_sprite(0x0099, EnemySprite.Popo, 0x00, 0, 0x0d, 0x18, 'Eastern Darkness') + create_sprite(0x0099, EnemySprite.Popo, 0x00, 0, 0x12, 0x18, 'Eastern Darkness') + create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x0e, 0x19, 'Eastern Darkness') + create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x0f, 0x19, 'Eastern Darkness') + create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x10, 0x19, 'Eastern Darkness') + create_sprite(0x0099, EnemySprite.Popo2, 0x00, 0, 0x11, 0x19, 'Eastern Darkness') + create_sprite(0x009b, EnemySprite.CrystalSwitch, 0x00, 0, 0x06, 0x08) + create_sprite(0x009b, EnemySprite.CrystalSwitch, 0x00, 0, 0x07, 0x08) + create_sprite(0x009b, EnemySprite.CrystalSwitch, 0x00, 0, 0x14, 0x08) + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x05, 'GT Spike Crystal Right') + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x06, 'GT Spike Crystal Right') + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x07, 'GT Spike Crystal Right') + create_sprite(0x009b, EnemySprite.FourWayShooter, 0x00, 0, 0x03, 0x08) + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x08, 'GT Spike Crystal Right') + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x09, 'GT Spike Crystal Right') + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x0a, 'GT Spike Crystal Right') + create_sprite(0x009b, EnemySprite.SpikeBlock, 0x00, 0, 0x1c, 0x0b, 'GT Spike Crystal Right') + create_sprite(0x009b, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x1a, 'GT Warp Maze - Pit Section') + create_sprite(0x009b, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x1b, 'GT Warp Maze - Pit Section') + create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x13, 0x09, 'GT Invisible Catwalk') + create_sprite(0x009c, EnemySprite.MiniHelmasaur, 0x00, 0, 0x0b, 0x0a, 'GT Invisible Catwalk') + create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x11, 0x0f, 'GT Invisible Catwalk') + create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x0e, 'GT Invisible Catwalk') + create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x0d, 0x12, 'GT Invisible Catwalk') + create_sprite(0x009c, EnemySprite.HardhatBeetle, 0x00, 0, 0x09, 0x13, 'GT Invisible Catwalk') + create_sprite(0x009c, EnemySprite.Firesnake, 0x00, 0, 0x0f, 0x1c, 'GT Invisible Catwalk') + create_sprite(0x009d, EnemySprite.CrystalSwitch, 0x00, 0, 0x1c, 0x06) + create_sprite(0x009d, EnemySprite.HardhatBeetle, 0x00, 0, 0x06, 0x04, 'GT Compass Room') + create_sprite(0x009d, EnemySprite.Gibdo, 0x00, 0, 0x14, 0x04, 'GT Crystal Conveyor Left') + create_sprite(0x009d, EnemySprite.Gibdo, 0x00, 0, 0x18, 0x09, 'GT Crystal Conveyor') + create_sprite(0x009d, EnemySprite.HardhatBeetle, 0x00, 0, 0x05, 0x0c, 'GT Compass Room') + create_sprite(0x009d, EnemySprite.Gibdo, 0x00, 0, 0x13, 0x0c, 'GT Crystal Conveyor Corner') + create_sprite(0x009d, EnemySprite.BlueBari, 0x00, 0, 0x10, 0x14, 'GT Invisible Bridges') + create_sprite(0x009d, EnemySprite.BlueBari, 0x00, 0, 0x0b, 0x18, 'GT Invisible Bridges') + create_sprite(0x009d, EnemySprite.BlueBari, 0x00, 0, 0x11, 0x1c, 'GT Invisible Bridges') + create_sprite(0x009e, EnemySprite.RedBari, 0x00, 0, 0x18, 0x05, 'Ice Backwards Room') + create_sprite(0x009e, EnemySprite.RedBari, 0x00, 0, 0x16, 0x08, 'Ice Backwards Room') + create_sprite(0x009e, EnemySprite.StalfosKnight, 0x00, 0, 0x18, 0x08, 'Ice Backwards Room') + create_sprite(0x009e, EnemySprite.RedBari, 0x00, 0, 0x19, 0x08, 'Ice Backwards Room') + create_sprite(0x009e, EnemySprite.Freezor, 0x00, 0, 0x14, 0x12, 'Ice Crystal Left') + create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x04, 0x12, 'Ice Many Pots', fix=True, embed=True) + create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x06, 0x12, 'Ice Many Pots', fix=True, embed=True) + create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x09, 0x12, 'Ice Many Pots', fix=True, embed=True) + create_sprite(0x009f, EnemySprite.Babasu, 0x00, 0, 0x0b, 0x12, 'Ice Many Pots', fix=True, embed=True) + create_sprite(0x009f, EnemySprite.AntiFairy, 0x00, 0, 0x07, 0x17, 'Ice Many Pots') + create_sprite(0x009f, EnemySprite.FirebarCW, 0x00, 0, 0x08, 0x18, 'Ice Many Pots') + create_sprite(0x00a0, EnemySprite.Medusa, 0x00, 0, 0x03, 0x08, 'Mire Antechamber') + create_sprite(0x00a0, EnemySprite.AntiFairy, 0x00, 0, 0x0e, 0x08, 'Mire Antechamber') + create_sprite(0x00a0, EnemySprite.Firesnake, 0x00, 0, 0x14, 0x0c, 'Mire Firesnake Skip') + create_sprite(0x00a1, EnemySprite.CrystalSwitch, 0x00, 0, 0x0a, 0x08) + create_sprite(0x00a1, EnemySprite.SparkCW, 0x00, 0, 0x18, 0x07, 'Mire Fishbone') + create_sprite(0x00a1, EnemySprite.SparkCW, 0x00, 0, 0x16, 0x0b, 'Mire Fishbone') + create_sprite(0x00a1, EnemySprite.Wizzrobe, 0x00, 0, 0x19, 0x10, 'Mire Fishbone') + create_sprite(0x00a1, EnemySprite.Medusa, 0x00, 0, 0x15, 0x15, 'Mire South Fish') + create_sprite(0x00a1, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x15, 'Mire South Fish') + create_sprite(0x00a1, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x19, 'Mire South Fish') + create_sprite(0x00a1, EnemySprite.BunnyBeam, 0x00, 0, 0x17, 0x19, 'Mire South Fish') # , fix=True) + create_sprite(0x00a1, EnemySprite.Stalfos, 0x00, 0, 0x1b, 0x19, 'Mire South Fish') + create_sprite(0x00a4, EnemySprite.TrinexxRockHead, 0x00, 0, 0x07, 0x05) + create_sprite(0x00a4, EnemySprite.TrinexxFireHead, 0x00, 0, 0x07, 0x05) + create_sprite(0x00a4, EnemySprite.TrinexxIceHead, 0x00, 0, 0x07, 0x05) + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x16, 0x05, 'GT Wizzrobes 2') + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x19, 0x05, 'GT Wizzrobes 2') + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x04, 0x07, 'GT Wizzrobes 1') + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x0b, 0x07, 'GT Wizzrobes 1') + create_sprite(0x00a5, EnemySprite.SpikeBlock, 0x00, 0, 0x17, 0x08, 'GT Wizzrobes 2') + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x15, 0x09, 'GT Wizzrobes 2') + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x1a, 0x09, 'GT Wizzrobes 2') + create_sprite(0x00a5, EnemySprite.Wizzrobe, 0x00, 0, 0x08, 0x0a, 'GT Wizzrobes 1') + create_sprite(0x00a5, EnemySprite.LaserEyeTop, 0x00, 0, 0x0c, 0x12, embed=True) + create_sprite(0x00a5, EnemySprite.LaserEyeTop, 0x00, 0, 0x12, 0x12, embed=True) + create_sprite(0x00a5, EnemySprite.RedSpearGuard, 0x00, 0, 0x12, 0x17, 'GT Dashing Bridge') + create_sprite(0x00a5, EnemySprite.BlueGuard, 0x00, 0, 0x13, 0x18, 'GT Dashing Bridge') + create_sprite(0x00a6, 0x15, SpriteType.Overlord, 0, 0x0f, 0x0f) + create_sprite(0x00a6, EnemySprite.AntiFairy, 0x00, 0, 0x0c, 0x0e, 'GT Moldorm Pit') + create_sprite(0x00a7, EnemySprite.Faerie, 0x00, 0, 0x06, 0x08, 'Hera Fairies') + create_sprite(0x00a7, EnemySprite.Faerie, 0x00, 0, 0x06, 0x09, 'Hera Fairies') + create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x0e, 'Eastern West Wing') + create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x1a, 0x0e, 'Eastern West Wing') + create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x12, 'Eastern West Wing') + create_sprite(0x00a8, EnemySprite.Stalfos, 0x00, 0, 0x1a, 0x12, 'Eastern West Wing') + create_sprite(0x00a8, 0x18, SpriteType.Overlord, 0, 0x08, 0x16) + create_sprite(0x00a9, EnemySprite.GreenEyegoreMimic, 0x00, 1, 0x09, 0x05, 'Eastern Courtyard') + create_sprite(0x00a9, EnemySprite.GreenEyegoreMimic, 0x00, 1, 0x16, 0x05, 'Eastern Courtyard') + create_sprite(0x00a9, 0x05, SpriteType.Overlord, 1, 0x0d, 0x0c) + create_sprite(0x00a9, 0x05, SpriteType.Overlord, 1, 0x12, 0x0c) + create_sprite(0x00a9, 0x05, SpriteType.Overlord, 1, 0x0d, 0x12) + create_sprite(0x00a9, 0x05, SpriteType.Overlord, 1, 0x12, 0x12) + create_sprite(0x00a9, EnemySprite.Stalfos, 0x00, 1, 0x0a, 0x10, 'Eastern Courtyard') + create_sprite(0x00a9, EnemySprite.Stalfos, 0x00, 1, 0x14, 0x10, 'Eastern Courtyard') + create_sprite(0x00aa, EnemySprite.AntiFairy, 0x00, 0, 0x18, 0x06, 'Eastern Pot Switch') + create_sprite(0x00aa, EnemySprite.Popo2, 0x00, 0, 0x0a, 0x07, 'Eastern East Wing') + create_sprite(0x00aa, EnemySprite.Stalfos, 0x00, 0, 0x06, 0x0b, 'Eastern East Wing') + create_sprite(0x00aa, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x0c, 'Eastern East Wing') + create_sprite(0x00aa, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x13, 'Eastern East Wing') + create_sprite(0x00aa, EnemySprite.Popo2, 0x00, 0, 0x0a, 0x14, 'Eastern East Wing') + create_sprite(0x00ab, EnemySprite.CrystalSwitch, 0x00, 0, 0x04, 0x18) + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x15, 'Thieves Spike Switch') + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x16, 'Thieves Spike Switch') + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x17, 'Thieves Spike Switch') + create_sprite(0x00ab, EnemySprite.Blob, 0x00, 0, 0x06, 0x18, 'Thieves Spike Switch') + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x19, 'Thieves Spike Switch') + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x0c, 0x1a, 'Thieves Spike Switch') + create_sprite(0x00ab, EnemySprite.SpikeBlock, 0x00, 0, 0x03, 0x1b, 'Thieves Spike Switch') + create_sprite(0x00ac, EnemySprite.Blind, 0x00, 0, 0x09, 0x05) + create_sprite(0x00ae, EnemySprite.BlueBari, 0x00, 0, 0x13, 0x07, 'Iced T') + create_sprite(0x00ae, EnemySprite.BlueBari, 0x00, 0, 0x15, 0x07, 'Iced T') + create_sprite(0x00af, EnemySprite.FirebarCW, 0x00, 0, 0x0a, 0x08, 'Ice Catwalk') + create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x07, 0x07, 'Tower Red Guards') + create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x17, 0x07, 'Tower Red Spears') + create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x18, 0x07, 'Tower Red Spears') + create_sprite(0x00b0, EnemySprite.RedJavelinGuard, 0x00, 0, 0x14, 0x08, 'Tower Red Spears') + create_sprite(0x00b0, EnemySprite.RedJavelinGuard, 0x00, 0, 0x1b, 0x08, 'Tower Red Spears') + create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x05, 0x0b, 'Tower Red Guards') + create_sprite(0x00b0, EnemySprite.BallNChain, 0x00, 0, 0x16, 0x14, 'Tower Pacifist Run') + create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x04, 0x16, 'Tower Circle of Pots') + create_sprite(0x00b0, EnemySprite.Keese, 0x00, 0, 0x0b, 0x16, 'Tower Circle of Pots') + create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x0a, 0x16, 'Tower Circle of Pots') + create_sprite(0x00b0, EnemySprite.RedSpearGuard, 0x00, 0, 0x08, 0x18, 'Tower Circle of Pots', True, 0xe4) + create_sprite(0x00b0, EnemySprite.BluesainBolt, 0x00, 0, 0x1b, 0x1a, 'Tower Pacifist Run') + create_sprite(0x00b0, EnemySprite.RedJavelinGuard, 0x00, 0, 0x17, 0x1c, 'Tower Pacifist Run') + create_sprite(0x00b1, EnemySprite.Medusa, 0x00, 0, 0x15, 0x07, 'Mire Spike Barrier') + create_sprite(0x00b1, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x07, 'Mire Spike Barrier') + create_sprite(0x00b1, EnemySprite.SpikeBlock, 0x00, 0, 0x16, 0x0e, 'Mire Spike Barrier') + create_sprite(0x00b1, EnemySprite.SpikeBlock, 0x00, 0, 0x19, 0x11, 'Mire Spike Barrier') + create_sprite(0x00b1, EnemySprite.Wizzrobe, 0x00, 0, 0x0c, 0x17, 'Mire Square Rail') + create_sprite(0x00b1, EnemySprite.BigSpike, 0x00, 0, 0x1a, 0x17, 'Mire Spike Barrier') + create_sprite(0x00b1, EnemySprite.FourWayShooter, 0x00, 0, 0x07, 0x18, 'Mire Square Rail') + create_sprite(0x00b1, EnemySprite.Wizzrobe, 0x00, 0, 0x03, 0x1a, 'Mire Square Rail') + create_sprite(0x00b1, EnemySprite.AntiFairy, 0x00, 0, 0x15, 0x1a, 'Mire Spike Barrier') + create_sprite(0x00b1, EnemySprite.Wizzrobe, 0x00, 0, 0x08, 0x1c, 'Mire Square Rail') + create_sprite(0x00b2, EnemySprite.Wizzrobe, 0x00, 1, 0x14, 0x08, 'Mire BK Door Room') + create_sprite(0x00b2, EnemySprite.BunnyBeam, 0x00, 1, 0x0c, 0x0a, 'Mire BK Door Room') # , fix=True) + create_sprite(0x00b2, EnemySprite.AntiFairy, 0x00, 1, 0x12, 0x0a, 'Mire BK Door Room') + create_sprite(0x00b2, EnemySprite.BunnyBeam, 0x00, 1, 0x13, 0x0a, 'Mire BK Door Room') # , fix=True) + create_sprite(0x00b2, EnemySprite.AntiFairy, 0x00, 1, 0x07, 0x0b, 'Mire BK Door Room') + create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x04, 0x15, 'Mire Cross') + create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x0b, 0x15, 'Mire Cross') + create_sprite(0x00b2, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x16, 'Mire Cross') + create_sprite(0x00b2, EnemySprite.Medusa, 0x00, 0, 0x15, 0x18, 'Mire Hidden Shooters') + create_sprite(0x00b2, EnemySprite.Medusa, 0x00, 0, 0x1a, 0x18, 'Mire Hidden Shooters') + create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x04, 0x1b, 'Mire Cross') + create_sprite(0x00b2, EnemySprite.Sluggula, 0x00, 0, 0x0b, 0x1b, 'Mire Cross') + create_sprite(0x00b2, EnemySprite.Popo, 0x00, 0, 0x14, 0x1b, 'Mire Hidden Shooters') + create_sprite(0x00b2, EnemySprite.Popo, 0x00, 0, 0x1b, 0x1b, 'Mire Hidden Shooters') + create_sprite(0x00b3, EnemySprite.Stalfos, 0x00, 0, 0x03, 0x15, 'Mire Spikes') + create_sprite(0x00b3, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x15, 'Mire Spikes') + create_sprite(0x00b3, EnemySprite.Beamos, 0x00, 0, 0x06, 0x18, 'Mire Spikes') + create_sprite(0x00b3, EnemySprite.FourWayShooter, 0x00, 0, 0x0a, 0x1a, 'Mire Spikes') + create_sprite(0x00b3, EnemySprite.Stalfos, 0x00, 0, 0x07, 0x1c, 'Mire Spikes') + create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x16, 0x0a, 'TR Dark Ride') + create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x09, 0x0f, 'TR Dark Ride') + create_sprite(0x00b5, EnemySprite.FirebarCW, 0x00, 0, 0x16, 0x16, 'TR Dark Ride') + create_sprite(0x00b6, EnemySprite.Chainchomp, 0x00, 0, 0x06, 0x07, 'TR Chain Chomps Top') + create_sprite(0x00b6, EnemySprite.Chainchomp, 0x00, 0, 0x0a, 0x07, 'TR Chain Chomps Top') + create_sprite(0x00b6, EnemySprite.CrystalSwitch, 0x00, 0, 0x03, 0x04) + create_sprite(0x00b6, EnemySprite.CrystalSwitch, 0x00, 0, 0x0c, 0x04) + create_sprite(0x00b6, EnemySprite.Faerie, 0x00, 0, 0x17, 0x07, 'TR Refill') + create_sprite(0x00b6, EnemySprite.Pokey, 0x00, 0, 0x07, 0x15, 'TR Pokey 1', True, 0xe4) + create_sprite(0x00b6, 0x14, SpriteType.Overlord, 0, 0x17, 0x18) + create_sprite(0x00b6, EnemySprite.Blob, 0x00, 0, 0x07, 0x1b, 'TR Pokey 1') + create_sprite(0x00b6, EnemySprite.Blob, 0x00, 0, 0x08, 0x1b, 'TR Pokey 1') + create_sprite(0x00b7, EnemySprite.RollerHorizontalLeft, 0x00, 0, 0x04, 0x09, 'TR Roller Room') + create_sprite(0x00b7, EnemySprite.RollerVerticalUp, 0x00, 0, 0x04, 0x11, 'TR Roller Room') + create_sprite(0x00b8, EnemySprite.Popo, 0x00, 0, 0x15, 0x0b, 'Eastern Big Key') + create_sprite(0x00b8, EnemySprite.Popo, 0x00, 0, 0x1b, 0x0b, 'Eastern Big Key') + create_sprite(0x00b8, EnemySprite.AntiFairyCircle, 0x00, 0, 0x18, 0x0d, 'Eastern Big Key') + create_sprite(0x00b8, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x13, 'Eastern Big Key') + create_sprite(0x00b8, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x16, 'Eastern Big Key') + create_sprite(0x00b8, EnemySprite.Stalfos, 0x00, 0, 0x1c, 0x16, 'Eastern Big Key') + create_sprite(0x00b9, 0x03, SpriteType.Overlord, 1, 0x11, 0x05) + create_sprite(0x00ba, EnemySprite.Stalfos, 0x00, 0, 0x14, 0x04, 'Eastern Dark Pots') + create_sprite(0x00ba, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x06, 'Eastern Dark Square') + create_sprite(0x00ba, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x06, 'Eastern Dark Pots') + create_sprite(0x00ba, EnemySprite.AntiFairy, 0x00, 0, 0x03, 0x09, 'Eastern Dark Square') + create_sprite(0x00ba, EnemySprite.Popo2, 0x00, 0, 0x0c, 0x09, 'Eastern Dark Square') + create_sprite(0x00ba, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x0a, 'Eastern Dark Pots') + create_sprite(0x00ba, EnemySprite.Popo2, 0x00, 0, 0x08, 0x0c, 'Eastern Dark Square') + create_sprite(0x00bb, EnemySprite.RedZazak, 0x00, 0, 0x1b, 0x04, 'Thieves Triple Bypass') + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x06, 0x0a, 'Thieves Hellway') + create_sprite(0x00bb, EnemySprite.RedZazak, 0x00, 0, 0x16, 0x0a, 'Thieves Triple Bypass') + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x19, 0x0a, 'Thieves Triple Bypass') + create_sprite(0x00bb, EnemySprite.AntiFairy, 0x00, 0, 0x08, 0x0c, 'Thieves Hellway') + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x09, 0x0e, 'Thieves Hellway') + create_sprite(0x00bb, EnemySprite.Firesnake, 0x00, 0, 0x07, 0x10, 'Thieves Hellway') + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x08, 0x14, 'Thieves Hellway') + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x19, 0x15, 'Thieves Spike Track') + create_sprite(0x00bb, EnemySprite.AntiFairy, 0x00, 0, 0x15, 0x16, 'Thieves Spike Track') + create_sprite(0x00bb, EnemySprite.Gibo, 0x00, 0, 0x17, 0x1a, 'Thieves Spike Track') + create_sprite(0x00bc, EnemySprite.BlueZazak, 0x00, 0, 0x06, 0x05, 'Thieves Conveyor Maze') + create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x05, 'Thieves Conveyor Maze') + create_sprite(0x00bc, EnemySprite.SpikeBlock, 0x00, 0, 0x08, 0x06, 'Thieves Conveyor Maze') + create_sprite(0x00bc, EnemySprite.RedZazak, 0x00, 0, 0x0a, 0x09, 'Thieves Conveyor Maze') + create_sprite(0x00bc, EnemySprite.SpikeBlock, 0x00, 0, 0x09, 0x0a, 'Thieves Conveyor Maze') + create_sprite(0x00bc, EnemySprite.BlueZazak, 0x00, 0, 0x05, 0x0b, 'Thieves Conveyor Maze') + create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x17, 0x0a, 'Thieves Hallway') + create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x11, 'Thieves Hallway') + create_sprite(0x00bc, EnemySprite.Stalfos, 0x00, 0, 0x16, 0x16, 'Thieves Hallway') + create_sprite(0x00bc, EnemySprite.BlueZazak, 0x00, 0, 0x08, 0x17, 'Thieves Pot Alcove Mid') + create_sprite(0x00bc, EnemySprite.Firesnake, 0x00, 0, 0x07, 0x18, 'Thieves Pot Alcove Mid') + create_sprite(0x00bc, EnemySprite.RedZazak, 0x00, 0, 0x08, 0x19, 'Thieves Pot Alcove Mid') + create_sprite(0x00be, EnemySprite.AntiFairy, 0x00, 0, 0x17, 0x08, 'Ice Anti-Fairy') + create_sprite(0x00be, EnemySprite.Freezor, 0x00, 0, 0x14, 0x12, 'Ice Switch Room') + create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x15, 'Ice Switch Room') + create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x15, 'Ice Switch Room') + create_sprite(0x00be, EnemySprite.StalfosKnight, 0x00, 0, 0x18, 0x16, 'Ice Switch Room') + create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x14, 0x1a, 'Ice Switch Room') + create_sprite(0x00be, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x1a, 'Ice Switch Room') + create_sprite(0x00bf, EnemySprite.CrystalSwitch, 0x00, 0, 0x0b, 0x18) + create_sprite(0x00bf, EnemySprite.BunnyBeam, 0x00, 0, 0x0c, 0x15, 'Ice Refill') + create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x17, 0x05, 'Tower Dark Archers') + create_sprite(0x00c0, EnemySprite.BlueArcher, 0x00, 0, 0x1a, 0x07, 'Tower Dark Archers') + create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x0b, 0x09, 'Tower Dark Pits') + create_sprite(0x00c0, EnemySprite.BlueArcher, 0x00, 0, 0x14, 0x0b, 'Tower Dark Archers', True, 0xe4) + create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x06, 0x0e, 'Tower Dark Pits') + create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x04, 0x18, 'Tower Dark Pits') + create_sprite(0x00c0, EnemySprite.BlueArcher, 0x00, 0, 0x14, 0x1b, 'Tower Dual Statues') + create_sprite(0x00c0, EnemySprite.BlueGuard, 0x00, 0, 0x1b, 0x1b, 'Tower Dual Statues') + create_sprite(0x00c1, EnemySprite.CrystalSwitch, 0x00, 0, 0x15, 0x17) + create_sprite(0x00c1, EnemySprite.Medusa, 0x00, 0, 0x14, 0x05) + create_sprite(0x00c1, EnemySprite.Medusa, 0x00, 0, 0x1b, 0x05) + create_sprite(0x00c1, EnemySprite.Stalfos, 0x00, 0, 0x06, 0x0b, 'Mire Compass Room') + create_sprite(0x00c1, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x0b, 'Mire Wizzrobe Bypass') + create_sprite(0x00c1, EnemySprite.FloatingSkull, 0x00, 0, 0x17, 0x15, 'Mire Conveyor Crystal') + create_sprite(0x00c1, EnemySprite.Medusa, 0x00, 0, 0x09, 0x16) + create_sprite(0x00c1, 0x14, SpriteType.Overlord, 0, 0x07, 0x18) + create_sprite(0x00c1, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x19, 'Mire Conveyor Crystal') + create_sprite(0x00c1, EnemySprite.FourWayShooter, 0x00, 0, 0x18, 0x1a, 'Mire Conveyor Crystal') + create_sprite(0x00c1, EnemySprite.BlueBari, 0x00, 0, 0x13, 0x1b, 'Mire Conveyor Crystal', True, 0xe4) + create_sprite(0x00c1, EnemySprite.FloatingSkull, 0x00, 0, 0x1b, 0x1b, 'Mire Conveyor Crystal') + create_sprite(0x00c2, EnemySprite.Firesnake, 0x00, 1, 0x15, 0x0b, 'Mire Hub') + create_sprite(0x00c2, EnemySprite.Firesnake, 0x00, 0, 0x0b, 0x0c, 'Mire Hub') + create_sprite(0x00c2, EnemySprite.Medusa, 0x00, 0, 0x08, 0x10, 'Mire Hub') + create_sprite(0x00c2, EnemySprite.SparkCW, 0x00, 1, 0x10, 0x12, 'Mire Hub') + create_sprite(0x00c2, EnemySprite.SparkCW, 0x00, 1, 0x19, 0x12, 'Mire Hub') + create_sprite(0x00c2, EnemySprite.BunnyBeam, 0x00, 1, 0x10, 0x14, 'Mire Hub') # , fix=True) + create_sprite(0x00c2, EnemySprite.Firesnake, 0x00, 1, 0x08, 0x16, 'Mire Hub') + create_sprite(0x00c2, EnemySprite.SparkCW, 0x00, 1, 0x16, 0x16, 'Mire Hub') + create_sprite(0x00c3, EnemySprite.Medusa, 0x00, 0, 0x05, 0x06) + create_sprite(0x00c3, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x09, embed=True) + create_sprite(0x00c3, EnemySprite.LaserEyeLeft, 0x00, 0, 0x11, 0x0d, embed=True) + create_sprite(0x00c3, EnemySprite.LaserEyeRight, 0x00, 0, 0x1e, 0x11, embed=True) + create_sprite(0x00c3, EnemySprite.LaserEyeLeft, 0x00, 0, 0x11, 0x15, embed=True) + create_sprite(0x00c3, 0x0b, SpriteType.Overlord, 0, 0x17, 0x1a) + create_sprite(0x00c3, EnemySprite.AntiFairy, 0x00, 0, 0x0a, 0x1b, 'Mire Lone Shooter') + create_sprite(0x00c3, EnemySprite.Medusa, 0x00, 0, 0x07, 0x1c, 'Mire Lone Shooter') + create_sprite(0x00c4, EnemySprite.CrystalSwitch, 0x00, 0, 0x0b, 0x0a) + create_sprite(0x00c4, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x0f) + create_sprite(0x00c4, EnemySprite.CrystalSwitch, 0x00, 0, 0x1c, 0x1b) + create_sprite(0x00c4, EnemySprite.CrystalSwitch, 0x00, 0, 0x0f, 0x15) + create_sprite(0x00c4, EnemySprite.Pokey, 0x00, 0, 0x0f, 0x0e, 'TR Crystal Maze Interior') + create_sprite(0x00c4, EnemySprite.AntiFairy, 0x00, 0, 0x0b, 0x0f, 'TR Crystal Maze Interior') + create_sprite(0x00c4, EnemySprite.MiniHelmasaur, 0x00, 0, 0x07, 0x14, 'TR Crystal Maze Interior') + create_sprite(0x00c4, EnemySprite.MiniHelmasaur, 0x00, 0, 0x18, 0x14, 'TR Crystal Maze Interior') + create_sprite(0x00c4, EnemySprite.AntiFairy, 0x00, 0, 0x0b, 0x1a, 'TR Crystal Maze Interior') + create_sprite(0x00c4, EnemySprite.AntiFairy, 0x00, 0, 0x14, 0x1a, 'TR Crystal Maze Interior') + create_sprite(0x00c5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x09, embed=True) + create_sprite(0x00c5, EnemySprite.LaserEyeLeft, 0x00, 0, 0x01, 0x0b, embed=True) + create_sprite(0x00c5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x0d, embed=True) + create_sprite(0x00c5, EnemySprite.LaserEyeLeft, 0x00, 0, 0x01, 0x0f, embed=True) + create_sprite(0x00c5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x11, embed=True) + create_sprite(0x00c5, EnemySprite.LaserEyeLeft, 0x00, 0, 0x01, 0x13, embed=True) + create_sprite(0x00c5, EnemySprite.MiniHelmasaur, 0x00, 0, 0x07, 0x15, 'TR Dash Bridge') + create_sprite(0x00c5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x15, embed=True) + create_sprite(0x00c6, EnemySprite.Stalfos, 0x00, 0, 0x0b, 0x04, 'TR Hub Ledges') + create_sprite(0x00c6, EnemySprite.Stalfos, 0x00, 0, 0x15, 0x04, 'TR Hub Ledges') + create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x09, 'TR Hub Ledges') + create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x17, 0x09, 'TR Hub Ledges') + create_sprite(0x00c6, EnemySprite.FloatingSkull, 0x00, 0, 0x10, 0x0e, 'TR Hub Ledges') + create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x14, 'TR Hub Ledges') + create_sprite(0x00c6, EnemySprite.BlueBari, 0x00, 0, 0x08, 0x17, 'TR Hub Ledges') + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x05) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x05) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x0a, 0x05) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x0a, 0x08) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x08) + create_sprite(0x00c8, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x08) + create_sprite(0x00c8, 0x19, SpriteType.Overlord, 0, 0x07, 0x08) + create_sprite(0x00c9, EnemySprite.Popo2, 0x00, 0, 0x10, 0x05, 'Eastern Lobby Bridge') + create_sprite(0x00c9, EnemySprite.Popo2, 0x00, 0, 0x0f, 0x06, 'Eastern Lobby Bridge') + create_sprite(0x00c9, EnemySprite.Popo2, 0x00, 0, 0x10, 0x07, 'Eastern Lobby Bridge') + create_sprite(0x00cb, EnemySprite.BunnyBeam, 0x00, 0, 0x14, 0x04, 'Thieves Ambush') # , fix=True) + create_sprite(0x00cb, EnemySprite.Firesnake, 0x00, 1, 0x08, 0x09, 'Thieves Ambush') + create_sprite(0x00cb, EnemySprite.BlueZazak, 0x00, 1, 0x10, 0x0a, 'Thieves Ambush') + create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x13, 0x0a, 'Thieves Ambush') + create_sprite(0x00cb, EnemySprite.SparkCW, 0x00, 1, 0x16, 0x0a, 'Thieves Ambush') + create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x1c, 0x0a, 'Thieves Ambush') + create_sprite(0x00cb, EnemySprite.Stalfos, 0x00, 0, 0x0c, 0x10, 'Thieves Ambush') + create_sprite(0x00cb, EnemySprite.RedZazak, 0x00, 1, 0x18, 0x15, 'Thieves Ambush') + create_sprite(0x00cb, EnemySprite.RedZazak, 0x00, 1, 0x08, 0x17, 'Thieves Ambush') + create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x0b, 0x17, 'Thieves Ambush') + create_sprite(0x00cb, EnemySprite.Blob, 0x00, 0, 0x0c, 0x18, 'Thieves Ambush') + create_sprite(0x00cb, EnemySprite.BunnyBeam, 0x00, 0, 0x14, 0x1c, 'Thieves Ambush') # , fix=True) + create_sprite(0x00cc, EnemySprite.Firesnake, 0x00, 0, 0x13, 0x04, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.BunnyBeam, 0x00, 1, 0x0b, 0x09, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.BlueZazak, 0x00, 1, 0x08, 0x0a, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.SparkCW, 0x00, 1, 0x0e, 0x0a, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.Blob, 0x00, 0, 0x0c, 0x0b, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.RedZazak, 0x00, 1, 0x10, 0x0c, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.BlueZazak, 0x00, 1, 0x18, 0x0c, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.Firesnake, 0x00, 1, 0x0e, 0x14, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.Blob, 0x00, 0, 0x1c, 0x15, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.SparkCW, 0x00, 1, 0x06, 0x16, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.SparkCW, 0x00, 1, 0x09, 0x16, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.RedZazak, 0x00, 1, 0x09, 0x18, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.Blob, 0x00, 0, 0x1c, 0x16, 'Thieves BK Corner') + create_sprite(0x00cc, EnemySprite.BunnyBeam, 0x00, 1, 0x07, 0x1c, 'Thieves BK Corner') + create_sprite(0x00ce, EnemySprite.RedBari, 0x00, 0, 0x16, 0x05, 'Ice Antechamber') + create_sprite(0x00ce, EnemySprite.RedBari, 0x00, 0, 0x19, 0x05, 'Ice Antechamber') + create_sprite(0x00ce, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x1c, 0x05, 'Ice Antechamber') + create_sprite(0x00ce, EnemySprite.Statue, 0x00, 0, 0x14, 0x09, 'Ice Antechamber') + create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x08, 'Ice Antechamber') + create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1c, 0x08, 'Ice Antechamber') + create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1b, 0x09, 'Ice Antechamber') + create_sprite(0x00ce, EnemySprite.BlueBari, 0x00, 0, 0x1c, 0x09, 'Ice Antechamber') + create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x0b, 0x05, 'Tower Dark Maze') + create_sprite(0x00d0, EnemySprite.BlueGuard, 0x00, 0, 0x09, 0x07, 'Tower Dark Maze') + create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x17, 0x07, 'Tower Lone Statue') + create_sprite(0x00d0, EnemySprite.BluesainBolt, 0x00, 0, 0x15, 0x0b, 'Tower Lone Statue') + create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x09, 0x0c, 'Tower Dark Maze') + create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x08, 0x0f, 'Tower Dark Maze') + create_sprite(0x00d0, EnemySprite.BlueGuard, 0x03, 0, 0x03, 0x10, 'Tower Dark Maze') + create_sprite(0x00d0, EnemySprite.BlueGuard, 0x00, 0, 0x09, 0x14, 'Tower Dark Maze') + create_sprite(0x00d0, EnemySprite.BluesainBolt, 0x00, 0, 0x1b, 0x16, 'Tower Dark Chargers') + create_sprite(0x00d0, EnemySprite.Keese, 0x00, 0, 0x06, 0x19, 'Tower Dark Maze') + create_sprite(0x00d0, EnemySprite.BluesainBolt, 0x00, 0, 0x1a, 0x19, 'Tower Dark Chargers') + create_sprite(0x00d1, EnemySprite.Beamos, 0x00, 0, 0x14, 0x06, 'Mire Neglected Room') + create_sprite(0x00d1, EnemySprite.Beamos, 0x00, 0, 0x1b, 0x06, 'Mire Neglected Room') + create_sprite(0x00d1, EnemySprite.Wizzrobe, 0x00, 0, 0x04, 0x07, 'Mire Conveyor Barrier') + create_sprite(0x00d1, EnemySprite.RedBari, 0x00, 0, 0x0c, 0x08, 'Mire Conveyor Barrier') + create_sprite(0x00d1, EnemySprite.FourWayShooter, 0x00, 0, 0x05, 0x09, 'Mire Conveyor Barrier') + create_sprite(0x00d1, EnemySprite.Sluggula, 0x00, 0, 0x04, 0x0b, 'Mire Conveyor Barrier') + create_sprite(0x00d1, EnemySprite.Sluggula, 0x00, 0, 0x0b, 0x0b, 'Mire Conveyor Barrier') + create_sprite(0x00d1, EnemySprite.Sluggula, 0x00, 0, 0x1b, 0x0b, 'Mire Neglected Room') + create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x18, 0x06, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x1a, 0x07, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x13, 0x08, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x1c, 0x08, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Beamos, 0x00, 0, 0x18, 0x0a, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x16, 0x0c, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x13, 0x0d, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Wizzrobe, 0x00, 0, 0x13, 0x10, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x14, 0x14, 'Mire 2') + create_sprite(0x00d2, EnemySprite.Popo, 0x00, 0, 0x1c, 0x14, 'Mire 2') + create_sprite(0x00d5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x09, embed=True) + create_sprite(0x00d5, EnemySprite.LaserEyeLeft, 0x00, 0, 0x01, 0x0d, embed=True) + create_sprite(0x00d5, EnemySprite.LaserEyeRight, 0x00, 0, 0x0e, 0x11, embed=True) + create_sprite(0x00d5, EnemySprite.LaserEyeLeft, 0x00, 0, 0x01, 0x15, embed=True) + create_sprite(0x00d5, EnemySprite.HardhatBeetle, 0x00, 0, 0x04, 0x15, 'TR Eye Bridge') + create_sprite(0x00d6, EnemySprite.LaserEyeTop, 0x00, 0, 0x07, 0x02) + create_sprite(0x00d6, EnemySprite.Medusa, 0x00, 0, 0x03, 0x16) + create_sprite(0x00d6, EnemySprite.Medusa, 0x00, 0, 0x0c, 0x16) + create_sprite(0x00d8, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x17, 0x05, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x18, 0x05, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x17, 0x09, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x18, 0x09, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x16, 0x0a, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.Popo2, 0x00, 0, 0x19, 0x0a, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.Popo, 0x00, 0, 0x16, 0x0b, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.Popo, 0x00, 0, 0x19, 0x0b, 'Eastern Duo Eyegores') + create_sprite(0x00d8, EnemySprite.RedEyegoreMimic, 0x00, 0, 0x17, 0x14, 'Eastern Single Eyegore') + create_sprite(0x00d8, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x16, 'Eastern Single Eyegore') + create_sprite(0x00d8, EnemySprite.Stalfos, 0x00, 0, 0x18, 0x1b, 'Eastern Single Eyegore') + create_sprite(0x00d9, 0x02, SpriteType.Overlord, 0, 0x0c, 0x14) + create_sprite(0x00d9, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x15, 'Eastern False Switches') + create_sprite(0x00d9, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x18, 'Eastern False Switches') + create_sprite(0x00d9, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x18, 0x1b, 'Eastern False Switches') + create_sprite(0x00da, EnemySprite.AntiFairy, 0x00, 0, 0x07, 0x18, 'Eastern Attic Start') + create_sprite(0x00da, EnemySprite.AntiFairy, 0x00, 0, 0x08, 0x18, 'Eastern Attic Start') + create_sprite(0x00db, EnemySprite.BunnyBeam, 0x00, 0, 0x03, 0x04, 'Thieves Lobby') # , fix=True) + create_sprite(0x00db, EnemySprite.SparkCW, 0x00, 1, 0x0e, 0x0a, 'Thieves Lobby') + create_sprite(0x00db, EnemySprite.RedZazak, 0x00, 1, 0x17, 0x0b, 'Thieves Lobby') + create_sprite(0x00db, EnemySprite.BlueZazak, 0x00, 1, 0x0f, 0x0c, 'Thieves Lobby') + create_sprite(0x00db, EnemySprite.Blob, 0x00, 0, 0x0b, 0x10, 'Thieves Lobby') + create_sprite(0x00db, EnemySprite.Firesnake, 0x00, 0, 0x14, 0x10, 'Thieves Lobby') + create_sprite(0x00db, EnemySprite.BlueZazak, 0x00, 1, 0x0f, 0x15, 'Thieves Lobby') + create_sprite(0x00dc, EnemySprite.BlueZazak, 0x00, 1, 0x09, 0x0a, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.SparkCCW, 0x00, 1, 0x0e, 0x0a, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.RedZazak, 0x00, 1, 0x0f, 0x0c, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.Blob, 0x00, 0, 0x0b, 0x10, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.Blob, 0x00, 0, 0x16, 0x10, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.BunnyBeam, 0x00, 1, 0x0c, 0x16, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.RedZazak, 0x00, 1, 0x0f, 0x16, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.BlueZazak, 0x00, 1, 0x09, 0x17, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.Firesnake, 0x00, 1, 0x16, 0x17, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.Firesnake, 0x00, 0, 0x05, 0x1c, 'Thieves Compass Room') + create_sprite(0x00dc, EnemySprite.Blob, 0x00, 0, 0x0f, 0x1c, 'Thieves Compass Room') + create_sprite(0x00de, EnemySprite.KholdstareShell, 0x00, 0, 0x07, 0x05) + create_sprite(0x00de, EnemySprite.FallingIce, 0x00, 0, 0x07, 0x05) + create_sprite(0x00de, EnemySprite.Kholdstare, 0x00, 0, 0x07, 0x05) + create_sprite(0x00df, EnemySprite.MiniMoldorm, 0x00, 1, 0x0c, 0x15, 'Paradox Cave') + create_sprite(0x00df, EnemySprite.MiniMoldorm, 0x00, 1, 0x0c, 0x16, 'Paradox Cave') + create_sprite(0x00e0, EnemySprite.BallNChain, 0x00, 0, 0x04, 0x06, 'Tower Gold Knights') + create_sprite(0x00e0, EnemySprite.BallNChain, 0x00, 0, 0x0b, 0x06, 'Tower Gold Knights') + create_sprite(0x00e0, EnemySprite.BluesainBolt, 0x00, 0, 0x1a, 0x06, 'Tower Room 03') + create_sprite(0x00e0, EnemySprite.BluesainBolt, 0x00, 0, 0x1a, 0x09, 'Tower Room 03') + create_sprite(0x00e1, EnemySprite.HeartPiece, 0x00, 0, 0x17, 0x0d) + create_sprite(0x00e1, EnemySprite.AdultNpc, 0x00, 1, 0x07, 0x12) + create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x07, 0x06, 'Lumberjack Tree (top)') + create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x08, 0x06, 'Lumberjack Tree (top)') + create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x07, 0x07, 'Lumberjack Tree (top)') + create_sprite(0x00e2, EnemySprite.Faerie, 0x00, 0, 0x08, 0x07, 'Lumberjack Tree (top)') + create_sprite(0x00e2, EnemySprite.HeartPiece, 0x00, 0, 0x13, 0x10) + create_sprite(0x00e3, EnemySprite.MagicBat, 0x00, 1, 0x17, 0x05) + create_sprite(0x00e4, EnemySprite.Keese, 0x00, 0, 0x19, 0x07, 'Old Man House', embed=True) # partial + create_sprite(0x00e4, EnemySprite.Keese, 0x00, 0, 0x18, 0x08, 'Old Man House') + create_sprite(0x00e4, EnemySprite.Keese, 0x00, 0, 0x17, 0x09, 'Old Man House', embed=True) # partial + create_sprite(0x00e4, EnemySprite.OldMan, 0x00, 0, 0x06, 0x16, 'Old Man House') + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x0f, 0x09, 'Old Man House Back') + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x10, 0x09, 'Old Man House Back') + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x11, 0x09, 'Old Man House Back') + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x1b, 0x0e, 'Old Man House Back', embed=True) + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x0f, 0x12, 'Old Man House Back', embed=True) # partial + create_sprite(0x00e5, EnemySprite.Keese, 0x00, 0, 0x11, 0x12, 'Old Man House Back', embed=True) # partial + create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x1b, 0x0b, 'Death Mountain Return Cave (left)', embed=True) # partial + create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x17, 0x0f, 'Death Mountain Return Cave (left)', embed=True) # partial + create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x13, 0x13, 'Death Mountain Return Cave (left)', embed=True) # partial + create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x0f, 0x17, 'Death Mountain Return Cave (left)', embed=True) # partial + create_sprite(0x00e6, EnemySprite.Keese, 0x00, 0, 0x0b, 0x1b, 'Death Mountain Return Cave (left)', embed=True) # partial + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x10, 0x04, 'Death Mountain Return Cave (right)', embed=True) # partial + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x13, 0x04, 'Death Mountain Return Cave (right)', embed=True) # partial + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x15, 0x0b, 'Death Mountain Return Cave (right)', embed=True) + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x0b, 0x0c, 'Death Mountain Return Cave (right)') + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x0b, 0x0d, 'Death Mountain Return Cave (right)') + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x15, 0x0d, 'Death Mountain Return Cave (right)', embed=True) + create_sprite(0x00e7, EnemySprite.Keese, 0x00, 0, 0x15, 0x0f, 'Death Mountain Return Cave (right)', embed=True) + create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x07, 0x05, 'Superbunny Cave (Bottom)') + create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x17, 0x08, 'Superbunny Cave (Bottom)') + create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x07, 0x0c, 'Superbunny Cave (Bottom)') + create_sprite(0x00e8, EnemySprite.HardhatBeetle, 0x00, 0, 0x19, 0x0c, 'Superbunny Cave (Bottom)') + create_sprite(0x00ea, EnemySprite.HeartPiece, 0x00, 0, 0x0b, 0x0b) + create_sprite(0x00eb, EnemySprite.Bumper, 0x00, 0, 0x17, 0x14, fix=True) # bumper cave sans logic? + create_sprite(0x00ee, EnemySprite.MiniMoldorm, 0x00, 0, 0x10, 0x04, 'Spiral Cave (Top)') + create_sprite(0x00ee, EnemySprite.MiniMoldorm, 0x00, 0, 0x0b, 0x0e, 'Spiral Cave (Top)') + create_sprite(0x00ee, EnemySprite.MiniMoldorm, 0x00, 0, 0x09, 0x1c, 'Spiral Cave (Top)') + create_sprite(0x00ee, EnemySprite.BlueBari, 0x00, 0, 0x03, 0x0b, 'Spiral Cave (Top)') + create_sprite(0x00ee, EnemySprite.BlueBari, 0x00, 0, 0x1c, 0x0c, 'Spiral Cave (Top)') + create_sprite(0x00ef, EnemySprite.MiniMoldorm, 0x00, 0, 0x17, 0x09, 'Paradox Cave Chest Area') + create_sprite(0x00ef, EnemySprite.MiniMoldorm, 0x00, 0, 0x14, 0x0a, 'Paradox Cave Chest Area') + create_sprite(0x00ef, EnemySprite.MiniMoldorm, 0x00, 0, 0x1b, 0x0a, 'Paradox Cave Chest Area') + create_sprite(0x00ef, EnemySprite.CrystalSwitch, 0x00, 0, 0x18, 0x06) + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x09, 0x03, 'Old Man Cave (West)', embed=True) + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x10, 0x03, 'Old Man Cave (West)', embed=True) + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x08, 0x04, 'Old Man Cave (West)', embed=True) # partial + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x0a, 0x04, 'Old Man Cave (West)', embed=True) # partial + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x09, 0x07, 'Old Man Cave (West)', embed=True) # partial + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x03, 0x0a, 'Old Man Cave (West)', embed=True) # partial + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x05, 0x0a, 'Old Man Cave (West)', embed=True) # partial + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x0e, 0x0c, 'Old Man Cave (West)', embed=True) + create_sprite(0x00f0, EnemySprite.OldMan, 0x00, 0, 0x1b, 0x10, 'Old Man Cave (West)') + create_sprite(0x00f0, EnemySprite.Keese, 0x00, 0, 0x13, 0x13, 'Old Man Cave (West)', embed=True) + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x19, 0x10, 'Old Man Cave (East)', embed=True) # partial + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x1c, 0x10, 'Old Man Cave (East)', embed=True) # partial + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x18, 0x11, 'Old Man Cave (East)', embed=True) + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x1d, 0x11, 'Old Man Cave (East)', embed=True) + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x17, 0x12, 'Old Man Cave (East)', embed=True) + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x1e, 0x12, 'Old Man Cave (East)', embed=True) + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x06, 0x1b, 'Old Man Cave (East)', embed=True) # partial + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x09, 0x1b, 'Old Man Cave (East)', embed=True) # partial + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x07, 0x1c, 'Old Man Cave (East)', embed=True) # partial + create_sprite(0x00f1, EnemySprite.Keese, 0x00, 0, 0x08, 0x1c, 'Old Man Cave (East)', embed=True) # partial + create_sprite(0x00f3, EnemySprite.Grandma, 0x00, 0, 0x06, 0x14) + create_sprite(0x00f4, EnemySprite.ArgueBros, 0x00, 0, 0x17, 0x14) + create_sprite(0x00f5, EnemySprite.ArgueBros, 0x00, 0, 0x08, 0x14) + create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x1a, 0x05, 'Spectacle Rock Cave (Bottom)') + create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x15, 0x0f, 'Spectacle Rock Cave (Bottom)') + create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x11, 0x13, 'Spectacle Rock Cave (Bottom)') + create_sprite(0x00f9, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x17, 'Spectacle Rock Cave (Bottom)') + create_sprite(0x00fa, EnemySprite.Faerie, 0x00, 0, 0x17, 0x0e, 'Spectacle Rock Cave Pool') + create_sprite(0x00fa, EnemySprite.Faerie, 0x00, 0, 0x18, 0x10, 'Spectacle Rock Cave Pool') + create_sprite(0x00fa, EnemySprite.Faerie, 0x00, 0, 0x15, 0x11, 'Spectacle Rock Cave Pool') + create_sprite(0x00fb, EnemySprite.Bumper, 0x00, 0, 0x17, 0x0d, 'Bumper Cave (bottom)') + create_sprite(0x00fb, EnemySprite.HardhatBeetle, 0x00, 0, 0x19, 0x0a, 'Bumper Cave (bottom)') + create_sprite(0x00fb, EnemySprite.HardhatBeetle, 0x00, 0, 0x15, 0x12, 'Bumper Cave (bottom)') + create_sprite(0x00fd, EnemySprite.MiniMoldorm, 0x00, 0, 0x09, 0x0e, 'Fairy Ascension Cave (Bottom)') + create_sprite(0x00fd, EnemySprite.BlueBari, 0x00, 0, 0x05, 0x08, 'Fairy Ascension Cave (Bottom)') + create_sprite(0x00fd, EnemySprite.Faerie, 0x00, 0, 0x16, 0x08, 'Fairy Ascension Cave (Top)') + create_sprite(0x00fd, EnemySprite.Faerie, 0x00, 0, 0x18, 0x08, 'Fairy Ascension Cave (Top)') + create_sprite(0x00fd, EnemySprite.BlueBari, 0x00, 0, 0x0f, 0x11, 'Fairy Ascension Cave (Bottom)') + create_sprite(0x00fe, EnemySprite.MiniMoldorm, 0x00, 0, 0x16, 0x12, 'Spiral Cave (Bottom)') + create_sprite(0x00fe, EnemySprite.MiniMoldorm, 0x00, 0, 0x14, 0x16, 'Spiral Cave (Bottom)') + create_sprite(0x00fe, EnemySprite.MiniMoldorm, 0x00, 0, 0x1a, 0x16, 'Spiral Cave (Bottom)') + create_sprite(0x00fe, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x12, 'Spiral Cave (Bottom)') + create_sprite(0x00fe, EnemySprite.BlueBari, 0x00, 0, 0x18, 0x18, 'Spiral Cave (Bottom)') + create_sprite(0x00ff, EnemySprite.Shopkeeper, 0x00, 0, 0x07, 0x04) + create_sprite(0x0100, EnemySprite.Shopkeeper, 0x00, 0, 0x0b, 0x1b) + create_sprite(0x0101, EnemySprite.RupeePull, 0x00, 0, 0x08, 0x13) + create_sprite(0x0102, EnemySprite.SickKid, 0x00, 0, 0x03, 0x18) + create_sprite(0x0103, EnemySprite.Drunkard, 0x00, 0, 0x06, 0x15) + create_sprite(0x0103, EnemySprite.AdultNpc, 0x00, 0, 0x0a, 0x1b) + create_sprite(0x0103, EnemySprite.Innkeeper, 0x00, 0, 0x17, 0x17) + create_sprite(0x0104, EnemySprite.UnclePriest, 0x00, 0, 0x1a, 0x17) + create_sprite(0x0105, EnemySprite.Wiseman, 0x00, 0, 0x07, 0x18) + create_sprite(0x0106, EnemySprite.Shopkeeper, 0x00, 0, 0x08, 0x1b) + create_sprite(0x0107, EnemySprite.BonkItem, 0x00, 0, 0x03, 0x15) + create_sprite(0x0107, EnemySprite.CricketRat, 0x00, 0, 0x17, 0x1b, 'Light World Bomb Hut') + create_sprite(0x0107, EnemySprite.CricketRat, 0x00, 0, 0x18, 0x1b, 'Light World Bomb Hut') + create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x09, 0x16, 'Chicken House') + create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x0c, 0x16, 'Chicken House') + create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x09, 0x19, 'Chicken House') + create_sprite(0x0108, EnemySprite.Cucco, 0x00, 0, 0x06, 0x1a, 'Chicken House') + create_sprite(0x0109, EnemySprite.MagicShopAssistant, 0x00, 0, 0x0a, 0x1b) + create_sprite(0x010a, EnemySprite.Wiseman, 0x00, 0, 0x19, 0x04) + create_sprite(0x010b, EnemySprite.WrongPullSwitch, 0x00, 0, 0x0f, 0x03) + create_sprite(0x010b, 0x1a, SpriteType.Overlord, 0, 0x0d, 0x06) + create_sprite(0x010b, 0x1a, SpriteType.Overlord, 0, 0x10, 0x06) + create_sprite(0x010b, 0x1a, SpriteType.Overlord, 0, 0x12, 0x07) + create_sprite(0x010b, 0x1a, SpriteType.Overlord, 0, 0x0f, 0x09) + create_sprite(0x010b, EnemySprite.CorrectPullSwitch, 0x00, 0, 0x12, 0x03) + create_sprite(0x010b, EnemySprite.AntiFairy, 0x00, 0, 0x0d, 0x07, 'Dam') + create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x17, 0x07, 'Hookshot Fairy') + create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x18, 0x07, 'Hookshot Fairy') + create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x17, 0x08, 'Hookshot Fairy') + create_sprite(0x010c, EnemySprite.Faerie, 0x00, 0, 0x18, 0x08, 'Hookshot Fairy') + create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x07, 0x14, 'Mimic Cave') + create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x08, 0x14, 'Mimic Cave') + create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x14, 'Mimic Cave') + create_sprite(0x010c, EnemySprite.GreenEyegoreMimic, 0x00, 0, 0x0c, 0x1a, 'Mimic Cave') + create_sprite(0x010d, EnemySprite.SparkCW, 0x00, 0, 0x05, 0x16, 'Mire Shed') + create_sprite(0x010d, EnemySprite.SparkCCW, 0x00, 0, 0x0a, 0x16, 'Mire Shed') + create_sprite(0x010e, EnemySprite.DarkWorldHintNpc, 0x00, 0, 0x06, 0x06) + create_sprite(0x010e, EnemySprite.DarkWorldHintNpc, 0x00, 0, 0x18, 0x06) + create_sprite(0x010f, EnemySprite.Shopkeeper, 0x00, 0, 0x07, 0x15) + create_sprite(0x0110, EnemySprite.Shopkeeper, 0x00, 0, 0x07, 0x15) + create_sprite(0x0111, EnemySprite.ArcheryNpc, 0x00, 0, 0x0b, 0x1b) + create_sprite(0x0112, EnemySprite.DarkWorldHintNpc, 0x00, 0, 0x07, 0x0a) + create_sprite(0x0112, EnemySprite.Shopkeeper, 0x00, 0, 0x17, 0x14) + create_sprite(0x0114, EnemySprite.FairyPondTrigger, 0x00, 0, 0x07, 0x18) + create_sprite(0x0114, EnemySprite.DarkWorldHintNpc, 0x00, 0, 0x19, 0x14) + create_sprite(0x0115, EnemySprite.BigFairy, 0x00, 0, 0x17, 0x16) + create_sprite(0x0115, EnemySprite.Faerie, 0x00, 0, 0x17, 0x07, 'Capacity Fairy Pool') + create_sprite(0x0115, EnemySprite.Faerie, 0x00, 0, 0x18, 0x07, 'Capacity Fairy Pool') + create_sprite(0x0115, EnemySprite.Faerie, 0x00, 0, 0x17, 0x08, 'Capacity Fairy Pool') + create_sprite(0x0115, EnemySprite.Faerie, 0x00, 0, 0x18, 0x08, 'Capacity Fairy Pool') + create_sprite(0x0115, EnemySprite.FairyPondTrigger, 0x00, 0, 0x07, 0x09) + create_sprite(0x0116, EnemySprite.FairyPondTrigger, 0x00, 0, 0x17, 0x18) + create_sprite(0x0118, EnemySprite.Shopkeeper, 0x00, 0, 0x19, 0x1b) + create_sprite(0x0119, EnemySprite.AdultNpc, 0x00, 0, 0x0e, 0x18) + create_sprite(0x011a, EnemySprite.DarkWorldHintNpc, 0x00, 0, 0x18, 0x17) + create_sprite(0x011b, EnemySprite.HeartPiece, 0x00, 0, 0x18, 0x09) + create_sprite(0x011b, EnemySprite.HeartPiece, 0x00, 1, 0x05, 0x16) + create_sprite(0x011c, EnemySprite.BombShopGuy, 0x00, 0, 0x09, 0x19) + create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x05, 0x07, 'Long Fairy Cave') + create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x06, 0x07, 'Long Fairy Cave') + create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x05, 0x08, 'Long Fairy Cave') + create_sprite(0x011e, EnemySprite.Faerie, 0x00, 0, 0x06, 0x08, 'Long Fairy Cave') + create_sprite(0x011e, EnemySprite.Shopkeeper, 0x00, 0, 0x18, 0x16) + create_sprite(0x011f, EnemySprite.Shopkeeper, 0x00, 0, 0x17, 0x16) + create_sprite(0x0120, EnemySprite.GoodBee, 0x00, 0, 0x17, 0x07) + create_sprite(0x0120, EnemySprite.Faerie, 0x00, 0, 0x1b, 0x08, 'Good Bee Cave (back)') + create_sprite(0x0120, EnemySprite.Faerie, 0x00, 0, 0x1a, 0x09, 'Good Bee Cave (back)') + create_sprite(0x0121, EnemySprite.Smithy, 0x00, 0, 0x04, 0x17) + create_sprite(0x0122, EnemySprite.FortuneTeller, 0x00, 0, 0x07, 0x18) + create_sprite(0x0122, EnemySprite.FortuneTeller, 0x00, 0, 0x17, 0x18) + create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x03, 0x16, 'Mini Moldorm Cave') + create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x0c, 0x16, 'Mini Moldorm Cave') + create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x08, 0x17, 'Mini Moldorm Cave') + create_sprite(0x0123, EnemySprite.MiniMoldorm, 0x00, 0, 0x03, 0x1a, 'Mini Moldorm Cave') + create_sprite(0x0123, EnemySprite.Shopkeeper, 0x00, 0, 0x08, 0x05) + create_sprite(0x0124, EnemySprite.Shopkeeper, 0x00, 0, 0x08, 0x16) + create_sprite(0x0125, EnemySprite.Shopkeeper, 0x00, 0, 0x08, 0x16) + create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x07, 0x15, 'Bonk Fairy Pool') + create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x08, 0x15, 'Bonk Fairy Pool') + create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x07, 0x16, 'Bonk Fairy Pool') + create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x08, 0x16, 'Bonk Fairy Pool') + create_sprite(0x0126, EnemySprite.HeartPiece, 0x00, 0, 0x1c, 0x14) + create_sprite(0x0127, EnemySprite.HeartPiece, 0x00, 0, 0x07, 0x16) + + # Dump above data into yaml + # class HexInt(int): pass + # + # def representer(dumper, data: HexInt): + # return yaml.ScalarNode('tag:yaml.org,2002:int', hex(data)) + # + # def build_dict(z): + # spr = { + # 'super_tile': HexInt(z.super_tile), + # 'kind': enemy_names[z.kind] if z.sub_type != 0x07 else HexInt(z.kind), + # 'sub_type': HexInt(z.sub_type), + # 'layer': z.layer, + # 'tile_x': HexInt(z.tile_x), + # 'tile_y': HexInt(z.tile_y) + # } + # if z.region: + # spr['region'] = z.region + # if z.drops_item: + # spr['drops_item'] = z.drops_item + # if z.drop_item_kind: + # spr['drop_item_kind'] = HexInt(z.drop_item_kind) + # return spr + # + # data_dump = {HexInt(x): [build_dict(z) for z in y] for x, y in vanilla_sprites.items()} + # + # yaml.add_representer(HexInt, representer) + # yaml.add_representer(defaultdict, Representer.represent_dict) + # with open('uw_enemy_list.yaml', 'w') as file: + # yaml.dump(data_dump, file, sort_keys=False) + + +layered_oam_rooms = { + 0x14, 0x15, 0x51, 0x59, 0x5b, 0x60, 0x62, 0x81, 0x86, 0xa8, 0xaa, 0xb2, 0xb9, 0xc2, 0xcb, 0xcc, 0xdb, 0xdc +} + + +class EnemyTable: + def __init__(self): + self.room_map = defaultdict(list) + self.special_bitmasks = None + + def write_sprite_data_to_rom(self, rom): + pointer_address = snes_to_pc(0x09D62E) + data_pointer = snes_to_pc(0x288000) + empty_pointer = pc_to_snes(data_pointer) & 0xFFFF + rom.write_bytes(data_pointer, [0x00, 0xff]) + data_pointer += 2 + for room in range(0, 0x128): + if room in self.room_map: + tracking_mask = 0x00 + data_address = pc_to_snes(data_pointer) & 0xFFFF + rom.write_bytes(pointer_address + room * 2, int16_as_bytes(data_address)) + rom.write_byte(data_pointer, 0x01 if room in layered_oam_rooms else 0x00) + list_offset = 1 + idx_adj = 0 + for idx, sprite in enumerate(self.room_map[room]): + data = sprite.sprite_data() + rom.write_bytes(data_pointer + list_offset, data) + list_offset += len(data) + if sprite.sub_type == 0x07: # overlord + idx_adj += 1 + continue + if sprite.location is not None: + tracking_mask |= 1 << (15 - idx + idx_adj) + rom.write_byte(data_pointer + list_offset, 0xff) + data_pointer += list_offset + 1 + rom.write_bytes(snes_to_pc(0x28ACB0) + room * 2, int16_as_bytes(tracking_mask)) + else: + rom.write_bytes(pointer_address + room * 2, int16_as_bytes(empty_pointer)) + + def size(self): + size = 2 + for room in range(0, 0x128): + if room in self.room_map: + size += sum(len(sprite.sprite_data()) for sprite in self.room_map[room]) + 2 + return size + + def check_special_bitmasks_size(self): + size = 0 + for super_tile, dungeon_list in self.special_bitmasks.items(): + size += (1 if len(dungeon_list) % 2 == 1 else 2) + len(dungeon_list) * 3 + if size > 256: + raise Exception("256 bytes limit reached on special bitmask table. Please revise") + + def write_special_bitmask_table(self, rom): + pointer = 0 + for super_tile, dungeon_list in self.special_bitmasks.items(): + pointer_index = pointer // 2 + rom.write_byte(snes_to_pc(0x28AF00) + super_tile, pointer_index) + is_even = len(dungeon_list) % 2 == 0 + for dungeon, bitmask in dungeon_list.items(): + rom.write_bytes(snes_to_pc(0x28B028) + pointer, [dungeon] + int16_as_bytes(bitmask)) + pointer += 3 + if is_even: + rom.write_bytes(snes_to_pc(0x28B028) + pointer, [0xFF, 0xFF]) + pointer += 2 + else: + rom.write_byte(snes_to_pc(0x28B028) + pointer, 0xFF) + pointer += 1 + + +def setup_enemy_locations(world, player): + for super_tile, enemy_list in world.data_tables[player].uw_enemy_table.room_map.items(): + for index, sprite in enumerate(enemy_list): + if valid_drop_location(sprite, index, world, player): + create_drop_location(sprite, index, super_tile, world, player) + + +# 1,1,1,1,2,1,2,2,1,2,3,2,2,2,2,2,2,1,2,1(2),1,1,1,1,2 +splittable_supertiles = {0x9, 0x1a, 0x35, 0x36, 0x37, 0x2a, 0x57, 0x74, 0x75, 0x6a, 0x7b, 0x7c, 0x7d, 0x9d, 0x8c, + 0x9b, 0x87, 0x82, 0xa2, 0xb2, 0xb6, 0xa9, 0xaa, 0xbc, 0xdb, 0xd1, + # fairy needs + 0x107, 0x10c, 0x115, 0x11e, 0x120, 0x126} + + +# minimum 159 bytes maybe reserve 256 (0x100) +# tr pipes, mire left/right bridges, eastern cannonball, tr front entrance = have zero enemies so far but are splittable +# 0x14, 0xa2, 0xb9, 0xd6 + +# only a concern once pits are done and cave interiors are shuffled somehow? +# identify by entrance ids, maybe? may need multiple +# e8? - separated hardhats, fa (if fairies (quardants)), fd (if fairies (quadrants almost...) + + +def setup_enemy_dungeon_tables(world, player): + enemy_table = world.data_tables[player].uw_enemy_table + dungeon_map = defaultdict(lambda: defaultdict(list)) + super_tile_entrance_id_map = {} # for memoization, very minor optimization + for super_tile, enemy_list in enemy_table.room_map.items(): + if super_tile in splittable_supertiles: + idx_adj = 0 + for index, sprite in enumerate(enemy_list): + if (super_tile, index) in key_drop_special: + loc_name = key_drop_special[(super_tile, index)] + else: + loc_name = f'{sprite.region} Enemy #{index + 1}' + loc = world.get_location_unsafe(loc_name, player) + if sprite.sub_type == 0x07: # overlord + idx_adj += 1 + continue # overlords can't have locations + if loc: + # possible to-do: caves really aren't supported yet - entrance ids? + if loc.parent_region.dungeon: + dungeon = loc.parent_region.dungeon.dungeon_id * 2 + dungeon_map[super_tile][dungeon].append((loc, index - idx_adj)) + else: + map_key = super_tile, loc.parent_region.name + if map_key not in super_tile_entrance_id_map: + super_tile_entrance_id_map[map_key] = find_entrance_ids(loc.parent_region) + for entrance_id in super_tile_entrance_id_map[map_key]: + dungeon_map[super_tile][entrance_id].append((loc, index - idx_adj)) + special_bitmasks = defaultdict(lambda: defaultdict(int)) + for super_tile, dungeon_list in dungeon_map.items(): + for dungeon, data_list in dungeon_list.items(): + for loc, index in data_list: + special_bitmasks[super_tile][dungeon] |= (1 << (15 - index)) + enemy_table.special_bitmasks = special_bitmasks + + +def find_entrance_ids(region): + entrance_list = [] + queue = deque([region]) + visited = {region} + while len(queue) > 0: + current = queue.popleft() + for ent in current.entrances: + if ent.parent_region.type in [RegionType.LightWorld, RegionType.DarkWorld]: + entrance_list.append(door_addresses[ent.name][0] + 1) + elif ent.parent_region not in visited: + queue.append(ent.parent_region) + visited.add(ent.parent_region) + return entrance_list + + +def valid_drop_location(sprite, index, world, player): + if world.dropshuffle[player] == 'underworld': + if sprite.drops_item and sprite.drop_item_kind in [0xe4, 0xe5]: + # already has a location + return False + elif sprite.never_drop: + return False + elif sprite.sub_type != SpriteType.Overlord: + stat = world.data_tables[player].enemy_stats[sprite.kind] + return stat.drop_flag and (not sprite.embedded or sprite.static) # static for the babusu spawners in Ice + + +def create_drop_location(sprite, index, super_tile, world, player): + address = drop_address(index, super_tile) + region_name = sprite.region + parent = world.get_region(region_name, player) + enemy_name = enemy_names[sprite.kind] + descriptor = f'Enemy #{index + 1}' + modifier = parent.hint_text not in {'a storyteller', 'fairies deep in a cave', 'a spiky hint', + 'a bounty of five items', 'the sick kid', 'Sahasrahla'} + hint_text = f'held by a {enemy_name} {"in" if modifier else "near"} {parent.hint_text}' + drop_location = Location(player, f'{region_name} {descriptor}', address, hint_text=hint_text, parent=parent, + note=enemy_name) + world.dynamic_locations.append(drop_location) + drop_location.drop = sprite + sprite.location = drop_location + drop_location.type = LocationType.Drop + parent.locations.append(drop_location) + + +# todo: placeholder address +def drop_address(index, super_tile): + return 0x7f9000 + super_tile * 2 + (index << 24) + + +prize_pack_selector = { + 0: ['Nothing'], + 1: ['Small Heart', 'Small Heart', 'Small Heart', 'Small Heart', + 'Rupee (1)', 'Small Heart', 'Small Heart', 'Rupee (1)'], + 2: ['Rupees (5)', 'Rupee (1)', 'Rupees (5)', 'Rupees (20)', + 'Rupees (5)', 'Rupee (1)', 'Rupees (5)', 'Rupees (5)'], + 3: ['Big Magic', 'Small Magic', 'Small Magic', 'Rupees (5)', + 'Big Magic', 'Small Magic', 'Small Heart', 'Small Magic'], + 4: ['Single Bomb', 'Single Bomb', 'Single Bomb', 'Single Bomb', + 'Single Bomb', 'Single Bomb', 'Bombs (10)', 'Single Bomb'], + 5: ['Arrows (5)', 'Small Heart', 'Arrows (5)', 'Arrows (10)', + 'Arrows (5)', 'Small Heart', 'Arrows (5)', 'Arrows (10)'], + 6: ['Small Magic', 'Rupee (1)', 'Small Heart', 'Arrows (5)', + 'Small Magic', 'Single Bomb', 'Rupee (1)', 'Small Heart'], + 7: ['Small Heart', 'Fairy', 'Big Magic', 'Rupees (20)', + 'Bombs (10)', 'Small Heart', 'Rupees (20)', 'Arrows (10)'], +} + + +def add_drop_contents(world, player): + retro_bow = world.bow_mode[player].startswith('retro') + index_selector = [0] * 8 + for super_tile, enemy_list in world.data_tables[player].uw_enemy_table.room_map.items(): + for sprite in enemy_list: + if sprite.drops_item and sprite.drop_item_kind == 0xe4: + continue + elif sprite.sub_type != SpriteType.Overlord: + stat = world.data_tables[player].enemy_stats[sprite.kind] + if stat.drop_flag: + pack = 0 + if isinstance(stat.prize_pack, int): + pack = stat.prize_pack + elif isinstance(stat.prize_pack, tuple): + pack = random.choice(stat.prize_pack) + pack_contents = prize_pack_selector[pack] + idx = index_selector[pack] + index_selector[pack] = (idx + 1) % len(pack_contents) + item_name = pack_contents[idx] + item_name = 'Rupees (5)' if retro_bow and 'Arrows' in item_name else item_name + world.itempool.append(ItemFactory(item_name, player)) + + +enemy_names = { + 0x00: 'Raven', + 0x01: 'Vulture', + 0x04: 'CorrectPullSwitch', + 0x06: 'WrongPullSwitch', + 0x08: 'Octorok', + 0x09: 'Moldorm', + 0x0a: 'Octorok4Way', + 0x0b: 'Cucco', + 0x0d: 'Buzzblob', + 0x0e: 'Snapdragon', + + 0x0f: 'Octoballoon', + 0x10: 'OctoballoonBaby', + 0x11: 'Hinox', + 0x12: 'Moblin', + 0x13: 'MiniHelmasaur', + 0x14: 'ThievesTownGrate', + 0x15: 'AntiFairy', + 0x16: 'Wiseman', + 0x17: 'Hoarder', + 0x18: 'MiniMoldorm', + 0x19: 'Poe', + 0x1a: 'Smithy', + 0x1b: 'Arrow', + 0x1c: 'Statue', + 0x1d: 'FluteQuest', + 0x1e: 'CrystalSwitch', + 0x1f: 'SickKid', + 0x20: 'Sluggula', + 0x21: 'WaterSwitch', + 0x22: 'Ropa', + 0x23: 'RedBari', + 0x24: 'BlueBari', + 0x25: 'TalkingTree', + 0x26: 'HardhatBeetle', + 0x27: 'Deadrock', + 0x28: 'DarkWorldHintNpc', + 0x29: 'AdultNpc', + 0x2a: 'SweepingLady', + 0x2b: 'Hobo', + 0x2c: 'Lumberjacks', + 0x2d: 'TelepathicTile', + 0x2e: 'FluteKid', + 0x2f: 'RaceGameLady', + + 0x30: 'RaceGameGuy', + 0x31: 'FortuneTeller', + 0x32: 'ArgueBros', + 0x33: 'RupeePull', + 0x34: 'YoungSnitch', + 0x35: 'Innkeeper', + 0x36: 'Witch', + 0x37: 'Waterfall', + 0x38: 'EyeStatue', + 0x39: 'Locksmith', + 0x3a: 'MagicBat', + 0x3b: 'BonkItem', + 0x3c: 'KidInKak', + 0x3d: 'OldSnitch', + 0x3e: 'Hoarder2', + 0x3f: 'TutorialGuard', + + 0x40: 'LightningGate', + 0x41: 'BlueGuard', + 0x42: 'GreenGuard', + 0x43: 'RedSpearGuard', + 0x44: 'BluesainBolt', + 0x45: 'UsainBolt', + 0x46: 'BlueArcher', + 0x47: 'GreenBushGuard', + 0x48: 'RedJavelinGuard', + 0x49: 'RedBushGuard', + 0x4a: 'BombGuard', + 0x4b: 'GreenKnifeGuard', + 0x4c: 'Geldman', + 0x4d: 'Toppo', + 0x4e: 'Popo', + 0x4f: 'Popo2', + + 0x51: 'ArmosStatue', + 0x52: 'KingZora', + 0x53: 'ArmosKnight', + 0x54: 'Lanmolas', + 0x55: 'FireballZora', + 0x56: 'Zora', + 0x57: 'DesertStatue', + 0x58: 'Crab', + 0x59: 'LostWoodsBird', + 0x5a: 'LostWoodsSquirrel', + 0x5b: 'SparkCW', + 0x5c: 'SparkCCW', + 0x5d: 'RollerVerticalUp', + 0x5e: 'RollerVerticalDown', + 0x5f: 'RollerHorizontalLeft', + 0x60: 'RollerHorizontalRight', + 0x61: 'Beamos', + 0x62: 'MasterSword', + 0x63: 'DebirandoPit', + 0x64: 'Debirando', + 0x65: 'ArcheryNpc', + 0x66: 'WallCannonVertLeft', + 0x67: 'WallCannonVertRight', + 0x68: 'WallCannonHorzTop', + 0x69: 'WallCannonHorzBottom', + 0x6a: 'BallNChain', + 0x6b: 'CannonTrooper', + 0x6d: 'CricketRat', + 0x6e: 'Snake', + 0x6f: 'Keese', + + 0x71: 'Leever', + 0x72: 'FairyPondTrigger', + 0x73: 'UnclePriest', + 0x74: 'RunningNpc', + 0x75: 'BottleMerchant', + 0x76: 'Zelda', + 0x78: 'Grandma', + 0x79: 'Bee', + 0x7a: 'Agahnim', + 0x7c: 'FloatingSkull', + 0x7d: 'BigSpike', + 0x7e: 'FirebarCW', + 0x7f: 'FirebarCCW', + 0x80: 'Firesnake', + 0x81: 'Hover', + 0x82: 'AntiFairyCircle', + 0x83: 'GreenEyegore', + 0x84: 'RedEyegore', + 0x85: 'YellowStalfos', # falling stalfos that shoots head + 0x86: 'Kodongo', + 0x88: 'Mothula', + 0x8a: 'SpikeBlock', + 0x8b: 'Gibdo', + 0x8c: 'Arrghus', + 0x8d: 'Arrghi', + 0x8e: 'Terrorpin', + 0x8f: 'Blob', + 0x90: 'Wallmaster', + 0x91: 'StalfosKnight', + 0x92: 'HelmasaurKing', + 0x93: 'Bumper', + 0x94: 'Pirogusu', + 0x95: 'LaserEyeLeft', + 0x96: 'LaserEyeRight', + 0x97: 'LaserEyeTop', + 0x98: 'LaserEyeBottom', + 0x99: 'Pengator', + 0x9a: 'Kyameron', + 0x9b: 'Wizzrobe', + 0x9c: 'Zoro', # babasu horizontal? + 0x9d: 'Babasu', # babasu vertical? + 0x9e: 'GroveOstritch', + 0x9f: 'GroveRabbit', + 0xa0: 'GroveBird', + 0xa1: 'Freezor', + 0xa2: 'Kholdstare', + 0xa3: 'KholdstareShell', + 0xa4: 'FallingIce', + 0xa5: 'BlueZazak', + 0xa6: 'RedZazak', + 0xa7: 'Stalfos', + 0xa8: 'GreenZirro', + 0xa9: 'BlueZirro', + 0xaa: 'Pikit', + 0xab: 'CrystalMaiden', + # ... OW + 0xac: 'Apple', + 0xad: 'OldMan', + 0xae: 'PipeDown', + 0xaf: 'PipeUp', + 0xb0: 'PipeRight', + 0xb1: 'PipeLeft', + 0xb2: 'GoodBee', + 0xb3: 'PedestalPlaque', + 0xb4: 'PurpleChest', + 0xb5: 'BombShopGuy', + 0xb6: 'Kiki', + 0xb7: 'BlindMaiden', + 0xb9: 'BullyPinkBall', + + 0xba: 'Whirlpool', + 0xbb: 'Shopkeeper', + 0xbc: 'Drunkard', + 0xbd: 'Vitreous', + # ... (spawnables) + 0xc0: 'Catfish', + 0xc1: 'CutsceneAgahnim', + 0xc2: 'Boulder', + 0xc3: 'Gibo', # patrick! + 0xc4: 'Thief', + 0xc5: 'Medusa', + 0xc6: 'FourWayShooter', + 0xc7: 'Pokey', + 0xc8: 'BigFairy', + 0xc9: 'Tektite', # firebat? + 0xca: 'Chainchomp', + 0xcb: 'TrinexxRockHead', + 0xcc: 'TrinexxFireHead', + 0xcd: 'TrinexxIceHead', + 0xce: 'Blind', + 0xcf: 'Swamola', + 0xd0: 'Lynel', + 0xd1: 'BunnyBeam', + 0xd2: 'FloppingFish', + 0xd3: 'Stal', # alive skull rock? + 0xd4: 'Landmine', + 0xd5: 'DiggingGameNPC', + 0xd6: 'Ganon', + 0xd8: 'SmallHeart', + 0xda: 'BlueRupee', + 0xdb: 'RedRupee', + 0xdc: 'BombRefill1', + 0xdd: 'BombRefill4', + 0xde: 'BombRefill8', + + 0xe0: 'LargeMagic', + 0xe3: 'Faerie', + 0xe4: 'SmallKey', + 0xe7: 'Mushroom', + 0xe8: 'FakeMasterSword', + 0xe9: 'MagicShopAssistant', + 0xeb: 'HeartPiece', + 0xed: 'SomariaPlatform', + 0xee: 'CastleMantle', + 0xef: 'GreenMimic', + 0xf0: 'RedMimic', + 0xf2: 'MedallionTablet', + 0xf3: 'PositionTarget', + 0xf4: 'Boulders' +} + +overlord_names = { + 0x01: 'PositionTarget', 0x02: 'FullRoomCannons', 0x03: 'VerticalCanon', + 0x05: 'FallingStalfos', 0x06: 'SnakeTrap', + 0x07: 'MovingFloor', 0x08: 'BlobSpawner', 0x09: 'Wallmaster', + 0x0A: 'FallingSquare', 0x0B: 'FallingBridge', + 0x10: 'Pirogusu_Left', 0x11: 'Pirogusu_Right', 0x12: 'Pirogusu_Top', 0x13: 'Pirogusu_Bottom', + 0x14: 'TileRoom', + 0x15: 'WizzrobeSpawner', 0x16: 'ZoroSpawner', 0x17: 'PotTrap', 0x18: 'InvisibleStalfos', + 0x19: 'ArmosCoordinator', 0x1A: 'BombTrap', +} + +sprite_translation = { + 'RollerVerticalDown': EnemySprite.RollerVerticalDown, + 'RollerVerticalUp': EnemySprite.RollerVerticalUp, + 'RollerHorizontalRight': EnemySprite.RollerHorizontalRight, + 'RollerHorizontalLeft': EnemySprite.RollerHorizontalLeft, + 'AntiFairyCircle': EnemySprite.AntiFairyCircle, + 'Beamos': EnemySprite.Beamos, + 'BigSpike': EnemySprite.BigSpike, + 'SpikeBlock': EnemySprite.SpikeBlock, + 'Bumper': EnemySprite.Bumper, + 'Statue': EnemySprite.Statue, + 'FirebarCW': EnemySprite.FirebarCW, + 'FirebarCCW': EnemySprite.FirebarCCW, + 'SparkCW': EnemySprite.SparkCW, + 'SparkCCW': EnemySprite.SparkCCW, + 'Kodongo': EnemySprite.Kodongo, + 'Antifairy': EnemySprite.AntiFairy, + 'AntiFairy': EnemySprite.AntiFairy, + 'ArmosStatue': EnemySprite.ArmosStatue, + 'Babasu': EnemySprite.Babasu, + 'BallNChain': EnemySprite.BallNChain, + 'Blob': EnemySprite.Blob, + 'BlueArcher': EnemySprite.BlueArcher, + 'BlueBari': EnemySprite.BlueBari, + 'BlueGuard': EnemySprite.BlueGuard, + 'BluesainBolt': EnemySprite.BluesainBolt, + 'BlueZazak': EnemySprite.BlueZazak, + 'BlueZirro': EnemySprite.BlueZirro, + 'BombGuard': EnemySprite.BombGuard, + 'BunnyBeam': EnemySprite.BunnyBeam, + 'Buzzblob': EnemySprite.Buzzblob, + 'CannonTrooper': EnemySprite.CannonTrooper, + 'Chainchomp': EnemySprite.Chainchomp, + 'Crab': EnemySprite.Crab, + 'CricketRat': EnemySprite.CricketRat, + 'Cucco': EnemySprite.Cucco, + 'Deadrock': EnemySprite.Deadrock, + 'Debirando': EnemySprite.Debirando, + 'DebirandoPit': EnemySprite.DebirandoPit, + 'FakeMasterSword': EnemySprite.FakeMasterSword, + 'Faerie': EnemySprite.Faerie, + 'FireballZora': EnemySprite.FireballZora, + 'Firesnake': EnemySprite.Firesnake, + 'FloatingSkull': EnemySprite.FloatingSkull, + 'FloppingFish': EnemySprite.FloppingFish, + 'FourWayShooter': EnemySprite.FourWayShooter, + 'Freezor': EnemySprite.Freezor, + 'Geldman': EnemySprite.Geldman, + 'Gibdo': EnemySprite.Gibdo, + 'Gibo': EnemySprite.Gibo, + 'GreenBushGuard': EnemySprite.GreenBushGuard, + 'GreenEyegoreMimic': EnemySprite.GreenEyegoreMimic, + 'GreenGuard': EnemySprite.GreenGuard, + 'GreenKnifeGuard': EnemySprite.GreenKnifeGuard, + 'GreenMimic': EnemySprite.GreenMimic, + 'GreenZirro': EnemySprite.GreenZirro, + 'HardhatBeetle': EnemySprite.HardhatBeetle, + 'Hinox': EnemySprite.Hinox, + 'Hoarder': EnemySprite.Hoarder, + 'Hoarder2': EnemySprite.Hoarder2, + 'Hover': EnemySprite.Hover, + 'Keese': EnemySprite.Keese, + 'Kyameron': EnemySprite.Kyameron, + 'Landmine': EnemySprite.Landmine, + 'Leever': EnemySprite.Leever, + 'Lynel': EnemySprite.Lynel, + 'Medusa': EnemySprite.Medusa, + 'MiniHelmasaur': EnemySprite.MiniHelmasaur, + 'MiniMoldorm': EnemySprite.MiniMoldorm, + 'Moblin': EnemySprite.Moblin, + 'Octoballoon': EnemySprite.Octoballoon, + 'Octorok': EnemySprite.Octorok, + 'Octorok4Way': EnemySprite.Octorok4Way, + 'Pengator': EnemySprite.Pengator, + 'Pikit': EnemySprite.Pikit, + 'Poe': EnemySprite.Poe, + 'Pokey': EnemySprite.Pokey, + 'Popo': EnemySprite.Popo, + 'Popo2': EnemySprite.Popo2, + 'Raven': EnemySprite.Raven, + 'RedBari': EnemySprite.RedBari, + 'RedBushGuard': EnemySprite.RedBushGuard, + 'RedEyegoreMimic': EnemySprite.RedEyegoreMimic, + 'RedJavelinGuard': EnemySprite.RedJavelinGuard, + 'RedMimic': EnemySprite.RedMimic, + 'RedSpearGuard': EnemySprite.RedSpearGuard, + 'RedZazak': EnemySprite.RedZazak, + 'Ropa': EnemySprite.Ropa, + 'Sluggula': EnemySprite.Sluggula, + 'Snake': EnemySprite.Snake, + 'Snapdragon': EnemySprite.Snapdragon, + 'Stal': EnemySprite.Stal, + 'Stalfos': EnemySprite.Stalfos, + 'StalfosKnight': EnemySprite.StalfosKnight, + 'Swamola': EnemySprite.Swamola, + 'Tektite': EnemySprite.Tektite, + 'Terrorpin': EnemySprite.Terrorpin, + 'Thief': EnemySprite.Thief, + 'Toppo': EnemySprite.Toppo, + 'UsainBolt': EnemySprite.UsainBolt, + 'Vulture': EnemySprite.Vulture, + 'YellowStalfos': EnemySprite.YellowStalfos, + 'Wallmaster': EnemySprite.Wallmaster, + 'Wizzrobe': EnemySprite.Wizzrobe, + 'Zora': EnemySprite.Zora, + 'Zoro': EnemySprite.Zoro, +} diff --git a/source/dungeon/RoomConstants.py b/source/dungeon/RoomConstants.py new file mode 100644 index 00000000..82df24fa --- /dev/null +++ b/source/dungeon/RoomConstants.py @@ -0,0 +1,266 @@ +Ganon = 0x0 +HC_NorthCorridor = 0x1 +HC_SwitchRoom = 0x2 +HoulihanRoom = 0x3 +TR_CrystalRollerRoom = 0x4 +Swamp_Arrghus = 0x6 +Hera_Moldorm = 0x7 +Cave_HealingFairy = 0x8 +PalaceofDarkness0x09 = 0x9 +PoD_StalfosTrapRoom = 0xa +PoD_TurtleRoom = 0xb +GT_EntranceRoom = 0xc +GT_Agahnim2 = 0xd +Ice_EntranceRoom = 0xe +GanonEvacuationRoute = 0x10 +HC_BombableStockRoom = 0x11 +Sanctuary = 0x12 +TR_Hokku_BokkuKeyRoom2 = 0x13 +TR_BigKeyRoom = 0x14 +TurtleRock0x15 = 0x15 +Swamp_SwimmingTreadmill = 0x16 +Hera_MoldormFallRoom = 0x17 +Cave0x18_BigFairyDropEntrance = 0x18 +PoD_DarkMaze = 0x19 +PoD_BigChestRoom = 0x1a +PoD_Mimics_MovingWallRoom = 0x1b +GT_IceArmos = 0x1c +GT_FinalHallway = 0x1d +Ice_BombFloor_BariRoom = 0x1e +Ice_Pengator_BigKeyRoom = 0x1f +Tower_Agahnim = 0x20 +HC_KeyRatRoom = 0x21 +HC_SewerTextTriggerRoom = 0x22 +TR_WestExittoBalcony = 0x23 +TR_DoubleHokku_Bokku_BigchestRoom = 0x24 +Swamp_StatueRoom = 0x26 +Hera_BigChest = 0x27 +Swamp_EntranceRoom = 0x28 +Skull_Mothula = 0x29 +PoD_BigHubRoom = 0x2a +PoD_MapChest_FairyRoom = 0x2b +Cave0x2C_HookshotCaveBackdoor = 0x2c +Ice_CompassRoom = 0x2e +Cave_KakarikoWellHP = 0x2f +Tower_MaidenSacrificeChamber = 0x30 +Hera_HardhatBeetlesRoom = 0x31 +HC_SewerKeyChestRoom = 0x32 +Desert_Lanmolas = 0x33 +Swamp_PushBlockPuzzle_Pre_BigKeyRoom = 0x34 +Swamp_BigKey_BSRoom = 0x35 +Swamp_BigChestRoom = 0x36 +Swamp_MapChest_WaterFillRoom = 0x37 +Swamp_KeyPotRoom = 0x38 +Skull_GibdoKey_MothulaHoleRoom = 0x39 +PoD_BombableFloorRoom = 0x3a +PoD_SpikeBlock_ConveyorRoom = 0x3b +Cave0x3C_HookshotCave = 0x3c +GT_TorchRoom2 = 0x3d +Ice_StalfosKnights_ConveyorHellway = 0x3e +Ice_MapChestRoom = 0x3f +Tower_FinalBridgeRoom = 0x40 +HC_FirstDarkRoom = 0x41 +HC_6RopesRoom = 0x42 +Desert_TorchPuzzle_MovingWallRoom = 0x43 +TT_BigChestRoom = 0x44 +TT_JailCellsRoom = 0x45 +Swamp_CompassChestRoom = 0x46 +Skull_GibdoTorchPuzzleRoom = 0x49 +PoD_EntranceRoom = 0x4a +PoD_Warps_SouthMimicsRoom = 0x4b +GT_Mini_HelmasaurConveyorRoom = 0x4c +GT_MoldormRoom = 0x4d +Ice_Bomb_JumpRoom = 0x4e +IcePalaceCloneRoom_FairyRoom = 0x4f +HC_WestCorridor = 0x50 +HC_ThroneRoom = 0x51 +HC_EastCorridor = 0x52 +Desert_Popos2_BeamosHellwayRoom = 0x53 +Swamp_UpstairsPitsRoom = 0x54 +CastleSecretEntrance_UncleDeathRoom = 0x55 +Skull_KeyPot_TrapRoom = 0x56 +Skull_BigKeyRoom = 0x57 +Skull_BigChestRoom = 0x58 +Skull_FinalSectionEntranceRoom = 0x59 +PoD_HelmasaurKing = 0x5a +GT_SpikePitRoom = 0x5b +GT_Ganon_BallZ = 0x5c +GT_Gauntlet1_2_3 = 0x5d +Ice_LonelyFirebar = 0x5e +Ice_HiddenChest_SpikeFloorRoom = 0x5f +HC_WestEntranceRoom = 0x60 +HC_MainEntranceRoom = 0x61 +HC_EastEntranceRoom = 0x62 +Desert_FinalSectionEntranceRoom = 0x63 +TT_WestAtticRoom = 0x64 +TT_EastAtticRoom = 0x65 +Swamp_HiddenChest_HiddenDoorRoom = 0x66 +Skull_CompassChestRoom = 0x67 +Skull_KeyChest_TrapRoom = 0x68 +PoD_RupeeRoom = 0x6a +GT_MimicsRooms = 0x6b +GT_LanmolasRoom = 0x6c +GT_Gauntlet4_5 = 0x6d +Ice_PengatorsRoom = 0x6e +HC_SmallCorridortoJailCells = 0x70 +HC_BoomerangChestRoom = 0x71 +HC_MapChestRoom = 0x72 +Desert_BigChestRoom = 0x73 +Desert_MapChestRoom = 0x74 +Desert_BigKeyChestRoom = 0x75 +Swamp_WaterDrainRoom = 0x76 +Hera_EntranceRoom = 0x77 +GanonsTower = 0x7b +GT_EastSideCollapsingBridge_ExplodingWallRoom = 0x7c +GT_Winder_WarpMazeRoom = 0x7d +Ice_HiddenChest_BombableFloorRoom = 0x7e +Ice_BigSpikeTrapsRoom = 0x7f +HC_JailCellRoom = 0x80 +HC_NextToChasmRoom = 0x81 +HC_BasementChasmRoom = 0x82 +Desert_WestEntranceRoom = 0x83 +Desert_MainEntranceRoom = 0x84 +Desert_EastEntranceRoom = 0x85 +Hera_TileRoom = 0x87 +Eastern_FairyRoom = 0x89 +GT_BlockPuzzle_SpikeSkip_MapChestRoom = 0x8b +GT_EastandWestDownstairs_BigChestRoom = 0x8c +GT_Tile_TorchPuzzleRoom = 0x8d +IcePalace0x8E = 0x8e +Mire_Vitreous = 0x90 +Mire_FinalSwitchRoom = 0x91 +Mire_DarkBombWall_SwitchesRoom = 0x92 +Mire_DarkCaneFloorSwitchPuzzleRoom = 0x93 +GT_FinalCollapsingBridgeRoom = 0x95 +GT_Torches1Room = 0x96 +Mire_TorchPuzzle_MovingWallRoom = 0x97 +Mire_EntranceRoom = 0x98 +Eastern_EyegoreKeyRoom = 0x99 +GT_ManySpikes_WarpMazeRoom = 0x9b +GT_InvisibleFloorMazeRoom = 0x9c +GT_CompassChest_InvisibleFloorRoom = 0x9d +Ice_BigChestRoom = 0x9e +IcePalace0x9F = 0x9f +Mire_Pre_VitreousRoom = 0xa0 +Mire_FishRoom = 0xa1 +Mire_BridgeKeyChestRoom = 0xa2 +MiseryMire0xA3 = 0xa3 +TR_Trinexx = 0xa4 +GT_WizzrobesRooms = 0xa5 +GT_MoldormFallRoom = 0xa6 +Hera_FairyRoom = 0xa7 +Eastern_StalfosSpawnRoom = 0xa8 +Eastern_BigChestRoom = 0xa9 +Eastern_MapChestRoom = 0xaa +TT_MovingSpikes_KeyPotRoom = 0xab +TT_BlindTheThief = 0xac +IcePalace0xAE = 0xae +Ice_IceBridgeRoom = 0xaf +Tower_CircleofPots = 0xb0 +Mire_HourglassRoom = 0xb1 +Mire_SlugRoom = 0xb2 +Mire_SpikeKeyChestRoom = 0xb3 +TR_Pre_TrinexxRoom = 0xb4 +TR_DarkMaze = 0xb5 +TR_ChainChompsRoom = 0xb6 +TR_MapChest_KeyChest_RollerRoom = 0xb7 +Eastern_BigKeyRoom = 0xb8 +Eastern_LobbyCannonballsRoom = 0xb9 +Eastern_DarkAntifairy_KeyPotRoom = 0xba +TT_Hellway = 0xbb +TT_ConveyorToilet = 0xbc +Ice_BlockPuzzleRoom = 0xbe +IcePalaceCloneRoom_SwitchRoom = 0xbf +Tower_DarkBridgeRoom = 0xc0 +Mire_CompassChest_TileRoom = 0xc1 +Mire_BigHubRoom = 0xc2 +Mire_BigChestRoom = 0xc3 +TR_FinalCrystalSwitchPuzzleRoom = 0xc4 +TR_LaserBridge = 0xc5 +TurtleRock0xC6 = 0xc6 +TR_TorchPuzzle = 0xc7 +Eastern_ArmosKnights = 0xc8 +Eastern_EntranceRoom = 0xc9 +UnknownRoom = 0xca +TT_NorthWestEntranceRoom = 0xcb +TT_NorthEastEntranceRoom = 0xcc +Ice_HoletoKholdstareRoom = 0xce +Tower_DarkMaze = 0xd0 +Mire_ConveyorSlug_BigKeyRoom = 0xd1 +Mire_Mire02_WizzrobesRoom = 0xd2 +TR_LaserKeyRoom = 0xd5 +TR_EntranceRoom = 0xd6 +Eastern_PreArmosKnightsRoom = 0xd8 +Eastern_CanonballRoom = 0xd9 +EasternPalace = 0xda +TT_Main_SouthWestEntranceRoom = 0xdb +TT_SouthEastEntranceRoom = 0xdc +Ice_Kholdstare = 0xde +Cave_BackwardsDeathMountainTopFloor = 0xdf +Tower_EntranceRoom = 0xe0 +Cave_LostWoodsHP = 0xe1 +Cave_LumberjacksTreeHP = 0xe2 +Cave_HalfMagic = 0xe3 +Cave_LostOldManFinalCave = 0xe4 +Cave_LostOldManFinalCave2 = 0xe5 +Cave0xE6 = 0xe6 +Cave0xE7 = 0xe7 +Cave0xE8 = 0xe8 +Cave_SpectacleRockHP = 0xea +Cave0xEB = 0xeb +Cave0xED = 0xed +Cave_SpiralCave = 0xee +Cave_CrystalSwitch_5ChestsRoom = 0xef +Cave_LostOldManStartingCave = 0xf0 +Cave_LostOldManStartingCave2 = 0xf1 +House = 0xf2 +House_OldWoman = 0xf3 +House_AngryBrothers = 0xf4 +House_AngryBrothers2 = 0xf5 +Cave0xF8 = 0xf8 +Cave0xF9 = 0xf9 +Cave0xFA = 0xfa +Cave0xFB = 0xfb +Cave0xFD = 0xfd +Cave0xFE = 0xfe +Cave0xFF = 0xff +ShopInLostWoods0x100 = 0x100 +ScaredLadyHouses = 0x101 +SickKid = 0x102 +Inn_BushHouse = 0x103 +LinksHouse = 0x104 +ShabadooHouse = 0x105 +ChestGame_BombHouse = 0x106 +Library_BombFarmRoom = 0x107 +ChickenHouse = 0x108 +WitchHut = 0x109 +Aginah = 0x10a +SwampFloodwayRoom = 0x10b +MimicCave = 0x10c +CaveOutsideMiseryMire = 0x10d +Cave0x10E = 0x10e +Shop0x10F = 0x10f +Shop0x110 = 0x110 +ArcherGame = 0x111 +CaveShop0x112 = 0x112 +KingsTomb = 0x113 +WishingWell_Cave0x114 = 0x114 +WishingWell_BigFairy = 0x115 +FatFairy = 0x116 +SpikeCave = 0x117 +Shop0x118 = 0x118 +BlindsHouse = 0x119 +Mutant = 0x11a +MirrorCaveGroveAndTomb = 0x11b +BombShop = 0x11c +BlindsBasement = 0x11d +HypeCave = 0x11e +Shop0x11F = 0x11f +IceRodCave = 0x120 +SmithHouse = 0x121 +FortuneTellers = 0x122 +MiniMoldormCave = 0x123 +UnknownCave_BonkCave = 0x124 +Cave0x125 = 0x125 +CheckerBoardCave = 0x126 +HammerPegCave = 0x127 \ No newline at end of file diff --git a/source/dungeon/RoomHeader.py b/source/dungeon/RoomHeader.py new file mode 100644 index 00000000..5dc0bef7 --- /dev/null +++ b/source/dungeon/RoomHeader.py @@ -0,0 +1,331 @@ + + +vanilla_headers = { + 0x0000: [0x41, 0x21, 0x13, 0x22, 0x07, 0x3D, 0x00, 0x00, 0x00, 0x10, 0xC0, 0x00, 0x00, 0x04], + 0x0001: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x72, 0x00, 0x50, 0x52], + 0x0002: [0xC0, 0x1D, 0x04, 0x06, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x11, 0x00, 0x18, 0x0D], + 0x0003: [0xC0, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x02, 0x12, 0x00, 0x00, 0x00], + 0x0004: [0x00, 0x18, 0x0D, 0x26, 0x00, 0x26, 0x14, 0x00, 0x00, 0x00, 0xB5, 0x00, 0x08, 0x08], + 0x0005: [0x00, 0x08, 0x08, 0x14, 0x00, 0x25, 0x00, 0x20, 0x06, 0x05, 0x0C, 0x00, 0x25, 0x00], + 0x0006: [0x00, 0x08, 0x08, 0x14, 0x00, 0x25, 0x00, 0x20, 0x06, 0x05, 0x0C, 0x00, 0x25, 0x00], + 0x0007: [0x20, 0x06, 0x05, 0x0C, 0x00, 0x25, 0x00, 0x00, 0x00, 0x17, 0x17, 0xC0, 0x07, 0x06], + 0x0008: [0xC0, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x27, 0x00], + 0x0009: [0x00, 0x0F, 0x07, 0x19, 0x00, 0x27, 0x00, 0x00, 0x00, 0x4B, 0x4A, 0x4A, 0x00, 0x0F], + 0x000A: [0x00, 0x0F, 0x07, 0x19, 0x00, 0x27, 0x00, 0x00, 0x00, 0x09, 0x3A, 0x01, 0x0F, 0x07], + 0x000B: [0x01, 0x0F, 0x07, 0x19, 0x00, 0x03, 0x00, 0x00, 0x00, 0x6A, 0x1B, 0xC0, 0x28, 0x0E], + 0x000C: [0xC0, 0x28, 0x0E, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x6B, 0x8C, 0x8C, 0x40], + 0x000D: [0x40, 0x1B, 0x0E, 0x18, 0x05, 0x38, 0x00, 0x00, 0x13, 0x0B, 0x1C, 0x00, 0x08, 0x00], + 0x000E: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x21, 0x13], + 0x000F: [0x00, 0x21, 0x13, 0x22, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00], + 0x0010: [0x00, 0x21, 0x13, 0x22, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00], + 0x0011: [0x00, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x02, 0xC0, 0x1D, 0x04], + 0x0012: [0xC0, 0x1D, 0x04, 0x06, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00], + 0x0013: [0x00, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x1E, 0x00, 0x00, 0x00], + 0x0014: [0x20, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00, 0xC0, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00], + 0x0015: [0xC0, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB6, 0x90, 0x08, 0x08], + 0x0016: [0x90, 0x08, 0x08, 0x11, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x66, 0x20, 0x06, 0x05], + 0x0017: [0x20, 0x06, 0x05, 0x19, 0x00, 0x35, 0x00, 0x00, 0x00, 0x27, 0x07, 0x27, 0x01, 0x0F], + 0x0018: [0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x07, 0x00, 0x00, 0x00], + 0x0019: [0x01, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x16, 0x00], + 0x001A: [0x00, 0x0F, 0x07, 0x19, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x6A, 0x6A, 0x68, 0x0F], + 0x001B: [0x68, 0x0F, 0x07, 0x08, 0x00, 0x03, 0x1C, 0x00, 0x00, 0x00, 0x0B, 0x00, 0x1A, 0x0E], + 0x001C: [0x00, 0x1A, 0x0E, 0x09, 0x00, 0x04, 0x3F, 0x00, 0x00, 0x00, 0x8C, 0x00, 0x1B, 0x0E], + 0x001D: [0x00, 0x1B, 0x0E, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4C, 0x20, 0x13, 0x0B], + 0x001E: [0x20, 0x13, 0x0B, 0x1C, 0x00, 0x17, 0x00, 0x00, 0x00, 0x3E, 0x0E, 0x00, 0x13, 0x0B], + 0x001F: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x20, 0x0C, 0x02], + 0x0020: [0x20, 0x0C, 0x02, 0x12, 0x00, 0x15, 0x25, 0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00], + 0x0021: [0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x26, 0x00, 0x01, 0x00], + 0x0022: [0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x26, 0x00, 0x01, 0x00], + 0x0023: [0x00, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x1E, 0x00, 0x00, 0x00], + 0x0024: [0x00, 0x18, 0x0D, 0x26, 0x00, 0x01, 0x00, 0x00, 0x0A, 0x08, 0x11, 0x00, 0x16, 0x00], + 0x0025: [0x00, 0x0A, 0x08, 0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x76, 0x76, 0x76, 0x20], + 0x0026: [0x00, 0x0A, 0x08, 0x11, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x76, 0x76, 0x76, 0x20], + 0x0027: [0x20, 0x06, 0x05, 0x19, 0x00, 0x36, 0x00, 0x00, 0x00, 0x31, 0x17, 0x31, 0x80, 0x0A], + 0x0028: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x32, 0x1B, 0x00, 0x00, 0x00, 0x38, 0xCC, 0x0E, 0x09], + 0x0029: [0xCC, 0x0E, 0x09, 0x1A, 0x02, 0x25, 0x00, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00], + 0x002A: [0x00, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00, 0xC0, 0x0F, 0x07, 0x2B, 0x00, 0x16, 0x00], + 0x002B: [0xC0, 0x0F, 0x07, 0x2B, 0x00, 0x16, 0x00, 0x00, 0x00, 0x00, 0x3B, 0x00, 0x13, 0x0B], + 0x002C: [0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x07, 0x00, 0x00, 0x00], + 0x002D: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x2A, 0x00, 0xC0, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00], + 0x002E: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x2A, 0x00, 0xC0, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00], + 0x002F: [0xC0, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00, 0x00, 0x0C, 0x02, 0x12, 0x00, 0x00, 0x00], + 0x0030: [0x00, 0x0C, 0x02, 0x12, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x20, 0x06, 0x05], + 0x0031: [0x20, 0x06, 0x05, 0x19, 0x00, 0x37, 0x04, 0x22, 0x00, 0x77, 0x27, 0x77, 0x01, 0x01], + 0x0032: [0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x04, 0x05], + 0x0033: [0x00, 0x04, 0x05, 0x0B, 0x00, 0x15, 0x25, 0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00], + 0x0034: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x54, 0x80, 0x0A, 0x08], + 0x0035: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x19, 0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00], + 0x0036: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00, 0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00], + 0x0037: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x19, 0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00], + 0x0038: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x28, 0x20, 0x0D, 0x09], + 0x0039: [0x20, 0x0D, 0x09, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x29, 0x20, 0x0F, 0x07, 0x19], + 0x003A: [0x20, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0A, 0x0A, 0x00, 0x0F, 0x07], + 0x003B: [0x00, 0x0F, 0x07, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x00, 0x07, 0x06], + 0x003C: [0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00, 0x20, 0x1A, 0x0E, 0x0C, 0x00, 0x33, 0x00], + 0x003D: [0x20, 0x1A, 0x0E, 0x0C, 0x00, 0x33, 0x00, 0x00, 0x00, 0x96, 0x96, 0xCC, 0x13, 0x0B], + 0x003E: [0xCC, 0x13, 0x0B, 0x29, 0x02, 0x02, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x00, 0x13, 0x0B], + 0x003F: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x27, 0x14, 0x00, 0x00, 0x00, 0x1F, 0x5F, 0xC0, 0x00], + 0x0040: [0xC0, 0x00, 0x02, 0x27, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x30, 0xB0, 0x01, 0x00], + 0x0041: [0x01, 0x00, 0x00, 0x02, 0x00, 0x13, 0x00, 0x00, 0x00, 0x00, 0x42, 0x01, 0x01, 0x01], + 0x0042: [0x01, 0x01, 0x01, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x41, 0x32, 0x68, 0x04], + 0x0043: [0x68, 0x04, 0x05, 0x0A, 0x00, 0x00, 0x1D, 0x00, 0x17, 0x0A, 0x1B, 0x00, 0x01, 0x00], + 0x0044: [0x00, 0x17, 0x0A, 0x1B, 0x00, 0x01, 0x00, 0x60, 0x17, 0x0A, 0x1B, 0x00, 0x01, 0x00], + 0x0045: [0x60, 0x17, 0x0A, 0x1B, 0x00, 0x01, 0x00, 0x00, 0x00, 0x00, 0xBC, 0x00, 0x0A, 0x08], + 0x0046: [0x00, 0x0A, 0x08, 0x11, 0x00, 0x3C, 0x00, 0x00, 0x0D, 0x09, 0x13, 0x00, 0x33, 0x34], + 0x0047: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x33, 0x34, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x17, 0x00], + 0x0048: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x33, 0x34, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x17, 0x00], + 0x0049: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x33, 0x34, 0x00, 0x0F, 0x07, 0x19, 0x00, 0x17, 0x00], + 0x004A: [0x00, 0x0F, 0x07, 0x19, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x09, 0x09, 0x00, 0x0F], + 0x004B: [0x00, 0x0F, 0x07, 0x08, 0x00, 0x01, 0x00, 0x00, 0x00, 0x09, 0x00, 0x1A, 0x0E, 0x0C], + 0x004C: [0x00, 0x1A, 0x0E, 0x0C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x20, 0x1A, 0x0E], + 0x004D: [0x20, 0x1A, 0x0E, 0x0C, 0x00, 0x32, 0x3F, 0x00, 0x00, 0xA6, 0xA6, 0x00, 0x13, 0x0B], + 0x004E: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x6E, 0x00, 0x13, 0x0B], + 0x004F: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xC0, 0x00, 0x00, 0x04], + 0x0050: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01], + 0x0051: [0xC0, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x61, 0xC0, 0x00, 0x00], + 0x0052: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x01], + 0x0053: [0xC0, 0x04, 0x05, 0x0A, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x63, 0x20, 0x0A, 0x08], + 0x0054: [0x20, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00, 0x00, 0x00, 0x34, 0x34, 0x01, 0x01, 0x10], + 0x0055: [0x01, 0x01, 0x10, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x0D, 0x09, 0x13, 0x00, 0x23, 0x00], + 0x0056: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x23, 0x00, 0x00, 0x0D, 0x09, 0x13, 0x00, 0x16, 0x00], + 0x0057: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x16, 0x00, 0x00, 0x0D, 0x09, 0x13, 0x00, 0x21, 0x28], + 0x0058: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x21, 0x28, 0xC0, 0x0D, 0x09, 0x13, 0x00, 0x00, 0x00], + 0x0059: [0xC0, 0x0D, 0x09, 0x13, 0x00, 0x00, 0x00, 0x00, 0x10, 0x07, 0x15, 0x00, 0x25, 0x00], + 0x005A: [0x00, 0x10, 0x07, 0x15, 0x00, 0x25, 0x00, 0xC0, 0x1B, 0x0E, 0x0A, 0x00, 0x17, 0x00], + 0x005B: [0xC0, 0x1B, 0x0E, 0x0A, 0x00, 0x17, 0x00, 0x00, 0x1B, 0x0E, 0x0A, 0x00, 0x00, 0x00], + 0x005C: [0x00, 0x1B, 0x0E, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5D, 0x00, 0x24, 0x0E], + 0x005D: [0x00, 0x24, 0x0E, 0x23, 0x00, 0x09, 0x00, 0x00, 0x00, 0x00, 0x5C, 0x20, 0x13, 0x0B], + 0x005E: [0x20, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7E, 0x7E, 0x00, 0x13, 0x0B], + 0x005F: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x3F, 0x7F, 0xC0, 0x00], + 0x0060: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00], + 0x0061: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x51, 0x00, 0x09, 0x05], + 0x0062: [0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00, 0xC0, 0x00, 0x00, 0x04, 0x00, 0x00, 0x00], + 0x0063: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x53, 0xE0, 0x23, 0x0A], + 0x0064: [0xE0, 0x23, 0x0A, 0x21, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0xAB, 0xE0, 0x23, 0x0A], + 0x0065: [0xE0, 0x23, 0x0A, 0x21, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAC, 0xC0, 0x0A, 0x08, 0x11], + 0x0066: [0xC0, 0x0A, 0x08, 0x11, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x00, 0x16, 0x00, 0x0D, 0x09], + 0x0067: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x22, 0x00, 0x00, 0x0D, 0x09, 0x13, 0x00, 0x00, 0x00], + 0x0068: [0x00, 0x0D, 0x09, 0x13, 0x00, 0x00, 0x00, 0x01, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00], + 0x0069: [0x01, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x1A, 0x00, 0x1B], + 0x006A: [0x01, 0x0F, 0x07, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1A, 0x1A, 0x00, 0x1B], + 0x006B: [0x00, 0x1B, 0x0E, 0x0A, 0x00, 0x08, 0x0B, 0x00, 0x00, 0x00, 0x0C, 0x00, 0x24, 0x0E], + 0x006C: [0x00, 0x24, 0x0E, 0x23, 0x00, 0x03, 0x3F, 0x00, 0x00, 0x00, 0xA5, 0x00, 0x24, 0x0E], + 0x006D: [0x00, 0x24, 0x0E, 0x23, 0x00, 0x05, 0x00, 0x00, 0x13, 0x0B, 0x1C, 0x00, 0x02, 0x00], + 0x006E: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 0x4E, 0x00, 0x01, 0x01], + 0x006F: [0x00, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x71, 0x80, 0xC0, 0x01], + 0x0070: [0x00, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x71, 0x80, 0xC0, 0x01], + 0x0071: [0xC0, 0x01, 0x01, 0x04, 0x00, 0x08, 0x00, 0x00, 0x00, 0x00, 0x70, 0xC0, 0x01, 0x01], + 0x0072: [0xC0, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x01, 0x00, 0x09, 0x05], + 0x0073: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x17, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x27, 0x00], + 0x0074: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x27, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x01, 0x00], + 0x0075: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x01, 0x00, 0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x18], + 0x0076: [0x80, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x18, 0x00, 0x00, 0x00, 0x26, 0x26, 0x26, 0xC0], + 0x0077: [0xC0, 0x06, 0x05, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA7, 0x31, 0x87, 0x87, 0x00], + 0x0078: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x03, 0x39, 0x00, 0x00, 0x9D, 0x00, 0x28, 0x0E, 0x13], + 0x0079: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x03, 0x39, 0x00, 0x00, 0x9D, 0x00, 0x28, 0x0E, 0x13], + 0x007A: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x03, 0x39, 0x00, 0x00, 0x9D, 0x00, 0x28, 0x0E, 0x13], + 0x007B: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x03, 0x39, 0x00, 0x00, 0x9D, 0x00, 0x28, 0x0E, 0x13], + 0x007C: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x20, 0x00, 0x00, 0x28, 0x0E, 0x13, 0x00, 0x04, 0x3C], + 0x007D: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x04, 0x3C, 0x00, 0x00, 0x9B, 0x20, 0x13, 0x0B, 0x1C], + 0x007E: [0x20, 0x13, 0x0B, 0x1C, 0x00, 0x2B, 0x17, 0x00, 0x00, 0x9E, 0x5E, 0x00, 0x13, 0x0B], + 0x007F: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x5F, 0x60, 0x01, 0x01], + 0x0080: [0x60, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x70, 0xC0, 0x01, 0x01], + 0x0081: [0xC0, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x0D, 0x00], + 0x0082: [0xC0, 0x01, 0x01, 0x04, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x0D, 0x00], + 0x0083: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x0D, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x00, 0x00], + 0x0084: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x09, 0x05, 0x0A, 0x00, 0x02, 0x00], + 0x0085: [0x00, 0x09, 0x05, 0x0A, 0x00, 0x02, 0x00, 0x00, 0x06, 0x05, 0x19, 0x00, 0x3E, 0x01], + 0x0086: [0x00, 0x06, 0x05, 0x19, 0x00, 0x3E, 0x01, 0x28, 0x00, 0x00, 0x77, 0x77, 0x00, 0x0B], + 0x0087: [0x00, 0x06, 0x05, 0x19, 0x00, 0x3E, 0x01, 0x28, 0x00, 0x00, 0x77, 0x77, 0x00, 0x0B], + 0x0088: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0xA9, 0x00, 0x28, 0x0E, 0x13], + 0x0089: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x00, 0x00, 0x02, 0x00, 0xA9, 0x00, 0x28, 0x0E, 0x13], + 0x008A: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x3A, 0x0C, 0x20, 0x28, 0x0E, 0x13, 0x00, 0x16, 0x00], + 0x008B: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x3A, 0x0C, 0x20, 0x28, 0x0E, 0x13, 0x00, 0x16, 0x00], + 0x008C: [0x20, 0x28, 0x0E, 0x13, 0x00, 0x16, 0x00, 0x28, 0x00, 0x1C, 0x0C, 0x0C, 0x1C, 0x00], + 0x008D: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x33, 0x29, 0x00, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00], + 0x008E: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAE, 0x80, 0x12, 0x0C], + 0x008F: [0x80, 0x12, 0x0C, 0x16, 0x00, 0x25, 0x00, 0x00, 0x11, 0x0C, 0x1C, 0x00, 0x00, 0x00], + 0x0090: [0x80, 0x12, 0x0C, 0x16, 0x00, 0x25, 0x00, 0x00, 0x11, 0x0C, 0x1C, 0x00, 0x00, 0x00], + 0x0091: [0x00, 0x11, 0x0C, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA0, 0x01, 0x11, 0x0C], + 0x0092: [0x01, 0x11, 0x0C, 0x1C, 0x00, 0x00, 0x00, 0x01, 0x11, 0x0C, 0x1C, 0x00, 0x16, 0x00], + 0x0093: [0x01, 0x11, 0x0C, 0x1C, 0x00, 0x16, 0x00, 0x08, 0x00, 0x00, 0xA2, 0x00, 0x25, 0x0E], + 0x0094: [0x00, 0x25, 0x0E, 0x24, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0E, 0x24, 0x00, 0x33, 0x00], + 0x0095: [0x00, 0x25, 0x0E, 0x24, 0x00, 0x00, 0x00, 0x00, 0x25, 0x0E, 0x24, 0x00, 0x33, 0x00], + 0x0096: [0x00, 0x25, 0x0E, 0x24, 0x00, 0x33, 0x00, 0x00, 0x00, 0x00, 0x3D, 0x68, 0x11, 0x0C], + 0x0097: [0x68, 0x11, 0x0C, 0x1D, 0x00, 0x1C, 0x00, 0x00, 0x00, 0xD1, 0xD1, 0x00, 0x11, 0x0C], + 0x0098: [0x00, 0x11, 0x0C, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xD2, 0x01, 0x0B, 0x05], + 0x0099: [0x01, 0x0B, 0x05, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xDA, 0x00, 0x28, 0x0E], + 0x009A: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x28, 0x0E, 0x13], + 0x009B: [0x00, 0x28, 0x0E, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x7D, 0x00, 0x28, 0x0E, 0x13], + 0x009C: [0x00, 0x28, 0x0E, 0x13, 0x06, 0x00, 0x00, 0x00, 0x28, 0x0E, 0x13, 0x06, 0x00, 0x3B], + 0x009D: [0x00, 0x28, 0x0E, 0x13, 0x06, 0x00, 0x3B, 0x00, 0x00, 0x7B, 0x20, 0x13, 0x0B, 0x1C], + 0x009E: [0x20, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x00, 0xBE, 0xBE, 0x00, 0x13, 0x0B], + 0x009F: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x17, 0x00, 0x00, 0x12, 0x0C, 0x1D, 0x00, 0x00, 0x00], + 0x00A0: [0x00, 0x12, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x91, 0x00, 0x11, 0x0C], + 0x00A1: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00], + 0x00A2: [0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x93, 0x60, 0x19, 0x0D], + 0x00A3: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00], + 0x00A4: [0x60, 0x19, 0x0D, 0x17, 0x04, 0x25, 0x00, 0x00, 0x25, 0x0E, 0x24, 0x00, 0x07, 0x00], + 0x00A5: [0x00, 0x25, 0x0E, 0x24, 0x00, 0x07, 0x00, 0x00, 0x00, 0x00, 0x6C, 0x00, 0x25, 0x0E], + 0x00A6: [0x00, 0x25, 0x0E, 0x24, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4D, 0x00, 0x06, 0x05], + 0x00A7: [0x00, 0x06, 0x05, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x17, 0xC0, 0x0B, 0x05, 0x08], + 0x00A8: [0xC0, 0x0B, 0x05, 0x08, 0x00, 0x03, 0x00, 0xC0, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00A9: [0xC0, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x89, 0xC0, 0x0B, 0x05, 0x08], + 0x00AA: [0xC0, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0x00, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00], + 0x00AB: [0x00, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x64, 0xE0, 0x17, 0x0A], + 0x00AC: [0xE0, 0x17, 0x0A, 0x20, 0x00, 0x25, 0x00, 0x00, 0x13, 0x0B, 0x1C, 0x00, 0x27, 0x00], + 0x00AD: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x13, 0x0B], + 0x00AE: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x27, 0x00, 0x00, 0x00, 0x00, 0x8E, 0x00, 0x13, 0x0B], + 0x00AF: [0x00, 0x13, 0x0B, 0x1C, 0x00, 0x00, 0x00, 0x00, 0x26, 0x02, 0x21, 0x00, 0x05, 0x02], + 0x00B0: [0x00, 0x26, 0x02, 0x21, 0x00, 0x05, 0x02, 0x08, 0x00, 0x00, 0x40, 0xC0, 0x00, 0x11], + 0x00B1: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0x02, 0x00, 0xB2, 0xC0, 0x11, 0x0C, 0x1D], + 0x00B2: [0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x03, 0x0E, 0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x27, 0x00], + 0x00B3: [0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x27, 0x00, 0x00, 0x19, 0x0D, 0x17, 0x00, 0x00, 0x00], + 0x00B4: [0x00, 0x19, 0x0D, 0x17, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xC4, 0x01, 0x18, 0x0D], + 0x00B5: [0x01, 0x18, 0x0D, 0x25, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x04, 0x00, 0x18, 0x0D], + 0x00B6: [0x00, 0x18, 0x0D, 0x1E, 0x00, 0x04, 0x3C, 0x00, 0x00, 0x00, 0x15, 0x00, 0x0B, 0x05], + 0x00B7: [0x00, 0x18, 0x0D, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00], + 0x00B8: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x27, 0x00, 0xC0, 0x0B, 0x05, 0x08, 0x00, 0x00, 0x00], + 0x00B9: [0xC0, 0x0B, 0x05, 0x08, 0x00, 0x00, 0x00, 0x01, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00BA: [0x01, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0x40, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00], + 0x00BB: [0x40, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x00, 0x17, 0x0A, 0x1B, 0x00, 0x17, 0x00], + 0x00BC: [0x00, 0x17, 0x0A, 0x1B, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x45, 0x00, 0x13, 0x0B], + 0x00BD: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x16, 0x00, 0x00, 0x00, 0x4F, 0x9E, 0x00, 0x13, 0x0B], + 0x00BE: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x16, 0x00, 0x00, 0x00, 0x4F, 0x9E, 0x00, 0x13, 0x0B], + 0x00BF: [0x00, 0x13, 0x0B, 0x29, 0x00, 0x00, 0x00, 0x01, 0x00, 0x02, 0x27, 0x00, 0x02, 0x0F], + 0x00C0: [0x01, 0x00, 0x02, 0x27, 0x00, 0x02, 0x0F, 0x00, 0x00, 0x00, 0xB0, 0xD0, 0x00, 0x11], + 0x00C1: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x33, 0x00, 0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x27, 0x00], + 0x00C2: [0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x27, 0x00, 0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00], + 0x00C3: [0xC0, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x25, 0x00, 0x00, 0x00], + 0x00C4: [0x00, 0x18, 0x0D, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB4, 0x00, 0x18, 0x0D], + 0x00C5: [0x00, 0x18, 0x0D, 0x25, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x1E, 0x00, 0x33, 0x00], + 0x00C6: [0x00, 0x18, 0x0D, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00], + 0x00C7: [0x00, 0x18, 0x0D, 0x1E, 0x00, 0x33, 0x00, 0x00, 0x0B, 0x05, 0x09, 0x00, 0x15, 0x25], + 0x00C8: [0x00, 0x0B, 0x05, 0x09, 0x00, 0x15, 0x25, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00C9: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00], + 0x00CA: [0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00], + 0x00CB: [0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00], + 0x00CC: [0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00], + 0x00CD: [0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00, 0x00, 0x00, 0xDE, 0x01, 0x00, 0x02, 0x21], + 0x00CE: [0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00, 0x00, 0x00, 0xDE, 0x01, 0x00, 0x02, 0x21], + 0x00CF: [0x01, 0x00, 0x02, 0x21, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0x00, 0x11], + 0x00D0: [0x01, 0x00, 0x02, 0x21, 0x00, 0x0F, 0x00, 0x00, 0x00, 0x00, 0xC0, 0xE0, 0x00, 0x11], + 0x00D1: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x00, 0x00, 0x00, 0x00, 0xB1, 0x97, 0x00, 0x11, 0x0C], + 0x00D2: [0x00, 0x11, 0x0C, 0x1D, 0x00, 0x0A, 0x00, 0x00, 0x00, 0x00, 0x98, 0x00, 0x0B, 0x05], + 0x00D3: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x06, 0x00, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00D4: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x06, 0x00, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00D5: [0x00, 0x18, 0x0D, 0x25, 0x00, 0x00, 0x00, 0x00, 0x18, 0x0D, 0x1E, 0x00, 0x33, 0x00], + 0x00D6: [0x00, 0x18, 0x0D, 0x1E, 0x00, 0x00, 0x00, 0x20, 0x18, 0x0D, 0x26, 0x00, 0x00, 0x00], + 0x00D7: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x06, 0x00, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00D8: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x06, 0x00, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00D9: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00], + 0x00DA: [0x00, 0x0B, 0x05, 0x08, 0x00, 0x17, 0x00, 0x00, 0x00, 0x00, 0x99, 0xE0, 0x14, 0x0B], + 0x00DB: [0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00], + 0x00DC: [0xC0, 0x17, 0x0A, 0x1B, 0x00, 0x00, 0x00, 0x20, 0x13, 0x0B, 0x29, 0x00, 0x14, 0x00], + 0x00DD: [0xE0, 0x14, 0x0B, 0x16, 0x00, 0x25, 0x00, 0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00DE: [0xE0, 0x14, 0x0B, 0x16, 0x00, 0x25, 0x00, 0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00DF: [0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x26, 0x02], + 0x00E0: [0x00, 0x26, 0x02, 0x21, 0x00, 0x01, 0x2A, 0x00, 0x00, 0x00, 0xD0, 0xC0, 0x07, 0x06], + 0x00E1: [0xC0, 0x07, 0x06, 0x28, 0x00, 0x00, 0x00, 0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00E2: [0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0xC0, 0x20, 0x06, 0x09, 0x00, 0x00, 0x00], + 0x00E3: [0xC0, 0x20, 0x06, 0x09, 0x00, 0x00, 0x00, 0x01, 0x07, 0x14, 0x01, 0x00, 0x00, 0x00], + 0x00E4: [0x01, 0x07, 0x14, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00], + 0x00E5: [0x01, 0x07, 0x14, 0x01, 0x00, 0x00, 0x00, 0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00], + 0x00E6: [0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00, 0x20, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00E7: [0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00, 0x20, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00E8: [0x20, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8], + 0x00E9: [0x20, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xFA, 0x20, 0x07, 0x06], + 0x00EA: [0x20, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFA, 0xFA, 0x20, 0x07, 0x06], + 0x00EB: [0x20, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFB, 0xFB, 0x20, 0x20, 0x06], + 0x00EC: [0x20, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFD, 0xFD, 0x20, 0x20], + 0x00ED: [0x20, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFD, 0xFD, 0xFD, 0x20, 0x20], + 0x00EE: [0x20, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0xFE, 0x20, 0x20, 0x06, 0x13], + 0x00EF: [0x20, 0x20, 0x06, 0x13, 0x00, 0x02, 0x00, 0x08, 0x00, 0xFF, 0xDF, 0xFF, 0x00, 0x02], + 0x00F0: [0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00, 0x20, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00F1: [0x01, 0x07, 0x06, 0x01, 0x00, 0x00, 0x00, 0x20, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00F2: [0x00, 0x02, 0x03, 0x05, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x07], + 0x00F3: [0x00, 0x02, 0x03, 0x05, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x07], + 0x00F4: [0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00F5: [0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00F6: [0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE8, 0xE8, 0xE8], + 0x00F7: [0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE8, 0xE8, 0xE8], + 0x00F8: [0x00, 0x07, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xE8, 0xE8, 0xE8, 0xE8], + 0x00F9: [0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00FA: [0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEA, 0x00, 0x07, 0x06], + 0x00FB: [0x00, 0x07, 0x06, 0x19, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEB, 0x00, 0x20, 0x06], + 0x00FC: [0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xED, 0x00, 0x07], + 0x00FD: [0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xED, 0xED, 0x00, 0x07], + 0x00FE: [0x00, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00, 0xC0, 0x20, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x00FF: [0x00, 0x07, 0x06, 0x05, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xEF, 0x00, 0x05, 0x03], + 0x0100: [0x00, 0x05, 0x03, 0x28, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x03, 0x05, 0x00, 0x00, 0x00], + 0x0101: [0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x15, 0x03, 0x0D, 0x00, 0x00, 0x00], + 0x0102: [0x00, 0x15, 0x03, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x0F, 0x00, 0x00, 0x00], + 0x0103: [0x00, 0x05, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x15, 0x03, 0x0D, 0x00, 0x00, 0x00], + 0x0104: [0x01, 0x15, 0x03, 0x0D, 0x00, 0x00, 0x00, 0x00, 0x1C, 0x0F, 0x10, 0x00, 0x00, 0x00], + 0x0105: [0x00, 0x1C, 0x0F, 0x10, 0x00, 0x00, 0x00, 0x00, 0x1F, 0x03, 0x0F, 0x00, 0x00, 0x00], + 0x0106: [0x00, 0x1F, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00], + 0x0107: [0x00, 0x02, 0x03, 0x01, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0E, 0x00, 0x00, 0x00], + 0x0108: [0x00, 0x02, 0x03, 0x0E, 0x00, 0x00, 0x00, 0x01, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00], + 0x0109: [0x01, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00, 0x01, 0x07, 0x06, 0x10, 0x00, 0x00, 0x00], + 0x010A: [0x01, 0x07, 0x06, 0x10, 0x00, 0x00, 0x00, 0x80, 0x0A, 0x08, 0x08, 0x00, 0x00, 0x1A], + 0x010B: [0x80, 0x0A, 0x08, 0x08, 0x00, 0x00, 0x1A, 0x00, 0x27, 0x06, 0x08, 0x00, 0x03, 0x00], + 0x010C: [0x00, 0x27, 0x06, 0x08, 0x00, 0x03, 0x00, 0x00, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00], + 0x010D: [0x00, 0x0A, 0x08, 0x11, 0x00, 0x00, 0x00, 0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00], + 0x010E: [0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00, 0x00, 0x1E, 0x11, 0x05, 0x00, 0x00, 0x00], + 0x010F: [0x00, 0x1F, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00], + 0x0110: [0x00, 0x1F, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00], + 0x0111: [0x00, 0x1E, 0x11, 0x05, 0x00, 0x00, 0x00, 0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00], + 0x0112: [0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00, 0x00, 0x03, 0x10, 0x08, 0x00, 0x00, 0x00], + 0x0113: [0x00, 0x03, 0x10, 0x08, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00], + 0x0114: [0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x07, 0x00, 0x00, 0x00], + 0x0115: [0x00, 0x07, 0x06, 0x07, 0x00, 0x00, 0x00, 0x00, 0x22, 0x12, 0x07, 0x00, 0x00, 0x00], + 0x0116: [0x00, 0x22, 0x12, 0x07, 0x00, 0x00, 0x00, 0x00, 0x20, 0x14, 0x05, 0x00, 0x00, 0x00], + 0x0117: [0x00, 0x20, 0x14, 0x05, 0x00, 0x00, 0x00, 0xE0, 0x23, 0x0A, 0x0F, 0x00, 0x00, 0x00], + 0x0118: [0x00, 0x05, 0x03, 0x0F, 0x00, 0x00, 0x00, 0x01, 0x15, 0x03, 0x0D, 0x00, 0x00, 0x00], + 0x0119: [0xE0, 0x23, 0x0A, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x1D, 0x00, 0x1C, 0x0F], + 0x011A: [0x00, 0x1C, 0x0F, 0x05, 0x00, 0x00, 0x00, 0xC0, 0x07, 0x06, 0x08, 0x00, 0x00, 0x00], + 0x011B: [0xC0, 0x07, 0x06, 0x08, 0x00, 0x00, 0x00, 0x00, 0x23, 0x0A, 0x0F, 0x00, 0x00, 0x00], + 0x011C: [0x00, 0x1F, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x02, 0x03, 0x0F, 0x00, 0x00, 0x00], + 0x011D: [0x00, 0x23, 0x0A, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x19, 0x00, 0x20, 0x06], + 0x011E: [0x00, 0x20, 0x06, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00], + 0x011F: [0x00, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00, 0x00, 0x13, 0x06, 0x13, 0x00, 0x00, 0x00], + 0x0120: [0x00, 0x13, 0x06, 0x13, 0x00, 0x00, 0x00, 0x00, 0x07, 0x06, 0x28, 0x00, 0x03, 0x00], + 0x0121: [0x00, 0x1E, 0x11, 0x05, 0x00, 0x00, 0x00, 0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00], + 0x0122: [0x00, 0x1E, 0x11, 0x05, 0x00, 0x00, 0x00, 0x00, 0x07, 0x14, 0x05, 0x00, 0x00, 0x00], + 0x0123: [0x00, 0x07, 0x06, 0x28, 0x00, 0x03, 0x00, 0x00, 0x07, 0x06, 0x28, 0x00, 0x00, 0x00], + 0x0124: [0x00, 0x07, 0x06, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x0125: [0x00, 0x07, 0x06, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x0126: [0x00, 0x07, 0x06, 0x28, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x0127: [0x00, 0x20, 0x06, 0x2A, 0x00, 0x00, 0x00, 0x00, 0x05, 0x03, 0x05, 0x00, 0x00, 0x00], + 0x0128: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x0129: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x012A: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x012B: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x012C: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x012D: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF], + 0x012E: [0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF] +} + + +class RoomHeader: + def __init__(self, room_id, byte_array): + self.room_id = room_id + + # todo: the rest of the header + self.byte_0 = byte_array[0] # bg2, collision, lights out + self.sprite_sheet = byte_array[3] # sprite gfx # + self.effect = byte_array[4] + + def write_to_rom(self, rom, base_address): + room_offest = self.room_id*14 + rom.write_byte(base_address + room_offest + 0, self.byte_0) + rom.write_byte(base_address + room_offest + 3, self.sprite_sheet) + rom.write_byte(base_address + room_offest + 4, self.effect) + + +def init_room_headers(): + header_table = {} + for room_id, header_bytes in vanilla_headers.items(): + header_table[room_id] = RoomHeader(room_id, header_bytes) + return header_table + diff --git a/source/dungeon/RoomList.py b/source/dungeon/RoomList.py index 4b8c2a75..951f5b33 100644 --- a/source/dungeon/RoomList.py +++ b/source/dungeon/RoomList.py @@ -1,34 +1,505 @@ +try: + from fast_enum import FastEnum +except ImportError: + from enum import IntFlag as FastEnum + + from RoomData import DoorKind, Position from source.dungeon.RoomObject import RoomObject, DoorObject class Room: - def __init__(self, layout, layer1, layer2, doors): + def __init__(self, layout, layer1, layer2, doors, layer3=None): self.layout = layout self.layer1 = layer1 self.layer2 = layer2 + self.layer3 = layer3 self.doors = doors def write_to_rom(self, address, rom): + offset = 0 rom.write_bytes(address, self.layout) - address += 2 + offset += 2 for obj in self.layer1: - rom.write_bytes(address, obj.data) - address += 3 - rom.write_bytes(address, [0xFF, 0xFF]) - address += 2 + rom.write_bytes(address + offset, obj.data) + offset += 3 + rom.write_bytes(address + offset, [0xFF, 0xFF]) + offset += 2 for obj in self.layer2: - rom.write_bytes(address, obj.data) - address += 3 - rom.write_bytes(address, [0xFF, 0xFF, 0xF0, 0xFF]) - address += 4 + rom.write_bytes(address + offset, obj.data) + offset += 3 + rom.write_bytes(address + offset, [0xFF, 0xFF]) + offset += 2 + if self.layer3: + for obj in self.layer3: + rom.write_bytes(address + offset, obj.data) + offset += 3 + rom.write_bytes(address + offset, [0xF0, 0xFF]) + offset += 2 + door_start = offset for door in self.doors: - rom.write_bytes(address, door.get_bytes()) - address += 2 - rom.write_bytes(address, [0xFF, 0xFF]) - return address + 2 # where the data ended + rom.write_bytes(address + offset, door.get_bytes()) + offset += 2 + rom.write_bytes(address + offset, [0xFF, 0xFF]) + return door_start, offset + 2 # how many bytes were written + def find_all_pots(self): + pots = [] + pots.extend([x for x in self.layer1 if x.data[2] in {0xFA, 0xFB} and not x.dummy]) + pots.extend([x for x in self.layer2 if x.data[2] in {0xFA, 0xFB} and not x.dummy]) + if self.layer3: + pots.extend([x for x in self.layer3 if x.data[2] in {0xFA, 0xFB} and not x.dummy]) + return pots + + + +Room0006 = Room([0xE1, 0x00], + [RoomObject(0x1FA15C, [0x1B, 0xA3, 0xC8]), + RoomObject(0x1FA15F, [0x58, 0xA3, 0xC8]), + RoomObject(0x1FA162, [0x1B, 0xD8, 0xC8]), + RoomObject(0x1FA165, [0x58, 0xD8, 0xC8]), + RoomObject(0x1FA168, [0x17, 0x9F, 0x3F]), + RoomObject(0x1FA16B, [0x54, 0x9F, 0x3F]), + RoomObject(0x1FA16E, [0x17, 0xA3, 0x79]), + RoomObject(0x1FA171, [0x14, 0xE1, 0x79]), + RoomObject(0x1FA174, [0x17, 0xEB, 0x40]), + RoomObject(0x1FA177, [0x54, 0xEB, 0x40]), + RoomObject(0x1FA17A, [0x6B, 0xA3, 0x7A]), + RoomObject(0x1FA17D, [0x68, 0xE1, 0x7A]), + RoomObject(0x1FA180, [0x21, 0x90, 0xF8]), + RoomObject(0x1FA183, [0x51, 0x90, 0xF8]), + RoomObject(0x1FA186, [0x0C, 0xA5, 0x7F]), + RoomObject(0x1FA189, [0x6C, 0xA5, 0x80])], + [], [DoorObject(Position.SouthW, DoorKind.Trap)]) + + +Room0007 = Room([0x81, 0x1C], + [RoomObject(0x1FCAF0, [0x0A, 0x4E, 0x0D]), + RoomObject(0x1FCAF3, [0x0A, 0xAA, 0x0E]), + RoomObject(0x1FCAF6, [0x0B, 0x51, 0x61]), + RoomObject(0x1FCAF9, [0xC0, 0x2C, 0xA2]), + RoomObject(0x1FCAFC, [0xB0, 0x20, 0x0F]), + RoomObject(0x1FCAFF, [0xB0, 0x22, 0x62]), + RoomObject(0x1FCB02, [0xFE, 0xC1, 0x02]), + RoomObject(0x1FCB05, [0xC9, 0x38, 0x01]), + RoomObject(0x1FCB08, [0xFF, 0xA3, 0x82]), + RoomObject(0x1FCB0B, [0xBA, 0xE6, 0x10]), + RoomObject(0x1FCB0E, [0xE8, 0xAA, 0x62]), + RoomObject(0x1FCB11, [0xFF, 0x43, 0xB9]), + RoomObject(0x1FCB14, [0x53, 0x53, 0xE0]), + RoomObject(0x1FCB17, [0x91, 0x53, 0xE0]), + RoomObject(0x1FCB1A, [0x53, 0x91, 0xE0]), + RoomObject(0x1FCB1D, [0x91, 0x91, 0xE0]), + RoomObject(0x1FCB20, [0x3C, 0x6B, 0xC2]), + RoomObject(0x1FCB23, [0x3D, 0x9B, 0xC3]), + RoomObject(0x1FCB26, [0x54, 0xA6, 0xC3]), + RoomObject(0x1FCB29, [0x5C, 0xAA, 0xC3]), + RoomObject(0x1FCB2C, [0x68, 0xB1, 0xC3]), + RoomObject(0x1FCB2F, [0x75, 0xB0, 0xC3]), + RoomObject(0x1FCB32, [0x8F, 0xB1, 0xC3]), + RoomObject(0x1FCB35, [0x9B, 0xAA, 0xC3]), + RoomObject(0x1FCB38, [0xA6, 0xA0, 0xC3]), + RoomObject(0x1FCB3B, [0xAD, 0x98, 0xC3]), + RoomObject(0x1FCB3E, [0xB4, 0x6A, 0xC2]), + RoomObject(0x1FCB41, [0x51, 0x3D, 0xC3]), + RoomObject(0x1FCB44, [0x45, 0x49, 0xC3]), + RoomObject(0x1FCB47, [0x3D, 0x51, 0xC3]), + RoomObject(0x1FCB4A, [0x9C, 0x39, 0xC2]), + RoomObject(0x1FCB4D, [0xA1, 0x49, 0xC3]), + RoomObject(0x1FCB50, [0xAD, 0x51, 0xC3]), + RoomObject(0x1FCB53, [0x3A, 0x50, 0x8A]), + RoomObject(0x1FCB56, [0x38, 0x50, 0x22]), + RoomObject(0x1FCB59, [0x44, 0x44, 0x69]), + RoomObject(0x1FCB5C, [0x44, 0x44, 0x22]), + RoomObject(0x1FCB5F, [0x58, 0x13, 0x05]), + RoomObject(0x1FCB62, [0x60, 0x15, 0x55]), + RoomObject(0x1FCB65, [0x78, 0x10, 0x3A]), + RoomObject(0x1FCB68, [0x08, 0x5B, 0x65]), + RoomObject(0x1FCB6B, [0x0C, 0x61, 0x7F]), + RoomObject(0x1FCB6E, [0xC8, 0x39, 0x05]), + RoomObject(0x1FCB71, [0xE8, 0x5B, 0x66]), + RoomObject(0x1FCB74, [0xEC, 0x4A, 0x80]), + RoomObject(0x1FCB77, [0x58, 0xEB, 0x06]), + RoomObject(0x1FCB7A, [0x60, 0xED, 0x56]), + RoomObject(0x1FCB7D, [0x78, 0xEC, 0x3B]), + RoomObject(0x1FCB80, [0x50, 0x38, 0x69]), + RoomObject(0x1FCB83, [0x50, 0x38, 0x5F]), + RoomObject(0x1FCB86, [0xA8, 0x38, 0x69]), + RoomObject(0x1FCB89, [0xA8, 0x44, 0x22]), + RoomObject(0x1FCB8C, [0xB4, 0x44, 0x69]), + RoomObject(0x1FCB8F, [0xB4, 0x51, 0x22]), + RoomObject(0x1FCB92, [0xC6, 0x50, 0x8A]), + RoomObject(0x1FCB95, [0x3B, 0xC8, 0x22]), + RoomObject(0x1FCB98, [0x8B, 0xC8, 0x22]), + RoomObject(0x1FCB9B, [0x74, 0xBC, 0x69]), + RoomObject(0x1FCB9E, [0x88, 0xBC, 0x69]), + RoomObject(0x1FCBA1, [0x63, 0x3C, 0xC2]), + RoomObject(0x1FCBA4, [0x66, 0x4F, 0x29]), + RoomObject(0x1FCBA7, [0x64, 0x50, 0x6B]), + RoomObject(0x1FCBAA, [0x5C, 0x54, 0x2B]), + RoomObject(0x1FCBAD, [0x5C, 0x58, 0x6B]), + RoomObject(0x1FCBB0, [0x54, 0x5C, 0x2B]), + RoomObject(0x1FCBB3, [0x54, 0x60, 0x6B]), + RoomObject(0x1FCBB6, [0x4C, 0x64, 0x2B]), + RoomObject(0x1FCBB9, [0x4E, 0x6B, 0x6B]), + RoomObject(0x1FCBBC, [0x4C, 0x98, 0x2D]), + RoomObject(0x1FCBBF, [0x54, 0x9C, 0x6B]), + RoomObject(0x1FCBC2, [0x54, 0xA0, 0x2D]), + RoomObject(0x1FCBC5, [0x5C, 0xA4, 0x6B]), + RoomObject(0x1FCBC8, [0x5C, 0xA8, 0x2D]), + RoomObject(0x1FCBCB, [0x64, 0xAC, 0x6B]), + RoomObject(0x1FCBCE, [0x66, 0xB3, 0x2A]), + RoomObject(0x1FCBD1, [0x98, 0xAC, 0x6A]), + RoomObject(0x1FCBD4, [0x98, 0xA8, 0x2E]), + RoomObject(0x1FCBD7, [0xA0, 0xA4, 0x6A]), + RoomObject(0x1FCBDA, [0xA0, 0xA0, 0x2E]), + RoomObject(0x1FCBDD, [0xA8, 0x9C, 0x6A]), + RoomObject(0x1FCBE0, [0xA8, 0x98, 0x2E]), + RoomObject(0x1FCBE3, [0xB2, 0x6B, 0x6A]), + RoomObject(0x1FCBE6, [0xA8, 0x64, 0x2C]), + RoomObject(0x1FCBE9, [0xA8, 0x60, 0x6A]), + RoomObject(0x1FCBEC, [0xA0, 0x5C, 0x2C]), + RoomObject(0x1FCBEF, [0xA0, 0x58, 0x6A]), + RoomObject(0x1FCBF2, [0x98, 0x54, 0x2C]), + RoomObject(0x1FCBF5, [0x98, 0x50, 0x6A]), + RoomObject(0x1FCBF8, [0x68, 0x74, 0xC2]), + RoomObject(0x1FCBFB, [0x68, 0x71, 0x27]), + RoomObject(0x1FCBFE, [0x68, 0x77, 0x6A]), + RoomObject(0x1FCC01, [0x74, 0x77, 0x6B]), + RoomObject(0x1FCC04, [0x68, 0x85, 0x28]), + RoomObject(0x1FCC07, [0xFC, 0x31, 0x72]), + RoomObject(0x1FCC0A, [0x74, 0xAE, 0x04]), + RoomObject(0x1FCC0D, [0x71, 0xA0, 0xE0]), + RoomObject(0x1FCC10, [0x0A, 0x13, 0xA0]), + RoomObject(0x1FCC13, [0x0A, 0xBF, 0xA1]), + RoomObject(0x1FCC16, [0xBE, 0xF7, 0xA3]), + RoomObject(0x1FCC19, [0xC3, 0x11, 0xC0]), + RoomObject(0x1FCC1C, [0xD1, 0x31, 0x00])], + [], []) + +Room001C = Room([0xE1, 0x00], + [RoomObject(0x1FF74B, [0x2D, 0x32, 0xA4]), + RoomObject(0x1FF74E, [0xA9, 0x1E, 0xDC]), + RoomObject(0x1FF751, [0xA8, 0x91, 0x3A]), + RoomObject(0x1FF754, [0x88, 0xAD, 0x76]), + RoomObject(0x1FF757, [0xEC, 0xAD, 0x77]), + RoomObject(0x1FF75A, [0xA8, 0x50, 0x3D]), + RoomObject(0x1FF75D, [0xD0, 0x50, 0x3D]), + RoomObject(0x1FF760, [0x30, 0xA9, 0x3D]), + RoomObject(0x1FF763, [0x30, 0xC1, 0x3D]), + RoomObject(0x1FF766, [0xFC, 0x69, 0x38]), + RoomObject(0x1FF769, [0x97, 0x9F, 0xD1]), + RoomObject(0x1FF76C, [0xCD, 0x9F, 0xD1]), + RoomObject(0x1FF76F, [0x97, 0xDC, 0xD1]), + RoomObject(0x1FF772, [0xCD, 0xDC, 0xD1]), + RoomObject(0x1FF775, [0xBD, 0x32, 0xF9]), + RoomObject(0x1FF778, [0xB1, 0x22, 0xF9]), + RoomObject(0x1FF77B, [0xC9, 0x22, 0xF9]),], [], + [DoorObject(Position.InteriorE, DoorKind.TrapTriggerable), + DoorObject(Position.InteriorS, DoorKind.Trap), DoorObject(Position.InteriorW, DoorKind.Dashable)]) + + +Room0029 = Room([0xE5, 0x00], + [RoomObject(0x1FC188, [0x97, 0x9C, 0xDE]), + RoomObject(0x1FC18B, [0xB7, 0x9C, 0xDE]), + RoomObject(0x1FC18E, [0xD6, 0x9C, 0xDE]), + RoomObject(0x1FC191, [0x97, 0xE4, 0xDE]), + RoomObject(0x1FC194, [0xB7, 0xE4, 0xDE]), + RoomObject(0x1FC197, [0xD6, 0xE4, 0xDE]), + RoomObject(0x1FC19A, [0x94, 0xA7, 0xDE]), + RoomObject(0x1FC19D, [0x94, 0xC7, 0xDE]), + RoomObject(0x1FC1A0, [0xE4, 0xA7, 0xDE]), + RoomObject(0x1FC1A3, [0xE4, 0xC7, 0xDE])], + [RoomObject(0x1FC1A8, [0x03, 0x03, 0xCA]), + RoomObject(0x1FC1AB, [0x43, 0x03, 0xCA]), + RoomObject(0x1FC1AE, [0x83, 0x03, 0xCA]), + RoomObject(0x1FC1B1, [0xC3, 0x03, 0xCA]), + RoomObject(0x1FC1B4, [0x03, 0x43, 0xCA]), + RoomObject(0x1FC1B7, [0x43, 0x43, 0xCA]), + RoomObject(0x1FC1BA, [0x83, 0x43, 0xCA]), + RoomObject(0x1FC1BD, [0xC3, 0x43, 0xCA]), + RoomObject(0x1FC1C0, [0x03, 0x83, 0xCA]), + RoomObject(0x1FC1C3, [0x43, 0x83, 0xCA]), + RoomObject(0x1FC1C6, [0x83, 0x83, 0xCA]), + RoomObject(0x1FC1C9, [0xC3, 0x83, 0xCA]), + RoomObject(0x1FC1CC, [0x03, 0xC3, 0xCA]), + RoomObject(0x1FC1CF, [0x43, 0xC3, 0xCA]), + RoomObject(0x1FC1D2, [0x83, 0xC3, 0xCA]), + RoomObject(0x1FC1D5, [0xC3, 0xC3, 0xCA])], + [], layer3=[RoomObject(0x1FC1DA, [0x9F, 0xA7, 0xC6]), + RoomObject(0x1FC1DD, [0xD4, 0xA7, 0xC6]), + RoomObject(0x1FC1E0, [0xFE, 0xF9, 0xF4]), + RoomObject(0x1FC1E3, [0xFF, 0x1E, 0x74]), + RoomObject(0x1FC1E6, [0xFE, 0x5C, 0x74]), + RoomObject(0x1FC1E9, [0xFF, 0x9C, 0x74])]) + + +Room0033 = Room([0xE9, 0x00], [], [], [DoorObject(Position.SouthW, DoorKind.Trap)]) + + +Room004D = Room([0x82, 0x1C], + [RoomObject(0x1FFD43, [0x09, 0x34, 0x0D]), + RoomObject(0x1FFD46, [0x08, 0x3A, 0x61]), + RoomObject(0x1FFD49, [0x09, 0xC0, 0x0E]), + RoomObject(0x1FFD4C, [0x08, 0xC2, 0x61]), + RoomObject(0x1FFD4F, [0xD1, 0x10, 0x0F]), + RoomObject(0x1FFD52, [0xE8, 0x3A, 0x62]), + RoomObject(0x1FFD55, [0x5E, 0x1C, 0x03]), + RoomObject(0x1FFD58, [0x17, 0x49, 0x63]), + RoomObject(0x1FFD5B, [0xDF, 0x4B, 0x64]), + RoomObject(0x1FFD5E, [0xDC, 0xCA, 0x64]), + RoomObject(0x1FFD61, [0xFF, 0x7D, 0xCB]), + RoomObject(0x1FFD64, [0x9D, 0xDF, 0x04]), + RoomObject(0x1FFD67, [0x3B, 0x5B, 0xE0]), + RoomObject(0x1FFD6A, [0x7B, 0x5B, 0xE0]), + RoomObject(0x1FFD6D, [0xB8, 0x5B, 0xE0]), + RoomObject(0x1FFD70, [0x6A, 0xB1, 0xE0]), + RoomObject(0x1FFD73, [0x78, 0x54, 0xC2]), + RoomObject(0x1FFD76, [0x5B, 0x2A, 0xC2]), + RoomObject(0x1FFD79, [0x98, 0x2A, 0xC2]), + RoomObject(0x1FFD7C, [0x21, 0x4B, 0xC3]), + RoomObject(0x1FFD7F, [0x21, 0x7B, 0xC3]), + RoomObject(0x1FFD82, [0x21, 0xA1, 0xC3]), + RoomObject(0x1FFD85, [0x38, 0x7B, 0xC2]), + RoomObject(0x1FFD88, [0x48, 0x8A, 0xC2]), + RoomObject(0x1FFD8B, [0x3A, 0xAA, 0xC2]), + RoomObject(0x1FFD8E, [0x5B, 0x9C, 0xC2]), + RoomObject(0x1FFD91, [0xC9, 0x4B, 0xC3]), + RoomObject(0x1FFD94, [0xC9, 0x7B, 0xC3]), + RoomObject(0x1FFD97, [0xB8, 0x79, 0xC2]), + RoomObject(0x1FFD9A, [0xA8, 0x88, 0xC2]), + RoomObject(0x1FFD9D, [0x9B, 0x9B, 0xC2]), + RoomObject(0x1FFDA0, [0x9B, 0xD0, 0xC2]), + RoomObject(0x1FFDA3, [0xD0, 0xA3, 0xC2]), + RoomObject(0x1FFDA6, [0x78, 0x8C, 0xC2]), + RoomObject(0x1FFDA9, [0x15, 0x45, 0x22]), + RoomObject(0x1FFDAC, [0x59, 0x1F, 0x69]), + RoomObject(0x1FFDAF, [0xA5, 0x1F, 0x69]), + RoomObject(0x1FFDB2, [0xC9, 0x45, 0x22]), + RoomObject(0x1FFDB5, [0x68, 0xE4, 0x5E]), + RoomObject(0x1FFDB8, [0x15, 0xB9, 0x22]), + RoomObject(0x1FFDBB, [0x35, 0xB9, 0x69]), + RoomObject(0x1FFDBE, [0x37, 0xD9, 0x22]), + RoomObject(0x1FFDC1, [0x88, 0xD9, 0x22]), + RoomObject(0x1FFDC4, [0x98, 0xD9, 0x69]), + RoomObject(0x1FFDC7, [0x66, 0xCB, 0x2A]), + RoomObject(0x1FFDCA, [0x69, 0xC9, 0x04]), + RoomObject(0x1FFDCD, [0x79, 0xCB, 0xF9]), + RoomObject(0x1FFDD0, [0x8D, 0xBA, 0xF9]), + RoomObject(0x1FFDD3, [0x37, 0x57, 0x29]), + RoomObject(0x1FFDD6, [0x87, 0x57, 0x29]), + RoomObject(0x1FFDD9, [0x78, 0x5A, 0x6A]), + RoomObject(0x1FFDDC, [0x84, 0x5A, 0x6B]), + RoomObject(0x1FFDDF, [0x78, 0x65, 0x28]), + RoomObject(0x1FFDE2, [0x35, 0x5B, 0x6B]), + RoomObject(0x1FFDE5, [0x34, 0x7A, 0x2D]), + RoomObject(0x1FFDE8, [0x44, 0x7E, 0x6B]), + RoomObject(0x1FFDEB, [0x44, 0x8A, 0x2D]), + RoomObject(0x1FFDEE, [0x54, 0x8E, 0x6B]), + RoomObject(0x1FFDF1, [0x55, 0x9B, 0x2A]), + RoomObject(0x1FFDF4, [0x78, 0x8E, 0x6A]), + RoomObject(0x1FFDF7, [0x78, 0x89, 0x27]), + RoomObject(0x1FFDFA, [0x84, 0x8E, 0x6B]), + RoomObject(0x1FFDFD, [0x85, 0x9B, 0x2A]), + RoomObject(0x1FFE00, [0xA8, 0x8E, 0x6A]), + RoomObject(0x1FFE03, [0xA8, 0x8A, 0x2E]), + RoomObject(0x1FFE06, [0xB8, 0x7E, 0x6A]), + RoomObject(0x1FFE09, [0xB8, 0x7A, 0x2E]), + RoomObject(0x1FFE0C, [0xC9, 0x5B, 0x6A]), + RoomObject(0x1FFE0F, [0x66, 0xAF, 0x29]), + RoomObject(0x1FFE12, [0x65, 0xB1, 0x6B]), + RoomObject(0x1FFE15, [0x99, 0xB1, 0x6A]), + RoomObject(0x1FFE18, [0x38, 0x4B, 0x03]), + RoomObject(0x1FFE1B, [0xA8, 0x4B, 0x03]), + RoomObject(0x1FFE1E, [0x48, 0x13, 0x3A]), + RoomObject(0x1FFE21, [0x0C, 0x4A, 0x7F]), + RoomObject(0x1FFE24, [0xEC, 0x4A, 0x80]), + RoomObject(0x1FFE27, [0xFE, 0xE1, 0x39]), + RoomObject(0x1FFE2A, [0x09, 0x11, 0xA0]), + RoomObject(0x1FFE2D, [0xD5, 0x11, 0xA2]), + RoomObject(0x1FFE30, [0x09, 0xD5, 0xA1])], + [RoomObject(0x1FFE35, [0x5B, 0x19, 0xDB]), + RoomObject(0x1FFE38, [0x98, 0x19, 0xDB]), + RoomObject(0x1FFE3B, [0x11, 0x4B, 0xDB]), + RoomObject(0x1FFE3E, [0x11, 0x8A, 0xDB]), + RoomObject(0x1FFE41, [0x39, 0x48, 0xDB]), + RoomObject(0x1FFE44, [0xA9, 0x48, 0xDB]), + RoomObject(0x1FFE47, [0xD9, 0x4B, 0xDB]), + RoomObject(0x1FFE4A, [0xD9, 0x8B, 0xDB]), + RoomObject(0x1FFE4D, [0x6A, 0xC8, 0xDB]), + RoomObject(0x1FFE50, [0x9B, 0xCA, 0xDB]), + RoomObject(0x1FFE53, [0xD9, 0xCA, 0xDB])], + [DoorObject(Position.NorthW, DoorKind.SmallKey), DoorObject(Position.WestS, DoorKind.Normal)]) + +Room005A = Room([0xE9, 0x00], + [RoomObject(0x1FA7CD, [0xA8, 0xA8, 0xDE]), + RoomObject(0x1FA7D0, [0xB0, 0xA0, 0xDE]), + RoomObject(0x1FA7D3, [0xB8, 0xA8, 0xDE]), + RoomObject(0x1FA7D6, [0xC0, 0xA0, 0xDE]), + RoomObject(0x1FA7D9, [0xC8, 0xA8, 0xDE]), + RoomObject(0x1FA7DC, [0xD0, 0xA0, 0xDE])], + [], [DoorObject(Position.SouthE, DoorKind.Trap)]) + +Room006C = Room([0xE2, 0x00], + [RoomObject(0x1FFA58, [0x17, 0x9F, 0xE8]), + RoomObject(0x1FFA5B, [0x4D, 0x9F, 0xE8]), + RoomObject(0x1FFA5E, [0x17, 0xDC, 0xE8]), + RoomObject(0x1FFA61, [0x4D, 0xDC, 0xE8]), + RoomObject(0x1FFA64, [0x18, 0xE1, 0xFE]), + RoomObject(0x1FFA67, [0x88, 0xAD, 0x76]), + RoomObject(0x1FFA6A, [0x99, 0xBC, 0x33]), + RoomObject(0x1FFA6D, [0x9B, 0xBB, 0x34]), + RoomObject(0x1FFA70, [0x9B, 0xCF, 0x34]), + RoomObject(0x1FFA73, [0xD8, 0xB8, 0x34]), + RoomObject(0x1FFA76, [0xD8, 0xCC, 0x34]), + RoomObject(0x1FFA79, [0xAF, 0xAA, 0xFE]), + RoomObject(0x1FFA7C, [0xC7, 0xAA, 0xFE]), + RoomObject(0x1FFA7F, [0xAF, 0xD2, 0xFE]), + RoomObject(0x1FFA82, [0xC7, 0xD2, 0xFE]), + RoomObject(0x1FFA85, [0x28, 0x11, 0x3A]), + RoomObject(0x1FFA88, [0x28, 0x91, 0x3A]), + RoomObject(0x1FFA8B, [0xFC, 0xE1, 0x38]), + RoomObject(0x1FFA8E, [0x2B, 0x33, 0xFA]), + RoomObject(0x1FFA91, [0x53, 0x33, 0xFA]), + RoomObject(0x1FFA94, [0x2B, 0x53, 0xFA]), + RoomObject(0x1FFA97, [0x53, 0x53, 0xFA])], [], + [DoorObject(Position.InteriorS, DoorKind.Trap2), DoorObject(Position.InteriorW, DoorKind.Trap), + DoorObject(Position.EastS, DoorKind.Normal)]) + +Room0090 = Room([0xE1, 0x00], + [RoomObject(0x1FBAA0, [0x28, 0xEC, 0x56]), + RoomObject(0x1FBAA3, [0x48, 0xEC, 0x56]), + RoomObject(0x1FBAA6, [0x1B, 0xA2, 0xFF])], + [RoomObject(0x1FBAAB, [0x16, 0x9C, 0xFE])], [DoorObject(Position.SouthW, DoorKind.Trap)]) + +Room00A4 = Room([0xE1, 0x00], + [RoomObject(0x1FE702, [0xFC, 0x08, 0x00]), + RoomObject(0x1FE705, [0x13, 0x80, 0x01]), + RoomObject(0x1FE708, [0xFD, 0xC8, 0x02]), + RoomObject(0x1FE70B, [0x02, 0x93, 0x61]), + RoomObject(0x1FE70E, [0xFC, 0x0E, 0x81]), + RoomObject(0x1FE711, [0x13, 0xE8, 0x02]), + RoomObject(0x1FE714, [0xFD, 0xCE, 0x83]), + RoomObject(0x1FE717, [0x72, 0x93, 0x62]), + RoomObject(0x1FE71A, [0x13, 0x93, 0xC4]), + RoomObject(0x1FE71D, [0x51, 0x93, 0xC4]), + RoomObject(0x1FE720, [0x51, 0xC9, 0xC4]), + RoomObject(0x1FE723, [0x10, 0xC9, 0xC4]), + RoomObject(0x1FE726, [0x0E, 0x8D, 0xDE]), + RoomObject(0x1FE729, [0x0D, 0x9C, 0xDE]), + RoomObject(0x1FE72C, [0x0C, 0xA5, 0xDE]), + RoomObject(0x1FE72F, [0x5E, 0x8C, 0xDE]), + RoomObject(0x1FE732, [0x65, 0x94, 0xDE]), + RoomObject(0x1FE735, [0x6C, 0x9C, 0xDE])], + [RoomObject(0x1FE73A, [0x2E, 0x98, 0xFF])], [DoorObject(Position.SouthW, DoorKind.Trap)]) + +Room00AC = Room([0xE9, 0x00], + [RoomObject(0x1FD9B1, [0x88, 0xA4, 0x0D]), + RoomObject(0x1FD9B4, [0x88, 0xD0, 0x0E]), + RoomObject(0x1FD9B7, [0xE0, 0x90, 0x0F]), + RoomObject(0x1FD9BA, [0xE0, 0xE4, 0x10]), + RoomObject(0x1FD9BD, [0x89, 0xAB, 0x61]), + RoomObject(0x1FD9C0, [0xE9, 0xAB, 0x62]), + RoomObject(0x1FD9C3, [0x88, 0x91, 0xA0]), + RoomObject(0x1FD9C6, [0x88, 0xE5, 0xA1]), + RoomObject(0x1FD9C9, [0xE4, 0x91, 0xA2]), + RoomObject(0x1FD9CC, [0xE4, 0xF5, 0xA3])], + [RoomObject(0x1FD9D1, [0xB1, 0xA8, 0xFF])], [DoorObject(Position.SouthE, DoorKind.Trap)]) + +Room00C8 = Room([0xE1, 0x00], + [RoomObject(0x0A9587, [0x98, 0x92, 0x3A]), + RoomObject(0x0A958A, [0x88, 0xAA, 0x65]), + RoomObject(0x0A958D, [0xE8, 0xAA, 0x66])], [], [DoorObject(Position.SouthE, DoorKind.Trap)]) + +Room00DE = Room([0xE4, 0x00], [], [RoomObject(0x1FCAE5, [0xAD, 0x21, 0xF9])], []) + +Room010C = Room([0xE0, 0x08], + [RoomObject(0x03F25D, [0xFC, 0x62, 0x00]), + RoomObject(0x03F260, [0x19, 0x33, 0x61]), + RoomObject(0x03F263, [0xFC, 0x66, 0x81]), + RoomObject(0x03F266, [0x29, 0x22, 0x01]), + RoomObject(0x03F269, [0xFD, 0x62, 0x02]), + RoomObject(0x03F26C, [0x59, 0x33, 0x62]), + RoomObject(0x03F26F, [0xFD, 0x66, 0x83]), + RoomObject(0x03F272, [0xFC, 0x89, 0x00]), + RoomObject(0x03F275, [0x22, 0xA1, 0x61]), + RoomObject(0x03F278, [0xFC, 0x8E, 0x81]), + RoomObject(0x03F27B, [0x90, 0xCA, 0x0E]), + RoomObject(0x03F27E, [0xD0, 0xE6, 0x10]), + RoomObject(0x03F281, [0xFE, 0x41, 0x00]), + RoomObject(0x03F284, [0x93, 0x23, 0x61]), + RoomObject(0x03F287, [0x92, 0x98, 0x61]), + RoomObject(0x03F28A, [0xFF, 0x81, 0x02]), + RoomObject(0x03F28D, [0xE3, 0x23, 0x62]), + RoomObject(0x03F290, [0xE2, 0x98, 0x62]), + RoomObject(0x03F293, [0xFE, 0x71, 0xC8]), + RoomObject(0x03F296, [0xAD, 0x1C, 0x03]), + RoomObject(0x03F299, [0xFF, 0x51, 0xCA]), + RoomObject(0x03F29C, [0x9D, 0x2D, 0x63]), + RoomObject(0x03F29F, [0xFE, 0x75, 0x89]), + RoomObject(0x03F2A2, [0xAD, 0x58, 0x04]), + RoomObject(0x03F2A5, [0xD5, 0x2D, 0x64]), + RoomObject(0x03F2A8, [0xFF, 0x55, 0x8B]), + RoomObject(0x03F2AB, [0xFE, 0x78, 0x08]), + RoomObject(0x03F2AE, [0xAD, 0x80, 0x03]), + RoomObject(0x03F2B1, [0xFF, 0x58, 0x0A]), + RoomObject(0x03F2B4, [0x9C, 0x91, 0x63]), + RoomObject(0x03F2B7, [0xFE, 0x7A, 0x09]), + RoomObject(0x03F2BA, [0xAD, 0xA0, 0x04]), + RoomObject(0x03F2BD, [0xFF, 0x5A, 0x0B]), + RoomObject(0x03F2C0, [0xD4, 0x91, 0x64]), + RoomObject(0x03F2C3, [0x2C, 0x2E, 0xDC]), + RoomObject(0x03F2C6, [0x3D, 0x3E, 0xF9]), + RoomObject(0x03F2C9, [0x32, 0xA9, 0xF9]), + RoomObject(0x03F2CC, [0x3A, 0xA9, 0xF9]), + RoomObject(0x03F2CF, [0x42, 0xA9, 0xF9]), + RoomObject(0x03F2D2, [0x4A, 0xA9, 0xF9]), + RoomObject(0x03F2D5, [0x57, 0x9E, 0x69]), + RoomObject(0x03F2D8, [0x54, 0xDC, 0x69]), + RoomObject(0x03F2DB, [0x38, 0xC8, 0x89]), + RoomObject(0x03F2DE, [0x58, 0x9C, 0x89]), + RoomObject(0x03F2E1, [0x58, 0xA8, 0x89]), + RoomObject(0x03F2E4, [0x58, 0xB4, 0x89]), + RoomObject(0x03F2E7, [0x58, 0xC0, 0x89]), + RoomObject(0x03F2EA, [0x58, 0xCC, 0x89]), + RoomObject(0x03F2ED, [0x58, 0xD8, 0x89]), + RoomObject(0x03F2F0, [0x58, 0xE4, 0x89]), + RoomObject(0x03F2F3, [0xAA, 0x2E, 0xC8]), + RoomObject(0x03F2F6, [0xAA, 0x29, 0x3F]), + RoomObject(0x03F2F9, [0xAA, 0x2E, 0x79]), + RoomObject(0x03F2FC, [0xAA, 0x59, 0x40]), + RoomObject(0x03F2FF, [0xD6, 0x2E, 0x7A]), + RoomObject(0x03F302, [0xAA, 0x90, 0xC8]), + RoomObject(0x03F305, [0xAA, 0x8D, 0x3F]), + RoomObject(0x03F308, [0xA8, 0x93, 0x79]), + RoomObject(0x03F30B, [0xAA, 0xA1, 0x40]), + RoomObject(0x03F30E, [0xD4, 0x93, 0x7A]), + RoomObject(0x03F311, [0xB9, 0x5B, 0xF9]), + RoomObject(0x03F314, [0xB9, 0xA3, 0xF9]), + RoomObject(0x03F317, [0x9C, 0x68, 0x22]), + RoomObject(0x03F31A, [0x9C, 0x6A, 0x69]), + RoomObject(0x03F31D, [0x9C, 0x7C, 0x22]), + RoomObject(0x03F320, [0xD4, 0x7C, 0x22]), + RoomObject(0x03F323, [0x9C, 0xB0, 0x22]), + RoomObject(0x03F326, [0xD4, 0xB0, 0x22]), + RoomObject(0x03F329, [0xB3, 0x73, 0xFA]), + RoomObject(0x03F32C, [0xCD, 0x68, 0xDD]), + RoomObject(0x03F32F, [0xB8, 0xC0, 0xDD]), + RoomObject(0x03F332, [0x08, 0x00, 0x60]), + RoomObject(0x03F335, [0x10, 0x00, 0x60]), + RoomObject(0x03F338, [0x1B, 0x10, 0xC0]), + RoomObject(0x03F33B, [0x59, 0x10, 0xC0]), + RoomObject(0x03F33E, [0x68, 0x23, 0xC0]), + RoomObject(0x03F341, [0x68, 0x61, 0xC0]), + RoomObject(0x03F344, [0x1B, 0x91, 0x60]), + RoomObject(0x03F347, [0x88, 0x10, 0x60]), + RoomObject(0x03F34A, [0xF0, 0x10, 0x60]), + RoomObject(0x03F34D, [0x90, 0xDF, 0xA1]), + RoomObject(0x03F350, [0xD4, 0xF7, 0xA3])], [], + [DoorObject(Position.InteriorW, DoorKind.TrapTriggerable), + DoorObject(Position.SouthW, DoorKind.CaveEntrance), DoorObject(Position.SouthE, DoorKind.CaveEntrance)] + ) Room0127 = Room([0xE1, 0x00], [RoomObject(0x0AB600, [0xFE, 0x89, 0x00]), @@ -44,15 +515,35 @@ Room0127 = Room([0xE1, 0x00], RoomObject(0x0AB61E, [0x43, 0xCB, 0xFA]), RoomObject(0x0AB621, [0x4B, 0xCB, 0xFA]), RoomObject(0x0AB624, [0xBF, 0x94, 0xF9]), - RoomObject(0x0AB627, [0xB3, 0xB3, 0xFA]), - RoomObject(0x0AB62A, [0xCB, 0xB3, 0xFA]), + RoomObject(0x0AB627, [0xB3, 0xB3, 0xFA], True), + RoomObject(0x0AB62A, [0xCB, 0xB3, 0xFA], True), RoomObject(0x0AB62D, [0xAD, 0xC8, 0xDF]), RoomObject(0x0AB630, [0xC4, 0xC8, 0xDF]), - RoomObject(0x0AB633, [0xB3, 0xE3, 0xFA]), - RoomObject(0x0AB636, [0xCB, 0xE3, 0xFA]), + RoomObject(0x0AB633, [0xB3, 0xE3, 0xFA], True), + RoomObject(0x0AB636, [0xCB, 0xE3, 0xFA], True), RoomObject(0x0AB639, [0x81, 0x93, 0xC0]), RoomObject(0x0AB63C, [0x81, 0xD2, 0xC0]), RoomObject(0x0AB63F, [0xE1, 0x93, 0xC0]), RoomObject(0x0AB642, [0xE1, 0xD2, 0xC0])], [], [DoorObject(Position.SouthW, DoorKind.CaveEntrance), DoorObject(Position.SouthE, DoorKind.CaveEntrance)]) + +# boss room id, room data, shell x, shell y, clear layer 2 +boss_rooms = { + 'Eastern Palace': (0xc8, Room00C8, 0x2B, 0x28, False), + 'Desert Palace': (0x33, Room0033, 0x0B, 0x28, False), + 'Tower of Hera': (7, Room0007, 0x18, 0x16, False), + 'Palace of Darkness': (0x5A, Room005A, 0x2B, 0x28, False), + 'Swamp Palace': (6, Room0006, 0x0B, 0x28, False), + 'Skull Woods': (0x29, Room0029, 0x2B, 0x28, False), + 'Thieves Town': (0xac, Room00AC, 0x2B, 0x28, True), + 'Ice Palace': (0xde, Room00DE, 0x2B, 0x08, True), + 'Misery Mire': (0x90, Room0090, 0x0B, 0x28, True), + 'Turtle Rock': (0xa4, Room00A4, 0x0B, 0x28, True), +} + +gt_boss_room = { + 'bottom': (0x1C, Room001C, 0x2B, 0x28, False), + 'middle': (0x6C, Room006C, 0x0B, 0x28, False), + 'top': (0x4D, Room004D, 0x18, 0x16, False), +} diff --git a/source/dungeon/RoomObject.py b/source/dungeon/RoomObject.py index 359231e0..a541c926 100644 --- a/source/dungeon/RoomObject.py +++ b/source/dungeon/RoomObject.py @@ -8,9 +8,10 @@ Shuffled_Pot = (0xFB, 0, 0) # formerly weird pot, or black diagonal thing class RoomObject: - def __init__(self, address, data): + def __init__(self, address, data, dummy=False): self.address = address self.data = data + self.dummy = dummy # some room objects are dummies, unreachable def change_type(self, new_type): type_id, datum_a, datum_b = new_type @@ -22,6 +23,17 @@ class RoomObject: def write_to_rom(self, rom): rom.write_bytes(snes_to_pc(self.address), self.data) + # subtype 3 only? + def matches_oid(self, oid): + my_oid = (self.data[2] << 4) | ((self.data[1] & 3) << 2) | (self.data[0] & 3) + return my_oid == oid + + @staticmethod + def subtype3_factory(x, y, type_id): + return RoomObject(None, [((x << 2) & 0xFC) | (type_id & 0x3), + ((y << 2) & 0xFC) | ((type_id >> 2) & 0x3), + 0xF0 | ((type_id >> 4) & 0xF)]) + class DoorObject: diff --git a/source/dungeon/__init__.py b/source/dungeon/__init__.py new file mode 100644 index 00000000..724252e9 --- /dev/null +++ b/source/dungeon/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make "source" package diff --git a/source/enemizer/Bossmizer.py b/source/enemizer/Bossmizer.py new file mode 100644 index 00000000..398cba89 --- /dev/null +++ b/source/enemizer/Bossmizer.py @@ -0,0 +1,222 @@ +import RaceRandom as random +from Utils import snes_to_pc + +from source.dungeon.EnemyList import EnemySprite, SpriteType, Sprite +from source.dungeon.RoomList import boss_rooms, gt_boss_room, Room0006 +from source.dungeon.RoomObject import RoomObject +from source.enemizer.SpriteSheets import required_boss_sheets + + +def get_dungeon_boss_room(dungeon_name, level): + if level is None: + return boss_rooms[dungeon_name] + return gt_boss_room[level] + + +def get_dungeon_boss_default(dungeon_name, level): + if level is None: + return boss_defaults[dungeon_name] + return gt_boss_defaults[level] + + +def add_shell_to_boss_room(data_tables, dungeon_name, level, shell_id): + room_id, room, shell_x, shell_y, clear_layer_2 = get_dungeon_boss_room(dungeon_name, level) + if room_id in data_tables.room_list: + room = data_tables.room_list[room_id] + else: + data_tables.room_list[room_id] = room + room.layout[0] = 0xF0 + if clear_layer_2: + room.layer2.clear() + y_offset = 0 if shell_id == 0xF95 else -2 + room.layer2.append(RoomObject.subtype3_factory(shell_x, shell_y + y_offset, shell_id)) + + +def remove_shell_from_boss_room(data_tables, dungeon_name, level, shell_id): + room_id, room, shell_x, shell_y, clear_layer_2 = get_dungeon_boss_room(dungeon_name, level) + if room_id in data_tables.room_list: + room = data_tables.room_list[room_id] + else: + data_tables.room_list[room_id] = room + room.layer2[:] = [obj for obj in room.layer2 if not obj.matches_oid(shell_id)] + + +def remove_water_tiles(data_tables): + room = Room0006 + if 0x6 in data_tables.room_list: + room = data_tables.room_list[0x6] + else: + data_tables.room_list[0x6] = room + room.layer1.clear() + + +def create_sprite(super_tile, kind, sub_type, layer, tile_x, tile_y): + return Sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, None, False, None) + + +def add_armos_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x05)) + sprite_list.insert(1, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x05)) + sprite_list.insert(2, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x0a, 0x05)) + sprite_list.insert(3, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x0a, 0x08)) + sprite_list.insert(4, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x07, 0x08)) + sprite_list.insert(5, create_sprite(room_id, EnemySprite.ArmosKnight, 0x00, 0, 0x04, 0x08)) + sprite_list.insert(6, create_sprite(room_id, 0x19, SpriteType.Overlord, 0, 0x07, 0x08)) + + +def add_lanmolas_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.Lanmolas, 0x00, 0, 0x06, 0x07)) + sprite_list.insert(1, create_sprite(room_id, EnemySprite.Lanmolas, 0x00, 0, 0x09, 0x07)) + sprite_list.insert(2, create_sprite(room_id, EnemySprite.Lanmolas, 0x00, 0, 0x07, 0x09)) + + +def add_moldorm_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.Moldorm, 0x00, 0, 0x09, 0x09)) + + +def add_helmasaur_king_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.HelmasaurKing, 0x00, 0, 0x07, 0x06)) + + +def add_arrghus_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.Arrghus, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(1, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(2, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(3, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(4, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(5, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(6, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(7, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(8, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(9, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(10, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(11, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(12, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + sprite_list.insert(13, create_sprite(room_id, EnemySprite.Arrghi, 0x00, 0, 0x07, 0x07)) + + +def add_mothula_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.Mothula, 0x00, 0, 0x08, 0x06)) + + +def add_blind_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.Blind, 0x00, 0, 0x09, 0x05)) + + +def add_kholdstare_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.KholdstareShell, 0x00, 0, 0x07, 0x05)) + sprite_list.insert(1, create_sprite(room_id, EnemySprite.FallingIce, 0x00, 0, 0x07, 0x05)) + sprite_list.insert(2, create_sprite(room_id, EnemySprite.Kholdstare, 0x00, 0, 0x07, 0x05)) + + +def add_vitreous_to_list(sprite_list, room_id): + sprite_list.clear() # vitreous does not play nice which other sprites on the tile, just kill them + sprite_list.append(create_sprite(room_id, EnemySprite.Vitreous, 0x00, 0, 0x07, 0x05)) + + +def add_trinexx_to_list(sprite_list, room_id): + sprite_list.insert(0, create_sprite(room_id, EnemySprite.TrinexxRockHead, 0x00, 0, 0x07, 0x05)) + sprite_list.insert(1, create_sprite(room_id, EnemySprite.TrinexxFireHead, 0x00, 0, 0x07, 0x05)) + sprite_list.insert(2, create_sprite(room_id, EnemySprite.TrinexxIceHead, 0x00, 0, 0x07, 0x05)) + + +def boss_adjust(world, player): + data_tables = world.data_tables[player] + for dungeon in world.get_dungeons(player): + for level, boss in dungeon.bosses.items(): + if not boss or boss.name in ['Agahnim', 'Agahnim2']: + continue + default_boss = get_dungeon_boss_default(dungeon.name, level) + room_data = get_dungeon_boss_room(dungeon.name, level) + room_id = room_data[0] + if default_boss != boss.name: + sprite_list = data_tables.uw_enemy_table.room_map[room_id] + data = boss_room_remove_data[room_id] + del sprite_list[:data] + add_func, sprite_type = boss_addition_table[boss.name] + add_func(sprite_list, room_id) + if len(sprite_list) > 15: + del sprite_list[15:] + data_tables.room_headers[room_id].sprite_sheet = required_boss_sheets[sprite_type] + + +def boss_writes(world, player, rom): + rom.write_byte(snes_to_pc(0x368107), 1) # centralize drops + eye_number = random.randint(0, 8) # randomize moldorm eyes (var + 1) + rom.write_byte(snes_to_pc(0x368102), eye_number) # enemizer flag + rom.write_byte(snes_to_pc(0x1DDBB3), eye_number) # loop variable + data_tables = world.data_tables[player] + arrghus_can_swim = True + water_tiles_on = True + for dungeon in world.get_dungeons(player): + for level, boss in dungeon.bosses.items(): + if not boss or boss.name in ['Agahnim', 'Agahnim2']: + continue + room_data = get_dungeon_boss_room(dungeon.name, level) + room_id = room_data[0] + # room changes + if boss.name == 'Arrghus' and (dungeon.name != 'Swamp Palace' or level is not None): + rom.write_byte(snes_to_pc(0x0DB6BE), 0) # arrghus can stand on ground + arrghus_can_swim = False + if boss.name != 'Arrghus' and dungeon.name == 'Swamp Palace' and level is None: + remove_water_tiles(data_tables) + water_tiles_on = False + if boss.name == 'Trinexx' and (dungeon.name != 'Turtle Rock' or level is not None): + add_shell_to_boss_room(data_tables, dungeon.name, level, 0xFF2) + data_tables.room_headers[room_id].byte_0 = 0x60 + data_tables.room_headers[room_id].effect = 4 + # $2E, $98, $FF (original shell) + if boss.name == 'Kholdstare' and (dungeon.name != 'Ice Palace' or level is not None): + add_shell_to_boss_room(data_tables, dungeon.name, level, 0xF95) + data_tables.room_headers[room_id].byte_0 = 0xE0 + data_tables.room_headers[room_id].effect = 1 + if boss.name != 'Trinexx' and dungeon.name == 'Turtle Rock' and level is None: + remove_shell_from_boss_room(data_tables, dungeon.name, level, 0xFF2) + # disable trinexx ice breath with No-ops + rom.write_bytes(snes_to_pc(0x09B37E), [0xEA, 0xEA, 0xEA, 0xEA]) + if boss.name != 'Kholdstare' and dungeon.name == 'Ice Palace' and level is None: + remove_shell_from_boss_room(data_tables, dungeon.name, level, 0xF95) + if boss.name != 'Blind' and dungeon.name == 'Thieves Town' and level is None: + rom.write_byte(snes_to_pc(0x368101), 1) # set blind boss door flag + # maiden is deleted + del data_tables.uw_enemy_table.room_map[0x45][0] + if not arrghus_can_swim and water_tiles_on: + remove_water_tiles(data_tables) + + +boss_defaults = { + 'Eastern Palace': 'Armos Knights', + 'Desert Palace': 'Lanmolas', + 'Tower of Hera': 'Moldorm', + 'Palace of Darkness': 'Helmasaur King', + 'Swamp Palace': 'Arrghus', + 'Skull Woods': 'Mothula', + 'Thieves Town': 'Blind', + 'Ice Palace': 'Kholdstare', + 'Misery Mire': 'Vitreous', + 'Turtle Rock': 'Trinexx', +} + +gt_boss_defaults = { + 'bottom': 'Armos Knights', + 'middle': 'Lanmolas', + 'top': 'Moldorm', +} + +boss_room_remove_data = { + 6: 14, 7: 1, 0x1c: 7, 0x29: 1, 0x33: 3, 0x4d: 1, 0x5a: 1, + 0x6c: 3, 0x90: 1, 0xa4: 2, 0xac: 1, 0xc8: 7, 0xde: 3 +} + +boss_addition_table = { + 'Armos Knights': (add_armos_to_list, EnemySprite.ArmosKnight), + 'Lanmolas': (add_lanmolas_to_list, EnemySprite.Lanmolas), + 'Moldorm': (add_moldorm_to_list, EnemySprite.Moldorm), + 'Helmasaur King': (add_helmasaur_king_to_list, EnemySprite.HelmasaurKing), + 'Arrghus': (add_arrghus_to_list, EnemySprite.Arrghus), + 'Mothula': (add_mothula_to_list, EnemySprite.Mothula), + 'Blind': (add_blind_to_list, EnemySprite.Blind), + 'Kholdstare': (add_kholdstare_to_list, EnemySprite.Kholdstare), + 'Vitreous': (add_vitreous_to_list, EnemySprite.Vitreous), + 'Trinexx': (add_trinexx_to_list, EnemySprite.TrinexxRockHead) +} diff --git a/source/enemizer/DamageTables.py b/source/enemizer/DamageTables.py new file mode 100644 index 00000000..77e98c21 --- /dev/null +++ b/source/enemizer/DamageTables.py @@ -0,0 +1,8 @@ +from Utils import load_cached_yaml + + +class DamageTable: + def __init__(self): + self.damage_table = load_cached_yaml(['source', 'enemizer', 'damage_table.yaml']) + self.enemy_damage = load_cached_yaml(['source', 'enemizer', 'enemy_damage_table.yaml']) + diff --git a/source/enemizer/Enemizer.py b/source/enemizer/Enemizer.py new file mode 100644 index 00000000..b4715268 --- /dev/null +++ b/source/enemizer/Enemizer.py @@ -0,0 +1,539 @@ +import RaceRandom as random +from Utils import snes_to_pc + +from source.dungeon.EnemyList import SpriteType, EnemySprite, sprite_translation +from source.dungeon.RoomList import Room010C +from source.enemizer.SpriteSheets import sub_group_choices +from source.enemizer.SpriteSheets import randomize_underworld_sprite_sheets, randomize_overworld_sprite_sheets +from source.enemizer.TilePattern import tile_patterns + +shutter_sprites = { + 0xb8: {0, 1, 2, 3, 4, 5}, 0xb: {4, 5, 6, 7, 8, 9}, 0x1b: {3, 4, 5}, 0x4b: {0, 3, 4}, 0x4: {9, 13, 14}, + 0x24: {3, 4, 5, 6}, # not sure about 6 - bunny beam under pot + 0x28: {0, 1, 2, 3, 4}, 0xe: {0, 1, 2, 3}, 0x2e: {0, 1, 2, 3, 4, 5}, 0x3e: {1, 2}, 0x6e: {0, 1, 2, 3, 4}, + 0x31: {7, 8, 10}, 0x44: {2, 3, 5}, 0x45: {1, 2, 3}, 0x53: {5, 6, 8, 9, 10}, 0x75: {0, 2, 3, 4, 5}, + 0x85: {2, 3, 4, 5}, 0x5d: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, 0x6b: {5, 6, 7, 8, 9, 10, 11, 12, 13}, + 0x6d: {0, 1, 2, 3, 4, 5, 6, 7, 8}, 0x7b: {2, 3, 4, 5, 8, 9, 10}, 0x7d: {4, 5, 6, 7, 8, 10}, 0x8d: {0, 1, 2, 3, 4}, + 0xa5: {0, 1, 2, 3, 4, 5, 6, 7}, 0x71: {0, 1}, 0xd8: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, + 0xb0: {0, 1, 2, 3, 4, 5, 7, 8, 9, 10}, 0xc0: {0, 1, 2}, 0xe0: {0, 1, 2, 3}, 0xb2: {5, 6, 7, 10, 11}, + 0xd2: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, 0xef: {0, 1, 2}, 0x10c: {4, 5, 6, 7}, 0x123: {0, 1, 2, 3}, + 0xee: {0, 1, 2, 3, 4} # low health traversal +} + + +def setup_specific_requirements(data_tables): + requirements = data_tables.sprite_requirements + water_groups = set() + water_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} + killable_groups = set() + killable_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} + key_groups = set() + key_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} + + for sid, requirement in requirements.items(): + if isinstance(requirement, dict): + continue + if requirement.good_for_uw_water(): + water_groups.update(requirement.groups) + for i in range(0, 4): + limited = [x for x in requirement.sub_groups[i] if x in sub_group_choices[i]] + water_sub_groups[i].update(limited) + if requirement.good_for_shutter([]): + killable_groups.update(requirement.groups) + for i in range(0, 4): + killable_sub_groups[i].update(requirement.sub_groups[i]) + if requirement.can_drop: + key_groups.update(requirement.groups) + for i in range(0, 4): + key_sub_groups[i].update(requirement.sub_groups[i]) + return water_groups, water_sub_groups, killable_groups, killable_sub_groups, key_groups, key_sub_groups + + +def get_possible_sheets(room_id, data_tables, specific, all_sheets, uw_sheets): + # forced sprites for room + requirements = data_tables.sprite_requirements + + water_groups, water_sub_groups, killable_groups, killable_sub_groups, key_groups, key_sub_groups = specific + + # forced_req = set() + key_needed = False + killable_needed = room_id in shutter_sprites + + for sheet in all_sheets: + if room_id in sheet.room_set: + return [sheet] + + match_all_room_groups = set() + match_all_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} + # match_all_sub_groups = {0: set(uw_sub_group_choices[0] + [70, 72]), 1: set(uw_sub_group_choices[1] + [13, 73]), + # 2: set(uw_sub_group_choices[2] + [19]), 3: set(uw_sub_group_choices[3] + [25, 68])} + + for sprite in data_tables.uw_enemy_table.room_map[room_id]: + sprite_secondary = 0 if sprite.sub_type != SpriteType.Overlord else sprite.sub_type + key = (sprite.kind, sprite_secondary) + if key not in requirements: + continue + req = requirements[key] + if isinstance(req, dict): + req = req[room_id] + if req.static or not req.can_randomize or sprite.static: + if req.groups: + match_all_room_groups.intersection_update(req.groups) + if not match_all_room_groups: + match_all_room_groups = set(req.groups) + for i in range(0, 4): + if req.sub_groups[i]: + match_all_sub_groups[i].intersection_update(req.sub_groups[i]) + if not match_all_sub_groups[i]: + match_all_sub_groups[i] = set(req.sub_groups[i]) + # forced_req.add(req) + if sprite.drops_item: + key_needed = True + + match_any_room_groups = set() + match_any_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} + exclude_all_groups = set() + exclude_all_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} + + if room_id in data_tables.room_requirements: + required_groups = data_tables.room_requirements[room_id] + for idx, grp in enumerate(required_groups): + if grp is not None: + if isinstance(grp, tuple): + match_any_sub_groups[idx].update(grp) + else: + match_all_sub_groups[idx] = {grp} + + if key_needed: + if key_groups: + match_any_room_groups.update(key_groups) + for i in range(0, 4): + if key_sub_groups[i]: + match_any_sub_groups[i].update(key_sub_groups[i]) + elif killable_needed: + if killable_groups: + match_any_room_groups.update(killable_groups) + for i in range(0, 4): + if killable_sub_groups[i]: + match_any_sub_groups[i].update(killable_sub_groups[i]) + + possible_sheets = [] + for sheet in uw_sheets: + if match_all_room_groups and sheet.id not in match_all_room_groups: + continue + if any(match_all_sub_groups[i] and sheet.sub_groups[i] not in match_all_sub_groups[i] for i in range(0, 4)): + continue + if exclude_all_groups and sheet.id in exclude_all_groups: + continue + if any(exclude_all_sub_groups[i] and sheet.sub_groups[i] in exclude_all_sub_groups[i] for i in range(0, 4)): + continue + if match_any_room_groups and sheet.id not in match_any_sub_groups: + continue + test_subs = [i for i in range(0, 4) if match_any_sub_groups[i]] + if test_subs and all(sheet.sub_groups[i] not in match_any_sub_groups[i] for i in test_subs): + continue + possible_sheets.append(sheet) + return possible_sheets + + +def get_possible_ow_sheets(area_id, all_sheets, ow_sheets, data_tables): + requirements = data_tables.sprite_requirements + + for sheet in all_sheets: + if area_id in sheet.room_set: + return [sheet] + + match_all_room_groups = set() + match_all_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} + + for sprite in data_tables.ow_enemy_table[area_id]: + sprite_secondary = 0 if sprite.sub_type != SpriteType.Overlord else sprite.sub_type + key = (sprite.kind, sprite_secondary) + if key not in requirements: + continue + req = requirements[key] + if isinstance(req, dict): + req = req[area_id] + if req.static or not req.can_randomize: + if req.groups: + match_all_room_groups.intersection_update(req.groups) + if not match_all_room_groups: + match_all_room_groups = set(req.groups) + for i in range(0, 4): + if req.sub_groups[i]: + match_all_sub_groups[i].intersection_update(req.sub_groups[i]) + if not match_all_sub_groups[i]: + match_all_sub_groups[i] = set(req.sub_groups[i]) + + possible_sheets = [] + for sheet in ow_sheets: + if match_all_room_groups and sheet.id not in match_all_room_groups: + continue + if any(match_all_sub_groups[i] and sheet.sub_groups[i] not in match_all_sub_groups[i] for i in range(0, 4)): + continue + possible_sheets.append(sheet) + return possible_sheets + + +ignore_sheets_uw = {65, 69, 71, 78, 79, 82, 88, 98} +ignore_sheets_ow = {6} + + +def find_candidate_sprites(data_tables, sheet_range, uw=True): + requirements = data_tables.sprite_requirements + sprite_candidates = [] + sheet_candidates = [] + all_sheets = [] + + candidate_groups = set() + candidate_sub_groups = {0: set(), 1: set(), 2: set(), 3: set()} + + for k, r in requirements.items(): + if isinstance(r, dict): + continue + valid_flag = (uw and r.uw_valid) or (not uw and r.ow_valid) + if not r.static and valid_flag and not r.dont_use: + candidate_groups.update(r.groups) + for i in range(0, 4): + candidate_sub_groups[i].update(r.sub_groups[i]) + sprite_candidates.append(k) + + for num in sheet_range: + sheet = data_tables.sprite_sheets[num] + all_sheets.append(sheet) + if (uw and num in ignore_sheets_uw) or (not uw and num in ignore_sheets_ow): + continue + if candidate_groups and sheet not in candidate_groups: + continue + test_subs = [i for i in range(0, 4) if candidate_sub_groups[i]] + if test_subs and all(sheet.sub_groups[i] not in candidate_sub_groups[i] for i in test_subs): + continue + sheet_candidates.append(sheet) + + return sprite_candidates, sheet_candidates, all_sheets + + +def get_possible_enemy_sprites(room_id, sheet, uw_sprites, data_tables): + ret = [] + for sprite in uw_sprites: + requirement = data_tables.sprite_requirements[sprite] + if isinstance(requirement, dict): + requirement = requirement[room_id] + if sheet.valid_sprite(requirement) and requirement.can_spawn_in_room(room_id): + ret.append(requirement) + return ret + + +def get_possible_enemy_sprites_ow(sheet, sprites, data_tables): + ret = [] + for sprite in sprites: + requirement = data_tables.sprite_requirements[sprite] + if isinstance(requirement, dict): + continue + if sheet.valid_sprite(requirement) and requirement.ow_valid: + ret.append(requirement) + return ret + + +def get_randomize_able_sprites(room_id, data_tables): + sprite_table = {} + for idx, sprite in enumerate(data_tables.uw_enemy_table.room_map[room_id]): + sprite_secondary = 0 if sprite.sub_type != SpriteType.Overlord else sprite.sub_type + key = (sprite.kind, sprite_secondary) + if key not in data_tables.sprite_requirements: + continue + req = data_tables.sprite_requirements[key] + if isinstance(req, dict): + continue + if not req.static and req.can_randomize and not sprite.static: + sprite_table[idx] = sprite + return sprite_table + + +def get_randomize_able_sprites_ow(area_id, data_tables): + sprite_table = {} + for idx, sprite in enumerate(data_tables.ow_enemy_table[area_id]): + sprite_secondary = 0 if sprite.sub_type != SpriteType.Overlord else sprite.sub_type + key = (sprite.kind, sprite_secondary) + if key not in data_tables.sprite_requirements: + continue + req = data_tables.sprite_requirements[key] + if isinstance(req, dict): + continue + if not req.static and req.can_randomize: + sprite_table[idx] = sprite + return sprite_table + + +sprite_limiter = { + EnemySprite.Debirando: 2, + EnemySprite.DebirandoPit: 2, + EnemySprite.Hinox: 2, + EnemySprite.Sluggula: 2, + EnemySprite.BombGuard: 2, + EnemySprite.Beamos: 2, + EnemySprite.Gibo: 2, + # EnemySprite.CannonTrooper: 2, ?? + EnemySprite.WallCannonHorzTop: 2, + EnemySprite.WallCannonHorzBottom: 2, + EnemySprite.WallCannonVertLeft: 2, + EnemySprite.WallCannonVertRight: 2, + EnemySprite.BlueArcher: 2, + EnemySprite.BlueGuard: 2, + EnemySprite.GreenGuard: 2, + EnemySprite.RedSpearGuard: 2, + EnemySprite.RedJavelinGuard: 2, + EnemySprite.AntiFairyCircle: 4 +} + + +def exceeds_sprite_limit(limit, sprite): + return sprite_limiter[sprite.sprite]-1+limit > 15 if sprite.sprite in sprite_limiter else False + + +def randomize_underworld_rooms(data_tables, world, player, custom_uw): + any_enemy_logic = world.any_enemy_logic[player] + enemy_drops_active = world.dropshuffle[player] in ['underworld'] + specific = setup_specific_requirements(data_tables) + uw_candidates, uw_sheets, all_sheets = find_candidate_sprites(data_tables, range(65, 124)) + for room_id in range(0, 0x128): + if room_id in {0, 1, 3, 6, 7, 0xd, 0x14, 0x20, 0x29, 0x30, 0x33, + 0x4d, 0x5a, 0x90, 0xa4, 0xac, 0xc8, 0xde}: + continue + current_sprites = data_tables.uw_enemy_table.room_map[room_id] + sprite_limit = sum(sprite_limiter[x.kind] if x.kind in sprite_limiter else 1 for x in current_sprites) + if room_id in {0x3f, 0x44, 0x45, 0x93, 0xce, 0x117}: + sprite_limit += 1 # for liftable blocks see PotFlags.Block in PotShuffle + randomizeable_sprites = get_randomize_able_sprites(room_id, data_tables) + if not randomizeable_sprites: + candidate_sheets = get_possible_sheets(room_id, data_tables, specific, all_sheets, uw_sheets) + chosen_sheet = random.choice(candidate_sheets) + data_tables.room_headers[room_id].sprite_sheet = chosen_sheet.id - 0x40 + if randomizeable_sprites: + candidate_sheets = get_possible_sheets(room_id, data_tables, specific, all_sheets, uw_sheets) + done = False + while not done: + chosen_sheet = random.choice(candidate_sheets) + data_tables.room_headers[room_id].sprite_sheet = chosen_sheet.id - 0x40 + candidate_sprites = get_possible_enemy_sprites(room_id, chosen_sheet, uw_candidates, data_tables) + randomized = True + # wallmaster in hera basement throws off hera basement key code + wallmaster_chosen = room_id in {0x0039, 0x0049, 0x0056, 0x0057, 0x0068, 0x0087, 0x008d} + for i, sprite in randomizeable_sprites.items(): + if room_id in custom_uw and i in custom_uw[room_id]: + sprite.kind = sprite_translation[custom_uw[room_id][i]] + else: + # filter out water if necessary + candidate_sprites = [x for x in candidate_sprites if not x.water_only or sprite.water] + # filter out wallmaster if already on tile + if wallmaster_chosen: + candidate_sprites = [x for x in candidate_sprites if x.sprite != EnemySprite.Wallmaster] + candidate_sprites = [x for x in candidate_sprites if not exceeds_sprite_limit(sprite_limit, x)] + if sprite.drops_item: + forbidden = determine_forbidden(any_enemy_logic == 'none', room_id, True) + choice_list = [x for x in candidate_sprites if x.good_for_key_drop(forbidden)] + # terrorpin, deadrock, buzzblob, lynel, redmimic/eyegore + elif room_id in shutter_sprites and i in shutter_sprites[room_id]: + forbidden = determine_forbidden(any_enemy_logic != 'allow_all', room_id) + choice_list = [x for x in candidate_sprites if x.good_for_shutter(forbidden)] + else: + choice_list = [x for x in candidate_sprites if not x.water_only] + choice_list = filter_choices(choice_list, room_id, i, data_tables.uw_enemy_denials) + if enemy_drops_active: + choice_list = filter_choices(choice_list, room_id, i, data_tables.uw_enemy_drop_denials) + if len(choice_list) == 0: + randomized = False + break + weight = [data_tables.uw_weights[r.sprite] for r in choice_list] + chosen = random.choices(choice_list, weight, k=1)[0] + sprite.kind = chosen.sprite + if sprite.kind in sprite_limiter: + sprite_limit += sprite_limiter[sprite.kind]-1 + if sprite.kind == EnemySprite.Wallmaster: + wallmaster_chosen = True + sprite.kind = 0x09 + sprite.sub_type = SpriteType.Overlord + done = randomized + # done with sprites + # done with rooms + + +def determine_forbidden(forbid, room_id, drop_flag=False): + forbidden_set = set() + if forbid: + forbidden_set.update({EnemySprite.Terrorpin, EnemySprite.Deadrock, EnemySprite.Buzzblob, + EnemySprite.Lynel}) + if drop_flag: + forbidden_set.add(EnemySprite.RedBari) # requires FireRod to Drop + # else: Not yet able to protect triggers, would change default GT tile room behavior + # forbidden_set.add(EnemySprite.AntiFairy) # can't drop anyway + if room_id not in {0x6b, 0x4b, 0x1b, 0xd8}: # mimics/eyegore are allowed in vanilla rooms + forbidden_set.add(EnemySprite.RedEyegoreMimic) + forbidden_set.add(EnemySprite.RedMimic) + return forbidden_set + + +def filter_choices(options, room_id, sprite_idx, denials): + key = room_id, sprite_idx + return [x for x in options if key not in denials or x.sprite not in denials[key]] + + +def filter_water_phobic(options, sprite): + return [x for x in options if not x.water_phobic or not sprite.water] + + +def randomize_overworld_enemies(data_tables, custom_ow): + ow_candidates, ow_sheets, all_sheets = find_candidate_sprites(data_tables, range(1, 64), False) + areas_to_randomize = [0, 2, 3, 5, 7, 0xA, 0xF, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, + 0x1a, 0x1b, 0x1d, 0x1e, 0x22, 0x25, 0x28, 0x29, 0x2A, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, + 0x30, 0x32, 0x33, 0x34, 0x35, 0x37, 0x3a, 0x3b, 0x3c, 0x3f] + area_list = areas_to_randomize + [x + 0x40 for x in areas_to_randomize] # light world + dark world + area_list += [0x80, 0x81] + [x + 0x90 for x in areas_to_randomize] # specials + post aga LW + for area_id in area_list: + randomizeable_sprites = get_randomize_able_sprites_ow(area_id, data_tables) + if not randomizeable_sprites: + candidate_sheets = get_possible_ow_sheets(area_id, all_sheets, ow_sheets, data_tables) + chosen_sheet = random.choice(candidate_sheets) + data_tables.overworld_sprite_sheets[area_id] = chosen_sheet + candidate_sprites = get_possible_enemy_sprites_ow(chosen_sheet, ow_candidates, data_tables) + else: + candidate_sheets = get_possible_ow_sheets(area_id, all_sheets, ow_sheets, data_tables) + chosen_sheet = random.choice(candidate_sheets) + data_tables.overworld_sprite_sheets[area_id] = chosen_sheet + candidate_sprites = get_possible_enemy_sprites_ow(chosen_sheet, ow_candidates, data_tables) + for i, sprite in randomizeable_sprites.items(): + if area_id in custom_ow and i in custom_ow[area_id]: + sprite.kind = sprite_translation[custom_ow[area_id][i]] + else: + candidate_sprites = filter_choices(candidate_sprites, area_id, i, data_tables.ow_enemy_denials) + candidate_sprites = filter_water_phobic(candidate_sprites, sprite) + weight = [data_tables.ow_weights[r.sprite] for r in candidate_sprites] + chosen = random.choices(candidate_sprites, weight, k=1)[0] + sprite.kind = chosen.sprite + # randomize the bush sprite per area + weight = [data_tables.ow_weights[r.sprite] for r in candidate_sprites] + bush_sprite_choice = random.choices(candidate_sprites, weight, k=1)[0] + data_tables.bush_sprite_table[area_id] = bush_sprite_choice + + +# damage and health tables only go to F2 +skip_sprites = { + EnemySprite.ArmosKnight, EnemySprite.Lanmolas, EnemySprite.Moldorm, EnemySprite.Mothula, EnemySprite.Arrghus, + EnemySprite.HelmasaurKing, EnemySprite.Vitreous, EnemySprite.TrinexxRockHead, EnemySprite.TrinexxFireHead, + EnemySprite.TrinexxIceHead, EnemySprite.Blind, EnemySprite.Kholdstare, EnemySprite.KholdstareShell, + EnemySprite.FallingIce, EnemySprite.Arrghi, EnemySprite.Agahnim, EnemySprite.Ganon, + EnemySprite.PositionTarget, EnemySprite.Boulders +} + + +def randomize_enemies(world, player): + if world.enemy_shuffle[player] != 'none': + data_tables = world.data_tables[player] + custom_uw, custom_ow = {}, {} + enemy_map = world.customizer.get_enemies() if world.customizer else None + if enemy_map and player in enemy_map: + if 'Underworld' in enemy_map[player]: + custom_uw = enemy_map[player]['Underworld'] + if 'Overworld' in enemy_map[player]: + custom_ow = enemy_map[player]['Overworld'] + randomize_underworld_sprite_sheets(data_tables.sprite_sheets, data_tables, custom_uw) + randomize_underworld_rooms(data_tables, world, player, custom_uw) + randomize_overworld_sprite_sheets(data_tables.sprite_sheets, data_tables, custom_ow) + randomize_overworld_enemies(data_tables, custom_ow) + # fix thief stats + # subclass_table = world.damage_table[player].damage_table['SubClassTable'] + # subclass_table[EnemySprite.Thief] = subclass_table[EnemySprite.GreenEyegoreMimic] + # data_tables.enemy_stats[EnemySprite.Thief].health = 4 + # could turn droppable on here if we wanted for killable theives + # health shuffle + if world.enemy_health[player] != 'default': + stats = world.data_tables[player].enemy_stats + min_health = {'easy': 1, 'normal': 2, 'hard': 2, 'expert': 4} + max_health = {'easy': 4, 'normal': 15, 'hard': 25, 'expert': 50} + min_h = min_health[world.enemy_health[player]] + max_h = max_health[world.enemy_health[player]] + for sprite, stat in stats.items(): + if sprite == EnemySprite.Octorok4Way: + stat.health = stats[EnemySprite.Octorok].health # these guys share data + elif sprite == EnemySprite.GreenMimic: + stat.health = stats[EnemySprite.GreenEyegoreMimic].health # these share data + elif sprite == EnemySprite.RedMimic: + stat.health = stats[EnemySprite.RedEyegoreMimic].health # these share data + elif sprite not in skip_sprites: + if isinstance(stat.health, tuple): + stat.health = random.randint(min_h, max_h), random.randint(min_h, max_h) + else: + stat.health = random.randint(min_h, max_h) + if world.enemy_damage[player] != 'default': + stats = world.data_tables[player].enemy_stats + # randomize damage groupings + for sprite, stat in stats.items(): + if sprite == EnemySprite.Octorok4Way: + stat.damage = stats[EnemySprite.Octorok].damage # these guys share data + elif sprite == EnemySprite.GreenMimic: + stat.damage = stats[EnemySprite.GreenEyegoreMimic].damage # these share data + elif sprite == EnemySprite.RedMimic: + stat.damage = stats[EnemySprite.RedEyegoreMimic].damage # these share data + elif sprite == EnemySprite.Thief: # always group 0 for 0 damage + stat.damage = 0 + elif sprite not in skip_sprites: + if isinstance(stat.damage, tuple): + stat.damage = random.randint(0, 8), random.randint(0, 8) + else: + stat.damage = random.randint(0, 8) + # randomize bump table + original_table = [ + (0x02, 0x01, 0x01), + (0x04, 0x04, 0x04), + (0x00, 0x00, 0x00), + (0x08, 0x04, 0x02), + (0x08, 0x08, 0x08), + (0x10, 0x08, 0x04), + (0x20, 0x10, 0x08), + (0x20, 0x18, 0x10), + (0x18, 0x10, 0x08), + (0x40, 0x30, 0x18)] + for i in range(0, 10): + if i == 0: # group 0 will always be 0 for thieves + green_mail, blue_mail, red_mail = 0, 0, 0 + del original_table[2] + else: + if world.enemy_damage[player] == 'random': + green_mail = random.randint(0, 64) + if world.enemy_damage[player] == 'random': + blue_mail = random.randint(0, 64) + red_mail = random.randint(0, 64) + else: + idx = random.randint(0, len(original_table)-1) + green_mail, blue_mail, red_mail = original_table[idx] + del original_table[idx] + world.data_tables[player].enemy_damage[i] = [green_mail, blue_mail, red_mail] + + +def write_enemy_shuffle_settings(world, player, rom): + if world.dropshuffle[player] in ['underworld']: + rom.write_byte(snes_to_pc(0x368109), 0x01) + if world.enemy_shuffle[player] != 'none': + # enable new mimics + rom.write_byte(snes_to_pc(0x368105), 0x01) + + # killable thief + # rom.write_byte(snes_to_pc(0x368108), 0xc4) + # rom.write_byte(snes_to_pc(0x0DB237), 4) # health value - randomize it if killable, maybe + + # mimic room barriers + data_tables = world.data_tables[player] + mimic_room = data_tables.room_list[0x10c] = Room010C + mimic_room.layer1[40].data[0] = 0x54 # rail adjust + mimic_room.layer1[40].data[1] = 0x9C + mimic_room.layer1[45].data[1] = 0xB0 # block adjust 1 + mimic_room.layer1[47].data[1] = 0xD0 # block adjust 2 + + # random tile pattern + pattern_name, tile_pattern = random.choice(tile_patterns) + rom.write_byte(snes_to_pc(0x9BA1D), len(tile_pattern)) + for idx, pair in enumerate(tile_pattern): + rom.write_byte(snes_to_pc(0x09BA2A + idx), (pair[0] + 3) * 16) + rom.write_byte(snes_to_pc(0x09BA40 + idx), (pair[1] + 4) * 16) + if world.enemy_shuffle[player] == 'random': + rom.write_byte(snes_to_pc(0x368100), 1) # randomize bushes diff --git a/source/enemizer/EnemizerTestHarness.py b/source/enemizer/EnemizerTestHarness.py new file mode 100644 index 00000000..37295bae --- /dev/null +++ b/source/enemizer/EnemizerTestHarness.py @@ -0,0 +1,185 @@ +from types import SimpleNamespace +from collections import Counter, defaultdict + +from source.dungeon.EnemyList import enemy_names, SpriteType +from source.enemizer.Enemizer import randomize_underworld_rooms, randomize_overworld_enemies +from source.enemizer.SpriteSheets import randomize_underworld_sprite_sheets, randomize_overworld_sprite_sheets +from source.rom.DataTables import init_data_tables +from source.enemizer.DamageTables import DamageTable +import RaceRandom as random + + +def calculate_odds(): + ctr_uw = Counter() + ctr_ow = Counter() + for trial in range(0, 100): + world = SimpleNamespace(pottery={1: 'none'}, damage_table={1: DamageTable()}) + data_tables = init_data_tables(world, 1) + + randomize_underworld_sprite_sheets(data_tables.sprite_sheets, data_tables) + randomize_overworld_sprite_sheets(data_tables.sprite_sheets) + + for num in range(65, 124): + sheet = data_tables.sprite_sheets[num] + ret = [] + for req in data_tables.sprite_requirements.values(): + if not isinstance(req, dict) and sheet.valid_sprite(req) and not req.overlord and not req.static: + ret.append(enemy_names[req.sprite]) + for x in ret: + ctr_uw[x] += 1 + + for num in range(1, 64): + sheet = data_tables.sprite_sheets[num] + ret = [] + for req in data_tables.sprite_requirements.values(): + if not isinstance(req, dict) and sheet.valid_sprite(req) and not req.overlord and not req.static: + ret.append(enemy_names[req.sprite]) + for x in ret: + ctr_ow[x] += 1 + ttl = sum(ctr_uw.values()) + print(f'UW: # Total {ttl}') + for k, v in ctr_uw.items(): + weight = round(.01 * ttl * 100 / v) + print(f' {k}: {weight} # {v*100/ttl:.5f}% raw:{v}') + ttl = sum(ctr_ow.values()) + print(f'OW: # Total {ttl}') + for k, v in ctr_ow.items(): + weight = round(.01 * ttl * 100 / v) + print(f' {k}: {weight} # {v*100/ttl:.5f}% raw:{v}') + + +if __name__ == '__main__': + # calculate_odds() + random.seed(42) + # + frequency = Counter() + sheet_ctr = Counter() + ctr_uw = Counter() + ctr_ow = Counter() + sheet_thing = Counter() + sheet_sub_groups = defaultdict(set) + + for trial in range(0, 100): + world = SimpleNamespace(pottery={1: 'none'}, damage_table={1: DamageTable()}, any_enemy_logic={1: 'allow_all'}, + dropshuffle={1: 'off'}) + data_tables = init_data_tables(world, 1) + stats = data_tables.enemy_stats + + def randomize_able_sprite(sprite, stats): + if sprite.static: + return False + if sprite.sub_type == 0x7 and sprite.kind != 0x9: + return False + if sprite.sub_type == 0x7 and sprite.kind == 0x9: + return True + return sprite.kind not in stats or not stats[sprite.kind].static + + if trial == 0: + print(f' Underworld:') + for room_id, enemy_list in data_tables.uw_enemy_table.room_map.items(): + print(f' {hex(room_id)}:') + for i, sprite in enumerate(enemy_list): + if randomize_able_sprite(sprite, stats): + print(f' {i}: {str(sprite)}') + print(f' Overworld:') + for area, enemy_list in data_tables.ow_enemy_table.items(): + print(f' {hex(area)}:') + for i, sprite in enumerate(enemy_list): + if randomize_able_sprite(sprite, stats): + print(f' {i}: {str(sprite)}') + + randomize_underworld_sprite_sheets(data_tables.sprite_sheets, data_tables, {}) + randomize_underworld_rooms(data_tables, world, 1) + randomize_overworld_sprite_sheets(data_tables.sprite_sheets, data_tables, {}) + randomize_overworld_enemies(data_tables) + + for room_id, enemy_list in data_tables.uw_enemy_table.room_map.items(): + # print(f'Room {hex(room_id)}:') + for i, sprite in enumerate(enemy_list): + if randomize_able_sprite(sprite, stats): + result = str(sprite) + frequency[result] += 1 + for area, enemy_list in data_tables.ow_enemy_table.items(): + for i, sprite in enumerate(enemy_list): + if randomize_able_sprite(sprite, stats): + result = str(sprite) + frequency[result] += 1 + + for num in range(65, 124): + if num in {65, 69, 71, 78, 79, 82, 88, 98}: + continue + sheet = data_tables.sprite_sheets[num] + ret = [] + for req in data_tables.sprite_requirements.values(): + if not isinstance(req, dict) and sheet.valid_sprite(req) and not req.overlord and not req.static: + ret.append(enemy_names[req.sprite]) + for x in ret: + ctr_uw[x] += 1 + sheet_ctr[x] += 1 + key = tuple(sorted(ret)) + sheet_thing[key] += 1 + sheet_sub_groups[key].add(tuple(sheet.sub_groups)) + + for num in range(1, 64): + if num == 6: + continue + sheet = data_tables.sprite_sheets[num] + ret = [] + for req in data_tables.sprite_requirements.values(): + if not isinstance(req, dict) and sheet.valid_sprite(req) and not req.overlord and not req.static: + ret.append(enemy_names[req.sprite]) + for x in ret: + ctr_ow[x] += 1 + sheet_ctr[x] += 1 + key = tuple(sorted(ret)) + sheet_thing[key] += 1 + sheet_sub_groups[key].add(tuple(sheet.sub_groups)) + + total_sheets = sum(sheet_thing.values()) + ttl = sum(ctr_uw.values()) + print(f'UW: # Total {ttl}') + for k in sorted(list(ctr_uw.keys())): + v = ctr_uw[k] + weight = round(.01 * ttl * 100 / v) + print(f' {k}: {weight} # {v*100/ttl:.5f}% raw:{v} {v*100/total_sheets:.5f}%') + ttl = sum(ctr_ow.values()) + print(f'OW: # Total {ttl}') + for k in sorted(list(ctr_ow.keys())): + v = ctr_ow[k] + weight = round(.01 * ttl * 100 / v) + print(f' {k}: {weight} # {v*100/ttl:.5f}% raw:{v} {v*100/total_sheets:.5f}%') + + ttl = sum(sheet_ctr.values()) + print(f'Sheet: # Total {ttl}') + for k, v in sheet_ctr.items(): + print(f' {k} {v*100/ttl:.5f}% raw:{v} {v*100/total_sheets:.5f}%') + + ttl = sum(frequency.values()) + print(f'Total: {ttl}') + for enemy, freq in frequency.items(): + print(f'{enemy} {freq*100/ttl:.5f}% raw:{freq}') + + ttl = sum(sheet_thing.values()) + print(f'Total Sheets?: {ttl}') + + def rejoin(list_of_things): + return f'[{",".join([str(i) for i in list_of_things])}]' + + for items, cnt in sheet_thing.items(): + print(f'{",".join(items)} {cnt} {cnt*100/ttl:.5f}% {",".join([rejoin(x) for x in sheet_sub_groups[items]])}') + + # if result not in column_headers: + # column_headers[result] = None + # stats[(room_id, i)][result] += 1 + # with open('result.csv', 'w') as result_file: + # result_file.write('room_id,slot,') + # result_file.write(','.join(column_headers.keys())) + # result_file.write('\n') + # + # for key, counter in stats.items(): + # rid, slot = key + # result_file.write(f'{rid},{slot}') + # for result_item in column_headers.keys(): + # result_file.write(f',{counter[result_item]}') + # result_file.write('\n') + diff --git a/source/enemizer/EnemyLogic.py b/source/enemizer/EnemyLogic.py new file mode 100644 index 00000000..67f6eb38 --- /dev/null +++ b/source/enemizer/EnemyLogic.py @@ -0,0 +1,525 @@ +import math +from collections import defaultdict + +import RaceRandom as random + +from source.logic.Rule import RuleFactory +from source.dungeon.EnemyList import EnemySprite + + +# these are for drops only +def defeat_rule_single(world, player, enemy_sprite, region): + if enemy_sprite.kind == EnemySprite.Terrorpin: + # must be flipped + return has('Hammer', player) + elif enemy_sprite.kind == EnemySprite.RedBari: + # must be burned to drop + return or_rule(has('Fire Rod', player), and_rule(has_sword(player), has('Bombos', player))) + vln = enemy_vulnerability(world, player, enemy_sprite, region) + rules = [] + if vln['Blunt'] != 0: + rules.append(has_blunt_weapon(player)) + if vln['Stun'] != 0: + rules.append(buzzblob_rule(player)) + if vln['Somaria'] != 0: + rules.append(somaria_rule(world, player, vln['Somaria'])) + if vln['Byrna'] != 0: + rules.append(byrna_rule(world, player, vln['Byrna'])) + if vln['Master'] != 0: + rules.append(has_class_2_weapon(player)) + if vln['Bow'] != 0: + rules.append(bow_rule(world, player, vln['Bow'])) + if vln['Silvers'] != 0: + rules.append(silvers_rule(world, player, vln['Silvers'])) + if vln['Bomb'] != 0: + rules.append(bombs_rule(world, player, vln['Bomb'])) + if vln['Hookshot'] != 0: + rules.append(has('Hookshot', player)) + if vln['IceRod'] != 0: + rules.append(ice_rod_rule(world, player, vln['IceRod'])) + if vln['FireRod'] != 0: + rules.append(fire_rod_rule(world, player, vln['FireRod'])) + if vln['Boomerang'] != 0: + rules.append(has_boomerang(player)) + if vln['Powder'] not in [0, -3]: # fairy doesn't make it drop + rules.append(magic_powder_rule(world, player, vln['Powder'])) + # skip medallions if vln to Blunt? + if vln['Bombos'] != 0 and vln['Blunt'] == 0: + rules.append(medallion_rule(world, player, 'Bombos', vln['Bombos'])) + if vln['Ether'] != 0 and vln['Blunt'] == 0: + rules.append(medallion_rule(world, player, 'Ether', vln['Ether'])) + if vln['Quake'] != 0 and vln['Blunt'] == 0: + rules.append(medallion_rule(world, player, 'Quake', vln['Quake'])) + if enemy_sprite.kind == EnemySprite.StalfosKnight: + # must be bombed once made vulnerable + return and_rule(can_use_bombs(world, player), or_rule(*rules)) + return or_rule(*rules) + + +damage_cost = { + 'Bomb': 1, 'Bow': 1, 'Silvers': 1, + 'Powder': .5, 'Somaria': .5, 'Byrna': 1.125, + 'FireRod': 1, 'IceRod': 1, + 'Bombos': 2, 'Ether': 2, 'Quake': 2 +} + +# damage_set = ['Blunt', 'Stun', 'Master', 'Tempered', 'Boomerang', 'Hookshot', 'Bomb', 'Silvers', 'Bow', +# 'Somaria', 'Powder', 'FireRod', 'IceRod', 'Byrna', 'Bombos', 'Ether', 'Quake'] + + +# these are for "challenge" rooms +def defeat_rule_multiple(world, player, enemy_sprite_region_pairs): + vln_list = {} + for sprite, region in enemy_sprite_region_pairs: + vln_list[(sprite, region)] = enemy_vulnerability(world, player, sprite, region) + + # damage_accounting = {x: list(y) for x, y in damage_types.items()} + used_resources = {'Bomb': 0, 'Arrow': 0, 'Magic': 0} + required_rules = [] + picky_enemies = [] + hammer_required = False + bombs_required = False + + for key, vln in vln_list.items(): + if key[0].kind == EnemySprite.Terrorpin: + if not hammer_required: + required_rules.append(has('Hammer', player)) + hammer_required = True + picky_enemies.append(key) + continue + if key[0].kind == EnemySprite.StalfosKnight: + if not bombs_required: + required_rules.append(bombs_rule(world, player, 1)) + bombs_required = True + used_resources['Bomb'] += 1 + picky_enemies.append(key) + continue + vln_types = [k for k in vln.keys() if vln[k] != 0] + if len(vln_types) == 1: + d_type = vln_types[0] + required_rules.append(defeat_rule_single(world, player, key[0], key[1])) + picky_enemies.append(key) + if d_type in damage_cost: + cost = damage_cost[d_type] + if d_type == 'Bomb': + used_resources['Bomb'] += cost + elif d_type in ['Bow', 'Silvers']: + used_resources['Arrow'] += cost + else: + used_resources['Magic'] += cost + vln_list = {k: v for k, v in vln_list.items() if k not in picky_enemies} + + while len(vln_list) > 0: + + optional_clears = find_possible_rules(vln_list, used_resources, world, player) + if len(optional_clears) == 0: + raise Exception('Kill rules seems to be insufficient for this enemy set, please report:' + + ', '.join([str(x) for x, y in enemy_sprite_region_pairs])) + + # find rules which kill the most + # idea: this could be multiple criteria: most-constrained then which method kills the most + best_rules = {} + best_size = 0 + for vln_option in optional_clears.keys(): + if len(vln_option) > best_size: + best_size = len(vln_option) + best_rules.clear() + best_rules[vln_option] = optional_clears[vln_option] + elif len(vln_option) == best_size: # assumes vln_option is different from prior options + best_rules[vln_option] = optional_clears[vln_option] + if len(best_rules) == 1: + vln_option, rule_pair_list = next(iter(best_rules.items())) + else: + vln_option, rule_pair_list = random.choice(list(best_rules.items())) + if best_size == 0: + raise Exception('Invulnerable enemy? rules seems to be insufficient for this enemy set, please report:' + + ', '.join([str(x) for x, y in enemy_sprite_region_pairs])) + + new_vln_list = {vln_kv[0]: vln_kv[1] for idx, vln_kv in enumerate(vln_list.items()) if idx not in vln_option} + rules_to_add = [rule for rule, resources in rule_pair_list] + resources_to_use = [resources for rule, resources in rule_pair_list] + required_rules.append(or_rule(*rules_to_add)) + for r in resources_to_use: + for k, v in r.items(): + used_resources[k] += v + vln_list = new_vln_list + + return and_rule(*required_rules) + + +def find_possible_rules(vln_list, used_resources, world, player): + optional_clears = defaultdict(list) + blunt_marker = defaultdict(bool) + for damage_type in ['Blunt', 'Stun', 'Master', 'Boomerang', 'Hookshot']: + # all_vln = all(vln[damage_type] != 0 for vln in vln_list.values()) + vln_sub_list = frozenset({idx for idx, vln in enumerate(vln_list.values()) if vln[damage_type] != 0}) + if vln_sub_list: + if damage_type == 'Blunt': + optional_clears[vln_sub_list].append((has_blunt_weapon(player), {})) + blunt_marker[vln_sub_list] = True + if damage_type == 'Stun': + optional_clears[vln_sub_list].append((buzzblob_rule(player), {})) + if damage_type == 'Master' and not blunt_marker[vln_sub_list]: + optional_clears[vln_sub_list].append((has_class_2_weapon(player), {})) + if damage_type == 'Boomerang': + optional_clears[vln_sub_list].append((has('Hookshot', player), {})) + elif damage_type == 'Hookshot': + optional_clears[vln_sub_list].append((has_boomerang(player), {})) + damage_type = 'Bomb' + vln_sub_list = frozenset({idx for idx, vln in enumerate(vln_list.values()) if vln[damage_type] != 0}) + if vln_sub_list: + hits = needed_resources(damage_type, vln_list) + if hits + used_resources['Bomb'] <= 8: + optional_clears[vln_sub_list].append( + (bombs_rule(world, player, hits + used_resources['Bomb']), {'Bomb': hits})) + for damage_type in ['Bow', 'Silvers']: + vln_sub_list = frozenset({idx for idx, vln in enumerate(vln_list.values()) if vln[damage_type] != 0}) + if vln_sub_list: + hits = needed_resources(damage_type, vln_list) + resources = {'Arrow': hits} + if damage_type == 'Bow' and hits + used_resources['Arrow'] <= 25: + optional_clears[vln_sub_list].append( + (bow_rule(world, player, hits + used_resources['Arrow']), resources)) + if damage_type == 'Silvers' and hits + used_resources['Arrow'] <= 25: + optional_clears[vln_sub_list].append( + (silvers_rule(world, player, hits + used_resources['Arrow']), resources)) + for damage_type in ['Powder', 'Somaria', 'Byrna', 'FireRod', 'IceRod', 'Bombos', 'Ether', 'Quake']: + vln_sub_list = frozenset({idx for idx, vln in enumerate(vln_list.values()) if vln[damage_type] != 0}) + if vln_sub_list: + hits = needed_resources(damage_type, vln_list) + resources = {'Magic': damage_cost[damage_type] * hits} + if damage_type == 'Powder' and math.ceil(hits / 16) * 8 + used_resources['Magic'] <= 160: + flag = min(vln[damage_type] for vln in vln_list.values()) + flag = flag if flag < 0 else (hits + used_resources['Magic'] * 2) + optional_clears[vln_sub_list].append((magic_powder_rule(world, player, flag), resources)) + elif damage_type == 'Somaria' and math.ceil(hits / 64) * 8 + used_resources['Magic'] <= 160: + flag = min(vln[damage_type] for vln in vln_list.values()) + flag = flag if flag < 0 else (hits + used_resources['Magic'] * 8) + optional_clears[vln_sub_list].append((somaria_rule(world, player, flag), resources)) + elif damage_type == 'Byrna' and math.ceil(hits / 7) * 8 + used_resources['Magic'] <= 160: + flag = min(vln[damage_type] for vln in vln_list.values()) + flag = flag if flag < 0 else (hits + used_resources['Magic'] * 7 / 8) + optional_clears[vln_sub_list].append((byrna_rule(world, player, flag), resources)) + elif damage_type == 'FireRod' and hits + used_resources['Magic'] <= 160: + flag = min(vln[damage_type] for vln in vln_list.values()) + flag = flag if flag < 0 else (hits + used_resources['Magic']) + optional_clears[vln_sub_list].append((fire_rod_rule(world, player, flag), resources)) + elif damage_type == 'IceRod' and hits + used_resources['Magic'] <= 160: + flag = min(vln[damage_type] for vln in vln_list.values()) + flag = flag if flag < 0 else (hits + used_resources['Magic']) + optional_clears[vln_sub_list].append((ice_rod_rule(world, player, flag), resources)) + elif hits * 2 + used_resources['Magic'] <= 160 and not blunt_marker[vln_sub_list]: + flag = min(vln[damage_type] for vln in vln_list.values()) + flag = flag if flag < 0 else (hits + used_resources['Magic'] / 2) + optional_clears[vln_sub_list].append((medallion_rule(world, player, damage_type, flag), resources)) + return optional_clears + + +def needed_resources(damage_type, vln_list): + return sum(vln[damage_type] if vln[damage_type] >= 0 else 1 for vln in vln_list.values() if vln[damage_type] != 0) + + +special_rules_check = { + 'Swamp Waterway': None, + 'Hera Back': [5, 6], + 'GT Petting Zoo': [5, 8, 9, 11], + 'Mimic Cave': [4, 5, 6, 7], + 'Ice Hookshot Ledge': None, + 'TR Hub Ledges': [3, 4, 5, 6, 7], + 'TR Dark Ride': [1, 2, 3], + 'GT Speed Torch': [11, 13], + 'Old Man Cave (West)': None, + 'Old Man Cave (East)': None, + 'Old Man House': [1, 3], + 'Old Man House Back': [4, 5, 6], + 'Death Mountain Return Cave (left)': None, + 'Death Mountain Return Cave (right)': [1, 2, 3, 6, 7], # 2, 5, 6 are considered embedded + 'Hookshot Fairy': [0, 1, 2, 3] +} + + +def special_rules_for_region(world, player, region_name, location, original_rule): + if region_name == 'Swamp Waterway': + return or_rule(medallion_rule(world, player, 'Quake', 1), + medallion_rule(world, player, 'Ether', 1), + medallion_rule(world, player, 'Bombos', 1)) + elif region_name in ['Hera Back', 'GT Petting Zoo', 'Mimic Cave']: + enemy_number = int(location.name.split('#')[1]) + if region_name == 'Mimic Cave': + if enemy_number in [4, 5]: # these are behind hammer blocks potentially + return and_rule(original_rule, has('Hammer', player)) + elif enemy_number in special_rules_check[region_name]: # these are behind rails + return and_rule(original_rule, has_boomerang(player)) + if enemy_number in special_rules_check[region_name]: + return and_rule(original_rule, has_boomerang(player)) + else: + return original_rule + elif region_name in ['TR Hub Ledges', 'Ice Hookshot Ledge', 'TR Dark Ride']: + enemy_number = int(location.name.split('#')[1]) + if special_rules_check[region_name] is None or enemy_number in special_rules_check[region_name]: + return and_rule(original_rule, or_rule(has_boomerang(player), has('Hookshot', player))) + else: + return original_rule + elif region_name in ['Old Man Cave (West)', 'Old Man Cave (East)', 'Old Man House Back', 'Old Man House', + 'Death Mountain Return Cave (left)', 'Death Mountain Return Cave (right)', + 'Hookshot Fairy']: + enemy_number = int(location.name.split('#')[1]) + if region_name == 'Death Mountain Return Cave (left)': + if enemy_number in [1, 5]: + return and_rule(original_rule, or_rule(has_boomerang(player), has('Hookshot', player))) + else: + return and_rule(original_rule, has('Hookshot', player)) + if special_rules_check[region_name] is None or enemy_number in special_rules_check[region_name]: + return and_rule(original_rule, has('Hookshot', player)) + else: + return original_rule + return original_rule + + +def has_blunt_weapon(player): + return or_rule(has_sword(player), has('Hammer', player)) + + +# Bombs, Arrows, Bombos, FireRod, Somaria, Byrna should be handled by the damage table logic +# Powder doesn't work (also handled by damage table) +def buzzblob_rule(player): + return or_rule(has('Golden Sword', player), + and_rule(has_blunt_weapon(player), + or_rule(has_boomerang(player), has('Hookshot', player), has('Ice Rod', player))), + and_rule(has('Ether', player), has_sword(player)), + and_rule(has('Quake', player), has_sword(player))) + + +def has_class_2_weapon(player): + return or_rule(has_beam_sword(player), has('Hammer', player)) + + +def somaria_rule(world, player, somaria_hits): + if somaria_hits == -1: + return has('Cane of Somaria', player) # insta-kill somaria? - not in vanilla + else: + magic_needed = math.ceil(somaria_hits / 64) * 8 # 64 hits per magic bar - 80 max? + if magic_needed > 8: + return and_rule(has('Cane of Somaria', player), can_extend_magic(world, player, magic_needed)) + else: + return has('Cane of Somaria', player) + + +def byrna_rule(world, player, byrna_hits): + if byrna_hits == -1: + return has('Cane of Byrna', player) # insta-kill byrna? - not in vanilla + else: + magic_needed = math.ceil(byrna_hits / 7) * 8 # 7 hits per magic bar - generous? + if magic_needed > 8: + return and_rule(has('Cane of Byrna', player), can_extend_magic(world, player, magic_needed)) + else: + return has('Cane of Byrna', player) + + +def bow_rule(world, player, arrows): + if arrows == -1 or 0 < arrows <= 25: + return can_shoot_normal_arrows(world, player) + return RuleFactory.static_rule(False) + + +def silvers_rule(world, player, arrows): + if arrows == -1 or 0 < arrows <= 25: + return can_shoot_silver_arrows(world, player) + return RuleFactory.static_rule(False) + + +def bombs_rule(world, player, bombs): + if bombs == -1 or 0 < bombs <= 8: + return can_use_bombs(world, player) + return RuleFactory.static_rule(False) + + +def ice_rod_rule(world, player, shots): + if shots == -1: + return has('Ice Rod', player) + if shots > 8: + return and_rule(has('Ice Rod', player), can_extend_magic(world, player, shots)) + else: + return has('Ice Rod', player) + + +def fire_rod_rule(world, player, shots): + if shots == -1: + return has('Fire Rod', player) + if shots > 8: + return and_rule(has('Fire Rod', player), can_extend_magic(world, player, shots)) + else: + return has('Fire Rod', player) + + +def magic_powder_rule(world, player, shots): + if shots == -1: + return has('Magic Powder', player) + if shots == -2: + # todo: other resources possible I guess - harder to keep track of though + return and_rule(has('Magic Powder', player), or_rule(has_blunt_weapon(player), has('Hookshot', player))) + magic_needed = math.ceil(shots / 16) * 8 # 16 tries per magic bar, that could be tight... + if magic_needed > 8: + return and_rule(has('Magic Powder', player), can_extend_magic(world, player, shots)) + else: + return has('Magic Powder', player) + + +def medallion_rule(world, player, medallion, shots): + if shots == -1: + return and_rule(has(medallion, player), has_sword(player)) + if shots == -2: + return and_rule(has(medallion, player), has_sword(player)) + magic_needed = shots * 2 + if magic_needed > 8: + return and_rule(has(medallion, player), has_sword(player), can_extend_magic(world, player, shots)) + else: + return and_rule(has(medallion, player), has_sword(player)) + + +def or_rule(*rules): + return RuleFactory.disj(rules) + + +def and_rule(*rules): + return RuleFactory.conj(rules) + + +def has(item, player, count=1): + return RuleFactory.item(item, player, count) + + +def has_sword(player): + return or_rule( + has('Fighter Sword', player), has('Master Sword', player), + has('Tempered Sword', player), has('Golden Sword', player) + ) + + +def has_beam_sword(player): + return or_rule( + has('Master Sword', player), has('Tempered Sword', player), has('Golden Sword', player) + ) + + +def has_class_3_sword(player): + return or_rule( + has('Tempered Sword', player), has('Golden Sword', player) + ) + + +def can_extend_magic(world, player, magic, flag_t=False): + potion_shops = (find_shops_that_sell('Blue Potion', world, player) | + find_shops_that_sell('Green Potion', world, player)) + return RuleFactory.extend_magic(player, magic, world.difficulty_adjustments[player], potion_shops, flag_t) + + +# class 0 damage (subtypes 1 and 2) +def has_boomerang(player): + return or_rule(has('Blue Boomerang', player), has('Red_Boomerang', player)) + + +def find_shops_that_sell(item, world, player): + return {shop.region for shop in world.shops[player] if shop.has_unlimited(item) and shop.region.player == player} + + +def can_shoot_normal_arrows(world, player): + if world.bow_mode[player].startswith('retro'): + shops = find_shops_that_sell('Single Arrow', world, player) + # retro+shopsanity, shops may not sell the Single Arrow at all + if world.bow_mode[player] == 'retro_silvers': + # non-progressive silvers grant wooden arrows, so shop may not be needed + return and_rule(has('Bow', player), or_rule(RuleFactory.unlimited('Single Arrow', player, shops), + has('Single Arrow', player), has('Silver Arrows', player))) + else: + return and_rule(has('Bow', player), or_rule(RuleFactory.unlimited('Single Arrow', player, shops), + has('Single Arrow', player))) + return has('Bow', player) + + +def can_shoot_silver_arrows(world, player): + # retro_silver requires the silver arrows item which is sufficient for the quiver + if world.bow_mode[player] == 'retro': + shops = find_shops_that_sell('Single Arrow', world, player) + # retro+shopsanity, shops may not sell the Single Arrow at all + return and_rule(has('Silver Arrows', player), or_rule(RuleFactory.unlimited('Single Arrow', player, shops), + has('Single Arrow', player))) + return and_rule(has('Bow', player), has('Silver Arrows', player)) + + +def can_use_bombs(world, player): + return or_rule(RuleFactory.static_rule(not world.bombbag[player]), has('Bomb Upgrade (+10)', player)) + + +def enemy_vulnerability(world, player, enemy_sprite, region): + damage_table = world.damage_table[player].damage_table + stats = world.data_tables[player].enemy_stats + damage_src = damage_table['DamageSource'] + sub_class_table = damage_table['SubClassTable'] + + enemy_sub_class = sub_class_table[enemy_sprite.kind] + + vulnerability = defaultdict(int) + + c1 = number_of_hits('Sword1', damage_src, enemy_sub_class, stats, enemy_sprite, region) + if c1 != 0: + if enemy_sprite.kind == EnemySprite.Buzzblob: + vulnerability['Stun'] = -1 + else: + vulnerability['Blunt'] = -1 + vulnerability['Master'] = -1 + vulnerability['Somaria'] = c1 + vulnerability['Byrna'] = c1 + else: + c2 = number_of_hits('Sword3', damage_src, enemy_sub_class, stats, enemy_sprite, region) + if c2 != 0: + vulnerability['Master'] = -1 # currently Lynels are only vulnerable to only master spins or above + hits = number_of_hits('Arrow', damage_src, enemy_sub_class, stats, enemy_sprite, region) + if hits != 0: + vulnerability['Bow'] = hits + hits = number_of_hits('SilverArrow', damage_src, enemy_sub_class, stats, enemy_sprite, region) + if hits != 0: + vulnerability['Silvers'] = hits + for method in ['Bomb', 'Hookshot', 'FireRod', 'IceRod', 'Boomerang', 'Powder', 'Bombos', 'Ether', 'Quake']: + hits = number_of_hits(method, damage_src, enemy_sub_class, stats, enemy_sprite, region) + if hits == -3: + if enemy_sprite.kind != EnemySprite.Buzzblob: # buzzblobs are special and don't die + vulnerability[method] = -1 + elif hits != 0: + vulnerability[method] = hits + return vulnerability + + +def number_of_hits(source_name, damage_src, enemy_sub_class, stats, enemy_sprite, region): + damage_class = damage_src[source_name]['class'] + sub_class = enemy_sub_class[damage_class] + damage_amount = damage_src[source_name]['subclass'][sub_class] + if damage_amount == 0: + return 0 + elif damage_amount <= 0x64: + health = stats[enemy_sprite.kind].health + if isinstance(health, tuple): + if enemy_sprite.kind in [EnemySprite.Tektite, EnemySprite.HardhatBeetle]: + idx = enemy_sprite.tile_x & 0x1 + health = health[idx] + elif region.is_light_world and region.is_dark_world: + health = min(health) + elif region.is_light_world: + health = health[0] + elif region.is_dark_world: + health = health[1] + else: + health = max(health) + return math.ceil(health / damage_amount) + elif damage_amount in [0xF9, 0xFA, 0xFD]: + # -1 incinerated; -2 blobbed, -3 fairy-ed (depends on enemy if "killed" or not) + # F9: fairy, defeated, but doesn't drop anything: -3 + # FA: blobbed - can you kill a blob? = -2 + # FD: incinerated + return special_classes[damage_amount] + else: + return 0 + + +special_classes = {0xF9: -3, 0xFA: -2, 0xFD: -1} + diff --git a/source/enemizer/OwEnemyList.py b/source/enemizer/OwEnemyList.py new file mode 100644 index 00000000..39f2b150 --- /dev/null +++ b/source/enemizer/OwEnemyList.py @@ -0,0 +1,1036 @@ +from source.dungeon.EnemyList import Sprite, EnemySprite +vanilla_sprites_ow = {} + + +def create_sprite(area_id, kind, tile_x, tile_y, region=None, address=None, fix=False, water=False, embed=False): + if area_id not in vanilla_sprites_ow: + vanilla_sprites_ow[area_id] = [] + sprite = Sprite(area_id, kind, 0, 0, tile_x, tile_y, region, False, None) + if water: + sprite.water = True + if fix: + sprite.static = True + if embed: + sprite.embedded = True + sprite.original_address = address + vanilla_sprites_ow[area_id].append(sprite) + + +def init_vanilla_sprites_ow(): + if vanilla_sprites_ow: + return + create_sprite(0x21b, EnemySprite.LightningGate, 0x1F, 0x06, '', 0x09CB42) + create_sprite(0x21b, EnemySprite.TutorialGuard, 0x01, 0x12, '', 0x09CB45) + create_sprite(0x21b, EnemySprite.TutorialGuard, 0x01, 0x14, '', 0x09CB48) + create_sprite(0x21b, EnemySprite.GreenGuard, 0x1F, 0x13, '', 0x09CB4B) + create_sprite(0x21b, EnemySprite.GreenKnifeGuard, 0x1F, 0x1A, '', 0x09CB4E) + create_sprite(0x21b, EnemySprite.GreenKnifeGuard, 0x20, 0x1A, '', 0x09CB51) + create_sprite(0x21b, EnemySprite.TutorialGuard, 0x2D, 0x25, '', 0x09CB54) + create_sprite(0x21b, EnemySprite.TutorialGuard, 0x20, 0x29, '', 0x09CB57) + create_sprite(0x21d, EnemySprite.Apple, 0x0B, 0x06, '', 0x09CB5B) + create_sprite(0x22b, EnemySprite.TutorialGuard, 0x09, 0x1E, '', 0x09CB5F) + create_sprite(0x22b, EnemySprite.TutorialGuard, 0x0B, 0x1E, '', 0x09CB62) + create_sprite(0x22c, EnemySprite.TutorialGuard, 0x1E, 0x18, '', 0x09CB66) + create_sprite(0x22c, EnemySprite.TutorialGuard, 0x1E, 0x1A, '', 0x09CB69) + create_sprite(0x22c, EnemySprite.TutorialGuard, 0x0D, 0x1E, '', 0x09CB6C) + create_sprite(0x22c, EnemySprite.TutorialGuard, 0x0F, 0x1E, '', 0x09CB6F) + create_sprite(0x232, EnemySprite.BombRefill1, 0x1A, 0x09, '', 0x09CB73) + create_sprite(0x232, EnemySprite.SmallHeart, 0x18, 0x12, '', 0x09CB76) + + # Screen40: + # dark world + create_sprite(0x40, EnemySprite.Ropa, 0x1A, 0x07, '', 0x09CB7A) + create_sprite(0x40, EnemySprite.Ropa, 0x12, 0x11, '', 0x09CB7D) + create_sprite(0x40, EnemySprite.Ropa, 0x0A, 0x1E, '', 0x09CB80) + create_sprite(0x40, EnemySprite.Ropa, 0x2F, 0x09, '', 0x09CB83) + create_sprite(0x40, EnemySprite.Snapdragon, 0x31, 0x0A, '', 0x09CB86) + create_sprite(0x40, EnemySprite.Ropa, 0x33, 0x0B, '', 0x09CB89) + create_sprite(0x40, EnemySprite.Ropa, 0x29, 0x14, '', 0x09CB8C) + create_sprite(0x40, EnemySprite.Ropa, 0x23, 0x16, '', 0x09CB8F) + create_sprite(0x40, EnemySprite.Pikit, 0x39, 0x17, '', 0x09CB92) + create_sprite(0x40, EnemySprite.Ropa, 0x0A, 0x21, '', 0x09CB95) + create_sprite(0x40, EnemySprite.Ropa, 0x1A, 0x25, '', 0x09CB98) + create_sprite(0x40, EnemySprite.Pikit, 0x0B, 0x28, '', 0x09CB9B) + create_sprite(0x40, EnemySprite.Ropa, 0x1E, 0x30, '', 0x09CB9E) + create_sprite(0x40, EnemySprite.Ropa, 0x0E, 0x38, '', 0x09CBA1) + create_sprite(0x40, EnemySprite.Ropa, 0x11, 0x38, '', 0x09CBA4) + create_sprite(0x40, EnemySprite.Ropa, 0x1A, 0x39, '', 0x09CBA7) + create_sprite(0x40, EnemySprite.Ropa, 0x2D, 0x21, '', 0x09CBAA) + create_sprite(0x40, EnemySprite.Ropa, 0x32, 0x28, '', 0x09CBAD) + create_sprite(0x40, EnemySprite.Ropa, 0x37, 0x32, '', 0x09CBB0) + create_sprite(0x40, EnemySprite.Pikit, 0x28, 0x37, '', 0x09CBB3) + # Screen42: + create_sprite(0x42, EnemySprite.Snapdragon, 0x0C, 0x11, '', 0x09CBB7) + create_sprite(0x42, EnemySprite.Snapdragon, 0x0C, 0x13, '', 0x09CBBA) + create_sprite(0x42, EnemySprite.Faerie, 0x06, 0x16, '', 0x09CBBD, fix=True) + create_sprite(0x42, EnemySprite.Moblin, 0x0E, 0x19, '', 0x09CBC0) + # Screen43: + create_sprite(0x43, EnemySprite.Waterfall, 0x2F, 0x0C, '', 0x09CBC4) + create_sprite(0x43, EnemySprite.BullyPinkBall, 0x20, 0x18, '', 0x09CBC7) + # Screen45: + create_sprite(0x45, EnemySprite.Lynel, 0x06, 0x0C, '', 0x09CBCB) + create_sprite(0x45, EnemySprite.Lynel, 0x1D, 0x0E, '', 0x09CBCE) + create_sprite(0x45, EnemySprite.Lynel, 0x20, 0x0B, '', 0x09CBD1) + # Screen47: + create_sprite(0x47, EnemySprite.RupeePull, 0x16, 0x14, '', 0x09CBD5) + # Screen4A: + create_sprite(0x4a, EnemySprite.RupeePull, 0x0E, 0x06, '', 0x09CBD9) + create_sprite(0x4a, EnemySprite.HeartPiece, 0x18, 0x08, '', 0x09CBDC) + create_sprite(0x4a, EnemySprite.Moblin, 0x0B, 0x0F, '', 0x09CBDF) + create_sprite(0x4a, EnemySprite.Moblin, 0x08, 0x10, '', 0x09CBE2) + create_sprite(0x4a, EnemySprite.Moblin, 0x16, 0x13, '', 0x09CBE5) + create_sprite(0x4a, EnemySprite.Raven, 0x13, 0x13, '', 0x09CBE8, embed=True) + create_sprite(0x4a, EnemySprite.Raven, 0x13, 0x14, '', 0x09CBEB, embed=True) + create_sprite(0x4a, EnemySprite.Ropa, 0x0E, 0x18, '', 0x09CBEE) + create_sprite(0x4a, EnemySprite.Stal, 0x14, 0x1A, '', 0x09CBF1) + # Screen4F: + create_sprite(0x4f, EnemySprite.FireballZora, 0x19, 0x08, '', 0x09CBF5, water=True) + create_sprite(0x4f, EnemySprite.Catfish, 0x04, 0x0B, '', 0x09CBF8) + create_sprite(0x4f, EnemySprite.Stal, 0x18, 0x0D, '', 0x09CBFB) + create_sprite(0x4f, EnemySprite.Ropa, 0x1A, 0x11, '', 0x09CBFE) + # Screen50: + create_sprite(0x50, EnemySprite.Poe, 0x16, 0x0B, '', 0x09CC02) + create_sprite(0x50, EnemySprite.Moblin, 0x05, 0x0C, '', 0x09CC05) + create_sprite(0x50, EnemySprite.TalkingTree, 0x08, 0x0E, '', 0x09CC08) + create_sprite(0x50, EnemySprite.Cucco, 0x19, 0x13, '', 0x09CC0B) + create_sprite(0x50, EnemySprite.Moblin, 0x08, 0x18, '', 0x09CC0E) + # Screen51: + create_sprite(0x51, EnemySprite.UsainBolt, 0x17, 0x0E, '', 0x09CC12) + create_sprite(0x51, EnemySprite.Faerie, 0x08, 0x10, '', 0x09CC15, fix=True) + create_sprite(0x51, EnemySprite.Faerie, 0x09, 0x10, '', 0x09CC18, fix=True) + create_sprite(0x51, EnemySprite.Stal, 0x1C, 0x15, '', 0x09CC1B) + create_sprite(0x51, EnemySprite.Moblin, 0x14, 0x16, '', 0x09CC1E) + create_sprite(0x51, EnemySprite.Moblin, 0x0E, 0x17, '', 0x09CC21) + # Screen52: + create_sprite(0x52, EnemySprite.Stal, 0x12, 0x09, '', 0x09CC25) + create_sprite(0x52, EnemySprite.Moblin, 0x15, 0x0D, '', 0x09CC28) + create_sprite(0x52, EnemySprite.BlueGuard, 0x07, 0x10, '', 0x09CC2B) + create_sprite(0x52, EnemySprite.BlueGuard, 0x14, 0x17, '', 0x09CC2E) + create_sprite(0x52, EnemySprite.Moblin, 0x0E, 0x18, '', 0x09CC31) + # Screen53: + create_sprite(0x53, EnemySprite.Stal, 0x06, 0x0B, '', 0x09CC35) + create_sprite(0x53, EnemySprite.Hinox, 0x15, 0x0C, '', 0x09CC38) + create_sprite(0x53, EnemySprite.Ropa, 0x08, 0x0D, '', 0x09CC3B) + create_sprite(0x53, EnemySprite.Moblin, 0x0D, 0x15, '', 0x09CC3E) + create_sprite(0x53, EnemySprite.Snapdragon, 0x16, 0x18, '', 0x09CC41) + # Screen54: + create_sprite(0x54, EnemySprite.Ropa, 0x14, 0x0D, '', 0x09CC45) + create_sprite(0x54, EnemySprite.BombRefill1, 0x05, 0x0B, '', 0x09CC48) + create_sprite(0x54, EnemySprite.RedRupee, 0x19, 0x0B, '', 0x09CC4B) + create_sprite(0x54, EnemySprite.Ropa, 0x07, 0x0F, '', 0x09CC4E) + create_sprite(0x54, EnemySprite.Faerie, 0x0F, 0x0E, '', 0x09CC51, fix=True) + create_sprite(0x54, EnemySprite.Ropa, 0x19, 0x10, '', 0x09CC54) + create_sprite(0x54, EnemySprite.Ropa, 0x0D, 0x14, '', 0x09CC57) + create_sprite(0x54, EnemySprite.Hinox, 0x11, 0x19, '', 0x09CC5A) + # Screen55: + create_sprite(0x55, EnemySprite.Whirlpool, 0x11, 0x09, '', 0x09CC5E) + create_sprite(0x55, EnemySprite.Hinox, 0x16, 0x0E, '', 0x09CC61) + create_sprite(0x55, EnemySprite.Stal, 0x18, 0x0E, '', 0x09CC64) + create_sprite(0x55, EnemySprite.BlueRupee, 0x1B, 0x0F, '', 0x09CC67) + create_sprite(0x55, EnemySprite.Hinox, 0x07, 0x17, '', 0x09CC6A) + create_sprite(0x55, EnemySprite.Bee, 0x0A, 0x1A, '', 0x09CC6D) + create_sprite(0x55, EnemySprite.Ropa, 0x1A, 0x1B, '', 0x09CC70) + # Screen56: + create_sprite(0x56, EnemySprite.FireballZora, 0x0A, 0x06, '', 0x09CC74, water=True) + create_sprite(0x56, EnemySprite.FireballZora, 0x13, 0x0A, '', 0x09CC77, water=True) + create_sprite(0x56, EnemySprite.Bee, 0x04, 0x0E, '', 0x09CC7A) + create_sprite(0x56, EnemySprite.Ropa, 0x11, 0x17, '', 0x09CC7D) + create_sprite(0x56, EnemySprite.Ropa, 0x05, 0x1A, '', 0x09CC80) + # Screen57: + create_sprite(0x57, EnemySprite.FireballZora, 0x0C, 0x04, '', 0x09CC84, water=True) + create_sprite(0x57, EnemySprite.Octorok, 0x16, 0x08, '', 0x09CC87) + create_sprite(0x57, EnemySprite.Octorok, 0x18, 0x0A, '', 0x09CC8A) + create_sprite(0x57, EnemySprite.Octorok, 0x0E, 0x0E, '', 0x09CC8D) + create_sprite(0x57, EnemySprite.Stal, 0x0E, 0x10, '', 0x09CC90) + create_sprite(0x57, EnemySprite.Octorok, 0x0E, 0x1A, '', 0x09CC93) + create_sprite(0x57, EnemySprite.Octorok, 0x0D, 0x1B, '', 0x09CC96) + # Screen58: + create_sprite(0x58, EnemySprite.Moblin, 0x13, 0x06, '', 0x09CC9A) + create_sprite(0x58, EnemySprite.TalkingTree, 0x18, 0x0C, '', 0x09CC9D) + create_sprite(0x58, EnemySprite.BlueGuard, 0x07, 0x1C, '', 0x09CCA0) + create_sprite(0x58, EnemySprite.Moblin, 0x35, 0x0A, '', 0x09CCA3) + create_sprite(0x58, EnemySprite.Poe, 0x2B, 0x0C, '', 0x09CCA6) + create_sprite(0x58, EnemySprite.Thief, 0x2E, 0x17, '', 0x09CCA9) + create_sprite(0x58, EnemySprite.ThievesTownGrate, 0x20, 0x1C, '', 0x09CCAC) + create_sprite(0x58, EnemySprite.Poe, 0x18, 0x25, '', 0x09CCAF) + create_sprite(0x58, EnemySprite.Thief, 0x0D, 0x27, '', 0x09CCB2) + create_sprite(0x58, EnemySprite.Poe, 0x1D, 0x28, '', 0x09CCB5) + create_sprite(0x58, EnemySprite.Moblin, 0x12, 0x2E, '', 0x09CCB8) + create_sprite(0x58, EnemySprite.Cucco, 0x16, 0x34, '', 0x09CCBB) + create_sprite(0x58, EnemySprite.Cucco, 0x15, 0x37, '', 0x09CCBE) + create_sprite(0x58, EnemySprite.Cucco, 0x28, 0x27, '', 0x09CCC1) + create_sprite(0x58, EnemySprite.BlueGuard, 0x33, 0x2F, '', 0x09CCC4) + create_sprite(0x58, EnemySprite.Poe, 0x2C, 0x34, '', 0x09CCC7) + create_sprite(0x58, EnemySprite.TalkingTree, 0x37, 0x35, '', 0x09CCCA) + # Screen5A: + create_sprite(0x5a, EnemySprite.Moblin, 0x0F, 0x08, '', 0x09CCCE) + create_sprite(0x5a, EnemySprite.TalkingTree, 0x12, 0x08, '', 0x09CCD1) + create_sprite(0x5a, EnemySprite.Stal, 0x12, 0x0D, '', 0x09CCD4) + create_sprite(0x5a, EnemySprite.Moblin, 0x15, 0x0C, '', 0x09CCD7) + create_sprite(0x5a, EnemySprite.Hinox, 0x0B, 0x0F, '', 0x09CCDA) + create_sprite(0x5a, EnemySprite.Moblin, 0x0E, 0x19, '', 0x09CCDD) + # Screen5B: + create_sprite(0x5b, EnemySprite.Ropa, 0x15, 0x17, '', 0x09CCE1) + create_sprite(0x5b, EnemySprite.HeartPiece, 0x34, 0x12, '', 0x09CCE4) + create_sprite(0x5b, EnemySprite.RupeePull, 0x13, 0x24, '', 0x09CCE7) + create_sprite(0x5b, EnemySprite.Moblin, 0x0F, 0x27, '', 0x09CCEA) + create_sprite(0x5b, EnemySprite.Faerie, 0x17, 0x2A, '', 0x09CCED, fix=True) + create_sprite(0x5b, EnemySprite.Moblin, 0x0C, 0x2A, '', 0x09CCF0) + create_sprite(0x5b, EnemySprite.Hinox, 0x1E, 0x2C, '', 0x09CCF3) + create_sprite(0x5b, EnemySprite.Snapdragon, 0x34, 0x25, '', 0x09CCF6) + create_sprite(0x5b, EnemySprite.Moblin, 0x32, 0x27, '', 0x09CCF9) + create_sprite(0x5b, EnemySprite.Moblin, 0x30, 0x29, '', 0x09CCFC) + create_sprite(0x5b, EnemySprite.Hinox, 0x21, 0x2C, '', 0x09CCFF) + # Screen5D: + create_sprite(0x5d, EnemySprite.TalkingTree, 0x0B, 0x08, '', 0x09CD03) + create_sprite(0x5d, EnemySprite.Hinox, 0x07, 0x09, '', 0x09CD06) + create_sprite(0x5d, EnemySprite.Snapdragon, 0x06, 0x0B, '', 0x09CD09) + create_sprite(0x5d, EnemySprite.Moblin, 0x18, 0x0B, '', 0x09CD0C) + create_sprite(0x5d, EnemySprite.Stal, 0x17, 0x0E, '', 0x09CD0F) + create_sprite(0x5d, EnemySprite.Moblin, 0x1A, 0x10, '', 0x09CD12) + create_sprite(0x5d, EnemySprite.BlueGuard, 0x08, 0x11, '', 0x09CD15) + # Screen5E: + create_sprite(0x5e, EnemySprite.Ropa, 0x0D, 0x04, '', 0x09CD19) + create_sprite(0x5e, EnemySprite.Ropa, 0x03, 0x11, '', 0x09CD1C) + create_sprite(0x5e, EnemySprite.Kiki, 0x15, 0x11, '', 0x09CD1F) + create_sprite(0x5e, EnemySprite.Ropa, 0x12, 0x1A, '', 0x09CD22) + create_sprite(0x5e, EnemySprite.Ropa, 0x27, 0x09, '', 0x09CD25) + create_sprite(0x5e, EnemySprite.Ropa, 0x2F, 0x10, '', 0x09CD28) + create_sprite(0x5e, EnemySprite.Moblin, 0x25, 0x15, '', 0x09CD2B) + create_sprite(0x5e, EnemySprite.Hinox, 0x26, 0x17, '', 0x09CD2E) + create_sprite(0x5e, EnemySprite.Ropa, 0x35, 0x18, '', 0x09CD31) + create_sprite(0x5e, EnemySprite.Ropa, 0x2A, 0x1E, '', 0x09CD34) + create_sprite(0x5e, EnemySprite.Moblin, 0x0A, 0x26, '', 0x09CD37) + create_sprite(0x5e, EnemySprite.Moblin, 0x0C, 0x2B, '', 0x09CD3A) + create_sprite(0x5e, EnemySprite.Ropa, 0x07, 0x35, '', 0x09CD3D) + create_sprite(0x5e, EnemySprite.Hinox, 0x16, 0x37, '', 0x09CD40) + create_sprite(0x5e, EnemySprite.Snapdragon, 0x09, 0x38, '', 0x09CD43) + create_sprite(0x5e, EnemySprite.Moblin, 0x32, 0x24, '', 0x09CD46) + create_sprite(0x5e, EnemySprite.Snapdragon, 0x35, 0x28, '', 0x09CD49) + create_sprite(0x5e, EnemySprite.Ropa, 0x24, 0x30, '', 0x09CD4C) + create_sprite(0x5e, EnemySprite.Faerie, 0x30, 0x30, '', 0x09CD4F, fix=True) + create_sprite(0x5e, EnemySprite.Hinox, 0x35, 0x36, '', 0x09CD52) + create_sprite(0x5e, EnemySprite.Raven, 0x29, 0x37, '', 0x09CD55, embed=True) + # Screen62: + create_sprite(0x62, EnemySprite.PurpleChest, 0x0D, 0x05, '', 0x09CD59) + create_sprite(0x62, EnemySprite.Cucco, 0x13, 0x11, '', 0x09CD5C) + create_sprite(0x62, EnemySprite.Cucco, 0x11, 0x13, '', 0x09CD5F) + create_sprite(0x62, EnemySprite.Cucco, 0x15, 0x15, '', 0x09CD62) + create_sprite(0x62, EnemySprite.Cucco, 0x09, 0x16, '', 0x09CD65) + create_sprite(0x62, EnemySprite.Cucco, 0x11, 0x17, '', 0x09CD68) + # Screen65: + create_sprite(0x65, EnemySprite.Moblin, 0x13, 0x07, '', 0x09CD6C) + create_sprite(0x65, EnemySprite.Stal, 0x0F, 0x0A, '', 0x09CD6F) + create_sprite(0x65, EnemySprite.Moblin, 0x0E, 0x0C, '', 0x09CD72) + create_sprite(0x65, EnemySprite.Hinox, 0x05, 0x11, '', 0x09CD75) + create_sprite(0x65, EnemySprite.Moblin, 0x0A, 0x16, '', 0x09CD78) + create_sprite(0x65, EnemySprite.Moblin, 0x13, 0x16, '', 0x09CD7B) + # Screen68: + create_sprite(0x68, EnemySprite.DiggingGameNPC, 0x0E, 0x11, '', 0x09CD7F) + # Screen69: + create_sprite(0x69, EnemySprite.Smithy, 0x06, 0x09, '', 0x09CD83) + # Screen6A: + create_sprite(0x6a, EnemySprite.FluteKid, 0x0E, 0x0F, '', 0x09CD87) + # Screen6B: + create_sprite(0x6b, EnemySprite.TalkingTree, 0x16, 0x08, '', 0x09CD8B) + create_sprite(0x6b, EnemySprite.Ropa, 0x08, 0x09, '', 0x09CD8E) + create_sprite(0x6b, EnemySprite.TalkingTree, 0x17, 0x0F, '', 0x09CD91) + create_sprite(0x6b, EnemySprite.Moblin, 0x13, 0x16, '', 0x09CD94) + create_sprite(0x6b, EnemySprite.Moblin, 0x0F, 0x19, '', 0x09CD97) + # Screen6C: + create_sprite(0x6c, EnemySprite.Snapdragon, 0x15, 0x06, '', 0x09CD9B) + create_sprite(0x6c, EnemySprite.Moblin, 0x15, 0x0A, '', 0x09CD9E) + create_sprite(0x6c, EnemySprite.Moblin, 0x14, 0x0D, '', 0x09CDA1) + create_sprite(0x6c, EnemySprite.Hinox, 0x14, 0x16, '', 0x09CDA4) + create_sprite(0x6c, EnemySprite.Ropa, 0x09, 0x19, '', 0x09CDA7) + # Screen6D: + create_sprite(0x6d, EnemySprite.Ropa, 0x0F, 0x05, '', 0x09CDAB) + create_sprite(0x6d, EnemySprite.Hinox, 0x0D, 0x07, '', 0x09CDAE) + create_sprite(0x6d, EnemySprite.BlueGuard, 0x12, 0x08, '', 0x09CDB1) + create_sprite(0x6d, EnemySprite.BlueGuard, 0x10, 0x0A, '', 0x09CDB4) + create_sprite(0x6d, EnemySprite.Stal, 0x10, 0x1A, '', 0x09CDB7) + create_sprite(0x6d, EnemySprite.Stal, 0x13, 0x1B, '', 0x09CDBA) + # Screen6E: + create_sprite(0x6e, EnemySprite.BlueRupee, 0x0C, 0x08, '', 0x09CDBE) + create_sprite(0x6e, EnemySprite.Bee, 0x10, 0x09, '', 0x09CDC1) + create_sprite(0x6e, EnemySprite.Apple, 0x14, 0x0A, '', 0x09CDC4) + create_sprite(0x6e, EnemySprite.BlueGuard, 0x08, 0x0B, '', 0x09CDC7) + create_sprite(0x6e, EnemySprite.BlueGuard, 0x10, 0x0E, '', 0x09CDCA) + create_sprite(0x6e, EnemySprite.Ropa, 0x1A, 0x18, '', 0x09CDCD) + # Screen6F: + create_sprite(0x6f, EnemySprite.Snapdragon, 0x0D, 0x08, '', 0x09CDD1) + create_sprite(0x6f, EnemySprite.Snapdragon, 0x0F, 0x08, '', 0x09CDD4) + create_sprite(0x6f, EnemySprite.Snapdragon, 0x0E, 0x0B, '', 0x09CDD7) + create_sprite(0x6f, EnemySprite.Raven, 0x17, 0x0C, '', 0x09CDDA, embed=True) + create_sprite(0x6f, EnemySprite.Stal, 0x09, 0x17, '', 0x09CDDD) + # Screen70: + create_sprite(0x70, EnemySprite.Raven, 0x21, 0x1B, '', 0x09CDE1, embed=True) + create_sprite(0x70, EnemySprite.FireballZora, 0x2B, 0x1C, '', 0x09CDE4, water=True) + create_sprite(0x70, EnemySprite.FireballZora, 0x12, 0x21, '', 0x09CDE7, water=True) + create_sprite(0x70, EnemySprite.Swamola, 0x1B, 0x24, '', 0x09CDEA, water=True) + create_sprite(0x70, EnemySprite.Swamola, 0x10, 0x27, '', 0x09CDED, water=True) + create_sprite(0x70, EnemySprite.Raven, 0x07, 0x28, '', 0x09CDF0, embed=True) + create_sprite(0x70, EnemySprite.FireballZora, 0x16, 0x2B, '', 0x09CDF3, water=True) + create_sprite(0x70, EnemySprite.FireballZora, 0x1E, 0x2E, '', 0x09CDF6, water=True) + create_sprite(0x70, EnemySprite.Swamola, 0x17, 0x33, '', 0x09CDF9, water=True) + create_sprite(0x70, EnemySprite.FireballZora, 0x11, 0x38, '', 0x09CDFC, water=True) + create_sprite(0x70, EnemySprite.FireballZora, 0x23, 0x2B, '', 0x09CDFF, water=True) + create_sprite(0x70, EnemySprite.Swamola, 0x27, 0x2C, '', 0x09CE02, water=True) + # Screen72: + create_sprite(0x72, EnemySprite.TalkingTree, 0x1B, 0x0B, '', 0x09CE06) + create_sprite(0x72, EnemySprite.BlueGuard, 0x10, 0x0D, '', 0x09CE09) + create_sprite(0x72, EnemySprite.BlueGuard, 0x13, 0x0E, '', 0x09CE0C) + create_sprite(0x72, EnemySprite.TalkingTree, 0x1A, 0x14, '', 0x09CE0F) + create_sprite(0x72, EnemySprite.Ropa, 0x0B, 0x17, '', 0x09CE12) + # Screen73: + create_sprite(0x73, EnemySprite.Pikit, 0x17, 0x0C, '', 0x09CE16) + create_sprite(0x73, EnemySprite.Moblin, 0x09, 0x0D, '', 0x09CE19) + create_sprite(0x73, EnemySprite.GreenZirro, 0x14, 0x0E, '', 0x09CE1C) + create_sprite(0x73, EnemySprite.GreenZirro, 0x15, 0x1A, '', 0x09CE1F) + create_sprite(0x73, EnemySprite.BlueZirro, 0x1B, 0x1B, '', 0x09CE22) + # Screen74: + create_sprite(0x74, EnemySprite.Moblin, 0x0B, 0x0D, '', 0x09CE26) + create_sprite(0x74, EnemySprite.RupeePull, 0x17, 0x0E, '', 0x09CE29) + create_sprite(0x74, EnemySprite.GreenZirro, 0x10, 0x11, '', 0x09CE2C) + create_sprite(0x74, EnemySprite.Moblin, 0x15, 0x11, '', 0x09CE2F) + create_sprite(0x74, EnemySprite.Pikit, 0x0A, 0x12, '', 0x09CE32) + create_sprite(0x74, EnemySprite.Apple, 0x0E, 0x14, '', 0x09CE35) + create_sprite(0x74, EnemySprite.Moblin, 0x11, 0x17, '', 0x09CE38) + # Screen75: + create_sprite(0x75, EnemySprite.Stal, 0x0A, 0x05, '', 0x09CE3C) + create_sprite(0x75, EnemySprite.BlueGuard, 0x09, 0x07, '', 0x09CE3F) + create_sprite(0x75, EnemySprite.BlueGuard, 0x0B, 0x09, '', 0x09CE42) + create_sprite(0x75, EnemySprite.Octorok, 0x07, 0x13, '', 0x09CE45) + create_sprite(0x75, EnemySprite.GreenZirro, 0x18, 0x16, '', 0x09CE48, water=True) + create_sprite(0x75, EnemySprite.Pikit, 0x09, 0x17, '', 0x09CE4B) + create_sprite(0x75, EnemySprite.FireballZora, 0x30, 0x0C, '', 0x09CE4E, water=True) + create_sprite(0x75, EnemySprite.BlueZirro, 0x29, 0x11, '', 0x09CE51) + create_sprite(0x75, EnemySprite.GreenZirro, 0x36, 0x15, '', 0x09CE54) + create_sprite(0x75, EnemySprite.Pikit, 0x31, 0x1F, '', 0x09CE57) + create_sprite(0x75, EnemySprite.FireballZora, 0x1B, 0x22, '', 0x09CE5A, water=True) + create_sprite(0x75, EnemySprite.GreenZirro, 0x14, 0x28, '', 0x09CE5D, water=True) + create_sprite(0x75, EnemySprite.Pikit, 0x16, 0x2E, '', 0x09CE60) + create_sprite(0x75, EnemySprite.GreenZirro, 0x19, 0x32, '', 0x09CE63) + create_sprite(0x75, EnemySprite.BlueZirro, 0x0A, 0x35, '', 0x09CE66) + create_sprite(0x75, EnemySprite.Ropa, 0x08, 0x39, '', 0x09CE69) + create_sprite(0x75, EnemySprite.FireballZora, 0x1B, 0x39, '', 0x09CE6C, water=True) + create_sprite(0x75, EnemySprite.Pikit, 0x2A, 0x26, '', 0x09CE6F) + create_sprite(0x75, EnemySprite.GreenZirro, 0x32, 0x28, '', 0x09CE72) + create_sprite(0x75, EnemySprite.FireballZora, 0x2A, 0x2C, '', 0x09CE75, water=True) + create_sprite(0x75, EnemySprite.FireballZora, 0x32, 0x35, '', 0x09CE78, water=True) + create_sprite(0x75, EnemySprite.Octorok, 0x37, 0x39, '', 0x09CE7B) + # Screen77: + create_sprite(0x77, EnemySprite.Octorok, 0x11, 0x08, '', 0x09CE7F) + create_sprite(0x77, EnemySprite.Stal, 0x09, 0x0A, '', 0x09CE82) + create_sprite(0x77, EnemySprite.BlueZirro, 0x0D, 0x0B, '', 0x09CE85) + create_sprite(0x77, EnemySprite.Octorok, 0x18, 0x11, '', 0x09CE88) + create_sprite(0x77, EnemySprite.FireballZora, 0x07, 0x12, '', 0x09CE8B, water=True) + create_sprite(0x77, EnemySprite.FireballZora, 0x12, 0x19, '', 0x09CE8E, water=True) + # Screen7A: + create_sprite(0x7a, EnemySprite.Hinox, 0x06, 0x07, '', 0x09CE92) + create_sprite(0x7a, EnemySprite.Ropa, 0x16, 0x09, '', 0x09CE95) + create_sprite(0x7a, EnemySprite.Ropa, 0x14, 0x0B, '', 0x09CE98) + create_sprite(0x7a, EnemySprite.Ropa, 0x16, 0x0B, '', 0x09CE9B) + # Screen7B: + create_sprite(0x7b, EnemySprite.BlueZirro, 0x12, 0x06, '', 0x09CE9F) + create_sprite(0x7b, EnemySprite.Pikit, 0x16, 0x0A, '', 0x09CEA2) + create_sprite(0x7b, EnemySprite.Moblin, 0x0D, 0x0F, '', 0x09CEA5) + create_sprite(0x7b, EnemySprite.Stal, 0x0A, 0x10, '', 0x09CEA8) + create_sprite(0x7b, EnemySprite.Moblin, 0x13, 0x14, '', 0x09CEAB) + create_sprite(0x7b, EnemySprite.Ropa, 0x16, 0x18, '', 0x09CEAE) + # Screen7C: + create_sprite(0x7c, EnemySprite.Stal, 0x03, 0x05, '', 0x09CEB2) + create_sprite(0x7c, EnemySprite.BlueGuard, 0x07, 0x06, '', 0x09CEB5) + create_sprite(0x7c, EnemySprite.RupeePull, 0x0F, 0x06, '', 0x09CEB8) + create_sprite(0x7c, EnemySprite.Hinox, 0x11, 0x11, '', 0x09CEBB) + create_sprite(0x7c, EnemySprite.Ropa, 0x18, 0x15, '', 0x09CEBE) + create_sprite(0x7c, EnemySprite.Ropa, 0x16, 0x19, '', 0x09CEC1) + # Screen7F: + create_sprite(0x7f, EnemySprite.GreenZirro, 0x10, 0x06, '', 0x09CEC5) + create_sprite(0x7f, EnemySprite.Octorok, 0x16, 0x06, '', 0x09CEC8) + create_sprite(0x7f, EnemySprite.Whirlpool, 0x07, 0x0C, '', 0x09CECB) + create_sprite(0x7f, EnemySprite.FireballZora, 0x07, 0x0E, '', 0x09CECE, water=True) + create_sprite(0x7f, EnemySprite.GreenZirro, 0x0D, 0x13, '', 0x09CED1) + create_sprite(0x7f, EnemySprite.Pikit, 0x16, 0x14, '', 0x09CED4) + create_sprite(0x7f, EnemySprite.Octorok, 0x0F, 0x17, '', 0x09CED7) + # Screen80: + create_sprite(0x80, EnemySprite.MasterSword, 0x07, 0x08, '', 0x09CEDB) + create_sprite(0x80, EnemySprite.PedestalPlaque, 0x07, 0x0A, '', 0x09CEDE) + create_sprite(0x80, EnemySprite.LostWoodsSquirrel, 0x0F, 0x14, '', 0x09CEE1) + create_sprite(0x80, EnemySprite.LostWoodsBird, 0x00, 0x16, '', 0x09CEE4) + create_sprite(0x80, EnemySprite.LostWoodsSquirrel, 0x02, 0x18, '', 0x09CEE7) + create_sprite(0x80, EnemySprite.LostWoodsBird, 0x0E, 0x1A, '', 0x09CEEA) + create_sprite(0x80, EnemySprite.LostWoodsSquirrel, 0x0F, 0x1B, '', 0x09CEED) + create_sprite(0x80, EnemySprite.Hobo, 0x16, 0x04, '', 0x09CEF0) + # Screen81: + create_sprite(0x81, EnemySprite.HeartPiece, 0x1B, 0x26, '', 0x09CEF4) + create_sprite(0x81, EnemySprite.Zora, 0x0A, 0x06, '', 0x09CEF7, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x1C, 0x06, '', 0x09CEFA, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x11, 0x07, '', 0x09CEFD, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x16, 0x0A, '', 0x09CF00, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x1A, 0x0A, '', 0x09CF03, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x09, 0x0C, '', 0x09CF06, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x12, 0x0D, '', 0x09CF09, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x1A, 0x12, '', 0x09CF0C, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x07, 0x13, '', 0x09CF0F, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x14, 0x13, '', 0x09CF12, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x08, 0x18, '', 0x09CF15, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x04, 0x1C, '', 0x09CF18, water=True) + create_sprite(0x81, EnemySprite.KingZora, 0x3B, 0x04, '', 0x09CF1B) + create_sprite(0x81, EnemySprite.FireballZora, 0x27, 0x08, '', 0x09CF1E, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x2D, 0x08, '', 0x09CF21, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x22, 0x0E, '', 0x09CF24, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x2D, 0x0E, '', 0x09CF27, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x21, 0x14, '', 0x09CF2A, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x0D, 0x20, '', 0x09CF2D, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x08, 0x31, '', 0x09CF30, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x14, 0x31, '', 0x09CF33, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x0C, 0x33, '', 0x09CF36, water=True) + create_sprite(0x81, EnemySprite.FireballZora, 0x0E, 0x35, '', 0x09CF39, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x08, 0x38, '', 0x09CF3C, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x3B, 0x28, '', 0x09CF3F, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x3A, 0x2B, '', 0x09CF42, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x2D, 0x35, '', 0x09CF45, water=True) + create_sprite(0x81, EnemySprite.Zora, 0x37, 0x36, '', 0x09CF48, water=True) + # Screen00_1: + create_sprite(0x0, EnemySprite.FakeMasterSword, 0x07, 0x12, '', 0x09CF4C) + create_sprite(0x0, EnemySprite.Raven, 0x12, 0x0B, '', 0x09CF4F, embed=True) + create_sprite(0x0, EnemySprite.Mushroom, 0x1E, 0x15, '', 0x09CF52) + create_sprite(0x0, EnemySprite.FakeMasterSword, 0x28, 0x06, '', 0x09CF55) + create_sprite(0x0, EnemySprite.Buzzblob, 0x31, 0x0A, '', 0x09CF58) + create_sprite(0x0, EnemySprite.Raven, 0x2D, 0x0A, '', 0x09CF5B) + create_sprite(0x0, EnemySprite.Hoarder, 0x2A, 0x10, '', 0x09CF5E) + create_sprite(0x0, EnemySprite.FakeMasterSword, 0x39, 0x15, '', 0x09CF61) + create_sprite(0x0, EnemySprite.Thief, 0x0E, 0x22, '', 0x09CF64) + create_sprite(0x0, EnemySprite.Hoarder, 0x1E, 0x2D, '', 0x09CF67) + create_sprite(0x0, EnemySprite.Bee, 0x29, 0x25, '', 0x09CF6A) + create_sprite(0x0, EnemySprite.RupeePull, 0x2A, 0x27, '', 0x09CF6D) + create_sprite(0x0, EnemySprite.Raven, 0x36, 0x2D, '', 0x09CF70) + create_sprite(0x0, EnemySprite.FakeMasterSword, 0x25, 0x35, '', 0x09CF73) + create_sprite(0x0, EnemySprite.Thief, 0x29, 0x35, '', 0x09CF76) + # Screen02_1: + create_sprite(0x2, EnemySprite.Buzzblob, 0x04, 0x13, '', 0x09CF7A) + create_sprite(0x2, EnemySprite.Lumberjacks, 0x0C, 0x13, '', 0x09CF7D) + create_sprite(0x2, EnemySprite.Hoarder, 0x0D, 0x1A, '', 0x09CF80) + # Screen03_1: + create_sprite(0x3, EnemySprite.Boulders, 0x00, 0x00, '', 0x09CF84) + create_sprite(0x3, EnemySprite.MedallionTablet, 0x0B, 0x04, '', 0x09CF87) + create_sprite(0x3, EnemySprite.Deadrock, 0x27, 0x0C, '', 0x09CF8A) + create_sprite(0x3, EnemySprite.HeartPiece, 0x22, 0x16, '', 0x09CF8D) + create_sprite(0x3, EnemySprite.Deadrock, 0x0A, 0x35, '', 0x09CF90) + create_sprite(0x3, EnemySprite.Deadrock, 0x06, 0x36, '', 0x09CF93) + create_sprite(0x3, EnemySprite.Deadrock, 0x0D, 0x3B, '', 0x09CF96) + create_sprite(0x3, EnemySprite.PositionTarget, 0x12, 0x3B, '', 0x09CF99) + create_sprite(0x3, EnemySprite.Deadrock, 0x2C, 0x2D, '', 0x09CF9C) + create_sprite(0x3, EnemySprite.Deadrock, 0x34, 0x33, '', 0x09CF9F) + create_sprite(0x3, EnemySprite.Deadrock, 0x2F, 0x34, '', 0x09CFA2) + # Screen05_1: + create_sprite(0x5, EnemySprite.Deadrock, 0x1E, 0x0E, '', 0x09CFA6) + create_sprite(0x5, EnemySprite.Tektite, 0x1F, 0x0F, '', 0x09CFA9) + create_sprite(0x5, EnemySprite.HeartPiece, 0x2F, 0x03, '', 0x09CFAC) + create_sprite(0x5, EnemySprite.Deadrock, 0x35, 0x0D, '', 0x09CFAF) + create_sprite(0x5, EnemySprite.Tektite, 0x29, 0x0F, '', 0x09CFB2) + create_sprite(0x5, EnemySprite.Deadrock, 0x35, 0x0F, '', 0x09CFB5) + create_sprite(0x5, EnemySprite.Faerie, 0x34, 0x10, '', 0x09CFB8, fix=True) + create_sprite(0x5, EnemySprite.Tektite, 0x1E, 0x31, '', 0x09CFBB) + create_sprite(0x5, EnemySprite.Tektite, 0x35, 0x2A, '', 0x09CFBE) + create_sprite(0x5, EnemySprite.Deadrock, 0x2A, 0x2F, '', 0x09CFC1) + create_sprite(0x5, EnemySprite.Tektite, 0x2F, 0x2F, '', 0x09CFC4) + create_sprite(0x5, EnemySprite.Deadrock, 0x29, 0x36, '', 0x09CFC7) + create_sprite(0x5, EnemySprite.Deadrock, 0x36, 0x36, '', 0x09CFCA) + # Screen07_1: + create_sprite(0x7, EnemySprite.Deadrock, 0x0E, 0x07, '', 0x09CFCE) + create_sprite(0x7, EnemySprite.Deadrock, 0x0A, 0x0D, '', 0x09CFD1) + create_sprite(0x7, EnemySprite.Deadrock, 0x17, 0x15, '', 0x09CFD4) + create_sprite(0x7, EnemySprite.Deadrock, 0x0F, 0x16, '', 0x09CFD7) + create_sprite(0x7, EnemySprite.Deadrock, 0x12, 0x16, '', 0x09CFDA) + # Screen0A_1: + create_sprite(0xa, EnemySprite.Bee, 0x0E, 0x04, '', 0x09CFDE) + create_sprite(0xa, EnemySprite.RupeePull, 0x0E, 0x06, '', 0x09CFE1) + create_sprite(0xa, EnemySprite.Raven, 0x05, 0x09, '', 0x09CFE4) + create_sprite(0xa, EnemySprite.Buzzblob, 0x10, 0x0D, '', 0x09CFE7) + create_sprite(0xa, EnemySprite.Buzzblob, 0x0B, 0x0E, '', 0x09CFEA) + create_sprite(0xa, EnemySprite.Raven, 0x13, 0x16, '', 0x09CFED, embed=True) + create_sprite(0xa, EnemySprite.Hoarder, 0x0E, 0x16, '', 0x09CFF0) + create_sprite(0xa, EnemySprite.Buzzblob, 0x16, 0x16, '', 0x09CFF3) + create_sprite(0xa, EnemySprite.Raven, 0x11, 0x17, '', 0x09CFF6, embed=True) + create_sprite(0xa, EnemySprite.Apple, 0x19, 0x1A, '', 0x09CFF9) + # Screen0F_1: + create_sprite(0xf, EnemySprite.Waterfall, 0x06, 0x02, '', 0x09CFFD) + create_sprite(0xf, EnemySprite.Crab, 0x0D, 0x0D, '', 0x09D000) + create_sprite(0xf, EnemySprite.FireballZora, 0x05, 0x10, '', 0x09D003, water=True) + create_sprite(0xf, EnemySprite.Crab, 0x11, 0x12, '', 0x09D006) + create_sprite(0xf, EnemySprite.Whirlpool, 0x08, 0x13, '', 0x09D009) + create_sprite(0xf, EnemySprite.Raven, 0x1C, 0x15, '', 0x09D00C, embed=True) + create_sprite(0xf, EnemySprite.Octorok4Way, 0x0E, 0x17, '', 0x09D00F) + # Screen10_1: + create_sprite(0x10, EnemySprite.GreenGuard, 0x05, 0x0C, '', 0x09D013) + create_sprite(0x10, EnemySprite.Apple, 0x07, 0x0C, '', 0x09D016) + create_sprite(0x10, EnemySprite.LargeMagic, 0x17, 0x0F, '', 0x09D019) + create_sprite(0x10, EnemySprite.GreenGuard, 0x08, 0x18, '', 0x09D01C) + # Screen11_1: + create_sprite(0x11, EnemySprite.GreenGuard, 0x17, 0x0C, '', 0x09D020) + create_sprite(0x11, EnemySprite.GreenGuard, 0x1A, 0x0D, '', 0x09D023) + create_sprite(0x11, EnemySprite.BombRefill1, 0x08, 0x10, '', 0x09D026) + create_sprite(0x11, EnemySprite.Cucco, 0x08, 0x17, '', 0x09D029) + # Screen12_1: + create_sprite(0x12, EnemySprite.GreenGuard, 0x15, 0x0E, '', 0x09D02D) + create_sprite(0x12, EnemySprite.GreenGuard, 0x07, 0x10, '', 0x09D030) + create_sprite(0x12, EnemySprite.Whirlpool, 0x0F, 0x10, '', 0x09D033) + create_sprite(0x12, EnemySprite.GreenGuard, 0x15, 0x15, '', 0x09D036) + # Screen13_1: + create_sprite(0x13, EnemySprite.Apple, 0x18, 0x09, '', 0x09D03A) + create_sprite(0x13, EnemySprite.GreenGuard, 0x11, 0x17, '', 0x09D03D) + # Screen14_1: + create_sprite(0x14, EnemySprite.RedJavelinGuard, 0x15, 0x11, '', 0x09D041) + create_sprite(0x14, EnemySprite.GreenGuard, 0x11, 0x19, '', 0x09D044) + create_sprite(0x14, EnemySprite.Medusa, 0x08, 0x0C, '', 0x09D047) + create_sprite(0x14, EnemySprite.Medusa, 0x17, 0x11, '', 0x09D04A) + create_sprite(0x14, EnemySprite.Medusa, 0x12, 0x0E, '', 0x09D04D) + # Screen15_1: + create_sprite(0x15, EnemySprite.Whirlpool, 0x11, 0x09, '', 0x09D051) + create_sprite(0x15, EnemySprite.BlueGuard, 0x16, 0x0E, '', 0x09D054) + create_sprite(0x15, EnemySprite.Faerie, 0x1B, 0x0F, '', 0x09D057, fix=True) + create_sprite(0x15, EnemySprite.BlueGuard, 0x0B, 0x17, '', 0x09D05A) + # Screen16_1: + create_sprite(0x16, EnemySprite.Buzzblob, 0x0D, 0x0A, '', 0x09D05E) + create_sprite(0x16, EnemySprite.Witch, 0x0F, 0x15, '', 0x09D061) + create_sprite(0x16, EnemySprite.Buzzblob, 0x06, 0x18, '', 0x09D064) + # Screen17_1: + create_sprite(0x17, EnemySprite.Buzzblob, 0x18, 0x08, '', 0x09D068) + create_sprite(0x17, EnemySprite.Buzzblob, 0x17, 0x0A, '', 0x09D06B) + create_sprite(0x17, EnemySprite.Buzzblob, 0x0D, 0x0B, '', 0x09D06E) + create_sprite(0x17, EnemySprite.Buzzblob, 0x16, 0x0C, '', 0x09D071) + create_sprite(0x17, EnemySprite.Buzzblob, 0x08, 0x16, '', 0x09D074) + # Screen18_1: + create_sprite(0x18, EnemySprite.Faerie, 0x18, 0x0A, '', 0x09D078, fix=True) + create_sprite(0x18, EnemySprite.PositionTarget, 0x0C, 0x17, '', 0x09D07B) + create_sprite(0x18, EnemySprite.BottleMerchant, 0x18, 0x16, '', 0x09D07E) + create_sprite(0x18, EnemySprite.OldSnitch, 0x0E, 0x1C, '', 0x09D081) + create_sprite(0x18, EnemySprite.FluteQuest, 0x20, 0x18, '', 0x09D084) + create_sprite(0x18, EnemySprite.PositionTarget, 0x34, 0x1B, '', 0x09D087) + create_sprite(0x18, EnemySprite.RunningNpc, 0x1D, 0x2E, '', 0x09D08A) + create_sprite(0x18, EnemySprite.SweepingLady, 0x19, 0x2C, '', 0x09D08D) + create_sprite(0x18, EnemySprite.KidInKak, 0x18, 0x31, '', 0x09D090) + create_sprite(0x18, EnemySprite.Cucco, 0x16, 0x35, '', 0x09D093) + create_sprite(0x18, EnemySprite.Cucco, 0x18, 0x36, '', 0x09D096) + create_sprite(0x18, EnemySprite.YoungSnitch, 0x33, 0x20, '', 0x09D099) + create_sprite(0x18, EnemySprite.BlueRupee, 0x36, 0x33, '', 0x09D09C) + # Screen1A_1: + create_sprite(0x1a, EnemySprite.BlueGuard, 0x14, 0x0C, '', 0x09D0A0) + create_sprite(0x1a, EnemySprite.GreenGuard, 0x0C, 0x0E, '', 0x09D0A3) + create_sprite(0x1a, EnemySprite.Faerie, 0x0D, 0x11, '', 0x09D0A6, fix=True) + create_sprite(0x1a, EnemySprite.BlueRupee, 0x17, 0x17, '', 0x09D0A9) + # create_sprite(0x1a, EnemySprite.SmallHeart, 0x0A, 0x18, '', 0x09D0AC) + create_sprite(0x1a, EnemySprite.RedSpearGuard, 0x0F, 0x18, '', 0x09D0AC) # was 0x09D0AF + # Screen1B_1: + create_sprite(0x1b, EnemySprite.Wiseman, 0x19, 0x12, '', 0x09D0B0) + create_sprite(0x1b, EnemySprite.LightningGate, 0x1F, 0x06, '', 0x09D0B3) + create_sprite(0x1b, EnemySprite.RedBushGuard, 0x09, 0x11, '', 0x09D0B6) + create_sprite(0x1b, EnemySprite.RedBushGuard, 0x0A, 0x13, '', 0x09D0B9) + create_sprite(0x1b, EnemySprite.Apple, 0x16, 0x14, '', 0x09D0BC) + create_sprite(0x1b, EnemySprite.BombGuard, 0x0E, 0x19, '', 0x09D0BF) + create_sprite(0x1b, EnemySprite.BlueGuard, 0x1F, 0x1A, '', 0x09D0C2) + create_sprite(0x1b, EnemySprite.RupeePull, 0x29, 0x17, '', 0x09D0C5) + create_sprite(0x1b, EnemySprite.BombGuard, 0x31, 0x19, '', 0x09D0C8) + create_sprite(0x1b, EnemySprite.BlueGuard, 0x20, 0x1A, '', 0x09D0CB) + create_sprite(0x1b, EnemySprite.BombGuard, 0x0E, 0x25, '', 0x09D0CE) + create_sprite(0x1b, EnemySprite.GreenGuard, 0x14, 0x2D, '', 0x09D0D1) + create_sprite(0x1b, EnemySprite.RedJavelinGuard, 0x26, 0x2D, '', 0x09D0D4) + create_sprite(0x1b, EnemySprite.RedJavelinGuard, 0x21, 0x32, '', 0x09D0D7) + # Screen1D_1: + create_sprite(0x1d, EnemySprite.Apple, 0x0B, 0x06, '', 0x09D0DB) + create_sprite(0x1d, EnemySprite.BlueArcher, 0x1B, 0x0C, '', 0x09D0DE) + create_sprite(0x1d, EnemySprite.BlueGuard, 0x07, 0x0D, '', 0x09D0E1) + create_sprite(0x1d, EnemySprite.BlueArcher, 0x1B, 0x0F, '', 0x09D0E4) + create_sprite(0x1d, EnemySprite.Crab, 0x07, 0x12, '', 0x09D0E7) + # Screen1E_1: + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x13, 0x08, '', 0x09D0EB) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x0E, 0x0E, '', 0x09D0EE) + create_sprite(0x1e, EnemySprite.Octorok, 0x11, 0x1A, '', 0x09D0F1) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x19, 0x1A, '', 0x09D0F4) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x33, 0x09, '', 0x09D0F7) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x37, 0x09, '', 0x09D0FA) + create_sprite(0x1e, EnemySprite.BlueGuard, 0x31, 0x10, '', 0x09D0FD) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x2F, 0x17, '', 0x09D100) + create_sprite(0x1e, EnemySprite.Octorok4Way, 0x35, 0x1D, '', 0x09D103) + create_sprite(0x1e, EnemySprite.Octorok4Way, 0x0F, 0x25, '', 0x09D106) + create_sprite(0x1e, EnemySprite.Octorok, 0x09, 0x28, '', 0x09D109) + create_sprite(0x1e, EnemySprite.Octorok, 0x15, 0x2C, '', 0x09D10C) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x14, 0x33, '', 0x09D10F) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x17, 0x33, '', 0x09D112) + create_sprite(0x1e, EnemySprite.Octorok, 0x09, 0x36, '', 0x09D115) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x24, 0x25, '', 0x09D118) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x28, 0x29, '', 0x09D11B) + create_sprite(0x1e, EnemySprite.ArmosStatue, 0x3D, 0x29, '', 0x09D11E) + create_sprite(0x1e, EnemySprite.Octorok, 0x2E, 0x3B, '', 0x09D121) + # Screen22_1: + create_sprite(0x22, EnemySprite.BunnyBeam, 0x0C, 0x04, '', 0x09D125, fix=True) # smithy smoke + create_sprite(0x22, EnemySprite.GreenGuard, 0x17, 0x12, '', 0x09D128) + create_sprite(0x22, EnemySprite.Cucco, 0x12, 0x14, '', 0x09D12B) + # Screen25_1: + create_sprite(0x25, EnemySprite.Octorok, 0x0F, 0x08, '', 0x09D12F) + create_sprite(0x25, EnemySprite.Octorok, 0x05, 0x0C, '', 0x09D132) + create_sprite(0x25, EnemySprite.Octorok, 0x14, 0x0C, '', 0x09D135) + create_sprite(0x25, EnemySprite.Octorok, 0x10, 0x0D, '', 0x09D138) + create_sprite(0x25, EnemySprite.Octorok, 0x0C, 0x11, '', 0x09D13B) + create_sprite(0x25, EnemySprite.Octorok, 0x18, 0x16, '', 0x09D13E) + create_sprite(0x25, EnemySprite.Octorok, 0x08, 0x17, '', 0x09D141) + create_sprite(0x25, EnemySprite.Octorok, 0x10, 0x17, '', 0x09D144) + # Screen28_1: + create_sprite(0x28, EnemySprite.HeartPiece, 0x07, 0x13, '', 0x09D148) + create_sprite(0x28, EnemySprite.RaceGameGuy, 0x08, 0x12, '', 0x09D14B) + create_sprite(0x28, EnemySprite.RaceGameLady, 0x19, 0x18, '', 0x09D14E) + # Screen2A_1: + create_sprite(0x2a, EnemySprite.FluteQuest, 0x09, 0x09, '', 0x09D152) + create_sprite(0x2a, EnemySprite.GroveOstritch, 0x0E, 0x0C, '', 0x09D155) + create_sprite(0x2a, EnemySprite.GroveBird, 0x0D, 0x0E, '', 0x09D158) + create_sprite(0x2a, EnemySprite.FluteKid, 0x0E, 0x0E, '', 0x09D15B) + create_sprite(0x2a, EnemySprite.GroveBird, 0x11, 0x0E, '', 0x09D15E) + create_sprite(0x2a, EnemySprite.GroveRabbit, 0x0C, 0x0F, '', 0x09D161) + create_sprite(0x2a, EnemySprite.GroveRabbit, 0x11, 0x10, '', 0x09D164) + # Screen2B_1: + create_sprite(0x2b, EnemySprite.Faerie, 0x16, 0x0D, '', 0x09D168, fix=True) + create_sprite(0x2b, EnemySprite.GreenGuard, 0x14, 0x11, '', 0x09D16B) + create_sprite(0x2b, EnemySprite.GreenGuard, 0x14, 0x15, '', 0x09D16E) + create_sprite(0x2b, EnemySprite.GreenGuard, 0x10, 0x17, '', 0x09D171) + # Screen2C_1: + create_sprite(0x2c, EnemySprite.GreenGuard, 0x18, 0x14, '', 0x09D175) + create_sprite(0x2c, EnemySprite.BlueGuard, 0x09, 0x19, '', 0x09D178) + # Screen2D_1: + create_sprite(0x2d, EnemySprite.GreenGuard, 0x13, 0x0B, '', 0x09D17C) + create_sprite(0x2d, EnemySprite.BlueArcher, 0x10, 0x10, '', 0x09D17F) + create_sprite(0x2d, EnemySprite.BlueGuard, 0x12, 0x16, '', 0x09D182) + # Screen2E_1: + create_sprite(0x2e, EnemySprite.BlueGuard, 0x0E, 0x0C, '', 0x09D186) + create_sprite(0x2e, EnemySprite.BlueGuard, 0x17, 0x0E, '', 0x09D189) + create_sprite(0x2e, EnemySprite.FireballZora, 0x05, 0x12, '', 0x09D18C, water=True) + create_sprite(0x2e, EnemySprite.Octorok, 0x19, 0x17, '', 0x09D18F) + # Screen2F_1: + create_sprite(0x2f, EnemySprite.BlueGuard, 0x0F, 0x0C, '', 0x09D193) + create_sprite(0x2f, EnemySprite.ArmosStatue, 0x07, 0x17, '', 0x09D196) + create_sprite(0x2f, EnemySprite.ArmosStatue, 0x0C, 0x17, '', 0x09D199) + # Screen30_1: + create_sprite(0x30, EnemySprite.DesertStatue, 0x12, 0x14, '', 0x09D19D) + create_sprite(0x30, EnemySprite.PedestalPlaque, 0x12, 0x19, '', 0x09D1A0) + create_sprite(0x30, EnemySprite.DesertStatue, 0x0E, 0x1C, '', 0x09D1A3) + create_sprite(0x30, EnemySprite.DesertStatue, 0x16, 0x1C, '', 0x09D1A6) + create_sprite(0x30, EnemySprite.Geldman, 0x27, 0x19, '', 0x09D1A9) + create_sprite(0x30, EnemySprite.Vulture, 0x22, 0x1C, '', 0x09D1AC) + create_sprite(0x30, EnemySprite.Geldman, 0x2A, 0x1F, '', 0x09D1AF) + create_sprite(0x30, EnemySprite.Geldman, 0x1D, 0x26, '', 0x09D1B2) + create_sprite(0x30, EnemySprite.Vulture, 0x07, 0x29, '', 0x09D1B5) + create_sprite(0x30, EnemySprite.Geldman, 0x0F, 0x29, '', 0x09D1B8) + create_sprite(0x30, EnemySprite.HeartPiece, 0x06, 0x2A, '', 0x09D1BB) + create_sprite(0x30, EnemySprite.Geldman, 0x1B, 0x2C, '', 0x09D1BE) + create_sprite(0x30, EnemySprite.Geldman, 0x0A, 0x30, '', 0x09D1C1) + create_sprite(0x30, EnemySprite.Geldman, 0x14, 0x35, '', 0x09D1C4) + create_sprite(0x30, EnemySprite.MedallionTablet, 0x37, 0x2B, '', 0x09D1C7) + create_sprite(0x30, EnemySprite.Landmine, 0x36, 0x21, '', 0x09D1CA) + create_sprite(0x30, EnemySprite.Geldman, 0x22, 0x24, '', 0x09D1CD) + create_sprite(0x30, EnemySprite.Landmine, 0x29, 0x25, '', 0x09D1D0) + create_sprite(0x30, EnemySprite.Geldman, 0x20, 0x2C, '', 0x09D1D3) + create_sprite(0x30, EnemySprite.Geldman, 0x23, 0x32, '', 0x09D1D6) + create_sprite(0x30, EnemySprite.Landmine, 0x30, 0x32, '', 0x09D1D9) + create_sprite(0x30, EnemySprite.Vulture, 0x34, 0x33, '', 0x09D1DC) + create_sprite(0x30, EnemySprite.Landmine, 0x2D, 0x3B, '', 0x09D1DF) + # Screen32_1: + create_sprite(0x32, EnemySprite.SmallHeart, 0x1A, 0x09, '', 0x09D1E3) + create_sprite(0x32, EnemySprite.BlueGuard, 0x0B, 0x0B, '', 0x09D1E6) + create_sprite(0x32, EnemySprite.BlueGuard, 0x12, 0x0B, '', 0x09D1E9) + create_sprite(0x32, EnemySprite.Faerie, 0x19, 0x12, '', 0x09D1EC, fix=True) + # Screen33_1: + create_sprite(0x33, EnemySprite.GreenBushGuard, 0x15, 0x0B, '', 0x09D1F0) + create_sprite(0x33, EnemySprite.BlueArcher, 0x09, 0x0E, '', 0x09D1F3) + create_sprite(0x33, EnemySprite.Whirlpool, 0x17, 0x12, '', 0x09D1F6) + create_sprite(0x33, EnemySprite.Octorok, 0x1A, 0x1B, '', 0x09D1F9) + # Screen34_1: + create_sprite(0x34, EnemySprite.BlueArcher, 0x0B, 0x0D, '', 0x09D1FD) + create_sprite(0x34, EnemySprite.Toppo, 0x15, 0x11, '', 0x09D200) + create_sprite(0x34, EnemySprite.GreenBushGuard, 0x11, 0x12, '', 0x09D203) + create_sprite(0x34, EnemySprite.Raven, 0x08, 0x13, '', 0x09D206) + create_sprite(0x34, EnemySprite.Faerie, 0x0E, 0x13, '', 0x09D209, fix=True) + create_sprite(0x34, EnemySprite.GreenBushGuard, 0x15, 0x17, '', 0x09D20C) + create_sprite(0x34, EnemySprite.BlueArcher, 0x0C, 0x18, '', 0x09D20F) + # Screen35_1: + create_sprite(0x35, EnemySprite.Raven, 0x0E, 0x07, '', 0x09D213) + create_sprite(0x35, EnemySprite.Octorok, 0x0D, 0x09, '', 0x09D216) + create_sprite(0x35, EnemySprite.BlueArcher, 0x0A, 0x0C, '', 0x09D219) + create_sprite(0x35, EnemySprite.HeartPiece, 0x19, 0x13, '', 0x09D21C) + create_sprite(0x35, EnemySprite.Buzzblob, 0x19, 0x14, '', 0x09D21F) + create_sprite(0x35, EnemySprite.Crab, 0x07, 0x17, '', 0x09D222) + create_sprite(0x35, EnemySprite.FireballZora, 0x11, 0x17, '', 0x09D225, water=True) + create_sprite(0x35, EnemySprite.FireballZora, 0x25, 0x0D, '', 0x09D228, water=True) + create_sprite(0x35, EnemySprite.Buzzblob, 0x27, 0x1F, '', 0x09D22B) + create_sprite(0x35, EnemySprite.Buzzblob, 0x2F, 0x1F, '', 0x09D22E) + create_sprite(0x35, EnemySprite.Octorok, 0x0A, 0x35, '', 0x09D231) + create_sprite(0x35, EnemySprite.FireballZora, 0x14, 0x35, '', 0x09D234, water=True) + create_sprite(0x35, EnemySprite.Raven, 0x0F, 0x35, '', 0x09D237) + create_sprite(0x35, EnemySprite.Octorok, 0x0B, 0x39, '', 0x09D23A) + create_sprite(0x35, EnemySprite.Buzzblob, 0x19, 0x3A, '', 0x09D23D) + create_sprite(0x35, EnemySprite.Crab, 0x11, 0x3B, '', 0x09D240) + create_sprite(0x35, EnemySprite.FireballZora, 0x24, 0x2B, '', 0x09D243, water=True) + create_sprite(0x35, EnemySprite.Whirlpool, 0x29, 0x2B, '', 0x09D246) + create_sprite(0x35, EnemySprite.FireballZora, 0x39, 0x31, '', 0x09D249, water=True) + create_sprite(0x35, EnemySprite.FireballZora, 0x21, 0x36, '', 0x09D24C, water=True) + create_sprite(0x35, EnemySprite.Buzzblob, 0x32, 0x37, '', 0x09D24F) + create_sprite(0x35, EnemySprite.Buzzblob, 0x34, 0x39, '', 0x09D252) + create_sprite(0x35, EnemySprite.Crab, 0x2E, 0x3A, '', 0x09D255) + # Screen37_1: + create_sprite(0x37, EnemySprite.Crab, 0x08, 0x08, '', 0x09D259) + create_sprite(0x37, EnemySprite.Crab, 0x10, 0x08, '', 0x09D25C) + create_sprite(0x37, EnemySprite.Crab, 0x0F, 0x0B, '', 0x09D25F) + create_sprite(0x37, EnemySprite.Crab, 0x16, 0x11, '', 0x09D262) + create_sprite(0x37, EnemySprite.Raven, 0x0C, 0x15, '', 0x09D265) + create_sprite(0x37, EnemySprite.FireballZora, 0x12, 0x19, '', 0x09D268, water=True) + # Screen3A_1: + create_sprite(0x3a, EnemySprite.Locksmith, 0x17, 0x05, '', 0x09D26C) + create_sprite(0x3a, EnemySprite.Raven, 0x0E, 0x09, '', 0x09D26F, embed=True) + create_sprite(0x3a, EnemySprite.Hoarder2, 0x0B, 0x0A, '', 0x09D272) + create_sprite(0x3a, EnemySprite.Hoarder2, 0x18, 0x0E, '', 0x09D275) + # Screen3B_1: + create_sprite(0x3b, EnemySprite.GreenBushGuard, 0x13, 0x06, '', 0x09D279) + create_sprite(0x3b, EnemySprite.BlueArcher, 0x0C, 0x0A, '', 0x09D27C) + create_sprite(0x3b, EnemySprite.FloppingFish, 0x13, 0x0D, '', 0x09D27F, water=True) + create_sprite(0x3b, EnemySprite.Raven, 0x08, 0x0B, '', 0x09D282) + create_sprite(0x3b, EnemySprite.HeartPiece, 0x14, 0x0E, '', 0x09D285) + create_sprite(0x3b, EnemySprite.FloppingFish, 0x1B, 0x10, '', 0x09D288, water=True) + create_sprite(0x3b, EnemySprite.Toppo, 0x0F, 0x14, '', 0x09D28B) + create_sprite(0x3b, EnemySprite.Raven, 0x14, 0x1B, '', 0x09D28E, embed=True) + # Screen3C_1: + create_sprite(0x3c, EnemySprite.GreenBushGuard, 0x08, 0x0C, '', 0x09D292) + create_sprite(0x3c, EnemySprite.Octorok, 0x14, 0x0F, '', 0x09D295) + create_sprite(0x3c, EnemySprite.Raven, 0x0E, 0x0F, '', 0x09D298) + create_sprite(0x3c, EnemySprite.Hoarder, 0x09, 0x11, '', 0x09D29B) + create_sprite(0x3c, EnemySprite.Octorok4Way, 0x14, 0x15, '', 0x09D29E) + create_sprite(0x3c, EnemySprite.Crab, 0x16, 0x17, '', 0x09D2A1) + create_sprite(0x3c, EnemySprite.Octorok, 0x0B, 0x18, '', 0x09D2A4) + # Screen3F_1: + create_sprite(0x3f, EnemySprite.Octorok, 0x11, 0x04, '', 0x09D2A8) + create_sprite(0x3f, EnemySprite.Octorok, 0x16, 0x05, '', 0x09D2AB) + create_sprite(0x3f, EnemySprite.FireballZora, 0x08, 0x0B, '', 0x09D2AE, water=True) + create_sprite(0x3f, EnemySprite.Whirlpool, 0x07, 0x0C, '', 0x09D2B1) + create_sprite(0x3f, EnemySprite.Octoballoon, 0x10, 0x16, '', 0x09D2B4) + # Screen00_2: + create_sprite(0x90, EnemySprite.BlueGuard, 0x0F, 0x11, '', 0x09D2B8) + create_sprite(0x90, EnemySprite.FakeMasterSword, 0x07, 0x12, '', 0x09D2BB) + create_sprite(0x90, EnemySprite.Mushroom, 0x1E, 0x15, '', 0x09D2BE) + create_sprite(0x90, EnemySprite.Thief, 0x0D, 0x1F, '', 0x09D2C1) + create_sprite(0x90, EnemySprite.FakeMasterSword, 0x28, 0x06, '', 0x09D2C4) + create_sprite(0x90, EnemySprite.RupeePull, 0x2B, 0x08, '', 0x09D2C7) + create_sprite(0x90, EnemySprite.BlueGuard, 0x33, 0x08, '', 0x09D2CA) + create_sprite(0x90, EnemySprite.Thief, 0x2B, 0x0A, '', 0x09D2CD) + create_sprite(0x90, EnemySprite.Buzzblob, 0x31, 0x0A, '', 0x09D2D0) + create_sprite(0x90, EnemySprite.Hoarder, 0x2A, 0x10, '', 0x09D2D3) + create_sprite(0x90, EnemySprite.Hoarder, 0x0D, 0x2C, '', 0x09D2D6) + create_sprite(0x90, EnemySprite.BlueGuard, 0x09, 0x33, '', 0x09D2D9) + create_sprite(0x90, EnemySprite.Bee, 0x29, 0x25, '', 0x09D2DC) + create_sprite(0x90, EnemySprite.Hoarder, 0x28, 0x2F, '', 0x09D2DF) + # Screen02_2: + create_sprite(0x92, EnemySprite.BonkItem, 0x0D, 0x12, '', 0x09D2E3) + # Screen03_2: + create_sprite(0x93, EnemySprite.Boulders, 0x00, 0x00, '', 0x09D2E7) + create_sprite(0x93, EnemySprite.MedallionTablet, 0x0B, 0x04, '', 0x09D2EA) + create_sprite(0x93, EnemySprite.Tektite, 0x10, 0x1A, '', 0x09D2ED) + create_sprite(0x93, EnemySprite.Tektite, 0x1A, 0x1E, '', 0x09D2F0) + create_sprite(0x93, EnemySprite.Deadrock, 0x27, 0x0C, '', 0x09D2F3) + create_sprite(0x93, EnemySprite.Tektite, 0x2C, 0x15, '', 0x09D2F6) + create_sprite(0x93, EnemySprite.HeartPiece, 0x22, 0x16, '', 0x09D2F9) + create_sprite(0x93, EnemySprite.Tektite, 0x28, 0x19, '', 0x09D2FC) + create_sprite(0x93, EnemySprite.Deadrock, 0x0A, 0x35, '', 0x09D2FF) + create_sprite(0x93, EnemySprite.Deadrock, 0x06, 0x36, '', 0x09D302) + create_sprite(0x93, EnemySprite.Deadrock, 0x0D, 0x3B, '', 0x09D305) + create_sprite(0x93, EnemySprite.PositionTarget, 0x12, 0x3B, '', 0x09D308) + create_sprite(0x93, EnemySprite.Deadrock, 0x2C, 0x2D, '', 0x09D30B) + create_sprite(0x93, EnemySprite.Deadrock, 0x34, 0x33, '', 0x09D30E) + create_sprite(0x93, EnemySprite.Deadrock, 0x2F, 0x34, '', 0x09D311) + # Screen05_2: + create_sprite(0x95, EnemySprite.Deadrock, 0x07, 0x0B, '', 0x09D315) + create_sprite(0x95, EnemySprite.Tektite, 0x08, 0x0D, '', 0x09D318) + create_sprite(0x95, EnemySprite.Deadrock, 0x1E, 0x0E, '', 0x09D31B) + create_sprite(0x95, EnemySprite.Tektite, 0x1F, 0x0F, '', 0x09D31E) + create_sprite(0x95, EnemySprite.HeartPiece, 0x2F, 0x03, '', 0x09D321) + create_sprite(0x95, EnemySprite.Deadrock, 0x35, 0x0D, '', 0x09D324) + create_sprite(0x95, EnemySprite.Tektite, 0x29, 0x0F, '', 0x09D327) + create_sprite(0x95, EnemySprite.Deadrock, 0x35, 0x0F, '', 0x09D32A) + create_sprite(0x95, EnemySprite.Faerie, 0x34, 0x10, '', 0x09D32D, fix=True) + create_sprite(0x95, EnemySprite.Tektite, 0x1E, 0x31, '', 0x09D330) + create_sprite(0x95, EnemySprite.Tektite, 0x35, 0x2A, '', 0x09D333) + create_sprite(0x95, EnemySprite.Deadrock, 0x2A, 0x2F, '', 0x09D336) + create_sprite(0x95, EnemySprite.Tektite, 0x2F, 0x2F, '', 0x09D339) + create_sprite(0x95, EnemySprite.Deadrock, 0x29, 0x36, '', 0x09D33C) + create_sprite(0x95, EnemySprite.Deadrock, 0x36, 0x36, '', 0x09D33F) + # Screen07_2: + create_sprite(0x97, EnemySprite.Deadrock, 0x0E, 0x07, '', 0x09D343) + create_sprite(0x97, EnemySprite.Deadrock, 0x0A, 0x0D, '', 0x09D346) + create_sprite(0x97, EnemySprite.Deadrock, 0x17, 0x15, '', 0x09D349) + create_sprite(0x97, EnemySprite.Deadrock, 0x0F, 0x16, '', 0x09D34C) + create_sprite(0x97, EnemySprite.Deadrock, 0x12, 0x16, '', 0x09D34F) + # Screen0A_2: + create_sprite(0x9a, EnemySprite.Bee, 0x0E, 0x04, '', 0x09D353) + create_sprite(0x9a, EnemySprite.BlueGuard, 0x10, 0x0D, '', 0x09D356) + create_sprite(0x9a, EnemySprite.Raven, 0x11, 0x16, '', 0x09D359, embed=True) + create_sprite(0x9a, EnemySprite.Raven, 0x13, 0x16, '', 0x09D35C, embed=True) + create_sprite(0x9a, EnemySprite.Hoarder, 0x0E, 0x16, '', 0x09D35F) + create_sprite(0x9a, EnemySprite.Raven, 0x11, 0x17, '', 0x09D362, embed=True) + create_sprite(0x9a, EnemySprite.Apple, 0x19, 0x1A, '', 0x09D365) + # Screen0F_2: + create_sprite(0x9f, EnemySprite.Waterfall, 0x06, 0x02, '', 0x09D369) + create_sprite(0x9f, EnemySprite.Crab, 0x0D, 0x0D, '', 0x09D36C) + create_sprite(0x9f, EnemySprite.FireballZora, 0x05, 0x10, '', 0x09D36F, water=True) + create_sprite(0x9f, EnemySprite.FireballZora, 0x0A, 0x11, '', 0x09D372, water=True) + create_sprite(0x9f, EnemySprite.Crab, 0x11, 0x12, '', 0x09D375) + create_sprite(0x9f, EnemySprite.Whirlpool, 0x08, 0x13, '', 0x09D378) + create_sprite(0x9f, EnemySprite.Octorok4Way, 0x0E, 0x17, '', 0x09D37B) + # Screen10_2: + create_sprite(0xa0, EnemySprite.BlueGuard, 0x05, 0x0C, '', 0x09D37F) + create_sprite(0xa0, EnemySprite.Apple, 0x07, 0x0C, '', 0x09D382) + create_sprite(0xa0, EnemySprite.LargeMagic, 0x17, 0x0F, '', 0x09D385) + create_sprite(0xa0, EnemySprite.BlueGuard, 0x07, 0x12, '', 0x09D388) + create_sprite(0xa0, EnemySprite.BlueGuard, 0x08, 0x18, '', 0x09D38B) + # Screen11_2: + create_sprite(0xa1, EnemySprite.BlueGuard, 0x17, 0x0C, '', 0x09D38F) + create_sprite(0xa1, EnemySprite.BlueGuard, 0x1A, 0x0D, '', 0x09D392) + create_sprite(0xa1, EnemySprite.BombRefill1, 0x08, 0x10, '', 0x09D395) + create_sprite(0xa1, EnemySprite.Cucco, 0x08, 0x17, '', 0x09D398) + # Screen12_2: + create_sprite(0xa2, EnemySprite.Faerie, 0x14, 0x0A, '', 0x09D39C, fix=True) + create_sprite(0xa2, EnemySprite.BlueGuard, 0x15, 0x0E, '', 0x09D39F) + create_sprite(0xa2, EnemySprite.Whirlpool, 0x0F, 0x10, '', 0x09D3A2) + create_sprite(0xa2, EnemySprite.GreenGuard, 0x15, 0x15, '', 0x09D3A5) + # Screen13_2: + create_sprite(0xa3, EnemySprite.Bee, 0x18, 0x09, '', 0x09D3A9) + create_sprite(0xa3, EnemySprite.BombRefill8, 0x07, 0x0C, '', 0x09D3AC) + create_sprite(0xa3, EnemySprite.BlueGuard, 0x0D, 0x17, '', 0x09D3AF) + create_sprite(0xa3, EnemySprite.BlueGuard, 0x12, 0x1A, '', 0x09D3B2) + # Screen14_2: + create_sprite(0xa4, EnemySprite.Poe, 0x0D, 0x0D, '', 0x09D3B6) + create_sprite(0xa4, EnemySprite.Poe, 0x19, 0x0F, '', 0x09D3B9) + create_sprite(0xa4, EnemySprite.Poe, 0x08, 0x10, '', 0x09D3BC) + create_sprite(0xa4, EnemySprite.Poe, 0x14, 0x11, '', 0x09D3BF) + create_sprite(0xa4, EnemySprite.Poe, 0x13, 0x14, '', 0x09D3C2) + create_sprite(0xa4, EnemySprite.BlueGuard, 0x11, 0x19, '', 0x09D3C5) + # Screen15_2: + create_sprite(0xa5, EnemySprite.Whirlpool, 0x11, 0x09, '', 0x09D3C9) + create_sprite(0xa5, EnemySprite.UsainBolt, 0x16, 0x0E, '', 0x09D3CC) + create_sprite(0xa5, EnemySprite.Faerie, 0x1B, 0x0F, '', 0x09D3CF, fix=True) + create_sprite(0xa5, EnemySprite.RedSpearGuard, 0x0B, 0x17, '', 0x09D3D2) + create_sprite(0xa5, EnemySprite.Apple, 0x04, 0x1A, '', 0x09D3D5) + # Screen16_2: + create_sprite(0xa6, EnemySprite.Buzzblob, 0x0D, 0x0A, '', 0x09D3D9) + create_sprite(0xa6, EnemySprite.Witch, 0x0F, 0x15, '', 0x09D3DC) + create_sprite(0xa6, EnemySprite.Buzzblob, 0x06, 0x18, '', 0x09D3DF) + # Screen17_2: + create_sprite(0xa7, EnemySprite.Buzzblob, 0x18, 0x08, '', 0x09D3E3) + create_sprite(0xa7, EnemySprite.Buzzblob, 0x17, 0x0A, '', 0x09D3E6) + create_sprite(0xa7, EnemySprite.UsainBolt, 0x0D, 0x0B, '', 0x09D3E9) + create_sprite(0xa7, EnemySprite.Buzzblob, 0x16, 0x0C, '', 0x09D3EC) + create_sprite(0xa7, EnemySprite.Buzzblob, 0x08, 0x16, '', 0x09D3EF) + # Screen18_2: + create_sprite(0xa8, EnemySprite.BlueGuard, 0x12, 0x08, '', 0x09D3F3) + create_sprite(0xa8, EnemySprite.RedRupee, 0x18, 0x0A, '', 0x09D3F6) + create_sprite(0xa8, EnemySprite.BottleMerchant, 0x18, 0x16, '', 0x09D3F9) + create_sprite(0xa8, EnemySprite.BlueGuard, 0x07, 0x1C, '', 0x09D3FC) + create_sprite(0xa8, EnemySprite.BlueGuard, 0x35, 0x0B, '', 0x09D3FF) + create_sprite(0xa8, EnemySprite.FluteQuest, 0x20, 0x18, '', 0x09D402) + create_sprite(0xa8, EnemySprite.BlueGuard, 0x12, 0x2E, '', 0x09D405) + create_sprite(0xa8, EnemySprite.Cucco, 0x14, 0x34, '', 0x09D408) + create_sprite(0xa8, EnemySprite.Cucco, 0x16, 0x35, '', 0x09D40B) + create_sprite(0xa8, EnemySprite.RedSpearGuard, 0x39, 0x22, '', 0x09D40E) + create_sprite(0xa8, EnemySprite.BlueGuard, 0x20, 0x2E, '', 0x09D411) + create_sprite(0xa8, EnemySprite.Bee, 0x36, 0x33, '', 0x09D414) + # Screen1A_2: + create_sprite(0xaa, EnemySprite.BlueGuard, 0x0F, 0x08, '', 0x09D418) + create_sprite(0xaa, EnemySprite.BlueGuard, 0x0C, 0x0E, '', 0x09D41B) + create_sprite(0xaa, EnemySprite.Faerie, 0x0D, 0x11, '', 0x09D41E, fix=True) + # create_sprite(0xaa, EnemySprite.SmallHeart, 0x0A, 0x18, '', 0x09D421) + create_sprite(0xaa, EnemySprite.UsainBolt, 0x0F, 0x18, '', 0x09D421) # was 0x09D424 + # Screen1B_2: + create_sprite(0xab, EnemySprite.Wiseman, 0x19, 0x12, '', 0x09D425) + create_sprite(0xab, EnemySprite.UsainBolt, 0x06, 0x0D, '', 0x09D428) + create_sprite(0xab, EnemySprite.Apple, 0x16, 0x14, '', 0x09D42B) + create_sprite(0xab, EnemySprite.UsainBolt, 0x1F, 0x1A, '', 0x09D42E) + create_sprite(0xab, EnemySprite.BlueGuard, 0x37, 0x13, '', 0x09D431) + create_sprite(0xab, EnemySprite.Whirlpool, 0x1E, 0x25, '', 0x09D434) + create_sprite(0xab, EnemySprite.RedSpearGuard, 0x08, 0x28, '', 0x09D437) + create_sprite(0xab, EnemySprite.GreenGuard, 0x1F, 0x2B, '', 0x09D43A) + create_sprite(0xab, EnemySprite.BlueGuard, 0x38, 0x29, '', 0x09D43D) + create_sprite(0xab, EnemySprite.BlueGuard, 0x21, 0x2D, '', 0x09D440) + create_sprite(0xab, EnemySprite.UsainBolt, 0x21, 0x32, '', 0x09D443) + # Screen1D_2: + create_sprite(0xad, EnemySprite.Bee, 0x0B, 0x06, '', 0x09D447) + create_sprite(0xad, EnemySprite.BlueArcher, 0x1B, 0x0C, '', 0x09D44A) + create_sprite(0xad, EnemySprite.UsainBolt, 0x07, 0x0D, '', 0x09D44D) + create_sprite(0xad, EnemySprite.BlueArcher, 0x1B, 0x0F, '', 0x09D450) + # Screen1E_2: + create_sprite(0xae, EnemySprite.ArmosStatue, 0x0E, 0x0E, '', 0x09D454) + create_sprite(0xae, EnemySprite.UsainBolt, 0x11, 0x1A, '', 0x09D457) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x19, 0x1A, '', 0x09D45A) + create_sprite(0xae, EnemySprite.RupeePull, 0x33, 0x04, '', 0x09D45D) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x33, 0x09, '', 0x09D460) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x37, 0x09, '', 0x09D463) + create_sprite(0xae, EnemySprite.UsainBolt, 0x31, 0x10, '', 0x09D466) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x2F, 0x17, '', 0x09D469) + create_sprite(0xae, EnemySprite.BlueGuard, 0x0F, 0x25, '', 0x09D46C) + create_sprite(0xae, EnemySprite.UsainBolt, 0x09, 0x28, '', 0x09D46F) + create_sprite(0xae, EnemySprite.RedSpearGuard, 0x15, 0x2C, '', 0x09D472) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x14, 0x33, '', 0x09D475) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x17, 0x33, '', 0x09D478) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x24, 0x25, '', 0x09D47B) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x31, 0x28, '', 0x09D47E) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x28, 0x29, '', 0x09D481) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x3A, 0x29, '', 0x09D484) + create_sprite(0xae, EnemySprite.ArmosStatue, 0x3D, 0x29, '', 0x09D487) + create_sprite(0xae, EnemySprite.Faerie, 0x22, 0x37, '', 0x09D48A, fix=True) + create_sprite(0xae, EnemySprite.UsainBolt, 0x2D, 0x3A, '', 0x09D48D) + # Screen22_2: + create_sprite(0xb2, EnemySprite.BunnyBeam, 0x0C, 0x04, '', 0x09D491, fix=True) # smithy smoke + create_sprite(0xb2, EnemySprite.Cucco, 0x0C, 0x14, '', 0x09D494) + create_sprite(0xb2, EnemySprite.Cucco, 0x12, 0x14, '', 0x09D497) + # Screen25_2: + create_sprite(0xb5, EnemySprite.BlueGuard, 0x0E, 0x08, '', 0x09D49B) + create_sprite(0xb5, EnemySprite.BlueGuard, 0x05, 0x0C, '', 0x09D49E) + create_sprite(0xb5, EnemySprite.BlueGuard, 0x09, 0x11, '', 0x09D4A1) + create_sprite(0xb5, EnemySprite.UsainBolt, 0x19, 0x16, '', 0x09D4A4) + # Screen28_2: + create_sprite(0xb8, EnemySprite.BlueGuard, 0x12, 0x0C, '', 0x09D4A8) + create_sprite(0xb8, EnemySprite.HeartPiece, 0x07, 0x13, '', 0x09D4AB) + create_sprite(0xb8, EnemySprite.RaceGameGuy, 0x08, 0x12, '', 0x09D4AE) + create_sprite(0xb8, EnemySprite.RaceGameLady, 0x19, 0x18, '', 0x09D4B1) + create_sprite(0xb8, EnemySprite.BlueGuard, 0x0C, 0x19, '', 0x09D4B4) + # Screen29_2: + create_sprite(0xb9, EnemySprite.BlueGuard, 0x0E, 0x05, '', 0x09D4B8) + create_sprite(0xb9, EnemySprite.UsainBolt, 0x0C, 0x0C, '', 0x09D4BB) + create_sprite(0xb9, EnemySprite.BlueGuard, 0x0B, 0x14, '', 0x09D4BE) + # Screen2A_2: + create_sprite(0xba, EnemySprite.FluteQuest, 0x09, 0x09, '', 0x09D4C2) + create_sprite(0xba, EnemySprite.GroveOstritch, 0x0E, 0x0C, '', 0x09D4C5) + create_sprite(0xba, EnemySprite.GroveBird, 0x0D, 0x0E, '', 0x09D4C8) + create_sprite(0xba, EnemySprite.FluteKid, 0x0E, 0x0E, '', 0x09D4CB) + create_sprite(0xba, EnemySprite.GroveBird, 0x11, 0x0E, '', 0x09D4CE) + create_sprite(0xba, EnemySprite.GroveRabbit, 0x0C, 0x0F, '', 0x09D4D1) + create_sprite(0xba, EnemySprite.GroveRabbit, 0x11, 0x10, '', 0x09D4D4) + create_sprite(0xba, EnemySprite.RedRupee, 0x15, 0x14, '', 0x09D4D7) + create_sprite(0xba, EnemySprite.RedRupee, 0x0F, 0x18, '', 0x09D4DA) + # Screen2B_2: + create_sprite(0xbb, EnemySprite.BlueGuard, 0x08, 0x06, '', 0x09D4DE) + create_sprite(0xbb, EnemySprite.Faerie, 0x16, 0x0D, '', 0x09D4E1, fix=True) + create_sprite(0xbb, EnemySprite.BlueGuard, 0x14, 0x11, '', 0x09D4E4) + create_sprite(0xbb, EnemySprite.BlueGuard, 0x14, 0x15, '', 0x09D4E7) + create_sprite(0xbb, EnemySprite.BlueGuard, 0x10, 0x17, '', 0x09D4EA) + # Screen2C_2: + create_sprite(0xbc, EnemySprite.BlueGuard, 0x18, 0x14, '', 0x09D4EE) + create_sprite(0xbc, EnemySprite.BlueGuard, 0x09, 0x19, '', 0x09D4F1) + # Screen2D_2: + create_sprite(0xbd, EnemySprite.Octorok4Way, 0x0F, 0x08, '', 0x09D4F5) + create_sprite(0xbd, EnemySprite.BlueGuard, 0x12, 0x0B, '', 0x09D4F8) + create_sprite(0xbd, EnemySprite.UsainBolt, 0x12, 0x16, '', 0x09D4FB) + create_sprite(0xbd, EnemySprite.FireballZora, 0x1C, 0x17, '', 0x09D4FE, water=True) + # Screen2E_2: + create_sprite(0xbe, EnemySprite.Faerie, 0x0C, 0x09, '', 0x09D502, fix=True) + create_sprite(0xbe, EnemySprite.Bee, 0x14, 0x0B, '', 0x09D505) + create_sprite(0xbe, EnemySprite.UsainBolt, 0x0E, 0x0C, '', 0x09D508) + create_sprite(0xbe, EnemySprite.BlueGuard, 0x17, 0x0E, '', 0x09D50B) + create_sprite(0xbe, EnemySprite.FireballZora, 0x05, 0x12, '', 0x09D50E, water=True) + create_sprite(0xbe, EnemySprite.Octorok, 0x19, 0x17, '', 0x09D511) + # Screen2F_2: + create_sprite(0xbf, EnemySprite.UsainBolt, 0x0F, 0x0C, '', 0x09D515) + create_sprite(0xbf, EnemySprite.ArmosStatue, 0x07, 0x17, '', 0x09D518) + create_sprite(0xbf, EnemySprite.ArmosStatue, 0x0C, 0x17, '', 0x09D51B) + # Screen30_2: + create_sprite(0xc0, EnemySprite.DesertStatue, 0x12, 0x14, '', 0x09D51F) + create_sprite(0xc0, EnemySprite.PedestalPlaque, 0x12, 0x19, '', 0x09D522) + create_sprite(0xc0, EnemySprite.DesertStatue, 0x0E, 0x1C, '', 0x09D525) + create_sprite(0xc0, EnemySprite.DesertStatue, 0x16, 0x1C, '', 0x09D528) + create_sprite(0xc0, EnemySprite.Geldman, 0x27, 0x19, '', 0x09D52B) + create_sprite(0xc0, EnemySprite.Vulture, 0x22, 0x1C, '', 0x09D52E) + create_sprite(0xc0, EnemySprite.Geldman, 0x2A, 0x1F, '', 0x09D531) + create_sprite(0xc0, EnemySprite.Landmine, 0x0C, 0x23, '', 0x09D534) + create_sprite(0xc0, EnemySprite.Geldman, 0x1D, 0x26, '', 0x09D537) + create_sprite(0xc0, EnemySprite.Vulture, 0x07, 0x29, '', 0x09D53A) + create_sprite(0xc0, EnemySprite.Geldman, 0x0F, 0x29, '', 0x09D53D) + create_sprite(0xc0, EnemySprite.HeartPiece, 0x06, 0x2A, '', 0x09D540) + create_sprite(0xc0, EnemySprite.Geldman, 0x1B, 0x2C, '', 0x09D543) + create_sprite(0xc0, EnemySprite.Geldman, 0x0A, 0x30, '', 0x09D546) + create_sprite(0xc0, EnemySprite.Geldman, 0x14, 0x35, '', 0x09D549) + create_sprite(0xc0, EnemySprite.MedallionTablet, 0x37, 0x2B, '', 0x09D54C) + create_sprite(0xc0, EnemySprite.Geldman, 0x22, 0x24, '', 0x09D54F) + create_sprite(0xc0, EnemySprite.Geldman, 0x28, 0x2A, '', 0x09D552) + create_sprite(0xc0, EnemySprite.Geldman, 0x23, 0x32, '', 0x09D555) + create_sprite(0xc0, EnemySprite.Vulture, 0x34, 0x33, '', 0x09D558) + # Screen32_2: + create_sprite(0xc2, EnemySprite.SmallHeart, 0x1A, 0x09, '', 0x09D55C) + create_sprite(0xc2, EnemySprite.GreenGuard, 0x0B, 0x0C, '', 0x09D55F) + create_sprite(0xc2, EnemySprite.BlueGuard, 0x12, 0x0C, '', 0x09D562) + create_sprite(0xc2, EnemySprite.GreenGuard, 0x13, 0x10, '', 0x09D565) + create_sprite(0xc2, EnemySprite.BombRefill4, 0x19, 0x12, '', 0x09D568) + create_sprite(0xc2, EnemySprite.Landmine, 0x08, 0x15, '', 0x09D56B) + # Screen33_2: + create_sprite(0xc3, EnemySprite.Octorok, 0x13, 0x06, '', 0x09D56F) + create_sprite(0xc3, EnemySprite.Octorok4Way, 0x14, 0x0B, '', 0x09D572) + create_sprite(0xc3, EnemySprite.Whirlpool, 0x17, 0x12, '', 0x09D575) + create_sprite(0xc3, EnemySprite.Octorok, 0x12, 0x16, '', 0x09D578) + create_sprite(0xc3, EnemySprite.Octorok, 0x1A, 0x1B, '', 0x09D57B) + # Screen34_2: + create_sprite(0xc4, EnemySprite.RupeePull, 0x17, 0x0E, '', 0x09D57F) + create_sprite(0xc4, EnemySprite.Raven, 0x08, 0x13, '', 0x09D582) + create_sprite(0xc4, EnemySprite.BlueGuard, 0x11, 0x12, '', 0x09D585) + create_sprite(0xc4, EnemySprite.Octorok, 0x06, 0x13, '', 0x09D588) + create_sprite(0xc4, EnemySprite.Octorok, 0x0C, 0x18, '', 0x09D58B) + # Screen35_2: + create_sprite(0xc5, EnemySprite.Raven, 0x0E, 0x07, '', 0x09D58F) + create_sprite(0xc5, EnemySprite.Octorok, 0x0D, 0x09, '', 0x09D592) + create_sprite(0xc5, EnemySprite.UsainBolt, 0x0A, 0x0C, '', 0x09D595) + create_sprite(0xc5, EnemySprite.HeartPiece, 0x19, 0x13, '', 0x09D598) + create_sprite(0xc5, EnemySprite.Buzzblob, 0x19, 0x14, '', 0x09D59B) + create_sprite(0xc5, EnemySprite.FireballZora, 0x11, 0x17, '', 0x09D59E, water=True) + create_sprite(0xc5, EnemySprite.Octorok4Way, 0x38, 0x0A, '', 0x09D5A1) + create_sprite(0xc5, EnemySprite.FireballZora, 0x25, 0x0D, '', 0x09D5A4, water=True) + create_sprite(0xc5, EnemySprite.FireballZora, 0x37, 0x19, '', 0x09D5A7, water=True) + create_sprite(0xc5, EnemySprite.Buzzblob, 0x27, 0x1F, '', 0x09D5AA) + create_sprite(0xc5, EnemySprite.Buzzblob, 0x2F, 0x1F, '', 0x09D5AD) + create_sprite(0xc5, EnemySprite.FireballZora, 0x1B, 0x26, '', 0x09D5B0, water=True) + create_sprite(0xc5, EnemySprite.Raven, 0x0D, 0x2F, '', 0x09D5B3, embed=True) + create_sprite(0xc5, EnemySprite.Octorok, 0x06, 0x34, '', 0x09D5B6) + create_sprite(0xc5, EnemySprite.Octorok, 0x0A, 0x35, '', 0x09D5B9) + create_sprite(0xc5, EnemySprite.FireballZora, 0x14, 0x35, '', 0x09D5BC, water=True) + create_sprite(0xc5, EnemySprite.Octorok, 0x0B, 0x39, '', 0x09D5BF) + create_sprite(0xc5, EnemySprite.Buzzblob, 0x19, 0x3A, '', 0x09D5C2) + create_sprite(0xc5, EnemySprite.Whirlpool, 0x29, 0x2B, '', 0x09D5C5) + create_sprite(0xc5, EnemySprite.FireballZora, 0x39, 0x31, '', 0x09D5C8, water=True) + create_sprite(0xc5, EnemySprite.FireballZora, 0x21, 0x36, '', 0x09D5CB, water=True) + create_sprite(0xc5, EnemySprite.Buzzblob, 0x32, 0x37, '', 0x09D5CE) + create_sprite(0xc5, EnemySprite.Buzzblob, 0x34, 0x39, '', 0x09D5D1) + # Screen37_2: + create_sprite(0xc7, EnemySprite.Crab, 0x08, 0x08, '', 0x09D5D5) + create_sprite(0xc7, EnemySprite.Crab, 0x10, 0x08, '', 0x09D5D8) + create_sprite(0xc7, EnemySprite.Crab, 0x0F, 0x0B, '', 0x09D5DB) + create_sprite(0xc7, EnemySprite.Crab, 0x16, 0x11, '', 0x09D5DE) + create_sprite(0xc7, EnemySprite.FireballZora, 0x12, 0x19, '', 0x09D5E1, water=True) + # Screen3A_2: + create_sprite(0xca, EnemySprite.Locksmith, 0x17, 0x05, '', 0x09D5E5) + create_sprite(0xca, EnemySprite.Hoarder2, 0x0B, 0x0A, '', 0x09D5E8) + create_sprite(0xca, EnemySprite.Raven, 0x14, 0x0D, '', 0x09D5EB, embed=True) + create_sprite(0xca, EnemySprite.Raven, 0x13, 0x0E, '', 0x09D5EE, embed=True) + create_sprite(0xca, EnemySprite.Raven, 0x14, 0x0F, '', 0x09D5F1, embed=True) + create_sprite(0xca, EnemySprite.Raven, 0x13, 0x10, '', 0x09D5F4, embed=True) + create_sprite(0xca, EnemySprite.UsainBolt, 0x11, 0x0F, '', 0x09D5F7) + create_sprite(0xca, EnemySprite.Hoarder2, 0x17, 0x17, '', 0x09D5FA) + # Screen3B_2: + create_sprite(0xcb, EnemySprite.FloppingFish, 0x13, 0x0D, '', 0x09D5FE, water=True) + create_sprite(0xcb, EnemySprite.Octorok, 0x0C, 0x0F, '', 0x09D601) + create_sprite(0xcb, EnemySprite.HeartPiece, 0x14, 0x0E, '', 0x09D604) + create_sprite(0xcb, EnemySprite.Octorok4Way, 0x0F, 0x14, '', 0x09D607) + create_sprite(0xcb, EnemySprite.BlueGuard, 0x17, 0x18, '', 0x09D60A) + create_sprite(0xcb, EnemySprite.Raven, 0x14, 0x1B, '', 0x09D60D, embed=True) + # Screen3C_2: + create_sprite(0xcc, EnemySprite.Raven, 0x0B, 0x09, '', 0x09D611, embed=True) + create_sprite(0xcc, EnemySprite.BlueGuard, 0x08, 0x0A, '', 0x09D614) + create_sprite(0xcc, EnemySprite.Octorok, 0x14, 0x0F, '', 0x09D617) + create_sprite(0xcc, EnemySprite.UsainBolt, 0x09, 0x11, '', 0x09D61A) + create_sprite(0xcc, EnemySprite.Octorok4Way, 0x14, 0x15, '', 0x09D61D) + # Screen3F_2: + create_sprite(0xcf, EnemySprite.Octorok4Way, 0x16, 0x05, '', 0x09D621) + create_sprite(0xcf, EnemySprite.Whirlpool, 0x07, 0x0C, '', 0x09D624) + create_sprite(0xcf, EnemySprite.FireballZora, 0x06, 0x13, '', 0x09D627, water=True) + create_sprite(0xcf, EnemySprite.Octoballoon, 0x11, 0x16, '', 0x09D62A) diff --git a/source/enemizer/SpriteSheets.py b/source/enemizer/SpriteSheets.py new file mode 100644 index 00000000..2a935048 --- /dev/null +++ b/source/enemizer/SpriteSheets.py @@ -0,0 +1,788 @@ +import logging +from collections import defaultdict +import RaceRandom as random + +from source.dungeon.EnemyList import EnemySprite, SpriteType, enemy_names, sprite_translation, overlord_names +from source.dungeon.RoomConstants import * + + +class SpriteRequirement: + def __init__(self, sprite, overlord=0): + self.sprite = sprite + self.overlord = overlord + + self.boss = False + self.static = False # npcs and do not randomize + self.killable = True + self.can_drop = True + self.water_only = False + self.dont_use = False + self.ow_valid = True + self.uw_valid = True + self.can_randomize = True + self.water_phobic = False + + self.groups = [] + self.sub_groups = defaultdict(list) + + self.excluded_rooms = set() + self.allowed_rooms = set() + + def can_spawn_in_room(self, room_id): + return room_id not in self.excluded_rooms and (self.sprite != EnemySprite.Wallmaster or room_id < 0x100) + + def no_drop(self): + self.can_drop = False + return self + + def sub_group(self, key, subs): + if isinstance(subs, list): + self.sub_groups[key].extend(subs) + else: + self.sub_groups[key].append(subs) + return self + + def group(self, group_id): + self.groups.append(group_id) + return self + + def exclude(self, exclusions): + self.excluded_rooms.update(exclusions) + return self + + def allow(self, allowed): + self.allowed_rooms.update(allowed) + return self + + def affix(self): + self.static = True + self.killable = False + self.can_drop = False + return self + + def stasis(self): + self.can_randomize = False + return self + + def exalt(self): + self.boss = True + self.static = True # not randomized by sprite sheet + return self + + def immune(self): + self.killable = False + self.can_drop = False + return self + + def immerse(self): + self.water_only = True + return self + + def aquaphobia(self): + self.water_phobic = True + return self + + def skip(self): + self.dont_use = True + return self + + def ow_skip(self): + self.ow_valid = False + return self + + def uw_skip(self): + self.uw_valid = False + return self + + def good_for_uw_water(self): + return self.water_only and not self.static and not self.dont_use and self.uw_valid + + def good_for_shutter(self, forbidden): + if self.sprite in forbidden: + return False + return self.killable and not self.static and not self.dont_use and self.uw_valid + + def good_for_key_drop(self, forbidden): + return self.good_for_shutter(forbidden) and self.can_drop + + def __str__(self): + return f'Req for {enemy_names[self.sprite] if self.overlord != 0x7 else overlord_names[self.sprite]}' + + +NoFlyingRooms = {0xd2, 0x10c} # Mire 2, Mimic Cave +NoBeamosOrTrapRooms = {0xb, 0x16, 0x19, 0x1e, 0x26, 0x27, 0x36, 0x3f, 0x40, 0x42, 0x46, 0x49, 0x4b, 0x4e, 0x55, 0x57, + 0x5f, 0x65, 0x6a, 0x74, 0x76, 0x7d, 0x7f, 0x83, 0x84, 0x85, 0x8c, 0x8d, 0x92, 0x95, 0x98, 0x9b, + 0x9c, 0x9d, 0x9e, 0xa0, 0xaa, 0xaf, 0xb3, 0xba, 0xbb, 0xbc, 0xc6, 0xcb, 0xce, 0xd0, 0xd2, 0xd5, + 0xd8, 0xdc, 0xdf, 0xe4, 0xe7, 0xee, 0xf9, 0xfd, 0x10c} +LenientTrapsForTesting = {0x16, 0x26, 0x3f, 0x40, 0x42, 0x46, 0x49, 0x4e, 0x57, + 0x65, 0x6a, 0x74, 0x76, 0x7d, 0x98, + 0x9e, 0xaf, 0xba, 0xc6, 0xcb, 0xce, 0xd2, 0xd5, + 0xd8, 0xdf, 0xe4, 0xe7, 0xee, 0xfd, 0x10c} +# this will have to be dynamic if cave rooms are allowed in dungeons +WallmasterValidRooms = { + HC_NorthCorridor, HC_SwitchRoom, HoulihanRoom, TR_CrystalRollerRoom, + PalaceofDarkness0x09, PoD_StalfosTrapRoom, PoD_TurtleRoom, GT_EntranceRoom, Ice_EntranceRoom, + GanonEvacuationRoute, HC_BombableStockRoom, Sanctuary, TR_Hokku_BokkuKeyRoom2, TR_BigKeyRoom, TurtleRock0x15, + Swamp_SwimmingTreadmill, Hera_MoldormFallRoom, PoD_DarkMaze, PoD_BigChestRoom, PoD_Mimics_MovingWallRoom, + GT_IceArmos, GT_FinalHallway, Ice_BombFloor_BariRoom, Ice_Pengator_BigKeyRoom, Tower_Agahnim, HC_KeyRatRoom, + HC_SewerTextTriggerRoom, TR_WestExittoBalcony, TR_DoubleHokku_Bokku_BigchestRoom, Swamp_StatueRoom, Hera_BigChest, + Swamp_EntranceRoom, Skull_Mothula, PoD_BigHubRoom, PoD_MapChest_FairyRoom, Ice_CompassRoom, Hera_HardhatBeetlesRoom, + HC_SewerKeyChestRoom, Desert_Lanmolas, Swamp_PushBlockPuzzle_Pre_BigKeyRoom, Swamp_BigKey_BSRoom, + Swamp_BigChestRoom, Swamp_MapChest_WaterFillRoom, Swamp_KeyPotRoom, Skull_GibdoKey_MothulaHoleRoom, + PoD_BombableFloorRoom, PoD_SpikeBlock_ConveyorRoom, GT_TorchRoom2, Ice_StalfosKnights_ConveyorHellway, + Ice_MapChestRoom, Tower_FinalBridgeRoom, HC_FirstDarkRoom, HC_6RopesRoom, Desert_TorchPuzzle_MovingWallRoom, + TT_BigChestRoom, TT_JailCellsRoom, Swamp_CompassChestRoom, Skull_GibdoTorchPuzzleRoom, PoD_EntranceRoom, + PoD_Warps_SouthMimicsRoom, GT_Mini_HelmasaurConveyorRoom, GT_MoldormRoom, Ice_Bomb_JumpRoom, + IcePalaceCloneRoom_FairyRoom, HC_WestCorridor, HC_ThroneRoom, HC_EastCorridor, Desert_Popos2_BeamosHellwayRoom, + Swamp_UpstairsPitsRoom, CastleSecretEntrance_UncleDeathRoom, Skull_KeyPot_TrapRoom, Skull_BigKeyRoom, + Skull_BigChestRoom, Skull_FinalSectionEntranceRoom, PoD_HelmasaurKing, GT_SpikePitRoom, GT_Ganon_BallZ, + GT_Gauntlet1_2_3, Ice_LonelyFirebar, Ice_HiddenChest_SpikeFloorRoom, HC_WestEntranceRoom, HC_MainEntranceRoom, + HC_EastEntranceRoom, Desert_FinalSectionEntranceRoom, TT_WestAtticRoom, TT_EastAtticRoom, + Swamp_HiddenChest_HiddenDoorRoom, Skull_CompassChestRoom, Skull_KeyChest_TrapRoom, PoD_RupeeRoom, GT_MimicsRooms, + GT_LanmolasRoom, GT_Gauntlet4_5, Ice_PengatorsRoom, HC_SmallCorridortoJailCells, HC_BoomerangChestRoom, + HC_MapChestRoom, Desert_BigChestRoom, Desert_MapChestRoom, Desert_BigKeyChestRoom, Swamp_WaterDrainRoom, + Hera_EntranceRoom, GanonsTower, GT_EastSideCollapsingBridge_ExplodingWallRoom, GT_Winder_WarpMazeRoom, + Ice_HiddenChest_BombableFloorRoom, Ice_BigSpikeTrapsRoom, HC_JailCellRoom, HC_NextToChasmRoom, HC_BasementChasmRoom, + Desert_WestEntranceRoom, Desert_MainEntranceRoom, Desert_EastEntranceRoom, Hera_TileRoom, Eastern_FairyRoom, + GT_BlockPuzzle_SpikeSkip_MapChestRoom, GT_EastandWestDownstairs_BigChestRoom, GT_Tile_TorchPuzzleRoom, + IcePalace0x8E, Mire_Vitreous, Mire_FinalSwitchRoom, Mire_DarkBombWall_SwitchesRoom, + Mire_DarkCaneFloorSwitchPuzzleRoom, GT_FinalCollapsingBridgeRoom, GT_Torches1Room, Mire_TorchPuzzle_MovingWallRoom, + Mire_EntranceRoom, Eastern_EyegoreKeyRoom, GT_ManySpikes_WarpMazeRoom, GT_InvisibleFloorMazeRoom, + GT_CompassChest_InvisibleFloorRoom, Ice_BigChestRoom, IcePalace0x9F, Mire_Pre_VitreousRoom, Mire_FishRoom, + Mire_BridgeKeyChestRoom, MiseryMire0xA3, TR_Trinexx, GT_WizzrobesRooms, GT_MoldormFallRoom, Hera_FairyRoom, + Eastern_StalfosSpawnRoom, Eastern_BigChestRoom, Eastern_MapChestRoom, TT_MovingSpikes_KeyPotRoom, TT_BlindTheThief, + IcePalace0xAE, Ice_IceBridgeRoom, Tower_CircleofPots, Mire_HourglassRoom, Mire_SlugRoom, Mire_SpikeKeyChestRoom, + TR_Pre_TrinexxRoom, TR_DarkMaze, TR_ChainChompsRoom, TR_MapChest_KeyChest_RollerRoom, Eastern_BigKeyRoom, + Eastern_LobbyCannonballsRoom, Eastern_DarkAntifairy_KeyPotRoom, TT_Hellway, TT_ConveyorToilet, Ice_BlockPuzzleRoom, + IcePalaceCloneRoom_SwitchRoom, Tower_DarkBridgeRoom, Mire_CompassChest_TileRoom, Mire_BigHubRoom, Mire_BigChestRoom, + TR_FinalCrystalSwitchPuzzleRoom, TR_LaserBridge, TurtleRock0xC6, TR_TorchPuzzle, + Eastern_EntranceRoom, UnknownRoom, TT_NorthWestEntranceRoom, TT_NorthEastEntranceRoom, Ice_HoletoKholdstareRoom, + Tower_DarkMaze, Mire_ConveyorSlug_BigKeyRoom, Mire_Mire02_WizzrobesRoom, TR_LaserKeyRoom, TR_EntranceRoom, + Eastern_PreArmosKnightsRoom, Eastern_CanonballRoom, EasternPalace, TT_Main_SouthWestEntranceRoom, + TT_SouthEastEntranceRoom, Tower_EntranceRoom +} + + +def init_sprite_requirements(): + reqs = [ + SpriteRequirement(EnemySprite.Raven).no_drop().sub_group(3, [0x11, 0x19]).exclude(NoFlyingRooms), + SpriteRequirement(EnemySprite.Vulture).no_drop().sub_group(2, 0x12).exclude(NoFlyingRooms), + SpriteRequirement(EnemySprite.CorrectPullSwitch).affix().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.WrongPullSwitch).affix().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.Octorok).sub_group(2, [0xc, 0x18]), + SpriteRequirement(EnemySprite.Moldorm).exalt().sub_group(2, 0x30), + SpriteRequirement(EnemySprite.Octorok4Way).sub_group(2, 0xc), + SpriteRequirement(EnemySprite.Cucco).immune().sub_group(3, [0x15, 0x50]).exclude(NoFlyingRooms), + SpriteRequirement(EnemySprite.Buzzblob).sub_group(3, 0x11), + SpriteRequirement(EnemySprite.Snapdragon).sub_group(0, 0x16).sub_group(2, 0x17), + SpriteRequirement(EnemySprite.Octoballoon).no_drop().sub_group(2, 0xc).exclude(NoFlyingRooms), + SpriteRequirement(EnemySprite.Hinox).sub_group(0, 0x16), + SpriteRequirement(EnemySprite.Moblin).sub_group(2, 0x17), + SpriteRequirement(EnemySprite.MiniHelmasaur).sub_group(1, 0x1e), + SpriteRequirement(EnemySprite.AntiFairy).no_drop().sub_group(3, [0x52, 0x53]) + .exclude(NoFlyingRooms).exclude({0x40}), # no anti-fairies in aga tower bridge room + SpriteRequirement(EnemySprite.Wiseman).affix().sub_group(2, 0x4c), + SpriteRequirement(EnemySprite.Hoarder).sub_group(3, 0x11).exclude({0x10c}), + SpriteRequirement(EnemySprite.MiniMoldorm).sub_group(1, 0x1e), + SpriteRequirement(EnemySprite.Poe).no_drop().sub_group(3, 0x15).exclude(NoFlyingRooms), + SpriteRequirement(EnemySprite.Smithy).affix().sub_group(1, 0x1d).sub_group(3, 0x15), + SpriteRequirement(EnemySprite.Statue).stasis().immune().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.CrystalSwitch).affix().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.SickKid).affix().sub_group(0, 0x51), + SpriteRequirement(EnemySprite.Sluggula).sub_group(2, 0x25), + SpriteRequirement(EnemySprite.WaterSwitch).affix().sub_group(3, 0x53), + SpriteRequirement(EnemySprite.Ropa).sub_group(0, 0x16), + SpriteRequirement(EnemySprite.RedBari).sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.BlueBari).sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.TalkingTree).affix().sub_group(3, [0x15, 0x1B]), + SpriteRequirement(EnemySprite.HardhatBeetle).sub_group(1, 0x1e), + SpriteRequirement(EnemySprite.Deadrock).sub_group(3, 0x10).exclude({0x7f, 0x10c}), + SpriteRequirement(EnemySprite.DarkWorldHintNpc).affix(), # no groups? + SpriteRequirement(EnemySprite.AdultNpc).affix().sub_group(0, [0xe, 0x4f]), + SpriteRequirement(EnemySprite.SweepingLady).affix().group(6), # no sub groups? + SpriteRequirement(EnemySprite.Lumberjacks).affix().sub_group(2, 0x4a), + SpriteRequirement(EnemySprite.RaceGameLady).affix().group(6), + SpriteRequirement(EnemySprite.FortuneTeller).affix().sub_group(0, 0x4b), + SpriteRequirement(EnemySprite.ArgueBros).affix().sub_group(0, 0x4f), + SpriteRequirement(EnemySprite.RupeePull).affix(), + SpriteRequirement(EnemySprite.YoungSnitch).affix().group(6), + SpriteRequirement(EnemySprite.Innkeeper).affix(), # no groups? + SpriteRequirement(EnemySprite.Witch).affix().sub_group(2, 0x7c), + SpriteRequirement(EnemySprite.Waterfall).affix(), + SpriteRequirement(EnemySprite.EyeStatue).affix(), + SpriteRequirement(EnemySprite.Locksmith).affix().sub_group(3, 0x11), + SpriteRequirement(EnemySprite.MagicBat).affix().sub_group(3, 0x1d), + SpriteRequirement(EnemySprite.KidInKak).affix().group(6), + SpriteRequirement(EnemySprite.OldSnitch).affix().group(6), + SpriteRequirement(EnemySprite.Hoarder2).sub_group(3, 0x11).exclude({0x10c}), + SpriteRequirement(EnemySprite.TutorialGuard).affix(), + SpriteRequirement(EnemySprite.LightningGate).affix().sub_group(3, 0x3f), + SpriteRequirement(EnemySprite.BlueGuard).sub_group(1, [0xd, 0x49]), + SpriteRequirement(EnemySprite.GreenGuard).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.RedSpearGuard).sub_group(1, [0xd, 0x49]), + SpriteRequirement(EnemySprite.BluesainBolt).sub_group(0, 0x46).sub_group(1, [0xd, 0x49]), + SpriteRequirement(EnemySprite.UsainBolt).sub_group(1, [0xd, 0x49]), + SpriteRequirement(EnemySprite.BlueArcher).sub_group(0, 0x48).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.GreenBushGuard).sub_group(0, 0x48).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.RedJavelinGuard).sub_group(0, 0x46).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.RedBushGuard).sub_group(0, 0x46).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.BombGuard).sub_group(0, 0x46).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.GreenKnifeGuard).sub_group(1, 0x49).sub_group(2, 0x13), + SpriteRequirement(EnemySprite.Geldman).sub_group(2, 0x12).exclude({0x10c}), + SpriteRequirement(EnemySprite.Toppo).immune().sub_group(3, 0x11), + SpriteRequirement(EnemySprite.Popo).sub_group(1, 0x2c), + SpriteRequirement(EnemySprite.Popo2).sub_group(1, 0x2c), + SpriteRequirement(EnemySprite.ArmosStatue).sub_group(3, 0x10).exclude({0x10c}), + SpriteRequirement(EnemySprite.KingZora).affix().sub_group(3, 0x44), + SpriteRequirement(EnemySprite.ArmosKnight).exalt().sub_group(3, 0x1d), + SpriteRequirement(EnemySprite.Lanmolas).exalt().sub_group(3, 0x31), + SpriteRequirement(EnemySprite.FireballZora).immerse().no_drop().sub_group(2, [0xc, 0x18]), # .uw_skip() test + SpriteRequirement(EnemySprite.Zora).sub_group(2, 0xc).sub_group(3, 0x44), # .uw_skip() test + SpriteRequirement(EnemySprite.DesertStatue).affix().sub_group(2, 0x12), + SpriteRequirement(EnemySprite.Crab).sub_group(2, 0xc), + SpriteRequirement(EnemySprite.LostWoodsBird).affix().sub_group(2, 0x37).sub_group(3, 0x36), + SpriteRequirement(EnemySprite.LostWoodsSquirrel).affix().sub_group(2, 0x37).sub_group(3, 0x36), + SpriteRequirement(EnemySprite.SparkCW).immune().sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.SparkCCW).immune().sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.RollerVerticalUp).immune().sub_group(2, 0x27).exclude(NoBeamosOrTrapRooms), + SpriteRequirement(EnemySprite.RollerVerticalDown).immune().sub_group(2, 0x27).exclude(NoBeamosOrTrapRooms), + SpriteRequirement(EnemySprite.RollerHorizontalLeft).immune().sub_group(2, 0x27).exclude(NoBeamosOrTrapRooms), + SpriteRequirement(EnemySprite.RollerHorizontalRight).immune().sub_group(2, 0x27).exclude(NoBeamosOrTrapRooms), + SpriteRequirement(EnemySprite.Beamos).no_drop().sub_group(1, 0x2c).exclude(NoBeamosOrTrapRooms), + SpriteRequirement(EnemySprite.MasterSword).affix().sub_group(2, 0x37).sub_group(3, 0x36), + + SpriteRequirement(EnemySprite.DebirandoPit).sub_group(0, 0x2f), # skip + SpriteRequirement(EnemySprite.Debirando).sub_group(0, 0x2f), # skip + SpriteRequirement(EnemySprite.ArcheryNpc).affix().sub_group(0, 0x4b), + SpriteRequirement(EnemySprite.WallCannonVertLeft).affix().sub_group(0, 0x2f), + SpriteRequirement(EnemySprite.WallCannonVertRight).affix().sub_group(0, 0x2f), + SpriteRequirement(EnemySprite.WallCannonHorzTop).affix().sub_group(0, 0x2f), + SpriteRequirement(EnemySprite.WallCannonHorzBottom).affix().sub_group(0, 0x2f), + SpriteRequirement(EnemySprite.BallNChain).sub_group(0, 0x46).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.CannonTrooper).sub_group(0, 0x46).sub_group(1, 0x49), + SpriteRequirement(EnemySprite.CricketRat).sub_group(2, [0x1c, 0x24]), + SpriteRequirement(EnemySprite.Snake).sub_group(2, [0x1c, 0x24]), + SpriteRequirement(EnemySprite.Keese).no_drop().sub_group(2, [0x1c, 0x24]), + SpriteRequirement(EnemySprite.Leever).sub_group(0, 0x2f), + SpriteRequirement(EnemySprite.FairyPondTrigger).affix().sub_group(3, 0x36), + SpriteRequirement(EnemySprite.UnclePriest).affix().sub_group(0, [0x47, 0x51]), + SpriteRequirement(EnemySprite.RunningNpc).affix().group(6), + SpriteRequirement(EnemySprite.BottleMerchant).affix().group(6), + SpriteRequirement(EnemySprite.Zelda).affix(), + SpriteRequirement(EnemySprite.Grandma).affix().sub_group(0, 0x4b).sub_group(1, 0x4d).sub_group(2, 0x4a), + SpriteRequirement(EnemySprite.Agahnim).exalt().sub_group(0, 0x55).sub_group(1, [0x1a, 0x3d]).sub_group(2, 0x42) + .sub_group(3, 0x43), + SpriteRequirement(EnemySprite.FloatingSkull).sub_group(0, 0x1f).exclude(NoFlyingRooms), + SpriteRequirement(EnemySprite.BigSpike).sub_group(3, [0x52, 0x53]).no_drop(), + SpriteRequirement(EnemySprite.FirebarCW).immune().sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.FirebarCCW).immune().sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.Firesnake).no_drop().sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.Hover).sub_group(2, 0x22), # .exclude(NoFlyingRooms), might be okay now + SpriteRequirement(EnemySprite.AntiFairyCircle).no_drop().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.GreenEyegoreMimic).sub_group(2, 0x2e), + SpriteRequirement(EnemySprite.RedEyegoreMimic).sub_group(2, 0x2e), + + SpriteRequirement(EnemySprite.Kodongo).sub_group(2, 0x2a), + # SpriteRequirement(EnemySprite.YellowStalfos).sub_group(0, 0x1f), # doesn't spawn + SpriteRequirement(EnemySprite.Mothula).exalt().sub_group(2, 0x38).sub_group(3, 0x52), + SpriteRequirement(EnemySprite.SpikeBlock).immune().sub_group(3, [0x52, 0x53]).exclude(NoBeamosOrTrapRooms) + .exclude({0x28}), # why exclude sp entrance? + SpriteRequirement(EnemySprite.Gibdo).sub_group(2, 0x23), + SpriteRequirement(EnemySprite.Arrghus).exalt().sub_group(2, 0x39), + SpriteRequirement(EnemySprite.Arrghi).exalt().sub_group(2, 0x39), + SpriteRequirement(EnemySprite.Terrorpin).sub_group(2, 0x2a).exclude({0x10c}), # probably fine in mimic now + SpriteRequirement(EnemySprite.Blob).sub_group(1, 0x20), + SpriteRequirement(EnemySprite.Wallmaster).immune().ow_skip().sub_group(2, 0x23) + .allow(WallmasterValidRooms), + SpriteRequirement(EnemySprite.StalfosKnight).sub_group(1, 0x20).exclude({0x10c}), + SpriteRequirement(EnemySprite.HelmasaurKing).exalt().sub_group(2, 0x3a).sub_group(3, 0x3e), + SpriteRequirement(EnemySprite.Bumper).immune().aquaphobia().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.LaserEyeLeft).affix().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.LaserEyeRight).affix().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.LaserEyeTop).affix().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.LaserEyeBottom).affix().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.Pengator).sub_group(2, 0x26), + SpriteRequirement(EnemySprite.Kyameron).no_drop().immerse().sub_group(2, 0x22), + SpriteRequirement(EnemySprite.Wizzrobe).sub_group(2, [0x25, 0x29]), + SpriteRequirement(EnemySprite.Zoro).sub_group(1, 0x20), + SpriteRequirement(EnemySprite.Babasu).sub_group(1, 0x20), + SpriteRequirement(EnemySprite.GroveOstritch).affix().sub_group(2, 0x4e), + SpriteRequirement(EnemySprite.GroveRabbit).affix(), + SpriteRequirement(EnemySprite.GroveBird).affix().sub_group(2, 0x4e), + SpriteRequirement(EnemySprite.Freezor).stasis().skip().sub_group(2, 0x26), + SpriteRequirement(EnemySprite.Kholdstare).exalt().sub_group(2, 0x3c), + SpriteRequirement(EnemySprite.KholdstareShell).exalt(), + SpriteRequirement(EnemySprite.FallingIce).exalt().sub_group(2, 0x3c), + SpriteRequirement(EnemySprite.BlueZazak).sub_group(2, 0x28), + SpriteRequirement(EnemySprite.RedZazak).sub_group(2, 0x28), + SpriteRequirement(EnemySprite.Stalfos).sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.GreenZirro).no_drop().sub_group(3, 0x1b).exclude(NoFlyingRooms), + SpriteRequirement(EnemySprite.BlueZirro).no_drop().sub_group(3, 0x1b).exclude(NoFlyingRooms), + SpriteRequirement(EnemySprite.Pikit).sub_group(3, 0x1b), + SpriteRequirement(EnemySprite.CrystalMaiden).affix(), + SpriteRequirement(EnemySprite.OldMan).affix().sub_group(0, 0x46).sub_group(1, 0x49).sub_group(2, 0x1c), + SpriteRequirement(EnemySprite.PipeDown).affix(), + SpriteRequirement(EnemySprite.PipeUp).affix(), + SpriteRequirement(EnemySprite.PipeRight).affix(), + SpriteRequirement(EnemySprite.PipeLeft).affix(), + SpriteRequirement(EnemySprite.GoodBee).affix().sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.PurpleChest).affix().sub_group(3, 0x15), + SpriteRequirement(EnemySprite.BombShopGuy).affix().sub_group(1, 0x4d), + SpriteRequirement(EnemySprite.Kiki).affix().sub_group(3, 0x19), + SpriteRequirement(EnemySprite.BlindMaiden).affix(), + # dialog tester.sub_group(1, 0x2c) + SpriteRequirement(EnemySprite.BullyPinkBall).affix().sub_group(3, 0x14), + # shop keepers in complex thing below + SpriteRequirement(EnemySprite.Drunkard).affix().sub_group(0, 0x4f).sub_group(1, 0x4d).sub_group(2, 0x4a). + sub_group(3, 0x50), + SpriteRequirement(EnemySprite.Vitreous).exalt().sub_group(3, 0x3d), + SpriteRequirement(EnemySprite.Catfish).affix().sub_group(2, 0x18), + SpriteRequirement(EnemySprite.CutsceneAgahnim).affix().sub_group(0, 0x55).sub_group(1, 0x3d) + .sub_group(2, 0x42).sub_group(3, 0x43), + SpriteRequirement(EnemySprite.Boulder).affix().sub_group(3, 0x10), + SpriteRequirement(EnemySprite.Gibo).sub_group(2, 0x28), + SpriteRequirement(EnemySprite.Thief).immune().uw_skip().sub_group(0, [0xe, 0x15]), + SpriteRequirement(EnemySprite.Medusa).affix(), + SpriteRequirement(EnemySprite.FourWayShooter).affix(), + SpriteRequirement(EnemySprite.Pokey).sub_group(2, 0x27), + SpriteRequirement(EnemySprite.BigFairy).affix().sub_group(2, 0x39).sub_group(3, 0x36), + SpriteRequirement(EnemySprite.Tektite).sub_group(3, 0x10), + SpriteRequirement(EnemySprite.Chainchomp).immune().sub_group(2, 0x27), + SpriteRequirement(EnemySprite.TrinexxRockHead).exalt().sub_group(0, 0x40).sub_group(3, 0x3f), + SpriteRequirement(EnemySprite.TrinexxFireHead).exalt().sub_group(0, 0x40).sub_group(3, 0x3f), + SpriteRequirement(EnemySprite.TrinexxIceHead).exalt().sub_group(0, 0x40).sub_group(3, 0x3f), + SpriteRequirement(EnemySprite.Blind).exalt().sub_group(1, 0x2c).sub_group(2, 0x3b), + SpriteRequirement(EnemySprite.Swamola).no_drop().sub_group(3, 0x19), + SpriteRequirement(EnemySprite.Lynel).sub_group(3, 0x14), + SpriteRequirement(EnemySprite.BunnyBeam).no_drop().ow_skip(), + SpriteRequirement(EnemySprite.FloppingFish).uw_skip().immune(), + SpriteRequirement(EnemySprite.Stal), + SpriteRequirement(EnemySprite.Landmine).skip(), + SpriteRequirement(EnemySprite.DiggingGameNPC).affix().sub_group(1, 0x2a), + SpriteRequirement(EnemySprite.Ganon).exalt().sub_group(0, 0x21).sub_group(1, 0x41) + .sub_group(2, 0x45).sub_group(3, 0x33), + SpriteRequirement(EnemySprite.Faerie).immune(), + SpriteRequirement(EnemySprite.FakeMasterSword).immune().sub_group(3, 0x11), + SpriteRequirement(EnemySprite.MagicShopAssistant).affix().sub_group(0, 0x4b).sub_group(3, 0x5a), + SpriteRequirement(EnemySprite.SomariaPlatform).affix().sub_group(2, 0x27), + SpriteRequirement(EnemySprite.CastleMantle).affix().sub_group(0, 0x5d), + SpriteRequirement(EnemySprite.GreenMimic).sub_group(1, 0x2c), + SpriteRequirement(EnemySprite.RedMimic).sub_group(1, 0x2c), + SpriteRequirement(EnemySprite.MedallionTablet).affix().sub_group(2, 0x12), + + # overlord requirements - encapsulated mostly in the required sheets + SpriteRequirement(2, 7).affix().sub_group(2, 46), + SpriteRequirement(3, 7).affix().sub_group(2, 46), + SpriteRequirement(5, 7).affix().sub_group(0, 31), + SpriteRequirement(6, 7).affix().sub_group(2, [28, 36]), + SpriteRequirement(7, 7).affix(), + SpriteRequirement(8, 7).affix().sub_group(1, 32), + SpriteRequirement(9, 7).affix().sub_group(2, 35), + SpriteRequirement(0xa, 7).affix().sub_group(3, 82), + SpriteRequirement(0xb, 7).affix().sub_group(3, 82), + SpriteRequirement(0x10, 7).affix().sub_group(2, 34), + SpriteRequirement(0x11, 7).affix().sub_group(2, 34), + SpriteRequirement(0x12, 7).affix().sub_group(2, 34), + SpriteRequirement(0x13, 7).affix().sub_group(2, 34), + SpriteRequirement(0x14, 7).affix(), + SpriteRequirement(0x15, 7).affix().sub_group(2, [37, 41]), + SpriteRequirement(0x16, 7).affix().sub_group(1, 32), + SpriteRequirement(0x17, 7).affix().sub_group(0, 31), + SpriteRequirement(0x18, 7).affix().sub_group(0, 31), + SpriteRequirement(0x19, 7).affix(), + SpriteRequirement(0x1a, 7).affix(), + ] + simple = {(r.sprite, r.overlord): r for r in reqs} + shopkeeper = [ + SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 75).sub_group(2, 74).sub_group(3, 90) + .allow({0xff, 0x112, 0x11f}), + SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 75).sub_group(1, 77).sub_group(2, 74) + .sub_group(3, 90).allow({0x10f, 0x110, 0x11f}), + SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 79).sub_group(2, 74).sub_group(3, 90) + .allow({0x118}), + SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 14).sub_group(2, 74).sub_group(3, 90) + .allow({0x123, 0x124}), + SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 14).sub_group(2, 74).sub_group(3, 80) + .allow({0x125, 0x100}), + SpriteRequirement(EnemySprite.Shopkeeper).affix().sub_group(0, 21).allow({0x11e}), + ] + complex_r = {} + for req in shopkeeper: + for r in req.allowed_rooms: + complex_r[r] = req + simple[(EnemySprite.Shopkeeper, 0)] = complex_r + return simple + + +# sheet 1 and 1c have group 4 modified from vanilla for murahdahla +vanilla_sheets = [ + (0x00, 0x49, 0x00, 0x00), (0x46, 0x49, 0x0C, 0x3F), (0x48, 0x49, 0x13, 0x3F), (0x46, 0x49, 0x13, 0x0E), + (0x48, 0x49, 0x0C, 0x11), (0x48, 0x49, 0x0C, 0x10), (0x4F, 0x49, 0x4A, 0x50), (0x0E, 0x49, 0x4A, 0x11), + (0x46, 0x49, 0x12, 0x00), (0x00, 0x49, 0x00, 0x50), (0x00, 0x49, 0x00, 0x11), (0x48, 0x49, 0x0C, 0x00), + (0x00, 0x00, 0x37, 0x36), (0x48, 0x49, 0x4C, 0x11), (0x5D, 0x2C, 0x0C, 0x44), (0x00, 0x00, 0x4E, 0x00), + + (0x0F, 0x00, 0x12, 0x10), (0x00, 0x00, 0x00, 0x4C), (0x00, 0x0D, 0x17, 0x00), (0x16, 0x0D, 0x17, 0x1B), + (0x16, 0x0D, 0x17, 0x14), (0x15, 0x0D, 0x17, 0x15), (0x16, 0x0D, 0x18, 0x19), (0x16, 0x0D, 0x17, 0x19), + (0x16, 0x0D, 0x00, 0x00), (0x16, 0x0D, 0x18, 0x1B), (0x0F, 0x49, 0x4A, 0x11), (0x4B, 0x2A, 0x5C, 0x15), + (0x16, 0x49, 0x17, 0x3F), (0x00, 0x00, 0x00, 0x15), (0x16, 0x0D, 0x17, 0x10), (0x16, 0x49, 0x12, 0x00), + + (0x16, 0x49, 0x0C, 0x11), (0x00, 0x00, 0x12, 0x10), (0x16, 0x0D, 0x00, 0x11), (0x16, 0x49, 0x0C, 0x00), + (0x16, 0x0D, 0x4C, 0x11), (0x0E, 0x0D, 0x4A, 0x11), (0x16, 0x1A, 0x17, 0x1B), (0x4F, 0x34, 0x4A, 0x50), + (0x35, 0x4D, 0x65, 0x36), (0x4A, 0x34, 0x4E, 0x00), (0x0E, 0x34, 0x4A, 0x11), (0x51, 0x34, 0x5D, 0x59), + (0x4B, 0x49, 0x4C, 0x11), (0x2D, 0x00, 0x00, 0x00), (0x5D, 0x00, 0x12, 0x59), (0x00, 0x00, 0x00, 0x00), + + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + + (0x47, 0x49, 0x2B, 0x2D), (0x46, 0x49, 0x1C, 0x52), (0x00, 0x49, 0x1C, 0x52), (0x5D, 0x49, 0x00, 0x52), + (0x46, 0x49, 0x13, 0x52), (0x4B, 0x4D, 0x4A, 0x5A), (0x47, 0x49, 0x1C, 0x52), (0x4B, 0x4D, 0x39, 0x36), + (0x1F, 0x2C, 0x2E, 0x52), (0x1F, 0x2C, 0x2E, 0x1D), (0x2F, 0x2C, 0x2E, 0x52), (0x2F, 0x2C, 0x2E, 0x31), + (0x1F, 0x1E, 0x30, 0x52), (0x51, 0x49, 0x13, 0x00), (0x4F, 0x49, 0x13, 0x50), (0x4F, 0x4D, 0x4A, 0x50), + + (0x4B, 0x49, 0x4C, 0x2B), (0x1F, 0x20, 0x22, 0x53), (0x55, 0x3D, 0x42, 0x43), (0x1F, 0x1E, 0x23, 0x52), + (0x1F, 0x1E, 0x39, 0x3A), (0x1F, 0x1E, 0x3A, 0x3E), (0x1F, 0x1E, 0x3C, 0x3D), (0x40, 0x1E, 0x27, 0x3F), + (0x55, 0x1A, 0x42, 0x43), (0x1F, 0x1E, 0x2A, 0x52), (0x1F, 0x1E, 0x38, 0x52), (0x1F, 0x20, 0x28, 0x52), + (0x1F, 0x20, 0x26, 0x52), (0x1F, 0x2C, 0x25, 0x52), (0x1F, 0x20, 0x27, 0x52), (0x1F, 0x1E, 0x29, 0x52), + + (0x1F, 0x2C, 0x3B, 0x52), (0x46, 0x49, 0x24, 0x52), (0x21, 0x41, 0x45, 0x33), (0x1F, 0x2C, 0x28, 0x31), + (0x1F, 0x0D, 0x29, 0x52), (0x1F, 0x1E, 0x27, 0x52), (0x1F, 0x20, 0x27, 0x53), (0x48, 0x49, 0x13, 0x52), + (0x0E, 0x1E, 0x4A, 0x50), (0x1F, 0x20, 0x26, 0x53), (0x15, 0x00, 0x00, 0x00), (0x1F, 0x00, 0x2A, 0x52), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x00), + (0x00, 0x00, 0x00, 0x00), (0x00, 0x00, 0x00, 0x08), (0x5D, 0x49, 0x00, 0x52), (0x55, 0x49, 0x42, 0x43), + (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50), + (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x56, 0x57, 0x50), (0x61, 0x62, 0x63, 0x50), + (0x61, 0x62, 0x63, 0x50), (0x61, 0x56, 0x57, 0x50), (0x61, 0x56, 0x63, 0x50), (0x61, 0x56, 0x57, 0x50), + (0x61, 0x56, 0x33, 0x50), (0x61, 0x56, 0x57, 0x50), (0x61, 0x62, 0x63, 0x50), (0x61, 0x62, 0x63, 0x50) +] + +required_boss_sheets = {EnemySprite.ArmosKnight: 9, EnemySprite.Lanmolas: 11, EnemySprite.Moldorm: 12, + EnemySprite.HelmasaurKing: 21, EnemySprite.Arrghus: 20, EnemySprite.Mothula: 26, + EnemySprite.Blind: 32, EnemySprite.Kholdstare: 22, EnemySprite.Vitreous: 22, + EnemySprite.TrinexxRockHead: 23} + + +class SpriteSheet: + def __init__(self, id, default_sub_groups): + self.id = id + self.sub_groups = list(default_sub_groups) + self.locked = [False] * 4 + self.room_set = set() + + def dungeon_id(self): + return self.id + 0x40 + + def valid_sprite(self, requirement): + if requirement.groups and self.id not in requirement.groups: + return False + for idx, sub in enumerate(self.sub_groups): + if requirement.sub_groups[idx] and sub not in requirement.sub_groups[idx]: + return False + return True + + def lock_sprite_in(self, sprite): + for i, options in sprite.sub_groups.items(): + self.locked[i] = len(options) > 0 + + def add_sprite_to_sheet(self, groups, rooms=None): + for idx, g in enumerate(groups): + if g is not None: + self.sub_groups[idx] = g + self.locked[idx] = True + if rooms is not None: + self.room_set.update(rooms) + + def write_to_rom(self, rom, base_address): + rom.write_bytes(base_address + self.id * 4, self.sub_groups) + + def __str__(self): + return f'{self.id} => [{", ".join([str(x) for x in self.sub_groups])}]' + + +# convert to dungeon id +def did(n): + return n + 0x40 + + +def init_sprite_sheets(requirements): + sheets = {id: SpriteSheet(id, def_sheet) for id, def_sheet in enumerate(vanilla_sheets)} + # wait until bosses are randomized to determine which are randomized + for sprite, sheet_num in required_boss_sheets.items(): + sheet = sheets[did(sheet_num)] # convert to dungeon sheet id + boss_sprite = requirements[(sprite, 0)] + sheet.lock_sprite_in(boss_sprite) + return sheets + + +def setup_required_dungeon_groups(sheets, data_tables): + + sheets[did(1)].add_sprite_to_sheet([70, 73, 28, 82], {0xe4, 0xf0}) # old man + # various npcs + sheets[did(5)].add_sprite_to_sheet([75, 77, 74, 90], {0xf3, 0x109, 0x10e, 0x10f, 0x110, 0x111, 0x112, + 0x11a, 0x11c, 0x11f, 0x122}) + sheets[did(7)].add_sprite_to_sheet([75, 77, 57, 54], {0x8, 0x2c, 0x114, 0x115, 0x116}) # big fairies + sheets[did(13)].add_sprite_to_sheet([81, None, None, None], {0x55, 0x102, 0x104}) # uncle, sick kid + sheets[did(14)].add_sprite_to_sheet([71, 73, 76, 80], {0x12, 0x105, 0x10a}) # wisemen + sheets[did(15)].add_sprite_to_sheet([79, 77, 74, 80], {0xf4, 0xf5, 0x101, 0x103, 0x106, 0x118, 0x119}) # more npcs + sheets[did(18)].add_sprite_to_sheet([85, 61, 66, 67], {0x20, 0x30}) # aga alter, aga1 + sheets[did(24)].add_sprite_to_sheet([85, 26, 66, 67], {0xd}) # aga2 + sheets[did(34)].add_sprite_to_sheet([33, 65, 69, 51], {0}) # ganon + sheets[did(40)].add_sprite_to_sheet([14, None, 74, 80], {0x124, 0x125, 0x126}) # fairy + rupee npcs + sheets[did(9)].add_sprite_to_sheet([None, None, None, 29], {0xe3}) # magic bat + sheets[did(28)].add_sprite_to_sheet([None, None, 38, 82], {0xe, 0x7e, 0x8e, 0x9e}) # freezors + sheets[did(3)].add_sprite_to_sheet([93, None, None, None], {0x51}) # mantle + sheets[did(42)].add_sprite_to_sheet([21, None, None, None], {0x11e}) # hype cave + sheets[did(10)].add_sprite_to_sheet([47, None, 46, None], {0x5c, 0x75, 0xb9, 0xd9}) # cannonballs + sheets[did(37)].add_sprite_to_sheet([31, None, 39, 82], {0x24, 0xb4, 0xb5, 0xc6, 0xc7, 0xd6}) # somaria platforms + # not sure 31 is needed above + + free_sheet_reqs = [ + ([75, None, None, None], [0xff, 0x11f]), # shopkeepers + ([None, 77, None, 21], [0x121]), # smithy + ([None, None, None, 80], [0x108]), # chicken house + ([14, 30, None, None], [0x123]), # mini moldorm (shutter door) + ([None, None, 34, None], [0x36, 0x46, 0x66, 0x76]), # pirogusu spawners + ([None, 32, None, None], [0x9f]), # babasu spawners + ([31, None, None, None], [0x7f]), # force baris + ([None, None, 35, None], [0x39, 0x49]), # wallmasters + # bumpers - why the split - because of other requirements - + ([None, None, None, (82, 83)], [0x17, 0x2a, 0x4c, 0x59, 0x67, 0x7e, 0x8b, 0xeb, 0xfb]), + # crystal switches - split for some reason + ([None, None, None, (82, 83)], [0xb, 0x13, 0x1b, 0x1e, 0x2a, 0x2b, 0x31, 0x5b, 0x6b, 0x77, 0x8b, + 0x91, 0x92, 0x9b, 0x9d, 0xa1, 0xab, 0xbf, 0xc4, 0xef]), + # laser eyes - split for some reason + ([None, None, None, (82, 83)], [0x13, 0x23, 0x96, 0xa5, 0xc5, 0xd5]), + # statues - split for some reason + ([None, None, None, (82, 83)], [0x26, 0x2b, 0x40, 0x4a, 0x6b, 0x7b]), + ([None, None, None, 83], [0x43, 0x63, 0x87]), # tile rooms + + # non-optional + ([None, None, None, 82], [0x58, 0x8c, 0x10b]), # pull switches + ([None, None, (28, 36), 82], [0x2, 0x64]), # pull switches (snakes) + ([None, None, None, 82], [0x1a, 0x3d, 0x44, 0x5e, 0x7c, 0x95, 0xc3]), # collapsing bridges + ([None, None, None, 83], [0x3f, 0xce]), # pull tongue + ([None, None, None, 83], [0x35, 0x37, 0x76]), # swamp drains + ([None, None, 34, None], [0x28]), # tektike forced? - spawn chest + ([None, None, 37, None], [0x97]), # wizzrobe spawner - in middle of room... + + # combined + ([None, 32, None, (82, 83)], [0x3e]), # babasu spawners + crystal switch + ([None, 32, None, 83], [0x4]), # zoro spawners + crystal switch + pull switch + ([None, None, 35, 82], [0x56]), # wallmaster + collasping bridge + ([None, None, 35, (82, 83)], [0x57, 0x68]), # wallmaster + statue and wallmaster + bumpers + ([None, None, 34, 83], [0x76]), # swamp drain + pirogusu spawners + ([None, None, 35, 83], [0x8d]), # wallmaster + tile room + ([None, None, None, 83], [0xb6, 0xc1]), # tile room + crystal switch + + # allow some sprites / increase odds: + ([72, 73, None, None], []), # allow for blue archer + greenbush + ([None, 73, 19, None], []), # allow for green knife guard + ([None, None, 12, 68], []), # increase odds for zora + ([22, None, 23, None], []), # increase odds for snapdragon + ] + + data_tables.room_requirements = {} + # find home for the free_sheet_reqs + for pair in free_sheet_reqs: + groups, room_list = pair + for room in room_list: + data_tables.room_requirements[room] = groups + find_matching_sheet(groups, sheets, range(65, 124), room_list) + + +# RandomizeRooms(optionFlags); + # roomCollection.LoadRooms() + # roomCollection.RandomizeRoomSpriteGroups(spriteGroupCollection, optionFlags); + # more stuff +sub_group_choices = { + 0: [22, 31, 47, 14], + 1: [44, 30, 32], # 73, 13 + 2: [12, 18, 23, 24, 28, 46, 34, 35, 39, 40, 38, 41, 36, 37, 42], + 3: [17, 16, 27, 20, 82, 83, 25] # 25 for Swamola +} +# 70, 72 for guards +# 0: 72 specifically for BlueArcher/GreenBush (but needs combination) +# 0: 70 for guards but needs combination +# 2: 19 for green knife guard, but needs combination +# 3: 68 for Zora, but needs combination + + +def combine_req(sub_groups, requirement): + for i in range(0, 4): + if requirement.sub_groups[i]: + if len(sub_groups[i]) == 0: + sub_groups[i].update(requirement.sub_groups[i]) + else: + if len(sub_groups[i].intersection(requirement.sub_groups[i])) == 0: + raise IncompatibleEnemyException + sub_groups[i].intersection_update(requirement.sub_groups[i]) + + +def setup_custom_enemy_sheets(custom_enemies, sheets, data_tables, sheet_range, uw=True): + requirements = data_tables.sprite_requirements + for room_id, enemy_map in custom_enemies.items(): + if uw: + original_list = data_tables.uw_enemy_table.room_map[room_id] + else: + original_list = data_tables.ow_enemy_table[room_id] + sub_groups_choices = [set(), set(), set(), set()] + for idx, sprite in enumerate(original_list): + if idx in enemy_map: + key = (sprite_translation[enemy_map[idx]], 0) + if key not in requirements: + continue + req = requirements[key] + try: + combine_req(sub_groups_choices, req) + except IncompatibleEnemyException: + logging.getLogger('').warning(f'Incompatible enemy: {hex(room_id)}:{idx} {enemy_map[idx]}') + else: + sprite_secondary = 0 if sprite.sub_type != SpriteType.Overlord else sprite.sub_type + key = (sprite.kind, sprite_secondary) + if key not in requirements: + continue + req = requirements[key] + if isinstance(req, dict): + req = req[room_id] + if req.static or not req.can_randomize: + try: + combine_req(sub_groups_choices, req) + except IncompatibleEnemyException: + raise IncompatibleEnemyException(f'Incompatible enemy: {hex(room_id)}:{idx} {str(req)}') + sheet_req = [None if not x else tuple(x) for x in sub_groups_choices] + find_matching_sheet(sheet_req, sheets, sheet_range, [room_id], True) + + +def randomize_underworld_sprite_sheets(sheets, data_tables, custom_enemies): + setup_required_dungeon_groups(sheets, data_tables) + + setup_custom_enemy_sheets(custom_enemies, sheets, data_tables, range(65, 124), True) + + for num in range(65, 124): # sheets 0x41 to 0x7B inclusive + sheet = sheets[num] + # if not sheet.locked[1] and num in [65, 66, 67, 68]: # guard stuff, kind of + # sheet.locked[1] = True + # sheet.sub_groups[1] = random.choice([13, 73]) + + free_slots = [idx for idx in range(0, 4) if not sheet.locked[idx]] + while free_slots: + choices = [c for c in data_tables.sheet_choices if all(slot in free_slots for slot in c.slots)] + weights = [c.weight for c in choices] + choice = random.choices(choices, weights, k=1)[0] + for idx, val in choice.assignments.items(): + v = random.choice(val) if isinstance(val, list) else val + sheet.sub_groups[idx] = v + sheet.locked[idx] = True + free_slots = [idx for idx in range(0, 4) if not sheet.locked[idx]] + + +def setup_required_overworld_groups(sheets): + sheets[7].add_sprite_to_sheet([None, None, 74, None], {0x2}) # lumberjacks + sheets[16].add_sprite_to_sheet([None, None, 18, 16], {0x3, 0x93}) # WDM (pre/post-Aga) + sheets[7].add_sprite_to_sheet([None, None, None, 17], {0xA, 0x9A}) # DM Foothills? (pre/post-Aga) + sheets[4].add_sprite_to_sheet([None, None, None, None], {0xF, 0x9F}) # Waterfall of wishing (pre/post-Aga) + sheets[3].add_sprite_to_sheet([None, None, None, 14], {0x14, 0xA4}) # Graveyard (pre/post-Aga) + sheets[1].add_sprite_to_sheet([None, None, 76, 0x3F], {0x1B, 0xAB}) # Hyrule Castle (pre/post-Aga) + sheets[2].add_sprite_to_sheet([None, None, None, 0x3F], {}) # Hyrule Castle - rain state + # Smithy/Race/Kak (pre/post-Aga) + sheets[6].add_sprite_to_sheet([0x4F, 0x49, 0x4A, 0x50], {0x18, 0x22, 0x28, 0xA8, 0xB2, 0xB8}) + sheets[8].add_sprite_to_sheet([None, None, 18, None], {0x30, 0xC0}) # Desert (pre/post-Aga) + sheets[10].add_sprite_to_sheet([None, None, None, 17], {0x3A, 0xCA}) # M-rock (pre/post-Aga) + sheets[22].add_sprite_to_sheet([None, None, 24, None], {0x4F, 0xDF}) # Catfish (pre/post-Aga) + sheets[21].add_sprite_to_sheet([21, None, None, 21], {0x62, 0xF2}) # Smith DW (pre/post-Aga) + sheets[27].add_sprite_to_sheet([None, 42, None, None], {0x68, 0xF8}) # Dig Game (pre/post-Aga) + sheets[13].add_sprite_to_sheet([None, None, 76, None], {0x16, 0xA6}) # Witch hut (pre/post-Aga) + sheets[29].add_sprite_to_sheet([None, 77, None, 21], {0x69, 0xF9}) # VoO South (pre/post-Aga) + sheets[15].add_sprite_to_sheet([None, None, 78, None], {0x2A, 0xBA}) # Haunted Grove (pre/post-Aga) + sheets[17].add_sprite_to_sheet([None, None, None, 76], {0x6A, 0xFA}) # Stumpy (pre/post-Aga) + sheets[12].add_sprite_to_sheet([None, None, 55, 54], {0x80, 0x110}) # Specials (pre/post-Aga) + sheets[14].add_sprite_to_sheet([None, None, 12, 68], {0x81, 0x111}) # Zora's Domain (pre/post-Aga) + sheets[26].add_sprite_to_sheet([15, None, None, None], {0x92}) # Lumberjacks post-Aga + sheets[23].add_sprite_to_sheet([None, None, None, 25], {0x5E, 0xEE}) # PoD pre/post-Aga + + free_sheet_reqs = [ + [None, None, None, 0x14], # bully+pink ball needs this + [72, 73, None, None], # allow for blue archer + green bush + [None, 73, 19, None], # allow for green knife guard + [22, None, 23, None], # increase odds for snapdragon + [70, 73, None, None], # guards group (ballnchain, redbush, redjav, cannon, bomb, bluesain + [None, None, None, 0x15], # an option for talking trees + [None, None, None, 0x1B], # an option for talking trees + ] + + for group in free_sheet_reqs: + find_matching_sheet(group, sheets, range(1, 64)) + + +class NoMatchingSheetException(Exception): + pass + + +class IncompatibleEnemyException(Exception): + pass + + +def find_matching_sheet(groups, sheets, search_sheets, room_list=None, lock_match=False): + possible_sheets = [] + found_match = False + for num in search_sheets: + if num in {6, 65, 69, 71, 78, 79, 82, 88, 98}: # these are not useful sheets for randomization + continue + sheet = sheets[num] + valid = True + match = True + for idx, value in enumerate(groups): + if value is not None and sheet.locked[idx]: + valid = False + if (sheet.sub_groups[idx] not in value if isinstance(value, tuple) + else value != sheet.sub_groups[idx]): + match = False + elif value is not None: + match = False + if match: + found_match = True + if lock_match and room_list is not None: + sheet.room_set.update(room_list) + break + if valid: + possible_sheets.append(sheet) + if not found_match: + if len(possible_sheets) == 0: + raise NoMatchingSheetException + chosen_sheet = random.choice(possible_sheets) + chosen_groups = [(random.choice(g) if isinstance(g, tuple) else g) for g in groups] + chosen_sheet.add_sprite_to_sheet(chosen_groups, room_list) + + +def randomize_overworld_sprite_sheets(sheets, data_tables, custom_enemies): + setup_required_overworld_groups(sheets) + + setup_custom_enemy_sheets(custom_enemies, sheets, data_tables, range(1, 64), False) + + for num in range(1, 64): # sheets 0x1 to 0x3F inclusive + sheet = sheets[num] + if num == 6: # skip this group - it is locked for kakariko + continue + + free_slots = [idx for idx in range(0, 4) if not sheet.locked[idx]] + while free_slots: + choices = [c for c in data_tables.sheet_choices if all(slot in free_slots for slot in c.slots)] + weights = [c.weight for c in choices] + choice = random.choices(choices, weights, k=1)[0] + for idx, val in choice.assignments.items(): + v = random.choice(val) if isinstance(val, list) else val + sheet.sub_groups[idx] = v + sheet.locked[idx] = True + free_slots = [idx for idx in range(0, 4) if not sheet.locked[idx]] + + +class SheetChoice: + + def __init__(self, slots, assignments, weight): + self.slots = slots + self.assignments = assignments + self.weight = weight diff --git a/source/enemizer/TilePattern.py b/source/enemizer/TilePattern.py new file mode 100644 index 00000000..a452c547 --- /dev/null +++ b/source/enemizer/TilePattern.py @@ -0,0 +1,102 @@ +import os +import json +import codecs + +if __name__ == '__main__': + directory = './EnemizerCLI.Core/tiles' + for filename in os.listdir(directory): + with codecs.open(directory+'/'+filename, 'r', 'utf-8-sig') as fin: + pattern = json.load(fin) + pairs = [f'({x["x"]}, {x["y"]})' for x in pattern["Items"]] + print(f'(\'{filename}\', [{", ".join(pairs)}]),') + +tile_patterns = [ + ('heart soft', [(3, 1), (5, 2), (4, 7), (2, 5), (7, 1), (7, 5), (8, 4), (1, 2), (2, 1), (1, 4), (5, 7), (6, 1), + (6, 6), (4, 2), (8, 3), (1, 3), (3, 6), (8, 2)]), + ('metroid', [(2, 7), (7, 7), (1, 3), (3, 1), (8, 3), (1, 6), (4, 5), (5, 3), (1, 4), (6, 1), (6, 4), (8, 6), (3, 4), + (7, 5), (4, 1), (5, 5), (2, 2), (2, 5), (7, 2), (5, 1), (4, 3), (8, 4)]), + # ('moldorm vertical', [(5, 1), (6, 0), (7, 2), (5, 4), (4, 4), (4, 1), (3, 5), (5, 6), (3, 2), (6, 6), (7, 5), + # (6, 1), (4, 8), (3, 3), (5, 7), (3, 8), (2, 7), (6, 4), (4, 0), (3, 6), (7, 3), (4, 6)]), + # ('scream emoji', [(2, 2), (7, 2), (2, 3), (7, 3), (1, 7), (8, 7), (3, 2), (6, 2), (2, 6), (7, 6), (3, 3), (6, 3), + # (2, 7), (7, 7), (4, 5), (5, 7), (5, 5), (4, 7), (4, 6), (5, 6), (2, 5), (7, 5)]), + ('mario mushroom', [(3, 7), (4, 7), (5, 7), (3, 4), (4, 4), (5, 4), (3, 1), (4, 1), (5, 1), (2, 2), (6, 6), (6, 2), + (2, 6), (1, 3), (7, 5), (7, 3), (1, 5), (1, 4), (6, 5), (7, 4), (2, 5)]), + ('moldorm', [(1, 3), (2, 5), (3, 6), (5, 5), (7, 5), (7, 2), (5, 3), (3, 2), (2, 4), (1, 5), (5, 4), (6, 2), (7, 4), + (8, 1), (8, 4), (4, 2), (9, 2), (2, 3), (4, 6), (9, 3), (7, 3), (6, 6)]), + # ('thinking emoji', [(5, 6), (6, 4), (4, 3), (3, 1), (2, 6), (5, 3), (6, 6), (6, 1), (3, 0), (4, 7), (2, 4), + # (3, 8), (6, 0), (3, 7), (3, 3)]), + ('triangle', [(1, 5), (7, 5), (4, 2), (3, 5), (5, 5), (4, 3), (2, 4), (6, 4), (5, 3), (2, 5), (6, 5), (3, 3), + (4, 5), (5, 4), (3, 4), (4, 4)]), + ('heart', [(8, 3), (2, 3), (5, 7), (2, 4), (8, 4), (4, 6), (6, 6), (8, 2), (2, 2), (5, 2), (7, 1), (3, 1), (3, 5), + (6, 1), (7, 5), (4, 1)]), + ('arrghus', [(4, 1), (6, 2), (2, 3), (4, 3), (3, 4), (4, 5), (5, 6), (3, 7), (1, 4), (2, 5), (5, 1), (6, 3), (2, 2), + (7, 4), (5, 4), (4, 4), (6, 5), (3, 6), (5, 7), (3, 1), (3, 5), (5, 5)]), + # ('cowboy smile', [(1, 2), (3, 3), (4, 1), (3, 5), (5, 8), (6, 7), (5, 2), (7, 2), (1, 3), (2, 7), (5, 5), (5, 3), + # (3, 2), (4, 3), (7, 3), (2, 3), (4, 2), (3, 8), (4, 8), (6, 3)]), + ('clown face happy', [(2, 2), (7, 6), (7, 2), (2, 6), (3, 3), (6, 7), (6, 3), (3, 7), (2, 3), (5, 6), (7, 3), + (4, 6), (2, 5), (6, 6), (7, 5), (3, 6), (4, 5), (5, 7), (5, 5), (4, 7), (3, 2), (6, 2)]), + ('generic happy face', [(2, 1), (6, 3), (6, 5), (4, 6), (2, 2), (6, 6), (2, 3), (3, 5), (3, 2), (3, 3), (6, 2), + (5, 5), (1, 2), (3, 6), (7, 5), (7, 1), (4, 5), (8, 2), (5, 6), (2, 5), (7, 3), (7, 2)]), + # ('YMCA', [(1, 2), (2, 3), (5, 2), (7, 2), (6, 3), (7, 4), (2, 4), (5, 4), (6, 2), (3, 2), (5, 3), (7, 3), (7, 6), + # (4, 8), (2, 7), (6, 7), (4, 6), (8, 8), (8, 7), (3, 8), (3, 6), (6, 8)]), + # ('ze', [(5, 7), (6, 7), (7, 7), (1, 3), (2, 3), (3, 3), (5, 5), (6, 5), (7, 5), (1, 7), (2, 7), (3, 7), (5, 3), + # (6, 3), (7, 3), (3, 4), (2, 5), (1, 6), (5, 4), (5, 6)]), + # ('space invader metroid', [(4, 1), (2, 3), (3, 5), (5, 6), (7, 6), (7, 3), (2, 8), (3, 2), (1, 7), (1, 4), (6, 2), + # (8, 5), (7, 8), (4, 6), (8, 7), (5, 1), (2, 6), (8, 4), (1, 5), (6, 5)]), + ('screw attack', [(2, 7), (7, 4), (6, 1), (3, 7), (2, 4), (5, 6), (4, 2), (5, 4), (3, 6), (7, 1), (3, 3), (6, 4), + (4, 6), (4, 3), (6, 2), (3, 4), (6, 5), (5, 2), (4, 5), (4, 4), (5, 3), (5, 5)]), + ('vanilla wrong order', [(7, 2), (7, 4), (7, 5), (7, 7), (6, 3), (6, 5), (5, 2), (5, 3), (5, 4), (5, 5), (5, 6), + (4, 2), (4, 3), (4, 4), (4, 5), (4, 6), (3, 3), (3, 5), (2, 2), (2, 4), (2, 5), (2, 7)]), + ('tile shaped tiles', [(2, 7), (2, 6), (2, 5), (2, 4), (2, 3), (2, 2), (3, 2), (4, 2), (5, 2), (6, 2), (7, 2), + (7, 3), (7, 4), (7, 5), (7, 6), (7, 7), (6, 7), (5, 7), (4, 7), (3, 7), (4, 5), (5, 4)]), + # ('panda shocked emoji', [(7, 3), (7, 4), (3, 3), (3, 4), (5, 5), (5, 6), (5, 7), (4, 6), (4, 7), (6, 7), (6, 6), + # (8, 6), (8, 7), (8, 8), (2, 8), (2, 7), (2, 6), (2, 1), (3, 1), (7, 1), (8, 1), (5, 8)]), + ('JK', [(1, 5), (3, 4), (5, 3), (7, 3), (3, 2), (7, 5), (6, 4), (2, 6), (5, 6), (5, 5), (8, 2), (3, 5), (8, 6), + (5, 2), (3, 3), (5, 4)]), + ('dollar sign', [(6, 2), (5, 1), (4, 1), (3, 1), (2, 2), (2, 6), (3, 7), (4, 7), (5, 7), (6, 6), (2, 3), (6, 5), + (3, 4), (5, 4), (4, 4), (4, 0), (4, 2), (4, 3), (4, 5), (4, 6), (4, 8)]), + # ('rupee diagonal', [(1, 4), (1, 5), (1, 6), (1, 7), (2, 7), (3, 7), (4, 7), (5, 6), (6, 5), (7, 4), (7, 3), + # (7, 2), (7, 1), (6, 1), (5, 1), (4, 1), (3, 2), (2, 3), (3, 5), (4, 4), (5, 3)]), + # ('sword', [(1, 8), (8, 1), (8, 2), (1, 4), (7, 1), (5, 8), (7, 3), (2, 4), (6, 2), (5, 7), (6, 4), (2, 5), (5, 3), + # (4, 7), (5, 5), (3, 6), (4, 4), (2, 7), (4, 6), (3, 5)]), + ('z1 dungeon1', [(4, 6), (6, 4), (2, 4), (3, 7), (4, 3), (5, 7), (3, 2), (5, 4), (3, 5), (5, 5), (4, 2), (6, 3), + (4, 7), (3, 4), (7, 3), (4, 5), (4, 4)]), + # ('z1 dungeon 4', [(4, 8), (3, 6), (4, 4), (5, 2), (3, 1), (4, 3), (6, 2), (4, 1), (3, 3), (5, 4), (3, 5), (4, 7), + # (6, 1), (3, 8), (5, 7), (3, 2), (4, 6), (3, 4), (5, 1)]), + # ('LTTP', [(3, 4), (2, 3), (6, 2), (6, 4), (2, 2), (5, 2), (6, 3), (2, 4), (7, 2), (3, 6), (4, 8), (8, 7), (7, 6), + # (7, 8), (5, 6), (7, 7), (4, 6), (4, 7), (8, 6)]), + ('triple triforce', [(4, 2), (3, 3), (4, 3), (5, 3), (6, 5), (5, 6), (6, 6), (7, 6), (3, 6), (2, 6), (2, 5), + (1, 6)]), + # ('TILE', [(2, 2), (5, 4), (3, 2), (2, 4), (5, 2), (2, 3), (1, 2), (5, 3), (3, 6), (6, 6), (7, 8), (8, 6), (6, 7), + # (3, 8), (4, 8), (3, 7), (6, 8), (7, 6), (7, 7), (8, 8)]), + # ('panda thinking emoji', [(2, 1), (3, 1), (6, 2), (7, 2), (6, 3), (3, 2), (3, 3), (3, 5), (4, 5), (5, 5), (2, 6), + # (2, 7), (1, 7), (2, 8), (1, 8), (3, 7), (4, 7), (3, 8)]), + # ('pokata key', [(3, 1), (4, 2), (5, 3), (4, 4), (4, 6), (5, 7), (6, 8), (4, 8), (6, 6), (3, 3), (5, 1), (4, 5), + # (3, 2), (4, 3), (5, 4), (5, 6), (4, 7), (5, 8), (3, 4), (5, 2), (4, 1)]), + ('tile shaped tiles randomish', [(4, 2), (2, 2), (7, 5), (5, 4), (3, 2), (4, 5), (4, 7), (7, 7), (2, 7), (2, 4), + (7, 2), (2, 5), (5, 7), (7, 4), (5, 2), (6, 2), (3, 7), (2, 3), (7, 6), (6, 7), + (7, 3), (2, 6)]), + ('NO', [(1, 5), (4, 4), (3, 4), (6, 2), (8, 4), (1, 2), (8, 5), (7, 5), (1, 3), (6, 5), (6, 4), (1, 4), (4, 5), + (4, 2), (2, 3), (7, 2), (8, 2), (8, 3), (4, 3), (6, 3)]), + ('bomb', [(3, 3), (5, 7), (6, 4), (2, 6), (5, 3), (3, 7), (6, 6), (2, 4), (7, 2), (2, 5), (4, 7), (5, 1), (4, 2), + (6, 5), (6, 1), (4, 3), (8, 2)]), + # ('boot', [(6, 7), (8, 6), (5, 5), (5, 3), (8, 2), (3, 6), (4, 8), (7, 8), (8, 4), (6, 2), (2, 7), (3, 8), (4, 5), + # (5, 4), (7, 2), (8, 7), (8, 8), (8, 3), (5, 8), (2, 8), (5, 2), (8, 5)]), + # ('javalogo', [(5, 8), (4, 8), (3, 8), (2, 8), (1, 7), (5, 6), (4, 6), (3, 6), (2, 6), (1, 5), (7, 7), (8, 6), + # (7, 5), (3, 4), (3, 3), (2, 2), (3, 1), (5, 4), (5, 3), (6, 2)]), + # ('pokata and ender key', [(3, 1), (5, 3), (4, 4), (4, 6), (5, 7), (6, 8), (4, 8), (6, 6), (3, 3), (5, 1), (4, 5), + # (3, 2), (4, 7), (4, 1), (5, 8), (5, 2), (3, 4), (5, 6), (5, 4)]), + ('kitty', [(3, 1), (6, 4), (7, 6), (8, 3), (1, 4), (3, 7), (3, 4), (2, 2), (5, 2), (6, 1), (6, 5), (6, 7), (2, 6), + (7, 2), (8, 5), (1, 3), (1, 5), (4, 2), (3, 5), (4, 6), (8, 4), (5, 6)]), + ('creeper face', [(3, 7), (4, 5), (5, 4), (5, 6), (3, 3), (2, 2), (3, 2), (7, 3), (6, 7), (4, 6), (6, 3), (7, 2), + (2, 3), (4, 4), (3, 6), (6, 2), (6, 6), (5, 5)]), + ('fast', [(7, 7)]) +] + + +banned_tiles = { + # these are pots in tower of hera, only ban if pottery is on? + (1, 8), (1, 7), (3, 8), (5, 8), (7, 8), + # these are crystal barrier tiles + # (8, 3), (8, 4), (8, 5) # should I ban these too? bypasses barrier logic? regex \(8, [345]\) +} diff --git a/source/enemizer/__init__.py b/source/enemizer/__init__.py new file mode 100644 index 00000000..724252e9 --- /dev/null +++ b/source/enemizer/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make "source" package diff --git a/source/enemizer/damage_table.yaml b/source/enemizer/damage_table.yaml new file mode 100644 index 00000000..a87db369 --- /dev/null +++ b/source/enemizer/damage_table.yaml @@ -0,0 +1,304 @@ +# From: https://spannerisms.github.io/damage +# There are 16 damage classes, each with 8 subclasses. +# These subclasses determine the damage or effect inflicted on a sprite. +# Subclass data is stored as a table in ROM at $0D:B8F1 + +# DamageSource and SubClassTable + +#_0DB8F1: db $00, $01, $20, $FF, $FC, $FB, $00, $00 ; 0x00 - Boomerang +#_0DB8F9: db $00, $02, $40, $04, $00, $00, $00, $00 ; 0x01 - Sword 1 +#_0DB901: db $00, $04, $40, $02, $03, $00, $00, $00 ; 0x02 - Sword 2 +#_0DB909: db $00, $08, $40, $04, $00, $00, $00, $00 ; 0x03 - Sword 3 +#_0DB911: db $00, $10, $40, $08, $00, $00, $00, $00 ; 0x04 - Sword 4 +#_0DB919: db $00, $10, $40, $08, $00, $00, $00, $00 ; 0x05 - Sword 5 +#_0DB921: db $00, $04, $40, $10, $00, $00, $00, $00 ; 0x06 - Arrow +#_0DB929: db $00, $FF, $40, $FF, $FC, $FB, $00, $00 ; 0x07 - Hookshot +#_0DB931: db $00, $04, $40, $FF, $FC, $FB, $20, $00 ; 0x08 - Bomb +#_0DB939: db $00, $64, $18, $64, $00, $00, $00, $00 ; 0x09 - Silver arrow +#_0DB941: db $00, $F9, $FA, $FF, $64, $00, $00, $00 ; 0x0A - Powder +#_0DB949: db $00, $08, $40, $FD, $04, $10, $00, $00 ; 0x0B - Fire rod +#_0DB951: db $00, $08, $40, $FE, $04, $00, $00, $00 ; 0x0C - Ice rod +#_0DB959: db $00, $10, $40, $FD, $00, $00, $00, $00 ; 0x0D - Bombos +#_0DB961: db $00, $FE, $40, $10, $00, $00, $00, $00 ; 0x0E - Ether +#_0DB969: db $00, $20, $40, $FF, $00, $00, $00, $FA ; 0x0F - Quake + +# Special Values: +# $F9 Target becomes a faerie +# $FA Target becomes a blob +# $FB Target stunned for 32 frames +# $FC Target stunned for 128 frames +# $FD Target incinerated +# $FE Target becomes frozen +# $FF Target stunned for 255 frames + +DamageSource: + Boomerang: + class: 0x00 + subclass: [0x00, 0x01, 0x20, 0xFF, 0xFC, 0xFB, 0x00, 0x00] + Sword1: + class: 0x01 + subclass: [0x00, 0x02, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00] + Sword2: + class: 0x02 + subclass: [0x00, 0x04, 0x40, 0x02, 0x03, 0x00, 0x00, 0x00] + Sword3: + class: 0x03 + subclass: [0x00, 0x08, 0x40, 0x04, 0x00, 0x00, 0x00, 0x00] + Sword4: + class: 0x04 + subclass: [0x00, 0x10, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00] + Sword5: + class: 0x05 + subclass: [0x00, 0x10, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00] + Arrow: + class: 0x06 + subclass: [0x00, 0x04, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00] + Hookshot: + class: 0x07 + subclass: [0x00, 0xFF, 0x40, 0xFF, 0xFC, 0xFB, 0x00, 0x00] + Bomb: + class: 0x08 + subclass: [0x00, 0x04, 0x40, 0xFF, 0xFC, 0xFB, 0x20, 0x00] + SilverArrow: + class: 0x09 + subclass: [0x00, 0x64, 0x18, 0x64, 0x00, 0x00, 0x00, 0x00] + Powder: + class: 0x0A + subclass: [0x00, 0xF9, 0xFA, 0xFF, 0x64, 0x00, 0x00, 0x00] + FireRod: + class: 0x0B + subclass: [0x00, 0x08, 0x40, 0xFD, 0x04, 0x10, 0x00, 0x00] + IceRod: + class: 0x0C + subclass: [0x00, 0x08, 0x40, 0xFE, 0x04, 0x00, 0x00, 0x00] + Bombos: + class: 0x0D + subclass: [0x00, 0x10, 0x40, 0xFD, 0x00, 0x00, 0x00, 0x00] + Ether: + class: 0x0E + subclass: [0x00, 0xFE, 0x40, 0x10, 0x00, 0x00, 0x00, 0x00] + Quake: + class: 0x0F + subclass: [0x00, 0x20, 0x40, 0xFF, 0x00, 0x00, 0x00, 0xFA] + +# The subclass that each sprite should look for in each damage class is in a table in WRAM at $7F:6000 +# Rando migrated it to $31:C800 +SubClassTable: + 0x0: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 1, 3, 1, 1] + 0x1: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 1, 3, 1, 1] + 0x2: [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x3: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x4: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x5: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x6: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x7: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x8: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1, 7] + 0x9: [0, 1, 3, 3, 3, 3, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0] + 0xA: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1, 7] + 0xB: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 1, 1, 0, 0, 0] + 0xC: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 1, 1, 1, 3, 1] + 0xD: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 3, 3, 1, 3] + 0xE: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 2, 7] + 0xF: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 1, 3, 3, 2] + 0x10: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 3, 1] + 0x11: [4, 1, 1, 1, 1, 2, 1, 0, 2, 1, 0, 1, 3, 3, 1, 7] + 0x12: [3, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 3, 3, 3, 1, 7] + 0x13: [0, 1, 1, 1, 1, 1, 1, 3, 2, 3, 2, 0, 0, 3, 2, 7] + 0x14: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x15: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] + 0x16: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x17: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 7] + 0x18: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 1, 3, 3, 7] + 0x19: [1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 0, 1, 1, 3, 2, 3] + 0x1A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x1B: [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 2, 2, 2, 3, 2] + 0x1C: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x1D: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x1E: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x1F: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x20: [3, 1, 1, 1, 1, 1, 1, 1, 0, 1, 2, 3, 3, 3, 1, 7] + 0x21: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x22: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 2, 7] + 0x23: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 2, 3, 2, 3] + 0x24: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 2, 3, 2, 3] + 0x25: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x26: [0, 1, 1, 1, 1, 1, 0, 3, 3, 1, 0, 0, 0, 3, 1, 3] + 0x27: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 7] + 0x28: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x29: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x2A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0] + 0x2B: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x2C: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x2D: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x2E: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x2F: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x30: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x31: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x32: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x33: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x34: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x35: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x36: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x37: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x38: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x39: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x3A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x3B: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x3C: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x3D: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x3E: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 2, 2, 7] + 0x3F: [0, 0, 2, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1] + 0x40: [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x41: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 3, 1, 7] + 0x42: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1, 7] + 0x43: [3, 1, 4, 3, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 1, 7] + 0x44: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 1, 7] + 0x45: [3, 1, 4, 3, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 1, 7] + 0x46: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 3, 3, 1, 7] + 0x47: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 3, 2, 7] + 0x48: [3, 1, 4, 3, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 1, 7] + 0x49: [3, 1, 4, 3, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 2, 7] + 0x4A: [3, 1, 4, 3, 1, 1, 1, 1, 1, 1, 0, 3, 3, 3, 1, 7] + 0x4B: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 3, 3, 3, 1, 7] + 0x4C: [1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 0, 1, 1, 3, 3, 3] + 0x4D: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 2, 3, 2, 3] + 0x4E: [3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 3, 3, 1, 7] + 0x4F: [3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 3, 3, 1, 7] + 0x50: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x51: [3, 1, 1, 1, 1, 1, 2, 1, 1, 1, 0, 1, 3, 3, 3, 3] + 0x52: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x53: [1, 3, 3, 3, 3, 3, 3, 0, 1, 1, 0, 1, 1, 0, 0, 0] + 0x54: [0, 1, 3, 3, 3, 3, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0] + 0x55: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 3, 2, 1] + 0x56: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 3, 2, 1] + 0x57: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x58: [3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 2, 1, 3, 3, 1, 7] + 0x59: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x5A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x5B: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3] + 0x5C: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3] + 0x5D: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x5E: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x5F: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x60: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x61: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x62: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x63: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 3, 3, 3] # the pit spawns the 0x64 + 0x64: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 3, 3, 3] + 0x65: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x66: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x67: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x68: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x69: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x6A: [5, 1, 3, 1, 1, 1, 1, 1, 1, 1, 0, 3, 0, 3, 1, 7] + 0x6B: [3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1] + 0x6C: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x6D: [3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 3, 3, 1, 7] + 0x6E: [3, 1, 1, 1, 1, 1, 1, 2, 1, 1, 2, 3, 3, 3, 1, 7] + 0x6F: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 3, 3, 1, 3] + 0x70: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x71: [3, 1, 1, 1, 1, 1, 1, 3, 1, 1, 0, 3, 3, 3, 1, 3] + 0x72: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x73: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x74: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x75: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x76: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x77: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x78: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x79: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 1, 1, 1, 3, 1] + 0x7A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x7B: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x7C: [0, 1, 1, 1, 1, 1, 1, 0, 2, 1, 0, 3, 3, 3, 3, 3] + 0x7D: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x7E: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x7F: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x80: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x81: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 3, 2, 3, 2] + 0x82: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x83: [0, 1, 1, 2, 2, 1, 2, 0, 1, 2, 0, 0, 0, 0, 0, 0] + 0x84: [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0] + 0x85: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 2, 3, 2, 3] + 0x86: [0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 3, 3, 1, 7] + 0x87: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x88: [0, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0] # mothula + 0x89: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x8A: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x8B: [3, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 3, 3, 3, 2, 3] + 0x8C: [0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0] + 0x8D: [0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0] + 0x8E: [1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 0, 1, 3, 2, 2, 3] + 0x8F: [3, 1, 1, 1, 1, 1, 1, 2, 2, 1, 0, 3, 3, 3, 1, 2] + 0x90: [1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 3, 3, 2] + 0x91: [1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x92: [0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0] + 0x93: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x94: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 1, 3, 2, 3] + 0x95: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x96: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x97: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x98: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x99: [1, 1, 1, 1, 1, 1, 1, 2, 2, 1, 0, 1, 0, 3, 1, 2] + 0x9A: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 2, 3, 2, 1, 1] + 0x9B: [0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 2, 3, 2] + 0x9C: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 2, 3, 2, 2] + 0x9D: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 2, 3, 2, 2] + 0x9E: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0x9F: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xA0: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xA1: [0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 2, 0, 2, 0, 0] + 0xA2: [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 5, 0, 0, 0, 0] + 0xA3: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 2, 0, 0] + 0xA4: [0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 3, 0, 1, 3, 1] + 0xA5: [3, 1, 1, 1, 1, 1, 1, 1, 2, 1, 0, 3, 3, 3, 1, 3] + 0xA6: [3, 1, 1, 1, 1, 1, 1, 1, 2, 1, 0, 3, 3, 3, 1, 3] + 0xA7: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 2, 3, 2, 7] + 0xA8: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 3, 3, 1, 1] + 0xA9: [0, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 3, 3, 3, 1, 1] + 0xAA: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1, 3, 1, 3] + 0xAB: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0] + 0xAC: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xAD: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xAE: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xAF: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB0: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB1: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB2: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 1, 1, 1, 3, 1] + 0xB3: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB4: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB5: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB6: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB7: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB8: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xB9: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xBA: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xBB: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xBC: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xBD: [0, 0, 1, 1, 1, 1, 3, 0, 1, 1, 0, 0, 0, 0, 0, 0] + 0xBE: [0, 0, 1, 1, 1, 1, 3, 0, 1, 1, 0, 0, 0, 0, 0, 0] + 0xBF: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xC0: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xC1: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xC2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xC3: [0, 1, 1, 1, 1, 1, 3, 0, 1, 1, 0, 0, 0, 0, 0, 0] + 0xC4: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xC5: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 1] + 0xC6: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 1] + 0xC7: [0, 1, 1, 1, 1, 1, 1, 0, 1, 2, 0, 3, 1, 3, 1, 3] + 0xC8: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xC9: [5, 1, 1, 1, 1, 1, 3, 0, 2, 1, 0, 3, 3, 1, 3, 1] + 0xCA: [5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xCB: [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xCC: [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 4, 0, 0, 0] + 0xCD: [0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 4, 0, 0, 0, 0] + 0xCE: [0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xCF: [1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 0, 1, 1, 1, 2, 1] + 0xD0: [0, 0, 0, 1, 1, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0] + 0xD1: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 2, 2, 2] + 0xD2: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 7] + 0xD3: [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 1, 1, 1, 2, 7] + 0xD4: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xD5: [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xD6: [0, 0, 0, 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + 0xD7: [0, 0, 0, 0, 0, 0, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0] + 0xEF: [0, 1, 1, 2, 2, 1, 2, 0, 1, 2, 0, 0, 0, 0, 0, 0] + 0xF0: [0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0] diff --git a/source/enemizer/enemy_damage_table.yaml b/source/enemizer/enemy_damage_table.yaml new file mode 100644 index 00000000..f7ed65b5 --- /dev/null +++ b/source/enemizer/enemy_damage_table.yaml @@ -0,0 +1,10 @@ +0x00: [0x02, 0x01, 0x01] +0x01: [0x04, 0x04, 0x04] +0x02: [0x00, 0x00, 0x00] +0x03: [0x08, 0x04, 0x02] +0x04: [0x08, 0x08, 0x08] +0x05: [0x10, 0x08, 0x04] +0x06: [0x20, 0x10, 0x08] +0x07: [0x20, 0x18, 0x10] +0x08: [0x18, 0x10, 0x08] # Roller class damage +0x09: [0x40, 0x30, 0x18] # Ganon class damage - max 8 hearts diff --git a/source/enemizer/enemy_deny.yaml b/source/enemizer/enemy_deny.yaml new file mode 100644 index 00000000..bea935bf --- /dev/null +++ b/source/enemizer/enemy_deny.yaml @@ -0,0 +1,713 @@ +UwGeneralDeny: + - [ 0x0002, 0, [ "RollerVerticalDown", "Statue" ] ] #"Sewers - Rat Pots - Rat 1" + - [ 0x0002, 1, [ "RollerVerticalDown", "Statue" ] ] #"Sewers - Rat Pots - Rat 2" + - [ 0x0002, 2, [ "RollerVerticalUp", "Statue" ] ] #"Sewers - Rat Pots - Rat 3" + - [ 0x0002, 3, [ "RollerVerticalUp", "Statue" ] ] #"Sewers - Rat Pots - Rat 4" + - [ 0x0002, 4, [ "Statue" ] ] #"Sewers - Rat Pots - Rat 5" + - [ 0x0002, 15, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft" ] ] #"Sewers - Rat Pots - Rat 6" + - [ 0x0002, 16, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft" ] ] #"Sewers - Rat Pots - Rat 7" + - [ 0x0004, 1, ["Statue"]] + - [ 0x0004, 2, ["Statue"]] + - [ 0x0004, 3, ["Statue"]] + - [ 0x0004, 4, ["Statue"]] + - [ 0x0004, 15, ["Statue"]] + - [ 0x000a, 0, [ "RollerVerticalDown", "RollerVerticalUp" ] ] #"Palace of Darkness - Basement Ledge - Terrorpin 1" + - [ 0x000a, 1, [ "RollerHorizontalRight", "RollerHorizontalLeft" ] ] #"Palace of Darkness - Basement Ledge - Terrorpin 2" + - [ 0x000b, 1, [ "RollerVerticalDown", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Callback - Terrorpin 1" + - [ 0x000e, 0, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft" ] ] #"Ice Palace - Entrance - Freezor" + - [ 0x000e, 1, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft" ] ] #"Ice Palace - Bari Key - Top Bari" + - [ 0x000e, 2, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft" ] ] #"Ice Palace - Bari Key - Middle Bari" + - [ 0x0016, 0, [ "RollerVerticalDown", "RollerVerticalUp", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Pool - Zol 1" + - [ 0x0016, 1, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Pool - Zol 2" + - [ 0x0016, 2, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Pool - Blue Bari" + - [ 0x0016, 3, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Pool - Zol 3" + - [ 0x0017, 5, [ "Beamos", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Tower Of Hera - Bumper Room - Fire Bar (Clockwise)" + - [ 0x0019, 0, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Palace of Darkness - Dark Maze - Kodongo 1" + - [ 0x0019, 1, [ "RollerVerticalUp" ] ] #"Palace of Darkness - Dark Maze - Kodongo 2" + - [ 0x0019, 2, [ "RollerVerticalDown", "RollerVerticalUp" ] ] #"Palace of Darkness - Dark Maze - Kodongo 3" + - [ 0x0019, 3, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft" ] ] #"Palace of Darkness - Dark Maze - Kodongo 4" + - [ 0x001a, 0, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Compass Room - Mini Helmasaur 1" + - [ 0x001a, 5, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Compass Room - Mini Helmasaur 2" + - [ 0x001b, 3, [ "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Palace of Darkness - Mimics 2 - Red Eyegore" + - [ 0x001b, 4, [ "RollerVerticalUp" ] ] #"Palace of Darkness - Mimics 2 - Green Eyegore L" + - [ 0x001e, 3, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "BigSpike", "Bumper" ] ] #"Ice Palace - Blob Ambush - Red Bari 3" + - [ 0x001e, 4, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "BigSpike", "Bumper" ] ] #"Ice Palace - Blob Ambush - Red Bari 4" + - [ 0x001e, 5, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ice Palace - Blob Ambush - Zol 1" + - [ 0x001e, 6, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ice Palace - Blob Ambush - Zol 2" + - [ 0x001f, 0, [ "RollerHorizontalRight" ] ] #"Ice Palace - Big Key View - Pengator 1" + - [ 0x001f, 3, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots + - [ 0x0021, 3, [ "RollerVerticalDown", "RollerVerticalUp" ] ] #"Sewers - Dark U - Rat 2" + - [ 0x0021, 4, [ "RollerVerticalDown", "RollerVerticalUp" ] ] #"Sewers - Dark U - Rat 3" + - [ 0x0024, 6, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots + - [ 0x0026, 1, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Statue" ] ] #"Swamp Palace - Big Spoon - Red Bari 1" + - [ 0x0026, 8, [ "AntiFairyCircle", "Bumper", "Statue" ] ] #"Swamp Palace - Big Spoon - Red Bari 3" + - [ 0x0026, 9, [ "RollerHorizontalRight", "Statue" ] ] #"Swamp Palace - Big Spoon - Kyameron" + - [ 0x0026, 10, [ "Statue" ] ] # multiple push statues in this room can cause issues + - [ 0x0026, 11, [ "Statue" ] ] # multiple push statues in this room can cause issues + - [ 0x0027, 0, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalLeft", "FirebarCW" ] ] #"Tower Of Hera - Petting Zoo - Mini Moldorm 1" + - [ 0x0027, 1, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Tower Of Hera - Petting Zoo - Mini Moldorm 2" + - [ 0x0027, 2, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Tower Of Hera - Petting Zoo - Mini Moldorm 3" + - [ 0x0027, 4, ["Bumper", "BigSpike", "AntiFairyCircle", "RollerVerticalDown", "RollerVerticalUp"]] + - [ 0x0027, 5, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Tower Of Hera - Petting Zoo - Kodongo 1" + - [ 0x0028, 4, [ "RollerVerticalUp" ] ] #"Swamp Palace - Entrance Ledge - Spike Trap" + - [ 0x002a, 2, [ "SparkCW", "SparkCCW", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] #"Palace of Darkness - Arena Main - Hardhat Beetle 1" + - [ 0x002a, 3, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Arena Main - Hardhat Beetle 2" + - [ 0x002a, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ]] + - [ 0x002a, 6, [ "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Arena Main - Hardhat Beetle 5" + - [ 0x002b, 5, [ "RollerHorizontalRight" ] ] #"Palace of Darkness - Fairies - Red Bari 2" + - [ 0x002e, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW" ] ] #"Ice Palace - Penguin Chest - Pengator 1" + - [ 0x002e, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW" ] ] #"Ice Palace - Penguin Chest - Pengator 2" + - [ 0x002e, 2, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW" ] ] #"Ice Palace - Penguin Chest - Pengator 3" + - [ 0x002e, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW" ] ] #"Ice Palace - Penguin Chest - Pengator 4" + - [ 0x002e, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW" ] ] #"Ice Palace - Penguin Chest - Pengator 5" + - [ 0x002e, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "BigSpike", "FirebarCW", "FirebarCCW" ] ] #"Ice Palace - Penguin Chest - Pengator 6" + - [ 0x0034, 0, [ "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - West Wing - Hover 1" + - [ 0x0034, 1, [ "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - West Wing - Hover 2" + - [ 0x0034, 2, [ "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - West Wing - Kyameron" + - [ 0x0034, 4, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - West Wing - Zol" + - [ 0x0035, 6, [ "RollerHorizontalRight" ] ] #"Swamp Palace - West Lever - Stalfos 2" + - [ 0x0035, 9, [ "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Swamp Palace - West Lever - Blue Bari" + - [ 0x0036, 7, [ "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Lobby - Hover 3" + - [ 0x0037, 7, [ "RollerHorizontalRight" ] ] #"Swamp Palace - Water 1 - Blue Bari" + - [ 0x0038, 4, [ "RollerHorizontalRight" ] ] #"Swamp Palace - Long Hall - Kyameron 2" + - [ 0x0039, 3, [ "RollerVerticalUp", "RollerHorizontalLeft" ] ] #"Skull Woods - Play Pen - Mini Helmasaur" + - [ 0x0039, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "FirebarCW", "FirebarCCW" ] ] #"Skull Woods - Play Pen - Spike Trap 1" + - [ 0x0039, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft" ] ] #"Skull Woods - Play Pen - Hardhat Beetle" + - [ 0x0039, 6, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "FirebarCW", "FirebarCCW" ] ] #"Skull Woods - Play Pen - Spike Trap 2" + - [ 0x003b, 1, [ "Bumper" ]] + - [ 0x003c, 0, ["BigSpike"]] + - [ 0x003c, 1, [ "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Hookshot Cave - Blue Bari 1" + - [ 0x003c, 2, [ "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Hookshot Cave - Blue Bari 2" + - [ 0x003d, 9, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Torches 2 - Spark (Counterclockwise)" + - [ 0x003d, 10, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Torches 2 - Spark (Clockwise) 1" + - [ 0x003d, 12, [ "AntiFairyCircle", "Bumper" ] ] #"Ganon's Tower - Torches 2 - Bunny Beam" + - [ 0x003d, 13, [ "AntiFairyCircle", "Bumper" ] ] #"Ganon's Tower - Torches 2 - Antifairy" + - [ 0x003f, 1, [ "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper", "Statue"] ] #"Ice Palace - P Room - Stalfos Knight 1" + - [ 0x003f, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Statue"] ] #"Ice Palace - P Room - Stalfos Knight 2" + - [ 0x003f, 4, [ "Wizzrobe", "Statue", "Bumper", "BigSpike", "AntiFairyCircle"]] # Wizzrobes can't spawn on pots + - [ 0x0040, 0, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] # Agahnims Tower - Bridge - Blue Guard 1 + - [ 0x0040, 1, [ "Statue", "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] # Agahnims Tower - Bridge - Blue Guard 2 + - [ 0x0041, 0, [ "RollerHorizontalLeft" ] ] #"Sewers - Dark Cactus - Rat 1" + - [ 0x0041, 1, [ "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Sewers - Dark Cactus - Rat 2" + - [ 0x0041, 2, [ "RollerVerticalUp" ] ] #"Sewers - Dark Cactus - Rat 3" + - [ 0x0042, 0, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Sewers - Dark Rope Corridor - Rope 1" + - [ 0x0042, 1, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Sewers - Dark Rope Corridor - Rope 2" + - [ 0x0042, 2, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Sewers - Dark Rope Corridor - Rope 3" + - [ 0x0042, 3, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Sewers - Dark Rope Corridor - Rope 4" + - [ 0x0042, 4, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Sewers - Dark Rope Corridor - Rope 5" + - [ 0x0042, 5, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Sewers - Dark Rope Corridor - Rope 6" + - [ 0x0044, 4, [ "RollerVerticalUp", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Thieves' Town - Joke Room - Zol" + - [ 0x0044, 6, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "BigSpike" ] ] #"Thieves' Town - Joke Room - Red Bari" + - [ 0x0044, 8, [ "Statue", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Thieves' Town - Joke Room - Blue Bari 4" + - [ 0x0045, 1, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Thieves' Town - Basement Block Totems - Red Zazak" + - [ 0x0045, 4, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots + - [ 0x0045, 7, [ "AntiFairyCircle", "Bumper" ] ] #"Thieves' Town - Cells - Blue Zazak 4" + - [ 0x0045, 8, [ "RollerHorizontalRight" ] ] #"Thieves' Town - Cells - Zol" + - [ 0x0046, 0, [ "RollerVerticalUp", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper", "Statue" ] ] #"Swamp Palace - Big O Top - Hover 1" + - [ 0x0046, 2, [ "RollerVerticalDown", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper", "Statue" ] ] #"Swamp Palace - Big O Top - Hover 2" + - [ 0x0046, 4, [ "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper", "Statue" ] ] #"Swamp Palace - Big O Top - Hover 3" + - [ 0x0049, 5, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Bari Pits - Gibdo 2" + - [ 0x0049, 7, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Bari Pits - Gibdo 4" + - [ 0x0049, 8, [ "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Skull Woods - Bari Pits - Gibdo 5" + - [ 0x004b, 0, [ "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Palace of Darkness - Mimics 1 - Red Eyegore" + - [ 0x004b, 1, [ "RollerHorizontalRight" ] ] #"Palace of Darkness - Warp Hint - Antifairy 1" + - [ 0x004b, 5, [ "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Palace of Darkness - Jelly Hall - Blue Bari 1" + - [ 0x004b, 6, [ "AntiFairyCircle", "BigSpike" ] ] #"Palace of Darkness - Jelly Hall - Blue Bari 2" + - [ 0x004b, 7, [ "AntiFairyCircle", "BigSpike" ] ] #"Palace of Darkness - Jelly Hall - Blue Bari 3" + - [ 0x004e, 0, [ "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Blob Alley - Zol 1" + - [ 0x004e, 1, [ "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Blob Alley - Zol 2" + - [ 0x004e, 2, [ "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Blob Alley - Zol 3" + - [ 0x0050, 0, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Hyrule Castle - North West Passage - Green Guard" + - [ 0x0050, 1, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Hyrule Castle - North West Passage - Green Knife Guard 1" + - [ 0x0050, 2, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Hyrule Castle - North West Passage - Green Knife Guard 2" + - [ 0x0052, 0, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "Bumper" ] ] #"Hyrule Castle - North East Passage - Green Guard" + - [ 0x0052, 1, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "Bumper" ] ] #"Hyrule Castle - North East Passage - Green Knife Guard 1" + - [ 0x0052, 2, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "Bumper" ] ] #"Hyrule Castle - North East Passage - Green Knife Guard 2" + - [ 0x0053, 1, [ "AntiFairyCircle", "Bumper" ] ] #"Desert Palace - Bridge - Beamos 1" + - [ 0x0053, 5, [ "RollerVerticalDown" ] ] #"Desert Palace - Popo Genocide - Popo TL" + - [ 0x0053, 7, [ "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Desert Palace - Bridge - Popo 5" + - [ 0x0055, 1, [ "RollerVerticalUp", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Secret Passage Exit - Green Knife Guard 1" + - [ 0x0057, 0, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots + - [ 0x0057, 1, ["Statue"]] # Statue switch issues + - [ 0x0057, 2, [ "RollerVerticalUp", "AntiFairyCircle", "Bumper", "Statue" ] ] #"Skull Woods - Big Key Room - Spike Trap" + - [ 0x0057, 3, ["Statue"]] # Statue switch issues + - [ 0x0057, 4, ["Statue"]] # Statue switch issues + - [ 0x0057, 5, ["Statue"]] # Statue switch issues + - [ 0x0057, 7, [ "RollerVerticalUp", "RollerVerticalDown", "Statue" ] ] #"Skull Woods - Big Key Room - Gibdo 2" + - [ 0x0057, 8, ["Statue"]] # Statue switch issues + - [ 0x0057, 9, ["Statue"]] # Statue switch issues + - [ 0x0057, 10, ["Statue"]] # Statue switch issues + - [ 0x0057, 11, ["Statue"]] # Statue switch issues + - [ 0x0057, 12, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "BigSpike", "SpikeBlock", "Statue"]] #"Skull Woods - Big Key Room - Gibdo 6" + - [ 0x0057, 13, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue" ] ] #"Skull Woods - Big Key Room - Blue Bari 1" + - [ 0x0057, 14, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue" ] ] #"Skull Woods - Big Key Room - Blue Bari 2" + - [ 0x0058, 0, ["Statue"]] + - [ 0x0058, 1, ["Statue"]] + - [ 0x0058, 2, ["Statue"]] + - [ 0x0058, 3, ["Statue"]] + - [ 0x0058, 4, ["Statue"]] + - [ 0x0058, 6, ["Statue"]] + - [ 0x0058, 7, [ "RollerHorizontalLeft", "Statue" ] ] #"Skull Woods - Lever Room - Hardhat Beetle 2" + - [ 0x0058, 8, ["Statue"]] + - [ 0x0059, 0, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Skull Woods - Bridge Room - Mini Moldorm 1" + - [ 0x0059, 1, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Skull Woods - Bridge Room - Mini Moldorm 2" + - [ 0x0059, 9, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Skull Woods - Bridge Room - Gibdo 1" + - [ 0x005e, 3, [ "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Pit Trap - Big Spike Trap" + - [ 0x005e, 4, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Ice Palace - Pit Trap - Fire Bar (Clockwise)" + - [ 0x005f, 0, [ "RollerVerticalDown", "RollerHorizontalLeft" ] ] #"Ice Palace - Bari University - Blue Bari 1" + - [ 0x005f, 1, [ "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Ice Palace - Bari University - Blue Bari 2" + - [ 0x0060, 0, [ "RollerVerticalUp", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Hyrule Castle - West - Blue Guard" + - [ 0x0062, 0, [ "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Hyrule Castle - East - Blue Guard" + - [ 0x0064, 2, [ "Bumper" , "Beamos" ] ] #"Thieves' Town - Attic Hall Left - Keese 2" + - [ 0x0064, 3, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots + - [ 0x0064, 4, [ "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Thieves' Town - Attic Hall Left - Rat 1" + - [ 0x0065, 0, [ "RollerVerticalUp", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Thieves' Town - Attic Window - Rat 1" + - [ 0x0065, 1, [ "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Thieves' Town - Attic Window - Rat 2" + - [ 0x0065, 2, [ "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Thieves' Town - Attic Window - Rat 3" + - [ 0x0066, 0, [ "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Swamp Palace - Waterfall Room - Hover 1" + - [ 0x0066, 2, [ "AntiFairyCircle", "Bumper"]] + - [ 0x0067, 1, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Firebar Pits - Blue Bari 1" + - [ 0x0067, 2, ["Bumper"]] #"Skull Woods - Firebar Pits - Blue Bari 2" + - [ 0x0067, 3, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Skull Woods - Firebar Pits - Hardhat Beetle 1" + - [ 0x0067, 4, [ "AntiFairyCircle", "Bumper" ]] + - [ 0x0067, 5, [ "RollerVerticalDown" ] ] #"Skull Woods - Firebar Pits - Hardhat Beetle 3" + - [ 0x0067, 6, [ "RollerVerticalDown" ] ] #"Skull Woods - Firebar Pits - Hardhat Beetle 4" + - [ 0x0067, 7, [ "Beamos", "AntiFairyCircle", "Bumper", "BunnyBeam" ] ] #"Skull Woods - Firebar Pits - Fire Bar (Clockwise)" + - [ 0x006a, 0, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Dark Alley - Terrorpin 1" + - [ 0x006a, 1, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Dark Alley - Terrorpin 2" + - [ 0x006a, 2, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Palace of Darkness - Dark Alley - Antifairy 1" + - [ 0x006a, 4, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Dark Alley - Terrorpin 3" + - [ 0x006a, 5, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Palace of Darkness - Dark Alley - Terrorpin 4" + - [ 0x006b, 7, [ "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Ganon's Tower - Mimics 1 - Spike Trap 1" + - [ 0x0071, 0, [ "RollerHorizontalLeft" ] ] #"Hyrule Castle - Basement Trap - Green Guard" + - [ 0x0074, 0, [ "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Desert Palace - North Hallway - Red Devalant 1" + - [ 0x0074, 1, [ "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Desert Palace - North Hallway - Red Devalant 2" + - [ 0x0074, 4, [ "AntiFairyCircle", "Bumper" ] ] #"Desert Palace - North Hallway - Leever 1" + - [ 0x0074, 5, [ "AntiFairyCircle", "Bumper" ] ] #"Desert Palace - North Hallway - Leever 2" + - [ 0x0076, 1, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Toilet Left - Hover 1" + - [ 0x0076, 2, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Toilet Left - Kyameron" + - [ 0x0076, 3, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Toilet Left - Hover 2" + - [ 0x0076, 4, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Swamp Palace - Toilet Left - Zol" + - [ 0x0076, 6, [ "RollerVerticalDown", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Swamp Palace - Toilet Left - Blue Bari" + - [ 0x007b, 0, [ "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - DMs Room - Blue Bari 1" + - [ 0x007b, 1, [ "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - DMs Room - Blue Bari 2" + - [ 0x007b, 6, [ "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - DMs Room - Statue" + - [ 0x007b, 7, [ "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - DMs Room - Hardhat Beetle" + - [ 0x007c, 1, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Randomizer Room - Fire Bar (Counterclockwise)" + - [ 0x007c, 2, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Randomizer Room - Spike Trap" + - [ 0x007c, 3, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "Bumper", "Statue"]] #"Ganon's Tower - Randomizer Room - Fire Bar (Clockwise)" + - [ 0x007c, 4, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Randomizer Room - Hardhat Beetle" + - [ 0x007d, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - The Zoo - Fire Snake 1" + - [ 0x007d, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - The Zoo - Fire Snake 2" + - [ 0x007d, 2, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - The Zoo - Fire Snake 3" + - [ 0x007d, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - The Zoo - Fire Snake 4" + - [ 0x007d, 4, ["StalfosKnight", "Geldman", "Blob", "Stal"]] + - [ 0x007d, 7, ["RollerVerticalUp", "RollerHorizontalLeft", "StalfosKnight", "Geldman", "Blob", "Stal"]] #"Ganon's Tower - The Zoo - Mini Helmasaur" + - [ 0x007d, 8, ["RollerVerticalUp", "RollerHorizontalLeft", "RollerHorizontalRight", "StalfosKnight", "Geldman", "Blob", "Stal"]] #"Ganon's Tower - The Zoo - Red Bari" + - [ 0x007d, 10, ["StalfosKnight", "Geldman", "Blob", "Stal"]] +# todo - consider adding firesnake to 0-3: has a hard time moving, could block hookshots for quite a while + - [ 0x007f, 0, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Big Spikes - Red Bari 1" + - [ 0x007f, 1, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper", "ArmosStatue" ] ] #"Ice Palace - Big Spikes - Red Bari 2" + - [ 0x007f, 2, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Big Spikes - Red Bari 3" + - [ 0x007f, 3, [ "Statue", "SparkCW", "SparkCCW", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Big Spikes - Red Bari 4" + - [ 0x007f, 4, [ "RollerVerticalDown" ] ] #"Ice Palace - Big Spikes - Big Spike Trap 1" + - [ 0x007f, 5, [ "RollerVerticalDown" ] ] #"Ice Palace - Big Spikes - Big Spike Trap 2" + - [ 0x0082, 0, [ "RollerVerticalDown" ] ] #"Hyrule Castle - Basement Playpit - Blue Guard 1" + - [ 0x0082, 2, [ "RollerVerticalUp" ] ] #"Hyrule Castle - Basement Playpit - Blue Guard 3" + - [ 0x0083, 0, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Desert Palace - Left Hallway - Blue Devalant 1" + - [ 0x0084, 0, [ "RollerVerticalDown", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Desert Palace - Main Room - Left - Leever 1" + - [ 0x0084, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Desert Palace - Main Room - Left - Leever 2" + - [ 0x0085, 2, [ "RollerHorizontalRight" ] ] #"Desert Palace - Compass Room - Popo TL" + - [ 0x0085, 7, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Desert Palace - Right Hallway - Leever 2" + - [ 0x008b, 3, ["RollerHorizontalRight"]] + - [ 0x008b, 4, [ "Statue", "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "BigSpike"]] #"Ganon's Tower - Map Room - Spike Trap" + - [ 0x008b, 6, [ "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Map Room - Fire Bar (Clockwise)" + - [ 0x008b, 7, [ "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Map Room - Fire Bar (Counterclockwise)" + - [ 0x008c, 14, ["AntiFairyCircle", "BigSpike", "Bumper"]] + - [ 0x008d, 1, [ "AntiFairyCircle", "Bumper" ] ] #"Ganon's Tower - Tile Room - Yomo Medusa T" + - [ 0x008d, 7, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Tile Room - Spike Trap" + - [ 0x008d, 8, [ "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Tile Room - Stalfos" + - [ 0x008d, 9, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Tile Room - Fire Bar (Clockwise)" + - [ 0x008d, 10, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Ganon's Tower - Tile Room - Blue Bari 1" + - [ 0x008d, 12, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Ganon's Tower - Tile Room - Blue Bari 2" + - [ 0x008e, 2, [ "Wizzrobe", "Statue"] ] # Wizzrobes can't spawn on pots + - [ 0x0092, 8, [ "RollerVerticalUp", "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Misery Mire - Dark Weave - Spike Trap" + - [ 0x0092, 9, [ "RollerHorizontalRight" ] ] #"Misery Mire - Dark Weave - Antifairy 3" + - [ 0x0092, 10, [ "RollerHorizontalLeft" ] ] #"Misery Mire - Dark Weave - Stalfos" + - [ 0x0095, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock" ] ] #"Ganon's Tower - Conveyer Falling Bridge - Red Spear Guard 1" + - [ 0x0095, 1, [ "Statue", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Conveyer Falling Bridge - Red Spear Guard 2" + - [ 0x0095, 2, [ "Statue", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Conveyer Falling Bridge - Red Spear Guard 3" + - [ 0x0095, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock" ] ] #"Ganon's Tower - Conveyer Falling Bridge - Red Spear Guard 4" + - [ 0x0096, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Torches 1 - Fire Bar (Clockwise)" + - [ 0x0098, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Entrance - Zol 1" + - [ 0x0098, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Entrance - Zol 2" + - [ 0x0098, 2, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Entrance - Zol 3" + - [ 0x0098, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Entrance - Zol 4" + - [ 0x0098, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Entrance - Zol 5" + - [0x0099, 8, ["RollerHorizontalLeft", "RollerHorizontalRight"]] + - [ 0x009b, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Spike Switch - Spike Trap 1" + - [ 0x009b, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Spike Switch - Spike Trap 2" + - [ 0x009b, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] + - [ 0x009b, 7, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Spike Switch - Spike Trap 4" + - [ 0x009b, 8, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Spike Switch - Spike Trap 5" + - [ 0x009b, 9, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Spike Switch - Spike Trap 6" + - [ 0x009b, 10, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Spike Switch - Spike Trap 7" + - [ 0x009c, 1, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Invisible Floor Maze - Mini Helmasaur" + - [ 0x009c, 2, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Invisible Floor Maze - Hardhat Beetle 2" + - [ 0x009c, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Invisible Floor Maze - Hardhat Beetle 3" + - [ 0x009c, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Invisible Floor Maze - Hardhat Beetle 4" + - [ 0x009c, 5, [ "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Invisible Floor Maze - Hardhat Beetle 5" + - [ 0x009d, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ganon's Tower - Compass Room - Gibdo 2" + - [ 0x009d, 6, [ "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Ganon's Tower - Compass Room - Blue Bari 1" + - [ 0x009d, 7, [ "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Compass Room - Blue Bari 2" + - [ 0x009d, 8, [ "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Ganon's Tower - Compass Room - Blue Bari 3" + - [ 0x009e, 0, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Fairy Drop - blue - Red Bari 1" + - [ 0x009e, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Fairy Drop - blue - Red Bari 2" + - [ 0x009e, 2, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Fairy Drop - blue - Stalfos Knight" + - [ 0x009e, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Fairy Drop - blue - Red Bari 3" + - [ 0x00a0, 1, [ "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Boss Antechamber - Antifairy" + - [ 0x00a1, 2, [ "Statue", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Fish Room - Spark (Clockwise) 2" + - [ 0x00a1, 7, [ "Wizzrobe", "Statue"] ] # Wizzrobes can't spawn on pots + - [ 0x00a5, 2, [ "BigSpike" ] ] #"GT Wizzrobes 1 - Wizzrobe 3" + - [ 0x00a5, 10, [ "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ganon's Tower - Laser Bridge - Red Spear Guard" + - [ 0x00a8, 1, [ "RollerVerticalUp", "RollerHorizontalLeft" ] ] #"Eastern Palace - West Wing - Top - Stalfos 2" + - [ 0x00a8, 3, [ "RollerVerticalDown", "RollerHorizontalLeft" ] ] #"Eastern Palace - West Wing - Top - Stalfos 4" + - [ 0x00a9, 1, [ "RollerHorizontalRight", "RollerHorizontalLeft" ] ] + - [ 0x00aa, 4, [ "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Eastern Palace - East Wing - Top - Stalfos 3" + - [ 0x00aa, 5, [ "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Eastern Palace - East Wing - Top - Popo B 2" + - [ 0x00ab, 7, [ "RollerVerticalUp", "RollerHorizontalLeft" ] ] #"Thieves' Town - Spike Dodge - Spike Trap 6" + - [ 0x00ae, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ice Palace - Ice T - Blue Bari 1" + - [ 0x00ae, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Ice Palace - Ice T - Blue Bari 2" + - [ 0x00af, 0, [ "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Ice Clock - Fire Bar (Clockwise)" + - [ 0x00b1, 2, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Misery Mire - Hourglass - Spike Trap 1" + - [ 0x00b1, 3, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Misery Mire - Hourglass - Spike Trap 2" + - [ 0x00b1, 4, ["Bumper", "BigSpike", "AntiFairyCircle" ]] + - [ 0x00b2, 1, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots + - [ 0x00b2, 3, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots + - [ 0x00b2, 6, [ "RollerVerticalUp", "RollerHorizontalLeft" ] ] #"Misery Mire - Sluggula Cross - Sluggula TR" + - [ 0x00b2, 8, [ "RollerVerticalDown" ] ] #"Misery Mire - Popo Push - Medusa 1" + - [ 0x00b2, 9, [ "RollerVerticalUp" ] ] #"Misery Mire - Sluggula Cross - Sluggula BL" + - [ 0x00b3, 0, [ "RollerVerticalUp", "RollerHorizontalRight", "BigSpike", "SpikeBlock" ] ] #"Misery Mire - Spike Room - Stalfos 1" + - [ 0x00b3, 2, [ "Statue", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "Bumper" ] ] #"Misery Mire - Spike Room - Beamos" + - [ 0x00b3, 3, ["AntiFairyCircle", "Bumper" ]] #"Misery Mire - Spike Room - Yomo Medusa" + - [ 0x00b3, 4, ["AntiFairyCircle", "Bumper"]] + - [ 0x00b6, 7, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Turtle Rock - Tile Room - Zol 1" + - [ 0x00b6, 8, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Turtle Rock - Tile Room - Zol 2" + - [ 0x00ba, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Eastern Palace - Dark Stalfos - Antifairy 1" + - [ 0x00ba, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Eastern Palace - Dark Stalfos - Antifairy 2" + - [ 0x00ba, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Eastern Palace - Dark Stalfos - Popo B 1" + - [ 0x00ba, 6, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Eastern Palace - Dark Stalfos - Popo B 2" + - [ 0x00bb, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Thieves' Town - Spikeveyer - Gibo 1" + - [ 0x00bb, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "Bumper" ] ] #"Thieves' Town - Spikeveyer - Antifairy 1" + - [ 0x00bb, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Thieves' Town - Spikeveyer - Gibo 3" + - [ 0x00bb, 6, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Thieves' Town - Spikeveyer - Fire Snake" + - [ 0x00bb, 7, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Thieves' Town - Spikeveyer - Gibo 4" + - [ 0x00bb, 8, [ "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Thieves' Town - Spikeveyer - Gibo 5" + - [ 0x00bb, 9, [ "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Thieves' Town - Spikeveyer - Antifairy 2" + - [ 0x00bc, 6, [ "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Thieves' Town - Toilet - Stalfos 2" + - [ 0x00bc, 7, [ "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Thieves' Town - Toilet - Stalfos 3" + - [ 0x00bc, 8, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Thieves' Town - Toilet - Stalfos 4" + - [ 0x00bf, 0, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on collision + - [ 0x00c1, 3, [ "RollerVerticalUp", "RollerHorizontalLeft" ] ] #"Misery Mire - 4 Rails - Stalfos 1" + - [ 0x00c2, 0, [ "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Misery Mire - Main Lobby - blue - Fire Snake 1" + - [ 0x00c2, 5, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots + - [ 0x00c5, 6, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Turtle Rock - Catwalk - Mini Helmasaur" + - [ 0x00c5, 7, [ "Statue" ] ] #"Turtle Rock - Catwalk - Laser Eye (Left) 4" + - [ 0x00cb, 0, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots + - [ 0x00cb, 3, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Thieves' Town - Grand Room NW - Zol 1" + - [ 0x00cb, 5, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Thieves' Town - Grand Room NW - Zol 2" + - [ 0x00cb, 9, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] + - [ 0x00cb, 10, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] + - [ 0x00cb, 11, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots + - [ 0x00cc, 8, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #Prevents Pot access (Beamos okay?) + - [ 0x00cc, 12, [ "RollerVerticalUp", "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #Prevents Pot access (Beamos okay?) + - [ 0x00ce, 0, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCCW", "Bumper" ] ] #"Ice Palace - Over Boss - top - Red Bari 1" + - [ 0x00ce, 1, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCW", "Bumper" ] ] #"Ice Palace - Over Boss - top - Red Bari 2" + - [ 0x00ce, 3, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "Antifairy", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Ice Palace - Over Boss - top - Statue" + - [ 0x00ce, 4, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] + - [ 0x00ce, 5, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] + - [ 0x00ce, 6, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] + - [ 0x00ce, 7, [ "RollerVerticalDown", "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Beamos", "Bumper", "FirebarCW", "FirebarCCW"]] + - [ 0x00d0, 0, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] + - [ 0x00d0, 1, [ "AntiFairyCircle", "BigSpike", "Bumper"]] + - [ 0x00d0, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] + - [ 0x00d0, 5, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] + - [ 0x00d0, 6, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] + - [ 0x00d0, 7, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper"]] + - [ 0x00d0, 9, [ "AntiFairyCircle", "BigSpike", "Bumper"]] + - [ 0x00d0, 6, [ "Statue", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] # Agahnims Tower - Dark Maze - Blue Guard 2 + - [ 0x00d2, 8, [ "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Misery Mire - Mire 2 - Popo BL" + - [ 0x00d5, 4, [ "Statue", "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "FirebarCW", "FirebarCCW", "SpikeBlock", "Bumper" ] ] #"Turtle Rock - Eye Bridge - Hardhat Beetle" + - [ 0x00d8, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Eastern Palace - Kill Room 2 - Red Eyegore L" + - [ 0x00d8, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Eastern Palace - Kill Room 2 - Red Eyegore R" + - [ 0x00d8, 2, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Eastern Palace - Kill Room 2 - Popo B TL" + - [ 0x00d8, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Eastern Palace - Kill Room 2 - Popo B TR" + - [ 0x00d8, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Eastern Palace - Kill Room 2 - Popo B LT" + - [ 0x00d8, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Eastern Palace - Kill Room 2 - Popo B RT" + - [ 0x00d8, 6, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Eastern Palace - Kill Room 2 - Popo LB" + - [ 0x00d8, 7, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Eastern Palace - Kill Room 2 - Popo RB" + - [ 0x00d8, 8, [ "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Eastern Palace - Kill Room 1 - Red Eyegore" + - [ 0x00d9, 1, [ "RollerHorizontalRight" ] ] #"Eastern Palace - Dodgeball - Green Eyegore 1" + - [ 0x00db, 0, [ "Wizzrobe", "Statue" ] ] # Wizzrobes can't spawn on pots + - [ 0x00db, 3, [ "Bumper" ] ] # Okay in vanilla + - [ 0x00dc, 2, [ "AntiFairyCircle", "BigSpike", "Bumper" ] ] + - [ 0x00dc, 9, [ "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Thieves' Town - Grand Room SE - Fire Snake 2" + - [ 0x00df, 0, [ "RollerVerticalDown", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Paradox Cave - Top - Mini Moldorm 1" + - [ 0x00df, 1, [ "RollerVerticalDown", "RollerHorizontalRight", "AntiFairyCircle" ] ] #"Paradox Cave - Top - Mini Moldorm 2" + - [ 0x00e4, 0, [ "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Old Man Home - Keese 1" + - [ 0x00e4, 1, [ "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Old Man Home - Keese 2" + - [ 0x00e4, 2, [ "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Old Man Home - Keese 3" + - [ 0x00e5, 4, [ "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Old Man Home Circle - Keese 5" + - [ 0x00e5, 5, [ "RollerVerticalDown", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Old Man Home Circle - Keese 6" + - [ 0x00e7, 0, [ "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Death Mountain Descent Cave Right - Keese 1" + - [ 0x00e7, 1, [ "RollerVerticalUp", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Death Mountain Descent Cave Right - Keese 2" + - [ 0x00e7, 2, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Death Mountain Descent Cave Right - Keese 3" + - [ 0x00e7, 3, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Death Mountain Descent Cave Right - Keese 4" + - [ 0x00e7, 4, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper" ] ] #"Death Mountain Descent Cave Right - Keese 5" + - [ 0x00e7, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Death Mountain Descent Cave Right - Keese 6" + - [ 0x00e7, 6, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalRight" ] ] #"Death Mountain Descent Cave Right - Keese 7" + - [ 0x00e8, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "AntiFairyCircle", "SpikeBlock", "Bumper" ] ] #"Super Bunny Exit - Hardhat Beetle 1" + - [ 0x00e8, 1, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Super Bunny Exit - Hardhat Beetle 2" + - [ 0x00ee, 0, [ "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper" ] ] #"Sprial Cave Top - Mini Moldorm 1" + - [ 0x00ee, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Sprial Cave Top - Mini Moldorm 2" + - [ 0x00ee, 2, [ "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Sprial Cave Top - Mini Moldorm 3" + - [ 0x00ee, 3, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Sprial Cave Top - Blue Bari 1" + - [ 0x00ee, 4, [ "RollerVerticalUp", "RollerVerticalDown", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper" ] ] #"Sprial Cave Top - Blue Bari 2" + - [ 0x00ef, 1, [ "RollerVerticalUp", "RollerVerticalDown" ] ] #"Paradox Cave - Middle - Mini Moldorm 2" + - [ 0x00f1, 0, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 1" + - [ 0x00f1, 1, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 2" + - [ 0x00f1, 2, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 3" + - [ 0x00f1, 3, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 4" + - [ 0x00f1, 4, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 5" + - [ 0x00f1, 5, [ "RollerVerticalUp", "RollerVerticalDown", "RollerHorizontalLeft", "RollerHorizontalRight" ] ] #"Old Man Maze - Keese 6" + - [ 0x00fd, 0, [ "Bumper" ] ] + - [ 0x0107, 1, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] + - [ 0x0107, 2, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] + - [0x010b, 6, ["RollerHorizontalRight"]] + - [0x010c, 6, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] + - [0x010c, 7, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] +OwGeneralDeny: + - [0x03, 2, ["Gibo"]] # OldMan eating Gibo + - [0x03, 4, ["Gibo"]] # OldMan eating Gibo + - [0x03, 5, ["Gibo"]] # OldMan eating Gibo + - [0x03, 6, ["Gibo"]] # OldMan eating Gibo + - [0x03, 8, ["Gibo"]] # OldMan eating Gibo + - [0x03, 9, ["Gibo"]] # OldMan eating Gibo + - [0x03, 10, ["Gibo"]] # OldMan eating Gibo + - [0x05, 11, ["Bumper", "AntiFairyCircle"]] # Blocks path to portal + - [0x1e, 3, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle"]] # forbid a beamos here + - [0x40, 0, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] + - [0x40, 7, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] + - [0x40, 13, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] + - [0x40, 14, ["Beamos", "Bumper", "BigSpike", "AntiFairyCircle", "Thief"]] + - [0x5e, 0, ["Gibo"]] # kiki eating Gibo + - [0x5e, 1, ["Gibo"]] # kiki eating Gibo + - [0x5e, 2, ["Gibo"]] # kiki eating Gibo + - [0x5e, 3, ["Gibo"]] # kiki eating Gibo + - [0x5e, 4, ["RollerVerticalUp", "Gibo"]] # forbid that one roller for kiki pod, and the kiki eating Gibo + - [0x5e, 5, ["Gibo"]] # kiki eating Gibo + - [0x5e, 6, ["Gibo"]] # kiki eating Gibo + - [0x5e, 7, ["Gibo"]] # kiki eating Gibo + - [0x5e, 8, ["Gibo"]] # kiki eating Gibo + - [0x5e, 9, ["Gibo"]] # kiki eating Gibo + - [0x5e, 10, ["Gibo"]] # kiki eating Gibo + - [0x5e, 11, ["Gibo"]] # kiki eating Gibo + - [0x5e, 12, ["Gibo"]] # kiki eating Gibo + - [0x5e, 13, ["Gibo"]] # kiki eating Gibo + - [0x5e, 14, ["Gibo"]] # kiki eating Gibo + - [0x5e, 15, ["Gibo"]] # kiki eating Gibo + - [0x5e, 16, ["Gibo"]] # kiki eating Gibo + - [0x5e, 17, ["Gibo"]] # kiki eating Gibo + - [0x5e, 18, ["Gibo"]] # kiki eating Gibo + - [0x5e, 19, ["Gibo"]] # kiki eating Gibo + - [0x5e, 20, ["Gibo"]] # kiki eating Gibo + - [0x77, 1, ["Bumper"]] # soft-lock potential near ladder +UwEnemyDrop: + - [0x0085, 9, ["Babasu"]] # ran off the edge and didn't return + - [0x00cb, 3, ["Zoro"]] # layer issues + - [0x00cb, 5, ["Zoro"]] # layer issues + - [0x00cb, 9, ["Zoro"]] # layer issues + - [0x00cb, 10, ["Zoro"]] # layer issues + - [0x00cc, 5, ["Babasu"]] # little hard to see and kill appropriately +# the following are behind rails or otherwise unactivate-able + - [0x0077, 4, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] # can't activate here + - [0x0077, 5, ["StalfosKnight", "Geldman", "Blob", "Stal", "Wizzrobe"]] + - [0x008D, 10, ["StalfosKnight", "Geldman", "Blob", "Stal"]] + - [0x008D, 12, ["StalfosKnight", "Geldman", "Blob", "Stal"]] + - [0x00b0, 7, ["StalfosKnight", "Blob", "Stal", "Wizzrobe"]] # blocked, but Geldmen are probably okay + - [0x00b0, 8, ["StalfosKnight", "Blob", "Stal", "Wizzrobe"]] # blocked, but Geldmen are probably okay +# the following are not allowed at certain pits (or on conveyors near pits) +# because they despawned or clipped away or immediately fell, etc + - [0x003d, 9, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x003d, 10, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x0044, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x0044, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x0044, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x0044, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x0044, 4, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x0044, 5, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x0044, 6, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x0044, 8, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x0049, 10, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x007b, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x007b, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x007f, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x007f, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x007f, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x007f, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x0095, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x0095, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x0095, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x0095, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + - [0x00b5, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x00b5, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x00b5, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x00c6, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x00c6, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x00c6, 4, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x00c6, 5, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Bumper", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x00c6, 6, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + - [0x00e6, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] + # wizzrobe despawn issues - on pots/blocks - too close to some object + - [0x0013, 3, ["Wizzrobe"]] + - [0x0016, 0, ["Wizzrobe"]] + - [0x0016, 1, ["Wizzrobe"]] + - [0x0016, 2, ["Wizzrobe"]] + - [0x0016, 3, ["Wizzrobe"]] + - [0x0017, 5, ["Wizzrobe", "Stal"]] + - [0x0019, 2, ["Wizzrobe"]] + - [0x0019, 3, ["Wizzrobe"]] + - [0x001e, 1, ["Wizzrobe"]] + - [0x001e, 2, ["Wizzrobe"]] + - [0x0027, 5, ["Wizzrobe"]] + - [0x0027, 6, ["Wizzrobe"]] + - [0x002a, 3, ["Wizzrobe"]] + - [0x002a, 7, ["Wizzrobe"]] + - [0x002e, 4, ["Wizzrobe"]] + - [0x0035, 5, ["Wizzrobe"]] + - [0x0036, 8, ["Wizzrobe"]] + - [0x003b, 0, ["Wizzrobe"]] + - [0x003b, 2, ["Wizzrobe"]] + - [0x003b, 4, ["Wizzrobe"]] + - [0x003b, 6, ["Wizzrobe"]] + - [0x003c, 1, ["Wizzrobe"]] + - [0x003d, 11, ["Wizzrobe"]] + - [0x003d, 12, ["Wizzrobe"]] + - [0x003d, 13, ["Wizzrobe"]] + - [0x004b, 2, ["Wizzrobe"]] + - [0x004b, 6, ["Wizzrobe"]] + - [0x004b, 7, ["Wizzrobe"]] + - [0x004e, 3, ["Wizzrobe", "Stal"]] + - [0x0054, 3, ["Wizzrobe", "Stal"]] + - [0x0055, 2, ["Wizzrobe"]] # slightly on wall + - [0x005e, 4, ["Wizzrobe", "Stal"]] + - [0x0065, 3, ["Wizzrobe"]] + - [0x0067, 5, ["Wizzrobe"]] + - [0x0067, 6, ["Wizzrobe"]] + - [0x0067, 7, ["Wizzrobe", "Stal"]] + - [0x0067, 8, ["Wizzrobe", "Stal"]] + - [0x0074, 5, ["Wizzrobe"]] + - [0x007c, 1, ["Wizzrobe", "Stal"]] + - [0x007c, 3, ["Wizzrobe", "Stal"]] + - [0x007e, 1, ["Wizzrobe", "Stal"]] + - [0x007e, 6, ["Wizzrobe", "Stal"]] + - [0x0083, 9, ["Wizzrobe"]] + - [0x008b, 6, ["Wizzrobe", "Stal"]] + - [0x008b, 7, ["Wizzrobe", "Stal"]] + - [0x008d, 9, ["Wizzrobe", "Stal"]] + - [0x0096, 0, ["Wizzrobe", "Stal"]] + - [0x009b, 11, ["Wizzrobe"]] + - [0x009f, 5, ["Wizzrobe", "Stal"]] + - [0x00a1, 1, ["Wizzrobe"]] + - [0x00aa, 5, ["Wizzrobe"]] + - [0x00af, 0, ["Wizzrobe", "Stal"]] + - [0x00b0, 1, ["Wizzrobe"]] + - [0x00b0, 2, ["Wizzrobe"]] + - [0x00b2, 4, ["Wizzrobe"]] + - [0x00b8, 2, ["Wizzrobe"]] + - [0x00bf, 1, ["Wizzrobe"]] + - [0x00c1, 3, ["Wizzrobe", "Stal"]] + - [0x00c2, 0, ["Wizzrobe"]] + - [0x00c2, 3, ["Wizzrobe"]] + - [0x00c2, 7, ["Wizzrobe"]] + - [0x00ce, 4, ["Wizzrobe", "Leever", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", + "BlueArcher", "RedJavelinGuard", "GreenKnifeGuard", "GreenMimic", "RedMimic"]] + - [0x00ce, 5, ["Wizzrobe", "Leever", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", + "BlueArcher", "RedJavelinGuard", "GreenKnifeGuard", "GreenMimic", "RedMimic"]] + - [0x00ce, 6, ["Wizzrobe", "Leever", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", + "BlueArcher", "RedJavelinGuard", "GreenKnifeGuard", "GreenMimic", "RedMimic"]] + - [0x00ce, 7, ["Wizzrobe", "Leever", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", + "BlueArcher", "RedJavelinGuard", "GreenKnifeGuard", "GreenMimic", "RedMimic"]] + - [0x00d0, 0, ["Wizzrobe"]] + - [0x00d0, 2, ["Wizzrobe"]] + - [0x00d0, 4, ["Wizzrobe"]] + - [0x00d0, 5, ["Wizzrobe"]] + - [0x00d0, 9, ["Wizzrobe"]] + - [0x00d5, 4, ["Wizzrobe"]] + - [0x00df, 1, ["Wizzrobe"]] # slightly on wall + - [0x00e7, 4, ["Wizzrobe"]] # slightly on wall + - [0x00fd, 1, ["Wizzrobe"]] # slightly on rock + - [0x010c, 4, ["Wizzrobe"]] + - [0x010c, 5, ["Wizzrobe"]] + # other mimic cave spots are in the rail section + + # enemies that have problems with conveyors + - [0x003b, 0, ["GreenMimic", "RedMimic"]] + - [0x003b, 1, ["GreenMimic", "RedMimic"]] + - [0x003b, 2, ["GreenMimic", "RedMimic"]] + - [0x003b, 3, ["GreenMimic", "RedMimic"]] + - [0x003b, 4, ["GreenMimic", "RedMimic"]] + - [0x003b, 5, ["GreenMimic", "RedMimic"]] + - [0x003b, 6, ["GreenMimic", "RedMimic"]] + - [0x003d, 6, ["GreenMimic", "RedMimic"]] + - [0x003d, 7, ["GreenMimic", "RedMimic"]] + - [0x003e, 8, ["GreenMimic", "RedMimic"]] + - [0x003e, 9, ["GreenMimic", "RedMimic"]] + - [0x003e, 10, ["GreenMimic", "RedMimic"]] + - [0x003e, 11, ["GreenMimic", "RedMimic"]] + - [0x0044, 0, ["GreenMimic", "RedMimic"]] + - [0x0044, 1, ["GreenMimic", "RedMimic"]] + - [0x0044, 2, ["GreenMimic", "RedMimic"]] + - [0x0044, 3, ["GreenMimic", "RedMimic"]] + - [0x0044, 5, ["GreenMimic", "RedMimic"]] + - [0x004c, 0, ["GreenMimic", "RedMimic"]] + - [0x004c, 1, ["GreenMimic", "RedMimic"]] + - [0x004c, 2, ["GreenMimic", "RedMimic"]] + - [0x004c, 3, ["GreenMimic", "RedMimic"]] + - [0x004c, 4, ["GreenMimic", "RedMimic"]] + - [0x004c, 5, ["GreenMimic", "RedMimic"]] + - [0x004c, 6, ["GreenMimic", "RedMimic"]] + - [0x004c, 7, ["GreenMimic", "RedMimic"]] + - [0x005d, 3, ["GreenMimic", "RedMimic"]] + - [0x005d, 4, ["GreenMimic", "RedMimic"]] + - [0x005d, 5, ["GreenMimic", "RedMimic"]] + - [0x005d, 6, ["GreenMimic", "RedMimic"]] + - [0x005d, 8, ["GreenMimic", "RedMimic"]] + - [0x005d, 9, ["GreenMimic", "RedMimic"]] + - [0x005d, 10, ["GreenMimic", "RedMimic"]] + - [0x005d, 11, ["GreenMimic", "RedMimic"]] + - [0x005d, 12, ["GreenMimic", "RedMimic"]] +# - [0x006d, ?, ["GreenMimic", "RedMimic"]] # conveyor doesn't hit edge +# - [0x008b, ?, ["GreenMimic", "RedMimic"]] # conveyor doesn't hit edge + - [0x0092, 2, ["GreenMimic", "RedMimic"]] + - [0x00a5, 0, ["GreenMimic", "RedMimic"]] + - [0x00a5, 1, ["GreenMimic", "RedMimic"]] + - [0x00a5, 4, ["GreenMimic", "RedMimic"]] + - [0x00a5, 5, ["GreenMimic", "RedMimic"]] + - [0x00a5, 6, ["GreenMimic", "RedMimic"]] + - [0x00bb, 1, ["GreenMimic", "RedMimic"]] + - [0x00bb, 4, ["GreenMimic", "RedMimic"]] + - [0x00bb, 5, ["GreenMimic", "RedMimic"]] + - [0x00bb, 6, ["GreenMimic", "RedMimic"]] + - [0x00bb, 7, ["GreenMimic", "RedMimic"]] + - [0x00bb, 8, ["GreenMimic", "RedMimic"]] + - [0x00bb, 9, ["GreenMimic", "RedMimic"]] + - [0x00bb, 10, ["GreenMimic", "RedMimic"]] + - [0x00bc, 0, ["GreenMimic", "RedMimic"]] + - [0x00bc, 1, ["GreenMimic", "RedMimic"]] + - [0x00bc, 2, ["GreenMimic", "RedMimic"]] + - [0x00bc, 3, ["GreenMimic", "RedMimic"]] + - [0x00bc, 4, ["GreenMimic", "RedMimic"]] + - [0x00bc, 5, ["GreenMimic", "RedMimic"]] + - [0x00c1, 5, ["GreenMimic", "RedMimic"]] + - [0x00c1, 8, ["GreenMimic", "RedMimic"]] + - [0x00c1, 9, ["GreenMimic", "RedMimic"]] + - [0x00c1, 10, ["GreenMimic", "RedMimic"]] + - [0x00c1, 11, ["GreenMimic", "RedMimic"]] + - [0x00d1, 5, ["GreenMimic", "RedMimic"]] + - [0x00d1, 6, ["GreenMimic", "RedMimic"]] + + # the following are all slightly in the wall on spawn - not too applicable right now, these don't drop anyway + - [0x0064, 0, ["Leever"]] + - [0x00e5, 3, ["Wizzrobe"]] + - [0x00e5, 4, ["Leever", "Wizzrobe"]] + - [0x00e5, 5, ["Leever", "Wizzrobe"]] + # the pit one in 0xe6 room is in the pit section + - [0x00e6, 1, ["Leever", "Wizzrobe"]] + - [0x00e6, 2, ["Leever", "Wizzrobe"]] + - [0x00e6, 3, ["Leever", "Wizzrobe"]] + - [0x00e6, 4, ["Leever", "Wizzrobe"]] + - [0x00e7, 0, ["Wizzrobe"]] + - [0x00e7, 1, ["Wizzrobe"]] + - [0x00e7, 2, ["Wizzrobe"]] + - [0x00e7, 3, ["Leever"]] + - [0x00e7, 4, ["Leever", "Wizzrobe"]] + - [0x00e7, 5, ["Leever", "Wizzrobe"]] + - [0x00e7, 6, ["Wizzrobe"]] + - [0x00f0, 2, ["Leever"]] # clipped away + - [0x00f0, 3, ["Leever"]] + - [0x00f0, 4, ["Leever"]] + - [0x00f0, 5, ["Leever"]] + - [0x00f0, 6, ["Leever"]] + - [0x00f0, 8, ["Leever"]] + - [0x00f1, 0, ["Leever", "Wizzrobe"]] + - [0x00f1, 1, ["Leever", "Wizzrobe"]] + - [0x00f1, 2, ["Wizzrobe"]] + - [0x00f1, 3, ["Wizzrobe"]] + - [0x00f1, 4, ["Wizzrobe"]] + - [0x00f1, 5, ["Wizzrobe"]] + - [0x00f1, 6, ["Leever", "Wizzrobe"]] + - [0x00f1, 7, ["Leever", "Wizzrobe"]] + - [0x00f1, 8, ["Leever", "Wizzrobe"]] + - [0x00f1, 9, ["Leever", "Wizzrobe"]] + diff --git a/source/enemizer/enemy_weight.yaml b/source/enemizer/enemy_weight.yaml new file mode 100644 index 00000000..97c011eb --- /dev/null +++ b/source/enemizer/enemy_weight.yaml @@ -0,0 +1,206 @@ +UW: # Total 94431 + AntiFairy: 40 # 1.82885% raw:1727 15.28319% + AntiFairyCircle: 40 # 1.82885% raw:1727 15.28319% + ArmosStatue: 186 # 0.53796% raw:508 4.49558% + Babasu: 80 # 1.15428% raw:1090 9.64602% + BallNChain: 163 # 0.61526% raw:581 5.14159% + Beamos: 155 # 0.64703% raw:611 5.40708% + BigSpike: 40 # 1.82885% raw:1727 15.28319% + Blob: 87 # 1.15428% raw:1090 9.64602% + BlueArcher: 325 # 0.30816% raw:291 2.57522% + BlueBari: 49 # 2.02688% raw:1914 16.93805% + BlueGuard: 44 # 2.28527% raw:2158 19.09735% + BlueZazak: 294 # 0.33993% raw:321 2.84071% + BlueZirro: 180 # 0.55702% raw:526 4.65487% + BluesainBolt: 150 # 0.66821% raw:631 5.58407% + BombGuard: 163 # 0.61526% raw:581 5.14159% + Bumper: 55 # 1.82885% raw:1727 15.28319% + BunnyBeam: 19 # 5.40077% raw:5100 45.13274% + Buzzblob: 123 # 0.81223% raw:767 6.78761% + CannonTrooper: 163 # 0.61526% raw:581 5.14159% + Chainchomp: 238 # 0.42041% raw:397 3.51327% + Crab: 204 # 0.48925% raw:462 4.08850% + CricketRat: 212 # 0.47230% raw:446 3.94690% + Cucco: 120 # 0.54325% raw:513 4.53982% + Deadrock: 186 # 0.53796% raw:508 4.49558% + Debirando: 154 # 0.65127% raw:615 5.44248% + DebirandoPit: 154 # 0.65127% raw:615 5.44248% + Faerie: 19 + FakeMasterSword: 120 # 0.81223% raw:767 6.78761% + FireballZora: 165 # 0.60679% raw:573 5.07080% + FirebarCCW: 49 # 2.02688% raw:1914 16.93805% + FirebarCW: 49 # 2.02688% raw:1914 16.93805% + Firesnake: 49 # 2.02688% raw:1914 16.93805% + FloatingSkull: 49 # 2.02688% raw:1914 16.93805% + FloppingFish: 30 # 5.40077% raw:5100 45.13274% + Freezor: 454 # 0.22027% raw:208 1.84071% + Geldman: 445 # 0.22450% raw:212 1.87611% + Gibdo: 150 # 0.40453% raw:382 3.38053% + Gibo: 294 # 0.33993% raw:321 2.84071% + GreenBushGuard: 325 # 0.30816% raw:291 2.57522% + GreenEyegoreMimic: 175 # 0.57291% raw:541 4.78761% + GreenMimic: 175 # 0.57291% raw:541 4.78761% + GreenGuard: 55 # 1.80237% raw:1702 15.06195% + GreenKnifeGuard: 621 # 0.16096% raw:152 1.34513% + GreenZirro: 180 # 0.55702% raw:526 4.65487% + HardhatBeetle: 109 # 0.91495% raw:864 7.64602% + Hinox: 140 # 0.71269% raw:673 5.95575% + Hoarder: 123 # 0.81223% raw:767 6.78761% + Hoarder2: 123 # 0.81223% raw:767 6.78761% + Hover: 150 # 0.42147% raw:398 3.52212% + Keese: 212 # 0.47230% raw:446 3.94690% + Kodongo: 463 # 0.21603% raw:204 1.80531% + Kyameron: 237 # 0.42147% raw:398 3.52212% + Landmine: 19 # 5.40077% raw:5100 45.13274% + Leever: 154 # 0.65127% raw:615 5.44248% + Lynel: 400 # 0.17897% raw:169 1.49558% + MiniHelmasaur: 109 # 0.91495% raw:864 7.64602% + MiniMoldorm: 109 # 0.91495% raw:864 7.64602% + Moblin: 367 # 0.27216% raw:257 2.27434% + Octoballoon: 204 # 0.48925% raw:462 4.08850% + Octorok: 165 # 0.60679% raw:573 5.07080% + Octorok4Way: 204 # 0.48925% raw:462 4.08850% + Pengator: 300 # 0.22027% raw:208 1.84071% + Pikit: 180 # 0.55702% raw:526 4.65487% + Poe: 284 # 0.35264% raw:333 2.94690% + Pokey: 238 # 0.42041% raw:397 3.51327% + Popo: 155 # 0.64703% raw:611 5.40708% + Popo2: 155 # 0.64703% raw:611 5.40708% + Raven: 94 # 1.06109% raw:1002 8.86726% + RedBari: 49 # 2.02688% raw:1914 16.93805% + RedBushGuard: 163 # 0.61526% raw:581 5.14159% + RedEyegoreMimic: 175 # 0.57291% raw:541 4.78761% + RedMimic: 175 # 0.57291% raw:541 4.78761% + RedJavelinGuard: 163 # 0.61526% raw:581 5.14159% + RedSpearGuard: 44 # 2.28527% raw:2158 19.09735% + RedZazak: 294 # 0.33993% raw:321 2.84071% + RollerHorizontalLeft: 238 # 0.42041% raw:397 3.51327% + RollerHorizontalRight: 238 # 0.42041% raw:397 3.51327% + RollerVerticalDown: 238 # 0.42041% raw:397 3.51327% + RollerVerticalUp: 238 # 0.42041% raw:397 3.51327% + Ropa: 140 # 0.71269% raw:673 5.95575% + Sluggula: 360 # 0.27745% raw:262 2.31858% + Snake: 212 # 0.47230% raw:446 3.94690% + Snapdragon: 562 # 0.17791% raw:168 1.48673% + SparkCCW: 49 # 2.02688% raw:1914 16.93805% + SparkCW: 49 # 2.02688% raw:1914 16.93805% + SpikeBlock: 55 # 1.82885% raw:1727 15.28319% + Stal: 19 # 5.40077% raw:5100 45.13274% + Stalfos: 49 # 2.02688% raw:1914 16.93805% + StalfosKnight: 87 # 1.15428% raw:1090 9.64602% + Statue: 30 # 1.82885% raw:1727 15.28319% + Swamola: 402 # 0.24886% raw:235 2.07965% + Tektite: 186 # 0.53796% raw:508 4.49558% + Terrorpin: 463 # 0.21603% raw:204 1.80531% + Thief: 100 # 0.60997% raw:576 5.09735% + Toppo: 123 # 0.81223% raw:767 6.78761% + UsainBolt: 44 # 2.28527% raw:2158 19.09735% + Vulture: 445 # 0.22450% raw:212 1.87611% + Wallmaster: 247 # 0.40453% raw:382 3.38053% + Wizzrobe: 306 # 0.32722% raw:309 2.73451% +# YellowStalfos: 49 + Zora: 609 # 0.16414% raw:155 1.37168% + Zoro: 80 # 1.15428% raw:1090 9.64602% +OW: # Total 117724 + AntiFairy: 60 # 0.97346% raw:1146 10.14159% + AntiFairyCircle: 60 # 0.97346% raw:1146 10.14159% + ArmosStatue: 144 # 0.69570% raw:819 7.24779% + Babasu: 100 # 0.84605% raw:996 8.81416% + BallNChain: 121 # 0.82651% raw:973 8.61062% + Beamos: 184 # 0.54364% raw:640 5.66372% + BigSpike: 70 # 0.97346% raw:1146 10.14159% + Blob: 118 # 0.84605% raw:996 8.81416% + BlueArcher: 291 # 0.34403% raw:405 3.58407% + BlueBari: 47 # 2.11767% raw:2493 22.06195% + BlueGuard: 39 # 2.56617% raw:3021 26.73451% + BlueZazak: 247 # 0.40519% raw:477 4.22124% + BlueZirro: 158 # 0.63369% raw:746 6.60177% + BluesainBolt: 111 # 0.90381% raw:1064 9.41593% + BombGuard: 121 # 0.82651% raw:973 8.61062% + Bumper: 103 # 0.97346% raw:1146 10.14159% + BunnyBeam: 19 # 5.26656% raw:6200 54.86726% + Buzzblob: 86 # 1.15609% raw:1361 12.04425% + CannonTrooper: 121 # 0.82651% raw:973 8.61062% + Chainchomp: 226 # 0.44341% raw:522 4.61947% + Crab: 163 # 0.61500% raw:724 6.40708% + CricketRat: 236 # 0.42387% raw:499 4.41593% + Cucco: 140 # 0.60396% raw:711 6.29204% + Deadrock: 144 # 0.69570% raw:819 7.24779% + Debirando: 164 # 0.60905% raw:717 6.34513% + DebirandoPit: 164 # 0.60905% raw:717 6.34513% + Faerie: 19 + FakeMasterSword: 120 # 1.15609% raw:1361 12.04425% + FireballZora: 119 # 0.84350% raw:993 8.78761% + FirebarCCW: 47 # 2.11767% raw:2493 22.06195% + FirebarCW: 47 # 2.11767% raw:2493 22.06195% + Firesnake: 47 # 2.11767% raw:2493 22.06195% + FloatingSkull: 47 # 2.11767% raw:2493 22.06195% + FloppingFish: 30 # 5.26656% raw:6200 54.86726% + Freezor: 692 # 0.14441% raw:170 1.50442% + Geldman: 229 # 0.43746% raw:515 4.55752% + Gibdo: 300 # 0.15545% raw:183 1.61947% + Gibo: 247 # 0.40519% raw:477 4.22124% + GreenBushGuard: 291 # 0.34403% raw:405 3.58407% + GreenEyegoreMimic: 171 # 0.58527% raw:689 6.09735% + GreenMimic: 171 # 0.58527% raw:689 6.09735% + GreenGuard: 48 # 2.06330% raw:2429 21.49558% + GreenKnifeGuard: 589 # 0.16989% raw:200 1.76991% + GreenZirro: 158 # 0.63369% raw:746 6.60177% + HardhatBeetle: 113 # 0.88682% raw:1044 9.23894% + Hinox: 123 # 0.81462% raw:959 8.48673% + Hoarder: 86 # 1.15609% raw:1361 12.04425% + Hoarder2: 86 # 1.15609% raw:1361 12.04425% + Hover: 200 # 0.32449% raw:382 3.38053% + Keese: 236 # 0.42387% raw:499 4.41593% + Kodongo: 346 # 0.28881% raw:340 3.00885% + Kyameron: 308 # 0.32449% raw:382 3.38053% + Landmine: 19 # 5.26656% raw:6200 54.86726% + Leever: 164 # 0.60905% raw:717 6.34513% + Lynel: 325 # 0.30750% raw:362 3.20354% + MiniHelmasaur: 113 # 0.88682% raw:1044 9.23894% + MiniMoldorm: 113 # 0.88682% raw:1044 9.23894% + Moblin: 303 # 0.33043% raw:389 3.44248% + Octoballoon: 163 # 0.61500% raw:724 6.40708% + Octorok: 119 # 0.84350% raw:993 8.78761% + Octorok4Way: 163 # 0.61500% raw:724 6.40708% + Pengator: 400 # 0.14441% raw:170 1.50442% + Pikit: 158 # 0.63369% raw:746 6.60177% + Poe: 202 # 0.49608% raw:584 5.16814% + Pokey: 226 # 0.44341% raw:522 4.61947% + Popo: 184 # 0.54364% raw:640 5.66372% + Popo2: 184 # 0.54364% raw:640 5.66372% + Raven: 65 # 1.53325% raw:1805 15.97345% + RedBari: 47 # 2.11767% raw:2493 22.06195% + RedBushGuard: 121 # 0.82651% raw:973 8.61062% + RedEyegoreMimic: 171 # 0.58527% raw:689 6.09735% + RedMimic: 171 # 0.58527% raw:689 6.09735% + RedJavelinGuard: 121 # 0.82651% raw:973 8.61062% + RedSpearGuard: 39 # 2.56617% raw:3021 26.73451% + RedZazak: 247 # 0.40519% raw:477 4.22124% + RollerHorizontalLeft: 226 # 0.44341% raw:522 4.61947% + RollerHorizontalRight: 226 # 0.44341% raw:522 4.61947% + RollerVerticalDown: 226 # 0.44341% raw:522 4.61947% + RollerVerticalUp: 226 # 0.44341% raw:522 4.61947% + Ropa: 123 # 0.81462% raw:959 8.48673% + Sluggula: 443 # 0.22595% raw:266 2.35398% + Snake: 236 # 0.42387% raw:499 4.41593% + Snapdragon: 526 # 0.19028% raw:224 1.98230% + SparkCCW: 47 # 2.11767% raw:2493 22.06195% + SparkCW: 47 # 2.11767% raw:2493 22.06195% + SpikeBlock: 103 # 0.97346% raw:1146 10.14159% + Stal: 19 # 5.26656% raw:6200 54.86726% + Stalfos: 47 # 2.11767% raw:2493 22.06195% + StalfosKnight: 118 # 0.84605% raw:996 8.81416% + Statue: 60 # 0.97346% raw:1146 10.14159% + Swamola: 265 # 0.37715% raw:444 3.92920% + Tektite: 144 # 0.69570% raw:819 7.24779% + Terrorpin: 346 # 0.28881% raw:340 3.00885% + Thief: 100 # 0.39244% raw:462 4.08850% + Toppo: 86 # 1.15609% raw:1361 12.04425% + UsainBolt: 39 # 2.56617% raw:3021 26.73451% + Vulture: 229 # 0.43746% raw:515 4.55752% + Wallmaster: 643 # 0.15545% raw:183 1.61947% + Wizzrobe: 345 # 0.28966% raw:341 3.01770% +# YellowStalfos: 47 + Zora: 558 # 0.17923% raw:211 1.86726% + Zoro: 100 # 0.84605% raw:996 8.81416% \ No newline at end of file diff --git a/source/enemizer/sheet_weight.yaml b/source/enemizer/sheet_weight.yaml new file mode 100644 index 00000000..07adea77 --- /dev/null +++ b/source/enemizer/sheet_weight.yaml @@ -0,0 +1,168 @@ +SheetChoices: +# Complex + - slots: [0, 1] + assignments: + 0: 0x46 + 1: 0xd + weight: .5 # BluesainBolt(1/2) - 2 types + - slots: [0, 1] + assignments: + 0: 0x46 + 1: 0x49 + weight: 5.5 # CannonTrooper, BallNChain, RedBushGuard, RedJavelinGuard, BombGuard, BluesainBolt(1/2) + - slots: [0, 1] + assignments: + 0: 0x48 + 1: 0x49 + weight: 2 # GreenBushGuard, BlueArcher + - slots: [1, 2] + assignments: + 1: 0x49 + 2: 0x13 + weight: 1 # GreenKnifeGuard + - slots: [0, 2] + assignments: + 0: 0x16 + 2: 0x17 + weight: 1 # Snapdragon + - slots: [2, 3] + assignments: + 2: 0xc + 3: 0x44 + weight: 1 # Zora (walking) + +# Slot 0 (21 enemy types require slot 0) + - slots: [0] + assignments: + 0: [0xe, 0x15] + weight: 1 # Thief + - slots: [0] + assignments: + 0: 0x16 + weight: 2 # Ropa, Hinox + - slots: [0] + assignments: + 0: 0x1f + weight: 7 # Sparks, Firebars, FloatingSkull, RedBari, BlueBari, Firesnake, Stalfos, ~~YellowStalfos~~ + - slots: [0] + assignments: + 0: 0x2f + weight: 2 # Debirandos, Leever + +# Slot 1 (24 enemy types require slot 1) + - slots: [1] + assignments: + 1: 0x1e + weight: 3 # MiniMoldorm, MiniHelmasaur, Hardhat + - slots: [1] + assignments: + 1: 0x20 + weight: 3 # StalfosKnight, Blob, Babasus + - slots: [1] + assignments: + 1: 0x23 + weight: 1 # Wallmaster + - slots: [1] + assignments: + 1: 0x2c + weight: 4 # Beamos, Popos, Mimics (2) + - slots: [1] + assignments: + 1: 0x49 + weight: 2.5 # GreenGuard, RedSpearGuard, BlueGuard, UsainBolt (1/2) + - slots: [1] + assignments: + 1: 0xd + weight: 1.5 # RedSpearGuard, BlueGuard, UsainBolt (1/2) + +# Slot 2 (29 enemy types require slot 2) + - slots: [2] + assignments: + 2: 0xc + weight: 3 # Crab, Octoballon, FireballZora(1/2), Octorocks(1/2) (4Way is 0xC only?) + - slots: [2] + assignments: + 2: 0x12 + weight: 2 # Vulture, Geldman + - slots: [2] + assignments: + 2: 0x17 + weight: 1 # Moblin + - slots: [2] + assignments: + 2: 0x18 + weight: 1 # FireballZora(1/2) (note: immersible only), Octoroks(1/2) + - slots: [2] + assignments: + 2: [0x1c, 0x24] + weight: 3 # Keese, CricketRat, Snake + - slots: [2] + assignments: + 2: 0x22 + weight: 2 # Kyameron (note: immersible only), Hover + - slots: [2] + assignments: + 2: 0x23 + weight: 1 # Gibdo + - slots: [2] + assignments: + 2: 0x25 + weight: 1.5 # Sluggula, Wizzrobe (half) + - slots: [2] + assignments: + 2: 0x26 + weight: 1 # Pengator + - slots: [2] + assignments: + 2: 0x27 + weight: 3 # Rollers4, Chainchomp, Pokey + - slots: [2] + assignments: + 2: 0x28 + weight: 3 # Gibo, BlueZazak, RedZazak + - slots: [2] + assignments: + 2: 0x29 + weight: .5 # Wizzrobe (half) + - slots: [2] + assignments: + 2: 0x2a + weight: 2 # Kodongo, Terrorpin + - slots: [2] + assignments: + 2: 0x2e + weight: 2 # GreenEyeGoreMimic, RedEyeGoreMimic (Eyegores only) + +# Slot 3 (21 enemy types require slot 3) + - slots: [3] + assignments: + 3: 0x10 + weight: 3 # Deadrock, Tektite, ArmosStatue + - slots: [3] + assignments: + 3: 0x11 + weight: 4.5 # FakeMasterSword, Hoarder, Buzzblob, Toppo, Raven(1/2) + - slots: [3] + assignments: + 3: 0x14 + weight: 1 # Lynel + - slots: [3] + assignments: + 3: 0x15 + weight: 1.5 # Poe, Cucco(1/2) + - slots: [3] + assignments: + 3: 0x19 + weight: 1.5 # Swamola, Raven(1/2) + - slots: [3] + assignments: + 3: 0x1b + weight: 3 # GreenZirro, BlueZirro, Pikit + - slots: [3] + assignments: + 3: [0x50] + weight: .5 # Cucco(1/2) + - slots: [3] + assignments: + 3: [0x52, 0x53] + weight: 5 # SpikeBlock, Bumper, BigSpike, AntiFairy, Statue (AntiFairyCircle?) diff --git a/source/gui/bottom.py b/source/gui/bottom.py index beb77a1f..96d7880e 100644 --- a/source/gui/bottom.py +++ b/source/gui/bottom.py @@ -1,4 +1,4 @@ -from tkinter import ttk, messagebox, StringVar, Button, Entry, Frame, Label, E, W, LEFT, RIGHT, X +from tkinter import ttk, messagebox, StringVar, Button, Entry, Frame, Label, LEFT, RIGHT, X from argparse import Namespace import logging import os @@ -70,65 +70,67 @@ def bottom_frame(self, parent, args=None): def generateRom(): guiargs = create_guiargs(parent) # get default values for missing parameters - for k,v in vars(parse_cli(['--multi', str(guiargs.multi)])).items(): + cliargs = ['--multi', str(guiargs.multi)] + if hasattr(guiargs, 'customizer'): + cliargs.extend(['--customizer', str(guiargs.customizer)]) + for k,v in vars(parse_cli(cliargs)).items(): if k not in vars(guiargs): setattr(guiargs, k, v) elif type(v) is dict: # use same settings for every player - setattr(guiargs, k, {player: getattr(guiargs, k) for player in range(1, guiargs.multi + 1)}) + setattr(guiargs, k, {player: getattr(guiargs, k) for player in range(1, len(v) + 1)}) argsDump = vars(guiargs) - hasEnemizer = "enemizercli" in argsDump and os.path.isfile(argsDump["enemizercli"]) - needEnemizer = False - if hasEnemizer: - falsey = ["none", "default", False, 0] - for enemizerOption in ["shuffleenemies", "enemy_damage", "shufflebosses", "enemy_health"]: - if enemizerOption in argsDump: - if isinstance(argsDump[enemizerOption], dict): - for playerID,playerSetting in argsDump[enemizerOption].items(): - if not playerSetting in falsey: - needEnemizer = True - elif not argsDump[enemizerOption] in falsey: - needEnemizer = True - seeds = [] - if not needEnemizer or (needEnemizer and hasEnemizer): - try: - if guiargs.count is not None and guiargs.seed: - seed = guiargs.seed - for _ in range(guiargs.count): - seeds.append(seed) - main(seed=seed, args=guiargs, fish=parent.fish) - seed = random.randint(0, 999999999) - else: - if guiargs.seed: - seeds.append(guiargs.seed) - else: - random.seed(None) - guiargs.seed = random.randint(0, 999999999) - seeds.append(guiargs.seed) - main(seed=guiargs.seed, args=guiargs, fish=parent.fish) - except (FillError, EnemizerError, Exception, RuntimeError) as e: - logging.exception(e) - messagebox.showerror(title="Error while creating seed", message=str(e)) - else: - YES = parent.fish.translate("cli","cli","yes") - NO = parent.fish.translate("cli","cli","no") - successMsg = "" - made = {} - for k in [ "rom", "playthrough", "spoiler" ]: - made[k] = parent.fish.translate("cli","cli","made." + k) - made["enemizer"] = parent.fish.translate("cli","cli","used.enemizer") - for k in made: - v = made[k] - pattern = "([\w]+)(:)([\s]+)(.*)" - m = re.search(pattern,made[k]) - made[k] = m.group(1) + m.group(2) + ' ' + m.group(4) - successMsg += (made["rom"] % (YES if (guiargs.create_rom) else NO)) + "\n" - successMsg += (made["playthrough"] % (YES if (guiargs.calc_playthrough) else NO)) + "\n" - successMsg += (made["spoiler"] % (YES if (not guiargs.jsonout and guiargs.create_spoiler) else NO)) + "\n" - successMsg += (made["enemizer"] % (YES if needEnemizer else NO)) + "\n" - # FIXME: English - successMsg += ("Seed%s: %s" % ('s' if len(seeds) > 1 else "", ','.join(str(x) for x in seeds))) - messagebox.showinfo(title="Success", message=successMsg) + needEnemizer = False + falsey = ["none", "default", False, 0] + for enemizerOption in ["shuffleenemies", "enemy_damage", "shufflebosses", "enemy_health"]: + if enemizerOption in argsDump: + if isinstance(argsDump[enemizerOption], dict): + for playerID,playerSetting in argsDump[enemizerOption].items(): + if not playerSetting in falsey: + needEnemizer = True + elif not argsDump[enemizerOption] in falsey: + needEnemizer = True + seeds = [] + + try: + if guiargs.count is not None and guiargs.seed: + seed = guiargs.seed + for _ in range(guiargs.count): + seeds.append(seed) + main(seed=seed, args=guiargs, fish=parent.fish) + seed = random.randint(0, 999999999) + else: + if guiargs.seed: + seeds.append(guiargs.seed) + else: + random.seed(None) + guiargs.seed = random.randint(0, 999999999) + seeds.append(guiargs.seed) + main(seed=guiargs.seed, args=guiargs, fish=parent.fish) + except (FillError, EnemizerError, Exception, RuntimeError) as e: + logging.exception(e) + messagebox.showerror(title="Error while creating seed", message=str(e)) + else: + YES = parent.fish.translate("cli","cli","yes") + NO = parent.fish.translate("cli","cli","no") + successMsg = "" + made = {} + for k in [ "rom", "playthrough", "spoiler" ]: + made[k] = parent.fish.translate("cli","cli","made." + k) + made["enemizer"] = parent.fish.translate("cli","cli","used.enemizer") + for k in made: + v = made[k] + pattern = "([\w]+)(:)([\s]+)(.*)" + m = re.search(pattern,made[k]) + made[k] = m.group(1) + m.group(2) + ' ' + m.group(4) + successMsg += (made["rom"] % (YES if (guiargs.create_rom) else NO)) + "\n" + successMsg += (made["playthrough"] % (YES if (guiargs.calc_playthrough) else NO)) + "\n" + successMsg += (made["spoiler"] % (YES if (not guiargs.jsonout and guiargs.create_spoiler) else NO)) + "\n" + successMsg += (made["enemizer"] % (YES if needEnemizer else NO)) + "\n" + # FIXME: English + successMsg += ("Seed%s: %s" % ('s' if len(seeds) > 1 else "", ','.join(str(x) for x in seeds))) + + messagebox.showinfo(title="Success", message=successMsg) ## Generate Button # widget ID @@ -218,9 +220,6 @@ def create_guiargs(parent): if hasattr(pagewidgets[widget], 'storageVar'): setattr(guiargs, arg, pagewidgets[widget].storageVar.get()) - # Get EnemizerCLI setting - guiargs.enemizercli = parent.pages["randomizer"].pages["enemizer"].widgets["enemizercli"].storageVar.get() - # Get Multiworld Worlds count guiargs.multi = int(parent.pages["bottom"].pages["content"].widgets["worlds"].storageVar.get()) @@ -253,7 +252,9 @@ def create_guiargs(parent): "heartbeep": "heartbeep", "menuspeed": "fastmenu", "owpalettes": "ow_palettes", - "uwpalettes": "uw_palettes" + "uwpalettes": "uw_palettes", + "reduce_flashing": "reduce_flashing", + "shuffle_sfx": "shuffle_sfx" } for adjustarg in adjustargs: internal = adjustargs[adjustarg] diff --git a/source/gui/loadcliargs.py b/source/gui/loadcliargs.py index aec14a72..24d76aaf 100644 --- a/source/gui/loadcliargs.py +++ b/source/gui/loadcliargs.py @@ -80,20 +80,6 @@ def loadcliargs(gui, args, settings=None): # If we've got a Game Options val and we don't have an Adjust val, use the Game Options val gui.pages["adjust"].content.widgets[widget].storageVar.set(args[arg]) - # Get EnemizerCLI setting - mainpage = "randomizer" - subpage = "enemizer" - widget = "enemizercli" - setting = "enemizercli" - # set storagevar - gui.pages[mainpage].pages[subpage].widgets[widget].storageVar.set(args[setting]) - # set textbox/frame label - label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget) - gui.pages[mainpage].pages[subpage].widgets[widget].pieces["frame"].label.configure(text=label) - # set get from web label - label = fish.translate("gui","gui",mainpage + '.' + subpage + '.' + widget + ".online") - gui.pages[mainpage].pages[subpage].widgets[widget].pieces["online"].label.configure(text=label) - # Get baserom path mainpage = "randomizer" subpage = "generation" @@ -206,7 +192,9 @@ def loadadjustargs(gui, settings): "heartbeep": "adjust.heartbeep", "menuspeed": "adjust.menuspeed", "owpalettes": "adjust.owpalettes", - "uwpalettes": "adjust.uwpalettes" + "uwpalettes": "adjust.uwpalettes", + "reduce_flashing": "adjust.reduce_flashing", + "shuffle_sfx": "adjust.shuffle_sfx" } } } diff --git a/source/gui/randomize/enemizer.py b/source/gui/randomize/enemizer.py index f6ba1846..40bf055c 100644 --- a/source/gui/randomize/enemizer.py +++ b/source/gui/randomize/enemizer.py @@ -6,10 +6,7 @@ import webbrowser from source.classes.Empty import Empty def enemizer_page(parent,settings): - def open_enemizer_download(_evt): - webbrowser.open("https://github.com/Bonta0/Enemizer/releases") - - # Enemizer + # Enemizer self = ttk.Frame(parent) # Enemizer options @@ -29,7 +26,7 @@ def enemizer_page(parent,settings): self.frames["selectOptionsFrame"].pack(fill=X) self.frames["leftEnemizerFrame"].pack(side=LEFT) self.frames["rightEnemizerFrame"].pack(side=RIGHT) - self.frames["bottomEnemizerFrame"].pack(fill=X) + self.frames["bottomEnemizerFrame"].pack(fill=X, padx=(12, 0)) # Load Enemizer option widgets as defined by JSON file # Defns include frame name, widget type, widget options, widget placement attributes @@ -43,47 +40,8 @@ def enemizer_page(parent,settings): packAttrs = {"anchor":E} if self.widgets[key].type == "checkbox": packAttrs["anchor"] = W + if framename == 'bottomEnemizerFrame': + packAttrs["anchor"] = W self.widgets[key].pack(packAttrs) - ## Enemizer CLI Path - # This one's more-complicated, build it and stuff it - # widget ID - widget = "enemizercli" - - # Empty object - self.widgets[widget] = Empty() - # pieces - self.widgets[widget].pieces = {} - - # frame - self.widgets[widget].pieces["frame"] = Frame(self.frames["bottomEnemizerFrame"]) - # frame: label - self.widgets[widget].pieces["frame"].label = Label(self.widgets[widget].pieces["frame"], text="EnemizerCLI path: ") - self.widgets[widget].pieces["frame"].label.pack(side=LEFT) - - # get app online - self.widgets[widget].pieces["online"] = Empty() - # get app online: label - self.widgets[widget].pieces["online"].label = Label(self.widgets[widget].pieces["frame"], text="(get online)", fg="blue", cursor="hand2") - self.widgets[widget].pieces["online"].label.pack(side=LEFT) - # get app online: open browser - self.widgets[widget].pieces["online"].label.bind("", open_enemizer_download) - # storage var - self.widgets[widget].storageVar = StringVar(value=settings["enemizercli"]) - # textbox - self.widgets[widget].pieces["textbox"] = Entry(self.widgets[widget].pieces["frame"], textvariable=self.widgets[widget].storageVar) - self.widgets[widget].pieces["textbox"].pack(side=LEFT, fill=X, expand=True) - - def EnemizerSelectPath(): - path = filedialog.askopenfilename(filetypes=[("EnemizerCLI executable", "*EnemizerCLI*")], initialdir=os.path.join(".")) - if path: - self.widgets[widget].storageVar.set(path) - settings["enemizercli"] = path - # dialog button - self.widgets[widget].pieces["opendialog"] = Button(self.widgets[widget].pieces["frame"], text='...', command=EnemizerSelectPath) - self.widgets[widget].pieces["opendialog"].pack(side=LEFT) - - # frame: pack - self.widgets[widget].pieces["frame"].pack(fill=X) - - return self,settings + return self, settings diff --git a/source/gui/widgets.py b/source/gui/widgets.py index be664369..5e24840f 100644 --- a/source/gui/widgets.py +++ b/source/gui/widgets.py @@ -289,9 +289,9 @@ def widget_command(widget, command=""): temp_widget.storageVar.set('keys') temp_widget = root.pages["randomizer"].pages["item"].widgets["dropshuffle"] - text_output += f'\n {temp_widget.checkbox.cget("text")}' - if temp_widget.storageVar.get() == 0: - temp_widget.storageVar.set(1) + text_output += f'\n {temp_widget.label.cget("text")}' + if temp_widget.storageVar.get() == 'none': + temp_widget.storageVar.set('keys') if text_output: messagebox.showinfo('', f'The following settings were changed:{text_output}') diff --git a/source/item/District.py b/source/item/District.py index cdfa7bbf..bd482ba4 100644 --- a/source/item/District.py +++ b/source/item/District.py @@ -75,7 +75,7 @@ def create_district_helper(world, player): 'Fortune Teller (Dark)', 'Dark World Shop', 'Dark Lumberjack Shop', 'Skull Woods First Section Hole (West)', 'Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole'] - mire_entrances = ['Misery Mire', 'Mire Shed', 'Dark Desert Hint', 'Dark Desert Fairy'] + mire_entrances = ['Misery Mire', 'Mire Shed', 'Mire Hint', 'Mire Fairy'] ddm_entrances = ['Turtle Rock', 'Dark Death Mountain Ledge (West)', 'Dark Death Mountain Ledge (East)', 'Turtle Rock Isolated Ledge Entrance', 'Superbunny Cave (Top)', 'Superbunny Cave (Bottom)', 'Hookshot Cave', 'Hookshot Cave Back Entrance', 'Ganons Tower', 'Spike Cave', @@ -150,10 +150,10 @@ def find_reachable_locations(state, player): return check_set -inaccessible_regions_std = {'Desert Palace Mouth', 'Bumper Cave Ledge', 'Skull Woods Forest (West)', +inaccessible_regions_std = {'Desert Mouth', 'Bumper Cave Ledge', 'Skull Woods Forest (West)', 'Dark Death Mountain Ledge', 'Dark Death Mountain Isolated Ledge', 'Dark Death Mountain Floating Island'} -inaccessible_regions_inv = {'Desert Palace Mouth', 'Maze Race Ledge', 'Desert Ledge', - 'Desert Palace Entrance (North) Spot', 'Hyrule Castle Ledge', 'Death Mountain Return Ledge'} +inaccessible_regions_inv = {'Desert Mouth', 'Maze Race Ledge', 'Desert Ledge', + 'Desert Ledge Keep', 'Hyrule Castle Ledge', 'Mountain Pass Ledge'} diff --git a/source/item/FillUtil.py b/source/item/FillUtil.py index 8b5225b0..3fbdfe86 100644 --- a/source/item/FillUtil.py +++ b/source/item/FillUtil.py @@ -70,7 +70,7 @@ def create_item_pool_config(world): for player in range(1, world.players + 1): config.static_placement[player] = defaultdict(list) config.static_placement[player].update(vanilla_mapping) - if world.dropshuffle[player]: + if world.dropshuffle[player] != 'none': for item, locs in keydrop_vanilla_mapping.items(): config.static_placement[player][item].extend(locs) if world.pottery[player] not in ['none', 'cave']: @@ -96,7 +96,7 @@ def create_item_pool_config(world): for item, locs in vanilla_mapping.items(): if 'Small Key' in item: universal_key_locations.extend(locs) - if world.dropshuffle[player]: + if world.dropshuffle[player] != 'none': for item, locs in keydrop_vanilla_mapping.items(): if 'Small Key' in item: universal_key_locations.extend(locs) @@ -357,7 +357,7 @@ def determine_major_items(world, player): major_item_set.add('Single Arrow') if world.keyshuffle[player] == 'universal': major_item_set.add('Small Key (Universal)') - if world.goal[player] in {'triforcehunt', 'ganonhunt'}: + if world.goal[player] in {'triforcehunt', 'ganonhunt', 'trinity'}: major_item_set.add('Triforce Piece') if world.bombbag[player]: major_item_set.add('Bomb Upgrade (+10)') @@ -416,11 +416,11 @@ def filter_locations(item_to_place, locations, world, vanilla_skip=False, potion return filtered if world.algorithm == 'district': config = world.item_pool_config - if ((isinstance(item_to_place,str) and item_to_place == 'Placeholder') + if ((isinstance(item_to_place, str) and item_to_place == 'Placeholder') or item_to_place.name in config.item_pool[item_to_place.player]): restricted = config.location_groups[0].locations filtered = [l for l in locations if l.name in restricted and l.player in restricted[l.name]] - return filtered if len(filtered) > 0 else locations + return filtered elif potion: restricted = config.location_groups[0].locations filtered = [l for l in locations if l.name not in restricted or l.player not in restricted[l.name]] @@ -439,14 +439,16 @@ def filter_locations(item_to_place, locations, world, vanilla_skip=False, potion return locations -def filter_pot_locations(locations, world): + + +def filter_special_locations(locations, world, vanilla_matcher): if world.algorithm == 'district': config = world.item_pool_config restricted = config.location_groups[0].locations filtered = [l for l in locations if l.name not in restricted or l.player not in restricted[l.name]] return filtered if len(filtered) > 0 else locations if world.algorithm == 'vanilla_fill': - filtered = [l for l in locations if l.pot and l.pot.item in [PotItem.Chicken, PotItem.BigMagic]] + filtered = [l for l in locations if vanilla_matcher(l)] return filtered if len(filtered) > 0 else locations return locations diff --git a/source/logic/Rule.py b/source/logic/Rule.py new file mode 100644 index 00000000..970062bb --- /dev/null +++ b/source/logic/Rule.py @@ -0,0 +1,576 @@ +import itertools + +from collections import OrderedDict +try: + from fast_enum import FastEnum +except ImportError: + from enum import IntFlag as FastEnum + +from BaseClasses import CrystalBarrier, KeyRuleType +from Dungeons import dungeon_keys + + +class RuleType(FastEnum): + Conjunction = 0 + Disjunction = 1 + Item = 2 + Glitch = 3 + Reachability = 4 + Static = 5 + Bottle = 6 + Crystal = 7 + Barrier = 8 + Hearts = 9 + Unlimited = 10 + ExtendMagic = 11 + Boss = 12 + Negate = 13 + LocationCheck = 14 + SmallKeyDoor = 15 + + +class Rule(object): + + def __init__(self, rule_type): + self.rule_type = rule_type + self.sub_rules = [] + self.principal = None + self.player = 0 + self.resolution_hint = None + self.barrier = None + self.flag = None + self.locations = [] + self.count = 1 + + self.std_req = None + + self.rule_lambda = lambda state: True + + def eval(self, state): + return self.rule_lambda(state) + + def get_requirements(self, progressive_flag=True): + if not self.std_req: + reqs = rule_requirements[self.rule_type](self, progressive_flag) + self.std_req = standardize_requirements(reqs, progressive_flag) + return self.std_req + + def __str__(self): + return str(self.__unicode__()) + + def __unicode__(self): + return rule_prints[self.rule_type](self) + + +rule_prints = { + RuleType.Conjunction: lambda self: f'({" and ".join([str(x) for x in self.sub_rules])})', + RuleType.Disjunction: lambda self: f'({" or ".join([str(x) for x in self.sub_rules])})', + RuleType.Item: lambda self: f'has {self.principal}' if self.count == 1 else f'has {self.count} {self.principal}(s)', + RuleType.Reachability: lambda self: f'canReach {self.principal}', + RuleType.Static: lambda self: f'{self.principal}', + RuleType.Crystal: lambda self: f'has {self.principal} crystals', + RuleType.Barrier: lambda self: f'{self.barrier} @ {self.principal}', + RuleType.Hearts: lambda self: f'has {self.principal} hearts', + RuleType.Unlimited: lambda self: f'canBuyUnlimited {self.principal}', + RuleType.ExtendMagic: lambda self: f'magicNeeded {self.principal}', + RuleType.Boss: lambda self: f'canDefeat({self.principal.defeat_rule})', + RuleType.Negate: lambda self: f'not ({self.sub_rules[0]})', + RuleType.LocationCheck: lambda self: f'{self.principal} in [{", ".join(self.locations)}]', + RuleType.SmallKeyDoor: lambda self: f'doorOpen {self.principal[0]}:{self.principal[1]}' +} + + +def or_rule(rule1, rule2): + return lambda state: rule1(state) or rule2(state) + + +def and_rule(rule1, rule2): + return lambda state: rule1(state) and rule2(state) + + +class RuleFactory(object): + + @staticmethod + def static_rule(boolean): + rule = Rule(RuleType.Static) + rule.principal = boolean + rule.rule_lambda = lambda state: boolean + return rule + + @staticmethod + def conj(rules): + if len(rules) == 1: + return rules[0] + rule = Rule(RuleType.Conjunction) + rule_lambda = None + for r in rules: + if r is None: + continue + if r.rule_type == RuleType.Conjunction: + rule.sub_rules.extend(r.sub_rules) # todo: this extension for the lambda calc + elif r.rule_type == RuleType.Static and r.principal: # remove static flag if unnecessary + continue + elif r.rule_type == RuleType.Static and not r.principal: # always evaluates to false + return r + else: + rule.sub_rules.append(r) + if not rule_lambda: + rule_lambda = r.rule_lambda + else: + rule_lambda = and_rule(rule_lambda, r.rule_lambda) + rule.rule_lambda = rule_lambda if rule_lambda else lambda state: True + return rule + + @staticmethod + def disj(rules): + if len(rules) == 1: + return rules[0] + rule = Rule(RuleType.Disjunction) + rule_lambda = None + for r in rules: + if r is None: + continue + if r.rule_type == RuleType.Disjunction: + rule.sub_rules.extend(r.sub_rules) # todo: this extension for the lambda calc + elif r.rule_type == RuleType.Static and not r.principal: # remove static flag if unnecessary + continue + elif r.rule_type == RuleType.Static and r.principal: # always evaluates to true + return r + else: + rule.sub_rules.append(r) + if not rule_lambda: + rule_lambda = r.rule_lambda + else: + rule_lambda = or_rule(rule_lambda, r.rule_lambda) + rule.rule_lambda = rule_lambda if rule_lambda else lambda state: True + return rule + + @staticmethod + def item(item, player, count=1): + rule = Rule(RuleType.Item) + rule.principal = item + rule.player = player + rule.count = count + rule.rule_lambda = lambda state: state.has(item, player, count) + return rule + + @staticmethod + def bottle(player): + rule = Rule(RuleType.Bottle) + rule.player = player + rule.rule_lambda = lambda state: state.has_bottle(player) + return rule + + @staticmethod + def crystals(number, player): + rule = Rule(RuleType.Crystal) + rule.principal = number + rule.player = player + rule.rule_lambda = lambda state: state.has_crystals(number, player) + return rule + + @staticmethod + def barrier(region, player, barrier): + rule = Rule(RuleType.Barrier) + rule.principal = region + rule.player = player + rule.barrier = barrier + rule.rule_lambda = lambda state: state.can_cross_barrier(region, player, barrier) + return rule + + @staticmethod + def hearts(number, player): + rule = Rule(RuleType.Hearts) + rule.principal = number + rule.player = player + rule.rule_lambda = lambda state: state.has_hearts(number, player) + return rule + + @staticmethod + def unlimited(item, player, shop_regions): + rule = Rule(RuleType.Unlimited) + rule.principal = item + rule.player = player + rule.locations = shop_regions # list of regions where said item can be bought + rule.rule_lambda = lambda state: state.can_buy_unlimited(item, player) + return rule + + @staticmethod + def extend_magic(player, magic, difficulty, magic_potion_regions, flag): + rule = Rule(RuleType.ExtendMagic) + rule.principal = magic + rule.player = player + rule.resolution_hint = difficulty # world difficulty setting + rule.locations = magic_potion_regions # list of regions where blue/green can be bought + rule.flag = flag + rule.rule_lambda = lambda state: state.can_extend_magic(player, magic, flag) + return rule + + @staticmethod + def boss(boss): + rule = Rule(RuleType.Boss) + rule.principal = boss + rule.rule_lambda = lambda state: boss.defeat_rule.eval(state) + return rule + + @staticmethod + def neg(orig): + rule = Rule(RuleType.Negate) + rule.sub_rules.append(orig) + rule.rule_lambda = lambda state: not orig.rule_lambda(state) + return rule + + @staticmethod + def check_location(item, location, player): + rule = Rule(RuleType.LocationCheck) + rule.principal = item + rule.location = location + rule.player = player + rule.rule_lambda = eval_location(item, location, player) + return rule + + @staticmethod + def small_key_door(door_name, dungeon, player, door_rules): + rule = Rule(RuleType.SmallKeyDoor) + rule.principal = (door_name, dungeon) + rule.player = player + rule.resolution_hint = door_rules # door_rule object from KeyDoorShuffle + rule.rule_lambda = eval_small_key_door(door_name, dungeon, player) + return rule + + +def eval_location(item, location, player): + return lambda state: eval_location_main(item, location, player, state) + + +def eval_location_main(item, location, player, state): + location = state.world.get_location(location, player) + return location.item and location.item.name == item and location.player == player + + +def eval_small_key_door_main(state, door_name, dungeon, player): + if state.is_door_open(door_name, player): + return True + key_logic = state.world.key_logic[player][dungeon] + door_rule = key_logic.door_rules[door_name] + door_openable = False + for ruleType, number in door_rule.new_rules.items(): + if door_openable: + return True + if ruleType == KeyRuleType.WorstCase: + door_openable |= state.has_sm_key(key_logic.small_key_name, player, number) + elif ruleType == KeyRuleType.AllowSmall: + if (door_rule.small_location.item and door_rule.small_location.item.name == key_logic.small_key_name + and door_rule.small_location.item.player == player): + return True # always okay if allow small is on + elif isinstance(ruleType, tuple): + lock, lock_item = ruleType + # this doesn't track logical locks yet, i.e. hammer locks the item and hammer is there, but the item isn't + for loc in door_rule.alternate_big_key_loc: + spot = state.world.get_location(loc, player) + if spot.item and spot.item.name == lock_item: + door_openable |= state.has_sm_key(key_logic.small_key_name, player, number) + break + return door_openable + + +def eval_small_key_door(door_name, dungeon, player): + return lambda state: eval_small_key_door_main(state, door_name, dungeon, player) + + +def conjunction_requirements(rule, f): + combined = [ReqSet()] + for r in rule.sub_rules: + result = r.get_requirements(f) + combined = merge_requirements(combined, result) + return combined + + +def disjunction_requirements(rule, f): + results = [] + for r in rule.sub_rules: + result = r.get_requirements(f) + results.extend(result) + return results + + +rule_requirements = { + RuleType.Conjunction: conjunction_requirements, + RuleType.Disjunction: disjunction_requirements, + RuleType.Item: lambda rule, f: [ReqSet([Requirement(ReqType.Item, rule.principal, rule.player, rule, rule.count)])], + RuleType.Reachability: lambda rule, f: [ReqSet([Requirement(ReqType.Reachable, rule.principal, rule.player, rule)])], + RuleType.Static: lambda rule, f: static_req(rule), + RuleType.Crystal: lambda rule, f: crystal_requirements(rule), + RuleType.Bottle: lambda rule, f: [ReqSet([Requirement(ReqType.Item, 'Bottle', rule.player, rule, 1)])], + RuleType.Barrier: lambda rule, f: barrier_req(rule), + RuleType.Hearts: lambda rule, f: empty_req(), # todo: the one heart container + RuleType.Unlimited: lambda rule, f: unlimited_buys(rule), + RuleType.ExtendMagic: lambda rule, f: magic_requirements(rule), + RuleType.Boss: lambda rule, f: rule.principal.defeat_rule.get_requirements(f), + RuleType.Negate: lambda rule, f: empty_req(), # ignore these and just don't flood the key too early + RuleType.LocationCheck: lambda rule, f: location_check(rule), + RuleType.SmallKeyDoor: lambda rule, f: small_key_reqs(rule) +} + + +avail_crystals = ['Crystal 1', 'Crystal 2', 'Crystal 3', 'Crystal 4', 'Crystal 5', 'Crystal 6', 'Crystal 7'] + + +def crystal_requirements(rule): + crystal_rules = map(lambda c: Requirement(ReqType.Item, c, rule.player, rule), avail_crystals) + combinations = itertools.combinations(crystal_rules, rule.principal) + counter_list = [] + for combo in combinations: + counter_list.append(ReqSet(combo)) + return counter_list + + +# todo: 1/4 magic +def magic_requirements(rule): + if rule.principal <= 8: + return [set()] + bottle_val = 1.0 + if rule.resolution_hint == 'expert' and not rule.flag: + bottle_val = 0.25 + elif rule.resolution_hint == 'hard' and not rule.flag: + bottle_val = 0.5 + base, min_bot, reqs = 8, None, [] + for i in range(1, 5): + if base + bottle_val*base*i >= rule.principal: + min_bot = i + break + if min_bot: + for region in rule.locations: + reqs.append(ReqSet([Requirement(ReqType.Item, 'Bottle', rule.player, rule, min_bot), + Requirement(ReqType.Reachable, region, rule.player, rule)])) + if rule.principal <= 16: + reqs.append(ReqSet([Requirement(ReqType.Item, 'Magic Upgrade (1/2)', rule.player, rule, 1)])) + return reqs + else: + base, min_bot = 16, 4 + for i in range(1, 5): + if base + bottle_val*base*i >= rule.principal: + min_bot = i + break + if min_bot: + for region in rule.locations: + reqs.append(ReqSet([Requirement(ReqType.Item, 'Magic Upgrade (1/2)', rule.player, rule, 1), + Requirement(ReqType.Item, 'Bottle', rule.player, rule, min_bot), + Requirement(ReqType.Reachable, region, rule.player, rule)])) + return reqs + + +def static_req(rule): + return [ReqSet()] if rule.principal else [ReqSet([Requirement(ReqType.Item, 'Impossible', rule.player, rule)])] + + +def barrier_req(rule): + return [ReqSet([Requirement(ReqType.Reachable, rule.principal, rule.player, rule, crystal=rule.barrier)])] + + +def empty_req(): + return [ReqSet()] + + +def location_check(rule): + return [ReqSet([Requirement(ReqType.Placement, rule.principal, rule.player, rule, locations=rule.locations)])] + + +def unlimited_buys(rule): + requirements = [] + for region in rule.locations: + requirements.append(ReqSet([Requirement(ReqType.Reachable, region, rule.player, rule)])) + return requirements + + +def small_key_reqs(rule): + requirements = [] + door_name, dungeon = rule.principal + key_name = dungeon_keys[dungeon] + for rule_type, number in rule.resolution_hint.new_rules.items(): + if rule_type == KeyRuleType.WorstCase: + requirements.append(ReqSet([Requirement(ReqType.Item, key_name, rule.player, rule, number)])) + elif rule_type == KeyRuleType.AllowSmall: + small_loc = rule.resolution_hint.small_location.name + requirements.append(ReqSet([ + Requirement(ReqType.Placement, key_name, rule.player, rule, locations=[small_loc]), + Requirement(ReqType.Item, key_name, rule.player, rule, number)])) + elif isinstance(rule_type, tuple): + lock, lock_item = rule_type + locs = [x.name for x in rule.resolution_hint.alternate_big_key_loc] + requirements.append(ReqSet([ + Requirement(ReqType.Placement, lock_item, rule.player, rule, locations=locs), + Requirement(ReqType.Item, key_name, rule.player, rule, number)])) + return requirements + + +class ReqType(FastEnum): + Item = 0 + Placement = 2 + + +class ReqSet(object): + + def __init__(self, requirements=None): + if requirements is None: + requirements = [] + self.keyed = OrderedDict() + for r in requirements: + self.keyed[r.simple_key()] = r + + def append(self, req): + self.keyed[req.simple_key()] = req + + def get_values(self): + return self.keyed.values() + + def merge(self, other): + new_set = ReqSet(self.get_values()) + for r in other.get_values(): + key = r.simple_key() + if key in new_set.keyed: + new_set.keyed[key] = max(r, new_set.keyed[key], key=lambda r: r.amount) + else: + new_set.keyed[key] = r + return new_set + + def redundant(self, other): + for k, req in other.keyed.items(): + if k not in self.keyed: + return False + elif self.keyed[k].amount < req.amount: + return False + return True + + def different(self, other): + for key in self.keyed.keys(): + if key not in other.keyed: + return True + if key in other.keyed and self.keyed[key].amount > other.keyed[key].amount: + return True + return False + + def find_item(self, item_name): + for key, req in self.keyed.items(): + if req.req_type == ReqType.Item and req.item == item_name: + return req + return None + + def __eq__(self, other): + for key, req in self.keyed.items(): + if key not in other.keyed: + return False + if req.amount != other.keyed[key].amount: + return False + for key in other.keyed: + if key not in self.keyed: + return False + return True + + def __str__(self): + return str(self.__unicode__()) + + def __unicode__(self): + return " and ".join([str(x) for x in self.keyed.values()]) + + +class Requirement(object): + + def __init__(self, req_type, item, player, rule, amount=1, crystal=CrystalBarrier.Null, locations=()): + self.req_type = req_type + self.item = item + self.player = player + self.rule = rule + self.amount = amount + self.crystal = crystal + self.locations = tuple(locations) + + def simple_key(self): + return self.req_type, self.item, self.player, self.crystal, self.locations + + def key(self): + return self.req_type, self.item, self.player, self.amount, self.crystal, self.locations + + def __eq__(self, other): + if isinstance(other, Requirement): + return self.key() == other.key() + return NotImplemented + + def __hash__(self): + return hash(self.key()) + + def __str__(self): + return str(self.__unicode__()) + + def __unicode__(self): + if self.req_type == ReqType.Item: + return f'has {self.item}' if self.amount == 1 else f'has {self.amount} {self.item}(s)' + elif self.req_type == ReqType.Placement: + return f'{self.item} located @ {",".join(self.locations)}' + + +# requirement utility methods +def merge_requirements(starting_requirements, new_requirements): + merge = [] + for req in starting_requirements: + for new_r in new_requirements: + merge.append(req.merge(new_r)) + return reduce_requirements(merge) + + +only_one = {'Moon Pearl', 'Hammer', 'Blue Boomerang', 'Red Boomerang', 'Hookshot', 'Mushroom', 'Powder', + 'Fire Rod', 'Ice Rod', 'Bombos', 'Ether', 'Quake', 'Lamp', 'Shovel', 'Ocarina', 'Bug Catching Net', + 'Book of Mudora', 'Magic Mirror', 'Cape', 'Cane of Somaria', 'Cane of Byrna', 'Flippers', 'Pegasus Boots'} + + +def standardize_requirements(requirements, progressive_flag): + assert isinstance(requirements, list) + for req in requirements: + for thing in req.get_values(): + if thing.item in only_one and thing.amount > 1: + thing.amount = 1 + if progressive_flag: + substitute_progressive(req) + return reduce_requirements(requirements) + + +def reduce_requirements(requirements): + removals = [] + reduced = list(requirements) + # subset manip + ttl = len(reduced) + for i in range(0, ttl - 1): + for j in range(i + 1, ttl): + req, other_req = reduced[i], reduced[j] + if req.redundant(other_req): + removals.append(req) + elif other_req.redundant(req): + removals.append(other_req) + for removal in removals: + if removal in reduced: + reduced.remove(removal) + assert len(reduced) != 0 + return reduced + + +progress_sub = { + 'Fighter Sword': ('Progressive Sword', 1), + 'Master Sword': ('Progressive Sword', 2), + 'Tempered Sword': ('Progressive Sword', 3), + 'Golden Sword': ('Progressive Sword', 4), + 'Power Glove': ('Progressive Glove', 1), + 'Titans Mitts': ('Progressive Glove', 2), + 'Bow': ('Progressive Bow', 1), + 'Silver Arrows': ('Progressive Bow', 2), + 'Blue Mail': ('Progressive Armor', 1), + 'Red Mail': ('Progressive Armor', 2), + 'Blue Shield': ('Progressive Shield', 1), + 'Red Shield': ('Progressive Shield', 2), + 'Mirror Shield': ('Progressive Shield', 3), +} + + +def substitute_progressive(req): + for item in req.get_values(): + if item.item in progress_sub.keys(): + item.item, item.amount = progress_sub[item.item] diff --git a/source/overworld/EntranceShuffle2.py b/source/overworld/EntranceShuffle2.py index 9f7ee90a..c15115b6 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -3,6 +3,7 @@ import logging import copy from collections import defaultdict +from BaseClasses import RegionType class EntrancePool(object): @@ -11,6 +12,7 @@ class EntrancePool(object): self.exits = set() self.inverted = False self.coupled = True + self.swapped = False self.default_map = {} self.one_way_map = {} self.skull_handled = False @@ -76,12 +78,6 @@ def link_entrances_new(world, player): # setup mandatory connections for exit_name, region_name in mandatory_connections: connect_simple(world, exit_name, region_name, player) - if not avail_pool.inverted: - for exit_name, region_name in open_mandatory_connections: - connect_simple(world, exit_name, region_name, player) - else: - for exit_name, region_name in inverted_mandatory_connections: - connect_simple(world, exit_name, region_name, player) connect_custom(avail_pool, world, player) @@ -92,6 +88,7 @@ def link_entrances_new(world, player): if mode not in modes: raise RuntimeError(f'Shuffle mode {mode} is not yet supported') mode_cfg = copy.deepcopy(modes[mode]) + avail_pool.swapped = mode_cfg['undefined'] == 'swap' if avail_pool.is_standard(): do_standard_connections(avail_pool) pool_list = mode_cfg['pools'] if 'pools' in mode_cfg else {} @@ -99,7 +96,10 @@ def link_entrances_new(world, player): special_shuffle = pool['special'] if 'special' in pool else None if special_shuffle == 'drops': holes, targets = find_entrances_and_targets_drops(avail_pool, pool['entrances']) - connect_random(holes, targets, avail_pool) + if avail_pool.swapped: + connect_swapped(holes, targets, avail_pool) + else: + connect_random(holes, targets, avail_pool) elif special_shuffle == 'fixed_shuffle': do_fixed_shuffle(avail_pool, pool['entrances']) elif special_shuffle == 'same_world': @@ -123,7 +123,10 @@ def link_entrances_new(world, player): do_vanilla_connect(pool, avail_pool) elif special_shuffle == 'skull': entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances']) - connect_random(entrances, exits, avail_pool, True) + if avail_pool.swapped: + connect_swapped(entrances, exits, avail_pool, True) + else: + connect_random(entrances, exits, avail_pool, True) avail_pool.skull_handled = True else: entrances, exits = find_entrances_and_exits(avail_pool, pool['entrances']) @@ -131,7 +134,7 @@ def link_entrances_new(world, player): undefined_behavior = mode_cfg['undefined'] if undefined_behavior == 'vanilla': do_vanilla_connections(avail_pool) - elif undefined_behavior == 'shuffle': + elif undefined_behavior in {'shuffle', 'swap'}: do_main_shuffle(set(avail_pool.entrances), set(avail_pool.exits), avail_pool, mode_cfg) # afterward @@ -186,6 +189,10 @@ def do_main_shuffle(entrances, exits, avail, mode_def): if not avail.coupled: avail.decoupled_entrances.remove('Agahnims Tower') avail.decoupled_exits.remove('Ganons Tower Exit') + if avail.swapped: + connect_swap('Agahnims Tower', 'Ganons Tower Exit', avail) + entrances.remove('Ganons Tower') + exits.remove('Agahnims Tower Exit') elif 'Ganons Tower' in entrances: connect_two_way('Ganons Tower', 'Ganons Tower Exit', avail) entrances.remove('Ganons Tower') @@ -207,7 +214,13 @@ def do_main_shuffle(entrances, exits, avail, mode_def): # inverted sanc if avail.inverted and 'Dark Sanctuary Hint' in exits: - choices = [e for e in Inverted_Dark_Sanctuary_Doors if e in entrances] + forbidden = set() + if avail.swapped: + forbidden.add('Dark Sanctuary Hint') + forbidden.update(Forbidden_Swap_Entrances) + if not avail.inverted: + forbidden.append('Links House') + choices = [e for e in Inverted_Dark_Sanctuary_Doors if e in entrances and e not in forbidden] choice = random.choice(choices) entrances.remove(choice) exits.remove('Dark Sanctuary Hint') @@ -216,6 +229,10 @@ def do_main_shuffle(entrances, exits, avail, mode_def): ext.connect(avail.world.get_entrance(choice, avail.player).parent_region) if not avail.coupled: avail.decoupled_entrances.remove(choice) + if avail.swapped and choice != 'Dark Sanctuary Hint': + swap_ent, swap_ext = connect_swap(choice, 'Dark Sanctuary Hint', avail) + entrances.remove(swap_ent) + exits.remove(swap_ext) # mandatory exits rem_entrances, rem_exits = set(), set() @@ -241,12 +258,19 @@ def do_main_shuffle(entrances, exits, avail, mode_def): else: # cross world mandantory entrance_list = list(entrances) + if avail.swapped: + forbidden = [e for e in Forbidden_Swap_Entrances if e in entrance_list] + entrance_list = [e for e in entrance_list if e not in forbidden] must_exit, multi_exit_caves = figure_out_must_exits_cross_world(entrances, exits, avail) do_mandatory_connections(avail, entrance_list, multi_exit_caves, must_exit) rem_entrances.update(entrance_list) + if avail.swapped: + rem_entrances.update(forbidden) rem_exits.update([x for item in multi_exit_caves for x in item]) rem_exits.update(exits) + if avail.swapped: + rem_exits = [x for x in rem_exits if x in avail.exits] # old man cave do_old_man_cave_exit(rem_entrances, rem_exits, avail, cross_world) @@ -254,9 +278,15 @@ def do_main_shuffle(entrances, exits, avail, mode_def): # blacksmith if 'Blacksmiths Hut' in rem_exits: blacksmith_options = [x for x in Blacksmith_Options if x in rem_entrances] + if avail.swapped: + blacksmith_options = [e for e in blacksmith_options if e not in Forbidden_Swap_Entrances] blacksmith_choice = random.choice(blacksmith_options) connect_entrance(blacksmith_choice, 'Blacksmiths Hut', avail) rem_entrances.remove(blacksmith_choice) + if avail.swapped and blacksmith_choice != 'Blacksmiths Hut': + swap_ent, swap_ext = connect_swap(blacksmith_choice, 'Blacksmiths Hut', avail) + rem_entrances.remove(swap_ent) + rem_exits.remove(swap_ext) if not avail.coupled: avail.decoupled_exits.remove('Blacksmiths Hut') rem_exits.remove('Blacksmiths Hut') @@ -266,9 +296,15 @@ def do_main_shuffle(entrances, exits, avail, mode_def): if bomb_shop in rem_exits: bomb_shop_options = Inverted_Bomb_Shop_Options if avail.inverted else Bomb_Shop_Options bomb_shop_options = [x for x in bomb_shop_options if x in rem_entrances] + if avail.swapped and len(bomb_shop_options) > 1: + bomb_shop_options = [x for x in bomb_shop_options if x != 'Big Bomb Shop'] bomb_shop_choice = random.choice(bomb_shop_options) connect_entrance(bomb_shop_choice, bomb_shop, avail) rem_entrances.remove(bomb_shop_choice) + if avail.swapped and bomb_shop_choice != 'Big Bomb Shop': + swap_ent, swap_ext = connect_swap(bomb_shop_choice, bomb_shop, avail) + rem_exits.remove(swap_ext) + rem_entrances.remove(swap_ent) if not avail.coupled: avail.decoupled_exits.remove(bomb_shop) rem_exits.remove(bomb_shop) @@ -326,12 +362,17 @@ def do_main_shuffle(entrances, exits, avail, mode_def): rem_entrances = list(unused_entrances) rem_entrances.sort() rem_exits = list(rem_exits if avail.coupled else avail.decoupled_exits) + if avail.swapped: + rem_exits = [x for x in rem_exits if x in avail.exits] rem_exits.sort() random.shuffle(rem_entrances) random.shuffle(rem_exits) placing = min(len(rem_entrances), len(rem_exits)) - for door, target in zip(rem_entrances, rem_exits): - connect_entrance(door, target, avail) + if avail.swapped: + connect_swapped(rem_entrances, rem_exits, avail) + else: + for door, target in zip(rem_entrances, rem_exits): + connect_entrance(door, target, avail) rem_entrances[:] = rem_entrances[placing:] rem_exits[:] = rem_exits[placing:] if rem_entrances or rem_exits: @@ -344,6 +385,8 @@ def do_old_man_cave_exit(entrances, exits, avail, cross_world): if avail.inverted and cross_world: om_cave_options = Inverted_Old_Man_Entrances + Old_Man_Entrances om_cave_options = [x for x in om_cave_options if x in entrances] + if avail.swapped: + om_cave_options = [e for e in om_cave_options if e not in Forbidden_Swap_Entrances] om_cave_choice = random.choice(om_cave_options) if not avail.coupled: connect_exit('Old Man Cave Exit (East)', om_cave_choice, avail) @@ -351,6 +394,10 @@ def do_old_man_cave_exit(entrances, exits, avail, cross_world): else: connect_two_way(om_cave_choice, 'Old Man Cave Exit (East)', avail) entrances.remove(om_cave_choice) + if avail.swapped and om_cave_choice != 'Old Man Cave (East)': + swap_ent, swap_ext = connect_swap(om_cave_choice, 'Old Man Cave Exit (East)', avail) + entrances.remove(swap_ent) + exits.remove(swap_ext) exits.remove('Old Man Cave Exit (East)') @@ -405,32 +452,74 @@ def do_holes_and_linked_drops(entrances, exits, avail, cross_world, keep_togethe random.shuffle(hole_entrances) if not cross_world and 'Sanctuary Grave' in holes_to_shuffle: - lw_entrance = next(entrance for entrance in hole_entrances if entrance[0] in LW_Entrances) - hole_entrances.remove(lw_entrance) - sanc_interior = next(target for target in hole_targets if target[0] == 'Sanctuary Exit') - hole_targets.remove(sanc_interior) - connect_two_way(lw_entrance[0], sanc_interior[0], avail) # two-way exit - connect_entrance(lw_entrance[1], sanc_interior[1], avail) # hole - remove_from_list(entrances, [lw_entrance[0], lw_entrance[1]]) - remove_from_list(exits, [sanc_interior[0], sanc_interior[1]]) + hc = avail.world.get_entrance('Hyrule Castle Exit (South)', avail.player) + is_hc_in_dw = avail.world.mode[avail.player] == 'inverted' + if hc.connected_region: + is_hc_in_dw = hc.connected_region.type == RegionType.DarkWorld + chosen_entrance = None + if is_hc_in_dw: + if avail.swapped: + chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances and e[0] != 'Sanctuary') + if not chosen_entrance: + chosen_entrance = next(e for e in hole_entrances if e[0] in DW_Entrances) + if not chosen_entrance: + if avail.swapped: + chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances and e[0] != 'Sanctuary') + if not chosen_entrance: + chosen_entrance = next(e for e in hole_entrances if e[0] in LW_Entrances) + + if chosen_entrance: + hole_entrances.remove(chosen_entrance) + sanc_interior = next(target for target in hole_targets if target[0] == 'Sanctuary Exit') + hole_targets.remove(sanc_interior) + connect_two_way(chosen_entrance[0], sanc_interior[0], avail) # two-way exit + connect_entrance(chosen_entrance[1], sanc_interior[1], avail) # hole + remove_from_list(entrances, [chosen_entrance[0], chosen_entrance[1]]) + remove_from_list(exits, [sanc_interior[0], sanc_interior[1]]) + if avail.swapped and drop_map[chosen_entrance[1]] != sanc_interior[1]: + swap_ent, swap_ext = connect_swap(chosen_entrance[0], sanc_interior[0], avail) + swap_drop, swap_tgt = connect_swap(chosen_entrance[1], sanc_interior[1], avail) + hole_entrances.remove((swap_ent, swap_drop)) + hole_targets.remove((swap_ext, swap_tgt)) + remove_from_list(entrances, [swap_ent, swap_drop]) + remove_from_list(exits, [swap_ext, swap_tgt]) random.shuffle(hole_targets) - for entrance, drop in hole_entrances: - ext, target = hole_targets.pop() + while len(hole_entrances): + entrance, drop = hole_entrances.pop() + if avail.swapped and len(hole_targets) > 1: + ext, target = next((x, t) for x, t in hole_targets if x != entrance_map[entrance]) + hole_targets.remove((ext, target)) + else: + ext, target = hole_targets.pop() connect_two_way(entrance, ext, avail) connect_entrance(drop, target, avail) remove_from_list(entrances, [entrance, drop]) remove_from_list(exits, [ext, target]) + if avail.swapped and drop_map[drop] != target: + swap_ent, swap_ext = connect_swap(entrance, ext, avail) + swap_drop, swap_tgt = connect_swap(drop, target, avail) + hole_entrances.remove((swap_ent, swap_drop)) + hole_targets.remove((swap_ext, swap_tgt)) + remove_from_list(entrances, [swap_ent, swap_drop]) + remove_from_list(exits, [swap_ext, swap_tgt]) def do_links_house(entrances, exits, avail, cross_world): lh_exit = 'Links House Exit' if lh_exit in exits: + links_house_vanilla = 'Big Bomb Shop' if avail.inverted else 'Links House' if not avail.world.shufflelinks[avail.player]: - links_house = 'Big Bomb Shop' if avail.inverted else 'Links House' + links_house = links_house_vanilla else: forbidden = list((Isolated_LH_Doors_Inv + Inverted_Dark_Sanctuary_Doors) if avail.inverted else Isolated_LH_Doors_Open) + if not avail.inverted: + if avail.world.doorShuffle[avail.player] != 'vanilla' and avail.world.intensity[avail.player] > 2: + forbidden.append('Hyrule Castle Entrance (South)') + if avail.swapped: + forbidden.append(links_house_vanilla) + forbidden.extend(Forbidden_Swap_Entrances) shuffle_mode = avail.world.shuffle[avail.player] # simple shuffle - if shuffle_mode == 'simple': @@ -471,6 +560,11 @@ def do_links_house(entrances, exits, avail, cross_world): avail.decoupled_entrances.remove(links_house) avail.decoupled_exits.remove('Links House Exit') avail.decoupled_exits.remove('Chris Houlihan Room Exit') + if avail.swapped and links_house != links_house_vanilla: + swap_ent, swap_ext = connect_swap(links_house, lh_exit, avail) + entrances.remove(swap_ent) + exits.remove(swap_ext) + # links on dm dm_spots = LH_DM_Connector_List.union(LH_DM_Exit_Forbidden) if links_house in dm_spots: @@ -491,20 +585,20 @@ def do_links_house(entrances, exits, avail, cross_world): possible_exits.sort() chosen_dm_escape = random.choice(possible_dm_exits) chosen_landing = random.choice(possible_exits) + chosen_exit_start = chosen_cave.pop(0) + chosen_exit_end = chosen_cave.pop() if avail.coupled: - connect_two_way(chosen_dm_escape, chosen_cave.pop(0), avail) - connect_two_way(chosen_landing, chosen_cave.pop(), avail) + connect_two_way(chosen_dm_escape, chosen_exit_start, avail) + connect_two_way(chosen_landing, chosen_exit_end, avail) entrances.remove(chosen_dm_escape) entrances.remove(chosen_landing) else: - chosen_cave_first = chosen_cave.pop(0) - connect_entrance(chosen_dm_escape, chosen_cave_first, avail) - connect_exit(chosen_cave.pop(), chosen_landing, avail) + connect_entrance(chosen_dm_escape, chosen_exit_start, avail) + connect_exit(chosen_exit_end, chosen_landing, avail) entrances.remove(chosen_dm_escape) - avail.decoupled_exits.remove(chosen_cave_first) + avail.decoupled_exits.remove(chosen_exit_start) avail.decoupled_entrances.remove(chosen_landing) - # chosen cave has already been removed from exits - exits.add(chosen_cave_first) # this needs to be added back in + exits.add(chosen_exit_start) # this needs to be added back in if len(chosen_cave): exits.update([x for x in chosen_cave]) exits.update([x for item in multi_exit_caves for x in item]) @@ -549,19 +643,29 @@ def figure_out_must_exits_same_world(entrances, exits, avail): if hyrule_forced: remove_from_list(multi_exit_caves, hyrule_forced) - must_exit_lw, must_exit_dw = must_exits_helper(avail, lw_entrances, dw_entrances) + must_exit_lw, must_exit_dw, unfiltered_lw, unfiltered_dw = must_exits_helper(avail, lw_entrances, dw_entrances) return must_exit_lw, must_exit_dw, lw_entrances, dw_entrances, multi_exit_caves, hyrule_forced def must_exits_helper(avail, lw_entrances, dw_entrances): - must_exit_lw = (Inverted_LW_Must_Exit if avail.inverted else LW_Must_Exit).copy() - must_exit_dw = (Inverted_DW_Must_Exit if avail.inverted else DW_Must_Exit).copy() + must_exit_lw_orig = (Inverted_LW_Must_Exit if avail.inverted else LW_Must_Exit).copy() + must_exit_dw_orig = (Inverted_DW_Must_Exit if avail.inverted else DW_Must_Exit).copy() if not avail.inverted and not avail.skull_handled: - must_exit_dw.append(('Skull Woods Second Section Door (West)', 'Skull Woods Final Section')) - must_exit_lw = must_exit_filter(avail, must_exit_lw, lw_entrances) - must_exit_dw = must_exit_filter(avail, must_exit_dw, dw_entrances) - return must_exit_lw, must_exit_dw + must_exit_dw_orig.append(('Skull Woods Second Section Door (West)', 'Skull Woods Final Section')) + must_exit_lw = must_exit_filter(avail, must_exit_lw_orig, lw_entrances) + must_exit_dw = must_exit_filter(avail, must_exit_dw_orig, dw_entrances) + return must_exit_lw, must_exit_dw, flatten(must_exit_lw_orig), flatten(must_exit_dw_orig) + + +def flatten(list_to_flatten): + ret = [] + for item in list_to_flatten: + if isinstance(item, tuple): + ret.extend(item) + else: + ret.append(item) + return ret def figure_out_must_exits_cross_world(entrances, exits, avail): @@ -616,21 +720,44 @@ def do_cross_world_connectors(entrances, caves, avail): cave_candidate = (None, 0) for i, cave in enumerate(caves): if isinstance(cave, str): - cave = (cave,) + cave = [cave] if len(cave) > cave_candidate[1]: cave_candidate = (i, len(cave)) cave = caves.pop(cave_candidate[0]) if isinstance(cave, str): - cave = (cave,) + cave = [cave] - for ext in cave: + while len(cave): + ext = cave.pop() if not avail.coupled: choice = random.choice(avail.decoupled_entrances) connect_exit(ext, choice, avail) avail.decoupled_entrances.remove(choice) else: - connect_two_way(entrances.pop(), ext, avail) + if avail.swapped and len(entrances) > 1: + chosen_entrance = next(e for e in entrances if combine_map[e] != ext) + entrances.remove(chosen_entrance) + else: + chosen_entrance = entrances.pop() + connect_two_way(chosen_entrance, ext, avail) + if avail.swapped: + swap_ent, swap_ext = connect_swap(chosen_entrance, ext, avail) + if swap_ent: + entrances.remove(swap_ent) + if chosen_entrance not in single_entrance_map: + if swap_ext in cave: + cave.remove(swap_ext) + else: + for c in caves: + if swap_ext == c: + caves.remove(swap_ext) + break + if not isinstance(c, str) and swap_ext in c: + c.remove(swap_ext) + if len(c) == 0: + caves.remove(c) + break def do_fixed_shuffle(avail, entrance_list): @@ -766,18 +893,25 @@ def do_limited_shuffle(pool_def, avail): def do_limited_shuffle_exclude_drops(pool_def, avail, lw=True): ignored_entrances, exits = find_entrances_and_exits(avail, pool_def['entrances']) reserved_drops = set(linked_drop_map.values()) - must_exit_lw, must_exit_dw = must_exits_helper(avail, LW_Entrances, DW_Entrances) + must_exit_lw, must_exit_dw, unfiltered_lw, unfiltered_dw = must_exits_helper(avail, LW_Entrances, DW_Entrances) must_exit = set(must_exit_lw if lw else must_exit_dw) + unfiltered = set(unfiltered_lw if lw else unfiltered_dw) base_set = LW_Entrances if lw else DW_Entrances entrance_pool = [x for x in base_set if x in avail.entrances and x not in reserved_drops] random.shuffle(entrance_pool) + all_connectors = {c: tuple(connector) for connector in Connector_List for c in connector} + multi_tracker = {tuple(connector): False for connector in Connector_List} # ensures multi_entrance for next_exit in exits: if next_exit not in Connector_Exit_Set: reduced_pool = [x for x in entrance_pool if x not in must_exit] + if next_exit in all_connectors and not multi_tracker[all_connectors[next_exit]]: + reduced_pool = [x for x in entrance_pool if x not in unfiltered] chosen_entrance = reduced_pool.pop() entrance_pool.remove(chosen_entrance) else: chosen_entrance = entrance_pool.pop() + if next_exit in all_connectors and chosen_entrance not in must_exit: + multi_tracker[all_connectors[next_exit]] = True connect_two_way(chosen_entrance, next_exit, avail) @@ -818,13 +952,18 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit): invalid_connections = Must_Exit_Invalid_Connections.copy() invalid_cave_connections = defaultdict(set) - if avail.world.logic[avail.player] in ['owglitches', 'nologic']: + if avail.world.logic[avail.player] in ['owglitches', 'hybridglitches', 'nologic']: import OverworldGlitchRules - for entrance in OverworldGlitchRules.get_non_mandatory_exits(avail.inverted): + for entrance in OverworldGlitchRules.inverted_non_mandatory_exits if avail.inverted else OverworldGlitchRules.open_non_mandatory_exits: invalid_connections[entrance] = set() if entrance in must_exit: must_exit.remove(entrance) - entrances.append(entrance) + if entrance not in entrances: + entrances.append(entrance) + if avail.swapped: + swap_forbidden = [e for e in entrances if combine_map[e] in must_exit] + for e in swap_forbidden: + entrances.remove(e) entrances.sort() # sort these for consistency random.shuffle(entrances) random.shuffle(cave_options) @@ -837,6 +976,19 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit): invalid_connections[ext] = invalid_connections[ext].union({'Agahnims Tower', 'Hyrule Castle Entrance (West)', 'Hyrule Castle Entrance (East)'}) break + def connect_cave_swap(entrance, exit, current_cave): + swap_entrance, swap_exit = connect_swap(entrance, exit, avail) + if swap_entrance and entrance not in single_entrance_map: + for option in cave_options: + if swap_exit in option and option == current_cave: + x=0 + if swap_exit in option and option != current_cave: + option.remove(swap_exit) + if len(option) == 0: + cave_options.remove(option) + break + return swap_entrance, swap_exit + used_caves = [] required_entrances = 0 # Number of entrances reserved for used_caves while must_exit: @@ -844,23 +996,43 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit): # find multi exit cave candidates = [] for candidate in cave_options: - if not isinstance(candidate, str) and (candidate in used_caves - or len(candidate) < len(entrances) - required_entrances): - candidates.append(candidate) + if not isinstance(candidate, str) and len(candidate) > 1 and (candidate in used_caves + or len(candidate) < len(entrances) - required_entrances): + if not avail.swapped or (combine_map[exit] not in candidate and not any(e for e in must_exit if combine_map[e] in candidate)): #maybe someday allow these, but we need to disallow mutual locks in Swapped + candidates.append(candidate) cave = random.choice(candidates) + + if avail.swapped and len(candidates) > 1 and not avail.inverted: + DM_Connector_Prefixes = ['Spectacle Rock Cave', 'Old Man House', 'Death Mountain Return'] + if any(p for p in DM_Connector_Prefixes if p in cave[0]): # if chosen cave is a DM connector + remain = [p for p in DM_Connector_Prefixes if len([e for e in entrances if p in e]) > 0] # gets remaining DM caves left in pool + if len(remain) == 1: # guarantee that old man rescue cave can still be placed + candidates.remove(cave) + cave = random.choice(candidates) + if cave is None: raise RuntimeError('No more caves left. Should not happen!') # all caves are sorted so that the last exit is always reachable rnd_cave = list(cave) shuffle_connector_exits(rnd_cave) # should be the same as unbiasing some entrances... - entrances.remove(exit) + if avail.swapped and exit in swap_forbidden: + swap_forbidden.remove(exit) + else: + entrances.remove(exit) connect_two_way(exit, rnd_cave[-1], avail) + if avail.swapped: + swap_ent, _ = connect_cave_swap(exit, rnd_cave[-1], cave) + entrances.remove(swap_ent) if len(cave) == 2: entrance = next(e for e in entrances[::-1] if e not in invalid_connections[exit] - and e not in invalid_cave_connections[tuple(cave)] and e not in must_exit) + and e not in invalid_cave_connections[tuple(cave)] and e not in must_exit + and (not avail.swapped or rnd_cave[0] != combine_map[e])) entrances.remove(entrance) connect_two_way(entrance, rnd_cave[0], avail) + if avail.swapped and combine_map[entrance] != rnd_cave[0]: + swap_ent, _ = connect_cave_swap(entrance, rnd_cave[0], cave) + entrances.remove(swap_ent) if cave in used_caves: required_entrances -= 2 used_caves.remove(cave) @@ -870,10 +1042,18 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit): elif cave[-1] == 'Spectacle Rock Cave Exit': # Spectacle rock only has one exit cave_entrances = [] for cave_exit in rnd_cave[:-1]: - entrance = next(e for e in entrances[::-1] if e not in invalid_connections[exit] and e not in must_exit) - cave_entrances.append(entrance) - entrances.remove(entrance) - connect_two_way(entrance, cave_exit, avail) + if avail.swapped and cave_exit not in avail.exits: + entrance = avail.world.get_entrance(cave_exit, avail.player).parent_region.entrances[0].name + cave_entrances.append(entrance) + else: + entrance = next(e for e in entrances[::-1] if e not in invalid_connections[exit] and e not in must_exit + and (not avail.swapped or cave_exit != combine_map[e])) + cave_entrances.append(entrance) + entrances.remove(entrance) + connect_two_way(entrance, cave_exit, avail) + if avail.swapped and combine_map[entrance] != cave_exit: + swap_ent, _ = connect_cave_swap(entrance, cave_exit, cave) + entrances.remove(swap_ent) if entrance not in invalid_connections: invalid_connections[exit] = set() if all(entrance in invalid_connections for entrance in cave_entrances): @@ -894,11 +1074,20 @@ def do_mandatory_connections(avail, entrances, cave_options, must_exit): for cave in used_caves: if cave in cave_options: # check if we placed multiple entrances from this 3 or 4 exit for cave_exit in cave: - entrance = next(e for e in entrances[::-1] if e not in invalid_cave_connections[tuple(cave)]) - invalid_cave_connections[tuple(cave)] = set() - entrances.remove(entrance) - connect_two_way(entrance, cave_exit, avail) + if avail.swapped and cave_exit not in avail.exits: + continue + else: + entrance = next(e for e in entrances[::-1] if e not in invalid_cave_connections[tuple(cave)] + and (not avail.swapped or cave_exit != combine_map[e])) + invalid_cave_connections[tuple(cave)] = set() + entrances.remove(entrance) + connect_two_way(entrance, cave_exit, avail) + if avail.swapped and combine_map[entrance] != cave_exit: + swap_ent, _ = connect_cave_swap(entrance, cave_exit, cave) + entrances.remove(swap_ent) cave_options.remove(cave) + if avail.swapped: + entrances.extend(swap_forbidden) def do_mandatory_connections_decoupled(avail, cave_options, must_exit): @@ -1016,6 +1205,47 @@ def inverted_substitution(avail_pool, collection, is_entrance, is_set=False): pass +def connect_swapped(entrancelist, targetlist, avail, two_way=False): + random.shuffle(entrancelist) + sorted_targets = list() + for ent in entrancelist: + if ent in combine_map: + if combine_map[ent] not in targetlist: + logging.getLogger('').error(f'{combine_map[ent]} not in target list, cannot swap entrance') + raise Exception(f'{combine_map[ent]} not in target list, cannot swap entrance') + sorted_targets.append(combine_map[ent]) + if len(sorted_targets): + targetlist = list(sorted_targets) + else: + targetlist = list(targetlist) + indexlist = list(range(len(targetlist))) + random.shuffle(indexlist) + + while len(indexlist) > 1: + index1 = indexlist.pop() + index2 = indexlist.pop() + targetlist[index1], targetlist[index2] = targetlist[index2], targetlist[index1] + + for exit, target in zip(entrancelist, targetlist): + if two_way: + connect_two_way(exit, target, avail) + else: + connect_entrance(exit, target, avail) + + +def connect_swap(entrance, exit, avail): + swap_exit = combine_map[entrance] + if swap_exit != exit: + swap_entrance = next(e for e, x in combine_map.items() if x == exit) + if swap_entrance in ['Pyramid Entrance', 'Pyramid Hole'] and avail.inverted: + swap_entrance = 'Inverted ' + swap_entrance + if entrance in entrance_map: + connect_two_way(swap_entrance, swap_exit, avail) + else: + connect_entrance(swap_entrance, swap_exit, avail) + return swap_entrance, swap_exit + return None, None + def connect_random(exitlist, targetlist, avail, two_way=False): targetlist = list(targetlist) random.shuffle(targetlist) @@ -1209,7 +1439,7 @@ modes = { 'fixed_non_items': { 'special': 'vanilla', 'condition': '', - 'entrances': ['Dark Death Mountain Fairy', 'Dark Desert Fairy', 'Archery Game', + 'entrances': ['Dark Death Mountain Fairy', 'Mire Fairy', 'Archery Game', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Bonk Fairy (Dark)', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Fairy', 'Dark Lake Hylia Shop', 'East Dark World Hint', 'Kakariko Gamble Game', 'Good Bee Cave', @@ -1230,7 +1460,7 @@ modes = { 'entrances': ['Lumberjack House', 'Snitch Lady (West)', 'Snitch Lady (East)', 'Tavern (Front)', 'Light World Bomb Hut', '20 Rupee Cave', '50 Rupee Cave', 'Hookshot Fairy', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', - 'Dark Desert Hint'] + 'Mire Hint'] }, 'item_caves': { # shuffles shops/pottery if they weren't fixed in the last steps @@ -1245,7 +1475,7 @@ modes = { 'Lumberjack House', 'Snitch Lady (West)', 'Snitch Lady (East)', 'Tavern (Front)', 'Light World Bomb Hut', '20 Rupee Cave', '50 Rupee Cave', 'Hookshot Fairy', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', - 'Dark Desert Hint', + 'Mire Hint', 'Links House', 'Tavern North'] }, 'old_man_cave': { # have to do old man cave first so lw dungeon don't use up everything @@ -1285,7 +1515,7 @@ modes = { 'fixed_non_items': { 'special': 'vanilla', 'condition': '', - 'entrances': ['Dark Death Mountain Fairy', 'Dark Desert Fairy', 'Archery Game', + 'entrances': ['Dark Death Mountain Fairy', 'Mire Fairy', 'Archery Game', 'Fortune Teller (Dark)', 'Dark Sanctuary Hint', 'Bonk Fairy (Dark)', 'Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Fairy', 'Dark Lake Hylia Shop', 'East Dark World Hint', 'Kakariko Gamble Game', 'Good Bee Cave', @@ -1306,7 +1536,7 @@ modes = { 'entrances': ['Lumberjack House', 'Snitch Lady (West)', 'Snitch Lady (East)', 'Tavern (Front)', 'Light World Bomb Hut', '20 Rupee Cave', '50 Rupee Cave', 'Hookshot Fairy', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', - 'Dark Desert Hint'] + 'Mire Hint'] }, 'item_caves': { # shuffles shops/pottery if they weren't fixed in the last steps @@ -1321,7 +1551,7 @@ modes = { 'Lumberjack House', 'Snitch Lady (West)', 'Snitch Lady (East)', 'Tavern (Front)', 'Light World Bomb Hut', '20 Rupee Cave', '50 Rupee Cave', 'Hookshot Fairy', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', - 'Dark Desert Hint', + 'Mire Hint', 'Links House', 'Tavern North'] # inverted links house gets substituted } } @@ -1462,6 +1692,23 @@ modes = { }, } }, + 'swapped': { + 'undefined': 'swap', + 'keep_drops_together': 'on', + 'cross_world': 'on', + 'pools': { + 'skull_drops': { + 'special': 'drops', + 'entrances': ['Skull Woods First Section Hole (East)', 'Skull Woods First Section Hole (West)', + 'Skull Woods First Section Hole (North)', 'Skull Woods Second Section Hole'] + }, + 'skull_doors': { + 'special': 'skull', + 'entrances': ['Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', + 'Skull Woods Second Section Door (West)'] + }, + } + }, 'crossed': { 'undefined': 'shuffle', 'keep_drops_together': 'on', @@ -1593,7 +1840,7 @@ entrance_map = { single_entrance_map = { 'Mimic Cave': 'Mimic Cave', 'Dark Death Mountain Fairy': 'Dark Death Mountain Healer Fairy', 'Dark Death Mountain Shop': 'Dark Death Mountain Shop', 'Spike Cave': 'Spike Cave', - 'Dark Desert Fairy': 'Dark Desert Healer Fairy', 'Dark Desert Hint': 'Dark Desert Hint', 'Mire Shed': 'Mire Shed', + 'Mire Fairy': 'Mire Healer Fairy', 'Mire Hint': 'Mire Hint', 'Mire Shed': 'Mire Shed', 'Archery Game': 'Archery Game', 'Dark Potion Shop': 'Dark Potion Shop', 'Dark Lumberjack Shop': 'Dark Lumberjack Shop', 'Dark World Shop': 'Village of Outcasts Shop', 'Fortune Teller (Dark)': 'Fortune Teller (Dark)', 'Dark Sanctuary Hint': 'Dark Sanctuary Hint', @@ -1624,6 +1871,8 @@ single_entrance_map = { 'Blinds Hideout': 'Blinds Hideout', 'Waterfall of Wishing': 'Waterfall of Wishing' } +combine_map = {**entrance_map, **single_entrance_map, **drop_map} + default_dw = { 'Thieves Town Exit', 'Skull Woods First Section Exit', 'Skull Woods Second Section Exit (East)', 'Skull Woods Second Section Exit (West)', 'Skull Woods Final Section Exit', 'Ice Palace Exit', 'Misery Mire Exit', @@ -1631,12 +1880,12 @@ default_dw = { 'Turtle Rock Ledge Exit (East)', 'Turtle Rock Isolated Ledge Exit', 'Bumper Cave Exit (Top)', 'Bumper Cave Exit (Bottom)', 'Superbunny Cave Exit (Top)', 'Superbunny Cave Exit (Bottom)', 'Hookshot Cave Front Exit', 'Hookshot Cave Back Exit', 'Ganons Tower Exit', 'Pyramid Exit', 'Bonk Fairy (Dark)', - 'Dark Lake Hylia Healer Fairy', 'Dark Lake Hylia Ledge Healer Fairy', 'Dark Desert Healer Fairy', + 'Dark Lake Hylia Healer Fairy', 'Dark Lake Hylia Ledge Healer Fairy', 'Mire Healer Fairy', 'Dark Death Mountain Healer Fairy', 'Dark Death Mountain Shop', 'Pyramid Fairy', 'East Dark World Hint', 'Palace of Darkness Hint', 'Village of Outcasts Shop', 'Dark Lake Hylia Shop', 'Dark Lumberjack Shop', 'Dark Potion Shop', 'Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Hint', 'Hype Cave', 'Brewery', 'C-Shaped House', 'Chest Game', 'Hammer Peg Cave', - 'Red Shield Shop', 'Dark Sanctuary Hint', 'Fortune Teller (Dark)', 'Archery Game', 'Mire Shed', 'Dark Desert Hint', + 'Red Shield Shop', 'Dark Sanctuary Hint', 'Fortune Teller (Dark)', 'Archery Game', 'Mire Shed', 'Mire Hint', 'Spike Cave', 'Skull Back Drop', 'Skull Left Drop', 'Skull Pinball', 'Skull Pot Circle', 'Pyramid' } @@ -1689,10 +1938,10 @@ DW_Entrances = ['Bumper Cave (Bottom)', 'Superbunny Cave (Top)', 'Superbunny Ca 'Turtle Rock Isolated Ledge Entrance', 'Bumper Cave (Top)', 'Hookshot Cave Back Entrance', 'Bonk Fairy (Dark)', 'Dark Sanctuary Hint', 'Dark Lake Hylia Fairy', 'C-Shaped House', 'Big Bomb Shop', 'Dark Death Mountain Fairy', 'Dark Lake Hylia Shop', 'Dark World Shop', 'Red Shield Shop', 'Mire Shed', - 'East Dark World Hint', 'Dark Desert Hint', 'Spike Cave', 'Palace of Darkness Hint', + 'East Dark World Hint', 'Mire Hint', 'Spike Cave', 'Palace of Darkness Hint', 'Dark Lake Hylia Ledge Spike Cave', 'Dark Death Mountain Shop', 'Dark Potion Shop', 'Pyramid Fairy', 'Archery Game', 'Dark Lumberjack Shop', 'Hype Cave', 'Brewery', - 'Dark Lake Hylia Ledge Hint', 'Chest Game', 'Dark Desert Fairy', 'Dark Lake Hylia Ledge Fairy', + 'Dark Lake Hylia Ledge Hint', 'Chest Game', 'Mire Fairy', 'Dark Lake Hylia Ledge Fairy', 'Fortune Teller (Dark)', 'Hammer Peg Cave', 'Pyramid Entrance', 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', 'Skull Woods Second Section Door (West)', 'Ganons Tower'] @@ -1826,8 +2075,8 @@ Bomb_Shop_Options = [ 'Dark Lake Hylia Fairy', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Hint', 'Hype Cave', 'Bonk Fairy (Dark)', 'Brewery', 'C-Shaped House', 'Chest Game', 'Hammer Peg Cave', 'Red Shield Shop', 'Dark Sanctuary Hint', 'Fortune Teller (Dark)', 'Dark World Shop', - 'Dark Lumberjack Shop', 'Dark Potion Shop', 'Archery Game', 'Mire Shed', 'Dark Desert Hint', - 'Dark Desert Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Dark Death Mountain Fairy', 'Mimic Cave', + 'Dark Lumberjack Shop', 'Dark Potion Shop', 'Archery Game', 'Mire Shed', 'Mire Hint', + 'Mire Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Dark Death Mountain Fairy', 'Mimic Cave', 'Big Bomb Shop', 'Dark Lake Hylia Shop', 'Bumper Cave (Top)', 'Links House', 'Hyrule Castle Entrance (South)', 'Misery Mire', 'Thieves Town', 'Bumper Cave (Bottom)', 'Swamp Palace', 'Hyrule Castle Secret Entrance Stairs', 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', @@ -1848,8 +2097,8 @@ Inverted_Bomb_Shop_Options = [ 'Dark Lake Hylia Fairy', 'Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Hint', 'Hype Cave', 'Bonk Fairy (Dark)', 'Brewery', 'C-Shaped House', 'Chest Game', 'Hammer Peg Cave', 'Red Shield Shop', 'Fortune Teller (Dark)', 'Dark World Shop', - 'Dark Lumberjack Shop', 'Dark Potion Shop', 'Archery Game', 'Mire Shed', 'Dark Desert Hint', - 'Dark Desert Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Dark Death Mountain Fairy', 'Mimic Cave', + 'Dark Lumberjack Shop', 'Dark Potion Shop', 'Archery Game', 'Mire Shed', 'Mire Hint', + 'Mire Fairy', 'Spike Cave', 'Dark Death Mountain Shop', 'Dark Death Mountain Fairy', 'Mimic Cave', 'Dark Lake Hylia Shop', 'Bumper Cave (Top)', 'Hyrule Castle Entrance (South)', 'Misery Mire', 'Thieves Town', 'Bumper Cave (Bottom)', 'Swamp Palace', 'Hyrule Castle Secret Entrance Stairs', 'Skull Woods First Section Door', 'Skull Woods Second Section Door (East)', @@ -1865,18 +2114,22 @@ Inverted_Bomb_Shop_Options = [ 'Agahnims Tower', 'Ganons Tower', 'Dark Sanctuary Hint', 'Big Bomb Shop', 'Links House'] + Blacksmith_Options +Forbidden_Swap_Entrances = {'Old Man Cave (East)', 'Blacksmiths Hut', 'Big Bomb Shop'} + # these are connections that cannot be shuffled and always exist. # They link together separate parts of the world we need to divide into regions -mandatory_connections = [('Links House S&Q', 'Links House'), - - # underworld +mandatory_connections = [# underworld ('Lost Woods Hideout (top to bottom)', 'Lost Woods Hideout (bottom)'), ('Lumberjack Tree (top to bottom)', 'Lumberjack Tree (bottom)'), ('Death Mountain Return Cave E', 'Death Mountain Return Cave (right)'), ('Death Mountain Return Cave W', 'Death Mountain Return Cave (left)'), - ('Old Man Cave Dropdown', 'Old Man Cave'), - ('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'), - ('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (Bottom)'), + ('Old Man Cave Dropdown', 'Old Man Cave (East)'), + ('Old Man Cave W', 'Old Man Cave (West)'), + ('Old Man Cave E', 'Old Man Cave (East)'), + ('Spectacle Rock Cave Drop', 'Spectacle Rock Cave Pool'), + ('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave Pool'), + ('Spectacle Rock Cave West Edge', 'Spectacle Rock Cave (Bottom)'), + ('Spectacle Rock Cave East Edge', 'Spectacle Rock Cave Pool'), ('Old Man House Front to Back', 'Old Man House Back'), ('Old Man House Back to Front', 'Old Man House'), ('Spiral Cave (top to bottom)', 'Spiral Cave (Bottom)'), @@ -1894,213 +2147,27 @@ mandatory_connections = [('Links House S&Q', 'Links House'), ('Kakariko Well (top to back)', 'Kakariko Well (back)'), ('Blinds Hideout N', 'Blinds Hideout (Top)'), ('Bat Cave Door', 'Bat Cave (left)'), + ('Good Bee Cave Front to Back', 'Good Bee Cave (back)'), + ('Good Bee Cave Back to Front', 'Good Bee Cave'), + ('Capacity Upgrade East', 'Capacity Fairy Pool'), + ('Capacity Fairy Pool West', 'Capacity Upgrade'), + ('Bonk Fairy (Dark) Pool', 'Bonk Fairy Pool'), + ('Bonk Fairy (Light) Pool', 'Bonk Fairy Pool'), ('Hookshot Cave Front to Middle', 'Hookshot Cave (Middle)'), ('Hookshot Cave Middle to Front', 'Hookshot Cave (Front)'), ('Hookshot Cave Middle to Back', 'Hookshot Cave (Back)'), ('Hookshot Cave Back to Middle', 'Hookshot Cave (Middle)'), + ('Hookshot Cave Back to Fairy', 'Hookshot Cave (Fairy Pool)'), + ('Hookshot Cave Fairy to Back', 'Hookshot Cave (Back)'), ('Hookshot Cave Bonk Path', 'Hookshot Cave (Bonk Islands)'), ('Hookshot Cave Hook Path', 'Hookshot Cave (Hook Islands)'), ('Superbunny Cave Climb', 'Superbunny Cave (Top)'), ('Bumper Cave Bottom to Top', 'Bumper Cave (top)'), ('Bumper Cave Top To Bottom', 'Bumper Cave (bottom)'), - ('Ganon Drop', 'Bottom of Pyramid'), - - # water entry - ('Waterfall Fairy Access', 'Zora Waterfall Entryway'), - ('Zora Waterfall Water Drop', 'Lake Hylia Water'), - ('Light World Water Drop', 'Lake Hylia Water'), - ('Potion Shop Water Drop', 'Lake Hylia Water'), - ('Northeast Light World Water Drop', 'Lake Hylia Water'), - ('Lake Hylia Central Island Water Drop', 'Lake Hylia Water'), - - ('West Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Northeast Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Catfish Water Drop', 'Dark Lake Hylia Water'), - ('East Dark World Water Drop', 'Dark Lake Hylia Water'), - ('South Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Southeast Dark World Water Drop', 'Dark Lake Hylia Water'), - ('Ice Palace Leave Water Drop', 'Dark Lake Hylia Water'), - - # water exit - ('Light World Pier', 'Light World'), # there are several piers in-game, only one needs to be modeled - ('Potion Shop Pier', 'Potion Shop Area'), - ('Hobo Pier', 'Hobo Bridge'), - ('Lake Hylia Central Island Pier', 'Lake Hylia Central Island'), - ('Lake Hylia Whirlpool', 'Northeast Light World'), - - ('Northeast Dark World Pier', 'Northeast Dark World'), - ('East Dark World Pier', 'East Dark World'), - ('Southeast Dark World Pier', 'Southeast Dark World'), - - # terrain - ('Master Sword Meadow', 'Master Sword Meadow'), - ('DM Hammer Bridge (West)', 'East Death Mountain (Top)'), - ('DM Hammer Bridge (East)', 'West Death Mountain (Top)'), - ('DM Broken Bridge (West)', 'East Death Mountain (Bottom)'), - ('DM Broken Bridge (East)', 'West Death Mountain (Bottom)'), - ('Fairy Ascension Rocks', 'Fairy Ascension Plateau'), - ('Death Mountain Entrance Rock', 'Death Mountain Entrance'), - ('Zoras Domain', 'Zoras Domain'), - ('Kings Grave Rocks (Outer)', 'Kings Grave Area'), - ('Kings Grave Rocks (Inner)', 'Light World'), - ('Potion Shop Rock (South)', 'Northeast Light World'), - ('Potion Shop Rock (North)', 'Potion Shop Area'), - ('Kakariko Southwest Bush (North)', 'Bomb Hut Area'), - ('Kakariko Southwest Bush (South)', 'Light World'), - ('Kakariko Yard Bush (North)', 'Light World'), - ('Kakariko Yard Bush (South)', 'Bush Covered Lawn'), - ('Hyrule Castle Courtyard Bush (North)', 'Hyrule Castle Courtyard'), - ('Hyrule Castle Courtyard Bush (South)', 'Hyrule Castle Secret Entrance Area'), - ('Hyrule Castle Main Gate', 'Hyrule Castle Courtyard'), - ('Hyrule Castle Main Gate (North)', 'Light World'), - ('Wooden Bridge Bush (North)', 'Light World'), - ('Wooden Bridge Bush (South)', 'Potion Shop Area'), - ('Bat Cave Ledge Peg', 'Bat Cave Ledge'), - ('Bat Cave Ledge Peg (East)', 'Light World'), - ('Desert Statue Move', 'Desert Palace Stairs'), - ('Desert Ledge Rocks (Outer)', 'Desert Palace Entrance (North) Spot'), - ('Desert Ledge Rocks (Inner)', 'Desert Ledge'), - - ('Skull Woods Forest', 'Skull Woods Forest'), - ('East Dark Death Mountain Bushes', 'East Dark Death Mountain (Bushes)'), - ('Bumper Cave Entrance Rock', 'Bumper Cave Entrance'), - ('Dark Witch Rock (North)', 'Northeast Dark World'), - ('Dark Witch Rock (South)', 'Catfish Area'), - ('Grassy Lawn Pegs (Top)', 'West Dark World'), - ('Grassy Lawn Pegs (Bottom)', 'Dark Grassy Lawn'), - ('West Dark World Gap', 'West Dark World'), - ('Dark Graveyard Bush (South)', 'Dark Graveyard North'), - ('Dark Graveyard Bush (North)', 'West Dark World'), - ('Broken Bridge Pass (Top)', 'East Dark World'), - ('Broken Bridge Pass (Bottom)', 'Northeast Dark World'), - ('Peg Area Rocks (Left)', 'Hammer Peg Area'), - ('Peg Area Rocks (Right)', 'West Dark World'), - ('Village of Outcasts Heavy Rock', 'West Dark World'), - ('Hammer Bridge Pegs (North)', 'South Dark World'), - ('Hammer Bridge Pegs (South)', 'East Dark World'), - ('Ice Island To East Pier', 'East Dark World'), - - # ledge drops - ('Spectacle Rock Drop', 'West Death Mountain (Top)'), - ('Death Mountain Drop', 'West Death Mountain (Bottom)'), - ('Spiral Cave Ledge Access', 'Spiral Cave Ledge'), - ('Fairy Ascension Ledge Access', 'Fairy Ascension Ledge'), - ('East Death Mountain Drop', 'East Death Mountain (Bottom)'), - ('Spiral Cave Ledge Drop', 'East Death Mountain (Bottom)'), - ('Fairy Ascension Ledge Drop', 'Fairy Ascension Plateau'), - ('Fairy Ascension Drop', 'East Death Mountain (Bottom)'), - ('Death Mountain Entrance Drop', 'Light World'), - ('Death Mountain Return Ledge Drop', 'Light World'), - ('Graveyard Ledge Drop', 'Light World'), - ('Hyrule Castle Ledge Courtyard Drop', 'Hyrule Castle Courtyard'), - ('Hyrule Castle Ledge Drop', 'Light World'), - ('Maze Race Ledge Drop', 'Light World'), - ('Desert Ledge Drop', 'Light World'), - ('Desert Palace Mouth Drop', 'Light World'), - ('Checkerboard Ledge Drop', 'Light World'), - ('Desert Teleporter Drop', 'Light World'), - ('Cave 45 Ledge Drop', 'Light World'), - - ('Dark Death Mountain Drop (West)', 'West Dark Death Mountain (Bottom)'), - ('Dark Death Mountain Drop (East)', 'East Dark Death Mountain (Bottom)'), - ('Floating Island Drop', 'Dark Death Mountain (Top)'), - ('Turtle Rock Drop', 'Dark Death Mountain (Top)'), - ('Bumper Cave Entrance Drop', 'West Dark World'), - ('Bumper Cave Ledge Drop', 'West Dark World'), - ('Pyramid Drop', 'East Dark World'), - ('Village of Outcasts Drop', 'South Dark World'), - ('Dark Desert Drop', 'Dark Desert') + ('Ganon Drop', 'Bottom of Pyramid') ] -open_mandatory_connections = [('Sanctuary S&Q', 'Sanctuary'), - ('Old Man S&Q', 'Old Man House'), - ('Other World S&Q', 'East Dark World'), - - # flute - ('Flute Spot 1', 'West Death Mountain (Bottom)'), - ('Flute Spot 2', 'Potion Shop Area'), - ('Flute Spot 3', 'Light World'), - ('Flute Spot 4', 'Light World'), - ('Flute Spot 5', 'Light World'), - ('Flute Spot 6', 'Desert Teleporter Ledge'), - ('Flute Spot 7', 'Light World'), - ('Flute Spot 8', 'Light World'), - ('LW Flute', 'Flute Sky'), - ('NWLW Flute', 'Flute Sky'), - ('ZLW Flute', 'Flute Sky'), - ('DM Flute', 'Flute Sky'), - ('EDM Flute', 'Flute Sky'), - - # portals - ('Death Mountain Teleporter', 'West Dark Death Mountain (Bottom)'), - ('East Death Mountain Teleporter', 'East Dark Death Mountain (Bottom)'), - ('Turtle Rock Teleporter', 'Turtle Rock (Top)'), - ('Kakariko Teleporter', 'West Dark World'), - ('Castle Gate Teleporter', 'East Dark World'), - ('East Hyrule Teleporter', 'East Dark World'), - ('South Hyrule Teleporter', 'South Dark World'), - ('Desert Teleporter', 'Dark Desert'), - ('Lake Hylia Teleporter', 'Dark Lake Hylia Central Island') - ] - -inverted_mandatory_connections = [('Sanctuary S&Q', 'Dark Sanctuary Hint'), - ('Old Man S&Q', 'West Dark Death Mountain (Bottom)'), - ('Other World S&Q', 'Hyrule Castle Ledge'), - - # flute - ('Flute Spot 1', 'West Dark Death Mountain (Bottom)'), - ('Flute Spot 2', 'Northeast Dark World'), - ('Flute Spot 3', 'West Dark World'), - ('Flute Spot 4', 'South Dark World'), - ('Flute Spot 5', 'East Dark World'), - ('Flute Spot 6', 'Dark Desert Ledge'), - ('Flute Spot 7', 'South Dark World'), - ('Flute Spot 8', 'Southeast Dark World'), - ('DDM Flute', 'Flute Sky'), - ('NEDW Flute', 'Flute Sky'), - ('WDW Flute', 'Flute Sky'), - ('SDW Flute', 'Flute Sky'), - ('EDW Flute', 'Flute Sky'), - ('DD Flute', 'Flute Sky'), - ('DLHL Flute', 'Flute Sky'), - ('EDDM Flute', 'Flute Sky'), - ('Dark Grassy Lawn Flute', 'Flute Sky'), - ('Hammer Peg Area Flute', 'Flute Sky'), - - # modified terrain - ('Spectacle Rock Approach', 'Spectacle Rock'), - ('Spectacle Rock Leave', 'West Death Mountain (Top)'), - ('Floating Island Bridge (West)', 'East Death Mountain (Top)'), - ('Floating Island Bridge (East)', 'Death Mountain Floating Island'), - ('Graveyard Ladder (Top)', 'Light World'), - ('Graveyard Ladder (Bottom)', 'Graveyard Ledge'), - ('Mimic Cave Ledge Access', 'Mimic Cave Ledge'), - ('Mimic Cave Ledge Drop', 'East Death Mountain (Bottom)'), - ('Checkerboard Ledge Approach', 'Desert Checkerboard Ledge'), - ('Checkerboard Ledge Leave', 'Light World'), - ('Cave 45 Approach', 'Cave 45 Ledge'), - ('Cave 45 Leave', 'Light World'), - ('Lake Hylia Island Pier', 'Lake Hylia Island'), - ('Bombos Tablet Ladder (Top)', 'Light World'), - ('Bombos Tablet Ladder (Bottom)', 'Bombos Tablet Ledge'), - ('Dark Death Mountain Ladder (Top)', 'West Dark Death Mountain (Bottom)'), - ('Dark Death Mountain Ladder (Bottom)', 'Dark Death Mountain (Top)'), - ('Turtle Rock Tail Drop', 'Turtle Rock (Top)'), - ('Ice Palace Approach', 'Dark Lake Hylia Central Island'), - - # portals - ('Dark Death Mountain Teleporter (West)', 'West Death Mountain (Bottom)'), - ('East Dark Death Mountain Teleporter (Bottom)', 'East Death Mountain (Bottom)'), - ('East Dark Death Mountain Teleporter (Top)', 'East Death Mountain (Top)'), - ('West Dark World Teleporter', 'Light World'), - ('Post Aga Teleporter', 'Light World'), - ('East Dark World Teleporter', 'Light World'), - ('South Dark World Teleporter', 'Light World'), - ('Dark Desert Teleporter', 'Light World'), - ('Dark Lake Hylia Teleporter', 'Lake Hylia Central Island') - ] - # non-shuffled entrance links default_connections = {'Lost Woods Gamble': 'Lost Woods Gamble', 'Lost Woods Hideout Drop': 'Lost Woods Hideout (top)', @@ -2137,7 +2204,7 @@ default_connections = {'Lost Woods Gamble': 'Lost Woods Gamble', 'Paradox Cave (Top)': 'Paradox Cave', 'Paradox Cave Exit (Bottom)': 'East Death Mountain (Bottom)', 'Paradox Cave Exit (Middle)': 'East Death Mountain (Bottom)', - 'Paradox Cave Exit (Top)': 'East Death Mountain (Top)', + 'Paradox Cave Exit (Top)': 'East Death Mountain (Top East)', 'Waterfall of Wishing': 'Waterfall of Wishing', 'Fortune Teller (Light)': 'Fortune Teller (Light)', 'Bonk Rock Cave': 'Bonk Rock Cave', @@ -2226,8 +2293,8 @@ default_connections = {'Lost Woods Gamble': 'Lost Woods Gamble', 'Dark Lake Hylia Fairy': 'Dark Lake Hylia Healer Fairy', 'East Dark World Hint': 'East Dark World Hint', 'Mire Shed': 'Mire Shed', - 'Dark Desert Fairy': 'Dark Desert Healer Fairy', - 'Dark Desert Hint': 'Dark Desert Hint', + 'Mire Fairy': 'Mire Healer Fairy', + 'Mire Hint': 'Mire Hint', 'Hype Cave': 'Hype Cave', 'Dark Lake Hylia Shop': 'Dark Lake Hylia Shop', 'Dark Lake Hylia Ledge Fairy': 'Dark Lake Hylia Ledge Healer Fairy', @@ -2239,7 +2306,7 @@ open_default_connections = {'Links House': 'Links House', 'Links House Exit': 'Light World', 'Big Bomb Shop': 'Big Bomb Shop', 'Old Man Cave (West)': 'Old Man Cave Ledge', - 'Old Man Cave (East)': 'Old Man Cave', + 'Old Man Cave (East)': 'Old Man Cave (East)', 'Old Man Cave Exit (West)': 'Light World', 'Old Man Cave Exit (East)': 'West Death Mountain (Bottom)', 'Death Mountain Return Cave (West)': 'Death Mountain Return Cave (left)', @@ -2268,7 +2335,7 @@ inverted_default_connections = {'Links House': 'Big Bomb Shop', 'Bumper Cave (Top)': 'Dark Death Mountain Healer Fairy', 'Bumper Cave Exit (Top)': 'Death Mountain Return Ledge', 'Bumper Cave Exit (Bottom)': 'Light World', - 'Dark Death Mountain Fairy': 'Old Man Cave', + 'Dark Death Mountain Fairy': 'Old Man Cave (East)', 'Inverted Pyramid Hole': 'Pyramid', 'Inverted Pyramid Entrance': 'Bottom of Pyramid', 'Pyramid Exit': 'Hyrule Castle Courtyard' @@ -2285,19 +2352,19 @@ default_dungeon_connections = [('Hyrule Castle Entrance (South)', 'Hyrule Castle ('Desert Palace Entrance (West)', 'Desert West Portal'), ('Desert Palace Entrance (North)', 'Desert Back Portal'), ('Desert Palace Entrance (East)', 'Desert East Portal'), - ('Desert Palace Exit (South)', 'Desert Palace Stairs'), + ('Desert Palace Exit (South)', 'Desert Stairs'), ('Desert Palace Exit (West)', 'Desert Ledge'), - ('Desert Palace Exit (East)', 'Desert Palace Mouth'), - ('Desert Palace Exit (North)', 'Desert Palace Entrance (North) Spot'), + ('Desert Palace Exit (East)', 'Desert Mouth'), + ('Desert Palace Exit (North)', 'Desert Ledge Keep'), ('Eastern Palace', 'Eastern Portal'), - ('Eastern Palace Exit', 'Light World'), + ('Eastern Palace Exit', 'Eastern Palace Area'), ('Tower of Hera', 'Hera Portal'), ('Tower of Hera Exit', 'West Death Mountain (Top)'), ('Palace of Darkness', 'Palace of Darkness Portal'), - ('Palace of Darkness Exit', 'East Dark World'), + ('Palace of Darkness Exit', 'Palace of Darkness Area'), ('Swamp Palace', 'Swamp Portal'), # requires additional patch for flooding moat if moved - ('Swamp Palace Exit', 'South Dark World'), + ('Swamp Palace Exit', 'Swamp Area'), ('Skull Woods First Section Hole (East)', 'Skull Pinball'), ('Skull Woods First Section Hole (West)', 'Skull Left Drop'), ('Skull Woods First Section Hole (North)', 'Skull Pot Circle'), @@ -2311,13 +2378,13 @@ default_dungeon_connections = [('Hyrule Castle Entrance (South)', 'Hyrule Castle ('Skull Woods Final Section', 'Skull 3 Portal'), ('Skull Woods Final Section Exit', 'Skull Woods Forest (West)'), ('Thieves Town', 'Thieves Town Portal'), - ('Thieves Town Exit', 'West Dark World'), + ('Thieves Town Exit', 'Village of Outcasts'), ('Ice Palace', 'Ice Portal'), - ('Ice Palace Exit', 'Dark Lake Hylia Central Island'), + ('Ice Palace Exit', 'Ice Palace Area'), ('Misery Mire', 'Mire Portal'), - ('Misery Mire Exit', 'Dark Desert'), + ('Misery Mire Exit', 'Mire Area'), ('Turtle Rock', 'Turtle Rock Main Portal'), - ('Turtle Rock Exit (Front)', 'Dark Death Mountain (Top)'), + ('Turtle Rock Exit (Front)', 'Turtle Rock Area'), ('Dark Death Mountain Ledge (West)', 'Turtle Rock Lazy Eyes Portal'), ('Dark Death Mountain Ledge (East)', 'Turtle Rock Chest Portal'), ('Turtle Rock Ledge Exit (West)', 'Dark Death Mountain Ledge'), @@ -2329,24 +2396,24 @@ default_dungeon_connections = [('Hyrule Castle Entrance (South)', 'Hyrule Castle open_default_dungeon_connections = [('Agahnims Tower', 'Agahnims Tower Portal'), ('Agahnims Tower Exit', 'Hyrule Castle Ledge'), ('Ganons Tower', 'Ganons Tower Portal'), - ('Ganons Tower Exit', 'Dark Death Mountain (Top)') + ('Ganons Tower Exit', 'West Dark Death Mountain (Top)') ] inverted_default_dungeon_connections = [('Agahnims Tower', 'Ganons Tower Portal'), - ('Agahnims Tower Exit', 'Dark Death Mountain (Top)'), + ('Agahnims Tower Exit', 'West Dark Death Mountain (Top)'), ('Ganons Tower', 'Agahnims Tower Portal'), ('Ganons Tower Exit', 'Hyrule Castle Ledge') ] indirect_connections = { - 'Turtle Rock (Top)': 'Turtle Rock', - 'East Dark World': 'Pyramid Fairy', + 'Turtle Rock Ledge': 'Turtle Rock', + 'Pyramid Area': 'Pyramid Fairy', 'Big Bomb Shop': 'Pyramid Fairy', - 'Dark Desert': 'Pyramid Fairy', - 'West Dark World': 'Pyramid Fairy', - 'South Dark World': 'Pyramid Fairy', - 'Light World': 'Pyramid Fairy', - 'Old Man Cave': 'Old Man S&Q' + 'Mire Area': 'Pyramid Fairy', + #'West Dark World': 'Pyramid Fairy', + 'Big Bomb Shop Area': 'Pyramid Fairy', + #'Light World': 'Pyramid Fairy', + 'Old Man Cave (East)': 'Old Man S&Q' } # format: # Key=Name @@ -2486,8 +2553,8 @@ door_addresses = {'Links House': (0x00, (0x0104, 0x2c, 0x0506, 0x0a9a, 0x0832, 0 'Dark 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)), + 'Mire Hint': (0x61, (0x0114, 0x70, 0x0654, 0x0cc5, 0x02aa, 0x0d16, 0x0328, 0x0d32, 0x032f, 0x09, 0xf7, 0x0000, 0x0000)), + 'Mire 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)), 'Dark Death Mountain Shop': (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)), @@ -2573,7 +2640,7 @@ exit_ids = {'Links House Exit': (0x01, 0x00), 'Desert Healer Fairy': 0x5E, 'Dark Lake Hylia Healer Fairy': 0x5E, 'Dark Lake Hylia Ledge Healer Fairy': 0x5E, - 'Dark Desert Healer Fairy': 0x5E, + 'Mire Healer Fairy': 0x5E, 'Dark Death Mountain Healer Fairy': 0x5E, 'Fortune Teller (Light)': 0x65, 'Lake Hylia Fortune Teller': 0x65, @@ -2628,7 +2695,7 @@ exit_ids = {'Links House Exit': (0x01, 0x00), 'Fortune Teller (Dark)': 0x66, 'Archery Game': 0x59, 'Mire Shed': 0x5F, - 'Dark Desert Hint': 0x62, + 'Mire Hint': 0x62, 'Spike Cave': 0x41, 'Mimic Cave': 0x4F, 'Kakariko Well (top)': 0x80, @@ -2764,8 +2831,8 @@ ow_prize_table = {'Links House': (0x8b1, 0xb2d), 'Dark Potion Shop': (0xc80, 0x4c0), 'Archery Game': (0x2f0, 0xaf0), 'Mire Shed': (0x060, 0xc90), - 'Dark Desert Hint': (0x2e0, 0xd00), - 'Dark Desert Fairy': (0x1c0, 0xc90), + 'Mire Hint': (0x2e0, 0xd00), + 'Mire Fairy': (0x1c0, 0xc90), 'Spike Cave': (0x860, 0x180), 'Dark Death Mountain Shop': (0xd80, 0x180), 'Dark Death Mountain Fairy': (0x620, 0x2c0), diff --git a/source/rom/DataTables.py b/source/rom/DataTables.py new file mode 100644 index 00000000..c818dfc4 --- /dev/null +++ b/source/rom/DataTables.py @@ -0,0 +1,188 @@ +from collections import defaultdict + +from Utils import snes_to_pc, int24_as_bytes, int16_as_bytes, load_cached_yaml + +from source.dungeon.EnemyList import EnemyTable, init_vanilla_sprites, vanilla_sprites, init_enemy_stats, EnemySprite +from source.dungeon.EnemyList import sprite_translation +from source.dungeon.RoomHeader import init_room_headers +from source.dungeon.RoomList import Room0127 +from source.enemizer.OwEnemyList import init_vanilla_sprites_ow, vanilla_sprites_ow +from source.enemizer.SpriteSheets import init_sprite_sheets, init_sprite_requirements, SheetChoice + + +def convert_area_id_to_offset(area_id): + if area_id < 0x40: + return area_id + if 0x40 <= area_id < 0x80: + return area_id + 0x40 + if 0x90 <= area_id <= 0xCF: + return area_id - 0x50 + raise Exception(f'{hex(area_id)} is not a valid area id for offset math') + + +class DataTables: + def __init__(self): + self.room_headers = None + self.room_list = None + self.sprite_sheets = None + self.uw_enemy_table = None + self.ow_enemy_table = None + self.pot_secret_table = None + self.overworld_sprite_sheets = None + + # associated data + self.sprite_requirements = None + self.room_requirements = None + self.enemy_stats = None + self.enemy_damage = None + self.bush_sprite_table = {} + + # enemizer conditions + self.uw_enemy_denials = {} + self.ow_enemy_denials = {} + self.uw_enemy_drop_denials = {} + self.sheet_choices = [] + denial_data = load_cached_yaml(['source', 'enemizer', 'enemy_deny.yaml']) + for denial in denial_data['UwGeneralDeny']: + self.uw_enemy_denials[denial[0], denial[1]] = {sprite_translation[x] for x in denial[2]} + for denial in denial_data['OwGeneralDeny']: + self.ow_enemy_denials[denial[0], denial[1]] = {sprite_translation[x] for x in denial[2]} + for denial in denial_data['UwEnemyDrop']: + self.uw_enemy_drop_denials[denial[0], denial[1]] = {sprite_translation[x] for x in denial[2]} + weights = load_cached_yaml(['source', 'enemizer', 'enemy_weight.yaml']) + self.uw_weights = {sprite_translation[k]: v for k, v in weights['UW'].items()} + self.ow_weights = {sprite_translation[k]: v for k, v in weights['OW'].items()} + sheet_weights = load_cached_yaml(['source', 'enemizer', 'sheet_weight.yaml']) + for item in sheet_weights['SheetChoices']: + choice = SheetChoice(tuple(item['slots']), item['assignments'], item['weight']) + self.sheet_choices.append(choice) + + def write_to_rom(self, rom, colorize_pots=False, increase_bush_sprite_chance=False): + if self.pot_secret_table.size() > 0x11c0: + raise Exception('Pot table is too big for current area') + self.pot_secret_table.write_pot_data_to_rom(rom, colorize_pots, self) + for room_id, header in self.room_headers.items(): + data_location = (0x30DA00 + room_id * 14) & 0xFFFF + rom.write_bytes(snes_to_pc(0x04F1E2) + room_id * 2, int16_as_bytes(data_location)) + header.write_to_rom(rom, snes_to_pc(0x30DA00)) # new header table, bank30, tables.asm + room_start_address = 0x378000 + for room_id, room in self.room_list.items(): + rom.write_bytes(snes_to_pc(0x1F8000 + room_id * 3), int24_as_bytes(room_start_address)) + door_start, bytes_written = room.write_to_rom(snes_to_pc(room_start_address), rom) + rom.write_bytes(snes_to_pc(0x1F83C0 + room_id * 3), int24_as_bytes(room_start_address + door_start)) + room_start_address += bytes_written + if room_start_address > 0x380000: + raise Exception('Room list exceeded bank size') + # size notes: bank 03 uses 140E bytes + # bank 0A uses 372A bytes + # bank 1F uses 77CE bytes: total is about a bank and a half + # probably should reuse bank 1F if writing all the rooms out + for sheet in self.sprite_sheets.values(): + sheet.write_to_rom(rom, snes_to_pc(0x00DB97)) # bank 00, SheetsTable_AA3 + if self.uw_enemy_table.size() > 0x2800: + raise Exception('Sprite table is too big for current area') + self.uw_enemy_table.write_sprite_data_to_rom(rom) + self.uw_enemy_table.check_special_bitmasks_size() + self.uw_enemy_table.write_special_bitmask_table(rom) + for area_id, sheet in self.overworld_sprite_sheets.items(): + if area_id in [0x80, 0x81]: + offset = area_id - 0x80 # 02E575 for special areas? + rom.write_byte(snes_to_pc(0x02E576+offset), sheet.id) + else: + offset = convert_area_id_to_offset(area_id) + rom.write_byte(snes_to_pc(0x00FA81+offset), sheet.id) + # _00FA81 is LW normal + # _00FAC1 is LW post-aga + # _00FB01 is DW + for area, sprite_list in self.ow_enemy_table.items(): + for sprite in sprite_list: + rom.write_bytes(snes_to_pc(sprite.original_address), sprite.sprite_data_ow()) + for sprite, stats in self.enemy_stats.items(): + # write health to rom + if stats.health is not None: + if isinstance(stats.health, tuple): + if sprite == EnemySprite.Octorok4Way: # skip this one + continue + if sprite in special_health_table: + a1, a2 = special_health_table[sprite] + rom.write_byte(snes_to_pc(a1), stats.health[0]) + rom.write_byte(snes_to_pc(a2), stats.health[1]) + else: + rom.write_byte(snes_to_pc(0x0DB173+int(sprite)), stats.health) + # write damage class to rom + if stats.damage is not None: + if isinstance(stats.damage, tuple): + if sprite == EnemySprite.Octorok4Way: # skip this one + continue + if sprite in special_damage_table: + a1, a2 = special_damage_table[sprite] + rom.write_byte(snes_to_pc(a1), stats.dmask | stats.damage[0]) + rom.write_byte(snes_to_pc(a2), stats.dmask | stats.damage[1]) + else: + rom.write_byte(snes_to_pc(0x0DB266+int(sprite)), stats.dmask | stats.damage) + # write damage table to rom + for idx, damage_list in self.enemy_damage.items(): + rom.write_bytes(snes_to_pc(0x06F42D + idx * 3), damage_list) + # write bush spawns to rom: + for area_id, bush_sprite in self.bush_sprite_table.items(): + rom.write_byte(snes_to_pc(0x368120 + area_id), bush_sprite.sprite) + if increase_bush_sprite_chance: + rom.write_bytes(snes_to_pc(0x1AFBBB), [ + 0x01, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x12, + 0x0F, 0x01, 0x0F, 0x0F, 0x11, 0x0F, 0x0F, 0x03 + ]) + + +special_health_table = { + EnemySprite.Octorok: (0x068F76, 0x068F77), + EnemySprite.HardhatBeetle: (0x06911F, 0x069120), + EnemySprite.Tektite: (0x068D97, 0x068D98), + EnemySprite.CricketRat: (0x068876, 0x068877), + EnemySprite.Keese: (0x06888A, 0x06888B), + EnemySprite.Snake: (0x0688A6, 0x0688A7), + EnemySprite.Raven: (0x068965, 0x068966) +} + +special_damage_table = { + EnemySprite.Octorok: (0x068F74, 0x068F75), + EnemySprite.Tektite: (0x068D99, 0x068D9A), + EnemySprite.CricketRat: (0x068874, 0x068875), + EnemySprite.Keese: (0x068888, 0x068889), + EnemySprite.Snake: (0x0688A4, 0x0688A5), + EnemySprite.Raven: (0x068963, 0x068964) +} + + +def init_data_tables(world, player): + data_tables = DataTables() + data_tables.room_headers = init_room_headers() + data_tables.room_list = {} + if world.pottery[player] not in ['none']: + data_tables.room_list[0x0127] = Room0127 + data_tables.sprite_requirements = init_sprite_requirements() + data_tables.sprite_sheets = init_sprite_sheets(data_tables.sprite_requirements) + init_vanilla_sprites() + data_tables.enemy_stats = init_enemy_stats() + uw_table = data_tables.uw_enemy_table = EnemyTable() + for room, sprite_list in vanilla_sprites.items(): + for sprite in sprite_list: + uw_table.room_map[room].append(sprite.copy()) + data_tables.overworld_sprite_sheets = {} + data_tables.ow_enemy_table = defaultdict(list) + init_vanilla_sprites_ow() + for area, sprite_list in vanilla_sprites_ow.items(): + for sprite in sprite_list: + data_tables.ow_enemy_table[area].append(sprite.copy()) + data_tables.enemy_damage = {k: list(v) for k, v in world.damage_table[player].enemy_damage.items()} + # todo: more denials based on enemy drops + return data_tables + + +def get_uw_enemy_table(): + init_vanilla_sprites() + uw_table = EnemyTable() + for room, sprite_list in vanilla_sprites.items(): + for sprite in sprite_list: + uw_table.room_map[room].append(sprite.copy()) + return uw_table + diff --git a/source/rom/__init__.py b/source/rom/__init__.py new file mode 100644 index 00000000..724252e9 --- /dev/null +++ b/source/rom/__init__.py @@ -0,0 +1 @@ +# do nothing, just exist to make "source" package diff --git a/source/tools/MysteryUtils.py b/source/tools/MysteryUtils.py index 5000f670..ec3518fb 100644 --- a/source/tools/MysteryUtils.py +++ b/source/tools/MysteryUtils.py @@ -33,6 +33,39 @@ def roll_settings(weights): return default return choice + def get_choice_bool(option, root=weights): + choice = get_choice(option, root) + if choice is True or choice == 'on': + return True + if choice is False or choice == 'off': + return False + if choice is None: + return choice + raise Exception("This fields needs to be true/false or off/on") + + def get_choice_non_bool(option, root=weights): + choice = get_choice(option, root) + if choice is True or choice == 'on': + return 'on' + if choice is False or choice == 'off': + return 'off' + return choice + + def get_choice_yn(option, root=weights): + choice = get_choice(option, root) + if choice is True or choice == 'yes': + return 'yes' + if choice is False or choice == 'no': + return 'no' + return choice + + + def get_choice_bool_default(option, root=weights, default=None): + choice = get_choice_bool(option, root) + if choice is None and default is not None: + return default + return choice + while True: subweights = weights.get('subweights', {}) if len(subweights) == 0: @@ -47,7 +80,8 @@ def roll_settings(weights): ret.algorithm = get_choice('algorithm') - glitch_map = {'none': 'noglitches', 'no_logic': 'nologic', 'owglitches': 'owglitches', + glitch_map = {'none': 'noglitches', 'no_logic': 'nologic', 'hybridglitches': 'hybridglitches', + 'hmg': 'hybridglitches', 'owglitches': 'owglitches', 'owg': 'owglitches', 'minorglitches': 'minorglitches'} glitches_required = get_choice('glitches_required') if glitches_required is not None: @@ -62,8 +96,8 @@ def roll_settings(weights): dungeon_items = get_choice('dungeon_items') dungeon_items = '' if dungeon_items == 'standard' or dungeon_items is None else dungeon_items dungeon_items = 'mcsb' if dungeon_items == 'full' else dungeon_items - ret.mapshuffle = get_choice('map_shuffle') == 'on' if 'map_shuffle' in weights else 'm' in dungeon_items - ret.compassshuffle = get_choice('compass_shuffle') == 'on' if 'compass_shuffle' in weights else 'c' in dungeon_items + ret.mapshuffle = get_choice_bool('map_shuffle') if 'map_shuffle' in weights else 'm' in dungeon_items + ret.compassshuffle = get_choice_bool('compass_shuffle') if 'compass_shuffle' in weights else 'c' in dungeon_items if 'smallkey_shuffle' in weights: ret.keyshuffle = get_choice('smallkey_shuffle') else: @@ -71,7 +105,7 @@ def roll_settings(weights): ret.keyshuffle = 'wild' if 'u' in dungeon_items: ret.keyshuffle = 'universal' - ret.bigkeyshuffle = get_choice('bigkey_shuffle') == 'on' if 'bigkey_shuffle' in weights else 'b' in dungeon_items + ret.bigkeyshuffle = get_choice_bool('bigkey_shuffle') if 'bigkey_shuffle' in weights else 'b' in dungeon_items ret.accessibility = get_choice('accessibility') ret.restrict_boss_items = get_choice('restrict_boss_items') @@ -86,30 +120,32 @@ def roll_settings(weights): ret.door_type_mode = get_choice('door_type_mode') ret.trap_door_mode = get_choice('trap_door_mode') ret.key_logic_algorithm = get_choice('key_logic_algorithm') - ret.decoupledoors = get_choice('decoupledoors') == 'on' - ret.door_self_loops = get_choice('door_self_loops') == 'on' - ret.experimental = get_choice('experimental') == 'on' - ret.collection_rate = get_choice('collection_rate') == 'on' + ret.decoupledoors = get_choice_bool('decoupledoors') + ret.door_self_loops = get_choice_bool('door_self_loops') + ret.experimental = get_choice_bool('experimental') + ret.collection_rate = get_choice_bool('collection_rate') - ret.dungeon_counters = get_choice('dungeon_counters') if 'dungeon_counters' in weights else 'default' + ret.dungeon_counters = get_choice_non_bool('dungeon_counters') if 'dungeon_counters' in weights else 'default' if ret.dungeon_counters == 'default': ret.dungeon_counters = 'pickup' if ret.door_shuffle != 'vanilla' or ret.compassshuffle == 'on' else 'off' - ret.shufflelinks = get_choice('shufflelinks') == 'on' - ret.shuffletavern = get_choice('shuffletavern') == 'on' - ret.pseudoboots = get_choice('pseudoboots') == 'on' - ret.shopsanity = get_choice('shopsanity') == 'on' - keydropshuffle = get_choice('keydropshuffle') == 'on' - ret.dropshuffle = get_choice('dropshuffle') == 'on' or keydropshuffle + ret.shufflelinks = get_choice_bool('shufflelinks') + ret.shuffletavern = get_choice_bool('shuffletavern') + ret.pseudoboots = get_choice_bool('pseudoboots') + ret.shopsanity = get_choice_bool('shopsanity') + keydropshuffle = get_choice_bool('keydropshuffle') + ret.dropshuffle = get_choice('dropshuffle') if 'dropshuffle' in weights else 'none' + ret.dropshuffle = 'keys' if ret.dropshuffle == 'none' and keydropshuffle else ret.dropshuffle ret.pottery = get_choice('pottery') if 'pottery' in weights else 'none' ret.pottery = 'keys' if ret.pottery == 'none' and keydropshuffle else ret.pottery - ret.colorizepots = get_choice_default('colorizepots', default='on') == 'on' - ret.shufflepots = get_choice('pot_shuffle') == 'on' + ret.colorizepots = get_choice_bool_default('colorizepots', default=True) + ret.shufflepots = get_choice_bool('pot_shuffle') + ret.aga_randomness = get_choice_bool('aga_randomness') ret.mixed_travel = get_choice('mixed_travel') if 'mixed_travel' in weights else 'prevent' ret.standardize_palettes = (get_choice('standardize_palettes') if 'standardize_palettes' in weights else 'standardize') - goal = get_choice('goals') + goal = get_choice_default('goals', default='ganon') if goal is not None: ret.goal = {'ganon': 'ganon', 'fast_ganon': 'crystals', @@ -120,7 +156,7 @@ def roll_settings(weights): 'ganonhunt': 'ganonhunt', 'completionist': 'completionist' }[goal] - ret.openpyramid = get_choice('open_pyramid') if 'open_pyramid' in weights else 'auto' + ret.openpyramid = get_choice_yn('open_pyramid') if 'open_pyramid' in weights else 'auto' ret.crystals_gt = get_choice('tower_open') @@ -139,12 +175,12 @@ def roll_settings(weights): if ret.mode == 'retro': ret.mode = 'open' ret.retro = True - ret.retro = get_choice('retro') == 'on' # this overrides world_state if used + ret.retro = get_choice_bool('retro') # this overrides world_state if used ret.take_any = get_choice_default('take_any', default='none') - ret.bombbag = get_choice('bombbag') == 'on' + ret.bombbag = get_choice_bool('bombbag') - ret.hints = get_choice('hints') == 'on' + ret.hints = get_choice_bool('hints') swords = get_choice('weapons') if swords is not None: @@ -181,6 +217,7 @@ def roll_settings(weights): ret.enemy_damage = damage_choice ret.enemy_health = get_choice('enemy_health') + ret.any_enemy_logic = get_choice('any_enemy_logic') ret.beemizer = get_choice('beemizer') if 'beemizer' in weights else '0' @@ -196,15 +233,15 @@ def roll_settings(weights): if 'rom' in weights: romweights = weights['rom'] ret.sprite = get_choice('sprite', romweights) - ret.disablemusic = get_choice('disablemusic', romweights) == 'on' - ret.quickswap = get_choice('quickswap', romweights) == 'on' - ret.reduce_flashing = get_choice('reduce_flashing', romweights) == 'on' + ret.disablemusic = get_choice_bool('disablemusic', romweights) + ret.quickswap = get_choice_bool('quickswap', romweights) + ret.reduce_flashing = get_choice_bool('reduce_flashing', romweights) ret.fastmenu = get_choice('menuspeed', romweights) ret.heartcolor = get_choice('heartcolor', romweights) - ret.heartbeep = get_choice('heartbeep', romweights) + ret.heartbeep = get_choice_non_bool('heartbeep', romweights) ret.ow_palettes = get_choice('ow_palettes', romweights) ret.uw_palettes = get_choice('uw_palettes', romweights) - ret.shuffle_sfx = get_choice('shuffle_sfx', romweights) == 'on' - ret.msu_resume = get_choice('msu_resume', romweights) == 'on' + ret.shuffle_sfx = get_choice_bool('shuffle_sfx', romweights) + ret.msu_resume = get_choice_bool('msu_resume', romweights) return ret diff --git a/test/NewTestSuite.py b/test/NewTestSuite.py index aca4e796..8e7b9e1c 100644 --- a/test/NewTestSuite.py +++ b/test/NewTestSuite.py @@ -28,7 +28,7 @@ def main(args=None): def test(test_name: str, command: str, test_file: str): tests[test_name] = [command] - base_command = f"python3.8 DungeonRandomizer.py --suppress_rom --suppress_spoiler" + base_command = f"python3 DungeonRandomizer.py --suppress_rom --suppress_spoiler" def gen_seed(): task_command = base_command + " " + command diff --git a/test/customizer/enemizer_test.yaml b/test/customizer/enemizer_test.yaml new file mode 100644 index 00000000..ce3f6753 --- /dev/null +++ b/test/customizer/enemizer_test.yaml @@ -0,0 +1,2058 @@ +#placements: +# 1: +# Dark Lake Hylia Shop - Left: Blue Potion +meta: + players: 1 +# seed: 140 + seed: 773569045 +settings: + 1: +# boss_shuffle: unique + enemy_shuffle: shuffled +# dropshuffle: underworld +# dungeon_counters: 'on' +# door_shuffle: crossed +# intensity: 3 +enemies: + 1: + Underworld: +# 0x2: +# 0: Wizzrobe +# 1: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe +# 4: Wizzrobe +# 14: Wizzrobe +# 15: Wizzrobe + 0x4: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 9: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 0x9: + 2: Wizzrobe + 0xa: + 0: Wizzrobe + 1: Wizzrobe + 0xb: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0xe: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x11: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x13: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0x15: + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0x16: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x17: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0x19: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x1a: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0x1b: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x1e: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x1f: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x21: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0x22: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x24: + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x26: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0x27: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x28: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x2a: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x2b: + 2: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x2e: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x31: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0x32: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x34: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x35: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0x36: + 1: Wizzrobe + 2: Wizzrobe + 5: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0x37: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 9: Wizzrobe + 0x38: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x39: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x3a: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x3b: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x3c: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x3d: + 2: Wizzrobe + 3: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 0x3e: + 1: Wizzrobe + 2: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0x3f: + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x40: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x41: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x42: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x43: + 0: Wizzrobe + 0x44: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 8: Wizzrobe + 0x45: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe +# 0x46: +# 0: Wizzrobe +# 2: Wizzrobe +# 4: Wizzrobe + 0x49: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 0x4a: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x4b: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x4c: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x4e: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x50: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x51: + 1: Wizzrobe + 2: Wizzrobe + 0x52: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x53: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 0x54: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x55: + 1: Wizzrobe + 2: Wizzrobe + 0x56: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 0x57: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 0x58: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0x59: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0x5b: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0x5d: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 0x5e: + 3: Wizzrobe + 4: Wizzrobe + 0x5f: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x60: + 0: Wizzrobe + 0x61: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x62: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x63: + 1: Wizzrobe +# 0x64: +# 0: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe +# 4: Wizzrobe +# 5: Wizzrobe +# 6: Wizzrobe + 0x65: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe +# 0x66: +# 0: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe +# 6: Wizzrobe +# 7: Wizzrobe +# 8: Wizzrobe +# 9: Wizzrobe +# 11: Wizzrobe + 0x67: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0x68: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x6a: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x6b: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 0x6d: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0x6e: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x71: + 0: Wizzrobe + 1: Wizzrobe + 0x72: + 0: Wizzrobe + 1: Wizzrobe + 0x73: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x74: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x75: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe +# 0x76: +# 1: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe +# 4: Wizzrobe +# 6: Wizzrobe + 0x77: + 0: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x7b: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0x7c: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x7d: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe +# 0x7e: +# 0: Wizzrobe +# 1: Wizzrobe +# 2: Wizzrobe +# 5: Wizzrobe +# 6: Wizzrobe + 0x7f: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x80: + 1: Wizzrobe + 2: Wizzrobe + 0x81: + 0: Wizzrobe + 1: Wizzrobe + 0x82: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x83: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0x84: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x85: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0x87: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 12: Wizzrobe + 0x8b: + 0: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x8c: + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 0x8d: + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 12: Wizzrobe + 0x8e: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x91: + 1: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x92: + 2: Wizzrobe + 4: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0x93: + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x95: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x96: + 0: Wizzrobe + 0x98: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x99: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0x9b: + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 0x9c: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x9d: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe +# 0x9e: +# 0: Wizzrobe +# 1: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe + 0x9f: + 4: Wizzrobe + 5: Wizzrobe + 0xa0: + 1: Wizzrobe + 2: Wizzrobe + 0xa1: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0xa5: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0xa6: + 1: Wizzrobe + 0xa8: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0xa9: + 0: Wizzrobe + 1: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0xaa: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0xab: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0xae: + 0: Wizzrobe + 1: Wizzrobe + 0xaf: + 0: Wizzrobe + 0xb0: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 0xb1: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0xb2: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 0xb3: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 0xb5: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0xb6: + 0: Wizzrobe + 1: Wizzrobe + 5: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0xb7: + 0: Wizzrobe + 1: Wizzrobe + 0xb8: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0xba: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0xbb: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0xbc: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe +# 0xbe: +# 0: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe +# 4: Wizzrobe +# 5: Wizzrobe +# 6: Wizzrobe + 0xbf: + 1: Wizzrobe + 0xc0: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0xc1: + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 8: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0xc2: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0xc3: + 6: Wizzrobe + 0xc4: + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0xc5: + 6: Wizzrobe + 0xc6: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0xc9: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0xcb: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0xcc: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 0xce: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0xd0: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0xd1: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0xd2: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0xd5: + 4: Wizzrobe + 0xd8: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0xd9: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0xda: + 0: Wizzrobe + 1: Wizzrobe + 0xdb: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0xdc: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0xdf: + 0: Wizzrobe + 1: Wizzrobe + 0xe0: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe +# 0xe4: +# 0: Wizzrobe +# 1: Wizzrobe +# 2: Wizzrobe + 0xe5: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0xe6: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xe7: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0xe8: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0xee: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xef: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe +# 0xf0: +# 0: Wizzrobe +# 1: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe +# 4: Wizzrobe +# 5: Wizzrobe +# 6: Wizzrobe +# 7: Wizzrobe +# 9: Wizzrobe + 0xf1: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0xf9: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0xfb: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0xfd: + 0: Wizzrobe + 1: Wizzrobe + 4: Wizzrobe + 0xfe: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x107: + 1: Wizzrobe + 2: Wizzrobe + 0x108: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x10b: + 6: Wizzrobe + 0x10c: + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x10d: + 0: Wizzrobe + 1: Wizzrobe +# 0x123: +# 0: Wizzrobe +# 1: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe + Overworld: + 0x21b: + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x40: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 18: Wizzrobe + 19: Wizzrobe + 0x42: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 0x45: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x4a: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe +# 0x4f: +# 0: Wizzrobe +# 2: Wizzrobe +# 3: Wizzrobe + 0x50: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x51: + 0: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x52: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x53: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x54: + 0: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x55: + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 0x56: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x57: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x58: + 0: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 0x5a: + 0: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x5b: + 0: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0x5d: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x5e: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 19: Wizzrobe + 20: Wizzrobe + 0x62: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x65: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x6b: + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x6c: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x6d: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x6e: + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x6f: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x70: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 0x72: + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 0x73: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x74: + 0: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 0x75: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 18: Wizzrobe + 19: Wizzrobe + 20: Wizzrobe + 21: Wizzrobe + 0x77: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x7a: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x7b: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x7c: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x7f: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x81: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 18: Wizzrobe + 19: Wizzrobe + 20: Wizzrobe + 21: Wizzrobe + 22: Wizzrobe + 23: Wizzrobe + 24: Wizzrobe + 25: Wizzrobe + 26: Wizzrobe + 27: Wizzrobe + 28: Wizzrobe + 0x0: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe +# 0x2: +# 0: Wizzrobe +# 2: Wizzrobe + 0x3: + 2: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0x5: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 0x7: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xa: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 0xf: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x10: + 0: Wizzrobe + 3: Wizzrobe + 0x11: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 0x12: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 0x13: + 1: Wizzrobe + 0x14: + 0: Wizzrobe + 1: Wizzrobe + 0x15: + 1: Wizzrobe + 3: Wizzrobe +# 0x16: +# 0: Wizzrobe +# 2: Wizzrobe + 0x17: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x18: + 9: Wizzrobe + 10: Wizzrobe + 0x1a: + 0: Wizzrobe + 1: Wizzrobe + 4: Wizzrobe + 0x1b: + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 0x1d: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x1e: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 18: Wizzrobe + 0x22: + 1: Wizzrobe + 2: Wizzrobe + 0x25: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x2b: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x2c: + 0: Wizzrobe + 1: Wizzrobe + 0x2d: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x2e: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x2f: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0x30: + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 18: Wizzrobe + 19: Wizzrobe + 20: Wizzrobe + 21: Wizzrobe + 22: Wizzrobe + 0x32: + 1: Wizzrobe + 2: Wizzrobe + 0x33: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 0x34: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x35: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 18: Wizzrobe + 19: Wizzrobe + 20: Wizzrobe + 21: Wizzrobe + 22: Wizzrobe + 0x37: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x3a: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0x3b: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0x3c: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 0x3f: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 0x90: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 13: Wizzrobe + 0x93: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 0x95: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 0x97: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0x9a: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0x9f: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 0xa0: + 0: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xa1: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 0xa2: + 1: Wizzrobe + 3: Wizzrobe + 0xa3: + 2: Wizzrobe + 3: Wizzrobe + 0xa4: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0xa5: + 1: Wizzrobe + 3: Wizzrobe +# 0xa6: +# 0: Wizzrobe +# 2: Wizzrobe + 0xa7: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xa8: + 0: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 0xaa: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 0xab: + 0: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 0xad: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0xae: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 19: Wizzrobe + 0xb2: + 1: Wizzrobe + 2: Wizzrobe + 0xb5: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0xb8: + 0: Wizzrobe + 4: Wizzrobe + 0xb9: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0xbb: + 0: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xbc: + 0: Wizzrobe + 1: Wizzrobe + 0xbd: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 0xbe: + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0xbf: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 0xc0: + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 18: Wizzrobe + 19: Wizzrobe + 0xc2: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 5: Wizzrobe + 0xc3: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xc4: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xc5: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 8: Wizzrobe + 9: Wizzrobe + 10: Wizzrobe + 11: Wizzrobe + 12: Wizzrobe + 13: Wizzrobe + 14: Wizzrobe + 15: Wizzrobe + 16: Wizzrobe + 17: Wizzrobe + 19: Wizzrobe + 20: Wizzrobe + 21: Wizzrobe + 22: Wizzrobe + 0xc7: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xca: + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 6: Wizzrobe + 7: Wizzrobe + 0xcb: + 0: Wizzrobe + 1: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 5: Wizzrobe + 0xcc: + 0: Wizzrobe + 1: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe + 4: Wizzrobe + 0xcf: + 0: Wizzrobe + 2: Wizzrobe + 3: Wizzrobe +start_inventory: + 1: + - Lamp + - Progressive Sword + - Ocarina (Activated) + - Hookshot + 2: [] + diff --git a/test/customizer/hmg/fireless_ice.yaml b/test/customizer/hmg/fireless_ice.yaml new file mode 100644 index 00000000..86affd94 --- /dev/null +++ b/test/customizer/hmg/fireless_ice.yaml @@ -0,0 +1,35 @@ +meta: + players: 1 +settings: + 1: + accessibility: items + door_shuffle: vanilla + logic: hybridglitches + mode: open + shuffle: vanilla +doors: + 1: + doors: + Hera Beetles WS: Hera Startile Corner ES + Hera Startile Corner ES: Hera Beetles WS + Hera Startile Corner NW: Hera Startile Wide SW + Hera Startile Wide SW: Hera Startile Corner NW + Skull 1 Lobby ES: Skull Map Room WS + Skull 2 West Lobby ES: Skull Small Hall WS + Skull 2 West Lobby NW: Skull X Room SW + Skull Big Chest N: Skull Pull Switch S + Skull Compass Room WS: Skull Left Drop ES + Skull Final Drop WS: Skull Spike Corner ES + Skull Left Drop ES: Skull Compass Room WS + Skull Map Room WS: Skull 1 Lobby ES + Skull Pot Circle WN: Skull Pull Switch EN + Skull Pull Switch EN: Skull Pot Circle WN + Skull Pull Switch S: Skull Big Chest N + Skull Small Hall WS: Skull 2 West Lobby ES + Skull Spike Corner ES: Skull Final Drop WS + Skull X Room SW: Skull 2 West Lobby NW + lobbies: {} +placements: + 1: + Ice Palace - Compass Chest: Fire Rod + Ice Palace - Freezor Chest: Bombos diff --git a/test/customizer/hmg/hammer_in_swamp.yaml b/test/customizer/hmg/hammer_in_swamp.yaml new file mode 100644 index 00000000..f2e3aae7 --- /dev/null +++ b/test/customizer/hmg/hammer_in_swamp.yaml @@ -0,0 +1,42 @@ +meta: + players: 1 +settings: + 1: + accessibility: items + door_shuffle: vanilla + logic: hybridglitches + mode: open + shuffle: vanilla +start_inventory: + 1: + - Flippers + - Lamp + - Tempered Sword + - Boss Heart Container + - Boss Heart Container + - Boss Heart Container +doors: + 1: + doors: + Hera Beetles WS: Hera Startile Corner ES + Hera Startile Corner ES: Hera Beetles WS + Hera Startile Corner NW: Hera Startile Wide SW + Hera Startile Wide SW: Hera Startile Corner NW + Skull 1 Lobby ES: Skull Map Room WS + Skull 2 West Lobby ES: Skull Small Hall WS + Skull 2 West Lobby NW: Skull X Room SW + Skull Big Chest N: Skull Pull Switch S + Skull Compass Room WS: Skull Left Drop ES + Skull Final Drop WS: Skull Spike Corner ES + Skull Left Drop ES: Skull Compass Room WS + Skull Map Room WS: Skull 1 Lobby ES + Skull Pot Circle WN: Skull Pull Switch EN + Skull Pull Switch EN: Skull Pot Circle WN + Skull Pull Switch S: Skull Big Chest N + Skull Small Hall WS: Skull 2 West Lobby ES + Skull Spike Corner ES: Skull Final Drop WS + Skull X Room SW: Skull 2 West Lobby NW + lobbies: {} +placements: + 1: + Swamp Palace - Big Chest: Hammer diff --git a/test/customizer/hmg/mearl_in_pod.yaml b/test/customizer/hmg/mearl_in_pod.yaml new file mode 100644 index 00000000..ec2d5b9a --- /dev/null +++ b/test/customizer/hmg/mearl_in_pod.yaml @@ -0,0 +1,34 @@ +meta: + players: 1 +settings: + 1: + accessibility: items + door_shuffle: vanilla + logic: hybridglitches + mode: open + shuffle: vanilla +doors: + 1: + doors: + Hera Beetles WS: Hera Startile Corner ES + Hera Startile Corner ES: Hera Beetles WS + Hera Startile Corner NW: Hera Startile Wide SW + Hera Startile Wide SW: Hera Startile Corner NW + Skull 1 Lobby ES: Skull Map Room WS + Skull 2 West Lobby ES: Skull Small Hall WS + Skull 2 West Lobby NW: Skull X Room SW + Skull Big Chest N: Skull Pull Switch S + Skull Compass Room WS: Skull Left Drop ES + Skull Final Drop WS: Skull Spike Corner ES + Skull Left Drop ES: Skull Compass Room WS + Skull Map Room WS: Skull 1 Lobby ES + Skull Pot Circle WN: Skull Pull Switch EN + Skull Pull Switch EN: Skull Pot Circle WN + Skull Pull Switch S: Skull Big Chest N + Skull Small Hall WS: Skull 2 West Lobby ES + Skull Spike Corner ES: Skull Final Drop WS + Skull X Room SW: Skull 2 West Lobby NW + lobbies: {} +placements: + 1: + Palace of Darkness - Shooter Room: Moon Pearl diff --git a/test/customizer/hmg/mirrorless_swamp.yaml b/test/customizer/hmg/mirrorless_swamp.yaml new file mode 100644 index 00000000..c8a109c3 --- /dev/null +++ b/test/customizer/hmg/mirrorless_swamp.yaml @@ -0,0 +1,42 @@ +meta: + players: 1 +settings: + 1: + accessibility: items + door_shuffle: vanilla + logic: hybridglitches + mode: open + shuffle: vanilla +start_inventory: + 1: + - Flippers + - Lamp + - Tempered Sword + - Boss Heart Container + - Boss Heart Container + - Boss Heart Container +doors: + 1: + doors: + Hera Beetles WS: Hera Startile Corner ES + Hera Startile Corner ES: Hera Beetles WS + Hera Startile Corner NW: Hera Startile Wide SW + Hera Startile Wide SW: Hera Startile Corner NW + Skull 1 Lobby ES: Skull Map Room WS + Skull 2 West Lobby ES: Skull Small Hall WS + Skull 2 West Lobby NW: Skull X Room SW + Skull Big Chest N: Skull Pull Switch S + Skull Compass Room WS: Skull Left Drop ES + Skull Final Drop WS: Skull Spike Corner ES + Skull Left Drop ES: Skull Compass Room WS + Skull Map Room WS: Skull 1 Lobby ES + Skull Pot Circle WN: Skull Pull Switch EN + Skull Pull Switch EN: Skull Pot Circle WN + Skull Pull Switch S: Skull Big Chest N + Skull Small Hall WS: Skull 2 West Lobby ES + Skull Spike Corner ES: Skull Final Drop WS + Skull X Room SW: Skull 2 West Lobby NW + lobbies: {} +placements: + 1: + Swamp Palace - Big Chest: Magic Mirror diff --git a/test/customizer/hmg/pod_as_connector.yaml b/test/customizer/hmg/pod_as_connector.yaml new file mode 100644 index 00000000..48f72d6e --- /dev/null +++ b/test/customizer/hmg/pod_as_connector.yaml @@ -0,0 +1,44 @@ +meta: + players: 1 +settings: + 1: + accessibility: items + door_shuffle: vanilla + logic: hybridglitches + mode: open + shuffle: crossed +doors: + 1: + doors: + Hera Beetles WS: Hera Startile Corner ES + Hera Startile Corner ES: Hera Beetles WS + Hera Startile Corner NW: Hera Startile Wide SW + Hera Startile Wide SW: Hera Startile Corner NW + Skull 1 Lobby ES: Skull Map Room WS + Skull 2 West Lobby ES: Skull Small Hall WS + Skull 2 West Lobby NW: Skull X Room SW + Skull Big Chest N: Skull Pull Switch S + Skull Compass Room WS: Skull Left Drop ES + Skull Final Drop WS: Skull Spike Corner ES + Skull Left Drop ES: Skull Compass Room WS + Skull Map Room WS: Skull 1 Lobby ES + Skull Pot Circle WN: Skull Pull Switch EN + Skull Pull Switch EN: Skull Pot Circle WN + Skull Pull Switch S: Skull Big Chest N + Skull Small Hall WS: Skull 2 West Lobby ES + Skull Spike Corner ES: Skull Final Drop WS + Skull X Room SW: Skull 2 West Lobby NW + lobbies: {} +entrances: + 1: + entrances: + Dark Lake Hylia Ledge Hint: Dark World Hammer Peg Cave + exits: + Links House: Chris Houlihan Room Exit + two-way: + Dark Lake Hylia Ledge Fairy: Palace of Darkness Exit + Lake Hylia Fortune Teller: Spectacle Rock Cave Exit + Links House: Links House Exit +placements: + 1: + Peg Cave: Moon Pearl diff --git a/test/customizer/hmg/swamp_as_connector.yaml b/test/customizer/hmg/swamp_as_connector.yaml new file mode 100644 index 00000000..ae343937 --- /dev/null +++ b/test/customizer/hmg/swamp_as_connector.yaml @@ -0,0 +1,55 @@ +meta: + players: 1 +settings: + 1: + accessibility: items + door_shuffle: vanilla + logic: hybridglitches + mode: open + shuffle: crossed +start_inventory: + 1: + - Hookshot + - Lamp + - Hammer + - Magic Mirror + - Tempered Sword + - Boss Heart Container + - Boss Heart Container + - Boss Heart Container +doors: + 1: + doors: + Hera Beetles WS: Hera Startile Corner ES + Hera Startile Corner ES: Hera Beetles WS + Hera Startile Corner NW: Hera Startile Wide SW + Hera Startile Wide SW: Hera Startile Corner NW + Skull 1 Lobby ES: Skull Map Room WS + Skull 2 West Lobby ES: Skull Small Hall WS + Skull 2 West Lobby NW: Skull X Room SW + Skull Big Chest N: Skull Pull Switch S + Skull Compass Room WS: Skull Left Drop ES + Skull Final Drop WS: Skull Spike Corner ES + Skull Left Drop ES: Skull Compass Room WS + Skull Map Room WS: Skull 1 Lobby ES + Skull Pot Circle WN: Skull Pull Switch EN + Skull Pull Switch EN: Skull Pot Circle WN + Skull Pull Switch S: Skull Big Chest N + Skull Small Hall WS: Skull 2 West Lobby ES + Skull Spike Corner ES: Skull Final Drop WS + Skull X Room SW: Skull 2 West Lobby NW + lobbies: {} +entrances: + 1: + entrances: + Dark Lake Hylia Ledge Hint: Dark World Hammer Peg Cave + exits: + Links House: Chris Houlihan Room Exit + two-way: + Dark Lake Hylia Ledge Fairy: Swamp Palace Exit + Lake Hylia Fortune Teller: Misery Mire Exit + Links House: Links House Exit +placements: + 1: + Peg Cave: Moon Pearl + diff --git a/test/customizer/hmg/swamp_small_in_swamp_back.yaml b/test/customizer/hmg/swamp_small_in_swamp_back.yaml new file mode 100644 index 00000000..b57cc9b6 --- /dev/null +++ b/test/customizer/hmg/swamp_small_in_swamp_back.yaml @@ -0,0 +1,44 @@ +meta: + players: 1 +settings: + 1: + accessibility: items + door_shuffle: vanilla + logic: hybridglitches + mode: open + shuffle: vanilla +start_inventory: + 1: + - Hookshot + - Lamp + - Hammer + - Magic Mirror + - Tempered Sword + - Boss Heart Container + - Boss Heart Container + - Boss Heart Container +doors: + 1: + doors: + Hera Beetles WS: Hera Startile Corner ES + Hera Startile Corner ES: Hera Beetles WS + Hera Startile Corner NW: Hera Startile Wide SW + Hera Startile Wide SW: Hera Startile Corner NW + Skull 1 Lobby ES: Skull Map Room WS + Skull 2 West Lobby ES: Skull Small Hall WS + Skull 2 West Lobby NW: Skull X Room SW + Skull Big Chest N: Skull Pull Switch S + Skull Compass Room WS: Skull Left Drop ES + Skull Final Drop WS: Skull Spike Corner ES + Skull Left Drop ES: Skull Compass Room WS + Skull Map Room WS: Skull 1 Lobby ES + Skull Pot Circle WN: Skull Pull Switch EN + Skull Pull Switch EN: Skull Pot Circle WN + Skull Pull Switch S: Skull Big Chest N + Skull Small Hall WS: Skull 2 West Lobby ES + Skull Spike Corner ES: Skull Final Drop WS + Skull X Room SW: Skull 2 West Lobby NW + lobbies: {} +# placements: +# 1: +# Swamp Palace - Entrance: Boss Heart Container diff --git a/test/customizer/test.yaml b/test/customizer/test.yaml new file mode 100644 index 00000000..86d9a2e9 --- /dev/null +++ b/test/customizer/test.yaml @@ -0,0 +1,61 @@ +meta: + seed: 394 +settings: + 1: +# mode: standard + +# boss_shuffle: random +# dropshuffle: underworld +# enemy_shuffle: shuffled + door_shuffle: partitioned + intensity: 3 + shuffle: lite + experimental: on + shopsanity: on + +# dungeon_counters: 'on' +#entrances: +# 1: +# entrances: +# Hyrule Castle Secret Entrance Drop: Lumberjack Tree (top) +# two-way: +# Hyrule Castle Entrance (South): Links House Exit +doors: + 1: +# lobbies: +# Hyrule Castle South: GT Lobby S + doors: + TR Eye Bridge SW: Ice Compass Room NE + TR Lazy Eyes SE: Mire Conveyor Barrier NW + Hera Basement Cage Up Stairs: Hera Lobby Down Stairs + Hera Lobby Key Stairs: + dest: Hera Boss Down Stairs + type: Key Door + Hera Tile Room Up Stairs: Hera 5F Down Stairs + + +#bosses: +# 1: +# Ganons Tower (middle): Trinexx + + +placements: + 1: + Tower of Hera - Basement Cage: Small Key (Tower of Hera) +# Link's House: Lamp + + +#placements: +# 1: +# 'Hera Basement Cage Enemy #4': Small Key (Palace of Darkness) +# 'Hera Basement Cage Enemy #9': Small Key (Tower of Hera) +# 'Hera Basement Cage Enemy #11': Fire Rod +# Tower of Hera - Basement Cage: Small Key (Turtle Rock) +#start_inventory: +# 1: +# - Pegasus Boots +# - Progressive Sword +# - Ocarina (Activated) +# - Hookshot + + diff --git a/test/inverted/TestInverted.py b/test/inverted/TestInverted.py index 75c945e6..cc3eb68a 100644 --- a/test/inverted/TestInverted.py +++ b/test/inverted/TestInverted.py @@ -2,6 +2,7 @@ from BaseClasses import World from DoorShuffle import link_doors from Doors import create_doors from Dungeons import create_dungeons, get_dungeon_item_pool +from OverworldShuffle import link_overworld from EntranceShuffle import link_entrances from ItemList import generate_itempool, difficulties from Items import ItemFactory @@ -23,6 +24,7 @@ class TestInverted(TestBase): create_doors(self.world, 1) create_rooms(self.world, 1) create_dungeons(self.world, 1) + link_overworld(self.world, 1) link_entrances(self.world, 1) link_doors(self.world, 1) generate_itempool(self.world, 1) diff --git a/test/inverted_owg/TestInvertedOWG.py b/test/inverted_owg/TestInvertedOWG.py index 2453d7ab..9c584c25 100644 --- a/test/inverted_owg/TestInvertedOWG.py +++ b/test/inverted_owg/TestInvertedOWG.py @@ -2,6 +2,7 @@ from BaseClasses import World from DoorShuffle import link_doors from Doors import create_doors from Dungeons import create_dungeons, get_dungeon_item_pool +from OverworldShuffle import link_overworld from EntranceShuffle import link_entrances from ItemList import generate_itempool, difficulties from Items import ItemFactory @@ -24,6 +25,7 @@ class TestInvertedOWG(TestBase): create_doors(self.world, 1) create_rooms(self.world, 1) create_dungeons(self.world, 1) + link_overworld(self.world, 1) create_owg_connections(self.world, 1) link_entrances(self.world, 1) link_doors(self.world, 1) diff --git a/test/owg/TestVanillaOWG.py b/test/owg/TestVanillaOWG.py index c5bd18d0..8c245125 100644 --- a/test/owg/TestVanillaOWG.py +++ b/test/owg/TestVanillaOWG.py @@ -2,6 +2,7 @@ from BaseClasses import World from DoorShuffle import link_doors from Doors import create_doors from Dungeons import create_dungeons, get_dungeon_item_pool +from OverworldShuffle import link_overworld from EntranceShuffle import link_entrances from ItemList import difficulties, generate_itempool from Items import ItemFactory @@ -24,6 +25,7 @@ class TestVanillaOWG(TestBase): create_doors(self.world, 1) create_rooms(self.world, 1) create_dungeons(self.world, 1) + link_overworld(self.world, 1) link_entrances(self.world, 1) link_doors(self.world, 1) create_owg_connections(self.world, 1) diff --git a/test/suite/hmg/entrance_bunny_pocket_sw.yaml b/test/suite/hmg/entrance_bunny_pocket_sw.yaml new file mode 100644 index 00000000..eba7743d --- /dev/null +++ b/test/suite/hmg/entrance_bunny_pocket_sw.yaml @@ -0,0 +1,34 @@ +meta: + players: 1 + +settings: + 1: + logic: hybridglitches + shuffle: crossed +start_inventory: + 1: + - Flippers + - Pegasus Boots + - Progressive Sword + - Hammer + - Progressive Glove + - Progressive Glove + - Fire Rod + - Book of Mudora + - Bottle + - Magic Mirror + - Lamp +advanced_placements: + 1: + - type: Verification + item: Moon Pearl + locations: + Pyramid Fairy - Left: True +entrances: + 1: + entrances: + Skull Woods Final Section: Pyramid Fairy + two-way: + Chicken House: Two Brothers House Exit (West) + Skull Woods Second Section Door (West): Two Brothers House Exit (East) + diff --git a/test/suite/hmg/fireless_ice.yaml b/test/suite/hmg/fireless_ice.yaml new file mode 100644 index 00000000..79b31f59 --- /dev/null +++ b/test/suite/hmg/fireless_ice.yaml @@ -0,0 +1,17 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches +start_inventory: + 1: + - Flippers + - Moon Pearl + - Progressive Sword + - Hammer + - Progressive Glove + - Progressive Glove +placements: + 1: + Ice Palace - Map Chest: Bombos + Ice Palace - Iced T Room: Fire Rod diff --git a/test/suite/hmg/flippers_locked_flippers.yaml b/test/suite/hmg/flippers_locked_flippers.yaml new file mode 100644 index 00000000..be71e07b --- /dev/null +++ b/test/suite/hmg/flippers_locked_flippers.yaml @@ -0,0 +1,20 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches +start_inventory: + 1: + - Pegasus Boots + - Moon Pearl + - Progressive Sword +advanced_placements: + 1: + - type: Verification + item: Flippers + locations: + Zora's Ledge: True + Hobo: True + Ice Palace - Boss: True + Swamp Palace - Entrance: False + diff --git a/test/suite/hmg/flippers_wraps.yaml b/test/suite/hmg/flippers_wraps.yaml new file mode 100644 index 00000000..3418b4ec --- /dev/null +++ b/test/suite/hmg/flippers_wraps.yaml @@ -0,0 +1,29 @@ +meta: + players: 1 +settings: + 1: + logic: owglitches +start_inventory: + 1: + - Pegasus Boots + - Moon Pearl + - Progressive Sword + - Flippers +placements: + 1: + Peg Cave: Magic Mirror +advanced_placements: + 1: + - type: Verification + item: Hammer + locations: + Link's House: True + Magic Bat: False +advanced_placements: + 1: + - type: Verification + item: Progressive Glove + locations: + Link's House: True + Ice Palace - Freezor Chest: False + diff --git a/test/suite/hmg/hera_from_mire.yaml b/test/suite/hmg/hera_from_mire.yaml new file mode 100644 index 00000000..3e7d0c49 --- /dev/null +++ b/test/suite/hmg/hera_from_mire.yaml @@ -0,0 +1,26 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches +start_inventory: + 1: + - Flippers + - Moon Pearl + - Progressive Sword + - Lamp + - Magic Mirror + - Ether + - Quake + - Bombos +advanced_placements: + 1: + - type: Verification + item: Big Key (Tower of Hera) + locations: + Tower of Hera - Big Key Chest: True + Tower of Hera - Basement Cage: True + Tower of Hera - Map Chest: True + Tower of Hera - Compass Chest: True + Tower of Hera - Big Chest: True + Tower of Hera - Boss: True \ No newline at end of file diff --git a/test/suite/hmg/inverted_inaccessible_desert.yaml b/test/suite/hmg/inverted_inaccessible_desert.yaml new file mode 100644 index 00000000..bb8ad8aa --- /dev/null +++ b/test/suite/hmg/inverted_inaccessible_desert.yaml @@ -0,0 +1,26 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches + mode: inverted + shuffle: crossed + +start_inventory: + 1: + - Pegasus Boots + - Progressive Sword + - Hammer + - Fire Rod +placements: + 1: + Desert Palace - Boss: Moon Pearl +entrances: + 1: + two-way: + Skull Woods Final Section: Desert Palace Exit (West) + Skull Woods Second Section Door (West): Desert Palace Exit (East) + Thieves Town: Thieves Town Exit + Hyrule Castle Entrance (East): Desert Palace Exit (South) + Hyrule Castle Entrance (West): Desert Palace Exit (North) + diff --git a/test/suite/hmg/inverted_moon_pearl_locs.yaml b/test/suite/hmg/inverted_moon_pearl_locs.yaml new file mode 100644 index 00000000..9ae1a9e1 --- /dev/null +++ b/test/suite/hmg/inverted_moon_pearl_locs.yaml @@ -0,0 +1,31 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches + mode: inverted +start_inventory: + 1: + - Flippers + - Progressive Sword + - Progressive Sword + - Book of Mudora + - Lamp + - Magic Mirror + - Ether + - Quake + - Bombos +advanced_placements: + 1: + - type: Verification + item: Moon Pearl + locations: + Tower of Hera - Big Chest: True + Desert Palace - Big Chest: True + Eastern Palace - Big Chest: True + Bombos Tablet: True + Cave 45: True + + + + diff --git a/test/suite/hmg/moon_pearl_locs.yaml b/test/suite/hmg/moon_pearl_locs.yaml new file mode 100644 index 00000000..a18d53d1 --- /dev/null +++ b/test/suite/hmg/moon_pearl_locs.yaml @@ -0,0 +1,48 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches +start_inventory: + 1: + - Pegasus Boots + - Flippers + - Fire Rod + - Book of Mudora + - Progressive Sword + - Progressive Sword + - Lamp + - Magic Mirror + - Ether + - Quake + - Bombos +advanced_placements: + 1: + - type: Verification + item: Moon Pearl + locations: + Skull Woods - Compass Chest: True + Skull Woods - Bridge Room: True + Palace of Darkness - Shooter Room: True + Palace of Darkness - The Arena - Bridge: True + Palace of Darkness - Stalfos Basement: True + Palace of Darkness - Big Key Chest: True + Palace of Darkness - The Arena - Ledge: True + Palace of Darkness - Map Chest: True + Palace of Darkness - Compass Chest: True + Palace of Darkness - Dark Basement - Left: True + Palace of Darkness - Dark Basement - Right: True + Palace of Darkness - Dark Maze - Top: True + Palace of Darkness - Dark Maze - Bottom: True + Palace of Darkness - Big Chest: True + Palace of Darkness - Harmless Hellway: True + Palace of Darkness - Boss: True + Bombos Tablet: True + C-Shaped House: True + Pyramid Fairy - Left: True + Swamp Palace - Entrance: False + Thieves' Town - Map Chest: False + + + + diff --git a/test/suite/hmg/pearlless_sw.yaml b/test/suite/hmg/pearlless_sw.yaml new file mode 100644 index 00000000..c26ce1d9 --- /dev/null +++ b/test/suite/hmg/pearlless_sw.yaml @@ -0,0 +1,25 @@ +meta: + players: 1 + +settings: + 1: + logic: hybridglitches +start_inventory: + 1: + - Flippers + - Pegasus Boots + - Progressive Sword + - Hammer + - Progressive Glove + - Progressive Glove + - Fire Rod + - Book of Mudora + - Bottle + - Magic Mirror + - Lamp +advanced_placements: + 1: + - type: Verification + item: Moon Pearl + locations: + Skull Woods - Bridge Room: True diff --git a/test/suite/hmg/swamp_from_mire.yaml b/test/suite/hmg/swamp_from_mire.yaml new file mode 100644 index 00000000..f3873909 --- /dev/null +++ b/test/suite/hmg/swamp_from_mire.yaml @@ -0,0 +1,43 @@ +meta: + players: 1 +settings: + 1: + logic: hybridglitches +start_inventory: + 1: + - Flippers + - Moon Pearl + - Progressive Sword + - Lamp + - Magic Mirror + - Ether + - Quake + - Bombos +advanced_placements: + 1: + - type: Verification + item: Small Key (Swamp Palace) + locations: + Swamp Palace - Entrance: True + Swamp Palace - Map Chest: True + Swamp Palace - Big Chest: True + Swamp Palace - Compass Chest: True + Swamp Palace - West Chest: True + Swamp Palace - Big Key Chest: True + Swamp Palace - Flooded Room - Left: True + Swamp Palace - Flooded Room - Right: True + Swamp Palace - Waterfall Room: True + Swamp Palace - Boss: True + - type: Verification + item: Big Key (Swamp Palace) + locations: + Swamp Palace - Entrance: True + Swamp Palace - Map Chest: True + Swamp Palace - Big Chest: True + Swamp Palace - Compass Chest: True + Swamp Palace - West Chest: True + Swamp Palace - Big Key Chest: True + Swamp Palace - Flooded Room - Left: True + Swamp Palace - Flooded Room - Right: True + Swamp Palace - Waterfall Room: True + Swamp Palace - Boss: True \ No newline at end of file diff --git a/test/vanilla/TestVanilla.py b/test/vanilla/TestVanilla.py index 80bbc7b9..f1bd23f2 100644 --- a/test/vanilla/TestVanilla.py +++ b/test/vanilla/TestVanilla.py @@ -2,6 +2,7 @@ from BaseClasses import World from DoorShuffle import link_doors from Doors import create_doors from Dungeons import create_dungeons, get_dungeon_item_pool +from OverworldShuffle import link_overworld from EntranceShuffle import link_entrances from ItemList import difficulties, generate_itempool from Items import ItemFactory @@ -23,6 +24,7 @@ class TestVanilla(TestBase): create_doors(self.world, 1) create_rooms(self.world, 1) create_dungeons(self.world, 1) + link_overworld(self.world, 1) link_entrances(self.world, 1) link_doors(self.world, 1) generate_itempool(self.world, 1)