Add some support for creating a plandomizer.
This commit is contained in:
		
							parent
							
								
									c4bfdefbdc
								
							
						
					
					
						commit
						c605046337
					
				| 
						 | 
				
			
			@ -0,0 +1,255 @@
 | 
			
		|||
from BaseClasses import World, CollectionState, Item
 | 
			
		||||
from Regions import create_regions
 | 
			
		||||
from EntranceShuffle import link_entrances
 | 
			
		||||
from Rom import patch_rom
 | 
			
		||||
from Rules import set_rules
 | 
			
		||||
from Items import ItemFactory
 | 
			
		||||
import random
 | 
			
		||||
import time
 | 
			
		||||
import logging
 | 
			
		||||
import argparse
 | 
			
		||||
import os
 | 
			
		||||
import hashlib
 | 
			
		||||
 | 
			
		||||
__version__ = '0.1-dev'
 | 
			
		||||
 | 
			
		||||
logic_hash = [182, 244, 144, 92, 149, 200, 93, 183, 124, 169, 226, 46, 111, 163, 5, 193, 13, 112, 125, 101, 128, 84, 31, 67, 107, 94, 184, 100, 189, 18, 8, 171,
 | 
			
		||||
              142, 57, 173, 38, 37, 211, 253, 131, 98, 239, 167, 116, 32, 186, 70, 148, 66, 151, 143, 86, 59, 83, 16, 51, 240, 152, 60, 242, 190, 117, 76, 122,
 | 
			
		||||
              15, 221, 62, 39, 174, 177, 223, 34, 150, 50, 178, 238, 95, 219, 10, 162, 222, 0, 165, 202, 74, 36, 206, 209, 251, 105, 175, 135, 121, 88, 214, 247,
 | 
			
		||||
              154, 161, 71, 19, 85, 157, 40, 96, 225, 27, 230, 49, 231, 207, 64, 35, 249, 134, 132, 108, 63, 24, 4, 127, 255, 14, 145, 23, 81, 216, 113, 90, 194,
 | 
			
		||||
              110, 65, 229, 43, 1, 11, 168, 147, 103, 156, 77, 80, 220, 28, 227, 213, 198, 172, 79, 75, 140, 44, 146, 188, 17, 6, 102, 56, 235, 166, 89, 218, 246,
 | 
			
		||||
              99, 78, 187, 126, 119, 196, 69, 137, 181, 55, 20, 215, 199, 130, 9, 45, 58, 185, 91, 33, 197, 72, 115, 195, 114, 29, 30, 233, 141, 129, 155, 159, 47,
 | 
			
		||||
              224, 236, 21, 234, 191, 136, 104, 87, 106, 26, 73, 250, 248, 228, 48, 53, 243, 237, 241, 61, 180, 12, 208, 245, 232, 192, 2, 7, 170, 123, 176, 160, 201,
 | 
			
		||||
              153, 217, 252, 158, 25, 205, 22, 133, 254, 138, 203, 118, 210, 204, 82, 97, 52, 164, 68, 139, 120, 109, 54, 3, 41, 179, 212, 42]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def main(args, seed=None):
 | 
			
		||||
    start = time.clock()
 | 
			
		||||
 | 
			
		||||
    # initialize the world
 | 
			
		||||
    world = World('default', 'noglitches', 'standard', 'normal', 'ganon', False)
 | 
			
		||||
    logger = logging.getLogger('')
 | 
			
		||||
 | 
			
		||||
    hasher = hashlib.md5()
 | 
			
		||||
    with open(args.plando, 'rb') as plandofile:
 | 
			
		||||
        buf = plandofile.read()
 | 
			
		||||
        hasher.update(buf)
 | 
			
		||||
    world.seed = int(hasher.hexdigest(), 16) % 1000000000
 | 
			
		||||
 | 
			
		||||
    random.seed(world.seed)
 | 
			
		||||
 | 
			
		||||
    world.spoiler += 'ALttP Plandomizer Version %s  -  Seed: %s\n\n' % (__version__, args.plando)
 | 
			
		||||
 | 
			
		||||
    logger.info(world.spoiler)
 | 
			
		||||
 | 
			
		||||
    create_regions(world)
 | 
			
		||||
 | 
			
		||||
    world.spoiler += link_entrances(world)
 | 
			
		||||
 | 
			
		||||
    logger.info('Calculating Access Rules.')
 | 
			
		||||
 | 
			
		||||
    world.spoiler += set_rules(world)
 | 
			
		||||
 | 
			
		||||
    logger.info('Fill the world.')
 | 
			
		||||
 | 
			
		||||
    world.spoiler += fill_world(world, args.plando)
 | 
			
		||||
 | 
			
		||||
    world.spoiler += print_location_spoiler(world)
 | 
			
		||||
 | 
			
		||||
    logger.info('Calculating playthrough.')
 | 
			
		||||
 | 
			
		||||
    world.spoiler += create_playthrough(world)
 | 
			
		||||
 | 
			
		||||
    logger.info('Patching ROM.')
 | 
			
		||||
 | 
			
		||||
    if args.sprite is not None:
 | 
			
		||||
        sprite = bytearray(open(args.sprite, 'rb').read())
 | 
			
		||||
    else:
 | 
			
		||||
        sprite = None
 | 
			
		||||
 | 
			
		||||
    rom = bytearray(open(args.rom, 'rb').read())
 | 
			
		||||
    patched_rom = patch_rom(world, rom, logic_hash, args.quickswap, args.heartbeep, sprite)
 | 
			
		||||
 | 
			
		||||
    outfilebase = 'Plando_%s_%s' % (os.path.splitext(os.path.basename(args.plando))[0], world.seed)
 | 
			
		||||
 | 
			
		||||
    with open('%s.sfc' % outfilebase, 'wb') as outfile:
 | 
			
		||||
        outfile.write(patched_rom)
 | 
			
		||||
    if args.create_spoiler:
 | 
			
		||||
        with open('%s_Spoiler.txt' % outfilebase, 'w') as outfile:
 | 
			
		||||
            outfile.write(world.spoiler)
 | 
			
		||||
 | 
			
		||||
    logger.info('Done. Enjoy.')
 | 
			
		||||
    logger.debug('Total Time: %s' % (time.clock() - start))
 | 
			
		||||
 | 
			
		||||
    return world
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def fill_world(world, plando):
 | 
			
		||||
    mm_medallion = 'Ether'
 | 
			
		||||
    tr_medallion = 'Quake'
 | 
			
		||||
    logger = logging.getLogger('')
 | 
			
		||||
    with open(plando, 'r') as plandofile:
 | 
			
		||||
        for line in plandofile.readlines():
 | 
			
		||||
            if ':' in line:
 | 
			
		||||
                line = line.lstrip()
 | 
			
		||||
 | 
			
		||||
                if line.startswith('#'):
 | 
			
		||||
                    continue
 | 
			
		||||
                if line.startswith('!'):
 | 
			
		||||
                    if line.startswith('!mm_medallion'):
 | 
			
		||||
                        _, medallionstr = line.split(':', 1)
 | 
			
		||||
                        mm_medallion = medallionstr.strip()
 | 
			
		||||
                    elif line.startswith('!tr_medallion'):
 | 
			
		||||
                        _, medallionstr = line.split(':', 1)
 | 
			
		||||
                        tr_medallion = medallionstr.strip()
 | 
			
		||||
                    elif line.startswith('!mode'):
 | 
			
		||||
                        _, modestr = line.split(':', 1)
 | 
			
		||||
                        world.mode = modestr.strip()
 | 
			
		||||
                    elif line.startswith('!logic'):
 | 
			
		||||
                        _, logicstr = line.split(':', 1)
 | 
			
		||||
                        world.logic = logicstr.strip()
 | 
			
		||||
                    elif line.startswith('!goal'):
 | 
			
		||||
                        _, goalstr = line.split(':', 1)
 | 
			
		||||
                        world.goal = goalstr.strip()
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                locationstr, itemstr = line.split(':', 1)
 | 
			
		||||
                location = world.get_location(locationstr.strip())
 | 
			
		||||
                if location is None:
 | 
			
		||||
                    logger.warn('Unknown location: %s' % locationstr)
 | 
			
		||||
                    continue
 | 
			
		||||
                else:
 | 
			
		||||
                    item = ItemFactory(itemstr.strip())
 | 
			
		||||
                    if item is not None:
 | 
			
		||||
                        world.push_item(location, item)
 | 
			
		||||
 | 
			
		||||
    world.required_medallions = (mm_medallion, tr_medallion)
 | 
			
		||||
    return 'Misery Mire Medallion: %s\nTurtle Rock Medallion: %s\n\n' % (mm_medallion, tr_medallion)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def copy_world(world):
 | 
			
		||||
    # ToDo: Not good yet
 | 
			
		||||
    ret = World(world.shuffle, world.logic, world.mode, world.difficulty, world.goal, world.place_dungeon_items)
 | 
			
		||||
    ret.required_medallions = list(world.required_medallions)
 | 
			
		||||
    ret.agahnim_fix_required = world.agahnim_fix_required
 | 
			
		||||
    ret.swamp_patch_required = world.swamp_patch_required
 | 
			
		||||
    create_regions(ret)
 | 
			
		||||
 | 
			
		||||
    # connect copied world
 | 
			
		||||
    for region in world.regions:
 | 
			
		||||
        for entrance in region.entrances:
 | 
			
		||||
            ret.get_entrance(entrance.name).connect(ret.get_region(region.name))
 | 
			
		||||
 | 
			
		||||
    set_rules(ret)
 | 
			
		||||
 | 
			
		||||
    # fill locations
 | 
			
		||||
    for location in world.get_locations():
 | 
			
		||||
        if location.item is not None:
 | 
			
		||||
            item = Item(location.item.name, location.item.advancement, location.item.key)
 | 
			
		||||
            ret.get_location(location.name).item = item
 | 
			
		||||
            item.location = ret.get_location(location.name)
 | 
			
		||||
 | 
			
		||||
    # copy remaining itempool. No item in itempool should have an assigned location
 | 
			
		||||
    for item in world.itempool:
 | 
			
		||||
        ret.itempool.append(Item(item.name, item.advancement, item.key))
 | 
			
		||||
 | 
			
		||||
    # copy progress items in state
 | 
			
		||||
    ret.state.prog_items = list(world.state.prog_items)
 | 
			
		||||
 | 
			
		||||
    return ret
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def create_playthrough(world):
 | 
			
		||||
    # create a copy as we will modify it
 | 
			
		||||
    world = copy_world(world)
 | 
			
		||||
 | 
			
		||||
    # if we do pedestal%, ganon should not be a viable option as far as the playthrough is concerned
 | 
			
		||||
    if world.goal == 'pedestal':
 | 
			
		||||
        world.get_location('Ganon').item = None
 | 
			
		||||
 | 
			
		||||
    # get locations containing progress items
 | 
			
		||||
    prog_locations = [location for location in world.get_locations() if location.item is not None and location.item.advancement]
 | 
			
		||||
 | 
			
		||||
    collection_spheres = []
 | 
			
		||||
    state = CollectionState(world)
 | 
			
		||||
    sphere_candidates = list(prog_locations)
 | 
			
		||||
    logging.getLogger('').debug('Building up collection spheres.')
 | 
			
		||||
    while sphere_candidates:
 | 
			
		||||
        sphere = []
 | 
			
		||||
        # build up spheres of collection radius. Everything in each sphere is independent from each other in dependencies and only depends on lower spheres
 | 
			
		||||
        for location in sphere_candidates:
 | 
			
		||||
            if state.can_reach(location):
 | 
			
		||||
                sphere.append(location)
 | 
			
		||||
 | 
			
		||||
        for location in sphere:
 | 
			
		||||
            sphere_candidates.remove(location)
 | 
			
		||||
            state.collect(location.item)
 | 
			
		||||
 | 
			
		||||
        collection_spheres.append(sphere)
 | 
			
		||||
 | 
			
		||||
        logging.getLogger('').debug('Calculated sphere %i, containing %i of %i progress items.' % (len(collection_spheres), len(sphere), len(prog_locations)))
 | 
			
		||||
 | 
			
		||||
        if not sphere:
 | 
			
		||||
            logging.getLogger('').debug('The following items could not be placed: %s' % ['%s at %s' % (location.item.name, location.name) for location in sphere_candidates])
 | 
			
		||||
            raise RuntimeError('Not all progression items reachable. Something went terribly wrong here.')
 | 
			
		||||
 | 
			
		||||
    # in the second phase, we cull each sphere such that the game is still beatable, reducing each range of influence to the bare minimum required inside it
 | 
			
		||||
    for sphere in reversed(collection_spheres):
 | 
			
		||||
        to_delete = []
 | 
			
		||||
        for location in sphere:
 | 
			
		||||
            # we remove the item at location and check if game is still beatable
 | 
			
		||||
            logging.getLogger('').debug('Checking if %s is required to beat the game.' % location.item.name)
 | 
			
		||||
            old_item = location.item
 | 
			
		||||
            location.item = None
 | 
			
		||||
            state.remove(old_item)
 | 
			
		||||
            world._item_cache = {}  # need to invalidate
 | 
			
		||||
            if world.can_beat_game():
 | 
			
		||||
                to_delete.append(location)
 | 
			
		||||
            else:
 | 
			
		||||
                # still required, got to keep it around
 | 
			
		||||
                location.item = old_item
 | 
			
		||||
 | 
			
		||||
        # cull entries in spheres for spoiler walkthrough at end
 | 
			
		||||
        for location in to_delete:
 | 
			
		||||
            sphere.remove(location)
 | 
			
		||||
 | 
			
		||||
    # we are now down to just the required progress items in collection_spheres in a minimum number of spheres. As a cleanup, we right trim empty spheres (can happen if we have multiple triforces)
 | 
			
		||||
    collection_spheres = [sphere for sphere in collection_spheres if sphere]
 | 
			
		||||
 | 
			
		||||
    # we can finally output our playthrough
 | 
			
		||||
    return 'Playthrough:\n' + ''.join(['%s: {\n%s}\n' % (i + 1, ''.join(['  %s: %s\n' % (location, location.item) for location in sphere])) for i, sphere in enumerate(collection_spheres)]) + '\n'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def print_location_spoiler(world):
 | 
			
		||||
    return 'Locations:\n\n' + '\n'.join(['%s: %s' % (location, location.item if location.item is not None else 'Nothing') for location in world.get_locations()]) + '\n\n'
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
if __name__ == '__main__':
 | 
			
		||||
    parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
 | 
			
		||||
    parser.add_argument('--create_spoiler', help='Output a Spoiler File', action='store_true')
 | 
			
		||||
    parser.add_argument('--rom', default='Base_Rom.sfc', help='Path to a VT21 standard normal difficulty rom to use as a base.')
 | 
			
		||||
    parser.add_argument('--loglevel', default='info', const='info', nargs='?', choices=['error', 'info', 'warning', 'debug'], help='Select level of logging for output.')
 | 
			
		||||
    parser.add_argument('--seed', help='Define seed number to generate.', type=int)
 | 
			
		||||
    parser.add_argument('--quickswap', help='Enable quick item swapping with L and R.', action='store_true')
 | 
			
		||||
    parser.add_argument('--heartbeep', default='normal', const='normal', nargs='?', choices=['normal', 'half', 'quarter', 'off'],
 | 
			
		||||
                        help='Select the rate at which the heart beep sound is played at low health.')
 | 
			
		||||
    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.')
 | 
			
		||||
    parser.add_argument('--plando', help='Filled out template to use for setting up the rom.')
 | 
			
		||||
    args = parser.parse_args()
 | 
			
		||||
 | 
			
		||||
    # ToDo: Validate files further than mere existance
 | 
			
		||||
    if not os.path.isfile(args.rom):
 | 
			
		||||
        input('Could not find valid base rom for patching at expected path %s. Please run with -h to see help for further information. \nPress Enter to exit.' % args.rom)
 | 
			
		||||
        exit(1)
 | 
			
		||||
    if not os.path.isfile(args.plando):
 | 
			
		||||
        input('Could not find Plandomizer distribution at expected path %s. Please run with -h to see help for further information. \nPress Enter to exit.' % args.plando)
 | 
			
		||||
        exit(1)
 | 
			
		||||
    if args.sprite is not None and not os.path.isfile(args.rom):
 | 
			
		||||
        input('Could not find link sprite sheet at given location. \nPress Enter to exit.' % args.sprite)
 | 
			
		||||
        exit(1)
 | 
			
		||||
 | 
			
		||||
    # set up logger
 | 
			
		||||
    loglevel = {'error': logging.ERROR, 'info': logging.INFO, 'warning': logging.WARNING, 'debug': logging.DEBUG}[args.loglevel]
 | 
			
		||||
    logging.basicConfig(format='%(message)s', level=loglevel)
 | 
			
		||||
 | 
			
		||||
    main(args=args)
 | 
			
		||||
