diff --git a/Adjuster.py b/Adjuster.py index a6e964a8..9e003b3f 100755 --- a/Adjuster.py +++ b/Adjuster.py @@ -37,6 +37,7 @@ def main(): parser.add_argument('--uw_palettes', default='default', choices=['default', 'random', 'blackout']) parser.add_argument('--reduce_flashing', help='Reduce some in-game flashing.', action='store_true') parser.add_argument('--shuffle_sfx', help='Shuffles sound sfx', action='store_true') + parser.add_argument('--msu_resume', help='Enable MSU resume', action='store_true') parser.add_argument('--sprite', help='''\ Path to a sprite sheet to use for Link. Needs to be in binary format and have a length of 0x7000 (28672) bytes, diff --git a/AdjusterMain.py b/AdjusterMain.py index 7d7e2f6e..c6f19680 100644 --- a/AdjusterMain.py +++ b/AdjusterMain.py @@ -2,8 +2,15 @@ import os import time import logging +try: + import bps.apply + import bps.io +except ImportError: + raise Exception('Could not load BPS module') + from Utils import output_path from Rom import LocalRom, apply_rom_settings +from source.tools.BPS import bps_read_vlv def adjust(args): @@ -25,7 +32,8 @@ def adjust(args): args.sprite = None apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.fastmenu, args.disablemusic, - args.sprite, args.ow_palettes, args.uw_palettes, args.reduce_flashing, args.shuffle_sfx) + args.sprite, args.ow_palettes, args.uw_palettes, args.reduce_flashing, args.shuffle_sfx, + args.msu_resume) output_path.cached_path = args.outputpath rom.write_to_file(output_path('%s.sfc' % outfilebase)) @@ -34,3 +42,38 @@ def adjust(args): logger.debug('Total Time: %s', time.process_time() - start) return args + + +def patch(args): + start = time.process_time() + logger = logging.getLogger('') + logger.info('Patching ROM.') + + outfile_base = os.path.basename(args.patch)[:-4] + + rom = LocalRom(args.baserom, False) + if os.path.isfile(args.baserom): + rom.verify_base_rom() + orig_buffer = rom.buffer.copy() + with open(args.patch, 'rb') as stream: + stream.seek(4) # skip BPS1 + bps_read_vlv(stream) # skip source size + target_length = bps_read_vlv(stream) + rom.buffer.extend(bytearray([0x00] * (target_length - len(rom.buffer)))) + stream.seek(0) + bps.apply.apply_to_bytearrays(bps.io.read_bps(stream), orig_buffer, rom.buffer) + + if not hasattr(args, "sprite"): + args.sprite = None + + apply_rom_settings(rom, args.heartbeep, args.heartcolor, args.quickswap, args.fastmenu, args.disablemusic, + args.sprite, args.ow_palettes, args.uw_palettes, args.reduce_flashing, args.shuffle_sfx, + args.msu_resume) + + output_path.cached_path = args.outputpath + rom.write_to_file(output_path('%s.sfc' % outfile_base)) + + logger.info('Done. Enjoy.') + logger.debug('Total Time: %s', time.process_time() - start) + + return args diff --git a/BaseClasses.py b/BaseClasses.py index d3be01e2..5073d6cd 100644 --- a/BaseClasses.py +++ b/BaseClasses.py @@ -15,6 +15,7 @@ from source.classes.BabelFish import BabelFish from Utils import int16_as_bytes from Tables import normal_offset_table, spiral_offset_table, multiply_lookup, divisor_lookup from RoomData import Room +from source.dungeon.RoomObject import RoomObject class World(object): @@ -62,7 +63,6 @@ class World(object): self.lock_aga_door_in_escape = False self.save_and_quit_from_boss = True self.accessibility = accessibility.copy() - self.initial_overworld_flags = {} self.fix_skullwoods_exit = {} self.fix_palaceofdarkness_exit = {} self.fix_trock_exit = {} @@ -99,6 +99,7 @@ class World(object): self._portal_cache = {} self.sanc_portal = {} self.fish = BabelFish() + self.pot_contents = {} for player in range(1, players + 1): # If World State is Retro, set to Open and set Retro flag @@ -120,7 +121,6 @@ class World(object): set_player_attr('ganon_at_pyramid', True) set_player_attr('ganonstower_vanilla', True) set_player_attr('sewer_light_cone', self.mode[player] == 'standard') - set_player_attr('initial_overworld_flags', [0] * 0x80) set_player_attr('fix_trock_doors', self.shuffle[player] != 'vanilla' or self.is_tile_swapped(0x05, player)) set_player_attr('fix_skullwoods_exit', self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple'] or self.doorShuffle[player] not in ['vanilla']) set_player_attr('fix_palaceofdarkness_exit', self.shuffle[player] not in ['vanilla', 'simple', 'restricted', 'dungeonssimple']) @@ -154,9 +154,11 @@ class World(object): set_player_attr('potshuffle', False) set_player_attr('pot_contents', None) set_player_attr('pseudoboots', False) + set_player_attr('collection_rate', False) + set_player_attr('colorizepots', False) + set_player_attr('pot_pool', {}) set_player_attr('shopsanity', False) - set_player_attr('keydropshuffle', False) set_player_attr('mixed_travel', 'prevent') set_player_attr('standardize_palettes', 'standardize') set_player_attr('force_fix', {'gt': False, 'sw': False, 'pod': False, 'tr': False}) @@ -456,6 +458,8 @@ class World(object): location.item = item item.location = location item.world = self + if location.player != item.player and location.type == LocationType.Pot: + self.pot_contents[location.player].multiworld_count += 1 if collect: self.state.collect(item, location.event, location) @@ -1361,6 +1365,8 @@ class CollectionState(object): return self.has('Fire Rod', player) or self.has('Lamp', player) def can_flute(self, player): + if self.world.mode[player] == 'standard' and not self.has('Zelda Delivered', player): + return False if any(map(lambda i: i.name in ['Ocarina', 'Ocarina (Activated)'], self.world.precollected_items)): return True lw = self.world.get_region('Kakariko Area', player) @@ -2632,10 +2638,19 @@ class Location(object): self.item_rule = lambda item: True self.player = player self.skip = False + self.type = LocationType.Normal if not crystal else LocationType.Prize + self.pot = None def can_fill(self, state, item, check_access=True): + if not self.valid_multiworld(state, item): + return False return self.always_allow(state, item) or (self.parent_region.can_fill(item) and self.item_rule(item) and (not check_access or self.can_reach(state))) + def valid_multiworld(self, state, item): + if self.type == LocationType.Pot and self.player != item.player: + return state.world.pot_contents[self.player].multiworld_count < 256 + return True + def can_reach(self, state): return self.parent_region.can_reach(state) and self.access_rule(state) @@ -2671,6 +2686,15 @@ class Location(object): return hash((self.name, self.player)) +class LocationType(FastEnum): + Normal = 0 + Prize = 1 + Logical = 2 + Shop = 3 + Pot = 4 + Drop = 5 + + class Item(object): def __init__(self, name='', advancement=False, priority=False, type=None, code=None, price=999, pedestal_hint=None, @@ -2907,17 +2931,19 @@ class Spoiler(object): 'enemy_shuffle': self.world.enemy_shuffle, 'enemy_health': self.world.enemy_health, 'enemy_damage': self.world.enemy_damage, - 'potshuffle': self.world.potshuffle, 'players': self.world.players, 'teams': self.world.teams, 'experimental': self.world.experimental, - 'keydropshuffle': self.world.keydropshuffle, + 'dropshuffle': self.world.dropshuffle, + 'pottery': self.world.pottery, + 'potshuffle': self.world.potshuffle, 'shopsanity': self.world.shopsanity, 'pseudoboots': self.world.pseudoboots, - 'triforcegoal': self.world.treasure_hunt_count, - 'triforcepool': self.world.treasure_hunt_total, + 'triforcegoal': self.world.treasure_hunt_count, + 'triforcepool': self.world.treasure_hunt_total, 'code': {p: Settings.make_code(self.world, p) for p in range(1, self.world.players + 1)} } + for p in range(1, self.world.players + 1): from ItemList import set_default_triforce if self.world.custom and p in self.world.customitemarray: @@ -2945,7 +2971,7 @@ class Spoiler(object): for player in range(1, self.world.players + 1): self.bottles[f'Waterfall Bottle ({self.world.get_player_names(player)})'] = self.world.bottle_refills[player][0] self.bottles[f'Pyramid Bottle ({self.world.get_player_names(player)})'] = self.world.bottle_refills[player][1] - + self.locations = OrderedDict() listed_locations = set() @@ -2962,11 +2988,11 @@ class Spoiler(object): listed_locations.update(cave_locations) for dungeon in self.world.dungeons: - dungeon_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.dungeon == dungeon and not loc.forced_item] + dungeon_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and loc.parent_region and loc.parent_region.dungeon == dungeon and not loc.forced_item and not loc.skip] self.locations[str(dungeon)] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in dungeon_locations]) listed_locations.update(dungeon_locations) - other_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations] + other_locations = [loc for loc in self.world.get_locations() if loc not in listed_locations and not loc.skip] if other_locations: self.locations['Other Locations'] = OrderedDict([(location.gen_name(), str(location.item) if location.item is not None else 'Nothing') for location in other_locations]) listed_locations.update(other_locations) @@ -3041,6 +3067,18 @@ class Spoiler(object): return json.dumps(out) + def mystery_meta_to_file(self, filename): + self.parse_meta() + with open(filename, 'w') as outfile: + line_width = 35 + outfile.write('ALttP Overworld Randomizer - Seed: %s\n\n' % (self.world.seed)) + for k,v in self.metadata["versions"].items(): + outfile.write((k + ' Version:').ljust(line_width) + '%s\n' % v) + for player in range(1, self.world.players + 1): + if self.world.players > 1: + outfile.write('\nPlayer %d: %s\n' % (player, self.world.get_player_names(player))) + outfile.write('Logic:'.ljust(line_width) + '%s\n' % self.metadata['logic'][player]) + def meta_to_file(self, filename): def yn(flag): return 'Yes' if flag else 'No' @@ -3048,7 +3086,7 @@ class Spoiler(object): self.parse_meta() with open(filename, 'w') as outfile: line_width = 35 - outfile.write('ALttP Entrance Randomizer - Seed: %s\n\n' % (self.world.seed)) + outfile.write('ALttP Overworld Randomizer - Seed: %s\n\n' % (self.world.seed)) for k,v in self.metadata["versions"].items(): outfile.write((k + ' Version:').ljust(line_width) + '%s\n' % v) outfile.write('Filling Algorithm:'.ljust(line_width) + '%s\n' % self.world.algorithm) @@ -3088,15 +3126,15 @@ class Spoiler(object): outfile.write('Shuffle Links:'.ljust(line_width) + '%s\n' % yn(self.metadata['shufflelinks'][player])) if self.metadata['shuffle'][player] != 'vanilla' or self.metadata['ow_mixed'][player]: outfile.write('Overworld Map:'.ljust(line_width) + '%s\n' % self.metadata['overworld_map'][player]) - if self.metadata['goal'][player] != 'trinity': - outfile.write('Pyramid Hole Pre-opened:'.ljust(line_width) + '%s\n' % self.metadata['open_pyramid'][player]) + outfile.write('Pyramid Hole Pre-opened:'.ljust(line_width) + '%s\n' % self.metadata['open_pyramid'][player]) outfile.write('Door Shuffle:'.ljust(line_width) + '%s\n' % self.metadata['door_shuffle'][player]) if self.metadata['door_shuffle'][player] != 'vanilla': outfile.write('Intensity:'.ljust(line_width) + '%s\n' % self.metadata['intensity'][player]) outfile.write('Experimental:'.ljust(line_width) + '%s\n' % yn(self.metadata['experimental'][player])) outfile.write('Dungeon Counters:'.ljust(line_width) + '%s\n' % self.metadata['dungeon_counters'][player]) - outfile.write('Pot Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['potshuffle'][player])) - outfile.write('Key Drop Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['keydropshuffle'][player])) + outfile.write('Drop Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['dropshuffle'][player])) + outfile.write('Pottery Mode:'.ljust(line_width) + '%s\n' % self.metadata['pottery'][player]) + outfile.write('Pot Shuffle (Legacy):'.ljust(line_width) + '%s\n' % yn(self.metadata['potshuffle'][player])) outfile.write('Map Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['mapshuffle'][player])) outfile.write('Compass Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['compassshuffle'][player])) outfile.write('Small Key Shuffle:'.ljust(line_width) + '%s\n' % yn(self.metadata['keyshuffle'][player])) @@ -3111,19 +3149,37 @@ class Spoiler(object): outfile.write('Starting Inventory:'.ljust(line_width)) outfile.write('\n'.ljust(line_width+1).join(self.startinventory) + '\n') + def hashes_to_file(self, filename): + with open(filename, 'r') as infile: + contents = infile.readlines() + + def insert(lines, i, value): + lines.insert(i, value) + i += 1 + return i + + idx = 2 + if self.world.players > 1: + idx = insert(contents, idx, 'Hashes:') + for player in range(1, self.world.players + 1): + if self.world.players > 1: + idx = insert(contents, idx, f'\nPlayer {player}: {self.world.get_player_names(player)}\n') + if len(self.hashes) > 0: + for team in range(self.world.teams): + player_name = self.world.player_names[player][team] + label = f"Hash - {player_name} (Team {team+1}): " if self.world.teams > 1 else 'Hash: ' + idx = insert(contents, idx, f'{label}{self.hashes[player, team]}\n') + if self.world.players > 1: + insert(contents, idx, '\n') # return value ignored here, if you want to add more lines + + with open(filename, "w") as f: + contents = "".join(contents) + f.write(contents) + def to_file(self, filename): self.parse_data() with open(filename, 'a') as outfile: line_width = 35 - if self.world.players > 1: - outfile.write('\nHashes:') - for player in range(1, self.world.players + 1): - if self.world.players > 1: - outfile.write('\nPlayer %d: %s\n' % (player, self.world.get_player_names(player))) - if len(self.hashes) > 0: - for team in range(self.world.teams): - outfile.write('%s%s\n' % (f"Hash - {self.world.player_names[player][team]} (Team {team+1}): " if self.world.teams > 1 else 'Hash: ', self.hashes[player, team])) - outfile.write('\n\nRequirements:\n\n') for dungeon, medallion in self.medallions.items(): outfile.write(f'{dungeon}:'.ljust(line_width) + '%s Medallion\n' % medallion) @@ -3220,7 +3276,7 @@ class Spoiler(object): outfile.write(f'\n\nBosses ({self.world.get_player_names(player)}):\n\n') outfile.write('\n'.join([f'{x}: {y}' for x, y in bossmap.items() if y not in ['Agahnim', 'Agahnim 2', 'Ganon']])) - def playthru_to_file(self, filename): + def playthrough_to_file(self, filename): with open(filename, 'a') as outfile: # locations: Change up location names; in the instance of a location with multiple sections, it'll try to translate the room name # items: Item names @@ -3318,15 +3374,40 @@ class PotFlags(FastEnum): Normal = 0x0 NoSwitch = 0x1 # A switch should never go here SwitchLogicChange = 0x2 # A switch can go here, but requires a logic change + Block = 0x4 # This is actually a block + LowerRegion = 0x8 # This is a pot in the lower region class Pot(object): - def __init__(self, x, y, item, room, flags = PotFlags.Normal): + def __init__(self, x, y, item, room, flags=PotFlags.Normal, obj=None): self.x = x self.y = y self.item = item self.room = room self.flags = flags + self.indicator = None # 0x80 for standing item, 0xC0 multiworld item + self.standing_item_code = None # standing item code if nay + self.obj_ref = obj + self.location = None # location back ref + + def copy(self): + obj_ref = RoomObject(self.obj_ref.address, self.obj_ref.data) if self.obj_ref else None + return Pot(self.x, self.y, self.item, self.room, self.flags, obj_ref) + + def pot_data(self): + high_byte = self.y + if self.flags & PotFlags.LowerRegion: + high_byte |= 0x20 + if self.indicator: + high_byte |= self.indicator + item = self.item if not self.indicator else self.standing_item_code + return [self.x, high_byte, item] + + def __eq__(self, other): + return self.x == other.x and self.y == other.y and self.room == other.room + + def __hash__(self): + return hash((self.x, self.y, self.room)) # byte 0: DDOO OEEE (DR, OR, ER) @@ -3344,33 +3425,43 @@ goal_mode = {"ganon": 0, "pedestal": 1, "dungeons": 2, "triforcehunt": 3, "cryst diff_mode = {"normal": 0, "hard": 1, "expert": 2} func_mode = {"normal": 0, "hard": 1, "expert": 2} -# byte 3: SKMM PIII (shop, keydrop, mixed, palettes, intensity) +# byte 3: S?MM PIII (shop, unused, mixed, palettes, intensity) +# keydrop now has it's own byte mixed_travel_mode = {"prevent": 0, "allow": 1, "force": 2} # intensity is 3 bits (reserves 4-7 levels) -# byte 4: CCCC CTTX (crystals gt, ctr2, experimental) +# new byte 4: ?DDD PPPP (unused, drop, pottery) +# dropshuffle reserves 2 bits, pottery needs 2 but reserves 2 for future modes) +pottery_mode = {'none': 0, 'keys': 2, 'lottery': 3, 'dungeon': 4, 'cave': 5, 'cavekeys': 6, 'reduced': 7, + 'clustered': 8, 'nonempty': 9} + +# byte 5: CCCC CTTX (crystals gt, ctr2, experimental) counter_mode = {"default": 0, "off": 1, "on": 2, "pickup": 3} -# byte 5: CCCC CPAA (crystals ganon, pyramid, access +# byte 6: CCCC CPAA (crystals ganon, pyramid, access access_mode = {"items": 0, "locations": 1, "none": 2} -# byte 6: BSMC BBEE (big, small, maps, compass, bosses, enemies) -boss_mode = {"none": 0, "simple": 1, "full": 2, "random": 3, "chaos": 3} -enemy_mode = {"none": 0, "shuffled": 1, "random": 2, "chaos": 2, "legacy": 3} +# byte 7: BSMC ??EE (big, small, maps, compass, bosses, enemies) +enemy_mode = {"none": 0, "shuffled": 1, "chaos": 2, "random": 2, "legacy": 3} -# byte 7: HHHD DPBS (enemy_health, enemy_dmg, potshuffle, bomb logic, shuffle links) +# byte 8: HHHD DPBS (enemy_health, enemy_dmg, potshuffle, bomb logic, shuffle links) +# potshuffle decprecated, now unused e_health = {"default": 0, "easy": 1, "normal": 2, "hard": 3, "expert": 4} e_dmg = {"default": 0, "shuffled": 1, "random": 2} -# byte 8: RRAA A??? (restrict boss mode, algorithm, ? = unused) +# byte 9: RRAA ABBB (restrict boss mode, algorithm, boss shuffle) rb_mode = {"none": 0, "mapcompass": 1, "dungeon": 2} # algorithm: algo_mode = {"balanced": 0, "equitable": 1, "vanilla_fill": 2, "dungeon_only": 3, "district": 4, 'major_only': 5} +boss_mode = {"none": 0, "simple": 1, "full": 2, "chaos": 3, 'random': 3, 'unique': 4} # additions # psuedoboots does not effect code # sfx_shuffle and other adjust items does not effect settings code +# Bump this when making changes that are not backwards compatible (nearly all of them) +settings_version = 0 + class Settings(object): @@ -3385,10 +3476,12 @@ class Settings(object): (goal_mode[w.goal[p]] << 5) | (diff_mode[w.difficulty[p]] << 3) | (func_mode[w.difficulty_adjustments[p]] << 1) | (1 if w.hints[p] else 0), - (0x80 if w.shopsanity[p] else 0) | (0x40 if w.keydropshuffle[p] else 0) - | (mixed_travel_mode[w.mixed_travel[p]] << 4) | (0x8 if w.standardize_palettes[p] == "original" else 0) + (0x80 if w.shopsanity[p] else 0) | (mixed_travel_mode[w.mixed_travel[p]] << 4) + | (0x8 if w.standardize_palettes[p] == "original" else 0) | (0 if w.intensity[p] == "random" else w.intensity[p]), + (0x10 if w.dropshuffle[p] else 0) | (pottery_mode[w.pottery[p]]), + ((8 if w.crystals_gt_orig[p] == "random" else int(w.crystals_gt_orig[p])) << 3) | (counter_mode[w.dungeon_counters[p]] << 1) | (1 if w.experimental[p] else 0), @@ -3397,18 +3490,25 @@ class Settings(object): (0x80 if w.bigkeyshuffle[p] else 0) | (0x40 if w.keyshuffle[p] else 0) | (0x20 if w.mapshuffle[p] else 0) | (0x10 if w.compassshuffle[p] else 0) - | (boss_mode[w.boss_shuffle[p]] << 2) | (enemy_mode[w.enemy_shuffle[p]]), + | (enemy_mode[w.enemy_shuffle[p]]), (e_health[w.enemy_health[p]] << 5) | (e_dmg[w.enemy_damage[p]] << 3) | (0x4 if w.potshuffle[p] else 0) | (0x2 if w.bombbag[p] else 0) | (1 if w.shufflelinks[p] else 0), - (rb_mode[w.restrict_boss_items[p]] << 6)]) + (rb_mode[w.restrict_boss_items[p]] << 6) | (algo_mode[w.algorithm] << 3) | (boss_mode[w.boss_shuffle[p]]), + + settings_version]) return base64.b64encode(code, "+-".encode()).decode() @staticmethod def adjust_args_from_code(code, player, args): settings, p = base64.b64decode(code.encode(), "+-".encode()), player + if len(settings) < 11: + raise Exception('Provided code is incompatible with this version') + if settings[10] != settings_version: + raise Exception('Provided code is incompatible with this version') + def r(d): return {y: x for x, y in d.items()} @@ -3421,36 +3521,45 @@ class Settings(object): args.difficulty[p] = r(diff_mode)[(settings[2] & 0x18) >> 3] args.item_functionality[p] = r(func_mode)[(settings[2] & 0x6) >> 1] args.goal[p] = r(goal_mode)[(settings[2] & 0xE0) >> 5] - args.accessibility[p] = r(access_mode)[settings[5] & 0x3] + args.accessibility[p] = r(access_mode)[settings[6] & 0x3] args.retro[p] = True if settings[1] & 0x01 else False args.hints[p] = True if settings[2] & 0x01 else False - args.retro[p] = True if settings[1] & 0x01 else False args.shopsanity[p] = True if settings[3] & 0x80 else False - args.keydropshuffle[p] = True if settings[3] & 0x40 else False + # args.keydropshuffle[p] = True if settings[3] & 0x40 else False args.mixed_travel[p] = r(mixed_travel_mode)[(settings[3] & 0x30) >> 4] args.standardize_palettes[p] = "original" if settings[3] & 0x8 else "standardize" intensity = settings[3] & 0x7 args.intensity[p] = "random" if intensity == 0 else intensity - args.dungeon_counters[p] = r(counter_mode)[(settings[4] & 0x6) >> 1] - cgt = (settings[4] & 0xf8) >> 3 + + # args.shuffleswitches[p] = True if settings[4] & 0x80 else False + args.dropshuffle[p] = True if settings[4] & 0x10 else False + args.pottery[p] = r(pottery_mode)[settings[4] & 0x0F] + + args.dungeon_counters[p] = r(counter_mode)[(settings[5] & 0x6) >> 1] + cgt = (settings[5] & 0xf8) >> 3 args.crystals_gt[p] = "random" if cgt == 8 else cgt - args.experimental[p] = True if settings[4] & 0x1 else False - cgan = (settings[5] & 0xf8) >> 3 + args.experimental[p] = True if settings[5] & 0x1 else False + + cgan = (settings[6] & 0xf8) >> 3 args.crystals_ganon[p] = "random" if cgan == 8 else cgan - args.openpyramid[p] = True if settings[5] & 0x4 else False - args.bigkeyshuffle[p] = True if settings[6] & 0x80 else False - args.keyshuffle[p] = True if settings[6] & 0x40 else False - args.mapshuffle[p] = True if settings[6] & 0x20 else False - args.compassshuffle[p] = True if settings[6] & 0x10 else False - args.shufflebosses[p] = r(boss_mode)[(settings[6] & 0xc) >> 2] - args.shuffleenemies[p] = r(enemy_mode)[settings[6] & 0x3] - args.enemy_health[p] = r(e_health)[(settings[7] & 0xE0) >> 5] - args.enemy_damage[p] = r(e_dmg)[(settings[7] & 0x18) >> 3] - args.shufflepots[p] = True if settings[7] & 0x4 else False - args.bombbag[p] = True if settings[7] & 0x2 else False - args.shufflelinks[p] = True if settings[7] & 0x1 else False - if len(settings) > 8: - args.restrict_boss_items[p] = True if r(rb_mode)[(settings[8] & 0x80) >> 6] else False + args.openpyramid[p] = True if settings[6] & 0x4 else False + + args.bigkeyshuffle[p] = True if settings[7] & 0x80 else False + args.keyshuffle[p] = True if settings[7] & 0x40 else False + args.mapshuffle[p] = True if settings[7] & 0x20 else False + args.compassshuffle[p] = True if settings[7] & 0x10 else False + # args.shufflebosses[p] = r(boss_mode)[(settings[7] & 0xc) >> 2] + args.shuffleenemies[p] = r(enemy_mode)[settings[7] & 0x3] + + args.enemy_health[p] = r(e_health)[(settings[8] & 0xE0) >> 5] + args.enemy_damage[p] = r(e_dmg)[(settings[8] & 0x18) >> 3] + args.shufflepots[p] = True if settings[8] & 0x4 else False + args.bombbag[p] = True if settings[8] & 0x2 else False + args.shufflelinks[p] = True if settings[8] & 0x1 else False + if len(settings) > 9: + args.restrict_boss_items[p] = r(rb_mode)[(settings[9] & 0xC0) >> 6] + args.algorithm = r(algo_mode)[(settings[9] & 0x38) >> 3] + args.shufflebosses[p] = r(boss_mode)[(settings[9] & 0x07)] class KeyRuleType(FastEnum): diff --git a/Bosses.py b/Bosses.py index 1764162a..53393d5f 100644 --- a/Bosses.py +++ b/Bosses.py @@ -166,18 +166,19 @@ def place_bosses(world, player): all_bosses = sorted(boss_table.keys()) #s orted to be deterministic on older pythons placeable_bosses = [boss for boss in all_bosses if boss not in ['Agahnim', 'Agahnim2', 'Ganon']] - if world.boss_shuffle[player] in ["simple", "full"]: - # temporary hack for swordless kholdstare: + # temporary hack for swordless kholdstare: + if world.boss_shuffle[player] in ["simple", "full", "unique"]: if world.swords[player] == 'swordless': world.get_dungeon('Ice Palace', player).boss = BossFactory('Kholdstare', player) logging.getLogger('').debug('Placing boss Kholdstare at Ice Palace') boss_locations.remove(['Ice Palace', None]) placeable_bosses.remove('Kholdstare') + if world.boss_shuffle[player] in ["simple", "full"]: if world.boss_shuffle[player] == "simple": # vanilla bosses shuffled bosses = placeable_bosses + ['Armos Knights', 'Lanmolas', 'Moldorm'] else: # all bosses present, the three duplicates chosen at random - bosses = all_bosses + random.sample(placeable_bosses, 3) + bosses = placeable_bosses + random.sample(placeable_bosses, 3) logging.getLogger('').debug('Bosses chosen %s', bosses) @@ -189,12 +190,7 @@ def place_bosses(world, player): raise FillError('Could not place boss for location %s' % loc_text) bosses.remove(boss) - # GT Bosses can move dungeon - find the real dungeon to place them in - if level: - loc = [x.name for x in world.dungeons if x.player == player and level in x.bosses.keys()][0] - loc_text = loc + ' (' + level + ')' - logging.getLogger('').debug('Placing boss %s at %s', boss, loc_text) - world.get_dungeon(loc, player).bosses[level] = BossFactory(boss, player) + place_boss(boss, level, loc, loc_text, world, player) elif world.boss_shuffle[player] == "random": #all bosses chosen at random for [loc, level] in boss_locations: loc_text = loc + (' ('+level+')' if level else '') @@ -203,9 +199,28 @@ def place_bosses(world, player): except IndexError: raise FillError('Could not place boss for location %s' % loc_text) - # GT Bosses can move dungeon - find the real dungeon to place them in - if level: - loc = [x.name for x in world.dungeons if x.player == player and level in x.bosses.keys()][0] - loc_text = loc + ' (' + level + ')' - logging.getLogger('').debug('Placing boss %s at %s', boss, loc_text) - world.get_dungeon(loc, player).bosses[level] = BossFactory(boss, player) + place_boss(boss, level, loc, loc_text, world, player) + elif world.boss_shuffle[player] == 'unique': + bosses = list(placeable_bosses) + + for [loc, level] in boss_locations: + loc_text = loc + (' ('+level+')' if level else '') + try: + if level: + boss = random.choice([b for b in placeable_bosses if can_place_boss(world, player, b, loc, level)]) + else: + boss = random.choice([b for b in bosses if can_place_boss(world, player, b, loc, level)]) + bosses.remove(boss) + except IndexError: + raise FillError('Could not place boss for location %s' % loc_text) + + place_boss(boss, level, loc, loc_text, world, player) + + +def place_boss(boss, level, loc, loc_text, world, player): + # GT Bosses can move dungeon - find the real dungeon to place them in + if level: + loc = [x.name for x in world.dungeons if x.player == player and level in x.bosses.keys()][0] + loc_text = loc + ' (' + level + ')' + logging.getLogger('').debug('Placing boss %s at %s', boss, loc_text) + world.get_dungeon(loc, player).bosses[level] = BossFactory(boss, player) diff --git a/CHANGELOG.md b/CHANGELOG.md index dbb65f37..aef17765 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,15 @@ # Changelog +### 0.2.8.0 +- ~Merged DR v1.0.1.0 - Pottery options, BPS support, MSU Resume, Collection Rate Counter~ +- Various improvements to increase generation success rate and reduce generation time +- Fixed issue with playthru recognizing Aga accessibility +- Fixed issue with applying rules correctly to Murahdahla, fixing Murahdahla+Beatable issues +- Fixed issue with Flute+Rainstate, flute use is no longer in logic until Zelda is delivered + ### 0.2.7.3 - Restructured OWR algorithm to include some additional scenarios not previously allowed -- Added new Inverted D-pad controls for Social Distorion (ie. Mirror Mode) support +- Added new Inverted D-pad controls for Social Distortion (ie. Mirror Mode) support - Crossed OWR/Special OW Areas are now included in the spoiler log - Fixed default TF pieces with Trinity in Mystery - Added bush crabs to rupee farm logic (only in non-enemizer) @@ -19,7 +26,7 @@ - Added proper branch-specific versioning (ie. Dev branch has '-u' suffixing the version number while Release/Main branch does not) ### 0.2.7.0 -- ~~Merged DR v1.0.0.3 - MANY changes, major things listed below~~ +- ~Merged DR v1.0.0.3 - MANY changes, major things listed below~ - New Item Fills (Districts/Vanilla/Major Location/Dungeon) - New OW Map Prize Indicators (In ER, map checks can spoil dungeon locations with a user setting) - Forbidden Boss Items (Exclude certain dungeon items from dropping on bosses) @@ -70,7 +77,7 @@ - Fixed issue with incorrect Mirror bonking - Fixed issue with old man follower death to Pyramid - Fixed Hera boss music not playing when boss not defeated -- ~~Merged DR v0.5.1.7 - TT boss trap door fix/Applied Glitched flag~~ +- ~Merged DR v0.5.1.7 - TT boss trap door fix/Applied Glitched flag~ ### 0.2.4.0 - Added Guaranteed OWR Reachability @@ -96,7 +103,7 @@ - Fake flipper damage fix improved to skip the long delay after the scroll - Fixed missing Blue Potion in Lake Shop in Inverted - Added legacy OW Crossed option 'None (Allowed)' to support old behavior when invalid option was used in Mystery -- ~~Merged DR v0.5.1.6 - Money balancing fix/Boss logic fixes with Bombbag~~ +- ~Merged DR v0.5.1.6 - Money balancing fix/Boss logic fixes with Bombbag~ ### 0.2.3.3 - Added OW Layout validation that reduces the cases where some screens are unreachable @@ -138,7 +145,7 @@ - Fixed music track change to Sanc music when Standard mode is delivering Zelda - Fixed SP flooding issue - Fixed issue with Shuffle Ganon in CLI/GUI -- ~~Merged DR v0.5.1.5 - Mystery subweights~~ +- ~Merged DR v0.5.1.5 - Mystery subweights~ ### 0.2.1.2 - Fixed issue with whirlpools not changing world when in Crossed OW @@ -159,7 +166,7 @@ - Smith deletion on S+Q only occurs if Blacksmith not reachable from starting locations - Spoiler log improvements to prevent spoiling in the beginning 'meta' section - Various minor fixes and improvements -- ~~Merged DR v0.5.1.4 - ROM bug fixes/keylogic improvements~~ +- ~Merged DR v0.5.1.4 - ROM bug fixes/keylogic improvements~ ### 0.1.9.4 - Hotfix for bad 0.1.9.3 version @@ -172,11 +179,11 @@ ### 0.1.9.2 - Fixed spoiler log and mystery for new Crossed/Mixed structure - Minor preparations and tweaks to ER framework (added global Entrance/Exit pool) -- ~~Merged DR v0.5.1.2 - Blind Prison shuffled outside TT/Keylogic Improvements~~ +- ~Merged DR v0.5.1.2 - Blind Prison shuffled outside TT/Keylogic Improvements~ ### 0.1.9.1 - Fixed logic issue with leaving IP entrance not requiring flippers -- ~~Merged DR v0.5.1.1 - Map Indicator Fix/Boss Shuffle Bias/Shop Hints~~ +- ~Merged DR v0.5.1.1 - Map Indicator Fix/Boss Shuffle Bias/Shop Hints~ ### 0.1.9.0 - Expanded Crossed OW to four separate options, see Readme for details @@ -190,7 +197,7 @@ - Fixed issues with Link/Bunny state in Crossed OW - Fixed issue with Standard+Parallel not using vanilla connections for Escape - Fixed issue with Mystery for OW boolean options -- ~~Merged DR v0.5.1.0 - Major Keylogic Update~~ +- ~Merged DR v0.5.1.0 - Major Keylogic Update~ ### 0.1.8.1 - Fixed issue with activating flute in DW (OW Mixed) @@ -205,12 +212,12 @@ - Added OW Shuffle support for Plando module (needs user testing) - Fixed issue with Sanc start at TR as bunny when it is LW - Fixed issue with Pyramid Hole not getting shuffled -- ~~Merged DR v0.5.0.3 - Minor DR fixes~~ +- ~Merged DR v0.5.0.3 - Minor DR fixes~ ### 0.1.7.4 - Fixed issue with Mixed OW failing to generate when HC/Pyramid is swapped - Various fixes to improve generation rates for Mixed OW Shuffle -- ~~Merged DR v0.5.0.2 - Shuffle SFX~~ +- ~Merged DR v0.5.0.2 - Shuffle SFX~ ### 0.1.7.3 - Fixed minor issue with ambient SFX stopping and starting on OW screen load @@ -230,10 +237,10 @@ ### 0.1.7.0 - Expanded new DR bomb logic to all modes (bomb usage in logic only if there is an unlimited supply of bombs available) -- ~~Merged DR v0.5.0.1 - Bombbag mode / Enemizer fixes~~ +- ~Merged DR v0.5.0.1 - Bombbag mode / Enemizer fixes~ ### 0.1.6.9 -- ~~Merged DR v0.4.0.12 - Secure random update / Credits fix~~ +- ~Merged DR v0.4.0.12 - Secure random update / Credits fix~ ### 0.1.6.8 - Implemented a smarter Balanced Flute Shuffle algorithm @@ -247,14 +254,14 @@ - Fixed Boss Music when boss room is entered thru straight stairs - Suppressed in-dungeon music changes when DR is enabled - Fixed issue with Pyramid Exit exiting to wrong location in ER -- ~~Merged DR v0.4.0.11 - Various DR changes~~ +- ~Merged DR v0.4.0.11 - Various DR changes~ ### 0.1.6.6 -- ~~Merged DR v0.4.0.9 - P/C Indicator / Credits fix / CLI Hints Fix~~ +- ~Merged DR v0.4.0.9 - P/C Indicator / Credits fix / CLI Hints Fix~ ### 0.1.6.5 - Reduced chance of diagonal flute spot in Balanced -- ~~Merged DR v0.4.0.8 - Boss Indicator / Psuedo Boots / Quickswap Update / Credits Updates~~ +- ~Merged DR v0.4.0.8 - Boss Indicator / Psuedo Boots / Quickswap Update / Credits Updates~ ### 0.1.6.4 - Fixed Frogsmith and Stumpy and restored progression in these locations @@ -293,15 +300,15 @@ ### 0.1.5.0 - Added OW Tile Swap setting - Fixed horizontal VRAM visual loading glitch on megatiles -- ~~Merged DR v0.4.0.7 - Fast Credits / Reduced Flashing / Sprite Author in Credits~~ +- ~~Merged DR v0.4.0.7 - Fast Credits / Reduced Flashing / Sprite Author in Credits~~ Didn't fully merge ### 0.1.4.3 -- Merged DR v0.4.0.6 - TT Maiden Attic Hint / DR Entrance Floor Mat Mods / Hard/Expert Item Pool Fix +- ~Merged DR v0.4.0.6 - TT Maiden Attic Hint / DR Entrance Floor Mat Mods / Hard/Expert Item Pool Fix~ ### 0.1.4.2 - Modified various OW map terrain specific to OW Shuffle - Changed World check to table-based vs OW ID-based (should have no effect with current modes) -- Merged DR v0.4.0.5 - Mystery Boss Shuffle Fix / Swordless+Hard Item Pool Fix / Insanity+Inverted ER Fixes +- ~Merged DR v0.4.0.5 - Mystery Boss Shuffle Fix / Swordless+Hard Item Pool Fix / Insanity+Inverted ER Fixes~ ### 0.1.4.1 - Moved Inverted Pyramid Entrance to top of HC Ledge @@ -315,11 +322,11 @@ - Various logic fixes and region prep for Inverted - Fixed muted MSU-1 music in door rando when descending GT Climb stairs - Fixed Standard + Vanilla (thanks compiling) -- Merged DR v0.4.0.4 - Shuffle Link's House / Experimental Bunny Start / 10 Bomb Fix +- ~Merged DR v0.4.0.4 - Shuffle Link's House / Experimental Bunny Start / 10 Bomb Fix~ ### 0.1.3.0 - Added OWG Logic for OW Shuffle -- Merged DR v0.4.0.2 - OWG Framework / YAML +- ~Merged DR v0.4.0.2 - OWG Framework / YAML~ ### 0.1.2.2 - Re-purposed OW Shuffle setting to Layout Shuffle @@ -328,7 +335,7 @@ ### 0.1.2.1 - Made possible fix for Standard -- Merged DR v0.3.1.10 - Fixed Standard generation +- ~Merged DR v0.3.1.10 - Fixed Standard generation~ ### 0.1.2.0 - Added 'Parallel Worlds' toggle option @@ -338,7 +345,7 @@ ### 0.1.1.2 - If Link's current position fits within the incoming gap, Link will not get re-centered to the incoming gap - Added Rule for Pearl required to drop down back of SW -- Merged DR v0.3.1.8 - Improved Shopsanity pricing - Fixed Retro generation +- ~Merged DR v0.3.1.8 - Improved Shopsanity pricing - Fixed Retro generation~ ### 0.1.1.1 - Fixed camera unlocking issue diff --git a/CLI.py b/CLI.py index e8ba78de..8ba224bd 100644 --- a/CLI.py +++ b/CLI.py @@ -88,6 +88,10 @@ def parse_cli(argv, no_defaults=False): if ret.keysanity: ret.mapshuffle, ret.compassshuffle, ret.keyshuffle, ret.bigkeyshuffle = [True] * 4 + if ret.keydropshuffle: + ret.dropshuffle = True + ret.pottery = 'keys' if ret.pottery == 'none' else ret.pottery + if multiargs.multi: defaults = copy.deepcopy(ret) for player in range(1, multiargs.multi + 1): @@ -97,14 +101,15 @@ def parse_cli(argv, no_defaults=False): 'ow_shuffle', 'ow_crossed', 'ow_keepsimilar', 'ow_mixed', 'ow_whirlpool', 'ow_fluteshuffle', 'shuffle', 'door_shuffle', 'intensity', 'crystals_ganon', 'crystals_gt', 'openpyramid', 'mapshuffle', 'compassshuffle', 'keyshuffle', 'bigkeyshuffle', 'startinventory', - 'bombbag', 'shuffleganon', 'overworld_map', 'restrict_boss_items', + 'usestartinventory', 'bombbag', 'shuffleganon', 'overworld_map', 'restrict_boss_items', 'triforce_pool_min', 'triforce_pool_max', 'triforce_goal_min', 'triforce_goal_max', 'triforce_min_difference', 'triforce_goal', 'triforce_pool', 'shufflelinks', 'pseudoboots', 'retro', 'accessibility', 'hints', 'beemizer', 'experimental', 'dungeon_counters', 'shufflebosses', 'shuffleenemies', 'enemy_health', 'enemy_damage', 'shufflepots', - 'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', 'heartbeep', - 'remote_items', 'shopsanity', 'keydropshuffle', 'mixed_travel', 'standardize_palettes', 'code', - 'reduce_flashing', 'shuffle_sfx']: + 'ow_palettes', 'uw_palettes', 'sprite', 'disablemusic', 'quickswap', 'fastmenu', 'heartcolor', + 'heartbeep', 'remote_items', 'shopsanity', 'dropshuffle', 'pottery', 'keydropshuffle', + 'mixed_travel', 'standardize_palettes', 'code', 'reduce_flashing', 'shuffle_sfx', + 'msu_resume', 'collection_rate', 'colorizepots']: value = getattr(defaults, name) if getattr(playerargs, name) is None else getattr(playerargs, name) if player == 1: setattr(ret, name, {1: value}) @@ -141,6 +146,8 @@ def parse_settings(): "progressive": "on", "accessibility": "items", "algorithm": "balanced", + 'mystery': False, + 'suppress_meta': False, "restrict_boss_items": "none", # Shuffle Ganon defaults to TRUE @@ -157,7 +164,6 @@ def parse_settings(): "overworld_map": "default", "pseudoboots": False, - "shufflepots": False, "shuffleenemies": "none", "shufflebosses": "none", "enemy_damage": "default", @@ -165,7 +171,11 @@ def parse_settings(): "enemizercli": os.path.join(".", "EnemizerCLI", "EnemizerCLI.Core"), "shopsanity": False, - "keydropshuffle": False, + 'keydropshuffle': False, + 'dropshuffle': False, + 'pottery': 'none', + 'colorizepots': False, + 'shufflepots': False, "mapshuffle": False, "compassshuffle": False, "keyshuffle": False, @@ -202,6 +212,8 @@ def parse_settings(): "uw_palettes": "default", "reduce_flashing": False, "shuffle_sfx": False, + 'msu_resume': False, + 'collection_rate': False, # Spoiler defaults to TRUE # Playthrough defaults to TRUE @@ -209,9 +221,11 @@ def parse_settings(): "create_spoiler": True, "calc_playthrough": True, "create_rom": True, + "bps": False, "usestartinventory": False, "custom": False, "rom": os.path.join(".", "Zelda no Densetsu - Kamigami no Triforce (Japan).sfc"), + "patch": os.path.join(".", "Patch File.bps"), "seed": "", "count": 1, diff --git a/DoorShuffle.py b/DoorShuffle.py index e00edb3e..09886e1f 100644 --- a/DoorShuffle.py +++ b/DoorShuffle.py @@ -6,6 +6,7 @@ from enum import unique, Flag from typing import DefaultDict, Dict, List from BaseClasses import RegionType, Region, Door, DoorType, Direction, Sector, CrystalBarrier, DungeonInfo, dungeon_keys +from BaseClasses import PotFlags, LocationType from Doors import reset_portals from Dungeons import dungeon_regions, region_starts, standard_starts, split_region_starts from Dungeons import dungeon_bigs, dungeon_hints @@ -380,7 +381,7 @@ def choose_portals(world, player): if world.doorShuffle[player] in ['basic', 'crossed']: cross_flag = world.doorShuffle[player] == 'crossed' # key drops allow the big key in the right place in Desert Tiles 2 - bk_shuffle = world.bigkeyshuffle[player] or world.keydropshuffle[player] + bk_shuffle = world.bigkeyshuffle[player] or world.dropshuffle[player] std_flag = world.mode[player] == 'standard' # roast incognito doors world.get_room(0x60, player).delete(5) @@ -414,7 +415,7 @@ def choose_portals(world, player): info.sole_entrance = inaccessible_portals[0] info.required_passage.clear() else: - raise Exception('please inspect this case') + raise Exception(f'No reachable entrances for {dungeon}') if len(reachable_portals) == 1: info.sole_entrance = reachable_portals[0] info_map[dungeon] = info @@ -521,7 +522,7 @@ def analyze_portals(world, player): info.sole_entrance = inaccessible_portals[0] info.required_passage.clear() else: - raise Exception('please inspect this case') + raise Exception(f'No reachable entrances for {dungeon}') if len(reachable_portals) == 1: info.sole_entrance = reachable_portals[0] if world.intensity[player] < 2 and world.doorShuffle[player] == 'basic' and dungeon == 'Desert Palace': @@ -996,10 +997,15 @@ def cross_dungeon(world, player): assign_cross_keys(dungeon_builders, world, player) all_dungeon_items_cnt = len(list(y for x in world.dungeons if x.player == player for y in x.all_items)) - if world.keydropshuffle[player]: - target_items = 35 if world.retro[player] else 96 + target_items = 34 + if world.retro[player]: + target_items += 1 if world.dropshuffle[player] else 0 # the hc big key else: - target_items = 34 if world.retro[player] else 63 + target_items += 29 # small keys in chests + if world.dropshuffle[player]: + target_items += 14 # 13 dropped smalls + 1 big + if world.pottery[player] not in ['none', 'cave']: + target_items += 19 # 19 pot keys d_items = target_items - all_dungeon_items_cnt world.pool_adjustment[player] = d_items smooth_door_pairs(world, player) @@ -1057,7 +1063,11 @@ def assign_cross_keys(dungeon_builders, world, player): logging.getLogger('').info(world.fish.translate("cli", "cli", "shuffling.keydoors")) start = time.process_time() if world.retro[player]: - remaining = 61 if world.keydropshuffle[player] else 29 + remaining = 29 + if world.dropshuffle[player]: + remaining += 13 + if world.pottery[player] not in ['none', 'cave']: + remaining += 19 else: remaining = len(list(x for dgn in world.dungeons if dgn.player == player for x in dgn.small_keys)) total_keys = remaining @@ -1076,11 +1086,10 @@ def assign_cross_keys(dungeon_builders, world, player): total_candidates += builder.key_doors_num start_regions_map[name] = start_regions - # Step 2: Initial Key Number Assignment & Calculate Flexibility for name, builder in dungeon_builders.items(): calculated = int(round(builder.key_doors_num*total_keys/total_candidates)) - max_keys = builder.location_cnt - calc_used_dungeon_items(builder) + max_keys = max(0, builder.location_cnt - calc_used_dungeon_items(builder)) cand_len = max(0, len(builder.candidates) - builder.key_drop_cnt) limit = min(max_keys, cand_len) suggested = min(calculated, limit) @@ -1253,7 +1262,13 @@ def refine_hints(dungeon_builders): for region in builder.master_sector.regions: for location in region.locations: if not location.event and '- Boss' not in location.name and '- Prize' not in location.name and location.name != 'Sanctuary': - location.hint_text = dungeon_hints[name] + if location.type == LocationType.Pot and location.pot: + hint_text = ('under a block' if location.pot.flags & PotFlags.Block else 'in a pot') + location.hint_text = f'{hint_text} {dungeon_hints[name]}' + elif location.type == LocationType.Drop: + location.hint_text = f'dropped {dungeon_hints[name]}' + else: + location.hint_text = dungeon_hints[name] def refine_boss_exits(world, player): @@ -1729,7 +1744,7 @@ def smooth_door_pairs(world, player): if type_b == DoorKind.SmallKey: remove_pair(door, world, player) else: - if valid_pair: + if valid_pair and not std_forbidden(door, world, player): bd_candidates[door.entrance.parent_region.dungeon].append(door) elif type_a in [DoorKind.Bombable, DoorKind.Dashable] or type_b in [DoorKind.Bombable, DoorKind.Dashable]: if type_a in [DoorKind.Bombable, DoorKind.Dashable]: @@ -1738,7 +1753,8 @@ def smooth_door_pairs(world, player): else: room_b.change(partner.doorListPos, DoorKind.Normal) remove_pair(partner, world, player) - elif valid_pair and type_a != DoorKind.SmallKey and type_b != DoorKind.SmallKey: + elif (valid_pair and type_a != DoorKind.SmallKey and type_b != DoorKind.SmallKey + and not std_forbidden(door, world, player)): bd_candidates[door.entrance.parent_region.dungeon].append(door) shuffle_bombable_dashable(bd_candidates, world, player) world.paired_doors[player] = [x for x in world.paired_doors[player] if x.pair or x.original] @@ -1777,12 +1793,37 @@ def stateful_door(door, kind): return False +def std_forbidden(door, world, player): + return (world.mode[player] == 'standard' and door.entrance.parent_region.dungeon.name == 'Hyrule Castle' and + 'Hyrule Castle Throne Room N' in [door.name, door.dest.name]) + + +dashable_forbidden = { + 'Swamp Trench 1 Key Ledge NW', 'Swamp Left Elbow WN', 'Swamp Right Elbow SE', 'Mire Hub WN', 'Mire Hub WS', + 'Mire Hub Top NW', 'Mire Hub NE', 'Ice Dead End WS' +} + +ohko_forbidden = { + 'GT Invisible Catwalk NE', 'GT Falling Bridge WN', 'GT Falling Bridge WS', 'GT Hidden Star ES', 'GT Hookshot EN', + 'GT Torch Cross WN', 'TR Torches WN', 'Mire Falling Bridge WS', 'Mire Falling Bridge W', 'Ice Hookshot Balcony SW', + 'Ice Catwalk WN', 'Ice Catwalk NW', 'Ice Bomb Jump NW', 'GT Cannonball Bridge SE' +} + + +def filter_dashable_candidates(candidates, world): + forbidden_set = dashable_forbidden + if world.timer in ['ohko', 'timed-ohko']: + forbidden_set = ohko_forbidden.union(dashable_forbidden) + return [x for x in candidates if x.name not in forbidden_set and x.dest.name not in forbidden_set] + + def shuffle_bombable_dashable(bd_candidates, world, player): if world.doorShuffle[player] == 'basic': for dungeon, candidates in bd_candidates.items(): diff = bomb_dash_counts[dungeon.name][1] if diff > 0: - for chosen in random.sample(candidates, min(diff, len(candidates))): + dash_candidates = filter_dashable_candidates(candidates, world) + for chosen in random.sample(dash_candidates, min(diff, len(candidates))): change_pair_type(chosen, DoorKind.Dashable, world, player) candidates.remove(chosen) diff = bomb_dash_counts[dungeon.name][0] @@ -1794,7 +1835,8 @@ def shuffle_bombable_dashable(bd_candidates, world, player): remove_pair_type_if_present(excluded, world, player) elif world.doorShuffle[player] == 'crossed': all_candidates = sum(bd_candidates.values(), []) - for chosen in random.sample(all_candidates, min(8, len(all_candidates))): + dash_candidates = filter_dashable_candidates(all_candidates, world) + for chosen in random.sample(dash_candidates, min(8, len(all_candidates))): change_pair_type(chosen, DoorKind.Dashable, world, player) all_candidates.remove(chosen) for chosen in random.sample(all_candidates, min(12, len(all_candidates))): @@ -2113,6 +2155,7 @@ logical_connections = [ ('Hera Startile Wide Crystal Exit', 'Hera Startile Wide'), ('Hera Big Chest Hook Path', 'Hera Big Chest Landing'), ('Hera Big Chest Landing Exit', 'Hera 4F'), + ('Hera 5F Orange Path', 'Hera 5F Pot Block'), ('PoD Pit Room Block Path N', 'PoD Pit Room Blocked'), ('PoD Pit Room Block Path S', 'PoD Pit Room'), @@ -2176,6 +2219,7 @@ logical_connections = [ ('Swamp Trench 1 Departure Approach', 'Swamp Trench 1 Approach'), ('Swamp Trench 1 Departure Key', 'Swamp Trench 1 Key Ledge'), ('Swamp Hub Hook Path', 'Swamp Hub North Ledge'), + ('Swamp Hub Side Hook Path', 'Swamp Hub Side Ledges'), ('Swamp Hub North Ledge Drop Down', 'Swamp Hub'), ('Swamp Crystal Switch Outer to Inner Barrier - Blue', 'Swamp Crystal Switch Inner'), ('Swamp Crystal Switch Outer to Ranged Crystal', 'Swamp Crystal Switch Outer - Ranged Crystal'), @@ -2207,13 +2251,17 @@ logical_connections = [ ('Skull Pot Circle Star Path', 'Skull Map Room'), ('Skull Big Chest Hookpath', 'Skull 1 Lobby'), ('Skull Back Drop Star Path', 'Skull Small Hall'), + ('Skull 2 West Lobby Pits', 'Skull 2 West Lobby Ledge'), + ('Skull 2 West Lobby Ledge Pits', 'Skull 2 West Lobby'), ('Thieves Rail Ledge Drop Down', 'Thieves BK Corner'), ('Thieves Hellway Orange Barrier', 'Thieves Hellway S Crystal'), ('Thieves Hellway Crystal Orange Barrier', 'Thieves Hellway'), ('Thieves Hellway Blue Barrier', 'Thieves Hellway N Crystal'), ('Thieves Hellway Crystal Blue Barrier', 'Thieves Hellway'), ('Thieves Attic Orange Barrier', 'Thieves Attic Hint'), + ('Thieves Attic Blue Barrier', 'Thieves Attic Switch'), ('Thieves Attic Hint Orange Barrier', 'Thieves Attic'), + ('Thieves Attic Switch Blue Barrier', 'Thieves Attic'), ('Thieves Basement Block Path', 'Thieves Blocked Entry'), ('Thieves Blocked Entry Path', 'Thieves Basement Block'), ('Thieves Conveyor Bridge Block Path', 'Thieves Conveyor Block'), @@ -2272,6 +2320,8 @@ logical_connections = [ ('TR Main Lobby Gap', 'TR Lobby Ledge'), ('TR Lobby Ledge Gap', 'TR Main Lobby'), + ('TR Hub Path', 'TR Hub Ledges'), + ('TR Hub Ledges Path', 'TR Hub'), ('TR Pipe Ledge Drop Down', 'TR Pipe Pit'), ('TR Big Chest Gap', 'TR Big Chest Entrance'), ('TR Big Chest Entrance Gap', 'TR Big Chest'), @@ -2300,6 +2350,8 @@ logical_connections = [ ('TR Crystaroller Chest to Middle Barrier - Blue', 'TR Crystaroller Middle'), ('TR Crystaroller Middle Ranged Crystal Exit', 'TR Crystaroller Middle'), ('TR Crystaroller Bottom Ranged Crystal Exit', 'TR Crystaroller Bottom'), + ('TR Dark Ride Path', 'TR Dark Ride Ledges'), + ('TR Dark Ride Ledges Path', 'TR Dark Ride'), ('TR Crystal Maze Start to Interior Barrier - Blue', 'TR Crystal Maze Interior'), ('TR Crystal Maze Start to Crystal', 'TR Crystal Maze Start - Crystal'), ('TR Crystal Maze Start Crystal Exit', 'TR Crystal Maze Start'), @@ -2310,16 +2362,18 @@ logical_connections = [ ('TR Crystal Maze End to Interior Barrier - Blue', 'TR Crystal Maze Interior'), ('TR Crystal Maze End to Ranged Crystal', 'TR Crystal Maze End - Ranged Crystal'), ('TR Crystal Maze End Ranged Crystal Exit', 'TR Crystal Maze End'), + ('TR Final Abyss Balcony Path', 'TR Final Abyss Ledge'), + ('TR Final Abyss Ledge Path', 'TR Final Abyss Balcony'), ('GT Blocked Stairs Block Path', 'GT Big Chest'), ('GT Speed Torch South Path', 'GT Speed Torch'), ('GT Speed Torch North Path', 'GT Speed Torch Upper'), - ('GT Hookshot East-North Path', 'GT Hookshot North Platform'), - ('GT Hookshot East-South Path', 'GT Hookshot South Platform'), - ('GT Hookshot North-East Path', 'GT Hookshot East Platform'), - ('GT Hookshot North-South Path', 'GT Hookshot South Platform'), - ('GT Hookshot South-East Path', 'GT Hookshot East Platform'), - ('GT Hookshot South-North Path', 'GT Hookshot North Platform'), + ('GT Hookshot East-Mid Path', 'GT Hookshot Mid Platform'), + ('GT Hookshot Mid-East Path', 'GT Hookshot East Platform'), + ('GT Hookshot North-Mid Path', 'GT Hookshot Mid Platform'), + ('GT Hookshot Mid-North Path', 'GT Hookshot North Platform'), + ('GT Hookshot South-Mid Path', 'GT Hookshot Mid Platform'), + ('GT Hookshot Mid-South Path', 'GT Hookshot South Platform'), ('GT Hookshot Platform Blue Barrier', 'GT Hookshot South Entry'), ('GT Hookshot Platform Barrier Bypass', 'GT Hookshot South Entry'), ('GT Hookshot Entry Blue Barrier', 'GT Hookshot South Platform'), @@ -2350,8 +2404,8 @@ logical_connections = [ ('GT Crystal Conveyor to Corner Barrier - Blue', 'GT Crystal Conveyor Corner'), ('GT Crystal Conveyor to Ranged Crystal', 'GT Crystal Conveyor - Ranged Crystal'), ('GT Crystal Conveyor Corner to Left Bypass', 'GT Crystal Conveyor Left'), - ('GT Crystal Conveyor Corner to Barrier - Blue', 'GT Crystal Conveyor Left'), - ('GT Crystal Conveyor Corner to Barrier - Orange', 'GT Crystal Conveyor'), + ('GT Crystal Conveyor Corner to Barrier - Blue', 'GT Crystal Conveyor'), + ('GT Crystal Conveyor Corner to Barrier - Orange', 'GT Crystal Conveyor Left'), ('GT Crystal Conveyor Corner to Ranged Crystal', 'GT Crystal Conveyor Corner - Ranged Crystal'), ('GT Crystal Conveyor Left to Corner Barrier - Orange', 'GT Crystal Conveyor Corner'), ('GT Crystal Conveyor Ranged Crystal Exit', 'GT Crystal Conveyor'), @@ -3017,7 +3071,8 @@ palette_map = { 'Tower of Hera': (0x6, None), 'Thieves Town': (0x17, None), # the attic uses 0x23 'Turtle Rock': (0x18, 0x19, 'TR Boss SW'), - 'Ganons Tower': (0x28, 0x1b, 'GT Agahnim 2 SW'), # other palettes: 0x1a (other) 0x24 (Gauntlet - Lanmo) 0x25 (conveyor-torch-wizzrode moldorm pit f5?) + 'Ganons Tower': (0x28, 0x1b, 'GT Agahnim 2 SW'), + # other palettes: 0x1a (other) 0x24 (Gauntlet - Lanmo) 0x25 (conveyor-torch-wizzrobe moldorm pit f5?) } # implications: diff --git a/Doors.py b/Doors.py index a23f46f0..26d474cc 100644 --- a/Doors.py +++ b/Doors.py @@ -309,6 +309,7 @@ def create_doors(world, player): create_door(player, 'Hera 5F Star Hole', Hole), create_door(player, 'Hera 5F Pothole Chain', Hole), create_door(player, 'Hera 5F Normal Holes', Hole), + create_door(player, 'Hera 5F Orange Path', Lgcl), create_door(player, 'Hera Fairies\' Warp', Warp), create_door(player, 'Hera Boss Down Stairs', Sprl).dir(Dn, 0x07, 0, HTH).ss(S, 0x61, 0xb0).kill(), create_door(player, 'Hera Boss Outer Hole', Hole), @@ -500,6 +501,7 @@ def create_doors(world, player): create_door(player, 'Swamp Hub WS', Nrml).dir(We, 0x36, Bot, High).pos(3), create_door(player, 'Swamp Hub WN', Nrml).dir(We, 0x36, Top, High).small_key().pos(2), create_door(player, 'Swamp Hub Hook Path', Lgcl), + create_door(player, 'Swamp Hub Side Hook Path', Lgcl), create_door(player, 'Swamp Hub Dead Ledge EN', Nrml).dir(Ea, 0x36, Top, High).pos(0), create_door(player, 'Swamp Hub North Ledge N', Nrml).dir(No, 0x36, Mid, High).small_key().pos(1), create_door(player, 'Swamp Hub North Ledge Drop Down', Lgcl), @@ -612,6 +614,8 @@ def create_doors(world, player): create_door(player, 'Skull 2 West Lobby S', Nrml).dir(So, 0x56, Left, High).pos(1).portal(Z, 0x00), create_door(player, 'Skull 2 West Lobby ES', Intr).dir(Ea, 0x56, Bot, High).pos(2), create_door(player, 'Skull 2 West Lobby NW', Intr).dir(No, 0x56, Left, High).small_key().pos(0), + create_door(player, 'Skull 2 West Lobby Pits', Lgcl), + create_door(player, 'Skull 2 West Lobby Ledge Pits', Lgcl), create_door(player, 'Skull X Room SW', Intr).dir(So, 0x56, Left, High).small_key().pos(0), create_door(player, 'Skull Back Drop Star Path', Lgcl), create_door(player, 'Skull 3 Lobby SW', Nrml).dir(So, 0x59, Left, High).pos(1).portal(Z, 0x02), @@ -684,7 +688,9 @@ def create_doors(world, player): create_door(player, 'Thieves Attic Down Stairs', Sprl).dir(Dn, 0x64, 0, HTH).ss(Z, 0x11, 0x80, True, True), create_door(player, 'Thieves Attic ES', Intr).dir(Ea, 0x64, Bot, High).pos(0), create_door(player, 'Thieves Attic Orange Barrier', Lgcl), + create_door(player, 'Thieves Attic Blue Barrier', Lgcl), create_door(player, 'Thieves Attic Hint Orange Barrier', Lgcl), + create_door(player, 'Thieves Attic Switch Blue Barrier', Lgcl), create_door(player, 'Thieves Cricket Hall Left WS', Intr).dir(We, 0x64, Bot, High).pos(0), create_door(player, 'Thieves Cricket Hall Left Edge', Open).dir(Ea, 0x64, None, High).edge(0, X, 0x30), create_door(player, 'Thieves Cricket Hall Right Edge', Open).dir(We, 0x65, None, High).edge(0, Z, 0x30), @@ -955,6 +961,8 @@ def create_doors(world, player): create_door(player, 'TR Hub EN', Nrml).dir(Ea, 0xc6, Top, High).pos(2), create_door(player, 'TR Hub NW', Nrml).dir(No, 0xc6, Left, High).small_key().pos(0), create_door(player, 'TR Hub NE', Nrml).dir(No, 0xc6, Right, High).pos(1), + create_door(player, 'TR Hub Path', Lgcl), + create_door(player, 'TR Hub Ledges Path', Lgcl), create_door(player, 'TR Torches Ledge WS', Nrml).dir(We, 0xc7, Bot, High).pos(2), create_door(player, 'TR Torches WN', Nrml).dir(We, 0xc7, Top, High).pos(1), create_door(player, 'TR Torches NW', Nrml).dir(No, 0xc7, Left, High).trap(0x4).pos(0), @@ -1030,6 +1038,8 @@ def create_doors(world, player): create_door(player, 'TR Crystaroller Down Stairs', Sprl).dir(Dn, 0x04, 0, HTH).ss(A, 0x12, 0x80, True, True).small_key().pos(0), create_door(player, 'TR Dark Ride Up Stairs', Sprl).dir(Up, 0xb5, 0, HTH).ss(A, 0x1b, 0x6c), create_door(player, 'TR Dark Ride SW', Nrml).dir(So, 0xb5, Left, High).trap(0x4).pos(0).portal(Z, 0x22), + create_door(player, 'TR Dark Ride Path', Lgcl), + create_door(player, 'TR Dark Ride Ledges Path', Lgcl), create_door(player, 'TR Dash Bridge NW', Nrml).dir(No, 0xc5, Left, High).pos(1), create_door(player, 'TR Dash Bridge SW', Nrml).dir(So, 0xc5, Left, High).pos(2).portal(Z, 0x02), create_door(player, 'TR Dash Bridge WS', Nrml).dir(We, 0xc5, Bot, High).small_key().pos(0), @@ -1048,6 +1058,8 @@ def create_doors(world, player): create_door(player, 'TR Crystal Maze End Ranged Crystal Exit', Lgcl), create_door(player, 'TR Crystal Maze North Stairs', StrS).dir(No, 0xc4, Mid, High), create_door(player, 'TR Final Abyss South Stairs', StrS).dir(So, 0xb4, Mid, High), + create_door(player, 'TR Final Abyss Balcony Path', Lgcl), + create_door(player, 'TR Final Abyss Ledge Path', Lgcl), create_door(player, 'TR Final Abyss NW', Nrml).dir(No, 0xb4, Left, High).big_key().pos(0), create_door(player, 'TR Boss SW', Nrml).dir(So, 0xa4, Left, High).no_exit().trap(0x4).pos(0), # .portal(Z, 0x00), -enemizer doesn't work @@ -1098,12 +1110,12 @@ def create_doors(world, player): create_door(player, 'GT Conveyor Cross EN', Nrml).dir(Ea, 0x8b, Top, High).pos(2), create_door(player, 'GT Conveyor Cross WN', Intr).dir(We, 0x8b, Top, High).pos(0), create_door(player, 'GT Hookshot EN', Intr).dir(Ea, 0x8b, Top, High).pos(0), - create_door(player, 'GT Hookshot East-North Path', Lgcl), - create_door(player, 'GT Hookshot East-South Path', Lgcl), - create_door(player, 'GT Hookshot North-East Path', Lgcl), - create_door(player, 'GT Hookshot North-South Path', Lgcl), - create_door(player, 'GT Hookshot South-East Path', Lgcl), - create_door(player, 'GT Hookshot South-North Path', Lgcl), + create_door(player, 'GT Hookshot East-Mid Path', Lgcl), + create_door(player, 'GT Hookshot Mid-East Path', Lgcl), + create_door(player, 'GT Hookshot North-Mid Path', Lgcl), + create_door(player, 'GT Hookshot Mid-North Path', Lgcl), + create_door(player, 'GT Hookshot South-Mid Path', Lgcl), + create_door(player, 'GT Hookshot Mid-South Path', Lgcl), create_door(player, 'GT Hookshot Platform Blue Barrier', Lgcl), create_door(player, 'GT Hookshot Entry Blue Barrier', Lgcl), create_door(player, 'GT Hookshot Platform Barrier Bypass', Lgcl), @@ -1291,6 +1303,7 @@ def create_doors(world, player): world.get_door('Hera Beetles Holes Front', player).c_switch() world.get_door('Hera Beetles Holes Landing', player).c_switch() world.get_door('Hera Startile Wide Crystal Exit', player).c_switch() + world.get_door('Hera 5F Orange Path', player).barrier(CrystalBarrier.Orange) world.get_door('PoD Arena North to Landing Barrier - Orange', player).barrier(CrystalBarrier.Orange) world.get_door('PoD Arena Main to Landing Barrier - Blue', player).barrier(CrystalBarrier.Blue) @@ -1345,7 +1358,9 @@ def create_doors(world, player): world.get_door('Thieves Hellway Orange Barrier', player).barrier(CrystalBarrier.Orange) world.get_door('Thieves Hellway Crystal Orange Barrier', player).barrier(CrystalBarrier.Orange) world.get_door('Thieves Attic Orange Barrier', player).barrier(CrystalBarrier.Orange) + world.get_door('Thieves Attic Blue Barrier', player).barrier(CrystalBarrier.Blue) world.get_door('Thieves Attic Hint Orange Barrier', player).barrier(CrystalBarrier.Orange) + world.get_door('Thieves Attic Switch Blue Barrier', player).barrier(CrystalBarrier.Blue) world.get_door('Ice Bomb Drop SE', player).c_switch() world.get_door('Ice Conveyor Crystal Exit', player).c_switch() @@ -1421,8 +1436,7 @@ def create_doors(world, player): world.get_door('GT Crystal Conveyor Ranged Crystal Exit', player).c_switch() world.get_door('GT Crystal Conveyor Corner Ranged Crystal Exit', player).c_switch() world.get_door('GT Crystal Conveyor Corner to Left Bypass', player).barrier(CrystalBarrier.Blue) - world.get_door('GT Hookshot South-North Path', player).c_switch() - world.get_door('GT Hookshot South-East Path', player).c_switch() + world.get_door('GT Hookshot South-Mid Path', player).c_switch() world.get_door('GT Hookshot ES', player).c_switch() world.get_door('GT Hookshot Platform Barrier Bypass', player).barrier(CrystalBarrier.Orange) world.get_door('GT Hookshot Platform Blue Barrier', player).barrier(CrystalBarrier.Blue) diff --git a/DungeonGenerator.py b/DungeonGenerator.py index 5be223c9..f0038f45 100644 --- a/DungeonGenerator.py +++ b/DungeonGenerator.py @@ -1326,7 +1326,17 @@ def create_dungeon_builders(all_sectors, connections_tuple, world, player, polarized_sectors[sector] = None if bow_sectors: assign_bow_sectors(dungeon_map, bow_sectors, global_pole) - assign_location_sectors(dungeon_map, free_location_sectors, global_pole, world, player) + leftover = assign_location_sectors_minimal(dungeon_map, free_location_sectors, global_pole, world, player) + free_location_sectors = scatter_extra_location_sectors(dungeon_map, leftover, global_pole) + for sector in free_location_sectors: + if sector.c_switch: + crystal_switches[sector] = None + elif sector.blue_barrier: + crystal_barriers[sector] = None + elif sector.polarity().is_neutral(): + neutral_sectors[sector] = None + else: + polarized_sectors[sector] = None leftover = assign_crystal_switch_sectors(dungeon_map, crystal_switches, crystal_barriers, global_pole) ensure_crystal_switches_reachable(dungeon_map, leftover, polarized_sectors, crystal_barriers, global_pole) for sector in leftover: @@ -1558,33 +1568,85 @@ def assign_bow_sectors(dungeon_map, bow_sectors, global_pole): assign_sector(sector_list[i], builder, bow_sectors, global_pole) -def assign_location_sectors(dungeon_map, free_location_sectors, global_pole, world, player): +def scatter_extra_location_sectors(dungeon_map, free_location_sectors, global_pole): + population = [n for n in dungeon_map.keys()] + k = round(len(free_location_sectors) * .50) valid = False choices = None + candidates = [] + sector_list = list(free_location_sectors) + while not valid: + candidates = random.sample(sector_list, k=k) + choices = random.choices(population, k=len(candidates)) + sector_dict = defaultdict(list) + for i, choice in enumerate(choices): + builder = dungeon_map[choice] + sector_dict[builder].append(candidates[i]) + valid = global_pole.is_valid_multi_choice_2(dungeon_map, dungeon_map.values(), sector_dict) + for i, choice in enumerate(choices): + builder = dungeon_map[choice] + assign_sector(candidates[i], builder, free_location_sectors, global_pole) + return free_location_sectors + + +def assign_location_sectors_minimal(dungeon_map, free_location_sectors, global_pole, world, player): + valid = False + choices = defaultdict(list) sector_list = list(free_location_sectors) random.shuffle(sector_list) orig_location_set = build_orig_location_set(dungeon_map) num_dungeon_items = requested_dungeon_items(world, player) + d_idx = {builder.name: i for i, builder in enumerate(dungeon_map.values())} + next_sector = sector_list.pop() while not valid: - choices, d_idx, totals = weighted_random_locations(dungeon_map, sector_list) - location_set = {x: set(y) for x, y in orig_location_set.items()} - for i, sector in enumerate(sector_list): - d_name = choices[i].name - choice = d_idx[d_name] - totals[choice] += sector.chest_locations - location_set[d_name].update(sector.chest_location_set) - valid = True - for d_name, idx in d_idx.items(): - free_items = count_reserved_locations(world, player, location_set[d_name]) - target = max(free_items, 2) + num_dungeon_items - if totals[idx] < target: - valid = False - break - for i, choice in enumerate(choices): - builder = dungeon_map[choice.name] - assign_sector(sector_list[i], builder, free_location_sectors, global_pole) + choice, totals, location_set = weighted_random_location(dungeon_map, choices, orig_location_set, world, player) + if not choice: + break + choices[choice].append(next_sector) + if global_pole.is_valid_multi_choice_2(dungeon_map, dungeon_map.values(), choices): + idx = d_idx[choice.name] + totals[idx] += next_sector.chest_locations + location_set[choice.name].update(next_sector.chest_location_set) + valid = True + for d_name, idx in d_idx.items(): + free_items = count_reserved_locations(world, player, location_set[d_name]) + target = max(free_items, 2) + num_dungeon_items + if totals[idx] < target: + valid = False + break + if not valid: + if len(sector_list) == 0: + choices = defaultdict(list) + sector_list = list(free_location_sectors) + else: + next_sector = sector_list.pop() + else: + choices[choice].remove(next_sector) + for builder, choice_list in choices.items(): + for choice in choice_list: + assign_sector(choice, builder, free_location_sectors, global_pole) + return free_location_sectors +def weighted_random_location(dungeon_map, choices, orig_location_set, world, player): + population = [] + totals = [] + location_set = {x: set(y) for x, y in orig_location_set.items()} + num_dungeon_items = requested_dungeon_items(world, player) + for i, dungeon_builder in enumerate(dungeon_map.values()): + ttl = dungeon_builder.location_cnt + sum(sector.chest_locations for sector in choices[dungeon_builder]) + totals.append(ttl) + builder_set = location_set[dungeon_builder.name] + builder_set.update(set().union(*(s.chest_location_set for s in choices[dungeon_builder]))) + free_items = count_reserved_locations(world, player, builder_set) + target = max(free_items, 2) + num_dungeon_items + if ttl < target: + population.append(dungeon_builder) + choice = random.choice(population) if len(population) > 0 else None + return choice, totals, location_set + + +# deprecated def weighted_random_locations(dungeon_map, free_location_sectors): population = [] ttl_assigned = 0 diff --git a/DungeonRandomizer.py b/DungeonRandomizer.py index d24e81d6..cf0f73bc 100755 --- a/DungeonRandomizer.py +++ b/DungeonRandomizer.py @@ -1,11 +1,11 @@ #!/usr/bin/env python3 -import argparse -import copy +if __name__ == '__main__': + from source.meta.check_requirements import check_requirements + check_requirements(console=True) + import os import logging import RaceRandom as random -import textwrap -import shlex import sys from source.classes.BabelFish import BabelFish diff --git a/Dungeons.py b/Dungeons.py index 08bb25ba..188cf59f 100644 --- a/Dungeons.py +++ b/Dungeons.py @@ -84,7 +84,7 @@ hera_regions = [ 'Hera Back - Ranged Crystal', 'Hera Basement Cage', 'Hera Basement Cage - Crystal', 'Hera Tile Room', 'Hera Tridorm', 'Hera Tridorm - Crystal', 'Hera Torches', 'Hera Beetles', 'Hera Startile Corner', 'Hera Startile Wide', 'Hera Startile Wide - Crystal', 'Hera 4F', 'Hera Big Chest Landing', 'Hera 5F', - 'Hera Fairies', 'Hera Boss', 'Hera Portal' + 'Hera 5F Pot Block', 'Hera Fairies', 'Hera Boss', 'Hera Portal' ] tower_regions = [ @@ -110,24 +110,24 @@ pod_regions = [ swamp_regions = [ 'Swamp Lobby', 'Swamp Entrance', 'Swamp Pot Row', 'Swamp Map Ledge', 'Swamp Trench 1 Approach', 'Swamp Trench 1 Nexus', 'Swamp Trench 1 Alcove', 'Swamp Trench 1 Key Ledge', 'Swamp Trench 1 Departure', - 'Swamp Hammer Switch', 'Swamp Hub', 'Swamp Hub Dead Ledge', 'Swamp Hub North Ledge', 'Swamp Donut Top', - 'Swamp Donut Bottom', 'Swamp Compass Donut', 'Swamp Crystal Switch Outer', 'Swamp Crystal Switch Outer - Ranged Crystal', - 'Swamp Crystal Switch Inner', 'Swamp Crystal Switch Inner - Crystal', 'Swamp Shortcut', 'Swamp Trench 2 Pots', - 'Swamp Trench 2 Blocks', 'Swamp Trench 2 Alcove', 'Swamp Trench 2 Departure', 'Swamp Big Key Ledge', - 'Swamp West Shallows', 'Swamp West Block Path', 'Swamp West Ledge', 'Swamp Barrier Ledge', 'Swamp Barrier', - 'Swamp Attic', 'Swamp Push Statue', 'Swamp Shooters', 'Swamp Left Elbow', 'Swamp Right Elbow', 'Swamp Drain Left', - 'Swamp Drain Right', 'Swamp Flooded Room', 'Swamp Flooded Spot', 'Swamp Basement Shallows', 'Swamp Waterfall Room', - 'Swamp Refill', 'Swamp Behind Waterfall', 'Swamp C', 'Swamp Waterway', 'Swamp I', 'Swamp T', 'Swamp Boss', - 'Swamp Portal' + 'Swamp Hammer Switch', 'Swamp Hub', 'Swamp Hub Side Ledges', 'Swamp Hub Dead Ledge', 'Swamp Hub North Ledge', + 'Swamp Donut Top', 'Swamp Donut Bottom', 'Swamp Compass Donut', 'Swamp Crystal Switch Outer', + 'Swamp Crystal Switch Outer - Ranged Crystal', 'Swamp Crystal Switch Inner', 'Swamp Crystal Switch Inner - Crystal', + 'Swamp Shortcut', 'Swamp Trench 2 Pots', 'Swamp Trench 2 Blocks', 'Swamp Trench 2 Alcove', + 'Swamp Trench 2 Departure', 'Swamp Big Key Ledge', 'Swamp West Shallows', 'Swamp West Block Path', + 'Swamp West Ledge', 'Swamp Barrier Ledge', 'Swamp Barrier', 'Swamp Attic', 'Swamp Push Statue', 'Swamp Shooters', + 'Swamp Left Elbow', 'Swamp Right Elbow', 'Swamp Drain Left', 'Swamp Drain Right', 'Swamp Flooded Room', + 'Swamp Flooded Spot', 'Swamp Basement Shallows', 'Swamp Waterfall Room', 'Swamp Refill', 'Swamp Behind Waterfall', + 'Swamp C', 'Swamp Waterway', 'Swamp I', 'Swamp T', 'Swamp Boss', 'Swamp Portal' ] skull_regions = [ 'Skull 1 Lobby', 'Skull Map Room', 'Skull Pot Circle', 'Skull Pull Switch', 'Skull Big Chest', 'Skull Pinball', 'Skull Pot Prison', 'Skull Compass Room', 'Skull Left Drop', 'Skull 2 East Lobby', 'Skull Big Key', - 'Skull Lone Pot', 'Skull Small Hall', 'Skull Back Drop', 'Skull 2 West Lobby', 'Skull X Room', 'Skull 3 Lobby', - 'Skull East Bridge', 'Skull West Bridge Nook', 'Skull Star Pits', 'Skull Torch Room', 'Skull Vines', - 'Skull Spike Corner', 'Skull Final Drop', 'Skull Boss', 'Skull 1 Portal', 'Skull 2 East Portal', - 'Skull 2 West Portal', 'Skull 3 Portal' + 'Skull Lone Pot', 'Skull Small Hall', 'Skull Back Drop', 'Skull 2 West Lobby', 'Skull 2 West Lobby Ledge', + 'Skull X Room', 'Skull 3 Lobby', 'Skull East Bridge', 'Skull West Bridge Nook', 'Skull Star Pits', + 'Skull Torch Room', 'Skull Vines', 'Skull Spike Corner', 'Skull Final Drop', 'Skull Boss', + 'Skull 1 Portal', 'Skull 2 East Portal', 'Skull 2 West Portal', 'Skull 3 Portal' ] thieves_regions = [ @@ -135,10 +135,10 @@ thieves_regions = [ 'Thieves Big Chest Nook', 'Thieves Hallway', 'Thieves Boss', 'Thieves Pot Alcove Mid', 'Thieves Pot Alcove Bottom', 'Thieves Pot Alcove Top', 'Thieves Conveyor Maze', 'Thieves Spike Track', 'Thieves Hellway', 'Thieves Hellway N Crystal', 'Thieves Hellway S Crystal', 'Thieves Triple Bypass', 'Thieves Spike Switch', - 'Thieves Attic', 'Thieves Attic Hint', 'Thieves Cricket Hall Left', 'Thieves Cricket Hall Right', - 'Thieves Attic Window', 'Thieves Basement Block', 'Thieves Blocked Entry', 'Thieves Lonely Zazak', - "Thieves Blind's Cell", "Thieves Blind's Cell Interior", 'Thieves Conveyor Bridge', 'Thieves Conveyor Block', - 'Thieves Big Chest Room', 'Thieves Trap', 'Thieves Town Portal' + 'Thieves Attic', 'Thieves Attic Hint', 'Thieves Attic Switch', 'Thieves Cricket Hall Left', + 'Thieves Cricket Hall Right', 'Thieves Attic Window', 'Thieves Basement Block', 'Thieves Blocked Entry', + 'Thieves Lonely Zazak', "Thieves Blind's Cell", "Thieves Blind's Cell Interior", 'Thieves Conveyor Bridge', + 'Thieves Conveyor Block', 'Thieves Big Chest Room', 'Thieves Trap', 'Thieves Town Portal' ] ice_regions = [ @@ -168,29 +168,31 @@ mire_regions = [ ] tr_regions = [ - 'TR Main Lobby', 'TR Lobby Ledge', 'TR Compass Room', 'TR Hub', 'TR Torches Ledge', 'TR Torches', 'TR Roller Room', - 'TR Tile Room', 'TR Refill', 'TR Pokey 1', 'TR Chain Chomps Top', 'TR Chain Chomps Top - Crystal', - 'TR Chain Chomps Bottom', 'TR Chain Chomps Bottom - Ranged Crystal', 'TR Pipe Pit', 'TR Pipe Ledge', 'TR Lava Dual Pipes', - 'TR Lava Island', 'TR Lava Escape', 'TR Pokey 2 Top', 'TR Pokey 2 Top - Crystal', 'TR Pokey 2 Bottom', 'TR Pokey 2 Bottom - Ranged Crystal', - 'TR Twin Pokeys', 'TR Hallway', 'TR Dodgers', 'TR Big View','TR Big Chest', 'TR Big Chest Entrance', - 'TR Lazy Eyes', 'TR Dash Room', 'TR Tongue Pull', 'TR Rupees', 'TR Crystaroller Bottom', - 'TR Crystaroller Middle', 'TR Crystaroller Top', 'TR Crystaroller Top - Crystal', 'TR Crystaroller Chest', - 'TR Crystaroller Middle - Ranged Crystal', 'TR Crystaroller Bottom - Ranged Crystal', 'TR Dark Ride', 'TR Dash Bridge', 'TR Eye Bridge', + 'TR Main Lobby', 'TR Lobby Ledge', 'TR Compass Room', 'TR Hub', 'TR Hub Ledges', 'TR Torches Ledge', 'TR Torches', + 'TR Roller Room', 'TR Tile Room', 'TR Refill', 'TR Pokey 1', 'TR Chain Chomps Top', 'TR Chain Chomps Top - Crystal', + 'TR Chain Chomps Bottom', 'TR Chain Chomps Bottom - Ranged Crystal', 'TR Pipe Pit', 'TR Pipe Ledge', + 'TR Lava Dual Pipes', 'TR Lava Island', 'TR Lava Escape', 'TR Pokey 2 Top', 'TR Pokey 2 Top - Crystal', + 'TR Pokey 2 Bottom', 'TR Pokey 2 Bottom - Ranged Crystal', 'TR Twin Pokeys', 'TR Hallway', 'TR Dodgers', + 'TR Big View', 'TR Big Chest', 'TR Big Chest Entrance', 'TR Lazy Eyes', 'TR Dash Room', 'TR Tongue Pull', + 'TR Rupees', 'TR Crystaroller Bottom', 'TR Crystaroller Middle', 'TR Crystaroller Top', + 'TR Crystaroller Top - Crystal', 'TR Crystaroller Chest', 'TR Crystaroller Middle - Ranged Crystal', + 'TR Crystaroller Bottom - Ranged Crystal', 'TR Dark Ride', 'TR Dark Ride Ledges', 'TR Dash Bridge', 'TR Eye Bridge', 'TR Crystal Maze Start', 'TR Crystal Maze Start - Crystal', 'TR Crystal Maze Interior', 'TR Crystal Maze End', - 'TR Crystal Maze End - Ranged Crystal', 'TR Final Abyss', 'TR Boss', 'Turtle Rock Main Portal', - 'Turtle Rock Lazy Eyes Portal', 'Turtle Rock Chest Portal', 'Turtle Rock Eye Bridge Portal' + 'TR Crystal Maze End - Ranged Crystal', 'TR Final Abyss Balcony', 'TR Final Abyss Ledge', 'TR Boss', + 'Turtle Rock Main Portal', 'Turtle Rock Lazy Eyes Portal', 'Turtle Rock Chest Portal', + 'Turtle Rock Eye Bridge Portal' ] gt_regions = [ 'GT Lobby', 'GT Bob\'s Torch', 'GT Hope Room', 'GT Big Chest', 'GT Blocked Stairs', 'GT Bob\'s Room', 'GT Tile Room', 'GT Speed Torch', 'GT Speed Torch Upper', 'GT Pots n Blocks', 'GT Crystal Conveyor', 'GT Crystal Conveyor Corner', 'GT Crystal Conveyor Left', 'GT Crystal Conveyor - Ranged Crystal', - 'GT Crystal Conveyor Corner - Ranged Crystal', - 'GT Compass Room', 'GT Invisible Bridges', 'GT Invisible Catwalk', 'GT Conveyor Cross', 'GT Hookshot East Platform', - 'GT Hookshot North Platform', 'GT Hookshot South Platform', 'GT Hookshot South Entry', 'GT Hookshot South Entry - Ranged Crystal', 'GT Map Room', + 'GT Crystal Conveyor Corner - Ranged Crystal', 'GT Compass Room', 'GT Invisible Bridges', 'GT Invisible Catwalk', + 'GT Conveyor Cross', 'GT Hookshot East Platform', 'GT Hookshot Mid Platform', 'GT Hookshot North Platform', + 'GT Hookshot South Platform', 'GT Hookshot South Entry', 'GT Hookshot South Entry - Ranged Crystal', 'GT Map Room', 'GT Double Switch Entry', 'GT Double Switch Pot Corners - Ranged Switches', 'GT Double Switch Pot Corners', - 'GT Double Switch Left', 'GT Double Switch Left - Crystal', - 'GT Double Switch Entry - Ranged Switches', 'GT Double Switch Exit', 'GT Spike Crystal Left', + 'GT Double Switch Left', 'GT Double Switch Left - Crystal', 'GT Double Switch Entry - Ranged Switches', + 'GT Double Switch Exit', 'GT Spike Crystal Left', 'GT Spike Crystal Right', 'GT Warp Maze - Left Section', 'GT Warp Maze - Mid Section', 'GT Warp Maze - Right Section', 'GT Warp Maze - Pit Section', 'GT Warp Maze - Pit Exit Warp Spot', 'GT Warp Maze Exit Section', 'GT Firesnake Room', 'GT Firesnake Room Ledge', 'GT Warp Maze - Rail Choice', diff --git a/EntranceShuffle.py b/EntranceShuffle.py index 54143e0c..dc48acdc 100644 --- a/EntranceShuffle.py +++ b/EntranceShuffle.py @@ -1448,9 +1448,8 @@ def junk_fill_inaccessible(world, player): for p in range(1, world.players + 1): world.key_logic[p] = {} - base_world = copy_world(world) + base_world = copy_world(world, True) base_world.override_bomb_check = True - world.key_logic = {} # remove regions that have a dungeon entrance accessible_regions = list() @@ -1617,9 +1616,8 @@ def build_accessible_entrance_list(world, start_region, player, assumed_inventor for p in range(1, world.players + 1): world.key_logic[p] = {} - base_world = copy_world(world) + base_world = copy_world(world, True) base_world.override_bomb_check = True - world.key_logic = {} connect_simple(base_world, 'Links House S&Q', start_region, player) blank_state = CollectionState(base_world) @@ -1727,9 +1725,8 @@ def can_reach(world, entrance_name, region_name, player): for p in range(1, world.players + 1): world.key_logic[p] = {} - base_world = copy_world(world) + base_world = copy_world(world, True) base_world.override_bomb_check = True - world.key_logic = {} entrance = world.get_entrance(entrance_name, player) connect_simple(base_world, 'Links House S&Q', entrance.parent_region.name, player) @@ -2059,6 +2056,8 @@ mandatory_connections = [('Old Man S&Q', 'Old Man House'), ('Lost Woods Hideout (top to bottom)', 'Lost Woods Hideout (bottom)'), ('Lumberjack Tree (top to bottom)', 'Lumberjack Tree (bottom)'), ('Kakariko Well (top to bottom)', 'Kakariko Well (bottom)'), + ('Kakariko Well (top to back)', 'Kakariko Well (back)'), + ('Blinds Hideout N', 'Blinds Hideout (Top)'), ('Bat Cave Door', 'Bat Cave (left)'), ('Sewer Drop', 'Sewers Rat Path'), ('Old Man Cave Dropdown', 'Old Man Cave'), @@ -2066,10 +2065,13 @@ mandatory_connections = [('Old Man S&Q', 'Old Man House'), ('Old Man House Back to Front', 'Old Man House'), ('Spectacle Rock Cave Drop', 'Spectacle Rock Cave (Bottom)'), ('Spectacle Rock Cave Peak Drop', 'Spectacle Rock Cave (Bottom)'), + ('Death Mountain Return Cave E', 'Death Mountain Return Cave (right)'), + ('Death Mountain Return Cave W', 'Death Mountain Return Cave (left)'), ('Spiral Cave (top to bottom)', 'Spiral Cave (Bottom)'), ('Light World Death Mountain Shop', 'Light World Death Mountain Shop'), ('Paradox Cave Push Block Reverse', 'Paradox Cave Chest Area'), ('Paradox Cave Push Block', 'Paradox Cave Front'), + ('Paradox Cave Chest Area NE', 'Paradox Cave Bomb Area'), ('Paradox Cave Bomb Jump', 'Paradox Cave'), ('Paradox Cave Drop', 'Paradox Cave Chest Area'), ('Fairy Ascension Cave Climb', 'Fairy Ascension Cave (Top)'), @@ -2081,6 +2083,8 @@ mandatory_connections = [('Old Man S&Q', 'Old Man House'), ('Hookshot Cave Middle to Front', 'Hookshot Cave (Front)'), ('Hookshot Cave Middle to Back', 'Hookshot Cave (Back)'), ('Hookshot Cave Back to Middle', 'Hookshot Cave (Middle)'), + ('Hookshot Cave Bonk Path', 'Hookshot Cave (Bonk Islands)'), + ('Hookshot Cave Hook Path', 'Hookshot Cave (Hook Islands)'), ('Ganon Drop', 'Bottom of Pyramid') ] diff --git a/Fill.py b/Fill.py index b5005182..9bf7f98f 100644 --- a/Fill.py +++ b/Fill.py @@ -2,11 +2,13 @@ import RaceRandom as random import collections import itertools import logging +import math -from BaseClasses import CollectionState, FillError +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 def get_dungeon_item_pool(world): @@ -350,6 +352,25 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None # get items to distribute classify_major_items(world) + # handle pot shuffle + pots_used = False + pot_item_pool = collections.defaultdict(list) + for item in world.itempool: + if item.name in ['Chicken', 'Big Magic']: # can only fill these in that players world + pot_item_pool[item.player].append(item) + for player, pot_pool in pot_item_pool.items(): + if pot_pool: + for pot_item in pot_pool: + world.itempool.remove(pot_item) + pot_locations = [location for location in fill_locations + if location.type == LocationType.Pot and location.player == player] + pot_locations = filter_pot_locations(pot_locations, world) + fast_fill_helper(world, pot_pool, pot_locations) + pots_used = True + if pots_used: + fill_locations = world.get_unfilled_locations() + random.shuffle(fill_locations) + random.shuffle(world.itempool) progitempool = [item for item in world.itempool if item.advancement] prioitempool = [item for item in world.itempool if not item.advancement and item.priority] @@ -360,12 +381,24 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None # fill in gtower locations with trash first for player in range(1, world.players + 1): - if not gftower_trash or not world.ganonstower_vanilla[player] or world.doorShuffle[player] == 'crossed' or world.logic[player] in ['owglitches', 'nologic']: + if (not gftower_trash or not world.ganonstower_vanilla[player] + or world.logic[player] in ['owglitches', 'nologic']): continue - max_trash = 8 if world.algorithm == 'dungeon_only' else 15 - gftower_trash_count = (random.randint(15, 50) if world.goal[player] in ['triforcehunt', 'trinity'] else random.randint(0, max_trash)) + gt_count, total_count = calc_trash_locations(world, player) + scale_factor = .75 * (world.crystals_needed_for_gt[player] / 7) + if world.algorithm == 'dungeon_only': + reserved_space = sum(1 for i in progitempool+prioitempool if i.player == player) + max_trash = max(0, min(gt_count, total_count - reserved_space)) + else: + max_trash = gt_count + scaled_trash = math.floor(max_trash * scale_factor) + if world.goal[player] in ['triforcehunt', 'trinity']: + gftower_trash_count = random.randint(scaled_trash, max_trash) + else: + gftower_trash_count = random.randint(0, scaled_trash) - gtower_locations = [location for location in fill_locations if 'Ganons Tower' in location.name and location.player == player] + gtower_locations = [location for location in fill_locations if location.parent_region.dungeon + and location.parent_region.dungeon.name == 'Ganons Tower' and location.player == player] random.shuffle(gtower_locations) trashcnt = 0 while gtower_locations and restitempool and trashcnt < gftower_trash_count: @@ -415,6 +448,8 @@ def distribute_items_restrictive(world, gftower_trash=False, fill_locations=None fill_locations.remove(l) filtered_fill(world, placeholder_items, placeholder_locations) + if world.players > 1: + fast_fill_pot_for_multiworld(world, restitempool, fill_locations) if world.algorithm == 'vanilla_fill': fast_vanilla_fill(world, restitempool, fill_locations) else: @@ -424,6 +459,55 @@ 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) + + +def calc_trash_locations(world, player): + total_count, gt_count = 0, 0 + for loc in world.get_locations(): + if (loc.player == player and loc.item is None + and (loc.type not in {LocationType.Pot, LocationType.Drop, LocationType.Normal} or not loc.forced_item) + and (loc.type != LocationType.Shop or world.shopsanity[player]) + and loc.parent_region.dungeon): + total_count += 1 + if loc.parent_region.dungeon.name == 'Ganons Tower': + gt_count += 1 + return gt_count, total_count + + +def ensure_good_pots(world, write_skips=False): + for loc in world.get_locations(): + # convert Arrows 5 and Nothing when necessary + if (loc.item.name in {'Arrows (5)', 'Nothing'} + and (loc.type != LocationType.Pot or loc.item.player != loc.player)): + loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.item.player) + # can be placed here by multiworld balancing or shop balancing + # change it to something normal for the player it got swapped to + elif (loc.item.name in {'Chicken', 'Big Magic'} + and (loc.type != LocationType.Pot or loc.item.player != loc.player)): + if loc.type == LocationType.Pot: + loc.item.player = loc.player + else: + loc.item = ItemFactory(invalid_location_replacement[loc.item.name], loc.player) + # do the arrow retro check + if world.retro[loc.item.player] and loc.item.name in {'Arrows (5)', 'Arrows (10)'}: + loc.item = ItemFactory('Rupees (5)', loc.item.player) + # don't write out all pots to spoiler + if write_skips: + if loc.type == LocationType.Pot and loc.item.name in valid_pot_items: + loc.skip = True + + +invalid_location_replacement = {'Arrows (5)': 'Arrows (10)', 'Nothing': 'Rupees (5)', + 'Chicken': 'Rupees (5)', 'Big Magic': 'Small Magic'} + + +def fast_fill_helper(world, item_pool, fill_locations): + if world.algorithm == 'vanilla_fill': + fast_vanilla_fill(world, item_pool, fill_locations) + else: + fast_fill(world, item_pool, fill_locations) + # todo: other fast fill methods? def fast_fill(world, item_pool, fill_locations): @@ -433,6 +517,28 @@ def fast_fill(world, item_pool, fill_locations): world.push_item(spot_to_fill, item_to_place, False) +def fast_fill_pot_for_multiworld(world, item_pool, fill_locations): + pot_item_pool = collections.defaultdict(list) + pot_fill_locations = collections.defaultdict(list) + for item in item_pool: + if item.name in valid_pot_items: + pot_item_pool[item.player].append(item) + for loc in fill_locations: + if loc.type == LocationType.Pot: + pot_fill_locations[loc.player].append(loc) + for player in range(1, world.players+1): + flex = 256 - world.pot_contents[player].multiworld_count + fill_count = len(pot_fill_locations[player]) - flex + if fill_count > 0: + fill_spots = random.sample(pot_fill_locations[player], fill_count) + fill_items = random.sample(pot_item_pool[player], fill_count) + for x in fill_items: + item_pool.remove(x) + for x in fill_spots: + fill_locations.remove(x) + fast_fill(world, fill_items, fill_spots) + + def filtered_fill(world, item_pool, fill_locations): while item_pool and fill_locations: item_to_place = item_pool.pop() diff --git a/Gui.py b/Gui.py index f6fc7d62..948b2b4e 100755 --- a/Gui.py +++ b/Gui.py @@ -1,3 +1,7 @@ +if __name__ == '__main__': + from source.meta.check_requirements import check_requirements + check_requirements() + import json import os import sys @@ -106,7 +110,7 @@ def guiMain(args=None): self.pages["startinventory"] = ttk.Frame(self.notebook) self.pages["custom"] = ttk.Frame(self.notebook) self.notebook.add(self.pages["randomizer"], text='Randomize') - self.notebook.add(self.pages["adjust"], text='Adjust') + self.notebook.add(self.pages["adjust"], text='Adjust/Patch') self.notebook.add(self.pages["startinventory"], text='Starting Inventory') self.notebook.add(self.pages["custom"], text='Custom Item Pool') self.notebook.pack() diff --git a/InitialSram.py b/InitialSram.py new file mode 100644 index 00000000..0be80de2 --- /dev/null +++ b/InitialSram.py @@ -0,0 +1,256 @@ +from dataclasses import dataclass, field +from typing import List + +from BaseClasses import CollectionState +from Utils import count_set_bits + +SRAM_SIZE = 0x500 +ROOM_DATA = 0x000 +OVERWORLD_DATA = 0x280 + +def _new_default_sram(): + sram_buf = [0x00] * 0x500 + sram_buf[ROOM_DATA+0x20D] = 0xF0 + sram_buf[ROOM_DATA+0x20F] = 0xF0 + sram_buf[0x379] = 0x68 + sram_buf[0x401] = 0xFF + sram_buf[0x402] = 0xFF + return sram_buf + +@dataclass +class InitialSram: + _initial_sram_bytes: List[int] = field(default_factory=_new_default_sram) + + def _set_value(self, idx: int, val:int): + if idx > SRAM_SIZE: + raise IndexError('SRAM index out of bounds: {idx}') + if not (-1 < val < 256): + raise ValueError('SRAM value must be between 0 and 255: {val}') + self._initial_sram_bytes[idx] = val + + def _or_value(self, idx: int, val:int): + if idx > SRAM_SIZE: + raise IndexError('SRAM index out of bounds: {idx}') + if not (-1 < val < 256): + raise ValueError('SRAM value must be between 0 and 255: {val}') + self._initial_sram_bytes[idx] |= val + + def pre_open_aga_curtains(self): + self._or_value(ROOM_DATA+0x61, 0x80) + + def pre_open_skullwoods_curtains(self): + self._or_value(ROOM_DATA+0x93, 0x80) + + def pre_open_lumberjack(self): + self._or_value(OVERWORLD_DATA+0x02, 0x20) + + def pre_open_castle_gate(self): + self._or_value(OVERWORLD_DATA+0x1B, 0x20) + + def pre_open_ganons_tower(self): + self._or_value(OVERWORLD_DATA+0x43, 0x20) + + def pre_open_pyramid_hole(self): + self._or_value(OVERWORLD_DATA+0x5B, 0x20) + + def pre_set_overworld_flag(self, owid, bitmask): + self._or_value(OVERWORLD_DATA+owid, bitmask) + + def set_starting_equipment(self, world: object, player: int): + equip = [0] * (0x340 + 0x4F) + equip[0x36C] = 0x18 + equip[0x36D] = 0x18 + equip[0x379] = 0x68 + if world.bombbag[player]: + starting_max_bombs = 0 + else: + starting_max_bombs = 10 + starting_max_arrows = 30 + starting_bomb_cap_upgrades = 0 + starting_arrow_cap_upgrades = 0 + starting_bombs = 0 + starting_arrows = 0 + + startingstate = CollectionState(world) + + if startingstate.has('Bow', player): + equip[0x340] = 3 if startingstate.has('Silver Arrows', player) else 1 + equip[0x38E] |= 0x20 # progressive flag to get the correct hint in all cases + if not world.retro[player]: + equip[0x38E] |= 0x80 + if startingstate.has('Silver Arrows', player): + equip[0x38E] |= 0x40 + + if startingstate.has('Titans Mitts', player): + equip[0x354] = 2 + elif startingstate.has('Power Glove', player): + equip[0x354] = 1 + + if startingstate.has('Golden Sword', player): + equip[0x359] = 4 + elif startingstate.has('Tempered Sword', player): + equip[0x359] = 3 + elif startingstate.has('Master Sword', player): + equip[0x359] = 2 + elif startingstate.has('Fighter Sword', player): + equip[0x359] = 1 + + if startingstate.has('Mirror Shield', player): + equip[0x35A] = 3 + elif startingstate.has('Red Shield', player): + equip[0x35A] = 2 + elif startingstate.has('Blue Shield', player): + equip[0x35A] = 1 + + if startingstate.has('Red Mail', player): + equip[0x35B] = 2 + elif startingstate.has('Blue Mail', player): + equip[0x35B] = 1 + + if startingstate.has('Magic Upgrade (1/4)', player): + equip[0x37B] = 2 + equip[0x36E] = 0x80 + elif startingstate.has('Magic Upgrade (1/2)', player): + equip[0x37B] = 1 + equip[0x36E] = 0x80 + + for item in world.precollected_items: + if item.player != player: + continue + + if item.name in ['Bow', 'Silver Arrows', 'Progressive Bow', 'Progressive Bow (Alt)', + 'Titans Mitts', 'Power Glove', 'Progressive Glove', + 'Golden Sword', 'Tempered Sword', 'Master Sword', 'Fighter Sword', 'Progressive Sword', + 'Mirror Shield', 'Red Shield', 'Blue Shield', 'Progressive Shield', + 'Red Mail', 'Blue Mail', 'Progressive Armor', + 'Magic Upgrade (1/4)', 'Magic Upgrade (1/2)']: + continue + + set_table = {'Book of Mudora': (0x34E, 1), 'Hammer': (0x34B, 1), 'Bug Catching Net': (0x34D, 1), 'Hookshot': (0x342, 1), 'Magic Mirror': (0x353, 2), + 'Cape': (0x352, 1), 'Lamp': (0x34A, 1), 'Moon Pearl': (0x357, 1), 'Cane of Somaria': (0x350, 1), 'Cane of Byrna': (0x351, 1), + 'Fire Rod': (0x345, 1), 'Ice Rod': (0x346, 1), 'Bombos': (0x347, 1), 'Ether': (0x348, 1), 'Quake': (0x349, 1)} + or_table = {'Green Pendant': (0x374, 0x04), 'Red Pendant': (0x374, 0x01), 'Blue Pendant': (0x374, 0x02), + 'Crystal 1': (0x37A, 0x02), 'Crystal 2': (0x37A, 0x10), 'Crystal 3': (0x37A, 0x40), 'Crystal 4': (0x37A, 0x20), + 'Crystal 5': (0x37A, 0x04), 'Crystal 6': (0x37A, 0x01), 'Crystal 7': (0x37A, 0x08), + 'Big Key (Eastern Palace)': (0x367, 0x20), 'Compass (Eastern Palace)': (0x365, 0x20), 'Map (Eastern Palace)': (0x369, 0x20), + 'Big Key (Desert Palace)': (0x367, 0x10), 'Compass (Desert Palace)': (0x365, 0x10), 'Map (Desert Palace)': (0x369, 0x10), + 'Big Key (Tower of Hera)': (0x366, 0x20), 'Compass (Tower of Hera)': (0x364, 0x20), 'Map (Tower of Hera)': (0x368, 0x20), + 'Big Key (Escape)': (0x367, 0xC0), 'Compass (Escape)': (0x365, 0xC0), 'Map (Escape)': (0x369, 0xC0), + 'Big Key (Agahnims Tower)': (0x367, 0x08), 'Compass (Agahnims Tower)': (0x365, 0x08), 'Map (Agahnims Tower)': (0x369, 0x08), + 'Big Key (Palace of Darkness)': (0x367, 0x02), 'Compass (Palace of Darkness)': (0x365, 0x02), 'Map (Palace of Darkness)': (0x369, 0x02), + 'Big Key (Thieves Town)': (0x366, 0x10), 'Compass (Thieves Town)': (0x364, 0x10), 'Map (Thieves Town)': (0x368, 0x10), + 'Big Key (Skull Woods)': (0x366, 0x80), 'Compass (Skull Woods)': (0x364, 0x80), 'Map (Skull Woods)': (0x368, 0x80), + 'Big Key (Swamp Palace)': (0x367, 0x04), 'Compass (Swamp Palace)': (0x365, 0x04), 'Map (Swamp Palace)': (0x369, 0x04), + 'Big Key (Ice Palace)': (0x366, 0x40), 'Compass (Ice Palace)': (0x364, 0x40), 'Map (Ice Palace)': (0x368, 0x40), + 'Big Key (Misery Mire)': (0x367, 0x01), 'Compass (Misery Mire)': (0x365, 0x01), 'Map (Misery Mire)': (0x369, 0x01), + 'Big Key (Turtle Rock)': (0x366, 0x08), 'Compass (Turtle Rock)': (0x364, 0x08), 'Map (Turtle Rock)': (0x368, 0x08), + 'Big Key (Ganons Tower)': (0x366, 0x04), 'Compass (Ganons Tower)': (0x364, 0x04), 'Map (Ganons Tower)': (0x368, 0x04)} + set_or_table = {'Flippers': (0x356, 1, 0x379, 0x02),'Pegasus Boots': (0x355, 1, 0x379, 0x04), + 'Shovel': (0x34C, 1, 0x38C, 0x04), 'Ocarina': (0x34C, 3, 0x38C, 0x01), + 'Mushroom': (0x344, 1, 0x38C, 0x20 | 0x08), 'Magic Powder': (0x344, 2, 0x38C, 0x10), + 'Blue Boomerang': (0x341, 1, 0x38C, 0x80), 'Red Boomerang': (0x341, 2, 0x38C, 0x40)} + keys = {'Small Key (Eastern Palace)': [0x37E], 'Small Key (Desert Palace)': [0x37F], + 'Small Key (Tower of Hera)': [0x386], + 'Small Key (Agahnims Tower)': [0x380], 'Small Key (Palace of Darkness)': [0x382], + 'Small Key (Thieves Town)': [0x387], + 'Small Key (Skull Woods)': [0x384], 'Small Key (Swamp Palace)': [0x381], + 'Small Key (Ice Palace)': [0x385], + 'Small Key (Misery Mire)': [0x383], 'Small Key (Turtle Rock)': [0x388], + 'Small Key (Ganons Tower)': [0x389], + 'Small Key (Universal)': [0x38B], 'Small Key (Escape)': [0x37C, 0x37D]} + bottles = {'Bottle': 2, 'Bottle (Red Potion)': 3, 'Bottle (Green Potion)': 4, 'Bottle (Blue Potion)': 5, + 'Bottle (Fairy)': 6, 'Bottle (Bee)': 7, 'Bottle (Good Bee)': 8} + rupees = {'Rupee (1)': 1, 'Rupees (5)': 5, 'Rupees (20)': 20, 'Rupees (50)': 50, 'Rupees (100)': 100, 'Rupees (300)': 300} + bomb_caps = {'Bomb Upgrade (+5)': 5, 'Bomb Upgrade (+10)': 10} + arrow_caps = {'Arrow Upgrade (+5)': 5, 'Arrow Upgrade (+10)': 10} + bombs = {'Single Bomb': 1, 'Bombs (3)': 3, 'Bombs (10)': 10} + arrows = {'Single Arrow': 1, 'Arrows (10)': 10} + + if item.name in set_table: + equip[set_table[item.name][0]] = set_table[item.name][1] + elif item.name in or_table: + equip[or_table[item.name][0]] |= or_table[item.name][1] + elif item.name in set_or_table: + equip[set_or_table[item.name][0]] = set_or_table[item.name][1] + equip[set_or_table[item.name][2]] |= set_or_table[item.name][3] + elif item.name in keys: + for address in keys[item.name]: + equip[address] = min(equip[address] + 1, 99) + elif item.name in bottles: + if equip[0x34F] < world.difficulty_requirements[player].progressive_bottle_limit: + equip[0x35C + equip[0x34F]] = bottles[item.name] + equip[0x34F] += 1 + elif item.name in rupees: + equip[0x360:0x362] = list(min(equip[0x360] + (equip[0x361] << 8) + rupees[item.name], 9999).to_bytes(2, byteorder='little', signed=False)) + equip[0x362:0x364] = list(min(equip[0x362] + (equip[0x363] << 8) + rupees[item.name], 9999).to_bytes(2, byteorder='little', signed=False)) + elif item.name in bomb_caps: + starting_bomb_cap_upgrades += bomb_caps[item.name] + elif item.name in arrow_caps: + starting_arrow_cap_upgrades += arrow_caps[item.name] + elif item.name in bombs: + starting_bombs += bombs[item.name] + elif item.name in arrows: + if world.retro[player]: + equip[0x38E] |= 0x80 + starting_arrows = 1 + else: + starting_arrows += arrows[item.name] + elif item.name in ['Piece of Heart', 'Boss Heart Container', 'Sanctuary Heart Container']: + if item.name == 'Piece of Heart': + equip[0x36B] = (equip[0x36B] + 1) % 4 + if item.name != 'Piece of Heart' or equip[0x36B] == 0: + equip[0x36C] = min(equip[0x36C] + 0x08, 0xA0) + equip[0x36D] = min(equip[0x36D] + 0x08, 0xA0) + else: + raise RuntimeError(f'Unsupported item in starting equipment: {item.name}') + + equip[0x370] = min(starting_bomb_cap_upgrades, 50) + equip[0x371] = min(starting_arrow_cap_upgrades, 70) + equip[0x343] = min(starting_bombs, (equip[0x370] + starting_max_bombs)) + equip[0x377] = min(starting_arrows, (equip[0x371] + starting_max_arrows)) + + # Assertion and copy equip to initial_sram_bytes + assert equip[:0x340] == [0] * 0x340 + self._initial_sram_bytes[0x340:0x38F] = equip[0x340:0x38F] + + # Set counters and highest equipment values + self._initial_sram_bytes[0x471] = count_set_bits(self._initial_sram_bytes[0x37A]) + self._initial_sram_bytes[0x429] = count_set_bits(self._initial_sram_bytes[0x374]) + self._initial_sram_bytes[0x417] = self._initial_sram_bytes[0x359] + self._initial_sram_bytes[0x422] = self._initial_sram_bytes[0x35A] + self._initial_sram_bytes[0x46E] = self._initial_sram_bytes[0x35B] + + if world.swords[player] == "swordless": + self._initial_sram_bytes[0x359] = 0xFF + self._initial_sram_bytes[0x417] = 0x00 + + def set_starting_rupees(self, rupees: int): + if not (-1 < rupees < 10000): + raise ValueError("Starting rupees must be between 0 and 9999") + self._initial_sram_bytes[0x362] = self._initial_sram_bytes[0x360] = rupees & 0xFF + self._initial_sram_bytes[0x363] = self._initial_sram_bytes[0x361] = rupees >> 8 + + def set_progress_indicator(self, indicator: int): + self._set_value(0x3C5, indicator) + + def set_progress_flags(self, flags: int): + self._set_value(0x3C6, flags) + + def set_starting_entrance(self, entrance: int): + self._set_value(0x3C8, entrance) + + def set_starting_timer(self, seconds: int): + timer = (seconds * 60).to_bytes(4, "little") + self._initial_sram_bytes[0x454] = timer[0] + self._initial_sram_bytes[0x455] = timer[1] + self._initial_sram_bytes[0x456] = timer[2] + self._initial_sram_bytes[0x457] = timer[3] + + def set_swordless_curtains(self): + self._or_value(ROOM_DATA+0x61, 0x80) + self._or_value(ROOM_DATA+0x93, 0x80) + + def get_initial_sram(self): + assert len(self._initial_sram_bytes) == SRAM_SIZE + + return self._initial_sram_bytes[:] diff --git a/ItemList.py b/ItemList.py index f1f9e2c9..18472fb0 100644 --- a/ItemList.py +++ b/ItemList.py @@ -3,13 +3,14 @@ import logging import math import RaceRandom as random -from BaseClasses import Region, RegionType, Shop, ShopType, Location, CollectionState +from BaseClasses import Region, RegionType, Shop, ShopType, Location, CollectionState, PotItem from EntranceShuffle import connect_entrance -from Regions import shop_to_location_table, retro_shops, shop_table_by_location +from Regions import shop_to_location_table, retro_shops, shop_table_by_location, valid_pot_location from Fill import FillError, fill_restrictive, fast_fill, get_dungeon_item_pool +from PotShuffle import vanilla_pots from Items import ItemFactory -from source.item.FillUtil import trash_items +from source.item.FillUtil import trash_items, pot_items import source.classes.constants as CONST @@ -43,6 +44,7 @@ Difficulty = namedtuple('Difficulty', 'progressive_bow_limit', 'heart_piece_limit', 'boss_heart_container_limit']) total_items_to_place = 153 +max_goal = 850 difficulties = { 'normal': Difficulty( @@ -203,6 +205,7 @@ def generate_itempool(world, player): world.push_item(loc, ItemFactory('Triforce', player), False) loc.event = True loc.locked = True + loc.forced_item = loc.item world.get_location('Ganon', player).event = True world.get_location('Ganon', player).locked = True @@ -254,6 +257,9 @@ def generate_itempool(world, player): world.push_item(world.get_location('Ice Block Drop', player), ItemFactory('Convenient Block', player), False) world.get_location('Ice Block Drop', player).event = True world.get_location('Ice Block Drop', player).locked = True + world.push_item(world.get_location('Skull Star Tile', player), ItemFactory('Hidden Pits', player), False) + world.get_location('Skull Star Tile', player).event = True + world.get_location('Skull Star Tile', player).locked = True if world.mode[player] == 'standard': world.push_item(world.get_location('Zelda Pickup', player), ItemFactory('Zelda Herself', player), False) world.get_location('Zelda Pickup', player).event = True @@ -342,8 +348,10 @@ def generate_itempool(world, player): if clock_mode is not None: world.clock_mode = clock_mode - if world.goal[player] in ['triforcehunt', 'trinity']: - world.treasure_hunt_count[player], world.treasure_hunt_total[player] = set_default_triforce(world.goal[player], world.treasure_hunt_count[player], world.treasure_hunt_total[player]) + goal = world.goal[player] + if goal in ['triforcehunt', 'trinity']: + g, t = set_default_triforce(goal, world.treasure_hunt_count[player], world.treasure_hunt_total[player]) + world.treasure_hunt_count[player], world.treasure_hunt_total[player] = g, t world.treasure_hunt_icon[player] = 'Triforce Piece' world.itempool.extend([item for item in get_dungeon_item_pool(world) if item.player == player @@ -393,11 +401,16 @@ def generate_itempool(world, player): if world.retro[player]: set_up_take_anys(world, player) - if world.keydropshuffle[player]: - world.itempool += [ItemFactory('Small Key (Universal)', player)] * 32 + if world.dropshuffle[player]: + world.itempool += [ItemFactory('Small Key (Universal)', player)] * 13 + if world.pottery[player] not in ['none', 'cave']: + world.itempool += [ItemFactory('Small Key (Universal)', player)] * 19 create_dynamic_shop_locations(world, player) + if world.pottery[player] not in ['none', 'keys']: + add_pot_contents(world, player) + take_any_locations = [ 'Snitch Lady (East)', 'Snitch Lady (West)', 'Bush Covered House', 'Light World Bomb Hut', @@ -418,7 +431,9 @@ def set_up_take_anys(world, player): if 'Archery Game' in take_any_locations: take_any_locations.remove('Archery Game') - regions = random.sample(take_any_locations, 5) + take_any_candidates = [x for x in take_any_locations if len(world.get_region(x, player).locations) == 0] + + regions = random.sample(take_any_candidates, 5) old_man_take_any = Region("Old Man Sword Cave", RegionType.Cave, 'the sword cave', player) world.regions.append(old_man_take_any) @@ -551,7 +566,7 @@ def set_up_shops(world, player): removals = [item for item in world.itempool if item.name == 'Bomb Upgrade (+5)' and item.player == player] for remove in removals: world.itempool.remove(remove) - world.itempool.append(ItemFactory('Rupees (50)', player)) # replace the bomb upgrade + world.itempool.append(ItemFactory('Rupees (50)', player)) # replace the bomb upgrade else: cap_shop = world.get_region('Capacity Upgrade', player).shop cap_shop.inventory[0] = cap_shop.inventory[1] # remove bomb capacity upgrades in bombbag @@ -754,17 +769,25 @@ rupee_chart = {'Rupee (1)': 1, 'Rupees (5)': 5, 'Rupees (20)': 20, 'Rupees (50)' 'Rupees (100)': 100, 'Rupees (300)': 300} +def add_pot_contents(world, player): + for super_tile, pot_list in vanilla_pots.items(): + for pot in pot_list: + if pot.item not in [PotItem.Hole, PotItem.Key, PotItem.Switch]: + if valid_pot_location(pot, world.pot_pool[player], world, player): + item = ('Rupees (5)' if world.retro[player] and pot_items[pot.item] == 'Arrows (5)' + else pot_items[pot.item]) + world.itempool.append(ItemFactory(item, player)) + + def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, goal, mode, swords, retro, bombbag, door_shuffle, logic, flute_activated): pool = [] placed_items = {} precollected_items = [] clock_mode = None - if treasure_hunt_total == 0: - if goal == 'triforcehunt': - treasure_hunt_total = 30 - elif goal == 'trinity': - treasure_hunt_total = 10 - triforcepool = ['Triforce Piece'] * int(treasure_hunt_total) + if treasure_hunt_total == 0 and goal in ['triforcehunt', 'trinity']: + treasure_hunt_total = 30 if goal == 'triforcehunt' else 10 + # triforce pieces max out + triforcepool = ['Triforce Piece'] * min(treasure_hunt_total, max_goal) pool.extend(alwaysitems) @@ -793,7 +816,7 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, lamps_needed_for_dark_rooms = 1 # insanity shuffle doesn't have fake LW/DW logic so for now guaranteed Mirror and Moon Pearl at the start - if shuffle == 'insanity_legacy': + if shuffle == 'insanity_legacy': place_item('Link\'s House', 'Magic Mirror') place_item('Sanctuary', 'Moon Pearl') else: @@ -851,6 +874,7 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, place_item('Master Sword Pedestal', swords_to_use.pop()) else: place_item('Master Sword Pedestal', 'Triforce') + pool.append(swords_to_use.pop()) else: pool.extend(diff.progressivesword if want_progressives() else diff.basicsword) if swords == 'assured': @@ -862,26 +886,19 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, pool.remove('Fighter Sword') pool.extend(['Rupees (50)']) - extraitems = total_items_to_place - len(pool) - len(placed_items) - if timer in ['timed', 'timed-countdown']: pool.extend(diff.timedother) - extraitems -= len(diff.timedother) clock_mode = 'stopwatch' if timer == 'timed' else 'countdown' elif timer == 'timed-ohko': pool.extend(diff.timedohko) - extraitems -= len(diff.timedohko) clock_mode = 'countdown-ohko' if goal in ['triforcehunt', 'trinity']: pool.extend(triforcepool) - extraitems -= len(triforcepool) for extra in diff.extras: - if extraitems > 0: - if len(extra) > extraitems: - extra = random.choices(extra, k=extraitems) - pool.extend(extra) - extraitems -= len(extra) + pool.extend(extra) + + # note: massage item pool now handles shrinking the pool appropriately if goal in ['pedestal', 'trinity'] and swords != 'vanilla': place_item('Master Sword Pedestal', 'Triforce') @@ -906,6 +923,7 @@ def get_pool_core(progressive, shuffle, difficulty, treasure_hunt_total, timer, pool.extend(['Small Key (Universal)']) return (pool, placed_items, precollected_items, clock_mode, lamps_needed_for_dark_rooms) + def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, swords, retro, bombbag, customitemarray): pool = [] placed_items = {} @@ -932,7 +950,8 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s # Triforce Pieces if goal in ['triforcehunt', 'trinity']: - customitemarray["triforcepiecesgoal"], customitemarray["triforcepieces"] = set_default_triforce(goal, customitemarray["triforcepiecesgoal"], customitemarray["triforcepieces"]) + g, t = set_default_triforce(goal, customitemarray["triforcepiecesgoal"], customitemarray["triforcepieces"]) + customitemarray["triforcepiecesgoal"], customitemarray["triforcepieces"] = g, t itemtotal = 0 # Bow to Silver Arrows Upgrade, including Generic Keys & Rupoors @@ -964,7 +983,15 @@ def make_custom_item_pool(progressive, shuffle, difficulty, timer, goal, mode, s pool.append(thisbottle) if customitemarray["triforcepieces"] > 0 or customitemarray["triforcepiecesgoal"] > 0: + # Location pool doesn't support larger values + treasure_hunt_count = max(min(customitemarray["triforcepiecesgoal"], max_goal), 1) treasure_hunt_icon = 'Triforce Piece' + # Ensure game is always possible to complete here, force sufficient pieces if the player is unwilling. + if ((customitemarray["triforcepieces"] < treasure_hunt_count) and (goal in ['triforcehunt', 'trinity']) + and (customitemarray["triforce"] == 0)): + extrapieces = treasure_hunt_count - customitemarray["triforcepieces"] + pool.extend(['Triforce Piece'] * extrapieces) + itemtotal = itemtotal + extrapieces if timer in ['display', 'timed', 'timed-countdown']: clock_mode = 'countdown' if timer == 'timed-countdown' else 'stopwatch' @@ -1020,6 +1047,54 @@ def set_default_triforce(goal, custom_goal, custom_total): triforce_total = max(min(custom_total, 128), triforce_goal) #128 max to ensure other progression can fit. return (triforce_goal, triforce_total) + +def make_customizer_pool(world, player): + pool = [] + placed_items = {} + precollected_items = [] + clock_mode = None + + def place_item(loc, item): + assert loc not in placed_items + placed_items[loc] = item + + diff = difficulties[world.difficulty[player]] + for item_name, amount in world.customizer.get_item_pool()[player].items(): + if isinstance(amount, int): + if item_name == 'Bottle (Random)': + for _ in range(amount): + pool.append(random.choice(diff.bottles)) + else: + pool.extend([item_name] * amount) + + timer = world.timer[player] + if timer in ['display', 'timed', 'timed-countdown']: + clock_mode = 'countdown' if timer == 'timed-countdown' else 'stopwatch' + elif timer == 'timed-ohko': + clock_mode = 'countdown-ohko' + elif timer == 'ohko': + clock_mode = 'ohko' + + if world.goal[player] == 'pedestal': + place_item('Master Sword Pedestal', 'Triforce') + + return pool, placed_items, precollected_items, clock_mode, 1 + + +# location pool doesn't support larger values at this time +def set_default_triforce(goal, custom_goal, custom_total): + triforce_goal, triforce_total = 0, 0 + if goal == 'triforcehunt': + triforce_goal, triforce_total = 20, 30 + elif goal == 'trinity': + triforce_goal, triforce_total = 8, 10 + if custom_goal > 0: + triforce_goal = max(min(custom_goal, max_goal), 1) + if custom_total > 0: + triforce_total = max(min(custom_total, max_goal), triforce_goal) + return triforce_goal, triforce_total + + # A quick test to ensure all combinations generate the correct amount of items. def test(): for difficulty in ['normal', 'hard', 'expert']: diff --git a/Items.py b/Items.py index dfb19e19..ea81c136 100644 --- a/Items.py +++ b/Items.py @@ -27,7 +27,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Progressive Bow': (True, False, None, 0x64, 150, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), 'Progressive Bow (Alt)': (True, False, None, 0x65, 150, 'You have\nchosen the\narcher class.', 'the stick and twine', 'arrow-slinging kid', 'arrow sling for sale', 'witch and robin hood', 'archer boy shoots again', 'a Bow'), 'Book of Mudora': (True, False, None, 0x1D, 150, 'This is a\nparadox?!', 'and the story book', 'the scholarly kid', 'moon runes for sale', 'drugs for literacy', 'book-worm boy can read again', 'the Book'), - 'Hammer': (True, False, None, 0x09, 250, 'stop\nhammer time!', 'and m c hammer', 'hammer-smashing kid', 'm c hammer for sale', 'stop... hammer time', 'stop, hammer time', 'the Hammer'), + 'Hammer': (True, False, None, 0x09, 250, 'Stop\nhammer time!', 'and m c hammer', 'hammer-smashing kid', 'm c hammer for sale', 'stop... hammer time', 'stop, hammer time', 'the Hammer'), 'Hookshot': (True, False, None, 0x0A, 250, 'BOING!!!\nBOING!!!\nBOING!!!', 'and the tickle beam', 'tickle-monster kid', 'tickle beam for sale', 'witch and tickle boy', 'beam boy tickles again', 'the Hookshot'), 'Magic Mirror': (True, False, None, 0x1A, 250, 'Isn\'t your\nreflection so\npretty?', 'the face reflector', 'the narcissistic kid', 'your face for sale', 'trades looking-glass', 'narcissistic boy is happy again', 'the Mirror'), 'Ocarina': (True, False, None, 0x14, 250, 'Save the duck\nand fly to\nfreedom!', 'and the duck call', 'the duck-call kid', 'duck call for sale', 'duck-calls for trade', 'ocarina boy plays again', 'the Flute'), @@ -38,65 +38,70 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Mushroom': (True, False, None, 0x29, 50, 'I\'m a fun guy!\n\nI\'m a funghi!', 'and the legal drugs', 'the drug-dealing kid', 'legal drugs for sale', 'shroom swap', 'shroom boy sells drugs again', 'the Mushroom'), 'Shovel': (True, False, None, 0x13, 50, 'Can\n You\n Dig it?', 'and the spade', 'archaeologist kid', 'dirt spade for sale', 'can you dig it', 'shovel boy digs again', 'the Shovel'), 'Lamp': (True, False, None, 0x12, 150, 'Baby, baby,\nbaby.\nLight my way!', 'and the flashlight', 'light-shining kid', 'flashlight for sale', 'fungus for illumination', 'illuminated boy can see again', 'the Lamp'), - 'Magic Powder': (True, False, None, 0x0D, 50, 'you can turn\nanti-faeries\ninto faeries', 'and the magic sack', 'the sack-holding kid', 'magic sack for sale', 'the witch and assistant', 'magic boy plays marbles again', 'the Powder'), + 'Magic Powder': (True, False, None, 0x0D, 50, 'You can turn\nanti-faeries\ninto faeries', 'and the magic sack', 'the sack-holding kid', 'magic sack for sale', 'the witch and assistant', 'magic boy plays marbles again', 'the Powder'), 'Moon Pearl': (True, False, None, 0x1F, 200, ' Bunny Link\n be\n gone!', 'and the jaw breaker', 'fortune-telling kid', 'lunar orb for sale', 'shrooms for moon rock', 'moon boy plays ball again', 'the Moon Pearl'), 'Cane of Somaria': (True, False, None, 0x15, 250, 'I make blocks\nto hold down\nswitches!', 'and the red blocks', 'the block-making kid', 'block stick for sale', 'block stick for trade', 'cane boy makes blocks again', 'the Red Cane'), 'Fire Rod': (True, False, None, 0x07, 250, 'I\'m the hot\nrod. I make\nthings burn!', 'and the flamethrower', 'fire-starting kid', 'rage rod for sale', 'fungus for rage-rod', 'firestarter boy burns again', 'the Fire Rod'), - 'Flippers': (True, False, None, 0x1E, 250, 'fancy a swim?', 'and the toewebs', 'the swimming kid', 'finger webs for sale', 'shrooms let you swim', 'swimming boy swims again', 'the Flippers'), + 'Flippers': (True, False, None, 0x1E, 250, 'Fancy a swim?', 'and the toewebs', 'the swimming kid', 'finger webs for sale', 'shrooms let you swim', 'swimming boy swims again', 'the Flippers'), 'Ice Rod': (True, False, None, 0x08, 250, 'I\'m the cold\nrod. I make\nthings freeze!', 'and the freeze ray', 'the ice-bending kid', 'freeze ray for sale', 'fungus for ice-rod', 'ice-cube boy freezes again', 'the Ice Rod'), 'Titans Mitts': (True, False, None, 0x1C, 200, 'Now you can\nlift heavy\nstuff!', 'and the golden glove', 'body-building kid', 'carry glove for sale', 'fungus for bling-gloves', 'body-building boy has gold again', 'the Mitts'), 'Bombos': (True, False, None, 0x0F, 100, 'Burn, baby,\nburn! Fear my\nring of fire!', 'and the swirly coin', 'coin-collecting kid', 'swirly coin for sale', 'shrooms for swirly-coin', 'medallion boy melts room again', 'Bombos'), 'Ether': (True, False, None, 0x10, 100, 'This magic\ncoin freezes\neverything!', 'and the bolt coin', 'coin-collecting kid', 'bolt coin for sale', 'shrooms for bolt-coin', 'medallion boy sees floor again', 'Ether'), 'Quake': (True, False, None, 0x11, 100, 'Maxing out the\nRichter scale\nis what I do!', 'and the wavy coin', 'coin-collecting kid', 'wavy coin for sale', 'shrooms for wavy-coin', 'medallion boy shakes dirt again', 'Quake'), - 'Bottle': (True, False, None, 0x16, 50, 'Now you can\nstore potions\nand stuff!', 'and the terrarium', 'the terrarium kid', 'terrarium for sale', 'special promotion', 'bottle boy has terrarium again', 'a Bottle'), - 'Bottle (Red Potion)': (True, False, None, 0x2B, 70, 'Hearty red goop!', 'and the red goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has red goo again', 'a Bottle'), - 'Bottle (Green Potion)': (True, False, None, 0x2C, 60, 'Refreshing green goop!', 'and the green goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has green goo again', 'a Bottle'), - 'Bottle (Blue Potion)': (True, False, None, 0x2D, 80, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a Bottle'), - 'Bottle (Fairy)': (True, False, None, 0x3D, 70, 'Save me and I will revive you', 'and the captive', 'the tingle kid', 'hostage for sale', 'fairy dust and shrooms', 'bottle boy has friend again', 'a Bottle'), - 'Bottle (Bee)': (True, False, None, 0x3C, 50, '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 Bottle'), - 'Bottle (Good Bee)': (True, False, None, 0x48, 60, 'I will sting your foes a whole lot!', 'and the sparkle sting', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has beetor again', 'a Bottle'), + 'Bottle': (True, False, None, 0x16, 50, 'Now you can\nstore potions\nand stuff!', 'and the terrarium', 'the terrarium kid', 'terrarium for sale', 'special promotion', 'bottle boy has terrarium again', 'a bottle'), + 'Bottle (Red Potion)': (True, False, None, 0x2B, 70, 'Hearty red goop!', 'and the red goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has red goo again', 'a bottle'), + 'Bottle (Green Potion)': (True, False, None, 0x2C, 60, 'Refreshing green goop!', 'and the green goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has green goo again', 'a bottle'), + 'Bottle (Blue Potion)': (True, False, None, 0x2D, 80, 'Delicious blue goop!', 'and the blue goo', 'the liquid kid', 'potion for sale', 'free samples', 'bottle boy has blue goo again', 'a bottle'), + 'Bottle (Fairy)': (True, False, None, 0x3D, 70, 'Save me and I will revive you', 'and the captive', 'the tingle kid', 'hostage for sale', 'fairy dust and shrooms', 'bottle boy has friend again', 'a bottle'), + 'Bottle (Bee)': (True, False, None, 0x3C, 50, '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 bottle'), + 'Bottle (Good Bee)': (True, False, None, 0x48, 60, 'I will sting your foes a whole lot!', 'and the sparkle sting', 'the beekeeper kid', 'insect for sale', 'shroom pollenation', 'bottle boy has beetor again', 'a bottle'), 'Master Sword': (True, False, 'Sword', 0x50, 100, 'I beat barries and pigs alike', 'and the master sword', 'sword-wielding kid', 'glow sword for sale', 'fungus for blue slasher', 'sword boy fights again', 'the Master Sword'), 'Tempered Sword': (True, False, 'Sword', 0x02, 150, 'I stole the\nblacksmith\'s\njob!', 'the tempered sword', 'sword-wielding kid', 'flame sword for sale', 'fungus for red slasher', 'sword boy fights again', 'the Tempered Sword'), 'Fighter Sword': (True, False, 'Sword', 0x49, 50, 'A pathetic\nsword rests\nhere!', 'the tiny sword', 'sword-wielding kid', 'tiny sword for sale', 'fungus for tiny slasher', 'sword boy fights again', 'the Fighter Sword'), + 'Sword and Shield': (True, False, 'Sword', 0x00, 'An uncle\nsword rests\nhere!', 'the sword and shield', 'sword and shield-wielding kid', 'training set for sale', 'fungus for training set', 'sword and shield boy fights again', 'the small sword and shield'), 'Golden Sword': (True, False, 'Sword', 0x03, 200, 'The butter\nsword rests\nhere!', 'and the butter sword', 'sword-wielding kid', 'butter for sale', 'cap churned to butter', 'sword boy fights again', 'the Golden Sword'), - 'Progressive Sword': (True, False, 'Sword', 0x5E, 150, 'a better copy\nof your sword\nfor your time', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a Sword'), - 'Progressive Glove': (True, False, None, 0x61, 150, 'a way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a Glove'), + 'Progressive Sword': (True, False, 'Sword', 0x5E, 150, 'A better copy\nof your sword\nfor your time', 'the unknown sword', 'sword-wielding kid', 'sword for sale', 'fungus for some slasher', 'sword boy fights again', 'a Sword'), + 'Progressive Glove': (True, False, None, 0x61, 150, 'A way to lift\nheavier things', 'and the lift upgrade', 'body-building kid', 'some glove for sale', 'fungus for gloves', 'body-building boy lifts again', 'a Glove'), 'Silver Arrows': (True, False, None, 0x58, 100, 'Do you fancy\nsilver tipped\narrows?', 'and the ganonsbane', 'ganon-killing kid', 'ganon doom for sale', 'fungus for pork', 'archer boy shines again', 'the Silver Arrows'), - 'Green Pendant': (True, False, 'Crystal', [0x04, 0x38, 0x62, 0x00, 0x69, 0x01], 999, None, None, None, None, None, None, None), - 'Blue Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x02], 999, None, None, None, None, None, None, None), - 'Red Pendant': (True, False, 'Crystal', [0x01, 0x32, 0x60, 0x00, 0x69, 0x03], 999, None, None, None, None, None, None, None), + 'Green Pendant': (True, False, 'Crystal', [0x04, 0x38, 0x62, 0x00, 0x69, 0x01, 0x08], 999, None, None, None, None, None, None, None), + 'Blue Pendant': (True, False, 'Crystal', [0x02, 0x34, 0x60, 0x00, 0x69, 0x02, 0x09], 999, None, None, None, None, None, None, None), + 'Red Pendant': (True, False, 'Crystal', [0x01, 0x32, 0x60, 0x00, 0x69, 0x03, 0x0a], 999, None, None, None, None, None, None, None), 'Triforce': (True, False, None, 0x6A, 777, '\n YOU WIN!', 'and the triforce', 'victorious kid', 'victory for sale', 'fungus for the win', 'greedy boy wins game again', 'the Triforce'), - 'Power Star': (True, False, None, 0x6B, 100, 'a small victory', 'and the power star', 'star-struck kid', 'star for sale', 'see stars with shroom', 'mario powers up again', 'a Power Star'), - 'Triforce Piece': (True, False, None, 0x6C, 100, 'a small victory', 'and the thirdforce', 'triangular kid', 'triangle for sale', 'fungus for triangle', 'wise boy has triangle again', 'a Triforce Piece'), - 'Crystal 1': (True, False, 'Crystal', [0x02, 0x34, 0x64, 0x40, 0x7F, 0x06], 999, None, None, None, None, None, None, None), - 'Crystal 2': (True, False, 'Crystal', [0x10, 0x34, 0x64, 0x40, 0x79, 0x06], 999, None, None, None, None, None, None, None), - 'Crystal 3': (True, False, 'Crystal', [0x40, 0x34, 0x64, 0x40, 0x6C, 0x06], 999, None, None, None, None, None, None, None), - 'Crystal 4': (True, False, 'Crystal', [0x20, 0x34, 0x64, 0x40, 0x6D, 0x06], 999, None, None, None, None, None, None, None), - 'Crystal 5': (True, False, 'Crystal', [0x04, 0x32, 0x64, 0x40, 0x6E, 0x06], 999, None, None, None, None, None, None, None), - 'Crystal 6': (True, False, 'Crystal', [0x01, 0x32, 0x64, 0x40, 0x6F, 0x06], 999, None, None, None, None, None, None, None), - 'Crystal 7': (True, False, 'Crystal', [0x08, 0x34, 0x64, 0x40, 0x7C, 0x06], 999, None, None, None, None, None, None, None), - 'Single Arrow': (False, False, None, 0x43, 3, 'a lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'), + 'Power Star': (True, False, None, 0x6B, 100, 'A small victory', 'and the power star', 'star-struck kid', 'star for sale', 'see stars with shroom', 'mario powers up again', 'a Power Star'), + 'Triforce Piece': (True, False, None, 0x6C, 100, 'A small victory', 'and the thirdforce', 'triangular kid', 'triangle for sale', 'fungus for triangle', 'wise boy has triangle again', 'a Triforce piece'), + 'Crystal 1': (True, False, 'Crystal', [0x02, 0x34, 0x64, 0x40, 0x7F, 0x06, 0x01], 999, None, None, None, None, None, None, None), + 'Crystal 2': (True, False, 'Crystal', [0x10, 0x34, 0x64, 0x40, 0x79, 0x06, 0x02], 999, None, None, None, None, None, None, None), + 'Crystal 3': (True, False, 'Crystal', [0x40, 0x34, 0x64, 0x40, 0x6C, 0x06, 0x03], 999, None, None, None, None, None, None, None), + 'Crystal 4': (True, False, 'Crystal', [0x20, 0x34, 0x64, 0x40, 0x6D, 0x06, 0x04], 999, None, None, None, None, None, None, None), + 'Crystal 5': (True, False, 'Crystal', [0x04, 0x32, 0x64, 0x40, 0x6E, 0x06, 0x05], 999, None, None, None, None, None, None, None), + 'Crystal 6': (True, False, 'Crystal', [0x01, 0x32, 0x64, 0x40, 0x6F, 0x06, 0x06], 999, None, None, None, None, None, None, None), + 'Crystal 7': (True, False, 'Crystal', [0x08, 0x34, 0x64, 0x40, 0x7C, 0x06, 0x07], 999, None, None, None, None, None, None, None), + 'Single Arrow': (False, False, None, 0x43, 3, 'A lonely arrow\nsits here.', 'and the arrow', 'stick-collecting kid', 'sewing needle for sale', 'fungus for arrow', 'archer boy sews again', 'an arrow'), 'Arrows (10)': (False, False, None, 0x44, 30, 'This will give\nyou ten shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'ten arrows'), - 'Arrow Upgrade (+10)': (False, False, None, 0x54, 100, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), - 'Arrow Upgrade (+5)': (False, False, None, 0x53, 100, 'increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), + 'Arrow Upgrade (+10)': (False, False, None, 0x54, 100, 'Increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), + 'Arrow Upgrade (+5)': (False, False, None, 0x53, 100, 'Increase arrow\nstorage, low\nlow price', 'and the quiver', 'quiver-enlarging kid', 'arrow boost for sale', 'witch and more skewers', 'upgrade boy sews more again', 'arrow capacity'), 'Single Bomb': (False, False, None, 0x27, 5, 'I make things\ngo BOOM! But\njust once.', 'and the explosion', 'the bomb-holding kid', 'firecracker for sale', 'blend fungus into bomb', '\'splosion boy explodes again', 'a bomb'), + 'Arrows (5)': (False, False, None, 0x5A, 15, 'This will give\nyou five shots\nwith your bow!', 'and the arrow pack', 'stick-collecting kid', 'sewing kit for sale', 'fungus for arrows', 'archer boy sews again', 'five arrows'), + 'Small Magic': (False, False, None, 0x45, 5, 'A bit of magic', 'and the bit of magic', 'bit-o-magic kid', 'magic bit for sale', 'fungus for magic', 'magic boy conjures again', 'a bit of magic'), + 'Big Magic': (False, False, None, 0x5A, 40, 'A lot of magic', 'and lots of magic', 'lot-o-magic kid', 'magic refill for sale', 'fungus for magic', 'magic boy conjures again', 'a magic refill'), + 'Chicken': (False, False, None, 0x5A, 999, 'Cucco of Legend', 'and the legendary cucco', 'chicken kid', 'fried chicken for sale', 'fungus for chicken', 'cucco boy clucks again', 'a cucco'), 'Bombs (3)': (False, False, None, 0x28, 15, 'I make things\ngo triple\nBOOM!!!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'three bombs'), 'Bombs (10)': (False, False, None, 0x31, 50, 'I make things\ngo BOOM! Ten\ntimes!', 'and the explosions', 'the bomb-holding kid', 'firecrackers for sale', 'blend fungus into bombs', '\'splosion boy explodes again', 'ten bombs'), - 'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), - 'Bomb Upgrade (+5)': (False, False, None, 0x51, 100, 'increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), + 'Bomb Upgrade (+10)': (False, False, None, 0x52, 100, 'Increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), + 'Bomb Upgrade (+5)': (False, False, None, 0x51, 100, 'Increase bomb\nstorage, low\nlow price', 'and the bomb bag', 'boom-enlarging kid', 'bomb boost for sale', 'the shroom goes boom', 'upgrade boy explodes more again', 'bomb capacity'), 'Blue Mail': (False, True, None, 0x22, 50, 'Now you\'re a\nblue elf!', 'and the banana hat', 'the protected kid', 'banana hat for sale', 'the clothing store', 'tailor boy banana hatted again', 'the Blue Mail'), 'Red Mail': (False, True, None, 0x23, 100, 'Now you\'re a\nred elf!', 'and the eggplant hat', 'well-protected kid', 'purple hat for sale', 'the nice clothing store', 'tailor boy fears nothing again', 'the Red Mail'), - 'Progressive Armor': (False, True, None, 0x60, 50, 'time for a\nchange of\nclothes?', 'and the unknown hat', 'the protected kid', 'new hat for sale', 'the clothing store', 'tailor boy has threads again', 'some armor'), + 'Progressive Armor': (False, True, None, 0x60, 50, 'Time for a\nchange of\nclothes?', 'and the unknown hat', 'the protected kid', 'new hat for sale', 'the clothing store', 'tailor boy has threads again', 'some armor'), 'Blue Boomerang': (True, False, None, 0x0C, 50, 'No matter what\nyou do, blue\nreturns to you', 'and the bluemarang', 'the bat-throwing kid', 'bent stick for sale', 'fungus for puma-stick', 'throwing boy plays fetch again', 'the Blue Boomerang'), 'Red Boomerang': (True, False, None, 0x2A, 50, 'No matter what\nyou do, red\nreturns to you', 'and the badmarang', 'the bat-throwing kid', 'air foil for sale', 'fungus for return-stick', 'magical boy plays fetch again', 'the Red Boomerang'), - 'Blue Shield': (False, True, None, 0x04, 50, 'Now you can\ndefend against\npebbles!', 'and the stone blocker', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'the Blue Shield'), - 'Red Shield': (False, True, None, 0x05, 500, 'Now you can\ndefend against\nfireballs!', 'and the shot blocker', 'shield-wielding kid', 'fire shield for sale', 'fungus for fire shield', 'shield boy defends again', 'the Red Shield'), + 'Blue Shield': (False, True, None, 0x04, 50, 'Now you can\ndefend against\npebbles!', 'and the stone blocker', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a Blue Shield'), + 'Red Shield': (False, True, None, 0x05, 500, 'Now you can\ndefend against\nfireballs!', 'and the shot blocker', 'shield-wielding kid', 'fire shield for sale', 'fungus for fire shield', 'shield boy defends again', 'a Red Shield'), 'Mirror Shield': (True, False, None, 0x06, 200, 'Now you can\ndefend against\nlasers!', 'and the laser blocker', 'shield-wielding kid', 'face shield for sale', 'fungus for face shield', 'shield boy defends again', 'the Mirror Shield'), - 'Progressive Shield': (True, False, None, 0x5F, 50, 'have a better\nblocker in\nfront of you', 'and the new shield', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a shield'), + 'Progressive Shield': (True, False, None, 0x5F, 50, 'Have a better\nblocker in\nfront of you', 'and the new shield', 'shield-wielding kid', 'shield for sale', 'fungus for shield', 'shield boy defends again', 'a shield'), 'Bug Catching Net': (True, False, None, 0x21, 50, 'Let\'s catch\nsome bees and\nfaeries!', 'and the bee catcher', 'the bug-catching kid', 'stick web for sale', 'fungus for butterflies', 'wrong boy catches bees again', 'the Bug Net'), 'Cane of Byrna': (True, False, None, 0x18, 50, 'Use this to\nbecome\ninvincible!', 'and the bad cane', 'the spark-making kid', 'spark stick for sale', 'spark-stick for trade', 'cane boy encircles again', 'the Blue Cane'), - 'Boss Heart Container': (False, False, None, 0x3E, 40, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'), - 'Sanctuary Heart Container': (False, False, None, 0x3F, 50, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'), + 'Boss Heart Container': (False, True, None, 0x3E, 40, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'), + 'Sanctuary Heart Container': (False, True, None, 0x3F, 50, 'Maximum health\nincreased!\nYeah!', 'and the full heart', 'the life-giving kid', 'love for sale', 'fungus for life', 'life boy feels love again', 'a heart'), 'Piece of Heart': (False, False, None, 0x17, 10, 'Just a little\npiece of love!', 'and the broken heart', 'the life-giving kid', 'little love for sale', 'fungus for life', 'life boy feels some love again', 'a heart piece'), 'Rupee (1)': (False, False, None, 0x34, 0, 'Just pocket\nchange. Move\nright along.', 'the pocket change', 'poverty-struck kid', 'life lesson for sale', 'buying cheap drugs', 'destitute boy has snack again', 'a green rupee'), 'Rupees (5)': (False, False, None, 0x35, 2, 'Just pocket\nchange. Move\nright along.', 'the pocket change', 'poverty-struck kid', 'life lesson for sale', 'buying cheap drugs', 'destitute boy has snack again', 'a blue rupee'), @@ -104,12 +109,12 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Rupees (50)': (False, False, None, 0x41, 25, 'A rupee pile!\nOkay?', 'and the rupee pile', 'the well-off kid', 'life lesson for sale', 'buying okay drugs', 'destitute boy has dinner again', 'fifty rupees'), 'Rupees (100)': (False, False, None, 0x40, 50, 'A rupee stash!\nHell yeah!', 'and the rupee stash', 'the kind-of-rich kid', 'life lesson for sale', 'buying good drugs', 'affluent boy goes drinking again', 'one hundred rupees'), 'Rupees (300)': (False, False, None, 0x46, 150, 'A rupee hoard!\nHell yeah!', 'and the rupee hoard', 'the really-rich kid', 'life lesson for sale', 'buying the best drugs', 'fat-cat boy is rich again', 'three hundred rupees'), - 'Rupoor': (False, False, None, 0x59, 0, 'a debt collector', 'and the toll-booth', 'the toll-booth kid', 'double loss for sale', 'witch stole your rupees', 'affluent boy steals rupees', 'a rupoor'), - 'Red Clock': (False, True, None, 0x5B, 0, 'a waste of time', 'the ruby clock', 'the ruby-time kid', 'red time for sale', 'for ruby time', 'moment boy travels time again', 'a red clock'), - 'Blue Clock': (False, True, None, 0x5C, 50, 'a bit of time', 'the sapphire clock', 'sapphire-time kid', 'blue time for sale', 'for sapphire time', 'moment boy time travels again', 'a blue clock'), - 'Green Clock': (False, True, None, 0x5D, 200, 'a lot of time', 'the emerald clock', 'the emerald-time kid', 'green time for sale', 'for emerald time', 'moment boy adjusts time again', 'a red clock'), - 'Single RNG': (False, True, None, 0x62, 300, 'something you don\'t yet have', None, None, None, None, 'unknown boy somethings again', 'a new mystery'), - 'Multi RNG': (False, True, None, 0x63, 100, 'something you may already have', None, None, None, None, 'unknown boy somethings again', 'a total mystery'), + 'Rupoor': (False, False, None, 0x59, 0, 'A debt collector', 'and the toll-booth', 'the toll-booth kid', 'double loss for sale', 'witch stole your rupees', 'affluent boy steals rupees', 'a rupoor'), + 'Red Clock': (False, True, None, 0x5B, 0, 'A waste of time', 'the ruby clock', 'the ruby-time kid', 'red time for sale', 'for ruby time', 'moment boy travels time again', 'a red clock'), + 'Blue Clock': (False, True, None, 0x5C, 50, 'A bit of time', 'the sapphire clock', 'sapphire-time kid', 'blue time for sale', 'for sapphire time', 'moment boy time travels again', 'a blue clock'), + 'Green Clock': (False, True, None, 0x5D, 200, 'A lot of time', 'the emerald clock', 'the emerald-time kid', 'green time for sale', 'for emerald time', 'moment boy adjusts time again', 'a red clock'), + 'Single RNG': (False, True, None, 0x62, 300, 'Something you don\'t yet have', None, None, None, None, 'unknown boy somethings again', 'a new mystery'), + 'Multi RNG': (False, True, None, 0x63, 100, 'Something you may already have', None, None, None, None, 'unknown boy somethings again', 'a total mystery'), 'Magic Upgrade (1/2)': (True, False, None, 0x4E, 50, 'Your magic\npower has been\ndoubled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'Half Magic'), # can be required to beat mothula in an open seed in very very rare circumstance 'Magic Upgrade (1/4)': (True, False, None, 0x4F, 100, 'Your magic\npower has been\nquadrupled!', 'and the spell power', 'the magic-saving kid', 'wizardry for sale', 'mekalekahi mekahiney ho', 'magic boy saves magic again', 'Quarter Magic'), # can be required to beat mothula in an open seed in very very rare circumstance 'Small Key (Eastern Palace)': (False, False, 'SmallKey', 0xA2, 40, 'A small key to Armos Knights', 'and the key', 'the unlocking kid', 'keys for sale', 'unlock the fungus', 'key boy opens door again', 'a Small Key to Eastern Palace'), @@ -188,6 +193,7 @@ item_table = {'Bow': (True, False, None, 0x0B, 200, 'You have\nchosen the\narche 'Maiden Rescued': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Maiden Unmasked': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Convenient Block': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), + 'Hidden Pits': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Zelda Herself': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), 'Zelda Delivered': (True, False, 'Event', 999, None, None, None, None, None, None, None, None), } diff --git a/Main.py b/Main.py index 8453c90a..ac5843e5 100644 --- a/Main.py +++ b/Main.py @@ -13,7 +13,7 @@ from Bosses import place_bosses from Items import ItemFactory from KeyDoorShuffle import validate_key_placement from OverworldGlitchRules import create_owg_connections -from PotShuffle import shuffle_pots +from PotShuffle import shuffle_pots, shuffle_pot_switches from Regions import create_regions, create_shops, mark_light_world_regions, mark_dark_world_regions, create_dungeon_regions, adjust_locations from OWEdges import create_owedges from OverworldShuffle import link_overworld, update_world_regions, create_flute_exits @@ -24,15 +24,15 @@ 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 +from Fill import distribute_items_restrictive, promote_dungeon_items, fill_dungeons_restrictive, ensure_good_pots from Fill import sell_potions, sell_keys, balance_multiworld_progression, balance_money_progression, lock_shop_locations, set_prize_drops from ItemList import generate_itempool, difficulties, fill_prizes, customize_shops from Utils import output_path, parse_player_names from source.item.FillUtil import create_item_pool_config, massage_item_pool, district_item_pool_config +from source.tools.BPS import create_bps_from_data - -__version__ = '1.0.0.3-u' +__version__ = '1.0.1.0-u' from source.classes.BabelFish import BabelFish @@ -100,18 +100,21 @@ def main(args, seed=None, fish=None): world.intensity = {player: random.randint(1, 3) if args.intensity[player] == 'random' else int(args.intensity[player]) for player in range(1, world.players + 1)} world.experimental = args.experimental.copy() world.dungeon_counters = args.dungeon_counters.copy() - world.potshuffle = args.shufflepots.copy() world.fish = fish world.shopsanity = args.shopsanity.copy() - world.keydropshuffle = args.keydropshuffle.copy() + world.dropshuffle = args.dropshuffle.copy() + world.pottery = args.pottery.copy() + world.potshuffle = args.shufflepots.copy() world.mixed_travel = args.mixed_travel.copy() world.standardize_palettes = args.standardize_palettes.copy() - world.treasure_hunt_count = args.triforce_goal.copy() - world.treasure_hunt_total = args.triforce_pool.copy() + world.treasure_hunt_count = {k: int(v) for k, v in args.triforce_goal.items()} + world.treasure_hunt_total = {k: int(v) for k, v in args.triforce_pool.items()} world.shufflelinks = args.shufflelinks.copy() world.pseudoboots = args.pseudoboots.copy() world.overworld_map = args.overworld_map.copy() world.restrict_boss_items = args.restrict_boss_items.copy() + world.collection_rate = args.collection_rate.copy() + world.colorizepots = args.colorizepots.copy() world.rom_seeds = {player: random.randint(0, 999999999) for player in range(1, world.players + 1)} @@ -147,14 +150,17 @@ def main(args, seed=None, fish=None): if hasattr(world,"escape_assist") and player in world.escape_assist: world.escape_assist[player].append('bombs') # enemized escape assumes infinite bombs available and will likely be unbeatable without it - for tok in filter(None, args.startinventory[player].split(',')): - item = ItemFactory(tok.strip(), player) - if item: - world.push_precollected(item) - + if args.usestartinventory[player]: + for tok in filter(None, args.startinventory[player].split(',')): + item = ItemFactory(tok.strip(), player) + if item: + world.push_precollected(item) + if args.create_spoiler and not args.jsonout: - logger.info(world.fish.translate("cli","cli","patching.spoiler")) - world.spoiler.meta_to_file(output_path('%s_Spoiler.txt' % outfilebase)) + logger.info(world.fish.translate("cli", "cli", "create.meta")) + world.spoiler.meta_to_file(output_path(f'{outfilebase}_Spoiler.txt')) + if args.mystery and not args.suppress_meta: + world.spoiler.mystery_meta_to_file(output_path(f'{outfilebase}_meta.txt')) for player in range(1, world.players + 1): create_regions(world, player) @@ -173,7 +179,10 @@ def main(args, seed=None, fish=None): logger.info(world.fish.translate("cli", "cli", "shuffling.pots")) for player in range(1, world.players + 1): if world.potshuffle[player]: - shuffle_pots(world, player) + if world.pottery[player] in ['none', 'cave', 'keys', 'cavekeys']: + shuffle_pots(world, player) + else: + shuffle_pot_switches(world, player) logger.info(world.fish.translate("cli","cli","shuffling.overworld")) @@ -273,11 +282,12 @@ 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) rom_names = [] jsonout = {} enemized = False - if not args.suppress_rom: + if not args.suppress_rom or args.bps: logger.info(world.fish.translate("cli","cli","patching.rom")) for team in range(world.teams): for player in range(1, world.players + 1): @@ -303,7 +313,7 @@ def main(args, seed=None, fish=None): logging.warning(enemizerMsg) raise EnemizerError(enemizerMsg) - patch_rom(world, rom, player, team, enemized, bool(args.outputname)) + patch_rom(world, rom, player, team, enemized, bool(args.mystery)) if args.race: patch_race_rom(rom) @@ -314,7 +324,7 @@ def main(args, seed=None, fish=None): apply_rom_settings(rom, args.heartbeep[player], args.heartcolor[player], args.quickswap[player], args.fastmenu[player], args.disablemusic[player], args.sprite[player], args.ow_palettes[player], args.uw_palettes[player], args.reduce_flashing[player], - args.shuffle_sfx[player]) + args.shuffle_sfx[player], args.msu_resume[player]) if args.jsonout: jsonout[f'patch_t{team}_p{player}'] = rom.patches @@ -325,7 +335,14 @@ def main(args, seed=None, fish=None): if world.players > 1 or world.teams > 1: outfilepname += f"_{world.player_names[player][team].replace(' ', '_')}" if world.player_names[player][team] != 'Player %d' % player else '' outfilesuffix = f'_{Settings.make_code(world, player)}' if not args.outputname else '' - rom.write_to_file(output_path(f'{outfilebase}{outfilepname}{outfilesuffix}.sfc')) + if args.bps: + patchfile = output_path(f'{outfilebase}{outfilepname}{outfilesuffix}.bps') + patch = create_bps_from_data(LocalRom(args.rom, patch=False).buffer, rom.buffer) + with open(patchfile, 'wb') as stream: + stream.write(patch.binary_ba) + if not args.suppress_rom: + sfc_file = output_path(f'{outfilebase}{outfilepname}{outfilesuffix}.sfc') + rom.write_to_file(sfc_file) if world.players > 1: multidata = zlib.compress(json.dumps({"names": parsed_names, @@ -341,9 +358,13 @@ def main(args, seed=None, fish=None): with open(output_path('%s_multidata' % outfilebase), 'wb') as f: f.write(multidata) + if args.mystery and not args.suppress_meta: + world.spoiler.hashes_to_file(output_path(f'{outfilebase}_meta.txt')) + elif args.create_spoiler and not args.jsonout: + world.spoiler.hashes_to_file(output_path(f'{outfilebase}_Spoiler.txt')) if args.create_spoiler and not args.jsonout: - logger.info(world.fish.translate("cli","cli","patching.spoiler")) - world.spoiler.to_file(output_path('%s_Spoiler.txt' % outfilebase)) + logger.info(world.fish.translate("cli", "cli", "patching.spoiler")) + world.spoiler.to_file(output_path(f'{outfilebase}_Spoiler.txt')) if not args.skip_playthrough: logger.info(world.fish.translate("cli","cli","calc.playthrough")) @@ -356,8 +377,8 @@ def main(args, seed=None, fish=None): if args.jsonout: with open(output_path('%s_Spoiler.json' % outfilebase), 'w') as outfile: outfile.write(world.spoiler.to_json()) - else: - world.spoiler.playthru_to_file(output_path('%s_Spoiler.txt' % outfilebase)) + elif world.players > 1 or world.logic[1] != "nologic": + world.spoiler.playthrough_to_file(output_path(f'{outfilebase}_Spoiler.txt')) YES = world.fish.translate("cli","cli","yes") NO = world.fish.translate("cli","cli","no") @@ -377,7 +398,7 @@ def main(args, seed=None, fish=None): return world -def copy_world(world): +def copy_world(world, partial_copy=False): # ToDo: Not good yet ret = World(world.players, world.owShuffle, world.owCrossed, world.owMixed, world.shuffle, world.doorShuffle, world.logic, world.mode, world.swords, world.difficulty, world.difficulty_adjustments, world.timer, world.progressive, world.goal, world.algorithm, @@ -426,7 +447,9 @@ def copy_world(world): ret.intensity = world.intensity.copy() ret.experimental = world.experimental.copy() ret.shopsanity = world.shopsanity.copy() - ret.keydropshuffle = world.keydropshuffle.copy() + ret.dropshuffle = world.dropshuffle.copy() + ret.pottery = world.pottery.copy() + ret.potshuffle = world.potshuffle.copy() ret.mixed_travel = world.mixed_travel.copy() ret.standardize_palettes = world.standardize_palettes.copy() ret.owswaps = world.owswaps.copy() @@ -434,8 +457,6 @@ def copy_world(world): ret.prizes = world.prizes.copy() ret.restrict_boss_items = world.restrict_boss_items.copy() - ret.exp_cache = world.exp_cache.copy() - for player in range(1, world.players + 1): create_regions(ret, player) update_world_regions(ret, player) @@ -447,6 +468,9 @@ def copy_world(world): if world.logic[player] in ('owglitches', 'nologic'): create_owg_connections(ret, player) + # there are region references here they must be migrated to preserve integrity + # ret.exp_cache = world.exp_cache.copy() + copy_dynamic_regions_and_locations(world, ret) for player in range(1, world.players + 1): if world.mode[player] == 'standard': @@ -495,6 +519,7 @@ def copy_world(world): new_location.access_rule = lambda state: True new_location.item_rule = lambda state: True new_location.forced_item = location.forced_item + new_location.pot = location.pot # copy remaining itempool. No item in itempool should have an assigned location for item in world.itempool: @@ -519,9 +544,10 @@ def copy_world(world): ret.dungeon_layouts = world.dungeon_layouts ret.key_logic = world.key_logic ret.dungeon_portals = world.dungeon_portals - for player, portals in world.dungeon_portals.items(): - for portal in portals: - connect_portal(portal, ret, player) + if not partial_copy: + for player, portals in world.dungeon_portals.items(): + for portal in portals: + connect_portal(portal, ret, player) ret.sanc_portal = world.sanc_portal from OverworldShuffle import categorize_world_regions @@ -529,6 +555,10 @@ def copy_world(world): categorize_world_regions(ret, player) set_rules(ret, player) + if partial_copy: + # undo some of the things that unintentionally affect the original world object + world.key_logic = {} + return ret @@ -566,7 +596,7 @@ def create_playthrough(world): # get locations containing progress items prog_locations = [location for location in world.get_filled_locations() if location.item.advancement] - optional_locations = ['Trench 1 Switch', 'Trench 2 Switch', 'Ice Block Drop'] + optional_locations = ['Trench 1 Switch', 'Trench 2 Switch', 'Ice Block Drop', 'Skull Star Tile'] state_cache = [None] collection_spheres = [] state = CollectionState(world) @@ -672,9 +702,11 @@ def create_playthrough(world): old_world.spoiler.paths = dict() for player in range(1, world.players + 1): - old_world.spoiler.paths.update({location.gen_name(): get_path(state, location.parent_region) for sphere in collection_spheres for location in sphere if location.player == player}) + if world.logic[player] != 'nologic': + old_world.spoiler.paths.update({location.gen_name(): get_path(state, location.parent_region) for sphere in collection_spheres for location in sphere if location.player == player}) # we can finally output our playthrough old_world.spoiler.playthrough = {"0": [str(item) for item in world.precollected_items if item.advancement]} for i, sphere in enumerate(collection_spheres): - old_world.spoiler.playthrough[str(i + 1)] = {location.gen_name(): str(location.item) for location in sphere} + if world.logic[player] != 'nologic': + old_world.spoiler.playthrough[str(i + 1)] = {location.gen_name(): str(location.item) for location in sphere} diff --git a/MultiClient.py b/MultiClient.py index 501630e0..fbde673d 100644 --- a/MultiClient.py +++ b/MultiClient.py @@ -8,8 +8,10 @@ import shlex import urllib.parse import websockets +from BaseClasses import PotItem, PotFlags import Items import Regions +import PotShuffle class ReceivedItem: @@ -57,8 +59,13 @@ class Context: self.key_drop_mode = False self.shop_mode = False self.retro_mode = False + self.pottery_mode = False + self.mystery_mode = False self.ignore_count = 0 + self.lookup_name_to_id = {} + self.lookup_id_to_name = {} + def color_code(*args): codes = {'reset': 0, 'bold': 1, 'underline': 4, 'black': 30, 'red': 31, 'green': 32, 'yellow': 33, 'blue': 34, 'magenta': 35, 'cyan': 36, 'white': 37 , 'black_bg': 40, 'red_bg': 41, 'green_bg': 42, 'yellow_bg': 43, @@ -83,6 +90,12 @@ INGAME_MODES = {0x07, 0x09, 0x0b} SAVEDATA_START = WRAM_START + 0xF000 SAVEDATA_SIZE = 0x500 +POT_ITEMS_SRAM_START = WRAM_START + 0x016018 # 2 bytes per room +SPRITE_ITEMS_SRAM_START = WRAM_START + 0x016268 # 2 bytes per room +SHOP_SRAM_START = WRAM_START + 0x0164B8 # 2 bytes? +ITEM_SRAM_SIZE = 0x250 +SHOP_SRAM_LEN = 0x29 # 41 tracked items + RECV_PROGRESS_ADDR = SAVEDATA_START + 0x4D0 # 2 bytes RECV_ITEM_ADDR = SAVEDATA_START + 0x4D2 # 1 byte RECV_ITEM_PLAYER_ADDR = SAVEDATA_START + 0x4D3 # 1 byte @@ -92,11 +105,9 @@ SCOUT_LOCATION_ADDR = SAVEDATA_START + 0x4D7 # 1 byte SCOUTREPLY_LOCATION_ADDR = SAVEDATA_START + 0x4D8 # 1 byte SCOUTREPLY_ITEM_ADDR = SAVEDATA_START + 0x4D9 # 1 byte SCOUTREPLY_PLAYER_ADDR = SAVEDATA_START + 0x4DA # 1 byte -SHOP_ADDR = SAVEDATA_START + 0x302 # 2 bytes? DYNAMIC_TOTAL_ADDR = SAVEDATA_START + 0x33E # 2 bytes MODE_FLAGS = SAVEDATA_START + 0x33D # 1 byte -SHOP_SRAM_LEN = 0x29 # 41 tracked items location_shop_order = [Regions.shop_to_location_table.keys()] + [Regions.retro_shops.keys()] location_shop_ids = {0x0112, 0x0110, 0x010F, 0x00FF, 0x011F, 0x0109, 0x0115} @@ -349,6 +360,8 @@ location_table_misc = {'Bottle Merchant': (0x3c9, 0x2), 'Purple Chest': (0x3c9, 0x10), "Link's Uncle": (0x3c6, 0x1), 'Hobo': (0x3c9, 0x1)} +location_table_pot_items = {} +location_table_sprite_items = {} SNES_DISCONNECTED = 0 SNES_CONNECTING = 1 @@ -676,7 +689,7 @@ async def process_server_cmd(ctx : Context, cmd, args): ctx.player_names = {p: n for p, n in args[1]} msgs = [] if ctx.locations_checked: - msgs.append(['LocationChecks', [Regions.lookup_name_to_id[loc] for loc in ctx.locations_checked]]) + msgs.append(['LocationChecks', [ctx.lookup_name_to_id[loc] for loc in ctx.locations_checked]]) if ctx.locations_scouted: msgs.append(['LocationScouts', list(ctx.locations_scouted)]) if msgs: @@ -689,7 +702,7 @@ async def process_server_cmd(ctx : Context, cmd, args): elif start_index != len(ctx.items_received): sync_msg = [['Sync']] if ctx.locations_checked: - sync_msg.append(['LocationChecks', [Regions.lookup_name_to_id[loc] for loc in ctx.locations_checked]]) + sync_msg.append(['LocationChecks', [ctx.lookup_name_to_id[loc] for loc in ctx.locations_checked]]) await send_msgs(ctx.socket, sync_msg) if start_index == len(ctx.items_received): for item in items: @@ -701,7 +714,7 @@ async def process_server_cmd(ctx : Context, cmd, args): if location not in ctx.locations_info: replacements = {0xA2: 'Small Key', 0x9D: 'Big Key', 0x8D: 'Compass', 0x7D: 'Map'} item_name = replacements.get(item, get_item_name_from_id(item)) - logging.info(f"Saw {color(item_name, 'red', 'bold')} at {list(Regions.lookup_id_to_name.keys())[location - 1]}") + logging.info(f"Saw {color(item_name, 'red', 'bold')} at {list(ctx.lookup_id_to_name.keys())[location - 1]}") ctx.locations_info[location] = (item, player) ctx.watcher_event.set() @@ -710,7 +723,8 @@ async def process_server_cmd(ctx : Context, cmd, args): item = color(get_item_name_from_id(item), 'cyan' if player_sent != ctx.slot else 'green') player_sent = color(ctx.player_names[player_sent], 'yellow' if player_sent != ctx.slot else 'magenta') player_recvd = color(ctx.player_names[player_recvd], 'yellow' if player_recvd != ctx.slot else 'magenta') - logging.info('%s sent %s to %s (%s)' % (player_sent, item, player_recvd, get_location_name_from_address(location))) + location_name = get_location_name_from_address(ctx, location) + logging.info('%s sent %s to %s (%s)' % (player_sent, item, player_recvd, location_name)) if cmd == 'Print': logging.info(args) @@ -779,10 +793,10 @@ async def console_loop(ctx : Context): for index, item in enumerate(ctx.items_received, 1): logging.info('%s from %s (%s) (%d/%d in list)' % ( color(get_item_name_from_id(item.item), 'red', 'bold'), color(ctx.player_names[item.player], 'yellow'), - get_location_name_from_address(item.location), index, len(ctx.items_received))) + get_location_name_from_address(ctx, item.location), index, len(ctx.items_received))) if command[0] == '/missing': - for location in [k for k, v in Regions.lookup_name_to_id.items() + for location in [k for k, v in ctx.lookup_name_to_id.items() if type(v) is int and not filter_location(ctx, k)]: if location not in ctx.locations_checked: logging.info('Missing: ' + location) @@ -804,15 +818,19 @@ def get_item_name_from_id(code): return items[0] if items else f'Unknown item (ID:{code})' -def get_location_name_from_address(address): +def get_location_name_from_address(ctx, address): if type(address) is str: return address - return Regions.lookup_id_to_name.get(address, f'Unknown location (ID:{address})') + return ctx.lookup_id_to_name.get(address, f'Unknown location (ID:{address})') def filter_location(ctx, location): - if not ctx.key_drop_mode and ('Key Drop' in location or 'Pot Key' in location): + if (not ctx.key_drop_mode and location in PotShuffle.key_drop_data + and PotShuffle.key_drop_data[location][0] == 'Drop'): + return True + if (not ctx.pottery_mode and location in PotShuffle.key_drop_data + and PotShuffle.key_drop_data[location][0] == 'Pot'): return True if not ctx.shop_mode and location in Regions.flat_normal_shops: return True @@ -821,6 +839,31 @@ def filter_location(ctx, location): return False +def init_lookups(ctx): + ctx.lookup_id_to_name = {x: y for x, y in Regions.lookup_id_to_name.items()} + ctx.lookup_name_to_id = {x: y for x, y in Regions.lookup_name_to_id.items()} + for location, datum in PotShuffle.key_drop_data.items(): + type = datum[0] + if type == 'Drop': + location_id, super_tile, sprite_index = datum[1] + location_table_sprite_items[location] = (2 * super_tile, 0x8000 >> sprite_index) + ctx.lookup_name_to_id[location] = location_id + ctx.lookup_id_to_name[location_id] = location + for super_tile, pot_list in PotShuffle.vanilla_pots.items(): + for pot_index, pot in enumerate(pot_list): + if pot.item != PotItem.Hole: + if pot.item == PotItem.Key: + loc_name = next(loc for loc, datum in PotShuffle.key_drop_data.items() + if datum[1] == super_tile) + else: + descriptor = 'Large Block' if pot.flags & PotFlags.Block else f'Pot #{pot_index+1}' + loc_name = f'{pot.room} {descriptor}' + location_table_pot_items[loc_name] = (2 * super_tile, 0x8000 >> pot_index) + location_id = Regions.pot_address(pot_index, super_tile) + ctx.lookup_name_to_id[loc_name] = location_id + ctx.lookup_id_to_name[location_id] = loc_name + + async def track_locations(ctx : Context, roomid, roomdata): new_locations = [] @@ -832,9 +875,12 @@ async def track_locations(ctx : Context, roomid, roomdata): if ctx.mode_flags is None: flags = await snes_read(ctx, MODE_FLAGS, 1) + ctx.mode_flags = flags ctx.key_drop_mode = flags[0] & 0x1 ctx.shop_mode = flags[0] & 0x2 ctx.retro_mode = flags[0] & 0x4 + ctx.pottery_mode = flags[0] & 0x8 + ctx.mystery_mode = flags[0] & 0x10 def new_check(location): ctx.locations_checked.add(location) @@ -842,12 +888,13 @@ async def track_locations(ctx : Context, roomid, roomdata): if ignored: ctx.ignore_count += 1 else: - logging.info(f"New check: {location} ({len(ctx.locations_checked)-ctx.ignore_count}/{ctx.total_locations})") - new_locations.append(Regions.lookup_name_to_id[location]) + total = '???' if ctx.mystery_mode else ctx.total_locations + logging.info(f"New check: {location} ({len(ctx.locations_checked)-ctx.ignore_count}/{total})") + new_locations.append(ctx.lookup_name_to_id[location]) try: if ctx.shop_mode or ctx.retro_mode: - misc_data = await snes_read(ctx, SHOP_ADDR, SHOP_SRAM_LEN) + misc_data = await snes_read(ctx, SHOP_SRAM_START, SHOP_SRAM_LEN) for cnt, b in enumerate(misc_data): my_check = Regions.shop_table_by_location_id[0x400000 + cnt] if int(b) > 0 and my_check not in ctx.locations_checked: @@ -908,6 +955,22 @@ async def track_locations(ctx : Context, roomid, roomdata): if misc_data[offset - 0x3c6] & mask != 0 and location not in ctx.locations_checked: new_check(location) + if not all([location in ctx.locations_checked for location in location_table_pot_items.keys()]): + pot_items_data = await snes_read(ctx, POT_ITEMS_SRAM_START, ITEM_SRAM_SIZE) + if pot_items_data is not None: + for location, (offset, mask) in location_table_pot_items.items(): + pot_value = pot_items_data[offset] | (pot_items_data[offset + 1] << 8) + if pot_value & mask != 0 and location not in ctx.locations_checked: + new_check(location) + + if not all([location in ctx.locations_checked for location in location_table_sprite_items.keys()]): + sprite_items_data = await snes_read(ctx, SPRITE_ITEMS_SRAM_START, ITEM_SRAM_SIZE) + if sprite_items_data is not None: + for location, (offset, mask) in location_table_sprite_items.items(): + sprite_value = sprite_items_data[offset] | (sprite_items_data[offset + 1] << 8) + if sprite_value & mask != 0 and location not in ctx.locations_checked: + new_check(location) + await send_msgs(ctx.socket, [['LocationChecks', new_locations]]) async def game_watcher(ctx : Context): @@ -955,7 +1018,7 @@ async def game_watcher(ctx : Context): item = ctx.items_received[recv_index] logging.info('Received %s from %s (%s) (%d/%d in list)' % ( color(get_item_name_from_id(item.item), 'red', 'bold'), color(ctx.player_names[item.player], 'yellow'), - get_location_name_from_address(item.location), recv_index + 1, len(ctx.items_received))) + get_location_name_from_address(ctx, item.location), recv_index + 1, len(ctx.items_received))) recv_index += 1 snes_buffered_write(ctx, RECV_PROGRESS_ADDR, bytes([recv_index & 0xFF, (recv_index >> 8) & 0xFF])) snes_buffered_write(ctx, RECV_ITEM_ADDR, bytes([item.item])) @@ -969,7 +1032,7 @@ async def game_watcher(ctx : Context): if scout_location > 0 and scout_location not in ctx.locations_scouted: ctx.locations_scouted.add(scout_location) - logging.info(f'Scouting item at {list(Regions.lookup_id_to_name.keys())[scout_location - 1]}') + logging.info(f'Scouting item at {list(ctx.lookup_id_to_name.keys())[scout_location - 1]}') await send_msgs(ctx.socket, [['LocationScouts', [scout_location]]]) await track_locations(ctx, roomid, roomdata) @@ -984,6 +1047,7 @@ async def main(): logging.basicConfig(format='%(message)s', level=getattr(logging, args.loglevel.upper(), logging.INFO)) ctx = Context(args.snes, args.connect, args.password) + init_lookups(ctx) input_task = asyncio.create_task(console_loop(ctx)) diff --git a/MultiServer.py b/MultiServer.py index cd333edf..4108b8dd 100644 --- a/MultiServer.py +++ b/MultiServer.py @@ -6,12 +6,15 @@ import json import logging import re import shlex +import ssl import urllib.request import websockets import zlib +from BaseClasses import PotItem, PotFlags import Items import Regions +import PotShuffle from MultiClient import ReceivedItem, get_item_name_from_id, get_location_name_from_address class Client: @@ -40,6 +43,9 @@ class Context: self.clients = [] self.received_items = {} + self.lookup_name_to_id = {} + self.lookup_id_to_name = {} + async def send_msgs(websocket, msgs): if not websocket or not websocket.open or websocket.closed: return @@ -154,8 +160,7 @@ def send_new_items(ctx : Context): client.send_index = len(items) def forfeit_player(ctx : Context, team, slot): - all_locations = {values[0] for values in Regions.location_table.values() if type(values[0]) is int} - all_locations.update({values[1] for values in Regions.key_drop_data.values()}) + all_locations = set(ctx.lookup_id_to_name.keys()) notify_all(ctx, "%s (Team #%d) has forfeited" % (ctx.player_names[(team, slot)], team + 1)) register_location_checks(ctx, team, slot, all_locations) @@ -176,7 +181,8 @@ def register_location_checks(ctx : Context, team, slot, locations): recvd_items.append(new_item) if slot != target_player: broadcast_team(ctx, team, [['ItemSent', (slot, location, target_player, target_item)]]) - logging.info('(Team #%d) %s sent %s to %s (%s)' % (team+1, ctx.player_names[(team, slot)], get_item_name_from_id(target_item), ctx.player_names[(team, target_player)], get_location_name_from_address(location))) + loc_name = get_location_name_from_address(ctx, location) + logging.info('(Team #%d) %s sent %s to %s (%s)' % (team+1, ctx.player_names[(team, slot)], get_item_name_from_id(target_item), ctx.player_names[(team, target_player)], loc_name)) found_items = True send_new_items(ctx) @@ -249,11 +255,11 @@ async def process_client_cmd(ctx : Context, client : Client, cmd, args): return locs = [] for location in args: - if type(location) is not int or 0 >= location > len(Regions.lookup_id_to_name.keys()): + if type(location) is not int or 0 >= location > len(ctx.lookup_id_to_name.keys()): await send_msgs(client.socket, [['InvalidArguments', 'LocationScouts']]) return - loc_name = list(Regions.lookup_id_to_name.keys())[location - 1] - target_item, target_player = ctx.locations[(Regions.lookup_name_to_id[loc_name], client.slot)] + loc_name = list(ctx.lookup_id_to_name.keys())[location - 1] + target_item, target_player = ctx.locations[(ctx.lookup_name_to_id[loc_name], client.slot)] replacements = {'SmallKey': 0xA2, 'BigKey': 0x9D, 'Compass': 0x8D, 'Map': 0x7D} item_type = [i[2] for i in Items.item_table.values() if type(i[3]) is int and i[3] == target_item] @@ -339,6 +345,30 @@ async def console(ctx : Context): if command[0][0] != '/': notify_all(ctx, '[Server]: ' + input) + +def init_lookups(ctx): + ctx.lookup_id_to_name = {x: y for x, y in Regions.lookup_id_to_name.items()} + ctx.lookup_name_to_id = {x: y for x, y in Regions.lookup_name_to_id.items()} + for location, datum in PotShuffle.key_drop_data.items(): + type = datum[0] + if type == 'Drop': + location_id = datum[1][0] + ctx.lookup_name_to_id[location] = location_id + ctx.lookup_id_to_name[location_id] = location + for super_tile, pot_list in PotShuffle.vanilla_pots.items(): + for pot_index, pot in enumerate(pot_list): + if pot.item != PotItem.Hole: + if pot.item == PotItem.Key: + loc_name = next(loc for loc, datum in PotShuffle.key_drop_data.items() + if datum[1] == super_tile) + else: + descriptor = 'Large Block' if pot.flags & PotFlags.Block else f'Pot #{pot_index+1}' + loc_name = f'{pot.room} {descriptor}' + location_id = Regions.pot_address(pot_index, super_tile) + ctx.lookup_name_to_id[loc_name] = location_id + ctx.lookup_id_to_name[location_id] = loc_name + + async def main(): parser = argparse.ArgumentParser() parser.add_argument('--host', default=None) @@ -353,7 +383,7 @@ async def main(): logging.basicConfig(format='[%(asctime)s] %(message)s', level=getattr(logging, args.loglevel.upper(), logging.INFO)) ctx = Context(args.host, args.port, args.password) - + init_lookups(ctx) ctx.data_filename = args.multidata try: @@ -376,7 +406,7 @@ async def main(): logging.error('Failed to read multiworld data (%s)' % e) return - ip = urllib.request.urlopen('https://v4.ident.me').read().decode('utf8') if not ctx.host else ctx.host + ip = urllib.request.urlopen('https://v4.ident.me', context=ssl._create_unverified_context()).read().decode('utf8') if not ctx.host else ctx.host logging.info('Hosting game at %s:%d (%s)' % (ip, ctx.port, 'No password' if not ctx.password else 'Password: %s' % ctx.password)) ctx.disable_save = args.disable_save diff --git a/Mystery.py b/Mystery.py index 0e2a556d..07955bff 100644 --- a/Mystery.py +++ b/Mystery.py @@ -30,6 +30,7 @@ def main(): parser.add_argument('--create_spoiler', action='store_true') parser.add_argument('--no_race', action='store_true') parser.add_argument('--suppress_rom', action='store_true') + parser.add_argument('--suppress_meta', action='store_true') parser.add_argument('--bps', action='store_true') parser.add_argument('--rom') parser.add_argument('--enemizercli') @@ -65,12 +66,14 @@ def main(): erargs.names = args.names erargs.create_spoiler = args.create_spoiler erargs.suppress_rom = args.suppress_rom + erargs.suppress_meta = args.suppress_meta erargs.bps = args.bps erargs.race = not args.no_race erargs.outputname = seedname if args.outputpath: erargs.outputpath = args.outputpath erargs.loglevel = args.loglevel + erargs.mystery = True if args.rom: erargs.rom = args.rom @@ -142,8 +145,8 @@ def roll_settings(weights): ret.algorithm = get_choice('algorithm') - glitch_map = {'none': 'noglitches', 'no_logic': 'nologic', 'owg': 'owglitches', - 'minorglitches': 'minorglitches'} + glitch_map = {'none': 'noglitches', 'no_logic': 'nologic', 'owglitches': 'owglitches', + 'owg': 'owglitches', 'minorglitches': 'minorglitches'} glitches_required = get_choice('glitches_required') if glitches_required is not None: if glitches_required not in glitch_map.keys(): @@ -179,6 +182,7 @@ def roll_settings(weights): ret.door_shuffle = door_shuffle if door_shuffle != 'none' else 'vanilla' ret.intensity = get_choice('intensity') ret.experimental = get_choice('experimental') == 'on' + ret.collection_rate = get_choice('collection_rate') == 'on' ret.dungeon_counters = get_choice('dungeon_counters') if 'dungeon_counters' in weights else 'default' if ret.dungeon_counters == 'default': @@ -186,7 +190,10 @@ def roll_settings(weights): ret.pseudoboots = get_choice('pseudoboots') == 'on' ret.shopsanity = get_choice('shopsanity') == 'on' - ret.keydropshuffle = get_choice('keydropshuffle') == 'on' + ret.dropshuffle = get_choice('dropshuffle') == 'on' + ret.pottery = get_choice('pottery') if 'pottery' in weights else 'none' + ret.colorizepots = get_choice('colorizepots') == 'on' + ret.shufflepots = get_choice('pot_shuffle') == 'on' ret.mixed_travel = get_choice('mixed_travel') if 'mixed_travel' in weights else 'prevent' ret.standardize_palettes = get_choice('standardize_palettes') if 'standardize_palettes' in weights else 'standardize' @@ -262,8 +269,6 @@ def roll_settings(weights): ret.enemy_health = get_choice('enemy_health') - ret.shufflepots = get_choice('pot_shuffle') == 'on' - ret.beemizer = get_choice('beemizer') if 'beemizer' in weights else '0' inventoryweights = weights.get('startinventory', {}) @@ -272,6 +277,8 @@ def roll_settings(weights): if get_choice(item, inventoryweights) == 'on': startitems.append(item) ret.startinventory = ','.join(startitems) + if len(startitems) > 0: + ret.usestartinventory = True if 'rom' in weights: romweights = weights['rom'] @@ -279,6 +286,7 @@ def roll_settings(weights): ret.disablemusic = get_choice('disablemusic', romweights) == 'on' ret.quickswap = get_choice('quickswap', romweights) == 'on' ret.reduce_flashing = get_choice('reduce_flashing', romweights) == 'on' + ret.msu_resume = get_choice('msu_resume', romweights) == 'on' ret.fastmenu = get_choice('menuspeed', romweights) ret.heartcolor = get_choice('heartcolor', romweights) ret.heartbeep = get_choice('heartbeep', romweights) diff --git a/OWEdges.py b/OWEdges.py index 69c26b90..8c980601 100644 --- a/OWEdges.py +++ b/OWEdges.py @@ -727,7 +727,7 @@ OWTileRegions = bidict({ 'Zora Waterfall Area': 0x0f, 'Zora Waterfall Water': 0x0f, - 'Waterfall of Wishing Cave': 0x0f, + 'Zora Waterfall Entryway': 0x0f, 'Lost Woods Pass West Area': 0x10, 'Lost Woods Pass East Top Area': 0x10, @@ -1253,7 +1253,7 @@ OWExitTypes = { 'Mountain Entry Entrance Rock (West)', 'Mountain Entry Entrance Rock (East)', 'Zora Waterfall Water Entry', - 'Waterfall of Wishing Cave Entry', + 'Zora Waterfall Water Approach', 'Zora Waterfall Landing', 'Lost Woods Pass Hammer (North)', 'Lost Woods Pass Hammer (South)', diff --git a/OverworldShuffle.py b/OverworldShuffle.py index 3087ebdf..1d806f6c 100644 --- a/OverworldShuffle.py +++ b/OverworldShuffle.py @@ -6,7 +6,7 @@ from Regions import mark_dark_world_regions, mark_light_world_regions from OWEdges import OWTileRegions, OWEdgeGroups, OWExitTypes, OpenStd, parallel_links, IsParallel from Utils import bidict -version_number = '0.2.7.3' +version_number = '0.2.8.0' version_branch = '' __version__ = '%s%s' % (version_number, version_branch) @@ -882,8 +882,7 @@ def build_sectors(world, player): # perform accessibility check on duplicate world for p in range(1, world.players + 1): world.key_logic[p] = {} - base_world = copy_world(world) - world.key_logic = {} + base_world = copy_world(world, True) # build lists of contiguous regions accessible with full inventory (excl portals/mirror/flute/entrances) regions = list(OWTileRegions.copy().keys()) @@ -958,9 +957,8 @@ def build_accessible_region_list(world, start_region, player, build_copy_world=F if build_copy_world: for p in range(1, world.players + 1): world.key_logic[p] = {} - base_world = copy_world(world) + base_world = copy_world(world, True) base_world.override_bomb_check = True - world.key_logic = {} else: base_world = world @@ -974,7 +972,7 @@ def build_accessible_region_list(world, start_region, player, build_copy_world=F return explored_regions def validate_layout(world, player): - if world.accessibility[player] == 'beatable': + if world.accessibility[player] == 'none': return True entrance_connectors = { @@ -1048,8 +1046,7 @@ def validate_layout(world, player): for p in range(1, world.players + 1): world.key_logic[p] = {} - base_world = copy_world(world) - world.key_logic = {} + base_world = copy_world(world, True) explored_regions = list() if world.shuffle[player] in ['vanilla', 'dungeonssimple', 'dungeonsfull'] or not world.shufflelinks[player]: @@ -1152,7 +1149,7 @@ mandatory_connections = [# Intra-tile OW Connections ('Zora Waterfall Landing', 'Zora Waterfall Area'), ('Zora Waterfall Water Drop', 'Zora Waterfall Water'), #flippers ('Zora Waterfall Water Entry', 'Zora Waterfall Water'), #flippers - ('Waterfall of Wishing Cave Entry', 'Waterfall of Wishing Cave'), #flippers + ('Zora Waterfall Water Approach', 'Zora Waterfall Entryway'), #flippers ('Lost Woods Pass Hammer (North)', 'Lost Woods Pass Portal Area'), #hammer ('Lost Woods Pass Hammer (South)', 'Lost Woods Pass East Top Area'), #hammer ('Lost Woods Pass Rock (North)', 'Lost Woods Pass East Bottom Area'), #mitts diff --git a/PotShuffle.py b/PotShuffle.py index a0c048bf..a1c96783 100644 --- a/PotShuffle.py +++ b/PotShuffle.py @@ -1,11 +1,15 @@ +import RaceRandom as random + from collections import defaultdict -from BaseClasses import PotItem, Pot, PotFlags, CrystalBarrier -from Regions import key_drop_data +from BaseClasses import PotItem, Pot, PotFlags, CrystalBarrier, LocationType, RegionType +from Utils import int16_as_bytes, pc_to_snes, snes_to_pc + +from source.dungeon.RoomObject import RoomObject, Shuffled_Pot movable_switch_rooms = defaultdict(lambda: [], {'PoD Stalfos Basement': ['PoD Basement Ledge'], - 'Thieves Attic': ['Thieves Attic Hint'], + 'Thieves Attic Switch': ['Thieves Attic Hint'], 'Mire Hub Switch': ['Mire Hub', 'Mire Hub Right']}) invalid_key_rooms = { @@ -18,269 +22,848 @@ invalid_key_rooms = { } vanilla_pots = { - 2: [Pot(80, 6, PotItem.Nothing, 'Sewers Yet More Rats'), Pot(80, 8, PotItem.Nothing, 'Sewers Yet More Rats'), Pot(44, 8, PotItem.Nothing, 'Sewers Yet More Rats'), Pot(44, 10, PotItem.Nothing, 'Sewers Yet More Rats')], - 4: [Pot(162, 25, PotItem.Nothing, 'TR Dash Room'), Pot(152, 25, PotItem.Nothing, 'TR Dash Room'), Pot(152, 22, PotItem.Nothing, 'TR Dash Room'), Pot(162, 22, PotItem.Nothing, 'TR Dash Room'), Pot(204, 19, PotItem.Bomb, 'TR Tongue Pull'), - Pot(240, 19, PotItem.Bomb, 'TR Tongue Pull')], - 9: [Pot(12, 4, PotItem.OneRupee, 'PoD Shooter Room'), Pot(48, 4, PotItem.Heart, 'PoD Shooter Room'), Pot(12, 12, PotItem.Switch, 'PoD Shooter Room')], - 10: [Pot(96, 8, PotItem.Heart, 'PoD Stalfos Basement'), Pot(104, 8, PotItem.Heart, 'PoD Stalfos Basement'), Pot(204, 11, PotItem.Switch, 'PoD Stalfos Basement'), Pot(100, 9, PotItem.Nothing, 'PoD Stalfos Basement'), - Pot(156, 17, PotItem.Bomb, 'PoD Basement Ledge', PotFlags.SwitchLogicChange), Pot(160, 17, PotItem.FiveArrows, 'PoD Basement Ledge', PotFlags.SwitchLogicChange)], - 11: [Pot(202, 3, PotItem.Bomb, 'PoD Dark Pegs Left'), Pot(202, 12, PotItem.Bomb, 'PoD Dark Pegs Left')], - 17: [Pot(152, 19, PotItem.Nothing, 'Sewers Secret Room'), Pot(152, 15, PotItem.Nothing, 'Sewers Secret Room'), Pot(144, 15, PotItem.Heart, 'Sewers Secret Room'), Pot(160, 15, PotItem.Heart, 'Sewers Secret Room'), - Pot(144, 19, PotItem.Heart, 'Sewers Secret Room'), Pot(160, 19, PotItem.Heart, 'Sewers Secret Room')], - 21: [Pot(96, 4, PotItem.Bomb, 'TR Pipe Pit'), Pot(100, 4, PotItem.SmallMagic, 'TR Pipe Pit'), Pot(104, 4, PotItem.Heart, 'TR Pipe Pit'), Pot(108, 4, PotItem.SmallMagic, 'TR Pipe Pit'), Pot(112, 4, PotItem.FiveArrows, 'TR Pipe Pit'), - Pot(12, 6, PotItem.OneRupee, 'TR Pipe Pit'), Pot(16, 6, PotItem.FiveArrows, 'TR Pipe Pit'), Pot(20, 6, PotItem.FiveRupees, 'TR Pipe Pit'), Pot(70, 11, PotItem.BigMagic, 'TR Pipe Ledge')], - 22: [Pot(188, 3, PotItem.Heart, 'Swamp I'), Pot(192, 3, PotItem.Heart, 'Swamp I'), Pot(188, 4, PotItem.SmallMagic, 'Swamp I'), Pot(192, 4, PotItem.SmallMagic, 'Swamp I'), Pot(188, 5, PotItem.FiveArrows, 'Swamp I'), - Pot(192, 5, PotItem.FiveArrows, 'Swamp I'), Pot(188, 6, PotItem.Bomb, 'Swamp I'), Pot(192, 6, PotItem.Bomb, 'Swamp I'), Pot(240, 19, PotItem.Key, 'Swamp Waterway')], - 23: [Pot(100, 13, PotItem.Heart, 'Hera 5F'), Pot(100, 14, PotItem.Heart, 'Hera 5F'), Pot(100, 15, PotItem.Heart, 'Hera 5F'), Pot(100, 16, PotItem.Heart, 'Hera 5F'), Pot(100, 17, PotItem.Heart, 'Hera 5F'), Pot(100, 18, PotItem.Heart, 'Hera 5F'), - Pot(104, 13, PotItem.Heart, 'Hera 5F'), Pot(104, 14, PotItem.Heart, 'Hera 5F'), Pot(104, 15, PotItem.Heart, 'Hera 5F'), Pot(104, 16, PotItem.Heart, 'Hera 5F'), Pot(104, 17, PotItem.Heart, 'Hera 5F'), Pot(104, 18, PotItem.Heart, 'Hera 5F')], - 26: [Pot(28, 5, PotItem.Bomb, 'PoD Falling Bridge Ledge'), Pot(32, 5, PotItem.Bomb, 'PoD Falling Bridge Ledge'), Pot(28, 27, PotItem.Bomb, 'PoD Falling Bridge'), Pot(32, 27, PotItem.Bomb, 'PoD Falling Bridge'), - Pot(232, 19, PotItem.Nothing, 'PoD Harmless Hellway'), Pot(212, 19, PotItem.Nothing, 'PoD Harmless Hellway')], - 27: [Pot(20, 23, PotItem.FiveArrows, 'PoD Mimics 2'), Pot(40, 23, PotItem.FiveArrows, 'PoD Mimics 2')], - 30: [Pot(84, 9, PotItem.Bomb, 'Ice Bomb Drop')], - 31: [Pot(28, 25, PotItem.Switch, 'Ice Pengator Switch'), Pot(28, 23, PotItem.Nothing, 'Ice Pengator Switch'), Pot(86, 26, PotItem.Nothing, 'Ice Big Key'), Pot(86, 27, PotItem.Nothing, 'Ice Big Key')], - 33: [Pot(160, 20, PotItem.Nothing, 'Sewers Key Rat'), Pot(168, 24, PotItem.SmallMagic, 'Sewers Key Rat'), Pot(48, 28, PotItem.Heart, 'Sewers Key Rat'), Pot(82, 28, PotItem.SmallMagic, 'Sewers Key Rat'), - Pot(100, 28, PotItem.Nothing, 'Sewers Key Rat'), Pot(104, 28, PotItem.Nothing, 'Sewers Key Rat')], - 35: [Pot(86, 26, PotItem.OneRupee, 'TR Lazy Eyes'), Pot(90, 26, PotItem.Heart, 'TR Lazy Eyes'), Pot(94, 26, PotItem.OneRupee, 'TR Lazy Eyes'), Pot(98, 26, PotItem.Bomb, 'TR Lazy Eyes'), Pot(102, 26, PotItem.OneRupee, 'TR Lazy Eyes')], - 36: [Pot(12, 4, PotItem.FiveRupees, 'TR Twin Pokeys'), Pot(48, 4, PotItem.Heart, 'TR Twin Pokeys'), Pot(12, 12, PotItem.SmallMagic, 'TR Twin Pokeys'), Pot(48, 12, PotItem.OneRupee, 'TR Twin Pokeys')], - 38: [Pot(28, 4, PotItem.Bomb, 'Swamp Shooters'), Pot(12, 8, PotItem.SmallMagic, 'Swamp Shooters'), Pot(150, 19, PotItem.Switch, 'Swamp Push Statue'), Pot(22, 26, PotItem.FiveRupees, 'Swamp Push Statue'), - Pot(220, 26, PotItem.FiveArrows, 'Swamp Push Statue', PotFlags.SwitchLogicChange)], - 39: [Pot(214, 19, PotItem.Nothing, 'Hera 4F'), Pot(214, 20, PotItem.Nothing, 'Hera 4F'), Pot(166, 20, PotItem.Bomb, 'Hera 4F'), Pot(214, 21, PotItem.Heart, 'Hera 4F'), Pot(40, 28, PotItem.OneRupee, 'Hera 4F'), - Pot(44, 28, PotItem.OneRupee, 'Hera 4F'), Pot(80, 28, PotItem.FiveRupees, 'Hera 4F'), Pot(84, 28, PotItem.FiveRupees, 'Hera 4F'), Pot(102, 17, PotItem.Nothing, 'Hera 4F'), Pot(98, 17, PotItem.Nothing, 'Hera 4F'), - Pot(106, 17, PotItem.Nothing, 'Hera 4F'), Pot(166, 21, PotItem.Nothing, 'Hera 4F'), Pot(166, 19, PotItem.Nothing, 'Hera 4F'), Pot(92, 12, PotItem.Nothing, 'Hera 4F'), Pot(160, 12, PotItem.Nothing, 'Hera 4F')], - 42: [Pot(80, 12, PotItem.OneRupee, 'PoD Arena Main'), Pot(80, 19, PotItem.Heart, 'PoD Arena Main')], - 43: [Pot(16, 5, PotItem.Heart, 'PoD Sexy Statue'), Pot(44, 5, PotItem.Switch, 'PoD Sexy Statue'), Pot(16, 6, PotItem.Heart, 'PoD Sexy Statue'), Pot(44, 6, PotItem.Bomb, 'PoD Sexy Statue'), Pot(16, 7, PotItem.Heart, 'PoD Sexy Statue'), - Pot(44, 7, PotItem.Bomb, 'PoD Sexy Statue'), Pot(146, 21, PotItem.Bomb, 'PoD Map Balcony'), Pot(170, 21, PotItem.FiveArrows, 'PoD Map Balcony'), Pot(146, 22, PotItem.Bomb, 'PoD Map Balcony'), - Pot(170, 22, PotItem.FiveArrows, 'PoD Map Balcony')], - 44: [Pot(108, 24, PotItem.Heart, 'Hookshot Cave (Middle)'), Pot(112, 24, PotItem.Heart, 'Hookshot Cave (Middle)')], - 47: [Pot(28, 7, PotItem.Heart, 'Kakariko Well (top)'), Pot(32, 7, PotItem.Heart, 'Kakariko Well (top)'), Pot(28, 9, PotItem.FiveRupees, 'Kakariko Well (top)'), Pot(32, 9, PotItem.FiveRupees, 'Kakariko Well (top)'), - Pot(172, 19, PotItem.FiveRupees, 'Kakariko Well (top)'), Pot(180, 19, PotItem.FiveRupees, 'Kakariko Well (top)'), Pot(104, 27, PotItem.Heart, 'Kakariko Well (bottom)'), Pot(104, 28, PotItem.Heart, 'Kakariko Well (bottom)')], - 49: [Pot(92, 28, PotItem.Bomb, 'Hera Beetles'), Pot(96, 28, PotItem.Nothing, 'Hera Beetles')], - 50: [Pot(28, 13, PotItem.SmallMagic, 'Sewers Dark Cross')], - 52: [Pot(78, 8, PotItem.FiveRupees, 'Swamp Barrier Ledge'), Pot(92, 8, PotItem.FiveRupees, 'Swamp Barrier Ledge')], - 53: [Pot(60, 6, PotItem.Key, 'Swamp Trench 2 Alcove'), Pot(20, 8, PotItem.FiveRupees, 'Swamp Big Key Ledge'), Pot(24, 8, PotItem.FiveRupees, 'Swamp Big Key Ledge'), Pot(28, 8, PotItem.FiveRupees, 'Swamp Big Key Ledge'), - Pot(32, 8, PotItem.FiveRupees, 'Swamp Big Key Ledge'), Pot(36, 8, PotItem.FiveRupees, 'Swamp Big Key Ledge'), Pot(48, 20, PotItem.Heart, 'Swamp Trench 2 Departure'), Pot(76, 23, PotItem.Nothing, 'Swamp Trench 2 Pots'), - Pot(88, 23, PotItem.Nothing, 'Swamp Trench 2 Pots'), Pot(100, 27, PotItem.Nothing, 'Swamp Trench 2 Pots'), Pot(242, 28, PotItem.Nothing, 'Swamp Trench 2 Pots'), Pot(240, 22, PotItem.Heart, 'Swamp Trench 2 Pots'), - Pot(76, 28, PotItem.Heart, 'Swamp Trench 2 Pots')], - 54: [Pot(108, 4, PotItem.Bomb, 'Swamp Hub Dead Ledge'), Pot(112, 4, PotItem.FiveRupees, 'Swamp Hub Dead Ledge'), Pot(10, 16, PotItem.Heart, 'Swamp Hub'), Pot(154, 15, PotItem.Nothing, 'Swamp Hub'), Pot(114, 16, PotItem.Key, 'Swamp Hub'), - Pot(222, 15, PotItem.Nothing, 'Swamp Hub'), Pot(188, 5, PotItem.Nothing, 'Swamp Hub North Ledge'), Pot(192, 5, PotItem.Nothing, 'Swamp Hub North Ledge')], - 55: [Pot(60, 6, PotItem.Key, 'Swamp Trench 1 Alcove'), Pot(48, 20, PotItem.Nothing, 'Swamp Trench 1 Key Ledge')], - 56: [Pot(164, 12, PotItem.Bomb, 'Swamp Pot Row'), Pot(164, 13, PotItem.FiveRupees, 'Swamp Pot Row'), Pot(164, 18, PotItem.Bomb, 'Swamp Pot Row'), Pot(164, 19, PotItem.Key, 'Swamp Pot Row')], - 57: [Pot(12, 20, PotItem.Heart, 'Skull Spike Corner'), Pot(48, 28, PotItem.FiveArrows, 'Skull Spike Corner'), Pot(100, 22, PotItem.SmallMagic, 'Skull Final Drop'), Pot(100, 26, PotItem.FiveArrows, 'Skull Final Drop')], - 60: [Pot(24, 8, PotItem.SmallMagic, 'Hookshot Cave (Front)'), Pot(64, 12, PotItem.FiveRupees, 'Hookshot Cave (Front)'), Pot(20, 14, PotItem.OneRupee, 'Hookshot Cave (Front)'), Pot(20, 19, PotItem.Nothing, 'Hookshot Cave (Front)'), - Pot(68, 18, PotItem.FiveRupees, 'Hookshot Cave (Front)'), Pot(96, 19, PotItem.Heart, 'Hookshot Cave (Front)'), Pot(64, 20, PotItem.FiveRupees, 'Hookshot Cave (Front)'), Pot(64, 26, PotItem.FiveRupees, 'Hookshot Cave (Front)')], - 61: [Pot(76, 12, PotItem.Bomb, 'GT Mini Helmasaur Room'), Pot(112, 12, PotItem.Bomb, 'GT Mini Helmasaur Room'), Pot(24, 22, PotItem.Heart, 'GT Crystal Inner Circle'), Pot(40, 22, PotItem.FiveArrows, 'GT Crystal Inner Circle'), - Pot(32, 24, PotItem.Heart, 'GT Crystal Inner Circle'), Pot(20, 26, PotItem.FiveRupees, 'GT Crystal Inner Circle'), Pot(36, 26, PotItem.BigMagic, 'GT Crystal Inner Circle')], - 62: [Pot(96, 6, PotItem.Bomb, 'Ice Stalfos Hint'), Pot(100, 6, PotItem.SmallMagic, 'Ice Stalfos Hint'), Pot(88, 10, PotItem.Heart, 'Ice Stalfos Hint'), Pot(92, 10, PotItem.SmallMagic, 'Ice Stalfos Hint')], - 63: [Pot(12, 25, PotItem.OneRupee, 'Ice Hammer Block'), Pot(20, 25, PotItem.OneRupee, 'Ice Hammer Block'), Pot(12, 26, PotItem.Bomb, 'Ice Hammer Block'), Pot(20, 26, PotItem.Bomb, 'Ice Hammer Block'), - Pot(12, 27, PotItem.Switch, 'Ice Hammer Block'), Pot(20, 27, PotItem.Heart, 'Ice Hammer Block'), Pot(28, 23, PotItem.Key, 'Ice Hammer Block')], - 65: [Pot(100, 10, PotItem.Heart, 'Sewers Behind Tapestry'), Pot(52, 15, PotItem.OneRupee, 'Sewers Behind Tapestry'), Pot(52, 16, PotItem.SmallMagic, 'Sewers Behind Tapestry'), Pot(148, 22, PotItem.SmallMagic, 'Sewers Behind Tapestry')], - 67: [Pot(66, 4, PotItem.FiveArrows, 'Desert Wall Slide'), Pot(78, 4, PotItem.SmallMagic, 'Desert Wall Slide'), Pot(66, 9, PotItem.Heart, 'Desert Wall Slide'), Pot(78, 9, PotItem.Heart, 'Desert Wall Slide'), - Pot(112, 28, PotItem.Nothing, 'Desert Tiles 2'), Pot(76, 28, PotItem.Nothing, 'Desert Tiles 2'), Pot(76, 20, PotItem.Nothing, 'Desert Tiles 2'), Pot(112, 20, PotItem.Key, 'Desert Tiles 2')], - 69: [Pot(12, 4, PotItem.FiveArrows, 'Thieves Basement Block'), Pot(48, 12, PotItem.FiveArrows, 'Thieves Basement Block'), Pot(92, 11, PotItem.Nothing, "Thieves Blind's Cell"), Pot(108, 11, PotItem.Heart, "Thieves Blind's Cell"), - Pot(220, 16, PotItem.SmallMagic, "Thieves Blind's Cell"), Pot(236, 16, PotItem.Heart, "Thieves Blind's Cell")], - 70: [Pot(96, 5, PotItem.Heart, 'Swamp Donut Top'), Pot(28, 27, PotItem.Heart, 'Swamp Donut Bottom')], - 73: [Pot(104, 15, PotItem.SmallMagic, 'Skull Torch Room'), Pot(104, 16, PotItem.SmallMagic, 'Skull Torch Room'), Pot(156, 27, PotItem.Nothing, 'Skull Star Pits'), Pot(172, 24, PotItem.Nothing, 'Skull Star Pits'), - Pot(172, 23, PotItem.Nothing, 'Skull Star Pits'), Pot(144, 20, PotItem.Nothing, 'Skull Star Pits'), Pot(144, 19, PotItem.SmallMagic, 'Skull Star Pits'), Pot(172, 20, PotItem.Heart, 'Skull Star Pits'), - Pot(144, 27, PotItem.Heart, 'Skull Star Pits'), Pot(172, 28, PotItem.SmallMagic, 'Skull Star Pits'), Pot(160, 27, PotItem.Nothing, 'Skull Star Pits')], - 74: [Pot(14, 5, PotItem.Switch, 'PoD Left Cage'), Pot(32, 5, PotItem.Bomb, 'PoD Left Cage'), Pot(14, 11, PotItem.Heart, 'PoD Left Cage'), Pot(32, 11, PotItem.OneRupee, 'PoD Left Cage'), Pot(56, 8, PotItem.Bomb, 'PoD Middle Cage'), - Pot(68, 8, PotItem.Bomb, 'PoD Middle Cage'), Pot(92, 5, PotItem.Bomb, 'PoD Middle Cage'), Pot(110, 5, PotItem.Switch, 'PoD Middle Cage'), Pot(92, 11, PotItem.OneRupee, 'PoD Middle Cage'), Pot(110, 11, PotItem.Heart, 'PoD Middle Cage')], - 75: [Pot(20, 6, PotItem.FiveArrows, 'PoD Mimics 1'), Pot(40, 6, PotItem.Heart, 'PoD Mimics 1')], - 78: [Pot(140, 7, PotItem.Nothing, 'Ice Bomb Jump Catwalk'), Pot(48, 10, PotItem.Nothing, 'Ice Bomb Jump Catwalk'), Pot(140, 11, PotItem.Switch, 'Ice Bomb Jump Catwalk'), Pot(28, 12, PotItem.Heart, 'Ice Bomb Jump Catwalk'), - Pot(112, 12, PotItem.SmallMagic, 'Ice Narrow Corridor')], - 80: [Pot(96, 38, PotItem.Heart, 'Hyrule Castle West Hall'), Pot(100, 38, PotItem.Heart, 'Hyrule Castle West Hall')], - 82: [Pot(138, 3, PotItem.Heart, 'Hyrule Castle East Hall'), Pot(194, 26, PotItem.Heart, 'Hyrule Castle East Hall')], - 83: [Pot(92, 11, PotItem.Heart, 'Desert Beamos Hall'), Pot(96, 11, PotItem.SmallMagic, 'Desert Beamos Hall'), Pot(100, 11, PotItem.Key, 'Desert Beamos Hall'), Pot(104, 11, PotItem.Heart, 'Desert Beamos Hall')], - 84: [Pot(186, 25, PotItem.FiveRupees, 'Swamp Attic'), Pot(186, 26, PotItem.Heart, 'Swamp Attic'), Pot(186, 27, PotItem.Heart, 'Swamp Attic'), Pot(186, 28, PotItem.Heart, 'Swamp Attic')], - 85: [Pot(230, 24, PotItem.SmallMagic, 'Secret Passage'), Pot(230, 25, PotItem.SmallMagic, 'Secret Passage')], - 86: [Pot(100, 6, PotItem.Nothing, 'Skull Back Drop'), Pot(96, 10, PotItem.Nothing, 'Skull Back Drop'), Pot(92, 10, PotItem.Nothing, 'Skull Back Drop'), Pot(20, 6, PotItem.SmallMagic, 'Skull X Room'), - Pot(40, 6, PotItem.SmallMagic, 'Skull X Room'), Pot(24, 7, PotItem.SmallMagic, 'Skull X Room'), Pot(36, 7, PotItem.SmallMagic, 'Skull X Room'), Pot(12, 8, PotItem.Heart, 'Skull X Room'), Pot(48, 8, PotItem.Heart, 'Skull X Room'), - Pot(24, 9, PotItem.SmallMagic, 'Skull X Room'), Pot(36, 9, PotItem.SmallMagic, 'Skull X Room'), Pot(20, 10, PotItem.FiveRupees, 'Skull X Room'), Pot(40, 10, PotItem.FiveRupees, 'Skull X Room'), Pot(12, 20, PotItem.Key, 'Skull 2 West Lobby'), - Pot(48, 20, PotItem.Nothing, 'Skull 2 West Lobby')], - 87: [Pot(92, 7, PotItem.BigMagic, 'Skull Lone Pot'), Pot(32, 4, PotItem.Nothing, 'Skull Big Key'), Pot(92, 23, PotItem.Bomb, 'Skull Pot Prison'), Pot(100, 23, PotItem.SmallMagic, 'Skull Pot Prison'), - Pot(84, 25, PotItem.FiveRupees, 'Skull Pot Prison'), Pot(76, 27, PotItem.Heart, 'Skull Pot Prison'), Pot(12, 20, PotItem.SmallMagic, 'Skull 2 East Lobby'), Pot(48, 20, PotItem.SmallMagic, 'Skull 2 East Lobby'), - Pot(30, 22, PotItem.Switch, 'Skull 2 East Lobby')], - 88: [Pot(12, 7, PotItem.SmallMagic, 'Skull Pull Switch'), Pot(16, 7, PotItem.Nothing, 'Skull Pull Switch'), Pot(16, 8, PotItem.SmallMagic, 'Skull Pull Switch'), Pot(12, 12, PotItem.Nothing, 'Skull Pull Switch'), - Pot(96, 9, PotItem.Nothing, 'Skull Pot Circle'), Pot(92, 8, PotItem.Nothing, 'Skull Pot Circle'), Pot(108, 8, PotItem.Nothing, 'Skull Pot Circle'), Pot(108, 6, PotItem.Nothing, 'Skull Pot Circle'), - Pot(104, 5, PotItem.Nothing, 'Skull Pot Circle'), Pot(92, 6, PotItem.Nothing, 'Skull Pot Circle'), Pot(96, 5, PotItem.Bomb, 'Skull Pot Circle'), Pot(100, 5, PotItem.SmallMagic, 'Skull Pot Circle'), - Pot(92, 7, PotItem.Heart, 'Skull Pot Circle'), Pot(108, 7, PotItem.Heart, 'Skull Pot Circle'), Pot(100, 9, PotItem.SmallMagic, 'Skull Pot Circle'), Pot(104, 9, PotItem.Bomb, 'Skull Pot Circle')], - 89: [Pot(26, 43, PotItem.Heart, 'Skull 3 Lobby'), Pot(32, 40, PotItem.Nothing, 'Skull 3 Lobby'), Pot(76, 28, PotItem.Nothing, 'Skull East Bridge'), Pot(112, 28, PotItem.Nothing, 'Skull East Bridge')], - 91: [Pot(218, 37, PotItem.Nothing, 'GT Hidden Spikes'), Pot(222, 37, PotItem.Switch, 'GT Hidden Spikes'), Pot(226, 37, PotItem.Nothing, 'GT Hidden Spikes')], - 92: [Pot(228, 25, PotItem.Nothing, 'GT Refill'), Pot(104, 24, PotItem.Nothing, 'GT Refill'), Pot(228, 22, PotItem.Nothing, 'GT Refill'), Pot(216, 25, PotItem.Nothing, 'GT Refill'), Pot(84, 24, PotItem.Nothing, 'GT Refill'), - Pot(216, 22, PotItem.Nothing, 'GT Refill'), Pot(94, 22, PotItem.Bomb, 'GT Refill'), Pot(94, 26, PotItem.BigMagic, 'GT Refill')], - 93: [Pot(16, 5, PotItem.Bomb, 'GT Gauntlet 2'), Pot(44, 5, PotItem.FiveRupees, 'GT Gauntlet 2'), Pot(16, 11, PotItem.OneRupee, 'GT Gauntlet 2'), Pot(44, 11, PotItem.FiveArrows, 'GT Gauntlet 2'), Pot(12, 20, PotItem.FiveArrows, 'GT Gauntlet 3'), - Pot(48, 20, PotItem.Bomb, 'GT Gauntlet 3'), Pot(12, 28, PotItem.SmallMagic, 'GT Gauntlet 3'), Pot(48, 28, PotItem.Bomb, 'GT Gauntlet 3')], - 94: [Pot(92, 4, PotItem.SmallMagic, 'Ice Falling Square'), Pot(96, 4, PotItem.SmallMagic, 'Ice Falling Square'), Pot(76, 8, PotItem.Heart, 'Ice Falling Square'), Pot(112, 8, PotItem.Heart, 'Ice Falling Square')], - 95: [Pot(44, 27, PotItem.Switch, 'Ice Spike Room')], - 96: [Pot(76, 4, PotItem.Heart, 'Hyrule Castle West Lobby'), Pot(112, 4, PotItem.Heart, 'Hyrule Castle West Lobby')], - 98: [Pot(208, 21, PotItem.Heart, 'Hyrule Castle East Lobby')], - 99: [Pot(48, 4, PotItem.Nothing, 'Desert Tiles 1'), Pot(12, 4, PotItem.Nothing, 'Desert Tiles 1'), Pot(12, 8, PotItem.Nothing, 'Desert Tiles 1'), Pot(48, 12, PotItem.Nothing, 'Desert Tiles 1'), Pot(48, 8, PotItem.Heart, 'Desert Tiles 1'), - Pot(12, 12, PotItem.Key, 'Desert Tiles 1')], - 100: [Pot(12, 22, PotItem.Bomb, 'Thieves Attic Hint', PotFlags.SwitchLogicChange), Pot(16, 22, PotItem.Bomb, 'Thieves Attic Hint', PotFlags.SwitchLogicChange), Pot(20, 22, PotItem.Bomb, 'Thieves Attic Hint', PotFlags.SwitchLogicChange), - Pot(36, 28, PotItem.Bomb, 'Thieves Attic'), Pot(40, 28, PotItem.SmallMagic, 'Thieves Attic'), - Pot(44, 28, PotItem.SmallMagic, 'Thieves Attic'), Pot(48, 28, PotItem.Switch, 'Thieves Attic')], - 101: [Pot(100, 28, PotItem.Bomb, 'Thieves Attic Window'), Pot(104, 28, PotItem.Bomb, 'Thieves Attic Window')], - 102: [Pot(48, 37, PotItem.FiveArrows, 'Swamp Refill'), Pot(52, 37, PotItem.Bomb, 'Swamp Refill'), Pot(56, 37, PotItem.FiveRupees, 'Swamp Refill'), Pot(48, 38, PotItem.FiveArrows, 'Swamp Refill'), Pot(52, 38, PotItem.Bomb, 'Swamp Refill'), - Pot(56, 38, PotItem.FiveRupees, 'Swamp Refill'), Pot(84, 5, PotItem.Heart, 'Swamp Behind Waterfall'), Pot(104, 5, PotItem.FiveArrows, 'Swamp Behind Waterfall'), Pot(84, 6, PotItem.Heart, 'Swamp Behind Waterfall'), - Pot(104, 6, PotItem.Bomb, 'Swamp Behind Waterfall')], - 103: [Pot(22, 26, PotItem.Nothing, 'Skull Left Drop'), Pot(18, 22, PotItem.Nothing, 'Skull Left Drop'), Pot(12, 7, PotItem.FiveArrows, 'Skull Left Drop'), Pot(48, 7, PotItem.SmallMagic, 'Skull Left Drop'), - Pot(18, 23, PotItem.SmallMagic, 'Skull Left Drop'), Pot(18, 26, PotItem.Heart, 'Skull Left Drop'), Pot(96, 19, PotItem.Heart, 'Skull Compass Room'), Pot(74, 20, PotItem.SmallMagic, 'Skull Compass Room'), - Pot(92, 9, PotItem.Nothing, 'Skull Compass Room'), Pot(84, 28, PotItem.Nothing, 'Skull Compass Room'), Pot(104, 28, PotItem.Heart, 'Skull Compass Room')], - 104: [Pot(84, 14, PotItem.Nothing, 'Skull Pinball'), Pot(84, 13, PotItem.Nothing, 'Skull Pinball'), Pot(88, 12, PotItem.Nothing, 'Skull Pinball'), Pot(88, 6, PotItem.Nothing, 'Skull Pinball'), Pot(88, 5, PotItem.Nothing, 'Skull Pinball'), - Pot(88, 4, PotItem.Nothing, 'Skull Pinball'), Pot(64, 17, PotItem.Nothing, 'Skull Pinball'), Pot(64, 15, PotItem.Nothing, 'Skull Pinball'), Pot(64, 7, PotItem.Heart, 'Skull Pinball'), Pot(88, 7, PotItem.SmallMagic, 'Skull Pinball'), - Pot(64, 16, PotItem.Heart, 'Skull Pinball'), Pot(64, 24, PotItem.SmallMagic, 'Skull Pinball'), Pot(64, 25, PotItem.Heart, 'Skull Pinball')], - 107: [Pot(28, 5, PotItem.Heart, 'GT Crystal Paths'), Pot(44, 5, PotItem.Nothing, 'GT Crystal Paths'), Pot(28, 8, PotItem.Nothing, 'GT Crystal Paths'), Pot(44, 8, PotItem.SmallMagic, 'GT Crystal Paths'), - Pot(28, 11, PotItem.SmallMagic, 'GT Crystal Paths'), Pot(44, 11, PotItem.Nothing, 'GT Crystal Paths'), Pot(94, 25, PotItem.Nothing, 'GT Mimics 2'), Pot(98, 25, PotItem.FiveArrows, 'GT Mimics 2')], - 108: [Pot(20, 6, PotItem.Heart, 'GT Quad Pot'), Pot(40, 6, PotItem.FiveArrows, 'GT Quad Pot'), Pot(20, 10, PotItem.Bomb, 'GT Quad Pot'), Pot(40, 10, PotItem.SmallMagic, 'GT Quad Pot')], - 109: [Pot(28, 26, PotItem.Heart, 'GT Gauntlet 5'), Pot(32, 26, PotItem.Heart, 'GT Gauntlet 5'), Pot(28, 27, PotItem.SmallMagic, 'GT Gauntlet 5'), Pot(32, 27, PotItem.SmallMagic, 'GT Gauntlet 5')], - 115: [Pot(154, 21, PotItem.FiveArrows, 'Desert Circle of Pots'), Pot(158, 21, PotItem.OneRupee, 'Desert Circle of Pots'), Pot(20, 23, PotItem.Switch, 'Desert Circle of Pots'), Pot(36, 23, PotItem.FiveRupees, 'Desert Circle of Pots'), - Pot(144, 24, PotItem.Heart, 'Desert Circle of Pots'), Pot(168, 24, PotItem.FiveArrows, 'Desert Circle of Pots'), Pot(20, 26, PotItem.SmallMagic, 'Desert Circle of Pots'), Pot(36, 26, PotItem.Heart, 'Desert Circle of Pots'), - Pot(154, 27, PotItem.OneRupee, 'Desert Circle of Pots'), Pot(158, 27, PotItem.FiveRupees, 'Desert Circle of Pots')], - 116: [Pot(30, 5, PotItem.SmallMagic, 'Desert Map Room'), Pot(62, 5, PotItem.Switch, 'Desert Map Room'), Pot(94, 5, PotItem.SmallMagic, 'Desert Map Room'), Pot(14, 11, PotItem.Heart, 'Desert Map Room'), - Pot(46, 11, PotItem.FiveArrows, 'Desert Map Room'), Pot(78, 11, PotItem.FiveArrows, 'Desert Map Room'), Pot(110, 11, PotItem.Heart, 'Desert Map Room')], - 117: [Pot(148, 22, PotItem.SmallMagic, 'Desert Arrow Pot Corner'), Pot(160, 22, PotItem.FiveArrows, 'Desert Arrow Pot Corner'), Pot(172, 22, PotItem.Heart, 'Desert Arrow Pot Corner')], - 118: [Pot(112, 12, PotItem.Heart, 'Swamp Drain Right'), Pot(84, 23, PotItem.Heart, 'Swamp Flooded Spot'), Pot(96, 23, PotItem.Heart, 'Swamp Flooded Spot')], - 123: [Pot(48, 10, PotItem.Nothing, 'GT Conveyor Star Pits'), Pot(88, 10, PotItem.Nothing, 'GT Conveyor Star Pits'), Pot(76, 7, PotItem.Nothing, 'GT Conveyor Star Pits'), Pot(60, 4, PotItem.Heart, 'GT Conveyor Star Pits'), - Pot(64, 4, PotItem.Key, 'GT Conveyor Star Pits')], - 124: [Pot(36, 21, PotItem.Nothing, 'GT Falling Bridge'), Pot(24, 11, PotItem.Nothing, 'GT Falling Bridge'), Pot(28, 4, PotItem.Heart, 'GT Falling Bridge'), Pot(32, 4, PotItem.Heart, 'GT Falling Bridge')], - 125: [Pot(44, 12, PotItem.Nothing, 'GT Firesnake Room'), Pot(44, 6, PotItem.Nothing, 'GT Firesnake Room'), Pot(112, 6, PotItem.Heart, 'GT Firesnake Room'), Pot(108, 20, PotItem.FiveArrows, 'GT Warp Maze - Pot Rail'), - Pot(114, 20, PotItem.Bomb, 'GT Petting Zoo'), Pot(76, 28, PotItem.Bomb, 'GT Petting Zoo')], - 126: [Pot(86, 15, PotItem.Heart, 'Ice Tall Hint'), Pot(82, 26, PotItem.SmallMagic, 'Ice Tall Hint'), Pot(100, 26, PotItem.Switch, 'Ice Tall Hint'), Pot(104, 26, PotItem.Nothing, 'Ice Tall Hint')], - 128: [Pot(48, 4, PotItem.Heart, 'Hyrule Dungeon Cellblock'), Pot(52, 4, PotItem.Heart, 'Hyrule Dungeon Cellblock'), Pot(56, 4, PotItem.Heart, 'Hyrule Dungeon Cellblock')], - 130: [Pot(50, 5, PotItem.Nothing, 'Hyrule Dungeon South Abyss'), Pot(50, 10, PotItem.Nothing, 'Hyrule Dungeon South Abyss'), Pot(76, 50, PotItem.Heart, 'Hyrule Dungeon South Abyss')], - 131: [Pot(76, 4, PotItem.FiveArrows, 'Desert West Wing'), Pot(80, 4, PotItem.OneRupee, 'Desert West Wing'), Pot(76, 28, PotItem.FiveRupees, 'Desert West Wing'), Pot(80, 28, PotItem.FiveArrows, 'Desert West Wing')], - 132: [Pot(64, 17, PotItem.Nothing, 'Desert Main Lobby'), Pot(60, 17, PotItem.Nothing, 'Desert Main Lobby'), Pot(80, 14, PotItem.Nothing, 'Desert Main Lobby'), Pot(44, 14, PotItem.Nothing, 'Desert Main Lobby'), - Pot(100, 6, PotItem.Nothing, 'Desert Main Lobby'), Pot(24, 6, PotItem.Nothing, 'Desert Main Lobby'), Pot(24, 7, PotItem.FiveArrows, 'Desert Main Lobby'), Pot(100, 7, PotItem.FiveArrows, 'Desert Main Lobby')], - 133: [Pot(44, 28, PotItem.Heart, 'Desert East Wing'), Pot(48, 28, PotItem.FiveArrows, 'Desert East Wing')], - 135: [Pot(12, 11, PotItem.Nothing, 'Hera Tile Room'), Pot(16, 12, PotItem.Nothing, 'Hera Tile Room'), Pot(40, 12, PotItem.Nothing, 'Hera Tile Room'), Pot(32, 12, PotItem.Nothing, 'Hera Tile Room'), Pot(24, 12, PotItem.Nothing, 'Hera Tile Room'), - Pot(16, 11, PotItem.Nothing, 'Hera Tile Room'), Pot(76, 20, PotItem.SmallMagic, 'Hera Torches'), Pot(112, 20, PotItem.BigMagic, 'Hera Torches')], - 139: [Pot(76, 12, PotItem.Nothing, 'GT Conveyor Cross'), Pot(112, 12, PotItem.Key, 'GT Conveyor Cross'), Pot(32, 23, PotItem.Nothing, 'GT Hookshot South Platform'), Pot(28, 23, PotItem.Nothing, 'GT Hookshot South Platform'), - Pot(32, 9, PotItem.SmallMagic, 'GT Hookshot East Platform'), Pot(76, 20, PotItem.Nothing, 'GT Map Room'), Pot(76, 28, PotItem.Heart, 'GT Map Room')], - 140: [Pot(76, 12, PotItem.Switch, 'GT Hope Room'), Pot(112, 12, PotItem.SmallMagic, 'GT Hope Room'), Pot(76, 20, PotItem.Bomb, "GT Bob's Room"), Pot(92, 20, PotItem.Bomb, "GT Bob's Room"), Pot(100, 21, PotItem.FiveArrows, "GT Bob's Room"), - Pot(104, 26, PotItem.Bomb, "GT Bob's Room"), Pot(88, 27, PotItem.Bomb, "GT Bob's Room")], - 141: [Pot(204, 11, PotItem.Nothing, 'GT Speed Torch Upper'), Pot(204, 14, PotItem.BigMagic, 'GT Speed Torch Upper'), Pot(28, 23, PotItem.Heart, 'GT Pots n Blocks'), Pot(36, 23, PotItem.Heart, 'GT Pots n Blocks'), - Pot(32, 24, PotItem.BigMagic, 'GT Pots n Blocks')], - 142: [Pot(80, 5, PotItem.FiveArrows, 'Ice Lonely Freezor'), Pot(80, 6, PotItem.Nothing, 'Ice Lonely Freezor')], - 145: [Pot(84, 4, PotItem.Heart, 'Mire Falling Foes'), Pot(104, 4, PotItem.SmallMagic, 'Mire Falling Foes')], - 146: [Pot(86, 23, PotItem.Nothing, 'Mire Tall Dark and Roomy'), Pot(92, 23, PotItem.Nothing, 'Mire Tall Dark and Roomy'), Pot(98, 23, PotItem.Nothing, 'Mire Tall Dark and Roomy'), Pot(104, 23, PotItem.Nothing, 'Mire Tall Dark and Roomy')], - 147: [Pot(28, 7, PotItem.Switch, 'Mire Dark Shooters'), Pot(96, 7, PotItem.Heart, 'Mire Dark Shooters', PotFlags.NoSwitch)], - 150: [Pot(14, 18, PotItem.Nothing, 'GT Torch Cross'), Pot(32, 5, PotItem.Nothing, 'GT Torch Cross'), Pot(32, 17, PotItem.SmallMagic, 'GT Torch Cross'), Pot(32, 24, PotItem.SmallMagic, 'GT Torch Cross'), - Pot(14, 24, PotItem.Nothing, 'GT Torch Cross'), Pot(76, 21, PotItem.Heart, 'GT Staredown'), Pot(112, 21, PotItem.BigMagic, 'GT Staredown')], - 153: [Pot(40, 20, PotItem.SmallMagic, 'Eastern Darkness'), Pot(84, 20, PotItem.Heart, 'Eastern Darkness')], - 155: [Pot(48, 4, PotItem.SmallMagic, 'GT Double Switch Pot Corners'), Pot(48, 12, PotItem.Key, 'GT Double Switch Pot Corners'), Pot(28, 24, PotItem.Nothing, 'GT Warp Maze - Mid Section'), Pot(32, 24, PotItem.Nothing, 'GT Warp Maze - Mid Section')], - 156: [Pot(56, 8, PotItem.SmallMagic, 'GT Invisible Catwalk'), Pot(56, 9, PotItem.FiveArrows, 'GT Invisible Catwalk')], - 157: [Pot(76, 4, PotItem.Bomb, 'GT Crystal Conveyor Left'), Pot(84, 4, PotItem.SmallMagic, 'GT Crystal Conveyor Left'), Pot(32, 7, PotItem.Nothing, 'GT Compass Room'), Pot(40, 9, PotItem.Nothing, 'GT Compass Room')], - 159: [Pot(138, 20, PotItem.Nothing, 'Ice Many Pots'), Pot(138, 19, PotItem.Heart, 'Ice Many Pots'), Pot(178, 19, PotItem.Heart, 'Ice Many Pots'), Pot(40, 21, PotItem.Switch, 'Ice Many Pots'), Pot(138, 21, PotItem.Key, 'Ice Many Pots'), - Pot(20, 27, PotItem.Heart, 'Ice Many Pots'), Pot(138, 27, PotItem.Heart, 'Ice Many Pots'), Pot(178, 28, PotItem.Heart, 'Ice Many Pots'), Pot(178, 21, PotItem.Nothing, 'Ice Many Pots'), Pot(178, 20, PotItem.Nothing, 'Ice Many Pots'), - Pot(40, 27, PotItem.Nothing, 'Ice Many Pots'), Pot(178, 27, PotItem.Nothing, 'Ice Many Pots'), Pot(178, 26, PotItem.Nothing, 'Ice Many Pots'), Pot(138, 28, PotItem.Nothing, 'Ice Many Pots'), Pot(138, 26, PotItem.Nothing, 'Ice Many Pots'), - Pot(20, 21, PotItem.Nothing, 'Ice Many Pots')], - 161: [Pot(150, 6, PotItem.Key, 'Mire Fishbone'), Pot(100, 11, PotItem.SmallMagic, 'Mire Fishbone'), Pot(104, 12, PotItem.Heart, 'Mire Fishbone'), Pot(108, 13, PotItem.SmallMagic, 'Mire Fishbone'), Pot(112, 14, PotItem.Heart, 'Mire Fishbone'), - Pot(96, 27, PotItem.Nothing, 'Mire South Fish'), Pot(92, 21, PotItem.Nothing, 'Mire South Fish'), Pot(96, 23, PotItem.Heart, 'Mire South Fish'), Pot(92, 25, PotItem.Nothing, 'Mire South Fish'), - Pot(76, 28, PotItem.Nothing, 'Mire South Fish'), Pot(112, 28, PotItem.Nothing, 'Mire South Fish')], - 162: [Pot(12, 28, PotItem.BigMagic, 'Mire Left Bridge')], - 168: [Pot(138, 28, PotItem.Nothing, 'Eastern Stalfos Spawn'), Pot(178, 28, PotItem.Nothing, 'Eastern Stalfos Spawn'), Pot(178, 19, PotItem.Nothing, 'Eastern Stalfos Spawn'), Pot(138, 19, PotItem.Heart, 'Eastern Stalfos Spawn'), - Pot(30, 24, PotItem.OneRupee, 'Eastern Stalfos Spawn')], - 169: [Pot(144, 43, PotItem.FiveArrows, 'Eastern Courtyard'), Pot(236, 43, PotItem.FiveArrows, 'Eastern Courtyard'), Pot(144, 44, PotItem.FiveArrows, 'Eastern Courtyard'), Pot(236, 44, PotItem.Heart, 'Eastern Courtyard'), - Pot(12, 19, PotItem.Nothing, 'Eastern Courtyard Ledge'), Pot(112, 19, PotItem.Nothing, 'Eastern Courtyard Ledge'), Pot(16, 20, PotItem.Heart, 'Eastern Courtyard Ledge'), Pot(108, 20, PotItem.Heart, 'Eastern Courtyard Ledge')], - 170: [Pot(212, 10, PotItem.Nothing, 'Eastern Pot Switch'), Pot(232, 10, PotItem.Nothing, 'Eastern Pot Switch'), Pot(232, 5, PotItem.Nothing, 'Eastern Pot Switch'), Pot(212, 5, PotItem.Heart, 'Eastern Pot Switch'), - Pot(94, 8, PotItem.Switch, 'Eastern Pot Switch'), Pot(108, 55, PotItem.Heart, 'Eastern Map Balcony'), Pot(108, 56, PotItem.Heart, 'Eastern Map Balcony'), Pot(108, 57, PotItem.Heart, 'Eastern Map Balcony')], - 171: [Pot(20, 24, PotItem.Key, 'Thieves Spike Switch')], - 174: [Pot(76, 12, PotItem.Switch, 'Iced T')], - 176: [Pot(20, 27, PotItem.Nothing, 'Tower Circle of Pots'), Pot(24, 24, PotItem.Nothing, 'Tower Circle of Pots'), Pot(44, 25, PotItem.Nothing, 'Tower Circle of Pots'), Pot(20, 21, PotItem.Bomb, 'Tower Circle of Pots'), - Pot(28, 21, PotItem.OneRupee, 'Tower Circle of Pots'), Pot(32, 21, PotItem.FiveRupees, 'Tower Circle of Pots'), Pot(40, 21, PotItem.FiveArrows, 'Tower Circle of Pots'), Pot(16, 23, PotItem.FiveRupees, 'Tower Circle of Pots'), - Pot(44, 23, PotItem.OneRupee, 'Tower Circle of Pots'), Pot(36, 24, PotItem.Heart, 'Tower Circle of Pots'), Pot(16, 25, PotItem.Heart, 'Tower Circle of Pots'), Pot(28, 27, PotItem.FiveArrows, 'Tower Circle of Pots'), - Pot(40, 27, PotItem.Bomb, 'Tower Circle of Pots'), Pot(32, 27, PotItem.Nothing, 'Tower Circle of Pots')], - 177: [Pot(76, 4, PotItem.Heart, 'Mire Spike Barrier'), Pot(112, 4, PotItem.OneRupee, 'Mire Spike Barrier')], - 178: [Pot(48, 40, PotItem.OneRupee, 'Mire BK Door Room'), Pot(76, 40, PotItem.OneRupee, 'Mire BK Door Room'), Pot(48, 41, PotItem.Nothing, 'Mire BK Door Room'), Pot(76, 41, PotItem.Heart, 'Mire BK Door Room'), - Pot(48, 42, PotItem.Nothing, 'Mire BK Door Room'), Pot(76, 40, PotItem.Nothing, 'Mire BK Door Room')], - 179: [Pot(12, 20, PotItem.Key, 'Mire Spikes'), Pot(48, 20, PotItem.SmallMagic, 'Mire Spikes'), Pot(48, 28, PotItem.Switch, 'Mire Spikes')], - 180: [Pot(44, 28, PotItem.BigMagic, 'TR Final Abyss'), Pot(48, 28, PotItem.Heart, 'TR Final Abyss')], - 181: [Pot(112, 4, PotItem.FiveRupees, 'TR Dark Ride'), Pot(112, 15, PotItem.Heart, 'TR Dark Ride'), Pot(76, 16, PotItem.Switch, 'TR Dark Ride'), Pot(112, 16, PotItem.BigMagic, 'TR Dark Ride'), Pot(112, 17, PotItem.Heart, 'TR Dark Ride'), - Pot(112, 28, PotItem.Bomb, 'TR Dark Ride')], - 182: [Pot(94, 9, PotItem.BigMagic, 'TR Refill')], - 183: [Pot(30, 5, PotItem.SmallMagic, 'TR Roller Room')], - 184: [Pot(96, 13, PotItem.Switch, 'Eastern Big Key'), Pot(88, 16, PotItem.Heart, 'Eastern Big Key'), Pot(104, 16, PotItem.Heart, 'Eastern Big Key')], - 185: [Pot(92, 18, PotItem.OneRupee, 'Eastern Cannonball'), Pot(96, 18, PotItem.FiveRupees, 'Eastern Cannonball'), Pot(104, 18, PotItem.FiveRupees, 'Eastern Cannonball'), Pot(108, 18, PotItem.OneRupee, 'Eastern Cannonball')], - 186: [Pot(100, 8, PotItem.Nothing, 'Eastern Dark Pots'), Pot(88, 8, PotItem.Nothing, 'Eastern Dark Pots'), Pot(94, 4, PotItem.OneRupee, 'Eastern Dark Pots'), Pot(76, 6, PotItem.Heart, 'Eastern Dark Pots'), - Pot(112, 6, PotItem.Key, 'Eastern Dark Pots'), Pot(76, 10, PotItem.Heart, 'Eastern Dark Pots'), Pot(112, 10, PotItem.SmallMagic, 'Eastern Dark Pots'), Pot(94, 12, PotItem.OneRupee, 'Eastern Dark Pots')], - 188: [Pot(86, 4, PotItem.Heart, 'Thieves Hallway'), Pot(102, 4, PotItem.Key, 'Thieves Hallway'), Pot(138, 3, PotItem.Bomb, 'Thieves Conveyor Maze'), Pot(178, 3, PotItem.Switch, 'Thieves Conveyor Maze'), - Pot(138, 12, PotItem.Heart, 'Thieves Conveyor Maze'), Pot(178, 12, PotItem.Bomb, 'Thieves Conveyor Maze'), Pot(12, 20, PotItem.Nothing, 'Thieves Pot Alcove Mid'), Pot(48, 20, PotItem.Bomb, 'Thieves Pot Alcove Mid'), - Pot(12, 28, PotItem.Bomb, 'Thieves Pot Alcove Mid'), Pot(48, 28, PotItem.Bomb, 'Thieves Pot Alcove Mid'), Pot(28, 21, PotItem.FiveRupees, 'Thieves Pot Alcove Top'), Pot(32, 21, PotItem.FiveRupees, 'Thieves Pot Alcove Top'), - Pot(28, 27, PotItem.FiveRupees, 'Thieves Pot Alcove Bottom'), Pot(32, 27, PotItem.FiveRupees, 'Thieves Pot Alcove Bottom')], - 190: [Pot(92, 25, PotItem.Switch, 'Ice Switch Room')], - 191: [Pot(40, 20, PotItem.FiveArrows, 'Ice Refill'), Pot(44, 20, PotItem.Heart, 'Ice Refill'), Pot(48, 20, PotItem.Bomb, 'Ice Refill'), Pot(40, 28, PotItem.SmallMagic, 'Ice Refill'), Pot(44, 28, PotItem.SmallMagic, 'Ice Refill'), - Pot(48, 28, PotItem.SmallMagic, 'Ice Refill')], - 192: [Pot(48, 10, PotItem.Bomb, 'Tower Dark Pits'), Pot(12, 14, PotItem.FiveRupees, 'Tower Dark Pits'), Pot(12, 26, PotItem.Heart, 'Tower Dark Pits'), Pot(28, 27, PotItem.OneRupee, 'Tower Dark Pits')], - 194: [Pot(180, 7, PotItem.Switch, 'Mire Hub Switch'), Pot(100, 46, PotItem.SmallMagic, 'Mire Hub Right'), Pot(68, 48, PotItem.OneRupee, 'Mire Hub'), Pot(64, 52, PotItem.FiveArrows, 'Mire Hub')], - 196: [Pot(84, 9, PotItem.Bomb, 'TR Crystal Maze Interior'), Pot(24, 14, PotItem.Heart, 'TR Crystal Maze Interior'), Pot(56, 17, PotItem.FiveRupees, 'TR Crystal Maze Interior'), Pot(84, 17, PotItem.Bomb, 'TR Crystal Maze Interior'), - Pot(12, 21, PotItem.FiveArrows, 'TR Crystal Maze Interior'), Pot(76, 23, PotItem.OneRupee, 'TR Crystal Maze Interior'), Pot(48, 25, PotItem.SmallMagic, 'TR Crystal Maze Interior'), Pot(12, 26, PotItem.Heart, 'TR Crystal Maze Interior')], - 198: [Pot(12, 7, PotItem.BigMagic, 'TR Hub'), Pot(12, 25, PotItem.Heart, 'TR Hub')], - 199: [Pot(12, 10, PotItem.Heart, 'TR Torches'), Pot(12, 11, PotItem.BigMagic, 'TR Torches'), Pot(12, 22, PotItem.SmallMagic, 'TR Torches Ledge'), Pot(12, 28, PotItem.FiveArrows, 'TR Torches Ledge')], - 201: [Pot(30, 22, PotItem.OneRupee, 'Eastern Lobby'), Pot(94, 22, PotItem.OneRupee, 'Eastern Lobby'), Pot(60, 22, PotItem.Switch, 'Eastern Lobby')], - 203: [Pot(80, 4, PotItem.Nothing, 'Thieves Ambush'), Pot(80, 28, PotItem.Nothing, 'Thieves Ambush'), Pot(88, 16, PotItem.Heart, 'Thieves Ambush'), Pot(88, 28, PotItem.FiveRupees, 'Thieves Ambush')], - 204: [Pot(36, 4, PotItem.FiveRupees, 'Thieves Rail Ledge'), Pot(36, 28, PotItem.FiveRupees, 'Thieves Rail Ledge'), Pot(112, 4, PotItem.Heart, 'Thieves BK Corner'), Pot(112, 28, PotItem.Bomb, 'Thieves BK Corner')], - 206: [Pot(76, 8, PotItem.SmallMagic, 'Ice Antechamber'), Pot(80, 8, PotItem.SmallMagic, 'Ice Antechamber'), Pot(108, 12, PotItem.Bomb, 'Ice Antechamber'), Pot(112, 12, PotItem.FiveArrows, 'Ice Antechamber'), - Pot(204, 11, PotItem.Hole, 'Ice Antechamber')], - 208: [Pot(158, 5, PotItem.SmallMagic, 'Tower Dark Maze'), Pot(140, 11, PotItem.OneRupee, 'Tower Dark Maze'), Pot(42, 13, PotItem.SmallMagic, 'Tower Dark Maze'), Pot(48, 16, PotItem.Heart, 'Tower Dark Maze'), - Pot(176, 20, PotItem.OneRupee, 'Tower Dark Maze'), Pot(146, 23, PotItem.FiveRupees, 'Tower Dark Maze'), Pot(12, 28, PotItem.Heart, 'Tower Dark Maze')], - 209: [Pot(48, 4, PotItem.BigMagic, 'Mire Conveyor Barrier'), Pot(168, 7, PotItem.OneRupee, 'Mire Conveyor Barrier'), Pot(76, 4, PotItem.OneRupee, 'Mire Neglected Room'), Pot(112, 4, PotItem.FiveArrows, 'Mire Neglected Room'), - Pot(76, 12, PotItem.Nothing, 'Mire Neglected Room'), Pot(112, 12, PotItem.OneRupee, 'Mire Neglected Room')], - 214: [Pot(92, 22, PotItem.BigMagic, 'TR Main Lobby'), Pot(96, 22, PotItem.Bomb, 'TR Main Lobby')], - 216: [Pot(202, 8, PotItem.Heart, 'Eastern Duo Eyegores'), Pot(242, 8, PotItem.FiveArrows, 'Eastern Duo Eyegores'), Pot(202, 10, PotItem.FiveArrows, 'Eastern Duo Eyegores'), Pot(242, 10, PotItem.FiveArrows, 'Eastern Duo Eyegores'), - Pot(202, 12, PotItem.FiveArrows, 'Eastern Duo Eyegores'), Pot(242, 12, PotItem.Heart, 'Eastern Duo Eyegores'), Pot(92, 24, PotItem.Heart, 'Eastern Single Eyegore'), Pot(96, 24, PotItem.FiveArrows, 'Eastern Single Eyegore')], - 217: [Pot(92, 20, PotItem.FiveArrows, 'Eastern False Switches'), Pot(92, 28, PotItem.Heart, 'Eastern False Switches')], - 218: [Pot(24, 23, PotItem.FiveArrows, 'Eastern Attic Start'), Pot(36, 23, PotItem.FiveArrows, 'Eastern Attic Start'), Pot(24, 25, PotItem.Switch, 'Eastern Attic Start'), Pot(36, 25, PotItem.Heart, 'Eastern Attic Start')], - 219: [Pot(12, 4, PotItem.Nothing, 'Thieves Lobby'), Pot(62, 19, PotItem.Nothing, 'Thieves Lobby'), Pot(112, 4, PotItem.FiveRupees, 'Thieves Lobby'), Pot(88, 16, PotItem.Heart, 'Thieves Lobby')], - 220: [Pot(56, 4, PotItem.FiveRupees, 'Thieves Compass Room'), Pot(112, 4, PotItem.Bomb, 'Thieves Compass Room'), Pot(68, 16, PotItem.Heart, 'Thieves Compass Room'), Pot(12, 28, PotItem.FiveArrows, 'Thieves Compass Room')], - 227: [Pot(88, 55, PotItem.Nothing, 'Bat Cave (right)'), Pot(100, 57, PotItem.OneRupee, 'Bat Cave (right)')], - 228: [Pot(32, 9, PotItem.FiveRupees, 'Old Man House'), Pot(112, 10, PotItem.OneRupee, 'Old Man House')], - 229: [Pot(48, 4, PotItem.OneRupee, 'Old Man House Back'), Pot(76, 4, PotItem.OneRupee, 'Old Man House Back'), Pot(112, 16, PotItem.OneRupee, 'Old Man House Back'), Pot(64, 18, PotItem.FiveRupees, 'Old Man House Back')], - 230: [Pot(108, 12, PotItem.FiveArrows, 'Death Mountain Return Cave'), Pot(88, 16, PotItem.Heart, 'Death Mountain Return Cave'), Pot(72, 20, PotItem.Nothing, 'Death Mountain Return Cave'), - Pot(56, 24, PotItem.OneRupee, 'Death Mountain Return Cave')], - 231: [Pot(68, 5, PotItem.OneRupee, 'Death Mountain Return Cave'), Pot(72, 5, PotItem.OneRupee, 'Death Mountain Return Cave')], - 232: [Pot(96, 4, PotItem.Heart, 'Superbunny Cave')], - 235: [Pot(206, 8, PotItem.FiveRupees, 'Bumper Cave'), Pot(210, 8, PotItem.FiveRupees, 'Bumper Cave'), Pot(88, 14, PotItem.SmallMagic, 'Bumper Cave'), Pot(92, 14, PotItem.Heart, 'Bumper Cave'), Pot(96, 14, PotItem.SmallMagic, 'Bumper Cave')], - 241: [Pot(64, 5, PotItem.Heart, 'Old Man Cave')], - 248: [Pot(242, 13, PotItem.BigMagic, 'Superbunny Cave')], - 253: [Pot(88, 6, PotItem.FiveRupees, 'Fairy Ascension Cave (Top)'), Pot(100, 6, PotItem.FiveRupees, 'Fairy Ascension Cave (Top)'), Pot(84, 23, PotItem.FiveRupees, 'Fairy Ascension Cave (Bottom)'), - Pot(84, 24, PotItem.FiveRupees, 'Fairy Ascension Cave (Bottom)')], - 255: [Pot(92, 8, PotItem.Heart, 'Paradox Cave'), Pot(96, 8, PotItem.Heart, 'Paradox Cave'), Pot(112, 28, PotItem.OneRupee, 'Paradox Cave Front')], - 257: [Pot(12, 20, PotItem.Heart, 'Snitch Lady (East)'), Pot(224, 19, PotItem.Chicken, 'Snitch Lady (West)'), Pot(228, 19, PotItem.Heart, 'Snitch Lady (West)')], - 258: [Pot(146, 19, PotItem.Heart, 'Sick Kids House'), Pot(150, 19, PotItem.Heart, 'Sick Kids House')], - 259: [Pot(140, 7, PotItem.Chicken, 'Tavern'), Pot(140, 9, PotItem.Nothing, 'Tavern'), Pot(12, 12, PotItem.Heart, 'Tavern (Front)')], - 260: [Pot(202, 21, PotItem.Heart, 'Links House'), Pot(202, 22, PotItem.Heart, 'Links House'), Pot(202, 23, PotItem.Heart, 'Links House')], - 261: [Pot(30, 20, PotItem.Heart, 'Sahasrahlas Hut'), Pot(28, 21, PotItem.Heart, 'Sahasrahlas Hut'), Pot(32, 21, PotItem.Heart, 'Sahasrahlas Hut')], - 263: [Pot(214, 23, PotItem.Bomb, 'Light World Bomb Hut'), Pot(222, 23, PotItem.FiveArrows, 'Light World Bomb Hut'), Pot(230, 23, PotItem.Bomb, 'Light World Bomb Hut'), Pot(214, 25, PotItem.OneRupee, 'Light World Bomb Hut'), - Pot(222, 25, PotItem.Nothing, 'Light World Bomb Hut'), Pot(230, 25, PotItem.OneRupee, 'Light World Bomb Hut'), Pot(214, 27, PotItem.Bomb, 'Light World Bomb Hut'), Pot(230, 27, PotItem.Bomb, 'Light World Bomb Hut')], - 264: [Pot(166, 19, PotItem.Chicken, 'Chicken House')], - 268: [Pot(88, 14, PotItem.Heart, 'Hookshot Fairy')], - 276: [Pot(92, 4, PotItem.Heart, 'Dark Desert Hint'), Pot(96, 4, PotItem.Heart, 'Dark Desert Hint'), Pot(92, 5, PotItem.Bomb, 'Dark Desert Hint'), Pot(96, 5, PotItem.Bomb, 'Dark Desert Hint'), Pot(92, 10, PotItem.FiveArrows, 'Dark Desert Hint'), - Pot(96, 10, PotItem.Heart, 'Dark Desert Hint')], - 279: [Pot(138, 3, PotItem.Heart, 'Spike Cave'), Pot(142, 3, PotItem.Heart, 'Spike Cave'), Pot(166, 3, PotItem.Heart, 'Spike Cave'), Pot(170, 3, PotItem.Heart, 'Spike Cave'), Pot(138, 4, PotItem.Heart, 'Spike Cave'), - Pot(142, 4, PotItem.Heart, 'Spike Cave'), Pot(166, 4, PotItem.Heart, 'Spike Cave'), Pot(170, 4, PotItem.Heart, 'Spike Cave')], - 281: [Pot(44, 28, PotItem.Heart, 'Blinds Hideout'), Pot(48, 28, PotItem.OneRupee, 'Blinds Hideout'), Pot(76, 28, PotItem.Heart, 'Blinds Hideout'), Pot(80, 28, PotItem.Heart, 'Blinds Hideout')], - 282: [Pot(214, 10, PotItem.Heart, 'Palace of Darkness Hint'), Pot(218, 10, PotItem.Heart, 'Palace of Darkness Hint'), Pot(226, 10, PotItem.Heart, 'Palace of Darkness Hint'), Pot(230, 10, PotItem.Heart, 'Palace of Darkness Hint')], - 283: [Pot(24, 53, PotItem.Nothing, 'Cave 45'), Pot(24, 54, PotItem.Heart, 'Cave 45'), Pot(32, 54, PotItem.Heart, 'Cave 45'), Pot(40, 54, PotItem.Heart, 'Cave 45'), Pot(24, 55, PotItem.Heart, 'Cave 45'), Pot(28, 56, PotItem.Heart, 'Cave 45'), - Pot(92, 22, PotItem.Bomb, 'Graveyard Cave'), Pot(96, 22, PotItem.Heart, 'Graveyard Cave'), Pot(92, 23, PotItem.Bomb, 'Graveyard Cave'), Pot(96, 23, PotItem.Heart, 'Graveyard Cave'), Pot(92, 24, PotItem.Bomb, 'Graveyard Cave'), - Pot(96, 24, PotItem.Heart, 'Graveyard Cave'), Pot(92, 25, PotItem.Bomb, 'Graveyard Cave'), Pot(96, 25, PotItem.Heart, 'Graveyard Cave')], - 285: [Pot(60, 6, PotItem.FiveRupees, 'Blinds Hideout'), Pot(64, 6, PotItem.FiveRupees, 'Blinds Hideout'), Pot(60, 7, PotItem.FiveRupees, 'Blinds Hideout'), Pot(64, 7, PotItem.FiveRupees, 'Blinds Hideout'), - Pot(60, 8, PotItem.FiveRupees, 'Blinds Hideout'), Pot(64, 8, PotItem.FiveRupees, 'Blinds Hideout')], - 287: [Pot(174, 28, PotItem.Heart, 'Lumberjack House'), Pot(178, 28, PotItem.Heart, 'Lumberjack House')], - 292: [Pot(20, 20, PotItem.FiveRupees, '50 Rupee Cave'), Pot(40, 20, PotItem.FiveRupees, '50 Rupee Cave'), Pot(20, 21, PotItem.FiveRupees, '50 Rupee Cave'), Pot(40, 21, PotItem.FiveRupees, '50 Rupee Cave'), - Pot(20, 22, PotItem.FiveRupees, '50 Rupee Cave'), Pot(40, 22, PotItem.FiveRupees, '50 Rupee Cave'), Pot(24, 24, PotItem.FiveRupees, '50 Rupee Cave'), Pot(28, 24, PotItem.FiveRupees, '50 Rupee Cave'), - Pot(32, 24, PotItem.FiveRupees, '50 Rupee Cave'), Pot(36, 24, PotItem.FiveRupees, '50 Rupee Cave')], - 293: [Pot(24, 25, PotItem.FiveRupees, '20 Rupee Cave'), Pot(28, 25, PotItem.FiveRupees, '20 Rupee Cave'), Pot(32, 25, PotItem.FiveRupees, '20 Rupee Cave'), Pot(36, 25, PotItem.FiveRupees, '20 Rupee Cave'), - Pot(88, 22, PotItem.Heart, 'Dev Cave Hint'), Pot(100, 22, PotItem.Heart, 'Dev Cave Hint'), Pot(88, 28, PotItem.Heart, 'Dev Cave Hint'), Pot(100, 28, PotItem.Heart, 'Dev Cave Hint')], - 295: [Pot(24, 25, PotItem.Nothing, 'Hammer Pegs Cave'), Pot(28, 25, PotItem.Nothing, 'Hammer Pegs Cave'), Pot(32, 25, PotItem.Nothing, 'Hammer Pegs Cave'), Pot(36, 25, PotItem.Nothing, 'Hammer Pegs Cave')], + 2: [Pot(80, 6, PotItem.Nothing, 'Sewers Yet More Rats', PotFlags.LowerRegion, obj=RoomObject(0x0A8B57, [0xA3, 0x33, 0xFA])), + Pot(80, 8, PotItem.Nothing, 'Sewers Yet More Rats', PotFlags.LowerRegion, obj=RoomObject(0x0A8B5A, [0xA3, 0x43, 0xFA])), + Pot(44, 8, PotItem.Nothing, 'Sewers Yet More Rats', PotFlags.LowerRegion, obj=RoomObject(0x0A8B5D, [0x5B, 0x43, 0xFA])), + Pot(44, 10, PotItem.Nothing, 'Sewers Yet More Rats', PotFlags.LowerRegion, obj=RoomObject(0x0A8B60, [0x5B, 0x53, 0xFA]))], + 4: [Pot(162, 25, PotItem.Nothing, 'TR Dash Room', obj=RoomObject(0x1FE220, [0x47, 0xCF, 0xFA])), + Pot(152, 25, PotItem.Nothing, 'TR Dash Room', obj=RoomObject(0x1FE21D, [0x33, 0xCF, 0xFA])), + Pot(152, 22, PotItem.Nothing, 'TR Dash Room', obj=RoomObject(0x1FE217, [0x33, 0xB7, 0xFA])), + Pot(162, 22, PotItem.Nothing, 'TR Dash Room', obj=RoomObject(0x1FE21A, [0x47, 0xB7, 0xFA])), + Pot(204, 19, PotItem.Bomb, 'TR Tongue Pull', obj=RoomObject(0x1FE241, [0x9B, 0x9F, 0xFA])), + Pot(240, 19, PotItem.Bomb, 'TR Tongue Pull', obj=RoomObject(0x1FE244, [0xE3, 0x9F, 0xFA]))], + 9: [Pot(12, 4, PotItem.OneRupee, 'PoD Shooter Room', obj=RoomObject(0x1FACEC, [0x1B, 0x23, 0xFA])), + Pot(48, 4, PotItem.Heart, 'PoD Shooter Room', obj=RoomObject(0x1FACEF, [0x63, 0x23, 0xFA])), + Pot(12, 12, PotItem.Switch, 'PoD Shooter Room', obj=RoomObject(0x1FACF5, [0x1B, 0x63, 0xFA]))], + 0xa: [Pot(96, 8, PotItem.Heart, 'PoD Stalfos Basement', obj=RoomObject(0x1FA6EC, [0xC3, 0x43, 0xFA])), + Pot(104, 8, PotItem.Heart, 'PoD Stalfos Basement', obj=RoomObject(0x1FA6F2, [0xD3, 0x43, 0xFA])), + Pot(204, 11, PotItem.Switch, 'PoD Stalfos Basement', obj=RoomObject(0x1FA6FB, [0x9B, 0x5F, 0xFA])), + Pot(100, 9, PotItem.Nothing, 'PoD Stalfos Basement', obj=RoomObject(0x1FA6EF, [0xCB, 0x4B, 0xFA])), + Pot(100, 7, PotItem.Nothing, 'PoD Stalfos Basement', obj=RoomObject(0x1FA6F5, [0xCB, 0x3B, 0xFA])), + Pot(156, 17, PotItem.Bomb, 'PoD Basement Ledge', PotFlags.SwitchLogicChange, obj=RoomObject(0x1FA6E6, [0x3B, 0x8F, 0xFA])), + Pot(160, 17, PotItem.FiveArrows, 'PoD Basement Ledge', PotFlags.SwitchLogicChange, obj=RoomObject(0x1FA6E9, [0x43, 0x8F, 0xFA]))], + 0xb: [Pot(202, 3, PotItem.Bomb, 'PoD Dark Pegs Left', obj=RoomObject(0x1FAB48, [0x97, 0x1F, 0xFA])), + Pot(202, 12, PotItem.Bomb, 'PoD Dark Pegs Left', obj=RoomObject(0x1FAB4E, [0x97, 0x67, 0xFA]))], + 0x11: [Pot(152, 19, PotItem.Nothing, 'Sewers Secret Room', obj=RoomObject(0x0A8C07, [0x33, 0x9F, 0xFA])), + Pot(152, 15, PotItem.Nothing, 'Sewers Secret Room', obj=RoomObject(0x0A8BF5, [0x33, 0x7F, 0xFA])), + Pot(144, 15, PotItem.Heart, 'Sewers Secret Room', obj=RoomObject(0x0A8BF2, [0x23, 0x7F, 0xFA])), + Pot(160, 15, PotItem.Heart, 'Sewers Secret Room', obj=RoomObject(0x0A8BF8, [0x43, 0x7F, 0xFA])), + Pot(144, 19, PotItem.Heart, 'Sewers Secret Room', obj=RoomObject(0x0A8C04, [0x23, 0x9F, 0xFA])), + Pot(160, 19, PotItem.Heart, 'Sewers Secret Room', obj=RoomObject(0x0A8C0A, [0x43, 0x9F, 0xFA]))], + 0x15: [Pot(96, 4, PotItem.Bomb, 'TR Pipe Pit', obj=RoomObject(0x1FE551, [0xC3, 0x23, 0xFA])), + Pot(100, 4, PotItem.SmallMagic, 'TR Pipe Pit', obj=RoomObject(0x1FE554, [0xCB, 0x23, 0xFA])), + Pot(104, 4, PotItem.Heart, 'TR Pipe Pit', obj=RoomObject(0x1FE557, [0xD3, 0x23, 0xFA])), + Pot(108, 4, PotItem.SmallMagic, 'TR Pipe Pit', obj=RoomObject(0x1FE55A, [0xDB, 0x23, 0xFA])), + Pot(112, 4, PotItem.FiveArrows, 'TR Pipe Pit', obj=RoomObject(0x1FE55D, [0xE3, 0x23, 0xFA])), + Pot(12, 6, PotItem.OneRupee, 'TR Pipe Pit', obj=RoomObject(0x1FE548, [0x1B, 0x33, 0xFA])), + Pot(16, 6, PotItem.FiveArrows, 'TR Pipe Pit', obj=RoomObject(0x1FE54B, [0x23, 0x33, 0xFA])), + Pot(20, 6, PotItem.FiveRupees, 'TR Pipe Pit', obj=RoomObject(0x1FE54E, [0x2B, 0x33, 0xFA])), + Pot(70, 11, PotItem.BigMagic, 'TR Pipe Ledge', obj=RoomObject(0x1FE545, [0x8F, 0x5B, 0xFA]))], + 0x16: [Pot(188, 3, PotItem.Heart, 'Swamp I', obj=RoomObject(0x1FA09C, [0x7B, 0x1F, 0xFA])), + Pot(192, 3, PotItem.Heart, 'Swamp I', obj=RoomObject(0x1FA09F, [0x83, 0x1F, 0xFA])), + Pot(188, 4, PotItem.SmallMagic, 'Swamp I', obj=RoomObject(0x1FA0A2, [0x7B, 0x27, 0xFA])), + Pot(192, 4, PotItem.SmallMagic, 'Swamp I', obj=RoomObject(0x1FA0A5, [0x83, 0x27, 0xFA])), + Pot(188, 5, PotItem.FiveArrows, 'Swamp I', obj=RoomObject(0x1FA0A8, [0x7B, 0x2F, 0xFA])), + Pot(192, 5, PotItem.FiveArrows, 'Swamp I', obj=RoomObject(0x1FA0AB, [0x83, 0x2F, 0xFA])), + Pot(188, 6, PotItem.Bomb, 'Swamp I', obj=RoomObject(0x1FA0AE, [0x7B, 0x37, 0xFA])), + Pot(192, 6, PotItem.Bomb, 'Swamp I', obj=RoomObject(0x1FA0B1, [0x83, 0x37, 0xFA])), + Pot(240, 19, PotItem.Key, 'Swamp Waterway', obj=RoomObject(0x1FA0D8, [0xE3, 0x9F, 0xFA]))], + 0x17: [Pot(100, 13, PotItem.Heart, 'Hera 5F Pot Block', obj=RoomObject(0x1FCCE0, [0xCB, 0x6B, 0xFA])), + Pot(100, 14, PotItem.Heart, 'Hera 5F Pot Block', obj=RoomObject(0x1FCCE3, [0xCB, 0x73, 0xFA])), + Pot(100, 15, PotItem.Heart, 'Hera 5F Pot Block', obj=RoomObject(0x1FCCE6, [0xCB, 0x7B, 0xFA])), + Pot(100, 16, PotItem.Heart, 'Hera 5F Pot Block', obj=RoomObject(0x1FCCE9, [0xCB, 0x83, 0xFA])), + Pot(100, 17, PotItem.Heart, 'Hera 5F Pot Block', obj=RoomObject(0x1FCCEC, [0xCB, 0x8B, 0xFA])), + Pot(100, 18, PotItem.Heart, 'Hera 5F Pot Block', obj=RoomObject(0x1FCCEF, [0xCB, 0x93, 0xFA])), + Pot(104, 13, PotItem.Heart, 'Hera 5F Pot Block', obj=RoomObject(0x1FCCF2, [0xD3, 0x6B, 0xFA])), + Pot(104, 14, PotItem.Heart, 'Hera 5F Pot Block', obj=RoomObject(0x1FCCF5, [0xD3, 0x73, 0xFA])), + Pot(104, 15, PotItem.Heart, 'Hera 5F Pot Block', obj=RoomObject(0x1FCCF8, [0xD3, 0x7B, 0xFA])), + Pot(104, 16, PotItem.Heart, 'Hera 5F Pot Block', obj=RoomObject(0x1FCCFB, [0xD3, 0x83, 0xFA])), + Pot(104, 17, PotItem.Heart, 'Hera 5F Pot Block', obj=RoomObject(0x1FCCFE, [0xD3, 0x8B, 0xFA])), + Pot(104, 18, PotItem.Heart, 'Hera 5F Pot Block', obj=RoomObject(0x1FCD01, [0xD3, 0x93, 0xFA]))], + 0x1A: [Pot(28, 5, PotItem.Bomb, 'PoD Falling Bridge Ledge', obj=RoomObject(0x1FA60D, [0x3B, 0x2B, 0xFA])), + Pot(32, 5, PotItem.Bomb, 'PoD Falling Bridge Ledge', obj=RoomObject(0x1FA610, [0x43, 0x2B, 0xFA])), + Pot(28, 27, PotItem.Bomb, 'PoD Falling Bridge', obj=RoomObject(0x1FA5F5, [0x3B, 0xDB, 0xFA])), + Pot(32, 27, PotItem.Bomb, 'PoD Falling Bridge', obj=RoomObject(0x1FA5F8, [0x43, 0xDB, 0xFA])), + Pot(232, 19, PotItem.Nothing, 'PoD Harmless Hellway', obj=RoomObject(0x1FA64C, [0xD3, 0x9F, 0xFA])), + Pot(212, 19, PotItem.Nothing, 'PoD Harmless Hellway', obj=RoomObject(0x1FA649, [0xAB, 0x9F, 0xFA]))], + 0x1B: [Pot(20, 23, PotItem.FiveArrows, 'PoD Mimics 2', obj=RoomObject(0x1FAAFE, [0x2B, 0xBB, 0xFA])), + Pot(40, 23, PotItem.FiveArrows, 'PoD Mimics 2', obj=RoomObject(0x1FAB01, [0x53, 0xBB, 0xFA]))], + 0x1E: [Pot(84, 9, PotItem.Bomb, 'Ice Bomb Drop', obj=RoomObject(0x1FC325, [0xAB, 0x4B, 0xFA]))], + 0x1F: [Pot(28, 25, PotItem.Switch, 'Ice Pengator Switch', obj=RoomObject(0x1FC38B, [0x3B, 0xCB, 0xFA])), + Pot(28, 23, PotItem.Nothing, 'Ice Pengator Switch', obj=RoomObject(0x1FC388, [0x3B, 0xBB, 0xFA])), + Pot(86, 26, PotItem.Nothing, 'Ice Big Key', obj=RoomObject(0x1FC397, [0xAF, 0xD3, 0xFA])), + Pot(86, 27, PotItem.Nothing, 'Ice Big Key', obj=RoomObject(0x1FC39A, [0xAF, 0xDB, 0xFA]))], + 0x21: [Pot(160, 20, PotItem.Nothing, 'Sewers Key Rat', obj=RoomObject(0x0A8C71, [0x43, 0xA7, 0xFA])), + Pot(168, 24, PotItem.SmallMagic, 'Sewers Key Rat', obj=RoomObject(0x0A8C7A, [0x53, 0xC7, 0xFA])), + Pot(48, 28, PotItem.Heart, 'Sewers Key Rat', obj=RoomObject(0x0A8C80, [0x63, 0xE3, 0xFA])), + Pot(82, 28, PotItem.SmallMagic, 'Sewers Key Rat', obj=RoomObject(0x0A8C7D, [0xA7, 0xE3, 0xFA])), + Pot(100, 28, PotItem.Nothing, 'Sewers Key Rat', obj=RoomObject(0x0A8C74, [0xCB, 0xE3, 0xFA])), + Pot(104, 28, PotItem.Nothing, 'Sewers Key Rat', obj=RoomObject(0x0A8C77, [0xD3, 0xE3, 0xFA]))], + 0x23: [Pot(86, 26, PotItem.OneRupee, 'TR Lazy Eyes', obj=RoomObject(0x1FED09, [0xAF, 0xD3, 0xFA])), + Pot(90, 26, PotItem.Heart, 'TR Lazy Eyes', obj=RoomObject(0x1FED0C, [0xB7, 0xD3, 0xFA])), + Pot(94, 26, PotItem.OneRupee, 'TR Lazy Eyes', obj=RoomObject(0x1FED0F, [0xBF, 0xD3, 0xFA])), + Pot(98, 26, PotItem.Bomb, 'TR Lazy Eyes', obj=RoomObject(0x1FED12, [0xC7, 0xD3, 0xFA])), + Pot(102, 26, PotItem.OneRupee, 'TR Lazy Eyes', obj=RoomObject(0x1FED15, [0xCF, 0xD3, 0xFA]))], + 0x24: [Pot(12, 4, PotItem.FiveRupees, 'TR Twin Pokeys', obj=RoomObject(0x1FE646, [0x1B, 0x23, 0xFA])), + Pot(48, 4, PotItem.Heart, 'TR Twin Pokeys', obj=RoomObject(0x1FE649, [0x63, 0x23, 0xFA])), + Pot(12, 12, PotItem.SmallMagic, 'TR Twin Pokeys', obj=RoomObject(0x1FE64C, [0x1B, 0x63, 0xFA])), + Pot(48, 12, PotItem.OneRupee, 'TR Twin Pokeys', obj=RoomObject(0x1FE64F, [0x63, 0x63, 0xFA]))], + 0x26: [Pot(28, 4, PotItem.Bomb, 'Swamp Shooters', obj=RoomObject(0x1F9BDD, [0x3B, 0x23, 0xFA])), + Pot(12, 8, PotItem.SmallMagic, 'Swamp Shooters', obj=RoomObject(0x1F9BDA, [0x1B, 0x43, 0xFA])), + Pot(150, 19, PotItem.Switch, 'Swamp Push Statue', obj=RoomObject(0x1F9C46, [0x2F, 0x9F, 0xFA])), + Pot(22, 26, PotItem.FiveRupees, 'Swamp Push Statue', obj=RoomObject(0x1F9C49, [0x2F, 0xD3, 0xFA])), + Pot(220, 26, PotItem.FiveArrows, 'Swamp Push Statue', PotFlags.SwitchLogicChange, obj=RoomObject(0x1F9C52, [0xBB, 0xD7, 0xFA]))], + 0x27: [Pot(214, 19, PotItem.Nothing, 'Hera 4F', obj=RoomObject(0x1FCE19, [0xAF, 0x9F, 0xFA])), + Pot(214, 20, PotItem.Nothing, 'Hera 4F', obj=RoomObject(0x1FCE1C, [0xAF, 0xA7, 0xFA])), + Pot(166, 20, PotItem.Bomb, 'Hera 4F', obj=RoomObject(0x1FCE28, [0x4F, 0xA7, 0xFA])), + Pot(214, 21, PotItem.Heart, 'Hera 4F', obj=RoomObject(0x1FCE1F, [0xAF, 0xAF, 0xFA])), + Pot(40, 28, PotItem.OneRupee, 'Hera 4F', obj=RoomObject(0x1FCE4C, [0x53, 0xE3, 0xFA])), + Pot(44, 28, PotItem.OneRupee, 'Hera 4F', obj=RoomObject(0x1FCE4F, [0x5B, 0xE3, 0xFA])), + Pot(80, 28, PotItem.FiveRupees, 'Hera 4F', obj=RoomObject(0x1FCE52, [0xA3, 0xE3, 0xFA])), + Pot(84, 28, PotItem.FiveRupees, 'Hera 4F', obj=RoomObject(0x1FCE55, [0xAB, 0xE3, 0xFA])), + Pot(102, 17, PotItem.Nothing, 'Hera 4F', obj=RoomObject(0x1FCE07, [0xCF, 0x8B, 0xFA])), + Pot(98, 17, PotItem.Nothing, 'Hera 4F', obj=RoomObject(0x1FCE04, [0xC7, 0x8B, 0xFA])), + Pot(106, 17, PotItem.Nothing, 'Hera 4F', obj=RoomObject(0x1FCE0A, [0xD7, 0x8B, 0xFA])), + Pot(166, 21, PotItem.Nothing, 'Hera 4F', obj=RoomObject(0x1FCE2B, [0x4F, 0xAF, 0xFA])), + Pot(166, 19, PotItem.Nothing, 'Hera 4F', obj=RoomObject(0x1FCE25, [0x4F, 0x9F, 0xFA])), + Pot(92, 12, PotItem.Nothing, 'Hera 4F', obj=RoomObject(0x1FCDF8, [0xBB, 0x63, 0xFA])), + Pot(160, 12, PotItem.Nothing, 'Hera 4F', obj=RoomObject(0x1FCDE0, [0x43, 0x67, 0xFA]))], + 0x2A: [Pot(80, 12, PotItem.OneRupee, 'PoD Arena Main', obj=RoomObject(0x1FA57F, [0xA3, 0x63, 0xFA])), + Pot(80, 19, PotItem.Heart, 'PoD Arena Main', obj=RoomObject(0x1FA582, [0xA3, 0x9B, 0xFA]))], + 0x2B: [Pot(16, 5, PotItem.Heart, 'PoD Sexy Statue', obj=RoomObject(0x1FAA14, [0x23, 0x2B, 0xFA])), + Pot(44, 5, PotItem.Switch, 'PoD Sexy Statue', obj=RoomObject(0x1FAA1D, [0x5B, 0x2B, 0xFA])), + Pot(16, 6, PotItem.Heart, 'PoD Sexy Statue', obj=RoomObject(0x1FAA17, [0x23, 0x33, 0xFA])), + Pot(44, 6, PotItem.Bomb, 'PoD Sexy Statue', obj=RoomObject(0x1FAA20, [0x5B, 0x33, 0xFA])), + Pot(16, 7, PotItem.Heart, 'PoD Sexy Statue', obj=RoomObject(0x1FAA1A, [0x23, 0x3B, 0xFA])), + Pot(44, 7, PotItem.Bomb, 'PoD Sexy Statue', obj=RoomObject(0x1FAA23, [0x5B, 0x3B, 0xFA])), + Pot(146, 21, PotItem.Bomb, 'PoD Map Balcony', obj=RoomObject(0x1FAA44, [0x27, 0xAF, 0xFA])), + Pot(170, 21, PotItem.FiveArrows, 'PoD Map Balcony', obj=RoomObject(0x1FAA4A, [0x57, 0xAF, 0xFA])), + Pot(146, 22, PotItem.Bomb, 'PoD Map Balcony', obj=RoomObject(0x1FAA47, [0x27, 0xB7, 0xFA])), + Pot(170, 22, PotItem.FiveArrows, 'PoD Map Balcony', obj=RoomObject(0x1FAA4D, [0x57, 0xB7, 0xFA]))], + 0x2C: [Pot(108, 24, PotItem.Heart, 'Hookshot Cave (Middle)', obj=RoomObject(0x0A889F, [0xDB, 0xC3, 0xFA])), + Pot(112, 24, PotItem.Heart, 'Hookshot Cave (Middle)', obj=RoomObject(0x0A88A2, [0xE3, 0xC3, 0xFA]))], + 0x2F: [Pot(28, 7, PotItem.Heart, 'Kakariko Well (back)', obj=RoomObject(0x0A8744, [0x3B, 0x3B, 0xFA])), + Pot(32, 7, PotItem.Heart, 'Kakariko Well (back)', obj=RoomObject(0x0A8747, [0x43, 0x3B, 0xFA])), + Pot(28, 9, PotItem.FiveRupees, 'Kakariko Well (back)', obj=RoomObject(0x0A874A, [0x3B, 0x4B, 0xFA])), + Pot(32, 9, PotItem.FiveRupees, 'Kakariko Well (back)', obj=RoomObject(0x0A874D, [0x43, 0x4B, 0xFA])), + Pot(172, 19, PotItem.FiveRupees, 'Kakariko Well (top)', obj=RoomObject(0x0A8762, [0x5B, 0x9F, 0xFA])), + Pot(180, 19, PotItem.FiveRupees, 'Kakariko Well (top)', obj=RoomObject(0x0A8765, [0x6B, 0x9F, 0xFA])), + Pot(104, 27, PotItem.Heart, 'Kakariko Well (bottom)', obj=RoomObject(0x0A8771, [0xD3, 0xDB, 0xFA])), + Pot(104, 28, PotItem.Heart, 'Kakariko Well (bottom)', obj=RoomObject(0x0A8774, [0xD3, 0xE3, 0xFA]))], + 0x31: [Pot(92, 28, PotItem.Bomb, 'Hera Beetles', obj=RoomObject(0x1FCF10, [0xBB, 0xE3, 0xFA])), + Pot(96, 28, PotItem.Nothing, 'Hera Beetles', obj=RoomObject(0x1FCF13, [0xC3, 0xE3, 0xFA]))], + 0x32: [Pot(28, 13, PotItem.SmallMagic, 'Sewers Dark Cross', obj=RoomObject(0x0A8E12, [0x3B, 0x6B, 0xFA]))], + 0x34: [Pot(78, 8, PotItem.FiveRupees, 'Swamp Barrier Ledge', obj=RoomObject(0x1F98A3, [0x9F, 0x43, 0xFA])), + Pot(92, 8, PotItem.FiveRupees, 'Swamp Barrier Ledge', obj=RoomObject(0x1F98A6, [0xBB, 0x43, 0xFA]))], + 0x35: [Pot(60, 6, PotItem.Key, 'Swamp Trench 2 Alcove', obj=RoomObject(0x1F979A, [0x7B, 0x33, 0xFA])), + Pot(20, 8, PotItem.FiveRupees, 'Swamp Big Key Ledge', obj=RoomObject(0x1F9725, [0x2B, 0x43, 0xFA])), + Pot(24, 8, PotItem.FiveRupees, 'Swamp Big Key Ledge', obj=RoomObject(0x1F9728, [0x33, 0x43, 0xFA])), + Pot(28, 8, PotItem.FiveRupees, 'Swamp Big Key Ledge', obj=RoomObject(0x1F972B, [0x3B, 0x43, 0xFA])), + Pot(32, 8, PotItem.FiveRupees, 'Swamp Big Key Ledge', obj=RoomObject(0x1F972E, [0x43, 0x43, 0xFA])), + Pot(36, 8, PotItem.FiveRupees, 'Swamp Big Key Ledge', obj=RoomObject(0x1F9731, [0x4B, 0x43, 0xFA])), + Pot(48, 20, PotItem.Heart, 'Swamp Trench 2 Departure', obj=RoomObject(0x1F9764, [0x63, 0xA3, 0xFA])), + Pot(76, 23, PotItem.Nothing, 'Swamp Trench 2 Pots', obj=RoomObject(0x1F976A, [0x9B, 0xBB, 0xFA])), + Pot(88, 23, PotItem.Nothing, 'Swamp Trench 2 Pots', obj=RoomObject(0x1F9776, [0xB3, 0xBB, 0xFA])), + Pot(100, 27, PotItem.Nothing, 'Swamp Trench 2 Pots', obj=RoomObject(0x1F9773, [0xCB, 0xDB, 0xFA])), + Pot(242, 28, PotItem.Nothing, 'Swamp Trench 2 Pots', obj=RoomObject(0x1F9779, [0xE7, 0xE7, 0xFA])), + Pot(240, 22, PotItem.Heart, 'Swamp Trench 2 Pots', obj=RoomObject(0x1F9770, [0xE3, 0xB7, 0xFA])), + Pot(76, 28, PotItem.Heart, 'Swamp Trench 2 Pots', obj=RoomObject(0x1F976D, [0x9B, 0xE3, 0xFA]))], + 0x36: [Pot(108, 4, PotItem.Bomb, 'Swamp Hub Dead Ledge', obj=RoomObject(0x1F9631, [0xDB, 0x23, 0xFA])), + Pot(112, 4, PotItem.FiveRupees, 'Swamp Hub Dead Ledge', obj=RoomObject(0x1F9634, [0xE3, 0x23, 0xFA])), + Pot(10, 16, PotItem.Heart, 'Swamp Hub Side Ledges', obj=RoomObject(0x1F9625, [0x17, 0x83, 0xFA])), + Pot(154, 15, PotItem.Nothing, 'Swamp Hub Side Ledges', obj=RoomObject(0x1F9628, [0x37, 0x7F, 0xFA])), + Pot(114, 16, PotItem.Key, 'Swamp Hub Side Ledges', obj=RoomObject(0x1F963A, [0xE7, 0x83, 0xFA])), + Pot(222, 15, PotItem.Nothing, 'Swamp Hub Side Ledges', obj=RoomObject(0x1F9637, [0xBF, 0x7F, 0xFA])), + Pot(188, 5, PotItem.Nothing, 'Swamp Hub North Ledge', obj=RoomObject(0x1F962B, [0x7B, 0x2F, 0xFA])), + Pot(192, 5, PotItem.Nothing, 'Swamp Hub North Ledge', obj=RoomObject(0x1F962E, [0x83, 0x2F, 0xFA]))], + 0x37: [Pot(60, 6, PotItem.Key, 'Swamp Trench 1 Alcove', obj=RoomObject(0x1F944A, [0x7B, 0x33, 0xFA])), + Pot(48, 20, PotItem.Nothing, 'Swamp Trench 1 Key Ledge', obj=RoomObject(0x1F9423, [0x63, 0xA3, 0xFA]))], + 0x38: [Pot(164, 12, PotItem.Bomb, 'Swamp Pot Row', obj=RoomObject(0x1F933A, [0x4B, 0x67, 0xFA])), + Pot(164, 13, PotItem.FiveRupees, 'Swamp Pot Row', obj=RoomObject(0x1F933D, [0x4B, 0x6F, 0xFA])), + Pot(164, 18, PotItem.Bomb, 'Swamp Pot Row', obj=RoomObject(0x1F9340, [0x4B, 0x97, 0xFA])), + Pot(164, 19, PotItem.Key, 'Swamp Pot Row', obj=RoomObject(0x1F9343, [0x4B, 0x9F, 0xFA]))], + 0x39: [Pot(12, 20, PotItem.Heart, 'Skull Spike Corner', obj=RoomObject(0x1FC14A, [0x1B, 0xA3, 0xFA])), + Pot(48, 28, PotItem.FiveArrows, 'Skull Spike Corner', obj=RoomObject(0x1FC153, [0x63, 0xE3, 0xFA])), + Pot(100, 22, PotItem.SmallMagic, 'Skull Final Drop', obj=RoomObject(0x1FC168, [0xCB, 0xB3, 0xFA])), + Pot(100, 26, PotItem.FiveArrows, 'Skull Final Drop', obj=RoomObject(0x1FC16B, [0xCB, 0xD3, 0xFA]))], + 0x3C: [Pot(24, 8, PotItem.SmallMagic, 'Hookshot Cave (Hook Islands)', obj=RoomObject(0x0A8979, [0x33, 0x43, 0xFA])), + Pot(64, 12, PotItem.FiveRupees, 'Hookshot Cave (Hook Islands)', obj=RoomObject(0x0A898E, [0x83, 0x63, 0xFA])), + Pot(20, 14, PotItem.OneRupee, 'Hookshot Cave (Hook Islands)', obj=RoomObject(0x0A897C, [0x2B, 0x73, 0xFA])), + Pot(20, 19, PotItem.Nothing, 'Hookshot Cave (Hook Islands)', obj=RoomObject(0x0A897F, [0x2B, 0x9B, 0xFA])), + Pot(68, 18, PotItem.FiveRupees, 'Hookshot Cave (Bonk Islands)', obj=RoomObject(0x0A8994, [0x8B, 0x93, 0xFA])), + Pot(96, 19, PotItem.Heart, 'Hookshot Cave (Front)', obj=RoomObject(0x0A8976, [0xC3, 0x9B, 0xFA])), + Pot(64, 20, PotItem.FiveRupees, 'Hookshot Cave (Bonk Islands)', obj=RoomObject(0x0A8997, [0x83, 0xA3, 0xFA])), + Pot(64, 26, PotItem.FiveRupees, 'Hookshot Cave (Bonk Islands)', obj=RoomObject(0x0A899A, [0x83, 0xD3, 0xFA]))], + 0x3D: [Pot(76, 12, PotItem.Bomb, 'GT Mini Helmasaur Room', obj=RoomObject(0x1FFC9B, [0x9B, 0x63, 0xFA])), + Pot(112, 12, PotItem.Bomb, 'GT Mini Helmasaur Room', obj=RoomObject(0x1FFC9E, [0xE3, 0x63, 0xFA])), + Pot(24, 22, PotItem.Heart, 'GT Crystal Inner Circle', obj=RoomObject(0x1FFCE3, [0x33, 0xB3, 0xFA])), + Pot(40, 22, PotItem.FiveArrows, 'GT Crystal Inner Circle', obj=RoomObject(0x1FFCE6, [0x53, 0xB3, 0xFA])), + Pot(32, 24, PotItem.Heart, 'GT Crystal Inner Circle', obj=RoomObject(0x1FFCF8, [0x43, 0xC3, 0xFA])), + Pot(20, 26, PotItem.FiveRupees, 'GT Crystal Inner Circle', obj=RoomObject(0x1FFCFB, [0x2B, 0xD3, 0xFA])), + Pot(36, 26, PotItem.BigMagic, 'GT Crystal Inner Circle', obj=RoomObject(0x1FFCFE, [0x4B, 0xD3, 0xFA]))], + 0x3E: [Pot(96, 6, PotItem.Bomb, 'Ice Stalfos Hint', obj=RoomObject(0x1FC41D, [0xC3, 0x33, 0xFA])), + Pot(100, 6, PotItem.SmallMagic, 'Ice Stalfos Hint', obj=RoomObject(0x1FC420, [0xCB, 0x33, 0xFA])), + Pot(88, 10, PotItem.Heart, 'Ice Stalfos Hint', obj=RoomObject(0x1FC429, [0xB3, 0x53, 0xFA])), + Pot(92, 10, PotItem.SmallMagic, 'Ice Stalfos Hint', obj=RoomObject(0x1FC42C, [0xBB, 0x53, 0xFA]))], + 0x3F: [Pot(12, 25, PotItem.OneRupee, 'Ice Hammer Block', obj=RoomObject(0x1FC4DC, [0x1B, 0xCB, 0xFA])), + Pot(20, 25, PotItem.OneRupee, 'Ice Hammer Block', obj=RoomObject(0x1FC4E5, [0x2B, 0xCB, 0xFA])), + Pot(12, 26, PotItem.Bomb, 'Ice Hammer Block', obj=RoomObject(0x1FC4DF, [0x1B, 0xD3, 0xFA])), + Pot(20, 26, PotItem.Bomb, 'Ice Hammer Block', obj=RoomObject(0x1FC4E8, [0x2B, 0xD3, 0xFA])), + Pot(12, 27, PotItem.Switch, 'Ice Hammer Block', obj=RoomObject(0x1FC4E2, [0x1B, 0xDB, 0xFA])), + Pot(20, 27, PotItem.Heart, 'Ice Hammer Block', obj=RoomObject(0x1FC4EB, [0x2B, 0xDB, 0xFA])), + Pot(28, 23, PotItem.Key, 'Ice Hammer Block', PotFlags.Block)], + 0x41: [Pot(100, 10, PotItem.Heart, 'Sewers Behind Tapestry', obj=RoomObject(0x0A8EDC, [0xCB, 0x53, 0xFA])), + Pot(52, 15, PotItem.OneRupee, 'Sewers Behind Tapestry', obj=RoomObject(0x0A8EDF, [0x6B, 0x7B, 0xFA])), + Pot(52, 16, PotItem.SmallMagic, 'Sewers Behind Tapestry', obj=RoomObject(0x0A8EE2, [0x6B, 0x83, 0xFA])), + Pot(148, 22, PotItem.SmallMagic, 'Sewers Behind Tapestry', obj=RoomObject(0x0A8EE5, [0x2B, 0xB7, 0xFA]))], + 0x43: [Pot(66, 4, PotItem.FiveArrows, 'Desert Wall Slide', obj=RoomObject(0x1F87BC, [0x87, 0x23, 0xFA])), + Pot(78, 4, PotItem.SmallMagic, 'Desert Wall Slide', obj=RoomObject(0x1F87BF, [0x9F, 0x23, 0xFA])), + Pot(66, 9, PotItem.Heart, 'Desert Wall Slide', obj=RoomObject(0x1F87C2, [0x87, 0x4B, 0xFA])), + Pot(78, 9, PotItem.Heart, 'Desert Wall Slide', obj=RoomObject(0x1F87C5, [0x9F, 0x4B, 0xFA])), + Pot(112, 28, PotItem.Nothing, 'Desert Tiles 2', obj=RoomObject(0x1F87DA, [0xE3, 0xE3, 0xFA])), + Pot(76, 28, PotItem.Nothing, 'Desert Tiles 2', obj=RoomObject(0x1F87D7, [0x9B, 0xE3, 0xFA])), + Pot(76, 20, PotItem.Nothing, 'Desert Tiles 2', obj=RoomObject(0x1F87D1, [0x9B, 0xA3, 0xFA])), + Pot(112, 20, PotItem.Key, 'Desert Tiles 2', obj=RoomObject(0x1F87D4, [0xE3, 0xA3, 0xFA]))], + 0x44: [Pot(204, 7, PotItem.Nothing, 'Thieves Conveyor Bridge', PotFlags.Block)], + 0x45: [Pot(12, 4, PotItem.FiveArrows, 'Thieves Basement Block', obj=RoomObject(0x1FDC2A, [0x1B, 0x23, 0xFA])), + Pot(48, 12, PotItem.FiveArrows, 'Thieves Basement Block', obj=RoomObject(0x1FDC2D, [0x63, 0x63, 0xFA])), + Pot(92, 11, PotItem.Nothing, "Thieves Blind's Cell Interior", obj=RoomObject(0x1FDC4E, [0xBB, 0x5B, 0xFA])), + Pot(108, 11, PotItem.Heart, "Thieves Blind's Cell Interior", obj=RoomObject(0x1FDC51, [0xDB, 0x5B, 0xFA])), + Pot(220, 16, PotItem.SmallMagic, "Thieves Blind's Cell Interior", obj=RoomObject(0x1FDC54, [0xBB, 0x87, 0xFA])), + Pot(236, 16, PotItem.Heart, "Thieves Blind's Cell Interior", obj=RoomObject(0x1FDC57, [0xDB, 0x87, 0xFA])), + Pot(0x9C, 7, PotItem.Nothing, 'Thieves Basement Block', PotFlags.Block)], + 0x46: [Pot(96, 5, PotItem.Heart, 'Swamp Donut Top', obj=RoomObject(0x1F9B91, [0xC3, 0x2B, 0xFA])), + Pot(28, 27, PotItem.Heart, 'Swamp Donut Bottom', obj=RoomObject(0x1F9B94, [0x3B, 0xDB, 0xFA]))], + 0x49: [Pot(104, 15, PotItem.SmallMagic, 'Skull Torch Room', obj=RoomObject(0x1FC102, [0xD3, 0x7B, 0xFA])), + Pot(104, 16, PotItem.SmallMagic, 'Skull Torch Room', obj=RoomObject(0x1FC105, [0xD3, 0x83, 0xFA])), + Pot(156, 27, PotItem.Nothing, 'Skull Star Pits', obj=RoomObject(0x1FC063, [0x3B, 0xDF, 0xFA])), + Pot(172, 24, PotItem.Nothing, 'Skull Star Pits', obj=RoomObject(0x1FC084, [0x5B, 0xC7, 0xFA])), + Pot(172, 23, PotItem.Nothing, 'Skull Star Pits', obj=RoomObject(0x1FC081, [0x5B, 0xBF, 0xFA])), + Pot(144, 20, PotItem.Nothing, 'Skull Star Pits', obj=RoomObject(0x1FC04E, [0x23, 0xA7, 0xFA])), + Pot(144, 19, PotItem.SmallMagic, 'Skull Star Pits', obj=RoomObject(0x1FC04B, [0x23, 0x9F, 0xFA])), + Pot(172, 20, PotItem.Heart, 'Skull Star Pits', obj=RoomObject(0x1FC07E, [0x5B, 0xA7, 0xFA])), + Pot(144, 27, PotItem.Heart, 'Skull Star Pits', obj=RoomObject(0x1FC051, [0x23, 0xDF, 0xFA])), + Pot(172, 28, PotItem.SmallMagic, 'Skull Star Pits', obj=RoomObject(0x1FC090, [0x5B, 0xE7, 0xFA])), + Pot(160, 27, PotItem.Nothing, 'Skull Star Pits', obj=RoomObject(0x1FC066, [0x43, 0xDF, 0xFA]))], + 0x4A: [Pot(14, 5, PotItem.Switch, 'PoD Left Cage', obj=RoomObject(0x1FA1C2, [0x1F, 0x2B, 0xFA])), + Pot(32, 5, PotItem.Bomb, 'PoD Left Cage', obj=RoomObject(0x1FA1C5, [0x43, 0x2B, 0xFA])), + Pot(14, 11, PotItem.Heart, 'PoD Left Cage', obj=RoomObject(0x1FA1C8, [0x1F, 0x5B, 0xFA])), + Pot(32, 11, PotItem.OneRupee, 'PoD Left Cage', obj=RoomObject(0x1FA1CB, [0x43, 0x5B, 0xFA])), + Pot(56, 8, PotItem.Bomb, 'PoD Middle Cage', obj=RoomObject(0x1FA1D1, [0x73, 0x43, 0xFA])), + Pot(68, 8, PotItem.Bomb, 'PoD Middle Cage', obj=RoomObject(0x1FA1D4, [0x8B, 0x43, 0xFA])), + Pot(92, 5, PotItem.Bomb, 'PoD Middle Cage', obj=RoomObject(0x1FA1DA, [0xBB, 0x2B, 0xFA])), + Pot(110, 5, PotItem.Switch, 'PoD Middle Cage', obj=RoomObject(0x1FA1DD, [0xDF, 0x2B, 0xFA])), + Pot(92, 11, PotItem.OneRupee, 'PoD Middle Cage', obj=RoomObject(0x1FA1E0, [0xBB, 0x5B, 0xFA])), + Pot(110, 11, PotItem.Heart, 'PoD Middle Cage', obj=RoomObject(0x1FA1E3, [0xDF, 0x5B, 0xFA]))], + 0x4B: [Pot(20, 6, PotItem.FiveArrows, 'PoD Mimics 1', obj=RoomObject(0x1FA83C, [0x2B, 0x33, 0xFA])), + Pot(40, 6, PotItem.Heart, 'PoD Mimics 1', obj=RoomObject(0x1FA83F, [0x53, 0x33, 0xFA]))], + 0x4E: [Pot(140, 7, PotItem.Nothing, 'Ice Bomb Jump Catwalk', obj=RoomObject(0x1FC57E, [0x1B, 0x3F, 0xFA])), + Pot(48, 10, PotItem.Nothing, 'Ice Bomb Jump Catwalk', obj=RoomObject(0x1FC587, [0x63, 0x53, 0xFA])), + Pot(140, 11, PotItem.Switch, 'Ice Bomb Jump Catwalk', obj=RoomObject(0x1FC581, [0x1B, 0x5F, 0xFA])), + Pot(28, 12, PotItem.Heart, 'Ice Bomb Jump Catwalk', obj=RoomObject(0x1FC584, [0x3B, 0x63, 0xFA])), + Pot(112, 12, PotItem.SmallMagic, 'Ice Narrow Corridor', obj=RoomObject(0x1FC58A, [0xE3, 0x63, 0xFA]))], + 0x50: [Pot(96, 0x6, PotItem.Heart, 'Hyrule Castle West Hall', PotFlags.LowerRegion, obj=RoomObject(0x0A9099, [0xC3, 0x33, 0xFA])), + Pot(100, 0x6, PotItem.Heart, 'Hyrule Castle West Hall', PotFlags.LowerRegion, obj=RoomObject(0x0A909C, [0xCB, 0x33, 0xFA]))], + 0x52: [Pot(138, 3, PotItem.Heart, 'Hyrule Castle East Hall', obj=RoomObject(0x0A91C7, [0x17, 0x1F, 0xFA])), + Pot(194, 26, PotItem.Heart, 'Hyrule Castle East Hall', obj=RoomObject(0x0A91CA, [0x87, 0xD7, 0xFA]))], + 0x53: [Pot(92, 11, PotItem.Heart, 'Desert Beamos Hall', obj=RoomObject(0x1F8844, [0xBB, 0x5B, 0xFA])), + Pot(96, 11, PotItem.SmallMagic, 'Desert Beamos Hall', obj=RoomObject(0x1F8847, [0xC3, 0x5B, 0xFA])), + Pot(100, 11, PotItem.Key, 'Desert Beamos Hall', obj=RoomObject(0x1F884A, [0xCB, 0x5B, 0xFA])), + Pot(104, 11, PotItem.Heart, 'Desert Beamos Hall', obj=RoomObject(0x1F884D, [0xD3, 0x5B, 0xFA]))], + 0x54: [Pot(186, 25, PotItem.FiveRupees, 'Swamp Attic', obj=RoomObject(0x1F9A28, [0x77, 0xCF, 0xFA])), + Pot(186, 26, PotItem.Heart, 'Swamp Attic', obj=RoomObject(0x1F9A2B, [0x77, 0xD7, 0xFA])), + Pot(186, 27, PotItem.Heart, 'Swamp Attic', obj=RoomObject(0x1F9A2E, [0x77, 0xDF, 0xFA])), + Pot(186, 28, PotItem.Heart, 'Swamp Attic', obj=RoomObject(0x1F9A31, [0x77, 0xE7, 0xFA]))], + 0x55: [Pot(230, 24, PotItem.SmallMagic, 'Hyrule Castle Secret Entrance', obj=RoomObject(0x0A8127, [0xCF, 0xC7, 0xFA])), + Pot(230, 25, PotItem.SmallMagic, 'Hyrule Castle Secret Entrance', obj=RoomObject(0x0A812A, [0xCF, 0xCF, 0xFA]))], + 0x56: [Pot(100, 6, PotItem.Nothing, 'Skull Back Drop', obj=RoomObject(0x1FBADC, [0xCB, 0x33, 0xFA])), + Pot(96, 10, PotItem.Nothing, 'Skull Back Drop', obj=RoomObject(0x1FBAEE, [0xC3, 0x53, 0xFA])), + Pot(92, 10, PotItem.Nothing, 'Skull Back Drop', obj=RoomObject(0x1FBAEB, [0xBB, 0x53, 0xFA])), + Pot(20, 6, PotItem.SmallMagic, 'Skull X Room', obj=RoomObject(0x1FBB1E, [0x2B, 0x33, 0xFA])), + Pot(40, 6, PotItem.SmallMagic, 'Skull X Room', obj=RoomObject(0x1FBB27, [0x53, 0x33, 0xFA])), + Pot(24, 7, PotItem.SmallMagic, 'Skull X Room', obj=RoomObject(0x1FBB21, [0x33, 0x3B, 0xFA])), + Pot(36, 7, PotItem.SmallMagic, 'Skull X Room', obj=RoomObject(0x1FBB24, [0x4B, 0x3B, 0xFA])), + Pot(12, 8, PotItem.Heart, 'Skull X Room', obj=RoomObject(0x1FBB12, [0x1B, 0x43, 0xFA])), + Pot(48, 8, PotItem.Heart, 'Skull X Room', obj=RoomObject(0x1FBB1B, [0x63, 0x43, 0xFA])), + Pot(24, 9, PotItem.SmallMagic, 'Skull X Room', obj=RoomObject(0x1FBB2A, [0x33, 0x4B, 0xFA])), + Pot(36, 9, PotItem.SmallMagic, 'Skull X Room', obj=RoomObject(0x1FBB30, [0x4B, 0x4B, 0xFA])), + Pot(20, 10, PotItem.FiveRupees, 'Skull X Room', obj=RoomObject(0x1FBB2D, [0x2B, 0x53, 0xFA])), + Pot(40, 10, PotItem.FiveRupees, 'Skull X Room', obj=RoomObject(0x1FBB33, [0x53, 0x53, 0xFA])), + Pot(12, 20, PotItem.Key, 'Skull 2 West Lobby', obj=RoomObject(0x1FBAC1, [0x1B, 0xA3, 0xFA])), + Pot(48, 20, PotItem.Nothing, 'Skull 2 West Lobby', obj=RoomObject(0x1FBAB8, [0x63, 0xA3, 0xFA]))], + 0x57: [Pot(92, 7, PotItem.BigMagic, 'Skull Lone Pot', obj=RoomObject(0x1FBB6F, [0xBB, 0x3B, 0xFA])), + Pot(32, 4, PotItem.Nothing, 'Skull Big Key', obj=RoomObject(0x1FBB72, [0x43, 0x23, 0xFA])), + Pot(92, 23, PotItem.Bomb, 'Skull Pot Prison', obj=RoomObject(0x1FBBB4, [0xBB, 0xBB, 0xFA])), + Pot(100, 23, PotItem.SmallMagic, 'Skull Pot Prison', obj=RoomObject(0x1FBBB7, [0xCB, 0xBB, 0xFA])), + Pot(84, 25, PotItem.FiveRupees, 'Skull Pot Prison', obj=RoomObject(0x1FBBB1, [0xAB, 0xCB, 0xFA])), + Pot(76, 27, PotItem.Heart, 'Skull Pot Prison', obj=RoomObject(0x1FBBAE, [0x9B, 0xDB, 0xFA])), + Pot(12, 20, PotItem.SmallMagic, 'Skull 2 East Lobby', obj=RoomObject(0x1FBB93, [0x1B, 0xA3, 0xFA])), + Pot(48, 20, PotItem.SmallMagic, 'Skull 2 East Lobby', obj=RoomObject(0x1FBB99, [0x63, 0xA3, 0xFA])), + Pot(30, 22, PotItem.Switch, 'Skull 2 East Lobby', obj=RoomObject(0x1FBB96, [0x3F, 0xB3, 0xFA]))], + 0x58: [Pot(12, 7, PotItem.SmallMagic, 'Skull Pull Switch', obj=RoomObject(0x1FBC4B, [0x1B, 0x3B, 0xFA])), + Pot(16, 7, PotItem.Nothing, 'Skull Pull Switch', obj=RoomObject(0x1FBC4E, [0x23, 0x3B, 0xFA])), + Pot(16, 8, PotItem.SmallMagic, 'Skull Pull Switch', obj=RoomObject(0x1FBC54, [0x23, 0x43, 0xFA])), + Pot(12, 12, PotItem.Nothing, 'Skull Pull Switch', obj=RoomObject(0x1FBC51, [0x1B, 0x63, 0xFA])), + Pot(96, 9, PotItem.Nothing, 'Skull Pot Circle', obj=RoomObject(0x1FBC90, [0xC3, 0x4B, 0xFA])), + Pot(92, 8, PotItem.Nothing, 'Skull Pot Circle', obj=RoomObject(0x1FBC8D, [0xBB, 0x43, 0xFA])), + Pot(108, 8, PotItem.Nothing, 'Skull Pot Circle', obj=RoomObject(0x1FBC9F, [0xDB, 0x43, 0xFA])), + Pot(108, 6, PotItem.Nothing, 'Skull Pot Circle', obj=RoomObject(0x1FBC99, [0xDB, 0x33, 0xFA])), + Pot(104, 5, PotItem.Nothing, 'Skull Pot Circle', obj=RoomObject(0x1FBC84, [0xD3, 0x2B, 0xFA])), + Pot(92, 6, PotItem.Nothing, 'Skull Pot Circle', obj=RoomObject(0x1FBC87, [0xBB, 0x33, 0xFA])), + Pot(96, 5, PotItem.Bomb, 'Skull Pot Circle', obj=RoomObject(0x1FBC7E, [0xC3, 0x2B, 0xFA])), + Pot(100, 5, PotItem.SmallMagic, 'Skull Pot Circle', obj=RoomObject(0x1FBC81, [0xCB, 0x2B, 0xFA])), + Pot(92, 7, PotItem.Heart, 'Skull Pot Circle', obj=RoomObject(0x1FBC8A, [0xBB, 0x3B, 0xFA])), + Pot(108, 7, PotItem.Heart, 'Skull Pot Circle', obj=RoomObject(0x1FBC9C, [0xDB, 0x3B, 0xFA])), + Pot(100, 9, PotItem.SmallMagic, 'Skull Pot Circle', obj=RoomObject(0x1FBC93, [0xCB, 0x4B, 0xFA])), + Pot(104, 9, PotItem.Bomb, 'Skull Pot Circle', obj=RoomObject(0x1FBC96, [0xD3, 0x4B, 0xFA]))], + 0x59: [Pot(26, 0xb, PotItem.Heart, 'Skull 3 Lobby', PotFlags.LowerRegion, obj=RoomObject(0x1FBFC0, [0x37, 0x5B, 0xFA])), + Pot(32, 8, PotItem.Nothing, 'Skull 3 Lobby', PotFlags.LowerRegion, obj=RoomObject(0x1FBFBD, [0x43, 0x43, 0xFA])), + Pot(76, 28, PotItem.Nothing, 'Skull East Bridge', obj=RoomObject(0x1FBF82, [0x9B, 0xE3, 0xFA])), + Pot(112, 28, PotItem.Nothing, 'Skull East Bridge', obj=RoomObject(0x1FBF85, [0xE3, 0xE3, 0xFA]))], + 0x5B: [Pot(218, 0x5, PotItem.Nothing, 'GT Hidden Spikes', PotFlags.LowerRegion, obj=RoomObject(0x1FF865, [0xB7, 0x2F, 0xFA])), + Pot(222, 0x5, PotItem.Switch, 'GT Hidden Spikes', PotFlags.LowerRegion, obj=RoomObject(0x1FF868, [0xBF, 0x2F, 0xFA])), + Pot(226, 0x5, PotItem.Nothing, 'GT Hidden Spikes', PotFlags.LowerRegion, obj=RoomObject(0x1FF86B, [0xC7, 0x2F, 0xFA]))], + 0x5C: [Pot(228, 25, PotItem.Nothing, 'GT Refill', obj=RoomObject(0x1FF964, [0xCB, 0xCF, 0xFA])), + Pot(104, 24, PotItem.Nothing, 'GT Refill', obj=RoomObject(0x1FF967, [0xD3, 0xC3, 0xFA])), + Pot(228, 22, PotItem.Nothing, 'GT Refill', obj=RoomObject(0x1FF96A, [0xCB, 0xB7, 0xFA])), + Pot(216, 25, PotItem.Nothing, 'GT Refill', obj=RoomObject(0x1FF95E, [0xB3, 0xCF, 0xFA])), + Pot(84, 24, PotItem.Nothing, 'GT Refill', obj=RoomObject(0x1FF95B, [0xAB, 0xC3, 0xFA])), + Pot(216, 22, PotItem.Nothing, 'GT Refill', obj=RoomObject(0x1FF958, [0xB3, 0xB7, 0xFA])), + Pot(94, 22, PotItem.Bomb, 'GT Refill', obj=RoomObject(0x1FF955, [0xBF, 0xB3, 0xFA])), + Pot(94, 26, PotItem.BigMagic, 'GT Refill', obj=RoomObject(0x1FF961, [0xBF, 0xD3, 0xFA]))], + 0x5D: [Pot(16, 5, PotItem.Bomb, 'GT Gauntlet 2', obj=RoomObject(0x1FF99F, [0x23, 0x2B, 0xFA])), + Pot(44, 5, PotItem.FiveRupees, 'GT Gauntlet 2', obj=RoomObject(0x1FF9A2, [0x5B, 0x2B, 0xFA])), + Pot(16, 11, PotItem.OneRupee, 'GT Gauntlet 2', obj=RoomObject(0x1FF9A5, [0x23, 0x5B, 0xFA])), + Pot(44, 11, PotItem.FiveArrows, 'GT Gauntlet 2', obj=RoomObject(0x1FF9A8, [0x5B, 0x5B, 0xFA])), + Pot(12, 20, PotItem.FiveArrows, 'GT Gauntlet 3', obj=RoomObject(0x1FF9C9, [0x1B, 0xA3, 0xFA])), + Pot(48, 20, PotItem.Bomb, 'GT Gauntlet 3', obj=RoomObject(0x1FF9CC, [0x63, 0xA3, 0xFA])), + Pot(12, 28, PotItem.SmallMagic, 'GT Gauntlet 3', obj=RoomObject(0x1FF9CF, [0x1B, 0xE3, 0xFA])), + Pot(48, 28, PotItem.Bomb, 'GT Gauntlet 3', obj=RoomObject(0x1FF9D2, [0x63, 0xE3, 0xFA]))], + 0x5E: [Pot(92, 4, PotItem.SmallMagic, 'Ice Falling Square', obj=RoomObject(0x1FC679, [0xBB, 0x23, 0xFA])), + Pot(96, 4, PotItem.SmallMagic, 'Ice Falling Square', obj=RoomObject(0x1FC67C, [0xC3, 0x23, 0xFA])), + Pot(76, 8, PotItem.Heart, 'Ice Falling Square', obj=RoomObject(0x1FC67F, [0x9B, 0x43, 0xFA])), + Pot(112, 8, PotItem.Heart, 'Ice Falling Square', obj=RoomObject(0x1FC688, [0xE3, 0x43, 0xFA]))], + 0x5F: [Pot(44, 27, PotItem.Switch, 'Ice Spike Room', obj=RoomObject(0x1FC6E8, [0x5B, 0xDB, 0xFA]))], + 0x60: [Pot(76, 4, PotItem.Heart, 'Hyrule Castle West Lobby', obj=RoomObject(0x0A92B2, [0x9B, 0x23, 0xFA])), + Pot(112, 4, PotItem.Heart, 'Hyrule Castle West Lobby', obj=RoomObject(0x0A92AF, [0xE3, 0x23, 0xFA]))], + 0x62: [Pot(208, 21, PotItem.Heart, 'Hyrule Castle East Lobby', obj=RoomObject(0x0A950E, [0xA3, 0xAF, 0xFA]))], + 0x63: [Pot(48, 4, PotItem.Nothing, 'Desert Tiles 1', obj=RoomObject(0x1F88C9, [0x63, 0x23, 0xFA])), + Pot(12, 4, PotItem.Nothing, 'Desert Tiles 1', obj=RoomObject(0x1F88C6, [0x1B, 0x23, 0xFA])), + Pot(12, 8, PotItem.Nothing, 'Desert Tiles 1', obj=RoomObject(0x1F88CC, [0x1B, 0x43, 0xFA])), + Pot(48, 12, PotItem.Nothing, 'Desert Tiles 1', obj=RoomObject(0x1F88D2, [0x63, 0x63, 0xFA])), + Pot(48, 8, PotItem.Heart, 'Desert Tiles 1', obj=RoomObject(0x1F88CF, [0x63, 0x43, 0xFA])), + Pot(12, 12, PotItem.Key, 'Desert Tiles 1', obj=RoomObject(0x1F88D5, [0x1B, 0x63, 0xFA]))], + 0x64: [Pot(12, 22, PotItem.Bomb, 'Thieves Attic Hint', PotFlags.SwitchLogicChange, obj=RoomObject(0x1FD9F9, [0x1B, 0xB3, 0xFA])), + Pot(16, 22, PotItem.Bomb, 'Thieves Attic Hint', PotFlags.SwitchLogicChange, obj=RoomObject(0x1FD9FC, [0x23, 0xB3, 0xFA])), + Pot(20, 22, PotItem.Bomb, 'Thieves Attic Hint', PotFlags.SwitchLogicChange, obj=RoomObject(0x1FD9FF, [0x2B, 0xB3, 0xFA])), + Pot(36, 28, PotItem.Bomb, 'Thieves Attic Switch', obj=RoomObject(0x1FDA1A, [0x4B, 0xE3, 0xFA])), + Pot(40, 28, PotItem.SmallMagic, 'Thieves Attic Switch', obj=RoomObject(0x1FDA1D, [0x53, 0xE3, 0xFA])), + Pot(44, 28, PotItem.SmallMagic, 'Thieves Attic Switch', obj=RoomObject(0x1FDA20, [0x5B, 0xE3, 0xFA])), + Pot(48, 28, PotItem.Switch, 'Thieves Attic Switch', obj=RoomObject(0x1FDA23, [0x63, 0xE3, 0xFA]))], + 0x65: [Pot(100, 28, PotItem.Bomb, 'Thieves Attic Window', obj=RoomObject(0x1FDA95, [0xCB, 0xE3, 0xFA])), + Pot(104, 28, PotItem.Bomb, 'Thieves Attic Window', obj=RoomObject(0x1FDA98, [0xD3, 0xE3, 0xFA]))], + 0x66: [Pot(48, 0x5, PotItem.FiveArrows, 'Swamp Refill', PotFlags.LowerRegion, obj=RoomObject(0x1F9F51, [0x63, 0x2B, 0xFA])), + Pot(52, 0x5, PotItem.Bomb, 'Swamp Refill', PotFlags.LowerRegion, obj=RoomObject(0x1F9F57, [0x6B, 0x2B, 0xFA])), + Pot(56, 0x5, PotItem.FiveRupees, 'Swamp Refill', PotFlags.LowerRegion, obj=RoomObject(0x1F9F5D, [0x73, 0x2B, 0xFA])), + Pot(48, 0x6, PotItem.FiveArrows, 'Swamp Refill', PotFlags.LowerRegion, RoomObject(0x1F9F54, [0x63, 0x33, 0xFA])), + Pot(52, 0x6, PotItem.Bomb, 'Swamp Refill', PotFlags.LowerRegion, obj=RoomObject(0x1F9F5A, [0x6B, 0x33, 0xFA])), + Pot(56, 0x6, PotItem.FiveRupees, 'Swamp Refill', PotFlags.LowerRegion, obj=RoomObject(0x1F9F60, [0x73, 0x33, 0xFA])), + Pot(84, 5, PotItem.Heart, 'Swamp Behind Waterfall', obj=RoomObject(0x1F9F07, [0xAB, 0x2B, 0xFA])), + Pot(104, 5, PotItem.FiveArrows, 'Swamp Behind Waterfall', obj=RoomObject(0x1F9F0D, [0xD3, 0x2B, 0xFA])), + Pot(84, 6, PotItem.Heart, 'Swamp Behind Waterfall', obj=RoomObject(0x1F9F0A, [0xAB, 0x33, 0xFA])), + Pot(104, 6, PotItem.Bomb, 'Swamp Behind Waterfall', obj=RoomObject(0x1F9F10, [0xD3, 0x33, 0xFA]))], + 0x67: [Pot(22, 26, PotItem.Nothing, 'Skull Left Drop', obj=RoomObject(0x1FBDDE, [0x2F, 0xD3, 0xFA])), + Pot(18, 22, PotItem.Nothing, 'Skull Left Drop', obj=RoomObject(0x1FBDD2, [0x27, 0xB3, 0xFA])), + Pot(12, 7, PotItem.FiveArrows, 'Skull Left Drop', obj=RoomObject(0x1FBDCC, [0x1B, 0x3B, 0xFA])), + Pot(48, 7, PotItem.SmallMagic, 'Skull Left Drop', obj=RoomObject(0x1FBDCF, [0x63, 0x3B, 0xFA])), + Pot(18, 23, PotItem.SmallMagic, 'Skull Left Drop', obj=RoomObject(0x1FBDD5, [0x27, 0xBB, 0xFA])), + Pot(18, 26, PotItem.Heart, 'Skull Left Drop', obj=RoomObject(0x1FBDDB, [0x27, 0xD3, 0xFA])), + Pot(96, 19, PotItem.Heart, 'Skull Compass Room', obj=RoomObject(0x1FBDE7, [0xC3, 0x9B, 0xFA])), + Pot(74, 20, PotItem.SmallMagic, 'Skull Compass Room', obj=RoomObject(0x1FBDEA, [0x97, 0xA3, 0xFA])), + Pot(92, 9, PotItem.Nothing, 'Skull Compass Room', obj=RoomObject(0x1FBDE1, [0xBB, 0x4B, 0xFA])), + Pot(84, 28, PotItem.Nothing, 'Skull Compass Room', obj=RoomObject(0x1FBDF0, [0xAB, 0xE3, 0xFA])), + Pot(104, 28, PotItem.Heart, 'Skull Compass Room', obj=RoomObject(0x1FBDF3, [0xD3, 0xE3, 0xFA]))], + 0x68: [Pot(84, 14, PotItem.Nothing, 'Skull Pinball', obj=RoomObject(0x1FBE8A, [0xAB, 0x73, 0xFA])), + Pot(84, 13, PotItem.Nothing, 'Skull Pinball', obj=RoomObject(0x1FBE87, [0xAB, 0x6B, 0xFA])), + Pot(88, 12, PotItem.Nothing, 'Skull Pinball', obj=RoomObject(0x1FBE84, [0xB3, 0x63, 0xFA])), + Pot(88, 6, PotItem.Nothing, 'Skull Pinball', obj=RoomObject(0x1FBE7E, [0xB3, 0x33, 0xFA])), + Pot(88, 5, PotItem.Nothing, 'Skull Pinball', obj=RoomObject(0x1FBE7B, [0xB3, 0x2B, 0xFA])), + Pot(88, 4, PotItem.Nothing, 'Skull Pinball', obj=RoomObject(0x1FBE78, [0xB3, 0x23, 0xFA])), + Pot(64, 17, PotItem.Nothing, 'Skull Pinball', obj=RoomObject(0x1FBEA5, [0x83, 0x8B, 0xFA])), + Pot(64, 15, PotItem.Nothing, 'Skull Pinball', obj=RoomObject(0x1FBE9F, [0x83, 0x7B, 0xFA])), + Pot(64, 7, PotItem.Heart, 'Skull Pinball', obj=RoomObject(0x1FBE75, [0x83, 0x3B, 0xFA])), + Pot(88, 7, PotItem.SmallMagic, 'Skull Pinball', obj=RoomObject(0x1FBE81, [0xB3, 0x3B, 0xFA])), + Pot(64, 16, PotItem.Heart, 'Skull Pinball', obj=RoomObject(0x1FBEA2, [0x83, 0x83, 0xFA])), + Pot(64, 24, PotItem.SmallMagic, 'Skull Pinball', obj=RoomObject(0x1FBEAB, [0x83, 0xC3, 0xFA])), + Pot(64, 25, PotItem.Heart, 'Skull Pinball', obj=RoomObject(0x1FBEAE, [0x83, 0xCB, 0xFA]))], + 0x6B: [Pot(28, 5, PotItem.Heart, 'GT Crystal Paths', obj=RoomObject(0x1FF7C1, [0x3B, 0x2B, 0xFA])), + Pot(44, 5, PotItem.Nothing, 'GT Crystal Paths', obj=RoomObject(0x1FF7C4, [0x5B, 0x2B, 0xFA])), + Pot(28, 8, PotItem.Nothing, 'GT Crystal Paths', obj=RoomObject(0x1FF7D0, [0x3B, 0x43, 0xFA])), + Pot(44, 8, PotItem.SmallMagic, 'GT Crystal Paths', obj=RoomObject(0x1FF7D3, [0x5B, 0x43, 0xFA])), + Pot(28, 11, PotItem.SmallMagic, 'GT Crystal Paths', obj=RoomObject(0x1FF7D6, [0x3B, 0x5B, 0xFA])), + Pot(44, 11, PotItem.Nothing, 'GT Crystal Paths', obj=RoomObject(0x1FF7D9, [0x5B, 0x5B, 0xFA])), + Pot(90, 25, PotItem.Nothing, 'GT Mimics 2', obj=RoomObject(0x1FF7FD, [0xB7, 0xCB, 0xFA])), + Pot(98, 25, PotItem.FiveArrows, 'GT Mimics 2', obj=RoomObject(0x1FF800, [0xC7, 0xCB, 0xFA]))], + 0x6C: [Pot(20, 6, PotItem.Heart, 'GT Quad Pot', obj=RoomObject(0x1FFA8E, [0x2B, 0x33, 0xFA])), + Pot(40, 6, PotItem.FiveArrows, 'GT Quad Pot', obj=RoomObject(0x1FFA91, [0x53, 0x33, 0xFA])), + Pot(20, 10, PotItem.Bomb, 'GT Quad Pot', obj=RoomObject(0x1FFA94, [0x2B, 0x53, 0xFA])), + Pot(40, 10, PotItem.SmallMagic, 'GT Quad Pot', obj=RoomObject(0x1FFA97, [0x53, 0x53, 0xFA]))], + 0x6D: [Pot(28, 26, PotItem.Heart, 'GT Gauntlet 5', obj=RoomObject(0x1FFA3C, [0x3B, 0xD3, 0xFA])), + Pot(32, 26, PotItem.Heart, 'GT Gauntlet 5', obj=RoomObject(0x1FFA3F, [0x43, 0xD3, 0xFA])), + Pot(28, 27, PotItem.SmallMagic, 'GT Gauntlet 5', obj=RoomObject(0x1FFA42, [0x3B, 0xDB, 0xFA])), + Pot(32, 27, PotItem.SmallMagic, 'GT Gauntlet 5', obj=RoomObject(0x1FFA45, [0x43, 0xDB, 0xFA]))], + 0x73: [Pot(154, 21, PotItem.FiveArrows, 'Desert Circle of Pots', obj=RoomObject(0x1F8933, [0x37, 0xAF, 0xFA])), + Pot(158, 21, PotItem.OneRupee, 'Desert Circle of Pots', obj=RoomObject(0x1F8936, [0x3F, 0xAF, 0xFA])), + Pot(20, 23, PotItem.Switch, 'Desert Circle of Pots', obj=RoomObject(0x1F8939, [0x2B, 0xBB, 0xFA])), + Pot(36, 23, PotItem.FiveRupees, 'Desert Circle of Pots', obj=RoomObject(0x1F894E, [0x4B, 0xBB, 0xFA])), + Pot(144, 24, PotItem.Heart, 'Desert Circle of Pots', obj=RoomObject(0x1F893C, [0x23, 0xC7, 0xFA])), + Pot(168, 24, PotItem.FiveArrows, 'Desert Circle of Pots', obj=RoomObject(0x1F894B, [0x53, 0xC7, 0xFA])), + Pot(20, 26, PotItem.SmallMagic, 'Desert Circle of Pots', obj=RoomObject(0x1F893F, [0x2B, 0xD3, 0xFA])), + Pot(36, 26, PotItem.Heart, 'Desert Circle of Pots', obj=RoomObject(0x1F8948, [0x4B, 0xD3, 0xFA])), + Pot(154, 27, PotItem.OneRupee, 'Desert Circle of Pots', obj=RoomObject(0x1F8942, [0x37, 0xDF, 0xFA])), + Pot(158, 27, PotItem.FiveRupees, 'Desert Circle of Pots', obj=RoomObject(0x1F8945, [0x3F, 0xDF, 0xFA]))], + 0x74: [Pot(30, 5, PotItem.SmallMagic, 'Desert Map Room', obj=RoomObject(0x1F8A39, [0x3F, 0x2B, 0xFA])), + Pot(62, 5, PotItem.Switch, 'Desert Map Room', obj=RoomObject(0x1F89FD, [0x7F, 0x2B, 0xFA])), + Pot(94, 5, PotItem.SmallMagic, 'Desert Map Room', obj=RoomObject(0x1F8A48, [0xBF, 0x2B, 0xFA])), + Pot(14, 11, PotItem.Heart, 'Desert Map Room', obj=RoomObject(0x1F8A3C, [0x1F, 0x5B, 0xFA])), + Pot(46, 11, PotItem.FiveArrows, 'Desert Map Room', obj=RoomObject(0x1F8A3F, [0x5F, 0x5B, 0xFA])), + Pot(78, 11, PotItem.FiveArrows, 'Desert Map Room', obj=RoomObject(0x1F8A42, [0x9F, 0x5B, 0xFA])), + Pot(110, 11, PotItem.Heart, 'Desert Map Room', obj=RoomObject(0x1F8A45, [0xDF, 0x5B, 0xFA]))], + 0x75: [Pot(148, 22, PotItem.SmallMagic, 'Desert Arrow Pot Corner', obj=RoomObject(0x1F8A89, [0x2B, 0xB7, 0xFA])), + Pot(160, 22, PotItem.FiveArrows, 'Desert Arrow Pot Corner', obj=RoomObject(0x1F8A8C, [0x43, 0xB7, 0xFA])), + Pot(172, 22, PotItem.Heart, 'Desert Arrow Pot Corner', obj=RoomObject(0x1F8A8F, [0x5B, 0xB7, 0xFA]))], + 0x76: [Pot(112, 12, PotItem.Heart, 'Swamp Drain Right', obj=RoomObject(0x1F9DCC, [0xE3, 0x63, 0xFA])), + Pot(84, 23, PotItem.Heart, 'Swamp Flooded Spot', obj=RoomObject(0x1F9DF3, [0xAB, 0xBB, 0xFA])), + Pot(96, 23, PotItem.Heart, 'Swamp Flooded Spot', obj=RoomObject(0x1F9DF6, [0xC3, 0xBB, 0xFA]))], + 0x7B: [Pot(48, 10, PotItem.Nothing, 'GT Conveyor Star Pits', obj=RoomObject(0x1FEF9B, [0x63, 0x53, 0xFA])), + Pot(88, 10, PotItem.Nothing, 'GT Conveyor Star Pits', obj=RoomObject(0x1FEFA1, [0xB3, 0x53, 0xFA])), + Pot(76, 7, PotItem.Nothing, 'GT Conveyor Star Pits', obj=RoomObject(0x1FEF9E, [0x9B, 0x3B, 0xFA])), + Pot(60, 4, PotItem.Heart, 'GT Conveyor Star Pits', obj=RoomObject(0x1FEFAD, [0x7B, 0x23, 0xFA])), + Pot(64, 4, PotItem.Key, 'GT Conveyor Star Pits', obj=RoomObject(0x1FEFB0, [0x83, 0x23, 0xFA]))], + 0x7C: [Pot(36, 21, PotItem.Nothing, 'GT Falling Bridge', obj=RoomObject(0x1FF0AA, [0x4B, 0xAB, 0xFA])), + Pot(24, 11, PotItem.Nothing, 'GT Falling Bridge', obj=RoomObject(0x1FF095, [0x33, 0x5B, 0xFA])), + Pot(28, 4, PotItem.Heart, 'GT Falling Bridge', obj=RoomObject(0x1FF08F, [0x3B, 0x23, 0xFA])), + Pot(32, 4, PotItem.Heart, 'GT Falling Bridge', obj=RoomObject(0x1FF092, [0x43, 0x23, 0xFA]))], + 0x7D: [Pot(44, 12, PotItem.Nothing, 'GT Firesnake Room', obj=RoomObject(0x1FF155, [0x5B, 0x63, 0xFA])), + Pot(44, 6, PotItem.Nothing, 'GT Firesnake Room', obj=RoomObject(0x1FF152, [0x5B, 0x33, 0xFA])), + Pot(112, 6, PotItem.Heart, 'GT Firesnake Room', obj=RoomObject(0x1FF16A, [0xE3, 0x33, 0xFA])), + Pot(108, 20, PotItem.FiveArrows, 'GT Warp Maze - Pot Rail', obj=RoomObject(0x1FF1EB, [0xDB, 0xA3, 0xFA])), + Pot(114, 20, PotItem.Bomb, 'GT Petting Zoo', obj=RoomObject(0x1FF1EE, [0xE7, 0xA3, 0xFA])), + Pot(76, 28, PotItem.Bomb, 'GT Petting Zoo', obj=RoomObject(0x1FF1F1, [0x9B, 0xE3, 0xFA]))], + 0x7E: [Pot(86, 15, PotItem.Heart, 'Ice Tall Hint', obj=RoomObject(0x1FC77E, [0xAF, 0x7B, 0xFA])), + Pot(82, 26, PotItem.SmallMagic, 'Ice Tall Hint', obj=RoomObject(0x1FC781, [0xA7, 0xD3, 0xFA])), + Pot(100, 26, PotItem.Switch, 'Ice Tall Hint', obj=RoomObject(0x1FC7A2, [0xCB, 0xD3, 0xFA])), + Pot(104, 26, PotItem.Nothing, 'Ice Tall Hint', obj=RoomObject(0x1FC7A5, [0xD3, 0xD3, 0xFA]))], + 0x80: [Pot(48, 4, PotItem.Heart, 'Hyrule Dungeon Cellblock', obj=RoomObject(0x0AA3CD, [0x63, 0x23, 0xFA])), + Pot(52, 4, PotItem.Heart, 'Hyrule Dungeon Cellblock', obj=RoomObject(0x0AA3D3, [0x6B, 0x23, 0xFA])), + Pot(56, 4, PotItem.Heart, 'Hyrule Dungeon Cellblock', obj=RoomObject(0x0AA3D6, [0x73, 0x23, 0xFA]))], + 0x82: [Pot(50, 0x5, PotItem.Nothing, 'Hyrule Dungeon South Abyss', PotFlags.LowerRegion, obj=RoomObject(0x0AA0D5, [0x67, 0x2B, 0xFA])), + Pot(50, 0xA, PotItem.Nothing, 'Hyrule Dungeon South Abyss', PotFlags.LowerRegion, obj=RoomObject(0x0AA0D8, [0x67, 0x53, 0xFA])), + Pot(76, 0x12, PotItem.Heart, 'Hyrule Dungeon South Abyss', PotFlags.LowerRegion, obj=RoomObject(0x0AA0D2, [0x9B, 0x93, 0xFA]))], + 0x83: [Pot(76, 4, PotItem.FiveArrows, 'Desert West Wing', obj=RoomObject(0x1F8B54, [0x9B, 0x23, 0xFA])), + Pot(80, 4, PotItem.OneRupee, 'Desert West Wing', obj=RoomObject(0x1F8B57, [0xA3, 0x23, 0xFA])), + Pot(76, 28, PotItem.FiveRupees, 'Desert West Wing', obj=RoomObject(0x1F8B5A, [0x9B, 0xE3, 0xFA])), + Pot(80, 28, PotItem.FiveArrows, 'Desert West Wing', obj=RoomObject(0x1F8B5D, [0xA3, 0xE3, 0xFA]))], + 0x84: [Pot(64, 17, PotItem.Nothing, 'Desert Main Lobby', obj=RoomObject(0x1F8C90, [0x83, 0x8B, 0xFA])), + Pot(60, 17, PotItem.Nothing, 'Desert Main Lobby', obj=RoomObject(0x1F8C8D, [0x7B, 0x8B, 0xFA])), + Pot(80, 14, PotItem.Nothing, 'Desert Main Lobby', obj=RoomObject(0x1F8C93, [0xA3, 0x73, 0xFA])), + Pot(44, 14, PotItem.Nothing, 'Desert Main Lobby', obj=RoomObject(0x1F8C96, [0x5B, 0x73, 0xFA])), + Pot(100, 6, PotItem.Nothing, 'Desert Main Lobby', obj=RoomObject(0x1F8C87, [0xCB, 0x33, 0xFA])), + Pot(24, 6, PotItem.Nothing, 'Desert Main Lobby', obj=RoomObject(0x1F8C81, [0x33, 0x33, 0xFA])), + Pot(24, 7, PotItem.FiveArrows, 'Desert Main Lobby', obj=RoomObject(0x1F8C84, [0x33, 0x3B, 0xFA])), + Pot(100, 7, PotItem.FiveArrows, 'Desert Main Lobby', obj=RoomObject(0x1F8C8A, [0xCB, 0x3B, 0xFA]))], + 0x85: [Pot(44, 28, PotItem.Heart, 'Desert East Wing', obj=RoomObject(0x1F8D59, [0x5B, 0xE3, 0xFA])), + Pot(48, 28, PotItem.FiveArrows, 'Desert East Wing', obj=RoomObject(0x1F8D5C, [0x63, 0xE3, 0xFA]))], + 0x87: [Pot(12, 11, PotItem.Nothing, 'Hera Tile Room', obj=RoomObject(0x1FD12A, [0x1B, 0x5B, 0xFA])), + Pot(16, 12, PotItem.Nothing, 'Hera Tile Room', obj=RoomObject(0x1FD130, [0x23, 0x63, 0xFA])), + Pot(40, 12, PotItem.Nothing, 'Hera Tile Room', obj=RoomObject(0x1FD139, [0x53, 0x63, 0xFA])), + Pot(32, 12, PotItem.Nothing, 'Hera Tile Room', obj=RoomObject(0x1FD136, [0x43, 0x63, 0xFA])), + Pot(24, 12, PotItem.Nothing, 'Hera Tile Room', obj=RoomObject(0x1FD133, [0x33, 0x63, 0xFA])), + Pot(16, 11, PotItem.Nothing, 'Hera Tile Room', obj=RoomObject(0x1FD12D, [0x23, 0x5B, 0xFA])), + Pot(76, 20, PotItem.SmallMagic, 'Hera Torches', obj=RoomObject(0x1FD18D, [0x9B, 0xA3, 0xFA])), + Pot(112, 20, PotItem.BigMagic, 'Hera Torches', obj=RoomObject(0x1FD190, [0xE3, 0xA3, 0xFA]))], + 0x8B: [Pot(76, 12, PotItem.Nothing, 'GT Conveyor Cross', obj=RoomObject(0x1FF2F7, [0x9B, 0x63, 0xFA])), + Pot(112, 12, PotItem.Key, 'GT Conveyor Cross', obj=RoomObject(0x1FF2FA, [0xE3, 0x63, 0xFA])), + Pot(32, 23, PotItem.Nothing, 'GT Hookshot South Platform', obj=RoomObject(0x1FF2C7, [0x43, 0xBB, 0xFA])), + Pot(28, 23, PotItem.Nothing, 'GT Hookshot South Platform', obj=RoomObject(0x1FF2C4, [0x3B, 0xBB, 0xFA])), + Pot(32, 9, PotItem.SmallMagic, 'GT Hookshot Mid Platform', obj=RoomObject(0x1FF2C1, [0x43, 0x4B, 0xFA])), + Pot(76, 20, PotItem.Nothing, 'GT Map Room', obj=RoomObject(0x1FF309, [0x9B, 0xA3, 0xFA])), + Pot(76, 28, PotItem.Heart, 'GT Map Room', obj=RoomObject(0x1FF30C, [0x9B, 0xE3, 0xFA]))], + 0x8C: [Pot(76, 12, PotItem.Switch, 'GT Hope Room', obj=RoomObject(0x1FF377, [0x9B, 0x63, 0xFA])), + Pot(112, 12, PotItem.SmallMagic, 'GT Hope Room', obj=RoomObject(0x1FF37A, [0xE3, 0x63, 0xFA])), + Pot(76, 20, PotItem.Bomb, "GT Bob's Room", obj=RoomObject(0x1FF3B9, [0x9B, 0xA3, 0xFA])), + Pot(92, 20, PotItem.Bomb, "GT Bob's Room", obj=RoomObject(0x1FF3BF, [0xBB, 0xA3, 0xFA])), + Pot(100, 21, PotItem.FiveArrows, "GT Bob's Room", obj=RoomObject(0x1FF3C2, [0xCB, 0xAB, 0xFA])), + Pot(104, 26, PotItem.Bomb, "GT Bob's Room", obj=RoomObject(0x1FF3E0, [0xD3, 0xD3, 0xFA])), + Pot(88, 27, PotItem.Bomb, "GT Bob's Room", obj=RoomObject(0x1FF3BC, [0xB3, 0xDB, 0xFA]))], + 0x8D: [Pot(204, 11, PotItem.Nothing, 'GT Speed Torch Upper', obj=RoomObject(0x1FF492, [0x9B, 0x5F, 0xFA])), + Pot(204, 14, PotItem.BigMagic, 'GT Speed Torch Upper', obj=RoomObject(0x1FF495, [0x9B, 0x77, 0xFA])), + Pot(28, 23, PotItem.Heart, 'GT Pots n Blocks', obj=RoomObject(0x1FF477, [0x3B, 0xBB, 0xFA])), + Pot(36, 23, PotItem.Heart, 'GT Pots n Blocks', obj=RoomObject(0x1FF47D, [0x4B, 0xBB, 0xFA])), + Pot(32, 24, PotItem.BigMagic, 'GT Pots n Blocks', obj=RoomObject(0x1FF47A, [0x43, 0xC3, 0xFA]))], + 0x8E: [Pot(80, 5, PotItem.FiveArrows, 'Ice Lonely Freezor', obj=RoomObject(0x1FC835, [0xA3, 0x2B, 0xFA])), + Pot(80, 6, PotItem.Nothing, 'Ice Lonely Freezor', obj=RoomObject(0x1FC838, [0xA3, 0x33, 0xFA]))], + 0x91: [Pot(84, 4, PotItem.Heart, 'Mire Falling Foes', obj=RoomObject(0x1FB9B0, [0xAB, 0x23, 0xFA])), + Pot(104, 4, PotItem.SmallMagic, 'Mire Falling Foes', obj=RoomObject(0x1FB9B3, [0xD3, 0x23, 0xFA]))], + 0x92: [Pot(86, 23, PotItem.Nothing, 'Mire Tall Dark and Roomy', obj=RoomObject(0x1FB966, [0xAF, 0xBB, 0xFA])), + Pot(92, 23, PotItem.Nothing, 'Mire Tall Dark and Roomy', obj=RoomObject(0x1FB969, [0xBB, 0xBB, 0xFA])), + Pot(98, 23, PotItem.Nothing, 'Mire Tall Dark and Roomy', obj=RoomObject(0x1FB96C, [0xC7, 0xBB, 0xFA])), + Pot(104, 23, PotItem.Nothing, 'Mire Tall Dark and Roomy', obj=RoomObject(0x1FB96F, [0xD3, 0xBB, 0xFA]))], + 0x93: [Pot(28, 7, PotItem.Switch, 'Mire Dark Shooters', obj=RoomObject(0x1FB85D, [0x3B, 0x3B, 0xFA])), + Pot(0x9C, 0x17, PotItem.Nothing, 'Mire Block X', PotFlags.Block), + Pot(96, 7, PotItem.Heart, 'Mire Dark Shooters', PotFlags.NoSwitch, obj=RoomObject(0x1FB860, [0xC3, 0x3B, 0xFA]))], + 0x96: [Pot(14, 18, PotItem.Nothing, 'GT Torch Cross', obj=RoomObject(0x1FFC69, [0x1F, 0x93, 0xFA])), + Pot(32, 5, PotItem.Nothing, 'GT Torch Cross', obj=RoomObject(0x1FFC5D, [0x43, 0x2B, 0xFA])), + Pot(46, 11, PotItem.Nothing, 'GT Torch Cross', obj=RoomObject(0x1FFC60, [0x5F, 0x5B, 0xFA])), + Pot(32, 17, PotItem.SmallMagic, 'GT Torch Cross', obj=RoomObject(0x1FFC63, [0x43, 0x8B, 0xFA])), + Pot(32, 24, PotItem.SmallMagic, 'GT Torch Cross', obj=RoomObject(0x1FFC6F, [0x43, 0xC3, 0xFA])), + Pot(14, 24, PotItem.Nothing, 'GT Torch Cross', obj=RoomObject(0x1FFC6C, [0x1F, 0xC3, 0xFA])), + Pot(76, 21, PotItem.Heart, 'GT Staredown', obj=RoomObject(0x1FFC0F, [0x9B, 0xAB, 0xFA])), + Pot(112, 21, PotItem.BigMagic, 'GT Staredown', obj=RoomObject(0x1FFC12, [0xE3, 0xAB, 0xFA]))], + 0x99: [Pot(40, 20, PotItem.SmallMagic, 'Eastern Darkness', obj=RoomObject(0x0A96F4, [0x53, 0xA3, 0xFA])), + Pot(84, 20, PotItem.Heart, 'Eastern Darkness', obj=RoomObject(0x0A96F7, [0xAB, 0xA3, 0xFA]))], + 0x9B: [Pot(48, 4, PotItem.SmallMagic, 'GT Double Switch Pot Corners', obj=RoomObject(0x1FF509, [0x63, 0x23, 0xFA])), + Pot(48, 12, PotItem.Key, 'GT Double Switch Pot Corners', obj=RoomObject(0x1FF50C, [0x63, 0x63, 0xFA])), + Pot(28, 24, PotItem.Nothing, 'GT Warp Maze - Mid Section', obj=RoomObject(0x1FF53C, [0x3B, 0xC3, 0xFA])), + Pot(32, 24, PotItem.Nothing, 'GT Warp Maze - Mid Section', obj=RoomObject(0x1FF53F, [0x43, 0xC3, 0xFA]))], + 0x9C: [Pot(56, 8, PotItem.SmallMagic, 'GT Invisible Catwalk', obj=RoomObject(0x1FF693, [0x73, 0x43, 0xFA])), + Pot(56, 9, PotItem.FiveArrows, 'GT Invisible Catwalk', obj=RoomObject(0x1FF696, [0x73, 0x4B, 0xFA]))], + 0x9D: [Pot(76, 4, PotItem.Bomb, 'GT Crystal Conveyor Left', obj=RoomObject(0x1FF6ED, [0x9B, 0x23, 0xFA])), + Pot(84, 4, PotItem.SmallMagic, 'GT Crystal Conveyor Left', obj=RoomObject(0x1FF6F0, [0xAB, 0x23, 0xFA])), + Pot(32, 7, PotItem.Nothing, 'GT Compass Room', obj=RoomObject(0x1FF6EA, [0x43, 0x3B, 0xFA])), + Pot(40, 9, PotItem.Nothing, 'GT Compass Room', obj=RoomObject(0x1FF6E7, [0x53, 0x4B, 0xFA]))], + 0x9F: [Pot(138, 20, PotItem.Nothing, 'Ice Many Pots', obj=RoomObject(0x1FC904, [0x17, 0xA7, 0xFA])), + Pot(138, 19, PotItem.Heart, 'Ice Many Pots', obj=RoomObject(0x1FC901, [0x17, 0x9F, 0xFA])), + Pot(178, 19, PotItem.Heart, 'Ice Many Pots', obj=RoomObject(0x1FC913, [0x67, 0x9F, 0xFA])), + Pot(40, 21, PotItem.Switch, 'Ice Many Pots', obj=RoomObject(0x1FC928, [0x53, 0xAB, 0xFA])), + Pot(138, 21, PotItem.Key, 'Ice Many Pots', obj=RoomObject(0x1FC907, [0x17, 0xAF, 0xFA])), + Pot(20, 27, PotItem.Heart, 'Ice Many Pots', obj=RoomObject(0x1FC92B, [0x2B, 0xDB, 0xFA])), + Pot(138, 27, PotItem.Heart, 'Ice Many Pots', obj=RoomObject(0x1FC90D, [0x17, 0xDF, 0xFA])), + Pot(178, 28, PotItem.Heart, 'Ice Many Pots', obj=RoomObject(0x1FC922, [0x67, 0xE7, 0xFA])), + Pot(178, 21, PotItem.Nothing, 'Ice Many Pots', obj=RoomObject(0x1FC919, [0x67, 0xAF, 0xFA])), + Pot(178, 20, PotItem.Nothing, 'Ice Many Pots', obj=RoomObject(0x1FC916, [0x67, 0xA7, 0xFA])), + Pot(40, 27, PotItem.Nothing, 'Ice Many Pots', obj=RoomObject(0x1FC92E, [0x53, 0xDB, 0xFA])), + Pot(178, 27, PotItem.Nothing, 'Ice Many Pots', obj=RoomObject(0x1FC91F, [0x67, 0xDF, 0xFA])), + Pot(178, 26, PotItem.Nothing, 'Ice Many Pots', obj=RoomObject(0x1FC91C, [0x67, 0xD7, 0xFA])), + Pot(138, 28, PotItem.Nothing, 'Ice Many Pots', obj=RoomObject(0x1FC910, [0x17, 0xE7, 0xFA])), + Pot(138, 26, PotItem.Nothing, 'Ice Many Pots', obj=RoomObject(0x1FC90A, [0x17, 0xD7, 0xFA])), + Pot(20, 21, PotItem.Nothing, 'Ice Many Pots', obj=RoomObject(0x1FC925, [0x2B, 0xAB, 0xFA]))], + 0xA1: [Pot(150, 6, PotItem.Key, 'Mire Fishbone', obj=RoomObject(0x1FB7F2, [0x2F, 0x37, 0xFA])), + Pot(100, 11, PotItem.SmallMagic, 'Mire Fishbone', obj=RoomObject(0x1FB80D, [0xCB, 0x5B, 0xFA])), + Pot(104, 12, PotItem.Heart, 'Mire Fishbone', obj=RoomObject(0x1FB810, [0xD3, 0x63, 0xFA])), + Pot(108, 13, PotItem.SmallMagic, 'Mire Fishbone', obj=RoomObject(0x1FB813, [0xDB, 0x6B, 0xFA])), + Pot(112, 14, PotItem.Heart, 'Mire Fishbone', obj=RoomObject(0x1FB816, [0xE3, 0x73, 0xFA])), + Pot(96, 27, PotItem.Nothing, 'Mire South Fish', obj=RoomObject(0x1FB804, [0xC3, 0xDB, 0xFA])), + Pot(92, 21, PotItem.Nothing, 'Mire South Fish', obj=RoomObject(0x1FB7FB, [0xBB, 0xAB, 0xFA])), + Pot(96, 23, PotItem.Heart, 'Mire South Fish', obj=RoomObject(0x1FB7FE, [0xC3, 0xBB, 0xFA])), + Pot(92, 25, PotItem.Nothing, 'Mire South Fish', obj=RoomObject(0x1FB801, [0xBB, 0xCB, 0xFA])), + Pot(76, 28, PotItem.Nothing, 'Mire South Fish', obj=RoomObject(0x1FB807, [0x9B, 0xE3, 0xFA])), + Pot(112, 28, PotItem.Nothing, 'Mire South Fish', obj=RoomObject(0x1FB80A, [0xE3, 0xE3, 0xFA]))], + 0xA2: [Pot(12, 28, PotItem.BigMagic, 'Mire Left Bridge', obj=RoomObject(0x1FB6B1, [0x1B, 0xE3, 0xFA]))], + 0xA8: [Pot(138, 28, PotItem.Nothing, 'Eastern Stalfos Spawn', obj=RoomObject(0x0A97BB, [0x17, 0xE7, 0xFA])), + Pot(178, 28, PotItem.Nothing, 'Eastern Stalfos Spawn', obj=RoomObject(0x0A97BE, [0x67, 0xE7, 0xFA])), + Pot(178, 19, PotItem.Nothing, 'Eastern Stalfos Spawn', obj=RoomObject(0x0A97B8, [0x67, 0x9F, 0xFA])), + Pot(138, 19, PotItem.Heart, 'Eastern Stalfos Spawn', obj=RoomObject(0x0A97B5, [0x17, 0x9F, 0xFA])), + Pot(30, 24, PotItem.OneRupee, 'Eastern Stalfos Spawn', obj=RoomObject(0x0A97C1, [0x3F, 0xC3, 0xFA]))], + 0xA9: [Pot(144, 0xB, PotItem.FiveArrows, 'Eastern Courtyard', PotFlags.LowerRegion, obj=RoomObject(0x0A9983, [0x23, 0x5F, 0xFA])), + Pot(236, 0xB, PotItem.FiveArrows, 'Eastern Courtyard', PotFlags.LowerRegion, obj=RoomObject(0x0A9989, [0xDB, 0x5F, 0xFA])), + Pot(144, 0xC, PotItem.FiveArrows, 'Eastern Courtyard', PotFlags.LowerRegion, obj=RoomObject(0x0A9986, [0x23, 0x67, 0xFA])), + Pot(236, 0xC, PotItem.Heart, 'Eastern Courtyard', PotFlags.LowerRegion, obj=RoomObject(0x0A998C, [0xDB, 0x67, 0xFA])), + Pot(12, 19, PotItem.Nothing, 'Eastern Courtyard Ledge', obj=RoomObject(0x0A994B, [0x1B, 0x9B, 0xFA])), + Pot(112, 19, PotItem.Nothing, 'Eastern Courtyard Ledge', obj=RoomObject(0x0A993C, [0xE3, 0x9B, 0xFA])), + Pot(16, 20, PotItem.Heart, 'Eastern Courtyard Ledge', obj=RoomObject(0x0A994E, [0x23, 0xA3, 0xFA])), + Pot(108, 20, PotItem.Heart, 'Eastern Courtyard Ledge', obj=RoomObject(0x0A9936, [0xDB, 0xA3, 0xFA]))], + 0xAA: [Pot(212, 10, PotItem.Nothing, 'Eastern Pot Switch', obj=RoomObject(0x0A9AA5, [0xAB, 0x57, 0xFA])), + Pot(232, 10, PotItem.Nothing, 'Eastern Pot Switch', obj=RoomObject(0x0A9AA8, [0xD3, 0x57, 0xFA])), + Pot(232, 5, PotItem.Nothing, 'Eastern Pot Switch', obj=RoomObject(0x0A9AA2, [0xD3, 0x2F, 0xFA])), + Pot(212, 5, PotItem.Heart, 'Eastern Pot Switch', obj=RoomObject(0x0A9A9F, [0xAB, 0x2F, 0xFA])), + Pot(94, 8, PotItem.Switch, 'Eastern Pot Switch', obj=RoomObject(0x0A9AAB, [0xBF, 0x43, 0xFA])), + Pot(108, 0x17, PotItem.Heart, 'Eastern Map Balcony', PotFlags.LowerRegion, obj=RoomObject(0x0A9AF5, [0xDB, 0xBB, 0xFA])), + Pot(108, 0x18, PotItem.Heart, 'Eastern Map Balcony', PotFlags.LowerRegion, obj=RoomObject(0x0A9AF8, [0xDB, 0xC3, 0xFA])), + Pot(108, 0x19, PotItem.Heart, 'Eastern Map Balcony', PotFlags.LowerRegion, obj=RoomObject(0x0A9AFB, [0xDB, 0xCB, 0xFA]))], + 0xAB: [Pot(20, 24, PotItem.Key, 'Thieves Spike Switch', obj=RoomObject(0x1FD99D, [0x2B, 0xC3, 0xFA]))], + 0xAE: [Pot(76, 12, PotItem.Switch, 'Iced T', obj=RoomObject(0x1FC95D, [0x9B, 0x63, 0xFA]))], + 0xB0: [Pot(20, 27, PotItem.Nothing, 'Tower Circle of Pots', obj=RoomObject(0x1F8F2C, [0x2B, 0xDB, 0xFA])), + Pot(24, 24, PotItem.Nothing, 'Tower Circle of Pots', obj=RoomObject(0x1F8F26, [0x33, 0xC3, 0xFA])), + Pot(44, 25, PotItem.Nothing, 'Tower Circle of Pots', obj=RoomObject(0x1F8F38, [0x5B, 0xCB, 0xFA])), + Pot(20, 21, PotItem.Bomb, 'Tower Circle of Pots', obj=RoomObject(0x1F8F14, [0x2B, 0xAB, 0xFA])), + Pot(28, 21, PotItem.OneRupee, 'Tower Circle of Pots', obj=RoomObject(0x1F8F17, [0x3B, 0xAB, 0xFA])), + Pot(32, 21, PotItem.FiveRupees, 'Tower Circle of Pots', obj=RoomObject(0x1F8F1A, [0x43, 0xAB, 0xFA])), + Pot(40, 21, PotItem.FiveArrows, 'Tower Circle of Pots', obj=RoomObject(0x1F8F1D, [0x53, 0xAB, 0xFA])), + Pot(16, 23, PotItem.FiveRupees, 'Tower Circle of Pots', obj=RoomObject(0x1F8F20, [0x23, 0xBB, 0xFA])), + Pot(44, 23, PotItem.OneRupee, 'Tower Circle of Pots', obj=RoomObject(0x1F8F3B, [0x5B, 0xBB, 0xFA])), + Pot(36, 24, PotItem.Heart, 'Tower Circle of Pots', obj=RoomObject(0x1F8F29, [0x4B, 0xC3, 0xFA])), + Pot(16, 25, PotItem.Heart, 'Tower Circle of Pots', obj=RoomObject(0x1F8F23, [0x23, 0xCB, 0xFA])), + Pot(28, 27, PotItem.FiveArrows, 'Tower Circle of Pots', obj=RoomObject(0x1F8F2F, [0x3B, 0xDB, 0xFA])), + Pot(40, 27, PotItem.Bomb, 'Tower Circle of Pots', obj=RoomObject(0x1F8F35, [0x53, 0xDB, 0xFA])), + Pot(32, 27, PotItem.Nothing, 'Tower Circle of Pots', obj=RoomObject(0x1F8F32, [0x43, 0xDB, 0xFA]))], + 0xB1: [Pot(76, 4, PotItem.Heart, 'Mire Spike Barrier', obj=RoomObject(0x1FB35A, [0x9B, 0x23, 0xFA])), + Pot(112, 4, PotItem.OneRupee, 'Mire Spike Barrier', obj=RoomObject(0x1FB35D, [0xE3, 0x23, 0xFA]))], + 0xB2: [Pot(48, 0x8, PotItem.OneRupee, 'Mire BK Door Room', PotFlags.LowerRegion, obj=RoomObject(0x1FB467, [0x63, 0x43, 0xFA])), + Pot(76, 0x8, PotItem.OneRupee, 'Mire BK Door Room', PotFlags.LowerRegion, obj=RoomObject(0x1FB470, [0x9B, 0x43, 0xFA])), + Pot(48, 0x9, PotItem.Nothing, 'Mire BK Door Room', PotFlags.LowerRegion, obj=RoomObject(0x1FB46A, [0x63, 0x4B, 0xFA])), + Pot(76, 0x9, PotItem.Heart, 'Mire BK Door Room', PotFlags.LowerRegion, obj=RoomObject(0x1FB473, [0x9B, 0x4B, 0xFA])), + Pot(48, 0xA, PotItem.Nothing, 'Mire BK Door Room', PotFlags.LowerRegion, obj=RoomObject(0x1FB46D, [0x63, 0x53, 0xFA])), + Pot(76, 0xA, PotItem.Nothing, 'Mire BK Door Room', PotFlags.LowerRegion, obj=RoomObject(0x1FB476, [0x9B, 0x53, 0xFA]))], + 0xB3: [Pot(12, 20, PotItem.Key, 'Mire Spikes', obj=RoomObject(0x1FB5A2, [0x1B, 0xA3, 0xFA])), + Pot(48, 20, PotItem.SmallMagic, 'Mire Spikes', obj=RoomObject(0x1FB5A5, [0x63, 0xA3, 0xFA])), + Pot(48, 28, PotItem.Switch, 'Mire Spikes', obj=RoomObject(0x1FB5C0, [0x63, 0xE3, 0xFA]))], + 0xB4: [Pot(44, 28, PotItem.BigMagic, 'TR Final Abyss Balcony', obj=RoomObject(0x1FE7B6, [0x5B, 0xE3, 0xFA])), + Pot(48, 28, PotItem.Heart, 'TR Final Abyss Balcony', obj=RoomObject(0x1FE7B9, [0x63, 0xE3, 0xFA]))], + 0xB5: [Pot(112, 4, PotItem.FiveRupees, 'TR Dark Ride Ledges', obj=RoomObject(0x1FEA78, [0xE3, 0x23, 0xFA])), + Pot(112, 15, PotItem.Heart, 'TR Dark Ride Ledges', obj=RoomObject(0x1FEAD5, [0xE3, 0x7B, 0xFA])), + Pot(76, 16, PotItem.Switch, 'TR Dark Ride Ledges', obj=RoomObject(0x1FEA93, [0x9B, 0x83, 0xFA])), + Pot(112, 16, PotItem.BigMagic, 'TR Dark Ride Ledges', obj=RoomObject(0x1FEAD8, [0xE3, 0x83, 0xFA])), + Pot(112, 17, PotItem.Heart, 'TR Dark Ride Ledges', obj=RoomObject(0x1FEADB, [0xE3, 0x8B, 0xFA])), + Pot(112, 28, PotItem.Bomb, 'TR Dark Ride Ledges', obj=RoomObject(0x1FEAF3, [0xE3, 0xE3, 0xFA]))], + 0xB6: [Pot(94, 9, PotItem.BigMagic, 'TR Refill', obj=RoomObject(0x1FDD29, [0xBF, 0x4B, 0xFA]))], + 0xB7: [Pot(30, 5, PotItem.SmallMagic, 'TR Roller Room', obj=RoomObject(0x1FDD76, [0x3F, 0x2B, 0xFA]))], + 0xB8: [Pot(96, 13, PotItem.Switch, 'Eastern Big Key', obj=RoomObject(0x0A9B66, [0xC3, 0x6B, 0xFA])), + Pot(88, 16, PotItem.Heart, 'Eastern Big Key', obj=RoomObject(0x0A9B60, [0xB3, 0x83, 0xFA])), + Pot(104, 16, PotItem.Heart, 'Eastern Big Key', obj=RoomObject(0x0A9B63, [0xD3, 0x83, 0xFA]))], + 0xB9: [Pot(92, 18, PotItem.OneRupee, 'Eastern Cannonball', obj=RoomObject(0x0A9C82, [0xBB, 0x93, 0xFA])), + Pot(96, 18, PotItem.FiveRupees, 'Eastern Cannonball', obj=RoomObject(0x0A9C85, [0xC3, 0x93, 0xFA])), + Pot(104, 18, PotItem.FiveRupees, 'Eastern Cannonball', obj=RoomObject(0x0A9C88, [0xD3, 0x93, 0xFA])), + Pot(108, 18, PotItem.OneRupee, 'Eastern Cannonball', obj=RoomObject(0x0A9C8B, [0xDB, 0x93, 0xFA]))], + 0xBA: [Pot(100, 8, PotItem.Nothing, 'Eastern Dark Pots', obj=RoomObject(0x0A9D3F, [0xCB, 0x43, 0xFA])), + Pot(88, 8, PotItem.Nothing, 'Eastern Dark Pots', obj=RoomObject(0x0A9D36, [0xB3, 0x43, 0xFA])), + Pot(94, 4, PotItem.OneRupee, 'Eastern Dark Pots', obj=RoomObject(0x0A9D39, [0xBF, 0x23, 0xFA])), + Pot(76, 6, PotItem.Heart, 'Eastern Dark Pots', obj=RoomObject(0x0A9D30, [0x9B, 0x33, 0xFA])), + Pot(112, 6, PotItem.Key, 'Eastern Dark Pots', obj=RoomObject(0x0A9D42, [0xE3, 0x33, 0xFA])), + Pot(76, 10, PotItem.Heart, 'Eastern Dark Pots', obj=RoomObject(0x0A9D33, [0x9B, 0x53, 0xFA])), + Pot(112, 10, PotItem.SmallMagic, 'Eastern Dark Pots', obj=RoomObject(0x0A9D45, [0xE3, 0x53, 0xFA])), + Pot(94, 12, PotItem.OneRupee, 'Eastern Dark Pots', obj=RoomObject(0x0A9D3C, [0xBF, 0x63, 0xFA]))], + 0xBC: [Pot(86, 4, PotItem.Heart, 'Thieves Hallway', obj=RoomObject(0x1FD941, [0xAF, 0x23, 0xFA])), + Pot(102, 4, PotItem.Key, 'Thieves Hallway', obj=RoomObject(0x1FD944, [0xCF, 0x23, 0xFA])), + Pot(138, 3, PotItem.Bomb, 'Thieves Conveyor Maze', obj=RoomObject(0x1FD8DB, [0x17, 0x1F, 0xFA])), + Pot(178, 3, PotItem.Switch, 'Thieves Conveyor Maze', obj=RoomObject(0x1FD8DE, [0x67, 0x1F, 0xFA])), + Pot(138, 12, PotItem.Heart, 'Thieves Conveyor Maze', obj=RoomObject(0x1FD8E1, [0x17, 0x67, 0xFA])), + Pot(178, 12, PotItem.Bomb, 'Thieves Conveyor Maze', obj=RoomObject(0x1FD8F3, [0x67, 0x67, 0xFA])), + Pot(12, 20, PotItem.Nothing, 'Thieves Pot Alcove Mid', obj=RoomObject(0x1FD8FF, [0x1B, 0xA3, 0xFA])), + Pot(48, 20, PotItem.Bomb, 'Thieves Pot Alcove Mid', obj=RoomObject(0x1FD902, [0x63, 0xA3, 0xFA])), + Pot(12, 28, PotItem.Bomb, 'Thieves Pot Alcove Mid', obj=RoomObject(0x1FD905, [0x1B, 0xE3, 0xFA])), + Pot(48, 28, PotItem.Bomb, 'Thieves Pot Alcove Mid', obj=RoomObject(0x1FD908, [0x63, 0xE3, 0xFA])), + Pot(28, 21, PotItem.FiveRupees, 'Thieves Pot Alcove Top', obj=RoomObject(0x1FD914, [0x3B, 0xAB, 0xFA])), + Pot(32, 21, PotItem.FiveRupees, 'Thieves Pot Alcove Top', obj=RoomObject(0x1FD917, [0x43, 0xAB, 0xFA])), + Pot(28, 27, PotItem.FiveRupees, 'Thieves Pot Alcove Bottom', obj=RoomObject(0x1FD923, [0x3B, 0xDB, 0xFA])), + Pot(32, 27, PotItem.FiveRupees, 'Thieves Pot Alcove Bottom', obj=RoomObject(0x1FD926, [0x43, 0xDB, 0xFA]))], + 0xBE: [Pot(92, 25, PotItem.Switch, 'Ice Switch Room', obj=RoomObject(0x1FC9E9, [0xBB, 0xCB, 0xFA]))], + 0xBF: [Pot(40, 20, PotItem.FiveArrows, 'Ice Refill', obj=RoomObject(0x1FCA56, [0x53, 0xA3, 0xFA])), + Pot(44, 20, PotItem.Heart, 'Ice Refill', obj=RoomObject(0x1FCA59, [0x5B, 0xA3, 0xFA])), + Pot(48, 20, PotItem.Bomb, 'Ice Refill', obj=RoomObject(0x1FCA5C, [0x63, 0xA3, 0xFA])), + Pot(40, 28, PotItem.SmallMagic, 'Ice Refill', obj=RoomObject(0x1FCA5F, [0x53, 0xE3, 0xFA])), + Pot(44, 28, PotItem.SmallMagic, 'Ice Refill', obj=RoomObject(0x1FCA62, [0x5B, 0xE3, 0xFA])), + Pot(48, 28, PotItem.SmallMagic, 'Ice Refill', obj=RoomObject(0x1FCA65, [0x63, 0xE3, 0xFA]))], + 0xC0: [Pot(48, 10, PotItem.Bomb, 'Tower Dark Pits', obj=RoomObject(0x1F8FE1, [0x63, 0x53, 0xFA])), + Pot(12, 14, PotItem.FiveRupees, 'Tower Dark Pits', obj=RoomObject(0x1F8FE7, [0x1B, 0x73, 0xFA])), + Pot(12, 26, PotItem.Heart, 'Tower Dark Pits', obj=RoomObject(0x1F8FF0, [0x1B, 0xD3, 0xFA])), + Pot(28, 27, PotItem.OneRupee, 'Tower Dark Pits', obj=RoomObject(0x1F8FF3, [0x3B, 0xDB, 0xFA]))], + 0xC2: [Pot(180, 7, PotItem.Switch, 'Mire Hub Switch', obj=RoomObject(0x1FB0C4, [0x6B, 0x3F, 0xFA])), + Pot(100, 0xE, PotItem.SmallMagic, 'Mire Hub Right', PotFlags.LowerRegion, RoomObject(0x1FB071, [0xCB, 0x73, 0xFA])), + Pot(68, 0x10, PotItem.OneRupee, 'Mire Hub', PotFlags.LowerRegion, RoomObject(0x1FB086, [0x8B, 0x83, 0xFA])), + Pot(64, 0x14, PotItem.FiveArrows, 'Mire Hub', PotFlags.LowerRegion, RoomObject(0x1FB089, [0x83, 0xA3, 0xFA]))], + 0xC4: [Pot(84, 9, PotItem.Bomb, 'TR Crystal Maze Interior', obj=RoomObject(0x1FEC06, [0xAB, 0x4B, 0xFA])), + Pot(24, 14, PotItem.Heart, 'TR Crystal Maze Interior', obj=RoomObject(0x1FEC09, [0x33, 0x73, 0xFA])), + Pot(56, 17, PotItem.FiveRupees, 'TR Crystal Maze Interior', obj=RoomObject(0x1FEC0C, [0x73, 0x8B, 0xFA])), + Pot(84, 17, PotItem.Bomb, 'TR Crystal Maze Interior', obj=RoomObject(0x1FEC0F, [0xAB, 0x8B, 0xFA])), + Pot(12, 21, PotItem.FiveArrows, 'TR Crystal Maze Interior', obj=RoomObject(0x1FEC15, [0x1B, 0xAB, 0xFA])), + Pot(76, 23, PotItem.OneRupee, 'TR Crystal Maze Interior', obj=RoomObject(0x1FEC12, [0x9B, 0xBB, 0xFA])), + Pot(48, 25, PotItem.SmallMagic, 'TR Crystal Maze Interior', obj=RoomObject(0x1FEC1B, [0x63, 0xCB, 0xFA])), + Pot(12, 26, PotItem.Heart, 'TR Crystal Maze Interior', obj=RoomObject(0x1FEC18, [0x1B, 0xD3, 0xFA]))], + 0xC6: [Pot(12, 7, PotItem.BigMagic, 'TR Hub Ledges', obj=RoomObject(0x1FDF50, [0x1B, 0x3B, 0xFA])), + Pot(12, 25, PotItem.Heart, 'TR Hub Ledges', obj=RoomObject(0x1FDF53, [0x1B, 0xCB, 0xFA]))], + 0xC7: [Pot(12, 10, PotItem.Heart, 'TR Torches', obj=RoomObject(0x1FE080, [0x1B, 0x53, 0xFA])), + Pot(12, 11, PotItem.BigMagic, 'TR Torches', obj=RoomObject(0x1FE083, [0x1B, 0x5B, 0xFA])), + Pot(12, 22, PotItem.SmallMagic, 'TR Torches Ledge', obj=RoomObject(0x1FE098, [0x1B, 0xB3, 0xFA])), + Pot(12, 28, PotItem.FiveArrows, 'TR Torches Ledge', obj=RoomObject(0x1FE095, [0x1B, 0xE3, 0xFA]))], + 0xC9: [Pot(30, 22, PotItem.OneRupee, 'Eastern Lobby', obj=RoomObject(0x0A9E30, [0x3F, 0xB3, 0xFA])), + Pot(94, 22, PotItem.OneRupee, 'Eastern Lobby', obj=RoomObject(0x0A9E36, [0xBF, 0xB3, 0xFA])), + Pot(60, 22, PotItem.Switch, 'Eastern Lobby', obj=RoomObject(0x0A9E33, [0x7B, 0xB3, 0xFA]))], + 0xCB: [Pot(80, 4, PotItem.Nothing, 'Thieves Ambush', obj=RoomObject(0x1FD59A, [0xA3, 0x23, 0xFA])), + Pot(80, 28, PotItem.Nothing, 'Thieves Ambush', obj=RoomObject(0x1FD5A3, [0xA3, 0xE3, 0xFA])), + Pot(88, 16, PotItem.Heart, 'Thieves Ambush', obj=RoomObject(0x1FD59D, [0xB3, 0x83, 0xFA])), + Pot(88, 28, PotItem.FiveRupees, 'Thieves Ambush', obj=RoomObject(0x1FD5A0, [0xB3, 0xE3, 0xFA]))], + 0xCC: [Pot(36, 4, PotItem.FiveRupees, 'Thieves Rail Ledge', obj=RoomObject(0x1FD6F0, [0x4B, 0x23, 0xFA])), + Pot(36, 28, PotItem.FiveRupees, 'Thieves Rail Ledge', obj=RoomObject(0x1FD6F3, [0x4B, 0xE3, 0xFA])), + Pot(112, 4, PotItem.Heart, 'Thieves BK Corner', obj=RoomObject(0x1FD6ED, [0xE3, 0x23, 0xFA])), + Pot(112, 28, PotItem.Bomb, 'Thieves BK Corner', obj=RoomObject(0x1FD6F6, [0xE3, 0xE3, 0xFA]))], + 0xCE: [Pot(76, 8, PotItem.SmallMagic, 'Ice Antechamber', obj=RoomObject(0x1FCAA7, [0x9B, 0x43, 0xFA])), + Pot(80, 8, PotItem.SmallMagic, 'Ice Antechamber', obj=RoomObject(0x1FCAAA, [0xA3, 0x43, 0xFA])), + Pot(108, 12, PotItem.Bomb, 'Ice Antechamber', obj=RoomObject(0x1FCAB9, [0xDB, 0x63, 0xFA])), + Pot(112, 12, PotItem.FiveArrows, 'Ice Antechamber', obj=RoomObject(0x1FCABC, [0xE3, 0x63, 0xFA])), + Pot(204, 11, PotItem.Hole, 'Ice Antechamber'), + Pot(0x6c, 8, PotItem.Nothing, 'Ice Antechamber', PotFlags.Block)], + 0xD0: [Pot(158, 5, PotItem.SmallMagic, 'Tower Dark Maze', obj=RoomObject(0x1F90A5, [0x3F, 0x2F, 0xFA])), + Pot(140, 11, PotItem.OneRupee, 'Tower Dark Maze', obj=RoomObject(0x1F90A8, [0x1B, 0x5F, 0xFA])), + Pot(42, 13, PotItem.SmallMagic, 'Tower Dark Maze', obj=RoomObject(0x1F90AE, [0x57, 0x6B, 0xFA])), + Pot(48, 16, PotItem.Heart, 'Tower Dark Maze', obj=RoomObject(0x1F90B1, [0x63, 0x83, 0xFA])), + Pot(176, 20, PotItem.OneRupee, 'Tower Dark Maze', obj=RoomObject(0x1F90B4, [0x63, 0xA7, 0xFA])), + Pot(146, 23, PotItem.FiveRupees, 'Tower Dark Maze', obj=RoomObject(0x1F90B7, [0x27, 0xBF, 0xFA])), + Pot(12, 28, PotItem.Heart, 'Tower Dark Maze', obj=RoomObject(0x1F90BA, [0x1B, 0xE3, 0xFA]))], + 0xD1: [Pot(48, 4, PotItem.BigMagic, 'Mire Conveyor Barrier', obj=RoomObject(0x1FB22C, [0x63, 0x23, 0xFA])), + Pot(168, 7, PotItem.OneRupee, 'Mire Conveyor Barrier', obj=RoomObject(0x1FB229, [0x53, 0x3F, 0xFA])), + Pot(76, 4, PotItem.OneRupee, 'Mire Neglected Room', obj=RoomObject(0x1FB23B, [0x9B, 0x23, 0xFA])), + Pot(112, 4, PotItem.FiveArrows, 'Mire Neglected Room', obj=RoomObject(0x1FB250, [0xE3, 0x23, 0xFA])), + Pot(76, 12, PotItem.Nothing, 'Mire Neglected Room', obj=RoomObject(0x1FB232, [0x9B, 0x63, 0xFA])), + Pot(112, 12, PotItem.OneRupee, 'Mire Neglected Room', obj=RoomObject(0x1FB235, [0xE3, 0x63, 0xFA]))], + 0xD6: [Pot(92, 22, PotItem.BigMagic, 'TR Main Lobby', obj=RoomObject(0x1FE0F9, [0xBB, 0xB3, 0xFA])), + Pot(96, 22, PotItem.Bomb, 'TR Main Lobby', obj=RoomObject(0x1FE0FC, [0xC3, 0xB3, 0xFA]))], + 0xD8: [Pot(202, 8, PotItem.Heart, 'Eastern Duo Eyegores', obj=RoomObject(0x0A95C0, [0x97, 0x47, 0xFA])), + Pot(242, 8, PotItem.FiveArrows, 'Eastern Duo Eyegores', obj=RoomObject(0x0A95CF, [0xE7, 0x47, 0xFA])), + Pot(202, 10, PotItem.FiveArrows, 'Eastern Duo Eyegores', obj=RoomObject(0x0A95C3, [0x97, 0x57, 0xFA])), + Pot(242, 10, PotItem.FiveArrows, 'Eastern Duo Eyegores', obj=RoomObject(0x0A95C9, [0xE7, 0x57, 0xFA])), + Pot(202, 12, PotItem.FiveArrows, 'Eastern Duo Eyegores', obj=RoomObject(0x0A95C6, [0x97, 0x67, 0xFA])), + Pot(242, 12, PotItem.Heart, 'Eastern Duo Eyegores', obj=RoomObject(0x0A95CC, [0xE7, 0x67, 0xFA])), + Pot(92, 24, PotItem.Heart, 'Eastern Single Eyegore', obj=RoomObject(0x0A95DB, [0xBB, 0xC3, 0xFA])), + Pot(96, 24, PotItem.FiveArrows, 'Eastern Single Eyegore', obj=RoomObject(0x0A95DE, [0xC3, 0xC3, 0xFA]))], + 0xD9: [Pot(92, 20, PotItem.FiveArrows, 'Eastern False Switches', obj=RoomObject(0x0A965A, [0xBB, 0xA3, 0xFA])), + Pot(92, 28, PotItem.Heart, 'Eastern False Switches', obj=RoomObject(0x0A965D, [0xBB, 0xE3, 0xFA]))], + 0xDA: [Pot(24, 23, PotItem.FiveArrows, 'Eastern Attic Start', obj=RoomObject(0x0A968B, [0x33, 0xBB, 0xFA])), + Pot(36, 23, PotItem.FiveArrows, 'Eastern Attic Start', obj=RoomObject(0x0A968E, [0x4B, 0xBB, 0xFA])), + Pot(24, 25, PotItem.Switch, 'Eastern Attic Start', obj=RoomObject(0x0A9691, [0x33, 0xCB, 0xFA])), + Pot(36, 25, PotItem.Heart, 'Eastern Attic Start', obj=RoomObject(0x0A9694, [0x4B, 0xCB, 0xFA]))], + 0xDB: [Pot(12, 4, PotItem.Nothing, 'Thieves Lobby', obj=RoomObject(0x1FD2C5, [0x1B, 0x23, 0xFA])), + Pot(62, 19, PotItem.Nothing, 'Thieves Lobby', PotFlags.LowerRegion, RoomObject(0x1FD300, [0x7F, 0x9B, 0xFA])), + Pot(112, 4, PotItem.FiveRupees, 'Thieves Lobby', obj=RoomObject(0x1FD2C8, [0xE3, 0x23, 0xFA])), + Pot(88, 16, PotItem.Heart, 'Thieves Lobby', obj=RoomObject(0x1FD2CB, [0xB3, 0x83, 0xFA]))], + 0xDC: [Pot(56, 4, PotItem.FiveRupees, 'Thieves Compass Room', obj=RoomObject(0x1FD447, [0x73, 0x23, 0xFA])), + Pot(112, 4, PotItem.Bomb, 'Thieves Compass Room', obj=RoomObject(0x1FD44A, [0xE3, 0x23, 0xFA])), + Pot(68, 16, PotItem.Heart, 'Thieves Compass Room', obj=RoomObject(0x1FD44D, [0x8B, 0x83, 0xFA])), + Pot(12, 28, PotItem.FiveArrows, 'Thieves Compass Room', obj=RoomObject(0x1FD453, [0x1B, 0xE3, 0xFA]))], + 0xE3: [Pot(88, 0x17, PotItem.Nothing, 'Bat Cave (right)', PotFlags.LowerRegion, RoomObject(0x0A82EE, [0xB3, 0xBB, 0xFA])), + Pot(100, 0x19, PotItem.OneRupee, 'Bat Cave (right)', PotFlags.LowerRegion, RoomObject(0x0A82F1, [0xCB, 0xCB, 0xFA]))], + 0xE4: [Pot(32, 9, PotItem.FiveRupees, 'Old Man House', obj=RoomObject(0x0AB42A, [0x43, 0x4B, 0xFA])), + Pot(112, 10, PotItem.OneRupee, 'Old Man House', obj=RoomObject(0x0AB46C, [0xE3, 0x53, 0xFA]))], + 0xE5: [Pot(48, 4, PotItem.OneRupee, 'Old Man House Back', obj=RoomObject(0x0AB545, [0x63, 0x23, 0xFA])), + Pot(76, 4, PotItem.OneRupee, 'Old Man House Back', obj=RoomObject(0x0AB548, [0x9B, 0x23, 0xFA])), + Pot(112, 16, PotItem.OneRupee, 'Old Man House Back', obj=RoomObject(0x0AB54B, [0xE3, 0x83, 0xFA])), + Pot(64, 18, PotItem.FiveRupees, 'Old Man House Back', obj=RoomObject(0x0AB54E, [0x83, 0x93, 0xFA]))], + 0xE6: [Pot(108, 12, PotItem.FiveArrows, 'Death Mountain Return Cave (left)', obj=RoomObject(0x0AB29D, [0xDB, 0x63, 0xFA])), + Pot(88, 16, PotItem.Heart, 'Death Mountain Return Cave (left)', obj=RoomObject(0x0AB2A0, [0xB3, 0x83, 0xFA])), + Pot(72, 20, PotItem.Nothing, 'Death Mountain Return Cave (left)', obj=RoomObject(0x0AB2A3, [0x93, 0xA3, 0xFA])), + Pot(56, 24, PotItem.OneRupee, 'Death Mountain Return Cave (left)', obj=RoomObject(0x0AB2A6, [0x73, 0xC3, 0xFA]))], + 0xE7: [Pot(68, 5, PotItem.OneRupee, 'Death Mountain Return Cave (right)', obj=RoomObject(0x0AB389, [0x8B, 0x2B, 0xFA])), + Pot(72, 5, PotItem.OneRupee, 'Death Mountain Return Cave (right)', obj=RoomObject(0x0AB38C, [0x93, 0x2B, 0xFA]))], + 0xE8: [Pot(96, 4, PotItem.Heart, 'Superbunny Cave (Bottom)', obj=RoomObject(0x0AA98E, [0xC3, 0x23, 0xFA]))], + 0xEB: [Pot(206, 8, PotItem.FiveRupees, 'Bumper Cave', obj=RoomObject(0x0AADE7, [0x9F, 0x47, 0xFA])), + Pot(210, 8, PotItem.FiveRupees, 'Bumper Cave', obj=RoomObject(0x0AADEA, [0xA7, 0x47, 0xFA])), + Pot(88, 14, PotItem.SmallMagic, 'Bumper Cave', obj=RoomObject(0x0AADED, [0xB3, 0x73, 0xFA])), + Pot(92, 14, PotItem.Heart, 'Bumper Cave', obj=RoomObject(0x0AADF0, [0xBB, 0x73, 0xFA])), + Pot(96, 14, PotItem.SmallMagic, 'Bumper Cave', obj=RoomObject(0x0AADF3, [0xC3, 0x73, 0xFA]))], + 0xF1: [Pot(64, 5, PotItem.Heart, 'Old Man Cave', obj=RoomObject(0x0AA6B2, [0x83, 0x2B, 0xFA]))], + 0xF3: [Pot(0x28, 0x14, PotItem.Nothing, 'Elder House', obj=RoomObject(0x0AA76F, [0x53, 0xA3, 0xFA])), + Pot(0x2C, 0x14, PotItem.Nothing, 'Elder House', obj=RoomObject(0x0AA772, [0x5B, 0xA3, 0xFA])), + Pot(0x30, 0x14, PotItem.Nothing, 'Elder House', obj=RoomObject(0x0AA775, [0x63, 0xA3, 0xFA]))], + 0xF8: [Pot(242, 13, PotItem.BigMagic, 'Superbunny Cave (Top)', obj=RoomObject(0x0AA8B9, [0xE7, 0x6F, 0xFA]))], + 0xFD: [Pot(88, 6, PotItem.FiveRupees, 'Fairy Ascension Cave (Top)', obj=RoomObject(0x0AAA51, [0xB3, 0x33, 0xFA])), + Pot(100, 6, PotItem.FiveRupees, 'Fairy Ascension Cave (Top)', obj=RoomObject(0x0AAA54, [0xCB, 0x33, 0xFA])), + Pot(84, 23, PotItem.FiveRupees, 'Fairy Ascension Cave (Bottom)', obj=RoomObject(0x0AAA57, [0xAB, 0xBB, 0xFA])), + Pot(84, 24, PotItem.FiveRupees, 'Fairy Ascension Cave (Bottom)', obj=RoomObject(0x0AAA5A, [0xAB, 0xC3, 0xFA]))], + 0xFF: [Pot(92, 8, PotItem.Heart, 'Paradox Cave Bomb Area', obj=RoomObject(0x0AAC5B, [0xBB, 0x43, 0xFA])), + Pot(96, 8, PotItem.Heart, 'Paradox Cave Bomb Area', obj=RoomObject(0x0AAC5E, [0xC3, 0x43, 0xFA])), + Pot(112, 28, PotItem.OneRupee, 'Paradox Cave Front', obj=RoomObject(0x0AAC6A, [0xE3, 0xE3, 0xFA]))], + 0x101: [Pot(12, 20, PotItem.Heart, 'Snitch Lady (East)', obj=RoomObject(0x03EBE5, [0x1B, 0xA3, 0xFA])), + Pot(224, 19, PotItem.Chicken, 'Snitch Lady (West)', obj=RoomObject(0x03EC1E, [0xC3, 0x9F, 0xFA])), + Pot(228, 19, PotItem.Heart, 'Snitch Lady (West)', obj=RoomObject(0x03EC21, [0xCB, 0x9F, 0xFA]))], + 0x102: [Pot(146, 19, PotItem.Heart, 'Sick Kids House', obj=RoomObject(0x03EC81, [0x27, 0x9F, 0xFA])), + Pot(150, 19, PotItem.Heart, 'Sick Kids House', obj=RoomObject(0x03EC84, [0x2F, 0x9F, 0xFA]))], + 0x103: [Pot(140, 7, PotItem.Chicken, 'Tavern', obj=RoomObject(0x03ECDA, [0x1B, 0x3F, 0xFA])), + Pot(140, 9, PotItem.Nothing, 'Tavern', obj=RoomObject(0x03ECDD, [0x1B, 0x4F, 0xFA])), + Pot(12, 12, PotItem.Heart, 'Tavern (Front)', obj=RoomObject(0x03ECE6, [0x1B, 0x63, 0xFA]))], + 0x104: [Pot(202, 21, PotItem.Heart, 'Links House', obj=RoomObject(0x0A8017, [0x97, 0xAF, 0xFA])), + Pot(202, 22, PotItem.Heart, 'Links House', obj=RoomObject(0x0A801A, [0x97, 0xB7, 0xFA])), + Pot(202, 23, PotItem.Heart, 'Links House', obj=RoomObject(0x0A801D, [0x97, 0xBF, 0xFA]))], + 0x105: [Pot(30, 20, PotItem.Heart, 'Sahasrahlas Hut', obj=RoomObject(0x03EDF2, [0x3F, 0xA3, 0xFA])), + Pot(28, 21, PotItem.Heart, 'Sahasrahlas Hut', obj=RoomObject(0x03EDEC, [0x3B, 0xAB, 0xFA])), + Pot(32, 21, PotItem.Heart, 'Sahasrahlas Hut', obj=RoomObject(0x03EDEF, [0x43, 0xAB, 0xFA]))], + 0x106: [Pot(0x6c, 0x1b, PotItem.Nothing, 'Brewery', obj=RoomObject(0x03EEB0, [0xDB, 0xDB, 0xFA]))], + 0x107: [Pot(214, 23, PotItem.Bomb, 'Light World Bomb Hut', obj=RoomObject(0x03EF49, [0xAF, 0xBF, 0xFA])), + Pot(222, 23, PotItem.FiveArrows, 'Light World Bomb Hut', obj=RoomObject(0x03EF4C, [0xBF, 0xBF, 0xFA])), + Pot(230, 23, PotItem.Bomb, 'Light World Bomb Hut', obj=RoomObject(0x03EF4F, [0xCF, 0xBF, 0xFA])), + Pot(214, 25, PotItem.OneRupee, 'Light World Bomb Hut', obj=RoomObject(0x03EF52, [0xAF, 0xCF, 0xFA])), + Pot(222, 25, PotItem.Nothing, 'Light World Bomb Hut', obj=RoomObject(0x03EF55, [0xBF, 0xCF, 0xFA])), + Pot(230, 25, PotItem.OneRupee, 'Light World Bomb Hut', obj=RoomObject(0x03EF58, [0xCF, 0xCF, 0xFA])), + Pot(214, 27, PotItem.Bomb, 'Light World Bomb Hut', obj=RoomObject(0x03EF5B, [0xAF, 0xDF, 0xFA])), + Pot(230, 27, PotItem.Bomb, 'Light World Bomb Hut', obj=RoomObject(0x03EF5E, [0xCF, 0xDF, 0xFA]))], + 0x108: [Pot(166, 19, PotItem.Chicken, 'Chicken House', obj=RoomObject(0x03EFA9, [0x4F, 0x9F, 0xFA]))], + 0x10C: [Pot(88, 14, PotItem.Heart, 'Hookshot Fairy', obj=RoomObject(0x03F329, [0xB3, 0x73, 0xFA]))], + 0x114: [Pot(92, 4, PotItem.Heart, 'Dark Desert Hint', obj=RoomObject(0x03F7A0, [0xBB, 0x23, 0xFA])), + Pot(96, 4, PotItem.Heart, 'Dark Desert Hint', obj=RoomObject(0x03F7A3, [0xC3, 0x23, 0xFA])), + Pot(92, 5, PotItem.Bomb, 'Dark Desert Hint', obj=RoomObject(0x03F7A6, [0xBB, 0x2B, 0xFA])), + Pot(96, 5, PotItem.Bomb, 'Dark Desert Hint', obj=RoomObject(0x03F7A9, [0xC3, 0x2B, 0xFA])), + Pot(92, 10, PotItem.FiveArrows, 'Dark Desert Hint', obj=RoomObject(0x03F7AC, [0xBB, 0x53, 0xFA])), + Pot(96, 10, PotItem.Heart, 'Dark Desert Hint', obj=RoomObject(0x03F7AF, [0xC3, 0x53, 0xFA]))], + 0x117: [Pot(138, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCB2, [0x17, 0x1F, 0xFA])), # 0x38A -> 38A + Pot(142, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCB8, [0x1F, 0x1F, 0xFA])), + Pot(166, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCC1, [0x4F, 0x1F, 0xFA])), + Pot(170, 3, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCC7, [0x57, 0x1F, 0xFA])), + Pot(138, 4, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCB5, [0x17, 0x27, 0xFA])), + Pot(142, 4, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCBB, [0x1F, 0x27, 0xFA])), + Pot(166, 4, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCC4, [0x4F, 0x27, 0xFA])), + Pot(170, 4, PotItem.Heart, 'Spike Cave', obj=RoomObject(0x03FCCA, [0x57, 0x27, 0xFA])), + Pot(0x18, 0x8, PotItem.Nothing, 'Spike Cave', PotFlags.Block)], + 0x119: [Pot(44, 28, PotItem.Heart, 'Blinds Hideout', obj=RoomObject(0x03FDFB, [0x5B, 0xE3, 0xFA])), + Pot(48, 28, PotItem.OneRupee, 'Blinds Hideout', obj=RoomObject(0x03FDFE, [0x63, 0xE3, 0xFA])), + Pot(76, 28, PotItem.Heart, 'Blinds Hideout', obj=RoomObject(0x03FE01, [0x9B, 0xE3, 0xFA])), + Pot(80, 28, PotItem.Heart, 'Blinds Hideout', obj=RoomObject(0x03FE04, [0xA3, 0xE3, 0xFA]))], + 0x11A: [Pot(214, 10, PotItem.Heart, 'Palace of Darkness Hint', obj=RoomObject(0x03F91F, [0xAF, 0x57, 0xFA])), + Pot(218, 10, PotItem.Heart, 'Palace of Darkness Hint', obj=RoomObject(0x03F922, [0xB7, 0x57, 0xFA])), + Pot(226, 10, PotItem.Heart, 'Palace of Darkness Hint', obj=RoomObject(0x03F925, [0xC7, 0x57, 0xFA])), + Pot(230, 10, PotItem.Heart, 'Palace of Darkness Hint', obj=RoomObject(0x03F928, [0xCF, 0x57, 0xFA]))], + 0x11B: [Pot(24, 0x15, PotItem.Nothing, 'Cave 45', PotFlags.LowerRegion, RoomObject(0x03F416, [0x33, 0xAB, 0xFA])), + Pot(24, 0x16, PotItem.Heart, 'Cave 45', PotFlags.LowerRegion, obj=RoomObject(0x03F419, [0x33, 0xB3, 0xFA])), + Pot(32, 0x16, PotItem.Heart, 'Cave 45', PotFlags.LowerRegion, obj=RoomObject(0x03F422, [0x43, 0xB3, 0xFA])), + Pot(40, 0x16, PotItem.Heart, 'Cave 45', PotFlags.LowerRegion, obj=RoomObject(0x03F425, [0x53, 0xB3, 0xFA])), + Pot(24, 0x17, PotItem.Heart, 'Cave 45', PotFlags.LowerRegion, obj=RoomObject(0x03F41C, [0x33, 0xBB, 0xFA])), + Pot(28, 0x18, PotItem.Heart, 'Cave 45', PotFlags.LowerRegion, obj=RoomObject(0x03F41F, [0x3B, 0xC3, 0xFA])), + Pot(92, 22, PotItem.Bomb, 'Graveyard Cave', obj=RoomObject(0x03F3D8, [0xBB, 0xB3, 0xFA])), + Pot(96, 22, PotItem.Heart, 'Graveyard Cave', obj=RoomObject(0x03F3DB, [0xC3, 0xB3, 0xFA])), + Pot(92, 23, PotItem.Bomb, 'Graveyard Cave', obj=RoomObject(0x03F3DE, [0xBB, 0xBB, 0xFA])), + Pot(96, 23, PotItem.Heart, 'Graveyard Cave', obj=RoomObject(0x03F3E1, [0xC3, 0xBB, 0xFA])), + Pot(92, 24, PotItem.Bomb, 'Graveyard Cave', obj=RoomObject(0x03F3E4, [0xBB, 0xC3, 0xFA])), + Pot(96, 24, PotItem.Heart, 'Graveyard Cave', obj=RoomObject(0x03F3E7, [0xC3, 0xC3, 0xFA])), + Pot(92, 25, PotItem.Bomb, 'Graveyard Cave', obj=RoomObject(0x03F3EA, [0xBB, 0xCB, 0xFA])), + Pot(96, 25, PotItem.Heart, 'Graveyard Cave', obj=RoomObject(0x03F3ED, [0xC3, 0xCB, 0xFA]))], + 0x11D: [Pot(60, 6, PotItem.FiveRupees, 'Blinds Hideout (Top)', obj=RoomObject(0x03FE69, [0x7B, 0x33, 0xFA])), + Pot(64, 6, PotItem.FiveRupees, 'Blinds Hideout (Top)', obj=RoomObject(0x03FE6C, [0x83, 0x33, 0xFA])), + Pot(60, 7, PotItem.FiveRupees, 'Blinds Hideout (Top)', obj=RoomObject(0x03FE6F, [0x7B, 0x3B, 0xFA])), + Pot(64, 7, PotItem.FiveRupees, 'Blinds Hideout (Top)', obj=RoomObject(0x03FE72, [0x83, 0x3B, 0xFA])), + Pot(60, 8, PotItem.FiveRupees, 'Blinds Hideout (Top)', obj=RoomObject(0x03FE75, [0x7B, 0x43, 0xFA])), + Pot(64, 8, PotItem.FiveRupees, 'Blinds Hideout (Top)', obj=RoomObject(0x03FE78, [0x83, 0x43, 0xFA]))], + 0x11F: [Pot(174, 28, PotItem.Heart, 'Lumberjack House', obj=RoomObject(0x03FF8A, [0x5F, 0xE7, 0xFA])), + Pot(178, 28, PotItem.Heart, 'Lumberjack House', obj=RoomObject(0x03FF8D, [0x67, 0xE7, 0xFA]))], + 0x124: [Pot(20, 20, PotItem.FiveRupees, '50 Rupee Cave', obj=RoomObject(0x0AB5C2, [0x2B, 0xA3, 0xFA])), + Pot(40, 20, PotItem.FiveRupees, '50 Rupee Cave', obj=RoomObject(0x0AB5E3, [0x53, 0xA3, 0xFA])), + Pot(20, 21, PotItem.FiveRupees, '50 Rupee Cave', obj=RoomObject(0x0AB5C5, [0x2B, 0xAB, 0xFA])), + Pot(40, 21, PotItem.FiveRupees, '50 Rupee Cave', obj=RoomObject(0x0AB5E0, [0x53, 0xAB, 0xFA])), + Pot(20, 22, PotItem.FiveRupees, '50 Rupee Cave', obj=RoomObject(0x0AB5C8, [0x2B, 0xB3, 0xFA])), + Pot(40, 22, PotItem.FiveRupees, '50 Rupee Cave', obj=RoomObject(0x0AB5DD, [0x53, 0xB3, 0xFA])), + Pot(24, 24, PotItem.FiveRupees, '50 Rupee Cave', obj=RoomObject(0x0AB5CE, [0x33, 0xC3, 0xFA])), + Pot(28, 24, PotItem.FiveRupees, '50 Rupee Cave', obj=RoomObject(0x0AB5D1, [0x3B, 0xC3, 0xFA])), + Pot(32, 24, PotItem.FiveRupees, '50 Rupee Cave', obj=RoomObject(0x0AB5D4, [0x43, 0xC3, 0xFA])), + Pot(36, 24, PotItem.FiveRupees, '50 Rupee Cave', obj=RoomObject(0x0AB5D7, [0x4B, 0xC3, 0xFA]))], + 0x125: [Pot(24, 25, PotItem.FiveRupees, '20 Rupee Cave', obj=RoomObject(0x0AB618, [0x33, 0xCB, 0xFA])), + Pot(28, 25, PotItem.FiveRupees, '20 Rupee Cave', obj=RoomObject(0x0AB61B, [0x3B, 0xCB, 0xFA])), + Pot(32, 25, PotItem.FiveRupees, '20 Rupee Cave', obj=RoomObject(0x0AB61E, [0x43, 0xCB, 0xFA])), + Pot(36, 25, PotItem.FiveRupees, '20 Rupee Cave', obj=RoomObject(0x0AB621, [0x4B, 0xCB, 0xFA])), + Pot(88, 22, PotItem.Heart, 'Dark Lake Hylia Ledge Spike Cave', obj=RoomObject(0x0AB627, [0xB3, 0xB3, 0xFA])), + Pot(100, 22, PotItem.Heart, 'Dark Lake Hylia Ledge Spike Cave', obj=RoomObject(0x0AB62A, [0xCB, 0xB3, 0xFA])), + Pot(88, 28, PotItem.Heart, 'Dark Lake Hylia Ledge Spike Cave', obj=RoomObject(0x0AB633, [0xB3, 0xE3, 0xFA])), + Pot(100, 28, PotItem.Heart, 'Dark Lake Hylia Ledge Spike Cave', obj=RoomObject(0x0AB636, [0xCB, 0xE3, 0xFA]))], + 0x127: [Pot(24, 25, PotItem.Nothing, 'Dark World Hammer Peg Cave', obj=RoomObject(0x2A801A, [0x33, 0xCB, 0xFA])), + Pot(28, 25, PotItem.Nothing, 'Dark World Hammer Peg Cave', obj=RoomObject(0x2A801D, [0x3B, 0xCB, 0xFA])), + Pot(32, 25, PotItem.Nothing, 'Dark World Hammer Peg Cave', obj=RoomObject(0x2A8020, [0x43, 0xCB, 0xFA])), + Pot(36, 25, PotItem.Nothing, 'Dark World Hammer Peg Cave', obj=RoomObject(0x2A8023, [0x4B, 0xCB, 0xFA]))], } def shuffle_pots(world, player): import RaceRandom as random - new_pot_contents = {} + new_pot_contents = PotSecretTable() - for supertile in vanilla_pots: - old_pots = vanilla_pots[supertile] + for super_tile in vanilla_pots: + old_pots = vanilla_pots[super_tile] new_pots = [Pot(pot.x, pot.y, PotItem.Nothing, pot.room, pot.flags) for pot in old_pots] # sort in the order Hole, Switch, Key, Other, Nothing sort_order = {PotItem.Hole: 4, PotItem.Switch: 3, PotItem.Key: 2, PotItem.Nothing: 0} @@ -295,7 +878,7 @@ def shuffle_pots(world, player): elif old_pot.item == PotItem.Switch: available_pots = (pot for pot in new_pots if (pot.room == old_pot.room or pot.room in movable_switch_rooms[old_pot.room]) and not (pot.flags & PotFlags.NoSwitch)) elif old_pot.item == PotItem.Key: - if world.doorShuffle[player] == 'vanilla' and not world.retro[player] and not world.keydropshuffle[player] and world.logic[player] != 'nologic': + if world.doorShuffle[player] == 'vanilla' and not world.retro[player] and world.pottery[player] == 'none' and world.logic[player] != 'nologic': available_pots = (pot for pot in new_pots if pot.room not in invalid_key_rooms) else: available_pots = new_pots @@ -309,12 +892,14 @@ def shuffle_pots(world, player): if world.retro[player] and new_pot.item == PotItem.FiveArrows: new_pot.item = PotItem.FiveRupees - if new_pot.item == PotItem.Key and new_pot.room != old_pot.room: - # Move pot key to new room + if new_pot.item == PotItem.Key: key = next(location for location in world.get_region(old_pot.room, player).locations if location.name in key_drop_data) - world.get_region(old_pot.room, player).locations.remove(key) - world.get_region(new_pot.room, player).locations.append(key) - key.parent_region = world.get_region(new_pot.room, player) + key.pot = new_pot + if new_pot.room != old_pot.room: + # Move pot key to new room + world.get_region(old_pot.room, player).locations.remove(key) + world.get_region(new_pot.room, player).locations.append(key) + key.parent_region = world.get_region(new_pot.room, player) elif new_pot.item == PotItem.Switch and (new_pot.flags & PotFlags.SwitchLogicChange): if new_pot.room == 'PoD Basement Ledge': basement = world.get_region(old_pot.room, player) @@ -328,8 +913,163 @@ def shuffle_pots(world, player): # Rule is created based on barrier world.get_door('Thieves Attic ES', player).barrier(CrystalBarrier.Orange) else: - raise Exception("Switch locattion in room %s requires logic change" % new_pot.room) + raise Exception("Switch location in room %s requires logic change" % new_pot.room) - new_pot_contents[supertile] = new_pots + new_pot_contents.room_map[super_tile] = new_pots world.pot_contents[player] = new_pot_contents + + +def shuffle_pot_switches(world, player): + import RaceRandom as random + + for super_tile in vanilla_pots: + new_pots = world.pot_contents[player].room_map[super_tile] + # sort in the order Hole, Switch, Key, Other, Nothing + sort_order = {PotItem.Hole: 4, PotItem.Switch: 3, PotItem.Key: 2, PotItem.Nothing: 0} + old_pots = sorted(new_pots, key=lambda pot: sort_order.get(pot.item, 1), reverse=True) + + for old_pot in old_pots: + if old_pot.item != PotItem.Switch: + break + else: + available_pots = [pot for pot in new_pots if (pot.room == old_pot.room or pot.room in movable_switch_rooms[old_pot.room]) and not (pot.flags & PotFlags.NoSwitch)] + + new_pot = random.choice(available_pots) + new_pot.item, old_pot.item = old_pot.item, new_pot.item + if world.retro[player] and new_pot.item == PotItem.FiveArrows: + new_pot.item = PotItem.FiveRupees + + if new_pot.item == PotItem.Switch and (new_pot.flags & PotFlags.SwitchLogicChange): + if new_pot.room == 'PoD Basement Ledge': + basement = world.get_region(old_pot.room, player) + ledge = world.get_region(new_pot.room, player) + ledge.locations.append(basement.locations.pop()) + elif new_pot.room == 'Swamp Push Statue': + from Rules import set_rule + set_rule(world.get_entrance('Swamp Push Statue NE', player), lambda state: state.has('Cane of Somaria', player)) + world.get_door('Swamp Push Statue NW', player).blocked = True + elif new_pot.room == 'Thieves Attic Hint': + # Rule is created based on barrier + world.get_door('Thieves Attic ES', player).barrier(CrystalBarrier.Orange) + else: + raise Exception("Switch location in room %s requires logic change" % new_pot.room) + for location in world.get_locations(): + if location.player == player and location.type == LocationType.Pot and location.pot.item == PotItem.Switch: + location.real = False + if location in world.dynamic_locations: + world.dynamic_locations.remove(location) + location.parent_region.locations.remove(location) + world.clear_location_cache() + + +def choose_pots(world, player): + pot_pool = set() + if world.pottery[player] == 'reduced': + dungeon_list = [] + for super_tile, pot_list in vanilla_pots.items(): + for pot in pot_list: + if world.get_region(pot.room, player).type == RegionType.Cave: + pot_pool.add(pot) + else: + dungeon_list.append(pot) + k = len(dungeon_list) // 4 + pot_pool.update(random.choices(dungeon_list, k=k)) + elif world.pottery[player] == 'clustered': + dungeon_map = defaultdict(list) + dungeon_count = 0 + for super_tile, pot_list in vanilla_pots.items(): + for pot in pot_list: + if world.get_region(pot.room, player).type == RegionType.Cave: + pot_pool.add(pot) + else: + dungeon_map[pot.room].append(pot) + dungeon_count += 1 + k = dungeon_count // 2 + options = sorted(list(dungeon_map.keys())) + chosen_amt = 0 + while chosen_amt < k: + chosen_section = random.choice(options) + pot_pool.update(dungeon_map[chosen_section]) + chosen_amt += len(dungeon_map[chosen_section]) + options.remove(chosen_section) + return pot_pool + + +key_drop_data = { + 'Hyrule Castle - Map Guard Key Drop': ['Drop', (0x09E20C, 0x72, 0), 'dropped in Hyrule Castle', 'Small Key (Escape)'], + 'Hyrule Castle - Boomerang Guard Key Drop': ['Drop', (0x09E204, 0x71, 1), 'dropped in Hyrule Castle', 'Small Key (Escape)'], + 'Hyrule Castle - Key Rat Key Drop': ['Drop', (0x09DB80, 0x21, 0), 'dropped in Hyrule Castle', 'Small Key (Escape)'], + 'Hyrule Castle - Big Key Drop': ['Drop', (0x09E327, 0x80, 2), 'dropped in Hyrule Castle', 'Big Key (Escape)'], + 'Eastern Palace - Dark Square Pot Key': ['Pot', 0xBA, 'in a pot in Eastern Palace', 'Small Key (Eastern Palace)'], + 'Eastern Palace - Dark Eyegore Key Drop': ['Drop', (0x09E4F8, 0x99, 3), 'dropped in Eastern Palace', 'Small Key (Eastern Palace)'], + 'Desert Palace - Desert Tiles 1 Pot Key': ['Pot', 0x63, 'in a pot in Desert Palace', 'Small Key (Desert Palace)'], + 'Desert Palace - Beamos Hall Pot Key': ['Pot', 0x53, 'in a pot in Desert Palace', 'Small Key (Desert Palace)'], + 'Desert Palace - Desert Tiles 2 Pot Key': ['Pot', 0x43, 'in a pot in Desert Palace', 'Small Key (Desert Palace)'], + 'Castle Tower - Dark Archer Key Drop': ['Drop', (0x09E7C9, 0xC0, 3), 'dropped in Castle Tower', 'Small Key (Agahnims Tower)'], + 'Castle Tower - Circle of Pots Key Drop': ['Drop', (0x09E688, 0xB0, 10), 'dropped in Castle Tower', 'Small Key (Agahnims Tower)'], + 'Swamp Palace - Pot Row Pot Key': ['Pot', 0x38, 'in a pot in Swamp Palace', 'Small Key (Swamp Palace)'], + 'Swamp Palace - Trench 1 Pot Key': ['Pot', 0x37, 'in a pot in Swamp Palace', 'Small Key (Swamp Palace)'], + 'Swamp Palace - Hookshot Pot Key': ['Pot', 0x36, 'in a pot in Swamp Palace', 'Small Key (Swamp Palace)'], + 'Swamp Palace - Trench 2 Pot Key': ['Pot', 0x35, 'in a pot in Swamp Palace', 'Small Key (Swamp Palace)'], + 'Swamp Palace - Waterway Pot Key': ['Pot', 0x16, 'in a pot in Swamp Palace', 'Small Key (Swamp Palace)'], + 'Skull Woods - West Lobby Pot Key': ['Pot', 0x56, 'in a pot in Skull Woods', 'Small Key (Skull Woods)'], + 'Skull Woods - Spike Corner Key Drop': ['Drop', (0x09DD74, 0x39, 1), 'dropped near Mothula', 'Small Key (Skull Woods)'], + "Thieves' Town - Hallway Pot Key": ['Pot', 0xBC, "in a pot in Thieves Town", 'Small Key (Thieves Town)'], + "Thieves' Town - Spike Switch Pot Key": ['Pot', 0xAB, "in a pot in Thieves Town", 'Small Key (Thieves Town)'], + 'Ice Palace - Jelly Key Drop': ['Drop', (0x09DA21, 0xE, 3), 'dropped in Ice Palace', 'Small Key (Ice Palace)'], + 'Ice Palace - Conveyor Key Drop': ['Drop', (0x09DE08, 0x3E, 8), 'dropped in Ice Palace', 'Small Key (Ice Palace)'], + 'Ice Palace - Hammer Block Key Drop': ['Pot', 0x3F, 'under a block in Ice Palace', 'Small Key (Ice Palace)'], + 'Ice Palace - Many Pots Pot Key': ['Pot', 0x9F, 'int a pot in Ice Palace', 'Small Key (Ice Palace)'], + 'Misery Mire - Spikes Pot Key': ['Pot', 0xB3, 'in a pot in Misery Mire', 'Small Key (Misery Mire)'], + 'Misery Mire - Fishbone Pot Key': ['Pot', 0xA1, 'in a pot in forgotten Mire', 'Small Key (Misery Mire)'], + 'Misery Mire - Conveyor Crystal Key Drop': ['Drop', (0x09E7FB, 0xC1, 9), 'dropped in Misery Mire', 'Small Key (Misery Mire)'], + 'Turtle Rock - Pokey 1 Key Drop': ['Drop', (0x09E70D, 0xB6, 5), 'dropped in Turtle Rock', 'Small Key (Turtle Rock)'], + 'Turtle Rock - Pokey 2 Key Drop': ['Drop', (0x09DA5D, 0x13, 6), 'dropped in Turtle Rock', 'Small Key (Turtle Rock)'], + 'Ganons Tower - Conveyor Cross Pot Key': ['Pot', 0x8B, "in a pot in Ganon's Tower", 'Small Key (Ganons Tower)'], + 'Ganons Tower - Double Switch Pot Key': ['Pot', 0x9B, "in a pot in Ganon's Tower", 'Small Key (Ganons Tower)'], + 'Ganons Tower - Conveyor Star Pits Pot Key': ['Pot', 0x7B, "in a pot in Ganon's Tower", 'Small Key (Ganons Tower)'], + 'Ganons Tower - Mini Helmasaur Key Drop': ['Drop', (0x09DDC4, 0x3D, 2), "dropped atop Ganon's Tower", 'Small Key (Ganons Tower)'] +} + + +class PotSecretTable(object): + def __init__(self): + self.room_map = defaultdict(list) + self.multiworld_count = 0 + + def write_pot_data_to_rom(self, rom, colorize): + pointer_address = 0x140000 # pots currently in bank 28 + pointer_offset = 0x128 * 2 + empty_address = (pointer_address + pointer_offset) + empty_pointer = pc_to_snes(empty_address) & 0xFFFF + data_pointer = pointer_address + pointer_offset + 2 + for room in range(0, 0x128): + if room in self.room_map: + list_idx = 0 + collection_rate_mask = 0x00 + data_address = pc_to_snes(data_pointer) & 0xFFFF + rom.write_bytes(pointer_address + room * 2, int16_as_bytes(data_address)) + for pot in self.room_map[room]: + rom.write_bytes(data_pointer + list_idx * 3, pot.pot_data()) + if pot.location is not None and not pot.location.forced_item: + collection_rate_mask |= 1 << (15 - list_idx) + if colorize and pot.obj_ref: + pot.obj_ref.change_type(Shuffled_Pot) + pot.obj_ref.write_to_rom(rom) + list_idx += 1 + rom.write_bytes(data_pointer + list_idx * 3, [0xFF, 0xFF]) + rom.write_bytes(snes_to_pc(0x28AA60) + room * 2, int16_as_bytes(collection_rate_mask)) + data_pointer += 3 * list_idx + 2 + else: + rom.write_bytes(pointer_address + room * 2, int16_as_bytes(empty_pointer)) + rom.write_bytes(empty_address, [0xFF, 0xFF]) + + def size(self): + size = 0x128 * 2 + for room in range(0, 0x128): + if room in self.room_map: + pot_list = [p for p in self.room_map[room]] + if pot_list: + size += len(pot_list) * 3 + 2 + return size diff --git a/README.md b/README.md index ad384f21..9dbb35fe 100644 --- a/README.md +++ b/README.md @@ -140,7 +140,7 @@ New flute spots are chosen at random with minimum bias. ### Trinity -This goal gives you the choice between 3 goals, only one of which the player needs to complete: Defeat Ganon (no Aga2), Pulling Pedestal, or turn in TF pieces to Murahdulah. By default, you need to find 8 of 10 total TF pieces but this can be changed with a Custom Item Pool. It is recommended to set GT Entry to 7 crystals and Ganon to 5 crystals or Random crystals, although the player can flexibly change these settings as they seem fit. +This goal gives you the choice between 3 goals, only one of which the player needs to complete: Defeat Ganon (no Aga2), Pulling Pedestal, or turn in TF pieces to Murahdahla. By default, you need to find 8 of 10 total TF pieces but this can be changed with a Custom Item Pool. It is recommended to set GT Entry to 7 crystals and Ganon to 5 crystals or Random crystals, although the player can flexibly change these settings as they seem fit. ## New Entrance Shuffle Options (--shuffle) diff --git a/RELEASENOTES.md b/RELEASENOTES.md index d6e8ebaf..65293af1 100644 --- a/RELEASENOTES.md +++ b/RELEASENOTES.md @@ -1,7 +1,77 @@ ## New Features -## Restricted Item Placement Algorithm +## Pottery Lottery and Key Drop Shuffle Changes +### Pottery + +New pottery option that control which pots (and large blocks) are in the locations pool: + +* None: No pots are in the pool, like normal randomizer +* Key Pots: The pots that have keys are in the pool. This is about half of the old keydropshuffle option +* Cave Pots: The pots that are not found in dungeons are in the pool. (Includes the large block in Spike Cave). Does +not include key pots. +* CaveKeys: Both non-dungeon pots and pots that used to have keys are in the pool. +* Reduced: Same as CaveKeys but also roughly a quarter of dungeon pots are added to the location pool picked at random. This is a dynamic mode so pots in the pool will be colored. Pots out of the pool will have vanilla contents. +* Clustered: LIke reduced but pot are grouped by logical sets and roughly 50% of pots are chosen from those group. This is a dynamic mode like the above. +* Nonempty: All pots that had some sort of objects under them are chosen to be in the location pool. This excludes most large blocks and some pots out of dungeons. +* Dungeon Pots: The pots that are in dungeons are in the pool. (Includes serveral large blocks) +* Lottery: All pots and large blocks are in the pool + +By default, switches remain in their vanilla location (unless you turn on the legacy option below) + +CLI `--pottery