diff --git a/Fill.py b/Fill.py index f695bd9a..4ebd4c04 100644 --- a/Fill.py +++ b/Fill.py @@ -9,7 +9,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): @@ -360,22 +360,53 @@ 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 pot/drop shuffle + chicken_pool = collections.defaultdict(list) + big_magic_pool = collections.defaultdict(list) + fairy_pool = collections.defaultdict(list) + for item in world.itempool: - if item.name in ['Chicken', 'Big Magic']: # can only fill these in that players world - pot_item_pool[item.player].append(item) - for player, pot_pool in pot_item_pool.items(): - if pot_pool: - for pot_item in pot_pool: - world.itempool.remove(pot_item) - pot_locations = [location for location in fill_locations - if location.type == LocationType.Pot and location.player == player] - pot_locations = filter_pot_locations(pot_locations, world) - fast_fill_helper(world, pot_pool, pot_locations) - pots_used = True - if pots_used: + # can only fill these in that players world + if item.name == 'Chicken': + chicken_pool[item.player].append(item) + elif item.name == 'Big Magic': + big_magic_pool[item.player].append(item) + elif item.name == 'Fairy': + fairy_pool[item.player].append(item) + + def fast_fill_certain_items(item_pool, location_types, filter_locations, matcher): + flag = False + for player, pool in item_pool.items(): + if pool: + for certain_item in pool: + world.itempool.remove(certain_item) + cand_locations = [location for location in fill_locations + if location.type in location_types and location.player == player] + locations = filter_locations(cand_locations, world, matcher) + fast_fill_helper(world, pool, locations) + flag = True + return flag + + def chicken_matcher(l): + return l.pot and l.pot.item == PotItem.Chicken + + def magic_matcher(l): + if l.drop: + pack = enemy_stats[l.drop.kind].prize_pack + return pack in {3,7} if isinstance(pack, int) else (3 in pack or 7 in pack) + return l.pot and l.pot.item == PotItem.BigMagic + + def fairy_matcher(l): + if l.drop: + pack = enemy_stats[l.drop.kind].prize_pack + return pack == 7 if isinstance(pack, int) else 7 in pack + return False + + reshuffle = fast_fill_certain_items(chicken_pool, [LocationType.Pot], filter_special_locations, chicken_matcher) + reshuffle |= fast_fill_certain_items(fairy_pool, [LocationType.Drop], filter_special_locations, fairy_matcher) + reshuffle |= fast_fill_certain_items(big_magic_pool, [LocationType.Pot, LocationType.Drop], + filter_special_locations, magic_matcher) + if reshuffle: fill_locations = world.get_unfilled_locations() random.shuffle(fill_locations) @@ -459,6 +490,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: @@ -468,7 +500,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 calc_trash_locations(world, player): @@ -484,7 +516,7 @@ def calc_trash_locations(world, player): return gt_count, total_count -def ensure_good_pots(world, write_skips=False): +def ensure_good_items(world, write_skips=False): for loc in world.get_locations(): if loc.item is None: loc.item = ItemFactory('Nothing', loc.player) @@ -494,12 +526,23 @@ def ensure_good_pots(world, write_skips=False): 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)): + elif loc.item.name == 'Chicken' 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) + elif (loc.item.name == 'Big Magic' + and (loc.type not in [LocationType.Pot, LocationType.Drop] or loc.item.player != loc.player)): + if loc.type in [LocationType.Pot, LocationType.Drop]: + loc.item.player = loc.player + else: + loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.player) + elif (loc.item.name == 'Fairy' + and (loc.type != LocationType.Drop or loc.item.player != loc.player)): + if loc.type == LocationType.Drop: + 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) @@ -510,7 +553,7 @@ def ensure_good_pots(world, write_skips=False): 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): diff --git a/ItemList.py b/ItemList.py index 2e7f0040..67e06644 100644 --- a/ItemList.py +++ b/ItemList.py @@ -10,6 +10,7 @@ from Fill import FillError, fill_restrictive, get_dungeon_item_pool, is_dungeon_ from PotShuffle import vanilla_pots from Items import ItemFactory +from source.dungeon.EnemyList import add_drop_contents from source.item.FillUtil import trash_items, pot_items import source.classes.constants as CONST @@ -423,6 +424,9 @@ def generate_itempool(world, player): if world.pottery[player] not in ['none', 'keys'] and not skip_pool_adjustments: add_pot_contents(world, player) + if world.dropshuffle[player] == 'underworld' and not skip_pool_adjustments: + add_drop_contents(world, player) + take_any_locations = [ 'Snitch Lady (East)', 'Snitch Lady (West)', 'Bush Covered House', 'Light World Bomb Hut', diff --git a/Items.py b/Items.py index da9d7385..8c27bf88 100644 --- a/Items.py +++ b/Items.py @@ -1,5 +1,3 @@ -import logging - from BaseClasses import Item @@ -176,6 +174,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Blue Potion': (False, False, None, 0x30, 160, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a blue potion'), 'Bee': (False, False, None, 0x0E, 10, 'I will sting your foes a few times', 'and the sting buddy', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has mad bee again', 'a bee'), 'Small Heart': (False, False, None, 0x42, 10, 'Just a little\npiece of love!', 'and the heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart'), + 'Fairy': (False, False, None, 0x5A, 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/Main.py b/Main.py index d77f6f58..2db44f17 100644 --- a/Main.py +++ b/Main.py @@ -23,7 +23,7 @@ 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 sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops, fill_specific_items from Utils import output_path, parse_player_names @@ -304,7 +304,7 @@ def main(args, seed=None, fish=None): customize_shops(world, player) if args.algorithm in ['balanced', 'equitable']: balance_money_progression(world) - ensure_good_pots(world, True) + ensure_good_items(world, True) if args.print_custom_yaml: world.settings.record_item_placements(world) diff --git a/PotShuffle.py b/PotShuffle.py index fd59f6cc..ec6536ef 100644 --- a/PotShuffle.py +++ b/PotShuffle.py @@ -1041,11 +1041,12 @@ class PotSecretTable(object): 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) + 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 diff --git a/Regions.py b/Regions.py index 23dcf79b..203a564a 100644 --- a/Regions.py +++ b/Regions.py @@ -1179,222 +1179,224 @@ flooded_keys_reverse = { 'Swamp Palace - Trench 1 Pot Key': 'Trench 1 Switch', 'Swamp Palace - Trench 2 Pot Key': 'Trench 2 Switch' } -location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), - 'Bottle Merchant': (0x2eb18, 0x186339, False, 'with a merchant'), - 'Flute Spot': (0x18014a, 0x18633d, False, 'underground'), - 'Sunken Treasure': (0x180145, 0x186354, False, 'underwater'), - 'Purple Chest': (0x33d68, 0x186359, False, 'from a box'), - "Blind's Hideout - Top": (0xeb0f, 0x1862e3, False, 'in a basement'), - "Blind's Hideout - Left": (0xeb12, 0x1862e6, False, 'in a basement'), - "Blind's Hideout - Right": (0xeb15, 0x1862e9, False, 'in a basement'), - "Blind's Hideout - Far Left": (0xeb18, 0x1862ec, False, 'in a basement'), - "Blind's Hideout - Far Right": (0xeb1b, 0x1862ef, False, 'in a basement'), - "Link's Uncle": (0x2df45, 0x18635f, False, 'with your uncle'), - 'Secret Passage': (0xe971, 0x186145, False, 'near your uncle'), - 'King Zora': (0xee1c3, 0x186360, False, 'at a high price'), - "Zora's Ledge": (0x180149, 0x186358, False, 'near Zora'), - 'Waterfall Fairy - Left': (0xe9b0, 0x186184, False, 'near a fairy'), - 'Waterfall Fairy - Right': (0xe9d1, 0x1861a5, False, 'near a fairy'), - "King's Tomb": (0xe97a, 0x18614e, False, 'alone in a cave'), - 'Floodgate Chest': (0xe98c, 0x186160, False, 'in the dam'), - "Link's House": (0xe9bc, 0x186190, False, 'in your home'), - 'Kakariko Tavern': (0xe9ce, 0x1861a2, False, 'in the bar'), - 'Chicken House': (0xe9e9, 0x1861bd, False, 'near poultry'), - "Aginah's Cave": (0xe9f2, 0x1861c6, False, 'with Aginah'), - "Sahasrahla's Hut - Left": (0xea82, 0x186256, False, 'near the elder'), - "Sahasrahla's Hut - Middle": (0xea85, 0x186259, False, 'near the elder'), - "Sahasrahla's Hut - Right": (0xea88, 0x18625c, False, 'near the elder'), - 'Sahasrahla': (0x2f1fc, 0x186365, False, 'with the elder'), - 'Kakariko Well - Top': (0xea8e, 0x186262, False, 'in a well'), - 'Kakariko Well - Left': (0xea91, 0x186265, False, 'in a well'), - 'Kakariko Well - Middle': (0xea94, 0x186268, False, 'in a well'), - 'Kakariko Well - Right': (0xea97, 0x18626b, False, 'in a well'), - 'Kakariko Well - Bottom': (0xea9a, 0x18626e, False, 'in a well'), - 'Blacksmith': (0x18002a, 0x186366, False, 'with the smith'), - 'Magic Bat': (0x180015, 0x18635e, False, 'with the bat'), - 'Sick Kid': (0x339cf, 0x186367, False, 'with the sick'), - 'Hobo': (0x33e7d, 0x186368, False, 'with the hobo'), - 'Lost Woods Hideout': (0x180000, 0x186348, False, 'near a thief'), - 'Lumberjack Tree': (0x180001, 0x186349, False, 'in a hole'), - 'Cave 45': (0x180003, 0x18634b, False, 'alone in a cave'), - 'Graveyard Cave': (0x180004, 0x18634c, False, 'alone in a cave'), - 'Checkerboard Cave': (0x180005, 0x18634d, False, 'alone in a cave'), - 'Mini Moldorm Cave - Far Left': (0xeb42, 0x186316, False, 'near Moldorms'), - 'Mini Moldorm Cave - Left': (0xeb45, 0x186319, False, 'near Moldorms'), - 'Mini Moldorm Cave - Right': (0xeb48, 0x18631c, False, 'near Moldorms'), - 'Mini Moldorm Cave - Far Right': (0xeb4b, 0x18631f, False, 'near Moldorms'), - 'Mini Moldorm Cave - Generous Guy': (0x180010, 0x18635a, False, 'near Moldorms'), - 'Ice Rod Cave': (0xeb4e, 0x186322, False, 'in a frozen cave'), - 'Bonk Rock Cave': (0xeb3f, 0x186313, False, 'alone in a cave'), - 'Library': (0x180012, 0x18635c, False, 'near books'), - 'Potion Shop': (0x180014, 0x18635d, False, 'near potions'), - 'Lake Hylia Island': (0x180144, 0x186353, False, 'on an island'), - 'Maze Race': (0x180142, 0x186351, False, 'at the race'), - 'Desert Ledge': (0x180143, 0x186352, False, 'in the desert'), - 'Desert Palace - Big Chest': (0xe98f, 0x186163, False, 'in Desert Palace'), - 'Desert Palace - Torch': (0x180160, 0x186362, False, 'in Desert Palace'), - 'Desert Palace - Map Chest': (0xe9b6, 0x18618a, False, 'in Desert Palace'), - 'Desert Palace - Compass Chest': (0xe9cb, 0x18619f, False, 'in Desert Palace'), - 'Desert Palace - Big Key Chest': (0xe9c2, 0x186196, False, 'in Desert Palace'), - 'Desert Palace - Boss': (0x180151, 0x18633f, False, 'with Lanmolas'), - 'Eastern Palace - Compass Chest': (0xe977, 0x18614b, False, 'in Eastern Palace'), - 'Eastern Palace - Big Chest': (0xe97d, 0x186151, False, 'in Eastern Palace'), - 'Eastern Palace - Cannonball Chest': (0xe9b3, 0x186187, False, 'in Eastern Palace'), - 'Eastern Palace - Big Key Chest': (0xe9b9, 0x18618d, False, 'in Eastern Palace'), - 'Eastern Palace - Map Chest': (0xe9f5, 0x1861c9, False, 'in Eastern Palace'), - 'Eastern Palace - Boss': (0x180150, 0x18633e, False, 'with the Armos'), - 'Master Sword Pedestal': (0x289b0, 0x186369, False, 'at the pedestal'), - 'Hyrule Castle - Boomerang Chest': (0xe974, 0x186148, False, 'in Hyrule Castle'), - 'Hyrule Castle - Map Chest': (0xeb0c, 0x1862e0, False, 'in Hyrule Castle'), - "Hyrule Castle - Zelda's Chest": (0xeb09, 0x1862dd, False, 'in Hyrule Castle'), - 'Sewers - Dark Cross': (0xe96e, 0x186142, False, 'in the sewers'), - 'Sewers - Secret Room - Left': (0xeb5d, 0x186331, False, 'in the sewers'), - 'Sewers - Secret Room - Middle': (0xeb60, 0x186334, False, 'in the sewers'), - 'Sewers - Secret Room - Right': (0xeb63, 0x186337, False, 'in the sewers'), - 'Sanctuary': (0xea79, 0x18624d, False, 'in Sanctuary'), - 'Castle Tower - Room 03': (0xeab5, 0x186289, False, 'in Castle Tower'), - 'Castle Tower - Dark Maze': (0xeab2, 0x186286, False, 'in Castle Tower'), - 'Old Man': (0xf69fa, 0x186364, False, 'with the old man'), - 'Spectacle Rock Cave': (0x180002, 0x18634a, False, 'alone in a cave'), - 'Paradox Cave Lower - Far Left': (0xeb2a, 0x1862fe, False, 'in a cave with seven chests'), - 'Paradox Cave Lower - Left': (0xeb2d, 0x186301, False, 'in a cave with seven chests'), - 'Paradox Cave Lower - Right': (0xeb30, 0x186304, False, 'in a cave with seven chests'), - 'Paradox Cave Lower - Far Right': (0xeb33, 0x186307, False, 'in a cave with seven chests'), - 'Paradox Cave Lower - Middle': (0xeb36, 0x18630a, False, 'in a cave with seven chests'), - 'Paradox Cave Upper - Left': (0xeb39, 0x18630d, False, 'in a cave with seven chests'), - 'Paradox Cave Upper - Right': (0xeb3c, 0x186310, False, 'in a cave with seven chests'), - 'Spiral Cave': (0xe9bf, 0x186193, False, 'in Spiral Cave'), - 'Ether Tablet': (0x180016, 0x18633b, False, 'at a monolith'), - 'Spectacle Rock': (0x180140, 0x18634f, False, 'atop a rock'), - 'Tower of Hera - Basement Cage': (0x180162, 0x18633a, False, 'in Tower of Hera'), - 'Tower of Hera - Map Chest': (0xe9ad, 0x186181, False, 'in Tower of Hera'), - 'Tower of Hera - Big Key Chest': (0xe9e6, 0x1861ba, False, 'in Tower of Hera'), - 'Tower of Hera - Compass Chest': (0xe9fb, 0x1861cf, False, 'in Tower of Hera'), - 'Tower of Hera - Big Chest': (0xe9f8, 0x1861cc, False, 'in Tower of Hera'), - 'Tower of Hera - Boss': (0x180152, 0x186340, False, 'with Moldorm'), - 'Pyramid': (0x180147, 0x186356, False, 'on the Pyramid'), - 'Catfish': (0xee185, 0x186361, False, 'with a catfish'), - 'Stumpy': (0x330c7, 0x18636a, False, 'with tree boy'), - 'Digging Game': (0x180148, 0x186357, False, 'underground'), - 'Bombos Tablet': (0x180017, 0x18633c, False, 'at a monolith'), - 'Hype Cave - Top': (0xeb1e, 0x1862f2, False, 'near a bat-like man'), - 'Hype Cave - Middle Right': (0xeb21, 0x1862f5, False, 'near a bat-like man'), - 'Hype Cave - Middle Left': (0xeb24, 0x1862f8, False, 'near a bat-like man'), - 'Hype Cave - Bottom': (0xeb27, 0x1862fb, False, 'near a bat-like man'), - 'Hype Cave - Generous Guy': (0x180011, 0x18635b, False, 'with a bat-like man'), - 'Peg Cave': (0x180006, 0x18634e, False, 'alone in a cave'), - 'Pyramid Fairy - Left': (0xe980, 0x186154, False, 'near a fairy'), - 'Pyramid Fairy - Right': (0xe983, 0x186157, False, 'near a fairy'), - 'Brewery': (0xe9ec, 0x1861c0, False, 'alone in a home'), - 'C-Shaped House': (0xe9ef, 0x1861c3, False, 'alone in a home'), - 'Chest Game': (0xeda8, 0x18636b, False, 'as a prize'), - 'Bumper Cave Ledge': (0x180146, 0x186355, False, 'on a ledge'), - 'Mire Shed - Left': (0xea73, 0x186247, False, 'near sparks'), - 'Mire Shed - Right': (0xea76, 0x18624a, False, 'near sparks'), - 'Superbunny Cave - Top': (0xea7c, 0x186250, False, 'in a connection'), - 'Superbunny Cave - Bottom': (0xea7f, 0x186253, False, 'in a connection'), - 'Spike Cave': (0xea8b, 0x18625f, False, 'beyond spikes'), - 'Hookshot Cave - Top Right': (0xeb51, 0x186325, False, 'across pits'), - 'Hookshot Cave - Top Left': (0xeb54, 0x186328, False, 'across pits'), - 'Hookshot Cave - Bottom Right': (0xeb5a, 0x18632e, False, 'across pits'), - 'Hookshot Cave - Bottom Left': (0xeb57, 0x18632b, False, 'across pits'), - 'Floating Island': (0x180141, 0x186350, False, 'on an island'), - 'Mimic Cave': (0xe9c5, 0x186199, False, 'in a cave of mimicry'), - 'Swamp Palace - Entrance': (0xea9d, 0x186271, False, 'in Swamp Palace'), - 'Swamp Palace - Map Chest': (0xe986, 0x18615a, False, 'in Swamp Palace'), - 'Swamp Palace - Big Chest': (0xe989, 0x18615d, False, 'in Swamp Palace'), - 'Swamp Palace - Compass Chest': (0xeaa0, 0x186274, False, 'in Swamp Palace'), - 'Swamp Palace - Big Key Chest': (0xeaa6, 0x18627a, False, 'in Swamp Palace'), - 'Swamp Palace - West Chest': (0xeaa3, 0x186277, False, 'in Swamp Palace'), - 'Swamp Palace - Flooded Room - Left': (0xeaa9, 0x18627d, False, 'in Swamp Palace'), - 'Swamp Palace - Flooded Room - Right': (0xeaac, 0x186280, False, 'in Swamp Palace'), - 'Swamp Palace - Waterfall Room': (0xeaaf, 0x186283, False, 'in Swamp Palace'), - 'Swamp Palace - Boss': (0x180154, 0x186342, False, 'with Arrghus'), - "Thieves' Town - Big Key Chest": (0xea04, 0x1861d8, False, "in Thieves Town"), - "Thieves' Town - Map Chest": (0xea01, 0x1861d5, False, "in Thieves Town"), - "Thieves' Town - Compass Chest": (0xea07, 0x1861db, False, "in Thieves Town"), - "Thieves' Town - Ambush Chest": (0xea0a, 0x1861de, False, "in Thieves Town"), - "Thieves' Town - Attic": (0xea0d, 0x1861e1, False, "in Thieves Town"), - "Thieves' Town - Big Chest": (0xea10, 0x1861e4, False, "in Thieves Town"), - "Thieves' Town - Blind's Cell": (0xea13, 0x1861e7, False, "in Thieves Town"), - "Thieves' Town - Boss": (0x180156, 0x186344, False, 'with Blind'), - 'Skull Woods - Compass Chest': (0xe992, 0x186166, False, 'in Skull Woods'), - 'Skull Woods - Map Chest': (0xe99b, 0x18616f, False, 'in Skull Woods'), - 'Skull Woods - Big Chest': (0xe998, 0x18616c, False, 'in Skull Woods'), - 'Skull Woods - Pot Prison': (0xe9a1, 0x186175, False, 'in Skull Woods'), - 'Skull Woods - Pinball Room': (0xe9c8, 0x18619c, False, 'in Skull Woods'), - 'Skull Woods - Big Key Chest': (0xe99e, 0x186172, False, 'in Skull Woods'), - 'Skull Woods - Bridge Room': (0xe9fe, 0x1861d2, False, 'near Mothula'), - 'Skull Woods - Boss': (0x180155, 0x186343, False, 'with Mothula'), - 'Ice Palace - Compass Chest': (0xe9d4, 0x1861a8, False, 'in Ice Palace'), - 'Ice Palace - Freezor Chest': (0xe995, 0x186169, False, 'in Ice Palace'), - 'Ice Palace - Big Chest': (0xe9aa, 0x18617e, False, 'in Ice Palace'), - 'Ice Palace - Iced T Room': (0xe9e3, 0x1861b7, False, 'in Ice Palace'), - 'Ice Palace - Spike Room': (0xe9e0, 0x1861b4, False, 'in Ice Palace'), - 'Ice Palace - Big Key Chest': (0xe9a4, 0x186178, False, 'in Ice Palace'), - 'Ice Palace - Map Chest': (0xe9dd, 0x1861b1, False, 'in Ice Palace'), - 'Ice Palace - Boss': (0x180157, 0x186345, False, 'with Kholdstare'), - 'Misery Mire - Big Chest': (0xea67, 0x18623b, False, 'in Misery Mire'), - 'Misery Mire - Map Chest': (0xea6a, 0x18623e, False, 'in Misery Mire'), - 'Misery Mire - Main Lobby': (0xea5e, 0x186232, False, 'in Misery Mire'), - 'Misery Mire - Bridge Chest': (0xea61, 0x186235, False, 'in Misery Mire'), - 'Misery Mire - Spike Chest': (0xe9da, 0x1861ae, False, 'in Misery Mire'), - 'Misery Mire - Compass Chest': (0xea64, 0x186238, False, 'in Misery Mire'), - 'Misery Mire - Big Key Chest': (0xea6d, 0x186241, False, 'in Misery Mire'), - 'Misery Mire - Boss': (0x180158, 0x186346, False, 'with Vitreous'), - 'Turtle Rock - Compass Chest': (0xea22, 0x1861f6, False, 'in Turtle Rock'), - 'Turtle Rock - Roller Room - Left': (0xea1c, 0x1861f0, False, 'in Turtle Rock'), - 'Turtle Rock - Roller Room - Right': (0xea1f, 0x1861f3, False, 'in Turtle Rock'), - 'Turtle Rock - Chain Chomps': (0xea16, 0x1861ea, False, 'in Turtle Rock'), - 'Turtle Rock - Big Key Chest': (0xea25, 0x1861f9, False, 'in Turtle Rock'), - 'Turtle Rock - Big Chest': (0xea19, 0x1861ed, False, 'in Turtle Rock'), - 'Turtle Rock - Crystaroller Room': (0xea34, 0x186208, False, 'in Turtle Rock'), - 'Turtle Rock - Eye Bridge - Bottom Left': (0xea31, 0x186205, False, 'in Turtle Rock'), - 'Turtle Rock - Eye Bridge - Bottom Right': (0xea2e, 0x186202, False, 'in Turtle Rock'), - 'Turtle Rock - Eye Bridge - Top Left': (0xea2b, 0x1861ff, False, 'in Turtle Rock'), - 'Turtle Rock - Eye Bridge - Top Right': (0xea28, 0x1861fc, False, 'in Turtle Rock'), - 'Turtle Rock - Boss': (0x180159, 0x186347, False, 'with Trinexx'), - 'Palace of Darkness - Shooter Room': (0xea5b, 0x18622f, False, 'in Palace of Darkness'), - 'Palace of Darkness - The Arena - Bridge': (0xea3d, 0x186211, False, 'in Palace of Darkness'), - 'Palace of Darkness - Stalfos Basement': (0xea49, 0x18621d, False, 'in Palace of Darkness'), - 'Palace of Darkness - Big Key Chest': (0xea37, 0x18620b, False, 'in Palace of Darkness'), - 'Palace of Darkness - The Arena - Ledge': (0xea3a, 0x18620e, False, 'in Palace of Darkness'), - 'Palace of Darkness - Map Chest': (0xea52, 0x186226, False, 'in Palace of Darkness'), - 'Palace of Darkness - Compass Chest': (0xea43, 0x186217, False, 'in Palace of Darkness'), - 'Palace of Darkness - Dark Basement - Left': (0xea4c, 0x186220, False, 'in Palace of Darkness'), - 'Palace of Darkness - Dark Basement - Right': (0xea4f, 0x186223, False, 'in Palace of Darkness'), - 'Palace of Darkness - Dark Maze - Top': (0xea55, 0x186229, False, 'in Palace of Darkness'), - 'Palace of Darkness - Dark Maze - Bottom': (0xea58, 0x18622c, False, 'in Palace of Darkness'), - 'Palace of Darkness - Big Chest': (0xea40, 0x186214, False, 'in Palace of Darkness'), - 'Palace of Darkness - Harmless Hellway': (0xea46, 0x18621a, False, 'in Palace of Darkness'), - 'Palace of Darkness - Boss': (0x180153, 0x186341, False, 'with Helmasaur King'), - "Ganons Tower - Bob's Torch": (0x180161, 0x186363, False, "in Ganon's Tower"), - 'Ganons Tower - Hope Room - Left': (0xead9, 0x1862ad, False, "in Ganon's Tower"), - 'Ganons Tower - Hope Room - Right': (0xeadc, 0x1862b0, False, "in Ganon's Tower"), - 'Ganons Tower - Tile Room': (0xeae2, 0x1862b6, False, "in Ganon's Tower"), - 'Ganons Tower - Compass Room - Top Left': (0xeae5, 0x1862b9, False, "in Ganon's Tower"), - 'Ganons Tower - Compass Room - Top Right': (0xeae8, 0x1862bc, False, "in Ganon's Tower"), - 'Ganons Tower - Compass Room - Bottom Left': (0xeaeb, 0x1862bf, False, "in Ganon's Tower"), - 'Ganons Tower - Compass Room - Bottom Right': (0xeaee, 0x1862c2, False, "in Ganon's Tower"), - 'Ganons Tower - DMs Room - Top Left': (0xeab8, 0x18628c, False, "in Ganon's Tower"), - 'Ganons Tower - DMs Room - Top Right': (0xeabb, 0x18628f, False, "in Ganon's Tower"), - 'Ganons Tower - DMs Room - Bottom Left': (0xeabe, 0x186292, False, "in Ganon's Tower"), - 'Ganons Tower - DMs Room - Bottom Right': (0xeac1, 0x186295, False, "in Ganon's Tower"), - 'Ganons Tower - Map Chest': (0xead3, 0x1862a7, False, "in Ganon's Tower"), - 'Ganons Tower - Firesnake Room': (0xead0, 0x1862a4, False, "in Ganon's Tower"), - 'Ganons Tower - Randomizer Room - Top Left': (0xeac4, 0x186298, False, "in Ganon's Tower"), - 'Ganons Tower - Randomizer Room - Top Right': (0xeac7, 0x18629b, False, "in Ganon's Tower"), - 'Ganons Tower - Randomizer Room - Bottom Left': (0xeaca, 0x18629e, False, "in Ganon's Tower"), - 'Ganons Tower - Randomizer Room - Bottom Right': (0xeacd, 0x1862a1, False, "in Ganon's Tower"), - "Ganons Tower - Bob's Chest": (0xeadf, 0x1862b3, False, "in Ganon's Tower"), - 'Ganons Tower - Big Chest': (0xead6, 0x1862aa, False, "in Ganon's Tower"), - 'Ganons Tower - Big Key Room - Left': (0xeaf4, 0x1862c8, False, "in Ganon's Tower"), - 'Ganons Tower - Big Key Room - Right': (0xeaf7, 0x1862cb, False, "in Ganon's Tower"), - 'Ganons Tower - Big Key Chest': (0xeaf1, 0x1862c5, False, "in Ganon's Tower"), - 'Ganons Tower - Mini Helmasaur Room - Left': (0xeafd, 0x1862d1, False, "atop Ganon's Tower"), - 'Ganons Tower - Mini Helmasaur Room - Right': (0xeb00, 0x1862d4, False, "atop Ganon's Tower"), - 'Ganons Tower - Pre-Moldorm Chest': (0xeb03, 0x1862d7, False, "atop Ganon's Tower"), - 'Ganons Tower - Validation Chest': (0xeb06, 0x1862da, False, "atop Ganon's Tower"), + +lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int} +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'), @@ -1455,8 +1457,6 @@ location_table = {'Mushroom': (0x180013, 0x186338, False, 'in the woods'), 'Potion Shop - Middle': (None, None, False, 'for sale near potions'), 'Potion Shop - Right': (None, None, False, 'for sale near potions'), } - -lookup_id_to_name = {data[0]: name for name, data in location_table.items() if type(data[0]) == int} lookup_id_to_name.update(shop_table_by_location_id) lookup_name_to_id = {name: data[0] for name, data in location_table.items() if type(data[0]) == int} lookup_name_to_id.update(shop_table_by_location) diff --git a/Rom.py b/Rom.py index 18677375..26b54dbf 100644 --- a/Rom.py +++ b/Rom.py @@ -1548,16 +1548,18 @@ def patch_rom(world, rom, player, team, enemized, is_mystery=False): colorize_pots = is_mystery or (world.pottery[player] not in ['vanilla', 'lottery'] and (world.colorizepots[player] or world.pottery[player] in ['reduced', 'clustered'])) - if world.pot_contents[player].size() > 0x2800: + if world.pot_contents[player].size() > 0x11c0: raise Exception('Pot table is too big for current area') world.pot_contents[player].write_pot_data_to_rom(rom, colorize_pots) + # todo: write sprites + write_strings(rom, world, player, team) # 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 @@ -1615,7 +1617,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(0x187E40 + 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: diff --git a/Utils.py b/Utils.py index d0bbb5fc..a4e3cee2 100644 --- a/Utils.py +++ b/Utils.py @@ -6,6 +6,7 @@ import sys import xml.etree.ElementTree as ET from collections import defaultdict from math import factorial +import fileinput def int16_as_bytes(value): @@ -696,10 +697,19 @@ def check_pots(): print(f'{pot.room}#{i+1} secret: {hex(secret_vram)} tile: {hex(tile_vram)}') +def find_and_replace(): + for data_line in fileinput.input('scratch.txt', inplace=True): + if '=' in data_line: + one, two = data_line.split(' = ') + number = int(two.strip()) + print(data_line.replace(two, hex(number))) + + if __name__ == '__main__': # make_new_base2current() # read_entrance_data(old_rom=sys.argv[1]) # room_palette_data(old_rom=sys.argv[1]) # extract_data_from_us_rom(sys.argv[1]) # extract_data_from_jp_rom(sys.argv[1]) - check_pots() + # check_pots() + find_and_replace() diff --git a/source/dungeon/EnemyList.py b/source/dungeon/EnemyList.py index cd5bb518..37e5e627 100644 --- a/source/dungeon/EnemyList.py +++ b/source/dungeon/EnemyList.py @@ -1,3 +1,4 @@ +from collections import defaultdict import math import typing @@ -6,11 +7,17 @@ try: except ImportError: from enum import IntFlag as FastEnum +import RaceRandom as random +from BaseClasses import Location, LocationType +from Items import ItemFactory +from Utils import snes_to_pc, pc_to_snes, int16_as_bytes from source.logic.Rule import RuleFactory +# todo: bosses shifted coordinates + class EnemyStats: - def __init__(self, sprite, static, drop_flag=False, prize_pack=0, sub_type=0): + def __init__(self, sprite, static, drop_flag=False, prize_pack: typing.Union[tuple, int] = 0, sub_type=0): self.sprite = sprite self.sub_type = sub_type self.static = static @@ -21,11 +28,16 @@ class EnemyStats: class EnemySprite(FastEnum): - CorrectPullSwitch = 0x04, + Raven = 0x00 + Vulture = 0x01 + CorrectPullSwitch = 0x04 WrongPullSwitch = 0x06 Octorok = 0x08 Moldorm = 0x09 + Octorok4Way = 0x0a Cucco = 0x0b + Buzzblob = 0x0d + Snapdragon = 0x0e Octoballoon = 0x0f OctoballoonBaby = 0x10 @@ -90,16 +102,20 @@ class EnemySprite(FastEnum): BombGuard = 0x4a GreenKnifeGuard = 0x4b Geldman = 0x4c + Toppo = 0x4d Popo = 0x4e Popo2 = 0x4f - + + ArmosStatue = 0x51 + KingZora = 0x52 ArmosKnight = 0x53 Lanmolas = 0x54 + FireballZora = 0x55 Zora = 0x56 DesertStatue = 0x57 Crab = 0x58 LostWoodsBird = 0x59 - LostWoodsSquirrel =0x5a + LostWoodsSquirrel = 0x5a SparkCW = 0x5b SparkCCW = 0x5c RollerVerticalUp = 0x5d @@ -159,8 +175,8 @@ class EnemySprite(FastEnum): Pengator = 0x99 Kyameron = 0x9a Wizzrobe = 0x9b - Zoro = 0x9c - Babasu = 0x9d + Zoro = 0x9c # babasu horizontal? + Babasu = 0x9d # babasu vertical? GroveOstritch = 0x9e GroveRabbit = 0x9f GroveBird = 0xa0 @@ -171,6 +187,10 @@ class EnemySprite(FastEnum): BlueZazak = 0xa5 RedZazak = 0xa6 Stalfos = 0xa7 + GreenZirro = 0xa8 + BlueZirro = 0xa9 + Pikit = 0xaa + CrystalMaiden = 0xab # ... OW OldMan = 0xad PipeDown = 0xae @@ -179,9 +199,12 @@ class EnemySprite(FastEnum): PipeLeft = 0xb1 GoodBee = 0xb2 PedestalPlaque = 0xb3 + PurpleChest = 0xb4 BombShopGuy = 0xb5 + Kiki = 0xb6 BlindMaiden = 0xb7 - + BullyPinkBall = 0xb9 + Whirlpool = 0xba Shopkeeper = 0xbb Drunkard = 0xbc @@ -206,14 +229,18 @@ class EnemySprite(FastEnum): Lynel = 0xd0 BunnyBeam = 0xd1 FloppingFish = 0xd2 - Stal = 0xd3 + Stal = 0xd3 # alive skull rock? + DiggingGameNPC = 0xd5 Ganon = 0xd6 Faerie = 0xe3 SmallKey = 0xe4 + FakeMasterSword = 0xe8 MagicShopAssistant = 0xe9 HeartPiece = 0xeb + SomariaPlatform = 0xed CastleMantle = 0xee + MedallionTablet = 0xf2 class SpriteType(FastEnum): @@ -222,7 +249,7 @@ class SpriteType(FastEnum): def init_enemy_stats(): - enemy_stats = { + stats = { EnemySprite.CorrectPullSwitch: EnemyStats(EnemySprite.CorrectPullSwitch, True), EnemySprite.WrongPullSwitch: EnemyStats(EnemySprite.WrongPullSwitch, True), EnemySprite.Octorok: EnemyStats(EnemySprite.Octorok, False, True, 2), @@ -417,6 +444,22 @@ def init_enemy_stats(): EnemySprite.CastleMantle: EnemyStats(EnemySprite.CastleMantle, True), } + 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): @@ -432,15 +475,36 @@ class Sprite(object): self.drops_item = drops_item self.drop_item_kind = drop_item_kind + self.location = None + + def copy(self): + return 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) + + 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 + # map of super_tile to list of Sprite objects: vanilla_sprites = {} +enemy_stats = {} -def create_sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, region=None, drops_item=False, drop_item_kind=None): +def create_sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, region=None, + drops_item=False, drop_item_kind=None): if super_tile not in vanilla_sprites: vanilla_sprites[super_tile] = [] - vanilla_sprites[super_tile].append(Sprite(kind, sub_type, layer, tile_x, tile_y, + vanilla_sprites[super_tile].append(Sprite(super_tile, kind, sub_type, layer, tile_x, tile_y, region, drops_item, drop_item_kind)) @@ -672,22 +736,22 @@ def init_vanilla_sprites(): create_sprite(0x0027, EnemySprite.SparkCW, 0x00, 0, 0x0f, 0x06, 'Hera Big Chest Landing') create_sprite(0x0027, EnemySprite.Kondongo, 0x00, 0, 0x05, 0x0e, 'Hera 4F') create_sprite(0x0027, EnemySprite.Kondongo, 0x00, 0, 0x04, 0x16, 'Hera 4F') - create_sprite(0x0028, EnemySprite.Kyameron, 0x00, 0, 0x0a, 0x06, 'Hera 4F') + create_sprite(0x0028, EnemySprite.Kyameron, 0x00, 0, 0x0a, 0x06, 'Swamp Entrance') create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x08, 0x08, 'Swamp Entrance') create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x0b, 0x0a, 'Swamp Entrance') create_sprite(0x0028, EnemySprite.Hover, 0x00, 0, 0x07, 0x0d, 'Swamp Entrance') create_sprite(0x0028, EnemySprite.SpikeBlock, 0x00, 0, 0x08, 0x10, 'Swamp Entrance') create_sprite(0x0029, EnemySprite.Mothula, 0x00, 0, 0x18, 0x16) create_sprite(0x0029, 0x07, SpriteType.Overlord, 0, 0x07, 0x16) - create_sprite(0x002a, EnemySprite.CrystalSwitch, 0x00, 0, 0x10, 0x17) - create_sprite(0x002a, EnemySprite.Bumper, 0x00, 0, 0x0f, 0x0f) + 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, 'PoD Arena Main') + create_sprite(0x002b, EnemySprite.CrystalSwitch, 0x00, 0, 0x0a, 0x11) create_sprite(0x002b, EnemySprite.Statue, 0x00, 0, 0x0a, 0x0a) create_sprite(0x002b, EnemySprite.RedBari, 0x00, 0, 0x07, 0x17, 'PoD Map Balcony') create_sprite(0x002b, EnemySprite.Faerie, 0x00, 0, 0x16, 0x17) @@ -776,9 +840,9 @@ def init_vanilla_sprites(): 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 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 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',) @@ -1920,6 +1984,8 @@ def init_vanilla_sprites(): create_sprite(0x0126, EnemySprite.Faerie, 0x00, 0, 0x08, 0x16) create_sprite(0x0126, EnemySprite.HeartPiece, 0x00, 0, 0x1c, 0x14) create_sprite(0x0127, EnemySprite.HeartPiece, 0x00, 0, 0x07, 0x16) + global enemy_stats + enemy_stats = init_enemy_stats() def kill_rules(world, player, stats): @@ -1984,6 +2050,132 @@ def kill_rules(world, player, stats): return defeat_rules +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.multiworld_count = 0 + + 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: + 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 + for sprite in self.room_map[room]: + data = sprite.sprite_data() + rom.write_bytes(data_pointer + list_offset, data) + list_offset + len(data) + rom.write_byte(data_pointer, 0xff) + data_pointer += list_offset + 1 + 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 setup_enemy_locations(world, player): + world.enemy_list[player] = EnemyTable() + for super_tile, enemy_list in vanilla_sprites.items(): + for index, sprite in enumerate(enemy_list): + # if sprite.drops_item and sprite.drop_item_kind == 0xe4: + # # normal key drops + # pass + my_sprite = sprite.copy() + world.enemy_list[player].room_map[super_tile].append() + + if valid_drop_location(my_sprite, world, player): + create_drop_location(my_sprite, index, super_tile, world, player) + + +def valid_drop_location(sprite, world, player): + if world.dropshuffle[player] == 'underworld': + if sprite.drops_item and sprite.drop_item_kind == 0xe4: + # already has a location -- hook it up? + return False + else: + stat = enemy_stats[sprite.kind] + return not stat.static and stat.drop_flag + + +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) + 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 an enemy"} {"in" if modifier else "near"} {parent.hint_text}' + drop_location = Location(player, f'{region_name} {descriptor}', address, hint_text=hint_text, parent=parent) + world.dynamic_locations.append(drop_location) + drop_location.drop = sprite + sprite.location = drop_location + drop_location.type = LocationType.Drop + + +# 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.enemy_list[player].room_map.items(): + for sprite in enemy_list: + if sprite.drops_item and sprite.drop_item_kind == 0xe4: + continue + else: + stat = enemy_stats[sprite.kind] + if not stat.static and 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)) + + def or_rule(*rules): return RuleFactory.disj(rules) @@ -2126,3 +2318,218 @@ def can_shoot_arrows(world, player): def can_use_bombs(world, player): return or_rule(RuleFactory.static_rule(not world.bombag[player]), has('Bomb Upgrade (+10)', 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', + + 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', + 0x7a: 'Agahnim', + 0x7c: 'FloatingSkull', + 0x7d: 'BigSpike', + 0x7e: 'FirebarCW', + 0x7f: 'FirebarCCW', + 0x80: 'Firesnake', + 0x81: 'Hover', + 0x82: 'AntiFairyCircle', + 0x83: 'GreenEyegoreMimic', + 0x84: 'RedEyegoreMimic', + 0x85: 'YellowStalfos', # falling stalfos that shoots head + 0x86: 'Kondongo', + 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 + 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? + 0xd5: 'DiggingGameNPC', + 0xd6: 'Ganon', + + 0xe3: 'Faerie', + 0xe4: 'SmallKey', + 0xe8: 'FakeMasterSword', + 0xe9: 'MagicShopAssistant', + 0xeb: 'HeartPiece', + 0xed: 'SomariaPlatform', + 0xee: 'CastleMantle', + 0xf2: 'MedallionTablet', +} \ No newline at end of file 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..13a4f7b7 --- /dev/null +++ b/source/dungeon/RoomHeader.py @@ -0,0 +1,326 @@ + + +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.sprite_sheet = byte_array[3] + + def write_to_rom(self, rom, base_address): + rom.write_byte(base_address + self.room_id*14 + 3, self.sprite_sheet) + + +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..ceff9405 100644 --- a/source/dungeon/RoomList.py +++ b/source/dungeon/RoomList.py @@ -1,3 +1,9 @@ +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 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/Enemizer.py b/source/enemizer/Enemizer.py new file mode 100644 index 00000000..dfea8102 --- /dev/null +++ b/source/enemizer/Enemizer.py @@ -0,0 +1,253 @@ +import RaceRandom as random + +from source.dungeon.EnemyList import SpriteType +from source.enemizer.SpriteSheets import uw_sub_group_choices, setup_required_dungeon_groups + +water_rooms = { + 0x16, 0x28, 0x34, 0x36, 0x38, 0x46, 0x66 +} # these room need to be locked on the gfx ID : 17 + +# todo: task list +# anti-fairy shutter logic +# check cucco, implement flag for certain immune enemies that are okay in shutter rooms +# Room 0x16 (sprites 4,5,6 need to be water but 0-3 don't) +# + +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, 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: {3, 4, 8}, 0x7d: {4, 5, 6, 7, 8}, 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} +} + +water_sprites = { + 0x16: {4, 5, 6}, 0x28: {0, 1, 2, 3}, 0x34: {0, 1, 2}, 0x36: {1, 2, 5, 7, 8}, 0x38: {0, 1, 2, 4, 5, 6}, +} + +# not really shutters: only tiles: +# 0xb6 TR Tile, TR Pokey 1, Chain chomp? +# 0x87 hera tile room? +# 0x3d gt minihelma? +# 0x8d gt tile room? +# 0x96 gt torch cross? + + +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 uw_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, 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 + water_needed = room_id in water_rooms + + for sheet in data_tables.sprite_sheets.values(): + 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: + 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 water_needed: + if water_groups: + match_any_room_groups.update(water_groups) + for i in range(0, 4): + if water_sub_groups[i]: + match_any_sub_groups[i].update(water_sub_groups[i]) + else: # exclude water stuff + exclude_all_groups.update(water_groups) + for i in range(0, 4): + exclude_all_sub_groups[i].update(water_sub_groups[i]) + + 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: + str(sheet) + if match_all_room_groups and sheet 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 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 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 uw_candidate_sprites(data_tables): + requirements = data_tables.sprite_requirements + uw_sprite_candidates = [] + uw_sheet_candidates = [] + + 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 + if not r.static and r.uw_valid 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]) + uw_sprite_candidates.append(k) + + for num in range(65, 124): + sheet = data_tables.sprite_sheets[num] + 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 + uw_sheet_candidates.append(sheet) + + return uw_sprite_candidates, uw_sheet_candidates + + +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_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: + sprite_table[idx] = sprite + return sprite_table + + +# RandomizeRooms(optionFlags); +def randomize_underworld_rooms(data_tables): + # RoomCollection.RandomizeRoomSpriteGroups + # randomize room sprite sheets + + specific = setup_specific_requirements(data_tables) + uw_candidates, uw_sheets = uw_candidate_sprites(data_tables) + for room_id in range(0, 0x128): + if room_id in {0, 1, 3, 6, 7, 0xd, 0x14, 0x1c, 0x20, 0x29, 0x30, 0x33, + 0x4d, 0x5a, 0x7F, 0x90, 0xa4, 0xac, 0xc8, 0xde}: + continue + if room_id not in data_tables.uw_enemy_table.room_map: + continue + # sprite_reqs = data_tables.sprite_requirements + randomizeable_sprites = get_randomize_able_sprites(room_id, data_tables) + if randomizeable_sprites: + candidate_sheets = get_possible_sheets(room_id, data_tables, specific, uw_sheets) + 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) + if room_id in water_rooms: + water_sprites = [x for x in candidate_sprites if x.water_only] + for i, sprite in randomizeable_sprites.items(): + chosen = random.choice(water_sprites) + sprite.kind = chosen.sprite + else: + # todo: stal sprites + for i, sprite in randomizeable_sprites.items(): + if sprite.drops_item: + key_sprites = [x for x in candidate_sprites if x.good_for_key_drop() and not x.water_only] + chosen = random.choice(key_sprites) + elif room_id in shutter_sprites and i in shutter_sprites[room_id]: + killable_sprite = [x for x in candidate_sprites if x.good_for_shutter() and not x.water_only] + chosen = random.choice(killable_sprite) + else: + non_water = [x for x in candidate_sprites if not x.water_only] + chosen = random.choice(non_water) + sprite.kind = chosen.sprite + # done with sprites + # done with rooms \ No newline at end of file diff --git a/source/enemizer/EnemizerTestHarness.py b/source/enemizer/EnemizerTestHarness.py new file mode 100644 index 00000000..09395da1 --- /dev/null +++ b/source/enemizer/EnemizerTestHarness.py @@ -0,0 +1,19 @@ +from source.dungeon.EnemyList import enemy_names, SpriteType +from source.enemizer.Enemizer import randomize_underworld_rooms +from source.enemizer.SpriteSheets import randomize_underworld_sprite_sheets +from source.rom.DataTables import init_data_tables +import RaceRandom as random + +if __name__ == '__main__': + random.seed(42) + data_tables = init_data_tables(None, None) + + randomize_underworld_sprite_sheets(data_tables.sprite_sheets) + randomize_underworld_rooms(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 sprite.sub_type == SpriteType.Overlord: + print(f' Overlord #{i+1} {hex(sprite.kind)}:') + else: + print(f' Enemy #{i+1} {enemy_names[sprite.kind]}:') diff --git a/source/enemizer/SpriteSheets.py b/source/enemizer/SpriteSheets.py new file mode 100644 index 00000000..9b6a877a --- /dev/null +++ b/source/enemizer/SpriteSheets.py @@ -0,0 +1,630 @@ +from collections import defaultdict +import RaceRandom as random + +from source.dungeon.EnemyList import EnemySprite, enemy_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.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 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): + return self.killable and not self.static and not self.dont_use and self.uw_valid + + def good_for_key_drop(self): + return self.good_for_shutter() and self.can_drop + + def __str__(self): + return f'Req for {enemy_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), + # todo: Buzzblob kill rule for mimics + 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), + # todo: antifairy kill rule + 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).no_drop().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).affix().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).no_drop().sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.BlueBari).sub_group(0, 0x1f), + # todo: don't randomize red/blue bari in room 0x7F + SpriteRequirement(EnemySprite.TalkingTree).affix().sub_group(0, 0x15), + SpriteRequirement(EnemySprite.HardhatBeetle).sub_group(1, 0x1e), + # todo: deadrock kill rule for mimics (not sure why ice spike room...) + 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).no_drop().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).no_drop().sub_group(2, 0x12).exclude({0x10c}), + SpriteRequirement(EnemySprite.Toppo).no_drop().sub_group(3, 0x11), + SpriteRequirement(EnemySprite.Popo).sub_group(1, 0x2c), + SpriteRequirement(EnemySprite.Popo2).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().uw_skip().sub_group(2, [0xc, 0x18]), + SpriteRequirement(EnemySprite.Zora).immerse().no_drop().uw_skip().sub_group(2, 0xc).sub_group(3, 0x44), + 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).immune().sub_group(1, 0x2c).exclude(NoBeamosOrTrapRooms), + SpriteRequirement(EnemySprite.MasterSword).affix().sub_group(2, 0x37).sub_group(3, 0x36), + # these are excluded for now + SpriteRequirement(EnemySprite.DebirandoPit).skip().sub_group(0, 0x2f), + SpriteRequirement(EnemySprite.Debirando).skip().sub_group(0, 0x2f), + 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).no_drop().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).no_drop().sub_group(0, 0x1f).exclude(NoFlyingRooms), + SpriteRequirement(EnemySprite.FirebarCW).immune().sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.FirebarCCW).immune().sub_group(0, 0x1f), + # todo: don't randomize these in GT Torch Cross and TR Dark Ride? was that ever implemented? + SpriteRequirement(EnemySprite.Firesnake).immune().sub_group(0, 0x1f), + SpriteRequirement(EnemySprite.Hover).immerse().sub_group(2, 0x22).exclude(NoFlyingRooms), + # todo: leave them in swamp palace entrance... + SpriteRequirement(EnemySprite.AntiFairyCircle).skip().no_drop().sub_group(3, [0x52, 0x53]), + SpriteRequirement(EnemySprite.GreenEyegoreMimic).sub_group(2, 0x2e), + SpriteRequirement(EnemySprite.RedEyegoreMimic).sub_group(2, 0x2e), + # kodongos apparently broken? + SpriteRequirement(EnemySprite.Kondongo).skip().sub_group(2, 0x2a), + 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), + # todo: wallmaster overlords + SpriteRequirement(EnemySprite.Wallmaster).immune().ow_skip().sub_group(2, 0x23) + .allow(WallmasterValidRooms), + SpriteRequirement(EnemySprite.StalfosKnight).sub_group(1, 0x10).exclude({0x10c}), + SpriteRequirement(EnemySprite.HelmasaurKing).exalt().sub_group(2, 0x3a).sub_group(3, 0x3e), + SpriteRequirement(EnemySprite.Bumper).affix().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), + # todo: leave them in swamp palace entrance... + SpriteRequirement(EnemySprite.Wizzrobe).sub_group(2, [0x25, 0x29]), + SpriteRequirement(EnemySprite.Zoro).no_drop().sub_group(1, 0x20), + SpriteRequirement(EnemySprite.Babasu).no_drop().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).no_drop().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).no_drop().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).immerse().no_drop().sub_group(3, 0x19), + SpriteRequirement(EnemySprite.Lynel).sub_group(3, 0x14), + SpriteRequirement(EnemySprite.BunnyBeam).affix(), + SpriteRequirement(EnemySprite.FloppingFish).uw_skip().immune(), + SpriteRequirement(EnemySprite.Stal).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).affix(), + 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.MedallionTablet).affix().sub_group(2, 0x12), + + # todo: overlord requirements + 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}), + 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 + + +vanilla_sheets = [ + (0x00, 0x49, 0x00, 0x00), (0x46, 0x49, 0x0C, 0x1D), (0x48, 0x49, 0x13, 0x1D), (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, 0x1D), (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): + + 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, 0x11a, 0x11c, 0x122}) + sheets[did(7)].add_sprite_to_sheet([75, 77, 57, 54], {0x8, 0x2c, 0x114, 0x115}) # 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, 0x112, 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], [0x3e, 0x9f]), # babasu spawners + ([31, None, None, None], [0x7f]), # force baris + ([None, None, 35, None], [0x39, 0x49, 0x56, 0x57, 0x68, 0x8d]), # wallmasters + # bumpers - why the split - because of other requirements - + ([None, None, None, (82, 83)], [0x17, 0x2a, 0x4c, 0x59, 0x67, 0x68, 0x7e, 0x8b, 0xeb, 0xfb]), + # crystal switches - split for some reason + ([None, None, None, (82, 83)], [0xb, 0x13, 0x1b, 0x1e, 0x2a, 0x2b, 0x31, 0x3e, 0x5b, 0x6b, 0x77, 0xb7, 0x8b, 0x91, 0x92, 0x9b, 0x9d, 0xa1, 0xab, 0xb6, 0xbf, 0xc1, 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, 0x57, 0x6b, 0x7b]), + + # non-optional + ([None, None, None, 82], [0x2, 0x58, 0x64, 0x8c, 0x10b]), # pull switches + ([None, None, None, 82], [0x1a, 0x3d, 0x44, 0x56, 0x5e, 0x7c, 0x95, 0xc3]), # collasping bridges + ([None, None, None, 83], [0x4, 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... + ] + + # find home for the free_sheet_reqs + for pair in free_sheet_reqs: + groups, room_list = pair + possible_sheets = [] + found_match = False + for num in range(65, 124): + 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 + break + if valid: + possible_sheets.append(sheet) + if not found_match: + 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) + + +# RandomizeRooms(optionFlags); + # roomCollection.LoadRooms() + # roomCollection.RandomizeRoomSpriteGroups(spriteGroupCollection, optionFlags); + # more stuff +uw_sub_group_choices = { + 0: [22, 31, 47, 14], # 70, 72 for guards + 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] +} + + +def randomize_underworld_sprite_sheets(sheets): + setup_required_dungeon_groups(sheets) + + 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]: + sheet.locked[1] = True + sheet.sub_groups[1] = random.choice([13, 73]) + for idx in range(0, 4): + if not sheet.locked[idx]: + sheet.sub_groups[idx] = random.choice(uw_sub_group_choices[idx]) + # lock the group? + + + + + + + + + + + + + + + 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/item/FillUtil.py b/source/item/FillUtil.py index 040497a2..82581a76 100644 --- a/source/item/FillUtil.py +++ b/source/item/FillUtil.py @@ -2,6 +2,7 @@ import RaceRandom as random import logging from collections import defaultdict +from source.dungeon.EnemyList import enemy_stats from source.item.District import resolve_districts from BaseClasses import PotItem, PotFlags from DoorShuffle import validate_vanilla_reservation @@ -421,19 +422,20 @@ 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 - vanilla_mapping = { 'Green Pendant': ['Eastern Palace - Prize'], 'Red Pendant': ['Desert Palace - Prize', 'Tower of Hera - Prize'], diff --git a/source/rom/DataTables.py b/source/rom/DataTables.py new file mode 100644 index 00000000..b05b319c --- /dev/null +++ b/source/rom/DataTables.py @@ -0,0 +1,45 @@ +from Utils import snes_to_pc + +from source.dungeon.EnemyList import EnemyTable, init_vanilla_sprites, vanilla_sprites +from source.dungeon.RoomHeader import init_room_headers +from source.dungeon.RoomList import Room0127 +from source.enemizer.SpriteSheets import init_sprite_sheets, init_sprite_requirements + + +class DataTables: + def __init__(self): + self.room_headers = None + self.room_list = None # todo: for boss rando + self.sprite_sheets = None + self.uw_enemy_table = None + self.ow_enemy_tables = None # todo : data migration + self.pot_secret_table = None # todo : migrate storage + + # associated data + self.sprite_requirements = None + + def write_to_rom(self, rom): + for header in self.room_headers.values(): + header.write_to_rom(rom, snes_to_pc(0x30DA00)) # new header table, bank30, tables.asm + # room list + for sheet in self.sprite_sheets.values(): + sheet.write_to_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) + + +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() + 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()) + return data_tables 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