| 
						 | 
				
			
			@ -0,0 +1,244 @@
 | 
			
		|||
# Lines starting with a # are comments and ignored by the parsers
 | 
			
		||||
# Lines without a : are also ignored
 | 
			
		||||
 | 
			
		||||
# These are special instructions for setting the medallion requirements to enter the dungeons
 | 
			
		||||
!mm_medallion: Bombos
 | 
			
		||||
!tr_medallion: Quake
 | 
			
		||||
 | 
			
		||||
# This sets the game mode
 | 
			
		||||
!mode: open
 | 
			
		||||
 | 
			
		||||
# This sets the logic (for verification purposes)
 | 
			
		||||
!logic: noglitches
 | 
			
		||||
 | 
			
		||||
# This sets the goal (only used for generating the spoiler log)
 | 
			
		||||
!goal: ganon
 | 
			
		||||
 | 
			
		||||
# Now we fill in all locations
 | 
			
		||||
 | 
			
		||||
Mushroom: Mushroom
 | 
			
		||||
Bottle Vendor: Bottle
 | 
			
		||||
Haunted Grove: Ocarina
 | 
			
		||||
Piece of Heart (Dam): Nothing
 | 
			
		||||
Purple Chest: Nothing
 | 
			
		||||
[cave-022-B1] Thiefs hut [top chest]: Nothing
 | 
			
		||||
