diff --git a/BaseClasses.py b/BaseClasses.py index 404428a1..0c014816 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 Utils import int16_as_bytes from Tables import normal_offset_table, spiral_offset_table, multiply_lookup, divisor_lookup @@ -60,8 +59,6 @@ class World(object): self._location_cache = {} self.required_locations = [] self.shuffle_bonk_drops = {} - self.light_world_light_cone = False - self.dark_world_light_cone = False self.clock_mode = 'none' self.rupoor_cost = 10 self.lock_aga_door_in_escape = False @@ -106,11 +103,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('owswaps', [[],[],[]]) @@ -148,6 +147,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) @@ -180,6 +180,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): @@ -282,6 +283,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): door = self.check_for_door(doorname, player) if door is None: @@ -461,7 +465,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) @@ -475,7 +479,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) @@ -842,7 +846,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 @@ -865,7 +869,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] @@ -921,8 +925,8 @@ class CollectionState(object): 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) @@ -1046,8 +1050,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: @@ -1094,7 +1098,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] @@ -1107,7 +1110,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 @@ -1225,16 +1228,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 @@ -1285,16 +1288,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) @@ -1342,9 +1345,8 @@ class CollectionState(object): 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('Cape', player) or - (self.has('Cane of Byrna', player) and self.world.difficulty_adjustments[player] not in ['hard', 'expert'])) + 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): return self.has_Pearl(player) or not region.can_cause_bunny(player) @@ -1526,13 +1528,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 @@ -1558,7 +1567,7 @@ 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 @@ -1624,6 +1633,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 @@ -2049,7 +2059,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 @@ -2501,17 +2511,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 @@ -2520,7 +2530,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 @@ -2540,7 +2550,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] @@ -2599,20 +2609,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 @@ -2624,11 +2636,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): @@ -2637,7 +2652,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): @@ -2659,6 +2674,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 can_cause_bunny(self, player): @@ -2761,12 +2778,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 @@ -2791,7 +2810,7 @@ class Shop(object): config = self.item_count from EntranceShuffle import door_addresses 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 @@ -2799,7 +2818,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: @@ -2947,6 +2966,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, @@ -3077,7 +3097,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 @@ -3142,7 +3162,7 @@ class Spoiler(object): outfile.write('Bonk Drops:'.ljust(line_width) + '%s\n' % yn(self.metadata['bonk_drops'][player])) outfile.write('Pottery Mode:'.ljust(line_width) + '%s\n' % self.metadata['pottery'][player]) outfile.write('Pot Shuffle (Legacy):'.ljust(line_width) + '%s\n' % yn(self.metadata['potshuffle'][player])) - outfile.write('Enemy Drop Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['dropshuffle'][player])) + outfile.write('Enemy Drop Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['dropshuffle'][player]) outfile.write('Take Any Caves:'.ljust(line_width) + '%s\n' % self.metadata['take_any'][player]) outfile.write('\n') outfile.write('Overworld Layout Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['ow_shuffle'][player]) @@ -3182,6 +3202,8 @@ class Spoiler(object): outfile.write('\n') outfile.write('Boss Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['boss_shuffle'][player]) outfile.write('Enemy Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['enemy_shuffle'][player]) + if self.metadata['enemy_shuffle'][player] != 'none': + outfile.write('Enemy Logic:'.ljust(line_width) + '%s\n' % self.metadata['any_enemy_logic'][player]) outfile.write('Enemy Health:'.ljust(line_width) + '%s\n' % self.metadata['enemy_health'][player]) outfile.write('Enemy Damage:'.ljust(line_width) + '%s\n' % self.metadata['enemy_damage'][player]) outfile.write('\n') @@ -3191,7 +3213,7 @@ class Spoiler(object): 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: @@ -3211,7 +3233,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 @@ -3285,16 +3307,16 @@ class Spoiler(object): if self.entrances: # entrances: To/From overworld; Checking w/ & w/out "Exit" and translating accordingly outfile.write('\n\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') @@ -3306,8 +3328,8 @@ 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 outfile.write('\n\nLocations:\n\n') @@ -3316,7 +3338,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': @@ -3324,12 +3346,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 @@ -3354,10 +3392,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)) @@ -3388,6 +3426,7 @@ dungeon_keys = { 'Universal': 'Small Key (Universal)' } + class PotItem(FastEnum): Nothing = 0x0 OneRupee = 0x1 @@ -3469,7 +3508,7 @@ er_mode = {"vanilla": 0, "simple": 1, "restricted": 2, "full": 3, "crossed": 4, # byte 1: LLLW WSS? (logic, mode, sword) 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, @@ -3484,6 +3523,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} @@ -3553,7 +3593,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) @@ -3584,7 +3624,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 @@ -3618,7 +3658,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 a4886be1..f60489a2 100644 --- a/Bosses.py +++ b/Bosses.py @@ -2,6 +2,7 @@ import logging import RaceRandom as random from BaseClasses import Boss, FillError +from source.enemizer.Bossmizer import boss_adjust def BossFactory(boss, player, on_ice=False): @@ -309,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): diff --git a/CLI.py b/CLI.py index b54c667a..91c6a209 100644 --- a/CLI.py +++ b/CLI.py @@ -110,7 +110,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': @@ -145,7 +145,7 @@ def parse_cli(argv, no_defaults=False): 'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle', 'mixed_travel', 'standardize_palettes', 'code', 'reduce_flashing', 'shuffle_sfx', 'shuffle_sfxinstruments', 'shuffle_songinstruments', 'msu_resume', 'collection_rate', 'colorizepots', 'decoupledoors', 'door_type_mode', - 'bonk_drops', 'trap_door_mode', 'key_logic_algorithm', 'door_self_loops', 'aga_randomness']: + 'bonk_drops', '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}) @@ -211,11 +211,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, @@ -366,9 +366,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 3ae1c709..905d65af 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -216,6 +216,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] = {} @@ -263,7 +264,7 @@ def vanilla_key_logic(world, player): log_key_logic(builder.name, key_layout.key_logic) # special adjustments for vanilla if world.keyshuffle[player] != 'universal': - if world.mode[player] != 'standard' and not world.dropshuffle[player]: + 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: @@ -903,10 +904,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 @@ -1314,10 +1315,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 @@ -1403,7 +1404,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 @@ -2437,20 +2438,25 @@ def change_door_to_trap(d, world, player): if d.entrance.connected_region is not None and d.blocked: d.entrance.connected_region.entrances.remove(d.entrance) d.entrance.connected_region = None + if d.dependents: + for dep in d.dependents: + if dep.entrance.connected_region is not None: + dep.entrance.connected_region.remove(dep.entrance) + dep.entrance.connected_region = None 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', + 'Eastern Lobby NE', 'Ice Cross Bottom SE', 'Ice Cross Right ES', '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', } @@ -3594,6 +3600,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 @@ -3666,8 +3673,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'), @@ -3755,11 +3764,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'), @@ -4130,6 +4142,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 8ba20e40..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), @@ -1505,8 +1512,8 @@ def create_doors(world, player): 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/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/EntranceShuffle.py b/EntranceShuffle.py index 9bae0b11..516b0aad 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -2155,20 +2155,20 @@ Exit_Pool_Base = ['Links House Exit', # these are connections that cannot be shuffled and always exist. # They link together underworld regions -mandatory_connections = [('Lost Woods Hideout (top to bottom)', 'Lost Woods Hideout (bottom)'), +mandatory_connections = [ + ('Lost Woods Hideout (top to bottom)', 'Lost Woods Hideout (bottom)'), ('Lumberjack Tree (top to bottom)', 'Lumberjack Tree (bottom)'), - ('Kakariko Well (top to bottom)', 'Kakariko Well (bottom)'), - ('Kakariko Well (top to back)', 'Kakariko Well (back)'), - ('Blinds Hideout N', 'Blinds Hideout (Top)'), - ('Bat Cave Door', 'Bat Cave (left)'), - ('Sewer Drop', 'Sewers Rat Path'), - ('Old Man Cave Dropdown', 'Old Man Cave'), - ('Old Man House Front to Back', 'Old Man House Back'), - ('Old Man House Back to Front', 'Old Man House'), - ('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'), - ('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (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 (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)'), ('Paradox Shop', 'Paradox Shop'), ('Paradox Cave Push Block Reverse', 'Paradox Cave Chest Area'), @@ -2179,89 +2179,102 @@ mandatory_connections = [('Lost Woods Hideout (top to bottom)', 'Lost Woods Hide ('Fairy Ascension Cave Climb', 'Fairy Ascension Cave (Top)'), ('Fairy Ascension Cave Pots', 'Fairy Ascension Cave (Bottom)'), ('Fairy Ascension Cave Drop', 'Fairy Ascension Cave (Drop)'), - ('Missing Smith', 'Missing Smith'), - ('Bumper Cave Bottom to Top', 'Bumper Cave (top)'), - ('Bumper Cave Top To Bottom', 'Bumper Cave (bottom)'), - ('Superbunny Cave Climb', 'Superbunny Cave (Top)'), + ('Sewer Drop', 'Sewers Rat Path'), + ('Kakariko Well (top to bottom)', 'Kakariko Well (bottom)'), + ('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)'), + ('Missing Smith', 'Missing Smith'), ('Ganon Drop', 'Bottom of Pyramid') ] # non-shuffled entrance links -default_connections = [('Bonk Fairy (Light)', 'Bonk Fairy (Light)'), - ('Lake Hylia Fairy', 'Lake Hylia Healer Fairy'), - ('Lake Hylia Fortune Teller', 'Lake Hylia Fortune Teller'), - ('Lost Woods Gamble', 'Lost Woods Gamble'), +default_connections = [('Lost Woods Gamble', 'Lost Woods Gamble'), ('Fortune Teller (Light)', 'Fortune Teller (Light)'), ('Bush Covered House', 'Bush Covered House'), - ('Long Fairy Cave', 'Long Fairy Cave'), # near East Light World Teleporter - ('Good Bee Cave', 'Good Bee Cave'), ('Kakariko Gamble Game', 'Kakariko Gamble Game'), + ('Bonk Fairy (Light)', 'Bonk Fairy (Light)'), + ('Lake Hylia Fairy', 'Lake Hylia Healer Fairy'), + ('Long Fairy Cave', 'Long Fairy Cave'), + ('Lake Hylia Fortune Teller', 'Lake Hylia Fortune Teller'), + ('Good Bee Cave', 'Good Bee Cave'), - ('East Dark World Hint', 'East Dark World Hint'), - ('Dark Lake Hylia Fairy', 'Dark Lake Hylia Healer Fairy'), - ('Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Hint'), - ('Dark Sanctuary Hint', 'Dark Sanctuary Hint'), ('Fortune Teller (Dark)', 'Fortune Teller (Dark)'), + ('Dark Sanctuary Hint', 'Dark Sanctuary Hint'), ('Archery Game', 'Archery Game'), - ('Mire Fairy', 'Mire Healer Fairy') + ('Dark Lake Hylia Fairy', 'Dark Lake Hylia Healer Fairy'), + ('East Dark World Hint', 'East Dark World Hint'), + ('Mire Fairy', 'Mire Healer Fairy'), + ('Dark Lake Hylia Ledge Hint', 'Dark Lake Hylia Ledge Hint') ] default_takeany_connections = [('Light Hype Fairy', 'Light Hype Fairy'), ('Desert Fairy', 'Desert Healer Fairy'), - ('Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Healer Fairy'), + ('Dark Death Mountain Fairy', 'Dark Death Mountain Healer Fairy'), ('Bonk Fairy (Dark)', 'Bonk Fairy (Dark)'), - ('Dark Death Mountain Fairy', 'Dark Death Mountain Healer Fairy') + ('Dark Lake Hylia Ledge Fairy', 'Dark Lake Hylia Ledge Healer Fairy') ] default_pot_connections = [('Lumberjack House', 'Lumberjack House'), + ('Hookshot Fairy', 'Hookshot Fairy'), ('Snitch Lady (East)', 'Snitch Lady (East)'), ('Snitch Lady (West)', 'Snitch Lady (West)'), - ('Tavern (Front)', 'Tavern (Front)'), ('Light World Bomb Hut', 'Light World Bomb Hut'), + ('Tavern (Front)', 'Tavern (Front)'), ('20 Rupee Cave', '20 Rupee Cave'), ('50 Rupee Cave', '50 Rupee Cave'), - ('Hookshot Fairy', 'Hookshot Fairy'), ('Palace of Darkness Hint', 'Palace of Darkness Hint'), - ('Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Spike Cave'), - ('Mire Hint', 'Mire Hint') + ('Mire Hint', 'Mire Hint'), + ('Dark Lake Hylia Ledge Spike Cave', 'Dark Lake Hylia Ledge Spike Cave') ] -default_connector_connections = [('Old Man Cave (West)', 'Old Man Cave Exit (West)'), - ('Old Man Cave (East)', 'Old Man Cave Exit (East)'), - ('Old Man House (Bottom)', 'Old Man House Exit (Bottom)'), - ('Old Man House (Top)', 'Old Man House Exit (Top)'), +default_connector_connections = [('Death Mountain Return Cave (West)', 'Death Mountain Return Cave Exit (West)'), ('Death Mountain Return Cave (East)', 'Death Mountain Return Cave Exit (East)'), - ('Death Mountain Return Cave (West)', 'Death Mountain Return Cave Exit (West)'), ('Spectacle Rock Cave Peak', 'Spectacle Rock Cave Exit (Peak)'), ('Spectacle Rock Cave (Bottom)', 'Spectacle Rock Cave Exit'), ('Spectacle Rock Cave', 'Spectacle Rock Cave Exit (Top)'), + ('Old Man Cave (East)', 'Old Man Cave Exit (East)'), + ('Old Man Cave (West)', 'Old Man Cave Exit (West)'), + ('Old Man House (Bottom)', 'Old Man House Exit (Bottom)'), + ('Old Man House (Top)', 'Old Man House Exit (Top)'), ('Spiral Cave', 'Spiral Cave Exit (Top)'), ('Spiral Cave (Bottom)', 'Spiral Cave Exit'), - ('Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave Exit (Bottom)'), ('Fairy Ascension Cave (Top)', 'Fairy Ascension Cave Exit (Top)'), + ('Fairy Ascension Cave (Bottom)', 'Fairy Ascension Cave Exit (Bottom)'), ('Paradox Cave (Bottom)', 'Paradox Cave Exit (Bottom)'), ('Paradox Cave (Middle)', 'Paradox Cave Exit (Middle)'), ('Paradox Cave (Top)', 'Paradox Cave Exit (Top)'), - ('Elder House (East)', 'Elder House Exit (East)'), ('Elder House (West)', 'Elder House Exit (West)'), - ('Two Brothers House (East)', 'Two Brothers House Exit (East)'), + ('Elder House (East)', 'Elder House Exit (East)'), ('Two Brothers House (West)', 'Two Brothers House Exit (West)'), - ('Bumper Cave (Top)', 'Bumper Cave Exit (Top)'), - ('Bumper Cave (Bottom)', 'Bumper Cave Exit (Bottom)'), + ('Two Brothers House (East)', 'Two Brothers House Exit (East)'), + ('Hookshot Cave Back Entrance', 'Hookshot Cave Back Exit'), + ('Hookshot Cave', 'Hookshot Cave Front Exit'), ('Superbunny Cave (Top)', 'Superbunny Cave Exit (Top)'), ('Superbunny Cave (Bottom)', 'Superbunny Cave Exit (Bottom)'), - ('Hookshot Cave', 'Hookshot Cave Front Exit'), - ('Hookshot Cave Back Entrance', 'Hookshot Cave Back Exit') + ('Bumper Cave (Bottom)', 'Bumper Cave Exit (Bottom)'), + ('Bumper Cave (Top)', 'Bumper Cave Exit (Top)') ] -default_item_connections = [('Links House', 'Links House Exit'), - ('Mimic Cave', 'Mimic Cave'), +default_item_connections = [('Mimic Cave', 'Mimic Cave'), ('Waterfall of Wishing', 'Waterfall of Wishing'), ('Bonk Rock Cave', 'Bonk Rock Cave'), ('Graveyard Cave', 'Graveyard Cave'), @@ -2273,6 +2286,7 @@ default_item_connections = [('Links House', 'Links House Exit'), ('Sahasrahlas Hut', 'Sahasrahlas Hut'), ('Blacksmiths Hut', 'Blacksmiths Hut'), ('Library', 'Library'), + ('Links House', 'Links House Exit'), ('Checkerboard Cave', 'Checkerboard Cave'), ('Aginahs Cave', 'Aginahs Cave'), ('Cave 45', 'Cave 45'), diff --git a/Fill.py b/Fill.py index 6bb233b3..b7e4b7cf 100644 --- a/Fill.py +++ b/Fill.py @@ -10,7 +10,7 @@ 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): @@ -284,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: @@ -386,28 +387,28 @@ 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) + # handle special case fast fill + locations_used = False + location_item_pool = collections.defaultdict(list) # guarantee one big magic in a bonk location for player in range(1, world.players + 1): if world.shuffle_bonk_drops[player]: for item in world.itempool: if item.name in ['Big Magic'] and item.player == player: - pot_item_pool[player].append(item) + location_item_pool[player].append(item) break from Regions import bonk_prize_table - for player, magic_pool in pot_item_pool.items(): + for player, magic_pool in location_item_pool.items(): if len(magic_pool) > 0: world.itempool.remove(magic_pool[0]) - pot_locations = [location for location in fill_locations if location.player == player + bonk_locations = [location for location in fill_locations if location.player == player and location.name in [n for n, (_, _, aga, _, _, _) in bonk_prize_table.items() if not aga]] - pot_locations = filter_pot_locations(pot_locations, world) - fast_fill_helper(world, magic_pool, pot_locations) - pots_used = True + bonk_locations = filter_special_locations(bonk_locations, world, lambda l: l.name == 'Kakariko Portal Tree') + fast_fill_helper(world, magic_pool, bonk_locations) + locations_used = True - if pots_used: + if locations_used: fill_locations = world.get_unfilled_locations() random.shuffle(fill_locations) @@ -498,6 +499,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: @@ -507,7 +509,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): @@ -537,36 +539,26 @@ 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) # convert Arrows 5 and Nothing when necessary - if (loc.item.name in {'Nothing'} + 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) - if (loc.item.name in {'Arrows (5)'} - 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 in [LocationType.Pot, LocationType.Bonk] 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): @@ -601,7 +593,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) diff --git a/Gui.py b/Gui.py index 899935ea..5155091d 100755 --- a/Gui.py +++ b/Gui.py @@ -46,7 +46,7 @@ def save_settings(gui, args, filename): output_args = {} settings = ["create_rom", "suppress_rom", "bps", "create_spoiler", "suppress_spoiler", "calc_playthrough", "skip_playthrough", "print_custom_yaml", - "settingsonload", "rom", "enemizercli", "outputpath"] + "settingsonload", "rom", "outputpath"] if filename == "settings.json": for s in settings: output_args[s] = args[s] diff --git a/InitialSram.py b/InitialSram.py index b3a969e9..f3f6a5d8 100644 --- a/InitialSram.py +++ b/InitialSram.py @@ -56,18 +56,17 @@ class InitialSram: def pre_set_overworld_flag(self, owid, bitmask): self._or_value(OVERWORLD_DATA+owid, bitmask) + 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 @@ -218,8 +217,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 e8e479cc..8d9fa046 100644 --- a/ItemList.py +++ b/ItemList.py @@ -11,6 +11,7 @@ from PotShuffle import vanilla_pots from Tables import bonk_prize_lookup 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 @@ -199,8 +200,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('Hyrule Castle Courtyard',player) - + region = world.get_region('Hyrule Castle Courtyard', player) loc = Location(player, "Murahdahla", parent=region) region.locations.append(loc) world.dynamic_locations.append(loc) @@ -468,7 +468,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 @@ -482,6 +482,9 @@ def generate_itempool(world, player): create_dynamic_bonkdrop_locations(world, player) add_bonkdrop_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) @@ -513,6 +516,8 @@ 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)', @@ -815,6 +820,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): @@ -1369,20 +1375,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', '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)') + 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_total, treasure_hunt_icon, lamps_needed_for_dark_rooms) diff --git a/Items.py b/Items.py index 1b7a041d..a637983f 100644 --- a/Items.py +++ b/Items.py @@ -1,5 +1,3 @@ -import logging - from BaseClasses import Item @@ -62,28 +60,28 @@ 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, 0x32, 0x60, 0x00, 0x69, 0x38, 0x09], 999, None, None, None, None, None, None, None), + 'Blue Pendant': (True, False, 'Crystal', [0x01, 0x34, 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'), 'Arrow Upgrade (+5)': (False, False, None, 0x53, 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'), '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'), + 'Arrows (5)': (False, False, None, 0xD5, 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, 0xB4, 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, 0xB3, 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,15 +168,15 @@ 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'), - 'Good Bee': (False, False, None, 0xB5, 10, 'I will sting your foes a lot', 'and the cold buddy', 'the beekeeper kid', 'cold insect for sale', 'shroom pollenation', 'bottle boy has cold bee again', 'a good bee'), + 'Good Bee': (False, False, None, 0xD6, 10, 'I will sting your foes a lot', 'and the cold buddy', 'the beekeeper kid', 'cold insect for sale', 'shroom pollenation', 'bottle boy has cold bee again', 'a good 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'), - 'Apples': (False, False, None, 0xB1, 30, 'Just a few pieces of fruit!', 'and the juicy fruit', 'the fruity kid', 'the fruit stand', 'expired fruit', 'bottle boy has fruit again', 'an apple hoard'), - 'Fairy': (False, False, None, 0xB2, 50, 'Just a pixie!', 'and the pixie', 'the pixie kid', 'pixie for sale', 'pixie fungus', 'bottle boy has pixie again', 'a pixie'), + 'Apples': (False, False, None, 0xD1, 30, 'Just a few pieces of fruit!', 'and the juicy fruit', 'the fruity kid', 'the fruit stand', 'expired fruit', 'bottle boy has fruit again', 'an apple hoard'), + '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 6f0cce5d..a5ca9cad 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) diff --git a/Main.py b/Main.py index ae47b522..8a9ecd47 100644 --- a/Main.py +++ b/Main.py @@ -18,13 +18,13 @@ from Regions import create_regions, create_shops, mark_light_dark_world_regions, from OWEdges import create_owedges from OverworldShuffle import link_overworld, update_world_regions, 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, set_prize_drops from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items, create_farm_locations @@ -36,8 +36,11 @@ from source.item.FillUtil import create_item_pool_config, massage_item_pool, dis 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.6' version_branch = '-u' __version__ = f'{version_number}{version_branch}' @@ -170,10 +173,13 @@ def main(args, seed=None, fish=None): 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 any(world.potshuffle.values()): logger.info(world.fish.translate("cli", "cli", "shuffling.pots")) @@ -287,7 +293,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_info(world) @@ -300,34 +306,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) @@ -380,6 +365,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")) @@ -403,7 +390,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) @@ -483,6 +469,7 @@ def init_world(args, fish): 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' 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() @@ -525,10 +512,11 @@ def set_starting_inventory(world, args): 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) def copy_world(world): @@ -548,8 +536,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() @@ -565,6 +551,7 @@ 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() @@ -577,10 +564,12 @@ def copy_world(world): ret.open_pyramid = world.open_pyramid.copy() ret.shufflelinks = world.shufflelinks.copy() ret.shuffle_ganon = world.shuffle_ganon.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() @@ -601,6 +590,8 @@ def copy_world(world): ret.prizes = world.prizes.copy() ret.restrict_boss_items = world.restrict_boss_items.copy() ret.inaccessible_regions = world.inaccessible_regions.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) @@ -740,8 +731,6 @@ def copy_world_premature(world, player): 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() @@ -756,6 +745,8 @@ def copy_world_premature(world, player): ret.keyshuffle = world.keyshuffle.copy() 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() @@ -768,10 +759,12 @@ def copy_world_premature(world, player): ret.open_pyramid = world.open_pyramid.copy() ret.shufflelinks = world.shufflelinks.copy() ret.shuffle_ganon = world.shuffle_ganon.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() @@ -791,6 +784,9 @@ def copy_world_premature(world, player): ret.owflutespots = world.owflutespots.copy() ret.prizes = world.prizes.copy() ret.restrict_boss_items = world.restrict_boss_items.copy() + ret.inaccessible_regions = world.inaccessible_regions.copy() + ret.damage_table = world.damage_table + ret.data_tables = world.data_tables # can be changed... ret.key_logic = world.key_logic.copy() ret.is_copied_world = True diff --git a/MultiClient.py b/MultiClient.py index 78fd0f5f..3723599f 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 @@ -836,9 +840,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: @@ -849,13 +855,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: @@ -869,6 +868,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): @@ -999,6 +1017,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 @@ -1015,6 +1034,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 27dc1a92..e937ce1b 100644 --- a/Mystery.py +++ b/Mystery.py @@ -34,7 +34,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): @@ -78,8 +77,6 @@ def main(): if args.rom: erargs.rom = args.rom - if args.enemizercli: - erargs.enemizercli = args.enemizercli mw_settings = {'algorithm': False} diff --git a/Plando.py b/Plando.py index 77e1d77c..2b11bed7 100755 --- a/Plando.py +++ b/Plando.py @@ -170,12 +170,6 @@ def prefill_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 76d5733c..bd970182 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]))], @@ -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/RELEASENOTES.md b/RELEASENOTES.md index cad049a6..a722fbb4 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 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. @@ -107,20 +139,156 @@ These are now independent of retro mode and have three options: None, Random, an * Dark Lake Hylia Ledge Healer Fairy (aka Shopping Mall Bomb) * Bonk Fairy (Dark) -# Bug Fixes and Notes +# Patch Notes +* 1.4.1.6u + * Difficulty: Fixed some issues around item caps not being respected + * Enemezier: Tutorial guards remove from South Kakariko + * Map: Pendant colors fixed + * Minor rom code cleanup + * Enemizer: Hovers added to problematic pool near pits. Some other bans +* 1.4.1.5u + * Major Fix: Problem with Ganon's Room sprites + * HMG: Remove extra Swamp Smalls in the pool due to pottery settings + * Enemizer: Couple enemy bans +* 1.4.1.4u + * Logic: Fixed logic bugs surrounding dynammic doors missing logic from big keys and other door types + * Inverted: Castle warp should not appear after defeating Aga 1 + * Enemzier: Fixed a crash with cached sprites Zora/Swamola +* 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 - 1.2.0.23u + * 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 * 1.2.0.22u diff --git a/Regions.py b/Regions.py index f9152e08..db9ad299 100644 --- a/Regions.py +++ b/Regions.py @@ -3,6 +3,8 @@ from Items import ItemFactory 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 += [ @@ -244,21 +246,23 @@ def create_regions(world, player): 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)']), create_cave_region(player, 'Lost Woods Hideout (bottom)', 'a drop\'s exit', None, ['Lost Woods Hideout Exit']), 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', ['Lost Old Man'], ['Old Man Cave Exit (East)']), + create_cave_region(player, 'Old Man Cave (West)', 'a connector', ['Lost 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']), @@ -308,7 +312,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'), @@ -319,11 +324,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'), @@ -337,7 +344,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']), @@ -357,7 +365,7 @@ 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', ['Big Bomb'], ['Big Bomb Shop Exit']), create_cave_region(player, 'Dark Lake Hylia Healer Fairy', 'a fairy fountain'), create_cave_region(player, 'East Dark World Hint', 'a storyteller'), @@ -578,8 +586,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']), @@ -589,6 +598,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']), @@ -743,7 +753,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']), @@ -755,7 +766,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']), @@ -1163,7 +1175,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] @@ -1171,6 +1183,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) @@ -1178,13 +1194,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 @@ -1202,7 +1217,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) @@ -1215,6 +1230,7 @@ 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', 'Frog', 'Missing Smith', 'Dark Blacksmith Ruins', 'Middle Aged Man', 'Floodgate', 'Trench 1 Switch', 'Trench 2 Switch', 'Swamp Drain', 'Turtle Medallion Pad', @@ -1312,7 +1328,7 @@ bonk_prize_table = { 'Dark Tree Line Tree 3': (0x26, 0x08, False, '', 'Dark Tree Line Area', 'in a tree'), 'Dark Tree Line Tree 4': (0x27, 0x04, False, '', 'Dark Tree Line Area', 'in a tree'), 'Hype Cave Statue': (0x28, 0x10, False, '', 'Hype Cave Area', 'encased in stone'), - 'Cold Fairy Statue': (0x29, 0x02, False, '', 'Good Bee Cave', 'encased in stone') + 'Cold Fairy Statue': (0x29, 0x02, False, '', 'Good Bee Cave (back)', 'encased in stone') } bonk_table_by_location_id = {0x2ABB00+(data[0]*6)+3: name for name, data in bonk_prize_table.items()} @@ -1388,222 +1404,222 @@ flooded_keys_reverse = { } -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'), @@ -1670,7 +1686,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_id_to_name.update(bonk_table_by_location_id) diff --git a/Rom.py b/Rom.py index aeb6d5e6..4741f457 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 @@ -34,11 +36,14 @@ from InitialSram import InitialSram from source.classes.SFX import randomize_sfx, randomize_sfxinstruments, randomize_songinstruments 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 = '3f08ebba5a2c79b373dbd0c9151ade9b' +RANDOMIZERBASEHASH = '184ebc6920dd7b6ba985827017bfd9c0' class JsonRom(object): @@ -219,182 +224,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(): @@ -589,7 +418,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 @@ -620,18 +449,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 elif location.type == LocationType.Bonk: address = snes_to_pc(location.address) @@ -799,14 +617,6 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): owFlags |= 0x0200 - # setting spriteID to D9, a placeholder sprite we use to inform ROM to spawn a dynamic item - #for address in bonk_addresses: - for address in [b for b in bonk_addresses if b != 0x4D0AE]: # temp fix for screen 1A murahdahla sprite replacement - rom.write_byte(address, 0xD9) - # temporary fix for screen 1A - rom.write_byte(snes_to_pc(0x09AE32), 0xD9) - rom.write_byte(snes_to_pc(0x09AE35), 0xD9) - rom.write_byte(snes_to_pc(0x06918E), 0x80) # skip good bee bottle check write_int16(rom, 0x150002, owMode) @@ -878,6 +688,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 @@ -901,6 +712,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) @@ -911,7 +724,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) @@ -950,17 +763,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) @@ -1046,9 +853,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) @@ -1056,13 +863,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 @@ -1072,14 +879,13 @@ 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 - owr_difference = 0x26 # can't remember why there is a difference between DR fork - stat_pc = snes_to_pc(stat_address - owr_difference) + 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) rom.write_byte(stat_pc+3, 0x80) # change to four digits (from 60) @@ -1139,8 +945,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 @@ -1357,11 +1161,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 warningflags = 0x00 # none @@ -1401,11 +1203,9 @@ 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 + rom.write_byte(0x180043, 0x00) # Hammer always breaks barrier rom.write_byte(0x02AF79, 0xD0 if world.mode[player] != 'inverted' else 0xF0) # vortexes: Normal (D0=light to dark, F0=dark to light, 42 = both) rom.write_byte(0x03A943, 0xD0 if world.mode[player] != 'inverted' else 0xF0) # Mirror: Normal (D0=Dark to Light, F0=light to dark, 42 = both) rom.write_byte(0x03A96D, 0xF0 if world.mode[player] != 'inverted' else 0xD0) # Residual Portal: Normal (F0= Light Side, D0=Dark Side, 42 = both (Darth Vader)) @@ -1418,20 +1218,21 @@ 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' and world.logic[player] != 'nologic' @@ -1471,7 +1272,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') or world.owMixed[player]: @@ -1543,7 +1344,8 @@ 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 @@ -1553,7 +1355,8 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): 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 = { @@ -1585,9 +1388,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) if world.bow_mode[player].startswith('retro'): rom.write_byte(0x30052, 0xE4 if world.keyshuffle[player] == 'universal' else 0xDB) # 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 @@ -1599,7 +1402,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', 'hybridglitches', '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]) @@ -1678,10 +1483,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]: @@ -1689,27 +1495,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) @@ -1717,15 +1528,14 @@ 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__ from OverworldShuffle import __version__ as ORVersion 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}O\0', 'utf8')[:21] + rom.name = bytearray(f'OR{__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) @@ -1779,7 +1589,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: @@ -1788,8 +1598,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']) @@ -1811,6 +1621,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(): @@ -1882,17 +1693,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: @@ -1912,8 +1713,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) @@ -2143,6 +1944,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', 'lite', 'lean']: @@ -2672,7 +2483,6 @@ def set_inverted_mode(world, player, rom, inverted_buffer): write_int16(rom, 0x15AEE + 2*0x38, 0x00E0) write_int16(rom, 0x15AEE + 2*0x25, 0x000C) - if world.is_tile_swapped(0x05, player): rom.write_bytes(snes_to_pc(0x1BC655), [0x4A, 0x1D, 0x82]) # add warp under rock rom.write_byte(snes_to_pc(0x1BC428), 0x00) # remove secret portal @@ -2767,9 +2577,6 @@ def set_inverted_mode(world, player, rom, inverted_buffer): write_int16(rom, snes_to_pc(0x1af584), 0x14AE) write_int16(rom, snes_to_pc(0x1af58c), 0x54AE) - rom.write_byte(0xF6E58, 0x80) # no whirlpool under castle gate - rom.write_byte(snes_to_pc(0x09D436), 0xF3) # replace whirlpool with harmless sprite - write_int16(rom, 0xDB96F + 2 * 0x35, 0x001B) # move pyramid exit door write_int16(rom, 0xDBA71 + 2 * 0x35, 0x011C) @@ -2789,6 +2596,7 @@ def set_inverted_mode(world, player, rom, inverted_buffer): rom.write_byte(0x1607C + 0x37, 0x0A) write_int16(rom, 0x160CB + 2 * 0x37, 0x0000) write_int16(rom, 0x16169 + 2 * 0x37, 0x811C) + del world.data_tables[player].ow_enemy_table[0xab][5] # remove castle gate warp if world.is_tile_swapped(0x29, player): rom.write_bytes(snes_to_pc(0x06B2AB), [0xF0, 0xE1, 0x05]) # frog pickup on contact if world.is_tile_swapped(0x2c, player): @@ -2875,9 +2683,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?') diff --git a/Rules.py b/Rules.py index 10a6860b..8184c3e6 100644 --- a/Rules.py +++ b/Rules.py @@ -11,9 +11,13 @@ from OWEdges import OWExitTypes 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): - if world.logic[player] == 'nologic': logging.getLogger('').info('WARNING! Seeds generated under this logic often require major glitches and may be impossible!') world.get_region('Menu', player).can_reach_private = lambda state: True @@ -54,13 +58,17 @@ def set_rules(world, player): bomb_rules(world, player) pot_rules(world, player) + drop_rules(world, player) + challenge_room_rules(world, player) if world.goal[player] == 'dungeons': # require all dungeons to beat ganon add_rule(world.get_location('Ganon', player), lambda state: state.can_reach('Master Sword Pedestal', 'Location', player) and state.has_beaten_aga(player) and state.has('Beat Agahnim 2', player) and state.has_crystals(7, player)) - elif world.goal[player] == 'ganon': - # require aga2 to beat ganon - add_rule(world.get_location('Ganon', player), lambda state: state.has('Beat Agahnim 2', player)) + elif world.goal[player] in ['crystals', 'ganon']: + add_rule(world.get_location('Ganon', player), lambda state: state.has_crystals(world.crystals_needed_for_ganon[player], player)) + if world.goal[player] == 'ganon': + # require aga2 to beat ganon + add_rule(world.get_location('Ganon', player), lambda state: state.has('Beat Agahnim 2', player)) elif world.goal[player] in ['triforcehunt', 'trinity']: for location in world.get_region('Hyrule Castle Courtyard', player).locations: if location.name == 'Murahdahla': @@ -70,6 +78,7 @@ def set_rules(world, player): elif world.goal[player] == 'completionist': add_rule(world.get_location('Ganon', player), lambda state: state.everything(player)) + if (world.flute_mode[player] != 'active' and not world.is_tile_swapped(0x18, player) and 'Ocarina (Activated)' not in list(map(str, [i for i in world.precollected_items if i.player == player]))): if not world.is_copied_world: @@ -146,6 +155,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': @@ -269,6 +286,7 @@ def global_rules(world, player): # 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('Old Man Cave Exit (West)', player), lambda state: False) # drop cannot be climbed up 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)) @@ -325,7 +343,7 @@ def global_rules(world, 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 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)) @@ -399,13 +417,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)) @@ -424,24 +438,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)) @@ -532,6 +534,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)) @@ -577,9 +580,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)) @@ -649,21 +653,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)) @@ -763,6 +758,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)) @@ -873,9 +869,6 @@ def global_rules(world, 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 @@ -884,7 +877,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: @@ -904,81 +899,10 @@ def bomb_rules(world, player): add_rule(world.get_location(location, player), lambda state: state.can_use_bombs(player)) add_bunny_rule(world.get_location(location, player), 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_bunny_rule(world.get_location(location, player), 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_bunny_rule(world.get_entrance('Spiral Cave (top to bottom)', 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)) add_bunny_rule(world.get_location(location, player), 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_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'] @@ -1012,8 +936,57 @@ def bomb_rules(world, player): for door in doors_to_bomb_check: if door.kind(world) in [DoorKind.Dashable]: add_rule(door.entrance, lambda state: state.can_use_bombs(player) or state.has_Boots(player)) + if door.dependents: + for dep in door.dependents: + add_rule(dep.entrance, lambda state: state.can_use_bombs(player) or state.has_Boots(player)) elif door.kind(world) in [DoorKind.Bombable]: add_rule(door.entrance, lambda state: state.can_use_bombs(player)) + if door.dependents: + for dep in door.dependents: + add_rule(dep.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): @@ -1061,6 +1034,20 @@ 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.is_atgt_swapped(player): set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has_crystals(world.crystals_needed_for_gt[player], player)) @@ -1304,6 +1291,7 @@ def ow_bunny_rules(world, 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) @@ -1440,7 +1428,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']}, @@ -1485,7 +1474,7 @@ def swordless_rules(world, player): set_rule(world.get_location('Ether Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player)) set_rule(world.get_location('Bombos Tablet', player), lambda state: state.has('Book of Mudora', player) and state.has('Hammer', player)) - set_rule(world.get_location('Ganon', player), lambda state: state.has('Hammer', player) and state.has_fire_source(player) and state.has('Silver Arrows', player) and state.can_shoot_arrows(player) and state.has_crystals(world.crystals_needed_for_ganon[player], player)) + set_rule(world.get_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)) 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('Misery Mire', player), lambda state: state.has_misery_mire_medallion(player)) # sword not required to use medallion for opening in swordless (!) @@ -1494,50 +1483,93 @@ def swordless_rules(world, player): if not world.is_atgt_swapped(player): set_rule(world.get_entrance('Agahnims Tower', player), lambda state: state.has('Cape', player) or state.has('Hammer', player)) # barrier gets removed after killing agahnim, rule for that added later -# 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) @@ -1589,12 +1621,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)) @@ -1930,8 +1962,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', @@ -1963,13 +1996,16 @@ 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' } @@ -2001,6 +2037,9 @@ def add_key_logic_rules(world, player): forbid_item(location, d_logic.small_key_name, player) for door in d_logic.bk_doors: add_rule(world.get_entrance(door.name, player), create_rule(d_logic.bk_name, player)) + if door.dependents: + for dep in door.dependents: + add_rule(dep.entrance, create_rule(d_logic.bk_name, player)) for chest in d_logic.bk_chests: big_chest = world.get_location(chest.name, player) add_rule(big_chest, create_rule(d_logic.bk_name, player)) diff --git a/Text.py b/Text.py index e3614056..d0011ff7 100644 --- a/Text.py +++ b/Text.py @@ -479,7 +479,7 @@ class Credits(object): ], 'pedestal': [ SceneSmallCreditLine(19, 'and the master sword'), - SceneSmallAltCreditLine(21, 'sleeps again...'), + SceneSmallAltCreditLine(21, 'sleeps again···'), SceneLargeCreditLine(23, 'Forever!'), ], } @@ -827,6 +827,7 @@ class CharTextMapper(object): class RawMBTextMapper(CharTextMapper): char_map = {' ': 0xFF, + '≥': 0x99, # Cursor '『': 0xC4, '』': 0xC5, '?': 0xC6, @@ -835,22 +836,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 @@ -1035,22 +1029,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, @@ -1276,7 +1277,7 @@ class RawMBTextMapper(CharTextMapper): "月": 0xFE, "姫": 0xFF} alpha_offset = 0x49 - alpha_lower_offset = -0x31 + alpha_lower_offset = 0x6F number_offset = 0x70 @classmethod @@ -1299,7 +1300,7 @@ class RawMBTextMapper(CharTextMapper): class GoldCreditMapper(CharTextMapper): char_map = {' ': 0x9F, - ',': 0x34, + ',': 0x34, # apostrophe/comma top "'": 0x35, '-': 0x36, '.': 0x37,} @@ -1321,21 +1322,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 @@ -1343,21 +1346,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 @@ -1570,16 +1575,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.") @@ -1642,7 +1647,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") @@ -1774,7 +1779,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.") @@ -1784,7 +1792,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.") @@ -2012,7 +2026,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/Utils.py b/Utils.py index 1a2a1c1c..06a83a19 100644 --- a/Utils.py +++ b/Utils.py @@ -7,6 +7,12 @@ import xml.etree.ElementTree as ET from collections import defaultdict from math import factorial from itertools import count +import fileinput + +import urllib.request +import urllib.parse +import yaml +from pathlib import Path def int16_as_bytes(value): @@ -14,6 +20,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] @@ -716,6 +727,42 @@ def stack_size3a(size=2): return size - 1 +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) @@ -750,4 +797,5 @@ if __name__ == '__main__': # 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/asm/owrando.asm b/asm/owrando.asm index bb9856b4..2b9eab12 100644 --- a/asm/owrando.asm +++ b/asm/owrando.asm @@ -1,10 +1,3 @@ -; Free RAM notes -; $06F8-$06F9: Used to store edge table addresses -; $06FA-$06FB: Used to store target edge IDs -; $06FC-$06FD: Used for custom walk destination after transitions -; $0703: Used to flag forced transitions -; $0704-$0705: Used to store terrain type at the start of a transition - org $aa8000 ;150000 db $4f, $52 ;OR OWMode: @@ -18,103 +11,103 @@ OWVersionInfo: dw $0000, $0000, $0000, $0000, $0000, $0000, $0000, $0000 ;Hooks -org $02a929 +org $82a929 OWDetectTransitionReturn: -org $02a939 +org $82a939 OverworldHandleTransitions_SpecialTrigger: JSL OWDetectEdgeTransition BCS OWDetectTransitionReturn -org $02a999 +org $82a999 jsl OWEdgeTransition : nop #4 ;LDA $02A4E3,X : ORA $7EF3CA -org $02aa07 +org $82aa07 JSL OWMarkVisited : NOP -org $04e8ae +org $84e8ae JSL OWDetectSpecialTransition RTL : NOP -org $02e809 +org $82e809 JSL OWSpecialExit -org $02bfe8 +org $82bfe8 JSL OWAdjustExitPosition -org $02c1a9 +org $82c1a9 JSL OWEndScrollTransition -org $04E881 +org $84E881 Overworld_LoadSpecialOverworld_RoomId: -org $04E8B4 +org $84E8B4 Overworld_LoadSpecialOverworld: -org $02A9DA +org $82A9DA JSL OWSkipPalettes BCC OverworldHandleTransitions_change_palettes : NOP #4 -org $07982A +org $87982A Link_ResetSwimmingState: ; mirror hooks -org $0283DC ; override world check when spawning mirror portal sprite in Crossed OWR -jsl.l OWLightWorldOrCrossed -org $05AF75 +org $8283DC ; override world check when spawning mirror portal sprite in Crossed OWR +jsl OWLightWorldOrCrossed +org $85AF75 Sprite_6C_MirrorPortal: jsl OWMirrorSpriteDisable ; LDA $7EF3CA -org $05AF88 +org $85AF88 jsl OWMirrorSpriteSkipDraw : NOP ; LDA.w $0FC6 : CMP.b #$03 -org $05AFDF +org $85AFDF Sprite_6C_MirrorPortal_missing_mirror: -org $0ABFB6 +org $8ABFB6 jsl OWMirrorSpriteOnMap : NOP ; LDA.w $008A : CMP.b #$40 ; whirlpool shuffle cross world change -org $02b3bd +org $82b3bd jsl OWWhirlpoolUpdate ;JSL $02EA6C -org $02B44E +org $82B44E jsl OWWhirlpoolEnd ; STZ.b $11 : STZ.b $B0 ; flute menu cancel -org $0ab7af ;LDA $F2 : ORA $F0 : AND #$C0 +org $8ab7af ;LDA $F2 : ORA $F0 : AND #$C0 jml OWFluteCancel2 : nop -org $0ab90d ;JSL $02E99D +org $8ab90d ;JSL $02E99D jsl OWFluteCancel ; allows Frog sprite to spawn in LW and also allows his friend to spawn in their house -org $068a76 ; < 30a76 - sprite_prep.asm:785 (LDA $7EF3CA : AND.w #$40) -lda $1b : eor.b #1 : nop #2 +org $868a76 ; < 30a76 - sprite_prep.asm:785 (LDA $7EF3CA : AND.w #$40) +lda.b IndoorsFlag : eor.b #1 : nop #2 ; allows Frog to be accepted at Blacksmith -org $06b3ee ; < 333ee - sprite_smithy_bros.asm:347 (LDA $7EF3CC : CMP.b #$08 : BEQ .no_returning_smithy_tagalong) +org $86b3ee ; < 333ee - sprite_smithy_bros.asm:347 (LDA $7EF3CC : CMP.b #$08 : BEQ .no_returning_smithy_tagalong) jsl OWSmithAccept : nop #2 db #$b0 ; BCS to replace BEQ ; load Stumpy per screen's original world, not current world flag -org $06907f ; < 3107f - sprite_prep.asm:2170 (LDA $7EF3CA) -lda $8a : and.b #$40 +org $86907f ; < 3107f - sprite_prep.asm:2170 (LDA $7EF3CA) +lda.b OverworldIndex : and.b #$40 ; override Link speed with Old Man following -org $09a32e ; < bank_09.asm:7457 (LDA.b #$0C : STA.b $5E) +org $89a32e ; < bank_09.asm:7457 (LDA.b #$0C : STA.b $5E) jsl OWOldManSpeed ; Dark Bonk Rocks Rain Sequence Guards (allowing Tile Swap on Dark Bonk Rocks) -;org $09c957 ; <- 4c957 +;org $89c957 ; <- 4c957 ;dw #$cb5f ; matches value on Central Bonk Rocks screen ; override world check when viewing overworld (incl. title screen portion) -org $0aba6c ; < ? - Bank0a.asm:474 () -jsl.l OWMapWorldCheck16 : nop +org $8aba6c ; < ? - Bank0a.asm:474 () +jsl OWMapWorldCheck16 : nop ; Mixed Overworld Map -org $0ABA99 +org $8ABA99 WorldMap_LoadDarkWorldMap: -LDA.b $10 : CMP.b #$14 ; attract module +LDA.b GameMode : CMP.b #$14 ; attract module BEQ .vanilla_light LDA.l OWMode+1 : AND.b #!FLAG_OW_MIXED : BNE .mixed - LDA.b $8A : AND.b #$40 + LDA.b OverworldIndex : AND.b #$40 BEQ .vanilla_light .mixed PHB : PHK : PLB @@ -123,55 +116,55 @@ BEQ .vanilla_light .vanilla_light ; $0ABAB5 ;(replacing -> LDA $8A : AND.b #$40) -org $00d8c4 ; < ? - Bank00.asm:4068 () -jsl.l OWWorldCheck -org $02aa36 ; < ? - Bank02.asm:6559 () -jsl.l OWWorldCheck -org $02aeca ; < ? - Bank02.asm:7257 () -jsl.l OWWorldCheck16 : nop -org $02b349 ; < ? - Bank02.asm:7902 () -jsl.l OWWorldCheck -org $02c40a ; < ? - Bank02.asm:10547 () -jsl.l OWWorldCheck -org $05afd9 ; < ? - sprite_warp_vortex.asm:60 () -jsl.l OWWorldCheck -org $07a3f0 ; < ? - Bank07.asm:5772 () ; flute activation/use -jsl.l OWWorldCheck -org $07a967 ; < ? - Bank07.asm:6578 () -jsl.l OWWorldCheck -org $07a9a1 ; < ? - Bank07.asm:6622 () -jsl.l OWWorldCheck -org $07a9ed ; < ? - Bank07.asm:6677 () -jsl.l OWWorldCheck -org $07aa34 ; < ? - Bank07.asm:6718 () -jsl.l OWWorldCheck -org $08d408 ; < ? - ancilla_morph_poof.asm:48 () -jsl.l OWWorldCheck -org $0bfeab ; < ? - Bank0b.asm:36 () -jsl.l OWWorldCheck16 : nop -org $0cffb6 ; < ? - ?.asm ? () -jsl.l OWWorldCheck16 : nop -org $0cffe8 ; < ? - ?.asm ? () -jsl.l OWWorldCheck16 : nop -org $1beca2 ; < ? - palettes.asm:556 () -jsl.l OWWorldCheck16 : nop -org $1bed95 ; < ? - palettes.asm:748 () -jsl.l OWWorldCheck16 : nop +org $80d8c4 ; < ? - Bank00.asm:4068 () +jsl OWWorldCheck +org $82aa36 ; < ? - Bank02.asm:6559 () +jsl OWWorldCheck +org $82aeca ; < ? - Bank02.asm:7257 () +jsl OWWorldCheck16 : nop +org $82b349 ; < ? - Bank02.asm:7902 () +jsl OWWorldCheck +org $82c40a ; < ? - Bank02.asm:10547 () +jsl OWWorldCheck +org $85afd9 ; < ? - sprite_warp_vortex.asm:60 () +jsl OWWorldCheck +org $87a3f0 ; < ? - Bank07.asm:5772 () ; flute activation/use +jsl OWWorldCheck +org $87a967 ; < ? - Bank07.asm:6578 () +jsl OWWorldCheck +org $87a9a1 ; < ? - Bank07.asm:6622 () +jsl OWWorldCheck +org $87a9ed ; < ? - Bank07.asm:6677 () +jsl OWWorldCheck +org $87aa34 ; < ? - Bank07.asm:6718 () +jsl OWWorldCheck +org $88d408 ; < ? - ancilla_morph_poof.asm:48 () +jsl OWWorldCheck +org $8bfeab ; < ? - Bank0b.asm:36 () +jsl OWWorldCheck16 : nop +org $8cffb6 ; < ? - ?.asm ? () +jsl OWWorldCheck16 : nop +org $8cffe8 ; < ? - ?.asm ? () +jsl OWWorldCheck16 : nop +org $9beca2 ; < ? - palettes.asm:556 () +jsl OWWorldCheck16 : nop +org $9bed95 ; < ? - palettes.asm:748 () +jsl OWWorldCheck16 : nop -org $02b16e ; AND #$3F : ORA 7EF3CA +org $82b16e ; AND #$3F : ORA 7EF3CA and #$7f : eor #$40 : nop #2 -org $09C3C4 -jsl.l OWBonkDropPrepSprite : nop #2 -org $09C801 -jsl.l OWBonkDropPrepSprite : nop #2 -org $06D052 -jsl.l OWBonkDropSparkle -org $06AD49 -jsl.l OWBonkDropsOverworld : nop -org $1EDE6A -jsl.l OWBonkDropSparkle : BNE GoldBee_Dormant_exit -jsl.l OWBonkDropsUnderworld : bra + +org $89C3C4 +jsl OWBonkDropPrepSprite : nop #2 +org $89C801 +jsl OWBonkDropPrepSprite : nop #2 +org $86D052 +jsl OWBonkDropSparkle +org $86AD49 +jsl OWBonkDropsOverworld : nop +org $9EDE6A +jsl OWBonkDropSparkle : BNE GoldBee_Dormant_exit +jsl OWBonkDropsUnderworld : bra + GoldBee_SpawnSelf_SetProperties: phb : lda.b #$1E : pha : plb ; switch to bank 1E jsr GoldBee_SpawnSelf+12 @@ -216,18 +209,18 @@ DivideByTwoPreserveSign: OWWorldCheck: { phx - ldx $8a : lda.l OWTileWorldAssoc,x + ldx.b OverworldIndex : lda.l OWTileWorldAssoc,x plx : and.b #$ff : rtl } OWWorldCheck16: { phx - ldx $8a : lda.l OWTileWorldAssoc,x + ldx.b OverworldIndex : lda.l OWTileWorldAssoc,x plx : and.w #$00ff : rtl } OWMapWorldCheck16: { - lda $10 : cmp #$0014 : beq .return ; attract module, return with Z flag cleared + lda.b GameMode : cmp.w #$0014 : beq .return ; attract module, return with Z flag cleared jsl OWWorldCheck16 .return rtl @@ -235,19 +228,19 @@ OWMapWorldCheck16: OWWhirlpoolUpdate: { - jsl $02ea6c ; what we wrote over - ldx $8a : ldy #$03 : jsr OWWorldTerrainUpdate + jsl FindPartnerWhirlpoolExit ; what we wrote over + ldx.b OverworldIndex : ldy.b #$03 : jsr OWWorldTerrainUpdate rtl } OWWhirlpoolEnd: { - STZ.b $B0 ; what we wrote over - LDA.w $0703 : BEQ .normal - LDA.b #$3C : STA.w $012E ; play error sound before forced transition + STZ.b SubSubModule ; what we wrote over + LDA.w RandoOverworldForceTrans : BEQ .normal + LDA.b #$3C : STA.w SFX2 ; play error sound before forced transition RTL .normal - STZ.b $11 ; end whirlpool transition + STZ.b GameSubMode ; end whirlpool transition RTL } @@ -255,11 +248,11 @@ OWDestroyItemSprites: { PHX : LDX.b #$0F .nextSprite - LDA.w $0E20,X + LDA.w SpriteTypeTable,X CMP.b #$D8 : BCC .continue CMP.b #$EC : BCS .continue .killSprite ; need to kill sprites from D8 to EB on screen transition - STZ.w $0DD0,X + STZ.w SpriteAITable,X .continue DEX : BPL .nextSprite PLX : RTL @@ -273,7 +266,7 @@ OWMirrorSpriteOnMap: } OWMirrorSpriteDisable: { - LDA.b $10 : CMP.b #$0F : BNE + ; avoid rare freeze during mirror superbunny + LDA.b GameMode : CMP.b #$0F : BNE + ; avoid rare freeze during mirror superbunny PLA : PLA : PLA : JML Sprite_6C_MirrorPortal_missing_mirror + @@ -293,7 +286,7 @@ OWMirrorSpriteSkipDraw: lda.l CurrentWorld : eor.b #$40 bra ++ + lda.l CurrentWorld : ++ beq .vanilla - stz.w $0D90,x ; disables collision + stz.w SpriteMovement,x ; disables collision sec : rtl .vanilla @@ -304,56 +297,56 @@ OWLightWorldOrCrossed: { lda.l OWMode+1 : and.b #!FLAG_OW_CROSSED : beq ++ lda.l InvertedMode : beq + - lda #$40 + lda.b #$40 + rtl ++ jsl OWWorldCheck : rtl } OWFluteCancel: { - lda.l OWFlags+1 : and #$01 : bne + + lda.l OWFlags+1 : and.b #$01 : bne + jsl FluteMenu_LoadTransport : rtl - + lda $7f5006 : cmp #$01 : beq + + + lda.l HexToDecDigit4 : cmp.b #$01 : beq + jsl FluteMenu_LoadTransport - + lda #$00 : sta $7f5006 : rtl + + lda.b #$00 : sta.l HexToDecDigit4 : rtl } OWFluteCancel2: { - lda $f2 : ora $f0 : and #$c0 : bne + + lda.b Joy1B_All : ora.b Joy1A_All : and.b #$c0 : bne + jml FluteMenu_HandleSelection_NoSelection - + inc $0200 - lda.l OWFlags+1 : and #$01 : beq + - lda $f2 : cmp #$40 : bne + - lda #$01 : sta $7f5006 + + inc.w SubModuleInterface + lda.l OWFlags+1 : and.b #$01 : beq + + lda.b Joy1B_All : cmp.b #$40 : bne + + lda.b #$01 : sta.l HexToDecDigit4 + rtl } OWSmithAccept: { - lda FollowerIndicator : cmp #$07 : beq + - cmp #$08 : beq + + lda FollowerIndicator : cmp.b #$07 : beq + + cmp.b #$08 : beq + clc : rtl + sec : rtl } OWOldManSpeed: { - lda $1b : beq .outdoors - lda $a0 : and #$fe : cmp #$f0 : beq .vanilla ; if in cave where you find Old Man + lda.b IndoorsFlag : beq .outdoors + lda.b RoomIndex : and.b #$fe : cmp.b #$f0 : beq .vanilla ; if in cave where you find Old Man bra .normalspeed .outdoors - lda $8a : cmp #$03 : beq .vanilla ; if on WDM screen + lda.b OverworldIndex : cmp.b #$03 : beq .vanilla ; if on WDM screen .normalspeed - lda $5e : cmp #$0c : rtl - stz $5e : rtl + lda.b LinkSpeed : cmp.b #$0c : rtl + stz.b LinkSpeed : rtl .vanilla - lda #$0c : sta $5e ; what we wrote over + lda.b #$0c : sta.b LinkSpeed ; what we wrote over rtl } OWMarkVisited: { - LDX.b $8A : STZ.w $0412 ; what we wrote over - LDA.b $10 : CMP.b #$14 : BCS .return + LDX.b OverworldIndex : STZ.w $0412 ; what we wrote over + LDA.b GameMode : CMP.b #$14 : BCS .return LDA.l OverworldEventDataWRAM,X ORA.b #$80 : STA.l OverworldEventDataWRAM,X @@ -366,19 +359,19 @@ LoadMapDarkOrMixed: CMP.b #!FLAG_OW_MIXED : REP #$30 : BEQ .mixed LDX.w #$03FE ; draw vanilla Dark World (what we wrote over) .copy_next - LDA.w $D739,X : STA.w $1000,X ; DB is $0A + LDA.w WorldMap_DarkWorldTilemap,X : STA.w $1000,X DEX : DEX : BPL .copy_next BRL .end .mixed - LDX.b $8A + LDX.b OverworldIndex LDA.l OWTileWorldAssoc,X - STA.b $00 + STA.b Scrap00 LDY.w #$139C LDX.w #$003F .next_screen PHX LDA.l OWTileWorldAssoc,X - EOR.b $00 + EOR.b Scrap00 AND.w #$0040 BEQ .light TYX : BRA .copy_screen @@ -404,7 +397,7 @@ LoadMapDarkOrMixed: BPL .next_screen .end SEP #$30 - LDA.b #$15 : STA.b $17 ; what we wrote over + LDA.b #$15 : STA.b NMIINCR ; what we wrote over RTL LWQuadrantOffsets: @@ -417,30 +410,30 @@ LoadMapDarkOrMixed: OWBonkDropPrepSprite: { - LDA.b $1B : BEQ + + LDA.b IndoorsFlag : BEQ + LDA.w $0FB5 ; what we wrote over PHA BRA .continue + - STZ.w $0F20,X : STZ.w $0E30,X ; what we wrote over + STZ.w SpriteLayer,X : STZ.w SpriteAux,X ; what we wrote over PHA .continue LDA.l OWFlags+1 : AND.b #!FLAG_OW_BONKDROP : BEQ .return - + LDA.w $0E20,X : CMP.b #$D9 : BNE + - LDA.b #$03 : STA.w $0F20,X + + LDA.w SpriteTypeTable,X : CMP.b #$D9 : BNE + + LDA.b #$03 : STA.w SpriteLayer,X BRA .prep + CMP.b #$B2 : BEQ .prep PLA : RTL .prep - STZ.w !SPRITE_REDRAW,X + STZ.w SprRedrawFlag,X PHB : PHK : PLB : PHY TXY : JSR OWBonkDropLookup : BCC .done ; found match ; X = rec + 1 INX : LDA.w OWBonkPrizeData,X : PHA JSR OWBonkDropCollected : PLA : BCC .done - TYX : LDA.b #$01 : STA.w !SPRITE_REDRAW,X + TYX : LDA.b #$01 : STA.w SprRedrawFlag,X .done TYX : PLY : PLB @@ -452,7 +445,7 @@ OWBonkDropSparkle: { LDA.l OWFlags+1 : AND.b #!FLAG_OW_BONKDROP : BEQ .nosparkle LDA.w $0E90,X : BEQ .nosparkle - LDA.w !SPRITE_REDRAW,X : BNE .nosparkle + LDA.w SprRedrawFlag,X : BNE .nosparkle JSL Sprite_SpawnSparkleGarnish ; move sparkle down 1 tile PHX : TYX : PLY @@ -461,12 +454,12 @@ OWBonkDropSparkle: PHY : TXY : PLX .nosparkle - LDA $0E20,X : CMP.b #$D9 : BEQ .greenrupee + LDA.w SpriteTypeTable,X : CMP.b #$D9 : BEQ .greenrupee CMP.b #$B2 : BEQ .goodbee RTL .goodbee - LDA $0E90,X ; what we wrote over + LDA.w $0E90,X ; what we wrote over RTL .greenrupee @@ -478,20 +471,20 @@ OWBonkDropsUnderworld: { LDA.l OWFlags+1 : AND.b #!FLAG_OW_BONKDROP : BNE .shuffled .vanilla ; what we wrote over - STZ.w $0DD0,X + STZ.w SpriteAITable,X LDA.l BottleContentsOne : ORA.l BottleContentsTwo ORA.l BottleContentsThree : ORA.l BottleContentsFour RTL .shuffled - LDA.w $0DD0,X : BNE + + LDA.w SpriteAITable,X : BNE + BRA .return+1 + PHY : TXY JSL OWBonkDrops .return PLY - LDA #$08 ; makes original good bee not spawn + LDA.b #$08 ; makes original good bee not spawn RTL } @@ -501,23 +494,23 @@ OWBonkDropsOverworld: BRA .vanilla .shuffled - LDA.w $0DD0,Y : BNE + + LDA.w SpriteAITable,Y : BNE + BRA .vanilla - + LDA.w $0E20,Y : CMP.b #$D9 : BEQ + + + LDA.w SpriteTypeTable,Y : CMP.b #$D9 : BEQ + BRA .vanilla+3 + - LDA.b #$00 : STA.w $0F20,Y ; restore proper layer + LDA.b #$00 : STA.w SpriteLayer,Y ; restore proper layer JSL OWBonkDrops .vanilla - LDA.w $0E20,Y : CMP.b #$D8 ; what we wrote over + LDA.w SpriteTypeTable,Y : CMP.b #$D8 ; what we wrote over RTL } OWBonkDrops: { PHB : PHK : PLB - LDA.b $1B : BEQ + + LDA.b IndoorsFlag : BEQ + LDX.b #((UWBonkPrizeData-OWBonkPrizeData)+1) BRA .found_match + @@ -527,7 +520,7 @@ OWBonkDrops: .found_match INX : LDA.w OWBonkPrizeData,X : PHX : PHA ; S = FlagBitmask, X (row + 2) JSR OWBonkDropCollected : PHA : BCS .load_item_and_mw ; S = Collected, FlagBitmask, X (row + 2) - LDA.b #$1B : STA $12F ; JSL Sound_SetSfx3PanLong ; seems that when you bonk, there is a pending bonk sfx, so we clear that out and replace with reveal secret sfx + LDA.b #$1B : STA.w SFX3 ; JSL Sound_SetSfx3PanLong ; seems that when you bonk, there is a pending bonk sfx, so we clear that out and replace with reveal secret sfx ; JSLSpriteSFX_QueueSFX3WithPan .load_item_and_mw @@ -539,10 +532,10 @@ OWBonkDrops: + DEX : PLA ; A = item id; X = row + 3 .determine_type ; A = item id; X = row + 3; S = Collected, FlagBitmask, X (row + 2) - CMP.b #$B0 : BNE + + CMP.b #$D0 : BNE + LDA.b #$79 : JMP .sprite_transform ; transform to bees + CMP.b #$42 : BNE + - JSL.l Sprite_TransmuteToBomb ; transform a heart to bomb, vanilla behavior + JSL Sprite_TransmuteToBomb ; transform a heart to bomb, vanilla behavior JMP .mark_collected + CMP.b #$34 : BNE + LDA.b #$D9 : JMP .sprite_transform ; transform to single rupee @@ -558,37 +551,37 @@ OWBonkDrops: LDA.b #$DE : BRA .sprite_transform ; transform to 8 bombs + CMP.b #$45 : BNE + LDA.b #$DF : BRA .sprite_transform ; transform to small magic - + CMP.b #$B4 : BNE + + + CMP.b #$D4 : BNE + LDA.b #$E0 : BRA .sprite_transform ; transform to big magic - + CMP.b #$B5 : BNE + - LDA.b #$79 : JSL.l OWBonkSpritePrep - JSL.l GoldBee_SpawnSelf_SetProperties ; transform to good bee + + CMP.b #$D6 : BNE + + LDA.b #$79 : JSL OWBonkSpritePrep + JSL GoldBee_SpawnSelf_SetProperties ; transform to good bee BRA .mark_collected + CMP.b #$44 : BNE + LDA.b #$E2 : BRA .sprite_transform ; transform to 10 arrows - + CMP.b #$B1 : BNE + + + CMP.b #$D1 : BNE + LDA.b #$AC : BRA .sprite_transform ; transform to apples - + CMP.b #$B2 : BNE + + + CMP.b #$D2 : BNE + LDA.b #$E3 : BRA .sprite_transform ; transform to fairy - + CMP.b #$B3 : BNE .spawn_item + + CMP.b #$D3 : BNE .spawn_item INX : INX : LDA.w OWBonkPrizeData,X ; X = row + 5 CLC : ADC.b #$08 : PHA - LDA.w $0D00,Y : SEC : SBC.b 1,S : STA.w $0D00,Y - LDA.w $0D20,Y : SBC.b #$00 : STA.w $0D20,Y : PLX + LDA.w SpritePosYLow,Y : SEC : SBC.b 1,S : STA.w SpritePosYLow,Y + LDA.w SpritePosYHigh,Y : SBC.b #$00 : STA.w SpritePosYHigh,Y : PLX LDA.b #$0B ; BRA .sprite_transform ; transform to chicken .sprite_transform - JSL.l OWBonkSpritePrep + JSL OWBonkSpritePrep .mark_collected ; S = Collected, FlagBitmask, X (row + 2) PLA : BNE + ; S = FlagBitmask, X (row + 2) TYX : JSL Sprite_IsOnscreen : BCC + - LDA.b $1B : BEQ ++ + LDA.b IndoorsFlag : BEQ ++ LDA.l RoomDataWRAM[$0120].high : ORA 1,S : STA.l RoomDataWRAM[$0120].high LDA.w $0400 : ORA 1,S : STA.w $0400 BRA .increment_collection ++ - LDX.b $8A : LDA.l OverworldEventDataWRAM,X : ORA 1,S : STA.l OverworldEventDataWRAM,X + LDX.b OverworldIndex : LDA.l OverworldEventDataWRAM,X : ORA 1,S : STA.l OverworldEventDataWRAM,X .increment_collection REP #$20 @@ -598,40 +591,41 @@ OWBonkDrops: ; spawn itemget item .spawn_item ; A = item id ; Y = bonk sprite slot ; S = Collected, FlagBitmask, X (row + 2) - PLX : BEQ + : LDA.b #$00 : STA.w $0DD0,Y : BRA .return ; S = FlagBitmask, X (row + 2) + PLX : BEQ + : LDA.b #$00 : STA.w SpriteAITable,Y : BRA .return ; S = FlagBitmask, X (row + 2) + PHA - LDA.b #$01 : STA !FORCE_HEART_SPAWN - - LDA.b #$EB : STA.l $7FFE00 + LDA.b #$EB : STA.l MiniGameTime JSL Sprite_SpawnDynamically+15 ; +15 to skip finding a new slot, use existing sprite - LDA.b #$01 : STA.w !SPRITE_REDRAW,Y + LDA.b #$01 : STA.w SprRedrawFlag,Y - PLA : STA.w $0E80,Y + PLA + JSL AttemptItemSubstitution + STA.w SpriteItemType,Y + STA.w SpriteID,Y ; affects the rate the item moves in the Y/X direction - LDA.b #$00 : STA.w $0D40,Y - LDA.b #$0A : STA.w $0D50,Y + LDA.b #$00 : STA.w SpriteVelocityY,Y + LDA.b #$0A : STA.w SpriteVelocityX,Y - LDA.b #$1A : STA.w $0F80,Y ; amount of force (gives height to the arch) - LDA.b #$FF : STA.w $0B58,Y ; stun timer - LDA.b #$30 : STA.w $0F10,Y ; aux delay timer 4 ?? dunno what that means + LDA.b #$1A : STA.w SpriteVelocityZ,Y ; amount of force (gives height to the arch) + LDA.b #$FF : STA.w EnemyStunTimer,Y ; stun timer + LDA.b #$30 : STA.w SpriteTimerE,Y ; aux delay timer 4 ?? dunno what that means - LDA.b #$00 : STA.w $0F20,Y ; layer the sprite is on + LDA.b #$00 : STA.w SpriteLayer,Y ; layer the sprite is on - LDA.b $1B : BEQ + + LDA.b IndoorsFlag : BEQ + ; sets the tile type that is underneath the sprite, water TYX : LDA.b #$09 : STA.l $7FF9C2,X ; TODO: Figure out how to get the game to set this + ; sets bitmask flag, uses free RAM - PLA : STA.w $0ED0,Y ; S = X (row + 2) + PLA : STA.w SpriteSpawnStep,Y ; S = X (row + 2) ; determines the initial spawn point of item PLX : INX : INX : INX - LDA.w $0D00,Y : SEC : SBC.w OWBonkPrizeData,X : STA.w $0D00,Y - LDA.w $0D20,Y : SBC #$00 : STA.w $0D20,Y + LDA.w SpritePosYLow,Y : SEC : SBC.w OWBonkPrizeData,X : STA.w SpritePosYLow,Y + LDA.w SpritePosYHigh,Y : SBC #$00 : STA.w SpritePosYHigh,Y BRA .return+2 @@ -644,14 +638,14 @@ OWBonkDrops: OWBonkDropLookup: { ; loop thru rando bonk table to find match - LDA.b $8A + LDA.b OverworldIndex LDX.b #((UWBonkPrizeData-OWBonkPrizeData)-sizeof(OWBonkPrizeTable)) ; 41 bonk items, 6 bytes each - CMP.w OWBonkPrizeData,X : BNE + INX - LDA.w $0D10,Y : LSR A : LSR A : LSR A : LSR A - EOR.w $0D00,Y : CMP.w OWBonkPrizeData,X : BNE ++ ; X = row + 1 + LDA.w SpritePosXLow,Y : LSR A : LSR A : LSR A : LSR A + EOR.w SpritePosYLow,Y : CMP.w OWBonkPrizeData,X : BNE ++ ; X = row + 1 SEC : RTS - ++ DEX : LDA.b $8A + ++ DEX : LDA.b OverworldIndex + CPX.b #$00 : BNE + CLC : RTS + DEX : DEX : DEX : DEX : DEX : DEX : BRA - @@ -662,11 +656,11 @@ OWBonkDropCollected: { ; check if collected CLC - LDA.b $1B : BEQ + + LDA.b IndoorsFlag : BEQ + LDA.l RoomDataWRAM[$0120].high : AND.b 3,S : BEQ .return ; S = Collected, FlagBitmask, X (row + 2) SEC : RTS + - LDX.b $8A : LDA.l OverworldEventDataWRAM,X : AND 3,S : BEQ .return ; S = Collected, FlagBitmask, X (row + 2) + LDX.b OverworldIndex : LDA.l OverworldEventDataWRAM,X : AND 3,S : BEQ .return ; S = Collected, FlagBitmask, X (row + 2) SEC : RTS .return @@ -676,13 +670,13 @@ OWBonkDropCollected: ; A = SpriteID, Y = Sprite Slot Index, X = free/overwritten OWBonkSpritePrep: { - STA.w $0E20,Y - TYX : JSL.l Sprite_LoadProperties + STA.w SpriteTypeTable,Y + TYX : JSL SpritePrep_LoadProperties BEQ + ; these are sprite properties that make it fall out of the tree to the east - LDA #$30 : STA $0F80,Y ; amount of force (related to speed) - LDA #$10 : STA $0D50,Y ; eastward rate of speed - LDA #$FF : STA $0B58,Y ; expiration timer + LDA.b #$30 : STA.w SpriteVelocityZ,Y ; amount of force (related to speed) + LDA.b #$10 : STA.w SpriteVelocityX,Y ; eastward rate of speed + LDA.b #$FF : STA.w EnemyStunTimer,Y ; expiration timer + RTL } @@ -690,27 +684,27 @@ org $aa9000 OWDetectEdgeTransition: { JSL OWDestroyItemSprites - STZ.w $06FC + STZ.w RandoOverworldWalkDist LDA.l OWMode : ORA.l OWMode+1 : BEQ .vanilla JSR OWShuffle - LDA.w $06FA : BMI .special + LDA.w RandoOverworldTargetEdge : BMI .special .vanilla - REP #$31 : LDX.b $02 : LDA.b $84 ; what we wrote over + REP #$31 : LDX.b Scrap02 : LDA.b OverworldMap16Buffer ; what we wrote over RTL .special REP #$30 AND.w #$0003 : TAY : ASL : TAX - LDA.w #$007F : STA.w $06FA + LDA.w #$007F : STA.w RandoOverworldTargetEdge JSR OWLoadSpecialArea SEC RTL } OWDetectSpecialTransition: { - STZ.w $06FC + STZ.w RandoOverworldWalkDist LDA.l OWMode : BEQ .normal TXA : AND.w #$0002 : LSR - STA.w $0704 + STA.w RandoOverworldTerrain LDA.l OWSpecialDestIndex,X : BIT.w #$0080 : BEQ .switch_to_edge AND.w #$0003 : TAY : ASL : TAX .normal @@ -719,40 +713,40 @@ OWDetectSpecialTransition: RTL .switch_to_edge - STA.w $06FA - LDA.l OWEdgeDataOffset,X : STA.w $06F8 + STA.w RandoOverworldTargetEdge + LDA.l OWEdgeDataOffset,X : STA.w RandoOverworldEdgeAddr PLA : SEP #$30 : PLA ; delete 3 bytes from stack JSL Link_CheckForEdgeScreenTransition : BCS .return ; Link_CheckForEdgeScreenTransition - LDA.l Overworld_CheckForSpecialOverworldTrigger_Direction,X : STA.b $00 : CMP.b #$08 : BNE .hobo - LSR : STA.b $20 : STZ.b $E8 ; move Link and camera to edge - LDA.b #$06 : STA.b $02 - STZ.w $0418 + LDA.l Overworld_CheckForSpecialOverworldTrigger_Direction,X : STA.b Scrap00 : CMP.b #$08 : BNE .hobo + LSR : STA.b LinkPosY : STZ.b BG2V ; move Link and camera to edge + LDA.b #$06 : STA.b Scrap02 + STZ.w TransitionDirection BRA .continue .hobo - STA.b $02 : STA.w $0418 - ASL : STA.b $22 : STZ.b $E2 ; move Link and camera to edge - LDA.b #$0A : STA.b $23 : STA.b $E3 + STA.b Scrap02 : STA.w TransitionDirection + ASL : STA.b LinkPosX : STZ.b BG2H ; move Link and camera to edge + LDA.b #$0A : STA.b LinkPosX+1 : STA.b BG2H+1 .continue - STZ.b $03 + STZ.b Scrap03 ; copied from DeleteCertainAncillaeStopDashing at $028A0E JSL Ancilla_TerminateSelectInteractives - LDA.w $0372 : BEQ .not_dashing - STZ.b $4D : STZ.b $46 - LDA.b #$FF : STA.b $29 : STA.b $C7 - STZ.b $3D : STZ.b $5E : STZ.w $032B : STZ.w $0372 : STZ.b $5D + LDA.w LinkDashing : BEQ .not_dashing + STZ.b LinkJumping : STZ.b LinkIncapacitatedTimer + LDA.b #$FF : STA.b LinkRecoilZ : STA.b $C7 + STZ.b $3D : STZ.b LinkSpeed : STZ.w $032B : STZ.w LinkDashing : STZ.b LinkState .not_dashing PLA : REP #$31 : PLA ; delete 3 bytes from stack - LDX.b $02 - LDA.b $84 + LDX.b Scrap02 + LDA.b OverworldMap16Buffer JML OverworldHandleTransitions_SpecialTrigger+6 } OWEdgeTransition: { LDA.l OWMode : ORA.l OWMode+1 : BEQ .unshuffled - LDY.w $06FA : CPY.b #$7F + LDY.w RandoOverworldTargetEdge : CPY.b #$7F BEQ .unshuffled REP #$10 - LDX.w $06F8 + LDX.w RandoOverworldEdgeAddr PHB : PHK : PLB JSR OWNewDestination PLB @@ -773,10 +767,10 @@ OWSpecialExit: LDA.l OWMode : ORA.l OWMode+1 : BEQ .vanilla PHY LDY.b #$00 - LDA.w $0418 : LSR : BNE + - LDY.w $0704 : BRA ++ + LDA.w TransitionDirection : LSR : BNE + + LDY.w RandoOverworldTerrain : BRA ++ + - LDA.w $0704 : BNE ++ + LDA.w RandoOverworldTerrain : BNE ++ LDY.b #$02 ++ JSR OWWorldTerrainUpdate @@ -788,21 +782,21 @@ OWSpecialExit: OWShuffle: { ;determine direction of edge transition - phx : lsr.w $0700 - tyx : lda.l OWTransitionDirection,X : sta.w $0418 + phx : lsr.w OverworldSlotPosition + tyx : lda.l OWTransitionDirection,X : sta.w TransitionDirection .setOWID ;look up transitions in current area in table OWEdgeOffsets ;offset is (8bytes * OW Slot ID) + (2bytes * direction) asl : rep #$20 : and.w #$00ff : pha : sep #$20 ;2 bytes per direction - ldx $8a : lda.l OWTileWorldAssoc,X : eor.l CurrentWorld : beq + + ldx.b OverworldIndex : lda.l OWTileWorldAssoc,X : eor.l CurrentWorld : beq + ; fake world, will treat this OW area as opposite world txa : eor.b #$40 : tax - + txa : and #$40 : !add $700 : rep #$30 : and #$00ff : asl #3 + + txa : and #$40 : !add.w OverworldSlotPosition : rep #$30 : and #$00ff : asl #3 adc 1,S : tax - asl $700 : pla + asl.w OverworldSlotPosition : pla ;x = offset to edgeoffsets table sep #$20 : lda.l OWEdgeOffsets,x : and #$ff : beq .noTransition : pha ;get number of transitions @@ -810,16 +804,16 @@ OWShuffle: inx : lda.l OWEdgeOffsets,x ;record id of first transition in table ;multiply ^ by 16, 16bytes per record - sta $4202 : lda #16 : sta $4203 ;wait 8 cycles + sta.w CPUMULTA : lda #16 : sta.w CPUMULTB ;wait 8 cycles pla ;a = number of trans rep #$20 - and #$00ff - ldx $4216 ;x = offset to first record + and.w #$00ff + ldx.w CPUPRODUCT ;x = offset to first record .nextTransition pha jsr OWSearchTransition_entry : bcs .newDestination - txa : !add #$0010 : tax + txa : !add.w #$0010 : tax pla : dec : bne .nextTransition : bra .noTransition .newDestination @@ -827,7 +821,7 @@ OWShuffle: .noTransition sep #$30 : plx - lda.b #$7f : sta.w $06fa + lda.b #$7f : sta.w RandoOverworldTargetEdge .return rts @@ -839,10 +833,10 @@ OWSearchTransition: .entry ;A-16 XY-16 - lda $418 : bne + ;north + lda.w TransitionDirection : bne + ;north lda.l OWNorthEdges,x : dec - cmp $22 : !bge .exitloop - lda.l OWNorthEdges+2,x : cmp $22 : !blt .exitloop + cmp.b LinkPosX : !bge .exitloop + lda.l OWNorthEdges+2,x : cmp.b LinkPosX : !blt .exitloop ;MATCH lda.l OWNorthEdges+14,x : tay ;y = record id of dest lda.l OWNorthEdges+12,x ;a = current terrain @@ -850,8 +844,8 @@ OWSearchTransition: bra .matchfound + dec : bne + ;south lda.l OWSouthEdges,x : dec - cmp $22 : !bge .exitloop - lda.l OWSouthEdges+2,x : cmp $22 : !blt .exitloop + cmp.b LinkPosX : !bge .exitloop + lda.l OWSouthEdges+2,x : cmp.b LinkPosX : !blt .exitloop ;MATCH lda.l OWSouthEdges+14,x : tay ;y = record id of dest lda.l OWSouthEdges+12,x ;a = current terrain @@ -859,135 +853,135 @@ OWSearchTransition: bra .matchfound + dec : bne + ; west lda.l OWWestEdges,x : dec - cmp $20 : !bge .exitloop - lda.l OWWestEdges+2,x : cmp $20 : !blt .exitloop + cmp.b LinkPosY : !bge .exitloop + lda.l OWWestEdges+2,x : cmp.b LinkPosY : !blt .exitloop ;MATCH lda.l OWWestEdges+14,x : tay ;y = record id of dest lda.l OWWestEdges+12,x ;a = current terrain ldx.w #OWEastEdges ;x = address of table bra .matchfound + lda.l OWEastEdges,x : dec ;east - cmp $20 : !bge .exitloop - lda.l OWEastEdges+2,x : cmp $20 : !blt .exitloop + cmp.b LinkPosY : !bge .exitloop + lda.l OWEastEdges+2,x : cmp.b LinkPosY : !blt .exitloop ;MATCH lda.l OWEastEdges+14,x : tay ;y = record id of dest lda.l OWEastEdges+12,x ;a = current terrain ldx.w #OWWestEdges ;x = address of table .matchfound - stx $06f8 : sty $06fa : sta $0704 : sec : rts - plx : pla : pea $0001 : phx + stx.w RandoOverworldEdgeAddr : sty.w RandoOverworldTargetEdge : sta.w RandoOverworldTerrain : sec : rts + plx : pla : pea.w $0001 : phx sec : rts } OWNewDestination: { - tya : sta $4202 : lda #16 : sta $4203 ;wait 8 cycles - rep #$20 : txa : nop : !add $4216 : tax ;a = offset to dest record - lda.w $0008,x : sta $04 ;save dest OW slot/ID - ldy $20 : lda $418 : dec #2 : bpl + : ldy $22 : + sty $06 + tya : sta.w CPUMULTA : lda.b #16 : sta.w CPUMULTB ;wait 8 cycles + rep #$20 : txa : nop : !add.w CPUPRODUCT : tax ;a = offset to dest record + lda.w $0008,x : sta.b Scrap04 ;save dest OW slot/ID + ldy.b LinkPosY : lda.w TransitionDirection : dec #2 : bpl + : ldy.b LinkPosX : + sty.b Scrap06 ;;22 e0 e2 61c 61e - X ;;20 e6 e8 618 61a - Y ;keep current position if within incoming gap - lda.w $0000,x : and #$01ff : pha : lda.w $0002,x : and #$01ff : pha + lda.w $0000,x : and.w #$01ff : pha : lda.w $0002,x : and.w #$01ff : pha LDA.l OWMode : AND.w #$0007 : BEQ .noLayoutShuffle ;temporary fix until VRAM issues are solved - lda.w $0006,x : sta $06 ;set coord - lda.w $000a,x : sta $84 ;VRAM - tya : and #$01ff : cmp 3,s : !blt .adjustMainAxis - dec : cmp 1,s : !bge .adjustMainAxis - inc : pha : lda $06 : and #$fe00 : !add 1,s : sta $06 : pla + lda.w $0006,x : sta.b Scrap06 ;set coord + lda.w $000a,x : sta.b OverworldMap16Buffer ;VRAM + tya : and.w #$01ff : cmp.b 3,s : !blt .adjustMainAxis + dec : cmp.b 1,s : !bge .adjustMainAxis + inc : pha : lda.b Scrap06 : and.w #$fe00 : !add.b 1,s : sta.b Scrap06 : pla ; adjust and set other VRAM addresses - lda.w $0006,x : pha : lda $06 : !sub 1,s + lda.w $0006,x : pha : lda.b Scrap06 : !sub 1,s jsl DivideByTwoPreserveSign : jsl DivideByTwoPreserveSign : jsl DivideByTwoPreserveSign : jsl DivideByTwoPreserveSign : pha ; number of tiles - lda $418 : dec #2 : bmi + - pla : pea $0000 : bra ++ ;pla : asl #7 : pha : bra ++ ; y-axis shifts VRAM by increments of 0x80 (disabled for now) + lda.w TransitionDirection : dec #2 : bmi + + pla : pea.w $0000 : bra ++ ;pla : asl #7 : pha : bra ++ ; y-axis shifts VRAM by increments of 0x80 (disabled for now) + pla : asl : pha ; x-axis shifts VRAM by increments of 0x02 - ++ lda $84 : !add 1,s : sta $84 : pla : pla + ++ lda.b OverworldMap16Buffer : !add 1,s : sta.b OverworldMap16Buffer : pla : pla .adjustMainAxis - LDA $84 : SEC : SBC #$0400 : AND #$0F00 : ASL : XBA : STA $88 ; vram - LDA $84 : SEC : SBC #$0010 : AND #$003E : LSR : STA $86 + LDA.b OverworldMap16Buffer : SEC : SBC #$0400 : AND #$0F00 : ASL : XBA : STA.b OverworldTilemapIndexY ; vram + LDA.b OverworldMap16Buffer : SEC : SBC #$0010 : AND #$003E : LSR : STA.b OverworldTilemapIndexX .noLayoutShuffle - LDA.w $000F,X : AND.w #$00FF : STA.w $06FC ; position to walk to after transition (if non-zero) + LDA.w $000F,X : AND.w #$00FF : STA.w RandoOverworldWalkDist ; position to walk to after transition (if non-zero) LDY.w #$0000 LDA.w $000C,X : AND.w #$0001 : BEQ + ; check if going to water transition - LDA.w $0704 : AND.w #$0001 : BNE ++ ; check if coming from water transition + LDA.w RandoOverworldTerrain : AND.w #$0001 : BNE ++ ; check if coming from water transition INY : BRA ++ + - LDA.w $0704 : BEQ ++ ; check if coming from water transition + LDA.w RandoOverworldTerrain : BEQ ++ ; check if coming from water transition LDY.w #$0002 ++ - STY.b $08 + STY.b Scrap08 - pla : pla : sep #$10 : ldy $418 - ldx OWCoordIndex,y : lda $20,x : and #$fe00 : pha - lda $20,x : and #$01ff : pha ;s1 = relative cur, s3 = ow cur - lda $06 : and #$fe00 : !sub 3,s : pha ;set coord, s1 = ow diff, s3 = relative cur, s5 = ow cur - lda $06 : and #$01ff : !sub 3,s : pha ;s1 = rel diff, s3 = ow diff, s5 = relative cur, s7 = ow cur - lda $06 : sta $20,x : and #$fe00 : sta $06 ;set coord - ldx OWBGIndex,y : lda $e2,x : !add 1,s : adc 3,s : sta $e2,x - ldx OWCameraIndex,y : lda $618,x : !add 1,s : adc 3,s : sta $618,x - ldx OWCameraIndex,y : lda $61a,x : !add 1,s : adc 3,s : sta $61a,x + pla : pla : sep #$10 : ldy.w TransitionDirection + ldx.w OWCoordIndex,y : lda.b LinkPosY,x : and.w #$fe00 : pha + lda.b LinkPosY,x : and.w #$01ff : pha ;s1 = relative cur, s3 = ow cur + lda.b Scrap06 : and #$fe00 : !sub.b 3,s : pha ;set coord, s1 = ow diff, s3 = relative cur, s5 = ow cur + lda.b Scrap06 : and.w #$01ff : !sub.b 3,s : pha ;s1 = rel diff, s3 = ow diff, s5 = relative cur, s7 = ow cur + lda.b Scrap06 : sta.b LinkPosY,x : and.w #$fe00 : sta.b Scrap06 ;set coord + ldx.w OWBGIndex,y : lda.b BG2H,x : !add.b 1,s : adc.b 3,s : sta.b BG2H,x + ldx.w OWCameraIndex,y : lda.w CameraScrollN,x : !add.b 1,s : adc.b 3,s : sta.w CameraScrollN,x + ldx.w OWCameraIndex,y : lda.w CameraScrollS,x : !add.b 1,s : adc.b 3,s : sta.w CameraScrollS,x pla : jsl DivideByTwoPreserveSign : pha - ldx OWBGIndex,y : lda $e0,x : !add 1,s : sta $e0,x : pla - ldx OWBGIndex,y : lda $e0,x : !add 1,s : sta $e0,x : pla + ldx.w OWBGIndex,y : lda.b BG1H,x : !add.b 1,s : sta.b BG1H,x : pla + ldx.w OWBGIndex,y : lda.b BG1H,x : !add.b 1,s : sta.b BG1H,x : pla pla : pla ;fix camera unlock - lda $e2,x : !sub $06 : bpl + - pha : lda $06 : sta $e2,x - ldx.w OWCameraIndex,y : lda $0618,x : !sub 1,s : sta $0618,x - lda $061a,x : !sub 1,s : sta $061a,x : pla + lda.b BG2H,x : !sub.b Scrap06 : bpl + + pha : lda.b Scrap06 : sta.b BG2H,x + ldx.w OWCameraIndex,y : lda.w CameraScrollN,x : !sub.b 1,s : sta.w CameraScrollN,x + lda.w CameraScrollS,x : !sub.b 1,s : sta.w CameraScrollS,x : pla bra .adjustOppositeAxis - + lda $06 : ldx.w OWCameraRangeIndex,y : !add.w OWCameraRange,x : sta $06 - ldx.w OWBGIndex,y : !sub $e2,x : bcs .adjustOppositeAxis - pha : lda $06 : sta $e2,x - ldx.w OWCameraIndex,y : lda $0618,x : !add 1,s : sta $0618,x - lda $061a,x : !add 1,s : sta $061a,x : pla + + lda.b Scrap06 : ldx.w OWCameraRangeIndex,y : !add.w OWCameraRange,x : sta.b Scrap06 + ldx.w OWBGIndex,y : !sub.b BG2H,x : bcs .adjustOppositeAxis + pha : lda.b Scrap06 : sta.b BG2H,x + ldx.w OWCameraIndex,y : lda.w CameraScrollN,x : !add.b 1,s : sta.w CameraScrollN,x + lda.w CameraScrollS,x : !add.b 1,s : sta.w CameraScrollS,x : pla .adjustOppositeAxis ;opposite coord stuff - rep #$30 : lda OWOppDirectionOffset,y : and #$00ff : bit #$0080 : beq + - ora #$ff00 ;extend 8-bit negative to 16-bit negative - + pha : cpy #$0002 : lda $700 : !bge + - and #$00f0 : pha : lda $04 : asl : and #$0070 : !sub 1,s : tax : pla : txa + rep #$30 : lda OWOppDirectionOffset,y : and.w #$00ff : bit.w #$0080 : beq + + ora.w #$ff00 ;extend 8-bit negative to 16-bit negative + + pha : cpy.w #$0002 : lda.w OverworldSlotPosition : !bge + + and.w #$00f0 : pha : lda.b Scrap04 : asl : and.w #$0070 : !sub.b 1,s : tax : pla : txa !add 1,s : tax : pla : txa : asl : asl : asl : asl : asl : pha : bra ++ - + and #$000f : pha : lda $04 : asl : and #$000f : !sub 1,s : !add 3,s - sep #$10 : tax : phx : ldx #$0 : phx : rep #$10 : pla : plx : plx : pha + + and.w #$000f : pha : lda.b Scrap04 : asl : and.w #$000f : !sub.b 1,s : !add.b 3,s + sep #$10 : tax : phx : ldx.b #$0 : phx : rep #$10 : pla : plx : plx : pha - ++ sep #$10 : ldx OWOppCoordIndex,y : lda $20,x : !add 1,s : sta $20,x ;set coord - ldx OWOppBGIndex,y : lda $e2,x : !add 1,s : sta $e2,x - ldx OWOppCameraIndex,y : lda $618,x : !add 1,s : sta $618,x - ldx OWOppCameraIndex,y : lda $61a,x : !add 1,s : sta $61a,x - ldx OWOppBGIndex,y : lda $e0,x : !add 1,s : sta $e0,x - lda $418 : asl : tax : lda $610,x : !add 1,s : sta $610,x : pla + ++ sep #$10 : ldx OWOppCoordIndex,y : lda.b LinkPosY,x : !add.b 1,s : sta.b LinkPosY,x ;set coord + ldx OWOppBGIndex,y : lda.b BG2H,x : !add.b 1,s : sta.b BG2H,x + ldx OWOppCameraIndex,y : lda.w CameraScrollN,x : !add.b 1,s : sta.w CameraScrollN,x + ldx OWOppCameraIndex,y : lda.w CameraScrollS,x : !add.b 1,s : sta.w CameraScrollS,x + ldx OWOppBGIndex,y : lda.b BG1H,x : !add.b 1,s : sta.b BG1H,x + lda.w TransitionDirection : asl : tax : lda.w CameraTargetN,x : !add.b 1,s : sta.w CameraTargetN,x : pla - sep #$30 : lda $04 : and #$3f : !add OWOppSlotOffset,y : asl : sta $700 + sep #$30 : lda.b Scrap04 : and.b #$3f : !add OWOppSlotOffset,y : asl : sta.w OverworldSlotPosition ; crossed OW shuffle and terrain - ldx $05 : ldy $08 : jsr OWWorldTerrainUpdate + ldx.b Scrap05 : ldy.b Scrap08 : jsr OWWorldTerrainUpdate - ldx $8a : lda $05 : sta $8a : stx $05 ; $05 is prev screen id, $8a is dest screen + ldx.b OverworldIndex : lda.b Scrap05 : sta.b OverworldIndex : stx.b Scrap05 ; $05 is prev screen id, $8a is dest screen jsr OWGfxUpdate - lda $8a + lda.b OverworldIndex rep #$30 : rts } OWLoadSpecialArea: { - LDA.l Overworld_LoadSpecialOverworld_RoomId,X : STA.b $A0 + LDA.l Overworld_LoadSpecialOverworld_RoomId,X : STA.b RoomIndex JSL Overworld_LoadSpecialOverworld ; sets M and X flags TYX LDY.b #$00 CPX.b #$01 : BNE + ; check if going to water transition - LDA.w $0704 : BNE ++ ; check if coming from water transition + LDA.w RandoOverworldTerrain : BNE ++ ; check if coming from water transition INY : BRA ++ + - LDA.w $0704 : BEQ ++ ; check if coming from water transition + LDA.w RandoOverworldTerrain : BEQ ++ ; check if coming from water transition LDY.b #$02 ++ LDA.l OWSpecialDestSlot,X : TAX @@ -1005,100 +999,100 @@ OWWorldTerrainUpdate: ; x = owid of destination screen, y = 1 for land to water, sta.l CurrentWorld ; change world ; moving mirror portal off screen when in DW - cmp #0 : beq + : lda #1 + cmp.b #0 : beq + : lda.b #1 + cmp.l InvertedMode : bne + - lda $1acf : and #$0f : sta $1acf : bra .playSfx ; bring portal back into position - + lda $1acf : ora #$40 : sta $1acf ; move portal off screen + lda.w MirrorPortalPosXH : and.b #$0f : sta.w MirrorPortalPosXH : bra .playSfx ; bring portal back into position + + lda.w MirrorPortalPosXH : ora.b #$40 : sta.w MirrorPortalPosXH ; move portal off screen .playSfx - lda #$38 : sta $012f ; play sfx - #$3b is an alternative + lda.b #$38 : sta.w SFX3 ; play sfx - #$3b is an alternative ; toggle bunny mode - lda MoonPearlEquipment : beq + : jmp .nobunny + lda.l MoonPearlEquipment : beq + : jmp .nobunny + lda.l InvertedMode : bne .inverted - lda CurrentWorld : bra + - .inverted lda CurrentWorld : eor #$40 - + and #$40 : beq .nobunny - LDA.w $0703 : BEQ + ; check if forced transition + lda.l CurrentWorld : bra + + .inverted lda.l CurrentWorld : eor.b #$40 + + and.b #$40 : beq .nobunny + LDA.w RandoOverworldForceTrans : BEQ + ; check if forced transition CPY.b #$03 : BEQ ++ - LDA.b #$17 : STA.b $5D - LDA.b #$01 : STA.w $02E0 : STA.b $56 - LDA.w $0703 : JSR OWLoadGearPalettes : BRA .end_forced_edge + LDA.b #$17 : STA.b LinkState + LDA.b #$01 : STA.w BunnyFlag : STA.b BunnyFlagDP + LDA.w RandoOverworldForceTrans : JSR OWLoadGearPalettes : BRA .end_forced_edge ++ JSR OWLoadGearPalettes : BRA .end_forced_whirlpool + CPY.b #$01 : BEQ .auto ; check if going from land to water CPY.b #$02 : BEQ .to_bunny_reset_swim ; bunny state if swimming to land - LDA.b $5D : CMP.b #$04 : BNE .to_bunny ; check if swimming + LDA.b LinkState : CMP.b #$04 : BNE .to_bunny ; check if swimming .auto PHX LDA.b #$01 - LDX.b $5D : CPX.b #$04 : BNE + + LDX.b LinkState : CPX.b #$04 : BNE + INC + - STA.w $0703 + STA.w RandoOverworldForceTrans CPY.b #$03 : BEQ .whirlpool - LDA.b #$01 : STA.w $0345 - LDX.w $0418 - LDA.l OWAutoWalk,X : STA.b $49 - STZ.b $5D + LDA.b #$01 : STA.w LinkDeepWater + LDX.w TransitionDirection + LDA.l OWAutoWalk,X : STA.b ForceMove + STZ.b LinkState PLX BRA .to_pseudo_bunny .whirlpool PLX : JMP OWLoadGearPalettes .to_bunny_reset_swim - LDA.b $5D : CMP.b #$04 : BNE .to_bunny ; check if swimming + LDA.b LinkState : CMP.b #$04 : BNE .to_bunny ; check if swimming JSL Link_ResetSwimmingState - STZ.w $0345 + STZ.w LinkDeepWater .to_bunny - LDA.b #$17 : STA.b $5D + LDA.b #$17 : STA.b LinkState .to_pseudo_bunny - LDA.b #$01 : STA.w $02E0 : STA.b $56 + LDA.b #$01 : STA.w BunnyFlag : STA.b BunnyFlagDP JMP OWLoadGearPalettes .nobunny - lda $5d : cmp #$17 : bne + ; retain current state unless bunny - stz $5d - + stz $02e0 : stz $56 + lda.b LinkState : cmp.b #$17 : bne + ; retain current state unless bunny + stz.b LinkState + + stz.w BunnyFlag : stz.b BunnyFlagDP .normal - LDA.w $0703 : BEQ .not_forced ; check if forced transition + LDA.w RandoOverworldForceTrans : BEQ .not_forced ; check if forced transition CPY.b #$03 : BEQ .end_forced_whirlpool .end_forced_edge - STZ.b $49 : STZ.w $0345 + STZ.b ForceMove : STZ.w LinkDeepWater .end_forced_whirlpool - STZ.w $0703 + STZ.w RandoOverworldForceTrans CMP.b #$02 : BNE + - DEC : STA.w $0345 : STZ.w $0340 + DEC : STA.w LinkDeepWater : STZ.w LinkSwimDirection LDA.b #$04 : BRA .set_state + CMP.b #$03 : BNE ++ LDA.b #$17 .set_state - STA.b $5D + STA.b LinkState ++ RTS .not_forced CPY.b #$02 : BNE + ; check if going from water to land - LDA.b $5D : CMP.b #$04 : BNE .return ; check if swimming + LDA.b LinkState : CMP.b #$04 : BNE .return ; check if swimming JSL Link_ResetSwimmingState - STZ.w $0345 - STZ.b $5D + STZ.w LinkDeepWater + STZ.b LinkState + CPY.b #$01 : BNE .return ; check if going from land to water - LDA.b $5D : CMP.b #$04 : BEQ .return ; check if swimming - LDA.b #$01 : STA.w $0345 + LDA.b LinkState : CMP.b #$04 : BEQ .return ; check if swimming + LDA.b #$01 : STA.w LinkDeepWater LDA.l FlippersEquipment : BEQ .no_flippers ; check if flippers obtained - LDA.b $5D : CMP.b #$17 : BEQ .no_flippers ; check if bunny - LDA.b #$04 : STA.b $5D : STZ.w $0340 : RTS + LDA.b LinkState : CMP.b #$17 : BEQ .no_flippers ; check if bunny + LDA.b #$04 : STA.b LinkState : STZ.w LinkSwimDirection : RTS .no_flippers PHX - INC : STA.w $0703 - LDX.w $0418 - LDA.l OWAutoWalk,X : STA.b $49 + INC : STA.w RandoOverworldForceTrans + LDX.w TransitionDirection + LDA.l OWAutoWalk,X : STA.b ForceMove PLX - LDA.b $5D : CMP.b #$17 : BNE .return ; check if bunny - LDA.b #$03 : STA.w $0703 - STZ.b $5D + LDA.b LinkState : CMP.b #$17 : BNE .return ; check if bunny + LDA.b #$03 : STA.w RandoOverworldForceTrans + STZ.b LinkState .return RTS } @@ -1106,13 +1100,13 @@ OWGfxUpdate: { REP #$20 : LDA.l OWMode : AND.w #$0207 : BEQ .is_only_mixed : SEP #$20 ;;;;PLA : AND.b #$3F : BEQ .leaving_woods - LDA.b $8A : AND.b #$3F : BEQ .entering_woods - ;LDA.b $05 : JSL OWSkipPalettes : BCS .skip_palettes - LDA.b $8A : JSR OWDetermineScreensPaletteSet + LDA.b OverworldIndex : AND.b #$3F : BEQ .entering_woods + ;LDA.b Scrap05 : JSL OWSkipPalettes : BCS .skip_palettes + LDA.b OverworldIndex : JSR OWDetermineScreensPaletteSet CPX.w $0AB3 : BEQ .skip_palettes ; check if next screen's palette is different - LDA $00 : PHA + LDA.b Scrap00 : PHA JSL OverworldLoadScreensPaletteSet_long ; loading correct OW palette - PLA : STA $00 + PLA : STA.b Scrap00 .leaving_woods .entering_woods .is_only_mixed @@ -1121,14 +1115,14 @@ OWGfxUpdate: } OWLoadGearPalettes: { - PHX : PHY : LDA $00 : PHA - LDA.w $02E0 : BEQ + + PHX : PHY : LDA.b Scrap00 : PHA + LDA.w BunnyFlag : BEQ + JSL LoadGearPalettes_bunny BRA .return + JSL LoadGearPalettes_link .return - PLA : STA $00 : PLY : PLX + PLA : STA.b Scrap00 : PLY : PLX RTS } OWDetermineScreensPaletteSet: ; A = OWID to check @@ -1146,19 +1140,19 @@ OWDetermineScreensPaletteSet: ; A = OWID to check } OWSkipPalettes: { - STA.b $05 ; A = previous screen, also stored in $05 + STA.b Scrap05 ; A = previous screen, also stored in $05 ; only skip mosaic if OWR Layout or Crossed PHP : REP #$20 : LDA.l OWMode : AND.w #$0207 : BEQ .vanilla : PLP ; checks to see if going to from any DM screens - ;LDA.b $05 : JSR OWDetermineScreensPaletteSet : TXA : AND.b #$FE : STA $04 - ;LDA.b $8A : JSR OWDetermineScreensPaletteSet : TXA : AND.b #$FE - ;CMP.b $04 : BNE .skip_palettes + ;LDA.b Scrap05 : JSR OWDetermineScreensPaletteSet : TXA : AND.b #$FE : STA.b Scrap04 + ;LDA.b OverworldIndex : JSR OWDetermineScreensPaletteSet : TXA : AND.b #$FE + ;CMP.b Scrap04 : BNE .skip_palettes BRA .vanilla+1 .vanilla PLP - LDA.b $05 : AND.b #$3F : BEQ .skip_palettes ; what we - LDA.b $8A : AND.b #$BF : BNE .change_palettes ; wrote over, kinda + LDA.b Scrap05 : AND.b #$3F : BEQ .skip_palettes ; what we + LDA.b OverworldIndex : AND.b #$BF : BNE .change_palettes ; wrote over, kinda .skip_palettes SEC : RTL ; mosaic transition occurs .change_palettes @@ -1166,26 +1160,26 @@ OWSkipPalettes: } OWAdjustExitPosition: { - LDA.w $06FC : CMP.b #$60 : BEQ .stone_bridge + LDA.w RandoOverworldWalkDist : CMP.b #$60 : BEQ .stone_bridge CMP.b #$B0 : BNE .normal - LDA.b #$80 : STA.b $20 : STZ.b $21 + LDA.b #$80 : STA.b LinkPosY : STZ.b LinkPosY+1 BRA .normal .stone_bridge - LDA.b #$A0 : STA.b $E2 - LDA.b #$3D : STA.w $061C - LDA.b #$3B : STA.w $061E - INC.b $23 : INC.w $061D : INC.w $061F + LDA.b #$A0 : STA.b BG2H + LDA.b #$3D : STA.w CameraScrollW + LDA.b #$3B : STA.w CameraScrollE + INC.b LinkPosX+1 : INC.w CameraScrollW+1 : INC.w CameraScrollE+1 .normal - LDA.w $0703 : BEQ + - LDA.b #$3C : STA.w $012E ; play error sound before forced transition + LDA.w RandoOverworldForceTrans : BEQ + + LDA.b #$3C : STA.w SFX2 ; play error sound before forced transition + - INC.b $11 : STZ.b $B0 ; what we wrote over + INC.b GameSubMode : STZ.b SubSubModule ; what we wrote over RTL } OWEndScrollTransition: { - LDY.w $06FC : BEQ .normal - CMP.w $06FC + LDY.w RandoOverworldWalkDist : BEQ .normal + CMP.w RandoOverworldWalkDist RTL .normal CMP.l Overworld_FinalizeEntryOntoScreen_Data,X ; what we wrote over @@ -1776,7 +1770,7 @@ db $ff, $00, $02, $b5, $00, $08 ; temporary fix - murahdahla replaces one of the bonk tree prizes ; so we copy the sprite table here and update the pointer ; longterm solution should be to spawn in murahdahla separately -org $09AE2A +org $89AE2A Overworld_Sprites_Screen1A_2: db $08, $0F, $41 ; yx:{ 0x080, 0x0F0 } db $0E, $0C, $41 ; yx:{ 0x0E0, 0x0C0 } @@ -1784,5 +1778,5 @@ db $11, $0D, $E3 ; yx:{ 0x110, 0x0D0 } db $18, $0A, $D8 ; yx:{ 0x180, 0x0A0 } db $18, $0F, $45 ; yx:{ 0x180, 0x0F0 } db $FF ; END -org $09CA55 +org $89CA55 dw Overworld_Sprites_Screen1A_2&$FFFF \ No newline at end of file diff --git a/data/base2current.bps b/data/base2current.bps index c31273e3..2586d909 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 75c997bc..f0e6743b 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 95% rename from docs/presets/async_doors_league/S3_BombBag.yaml rename to docs/presets/async_doors_league/prior/S3_BombBag.yaml index 9edc1803..3e8507c9 100644 --- a/docs/presets/async_doors_league/S3_BombBag.yaml +++ b/docs/presets/async_doors_league/prior/S3_BombBag.yaml @@ -15,7 +15,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_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 95% rename from docs/presets/async_doors_league/S3_PotteryLottery.yaml rename to docs/presets/async_doors_league/prior/S3_PotteryLottery.yaml index baae256d..422b409a 100644 --- a/docs/presets/async_doors_league/S3_PotteryLottery.yaml +++ b/docs/presets/async_doors_league/prior/S3_PotteryLottery.yaml @@ -14,7 +14,7 @@ settings: intensity: 3 door_type_mode: big pottery: lottery - dropshuffle: true + dropshuffle: keys experimental: true dungeon_counters: 'on' hints: true diff --git a/docs/presets/async_doors_league/S3_Standard.yaml b/docs/presets/async_doors_league/prior/S3_Standard.yaml similarity index 91% rename from docs/presets/async_doors_league/S3_Standard.yaml rename to docs/presets/async_doors_league/prior/S3_Standard.yaml index 050db332..ea4a5858 100644 --- a/docs/presets/async_doors_league/S3_Standard.yaml +++ b/docs/presets/async_doors_league/prior/S3_Standard.yaml @@ -16,7 +16,7 @@ settings: intensity: 3 door_type_mode: big pottery: keys - dropshuffle: true + dropshuffle: keys experimental: true dungeon_counters: 'on' hints: true diff --git a/mystery_example.yml b/mystery_example.yml index 9fc7951e..33205a3d 100644 --- a/mystery_example.yml +++ b/mystery_example.yml @@ -55,8 +55,9 @@ on: 1 off: 1 dropshuffle: - on: 1 - off: 1 + none: 4 + keys: 1 + underworld: 1 pottery: none: 8 keys: 1 @@ -212,6 +213,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 7a2ad91c..38393165 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 @@ -152,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 85d8851b..dbbc8c8b 100644 --- a/resources/app/cli/args.json +++ b/resources/app/cli/args.json @@ -96,8 +96,11 @@ "type": "bool" }, "dropshuffle" : { - "action": "store_true", - "type": "bool" + "choices" : [ + "none", + "keys", + "underworld" + ] }, "pottery" : { "choices" : [ @@ -535,9 +538,6 @@ "bps": { "action": "store_true" }, - "enemizercli": { - "setting": "enemizercli" - }, "shufflebosses": { "choices": [ "none", @@ -550,9 +550,7 @@ "shuffleenemies": { "choices": [ "none", - "shuffled", - "random", - "legacy" + "shuffled" ] }, "enemy_health": { @@ -571,6 +569,13 @@ "random" ] }, + "any_enemy_logic": { + "choices": [ + "none", + "allow_drops", + "allow_all" + ] + }, "remote_items": { "action": "store_true", "type": "bool" diff --git a/resources/app/cli/lang/en.json b/resources/app/cli/lang/en.json index eec33d7e..15dae0aa 100644 --- a/resources/app/cli/lang/en.json +++ b/resources/app/cli/lang/en.json @@ -343,7 +343,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", @@ -382,6 +386,12 @@ "collection_rate": [ "Display collection rate (default: %(default)s)" ], "pseudoboots": [ " Start 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 b069e129..53ea2c74 100644 --- a/resources/app/gui/lang/en.json +++ b/resources/app/gui/lang/en.json @@ -134,8 +134,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.overworld.overworldshuffle": "Layout Shuffle", @@ -355,7 +357,10 @@ "randomizer.item.colorizepots": "Colorize Randomized Pots", "randomizer.item.potshuffle": "Pot Shuffle (Legacy)", - "randomizer.item.dropshuffle": "Shuffle Enemy Key Drops", + "randomizer.item.dropshuffle": "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/item/widgets.json b/resources/app/gui/randomize/item/widgets.json index 0c65b694..bb1d6024 100644 --- a/resources/app/gui/randomize/item/widgets.json +++ b/resources/app/gui/randomize/item/widgets.json @@ -158,12 +158,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 c38ae024..fa1f9582 100644 --- a/source/classes/CustomSettings.py +++ b/source/classes/CustomSettings.py @@ -110,7 +110,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' @@ -153,6 +154,7 @@ class CustomSettings(object): 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]) @@ -260,6 +262,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 = {}, {} @@ -319,6 +326,7 @@ class CustomSettings(object): 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] diff --git a/source/classes/constants.py b/source/classes/constants.py index 1eae52dd..1f62560c 100644 --- a/source/classes/constants.py +++ b/source/classes/constants.py @@ -127,7 +127,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..fc07cb1c --- /dev/null +++ b/source/dungeon/EnemyList.py @@ -0,0 +1,2676 @@ +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 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 + GreenRupee = 0xd9 + 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.bonk = False # bonk location + 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, bonk=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 or bonk: + sprite.static = True + if bonk: + sprite.bonk = 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, bonk=True) + 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): + from EntranceShuffle import door_addresses + 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..c8f1070c --- /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: 'none'}) + 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..58c15b43 --- /dev/null +++ b/source/enemizer/OwEnemyList.py @@ -0,0 +1,1038 @@ +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, bonk=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 or bonk: + sprite.static = True + if bonk: + sprite.bonk = 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, bonk=True) + 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, bonk=True) + create_sprite(0x232, EnemySprite.SmallHeart, 0x19, 0x12, '', 0x09CB76, bonk=True) + + # 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, bonk=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, bonk=True) + create_sprite(0x51, EnemySprite.Faerie, 0x09, 0x10, '', 0x09CC18, bonk=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, bonk=True) + create_sprite(0x54, EnemySprite.RedRupee, 0x19, 0x0B, '', 0x09CC4B, bonk=True) + create_sprite(0x54, EnemySprite.Ropa, 0x07, 0x0F, '', 0x09CC4E) + create_sprite(0x54, EnemySprite.Faerie, 0x0F, 0x0E, '', 0x09CC51, bonk=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, bonk=True) + create_sprite(0x55, EnemySprite.Hinox, 0x07, 0x17, '', 0x09CC6A) + create_sprite(0x55, EnemySprite.Bee, 0x0A, 0x1A, '', 0x09CC6D, bonk=True) + 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, bonk=True) + 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, bonk=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, bonk=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, bonk=True) + create_sprite(0x6e, EnemySprite.Bee, 0x10, 0x09, '', 0x09CDC1, bonk=True) + create_sprite(0x6e, EnemySprite.Apple, 0x14, 0x0A, '', 0x09CDC4, bonk=True) + 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, bonk=True) + 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, bonk=True) + 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, bonk=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, bonk=True) + 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, bonk=True) + # 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, bonk=True) + create_sprite(0x10, EnemySprite.LargeMagic, 0x17, 0x0F, '', 0x09D019, bonk=True) + 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, bonk=True) + 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, bonk=True) + 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, bonk=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, bonk=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, bonk=True) + # 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, bonk=True) + create_sprite(0x1a, EnemySprite.BlueRupee, 0x17, 0x17, '', 0x09D0A9) + create_sprite(0x1a, EnemySprite.SmallHeart, 0x0A, 0x18, '', 0x09D0AC, bonk=True) + 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, bonk=True) + 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, bonk=True) + 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, bonk=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, bonk=True) + create_sprite(0x32, EnemySprite.BlueGuard, 0x0B, 0x0B, '', 0x09D1E6) + create_sprite(0x32, EnemySprite.BlueGuard, 0x12, 0x0B, '', 0x09D1E9) + create_sprite(0x32, EnemySprite.Faerie, 0x19, 0x12, '', 0x09D1EC, bonk=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, bonk=True) + 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, bonk=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, bonk=True) + 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, bonk=True) + # 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, bonk=True) + create_sprite(0xa0, EnemySprite.LargeMagic, 0x17, 0x0F, '', 0x09D385, bonk=True) + 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, bonk=True) + create_sprite(0xa1, EnemySprite.Cucco, 0x08, 0x17, '', 0x09D398) + # Screen12_2: + create_sprite(0xa2, EnemySprite.Faerie, 0x14, 0x0A, '', 0x09D39C, bonk=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, bonk=True) + create_sprite(0xa3, EnemySprite.BombRefill8, 0x07, 0x0C, '', 0x09D3AC, bonk=True) + 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, bonk=True) + create_sprite(0xa5, EnemySprite.RedSpearGuard, 0x0B, 0x17, '', 0x09D3D2) + create_sprite(0xa5, EnemySprite.Apple, 0x04, 0x1A, '', 0x09D3D5, bonk=True) + # 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, bonk=True) + 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, bonk=True) + # 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, bonk=True) + create_sprite(0xaa, EnemySprite.SmallHeart, 0x0A, 0x18, '', 0x09D421, bonk=True) + 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, bonk=True) + 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, bonk=True) + 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, bonk=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, bonk=True) + create_sprite(0xba, EnemySprite.RedRupee, 0x0F, 0x18, '', 0x09D4DA, bonk=True) + # Screen2B_2: + create_sprite(0xbb, EnemySprite.BlueGuard, 0x08, 0x06, '', 0x09D4DE) + create_sprite(0xbb, EnemySprite.Faerie, 0x16, 0x0D, '', 0x09D4E1, bonk=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, bonk=True) + create_sprite(0xbe, EnemySprite.Bee, 0x14, 0x0B, '', 0x09D505, bonk=True) + 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, bonk=True) + 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, bonk=True) + 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 index 46842052..7c378cd3 100644 --- a/source/enemizer/enemy_deny.yaml +++ b/source/enemizer/enemy_deny.yaml @@ -17,9 +17,9 @@ UwGeneralDeny: - [ 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, 0, [ "RollerVerticalDown", "RollerVerticalUp", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit"] ] #"Swamp Palace - Pool - Zol 1" + - [ 0x0016, 1, [ "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit" ] ] #"Swamp Palace - Pool - Zol 2" + - [ 0x0016, 2, [ "SparkCW", "SparkCCW", "RollerVerticalDown", "RollerVerticalUp", "RollerHorizontalRight", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "GreenMimic", "RedMimic", "Pikit" ] ] #"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" @@ -52,7 +52,7 @@ UwGeneralDeny: - [ 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, 4, [ "Statue", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper", "RollerHorizontalRight", "RollerHorizontalLeft"]] - [ 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" @@ -67,6 +67,7 @@ UwGeneralDeny: - [ 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, 5, ["AntiFairyCircle", "Bumper"]] - [ 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" @@ -90,12 +91,12 @@ UwGeneralDeny: - [ 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" + - [ 0x0042, 0, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 1" + - [ 0x0042, 1, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 2" + - [ 0x0042, 2, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "SpikeBlock", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 3" + - [ 0x0042, 3, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 4" + - [ 0x0042, 4, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper", "Chainchomp" ] ] #"Sewers - Dark Rope Corridor - Rope 5" + - [ 0x0042, 5, [ "SparkCW", "SparkCCW", "RollerHorizontalLeft", "Beamos", "AntiFairyCircle", "BigSpike", "Bumper", "Chainchomp" ] ] #"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" @@ -125,7 +126,7 @@ UwGeneralDeny: - [ 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" + - [ 0x0053, 7, ["Beamos", "AntiFairyCircle", "Bumper", "RollerVerticalUp", "RollerVerticalDown"]] #"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 @@ -139,8 +140,8 @@ UwGeneralDeny: - [ 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" + - [ 0x0057, 13, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue", "BigSpike"]] #"Skull Woods - Big Key Room - Blue Bari 1" + - [ 0x0057, 14, [ "RollerVerticalDown", "Beamos", "AntiFairyCircle", "Bumper", "Statue", "BigSpike"]] #"Skull Woods - Big Key Room - Blue Bari 2" - [ 0x0058, 0, ["Statue"]] - [ 0x0058, 1, ["Statue"]] - [ 0x0058, 2, ["Statue"]] @@ -156,7 +157,7 @@ UwGeneralDeny: - [ 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" + - [ 0x0060, 0, [ "RollerVerticalUp", "RollerHorizontalLeft", "AntiFairyCircle", "BigSpike", "Bumper", "Beamos" ] ] #"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 @@ -170,7 +171,7 @@ UwGeneralDeny: - [ 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, 5, ["RollerVerticalDown", "Beamos"]] #"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" @@ -195,7 +196,7 @@ UwGeneralDeny: - [ 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" ] ] #"Ganon's Tower - Randomizer Room - Fire Bar (Clockwise)" + - [ 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" @@ -244,6 +245,7 @@ UwGeneralDeny: - [ 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" ] ] @@ -288,7 +290,8 @@ UwGeneralDeny: - [ 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, 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" @@ -347,6 +350,7 @@ UwGeneralDeny: - [ 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" @@ -398,10 +402,10 @@ OwGeneralDeny: - [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, 1, ["Gibo", "RollerVerticalUp", "RollerVerticalDown"]] # 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, 4, ["RollerVerticalUp", "RollerVerticalDown", "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 @@ -435,94 +439,97 @@ UwEnemyDrop: - [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", + - [0x003d, 9, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x003d, 10, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x003d, 10, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "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"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0044, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0044, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0044, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0044, 4, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0044, 5, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0044, 6, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0044, 8, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x0049, 10, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [0x007b, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", - "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic"]] + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] - [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", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] + - [0x007f, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x007f, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x007f, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x007f, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x007f, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x007f, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x007f, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "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", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] + - [0x0095, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x0095, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x0095, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "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", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] + - [0x00af, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", + "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "Hover"]] + - [0x00b5, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00b5, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00b5, 1, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00b5, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00b5, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00c6, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00c6, 2, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00c6, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00c6, 3, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00c6, 4, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00c6, 4, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00c6, 5, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00c6, 5, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Bumper", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00c6, 6, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00c6, 6, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "BluesainBolt", "UsainBolt", "BlueArcher", "GreenBushGuard", "RedJavelinGuard", "RedBushGuard", "BombGuard", "GreenKnifeGuard", "Stal", "GreenMimic", "RedMimic", "StalfosKnight", "Geldman", "Blob"]] - - [0x00e6, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", + - [0x00e6, 0, ["HardhatBeetle", "Wizzrobe", "MiniHelmasaur", "BlueGuard", "GreenGuard", "RedSpearGuard", "Hover", "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 @@ -577,7 +584,6 @@ UwEnemyDrop: - [0x009f, 5, ["Wizzrobe", "Stal"]] - [0x00a1, 1, ["Wizzrobe"]] - [0x00aa, 5, ["Wizzrobe"]] - - [0x00af, 0, ["Wizzrobe", "Stal"]] - [0x00b0, 1, ["Wizzrobe"]] - [0x00b0, 2, ["Wizzrobe"]] - [0x00b2, 4, ["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 d50010f2..dc5bc3cd 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 @@ -79,65 +79,67 @@ def bottom_frame(self, parent, args=None): 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 @@ -273,9 +275,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()) diff --git a/source/gui/loadcliargs.py b/source/gui/loadcliargs.py index a1f07d7d..9230fe59 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" 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 67efe0ba..6038e3a2 100644 --- a/source/gui/widgets.py +++ b/source/gui/widgets.py @@ -287,9 +287,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/FillUtil.py b/source/item/FillUtil.py index e30e7141..1e8a8092 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) @@ -129,11 +129,11 @@ def create_item_pool_config(world): groups = LocationGroup('Major').locs(init_set) if world.bigkeyshuffle[player]: groups.locations.extend(mode_grouping['Big Keys']) - if world.dropshuffle[player]: + if world.dropshuffle[player] != 'none': groups.locations.extend(mode_grouping['Big Key Drops']) if world.keyshuffle[player] != 'none': groups.locations.extend(mode_grouping['Small Keys']) - if world.dropshuffle[player]: + if world.dropshuffle[player] != 'none': groups.locations.extend(mode_grouping['Key Drops']) if world.pottery[player] not in ['none', 'cave']: groups.locations.extend(mode_grouping['Pot Keys']) @@ -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', 'trinity', '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,14 @@ 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 76b459f8..2042ade8 100644 --- a/source/overworld/EntranceShuffle2.py +++ b/source/overworld/EntranceShuffle2.py @@ -2561,9 +2561,13 @@ mandatory_connections = [('Lost Woods Hideout (top to bottom)', 'Lost Woods Hide ('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)'), @@ -2582,11 +2586,19 @@ mandatory_connections = [('Lost Woods Hideout (top to bottom)', 'Lost Woods Hide ('Sewer Drop', 'Sewers Rat Path'), ('Missing Smith', 'Missing Smith'), ('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)'), @@ -2606,7 +2618,7 @@ default_connections = {'Lost Woods Gamble': 'Lost Woods Gamble', '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)', - 'Old Man Cave (East)': 'Old Man Cave', + 'Old Man Cave (East)': 'Old Man Cave (East)', 'Old Man Cave Exit (East)': 'West Death Mountain (Bottom)', 'Spectacle Rock Cave': 'Spectacle Rock Cave (Top)', 'Spectacle Rock Cave Exit (Top)': 'West Death Mountain (Bottom)', diff --git a/source/rom/DataTables.py b/source/rom/DataTables.py new file mode 100644 index 00000000..69b570a1 --- /dev/null +++ b/source/rom/DataTables.py @@ -0,0 +1,228 @@ +from collections import defaultdict + +from Utils import snes_to_pc, int24_as_bytes, int16_as_bytes, load_cached_yaml, pc_to_snes + +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 + self.write_ow_sprite_data_to_rom(rom) + 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 + ]) + + def write_ow_sprite_data_to_rom(self, rom): + # calculate how big this table is going to be? + # bytes = sum(1+len(x)*3 for x in self.ow_enemy_table.values() if len(x) > 0)+1 + # ending_byte = 0x09CB3B + bytes + max_per_state = {0: 0x40, 1: 0x90, 2: 0x8D} # dropped max on state 2 to steal space for a couple extra sprites (Murahdahla) + pointer_address = snes_to_pc(0x09C881) + data_pointer = snes_to_pc(0x09CB3B) # was originally 0x09CB41 - stealing space for a couple extra sprites (Murahdahla) + empty_pointer = pc_to_snes(data_pointer) & 0xFFFF + rom.write_byte(data_pointer, 0xff) + cached_dark_world = {} + data_pointer += 1 + for state in range(0, 3): + if state > 0: # move pointer to next section + pointer_address += max_per_state[state-1] * 2 + for screen in range(0, max_per_state[state]): + internal_screen_id = screen + if state == 0: + internal_screen_id += 0x200 + if state == 2 and screen < 0x40: + internal_screen_id += 0x90 + # has no sprites + if internal_screen_id not in self.ow_enemy_table or len(self.ow_enemy_table[internal_screen_id]) == 0: + rom.write_bytes(pointer_address + screen * 2, int16_as_bytes(empty_pointer)) + else: + if state == 2 and screen >= 0x40: # state 2 uses state 1 pointer for screens >= 0x40 + rom.write_bytes(pointer_address + screen * 2, cached_dark_world[screen]) + # the sprites are already written out + elif len(self.ow_enemy_table[internal_screen_id]) > 0: + data_address = pc_to_snes(data_pointer) & 0xFFFF + ref = int16_as_bytes(data_address) + if screen >= 40: + cached_dark_world[screen] = ref + rom.write_bytes(pointer_address + screen * 2, ref) + for sprite in self.ow_enemy_table[internal_screen_id]: + data = sprite.sprite_data() + rom.write_bytes(data_pointer, data) + data_pointer += len(data) + rom.write_byte(data_pointer, 0xff) + data_pointer += 1 + + +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: + if sprite.bonk and world.shuffle_bonk_drops[player]: + sprite.kind = EnemySprite.GreenRupee + 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 b80b21f4..c37ea0a3 100644 --- a/source/tools/MysteryUtils.py +++ b/source/tools/MysteryUtils.py @@ -32,6 +32,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: @@ -46,9 +79,10 @@ def roll_settings(weights): ret.algorithm = get_choice('algorithm') - glitch_map = {'none': 'noglitches', 'minorglitches': 'minorglitches', 'no_logic': 'nologic', + glitch_map = {'none': 'noglitches', 'minorglitches': 'minorglitches', 'hmg': 'hybridglitches', 'hybridglitches': 'hybridglitches', - 'owg': 'owglitches', 'owglitches': 'owglitches'} + 'owg': 'owglitches', 'owglitches': 'owglitches', + 'no_logic': 'nologic'} glitches_required = get_choice('glitches_required') if glitches_required is not None: if glitches_required not in glitch_map.keys(): @@ -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,23 +105,23 @@ 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') overworld_shuffle = get_choice('overworld_shuffle') ret.ow_shuffle = overworld_shuffle if overworld_shuffle != 'none' else 'vanilla' - ret.ow_terrain = get_choice('overworld_terrain') == 'on' + ret.ow_terrain = get_choice_bool('overworld_terrain') valid_options = {'none': 'none', 'polar': 'polar', 'grouped': 'polar', 'chaos': 'unrestricted', 'unrestricted': 'unrestricted'} ret.ow_crossed = get_choice('overworld_crossed') ret.ow_crossed = valid_options[ret.ow_crossed] if ret.ow_crossed in valid_options else 'none' - ret.ow_keepsimilar = get_choice('overworld_keepsimilar') == 'on' - ret.ow_mixed = get_choice('overworld_swap') == 'on' - ret.ow_whirlpool = get_choice('whirlpool_shuffle') == 'on' + ret.ow_keepsimilar = get_choice_bool('overworld_keepsimilar') + ret.ow_mixed = get_choice_bool('overworld_swap') + ret.ow_whirlpool = get_choice_bool('whirlpool_shuffle') overworld_flute = get_choice('flute_shuffle') ret.ow_fluteshuffle = overworld_flute if overworld_flute != 'none' else 'vanilla' - ret.bonk_drops = get_choice('bonk_drops') == 'on' + ret.bonk_drops = get_choice_bool('bonk_drops') entrance_shuffle = get_choice('entrance_shuffle') ret.shuffle = entrance_shuffle if entrance_shuffle != 'none' else 'vanilla' overworld_map = get_choice('overworld_map') @@ -98,29 +132,30 @@ 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.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.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.aga_randomness = get_choice('aga_randomness') == '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', @@ -134,9 +169,9 @@ def roll_settings(weights): ret.openpyramid = get_choice('open_pyramid') if 'open_pyramid' in weights else 'auto' - ret.shuffleganon = get_choice('shuffleganon') == 'on' - ret.shufflelinks = get_choice('shufflelinks') == 'on' - ret.shuffletavern = get_choice('shuffletavern') == 'on' + ret.shuffleganon = get_choice_bool('shuffleganon') + ret.shufflelinks = get_choice_bool('shufflelinks') + ret.shuffletavern = get_choice_bool('shuffletavern') ret.crystals_gt = get_choice('tower_open') ret.crystals_ganon = get_choice('ganon_open') @@ -154,12 +189,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: @@ -196,6 +231,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' @@ -211,18 +247,17 @@ 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.msu_resume = get_choice('msu_resume', 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.shuffle_sfxinstruments = get_choice('shuffle_sfxinstruments', romweights) == 'on' - ret.shuffle_songinstruments = get_choice('shuffle_songinstruments', romweights) == 'on' - ret.msu_resume = get_choice('msu_resume', romweights) == 'on' + ret.shuffle_sfx = get_choice_bool('shuffle_sfx', romweights) + ret.shuffle_sfxinstruments = get_choice_bool('shuffle_sfxinstruments', romweights) + ret.shuffle_songinstruments = get_choice_bool('shuffle_songinstruments', 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/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 + +