[cave-022-B1] Thiefs hut [top left chest]: Nothing
 | 
			
		||||
[cave-022-B1] Thiefs hut [top right chest]: Nothing
 | 
			
		||||
[cave-022-B1] Thiefs hut [bottom left chest]: Nothing
 | 
			
		||||
[cave-022-B1] Thiefs hut [bottom right chest]: Nothing
 | 
			
		||||
Uncle: Fighter Sword
 | 
			
		||||
[cave-034] Hyrule Castle Secret Entrance: Nothing
 | 
			
		||||
King Zora: Flippers
 | 
			
		||||
Piece of Heart (Zoras River): Nothing
 | 
			
		||||
[cave-018] Graveyard - top right grave: Cape
 | 
			
		||||
[cave-047] Dam: Nothing
 | 
			
		||||
[cave-040] Links House: Lamp
 | 
			
		||||
[cave-031] Tavern: Nothing
 | 
			
		||||
[cave-026] Chicken House: Nothing
 | 
			
		||||
[cave-044] Aginahs Cave: Nothing
 | 
			
		||||
[cave-035] Sahasrahlas Hut [left chest]: Nothing
 | 
			
		||||
[cave-035] Sahasrahlas Hut [center chest]: Nothing
 | 
			
		||||
[cave-035] Sahasrahlas Hut [right chest]: Nothing
 | 
			
		||||
Sahasrahla: Pegasus Boots
 | 
			
		||||
[cave-021] Kakariko Well [top chest]: Nothing
 | 
			
		||||
[cave-021] Kakariko Well [left chest row of 3]: Nothing
 | 
			
		||||
[cave-021] Kakariko Well [center chest row of 3]: Nothing
 | 
			
		||||
[cave-021] Kakariko Well [right chest row of 3]: Nothing
 | 
			
		||||
[cave-021] Kakariko Well [bottom chest]: Nothing
 | 
			
		||||
Blacksmiths: Tempered Sword
 | 
			
		||||
Magic Bat: Magic Upgrade (1/2)
 | 
			
		||||
Sick Kid: Bug Catching Net
 | 
			
		||||
Hobo: Bottle
 | 
			
		||||
Piece of Heart (Thieves Forest Hideout): Nothing
 | 
			
		||||
Piece of Heart (Lumberjack Tree): Nothing
 | 
			
		||||
Piece of Heart (Cave South of Haunted Grove): Nothing
 | 
			
		||||
Piece of Heart (Graveyard Cave): Nothing
 | 
			
		||||
Piece of Heart (Desert Cave): Nothing
 | 
			
		||||
[cave-050] Lake Hylia Cave [bottom left chest]: Nothing
 | 
			
		||||
[cave-050] Lake Hylia Cave [top left chest]: Nothing
 | 
			
		||||
[cave-050] Lake Hylia Cave [top right chest]: Nothing
 | 
			
		||||
[cave-050] Lake Hylia Cave [bottom right chest]: Nothing
 | 
			
		||||
[cave-050] Lake Hylia Cave [generous guy]: Nothing
 | 
			
		||||
[cave-051] Ice Cave: Ice Rod
 | 
			
		||||
[cave-016] Bonk Rock Cave: Nothing
 | 
			
		||||
Library: Book of Mudora
 | 
			
		||||
Witch: Magic Powder
 | 
			
		||||
Piece of Heart (Lake Hylia): Nothing
 | 
			
		||||
Piece of Heart (Maze Race): Nothing
 | 
			
		||||
Piece of Heart (Desert - west side): Nothing
 | 
			
		||||
[dungeon-L2-B1] Desert Palace - Big Chest: Power Glove
 | 
			
		||||
[dungeon-L2-B1] Desert Palace - Torch: Small Key (Desert Palace)
 | 
			
		||||
[dungeon-L2-B1] Desert Palace - Map Room: Nothing
 | 
			
		||||
[dungeon-L2-B1] Desert Palace - Compass Room: Nothing
 | 
			
		||||
[dungeon-L2-B1] Desert Palace - Big Key Room: Big Key (Desert Palace)
 | 
			
		||||
Lanmolas - Heart Container: Nothing
 | 
			
		||||
Lanmolas - Pendant: Blue Pendant
 | 
			
		||||
[dungeon-L1-1F] Eastern Palace - Compass Room: Nothing
 | 
			
		||||
[dungeon-L1-1F] Eastern Palace - Big Chest: Bow
 | 
			
		||||
[dungeon-L1-1F] Eastern Palace - Big Ball Room: Nothing
 | 
			
		||||
[dungeon-L1-1F] Eastern Palace - Big Key Room: Big Key (Eastern Palace)
 | 
			
		||||
[dungeon-L1-1F] Eastern Palace - Map Room: Nothing
 | 
			
		||||
Armos - Heart Container: Nothing
 | 
			
		||||
Armos - Pendant: Green Pendant
 | 
			
		||||
Altar: Master Sword
 | 
			
		||||
[dungeon-C-B1] Hyrule Castle - Boomerang Room: Nothing
 | 
			
		||||
[dungeon-C-B1] Hyrule Castle - Map Room: Nothing
 | 
			
		||||
[dungeon-C-B1] Hyrule Castle - Next To Zelda: Nothing
 | 
			
		||||
[dungeon-C-B1] Escape - First B1 Room: Small Key (Escape)
 | 
			
		||||
[dungeon-C-B1] Escape - Final Basement Room [left chest]: Nothing
 | 
			
		||||
[dungeon-C-B1] Escape - Final Basement Room [middle chest]: Nothing
 | 
			
		||||
[dungeon-C-B1] Escape - Final Basement Room [right chest]: Nothing
 | 
			
		||||
[dungeon-C-1F] Sanctuary: Sanctuary Heart Container
 | 
			
		||||
[dungeon-A1-2F] Hyrule Castle Tower - 2 Knife Guys Room: Small Key (Agahnims Tower)
 | 
			
		||||
[dungeon-A1-3F] Hyrule Castle Tower - Maze Room: Small Key (Agahnims Tower)
 | 
			
		||||
Old Mountain Man: Magic Mirror
 | 
			
		||||
Piece of Heart (Spectacle Rock Cave): Nothing
 | 
			
		||||
[cave-009-1F] Death Mountain - right cave [top left chest]: Nothing
 | 
			
		||||
[cave-009-1F] Death Mountain - right cave [top left middle chest]: Nothing
 | 
			
		||||
[cave-009-1F] Death Mountain - right cave [top right middle chest]: Nothing
 | 
			
		||||
[cave-009-1F] Death Mountain - right cave [top right chest]: Nothing
 | 
			
		||||
[cave-009-1F] Death Mountain - right cave [bottom chest]: Nothing
 | 
			
		||||
[cave-009-B1] Death Mountain - right cave [left chest]: Nothing
 | 
			
		||||
[cave-009-B1] Death Mountain - right cave [right chest]: Nothing
 | 
			
		||||
[cave-012-1F] Death Mountain - left cave]: Nothing
 | 
			
		||||
Ether Tablet: Ether
 | 
			
		||||
Piece of Heart (Spectacle Rock): Nothing
 | 
			
		||||
[dungeon-L3-1F] Tower of Hera - Freestanding Key: Small Key (Tower of Hera)
 | 
			
		||||
[dungeon-L3-1F] Tower of Hera - Entrance: Nothing
 | 
			
		||||
[dungeon-L3-1F] Tower of Hera - Basement: Big Key (Tower of Hera)
 | 
			
		||||
[dungeon-L3-1F] Tower of Hera - 4F [small chest]: Nothing
 | 
			
		||||
[dungeon-L3-1F] Tower of Hera - Big Chest: Moon Pearl
 | 
			
		||||
Moldorm - Heart Container: Nothing
 | 
			
		||||
Moldorm - Pendant: Red Pendant
 | 
			
		||||
Piece of Heart (Pyramid): Nothing
 | 
			
		||||
Catfish: Quake
 | 
			
		||||
Flute Boy: Shovel
 | 
			
		||||
Piece of Heart (Digging Game): Nothing
 | 
			
		||||
Bombos Tablet: Bombos
 | 
			
		||||
[cave-073] Cave Northeast of Swamp Palace [top chest]: Nothing
 | 
			
		||||
[cave-073] Cave Northeast of Swamp Palace [top middle chest]: Nothing
 | 
			
		||||
[cave-073] Cave Northeast of Swamp Palace [bottom middle chest]: Nothing
 | 
			
		||||
[cave-073] Cave Northeast of Swamp Palace [bottom chest]: Nothing
 | 
			
		||||
[cave-073] Cave Northeast of Swamp Palace [generous guy]: Nothing
 | 
			
		||||
Piece of Heart (Dark World Blacksmith Pegs): Nothing
 | 
			
		||||
Pyramid Fairy [left chest]: Golden Sword
 | 
			
		||||
Pyramid Fairy [right chest]: Silver Arrows
 | 
			
		||||
[cave-063] Doorless Hut: Nothing
 | 
			
		||||
[cave-062] C-Shaped House: Nothing
 | 
			
		||||
Piece of Heart (Treasure Chest Game): Nothing
 | 
			
		||||
Piece of Heart (Bumper Cave): Nothing
 | 
			
		||||
[cave-071] Misery Mire West Area [left chest]: Nothing
 | 
			
		||||
[cave-071] Misery Mire West Area [right chest]: Nothing
 | 
			
		||||
[cave-057-1F] Dark World Death Mountain Climb [top chest]: Nothing
 | 
			
		||||
[cave-057-1F] Dark World Death Mountain Climb [bottom chest]: Nothing
 | 
			
		||||
[cave-055] Spike Cave: Cane of Byrna
 | 
			
		||||
[cave-056] Hookshot Cave [top right chest]: Nothing
 | 
			
		||||
[cave-056] Hookshot Cave [top left chest]: Nothing
 | 
			
		||||
[cave-056] Hookshot Cave [bottom right chest]: Nothing
 | 
			
		||||
[cave-056] Hookshot Cave [bottom left chest]: Nothing
 | 
			
		||||
Piece of Heart (Death Mountain - Floating Island): Nothing
 | 
			
		||||
[cave-013] Mimic Cave: Nothing
 | 
			
		||||
[dungeon-D2-1F] Swamp Palace - First Room: Small Key (Swamp Palace)
 | 
			
		||||
[dungeon-D2-1F] Swamp Palace - Map Room: Nothing
 | 
			
		||||
[dungeon-D2-B1] Swamp Palace - Big Chest: Hookshot
 | 
			
		||||
[dungeon-D2-B1] Swamp Palace - South of Hookshot Room: Nothing
 | 
			
		||||
[dungeon-D2-B1] Swamp Palace - Big Key Chest: Big Key (Swamp Palace)
 | 
			
		||||
[dungeon-D2-B1] Swamp Palace - Compass Chest: Nothing
 | 
			
		||||
[dungeon-D2-B2] Swamp Palace - Flooded Room [left chest]: Nothing
 | 
			
		||||
[dungeon-D2-B2] Swamp Palace - Flooded Room [right chest]: Nothing
 | 
			
		||||
[dungeon-D2-B2] Swamp Palace - Waterfall Room: Nothing
 | 
			
		||||
Arrghus - Heart Container: Nothing
 | 
			
		||||
Arrghus - Crystal: Crystal 2
 | 
			
		||||
[dungeon-D4-B1] Thieves Town - Bottom Left of Huge Room [bottom right chest]: Big Key (Thieves Town)
 | 
			
		||||
[dungeon-D4-B1] Thieves Town - Bottom Left of Huge Room [top left chest]: Nothing
 | 
			
		||||
[dungeon-D4-B1] Thieves Town - Bottom Right of Huge Room: Nothing
 | 
			
		||||
[dungeon-D4-B1] Thieves Town - Top Left of Huge Room: Nothing
 | 
			
		||||
[dungeon-D4-1F] Thieves Town - Room above Boss: Nothing
 | 
			
		||||
[dungeon-D4-B2] Thieves Town - Big Chest: Titans Mitts
 | 
			
		||||
[dungeon-D4-B2] Thieves Town - Chest next to Blind: Small Key (Thieves Town)
 | 
			
		||||
Blind - Heart Container: Nothing
 | 
			
		||||
Blind - Crystal: Crystal 4
 | 
			
		||||
[dungeon-D3-B1] Skull Woods - Compass Room: Nothing
 | 
			
		||||
[dungeon-D3-B1] Skull Woods - East of Big Chest: Nothing
 | 
			
		||||
[dungeon-D3-B1] Skull Woods - Big Chest: Fire Rod
 | 
			
		||||
[dungeon-D3-B1] Skull Woods - Push Statue Room: Small Key (Skull Woods)
 | 
			
		||||
[dungeon-D3-B1] Skull Woods - South of Big Chest: Small Key (Skull Woods)
 | 
			
		||||
[dungeon-D3-B1] Skull Woods - Big Key Room: Big Key (Skull Woods)
 | 
			
		||||
[dungeon-D3-B1] Skull Woods - Final Section Entrance: Small Key (Skull Woods)
 | 
			
		||||
Mothula - Heart Container: Nothing
 | 
			
		||||
Mothula - Crystal: Crystal 3
 | 
			
		||||
[dungeon-D5-B1] Ice Palace - Compass Room: Nothing
 | 
			
		||||
[dungeon-D5-B4] Ice Palace - Above Big Chest: Nothing
 | 
			
		||||
[dungeon-D5-B5] Ice Palace - Big Chest: Blue Mail
 | 
			
		||||
[dungeon-D5-B5] Ice Palace - Jellyfish Room: Small Key (Ice Palace)
 | 
			
		||||
[dungeon-D5-B3] Ice Palace - Spike Room: Small Key (Ice Palace)
 | 
			
		||||
[dungeon-D5-B1] Ice Palace - Big Key Room: Big Key (Ice Palace)
 | 
			
		||||
[dungeon-D5-B2] Ice Palace - Map Room: Nothing
 | 
			
		||||
Kholdstare - Heart Container: Nothing
 | 
			
		||||
Kholdstare - Crystal: Crystal 5
 | 
			
		||||
[dungeon-D6-B1] Misery Mire - Big Chest: Cane of Somaria
 | 
			
		||||
[dungeon-D6-B1] Misery Mire - Map Room: Nothing
 | 
			
		||||
[dungeon-D6-B1] Misery Mire - Hub Room: Small Key (Misery Mire)
 | 
			
		||||
[dungeon-D6-B1] Misery Mire - End of Bridge: Small Key (Misery Mire)
 | 
			
		||||
[dungeon-D6-B1] Misery Mire - Spike Room: Small Key (Misery Mire)
 | 
			
		||||
[dungeon-D6-B1] Misery Mire - Compass Room: Nothing
 | 
			
		||||
[dungeon-D6-B1] Misery Mire - Big Key Room: Big Key (Misery Mire)
 | 
			
		||||
Vitreous - Heart Container: Nothing
 | 
			
		||||
Vitreous - Crystal: Crystal 6
 | 
			
		||||
[dungeon-D7-1F] Turtle Rock - Compass Room: Nothing
 | 
			
		||||
[dungeon-D7-1F] Turtle Rock - Map Room [left chest]: Nothing
 | 
			
		||||
[dungeon-D7-1F] Turtle Rock - Map Room [right chest]: Small Key (Turtle Rock)
 | 
			
		||||
[dungeon-D7-1F] Turtle Rock - Chain Chomp Room: Small Key (Turtle Rock)
 | 
			
		||||
[dungeon-D7-B1] Turtle Rock - Big Key Room: Big Key (Turtle Rock)
 | 
			
		||||
[dungeon-D7-B1] Turtle Rock - Big Chest: Mirror Shield
 | 
			
		||||
[dungeon-D7-B1] Turtle Rock - Roller Switch Room: Small Key (Turtle Rock)
 | 
			
		||||
[dungeon-D7-B2] Turtle Rock - Eye Bridge Room [bottom left chest]: Small Key (Turtle Rock)
 | 
			
		||||
[dungeon-D7-B2] Turtle Rock - Eye Bridge Room [bottom right chest]: Nothing
 | 
			
		||||
[dungeon-D7-B2] Turtle Rock - Eye Bridge Room [top left chest]: Nothing
 | 
			
		||||
[dungeon-D7-B2] Turtle Rock - Eye Bridge Room [top right chest]: Nothing
 | 
			
		||||
Trinexx - Heart Container: Nothing
 | 
			
		||||
Trinexx - Crystal: Crystal 7
 | 
			
		||||
[dungeon-D1-B1] Dark Palace - Shooter Room: Small Key (Palace of Darkness)
 | 
			
		||||
[dungeon-D1-1F] Dark Palace - Jump Room [left chest]: Small Key (Palace of Darkness)
 | 
			
		||||
[dungeon-D1-B1] Dark Palace - Turtle Stalfos Room: Small Key (Palace of Darkness)
 | 
			
		||||
[dungeon-D1-1F] Dark Palace - Big Key Room: Big Key (Palace of Darkness)
 | 
			
		||||
[dungeon-D1-1F] Dark Palace - Jump Room [right chest]: Small Key (Palace of Darkness)
 | 
			
		||||
[dungeon-D1-1F] Dark Palace - Statue Push Room: Nothing
 | 
			
		||||
[dungeon-D1-1F] Dark Palace - Compass Room: Nothing
 | 
			
		||||
# logic cannot account for hammer and small key in maze
 | 
			
		||||
[dungeon-D1-B1] Dark Palace - Dark Room [left chest]: Small Key (Palace of Darkness)
 | 
			
		||||
[dungeon-D1-B1] Dark Palace - Dark Room [right chest]: Small Key (Palace of Darkness)
 | 
			
		||||
[dungeon-D1-1F] Dark Palace - Maze Room [top chest]: Nothing
 | 
			
		||||
[dungeon-D1-1F] Dark Palace - Maze Room [bottom chest]: Nothing
 | 
			
		||||
[dungeon-D1-1F] Dark Palace - Big Chest: Hammer
 | 
			
		||||
[dungeon-D1-1F] Dark Palace - Spike Statue Room: Nothing
 | 
			
		||||
Helmasaur - Heart Container: Nothing
 | 
			
		||||
Helmasaur - Crystal: Crystal 1
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - Torch: Small Key (Ganons Tower)
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - Right Staircase [left chest]: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - Right Staircase [right chest]: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - Tile Room: Small Key (Ganons Tower)
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - Compass Room [top left chest]: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - Compass Room [top right chest]: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - Compass Room [bottom left chest]: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - Compass Room [bottom right chest]: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - North of Hookshot Room [top left chest]: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - North of Hookshot Room [top right chest]: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - North of Hookshot Room [bottom left chest]: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - North of Hookshot Room [bottom right chest]: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - Map Room: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - Firesnake Room: Small Key (Ganons Tower)
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - Teleport Room [top left chest]: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - Teleport Room [top right chest]: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - Teleport Room [bottom left chest]: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - Teleport Room [bottom right chest]: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - above Armos: Nothing
 | 
			
		||||
[dungeon-A2-1F] Ganons Tower - Big Chest: Red Mail
 | 
			
		||||
[dungeon-A2-B1] Ganons Tower - Armos Room [left chest]: Nothing
 | 
			
		||||
[dungeon-A2-B1] Ganons Tower - Armos Room [right chest]: Nothing
 | 
			
		||||
[dungeon-A2-B1] Ganons Tower - Armos Room [bottom chest]: Big Key (Ganons Tower)
 | 
			
		||||
[dungeon-A2-6F] Ganons Tower - Mini Helmasaur Room [left chest]: Nothing
 | 
			
		||||
[dungeon-A2-6F] Ganons Tower - Mini Helmasaur Room [right chest]: Nothing
 | 
			
		||||
[dungeon-A2-6F] Ganons Tower - Room before Moldorm: Small Key (Ganons Tower)
 | 
			
		||||
[dungeon-A2-6F] Ganons Tower - Moldorm Room: Nothing
 | 
			
		||||
Ganon: Triforce
 | 
			
		||||
		Loading…
	
		Reference in New Issue